Skip to content

Commit

Permalink
Add decomposition for CCZ gate and IonQTargetGateset when qubits are …
Browse files Browse the repository at this point in the history
…all-to-all connected (#6095)
  • Loading branch information
yinghui-hu committed Aug 7, 2023
1 parent 14baaa5 commit 081afab
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
2 changes: 1 addition & 1 deletion cirq-ionq/cirq_ionq/__init__.py
Expand Up @@ -18,7 +18,7 @@

from cirq_ionq.ionq_devices import IonQAPIDevice, decompose_to_device

from cirq_ionq.ionq_gateset import IonQTargetGateset
from cirq_ionq.ionq_gateset import IonQTargetGateset, decompose_all_to_all_connect_ccz_gate

from cirq_ionq.ionq_exceptions import (
IonQException,
Expand Down
64 changes: 62 additions & 2 deletions cirq-ionq/cirq_ionq/ionq_gateset.py
Expand Up @@ -16,6 +16,7 @@
from typing import Any
from typing import Dict
from typing import List
from typing import Tuple

import cirq

Expand Down Expand Up @@ -78,12 +79,21 @@ def _decompose_two_qubit_operation(self, op: cirq.Operation, _) -> cirq.OP_TREE:
temp, k=1, rewriter=lambda op: self._decompose_single_qubit_operation(op, -1)
).all_operations()

def _decompose_multi_qubit_operation(self, op: cirq.Operation, _) -> cirq.OP_TREE:
if isinstance(op.gate, cirq.CCZPowGate):
return decompose_all_to_all_connect_ccz_gate(op.gate, op.qubits)
return NotImplemented

@property
def preprocess_transformers(self) -> List['cirq.TRANSFORMER']:
"""List of transformers which should be run before decomposing individual operations."""
"""List of transformers which should be run before decomposing individual operations.
Decompose to three qubit gates because three qubit gates have different decomposition
for all-to-all connectivity between qubits.
"""
return [
cirq.create_transformer_with_kwargs(
cirq.expand_composite, no_decomp=lambda op: cirq.num_qubits(op) <= self.num_qubits
cirq.expand_composite, no_decomp=lambda op: cirq.num_qubits(op) <= 3
)
]

Expand All @@ -104,3 +114,53 @@ def _json_dict_(self) -> Dict[str, Any]:
@classmethod
def _from_json_dict_(cls, atol, **kwargs):
return cls(atol=atol)


def decompose_all_to_all_connect_ccz_gate(
ccz_gate: 'cirq.CCZPowGate', qubits: Tuple['cirq.Qid', ...]
) -> 'cirq.OP_TREE':
"""Decomposition of all-to-all connected qubits are different from line qubits or grid qubits.
For example, for qubits in the same ion trap, the decomposition of CCZ gate will be:
0: ──────────────@──────────────────@───@───p──────@───
│ │ │ │
1: ───@──────────┼───────@───p──────┼───X───p^-1───X───
│ │ │ │
2: ───X───p^-1───X───p───X───p^-1───X───p──────────────
where p = T**ccz_gate._exponent
"""
if len(qubits) != 3:
raise ValueError(f'Expect 3 qubits for CCZ gate, got {len(qubits)} qubits.')

a, b, c = qubits

p = cirq.T**ccz_gate._exponent
global_phase = 1j ** (2 * ccz_gate.global_shift * ccz_gate._exponent)
global_phase = (
complex(global_phase)
if cirq.is_parameterized(global_phase) and global_phase.is_complex # type: ignore
else global_phase
)
global_phase_operation = (
[cirq.global_phase_operation(global_phase)]
if cirq.is_parameterized(global_phase) or abs(global_phase - 1.0) > 0
else []
)

return global_phase_operation + [
cirq.CNOT(b, c),
p(c) ** -1,
cirq.CNOT(a, c),
p(c),
cirq.CNOT(b, c),
p(c) ** -1,
cirq.CNOT(a, c),
p(b),
p(c),
cirq.CNOT(a, b),
p(a),
p(b) ** -1,
cirq.CNOT(a, b),
]
43 changes: 43 additions & 0 deletions cirq-ionq/cirq_ionq/ionq_gateset_test.py
Expand Up @@ -131,3 +131,46 @@ def test_decompose_parameterized_operation():
atol=1e-6,
)
assert ionq_target_gateset.validate(decomposed_circuit)


def test_decomposition_all_to_all_connectivity():
"""This function only accepts 3 qubits as input"""
with pytest.raises(ValueError):
decompose_result = ionq.decompose_all_to_all_connect_ccz_gate(
cirq.CCZ, cirq.LineQubit.range(4)
)

decompose_result = ionq.decompose_all_to_all_connect_ccz_gate(cirq.CCZ, cirq.LineQubit.range(3))

cirq.testing.assert_has_diagram(
cirq.Circuit(decompose_result),
"""
0: ──────────────@──────────────────@───@───T──────@───
│ │ │ │
1: ───@──────────┼───────@───T──────┼───X───T^-1───X───
│ │ │ │
2: ───X───T^-1───X───T───X───T^-1───X───T──────────────
""",
)


def test_decompose_toffoli_gate():
"""Decompose result should reflect all-to-all connectivity"""
circuit = cirq.Circuit(cirq.TOFFOLI(*cirq.LineQubit.range(3)))
decomposed_circuit = cirq.optimize_for_target_gateset(
circuit, gateset=ionq_target_gateset, ignore_failures=False
)
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(
circuit, decomposed_circuit, atol=1e-8
)
assert ionq_target_gateset.validate(decomposed_circuit)
cirq.testing.assert_has_diagram(
decomposed_circuit,
"""
0: ──────────────────@──────────────────@───@───T──────@───
│ │ │ │
1: ───────@──────────┼───────@───T──────┼───X───T^-1───X───
│ │ │ │
2: ───H───X───T^-1───X───T───X───T^-1───X───T───H──────────
""",
)

0 comments on commit 081afab

Please sign in to comment.