From 0fd5c90fbdb0c9d6d1ec4cc1cc46a83b479e33e7 Mon Sep 17 00:00:00 2001 From: SveaMeyer13 Date: Mon, 14 Aug 2023 12:03:08 +0200 Subject: [PATCH 1/7] ndarray as input weights for Sparse --- src/lava/proc/sparse/process.py | 27 +++++++++----- tests/lava/proc/sparse/test_process.py | 50 +++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/lava/proc/sparse/process.py b/src/lava/proc/sparse/process.py index d8c52c7ce..9c646e74c 100644 --- a/src/lava/proc/sparse/process.py +++ b/src/lava/proc/sparse/process.py @@ -3,7 +3,7 @@ # See: https://spdx.org/licenses/ import numpy as np -from scipy.sparse import spmatrix +from scipy.sparse import spmatrix, csr_matrix import typing as ty from lava.magma.core.process.process import AbstractProcess, LogConfig @@ -21,7 +21,8 @@ class Sparse(AbstractProcess): Parameters ---------- - weights : scipy.sparse.spmatrix + weights : scipy.sparse.spmatrix or np.ndarray + both get directly converted to a csr_matrix 2D connection weight matrix as sparse matrix of form (num_flat_output_neurons, num_flat_input_neurons). @@ -57,7 +58,7 @@ class Sparse(AbstractProcess): """ def __init__(self, *, - weights: spmatrix, + weights: ty.Union[spmatrix, np.ndarray], name: ty.Optional[str] = None, num_message_bits: ty.Optional[int] = 0, log_config: ty.Optional[LogConfig] = None, @@ -69,7 +70,10 @@ def __init__(self, **kwargs) # Transform weights to csr matrix - weights = weights.tocsr() + if type(weights) == np.ndarray: + weights = csr_matrix(weights) + else: + weights = weights.tocsr() shape = weights.shape @@ -90,7 +94,8 @@ class LearningSparse(LearningConnectionProcess, Sparse): Parameters ---------- - weights : scipy.sparse.spmatrix + weights : scipy.sparse.spmatrix or np.ndarray + both get directly converted to a csr_matrix 2D connection weight matrix as sparse matrix of form (num_flat_output_neurons, num_flat_input_neurons). @@ -150,7 +155,7 @@ class LearningSparse(LearningConnectionProcess, Sparse): """ def __init__(self, *, - weights: spmatrix, + weights: ty.Union[spmatrix, np.ndarray], name: ty.Optional[str] = None, num_message_bits: ty.Optional[int] = 0, log_config: ty.Optional[LogConfig] = None, @@ -172,7 +177,10 @@ def __init__(self, **kwargs) # Transform weights to csr matrix - weights = weights.tocsr() + if type(weights) == np.ndarray: + weights = csr_matrix(weights) + else: + weights = weights.tocsr() shape = weights.shape @@ -189,7 +197,7 @@ def __init__(self, class DelaySparse(Sparse): def __init__(self, *, - weights: spmatrix, + weights: ty.Union[spmatrix, np.ndarray], delays: ty.Union[spmatrix, int], max_delay: ty.Optional[int] = 0, name: ty.Optional[str] = None, @@ -201,7 +209,8 @@ def __init__(self, Parameters ---------- - weights : spmatrix + weights : scipy.sparse.spmatrix or np.ndarray + both get directly converted to a csr_matrix 2D connection weight matrix of form (num_flat_output_neurons, num_flat_input_neurons) in C-order (row major). diff --git a/tests/lava/proc/sparse/test_process.py b/tests/lava/proc/sparse/test_process.py index e26c22ae8..d5826c929 100644 --- a/tests/lava/proc/sparse/test_process.py +++ b/tests/lava/proc/sparse/test_process.py @@ -15,7 +15,6 @@ class TestFunctions(unittest.TestCase): """Test helper function for Sparse""" def test_find_with_explicit_zeros(self): - mat = np.random.randint(-10, 10, (3, 5)) spmat = csr_matrix(mat) spmat.data[0] = 0 @@ -132,3 +131,52 @@ def test_validate_nonzero_delays(self): DelaySparse, weights=weights_sparse, delays=delays_sparse) + + def test_ndarray_gets_converted_into_sparse(self): + """Tests if ndarray weights get converted to a spmatrix""" + + shape = (3, 2) + weights = np.random.random(shape) + + conn = Sparse(weights=weights) + + self.assertIsInstance(conn.weights.get(), spmatrix) + np.testing.assert_array_equal(conn.weights.get().toarray(), weights) + + def test_ndarray_gets_converted_into_sparse_for_learning(self): + """Tests if ndarray weights get converted to a spmatrix for + LearingSparse""" + + shape = (3, 2) + weights = np.random.random(shape) + + learning_rule = STDPLoihi( + learning_rate=1, + A_plus=1, + A_minus=-2, + tau_plus=10, + tau_minus=10, + t_epoch=2, + ) + + conn = LearningSparse(weights=weights, learning_rule=learning_rule) + + self.assertIsInstance(conn.weights.get(), spmatrix) + np.testing.assert_array_equal(conn.weights.get().toarray(), weights) + + def test_ndarray_gets_converted_into_sparse_for_delay(self): + """Tests if ndarray weights get converted to a spmatrix for + DelaySparse""" + + shape = (3, 2) + weights = np.random.random(shape) + delays = np.random.randint(0, 3, shape) + + conn = DelaySparse(weights=weights, delays=delays) + + self.assertIsInstance(conn.weights.get(), spmatrix) + np.testing.assert_array_equal(conn.weights.get().toarray(), weights) + + +if __name__ == '__main__': + unittest.main() From 3934029ddb3e4d3ded86a281792d7c3ec20af6a1 Mon Sep 17 00:00:00 2001 From: SveaMeyer13 Date: Mon, 14 Aug 2023 12:07:58 +0200 Subject: [PATCH 2/7] docs --- src/lava/proc/sparse/process.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lava/proc/sparse/process.py b/src/lava/proc/sparse/process.py index 9c646e74c..a814becd9 100644 --- a/src/lava/proc/sparse/process.py +++ b/src/lava/proc/sparse/process.py @@ -23,7 +23,7 @@ class Sparse(AbstractProcess): ---------- weights : scipy.sparse.spmatrix or np.ndarray both get directly converted to a csr_matrix - 2D connection weight matrix as sparse matrix of form + 2D connection weight matrix of form (num_flat_output_neurons, num_flat_input_neurons). weight_exp : int, optional @@ -96,7 +96,7 @@ class LearningSparse(LearningConnectionProcess, Sparse): ---------- weights : scipy.sparse.spmatrix or np.ndarray both get directly converted to a csr_matrix - 2D connection weight matrix as sparse matrix of form + 2D connection weight matrix of form (num_flat_output_neurons, num_flat_input_neurons). weight_exp : int, optional From cf0010850faeb6272faf2bcf6bda0d551a9f8e8a Mon Sep 17 00:00:00 2001 From: SveaMeyer13 Date: Mon, 14 Aug 2023 12:18:38 +0200 Subject: [PATCH 3/7] codacy --- src/lava/proc/sparse/process.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lava/proc/sparse/process.py b/src/lava/proc/sparse/process.py index a814becd9..14ae603da 100644 --- a/src/lava/proc/sparse/process.py +++ b/src/lava/proc/sparse/process.py @@ -70,7 +70,7 @@ def __init__(self, **kwargs) # Transform weights to csr matrix - if type(weights) == np.ndarray: + if isinstance(weights, np.ndarray): weights = csr_matrix(weights) else: weights = weights.tocsr() @@ -177,7 +177,7 @@ def __init__(self, **kwargs) # Transform weights to csr matrix - if type(weights) == np.ndarray: + if isinstance(weights, np.ndarray): weights = csr_matrix(weights) else: weights = weights.tocsr() From 058693602e3c1ed33897cb9403e8a2b10045cdc5 Mon Sep 17 00:00:00 2001 From: SveaMeyer13 Date: Thu, 17 Aug 2023 09:29:24 +0200 Subject: [PATCH 4/7] remove implementation details from docstring and from tests --- src/lava/proc/sparse/process.py | 3 --- tests/lava/proc/sparse/test_process.py | 22 ++++++++-------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/lava/proc/sparse/process.py b/src/lava/proc/sparse/process.py index 14ae603da..a4c7bfb45 100644 --- a/src/lava/proc/sparse/process.py +++ b/src/lava/proc/sparse/process.py @@ -22,7 +22,6 @@ class Sparse(AbstractProcess): Parameters ---------- weights : scipy.sparse.spmatrix or np.ndarray - both get directly converted to a csr_matrix 2D connection weight matrix of form (num_flat_output_neurons, num_flat_input_neurons). @@ -95,7 +94,6 @@ class LearningSparse(LearningConnectionProcess, Sparse): Parameters ---------- weights : scipy.sparse.spmatrix or np.ndarray - both get directly converted to a csr_matrix 2D connection weight matrix of form (num_flat_output_neurons, num_flat_input_neurons). @@ -210,7 +208,6 @@ def __init__(self, Parameters ---------- weights : scipy.sparse.spmatrix or np.ndarray - both get directly converted to a csr_matrix 2D connection weight matrix of form (num_flat_output_neurons, num_flat_input_neurons) in C-order (row major). diff --git a/tests/lava/proc/sparse/test_process.py b/tests/lava/proc/sparse/test_process.py index d5826c929..d4ad980cb 100644 --- a/tests/lava/proc/sparse/test_process.py +++ b/tests/lava/proc/sparse/test_process.py @@ -40,7 +40,6 @@ def test_init(self): conn = Sparse(weights=weights_sparse) - self.assertIsInstance(conn.weights.init, spmatrix) np.testing.assert_array_equal(conn.weights.init.toarray(), weights) @@ -71,7 +70,6 @@ def test_init(self): conn = LearningSparse(weights=weights_sparse, learning_rule=learning_rule) - self.assertIsInstance(conn.weights.init, spmatrix) np.testing.assert_array_equal(conn.weights.init.toarray(), weights) @@ -94,7 +92,6 @@ def test_init(self): conn = DelaySparse(weights=weights_sparse, delays=delays_sparse) - self.assertIsInstance(conn.weights.init, spmatrix) np.testing.assert_array_equal(conn.weights.init.toarray(), weights) def test_validate_shapes(self): @@ -132,20 +129,20 @@ def test_validate_nonzero_delays(self): weights=weights_sparse, delays=delays_sparse) - def test_ndarray_gets_converted_into_sparse(self): - """Tests if ndarray weights get converted to a spmatrix""" + def test_init_of_sparse_with_ndarray(self): + """Tests instantiation of Sparse with ndarray as + weights""" shape = (3, 2) weights = np.random.random(shape) conn = Sparse(weights=weights) - self.assertIsInstance(conn.weights.get(), spmatrix) np.testing.assert_array_equal(conn.weights.get().toarray(), weights) - def test_ndarray_gets_converted_into_sparse_for_learning(self): - """Tests if ndarray weights get converted to a spmatrix for - LearingSparse""" + def test_init_of_learningsparse_with_ndarray(self): + """Tests instantiation of LearningSparse with + ndarray as weights""" shape = (3, 2) weights = np.random.random(shape) @@ -161,12 +158,10 @@ def test_ndarray_gets_converted_into_sparse_for_learning(self): conn = LearningSparse(weights=weights, learning_rule=learning_rule) - self.assertIsInstance(conn.weights.get(), spmatrix) np.testing.assert_array_equal(conn.weights.get().toarray(), weights) - def test_ndarray_gets_converted_into_sparse_for_delay(self): - """Tests if ndarray weights get converted to a spmatrix for - DelaySparse""" + def test_init_of_delaysparse_with_ndarray(self): + """Tests instantiation of DelaySparse with ndarray as weights""" shape = (3, 2) weights = np.random.random(shape) @@ -174,7 +169,6 @@ def test_ndarray_gets_converted_into_sparse_for_delay(self): conn = DelaySparse(weights=weights, delays=delays) - self.assertIsInstance(conn.weights.get(), spmatrix) np.testing.assert_array_equal(conn.weights.get().toarray(), weights) From 4c00a6126a6cff9fa2d1cf42c962e72391cec885 Mon Sep 17 00:00:00 2001 From: SveaMeyer13 Date: Thu, 17 Aug 2023 13:59:21 +0200 Subject: [PATCH 5/7] move tests to corresponding classes --- tests/lava/proc/sparse/test_process.py | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/lava/proc/sparse/test_process.py b/tests/lava/proc/sparse/test_process.py index d4ad980cb..7fba10171 100644 --- a/tests/lava/proc/sparse/test_process.py +++ b/tests/lava/proc/sparse/test_process.py @@ -42,6 +42,17 @@ def test_init(self): np.testing.assert_array_equal(conn.weights.init.toarray(), weights) + def test_init_of_sparse_with_ndarray(self): + """Tests instantiation of Sparse with ndarray as + weights""" + + shape = (3, 2) + weights = np.random.random(shape) + + conn = Sparse(weights=weights) + + np.testing.assert_array_equal(conn.weights.get().toarray(), weights) + class TestLearningSparseProcess(unittest.TestCase): """Tests for LearningSparse class""" @@ -72,6 +83,26 @@ def test_init(self): np.testing.assert_array_equal(conn.weights.init.toarray(), weights) + def test_init_of_learningsparse_with_ndarray(self): + """Tests instantiation of LearningSparse with + ndarray as weights""" + + shape = (3, 2) + weights = np.random.random(shape) + + learning_rule = STDPLoihi( + learning_rate=1, + A_plus=1, + A_minus=-2, + tau_plus=10, + tau_minus=10, + t_epoch=2, + ) + + conn = LearningSparse(weights=weights, learning_rule=learning_rule) + + np.testing.assert_array_equal(conn.weights.get().toarray(), weights) + class TestDelaySparseProcess(unittest.TestCase): """Tests for Sparse class""" @@ -129,37 +160,6 @@ def test_validate_nonzero_delays(self): weights=weights_sparse, delays=delays_sparse) - def test_init_of_sparse_with_ndarray(self): - """Tests instantiation of Sparse with ndarray as - weights""" - - shape = (3, 2) - weights = np.random.random(shape) - - conn = Sparse(weights=weights) - - np.testing.assert_array_equal(conn.weights.get().toarray(), weights) - - def test_init_of_learningsparse_with_ndarray(self): - """Tests instantiation of LearningSparse with - ndarray as weights""" - - shape = (3, 2) - weights = np.random.random(shape) - - learning_rule = STDPLoihi( - learning_rate=1, - A_plus=1, - A_minus=-2, - tau_plus=10, - tau_minus=10, - t_epoch=2, - ) - - conn = LearningSparse(weights=weights, learning_rule=learning_rule) - - np.testing.assert_array_equal(conn.weights.get().toarray(), weights) - def test_init_of_delaysparse_with_ndarray(self): """Tests instantiation of DelaySparse with ndarray as weights""" From 96c5031424652500cdede2252f7d3c6c7ab47e37 Mon Sep 17 00:00:00 2001 From: SveaMeyer13 Date: Fri, 18 Aug 2023 14:09:09 +0200 Subject: [PATCH 6/7] put weight casting into extra method --- src/lava/proc/sparse/process.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/lava/proc/sparse/process.py b/src/lava/proc/sparse/process.py index a4c7bfb45..7e7d1bf64 100644 --- a/src/lava/proc/sparse/process.py +++ b/src/lava/proc/sparse/process.py @@ -1,6 +1,7 @@ # Copyright (C) 2021-23 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # See: https://spdx.org/licenses/ +from abc import abstractmethod import numpy as np from scipy.sparse import spmatrix, csr_matrix @@ -55,6 +56,7 @@ class Sparse(AbstractProcess): spikes as binary spikes (num_message_bits = 0) or as graded spikes (num_message_bits > 0). Default is 0. """ + def __init__(self, *, weights: ty.Union[spmatrix, np.ndarray], @@ -68,12 +70,7 @@ def __init__(self, log_config=log_config, **kwargs) - # Transform weights to csr matrix - if isinstance(weights, np.ndarray): - weights = csr_matrix(weights) - else: - weights = weights.tocsr() - + weights = self._create_csr_matrix_from_weights(weights) shape = weights.shape # Ports @@ -85,6 +82,15 @@ def __init__(self, self.a_buff = Var(shape=(shape[0],), init=0) self.num_message_bits = Var(shape=(1,), init=num_message_bits) + @staticmethod + def _create_csr_matrix_from_weights(weights): + # Transform weights to csr matrix + if isinstance(weights, np.ndarray): + weights = csr_matrix(weights) + else: + weights = weights.tocsr() + return weights + class LearningSparse(LearningConnectionProcess, Sparse): """Sparse connections between neurons. Realizes the following abstract @@ -151,6 +157,7 @@ class LearningSparse(LearningConnectionProcess, Sparse): x1 and regular impulse addition to x2 will be considered by the learning rule Products conditioned on x0. """ + def __init__(self, *, weights: ty.Union[spmatrix, np.ndarray], @@ -174,12 +181,7 @@ def __init__(self, graded_spike_cfg=graded_spike_cfg, **kwargs) - # Transform weights to csr matrix - if isinstance(weights, np.ndarray): - weights = csr_matrix(weights) - else: - weights = weights.tocsr() - + weights = self._create_csr_matrix_from_weights(weights) shape = weights.shape # Ports From db5b5a558800e2cf5c5648357703ba8000b5dddd Mon Sep 17 00:00:00 2001 From: Mathis Richter Date: Tue, 22 Aug 2023 09:25:30 +0200 Subject: [PATCH 7/7] Removed unused import --- src/lava/proc/sparse/process.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lava/proc/sparse/process.py b/src/lava/proc/sparse/process.py index 7e7d1bf64..0f702d398 100644 --- a/src/lava/proc/sparse/process.py +++ b/src/lava/proc/sparse/process.py @@ -1,7 +1,6 @@ # Copyright (C) 2021-23 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # See: https://spdx.org/licenses/ -from abc import abstractmethod import numpy as np from scipy.sparse import spmatrix, csr_matrix