From 657ae95a3ff461029cb904abec25d49c86fb2f54 Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Mon, 27 Nov 2023 22:45:42 -0300 Subject: [PATCH 1/5] ENH: set Function operation prioritary with ufunc. --- rocketpy/mathutils/function.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index e52fbb2fb..2dab353de 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -24,6 +24,9 @@ class Function: extrapolation, plotting and algebra. """ + # Arithmetic priority + __array_ufunc__ = None + def __init__( self, source, @@ -1837,7 +1840,7 @@ def __add__(self, other): return Function(lambda x: (self.get_value(x) + other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex)): + if isinstance(other, (float, int, complex, np.ndarray)): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -1967,7 +1970,7 @@ def __mul__(self, other): return Function(lambda x: (self.get_value(x) * other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex)): + if isinstance(other, (float, int, complex, np.ndarray)): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -2056,7 +2059,7 @@ def __truediv__(self, other): return Function(lambda x: (self.get_value_opt(x) / other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex)): + if isinstance(other, (float, int, complex, np.ndarray)): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -2095,7 +2098,7 @@ def __rtruediv__(self, other): A Function object which gives the result of other(x)/self(x). """ # Check if Function object source is array and other is float - if isinstance(other, (float, int, complex)): + if isinstance(other, (float, int, complex, np.ndarray)): if isinstance(self.source, np.ndarray): # Operate on grid values ys = other / self.y_array @@ -2163,7 +2166,7 @@ def __pow__(self, other): return Function(lambda x: (self.get_value_opt(x) ** other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex)): + if isinstance(other, (float, int, complex, np.ndarray)): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -2202,7 +2205,7 @@ def __rpow__(self, other): A Function object which gives the result of other(x)**self(x). """ # Check if Function object source is array and other is float - if isinstance(other, (float, int, complex)): + if isinstance(other, (float, int, complex, np.ndarray)): if isinstance(self.source, np.ndarray): # Operate on grid values ys = other**self.y_array From b82810ff9275d0170a9e3b92b90fa90351eec89b Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Mon, 27 Nov 2023 22:46:17 -0300 Subject: [PATCH 2/5] TST: test result type of reverse Function arithmetic. --- tests/test_function.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_function.py b/tests/test_function.py index c67a21b30..fdaf092e3 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -390,3 +390,19 @@ def test_shepard_interpolation(x, y, z_expected): func = Function(source=source, inputs=["x", "y"], outputs=["z"]) z = func(x, y) assert np.isclose(z, z_expected, atol=1e-8).all() + + +def test_arithmetic_priority(): + """Test the arithmetic priority of the Function class, specially + comparing to the numpy array operations. + """ + func_lambda = Function(lambda x: x**2) + func_array = Function([(0, 0), (1, 1), (2, 4)]) + array = np.array([1]) + + assert isinstance(func_lambda + func_array, Function) + assert isinstance(func_array + func_lambda, Function) + assert isinstance(func_lambda + array, Function) + assert isinstance(array + func_lambda, Function) + assert isinstance(func_array + array, Function) + assert isinstance(array + func_array, Function) From 63881c4df73c0a6029464eeb0eff63974e9cb750 Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Tue, 28 Nov 2023 13:50:02 -0300 Subject: [PATCH 3/5] FIX: include numpy numeric types in Function arithmetic. --- rocketpy/mathutils/function.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index 2dab353de..8bdad9d2b 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -1840,7 +1840,9 @@ def __add__(self, other): return Function(lambda x: (self.get_value(x) + other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex, np.ndarray)): + if isinstance( + other, (float, int, complex, np.ndarray, np.integer, np.floating) + ): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -1970,7 +1972,9 @@ def __mul__(self, other): return Function(lambda x: (self.get_value(x) * other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex, np.ndarray)): + if isinstance( + other, (float, int, complex, np.ndarray, np.integer, np.floating) + ): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -2059,7 +2063,9 @@ def __truediv__(self, other): return Function(lambda x: (self.get_value_opt(x) / other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex, np.ndarray)): + if isinstance( + other, (float, int, complex, np.ndarray, np.integer, np.floating) + ): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -2098,7 +2104,9 @@ def __rtruediv__(self, other): A Function object which gives the result of other(x)/self(x). """ # Check if Function object source is array and other is float - if isinstance(other, (float, int, complex, np.ndarray)): + if isinstance( + other, (float, int, complex, np.ndarray, np.integer, np.floating) + ): if isinstance(self.source, np.ndarray): # Operate on grid values ys = other / self.y_array @@ -2166,7 +2174,9 @@ def __pow__(self, other): return Function(lambda x: (self.get_value_opt(x) ** other(x))) # If other is Float except... except AttributeError: - if isinstance(other, (float, int, complex, np.ndarray)): + if isinstance( + other, (float, int, complex, np.ndarray, np.integer, np.floating) + ): # Check if Function object source is array or callable if isinstance(self.source, np.ndarray): # Operate on grid values @@ -2205,7 +2215,9 @@ def __rpow__(self, other): A Function object which gives the result of other(x)**self(x). """ # Check if Function object source is array and other is float - if isinstance(other, (float, int, complex, np.ndarray)): + if isinstance( + other, (float, int, complex, np.ndarray, np.integer, np.floating) + ): if isinstance(self.source, np.ndarray): # Operate on grid values ys = other**self.y_array From 9162921f9d4ffae580d2e48b002ff7b555d13612 Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Tue, 28 Nov 2023 13:54:08 -0300 Subject: [PATCH 4/5] TST: expand testing of data type priorities. --- tests/test_function.py | 80 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/tests/test_function.py b/tests/test_function.py index fdaf092e3..5362b0486 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -392,17 +392,81 @@ def test_shepard_interpolation(x, y, z_expected): assert np.isclose(z, z_expected, atol=1e-8).all() -def test_arithmetic_priority(): - """Test the arithmetic priority of the Function class, specially - comparing to the numpy array operations. +@pytest.mark.parametrize("other", [1, 0.1, np.int_(1), np.float_(0.1), np.array([1])]) +def test_sum_arithmetic_priority(other): + """Test the arithmetic priority of the add operation of the Function class, + specially comparing to the numpy array operations. """ func_lambda = Function(lambda x: x**2) func_array = Function([(0, 0), (1, 1), (2, 4)]) - array = np.array([1]) assert isinstance(func_lambda + func_array, Function) assert isinstance(func_array + func_lambda, Function) - assert isinstance(func_lambda + array, Function) - assert isinstance(array + func_lambda, Function) - assert isinstance(func_array + array, Function) - assert isinstance(array + func_array, Function) + assert isinstance(func_lambda + other, Function) + assert isinstance(other + func_lambda, Function) + assert isinstance(func_array + other, Function) + assert isinstance(other + func_array, Function) + + +@pytest.mark.parametrize("other", [1, 0.1, np.int_(1), np.float_(0.1), np.array([1])]) +def test_sub_arithmetic_priority(other): + """Test the arithmetic priority of the sub operation of the Function class, + specially comparing to the numpy array operations. + """ + func_lambda = Function(lambda x: x**2) + func_array = Function([(0, 0), (1, 1), (2, 4)]) + + assert isinstance(func_lambda - func_array, Function) + assert isinstance(func_array - func_lambda, Function) + assert isinstance(func_lambda - other, Function) + assert isinstance(other - func_lambda, Function) + assert isinstance(func_array - other, Function) + assert isinstance(other - func_array, Function) + + +@pytest.mark.parametrize("other", [1, 0.1, np.int_(1), np.float_(0.1), np.array([1])]) +def test_mul_arithmetic_priority(other): + """Test the arithmetic priority of the mul operation of the Function class, + specially comparing to the numpy array operations. + """ + func_lambda = Function(lambda x: x**2) + func_array = Function([(0, 0), (1, 1), (2, 4)]) + + assert isinstance(func_lambda * func_array, Function) + assert isinstance(func_array * func_lambda, Function) + assert isinstance(func_lambda * other, Function) + assert isinstance(other * func_lambda, Function) + assert isinstance(func_array * other, Function) + assert isinstance(other * func_array, Function) + + +@pytest.mark.parametrize("other", [1, 0.1, np.int_(1), np.float_(0.1), np.array([1])]) +def test_truediv_arithmetic_priority(other): + """Test the arithmetic priority of the truediv operation of the Function class, + specially comparing to the numpy array operations. + """ + func_lambda = Function(lambda x: x**2) + func_array = Function([(1, 1), (2, 4)]) + + assert isinstance(func_lambda / func_array, Function) + assert isinstance(func_array / func_lambda, Function) + assert isinstance(func_lambda / other, Function) + assert isinstance(other / func_lambda, Function) + assert isinstance(func_array / other, Function) + assert isinstance(other / func_array, Function) + + +@pytest.mark.parametrize("other", [1, 0.1, np.int_(1), np.float_(0.1), np.array([1])]) +def test_pow_arithmetic_priority(other): + """Test the arithmetic priority of the pow operation of the Function class, + specially comparing to the numpy array operations. + """ + func_lambda = Function(lambda x: x**2) + func_array = Function([(0, 0), (1, 1), (2, 4)]) + + assert isinstance(func_lambda**func_array, Function) + assert isinstance(func_array**func_lambda, Function) + assert isinstance(func_lambda**other, Function) + assert isinstance(other**func_lambda, Function) + assert isinstance(func_array**other, Function) + assert isinstance(other**func_array, Function) From beb22ab4de0d78ea998150d598413a940eceb0aa Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Tue, 28 Nov 2023 14:00:57 -0300 Subject: [PATCH 5/5] MNT: update CHANGELOG. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 311189757..2abd6ed91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ straightforward as possible. ### Changed -- +- ENH: Function Reverse Arithmetic Priority [#488](https://github.com/RocketPy-Team/RocketPy/pull/488) ### Fixed