|
| 1 | +#[macro_use] |
| 2 | +extern crate ndarray; |
| 3 | + |
| 4 | +use ndarray::prelude::*; |
| 5 | + |
| 6 | +fn main() { |
| 7 | + |
| 8 | + //Creating an array of 3s |
| 9 | + let mut a = Array::from_elem((5,2), 3.0); |
| 10 | + //Here's just a simple example showing just one way to change a slice of our data. |
| 11 | + //I'm just assigning a new value to each set of our slice, but we could also apply a |
| 12 | + //scalar function to all of the values of the slice as well. |
| 13 | + a.slice_mut(s![..,0]).mapv_inplace(|_x| 1.0); |
| 14 | + //We can see here that the values did change. |
| 15 | + println!("\nA\n{:?}", a); |
| 16 | + |
| 17 | + //If we want to work on a set of data that meets a logical set of conditions we have a couple of options available. |
| 18 | + //We could loop through the data by using a mutable iterator in conjunction with a filter. |
| 19 | + for i in a.iter_mut().filter(|x| **x < 3.0){ |
| 20 | + *i = 4.0; |
| 21 | + } |
| 22 | + //If we want to avoid the for loop we could also do the following: |
| 23 | + a.mapv_inplace(|x| {if x < 3.0 {4.0}else{x}}); |
| 24 | + |
| 25 | + //We can now print our data and see that the values were modified based upon our logical condition. |
| 26 | + println!("\nA\n{:?}", a); |
| 27 | + |
| 28 | + //Let's do a rotation example next using Bunge angles and then a simple passive rotation of our |
| 29 | + //coordinate system. The difference between a passive and active rotation can pretty much come |
| 30 | + //down to whether we want to rotate our coordinate system or simply the body itself. If we |
| 31 | + //are rotating the body then it's an active rotation. If we are rotating the coordinate |
| 32 | + //system it's a passive rotation. Also, the active and passive rotation matrices differ by a simple |
| 33 | + //transpose operation on the rotation matrix. |
| 34 | + |
| 35 | + //We're going to be going row by row here, so it makes sense to keep the standard |
| 36 | + //row memory stride setup |
| 37 | + let bunge = Array2::<f64>::from_elem((3, 4), 1.0); |
| 38 | + |
| 39 | + let s1:Array1<f64> = bunge.slice(s![0, ..]).iter().map(|&rho1| f64::sin(rho1)).collect(); |
| 40 | + let c1:Array1<f64> = bunge.slice(s![0, ..]).iter().map(|&rho1| f64::cos(rho1)).collect(); |
| 41 | + |
| 42 | + let s2:Array1<f64> = bunge.slice(s![1, ..]).iter().map(|&phi1| f64::sin(phi1)).collect(); |
| 43 | + let c2:Array1<f64> = bunge.slice(s![1, ..]).iter().map(|&phi1| f64::cos(phi1)).collect(); |
| 44 | + |
| 45 | + let s3:Array1<f64> = bunge.slice(s![2, ..]).iter().map(|&rho2| f64::sin(rho2)).collect(); |
| 46 | + let c3:Array1<f64> = bunge.slice(s![2, ..]).iter().map(|&rho2| f64::cos(rho2)).collect(); |
| 47 | + |
| 48 | + let nelems = bunge.len_of(Axis(1)); |
| 49 | + //We're going to make this a column memory stride setup, since we'll be using the |
| 50 | + //first two dimensions the most often. |
| 51 | + let mut rmat = Array3::<f64>::zeros((3, 3, nelems).set_f(true)); |
| 52 | + |
| 53 | + //We could also do this using iterators like seen above. However, we would be taking |
| 54 | + //a hit due to the fact that we aren't striding over memory instead of operating on |
| 55 | + //consecutive memory. |
| 56 | + // |
| 57 | + //Also, if we'd wanted to we could have also have just calculated the necessary sines and |
| 58 | + //cosines in this loop instead of doing it all at once like seen above. |
| 59 | + //However if we'd gone that route, then we'd would want to change the bunge array so that it was |
| 60 | + //using column strides for its memory layout. |
| 61 | + for i in 0..nelems{ |
| 62 | + |
| 63 | + rmat[(0, 0, i)] = c1[i] * c3[i] - s1[i] * s3[i] * c2[i]; |
| 64 | + rmat[(0, 1, i)] = -c1[i] * s3[i] - s1[i] * c2[i] * c3[i]; |
| 65 | + rmat[(0, 2, i)] = s1[i] * s2[i]; |
| 66 | + |
| 67 | + rmat[(1, 0, i)] = s1[i] * c3[i] + c1[i] * c2[i] * s3[i]; |
| 68 | + rmat[(1, 1, i)] = -s1[i] * s3[i] + c1[i] * c2[i] * c3[i]; |
| 69 | + rmat[(1, 2, i)] = -c1[i] * s2[i]; |
| 70 | + |
| 71 | + rmat[(2, 0, i)] = s2[i] * s3[i]; |
| 72 | + rmat[(2, 1, i)] = s2[i] * c3[i]; |
| 73 | + rmat[(2, 2, i)] = c2[i]; |
| 74 | + |
| 75 | + } |
| 76 | + |
| 77 | + //Here we're just seeing what our rmat initially looks like. |
| 78 | + //We'll have another example down below that should be identical to this value. |
| 79 | + println!("\nrmat\n{:?}", rmat.slice(s![.., .., 0])); |
| 80 | + |
| 81 | + let eye2d = Array2::<f64>::eye(3); |
| 82 | + |
| 83 | + let mut mat_rot = Array3::<f64>::zeros((3, 3, nelems).set_f(true)); |
| 84 | + |
| 85 | + let mut crd_sys_rot = Array3::<f64>::zeros((3, 3, nelems).set_f(true)); |
| 86 | + |
| 87 | + //The below shows two examples: |
| 88 | + //The first example mat_rot shows how to apply a rotation to a tensor |
| 89 | + //In our example this will just return the identity matrix because R*I*R^T = R*R^T = I |
| 90 | + //The second example crd_sys_rot shows how to apply a rotatation to a coordinate |
| 91 | + //system or even a series of vectors. |
| 92 | + for i in 0..nelems{ |
| 93 | + mat_rot.slice_mut(s![.., .., i]).assign({ |
| 94 | + &rmat.slice(s![.., .., i]).dot({ |
| 95 | + &eye2d.dot(&rmat.slice(s![.., .., i]).t()) |
| 96 | + }) |
| 97 | + }); |
| 98 | + //Since we are just multiplying by identity here our |
| 99 | + //coordinate system is just equal to our rotation matrix |
| 100 | + crd_sys_rot.slice_mut(s![.., .., i]).assign({ |
| 101 | + &rmat.slice(s![.., .., i]).dot(&eye2d) |
| 102 | + }); |
| 103 | + |
| 104 | + } |
| 105 | + |
| 106 | + //Here we're going to just print off the first tensor/matrix and coordinate system |
| 107 | + //corresponding to our n_element 3D array of matrices and coordinate systems. |
| 108 | + //The rest of the tensors and coordinate systems will be exactly the same. |
| 109 | + println!("\ncrd_sys_rot\n{:?}", crd_sys_rot.slice(s![.., .., 0])); |
| 110 | + println!("\nmat_rot\n{:?}", mat_rot.slice(s![.., .., 0])); |
| 111 | + |
| 112 | +} |
0 commit comments