Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Rust ndarray equivalent for numpy arithmetic on a slice?

The following Python code will repeatedly add the vector [1, 2, 3, 4] to each row in the two-dimensional array a, starting only from the 20th row.

import numpy as np

# an array of shape (100, 4)
a = np.zeros((100, 4), dtype=np.float32)

# and this is the operation explained above
a[20:, :] += [1.0, 2.0, 3.0, 0.0]

Is there a simple equivalent of this with ndarray? I can already do what I need with more complicated messy looking code but feel there is probably a tidy ndarray.rs equivalent.

OK so, at the risk of over complicating a question I thought might have a simple answer that I just couldn't unearth...

I am using arrays of f32 shape (n, 8) representing three vertex locations, three normal components and two texture mapping coordinates. I am merging buffers from multiple 3D objects into one for more efficient graphics rendering. Within the 8 wide array the first three values need to be scaled i.e. multiplied by &[sx, sy, sz] then rotated using a standard rz.dot(&rx.dot(&ry.dot())) function and finally have a displacement &[dx, dy, dz] added. The normals just need to be rotated. My current system involves holding data in intermediate array variables.

use ndarray as nd;
array_buffer: nd::Array2<f32>, loc: &[f32; 3], scl: &[f32; 3]...
...
// scale then rotate new verts then add displacement
let new_verts = &new_buf.array_buffer.slice(s![.., 0..3]) * &nd::arr1(scl);
let new_verts = rotate_vec(rot, &new_verts) + &nd::arr1(loc);
// then add them to existing verts
let mut verts = nd::stack(nd::Axis(0),
                &[old_buf.array_buffer.slice(s![.., 0..3]),
                  new_verts.view()]).unwrap();
...

I know I won't be able to reduce it to the numpy one liner

verts = np.append(old_buf.array_buffer[:,0:3], 
        rotate_vec(rot, (new_buf.array_buffer[:,0:3] * scl) + loc))

but I thought that maybe some of the map or zip variants or macros might help me.

like image 359
paddyg Avatar asked May 17 '18 21:05

paddyg


1 Answers

That can be done with the same two steps as those performed in Python: slicing, then add-assigning to a broadcast right-handed array.

use ndarray::Array2;

let mut a: Array2<f32> = Array2::zeros((100, 4));
{ 
    let mut slice = a.slice_mut(s![20.., ..]);
    slice += &ArrayView::from(&[1.0, 2.0, 3.0, 4.0]);
}

Slicing is done with slice_mut and the s! macro for defining the intended range. The outcome of a slice is a mutable array view, and so, most operations seen in ArrayBase are available, including arithmetic operations. By broadcasting rules, a right-handed array of shape [4] can be automatically broadcast to one of shape [100, 4] for the += operator.


In case of other confusions in the transition from Python to Rust's ndarray crate, the documentation contains a guide for Python users.

like image 167
E_net4 stands with Ukraine Avatar answered Nov 18 '22 13:11

E_net4 stands with Ukraine