1use crate::op::{Activation, BinaryOp, CmpOp, ReduceOp};
42
43impl BinaryOp {
44 pub const ALL: [BinaryOp; 14] = [
46 BinaryOp::Add,
47 BinaryOp::Sub,
48 BinaryOp::Mul,
49 BinaryOp::Div,
50 BinaryOp::Max,
51 BinaryOp::Min,
52 BinaryOp::Pow,
53 BinaryOp::Mod,
54 BinaryOp::BitAnd,
55 BinaryOp::BitOr,
56 BinaryOp::BitXor,
57 BinaryOp::Shl,
58 BinaryOp::Shr,
59 BinaryOp::Atan2,
60 ];
61
62 pub const fn opcode(self) -> u32 {
65 match self {
66 BinaryOp::Add => 0,
67 BinaryOp::Sub => 1,
68 BinaryOp::Mul => 2,
69 BinaryOp::Div => 3,
70 BinaryOp::Max => 4,
71 BinaryOp::Min => 5,
72 BinaryOp::Pow => 6,
73 BinaryOp::Mod => 7,
74 BinaryOp::BitAnd => 8,
75 BinaryOp::BitOr => 9,
76 BinaryOp::BitXor => 10,
77 BinaryOp::Shl => 11,
78 BinaryOp::Shr => 12,
79 BinaryOp::Atan2 => 13,
80 }
81 }
82}
83
84impl CmpOp {
85 pub const ALL: [CmpOp; 6] = [
87 CmpOp::Eq,
88 CmpOp::Ne,
89 CmpOp::Lt,
90 CmpOp::Le,
91 CmpOp::Gt,
92 CmpOp::Ge,
93 ];
94
95 pub const fn opcode(self) -> u32 {
97 match self {
98 CmpOp::Eq => 0,
99 CmpOp::Ne => 1,
100 CmpOp::Lt => 2,
101 CmpOp::Le => 3,
102 CmpOp::Gt => 4,
103 CmpOp::Ge => 5,
104 }
105 }
106}
107
108impl ReduceOp {
109 pub const ALL: [ReduceOp; 5] = [
111 ReduceOp::Sum,
112 ReduceOp::Mean,
113 ReduceOp::Max,
114 ReduceOp::Min,
115 ReduceOp::Prod,
116 ];
117
118 pub const fn opcode(self) -> u32 {
121 match self {
122 ReduceOp::Sum => 0,
123 ReduceOp::Mean => 1,
124 ReduceOp::Max => 2,
125 ReduceOp::Min => 3,
126 ReduceOp::Prod => 4,
127 }
128 }
129
130 pub const fn pool_opcode(self) -> u32 {
135 match self {
136 ReduceOp::Max => 0,
137 ReduceOp::Mean => 1,
138 ReduceOp::Sum => 2,
139 ReduceOp::Min => 3,
140 ReduceOp::Prod => 4,
141 }
142 }
143}
144
145impl Activation {
146 pub const ALL: [Activation; 29] = [
148 Activation::Gelu,
149 Activation::GeluApprox,
150 Activation::Silu,
151 Activation::Relu,
152 Activation::Sigmoid,
153 Activation::Tanh,
154 Activation::Exp,
155 Activation::Log,
156 Activation::Sqrt,
157 Activation::Rsqrt,
158 Activation::Neg,
159 Activation::Abs,
160 Activation::Sin,
161 Activation::Cos,
162 Activation::Tan,
163 Activation::Atan,
164 Activation::Recip,
165 Activation::Round,
166 Activation::Floor,
167 Activation::Ceil,
168 Activation::Sign,
169 Activation::Softplus,
170 Activation::Elu,
171 Activation::Erf,
172 Activation::HardSwish,
173 Activation::HardSigmoid,
174 Activation::Mish,
175 Activation::Softsign,
176 Activation::LogSigmoid,
177 ];
178
179 pub const fn opcode_relu_first(self) -> u32 {
184 match self {
185 Activation::Relu => 0,
186 Activation::Sigmoid => 1,
187 Activation::Tanh => 2,
188 Activation::Exp => 3,
189 Activation::Log => 4,
190 Activation::Sqrt => 5,
191 Activation::Rsqrt => 6,
192 Activation::Neg => 7,
193 Activation::Abs => 8,
194 Activation::Gelu => 9,
195 Activation::Silu => 10,
196 Activation::GeluApprox => 11,
197 Activation::Round => 12,
198 Activation::Sin => 13,
199 Activation::Cos => 14,
200 Activation::Tan => 15,
201 Activation::Atan => 16,
202 Activation::Recip => 17,
203 Activation::Floor => 18,
204 Activation::Ceil => 19,
205 Activation::Sign => 20,
206 Activation::Softplus => 21,
207 Activation::Elu => 22,
208 Activation::Erf => 23,
209 Activation::HardSwish => 24,
210 Activation::HardSigmoid => 25,
211 Activation::Mish => 26,
212 Activation::Softsign => 27,
213 Activation::LogSigmoid => 28,
214 }
215 }
216
217 pub const fn opcode_gelu_first(self) -> u32 {
221 match self {
222 Activation::Gelu => 0,
223 Activation::GeluApprox => 1,
224 Activation::Silu => 2,
225 Activation::Relu => 3,
226 Activation::Sigmoid => 4,
227 Activation::Tanh => 5,
228 Activation::Exp => 6,
229 Activation::Log => 7,
230 Activation::Sqrt => 8,
231 Activation::Rsqrt => 9,
232 Activation::Neg => 10,
233 Activation::Abs => 11,
234 Activation::Sin => 12,
235 Activation::Cos => 13,
236 Activation::Tan => 14,
237 Activation::Atan => 15,
238 Activation::Round => 16,
239 Activation::Recip => 17,
240 Activation::Floor => 18,
241 Activation::Ceil => 19,
242 Activation::Sign => 20,
243 Activation::Softplus => 21,
244 Activation::Elu => 22,
245 Activation::Erf => 23,
246 Activation::HardSwish => 24,
247 Activation::HardSigmoid => 25,
248 Activation::Mish => 26,
249 Activation::Softsign => 27,
250 Activation::LogSigmoid => 28,
251 }
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 fn assert_bijection(name: &str, ids: &[u32]) {
263 let n = ids.len();
264 let mut sorted = ids.to_vec();
265 sorted.sort_unstable();
266 for (i, &id) in sorted.iter().enumerate() {
267 assert_eq!(
268 id as usize, i,
269 "{name}: opcodes must be a contiguous bijection onto 0..{n}; \
270 got {sorted:?} (duplicate or gap at index {i})"
271 );
272 }
273 }
274
275 #[test]
276 fn binary_opcodes_are_a_bijection() {
277 let ids: Vec<u32> = BinaryOp::ALL.iter().map(|&o| o.opcode()).collect();
278 assert_bijection("BinaryOp::opcode", &ids);
279 }
280
281 #[test]
282 fn compare_opcodes_are_a_bijection() {
283 let ids: Vec<u32> = CmpOp::ALL.iter().map(|&o| o.opcode()).collect();
284 assert_bijection("CmpOp::opcode", &ids);
285 }
286
287 #[test]
288 fn reduce_opcodes_are_a_bijection() {
289 let ids: Vec<u32> = ReduceOp::ALL.iter().map(|&o| o.opcode()).collect();
290 assert_bijection("ReduceOp::opcode", &ids);
291 let pool: Vec<u32> = ReduceOp::ALL.iter().map(|&o| o.pool_opcode()).collect();
292 assert_bijection("ReduceOp::pool_opcode", &pool);
293 }
294
295 #[test]
296 fn activation_schemes_are_bijections() {
297 let relu: Vec<u32> = Activation::ALL
298 .iter()
299 .map(|&a| a.opcode_relu_first())
300 .collect();
301 assert_bijection("Activation::opcode_relu_first", &relu);
302 let gelu: Vec<u32> = Activation::ALL
303 .iter()
304 .map(|&a| a.opcode_gelu_first())
305 .collect();
306 assert_bijection("Activation::opcode_gelu_first", &gelu);
307 }
308
309 #[test]
313 fn all_arrays_have_expected_lengths() {
314 assert_eq!(BinaryOp::ALL.len(), 14);
315 assert_eq!(CmpOp::ALL.len(), 6);
316 assert_eq!(ReduceOp::ALL.len(), 5);
317 assert_eq!(Activation::ALL.len(), 29);
318 }
319
320 #[test]
327 fn canonical_ids_are_pinned() {
328 for (op, want) in [
330 (BinaryOp::Add, 0),
331 (BinaryOp::Div, 3),
332 (BinaryOp::Pow, 6),
333 (BinaryOp::Mod, 7),
334 (BinaryOp::Shr, 12),
335 (BinaryOp::Atan2, 13),
336 ] {
337 assert_eq!(op.opcode(), want, "BinaryOp::{op:?}");
338 }
339 assert_eq!(CmpOp::Eq.opcode(), 0);
341 assert_eq!(CmpOp::Ge.opcode(), 5);
342 assert_eq!(ReduceOp::Sum.opcode(), 0);
343 assert_eq!(ReduceOp::Prod.opcode(), 4);
344 assert_eq!(ReduceOp::Max.opcode(), 2);
346 assert_eq!(ReduceOp::Max.pool_opcode(), 0);
347 assert_eq!(ReduceOp::Sum.pool_opcode(), 2);
348 assert_eq!(Activation::Relu.opcode_relu_first(), 0);
350 assert_eq!(Activation::Gelu.opcode_relu_first(), 9);
351 assert_eq!(Activation::Recip.opcode_relu_first(), 17);
352 assert_eq!(Activation::LogSigmoid.opcode_relu_first(), 28);
353 assert_eq!(Activation::Gelu.opcode_gelu_first(), 0);
355 assert_eq!(Activation::Relu.opcode_gelu_first(), 3);
356 assert_eq!(Activation::Atan.opcode_gelu_first(), 15);
357 assert_eq!(Activation::Round.opcode_gelu_first(), 16);
358 assert_eq!(Activation::Recip.opcode_gelu_first(), 17);
359 assert_eq!(Activation::LogSigmoid.opcode_gelu_first(), 28);
360 }
361}