Hello! I have a test that I'm running whose outcome appears to be affected by how I construct the input. Here's the short story:
- I construct a linearly-spaced
ndarrayin two different ways: Construction A and Construction B - These result in the exact same values
- I have a previously-existing test that works on Construction A, but not on Construction B
- What's worse, simply asserting that the two constructions are equal in value causes the original test to start failing on Construction A.
Here's a main.rs to demonstrate:
Rust Code
use approx::assert_abs_diff_eq;
use ndarray::{Array, Array2, Ixs, s};
/// Two equivalent methods of doing `linspace`.
/// We show here that the two methods yield _identical_ results.
#[inline(never)]
fn equal_linspace() {
let a = Array::linspace(0.5, 2., 128)
.into_shape_with_order((8, 16))
.unwrap();
let step = (2. - 0.5) / 127.;
let a_other = Array::from_iter((0..128).map(|i| 0.5 + step * (i as f32)))
.into_shape_with_order((8, 16))
.unwrap();
// Equality according to both ndarray and Rust slices
assert_eq!(a, a_other);
assert_eq!(a.as_slice().unwrap(), a_other.as_slice().unwrap());
}
/// The original `product` test from `oper.rs`.
/// As expected, runs without issue.
#[inline(never)]
fn product() {
let a = Array::linspace(0.5, 2., 128)
.into_shape_with_order((8, 16))
.unwrap();
assert_abs_diff_eq!(a.fold(1., |acc, &x| acc * x), a.product(), epsilon = 1e-5);
// test different strides
let max = 8 as Ixs;
for i in 1..max {
for j in 1..max {
let a1 = a.slice(s![..;i, ..;j]);
let mut prod = 1.;
for elt in a1.iter() {
prod *= *elt;
}
assert_abs_diff_eq!(a1.fold(1., |acc, &x| acc * x), prod, epsilon = 1e-5);
assert_abs_diff_eq!(prod, a1.product(), epsilon = 1e-5);
}
}
}
/// The product test, now with an additional, EQUIVALENT array `a_other`.
///
/// 1. We define `a` the same as before.
/// 2. We define `a_other` using a different method, but prior tests show us it's equivalent.
/// 3. We then run the test as before.
/// 4. Adding a simple `assert_eq` between the two arrays causes the test to fail..
#[inline(never)]
fn bad_product() {
let a = Array::linspace(0.5, 2., 128)
.into_shape_with_order((8, 16))
.unwrap();
let step = (2. - 0.5) / 127.;
let a_other = Array::from_iter((0..128).map(|i| 0.5 + step * (i as f32)))
.into_shape_with_order((8, 16))
.unwrap();
// UNCOMMENTING THIS LINE WILL CAUSE THE TEST TO FAIL.
// THIS IS NOT THE LINE THAT CAUSES THE FAILURE.
// IT WILL FAIL AT THE FIRST assert_abs_diff_eq IN THE LOOP ON i = 1, j = 2.
assert_eq!(a, a_other);
assert_abs_diff_eq!(a.fold(1., |acc, &x| acc * x), a.product(), epsilon = 1e-5);
// test different strides
let max = 8 as Ixs;
for i in 1..max {
for j in 1..max {
let a1 = a.slice(s![..;i, ..;j]);
let mut prod = 1.;
for elt in a1.iter() {
prod *= *elt;
}
eprintln!("{i}, {j}");
assert_abs_diff_eq!(a1.fold(1., |acc, &x| acc * x), prod, epsilon = 1e-5);
assert_abs_diff_eq!(prod, a1.product(), epsilon = 1e-5);
}
}
}
fn impl_test(a: &Array2<f32>) {
assert_abs_diff_eq!(a.fold(1., |acc, &x| acc * x), a.product(), epsilon = 1e-5);
// test different strides
let max = 8 as Ixs;
for i in 1..max {
for j in 1..max {
let a1 = a.slice(s![..;i, ..;j]);
let mut prod = 1.;
for elt in a1.iter() {
prod *= *elt;
}
eprintln!("{i}, {j}");
assert_abs_diff_eq!(a1.fold(1., |acc, &x| acc * x), prod, epsilon = 1e-5);
assert_abs_diff_eq!(prod, a1.product(), epsilon = 1e-5);
}
}
}
fn main() {
equal_linspace();
product();
bad_product();
}
I'm really stumped by what could be causing this. My best guess right now is some sort of difference in compiler optimizations? Any help would be greatly appreciated.