From 03f43f8f5a67b25ea4967002193efa7488f79322 Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Fri, 7 Jan 2022 11:27:00 +0100 Subject: [PATCH 01/11] added abs method to parameterexpression --- qiskit/circuit/parameterexpression.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 55d35af2a3bf..58cdd25efa3c 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -421,6 +421,15 @@ def log(self): return self._call(_log) + def abs(self): + """Absolute of a ParameterExpression""" + if HAS_SYMENGINE: + return self._call(symengine.abs) + else: + from sympy import Abs as _abs + + return self._call(_abs) + def __repr__(self): return f"{self.__class__.__name__}({str(self)})" From eff2f3481c751ab161bc1f26108db30ee8b6bc43 Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Fri, 7 Jan 2022 13:42:39 +0100 Subject: [PATCH 02/11] fixed wrong call in abs function --- qiskit/circuit/parameterexpression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 58cdd25efa3c..b02a403d050b 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -424,7 +424,7 @@ def log(self): def abs(self): """Absolute of a ParameterExpression""" if HAS_SYMENGINE: - return self._call(symengine.abs) + return self._call(symengine.Abs) else: from sympy import Abs as _abs From 966ee8e35735e0bd5c9949a6dd3c8b830a869fc7 Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Mon, 10 Jan 2022 09:13:36 +0100 Subject: [PATCH 03/11] changed definition of abs to __abs__ --- qiskit/circuit/parameterexpression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index b02a403d050b..0f30c68e9b4a 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -421,7 +421,7 @@ def log(self): return self._call(_log) - def abs(self): + def __abs__(self): """Absolute of a ParameterExpression""" if HAS_SYMENGINE: return self._call(symengine.Abs) From fa1bae4a50e6f7d411ef203ffd8e493f806e9de8 Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Mon, 10 Jan 2022 14:28:10 +0100 Subject: [PATCH 04/11] additionally implemented __le__, __ge__, __lt__, __gt__ operators --- qiskit/circuit/parameterexpression.py | 64 ++++++++++++++++++++++---- test/python/circuit/test_parameters.py | 3 ++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 0f30c68e9b4a..88923c80f16b 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -421,15 +421,6 @@ def log(self): return self._call(_log) - def __abs__(self): - """Absolute of a ParameterExpression""" - if HAS_SYMENGINE: - return self._call(symengine.Abs) - else: - from sympy import Abs as _abs - - return self._call(_abs) - def __repr__(self): return f"{self.__class__.__name__}({str(self)})" @@ -477,6 +468,15 @@ def __copy__(self): def __deepcopy__(self, memo=None): return self + def __abs__(self): + """Absolute of a ParameterExpression""" + if HAS_SYMENGINE: + return self._call(symengine.Abs) + else: + from sympy import Abs as _abs + + return self._call(_abs) + def __eq__(self, other): """Check if this parameter expression is equal to another parameter expression or a fixed value (only if this is a bound expression). @@ -499,6 +499,52 @@ def __eq__(self, other): return len(self.parameters) == 0 and complex(self._symbol_expr) == other return False + def __lt__(self, other): + """Check if this parameter expression is less than another parameter expression + or a fixed value (only if this is a bound expression). + Args: + other (ParameterExpression or a number): + Parameter expression or numeric constant used for comparison + Returns: + bool: result of the comparison + """ + if isinstance(other, ParameterExpression): + if HAS_SYMENGINE: + from sympy import sympify + + return sympify(self._symbol_expr).__lt__(sympify(other._symbol_expr)) + else: + return self._symbol_expr.__lt__(other._symbol_expr) + elif isinstance(other, numbers.Number): + return len(self.parameters) == 0 and float(self._symbol_expr) < other + return False + + def __gt__(self, other): + """Check if this parameter expression is greater than another parameter expression + or a fixed value (only if this is a bound expression). + Args: + other (ParameterExpression or a number): + Parameter expression or numeric constant used for comparison + Returns: + bool: result of the comparison + """ + if isinstance(other, ParameterExpression): + if HAS_SYMENGINE: + from sympy import sympify + + return sympify(self._symbol_expr).__gt__(sympify(other._symbol_expr)) + else: + return self._symbol_expr.__gt__(other._symbol_expr) + elif isinstance(other, numbers.Number): + return len(self.parameters) == 0 and float(self._symbol_expr) > other + return False + + def __ge__(self, other): + return not self.__lt__(other) + + def __le__(self, other): + return not self.__gt__(other) + def __getstate__(self): if HAS_SYMENGINE: from sympy import sympify diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 054e02850e73..427f8671d389 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -1178,7 +1178,10 @@ def test_compare_to_value_when_bound(self): x = Parameter("x") bound_expr = x.bind({x: 2.3}) + self.assertEqual(bound_expr, 2.3) + self.assertTrue(bound_expr < 3.0) + self.assertTrue(bound_expr > 1.0) def test_cast_to_float_when_bound(self): """Verify expression can be cast to a float when fully bound.""" From 93e1d6d86da1232b8e1a45eeb2994c89f1a41420 Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Mon, 17 Jan 2022 10:07:13 +0100 Subject: [PATCH 05/11] fixed requested features and implemented more tests for __lt__, __gt__ --- qiskit/circuit/parameterexpression.py | 30 +++++++++++++++++++------- test/python/circuit/test_parameters.py | 20 +++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 88923c80f16b..078b3014670e 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -499,7 +499,7 @@ def __eq__(self, other): return len(self.parameters) == 0 and complex(self._symbol_expr) == other return False - def __lt__(self, other): + def __lt__(self, other: ParameterValueType) -> bool: """Check if this parameter expression is less than another parameter expression or a fixed value (only if this is a bound expression). Args: @@ -516,10 +516,17 @@ def __lt__(self, other): else: return self._symbol_expr.__lt__(other._symbol_expr) elif isinstance(other, numbers.Number): - return len(self.parameters) == 0 and float(self._symbol_expr) < other - return False + if len(self.parameters) == 0: + return float(self._symbol_expr) < other + else: + raise TypeError( + "'<' not supported between instances of {type(self)} " + "with unbound parameters {self.parameters} and {type(other)}." + ) + else: + raise TypeError("'<' not supported between instances of {type(self)} and {type(other)}") - def __gt__(self, other): + def __gt__(self, other: ParameterValueType) -> bool: """Check if this parameter expression is greater than another parameter expression or a fixed value (only if this is a bound expression). Args: @@ -536,13 +543,20 @@ def __gt__(self, other): else: return self._symbol_expr.__gt__(other._symbol_expr) elif isinstance(other, numbers.Number): - return len(self.parameters) == 0 and float(self._symbol_expr) > other - return False + if len(self.parameters) == 0: + return float(self._symbol_expr) > other + else: + raise TypeError( + "'>' not supported between instances of {type(self)} " + "with unbound parameters {self.parameters} and {type(other)}." + ) + else: + raise TypeError("'>' not supported between instances of {type(self)} and {type(other)}") - def __ge__(self, other): + def __ge__(self, other: ParameterValueType) -> bool: return not self.__lt__(other) - def __le__(self, other): + def __le__(self, other: ParameterValueType) -> bool: return not self.__gt__(other) def __getstate__(self): diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 427f8671d389..d57d856c4164 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -1182,6 +1182,26 @@ def test_compare_to_value_when_bound(self): self.assertEqual(bound_expr, 2.3) self.assertTrue(bound_expr < 3.0) self.assertTrue(bound_expr > 1.0) + self.assertEqual(abs(bound_expr), 2.3) + + def test_raise_if_compare_not_supported(self): + """Verify raises if compare to object.""" + x = Parameter("x") + y = object + + with self.assertRaisesRegex(TypeError, "not supported"): + x.__lt__(y) + with self.assertRaisesRegex(TypeError, "not supported"): + x.__gt__(y) + + def test_raise_if_compare_to_value_not_bound(self): + """Verify raises if compare to value and not bound.""" + x = Parameter("x") + + with self.assertRaisesRegex(TypeError, "unbound parameters"): + x.__gt__(2.3) + with self.assertRaisesRegex(TypeError, "unbound parameters"): + x.__lt__(2.3) def test_cast_to_float_when_bound(self): """Verify expression can be cast to a float when fully bound.""" From a30b8db7cc427268a40c497e75d15e66a13ec94e Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Mon, 17 Jan 2022 10:17:20 +0100 Subject: [PATCH 06/11] fixed lint errors in parameterexpression and its tests --- qiskit/circuit/parameterexpression.py | 2 +- test/python/circuit/test_parameters.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 078b3014670e..6fb3dfdf9fe1 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -518,7 +518,7 @@ def __lt__(self, other: ParameterValueType) -> bool: elif isinstance(other, numbers.Number): if len(self.parameters) == 0: return float(self._symbol_expr) < other - else: + else: raise TypeError( "'<' not supported between instances of {type(self)} " "with unbound parameters {self.parameters} and {type(other)}." diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index d57d856c4164..08c272ab3fc0 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -1197,7 +1197,7 @@ def test_raise_if_compare_not_supported(self): def test_raise_if_compare_to_value_not_bound(self): """Verify raises if compare to value and not bound.""" x = Parameter("x") - + with self.assertRaisesRegex(TypeError, "unbound parameters"): x.__gt__(2.3) with self.assertRaisesRegex(TypeError, "unbound parameters"): From 1b98392fd00f8b78cb8e993dd25e5281d3d3f690 Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Tue, 18 Jan 2022 14:57:14 +0100 Subject: [PATCH 07/11] fixed formatting error in docstrings --- qiskit/circuit/parameterexpression.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 6fb3dfdf9fe1..3991e0a822e5 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -505,6 +505,10 @@ def __lt__(self, other: ParameterValueType) -> bool: Args: other (ParameterExpression or a number): Parameter expression or numeric constant used for comparison + Raises: + TypeError: + - If comparison to number with unbound parameters. + - If comparison to type of other object unsupported. Returns: bool: result of the comparison """ @@ -532,6 +536,10 @@ def __gt__(self, other: ParameterValueType) -> bool: Args: other (ParameterExpression or a number): Parameter expression or numeric constant used for comparison + Raises: + TypeError: + - If comparison to number with unbound parameters. + - If comparison to type of other object unsupported. Returns: bool: result of the comparison """ From 296e6aedcb90fef14a5b13dca52eb81a4eb508fb Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Wed, 19 Jan 2022 10:54:02 +0100 Subject: [PATCH 08/11] removed __lt__ and friends, fixed recommendations --- qiskit/circuit/parameterexpression.py | 68 -------------------------- test/python/circuit/test_parameters.py | 21 -------- 2 files changed, 89 deletions(-) diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 3991e0a822e5..54ef9aeaa598 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -499,74 +499,6 @@ def __eq__(self, other): return len(self.parameters) == 0 and complex(self._symbol_expr) == other return False - def __lt__(self, other: ParameterValueType) -> bool: - """Check if this parameter expression is less than another parameter expression - or a fixed value (only if this is a bound expression). - Args: - other (ParameterExpression or a number): - Parameter expression or numeric constant used for comparison - Raises: - TypeError: - - If comparison to number with unbound parameters. - - If comparison to type of other object unsupported. - Returns: - bool: result of the comparison - """ - if isinstance(other, ParameterExpression): - if HAS_SYMENGINE: - from sympy import sympify - - return sympify(self._symbol_expr).__lt__(sympify(other._symbol_expr)) - else: - return self._symbol_expr.__lt__(other._symbol_expr) - elif isinstance(other, numbers.Number): - if len(self.parameters) == 0: - return float(self._symbol_expr) < other - else: - raise TypeError( - "'<' not supported between instances of {type(self)} " - "with unbound parameters {self.parameters} and {type(other)}." - ) - else: - raise TypeError("'<' not supported between instances of {type(self)} and {type(other)}") - - def __gt__(self, other: ParameterValueType) -> bool: - """Check if this parameter expression is greater than another parameter expression - or a fixed value (only if this is a bound expression). - Args: - other (ParameterExpression or a number): - Parameter expression or numeric constant used for comparison - Raises: - TypeError: - - If comparison to number with unbound parameters. - - If comparison to type of other object unsupported. - Returns: - bool: result of the comparison - """ - if isinstance(other, ParameterExpression): - if HAS_SYMENGINE: - from sympy import sympify - - return sympify(self._symbol_expr).__gt__(sympify(other._symbol_expr)) - else: - return self._symbol_expr.__gt__(other._symbol_expr) - elif isinstance(other, numbers.Number): - if len(self.parameters) == 0: - return float(self._symbol_expr) > other - else: - raise TypeError( - "'>' not supported between instances of {type(self)} " - "with unbound parameters {self.parameters} and {type(other)}." - ) - else: - raise TypeError("'>' not supported between instances of {type(self)} and {type(other)}") - - def __ge__(self, other: ParameterValueType) -> bool: - return not self.__lt__(other) - - def __le__(self, other: ParameterValueType) -> bool: - return not self.__gt__(other) - def __getstate__(self): if HAS_SYMENGINE: from sympy import sympify diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 08c272ab3fc0..0da2a3c19179 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -1180,29 +1180,8 @@ def test_compare_to_value_when_bound(self): bound_expr = x.bind({x: 2.3}) self.assertEqual(bound_expr, 2.3) - self.assertTrue(bound_expr < 3.0) - self.assertTrue(bound_expr > 1.0) self.assertEqual(abs(bound_expr), 2.3) - def test_raise_if_compare_not_supported(self): - """Verify raises if compare to object.""" - x = Parameter("x") - y = object - - with self.assertRaisesRegex(TypeError, "not supported"): - x.__lt__(y) - with self.assertRaisesRegex(TypeError, "not supported"): - x.__gt__(y) - - def test_raise_if_compare_to_value_not_bound(self): - """Verify raises if compare to value and not bound.""" - x = Parameter("x") - - with self.assertRaisesRegex(TypeError, "unbound parameters"): - x.__gt__(2.3) - with self.assertRaisesRegex(TypeError, "unbound parameters"): - x.__lt__(2.3) - def test_cast_to_float_when_bound(self): """Verify expression can be cast to a float when fully bound.""" From f72c7fe9aa2f8f52866729b5b1dd6a7bdc523620 Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Wed, 19 Jan 2022 18:15:51 +0100 Subject: [PATCH 09/11] added more tests for abs function --- test/python/circuit/test_parameters.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 0da2a3c19179..b72367e473ee 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -1180,7 +1180,18 @@ def test_compare_to_value_when_bound(self): bound_expr = x.bind({x: 2.3}) self.assertEqual(bound_expr, 2.3) - self.assertEqual(abs(bound_expr), 2.3) + + def test_abs_function_when_bound(self): + """Verify expression can be used with + abs functions when bound.""" + + x = Parameter("x") + xb_1 = x.bind({x: 1.0}) + xb_2 = x.bind({x: 3.+4.j}) + + self.assertEqual(abs(xb_1), 1.0) + self.assertEqual(abs(-xb_1), 1.0) + self.assertEqual(abs(xb_2), 5.0) def test_cast_to_float_when_bound(self): """Verify expression can be cast to a float when fully bound.""" From 7af0db7d84dc13c695b47d214fe6c59af804570e Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Wed, 19 Jan 2022 18:16:20 +0100 Subject: [PATCH 10/11] fixed lint --- test/python/circuit/test_parameters.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index b72367e473ee..7228e1fa148b 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -1180,15 +1180,15 @@ def test_compare_to_value_when_bound(self): bound_expr = x.bind({x: 2.3}) self.assertEqual(bound_expr, 2.3) - + def test_abs_function_when_bound(self): - """Verify expression can be used with + """Verify expression can be used with abs functions when bound.""" x = Parameter("x") xb_1 = x.bind({x: 1.0}) - xb_2 = x.bind({x: 3.+4.j}) - + xb_2 = x.bind({x: 3.0 + 4.0j}) + self.assertEqual(abs(xb_1), 1.0) self.assertEqual(abs(-xb_1), 1.0) self.assertEqual(abs(xb_2), 5.0) From 76e87196e1653c3a56c0a39191172111e187806b Mon Sep 17 00:00:00 2001 From: Christopher Zachow Date: Thu, 20 Jan 2022 08:24:04 +0100 Subject: [PATCH 11/11] added a releasenote to document change --- .../add-abs-to-parameterexpression-347ffef62946b38b.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 releasenotes/notes/add-abs-to-parameterexpression-347ffef62946b38b.yaml diff --git a/releasenotes/notes/add-abs-to-parameterexpression-347ffef62946b38b.yaml b/releasenotes/notes/add-abs-to-parameterexpression-347ffef62946b38b.yaml new file mode 100644 index 000000000000..8106134e445b --- /dev/null +++ b/releasenotes/notes/add-abs-to-parameterexpression-347ffef62946b38b.yaml @@ -0,0 +1,4 @@ + +issues: + - | + Resolves issue #6518 by implementing an '__abs__' function for the class ParameterExpression. \ No newline at end of file