Coverage Report

Created: 2026-09-19 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ndarray-0.17.2/src/dimension/broadcast.rs
Line
Count
Source
1
use crate::error::*;
2
use crate::{Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn};
3
4
/// Calculate the common shape for a pair of array shapes, that they can be broadcasted
5
/// to. Return an error if the shapes are not compatible.
6
///
7
/// Uses the [NumPy broadcasting rules]
8
//  (https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html#general-broadcasting-rules).
9
0
pub(crate) fn co_broadcast<D1, D2, Output>(shape1: &D1, shape2: &D2) -> Result<Output, ShapeError>
10
0
where
11
0
    D1: Dimension,
12
0
    D2: Dimension,
13
0
    Output: Dimension,
14
{
15
0
    let (k, overflow) = shape1.ndim().overflowing_sub(shape2.ndim());
16
    // Swap the order if d2 is longer.
17
0
    if overflow {
18
0
        return co_broadcast::<D2, D1, Output>(shape2, shape1);
19
0
    }
20
    // The output should be the same length as shape1.
21
0
    let mut out = Output::zeros(shape1.ndim());
22
0
    for (out, s) in izip!(out.slice_mut(), shape1.slice()) {
23
0
        *out = *s;
24
0
    }
25
0
    for (out, s2) in izip!(&mut out.slice_mut()[k..], shape2.slice()) {
26
0
        if *out != *s2 {
27
0
            if *out == 1 {
28
0
                *out = *s2
29
0
            } else if *s2 != 1 {
30
0
                return Err(from_kind(ErrorKind::IncompatibleShape));
31
0
            }
32
0
        }
33
    }
34
0
    Ok(out)
35
0
}
Unexecuted instantiation: ndarray::dimension::broadcast::co_broadcast::<ndarray::dimension::dim::Dim<[usize; 1]>, ndarray::dimension::dim::Dim<[usize; 1]>, ndarray::dimension::dim::Dim<[usize; 1]>>
Unexecuted instantiation: ndarray::dimension::broadcast::co_broadcast::<_, _, _>
36
37
/// A trait to specify when one dimension is strictly larger than another.
38
///
39
/// Broadcasting two arrays together frequently requires typing the resultant
40
/// array has having a dimensionality equal to the maximum of the two input arrays.
41
/// This trait is what determines that typing.
42
///
43
/// For example, `Ix1: DimMax<Ix0>`, but not vice-versa.
44
pub trait DimMax<Other: Dimension>
45
{
46
    /// The resulting dimension type after broadcasting.
47
    type Output: Dimension;
48
}
49
50
/// Dimensions of the same type remain unchanged when co_broadcast.
51
/// So you can directly use `D` as the resulting type.
52
/// (Instead of `<D as DimMax<D>>::BroadcastOutput`)
53
impl<D: Dimension> DimMax<D> for D
54
{
55
    type Output = D;
56
}
57
58
macro_rules! impl_broadcast_distinct_fixed {
59
    ($smaller:ty, $larger:ty) => {
60
        impl DimMax<$larger> for $smaller {
61
            type Output = $larger;
62
        }
63
64
        impl DimMax<$smaller> for $larger {
65
            type Output = $larger;
66
        }
67
    };
68
}
69
70
impl_broadcast_distinct_fixed!(Ix0, Ix1);
71
impl_broadcast_distinct_fixed!(Ix0, Ix2);
72
impl_broadcast_distinct_fixed!(Ix0, Ix3);
73
impl_broadcast_distinct_fixed!(Ix0, Ix4);
74
impl_broadcast_distinct_fixed!(Ix0, Ix5);
75
impl_broadcast_distinct_fixed!(Ix0, Ix6);
76
impl_broadcast_distinct_fixed!(Ix1, Ix2);
77
impl_broadcast_distinct_fixed!(Ix1, Ix3);
78
impl_broadcast_distinct_fixed!(Ix1, Ix4);
79
impl_broadcast_distinct_fixed!(Ix1, Ix5);
80
impl_broadcast_distinct_fixed!(Ix1, Ix6);
81
impl_broadcast_distinct_fixed!(Ix2, Ix3);
82
impl_broadcast_distinct_fixed!(Ix2, Ix4);
83
impl_broadcast_distinct_fixed!(Ix2, Ix5);
84
impl_broadcast_distinct_fixed!(Ix2, Ix6);
85
impl_broadcast_distinct_fixed!(Ix3, Ix4);
86
impl_broadcast_distinct_fixed!(Ix3, Ix5);
87
impl_broadcast_distinct_fixed!(Ix3, Ix6);
88
impl_broadcast_distinct_fixed!(Ix4, Ix5);
89
impl_broadcast_distinct_fixed!(Ix4, Ix6);
90
impl_broadcast_distinct_fixed!(Ix5, Ix6);
91
impl_broadcast_distinct_fixed!(Ix0, IxDyn);
92
impl_broadcast_distinct_fixed!(Ix1, IxDyn);
93
impl_broadcast_distinct_fixed!(Ix2, IxDyn);
94
impl_broadcast_distinct_fixed!(Ix3, IxDyn);
95
impl_broadcast_distinct_fixed!(Ix4, IxDyn);
96
impl_broadcast_distinct_fixed!(Ix5, IxDyn);
97
impl_broadcast_distinct_fixed!(Ix6, IxDyn);
98
99
#[cfg(test)]
100
#[cfg(feature = "std")]
101
mod tests
102
{
103
    use super::co_broadcast;
104
    use crate::{Dim, DimMax, Dimension, ErrorKind, Ix0, IxDynImpl, ShapeError};
105
106
    #[test]
107
    fn test_broadcast_shape()
108
    {
109
        fn test_co<D1, D2>(d1: &D1, d2: &D2, r: Result<<D1 as DimMax<D2>>::Output, ShapeError>)
110
        where
111
            D1: Dimension + DimMax<D2>,
112
            D2: Dimension,
113
        {
114
            let d = co_broadcast::<D1, D2, <D1 as DimMax<D2>>::Output>(d1, d2);
115
            assert_eq!(d, r);
116
        }
117
        test_co(&Dim([2, 3]), &Dim([4, 1, 3]), Ok(Dim([4, 2, 3])));
118
        test_co(&Dim([1, 2, 2]), &Dim([1, 3, 4]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)));
119
        test_co(&Dim([3, 4, 5]), &Ix0(), Ok(Dim([3, 4, 5])));
120
        let v = vec![1, 2, 3, 4, 5, 6, 7];
121
        test_co(&Dim(vec![1, 1, 3, 1, 5, 1, 7]), &Dim([2, 1, 4, 1, 6, 1]), Ok(Dim(IxDynImpl::from(v.as_slice()))));
122
        let d = Dim([1, 2, 1, 3]);
123
        test_co(&d, &d, Ok(d));
124
        test_co(&Dim([2, 1, 2]).into_dyn(), &Dim(0), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)));
125
        test_co(&Dim([2, 1, 1]), &Dim([0, 0, 1, 3, 4]), Ok(Dim([0, 0, 2, 3, 4])));
126
        test_co(&Dim([0]), &Dim([0, 0, 0]), Ok(Dim([0, 0, 0])));
127
        test_co(&Dim(1), &Dim([1, 0, 0]), Ok(Dim([1, 0, 0])));
128
        test_co(&Dim([1, 3, 0, 1, 1]), &Dim([1, 2, 3, 1]), Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)));
129
    }
130
}