# 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.
from typing import Optional
from cirq import circuits, ops, protocols, value
from cirq.optimizers import two_qubit_decompositions
[docs]class ConvertToCzAndSingleGates(circuits.PointOptimizer):
"""Attempts to convert strange multi-qubit gates into CZ and single qubit
gates.
First, checks if the operation has a unitary effect. If so, and the gate is
a 1-qubit or 2-qubit gate, then performs circuit synthesis of the
operation.
Second, attempts to `cirq.decompose` to the operation.
Third, if ignore_failures is set, gives up and returns the gate unchanged.
Otherwise raises a TypeError.
"""
[docs] def __init__(self,
ignore_failures: bool = False,
allow_partial_czs: bool = False) -> None:
"""
Args:
ignore_failures: If set, gates that fail to convert are forwarded
unchanged. If not set, conversion failures raise a TypeError.
allow_partial_czs: If set, the decomposition is permitted to use
gates of the form `cirq.CZ**t`, instead of only `cirq.CZ`.
"""
super().__init__()
self.ignore_failures = ignore_failures
self.allow_partial_czs = allow_partial_czs
def _keep(self, op: ops.Operation) -> bool:
# Check if this is a CZ
# Only keep partial CZ gates if allow_partial_czs
if (isinstance(op, ops.GateOperation)
and isinstance(op.gate, ops.CZPowGate)
and (self.allow_partial_czs or value.canonicalize_half_turns(
op.gate.exponent) == 1)):
return True
# Measurement?
if ops.MeasurementGate.is_measurement(op):
return True
# SingleQubit known matrix
if len(op.qubits) == 1 and protocols.has_unitary(op):
return True
return False
def _decompose_two_qubit_unitaries(self, op: ops.Operation) -> ops.OP_TREE:
# Known matrix?
if len(op.qubits) == 2:
mat = protocols.unitary(op, None)
if mat is not None:
return two_qubit_decompositions.two_qubit_matrix_to_operations(
op.qubits[0],
op.qubits[1],
mat,
allow_partial_czs=self.allow_partial_czs)
return NotImplemented
def _on_stuck_raise(self, op: ops.Operation):
raise TypeError("Don't know how to work with {!r}. "
"It isn't composite or an operation with a "
"known unitary effect on 1 or 2 qubits.".format(op))
[docs] def optimization_at(self,
circuit: circuits.Circuit,
index: int,
op: ops.Operation
) -> Optional[circuits.PointOptimizationSummary]:
converted = protocols.decompose(
op,
intercepting_decomposer=self._decompose_two_qubit_unitaries,
keep=self._keep,
on_stuck_raise=(None
if self.ignore_failures
else self._on_stuck_raise))
if converted == [op]:
return None
return circuits.PointOptimizationSummary(
clear_span=1,
new_operations=converted,
clear_qubits=op.qubits)