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 <qnnpack.h>
16#include <qnnpack/operator.h>
17#include <qnnpack/log.h>
18
19
20enum qnnp_status qnnp_create_leaky_relu_nc_q8(
21 size_t channels,
22 float negative_slope,
23 uint8_t input_zero_point,
24 float input_scale,
25 uint8_t output_zero_point,
26 float output_scale,
27 uint8_t output_min,
28 uint8_t output_max,
29 uint32_t flags,
30 qnnp_operator_t* leaky_relu_out)
31{
32 qnnp_operator_t leaky_relu_op = NULL;
33 enum qnnp_status status = qnnp_status_uninitialized;
34
35 if (!qnnp_params.initialized) {
36 qnnp_log_error("qnnp_create_leaky_relu_nc_q8 failed because QNNPACK is not properly initialized");
37 goto error;
38 }
39
40 status = qnnp_status_invalid_parameter;
41
42 if (channels == 0) {
43 qnnp_log_error(
44 "failed to create Leaky ReLU operator with %zu channels: number of channels must be non-zero", channels);
45 goto error;
46 }
47
48 if (negative_slope <= 0.0f || !isnormal(negative_slope)) {
49 qnnp_log_error(
50 "failed to create Leaky ReLU operator with %.7g negative slope: slope must be finite and positive", negative_slope);
51 goto error;
52 }
53
54 if (negative_slope > 1.0f) {
55 qnnp_log_error(
56 "failed to create Leaky ReLU operator with %.7g negative slope: slope must not exceed 1.0", negative_slope);
57 goto error;
58 }
59
60 if (input_scale <= 0.0f || !isnormal(input_scale)) {
61 qnnp_log_error(
62 "failed to create Leaky ReLU operator with %.7g input scale: scale must be finite and positive", input_scale);
63 goto error;
64 }
65
66 if (output_scale <= 0.0f || !isnormal(output_scale)) {
67 qnnp_log_error(
68 "failed to create Leaky ReLU operator with %.7g output scale: scale must be finite and positive", output_scale);
69 goto error;
70 }
71
72 if (output_min >= output_max) {
73 qnnp_log_error(
74 "failed to create Leaky ReLU operator with [%" PRIu8 ", %" PRIu8 "] output range: range min must be below range max",
75 output_min, output_max);
76 goto error;
77 }
78
79 status = qnnp_status_unsupported_parameter;
80
81 const float input_output_scale = input_scale / output_scale;
82 if (input_output_scale < 0x1.0p-8f || input_output_scale >= 0x1.0p+8f) {
83 qnnp_log_error(
84 "failed to create Leaky ReLU operator with %.7g input-to-output scale ratio: "
85 "scale ratio must be in [2**-8, 2**8) range",
86 input_output_scale);
87 goto error;
88 }
89
90 status = qnnp_status_out_of_memory;
91
92 leaky_relu_op = calloc(1, sizeof(struct qnnp_operator));
93 if (leaky_relu_op == NULL) {
94 qnnp_log_error("failed to allocate %zu bytes for qnnp_operator structure", sizeof(struct qnnp_operator));
95 goto error;
96 }
97
98 leaky_relu_op->lookup_table = malloc(256 * sizeof(uint8_t));
99 if (leaky_relu_op->lookup_table == NULL) {
100 qnnp_log_error("failed to allocate 256 bytes for Leaky ReLU lookup table");
101 goto error;
102 }
103
104 uint8_t* lookup_table = leaky_relu_op->lookup_table;
105 const float scaled_min_less_zero_point = (float) ((int32_t) output_min - (int32_t) output_zero_point);
106 const float scaled_max_less_zero_point = (float) ((int32_t) output_max - (int32_t) output_zero_point);
107 for (int32_t i = 0; i < 256; i++) {
108 const float x = input_output_scale * (float) (i - (int32_t) (uint32_t) input_zero_point);
109 float y = x < 0.0f ? x * negative_slope : x;
110 if (y < scaled_min_less_zero_point) {
111 y = scaled_min_less_zero_point;
112 }
113 if (y > scaled_max_less_zero_point) {
114 y = scaled_max_less_zero_point;
115 }
116 lookup_table[(uint32_t) i] = (uint8_t) (lrintf(y) + (long) output_zero_point);
117 }
118
119 leaky_relu_op->channels = channels;
120
121 leaky_relu_op->ukernel_type = qnnp_ukernel_type_lut;
122 leaky_relu_op->format = qnnp_format_quint8;
123
124 *leaky_relu_out = leaky_relu_op;
125 return qnnp_status_success;
126
127error:
128 qnnp_delete_operator(leaky_relu_op);
129 return status;
130}
131
132enum qnnp_status qnnp_setup_leaky_relu_nc_q8(
133 qnnp_operator_t leaky_relu,
134 size_t batch_size,
135 const uint8_t* input,
136 size_t input_stride,
137 uint8_t* output,
138 size_t output_stride)
139{
140 if (!qnnp_params.initialized) {
141 qnnp_log_error("qnnp_setup_leaky_relu_nc_q8 failed because QNNPACK is not properly initialized");
142 return qnnp_status_uninitialized;
143 }
144
145 if (batch_size == 0) {
146 leaky_relu->batch_size = 0;
147 return qnnp_status_success;
148 }
149
150 leaky_relu->batch_size = batch_size;
151 leaky_relu->input = input;
152 leaky_relu->input_pixel_stride = input_stride;
153 leaky_relu->output = output;
154 leaky_relu->output_pixel_stride = output_stride;
155
156 return qnnp_status_success;
157}
158