# Copyright 2019-2026 The Blueqat Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The 'eo' backend: transpile a logical Circuit into exchange pulses.
import blueqat.eo # registers the backend
physical = Circuit(2).h[0].cx[0, 1].run(backend='eo')
Logical qubit i is encoded in physical spins 3i, 3i+1, 3i+2, and the output
is an ordinary Circuit containing only `exch` pulses, runnable on any
simulation backend. All logical gates are exact up to global phase.
Topology note: the emitted pulses assume any pair inside the two triples
involved in a gate can be pulsed (in particular, the Fong-Wandzura CNOT's
bridge pulse connects spin 3c+2 with spin 3t+2, and the encoded SWAP pulses
pair the triples spin-by-spin). This is always fine for simulation; mapping
onto strict nearest-neighbor-only hardware additionally requires dot
orientation assignment and spin-level SWAP routing, which is future work
(cf. exchange-pulse-optimizer).
"""
from typing import Any, List
from ..backends.backendbase import Backend, register_backend
from ..circuit import Circuit
from ..gate import Operation
from . import sequences
[docs]
class EOTranspiler(Backend):
"""Transpiler backend converting logical circuits to exchange pulses."""
_FIXED = {
'x': sequences.x_sequence,
'y': sequences.y_sequence,
'z': sequences.z_sequence,
'h': sequences.h_sequence,
's': sequences.s_sequence,
'sdg': sequences.sdg_sequence,
't': sequences.t_sequence,
'tdg': sequences.tdg_sequence,
}
_ROTATIONS = {
'rz': sequences.rz_sequence,
'phase': sequences.rz_sequence, # equal to rz up to global phase
'rx': sequences.rx_sequence,
'ry': sequences.ry_sequence,
}
[docs]
def run(self, gates: List[Operation], n_qubits: int, *args: Any,
**kwargs: Any) -> Circuit:
from ..gate import GateBlock
pulses: List[sequences.Pulse] = []
for gate in gates:
name = gate.lowername
if isinstance(gate, GateBlock):
# ブロックは中身を展開してトランスパイルする
sub = self.run(gate.ops, n_qubits)
for op in sub.ops:
i, j = op.targets
pulses.append(((int(i), int(j)), float(op.theta)))
continue
if name in ('i', 'barrier'):
continue
if name in self._FIXED:
for t in gate.target_iter(n_qubits):
pulses += self._FIXED[name](offset=3 * t)
elif name in self._ROTATIONS:
for t in gate.target_iter(n_qubits):
pulses += self._ROTATIONS[name](gate.theta, offset=3 * t)
elif name == 'cx':
for c, t in gate.control_target_iter(n_qubits):
pulses += sequences.cx_sequence(3 * c, 3 * t)
elif name == 'cz':
for c, t in gate.control_target_iter(n_qubits):
pulses += sequences.cz_sequence(3 * c, 3 * t)
elif name == 'swap':
for a, b in gate.control_target_iter(n_qubits):
pulses += sequences.swap_sequence(3 * a, 3 * b)
else:
raise ValueError(
f"Gate '{name}' is not supported by the exchange-only "
"transpiler. Decompose it into "
"x/y/z/h/s/t/rx/ry/rz/cx/cz/swap first.")
return sequences.sequence_to_circuit(pulses, 3 * n_qubits)
register_backend('eo', EOTranspiler, overwrite=True)