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_sigmoid_nc_q8( |
21 | size_t channels, |
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 | qnnp_operator_t* sigmoid_out) |
30 | { |
31 | qnnp_operator_t sigmoid_op = NULL; |
32 | enum qnnp_status status = qnnp_status_uninitialized; |
33 | |
34 | if (!qnnp_params.initialized) { |
35 | qnnp_log_error("qnnp_create_sigmoid_nc_q8 failed because QNNPACK is not properly initialized" ); |
36 | goto error; |
37 | } |
38 | |
39 | status = qnnp_status_invalid_parameter; |
40 | |
41 | if (channels == 0) { |
42 | qnnp_log_error( |
43 | "failed to create Sigmoid operator with %zu channels: number of channels must be non-zero" , channels); |
44 | goto error; |
45 | } |
46 | |
47 | if (input_scale <= 0.0f || !isnormal(input_scale)) { |
48 | qnnp_log_error( |
49 | "failed to create Sigmoid operator with %.7g input scale: scale must be finite and positive" , input_scale); |
50 | goto error; |
51 | } |
52 | |
53 | if (output_scale <= 0.0f || !isnormal(output_scale)) { |
54 | qnnp_log_error( |
55 | "failed to create Sigmoid operator with %.7g output scale: scale must be finite and positive" , output_scale); |
56 | goto error; |
57 | } |
58 | |
59 | if (output_min >= output_max) { |
60 | qnnp_log_error( |
61 | "failed to create Sigmoid operator with [%" PRIu8 ", %" PRIu8 "] output range: range min must be below range max" , |
62 | output_min, output_max); |
63 | goto error; |
64 | } |
65 | |
66 | status = qnnp_status_unsupported_parameter; |
67 | |
68 | if (output_scale != 0x1.0p-8f) { |
69 | qnnp_log_error( |
70 | "failed to create Sigmoid operator with %.7g output scale: only output scale of 1/256 is supported" , |
71 | output_scale); |
72 | goto error; |
73 | } |
74 | |
75 | if (output_zero_point != 0) { |
76 | qnnp_log_error( |
77 | "failed to create Sigmoid operator with %" PRIu8 " output zero point: only output zero point of 0 is supported" , |
78 | output_zero_point); |
79 | goto error; |
80 | } |
81 | |
82 | status = qnnp_status_out_of_memory; |
83 | |
84 | sigmoid_op = calloc(1, sizeof(struct qnnp_operator)); |
85 | if (sigmoid_op == NULL) { |
86 | qnnp_log_error("failed to allocate %zu bytes for qnnp_operator structure" , sizeof(struct qnnp_operator)); |
87 | goto error; |
88 | } |
89 | |
90 | sigmoid_op->lookup_table = malloc(256 * sizeof(uint8_t)); |
91 | if (sigmoid_op->lookup_table == NULL) { |
92 | qnnp_log_error("failed to allocate 256 bytes for Sigmoid lookup table" ); |
93 | goto error; |
94 | } |
95 | |
96 | uint8_t* lookup_table = sigmoid_op->lookup_table; |
97 | const float scaled_min = (float) (int32_t) output_min; |
98 | const float scaled_max = (float) (int32_t) output_max; |
99 | for (int32_t i = 0; i < 256; i++) { |
100 | const float x = input_scale * (float) (i - (int32_t) (uint32_t) input_zero_point); |
101 | /* Scale sigmoid(x) by 1 / output scale = 256.0 */ |
102 | float scaled_sigmoid_x = 256.0f / (1.0f + expf(-x)); |
103 | if (scaled_sigmoid_x < scaled_min) { |
104 | scaled_sigmoid_x = scaled_min; |
105 | } |
106 | if (scaled_sigmoid_x > scaled_max) { |
107 | scaled_sigmoid_x = scaled_max; |
108 | } |
109 | lookup_table[(uint32_t) i] = (uint8_t) lrintf(scaled_sigmoid_x); |
110 | } |
111 | |
112 | sigmoid_op->channels = channels; |
113 | |
114 | sigmoid_op->ukernel_type = qnnp_ukernel_type_lut; |
115 | sigmoid_op->format = qnnp_format_quint8; |
116 | |
117 | *sigmoid_out = sigmoid_op; |
118 | return qnnp_status_success; |
119 | |
120 | error: |
121 | qnnp_delete_operator(sigmoid_op); |
122 | return status; |
123 | } |
124 | |
125 | enum qnnp_status qnnp_setup_sigmoid_nc_q8( |
126 | qnnp_operator_t sigmoid, |
127 | size_t batch_size, |
128 | const uint8_t* input, |
129 | size_t input_stride, |
130 | uint8_t* output, |
131 | size_t output_stride) |
132 | { |
133 | if (!qnnp_params.initialized) { |
134 | qnnp_log_error("qnnp_setup_sigmoid_nc_q8 failed because QNNPACK is not properly initialized" ); |
135 | return qnnp_status_uninitialized; |
136 | } |
137 | |
138 | if (batch_size == 0) { |
139 | sigmoid->batch_size = 0; |
140 | return qnnp_status_success; |
141 | } |
142 | |
143 | sigmoid->batch_size = batch_size; |
144 | sigmoid->input = input; |
145 | sigmoid->input_pixel_stride = input_stride; |
146 | sigmoid->output = output; |
147 | sigmoid->output_pixel_stride = output_stride; |
148 | |
149 | return qnnp_status_success; |
150 | } |
151 | |