aclnnChamferDistanceBackward

📄 View Source Code

Applicable Products

ProductSupported
Ascend 950PR/Ascend 950DTNo
Atlas A3 training products/Atlas A3 inference productsYes
Atlas A2 training products/Atlas A2 inference productsYes
Atlas 200I/500 A2 inference productsNo
Atlas inference productsNo
Atlas training productsNo

Function

  • Description: The backward operator of Chamfer distance. It computes the gradient corresponding to the inputs based on the contribution of the forward inputs to the outputs and the initial gradient.

  • Formula:

    Assume there are two point sets: xyz1=[B,N,2], xyz2=[B,M,2]

    • The Chamfer distance forward operator formula is:

      dist1i=Min((x1i−x2)2+(y1i−y2)2),x2,y2∈xyz2dist1_i=Min((x_{1_i}−x_2)^2+(y_{1_i}−y_2)^2),x_2, y_2∈xyz2 dist2i=Min((x2i−x1)2+(y2i−y1)2),x1,y1∈xyz1dist2_i=Min((x_{2_i}-x_1)^2+(y_{2_i}-y_1)^2),x_1, y_1∈xyz1

    • The backward operator computes the derivative of this formula, as follows:

      • Derivative of dist1idist1_i with respect to x1ix_{1_i} =2∗grad_dist1∗(x1i−x2)=2*grad\_dist1*(x_{1_i}-x_2)

        Where: x1i∈xyz1x_{1_i}∈xyz1, x2x_2 is the x-coordinate of the point with the minimum distance retrieved from xyz2 based on the index value of the forward output id1. The single-point derivative formula is as above. Since the positions of single-point gradient updates are continuous, multi-point parallel computation is considered.

      • Derivative of dist1idist1_i with respect to y1iy_{1_i} =2∗grad_dist1∗(y1i−y2)=2*grad\_dist1*(y_{1_i}-y_2)

        Where y1i∈xyz1y_{1_i}∈xyz1, y2y_2 is the ordinate of the point with the minimum distance retrieved from xyz2 based on the index value of id1 from the forward output. The single-point derivative formula is as above. Since the positions for single-point gradient updates are contiguous, multi-point parallel computation can also be considered.

      • Derivative of dist1idist1_i with respect to x2x_2 =−2∗grad_dist1∗(x1−x2i)=-2*grad\_dist1*(x_1-x_{2_i})

        Where x2i∈xyz2x_{2_i}∈xyz2, x1x_1 is the abscissa of the point with the minimum distance retrieved from xyz2 based on the index value of id1 from the forward output. The single-point derivative formula is as above. Since the single-point gradient needs to be updated based on the index value corresponding to the minimum distance value, this part cannot be parallelized and can only be computed point by point.

      • Derivative of dist1idist1_i with respect to y2y_2 =−2∗grad_dist1∗(y1−y2i)=-2*grad\_dist1*(y_1-y_{2_i})

        where y2i∈xyz2y_{2_i}∈xyz2, and y1y_1 is the ordinate of the point with the minimum distance retrieved from xyz2 based on the index value of id1 from the forward output. The single-point derivative formula is as above. Since the single-point gradient needs to be updated based on the index value corresponding to the minimum value, this part cannot be parallelized and can only be computed point by point.

    The derivatives of dist2idist2_i with respect to x2ix_{2_i}, x1x_1, y2iy_{2_i}, and y1y_1 are similar to the above process and are not repeated here.

    The final formula is as follows, where i∈[0,n):

    gradxyz1[2∗i]=2∗grad_dist1∗(x1i−x2)−2∗grad_dist1∗(x1−x2i)grad_xyz1[2*i] = 2*grad\_dist1*(x_{1_i}-x_2) - 2*grad\_dist1*(x_1-x_{2_i})

    gradxyz1[2∗i+1]=2∗grad_dist1∗(y1i−y2)−2∗grad_dist1∗(y1−y2i)grad_xyz1[2*i+1] = 2*grad\_dist1*(y_{1_i}-y_2) - 2*grad\_dist1*(y_1-y_{2_i})

    gradxyz2[2∗i]=2∗grad_dist2∗(x1i−x2)−2∗grad_dist2∗(x1−x2i)grad_xyz2[2*i] = 2*grad\_dist2*(x_{1_i}-x_2) - 2*grad\_dist2*(x_1-x_{2_i})

    gradxyz2[2∗i+1]=2∗grad_dist2∗(y1i−y2)−2∗grad_dist2∗(y1−y2i)grad_xyz2[2*i+1] = 2*grad\_dist2*(y_{1_i}-y_2) - 2*grad\_dist2*(y_1-y_{2_i})

Function Prototype

Each operator is divided into a two-phase API. You must first call the "aclnnChamferDistanceBackwardGetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the process, and then call the "aclnnChamferDistanceBackward" API to perform the computation.

