Skip to main content

oxicuda_backend/
ops.rs

1//! Operation descriptor enums shared by every backend.
2
3use std::fmt;
4
5/// Transpose mode for matrix operations.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum BackendTranspose {
8    /// No transpose — use the matrix as-is.
9    NoTrans,
10    /// Transpose (swap rows and columns).
11    Trans,
12    /// Conjugate transpose (Hermitian).
13    ConjTrans,
14}
15
16impl fmt::Display for BackendTranspose {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        match self {
19            Self::NoTrans => write!(f, "N"),
20            Self::Trans => write!(f, "T"),
21            Self::ConjTrans => write!(f, "C"),
22        }
23    }
24}
25
26/// Reduction operation applied along an axis.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum ReduceOp {
29    /// Summation.
30    Sum,
31    /// Maximum value.
32    Max,
33    /// Minimum value.
34    Min,
35    /// Arithmetic mean.
36    Mean,
37}
38
39impl fmt::Display for ReduceOp {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            Self::Sum => write!(f, "sum"),
43            Self::Max => write!(f, "max"),
44            Self::Min => write!(f, "min"),
45            Self::Mean => write!(f, "mean"),
46        }
47    }
48}
49
50/// Element-wise unary operation.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52pub enum UnaryOp {
53    /// Rectified linear unit: max(0, x).
54    Relu,
55    /// Sigmoid: 1 / (1 + exp(-x)).
56    Sigmoid,
57    /// Hyperbolic tangent.
58    Tanh,
59    /// Exponential.
60    Exp,
61    /// Natural logarithm.
62    Log,
63    /// Square root.
64    Sqrt,
65    /// Absolute value.
66    Abs,
67    /// Negation.
68    Neg,
69}
70
71impl fmt::Display for UnaryOp {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        match self {
74            Self::Relu => write!(f, "relu"),
75            Self::Sigmoid => write!(f, "sigmoid"),
76            Self::Tanh => write!(f, "tanh"),
77            Self::Exp => write!(f, "exp"),
78            Self::Log => write!(f, "log"),
79            Self::Sqrt => write!(f, "sqrt"),
80            Self::Abs => write!(f, "abs"),
81            Self::Neg => write!(f, "neg"),
82        }
83    }
84}
85
86/// Reduced-precision **input storage** format for a mixed-precision GEMM.
87///
88/// Mixed-precision GEMM stores its `A`/`B` operands in a 16-bit format but
89/// accumulates the dot products in `f32` (the Tensor-Core / WMMA contract).
90/// This enum names the input storage format; the accumulator and output are
91/// always `f32`.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
93pub enum MixedPrecision {
94    /// IEEE-754 binary16 (half): 1 sign, 5 exponent, 10 mantissa bits.
95    F16,
96    /// Google bfloat16: 1 sign, 8 exponent, 7 mantissa bits (the top 16 bits
97    /// of an `f32`).
98    Bf16,
99}
100
101impl fmt::Display for MixedPrecision {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            Self::F16 => write!(f, "f16"),
105            Self::Bf16 => write!(f, "bf16"),
106        }
107    }
108}
109
110/// Element-wise binary operation.
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112pub enum BinaryOp {
113    /// Addition.
114    Add,
115    /// Subtraction.
116    Sub,
117    /// Multiplication.
118    Mul,
119    /// Division.
120    Div,
121    /// Element-wise maximum.
122    Max,
123    /// Element-wise minimum.
124    Min,
125}
126
127impl fmt::Display for BinaryOp {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        match self {
130            Self::Add => write!(f, "add"),
131            Self::Sub => write!(f, "sub"),
132            Self::Mul => write!(f, "mul"),
133            Self::Div => write!(f, "div"),
134            Self::Max => write!(f, "max"),
135            Self::Min => write!(f, "min"),
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn backend_transpose_display_and_values() {
146        assert_eq!(BackendTranspose::NoTrans.to_string(), "N");
147        assert_eq!(BackendTranspose::Trans.to_string(), "T");
148        assert_eq!(BackendTranspose::ConjTrans.to_string(), "C");
149
150        assert_eq!(BackendTranspose::NoTrans, BackendTranspose::NoTrans);
151        assert_ne!(BackendTranspose::NoTrans, BackendTranspose::Trans);
152    }
153
154    #[test]
155    fn reduce_op_display_and_coverage() {
156        let ops = [ReduceOp::Sum, ReduceOp::Max, ReduceOp::Min, ReduceOp::Mean];
157        let names = ["sum", "max", "min", "mean"];
158        for (op, name) in ops.iter().zip(names.iter()) {
159            assert_eq!(op.to_string(), *name);
160        }
161    }
162
163    #[test]
164    fn unary_op_display_and_coverage() {
165        let ops = [
166            UnaryOp::Relu,
167            UnaryOp::Sigmoid,
168            UnaryOp::Tanh,
169            UnaryOp::Exp,
170            UnaryOp::Log,
171            UnaryOp::Sqrt,
172            UnaryOp::Abs,
173            UnaryOp::Neg,
174        ];
175        let names = [
176            "relu", "sigmoid", "tanh", "exp", "log", "sqrt", "abs", "neg",
177        ];
178        for (op, name) in ops.iter().zip(names.iter()) {
179            assert_eq!(op.to_string(), *name);
180        }
181    }
182
183    #[test]
184    fn mixed_precision_display_and_coverage() {
185        assert_eq!(MixedPrecision::F16.to_string(), "f16");
186        assert_eq!(MixedPrecision::Bf16.to_string(), "bf16");
187        assert_ne!(MixedPrecision::F16, MixedPrecision::Bf16);
188        // Participates in a HashSet like the other op descriptors.
189        use std::collections::HashSet;
190        let mut set = HashSet::new();
191        set.insert(MixedPrecision::F16);
192        assert!(set.contains(&MixedPrecision::F16));
193        assert!(!set.contains(&MixedPrecision::Bf16));
194    }
195
196    #[test]
197    fn binary_op_display_and_coverage() {
198        let ops = [
199            BinaryOp::Add,
200            BinaryOp::Sub,
201            BinaryOp::Mul,
202            BinaryOp::Div,
203            BinaryOp::Max,
204            BinaryOp::Min,
205        ];
206        let names = ["add", "sub", "mul", "div", "max", "min"];
207        for (op, name) in ops.iter().zip(names.iter()) {
208            assert_eq!(op.to_string(), *name);
209        }
210    }
211
212    #[test]
213    fn enum_clone_and_hash() {
214        use std::collections::HashSet;
215
216        let mut set = HashSet::new();
217        set.insert(ReduceOp::Sum);
218        set.insert(ReduceOp::Max);
219        assert!(set.contains(&ReduceOp::Sum));
220        assert!(!set.contains(&ReduceOp::Min));
221
222        let op = UnaryOp::Relu;
223        let cloned = op;
224        assert_eq!(op, cloned);
225
226        let bop = BinaryOp::Add;
227        let bcloned = bop;
228        assert_eq!(bop, bcloned);
229
230        let trans = BackendTranspose::ConjTrans;
231        let tcloned = trans;
232        assert_eq!(trans, tcloned);
233    }
234}