def test_puretone_run_sequence(): """ Check that puretone's synthesize_sequence() correctly accepts a list of dicts and returns a correspond ing number of stimuli """ synth = sy.PureTone() # Generate 1000 and 2000 Hz pure tones using synthesize_parameter_sequence results = synth.synthesize_sequence([{ 'freq': 1000, 'level': 50, 'phase': 0, 'dur': 1, 'fs': int(48e3), 'dur_ramp': 0.1 }, { 'freq': 2000, 'level': 50, 'phase': 0, 'dur': 1, 'fs': int(48e3), 'dur_ramp': 0.1 }]) # Generate references manually reference1 = sg.cosine_ramp( sg.scale_dbspl(sg.pure_tone(1000, 0, 1, int(48e3)), 50), 0.1, int(48e3)) reference2 = sg.cosine_ramp( sg.scale_dbspl(sg.pure_tone(2000, 0, 1, int(48e3)), 50), 0.1, int(48e3)) assert np.all(results[0] == reference1) and np.all( results[1] == reference2)
def test_puretone_random_level(): """ Check that pure tone can accept a random level as a parameter and handle its evaluation when synthesis() is called. We check this by making sure that the output RMS level is not the same from one sample to another. """ synth = sy.PureTone() tempfunc = lambda: np.random.uniform(40, 60, 1) assert sg.rms(synth.synthesize_sequence(parameters=[{'level': tempfunc}])[0]) != \ sg.rms(synth.synthesize_sequence(parameters=[{'level': tempfunc}])[0])
def test_ideal_observer_valid_input(): """ Test that if we provide a valid params to a ratefunc wrapped in decode_ideal_observer that everything runs without any errors """ # Initialize simulator object sim = anf.AuditoryNerveHeinz2001() # Define stimulus parameters fs = int(200e3) tone_level = 30 tone_dur = 0.1 tone_ramp_dur = 0.01 tone_freq = 1000 # Synthesize stimuli synth = sy.PureTone() params = si.Parameters(level=tone_level, dur=tone_dur, dur_ramp=tone_ramp_dur, freq=tone_freq, fs=fs) params.increment({'freq': 0.001}) stimuli = synth.synthesize_sequence(params) params.add_inputs(stimuli) # Add stimuli and model params params.append(['cf_low', 'cf_high', 'n_cf'], [1000, 1000, 1]) params.append(['n_fiber_per_chan', 'fs', 'delta_theta', 'API'], [1, int(200e3), [0.001], np.zeros(1)]) # Run out = sim.run(params, runfunc=decode_ideal_observer(sim.simulate))
def test_puretone_incremented_level(): """ Check that pure tone can accept a level with an increment and return appropriately scaled pure tones """ synth = sy.PureTone() params = increment_parameters({'level': 20}, {'level': 1}) np.testing.assert_approx_equal( sg.dbspl_pascal(synth.synthesize_sequence(parameters=params)[0]) - sg.dbspl_pascal(synth.synthesize_sequence(parameters=params)[1]), -1, 5)
def test_puretone_synthesize(): """ Check that PureTone object can successfully synthesize and replicates standard pure tone synthesis""" synth = sy.PureTone() output = synth.synthesize(1000, 50, 0, 1, 0.1, int(48e3)) reference = sg.cosine_ramp( sg.scale_dbspl(sg.pure_tone(1000, 0, 1, int(48e3)), 50), 0.1, int(48e3)) assert np.all(output == reference)
def test_puretone_wiggled_level_with_random_variables(): """ Check that we can construct a parameter dict, wiggle level to be various random variables, and then synthesize and get plausible output values. """ synth = sy.PureTone() params = wiggle_parameters(dict(), 'level', [ lambda: np.random.uniform(35, 45, 1), lambda: np.random.uniform(45, 55, 1) ]) outs = synth.synthesize_sequence(parameters=params) assert sg.rms(outs[0]) < sg.rms(outs[1])
def test_puretone_incremented_level_with_random_level(): """ Check that pure tone can accept a random level as a parameter with an increment and return appropriately scaled pure tones. We simplify the calculation by using a ~random~ distribution with no variance. """ synth = sy.PureTone() tempfunc = lambda: np.random.uniform(50, 50, 1) params = increment_parameters({'level': tempfunc}, {'level': 1}) np.testing.assert_approx_equal( sg.dbspl_pascal(synth.synthesize_sequence(parameters=params)[0]) - sg.dbspl_pascal(synth.synthesize_sequence(parameters=params)[1]), -1, 5)
def test_ideal_observer_real_simulation_with_level_roving(): """ Test that ideal observer analysis on simple pure tone FDLs shows increasing FDLs with increasing frequency in the context of a mild level rove on the pure tone """ # Initialize simulator object sim = anf.AuditoryNerveHeinz2001() # Define stimulus parameters fs = int(200e3) def tone_level(): return np.random.uniform(25, 35, 1) tone_dur = 0.1 tone_ramp_dur = 0.01 tone_freqs = [1000, 2000, 4000, 8000] # Encode stimulus parameters params = { 'level': tone_level, 'dur': tone_dur, 'dur_ramp': tone_ramp_dur, 'fs': fs } params = si.wiggle_parameters(params, 'freq', tone_freqs) # Encode model parameters params = si.stitch_parameters(params, 'cf_low', [1000, 2000, 4000, 8000]) params = si.stitch_parameters(params, 'cf_high', [1000, 2000, 4000, 8000]) params = si.append_parameters( params, ['fs', 'n_cf', 'n_fiber_per_chan', 'delta_theta', 'API'], [int(200e3), 1, 5, [0.001, 0.001], np.array([[0, 0], [0, 1 / 6**2]])]) # Encode repeats and increments params = si.repeat_parameters(params, 10) params = si.increment_parameters(params, {'freq': 0.001, 'level': 0.001}) # Synthesize stimuli and encode in params synth = sy.PureTone() stimuli = synth.synthesize_sequence(params) params = si.stitch_parameters(params, '_input', stimuli) # Run model output = sim.run(params, parallel=True, runfunc=decode_ideal_observer(sim.simulate)) # Extract AI thresholds output = [ out[0] for out in output ] # AI thresholds are always the first element of each tuple in output # Check to make sure that thresholds grow with frequency assert np.all(np.diff(output) > 0)
def test_ideal_observer_FDL_vs_frequency(): """ Test that ideal observer analysis on simple pure tone FDLs shows increasing FDLs with increasing frequency """ # Initialize simulator object sim = anf.AuditoryNerveHeinz2001() # Define stimulus parameters fs = int(200e3) tone_level = 30 tone_dur = 0.1 tone_ramp_dur = 0.01 tone_freqs = [1000, 2000, 4000, 8000] # Encode stimulus information params = { 'level': tone_level, 'dur': tone_dur, 'dur_ramp': tone_ramp_dur, 'fs': fs } params = si.wiggle_parameters(params, 'freq', tone_freqs) # Encode model information params = si.stitch_parameters(params, 'cf_low', [1000, 2000, 4000, 8000]) params = si.stitch_parameters(params, 'cf_high', [1000, 2000, 4000, 8000]) params = si.append_parameters( params, ['n_cf', 'fs', 'n_fiber_per_chan', 'delta_theta', 'API'], [1, int(200e3), 5, [0.001], np.zeros((1))]) # Flatten and increment frequency params = si.flatten_parameters(params) params = si.increment_parameters(params, {'freq': 0.001}) synth = sy.PureTone() stimuli = synth.synthesize_sequence(params) params = si.stitch_parameters(params, '_input', stimuli) # Run model output = sim.run(params, parallel=True, runfunc=decode_ideal_observer(sim.simulate)) # Extract AI thresholds output = [ out[0] for out in output ] # AI thresholds are always the first element of each tuple in output # Check to make sure that thresholds grow with frequency assert np.all(np.diff(output) > 0)
def test_puretone_incremented_sequence(): """ Check that puretone's synthesize_sequence() and increment_sequence() successfully combine to produce two pure tones, one with a slightly higher frequency""" synth = sy.PureTone() # Generate 1000 and 2000 Hz pure tones using synthesize_parameter_sequence results = synth.synthesize_sequence( increment_parameters(parameters={'freq': 1000}, increments={'freq': 0.001})) # Generate references manually reference1 = sg.cosine_ramp( sg.scale_dbspl(sg.pure_tone(1000, 0, 1, int(48e3)), 50), 0.1, int(48e3)) reference2 = sg.cosine_ramp( sg.scale_dbspl(sg.pure_tone(1000.001, 0, 1, int(48e3)), 50), 0.1, int(48e3)) assert np.all(results[0] == reference1) and np.all( results[1] == reference2)
def test_ideal_observer_single_input(): """ Test that if we provide a single stimulus to a ratefunc wrapped in decode_ideal_observer that some sort of error is raised to indicate that an ideal observer can't be calculated based on a single simulation! """ # Initialize simulator object sim = anf.AuditoryNerveHeinz2001() # Define stimulus parameters fs = int(200e3) tone_level = 30 tone_dur = 0.1 tone_ramp_dur = 0.01 tone_freq = 1000 # Synthesize stimuli synth = sy.PureTone() params = { 'level': tone_level, 'dur': tone_dur, 'dur_ramp': tone_ramp_dur, 'freq': tone_freq, 'fs': fs } stimuli = synth.synthesize_sequence([params]) # Add stimuli and model params params = si.append_parameters(params, ['_input', 'cf_low', 'cf_high', 'n_cf'], [stimuli[0], 1000, 1000, 1]) params = si.append_parameters( params, ['n_fiber_per_chan', 'fs', 'delta_theta', 'API'], [5, int(200e3), [0.001], np.zeros(1)]) # Run model try: sim.run([params], runfunc=decode_ideal_observer(sim.simulate)) raise Exception('This should have failed!') except ValueError: return
def test_anf_rate_level_function(anf_model): """ Test to make sure that a basic anf simulation can be set up and run on a single sequence of pure tones with increasing levels and that the simulation returns a corresponding increasing rate response. Check both the Heinz et al. (2001) and the Zilany et al. (2014) nerve model. """ # Initialize simulator object sim = anf_model() # Define stimulus parameters fs = int(200e3) # sampling rate, Hz tone_freq = 1000 # tone frequency, Hz tone_dur = 0.1 # tone duration, s tone_ramp_dur = 0.01 # ramp duration, s tone_levels = [0, 10, 20, 30, 40, 50] # levels to test, dB SPL cf_low = 1000 # cf of auditory nerve, Hz cf_high = 1000 # cf of auditory nerve, Hz n_cf = 1 # how many auditory nerves to test, int # Encode parameters in Parameters params = si.Parameters(freq=tone_freq, dur=tone_dur, dur_ramp=tone_ramp_dur, fs=fs, cf_low=cf_low, cf_high=cf_high, n_cf=n_cf) params.wiggle('level', tone_levels) # Add stimuli to Parameters params.add_inputs(sy.PureTone().synthesize_sequence(params)) # Run model output = sim.run(params) means = [np.mean(resp) for resp in output] # calculate mean of each response assert np.all(np.diff(means) > 0)
def test_synthesizer_raises_warnings_about_kwargs(): """ Check that when we pass through kwargs to Synthesizer objects that we do get warnings """ synth = sy.PureTone() with pytest.warns(UserWarning): synth.synthesize(freq=1000, testparam='foo')