From 82b95b4338c23428ab1ef60fe034895c880112b9 Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 11:46:41 +0200 Subject: [PATCH 01/12] issue almost finished need to adjust return vlaue of Layer.input/output --- src/safeds/ml/nn/layers/_layer.py | 8 ++++---- src/safeds/ml/nn/typing/__init__.py | 3 +++ src/safeds/ml/nn/typing/_model_image_size.py | 4 +++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/safeds/ml/nn/layers/_layer.py b/src/safeds/ml/nn/layers/_layer.py index 058036688..63edfec5d 100644 --- a/src/safeds/ml/nn/layers/_layer.py +++ b/src/safeds/ml/nn/layers/_layer.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: from torch import nn - from safeds.ml.nn.typing import ModelImageSize + from safeds.ml.nn.typing import TensorShape class Layer(ABC): @@ -20,16 +20,16 @@ def _get_internal_layer(self, **kwargs: Any) -> nn.Module: @property @abstractmethod - def input_size(self) -> int | ModelImageSize: + def input_size(self) -> TensorShape: pass # pragma: no cover @property @abstractmethod - def output_size(self) -> int | ModelImageSize: + def output_size(self) -> TensorShape: pass # pragma: no cover @abstractmethod - def _set_input_size(self, input_size: int | ModelImageSize) -> None: + def _set_input_size(self, input_size: TensorShape) -> None: pass # pragma: no cover @abstractmethod diff --git a/src/safeds/ml/nn/typing/__init__.py b/src/safeds/ml/nn/typing/__init__.py index 913ad6f65..08ea81601 100644 --- a/src/safeds/ml/nn/typing/__init__.py +++ b/src/safeds/ml/nn/typing/__init__.py @@ -6,12 +6,14 @@ if TYPE_CHECKING: from ._model_image_size import ConstantImageSize, ModelImageSize, VariableImageSize + from ._tensor_shape import TensorShape apipkg.initpkg( __name__, { "ConstantImageSize": "._model_image_size:ConstantImageSize", "ModelImageSize": "._model_image_size:ModelImageSize", + "TensorShape": "._tensor_shape:TensorShape", "VariableImageSize": "._model_image_size:VariableImageSize", }, ) @@ -19,5 +21,6 @@ __all__ = [ "ConstantImageSize", "ModelImageSize", + "TensorShape", "VariableImageSize", ] diff --git a/src/safeds/ml/nn/typing/_model_image_size.py b/src/safeds/ml/nn/typing/_model_image_size.py index b28ea5ed1..a0e5cf8a8 100644 --- a/src/safeds/ml/nn/typing/_model_image_size.py +++ b/src/safeds/ml/nn/typing/_model_image_size.py @@ -6,12 +6,13 @@ from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound +from safeds.ml.nn.typing._tensor_shape import TensorShape if TYPE_CHECKING: from safeds.data.image.containers import Image -class ModelImageSize(ABC): +class ModelImageSize(TensorShape): """ A container for image size in neural networks. @@ -38,6 +39,7 @@ def __init__(self, width: int, height: int, channel: int, *, _ignore_invalid_cha if not _ignore_invalid_channel and channel not in (1, 3, 4): raise ValueError(f"Channel {channel} is not a valid channel option. Use either 1, 3 or 4") _check_bounds("channel", channel, lower_bound=_ClosedBound(1)) + super.__init__(dims = [width, height, channel]) self._width = width self._height = height From 815ff3795020dbeda6d7c0a4793deca9a8949fa3 Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 11:53:27 +0200 Subject: [PATCH 02/12] added tensor shape class --- src/safeds/ml/nn/typing/_tensor_shape.py | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/safeds/ml/nn/typing/_tensor_shape.py diff --git a/src/safeds/ml/nn/typing/_tensor_shape.py b/src/safeds/ml/nn/typing/_tensor_shape.py new file mode 100644 index 000000000..90bdb94b6 --- /dev/null +++ b/src/safeds/ml/nn/typing/_tensor_shape.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import sys +from abc import ABC, abstractmethod +from typing import Self + +from safeds._utils import _structural_hash +from safeds._validation import _check_bounds, _ClosedBound + + +class TensorShape(ABC): + """ + Initializes a TensorShape object with the given dimensions. + + Parameters + ---------- + dims: + A list of integers where each integer represents + the size of the tensor in a particular dimension. + """ + + def __init__(self, dims: list[int]) -> None: + self._dims = dims + + @classmethod + def get_size(cls: type[Self], dimension: int | None = None) -> int: + """ + Return the size of the tensor in the specified dimension. + + Parameters. + ---------- + dimension: + The dimension index for which the size is to be retrieved. + + Return + ------- + int: The size of the tensor in the specified dimension. + """ + _check_bounds("dimension",dimension, lower_bound=_ClosedBound(0)) + if(dimension >= cls.dimensionality): + #TODO maybe add error message indicating that the dimension is out of range + return 0 + if(dimension is None): + return cls._dims + return cls._dims[dimension] + + @abstractmethod + def __hash__(self) -> int: + return _structural_hash(self._dims) + + @property + def dimensionality(self) -> int: + """ + Returns the number of dimensions of the tensor. + + Returns + ------- + int: The number of dimensions of the tensor. + """ + return len(self._dims) \ No newline at end of file From 0510fdaccb7a5e5870fae25599f1a10e491ab2e0 Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 15:00:22 +0200 Subject: [PATCH 03/12] src file now updated --- src/safeds/ml/nn/typing/_tensor_shape.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/safeds/ml/nn/typing/_tensor_shape.py b/src/safeds/ml/nn/typing/_tensor_shape.py index 90bdb94b6..698045611 100644 --- a/src/safeds/ml/nn/typing/_tensor_shape.py +++ b/src/safeds/ml/nn/typing/_tensor_shape.py @@ -1,14 +1,12 @@ from __future__ import annotations -import sys -from abc import ABC, abstractmethod from typing import Self from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound -class TensorShape(ABC): +class TensorShape: """ Initializes a TensorShape object with the given dimensions. @@ -22,8 +20,7 @@ class TensorShape(ABC): def __init__(self, dims: list[int]) -> None: self._dims = dims - @classmethod - def get_size(cls: type[Self], dimension: int | None = None) -> int: + def get_size(self, dimension: int | None = None) -> int: """ Return the size of the tensor in the specified dimension. @@ -32,19 +29,23 @@ def get_size(cls: type[Self], dimension: int | None = None) -> int: dimension: The dimension index for which the size is to be retrieved. - Return + Returns ------- int: The size of the tensor in the specified dimension. + + Raises + ------ + OutOfBoundsError: + If the actual value is outside its expected range. """ _check_bounds("dimension",dimension, lower_bound=_ClosedBound(0)) - if(dimension >= cls.dimensionality): + if dimension is not None and dimension >= self.dimensionality: #TODO maybe add error message indicating that the dimension is out of range return 0 if(dimension is None): - return cls._dims - return cls._dims[dimension] + return self._dims + return self._dims[dimension] - @abstractmethod def __hash__(self) -> int: return _structural_hash(self._dims) From 1fd480792f3b9a0ff9cbaf3a35d58b8fe8d6d421 Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 15:08:31 +0200 Subject: [PATCH 04/12] pytests written --- .../safeds/ml/nn/typing/test_tensor_shape.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/safeds/ml/nn/typing/test_tensor_shape.py diff --git a/tests/safeds/ml/nn/typing/test_tensor_shape.py b/tests/safeds/ml/nn/typing/test_tensor_shape.py new file mode 100644 index 000000000..b2564ed4c --- /dev/null +++ b/tests/safeds/ml/nn/typing/test_tensor_shape.py @@ -0,0 +1,38 @@ +import pytest +from safeds.exceptions import OutOfBoundsError +from safeds.ml.nn.typing import TensorShape + + +def test_dims(): + dims = [2, 3, 4] + ts = TensorShape(dims) + assert ts._dims == dims + +def test_dimensionality(): + dims = [2, 3, 4] + ts = TensorShape(dims) + assert ts.dimensionality == 3 + +def test_get_size_no_dimension(): + dims = [2, 3, 4] + ts = TensorShape(dims) + assert ts.get_size() == dims + +def test_get_size_specific_dimension(): + dims = [2, 3, 4] + ts = TensorShape(dims) + assert ts.get_size(0) == 2 + assert ts.get_size(1) == 3 + assert ts.get_size(2) == 4 + +def test_get_size_out_of_bounds_dimension(): + dims = [2, 3, 4] + ts = TensorShape(dims) + with pytest.raises(OutOfBoundsError): + ts.get_size(-1) + +def test_hash(): + dims = [2, 3, 4] + ts1 = TensorShape(dims) + ts2 = TensorShape(dims) + assert hash(ts1) == hash(ts2) \ No newline at end of file From 6f96d4fb2adc2ce02c60b462abcc37b15e024f57 Mon Sep 17 00:00:00 2001 From: xXstupidnameXx Date: Fri, 12 Jul 2024 15:13:05 +0200 Subject: [PATCH 05/12] Adjusted some of the layers --- src/safeds/ml/nn/layers/_flatten_layer.py | 8 ++++---- src/safeds/ml/nn/layers/_forward_layer.py | 12 ++++++------ src/safeds/ml/nn/layers/_gru_layer.py | 14 +++++++------- src/safeds/ml/nn/typing/_model_image_size.py | 4 ++-- tests/safeds/ml/nn/layers/test_flatten_layer.py | 2 +- tests/safeds/ml/nn/layers/test_gru_layer.py | 4 ++-- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/safeds/ml/nn/layers/_flatten_layer.py b/src/safeds/ml/nn/layers/_flatten_layer.py index a84551c2b..58989d78c 100644 --- a/src/safeds/ml/nn/layers/_flatten_layer.py +++ b/src/safeds/ml/nn/layers/_flatten_layer.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any from safeds._utils import _structural_hash -from safeds.ml.nn.typing import ConstantImageSize +from safeds.ml.nn.typing import ConstantImageSize, TensorShape from ._layer import Layer @@ -46,14 +46,14 @@ def input_size(self) -> ModelImageSize: return self._input_size @property - def output_size(self) -> int: + def output_size(self) -> TensorShape: """ Get the output_size of this layer. Returns ------- result: - The number of neurons in this layer. + A 1D TensorShape object containing the number of neurons in this layer. Raises ------ @@ -66,7 +66,7 @@ def output_size(self) -> int: ) if self._output_size is None: self._output_size = self._input_size.width * self._input_size.height * self._input_size.channel - return self._output_size + return TensorShape([self._output_size]) def _set_input_size(self, input_size: int | ModelImageSize) -> None: if isinstance(input_size, int): diff --git a/src/safeds/ml/nn/layers/_forward_layer.py b/src/safeds/ml/nn/layers/_forward_layer.py index e420b78ec..b2cb17b7e 100644 --- a/src/safeds/ml/nn/layers/_forward_layer.py +++ b/src/safeds/ml/nn/layers/_forward_layer.py @@ -4,7 +4,7 @@ from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound -from safeds.ml.nn.typing import ModelImageSize +from safeds.ml.nn.typing import ModelImageSize, TensorShape from ._layer import Layer @@ -50,29 +50,29 @@ def _get_internal_layer(self, **kwargs: Any) -> nn.Module: return _InternalForwardLayer(self._input_size, self._output_size, activation_function) @property - def input_size(self) -> int: + def input_size(self) -> TensorShape: """ Get the input_size of this layer. Returns ------- result: - The amount of values being passed into this layer. + A 1D TensorShape object containing the amount of values being passed into this layer. """ if self._input_size is None: raise ValueError("The input_size is not yet set.") - return self._input_size + return TensorShape([self._input_size]) @property - def output_size(self) -> int: + def output_size(self) -> TensorShape: """ Get the output_size of this layer. Returns ------- result: - The number of neurons in this layer. + A 1D TensorShape object containing the number of neurons in this layer. """ return self._output_size diff --git a/src/safeds/ml/nn/layers/_gru_layer.py b/src/safeds/ml/nn/layers/_gru_layer.py index e74fec417..0a57dc9ed 100644 --- a/src/safeds/ml/nn/layers/_gru_layer.py +++ b/src/safeds/ml/nn/layers/_gru_layer.py @@ -5,7 +5,7 @@ from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound -from safeds.ml.nn.typing import ModelImageSize +from safeds.ml.nn.typing import ModelImageSize, TensorShape from ._layer import Layer @@ -51,30 +51,30 @@ def _get_internal_layer(self, **kwargs: Any) -> nn.Module: return _InternalGRULayer(self._input_size, self._output_size, activation_function) @property - def input_size(self) -> int: + def input_size(self) -> TensorShape: """ Get the input_size of this layer. Returns ------- result: - The amount of values being passed into this layer. + A 1D TensorShape object containing the amount of values being passed into this layer. """ if self._input_size is None: raise ValueError("The input_size is not yet set.") - return self._input_size + return TensorShape([self._input_size]) @property - def output_size(self) -> int: + def output_size(self) -> TensorShape: """ Get the output_size of this layer. Returns ------- result: - The number of neurons in this layer. + A 1D TensorShape object containing the number of neurons in this layer. """ - return self._output_size + return TensorShape([self._output_size]) def _set_input_size(self, input_size: int | ModelImageSize) -> None: if isinstance(input_size, ModelImageSize): diff --git a/src/safeds/ml/nn/typing/_model_image_size.py b/src/safeds/ml/nn/typing/_model_image_size.py index a0e5cf8a8..e0ee23498 100644 --- a/src/safeds/ml/nn/typing/_model_image_size.py +++ b/src/safeds/ml/nn/typing/_model_image_size.py @@ -1,7 +1,7 @@ from __future__ import annotations import sys -from abc import ABC, abstractmethod +from abc import abstractmethod from typing import TYPE_CHECKING, Self from safeds._utils import _structural_hash @@ -39,7 +39,7 @@ def __init__(self, width: int, height: int, channel: int, *, _ignore_invalid_cha if not _ignore_invalid_channel and channel not in (1, 3, 4): raise ValueError(f"Channel {channel} is not a valid channel option. Use either 1, 3 or 4") _check_bounds("channel", channel, lower_bound=_ClosedBound(1)) - super.__init__(dims = [width, height, channel]) + super().__init__(dims = [width, height, channel]) self._width = width self._height = height diff --git a/tests/safeds/ml/nn/layers/test_flatten_layer.py b/tests/safeds/ml/nn/layers/test_flatten_layer.py index 64db6127c..d4214669e 100644 --- a/tests/safeds/ml/nn/layers/test_flatten_layer.py +++ b/tests/safeds/ml/nn/layers/test_flatten_layer.py @@ -14,7 +14,7 @@ def test_should_create_flatten_layer(self) -> None: input_size = ImageSize(10, 20, 30, _ignore_invalid_channel=True) layer._set_input_size(input_size) assert layer.input_size == input_size - assert layer.output_size == input_size.width * input_size.height * input_size.channel + assert layer.output_size.get_size[0] == input_size.width * input_size.height * input_size.channel assert isinstance(next(next(layer._get_internal_layer().modules()).children()), nn.Flatten) def test_should_raise_if_input_size_not_set(self) -> None: diff --git a/tests/safeds/ml/nn/layers/test_gru_layer.py b/tests/safeds/ml/nn/layers/test_gru_layer.py index 4a6f366e4..dc5fdd694 100644 --- a/tests/safeds/ml/nn/layers/test_gru_layer.py +++ b/tests/safeds/ml/nn/layers/test_gru_layer.py @@ -71,7 +71,7 @@ def test_should_raise_if_output_size_out_of_bounds(output_size: int) -> None: ids=["one", "twenty"], ) def test_should_raise_if_output_size_doesnt_match(output_size: int) -> None: - assert GRULayer(neuron_count=output_size).output_size == output_size + assert GRULayer(neuron_count=output_size).output_size.get_size[0] == output_size def test_should_raise_if_input_size_is_set_with_image_size() -> None: @@ -170,7 +170,7 @@ def test_should_assert_that_layer_size_is_greater_than_normal_object(layer: GRUL def test_set_input_size() -> None: layer = GRULayer(1) layer._set_input_size(3) - assert layer.input_size == 3 + assert layer.input_size.get_size[0] == 3 def test_input_size_should_raise_error() -> None: From 63023b4f496f3565ae429f76e440fdbd48c5e965 Mon Sep 17 00:00:00 2001 From: xXstupidnameXx Date: Fri, 12 Jul 2024 15:19:43 +0200 Subject: [PATCH 06/12] minor changes --- src/safeds/ml/nn/layers/_lstm_layer.py | 10 +++++----- tests/safeds/ml/nn/layers/test_forward_layer.py | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/safeds/ml/nn/layers/_lstm_layer.py b/src/safeds/ml/nn/layers/_lstm_layer.py index 330809474..cbf5140d9 100644 --- a/src/safeds/ml/nn/layers/_lstm_layer.py +++ b/src/safeds/ml/nn/layers/_lstm_layer.py @@ -5,7 +5,7 @@ from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound -from safeds.ml.nn.typing import ModelImageSize +from safeds.ml.nn.typing import ModelImageSize, TensorShape from ._layer import Layer @@ -51,7 +51,7 @@ def _get_internal_layer(self, **kwargs: Any) -> nn.Module: return _InternalLSTMLayer(self._input_size, self._output_size, activation_function) @property - def input_size(self) -> int: + def input_size(self) -> TensorShape: """ Get the input_size of this layer. @@ -62,10 +62,10 @@ def input_size(self) -> int: """ if self._input_size is None: raise ValueError("The input_size is not yet set.") - return self._input_size + return TensorShape([self._input_size]) @property - def output_size(self) -> int: + def output_size(self) -> TensorShape: """ Get the output_size of this layer. @@ -74,7 +74,7 @@ def output_size(self) -> int: result: The number of neurons in this layer. """ - return self._output_size + return TensorShape([self._output_size]) def _set_input_size(self, input_size: int | ModelImageSize) -> None: if isinstance(input_size, ModelImageSize): diff --git a/tests/safeds/ml/nn/layers/test_forward_layer.py b/tests/safeds/ml/nn/layers/test_forward_layer.py index 0ecd3bd05..f101a2758 100644 --- a/tests/safeds/ml/nn/layers/test_forward_layer.py +++ b/tests/safeds/ml/nn/layers/test_forward_layer.py @@ -5,6 +5,7 @@ from safeds.data.image.typing import ImageSize from safeds.exceptions import OutOfBoundsError from safeds.ml.nn.layers import ForwardLayer +from safeds.ml.nn.typing import TensorShape from torch import nn # TODO: Should be tested on a model, not a layer, since input size gets inferred @@ -83,7 +84,7 @@ def test_should_raise_if_output_size_out_of_bounds(output_size: int) -> None: ids=["one", "twenty"], ) def test_should_return_output_size(output_size: int) -> None: - assert ForwardLayer(neuron_count=output_size).output_size == output_size + assert ForwardLayer(neuron_count=output_size).output_size.get_size[0] == output_size def test_should_raise_if_input_size_is_set_with_image_size() -> None: From 170550dfdb9ad3640da0749f82e03b4ee1fda3ee Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 15:33:42 +0200 Subject: [PATCH 07/12] corrected tests of some layers and added list[int]retrun value --- src/safeds/ml/nn/typing/_tensor_shape.py | 2 +- tests/safeds/ml/nn/layers/test_flatten_layer.py | 2 +- tests/safeds/ml/nn/layers/test_gru_layer.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/safeds/ml/nn/typing/_tensor_shape.py b/src/safeds/ml/nn/typing/_tensor_shape.py index 698045611..308a91f1b 100644 --- a/src/safeds/ml/nn/typing/_tensor_shape.py +++ b/src/safeds/ml/nn/typing/_tensor_shape.py @@ -20,7 +20,7 @@ class TensorShape: def __init__(self, dims: list[int]) -> None: self._dims = dims - def get_size(self, dimension: int | None = None) -> int: + def get_size(self, dimension: int | None = None) -> int | list[int]: """ Return the size of the tensor in the specified dimension. diff --git a/tests/safeds/ml/nn/layers/test_flatten_layer.py b/tests/safeds/ml/nn/layers/test_flatten_layer.py index d4214669e..e30cef24b 100644 --- a/tests/safeds/ml/nn/layers/test_flatten_layer.py +++ b/tests/safeds/ml/nn/layers/test_flatten_layer.py @@ -14,7 +14,7 @@ def test_should_create_flatten_layer(self) -> None: input_size = ImageSize(10, 20, 30, _ignore_invalid_channel=True) layer._set_input_size(input_size) assert layer.input_size == input_size - assert layer.output_size.get_size[0] == input_size.width * input_size.height * input_size.channel + assert layer.output_size.get_size(dimension=0) == input_size.width * input_size.height * input_size.channel assert isinstance(next(next(layer._get_internal_layer().modules()).children()), nn.Flatten) def test_should_raise_if_input_size_not_set(self) -> None: diff --git a/tests/safeds/ml/nn/layers/test_gru_layer.py b/tests/safeds/ml/nn/layers/test_gru_layer.py index dc5fdd694..b5315c4b2 100644 --- a/tests/safeds/ml/nn/layers/test_gru_layer.py +++ b/tests/safeds/ml/nn/layers/test_gru_layer.py @@ -71,7 +71,7 @@ def test_should_raise_if_output_size_out_of_bounds(output_size: int) -> None: ids=["one", "twenty"], ) def test_should_raise_if_output_size_doesnt_match(output_size: int) -> None: - assert GRULayer(neuron_count=output_size).output_size.get_size[0] == output_size + assert GRULayer(neuron_count=output_size).output_size.get_size(dimension=0) == output_size def test_should_raise_if_input_size_is_set_with_image_size() -> None: @@ -170,7 +170,7 @@ def test_should_assert_that_layer_size_is_greater_than_normal_object(layer: GRUL def test_set_input_size() -> None: layer = GRULayer(1) layer._set_input_size(3) - assert layer.input_size.get_size[0] == 3 + assert layer.input_size.get_size(dimension=0) == 3 def test_input_size_should_raise_error() -> None: From dd329d3f59f66b13381c61df0864656ddb7e8cca Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 15:48:02 +0200 Subject: [PATCH 08/12] ready for pull request --- src/safeds/ml/nn/layers/_forward_layer.py | 2 +- src/safeds/ml/nn/layers/_lstm_layer.py | 4 ++-- tests/safeds/ml/nn/layers/test_forward_layer.py | 3 ++- tests/safeds/ml/nn/layers/test_lstm_layer.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/safeds/ml/nn/layers/_forward_layer.py b/src/safeds/ml/nn/layers/_forward_layer.py index b2cb17b7e..77e679279 100644 --- a/src/safeds/ml/nn/layers/_forward_layer.py +++ b/src/safeds/ml/nn/layers/_forward_layer.py @@ -74,7 +74,7 @@ def output_size(self) -> TensorShape: result: A 1D TensorShape object containing the number of neurons in this layer. """ - return self._output_size + return TensorShape([self._output_size]) def _set_input_size(self, input_size: int | ModelImageSize) -> None: if isinstance(input_size, ModelImageSize): diff --git a/src/safeds/ml/nn/layers/_lstm_layer.py b/src/safeds/ml/nn/layers/_lstm_layer.py index cbf5140d9..4f0a02749 100644 --- a/src/safeds/ml/nn/layers/_lstm_layer.py +++ b/src/safeds/ml/nn/layers/_lstm_layer.py @@ -58,7 +58,7 @@ def input_size(self) -> TensorShape: Returns ------- result: - The amount of values being passed into this layer. + A 1D TensorShape object containing the amount of values being passed into this layer. """ if self._input_size is None: raise ValueError("The input_size is not yet set.") @@ -72,7 +72,7 @@ def output_size(self) -> TensorShape: Returns ------- result: - The number of neurons in this layer. + A 1D TensorShape object containing the number of neurons in this layer. """ return TensorShape([self._output_size]) diff --git a/tests/safeds/ml/nn/layers/test_forward_layer.py b/tests/safeds/ml/nn/layers/test_forward_layer.py index f101a2758..90925d597 100644 --- a/tests/safeds/ml/nn/layers/test_forward_layer.py +++ b/tests/safeds/ml/nn/layers/test_forward_layer.py @@ -84,7 +84,8 @@ def test_should_raise_if_output_size_out_of_bounds(output_size: int) -> None: ids=["one", "twenty"], ) def test_should_return_output_size(output_size: int) -> None: - assert ForwardLayer(neuron_count=output_size).output_size.get_size[0] == output_size + r = ForwardLayer(neuron_count=output_size).output_size.get_size(dimension=0) + assert r == output_size def test_should_raise_if_input_size_is_set_with_image_size() -> None: diff --git a/tests/safeds/ml/nn/layers/test_lstm_layer.py b/tests/safeds/ml/nn/layers/test_lstm_layer.py index 8d58e5dd8..48726e525 100644 --- a/tests/safeds/ml/nn/layers/test_lstm_layer.py +++ b/tests/safeds/ml/nn/layers/test_lstm_layer.py @@ -83,7 +83,7 @@ def test_should_raise_if_output_size_out_of_bounds(output_size: int) -> None: ids=["one", "twenty"], ) def test_should_raise_if_output_size_doesnt_match(output_size: int) -> None: - assert LSTMLayer(neuron_count=output_size).output_size == output_size + assert LSTMLayer(neuron_count=output_size).output_size.get_size(dimension=0) == output_size def test_should_raise_if_input_size_is_set_with_image_size() -> None: From d430916bc40ab8daa648ce56e700a4c55fded7bf Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 15:59:14 +0200 Subject: [PATCH 09/12] fixed test functions to return none --- tests/safeds/ml/nn/typing/test_tensor_shape.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/safeds/ml/nn/typing/test_tensor_shape.py b/tests/safeds/ml/nn/typing/test_tensor_shape.py index b2564ed4c..262fabe46 100644 --- a/tests/safeds/ml/nn/typing/test_tensor_shape.py +++ b/tests/safeds/ml/nn/typing/test_tensor_shape.py @@ -3,35 +3,35 @@ from safeds.ml.nn.typing import TensorShape -def test_dims(): +def test_dims()-> None: dims = [2, 3, 4] ts = TensorShape(dims) assert ts._dims == dims -def test_dimensionality(): +def test_dimensionality()-> None: dims = [2, 3, 4] ts = TensorShape(dims) assert ts.dimensionality == 3 -def test_get_size_no_dimension(): +def test_get_size_no_dimension()-> None: dims = [2, 3, 4] ts = TensorShape(dims) assert ts.get_size() == dims -def test_get_size_specific_dimension(): +def test_get_size_specific_dimension()-> None: dims = [2, 3, 4] ts = TensorShape(dims) assert ts.get_size(0) == 2 assert ts.get_size(1) == 3 assert ts.get_size(2) == 4 -def test_get_size_out_of_bounds_dimension(): +def test_get_size_out_of_bounds_dimension()-> None: dims = [2, 3, 4] ts = TensorShape(dims) with pytest.raises(OutOfBoundsError): ts.get_size(-1) -def test_hash(): +def test_hash()-> None: dims = [2, 3, 4] ts1 = TensorShape(dims) ts2 = TensorShape(dims) From fc0dbd89b904dab172e47f739da121a3854b0c64 Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 16:08:15 +0200 Subject: [PATCH 10/12] linter hs --- src/safeds/ml/nn/layers/_flatten_layer.py | 2 +- src/safeds/ml/nn/layers/_forward_layer.py | 4 ++-- src/safeds/ml/nn/layers/_gru_layer.py | 4 ++-- src/safeds/ml/nn/layers/_lstm_layer.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/safeds/ml/nn/layers/_flatten_layer.py b/src/safeds/ml/nn/layers/_flatten_layer.py index 58989d78c..9e464271d 100644 --- a/src/safeds/ml/nn/layers/_flatten_layer.py +++ b/src/safeds/ml/nn/layers/_flatten_layer.py @@ -68,7 +68,7 @@ def output_size(self) -> TensorShape: self._output_size = self._input_size.width * self._input_size.height * self._input_size.channel return TensorShape([self._output_size]) - def _set_input_size(self, input_size: int | ModelImageSize) -> None: + def _set_input_size(self, input_size: int | TensorShapegi) -> None: if isinstance(input_size, int): raise TypeError("The input_size of a flatten layer has to be of type ImageSize.") if not isinstance(input_size, ConstantImageSize): diff --git a/src/safeds/ml/nn/layers/_forward_layer.py b/src/safeds/ml/nn/layers/_forward_layer.py index 77e679279..ca3412e2a 100644 --- a/src/safeds/ml/nn/layers/_forward_layer.py +++ b/src/safeds/ml/nn/layers/_forward_layer.py @@ -76,8 +76,8 @@ def output_size(self) -> TensorShape: """ return TensorShape([self._output_size]) - def _set_input_size(self, input_size: int | ModelImageSize) -> None: - if isinstance(input_size, ModelImageSize): + def _set_input_size(self, input_size: int | TensorShape) -> None: + if isinstance(input_size, TensorShape): raise TypeError("The input_size of a forward layer has to be of type int.") self._input_size = input_size diff --git a/src/safeds/ml/nn/layers/_gru_layer.py b/src/safeds/ml/nn/layers/_gru_layer.py index 0a57dc9ed..2619ea42e 100644 --- a/src/safeds/ml/nn/layers/_gru_layer.py +++ b/src/safeds/ml/nn/layers/_gru_layer.py @@ -76,8 +76,8 @@ def output_size(self) -> TensorShape: """ return TensorShape([self._output_size]) - def _set_input_size(self, input_size: int | ModelImageSize) -> None: - if isinstance(input_size, ModelImageSize): + def _set_input_size(self, input_size: int | TensorShape) -> None: + if isinstance(input_size, TensorShape): raise TypeError("The input_size of a forward layer has to be of type int.") self._input_size = input_size diff --git a/src/safeds/ml/nn/layers/_lstm_layer.py b/src/safeds/ml/nn/layers/_lstm_layer.py index 4f0a02749..082262f4a 100644 --- a/src/safeds/ml/nn/layers/_lstm_layer.py +++ b/src/safeds/ml/nn/layers/_lstm_layer.py @@ -76,8 +76,8 @@ def output_size(self) -> TensorShape: """ return TensorShape([self._output_size]) - def _set_input_size(self, input_size: int | ModelImageSize) -> None: - if isinstance(input_size, ModelImageSize): + def _set_input_size(self, input_size: int | TensorShape) -> None: + if isinstance(input_size, TensorShape): raise TypeError("The input_size of a forward layer has to be of type int.") self._input_size = input_size From 6420ed9cd24f41d972b0d02d7b68b15e25241a21 Mon Sep 17 00:00:00 2001 From: peplaul0 Date: Fri, 12 Jul 2024 17:20:26 +0200 Subject: [PATCH 11/12] fixes --- src/safeds/ml/nn/layers/_flatten_layer.py | 2 +- src/safeds/ml/nn/layers/_forward_layer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/safeds/ml/nn/layers/_flatten_layer.py b/src/safeds/ml/nn/layers/_flatten_layer.py index 9e464271d..35e11c2bb 100644 --- a/src/safeds/ml/nn/layers/_flatten_layer.py +++ b/src/safeds/ml/nn/layers/_flatten_layer.py @@ -68,7 +68,7 @@ def output_size(self) -> TensorShape: self._output_size = self._input_size.width * self._input_size.height * self._input_size.channel return TensorShape([self._output_size]) - def _set_input_size(self, input_size: int | TensorShapegi) -> None: + def _set_input_size(self, input_size: int | TensorShape) -> None: if isinstance(input_size, int): raise TypeError("The input_size of a flatten layer has to be of type ImageSize.") if not isinstance(input_size, ConstantImageSize): diff --git a/src/safeds/ml/nn/layers/_forward_layer.py b/src/safeds/ml/nn/layers/_forward_layer.py index ca3412e2a..779796fd7 100644 --- a/src/safeds/ml/nn/layers/_forward_layer.py +++ b/src/safeds/ml/nn/layers/_forward_layer.py @@ -4,7 +4,7 @@ from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound -from safeds.ml.nn.typing import ModelImageSize, TensorShape +from safeds.ml.nn.typing import TensorShape from ._layer import Layer From e24bf3d6958ae5edf76c84c9479f42cc8faad63f Mon Sep 17 00:00:00 2001 From: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:22:05 +0000 Subject: [PATCH 12/12] style: apply automated linter fixes --- src/safeds/ml/nn/layers/_gru_layer.py | 2 +- src/safeds/ml/nn/layers/_lstm_layer.py | 2 +- src/safeds/ml/nn/typing/_model_image_size.py | 2 +- src/safeds/ml/nn/typing/_tensor_shape.py | 24 +++++++------- .../safeds/ml/nn/layers/test_forward_layer.py | 1 - .../safeds/ml/nn/typing/test_tensor_shape.py | 31 +++++++++++-------- 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/safeds/ml/nn/layers/_gru_layer.py b/src/safeds/ml/nn/layers/_gru_layer.py index 2619ea42e..044200729 100644 --- a/src/safeds/ml/nn/layers/_gru_layer.py +++ b/src/safeds/ml/nn/layers/_gru_layer.py @@ -5,7 +5,7 @@ from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound -from safeds.ml.nn.typing import ModelImageSize, TensorShape +from safeds.ml.nn.typing import TensorShape from ._layer import Layer diff --git a/src/safeds/ml/nn/layers/_lstm_layer.py b/src/safeds/ml/nn/layers/_lstm_layer.py index 082262f4a..f02e27242 100644 --- a/src/safeds/ml/nn/layers/_lstm_layer.py +++ b/src/safeds/ml/nn/layers/_lstm_layer.py @@ -5,7 +5,7 @@ from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound -from safeds.ml.nn.typing import ModelImageSize, TensorShape +from safeds.ml.nn.typing import TensorShape from ._layer import Layer diff --git a/src/safeds/ml/nn/typing/_model_image_size.py b/src/safeds/ml/nn/typing/_model_image_size.py index e0ee23498..4157317fd 100644 --- a/src/safeds/ml/nn/typing/_model_image_size.py +++ b/src/safeds/ml/nn/typing/_model_image_size.py @@ -39,7 +39,7 @@ def __init__(self, width: int, height: int, channel: int, *, _ignore_invalid_cha if not _ignore_invalid_channel and channel not in (1, 3, 4): raise ValueError(f"Channel {channel} is not a valid channel option. Use either 1, 3 or 4") _check_bounds("channel", channel, lower_bound=_ClosedBound(1)) - super().__init__(dims = [width, height, channel]) + super().__init__(dims=[width, height, channel]) self._width = width self._height = height diff --git a/src/safeds/ml/nn/typing/_tensor_shape.py b/src/safeds/ml/nn/typing/_tensor_shape.py index 308a91f1b..4ee867b1f 100644 --- a/src/safeds/ml/nn/typing/_tensor_shape.py +++ b/src/safeds/ml/nn/typing/_tensor_shape.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Self - from safeds._utils import _structural_hash from safeds._validation import _check_bounds, _ClosedBound @@ -12,13 +10,13 @@ class TensorShape: Parameters ---------- - dims: - A list of integers where each integer represents + dims: + A list of integers where each integer represents the size of the tensor in a particular dimension. """ - + def __init__(self, dims: list[int]) -> None: - self._dims = dims + self._dims = dims def get_size(self, dimension: int | None = None) -> int | list[int]: """ @@ -38,17 +36,17 @@ def get_size(self, dimension: int | None = None) -> int | list[int]: OutOfBoundsError: If the actual value is outside its expected range. """ - _check_bounds("dimension",dimension, lower_bound=_ClosedBound(0)) + _check_bounds("dimension", dimension, lower_bound=_ClosedBound(0)) if dimension is not None and dimension >= self.dimensionality: - #TODO maybe add error message indicating that the dimension is out of range - return 0 - if(dimension is None): + # TODO maybe add error message indicating that the dimension is out of range + return 0 + if dimension is None: return self._dims return self._dims[dimension] - + def __hash__(self) -> int: return _structural_hash(self._dims) - + @property def dimensionality(self) -> int: """ @@ -58,4 +56,4 @@ def dimensionality(self) -> int: ------- int: The number of dimensions of the tensor. """ - return len(self._dims) \ No newline at end of file + return len(self._dims) diff --git a/tests/safeds/ml/nn/layers/test_forward_layer.py b/tests/safeds/ml/nn/layers/test_forward_layer.py index 90925d597..06f37e7c1 100644 --- a/tests/safeds/ml/nn/layers/test_forward_layer.py +++ b/tests/safeds/ml/nn/layers/test_forward_layer.py @@ -5,7 +5,6 @@ from safeds.data.image.typing import ImageSize from safeds.exceptions import OutOfBoundsError from safeds.ml.nn.layers import ForwardLayer -from safeds.ml.nn.typing import TensorShape from torch import nn # TODO: Should be tested on a model, not a layer, since input size gets inferred diff --git a/tests/safeds/ml/nn/typing/test_tensor_shape.py b/tests/safeds/ml/nn/typing/test_tensor_shape.py index 262fabe46..f1825d911 100644 --- a/tests/safeds/ml/nn/typing/test_tensor_shape.py +++ b/tests/safeds/ml/nn/typing/test_tensor_shape.py @@ -3,36 +3,41 @@ from safeds.ml.nn.typing import TensorShape -def test_dims()-> None: - dims = [2, 3, 4] - ts = TensorShape(dims) - assert ts._dims == dims +def test_dims() -> None: + dims = [2, 3, 4] + ts = TensorShape(dims) + assert ts._dims == dims -def test_dimensionality()-> None: - dims = [2, 3, 4] - ts = TensorShape(dims) - assert ts.dimensionality == 3 -def test_get_size_no_dimension()-> None: +def test_dimensionality() -> None: + dims = [2, 3, 4] + ts = TensorShape(dims) + assert ts.dimensionality == 3 + + +def test_get_size_no_dimension() -> None: dims = [2, 3, 4] ts = TensorShape(dims) assert ts.get_size() == dims -def test_get_size_specific_dimension()-> None: + +def test_get_size_specific_dimension() -> None: dims = [2, 3, 4] ts = TensorShape(dims) assert ts.get_size(0) == 2 assert ts.get_size(1) == 3 assert ts.get_size(2) == 4 -def test_get_size_out_of_bounds_dimension()-> None: + +def test_get_size_out_of_bounds_dimension() -> None: dims = [2, 3, 4] ts = TensorShape(dims) with pytest.raises(OutOfBoundsError): ts.get_size(-1) -def test_hash()-> None: + +def test_hash() -> None: dims = [2, 3, 4] ts1 = TensorShape(dims) ts2 = TensorShape(dims) - assert hash(ts1) == hash(ts2) \ No newline at end of file + assert hash(ts1) == hash(ts2)