1use crate::op::{Op, OpKind};
36
37pub const N_KINDS: usize = 184;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub struct OpCaps(u32);
47
48impl OpCaps {
49 pub const NONE: Self = Self(0);
52
53 pub const LEAF: Self = Self(1 << 0);
55
56 pub const ELEMENTWISE: Self = Self(1 << 1);
59
60 pub const CONST_FOLDABLE: Self = Self(1 << 2);
67
68 pub const BLAS: Self = Self(1 << 3);
71
72 pub const FUSION_BOUNDARY: Self = Self(1 << 4);
77
78 pub const REDUCTION: Self = Self(1 << 5);
80
81 pub const TRANSFORM: Self = Self(1 << 6);
83
84 pub const FUSED: Self = Self(1 << 7);
87
88 pub const NESTED_BODY: Self = Self(1 << 8);
91
92 pub const NONDETERMINISTIC: Self = Self(1 << 9);
95
96 pub const fn union(self, other: Self) -> Self {
99 Self(self.0 | other.0)
100 }
101
102 pub const fn contains(self, other: Self) -> bool {
104 self.0 & other.0 == other.0
105 }
106
107 pub const fn intersects(self, other: Self) -> bool {
109 self.0 & other.0 != 0
110 }
111
112 pub const fn is_empty(self) -> bool {
114 self.0 == 0
115 }
116}
117
118impl std::ops::BitOr for OpCaps {
119 type Output = Self;
120 fn bitor(self, rhs: Self) -> Self {
121 self.union(rhs)
122 }
123}
124
125impl OpKind {
126 pub const ALL: [OpKind; N_KINDS] = [
131 OpKind::Input,
132 OpKind::Param,
133 OpKind::Constant,
134 OpKind::Activation,
135 OpKind::Cast,
136 OpKind::StopGradient,
137 OpKind::Quantize,
138 OpKind::Dequantize,
139 OpKind::FakeQuantize,
140 OpKind::FakeQuantizeLSQ,
141 OpKind::FakeQuantizeLSQBackwardX,
142 OpKind::FakeQuantizeLSQBackwardScale,
143 OpKind::Binary,
144 OpKind::Compare,
145 OpKind::Where,
146 OpKind::Fma,
147 OpKind::ElementwiseRegion,
148 OpKind::TransformRegion,
149 OpKind::BatchElementwiseRegion,
150 OpKind::MatMul,
151 OpKind::DotGeneral,
152 OpKind::DenseSolve,
153 OpKind::BatchedDenseSolve,
154 OpKind::Cholesky,
155 OpKind::TriangularSolve,
156 OpKind::Det,
157 OpKind::LogDet,
158 OpKind::Sort,
159 OpKind::ArgSort,
160 OpKind::Svd,
161 OpKind::Qr,
162 OpKind::LayerNorm,
163 OpKind::LayerNorm2d,
164 OpKind::GroupNorm,
165 OpKind::BatchNormInference,
166 OpKind::RmsNorm,
167 OpKind::ResizeNearest2x,
168 OpKind::Interpolate3d,
169 OpKind::Attention,
170 OpKind::Rope,
171 OpKind::AxialRope2d,
172 OpKind::Reshape,
173 OpKind::Transpose,
174 OpKind::Narrow,
175 OpKind::Concat,
176 OpKind::KvAppend,
177 OpKind::Expand,
178 OpKind::Gather,
179 OpKind::Reverse,
180 OpKind::Pad,
181 OpKind::Slice,
182 OpKind::Clamp,
183 OpKind::Tile,
184 OpKind::Trilu,
185 OpKind::Reduce,
186 OpKind::Histogram,
187 OpKind::Softmax,
188 OpKind::Cumsum,
189 OpKind::CumProd,
190 OpKind::CumMax,
191 OpKind::ArgMax,
192 OpKind::ArgMin,
193 OpKind::TopK,
194 OpKind::Sample,
195 OpKind::RngNormal,
196 OpKind::RngUniform,
197 OpKind::Conv,
198 OpKind::Im2Col,
199 OpKind::ConvTranspose2d,
200 OpKind::Conv3d,
201 OpKind::ConvTranspose3d,
202 OpKind::Pool,
203 OpKind::ReluBackward,
204 OpKind::ActivationBackward,
205 OpKind::FakeQuantizeBackward,
206 OpKind::ComplexNormSq,
207 OpKind::ComplexNormSqBackward,
208 OpKind::Conjugate,
209 OpKind::MaxPool2dBackward,
210 OpKind::Conv2dBackwardInput,
211 OpKind::Conv2dBackwardWeight,
212 OpKind::MaxPool3dBackward,
213 OpKind::Conv3dBackwardInput,
214 OpKind::Conv3dBackwardWeight,
215 OpKind::SoftmaxCrossEntropy,
216 OpKind::SoftmaxCrossEntropyWithLogits,
217 OpKind::SoftmaxCrossEntropyBackward,
218 OpKind::AttentionBackward,
219 OpKind::AttentionBackwardAll,
220 OpKind::LayerNormBackwardInput,
221 OpKind::LayerNormBackwardGamma,
222 OpKind::RmsNormBackwardInput,
223 OpKind::RmsNormBackwardGamma,
224 OpKind::RmsNormBackwardBeta,
225 OpKind::RopeBackward,
226 OpKind::GroupNormBackwardInput,
227 OpKind::GroupNormBackwardGamma,
228 OpKind::GroupNormBackwardBeta,
229 OpKind::BatchNormInferenceBackwardInput,
230 OpKind::BatchNormInferenceBackwardGamma,
231 OpKind::BatchNormInferenceBackwardBeta,
232 OpKind::CumsumBackward,
233 OpKind::GatherBackward,
234 OpKind::GroupedMatMul,
235 OpKind::DequantGroupedMatMul,
236 OpKind::DequantGroupedMatMulMlx,
237 OpKind::DequantMoEWeights,
238 OpKind::ScaledGroupedMatMul,
239 OpKind::ScatterAdd,
240 OpKind::ScatterNd,
241 OpKind::ScatterElements,
242 OpKind::GatherNd,
243 OpKind::GatherElements,
244 OpKind::LoraMatMul,
245 OpKind::PartitionedConv,
246 OpKind::DequantMatMul,
247 OpKind::SynthMatMul,
248 OpKind::SynthMatMulBackward,
249 OpKind::SynthReconstruct,
250 OpKind::SplineActivation,
251 OpKind::SplineActivationBackwardX,
252 OpKind::SplineActivationBackwardCoeff,
253 OpKind::QMatMul,
254 OpKind::QConv2d,
255 OpKind::ScaledMatMul,
256 OpKind::ScaledQuantize,
257 OpKind::ScaledQuantScale,
258 OpKind::ScaledDequantize,
259 OpKind::SelectiveScan,
260 OpKind::GatedDeltaNet,
261 OpKind::Lstm,
262 OpKind::Gru,
263 OpKind::Rnn,
264 OpKind::Mamba2,
265 OpKind::FusedSwiGLU,
266 OpKind::FusedMatMulBiasAct,
267 OpKind::FusedMatMulResidual,
268 OpKind::FusedConvBiasAct,
269 OpKind::FusedResidualLN,
270 OpKind::FusedResidualRmsNorm,
271 OpKind::FusedAttentionBlock,
272 OpKind::FusedTransformerLayer,
273 OpKind::If,
274 OpKind::While,
275 OpKind::Scan,
276 OpKind::ScanBackward,
277 OpKind::ScanBackwardXs,
278 OpKind::GaussianSplatRender,
279 OpKind::GaussianSplatRenderBackward,
280 OpKind::GaussianSplatPrepare,
281 OpKind::GaussianSplatRasterize,
282 OpKind::Custom,
283 OpKind::CustomFn,
284 OpKind::Fft,
285 OpKind::FftButterflyStage,
286 OpKind::LogMel,
287 OpKind::LogMelBackward,
288 OpKind::WelchPeaks,
289 OpKind::BiMap,
290 OpKind::ReEig,
291 OpKind::LogEig,
292 OpKind::SpdBatchNorm,
293 OpKind::SpdKarcherMean,
294 OpKind::ReEigBackward,
295 OpKind::LogEigBackward,
296 OpKind::SpdBatchNormBackwardX,
297 OpKind::SpdBatchNormBackwardG,
298 OpKind::SpdKarcherMeanWeighted,
299 OpKind::SpdLogMap,
300 OpKind::SpdExpMap,
301 OpKind::SpdParallelTransport,
302 OpKind::SpdMatrixFnBatch,
303 OpKind::SpdLogMapBackward,
304 OpKind::SpdExpMapBackward,
305 OpKind::SpdParallelTransportBackward,
306 OpKind::SpdMatrixFnBatchBackward,
307 OpKind::Eigh,
308 OpKind::EighBackward,
309 OpKind::EighBatch,
310 OpKind::EighBatchBackward,
311 OpKind::AdaLayerNorm,
312 OpKind::AdaLayerNormBackward,
313 OpKind::GatedResidual,
314 OpKind::GatedResidualBackward,
315 ];
316
317 pub const fn caps(self) -> OpCaps {
324 use OpKind::*;
325 match self {
326 Input | Param | Constant => OpCaps::LEAF,
327 Activation | Cast | Binary | Compare => {
328 OpCaps::ELEMENTWISE.union(OpCaps::CONST_FOLDABLE)
329 }
330 StopGradient | Where | Fma | ElementwiseRegion | BatchElementwiseRegion => {
331 OpCaps::ELEMENTWISE
332 }
333 MatMul
334 | DotGeneral
335 | DenseSolve
336 | BatchedDenseSolve
337 | Conv
338 | Im2Col
339 | ConvTranspose2d
340 | Conv3d
341 | ConvTranspose3d
342 | GroupedMatMul
343 | DequantGroupedMatMul
344 | DequantGroupedMatMulMlx
345 | DequantMoEWeights
346 | ScaledGroupedMatMul
347 | DequantMatMul
348 | SynthMatMul
349 | QMatMul
350 | QConv2d
351 | ScaledMatMul
352 | FusedMatMulResidual => OpCaps::BLAS.union(OpCaps::FUSION_BOUNDARY),
353 ResizeNearest2x => OpCaps::TRANSFORM,
354 Reshape | Expand => OpCaps::CONST_FOLDABLE,
355 Reduce | Softmax | TopK => OpCaps::REDUCTION,
356 Sample | RngNormal | RngUniform => OpCaps::NONDETERMINISTIC,
357 LoraMatMul | FusedMatMulBiasAct | FusedConvBiasAct => {
358 OpCaps::BLAS.union(OpCaps::FUSION_BOUNDARY.union(OpCaps::FUSED))
359 }
360 PartitionedConv
361 | SelectiveScan
362 | GatedDeltaNet
363 | Lstm
364 | Gru
365 | Rnn
366 | Mamba2
367 | FusedSwiGLU
368 | FusedResidualLN
369 | FusedResidualRmsNorm
370 | FusedAttentionBlock
371 | FusedTransformerLayer
372 | AdaLayerNorm
373 | GatedResidual => OpCaps::FUSED,
374 If | While | Scan | ScanBackward | ScanBackwardXs | CustomFn => OpCaps::NESTED_BODY,
375 GaussianSplatRender
376 | GaussianSplatRenderBackward
377 | GaussianSplatPrepare
378 | GaussianSplatRasterize => OpCaps::FUSION_BOUNDARY,
379 Quantize
380 | Dequantize
381 | FakeQuantize
382 | FakeQuantizeLSQ
383 | FakeQuantizeLSQBackwardX
384 | FakeQuantizeLSQBackwardScale
385 | TransformRegion
386 | Cholesky
387 | TriangularSolve
388 | Det
389 | LogDet
390 | Sort
391 | ArgSort
392 | Svd
393 | Qr
394 | LayerNorm
395 | LayerNorm2d
396 | GroupNorm
397 | BatchNormInference
398 | RmsNorm
399 | Interpolate3d
400 | Attention
401 | Rope
402 | AxialRope2d
403 | Transpose
404 | Narrow
405 | Concat
406 | KvAppend
407 | Gather
408 | Reverse
409 | Pad
410 | Slice
411 | Clamp
412 | Tile
413 | Trilu
414 | Histogram
415 | Cumsum
416 | CumProd
417 | CumMax
418 | ArgMax
419 | ArgMin
420 | Pool
421 | ReluBackward
422 | ActivationBackward
423 | FakeQuantizeBackward
424 | ComplexNormSq
425 | ComplexNormSqBackward
426 | Conjugate
427 | MaxPool2dBackward
428 | Conv2dBackwardInput
429 | Conv2dBackwardWeight
430 | MaxPool3dBackward
431 | Conv3dBackwardInput
432 | Conv3dBackwardWeight
433 | SoftmaxCrossEntropy
434 | SoftmaxCrossEntropyWithLogits
435 | SoftmaxCrossEntropyBackward
436 | AttentionBackward
437 | AttentionBackwardAll
438 | LayerNormBackwardInput
439 | LayerNormBackwardGamma
440 | RmsNormBackwardInput
441 | RmsNormBackwardGamma
442 | RmsNormBackwardBeta
443 | RopeBackward
444 | GroupNormBackwardInput
445 | GroupNormBackwardGamma
446 | GroupNormBackwardBeta
447 | BatchNormInferenceBackwardInput
448 | BatchNormInferenceBackwardGamma
449 | BatchNormInferenceBackwardBeta
450 | CumsumBackward
451 | GatherBackward
452 | ScatterAdd
453 | ScatterNd
454 | ScatterElements
455 | GatherNd
456 | GatherElements
457 | SynthMatMulBackward
458 | SynthReconstruct
459 | SplineActivation
460 | SplineActivationBackwardX
461 | SplineActivationBackwardCoeff
462 | ScaledQuantize
463 | ScaledQuantScale
464 | ScaledDequantize
465 | Custom
466 | Fft
467 | FftButterflyStage
468 | LogMel
469 | LogMelBackward
470 | WelchPeaks
471 | BiMap
472 | ReEig
473 | LogEig
474 | SpdBatchNorm
475 | SpdKarcherMean
476 | ReEigBackward
477 | LogEigBackward
478 | SpdBatchNormBackwardX
479 | SpdBatchNormBackwardG
480 | SpdKarcherMeanWeighted
481 | SpdLogMap
482 | SpdExpMap
483 | SpdParallelTransport
484 | SpdMatrixFnBatch
485 | SpdLogMapBackward
486 | SpdExpMapBackward
487 | SpdParallelTransportBackward
488 | SpdMatrixFnBatchBackward
489 | Eigh
490 | EighBackward
491 | EighBatch
492 | EighBatchBackward
493 | AdaLayerNormBackward
494 | GatedResidualBackward => OpCaps::NONE,
495 }
496 }
497
498 pub const fn has(self, caps: OpCaps) -> bool {
500 self.caps().contains(caps)
501 }
502}
503
504impl Op {
511 pub fn is_elementwise(&self) -> bool {
514 self.kind().has(OpCaps::ELEMENTWISE)
515 }
516
517 pub fn is_transform_eligible(&self) -> bool {
519 self.kind().has(OpCaps::TRANSFORM)
520 }
521
522 pub fn is_blas(&self) -> bool {
524 self.kind().has(OpCaps::BLAS)
525 }
526
527 pub fn is_fusion_boundary(&self) -> bool {
529 self.kind().has(OpCaps::FUSION_BOUNDARY)
530 }
531
532 pub fn is_reduction(&self) -> bool {
534 self.kind().has(OpCaps::REDUCTION)
535 }
536
537 pub fn is_leaf(&self) -> bool {
539 self.kind().has(OpCaps::LEAF)
540 }
541
542 pub fn is_const_foldable(&self) -> bool {
544 self.kind().has(OpCaps::CONST_FOLDABLE)
545 }
546
547 pub fn is_fused(&self) -> bool {
549 self.kind().has(OpCaps::FUSED)
550 }
551
552 pub fn has_nested_body(&self) -> bool {
555 self.kind().has(OpCaps::NESTED_BODY)
556 }
557
558 pub fn is_nondeterministic(&self) -> bool {
561 self.kind().has(OpCaps::NONDETERMINISTIC)
562 }
563}
564
565pub fn fused_kinds() -> Vec<OpKind> {
568 OpKind::ALL
569 .into_iter()
570 .filter(|k| k.has(OpCaps::FUSED))
571 .collect()
572}
573
574#[cfg(test)]
575mod tests {
576 use super::*;
577
578 #[test]
579 fn all_is_complete_and_duplicate_free() {
580 let mut seen = std::collections::HashSet::new();
581 for kind in OpKind::ALL {
582 assert!(seen.insert(kind), "{kind:?} appears twice in OpKind::ALL");
583 }
584 assert_eq!(seen.len(), N_KINDS);
585 }
586
587 #[test]
588 fn blas_implies_fusion_boundary() {
589 for kind in OpKind::ALL {
592 if kind.has(OpCaps::BLAS) {
593 assert!(
594 kind.has(OpCaps::FUSION_BOUNDARY),
595 "{kind:?} is BLAS but not a fusion boundary"
596 );
597 }
598 }
599 }
600
601 #[test]
602 fn leaves_are_not_elementwise() {
603 for kind in OpKind::ALL {
604 if kind.has(OpCaps::LEAF) {
605 assert!(!kind.has(OpCaps::ELEMENTWISE), "{kind:?}");
606 assert!(!kind.has(OpCaps::FUSION_BOUNDARY), "{kind:?}");
607 }
608 }
609 }
610
611 #[test]
612 fn nondeterministic_ops_are_never_const_foldable() {
613 for kind in OpKind::ALL {
615 if kind.has(OpCaps::NONDETERMINISTIC) {
616 assert!(!kind.has(OpCaps::CONST_FOLDABLE), "{kind:?}");
617 }
618 }
619 }
620
621 #[test]
622 fn nested_body_flag_agrees_with_the_subgraph_accessor() {
623 let g = || Box::new(crate::Graph::new("b"));
627 let cases: Vec<Op> = vec![
628 Op::MatMul,
629 Op::StopGradient,
630 Op::If {
631 then_branch: g(),
632 else_branch: g(),
633 },
634 Op::While {
635 cond: g(),
636 body: g(),
637 max_iterations: None,
638 },
639 Op::Scan {
640 body: g(),
641 length: 1,
642 save_trajectory: false,
643 num_bcast: 0,
644 num_xs: 0,
645 num_checkpoints: 0,
646 },
647 Op::CustomFn {
648 fwd_body: g(),
649 vjp_body: None,
650 jvp_body: None,
651 num_inputs: 1,
652 },
653 ];
654 for op in cases {
655 assert_eq!(
656 op.has_nested_body(),
657 !op.subgraphs().is_empty(),
658 "{:?} disagrees with its subgraph accessor",
659 op.kind()
660 );
661 }
662 }
663
664 #[test]
665 fn spot_check_known_classifications() {
666 assert!(OpKind::Input.has(OpCaps::LEAF));
667 assert!(OpKind::Activation.has(OpCaps::ELEMENTWISE.union(OpCaps::CONST_FOLDABLE)));
668 assert!(OpKind::MatMul.has(OpCaps::BLAS));
669 assert!(OpKind::Reduce.has(OpCaps::REDUCTION));
670 assert!(OpKind::Scan.has(OpCaps::NESTED_BODY));
671 assert!(OpKind::RngNormal.has(OpCaps::NONDETERMINISTIC));
672 assert!(OpKind::FusedSwiGLU.has(OpCaps::FUSED));
673 assert!(OpKind::Reshape.has(OpCaps::CONST_FOLDABLE));
675 assert!(!OpKind::Reshape.has(OpCaps::ELEMENTWISE));
676 assert!(OpKind::Transpose.caps().is_empty());
678 }
679
680 #[test]
681 fn fused_kinds_matches_the_flag() {
682 let listed = fused_kinds();
683 assert!(!listed.is_empty());
684 for kind in &listed {
685 assert!(kind.has(OpCaps::FUSED));
686 }
687 assert_eq!(
688 listed.len(),
689 OpKind::ALL.iter().filter(|k| k.has(OpCaps::FUSED)).count()
690 );
691 }
692}