chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
// Created by Abdelrauf 2020
|
||||
|
||||
#include "armcomputeUtils.h"
|
||||
|
||||
#include <helpers/LoopsCoordsHelper.h>
|
||||
#include <ops/declarable/OpRegistrator.h>
|
||||
#include <ops/declarable/PlatformHelper.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <system/platform_boilerplate.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace platforms {
|
||||
|
||||
Arm_DataType getArmType(const DataType& dType) {
|
||||
Arm_DataType ret;
|
||||
switch (dType) {
|
||||
case HALF:
|
||||
ret = Arm_DataType::F16;
|
||||
break;
|
||||
case FLOAT32:
|
||||
ret = Arm_DataType::F32;
|
||||
break;
|
||||
case DOUBLE:
|
||||
ret = Arm_DataType::F64;
|
||||
break;
|
||||
case INT8:
|
||||
ret = Arm_DataType::S8;
|
||||
break;
|
||||
case INT16:
|
||||
ret = Arm_DataType::S16;
|
||||
break;
|
||||
case INT32:
|
||||
ret = Arm_DataType::S32;
|
||||
break;
|
||||
case INT64:
|
||||
ret = Arm_DataType::S64;
|
||||
break;
|
||||
case UINT8:
|
||||
ret = Arm_DataType::U8;
|
||||
break;
|
||||
case UINT16:
|
||||
ret = Arm_DataType::U16;
|
||||
break;
|
||||
case UINT32:
|
||||
ret = Arm_DataType::U32;
|
||||
break;
|
||||
case UINT64:
|
||||
ret = Arm_DataType::U64;
|
||||
break;
|
||||
case BFLOAT16:
|
||||
ret = Arm_DataType::BFLOAT16;
|
||||
break;
|
||||
default:
|
||||
ret = Arm_DataType::UNKNOWN;
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isArmcomputeFriendly(NDArray& arr) {
|
||||
auto dType = getArmType(arr.dataType());
|
||||
int rank = (int)(arr.rankOf());
|
||||
int ind = arr.ordering() == 'c' ? rank - 1 : 0;
|
||||
auto arrStrides = arr.stridesOf();
|
||||
return dType != Arm_DataType::UNKNOWN && rank <= arm_compute::MAX_DIMS && arr.ordering() == 'c' &&
|
||||
arrStrides[ind] == 1;
|
||||
}
|
||||
|
||||
Arm_TensorInfo getArmTensorInfo(int rank, sd::LongType* bases, sd::DataType ndArrayType,
|
||||
arm_compute::DataLayout layout) {
|
||||
constexpr int numChannels = 1;
|
||||
auto dType = getArmType(ndArrayType);
|
||||
|
||||
Arm_TensorShape shape;
|
||||
shape.set_num_dimensions(rank);
|
||||
for (int i = 0, j = rank - 1; i < rank; i++, j--) {
|
||||
shape[i] = static_cast<uint32_t>(bases[j]);
|
||||
}
|
||||
// fill the rest unused with 1
|
||||
for (int i = rank; i < arm_compute::MAX_DIMS; i++) {
|
||||
shape[i] = 1;
|
||||
}
|
||||
|
||||
return Arm_TensorInfo(shape, numChannels, dType, layout);
|
||||
}
|
||||
|
||||
Arm_TensorInfo getArmTensorInfo(NDArray& arr, arm_compute::DataLayout layout) {
|
||||
auto dType = getArmType(arr.dataType());
|
||||
|
||||
internal_print_nd_shape(arr, "shape");
|
||||
internal_print_nd_array(arr, "data");
|
||||
//
|
||||
constexpr int numChannels = 1;
|
||||
int rank = (int)(arr.rankOf());
|
||||
auto bases = arr.shapeOf();
|
||||
auto arrStrides = arr.stridesOf();
|
||||
|
||||
// https://arm-software.github.io/ComputeLibrary/v20.05/_dimensions_8h_source.xhtml
|
||||
// note: underhood it is stored as std::array<T, num_SD_MAX_DIMENSIONs> _id;
|
||||
// TensorShape is derived from Dimensions<uint32_t>
|
||||
// as well as Strides : public Dimensions<uint32_t>
|
||||
Arm_TensorShape shape;
|
||||
Arm_Strides strides;
|
||||
shape.set_num_dimensions(rank);
|
||||
strides.set_num_dimensions(rank);
|
||||
size_t element_size = arr.sizeOfT();
|
||||
for (int i = 0, j = rank - 1; i < rank; i++, j--) {
|
||||
shape[i] = static_cast<uint32_t>(bases[j]);
|
||||
strides[i] = static_cast<uint32_t>(arrStrides[j] * element_size);
|
||||
}
|
||||
// fill the rest unused with 1
|
||||
for (int i = rank; i < arm_compute::MAX_DIMS; i++) {
|
||||
shape[i] = 1;
|
||||
}
|
||||
|
||||
size_t total_size = arr.lengthOf() * element_size;
|
||||
size_t offset = 0;
|
||||
if (arr.hasPaddedBuffer()) {
|
||||
internal_printf("---has padded buffer %d\n", 0);
|
||||
total_size = arr.getDataBuffer()->getLenInBytes();
|
||||
offset = arr.offset() * element_size;
|
||||
}
|
||||
internal_printf(":: offset %d el size %d arr.getDataBuffer()->getLenInBytes() %d lengthof %d \n",
|
||||
(int)arr.offset(), (int)element_size, (int)arr.getDataBuffer()->getLenInBytes(),
|
||||
(int)arr.lengthOf());
|
||||
Arm_TensorInfo info;
|
||||
info.init(shape, numChannels, dType, strides, offset, total_size);
|
||||
info.set_data_layout(layout);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
Arm_Tensor getArmTensor(NDArray& arr, arm_compute::DataLayout layout) {
|
||||
// - Ownership of the backing memory is not transferred to the tensor itself.
|
||||
// - The tensor mustn't be memory managed.
|
||||
// - Padding requirements should be accounted by the client code.
|
||||
// In other words, if padding is required by the tensor after the function
|
||||
// configuration step, then the imported backing memory should account for it.
|
||||
// Padding can be checked through the TensorInfo::padding() interface.
|
||||
|
||||
// Import existing pointer as backing memory
|
||||
auto info = getArmTensorInfo(arr, layout);
|
||||
Arm_Tensor tensor;
|
||||
tensor.allocator()->init(info);
|
||||
// get without offset
|
||||
void* buff = arr.getDataBuffer()->primary();
|
||||
tensor.allocator()->import_memory(buff);
|
||||
return tensor;
|
||||
}
|
||||
|
||||
void copyFromTensor(const Arm_Tensor& inTensor, sd::NDArray& output) {
|
||||
// only for C order
|
||||
if (output.ordering() != 'c') return;
|
||||
sd::LongType* shapeInfo = output.shapeInfo();
|
||||
sd::LongType* bases = &(shapeInfo[1]);
|
||||
sd::LongType rank = shapeInfo[0];
|
||||
sd::LongType* strides = output.stridesOf();
|
||||
int width = bases[rank - 1];
|
||||
uint8_t* outputBuffer = (uint8_t*)output.buffer();
|
||||
size_t offset = 0;
|
||||
arm_compute::Window window;
|
||||
arm_compute::Iterator tensor_it(&inTensor, window);
|
||||
|
||||
int element_size = inTensor.info()->element_size();
|
||||
window.use_tensor_dimensions(inTensor.info()->tensor_shape(), /* first_dimension =*/arm_compute::Window::DimY);
|
||||
|
||||
|
||||
sd::LongType coords[SD_MAX_RANK] = {};
|
||||
auto copySize = width * element_size;
|
||||
arm_compute::execute_window_loop(
|
||||
window,
|
||||
[&](const arm_compute::Coordinates& id) {
|
||||
auto src = tensor_it.ptr();
|
||||
auto dest = outputBuffer + offset * element_size;
|
||||
memcpy(dest, src, copySize);
|
||||
offset = sd::inc_coords(bases, strides, coords, offset, rank, 1);
|
||||
},
|
||||
tensor_it);
|
||||
|
||||
}
|
||||
|
||||
void copyToTensor(sd::NDArray& input, Arm_Tensor& outTensor) {
|
||||
// only for C order
|
||||
if (input.ordering() != 'c') return;
|
||||
sd::LongType* shapeInfo = input.shapeInfo();
|
||||
sd::LongType* bases = &(shapeInfo[1]);
|
||||
sd::LongType rank = shapeInfo[0];
|
||||
sd::LongType* strides = input.stridesOf();
|
||||
uint8_t* inputBuffer = (uint8_t*)input.buffer();
|
||||
int width = bases[rank - 1];
|
||||
size_t offset = 0;
|
||||
arm_compute::Window window;
|
||||
arm_compute::Iterator tensor_it(&outTensor, window);
|
||||
int element_size = outTensor.info()->element_size();
|
||||
|
||||
window.use_tensor_dimensions(outTensor.info()->tensor_shape(), /* first_dimension =*/arm_compute::Window::DimY);
|
||||
|
||||
sd::LongType coords[SD_MAX_RANK] = {};
|
||||
auto copySize = width * element_size;
|
||||
arm_compute::execute_window_loop(
|
||||
window,
|
||||
[&](const arm_compute::Coordinates& id) {
|
||||
auto dest = tensor_it.ptr();
|
||||
auto src = inputBuffer + offset * element_size;
|
||||
memcpy(dest, src, copySize);
|
||||
offset = sd::inc_coords(bases, strides, coords, offset, rank, 1);
|
||||
},
|
||||
tensor_it);
|
||||
|
||||
}
|
||||
|
||||
// armcompute should be built with debug option
|
||||
void print_tensor(Arm_ITensor& tensor, const char* msg) {
|
||||
auto info = tensor.info();
|
||||
auto padding = info->padding();
|
||||
std::cout << msg << "\ntotal: " << info->total_size() << "\n";
|
||||
|
||||
for (int i = 0; i < arm_compute::MAX_DIMS; i++) {
|
||||
std::cout << info->dimension(i) << ",";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
for (int i = 0; i < arm_compute::MAX_DIMS; i++) {
|
||||
std::cout << info->strides_in_bytes()[i] << ",";
|
||||
}
|
||||
std::cout << "\npadding: l " << padding.left << ", r " << padding.right << ", t " << padding.top << ", b "
|
||||
<< padding.bottom << std::endl;
|
||||
|
||||
#ifdef ARM_COMPUTE_ASSERTS_ENABLED
|
||||
// note it did not print correctly fro NHWC
|
||||
std::cout << msg << ":\n";
|
||||
tensor.print(std::cout);
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace platforms
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
// Created by Abdelrauf 2020
|
||||
|
||||
#ifndef DEV_TESTSARMCOMPUTEUTILS_H
|
||||
#define DEV_TESTSARMCOMPUTEUTILS_H
|
||||
|
||||
#include <arm_compute/core/Helpers.h>
|
||||
#include <arm_compute/core/ITensor.h>
|
||||
#include <arm_compute/core/Strides.h>
|
||||
#include <arm_compute/core/TensorInfo.h>
|
||||
#include <arm_compute/core/TensorShape.h>
|
||||
#include <arm_compute/core/Types.h>
|
||||
#include <arm_compute/core/Validate.h>
|
||||
#include <arm_compute/core/Window.h>
|
||||
#include <arm_compute/runtime/NEON/NEFunctions.h>
|
||||
#include <arm_compute/runtime/Tensor.h>
|
||||
#include <arm_compute/runtime/TensorAllocator.h>
|
||||
#include <array/NDArray.h>
|
||||
#include <graph/Context.h>
|
||||
#include <legacy/NativeOps.h>
|
||||
#include <ops/declarable/PlatformHelper.h>
|
||||
#include <system/platform_boilerplate.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace samediff;
|
||||
|
||||
#if 0
|
||||
#define internal_printf(FORMAT, ...) sd_printf(FORMAT, __VA_ARGS__)
|
||||
#define internal_print_arm_array(a, b) print_tensor(a, b)
|
||||
#define internal_print_nd_array(a, b) ((a).printIndexedBuffer(b))
|
||||
#define internal_print_nd_shape(a, b) ((a).printShapeInfo(b))
|
||||
#else
|
||||
#define internal_printf(FORMAT, ...)
|
||||
#define internal_print_arm_array(a, b)
|
||||
#define internal_print_nd_array(a, b)
|
||||
#define internal_print_nd_shape(a, b)
|
||||
#endif
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace platforms {
|
||||
|
||||
using Arm_DataType = arm_compute::DataType;
|
||||
using Arm_Tensor = arm_compute::Tensor;
|
||||
using Arm_ITensor = arm_compute::ITensor;
|
||||
using Arm_TensorInfo = arm_compute::TensorInfo;
|
||||
using Arm_TensorShape = arm_compute::TensorShape;
|
||||
using Arm_Strides = arm_compute::Strides;
|
||||
using Arm_WeightsInfo = arm_compute::WeightsInfo;
|
||||
using Arm_PermutationVector = arm_compute::PermutationVector;
|
||||
using Arm_DataLayout = arm_compute::DataLayout;
|
||||
|
||||
/**
|
||||
* Here we actually declare our platform helpers
|
||||
*/
|
||||
DECLARE_PLATFORM(maxpool2d, ENGINE_CPU);
|
||||
|
||||
DECLARE_PLATFORM(avgpool2d, ENGINE_CPU);
|
||||
|
||||
DECLARE_PLATFORM(conv2d, ENGINE_CPU);
|
||||
|
||||
DECLARE_PLATFORM(deconv2d, ENGINE_CPU);
|
||||
|
||||
// utils
|
||||
Arm_DataType getArmType(const sd::DataType& dType);
|
||||
|
||||
Arm_TensorInfo getArmTensorInfo(int rank, sd::LongType* bases, sd::DataType ndArrayType,
|
||||
Arm_DataLayout layout = Arm_DataLayout::UNKNOWN);
|
||||
|
||||
Arm_TensorInfo getArmTensorInfo(NDArray& arr, Arm_DataLayout layout = Arm_DataLayout::UNKNOWN);
|
||||
|
||||
Arm_Tensor getArmTensor(NDArray& arr, Arm_DataLayout layout = Arm_DataLayout::UNKNOWN);
|
||||
|
||||
void copyFromTensor(const Arm_Tensor& inTensor, NDArray& output);
|
||||
void copyToTensor(NDArray& input, Arm_Tensor& outTensor);
|
||||
void print_tensor(Arm_ITensor& tensor, const char* msg);
|
||||
bool isArmcomputeFriendly(NDArray& arr);
|
||||
|
||||
template <typename F>
|
||||
class ArmFunction {
|
||||
public:
|
||||
template <typename... Args>
|
||||
void configure(NDArray* input, NDArray* output, Arm_DataLayout layout, Args&&... args) {
|
||||
bool inputHasPaddedBuffer = input->hasPaddedBuffer();
|
||||
bool outputHasPaddedBuffer = output->hasPaddedBuffer();
|
||||
if (inputHasPaddedBuffer) {
|
||||
in = getArmTensor(*input, layout);
|
||||
internal_printf("input is a padded buffer %d\n", 0);
|
||||
} else {
|
||||
auto inInfo = getArmTensorInfo(*input, layout);
|
||||
in.allocator()->init(inInfo);
|
||||
}
|
||||
if (outputHasPaddedBuffer) {
|
||||
out = getArmTensor(*output, layout);
|
||||
internal_printf("output is a padded buffer %d\n", 0);
|
||||
} else {
|
||||
auto outInfo = getArmTensorInfo(*output, layout);
|
||||
out.allocator()->init(outInfo);
|
||||
}
|
||||
armFunction.configure(&in, &out, std::forward<Args>(args)...);
|
||||
if (!inputHasPaddedBuffer) {
|
||||
if (in.info()->has_padding()) {
|
||||
// allocate and copy
|
||||
in.allocator()->allocate();
|
||||
inputNd = input;
|
||||
} else {
|
||||
// import only for ews()==1
|
||||
in.allocator()->import_memory(input->buffer());
|
||||
internal_printf("input import %d\n", 0);
|
||||
}
|
||||
}
|
||||
if (!outputHasPaddedBuffer) {
|
||||
if (out.info()->has_padding()) {
|
||||
// store pointer to our array to copy after run
|
||||
out.allocator()->allocate();
|
||||
outNd = output;
|
||||
} else {
|
||||
// import only for ews()==1
|
||||
out.allocator()->import_memory(output->buffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
void run() {
|
||||
if (inputNd) {
|
||||
// copy
|
||||
copyToTensor(*inputNd, in);
|
||||
}
|
||||
armFunction.run();
|
||||
if (outNd) {
|
||||
copyFromTensor(out, *outNd);
|
||||
internal_printf("output copy %d\n", 0);
|
||||
internal_print_arm_array(out, "out");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Arm_Tensor in;
|
||||
Arm_Tensor out;
|
||||
NDArray* inputNd = nullptr;
|
||||
NDArray* outNd = nullptr;
|
||||
F armFunction{};
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
class ArmFunctionWeighted {
|
||||
public:
|
||||
template <typename... Args>
|
||||
void configure(NDArray* input, NDArray* weights, NDArray* biases, NDArray* output, Arm_DataLayout layout,
|
||||
arm_compute::PermutationVector permuteVector, Args&&... args) {
|
||||
bool inputHasPaddedBuffer = input->hasPaddedBuffer();
|
||||
bool weightsHasPaddedBuffer = weights->hasPaddedBuffer();
|
||||
bool outputHasPaddedBuffer = output->hasPaddedBuffer();
|
||||
bool biasesHasPaddedBuffer = false;
|
||||
if (inputHasPaddedBuffer) {
|
||||
in = getArmTensor(*input, layout);
|
||||
} else {
|
||||
in.allocator()->init(getArmTensorInfo(*input, layout));
|
||||
}
|
||||
if (weightsHasPaddedBuffer) {
|
||||
w = getArmTensor(*weights, layout);
|
||||
} else {
|
||||
w.allocator()->init(getArmTensorInfo(*weights, layout));
|
||||
}
|
||||
if (outputHasPaddedBuffer) {
|
||||
out = getArmTensor(*output, layout);
|
||||
} else {
|
||||
out.allocator()->init(getArmTensorInfo(*output, layout));
|
||||
}
|
||||
Arm_Tensor* bias_ptr = nullptr;
|
||||
if (biases) {
|
||||
biasesHasPaddedBuffer = biases->hasPaddedBuffer();
|
||||
if (biasesHasPaddedBuffer) {
|
||||
b = getArmTensor(*biases, layout);
|
||||
} else {
|
||||
b.allocator()->init(getArmTensorInfo(*biases, layout));
|
||||
}
|
||||
bias_ptr = &b;
|
||||
}
|
||||
if (permuteVector.num_dimensions() == 0) {
|
||||
armFunction.configure(&in, &w, bias_ptr, &out, std::forward<Args>(args)...);
|
||||
} else {
|
||||
// configure with permute kernel
|
||||
Arm_TensorShape shape;
|
||||
int rank = permuteVector.num_dimensions();
|
||||
shape.set_num_dimensions(rank);
|
||||
auto wInfoPtr = w.info();
|
||||
for (int i = 0; i < rank; i++) {
|
||||
shape[i] = wInfoPtr->dimension(permuteVector[i]);
|
||||
}
|
||||
for (int i = rank; i < arm_compute::MAX_DIMS; i++) {
|
||||
shape[i] = 1;
|
||||
}
|
||||
Arm_TensorInfo wPermInfo(shape, 1, wInfoPtr->data_type(), layout);
|
||||
wPerm.allocator()->init(wPermInfo);
|
||||
permuter.configure(&w, &wPerm, permuteVector);
|
||||
armFunction.configure(&in, &wPerm, bias_ptr, &out, std::forward<Args>(args)...);
|
||||
wPerm.allocator()->allocate();
|
||||
runPerm = true;
|
||||
}
|
||||
// import buffer
|
||||
if (!inputHasPaddedBuffer) {
|
||||
if (in.info()->has_padding()) {
|
||||
// allocate and copy
|
||||
in.allocator()->allocate();
|
||||
inputNd = input;
|
||||
} else {
|
||||
// import buffer
|
||||
in.allocator()->import_memory(input->buffer());
|
||||
}
|
||||
}
|
||||
if (!weightsHasPaddedBuffer) {
|
||||
if (w.info()->has_padding()) {
|
||||
// store pointer to our array to copy after run
|
||||
w.allocator()->allocate();
|
||||
wNd = weights;
|
||||
} else {
|
||||
// import
|
||||
w.allocator()->import_memory(weights->buffer());
|
||||
}
|
||||
}
|
||||
if (biases && !biasesHasPaddedBuffer) {
|
||||
if (b.info()->has_padding()) {
|
||||
// store pointer to our array to copy after run
|
||||
b.allocator()->allocate();
|
||||
bNd = biases;
|
||||
} else {
|
||||
// import
|
||||
b.allocator()->import_memory(biases->buffer());
|
||||
}
|
||||
}
|
||||
if (!outputHasPaddedBuffer) {
|
||||
if (out.info()->has_padding()) {
|
||||
// store pointer to our array to copy after run
|
||||
out.allocator()->allocate();
|
||||
outNd = output;
|
||||
} else {
|
||||
// import
|
||||
out.allocator()->import_memory(output->buffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
void run() {
|
||||
if (inputNd) {
|
||||
// copy
|
||||
copyToTensor(*inputNd, in);
|
||||
}
|
||||
if (bNd) {
|
||||
// copy
|
||||
copyToTensor(*bNd, b);
|
||||
}
|
||||
if (wNd) {
|
||||
// copy
|
||||
copyToTensor(*wNd, w);
|
||||
}
|
||||
if (runPerm) {
|
||||
permuter.run();
|
||||
}
|
||||
armFunction.run();
|
||||
if (outNd) {
|
||||
copyFromTensor(out, *outNd);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool runPerm = false;
|
||||
Arm_Tensor in;
|
||||
Arm_Tensor b;
|
||||
Arm_Tensor w;
|
||||
Arm_Tensor wPerm;
|
||||
Arm_Tensor out;
|
||||
NDArray* inputNd = nullptr;
|
||||
NDArray* wNd = nullptr;
|
||||
NDArray* bNd = nullptr;
|
||||
NDArray* outNd = nullptr;
|
||||
arm_compute::NEPermute permuter;
|
||||
F armFunction{};
|
||||
};
|
||||
|
||||
} // namespace platforms
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
|
||||
#endif // DEV_TESTSARMCOMPUTEUTILS_H
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
// Created by Abdelrauf (rauf@konduit.ai) 2020
|
||||
#include <ops/declarable/OpRegistrator.h>
|
||||
#include <ops/declarable/PlatformHelper.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <system/platform_boilerplate.h>
|
||||
|
||||
#include "armcomputeUtils.h"
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace platforms {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
PLATFORM_IMPL(avgpool2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0);
|
||||
auto output = OUTPUT_VARIABLE(0);
|
||||
|
||||
// 0,1 - kernel Height/Width; 2,3 - stride Height/Width; 4,5 - pad Height/Width; 6,7 - dilation Height/Width; 8 - same
|
||||
// mode;
|
||||
|
||||
const sd::LongType kH = INT_ARG(0);
|
||||
const sd::LongType kW = INT_ARG(1);
|
||||
const sd::LongType sH = INT_ARG(2);
|
||||
const sd::LongType sW = INT_ARG(3);
|
||||
sd::LongType pH = INT_ARG(4);
|
||||
sd::LongType pW = INT_ARG(5);
|
||||
const sd::LongType dH = INT_ARG(6);
|
||||
const sd::LongType dW = INT_ARG(7);
|
||||
const auto paddingMode = INT_ARG(8);
|
||||
const auto extraParam0 = INT_ARG(9);
|
||||
const int isNCHW = block.getIArguments()->size() > 10 ? !INT_ARG(10) : 1; // INT_ARG(10): 0-NCHW, 1-NHWC
|
||||
|
||||
REQUIRE_TRUE(input->rankOf() == 4, 0, "AVGPOOL2D ARMCOMPUTE op: input should have rank of 4, but got %i instead",
|
||||
input->rankOf());
|
||||
REQUIRE_TRUE(dH != 0 && dW != 0, 0, "AVGPOOL2D ARMCOMPUTE op: dilation must not be zero, but got instead {%i, %i}",
|
||||
dH, dW);
|
||||
|
||||
bool excludePadding = (extraParam0 == 0) ? true : false;
|
||||
|
||||
auto dataLayout = isNCHW ? arm_compute::DataLayout::NCHW : arm_compute::DataLayout::NHWC;
|
||||
|
||||
// Calculate individual paddings
|
||||
sd::LongType padLeft, padTop, padRight, padBottom;
|
||||
sd::LongType bS, iC, iH, iW, oC, oH,
|
||||
oW; // batch size, input channels, input height/width, output channels, output height/width;
|
||||
sd::LongType indIOioC, indIiH, indWoC, indWiC, indWkH, indOoH; // corresponding indexes
|
||||
ConvolutionUtils::getSizesAndIndexesConv2d(isNCHW, 0, *input, *output, bS, iC, iH, iW, oC, oH, oW, indIOioC, indIiH,
|
||||
indWiC, indWoC, indWkH, indOoH);
|
||||
|
||||
if (paddingMode) {
|
||||
ConvolutionUtils::calcPadding2D(pH, pW, oH, oW, iH, iW, kH, kW, sH, sW, dH, dW);
|
||||
}
|
||||
padLeft = pW;
|
||||
padTop = pH;
|
||||
padRight = (oW - 1) * sW - iW + kW - pW;
|
||||
padBottom = (oH - 1) * sH - iH + kH - pH;
|
||||
|
||||
|
||||
auto poolPad = arm_compute::PadStrideInfo(sW, sH, padLeft, padRight, padTop, padBottom,
|
||||
arm_compute::DimensionRoundingType::FLOOR);
|
||||
auto poolInfo = arm_compute::PoolingLayerInfo(arm_compute::PoolingType::AVG, arm_compute::Size2D(kW, kH), dataLayout,
|
||||
poolPad, excludePadding);
|
||||
ArmFunction<arm_compute::NEPoolingLayer> pool;
|
||||
pool.configure(input, output, dataLayout, poolInfo);
|
||||
|
||||
pool.run(); // run function
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
PLATFORM_CHECK(avgpool2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0);
|
||||
auto output = OUTPUT_VARIABLE(0);
|
||||
const sd::LongType dH = INT_ARG(6);
|
||||
const sd::LongType dW = INT_ARG(7);
|
||||
// Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32
|
||||
// for now, we will ignore F16 as it shoulde be preconditioned for pool size 2,3 and arm64-v8.2-a architecture
|
||||
Requirements req("ARMCOMPUTE AVGPOOL2d OP");
|
||||
req.expectEq(makeInfoVariable(input->dataType(), TYPE_MSG_INPUT), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(output->dataType(), TYPE_MSG_OUTPUT), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(dH, "dilation#H"), 1) && req.expectEq(makeInfoVariable(dW, "dilation#W"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(input->rankOf(), RANK_MSG_INPUT), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(input->ordering(), ORDERING_MSG_INPUT), 'c') &&
|
||||
req.expectEq(makeInfoVariable(input->stridesOf()[input->rankOf() - 1], "input#lastStride"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(output->rankOf(), RANK_MSG_OUTPUT), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(output->ordering(), ORDERING_MSG_OUTPUT), 'c') &&
|
||||
req.expectEq(makeInfoVariable(output->stridesOf()[output->rankOf() - 1], "output#lastStride"), 1);
|
||||
req.logTheSuccess();
|
||||
return req;
|
||||
}
|
||||
|
||||
} // namespace platforms
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
// Created by Abdelrauf (rauf@konduit.ai) 2020
|
||||
|
||||
#include <ops/declarable/OpRegistrator.h>
|
||||
#include <ops/declarable/PlatformHelper.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <system/platform_boilerplate.h>
|
||||
|
||||
#include "armcomputeUtils.h"
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace platforms {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
PLATFORM_IMPL(conv2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0); // [bS, iH, iW, iC] (NHWC) or [bS, iC, iH, iW] (NCHW)
|
||||
auto weights = INPUT_VARIABLE(1); // [kH, kW, iC, oC], [oC, iC, kH, kW], [oC, kH, kW, iC]
|
||||
auto bias = block.width() > 2 ? INPUT_VARIABLE(2) : nullptr; // [oC]
|
||||
|
||||
auto output = OUTPUT_VARIABLE(0); // [bS, oH, oW, oC] (NHWC) or [bS, oC, oH, oW] (NCHW)
|
||||
|
||||
sd::LongType sH = INT_ARG(2); // strides height
|
||||
sd::LongType sW = INT_ARG(3); // strides width
|
||||
sd::LongType pH = INT_ARG(4); // paddings height
|
||||
sd::LongType pW = INT_ARG(5); // paddings width
|
||||
sd::LongType dH = INT_ARG(6); // dilations height
|
||||
sd::LongType dW = INT_ARG(7); // dilations width
|
||||
int paddingMode = INT_ARG(8); // 0-VALID, 1-SAME
|
||||
bool isNCHW = block.getIArguments()->size() > 9 ? !INT_ARG(9) : 1; // INT_ARG(9): 0-NCHW, 1-NHWC
|
||||
int wFormat = block.getIArguments()->size() > 10
|
||||
? INT_ARG(10)
|
||||
: 0; // 0 - [kH, kW, iC, oC], 1 - [oC, iC, kH, kW], 2 - [oC, kH, kW, iC]
|
||||
|
||||
sd::LongType kH = INT_ARG(0) > 0 ? INT_ARG(0) : static_cast<sd::LongType>(weights->sizeAt(0)); // filter(kernel) height
|
||||
sd::LongType kW = INT_ARG(1) > 0 ? INT_ARG(1) : static_cast<sd::LongType>(weights->sizeAt(1)); // filter(kernel) width
|
||||
|
||||
// Calculate individual paddings
|
||||
sd::LongType padLeft, padTop, padRight, padBottom;
|
||||
sd::LongType bS, iC, iH, iW, oC, oH,
|
||||
oW; // batch size, input channels, input height/width, output channels, output height/width;
|
||||
sd::LongType indIOioC, indIiH, indWoC, indWiC, indWkH, indOoH; // corresponding indexes
|
||||
ConvolutionUtils::getSizesAndIndexesConv2d(isNCHW, 0, *input, *output, bS, iC, iH, iW, oC, oH, oW, indIOioC, indIiH,
|
||||
indWiC, indWoC, indWkH, indOoH);
|
||||
|
||||
ConvolutionUtils::calcPadding2D(pH, pW, oH, oW, iH, iW, kH, kW, sH, sW, dH, dW, paddingMode);
|
||||
int pWSame = (paddingMode == 2 && dW > 1) ? ((oW - 1) * sW + (kW - 1) * dW + 1 - iW) / 2
|
||||
: pW; // dH == 1 for causal mode in conv1d
|
||||
padLeft = pW;
|
||||
padTop = pH;
|
||||
padRight = (oW - 1) * sW - iW + kW - pWSame;
|
||||
padBottom = (oH - 1) * sH - iH + kH - pH;
|
||||
|
||||
std::vector<sd::LongType> expectedWeightsShape = ConvolutionUtils::expectWeightsShape(wFormat, kH, kW, iC, oC);
|
||||
REQUIRE_TRUE(weights->isSameShape(expectedWeightsShape), 0,
|
||||
"CONV2D ARMCOMPUTE OP: wrong shape of weights array, expected is %s, but got %s instead !",
|
||||
ShapeUtils::shapeAsString(expectedWeightsShape).c_str(), ShapeUtils::shapeAsString(weights).c_str());
|
||||
if (bias)
|
||||
REQUIRE_TRUE(bias->rankOf() <= 2 && oC == bias->lengthOf(), 0,
|
||||
"CONV2D ARMCOMPUTE OP: wrong shape of array with biases, expected rank, length: <=2, %i, but got %i, "
|
||||
"%i instead !",
|
||||
oC, bias->rankOf(), bias->lengthOf());
|
||||
|
||||
|
||||
|
||||
auto dataLayout = isNCHW ? arm_compute::DataLayout::NCHW : arm_compute::DataLayout::NHWC;
|
||||
// check weight input datalayout match
|
||||
bool dataLayoutMatch = (isNCHW && wFormat == 1) || (!isNCHW && wFormat == 2);
|
||||
arm_compute::PermutationVector permuteVector;
|
||||
if (!dataLayoutMatch) {
|
||||
// lets premute
|
||||
if (wFormat == 0) {
|
||||
if (isNCHW) {
|
||||
|
||||
// reshape
|
||||
permuteVector = arm_compute::PermutationVector(2U, 3U, 1U, 0U);
|
||||
} else {
|
||||
|
||||
// reshape
|
||||
permuteVector = arm_compute::PermutationVector(1U, 2U, 3U, 0U);
|
||||
}
|
||||
} else if (wFormat == 1) {
|
||||
|
||||
permuteVector = arm_compute::PermutationVector(2U, 0U, 1U, 3U);
|
||||
} else {
|
||||
|
||||
permuteVector = arm_compute::PermutationVector(1U, 2U, 0U, 3U);
|
||||
}
|
||||
} else {
|
||||
|
||||
// set 0
|
||||
permuteVector.set_num_dimensions(0);
|
||||
}
|
||||
|
||||
Arm_WeightsInfo wInfo(false, kW, kH, 1);
|
||||
arm_compute::Size2D dilation(dW, dH);
|
||||
arm_compute::PadStrideInfo pad(sW, sH, padLeft, padRight, padTop, padBottom,
|
||||
arm_compute::DimensionRoundingType::FLOOR);
|
||||
ArmFunctionWeighted<arm_compute::NEConvolutionLayer> conv;
|
||||
conv.configure(input, weights, bias, output, dataLayout, permuteVector, pad, wInfo, dilation);
|
||||
conv.run(); // run function
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
PLATFORM_CHECK(conv2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0);
|
||||
auto weights = INPUT_VARIABLE(1);
|
||||
auto output = OUTPUT_VARIABLE(0);
|
||||
// Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32.
|
||||
Requirements req("ARMCOMPUTE CONV2d OP");
|
||||
req.expectEq(makeInfoVariable(input->dataType(), TYPE_MSG_INPUT0), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(weights->dataType(), TYPE_MSG_INPUT1), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(output->dataType(), TYPE_MSG_OUTPUT), DataType::FLOAT32) &&
|
||||
req.expectLessEq(makeInfoVariable(input->rankOf(), RANK_MSG_INPUT0), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(input->ordering(), ORDERING_MSG_INPUT0), 'c') &&
|
||||
req.expectEq(makeInfoVariable(input->stridesOf()[input->rankOf() - 1], "input0#lastStride"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(weights->rankOf(), RANK_MSG_INPUT1), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(weights->ordering(), ORDERING_MSG_INPUT1), 'c') &&
|
||||
req.expectEq(makeInfoVariable(weights->stridesOf()[weights->rankOf() - 1], "input1#lastStride"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(output->rankOf(), RANK_MSG_OUTPUT), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(output->ordering(), ORDERING_MSG_OUTPUT), 'c') &&
|
||||
req.expectEq(makeInfoVariable(output->stridesOf()[output->rankOf() - 1], "output#lastStride"), 1);
|
||||
req.logTheSuccess();
|
||||
return req;
|
||||
}
|
||||
|
||||
} // namespace platforms
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
// Created by Abdelrauf (rauf@konduit.ai) 2020
|
||||
|
||||
#include <ops/declarable/OpRegistrator.h>
|
||||
#include <ops/declarable/PlatformHelper.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <system/platform_boilerplate.h>
|
||||
|
||||
#include "armcomputeUtils.h"
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace platforms {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
PLATFORM_IMPL(deconv2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0); // [bS, iH, iW, iC] (NHWC) or [bS, iC, iH, iW] (NCHW)
|
||||
auto weights = INPUT_VARIABLE(1); // [kH, kW, oC, iC], [iC, oC, kH, kW], [iC, kH, kW, oC]
|
||||
auto bias = block.width() > 2 ? INPUT_VARIABLE(2) : nullptr; // [oC]
|
||||
|
||||
auto output = OUTPUT_VARIABLE(0); // [bS, oH, oW, oC] (NHWC) or [bS, oC, oH, oW] (NCHW)
|
||||
|
||||
REQUIRE_TRUE(input->rankOf() == 4, 0,
|
||||
"CUSTOM DECONV2D ARMCOMPUTE OP: rank of input array must be equal to 4, but got %i instead !",
|
||||
input->rankOf());
|
||||
REQUIRE_TRUE(weights->rankOf() == 4, 0,
|
||||
"CUSTOM DECONV2D ARMCOMPUTE OP: rank of weights array must be equal to 4, but got %i instead !",
|
||||
weights->rankOf());
|
||||
|
||||
sd::LongType kH = INT_ARG(0) > 0 ? INT_ARG(0) : static_cast<sd::LongType>(weights->sizeAt(0)); // filter(kernel) height
|
||||
sd::LongType kW = INT_ARG(1) > 0 ? INT_ARG(1) : static_cast<sd::LongType>(weights->sizeAt(1)); // filter(kernel) width
|
||||
sd::LongType sH = INT_ARG(2); // strides height
|
||||
sd::LongType sW = INT_ARG(3); // strides width
|
||||
sd::LongType pH = INT_ARG(4); // paddings height
|
||||
sd::LongType pW = INT_ARG(5); // paddings width
|
||||
sd::LongType dH = INT_ARG(6); // dilations height
|
||||
sd::LongType dW = INT_ARG(7); // dilations width
|
||||
int paddingMode = INT_ARG(8); // 0-VALID, 1-SAME
|
||||
bool isNCHW = block.getIArguments()->size() > 9 ? !INT_ARG(9) : 1; // INT_ARG(9): 0-NCHW, 1-NHWC
|
||||
int wFormat = block.getIArguments()->size() > 10
|
||||
? INT_ARG(10)
|
||||
: 0; // 0 - [kH, kW, iC, oC], 1 - [oC, iC, kH, kW], 2 - [oC, kH, kW, iC]
|
||||
|
||||
// Calculate individual paddings
|
||||
sd::LongType padLeft, padTop, padRight, padBottom;
|
||||
sd::LongType bS, iC, iH, iW, oC, oH,
|
||||
oW; // batch size, input channels, input height/width, output channels, output height/width;
|
||||
sd::LongType indIOioC, indIiH, indWoC, indWiC, indWkH, indOoH; // corresponding indexes
|
||||
ConvolutionUtils::getSizesAndIndexesConv2d(isNCHW, wFormat, *input, *output, bS, iC, iH, iW, oC, oH, oW, indIOioC,
|
||||
indIiH, indWoC, indWiC, indWkH, indOoH);
|
||||
|
||||
std::vector<sd::LongType> expectedWeightsShape = ConvolutionUtils::expectWeightsShape(wFormat, kH, kW, oC, iC);
|
||||
REQUIRE_TRUE(weights->isSameShape(expectedWeightsShape), 0,
|
||||
"CUSTOM DECONV2D ARMCOMPUTE OP: wrong shape of weights array, expected is %s, but got %s instead !",
|
||||
ShapeUtils::shapeAsString(expectedWeightsShape).c_str(), ShapeUtils::shapeAsString(weights).c_str());
|
||||
if (bias)
|
||||
REQUIRE_TRUE(bias->rankOf() <= 2 && oC == bias->lengthOf(), 0,
|
||||
"CUSTOM DECONV2D ARMCOMPUTE OP: wrong shape of array with biases, expected rank, length: <=2, %i, but "
|
||||
"got %i, %i instead !",
|
||||
oC, bias->rankOf(), bias->lengthOf());
|
||||
|
||||
if (paddingMode) {
|
||||
// Note: we're intentionally swapping iH and oH, to calculated the padding for a"normal" conv (not deconv) forward
|
||||
// pass
|
||||
ConvolutionUtils::calcPadding2D(pH, pW, iH, iW, oH, oW, kH, kW, sH, sW, dH, dW);
|
||||
}
|
||||
padLeft = pW;
|
||||
padTop = pH;
|
||||
padRight = (iW - 1) * sW - oW + kW - pW;
|
||||
padBottom = (iH - 1) * sH - oH + kH - pH;
|
||||
auto dataLayout = isNCHW ? arm_compute::DataLayout::NCHW : arm_compute::DataLayout::NHWC;
|
||||
// check weight input datalayout match
|
||||
bool dataLayoutMatch = (isNCHW && wFormat == 1) || (!isNCHW && wFormat == 2);
|
||||
arm_compute::PermutationVector permuteVector;
|
||||
// unlike in cov2d for weights iC and oC permutted : for example {oC, iC, kH, kW}, {iC, oC, kH, kW}
|
||||
// but we need it normal way for arm
|
||||
if (!dataLayoutMatch) {
|
||||
// lets premute
|
||||
if (wFormat == 0) {
|
||||
if (isNCHW) {
|
||||
// reshape
|
||||
permuteVector = arm_compute::PermutationVector(2U, 3U, 0U, 1U);
|
||||
} else {
|
||||
// reshape
|
||||
permuteVector = arm_compute::PermutationVector(0U, 2U, 3U, 1U);
|
||||
}
|
||||
} else if (wFormat == 1) {
|
||||
|
||||
permuteVector = arm_compute::PermutationVector(3U, 0U, 1U, 2U);
|
||||
} else {
|
||||
|
||||
permuteVector = arm_compute::PermutationVector(1U, 2U, 3U, 0U);
|
||||
}
|
||||
} else {
|
||||
// fix weight
|
||||
if (isNCHW) {
|
||||
permuteVector = arm_compute::PermutationVector(0U, 1U, 3U, 2U);
|
||||
} else {
|
||||
|
||||
permuteVector = arm_compute::PermutationVector(3U, 1U, 2U, 0U);
|
||||
}
|
||||
}
|
||||
|
||||
Arm_WeightsInfo wInfo(false, kW, kH, 1);
|
||||
arm_compute::PadStrideInfo pad(sW, sH, padLeft, padRight, padTop, padBottom,
|
||||
arm_compute::DimensionRoundingType::FLOOR);
|
||||
ArmFunctionWeighted<arm_compute::NEDeconvolutionLayer> deconv;
|
||||
deconv.configure(input, weights, bias, output, dataLayout, permuteVector, pad);
|
||||
deconv.run(); // run function
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
PLATFORM_CHECK(deconv2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0);
|
||||
auto weights = INPUT_VARIABLE(1);
|
||||
auto output = OUTPUT_VARIABLE(0);
|
||||
int dH = INT_ARG(6);
|
||||
int dW = INT_ARG(7);
|
||||
// Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32.
|
||||
Requirements req("ARMCOMPUTE DECONV2d OP");
|
||||
req.expectEq(makeInfoVariable(input->dataType(), TYPE_MSG_INPUT0), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(weights->dataType(), TYPE_MSG_INPUT1), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(output->dataType(), TYPE_MSG_OUTPUT), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(dH, "dilation#H"), 1) && req.expectEq(makeInfoVariable(dW, "dilation#W"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(input->rankOf(), RANK_MSG_INPUT0), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(input->ordering(), ORDERING_MSG_INPUT0), 'c') &&
|
||||
req.expectEq(makeInfoVariable(input->stridesOf()[input->rankOf() - 1], "input0#lastStride"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(weights->rankOf(), RANK_MSG_INPUT1), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(weights->ordering(), ORDERING_MSG_INPUT1), 'c') &&
|
||||
req.expectEq(makeInfoVariable(weights->stridesOf()[weights->rankOf() - 1], "input1#lastStride"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(output->rankOf(), RANK_MSG_OUTPUT), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(output->ordering(), ORDERING_MSG_OUTPUT), 'c') &&
|
||||
req.expectEq(makeInfoVariable(output->stridesOf()[output->rankOf() - 1], "output#lastStride"), 1);
|
||||
req.logTheSuccess();
|
||||
return req;
|
||||
}
|
||||
|
||||
} // namespace platforms
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* *
|
||||
* *
|
||||
* * This program and the accompanying materials are made available under the
|
||||
* * terms of the Apache License, Version 2.0 which is available at
|
||||
* * https://www.apache.org/licenses/LICENSE-2.0.
|
||||
* *
|
||||
* * See the NOTICE file distributed with this work for additional
|
||||
* * information regarding copyright ownership.
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* * License for the specific language governing permissions and limitations
|
||||
* * under the License.
|
||||
* *
|
||||
* * SPDX-License-Identifier: Apache-2.0
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
// Created by Abdelrauf 2020
|
||||
|
||||
#include <ops/declarable/OpRegistrator.h>
|
||||
#include <ops/declarable/PlatformHelper.h>
|
||||
#include <ops/declarable/helpers/convolutions.h>
|
||||
#include <system/platform_boilerplate.h>
|
||||
|
||||
#include "armcomputeUtils.h"
|
||||
|
||||
namespace sd {
|
||||
namespace ops {
|
||||
namespace platforms {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
PLATFORM_IMPL(maxpool2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0);
|
||||
auto output = OUTPUT_VARIABLE(0);
|
||||
|
||||
REQUIRE_TRUE(input->rankOf() == 4, 0,
|
||||
"MAXPOOL2D ARMCOMPUTE OP: input array should have rank of 4, but got %i instead", input->rankOf());
|
||||
|
||||
// 0,1 - kernel Height/Width; 2,3 - stride Height/Width; 4,5 - pad Height/Width; 6,7 - dilation Height/Width; 8 - same
|
||||
// mode;
|
||||
const sd::LongType kH = INT_ARG(0);
|
||||
const sd::LongType kW = INT_ARG(1);
|
||||
const sd::LongType sH = INT_ARG(2);
|
||||
const sd::LongType sW = INT_ARG(3);
|
||||
sd::LongType pH = INT_ARG(4);
|
||||
sd::LongType pW = INT_ARG(5);
|
||||
const sd::LongType dH = INT_ARG(6);
|
||||
const sd::LongType dW = INT_ARG(7);
|
||||
const int paddingMode = INT_ARG(8);
|
||||
// const int extraParam0 = INT_ARG(9);
|
||||
const int isNCHW = block.getIArguments()->size() > 10 ? !INT_ARG(10) : 1; // INT_ARG(10): 1-NHWC, 0-NCHW
|
||||
|
||||
auto dataLayout = isNCHW ? arm_compute::DataLayout::NCHW : arm_compute::DataLayout::NHWC;
|
||||
|
||||
// Calculate individual paddings
|
||||
sd::LongType padLeft, padTop, padRight, padBottom;
|
||||
sd::LongType bS, iC, iH, iW, oC, oH,
|
||||
oW; // batch size, input channels, input height/width, output channels, output height/width;
|
||||
sd::LongType indIOioC, indIiH, indWoC, indWiC, indWkH, indOoH; // corresponding indexes
|
||||
ConvolutionUtils::getSizesAndIndexesConv2d(isNCHW, 0, *input, *output, bS, iC, iH, iW, oC, oH, oW, indIOioC, indIiH,
|
||||
indWiC, indWoC, indWkH, indOoH);
|
||||
|
||||
if (paddingMode) {
|
||||
ConvolutionUtils::calcPadding2D(pH, pW, oH, oW, iH, iW, kH, kW, sH, sW, dH, dW);
|
||||
}
|
||||
padLeft = pW;
|
||||
padTop = pH;
|
||||
padRight = (oW - 1) * sW - iW + kW - pW;
|
||||
padBottom = (oH - 1) * sH - iH + kH - pH;
|
||||
|
||||
auto poolPad = arm_compute::PadStrideInfo(sW, sH, padLeft, padRight, padTop, padBottom,
|
||||
arm_compute::DimensionRoundingType::FLOOR);
|
||||
auto poolInfo =
|
||||
arm_compute::PoolingLayerInfo(arm_compute::PoolingType::MAX, arm_compute::Size2D(kW, kH), dataLayout, poolPad);
|
||||
ArmFunction<arm_compute::NEPoolingLayer> pool;
|
||||
|
||||
pool.configure(input, output, dataLayout, poolInfo);
|
||||
|
||||
pool.run(); // run function
|
||||
|
||||
return sd::Status::OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
PLATFORM_CHECK(maxpool2d, ENGINE_CPU) {
|
||||
auto input = INPUT_VARIABLE(0);
|
||||
auto output = OUTPUT_VARIABLE(0);
|
||||
const int dH = INT_ARG(6);
|
||||
const int dW = INT_ARG(7);
|
||||
// Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32
|
||||
// for now, we will ignore F16 as it shoulde be preconditioned for pool size 2,3 and arm64-v8.2-a architecture
|
||||
Requirements req("ARMCOMPUTE MAXPOOL2d OP");
|
||||
req.expectEq(makeInfoVariable(input->dataType(), TYPE_MSG_INPUT), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(output->dataType(), TYPE_MSG_OUTPUT), DataType::FLOAT32) &&
|
||||
req.expectEq(makeInfoVariable(dH, "dilation#H"), 1) && req.expectEq(makeInfoVariable(dW, "dilation#W"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(input->rankOf(), RANK_MSG_INPUT), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(input->ordering(), ORDERING_MSG_INPUT), 'c') &&
|
||||
req.expectEq(makeInfoVariable(input->stridesOf()[input->rankOf() - 1], "input#lastStride"), 1) &&
|
||||
req.expectLessEq(makeInfoVariable(output->rankOf(), RANK_MSG_OUTPUT), arm_compute::MAX_DIMS) &&
|
||||
req.expectEq(makeInfoVariable(output->ordering(), ORDERING_MSG_OUTPUT), 'c') &&
|
||||
req.expectEq(makeInfoVariable(output->stridesOf()[output->rankOf() - 1], "output#lastStride"), 1);
|
||||
req.logTheSuccess();
|
||||
return req;
|
||||
}
|
||||
|
||||
} // namespace platforms
|
||||
} // namespace ops
|
||||
} // namespace sd
|
||||
Reference in New Issue
Block a user