def test_generate_noise_spatial(): # Set up the inputs dimensions = np.array([10, 5, 10]) mask = np.ones(dimensions) vol = sim._generate_noise_spatial(dimensions, mask) # Run the analysis from _calc_FHWM but for th elast step of aggregating # across dimensions v_count = 0 v_sum = 0 v_sq = 0 d_sum = [0.0, 0.0, 0.0] d_sq = [0.0, 0.0, 0.0] d_count = [0, 0, 0] # Pull out all the voxel coordinates coordinates = list( product(range(dimensions[0]), range(dimensions[1]), range(dimensions[2]))) # Find the sum of squared error for the non-masked voxels in the brain for i in list(range(len(coordinates))): # Pull out this coordinate x, y, z = coordinates[i] # Is this within the mask? if mask[x, y, z] > 0: # Find the the volume sum and squared values v_count += 1 v_sum += vol[x, y, z] v_sq += vol[x, y, z]**2 # Get the volume variance v_var = (v_sq - ((v_sum**2) / v_count)) / (v_count - 1) for i in list(range(len(coordinates))): # Pull out this coordinate x, y, z = coordinates[i] # Is this within the mask? if mask[x, y, z] > 0: # For each xyz dimension calculate the squared # difference of this voxel and the next in_range = (x < dimensions[0] - 1) in_mask = in_range and (mask[x + 1, y, z] > 0) included = in_mask and (~np.isnan(vol[x + 1, y, z])) if included: d_sum[0] += vol[x, y, z] - vol[x + 1, y, z] d_sq[0] += (vol[x, y, z] - vol[x + 1, y, z])**2 d_count[0] += 1 in_range = (y < dimensions[1] - 1) in_mask = in_range and (mask[x, y + 1, z] > 0) included = in_mask and (~np.isnan(vol[x, y + 1, z])) if included: d_sum[1] += vol[x, y, z] - vol[x, y + 1, z] d_sq[1] += (vol[x, y, z] - vol[x, y + 1, z])**2 d_count[1] += 1 in_range = (z < dimensions[2] - 1) in_mask = in_range and (mask[x, y, z + 1] > 0) included = in_mask and (~np.isnan(vol[x, y, z + 1])) if included: d_sum[2] += vol[x, y, z] - vol[x, y, z + 1] d_sq[2] += (vol[x, y, z] - vol[x, y, z + 1])**2 d_count[2] += 1 # Find the variance d_var = np.divide((d_sq - np.divide(np.power(d_sum, 2), d_count)), (np.add(d_count, -1))) o_var = np.divide(-1, (4 * np.log(1 - (0.5 * d_var / v_var)))) fwhm3 = np.sqrt(o_var) * 2 * np.sqrt(2 * np.log(2)) # Calculate the proportion of std relative to the mean std_proportion = np.nanstd(fwhm3) / np.nanmean(fwhm3) assert std_proportion < 0.25, 'Variance is inconsistent across dim'
def test_generate_noise_spatial(): # Set up the inputs dimensions = np.array([10, 5, 10]) mask = np.ones(dimensions) vol = sim._generate_noise_spatial(dimensions, mask) # Run the analysis from _calc_FHWM but for th elast step of aggregating # across dimensions v_count = 0 v_sum = 0 v_sq = 0 d_sum = [0.0, 0.0, 0.0] d_sq = [0.0, 0.0, 0.0] d_count = [0, 0, 0] # Pull out all the voxel coordinates coordinates = list(product(range(dimensions[0]), range(dimensions[1]), range(dimensions[2]))) # Find the sum of squared error for the non-masked voxels in the brain for i in list(range(len(coordinates))): # Pull out this coordinate x, y, z = coordinates[i] # Is this within the mask? if mask[x, y, z] > 0: # Find the the volume sum and squared values v_count += 1 v_sum += vol[x, y, z] v_sq += vol[x, y, z] ** 2 # Get the volume variance v_var = (v_sq - ((v_sum ** 2) / v_count)) / (v_count - 1) for i in list(range(len(coordinates))): # Pull out this coordinate x, y, z = coordinates[i] # Is this within the mask? if mask[x, y, z] > 0: # For each xyz dimension calculate the squared # difference of this voxel and the next in_range = (x < dimensions[0] - 1) in_mask = in_range and (mask[x + 1, y, z] > 0) included = in_mask and (~np.isnan(vol[x + 1, y, z])) if included: d_sum[0] += vol[x, y, z] - vol[x + 1, y, z] d_sq[0] += (vol[x, y, z] - vol[x + 1, y, z]) ** 2 d_count[0] += 1 in_range = (y < dimensions[1] - 1) in_mask = in_range and (mask[x, y + 1, z] > 0) included = in_mask and (~np.isnan(vol[x, y + 1, z])) if included: d_sum[1] += vol[x, y, z] - vol[x, y + 1, z] d_sq[1] += (vol[x, y, z] - vol[x, y + 1, z]) ** 2 d_count[1] += 1 in_range = (z < dimensions[2] - 1) in_mask = in_range and (mask[x, y, z + 1] > 0) included = in_mask and (~np.isnan(vol[x, y, z + 1])) if included: d_sum[2] += vol[x, y, z] - vol[x, y, z + 1] d_sq[2] += (vol[x, y, z] - vol[x, y, z + 1]) ** 2 d_count[2] += 1 # Find the variance d_var = np.divide((d_sq - np.divide(np.power(d_sum, 2), d_count)), (np.add(d_count, -1))) o_var = np.divide(-1, (4 * np.log(1 - (0.5 * d_var / v_var)))) fwhm3 = np.sqrt(o_var) * 2 * np.sqrt(2 * np.log(2)) # Calculate the proportion of std relative to the mean std_proportion = np.nanstd(fwhm3) / np.nanmean(fwhm3) print(fwhm3) assert std_proportion < 0.25, 'Variance is inconsistent across dim'
def test_generate_noise(): dimensions = np.array([10, 10, 10]) # What is the size of the brain feature_size = [2] feature_type = ['cube'] feature_coordinates = np.array([[5, 5, 5]]) signal_magnitude = [1] # Generate a volume representing the location and quality of the signal volume = sim.generate_signal( dimensions=dimensions, feature_coordinates=feature_coordinates, feature_type=feature_type, feature_size=feature_size, signal_magnitude=signal_magnitude, ) # Inputs for generate_stimfunction onsets = [10, 30, 50, 70, 90] event_durations = [6] tr_duration = 2 duration = 200 # Create the time course for the signal to be generated stimfunction = sim.generate_stimfunction( onsets=onsets, event_durations=event_durations, total_time=duration, ) signal_function = sim.convolve_hrf( stimfunction=stimfunction, tr_duration=tr_duration, ) # Convolve the HRF with the stimulus sequence signal = sim.apply_signal( signal_function=signal_function, volume_signal=volume, ) # Generate the mask of the signal mask, template = sim.mask_brain(signal, mask_self=None) assert min(mask[mask > 0]) > 0.1, "Mask thresholding did not work" assert len(np.unique(template) > 2), "Template creation did not work" stimfunction_tr = stimfunction[::int(tr_duration * 100)] # Create the noise volumes (using the default parameters) noise = sim.generate_noise( dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, iterations=[1, 0], ) assert signal.shape == noise.shape, "The dimensions of signal and noise " \ "the same" noise_high = sim.generate_noise( dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={ 'sfnr': 50, 'snr': 25 }, iterations=[1, 0], ) noise_low = sim.generate_noise( dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={ 'sfnr': 100, 'snr': 25 }, iterations=[1, 0], ) system_high = np.std(noise_high[mask > 0], 1).mean() system_low = np.std(noise_low[mask > 0], 1).mean() assert system_low < system_high, "SFNR noise could not be manipulated" # Check that you check for the appropriate template values with pytest.raises(ValueError): sim.generate_noise( dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template * 2, mask=mask, noise_dict={}, ) # Check that iterations does what it should sim.generate_noise( dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={}, iterations=[0, 0], ) sim.generate_noise( dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={}, iterations=None, ) # Test drift noise trs = 1000 period = 100 drift = sim._generate_noise_temporal_drift( trs, tr_duration, 'sine', period, ) # Check that the max frequency is the appropriate frequency power = abs(np.fft.fft(drift))[1:trs // 2] freq = np.linspace(1, trs // 2 - 1, trs // 2 - 1) / trs period_freq = np.where(freq == 1 / (period // tr_duration)) max_freq = np.argmax(power) assert period_freq == max_freq, 'Max frequency is not where it should be' # Do the same but now with cosine basis functions, answer should be close drift = sim._generate_noise_temporal_drift( trs, tr_duration, 'discrete_cos', period, ) # Check that the appropriate frequency is peaky (may not be the max) power = abs(np.fft.fft(drift))[1:trs // 2] freq = np.linspace(1, trs // 2 - 1, trs // 2 - 1) / trs period_freq = np.where(freq == 1 / (period // tr_duration))[0][0] assert power[period_freq] > power[period_freq + 1], 'Power is low' assert power[period_freq] > power[period_freq - 1], 'Power is low' # Check it runs fine drift = sim._generate_noise_temporal_drift( 50, tr_duration, 'discrete_cos', period, ) # Check it runs fine drift = sim._generate_noise_temporal_drift( 300, tr_duration, 'cos_power_drop', period, ) # Check that when the TR is greater than the period it errors with pytest.raises(ValueError): sim._generate_noise_temporal_drift(30, 10, 'cos_power_drop', 5) # Test physiological noise (using unrealistic parameters so that it's easy) timepoints = list(np.linspace(0, (trs - 1) * tr_duration, trs)) resp_freq = 0.2 heart_freq = 1.17 phys = sim._generate_noise_temporal_phys( timepoints, resp_freq, heart_freq, ) # Check that the max frequency is the appropriate frequency power = abs(np.fft.fft(phys))[1:trs // 2] freq = np.linspace(1, trs // 2 - 1, trs // 2 - 1) / (trs * tr_duration) peaks = (power > (power.mean() + power.std())) # Where are the peaks peak_freqs = freq[peaks] assert np.any(resp_freq == peak_freqs), 'Resp frequency not found' assert len(peak_freqs) == 2, 'Two peaks not found' # Test task noise sim._generate_noise_temporal_task( stimfunction_tr, motion_noise='gaussian', ) sim._generate_noise_temporal_task( stimfunction_tr, motion_noise='rician', ) # Test ARMA noise with pytest.raises(ValueError): noise_dict = {'fwhm': 4, 'auto_reg_rho': [1], 'ma_rho': [1, 1]} sim._generate_noise_temporal_autoregression( stimfunction_tr, noise_dict, dimensions, mask, ) # Generate spatial noise vol = sim._generate_noise_spatial(np.array([10, 10, 10, trs])) assert len(vol.shape) == 3, 'Volume was not reshaped to ignore TRs' # Switch some of the noise types on noise_dict = dict(physiological_sigma=1, drift_sigma=1, task_sigma=1, auto_reg_sigma=0) sim.generate_noise( dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict=noise_dict, iterations=[0, 0], )
def test_generate_noise(): dimensions = np.array([10, 10, 10]) # What is the size of the brain feature_size = [2] feature_type = ['cube'] feature_coordinates = np.array( [[5, 5, 5]]) signal_magnitude = [1] # Generate a volume representing the location and quality of the signal volume = sim.generate_signal(dimensions=dimensions, feature_coordinates=feature_coordinates, feature_type=feature_type, feature_size=feature_size, signal_magnitude=signal_magnitude, ) # Inputs for generate_stimfunction onsets = [10, 30, 50, 70, 90] event_durations = [6] tr_duration = 2 duration = 200 # Create the time course for the signal to be generated stimfunction = sim.generate_stimfunction(onsets=onsets, event_durations=event_durations, total_time=duration, ) signal_function = sim.convolve_hrf(stimfunction=stimfunction, tr_duration=tr_duration, ) # Convolve the HRF with the stimulus sequence signal = sim.apply_signal(signal_function=signal_function, volume_signal=volume, ) # Generate the mask of the signal mask, template = sim.mask_brain(signal, mask_self=None) assert min(mask[mask > 0]) > 0.1, "Mask thresholding did not work" assert len(np.unique(template) > 2), "Template creation did not work" stimfunction_tr = stimfunction[::int(tr_duration * 100)] # Create the noise volumes (using the default parameters) noise = sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, iterations=[1, 0], ) assert signal.shape == noise.shape, "The dimensions of signal and noise " \ "the same" noise_high = sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={'sfnr': 50, 'snr': 25}, iterations=[1, 0], ) noise_low = sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={'sfnr': 100, 'snr': 25}, iterations=[1, 0], ) system_high = np.std(noise_high[mask > 0], 1).mean() system_low = np.std(noise_low[mask > 0], 1).mean() assert system_low < system_high, "SFNR noise could not be manipulated" # Check that you check for the appropriate template values with pytest.raises(ValueError): sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template * 2, mask=mask, noise_dict={}, ) # Check that iterations does what it should sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={}, iterations=[0, 0], ) sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict={}, iterations=None, ) # Test drift noise trs = 1000 period = 100 drift = sim._generate_noise_temporal_drift(trs, tr_duration, 'sine', period, ) # Check that the max frequency is the appropriate frequency power = abs(np.fft.fft(drift))[1:trs // 2] freq = np.linspace(1, trs // 2 - 1, trs // 2 - 1) / trs period_freq = np.where(freq == 1 / (period // tr_duration)) max_freq = np.argmax(power) assert period_freq == max_freq, 'Max frequency is not where it should be' # Do the same but now with cosine basis functions, answer should be close drift = sim._generate_noise_temporal_drift(trs, tr_duration, 'discrete_cos', period, ) # Check that the appropriate frequency is peaky (may not be the max) power = abs(np.fft.fft(drift))[1:trs // 2] freq = np.linspace(1, trs // 2 - 1, trs // 2 - 1) / trs period_freq = np.where(freq == 1 / (period // tr_duration))[0][0] assert power[period_freq] > power[period_freq + 1], 'Power is low' assert power[period_freq] > power[period_freq - 1], 'Power is low' # Check it gives a warning if the duration is too short drift = sim._generate_noise_temporal_drift(50, tr_duration, 'discrete_cos', period, ) # Test physiological noise (using unrealistic parameters so that it's easy) timepoints = list(np.linspace(0, (trs - 1) * tr_duration, trs)) resp_freq = 0.2 heart_freq = 1.17 phys = sim._generate_noise_temporal_phys(timepoints, resp_freq, heart_freq, ) # Check that the max frequency is the appropriate frequency power = abs(np.fft.fft(phys))[1:trs // 2] freq = np.linspace(1, trs // 2 - 1, trs // 2 - 1) / (trs * tr_duration) peaks = (power > (power.mean() + power.std())) # Where are the peaks peak_freqs = freq[peaks] assert np.any(resp_freq == peak_freqs), 'Resp frequency not found' assert len(peak_freqs) == 2, 'Two peaks not found' # Test task noise sim._generate_noise_temporal_task(stimfunction_tr, motion_noise='gaussian', ) sim._generate_noise_temporal_task(stimfunction_tr, motion_noise='rician', ) # Test ARMA noise with pytest.raises(ValueError): noise_dict = {'fwhm': 4, 'auto_reg_rho': [1], 'ma_rho': [1, 1]} sim._generate_noise_temporal_autoregression(stimfunction_tr, noise_dict, dimensions, mask, ) # Generate spatial noise vol = sim._generate_noise_spatial(np.array([10, 10, 10, trs])) assert len(vol.shape) == 3, 'Volume was not reshaped to ignore TRs' # Switch some of the noise types on noise_dict = dict(physiological_sigma=1, drift_sigma=1, task_sigma=1, auto_reg_sigma=0) sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict=noise_dict, iterations=[0, 0], )