Skip to content

Commit

Permalink
Adds BasisTanslator, UnrollCustomDefinitions to default levels. (#4446)
Browse files Browse the repository at this point in the history
* Add Rz->RX,RY, U3->RX,RY, CX->RXX to StandardEquivalenceLibrary.

Updates BasisTranslator-Unroller compatability test
test_basis_translator.TestUnrollerCompatibility.test_unroll_all_instructions
to verify the generated circuit is equivalent (with snapshot,
measure instructions removed) as the circuit transpiled by the
BasisTranslator is no longer gate-for-gate equal with that of the
Unroller.

* Fix free param calculation in standard gates to_matrix test.

Previous code mis-calculated the number of available free parameters
from the gate signature, resulting in creation of gates like
XGate(label=0.1). These would cause errors if the circuits containing
these gates were drawn.

* Remove definition for C3XGate, C4XGate due to name conflict with MCXGate.

* Add UnrollCustomDefinitions and BasisTranslator into default levels.

* Add CX to CZ, to iSwap decompositions.

* Support block collection for non-CX two-qubit gates.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
kdk and mergify[bot] authored Jun 25, 2020
1 parent 7da1ed4 commit 93a51c8
Show file tree
Hide file tree
Showing 16 changed files with 340 additions and 158 deletions.
143 changes: 58 additions & 85 deletions qiskit/circuit/library/standard_gates/equivalence_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@
from qiskit.qasm import pi
from qiskit.circuit import EquivalenceLibrary, Parameter, QuantumCircuit, QuantumRegister

from qiskit.quantum_info.synthesis.ion_decompose import cnot_rxx_decompose

from . import (
HGate,
CHGate,
MSGate,
RGate,
RCCXGate,
RC3XGate,
C3XGate,
C4XGate,
RXGate,
CRXGate,
RXXGate,
Expand Down Expand Up @@ -127,88 +126,6 @@
def_rccx.append(inst, qargs, cargs)
_sel.add_equivalence(RCCXGate(), def_rccx)

# RC3XGate

q = QuantumRegister(4, 'q')
def_rc3x = QuantumCircuit(q)
for inst, qargs, cargs in [
(HGate(), [q[3]], []),
(TGate(), [q[3]], []),
(CXGate(), [q[2], q[3]], []),
(TdgGate(), [q[3]], []),
(HGate(), [q[3]], []),
(CXGate(), [q[0], q[3]], []),
(TGate(), [q[3]], []),
(CXGate(), [q[1], q[3]], []),
(TdgGate(), [q[3]], []),
(CXGate(), [q[0], q[3]], []),
(TGate(), [q[3]], []),
(CXGate(), [q[1], q[3]], []),
(TdgGate(), [q[3]], []),
(HGate(), [q[3]], []),
(TGate(), [q[3]], []),
(CXGate(), [q[2], q[3]], []),
(TdgGate(), [q[3]], []),
(HGate(), [q[3]], []),
]:
def_rc3x.append(inst, qargs, cargs)
_sel.add_equivalence(RC3XGate(), def_rc3x)

# C3XGate

q = QuantumRegister(4, 'q')
def_c3x = QuantumCircuit(q)
for inst, qargs, cargs in [
(HGate(), [q[3]], []),
(CU1Gate(-pi/4), [q[0], q[3]], []),
(HGate(), [q[3]], []),
(CXGate(), [q[0], q[1]], []),
(HGate(), [q[3]], []),
(CU1Gate(pi/4), [q[1], q[3]], []),
(HGate(), [q[3]], []),
(CXGate(), [q[0], q[1]], []),
(HGate(), [q[3]], []),
(CU1Gate(-pi/4), [q[1], q[3]], []),
(HGate(), [q[3]], []),
(CXGate(), [q[1], q[2]], []),
(HGate(), [q[3]], []),
(CU1Gate(pi/4), [q[2], q[3]], []),
(HGate(), [q[3]], []),
(CXGate(), [q[0], q[2]], []),
(HGate(), [q[3]], []),
(CU1Gate(-pi/4), [q[2], q[3]], []),
(HGate(), [q[3]], []),
(CXGate(), [q[1], q[2]], []),
(HGate(), [q[3]], []),
(CU1Gate(pi/4), [q[2], q[3]], []),
(HGate(), [q[3]], []),
(CXGate(), [q[0], q[2]], []),
(HGate(), [q[3]], []),
(CU1Gate(-pi/4), [q[2], q[3]], []),
(HGate(), [q[3]], [])
]:
def_c3x.append(inst, qargs, cargs)
_sel.add_equivalence(C3XGate(), def_c3x)


# C4XGate

q = QuantumRegister(5, 'q')
def_c4x = QuantumCircuit(q)
for inst, qargs, cargs in [
(HGate(), [q[4]], []),
(CU1Gate(-pi / 2), [q[3], q[4]], []),
(HGate(), [q[4]], []),
(C3XGate(), [q[0], q[1], q[2], q[3]], []),
(HGate(), [q[4]], []),
(CU1Gate(pi / 2), [q[3], q[4]], []),
(HGate(), [q[4]], []),
(C3XGate(), [q[0], q[1], q[2], q[3]], []),
(C3XGate(pi / 8), [q[0], q[1], q[2], q[4]], []),
]:
def_c4x.append(inst, qargs, cargs)
_sel.add_equivalence(C4XGate(), def_c4x)

# RXGate

q = QuantumRegister(1, 'q')
Expand Down Expand Up @@ -296,6 +213,14 @@
def_rz.append(U1Gate(theta), [q[0]], [])
_sel.add_equivalence(RZGate(theta), def_rz)

q = QuantumRegister(1, 'q')
theta = Parameter('theta')
rz_to_rxry = QuantumCircuit(q)
rz_to_rxry.append(RXGate(pi/2), [q[0]], [])
rz_to_rxry.append(RYGate(-theta), [q[0]], [])
rz_to_rxry.append(RXGate(-pi/2), [q[0]], [])
_sel.add_equivalence(RZGate(theta), rz_to_rxry)

# CRZGate

q = QuantumRegister(2, 'q')
Expand Down Expand Up @@ -447,6 +372,18 @@

# U3Gate

q = QuantumRegister(1, 'q')
theta = Parameter('theta')
phi = Parameter('phi')
lam = Parameter('lam')
u3_qasm_def = QuantumCircuit(q)
u3_qasm_def.rz(lam, 0)
u3_qasm_def.rx(pi/2, 0)
u3_qasm_def.rz(theta+pi, 0)
u3_qasm_def.rx(pi/2, 0)
u3_qasm_def.rz(phi+3*pi, 0)
_sel.add_equivalence(U3Gate(theta, phi, lam), u3_qasm_def)

# CU3Gate

q = QuantumRegister(2, 'q')
Expand Down Expand Up @@ -474,6 +411,42 @@

# CXGate

for plus_ry in [False, True]:
for plus_rxx in [False, True]:
cx_to_rxx = cnot_rxx_decompose(plus_ry, plus_rxx)
_sel.add_equivalence(CXGate(), cx_to_rxx)

q = QuantumRegister(2, 'q')
cx_to_cz = QuantumCircuit(q)
for inst, qargs, cargs in [
(HGate(), [q[1]], []),
(CZGate(), [q[0], q[1]], []),
(HGate(), [q[1]], [])
]:
cx_to_cz.append(inst, qargs, cargs)
_sel.add_equivalence(CXGate(), cx_to_cz)

q = QuantumRegister(2, 'q')
cx_to_iswap = QuantumCircuit(q)
for inst, qargs, cargs in [
(HGate(), [q[0]], []),
(XGate(), [q[1]], []),
(HGate(), [q[1]], []),
(iSwapGate(), [q[0], q[1]], []),
(XGate(), [q[0]], []),
(XGate(), [q[1]], []),
(HGate(), [q[1]], []),
(iSwapGate(), [q[0], q[1]], []),
(HGate(), [q[0]], []),
(SGate(), [q[0]], []),
(SGate(), [q[1]], []),
(XGate(), [q[1]], []),
(HGate(), [q[1]], []),
]:
cx_to_iswap.append(inst, qargs, cargs)
_sel.add_equivalence(CXGate(), cx_to_iswap)


# CCXGate

q = QuantumRegister(3, 'q')
Expand Down
31 changes: 22 additions & 9 deletions qiskit/compiler/transpile.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def transpile(circuits: Union[QuantumCircuit, List[QuantumCircuit]],
initial_layout: Optional[Union[Layout, Dict, List]] = None,
layout_method: Optional[str] = None,
routing_method: Optional[str] = None,
translation_method: Optional[str] = None,
seed_transpiler: Optional[int] = None,
optimization_level: Optional[int] = None,
pass_manager: Optional[PassManager] = None,
Expand Down Expand Up @@ -119,6 +120,7 @@ def transpile(circuits: Union[QuantumCircuit, List[QuantumCircuit]],
Sometimes a perfect layout can be available in which case the layout_method
may not run.
routing_method: Name of routing pass ('basic', 'lookahead', 'stochastic', 'sabre')
translation_method: Name of translation pass ('unroller', 'translator')
seed_transpiler: Sets random seed for the stochastic parts of the transpiler
optimization_level: How much optimization to perform on the circuits.
Higher levels generate more optimized circuits,
Expand Down Expand Up @@ -185,7 +187,9 @@ def callback_func(**kwargs):
coupling_map=coupling_map, seed_transpiler=seed_transpiler,
backend_properties=backend_properties,
initial_layout=initial_layout, layout_method=layout_method,
routing_method=routing_method, backend=backend)
routing_method=routing_method,
translation_method=translation_method,
backend=backend)

warnings.warn("The parameter pass_manager in transpile is being deprecated. "
"The preferred way to tranpile a circuit using a custom pass manager is"
Expand All @@ -200,7 +204,7 @@ def callback_func(**kwargs):
# Get transpile_args to configure the circuit transpilation job(s)
transpile_args = _parse_transpile_args(circuits, backend, basis_gates, coupling_map,
backend_properties, initial_layout,
layout_method, routing_method,
layout_method, routing_method, translation_method,
seed_transpiler, optimization_level,
callback, output_name)

Expand Down Expand Up @@ -274,7 +278,8 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua
pass_manager_config = transpile_config['pass_manager_config']

ms_basis_swap = None
if pass_manager_config.basis_gates is not None:
if (pass_manager_config.translation_method == 'unroller'
and pass_manager_config.basis_gates is not None):
# Workaround for ion trap support: If basis gates includes
# Mølmer-Sørensen (rxx) and the circuit includes gates outside the basis,
# first unroll to u3, cx, then run MSBasisDecomposer to target basis.
Expand Down Expand Up @@ -309,7 +314,7 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua

def _parse_transpile_args(circuits, backend,
basis_gates, coupling_map, backend_properties,
initial_layout, layout_method, routing_method,
initial_layout, layout_method, routing_method, translation_method,
seed_transpiler, optimization_level,
callback, output_name) -> List[Dict]:
"""Resolve the various types of args allowed to the transpile() function through
Expand All @@ -336,14 +341,15 @@ def _parse_transpile_args(circuits, backend,
initial_layout = _parse_initial_layout(initial_layout, circuits)
layout_method = _parse_layout_method(layout_method, num_circuits)
routing_method = _parse_routing_method(routing_method, num_circuits)
translation_method = _parse_translation_method(translation_method, num_circuits)
seed_transpiler = _parse_seed_transpiler(seed_transpiler, num_circuits)
optimization_level = _parse_optimization_level(optimization_level, num_circuits)
output_name = _parse_output_name(output_name, circuits)
callback = _parse_callback(callback, num_circuits)

list_transpile_args = []
for args in zip(basis_gates, coupling_map, backend_properties,
initial_layout, layout_method, routing_method,
initial_layout, layout_method, routing_method, translation_method,
seed_transpiler, optimization_level,
output_name, callback):
transpile_args = {'pass_manager_config': PassManagerConfig(basis_gates=args[0],
Expand All @@ -352,10 +358,11 @@ def _parse_transpile_args(circuits, backend,
initial_layout=args[3],
layout_method=args[4],
routing_method=args[5],
seed_transpiler=args[6]),
'optimization_level': args[7],
'output_name': args[8],
'callback': args[9]}
translation_method=args[6],
seed_transpiler=args[7]),
'optimization_level': args[8],
'output_name': args[9],
'callback': args[10]}
list_transpile_args.append(transpile_args)

return list_transpile_args
Expand Down Expand Up @@ -446,6 +453,12 @@ def _parse_routing_method(routing_method, num_circuits):
return routing_method


def _parse_translation_method(translation_method, num_circuits):
if not isinstance(translation_method, list):
translation_method = [translation_method] * num_circuits
return translation_method


def _parse_seed_transpiler(seed_transpiler, num_circuits):
if not isinstance(seed_transpiler, list):
seed_transpiler = [seed_transpiler] * num_circuits
Expand Down
4 changes: 3 additions & 1 deletion qiskit/quantum_info/synthesis/two_qubit_decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from qiskit.circuit.library.standard_gates.u3 import U3Gate
from qiskit.circuit.library.standard_gates.x import CXGate
from qiskit.exceptions import QiskitError
from qiskit.quantum_info.operators import Operator
from qiskit.quantum_info.operators.predicates import is_unitary_matrix
from qiskit.quantum_info.synthesis.weyl import weyl_coordinates
from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer
Expand Down Expand Up @@ -284,7 +285,8 @@ class TwoQubitBasisDecomposer():
def __init__(self, gate, basis_fidelity=1.0):
self.gate = gate
self.basis_fidelity = basis_fidelity
basis = self.basis = TwoQubitWeylDecomposition(gate.to_matrix())

basis = self.basis = TwoQubitWeylDecomposition(Operator(gate).data)

# FIXME: find good tolerances
self.is_supercontrolled = np.isclose(basis.a, np.pi/4) and np.isclose(basis.c, 0.)
Expand Down
Loading

0 comments on commit 93a51c8

Please sign in to comment.