Source code for pytket.extensions.braket.braket_convert

# Copyright 2020-2024 Quantinuum
#
# 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.

""" Conversion from tket to braket
"""

from typing import (
    cast,
    Any,
    Callable,
    Dict,
    List,
    Optional,
    Tuple,
    TypeVar,
    TYPE_CHECKING,
)
from numpy import pi
from braket.circuits import Circuit as BK_Circuit  # type: ignore
from pytket.circuit import Circuit, OpType, Qubit

if TYPE_CHECKING:
    from pytket.circuit import Node


[docs] def tk_to_braket( tkcirc: Circuit, mapped_qubits: bool = False, forced_qubits: Optional[List[int]] = None, force_ops_on_target_qubits: bool = False, ) -> Tuple[BK_Circuit, List[int], Dict[int, int]]: """ Convert a tket :py:class:`Circuit` to a braket circuit. :param tkcirc: circuit to be converted :param mapped_qubits: if True, `tkcirc` must have a single one-dimensional qubit register; the indices of the qubits in that register correspond directly to the qubit identifiers in the braket circuit :param forced_qubits: optional list of braket qubit identifiers to include in the converted circuit even if they are unused :param force_ops_on_target_qubits: if True, add no-ops to all target qubits :returns: circuit converted to braket; list of braket qubit ids corresponding in order of corresponding positions in tkcirc.qubits; (partial) map from braket qubit ids to corresponding pytket bit indices holding measurement results """ tkcirc1 = tkcirc.copy() if tkcirc1.n_gates_of_type(OpType.Measure) == 0: tkcirc1.replace_implicit_wire_swaps() bkcirc = BK_Circuit() target_qubits = [] for i, qb in enumerate(tkcirc1.qubits): bkq = qb.index[0] if mapped_qubits else i target_qubits.append(bkq) measures = {} # Add no-ops on all qubits to ensure that even unused qubits are included in bkcirc: if force_ops_on_target_qubits: bkcirc.i(target_qubits) if forced_qubits is not None: bkcirc.i(forced_qubits) # Add commands for cmd in tkcirc1.get_commands(): qbs = [ qb.index[0] if mapped_qubits else tkcirc1.qubits.index(qb) for qb in cmd.qubits ] cbs = [tkcirc1.bits.index(cb) for cb in cmd.bits] op = cmd.op optype = op.type if optype == OpType.Barrier: continue params = op.params if optype == OpType.CCX: bkcirc.ccnot(*qbs) elif optype == OpType.CX: bkcirc.cnot(*qbs) elif optype == OpType.CU1: bkcirc.cphaseshift(*qbs, params[0] * pi) elif optype == OpType.CSWAP: bkcirc.cswap(*qbs) elif optype == OpType.CY: bkcirc.cy(*qbs) elif optype == OpType.CZ: bkcirc.cz(*qbs) elif optype == OpType.H: bkcirc.h(*qbs) elif optype == OpType.noop: pass elif optype == OpType.ISWAPMax: bkcirc.iswap(*qbs) elif optype == OpType.U1: bkcirc.phaseshift(*qbs, params[0] * pi) elif optype == OpType.Rx: bkcirc.rx(*qbs, params[0] * pi) elif optype == OpType.Ry: bkcirc.ry(*qbs, params[0] * pi) elif optype == OpType.Rz: bkcirc.rz(*qbs, params[0] * pi) elif optype == OpType.S: bkcirc.s(*qbs) elif optype == OpType.Sdg: bkcirc.si(*qbs) elif optype == OpType.SWAP: bkcirc.swap(*qbs) elif optype == OpType.T: bkcirc.t(*qbs) elif optype == OpType.Tdg: bkcirc.ti(*qbs) # V amd Vdg differ by a pi/4 phase from braket according to the get_matrix # methods. However, braket circuits do not seem to be phase-aware. elif optype == OpType.V: bkcirc.v(*qbs) elif optype == OpType.Vdg: bkcirc.vi(*qbs) elif optype == OpType.X: bkcirc.x(*qbs) elif optype == OpType.XXPhase: bkcirc.xx(*qbs, params[0] * pi) elif optype == OpType.ISWAP: bkcirc.xy(*qbs, params[0] * pi) elif optype == OpType.Y: bkcirc.y(*qbs) elif optype == OpType.YYPhase: bkcirc.yy(*qbs, params[0] * pi) elif optype == OpType.Z: bkcirc.z(*qbs) elif optype == OpType.ZZPhase: bkcirc.zz(*qbs, params[0] * pi) elif optype == OpType.Measure: # Not wanted by braket, but must be tracked for final conversion of results. measures[qbs[0]] = cbs[0] else: raise NotImplementedError(f"Cannot convert {op.get_name()} to braket") return (bkcirc, target_qubits, measures)
[docs] def braket_to_tk(bkcirc: BK_Circuit) -> Circuit: """ Convert a braket circuit to a tket :py:class:`Circuit` :param bkcirc: circuit to be converted :returns: circuit converted to tket """ tkcirc = Circuit() for qb in bkcirc.qubits: tkcirc.add_qubit(Qubit("q", int(qb))) for instr in bkcirc.instructions: op = instr.operator qbs = [int(qb) for qb in instr.target] opname = op.name if opname == "CCNot": tkcirc.add_gate(OpType.CCX, qbs) elif opname == "CNot": tkcirc.add_gate(OpType.CX, qbs) elif opname == "CPhaseShift": tkcirc.add_gate(OpType.CU1, op.angle / pi, qbs) elif opname == "CSwap": tkcirc.add_gate(OpType.CSWAP, qbs) elif opname == "CY": tkcirc.add_gate(OpType.CY, qbs) elif opname == "CZ": tkcirc.add_gate(OpType.CZ, qbs) elif opname == "H": tkcirc.add_gate(OpType.H, qbs) elif opname == "I": pass elif opname == "ISwap": tkcirc.add_gate(OpType.ISWAPMax, qbs) elif opname == "PhaseShift": tkcirc.add_gate(OpType.U1, op.angle / pi, qbs) elif opname == "Rx": tkcirc.add_gate(OpType.Rx, op.angle / pi, qbs) elif opname == "Ry": tkcirc.add_gate(OpType.Ry, op.angle / pi, qbs) elif opname == "Rz": tkcirc.add_gate(OpType.Rz, op.angle / pi, qbs) elif opname == "S": tkcirc.add_gate(OpType.S, qbs) elif opname == "Si": tkcirc.add_gate(OpType.Sdg, qbs) elif opname == "Swap": tkcirc.add_gate(OpType.SWAP, qbs) elif opname == "T": tkcirc.add_gate(OpType.T, qbs) elif opname == "Ti": tkcirc.add_gate(OpType.Tdg, qbs) elif opname == "V": tkcirc.add_gate(OpType.V, qbs) tkcirc.add_phase(0.25) elif opname == "Vi": tkcirc.add_gate(OpType.Vdg, qbs) tkcirc.add_phase(-0.25) elif opname == "X": tkcirc.add_gate(OpType.X, qbs) elif opname == "XX": tkcirc.add_gate(OpType.XXPhase, op.angle / pi, qbs) elif opname == "XY": tkcirc.add_gate(OpType.ISWAP, op.angle / pi, qbs) elif opname == "Y": tkcirc.add_gate(OpType.Y, qbs) elif opname == "YY": tkcirc.add_gate(OpType.YYPhase, op.angle / pi, qbs) elif opname == "Z": tkcirc.add_gate(OpType.Z, qbs) elif opname == "ZZ": tkcirc.add_gate(OpType.ZZPhase, op.angle / pi, qbs) else: # The following don't have direct equivalents: # - CPhaseShift00, CPhaseShift01, CPhaseShift10: diagonal unitaries with 1s # on the diagonal except for a phase e^{ia} in the (0,0), (1,1) or (2,2) # position respectively. # - PSwap: unitary with 1s at (0,0) and (3,3), a phase e^{ia} at (1,2) and # (2,1), and zeros elsewhere. # They could be decomposed into pytket gates, but it would be better to add # the gate types to tket. # The "Unitary" type could be represented as a box in the 1q and 2q cases, # but not in general. raise NotImplementedError(f"Cannot convert {opname} to tket") return tkcirc
def get_avg_characterisation( characterisation: Dict[str, Any] ) -> Dict[str, Dict["Node", float]]: """ Convert gate-specific characterisation into readout, one- and two-qubit errors Used to convert the stored full `characterisation` into an input noise characterisation for NoiseAwarePlacement """ K = TypeVar("K") V1 = TypeVar("V1") V2 = TypeVar("V2") map_values_t = Callable[[Callable[[V1], V2], Dict[K, V1]], Dict[K, V2]] map_values: map_values_t = lambda f, d: {k: f(v) for k, v in d.items()} node_errors = cast( Dict["Node", Dict[OpType, float]], characterisation["NodeErrors"] ) link_errors = cast( Dict[Tuple["Node", "Node"], Dict[OpType, float]], characterisation["EdgeErrors"] ) readout_errors = cast( Dict["Node", List[List[float]]], characterisation["ReadoutErrors"] ) avg: Callable[[Dict[Any, float]], float] = lambda xs: sum(xs.values()) / len(xs) avg_mat: Callable[[List[List[float]]], float] = ( lambda xs: (xs[0][1] + xs[1][0]) / 2.0 ) avg_readout_errors = map_values(avg_mat, readout_errors) avg_node_errors = map_values(avg, node_errors) avg_link_errors = map_values(avg, link_errors) return { "node_errors": avg_node_errors, "link_errors": avg_link_errors, "readout_errors": avg_readout_errors, }