1# Copyright 2021 The Cirq Developers 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# https://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13"""Devices for IonQ hardware.""" 14 15from typing import AbstractSet, Sequence, Union 16 17import numpy as np 18 19import cirq 20 21 22class IonQAPIDevice(cirq.Device): 23 """A device that uses the gates exposed by the IonQ API. 24 25 When using this device in constructing a circuit, it will convert one and two qubit gates 26 that are not supported by the API into those supported by the API if they have a unitary 27 matrix (support the unitary protocol). 28 29 Note that this device does not do any compression of the resulting circuit, i.e. it may 30 result in a series of single qubit gates that could be executed using far fewer elements. 31 32 The gates supported by the API are 33 * `cirq.XPowGate`, `cirq.YPowGate`, `cirq.ZPowGate` 34 * `cirq.XXPowGate`, `cirq.YYPowGate`, `cirq.ZZPowGate` 35 * `cirq.CNOT`, `cirq.H`, `cirq.SWAP` 36 * `cirq.MeasurementGate` 37 """ 38 39 def __init__(self, qubits: Union[Sequence[cirq.LineQubit], int], atol=1e-8): 40 """Construct the device. 41 42 Args: 43 qubits: The qubits upon which this device acts or the number of qubits. If the number 44 of qubits, then the qubits will be `cirq.LineQubit`s from 0 to this number minus 45 one. 46 atol: The absolute tolerance used for gate calculations and decompositions. 47 """ 48 if isinstance(qubits, int): 49 self.qubits = frozenset(cirq.LineQubit.range(qubits)) 50 else: 51 self.qubits = frozenset(qubits) 52 self.atol = atol 53 self.gateset = cirq.Gateset( 54 cirq.H, 55 cirq.CNOT, 56 cirq.SWAP, 57 cirq.XPowGate, 58 cirq.YPowGate, 59 cirq.ZPowGate, 60 cirq.XXPowGate, 61 cirq.YYPowGate, 62 cirq.ZZPowGate, 63 cirq.MeasurementGate, 64 unroll_circuit_op=False, 65 accept_global_phase_op=False, 66 ) 67 68 def qubit_set(self) -> AbstractSet['cirq.Qid']: 69 return self.qubits 70 71 def validate_operation(self, operation: cirq.Operation): 72 if operation.gate is None: 73 raise ValueError( 74 f'IonQAPIDevice does not support operations with no gates {operation}.' 75 ) 76 if not self.is_api_gate(operation): 77 raise ValueError(f'IonQAPIDevice has unsupported gate {operation.gate}.') 78 if not set(operation.qubits).intersection(self.qubit_set()): 79 raise ValueError(f'Operation with qubits not on the device. Qubits: {operation.qubits}') 80 81 def is_api_gate(self, operation: cirq.Operation) -> bool: 82 return operation in self.gateset 83 84 def decompose_operation(self, operation: cirq.Operation) -> cirq.OP_TREE: 85 if self.is_api_gate(operation): 86 return operation 87 assert cirq.has_unitary(operation), ( 88 f'Operation {operation} that is not available on the IonQ API nor does it have a ' 89 'unitary matrix to use to decompose it to the API.' 90 ) 91 num_qubits = len(operation.qubits) 92 if num_qubits == 1: 93 return self._decompose_single_qubit(operation) 94 if num_qubits == 2: 95 return self._decompose_two_qubit(operation) 96 raise ValueError(f'Operation {operation} not supported by IonQ API.') 97 98 def _decompose_single_qubit(self, operation: cirq.Operation) -> cirq.OP_TREE: 99 qubit = operation.qubits[0] 100 mat = cirq.unitary(operation) 101 for gate in cirq.single_qubit_matrix_to_gates(mat, self.atol): 102 yield gate(qubit) 103 104 def _decompose_two_qubit(self, operation: cirq.Operation) -> cirq.OP_TREE: 105 """Decomposes a two qubit gate into XXPow, YYPow, and ZZPow plus single qubit gates.""" 106 mat = cirq.unitary(operation) 107 kak = cirq.kak_decomposition(mat, check_preconditions=False) 108 109 for qubit, mat in zip(operation.qubits, kak.single_qubit_operations_before): 110 gates = cirq.single_qubit_matrix_to_gates(mat, self.atol) 111 for gate in gates: 112 yield gate(qubit) 113 114 two_qubit_gates = [cirq.XX, cirq.YY, cirq.ZZ] 115 for two_qubit_gate, coefficient in zip(two_qubit_gates, kak.interaction_coefficients): 116 yield (two_qubit_gate ** (-coefficient * 2 / np.pi))(*operation.qubits) 117 118 for qubit, mat in zip(operation.qubits, kak.single_qubit_operations_after): 119 for gate in cirq.single_qubit_matrix_to_gates(mat, self.atol): 120 yield gate(qubit) 121