def test_FadingTemporal(): model = FadingTemporal() # 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(None), None) # Zero in = zero out: stim = BiphasicPulse(0, 1) percept = model.predict_percept(stim, t_percept=[0, 1, 2]) npt.assert_equal(isinstance(percept, Percept), True) npt.assert_equal(percept.shape, (1, 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): stim = Stimulus(np.ones((1, 100))) model.predict_percept(stim, t_percept=[0.2, 0.2]) # Simple decay for single cathodic pulse: model = FadingTemporal(tau=1).build() stim = MonophasicPulse(-1, 1, stim_dur=10) percept = model.predict_percept(stim, np.arange(stim.duration)) npt.assert_almost_equal(percept.data.ravel()[:3], [0, 0.633, 0.232], decimal=3) npt.assert_almost_equal(percept.data.ravel()[-1], 0, decimal=3) # But all zeros for anodic pulse: stim = MonophasicPulse(1, 1, stim_dur=10) percept = model.predict_percept(stim, np.arange(stim.duration)) npt.assert_almost_equal(percept.data, 0) # tau cannot be negative: with pytest.raises(ValueError): FadingTemporal(tau=-1).build()
def test_MonophasicPulse(amp, delay_dur): phase_dur = 3.456 # Basic usage: pulse = MonophasicPulse(amp, phase_dur, delay_dur=delay_dur) npt.assert_almost_equal(pulse[0, 0], 0) npt.assert_almost_equal(pulse[0, delay_dur + phase_dur / 2.0], amp) npt.assert_almost_equal(pulse.time[0], 0) npt.assert_almost_equal(pulse.time[-1], phase_dur + delay_dur) npt.assert_equal(pulse.cathodic, amp <= 0) npt.assert_equal(pulse.charge_balanced, False) # Custom stim dur: pulse = MonophasicPulse(amp, phase_dur, delay_dur=delay_dur, stim_dur=100) npt.assert_almost_equal(pulse[0, 0], 0) npt.assert_almost_equal(pulse[0, delay_dur + phase_dur / 2.0], amp) npt.assert_almost_equal(pulse.time[0], 0) npt.assert_almost_equal(pulse.time[-1], 100) # Zero amplitude: pulse = MonophasicPulse(0, phase_dur, delay_dur=delay_dur, electrode='A1') npt.assert_almost_equal(pulse.data, 0) npt.assert_almost_equal(pulse.time[0], 0) npt.assert_almost_equal(pulse.time[-1], phase_dur + delay_dur) npt.assert_equal(pulse.charge_balanced, True) npt.assert_equal(pulse.electrodes, 'A1') # You can wrap a pulse in a Stimulus to overwrite attributes: stim = Stimulus(pulse, electrodes='AA1') npt.assert_equal(stim.electrodes, 'AA1') # Or concatenate: stim = Stimulus([pulse, pulse]) npt.assert_equal(stim.shape[0], 2) npt.assert_almost_equal(stim.data[0, :], stim.data[1, :]) npt.assert_almost_equal(stim.time, pulse.time) npt.assert_equal(stim.electrodes, ['A1', 1]) # Concatenate and rename: stim = Stimulus([pulse, pulse], electrodes=['C1', 'D2']) npt.assert_equal(stim.electrodes, ['C1', 'D2']) # Invalid calls: with pytest.raises(ValueError): MonophasicPulse(amp, 0) with pytest.raises(ValueError): MonophasicPulse(amp, phase_dur, delay_dur=-1) with pytest.raises(ValueError): MonophasicPulse(amp, phase_dur, delay_dur=delay_dur, stim_dur=1) with pytest.raises(ValueError): MonophasicPulse(amp, phase_dur, delay_dur=delay_dur, electrode=['A1', 'B2'])
# They are pseudo-monophasic pulse pairs, where the anodic phases were # presented 20 ms after the end of the second cathodic pulse. # # The initial cathodic pulse always has a fixed amplitude of 50% of the single # pulse threshold: from pulse2percept.stimuli import MonophasicPulse # Phase duration: phase_dur = 0.075 # Single-pulse threshold determines this current: amp_th = 20 # Cathodic phase of the standard pulse:: cath_standard = MonophasicPulse(-0.5 * amp_th, phase_dur) ############################################################################### # The delay between the start of the conditioning pulse and the start of the # test pulse was varied systematically (between 0.15 and 12 ms). # The amplitude of the second pulse was varied to determine thresholds. # Delay was varied between 0.15 and 12 ms: delay_dur = 12 # Vary this current to determine threshold: amp_test = 45 # Cathodic phase of the test pulse (delivered after a delay): cath_test = MonophasicPulse(-amp_test, phase_dur, delay_dur=delay_dur)
def test_pulse_append(amp, phase_dur): # Build a biphasic pulse from two monophasic pulses: mono = MonophasicPulse(amp, phase_dur) bi = BiphasicPulse(amp, phase_dur) npt.assert_equal(mono.append(-mono) == bi, True)
def test_AsymmetricBiphasicPulse(amp1, amp2, interphase_dur, delay_dur, cathodic_first): phase_dur1 = 2.1 phase_dur2 = 4.87 mid_first_pulse = delay_dur + phase_dur1 / 2.0 mid_interphase = delay_dur + phase_dur1 + interphase_dur / 2.0 mid_second_pulse = delay_dur + phase_dur1 + interphase_dur + phase_dur2 / 2 first_amp = -np.abs(amp1) if cathodic_first else np.abs(amp1) second_amp = np.abs(amp2) if cathodic_first else -np.abs(amp2) min_dur = delay_dur + phase_dur1 + interphase_dur + phase_dur2 # Basic usage: pulse = AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=delay_dur, cathodic_first=cathodic_first) npt.assert_almost_equal(pulse[0, 0], 0) npt.assert_almost_equal(pulse[0, mid_first_pulse], first_amp) npt.assert_almost_equal(pulse[0, mid_interphase], 0) npt.assert_almost_equal(pulse[0, mid_second_pulse], second_amp) npt.assert_almost_equal(pulse.time[0], 0) npt.assert_almost_equal(pulse.time[-1], min_dur, decimal=3) npt.assert_equal(pulse.cathodic_first, cathodic_first) npt.assert_equal(pulse.is_charge_balanced, np.isclose(np.trapz(pulse.data, pulse.time)[0], 0)) # Custom stim dur: pulse = AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=delay_dur, cathodic_first=cathodic_first, stim_dur=100, electrode='A1') npt.assert_almost_equal(pulse[0, 0], 0) npt.assert_almost_equal(pulse[0, mid_first_pulse], first_amp) npt.assert_almost_equal(pulse[0, mid_interphase], 0) npt.assert_almost_equal(pulse[0, mid_second_pulse], second_amp) npt.assert_almost_equal(pulse.time[0], 0) npt.assert_almost_equal(pulse.time[-1], 100) npt.assert_equal(pulse.electrodes, 'A1') # Exact stim dur: stim_dur = delay_dur + phase_dur1 + interphase_dur + phase_dur2 pulse = AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=delay_dur, cathodic_first=cathodic_first, stim_dur=stim_dur, electrode='A1') npt.assert_almost_equal(pulse.time[0], 0) npt.assert_almost_equal(pulse.time[-1], stim_dur, decimal=6) # Zero amplitude: pulse = AsymmetricBiphasicPulse(0, 0, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=delay_dur, cathodic_first=cathodic_first) npt.assert_almost_equal(pulse.data, 0) npt.assert_almost_equal(pulse.time[0], 0) npt.assert_almost_equal(pulse.time[-1], min_dur, decimal=3) npt.assert_equal(pulse.is_charge_balanced, np.isclose(np.trapz(pulse.data, pulse.time)[0], 0)) # If both phases have the same values, it's basically a symmetric biphasic # pulse: abp = AsymmetricBiphasicPulse(amp1, amp1, phase_dur1, phase_dur1, interphase_dur=interphase_dur, delay_dur=delay_dur, cathodic_first=cathodic_first) bp = BiphasicPulse(amp1, phase_dur1, interphase_dur=interphase_dur, delay_dur=delay_dur, cathodic_first=cathodic_first) bp_min_dur = phase_dur1 * 2 + interphase_dur + delay_dur npt.assert_almost_equal(abp[:, np.linspace(0, bp_min_dur, num=5)], bp[:, np.linspace(0, bp_min_dur, num=5)]) npt.assert_equal(abp.cathodic_first, bp.cathodic_first) # If one phase is zero, it's basically a monophasic pulse: abp = AsymmetricBiphasicPulse(amp1, 0, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=delay_dur, cathodic_first=cathodic_first) mono = MonophasicPulse(first_amp, phase_dur1, delay_dur=delay_dur, stim_dur=min_dur) npt.assert_almost_equal(abp[:, np.linspace(0, min_dur, num=5)], mono[:, np.linspace(0, min_dur, num=5)]) npt.assert_equal(abp.cathodic_first, mono.cathodic) # You can wrap a pulse in a Stimulus to overwrite attributes: stim = Stimulus(pulse, electrodes='AA1') npt.assert_equal(stim.electrodes, 'AA1') # Or concatenate: stim = Stimulus([pulse, pulse]) npt.assert_equal(stim.shape[0], 2) npt.assert_almost_equal(stim.data[0, :], stim.data[1, :]) npt.assert_almost_equal(stim.time, pulse.time, decimal=2) npt.assert_equal(stim.electrodes, [0, 1]) # Concatenate and rename: stim = Stimulus([pulse, pulse], electrodes=['C1', 'D2']) npt.assert_equal(stim.electrodes, ['C1', 'D2']) # Invalid calls: with pytest.raises(ValueError): AsymmetricBiphasicPulse(amp1, amp2, 0, phase_dur2) with pytest.raises(ValueError): AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, 0) with pytest.raises(ValueError): AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=-1) with pytest.raises(ValueError): AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=-1) with pytest.raises(ValueError): AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=delay_dur, stim_dur=1) with pytest.raises(ValueError): AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=interphase_dur, delay_dur=delay_dur, electrode=['A1', 'B2'])
pulse_type = 'anodic' # anodic: positive amplitude, cathodic: negative pulse_dur = 4.6 / 1000 # pulse duration in seconds delay_dur = 10.0 / 1000 # pulse delivered after delay in seconds stim_dur = 0.5 # stimulus duration in seconds (pulse padded with zeros) time_step = 0.1 / 1000 # temporal sampling step in seconds ############################################################################## # The sampling step ``time_step`` defines at which temporal resolution the # stimulus is resolved. In the above example, the time step is 0.1 ms. # # By calling Stimulus with a ``MonophasicPulse`` source, we can generate a # single pulse: monophasic_stim = Stimulus( MonophasicPulse(ptype=pulse_type, pdur=pulse_dur, delay_dur=delay_dur, stim_dur=stim_dur, tsample=time_step)) print(monophasic_stim) ############################################################################## # Here, ``data`` is a 2D NumPy array where rows are electrodes and columns are # the points in time. Since we did not specify any electrode names in # ``MonophasicPulse``, the number of electrodes is inferred from the input # source type. There is only one row in the above example, denoting a single # electrode. # # By default, the :py:class:`~pulse2percept.stimuli.MonophasicPulse` object # automatically assumes a current amplitude of 1 uA. ##############################################################################
------------------ A :py:class:`~pulse2percept.stimuli.MonophasicPulse` has a single phase and can be either anodic (by definition: has a positive current amplitude) or cathodic (negative current amplitude). Monophasic pulses require an amplitude (in uA) and a phase duration (in ms). You can also specify the total stimulus duration: zeros will be inserted after the pulse up to the desired duration: """ # sphinx_gallery_thumbnail_number = 6 from pulse2percept.stimuli import MonophasicPulse mono = MonophasicPulse(-20, 1, stim_dur=50) mono.plot() ############################################################################## # .. note :: # # The sign of ``amp`` will determine whether the pulse is cathodic # (negative current) or anodic (positive current). # # A (symmetric) biphasic pulse # ---------------------------- # # A :py:class:`~pulse2percept.stimuli.BiphasicPulse` consists of a cathodic and # an anodic phase, optionally separated by an interphase gap. # Both cathodic and anodic phases will have the same duration ("symmetric"). #