Repository: Qiskit/qiskit

Issue:
ParametereExpression.__eq__ doesn't gracefully convert numeric types
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

`__eq__` in python conventionally considers objects equal if they are semantically equivalent, even if their types differ. 

```
>>> 2 == 2
True
>>> 2.0 == 2.0
True
>>> 2 == 2.0
True
```
Same for Sympy:
```
>>> import sympy as sp
>>> sp.Integer(2) == sp.Integer(2)
True
>>> sp.Float('2.0', precision=53) == sp.Float(2.0, precision=54)
True
>>> sp.Integer(2) == sp.Float('2.0', precision=53)
True
```

But `ParameterExpression.__eq__` breaks this convention.

```
>>> import qiskit as qk
>>> theta = qk.circuit.Parameter('theta')
>>> theta * 2 == theta * 2
True
>>> theta * 2.0 == theta * 2.0
True
>>> theta * 2 == theta * 2.0
False
```

Currently, at https://github.com/Qiskit/qiskit-terra/blob/44462a8b13ea6c2cce0f9c7345c26c15fb0d4ce3/qiskit/circuit/parameterexpression.py#L389 , we compare `srepr(self._symbol_expr) == srepr(other._symbol_expr)` as strings. (`srepr` of the examples above would look like `Mul(Integer(2), Symbol('th'))` for `theta * 2` and `Mul(Float('2.0', precision=53), Symbol('th'))` for `theta * 2.0`. Instead, we should walk the sympy expression tree (following https://docs.sympy.org/latest/tutorial/manipulation.html#recursing-through-an-expression-tree ) and rely on sympy's built in `__eq__`. 

(The original motivation for using `srepr` instead of directly comparing `._symbol_expr` isn't clear from the current code, so that may be another option worth investigating.)


Discussion:
This sounds reasonable to me. The `srepr` approach seemed a bit strict.

Resolving patch:
diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py
--- a/qiskit/circuit/parameterexpression.py
+++ b/qiskit/circuit/parameterexpression.py
@@ -383,10 +383,9 @@ def __eq__(self, other):
         Returns:
             bool: result of the comparison
         """
-        from sympy import srepr
         if isinstance(other, ParameterExpression):
             return (self.parameters == other.parameters
-                    and srepr(self._symbol_expr) == srepr(other._symbol_expr))
+                    and self._symbol_expr.equals(other._symbol_expr))
         elif isinstance(other, numbers.Number):
             return (len(self.parameters) == 0
                     and complex(self._symbol_expr) == other)


Test patch:
diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py
--- a/test/python/circuit/test_parameters.py
+++ b/test/python/circuit/test_parameters.py
@@ -1546,6 +1546,14 @@ def test_parameter_expression_equal_to_identical(self):
 
         self.assertEqual(expr1, expr2)
 
+    def test_parameter_expression_equal_floats_to_ints(self):
+        """Verify an expression with float and int is identical."""
+        theta = Parameter('theta')
+        expr1 = 2.0 * theta
+        expr2 = 2 * theta
+
+        self.assertEqual(expr1, expr2)
+
     def test_parameter_expression_not_equal_if_params_differ(self):
         """Verify expressions not equal if parameters are different."""
         theta1 = Parameter('theta')