/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ndarray-0.17.2/src/tri.rs
Line | Count | Source |
1 | | // Copyright 2014-2024 bluss and ndarray developers. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
4 | | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
5 | | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
6 | | // option. This file may not be copied, modified, or distributed |
7 | | // except according to those terms. |
8 | | |
9 | | use core::cmp::min; |
10 | | |
11 | | use num_traits::Zero; |
12 | | |
13 | | use crate::{ |
14 | | dimension::{is_layout_c, is_layout_f}, |
15 | | Array, |
16 | | ArrayRef, |
17 | | Axis, |
18 | | Dimension, |
19 | | Zip, |
20 | | }; |
21 | | |
22 | | impl<A, D> ArrayRef<A, D> |
23 | | where |
24 | | D: Dimension, |
25 | | A: Clone + Zero, |
26 | | { |
27 | | /// Upper triangular of an array. |
28 | | /// |
29 | | /// Return a copy of the array with elements below the *k*-th diagonal zeroed. |
30 | | /// For arrays with `ndim` exceeding 2, `triu` will apply to the final two axes. |
31 | | /// For 0D and 1D arrays, `triu` will return an unchanged clone. |
32 | | /// |
33 | | /// See also [`ArrayRef::tril`] |
34 | | /// |
35 | | /// ``` |
36 | | /// use ndarray::array; |
37 | | /// |
38 | | /// let arr = array![ |
39 | | /// [1, 2, 3], |
40 | | /// [4, 5, 6], |
41 | | /// [7, 8, 9] |
42 | | /// ]; |
43 | | /// assert_eq!( |
44 | | /// arr.triu(0), |
45 | | /// array![ |
46 | | /// [1, 2, 3], |
47 | | /// [0, 5, 6], |
48 | | /// [0, 0, 9] |
49 | | /// ] |
50 | | /// ); |
51 | | /// ``` |
52 | 0 | pub fn triu(&self, k: isize) -> Array<A, D> |
53 | | { |
54 | 0 | if self.ndim() <= 1 { |
55 | 0 | return self.to_owned(); |
56 | 0 | } |
57 | | |
58 | | // Performance optimization for F-order arrays. |
59 | | // C-order array check prevents infinite recursion in edge cases like [[1]]. |
60 | | // k-size check prevents underflow when k == isize::MIN |
61 | 0 | let n = self.ndim(); |
62 | 0 | if is_layout_f(self._dim(), self._strides()) && !is_layout_c(self._dim(), self._strides()) && k > isize::MIN { |
63 | 0 | let mut x = self.view(); |
64 | 0 | x.swap_axes(n - 2, n - 1); |
65 | 0 | let mut tril = x.tril(-k); |
66 | 0 | tril.swap_axes(n - 2, n - 1); |
67 | | |
68 | 0 | return tril; |
69 | 0 | } |
70 | | |
71 | 0 | let mut res = Array::zeros(self.raw_dim()); |
72 | 0 | let ncols = self.len_of(Axis(n - 1)); |
73 | 0 | let nrows = self.len_of(Axis(n - 2)); |
74 | 0 | let indices = Array::from_iter(0..nrows); |
75 | 0 | Zip::from(self.rows()) |
76 | 0 | .and(res.rows_mut()) |
77 | 0 | .and_broadcast(&indices) |
78 | 0 | .for_each(|src, mut dst, row_num| { |
79 | 0 | let mut lower = match k >= 0 { |
80 | 0 | true => row_num.saturating_add(k as usize), // Avoid overflow |
81 | 0 | false => row_num.saturating_sub(k.unsigned_abs()), // Avoid underflow, go to 0 |
82 | | }; |
83 | 0 | lower = min(lower, ncols); |
84 | 0 | (*dst) |
85 | 0 | .slice_mut(s![lower..]) |
86 | 0 | .assign(&(*src).slice(s![lower..])); |
87 | 0 | }); |
88 | | |
89 | 0 | res |
90 | 0 | } |
91 | | |
92 | | /// Lower triangular of an array. |
93 | | /// |
94 | | /// Return a copy of the array with elements above the *k*-th diagonal zeroed. |
95 | | /// For arrays with `ndim` exceeding 2, `tril` will apply to the final two axes. |
96 | | /// For 0D and 1D arrays, `tril` will return an unchanged clone. |
97 | | /// |
98 | | /// See also [`ArrayRef::triu`] |
99 | | /// |
100 | | /// ``` |
101 | | /// use ndarray::array; |
102 | | /// |
103 | | /// let arr = array![ |
104 | | /// [1, 2, 3], |
105 | | /// [4, 5, 6], |
106 | | /// [7, 8, 9] |
107 | | /// ]; |
108 | | /// assert_eq!( |
109 | | /// arr.tril(0), |
110 | | /// array![ |
111 | | /// [1, 0, 0], |
112 | | /// [4, 5, 0], |
113 | | /// [7, 8, 9] |
114 | | /// ] |
115 | | /// ); |
116 | | /// ``` |
117 | 0 | pub fn tril(&self, k: isize) -> Array<A, D> |
118 | | { |
119 | 0 | if self.ndim() <= 1 { |
120 | 0 | return self.to_owned(); |
121 | 0 | } |
122 | | |
123 | | // Performance optimization for F-order arrays. |
124 | | // C-order array check prevents infinite recursion in edge cases like [[1]]. |
125 | | // k-size check prevents underflow when k == isize::MIN |
126 | 0 | let n = self.ndim(); |
127 | 0 | if is_layout_f(self._dim(), self._strides()) && !is_layout_c(self._dim(), self._strides()) && k > isize::MIN { |
128 | 0 | let mut x = self.view(); |
129 | 0 | x.swap_axes(n - 2, n - 1); |
130 | 0 | let mut triu = x.triu(-k); |
131 | 0 | triu.swap_axes(n - 2, n - 1); |
132 | | |
133 | 0 | return triu; |
134 | 0 | } |
135 | | |
136 | 0 | let mut res = Array::zeros(self.raw_dim()); |
137 | 0 | let ncols = self.len_of(Axis(n - 1)); |
138 | 0 | let nrows = self.len_of(Axis(n - 2)); |
139 | 0 | let indices = Array::from_iter(0..nrows); |
140 | 0 | Zip::from(self.rows()) |
141 | 0 | .and(res.rows_mut()) |
142 | 0 | .and_broadcast(&indices) |
143 | 0 | .for_each(|src, mut dst, row_num| { |
144 | | // let row_num = i.into_dimension().last_elem(); |
145 | 0 | let mut upper = match k >= 0 { |
146 | 0 | true => row_num.saturating_add(k as usize).saturating_add(1), // Avoid overflow |
147 | 0 | false => row_num.saturating_sub((k + 1).unsigned_abs()), // Avoid underflow |
148 | | }; |
149 | 0 | upper = min(upper, ncols); |
150 | 0 | (*dst) |
151 | 0 | .slice_mut(s![..upper]) |
152 | 0 | .assign(&(*src).slice(s![..upper])); |
153 | 0 | }); |
154 | | |
155 | 0 | res |
156 | 0 | } |
157 | | } |
158 | | |
159 | | #[cfg(test)] |
160 | | mod tests |
161 | | { |
162 | | use crate::{array, dimension, Array0, Array1, Array2, Array3, ShapeBuilder}; |
163 | | use alloc::vec; |
164 | | |
165 | | #[test] |
166 | | fn test_keep_order() |
167 | | { |
168 | | let x = Array2::<f64>::ones((3, 3).f()); |
169 | | let res = x.triu(0); |
170 | | assert!(dimension::is_layout_f(&res.parts.dim, &res.parts.strides)); |
171 | | |
172 | | let res = x.tril(0); |
173 | | assert!(dimension::is_layout_f(&res.parts.dim, &res.parts.strides)); |
174 | | } |
175 | | |
176 | | #[test] |
177 | | fn test_0d() |
178 | | { |
179 | | let x = Array0::<f64>::ones(()); |
180 | | let res = x.triu(0); |
181 | | assert_eq!(res, x); |
182 | | |
183 | | let res = x.tril(0); |
184 | | assert_eq!(res, x); |
185 | | |
186 | | let x = Array0::<f64>::ones(().f()); |
187 | | let res = x.triu(0); |
188 | | assert_eq!(res, x); |
189 | | |
190 | | let res = x.tril(0); |
191 | | assert_eq!(res, x); |
192 | | } |
193 | | |
194 | | #[test] |
195 | | fn test_1d() |
196 | | { |
197 | | let x = array![1, 2, 3]; |
198 | | let res = x.triu(0); |
199 | | assert_eq!(res, x); |
200 | | |
201 | | let res = x.triu(0); |
202 | | assert_eq!(res, x); |
203 | | |
204 | | let x = Array1::<f64>::ones(3.f()); |
205 | | let res = x.triu(0); |
206 | | assert_eq!(res, x); |
207 | | |
208 | | let res = x.triu(0); |
209 | | assert_eq!(res, x); |
210 | | } |
211 | | |
212 | | #[test] |
213 | | fn test_2d() |
214 | | { |
215 | | let x = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; |
216 | | |
217 | | // Upper |
218 | | let res = x.triu(0); |
219 | | assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); |
220 | | |
221 | | // Lower |
222 | | let res = x.tril(0); |
223 | | assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); |
224 | | |
225 | | let x = Array2::from_shape_vec((3, 3).f(), vec![1, 4, 7, 2, 5, 8, 3, 6, 9]).unwrap(); |
226 | | |
227 | | // Upper |
228 | | let res = x.triu(0); |
229 | | assert_eq!(res, array![[1, 2, 3], [0, 5, 6], [0, 0, 9]]); |
230 | | |
231 | | // Lower |
232 | | let res = x.tril(0); |
233 | | assert_eq!(res, array![[1, 0, 0], [4, 5, 0], [7, 8, 9]]); |
234 | | } |
235 | | |
236 | | #[test] |
237 | | fn test_2d_single() |
238 | | { |
239 | | let x = array![[1]]; |
240 | | |
241 | | assert_eq!(x.triu(0), array![[1]]); |
242 | | assert_eq!(x.tril(0), array![[1]]); |
243 | | assert_eq!(x.triu(1), array![[0]]); |
244 | | assert_eq!(x.tril(1), array![[1]]); |
245 | | assert_eq!(x.triu(-1), array![[1]]); |
246 | | assert_eq!(x.tril(-1), array![[0]]); |
247 | | } |
248 | | |
249 | | #[test] |
250 | | fn test_3d() |
251 | | { |
252 | | let x = array![ |
253 | | [[1, 2, 3], [4, 5, 6], [7, 8, 9]], |
254 | | [[10, 11, 12], [13, 14, 15], [16, 17, 18]], |
255 | | [[19, 20, 21], [22, 23, 24], [25, 26, 27]] |
256 | | ]; |
257 | | |
258 | | // Upper |
259 | | let res = x.triu(0); |
260 | | assert_eq!( |
261 | | res, |
262 | | array![ |
263 | | [[1, 2, 3], [0, 5, 6], [0, 0, 9]], |
264 | | [[10, 11, 12], [0, 14, 15], [0, 0, 18]], |
265 | | [[19, 20, 21], [0, 23, 24], [0, 0, 27]] |
266 | | ] |
267 | | ); |
268 | | |
269 | | // Lower |
270 | | let res = x.tril(0); |
271 | | assert_eq!( |
272 | | res, |
273 | | array![ |
274 | | [[1, 0, 0], [4, 5, 0], [7, 8, 9]], |
275 | | [[10, 0, 0], [13, 14, 0], [16, 17, 18]], |
276 | | [[19, 0, 0], [22, 23, 0], [25, 26, 27]] |
277 | | ] |
278 | | ); |
279 | | |
280 | | let x = Array3::from_shape_vec( |
281 | | (3, 3, 3).f(), |
282 | | vec![1, 10, 19, 4, 13, 22, 7, 16, 25, 2, 11, 20, 5, 14, 23, 8, 17, 26, 3, 12, 21, 6, 15, 24, 9, 18, 27], |
283 | | ) |
284 | | .unwrap(); |
285 | | |
286 | | // Upper |
287 | | let res = x.triu(0); |
288 | | assert_eq!( |
289 | | res, |
290 | | array![ |
291 | | [[1, 2, 3], [0, 5, 6], [0, 0, 9]], |
292 | | [[10, 11, 12], [0, 14, 15], [0, 0, 18]], |
293 | | [[19, 20, 21], [0, 23, 24], [0, 0, 27]] |
294 | | ] |
295 | | ); |
296 | | |
297 | | // Lower |
298 | | let res = x.tril(0); |
299 | | assert_eq!( |
300 | | res, |
301 | | array![ |
302 | | [[1, 0, 0], [4, 5, 0], [7, 8, 9]], |
303 | | [[10, 0, 0], [13, 14, 0], [16, 17, 18]], |
304 | | [[19, 0, 0], [22, 23, 0], [25, 26, 27]] |
305 | | ] |
306 | | ); |
307 | | } |
308 | | |
309 | | #[test] |
310 | | fn test_off_axis() |
311 | | { |
312 | | let x = array![ |
313 | | [[1, 2, 3], [4, 5, 6], [7, 8, 9]], |
314 | | [[10, 11, 12], [13, 14, 15], [16, 17, 18]], |
315 | | [[19, 20, 21], [22, 23, 24], [25, 26, 27]] |
316 | | ]; |
317 | | |
318 | | let res = x.triu(1); |
319 | | assert_eq!( |
320 | | res, |
321 | | array![ |
322 | | [[0, 2, 3], [0, 0, 6], [0, 0, 0]], |
323 | | [[0, 11, 12], [0, 0, 15], [0, 0, 0]], |
324 | | [[0, 20, 21], [0, 0, 24], [0, 0, 0]] |
325 | | ] |
326 | | ); |
327 | | |
328 | | let res = x.triu(-1); |
329 | | assert_eq!( |
330 | | res, |
331 | | array![ |
332 | | [[1, 2, 3], [4, 5, 6], [0, 8, 9]], |
333 | | [[10, 11, 12], [13, 14, 15], [0, 17, 18]], |
334 | | [[19, 20, 21], [22, 23, 24], [0, 26, 27]] |
335 | | ] |
336 | | ); |
337 | | } |
338 | | |
339 | | #[test] |
340 | | fn test_odd_shape() |
341 | | { |
342 | | let x = array![[1, 2, 3], [4, 5, 6]]; |
343 | | let res = x.triu(0); |
344 | | assert_eq!(res, array![[1, 2, 3], [0, 5, 6]]); |
345 | | |
346 | | let res = x.tril(0); |
347 | | assert_eq!(res, array![[1, 0, 0], [4, 5, 0]]); |
348 | | |
349 | | let x = array![[1, 2], [3, 4], [5, 6]]; |
350 | | let res = x.triu(0); |
351 | | assert_eq!(res, array![[1, 2], [0, 4], [0, 0]]); |
352 | | |
353 | | let res = x.tril(0); |
354 | | assert_eq!(res, array![[1, 0], [3, 4], [5, 6]]); |
355 | | } |
356 | | |
357 | | #[test] |
358 | | fn test_odd_k() |
359 | | { |
360 | | let x = array![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; |
361 | | let z = Array2::zeros([3, 3]); |
362 | | assert_eq!(x.triu(isize::MIN), x); |
363 | | assert_eq!(x.tril(isize::MIN), z); |
364 | | assert_eq!(x.triu(isize::MAX), z); |
365 | | assert_eq!(x.tril(isize::MAX), x); |
366 | | } |
367 | | } |