1use crate::ElementConversion;
4use core::num::NonZeroUsize;
5
6pub(crate) fn check_nonzero(value: usize, msg: &str) -> usize {
9 NonZeroUsize::new(value).expect(msg);
10 value
11}
12
13#[derive(Debug, Clone, Hash, PartialEq, Eq)]
15pub struct ConvOptions<const N: usize> {
16 pub stride: [usize; N],
18
19 pub padding: [(usize, usize); N],
21
22 pub dilation: [usize; N],
24
25 pub groups: usize,
27}
28
29impl<const N: usize> ConvOptions<N> {
30 pub fn new(
32 stride: [usize; N],
33 padding: [usize; N],
34 dilation: [usize; N],
35 groups: usize,
36 ) -> Self {
37 Self {
38 stride: stride.map(|s| check_nonzero(s, "stride must be non-zero")),
39 padding: padding.map(|padding| (padding, padding)),
40 dilation: dilation.map(|d| check_nonzero(d, "dilation must be non-zero")),
41 groups: check_nonzero(groups, "groups must be non-zero"),
42 }
43 }
44
45 pub fn new_with_padding(
47 stride: [usize; N],
48 padding: [(usize, usize); N],
49 dilation: [usize; N],
50 groups: usize,
51 ) -> Self {
52 Self {
53 stride: stride.map(|s| check_nonzero(s, "stride must be non-zero")),
54 padding,
55 dilation: dilation.map(|d| check_nonzero(d, "dilation must be non-zero")),
56 groups: check_nonzero(groups, "groups must be non-zero"),
57 }
58 }
59
60 pub fn is_asymmetric(&self) -> bool {
62 self.padding.iter().any(|(begin, end)| begin != end)
63 }
64
65 pub fn padding_begin(&self) -> [usize; N] {
67 self.padding.map(|(begin, _)| begin)
68 }
69
70 pub fn padding_end(&self) -> [usize; N] {
72 self.padding.map(|(_, after)| after)
73 }
74
75 pub fn symmetric_padding(&self) -> [usize; N] {
80 assert!(
81 !self.is_asymmetric(),
82 "expected symmetric convolution padding"
83 );
84 self.padding_begin()
85 }
86}
87
88#[deprecated(since = "0.22.0", note = "Use `ConvOptions::new_with_padding` instead")]
93#[derive(Debug, Clone)]
94pub struct PaddedConvOptions<const N: usize> {
95 pub options: ConvOptions<N>,
97 pub padding_end: Option<[usize; N]>,
101}
102
103#[allow(deprecated)]
104impl<const N: usize> PaddedConvOptions<N> {
105 pub fn asymmetric(
107 stride: [usize; N],
108 padding_begin: [usize; N],
109 padding_end: [usize; N],
110 dilation: [usize; N],
111 groups: usize,
112 ) -> Self {
113 let options = ConvOptions::new(stride, padding_begin, dilation, groups);
114 let padding_end = (padding_begin != padding_end).then_some(padding_end);
115 Self {
116 options,
117 padding_end,
118 }
119 }
120
121 pub fn is_asymmetric(&self) -> bool {
123 self.padding_end.is_some()
124 }
125}
126
127#[allow(deprecated)]
128impl<const N: usize> From<PaddedConvOptions<N>> for ConvOptions<N> {
129 fn from(value: PaddedConvOptions<N>) -> Self {
130 let Some(padding_end) = value.padding_end else {
131 return value.options;
132 };
133 let padding_begin = value.options.padding_begin();
134 let padding = core::array::from_fn(|i| (padding_begin[i], padding_end[i]));
135
136 ConvOptions::new_with_padding(
137 value.options.stride,
138 padding,
139 value.options.dilation,
140 value.options.groups,
141 )
142 }
143}
144
145#[allow(deprecated)]
146impl<const N: usize> From<ConvOptions<N>> for PaddedConvOptions<N> {
147 fn from(options: ConvOptions<N>) -> Self {
148 if options.is_asymmetric() {
149 let padding_begin = options.padding_begin();
150 let padding_end = options.padding_end();
151 Self {
152 options: ConvOptions::new(
153 options.stride,
154 padding_begin,
155 options.dilation,
156 options.groups,
157 ),
158 padding_end: Some(padding_end),
159 }
160 } else {
161 Self {
162 options,
163 padding_end: None,
164 }
165 }
166 }
167}
168
169#[derive(Debug, Clone, Hash, PartialEq, Eq)]
171pub struct MaxPoolOptions<const N: usize> {
172 pub kernel_size: [usize; N],
174
175 pub stride: [usize; N],
177
178 pub padding: [(usize, usize); N],
180
181 pub dilation: [usize; N],
183
184 pub ceil_mode: bool,
186}
187
188impl<const N: usize> MaxPoolOptions<N> {
189 pub fn new(kernel_size: [usize; N]) -> Self {
194 let kernel_size = kernel_size.map(|k| check_nonzero(k, "kernel size must be non-zero"));
195 Self {
196 kernel_size,
197 stride: kernel_size,
198 padding: [(0, 0); N],
199 dilation: [1; N],
200 ceil_mode: false,
201 }
202 }
203
204 pub fn with_stride(mut self, stride: [usize; N]) -> Self {
206 self.stride = stride.map(|s| check_nonzero(s, "stride must be non-zero"));
207 self
208 }
209
210 pub fn with_padding(mut self, padding: [usize; N]) -> Self {
212 self.padding = padding.map(|padding| (padding, padding));
213 self
214 }
215
216 pub fn with_padding_pairs(mut self, padding: [(usize, usize); N]) -> Self {
218 self.padding = padding;
219 self
220 }
221
222 pub fn with_dilation(mut self, dilation: [usize; N]) -> Self {
224 self.dilation = dilation.map(|d| check_nonzero(d, "dilation must be non-zero"));
225 self
226 }
227
228 pub fn with_ceil_mode(mut self, ceil_mode: bool) -> Self {
230 self.ceil_mode = ceil_mode;
231 self
232 }
233
234 pub fn is_asymmetric(&self) -> bool {
236 self.padding.iter().any(|(begin, end)| begin != end)
237 }
238}
239
240#[derive(Debug, Clone, Hash, PartialEq, Eq)]
242pub struct AvgPoolOptions<const N: usize> {
243 pub kernel_size: [usize; N],
245
246 pub stride: [usize; N],
248
249 pub padding: [(usize, usize); N],
251
252 pub dilation: [usize; N],
254
255 pub count_include_pad: bool,
257
258 pub ceil_mode: bool,
260}
261
262impl<const N: usize> AvgPoolOptions<N> {
263 pub fn new(kernel_size: [usize; N]) -> Self {
268 let kernel_size = kernel_size.map(|k| check_nonzero(k, "kernel size must be non-zero"));
269 Self {
270 kernel_size,
271 stride: kernel_size,
272 padding: [(0, 0); N],
273 dilation: [1; N],
274 count_include_pad: true,
275 ceil_mode: false,
276 }
277 }
278
279 pub fn with_stride(mut self, stride: [usize; N]) -> Self {
281 self.stride = stride.map(|s| check_nonzero(s, "stride must be non-zero"));
282 self
283 }
284
285 pub fn with_padding(mut self, padding: [usize; N]) -> Self {
287 self.padding = padding.map(|padding| (padding, padding));
288 self
289 }
290
291 pub fn with_padding_pairs(mut self, padding: [(usize, usize); N]) -> Self {
293 self.padding = padding;
294 self
295 }
296
297 pub fn with_dilation(mut self, dilation: [usize; N]) -> Self {
301 self.dilation = dilation.map(|d| check_nonzero(d, "dilation must be non-zero"));
302 self
303 }
304
305 pub fn with_count_include_pad(mut self, count_include_pad: bool) -> Self {
307 self.count_include_pad = count_include_pad;
308 self
309 }
310
311 pub fn with_ceil_mode(mut self, ceil_mode: bool) -> Self {
313 self.ceil_mode = ceil_mode;
314 self
315 }
316
317 pub fn is_asymmetric(&self) -> bool {
319 self.padding.iter().any(|(begin, end)| begin != end)
320 }
321}
322
323#[derive(Debug, Clone, Hash, PartialEq, Eq)]
325pub struct DeformConvOptions<const N: usize> {
326 pub stride: [usize; N],
328
329 pub padding: [usize; N],
331
332 pub dilation: [usize; N],
334
335 pub weight_groups: usize,
337
338 pub offset_groups: usize,
340}
341
342impl<const N: usize> DeformConvOptions<N> {
343 pub fn new(
345 stride: [usize; N],
346 padding: [usize; N],
347 dilation: [usize; N],
348 weight_groups: usize,
349 offset_groups: usize,
350 ) -> Self {
351 Self {
352 stride: stride.map(|s| check_nonzero(s, "stride must be non-zero")),
353 padding,
354 dilation: dilation.map(|d| check_nonzero(d, "dilation must be non-zero")),
355 weight_groups: check_nonzero(weight_groups, "weight groups must be non-zero"),
356 offset_groups: check_nonzero(offset_groups, "offset groups must be non-zero"),
357 }
358 }
359}
360
361#[derive(Debug, Clone, Hash, PartialEq, Eq)]
363pub struct ConvTransposeOptions<const N: usize> {
364 pub stride: [usize; N],
366
367 pub padding: [usize; N],
369
370 pub padding_out: [usize; N],
372
373 pub dilation: [usize; N],
375
376 pub groups: usize,
378}
379
380impl<const N: usize> ConvTransposeOptions<N> {
381 pub fn new(
383 stride: [usize; N],
384 padding: [usize; N],
385 padding_out: [usize; N],
386 dilation: [usize; N],
387 groups: usize,
388 ) -> Self {
389 Self {
390 stride: stride.map(|s| check_nonzero(s, "stride must be non-zero")),
391 padding,
392 padding_out,
393 dilation: dilation.map(|d| check_nonzero(d, "dilation must be non-zero")),
394 groups: check_nonzero(groups, "groups must be non-zero"),
395 }
396 }
397}
398
399#[derive(Debug, Clone)]
401pub struct UnfoldOptions {
402 pub stride: [usize; 2],
405
406 pub padding: [usize; 2],
408
409 pub dilation: [usize; 2],
411}
412
413impl UnfoldOptions {
414 pub fn new(stride: [usize; 2], padding: [usize; 2], dilation: [usize; 2]) -> Self {
416 Self {
417 stride: stride.map(|s| check_nonzero(s, "stride must be non-zero")),
418 padding,
419 dilation: dilation.map(|d| check_nonzero(d, "dilation must be non-zero")),
420 }
421 }
422}
423
424#[derive(new, Debug, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
426pub enum InterpolateMode {
427 Nearest,
431
432 NearestExact,
435
436 Bilinear,
439
440 Bicubic,
443
444 Lanczos3,
447}
448
449#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
451pub enum CoordinateTransformMode {
452 HalfPixel,
454 AlignCorners,
456 Asymmetric,
458 PytorchHalfPixel,
460 HalfPixelSymmetric,
462 TfCropAndResize {
464 roi: [[f32; 2]; 2],
466 extrapolation_value: f32,
468 },
469}
470
471#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
473pub enum NearestRoundingMode {
474 RoundPreferFloor,
476 RoundPreferCeil,
478 Floor,
480 Ceil,
482}
483
484#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
486pub struct CubicOptions {
487 pub a: f32,
489 pub exclude_outside: bool,
491}
492
493impl Default for CubicOptions {
494 fn default() -> Self {
495 Self {
496 a: -0.75,
497 exclude_outside: false,
498 }
499 }
500}
501
502#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
510pub struct InterpolateOptions {
511 pub mode: InterpolateMode,
513 pub coordinate_transform: CoordinateTransformMode,
515 pub nearest_mode: NearestRoundingMode,
517 pub cubic_options: CubicOptions,
519 pub output_size: Option<[usize; 2]>,
524 pub scale_factor: Option<[f32; 2]>,
530}
531
532impl InterpolateOptions {
533 pub fn new(mode: InterpolateMode) -> Self {
535 let (coordinate_transform, nearest_mode) = match mode {
536 InterpolateMode::Nearest => (
537 CoordinateTransformMode::Asymmetric,
538 NearestRoundingMode::Floor,
539 ),
540 InterpolateMode::NearestExact => (
541 CoordinateTransformMode::HalfPixel,
542 NearestRoundingMode::RoundPreferCeil,
543 ),
544 InterpolateMode::Bilinear | InterpolateMode::Bicubic | InterpolateMode::Lanczos3 => (
545 CoordinateTransformMode::AlignCorners,
546 NearestRoundingMode::RoundPreferFloor,
547 ),
548 };
549
550 Self {
551 mode,
552 coordinate_transform,
553 nearest_mode,
554 cubic_options: CubicOptions::default(),
555 output_size: None,
556 scale_factor: None,
557 }
558 }
559
560 pub fn with_coordinate_transform(mut self, mode: CoordinateTransformMode) -> Self {
562 self.coordinate_transform = mode;
563 self
564 }
565
566 pub fn with_nearest_mode(mut self, mode: NearestRoundingMode) -> Self {
568 self.nearest_mode = mode;
569 self
570 }
571
572 pub fn with_cubic_options(mut self, options: CubicOptions) -> Self {
574 self.cubic_options = options;
575 self
576 }
577
578 pub fn with_align_corners(mut self, align_corners: bool) -> Self {
582 if !matches!(
583 self.mode,
584 InterpolateMode::Nearest | InterpolateMode::NearestExact
585 ) {
586 self.coordinate_transform = if align_corners {
587 CoordinateTransformMode::AlignCorners
588 } else {
589 CoordinateTransformMode::HalfPixel
590 };
591 }
592 self
593 }
594
595 pub fn align_corners(&self) -> bool {
597 matches!(
598 self.coordinate_transform,
599 CoordinateTransformMode::AlignCorners
600 )
601 }
602
603 pub fn with_output_size(mut self, output_size: [usize; 2]) -> Self {
605 self.output_size = Some(output_size);
606 self.scale_factor = None;
607 self
608 }
609
610 pub fn with_scale_factor(mut self, scale_factor: [f32; 2]) -> Self {
612 self.scale_factor = Some(scale_factor);
613 self.output_size = None;
614 self
615 }
616}
617
618#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Deserialize, serde::Serialize)]
622pub enum GridSamplePaddingMode {
623 #[default]
625 Zeros,
626 Border,
628 Reflection,
630}
631
632#[derive(Debug, Clone)]
634pub struct GridSampleOptions {
635 pub mode: InterpolateMode,
637 pub padding_mode: GridSamplePaddingMode,
639 pub align_corners: bool,
643}
644
645impl Default for GridSampleOptions {
646 fn default() -> Self {
647 Self {
648 mode: InterpolateMode::Bilinear,
649 padding_mode: GridSamplePaddingMode::Zeros,
650 align_corners: false,
651 }
652 }
653}
654
655impl From<InterpolateMode> for GridSampleOptions {
656 fn from(value: InterpolateMode) -> Self {
657 GridSampleOptions::new(value)
658 }
659}
660
661impl GridSampleOptions {
662 pub fn new(mode: InterpolateMode) -> Self {
666 Self {
667 mode,
668 ..Default::default()
669 }
670 }
671
672 pub fn with_padding_mode(mut self, padding_mode: GridSamplePaddingMode) -> Self {
674 self.padding_mode = padding_mode;
675 self
676 }
677
678 pub fn with_align_corners(mut self, align_corners: bool) -> Self {
680 self.align_corners = align_corners;
681 self
682 }
683}
684
685#[derive(Debug, Clone, Copy, PartialEq, serde::Deserialize, serde::Serialize)]
696pub enum PadMode {
697 Constant(f32),
703
704 Reflect,
712
713 Edge,
719}
720
721impl Default for PadMode {
722 fn default() -> Self {
723 PadMode::Constant(0.0)
724 }
725}
726
727impl<E: ElementConversion> From<E> for PadMode {
728 fn from(value: E) -> Self {
729 PadMode::Constant(value.elem())
730 }
731}
732
733#[derive(Debug, Clone, Copy, Default, PartialEq, serde::Deserialize, serde::Serialize)]
735pub struct AttentionModuleOptions {
736 pub scale: Option<f64>,
738
739 pub softcap: Option<f64>,
742
743 pub is_causal: bool,
751
752 #[serde(default)]
758 pub causal_alignment: CausalAlignment,
759}
760
761#[derive(
766 Debug, Clone, Copy, Default, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize,
767)]
768pub enum CausalAlignment {
769 #[default]
773 BottomRight,
774 TopLeft,
778}
779
780impl CausalAlignment {
781 pub fn offset(self, seq_q: usize, seq_k: usize) -> i64 {
783 match self {
784 CausalAlignment::BottomRight => seq_k as i64 - seq_q as i64,
785 CausalAlignment::TopLeft => 0,
786 }
787 }
788}
789
790#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
792pub enum IndexingUpdateOp {
793 Assign,
795 Add,
797 Mul,
799 Min,
801 Max,
803}
804
805#[cfg(test)]
806mod tests {
807 use super::*;
808
809 #[test]
810 fn conv_options_symmetric_constructor() {
811 let options = ConvOptions::new([1, 2], [3, 4], [5, 6], 7);
812
813 assert_eq!(options.padding, [(3, 3), (4, 4)]);
814 assert_eq!(options.padding_begin(), [3, 4]);
815 assert_eq!(options.padding_end(), [3, 4]);
816 assert_eq!(options.symmetric_padding(), [3, 4]);
817 assert!(!options.is_asymmetric());
818 }
819
820 #[test]
821 fn conv_options_explicit_padding_constructor() {
822 let options = ConvOptions::new_with_padding([1, 2], [(3, 4), (5, 6)], [7, 8], 9);
823
824 assert_eq!(options.padding, [(3, 4), (5, 6)]);
825 assert_eq!(options.padding_begin(), [3, 5]);
826 assert_eq!(options.padding_end(), [4, 6]);
827 assert!(options.is_asymmetric());
828 }
829
830 #[test]
831 #[allow(deprecated)]
832 fn padded_conv_options_convert_to_conv_options() {
833 let options: ConvOptions<2> =
834 PaddedConvOptions::asymmetric([1, 2], [3, 5], [4, 6], [7, 8], 9).into();
835
836 assert_eq!(options.padding, [(3, 4), (5, 6)]);
837 assert_eq!(options.stride, [1, 2]);
838 assert_eq!(options.dilation, [7, 8]);
839 assert_eq!(options.groups, 9);
840 }
841
842 #[test]
843 #[allow(deprecated)]
844 fn asymmetric_conv_options_roundtrip_through_padded_options() {
845 let expected = ConvOptions::new_with_padding([1, 2], [(3, 4), (5, 6)], [7, 8], 9);
846 let padded: PaddedConvOptions<2> = expected.clone().into();
847 let actual: ConvOptions<2> = padded.into();
848
849 assert_eq!(actual, expected);
850 }
851
852 #[test]
853 #[should_panic = "expected symmetric convolution padding"]
854 fn conv_options_symmetric_padding_with_asymmetric_options() {
855 let options = ConvOptions::new_with_padding([1], [(1, 2)], [1], 1);
856 let _ = options.symmetric_padding();
857 }
858
859 #[test]
860 #[should_panic = "stride must be non-zero"]
861 fn conv_options_stride_zero() {
862 let _opt = ConvOptions::new([0, 1], [0, 0], [1, 1], 1);
863 }
864
865 #[test]
866 #[should_panic = "dilation must be non-zero"]
867 fn conv_options_dilation_zero() {
868 let _opt = ConvOptions::new([1, 1], [0, 0], [0, 0], 1);
869 }
870
871 #[test]
872 #[should_panic = "groups must be non-zero"]
873 fn conv_options_groups_zero() {
874 let _opt = ConvOptions::new([1, 1], [0, 0], [1, 1], 0);
875 }
876
877 #[test]
878 fn pool_options_defaults() {
879 let max = MaxPoolOptions::new([2, 3]);
880 let avg = AvgPoolOptions::new([2, 3]);
881
882 assert_eq!(max.stride, [2, 3]);
883 assert_eq!(max.padding, [(0, 0), (0, 0)]);
884 assert_eq!(max.dilation, [1, 1]);
885 assert!(!max.ceil_mode);
886 assert_eq!(avg.stride, [2, 3]);
887 assert_eq!(avg.padding, [(0, 0), (0, 0)]);
888 assert_eq!(avg.dilation, [1, 1]);
889 assert!(avg.count_include_pad);
890 assert!(!avg.ceil_mode);
891 }
892
893 #[test]
894 fn pool_options_padding() {
895 let symmetric = MaxPoolOptions::new([2, 2]).with_padding([0, 1]);
896 let asymmetric = AvgPoolOptions::new([2]).with_padding_pairs([(0, 1)]);
897
898 assert_eq!(symmetric.padding, [(0, 0), (1, 1)]);
899 assert!(!symmetric.is_asymmetric());
900 assert!(asymmetric.is_asymmetric());
901 }
902
903 #[test]
904 #[should_panic = "kernel size must be non-zero"]
905 fn max_pool_options_kernel_size_zero() {
906 let _opt = MaxPoolOptions::new([0, 1]);
907 }
908
909 #[test]
910 #[should_panic = "dilation must be non-zero"]
911 fn max_pool_options_dilation_zero() {
912 let _opt = MaxPoolOptions::new([1, 1]).with_dilation([0, 1]);
913 }
914
915 #[test]
916 #[should_panic = "dilation must be non-zero"]
917 fn avg_pool_options_dilation_zero() {
918 let _opt = AvgPoolOptions::new([1, 1]).with_dilation([0, 1]);
919 }
920
921 #[test]
922 #[should_panic = "stride must be non-zero"]
923 fn avg_pool_options_stride_zero() {
924 let _opt = AvgPoolOptions::new([1, 1]).with_stride([0, 1]);
925 }
926
927 #[test]
928 #[should_panic = "stride must be non-zero"]
929 fn conv_transpose_options_stride_zero() {
930 let _opt = ConvTransposeOptions::new([0, 1], [0, 0], [0, 0], [1, 1], 1);
931 }
932
933 #[test]
934 #[should_panic = "dilation must be non-zero"]
935 fn conv_transpose_options_dilation_zero() {
936 let _opt = ConvTransposeOptions::new([1, 1], [0, 0], [0, 0], [0, 0], 1);
937 }
938
939 #[test]
940 #[should_panic = "groups must be non-zero"]
941 fn conv_transpose_options_groups_zero() {
942 let _opt = ConvTransposeOptions::new([1, 1], [0, 0], [0, 0], [1, 1], 0);
943 }
944
945 #[test]
946 #[should_panic = "stride must be non-zero"]
947 fn deform_conv_options_stride_zero() {
948 let _opt = DeformConvOptions::new([0, 1], [0, 0], [1, 1], 1, 1);
949 }
950
951 #[test]
952 #[should_panic = "dilation must be non-zero"]
953 fn deform_conv_options_dilation_zero() {
954 let _opt = DeformConvOptions::new([1, 1], [0, 0], [0, 0], 1, 1);
955 }
956
957 #[test]
958 #[should_panic = "weight groups must be non-zero"]
959 fn deform_conv_options_weights_groups_zero() {
960 let _opt = DeformConvOptions::new([1, 1], [0, 0], [1, 1], 0, 1);
961 }
962
963 #[test]
964 #[should_panic = "offset groups must be non-zero"]
965 fn deform_conv_options_offset_groups_zero() {
966 let _opt = DeformConvOptions::new([1, 1], [0, 0], [1, 1], 1, 0);
967 }
968
969 #[test]
970 #[should_panic = "stride must be non-zero"]
971 fn unfold_options_stride_zero() {
972 let _opt = UnfoldOptions::new([0, 1], [0, 0], [1, 1]);
973 }
974
975 #[test]
976 #[should_panic = "dilation must be non-zero"]
977 fn unfold_options_dilation_zero() {
978 let _opt = UnfoldOptions::new([1, 1], [0, 0], [0, 0]);
979 }
980}