aclnnStatus aclnnChamferDistanceBackwardGetWorkspaceSize(
    const aclTensor* xyz1,
    const aclTensor* xyz2,
    const aclTensor* idx1,
    const aclTensor* idx2,
    const aclTensor* gradDist1,
    const aclTensor* gradDist2,
    aclTensor*       gradXyz1,
    aclTensor*       gradXyz2,
    uint64_t*        workspaceSize,
    aclOpExecutor**  executor)
aclnnStatus aclnnChamferDistanceBackward(
    void*            workspace,
    uint64_t         workspaceSize,
    aclOpExecutor*   executor,
    aclrtStream      stream)

aclnnChamferDistanceBackwardGetWorkspaceSize

  • Parameters:

    Parameter Input/Output Description Instruction Data Type Data Format Dimension (shape) Non-contiguous Tensor
    xyz1 (aclTensor*) Input Coordinates of point set 1 for the operator forward input. Shape: (B, N, 2). FLOAT, FLOAT16 ND 3 √
    xyz2 (aclTensor*) Input Coordinates of point set 2 input to the operator forward. Shape (B, M, 2). FLOAT, FLOAT16 ND 3 √
    idx1 (aclTensor*) Input Specifies the index tensor of points in xyz2 that have the minimum distance to xyz1, output by the operator forward. Its shape is (B, N). INT32 ND 2 √
    idx2 (aclTensor*) Input Index tensor of the point in xyz1 with the minimum distance to xyz2, output by the operator forward. Shape: (B, N). INT32 ND 2 √
    gradDist1 (aclTensor*) Input Backward gradient of the forward output dist1, which also serves as the initial gradient of the backward operator. Shape: (B, N). FLOAT, FLOAT16 ND 2 √
    gradDist2 (aclTensor*) Input Backward gradient of the forward output dist2, which is also the initial gradient of the backward operator. Shape: (B, N). FLOAT, FLOAT16 ND 2 √
    gradXyz1 (aclTensor*) Output Gradient corresponding to the operator forward input xyz1 after gradient update. Shape (B, N, 2). FLOAT, FLOAT16 ND 3 √
    gradXyz2 (aclTensor*) Output Gradient corresponding to the forward operator input xyz2 after gradient update. Shape: (B, N, 2). FLOAT, FLOAT16 ND 3 √
    workspaceSize (uint64_t*) Output Size of the workspace to be applied on the Device side. - - - - -
    executor (aclOpExecutor**) Output Returns the op executor, which contains the operator computation flow. - - - - -
  • Return value:

    aclnnStatus: return code. For details, see aclnn Return Code.

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

    return value Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 The input xyz1, xyz2, idx1, idx2, gradDist1, gradDist2 or output grad_xyz1, grad_xyz2 is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type of the input or output is not within the supported range.
    The data type of the input cannot be deduced.
    The deduced data type cannot be converted to the type of the specified output out.

aclnnChamferDistanceBackward

  • Parameters:

    Parameter Input/Output Description
    workspace Input Memory address of the workspace applied for on the Device side.
    workspaceSize Input Size of the workspace applied for on the Device side, obtained from the first-phase API aclnnChamferDistanceBackwardGetWorkspaceSize.
    executor Input Operator executor, which contains the operator computation process.
    stream Input Specifies the stream for task execution.
  • Return value:

    aclnnStatus: return code. For details, see aclnn Return Code.

Constraints

  • Deterministic computation:

    • aclnnChamferDistanceBackward defaults to a non-deterministic implementation. You can enable deterministic computation by calling aclrtCtxSetSysParamOpt.

Example

