aclnnChamferDistanceBackward
Applicable Products
| Product | Supported |
|---|---|
| No | |
| Yes | |
| Yes | |
| No | |
| No | |
| No |
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:
The backward operator computes the derivative of this formula, as follows:
Derivative of with respect to
Where: , 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 with respect to
Where , 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 with respect to
Where , 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 with respect to
where , and 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 with respect to , , , and are similar to the above process and are not repeated here.
The final formula is as follows, where i∈[0,n):
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;
}