From c5ab2178804b7e2dfce61847a017b26317cd5ba0 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Tue, 30 May 2023 16:12:01 -0400 Subject: [PATCH 01/17] Assign circuit parameters as int/float to instructions --- qiskit/circuit/quantumcircuit.py | 13 +++++++++++-- ...arameter-to-concrete-value-7cad75c97183257f.yaml | 13 +++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 98e056c2a2a8..48fa0313fc4c 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2854,7 +2854,13 @@ def _assign_parameter(self, parameter: Parameter, value: ParameterValueType) -> new_param = assignee.assign(parameter, value) # if fully bound, validate if len(new_param.parameters) == 0: - instr.params[param_index] = instr.validate_parameter(new_param) + if new_param._symbol_expr.is_integer and new_param.is_real(): + val = int(new_param) + elif new_param.is_real(): + val = float(new_param) + else: + val = complex(new_param) + instr.params[param_index] = instr.validate_parameter(val) else: instr.params[param_index] = new_param @@ -2911,7 +2917,10 @@ def _assign_calibration_parameters( if isinstance(p, ParameterExpression) and parameter in p.parameters: new_param = p.assign(parameter, value) if not new_param.parameters: - new_param = float(new_param) + if new_param._symbol_expr.is_integer: + new_param = int(new_param) + else: + new_param = float(new_param) new_cal_params.append(new_param) else: new_cal_params.append(p) diff --git a/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml new file mode 100644 index 000000000000..38d3aab8581c --- /dev/null +++ b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml @@ -0,0 +1,13 @@ +--- +fixes: + - | + Changed :meth:`~qiskit.circuit.QuantumCircuit.assign_parameters` to bind + assigned integer and float values directly into the parameters of + :class:`~qiskit.circuit.Instruction` instances in the circuit rather than + binding those values wrapped within a :class:`~qiskit.circuit.Parameter`. + Binding the value directly avoids mismatches between the values of circuit + instruction parameters and corresponding paramter keys in the circuit's + calibration dictionary which were already being cast from + :class:`~qiskit.circuit.Parameter` to ``float``. Fixes `#9764 + `_ and `#10166 + `_ From 80a1fe087d20096d045a80d1307d8d225d08504d Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Tue, 30 May 2023 17:02:17 -0400 Subject: [PATCH 02/17] Do not test that fully bound parameters are still ParameterExpressions --- test/python/circuit/test_parameters.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index e19fbd205166..360e3350f21d 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -404,7 +404,6 @@ def test_expression_partial_binding(self): fbqc = getattr(pqc, assign_fun)({phi: 1}) self.assertEqual(fbqc.parameters, set()) - self.assertTrue(isinstance(fbqc.data[0].operation.params[0], ParameterExpression)) self.assertEqual(float(fbqc.data[0].operation.params[0]), 3) def test_two_parameter_expression_binding(self): @@ -448,7 +447,6 @@ def test_expression_partial_binding_zero(self): fbqc = getattr(pqc, assign_fun)({phi: 1}) self.assertEqual(fbqc.parameters, set()) - self.assertTrue(isinstance(fbqc.data[0].operation.params[0], ParameterExpression)) self.assertEqual(float(fbqc.data[0].operation.params[0]), 0) def test_raise_if_assigning_params_not_in_circuit(self): @@ -802,7 +800,6 @@ def test_binding_parameterized_circuits_built_in_multiproc(self): self.assertTrue( all( len(inst.params) == 1 - and isinstance(inst.params[0], ParameterExpression) and float(inst.params[0]) == 1 for inst in qobj.experiments[0].instructions ) From d6f9ee6ee7c7d2c32d0ff941cdb5f7ac053a9a4c Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Tue, 30 May 2023 17:26:56 -0400 Subject: [PATCH 03/17] Change int to float in qasm output pi_check casts integers to floats but not integers inside ParameterExpressions. --- test/python/qasm3/test_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/qasm3/test_export.py b/test/python/qasm3/test_export.py index 2f3030ca3ce6..9978426e6d9e 100644 --- a/test/python/qasm3/test_export.py +++ b/test/python/qasm3/test_export.py @@ -469,7 +469,7 @@ def test_reused_custom_parameter(self): " rx(0.5) _gate_q_0;", "}", f"gate {circuit_name_1} _gate_q_0 {{", - " rx(1) _gate_q_0;", + " rx(1.0) _gate_q_0;", "}", "qubit[1] _all_qubits;", "let q = _all_qubits[0:0];", From 27681c45a41e731b4584189feb6ea50b8e77d9cb Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Tue, 30 May 2023 20:49:13 -0400 Subject: [PATCH 04/17] Workaround symengine ComplexDouble not supporting float --- qiskit/circuit/quantumcircuit.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 48fa0313fc4c..26f679e8dc25 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2857,7 +2857,8 @@ def _assign_parameter(self, parameter: Parameter, value: ParameterValueType) -> if new_param._symbol_expr.is_integer and new_param.is_real(): val = int(new_param) elif new_param.is_real(): - val = float(new_param) + # Workaround symengine not supporting float() + val = complex(new_param).real else: val = complex(new_param) instr.params[param_index] = instr.validate_parameter(val) @@ -2920,7 +2921,8 @@ def _assign_calibration_parameters( if new_param._symbol_expr.is_integer: new_param = int(new_param) else: - new_param = float(new_param) + # Workaround symengine not supporting float() + new_param = complex(new_param).real new_cal_params.append(new_param) else: new_cal_params.append(p) From 84cc95259441ec7c681f486d2e91bef772076965 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 31 May 2023 00:02:46 -0400 Subject: [PATCH 05/17] black --- test/python/circuit/test_parameters.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 360e3350f21d..9b7ab8c73282 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -799,8 +799,7 @@ def test_binding_parameterized_circuits_built_in_multiproc(self): self.assertEqual(len(qobj.experiments[0].instructions), 4) self.assertTrue( all( - len(inst.params) == 1 - and float(inst.params[0]) == 1 + len(inst.params) == 1 and float(inst.params[0]) == 1 for inst in qobj.experiments[0].instructions ) ) From c58e0949312daf9aab6e5d1e4b5c45ea3fb830f3 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 31 May 2023 14:08:11 -0400 Subject: [PATCH 06/17] Update releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml Co-authored-by: Jake Lishman --- ...uit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml index 38d3aab8581c..ca799b805e84 100644 --- a/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml +++ b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml @@ -1,7 +1,7 @@ --- fixes: - | - Changed :meth:`~qiskit.circuit.QuantumCircuit.assign_parameters` to bind + Changed :meth:`.QuantumCircuit.assign_parameters` to bind assigned integer and float values directly into the parameters of :class:`~qiskit.circuit.Instruction` instances in the circuit rather than binding those values wrapped within a :class:`~qiskit.circuit.Parameter`. From 3901e768b9c4cb8e7f34edf8444a512f35bbe808 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 31 May 2023 14:08:23 -0400 Subject: [PATCH 07/17] Update releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml Co-authored-by: Jake Lishman --- ...uit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml index ca799b805e84..a8ef648e5981 100644 --- a/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml +++ b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml @@ -6,7 +6,7 @@ fixes: :class:`~qiskit.circuit.Instruction` instances in the circuit rather than binding those values wrapped within a :class:`~qiskit.circuit.Parameter`. Binding the value directly avoids mismatches between the values of circuit - instruction parameters and corresponding paramter keys in the circuit's + instruction parameters and corresponding parameter keys in the circuit's calibration dictionary which were already being cast from :class:`~qiskit.circuit.Parameter` to ``float``. Fixes `#9764 `_ and `#10166 From 9d1a9ae2771e37d860106ae0734590849314e790 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 31 May 2023 15:46:25 -0400 Subject: [PATCH 08/17] Restore assigned parameter value type check to tests --- test/python/circuit/test_parameters.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 9b7ab8c73282..7da051850d33 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -401,9 +401,10 @@ def test_expression_partial_binding(self): self.assertTrue(isinstance(pqc.data[0].operation.params[0], ParameterExpression)) self.assertEqual(str(pqc.data[0].operation.params[0]), "phi + 2") - fbqc = getattr(pqc, assign_fun)({phi: 1}) + fbqc = getattr(pqc, assign_fun)({phi: 1.0}) self.assertEqual(fbqc.parameters, set()) + self.assertTrue(isinstance(fbqc.data[0].operation.params[0], float)) self.assertEqual(float(fbqc.data[0].operation.params[0]), 3) def test_two_parameter_expression_binding(self): @@ -447,6 +448,7 @@ def test_expression_partial_binding_zero(self): fbqc = getattr(pqc, assign_fun)({phi: 1}) self.assertEqual(fbqc.parameters, set()) + self.assertTrue(isinstance(fbqc.data[0].operation.params[0], int)) self.assertEqual(float(fbqc.data[0].operation.params[0]), 0) def test_raise_if_assigning_params_not_in_circuit(self): @@ -787,7 +789,7 @@ def test_binding_parameterized_circuits_built_in_multiproc(self): for qc in results: circuit.compose(qc, inplace=True) - parameter_values = [{x: 1 for x in parameters}] + parameter_values = [{x: 1.0 for x in parameters}] qobj = assemble( circuit, @@ -799,7 +801,9 @@ def test_binding_parameterized_circuits_built_in_multiproc(self): self.assertEqual(len(qobj.experiments[0].instructions), 4) self.assertTrue( all( - len(inst.params) == 1 and float(inst.params[0]) == 1 + len(inst.params) == 1 + and isinstance(inst.params[0], float) + and float(inst.params[0]) == 1 for inst in qobj.experiments[0].instructions ) ) From 75529496a95a16a8e2e75d1fe4ef1180be802412 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 31 May 2023 15:46:54 -0400 Subject: [PATCH 09/17] Add test to check type and value of simple circuit parameter assignment --- test/python/circuit/test_parameters.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 7da051850d33..908865938e14 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -21,7 +21,7 @@ from test import combine import numpy -from ddt import data, ddt +from ddt import data, ddt, named_data import qiskit import qiskit.circuit.library as circlib @@ -346,6 +346,24 @@ def test_multiple_named_parameters(self): self.assertEqual(theta.name, "θ") self.assertEqual(qc.parameters, {theta, x}) + @named_data( + ["int", 2, int], + ["float", 2.1, float], + ["float16", numpy.float16(2.1), float], + ["float32", numpy.float32(2.1), float], + ["float64", numpy.float64(2.1), float], + ["complex", 1 + 2j, complex], + ) + def test_circuit_assignment_to_numeric(self, value, type_): + """Test binding a numeric value to a circuit instruction""" + x = Parameter("x") + qc = QuantumCircuit(1) + qc.append(Instruction("inst", 1, 0, [x]), (0,)) + qc.assign_parameters({x: value}, inplace=True) + bound = qc.data[0].operation.params[0] + self.assertIsInstance(bound, type_) + self.assertAlmostEqual(bound, value, delta=1e-4) + def test_partial_binding(self): """Test that binding a subset of circuit parameters returns a new parameterized circuit.""" theta = Parameter("θ") From 56a9955d35196a177c9bb5680c1f4688fbe55bf0 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 31 May 2023 16:07:37 -0400 Subject: [PATCH 10/17] Add consistency check between assigned instruction data and calibrations dict keys --- test/python/circuit/test_parameters.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 908865938e14..fd2fec3d20a5 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -523,8 +523,15 @@ def test_calibration_assignment(self): circ.add_calibration("rxt", [0], rxt_q0, [theta]) circ = circ.assign_parameters({theta: 3.14}) - self.assertTrue(((0,), (3.14,)) in circ.calibrations["rxt"]) - sched = circ.calibrations["rxt"][((0,), (3.14,))] + instruction = circ.data[0] + cal_key = ( + tuple(circ.find_bit(q).index for q in instruction.qubits), + tuple(instruction.operation.params), + ) + self.assertEqual(cal_key, ((0,), (3.14,))) + # Make sure that key from instruction data matches the calibrations dictionary + self.assertTrue(cal_key in circ.calibrations["rxt"]) + sched = circ.calibrations["rxt"][cal_key] self.assertEqual(sched.instructions[0][1].pulse.amp, 0.2) def test_calibration_assignment_doesnt_mutate(self): @@ -549,11 +556,11 @@ def test_calibration_assignment_doesnt_mutate(self): self.assertNotEqual(assigned_circ.calibrations, circ.calibrations) def test_calibration_assignment_w_expressions(self): - """That calibrations with multiple parameters and more expressions.""" + """That calibrations with multiple parameters are assigned correctly""" theta = Parameter("theta") sigma = Parameter("sigma") circ = QuantumCircuit(3, 3) - circ.append(Gate("rxt", 1, [theta, sigma]), [0]) + circ.append(Gate("rxt", 1, [theta / 2, sigma]), [0]) circ.measure(0, 0) rxt_q0 = pulse.Schedule( @@ -566,8 +573,15 @@ def test_calibration_assignment_w_expressions(self): circ.add_calibration("rxt", [0], rxt_q0, [theta / 2, sigma]) circ = circ.assign_parameters({theta: 3.14, sigma: 4}) - self.assertTrue(((0,), (3.14 / 2, 4)) in circ.calibrations["rxt"]) - sched = circ.calibrations["rxt"][((0,), (3.14 / 2, 4))] + instruction = circ.data[0] + cal_key = ( + tuple(circ.find_bit(q).index for q in instruction.qubits), + tuple(instruction.operation.params), + ) + self.assertEqual(cal_key, ((0,), (3.14 / 2, 4))) + # Make sure that key from instruction data matches the calibrations dictionary + self.assertTrue(cal_key in circ.calibrations["rxt"]) + sched = circ.calibrations["rxt"][cal_key] self.assertEqual(sched.instructions[0][1].pulse.amp, 0.2) self.assertEqual(sched.instructions[0][1].pulse.sigma, 16) From 715decadf6d48f1ec6ca3fe59f9335af6599d21b Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 31 May 2023 16:26:08 -0400 Subject: [PATCH 11/17] Add regression test --- .../circuit/test_circuit_load_from_qpy.py | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 3bedc5cb9da0..9dfc01a36767 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -19,7 +19,7 @@ import numpy as np -from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister +from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, pulse from qiskit.circuit import CASE_DEFAULT from qiskit.circuit.classicalregister import Clbit from qiskit.circuit.quantumregister import Qubit @@ -274,6 +274,39 @@ def test_bound_parameter(self): self.assertEqual(qc, new_circ) self.assertDeprecatedBitProperties(qc, new_circ) + def test_bound_calibration_parameter(self): + """Test a circuit with a bound calibration parameter is correctly serialized. + + In particular, this test ensures that parameters on a circuit + instruction are consistent with the circuit's calibrations dictionary + after serialization. + """ + amp = Parameter("amp") + + with pulse.builder.build() as sched: + pulse.builder.play(pulse.Constant(100, amp), pulse.DriveChannel(0)) + + gate = Gate("custom", 1, [amp]) + + qc = QuantumCircuit(1) + qc.append(gate, (0,)) + qc.add_calibration(gate, (0,), sched) + qc.assign_parameters({amp: 1/3}, inplace=True) + + qpy_file = io.BytesIO() + dump(qc, qpy_file) + qpy_file.seek(0) + new_circ = load(qpy_file)[0] + self.assertEqual(qc, new_circ) + instruction = new_circ.data[0] + cal_key = ( + tuple(new_circ.find_bit(q).index for q in instruction.qubits), + tuple(instruction.operation.params), + ) + # Make sure that looking for a calibration based on the instruction's + # parameters succeeds + self.assertTrue(cal_key in new_circ.calibrations[gate.name]) + def test_parameter_expression(self): """Test a circuit with a parameter expression.""" theta = Parameter("theta") From 03189be703a1d8891da20175b70e73ce72940264 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Thu, 1 Jun 2023 00:19:18 -0400 Subject: [PATCH 12/17] Add upgrade note --- ...er-to-concrete-value-7cad75c97183257f.yaml | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml index a8ef648e5981..036d672e480d 100644 --- a/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml +++ b/releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml @@ -1,13 +1,29 @@ --- fixes: + - | + Changed the binding of numeric values with + :meth:`.QuantumCircuit.assign_parameters` to avoid a mismatch between the + values of circuit instruction parameters and corresponding parameter keys + in the circuit's calibration dictionary. Fixed `#9764 + `_ and `#10166 + `_. See also the + related upgrade note regarding :meth:`.QuantumCircuit.assign_parameters`. +upgrade: - | Changed :meth:`.QuantumCircuit.assign_parameters` to bind assigned integer and float values directly into the parameters of :class:`~qiskit.circuit.Instruction` instances in the circuit rather than - binding those values wrapped within a :class:`~qiskit.circuit.Parameter`. - Binding the value directly avoids mismatches between the values of circuit - instruction parameters and corresponding parameter keys in the circuit's - calibration dictionary which were already being cast from - :class:`~qiskit.circuit.Parameter` to ``float``. Fixes `#9764 - `_ and `#10166 - `_ + binding the values wrapped within a + :class:`~qiskit.circuit.ParameterExpression`. This change should have + little user impact as ``float(QuantumCircuit.data[i].operation.params[j])`` + still produces a ``float`` (and is the only way to access the value of a + :class:`~qiskit.circuit.ParameterExpression`). Also, + :meth:`~qiskit.circuit.Instruction` parameters could already be ``float`` + as well as a :class:`~qiskit.circuit.ParameterExpression`, so code dealing + with instruction parameters should already handle both cases. The most + likely chance for user impact is in code that uses ``isinstance`` to check + for :class:`~qiskit.circuit.ParameterExpression` and behaves differently + depending on the result. Additionally, qpy serializes the numeric value in + a bound :class:`~qiskit.circuit.ParameterExpression` at a different + precision than a ``float`` (see also the related bug fix note about + :meth:`.QuantumCircuit.assign_parameters`). From 92a0b80cd219487b5d30248c659224a590c7f378 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Thu, 1 Jun 2023 00:21:12 -0400 Subject: [PATCH 13/17] Remove support for complex instruction parameter assignment --- qiskit/circuit/quantumcircuit.py | 4 +--- test/python/circuit/test_parameters.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 26f679e8dc25..4063a0290e49 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2856,11 +2856,9 @@ def _assign_parameter(self, parameter: Parameter, value: ParameterValueType) -> if len(new_param.parameters) == 0: if new_param._symbol_expr.is_integer and new_param.is_real(): val = int(new_param) - elif new_param.is_real(): + else: # Workaround symengine not supporting float() val = complex(new_param).real - else: - val = complex(new_param) instr.params[param_index] = instr.validate_parameter(val) else: instr.params[param_index] = new_param diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index fd2fec3d20a5..09acb429ad51 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -352,7 +352,6 @@ def test_multiple_named_parameters(self): ["float16", numpy.float16(2.1), float], ["float32", numpy.float32(2.1), float], ["float64", numpy.float64(2.1), float], - ["complex", 1 + 2j, complex], ) def test_circuit_assignment_to_numeric(self, value, type_): """Test binding a numeric value to a circuit instruction""" From 061cc25338ce1cfbb4308a364ce21529a2775236 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Thu, 1 Jun 2023 08:56:24 -0400 Subject: [PATCH 14/17] Restore complex assignment Complex assignment maybe not be supported but allowing it in the parameter assignment step lets validate_parameters get the value and raise an appropriate exception. --- qiskit/circuit/quantumcircuit.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 4063a0290e49..37cb1c508575 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2856,9 +2856,14 @@ def _assign_parameter(self, parameter: Parameter, value: ParameterValueType) -> if len(new_param.parameters) == 0: if new_param._symbol_expr.is_integer and new_param.is_real(): val = int(new_param) - else: + elif new_param.is_real(): # Workaround symengine not supporting float() val = complex(new_param).real + else: + # complex values may no longer be supported but we + # defer raising an exception to validdate_parameter + # below for now. + val = complex(new_param) instr.params[param_index] = instr.validate_parameter(val) else: instr.params[param_index] = new_param From c37770e53f3ed1677815555fc81a3c1bf1e32ee4 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Thu, 1 Jun 2023 08:57:55 -0400 Subject: [PATCH 15/17] black --- test/python/circuit/test_circuit_load_from_qpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 9dfc01a36767..476a3ec956db 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -291,7 +291,7 @@ def test_bound_calibration_parameter(self): qc = QuantumCircuit(1) qc.append(gate, (0,)) qc.add_calibration(gate, (0,), sched) - qc.assign_parameters({amp: 1/3}, inplace=True) + qc.assign_parameters({amp: 1 / 3}, inplace=True) qpy_file = io.BytesIO() dump(qc, qpy_file) From f00fb4036515fcf1464907b3c8e4d2c289dd306d Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 1 Jun 2023 18:18:10 +0100 Subject: [PATCH 16/17] More specific assertion methods --- test/python/circuit/test_circuit_load_from_qpy.py | 2 +- test/python/circuit/test_parameters.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 476a3ec956db..331c30471032 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -305,7 +305,7 @@ def test_bound_calibration_parameter(self): ) # Make sure that looking for a calibration based on the instruction's # parameters succeeds - self.assertTrue(cal_key in new_circ.calibrations[gate.name]) + self.assertIn(cal_key, new_circ.calibrations[gate.name]) def test_parameter_expression(self): """Test a circuit with a parameter expression.""" diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index 09acb429ad51..a6d7ab79fe79 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -421,7 +421,7 @@ def test_expression_partial_binding(self): fbqc = getattr(pqc, assign_fun)({phi: 1.0}) self.assertEqual(fbqc.parameters, set()) - self.assertTrue(isinstance(fbqc.data[0].operation.params[0], float)) + self.assertIsInstance(fbqc.data[0].operation.params[0], float) self.assertEqual(float(fbqc.data[0].operation.params[0]), 3) def test_two_parameter_expression_binding(self): @@ -465,7 +465,7 @@ def test_expression_partial_binding_zero(self): fbqc = getattr(pqc, assign_fun)({phi: 1}) self.assertEqual(fbqc.parameters, set()) - self.assertTrue(isinstance(fbqc.data[0].operation.params[0], int)) + self.assertIsInstance(fbqc.data[0].operation.params[0], int) self.assertEqual(float(fbqc.data[0].operation.params[0]), 0) def test_raise_if_assigning_params_not_in_circuit(self): @@ -529,7 +529,7 @@ def test_calibration_assignment(self): ) self.assertEqual(cal_key, ((0,), (3.14,))) # Make sure that key from instruction data matches the calibrations dictionary - self.assertTrue(cal_key in circ.calibrations["rxt"]) + self.assertIn(cal_key, circ.calibrations["rxt"]) sched = circ.calibrations["rxt"][cal_key] self.assertEqual(sched.instructions[0][1].pulse.amp, 0.2) @@ -579,7 +579,7 @@ def test_calibration_assignment_w_expressions(self): ) self.assertEqual(cal_key, ((0,), (3.14 / 2, 4))) # Make sure that key from instruction data matches the calibrations dictionary - self.assertTrue(cal_key in circ.calibrations["rxt"]) + self.assertIn(cal_key, circ.calibrations["rxt"]) sched = circ.calibrations["rxt"][cal_key] self.assertEqual(sched.instructions[0][1].pulse.amp, 0.2) self.assertEqual(sched.instructions[0][1].pulse.sigma, 16) From 1c1882505c53cb19b23c6c4570ba651ff2573d45 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Thu, 1 Jun 2023 18:27:43 +0100 Subject: [PATCH 17/17] Use exact floating-point check --- test/python/circuit/test_parameters.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index a6d7ab79fe79..d610a3353067 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -348,10 +348,10 @@ def test_multiple_named_parameters(self): @named_data( ["int", 2, int], - ["float", 2.1, float], - ["float16", numpy.float16(2.1), float], - ["float32", numpy.float32(2.1), float], - ["float64", numpy.float64(2.1), float], + ["float", 2.5, float], + ["float16", numpy.float16(2.5), float], + ["float32", numpy.float32(2.5), float], + ["float64", numpy.float64(2.5), float], ) def test_circuit_assignment_to_numeric(self, value, type_): """Test binding a numeric value to a circuit instruction""" @@ -361,7 +361,7 @@ def test_circuit_assignment_to_numeric(self, value, type_): qc.assign_parameters({x: value}, inplace=True) bound = qc.data[0].operation.params[0] self.assertIsInstance(bound, type_) - self.assertAlmostEqual(bound, value, delta=1e-4) + self.assertEqual(bound, value) def test_partial_binding(self): """Test that binding a subset of circuit parameters returns a new parameterized circuit."""