Repository: sympy/sympy

Issue:
Subtracting waves in optics module
The Optics module enables working with waves.

However, subtracting the waves which follows intuitively results in the result being an instance of the `sympy.core.add.Add` class. This stops the user from accessing properties of the resultant wave like amplitude, phase etc. 

This can be fixed by simply returning the result of the  `__add__(self, other)` method with the amplitude of `other` multiplied by `-1`.

Is it a good idea to make this change ?


``` python
In [1]: from sympy.physics.optics import *
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-1-1d52e0c05aa5> in <module>
----> 1 from sympy.physics.optics import *

AttributeError: module 'sympy.physics.optics' has no attribute 'polarizing_beam_splitte'

In [2]: from sympy.physics.optics import TWave

In [3]: A,f,phi = symbols('A,f,phi')

In [4]: w1 = TWave(A, f)

In [5]: w2 = TWave(A, f, phi)

In [6]: w1+w2
Out[6]: TWave(sqrt(2*A**2*cos(phi) + 2*A**2), f, atan2(A*cos(phi) + A, A*sin(phi)))

In [7]: _.amplitude
Out[7]: 
   ____________________
  ╱    2             2 
╲╱  2⋅A ⋅cos(φ) + 2⋅A  

In [8]: w1 - w2
Out[8]: TWave(A, f) - TWave(A, f, phi)

In [9]: type(_)
Out[9]: sympy.core.add.Add

In [10]: (w1 - w2).amplitude
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-10-cea1328571ac> in <module>
----> 1 (w1 - w2).amplitude

AttributeError: 'Add' object has no attribute 'amplitude'
```

Discussion:

Patch:
diff --git a/LICENSE b/LICENSE
index 5a3d97daf3..551e9de593 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2006-2021 SymPy Development Team
+Copyright (c) 2006-2020 SymPy Development Team
 
 All rights reserved.
 
@@ -33,7 +33,7 @@ Patches that were taken from the Diofant project (https://github.com/diofant/dio
 are licensed as:
 
 Copyright (c) 2006-2018 SymPy Development Team,
-              2013-2021 Sergey B Kirpichev
+              2013-2020 Sergey B Kirpichev
 
 All rights reserved.
 
@@ -101,7 +101,7 @@ DAMAGE.
 The files under the directory sympy/parsing/autolev/tests/pydy-example-repo
 are directly copied from PyDy project and are licensed as:
 
-Copyright (c) 2009-2021, PyDy Authors
+Copyright (c) 2009-2020, PyDy Authors
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
index f68dcd78f9..ea1ea035ad 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -17,24 +17,11 @@ open. -->
 
 #### Release Notes
 
-<!-- Write the release notes for this release below between the BEGIN and END
-statements. The basic format is a bulleted list with the name of the subpackage
-and the release note for this PR. For example:
-
-* solvers
-  * Added a new solver for logarithmic equations.
-
-* functions
-  * Fixed a bug with log of integers.
-
-or if no release note(s) should be included use:
-
-NO ENTRY
-
-See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
-information on how to write release notes. The bot will check your release
-notes automatically to see if they are formatted correctly. -->
+<!-- Write the release notes for this release below. See
+https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more information
+on how to write release notes. The bot will check your release notes
+automatically to see if they are formatted correctly. -->
 
 <!-- BEGIN RELEASE NOTES -->
 
-<!-- END RELEASE NOTES -->
+<!-- END RELEASE NOTES -->
\ No newline at end of file
diff --git a/sympy/physics/optics/__init__.py b/sympy/physics/optics/__init__.py
index 1a0a31632f..d2d83d452f 100644
--- a/sympy/physics/optics/__init__.py
+++ b/sympy/physics/optics/__init__.py
@@ -16,7 +16,7 @@
     'jones_vector', 'stokes_vector', 'jones_2_stokes', 'linear_polarizer',
     'phase_retarder', 'half_wave_retarder', 'quarter_wave_retarder',
     'transmissive_filter', 'reflective_filter', 'mueller_matrix',
-    'polarizing_beam_splitte',
+    'polarizing_beam_splitter',
 ]
 from .waves import TWave
 
