use crate::array::Array;
use crate::error::{NumRs2Error, Result};
use num_traits::Float;
pub fn meshgrid<T>(xi: &[&Array<T>], indexing: &str, sparse: bool) -> Result<Vec<Array<T>>>
where
T: Clone + num_traits::Zero + num_traits::One,
{
if xi.is_empty() {
return Ok(vec![]);
}
let use_matrix_indexing = match indexing {
"ij" => true,
"xy" => false,
_ => {
return Err(NumRs2Error::InvalidOperation(format!(
"Invalid indexing '{}'. Use 'xy' or 'ij'",
indexing
)))
}
};
let ndim = xi.len();
let mut shape: Vec<usize> = xi.iter().map(|arr| arr.shape()[0]).collect();
if !use_matrix_indexing && ndim >= 2 {
shape.swap(0, 1);
}
let mut grids = Vec::with_capacity(ndim);
if sparse {
for (axis_idx, &arr) in xi.iter().enumerate() {
let mut grid_shape = vec![1; ndim];
if use_matrix_indexing {
grid_shape[axis_idx] = arr.shape()[0];
} else {
if ndim >= 2 {
if axis_idx == 0 {
grid_shape[1] = arr.shape()[0];
} else if axis_idx == 1 {
grid_shape[0] = arr.shape()[0];
} else {
grid_shape[axis_idx] = arr.shape()[0];
}
} else {
grid_shape[axis_idx] = arr.shape()[0];
}
}
let grid = arr.clone().reshape(&grid_shape);
grids.push(grid);
}
} else {
for (axis_idx, &arr) in xi.iter().enumerate() {
let mut grid = Array::zeros(&shape);
let arr_data = arr.to_vec();
let total_elements: usize = shape.iter().product();
let mut indices = vec![0; ndim];
for linear_idx in 0..total_elements {
let mut temp = linear_idx;
for i in (0..ndim).rev() {
indices[i] = temp % shape[i];
temp /= shape[i];
}
let src_idx = if !use_matrix_indexing && ndim >= 2 {
if axis_idx == 0 {
indices[1]
} else if axis_idx == 1 {
indices[0]
} else {
indices[axis_idx]
}
} else {
indices[axis_idx]
};
grid.set(&indices, arr_data[src_idx].clone())?;
}
grids.push(grid);
}
}
Ok(grids)
}
pub fn mgrid<T>(slices: &[(T, T, T)]) -> Result<Vec<Array<T>>>
where
T: Float + Clone + PartialOrd + num_traits::FromPrimitive + 'static,
{
use crate::array::Array;
use crate::math::linspace;
if slices.is_empty() {
return Ok(vec![]);
}
let mut coord_arrays = Vec::with_capacity(slices.len());
for &(start, stop, num_or_step) in slices {
let arr = if num_or_step > T::one() && num_or_step == num_or_step.floor() {
let num = num_or_step.to_usize().ok_or_else(|| {
NumRs2Error::InvalidOperation("Cannot convert num to usize".to_string())
})?;
linspace(start, stop, num)
} else {
let step = num_or_step;
if step == T::zero() {
return Err(NumRs2Error::InvalidOperation(
"Step size cannot be zero".to_string(),
));
}
let mut points = Vec::new();
let mut current = start;
if step > T::zero() {
while current <= stop {
points.push(current);
current = current + step;
}
} else if step < T::zero() {
while current >= stop {
points.push(current);
current = current + step;
}
}
Array::from_vec(points)
};
coord_arrays.push(arr);
}
meshgrid(&coord_arrays.iter().collect::<Vec<_>>(), "ij", false)
}
pub fn ogrid<T>(slices: &[(T, T, T)]) -> Result<Vec<Array<T>>>
where
T: Float + Clone + PartialOrd + num_traits::FromPrimitive + 'static,
{
use crate::array::Array;
use crate::math::linspace;
if slices.is_empty() {
return Ok(vec![]);
}
let mut coord_arrays = Vec::with_capacity(slices.len());
for &(start, stop, num_or_step) in slices {
let arr = if num_or_step > T::one() && num_or_step == num_or_step.floor() {
let num = num_or_step.to_usize().ok_or_else(|| {
NumRs2Error::InvalidOperation("Cannot convert num to usize".to_string())
})?;
linspace(start, stop, num)
} else {
let step = num_or_step;
if step == T::zero() {
return Err(NumRs2Error::InvalidOperation(
"Step size cannot be zero".to_string(),
));
}
let mut points = Vec::new();
let mut current = start;
if step > T::zero() {
while current <= stop {
points.push(current);
current = current + step;
}
} else if step < T::zero() {
while current >= stop {
points.push(current);
current = current + step;
}
}
Array::from_vec(points)
};
coord_arrays.push(arr);
}
meshgrid(&coord_arrays.iter().collect::<Vec<_>>(), "ij", true)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_meshgrid() {
let x = Array::from_vec(vec![1.0, 2.0, 3.0]);
let y = Array::from_vec(vec![4.0, 5.0]);
let grids = meshgrid(&[&x, &y], "xy", false).expect("operation should succeed");
assert_eq!(grids.len(), 2);
let xx = &grids[0];
let yy = &grids[1];
assert_eq!(xx.shape(), vec![2, 3]); assert_eq!(yy.shape(), vec![2, 3]);
assert_eq!(xx.get(&[0, 0]).expect("operation should succeed"), 1.0);
assert_eq!(xx.get(&[0, 1]).expect("operation should succeed"), 2.0);
assert_eq!(xx.get(&[0, 2]).expect("operation should succeed"), 3.0);
assert_eq!(xx.get(&[1, 0]).expect("operation should succeed"), 1.0);
assert_eq!(xx.get(&[1, 1]).expect("operation should succeed"), 2.0);
assert_eq!(xx.get(&[1, 2]).expect("operation should succeed"), 3.0);
assert_eq!(yy.get(&[0, 0]).expect("operation should succeed"), 4.0);
assert_eq!(yy.get(&[0, 1]).expect("operation should succeed"), 4.0);
assert_eq!(yy.get(&[0, 2]).expect("operation should succeed"), 4.0);
assert_eq!(yy.get(&[1, 0]).expect("operation should succeed"), 5.0);
assert_eq!(yy.get(&[1, 1]).expect("operation should succeed"), 5.0);
assert_eq!(yy.get(&[1, 2]).expect("operation should succeed"), 5.0);
let grids_ij = meshgrid(&[&x, &y], "ij", false).expect("operation should succeed");
let xx_ij = &grids_ij[0];
let yy_ij = &grids_ij[1];
assert_eq!(xx_ij.shape(), vec![3, 2]); assert_eq!(yy_ij.shape(), vec![3, 2]);
let sparse_grids = meshgrid(&[&x, &y], "xy", true).expect("operation should succeed");
assert_eq!(sparse_grids.len(), 2);
assert_eq!(sparse_grids[0].shape(), vec![1, 3]);
assert_eq!(sparse_grids[1].shape(), vec![2, 1]);
}
#[test]
fn test_mgrid() {
use approx::assert_relative_eq;
let grids = mgrid(&[(0.0, 1.0, 3.0), (0.0, 2.0, 3.0)]).expect("operation should succeed");
assert_eq!(grids.len(), 2);
assert_eq!(grids[0].shape(), vec![3, 3]);
assert_eq!(grids[1].shape(), vec![3, 3]);
assert_relative_eq!(
grids[0].get(&[0, 0]).expect("operation should succeed"),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[0].get(&[0, 1]).expect("operation should succeed"),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[0].get(&[0, 2]).expect("operation should succeed"),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[0].get(&[1, 0]).expect("operation should succeed"),
0.5,
epsilon = 1e-10
);
assert_relative_eq!(
grids[0].get(&[2, 0]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[0, 0]).expect("operation should succeed"),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[0, 1]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[0, 2]).expect("operation should succeed"),
2.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[1, 0]).expect("operation should succeed"),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[2, 2]).expect("operation should succeed"),
2.0,
epsilon = 1e-10
);
let grids_step =
mgrid(&[(0.0, 1.0, 0.5), (0.0, 2.0, 1.0)]).expect("operation should succeed");
assert_eq!(grids_step.len(), 2);
assert_eq!(grids_step[0].shape(), vec![3, 3]);
assert_eq!(grids_step[1].shape(), vec![3, 3]);
let grids_1d = mgrid(&[(0.0, 2.0, 5.0)]).expect("operation should succeed");
assert_eq!(grids_1d.len(), 1);
assert_eq!(grids_1d[0].shape(), vec![5]);
let grids_empty = mgrid::<f64>(&[]).expect("operation should succeed");
assert_eq!(grids_empty.len(), 0);
}
#[test]
fn test_ogrid() {
use approx::assert_relative_eq;
let grids = ogrid(&[(0.0, 1.0, 3.0), (0.0, 2.0, 3.0)]).expect("operation should succeed");
assert_eq!(grids.len(), 2);
assert_eq!(grids[0].shape(), vec![3, 1]); assert_eq!(grids[1].shape(), vec![1, 3]);
assert_relative_eq!(
grids[0].get(&[0, 0]).expect("operation should succeed"),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[0].get(&[1, 0]).expect("operation should succeed"),
0.5,
epsilon = 1e-10
);
assert_relative_eq!(
grids[0].get(&[2, 0]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[0, 0]).expect("operation should succeed"),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[0, 1]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
grids[1].get(&[0, 2]).expect("operation should succeed"),
2.0,
epsilon = 1e-10
);
let grids_3d = ogrid(&[(0.0, 1.0, 2.0), (0.0, 1.0, 2.0), (0.0, 1.0, 2.0)])
.expect("operation should succeed");
assert_eq!(grids_3d.len(), 3);
assert_eq!(grids_3d[0].shape(), vec![2, 1, 1]);
assert_eq!(grids_3d[1].shape(), vec![1, 2, 1]);
assert_eq!(grids_3d[2].shape(), vec![1, 1, 2]);
let grids_step =
ogrid(&[(0.0, 1.0, 0.5), (0.0, 2.0, 1.0)]).expect("operation should succeed");
assert_eq!(grids_step.len(), 2);
assert_eq!(grids_step[0].shape(), vec![3, 1]);
assert_eq!(grids_step[1].shape(), vec![1, 3]);
}
}