add abs method to ParameterExpression (#9309)

* added abs method to parameterexpression

* fixed wrong call in abs function

* changed definition of abs to __abs__

* additionally implemented __le__, __ge__, __lt__, __gt__ operators

* fixed requested features and implemented more tests for __lt__, __gt__

* fixed lint errors in parameterexpression and its tests

* fixed formatting error in docstrings

* removed __lt__ and friends, fixed recommendations

* added more tests for abs function

* fixed lint

* added a releasenote to document change

* mod function & add more tests

* add new line at the end of file

* fix lint

* add alias of __abs__ for doc

* mod test_compile_with_ufunc

* add test

* fix test

* fix test

* fix test

* fix test

* fix test

* Update test_parameters.py

* Update releasenotes/notes/add-abs-to-parameterexpression-347ffef62946b38b.yaml

Co-authored-by: Julien Gacon <gaconju@gmail.com>

---------

Co-authored-by: Christopher Zachow <christopher.zachow@sva.de>
Co-authored-by: Julien Gacon <gaconju@gmail.com>
This commit is contained in:
Kazuki Tsuoka 2023-05-17 18:41:48 +09:00 committed by GitHub
parent a45a787ac8
commit 1d844eccae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 10 deletions

View File

@ -306,9 +306,9 @@ Parametric Quantum Circuits
.. autosummary::
:toctree: ../stubs/
Parameter
ParameterVector
ParameterExpression
Parameter
ParameterVector
ParameterExpression
Random Circuits
---------------

View File

@ -498,6 +498,21 @@ class ParameterExpression:
def __deepcopy__(self, memo=None):
return self
def __abs__(self):
"""Absolute of a ParameterExpression"""
if _optionals.HAS_SYMENGINE:
import symengine
return self._call(symengine.Abs)
else:
from sympy import Abs as _abs
return self._call(_abs)
def abs(self):
"""Absolute of a ParameterExpression"""
return self.__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).

View File

@ -0,0 +1,14 @@
issues:
- |
Added support for taking absolute values of :class:`.ParameterExpression`\s. For example,
the following is now possible::
from qiskit.circuit import QuantumCircuit, Parameter
x = Parameter("x")
circuit = QuantumCircuit(1)
circuit.rx(abs(x), 0)
bound = circuit.bind_parameters({x: -1})

View File

@ -1113,6 +1113,7 @@ class TestParameters(QiskitTestCase):
theta = Parameter(name="theta")
qc = QuantumCircuit(2)
qc.p(numpy.abs(-phi), 0)
qc.p(numpy.cos(phi), 0)
qc.p(numpy.sin(phi), 0)
qc.p(numpy.tan(phi), 0)
@ -1123,6 +1124,7 @@ class TestParameters(QiskitTestCase):
qc.assign_parameters({phi: pi, theta: 1}, inplace=True)
qc_ref = QuantumCircuit(2)
qc_ref.p(pi, 0)
qc_ref.p(-1, 0)
qc_ref.p(0, 0)
qc_ref.p(0, 0)
@ -1133,14 +1135,40 @@ class TestParameters(QiskitTestCase):
self.assertEqual(qc, qc_ref)
def test_compile_with_ufunc(self):
"""Test compiling of circuit with unbounded parameters
"""Test compiling of circuit with unbound parameters
after we apply universal functions."""
phi = Parameter("phi")
qc = QuantumCircuit(1)
qc.rx(numpy.cos(phi), 0)
backend = BasicAer.get_backend("qasm_simulator")
qc_aer = transpile(qc, backend)
self.assertIn(phi, qc_aer.parameters)
from math import pi
theta = ParameterVector("theta", length=7)
qc = QuantumCircuit(7)
qc.rx(numpy.abs(theta[0]), 0)
qc.rx(numpy.cos(theta[1]), 1)
qc.rx(numpy.sin(theta[2]), 2)
qc.rx(numpy.tan(theta[3]), 3)
qc.rx(numpy.arccos(theta[4]), 4)
qc.rx(numpy.arctan(theta[5]), 5)
qc.rx(numpy.arcsin(theta[6]), 6)
# transpile to different basis
transpiled = transpile(qc, basis_gates=["rz", "sx", "x", "cx"], optimization_level=0)
for x in theta:
self.assertIn(x, transpiled.parameters)
bound = transpiled.bind_parameters({theta: [-1, pi, pi, pi, 1, 1, 1]})
expected = QuantumCircuit(7)
expected.rx(1.0, 0)
expected.rx(-1.0, 1)
expected.rx(0.0, 2)
expected.rx(0.0, 3)
expected.rx(0.0, 4)
expected.rx(pi / 4, 5)
expected.rx(pi / 2, 6)
expected = transpile(expected, basis_gates=["rz", "sx", "x", "cx"], optimization_level=0)
self.assertEqual(expected, bound)
def test_parametervector_resize(self):
"""Test the resize method of the parameter vector."""
@ -1207,6 +1235,29 @@ class TestParameterExpressions(QiskitTestCase):
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
abs functions when bound."""
x = Parameter("x")
xb_1 = x.bind({x: 2.0})
xb_2 = x.bind({x: 3.0 + 4.0j})
self.assertEqual(abs(xb_1), 2.0)
self.assertEqual(abs(-xb_1), 2.0)
self.assertEqual(abs(xb_2), 5.0)
def test_abs_function_when_not_bound(self):
"""Verify expression can be used with
abs functions when not bound."""
x = Parameter("x")
y = Parameter("y")
self.assertEqual(abs(x), abs(-x))
self.assertEqual(abs(x) * abs(y), abs(x * y))
self.assertEqual(abs(x) / abs(y), abs(x / y))
def test_cast_to_float_when_bound(self):
"""Verify expression can be cast to a float when fully bound."""