From d547ab2854475a66ff65422e0cede825ece326a7 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Mon, 3 Aug 2020 19:25:15 -0400 Subject: [PATCH 01/14] First commit adding _calibrations dict --- qiskit/circuit/exceptions.py | 5 +++++ qiskit/circuit/quantumcircuit.py | 23 +++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/qiskit/circuit/exceptions.py b/qiskit/circuit/exceptions.py index 81609de968a1..11c9c0754c3e 100644 --- a/qiskit/circuit/exceptions.py +++ b/qiskit/circuit/exceptions.py @@ -21,3 +21,8 @@ class CircuitError(QiskitError): """Base class for errors raised while processing a circuit.""" pass + +class CalibrationError(QiskitError): + """Base class for errors raised during waveform level calibrations.""" + + pass diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index e63d96afca19..170e39cd2305 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -20,15 +20,16 @@ import warnings import numbers import multiprocessing as mp -from collections import OrderedDict +from collections import OrderedDict, defaultdict import numpy as np +from typing import Union, List, Tuple from qiskit.exceptions import QiskitError from qiskit.util import is_main_process from qiskit.util import deprecate_arguments from qiskit.circuit.instruction import Instruction from qiskit.circuit.gate import Gate from qiskit.qasm.qasm import Qasm -from qiskit.circuit.exceptions import CircuitError +from qiskit.circuit.exceptions import CircuitError, CalibrationError from .parameterexpression import ParameterExpression from .quantumregister import QuantumRegister, Qubit, AncillaRegister from .classicalregister import ClassicalRegister, Clbit @@ -167,6 +168,7 @@ def __init__(self, *regs, name=None, global_phase=0): self._qubits = [] self._clbits = [] self._ancillas = [] + self._calibrations = defaultdict(dict) self.add_register(*regs) # Parameter table tracks instructions with variable parameters. @@ -2273,6 +2275,23 @@ def cz(self, control_qubit, target_qubit, *, label=None, ctrl_state=None, return self.append(CZGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []) + def add_calibrations(self, gate, qubits, params, schedule): + """Add calibrations information to a calibration dictionary. + + Args: + gate (Union[Gate, str]): Gate information. + qubits (Union[int, Tuple[int]]): Qubits + params (List[Union[float, Parameter]]): Parameters + schedule (Schedule): Schedule + """ + if gate == None or qubits == None or params == None: + raise CalibrationError("One of the parameter is None. gate- {}, qubits- {}, " + "params- {}, schedule- {}".format(gate, qubits, params, + schedule)) + calibrations = dict() + calibrations[tuple(params)] = schedule + self._calibrations[gate.name][tuple(qubits)] = calibrations + def _circuit_from_qasm(qasm): # pylint: disable=cyclic-import From 2a2000b7f45d2df9144a23c703ac00ef594b587b Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Mon, 3 Aug 2020 19:26:25 -0400 Subject: [PATCH 02/14] Remove unwanted imports --- qiskit/circuit/quantumcircuit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 170e39cd2305..8c24dffc05c5 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -22,7 +22,6 @@ import multiprocessing as mp from collections import OrderedDict, defaultdict import numpy as np -from typing import Union, List, Tuple from qiskit.exceptions import QiskitError from qiskit.util import is_main_process from qiskit.util import deprecate_arguments From 850a15871a78e61c63a83296db101be1ff28edc7 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Tue, 4 Aug 2020 14:43:18 -0400 Subject: [PATCH 03/14] more additions --- qiskit/circuit/exceptions.py | 5 ----- qiskit/circuit/quantumcircuit.py | 10 +++++----- qiskit/compiler/assemble.py | 10 ++++++++++ qiskit/compiler/transpile.py | 5 +++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/qiskit/circuit/exceptions.py b/qiskit/circuit/exceptions.py index 11c9c0754c3e..81609de968a1 100644 --- a/qiskit/circuit/exceptions.py +++ b/qiskit/circuit/exceptions.py @@ -21,8 +21,3 @@ class CircuitError(QiskitError): """Base class for errors raised while processing a circuit.""" pass - -class CalibrationError(QiskitError): - """Base class for errors raised during waveform level calibrations.""" - - pass diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 8c24dffc05c5..30d06f89cacc 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -28,7 +28,7 @@ from qiskit.circuit.instruction import Instruction from qiskit.circuit.gate import Gate from qiskit.qasm.qasm import Qasm -from qiskit.circuit.exceptions import CircuitError, CalibrationError +from qiskit.circuit.exceptions import CircuitError from .parameterexpression import ParameterExpression from .quantumregister import QuantumRegister, Qubit, AncillaRegister from .classicalregister import ClassicalRegister, Clbit @@ -190,6 +190,10 @@ def data(self): """ return QuantumCircuitData(self) + def _get_calibrations(self): + """Return calibration dictionary.""" + return dict(self._calibrations) + @data.setter def data(self, data_input): """Sets the circuit data from a list of instructions and context. @@ -2283,10 +2287,6 @@ def add_calibrations(self, gate, qubits, params, schedule): params (List[Union[float, Parameter]]): Parameters schedule (Schedule): Schedule """ - if gate == None or qubits == None or params == None: - raise CalibrationError("One of the parameter is None. gate- {}, qubits- {}, " - "params- {}, schedule- {}".format(gate, qubits, params, - schedule)) calibrations = dict() calibrations[tuple(params)] = schedule self._calibrations[gate.name][tuple(qubits)] = calibrations diff --git a/qiskit/compiler/assemble.py b/qiskit/compiler/assemble.py index df8c45be833d..b04eca35928e 100644 --- a/qiskit/compiler/assemble.py +++ b/qiskit/compiler/assemble.py @@ -132,6 +132,7 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], Schedule, Raises: QiskitError: if the input cannot be interpreted as either circuits or schedules + NotImplementedError: if circuit._get_calibrations() is not empty. """ start_time = time() experiments = experiments if isinstance(experiments, list) else [experiments] @@ -142,6 +143,15 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], Schedule, # assemble either circuits or schedules if all(isinstance(exp, QuantumCircuit) for exp in experiments): + # calibrate circuits to schedules (if any) + for exp in experiments: + if len(exp._get_calibrations()) != 0: + # TODO: Do something here to schedule the circuits + # Raise an error - NotImplementedError" or try to schedule by adding + # cals to inst_map and call schedule. + # if scheduled, raise a warning - "Let the user know whats happening" + raise NotImplementedError + run_config = _parse_circuit_args(parameter_binds, **run_config_common_dict) # If circuits are parameterized, bind parameters and remove from run_config diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py index 0bbb0c42db91..b18e56e8b061 100644 --- a/qiskit/compiler/transpile.py +++ b/qiskit/compiler/transpile.py @@ -182,6 +182,11 @@ def callback_func(**kwargs): _log_transpile_time(start_time, end_time) return circuits + for circuit in circuits: + if len(circuit._get_calibrations()) != 0: + # TODO: do something here + pass + if pass_manager is not None: _check_conflicting_argument(optimization_level=optimization_level, basis_gates=basis_gates, coupling_map=coupling_map, seed_transpiler=seed_transpiler, From f7cb3e4775b2a5eab3e1de89edcdf34ca17716df Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Mon, 10 Aug 2020 14:58:46 -0400 Subject: [PATCH 04/14] add unit test and fix some variable names --- qiskit/circuit/quantumcircuit.py | 12 ++++----- .../python/circuit/test_circuit_properties.py | 27 ++++++++++++++++++- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 30d06f89cacc..11d0dd706af3 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -190,7 +190,8 @@ def data(self): """ return QuantumCircuitData(self) - def _get_calibrations(self): + @property + def calibrations(self): """Return calibration dictionary.""" return dict(self._calibrations) @@ -2278,18 +2279,17 @@ def cz(self, control_qubit, target_qubit, *, label=None, ctrl_state=None, return self.append(CZGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []) - def add_calibrations(self, gate, qubits, params, schedule): + def add_calibration(self, gate, qubits, schedule): """Add calibrations information to a calibration dictionary. Args: gate (Union[Gate, str]): Gate information. qubits (Union[int, Tuple[int]]): Qubits - params (List[Union[float, Parameter]]): Parameters schedule (Schedule): Schedule """ - calibrations = dict() - calibrations[tuple(params)] = schedule - self._calibrations[gate.name][tuple(qubits)] = calibrations + calibration = dict() + calibration[tuple(gate.params)] = schedule + self._calibrations[gate.name][tuple(qubits)] = calibration def _circuit_from_qasm(qasm): diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index 52f5eba1f11e..9b70e6e290e5 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -18,7 +18,8 @@ import unittest import numpy as np -from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit +from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, pulse +from qiskit.circuit.library import RXGate, RYGate from qiskit.test import QiskitTestCase from qiskit.circuit.exceptions import CircuitError # pylint: disable=unused-import @@ -616,6 +617,30 @@ def test_num_qubits_multiple_register_circuit(self): circ = QuantumCircuit(q_reg1, q_reg2, q_reg3) self.assertEqual(circ.num_qubits, 18) + def test_calibrations(self): + """Check if the calibrations provided are added correctly.""" + circ = QuantumCircuit(2) + circ.rx(3.14, 0) + circ.ry(1.57, 1) + + with pulse.build() as q0_x180: + pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0)) + with pulse.build() as q1_y90: + pulse.play(pulse.library.Gaussian(20, -1.0, 3.0), pulse.DriveChannel(1)) + + # Add calibration + circ.add_calibration(RXGate(3.14), [0], q0_x180) + circ.add_calibration(RYGate(1.57), [1], q1_y90) + + self.assertEqual(set(circ.calibrations.keys()), {'rx', 'ry'}) + self.assertEqual(set(circ.calibrations['rx'].keys()), {(0,)}) + self.assertEqual(set(circ.calibrations['ry'].keys()), {(1,)}) + self.assertEqual(set(circ.calibrations['rx'][(0,)].keys()), {(3.14,)}) + self.assertEqual(set(circ.calibrations['ry'][(1,)].keys()), {(1.57,)}) + self.assertEqual(circ.calibrations['rx'][(0,)][(3.14,)].instructions, + q0_x180.instructions) + self.assertEqual(circ.calibrations['ry'][(1,)][(1.57,)].instructions, + q1_y90.instructions) if __name__ == '__main__': unittest.main() From b32f24972e1f929bd57ae71335b8452c87f25bbd Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Mon, 10 Aug 2020 15:02:03 -0400 Subject: [PATCH 05/14] replace _get_calibrations() to calibrations --- qiskit/compiler/assemble.py | 4 ++-- qiskit/compiler/transpile.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/compiler/assemble.py b/qiskit/compiler/assemble.py index b04eca35928e..59430a64a915 100644 --- a/qiskit/compiler/assemble.py +++ b/qiskit/compiler/assemble.py @@ -132,7 +132,7 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], Schedule, Raises: QiskitError: if the input cannot be interpreted as either circuits or schedules - NotImplementedError: if circuit._get_calibrations() is not empty. + NotImplementedError: if circuit.calibrations is not empty. """ start_time = time() experiments = experiments if isinstance(experiments, list) else [experiments] @@ -145,7 +145,7 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], Schedule, if all(isinstance(exp, QuantumCircuit) for exp in experiments): # calibrate circuits to schedules (if any) for exp in experiments: - if len(exp._get_calibrations()) != 0: + if len(exp.calibrations) != 0: # TODO: Do something here to schedule the circuits # Raise an error - NotImplementedError" or try to schedule by adding # cals to inst_map and call schedule. diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py index b18e56e8b061..fa61c6fc9ac2 100644 --- a/qiskit/compiler/transpile.py +++ b/qiskit/compiler/transpile.py @@ -183,7 +183,7 @@ def callback_func(**kwargs): return circuits for circuit in circuits: - if len(circuit._get_calibrations()) != 0: + if len(circuit.calibrations) != 0: # TODO: do something here pass From 4afe0816458a4491d856ffef8de40e66f81f0520 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Wed, 12 Aug 2020 13:20:20 -0400 Subject: [PATCH 06/14] review comments --- qiskit/circuit/quantumcircuit.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 11d0dd706af3..175acf5279f1 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2279,17 +2279,24 @@ def cz(self, control_qubit, target_qubit, *, label=None, ctrl_state=None, return self.append(CZGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], []) - def add_calibration(self, gate, qubits, schedule): - """Add calibrations information to a calibration dictionary. + def add_calibration(self, gate, qubits, schedule, params=None): + """Register a low-level, custom pulse definition for the given gate. Args: gate (Union[Gate, str]): Gate information. - qubits (Union[int, Tuple[int]]): Qubits - schedule (Schedule): Schedule + qubits (Union[int, Tuple[int]]): List of qubits to be measured. + schedule (Schedule): Schedule information. + params (Optional[List[Union[float, Parameter]]]): A list of parameters. """ - calibration = dict() - calibration[tuple(gate.params)] = schedule - self._calibrations[gate.name][tuple(qubits)] = calibration + if isinstance(gate, Gate): + self._calibrations[gate.name] = {} + self._calibrations[gate.name][tuple(qubits)] = {tuple(gate.params): schedule} + elif isinstance(gate, str) and params != None: + self._calibrations[gate] = {} + self._calibrations[gate][tuple(qubits)] = {tuple(params):schedule} + elif isinstance(gate, str) and params == None: + raise Exception("Params for the gate {} is not " + "specified".format(gate)) def _circuit_from_qasm(qasm): From 675af9c3f2d4af83579472c1320ab18a2e47f83e Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Thu, 13 Aug 2020 08:43:09 -0400 Subject: [PATCH 07/14] Review comments --- qiskit/circuit/quantumcircuit.py | 14 ++++++++++---- test/python/circuit/test_circuit_properties.py | 11 +++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 175acf5279f1..8160e8e49b84 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2282,19 +2282,25 @@ def cz(self, control_qubit, target_qubit, *, label=None, ctrl_state=None, def add_calibration(self, gate, qubits, schedule, params=None): """Register a low-level, custom pulse definition for the given gate. + The custom pulse definition of a given gate is of the form + {'gate_name': {(qubits, params): schedule}} + Args: gate (Union[Gate, str]): Gate information. qubits (Union[int, Tuple[int]]): List of qubits to be measured. schedule (Schedule): Schedule information. params (Optional[List[Union[float, Parameter]]]): A list of parameters. + + Raises: + Exception: if the gate is of type string and params is None. """ if isinstance(gate, Gate): self._calibrations[gate.name] = {} - self._calibrations[gate.name][tuple(qubits)] = {tuple(gate.params): schedule} - elif isinstance(gate, str) and params != None: + self._calibrations[gate.name][(tuple(qubits), tuple(gate.params))] = schedule + elif isinstance(gate, str) and params is not None: self._calibrations[gate] = {} - self._calibrations[gate][tuple(qubits)] = {tuple(params):schedule} - elif isinstance(gate, str) and params == None: + self._calibrations[gate][(tuple(qubits), tuple(params))] = schedule + elif isinstance(gate, str) and params is None: raise Exception("Params for the gate {} is not " "specified".format(gate)) diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index 9b70e6e290e5..d152bfcf60c7 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -633,14 +633,13 @@ def test_calibrations(self): circ.add_calibration(RYGate(1.57), [1], q1_y90) self.assertEqual(set(circ.calibrations.keys()), {'rx', 'ry'}) - self.assertEqual(set(circ.calibrations['rx'].keys()), {(0,)}) - self.assertEqual(set(circ.calibrations['ry'].keys()), {(1,)}) - self.assertEqual(set(circ.calibrations['rx'][(0,)].keys()), {(3.14,)}) - self.assertEqual(set(circ.calibrations['ry'][(1,)].keys()), {(1.57,)}) - self.assertEqual(circ.calibrations['rx'][(0,)][(3.14,)].instructions, + self.assertEqual(set(circ.calibrations['rx'].keys()), {((0,), (3.14,))}) + self.assertEqual(set(circ.calibrations['ry'].keys()), {((1,), (1.57,))}) + self.assertEqual(circ.calibrations['rx'][((0,), (3.14,))].instructions, q0_x180.instructions) - self.assertEqual(circ.calibrations['ry'][(1,)][(1.57,)].instructions, + self.assertEqual(circ.calibrations['ry'][((1,), (1.57,))].instructions, q1_y90.instructions) + if __name__ == '__main__': unittest.main() From 54fe95df3025b4b37e433e55d45e6ca21a3c5b02 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Thu, 13 Aug 2020 08:47:45 -0400 Subject: [PATCH 08/14] remove unwanted lines --- qiskit/circuit/quantumcircuit.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 8160e8e49b84..27cb295b0352 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2295,10 +2295,8 @@ def add_calibration(self, gate, qubits, schedule, params=None): Exception: if the gate is of type string and params is None. """ if isinstance(gate, Gate): - self._calibrations[gate.name] = {} self._calibrations[gate.name][(tuple(qubits), tuple(gate.params))] = schedule elif isinstance(gate, str) and params is not None: - self._calibrations[gate] = {} self._calibrations[gate][(tuple(qubits), tuple(params))] = schedule elif isinstance(gate, str) and params is None: raise Exception("Params for the gate {} is not " From 0e719ebb387a8187fa06d39b7c642d5185dc35cc Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Thu, 13 Aug 2020 15:15:12 -0400 Subject: [PATCH 09/14] few more fixes quantum circuit --- qiskit/circuit/quantumcircuit.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 27cb295b0352..278b181ecbbd 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -192,7 +192,11 @@ def data(self): @property def calibrations(self): - """Return calibration dictionary.""" + """Return calibration dictionary. + + The custom pulse definition of a given gate is of the form + {'gate_name': {(qubits, params): schedule}} + """ return dict(self._calibrations) @data.setter @@ -2282,9 +2286,6 @@ def cz(self, control_qubit, target_qubit, *, label=None, ctrl_state=None, def add_calibration(self, gate, qubits, schedule, params=None): """Register a low-level, custom pulse definition for the given gate. - The custom pulse definition of a given gate is of the form - {'gate_name': {(qubits, params): schedule}} - Args: gate (Union[Gate, str]): Gate information. qubits (Union[int, Tuple[int]]): List of qubits to be measured. @@ -2296,11 +2297,8 @@ def add_calibration(self, gate, qubits, schedule, params=None): """ if isinstance(gate, Gate): self._calibrations[gate.name][(tuple(qubits), tuple(gate.params))] = schedule - elif isinstance(gate, str) and params is not None: + else: self._calibrations[gate][(tuple(qubits), tuple(params))] = schedule - elif isinstance(gate, str) and params is None: - raise Exception("Params for the gate {} is not " - "specified".format(gate)) def _circuit_from_qasm(qasm): From b6f6d58aa284d18242d7c82828055dcdd9c5ca0b Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Fri, 21 Aug 2020 14:34:46 -0400 Subject: [PATCH 10/14] add more tests and user warning during transpilation --- qiskit/compiler/transpile.py | 2 +- .../python/circuit/test_circuit_properties.py | 29 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py index fa61c6fc9ac2..4ec157e9d7dd 100644 --- a/qiskit/compiler/transpile.py +++ b/qiskit/compiler/transpile.py @@ -184,7 +184,7 @@ def callback_func(**kwargs): for circuit in circuits: if len(circuit.calibrations) != 0: - # TODO: do something here + warnings.warn("Transpiling with calibrations aren't supported currently. ") pass if pass_manager is not None: diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index d152bfcf60c7..03a79d8dd755 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -19,6 +19,7 @@ import unittest import numpy as np from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, pulse +from qiskit.circuit import Parameter, Gate from qiskit.circuit.library import RXGate, RYGate from qiskit.test import QiskitTestCase from qiskit.circuit.exceptions import CircuitError @@ -617,8 +618,8 @@ def test_num_qubits_multiple_register_circuit(self): circ = QuantumCircuit(q_reg1, q_reg2, q_reg3) self.assertEqual(circ.num_qubits, 18) - def test_calibrations(self): - """Check if the calibrations provided are added correctly.""" + def test_calibrations_basis_gates(self): + """Check if the calibrations for basis gates provided are added correctly.""" circ = QuantumCircuit(2) circ.rx(3.14, 0) circ.ry(1.57, 1) @@ -640,6 +641,30 @@ def test_calibrations(self): self.assertEqual(circ.calibrations['ry'][((1,), (1.57,))].instructions, q1_y90.instructions) + def test_calibrations_custom_gates(self): + """Check if the calibrations for custom gates with params provided are added correctly.""" + class RxGate(Gate): + def __init__(self, theta): + super().__init__('rxt', 1, [theta]) + + circ = QuantumCircuit(3) + circ.append(RxGate(1.57), [0]) + circ.append(RxGate(1.57), [0]) + + theta = Parameter('theta') + circ.append(RxGate(2*theta), [1]) + + with pulse.build() as q0_x180: + pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0)) + + # Add calibrations + circ.add_calibration('rxt', [0], q0_x180, params=(1.57,)) + + self.assertEqual(set(circ.calibrations.keys()), {'rxt'}) + self.assertEqual(set(circ.calibrations['rxt'].keys()), {((0,), (1.57,))}) + self.assertEqual(circ.calibrations['rxt'][((0,), (1.57,))].instructions, + q0_x180.instructions) + if __name__ == '__main__': unittest.main() From 77dc3843bc32e8740a5da7b0357d702f1dec6c40 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Fri, 21 Aug 2020 15:29:00 -0400 Subject: [PATCH 11/14] review comments --- qiskit/compiler/transpile.py | 3 ++- test/python/circuit/test_circuit_properties.py | 13 +++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py index b6b5dc18872e..088c8ce5d4ab 100644 --- a/qiskit/compiler/transpile.py +++ b/qiskit/compiler/transpile.py @@ -184,7 +184,8 @@ def callback_func(**kwargs): for circuit in circuits: if len(circuit.calibrations) != 0: - warnings.warn("Transpiling with calibrations aren't supported currently. ") + warnings.warn("Transpiling with calibrations are not supported currently. ", + UserWarning) pass if pass_manager is not None: diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index fb78b3122593..698251506c35 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -619,8 +619,6 @@ def test_num_qubits_multiple_register_circuit(self): def test_calibrations_basis_gates(self): """Check if the calibrations for basis gates provided are added correctly.""" circ = QuantumCircuit(2) - circ.rx(3.14, 0) - circ.ry(1.57, 1) with pulse.build() as q0_x180: pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0)) @@ -646,21 +644,16 @@ def __init__(self, theta): super().__init__('rxt', 1, [theta]) circ = QuantumCircuit(3) - circ.append(RxGate(1.57), [0]) - circ.append(RxGate(1.57), [0]) - - theta = Parameter('theta') - circ.append(RxGate(2*theta), [1]) with pulse.build() as q0_x180: pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0)) # Add calibrations - circ.add_calibration('rxt', [0], q0_x180, params=(1.57,)) + circ.add_calibration('rxt', [0], q0_x180, params=[1.57, 3.14, 4.71]) self.assertEqual(set(circ.calibrations.keys()), {'rxt'}) - self.assertEqual(set(circ.calibrations['rxt'].keys()), {((0,), (1.57,))}) - self.assertEqual(circ.calibrations['rxt'][((0,), (1.57,))].instructions, + self.assertEqual(set(circ.calibrations['rxt'].keys()), {((0,), (1.57, 3.14, 4.71))}) + self.assertEqual(circ.calibrations['rxt'][((0,), (1.57, 3.14, 4.71))].instructions, q0_x180.instructions) From 7b080316b7988d4ca0d82e4847baf0b6bc0602d4 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Fri, 21 Aug 2020 16:13:54 -0400 Subject: [PATCH 12/14] review comments --- qiskit/compiler/transpile.py | 1 - test/python/circuit/test_circuit_properties.py | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py index 088c8ce5d4ab..db695e9b37a8 100644 --- a/qiskit/compiler/transpile.py +++ b/qiskit/compiler/transpile.py @@ -186,7 +186,6 @@ def callback_func(**kwargs): if len(circuit.calibrations) != 0: warnings.warn("Transpiling with calibrations are not supported currently. ", UserWarning) - pass if pass_manager is not None: _check_conflicting_argument(optimization_level=optimization_level, basis_gates=basis_gates, diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index 698251506c35..005caff992c9 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -17,7 +17,6 @@ import unittest import numpy as np from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, pulse -from qiskit.circuit import Parameter, Gate from qiskit.circuit.library import RXGate, RYGate from qiskit.test import QiskitTestCase from qiskit.circuit.exceptions import CircuitError @@ -639,16 +638,12 @@ def test_calibrations_basis_gates(self): def test_calibrations_custom_gates(self): """Check if the calibrations for custom gates with params provided are added correctly.""" - class RxGate(Gate): - def __init__(self, theta): - super().__init__('rxt', 1, [theta]) - circ = QuantumCircuit(3) with pulse.build() as q0_x180: pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0)) - # Add calibrations + # Add calibrations with a custom gate 'rxt' circ.add_calibration('rxt', [0], q0_x180, params=[1.57, 3.14, 4.71]) self.assertEqual(set(circ.calibrations.keys()), {'rxt'}) From d91444ee7ec7ecab3fedda77b5023542fb047281 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Fri, 21 Aug 2020 16:38:10 -0400 Subject: [PATCH 13/14] review comment --- qiskit/compiler/transpile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/compiler/transpile.py b/qiskit/compiler/transpile.py index db695e9b37a8..5a140811c105 100644 --- a/qiskit/compiler/transpile.py +++ b/qiskit/compiler/transpile.py @@ -184,7 +184,7 @@ def callback_func(**kwargs): for circuit in circuits: if len(circuit.calibrations) != 0: - warnings.warn("Transpiling with calibrations are not supported currently. ", + warnings.warn("Transpiling with calibrations are not supported currently.", UserWarning) if pass_manager is not None: From 755c5e257a018464a7a1607e94afa50e09e27e15 Mon Sep 17 00:00:00 2001 From: SooluThomas Date: Fri, 21 Aug 2020 16:53:42 -0400 Subject: [PATCH 14/14] add reno --- ...se-gate-calibrations-78fd3fa5a5328761.yaml | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 releasenotes/notes/pulse-gate-calibrations-78fd3fa5a5328761.yaml diff --git a/releasenotes/notes/pulse-gate-calibrations-78fd3fa5a5328761.yaml b/releasenotes/notes/pulse-gate-calibrations-78fd3fa5a5328761.yaml new file mode 100644 index 000000000000..e501057a1eb0 --- /dev/null +++ b/releasenotes/notes/pulse-gate-calibrations-78fd3fa5a5328761.yaml @@ -0,0 +1,36 @@ +--- +features: + - | + Circuits now support a new feature: Pulse gates. This feature enables users who are working + primarily with ``QuantumCircuits`` to override (for basis gates) or specify (for standard + and custom gates) a definition of a ``Gate`` operation in terms of time-ordered signals + across hardware channels. In other words, it allow users to provide a pulse-level "custom + gate calibration." + + The circuits are built exactly as before. For example:: + + from qiskit import pulse + from qiskit.circuit import QuantumCircuit, Gate + + class RxGate(Gate): + def __init__(self, theta): + super().__init__('rxtheta', 1, [theta]) + + circ = QuantumCircuit(1) + circ.h(0) + circ.append(RxGate(3.14), [1]) + + Then, the user must register the calibration with the gate via the circuit method + ``add_calibration``, which takes a schedule definition as well as the specific qubits + and parameters that it is defined for:: + + # Define the gate implementation as a schedule + with pulse.build() as custom_h_schedule: + pulse.play(pulse.library.Drag(...), pulse.DriveChannel(0)) + + with pulse.build() as q1_x180: + pulse.play(pulse.library.Gaussian(...), pulse.DriveChannel(1)) + + # Register the schedule to the gate + circ.add_calibration('h', [0], custom_h_schedule) # or gate.name string to register + circ.add_calibration(RxGate(3.14), [0], q1_x180) # Can accept gate