aclnnRenorm&aclnnInplaceRenorm

📄 View source code

Supported Products

ProductSupported
Atlas A3 training series products/Atlas A3 inference series products√
Atlas A2 training products/Atlas A2 inference products√
Atlas 200I/500 A2 inference products×
Atlas inference series products×
Atlas training series products√

Function

  • Description: Returns a tensor where each sub-tensor of the input tensor self along the dimension dim is normalized so that the p-norm of the sub-tensor is less than maxNorm.

  • Formula:

    outputi={inputi,∣∣inputi∣∣p<=maxNorminputi∣∣inputi∣∣p⋅maxNorm,∣∣inputi∣∣p>maxNormoutput_i=\left\{ \begin{aligned} input_i,\quad ||input_i||_p <= maxNorm \\ \frac {input_i} {||input_i||_p} \cdot maxNorm,\quad ||input_i||_p>maxNorm \end{aligned} \right.

    Where: ii is the tensor slice of a dimension determined by dim:

    ∣∣inputi∣∣p=(∑i=0ninputip1p)||input_i||_p = (\sum_{i=0}^{n}{input_i^p}^\frac{1}{p})
  • Example:

    x = tensor([[1.,1.,1.],
                [2.,2.,2.],
                [3.,3.,3.]])
    In this example, p = 1, dim = 0, and maxNorm = 5. The parameters are passed to the aclnn API.
    Because dim = 0, the judgment and computation are performed in the unit of row (dimension 0).
    - The norm of the sub-tensor in the first row is 1 + 1 + 1 = 3, which is less than 5. Therefore, the sub-tensor remains unchanged.
    - The norm of the sub-tensor in the second row is 2 + 2 + 2 = 6, which is greater than 5. Therefore, the sub-tensor is computed as follows: (2/6) × 5 = 1.6667.
    - The norm of the sub-tensor in the third row is 3 + 3 + 3 = 9, which is greater than 5. Therefore, the sub-tensor is computed as follows: (3/9) × 5 = 1.6667.
      tensor([[1.0000,1.0000,1.0000],
             [1.6667,1.6667,1.6667],
             [1.6667,1.6667,1.6667]])
    If p = 2, the norm of the sub-tensor in the first row changes to √1 + 1 + 1 = 1.73 during computation. Similarly, the second and third rows change to:
    √2 × 2 + 2 × 2 + 2 × 2 = 3.46, √3 × 3 + 3 × 3 + 3 × 3 = 5.19

Prototype

  • aclnnRenorm and aclnnInplaceRenorm implement the same function in different ways. Select a proper operator based on your requirements.

    • aclnnRenorm: An output tensor object needs to be created to store the computation result.
    • aclnnInplaceRenorm: No output tensor object needs to be created, and the computation result is stored in the memory of the input tensor.
  • Each operator has two-phase API calls. First, aclnnRenormGetWorkspaceSize or aclnnInplaceRenormGetWorkspaceSize is called to obtain the workspace size required for computation and the executor that contains the operator computation process. Then, aclnnRenorm or aclnnInplaceRenorm is called to perform computation.

    aclnnStatus aclnnRenormGetWorkspaceSize(
      const aclTensor* self,
      const aclScalar* p,
      int64_t          dim,
      const aclScalar* maxNorm,
      aclTensor*       out,
      uint64_t*        workspaceSize,
      aclOpExecutor**  executor)
    aclnnStatus aclnnRenorm(
      void*          workspace,
      uint64_t       workspaceSize,
      aclOpExecutor* executor,
      aclrtStream    stream)
    aclnnStatus aclnnInplaceRenormGetWorkspaceSize(
      aclTensor*       selfRef,
      const aclScalar* p,
      int64_t          dim,
      const aclScalar* maxNorm,
      uint64_t*        workspaceSize,
      aclOpExecutor**  executor)
    aclnnStatus aclnnInplaceRenorm(
      void*          workspace,
      uint64_t       workspaceSize,
      aclOpExecutor* executor,
      aclrtStream    stream)

aclnnRenormGetWorkspaceSize

  • Parameters:

    Name Input/Output Description Usage Notes Data Type Data Format Dimension (Shape) Non-contiguous Tensor
    self Input Input for re-normalization computation, corresponding to `input` in the formula. Empty tensors are supported. FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √
    p Input Norm, corresponding to `p` in the formula. The value must be greater than or equal to 0. FLOAT32 - - -
    dim Input Dimension along which to calculate the norm, corresponding to `i` in the formula. The value range is [–(number of dimensions of self), (number of dimensions of self) – 1]. INT64 - - -
    maxNorm Input Maximum allowed normalization value, corresponding to `maxNorm` in the formula.
    • The value is greater than or equal to 0.
    • If the `p` norm (determined by the `p` value) of a dimension is greater than `maxNorm`, the value of the dimension is normalized with respect to the `p` norm and multiplied by `maxNorm`.
    • If the `p` norm (determined by the `p` value) of a dimension is less than `maxNorm`, the tensor of the dimension remains unchanged in the output.
    FLOAT32 - - -
    out Output Final output, corresponding to `output` in the formula.
    • Empty tensors are supported.
    • The data type and shape must be the same as those of the input parameter `self`.
    FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √
    workspaceSize Output Size of the workspace to be allocated on the device. - - - - -
    executor Output Operator executor, containing the operator computation process. - - - - -
    • Atlas training series products: The data types of self and out cannot be BFLOAT16.
  • Returns:

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

    The first-phase API implements input parameter verification. The following errors may be thrown:

    Return Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 The passed self, p, maxNorm, or out is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type of self or out is not supported.
    The shapes of self and out are inconsistent.
    The dtypes of self and out are inconsistent.
    p < 0
    The value of dim is not in the range [–(number of dimensions of self), (number of dimensions of self) – 1].
    maxNorm < 0
    The dimension of the input self is not in the range of [2,8].

aclnnRenorm

  • Parameters:

    Name Input/Output Description
    workspace Input Address of the workspace to be allocated on the device.
    workspaceSize Input Size of the workspace to be allocated on the device, which is obtained by the first-phase API aclnnRenormGetWorkspaceSize.
    executor Input Operator executor, containing the operator computation process.
    stream Input Stream for executing the task.
  • Returns:

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

aclnnInplaceRenormGetWorkspaceSize

  • Parameters:

    Name Input/Output Description Usage Notes Data Type Data Format Dimension (Shape) Non-contiguous Tensor
    selfRef Input/Output Input and final output for re-normalization computation, corresponding to `input` and `output` in the formula. Empty tensors are supported. FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √
    p Input Norm, corresponding to `p` in the formula. The value must be greater than or equal to 0. FLOAT32 - - -
    dim Input Dimension along which to calculate the norm, corresponding to `i` in the formula. The value range is [–(number of dimensions of selfRef), (number of dimensions of selfRef) – 1]. INT64 - - -
    maxNorm Input Maximum allowed normalization value, corresponding to `maxNorm` in the formula.
    • The value is greater than or equal to 0.
    • If the p norm (determined by the p value) of a dimension is greater than maxNorm, the value of the dimension is normalized with respect to the p norm and multiplied by maxNorm.
    • If the p norm (determined by the p value) of a dimension is less than maxNorm, the tensor of the dimension remains unchanged in the output.
    FLOAT32 - - -
    workspaceSize Output Size of the workspace to be allocated on the device. - - - - -
    executor Output Operator executor, containing the operator computation process. - - - - -
    • Atlas training series products: The data type of selfRef cannot be BFLOAT16.
  • Returns:

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

    The first-phase API implements input parameter verification. The following errors may be thrown:

    Return Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 The passed selfRef, p, or maxNorm is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type of selfRef is not supported.
    p < 0
    The value of dim is not in the range [–(number of dimensions of selfRef), (number of dimensions of selfRef) – 1].
    maxNorm < 0
    The dimension of the input selfRef is not in the range of [2,8].

aclnnInplaceRenorm

  • Parameters:

    Name Input/Output Description
    workspace Input Address of the workspace to be allocated on the device.
    workspaceSize Input Size of the workspace to be allocated on the device, which is obtained by the first-phase API aclnnInplaceRenormGetWorkspaceSize.
    executor Input Operator executor, containing the operator computation process.
    stream Input Stream for executing the task.
  • Returns:

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

Constraints

  • Deterministic compute:
    • aclnnRenorm defaults to a deterministic implementation.
    • aclnnInplaceRenorm defaults to a deterministic implementation.

Example

The following example is for reference only. For details, see Compilation and Running Sample.

  • aclnnRenorm sample code:

    #include <iostream>
    #include <vector>
    #include "acl/acl.h"
    #include "aclnnop/aclnn_renorm.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) {
      // (Fixed writing) 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 allocate memory on the device.
      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 the data on the host to the memory on the device.
      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. (Fixed writing) Initialize the device and stream. For details, see the ACL API manual.
      // Set the device ID in use.
      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 based on the API definition.
      std::vector<int64_t> selfShape = {3, 3};
      std::vector<int64_t> outShape = {3, 3};
      void* selfDeviceAddr = nullptr;
      void* outDeviceAddr = nullptr;
      aclTensor* self = nullptr;
      aclScalar* p = nullptr;
      aclScalar* maxNorm = nullptr;
      aclTensor* out = nullptr;
      std::vector<float> selfHostData = {1, 1, 1, 2, 2, 2, 3, 3, 3};
      std::vector<float> outHostData(9, 0);
      int64_t dim = -1;
      float pValue = 1.0f;
      float maxNormValue = 5.0f;
      // Create a self aclTensor.
      ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
      // Create a p aclScalar.
      p = aclCreateScalar(&pValue, aclDataType::ACL_FLOAT);
      CHECK_RET(p != nullptr, return ret);
      // Create a maxNorm aclScalar.
      maxNorm = aclCreateScalar(&maxNormValue, aclDataType::ACL_FLOAT);
      CHECK_RET(maxNorm != nullptr, return ret);
      // Create an out aclTensor.
      ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
    
      // 3. Call the CANN operator library API, which needs to be replaced with the actual API.
      uint64_t workspaceSize = 0;
      aclOpExecutor* executor;
      // Call the first-phase API of aclnnRenorm.
      ret = aclnnRenormGetWorkspaceSize(self, p, dim, maxNorm, out, &workspaceSize, &executor);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRenormGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
      // Allocate device memory based on workspaceSize computed 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 aclnnRenorm.
      ret = aclnnRenorm(workspaceAddr, workspaceSize, executor, stream);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRenorm failed. ERROR: %d\n", ret); return ret);
    
      // 4. (Fixed writing) Wait until the task execution is 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 memory to the host. Modify the configuration based on the 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 the configuration based on the API definition.
      aclDestroyTensor(self);
      aclDestroyScalar(p);
      aclDestroyScalar(maxNorm);
      aclDestroyTensor(out);
      return 0;
    }
  • aclnnInplaceRenorm sample code:

    #include <iostream>
    #include <vector>
    #include "acl/acl.h"
    #include "aclnnop/aclnn_renorm.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) {
      // (Fixed writing) 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 allocate memory on the device.
      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 the data on the host to the memory on the device.
      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. (Fixed writing) Initialize the device and stream. For details, see the ACL API manual.
      // Set the device ID in use.
      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 based on the API definition.
      std::vector<int64_t> selfRefShape = {3, 3};
      void* selfRefDeviceAddr = nullptr;
      aclTensor* selfRef = nullptr;
      aclScalar* p = nullptr;
      aclScalar* maxNorm = nullptr;
      aclTensor* out = nullptr;
      std::vector<float> selfRefHostData = {1, 1, 1, 2, 2, 2, 3, 3, 3};
      int64_t dim = -1;
      float pValue = 1.0f;
      float maxNormValue = 5.0f;
      // Create a selfRef aclTensor.
      ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_FLOAT, &selfRef);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
      // Create a p aclScalar.
      p = aclCreateScalar(&pValue, aclDataType::ACL_FLOAT);
      CHECK_RET(p != nullptr, return ret);
      // Create a maxNorm aclScalar.
      maxNorm = aclCreateScalar(&maxNormValue, aclDataType::ACL_FLOAT);
      CHECK_RET(maxNorm != nullptr, return ret);
    
      // 3. Call the CANN operator library API, which needs to be replaced with the actual API.
      uint64_t workspaceSize = 0;
      aclOpExecutor* executor;
      // Call the first-phase API of aclnnInplaceRenorm.
      ret = aclnnInplaceRenormGetWorkspaceSize(selfRef, p, dim, maxNorm, &workspaceSize, &executor);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRenormGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
      // Allocate device memory based on workspaceSize computed 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 aclnnInplaceRenorm.
      ret = aclnnInplaceRenorm(workspaceAddr, workspaceSize, executor, stream);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRenorm failed. ERROR: %d\n", ret); return ret);
    
      // 4. (Fixed writing) Wait until the task execution is 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 memory to the host. Modify the configuration based on the API definition.
      auto size = GetShapeSize(selfRefShape);
      std::vector<float> resultData(size, 0);
      ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfRefDeviceAddr,
                        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 the configuration based on the API definition.
      aclDestroyTensor(selfRef);
      aclDestroyScalar(p);
      aclDestroyScalar(maxNorm);
    
      // 7. Release device resources. Modify the configuration based on the API definition.
      aclrtFree(selfRefDeviceAddr);
      if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
      }
      aclrtDestroyStream(stream);
      aclrtResetDevice(deviceId);
      aclFinalize();
      return 0;
    }