From 64098997ad3e3775010e23e91fe87a36b5d0aba4 Mon Sep 17 00:00:00 2001 From: "shinya.ito" Date: Wed, 26 Jun 2024 14:12:35 -0700 Subject: [PATCH 1/3] Fixing FilterNet behavior for frame_rate != 1000.0 --- bmtk/simulator/filternet/lgnmodel/cursor.py | 4 ++++ .../filternet/lgnmodel/test_units.py | 23 ++++++++++++------- .../filternet/test_default_setters.py | 17 ++++++++++---- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/bmtk/simulator/filternet/lgnmodel/cursor.py b/bmtk/simulator/filternet/lgnmodel/cursor.py index 24f4f3ca1..42fcc9521 100644 --- a/bmtk/simulator/filternet/lgnmodel/cursor.py +++ b/bmtk/simulator/filternet/lgnmodel/cursor.py @@ -76,6 +76,8 @@ def evaluate(self, t_min=None, t_max=None, downsample=1): t_range = convert_tmin_tmax_framerate_to_trange(t_min, t_max, self.movie.frame_rate)[::int(downsample)] y_vals = np.array([self(t) for t in t_range]) + # normalize by the frame rate + y_vals = y_vals * 1000.0 / self.movie.frame_rate return t_range, y_vals @@ -240,6 +242,8 @@ def evaluate(self, threshold=0): sig_tmp = np.zeros(len(full_temporal_kernel) + len(convolution_answer_sep_spatial) - 1) sig_tmp[len(full_temporal_kernel)-1:] = convolution_answer_sep_spatial convolution_answer_sep = spsig.convolve(sig_tmp, full_temporal_kernel[::-1], mode='valid') + # normalize by the frame rate + convolution_answer_sep = convolution_answer_sep * 1000.0 / self.movie.frame_rate t = np.arange(len(convolution_answer_sep))/self.movie.frame_rate return t, convolution_answer_sep diff --git a/bmtk/tests/simulator/filternet/lgnmodel/test_units.py b/bmtk/tests/simulator/filternet/lgnmodel/test_units.py index 049b27bf2..a20494508 100644 --- a/bmtk/tests/simulator/filternet/lgnmodel/test_units.py +++ b/bmtk/tests/simulator/filternet/lgnmodel/test_units.py @@ -10,9 +10,13 @@ from bmtk.simulator.filternet.lgnmodel.lgnmodel1 import LGNModel from bmtk.simulator.filternet.lgnmodel import movie +# FilterNet now correctly normalize the rate other than 1000 Hz. +# The tests below were made before the change, so we correctionso on the +# firing rates. +norm_factor = 1000.0 / 24.0 def test_onunit(): - ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7) + ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7, frame_rate=24.0) mv = ffm.full(t_max=2.0) spatial_filter = GaussianSpatialFilter(translate=(120.0, 60.0), sigma=(0.615, 0.615), origin=(0.0, 0.0)) @@ -29,11 +33,11 @@ def test_onunit(): rates = np.array(results[0][1], dtype=np.float64) assert(np.allclose(times, [0.0, 0.41666, 0.83333, 1.250, 1.6666], atol=1.0e-4)) - assert(np.allclose(rates, [1.05, 0.8635, 1.05, 1.05, 1.05], atol=1.0e-3)) + assert(np.allclose(rates / norm_factor, [1.05, 0.8635, 1.05, 1.05, 1.05], atol=1.0e-3)) def test_offunit(): - ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7) + ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7, frame_rate=24.0) mv = ffm.full(t_max=2.0) spatial_filter = GaussianSpatialFilter(translate=(120.0, 60.0), sigma=(0.615, 0.615), origin=(0.0, 0.0)) @@ -50,11 +54,11 @@ def test_offunit(): rates = np.array(results[0][1], dtype=np.float64) assert(np.allclose(times, [0.0, 0.41666, 0.83333, 1.250, 1.6666], atol=1.0e-4)) - assert(np.allclose(rates, [1.05, 1.2364, 1.05, 1.05, 1.05], atol=1.0e-3)) + assert(np.allclose(rates / norm_factor, [1.05, 1.2364, 1.05, 1.05, 1.05], atol=1.0e-3)) def test_lgnonoffcell(): - ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7) + ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7, frame_rate=24.0) mv = ffm.full(t_max=2.0) temporal_filter = TemporalFilterCosineBump(weights=[3.441, -2.115], kpeaks=[8.269, 19.991], delays=[0.0, 0.0]) @@ -74,11 +78,11 @@ def test_lgnonoffcell(): rates = np.array(results[0][1], dtype=np.float64) assert(np.allclose(times, [0.0, 0.41666, 0.83333, 1.250, 1.6666], atol=1.0e-4)) - assert(np.allclose(rates, [0.0, 3.7286, 0.0, 0.0, 0.0], atol=1.0e-3)) + assert(np.allclose(rates / norm_factor, [0.0, 3.7286, 0.0, 0.0, 0.0], atol=1.0e-3)) def test_twosubfieldlinearcell(): - ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7) + ffm = movie.FullFieldFlashMovie(range(120), range(240), 0.3, 0.7, frame_rate=24.0) mv = ffm.full(t_max=2.0) spatial_filter = GaussianSpatialFilter(translate=(120.0, 60.0), sigma=(0.615, 0.615), origin=(0.0, 0.0)) @@ -107,7 +111,10 @@ def test_twosubfieldlinearcell(): rates = np.array(results[0][1], dtype=np.float64) assert(np.allclose(times, [0.0, 0.41666, 0.83333, 1.250, 1.6666], atol=1.0e-4)) - assert(np.allclose(rates, [4.0, 3.26931, 3.885, 4.0, 4.0], atol=1.0e-3)) + # for this example, the offset (2.0) of the transfer function breaks the + # compatibility. It makes sense to update the values. + # assert(np.allclose(rates, [4.0, 3.26931, 3.885, 4.0, 4.0], atol=1.0e-3)) + assert(np.allclose(rates, [4.0, 18.929, 11.894, 4.0, 4.0], atol=1.0e-3)) if __name__ == '__main__': diff --git a/bmtk/tests/simulator/filternet/test_default_setters.py b/bmtk/tests/simulator/filternet/test_default_setters.py index ac29f013d..012f9c792 100644 --- a/bmtk/tests/simulator/filternet/test_default_setters.py +++ b/bmtk/tests/simulator/filternet/test_default_setters.py @@ -55,6 +55,10 @@ def __contains__(self, item): np.set_printoptions(precision=4) +# FilterNet now correctly normalize the rate other than 1000 Hz. +# The tests below were made before the change, so we correctionso on the +# firing rates. +norm_factor = 1000.0 / 24.0 @pytest.mark.parametrize("cell_type,expected_val", [ ('tON_TF8', [2.6, 2.5502, 2.7221, 2.7718, 2.6497]), @@ -77,7 +81,7 @@ def test_onunit(cell_type, expected_val): rates = np.array(results[0][1], dtype=np.float64) assert(np.allclose(times, [0.0, 0.2083, 0.4167, 0.6250, 0.8333], atol=1.0e-3)) - assert(np.allclose(rates, expected_val, atol=1.0e-3)) + assert(np.allclose(rates / norm_factor, expected_val, atol=1.0e-3)) @pytest.mark.parametrize("cell_type,expected_val", [ @@ -105,12 +109,15 @@ def test_offunit(cell_type, expected_val): rates = np.array(results[0][1], dtype=np.float64) assert(np.allclose(times, [0.0, 0.2083, 0.4167, 0.6250, 0.8333], atol=1.0e-3)) - assert(np.allclose(rates, expected_val, atol=1.0e-3)) + assert(np.allclose(rates / norm_factor, expected_val, atol=1.0e-3)) @pytest.mark.parametrize("cell_type,expected_val", [ # ('sONsOFF_001', [4.0, 3.5654, 2.2956, 2.7437, 4.4480]) - ('sONsOFF_001', [4.0, 3.0136, 3.335, 4.3349, 4.9999]) # updated value after OS bug fix. (issue, #339) + # ('sONsOFF_001', [4.0, 3.0136, 3.335, 4.3349, 4.9999]) # updated value after OS bug fix. (issue, #339) + # Again, due to the offset in the transfer function, the expected values + # won't nicely convert, so updating the values. + ('sONsOFF_001', [4.0, 20.1642, 0, 45.4373, 61.2649]) # updated value after OS bug fix. (issue, #339) ]) def test_sONsOFF(cell_type, expected_val): gm = movie.GratingMovie(row_size=120, col_size=240, frame_rate=24.0) @@ -130,7 +137,9 @@ def test_sONsOFF(cell_type, expected_val): @pytest.mark.parametrize("cell_type,expected_val", [ # ('sONsOFF_001', [4.0, 3.5654, 2.2957, 2.7437, 4.4481]) - ('sONsOFF_001', [4.0, 3.0136, 3.335, 4.3349, 4.9999]) # updated value after OS bug fix. (issue, #339) + # ('sONsOFF_001', [4.0, 3.0136, 3.335, 4.3349, 4.9999]) # updated value after OS bug fix. (issue, #339) + ('sONsOFF_001', [4.0, 20.1642, 0, 45.4373, 61.2649]) # updated value after OS bug fix. (issue, #339) + ]) def test_sONtOFF(cell_type, expected_val): gm = movie.GratingMovie(row_size=120, col_size=240, frame_rate=24.0) From c08acacb6380ec63df403ac80e8d5975f4da2dab Mon Sep 17 00:00:00 2001 From: Kael Dai Date: Mon, 8 Jul 2024 10:44:00 -0700 Subject: [PATCH 2/3] Update version to 1.1.1 Updating for a new release --- bmtk/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bmtk/__init__.py b/bmtk/__init__.py index eb1553a5d..567950c3c 100644 --- a/bmtk/__init__.py +++ b/bmtk/__init__.py @@ -20,4 +20,4 @@ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -__version__ = '1.1.0' +__version__ = '1.1.1' From a5dcedbaa8de7b0a2ca1b5e2291b96095bc4717e Mon Sep 17 00:00:00 2001 From: "shinya.ito" Date: Thu, 29 Aug 2024 14:45:17 -0700 Subject: [PATCH 3/3] Allowing grating contrast to be 0. --- bmtk/simulator/filternet/lgnmodel/movie.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bmtk/simulator/filternet/lgnmodel/movie.py b/bmtk/simulator/filternet/lgnmodel/movie.py index 94fbf0354..acee5e7b3 100755 --- a/bmtk/simulator/filternet/lgnmodel/movie.py +++ b/bmtk/simulator/filternet/lgnmodel/movie.py @@ -271,7 +271,7 @@ def create_movie(self, t_min=0, t_max=1, gray_screen_dur=0, cpd=0.05, temporal_f :return: Movie object of grating with desired parameters """ assert contrast <= 1, "Contrast must be <= 1" - assert contrast > 0, "Contrast must be > 0" + assert contrast >= 0, "Contrast must be >= 0" if degrees_per_pixel is None: # default behavior when not specified degrees_per_pixel = 1.0 / (float(cpd) * 10) # To make sure no aliasing occurs