Skip to content

Commit fc170c4

Browse files
committed
TST - add min / max / spread round trip tests
Add tests for arraywriters, and for analyze
1 parent 903bb3d commit fc170c4

File tree

2 files changed

+121
-2
lines changed

2 files changed

+121
-2
lines changed

nibabel/tests/test_arraywriters.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ def test_writer_maker():
350350
assert_equal((aw.slope, aw.inter), (slope, inter))
351351

352352

353-
def test_float_int():
353+
def test_float_int_min_max():
354354
# Conversion between float and int
355355
for in_dt in FLOAT_TYPES:
356356
finf = np.finfo(in_dt)
@@ -362,3 +362,58 @@ def test_float_int():
362362
continue
363363
arr_back_sc = round_trip(aw)
364364
assert_true(np.allclose(arr, arr_back_sc))
365+
366+
367+
def test_float_int_spread():
368+
# Test rounding error for spread of values
369+
powers = np.arange(-10, 10, 0.5)
370+
arr = np.concatenate((-10**powers, 10**powers))
371+
for in_dt in (np.float32, np.float64):
372+
arr_t = arr.astype(in_dt)
373+
for out_dt in IUINT_TYPES:
374+
aw = SlopeInterArrayWriter(arr_t, out_dt)
375+
arr_back_sc = round_trip(aw)
376+
# Get estimate for error
377+
max_miss = rt_err_estimate(arr_t,
378+
arr_back_sc.dtype,
379+
aw.slope,
380+
aw.inter)
381+
# Simulate allclose test with large atol
382+
diff = np.abs(arr_t - arr_back_sc)
383+
rdiff = diff / np.abs(arr_t)
384+
assert_true(np.all((diff <= max_miss) | (rdiff <= 1e-5)))
385+
386+
387+
def rt_err_estimate(arr_t, out_dtype, slope, inter):
388+
# Error attributable to rounding
389+
max_int_miss = slope / 2.
390+
# Estimate error attributable to floating point slope / inter;
391+
# Remove inter / slope, put in a float type to simulate the type
392+
# promotion for the multiplication, apply slope / inter
393+
flt_there = (arr_t - inter) / slope
394+
flt_back = flt_there.astype(out_dtype) * slope + inter
395+
max_flt_miss = np.abs(arr_t - flt_back).max()
396+
# Max error is sum of rounding and fp error
397+
return max_int_miss + max_flt_miss
398+
399+
400+
def test_rt_bias():
401+
# Check for bias in round trip
402+
rng = np.random.RandomState(20111214)
403+
mu, std, count = 100, 10, 100
404+
arr = rng.normal(mu, std, size=(count,))
405+
eps = np.finfo(np.float32).eps
406+
for in_dt in (np.float32, np.float64):
407+
arr_t = arr.astype(in_dt)
408+
for out_dt in IUINT_TYPES:
409+
aw = SlopeInterArrayWriter(arr_t, out_dt)
410+
arr_back_sc = round_trip(aw)
411+
bias = np.mean(arr_t - arr_back_sc)
412+
# Get estimate for error
413+
max_miss = rt_err_estimate(arr_t,
414+
arr_back_sc.dtype,
415+
aw.slope,
416+
aw.inter)
417+
# Hokey use of max_miss as a std estimate
418+
bias_thresh = np.max([max_miss / np.sqrt(count), eps])
419+
assert_true(np.abs(bias) < bias_thresh)

nibabel/tests/test_nifti1.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
Nifti1Pair, Nifti1Extension, Nifti1Extensions,
2222
data_type_codes, extension_codes, slice_order_codes)
2323

24+
from .test_arraywriters import rt_err_estimate, IUINT_TYPES
25+
2426
from numpy.testing import assert_array_equal, assert_array_almost_equal
2527
from nose.tools import (assert_true, assert_false, assert_equal,
2628
assert_raises)
@@ -688,4 +690,66 @@ def test_affines_init():
688690
assert_array_equal(new_hdr.get_zooms(), [3, 4, 5])
689691

690692

691-
693+
def round_trip(img):
694+
stio = BytesIO()
695+
img.file_map['image'].fileobj = stio
696+
img.to_file_map()
697+
return Nifti1Image.from_file_map(img.file_map)
698+
699+
700+
def test_float_int_min_max():
701+
# Conversion between float and int
702+
# Parallel test to arraywriters
703+
aff = np.eye(4)
704+
for in_dt in (np.float32, np.float64):
705+
finf = np.finfo(in_dt)
706+
arr = np.array([finf.min, finf.max], dtype=in_dt)
707+
for out_dt in IUINT_TYPES:
708+
img = Nifti1Image(arr, aff)
709+
img_back = round_trip(img)
710+
arr_back_sc = img_back.get_data()
711+
assert_true(np.allclose(arr, arr_back_sc))
712+
713+
714+
def test_float_int_spread():
715+
# Test rounding error for spread of values
716+
# Parallel test to arraywriters
717+
powers = np.arange(-10, 10, 0.5)
718+
arr = np.concatenate((-10**powers, 10**powers))
719+
aff = np.eye(4)
720+
for in_dt in (np.float32, np.float64):
721+
arr_t = arr.astype(in_dt)
722+
for out_dt in IUINT_TYPES:
723+
img = Nifti1Image(arr_t, aff)
724+
img_back = round_trip(img)
725+
arr_back_sc = img_back.get_data()
726+
slope, inter = img_back.get_header().get_slope_inter()
727+
# Get estimate for error
728+
max_miss = rt_err_estimate(arr_t, arr_back_sc.dtype, slope, inter)
729+
# Simulate allclose test with large atol
730+
diff = np.abs(arr_t - arr_back_sc)
731+
rdiff = diff / np.abs(arr_t)
732+
assert_true(np.all((diff <= max_miss) | (rdiff <= 1e-5)))
733+
734+
735+
def test_rt_bias():
736+
# Check for bias in round trip
737+
# Parallel test to arraywriters
738+
rng = np.random.RandomState(20111214)
739+
mu, std, count = 100, 10, 100
740+
arr = rng.normal(mu, std, size=(count,))
741+
eps = np.finfo(np.float32).eps
742+
aff = np.eye(4)
743+
for in_dt in (np.float32, np.float64):
744+
arr_t = arr.astype(in_dt)
745+
for out_dt in IUINT_TYPES:
746+
img = Nifti1Image(arr_t, aff)
747+
img_back = round_trip(img)
748+
arr_back_sc = img_back.get_data()
749+
slope, inter = img_back.get_header().get_slope_inter()
750+
bias = np.mean(arr_t - arr_back_sc)
751+
# Get estimate for error
752+
max_miss = rt_err_estimate(arr_t, arr_back_sc.dtype, slope, inter)
753+
# Hokey use of max_miss as a std estimate
754+
bias_thresh = np.max([max_miss / np.sqrt(count), eps])
755+
assert_true(np.abs(bias) < bias_thresh)

0 commit comments

Comments
 (0)