use crate::op::{Activation, BinaryOp, CmpOp, ReduceOp};
impl BinaryOp {
pub const ALL: [BinaryOp; 14] = [
BinaryOp::Add,
BinaryOp::Sub,
BinaryOp::Mul,
BinaryOp::Div,
BinaryOp::Max,
BinaryOp::Min,
BinaryOp::Pow,
BinaryOp::Mod,
BinaryOp::BitAnd,
BinaryOp::BitOr,
BinaryOp::BitXor,
BinaryOp::Shl,
BinaryOp::Shr,
BinaryOp::Atan2,
];
pub const fn opcode(self) -> u32 {
match self {
BinaryOp::Add => 0,
BinaryOp::Sub => 1,
BinaryOp::Mul => 2,
BinaryOp::Div => 3,
BinaryOp::Max => 4,
BinaryOp::Min => 5,
BinaryOp::Pow => 6,
BinaryOp::Mod => 7,
BinaryOp::BitAnd => 8,
BinaryOp::BitOr => 9,
BinaryOp::BitXor => 10,
BinaryOp::Shl => 11,
BinaryOp::Shr => 12,
BinaryOp::Atan2 => 13,
}
}
}
impl CmpOp {
pub const ALL: [CmpOp; 6] = [
CmpOp::Eq,
CmpOp::Ne,
CmpOp::Lt,
CmpOp::Le,
CmpOp::Gt,
CmpOp::Ge,
];
pub const fn opcode(self) -> u32 {
match self {
CmpOp::Eq => 0,
CmpOp::Ne => 1,
CmpOp::Lt => 2,
CmpOp::Le => 3,
CmpOp::Gt => 4,
CmpOp::Ge => 5,
}
}
}
impl ReduceOp {
pub const ALL: [ReduceOp; 5] = [
ReduceOp::Sum,
ReduceOp::Mean,
ReduceOp::Max,
ReduceOp::Min,
ReduceOp::Prod,
];
pub const fn opcode(self) -> u32 {
match self {
ReduceOp::Sum => 0,
ReduceOp::Mean => 1,
ReduceOp::Max => 2,
ReduceOp::Min => 3,
ReduceOp::Prod => 4,
}
}
pub const fn pool_opcode(self) -> u32 {
match self {
ReduceOp::Max => 0,
ReduceOp::Mean => 1,
ReduceOp::Sum => 2,
ReduceOp::Min => 3,
ReduceOp::Prod => 4,
}
}
}
impl Activation {
pub const ALL: [Activation; 29] = [
Activation::Gelu,
Activation::GeluApprox,
Activation::Silu,
Activation::Relu,
Activation::Sigmoid,
Activation::Tanh,
Activation::Exp,
Activation::Log,
Activation::Sqrt,
Activation::Rsqrt,
Activation::Neg,
Activation::Abs,
Activation::Sin,
Activation::Cos,
Activation::Tan,
Activation::Atan,
Activation::Recip,
Activation::Round,
Activation::Floor,
Activation::Ceil,
Activation::Sign,
Activation::Softplus,
Activation::Elu,
Activation::Erf,
Activation::HardSwish,
Activation::HardSigmoid,
Activation::Mish,
Activation::Softsign,
Activation::LogSigmoid,
];
pub const fn opcode_relu_first(self) -> u32 {
match self {
Activation::Relu => 0,
Activation::Sigmoid => 1,
Activation::Tanh => 2,
Activation::Exp => 3,
Activation::Log => 4,
Activation::Sqrt => 5,
Activation::Rsqrt => 6,
Activation::Neg => 7,
Activation::Abs => 8,
Activation::Gelu => 9,
Activation::Silu => 10,
Activation::GeluApprox => 11,
Activation::Round => 12,
Activation::Sin => 13,
Activation::Cos => 14,
Activation::Tan => 15,
Activation::Atan => 16,
Activation::Recip => 17,
Activation::Floor => 18,
Activation::Ceil => 19,
Activation::Sign => 20,
Activation::Softplus => 21,
Activation::Elu => 22,
Activation::Erf => 23,
Activation::HardSwish => 24,
Activation::HardSigmoid => 25,
Activation::Mish => 26,
Activation::Softsign => 27,
Activation::LogSigmoid => 28,
}
}
pub const fn opcode_gelu_first(self) -> u32 {
match self {
Activation::Gelu => 0,
Activation::GeluApprox => 1,
Activation::Silu => 2,
Activation::Relu => 3,
Activation::Sigmoid => 4,
Activation::Tanh => 5,
Activation::Exp => 6,
Activation::Log => 7,
Activation::Sqrt => 8,
Activation::Rsqrt => 9,
Activation::Neg => 10,
Activation::Abs => 11,
Activation::Sin => 12,
Activation::Cos => 13,
Activation::Tan => 14,
Activation::Atan => 15,
Activation::Round => 16,
Activation::Recip => 17,
Activation::Floor => 18,
Activation::Ceil => 19,
Activation::Sign => 20,
Activation::Softplus => 21,
Activation::Elu => 22,
Activation::Erf => 23,
Activation::HardSwish => 24,
Activation::HardSigmoid => 25,
Activation::Mish => 26,
Activation::Softsign => 27,
Activation::LogSigmoid => 28,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_bijection(name: &str, ids: &[u32]) {
let n = ids.len();
let mut sorted = ids.to_vec();
sorted.sort_unstable();
for (i, &id) in sorted.iter().enumerate() {
assert_eq!(
id as usize, i,
"{name}: opcodes must be a contiguous bijection onto 0..{n}; \
got {sorted:?} (duplicate or gap at index {i})"
);
}
}
#[test]
fn binary_opcodes_are_a_bijection() {
let ids: Vec<u32> = BinaryOp::ALL.iter().map(|&o| o.opcode()).collect();
assert_bijection("BinaryOp::opcode", &ids);
}
#[test]
fn compare_opcodes_are_a_bijection() {
let ids: Vec<u32> = CmpOp::ALL.iter().map(|&o| o.opcode()).collect();
assert_bijection("CmpOp::opcode", &ids);
}
#[test]
fn reduce_opcodes_are_a_bijection() {
let ids: Vec<u32> = ReduceOp::ALL.iter().map(|&o| o.opcode()).collect();
assert_bijection("ReduceOp::opcode", &ids);
let pool: Vec<u32> = ReduceOp::ALL.iter().map(|&o| o.pool_opcode()).collect();
assert_bijection("ReduceOp::pool_opcode", &pool);
}
#[test]
fn activation_schemes_are_bijections() {
let relu: Vec<u32> = Activation::ALL
.iter()
.map(|&a| a.opcode_relu_first())
.collect();
assert_bijection("Activation::opcode_relu_first", &relu);
let gelu: Vec<u32> = Activation::ALL
.iter()
.map(|&a| a.opcode_gelu_first())
.collect();
assert_bijection("Activation::opcode_gelu_first", &gelu);
}
#[test]
fn all_arrays_have_expected_lengths() {
assert_eq!(BinaryOp::ALL.len(), 14);
assert_eq!(CmpOp::ALL.len(), 6);
assert_eq!(ReduceOp::ALL.len(), 5);
assert_eq!(Activation::ALL.len(), 29);
}
#[test]
fn canonical_ids_are_pinned() {
for (op, want) in [
(BinaryOp::Add, 0),
(BinaryOp::Div, 3),
(BinaryOp::Pow, 6),
(BinaryOp::Mod, 7),
(BinaryOp::Shr, 12),
(BinaryOp::Atan2, 13),
] {
assert_eq!(op.opcode(), want, "BinaryOp::{op:?}");
}
assert_eq!(CmpOp::Eq.opcode(), 0);
assert_eq!(CmpOp::Ge.opcode(), 5);
assert_eq!(ReduceOp::Sum.opcode(), 0);
assert_eq!(ReduceOp::Prod.opcode(), 4);
assert_eq!(ReduceOp::Max.opcode(), 2);
assert_eq!(ReduceOp::Max.pool_opcode(), 0);
assert_eq!(ReduceOp::Sum.pool_opcode(), 2);
assert_eq!(Activation::Relu.opcode_relu_first(), 0);
assert_eq!(Activation::Gelu.opcode_relu_first(), 9);
assert_eq!(Activation::Recip.opcode_relu_first(), 17);
assert_eq!(Activation::LogSigmoid.opcode_relu_first(), 28);
assert_eq!(Activation::Gelu.opcode_gelu_first(), 0);
assert_eq!(Activation::Relu.opcode_gelu_first(), 3);
assert_eq!(Activation::Atan.opcode_gelu_first(), 15);
assert_eq!(Activation::Round.opcode_gelu_first(), 16);
assert_eq!(Activation::Recip.opcode_gelu_first(), 17);
assert_eq!(Activation::LogSigmoid.opcode_gelu_first(), 28);
}
}