From 9c851be8472e9e8c4241180977ba0c1563c2c3a2 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 20 Jan 2024 17:57:04 -0800 Subject: [PATCH 01/12] inital commit with creation of lut.py and lut_test.py --- controller/common/lut.py | 18 ++++++++++++++++++ tests/unit/wingsail/common/test_lut.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 controller/common/lut.py create mode 100644 tests/unit/wingsail/common/test_lut.py diff --git a/controller/common/lut.py b/controller/common/lut.py new file mode 100644 index 0000000..12ef9a8 --- /dev/null +++ b/controller/common/lut.py @@ -0,0 +1,18 @@ +class LUT: + def __init__(self, lookup_table, interpolation_method="linear"): + self.table = lookup_table + self.interpolation = interpolation_method + + def __call__(self, x): + if self.interpolation == "linear": + return self.linearInterpolation(x) + elif self.interpolation == "spline": + return self.splineInterpolation(x) + else: + raise ValueError(self.interpolation + " is an unknown interpolation method!") + + def linearInterpolation(self, x): + return 0 + + def splineInterpolation(self, x): + return 0 diff --git a/tests/unit/wingsail/common/test_lut.py b/tests/unit/wingsail/common/test_lut.py new file mode 100644 index 0000000..71db2e3 --- /dev/null +++ b/tests/unit/wingsail/common/test_lut.py @@ -0,0 +1 @@ +# import pytest From e2de4fd5d7ad7526db37acf639e09829ec3236ef Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 20 Jan 2024 18:09:12 -0800 Subject: [PATCH 02/12] implemented initial implementation of linear and spline interpolation --- controller/common/lut.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index 12ef9a8..317e3d4 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -1,6 +1,9 @@ +import numpy as np + + class LUT: def __init__(self, lookup_table, interpolation_method="linear"): - self.table = lookup_table + self.table = np.array(lookup_table) self.interpolation = interpolation_method def __call__(self, x): @@ -12,7 +15,8 @@ def __call__(self, x): raise ValueError(self.interpolation + " is an unknown interpolation method!") def linearInterpolation(self, x): - return 0 + return np.interp(x, self.table[:, 0], self.table[:, 1]) def splineInterpolation(self, x): - return 0 + cs = np.interpolate.CubicSpline(self.table[:, 0], self.table[:, 1]) + return cs(x) From 078a42818cc1cb4000a12d6391355c85736ec7b4 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 20 Jan 2024 23:22:48 -0800 Subject: [PATCH 03/12] added basic test to test_lut and added robustness to constructor of lut --- controller/common/lut.py | 4 +++- tests/unit/wingsail/common/test_lut.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index 317e3d4..1d6d702 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -5,6 +5,8 @@ class LUT: def __init__(self, lookup_table, interpolation_method="linear"): self.table = np.array(lookup_table) self.interpolation = interpolation_method + if self.interpolation != "linear" and self.interpolation != "spline": + raise ValueError(self.interpolation + " is an unknown interpolation method!") def __call__(self, x): if self.interpolation == "linear": @@ -12,7 +14,7 @@ def __call__(self, x): elif self.interpolation == "spline": return self.splineInterpolation(x) else: - raise ValueError(self.interpolation + " is an unknown interpolation method!") + return 0 def linearInterpolation(self, x): return np.interp(x, self.table[:, 0], self.table[:, 1]) diff --git a/tests/unit/wingsail/common/test_lut.py b/tests/unit/wingsail/common/test_lut.py index 71db2e3..f74bcd4 100644 --- a/tests/unit/wingsail/common/test_lut.py +++ b/tests/unit/wingsail/common/test_lut.py @@ -1 +1,16 @@ # import pytest + +from controller.common.lut import LUT + + +class TestLUT: + def test_LUT_constructor(self): + look_up_table = [[50000, 5.75], [100000, 6.75], [200000, 7], [500000, 9.75], [1000000, 10]] + testLUT = LUT(look_up_table) + assert testLUT(40000) == 5.75 + + # def test_linear_interpolation + + # def test_spline_interpolation + + # test_LUT_constructor From b224731d8bd3f772ccb42599c03bd37cf1076c36 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 25 Jan 2024 18:22:29 -0800 Subject: [PATCH 04/12] added comments from sync up --- controller/common/lut.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/controller/common/lut.py b/controller/common/lut.py index 1d6d702..d1eddb0 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -5,9 +5,18 @@ class LUT: def __init__(self, lookup_table, interpolation_method="linear"): self.table = np.array(lookup_table) self.interpolation = interpolation_method + # look at python "match" statement to replace below if self.interpolation != "linear" and self.interpolation != "spline": raise ValueError(self.interpolation + " is an unknown interpolation method!") + # Devon suggestions + # rather then if else, store a reference to the function of the interpolation you want and call + # it here. You can define the reference in the constructor + + # make self.interpolation field private + # also make the interpolation methods private + + # add variable and return typing def __call__(self, x): if self.interpolation == "linear": return self.linearInterpolation(x) From 664b47fb42122fbcde43c5db7786cbf84304f803 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 27 Jan 2024 14:49:57 -0800 Subject: [PATCH 05/12] completed testing suite for LUT class and implemented feedback from last sync up --- controller/common/lut.py | 41 +++++++++------------ tests/unit/wingsail/common/test_lut.py | 49 ++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index d1eddb0..1c0070b 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -1,33 +1,26 @@ import numpy as np +from scipy import interpolate class LUT: - def __init__(self, lookup_table, interpolation_method="linear"): - self.table = np.array(lookup_table) - self.interpolation = interpolation_method - # look at python "match" statement to replace below - if self.interpolation != "linear" and self.interpolation != "spline": - raise ValueError(self.interpolation + " is an unknown interpolation method!") + def __init__(self, lookup_table: list, interpolation_method: str = "linear"): + self.__table = np.array(lookup_table) + self.__interpolation = interpolation_method - # Devon suggestions - # rather then if else, store a reference to the function of the interpolation you want and call - # it here. You can define the reference in the constructor + match self.__interpolation: + case "linear": + self.__method = self.__linearInterpolation + case "spline": + self.__method = self.__splineInterpolation + case _: + raise ValueError(self.__interpolation + " is an unknown interpolation method!") - # make self.interpolation field private - # also make the interpolation methods private + def __call__(self, x: float) -> float: + return self.__method(x) - # add variable and return typing - def __call__(self, x): - if self.interpolation == "linear": - return self.linearInterpolation(x) - elif self.interpolation == "spline": - return self.splineInterpolation(x) - else: - return 0 + def __linearInterpolation(self, x: float) -> float: + return np.interp(x, self.__table[:, 0], self.__table[:, 1]) - def linearInterpolation(self, x): - return np.interp(x, self.table[:, 0], self.table[:, 1]) - - def splineInterpolation(self, x): - cs = np.interpolate.CubicSpline(self.table[:, 0], self.table[:, 1]) + def __splineInterpolation(self, x: float) -> float: + cs = interpolate.CubicSpline(self.__table[:, 0], self.__table[:, 1]) return cs(x) diff --git a/tests/unit/wingsail/common/test_lut.py b/tests/unit/wingsail/common/test_lut.py index f74bcd4..a5da287 100644 --- a/tests/unit/wingsail/common/test_lut.py +++ b/tests/unit/wingsail/common/test_lut.py @@ -1,16 +1,53 @@ # import pytest +import numpy as np +from scipy import interpolate + from controller.common.lut import LUT class TestLUT: + # Intialize lookup table + look_up_table = [[50000, 5.75], [100000, 6.75], [200000, 7], [500000, 9.75], [1000000, 10]] + def test_LUT_constructor(self): - look_up_table = [[50000, 5.75], [100000, 6.75], [200000, 7], [500000, 9.75], [1000000, 10]] - testLUT = LUT(look_up_table) - assert testLUT(40000) == 5.75 + # set up + testLUT = LUT(self.look_up_table) + + # test that LUT return a known value + assert testLUT(50000) == 5.75 + + def test_unknown_interpolation_exception(self): + try: + testLUT = LUT(self.look_up_table, "gabagool") + assert False # failure: constructor accepted deli meat interpolation method + + except ValueError: + assert True + + except: + assert False # failure: constructor threw wrong exception + + def test_linear_interpolation(self): + # set up + testLUT = LUT(self.look_up_table) + table = np.array(self.look_up_table) + test_values = list(range(50000, 1100000, 10000)) + + # Test that linear interpolation does not extrapolate + assert testLUT(10000) == 5.75 + assert testLUT(2000000) == 10 - # def test_linear_interpolation + # Test that LUT returns same values as np linear interpolate function + for value in test_values: + assert testLUT(value) == np.interp(value, table[:, 0], table[:, 1]) - # def test_spline_interpolation + def test_spline_interpolation(self): + testLUT = LUT(self.look_up_table, "spline") + table = np.array(self.look_up_table) + test_values = list(range(10000, 2100000, 10000)) + cs = interpolate.CubicSpline(table[:, 0], table[:, 1]) - # test_LUT_constructor + # Test that LUT returns same values as cubic interpolate function + for value in test_values: + assert testLUT(value) == cs(value) From e09a58d8ea5523367f70d93416c54ba653f48708 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 27 Jan 2024 14:55:32 -0800 Subject: [PATCH 06/12] documented LUT class --- controller/common/lut.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/controller/common/lut.py b/controller/common/lut.py index 1c0070b..24ccb0b 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -3,7 +3,26 @@ class LUT: + """ + Class for performing look-up table interpolation. + + Methods: + __init__: Initializes the LUT object with lookup table data and interpolation method. + __call__: Calls the interpolation method with the given input. + """ + def __init__(self, lookup_table: list, interpolation_method: str = "linear"): + """ + Initializes the LUT object. + + Args: + lookup_table (list): A list of tuples or lists containing x-y data points for + interpolation. Format is [[x-values],[y-values]] + interpolation_method (str): Interpolation method to use. Default is "linear". + + Raises: + ValueError: If the specified interpolation method is unknown. + """ self.__table = np.array(lookup_table) self.__interpolation = interpolation_method @@ -16,6 +35,16 @@ def __init__(self, lookup_table: list, interpolation_method: str = "linear"): raise ValueError(self.__interpolation + " is an unknown interpolation method!") def __call__(self, x: float) -> float: + """ + Calls the interpolation method with the given input. + + Args: + x (float): The input value to interpolate. + + Returns: + float: The interpolated value using the interpolation method defined when LUT instance + creation. + """ return self.__method(x) def __linearInterpolation(self, x: float) -> float: From 34968983dfdc4b56e486abba1b20a2c5f9738ec0 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 3 Feb 2024 17:48:30 -0800 Subject: [PATCH 07/12] resolved issues raised in PR#25 --- controller/common/lut.py | 33 ++++++++++++++--- tests/unit/wingsail/common/test_lut.py | 50 +++++++++++++++----------- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index 24ccb0b..f14051a 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -1,3 +1,5 @@ +from typing import List, Union + import numpy as np from scipy import interpolate @@ -11,19 +13,31 @@ class LUT: __call__: Calls the interpolation method with the given input. """ - def __init__(self, lookup_table: list, interpolation_method: str = "linear"): + def __init__( + self, + lookup_table: Union[List[List[int | float]], np.ndarray], + interpolation_method: str = "linear", + ): """ Initializes the LUT object. Args: lookup_table (list): A list of tuples or lists containing x-y data points for - interpolation. Format is [[x-values],[y-values]] + interpolation. Format is [[x1,y1], [x2,y2], ..., [xn,yn]] interpolation_method (str): Interpolation method to use. Default is "linear". Raises: ValueError: If the specified interpolation method is unknown. """ - self.__table = np.array(lookup_table) + if type(lookup_table) is np.ndarray: + self.__table = lookup_table + else: + self.__table = np.array(lookup_table) + + self.verifyTable(self.__table) + self.x = self.__table[:, 0] + self.y = self.__table[:, 1] + self.__interpolation = interpolation_method match self.__interpolation: @@ -48,8 +62,17 @@ def __call__(self, x: float) -> float: return self.__method(x) def __linearInterpolation(self, x: float) -> float: - return np.interp(x, self.__table[:, 0], self.__table[:, 1]) + return np.interp(x, self.x, self.y) def __splineInterpolation(self, x: float) -> float: - cs = interpolate.CubicSpline(self.__table[:, 0], self.__table[:, 1]) + cs = interpolate.CubicSpline(self.x, self.y) return cs(x) + + def verifyTable(self, table: np.ndarray) -> None: + if len(table.shape) == 2: + if table.shape[1] == 2: + return + else: + raise ValueError("Input table is invalid shape.") + else: + raise ValueError("Input table is invalid shape.") diff --git a/tests/unit/wingsail/common/test_lut.py b/tests/unit/wingsail/common/test_lut.py index a5da287..53b5dc9 100644 --- a/tests/unit/wingsail/common/test_lut.py +++ b/tests/unit/wingsail/common/test_lut.py @@ -1,6 +1,7 @@ -# import pytest +import math import numpy as np +import pytest from scipy import interpolate from controller.common.lut import LUT @@ -15,39 +16,48 @@ def test_LUT_constructor(self): testLUT = LUT(self.look_up_table) # test that LUT return a known value - assert testLUT(50000) == 5.75 + assert math.isclose(testLUT(50000), 5.75) def test_unknown_interpolation_exception(self): - try: + with pytest.raises(ValueError): testLUT = LUT(self.look_up_table, "gabagool") - assert False # failure: constructor accepted deli meat interpolation method - except ValueError: - assert True + def test_invalid_table_exception(self): + invalid_table1 = [[10000, 10000, 10000], [1, 1, 1]] + invalid_table2 = [10000, 10000, 10000] + invalid_table3 = [[0, 1], 10000, 10000] - except: - assert False # failure: constructor threw wrong exception + with pytest.raises(ValueError): + testLUT = LUT(invalid_table1) - def test_linear_interpolation(self): + with pytest.raises(ValueError): + testLUT = LUT(invalid_table2) + + with pytest.raises(ValueError): + testLUT = LUT(invalid_table3) + + @pytest.mark.parametrize("linear_test_values", list(range(50000, 1100000, 10000))) + def test_linear_interpolation(self, linear_test_values): # set up testLUT = LUT(self.look_up_table) table = np.array(self.look_up_table) - test_values = list(range(50000, 1100000, 10000)) - - # Test that linear interpolation does not extrapolate - assert testLUT(10000) == 5.75 - assert testLUT(2000000) == 10 # Test that LUT returns same values as np linear interpolate function - for value in test_values: - assert testLUT(value) == np.interp(value, table[:, 0], table[:, 1]) + assert math.isclose( + testLUT(linear_test_values), np.interp(linear_test_values, table[:, 0], table[:, 1]) + ) + + def test_linear_extrapolation(self): + testLUT = LUT(self.look_up_table) + # Test that linear interpolation does not extrapolate + assert math.isclose(testLUT(10000), 5.75) + assert math.isclose(testLUT(2000000), 10) - def test_spline_interpolation(self): + @pytest.mark.parametrize("spline_test_values", list(range(10000, 2100000, 10000))) + def test_spline_interpolation_extrapolation(self, spline_test_values): testLUT = LUT(self.look_up_table, "spline") table = np.array(self.look_up_table) - test_values = list(range(10000, 2100000, 10000)) cs = interpolate.CubicSpline(table[:, 0], table[:, 1]) # Test that LUT returns same values as cubic interpolate function - for value in test_values: - assert testLUT(value) == cs(value) + assert math.isclose(testLUT(spline_test_values), cs(spline_test_values)) From 1b4f203700b1b547faa120d9bd358c1ddda0af89 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 3 Feb 2024 17:54:05 -0800 Subject: [PATCH 08/12] made verifyTable method private --- controller/common/lut.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index f14051a..449d20a 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -34,7 +34,7 @@ def __init__( else: self.__table = np.array(lookup_table) - self.verifyTable(self.__table) + self.__verifyTable(self.__table) self.x = self.__table[:, 0] self.y = self.__table[:, 1] @@ -68,7 +68,7 @@ def __splineInterpolation(self, x: float) -> float: cs = interpolate.CubicSpline(self.x, self.y) return cs(x) - def verifyTable(self, table: np.ndarray) -> None: + def __verifyTable(self, table: np.ndarray) -> None: if len(table.shape) == 2: if table.shape[1] == 2: return From ae2ccf159ceb52ab504dca41c99d33501b51807e Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 8 Feb 2024 22:07:49 -0800 Subject: [PATCH 09/12] implemented resolutions to issues raised by Devon --- controller/common/lut.py | 55 ++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index 449d20a..fffd915 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -1,8 +1,11 @@ -from typing import List, Union +from typing import List import numpy as np +from numpy.typing import NDArray from scipy import interpolate +from controller.common.types import Scalar + class LUT: """ @@ -15,38 +18,41 @@ class LUT: def __init__( self, - lookup_table: Union[List[List[int | float]], np.ndarray], + lookup_table: List[List[Scalar]] | NDArray, interpolation_method: str = "linear", ): """ Initializes the LUT object. Args: - lookup_table (list): A list of tuples or lists containing x-y data points for - interpolation. Format is [[x1,y1], [x2,y2], ..., [xn,yn]] + lookup_table (List[List[Scalar]]|NDArray): A list of lists or NDArray containing x-y + data points for interpolation. Shape should be (n, 2) interpolation_method (str): Interpolation method to use. Default is "linear". Raises: - ValueError: If the specified interpolation method is unknown. + ValueError: If the specified interpolation method is unknown + or if the table shape is incorrect. """ - if type(lookup_table) is np.ndarray: - self.__table = lookup_table + if isinstance(lookup_table, np.ndarray): + table = lookup_table else: - self.__table = np.array(lookup_table) + table = np.array(lookup_table) - self.__verifyTable(self.__table) - self.x = self.__table[:, 0] - self.y = self.__table[:, 1] + self.__verifyTable(table) + self.x = table[:, 0] + self.y = table[:, 1] - self.__interpolation = interpolation_method + self.__interpolation_method = interpolation_method - match self.__interpolation: + match self.__interpolation_method: case "linear": - self.__method = self.__linearInterpolation + self.__interpolation_function = self.__linearInterpolation case "spline": - self.__method = self.__splineInterpolation + self.__interpolation_function = self.__splineInterpolation case _: - raise ValueError(self.__interpolation + " is an unknown interpolation method!") + raise ValueError( + self.__interpolation_method + " is an unknown interpolation method!" + ) def __call__(self, x: float) -> float: """ @@ -59,20 +65,15 @@ def __call__(self, x: float) -> float: float: The interpolated value using the interpolation method defined when LUT instance creation. """ - return self.__method(x) + return self.__interpolation_function(x) - def __linearInterpolation(self, x: float) -> float: + def __linearInterpolation(self, x: Scalar) -> float: return np.interp(x, self.x, self.y) - def __splineInterpolation(self, x: float) -> float: + def __splineInterpolation(self, x: Scalar) -> float: cs = interpolate.CubicSpline(self.x, self.y) return cs(x) - def __verifyTable(self, table: np.ndarray) -> None: - if len(table.shape) == 2: - if table.shape[1] == 2: - return - else: - raise ValueError("Input table is invalid shape.") - else: - raise ValueError("Input table is invalid shape.") + def __verifyTable(self, table: NDArray) -> None: + if (len(table.shape) != 2) or table.shape[1] != 2: + raise ValueError(f"Input table has shape {table.shape}, but expected shape of (n, 2)") From 13bb0fd7db352e45e54120d744b79011678bc704 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 8 Feb 2024 22:58:58 -0800 Subject: [PATCH 10/12] implemented resolutions to issues raised by Devon for test class --- tests/unit/wingsail/common/test_lut.py | 45 +++++++++++++++++--------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/tests/unit/wingsail/common/test_lut.py b/tests/unit/wingsail/common/test_lut.py index 53b5dc9..fd69069 100644 --- a/tests/unit/wingsail/common/test_lut.py +++ b/tests/unit/wingsail/common/test_lut.py @@ -10,31 +10,46 @@ class TestLUT: # Intialize lookup table look_up_table = [[50000, 5.75], [100000, 6.75], [200000, 7], [500000, 9.75], [1000000, 10]] + look_up_np_array = np.array(look_up_table) def test_LUT_constructor(self): # set up testLUT = LUT(self.look_up_table) - + testLUT2 = LUT(self.look_up_np_array) # test that LUT return a known value assert math.isclose(testLUT(50000), 5.75) + assert math.isclose(testLUT2(50000), 5.75) def test_unknown_interpolation_exception(self): with pytest.raises(ValueError): testLUT = LUT(self.look_up_table, "gabagool") - def test_invalid_table_exception(self): - invalid_table1 = [[10000, 10000, 10000], [1, 1, 1]] - invalid_table2 = [10000, 10000, 10000] - invalid_table3 = [[0, 1], 10000, 10000] - + @pytest.mark.parametrize( + "invalid_table", + [ + [[10000, 10000, 10000], [1, 1, 1]], + [10000, 10000, 10000], + [[0, 1], 10000, 10000], + np.array([[10000, 10000, 10000], [1, 1, 1]]), + np.array([10000, 10000, 10000]), + np.array([[[0, 1]], [[0, 1]], [[0, 1]]]), + ], + ) + def test_invalid_table_exception(self, invalid_table): with pytest.raises(ValueError): - testLUT = LUT(invalid_table1) - - with pytest.raises(ValueError): - testLUT = LUT(invalid_table2) - + testLUT = LUT(invalid_table) + + @pytest.mark.parametrize( + "invalid_table", + [ + np.array([[10000, 10000, 10000], [1, 1, 1]]), + np.array([10000, 10000, 10000]), + np.array([[[0, 1]], [[0, 1]], [[0, 1]]]), + ], + ) + def test_invalid_numpy_array_exception(self, invalid_table): with pytest.raises(ValueError): - testLUT = LUT(invalid_table3) + testLUT = LUT(invalid_table) @pytest.mark.parametrize("linear_test_values", list(range(50000, 1100000, 10000))) def test_linear_interpolation(self, linear_test_values): @@ -47,11 +62,11 @@ def test_linear_interpolation(self, linear_test_values): testLUT(linear_test_values), np.interp(linear_test_values, table[:, 0], table[:, 1]) ) - def test_linear_extrapolation(self): + @pytest.mark.parametrize("test_value, expected_value", [(1000, 5.75), (2000000, 10)]) + def test_linear_extrapolation(self, test_value, expected_value): testLUT = LUT(self.look_up_table) # Test that linear interpolation does not extrapolate - assert math.isclose(testLUT(10000), 5.75) - assert math.isclose(testLUT(2000000), 10) + assert math.isclose(testLUT(test_value), expected_value) @pytest.mark.parametrize("spline_test_values", list(range(10000, 2100000, 10000))) def test_spline_interpolation_extrapolation(self, spline_test_values): From ea3f919e8e2bcbcb41952f27d3e58fb986efa14b Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 9 Feb 2024 12:00:48 -0800 Subject: [PATCH 11/12] minor edit to doc string --- controller/common/lut.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index fffd915..8969313 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -25,7 +25,7 @@ def __init__( Initializes the LUT object. Args: - lookup_table (List[List[Scalar]]|NDArray): A list of lists or NDArray containing x-y + lookup_table (List[List[Scalar]] or NDArray): A list of lists or NDArray containing x-y data points for interpolation. Shape should be (n, 2) interpolation_method (str): Interpolation method to use. Default is "linear". From c6d17fbca079dd0be8fd98b15b13b4ea989afc4d Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 10 Feb 2024 10:19:16 -0800 Subject: [PATCH 12/12] resolved error flake8 pointed out --- controller/common/lut.py | 7 ++++++- tests/unit/wingsail/common/test_lut.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/controller/common/lut.py b/controller/common/lut.py index 8969313..c7a0d24 100644 --- a/controller/common/lut.py +++ b/controller/common/lut.py @@ -68,7 +68,12 @@ def __call__(self, x: float) -> float: return self.__interpolation_function(x) def __linearInterpolation(self, x: Scalar) -> float: - return np.interp(x, self.x, self.y) + output = np.interp(x, self.x, self.y) + if isinstance(output, np.ndarray): + raise ValueError( + "linear interpolation returned a NDArray when it should have returned a float" + ) + return output def __splineInterpolation(self, x: Scalar) -> float: cs = interpolate.CubicSpline(self.x, self.y) diff --git a/tests/unit/wingsail/common/test_lut.py b/tests/unit/wingsail/common/test_lut.py index fd69069..dc7c1d9 100644 --- a/tests/unit/wingsail/common/test_lut.py +++ b/tests/unit/wingsail/common/test_lut.py @@ -23,6 +23,7 @@ def test_LUT_constructor(self): def test_unknown_interpolation_exception(self): with pytest.raises(ValueError): testLUT = LUT(self.look_up_table, "gabagool") + assert math.isclose(testLUT(50000), 5.75) @pytest.mark.parametrize( "invalid_table", @@ -38,6 +39,7 @@ def test_unknown_interpolation_exception(self): def test_invalid_table_exception(self, invalid_table): with pytest.raises(ValueError): testLUT = LUT(invalid_table) + assert math.isclose(testLUT(50000), 5.75) @pytest.mark.parametrize( "invalid_table", @@ -50,6 +52,7 @@ def test_invalid_table_exception(self, invalid_table): def test_invalid_numpy_array_exception(self, invalid_table): with pytest.raises(ValueError): testLUT = LUT(invalid_table) + assert math.isclose(testLUT(50000), 5.75) @pytest.mark.parametrize("linear_test_values", list(range(50000, 1100000, 10000))) def test_linear_interpolation(self, linear_test_values): @@ -68,6 +71,13 @@ def test_linear_extrapolation(self, test_value, expected_value): # Test that linear interpolation does not extrapolate assert math.isclose(testLUT(test_value), expected_value) + @pytest.mark.parametrize("test_value", [[100, 200, 300]]) + def test_linear_interpolation_exception(self, test_value): + testLUT = LUT(self.look_up_table) + # Test that linear interpolation does not extrapolate + with pytest.raises(ValueError): + testLUT(test_value) + @pytest.mark.parametrize("spline_test_values", list(range(10000, 2100000, 10000))) def test_spline_interpolation_extrapolation(self, spline_test_values): testLUT = LUT(self.look_up_table, "spline")