skippy_protocol/binary/
activation.rs1use std::io;
2
3use super::{
4 WireActivationDType, invalid_data, state_flags, types::MAX_STAGE_DECODED_ACTIVATION_BYTES,
5};
6
7pub fn activation_wire_bytes(
8 dtype: WireActivationDType,
9 token_count: i32,
10 n_embd: i32,
11) -> io::Result<usize> {
12 activation_wire_bytes_with_state_flags(dtype, token_count, n_embd, 0)
13}
14
15pub fn activation_wire_bytes_with_state_flags(
16 dtype: WireActivationDType,
17 token_count: i32,
18 n_embd: i32,
19 state_flag_bits: i32,
20) -> io::Result<usize> {
21 if token_count < 0 || n_embd < 0 {
22 return Err(invalid_data("negative activation dimensions"));
23 }
24 let token_count = (token_count as usize)
25 .checked_mul(activation_payload_multiplier_from_state_flags(
26 state_flag_bits,
27 ))
28 .ok_or_else(|| invalid_data("activation token count overflow"))?;
29 let n_embd = n_embd as usize;
30 let elements = token_count
31 .checked_mul(n_embd)
32 .ok_or_else(|| invalid_data("activation element count overflow"))?;
33 match dtype {
34 WireActivationDType::F32 => elements
35 .checked_mul(4)
36 .ok_or_else(|| invalid_data("activation byte count overflow")),
37 WireActivationDType::F16 => elements
38 .checked_mul(2)
39 .ok_or_else(|| invalid_data("activation byte count overflow")),
40 WireActivationDType::Q8 => token_count
41 .checked_mul(4)
42 .and_then(|scales| scales.checked_add(elements))
43 .ok_or_else(|| invalid_data("activation byte count overflow")),
44 }
45}
46
47pub(crate) fn activation_decoded_f32_bytes_with_state_flags(
48 token_count: i32,
49 n_embd: i32,
50 state_flag_bits: i32,
51) -> io::Result<usize> {
52 activation_wire_bytes_with_state_flags(
53 WireActivationDType::F32,
54 token_count,
55 n_embd,
56 state_flag_bits,
57 )
58}
59
60pub fn encode_f32_activation_payload(
61 dtype: WireActivationDType,
62 token_count: i32,
63 n_embd: i32,
64 f32_payload: &[u8],
65) -> io::Result<Vec<u8>> {
66 encode_f32_activation_payload_with_state_flags(dtype, token_count, n_embd, f32_payload, 0)
67}
68
69pub fn encode_f32_activation_payload_with_state_flags(
70 dtype: WireActivationDType,
71 token_count: i32,
72 n_embd: i32,
73 f32_payload: &[u8],
74 state_flag_bits: i32,
75) -> io::Result<Vec<u8>> {
76 let expected_f32_bytes = activation_wire_bytes_with_state_flags(
77 WireActivationDType::F32,
78 token_count,
79 n_embd,
80 state_flag_bits,
81 )?;
82 if expected_f32_bytes > MAX_STAGE_DECODED_ACTIVATION_BYTES {
83 return Err(invalid_data(
84 "decoded activation payload byte count exceeds maximum",
85 ));
86 }
87 if f32_payload.len() != expected_f32_bytes {
88 return Err(invalid_data("F32 activation payload size mismatch"));
89 }
90 match dtype {
91 WireActivationDType::F32 => Ok(f32_payload.to_vec()),
92 WireActivationDType::F16 => encode_f32_to_f16_bytes(f32_payload),
93 WireActivationDType::Q8 => {
94 let token_count = token_count
95 .checked_mul(activation_payload_multiplier_from_state_flags(state_flag_bits) as i32)
96 .ok_or_else(|| invalid_data("Q8 activation token count overflow"))?;
97 encode_f32_to_q8_bytes(f32_payload, token_count, n_embd)
98 }
99 }
100}
101
102pub fn activation_payload_multiplier_from_state_flags(state_flag_bits: i32) -> usize {
103 if (state_flag_bits & state_flags::GEMMA3N_ALTUP_SIDEBAND) != 0 {
104 4
105 } else if (state_flag_bits
106 & (state_flags::INKLING_MTP_EMBD_SIDEBAND | state_flags::RWKV7_V_FIRST_SIDEBAND))
107 != 0
108 {
109 2
110 } else {
111 1
112 }
113}
114
115pub(crate) fn decode_f16_to_f32_bytes(input: &[u8]) -> io::Result<Vec<u8>> {
116 if input.len() & 1 != 0 {
117 return Err(invalid_data("F16 activation payload has odd byte length"));
118 }
119 let decoded_bytes = input
120 .len()
121 .checked_mul(2)
122 .ok_or_else(|| invalid_data("decoded activation byte count overflow"))?;
123 if decoded_bytes > MAX_STAGE_DECODED_ACTIVATION_BYTES {
124 return Err(invalid_data(
125 "decoded activation payload byte count exceeds maximum",
126 ));
127 }
128 let mut out = Vec::with_capacity(decoded_bytes);
129 for chunk in input.chunks_exact(2) {
130 let bits = u16::from_le_bytes([chunk[0], chunk[1]]);
131 out.extend_from_slice(&f16_bits_to_f32(bits).to_le_bytes());
132 }
133 Ok(out)
134}
135
136fn encode_f32_to_f16_bytes(input: &[u8]) -> io::Result<Vec<u8>> {
137 if input.len() & 3 != 0 {
138 return Err(invalid_data("F32 activation payload size is not aligned"));
139 }
140 let mut out = Vec::with_capacity(input.len() / 2);
141 for chunk in input.chunks_exact(4) {
142 let value = f32::from_le_bytes(chunk.try_into().expect("chunks_exact size"));
143 out.extend_from_slice(&f32_to_f16_bits(value).to_le_bytes());
144 }
145 Ok(out)
146}
147
148pub(crate) fn decode_q8_to_f32_bytes_with_state_flags(
149 input: &[u8],
150 token_count: i32,
151 n_embd: i32,
152 state_flag_bits: i32,
153) -> io::Result<Vec<u8>> {
154 if token_count < 0 || n_embd < 0 {
155 return Err(invalid_data("negative Q8 activation dimensions"));
156 }
157 let token_count = (token_count as usize)
158 .checked_mul(activation_payload_multiplier_from_state_flags(
159 state_flag_bits,
160 ))
161 .ok_or_else(|| invalid_data("Q8 activation token count overflow"))?;
162 let n_embd = n_embd as usize;
163 let scale_bytes = token_count
164 .checked_mul(4)
165 .ok_or_else(|| invalid_data("Q8 scale byte count overflow"))?;
166 let value_bytes = token_count
167 .checked_mul(n_embd)
168 .ok_or_else(|| invalid_data("Q8 value byte count overflow"))?;
169 let expected_bytes = scale_bytes
170 .checked_add(value_bytes)
171 .ok_or_else(|| invalid_data("Q8 activation payload byte count overflow"))?;
172 if input.len() != expected_bytes {
173 return Err(invalid_data("Q8 activation payload size mismatch"));
174 }
175 let decoded_bytes = value_bytes
176 .checked_mul(4)
177 .ok_or_else(|| invalid_data("decoded activation byte count overflow"))?;
178 if decoded_bytes > MAX_STAGE_DECODED_ACTIVATION_BYTES {
179 return Err(invalid_data(
180 "decoded activation payload byte count exceeds maximum",
181 ));
182 }
183 let mut out = Vec::with_capacity(decoded_bytes);
184 for token_index in 0..token_count {
185 let scale_offset = token_index * 4;
186 let scale = f32::from_le_bytes([
187 input[scale_offset],
188 input[scale_offset + 1],
189 input[scale_offset + 2],
190 input[scale_offset + 3],
191 ]);
192 let row_offset = scale_bytes + token_index * n_embd;
193 for value in &input[row_offset..row_offset + n_embd] {
194 let signed = *value as i8;
195 out.extend_from_slice(&((signed as f32) * scale).to_le_bytes());
196 }
197 }
198 Ok(out)
199}
200
201#[cfg(test)]
202pub(crate) fn decode_q8_to_f32_bytes(
203 input: &[u8],
204 token_count: i32,
205 n_embd: i32,
206) -> io::Result<Vec<u8>> {
207 decode_q8_to_f32_bytes_with_state_flags(input, token_count, n_embd, 0)
208}
209
210fn encode_f32_to_q8_bytes(input: &[u8], token_count: i32, n_embd: i32) -> io::Result<Vec<u8>> {
211 if token_count < 0 || n_embd < 0 {
212 return Err(invalid_data("negative Q8 activation dimensions"));
213 }
214 let token_count = token_count as usize;
215 let n_embd = n_embd as usize;
216 let expected_bytes = token_count
217 .checked_mul(n_embd)
218 .and_then(|elements| elements.checked_mul(4))
219 .ok_or_else(|| invalid_data("Q8 source byte count overflow"))?;
220 if input.len() != expected_bytes {
221 return Err(invalid_data("Q8 source payload size mismatch"));
222 }
223
224 let mut scales = Vec::with_capacity(token_count * 4);
225 let mut packed = Vec::with_capacity(token_count * n_embd);
226 for token_index in 0..token_count {
227 let row_offset = token_index * n_embd * 4;
228 let row = &input[row_offset..row_offset + n_embd * 4];
229 let mut max_abs = 0.0_f32;
230 for chunk in row.chunks_exact(4) {
231 let value = f32::from_le_bytes(chunk.try_into().expect("chunks_exact size"));
232 max_abs = max_abs.max(value.abs());
233 }
234 let scale = if max_abs > 0.0 { max_abs / 127.0 } else { 1.0 };
235 scales.extend_from_slice(&scale.to_le_bytes());
236 for chunk in row.chunks_exact(4) {
237 let value = f32::from_le_bytes(chunk.try_into().expect("chunks_exact size"));
238 let quantized = (value / scale).round().clamp(-127.0, 127.0) as i8;
239 packed.push(quantized as u8);
240 }
241 }
242 scales.extend_from_slice(&packed);
243 Ok(scales)
244}
245
246fn f16_bits_to_f32(bits: u16) -> f32 {
247 let sign = ((bits & 0x8000) as u32) << 16;
248 let exponent = (bits >> 10) & 0x1f;
249 let mantissa = bits & 0x03ff;
250 let f32_bits = if exponent == 0 {
251 if mantissa == 0 {
252 sign
253 } else {
254 let mut mant = mantissa as u32;
255 let mut exp = -14_i32;
256 while (mant & 0x0400) == 0 {
257 mant <<= 1;
258 exp -= 1;
259 }
260 mant &= 0x03ff;
261 let exp_bits = ((exp + 127) as u32) << 23;
262 sign | exp_bits | (mant << 13)
263 }
264 } else if exponent == 0x1f {
265 sign | 0x7f80_0000 | ((mantissa as u32) << 13)
266 } else {
267 let exp_bits = ((exponent as u32) + (127 - 15)) << 23;
268 sign | exp_bits | ((mantissa as u32) << 13)
269 };
270 f32::from_bits(f32_bits)
271}
272
273fn f32_to_f16_bits(value: f32) -> u16 {
274 let bits = value.to_bits();
275 let sign = ((bits >> 16) & 0x8000) as u16;
276 let exponent = ((bits >> 23) & 0xff) as i32;
277 let mantissa = bits & 0x007f_ffff;
278
279 if exponent == 0xff {
280 let nan_bit = if mantissa == 0 { 0 } else { 0x0200 };
281 return sign | 0x7c00 | nan_bit;
282 }
283
284 let half_exp = exponent - 127 + 15;
285 if half_exp >= 0x1f {
286 return sign | 0x7c00;
287 }
288 if half_exp <= 0 {
289 if half_exp < -10 {
290 return sign;
291 }
292 let mant = mantissa | 0x0080_0000;
293 let shift = (14 - half_exp) as u32;
294 let mut half_mant = (mant >> shift) as u16;
295 if ((mant >> (shift - 1)) & 1) != 0 {
296 half_mant = half_mant.saturating_add(1);
297 }
298 return sign | half_mant;
299 }
300
301 let mut half = sign | ((half_exp as u16) << 10) | ((mantissa >> 13) as u16);
302 if (mantissa & 0x0000_1000) != 0 {
303 half = half.saturating_add(1);
304 }
305 half
306}