// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT 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 "lite/core/subgraph_bridge_registry.h" #include "lite/kernels/apu/bridges/graph.h" #include "lite/kernels/apu/bridges/utility.h" namespace paddle { namespace lite { namespace subgraph { namespace apu { int ConcatConverter(void* ctx, OpLite* op, KernelBase* kernel) { CHECK(ctx != nullptr); CHECK(op != nullptr); auto graph = static_cast(ctx); auto model = graph->model(); auto op_info = op->op_info(); auto op_type = op_info->Type(); auto scope = op->scope(); int neuron_errCode; VLOG(3) << "[APU] Converting [" << op_type << "]"; // Get input and output vars and op attributes auto x_names = op_info->Input("X"); auto out_name = op_info->Output("Out").front(); auto axis = op_info->GetAttr("axis"); auto num = x_names.size(); // Process data layout axis change if (axis == 1) axis = 3; else if (axis == 2) axis = 1; else if (axis == 3) axis = 2; // Limitation: // All input tensors of NEURON_TENSOR_QUANT8_ASYMM must // have the same scale and zeroPoint as the output tensor CHECK(op_info->HasOutputScale(out_name)); auto output_scale = op_info->GetOutputScale(out_name)[0]; // Traverse all of input nodes std::vector> input_nodes; NeuronOperandType xType; for (auto& x_name : x_names) { auto x = scope->FindMutableTensor(x_name); auto x_dims = x->dims(); std::shared_ptr x_node = nullptr; CHECK(op_info->HasInputScale(x_name)); auto input_scale = op_info->GetInputScale(x_name)[0]; // Add x tensor type xType.type = NEURON_TENSOR_QUANT8_ASYMM; xType.scale = input_scale; xType.zeroPoint = 128; xType.dimensionCount = x_dims.size(); std::vector dims_x = {(uint32_t)x_dims[0], (uint32_t)x_dims[2], (uint32_t)x_dims[3], (uint32_t)x_dims[1]}; xType.dimensions = &dims_x[0]; if (graph->Has(x_name)) { VLOG(3) << "Graph has " << x_name; if (graph->IsInput(x_name)) { VLOG(3) << x_name << "is input and already exist"; x_name = "transpose_" + x_name; } if (graph->IsOutput(x_name)) { VLOG(3) << x_name << "is input and output node"; x_name = "transpose_" + x_name; } x_node = graph->Get(x_name); } else { // Add input operand if (graph->IsInput(x_name)) { // Insert transpose for NCHW -> NHWC insert_transpose_node(ctx, x_name, "transpose_" + x_name, {(uint32_t)x_dims[0], (uint32_t)x_dims[1], (uint32_t)x_dims[2], (uint32_t)x_dims[3]}, dims_x, {0, 2, 3, 1}, xType.scale, xType.zeroPoint); // Change x_name because we add transpose op x_name = "transpose_" + x_name; x_node = graph->Get(x_name); } else { NeuronModel_addOperand(model, &xType); x_node = graph->Add(x_name, dims_x); } } // End of else if (x_node == nullptr) return subgraph::FAILED; input_nodes.push_back(x_node); VLOG(3) << "input node x: " << x_node->index() << ": input_scale: " << input_scale << " x_dims:" << x_dims[0] << ":" << x_dims[1] << ":" << x_dims << ", inType: " << xType.dimensions[0] << ":" << xType.dimensions[1] << ":" << xType.dimensions[2] << ":" << xType.dimensions[3]; } // End of for if (input_nodes.size() != num) { LOG(WARNING) << "Create input operand failed!"; return subgraph::FAILED; } // Add axis operand type NeuronOperandType int32Type; int32Type.type = NEURON_INT32; int32Type.dimensionCount = 0; std::vector dims_int32 = {1}; // Add axis operand std::shared_ptr axis_node = nullptr; NeuronModel_addOperand(model, &int32Type); // axis axis_node = graph->Add(out_name + "_axis", dims_int32); VLOG(3) << "axis:" << axis; // Add out operand type auto out = scope->FindMutableTensor(out_name); auto out_dims = out->dims(); NeuronOperandType outType; outType.type = NEURON_TENSOR_QUANT8_ASYMM; outType.scale = output_scale; outType.zeroPoint = 128; outType.dimensionCount = out_dims.size(); std::vector dims_out = {(uint32_t)out_dims[0], (uint32_t)out_dims[2], (uint32_t)out_dims[3], (uint32_t)out_dims[1]}; outType.dimensions = &dims_out[0]; // Add out operand std::shared_ptr out_node = nullptr; if (graph->Has(out_name)) { out_node = graph->Get(out_name); } else { if (graph->IsOutput(out_name)) { NeuronModel_addOperand(model, &outType); out_node = graph->Add("transpose_" + out_name, dims_out); } else { NeuronModel_addOperand(model, &outType); out_node = graph->Add(out_name, dims_out); } } VLOG(3) << "out node idx: " << out_node->index() << ": output_scle: " << outType.scale << ", outType: " << outType.dimensions[0] << ":" << outType.dimensions[1] << ":" << outType.dimensions[2] << ":" << outType.dimensions[3]; // Set axis value int32_t axis_val[1] = {(int32_t)axis}; NeuronModel_setOperandValue( model, axis_node->index(), axis_val, sizeof(int32_t) * 1); std::vector addInIndex; for (auto& node : input_nodes) { addInIndex.push_back(node->index()); } addInIndex.push_back(axis_node->index()); std::vector addOutIndex = {out_node->index()}; neuron_errCode = NeuronModel_addOperation(model, NEURON_CONCATENATION, addInIndex.size(), &addInIndex[0], addOutIndex.size(), &addOutIndex[0]); if (NEURON_NO_ERROR != neuron_errCode) { LOG(WARNING) << "Add op fail:" << op_type; return subgraph::FAILED; } if (graph->IsOutput(out_name)) { // Insert transpose for NHWC -> NCHW insert_transpose_node(ctx, "transpose_" + out_name, out_name, dims_out, {(uint32_t)out_dims[0], (uint32_t)out_dims[1], (uint32_t)out_dims[2], (uint32_t)out_dims[3]}, {0, 3, 1, 2}, outType.scale, outType.zeroPoint); out_node = graph->Get(out_name); if (out_node == nullptr) return subgraph::FAILED; } return SUCCESS; } } // namespace apu } // namespace subgraph } // namespace lite } // namespace paddle REGISTER_SUBGRAPH_BRIDGE(concat, kAPU, paddle::lite::subgraph::apu::ConcatConverter);