Inconsistency in adding ArrayView and Array
rkshthrmsh opened this issue · comments
While trying to add an ArrayBase<ViewRepr<&i32>, Dim<[usize; 1]>>
and an ArrayBase<OwnedRepr<i32>, Dim<[usize; 1]>>
, in the following code, I am getting the error shown below:
fn update_weight_vec(&mut self, U: ArrayBase<ViewRepr<&i32>, Dim<[usize; 1]>>) {
let mut W_z_tAdd1 = self.W.slice(s![z, ..]) + U.to_owned(); // shapes are equivalent as seen in the error message below.
}
Error:
Compiling rust v0.1.
error[E0369]: cannot add `ArrayBase<OwnedRepr<i32>, Dim<[usize; 1]>>` to `ArrayBase<ViewRepr<&i32>, Dim<[usize; 1]>>`
--> src/main.rs:418:53
|
418 | let mut W_z_tAdd1 = self.W.slice(s![z, ..]) + U.to_owned();
| ----------------------- ^ ------------ ArrayBase<OwnedRepr<i32>, Dim<[usize; 1]>>
| |
| ArrayBase<ViewRepr<&i32>, Dim<[usize; 1]>>
However, attempting the same addition between the two Repr
types elsewhere seems to work fine.
fn main() {
let mut n = Array2::<i32>::ones((2, 4));
let m = Array1::<i32>::ones(6);
let m_sliced = m.slice(s![..4]);
let n_sliced = n.slice(s![0, ..]);
let mut s = m_sliced.to_owned() + n_sliced; // works fine
}
Could you please help me understand what is happening here?
Exploring this some more, it appears that addition between ArrayBase<ViewRepr<&i32>, Dim<[usize; 1]>>
and an ArrayBase<OwnedRepr<i32>, Dim<[usize; 1]>>
is not commutative. The following worked:
fn update_weight_vec(&mut self, U: ArrayBase<ViewRepr<&i32>, Dim<[usize; 1]>>) {
// let mut W_z_tAdd1 = self.W.slice(s![z, ..]) + U.to_owned(); causes error
let mut W_z_tAdd1 = U.to_owned() + self.W.slice(s![z, ..]); // this works
}
I don't have the exact technical answer you want, but here's some details
- You can use
ArrayView1<i32>
instead ofArrayBase<ViewRepr<&i32>, Dim<[usize; 1]>>)
. In fact,ArrayView1
is an alias to the looong type you use. - You don't have to call
to_owned
each time you want to use math operations. I know that some operations are forbidden, likeview op view
, but they can be valid if you use&view op &view
, like&U + &self.W.slice(s![z, ..])
- My understanding is that without the
&
, the array is consumed (and thus the memory is reused for the result). With the&
, ndarray understands that it needs to allocate a new array. Please read this section of the documentation for more information.
Thank you, @nilgoyette.
- Yes, I have now aliased with
ArrayView1
and the code looks much cleaner. - In this usecase,
to_owned
is necessary for Rust's borrow-checker. The code snippet here is a smaller part of another program. - Right, so it is related to memory handling. However, this behaviour still seems counter-intiutive since most users would expect addition to be commutative. Maybe this could be mentioned in the docs somewhere?
I haven't see your complete code, but you should never have to call to_owned
to do math. You can apply all math operations on a view. Simply use the &
when needed.
As for the "commutative" point. It's mentioned in the doc, right in the link I sent you
// let sum2 = view1 + &view2; // This doesn't work because `view1` is not an owned array.
IIUC, view1
, being a view, can be consumed (the struct can be), but it's memory can't be reused to store the result of the +
operation because it doesn't own the memory, so view1 + &view2
is forbidden. The first array must either be
- owned (and consumed)
- referenced (
&
) to explicitly ask ndarray to allocate (instead of reusing the memory)
I guess we could explain the reason more clearly in the doc.
Thanks for clearing it up, @nilgoyette. :)
Closing this issue now.