aclnnUpsampleLinear1d

📄 View source code

Applicable Products

ProductSupported
Ascend 950PR/Ascend 950DT√
Atlas A3 training products/Atlas A3 inference products√
Atlas A2 training products/Atlas A2 inference products√
Atlas 200I/500 A2 inference products×
Atlas inference products×
Atlas training products√

Function

  • Description: Applies a linear interpolation algorithm to the input signal consisting of multiple input channels for upsampling. If the input shape is (N, C, L), the output shape is (N, C, outputSize).

  • Formula:

    • Core algorithm logic:
      1. Map each point of the target image back to the original image to obtain a coordinate with a decimal point.
      2. Based on this floating-point coordinate, the adjacent points in the original image are calculated.
      3. The weights from the adjacent points to the corresponding target point are calculated separately, and the target point value is obtained by multiplying and accumulating according to the weights.
    • Specific calculation logic: The scaling mode is divided into corner alignment and edge alignment. Corner alignment means aligning with the pixel center of the top-left corner of the original image, while edge alignment means aligning with the top-left vertex and two edges of the original image. Differences exist when calculating the scale factor and coordinate positions. The corresponding formulas are as follows:scale={(self.dim[2]−1)/(outputSize[0]−1)alignCorners=true1/scalesalignCorners=false&scales>0self.dim[2]/outputSize[0]alignCorners=falsescale =\begin{cases} (self.dim[2]-1) / (outputSize[0]-1) & alignCorners=true \\ 1 / scales & alignCorners=false\&scales>0\\ self.dim[2] / outputSize[0] & alignCorners=false \end{cases}

Therefore, for a point p(x) in a certain direction of the output, the point mapped back to the original image is denoted as q(x'), and the relationship is as follows:

x′={x∗scale_halignCorners=trueMAX(0,(x+0.5)∗scale_h−0.5)alignCorners=falsex' =\begin{cases} x * scale\_h & alignCorners=true \\ MAX(0,{(x+0.5)*scale\_h-0.5}) & alignCorners=false \end{cases}
  • Denote:
x0=int(x′),x1=int(x′)+1,lambda0=x1−x′,lambda1=1−lambda0x_{0} =int(x'),x_{1} =int(x')+1, lambda_{0} = x_{1}-x', lambda_{1} = 1-lambda_{0}
  • Then the following formula is obtained:V(px)=V(px0)∗lambda0+V(px1)∗lambda1{V(p_{x})} = {V(p_{x0})} * {lambda_{0}} + {V(p_{x1})} * {lambda_{1}}

Function Prototype

Each operator is divided into a two-phase API. The "aclnnUpsampleLinear1dGetWorkspaceSize" API must be called first to obtain the input parameters and calculate the required workspace size based on the computation process, and then the "aclnnUpsampleLinear1d" API is called to perform the computation.

aclnnStatus aclnnUpsampleLinear1dGetWorkspaceSize(
  const aclTensor   *self,
  const aclIntArray *outputSize,
  const bool         alignCorners,
  const double       scales,
  aclTensor         *out,
  uint64_t          *workspaceSize,
  aclOpExecutor    **executor)
aclnnStatus aclnnUpsampleLinear1d(
  void          *workspace,
  uint64_t       workspaceSize,
  aclOpExecutor *executor,
  aclrtStream    stream)

aclnnUpsampleLinear1dGetWorkspaceSize

  • Parameters

    Parameter Name Input/Output Description Usage Notes Data Type Data Format Dimension (shape) Non-contiguous Tensor
    self (aclTensor*) Input Represents the input tensor for upsampling, corresponding to `self` in the formula.
    • Empty tensors are not supported.
    • When the data format is ND, it is processed in NCL format by default.
    FLOAT32, FLOAT16, BFLOAT16 ND, NCL 3 √
    outputSize (aclIntArray*) Input Indicates the spatial size of the output out in the L dimension, corresponding to `outputSize` in the formula. The size is 1, and the value must be greater than 0. INT64 - - -
    alignCorners (bool) Input A bool type parameter that determines whether to align corner pixels, corresponding to `alignCorners` in the formula.
    • If set to True, the input and output tensors are aligned by the center points of their corner pixels, preserving the values at the corner pixels.
    • If set to False, the input and output tensors are aligned by the corner points of their corner pixels, and the interpolation uses edge values to fill in for out-of-boundary values, making this operation independent of the input size scales when it remains unchanged.
    - - - -
    scales (double) Input Represents the multiplier for the L dimension of the output, corresponding to `scales` in the formula. - - - - -
    out (aclTensor*) output Represents the output tensor after upsampling.
    • Empty tensors are not supported.
    • The output must be 3-dimensional. The data type and data format are consistent with the input `self`.
    FLOAT32, FLOAT16, BFLOAT16 ND, NCL 3 √
    workspaceSize (uint64_t*) output Returns the workspace size to be requested on the Device side. - - - - -
    executor (aclOpExecutor**) output Returns the operator executor, which contains the operator computation flow. - - - - -
    • Atlas training products:

      The BFLOAT16 data type is not supported for the input self and output out.

  • Return Value

    aclnnStatus: return status code. For details, see aclnn Return Codes.

The first-phase API performs input parameter validation and reports an error in the following scenarios:

Return Code Error Code Description
ACLNN_ERR_PARAM_NULLPTR 161001 If the passed parameter is a required input, output, or required attribute, and is a null pointer.
ACLNN_ERR_PARAM_INVALID 161002
The data type of self is not within the supported range.
The data types of self and out are inconsistent.
The dimensions of self and out are not 3D.
The size of outputSize is not equal to 1.
An element value of outputSize is less than 1.
The size of out in the L dimension is inconsistent with outputSize[0].
self, outputSize, and scales do not meet the constraints.

aclnnUpsampleLinear1d

  • Parameters

    Parameter Name Input/Output Description
    workspace Input The workspace memory address applied for on the Device side.
    workspaceSize Input The workspace size applied on the Device side, obtained through the first-phase API aclnnUpsampleLinear1dGetWorkspaceSize.
    executor Input The operator executor, which contains the operator computation flow.
    stream Input Specifies the stream for task execution.
  • Return Value

    aclnnStatus: return status code. For details, see aclnn Return Codes.

Constraints

  • When the data format of the input self and the output out is neither ND nor NCL, other input data formats are processed as NCL by default.

  • The parameters self, outputSize, and scales must satisfy the following constraint:

    outputSize=floor(self_L∗scales)outputSize = floor(self\_L * scales)
  • Deterministic computation:

    • aclnnUpsampleLinear1d uses deterministic implementation by default.

Example

The following sample code is for reference only. For details about compilation and running, see Compile and Run Samples.

#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_upsample_linear_1d.h"

#define CHECK_RET(cond, return_expr) \
    do {                             \
        if (!(cond)) {               \
            return_expr;             \
        }                            \
    } while (0)

#define LOG_PRINT(message, ...)         \
    do {                                \
        printf(message, ##__VA_ARGS__); \
    } while (0)

int64_t GetShapeSize(const std::vector<int64_t> &shape)
{
    int64_t shapeSize = 1;
    for (auto i : shape) {
        shapeSize *= i;
    }
    return shapeSize;
}

int Init(int32_t deviceId, aclrtStream *stream)
{
    // Standard practice: resource initialization.
    auto ret = aclInit(nullptr);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
    ret = aclrtSetDevice(deviceId);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
    ret = aclrtCreateStream(stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
    return 0;
}

template <typename T>
int CreateAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr,
    aclDataType dataType, aclTensor **tensor)
{
    auto size = GetShapeSize(shape) * sizeof(T);
    // Call aclrtMalloc to allocate device-side memory.
    auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
    // Call aclrtMemcpy to copy host-side data to device-side memory.
    ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);

    // Compute the strides of the contiguous tensor.
    std::vector<int64_t> strides(shape.size(), 1);
    for (int64_t i = shape.size() - 2; i >= 0; i--) {
        strides[i] = shape[i + 1] * strides[i + 1];
    }

    // Call the aclCreateTensor API to create an aclTensor.
    *tensor = aclCreateTensor(shape.data(),
        shape.size(),
        dataType,
        strides.data(),
        0,
        aclFormat::ACL_FORMAT_ND,
        shape.data(),
        shape.size(),
        *deviceAddr);
    return 0;
}

int main()
{
    // 1. (Boilerplate) Initialize the device/stream. Refer to the ACL API manual.
    // Set deviceId based on your actual device.
    int32_t deviceId = 0;
    aclrtStream stream;
    auto ret = Init(deviceId, &stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

    // 2. Construct the input and output. Customize the construction based on the API.
    std::vector<int64_t> selfShape = {1, 1, 2};
    std::vector<int64_t> outShape = {1, 1, 3};
    void *selfDeviceAddr = nullptr;
    void *outDeviceAddr = nullptr;
    aclTensor *self = nullptr;
    aclTensor *out = nullptr;
    std::vector<float> selfHostData = {1, 1};
    std::vector<float> outHostData = {0, 0, 0};

    // Create the self aclTensor.
    ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    // Create the out aclTensor.
    ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    std::vector<int64_t> outArraySize = {3};
    const aclIntArray *outputSize = aclCreateIntArray(outArraySize.data(), outArraySize.size());
    CHECK_RET(outputSize != nullptr, return ACL_ERROR_INTERNAL_ERROR);

    // 3. Call the CANN operator library API. Replace with the specific API name.
    uint64_t workspaceSize = 0;
    aclOpExecutor *executor;
    // Call the first-phase API of aclnnUpsampleLinear1d.
    ret = aclnnUpsampleLinear1dGetWorkspaceSize(self, outputSize, false, -1, out, &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnUpsampleLinear1dGetWorkspaceSize failed. ERROR: %d\n", ret);
              return ret);
    // Allocate device memory based on the workspaceSize calculated by the first-phase API.
    void *workspaceAddr = nullptr;
    if (workspaceSize > 0) {
        ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
        CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
    }
    // Call the second-phase API of aclnnUpsampleLinear1d.
    ret = aclnnUpsampleLinear1d(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnUpsampleLinear1d failed. ERROR: %d\n", ret); return ret);

    // 4. (Boilerplate) Synchronously wait for task execution to complete.
    ret = aclrtSynchronizeStream(stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);

    // 5. Obtain the output value and copy the result from device memory to host memory. Modify based on the specific API definition.
    auto size = GetShapeSize(outShape);
    std::vector<float> resultData(size, 0);
    ret = aclrtMemcpy(resultData.data(),
        resultData.size() * sizeof(resultData[0]),
        outDeviceAddr,
        size * sizeof(resultData[0]),
        ACL_MEMCPY_DEVICE_TO_HOST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
    for (int64_t i = 0; i < size; i++) {
        LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);
    }

    // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
    aclDestroyTensor(self);
    aclDestroyTensor(out);
    aclDestroyIntArray(outputSize);

    // 7. Release device resources. Modify based on the specific API definition.
    aclrtFree(selfDeviceAddr);
    aclrtFree(outDeviceAddr);
    if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();
    return 0;
}