@@ -35,4 +35,4 @@
 from .polarization import (jones_vector, stokes_vector, jones_2_stokes,
         linear_polarizer, phase_retarder, half_wave_retarder,
         quarter_wave_retarder, transmissive_filter, reflective_filter,
-        mueller_matrix)
+        mueller_matrix, polarizing_beam_splitter)
diff --git a/sympy/physics/optics/waves.py b/sympy/physics/optics/waves.py
index 9fba2e2cfc..01883cf6d3 100644
--- a/sympy/physics/optics/waves.py
+++ b/sympy/physics/optics/waves.py
@@ -8,7 +8,7 @@
 
 __all__ = ['TWave']
 
-from sympy import (sympify, pi, sin, cos, sqrt, Symbol, S,
+from sympy import (sympify, pi, sin, cos, sqrt, Number, Symbol, S,
     symbols, Derivative, atan2)
 from sympy.core.expr import Expr
 from sympy.physics.units import speed_of_light, meter, second
@@ -28,7 +28,7 @@ class TWave(Expr):
     ===========
 
     It is represented as :math:`A \times cos(k*x - \omega \times t + \phi )`,
-    where :math:`A` is the amplitude, :math:`\omega` is the angular velocity,
+    where :math:`A` is the amplitude, :math:`\omega` is the angular frequency,
     :math:`k` is the wavenumber (spatial frequency), :math:`x` is a spatial variable
     to represent the position on the dimension on which the wave propagates,
     and :math:`\phi` is the phase angle of the wave.
@@ -67,11 +67,11 @@ class TWave(Expr):
     >>> w3 = w1 + w2  # Superposition of two waves
     >>> w3
     TWave(sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2), f,
-        atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2)))
+        atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2)))
     >>> w3.amplitude
     sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)
     >>> w3.phase
-    atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2))
+    atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2))
     >>> w3.speed
     299792458*meter/(second*n)
     >>> w3.angular_velocity
@@ -266,13 +266,13 @@ def __add__(self, other):
         if isinstance(other, TWave):
             if self._frequency == other._frequency and self.wavelength == other.wavelength:
                 return TWave(sqrt(self._amplitude**2 + other._amplitude**2 + 2 *
-                                  self.amplitude*other.amplitude*cos(
+                                  self._amplitude*other._amplitude*cos(
                                       self._phase - other.phase)),
-                             self.frequency,
-                             atan2(self._amplitude*cos(self._phase)
-                             +other._amplitude*cos(other._phase),
-                             self._amplitude*sin(self._phase)
-                             +other._amplitude*sin(other._phase))
+                             self._frequency,
+                             atan2(self._amplitude*sin(self._phase)
+                             + other._amplitude*sin(other._phase),
+                             self._amplitude*cos(self._phase)
+                             + other._amplitude*cos(other._phase))
                              )
             else:
                 raise NotImplementedError("Interference of waves with different frequencies"
@@ -280,6 +280,31 @@ def __add__(self, other):
         else:
             raise TypeError(type(other).__name__ + " and TWave objects can't be added.")
 
+    def __mul__(self, other):
+        """
+        Multiplying a wave by a scalar rescales the amplitude of the wave.
+        """
+        other = sympify(other)
+        if isinstance(other, Number):
+            return TWave(self._amplitude*other, *self.args[1:])
+        else:
+            raise TypeError(type(other).__name__ + " and TWave objects can't be multiplied.")
+
+    def __sub__(self, other):
+        return self.__add__(-1*other)
+
+    def __neg__(self):
+        return self.__mul__(-1)
+
+    def __radd__(self, other):
+        return self.__add__(other)
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __rsub__(self, other):
+        return (-self).__radd__(other)
+
     def _eval_rewrite_as_sin(self, *args, **kwargs):
         return self._amplitude*sin(self.wavenumber*Symbol('x')
             - self.angular_velocity*Symbol('t') + self._phase + pi/2, evaluate=False)


Test patch:
diff --git a/sympy/physics/optics/tests/test_waves.py b/sympy/physics/optics/tests/test_waves.py
index 675efe72d9..59c5dc6da5 100644
--- a/sympy/physics/optics/tests/test_waves.py
+++ b/sympy/physics/optics/tests/test_waves.py
@@ -9,7 +9,6 @@
 
 c = speed_of_light.convert_to(m/s)
 
-
 def test_twave():
     A1, phi1, A2, phi2, f = symbols('A1, phi1, A2, phi2, f')
     n = Symbol('n')  # Refractive index
@@ -26,26 +25,54 @@ def test_twave():
     assert w1.angular_velocity == 2*pi*f
     assert w1.wavenumber == 2*pi*f*n/c
     assert w1.speed == c/n
+
     w3 = w1 + w2
     assert w3.amplitude == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2) + A2**2)
     assert w3.frequency == f
