From 027a42fe03b30497c06716b0c99da47e724f8fd4 Mon Sep 17 00:00:00 2001 From: Javid Ahmed Date: Thu, 12 Sep 2024 21:05:56 +0100 Subject: [PATCH 1/6] Add pytest-cov --- Pipfile | 3 +- Pipfile.lock | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/Pipfile b/Pipfile index 369a3ff..5eb186d 100644 --- a/Pipfile +++ b/Pipfile @@ -8,6 +8,7 @@ numpy = "*" [dev-packages] pytest = "*" +pytest-cov = "*" ruff = "*" ipykernel = "*" @@ -15,5 +16,5 @@ ipykernel = "*" python_version = "3.12" [scripts] -test = "python -m pytest -vx" +test = "python -m pytest -vx --cov=neural_network --cov-report term-missing" ruff = "python -m ruff check ." diff --git a/Pipfile.lock b/Pipfile.lock index 39bf4a8..cb30ff6 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "5930b1bb9a8c292202a3166749d41ba1bf670f32b1a1f57aabbada6c82f64c12" + "sha256": "435ed28d2347f04253e86b4198391d2f7a8f7655d3268a4dc12f1c645ae854c2" }, "pipfile-spec": 6, "requires": { @@ -101,6 +101,87 @@ "markers": "python_version >= '3.8'", "version": "==0.2.2" }, + "coverage": { + "extras": [ + "toml" + ], + "hashes": [ + "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", + "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", + "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", + "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989", + "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", + "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", + "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", + "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", + "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", + "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", + "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", + "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", + "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", + "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", + "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", + "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8", + "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", + "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", + "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", + "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", + "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", + "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", + "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", + "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", + "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", + "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0", + "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", + "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", + "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", + "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", + "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", + "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", + "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", + "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", + "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de", + "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", + "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", + "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569", + "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", + "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", + "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", + "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", + "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", + "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6", + "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", + "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", + "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", + "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", + "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", + "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b", + "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", + "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", + "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3", + "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", + "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", + "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", + "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", + "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", + "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", + "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", + "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", + "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", + "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7", + "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", + "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", + "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", + "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", + "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", + "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a", + "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", + "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", + "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc" + ], + "markers": "python_version >= '3.8'", + "version": "==7.6.1" + }, "debugpy": { "hashes": [ "sha256:0a1029a2869d01cb777216af8c53cda0476875ef02a2b6ff8b2f2c9a4b04176c", @@ -297,12 +378,21 @@ "markers": "python_version >= '3.8'", "version": "==8.3.3" }, + "pytest-cov": { + "hashes": [ + "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", + "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==5.0.0" + }, "python-dateutil": { "hashes": [ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.9.0.post0" }, "pywin32": { @@ -470,7 +560,7 @@ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, "stack-data": { From 6f1ca655189d1bfcfc8bb038feb7f942cc578283 Mon Sep 17 00:00:00 2001 From: Javid Ahmed Date: Thu, 12 Sep 2024 21:06:04 +0100 Subject: [PATCH 2/6] Add ActivationFunction tests --- tests/math/test_activation_functions.py | 55 +++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/math/test_activation_functions.py diff --git a/tests/math/test_activation_functions.py b/tests/math/test_activation_functions.py new file mode 100644 index 0000000..3068441 --- /dev/null +++ b/tests/math/test_activation_functions.py @@ -0,0 +1,55 @@ +import numpy as np + +from neural_network.math.activation_functions import LinearActivation, ReluActivation, SigmoidActivation + + +class TestLinearActivation: + def test_given_x_when_calculating_y_then_check_calculated_correctly(self) -> None: + x = 5 + expected_y = x + actual_y = LinearActivation.func(x) + assert actual_y == expected_y + + def test_given_x_when_calculating_derivative_then_check_calculated_correctly(self) -> None: + x = 5 + expected_y = 1 + actual_y = LinearActivation.derivative(x) + assert actual_y == expected_y + + +class TestReluActivation: + def test_given_x_when_calculating_y_then_check_calculated_correctly(self) -> None: + x = -5 + expected_y = 0 + actual_y = ReluActivation.func(x) + assert actual_y == expected_y + + x = 5 + expected_y = x + actual_y = ReluActivation.func(x) + assert actual_y == expected_y + + def test_given_x_when_calculating_derivative_then_check_calculated_correctly(self) -> None: + x = -5 + expected_y = 0 + actual_y = ReluActivation.derivative(x) + assert actual_y == expected_y + + x = 5 + expected_y = 1 + actual_y = ReluActivation.derivative(x) + assert actual_y == expected_y + + +class TestSigmoidActivation: + def test_given_x_when_calculating_y_then_check_calculated_correctly(self) -> None: + x = 5 + expected_y = 1 / (1 + np.exp(-x)) + actual_y = SigmoidActivation.func(x) + assert actual_y == expected_y + + def test_given_x_when_calculating_derivative_then_check_calculated_correctly(self) -> None: + x = 5 + expected_y = x * (1 - x) + actual_y = SigmoidActivation.derivative(x) + assert actual_y == expected_y From 337dcae78f6a7a60d351b37a7c438f31d2c637d6 Mon Sep 17 00:00:00 2001 From: Javid Ahmed Date: Thu, 12 Sep 2024 21:24:12 +0100 Subject: [PATCH 3/6] Add more Matrix tests --- neural_network/math/matrix.py | 2 +- tests/math/test_matrix.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/neural_network/math/matrix.py b/neural_network/math/matrix.py index b19cd12..b6d5ce5 100644 --- a/neural_network/math/matrix.py +++ b/neural_network/math/matrix.py @@ -20,7 +20,7 @@ def __init__(self, rows: int, cols: int, vals: NDArray | None = None) -> None: Parameters: rows (int): Number of rows in matrix cols (int): Number of columns in matrix - vals (Optional[NDArray]): Matrix values if specified + vals (NDArray | None): Matrix values if specified """ self._rows = rows self._cols = cols diff --git a/tests/math/test_matrix.py b/tests/math/test_matrix.py index caf95a7..97db1de 100644 --- a/tests/math/test_matrix.py +++ b/tests/math/test_matrix.py @@ -5,6 +5,12 @@ class TestMatrix: + def test_given_no_vals_when_creating_matrix_then_check_matrix_has_zero_vals( + self, mock_len_inputs: int, mock_len_outputs: int + ) -> None: + test_matrix = Matrix(rows=mock_len_inputs, cols=mock_len_outputs) + assert not np.any(test_matrix.vals) + def test_given_shape_when_creating_random_matrix_then_check_matrix_has_correct_shape( self, mock_weights_range: list[float], mock_len_inputs: int, mock_len_outputs: int ) -> None: @@ -161,3 +167,14 @@ def test_given_matrices_when_mixing_then_check_output_has_correct_shape(self) -> new_matrix = Matrix.mix_matrices(matrix_in_1, matrix_in_2, matrix_out) assert np.all(new_matrix.shape == matrix_out.shape) + + def test_given_matrix_when_shifting_vals_then_check_vals_are_different(self) -> None: + array = np.array([[1, 2], [4, 3], [2, 4]]) + + matrix_1 = Matrix.from_array(array) + matrix_2 = Matrix.from_array(array) + + assert np.all(matrix_1.vals == matrix_2.vals) + + matrix_1.shift_vals(0.5) + assert not np.all(matrix_1.vals == matrix_2.vals) From e522c1bb9a47812dd37aaa650b808af5ecd4fe1c Mon Sep 17 00:00:00 2001 From: Javid Ahmed Date: Thu, 12 Sep 2024 21:46:15 +0100 Subject: [PATCH 4/6] Move layer --- neural_network/{nn => }/layer.py | 0 neural_network/neural_network.py | 2 +- neural_network/nn/__init__.py | 0 tests/conftest.py | 2 +- tests/{nn => }/test_layer.py | 2 +- tests/test_neural_network.py | 2 +- 6 files changed, 4 insertions(+), 4 deletions(-) rename neural_network/{nn => }/layer.py (100%) delete mode 100644 neural_network/nn/__init__.py rename tests/{nn => }/test_layer.py (96%) diff --git a/neural_network/nn/layer.py b/neural_network/layer.py similarity index 100% rename from neural_network/nn/layer.py rename to neural_network/layer.py diff --git a/neural_network/neural_network.py b/neural_network/neural_network.py index d918387..6d8ff1a 100644 --- a/neural_network/neural_network.py +++ b/neural_network/neural_network.py @@ -4,9 +4,9 @@ import numpy as np +from neural_network.layer import HiddenLayer, InputLayer, Layer, OutputLayer from neural_network.math.matrix import Matrix from neural_network.math.nn_math import calculate_error_from_expected, calculate_next_errors -from neural_network.nn.layer import HiddenLayer, InputLayer, Layer, OutputLayer class NeuralNetwork: diff --git a/neural_network/nn/__init__.py b/neural_network/nn/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/conftest.py b/tests/conftest.py index 992439e..5fb6727 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,9 @@ import pytest +from neural_network.layer import HiddenLayer, InputLayer, OutputLayer from neural_network.math.activation_functions import LinearActivation from neural_network.math.matrix import Matrix from neural_network.neural_network import NeuralNetwork -from neural_network.nn.layer import HiddenLayer, InputLayer, OutputLayer @pytest.fixture diff --git a/tests/nn/test_layer.py b/tests/test_layer.py similarity index 96% rename from tests/nn/test_layer.py rename to tests/test_layer.py index e0a9658..1aca760 100644 --- a/tests/nn/test_layer.py +++ b/tests/test_layer.py @@ -1,5 +1,5 @@ +from neural_network.layer import HiddenLayer, InputLayer from neural_network.math.matrix import Matrix -from neural_network.nn.layer import HiddenLayer, InputLayer class TestLayer: diff --git a/tests/test_neural_network.py b/tests/test_neural_network.py index 591beb8..018022c 100644 --- a/tests/test_neural_network.py +++ b/tests/test_neural_network.py @@ -1,6 +1,6 @@ +from neural_network.layer import HiddenLayer, InputLayer, OutputLayer from neural_network.math.activation_functions import ActivationFunction from neural_network.neural_network import NeuralNetwork -from neural_network.nn.layer import HiddenLayer, InputLayer, OutputLayer def make_hidden_layer( From 38f1eb726b112daf2dd42ffa12e26ffc8b87094f Mon Sep 17 00:00:00 2001 From: Javid Ahmed Date: Thu, 12 Sep 2024 21:50:53 +0100 Subject: [PATCH 5/6] Update docstrings --- neural_network/layer.py | 12 ++++++------ neural_network/neural_network.py | 4 +--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/neural_network/layer.py b/neural_network/layer.py index 82bf4fa..9faa87b 100644 --- a/neural_network/layer.py +++ b/neural_network/layer.py @@ -141,7 +141,7 @@ def __init__( Parameters: size (int): Size of InputLayer - activation (ActivationFunction): Layer activation function + activation (ActivationFunction): InputLayer activation function """ super().__init__(size, activation, [1, 1], [0, 0]) @@ -180,10 +180,10 @@ def __init__( Initialise HiddenLayer object with number of nodes, activation function, weights range and bias range. Parameters: - size (int): Size of Layer - activation (ActivationFunction): Layer activation function - weights_range (tuple[float, float]): Range for Layer weights - bias_range (tuple[float, float]): Range for Layer bias + size (int): Size of HiddenLayer + activation (ActivationFunction): HiddenLayer activation function + weights_range (tuple[float, float]): Range for HiddenLayer weights + bias_range (tuple[float, float]): Range for HiddenLayer bias """ super().__init__(size, activation, weights_range, bias_range) @@ -205,7 +205,7 @@ def __init__( Parameters: size (int): Size of OutputLayer - activation (ActivationFunction): Layer activation function + activation (ActivationFunction): OutputLayer activation function weights_range (tuple[float, float]): Range for OutputLayer weights bias_range (tuple[float, float]): Range for OutputLayer bias """ diff --git a/neural_network/neural_network.py b/neural_network/neural_network.py index 6d8ff1a..21a7d20 100644 --- a/neural_network/neural_network.py +++ b/neural_network/neural_network.py @@ -98,12 +98,10 @@ def save(self, filepath: str) -> None: def mutate(self, shift_vals: float) -> None: """ - Mutate NeuralNetwork Layers by adjusting weights and biases, and potentially adding new Nodes. + Mutate NeuralNetwork Layers by adjusting weights and biases. Parameters: shift_vals (float): Factor to adjust Layer weights and biases by - prob_new_node (float): Probability per Layer for a new Node, range [0, 1] - prob_toggle_connection (float): Probability per Layer to toggle a random Node, range[0, 1] """ for layer in self.layers[1:]: layer.mutate(shift_vals) From 5cf49cc5173ea04b6be600975e2ae0ee4715d5f2 Mon Sep 17 00:00:00 2001 From: Javid Ahmed Date: Thu, 12 Sep 2024 21:51:37 +0100 Subject: [PATCH 6/6] Update version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d23dda6..3542a2b 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import find_packages, setup -__version__ = "1.12.0" +__version__ = "1.12.1" setup( name="neural_network",