Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import warnings
import numbers
import multiprocessing as mp
from collections import OrderedDict
from collections import OrderedDict, defaultdict
import numpy as np
from qiskit.exceptions import QiskitError
from qiskit.util import is_main_process
Expand Down Expand Up @@ -164,6 +164,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.
Expand All @@ -186,6 +187,15 @@ def data(self):
"""
return QuantumCircuitData(self)

@property
def calibrations(self):
"""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
def data(self, data_input):
"""Sets the circuit data from a list of instructions and context.
Expand Down Expand Up @@ -2253,6 +2263,23 @@ def cz(self, control_qubit, target_qubit, # pylint: disable=invalid-name
return self.append(CZGate(label=label, ctrl_state=ctrl_state),
[control_qubit, target_qubit], [])

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.
Comment thread
SooluThomas marked this conversation as resolved.
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][(tuple(qubits), tuple(gate.params))] = schedule
Comment thread
lcapelluto marked this conversation as resolved.
else:
self._calibrations[gate][(tuple(qubits), tuple(params))] = schedule


def _circuit_from_qasm(qasm):
# pylint: disable=cyclic-import
Expand Down
10 changes: 10 additions & 0 deletions qiskit/compiler/assemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,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.calibrations is not empty.
"""
start_time = time()
experiments = experiments if isinstance(experiments, list) else [experiments]
Expand All @@ -141,6 +142,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.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
Expand Down
6 changes: 6 additions & 0 deletions qiskit/compiler/transpile.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ def callback_func(**kwargs):
_log_transpile_time(start_time, end_time)
return circuits

for circuit in circuits:
if len(circuit.calibrations) != 0:
warnings.warn("Transpiling with calibrations are not supported currently. ",
Comment thread
SooluThomas marked this conversation as resolved.
Outdated
UserWarning)
pass
Comment thread
SooluThomas marked this conversation as resolved.
Outdated

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,
Expand Down
44 changes: 43 additions & 1 deletion test/python/circuit/test_circuit_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

import unittest
import numpy as np
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
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
# pylint: disable=unused-import
Expand Down Expand Up @@ -614,6 +616,46 @@ 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_basis_gates(self):
"""Check if the calibrations for basis gates provided are added correctly."""
circ = QuantumCircuit(2)

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,), (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,
q1_y90.instructions)

Comment thread
lcapelluto marked this conversation as resolved.
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])
Comment thread
SooluThomas marked this conversation as resolved.
Outdated

circ = QuantumCircuit(3)

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, 3.14, 4.71])

self.assertEqual(set(circ.calibrations.keys()), {'rxt'})
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)


if __name__ == '__main__':
unittest.main()