Source code for cirq.circuits.qasm_output

# Copyright 2018 The Cirq 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
#
#     https://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.

"""Utility classes for representing QASM."""

from typing import Set  # pylint: disable=unused-import
from typing import (
    Callable, Dict, Optional, Sequence, Tuple, Union, cast
)

import re
import numpy as np

from cirq import ops, linalg, protocols, value


class QasmUGate(ops.SingleQubitGate):
    def __init__(self, lmda, theta, phi) -> None:
        """A QASM gate representing any single qubit unitary with a series of
        three rotations, Z, Y, and Z.

        The angles are normalized to the range [0, 2) half_turns.

        Args:
            lmda: Half turns to rotate about Z (applied first).
            theta: Half turns to rotate about Y.
            phi: Half turns to rotate about Z (applied last).
        """
        self.lmda = lmda % 2
        self.theta = theta % 2
        self.phi = phi % 2

    @staticmethod
    def from_matrix(mat: np.array) -> 'QasmUGate':
        pre_phase, rotation, post_phase = (
            linalg.deconstruct_single_qubit_matrix_into_angles(mat))
        return QasmUGate(pre_phase/np.pi, rotation/np.pi, post_phase/np.pi)

    def _qasm_(self,
               qubits: Tuple[ops.QubitId, ...],
               args: protocols.QasmArgs) -> str:
        args.validate_version('2.0')
        return args.format(
                'u3({0:half_turns},{1:half_turns},{2:half_turns}) {3};\n',
                self.theta, self.phi, self.lmda, qubits[0])

    def __repr__(self) -> str:
        return 'cirq.QasmUGate({}, {}, {})'.format(self.lmda,
                                                   self.theta,
                                                   self.phi)


@value.value_equality
class QasmTwoQubitGate(ops.TwoQubitGate):
    def __init__(self, kak: linalg.KakDecomposition) -> None:
        """A two qubit gate represented in QASM by the KAK decomposition.

        All angles are in half turns.  Assumes a canonicalized KAK
        decomposition.

        Args:
            kak: KAK decomposition of the two-qubit gate.
        """
        self.kak = kak

    def _value_equality_values_(self):
        return self.kak

    @staticmethod
    def from_matrix(mat: np.array, atol=1e-8) -> 'QasmTwoQubitGate':
        """Creates a QasmTwoQubitGate from the given matrix.

        Args:
            mat: The unitary matrix of the two qubit gate.
            atol: Absolute error tolerance when decomposing.

        Returns:
            A QasmTwoQubitGate implementing the matrix.
        """
        kak = linalg.kak_decomposition(mat, linalg.Tolerance(atol=atol))
        return QasmTwoQubitGate(kak)

    def _unitary_(self):
        return protocols.unitary(self.kak)

    def _decompose_(self, qubits: Sequence[ops.QubitId]) -> ops.OP_TREE:
        q0, q1 = qubits
        x, y, z = self.kak.interaction_coefficients
        a = x * -2 / np.pi + 0.5
        b = y * -2 / np.pi + 0.5
        c = z * -2 / np.pi + 0.5

        b0, b1 = self.kak.single_qubit_operations_before
        yield QasmUGate.from_matrix(b0).on(q0)
        yield QasmUGate.from_matrix(b1).on(q1)

        yield ops.X(q0)**0.5
        yield ops.CNOT(q0, q1)
        yield ops.X(q0)**a
        yield ops.Y(q1)**b
        yield ops.CNOT(q1, q0)
        yield ops.X(q1)**-0.5
        yield ops.Z(q1)**c
        yield ops.CNOT(q0, q1)

        a0, a1 = self.kak.single_qubit_operations_after
        yield QasmUGate.from_matrix(a0).on(q0)
        yield QasmUGate.from_matrix(a1).on(q1)

    def __repr__(self) -> str:
        return 'cirq.circuits.qasm_output.QasmTwoQubitGate({!r})'.format(
            self.kak)


