1use crate::types::{Dims, Result, Tile};
4
5pub fn f16_to_f32(h: u16) -> f32 {
7 let sign = (h >> 15) & 0x0001;
8 let exp = (h >> 10) & 0x001F;
9 let mant = h & 0x03FF;
10
11 if exp == 0 {
12 if mant == 0 {
13 f32::from_bits((sign as u32) << 31)
14 } else {
15 let mut m = mant as u32;
17 let mut e = 0i32;
18 while (m & 0x0400) == 0 {
19 m <<= 1;
20 e -= 1;
21 }
22 m &= 0x03FF;
23 let exp_f32 = (127 - 15 + 1 + e) as u32;
24 f32::from_bits(((sign as u32) << 31) | (exp_f32 << 23) | (m << 13))
25 }
26 } else if exp == 0x1F {
27 f32::from_bits(((sign as u32) << 31) | (0xFF << 23) | ((mant as u32) << 13))
29 } else {
30 let exp_f32 = (exp as u32 + 127 - 15) & 0xFF;
32 f32::from_bits(((sign as u32) << 31) | (exp_f32 << 23) | ((mant as u32) << 13))
33 }
34}
35
36pub fn f32_to_f16(f: f32) -> u16 {
38 let bits = f.to_bits();
39 let sign = (bits >> 31) & 0x0001;
40 let exp = (bits >> 23) & 0x00FF;
41 let mant = bits & 0x007F_FFFF;
42
43 if exp == 0xFF {
44 ((sign as u16) << 15) | (0x001F << 10) | ((mant >> 13) as u16)
46 } else if exp == 0 {
47 (sign as u16) << 15
48 } else {
49 let new_exp = exp as i32 - 127 + 15;
50 if new_exp >= 31 {
51 ((sign as u16) << 15) | (0x001F << 10)
53 } else if new_exp <= 0 {
54 (sign as u16) << 15
56 } else {
57 ((sign as u16) << 15) | ((new_exp as u16) << 10) | ((mant >> 13) as u16)
58 }
59 }
60}
61
62pub fn convolve_f32(
64 stride: Tile,
65 padding: Tile,
66 dilation: Tile,
67 input_dims: &Dims,
68 input: &[f32],
69 filter_dims: &Dims,
70 kernel: &[f32],
71 bias: Option<&[f32]>,
72 output_dims: &Dims,
73 output: &mut [f32],
74) -> Result<()> {
75 let input_batches = input_dims.n as usize;
76 let input_h = input_dims.h as usize;
77 let input_w = input_dims.w as usize;
78 let input_c = input_dims.c as usize;
79
80 let kernel_h = filter_dims.h as usize;
81 let kernel_w = filter_dims.w as usize;
82 let kernel_c = filter_dims.c as usize;
83
84 let output_h = output_dims.h as usize;
85 let output_w = output_dims.w as usize;
86 let output_c = output_dims.c as usize;
87
88 let groups = input_c / kernel_c;
89 let output_c_per_group = output_c / groups;
90
91 for b in 0..input_batches {
92 for out_y in 0..output_h {
93 let base_y = out_y as i32 * stride.h - padding.h;
94 for out_x in 0..output_w {
95 let base_x = out_x as i32 * stride.w - padding.w;
96
97 for g in 0..groups {
98 for out_ch_idx in 0..output_c_per_group {
99 let out_c = g * output_c_per_group + out_ch_idx;
100 let mut acc: f32 = match bias {
101 Some(b_slice) => b_slice[out_c],
102 None => 0.0,
103 };
104
105 for ky in 0..kernel_h {
106 let in_y = base_y + ky as i32 * dilation.h;
107 if in_y >= 0 && in_y < input_dims.h {
108 for kx in 0..kernel_w {
109 let in_x = base_x + kx as i32 * dilation.w;
110 if in_x >= 0 && in_x < input_dims.w {
111 let in_idx_base = ((b * input_h + in_y as usize) * input_w
112 + in_x as usize)
113 * input_c
114 + g * kernel_c;
115 let ker_idx_base =
116 ((out_c * kernel_h + ky) * kernel_w + kx) * kernel_c;
117
118 for ic in 0..kernel_c {
119 acc +=
120 input[in_idx_base + ic] * kernel[ker_idx_base + ic];
121 }
122 }
123 }
124 }
125 }
126
127 let out_idx =
128 ((b * output_h + out_y) * output_w + out_x) * output_c + out_c;
129 output[out_idx] = acc;
130 }
131 }
132 }
133 }
134 }
135
136 Ok(())
137}
138
139pub fn fully_connected_f32(
141 input_dims: &Dims,
142 input: &[f32],
143 filter_dims: &Dims,
144 kernel: &[f32],
145 bias: Option<&[f32]>,
146 output_dims: &Dims,
147 output: &mut [f32],
148) -> Result<()> {
149 let batches = input_dims.n as usize;
150 let accum_depth = filter_dims.n as usize;
151 let output_depth = output_dims.c as usize;
152
153 for b in 0..batches {
154 let input_batch = &input[b * accum_depth..(b + 1) * accum_depth];
155 let output_batch = &mut output[b * output_depth..(b + 1) * output_depth];
156
157 for out_c in 0..output_depth {
158 let mut acc: f32 = match bias {
159 Some(b_slice) => b_slice[out_c],
160 None => 0.0,
161 };
162
163 let kernel_row = &kernel[out_c * accum_depth..(out_c + 1) * accum_depth];
164
165 for i in 0..accum_depth {
166 acc += input_batch[i] * kernel_row[i];
167 }
168
169 output_batch[out_c] = acc;
170 }
171 }
172 Ok(())
173}
174
175pub fn relu_f32(data: &mut [f32]) {
177 for val in data.iter_mut() {
178 if *val < 0.0 {
179 *val = 0.0;
180 }
181 }
182}
183
184pub fn relu6_f32(data: &mut [f32]) {
186 for val in data.iter_mut() {
187 if *val < 0.0 {
188 *val = 0.0;
189 } else if *val > 6.0 {
190 *val = 6.0;
191 }
192 }
193}
194
195fn exp_f32(x: f32) -> f32 {
197 if x < -88.0 {
198 return 0.0;
199 }
200 if x > 88.0 {
201 return f32::INFINITY;
202 }
203 let y = x / 64.0;
205 let mut poly = 1.0
206 + y * (1.0
207 + y * (0.5
208 + y * (1.0 / 6.0
209 + y * (1.0 / 24.0
210 + y * (1.0 / 120.0 + y * (1.0 / 720.0 + y * (1.0 / 5040.0)))))));
211 for _ in 0..6 {
212 poly *= poly;
213 }
214 poly
215}
216
217pub fn softmax_f32(
219 input: &[f32],
220 num_rows: usize,
221 row_size: usize,
222 output: &mut [f32],
223) -> Result<()> {
224 for row in 0..num_rows {
225 let in_row = &input[row * row_size..(row + 1) * row_size];
226 let out_row = &mut output[row * row_size..(row + 1) * row_size];
227
228 let mut max_val = in_row[0];
229 for &val in &in_row[1..] {
230 if val > max_val {
231 max_val = val;
232 }
233 }
234
235 let mut sum = 0.0f32;
236 for (i, &val) in in_row.iter().enumerate() {
237 let diff = val - max_val;
238 let exp_val = exp_f32(diff);
239 out_row[i] = exp_val;
240 sum += exp_val;
241 }
242
243 if sum > 0.0 {
244 let inv_sum = 1.0 / sum;
245 for val in out_row.iter_mut() {
246 *val *= inv_sum;
247 }
248 }
249 }
250 Ok(())
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn test_f16_conversion() {
259 let val = 3.14159f32;
260 let h = f32_to_f16(val);
261 let back = f16_to_f32(h);
262 assert!((back - val).abs() < 0.01);
263 }
264
265 #[test]
266 fn test_fully_connected_f32() {
267 let input_dims = Dims::new(1, 1, 1, 2);
268 let input = [1.5f32, 2.5f32];
269 let filter_dims = Dims::new(2, 1, 1, 1);
270 let kernel = [2.0f32, 4.0f32];
271 let output_dims = Dims::new(1, 1, 1, 1);
272 let mut output = [0.0f32; 1];
273
274 fully_connected_f32(
275 &input_dims,
276 &input,
277 &filter_dims,
278 &kernel,
279 None,
280 &output_dims,
281 &mut output,
282 )
283 .unwrap();
284
285 assert_eq!(output[0], 13.0);
287 }
288}