diff --git a/cgmanifest.json b/cgmanifest.json index 9c70387a6b..f567192d80 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -49,7 +49,7 @@ "component":{ "type":"git", "git":{ - "commitHash":"8a1319733a5518bd0001842db27e2df53a306eff", + "commitHash":"2896c77cfc628f18b6ca6b28e3a380807fa00f53", "repositoryUrl":"https://github.com/onnx/onnx.git" } } diff --git a/cmake/external/onnx b/cmake/external/onnx index dbf3581835..2896c77cfc 160000 --- a/cmake/external/onnx +++ b/cmake/external/onnx @@ -1 +1 @@ -Subproject commit dbf3581835e3a05716e10587511d7ab3b2cdc386 +Subproject commit 2896c77cfc628f18b6ca6b28e3a380807fa00f53 diff --git a/onnxruntime/contrib_ops/cpu/quantize_linear.cc b/onnxruntime/contrib_ops/cpu/quantize_linear.cc index 2111475b8b..a5fb145413 100644 --- a/onnxruntime/contrib_ops/cpu/quantize_linear.cc +++ b/onnxruntime/contrib_ops/cpu/quantize_linear.cc @@ -3,7 +3,6 @@ #include "contrib_ops/cpu/quantize_linear.h" #include "core/providers/cpu/math/element_wise_ops.h" -#include "core/providers/cpu/tensor/cast_op.h" #include "core/providers/common.h" namespace onnxruntime { diff --git a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc index 5d7a46313d..ddccb77cf2 100644 --- a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc +++ b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc @@ -134,18 +134,18 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, GRU); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, LSTM); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, RNN); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, uint8_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, uint16_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, uint32_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, uint64_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, int8_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, int16_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, int32_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, int64_t, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, bool, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, float, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, double, Cast); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, MLFloat16, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, uint8_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, uint16_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, uint32_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, uint64_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, int8_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, int16_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, int32_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, int64_t, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, bool, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, float, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, double, Cast); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 6, 9, MLFloat16, Cast); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 4, Concat); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Crop); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Gather); @@ -233,6 +233,7 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, Con class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, MeanVarianceNormalization); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, int32_t, Greater); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, int32_t, Less); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, string, Cast); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, EyeLike); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, float, IsNaN); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, MLFloat16, IsNaN); @@ -384,18 +385,18 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) { kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); - kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); @@ -485,6 +486,7 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) { kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); + kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); diff --git a/onnxruntime/core/providers/cpu/tensor/cast_op.cc b/onnxruntime/core/providers/cpu/tensor/cast_op.cc index c58b5a9182..9628caffa1 100644 --- a/onnxruntime/core/providers/cpu/tensor/cast_op.cc +++ b/onnxruntime/core/providers/cpu/tensor/cast_op.cc @@ -1,13 +1,202 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/providers/cpu/tensor/cast_op.h" + #include #include "core/common/common.h" +#include "core/framework/op_kernel.h" +#include "core/util/math.h" +#include "core/util/math_cpuonly.h" +#include "Eigen/src/Core/arch/CUDA/Half.h" +#include "core/common/common.h" + +#if defined(USE_MLAS) && defined(_M_AMD64) +#include "core/mlas/inc/mlas.h" +#endif using namespace ONNX_NAMESPACE; namespace onnxruntime { +template +inline void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) { + auto shape_size = shape.Size(); + auto in_vector = ConstEigenVectorMap(in->template Data(), shape_size); + auto output_vector = EigenVectorMap(out->template MutableData(), shape_size); + output_vector = in_vector.template cast(); +} + +template <> +inline void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) { + auto out_data = out->template MutableData(); + auto shape_size = shape.Size(); + auto in_vector = ConstEigenVectorMap(in->template Data(), shape_size); + auto output_vector = EigenVectorMap(static_cast(static_cast(out_data)), shape_size); + output_vector = in_vector.template cast(); +} + +template <> +inline void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) { + auto out_data = out->template MutableData(); + auto in_data = in->template Data(); + auto shape_size = shape.Size(); +#if defined(USE_MLAS) && defined(_M_AMD64) + MlasConvertHalfToFloatBuffer(&in_data[0].val, out_data, shape_size); +#else + auto in_vector = ConstEigenVectorMap(static_cast(static_cast(in_data)), shape_size); + auto output_vector = EigenVectorMap(out_data, shape_size); + output_vector = in_vector.template cast(); +#endif +} + +template +inline void CastFloat16Data(const Tensor* in, Tensor* out, const TensorShape& shape, const AllocatorPtr& allocator) { + ORT_ENFORCE(allocator != nullptr); + const int64_t len = shape.Size(); + ORT_ENFORCE(len > 0); + void* buffer = allocator->AllocArray(sizeof(float), len); + ORT_ENFORCE(buffer); + Tensor tmp_tensor(DataTypeImpl::GetType(), shape, buffer, allocator->Info(), nullptr); + if (std::is_same::value) { + CastData(in, &tmp_tensor, shape); // first cast to float + CastData(&tmp_tensor, out, shape); // then cast to the destination type. + } else if (std::is_same::value) { + CastData(in, &tmp_tensor, shape); + CastData(&tmp_tensor, out, shape); + } + allocator->Free(buffer); +} + +template +inline void CastToStringData(const Tensor* in, Tensor* out, const TensorShape& shape) { + const int64_t len = shape.Size(); + ORT_ENFORCE(len > 0); + for (int i = 0; i < len; ++i) { + if (std::is_floating_point::value && std::isnan(in->Data()[i])) { + out->MutableData()[i] = "NaN"; + } else if (std::is_floating_point::value && std::isinf(in->Data()[i])) { + if (in->Data()[i] < std::numeric_limits::lowest()) { + out->MutableData()[i] = "-INF"; + } else { + out->MutableData()[i] = "INF"; + } + } else { + std::ostringstream convert; + convert << in->Data()[i]; + out->MutableData()[i] = convert.str(); + } + } +} + +template +inline void CastFromStringData(const Tensor* in, Tensor* out, const TensorShape& shape) { + if (std::is_same::value) return; + const int64_t len = shape.Size(); + ORT_ENFORCE(len > 0); + if (std::is_same::value) { + float* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + mutable_data[i] = std::stof(in->Data()[i]); + } + } else if (std::is_same::value) { + double* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + mutable_data[i] = std::stod(in->Data()[i]); + } + } else if (std::is_same::value) { + int8_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + int temp_i = std::stoi(in->Data()[i]); + mutable_data[i] = static_cast(temp_i); + } + } else if (std::is_same::value) { + uint8_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + unsigned long temp_ui = std::stoul(in->Data()[i]); + mutable_data[i] = static_cast(temp_ui); + } + } else if (std::is_same::value) { + int16_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + int temp_i = std::stoi(in->Data()[i]); + mutable_data[i] = static_cast(temp_i); + } + } else if (std::is_same::value) { + uint16_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + unsigned long temp_ui = std::stoul(in->Data()[i]); + mutable_data[i] = static_cast(temp_ui); + } + } else if (std::is_same::value) { + int32_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + mutable_data[i] = std::stol(in->Data()[i]); + } + } else if (std::is_same::value) { + uint32_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + mutable_data[i] = std::stoul(in->Data()[i]); + } + } else if (std::is_same::value) { + int64_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + mutable_data[i] = std::stoll(in->Data()[i]); + } + } else if (std::is_same::value) { + uint64_t* mutable_data = out->MutableData(); + for (int i = 0; i < len; ++i) { + mutable_data[i] = std::stoull(in->Data()[i]); + } + } else { + ORT_THROW("Unsupported type in cast op: from String to ", typeid(DstType).name()); + } +} // namespace onnxruntime + +template +class Cast final : public OpKernel { + public: + Cast(const OpKernelInfo& info) : OpKernel(info) { + int64_t to; + Status status = info.GetAttr("to", &to); + ORT_ENFORCE(status.IsOK(), "Attribute to is not set."); + to_ = gsl::narrow_cast(to); + } + + Status Compute(OpKernelContext* context) const override; + + private: + template + void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) const { + ::onnxruntime::CastData(in, out, shape); + } + + template + Status CastFloat16Data(const Tensor* in, Tensor* out, const TensorShape& shape, OpKernelContext* context) const { + AllocatorPtr allocator; + ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&allocator)); + ::onnxruntime::CastFloat16Data(in, out, shape, allocator); + return Status::OK(); + } + + template + Status CastToStringData(const Tensor* in, Tensor* out, const TensorShape& shape) const { + ::onnxruntime::CastToStringData(in, out, shape); + return Status::OK(); + } + + template + Status CastFromStringData(const Tensor* in, Tensor* out, const TensorShape& shape) const { + ::onnxruntime::CastFromStringData(in, out, shape); + return Status::OK(); + } + + ONNX_NAMESPACE::TensorProto_DataType to_; +}; + + const std::vector castOpTypeConstraints{ DataTypeImpl::GetTensorType(), DataTypeImpl::GetTensorType(), @@ -20,12 +209,14 @@ const std::vector castOpTypeConstraints{ DataTypeImpl::GetTensorType(), DataTypeImpl::GetTensorType(), DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType()}; + DataTypeImpl::GetTensorType(), + DataTypeImpl::GetTensorType()}; #define ADD_FROM_CAST_OP(in_type) \ - ONNX_CPU_OPERATOR_TYPED_KERNEL( \ + ONNX_CPU_OPERATOR_VERSIONED_TYPED_KERNEL( \ Cast, \ 6, \ + 9, \ in_type, \ KernelDefBuilder().TypeConstraint("T1", DataTypeImpl::GetTensorType()).TypeConstraint("T2", castOpTypeConstraints), \ Cast); \ @@ -33,7 +224,6 @@ const std::vector castOpTypeConstraints{ template <> \ Status Cast::Compute(OpKernelContext* context) const { \ const Tensor* X = context->Input(0); \ - if (X == nullptr) return Status(common::ONNXRUNTIME, common::FAIL, "input count mismatch"); \ const TensorShape& shape = X->Shape(); \ Tensor* Y = context->Output(0, TensorShape(shape)); \ \ @@ -80,11 +270,12 @@ const std::vector castOpTypeConstraints{ } \ break; \ case TensorProto_DataType_STRING: \ - ORT_THROW("Casting to and from strings is not supported yet."); /*break;*/ \ + CastToStringData(X, Y, shape); \ + break; \ case TensorProto_DataType_UNDEFINED: \ - ORT_THROW("Cast op must have 'to' argument of type DataType"); /*break;*/ \ + ORT_THROW("Cast op must have 'to' argument of type DataType"); /*break;*/ \ default: \ - ORT_THROW("Unexpected 'to' argument value: ", to_); \ + ORT_THROW("Unexpected 'to' argument value: ", to_); \ } \ return Status::OK(); \ } @@ -101,9 +292,10 @@ ADD_FROM_CAST_OP(bool); ADD_FROM_CAST_OP(float); ADD_FROM_CAST_OP(double); -ONNX_CPU_OPERATOR_TYPED_KERNEL( +ONNX_CPU_OPERATOR_VERSIONED_TYPED_KERNEL( Cast, 6, + 9, MLFloat16, KernelDefBuilder().TypeConstraint("T1", DataTypeImpl::GetTensorType()).TypeConstraint("T2", castOpTypeConstraints), Cast); @@ -111,7 +303,6 @@ ONNX_CPU_OPERATOR_TYPED_KERNEL( template <> Status Cast::Compute(OpKernelContext* context) const { const Tensor* X = context->Input(0); - if (X == nullptr) return Status(common::ONNXRUNTIME, common::FAIL, "input count mismatch"); const TensorShape& shape = X->Shape(); Tensor* Y = context->Output(0, TensorShape(shape)); Status st; @@ -144,14 +335,14 @@ Status Cast::Compute(OpKernelContext* context) const { CastData(X, Y, shape); break; case TensorProto_DataType_FLOAT16: { - auto X_type = X->DataType(); - const void* source = X->DataRaw(X_type); - void* target = Y->MutableDataRaw(X_type); - // if source and target pointers are not equal, we need to copy the data. - if (target != source) { - memcpy(target, source, shape.Size() * X_type->Size()); - } - st = Status::OK(); + auto X_type = X->DataType(); + const void* source = X->DataRaw(X_type); + void* target = Y->MutableDataRaw(X_type); + // if source and target pointers are not equal, we need to copy the data. + if (target != source) { + memcpy(target, source, shape.Size() * X_type->Size()); + } + st = Status::OK(); break; } case TensorProto_DataType_DOUBLE: @@ -170,4 +361,57 @@ Status Cast::Compute(OpKernelContext* context) const { return st; } +ONNX_CPU_OPERATOR_TYPED_KERNEL( + Cast, + 9, + string, + KernelDefBuilder().TypeConstraint("T1", DataTypeImpl::GetTensorType()).TypeConstraint("T2", castOpTypeConstraints), + Cast); + +template <> +Status Cast::Compute(OpKernelContext* context) const { + const Tensor* X = context->Input(0); + if (X == nullptr) return Status(common::ONNXRUNTIME, common::FAIL, + "Input is missing. The operator Cast expects one and only one input"); + const TensorShape& shape = X->Shape(); + Tensor* Y = context->Output(0, TensorShape(shape)); + Status st; + switch (to_) { + case TensorProto_DataType_INT16: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_INT32: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_INT64: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_UINT8: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_UINT16: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_UINT32: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_UINT64: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_FLOAT: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_DOUBLE: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_INT8: + st = CastFromStringData(X, Y, shape); + break; + case TensorProto_DataType_UNDEFINED: + ORT_THROW("Cast op must have 'to' argument of type DataType"); + default: + ORT_THROW("Unexpected 'to' argument value: ", to_); + } + return st; +} } //namespace onnxruntime diff --git a/onnxruntime/core/providers/cpu/tensor/cast_op.h b/onnxruntime/core/providers/cpu/tensor/cast_op.h deleted file mode 100644 index 54feb86a08..0000000000 --- a/onnxruntime/core/providers/cpu/tensor/cast_op.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -#include "core/common/common.h" -#include "core/framework/op_kernel.h" -#include "core/util/math.h" -#include "core/util/math_cpuonly.h" -#include "Eigen/src/Core/arch/CUDA/Half.h" - -#if defined(USE_MLAS) && defined(_M_AMD64) -#include "core/mlas/inc/mlas.h" -#endif - -namespace onnxruntime { - -template -inline void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) { - auto shape_size = shape.Size(); - auto in_vector = ConstEigenVectorMap(in->template Data(), shape_size); - auto output_vector = EigenVectorMap(out->template MutableData(), shape_size); - output_vector = in_vector.template cast(); -} - -template <> -inline void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) { - auto out_data = out->template MutableData(); - auto shape_size = shape.Size(); - auto in_vector = ConstEigenVectorMap(in->template Data(), shape_size); - auto output_vector = EigenVectorMap(static_cast(static_cast(out_data)), shape_size); - output_vector = in_vector.template cast(); -} - -template <> -inline void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) { - auto out_data = out->template MutableData(); - auto in_data = in->template Data(); - auto shape_size = shape.Size(); -#if defined(USE_MLAS) && defined(_M_AMD64) - MlasConvertHalfToFloatBuffer(&in_data[0].val, out_data, shape_size); -#else - auto in_vector = ConstEigenVectorMap(static_cast(static_cast(in_data)), shape_size); - auto output_vector = EigenVectorMap(out_data, shape_size); - output_vector = in_vector.template cast(); -#endif -} - -template -inline void CastFloat16Data(const Tensor* in, Tensor* out, const TensorShape& shape, const AllocatorPtr& allocator) { - ORT_ENFORCE(allocator != nullptr); - const int64_t len = shape.Size(); - ORT_ENFORCE(len > 0); - void* buffer = allocator->AllocArray(sizeof(float), len); - ORT_ENFORCE(buffer); - Tensor tmp_tensor(DataTypeImpl::GetType(), shape, buffer, allocator->Info(), nullptr); - if (std::is_same::value) { - CastData(in, &tmp_tensor, shape); // first cast to float - CastData(&tmp_tensor, out, shape); // then cast to the destination type. - } else if (std::is_same::value) { - CastData(in, &tmp_tensor, shape); - CastData(&tmp_tensor, out, shape); - } - allocator->Free(buffer); -} - -template -class Cast final : public OpKernel { - public: - Cast(const OpKernelInfo& info) : OpKernel(info) { - int64_t to; - Status status = info.GetAttr("to", &to); - ORT_ENFORCE(status.IsOK(), "Attribute to is not set."); - to_ = gsl::narrow_cast(to); - } - - Status Compute(OpKernelContext* context) const override; - - private: - template - void CastData(const Tensor* in, Tensor* out, const TensorShape& shape) const { - ::onnxruntime::CastData(in, out, shape); - } - - template - Status CastFloat16Data(const Tensor* in, Tensor* out, const TensorShape& shape, OpKernelContext* context) const { - AllocatorPtr allocator; - ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&allocator)); - ::onnxruntime::CastFloat16Data(in, out, shape, allocator); - return Status::OK(); - } - - ONNX_NAMESPACE::TensorProto_DataType to_; -}; - -} //namespace onnxruntime diff --git a/onnxruntime/test/providers/cpu/tensor/tensor_op_test.cc b/onnxruntime/test/providers/cpu/tensor/tensor_op_test.cc index b43431ce15..df84f2f9c6 100644 --- a/onnxruntime/test/providers/cpu/tensor/tensor_op_test.cc +++ b/onnxruntime/test/providers/cpu/tensor/tensor_op_test.cc @@ -3,7 +3,6 @@ #include "gtest/gtest.h" #include "test/providers/provider_test_utils.h" -#include "core/providers/cpu/tensor/cast_op.h" #include "core/providers/cpu/tensor/crop.h" #include "core/util/math.h" @@ -80,7 +79,7 @@ void TestCastOp(const std::initializer_list& input, int64_t toType, ExpectResult expect_result = ExpectResult::kExpectSuccess, const std::string& expected_failure_string = "") { - OpTester test("Cast"); + OpTester test("Cast", 9); test.AddAttribute("to", toType); test.AddInput("input", dimensions, input); test.AddOutput("output", dimensions, output); @@ -277,6 +276,32 @@ TEST(TensorOpTest, CastFromFloat16) { TestCastOp(input, int64_t_data, shape, TensorProto::INT64); } +TEST(TensorOpTest, CastFromString) { + const std::vector shape{2, 2, 2}; + std::initializer_list string_data = {"-inf", "+INF", "2.0f", "3.0f", "4.0f", "5.0f", "NaN", "nan"}; + const std::initializer_list float_output = {-(std::numeric_limits::infinity()), std::numeric_limits::infinity(), 2.0f, 3.0f, 4.0f, 5.0f, NAN, NAN}; + TestCastOp(string_data, float_output, shape, TensorProto::FLOAT); + + std::initializer_list int_16_string_data = {"0", "1", "2", "3", "4", "5", "-32768", "32767"}; + const std::initializer_list int_16_output = {0, 1, 2, 3, 4, 5, SHRT_MIN, SHRT_MAX}; + TestCastOp(int_16_string_data, int_16_output, shape, TensorProto::INT16); + + std::initializer_list int_64_string_data = {"0", "1", "2", "3", "4", "5", "-9223372036854775808", "9223372036854775807"}; + const std::initializer_list int_64_output = {0, 1, 2, 3, 4, 5, LLONG_MIN, LLONG_MAX}; + TestCastOp(int_64_string_data, int_64_output, shape, TensorProto::INT64); +} + +TEST(TensorOpTest, CastToString) { + const std::vector shape{2, 2, 2}; + const std::initializer_list float_input = {NAN, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, -std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + std::initializer_list string_output = {"NaN", "1", "2", "3", "4", "5", "-INF", "INF"}; + TestCastOp(float_input, string_output, shape, TensorProto::STRING); + + std::initializer_list int_string_data = {"0", "1", "2", "3", "4", "5", "6", "7"}; + const std::initializer_list int_16_input = {0, 1, 2, 3, 4, 5, 6, 7}; + TestCastOp(int_16_input, int_string_data, shape, TensorProto::STRING); +} + TEST(TensorOpTest, CropBorderOnly) { const int N = 2, C = 1, H = 3, W = 4; std::vector X = {1.0f, 2.0f, 3.0f, 4.0f, diff --git a/onnxruntime/test/providers/provider_test_utils.cc b/onnxruntime/test/providers/provider_test_utils.cc index 62f4f3b62d..abe7413475 100644 --- a/onnxruntime/test/providers/provider_test_utils.cc +++ b/onnxruntime/test/providers/provider_test_utils.cc @@ -50,9 +50,11 @@ void Check(const OpTester::Data& expected_data, const Tensor& output_tens #endif for (int i = 0; i < size; ++i) { - if (std::isinf(expected[i])) // Test infinity for equality + if (std::isinf(expected[i])){ // Test infinity for equality EXPECT_EQ(expected[i], output[i]); - else { + } else if (std::isnan(expected[i])) { + EXPECT_TRUE(std::isnan(output[i])) << "Expected output " << i << " to be NaN"; + } else { if (!has_abs_err && !has_rel_err) { // the default for existing tests EXPECT_NEAR(expected[i], output[i], threshold) << "provider_type: " << provider_type; diff --git a/tools/ci_build/github/linux/docker/scripts/install_deps.sh b/tools/ci_build/github/linux/docker/scripts/install_deps.sh index 13a77a7fa8..0a30abf374 100755 --- a/tools/ci_build/github/linux/docker/scripts/install_deps.sh +++ b/tools/ci_build/github/linux/docker/scripts/install_deps.sh @@ -37,8 +37,9 @@ else #Install ONNX #5af210ca8a1c73aa6bae8754c9346ec54d0a756e is v1.2.3 #bae6333e149a59a3faa9c4d9c44974373dcf5256 is v1.3.0 - #dbf3581835e3a05716e10587511d7ab3b2cdc386 is v1.3.0 latest - for onnx_version in "5af210ca8a1c73aa6bae8754c9346ec54d0a756e" "bae6333e149a59a3faa9c4d9c44974373dcf5256" "dbf3581835e3a05716e10587511d7ab3b2cdc386"; do + #9e55ace55aad1ada27516038dfbdc66a8a0763db is v1.4.1 + #2896c77cfc628f18b6ca6b28e3a380807fa00f53 is v1.4.1 latest + for onnx_version in "5af210ca8a1c73aa6bae8754c9346ec54d0a756e" "bae6333e149a59a3faa9c4d9c44974373dcf5256" "9e55ace55aad1ada27516038dfbdc66a8a0763db" "2896c77cfc628f18b6ca6b28e3a380807fa00f53"; do if [ -z ${lastest_onnx_version+x} ]; then echo "first pass"; else