-    assert w3.phase == atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2))
+    assert w3.phase == atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2))
     assert w3.wavelength == c/(f*n)
     assert w3.time_period == 1/f
     assert w3.angular_velocity == 2*pi*f
     assert w3.wavenumber == 2*pi*f*n/c
     assert w3.speed == c/n
-    assert simplify(w3.rewrite(sin) - sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2)
-    + A2**2)*sin(pi*f*n*x*s/(149896229*m) - 2*pi*f*t + atan2(A1*cos(phi1)
-    + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2)) + pi/2)) == 0
+    assert simplify(w3.rewrite(sin) - w2.rewrite(sin) - w1.rewrite(sin)) == 0
     assert w3.rewrite('pde') == epsilon*mu*Derivative(E(x, t), t, t) + Derivative(E(x, t), x, x)
     assert w3.rewrite(cos) == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2)
-    + A2**2)*cos(pi*f*n*x*s/(149896229*m) - 2*pi*f*t + atan2(A1*cos(phi1)
-    + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2)))
+        + A2**2)*cos(pi*f*n*x*s/(149896229*m) - 2*pi*f*t + atan2(A1*sin(phi1)
+        + A2*sin(phi2), A1*cos(phi1) + A2*cos(phi2)))
     assert w3.rewrite(exp) == sqrt(A1**2 + 2*A1*A2*cos(phi1 - phi2)
-    + A2**2)*exp(I*(pi*f*n*x*s/(149896229*m) - 2*pi*f*t
-    + atan2(A1*cos(phi1) + A2*cos(phi2), A1*sin(phi1) + A2*sin(phi2))))
+        + A2**2)*exp(I*(-2*pi*f*t + atan2(A1*sin(phi1) + A2*sin(phi2), A1*cos(phi1)
+        + A2*cos(phi2)) + pi*s*f*n*x/(149896229*m)))
+
     w4 = TWave(A1, None, 0, 1/f)
     assert w4.frequency == f
+
+    w5 = w1 - w2
+    assert w5.amplitude == sqrt(A1**2 - 2*A1*A2*cos(phi1 - phi2) + A2**2)
+    assert w5.frequency == f
+    assert w5.phase == atan2(A1*sin(phi1) - A2*sin(phi2), A1*cos(phi1) - A2*cos(phi2))
+    assert w5.wavelength == c/(f*n)
+    assert w5.time_period == 1/f
+    assert w5.angular_velocity == 2*pi*f
+    assert w5.wavenumber == 2*pi*f*n/c
+    assert w5.speed == c/n
+    assert simplify(w5.rewrite(sin) - w1.rewrite(sin) + w2.rewrite(sin)) == 0
+    assert w5.rewrite('pde') == epsilon*mu*Derivative(E(x, t), t, t) + Derivative(E(x, t), x, x)
+    assert w5.rewrite(cos) == sqrt(A1**2 - 2*A1*A2*cos(phi1 - phi2)
+        + A2**2)*cos(-2*pi*f*t + atan2(A1*sin(phi1) - A2*sin(phi2), A1*cos(phi1)
+        - A2*cos(phi2)) + pi*s*f*n*x/(149896229*m))
+    assert w5.rewrite(exp) == sqrt(A1**2 - 2*A1*A2*cos(phi1 - phi2)
+        + A2**2)*exp(I*(-2*pi*f*t + atan2(A1*sin(phi1) - A2*sin(phi2), A1*cos(phi1)
+        - A2*cos(phi2)) + pi*s*f*n*x/(149896229*m)))
+
+    w6 = 2*w1
+    assert w6.amplitude == 2*A1
+    assert w6.frequency == f
+    assert w6.phase == phi1
+    w7 = -w6
+    assert w7.amplitude == -2*A1
+    assert w7.frequency == f
+    assert w7.phase == phi1
+
     raises(ValueError, lambda:TWave(A1))
     raises(ValueError, lambda:TWave(A1, f, phi1, t))