diff --git a/mindspore-lite/src/litert/delegate/pnna/op/pnna_op.h b/mindspore-lite/src/litert/delegate/pnna/op/pnna_op.h index b553d4b24f9fc8499100dc3eeda571d9ab3ba34e..dd6dee01b36e92eec5abc8ec364cf9868e0dd7eb 100644 --- a/mindspore-lite/src/litert/delegate/pnna/op/pnna_op.h +++ b/mindspore-lite/src/litert/delegate/pnna/op/pnna_op.h @@ -19,8 +19,8 @@ #include #include #include -#include "include/cxx_api/kernel.h" -#include "include/cxx_api/data_type.h" +#include "include/api/kernel.h" +#include "include/api/data_type.h" #include "include/errorcode.h" #include "src/common/log_adapter.h" #include "schema/ops_generated.h" diff --git a/mindspore-lite/src/litert/delegate/pnna/op/transpose_pnna.cc b/mindspore-lite/src/litert/delegate/pnna/op/transpose_pnna.cc new file mode 100644 index 0000000000000000000000000000000000000000..6f66dc7a9a0658e4c733b9e24af910dcf9c9813c --- /dev/null +++ b/mindspore-lite/src/litert/delegate/pnna/op/transpose_pnna.cc @@ -0,0 +1,68 @@ +/** + * Copyright 2025 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "src/litert/delegate/pnna/op/transpose_pnna.h" +#include "src/litert/delegate/pnna/pnna_utils.h" + +namespace mindspore { +namespace lite { +int PNNATranspose::InitParams() { + if (!perm_.empty()) { + return RET_OK; + } + if (in_tensors_.size() != kInputSize1) { + MS_LOG(ERROR) << "Transpose op expects " << kInputSize1 << " inputs, but got " << in_tensors_.size(); + return RET_ERROR; + } + auto perm_tensor = in_tensors_.at(1); + if (perm_tensor.Data() == nullptr) { + MS_LOG(ERROR) << "Transpose perm tensor data is nullptr"; + return RET_ERROR; + } + auto perm_num = perm_tensor.ElementNum(); + auto perm_data = reinterpret_cast(perm_tensor.Data().get()); + for (size_t i = 0; i < perm_num; i++) { + perm_.push_back(perm_data[i]); + } + return RET_OK; +} + +bool PNNATranspose::IsSupport() { + MS_CHECK_GE(in_tensors_.size(), kInputSize1, RET_NOT_SUPPORT); + auto perm_tensor = in_tensors_.at(1); + if (!perm_tensor.IsConst()) { + MS_LOG(WARNING) << "PNNA transpose must get fixed axis values."; + return false; + } + return true; +} + +int PNNATranspose::AddOpToPNNAModel(PNNASubGraph *graph) { + MS_CHECK_TRUE_RET(graph != nullptr, RET_ERROR); + + auto input_tensor = graph->GetMappedTensor(&in_tensors_[0]); + if (!input_tensor) { + input_tensor = graph->ConvertOperand(&in_tensors_[0]); + } + auto output_tensor = graph->ConvertOperand(&out_tensors_[0]); + auto transpose_op = + graph->graph()->CreateOperation(ConvertToPnnaPerm(perm_.data(), perm_.size())); + transpose_op->BindInputs({input_tensor}); + transpose_op->BindOutputs({output_tensor}); + return RET_OK; +} +} // namespace lite +} // namespace mindspore diff --git a/mindspore-lite/src/litert/delegate/pnna/op/transpose_pnna.h b/mindspore-lite/src/litert/delegate/pnna/op/transpose_pnna.h new file mode 100644 index 0000000000000000000000000000000000000000..570e25dddcdbd2d8f6bcbb5fa84358037f81783b --- /dev/null +++ b/mindspore-lite/src/litert/delegate/pnna/op/transpose_pnna.h @@ -0,0 +1,51 @@ +/** + * Copyright 2025 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 MINDSPORE_LITE_SRC_LITERT_DELEGATE_PNNA_OP_TRANSPOSE_PNNA_H_ +#define MINDSPORE_LITE_SRC_LITERT_DELEGATE_PNNA_OP_TRANSPOSE_PNNA_H_ + +#include +#include +#include +#include "src/litert/delegate/pnna/op/pnna_op.h" + +namespace mindspore { +namespace lite { +class PNNATranspose : public PNNAOp { + public: + PNNATranspose(const std::string &name, std::vector perm, const std::vector &in_tensors, + const std::vector &out_tensors) + : PNNAOp(name, nullptr, in_tensors, out_tensors, schema::QuantType_QUANT_NONE) { + perm_ = std::move(perm); + type_ = schema::PrimitiveType_Transpose; + } + + PNNATranspose(const std::string &name, const schema::Primitive *primitive, + const std::vector &in_tensors, const std::vector &out_tensors, + schema::QuantType quant_type) + : PNNAOp(name, primitive, in_tensors, out_tensors, quant_type) {} + ~PNNATranspose() override {} + + bool IsSupport() override; + int InitParams() override; + int AddOpToPNNAModel(PNNASubGraph *graph) override; + std::vector GetPerm() { return perm_; } + + private: + std::vector perm_; +}; +} // namespace lite +} // namespace mindspore +#endif // MINDSPORE_LITE_SRC_LITERT_DELEGATE_PNNA_OP_TRANSPOSE_PNNA_H_ diff --git a/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.cc b/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.cc index ee47d6d5d360387659592142f2ddf397a3681066..f28326cb0cdab5df6e88148c25123d99eb5a362c 100644 --- a/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.cc +++ b/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.cc @@ -25,6 +25,8 @@ namespace mindspore { namespace lite { Status PNNADelegate::Init() { ctx_ = pnna::Context::Create(); + op_func_lists_.clear(); + op_func_lists_ = {{schema::PrimitiveType_Transpose, GetPNNAOp}}; return mindspore::kSuccess; } diff --git a/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.h b/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.h index 1c25f6d9b0a7f464ab1b4aae8871671bbec49307..06ba765401f5d123cf4148d7e88be9c27553b48c 100644 --- a/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.h +++ b/mindspore-lite/src/litert/delegate/pnna/pnna_delegate.h @@ -23,9 +23,10 @@ #include #include #include -#include "include/cxx_api/delegate.h" +#include "include/api/delegate.h" #include "src/litert/delegate/pnna/pnna_subgraph.h" #include "src/litert/delegate/pnna/op/pnna_op.h" +#include "src/litert/delegate/pnna/op/transpose_pnna.h" namespace mindspore { namespace lite { diff --git a/mindspore-lite/src/litert/delegate/pnna/pnna_subgraph.h b/mindspore-lite/src/litert/delegate/pnna/pnna_subgraph.h index ae6d851369f7e280692bf16ec736304c07a76149..e1bda58de987cb5f068b88fc56b1f3cf8f3b9fb0 100644 --- a/mindspore-lite/src/litert/delegate/pnna/pnna_subgraph.h +++ b/mindspore-lite/src/litert/delegate/pnna/pnna_subgraph.h @@ -22,7 +22,7 @@ #include #include #include -#include "include/cxx_api/kernel.h" +#include "include/api/kernel.h" #include "src/common/log_adapter.h" #include "src/litert/delegate/pnna/op/pnna_op.h" #include "src/litert/delegate/pnna/pnna_utils.h" diff --git a/mindspore-lite/src/litert/delegate/pnna/pnna_utils.h b/mindspore-lite/src/litert/delegate/pnna/pnna_utils.h index 29d3870aa7057ec9f6f08717ef1ef4f1b85f8c21..ef62df7c47fbd85bba6730e6261e6fea1b08cb96 100644 --- a/mindspore-lite/src/litert/delegate/pnna/pnna_utils.h +++ b/mindspore-lite/src/litert/delegate/pnna/pnna_utils.h @@ -22,7 +22,7 @@ #include #include #include -#include "include/cxx_api/types.h" +#include "include/api/types.h" #include "pnna_core.h" // NOLINT(build/include_subdir) #define kNCHW_N 0 diff --git a/mindspore-lite/test/ut/src/runtime/kernel/dsp/dsp_test.h b/mindspore-lite/test/ut/src/runtime/kernel/dsp/dsp_test.h index 88419f42d7e853af569ac4d207993293a3f96258..0ee9e34d33a0c787a5f7ca5d2d6c7e543466a64e 100644 --- a/mindspore-lite/test/ut/src/runtime/kernel/dsp/dsp_test.h +++ b/mindspore-lite/test/ut/src/runtime/kernel/dsp/dsp_test.h @@ -19,11 +19,13 @@ #include #include +#include #include "schema/inner/model_generated.h" #include "src/litert/kernel_registry.h" #include "src/litert/kernel/dsp/dsp_subgraph.h" #include "common/common_test.h" #include "nnacl_c/arithmetic_parameter.h" +#include "nnacl_c/int8/quantize.h" namespace mindspore::lite::dsp::test { @@ -50,6 +52,14 @@ class DSPCommonTest : public CommonTest { dsp::DSPRuntimeInnerWrapper *dsp_runtime_wrapper_{nullptr}; std::shared_ptr allocator_; }; + +inline void QuantProcess(float *input, int len, float min, float max, float *scale, int *zero_point, int8_t *output) { + *scale = (max - min) / (std::numeric_limits::max() - std::numeric_limits::min()); + *zero_point = std::numeric_limits::max() - max / (*scale); + if (output) { + Quantize(input, len, *scale, *zero_point, output); + } +} } // namespace mindspore::lite::dsp::test #endif // MINDSPORE_LITE_TEST_UT_SRC_RUNTIME_KERNEL_DSP_DSP_TEST_H_ diff --git a/mindspore-lite/test/ut/src/runtime/kernel/dsp/transpose_tests.cc b/mindspore-lite/test/ut/src/runtime/kernel/dsp/transpose_tests.cc new file mode 100644 index 0000000000000000000000000000000000000000..a15aca5e7cb58e017e9fcbbb01716b0d9ff6eb04 --- /dev/null +++ b/mindspore-lite/test/ut/src/runtime/kernel/dsp/transpose_tests.cc @@ -0,0 +1,290 @@ +/** + * Copyright 2025 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "ut/src/runtime/kernel/dsp/dsp_test.h" +#include "include/api/context.h" +#include "include/api/data_type.h" +#include "include/api/model.h" +#include "nnacl_c/exp_parameter.h" +#include "schema/inner/model_generated.h" +#include "src/litert/kernel/dsp/dsp_subgraph.h" +#include "src/litert/kernel_registry.h" +#include "nnacl_c/int8/quantize.h" + +namespace mindspore::lite::dsp::test { +class TestDSP_Transpose : public DSPCommonTest {}; + +#ifdef SUPPORT_FT78 +TEST_F(TestDSP_Transpose, shape_1_3_2_3_float32_1_3_3_2) { + auto meta_graph = std::make_shared(); + meta_graph->name = "graph"; + + auto node = std::make_unique(); + node->inputIndex = {0, 1}; + node->outputIndex = {2}; + node->primitive = std::make_unique(); + node->primitive->value.type = schema::PrimitiveType_Transpose; + auto primitive = new schema::TransposeT; + node->primitive->value.value = primitive; + node->name = "Transpose"; + meta_graph->nodes.emplace_back(std::move(node)); + meta_graph->inputIndex = {0}; + meta_graph->outputIndex = {2}; + + auto input0 = std::make_unique(); + input0->nodeType = lite::NodeType_Parameter; + input0->dataType = TypeId::kNumberTypeFloat32; + input0->dims = {1, 3, 2, 3}; + input0->offset = -1; + meta_graph->allTensors.emplace_back(std::move(input0)); + + auto perm = std::make_unique(); + perm->nodeType = lite::NodeType_ValueNode; + perm->dataType = TypeId::kNumberTypeInt32; + perm->dims = {4}; + perm->offset = -1; + std::vector perm_data = {0, 3, 1, 2}; + perm->data.resize(sizeof(int32_t) * 4); + memcpy(perm->data.data(), perm_data.data(), 4 * sizeof(int32_t)); + meta_graph->allTensors.emplace_back(std::move(perm)); + + auto output = std::make_unique(); + output->nodeType = lite::NodeType_Parameter; + output->dataType = TypeId::kNumberTypeFloat32; + output->offset = -1; + meta_graph->allTensors.emplace_back(std::move(output)); + + flatbuffers::FlatBufferBuilder builder(1024); + auto offset = schema::MetaGraph::Pack(builder, meta_graph.get()); + builder.Finish(offset); + schema::FinishMetaGraphBuffer(builder, offset); + size_t size = builder.GetSize(); + const char *content = reinterpret_cast(builder.GetBufferPointer()); + + // create a context + auto context = std::make_shared(); + context->SetBuiltInDelegate(mindspore::DelegateMode::kPNNA); + auto &device_list = context->MutableDeviceInfo(); + std::shared_ptr device_info = std::make_shared(); + device_list.push_back(device_info); + + // build a model + auto model = std::make_shared(); + auto ret = model->Build(content, size, kMindIR_Lite, context); + ASSERT_EQ(kSuccess, ret.StatusCode()); + auto inputs = model->GetInputs(); + ASSERT_EQ(inputs.size(), 1); + auto inTensor = inputs.front(); + auto impl = inTensor.impl(); + ASSERT_NE(nullptr, impl); + float *in0_data = static_cast(inTensor.MutableData()); + std::vector input_data = {0.59885779, 0.62662862, 0.63011179, 0.82569427, 0.64772359, 0.42895413, + 0.30216458, 0.01351635, 0.32545444, 0.0360674, 0.33967769, 0.18092504, + 0.09479915, 0.52258112, 0.46735646, 0.95689111, 0.51619059, 0.82685718}; + std::vector expect = {0.59885776, 0.82569426, 0.30216458, 0.0360674, 0.09479915, 0.9568911, + 0.62662864, 0.6477236, 0.01351635, 0.3396777, 0.5225811, 0.5161906, + 0.6301118, 0.4289541, 0.32545444, 0.18092504, 0.46735647, 0.8268572}; + for (size_t i = 0; i < input_data.size(); ++i) in0_data[i] = input_data[i]; + std::vector outputs; + ret = model->Predict(inputs, &outputs); + ASSERT_EQ(kSuccess, ret.StatusCode()); + ASSERT_EQ(outputs.size(), 1); + auto *outData = reinterpret_cast(outputs.front().Data().get()); + ASSERT_EQ(0, CompareOutputData(outData, expect.data(), expect.size(), 1e-3f)); +} + +TEST_F(TestDSP_Transpose, shape_1_3_3_2_float32_1_3_2_3) { + auto meta_graph = std::make_shared(); + meta_graph->name = "graph"; + + auto node = std::make_unique(); + node->inputIndex = {0, 1}; + node->outputIndex = {2}; + node->primitive = std::make_unique(); + node->primitive->value.type = schema::PrimitiveType_Transpose; + auto primitive = new schema::TransposeT; + node->primitive->value.value = primitive; + node->name = "Transpose"; + meta_graph->nodes.emplace_back(std::move(node)); + meta_graph->inputIndex = {0}; + meta_graph->outputIndex = {2}; + + auto input0 = std::make_unique(); + input0->nodeType = lite::NodeType_Parameter; + input0->dataType = TypeId::kNumberTypeFloat32; + input0->dims = {1, 3, 3, 2}; + input0->offset = -1; + meta_graph->allTensors.emplace_back(std::move(input0)); + + auto perm = std::make_unique(); + perm->nodeType = lite::NodeType_ValueNode; + perm->dataType = TypeId::kNumberTypeInt32; + perm->dims = {4}; + perm->offset = -1; + std::vector perm_data = {0, 2, 3, 1}; + perm->data.resize(sizeof(int32_t) * 4); + memcpy(perm->data.data(), perm_data.data(), 4 * sizeof(int32_t)); + meta_graph->allTensors.emplace_back(std::move(perm)); + + auto output = std::make_unique(); + output->nodeType = lite::NodeType_Parameter; + output->dataType = TypeId::kNumberTypeFloat32; + output->offset = -1; + meta_graph->allTensors.emplace_back(std::move(output)); + + flatbuffers::FlatBufferBuilder builder(1024); + auto offset = schema::MetaGraph::Pack(builder, meta_graph.get()); + builder.Finish(offset); + schema::FinishMetaGraphBuffer(builder, offset); + size_t size = builder.GetSize(); + const char *content = reinterpret_cast(builder.GetBufferPointer()); + + // create a context + auto context = std::make_shared(); + context->SetBuiltInDelegate(mindspore::DelegateMode::kPNNA); + auto &device_list = context->MutableDeviceInfo(); + std::shared_ptr device_info = std::make_shared(); + device_list.push_back(device_info); + + // build a model + auto model = std::make_shared(); + auto ret = model->Build(content, size, kMindIR_Lite, context); + ASSERT_EQ(kSuccess, ret.StatusCode()); + auto inputs = model->GetInputs(); + ASSERT_EQ(inputs.size(), 1); + auto inTensor = inputs.front(); + auto impl = inTensor.impl(); + ASSERT_NE(nullptr, impl); + float *in0_data = static_cast(inTensor.MutableData()); + std::vector input_data = {0.59885779, 0.62662862, 0.63011179, 0.82569427, 0.64772359, 0.42895413, + 0.30216458, 0.01351635, 0.32545444, 0.0360674, 0.33967769, 0.18092504, + 0.09479915, 0.52258112, 0.46735646, 0.95689111, 0.51619059, 0.82685718}; + std::vector expect = {0.59885776, 0.30216458, 0.09479915, 0.62662864, 0.01351635, 0.5225811, + 0.6301118, 0.32545444, 0.46735647, 0.82569426, 0.0360674, 0.9568911, + 0.6477236, 0.3396777, 0.5161906, 0.42895412, 0.18092504, 0.8268572}; + for (size_t i = 0; i < input_data.size(); ++i) in0_data[i] = input_data[i]; + std::vector outputs; + ret = model->Predict(inputs, &outputs); + ASSERT_EQ(kSuccess, ret.StatusCode()); + ASSERT_EQ(outputs.size(), 1); + auto *outData = reinterpret_cast(outputs.front().Data().get()); + ASSERT_EQ(0, CompareOutputData(outData, expect.data(), expect.size(), 1e-3f)); +} + +TEST_F(TestDSP_Transpose, shape_1_3_2_3_int8_1_3_3_2) { + auto meta_graph = std::make_shared(); + meta_graph->name = "graph"; + + auto node = std::make_unique(); + node->inputIndex = {0, 1}; + node->outputIndex = {2}; + node->primitive = std::make_unique(); + node->primitive->value.type = schema::PrimitiveType_Transpose; + auto primitive = new schema::TransposeT; + node->primitive->value.value = primitive; + node->name = "Transpose"; + meta_graph->nodes.emplace_back(std::move(node)); + meta_graph->inputIndex = {0}; + meta_graph->outputIndex = {2}; + + int length = 3 * 3 * 2; + std::vector in0 = {0.59885779, 0.62662862, 0.63011179, 0.82569427, 0.64772359, 0.42895413, + 0.30216458, 0.01351635, 0.32545444, 0.0360674, 0.33967769, 0.18092504, + 0.09479915, 0.52258112, 0.46735646, 0.95689111, 0.51619059, 0.82685718}; + std::vector input0_data(length); + float in0_scale; + int in0_zp; + QuantProcess(in0.data(), length, 0, 1, &in0_scale, &in0_zp, input0_data.data()); + + std::vector expect = {0.59885776, 0.82569426, 0.30216458, 0.0360674, 0.09479915, 0.9568911, + 0.62662864, 0.6477236, 0.01351635, 0.3396777, 0.5225811, 0.5161906, + 0.6301118, 0.4289541, 0.32545444, 0.18092504, 0.46735647, 0.8268572}; + float out_scale; + int out_zp; + QuantProcess(expect.data(), length, 0, 1, &out_scale, &out_zp, nullptr); + + auto input_quant0 = std::make_unique(); + input_quant0->scale = in0_scale; + input_quant0->zeroPoint = in0_zp; + + auto out_quant = std::make_unique(); + out_quant->scale = out_scale; + out_quant->zeroPoint = out_zp; + + auto input0 = std::make_unique(); + input0->nodeType = lite::NodeType_Parameter; + input0->dataType = TypeId::kNumberTypeInt8; + input0->dims = {1, 3, 2, 3}; + input0->offset = -1; + input0->quantParams.emplace_back(std::move(input_quant0)); + meta_graph->allTensors.emplace_back(std::move(input0)); + + auto perm = std::make_unique(); + perm->nodeType = lite::NodeType_ValueNode; + perm->dataType = TypeId::kNumberTypeInt32; + perm->dims = {4}; + perm->offset = -1; + std::vector perm_data = {0, 3, 1, 2}; + perm->data.resize(sizeof(int32_t) * 4); + memcpy(perm->data.data(), perm_data.data(), 4 * sizeof(int32_t)); + meta_graph->allTensors.emplace_back(std::move(perm)); + + auto output = std::make_unique(); + output->nodeType = lite::NodeType_Parameter; + output->dataType = TypeId::kNumberTypeInt8; + output->offset = -1; + output->quantParams.emplace_back(std::move(out_quant)); + meta_graph->allTensors.emplace_back(std::move(output)); + + flatbuffers::FlatBufferBuilder builder(1024); + auto offset = schema::MetaGraph::Pack(builder, meta_graph.get()); + builder.Finish(offset); + schema::FinishMetaGraphBuffer(builder, offset); + size_t size = builder.GetSize(); + const char *content = reinterpret_cast(builder.GetBufferPointer()); + + // create a context + auto context = std::make_shared(); + context->SetBuiltInDelegate(mindspore::DelegateMode::kPNNA); + auto &device_list = context->MutableDeviceInfo(); + std::shared_ptr device_info = std::make_shared(); + device_list.push_back(device_info); + + // build a model + auto model = std::make_shared(); + auto ret = model->Build(content, size, kMindIR_Lite, context); + ASSERT_EQ(kSuccess, ret.StatusCode()); + auto inputs = model->GetInputs(); + ASSERT_EQ(inputs.size(), 1); + auto inTensor = inputs.front(); + auto impl = inTensor.impl(); + ASSERT_NE(nullptr, impl); + int8_t *in0_data = static_cast(inTensor.MutableData()); + for (size_t i = 0; i < inputs[0].ElementNum(); ++i) in0_data[i] = input0_data[i]; + std::vector outputs; + ret = model->Predict(inputs, &outputs); + ASSERT_EQ(kSuccess, ret.StatusCode()); + ASSERT_EQ(outputs.size(), 1); + std::vector out(length); + auto *outData = reinterpret_cast(outputs.front().Data().get()); + Dequantize(outData, length, out_scale, out_zp, out.data()); + ASSERT_EQ(0, CompareOutputData(out.data(), expect.data(), length, 1e-3)); +} +#endif +} // namespace mindspore::lite::dsp::test