From 8a72709836a8f9584b209eb9f47594405b2e9c94 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 2 Dec 2020 20:13:09 +0100 Subject: [PATCH 01/38] * Added fix and corresponding test to catch bug related to parameter assignment in QuantumCircuit. --- qiskit/circuit/quantumcircuit.py | 3 +++ test/python/circuit/test_parameters.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index b7c3a4c9059c..c224f758d83b 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2043,6 +2043,9 @@ def _assign_parameter(self, parameter, value): if isinstance(value, ParameterExpression): entry = self._parameter_table.pop(parameter) for new_parameter in value.parameters: + if new_parameter in self._parameter_table: + entry.extend(self._parameter_table[new_parameter]) + self._parameter_table[new_parameter] = entry else: del self._parameter_table[parameter] # clear evaluated expressions diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index d30836a0bbd2..e3cc2f9b04a1 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -252,6 +252,24 @@ def test_expression_partial_binding(self): self.assertTrue(isinstance(fbqc.data[0][0].params[0], ParameterExpression)) self.assertEqual(float(fbqc.data[0][0].params[0]), 3) + def test_two_parameter_expression_binding(self): + """Verify that for a circuit with parameters theta and phi that + we can correctly assign theta to -phi. + """ + theta = Parameter('theta') + phi = Parameter('phi') + + qc = QuantumCircuit(1) + qc.rx(theta, 0) + qc.ry(phi, 0) + + self.assertEqual(len(qc._parameter_table[theta]), 1) + self.assertEqual(len(qc._parameter_table[phi]), 1) + + qc.assign_parameters({theta: -phi}, inplace=True) + + self.assertEqual(len(qc._parameter_table[phi]), 2) + def test_expression_partial_binding_zero(self): """Verify that binding remains possible even if a previous partial bind would reduce the expression to zero. From 45927737c6db7217df91ecd385424ccd0e6746c1 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 3 Dec 2020 09:07:35 +0100 Subject: [PATCH 02/38] * Applied suggestion from cryoris. --- qiskit/circuit/quantumcircuit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index c224f758d83b..19b4ab07bfe0 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -2044,9 +2044,9 @@ def _assign_parameter(self, parameter, value): entry = self._parameter_table.pop(parameter) for new_parameter in value.parameters: if new_parameter in self._parameter_table: - entry.extend(self._parameter_table[new_parameter]) - - self._parameter_table[new_parameter] = entry + self._parameter_table[new_parameter].extend(entry) + else: + self._parameter_table[new_parameter] = entry else: del self._parameter_table[parameter] # clear evaluated expressions From 6ff48b8e98b3eaf74300773560565a3a2b37b827 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 29 Jan 2021 23:25:42 +0100 Subject: [PATCH 03/38] * Created a new TransformationPass pass CalibrationAdder to add calibrations to a quantum circuit. * Created a new abstract base class CalibrationCreator that is given to CalibrationAdder - CalibrationCreator is capable of telling if it supports a given DAG node. - If a DAG node is supported then it can create a calibration for that node and taylored to the backend. * Created a child class of CalibrationCreator to build ZX schedules based on ECR gates. * Created the library zx templates which provides ZX-based templates for the TemplateOptimizer. * Added the method add_calibration to DAGCircuit. * Amended corresponding __init__ files. --- qiskit/dagcircuit/dagcircuit.py | 17 ++ .../template_matching/__init__.py | 2 + .../template_matching/calibration_adder.py | 55 ++++++ .../template_matching/zx_templates.py | 132 +++++++++++++ .../transpiler/passes/scheduling/__init__.py | 1 + .../passes/scheduling/calibration_creators.py | 173 ++++++++++++++++++ 6 files changed, 380 insertions(+) create mode 100644 qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py create mode 100644 qiskit/transpiler/passes/optimization/template_matching/zx_templates.py create mode 100644 qiskit/transpiler/passes/scheduling/calibration_creators.py diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index b720ddcaf212..e4000ba7b239 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -195,6 +195,23 @@ def calibrations(self, calibrations): """ self._calibrations = defaultdict(dict, calibrations) + 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]]): 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 + else: + self._calibrations[gate][(tuple(qubits), tuple(params or []))] = schedule + def has_calibration_for(self, node): """Return True if the dag has a calibration defined for the node operation. In this case, the operation does not need to be translated to the device basis. diff --git a/qiskit/transpiler/passes/optimization/template_matching/__init__.py b/qiskit/transpiler/passes/optimization/template_matching/__init__.py index b2ea3de2d786..28fa00a5a719 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/__init__.py +++ b/qiskit/transpiler/passes/optimization/template_matching/__init__.py @@ -17,3 +17,5 @@ from .template_matching import TemplateMatching from .maximal_matches import MaximalMatches from .template_substitution import SubstitutionConfig, TemplateSubstitution +from .zx_templates import zx_templates +from .calibration_adder import CalibrationAdder diff --git a/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py b/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py new file mode 100644 index 000000000000..fa903ba8c3fc --- /dev/null +++ b/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py @@ -0,0 +1,55 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Scheduling transformation pass to add calibrations to the dag.""" + +from qiskit.transpiler.passes.scheduling import CalibrationCreator +from qiskit.transpiler.basepasses import TransformationPass +from qiskit.circuit import Gate +from qiskit.extensions import UnitaryGate + + +class CalibrationAdder(TransformationPass): + """Identifies gates for which we can add calibrations.""" + + def __init__(self, calibration_creator: CalibrationCreator): + """ + Args: + calibration_creator: An instance of CalibrationCreator capable of generating the + schedules that will be added to the circuit. + """ + super().__init__() + self._calibration_adder = calibration_creator + + def run(self, dag): + """Run the calibration adder pass on `dag`. + + Args: + dag (DAGCircuit): DAG to schedule. + + Returns: + DAGCircuit: A DAG with calibrations added to it. + """ + for node in dag.nodes(): + if self._calibration_adder.supported(node.name): + name = node.name + qubits = [_.index for _ in node.qargs] + + schedule, params = self._calibration_adder.get_calibration(name, qubits) + + dag.add_calibration(name, qubits, schedule, params=params) + + # Unitary gate has unhashable data + if isinstance(node.op, UnitaryGate): + node.op = Gate(name, node.op.num_qubits, params) + + return dag diff --git a/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py b/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py new file mode 100644 index 000000000000..25bdff7c8b40 --- /dev/null +++ b/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py @@ -0,0 +1,132 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +A library of ZX based two qubit gates. +""" +import numpy as np +from typing import List + +from qiskit import QuantumCircuit +from qiskit.circuit import Parameter, Gate +from qiskit.extensions import UnitaryGate + + +class ZX(Gate): + """CZ gates used for the test.""" + + def __init__(self, params): + super().__init__('ZX', 2, params) + self.num_ctrl_qubits = 1 + self.ctrl_state = 1 + + def inverse(self): + theta = -self.params[0] + inverse = UnitaryGate( + [[np.cos(-0.5 * theta), 1.0j * np.sin(-0.5 * theta), 0, 0], + [1.0j * np.sin(-0.5 * theta), np.cos(-0.5 * theta), 0, 0], + [0, 0, np.cos(0.5 * theta), 1.0j * np.sin(0.5 * theta)], + [0, 0, 1.0j * np.sin(0.5 * theta), np.cos(0.5 * theta)]]) + inverse.name = 'zx(%.3f)' % theta + return inverse + + +def zx_zz1(): + """ZZ template with rz gate.""" + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.rz(theta, 1) + qc.sx(1) + qc.rz(np.pi, 1) + qc.sx(1) + qc.rz(3 * np.pi, 1) + qc.cx(0, 1) + qc.p(-theta, 1) + + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + qc.rx(theta, 1) + qc.append(ZX([-theta]), [1, 0]) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + return qc + + +def zx_zz2(): + """ZZ template is p gate.""" + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.p(theta, 1) + qc.cx(0, 1) + qc.p(-theta, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + qc.rx(theta, 1) + qc.append(ZX([-theta]), [1, 0]) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + return qc + + +def zx_zy(): + """ZY template.""" + theta = Parameter('ϴ') + + circ = QuantumCircuit(2) + circ.cx(0, 1) + circ.ry(-theta, 0) + circ.cx(0, 1) + circ.rx(np.pi / 2, 0) + circ.append(ZX([theta]), [1, 0]) + circ.rx(-np.pi / 2, 0) + + return circ + + +def zx_templates(template_list: List[str] = None): + """ + Convenience function to get the cost_dict and + templates for template matching. + """ + + if template_list is None: + template_list = ['zz1', 'zz2', 'zy'] + + templates = [] + if 'zz1' in template_list: + templates.append(zx_zz1()) + if 'zz2' in template_list: + templates.append(zx_zz2()) + if 'zy' in template_list: + templates.append(zx_zy()) + + cost_dict = {'ZX': 0, 'cx': 6, 'rz': 1, 'sx': 2, 'p': 0, 'h': 1, 'rx': 1, 'ry': 1} + + zx_dict = {'template_list': templates, 'user_cost_dict': cost_dict} + + return zx_dict diff --git a/qiskit/transpiler/passes/scheduling/__init__.py b/qiskit/transpiler/passes/scheduling/__init__.py index e2e2b46f8357..d08622ed8b71 100644 --- a/qiskit/transpiler/passes/scheduling/__init__.py +++ b/qiskit/transpiler/passes/scheduling/__init__.py @@ -15,3 +15,4 @@ from .alap import ALAPSchedule from .asap import ASAPSchedule from .time_unit_analysis import TimeUnitAnalysis +from .calibration_creators import CalibrationCreator, ZXScheduleBuilder diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py new file mode 100644 index 000000000000..cfe7842e2714 --- /dev/null +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -0,0 +1,173 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Calibration adders.""" + +import math +import numpy as np +from typing import List, Tuple +from abc import ABC, abstractmethod +import re + +from qiskit.pulse import Play, ShiftPhase, Schedule, ControlChannel, DriveChannel, GaussianSquare +from qiskit import QiskitError +from qiskit.providers import basebackend + + +class CalibrationCreator(ABC): + + @abstractmethod + def supported(self, name: str) -> bool: + """Determine if a given name supports the calibration.""" + + @abstractmethod + def get_calibration(self, name: str, qubits: List) -> Tuple[Schedule, List]: + """Gets the calibrated schedule for the given qubits and parameters.""" + + +class ZXScheduleBuilder(CalibrationCreator): + + def __init__(self, backend: basebackend): + """ + Initializes a Parameterized Controlled-Z gate builder. + Args: + backend: Backend for which to construct the gates. + """ + if not backend.configuration().open_pulse: + raise QiskitError(backend.name() + ' is not a pulse backend.') + + self._inst_map = backend.defaults().instruction_schedule_map + self._config = backend.configuration() + self._channel_map = backend.configuration().qubit_channel_mapping + + def supported(self, name: str) -> bool: + """ + Args: + name: This calibration builder supports names of the form zx(%.3f) + + Returns: + match: True if the name of the node is zx(%.3f) where %.3f is the rotation + angle. + """ + pattern = r'^zx\([+-]?([0-9]*[.])?[0-9]+\)$' + if re.match(pattern, name): + return True + + return False + + @staticmethod + def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> Play: + """ + Args: + instruction: The instruction from which to create a new shortened or lengthened pulse. + theta: desired angle, pi/2 is assumed to be the angle that + the schedule with name 'name' in 'sched' implements. + sample_mult: All pulses must be a multiple of sample_mult. + """ + pulse_ = instruction.pulse + if isinstance(pulse_, GaussianSquare): + amp = pulse_.amp + width = pulse_.width + sigma = pulse_.sigma + n_sigmas = (pulse_.duration - width) / sigma + + # The error function is used because the Gaussian may have chopped tails. + gaussian_area = abs(amp) * sigma * np.sqrt(2 * np.pi) * math.erf(n_sigmas) + area = gaussian_area + abs(amp) * width + + target_area = abs(theta) / (np.pi / 2.) * area + sign = theta / abs(theta) + + if target_area > gaussian_area: + width = (target_area - gaussian_area) / abs(amp) + duration = math.ceil((width + n_sigmas * sigma) / sample_mult) * sample_mult + return Play(GaussianSquare(amp=sign*amp, width=width, sigma=sigma, + duration=duration), channel=instruction.channel) + else: + amp_scale = sign * target_area / gaussian_area + duration = math.ceil(n_sigmas * sigma / sample_mult) * sample_mult + return Play( + GaussianSquare(amp=amp * amp_scale, width=0, sigma=sigma, duration=duration), + channel=instruction.channel) + else: + raise QiskitError('ZXScheduleBuilder builder only stretches/compresses GaussianSquare.') + + @staticmethod + def name(theta: float) -> str: + """ + Args: + theta: Rotation angle of the parametric CZ gate. + """ + return 'zx(%.3f)' % theta + + def get_calibration(self, name: str, qubits: List) -> Tuple[Schedule, List]: + """ + Args: + name: Name of the instruction with the rotation angle in it. + qubits: List of qubits for which to get the schedules. + + Returns: + schedule: The calibration schedule for the instruction with name. + """ + theta = float(name.replace('zx(', '').replace(')', '')) + q1, q2 = qubits[0], qubits[1] + cx_sched = self._inst_map.get('cx', qubits=(q1, q2)) + zx_theta = Schedule(name=self.name(theta)) + + crs, comp_tones, shift_phases = [], [], [] + control, target = None, None + + for time, inst in cx_sched.instructions: + + if isinstance(inst, ShiftPhase) and time == 0: + shift_phases.append(ShiftPhase(-theta, inst.channel)) + + # Identify the CR pulses. + if isinstance(inst, Play) and not isinstance(inst, ShiftPhase): + if isinstance(inst.channel, ControlChannel): + crs.append((time, inst)) + + # Identify the compensation tones. + if isinstance(inst.channel, DriveChannel) and not isinstance(inst, ShiftPhase): + if isinstance(inst.pulse, GaussianSquare): + comp_tones.append((time, inst)) + target = inst.channel.index + control = q1 if target == q2 else q2 + + if control is None: + raise QiskitError('Control qubit is None.') + if target is None: + raise QiskitError('Target qubit is None.') + + echo_x = self._inst_map.get('x', qubits=control) + + # Build the schedule + for inst in shift_phases: + zx_theta = zx_theta.insert(0, inst) + + # Stretch/compress the CR gates and compensation tones + cr1 = self.rescale_cr_inst(crs[0][1], theta) + cr2 = self.rescale_cr_inst(crs[1][1], theta) + comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) + comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) + + if theta != 0.0: + zx_theta = zx_theta.insert(0, cr1) + zx_theta = zx_theta.insert(0, comp1) + zx_theta = zx_theta.insert(comp1.duration, echo_x) + time = comp1.duration + echo_x.duration + zx_theta = zx_theta.insert(time, cr2) + zx_theta = zx_theta.insert(time, comp2) + time = 2*comp1.duration + echo_x.duration + zx_theta = zx_theta.insert(time, echo_x) + + return zx_theta, [theta] From 996b24cb16e8e362442643b637d6df7909327b31 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 10:49:48 +0100 Subject: [PATCH 04/38] * Cleaned-up code by using the standard RZXGate. --- .../template_matching/calibration_adder.py | 17 +++----- .../template_matching/zx_templates.py | 30 +++----------- .../passes/scheduling/calibration_creators.py | 40 +++++++------------ 3 files changed, 25 insertions(+), 62 deletions(-) diff --git a/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py b/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py index fa903ba8c3fc..43643e3773ea 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py +++ b/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py @@ -14,8 +14,6 @@ from qiskit.transpiler.passes.scheduling import CalibrationCreator from qiskit.transpiler.basepasses import TransformationPass -from qiskit.circuit import Gate -from qiskit.extensions import UnitaryGate class CalibrationAdder(TransformationPass): @@ -40,16 +38,13 @@ def run(self, dag): DAGCircuit: A DAG with calibrations added to it. """ for node in dag.nodes(): - if self._calibration_adder.supported(node.name): - name = node.name - qubits = [_.index for _ in node.qargs] + if node.type == 'op': + if self._calibration_adder.supported(node.op): + params = node.op.params + qubits = [_.index for _ in node.qargs] - schedule, params = self._calibration_adder.get_calibration(name, qubits) + schedule = self._calibration_adder.get_calibration(params, qubits) - dag.add_calibration(name, qubits, schedule, params=params) - - # Unitary gate has unhashable data - if isinstance(node.op, UnitaryGate): - node.op = Gate(name, node.op.num_qubits, params) + dag.add_calibration(node.op, qubits, schedule, params=params) return dag diff --git a/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py b/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py index 25bdff7c8b40..2a55b044cde1 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py +++ b/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py @@ -17,27 +17,7 @@ from typing import List from qiskit import QuantumCircuit -from qiskit.circuit import Parameter, Gate -from qiskit.extensions import UnitaryGate - - -class ZX(Gate): - """CZ gates used for the test.""" - - def __init__(self, params): - super().__init__('ZX', 2, params) - self.num_ctrl_qubits = 1 - self.ctrl_state = 1 - - def inverse(self): - theta = -self.params[0] - inverse = UnitaryGate( - [[np.cos(-0.5 * theta), 1.0j * np.sin(-0.5 * theta), 0, 0], - [1.0j * np.sin(-0.5 * theta), np.cos(-0.5 * theta), 0, 0], - [0, 0, np.cos(0.5 * theta), 1.0j * np.sin(0.5 * theta)], - [0, 0, 1.0j * np.sin(0.5 * theta), np.cos(0.5 * theta)]]) - inverse.name = 'zx(%.3f)' % theta - return inverse +from qiskit.circuit import Parameter def zx_zz1(): @@ -60,7 +40,7 @@ def zx_zz1(): qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.append(ZX([-theta]), [1, 0]) + qc.rzx(-theta, 1, 0) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) @@ -84,7 +64,7 @@ def zx_zz2(): qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.append(ZX([-theta]), [1, 0]) + qc.rzx(-theta, 1, 0) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) @@ -102,7 +82,7 @@ def zx_zy(): circ.ry(-theta, 0) circ.cx(0, 1) circ.rx(np.pi / 2, 0) - circ.append(ZX([theta]), [1, 0]) + circ.rzx(theta, 1, 0) circ.rx(-np.pi / 2, 0) return circ @@ -125,7 +105,7 @@ def zx_templates(template_list: List[str] = None): if 'zy' in template_list: templates.append(zx_zy()) - cost_dict = {'ZX': 0, 'cx': 6, 'rz': 1, 'sx': 2, 'p': 0, 'h': 1, 'rx': 1, 'ry': 1} + cost_dict = {'rzx': 0, 'cx': 6, 'rz': 1, 'sx': 2, 'p': 0, 'h': 1, 'rx': 1, 'ry': 1} zx_dict = {'template_list': templates, 'user_cost_dict': cost_dict} diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index cfe7842e2714..6e686e80f4cd 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -14,23 +14,24 @@ import math import numpy as np -from typing import List, Tuple +from typing import List from abc import ABC, abstractmethod -import re from qiskit.pulse import Play, ShiftPhase, Schedule, ControlChannel, DriveChannel, GaussianSquare from qiskit import QiskitError from qiskit.providers import basebackend +from qiskit.dagcircuit import DAGNode +from qiskit.circuit.library.standard_gates import RZXGate class CalibrationCreator(ABC): @abstractmethod - def supported(self, name: str) -> bool: + def supported(self, node_op: DAGNode) -> bool: """Determine if a given name supports the calibration.""" @abstractmethod - def get_calibration(self, name: str, qubits: List) -> Tuple[Schedule, List]: + def get_calibration(self, params: List, qubits: List) -> Schedule: """Gets the calibrated schedule for the given qubits and parameters.""" @@ -49,20 +50,15 @@ def __init__(self, backend: basebackend): self._config = backend.configuration() self._channel_map = backend.configuration().qubit_channel_mapping - def supported(self, name: str) -> bool: + def supported(self, node_op: DAGNode) -> bool: """ Args: - name: This calibration builder supports names of the form zx(%.3f) + node_op: The node from the dag dep. Returns: - match: True if the name of the node is zx(%.3f) where %.3f is the rotation - angle. + match: True if the node is a RZXGate. """ - pattern = r'^zx\([+-]?([0-9]*[.])?[0-9]+\)$' - if re.match(pattern, name): - return True - - return False + return isinstance(node_op, RZXGate) @staticmethod def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> Play: @@ -101,27 +97,19 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> P else: raise QiskitError('ZXScheduleBuilder builder only stretches/compresses GaussianSquare.') - @staticmethod - def name(theta: float) -> str: - """ - Args: - theta: Rotation angle of the parametric CZ gate. - """ - return 'zx(%.3f)' % theta - - def get_calibration(self, name: str, qubits: List) -> Tuple[Schedule, List]: + def get_calibration(self, params: List, qubits: List) -> Schedule: """ Args: - name: Name of the instruction with the rotation angle in it. + params: Name of the instruction with the rotation angle in it. qubits: List of qubits for which to get the schedules. Returns: schedule: The calibration schedule for the instruction with name. """ - theta = float(name.replace('zx(', '').replace(')', '')) + theta = params[0] q1, q2 = qubits[0], qubits[1] cx_sched = self._inst_map.get('cx', qubits=(q1, q2)) - zx_theta = Schedule(name=self.name(theta)) + zx_theta = Schedule(name='zx(%.3f)' % theta) crs, comp_tones, shift_phases = [], [], [] control, target = None, None @@ -170,4 +158,4 @@ def get_calibration(self, name: str, qubits: List) -> Tuple[Schedule, List]: time = 2*comp1.duration + echo_x.duration zx_theta = zx_theta.insert(time, echo_x) - return zx_theta, [theta] + return zx_theta From c954f50245781739968d45e0d768fd2c838ac32b Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 12:43:13 +0100 Subject: [PATCH 05/38] * Added tests. * Fixed some lint errors. * Debuged zx templates and moved them to the circuit library. --- qiskit/circuit/library/templates/__init__.py | 5 +++ .../library/templates}/zx_templates.py | 29 ++++++++------- .../template_matching/__init__.py | 1 - .../passes/scheduling/calibration_creators.py | 20 ++++++++-- test/python/transpiler/test_zx_templates.py | 37 +++++++++++++++++++ 5 files changed, 75 insertions(+), 17 deletions(-) rename qiskit/{transpiler/passes/optimization/template_matching => circuit/library/templates}/zx_templates.py (83%) create mode 100644 test/python/transpiler/test_zx_templates.py diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index 91c15e0720d4..7dae1c854345 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -64,3 +64,8 @@ from .nct.template_nct_9d_8 import template_nct_9d_8 from .nct.template_nct_9d_9 import template_nct_9d_9 from .nct.template_nct_9d_10 import template_nct_9d_10 + +from .zx_templates import zx_zy +from .zx_templates import zx_zz1 +from .zx_templates import zx_zz2 +from .zx_templates import zx_templates diff --git a/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py b/qiskit/circuit/library/templates/zx_templates.py similarity index 83% rename from qiskit/transpiler/passes/optimization/template_matching/zx_templates.py rename to qiskit/circuit/library/templates/zx_templates.py index 2a55b044cde1..8d78cc7f1e18 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/zx_templates.py +++ b/qiskit/circuit/library/templates/zx_templates.py @@ -13,16 +13,17 @@ """ A library of ZX based two qubit gates. """ -import numpy as np + from typing import List +import numpy as np -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter +from qiskit.circuit import Parameter, QuantumCircuit -def zx_zz1(): +def zx_zz1(theta: float = None): """ZZ template with rz gate.""" - theta = Parameter('ϴ') + if theta is None: + theta = Parameter('ϴ') qc = QuantumCircuit(2) qc.cx(0, 1) @@ -32,7 +33,7 @@ def zx_zz1(): qc.sx(1) qc.rz(3 * np.pi, 1) qc.cx(0, 1) - qc.p(-theta, 1) + qc.rz(-theta, 1) # Hadamard qc.rz(np.pi / 2, 1) @@ -40,7 +41,7 @@ def zx_zz1(): qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.rzx(-theta, 1, 0) + qc.rzx(-theta, 0, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) @@ -49,9 +50,10 @@ def zx_zz1(): return qc -def zx_zz2(): +def zx_zz2(theta: float = None): """ZZ template is p gate.""" - theta = Parameter('ϴ') + if theta is None: + theta = Parameter('ϴ') qc = QuantumCircuit(2) qc.cx(0, 1) @@ -64,7 +66,7 @@ def zx_zz2(): qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.rzx(-theta, 1, 0) + qc.rzx(-theta, 0, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) @@ -73,16 +75,17 @@ def zx_zz2(): return qc -def zx_zy(): +def zx_zy(theta: float = None): """ZY template.""" - theta = Parameter('ϴ') + if theta is None: + theta = Parameter('ϴ') circ = QuantumCircuit(2) circ.cx(0, 1) circ.ry(-theta, 0) circ.cx(0, 1) circ.rx(np.pi / 2, 0) - circ.rzx(theta, 1, 0) + circ.rzx(theta, 0, 1) circ.rx(-np.pi / 2, 0) return circ diff --git a/qiskit/transpiler/passes/optimization/template_matching/__init__.py b/qiskit/transpiler/passes/optimization/template_matching/__init__.py index 28fa00a5a719..638c3531fede 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/__init__.py +++ b/qiskit/transpiler/passes/optimization/template_matching/__init__.py @@ -17,5 +17,4 @@ from .template_matching import TemplateMatching from .maximal_matches import MaximalMatches from .template_substitution import SubstitutionConfig, TemplateSubstitution -from .zx_templates import zx_templates from .calibration_adder import CalibrationAdder diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 6e686e80f4cd..9d6ca36a0ef0 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -13,9 +13,9 @@ """Calibration adders.""" import math -import numpy as np from typing import List from abc import ABC, abstractmethod +import numpy as np from qiskit.pulse import Play, ShiftPhase, Schedule, ControlChannel, DriveChannel, GaussianSquare from qiskit import QiskitError @@ -25,6 +25,7 @@ class CalibrationCreator(ABC): + """Abstract base class to inject calibrations into circuits.""" @abstractmethod def supported(self, node_op: DAGNode) -> bool: @@ -36,6 +37,10 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: class ZXScheduleBuilder(CalibrationCreator): + """ + Creates calibrations for ZX(theta) by stretching and compressing + Gaussian square pulses. + """ def __init__(self, backend: basebackend): """ @@ -65,9 +70,15 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> P """ Args: instruction: The instruction from which to create a new shortened or lengthened pulse. - theta: desired angle, pi/2 is assumed to be the angle that - the schedule with name 'name' in 'sched' implements. + theta: desired angle, pi/2 is assumed to be the angle that the pulse in the given + play instruction implements. sample_mult: All pulses must be a multiple of sample_mult. + + Returns: + Play: The play instruction with the stretched compressed GaussianSquare pulse. + + Raises: + QiskitError: if the pulses are not GaussianSquare. """ pulse_ = instruction.pulse if isinstance(pulse_, GaussianSquare): @@ -105,6 +116,9 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: Returns: schedule: The calibration schedule for the instruction with name. + + Raises: + QiskitError: if the the control and target qubits cannot be identified. """ theta = params[0] q1, q2 = qubits[0], qubits[1] diff --git a/test/python/transpiler/test_zx_templates.py b/test/python/transpiler/test_zx_templates.py new file mode 100644 index 000000000000..93df5d13534a --- /dev/null +++ b/test/python/transpiler/test_zx_templates.py @@ -0,0 +1,37 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Test the zx_templates.""" + +import numpy as np + +from qiskit.quantum_info import Operator +from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_zy +from qiskit.test import QiskitTestCase + + +class TestZXTemplates(QiskitTestCase): + """Test the parametric templates.""" + + def test_templates(self): + """Test that the templates compose to the identity.""" + + print(Operator(zx_zy(0.456)).data) + print(np.allclose(Operator(zx_zy(0.456)).data, np.eye(4))) + + self.assertTrue(np.allclose(Operator(zx_zy(0.456)).data, np.eye(4))) + + data = Operator(zx_zz1(0.456)).data + self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) + + data = Operator(zx_zz2(0.456)).data + self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) From bedd9310fdd013910d9743deb2d68f87e18fd47d Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 13:13:22 +0100 Subject: [PATCH 06/38] * Moved calibration_adder to scheduling. * Fixed lint. --- qiskit/circuit/library/templates/zx_templates.py | 10 +++++----- .../passes/optimization/template_matching/__init__.py | 1 - qiskit/transpiler/passes/scheduling/__init__.py | 1 + .../calibration_adder.py | 0 .../passes/scheduling/calibration_creators.py | 6 +++++- test/python/transpiler/test_zx_templates.py | 3 --- 6 files changed, 11 insertions(+), 10 deletions(-) rename qiskit/transpiler/passes/{optimization/template_matching => scheduling}/calibration_adder.py (100%) diff --git a/qiskit/circuit/library/templates/zx_templates.py b/qiskit/circuit/library/templates/zx_templates.py index 8d78cc7f1e18..246c8bc6e6fa 100644 --- a/qiskit/circuit/library/templates/zx_templates.py +++ b/qiskit/circuit/library/templates/zx_templates.py @@ -33,7 +33,7 @@ def zx_zz1(theta: float = None): qc.sx(1) qc.rz(3 * np.pi, 1) qc.cx(0, 1) - qc.rz(-theta, 1) + qc.rz(-1*theta, 1) # Hadamard qc.rz(np.pi / 2, 1) @@ -41,7 +41,7 @@ def zx_zz1(theta: float = None): qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.rzx(-theta, 0, 1) + qc.rzx(-1*theta, 0, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) @@ -59,14 +59,14 @@ def zx_zz2(theta: float = None): qc.cx(0, 1) qc.p(theta, 1) qc.cx(0, 1) - qc.p(-theta, 1) + qc.p(-1*theta, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.rzx(-theta, 0, 1) + qc.rzx(-1*theta, 0, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) @@ -82,7 +82,7 @@ def zx_zy(theta: float = None): circ = QuantumCircuit(2) circ.cx(0, 1) - circ.ry(-theta, 0) + circ.ry(-1*theta, 0) circ.cx(0, 1) circ.rx(np.pi / 2, 0) circ.rzx(theta, 0, 1) diff --git a/qiskit/transpiler/passes/optimization/template_matching/__init__.py b/qiskit/transpiler/passes/optimization/template_matching/__init__.py index 638c3531fede..b2ea3de2d786 100644 --- a/qiskit/transpiler/passes/optimization/template_matching/__init__.py +++ b/qiskit/transpiler/passes/optimization/template_matching/__init__.py @@ -17,4 +17,3 @@ from .template_matching import TemplateMatching from .maximal_matches import MaximalMatches from .template_substitution import SubstitutionConfig, TemplateSubstitution -from .calibration_adder import CalibrationAdder diff --git a/qiskit/transpiler/passes/scheduling/__init__.py b/qiskit/transpiler/passes/scheduling/__init__.py index d08622ed8b71..da6d7c41f30c 100644 --- a/qiskit/transpiler/passes/scheduling/__init__.py +++ b/qiskit/transpiler/passes/scheduling/__init__.py @@ -16,3 +16,4 @@ from .asap import ASAPSchedule from .time_unit_analysis import TimeUnitAnalysis from .calibration_creators import CalibrationCreator, ZXScheduleBuilder +from .calibration_adder import CalibrationAdder \ No newline at end of file diff --git a/qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py b/qiskit/transpiler/passes/scheduling/calibration_adder.py similarity index 100% rename from qiskit/transpiler/passes/optimization/template_matching/calibration_adder.py rename to qiskit/transpiler/passes/scheduling/calibration_adder.py diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 9d6ca36a0ef0..4f0937e87d45 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Calibration adders.""" +"""Calibration creators.""" import math from typing import List @@ -45,8 +45,12 @@ class ZXScheduleBuilder(CalibrationCreator): def __init__(self, backend: basebackend): """ Initializes a Parameterized Controlled-Z gate builder. + Args: backend: Backend for which to construct the gates. + + Raises: + QiskitError: if open pulse is not supported by the backend. """ if not backend.configuration().open_pulse: raise QiskitError(backend.name() + ' is not a pulse backend.') diff --git a/test/python/transpiler/test_zx_templates.py b/test/python/transpiler/test_zx_templates.py index 93df5d13534a..84b83ef48654 100644 --- a/test/python/transpiler/test_zx_templates.py +++ b/test/python/transpiler/test_zx_templates.py @@ -25,9 +25,6 @@ class TestZXTemplates(QiskitTestCase): def test_templates(self): """Test that the templates compose to the identity.""" - print(Operator(zx_zy(0.456)).data) - print(np.allclose(Operator(zx_zy(0.456)).data, np.eye(4))) - self.assertTrue(np.allclose(Operator(zx_zy(0.456)).data, np.eye(4))) data = Operator(zx_zz1(0.456)).data From 958af81b8128864559620d708b49d8aee857362d Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 13:27:25 +0100 Subject: [PATCH 07/38] * Added missing new line at end of file. --- qiskit/transpiler/passes/scheduling/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/__init__.py b/qiskit/transpiler/passes/scheduling/__init__.py index da6d7c41f30c..3d3b6ea777b9 100644 --- a/qiskit/transpiler/passes/scheduling/__init__.py +++ b/qiskit/transpiler/passes/scheduling/__init__.py @@ -16,4 +16,4 @@ from .asap import ASAPSchedule from .time_unit_analysis import TimeUnitAnalysis from .calibration_creators import CalibrationCreator, ZXScheduleBuilder -from .calibration_adder import CalibrationAdder \ No newline at end of file +from .calibration_adder import CalibrationAdder From 70656cce77b984cf0143e26517f0d18b9327c52c Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 15:21:29 +0100 Subject: [PATCH 08/38] * Fix cyclic import. --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 4f0937e87d45..3ea4087b23da 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -18,7 +18,7 @@ import numpy as np from qiskit.pulse import Play, ShiftPhase, Schedule, ControlChannel, DriveChannel, GaussianSquare -from qiskit import QiskitError +from qiskit.exceptions import QiskitError from qiskit.providers import basebackend from qiskit.dagcircuit import DAGNode from qiskit.circuit.library.standard_gates import RZXGate From 3871f130332d36303162032aa8a35418aa360de4 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 15:53:48 +0100 Subject: [PATCH 09/38] * Added cy template. --- qiskit/circuit/library/templates/__init__.py | 1 + .../circuit/library/templates/zx_templates.py | 18 ++++++++++++++++++ .../passes/scheduling/calibration_adder.py | 2 +- test/python/transpiler/test_zx_templates.py | 5 ++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index 7dae1c854345..b8b07330dee4 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -66,6 +66,7 @@ from .nct.template_nct_9d_10 import template_nct_9d_10 from .zx_templates import zx_zy +from .zx_templates import zx_cy from .zx_templates import zx_zz1 from .zx_templates import zx_zz2 from .zx_templates import zx_templates diff --git a/qiskit/circuit/library/templates/zx_templates.py b/qiskit/circuit/library/templates/zx_templates.py index 246c8bc6e6fa..2966794a5e59 100644 --- a/qiskit/circuit/library/templates/zx_templates.py +++ b/qiskit/circuit/library/templates/zx_templates.py @@ -91,6 +91,24 @@ def zx_zy(theta: float = None): return circ +def zx_cy(theta: float = None): + """ZY template.""" + if theta is None: + theta = Parameter('ϴ') + + circ = QuantumCircuit(2) + circ.cx(0, 1) + circ.ry(theta, 1) + circ.cx(0, 1) + circ.ry(-theta, 1) + circ.rz(-np.pi / 2, 1) + circ.rx(theta, 1) + circ.rzx(-theta, 0, 1) + circ.rz(np.pi / 2, 1) + + return circ + + def zx_templates(template_list: List[str] = None): """ Convenience function to get the cost_dict and diff --git a/qiskit/transpiler/passes/scheduling/calibration_adder.py b/qiskit/transpiler/passes/scheduling/calibration_adder.py index 43643e3773ea..c6e43b69c674 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_adder.py +++ b/qiskit/transpiler/passes/scheduling/calibration_adder.py @@ -12,8 +12,8 @@ """Scheduling transformation pass to add calibrations to the dag.""" -from qiskit.transpiler.passes.scheduling import CalibrationCreator from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.passes.scheduling import CalibrationCreator class CalibrationAdder(TransformationPass): diff --git a/test/python/transpiler/test_zx_templates.py b/test/python/transpiler/test_zx_templates.py index 84b83ef48654..3736f63fe71f 100644 --- a/test/python/transpiler/test_zx_templates.py +++ b/test/python/transpiler/test_zx_templates.py @@ -15,7 +15,7 @@ import numpy as np from qiskit.quantum_info import Operator -from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_zy +from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_zy, zx_cy from qiskit.test import QiskitTestCase @@ -27,6 +27,9 @@ def test_templates(self): self.assertTrue(np.allclose(Operator(zx_zy(0.456)).data, np.eye(4))) + data = Operator(zx_cy(0.456)).data + self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) + data = Operator(zx_zz1(0.456)).data self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) From 021fee743636a32e70e659f8a675bd30d72eb7fd Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 16:49:16 +0100 Subject: [PATCH 10/38] * Added hadamards to reverse the ZX direction. --- .../passes/scheduling/calibration_creators.py | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 3ea4087b23da..96eb0a71fd45 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -115,7 +115,8 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> P def get_calibration(self, params: List, qubits: List) -> Schedule: """ Args: - params: Name of the instruction with the rotation angle in it. + params: Name of the instruction with the rotation angle in it. The first qubit is + the control and the second is the target. qubits: List of qubits for which to get the schedules. Returns: @@ -129,6 +130,9 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: cx_sched = self._inst_map.get('cx', qubits=(q1, q2)) zx_theta = Schedule(name='zx(%.3f)' % theta) + if theta == 0.0: + return zx_theta + crs, comp_tones, shift_phases = [], [], [] control, target = None, None @@ -166,14 +170,29 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) - if theta != 0.0: - zx_theta = zx_theta.insert(0, cr1) - zx_theta = zx_theta.insert(0, comp1) - zx_theta = zx_theta.insert(comp1.duration, echo_x) - time = comp1.duration + echo_x.duration - zx_theta = zx_theta.insert(time, cr2) - zx_theta = zx_theta.insert(time, comp2) - time = 2*comp1.duration + echo_x.duration - zx_theta = zx_theta.insert(time, echo_x) - - return zx_theta + zx_theta = zx_theta.insert(0, cr1) + zx_theta = zx_theta.insert(0, comp1) + zx_theta = zx_theta.insert(comp1.duration, echo_x) + time = comp1.duration + echo_x.duration + zx_theta = zx_theta.insert(time, cr2) + zx_theta = zx_theta.insert(time, comp2) + time = 2*comp1.duration + echo_x.duration + zx_theta = zx_theta.insert(time, echo_x) + + # Reverse direction of the ZX with Hadamard gates + if control == qubits[0]: + return zx_theta + else: + rzc = self._inst_map.get('rz', [control], np.pi / 2) + sxc = self._inst_map.get('sx', [control]) + rzt = self._inst_map.get('rz', [target], np.pi / 2) + sxt = self._inst_map.get('sx', [target]) + h_sched = Schedule(name='hadamards') + h_sched = h_sched.insert(0, rzc) + h_sched = h_sched.insert(0, sxc) + h_sched = h_sched.insert(sxc.duration, rzc) + h_sched = h_sched.insert(0, rzt) + h_sched = h_sched.insert(0, sxt) + h_sched = h_sched.insert(sxc.duration, rzt) + zx_theta = h_sched.append(zx_theta) + return zx_theta.append(h_sched) From e49c85a1dfcad0114706f06b65a538cd9cde789b Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 17:12:55 +0100 Subject: [PATCH 11/38] * Fix cyclical import. * Added template. --- qiskit/circuit/library/templates/__init__.py | 3 +- .../circuit/library/templates/zx_templates.py | 28 ++++++++++++++++--- .../passes/scheduling/calibration_adder.py | 2 +- test/python/transpiler/test_zx_templates.py | 7 +++-- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index b8b07330dee4..60681cbe0514 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -65,7 +65,8 @@ from .nct.template_nct_9d_9 import template_nct_9d_9 from .nct.template_nct_9d_10 import template_nct_9d_10 -from .zx_templates import zx_zy +from .zx_templates import zx_yz +from .zx_templates import zx_xz from .zx_templates import zx_cy from .zx_templates import zx_zz1 from .zx_templates import zx_zz2 diff --git a/qiskit/circuit/library/templates/zx_templates.py b/qiskit/circuit/library/templates/zx_templates.py index 2966794a5e59..947bf7ae7d70 100644 --- a/qiskit/circuit/library/templates/zx_templates.py +++ b/qiskit/circuit/library/templates/zx_templates.py @@ -75,7 +75,27 @@ def zx_zz2(theta: float = None): return qc -def zx_zy(theta: float = None): +def zx_xz(theta: float = None): + """ZY template.""" + if theta is None: + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(1, 1) + qc.rx(theta, 0) + qc.cx(1, 1) + + qc.rz(np.pi / 2, 0) + qc.rx(np.pi / 2, 0) + qc.rz(np.pi / 2, 0) + qc.rzx(-1*theta, 0, 1) + qc.rz(np.pi / 2, 0) + qc.rx(np.pi / 2, 0) + qc.rz(np.pi / 2, 0) + return qc + + +def zx_yz(theta: float = None): """ZY template.""" if theta is None: theta = Parameter('ϴ') @@ -100,10 +120,10 @@ def zx_cy(theta: float = None): circ.cx(0, 1) circ.ry(theta, 1) circ.cx(0, 1) - circ.ry(-theta, 1) + circ.ry(-1*theta, 1) circ.rz(-np.pi / 2, 1) circ.rx(theta, 1) - circ.rzx(-theta, 0, 1) + circ.rzx(-1*theta, 0, 1) circ.rz(np.pi / 2, 1) return circ @@ -124,7 +144,7 @@ def zx_templates(template_list: List[str] = None): if 'zz2' in template_list: templates.append(zx_zz2()) if 'zy' in template_list: - templates.append(zx_zy()) + templates.append(zx_yz()) cost_dict = {'rzx': 0, 'cx': 6, 'rz': 1, 'sx': 2, 'p': 0, 'h': 1, 'rx': 1, 'ry': 1} diff --git a/qiskit/transpiler/passes/scheduling/calibration_adder.py b/qiskit/transpiler/passes/scheduling/calibration_adder.py index c6e43b69c674..0c8f2c259016 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_adder.py +++ b/qiskit/transpiler/passes/scheduling/calibration_adder.py @@ -13,7 +13,7 @@ """Scheduling transformation pass to add calibrations to the dag.""" from qiskit.transpiler.basepasses import TransformationPass -from qiskit.transpiler.passes.scheduling import CalibrationCreator +from qiskit.transpiler.passes.scheduling.calibration_creators import CalibrationCreator class CalibrationAdder(TransformationPass): diff --git a/test/python/transpiler/test_zx_templates.py b/test/python/transpiler/test_zx_templates.py index 3736f63fe71f..baf904bf4a63 100644 --- a/test/python/transpiler/test_zx_templates.py +++ b/test/python/transpiler/test_zx_templates.py @@ -15,7 +15,7 @@ import numpy as np from qiskit.quantum_info import Operator -from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_zy, zx_cy +from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_yz, zx_cy, zx_xz from qiskit.test import QiskitTestCase @@ -25,7 +25,10 @@ class TestZXTemplates(QiskitTestCase): def test_templates(self): """Test that the templates compose to the identity.""" - self.assertTrue(np.allclose(Operator(zx_zy(0.456)).data, np.eye(4))) + self.assertTrue(np.allclose(Operator(zx_yz(0.456)).data, np.eye(4))) + + data = Operator(zx_xz(0.456)).data + self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) data = Operator(zx_cy(0.456)).data self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) From 24471980f3022e3b401ef32ebcbac6c876e451dc Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sat, 30 Jan 2021 18:05:17 +0100 Subject: [PATCH 12/38] * Fix tests. --- qiskit/circuit/library/templates/zx_templates.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/circuit/library/templates/zx_templates.py b/qiskit/circuit/library/templates/zx_templates.py index 947bf7ae7d70..f0d24439bed1 100644 --- a/qiskit/circuit/library/templates/zx_templates.py +++ b/qiskit/circuit/library/templates/zx_templates.py @@ -81,9 +81,9 @@ def zx_xz(theta: float = None): theta = Parameter('ϴ') qc = QuantumCircuit(2) - qc.cx(1, 1) - qc.rx(theta, 0) - qc.cx(1, 1) + qc.cx(1, 0) + qc.rx(theta, 1) + qc.cx(1, 0) qc.rz(np.pi / 2, 0) qc.rx(np.pi / 2, 0) From 1d5f472f87740ebac629ecdc63a5dd72d7914344 Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Sun, 31 Jan 2021 09:14:23 +0100 Subject: [PATCH 13/38] Update qiskit/transpiler/passes/scheduling/calibration_creators.py Co-authored-by: Lauren Capelluto --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 96eb0a71fd45..1b2bd3c87959 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -53,7 +53,8 @@ def __init__(self, backend: basebackend): QiskitError: if open pulse is not supported by the backend. """ if not backend.configuration().open_pulse: - raise QiskitError(backend.name() + ' is not a pulse backend.') + raise QiskitError('Calibrations can only be added to Pulse-enabled backends, ' + 'but {0} is not enabled with Pulse.'.format(backend.name())) self._inst_map = backend.defaults().instruction_schedule_map self._config = backend.configuration() From 5e22d9b193427d572a5b863d9f77ced6663e1b8c Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Sun, 31 Jan 2021 09:15:40 +0100 Subject: [PATCH 14/38] Update qiskit/transpiler/passes/scheduling/calibration_adder.py Co-authored-by: Lauren Capelluto --- qiskit/transpiler/passes/scheduling/calibration_adder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_adder.py b/qiskit/transpiler/passes/scheduling/calibration_adder.py index 0c8f2c259016..0c85d0681faa 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_adder.py +++ b/qiskit/transpiler/passes/scheduling/calibration_adder.py @@ -26,7 +26,7 @@ def __init__(self, calibration_creator: CalibrationCreator): schedules that will be added to the circuit. """ super().__init__() - self._calibration_adder = calibration_creator + self._calibration_creator = calibration_creator def run(self, dag): """Run the calibration adder pass on `dag`. From 3ce6a82709a7a26f4b77379615717de3f33b17b8 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sun, 31 Jan 2021 09:16:38 +0100 Subject: [PATCH 15/38] * Propagated the change self._calibration_adder to self._calibration_creator in calibration_adder.py. --- qiskit/transpiler/passes/scheduling/calibration_adder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_adder.py b/qiskit/transpiler/passes/scheduling/calibration_adder.py index 0c85d0681faa..8f2758754f28 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_adder.py +++ b/qiskit/transpiler/passes/scheduling/calibration_adder.py @@ -39,11 +39,11 @@ def run(self, dag): """ for node in dag.nodes(): if node.type == 'op': - if self._calibration_adder.supported(node.op): + if self._calibration_creator.supported(node.op): params = node.op.params qubits = [_.index for _ in node.qargs] - schedule = self._calibration_adder.get_calibration(params, qubits) + schedule = self._calibration_creator.get_calibration(params, qubits) dag.add_calibration(node.op, qubits, schedule, params=params) From a96335aff6882043197715b2a8da471e79fe146e Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sun, 31 Jan 2021 09:37:32 +0100 Subject: [PATCH 16/38] * Style and lint fix. --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 1b2bd3c87959..36c347d01b51 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -54,7 +54,7 @@ def __init__(self, backend: basebackend): """ if not backend.configuration().open_pulse: raise QiskitError('Calibrations can only be added to Pulse-enabled backends, ' - 'but {0} is not enabled with Pulse.'.format(backend.name())) + 'but {0} is not enabled with Pulse.'.format(backend.name())) self._inst_map = backend.defaults().instruction_schedule_map self._config = backend.configuration() From 8179210a7def23db2fd839322ae111426a00be73 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sun, 31 Jan 2021 12:22:47 +0100 Subject: [PATCH 17/38] * Added new template. * Fixed minor bug. --- qiskit/circuit/library/templates/__init__.py | 1 + .../circuit/library/templates/zx_templates.py | 37 +++++++++++++++++-- test/python/transpiler/test_zx_templates.py | 3 ++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index 60681cbe0514..5766e556dca6 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -70,4 +70,5 @@ from .zx_templates import zx_cy from .zx_templates import zx_zz1 from .zx_templates import zx_zz2 +from .zx_templates import zx_zz3 from .zx_templates import zx_templates diff --git a/qiskit/circuit/library/templates/zx_templates.py b/qiskit/circuit/library/templates/zx_templates.py index f0d24439bed1..c4e44e4b25a4 100644 --- a/qiskit/circuit/library/templates/zx_templates.py +++ b/qiskit/circuit/library/templates/zx_templates.py @@ -75,6 +75,31 @@ def zx_zz2(theta: float = None): return qc +def zx_zz3(theta: float = None): + """ZZ template is p gate.""" + if theta is None: + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.rz(theta, 1) + qc.cx(0, 1) + qc.rz(-1*theta, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + qc.rx(theta, 1) + qc.rzx(-1*theta, 0, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + return qc + + def zx_xz(theta: float = None): """ZY template.""" if theta is None: @@ -136,17 +161,23 @@ def zx_templates(template_list: List[str] = None): """ if template_list is None: - template_list = ['zz1', 'zz2', 'zy'] + template_list = ['zz1', 'zz2', 'zz3', 'yz', 'xz', 'cy'] templates = [] if 'zz1' in template_list: templates.append(zx_zz1()) if 'zz2' in template_list: templates.append(zx_zz2()) - if 'zy' in template_list: + if 'zz3' in template_list: + templates.append(zx_zz3()) + if 'yz' in template_list: templates.append(zx_yz()) + if 'xz' in template_list: + templates.append(zx_xz()) + if 'cy' in template_list: + templates.append(zx_cy()) - cost_dict = {'rzx': 0, 'cx': 6, 'rz': 1, 'sx': 2, 'p': 0, 'h': 1, 'rx': 1, 'ry': 1} + cost_dict = {'rzx': 0, 'cx': 6, 'rz': 0, 'sx': 1, 'p': 0, 'h': 1, 'rx': 1, 'ry': 1} zx_dict = {'template_list': templates, 'user_cost_dict': cost_dict} diff --git a/test/python/transpiler/test_zx_templates.py b/test/python/transpiler/test_zx_templates.py index baf904bf4a63..2a3c97d4445b 100644 --- a/test/python/transpiler/test_zx_templates.py +++ b/test/python/transpiler/test_zx_templates.py @@ -38,3 +38,6 @@ def test_templates(self): data = Operator(zx_zz2(0.456)).data self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) + + data = Operator(zx_zz3(0.456)).data + self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) From c4f3af95273b696202b9071871290a85d60d6bd1 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Sun, 31 Jan 2021 16:37:37 +0100 Subject: [PATCH 18/38] * Fixed missing import. --- test/python/transpiler/test_zx_templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/transpiler/test_zx_templates.py b/test/python/transpiler/test_zx_templates.py index 2a3c97d4445b..62fd73630d1a 100644 --- a/test/python/transpiler/test_zx_templates.py +++ b/test/python/transpiler/test_zx_templates.py @@ -15,7 +15,7 @@ import numpy as np from qiskit.quantum_info import Operator -from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_yz, zx_cy, zx_xz +from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_yz, zx_cy, zx_xz, zx_zz3 from qiskit.test import QiskitTestCase From 8f980eb3494db14a75037897fb6b80255ce78355 Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Tue, 2 Feb 2021 17:19:21 +0100 Subject: [PATCH 19/38] Update qiskit/transpiler/passes/scheduling/calibration_creators.py Co-authored-by: Ali Javadi-Abhari --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 36c347d01b51..3d141a8f83a9 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -38,7 +38,7 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: class ZXScheduleBuilder(CalibrationCreator): """ - Creates calibrations for ZX(theta) by stretching and compressing + Creates calibrations for RZX(theta) by stretching and compressing Gaussian square pulses. """ From 966f4842c72ca1ea6f7b1ed09d6061f9c30bf20e Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 17:30:26 +0100 Subject: [PATCH 20/38] * Renamed ZXScheduleBuilder to RZXCalibrationBuilder. --- qiskit/transpiler/passes/scheduling/__init__.py | 2 +- qiskit/transpiler/passes/scheduling/calibration_creators.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/transpiler/passes/scheduling/__init__.py b/qiskit/transpiler/passes/scheduling/__init__.py index 3d3b6ea777b9..b83ed3f4bead 100644 --- a/qiskit/transpiler/passes/scheduling/__init__.py +++ b/qiskit/transpiler/passes/scheduling/__init__.py @@ -15,5 +15,5 @@ from .alap import ALAPSchedule from .asap import ASAPSchedule from .time_unit_analysis import TimeUnitAnalysis -from .calibration_creators import CalibrationCreator, ZXScheduleBuilder +from .calibration_creators import CalibrationCreator, RZXCalibrationBuilder from .calibration_adder import CalibrationAdder diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 3d141a8f83a9..bb3ff060fe19 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -36,15 +36,15 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: """Gets the calibrated schedule for the given qubits and parameters.""" -class ZXScheduleBuilder(CalibrationCreator): +class RZXCalibrationBuilder(CalibrationCreator): """ - Creates calibrations for RZX(theta) by stretching and compressing + Creates calibrations for RZXGate(theta) by stretching and compressing Gaussian square pulses. """ def __init__(self, backend: basebackend): """ - Initializes a Parameterized Controlled-Z gate builder. + Initializes a RZXGate calibration builder. Args: backend: Backend for which to construct the gates. From fb8e01b199f29c2ac30c5f9c67c961b74b4fd310 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 17:42:44 +0100 Subject: [PATCH 21/38] * Renamed zx to rzx. * Fixed incorrect docstring. --- qiskit/circuit/library/templates/__init__.py | 14 ++++----- .../{zx_templates.py => rzx_templates.py} | 30 +++++++++---------- .../passes/scheduling/calibration_creators.py | 30 +++++++++---------- ..._zx_templates.py => test_rzx_templates.py} | 16 +++++----- 4 files changed, 45 insertions(+), 45 deletions(-) rename qiskit/circuit/library/templates/{zx_templates.py => rzx_templates.py} (85%) rename test/python/transpiler/{test_zx_templates.py => test_rzx_templates.py} (71%) diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index 5766e556dca6..10a689772cb2 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -65,10 +65,10 @@ from .nct.template_nct_9d_9 import template_nct_9d_9 from .nct.template_nct_9d_10 import template_nct_9d_10 -from .zx_templates import zx_yz -from .zx_templates import zx_xz -from .zx_templates import zx_cy -from .zx_templates import zx_zz1 -from .zx_templates import zx_zz2 -from .zx_templates import zx_zz3 -from .zx_templates import zx_templates +from .rzx_templates import rzx_yz +from .rzx_templates import rzx_xz +from .rzx_templates import rzx_cy +from .rzx_templates import rzx_zz1 +from .rzx_templates import rzx_zz2 +from .rzx_templates import rzx_zz3 +from .rzx_templates import rzx_templates diff --git a/qiskit/circuit/library/templates/zx_templates.py b/qiskit/circuit/library/templates/rzx_templates.py similarity index 85% rename from qiskit/circuit/library/templates/zx_templates.py rename to qiskit/circuit/library/templates/rzx_templates.py index c4e44e4b25a4..8ce45c7e43c3 100644 --- a/qiskit/circuit/library/templates/zx_templates.py +++ b/qiskit/circuit/library/templates/rzx_templates.py @@ -20,7 +20,7 @@ from qiskit.circuit import Parameter, QuantumCircuit -def zx_zz1(theta: float = None): +def rzx_zz1(theta: float = None): """ZZ template with rz gate.""" if theta is None: theta = Parameter('ϴ') @@ -50,7 +50,7 @@ def zx_zz1(theta: float = None): return qc -def zx_zz2(theta: float = None): +def rzx_zz2(theta: float = None): """ZZ template is p gate.""" if theta is None: theta = Parameter('ϴ') @@ -75,7 +75,7 @@ def zx_zz2(theta: float = None): return qc -def zx_zz3(theta: float = None): +def rzx_zz3(theta: float = None): """ZZ template is p gate.""" if theta is None: theta = Parameter('ϴ') @@ -100,7 +100,7 @@ def zx_zz3(theta: float = None): return qc -def zx_xz(theta: float = None): +def rzx_xz(theta: float = None): """ZY template.""" if theta is None: theta = Parameter('ϴ') @@ -120,7 +120,7 @@ def zx_xz(theta: float = None): return qc -def zx_yz(theta: float = None): +def rzx_yz(theta: float = None): """ZY template.""" if theta is None: theta = Parameter('ϴ') @@ -136,7 +136,7 @@ def zx_yz(theta: float = None): return circ -def zx_cy(theta: float = None): +def rzx_cy(theta: float = None): """ZY template.""" if theta is None: theta = Parameter('ϴ') @@ -154,7 +154,7 @@ def zx_cy(theta: float = None): return circ -def zx_templates(template_list: List[str] = None): +def rzx_templates(template_list: List[str] = None): """ Convenience function to get the cost_dict and templates for template matching. @@ -165,20 +165,20 @@ def zx_templates(template_list: List[str] = None): templates = [] if 'zz1' in template_list: - templates.append(zx_zz1()) + templates.append(rzx_zz1()) if 'zz2' in template_list: - templates.append(zx_zz2()) + templates.append(rzx_zz2()) if 'zz3' in template_list: - templates.append(zx_zz3()) + templates.append(rzx_zz3()) if 'yz' in template_list: - templates.append(zx_yz()) + templates.append(rzx_yz()) if 'xz' in template_list: - templates.append(zx_xz()) + templates.append(rzx_xz()) if 'cy' in template_list: - templates.append(zx_cy()) + templates.append(rzx_cy()) cost_dict = {'rzx': 0, 'cx': 6, 'rz': 0, 'sx': 1, 'p': 0, 'h': 1, 'rx': 1, 'ry': 1} - zx_dict = {'template_list': templates, 'user_cost_dict': cost_dict} + rzx_dict = {'template_list': templates, 'user_cost_dict': cost_dict} - return zx_dict + return rzx_dict diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index bb3ff060fe19..ba8361b4bcb1 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -116,12 +116,12 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> P def get_calibration(self, params: List, qubits: List) -> Schedule: """ Args: - params: Name of the instruction with the rotation angle in it. The first qubit is + params: Parameters of the RZXGate(theta). I.e. params[0] is theta. + qubits: List of qubits for which to get the schedules. The first qubit is the control and the second is the target. - qubits: List of qubits for which to get the schedules. Returns: - schedule: The calibration schedule for the instruction with name. + schedule: The calibration schedule for the RZXGate(theta). Raises: QiskitError: if the the control and target qubits cannot be identified. @@ -129,10 +129,10 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: theta = params[0] q1, q2 = qubits[0], qubits[1] cx_sched = self._inst_map.get('cx', qubits=(q1, q2)) - zx_theta = Schedule(name='zx(%.3f)' % theta) + rzx_theta = Schedule(name='rzx(%.3f)' % theta) if theta == 0.0: - return zx_theta + return rzx_theta crs, comp_tones, shift_phases = [], [], [] control, target = None, None @@ -163,7 +163,7 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: # Build the schedule for inst in shift_phases: - zx_theta = zx_theta.insert(0, inst) + rzx_theta = rzx_theta.insert(0, inst) # Stretch/compress the CR gates and compensation tones cr1 = self.rescale_cr_inst(crs[0][1], theta) @@ -171,18 +171,18 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) - zx_theta = zx_theta.insert(0, cr1) - zx_theta = zx_theta.insert(0, comp1) - zx_theta = zx_theta.insert(comp1.duration, echo_x) + rzx_theta = rzx_theta.insert(0, cr1) + rzx_theta = rzx_theta.insert(0, comp1) + rzx_theta = rzx_theta.insert(comp1.duration, echo_x) time = comp1.duration + echo_x.duration - zx_theta = zx_theta.insert(time, cr2) - zx_theta = zx_theta.insert(time, comp2) + rzx_theta = rzx_theta.insert(time, cr2) + rzx_theta = rzx_theta.insert(time, comp2) time = 2*comp1.duration + echo_x.duration - zx_theta = zx_theta.insert(time, echo_x) + rzx_theta = rzx_theta.insert(time, echo_x) # Reverse direction of the ZX with Hadamard gates if control == qubits[0]: - return zx_theta + return rzx_theta else: rzc = self._inst_map.get('rz', [control], np.pi / 2) sxc = self._inst_map.get('sx', [control]) @@ -195,5 +195,5 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: h_sched = h_sched.insert(0, rzt) h_sched = h_sched.insert(0, sxt) h_sched = h_sched.insert(sxc.duration, rzt) - zx_theta = h_sched.append(zx_theta) - return zx_theta.append(h_sched) + rzx_theta = h_sched.append(rzx_theta) + return rzx_theta.append(h_sched) diff --git a/test/python/transpiler/test_zx_templates.py b/test/python/transpiler/test_rzx_templates.py similarity index 71% rename from test/python/transpiler/test_zx_templates.py rename to test/python/transpiler/test_rzx_templates.py index 62fd73630d1a..7eba4e8972ba 100644 --- a/test/python/transpiler/test_zx_templates.py +++ b/test/python/transpiler/test_rzx_templates.py @@ -15,29 +15,29 @@ import numpy as np from qiskit.quantum_info import Operator -from qiskit.circuit.library.templates import zx_zz1, zx_zz2, zx_yz, zx_cy, zx_xz, zx_zz3 +from qiskit.circuit.library.templates import rzx_zz1, rzx_zz2, rzx_yz, rzx_cy, rzx_xz, rzx_zz3 from qiskit.test import QiskitTestCase -class TestZXTemplates(QiskitTestCase): +class TestRZXTemplates(QiskitTestCase): """Test the parametric templates.""" def test_templates(self): """Test that the templates compose to the identity.""" - self.assertTrue(np.allclose(Operator(zx_yz(0.456)).data, np.eye(4))) + self.assertTrue(np.allclose(Operator(rzx_yz(0.456)).data, np.eye(4))) - data = Operator(zx_xz(0.456)).data + data = Operator(rzx_xz(0.456)).data self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) - data = Operator(zx_cy(0.456)).data + data = Operator(rzx_cy(0.456)).data self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) - data = Operator(zx_zz1(0.456)).data + data = Operator(rzx_zz1(0.456)).data self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) - data = Operator(zx_zz2(0.456)).data + data = Operator(rzx_zz2(0.456)).data self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) - data = Operator(zx_zz3(0.456)).data + data = Operator(rzx_zz3(0.456)).data self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) From 4e84b70fcf3a7582344be37a07415a7c033f8993 Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Tue, 2 Feb 2021 17:46:19 +0100 Subject: [PATCH 22/38] Update qiskit/circuit/library/templates/rzx_templates.py --- qiskit/circuit/library/templates/rzx_templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/library/templates/rzx_templates.py b/qiskit/circuit/library/templates/rzx_templates.py index 8ce45c7e43c3..e905aa809a9f 100644 --- a/qiskit/circuit/library/templates/rzx_templates.py +++ b/qiskit/circuit/library/templates/rzx_templates.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """ -A library of ZX based two qubit gates. +A template library based on RZXGate. """ from typing import List From 6cdccd45609813884227ac62b308f5d0a4864d05 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 17:50:01 +0100 Subject: [PATCH 23/38] * Fixed warning message length. --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index ba8361b4bcb1..f89ec25844a2 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -111,7 +111,8 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> P GaussianSquare(amp=amp * amp_scale, width=0, sigma=sigma, duration=duration), channel=instruction.channel) else: - raise QiskitError('ZXScheduleBuilder builder only stretches/compresses GaussianSquare.') + raise QiskitError('RZXCalibrationBuilder builder only ' + 'stretches/compresses GaussianSquare.') def get_calibration(self, params: List, qubits: List) -> Schedule: """ From ec16a62aa51fadedb71518d608f83bc953a9d244 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 17:51:29 +0100 Subject: [PATCH 24/38] * Improved error message. --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index f89ec25844a2..c03558bb3fb7 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -111,8 +111,7 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> P GaussianSquare(amp=amp * amp_scale, width=0, sigma=sigma, duration=duration), channel=instruction.channel) else: - raise QiskitError('RZXCalibrationBuilder builder only ' - 'stretches/compresses GaussianSquare.') + raise QiskitError('RZXCalibrationBuilder only stretches/compresses GaussianSquare.') def get_calibration(self, params: List, qubits: List) -> Schedule: """ From 4d6d8d63befe64f4e4a4f907c6bbe264478bceb7 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 20:17:00 +0100 Subject: [PATCH 25/38] * Moved rzx templates to their own files. --- qiskit/circuit/library/templates/__init__.py | 14 +- .../circuit/library/templates/rzx/__init__.py | 25 ++++ .../circuit/library/templates/rzx/rzx_cy.py | 42 ++++++ .../circuit/library/templates/rzx/rzx_xz.py | 49 ++++++ .../circuit/library/templates/rzx/rzx_yz.py | 40 +++++ .../circuit/library/templates/rzx/rzx_zz1.py | 64 ++++++++ .../circuit/library/templates/rzx/rzx_zz2.py | 54 +++++++ .../circuit/library/templates/rzx/rzx_zz3.py | 54 +++++++ .../library/templates/rzx_templates.py | 139 +----------------- test/python/transpiler/test_rzx_templates.py | 2 +- 10 files changed, 337 insertions(+), 146 deletions(-) create mode 100644 qiskit/circuit/library/templates/rzx/__init__.py create mode 100644 qiskit/circuit/library/templates/rzx/rzx_cy.py create mode 100644 qiskit/circuit/library/templates/rzx/rzx_xz.py create mode 100644 qiskit/circuit/library/templates/rzx/rzx_yz.py create mode 100644 qiskit/circuit/library/templates/rzx/rzx_zz1.py create mode 100644 qiskit/circuit/library/templates/rzx/rzx_zz2.py create mode 100644 qiskit/circuit/library/templates/rzx/rzx_zz3.py diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index 10a689772cb2..13b3dfbf8f57 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -65,10 +65,10 @@ from .nct.template_nct_9d_9 import template_nct_9d_9 from .nct.template_nct_9d_10 import template_nct_9d_10 -from .rzx_templates import rzx_yz -from .rzx_templates import rzx_xz -from .rzx_templates import rzx_cy -from .rzx_templates import rzx_zz1 -from .rzx_templates import rzx_zz2 -from .rzx_templates import rzx_zz3 -from .rzx_templates import rzx_templates +from .rzx.rzx_yz import rzx_yz +from .rzx.rzx_xz import rzx_xz +from .rzx.rzx_cy import rzx_cy +from .rzx.rzx_zz1 import rzx_zz1 +from .rzx.rzx_zz2 import rzx_zz2 +from .rzx.rzx_zz3 import rzx_zz3 +from .rzx.rzx_yz import rzx_templates diff --git a/qiskit/circuit/library/templates/rzx/__init__.py b/qiskit/circuit/library/templates/rzx/__init__.py new file mode 100644 index 000000000000..05895d875c4e --- /dev/null +++ b/qiskit/circuit/library/templates/rzx/__init__.py @@ -0,0 +1,25 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +A library of template circuits. + +Templates are circuits that compute the identity. They find use +in circuit optimization where matching part of the template allows the compiler +to replace the match with the inverse of the remainder from the template. +""" +from .rzx_yz import rzx_yz +from .rzx_xz import rzx_xz +from .rzx_cy import rzx_cy +from .rzx_zz1 import rzx_zz1 +from .rzx_zz2 import rzx_zz2 +from .rzx_zz3 import rzx_zz3 diff --git a/qiskit/circuit/library/templates/rzx/rzx_cy.py b/qiskit/circuit/library/templates/rzx/rzx_cy.py new file mode 100644 index 000000000000..13377aa79660 --- /dev/null +++ b/qiskit/circuit/library/templates/rzx/rzx_cy.py @@ -0,0 +1,42 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +RZX based template for CX - RYGate - CX +.. parsed-literal:: + ┌──────────┐ +q_0: ──■─────────────■─────────────────────────────────┤0 ├─────────── + ┌─┴─┐┌───────┐┌─┴─┐┌────────┐┌──────────┐┌───────┐│ RZX(-ϴ) │┌─────────┐ +q_1: ┤ X ├┤ RY(ϴ) ├┤ X ├┤ RY(-ϴ) ├┤ RZ(-π/2) ├┤ RX(ϴ) ├┤1 ├┤ RZ(π/2) ├ + └───┘└───────┘└───┘└────────┘└──────────┘└───────┘└──────────┘└─────────┘ +""" + +from qiskit.circuit import Parameter, QuantumCircuit +import numpy as np + + +def rzx_cy(theta: float = None): + """ZY template.""" + if theta is None: + theta = Parameter('ϴ') + + circ = QuantumCircuit(2) + circ.cx(0, 1) + circ.ry(theta, 1) + circ.cx(0, 1) + circ.ry(-1*theta, 1) + circ.rz(-np.pi / 2, 1) + circ.rx(theta, 1) + circ.rzx(-1*theta, 0, 1) + circ.rz(np.pi / 2, 1) + + return circ diff --git a/qiskit/circuit/library/templates/rzx/rzx_xz.py b/qiskit/circuit/library/templates/rzx/rzx_xz.py new file mode 100644 index 000000000000..857c3bc6f67f --- /dev/null +++ b/qiskit/circuit/library/templates/rzx/rzx_xz.py @@ -0,0 +1,49 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +RZX based template for CX - RXGate - CX +.. parsed-literal:: + ┌───┐ ┌───┐┌─────────┐┌─────────┐┌─────────┐┌──────────┐» +q_0: ┤ X ├─────────┤ X ├┤ RZ(π/2) ├┤ RX(π/2) ├┤ RZ(π/2) ├┤0 ├» + └─┬─┘┌───────┐└─┬─┘└─────────┘└─────────┘└─────────┘│ RZX(-ϴ) │» +q_1: ──■──┤ RX(ϴ) ├──■───────────────────────────────────┤1 ├» + └───────┘ └──────────┘» +« ┌─────────┐┌─────────┐┌─────────┐ +«q_0: ┤ RZ(π/2) ├┤ RX(π/2) ├┤ RZ(π/2) ├ +« └─────────┘└─────────┘└─────────┘ +«q_1: ───────────────────────────────── +« +""" + +from qiskit.circuit import Parameter, QuantumCircuit +import numpy as np + + +def rzx_xz(theta: float = None): + """Template for CX - RXGate - CX.""" + if theta is None: + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(1, 0) + qc.rx(theta, 1) + qc.cx(1, 0) + + qc.rz(np.pi / 2, 0) + qc.rx(np.pi / 2, 0) + qc.rz(np.pi / 2, 0) + qc.rzx(-1*theta, 0, 1) + qc.rz(np.pi / 2, 0) + qc.rx(np.pi / 2, 0) + qc.rz(np.pi / 2, 0) + return qc diff --git a/qiskit/circuit/library/templates/rzx/rzx_yz.py b/qiskit/circuit/library/templates/rzx/rzx_yz.py new file mode 100644 index 000000000000..a0ae58f395be --- /dev/null +++ b/qiskit/circuit/library/templates/rzx/rzx_yz.py @@ -0,0 +1,40 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +RZX based template for CX - RYGate - CX +.. parsed-literal:: + ┌────────┐ ┌─────────┐┌─────────┐┌──────────┐ +q_0: ──■──┤ RY(-ϴ) ├──■──┤ RX(π/2) ├┤0 ├┤ RX(-π/2) ├ + ┌─┴─┐└────────┘┌─┴─┐└─────────┘│ RZX(ϴ) │└──────────┘ +q_1: ┤ X ├──────────┤ X ├───────────┤1 ├──────────── + └───┘ └───┘ └─────────┘ +""" + +from qiskit.circuit import Parameter, QuantumCircuit +import numpy as np + + +def rzx_yz(theta: float = None): + """Template for CX - RYGate - CX.""" + if theta is None: + theta = Parameter('ϴ') + + circ = QuantumCircuit(2) + circ.cx(0, 1) + circ.ry(-1*theta, 0) + circ.cx(0, 1) + circ.rx(np.pi / 2, 0) + circ.rzx(theta, 0, 1) + circ.rx(-np.pi / 2, 0) + + return circ diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz1.py b/qiskit/circuit/library/templates/rzx/rzx_zz1.py new file mode 100644 index 000000000000..f97dbcaa8528 --- /dev/null +++ b/qiskit/circuit/library/templates/rzx/rzx_zz1.py @@ -0,0 +1,64 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +RZX based template for CX - phase - CX +.. parsed-literal:: + » +q_0: ──■────────────────────────────────────────────■───────────────────────» + ┌─┴─┐┌───────┐┌────┐┌───────┐┌────┐┌────────┐┌─┴─┐┌────────┐┌─────────┐» +q_1: ┤ X ├┤ RZ(ϴ) ├┤ √X ├┤ RZ(π) ├┤ √X ├┤ RZ(3π) ├┤ X ├┤ RZ(-ϴ) ├┤ RZ(π/2) ├» + └───┘└───────┘└────┘└───────┘└────┘└────────┘└───┘└────────┘└─────────┘» +« ┌──────────┐ » +«q_0: ───────────────────────────────┤0 ├──────────────────────» +« ┌─────────┐┌─────────┐┌───────┐│ RZX(-ϴ) │┌─────────┐┌─────────┐» +«q_1: ┤ RX(π/2) ├┤ RZ(π/2) ├┤ RX(ϴ) ├┤1 ├┤ RZ(π/2) ├┤ RX(π/2) ├» +« └─────────┘└─────────┘└───────┘└──────────┘└─────────┘└─────────┘» +« +«q_0: ─────────── +« ┌─────────┐ +«q_1: ┤ RZ(π/2) ├ +« └─────────┘ +""" + +from qiskit.circuit import Parameter, QuantumCircuit +import numpy as np + + +def rzx_zz1(theta: float = None): + """ZZ template with RZGate.""" + if theta is None: + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.rz(theta, 1) + qc.sx(1) + qc.rz(np.pi, 1) + qc.sx(1) + qc.rz(3 * np.pi, 1) + qc.cx(0, 1) + qc.rz(-1*theta, 1) + + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + qc.rx(theta, 1) + qc.rzx(-1*theta, 0, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + return qc diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz2.py b/qiskit/circuit/library/templates/rzx/rzx_zz2.py new file mode 100644 index 000000000000..b7805361ebd5 --- /dev/null +++ b/qiskit/circuit/library/templates/rzx/rzx_zz2.py @@ -0,0 +1,54 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +RZX based template for CX - phase - CX +.. parsed-literal:: + » +q_0: ──■────────────■─────────────────────────────────────────────────────» + ┌─┴─┐┌──────┐┌─┴─┐┌───────┐┌─────────┐┌─────────┐┌─────────┐┌───────┐» +q_1: ┤ X ├┤ P(ϴ) ├┤ X ├┤ P(-ϴ) ├┤ RZ(π/2) ├┤ RX(π/2) ├┤ RZ(π/2) ├┤ RX(ϴ) ├» + └───┘└──────┘└───┘└───────┘└─────────┘└─────────┘└─────────┘└───────┘» +« ┌──────────┐ +«q_0: ┤0 ├───────────────────────────────── +« │ RZX(-ϴ) │┌─────────┐┌─────────┐┌─────────┐ +«q_1: ┤1 ├┤ RZ(π/2) ├┤ RX(π/2) ├┤ RZ(π/2) ├ +« └──────────┘└─────────┘└─────────┘└─────────┘ +""" + +from qiskit.circuit import Parameter, QuantumCircuit +import numpy as np + + +def rzx_zz2(theta: float = None): + """ZZ template with PhaseGate.""" + if theta is None: + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.p(theta, 1) + qc.cx(0, 1) + qc.p(-1*theta, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + qc.rx(theta, 1) + qc.rzx(-1*theta, 0, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + return qc diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz3.py b/qiskit/circuit/library/templates/rzx/rzx_zz3.py new file mode 100644 index 000000000000..948a19d48c41 --- /dev/null +++ b/qiskit/circuit/library/templates/rzx/rzx_zz3.py @@ -0,0 +1,54 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +RZX based template for CX - phase - CX +.. parsed-literal:: + » +q_0: ──■─────────────■──────────────────────────────────────────────────────» + ┌─┴─┐┌───────┐┌─┴─┐┌────────┐┌─────────┐┌─────────┐┌─────────┐┌───────┐» +q_1: ┤ X ├┤ RZ(ϴ) ├┤ X ├┤ RZ(-ϴ) ├┤ RZ(π/2) ├┤ RX(π/2) ├┤ RZ(π/2) ├┤ RX(ϴ) ├» + └───┘└───────┘└───┘└────────┘└─────────┘└─────────┘└─────────┘└───────┘» +« ┌──────────┐ +«q_0: ┤0 ├───────────────────────────────── +« │ RZX(-ϴ) │┌─────────┐┌─────────┐┌─────────┐ +«q_1: ┤1 ├┤ RZ(π/2) ├┤ RX(π/2) ├┤ RZ(π/2) ├ +« └──────────┘└─────────┘└─────────┘└─────────┘ +""" + +from qiskit.circuit import Parameter, QuantumCircuit +import numpy as np + + +def rzx_zz3(theta: float = None): + """ZZ template with RZGate.""" + if theta is None: + theta = Parameter('ϴ') + + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.rz(theta, 1) + qc.cx(0, 1) + qc.rz(-1*theta, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + qc.rx(theta, 1) + qc.rzx(-1*theta, 0, 1) + # Hadamard + qc.rz(np.pi / 2, 1) + qc.rx(np.pi / 2, 1) + qc.rz(np.pi / 2, 1) + + return qc diff --git a/qiskit/circuit/library/templates/rzx_templates.py b/qiskit/circuit/library/templates/rzx_templates.py index e905aa809a9f..1a7b4272036f 100644 --- a/qiskit/circuit/library/templates/rzx_templates.py +++ b/qiskit/circuit/library/templates/rzx_templates.py @@ -11,147 +11,10 @@ # that they have been altered from the originals. """ -A template library based on RZXGate. +Convenience function to load RZXGate based templates. """ from typing import List -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit - - -def rzx_zz1(theta: float = None): - """ZZ template with rz gate.""" - if theta is None: - theta = Parameter('ϴ') - - qc = QuantumCircuit(2) - qc.cx(0, 1) - qc.rz(theta, 1) - qc.sx(1) - qc.rz(np.pi, 1) - qc.sx(1) - qc.rz(3 * np.pi, 1) - qc.cx(0, 1) - qc.rz(-1*theta, 1) - - # Hadamard - qc.rz(np.pi / 2, 1) - qc.rx(np.pi / 2, 1) - qc.rz(np.pi / 2, 1) - - qc.rx(theta, 1) - qc.rzx(-1*theta, 0, 1) - # Hadamard - qc.rz(np.pi / 2, 1) - qc.rx(np.pi / 2, 1) - qc.rz(np.pi / 2, 1) - - return qc - - -def rzx_zz2(theta: float = None): - """ZZ template is p gate.""" - if theta is None: - theta = Parameter('ϴ') - - qc = QuantumCircuit(2) - qc.cx(0, 1) - qc.p(theta, 1) - qc.cx(0, 1) - qc.p(-1*theta, 1) - # Hadamard - qc.rz(np.pi / 2, 1) - qc.rx(np.pi / 2, 1) - qc.rz(np.pi / 2, 1) - - qc.rx(theta, 1) - qc.rzx(-1*theta, 0, 1) - # Hadamard - qc.rz(np.pi / 2, 1) - qc.rx(np.pi / 2, 1) - qc.rz(np.pi / 2, 1) - - return qc - - -def rzx_zz3(theta: float = None): - """ZZ template is p gate.""" - if theta is None: - theta = Parameter('ϴ') - - qc = QuantumCircuit(2) - qc.cx(0, 1) - qc.rz(theta, 1) - qc.cx(0, 1) - qc.rz(-1*theta, 1) - # Hadamard - qc.rz(np.pi / 2, 1) - qc.rx(np.pi / 2, 1) - qc.rz(np.pi / 2, 1) - - qc.rx(theta, 1) - qc.rzx(-1*theta, 0, 1) - # Hadamard - qc.rz(np.pi / 2, 1) - qc.rx(np.pi / 2, 1) - qc.rz(np.pi / 2, 1) - - return qc - - -def rzx_xz(theta: float = None): - """ZY template.""" - if theta is None: - theta = Parameter('ϴ') - - qc = QuantumCircuit(2) - qc.cx(1, 0) - qc.rx(theta, 1) - qc.cx(1, 0) - - qc.rz(np.pi / 2, 0) - qc.rx(np.pi / 2, 0) - qc.rz(np.pi / 2, 0) - qc.rzx(-1*theta, 0, 1) - qc.rz(np.pi / 2, 0) - qc.rx(np.pi / 2, 0) - qc.rz(np.pi / 2, 0) - return qc - - -def rzx_yz(theta: float = None): - """ZY template.""" - if theta is None: - theta = Parameter('ϴ') - - circ = QuantumCircuit(2) - circ.cx(0, 1) - circ.ry(-1*theta, 0) - circ.cx(0, 1) - circ.rx(np.pi / 2, 0) - circ.rzx(theta, 0, 1) - circ.rx(-np.pi / 2, 0) - - return circ - - -def rzx_cy(theta: float = None): - """ZY template.""" - if theta is None: - theta = Parameter('ϴ') - - circ = QuantumCircuit(2) - circ.cx(0, 1) - circ.ry(theta, 1) - circ.cx(0, 1) - circ.ry(-1*theta, 1) - circ.rz(-np.pi / 2, 1) - circ.rx(theta, 1) - circ.rzx(-1*theta, 0, 1) - circ.rz(np.pi / 2, 1) - - return circ def rzx_templates(template_list: List[str] = None): diff --git a/test/python/transpiler/test_rzx_templates.py b/test/python/transpiler/test_rzx_templates.py index 7eba4e8972ba..d7f1a5e88a2e 100644 --- a/test/python/transpiler/test_rzx_templates.py +++ b/test/python/transpiler/test_rzx_templates.py @@ -15,7 +15,7 @@ import numpy as np from qiskit.quantum_info import Operator -from qiskit.circuit.library.templates import rzx_zz1, rzx_zz2, rzx_yz, rzx_cy, rzx_xz, rzx_zz3 +from qiskit.circuit.library.templates.rzx import rzx_zz1, rzx_zz2, rzx_yz, rzx_cy, rzx_xz, rzx_zz3 from qiskit.test import QiskitTestCase From fcf04109e4424283d1cc73788d10f2b6f17e1d71 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 20:19:51 +0100 Subject: [PATCH 26/38] * Harmonized docstrings. --- qiskit/circuit/library/templates/rzx/rzx_cy.py | 2 +- qiskit/circuit/library/templates/rzx/rzx_zz1.py | 2 +- qiskit/circuit/library/templates/rzx/rzx_zz2.py | 4 ++-- qiskit/circuit/library/templates/rzx/rzx_zz3.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qiskit/circuit/library/templates/rzx/rzx_cy.py b/qiskit/circuit/library/templates/rzx/rzx_cy.py index 13377aa79660..ed569fa369fa 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_cy.py +++ b/qiskit/circuit/library/templates/rzx/rzx_cy.py @@ -25,7 +25,7 @@ def rzx_cy(theta: float = None): - """ZY template.""" + """Template for CX - RYGate - CX.""" if theta is None: theta = Parameter('ϴ') diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz1.py b/qiskit/circuit/library/templates/rzx/rzx_zz1.py index f97dbcaa8528..d0407c187a41 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz1.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz1.py @@ -35,7 +35,7 @@ def rzx_zz1(theta: float = None): - """ZZ template with RZGate.""" + """Template for CX - RZGate - CX.""" if theta is None: theta = Parameter('ϴ') diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz2.py b/qiskit/circuit/library/templates/rzx/rzx_zz2.py index b7805361ebd5..3184ed08c189 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz2.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz2.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """ -RZX based template for CX - phase - CX +RZX based template for CX - PhaseGate - CX .. parsed-literal:: » q_0: ──■────────────■─────────────────────────────────────────────────────» @@ -30,7 +30,7 @@ def rzx_zz2(theta: float = None): - """ZZ template with PhaseGate.""" + """Template for CX - RZGate - CX.""" if theta is None: theta = Parameter('ϴ') diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz3.py b/qiskit/circuit/library/templates/rzx/rzx_zz3.py index 948a19d48c41..654c692a7c72 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz3.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz3.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """ -RZX based template for CX - phase - CX +RZX based template for CX - RZGate - CX .. parsed-literal:: » q_0: ──■─────────────■──────────────────────────────────────────────────────» @@ -30,7 +30,7 @@ def rzx_zz3(theta: float = None): - """ZZ template with RZGate.""" + """Template for CX - RZGate - CX.""" if theta is None: theta = Parameter('ϴ') From 212cfbba6d8ac9752b3f4c0935a5db09ba6b8d52 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 20:31:12 +0100 Subject: [PATCH 27/38] * Fixed import issue. --- qiskit/circuit/library/templates/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index 13b3dfbf8f57..8e311e925c28 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -71,4 +71,4 @@ from .rzx.rzx_zz1 import rzx_zz1 from .rzx.rzx_zz2 import rzx_zz2 from .rzx.rzx_zz3 import rzx_zz3 -from .rzx.rzx_yz import rzx_templates +from .rzx_templates import rzx_templates From c663b21fe03c5d81052d65fdb0c03889222b780f Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 21:41:54 +0100 Subject: [PATCH 28/38] * Fix lint. --- qiskit/circuit/library/templates/rzx/rzx_cy.py | 2 +- qiskit/circuit/library/templates/rzx/rzx_xz.py | 2 +- qiskit/circuit/library/templates/rzx/rzx_yz.py | 2 +- qiskit/circuit/library/templates/rzx/rzx_zz1.py | 2 +- qiskit/circuit/library/templates/rzx/rzx_zz2.py | 2 +- qiskit/circuit/library/templates/rzx/rzx_zz3.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qiskit/circuit/library/templates/rzx/rzx_cy.py b/qiskit/circuit/library/templates/rzx/rzx_cy.py index ed569fa369fa..786255398f2f 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_cy.py +++ b/qiskit/circuit/library/templates/rzx/rzx_cy.py @@ -20,8 +20,8 @@ └───┘└───────┘└───┘└────────┘└──────────┘└───────┘└──────────┘└─────────┘ """ -from qiskit.circuit import Parameter, QuantumCircuit import numpy as np +from qiskit.circuit import Parameter, QuantumCircuit def rzx_cy(theta: float = None): diff --git a/qiskit/circuit/library/templates/rzx/rzx_xz.py b/qiskit/circuit/library/templates/rzx/rzx_xz.py index 857c3bc6f67f..8ad6c0eb79fc 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_xz.py +++ b/qiskit/circuit/library/templates/rzx/rzx_xz.py @@ -25,8 +25,8 @@ « """ -from qiskit.circuit import Parameter, QuantumCircuit import numpy as np +from qiskit.circuit import Parameter, QuantumCircuit def rzx_xz(theta: float = None): diff --git a/qiskit/circuit/library/templates/rzx/rzx_yz.py b/qiskit/circuit/library/templates/rzx/rzx_yz.py index a0ae58f395be..081202dee4b0 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_yz.py +++ b/qiskit/circuit/library/templates/rzx/rzx_yz.py @@ -20,8 +20,8 @@ └───┘ └───┘ └─────────┘ """ -from qiskit.circuit import Parameter, QuantumCircuit import numpy as np +from qiskit.circuit import Parameter, QuantumCircuit def rzx_yz(theta: float = None): diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz1.py b/qiskit/circuit/library/templates/rzx/rzx_zz1.py index d0407c187a41..85c490ad5b2a 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz1.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz1.py @@ -30,8 +30,8 @@ « └─────────┘ """ -from qiskit.circuit import Parameter, QuantumCircuit import numpy as np +from qiskit.circuit import Parameter, QuantumCircuit def rzx_zz1(theta: float = None): diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz2.py b/qiskit/circuit/library/templates/rzx/rzx_zz2.py index 3184ed08c189..677f7fb636e4 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz2.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz2.py @@ -25,8 +25,8 @@ « └──────────┘└─────────┘└─────────┘└─────────┘ """ -from qiskit.circuit import Parameter, QuantumCircuit import numpy as np +from qiskit.circuit import Parameter, QuantumCircuit def rzx_zz2(theta: float = None): diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz3.py b/qiskit/circuit/library/templates/rzx/rzx_zz3.py index 654c692a7c72..92632a4766dc 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz3.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz3.py @@ -25,8 +25,8 @@ « └──────────┘└─────────┘└─────────┘└─────────┘ """ -from qiskit.circuit import Parameter, QuantumCircuit import numpy as np +from qiskit.circuit import Parameter, QuantumCircuit def rzx_zz3(theta: float = None): From 5694ba330f12cf6858402c6e3e2ec1d6521fd307 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 2 Feb 2021 22:38:21 +0100 Subject: [PATCH 29/38] * Fixed import. * Deleted CalibrationAdder and made CalibrationCreator a subclass of TranspilationPass. --- .../library/templates/rzx_templates.py | 2 + .../transpiler/passes/scheduling/__init__.py | 1 - .../passes/scheduling/calibration_adder.py | 50 ------------------- .../passes/scheduling/calibration_creators.py | 27 +++++++++- 4 files changed, 27 insertions(+), 53 deletions(-) delete mode 100644 qiskit/transpiler/passes/scheduling/calibration_adder.py diff --git a/qiskit/circuit/library/templates/rzx_templates.py b/qiskit/circuit/library/templates/rzx_templates.py index 1a7b4272036f..e5350f66e76b 100644 --- a/qiskit/circuit/library/templates/rzx_templates.py +++ b/qiskit/circuit/library/templates/rzx_templates.py @@ -16,6 +16,8 @@ from typing import List +from .rzx import rzx_zz1, rzx_zz2, rzx_zz3, rzx_yz, rzx_xz, rzx_cy + def rzx_templates(template_list: List[str] = None): """ diff --git a/qiskit/transpiler/passes/scheduling/__init__.py b/qiskit/transpiler/passes/scheduling/__init__.py index b83ed3f4bead..a305ece0903a 100644 --- a/qiskit/transpiler/passes/scheduling/__init__.py +++ b/qiskit/transpiler/passes/scheduling/__init__.py @@ -16,4 +16,3 @@ from .asap import ASAPSchedule from .time_unit_analysis import TimeUnitAnalysis from .calibration_creators import CalibrationCreator, RZXCalibrationBuilder -from .calibration_adder import CalibrationAdder diff --git a/qiskit/transpiler/passes/scheduling/calibration_adder.py b/qiskit/transpiler/passes/scheduling/calibration_adder.py deleted file mode 100644 index 8f2758754f28..000000000000 --- a/qiskit/transpiler/passes/scheduling/calibration_adder.py +++ /dev/null @@ -1,50 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Scheduling transformation pass to add calibrations to the dag.""" - -from qiskit.transpiler.basepasses import TransformationPass -from qiskit.transpiler.passes.scheduling.calibration_creators import CalibrationCreator - - -class CalibrationAdder(TransformationPass): - """Identifies gates for which we can add calibrations.""" - - def __init__(self, calibration_creator: CalibrationCreator): - """ - Args: - calibration_creator: An instance of CalibrationCreator capable of generating the - schedules that will be added to the circuit. - """ - super().__init__() - self._calibration_creator = calibration_creator - - def run(self, dag): - """Run the calibration adder pass on `dag`. - - Args: - dag (DAGCircuit): DAG to schedule. - - Returns: - DAGCircuit: A DAG with calibrations added to it. - """ - for node in dag.nodes(): - if node.type == 'op': - if self._calibration_creator.supported(node.op): - params = node.op.params - qubits = [_.index for _ in node.qargs] - - schedule = self._calibration_creator.get_calibration(params, qubits) - - dag.add_calibration(node.op, qubits, schedule, params=params) - - return dag diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index c03558bb3fb7..6f1e418419c8 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -14,7 +14,7 @@ import math from typing import List -from abc import ABC, abstractmethod +from abc import abstractmethod import numpy as np from qiskit.pulse import Play, ShiftPhase, Schedule, ControlChannel, DriveChannel, GaussianSquare @@ -22,9 +22,10 @@ from qiskit.providers import basebackend from qiskit.dagcircuit import DAGNode from qiskit.circuit.library.standard_gates import RZXGate +from qiskit.transpiler.basepasses import TransformationPass -class CalibrationCreator(ABC): +class CalibrationCreator(TransformationPass): """Abstract base class to inject calibrations into circuits.""" @abstractmethod @@ -35,6 +36,27 @@ def supported(self, node_op: DAGNode) -> bool: def get_calibration(self, params: List, qubits: List) -> Schedule: """Gets the calibrated schedule for the given qubits and parameters.""" + def run(self, dag): + """Run the calibration adder pass on `dag`. + + Args: + dag (DAGCircuit): DAG to schedule. + + Returns: + DAGCircuit: A DAG with calibrations added to it. + """ + for node in dag.nodes(): + if node.type == 'op': + if self.supported(node.op): + params = node.op.params + qubits = [_.index for _ in node.qargs] + + schedule = self.get_calibration(params, qubits) + + dag.add_calibration(node.op, qubits, schedule, params=params) + + return dag + class RZXCalibrationBuilder(CalibrationCreator): """ @@ -52,6 +74,7 @@ def __init__(self, backend: basebackend): Raises: QiskitError: if open pulse is not supported by the backend. """ + super().__init__() if not backend.configuration().open_pulse: raise QiskitError('Calibrations can only be added to Pulse-enabled backends, ' 'but {0} is not enabled with Pulse.'.format(backend.name())) From 7d7526573a62bf5660ec464b6ee161d575bab33e Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 3 Feb 2021 09:19:54 +0100 Subject: [PATCH 30/38] * Added binding of parameters to test_templates for templates with parameters. * Moved convenience function rzx_templates out of the template library and closer to CalibrationBuilder. --- qiskit/circuit/library/templates/__init__.py | 2 - .../transpiler/passes/scheduling/__init__.py | 1 + .../passes/scheduling}/rzx_templates.py | 2 +- test/python/circuit/test_templates.py | 5 +++ test/python/transpiler/test_rzx_templates.py | 43 ------------------- 5 files changed, 7 insertions(+), 46 deletions(-) rename qiskit/{circuit/library/templates => transpiler/passes/scheduling}/rzx_templates.py (93%) delete mode 100644 test/python/transpiler/test_rzx_templates.py diff --git a/qiskit/circuit/library/templates/__init__.py b/qiskit/circuit/library/templates/__init__.py index d210636ad40b..5c7ac3480408 100644 --- a/qiskit/circuit/library/templates/__init__.py +++ b/qiskit/circuit/library/templates/__init__.py @@ -65,14 +65,12 @@ from .nct.template_nct_9d_9 import template_nct_9d_9 from .nct.template_nct_9d_10 import template_nct_9d_10 - from .rzx.rzx_yz import rzx_yz from .rzx.rzx_xz import rzx_xz from .rzx.rzx_cy import rzx_cy from .rzx.rzx_zz1 import rzx_zz1 from .rzx.rzx_zz2 import rzx_zz2 from .rzx.rzx_zz3 import rzx_zz3 -from .rzx_templates import rzx_templates from .clifford.clifford_2_1 import clifford_2_1 from .clifford.clifford_2_2 import clifford_2_2 diff --git a/qiskit/transpiler/passes/scheduling/__init__.py b/qiskit/transpiler/passes/scheduling/__init__.py index a305ece0903a..de3e52a98596 100644 --- a/qiskit/transpiler/passes/scheduling/__init__.py +++ b/qiskit/transpiler/passes/scheduling/__init__.py @@ -16,3 +16,4 @@ from .asap import ASAPSchedule from .time_unit_analysis import TimeUnitAnalysis from .calibration_creators import CalibrationCreator, RZXCalibrationBuilder +from .rzx_templates import rzx_templates diff --git a/qiskit/circuit/library/templates/rzx_templates.py b/qiskit/transpiler/passes/scheduling/rzx_templates.py similarity index 93% rename from qiskit/circuit/library/templates/rzx_templates.py rename to qiskit/transpiler/passes/scheduling/rzx_templates.py index e5350f66e76b..0cc01025cc55 100644 --- a/qiskit/circuit/library/templates/rzx_templates.py +++ b/qiskit/transpiler/passes/scheduling/rzx_templates.py @@ -16,7 +16,7 @@ from typing import List -from .rzx import rzx_zz1, rzx_zz2, rzx_zz3, rzx_yz, rzx_xz, rzx_cy +from qiskit.circuit.library.templates.rzx import rzx_zz1, rzx_zz2, rzx_zz3, rzx_yz, rzx_xz, rzx_cy def rzx_templates(template_list: List[str] = None): diff --git a/test/python/circuit/test_templates.py b/test/python/circuit/test_templates.py index 4fc700f924fc..9bbb41dad7e5 100644 --- a/test/python/circuit/test_templates.py +++ b/test/python/circuit/test_templates.py @@ -19,6 +19,7 @@ import numpy as np +from qiskit import QuantumCircuit from qiskit.test import QiskitTestCase from qiskit.quantum_info.operators import Operator import qiskit.circuit.library.templates as templib @@ -30,6 +31,10 @@ class TestTemplates(QiskitTestCase): circuits = [o[1]() for o in getmembers(templib) if isfunction(o[1])] + for circuit in circuits: + if isinstance(circuit, QuantumCircuit): + circuit.assign_parameters({param: 0.2 for param in circuit.parameters}, inplace=True) + @combine(template_circuit=circuits) def test_template(self, template_circuit): """test to verify that all templates are equivalent to the identity""" diff --git a/test/python/transpiler/test_rzx_templates.py b/test/python/transpiler/test_rzx_templates.py deleted file mode 100644 index d7f1a5e88a2e..000000000000 --- a/test/python/transpiler/test_rzx_templates.py +++ /dev/null @@ -1,43 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test the zx_templates.""" - -import numpy as np - -from qiskit.quantum_info import Operator -from qiskit.circuit.library.templates.rzx import rzx_zz1, rzx_zz2, rzx_yz, rzx_cy, rzx_xz, rzx_zz3 -from qiskit.test import QiskitTestCase - - -class TestRZXTemplates(QiskitTestCase): - """Test the parametric templates.""" - - def test_templates(self): - """Test that the templates compose to the identity.""" - - self.assertTrue(np.allclose(Operator(rzx_yz(0.456)).data, np.eye(4))) - - data = Operator(rzx_xz(0.456)).data - self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) - - data = Operator(rzx_cy(0.456)).data - self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) - - data = Operator(rzx_zz1(0.456)).data - self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) - - data = Operator(rzx_zz2(0.456)).data - self.assertTrue(np.allclose(data, data[0, 0]*np.eye(4))) - - data = Operator(rzx_zz3(0.456)).data - self.assertTrue(np.allclose(data, data[0, 0] * np.eye(4))) From 756d61ad612cfc075b7b9f6872a932fec1033ea3 Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Wed, 3 Feb 2021 09:22:36 +0100 Subject: [PATCH 31/38] Update qiskit/transpiler/passes/scheduling/calibration_creators.py Co-authored-by: Ali Javadi-Abhari --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 6f1e418419c8..0ce038181f0f 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -147,7 +147,7 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: schedule: The calibration schedule for the RZXGate(theta). Raises: - QiskitError: if the the control and target qubits cannot be identified. + QiskitError: if the control and target qubits cannot be identified. """ theta = params[0] q1, q2 = qubits[0], qubits[1] From 278e609296b4ef62e9a495ad818df5671fc1c5a7 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 3 Feb 2021 09:28:54 +0100 Subject: [PATCH 32/38] * Added a check to ensure that the backend has the cx gate in RZXCalibrationBuilder. --- .../transpiler/passes/scheduling/calibration_creators.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 0ce038181f0f..904a847eca39 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -147,10 +147,16 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: schedule: The calibration schedule for the RZXGate(theta). Raises: - QiskitError: if the control and target qubits cannot be identified. + QiskitError: if the control and target qubits cannot be identified or the backend + does not support cx between the qubits. """ theta = params[0] q1, q2 = qubits[0], qubits[1] + + if not self._inst_map.has('cx', qubits): + raise QiskitError('This transpilation pass requires the backend to support cx ' + 'between qubits %i and %i.' % (q1, q2)) + cx_sched = self._inst_map.get('cx', qubits=(q1, q2)) rzx_theta = Schedule(name='rzx(%.3f)' % theta) From d69dce8a17717f5d0150eba2f5400eaf2f72c923 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 3 Feb 2021 11:38:33 +0100 Subject: [PATCH 33/38] * Made RZXCalibrationCreator support the presence and absence of rotary tones. --- .../passes/scheduling/calibration_creators.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 904a847eca39..7cb871e52f0d 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -152,7 +152,7 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: """ theta = params[0] q1, q2 = qubits[0], qubits[1] - + if not self._inst_map.has('cx', qubits): raise QiskitError('This transpilation pass requires the backend to support cx ' 'between qubits %i and %i.' % (q1, q2)) @@ -197,15 +197,29 @@ def get_calibration(self, params: List, qubits: List) -> Schedule: # Stretch/compress the CR gates and compensation tones cr1 = self.rescale_cr_inst(crs[0][1], theta) cr2 = self.rescale_cr_inst(crs[1][1], theta) - comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) - comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) + if len(comp_tones) == 0: + comp1, comp2 = None, None + elif len(comp_tones) == 2: + comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) + comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) + else: + raise QiskitError('CX must have either 0 or 2 rotary tones between qubits %i and %i ' + 'but %i were found.' % (control, target, len(comp_tones))) + + # Build the schedule for the RZXGate rzx_theta = rzx_theta.insert(0, cr1) - rzx_theta = rzx_theta.insert(0, comp1) + + if comp1 is not None: + rzx_theta = rzx_theta.insert(0, comp1) + rzx_theta = rzx_theta.insert(comp1.duration, echo_x) time = comp1.duration + echo_x.duration rzx_theta = rzx_theta.insert(time, cr2) - rzx_theta = rzx_theta.insert(time, comp2) + + if comp2 is not None: + rzx_theta = rzx_theta.insert(time, comp2) + time = 2*comp1.duration + echo_x.duration rzx_theta = rzx_theta.insert(time, echo_x) From c589104d530b219adb79c7d62cb65bc45dfcd62d Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 4 Feb 2021 09:09:33 +0100 Subject: [PATCH 34/38] * Improved class docstring of RZXCalibrationBuilder. --- .../transpiler/passes/scheduling/calibration_creators.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 7cb871e52f0d..864b6bf321d9 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -61,7 +61,14 @@ def run(self, dag): class RZXCalibrationBuilder(CalibrationCreator): """ Creates calibrations for RZXGate(theta) by stretching and compressing - Gaussian square pulses. + Gaussian square pulses in the CX gate. This is done by retrieving (for a given pair of + qubits) the CX schedule in the instruction schedule map of the backend defaults. + The CX schdule must be an echoed cross-resonance gate optionally with rotary tones. + The cross-resonance drive tones and rotary pulses must be Gaussian square pulses. + The width of the Gaussian square pulse is adjusted so as to match the desired rotation angle. + If the rotation angle is small such that the width disapears then the amplitude of the + zero width Gaussian square pulse (i.e. a Gaussian) is reduced to reach the target rotation + angle. """ def __init__(self, backend: basebackend): From 47b63d63590a2d7ee4172c43d2feaf5695abb6b8 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 4 Feb 2021 09:12:38 +0100 Subject: [PATCH 35/38] * Added arxiv link to the docstring. --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index 864b6bf321d9..aba28ca33079 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -68,7 +68,7 @@ class RZXCalibrationBuilder(CalibrationCreator): The width of the Gaussian square pulse is adjusted so as to match the desired rotation angle. If the rotation angle is small such that the width disapears then the amplitude of the zero width Gaussian square pulse (i.e. a Gaussian) is reduced to reach the target rotation - angle. + angle. Additional details can be found in https://arxiv.org/abs/2012.11660. """ def __init__(self, backend: basebackend): From 0885e89faf8f6da7869626677e6f6743bf6a7bd9 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Thu, 4 Feb 2021 09:18:53 -0500 Subject: [PATCH 36/38] Update qiskit/transpiler/passes/scheduling/calibration_creators.py --- qiskit/transpiler/passes/scheduling/calibration_creators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/scheduling/calibration_creators.py b/qiskit/transpiler/passes/scheduling/calibration_creators.py index aba28ca33079..7dbe4d8b54d0 100644 --- a/qiskit/transpiler/passes/scheduling/calibration_creators.py +++ b/qiskit/transpiler/passes/scheduling/calibration_creators.py @@ -63,7 +63,7 @@ class RZXCalibrationBuilder(CalibrationCreator): Creates calibrations for RZXGate(theta) by stretching and compressing Gaussian square pulses in the CX gate. This is done by retrieving (for a given pair of qubits) the CX schedule in the instruction schedule map of the backend defaults. - The CX schdule must be an echoed cross-resonance gate optionally with rotary tones. + The CX schedule must be an echoed cross-resonance gate optionally with rotary tones. The cross-resonance drive tones and rotary pulses must be Gaussian square pulses. The width of the Gaussian square pulse is adjusted so as to match the desired rotation angle. If the rotation angle is small such that the width disapears then the amplitude of the From 58251ad2fb6199cb31a6f92d15926d7396c774e4 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Mon, 8 Feb 2021 22:20:20 +0100 Subject: [PATCH 37/38] * Added release note. --- releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml diff --git a/releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml b/releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml new file mode 100644 index 000000000000..7cbf13a85e13 --- /dev/null +++ b/releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + This PR introduces a new transpiler pass capable of generating calibrations + and adding them to a quantum circuit. In this PR we introduce the + RZXCalibrationBuilder which takes calibrated CNOT gates and creates the + calibrations for RZXGates with an arbitrary rotation angle. The schedules + are created by stretching and compressing the GaussianSquare pulses of the + echoed-cross resonance gates. Furthermore, we added templates so that users + can leverage the template matching to automatically find and replace gate + sequences, such as CNOT - P(theta) - CNOT, with more efficent circuits + based on RZXGates with a calibration. From 3d5e6de391f09e263425eb7a6ab0c697cbbbc544 Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Mon, 8 Feb 2021 22:29:32 +0100 Subject: [PATCH 38/38] Update releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml Co-authored-by: Ali Javadi-Abhari --- releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml b/releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml index 7cbf13a85e13..bfa6f5845b14 100644 --- a/releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml +++ b/releasenotes/notes/issue-5751-1b6249f6263c9c30.yaml @@ -1,9 +1,10 @@ --- features: - | - This PR introduces a new transpiler pass capable of generating calibrations - and adding them to a quantum circuit. In this PR we introduce the - RZXCalibrationBuilder which takes calibrated CNOT gates and creates the + Introduced a new transpiler pass capable of generating calibrations + and adding them to a quantum circuit. The + :py:class:`~qiskit.transpiler.passes.RZXCalibrationBuilder` takes calibrated + CNOT gates and creates the calibrations for RZXGates with an arbitrary rotation angle. The schedules are created by stretching and compressing the GaussianSquare pulses of the echoed-cross resonance gates. Furthermore, we added templates so that users