[docs]class QasmOutput: valid_id_re = re.compile('[a-z][a-zA-Z0-9_]*\Z')
[docs] def __init__(self, operations: ops.OP_TREE, qubits: Tuple[ops.QubitId, ...], header: str = '', precision: int = 10, version: str = '2.0') -> None: self.operations = tuple(ops.flatten_op_tree(operations)) self.qubits = qubits self.header = header self.measurements = tuple(cast(ops.GateOperation, op) for op in self.operations if ops.MeasurementGate.is_measurement(op)) meas_key_id_map, meas_comments = self._generate_measurement_ids() self.meas_comments = meas_comments qubit_id_map = self._generate_qubit_ids() self.args = protocols.QasmArgs( precision=precision, version=version, qubit_id_map=qubit_id_map, meas_key_id_map=meas_key_id_map)
def _generate_measurement_ids(self ) -> Tuple[Dict[str, str], Dict[str, Optional[str]]]: # Pick an id for the creg that will store each measurement meas_key_id_map = {} # type: Dict[str, str] meas_comments = {} # type: Dict[str, Optional[str]] meas_i = 0 for meas in self.measurements: key = cast(ops.MeasurementGate, meas.gate).key if key in meas_key_id_map: continue meas_id = 'm_{}'.format(key) if self.is_valid_qasm_id(meas_id): meas_comments[key] = None else: meas_id = 'm{}'.format(meas_i) meas_i += 1 meas_comments[key] = ' '.join(key.split('\n')) meas_key_id_map[key] = meas_id return meas_key_id_map, meas_comments def _generate_qubit_ids(self) -> Dict[ops.QubitId, str]: return {qubit: 'q[{}]'.format(i) for i, qubit in enumerate(self.qubits)}
[docs] def is_valid_qasm_id(self, id_str: str) -> bool: """Test if id_str is a valid id in QASM grammar.""" return self.valid_id_re.match(id_str) != None
[docs] def save(self, path: Union[str, bytes, int]) -> None: """Write QASM output to a file specified by path.""" with open(path, 'w') as f: def write(s: str) -> None: f.write(s) self._write_qasm(write)
def __str__(self) -> str: """Return QASM output as a string.""" output = [] self._write_qasm(lambda s: output.append(s)) return ''.join(output) def _write_qasm(self, output_func: Callable[[str], None]) -> None: self.args.validate_version('2.0') # Generate nice line spacing line_gap = [0] def output_line_gap(n): line_gap[0] = max(line_gap[0], n) def output(text): if line_gap[0] > 0: output_func('\n' * line_gap[0]) line_gap[0] = 0 output_func(text) # Comment header if self.header: for line in self.header.split('\n'): output(('// ' + line).rstrip() + '\n') output('\n') # Version output('OPENQASM 2.0;\n') output('include "qelib1.inc";\n') output_line_gap(2) # Function definitions # None yet # Register definitions # Qubit registers output('// Qubits: [{}]\n'.format(', '.join(map(str, self.qubits)))) output('qreg q[{}];\n'.format(len(self.qubits))) # Classical registers # Pick an id for the creg that will store each measurement already_output_keys = set() # type: Set[str] for meas in self.measurements: key = cast(ops.MeasurementGate, meas.gate).key if key in already_output_keys: continue already_output_keys.add(key) meas_id = self.args.meas_key_id_map[key] comment = self.meas_comments[key] if comment is None: output('creg {}[{}];\n'.format(meas_id, len(meas.qubits))) else: output('creg {}[{}]; // Measurement: {}\n'.format( meas_id, len(meas.qubits), comment)) output_line_gap(2) # Operations self._write_operations(self.operations, output, output_line_gap) def _write_operations(self, op_tree: ops.OP_TREE, output: Callable[[str], None], output_line_gap: Callable[[int], None]) -> None: def keep(op: ops.Operation) -> bool: return protocols.qasm(op, args=self.args, default=None) is not None def fallback(op): if len(op.qubits) not in [1, 2]: return NotImplemented mat = protocols.unitary(op, None) if mat is None: return NotImplemented if len(op.qubits) == 1: return QasmUGate.from_matrix(mat).on(*op.qubits) return QasmTwoQubitGate.from_matrix(mat).on(*op.qubits) def on_stuck(bad_op): return ValueError( 'Cannot output operation as QASM: {!r}'.format(bad_op)) for main_op in ops.flatten_op_tree(op_tree): decomposed = protocols.decompose( main_op, keep=keep, fallback_decomposer=fallback, on_stuck_raise=on_stuck) should_annotate = decomposed != [main_op] if should_annotate: output_line_gap(1) if isinstance(main_op, ops.GateOperation): x = str(main_op.gate).replace('\n', '\n //') output('// Gate: {!s}\n'.format(x)) else: x = str(main_op).replace('\n', '\n //') output('// Operation: {!s}\n'.format(x)) for decomposed_op in decomposed: output(protocols.qasm(decomposed_op, args=self.args)) if should_annotate: output_line_gap(1)