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 | |
20 | enum 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 | |
127 | error: |
128 | qnnp_delete_operator(leaky_relu_op); |
129 | return status; |
130 | } |
131 | |
132 | enum 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 | |