Coverage Report

Created: 2026-07-13 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}