use crate::array::Array;
use crate::error::{NumRs2Error, Result};
use num_traits::Float;
pub fn logspace<T>(start: T, stop: T, num: usize, endpoint: bool, base: T) -> Result<Array<T>>
where
T: Float + Clone,
{
if num == 0 {
return Ok(Array::from_vec(vec![]));
}
if num == 1 {
return Ok(Array::from_vec(vec![base.powf(start)]));
}
let divisor = if endpoint {
T::from(num - 1).expect("num-1 should be convertible to T (Float type)")
} else {
T::from(num).expect("num should be convertible to T (Float type)")
};
let mut result = Vec::with_capacity(num);
for i in 0..num {
let t = T::from(i).expect("loop index i should be convertible to T (Float type)") / divisor;
let exponent = start + t * (stop - start);
result.push(base.powf(exponent));
}
Ok(Array::from_vec(result))
}
pub fn geomspace<T>(start: T, stop: T, num: usize, endpoint: bool) -> Result<Array<T>>
where
T: Float + Clone,
{
if num == 0 {
return Ok(Array::from_vec(vec![]));
}
if start.is_sign_positive() != stop.is_sign_positive() {
return Err(NumRs2Error::InvalidOperation(
"Geometric sequence cannot include zero or change sign".to_string(),
));
}
if start == T::zero() || stop == T::zero() {
return Err(NumRs2Error::InvalidOperation(
"Geometric sequence endpoints cannot be zero".to_string(),
));
}
let log_start = start.abs().ln();
let log_stop = stop.abs().ln();
let result = logspace(
log_start,
log_stop,
num,
endpoint,
T::from(std::f64::consts::E)
.expect("Euler's number E should be convertible to Float type T"),
)?;
if start.is_sign_negative() {
let result_vec: Vec<T> = result.to_vec().into_iter().map(|x| -x).collect();
Ok(Array::from_vec(result_vec))
} else {
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_logspace() {
let result = logspace(0.0, 2.0, 3, true, 10.0).expect("operation should succeed");
assert_eq!(result.shape(), vec![3]);
assert_relative_eq!(
result.get(&[0]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[1]).expect("operation should succeed"),
10.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[2]).expect("operation should succeed"),
100.0,
epsilon = 1e-10
);
let result = logspace(1.0, 4.0, 4, true, 2.0).expect("operation should succeed");
assert_eq!(result.shape(), vec![4]);
assert_relative_eq!(
result.get(&[0]).expect("operation should succeed"),
2.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[1]).expect("operation should succeed"),
4.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[2]).expect("operation should succeed"),
8.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[3]).expect("operation should succeed"),
16.0,
epsilon = 1e-10
);
let result = logspace(0.0, 2.0, 2, false, 10.0).expect("operation should succeed");
assert_eq!(result.shape(), vec![2]);
assert_relative_eq!(
result.get(&[0]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[1]).expect("operation should succeed"),
10.0,
epsilon = 1e-10
);
let result = logspace(1.0, 1.0, 1, true, 10.0).expect("operation should succeed");
assert_eq!(result.shape(), vec![1]);
assert_relative_eq!(
result.get(&[0]).expect("operation should succeed"),
10.0,
epsilon = 1e-10
);
let result = logspace(0.0, 0.0, 0, true, 10.0).expect("operation should succeed");
assert_eq!(result.shape(), vec![0]);
}
#[test]
fn test_geomspace() {
let result = geomspace(1.0, 100.0, 3, true).expect("operation should succeed");
assert_eq!(result.shape(), vec![3]);
assert_relative_eq!(
result.get(&[0]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[1]).expect("operation should succeed"),
10.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[2]).expect("operation should succeed"),
100.0,
epsilon = 1e-10
);
let result = geomspace(1.0, 81.0, 5, true).expect("operation should succeed");
assert_eq!(result.shape(), vec![5]);
assert_relative_eq!(
result.get(&[0]).expect("operation should succeed"),
1.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[1]).expect("operation should succeed"),
3.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[2]).expect("operation should succeed"),
9.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[3]).expect("operation should succeed"),
27.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[4]).expect("operation should succeed"),
81.0,
epsilon = 1e-10
);
let result = geomspace(-1.0, -100.0, 3, true).expect("operation should succeed");
assert_eq!(result.shape(), vec![3]);
assert_relative_eq!(
result.get(&[0]).expect("operation should succeed"),
-1.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[1]).expect("operation should succeed"),
-10.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.get(&[2]).expect("operation should succeed"),
-100.0,
epsilon = 1e-10
);
assert!(geomspace(1.0, -1.0, 3, true).is_err()); assert!(geomspace(0.0, 1.0, 3, true).is_err()); assert!(geomspace(1.0, 0.0, 3, true).is_err()); }
}