From 9c00192a54d4a7064660a48136093db46c3d2f58 Mon Sep 17 00:00:00 2001 From: Sudeep Sidhu Date: Wed, 18 Nov 2020 17:32:12 +0530 Subject: [PATCH 1/5] Added .xreplace() to Vector and Dyadic --- sympy/physics/vector/dyadic.py | 63 +++++++++++++++++++++++ sympy/physics/vector/tests/test_dyadic.py | 12 +++++ sympy/physics/vector/tests/test_vector.py | 10 ++++ sympy/physics/vector/vector.py | 61 ++++++++++++++++++++++ 4 files changed, 146 insertions(+) diff --git a/sympy/physics/vector/dyadic.py b/sympy/physics/vector/dyadic.py index a608b7cc1983..577b1bff6d99 100644 --- a/sympy/physics/vector/dyadic.py +++ b/sympy/physics/vector/dyadic.py @@ -539,6 +539,69 @@ def _eval_evalf(self, prec): new_args.append(tuple(new_inlist)) return Dyadic(new_args) + def xreplace(self, rule): + """ + Replace occurrences of objects within the expression. + + Parameters + ========== + + rule : dict-like + Expresses a replacement rule + + Returns + ======= + + xreplace : the result of the replacement + + Examples + ======== + + >>> from sympy import symbols, pi + >>> from sympy.physics.vector import ReferenceFrame + >>> N = ReferenceFrame('N') + >>> x, y, z = symbols('x y z') + >>> ((1 + x*y) * (N.x | N.x)).xreplace({x: pi}) + (pi*y + 1)*(N.x|N.x) + >>> ((1 + x*y) * (N.x | N.x)).xreplace({x: pi, y: 2}) + (1 + 2*pi)*(N.x|N.x) + + Replacements occur only if an entire node in the expression tree is + matched: + + >>> ((x*y + z) * (N.x | N.x)).xreplace({x*y: pi}) + (z + pi)*(N.x|N.x) + >>> ((x*y*z) * (N.x | N.x)).xreplace({x*y: pi}) + x*y*z*(N.x|N.x) + + """ + + value, _ = self._xreplace(rule) + return value + + def _xreplace(self, rule): + """ + Helper for xreplace. Tracks whether a replacement actually occurred. + """ + + if rule: + changed = False + new_args = [] + for inlist in self.args: + new_inlist = list(inlist) + a = new_inlist[0] + _xreplace = getattr(a, '_xreplace', None) + if _xreplace is not None: + a_xr = _xreplace(rule) + new_inlist[0] = a_xr[0] + new_args.append(tuple(new_inlist)) + changed |= a_xr[1] + else: + new_args.append(tuple(new_inlist)) + if changed: + return Dyadic(new_args), True + return self, False + def _check_dyadic(other): if not isinstance(other, Dyadic): raise TypeError('A Dyadic must be supplied') diff --git a/sympy/physics/vector/tests/test_dyadic.py b/sympy/physics/vector/tests/test_dyadic.py index 527f757a2fe5..3dc5673c04b8 100644 --- a/sympy/physics/vector/tests/test_dyadic.py +++ b/sympy/physics/vector/tests/test_dyadic.py @@ -104,3 +104,15 @@ def test_dyadic_evalf(): s = symbols('s') a = 5 * s * pi* (N.x | N.x) assert a.evalf(2) == Float('5', 2) * Float('3.1416', 2) * s * (N.x | N.x) + +def test_dyadic_xreplace(): + x, y, z = symbols('x y z') + N = ReferenceFrame('N') + v = x*y * (N.x | N.x) + assert v.xreplace({x : cos(x)}) == cos(x)*y * (N.x | N.x) + assert v.xreplace({x*y : pi}) == pi * (N.x | N.x) + v = (x*y)**z * (N.x | N.x) + assert v.xreplace({(x*y)**z : 1}) == (N.x | N.x) + assert v.xreplace({x:1, z:0}) == (N.x | N.x) + raises(TypeError, lambda: v.xreplace()) + raises(TypeError, lambda: v.xreplace([x, y])) diff --git a/sympy/physics/vector/tests/test_vector.py b/sympy/physics/vector/tests/test_vector.py index 82fe0a8f6415..675f6ac4a29a 100644 --- a/sympy/physics/vector/tests/test_vector.py +++ b/sympy/physics/vector/tests/test_vector.py @@ -177,3 +177,13 @@ def test_vector_evalf(): assert v.evalf(2) == Float('3.1416', 2) * A.x v = pi * A.x + 5 * a * A.y - b * A.z assert v.evalf(3) == Float('3.1416', 3) * A.x + Float('5', 3) * a * A.y - b * A.z + +def test_vector_xreplace(): + x, y, z = symbols('x y z') + v = x**2 * A.x + x*y * A.y + x*y*z * A.z + assert v.xreplace({x : cos(x)}) == cos(x)**2 * A.x + y*cos(x) * A.y + y*z*cos(x) * A.z + assert v.xreplace({x*y : pi}) == x**2 * A.x + pi * A.y + x*y*z * A.z + assert v.xreplace({x*y*z : 1}) == x**2*A.x + x*y*A.y + A.z + assert v.xreplace({x:1, z:0}) == A.x + y * A.y + raises(TypeError, lambda: v.xreplace()) + raises(TypeError, lambda: v.xreplace([x, y])) diff --git a/sympy/physics/vector/vector.py b/sympy/physics/vector/vector.py index d87d51e7f7fd..491eb37f588e 100644 --- a/sympy/physics/vector/vector.py +++ b/sympy/physics/vector/vector.py @@ -719,6 +719,67 @@ def _eval_evalf(self, prec): new_args.append([mat.evalf(n=prec_to_dps(prec)), frame]) return Vector(new_args) + def xreplace(self, rule): + """ + Replace occurrences of objects within the expression. + + Parameters + ========== + + rule : dict-like + Expresses a replacement rule + + Returns + ======= + + xreplace : the result of the replacement + + Examples + ======== + + >>> from sympy import symbols, pi + >>> from sympy.physics.vector import ReferenceFrame + >>> A = ReferenceFrame('A') + >>> x, y, z = symbols('x y z') + >>> ((1 + x*y) * A.x).xreplace({x: pi}) + (pi*y + 1)*A.x + >>> ((1 + x*y) * A.x).xreplace({x: pi, y: 2}) + (1 + 2*pi)*A.x + + Replacements occur only if an entire node in the expression tree is + matched: + + >>> ((x*y + z) * A.x).xreplace({x*y: pi}) + (z + pi)*A.x + >>> ((x*y*z) * A.x).xreplace({x*y: pi}) + x*y*z*A.x + + """ + + value, _ = self._xreplace(rule) + return value + + def _xreplace(self, rule): + """ + Helper for xreplace. Tracks whether a replacement actually occurred. + """ + if self in rule: + return rule[self], True + elif rule: + changed = False + new_args = [] + for mat, frame in self.args: + _xreplace = getattr(mat, '_xreplace', None) + if _xreplace is not None: + mat_xr = _xreplace(rule) + new_args.append([mat_xr[0], frame]) + changed |= mat_xr[1] + else: + new_args.append([mat, frame]) + if changed: + return Vector(new_args), True + return self, False + class VectorTypeError(TypeError): From cce9bbb40d506da2c64e02d649c2778394dbdfeb Mon Sep 17 00:00:00 2001 From: Sudeep Sidhu Date: Wed, 18 Nov 2020 17:42:27 +0530 Subject: [PATCH 2/5] Minor upg to Dyadic --- sympy/physics/vector/dyadic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sympy/physics/vector/dyadic.py b/sympy/physics/vector/dyadic.py index 577b1bff6d99..0f24dd3c16bc 100644 --- a/sympy/physics/vector/dyadic.py +++ b/sympy/physics/vector/dyadic.py @@ -597,7 +597,7 @@ def _xreplace(self, rule): new_args.append(tuple(new_inlist)) changed |= a_xr[1] else: - new_args.append(tuple(new_inlist)) + new_args.append(inlist) if changed: return Dyadic(new_args), True return self, False From f0ca07a43960df7f17999472b6e1177daac1054b Mon Sep 17 00:00:00 2001 From: Sudeep Sidhu Date: Wed, 18 Nov 2020 22:02:51 +0530 Subject: [PATCH 3/5] Add xreplace() to Vector and Dyadic --- sympy/physics/vector/dyadic.py | 31 ++++++------------------------- sympy/physics/vector/vector.py | 29 +++++------------------------ 2 files changed, 11 insertions(+), 49 deletions(-) diff --git a/sympy/physics/vector/dyadic.py b/sympy/physics/vector/dyadic.py index 0f24dd3c16bc..48a67c203149 100644 --- a/sympy/physics/vector/dyadic.py +++ b/sympy/physics/vector/dyadic.py @@ -576,31 +576,12 @@ def xreplace(self, rule): """ - value, _ = self._xreplace(rule) - return value - - def _xreplace(self, rule): - """ - Helper for xreplace. Tracks whether a replacement actually occurred. - """ - - if rule: - changed = False - new_args = [] - for inlist in self.args: - new_inlist = list(inlist) - a = new_inlist[0] - _xreplace = getattr(a, '_xreplace', None) - if _xreplace is not None: - a_xr = _xreplace(rule) - new_inlist[0] = a_xr[0] - new_args.append(tuple(new_inlist)) - changed |= a_xr[1] - else: - new_args.append(inlist) - if changed: - return Dyadic(new_args), True - return self, False + new_args = [] + for inlist in self.args: + new_inlist = list(inlist) + new_inlist[0] = new_inlist[0].xreplace(rule) + new_args.append(tuple(new_inlist)) + return Dyadic(new_args) def _check_dyadic(other): if not isinstance(other, Dyadic): diff --git a/sympy/physics/vector/vector.py b/sympy/physics/vector/vector.py index 491eb37f588e..80f942946510 100644 --- a/sympy/physics/vector/vector.py +++ b/sympy/physics/vector/vector.py @@ -756,30 +756,11 @@ def xreplace(self, rule): """ - value, _ = self._xreplace(rule) - return value - - def _xreplace(self, rule): - """ - Helper for xreplace. Tracks whether a replacement actually occurred. - """ - if self in rule: - return rule[self], True - elif rule: - changed = False - new_args = [] - for mat, frame in self.args: - _xreplace = getattr(mat, '_xreplace', None) - if _xreplace is not None: - mat_xr = _xreplace(rule) - new_args.append([mat_xr[0], frame]) - changed |= mat_xr[1] - else: - new_args.append([mat, frame]) - if changed: - return Vector(new_args), True - return self, False - + new_args = [] + for mat, frame in self.args: + mat = mat.xreplace(rule) + new_args.append([mat, frame]) + return Vector(new_args) class VectorTypeError(TypeError): From 1fe24e6319045867c1a791a7c7e6e6862883fed2 Mon Sep 17 00:00:00 2001 From: Sudeep Sidhu Date: Mon, 23 Nov 2020 16:39:20 +0530 Subject: [PATCH 4/5] Make use of outer and improved docstring --- sympy/physics/vector/dyadic.py | 17 +++++++++-------- sympy/physics/vector/tests/test_dyadic.py | 15 ++++++++------- sympy/physics/vector/vector.py | 6 +++--- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/sympy/physics/vector/dyadic.py b/sympy/physics/vector/dyadic.py index 48a67c203149..0555f3c60faf 100644 --- a/sympy/physics/vector/dyadic.py +++ b/sympy/physics/vector/dyadic.py @@ -541,37 +541,38 @@ def _eval_evalf(self, prec): def xreplace(self, rule): """ - Replace occurrences of objects within the expression. + Replace occurrences of objects within the measure numbers of the Dyadic. Parameters ========== rule : dict-like - Expresses a replacement rule + Expresses a replacement rule. Returns ======= - xreplace : the result of the replacement + Dyadic : Result of the replacement. Examples ======== >>> from sympy import symbols, pi - >>> from sympy.physics.vector import ReferenceFrame + >>> from sympy.physics.vector import ReferenceFrame, outer >>> N = ReferenceFrame('N') + >>> D = outer(N.x, N.x) >>> x, y, z = symbols('x y z') - >>> ((1 + x*y) * (N.x | N.x)).xreplace({x: pi}) + >>> ((1 + x*y) * D).xreplace({x: pi}) (pi*y + 1)*(N.x|N.x) - >>> ((1 + x*y) * (N.x | N.x)).xreplace({x: pi, y: 2}) + >>> ((1 + x*y) * D).xreplace({x: pi, y: 2}) (1 + 2*pi)*(N.x|N.x) Replacements occur only if an entire node in the expression tree is matched: - >>> ((x*y + z) * (N.x | N.x)).xreplace({x*y: pi}) + >>> ((x*y + z) * D).xreplace({x*y: pi}) (z + pi)*(N.x|N.x) - >>> ((x*y*z) * (N.x | N.x)).xreplace({x*y: pi}) + >>> ((x*y*z) * D).xreplace({x*y: pi}) x*y*z*(N.x|N.x) """ diff --git a/sympy/physics/vector/tests/test_dyadic.py b/sympy/physics/vector/tests/test_dyadic.py index 3dc5673c04b8..44ce43d450cd 100644 --- a/sympy/physics/vector/tests/test_dyadic.py +++ b/sympy/physics/vector/tests/test_dyadic.py @@ -1,5 +1,5 @@ from sympy import sin, cos, symbols, pi, Float, ImmutableMatrix as Matrix -from sympy.physics.vector import ReferenceFrame, Vector, dynamicsymbols +from sympy.physics.vector import ReferenceFrame, Vector, dynamicsymbols, outer from sympy.physics.vector.dyadic import _check_dyadic from sympy.testing.pytest import raises @@ -108,11 +108,12 @@ def test_dyadic_evalf(): def test_dyadic_xreplace(): x, y, z = symbols('x y z') N = ReferenceFrame('N') - v = x*y * (N.x | N.x) - assert v.xreplace({x : cos(x)}) == cos(x)*y * (N.x | N.x) - assert v.xreplace({x*y : pi}) == pi * (N.x | N.x) - v = (x*y)**z * (N.x | N.x) - assert v.xreplace({(x*y)**z : 1}) == (N.x | N.x) - assert v.xreplace({x:1, z:0}) == (N.x | N.x) + D = outer(N.x, N.x) + v = x*y * D + assert v.xreplace({x : cos(x)}) == cos(x)*y * D + assert v.xreplace({x*y : pi}) == pi * D + v = (x*y)**z * D + assert v.xreplace({(x*y)**z : 1}) == D + assert v.xreplace({x:1, z:0}) == D raises(TypeError, lambda: v.xreplace()) raises(TypeError, lambda: v.xreplace([x, y])) diff --git a/sympy/physics/vector/vector.py b/sympy/physics/vector/vector.py index 80f942946510..924dc47d244d 100644 --- a/sympy/physics/vector/vector.py +++ b/sympy/physics/vector/vector.py @@ -721,18 +721,18 @@ def _eval_evalf(self, prec): def xreplace(self, rule): """ - Replace occurrences of objects within the expression. + Replace occurrences of objects within the measure numbers of the vector. Parameters ========== rule : dict-like - Expresses a replacement rule + Expresses a replacement rule. Returns ======= - xreplace : the result of the replacement + Vector : Result of the replacement. Examples ======== From 83df99beb9288e15792d6e6209bc64180d290333 Mon Sep 17 00:00:00 2001 From: Sudeep Sidhu Date: Mon, 23 Nov 2020 17:04:58 +0530 Subject: [PATCH 5/5] Follow numpy doc convention --- sympy/physics/vector/dyadic.py | 3 ++- sympy/physics/vector/vector.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sympy/physics/vector/dyadic.py b/sympy/physics/vector/dyadic.py index 0555f3c60faf..4904e305ee7e 100644 --- a/sympy/physics/vector/dyadic.py +++ b/sympy/physics/vector/dyadic.py @@ -552,7 +552,8 @@ def xreplace(self, rule): Returns ======= - Dyadic : Result of the replacement. + Dyadic + Result of the replacement. Examples ======== diff --git a/sympy/physics/vector/vector.py b/sympy/physics/vector/vector.py index 924dc47d244d..0a1f66aaa21d 100644 --- a/sympy/physics/vector/vector.py +++ b/sympy/physics/vector/vector.py @@ -732,7 +732,8 @@ def xreplace(self, rule): Returns ======= - Vector : Result of the replacement. + Vector + Result of the replacement. Examples ========