Skip to content

Commit e12e809

Browse files
authored
Update ndarray to 0.16 and ndarray-rand to 0.15 (#7358)
<!-- Open the PR up as a draft until you feel it is ready for a proper review. Do not make PR:s from your own `main` branch, as that makes it difficult for reviewers to add their own fixes. Add any improvements to the branch as new commits to make it easier for reviewers to follow the progress. All commits will be squashed to a single commit once the PR is merged into `main`. Make sure you mention any issues that this PR closes in the description, as well as any other related issues. To get an auto-generated PR description you can put "copilot:summary" or "copilot:walkthrough" anywhere. --> ### What Fixes #7157. I also updated ndarray-rand (sorry if that should be separate - it didn't appear to require any changes). into_raw_vec() is now into_raw_vec_and_offset().0 (the offset is ignored) I wasn't sure whether an order (`RowMajor` vs. `ColumnMajor`) should be specified in `into_shape_with_order()`. I ran through all the examples and couldn't find one that actually used this code, so guidance here is appreciated. ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7358?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7358?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! * [x] If have noted any breaking changes to the log API in `CHANGELOG.md` and the migration guide - [PR Build Summary](https://build.rerun.io/pr/7358) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) To run all checks from `main`, comment on the PR with `@rerun-bot full-check`.
1 parent 81d3e0b commit e12e809

File tree

7 files changed

+144
-28
lines changed

7 files changed

+144
-28
lines changed

Cargo.lock

+17-6
Original file line numberDiff line numberDiff line change
@@ -3529,22 +3529,24 @@ checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c"
35293529

35303530
[[package]]
35313531
name = "ndarray"
3532-
version = "0.15.6"
3532+
version = "0.16.1"
35333533
source = "registry+https://github.com/rust-lang/crates.io-index"
3534-
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
3534+
checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
35353535
dependencies = [
35363536
"matrixmultiply",
35373537
"num-complex",
35383538
"num-integer",
35393539
"num-traits",
3540+
"portable-atomic",
3541+
"portable-atomic-util",
35403542
"rawpointer",
35413543
]
35423544

35433545
[[package]]
35443546
name = "ndarray-rand"
3545-
version = "0.14.0"
3547+
version = "0.15.0"
35463548
source = "registry+https://github.com/rust-lang/crates.io-index"
3547-
checksum = "65608f937acc725f5b164dcf40f4f0bc5d67dc268ab8a649d3002606718c4588"
3549+
checksum = "f093b3db6fd194718dcdeea6bd8c829417deae904e3fcc7732dabcd4416d25d8"
35483550
dependencies = [
35493551
"ndarray",
35503552
"rand",
@@ -4348,9 +4350,18 @@ checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
43484350

43494351
[[package]]
43504352
name = "portable-atomic"
4351-
version = "1.5.1"
4353+
version = "1.7.0"
4354+
source = "registry+https://github.com/rust-lang/crates.io-index"
4355+
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
4356+
4357+
[[package]]
4358+
name = "portable-atomic-util"
4359+
version = "0.2.2"
43524360
source = "registry+https://github.com/rust-lang/crates.io-index"
4353-
checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b"
4361+
checksum = "fcdd8420072e66d54a407b3316991fe946ce3ab1083a7f575b2463866624704d"
4362+
dependencies = [
4363+
"portable-atomic",
4364+
]
43544365

43554366
[[package]]
43564367
name = "powerfmt"

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ mime_guess2 = "2.0" # infer MIME type by file extension, and map mime to file ex
198198
mint = "0.5.9"
199199
mp4 = "0.14.0"
200200
natord = "1.0.9"
201-
ndarray = "0.15"
202-
ndarray-rand = "0.14"
201+
ndarray = "0.16"
202+
ndarray-rand = "0.15"
203203
never = "0.1"
204204
nohash-hasher = "0.2"
205205
notify = "6.0"

crates/store/re_types/src/archetypes/image.rs

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/store/re_types/src/datatypes/tensor_data_ext.rs

+30-16
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ use crate::archetypes::EncodedImage;
88

99
use super::{TensorBuffer, TensorData, TensorDimension};
1010

11-
// Much of the following duplicates code from: `crates/re_components/src/tensor.rs`, which
12-
// will eventually go away as the Tensor migration is completed.
13-
1411
// ----------------------------------------------------------------------------
1512

1613
impl TensorData {
@@ -169,7 +166,6 @@ macro_rules! tensor_from_ndarray {
169166
type Error = TensorCastError;
170167

171168
fn try_from(value: ndarray::Array<$type, D>) -> Result<Self, Self::Error> {
172-
let value = value.as_standard_layout();
173169
let shape = value
174170
.shape()
175171
.iter()
@@ -178,13 +174,26 @@ macro_rules! tensor_from_ndarray {
178174
name: None,
179175
})
180176
.collect();
181-
value
182-
.is_standard_layout()
183-
.then(|| TensorData {
184-
shape,
185-
buffer: TensorBuffer::$variant(value.to_owned().into_raw_vec().into()),
186-
})
187-
.ok_or(TensorCastError::NotContiguousStdOrder)
177+
178+
let vec = if value.is_standard_layout() {
179+
let (mut vec, offset) = value.into_raw_vec_and_offset();
180+
// into_raw_vec_and_offset() guarantees that the logical element order (.iter()) matches the internal
181+
// storage order in the returned vector if the array is in standard layout.
182+
if let Some(offset) = offset {
183+
vec.drain(..offset);
184+
vec
185+
} else {
186+
debug_assert!(vec.is_empty());
187+
vec
188+
}
189+
} else {
190+
value.into_iter().collect::<Vec<_>>()
191+
};
192+
193+
Ok(Self {
194+
shape,
195+
buffer: TensorBuffer::$variant(vec.into()),
196+
})
188197
}
189198
}
190199

@@ -311,13 +320,18 @@ impl<D: ::ndarray::Dimension> TryFrom<::ndarray::Array<half::f16, D>> for Tensor
311320
})
312321
.collect();
313322
if value.is_standard_layout() {
323+
let (vec, offset) = value.into_raw_vec_and_offset();
324+
// into_raw_vec_and_offset() guarantees that the logical element order (.iter()) matches the internal
325+
// storage order in the returned vector if the array is in standard layout.
326+
let vec_slice = if let Some(offset) = offset {
327+
&vec[offset..]
328+
} else {
329+
debug_assert!(vec.is_empty());
330+
&vec
331+
};
314332
Ok(Self {
315333
shape,
316-
buffer: TensorBuffer::F16(
317-
bytemuck::cast_slice(value.into_raw_vec().as_slice())
318-
.to_vec()
319-
.into(),
320-
),
334+
buffer: TensorBuffer::F16(Vec::from(bytemuck::cast_slice(vec_slice)).into()),
321335
})
322336
} else {
323337
Ok(Self {

crates/store/re_types/tests/types/tensor.rs

+92-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fn convert_tensor_to_ndarray_f32() {
106106
}
107107

108108
#[test]
109-
fn convert_ndarray_u8_to_tensor() {
109+
fn convert_ndarray_f64_to_tensor() {
110110
let n = ndarray::array![[1., 2., 3.], [4., 5., 6.]];
111111
let t = TensorData::try_from(n).unwrap();
112112

@@ -125,6 +125,97 @@ fn convert_ndarray_slice_to_tensor() {
125125
assert_eq!(t.shape(), &[TensorDimension::unnamed(2)]);
126126
}
127127

128+
#[test]
129+
fn convert_ndarray_to_tensor_both_layouts() {
130+
#[rustfmt::skip]
131+
let row_major_vec = vec![
132+
1, 2, 3,
133+
4, 5, 6,
134+
7, 8, 9
135+
];
136+
#[rustfmt::skip]
137+
let col_major_vec = vec![
138+
1, 4, 7,
139+
2, 5, 8,
140+
3, 6, 9
141+
];
142+
143+
let shape = ndarray::Ix2(3, 3);
144+
145+
let row_major = ndarray::Array::from_vec(row_major_vec)
146+
.into_shape_with_order((shape, ndarray::Order::RowMajor))
147+
.unwrap();
148+
149+
let col_major = ndarray::Array::from_vec(col_major_vec)
150+
.into_shape_with_order((shape, ndarray::Order::ColumnMajor))
151+
.unwrap();
152+
153+
assert!(row_major.is_standard_layout());
154+
assert!(!col_major.is_standard_layout());
155+
156+
// make sure that the offset is in fact zero, in case ndarray behavior changes
157+
let rm = row_major.clone();
158+
let cm = col_major.clone();
159+
let (_, rm_offset) = rm.into_raw_vec_and_offset();
160+
let (_, cm_offset) = cm.into_raw_vec_and_offset();
161+
assert_eq!(rm_offset.unwrap(), 0);
162+
assert_eq!(cm_offset.unwrap(), 0);
163+
164+
let tensor_row_major = TensorData::try_from(row_major).unwrap();
165+
let tensor_col_major = TensorData::try_from(col_major).unwrap();
166+
167+
assert_eq!(tensor_row_major, tensor_col_major);
168+
}
169+
170+
#[test]
171+
fn convert_ndarray_to_tensor_both_layouts_nonzero_offset() {
172+
#[rustfmt::skip]
173+
let row_major_vec = vec![
174+
1, 2, 3,
175+
4, 5, 6,
176+
7, 8, 9
177+
];
178+
#[rustfmt::skip]
179+
let col_major_vec = vec![
180+
1, 4, 7,
181+
2, 5, 8,
182+
3, 6, 9
183+
];
184+
185+
let shape = ndarray::Ix2(3, 3);
186+
187+
let row_major = ndarray::Array::from_vec(row_major_vec)
188+
.into_shape_with_order((shape, ndarray::Order::RowMajor))
189+
.unwrap();
190+
assert!(row_major.is_standard_layout());
191+
let row_major_nonzero_offset = row_major.slice_move(ndarray::s![1.., ..]);
192+
193+
let col_major = ndarray::Array::from_vec(col_major_vec)
194+
.into_shape_with_order((shape, ndarray::Order::ColumnMajor))
195+
.unwrap();
196+
assert!(!col_major.is_standard_layout());
197+
let col_major_nonzero_offset = col_major.slice_move(ndarray::s![1.., ..]);
198+
199+
assert!(row_major_nonzero_offset.is_standard_layout());
200+
assert!(!col_major_nonzero_offset.is_standard_layout());
201+
202+
// make sure that the offset is in fact non-zero, in case ndarray behavior changes
203+
let rmno = row_major_nonzero_offset.clone();
204+
let cmno = col_major_nonzero_offset.clone();
205+
let (_, rm_offset) = rmno.into_raw_vec_and_offset();
206+
let (_, cm_offset) = cmno.into_raw_vec_and_offset();
207+
assert!(rm_offset.unwrap() > 0);
208+
assert!(cm_offset.unwrap() > 0);
209+
210+
let tensor_row_major_nonzero_offset = TensorData::try_from(row_major_nonzero_offset).unwrap();
211+
let tensor_col_major_nonzero_offset = TensorData::try_from(col_major_nonzero_offset).unwrap();
212+
213+
assert_eq!(
214+
tensor_row_major_nonzero_offset,
215+
tensor_col_major_nonzero_offset
216+
);
217+
}
218+
128219
#[test]
129220
fn check_slices() {
130221
let t = TensorData::new(

crates/viewer/re_space_view_tensor/src/space_view_class.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ pub fn selected_tensor_slice<'a, T: Copy>(
424424
// This is important for above width/height conversion to work since this assumes at least 2 dimensions.
425425
tensor
426426
.view()
427-
.into_shape(ndarray::IxDyn(&[tensor.len(), 1]))
427+
.into_shape_with_order(ndarray::IxDyn(&[tensor.len(), 1]))
428428
.unwrap()
429429
} else {
430430
tensor.view()

docs/snippets/all/archetypes/image_send_columns.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
3131

3232
// Split up the image data into several components referencing the underlying data.
3333
let image_size_in_bytes = width * height * 3;
34-
let blob = rerun::datatypes::Blob::from(images.into_raw_vec());
34+
let blob = rerun::datatypes::Blob::from(images.into_raw_vec_and_offset().0);
3535
let image_column = times
3636
.iter()
3737
.map(|&t| {

0 commit comments

Comments
 (0)