The following provides example code 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_chamfer_distance_backward.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) {
    // Boilerplate: initialize resources.
    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 apply for 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 data from the host side to the device 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 aclCreateTensor 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 list.
    // Set deviceId based on the actual device.
    int32_t deviceId = 0;
    aclrtStream stream;
    auto ret = Init(deviceId, &stream);
    // Handle the check based on your needs.
    CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

    // 2. Construct the inputs and outputs. Customize the construction based on the API.
    std::vector<int64_t> xyz1Shape = {2, 2, 2};
    std::vector<int64_t> xyz2Shape = {2, 2, 2};
    std::vector<int64_t> idx1Shape = {2, 2};
    std::vector<int64_t> idx2Shape = {2, 2};
    std::vector<int64_t> gradDist1Shape = {2, 2};
    std::vector<int64_t> gradDist2Shape = {2, 2};
    std::vector<int64_t> gradXyz1Shape = {2, 2, 2};
    std::vector<int64_t> gradXyz2Shape = {2, 2, 2};
    void* xyz1DeviceAddr = nullptr;
    void* xyz2DeviceAddr = nullptr;
    void* idx1DeviceAddr = nullptr;
    void* idx2DeviceAddr = nullptr;
    void* gradDist1DeviceAddr = nullptr;
    void* gradDist2DeviceAddr = nullptr;
    void* gradXyz1DeviceAddr = nullptr;
    void* gradXyz2DeviceAddr = nullptr;
    aclTensor* xyz1 = nullptr;
    aclTensor* xyz2 = nullptr;
    aclTensor* idx1 = nullptr;
    aclTensor* idx2 = nullptr;
    aclTensor* gradDist1 = nullptr;
    aclTensor* gradDist2 = nullptr;
    aclTensor* gradXyz1 = nullptr;
    aclTensor* gradXyz2 = nullptr;
    std::vector<float> xyz1HostData = {0, 1, 2, 3, 4, 5, 6, 7};
    std::vector<float> xyz2HostData = {1, 1, 1, 2, 2, 2, 3, 3};
    std::vector<int32_t> idx1HostData = {0, 1, 2, 3};
    std::vector<int32_t> idx2HostData = {0, 1, 2, 3};
    std::vector<float> gradDist1HostData = {0, 1, 2, 3};
    std::vector<float> gradDist2HostData = {0, 1, 2, 3};
    std::vector<float> gradXyz1HostData = {0, 0, 0, 0, 0, 0, 0, 0};
    std::vector<float> gradXyz2HostData = {0, 0, 0, 0, 0, 0, 0, 0};
    // Create the xyz1 aclTensor.
    ret = CreateAclTensor(xyz1HostData, xyz1Shape, &xyz1DeviceAddr, aclDataType::ACL_FLOAT, &xyz1);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the xyz2 aclTensor.
    ret = CreateAclTensor(xyz2HostData, xyz2Shape, &xyz2DeviceAddr, aclDataType::ACL_FLOAT, &xyz2);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the idx1 aclTensor.
    ret = CreateAclTensor(idx1HostData, idx1Shape, &idx1DeviceAddr, aclDataType::ACL_INT32, &idx1);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the idx2 aclTensor.
    ret = CreateAclTensor(idx2HostData, idx2Shape, &idx2DeviceAddr, aclDataType::ACL_INT32, &idx2);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the gradDist1 aclTensor.
    ret = CreateAclTensor(gradDist1HostData, gradDist1Shape, &gradDist1DeviceAddr, aclDataType::ACL_FLOAT, &gradDist1);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the gradDist2 aclTensor.
    ret = CreateAclTensor(gradDist2HostData, gradDist2Shape, &gradDist2DeviceAddr, aclDataType::ACL_FLOAT, &gradDist2);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the gradXyz1 aclTensor.
    ret = CreateAclTensor(gradXyz1HostData, gradXyz1Shape, &gradXyz1DeviceAddr, aclDataType::ACL_FLOAT, &gradXyz1);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the gradXyz2 aclTensor.
    ret = CreateAclTensor(gradXyz2HostData, gradXyz2Shape, &gradXyz2DeviceAddr, aclDataType::ACL_FLOAT, &gradXyz2);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    // 3. Call the CANN operator library API. Replace it with the specific API.
    uint64_t workspaceSize = 0;
    aclOpExecutor* executor;
    // Call the first-phase API of aclnnChamferDistanceBackward.
    ret = aclnnChamferDistanceBackwardGetWorkspaceSize(xyz1, xyz2, idx1, idx2, gradDist1, gradDist2, gradXyz1, gradXyz2,
                                                       &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnChamferDistanceBackwardGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
    // Apply for 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 aclnnChamferDistanceBackward.
    ret = aclnnChamferDistanceBackward(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnChamferDistanceBackward failed. ERROR: %d\n", ret); return ret);

    // 4. (Boilerplate) Synchronize and 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 the device side memory to the host side. Modify based on the specific API definition.
    auto size = GetShapeSize(gradXyz1Shape);
    std::vector<float> resultData(size, 0);
    ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), gradXyz1DeviceAddr,
                      size * sizeof(float), 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("result1[%ld] is: %f\n", i, resultData[i]);
    }

    ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), gradXyz2DeviceAddr,
                      size * sizeof(float), 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("result2[%ld] is: %f\n", i, resultData[i]);
    }

    // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
    aclDestroyTensor(xyz1);
    aclDestroyTensor(xyz2);
    aclDestroyTensor(idx1);
    aclDestroyTensor(idx2);
    aclDestroyTensor(gradDist1);
    aclDestroyTensor(gradDist2);
    aclDestroyTensor(gradXyz1);
    aclDestroyTensor(gradXyz2);

    // 7. Release device resources. Modify based on the specific API definition.
    aclrtFree(xyz1DeviceAddr);
    aclrtFree(xyz2DeviceAddr);
    aclrtFree(idx1DeviceAddr);
    aclrtFree(idx2DeviceAddr);
    aclrtFree(gradDist1DeviceAddr);
    aclrtFree(gradDist2DeviceAddr);
    aclrtFree(gradXyz1DeviceAddr);
    aclrtFree(gradXyz2DeviceAddr);
    if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();
    return 0;
}