Skip to main content

tenferro_tensor/
config.rs

1use crate::{Error, Result};
2
3const DOT_GENERAL_OP: &str = "dot_general";
4
5fn invalid_dot_general_config(message: impl Into<String>) -> Error {
6    Error::InvalidConfig {
7        op: DOT_GENERAL_OP,
8        message: message.into(),
9    }
10}
11
12/// DotGeneral dimension configuration.
13///
14/// Records only the dim-numbering roles (contracting / batch; free is derived).
15/// Rank info travels with the enclosing `StdTensorOp::DotGeneral` variant at
16/// the trace/StdTensorOp layer, and with `ExecInstruction::output_shapes` at
17/// the exec layer. This separation makes it structurally impossible for
18/// stored ranks to drift from actual tensor ranks (issue #664).
19///
20/// The output shape is `[lhs_free..., rhs_free..., batch...]` (col-major
21/// batch-trailing convention). Batch dims have the largest stride so that
22/// each batch slice occupies a contiguous block of memory.
23///
24/// # Examples
25///
26/// ```rust
27/// use tenferro_tensor::DotGeneralConfig;
28///
29/// let config = DotGeneralConfig {
30///     lhs_contracting_dims: vec![1],
31///     rhs_contracting_dims: vec![0],
32///     lhs_batch_dims: vec![],
33///     rhs_batch_dims: vec![],
34/// };
35/// ```
36#[derive(Clone, Debug, Hash, PartialEq, Eq)]
37pub struct DotGeneralConfig {
38    pub lhs_contracting_dims: Vec<usize>,
39    pub rhs_contracting_dims: Vec<usize>,
40    pub lhs_batch_dims: Vec<usize>,
41    pub rhs_batch_dims: Vec<usize>,
42}
43
44impl DotGeneralConfig {
45    fn check_no_duplicates(dims: &[usize], label: &str) -> Result<()> {
46        let mut seen = std::collections::HashSet::new();
47        for &d in dims {
48            if !seen.insert(d) {
49                return Err(invalid_dot_general_config(format!(
50                    "{} contains duplicate dim {}",
51                    label, d
52                )));
53            }
54        }
55        Ok(())
56    }
57
58    /// Validate that all dimension indices are within range for the given
59    /// explicit ranks and that no axis appears in multiple roles.
60    ///
61    /// Call sites supply the actual operand ranks (from the tensor shapes they
62    /// have in hand). The config itself carries only the dim-numbering roles.
63    ///
64    /// # Examples
65    ///
66    /// ```rust
67    /// use tenferro_tensor::DotGeneralConfig;
68    ///
69    /// let config = DotGeneralConfig {
70    ///     lhs_contracting_dims: vec![1],
71    ///     rhs_contracting_dims: vec![0],
72    ///     lhs_batch_dims: vec![],
73    ///     rhs_batch_dims: vec![],
74    /// };
75    /// config.validate_dims_with_ranks(2, 2).unwrap();
76    /// ```
77    pub fn validate_dims_with_ranks(&self, lhs_rank: usize, rhs_rank: usize) -> Result<()> {
78        for &d in &self.lhs_contracting_dims {
79            if d >= lhs_rank {
80                return Err(invalid_dot_general_config(format!(
81                    "lhs_contracting_dim {} out of bounds for lhs_rank {}",
82                    d, lhs_rank
83                )));
84            }
85        }
86        for &d in &self.rhs_contracting_dims {
87            if d >= rhs_rank {
88                return Err(invalid_dot_general_config(format!(
89                    "rhs_contracting_dim {} out of bounds for rhs_rank {}",
90                    d, rhs_rank
91                )));
92            }
93        }
94        for &d in &self.lhs_batch_dims {
95            if d >= lhs_rank {
96                return Err(invalid_dot_general_config(format!(
97                    "lhs_batch_dim {} out of bounds for lhs_rank {}",
98                    d, lhs_rank
99                )));
100            }
101        }
102        for &d in &self.rhs_batch_dims {
103            if d >= rhs_rank {
104                return Err(invalid_dot_general_config(format!(
105                    "rhs_batch_dim {} out of bounds for rhs_rank {}",
106                    d, rhs_rank
107                )));
108            }
109        }
110        Self::check_no_duplicates(&self.lhs_contracting_dims, "lhs_contracting_dims")?;
111        Self::check_no_duplicates(&self.rhs_contracting_dims, "rhs_contracting_dims")?;
112        Self::check_no_duplicates(&self.lhs_batch_dims, "lhs_batch_dims")?;
113        Self::check_no_duplicates(&self.rhs_batch_dims, "rhs_batch_dims")?;
114        for &d in &self.lhs_contracting_dims {
115            if self.lhs_batch_dims.contains(&d) {
116                return Err(invalid_dot_general_config(format!(
117                    "lhs dim {} appears in both contracting and batch dims",
118                    d
119                )));
120            }
121        }
122        for &d in &self.rhs_contracting_dims {
123            if self.rhs_batch_dims.contains(&d) {
124                return Err(invalid_dot_general_config(format!(
125                    "rhs dim {} appears in both contracting and batch dims",
126                    d
127                )));
128            }
129        }
130        if self.lhs_contracting_dims.len() != self.rhs_contracting_dims.len() {
131            return Err(invalid_dot_general_config(format!(
132                "lhs/rhs contracting dim counts differ ({} vs {})",
133                self.lhs_contracting_dims.len(),
134                self.rhs_contracting_dims.len()
135            )));
136        }
137        if self.lhs_batch_dims.len() != self.rhs_batch_dims.len() {
138            return Err(invalid_dot_general_config(format!(
139                "lhs/rhs batch dim counts differ ({} vs {})",
140                self.lhs_batch_dims.len(),
141                self.rhs_batch_dims.len()
142            )));
143        }
144        Ok(())
145    }
146}
147
148/// Comparison direction.
149///
150/// # Examples
151///
152/// ```rust
153/// use tenferro_tensor::CompareDir;
154///
155/// let dir = CompareDir::Eq;
156/// ```
157#[derive(Clone, Debug, Hash, PartialEq, Eq)]
158pub enum CompareDir {
159    Eq,
160    Lt,
161    Le,
162    Gt,
163    Ge,
164}
165
166/// StableHLO gather dimension configuration.
167///
168/// # Examples
169///
170/// ```rust
171/// use tenferro_tensor::GatherConfig;
172///
173/// let config = GatherConfig {
174///     offset_dims: vec![],
175///     collapsed_slice_dims: vec![0],
176///     start_index_map: vec![0],
177///     index_vector_dim: 1,
178///     slice_sizes: vec![1],
179/// };
180/// ```
181#[derive(Clone, Debug, Hash, PartialEq, Eq)]
182pub struct GatherConfig {
183    pub offset_dims: Vec<usize>,
184    pub collapsed_slice_dims: Vec<usize>,
185    pub start_index_map: Vec<usize>,
186    pub index_vector_dim: usize,
187    pub slice_sizes: Vec<usize>,
188}
189
190/// StableHLO scatter dimension configuration.
191///
192/// # Examples
193///
194/// ```rust
195/// use tenferro_tensor::ScatterConfig;
196///
197/// let config = ScatterConfig {
198///     update_window_dims: vec![],
199///     inserted_window_dims: vec![0],
200///     scatter_dims_to_operand_dims: vec![0],
201///     index_vector_dim: 1,
202/// };
203/// ```
204#[derive(Clone, Debug, Hash, PartialEq, Eq)]
205pub struct ScatterConfig {
206    pub update_window_dims: Vec<usize>,
207    pub inserted_window_dims: Vec<usize>,
208    pub scatter_dims_to_operand_dims: Vec<usize>,
209    pub index_vector_dim: usize,
210}
211
212/// Slice configuration.
213///
214/// # Examples
215///
216/// ```rust
217/// use tenferro_tensor::SliceConfig;
218///
219/// let config = SliceConfig {
220///     starts: vec![0],
221///     limits: vec![2],
222///     strides: vec![1],
223/// };
224/// ```
225#[derive(Clone, Debug, Hash, PartialEq, Eq)]
226pub struct SliceConfig {
227    pub starts: Vec<usize>,
228    pub limits: Vec<usize>,
229    pub strides: Vec<usize>,
230}
231
232/// StableHLO pad configuration.
233///
234/// # Examples
235///
236/// ```rust
237/// use tenferro_tensor::PadConfig;
238///
239/// let config = PadConfig {
240///     edge_padding_low: vec![1, 1],
241///     edge_padding_high: vec![1, 1],
242///     interior_padding: vec![0, 0],
243/// };
244/// ```
245#[derive(Clone, Debug, Hash, PartialEq, Eq)]
246pub struct PadConfig {
247    pub edge_padding_low: Vec<i64>,
248    pub edge_padding_high: Vec<i64>,
249    pub interior_padding: Vec<i64>,
250}