1use super::MatZq;
12use crate::{
13 error::MathError,
14 traits::{CompareBase, MatrixDimensions, Tensor},
15};
16use flint3_sys::{_fmpz_mod_mat_reduce, fmpz_mat_kronecker_product};
17
18impl Tensor for MatZq {
19 fn tensor_product(&self, other: &Self) -> Self {
49 self.tensor_product_safe(other).unwrap()
50 }
51}
52
53impl MatZq {
54 pub fn tensor_product_safe(&self, other: &Self) -> Result<Self, MathError> {
84 if !self.compare_base(other) {
85 return Err(self.call_compare_base_error(other).unwrap());
86 }
87
88 let mut out = MatZq::new(
89 self.get_num_rows() * other.get_num_rows(),
90 self.get_num_columns() * other.get_num_columns(),
91 self.get_mod(),
92 );
93
94 unsafe { fmpz_mat_kronecker_product(&mut out.matrix, &self.matrix, &other.matrix) };
95
96 unsafe { _fmpz_mod_mat_reduce(&mut out.matrix, self.modulus.get_fmpz_mod_ctx_struct()) }
97
98 Ok(out)
99 }
100}
101
102#[cfg(test)]
103mod test_tensor {
104 use crate::{
105 integer_mod_q::MatZq,
106 traits::{MatrixDimensions, Tensor},
107 };
108 use std::str::FromStr;
109
110 #[test]
112 fn dimensions_fit() {
113 let mat_1 = MatZq::new(17, 13, 13);
114 let mat_2 = MatZq::new(3, 4, 13);
115
116 let mat_3 = mat_1.tensor_product(&mat_2);
117 let mat_3_safe = mat_1.tensor_product_safe(&mat_2).unwrap();
118
119 assert_eq!(51, mat_3.get_num_rows());
120 assert_eq!(52, mat_3.get_num_columns());
121 assert_eq!(&mat_3, &mat_3_safe);
122 }
123
124 #[test]
126 fn identity() {
127 let identity = MatZq::from_str(&format!("[[1, 0],[0, 1]] mod {}", u128::MAX)).unwrap();
128 let mat_1 = MatZq::from_str(&format!(
129 "[[1, {}, 1],[0, {}, -1]] mod {}",
130 u64::MAX,
131 i64::MIN,
132 u128::MAX
133 ))
134 .unwrap();
135
136 let mat_2 = identity.tensor_product(&mat_1);
137 let mat_3 = mat_1.tensor_product(&identity);
138 let mat_2_safe = identity.tensor_product_safe(&mat_1).unwrap();
139 let mat_3_safe = mat_1.tensor_product_safe(&identity).unwrap();
140
141 let cmp_mat_2 = MatZq::from_str(&format!(
142 "[[1, {}, 1, 0, 0, 0], \
143 [0, {}, -1, 0, 0, 0], \
144 [0, 0, 0, 1, {}, 1], \
145 [0, 0, 0, 0, {}, -1]] mod {}",
146 u64::MAX,
147 i64::MIN,
148 u64::MAX,
149 i64::MIN,
150 u128::MAX
151 ))
152 .unwrap();
153 let cmp_mat_3 = MatZq::from_str(&format!(
154 "[[1, 0, {}, 0, 1, 0], \
155 [0, 1, 0, {}, 0, 1], \
156 [0, 0, {}, 0, -1, 0], \
157 [0, 0, 0, {}, 0, -1]] mod {}",
158 u64::MAX,
159 u64::MAX,
160 i64::MIN,
161 i64::MIN,
162 u128::MAX
163 ))
164 .unwrap();
165
166 assert_eq!(cmp_mat_2, mat_2);
167 assert_eq!(cmp_mat_3, mat_3);
168 assert_eq!(cmp_mat_2, mat_2_safe);
169 assert_eq!(cmp_mat_3, mat_3_safe);
170 }
171
172 #[test]
174 fn vector_matrix() {
175 let vector = MatZq::from_str(&format!("[[1],[-1]] mod {}", u128::MAX)).unwrap();
176 let mat_1 = MatZq::from_str(&format!(
177 "[[1, {}, 1],[0, {}, -1]] mod {}",
178 u64::MAX,
179 i64::MAX,
180 u128::MAX
181 ))
182 .unwrap();
183
184 let mat_2 = vector.tensor_product(&mat_1);
185 let mat_3 = mat_1.tensor_product(&vector);
186 let mat_2_safe = vector.tensor_product_safe(&mat_1).unwrap();
187 let mat_3_safe = mat_1.tensor_product_safe(&vector).unwrap();
188
189 let cmp_mat_2 = MatZq::from_str(&format!(
190 "[[1, {}, 1],[0, {}, -1],[-1, -{}, -1],[0, -{}, 1]] mod {}",
191 u64::MAX,
192 i64::MAX,
193 u64::MAX,
194 i64::MAX,
195 u128::MAX
196 ))
197 .unwrap();
198 let cmp_mat_3 = MatZq::from_str(&format!(
199 "[[1, {}, 1],[-1, -{}, -1],[0, {}, -1],[0, -{}, 1]] mod {}",
200 u64::MAX,
201 u64::MAX,
202 i64::MAX,
203 i64::MAX,
204 u128::MAX
205 ))
206 .unwrap();
207
208 assert_eq!(cmp_mat_2, mat_2);
209 assert_eq!(cmp_mat_3, mat_3);
210 assert_eq!(cmp_mat_2, mat_2_safe);
211 assert_eq!(cmp_mat_3, mat_3_safe);
212 }
213
214 #[test]
216 fn vector_vector() {
217 let vec_1 = MatZq::from_str(&format!("[[2],[1]] mod {}", u128::MAX)).unwrap();
218 let vec_2 = MatZq::from_str(&format!(
219 "[[{}],[{}]] mod {}",
220 (u64::MAX - 1) / 2,
221 i64::MIN / 2,
222 u128::MAX
223 ))
224 .unwrap();
225
226 let vec_3 = vec_1.tensor_product(&vec_2);
227 let vec_4 = vec_2.tensor_product(&vec_1);
228 let vec_3_safe = vec_1.tensor_product_safe(&vec_2).unwrap();
229 let vec_4_safe = vec_2.tensor_product_safe(&vec_1).unwrap();
230
231 let cmp_vec_3 = MatZq::from_str(&format!(
232 "[[{}],[{}],[{}],[{}]] mod {}",
233 u64::MAX - 1,
234 i64::MIN,
235 (u64::MAX - 1) / 2,
236 i64::MIN / 2,
237 u128::MAX
238 ))
239 .unwrap();
240 let cmp_vec_4 = MatZq::from_str(&format!(
241 "[[{}],[{}],[{}],[{}]] mod {}",
242 u64::MAX - 1,
243 (u64::MAX - 1) / 2,
244 i64::MIN,
245 i64::MIN / 2,
246 u128::MAX
247 ))
248 .unwrap();
249
250 assert_eq!(cmp_vec_3, vec_3);
251 assert_eq!(cmp_vec_4, vec_4);
252 assert_eq!(cmp_vec_3, vec_3_safe);
253 assert_eq!(cmp_vec_4, vec_4_safe);
254 }
255
256 #[test]
258 fn entries_reduced() {
259 let mat_1 = MatZq::from_str(&format!("[[1, 2],[3, 4]] mod {}", u64::MAX - 58)).unwrap();
260 let mat_2 = MatZq::from_str(&format!("[[1, 58],[0, -1]] mod {}", u64::MAX - 58)).unwrap();
261
262 let mat_3 = mat_1.tensor_product(&mat_2);
263 let mat_3_safe = mat_1.tensor_product_safe(&mat_2).unwrap();
264
265 let mat_3_cmp = MatZq::from_str(&format!(
266 "[[1, 58, 2, 116],[0, -1, 0, -2],[3, 174, 4, 232],[0, -3, 0, -4]] mod {}",
267 u64::MAX - 58
268 ))
269 .unwrap();
270 assert_eq!(mat_3_cmp, mat_3);
271 assert_eq!(mat_3_cmp, mat_3_safe);
272 }
273
274 #[test]
276 #[should_panic]
277 fn mismatching_moduli_tensor_product() {
278 let mat_1 = MatZq::new(1, 2, u64::MAX);
279 let mat_2 = MatZq::new(1, 2, u64::MAX - 58);
280
281 let _ = mat_1.tensor_product(&mat_2);
282 }
283
284 #[test]
286 fn mismatching_moduli_tensor_product_safe() {
287 let mat_1 = MatZq::new(1, 2, u64::MAX);
288 let mat_2 = MatZq::new(1, 2, u64::MAX - 58);
289
290 assert!(mat_1.tensor_product_safe(&mat_2).is_err());
291 assert!(mat_2.tensor_product_safe(&mat_1).is_err());
292 }
293}