def test_TemporalModel(): # Build grid: model = ValidTemporalModel() npt.assert_equal(model.is_built, False) model.build() npt.assert_equal(model.is_built, True) # Can overwrite default values: model = ValidTemporalModel(dt=2e-5) npt.assert_almost_equal(model.dt, 2e-5) model.build(dt=1.234) npt.assert_almost_equal(model.dt, 1.234) # Cannot add more attributes: with pytest.raises(AttributeError): ValidTemporalModel(newparam=1) with pytest.raises(FreezeError): model.newparam = 1 # Returns Percept object of proper size: npt.assert_equal(model.predict_percept(ArgusI().stim), None) model.dt = 1 for stim in [ np.ones((16, 3)), np.zeros((16, 3)), { 'A1': [1, 2] }, np.ones((16, 2)) ]: implant = ArgusI(stim=stim) percept = model.predict_percept(implant.stim) # By default, percept is output every 20ms. If stimulus is too short, # output at t=[0, 20]. This is mentioned in the docs - for really short # stimuli, users should specify the desired time points manually. n_time = 1 if implant.stim.time is None else 2 npt.assert_equal(percept.shape, (implant.stim.shape[0], 1, n_time)) npt.assert_almost_equal(percept.data, 0) # t_percept is automatically sorted: model.dt = 0.1 percept = model.predict_percept(Stimulus(np.zeros((3, 17))), t_percept=[0.1, 0.8, 0.6]) npt.assert_almost_equal(percept.time, [0.1, 0.6, 0.8]) # Invalid calls: with pytest.raises(ValueError): # Cannot request t_percepts that are not multiples of dt: model.predict_percept(Stimulus(np.ones((3, 9))), t_percept=[0.1, 0.11]) with pytest.raises(ValueError): # Has temporal model but stim.time is None: ValidTemporalModel().predict_percept(Stimulus(3)) with pytest.raises(ValueError): # stim.time==None but requesting t_percept != None ValidTemporalModel().predict_percept(Stimulus(3), t_percept=[0, 1, 2]) with pytest.raises(NotBuiltError): # Must call build first: ValidTemporalModel().predict_percept(Stimulus(3)) with pytest.raises(TypeError): # Must pass a stimulus: ValidTemporalModel().build().predict_percept(ArgusI())
def test_ScoreboardModel(): # ScoreboardModel automatically sets `rho`: model = ScoreboardModel(engine='serial', xystep=5) npt.assert_equal(model.has_space, True) npt.assert_equal(model.has_time, False) npt.assert_equal(hasattr(model.spatial, 'rho'), True) # User can set `rho`: model.rho = 123 npt.assert_equal(model.rho, 123) npt.assert_equal(model.spatial.rho, 123) model.build(rho=987) npt.assert_equal(model.rho, 987) npt.assert_equal(model.spatial.rho, 987) # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI()), None) # Zero in = zero out: implant = ArgusI(stim=np.zeros(16)) npt.assert_almost_equal(model.predict_percept(implant).data, 0) # Multiple frames are processed independently: model = ScoreboardModel(engine='serial', rho=200, xystep=5) model.build() percept = model.predict_percept(ArgusI(stim={'A1': [1, 2]})) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [2]) pmax = percept.data.max(axis=(0, 1)) npt.assert_almost_equal(percept.data[2, 3, :], pmax) npt.assert_almost_equal(pmax[1] / pmax[0], 2.0)
def test_AxonMapSpatial(engine): # AxonMapSpatial automatically sets `rho`, `axlambda`: model = AxonMapSpatial(engine=engine, xystep=5) # User can set `rho`: model.rho = 123 npt.assert_equal(model.rho, 123) model.build(rho=987) npt.assert_equal(model.rho, 987) # Converting ret <=> dva npt.assert_almost_equal(model.ret2dva(0), 0) npt.assert_almost_equal(model.dva2ret(0), 0) # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI()), None) # Zero in = zero out: implant = ArgusI(stim=np.zeros(16)) percept = model.predict_percept(implant) npt.assert_equal(isinstance(percept, Percept), True) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [1]) npt.assert_almost_equal(percept.data, 0) # Multiple frames are processed independently: model = AxonMapSpatial(engine=engine, rho=200, axlambda=100, xystep=5) model.build() percept = model.predict_percept(ArgusI(stim={'A1': [1, 0], 'B3': [0, 2]})) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [2]) pmax = percept.data.max(axis=(0, 1)) npt.assert_almost_equal(percept.data[2, 3, 0], pmax[0]) npt.assert_almost_equal(percept.data[2, 3, 1], 0) npt.assert_almost_equal(percept.data[3, 4, 0], 0) npt.assert_almost_equal(percept.data[3, 4, 1], pmax[1])
def test_AxonMapSpatial(engine): # AxonMapSpatial automatically sets `rho`, `axlambda`: model = AxonMapSpatial(engine=engine, xystep=5) # User can set `rho`: model.rho = 123 npt.assert_equal(model.rho, 123) model.build(rho=987) npt.assert_equal(model.rho, 987) # Converting ret <=> dva npt.assert_equal(isinstance(model.retinotopy, Watson2014Map), True) npt.assert_almost_equal(model.retinotopy.ret2dva(0, 0), (0, 0)) npt.assert_almost_equal(model.retinotopy.dva2ret(0, 0), (0, 0)) model2 = AxonMapSpatial(retinotopy=Watson2014DisplaceMap()) npt.assert_equal(isinstance(model2.retinotopy, Watson2014DisplaceMap), True) # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI()), None) # Zero in = zero out: implant = ArgusI(stim=np.zeros(16)) percept = model.predict_percept(implant) npt.assert_equal(isinstance(percept, Percept), True) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [1]) npt.assert_almost_equal(percept.data, 0) npt.assert_equal(percept.time, None) # Lambda cannot be too small: with pytest.raises(ValueError): AxonMapSpatial(axlambda=9).build() # Multiple frames are processed independently: model = AxonMapSpatial(engine=engine, rho=200, axlambda=100, xystep=5, xrange=(-20, 20), yrange=(-15, 15)) model.build() # Axon map jax predict_percept not implemented yet if engine == 'jax': with pytest.raises(NotImplementedError): percept = model.predict_percept( ArgusII(stim={ 'A1': [1, 0], 'B3': [0, 2] })) return percept = model.predict_percept(ArgusI(stim={'A1': [1, 0], 'B3': [0, 2]})) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [2]) pmax = percept.data.max(axis=(0, 1)) npt.assert_almost_equal(percept.data[2, 3, 0], pmax[0]) npt.assert_almost_equal(percept.data[2, 3, 1], 0) npt.assert_almost_equal(percept.data[3, 4, 0], 0) npt.assert_almost_equal(percept.data[3, 4, 1], pmax[1]) npt.assert_almost_equal(percept.time, [0, 1])
def test_Model_predict_percept(): # A None Model has nothing to build, nothing to perceive: model = Model() npt.assert_equal(model.predict_percept(ArgusI()), None) npt.assert_equal(model.predict_percept(ArgusI(stim={'A1': 1})), None) npt.assert_equal( model.predict_percept(ArgusI(stim={'A1': 1}), t_percept=[0, 1]), None) # Just the spatial model: model = Model(spatial=ValidSpatialModel()).build() npt.assert_equal(model.predict_percept(ArgusI()), None) # Just the temporal model: model = Model(temporal=ValidTemporalModel()).build() npt.assert_equal(model.predict_percept(ArgusI()), None) # Invalid calls: model = Model(spatial=ValidSpatialModel(), temporal=ValidTemporalModel()) with pytest.raises(NotBuiltError): # Must call build first: model.predict_percept(ArgusI()) model.build() with pytest.raises(ValueError): # Cannot request t_percepts that are not multiples of dt: model.predict_percept(ArgusI(stim=np.ones(16)), t_percept=[0.1, 0.11]) with pytest.raises(ValueError): # stim.time==None but requesting t_percept != None model.predict_percept(ArgusI(stim=np.ones(16)), t_percept=[0, 1, 2]) with pytest.raises(TypeError): # Must pass an implant: model.predict_percept(Stimulus(3))
def test_SpatialModel(): # Build grid: model = ValidSpatialModel() npt.assert_equal(model.grid, None) npt.assert_equal(model.is_built, False) model.build() npt.assert_equal(model.is_built, True) npt.assert_equal(isinstance(model.grid, GridXY), True) npt.assert_equal(isinstance(model.grid.xret, np.ndarray), True) # Can overwrite default values: model = ValidSpatialModel(xystep=1.234) npt.assert_almost_equal(model.xystep, 1.234) model.build(xystep=2.345) npt.assert_almost_equal(model.xystep, 2.345) # Cannot add more attributes: with pytest.raises(AttributeError): ValidSpatialModel(newparam=1) with pytest.raises(FreezeError): model.newparam = 1 # Returns Percept object of proper size: npt.assert_equal(model.predict_percept(ArgusI()), None) for stim in [np.ones(16), np.zeros(16), {'A1': 2}, np.ones((16, 2))]: implant = ArgusI(stim=stim) percept = model.predict_percept(implant) npt.assert_equal(isinstance(percept, Percept), True) n_time = 1 if implant.stim.time is None else len(implant.stim.time) npt.assert_equal( percept.shape, (model.grid.x.shape[0], model.grid.x.shape[1], n_time)) npt.assert_almost_equal(percept.data, 0) # Invalid calls: with pytest.raises(ValueError): # stim.time==None but requesting t_percept != None implant.stim = np.ones(16) model.predict_percept(implant, t_percept=[0, 1, 2]) with pytest.raises(NotBuiltError): # must call build first model = ValidSpatialModel() model.predict_percept(ArgusI()) with pytest.raises(TypeError): # must pass an implant ValidSpatialModel().build().predict_percept(Stimulus(3))
def test_Nanduri2012Spatial(): # Nanduri2012Spatial automatically sets `atten_a`: model = Nanduri2012Spatial(engine='serial', xystep=5) # User can set `atten_a`: model.atten_a = 12345 npt.assert_equal(model.atten_a, 12345) model.build(atten_a=987) npt.assert_equal(model.atten_a, 987) # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI()), None) # Zero in = zero out: implant = ArgusI(stim=np.zeros(16)) percept = model.predict_percept(implant) npt.assert_equal(isinstance(percept, Percept), True) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [1]) npt.assert_almost_equal(percept.data, 0) # Only works for DiskElectrode arrays: with pytest.raises(TypeError): implant = ProsthesisSystem(ElectrodeArray(PointSource(0, 0, 0))) implant.stim = 1 model.predict_percept(implant) with pytest.raises(TypeError): implant = ProsthesisSystem( ElectrodeArray( [DiskElectrode(0, 0, 0, 100), PointSource(100, 100, 0)])) implant.stim = [1, 1] model.predict_percept(implant) # Multiple frames are processed independently: model = Nanduri2012Spatial(engine='serial', atten_a=14000, xystep=5, xrange=(-20, 20), yrange=(-15, 15)) model.build() percept = model.predict_percept(ArgusI(stim={'A1': [1, 2]})) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [2]) pmax = percept.data.max(axis=(0, 1)) npt.assert_almost_equal(percept.data[2, 3, :], pmax) npt.assert_almost_equal(pmax[1] / pmax[0], 2.0) # Nanduri model uses a linear dva2ret conversion factor: for factor in [0.0, 1.0, 2.0]: npt.assert_almost_equal(model.retinotopy.dva2ret(factor, factor), (280.0 * factor, 280.0 * factor)) for factor in [0.0, 1.0, 2.0]: npt.assert_almost_equal( model.retinotopy.ret2dva(280.0 * factor, 280.0 * factor), (factor, factor))
def test_ScoreboardSpatial(): # ScoreboardSpatial automatically sets `rho`: model = ScoreboardSpatial(engine='serial', xystep=5) # User can set `rho`: model.rho = 123 npt.assert_equal(model.rho, 123) model.build(rho=987) npt.assert_equal(model.rho, 987) # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI()), None) # Converting ret <=> dva npt.assert_equal(isinstance(model.retinotopy, Watson2014Map), True) npt.assert_almost_equal(model.retinotopy.ret2dva(0, 0), (0, 0)) npt.assert_almost_equal(model.retinotopy.dva2ret(0, 0), (0, 0)) model2 = ScoreboardSpatial(retinotopy=Watson2014DisplaceMap()) npt.assert_equal(isinstance(model2.retinotopy, Watson2014DisplaceMap), True) implant = ArgusI(stim=np.zeros(16)) # Zero in = zero out: percept = model.predict_percept(implant) npt.assert_equal(isinstance(percept, Percept), True) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [1]) npt.assert_almost_equal(percept.data, 0) # Multiple frames are processed independently: model = ScoreboardSpatial(engine='serial', rho=200, xystep=5, xrange=(-20, 20), yrange=(-15, 15)) model.build() percept = model.predict_percept(ArgusI(stim={'A1': [1, 0], 'B3': [0, 2]})) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [2]) pmax = percept.data.max(axis=(0, 1)) npt.assert_almost_equal(percept.data[2, 3, 0], pmax[0]) npt.assert_almost_equal(percept.data[2, 3, 1], 0) npt.assert_almost_equal(percept.data[3, 4, 0], 0) npt.assert_almost_equal(percept.data[3, 4, 1], pmax[1]) npt.assert_almost_equal(percept.time, [0, 1])
def test_Thompson2003Model(): model = Thompson2003Model(engine='serial', xystep=5) npt.assert_equal(model.has_space, True) npt.assert_equal(model.has_time, False) npt.assert_equal(hasattr(model.spatial, 'radius'), True) # User can set `radius`: model.radius = 123 npt.assert_equal(model.radius, 123) npt.assert_equal(model.spatial.radius, 123) model.build(radius=987) npt.assert_equal(model.radius, 987) npt.assert_equal(model.spatial.radius, 987) # Converting ret <=> dva npt.assert_equal(isinstance(model.retinotopy, Curcio1990Map), True) npt.assert_almost_equal(model.retinotopy.ret2dva(0, 0), (0, 0)) npt.assert_almost_equal(model.retinotopy.dva2ret(0, 0), (0, 0)) model2 = Thompson2003Model(retinotopy=Watson2014DisplaceMap()) npt.assert_equal(isinstance(model2.retinotopy, Watson2014DisplaceMap), True) # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI()), None) # Zero in = zero out: implant = ArgusI(stim=np.zeros(16)) npt.assert_almost_equal(model.predict_percept(implant).data, 0) # Multiple frames are processed independently: model = Thompson2003Model(engine='serial', radius=1000, xystep=5, xrange=(-20, 20), yrange=(-15, 15)) model.build() percept = model.predict_percept(ArgusI(stim={'A1': [1, 2]})) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [2]) pmax = percept.data.max(axis=(0, 1)) npt.assert_almost_equal(percept.data[2, 3, :], pmax) print(pmax, percept.data) npt.assert_almost_equal(pmax[1] / pmax[0], 2.0) npt.assert_almost_equal(percept.time, [0, 1])
def test_Nanduri2012Temporal(): model = Nanduri2012Temporal() # User can set their own params: model.dt = 0.1 npt.assert_equal(model.dt, 0.1) model.build(dt=1e-4) npt.assert_equal(model.dt, 1e-4) # User cannot add more model parameters: with pytest.raises(FreezeError): model.rho = 100 # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI().stim), None) # Zero in = zero out: implant = ArgusI(stim=np.zeros((16, 100))) percept = model.predict_percept(implant.stim, t_percept=[0, 1, 2]) npt.assert_equal(isinstance(percept, Percept), True) npt.assert_equal(percept.shape, (16, 1, 3)) npt.assert_almost_equal(percept.data, 0) # Can't request the same time more than once (this would break the Cython # loop, because `idx_frame` is incremented after a write; also doesn't # make much sense): with pytest.raises(ValueError): implant.stim = np.ones((16, 100)) model.predict_percept(implant.stim, t_percept=[0.2, 0.2]) # Brightness scales differently with amplitude vs frequency: model = Nanduri2012Temporal(dt=5e-3) model.build() sdur = 1000.0 # stimulus duration (ms) pdur = 0.45 # (ms) t_percept = np.arange(0, sdur, 5) implant = ProsthesisSystem(ElectrodeArray(DiskElectrode(0, 0, 0, 260))) bright_amp = [] for amp in np.linspace(0, 50, 5): # implant.stim = PulseTrain(model.dt, freq=20, amp=amp, dur=sdur, # pulse_dur=pdur, interphase_dur=pdur) implant.stim = BiphasicPulseTrain(20, amp, pdur, interphase_dur=pdur, stim_dur=sdur) percept = model.predict_percept(implant.stim, t_percept=t_percept) bright_amp.append(percept.data.max()) bright_amp_ref = [0.0, 0.00890, 0.0657, 0.1500, 0.1691] npt.assert_almost_equal(bright_amp, bright_amp_ref, decimal=3) bright_freq = [] for freq in np.linspace(0, 100, 5): # implant.stim = PulseTrain(model.dt, freq=freq, amp=20, dur=sdur, # pulse_dur=pdur, interphase_dur=pdur) implant.stim = BiphasicPulseTrain(freq, 20, pdur, interphase_dur=pdur, stim_dur=sdur) percept = model.predict_percept(implant.stim, t_percept=t_percept) bright_freq.append(percept.data.max()) bright_freq_ref = [0.0, 0.0394, 0.0741, 0.1073, 0.1385] npt.assert_almost_equal(bright_freq, bright_freq_ref, decimal=3)
def test_Model_predict_percept_correctly_parallelizes(): # setup and time spatial model with 1 thread one_thread_spatial = Model(spatial=ValidSpatialModel(n_threads=1)).build() start_time_one_thread_spatial = time.perf_counter() one_thread_spatial.predict_percept(ArgusI()) one_thread_spatial_predict_time = time.perf_counter( ) - start_time_one_thread_spatial # setup and time spatial model with 2 threads two_thread_spatial = Model(spatial=ValidSpatialModel(n_threads=2)).build() start_time_two_thread_spatial = time.perf_counter() two_thread_spatial.predict_percept(ArgusI()) two_threaded_spatial_predict_time = time.perf_counter( ) - start_time_two_thread_spatial # we expect roughly a linear decrease in time as thread count increases npt.assert_almost_equal(actual=two_threaded_spatial_predict_time, desired=one_thread_spatial_predict_time / 2, decimal=1e-5) # setup and time temporal model with 1 thread one_thread_temporal = Model(temporal=ValidTemporalModel( n_threads=1)).build() start_time_one_thread_temporal = time.perf_counter() one_thread_temporal.predict_percept(ArgusI()) one_thread_temporal_predict_time = time.perf_counter( ) - start_time_one_thread_temporal # setup and time temporal model with 2 threads two_thread_temporal = Model(temporal=ValidTemporalModel( n_threads=2)).build() start_time_two_thread_temporal = time.perf_counter() two_thread_temporal.predict_percept(ArgusI()) two_thread_temporal_predict_time = time.perf_counter( ) - start_time_two_thread_temporal # we expect roughly a linear decrease in time as thread count increases npt.assert_almost_equal(actual=two_thread_temporal_predict_time, desired=one_thread_temporal_predict_time / 2, decimal=1e-5)
def test_plot_argus_phosphenes(): df = pd.DataFrame([ { 'subject': 'S1', 'electrode': 'A1', 'image': np.random.rand(10, 10), 'img_x_dva': (-10, 10), 'img_y_dva': (-10, 10) }, { 'subject': 'S1', 'electrode': 'B2', 'image': np.random.rand(10, 10), 'img_x_dva': (-10, 10), 'img_y_dva': (-10, 10) }, ]) _, ax = plt.subplots() plot_argus_phosphenes(df, ArgusI(), ax=ax) plot_argus_phosphenes(df, ArgusII(), ax=ax) # Add axon map: _, ax = plt.subplots() plot_argus_phosphenes(df, ArgusI(), ax=ax, axon_map=AxonMapModel()) # Data must be a DataFrame: with pytest.raises(TypeError): plot_argus_phosphenes(np.ones(10), ArgusI()) # DataFrame must have the required columns: with pytest.raises(ValueError): plot_argus_phosphenes(pd.DataFrame(), ArgusI()) # Subjects must all be the same: with pytest.raises(ValueError): dff = pd.DataFrame([{'subject': 'S1'}, {'subject': 'S2'}]) plot_argus_phosphenes(dff, ArgusI()) # Works only for Argus: with pytest.raises(TypeError): plot_argus_phosphenes(df, AlphaAMS())
def test_plot_argus_phosphenes(): df = pd.DataFrame([ { 'subject': 'S1', 'electrode': 'A1', 'image': np.random.rand(10, 10), 'xrange': (-10, 10), 'yrange': (-10, 10) }, { 'subject': 'S1', 'electrode': 'B2', 'image': np.random.rand(10, 10), 'xrange': (-10, 10), 'yrange': (-10, 10) }, ]) _, ax = plt.subplots() plot_argus_phosphenes(df, ArgusI(), ax=ax) plot_argus_phosphenes(df, ArgusII(), ax=ax) # Add axon map: _, ax = plt.subplots() plot_argus_phosphenes(df, ArgusI(), ax=ax, axon_map=AxonMapModel()) # Data must be a DataFrame: with pytest.raises(TypeError): plot_argus_phosphenes(np.ones(10), ArgusI()) # DataFrame must have the required columns: with pytest.raises(ValueError): plot_argus_phosphenes(pd.DataFrame(), ArgusI()) # Subjects must all be the same: with pytest.raises(ValueError): dff = pd.DataFrame([{'subject': 'S1'}, {'subject': 'S2'}]) plot_argus_phosphenes(dff, ArgusI()) # Works only for Argus: with pytest.raises(TypeError): plot_argus_phosphenes(df, AlphaAMS()) # Works only for axon maps: with pytest.raises(TypeError): plot_argus_phosphenes(df, ArgusI(), ax=ax, axon_map=ScoreboardModel()) # Manual subject selection plot_argus_phosphenes(df[df.electrode == 'B2'], ArgusI(), ax=ax) # If no implant given, dataframe must have additional columns: with pytest.raises(ValueError): plot_argus_phosphenes(df, ax=ax) df['implant_type_str'] = 'ArgusII' df['implant_x'] = 0 df['implant_y'] = 0 df['implant_rot'] = 0 plot_argus_phosphenes(df, ax=ax)
def test_ScoreboardModel_predict_percept(): model = ScoreboardModel(xystep=0.55, rho=100, thresh_percept=0, xrange=(-20, 20), yrange=(-15, 15)) model.build() # Single-electrode stim: img_stim = np.zeros(60) img_stim[47] = 1 percept = model.predict_percept(ArgusII(stim=img_stim)) # Single bright pixel, very small Gaussian kernel: npt.assert_equal(np.sum(percept.data > 0.8), 1) npt.assert_equal(np.sum(percept.data > 0.5), 2) npt.assert_equal(np.sum(percept.data > 0.1), 7) npt.assert_equal(np.sum(percept.data > 0.00001), 32) # Brightest pixel is in lower right: npt.assert_almost_equal(percept.data[33, 46, 0], np.max(percept.data)) # Full Argus II: 60 bright spots model = ScoreboardModel(engine='serial', xystep=0.55, rho=100) model.build() percept = model.predict_percept(ArgusII(stim=np.ones(60))) npt.assert_equal(np.sum(np.isclose(percept.data, 0.8, rtol=0.1, atol=0.1)), 88) # Model gives same outcome as Spatial: spatial = ScoreboardSpatial(engine='serial', xystep=1, rho=100) spatial.build() spatial_percept = model.predict_percept(ArgusII(stim=np.ones(60))) npt.assert_almost_equal(percept.data, spatial_percept.data) npt.assert_equal(percept.time, None) # Warning for nonzero electrode-retina distances implant = ArgusI(stim=np.ones(16), z=10) msg = ("Nonzero electrode-retina distances do not have any effect on the " "model output.") assert_warns_msg(UserWarning, model.predict_percept, msg, implant)
def test_Nanduri2012Model_predict_percept(): # Nothing in = nothing out: model = Nanduri2012Model(xrange=(0, 0), yrange=(0, 0), engine='serial') model.build() implant = ArgusI(stim=None) npt.assert_equal(model.predict_percept(implant), None) implant.stim = np.zeros(16) npt.assert_almost_equal(model.predict_percept(implant).data, 0) # Single-pixel model same as TemporalModel: implant = ProsthesisSystem(DiskElectrode(0, 0, 0, 100)) # implant.stim = PulseTrain(5e-6) implant.stim = BiphasicPulseTrain(20, 20, 0.45, interphase_dur=0.45) t_percept = [0, 0.01, 1.0] percept = model.predict_percept(implant, t_percept=t_percept) temp = Nanduri2012Temporal().build() temp = temp.predict_percept(implant.stim, t_percept=t_percept) npt.assert_almost_equal(percept.data, temp.data, decimal=4) # Only works for DiskElectrode arrays: with pytest.raises(TypeError): implant = ProsthesisSystem(ElectrodeArray(PointSource(0, 0, 0))) implant.stim = 1 model.predict_percept(implant) with pytest.raises(TypeError): implant = ProsthesisSystem( ElectrodeArray( [DiskElectrode(0, 0, 0, 100), PointSource(100, 100, 0)])) implant.stim = [1, 1] model.predict_percept(implant) # Requested times must be multiples of model.dt: implant = ProsthesisSystem(ElectrodeArray(DiskElectrode(0, 0, 0, 260))) # implant.stim = PulseTrain(tsample) implant.stim = BiphasicPulseTrain(20, 20, 0.45) model.temporal.dt = 0.1 with pytest.raises(ValueError): model.predict_percept(implant, t_percept=[0.01]) with pytest.raises(ValueError): model.predict_percept(implant, t_percept=[0.01, 1.0]) with pytest.raises(ValueError): model.predict_percept(implant, t_percept=np.arange(0, 0.5, 0.101)) model.predict_percept(implant, t_percept=np.arange(0, 0.5, 1.0000001)) # Can't request the same time more than once (this would break the Cython # loop, because `idx_frame` is incremented after a write; also doesn't # make much sense): with pytest.raises(ValueError): model.predict_percept(implant, t_percept=[0.2, 0.2]) # It's ok to extrapolate beyond `stim` if the `extrapolate` flag is set: model.temporal.dt = 1e-2 npt.assert_almost_equal( model.predict_percept(implant, t_percept=10000).data, 0) # Output shape must be determined by t_percept: npt.assert_equal( model.predict_percept(implant, t_percept=0).shape, (1, 1, 1)) npt.assert_equal( model.predict_percept(implant, t_percept=[0, 1]).shape, (1, 1, 2)) # Brightness vs. size (use values from Nanduri paper): model = Nanduri2012Model(xystep=0.5, xrange=(-4, 4), yrange=(-4, 4)) model.build() implant = ProsthesisSystem(ElectrodeArray(DiskElectrode(0, 0, 0, 260))) amp_th = 30 bright_th = 0.107 stim_dur = 1000.0 pdur = 0.45 t_percept = np.arange(0, stim_dur, 5) amp_factors = [1, 6] frames_amp = [] for amp_f in amp_factors: implant.stim = BiphasicPulseTrain(20, amp_f * amp_th, pdur, interphase_dur=pdur, stim_dur=stim_dur) percept = model.predict_percept(implant, t_percept=t_percept) idx_frame = np.argmax(np.max(percept.data, axis=(0, 1))) brightest_frame = percept.data[..., idx_frame] frames_amp.append(brightest_frame) npt.assert_equal([np.sum(f > bright_th) for f in frames_amp], [0, 161]) freqs = [20, 120] frames_freq = [] for freq in freqs: implant.stim = BiphasicPulseTrain(freq, 1.25 * amp_th, pdur, interphase_dur=pdur, stim_dur=stim_dur) percept = model.predict_percept(implant, t_percept=t_percept) idx_frame = np.argmax(np.max(percept.data, axis=(0, 1))) brightest_frame = percept.data[..., idx_frame] frames_freq.append(brightest_frame) npt.assert_equal([np.sum(f > bright_th) for f in frames_freq], [21, 49])
def test_AxonMapModel_predict_percept(engine): model = AxonMapModel(xystep=0.55, axlambda=100, rho=100, thresh_percept=0, engine=engine, xrange=(-20, 20), yrange=(-15, 15), n_axons=500) model.build() # Single-electrode stim: img_stim = np.zeros(60) img_stim[47] = 1 # Axon map jax predict_percept not implemented yet if engine == 'jax': with pytest.raises(NotImplementedError): percept = model.predict_percept(ArgusII(stim=img_stim)) return percept = model.predict_percept(ArgusII(stim=img_stim)) # Single bright pixel, rest of arc is less bright: npt.assert_equal(np.sum(percept.data > 0.8), 1) npt.assert_equal(np.sum(percept.data > 0.6), 2) npt.assert_equal(np.sum(percept.data > 0.1), 7) npt.assert_equal(np.sum(percept.data > 0.0001), 31) # Overall only a few bright pixels: npt.assert_almost_equal(np.sum(percept.data), 3.3087, decimal=3) # Brightest pixel is in lower right: npt.assert_almost_equal(percept.data[33, 46, 0], np.max(percept.data)) # Top half is empty: npt.assert_almost_equal(np.sum(percept.data[:27, :, 0]), 0) # Same for lower band: npt.assert_almost_equal(np.sum(percept.data[39:, :, 0]), 0) # Full Argus II with small lambda: 60 bright spots model = AxonMapModel(engine='serial', xystep=1, rho=100, axlambda=40, xrange=(-20, 20), yrange=(-15, 15), n_axons=500) model.build() percept = model.predict_percept(ArgusII(stim=np.ones(60))) # Most spots are pretty bright, but there are 2 dimmer ones (due to their # location on the retina): npt.assert_equal(np.sum(percept.data > 0.5), 28) npt.assert_equal(np.sum(percept.data > 0.275), 56) # Model gives same outcome as Spatial: spatial = AxonMapSpatial(engine='serial', xystep=1, rho=100, axlambda=40, xrange=(-20, 20), yrange=(-15, 15), n_axons=500) spatial.build() spatial_percept = spatial.predict_percept(ArgusII(stim=np.ones(60))) npt.assert_almost_equal(percept.data, spatial_percept.data) npt.assert_equal(percept.time, None) # Warning for nonzero electrode-retina distances implant = ArgusI(stim=np.ones(16), z=10) msg = ("Nonzero electrode-retina distances do not have any effect on the " "model output.") assert_warns_msg(UserWarning, model.predict_percept, msg, implant)
with pytest.raises(ValueError): plot_argus_phosphenes(pd.DataFrame(), ArgusI()) # Subjects must all be the same: with pytest.raises(ValueError): dff = pd.DataFrame([{'subject': 'S1'}, {'subject': 'S2'}]) plot_argus_phosphenes(dff, ArgusI()) # Works only for Argus: with pytest.raises(TypeError): plot_argus_phosphenes(df, AlphaAMS()) # Works only for axon maps: with pytest.raises(TypeError): plot_argus_phosphenes(df, ArgusI(), ax=ax, axon_map=ScoreboardModel()) # Manual subject selection plot_argus_phosphenes(df[df.electrode == 'B2'], ArgusI(), ax=ax) @pytest.mark.parametrize('implant', (ArgusI(), ArgusII())) def test_plot_argus_simulated_phosphenes(implant): implant.stim = {'A1': [1, 0, 0], 'B2': [0, 1, 0], 'C3': [0, 0, 1]} percepts = ScoreboardModel().build().predict_percept(implant) plot_argus_simulated_phosphenes(percepts, implant) # Add axon map: _, ax = plt.subplots() plot_argus_simulated_phosphenes(percepts, implant, ax=ax, axon_map=AxonMapModel())
def test_biphasicAxonMapSpatial(engine): # Lambda cannot be too small: with pytest.raises(ValueError): BiphasicAxonMapSpatial(axlambda=9).build() model = BiphasicAxonMapModel(engine=engine, xystep=2).build() # Jax not implemented yet if engine == 'jax': with pytest.raises(NotImplementedError): implant = ArgusII() implant.stim = Stimulus({'A5': BiphasicPulseTrain(20, 1, 0.45)}) percept = model.predict_percept(implant) return # Only accepts biphasic pulse trains with no delay dur implant = ArgusI(stim=np.ones(16)) with pytest.raises(TypeError): model.predict_percept(implant) # Nothing in, None out: npt.assert_equal(model.predict_percept(ArgusI()), None) # Zero in = zero out: implant = ArgusI(stim=np.zeros(16)) percept = model.predict_percept(implant) npt.assert_equal(isinstance(percept, Percept), True) npt.assert_equal(percept.shape, list(model.grid.x.shape) + [1]) npt.assert_almost_equal(percept.data, 0) npt.assert_equal(percept.time, None) # Should be equal to axon map model if effects models return 1 model = BiphasicAxonMapSpatial(engine=engine, xystep=2) def bright_model(freq, amp, pdur): return 1 def size_model(freq, amp, pdur): return 1 def streak_model(freq, amp, pdur): return 1 model.bright_model = bright_model model.size_model = size_model model.streak_model = streak_model model.build() axon_map = AxonMapSpatial(xystep=2).build() implant = ArgusII() implant.stim = Stimulus({'A5': BiphasicPulseTrain(20, 1, 0.45)}) percept = model.predict_percept(implant) percept_axon = axon_map.predict_percept(implant) npt.assert_almost_equal(percept.data[:, :, 0], percept_axon.get_brightest_frame()) # Effect models must be callable model = BiphasicAxonMapSpatial(engine=engine, xystep=2) model.bright_model = 1.0 with pytest.raises(TypeError): model.build() # If t_percept is not specified, there should only be one frame model = BiphasicAxonMapSpatial(engine=engine, xystep=2) model.build() implant = ArgusII() implant.stim = Stimulus({'A5': BiphasicPulseTrain(20, 1, 0.45)}) percept = model.predict_percept(implant) npt.assert_equal(percept.time is None, True) # If t_percept is specified, only first frame should have data # and the rest should be empty percept = model.predict_percept(implant, t_percept=[0, 1, 2, 5, 10]) npt.assert_equal(len(percept.time), 5) npt.assert_equal(np.any(percept.data[:, :, 0]), True) npt.assert_equal(np.any(percept.data[:, :, 1:]), False) # Test that default models give expected values model = BiphasicAxonMapSpatial(engine=engine, rho=400, axlambda=600, xystep=1, xrange=(-20, 20), yrange=(-15, 15)) model.build() implant = ArgusII() implant.stim = Stimulus({'A4': BiphasicPulseTrain(20, 1, 1)}) percept = model.predict_percept(implant) npt.assert_equal(np.sum(percept.data > 0.0813), 81) npt.assert_equal(np.sum(percept.data > 0.1626), 59) npt.assert_equal(np.sum(percept.data > 0.2439), 44) npt.assert_equal(np.sum(percept.data > 0.4065), 26) npt.assert_equal(np.sum(percept.data > 0.5691), 14)