xref: /aosp_15_r20/external/pytorch/aten/src/ATen/native/quantized/cpu/qnnpack/src/leaky-relu.c (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 #include <assert.h>
10 #include <math.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 
15 #include <pytorch_qnnpack.h>
16 #include <qnnpack/log.h>
17 #include <qnnpack/operator.h>
18 
pytorch_qnnp_create_leaky_relu_nc_q8(size_t channels,float negative_slope,uint8_t input_zero_point,float input_scale,uint8_t output_zero_point,float output_scale,uint8_t output_min,uint8_t output_max,uint32_t flags,pytorch_qnnp_operator_t * leaky_relu_out)19 enum pytorch_qnnp_status pytorch_qnnp_create_leaky_relu_nc_q8(
20     size_t channels,
21     float negative_slope,
22     uint8_t input_zero_point,
23     float input_scale,
24     uint8_t output_zero_point,
25     float output_scale,
26     uint8_t output_min,
27     uint8_t output_max,
28     uint32_t flags,
29     pytorch_qnnp_operator_t* leaky_relu_out) {
30   pytorch_qnnp_operator_t leaky_relu_op = NULL;
31   enum pytorch_qnnp_status status = pytorch_qnnp_status_uninitialized;
32 
33   if (!pytorch_qnnp_params.initialized) {
34     pytorch_qnnp_log_error(
35         "pytorch_qnnp_create_leaky_relu_nc_q8 failed because QNNPACK is not properly initialized");
36     goto error;
37   }
38 
39   status = pytorch_qnnp_status_invalid_parameter;
40 
41   if (channels == 0) {
42     pytorch_qnnp_log_error(
43         "failed to create Leaky ReLU operator with %zu channels: number of channels must be non-zero",
44         channels);
45     goto error;
46   }
47 
48   if (negative_slope <= 0.0f || !isnormal(negative_slope)) {
49     pytorch_qnnp_log_error(
50         "failed to create Leaky ReLU operator with %.7g negative slope: slope must be finite and positive",
51         negative_slope);
52     goto error;
53   }
54 
55   if (negative_slope > 1.0f) {
56     pytorch_qnnp_log_error(
57         "failed to create Leaky ReLU operator with %.7g negative slope: slope must not exceed 1.0",
58         negative_slope);
59     goto error;
60   }
61 
62   if (input_scale <= 0.0f || !isnormal(input_scale)) {
63     pytorch_qnnp_log_error(
64         "failed to create Leaky ReLU operator with %.7g input scale: scale must be finite and positive",
65         input_scale);
66     goto error;
67   }
68 
69   if (output_scale <= 0.0f || !isnormal(output_scale)) {
70     pytorch_qnnp_log_error(
71         "failed to create Leaky ReLU operator with %.7g output scale: scale must be finite and positive",
72         output_scale);
73     goto error;
74   }
75 
76   if (output_min >= output_max) {
77     pytorch_qnnp_log_error(
78         "failed to create Leaky ReLU operator with [%" PRIu8 ", %" PRIu8
79         "] output range: range min must be below range max",
80         output_min,
81         output_max);
82     goto error;
83   }
84 
85   status = pytorch_qnnp_status_unsupported_parameter;
86 
87   const float input_output_scale = input_scale / output_scale;
88   if (input_output_scale < 0x1.0p-8f || input_output_scale >= 0x1.0p+8f) {
89     pytorch_qnnp_log_error(
90         "failed to create Leaky ReLU operator with %.7g input-to-output scale ratio: "
91         "scale ratio must be in [2**-8, 2**8) range",
92         input_output_scale);
93     goto error;
94   }
95 
96   status = pytorch_qnnp_status_out_of_memory;
97 
98   leaky_relu_op = calloc(1, sizeof(struct pytorch_qnnp_operator));
99   if (leaky_relu_op == NULL) {
100     pytorch_qnnp_log_error(
101         "failed to allocate %zu bytes for pytorch_qnnp_operator structure",
102         sizeof(struct pytorch_qnnp_operator));
103     goto error;
104   }
105 
106   leaky_relu_op->lookup_table = malloc(256 * sizeof(uint8_t));
107   if (leaky_relu_op->lookup_table == NULL) {
108     pytorch_qnnp_log_error(
109         "failed to allocate 256 bytes for Leaky ReLU lookup table");
110     goto error;
111   }
112 
113   uint8_t* lookup_table = leaky_relu_op->lookup_table;
114   const float scaled_min_less_zero_point =
115       (float)((int32_t)output_min - (int32_t)output_zero_point);
116   const float scaled_max_less_zero_point =
117       (float)((int32_t)output_max - (int32_t)output_zero_point);
118   for (int32_t i = 0; i < 256; i++) {
119     const float x =
120         input_output_scale * (float)(i - (int32_t)(uint32_t)input_zero_point);
121     float y = x < 0.0f ? x * negative_slope : x;
122     if (y < scaled_min_less_zero_point) {
123       y = scaled_min_less_zero_point;
124     }
125     if (y > scaled_max_less_zero_point) {
126       y = scaled_max_less_zero_point;
127     }
128     lookup_table[(uint32_t)i] = (uint8_t)(lrintf(y) + (long)output_zero_point);
129   }
130 
131   leaky_relu_op->channels = channels;
132 
133   leaky_relu_op->ukernel_type = pytorch_qnnp_ukernel_type_lut;
134   leaky_relu_op->format = pytorch_qnnp_format_quint8;
135 
136   *leaky_relu_out = leaky_relu_op;
137   return pytorch_qnnp_status_success;
138 
139 error:
140   pytorch_qnnp_delete_operator(leaky_relu_op);
141   return status;
142 }
143 
pytorch_qnnp_setup_leaky_relu_nc_q8(pytorch_qnnp_operator_t leaky_relu,size_t batch_size,const uint8_t * input,size_t input_stride,uint8_t * output,size_t output_stride)144 enum pytorch_qnnp_status pytorch_qnnp_setup_leaky_relu_nc_q8(
145     pytorch_qnnp_operator_t leaky_relu,
146     size_t batch_size,
147     const uint8_t* input,
148     size_t input_stride,
149     uint8_t* output,
150     size_t output_stride) {
151   if (!pytorch_qnnp_params.initialized) {
152     pytorch_qnnp_log_error(
153         "pytorch_qnnp_setup_leaky_relu_nc_q8 failed because QNNPACK is not properly initialized");
154     return pytorch_qnnp_status_uninitialized;
155   }
156 
157   if (batch_size == 0) {
158     leaky_relu->batch_size = 0;
159     return pytorch_qnnp_status_success;
160   }
161 
162   leaky_relu->batch_size = batch_size;
163   leaky_relu->input = input;
164   leaky_relu->input_pixel_stride = input_stride;
165   leaky_relu->output = output;
166   leaky_relu->output_pixel_stride = output_stride;
167 
168   return pytorch_qnnp_status_success;
169 }
170