diff --git a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc index 5d64698f6a..6cc6745540 100644 --- a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc +++ b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc @@ -713,11 +713,11 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 14, Id The process is the same for TYPED and untyped kernels - just repeat for each type when updating the typed entries. - The changes below in the registrations using BuildKernelCreateInfo are essentially the same. Update existing + The changes below in the registrations using BuildKernelCreateInfo are essentially the same. Update existing registration to use the VERSIONED_ macro, add end version, add new un-versioned entry in the section for the new opset. - To double-check what versions an operator should have registrations for see + To double-check what versions an operator should have registrations for see https://github.com/onnx/onnx/blob/master/docs/Operators.md *****/ diff --git a/onnxruntime/core/providers/cpu/sequence/sequence_ops.cc b/onnxruntime/core/providers/cpu/sequence/sequence_ops.cc index 6b40f05ffb..3ed6b89be0 100644 --- a/onnxruntime/core/providers/cpu/sequence/sequence_ops.cc +++ b/onnxruntime/core/providers/cpu/sequence/sequence_ops.cc @@ -209,10 +209,10 @@ Status SequenceInsert::Compute(OpKernelContext* context) const { const auto* I = context->Input(2); int64_t num_tensors_input_seq = static_cast(S->Size()); - int64_t input_seq_idx = num_tensors_input_seq + 1; // default is append - if (I) { // position is optional + int64_t input_seq_idx = num_tensors_input_seq; // default is append + if (I) { // position is optional input_seq_idx = GetSeqIdx(*I); - if (!ValidateSeqIdx(input_seq_idx, static_cast(num_tensors_input_seq))) { + if (!ValidateSeqIdx(input_seq_idx, num_tensors_input_seq) && input_seq_idx != num_tensors_input_seq) { return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "Invalid sequence index (", input_seq_idx, ") specified for sequence of size (", num_tensors_input_seq, ")"); } @@ -234,7 +234,7 @@ Status SequenceInsert::Compute(OpKernelContext* context) const { CreateCopyAndAppendCpuTensor(S->Get(i), context, tensors); } } - if (input_seq_idx == num_tensors_input_seq + 1) { + if (input_seq_idx == num_tensors_input_seq) { CreateCopyAndAppendCpuTensor(*X, context, tensors); } diff --git a/onnxruntime/core/providers/cpu/tensor/identity_op.cc b/onnxruntime/core/providers/cpu/tensor/identity_op.cc index f62db3c995..c928d24aa3 100644 --- a/onnxruntime/core/providers/cpu/tensor/identity_op.cc +++ b/onnxruntime/core/providers/cpu/tensor/identity_op.cc @@ -41,7 +41,7 @@ ONNX_CPU_OPERATOR_VERSIONED_KERNEL( ONNX_CPU_OPERATOR_KERNEL( Identity, 14, - KernelDefBuilder().TypeConstraint("V", DataTypeImpl::AllTensorTypes()).Alias(0, 0), + KernelDefBuilder().TypeConstraint("V", DataTypeImpl::AllTensorAndSequenceTensorTypes()).Alias(0, 0), IdentityOp); } // namespace onnxruntime diff --git a/onnxruntime/core/providers/cpu/tensor/identity_op.h b/onnxruntime/core/providers/cpu/tensor/identity_op.h index e0f77a977e..f1e54a7310 100644 --- a/onnxruntime/core/providers/cpu/tensor/identity_op.h +++ b/onnxruntime/core/providers/cpu/tensor/identity_op.h @@ -12,6 +12,7 @@ #pragma warning(pop) #endif #include "core/framework/op_kernel.h" +#include "core/framework/TensorSeq.h" namespace onnxruntime { @@ -22,39 +23,62 @@ class IdentityOp final : public OpKernel { } Status Compute(OpKernelContext* context) const override { - const auto* X = context->Input(0); - ORT_ENFORCE(X != nullptr); - const TensorShape& shape = X->Shape(); - Tensor* Y = context->Output(0, shape); - auto X_type = X->DataType(); + auto X_ml_type = context->InputType(0); + if (X_ml_type == DataTypeImpl::GetType()) { + const auto* X = context->Input(0); + ORT_ENFORCE(X != nullptr); + const TensorShape& shape = X->Shape(); + Tensor* Y = context->Output(0, shape); + 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) { - if (!X->IsDataTypeString()) { - memcpy(target, source, shape.Size() * X_type->Size()); - } else { - // handle std::string - const auto* src = X->template Data(); - auto* dst = Y->template MutableData(); - std::copy(src, src + shape.Size(), dst); + 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) { + if (!X->IsDataTypeString()) { + memcpy(target, source, shape.Size() * X_type->Size()); + } else { + // handle std::string + const auto* src = X->template Data(); + auto* dst = Y->template MutableData(); + std::copy(src, src + shape.Size(), dst); + } } - } - if (is_dropout) { - Tensor* mask = context->Output(1, shape); - // a 'nullptr' returned would make it an unused optional output - if (mask != nullptr) { - // Opset 7 differs with Opset 10 in that the type of the 'mask' - // output is tied with the type of the input in Opset 7 whereas - // the type of 'mask' in Opset 10 is 'bool' always - // so we have a common solution - void* mask_data = mask->MutableDataRaw(); - // In 'test'/'inference' mode, there are no input values dropped out - // so fill the buffer with 0/false - memset(mask_data, 0, mask->SizeInBytes()); + if (is_dropout) { + Tensor* mask = context->Output(1, shape); + // a 'nullptr' returned would make it an unused optional output + if (mask != nullptr) { + // Opset 7 differs with Opset 10 in that the type of the 'mask' + // output is tied with the type of the input in Opset 7 whereas + // the type of 'mask' in Opset 10 is 'bool' always + // so we have a common solution + void* mask_data = mask->MutableDataRaw(); + // In 'test'/'inference' mode, there are no input values dropped out + // so fill the buffer with 0/false + memset(mask_data, 0, mask->SizeInBytes()); + } } + } else { + const auto* X = context->Input(0); + ORT_ENFORCE(X != nullptr); + TensorSeq* output = context->Output(0); + output->SetType(X->DataType()); + + AllocatorPtr alloc; + auto status = context->GetTempSpaceAllocator(&alloc); + if (!status.IsOK()) { + ORT_THROW("Unable to get an allocator"); + } + std::vector tensors; + for (auto it = X->begin(), end = X->end(); it != end; ++it) { + Tensor tmp(it->DataType(), onnxruntime::TensorShape(it->Shape()), alloc); + size_t bytes = it->SizeInBytes(); + memcpy(tmp.MutableDataRaw(), it->DataRaw(), bytes); + tensors.push_back(std::move(tmp)); + } + + output->SetElements(std::move(tensors)); } return Status::OK(); diff --git a/onnxruntime/test/providers/cpu/sequence/sequence_ops_test.cc b/onnxruntime/test/providers/cpu/sequence/sequence_ops_test.cc index d46c0626e0..d29aac8115 100644 --- a/onnxruntime/test/providers/cpu/sequence/sequence_ops_test.cc +++ b/onnxruntime/test/providers/cpu/sequence/sequence_ops_test.cc @@ -129,6 +129,36 @@ TEST(SequenceOpsTest, SequenceInsertPositiveDefault) { test.Run(); } +TEST(SequenceOpsTest, SequenceInsertEmptyLast) { + OpTester test("SequenceInsert", 11); + SeqTensors input; + test.AddSeqInput("S", input); + test.AddInput("T", {3, 2}, {10, 20, 30, 40, 50, 60}); + test.AddInput("I", {1}, {0}); + + SeqTensors output; + output.AddTensor({3, 2}, {10, 20, 30, 40, 50, 60}); + test.AddSeqOutput("S2", output); + test.Run(); +} + +TEST(SequenceOpsTest, SequenceInsertLast) { + OpTester test("SequenceInsert", 11); + SeqTensors input; + input.AddTensor({3, 2}, {1, 2, 3, 4, 5, 6}); + input.AddTensor({3, 3}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + test.AddSeqInput("S", input); + test.AddInput("T", {3, 2}, {10, 20, 30, 40, 50, 60}); + test.AddInput("I", {1}, {2}); + + SeqTensors output; + output.AddTensor({3, 2}, {1, 2, 3, 4, 5, 6}); + output.AddTensor({3, 3}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + output.AddTensor({3, 2}, {10, 20, 30, 40, 50, 60}); + test.AddSeqOutput("S2", output); + test.Run(); +} + TEST(SequenceOpsTest, SequenceInsertValidPositiveIdx) { OpTester test("SequenceInsert", 11); SeqTensors input; diff --git a/onnxruntime/test/providers/cpu/tensor/identity_op_test.cc b/onnxruntime/test/providers/cpu/tensor/identity_op_test.cc index a95ce0e844..6c430e1888 100644 --- a/onnxruntime/test/providers/cpu/tensor/identity_op_test.cc +++ b/onnxruntime/test/providers/cpu/tensor/identity_op_test.cc @@ -23,5 +23,15 @@ TEST(Identity, StringType) { test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider});//TensorRT: unsupported data type } +TEST(Identity, SequenceType) { + OpTester test("Identity", 14, kOnnxDomain); + SeqTensors input; + input.AddTensor({3, 2}, {1, 2, 3, 4, 5, 6}); + input.AddTensor({3, 3}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + test.AddSeqInput("X", input); + test.AddSeqOutput("Y", input); + test.Run(); +} + } // namespace test } // namespace onnxruntime diff --git a/onnxruntime/test/testdata/kernel_def_hashes/onnx.cpu.json b/onnxruntime/test/testdata/kernel_def_hashes/onnx.cpu.json index 049e8aba3e..c06ab7c7f9 100644 --- a/onnxruntime/test/testdata/kernel_def_hashes/onnx.cpu.json +++ b/onnxruntime/test/testdata/kernel_def_hashes/onnx.cpu.json @@ -891,10 +891,6 @@ "HardSigmoid ai.onnx CPUExecutionProvider", 8073874088399301792 ], - [ - "Identity ai.onnx CPUExecutionProvider", - 13401700334046441640 - ], [ "Identity ai.onnx CPUExecutionProvider", 16879814636194901248 @@ -903,6 +899,10 @@ "Identity ai.onnx CPUExecutionProvider", 18001636502361632792 ], + [ + "Identity ai.onnx CPUExecutionProvider", + 16515685968327103576 + ], [ "If ai.onnx CPUExecutionProvider", 2236645891232685176