1# Copyright 2018 The Cirq Developers
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from typing import Union, TYPE_CHECKING
16
17import abc
18
19from cirq import circuits, devices, ops
20from cirq.contrib.acquaintance.gates import AcquaintanceOpportunityGate, SwapNetworkGate
21from cirq.contrib.acquaintance.bipartite import BipartiteSwapNetworkGate
22from cirq.contrib.acquaintance.shift_swap_network import ShiftSwapNetworkGate
23from cirq.contrib.acquaintance.permutation import PermutationGate
24
25if TYPE_CHECKING:
26    import cirq
27
28
29class AcquaintanceDevice(devices.Device, metaclass=abc.ABCMeta):
30    """A device that contains only acquaintance and permutation gates."""
31
32    gate_types = (AcquaintanceOpportunityGate, PermutationGate)
33
34    def validate_operation(self, operation: 'cirq.Operation') -> None:
35        if not (
36            isinstance(operation, ops.GateOperation) and isinstance(operation.gate, self.gate_types)
37        ):
38            raise ValueError(
39                'not (isinstance({0!r}, {1!r}) and '
40                'ininstance({0!r}.gate, {2!r})'.format(operation, ops.Operation, self.gate_types)
41            )
42
43
44def is_acquaintance_strategy(circuit: 'cirq.Circuit') -> bool:
45    return isinstance(circuit._device, AcquaintanceDevice)
46
47
48def get_acquaintance_size(obj: Union[circuits.Circuit, ops.Operation]) -> int:
49    """The maximum number of qubits to be acquainted with each other."""
50    if isinstance(obj, circuits.Circuit):
51        if not is_acquaintance_strategy(obj):
52            raise TypeError('not is_acquaintance_strategy(circuit)')
53        return max(tuple(get_acquaintance_size(op) for op in obj.all_operations()) or (0,))
54    if not isinstance(obj, ops.Operation):
55        raise TypeError('not isinstance(obj, (Circuit, Operation))')
56    if not isinstance(obj, ops.GateOperation):
57        return 0
58    if isinstance(obj.gate, AcquaintanceOpportunityGate):
59        return len(obj.qubits)
60    if isinstance(obj.gate, BipartiteSwapNetworkGate):
61        return 2
62    if isinstance(obj.gate, ShiftSwapNetworkGate):
63        return obj.gate.acquaintance_size()
64    if isinstance(obj.gate, SwapNetworkGate):
65        if obj.gate.acquaintance_size is None:
66            return sum(sorted(obj.gate.part_lens)[-2:])
67        if (obj.gate.acquaintance_size - 1) in obj.gate.part_lens:
68            return obj.gate.acquaintance_size
69    sizer = getattr(obj.gate, '_acquaintance_size_', None)
70    return 0 if sizer is None else sizer(len(obj.qubits))
71
72
73class _UnconstrainedAcquaintanceDevice(AcquaintanceDevice):
74    """An acquaintance device with no constraints other than of the gate types."""
75
76    def __repr__(self) -> str:
77        return 'UnconstrainedAcquaintanceDevice'  # coverage: ignore
78
79
80UnconstrainedAcquaintanceDevice = _UnconstrainedAcquaintanceDevice()
81