From 17f8cdba2ba58b6afe009edbd7883e9de81592ec Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Mon, 19 Oct 2020 12:28:49 +0100 Subject: [PATCH 01/27] Added sin and cos series expansions --- clifford/_multivector.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/clifford/_multivector.py b/clifford/_multivector.py index 1790e059..a5e7a511 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -101,6 +101,18 @@ def _newMV(self, newValue=None, *, dtype: np.dtype = None) -> 'MultiVector': def exp(self) -> 'MultiVector': return general_exp(self) + def cos(self): + op = 0 * self + for n in range(20): + op += ((-1) ** (n) / math.gamma(2 * n + 1)) * self ** (2 * n) + return op + + def sin(self): + op = 0 * self + for n in range(20): + op += ((-1) ** (n) / math.gamma(2 * n + 2)) * self ** (2 * n + 1) + return op + def vee(self, other) -> 'MultiVector': r""" Vee product :math:`A \vee B`. From 0bee59d3ead41ec28f1ebf405de1cbd38205c1ea Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Tue, 20 Oct 2020 13:50:17 +0100 Subject: [PATCH 02/27] Added tan and hyperbolic functions too --- clifford/_multivector.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/clifford/_multivector.py b/clifford/_multivector.py index a5e7a511..47c7dbbf 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -102,17 +102,47 @@ def exp(self) -> 'MultiVector': return general_exp(self) def cos(self): + """ + A taylor series expansion for cos + """ op = 0 * self for n in range(20): op += ((-1) ** (n) / math.gamma(2 * n + 1)) * self ** (2 * n) return op def sin(self): + """ + A taylor series expansion for sin + """ op = 0 * self for n in range(20): op += ((-1) ** (n) / math.gamma(2 * n + 2)) * self ** (2 * n + 1) return op + def tan(self): + return self.sin()/self.cos() + + def sinh(self): + """ + A taylor series expansion for sinh + """ + op = 0 * self + for n in range(20): + op += (1 / math.gamma(2 * n + 2)) * self ** (2 * n + 1) + return op + + def cosh(self): + """ + A taylor series expansion for cosh + """ + op = 0 * self + for n in range(20): + op += (1 / math.gamma(2 * n + 1)) * self ** (2 * n) + return op + + def tanh(self): + return self.sinh() / self.cosh() + def vee(self, other) -> 'MultiVector': r""" Vee product :math:`A \vee B`. From 4ac8df855b0081647067883c846e15847aad44d7 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Wed, 21 Oct 2020 14:56:09 +0100 Subject: [PATCH 03/27] Add docstrings --- clifford/_multivector.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clifford/_multivector.py b/clifford/_multivector.py index 47c7dbbf..08129f04 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -120,6 +120,10 @@ def sin(self): return op def tan(self): + """ + The tan function as the ratio of sin and cos + Note. It may be better to implement this as its own taylor series. + """ return self.sin()/self.cos() def sinh(self): @@ -141,6 +145,10 @@ def cosh(self): return op def tanh(self): + """ + The tanh function as the ratio of sinh and cosh + Note. It may be better to implement this as its own taylor series. + """ return self.sinh() / self.cosh() def vee(self, other) -> 'MultiVector': From 3d37b017a183bbbbed30faa8451c94591a0acdaf Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Wed, 21 Oct 2020 15:11:28 +0100 Subject: [PATCH 04/27] JIT general_exp --- clifford/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clifford/__init__.py b/clifford/__init__.py index 15ca452c..71284e56 100644 --- a/clifford/__init__.py +++ b/clifford/__init__.py @@ -232,13 +232,14 @@ def grade_obj_func(objin_val, gradeList, threshold): return np.argmax(modal_value_count) +@_numba_utils.njit def general_exp(x, max_order=15): """ This implements the series expansion of e**mv where mv is a multivector The parameter order is the maximum order of the taylor series to use """ - result = 1.0 + result = 1.0 + 0.0*x if max_order == 0: return result @@ -258,13 +259,13 @@ def general_exp(x, max_order=15): for i in range(1, max_order): if np.any(np.abs(tmp.value) > _settings._eps): tmp = tmp*scaled * (1.0 / i) - result += tmp + result = result + tmp else: break # undo scaling while scale > 1: - result *= result + result = result*result scale >>= 1 return result From c7f0cd03dae374030c991ba51114466eed4189f3 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Wed, 21 Oct 2020 15:20:08 +0100 Subject: [PATCH 05/27] Move general trig functions --- clifford/__init__.py | 57 ++++++++++++++++++++++++++++++++++++++++ clifford/_multivector.py | 46 +++++--------------------------- 2 files changed, 64 insertions(+), 39 deletions(-) diff --git a/clifford/__init__.py b/clifford/__init__.py index 71284e56..2c874a78 100644 --- a/clifford/__init__.py +++ b/clifford/__init__.py @@ -78,6 +78,7 @@ import itertools import warnings from typing import List, Tuple, Set +import math # Major library imports. import numpy as np @@ -270,6 +271,62 @@ def general_exp(x, max_order=15): return result +def general_sin(X, max_order=20): + """ + A taylor series expansion for sin + """ + op = 0 * X + for n in range(max_order): + op += ((-1) ** (n) / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + return op + + +def general_cos(X, max_order=20): + """ + A taylor series expansion for cos + """ + op = 0 * X + for n in range(max_order): + op += ((-1) ** (n) / math.gamma(2 * n + 1)) * X ** (2 * n) + return op + + +def general_tan(X, max_order=20): + """ + The tan function as the ratio of sin and cos + Note. It would probably be better to implement this as its own taylor series. + """ + return general_sin(X, max_order) / general_cos(X, max_order) + + +def general_sinh(X, max_order=20): + """ + A taylor series expansion for sinh + """ + op = 0 * X + for n in range(max_order): + op += (1 / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + return op + + +def general_cosh(X, max_order=20): + """ + A taylor series expansion for cosh + """ + op = 0 * X + for n in range(max_order): + op += (1 / math.gamma(2 * n + 1)) * X ** (2 * n) + return op + + +def general_tanh(X, max_order=20): + """ + The tanh function as the ratio of sinh and cosh + Note. It would probably be better to implement this as its own taylor series. + """ + return general_sinh(X, max_order) / general_cosh(X, max_order) + + def grade_obj(objin, threshold=0.0000001): ''' Returns the modal grade of a multivector diff --git a/clifford/_multivector.py b/clifford/_multivector.py index 08129f04..b10e839e 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -6,7 +6,7 @@ import numpy as np import clifford as cf -from . import general_exp +from . import general_exp, general_sin, general_cos, general_tan, general_sinh, general_cosh, general_tanh from . import _settings from ._layout_helpers import layout_short_name @@ -102,54 +102,22 @@ def exp(self) -> 'MultiVector': return general_exp(self) def cos(self): - """ - A taylor series expansion for cos - """ - op = 0 * self - for n in range(20): - op += ((-1) ** (n) / math.gamma(2 * n + 1)) * self ** (2 * n) - return op + return general_cos(self) def sin(self): - """ - A taylor series expansion for sin - """ - op = 0 * self - for n in range(20): - op += ((-1) ** (n) / math.gamma(2 * n + 2)) * self ** (2 * n + 1) - return op + return general_sin(self) def tan(self): - """ - The tan function as the ratio of sin and cos - Note. It may be better to implement this as its own taylor series. - """ - return self.sin()/self.cos() + return general_tan(self) def sinh(self): - """ - A taylor series expansion for sinh - """ - op = 0 * self - for n in range(20): - op += (1 / math.gamma(2 * n + 2)) * self ** (2 * n + 1) - return op + return general_sinh(self) def cosh(self): - """ - A taylor series expansion for cosh - """ - op = 0 * self - for n in range(20): - op += (1 / math.gamma(2 * n + 1)) * self ** (2 * n) - return op + return general_cosh(self) def tanh(self): - """ - The tanh function as the ratio of sinh and cosh - Note. It may be better to implement this as its own taylor series. - """ - return self.sinh() / self.cosh() + return general_tanh(self) def vee(self, other) -> 'MultiVector': r""" From a7b671d908195550e972f5f002ca8e7ec14c5bd1 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Wed, 21 Oct 2020 18:59:05 +0100 Subject: [PATCH 06/27] Fix inverse and add trig tests --- clifford/_layout.py | 4 +- clifford/test/test_trig_functions.py | 79 ++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 clifford/test/test_trig_functions.py diff --git a/clifford/_layout.py b/clifford/_layout.py index 33b00a59..62010f4f 100644 --- a/clifford/_layout.py +++ b/clifford/_layout.py @@ -577,11 +577,11 @@ def _hitzer_inverse(self): """ Performs the inversion operation as described in the paper :cite:`Hitzer_Sangwine_2017` """ - tot = np.sum(self.sig != 0) + tot = len(self.sig) @_numba_utils.njit def hitzer_inverse(operand): if tot == 0: - numerator = operand.layout.scalar + numerator = 1 + 0*operand elif tot == 1: # Equation 4.3 mv_invol = operand.gradeInvol() diff --git a/clifford/test/test_trig_functions.py b/clifford/test/test_trig_functions.py new file mode 100644 index 00000000..e852083c --- /dev/null +++ b/clifford/test/test_trig_functions.py @@ -0,0 +1,79 @@ + +from .. import Cl +import numpy as np +import pytest + + +class TestScalarProperties: + + @pytest.fixture() + def element(self): + alg, blades = Cl(0, 0, 0) + return blades[''] + + def test_sin(self, element): + for x in np.linspace(0, 2*np.pi, 100): + assert abs(np.sin(x*element).value[0] - np.sin(x)) < 1E-10 + + def test_cos(self, element): + for x in np.linspace(0, 2*np.pi, 100): + assert abs(np.cos(x*element).value[0] - np.cos(x)) < 1E-10 + + def test_tan(self, element): + for x in np.linspace(-0.9*np.pi/2, 0.9*np.pi/2, 100): + assert abs(np.tan(x*element).value[0] - np.tan(x)) < 1E-10 + + def test_sinh(self, element): + for x in np.linspace(0, 2*np.pi, 100): + assert abs(np.sinh(x*element).value[0] - np.sinh(x)) < 1E-10 + + def test_cosh(self, element): + for x in np.linspace(0, 2*np.pi, 100): + assert abs(np.cosh(x*element).value[0] - np.cosh(x)) < 1E-10 + + def test_tanh(self, element): + for x in np.linspace(-np.pi, np.pi, 100): + assert abs(np.tanh(x*element).value[0] - np.tanh(x)) < 1E-10 + + +class TestDualNumberProperties: + @pytest.fixture() + def element(self): + alg, blades = Cl(0, 0, 1) + return blades[''], blades['e1'] + + def test_sin(self, element): + for x in np.linspace(0, 2*np.pi, 10): + result = np.sin(x * element[0] + element[1]) + assert abs(result.value[0] - np.sin(x)) < 1E-10 + assert abs(result.value[1] - np.cos(x)) < 1E-10 + + def test_cos(self, element): + for x in np.linspace(0, 2*np.pi, 10): + result = np.cos(x * element[0] + element[1]) + assert abs(result.value[0] - np.cos(x)) < 1E-10 + assert abs(result.value[1] + np.sin(x)) < 1E-10 + + def test_tan(self, element): + for x in np.linspace(0, 2*np.pi, 10): + result = np.tan(x * element[0] + element[1]) + assert abs(result.value[0] - np.tan(x)) < 1E-10 + assert abs(result.value[1] - (1/np.cos(x)**2)) < 1E-10 + + def test_sinh(self, element): + for x in np.linspace(0, 2*np.pi, 10): + result = np.sinh(x * element[0] + element[1]) + assert abs(result.value[0] - np.sinh(x)) < 1E-10 + assert abs(result.value[1] - np.cosh(x)) < 1E-10 + + def test_cosh(self, element): + for x in np.linspace(0, 2*np.pi, 10): + result = np.cosh(x * element[0] + element[1]) + assert abs(result.value[0] - np.cosh(x)) < 1E-10 + assert abs(result.value[1] - np.sinh(x)) < 1E-10 + + def test_tanh(self, element): + for x in np.linspace(0, 2*np.pi, 10): + result = np.tanh(x * element[0] + element[1]) + assert abs(result.value[0] - np.tanh(x)) < 1E-10 + assert abs(result.value[1] - (1 - np.tanh(x)**2)) < 1E-10 From 89a116f7c11260d555dcd6ca7681e67a2bf779b4 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 13:57:33 +0100 Subject: [PATCH 07/27] Use alg.scalar for readability --- clifford/test/test_trig_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clifford/test/test_trig_functions.py b/clifford/test/test_trig_functions.py index e852083c..d8af420b 100644 --- a/clifford/test/test_trig_functions.py +++ b/clifford/test/test_trig_functions.py @@ -9,7 +9,7 @@ class TestScalarProperties: @pytest.fixture() def element(self): alg, blades = Cl(0, 0, 0) - return blades[''] + return alg.scalar def test_sin(self, element): for x in np.linspace(0, 2*np.pi, 100): @@ -40,7 +40,7 @@ class TestDualNumberProperties: @pytest.fixture() def element(self): alg, blades = Cl(0, 0, 1) - return blades[''], blades['e1'] + return alg.scalar, blades['e1'] def test_sin(self, element): for x in np.linspace(0, 2*np.pi, 10): From 8c4cc470738e727005518d11e329d400f0d3be87 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 14:32:36 +0100 Subject: [PATCH 08/27] Bump the default max order --- clifford/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clifford/__init__.py b/clifford/__init__.py index 2c874a78..02c0c493 100644 --- a/clifford/__init__.py +++ b/clifford/__init__.py @@ -271,7 +271,7 @@ def general_exp(x, max_order=15): return result -def general_sin(X, max_order=20): +def general_sin(X, max_order=30): """ A taylor series expansion for sin """ @@ -281,7 +281,7 @@ def general_sin(X, max_order=20): return op -def general_cos(X, max_order=20): +def general_cos(X, max_order=30): """ A taylor series expansion for cos """ @@ -291,7 +291,7 @@ def general_cos(X, max_order=20): return op -def general_tan(X, max_order=20): +def general_tan(X, max_order=30): """ The tan function as the ratio of sin and cos Note. It would probably be better to implement this as its own taylor series. @@ -299,7 +299,7 @@ def general_tan(X, max_order=20): return general_sin(X, max_order) / general_cos(X, max_order) -def general_sinh(X, max_order=20): +def general_sinh(X, max_order=30): """ A taylor series expansion for sinh """ @@ -309,7 +309,7 @@ def general_sinh(X, max_order=20): return op -def general_cosh(X, max_order=20): +def general_cosh(X, max_order=30): """ A taylor series expansion for cosh """ @@ -319,7 +319,7 @@ def general_cosh(X, max_order=20): return op -def general_tanh(X, max_order=20): +def general_tanh(X, max_order=30): """ The tanh function as the ratio of sinh and cosh Note. It would probably be better to implement this as its own taylor series. From e6c4f15066e95ec4ad86f2995a5e50d879a0bee4 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 14:32:51 +0100 Subject: [PATCH 09/27] Refactor tests for concision --- clifford/test/test_trig_functions.py | 91 +++++++++++----------------- 1 file changed, 37 insertions(+), 54 deletions(-) diff --git a/clifford/test/test_trig_functions.py b/clifford/test/test_trig_functions.py index d8af420b..d1588d07 100644 --- a/clifford/test/test_trig_functions.py +++ b/clifford/test/test_trig_functions.py @@ -1,39 +1,25 @@ from .. import Cl import numpy as np +import cmath import pytest class TestScalarProperties: - @pytest.fixture() def element(self): alg, blades = Cl(0, 0, 0) return alg.scalar - def test_sin(self, element): - for x in np.linspace(0, 2*np.pi, 100): - assert abs(np.sin(x*element).value[0] - np.sin(x)) < 1E-10 - - def test_cos(self, element): - for x in np.linspace(0, 2*np.pi, 100): - assert abs(np.cos(x*element).value[0] - np.cos(x)) < 1E-10 - - def test_tan(self, element): - for x in np.linspace(-0.9*np.pi/2, 0.9*np.pi/2, 100): - assert abs(np.tan(x*element).value[0] - np.tan(x)) < 1E-10 - - def test_sinh(self, element): - for x in np.linspace(0, 2*np.pi, 100): - assert abs(np.sinh(x*element).value[0] - np.sinh(x)) < 1E-10 - - def test_cosh(self, element): + @pytest.mark.parametrize('np_func', [np.sin, + np.cos, + np.tan, + np.sinh, + np.cosh, + np.tanh]) + def test_trig(self, element, np_func): for x in np.linspace(0, 2*np.pi, 100): - assert abs(np.cosh(x*element).value[0] - np.cosh(x)) < 1E-10 - - def test_tanh(self, element): - for x in np.linspace(-np.pi, np.pi, 100): - assert abs(np.tanh(x*element).value[0] - np.tanh(x)) < 1E-10 + assert abs(np_func(x*element).value[0] - np_func(x)) < 1E-10 class TestDualNumberProperties: @@ -42,38 +28,35 @@ def element(self): alg, blades = Cl(0, 0, 1) return alg.scalar, blades['e1'] - def test_sin(self, element): + @pytest.mark.parametrize('func, deriv_func', [(np.sin, np.cos), + (np.cos, lambda x: -np.sin(x)), + (np.tan, lambda x: (1/np.cos(x)**2)), + (np.sinh, np.cosh), + (np.cosh, np.sinh), + (np.tanh, lambda x: (1 - np.tanh(x)**2))]) + def test_derivatives(self, element, func, deriv_func): for x in np.linspace(0, 2*np.pi, 10): - result = np.sin(x * element[0] + element[1]) - assert abs(result.value[0] - np.sin(x)) < 1E-10 - assert abs(result.value[1] - np.cos(x)) < 1E-10 + result = func(x * element[0] + element[1]) + assert abs(result.value[0] - func(x)) < 1E-10 + assert abs(result.value[1] - deriv_func(x)) < 1E-10 - def test_cos(self, element): - for x in np.linspace(0, 2*np.pi, 10): - result = np.cos(x * element[0] + element[1]) - assert abs(result.value[0] - np.cos(x)) < 1E-10 - assert abs(result.value[1] + np.sin(x)) < 1E-10 - def test_tan(self, element): - for x in np.linspace(0, 2*np.pi, 10): - result = np.tan(x * element[0] + element[1]) - assert abs(result.value[0] - np.tan(x)) < 1E-10 - assert abs(result.value[1] - (1/np.cos(x)**2)) < 1E-10 - - def test_sinh(self, element): - for x in np.linspace(0, 2*np.pi, 10): - result = np.sinh(x * element[0] + element[1]) - assert abs(result.value[0] - np.sinh(x)) < 1E-10 - assert abs(result.value[1] - np.cosh(x)) < 1E-10 - - def test_cosh(self, element): - for x in np.linspace(0, 2*np.pi, 10): - result = np.cosh(x * element[0] + element[1]) - assert abs(result.value[0] - np.cosh(x)) < 1E-10 - assert abs(result.value[1] - np.sinh(x)) < 1E-10 +class TestComplexNumberProperties: + @pytest.fixture() + def element(self): + alg, blades = Cl(0, 1, 0) + return alg.scalar, blades['e1'] - def test_tanh(self, element): - for x in np.linspace(0, 2*np.pi, 10): - result = np.tanh(x * element[0] + element[1]) - assert abs(result.value[0] - np.tanh(x)) < 1E-10 - assert abs(result.value[1] - (1 - np.tanh(x)**2)) < 1E-10 + @pytest.mark.parametrize('np_func,cmath_func', [(np.sin, cmath.sin), + (np.cos, cmath.cos), + (np.tan, cmath.tan), + (np.sinh, cmath.sinh), + (np.cosh, cmath.cosh), + (np.tanh, cmath.tanh)]) + def test_trig(self, element, np_func, cmath_func): + for x in np.linspace(0, 2 * np.pi, 10): + for y in np.linspace(0, 2 * np.pi, 10): + complex_mv = x * element[0] + y * element[1] + complex_value = x + 1j * y + result = np_func(complex_mv) + assert abs(result.value[0] + 1j * result.value[1] - cmath_func(complex_value)) < 1E-10 From de66532b5ac4dcf69efa30948b129caaba86502d Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 14:50:16 +0100 Subject: [PATCH 10/27] Allow pow to be overloaded in numba --- clifford/numba/_multivector.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/clifford/numba/_multivector.py b/clifford/numba/_multivector.py index 010749a0..e71fdc32 100644 --- a/clifford/numba/_multivector.py +++ b/clifford/numba/_multivector.py @@ -250,6 +250,20 @@ def impl(a, b): return impl +@numba.extending.overload(operator.pow) +def ga_pow(a, b): + if isinstance(a, MultiVectorType) and isinstance(b, types.Integer): + gmt_func = a.layout_type.obj.gmt_func + def impl(a, b): + if b == 0: + return 1 + 0*a + op = a.value + for i in range(1, b): + op = gmt_func(op, a.value) + return a.layout.MultiVector(op) + return impl + + @numba.extending.overload(operator.truediv) def ga_truediv(a, b): if isinstance(a, MultiVectorType) and isinstance(b, types.abstract.Number): From 183d269a8124bd06fed0dc466895254e9c02053e Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 14:50:30 +0100 Subject: [PATCH 11/27] JIT several of the trig functions --- clifford/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clifford/__init__.py b/clifford/__init__.py index 02c0c493..49853402 100644 --- a/clifford/__init__.py +++ b/clifford/__init__.py @@ -271,23 +271,25 @@ def general_exp(x, max_order=15): return result +@_numba_utils.njit def general_sin(X, max_order=30): """ A taylor series expansion for sin """ op = 0 * X for n in range(max_order): - op += ((-1) ** (n) / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + op = op + ((-1) ** (n) / math.gamma(2 * n + 2)) * X ** (2 * n + 1) return op +@_numba_utils.njit def general_cos(X, max_order=30): """ A taylor series expansion for cos """ op = 0 * X for n in range(max_order): - op += ((-1) ** (n) / math.gamma(2 * n + 1)) * X ** (2 * n) + op = op + ((-1) ** (n) / math.gamma(2 * n + 1)) * X ** (2 * n) return op @@ -299,23 +301,25 @@ def general_tan(X, max_order=30): return general_sin(X, max_order) / general_cos(X, max_order) +@_numba_utils.njit def general_sinh(X, max_order=30): """ A taylor series expansion for sinh """ op = 0 * X for n in range(max_order): - op += (1 / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + op = op + (1 / math.gamma(2 * n + 2)) * X ** (2 * n + 1) return op +@_numba_utils.njit def general_cosh(X, max_order=30): """ A taylor series expansion for cosh """ op = 0 * X for n in range(max_order): - op += (1 / math.gamma(2 * n + 1)) * X ** (2 * n) + op = op + (1 / math.gamma(2 * n + 1)) * X ** (2 * n) return op From 5a94b58976ea39a289b3fcea8871d44d2844c550 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 15:02:20 +0100 Subject: [PATCH 12/27] Added the complexified scalars as a test too --- clifford/test/test_trig_functions.py | 31 +++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/clifford/test/test_trig_functions.py b/clifford/test/test_trig_functions.py index d1588d07..fa27d499 100644 --- a/clifford/test/test_trig_functions.py +++ b/clifford/test/test_trig_functions.py @@ -43,20 +43,45 @@ def test_derivatives(self, element, func, deriv_func): class TestComplexNumberProperties: @pytest.fixture() - def element(self): + def Cl010element(self): alg, blades = Cl(0, 1, 0) return alg.scalar, blades['e1'] + @pytest.fixture() + def Cl000element(self): + alg, blades = Cl(0, 0, 0) + return alg.MultiVector(0*1j + alg.scalar.value), alg.MultiVector(1j*alg.scalar.value) + @pytest.mark.parametrize('np_func,cmath_func', [(np.sin, cmath.sin), (np.cos, cmath.cos), (np.tan, cmath.tan), (np.sinh, cmath.sinh), (np.cosh, cmath.cosh), (np.tanh, cmath.tanh)]) - def test_trig(self, element, np_func, cmath_func): + def test_trig_Cl010(self, Cl010element, np_func, cmath_func): + """ + This tests the a clifford algebra isomorphic to the complex numbers + """ for x in np.linspace(0, 2 * np.pi, 10): for y in np.linspace(0, 2 * np.pi, 10): - complex_mv = x * element[0] + y * element[1] + complex_mv = x * Cl010element[0] + y * Cl010element[1] complex_value = x + 1j * y result = np_func(complex_mv) assert abs(result.value[0] + 1j * result.value[1] - cmath_func(complex_value)) < 1E-10 + + @pytest.mark.parametrize('np_func,cmath_func', [(np.sin, cmath.sin), + (np.cos, cmath.cos), + (np.tan, cmath.tan), + (np.sinh, cmath.sinh), + (np.cosh, cmath.cosh), + (np.tanh, cmath.tanh)]) + def test_trig_CxCl000(self, Cl000element, np_func, cmath_func): + """ + This tests the complexified clifford algebra of only the scalars + """ + for x in np.linspace(0, 2 * np.pi, 10): + for y in np.linspace(0, 2 * np.pi, 10): + complex_mv = x * Cl000element[0] + y * Cl000element[1] + complex_value = x + 1j * y + result = np_func(complex_mv) + assert abs(result.value[0] - cmath_func(complex_value)) < 1E-10 From fe122e2df1ba138cbc9564b9120df50c7836becb Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 16:15:57 +0100 Subject: [PATCH 13/27] Move taylor expansions to their own file --- clifford/__init__.py | 99 -------------------------------- clifford/_multivector.py | 2 +- clifford/taylor_expansions.py | 105 ++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 100 deletions(-) create mode 100644 clifford/taylor_expansions.py diff --git a/clifford/__init__.py b/clifford/__init__.py index 49853402..a74ef9ef 100644 --- a/clifford/__init__.py +++ b/clifford/__init__.py @@ -78,7 +78,6 @@ import itertools import warnings from typing import List, Tuple, Set -import math # Major library imports. import numpy as np @@ -233,104 +232,6 @@ def grade_obj_func(objin_val, gradeList, threshold): return np.argmax(modal_value_count) -@_numba_utils.njit -def general_exp(x, max_order=15): - """ - This implements the series expansion of e**mv where mv is a multivector - The parameter order is the maximum order of the taylor series to use - """ - - result = 1.0 + 0.0*x - if max_order == 0: - return result - - # scale by power of 2 so that its norm is < 1 - max_val = int(np.max(np.abs(x.value))) - scale = 1 - if max_val > 1: - max_val <<= 1 - while max_val: - max_val >>= 1 - scale <<= 1 - - scaled = x * (1.0 / scale) - - # taylor approximation - tmp = 1.0 + 0.0*x - for i in range(1, max_order): - if np.any(np.abs(tmp.value) > _settings._eps): - tmp = tmp*scaled * (1.0 / i) - result = result + tmp - else: - break - - # undo scaling - while scale > 1: - result = result*result - scale >>= 1 - return result - - -@_numba_utils.njit -def general_sin(X, max_order=30): - """ - A taylor series expansion for sin - """ - op = 0 * X - for n in range(max_order): - op = op + ((-1) ** (n) / math.gamma(2 * n + 2)) * X ** (2 * n + 1) - return op - - -@_numba_utils.njit -def general_cos(X, max_order=30): - """ - A taylor series expansion for cos - """ - op = 0 * X - for n in range(max_order): - op = op + ((-1) ** (n) / math.gamma(2 * n + 1)) * X ** (2 * n) - return op - - -def general_tan(X, max_order=30): - """ - The tan function as the ratio of sin and cos - Note. It would probably be better to implement this as its own taylor series. - """ - return general_sin(X, max_order) / general_cos(X, max_order) - - -@_numba_utils.njit -def general_sinh(X, max_order=30): - """ - A taylor series expansion for sinh - """ - op = 0 * X - for n in range(max_order): - op = op + (1 / math.gamma(2 * n + 2)) * X ** (2 * n + 1) - return op - - -@_numba_utils.njit -def general_cosh(X, max_order=30): - """ - A taylor series expansion for cosh - """ - op = 0 * X - for n in range(max_order): - op = op + (1 / math.gamma(2 * n + 1)) * X ** (2 * n) - return op - - -def general_tanh(X, max_order=30): - """ - The tanh function as the ratio of sinh and cosh - Note. It would probably be better to implement this as its own taylor series. - """ - return general_sinh(X, max_order) / general_cosh(X, max_order) - - def grade_obj(objin, threshold=0.0000001): ''' Returns the modal grade of a multivector diff --git a/clifford/_multivector.py b/clifford/_multivector.py index b10e839e..20f30df4 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -6,7 +6,7 @@ import numpy as np import clifford as cf -from . import general_exp, general_sin, general_cos, general_tan, general_sinh, general_cosh, general_tanh +from .taylor_expansions import general_exp, general_sin, general_cos, general_tan, general_sinh, general_cosh, general_tanh from . import _settings from ._layout_helpers import layout_short_name diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py new file mode 100644 index 00000000..5d38a3d7 --- /dev/null +++ b/clifford/taylor_expansions.py @@ -0,0 +1,105 @@ + +import math + +import numpy as np + +from . import _numba_utils +from . import _settings + + +@_numba_utils.njit +def general_exp(x, max_order=15): + """ + This implements the series expansion of e**mv where mv is a multivector + The parameter order is the maximum order of the taylor series to use + """ + + result = 1.0 + 0.0*x + if max_order == 0: + return result + + # scale by power of 2 so that its norm is < 1 + max_val = int(np.max(np.abs(x.value))) + scale = 1 + if max_val > 1: + max_val <<= 1 + while max_val: + max_val >>= 1 + scale <<= 1 + + scaled = x * (1.0 / scale) + + # taylor approximation + tmp = 1.0 + 0.0*x + for i in range(1, max_order): + if np.any(np.abs(tmp.value) > _settings._eps): + tmp = tmp*scaled * (1.0 / i) + result = result + tmp + else: + break + + # undo scaling + while scale > 1: + result = result*result + scale >>= 1 + return result + + +@_numba_utils.njit +def general_sin(X, max_order=30): + """ + A taylor series expansion for sin + """ + op = 0 * X + for n in range(max_order): + op = op + ((-1) ** (n) / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + return op + + +@_numba_utils.njit +def general_cos(X, max_order=30): + """ + A taylor series expansion for cos + """ + op = 0 * X + for n in range(max_order): + op = op + ((-1) ** (n) / math.gamma(2 * n + 1)) * X ** (2 * n) + return op + + +def general_tan(X, max_order=30): + """ + The tan function as the ratio of sin and cos + Note. It would probably be better to implement this as its own taylor series. + """ + return general_sin(X, max_order) / general_cos(X, max_order) + + +@_numba_utils.njit +def general_sinh(X, max_order=30): + """ + A taylor series expansion for sinh + """ + op = 0 * X + for n in range(max_order): + op = op + (1 / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + return op + + +@_numba_utils.njit +def general_cosh(X, max_order=30): + """ + A taylor series expansion for cosh + """ + op = 0 * X + for n in range(max_order): + op = op + (1 / math.gamma(2 * n + 1)) * X ** (2 * n) + return op + + +def general_tanh(X, max_order=30): + """ + The tanh function as the ratio of sinh and cosh + Note. It would probably be better to implement this as its own taylor series. + """ + return general_sinh(X, max_order) / general_cosh(X, max_order) From 0d13b3b8375a6e7997039d9b2b6727b81845508a Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 16:45:26 +0100 Subject: [PATCH 14/27] Remove cmath as numpy works fine --- clifford/test/test_trig_functions.py | 33 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/clifford/test/test_trig_functions.py b/clifford/test/test_trig_functions.py index fa27d499..80eb31a0 100644 --- a/clifford/test/test_trig_functions.py +++ b/clifford/test/test_trig_functions.py @@ -1,7 +1,6 @@ from .. import Cl import numpy as np -import cmath import pytest @@ -52,13 +51,13 @@ def Cl000element(self): alg, blades = Cl(0, 0, 0) return alg.MultiVector(0*1j + alg.scalar.value), alg.MultiVector(1j*alg.scalar.value) - @pytest.mark.parametrize('np_func,cmath_func', [(np.sin, cmath.sin), - (np.cos, cmath.cos), - (np.tan, cmath.tan), - (np.sinh, cmath.sinh), - (np.cosh, cmath.cosh), - (np.tanh, cmath.tanh)]) - def test_trig_Cl010(self, Cl010element, np_func, cmath_func): + @pytest.mark.parametrize('np_func', [np.sin, + np.cos, + np.tan, + np.sinh, + np.cosh, + np.tanh]) + def test_trig_Cl010(self, Cl010element, np_func): """ This tests the a clifford algebra isomorphic to the complex numbers """ @@ -67,15 +66,15 @@ def test_trig_Cl010(self, Cl010element, np_func, cmath_func): complex_mv = x * Cl010element[0] + y * Cl010element[1] complex_value = x + 1j * y result = np_func(complex_mv) - assert abs(result.value[0] + 1j * result.value[1] - cmath_func(complex_value)) < 1E-10 + assert abs(result.value[0] + 1j * result.value[1] - np_func(complex_value)) < 1E-10 - @pytest.mark.parametrize('np_func,cmath_func', [(np.sin, cmath.sin), - (np.cos, cmath.cos), - (np.tan, cmath.tan), - (np.sinh, cmath.sinh), - (np.cosh, cmath.cosh), - (np.tanh, cmath.tanh)]) - def test_trig_CxCl000(self, Cl000element, np_func, cmath_func): + @pytest.mark.parametrize('np_func', [np.sin, + np.cos, + np.tan, + np.sinh, + np.cosh, + np.tanh]) + def test_trig_CxCl000(self, Cl000element, np_func): """ This tests the complexified clifford algebra of only the scalars """ @@ -84,4 +83,4 @@ def test_trig_CxCl000(self, Cl000element, np_func, cmath_func): complex_mv = x * Cl000element[0] + y * Cl000element[1] complex_value = x + 1j * y result = np_func(complex_mv) - assert abs(result.value[0] - cmath_func(complex_value)) < 1E-10 + assert abs(result.value[0] - np_func(complex_value)) < 1E-10 From a6bd6f706d0d2154a61dc884177f9f31a68fdce1 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Thu, 22 Oct 2020 16:59:12 +0100 Subject: [PATCH 15/27] Use previous pow in series to calculate next term --- clifford/taylor_expansions.py | 36 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py index 5d38a3d7..4f5b4ce8 100644 --- a/clifford/taylor_expansions.py +++ b/clifford/taylor_expansions.py @@ -50,9 +50,12 @@ def general_sin(X, max_order=30): """ A taylor series expansion for sin """ - op = 0 * X - for n in range(max_order): - op = op + ((-1) ** (n) / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + op = +X + X2 = X*X + X2np1 = X + for n in range(1, max_order): + X2np1 = X2np1 * X2 + op = op + ((-1) ** (n) / math.gamma(2 * n + 2)) * X2np1 return op @@ -61,9 +64,12 @@ def general_cos(X, max_order=30): """ A taylor series expansion for cos """ - op = 0 * X - for n in range(max_order): - op = op + ((-1) ** (n) / math.gamma(2 * n + 1)) * X ** (2 * n) + op = 1 + 0*X + X2 = X * X + X2n = 1 + 0*X + for n in range(1, max_order): + X2n = X2n*X2 + op = op + ((-1) ** (n) / math.gamma(2 * n + 1)) * X2n return op @@ -80,9 +86,12 @@ def general_sinh(X, max_order=30): """ A taylor series expansion for sinh """ - op = 0 * X - for n in range(max_order): - op = op + (1 / math.gamma(2 * n + 2)) * X ** (2 * n + 1) + op = +X + X2 = X * X + X2np1 = X + for n in range(1, max_order): + X2np1 = X2np1 * X2 + op = op + (1 / math.gamma(2 * n + 2)) * X2np1 return op @@ -91,9 +100,12 @@ def general_cosh(X, max_order=30): """ A taylor series expansion for cosh """ - op = 0 * X - for n in range(max_order): - op = op + (1 / math.gamma(2 * n + 1)) * X ** (2 * n) + op = 1 + 0 * X + X2 = X * X + X2n = 1 + 0 * X + for n in range(1, max_order): + X2n = X2n * X2 + op = op + (1 / math.gamma(2 * n + 1)) * X2n return op From a6cbde1272155cb2e682fef004279f224116f56d Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 16:05:02 +0000 Subject: [PATCH 16/27] Rename taylor expansion functions --- clifford/_multivector.py | 21 +++++++++++++-------- clifford/taylor_expansions.py | 24 ++++++++++++++---------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/clifford/_multivector.py b/clifford/_multivector.py index 20f30df4..e513de13 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -6,11 +6,16 @@ import numpy as np import clifford as cf -from .taylor_expansions import general_exp, general_sin, general_cos, general_tan, general_sinh, general_cosh, general_tanh +import clifford.taylor_expansions as taylor_expansions from . import _settings from ._layout_helpers import layout_short_name +def general_exp(x, **kwargs): + warnings.warn("cf.general_exp is deprecated. Use `mv.exp()` or `np.exp(mv)` on multivectors, or `cf.taylor_expansions.exp(x)` on arbitrary objects", DeprecationWarning) + return taylor_expansions.exp(x, **kwargs) + + class MultiVector(object): """An element of the algebra @@ -99,25 +104,25 @@ def _newMV(self, newValue=None, *, dtype: np.dtype = None) -> 'MultiVector': # binary def exp(self) -> 'MultiVector': - return general_exp(self) + return taylor_expansions.exp(self) def cos(self): - return general_cos(self) + return taylor_expansions.cos(self) def sin(self): - return general_sin(self) + return taylor_expansions.sin(self) def tan(self): - return general_tan(self) + return taylor_expansions.tan(self) def sinh(self): - return general_sinh(self) + return taylor_expansions.sinh(self) def cosh(self): - return general_cosh(self) + return taylor_expansions.cosh(self) def tanh(self): - return general_tanh(self) + return taylor_expansions.tanh(self) def vee(self, other) -> 'MultiVector': r""" diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py index 4f5b4ce8..4696b8bd 100644 --- a/clifford/taylor_expansions.py +++ b/clifford/taylor_expansions.py @@ -1,6 +1,10 @@ +""" +This file implements various Taylor expansions for useful functions of multivectors. +For some algebra signatures there may exist closed forms of these functions which would likely be faster +and more accurate. Nonetheless having pre-written taylor expansions for the general case is useful. +""" import math - import numpy as np from . import _numba_utils @@ -8,7 +12,7 @@ @_numba_utils.njit -def general_exp(x, max_order=15): +def exp(x, max_order=15): """ This implements the series expansion of e**mv where mv is a multivector The parameter order is the maximum order of the taylor series to use @@ -46,7 +50,7 @@ def general_exp(x, max_order=15): @_numba_utils.njit -def general_sin(X, max_order=30): +def sin(X, max_order=30): """ A taylor series expansion for sin """ @@ -60,7 +64,7 @@ def general_sin(X, max_order=30): @_numba_utils.njit -def general_cos(X, max_order=30): +def cos(X, max_order=30): """ A taylor series expansion for cos """ @@ -73,16 +77,16 @@ def general_cos(X, max_order=30): return op -def general_tan(X, max_order=30): +def tan(X, max_order=30): """ The tan function as the ratio of sin and cos Note. It would probably be better to implement this as its own taylor series. """ - return general_sin(X, max_order) / general_cos(X, max_order) + return sin(X, max_order) / cos(X, max_order) @_numba_utils.njit -def general_sinh(X, max_order=30): +def sinh(X, max_order=30): """ A taylor series expansion for sinh """ @@ -96,7 +100,7 @@ def general_sinh(X, max_order=30): @_numba_utils.njit -def general_cosh(X, max_order=30): +def cosh(X, max_order=30): """ A taylor series expansion for cosh """ @@ -109,9 +113,9 @@ def general_cosh(X, max_order=30): return op -def general_tanh(X, max_order=30): +def tanh(X, max_order=30): """ The tanh function as the ratio of sinh and cosh Note. It would probably be better to implement this as its own taylor series. """ - return general_sinh(X, max_order) / general_cosh(X, max_order) + return sinh(X, max_order) / cosh(X, max_order) From b3db4d32b4afabb77c07418f32457952430e27f2 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 16:09:53 +0000 Subject: [PATCH 17/27] Add taylor_expansions to index_rst --- docs/api/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/index.rst b/docs/api/index.rst index 974ef577..ccb38db8 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -10,3 +10,4 @@ API tools operator transformations + taylor_expansions From efa563d6d148d16ff2b23bcf624f1a80227783b5 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 18:32:09 +0000 Subject: [PATCH 18/27] Move general_exp deprecation to the original expected place --- clifford/__init__.py | 7 ++++++- clifford/_multivector.py | 7 +------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clifford/__init__.py b/clifford/__init__.py index a74ef9ef..4d58d12f 100644 --- a/clifford/__init__.py +++ b/clifford/__init__.py @@ -93,9 +93,9 @@ from ._version import __version__ # noqa: F401 from . import _numba_utils -from . import _settings from ._settings import pretty, ugly, eps, print_precision # noqa: F401 +import clifford.taylor_expansions as taylor_expansions # For backwards-compatibility. New code should import directly from `clifford.operator` from .operator import gp, op, ip # noqa: F401 @@ -108,6 +108,11 @@ NUMBA_PARALLEL = not bool(NUMBA_DISABLE_PARALLEL) +def general_exp(x, **kwargs): + warnings.warn("cf.general_exp is deprecated. Use `mv.exp()` or `np.exp(mv)` on multivectors, or `cf.taylor_expansions.exp(x)` on arbitrary objects", DeprecationWarning, stacklevel=2) + return taylor_expansions.exp(x, **kwargs) + + def linear_operator_as_matrix(func, input_blades, output_blades): """ Return a matrix that performs the operation of the provided linear diff --git a/clifford/_multivector.py b/clifford/_multivector.py index e513de13..e2acf6ec 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -11,11 +11,6 @@ from ._layout_helpers import layout_short_name -def general_exp(x, **kwargs): - warnings.warn("cf.general_exp is deprecated. Use `mv.exp()` or `np.exp(mv)` on multivectors, or `cf.taylor_expansions.exp(x)` on arbitrary objects", DeprecationWarning) - return taylor_expansions.exp(x, **kwargs) - - class MultiVector(object): """An element of the algebra @@ -324,7 +319,7 @@ def __rpow__(self, other) -> 'MultiVector': # else. # pow(x, y) == exp(y * log(x)) - newMV = general_exp(math.log(other) * self) + newMV = taylor_expansions.exp(math.log(other) * self) return newMV From 519a07aabd2c4ef62a78c4b73116d7d65a4e63b3 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 18:32:34 +0000 Subject: [PATCH 19/27] Added a negative power error --- clifford/numba/_multivector.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clifford/numba/_multivector.py b/clifford/numba/_multivector.py index e71fdc32..1a3812cf 100644 --- a/clifford/numba/_multivector.py +++ b/clifford/numba/_multivector.py @@ -255,6 +255,8 @@ def ga_pow(a, b): if isinstance(a, MultiVectorType) and isinstance(b, types.Integer): gmt_func = a.layout_type.obj.gmt_func def impl(a, b): + if b < 0: + raise NotImplementedError('Negative powers are currently not implemented') if b == 0: return 1 + 0*a op = a.value From d3469691d18a1e1e4ce34289b2fa98bed3e3a8e2 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 19:54:07 +0000 Subject: [PATCH 20/27] improved docstring --- clifford/taylor_expansions.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py index 4696b8bd..be0f0637 100644 --- a/clifford/taylor_expansions.py +++ b/clifford/taylor_expansions.py @@ -1,9 +1,28 @@ """ +.. currentmodule:: clifford.taylor_expansions + +================================================= +taylor_expansions (:mod:`clifford.taylor_expansions`) +================================================= + +.. versionadded:: 1.3.2 + This file implements various Taylor expansions for useful functions of multivectors. For some algebra signatures there may exist closed forms of these functions which would likely be faster and more accurate. Nonetheless having pre-written taylor expansions for the general case is useful. -""" +Implemented functions +---------------- + +.. autofunction:: exp +.. autofunction:: sin +.. autofunction:: cos +.. autofunction:: tan +.. autofunction:: sinh +.. autofunction:: cosh +.. autofunction:: tanh + +""" import math import numpy as np From dde49e65d75c7bfe70a445473d75860d9e7b77f8 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 20:03:02 +0000 Subject: [PATCH 21/27] Add a stub rst file for the taylor_expansions --- docs/api/taylor_expansions.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/api/taylor_expansions.rst diff --git a/docs/api/taylor_expansions.rst b/docs/api/taylor_expansions.rst new file mode 100644 index 00000000..c4e8be0b --- /dev/null +++ b/docs/api/taylor_expansions.rst @@ -0,0 +1 @@ +.. automodule:: clifford.taylor_expansions From 7dd1e79833fd29171b863ade37aff9fab7509149 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 20:25:06 +0000 Subject: [PATCH 22/27] Add missing type annotations --- clifford/_multivector.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clifford/_multivector.py b/clifford/_multivector.py index e2acf6ec..863a7769 100644 --- a/clifford/_multivector.py +++ b/clifford/_multivector.py @@ -101,22 +101,22 @@ def _newMV(self, newValue=None, *, dtype: np.dtype = None) -> 'MultiVector': def exp(self) -> 'MultiVector': return taylor_expansions.exp(self) - def cos(self): + def cos(self) -> 'MultiVector': return taylor_expansions.cos(self) - def sin(self): + def sin(self) -> 'MultiVector': return taylor_expansions.sin(self) - def tan(self): + def tan(self) -> 'MultiVector': return taylor_expansions.tan(self) - def sinh(self): + def sinh(self) -> 'MultiVector': return taylor_expansions.sinh(self) - def cosh(self): + def cosh(self) -> 'MultiVector': return taylor_expansions.cosh(self) - def tanh(self): + def tanh(self) -> 'MultiVector': return taylor_expansions.tanh(self) def vee(self, other) -> 'MultiVector': From 09053b8d354fbb8205e0064e79362eda437eeff3 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Sun, 27 Dec 2020 20:27:32 +0000 Subject: [PATCH 23/27] Add JIT comments --- clifford/taylor_expansions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py index be0f0637..c9be1a50 100644 --- a/clifford/taylor_expansions.py +++ b/clifford/taylor_expansions.py @@ -99,7 +99,8 @@ def cos(X, max_order=30): def tan(X, max_order=30): """ The tan function as the ratio of sin and cos - Note. It would probably be better to implement this as its own taylor series. + Note. It would probably be better to implement this as its own taylor series. This function + is not JITed as currently we do not overload the truediv operator for multivectors. """ return sin(X, max_order) / cos(X, max_order) @@ -135,6 +136,7 @@ def cosh(X, max_order=30): def tanh(X, max_order=30): """ The tanh function as the ratio of sinh and cosh - Note. It would probably be better to implement this as its own taylor series. + Note. It would probably be better to implement this as its own taylor series. This function + is not JITed as currently we do not overload the truediv operator for multivectors. """ return sinh(X, max_order) / cosh(X, max_order) From e7b411184452dcb8b1a51d56fec4726f2c4ec547 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Mon, 28 Dec 2020 11:32:43 +0000 Subject: [PATCH 24/27] Remove relative import --- clifford/test/test_trig_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clifford/test/test_trig_functions.py b/clifford/test/test_trig_functions.py index 80eb31a0..3de6c0f2 100644 --- a/clifford/test/test_trig_functions.py +++ b/clifford/test/test_trig_functions.py @@ -1,5 +1,5 @@ -from .. import Cl +from clifford import Cl import numpy as np import pytest From 662a7be21e35f8ed085edbe17bd84f145e36fc94 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Mon, 28 Dec 2020 11:39:15 +0000 Subject: [PATCH 25/27] Include improved docstrings --- clifford/taylor_expansions.py | 36 ++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py index c9be1a50..47daf93d 100644 --- a/clifford/taylor_expansions.py +++ b/clifford/taylor_expansions.py @@ -9,7 +9,19 @@ This file implements various Taylor expansions for useful functions of multivectors. For some algebra signatures there may exist closed forms of these functions which would likely be faster -and more accurate. Nonetheless having pre-written taylor expansions for the general case is useful. +and more accurate. Nonetheless, having pre-written taylor expansions for the general case is useful. + +.. note:: + Many of these functions are also exposed as :class:`~clifford.MultiVector` methods, + such as :meth:`clifford.MultiVector.sin`. This means that ``mv.sin()`` or even ``np.sin(mv)`` can be used + as a convenient interface to functions in this module, without having to import it directly. + + For example:: + + >>> from clifford.g3 import * + >>> import numpy as np + >>> np.sin(np.pi*e12/4) + >>> # Produces (0.86867^e12) Implemented functions ---------------- @@ -33,8 +45,8 @@ @_numba_utils.njit def exp(x, max_order=15): """ - This implements the series expansion of e**mv where mv is a multivector - The parameter order is the maximum order of the taylor series to use + This implements the series expansion of :math:`\exp x` where :math:`x` is a multivector + The parameter `max_order` is the maximum order of the taylor series to use """ result = 1.0 + 0.0*x @@ -72,6 +84,7 @@ def exp(x, max_order=15): def sin(X, max_order=30): """ A taylor series expansion for sin + The parameter `max_order` is the maximum order of the taylor series to use """ op = +X X2 = X*X @@ -86,6 +99,7 @@ def sin(X, max_order=30): def cos(X, max_order=30): """ A taylor series expansion for cos + The parameter `max_order` is the maximum order of the taylor series to use """ op = 1 + 0*X X2 = X * X @@ -99,8 +113,11 @@ def cos(X, max_order=30): def tan(X, max_order=30): """ The tan function as the ratio of sin and cos - Note. It would probably be better to implement this as its own taylor series. This function - is not JITed as currently we do not overload the truediv operator for multivectors. + The parameter `max_order` is the maximum order of the taylor series to use + + .. note:: + It would probably be better to implement this as its own taylor series. This function + is not JITed as currently we do not overload the truediv operator for multivectors. """ return sin(X, max_order) / cos(X, max_order) @@ -109,6 +126,7 @@ def tan(X, max_order=30): def sinh(X, max_order=30): """ A taylor series expansion for sinh + The parameter `max_order` is the maximum order of the taylor series to use """ op = +X X2 = X * X @@ -123,6 +141,7 @@ def sinh(X, max_order=30): def cosh(X, max_order=30): """ A taylor series expansion for cosh + The parameter `max_order` is the maximum order of the taylor series to use """ op = 1 + 0 * X X2 = X * X @@ -136,7 +155,10 @@ def cosh(X, max_order=30): def tanh(X, max_order=30): """ The tanh function as the ratio of sinh and cosh - Note. It would probably be better to implement this as its own taylor series. This function - is not JITed as currently we do not overload the truediv operator for multivectors. + The parameter `max_order` is the maximum order of the taylor series to use + + .. note:: + It would probably be better to implement this as its own taylor series. This function + is not JITed as currently we do not overload the truediv operator for multivectors. """ return sinh(X, max_order) / cosh(X, max_order) From ea63218b288b93c866fdd28d072795ce2caf1d11 Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Mon, 28 Dec 2020 12:25:09 +0000 Subject: [PATCH 26/27] Include in changelog, expand to negative values for trig testing --- clifford/taylor_expansions.py | 2 +- clifford/test/test_trig_functions.py | 12 ++++++------ docs/changelog.rst | 4 ++++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py index 47daf93d..3e1d3b47 100644 --- a/clifford/taylor_expansions.py +++ b/clifford/taylor_expansions.py @@ -5,7 +5,7 @@ taylor_expansions (:mod:`clifford.taylor_expansions`) ================================================= -.. versionadded:: 1.3.2 +.. versionadded:: 1.4.1 This file implements various Taylor expansions for useful functions of multivectors. For some algebra signatures there may exist closed forms of these functions which would likely be faster diff --git a/clifford/test/test_trig_functions.py b/clifford/test/test_trig_functions.py index 3de6c0f2..30ce1264 100644 --- a/clifford/test/test_trig_functions.py +++ b/clifford/test/test_trig_functions.py @@ -17,7 +17,7 @@ def element(self): np.cosh, np.tanh]) def test_trig(self, element, np_func): - for x in np.linspace(0, 2*np.pi, 100): + for x in np.linspace(-2 * np.pi, 2 * np.pi, 13): assert abs(np_func(x*element).value[0] - np_func(x)) < 1E-10 @@ -34,7 +34,7 @@ def element(self): (np.cosh, np.sinh), (np.tanh, lambda x: (1 - np.tanh(x)**2))]) def test_derivatives(self, element, func, deriv_func): - for x in np.linspace(0, 2*np.pi, 10): + for x in np.linspace(-2 * np.pi, 2 * np.pi, 13): result = func(x * element[0] + element[1]) assert abs(result.value[0] - func(x)) < 1E-10 assert abs(result.value[1] - deriv_func(x)) < 1E-10 @@ -61,8 +61,8 @@ def test_trig_Cl010(self, Cl010element, np_func): """ This tests the a clifford algebra isomorphic to the complex numbers """ - for x in np.linspace(0, 2 * np.pi, 10): - for y in np.linspace(0, 2 * np.pi, 10): + for x in np.linspace(-2 * np.pi, 2 * np.pi, 13): + for y in np.linspace(-2 * np.pi, 2 * np.pi, 13): complex_mv = x * Cl010element[0] + y * Cl010element[1] complex_value = x + 1j * y result = np_func(complex_mv) @@ -78,8 +78,8 @@ def test_trig_CxCl000(self, Cl000element, np_func): """ This tests the complexified clifford algebra of only the scalars """ - for x in np.linspace(0, 2 * np.pi, 10): - for y in np.linspace(0, 2 * np.pi, 10): + for x in np.linspace(-2 * np.pi, 2 * np.pi, 13): + for y in np.linspace(-2 * np.pi, 2 * np.pi, 13): complex_mv = x * Cl000element[0] + y * Cl000element[1] complex_value = x + 1j * y result = np_func(complex_mv) diff --git a/docs/changelog.rst b/docs/changelog.rst index e1c7e2a1..3276d949 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -12,6 +12,10 @@ Changes in 1.4.x * Where possible, ``MultiVector``\ s preserve their data type in the dual, and the right and left complements. + * A new :mod:`clifford.taylor_expansions` is added to implement taylor series of various + multivector function, starting with common trigonometric functions. These functions are + additionally exposed via a + Changes in 1.3.x ++++++++++++++++ From 9f0ed3188340c25b8ac1431ad4ca631c3059ba4c Mon Sep 17 00:00:00 2001 From: hugo hadfield Date: Mon, 28 Dec 2020 20:59:44 +0000 Subject: [PATCH 27/27] Fix docstrings and release note --- clifford/taylor_expansions.py | 8 ++++---- docs/changelog.rst | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clifford/taylor_expansions.py b/clifford/taylor_expansions.py index 3e1d3b47..b317eed8 100644 --- a/clifford/taylor_expansions.py +++ b/clifford/taylor_expansions.py @@ -1,11 +1,11 @@ """ .. currentmodule:: clifford.taylor_expansions -================================================= +===================================================== taylor_expansions (:mod:`clifford.taylor_expansions`) -================================================= +===================================================== -.. versionadded:: 1.4.1 +.. versionadded:: 1.4.0 This file implements various Taylor expansions for useful functions of multivectors. For some algebra signatures there may exist closed forms of these functions which would likely be faster @@ -21,7 +21,7 @@ >>> from clifford.g3 import * >>> import numpy as np >>> np.sin(np.pi*e12/4) - >>> # Produces (0.86867^e12) + (0.86867^e12) Implemented functions ---------------- diff --git a/docs/changelog.rst b/docs/changelog.rst index 3276d949..21108df5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,8 +13,8 @@ Changes in 1.4.x the right and left complements. * A new :mod:`clifford.taylor_expansions` is added to implement taylor series of various - multivector function, starting with common trigonometric functions. These functions are - additionally exposed via a + multivector functions, starting with common trigonometric functions. These functions are + additionally exposed via methods on the MultiVector class itself. Changes in 1.3.x ++++++++++++++++