diff --git a/qiskit/circuit/_standard_gates_commutations.py b/qiskit/circuit/_standard_gates_commutations.py new file mode 100644 index 0000000000..9dc95b675d --- /dev/null +++ b/qiskit/circuit/_standard_gates_commutations.py @@ -0,0 +1,3264 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Dictionary containing commutation relations of unparameterizable standard gates. The dictionary key +is defined by a pair of gates and, optionally, their relative placement to each other, i.e.: + + ┌───┐┌───┐ +q: ┤ X ├┤ Z ├ -> standard_gates_commutations["x", "z"] = False + └───┘└───┘ + +or + ┌───┐ +q_0: ──■──┤ X ├ + ┌─┴─┐└─┬─┘ -> standard_gates_commutations["cx", "cy"][1, 0] = False +q_1: ┤ Y ├──■── + └───┘ + +or + +q_0: ──■─────── + ┌─┴─┐ +q_1: ┤ X ├──■── -> standard_gates_commutations['cx', 'cx'][None, 0] = False + └───┘┌─┴─┐ +q_2: ─────┤ X ├ + └───┘ +Note that the commutation of a pair of gates is only stored in one specific order, i.e. the commutation +of a pair of cy and cx gates is only stored in standard_gates_commutations["cx", "cy"]. The order of the +gates is derived from the number of qubits in each gate and the value of the integer representation of +the gate name. + +The values of standard_gates_commutations can be a Boolean value denoting whether the pair of gates +commute or another dictionary if the relative placement of the pair of gates has an impact on the +commutation. The relative placement is defined by the gate qubit arrangements as q2^{-1}[q1[i]] where +q1[i] is the ith qubit of the first gate and q2^{-1}[q] returns the qubit index of qubit q in the second +gate (possibly 'None'). In the second example, the zeroth qubit of cy overlaps with qubit 1 of cx. Non- +overlapping qubits are specified as 'None' in the dictionary key, e.g. example 3, there is no overlap +on the zeroth qubit of the first cx but the zeroth qubit of the second gate overlaps with qubit 1 of the +first cx. +""" + +standard_gates_commutations = { + ("id", "id"): True, + ("id", "sx"): True, + ("id", "cx"): True, + ("id", "c3sx"): True, + ("id", "ccx"): True, + ("id", "dcx"): True, + ("id", "ch"): True, + ("id", "cswap"): True, + ("id", "csx"): True, + ("id", "cy"): True, + ("id", "cz"): True, + ("id", "ccz"): True, + ("id", "rccx"): True, + ("id", "rcccx"): True, + ("id", "ecr"): True, + ("id", "sdg"): True, + ("id", "cs"): True, + ("id", "csdg"): True, + ("id", "swap"): True, + ("id", "iswap"): True, + ("id", "sxdg"): True, + ("id", "tdg"): True, + ("sx", "sx"): True, + ("sx", "cx"): { + (0,): False, + (1,): True, + }, + ("sx", "c3sx"): { + (0,): False, + (1,): False, + (2,): False, + (3,): True, + }, + ("sx", "ccx"): { + (0,): False, + (1,): False, + (2,): True, + }, + ("sx", "dcx"): False, + ("sx", "ch"): False, + ("sx", "cswap"): False, + ("sx", "csx"): { + (0,): False, + (1,): True, + }, + ("sx", "cy"): False, + ("sx", "cz"): False, + ("sx", "ccz"): False, + ("sx", "rccx"): False, + ("sx", "rcccx"): False, + ("sx", "ecr"): { + (0,): False, + (1,): True, + }, + ("sx", "sdg"): False, + ("sx", "cs"): False, + ("sx", "csdg"): False, + ("sx", "swap"): False, + ("sx", "iswap"): False, + ("sx", "sxdg"): True, + ("sx", "tdg"): False, + ("x", "id"): True, + ("x", "sx"): True, + ("x", "x"): True, + ("x", "cx"): { + (0,): False, + (1,): True, + }, + ("x", "c3sx"): { + (0,): False, + (1,): False, + (2,): False, + (3,): True, + }, + ("x", "ccx"): { + (0,): False, + (1,): False, + (2,): True, + }, + ("x", "dcx"): False, + ("x", "ch"): False, + ("x", "cswap"): False, + ("x", "csx"): { + (0,): False, + (1,): True, + }, + ("x", "cy"): False, + ("x", "cz"): False, + ("x", "ccz"): False, + ("x", "rccx"): False, + ("x", "rcccx"): False, + ("x", "ecr"): { + (0,): False, + (1,): True, + }, + ("x", "sdg"): False, + ("x", "cs"): False, + ("x", "csdg"): False, + ("x", "swap"): False, + ("x", "iswap"): False, + ("x", "sxdg"): True, + ("x", "tdg"): False, + ("x", "y"): False, + ("x", "z"): False, + ("cx", "cx"): { + (0, 1): True, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("cx", "c3sx"): { + (0, 1): False, + (0, 2): False, + (0, 3): True, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): True, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): True, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): True, + }, + ("cx", "ccx"): { + (0, 1): False, + (0, 2): True, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): True, + }, + ("cx", "dcx"): False, + ("cx", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("cx", "csx"): { + (0, 1): True, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("cx", "cy"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("cx", "cz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("cx", "ccz"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): True, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("cx", "rccx"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("cx", "rcccx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("cx", "ecr"): { + (0, 1): False, + (0, None): False, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("cx", "csdg"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("cx", "swap"): False, + ("cx", "iswap"): False, + ("c3sx", "c3sx"): { + (0, 1, 2, 3): True, + (0, 1, 2, None): True, + (0, 1, 3, 2): False, + (0, 1, 3, None): False, + (0, 1, None, 2): False, + (0, 1, None, 3): True, + (0, 1, None, None): True, + (0, 2, 1, 3): True, + (0, 2, 1, None): True, + (0, 2, 3, 1): False, + (0, 2, 3, None): False, + (0, 2, None, 1): False, + (0, 2, None, 3): True, + (0, 2, None, None): True, + (0, 3, 1, 2): False, + (0, 3, 1, None): False, + (0, 3, 2, 1): False, + (0, 3, 2, None): False, + (0, 3, None, 1): False, + (0, 3, None, 2): False, + (0, 3, None, None): False, + (0, None, 1, 2): False, + (0, None, 1, 3): True, + (0, None, 1, None): True, + (0, None, 2, 1): False, + (0, None, 2, 3): True, + (0, None, 2, None): True, + (0, None, 3, 1): False, + (0, None, 3, 2): False, + (0, None, 3, None): False, + (0, None, None, 1): False, + (0, None, None, 2): False, + (0, None, None, 3): True, + (0, None, None, None): True, + (1, 0, 2, 3): True, + (1, 0, 2, None): True, + (1, 0, 3, 2): False, + (1, 0, 3, None): False, + (1, 0, None, 2): False, + (1, 0, None, 3): True, + (1, 0, None, None): True, + (1, 2, 0, 3): True, + (1, 2, 0, None): True, + (1, 2, 3, 0): False, + (1, 2, 3, None): False, + (1, 2, None, 0): False, + (1, 2, None, 3): True, + (1, 2, None, None): True, + (1, 3, 0, 2): False, + (1, 3, 0, None): False, + (1, 3, 2, 0): False, + (1, 3, 2, None): False, + (1, 3, None, 0): False, + (1, 3, None, 2): False, + (1, 3, None, None): False, + (1, None, 0, 2): False, + (1, None, 0, 3): True, + (1, None, 0, None): True, + (1, None, 2, 0): False, + (1, None, 2, 3): True, + (1, None, 2, None): True, + (1, None, 3, 0): False, + (1, None, 3, 2): False, + (1, None, 3, None): False, + (1, None, None, 0): False, + (1, None, None, 2): False, + (1, None, None, 3): True, + (1, None, None, None): True, + (2, 0, 1, 3): True, + (2, 0, 1, None): True, + (2, 0, 3, 1): False, + (2, 0, 3, None): False, + (2, 0, None, 1): False, + (2, 0, None, 3): True, + (2, 0, None, None): True, + (2, 1, 0, 3): True, + (2, 1, 0, None): True, + (2, 1, 3, 0): False, + (2, 1, 3, None): False, + (2, 1, None, 0): False, + (2, 1, None, 3): True, + (2, 1, None, None): True, + (2, 3, 0, 1): False, + (2, 3, 0, None): False, + (2, 3, 1, 0): False, + (2, 3, 1, None): False, + (2, 3, None, 0): False, + (2, 3, None, 1): False, + (2, 3, None, None): False, + (2, None, 0, 1): False, + (2, None, 0, 3): True, + (2, None, 0, None): True, + (2, None, 1, 0): False, + (2, None, 1, 3): True, + (2, None, 1, None): True, + (2, None, 3, 0): False, + (2, None, 3, 1): False, + (2, None, 3, None): False, + (2, None, None, 0): False, + (2, None, None, 1): False, + (2, None, None, 3): True, + (2, None, None, None): True, + (3, 0, 1, 2): False, + (3, 0, 1, None): False, + (3, 0, 2, 1): False, + (3, 0, 2, None): False, + (3, 0, None, 1): False, + (3, 0, None, 2): False, + (3, 0, None, None): False, + (3, 1, 0, 2): False, + (3, 1, 0, None): False, + (3, 1, 2, 0): False, + (3, 1, 2, None): False, + (3, 1, None, 0): False, + (3, 1, None, 2): False, + (3, 1, None, None): False, + (3, 2, 0, 1): False, + (3, 2, 0, None): False, + (3, 2, 1, 0): False, + (3, 2, 1, None): False, + (3, 2, None, 0): False, + (3, 2, None, 1): False, + (3, 2, None, None): False, + (3, None, 0, 1): False, + (3, None, 0, 2): False, + (3, None, 0, None): False, + (3, None, 1, 0): False, + (3, None, 1, 2): False, + (3, None, 1, None): False, + (3, None, 2, 0): False, + (3, None, 2, 1): False, + (3, None, 2, None): False, + (3, None, None, 0): False, + (3, None, None, 1): False, + (3, None, None, 2): False, + (3, None, None, None): False, + (None, 0, 1, 2): False, + (None, 0, 1, 3): True, + (None, 0, 1, None): True, + (None, 0, 2, 1): False, + (None, 0, 2, 3): True, + (None, 0, 2, None): True, + (None, 0, 3, 1): False, + (None, 0, 3, 2): False, + (None, 0, 3, None): False, + (None, 0, None, 1): False, + (None, 0, None, 2): False, + (None, 0, None, 3): True, + (None, 0, None, None): True, + (None, 1, 0, 2): False, + (None, 1, 0, 3): True, + (None, 1, 0, None): True, + (None, 1, 2, 0): False, + (None, 1, 2, 3): True, + (None, 1, 2, None): True, + (None, 1, 3, 0): False, + (None, 1, 3, 2): False, + (None, 1, 3, None): False, + (None, 1, None, 0): False, + (None, 1, None, 2): False, + (None, 1, None, 3): True, + (None, 1, None, None): True, + (None, 2, 0, 1): False, + (None, 2, 0, 3): True, + (None, 2, 0, None): True, + (None, 2, 1, 0): False, + (None, 2, 1, 3): True, + (None, 2, 1, None): True, + (None, 2, 3, 0): False, + (None, 2, 3, 1): False, + (None, 2, 3, None): False, + (None, 2, None, 0): False, + (None, 2, None, 1): False, + (None, 2, None, 3): True, + (None, 2, None, None): True, + (None, 3, 0, 1): False, + (None, 3, 0, 2): False, + (None, 3, 0, None): False, + (None, 3, 1, 0): False, + (None, 3, 1, 2): False, + (None, 3, 1, None): False, + (None, 3, 2, 0): False, + (None, 3, 2, 1): False, + (None, 3, 2, None): False, + (None, 3, None, 0): False, + (None, 3, None, 1): False, + (None, 3, None, 2): False, + (None, 3, None, None): False, + (None, None, 0, 1): False, + (None, None, 0, 2): False, + (None, None, 0, 3): True, + (None, None, 0, None): True, + (None, None, 1, 0): False, + (None, None, 1, 2): False, + (None, None, 1, 3): True, + (None, None, 1, None): True, + (None, None, 2, 0): False, + (None, None, 2, 1): False, + (None, None, 2, 3): True, + (None, None, 2, None): True, + (None, None, 3, 0): False, + (None, None, 3, 1): False, + (None, None, 3, 2): False, + (None, None, 3, None): False, + (None, None, None, 0): False, + (None, None, None, 1): False, + (None, None, None, 2): False, + (None, None, None, 3): True, + }, + ("c3sx", "rcccx"): { + (0, 1, 2, 3): False, + (0, 1, 2, None): True, + (0, 1, 3, 2): False, + (0, 1, 3, None): False, + (0, 1, None, 2): False, + (0, 1, None, 3): False, + (0, 1, None, None): True, + (0, 2, 1, 3): False, + (0, 2, 1, None): True, + (0, 2, 3, 1): False, + (0, 2, 3, None): False, + (0, 2, None, 1): False, + (0, 2, None, 3): False, + (0, 2, None, None): True, + (0, 3, 1, 2): False, + (0, 3, 1, None): False, + (0, 3, 2, 1): False, + (0, 3, 2, None): False, + (0, 3, None, 1): False, + (0, 3, None, 2): False, + (0, 3, None, None): False, + (0, None, 1, 2): False, + (0, None, 1, 3): False, + (0, None, 1, None): True, + (0, None, 2, 1): False, + (0, None, 2, 3): False, + (0, None, 2, None): True, + (0, None, 3, 1): False, + (0, None, 3, 2): False, + (0, None, 3, None): False, + (0, None, None, 1): False, + (0, None, None, 2): False, + (0, None, None, 3): False, + (0, None, None, None): True, + (1, 0, 2, 3): False, + (1, 0, 2, None): True, + (1, 0, 3, 2): False, + (1, 0, 3, None): False, + (1, 0, None, 2): False, + (1, 0, None, 3): False, + (1, 0, None, None): True, + (1, 2, 0, 3): False, + (1, 2, 0, None): True, + (1, 2, 3, 0): False, + (1, 2, 3, None): False, + (1, 2, None, 0): False, + (1, 2, None, 3): False, + (1, 2, None, None): True, + (1, 3, 0, 2): False, + (1, 3, 0, None): False, + (1, 3, 2, 0): False, + (1, 3, 2, None): False, + (1, 3, None, 0): False, + (1, 3, None, 2): False, + (1, 3, None, None): False, + (1, None, 0, 2): False, + (1, None, 0, 3): False, + (1, None, 0, None): True, + (1, None, 2, 0): False, + (1, None, 2, 3): False, + (1, None, 2, None): True, + (1, None, 3, 0): False, + (1, None, 3, 2): False, + (1, None, 3, None): False, + (1, None, None, 0): False, + (1, None, None, 2): False, + (1, None, None, 3): False, + (1, None, None, None): True, + (2, 0, 1, 3): False, + (2, 0, 1, None): True, + (2, 0, 3, 1): False, + (2, 0, 3, None): False, + (2, 0, None, 1): False, + (2, 0, None, 3): False, + (2, 0, None, None): True, + (2, 1, 0, 3): False, + (2, 1, 0, None): True, + (2, 1, 3, 0): False, + (2, 1, 3, None): False, + (2, 1, None, 0): False, + (2, 1, None, 3): False, + (2, 1, None, None): True, + (2, 3, 0, 1): False, + (2, 3, 0, None): False, + (2, 3, 1, 0): False, + (2, 3, 1, None): False, + (2, 3, None, 0): False, + (2, 3, None, 1): False, + (2, 3, None, None): False, + (2, None, 0, 1): False, + (2, None, 0, 3): False, + (2, None, 0, None): True, + (2, None, 1, 0): False, + (2, None, 1, 3): False, + (2, None, 1, None): True, + (2, None, 3, 0): False, + (2, None, 3, 1): False, + (2, None, 3, None): False, + (2, None, None, 0): False, + (2, None, None, 1): False, + (2, None, None, 3): False, + (2, None, None, None): True, + (3, 0, 1, 2): False, + (3, 0, 1, None): False, + (3, 0, 2, 1): False, + (3, 0, 2, None): False, + (3, 0, None, 1): False, + (3, 0, None, 2): False, + (3, 0, None, None): False, + (3, 1, 0, 2): False, + (3, 1, 0, None): False, + (3, 1, 2, 0): False, + (3, 1, 2, None): False, + (3, 1, None, 0): False, + (3, 1, None, 2): False, + (3, 1, None, None): False, + (3, 2, 0, 1): False, + (3, 2, 0, None): False, + (3, 2, 1, 0): False, + (3, 2, 1, None): False, + (3, 2, None, 0): False, + (3, 2, None, 1): False, + (3, 2, None, None): False, + (3, None, 0, 1): False, + (3, None, 0, 2): False, + (3, None, 0, None): False, + (3, None, 1, 0): False, + (3, None, 1, 2): False, + (3, None, 1, None): False, + (3, None, 2, 0): False, + (3, None, 2, 1): False, + (3, None, 2, None): False, + (3, None, None, 0): False, + (3, None, None, 1): False, + (3, None, None, 2): False, + (3, None, None, None): False, + (None, 0, 1, 2): False, + (None, 0, 1, 3): False, + (None, 0, 1, None): True, + (None, 0, 2, 1): False, + (None, 0, 2, 3): False, + (None, 0, 2, None): True, + (None, 0, 3, 1): False, + (None, 0, 3, 2): False, + (None, 0, 3, None): False, + (None, 0, None, 1): False, + (None, 0, None, 2): False, + (None, 0, None, 3): False, + (None, 0, None, None): True, + (None, 1, 0, 2): False, + (None, 1, 0, 3): False, + (None, 1, 0, None): True, + (None, 1, 2, 0): False, + (None, 1, 2, 3): False, + (None, 1, 2, None): True, + (None, 1, 3, 0): False, + (None, 1, 3, 2): False, + (None, 1, 3, None): False, + (None, 1, None, 0): False, + (None, 1, None, 2): False, + (None, 1, None, 3): False, + (None, 1, None, None): True, + (None, 2, 0, 1): False, + (None, 2, 0, 3): False, + (None, 2, 0, None): True, + (None, 2, 1, 0): False, + (None, 2, 1, 3): False, + (None, 2, 1, None): True, + (None, 2, 3, 0): False, + (None, 2, 3, 1): False, + (None, 2, 3, None): False, + (None, 2, None, 0): False, + (None, 2, None, 1): False, + (None, 2, None, 3): False, + (None, 2, None, None): True, + (None, 3, 0, 1): False, + (None, 3, 0, 2): False, + (None, 3, 0, None): False, + (None, 3, 1, 0): False, + (None, 3, 1, 2): False, + (None, 3, 1, None): False, + (None, 3, 2, 0): False, + (None, 3, 2, 1): False, + (None, 3, 2, None): False, + (None, 3, None, 0): False, + (None, 3, None, 1): False, + (None, 3, None, 2): False, + (None, 3, None, None): False, + (None, None, 0, 1): False, + (None, None, 0, 2): False, + (None, None, 0, 3): False, + (None, None, 0, None): True, + (None, None, 1, 0): False, + (None, None, 1, 2): False, + (None, None, 1, 3): False, + (None, None, 1, None): True, + (None, None, 2, 0): False, + (None, None, 2, 1): False, + (None, None, 2, 3): False, + (None, None, 2, None): True, + (None, None, 3, 0): False, + (None, None, 3, 1): False, + (None, None, 3, 2): False, + (None, None, 3, None): False, + (None, None, None, 0): False, + (None, None, None, 1): False, + (None, None, None, 2): False, + (None, None, None, 3): False, + }, + ("ccx", "c3sx"): { + (0, 1, 2): False, + (0, 1, 3): True, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, 3): True, + (0, 2, None): True, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, 3): True, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, 3): True, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, 3): True, + (1, 2, None): True, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, 3): True, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, 3): True, + (2, 0, None): True, + (2, 1, 0): False, + (2, 1, 3): True, + (2, 1, None): True, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, 3): True, + (2, None, None): True, + (3, 0, 1): False, + (3, 0, 2): False, + (3, 0, None): False, + (3, 1, 0): False, + (3, 1, 2): False, + (3, 1, None): False, + (3, 2, 0): False, + (3, 2, 1): False, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, 3): True, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, 3): True, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, 3): True, + (None, 2, None): True, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + (None, None, 3): True, + }, + ("ccx", "ccx"): { + (0, 1, 2): True, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, None): False, + (0, None, 1): False, + (0, None, 2): True, + (0, None, None): True, + (1, 0, 2): True, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, None): False, + (1, None, 0): False, + (1, None, 2): True, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, None): False, + (2, 1, 0): False, + (2, 1, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): False, + (None, 0, 1): False, + (None, 0, 2): True, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): True, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): True, + }, + ("ccx", "cswap"): { + (0, 1, 2): False, + (0, 1, None): False, + (0, 2, 1): False, + (0, 2, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, None): False, + (1, 2, 0): True, + (1, 2, None): True, + (1, None, 0): False, + (1, None, 2): False, + (1, None, None): False, + (2, 0, 1): False, + (2, 0, None): False, + (2, 1, 0): True, + (2, 1, None): True, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, None): False, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + }, + ("ccx", "ccz"): { + (0, 1, 2): False, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, None): True, + (0, None, 1): False, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, None): True, + (1, None, 0): False, + (1, None, 2): False, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, None): True, + (2, 1, 0): False, + (2, 1, None): True, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): True, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, None): True, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + }, + ("ccx", "rccx"): { + (0, 1, 2): False, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, None): False, + (2, 1, 0): False, + (2, 1, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + }, + ("ccx", "rcccx"): { + (0, 1, 2): False, + (0, 1, 3): False, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, 3): False, + (0, 2, None): True, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, 3): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, 3): False, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, 3): False, + (1, 2, None): True, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, 3): False, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, 3): False, + (2, 0, None): True, + (2, 1, 0): False, + (2, 1, 3): False, + (2, 1, None): True, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, 3): False, + (2, None, None): True, + (3, 0, 1): False, + (3, 0, 2): False, + (3, 0, None): False, + (3, 1, 0): False, + (3, 1, 2): False, + (3, 1, None): False, + (3, 2, 0): False, + (3, 2, 1): False, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, 3): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, 3): False, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, 3): False, + (None, 2, None): True, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + (None, None, 3): False, + }, + ("dcx", "c3sx"): False, + ("dcx", "ccx"): False, + ("dcx", "dcx"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("dcx", "cswap"): False, + ("dcx", "ccz"): False, + ("dcx", "rccx"): False, + ("dcx", "rcccx"): False, + ("dcx", "ecr"): False, + ("dcx", "csdg"): False, + ("dcx", "swap"): False, + ("dcx", "iswap"): False, + ("ch", "cx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("ch", "c3sx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("ch", "ccx"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("ch", "dcx"): False, + ("ch", "ch"): { + (0, 1): True, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("ch", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("ch", "csx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("ch", "cy"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("ch", "cz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("ch", "ccz"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): True, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("ch", "rccx"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("ch", "rcccx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("ch", "ecr"): False, + ("ch", "cs"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("ch", "csdg"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("ch", "swap"): False, + ("ch", "iswap"): False, + ("cswap", "c3sx"): { + (0, 1, 2): True, + (0, 1, 3): False, + (0, 1, None): False, + (0, 2, 1): True, + (0, 2, 3): False, + (0, 2, None): False, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, 3): False, + (0, None, None): True, + (1, 0, 2): True, + (1, 0, 3): False, + (1, 0, None): False, + (1, 2, 0): True, + (1, 2, 3): False, + (1, 2, None): False, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, 3): False, + (1, None, None): True, + (2, 0, 1): True, + (2, 0, 3): False, + (2, 0, None): False, + (2, 1, 0): True, + (2, 1, 3): False, + (2, 1, None): False, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, 3): False, + (2, None, None): True, + (3, 0, 1): True, + (3, 0, 2): True, + (3, 0, None): False, + (3, 1, 0): True, + (3, 1, 2): True, + (3, 1, None): False, + (3, 2, 0): True, + (3, 2, 1): True, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): True, + (None, 0, 2): True, + (None, 0, 3): False, + (None, 0, None): False, + (None, 1, 0): True, + (None, 1, 2): True, + (None, 1, 3): False, + (None, 1, None): False, + (None, 2, 0): True, + (None, 2, 1): True, + (None, 2, 3): False, + (None, 2, None): False, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + (None, None, 3): False, + }, + ("cswap", "cswap"): { + (0, 1, 2): True, + (0, 1, None): False, + (0, 2, 1): True, + (0, 2, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, None): False, + (1, 2, 0): False, + (1, 2, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, None): False, + (2, 0, 1): False, + (2, 0, None): False, + (2, 1, 0): False, + (2, 1, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, None): False, + (None, 1, 0): False, + (None, 1, 2): True, + (None, 1, None): False, + (None, 2, 0): False, + (None, 2, 1): True, + (None, 2, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + }, + ("cswap", "rcccx"): { + (0, 1, 2): False, + (0, 1, 3): False, + (0, 1, None): False, + (0, 2, 1): False, + (0, 2, 3): False, + (0, 2, None): False, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, 3): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, 3): False, + (1, 0, None): False, + (1, 2, 0): False, + (1, 2, 3): False, + (1, 2, None): False, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, 3): False, + (1, None, None): True, + (2, 0, 1): True, + (2, 0, 3): False, + (2, 0, None): False, + (2, 1, 0): True, + (2, 1, 3): False, + (2, 1, None): False, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, 3): False, + (2, None, None): True, + (3, 0, 1): True, + (3, 0, 2): False, + (3, 0, None): False, + (3, 1, 0): True, + (3, 1, 2): False, + (3, 1, None): False, + (3, 2, 0): False, + (3, 2, 1): False, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): True, + (None, 0, 2): False, + (None, 0, 3): False, + (None, 0, None): False, + (None, 1, 0): True, + (None, 1, 2): False, + (None, 1, 3): False, + (None, 1, None): False, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, 3): False, + (None, 2, None): False, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + (None, None, 3): False, + }, + ("csx", "c3sx"): { + (0, 1): False, + (0, 2): False, + (0, 3): True, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): True, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): True, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): True, + }, + ("csx", "ccx"): { + (0, 1): False, + (0, 2): True, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): True, + }, + ("csx", "dcx"): False, + ("csx", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("csx", "csx"): { + (0, 1): True, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("csx", "ccz"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): True, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("csx", "rccx"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("csx", "rcccx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("csx", "ecr"): { + (0, 1): False, + (0, None): False, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("csx", "csdg"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("csx", "swap"): False, + ("csx", "iswap"): False, + ("cy", "c3sx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("cy", "ccx"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("cy", "dcx"): False, + ("cy", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("cy", "csx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("cy", "cy"): { + (0, 1): True, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("cy", "cz"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("cy", "ccz"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): True, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("cy", "rccx"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("cy", "rcccx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): True, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, 3): True, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("cy", "ecr"): False, + ("cy", "csdg"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): True, + (None, 0): False, + (None, 1): False, + }, + ("cy", "swap"): False, + ("cy", "iswap"): False, + ("cz", "c3sx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("cz", "ccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("cz", "dcx"): False, + ("cz", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): True, + (None, 1): False, + (None, 2): False, + }, + ("cz", "csx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): False, + }, + ("cz", "cz"): True, + ("cz", "ccz"): True, + ("cz", "rccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("cz", "rcccx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("cz", "ecr"): False, + ("cz", "csdg"): True, + ("cz", "swap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("cz", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("ccz", "c3sx"): { + (0, 1, 2): True, + (0, 1, 3): False, + (0, 1, None): True, + (0, 2, 1): True, + (0, 2, 3): False, + (0, 2, None): True, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): True, + (0, None, 2): True, + (0, None, 3): False, + (0, None, None): True, + (1, 0, 2): True, + (1, 0, 3): False, + (1, 0, None): True, + (1, 2, 0): True, + (1, 2, 3): False, + (1, 2, None): True, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): True, + (1, None, 2): True, + (1, None, 3): False, + (1, None, None): True, + (2, 0, 1): True, + (2, 0, 3): False, + (2, 0, None): True, + (2, 1, 0): True, + (2, 1, 3): False, + (2, 1, None): True, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): True, + (2, None, 1): True, + (2, None, 3): False, + (2, None, None): True, + (3, 0, 1): False, + (3, 0, 2): False, + (3, 0, None): False, + (3, 1, 0): False, + (3, 1, 2): False, + (3, 1, None): False, + (3, 2, 0): False, + (3, 2, 1): False, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): True, + (None, 0, 2): True, + (None, 0, 3): False, + (None, 0, None): True, + (None, 1, 0): True, + (None, 1, 2): True, + (None, 1, 3): False, + (None, 1, None): True, + (None, 2, 0): True, + (None, 2, 1): True, + (None, 2, 3): False, + (None, 2, None): True, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): True, + (None, None, 1): True, + (None, None, 2): True, + (None, None, 3): False, + }, + ("ccz", "cswap"): { + (0, 1, 2): True, + (0, 1, None): False, + (0, 2, 1): True, + (0, 2, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): True, + (1, 0, None): False, + (1, 2, 0): True, + (1, 2, None): True, + (1, None, 0): False, + (1, None, 2): True, + (1, None, None): False, + (2, 0, 1): True, + (2, 0, None): False, + (2, 1, 0): True, + (2, 1, None): True, + (2, None, 0): False, + (2, None, 1): True, + (2, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): True, + (None, 1, None): False, + (None, 2, 0): False, + (None, 2, 1): True, + (None, 2, None): False, + (None, None, 0): True, + (None, None, 1): False, + (None, None, 2): False, + }, + ("ccz", "ccz"): True, + ("ccz", "rccx"): { + (0, 1, 2): False, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, None): False, + (0, None, 1): True, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, None): False, + (1, None, 0): True, + (1, None, 2): False, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, None): False, + (2, 1, 0): False, + (2, 1, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): False, + (None, 0, 1): True, + (None, 0, 2): False, + (None, 0, None): True, + (None, 1, 0): True, + (None, 1, 2): False, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, None): False, + (None, None, 0): True, + (None, None, 1): True, + (None, None, 2): False, + }, + ("ccz", "rcccx"): { + (0, 1, 2): True, + (0, 1, 3): False, + (0, 1, None): True, + (0, 2, 1): True, + (0, 2, 3): False, + (0, 2, None): True, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): True, + (0, None, 2): True, + (0, None, 3): False, + (0, None, None): True, + (1, 0, 2): True, + (1, 0, 3): False, + (1, 0, None): True, + (1, 2, 0): True, + (1, 2, 3): False, + (1, 2, None): True, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): True, + (1, None, 2): True, + (1, None, 3): False, + (1, None, None): True, + (2, 0, 1): True, + (2, 0, 3): False, + (2, 0, None): True, + (2, 1, 0): True, + (2, 1, 3): False, + (2, 1, None): True, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): True, + (2, None, 1): True, + (2, None, 3): False, + (2, None, None): True, + (3, 0, 1): False, + (3, 0, 2): False, + (3, 0, None): False, + (3, 1, 0): False, + (3, 1, 2): False, + (3, 1, None): False, + (3, 2, 0): False, + (3, 2, 1): False, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): True, + (None, 0, 2): True, + (None, 0, 3): False, + (None, 0, None): True, + (None, 1, 0): True, + (None, 1, 2): True, + (None, 1, 3): False, + (None, 1, None): True, + (None, 2, 0): True, + (None, 2, 1): True, + (None, 2, 3): False, + (None, 2, None): True, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): True, + (None, None, 1): True, + (None, None, 2): True, + (None, None, 3): False, + }, + ("h", "id"): True, + ("h", "sx"): False, + ("h", "x"): False, + ("h", "cx"): False, + ("h", "c3sx"): False, + ("h", "ccx"): False, + ("h", "dcx"): False, + ("h", "ch"): { + (0,): False, + (1,): True, + }, + ("h", "cswap"): False, + ("h", "csx"): False, + ("h", "cy"): False, + ("h", "cz"): False, + ("h", "ccz"): False, + ("h", "h"): True, + ("h", "rccx"): False, + ("h", "rcccx"): False, + ("h", "ecr"): False, + ("h", "s"): False, + ("h", "sdg"): False, + ("h", "cs"): False, + ("h", "csdg"): False, + ("h", "swap"): False, + ("h", "iswap"): False, + ("h", "sxdg"): False, + ("h", "t"): False, + ("h", "tdg"): False, + ("h", "y"): False, + ("h", "z"): False, + ("rccx", "c3sx"): { + (0, 1, 2): False, + (0, 1, 3): False, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, 3): False, + (0, 2, None): True, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, 3): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, 3): False, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, 3): False, + (1, 2, None): True, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, 3): False, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, 3): False, + (2, 0, None): True, + (2, 1, 0): False, + (2, 1, 3): False, + (2, 1, None): True, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, 3): False, + (2, None, None): True, + (3, 0, 1): False, + (3, 0, 2): False, + (3, 0, None): False, + (3, 1, 0): False, + (3, 1, 2): False, + (3, 1, None): False, + (3, 2, 0): False, + (3, 2, 1): False, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, 3): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, 3): False, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, 3): False, + (None, 2, None): True, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + (None, None, 3): False, + }, + ("rccx", "cswap"): { + (0, 1, 2): False, + (0, 1, None): False, + (0, 2, 1): False, + (0, 2, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, None): False, + (1, 2, 0): False, + (1, 2, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, None): False, + (2, 0, 1): False, + (2, 0, None): False, + (2, 1, 0): False, + (2, 1, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, None): False, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + }, + ("rccx", "rccx"): { + (0, 1, 2): True, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, None): True, + (1, 0, 2): True, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, None): False, + (2, 1, 0): False, + (2, 1, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): True, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + }, + ("rccx", "rcccx"): { + (0, 1, 2): False, + (0, 1, 3): False, + (0, 1, None): True, + (0, 2, 1): False, + (0, 2, 3): True, + (0, 2, None): True, + (0, 3, 1): False, + (0, 3, 2): False, + (0, 3, None): False, + (0, None, 1): False, + (0, None, 2): False, + (0, None, 3): False, + (0, None, None): True, + (1, 0, 2): False, + (1, 0, 3): False, + (1, 0, None): True, + (1, 2, 0): False, + (1, 2, 3): True, + (1, 2, None): True, + (1, 3, 0): False, + (1, 3, 2): False, + (1, 3, None): False, + (1, None, 0): False, + (1, None, 2): False, + (1, None, 3): False, + (1, None, None): True, + (2, 0, 1): False, + (2, 0, 3): True, + (2, 0, None): True, + (2, 1, 0): False, + (2, 1, 3): True, + (2, 1, None): True, + (2, 3, 0): False, + (2, 3, 1): False, + (2, 3, None): False, + (2, None, 0): False, + (2, None, 1): False, + (2, None, 3): False, + (2, None, None): True, + (3, 0, 1): False, + (3, 0, 2): False, + (3, 0, None): False, + (3, 1, 0): False, + (3, 1, 2): False, + (3, 1, None): False, + (3, 2, 0): False, + (3, 2, 1): False, + (3, 2, None): False, + (3, None, 0): False, + (3, None, 1): False, + (3, None, 2): False, + (3, None, None): False, + (None, 0, 1): False, + (None, 0, 2): False, + (None, 0, 3): False, + (None, 0, None): True, + (None, 1, 0): False, + (None, 1, 2): False, + (None, 1, 3): False, + (None, 1, None): True, + (None, 2, 0): False, + (None, 2, 1): False, + (None, 2, 3): True, + (None, 2, None): True, + (None, 3, 0): False, + (None, 3, 1): False, + (None, 3, 2): False, + (None, 3, None): False, + (None, None, 0): False, + (None, None, 1): False, + (None, None, 2): False, + (None, None, 3): False, + }, + ("rcccx", "rcccx"): { + (0, 1, 2, 3): True, + (0, 1, 2, None): True, + (0, 1, 3, 2): False, + (0, 1, 3, None): False, + (0, 1, None, 2): False, + (0, 1, None, 3): False, + (0, 1, None, None): True, + (0, 2, 1, 3): True, + (0, 2, 1, None): True, + (0, 2, 3, 1): False, + (0, 2, 3, None): False, + (0, 2, None, 1): False, + (0, 2, None, 3): False, + (0, 2, None, None): True, + (0, 3, 1, 2): False, + (0, 3, 1, None): False, + (0, 3, 2, 1): False, + (0, 3, 2, None): False, + (0, 3, None, 1): False, + (0, 3, None, 2): False, + (0, 3, None, None): False, + (0, None, 1, 2): False, + (0, None, 1, 3): False, + (0, None, 1, None): True, + (0, None, 2, 1): False, + (0, None, 2, 3): True, + (0, None, 2, None): True, + (0, None, 3, 1): False, + (0, None, 3, 2): False, + (0, None, 3, None): False, + (0, None, None, 1): False, + (0, None, None, 2): False, + (0, None, None, 3): False, + (0, None, None, None): True, + (1, 0, 2, 3): True, + (1, 0, 2, None): True, + (1, 0, 3, 2): False, + (1, 0, 3, None): False, + (1, 0, None, 2): False, + (1, 0, None, 3): False, + (1, 0, None, None): True, + (1, 2, 0, 3): True, + (1, 2, 0, None): True, + (1, 2, 3, 0): False, + (1, 2, 3, None): False, + (1, 2, None, 0): False, + (1, 2, None, 3): False, + (1, 2, None, None): True, + (1, 3, 0, 2): False, + (1, 3, 0, None): False, + (1, 3, 2, 0): False, + (1, 3, 2, None): False, + (1, 3, None, 0): False, + (1, 3, None, 2): False, + (1, 3, None, None): False, + (1, None, 0, 2): False, + (1, None, 0, 3): False, + (1, None, 0, None): True, + (1, None, 2, 0): False, + (1, None, 2, 3): True, + (1, None, 2, None): True, + (1, None, 3, 0): False, + (1, None, 3, 2): False, + (1, None, 3, None): False, + (1, None, None, 0): False, + (1, None, None, 2): False, + (1, None, None, 3): False, + (1, None, None, None): True, + (2, 0, 1, 3): True, + (2, 0, 1, None): True, + (2, 0, 3, 1): False, + (2, 0, 3, None): False, + (2, 0, None, 1): False, + (2, 0, None, 3): False, + (2, 0, None, None): True, + (2, 1, 0, 3): True, + (2, 1, 0, None): True, + (2, 1, 3, 0): False, + (2, 1, 3, None): False, + (2, 1, None, 0): False, + (2, 1, None, 3): False, + (2, 1, None, None): True, + (2, 3, 0, 1): False, + (2, 3, 0, None): False, + (2, 3, 1, 0): False, + (2, 3, 1, None): False, + (2, 3, None, 0): False, + (2, 3, None, 1): False, + (2, 3, None, None): False, + (2, None, 0, 1): False, + (2, None, 0, 3): True, + (2, None, 0, None): True, + (2, None, 1, 0): False, + (2, None, 1, 3): True, + (2, None, 1, None): True, + (2, None, 3, 0): False, + (2, None, 3, 1): False, + (2, None, 3, None): False, + (2, None, None, 0): False, + (2, None, None, 1): False, + (2, None, None, 3): False, + (2, None, None, None): True, + (3, 0, 1, 2): False, + (3, 0, 1, None): False, + (3, 0, 2, 1): False, + (3, 0, 2, None): False, + (3, 0, None, 1): False, + (3, 0, None, 2): False, + (3, 0, None, None): False, + (3, 1, 0, 2): False, + (3, 1, 0, None): False, + (3, 1, 2, 0): False, + (3, 1, 2, None): False, + (3, 1, None, 0): False, + (3, 1, None, 2): False, + (3, 1, None, None): False, + (3, 2, 0, 1): False, + (3, 2, 0, None): False, + (3, 2, 1, 0): False, + (3, 2, 1, None): False, + (3, 2, None, 0): False, + (3, 2, None, 1): False, + (3, 2, None, None): False, + (3, None, 0, 1): False, + (3, None, 0, 2): False, + (3, None, 0, None): False, + (3, None, 1, 0): False, + (3, None, 1, 2): False, + (3, None, 1, None): False, + (3, None, 2, 0): False, + (3, None, 2, 1): False, + (3, None, 2, None): False, + (3, None, None, 0): False, + (3, None, None, 1): False, + (3, None, None, 2): False, + (3, None, None, None): False, + (None, 0, 1, 2): False, + (None, 0, 1, 3): False, + (None, 0, 1, None): True, + (None, 0, 2, 1): False, + (None, 0, 2, 3): True, + (None, 0, 2, None): True, + (None, 0, 3, 1): False, + (None, 0, 3, 2): False, + (None, 0, 3, None): False, + (None, 0, None, 1): False, + (None, 0, None, 2): False, + (None, 0, None, 3): False, + (None, 0, None, None): True, + (None, 1, 0, 2): False, + (None, 1, 0, 3): False, + (None, 1, 0, None): True, + (None, 1, 2, 0): False, + (None, 1, 2, 3): True, + (None, 1, 2, None): True, + (None, 1, 3, 0): False, + (None, 1, 3, 2): False, + (None, 1, 3, None): False, + (None, 1, None, 0): False, + (None, 1, None, 2): False, + (None, 1, None, 3): False, + (None, 1, None, None): True, + (None, 2, 0, 1): False, + (None, 2, 0, 3): True, + (None, 2, 0, None): True, + (None, 2, 1, 0): False, + (None, 2, 1, 3): True, + (None, 2, 1, None): True, + (None, 2, 3, 0): False, + (None, 2, 3, 1): False, + (None, 2, 3, None): False, + (None, 2, None, 0): False, + (None, 2, None, 1): False, + (None, 2, None, 3): False, + (None, 2, None, None): True, + (None, 3, 0, 1): False, + (None, 3, 0, 2): False, + (None, 3, 0, None): False, + (None, 3, 1, 0): False, + (None, 3, 1, 2): False, + (None, 3, 1, None): False, + (None, 3, 2, 0): False, + (None, 3, 2, 1): False, + (None, 3, 2, None): False, + (None, 3, None, 0): False, + (None, 3, None, 1): False, + (None, 3, None, 2): False, + (None, 3, None, None): False, + (None, None, 0, 1): False, + (None, None, 0, 2): False, + (None, None, 0, 3): False, + (None, None, 0, None): True, + (None, None, 1, 0): False, + (None, None, 1, 2): False, + (None, None, 1, 3): False, + (None, None, 1, None): True, + (None, None, 2, 0): False, + (None, None, 2, 1): False, + (None, None, 2, 3): True, + (None, None, 2, None): True, + (None, None, 3, 0): False, + (None, None, 3, 1): False, + (None, None, 3, 2): False, + (None, None, 3, None): False, + (None, None, None, 0): False, + (None, None, None, 1): False, + (None, None, None, 2): False, + (None, None, None, 3): False, + }, + ("ecr", "c3sx"): { + (0, 1): False, + (0, 2): False, + (0, 3): False, + (0, None): False, + (1, 0): False, + (1, 2): False, + (1, 3): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): False, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): True, + }, + ("ecr", "ccx"): { + (0, 1): False, + (0, 2): False, + (0, None): False, + (1, 0): False, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): True, + }, + ("ecr", "cswap"): False, + ("ecr", "ccz"): False, + ("ecr", "rccx"): False, + ("ecr", "rcccx"): False, + ("ecr", "ecr"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): True, + }, + ("ecr", "csdg"): False, + ("ecr", "swap"): False, + ("ecr", "iswap"): False, + ("s", "id"): True, + ("s", "sx"): False, + ("s", "x"): False, + ("s", "cx"): { + (0,): True, + (1,): False, + }, + ("s", "c3sx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("s", "ccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("s", "dcx"): False, + ("s", "ch"): { + (0,): True, + (1,): False, + }, + ("s", "cswap"): { + (0,): True, + (1,): False, + (2,): False, + }, + ("s", "csx"): { + (0,): True, + (1,): False, + }, + ("s", "cy"): { + (0,): True, + (1,): False, + }, + ("s", "cz"): True, + ("s", "ccz"): True, + ("s", "rccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("s", "rcccx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("s", "ecr"): False, + ("s", "s"): True, + ("s", "sdg"): True, + ("s", "cs"): True, + ("s", "csdg"): True, + ("s", "swap"): False, + ("s", "iswap"): False, + ("s", "sxdg"): False, + ("s", "t"): True, + ("s", "tdg"): True, + ("s", "y"): False, + ("s", "z"): True, + ("sdg", "cx"): { + (0,): True, + (1,): False, + }, + ("sdg", "c3sx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("sdg", "ccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("sdg", "dcx"): False, + ("sdg", "ch"): { + (0,): True, + (1,): False, + }, + ("sdg", "cswap"): { + (0,): True, + (1,): False, + (2,): False, + }, + ("sdg", "csx"): { + (0,): True, + (1,): False, + }, + ("sdg", "cy"): { + (0,): True, + (1,): False, + }, + ("sdg", "cz"): True, + ("sdg", "ccz"): True, + ("sdg", "rccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("sdg", "rcccx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("sdg", "ecr"): False, + ("sdg", "sdg"): True, + ("sdg", "cs"): True, + ("sdg", "csdg"): True, + ("sdg", "swap"): False, + ("sdg", "iswap"): False, + ("sdg", "sxdg"): False, + ("sdg", "tdg"): True, + ("cs", "cx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): False, + }, + ("cs", "c3sx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("cs", "ccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("cs", "dcx"): False, + ("cs", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): True, + (None, 1): False, + (None, 2): False, + }, + ("cs", "csx"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): False, + }, + ("cs", "cy"): { + (0, 1): False, + (0, None): True, + (1, 0): False, + (1, None): False, + (None, 0): True, + (None, 1): False, + }, + ("cs", "cz"): True, + ("cs", "ccz"): True, + ("cs", "rccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("cs", "rcccx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("cs", "ecr"): False, + ("cs", "cs"): True, + ("cs", "csdg"): True, + ("cs", "swap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("cs", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("csdg", "c3sx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("csdg", "ccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("csdg", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): True, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): True, + (None, 1): False, + (None, 2): False, + }, + ("csdg", "ccz"): True, + ("csdg", "rccx"): { + (0, 1): True, + (0, 2): False, + (0, None): True, + (1, 0): True, + (1, 2): False, + (1, None): True, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): False, + }, + ("csdg", "rcccx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): True, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): True, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): True, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): True, + (None, 1): True, + (None, 2): True, + (None, 3): False, + }, + ("csdg", "csdg"): True, + ("csdg", "swap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("csdg", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("swap", "c3sx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): False, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): False, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): False, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("swap", "ccx"): { + (0, 1): True, + (0, 2): False, + (0, None): False, + (1, 0): True, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("swap", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): False, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("swap", "ccz"): { + (0, 1): True, + (0, 2): True, + (0, None): False, + (1, 0): True, + (1, 2): True, + (1, None): False, + (2, 0): True, + (2, 1): True, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("swap", "rccx"): False, + ("swap", "rcccx"): { + (0, 1): True, + (0, 2): False, + (0, 3): False, + (0, None): False, + (1, 0): True, + (1, 2): False, + (1, 3): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): False, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("swap", "swap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("swap", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("iswap", "c3sx"): { + (0, 1): True, + (0, 2): True, + (0, 3): False, + (0, None): False, + (1, 0): True, + (1, 2): True, + (1, 3): False, + (1, None): False, + (2, 0): True, + (2, 1): True, + (2, 3): False, + (2, None): False, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("iswap", "ccx"): { + (0, 1): True, + (0, 2): False, + (0, None): False, + (1, 0): True, + (1, 2): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("iswap", "cswap"): { + (0, 1): False, + (0, 2): False, + (0, None): False, + (1, 0): False, + (1, 2): True, + (1, None): False, + (2, 0): False, + (2, 1): True, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("iswap", "ccz"): { + (0, 1): True, + (0, 2): True, + (0, None): False, + (1, 0): True, + (1, 2): True, + (1, None): False, + (2, 0): True, + (2, 1): True, + (2, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + }, + ("iswap", "rccx"): False, + ("iswap", "rcccx"): { + (0, 1): True, + (0, 2): False, + (0, 3): False, + (0, None): False, + (1, 0): True, + (1, 2): False, + (1, 3): False, + (1, None): False, + (2, 0): False, + (2, 1): False, + (2, 3): False, + (2, None): False, + (3, 0): False, + (3, 1): False, + (3, 2): False, + (3, None): False, + (None, 0): False, + (None, 1): False, + (None, 2): False, + (None, 3): False, + }, + ("iswap", "iswap"): { + (0, 1): True, + (0, None): False, + (1, 0): True, + (1, None): False, + (None, 0): False, + (None, 1): False, + }, + ("sxdg", "cx"): { + (0,): False, + (1,): True, + }, + ("sxdg", "c3sx"): { + (0,): False, + (1,): False, + (2,): False, + (3,): True, + }, + ("sxdg", "ccx"): { + (0,): False, + (1,): False, + (2,): True, + }, + ("sxdg", "dcx"): False, + ("sxdg", "ch"): False, + ("sxdg", "cswap"): False, + ("sxdg", "csx"): { + (0,): False, + (1,): True, + }, + ("sxdg", "cy"): False, + ("sxdg", "cz"): False, + ("sxdg", "ccz"): False, + ("sxdg", "rccx"): False, + ("sxdg", "rcccx"): False, + ("sxdg", "ecr"): { + (0,): False, + (1,): True, + }, + ("sxdg", "cs"): False, + ("sxdg", "csdg"): False, + ("sxdg", "swap"): False, + ("sxdg", "iswap"): False, + ("sxdg", "sxdg"): True, + ("t", "id"): True, + ("t", "sx"): False, + ("t", "x"): False, + ("t", "cx"): { + (0,): True, + (1,): False, + }, + ("t", "c3sx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("t", "ccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("t", "dcx"): False, + ("t", "ch"): { + (0,): True, + (1,): False, + }, + ("t", "cswap"): { + (0,): True, + (1,): False, + (2,): False, + }, + ("t", "csx"): { + (0,): True, + (1,): False, + }, + ("t", "cy"): { + (0,): True, + (1,): False, + }, + ("t", "cz"): True, + ("t", "ccz"): True, + ("t", "rccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("t", "rcccx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("t", "ecr"): False, + ("t", "sdg"): True, + ("t", "cs"): True, + ("t", "csdg"): True, + ("t", "swap"): False, + ("t", "iswap"): False, + ("t", "sxdg"): False, + ("t", "t"): True, + ("t", "tdg"): True, + ("t", "y"): False, + ("t", "z"): True, + ("tdg", "cx"): { + (0,): True, + (1,): False, + }, + ("tdg", "c3sx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("tdg", "ccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("tdg", "dcx"): False, + ("tdg", "ch"): { + (0,): True, + (1,): False, + }, + ("tdg", "cswap"): { + (0,): True, + (1,): False, + (2,): False, + }, + ("tdg", "csx"): { + (0,): True, + (1,): False, + }, + ("tdg", "cy"): { + (0,): True, + (1,): False, + }, + ("tdg", "cz"): True, + ("tdg", "ccz"): True, + ("tdg", "rccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("tdg", "rcccx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("tdg", "ecr"): False, + ("tdg", "cs"): True, + ("tdg", "csdg"): True, + ("tdg", "swap"): False, + ("tdg", "iswap"): False, + ("tdg", "sxdg"): False, + ("tdg", "tdg"): True, + ("y", "id"): True, + ("y", "sx"): False, + ("y", "cx"): False, + ("y", "c3sx"): False, + ("y", "ccx"): False, + ("y", "dcx"): False, + ("y", "ch"): False, + ("y", "cswap"): False, + ("y", "csx"): False, + ("y", "cy"): { + (0,): False, + (1,): True, + }, + ("y", "cz"): False, + ("y", "ccz"): False, + ("y", "rccx"): False, + ("y", "rcccx"): False, + ("y", "ecr"): False, + ("y", "sdg"): False, + ("y", "cs"): False, + ("y", "csdg"): False, + ("y", "swap"): False, + ("y", "iswap"): False, + ("y", "sxdg"): False, + ("y", "tdg"): False, + ("y", "y"): True, + ("y", "z"): False, + ("z", "id"): True, + ("z", "sx"): False, + ("z", "cx"): { + (0,): True, + (1,): False, + }, + ("z", "c3sx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("z", "ccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("z", "dcx"): False, + ("z", "ch"): { + (0,): True, + (1,): False, + }, + ("z", "cswap"): { + (0,): True, + (1,): False, + (2,): False, + }, + ("z", "csx"): { + (0,): True, + (1,): False, + }, + ("z", "cy"): { + (0,): True, + (1,): False, + }, + ("z", "cz"): True, + ("z", "ccz"): True, + ("z", "rccx"): { + (0,): True, + (1,): True, + (2,): False, + }, + ("z", "rcccx"): { + (0,): True, + (1,): True, + (2,): True, + (3,): False, + }, + ("z", "ecr"): False, + ("z", "sdg"): True, + ("z", "cs"): True, + ("z", "csdg"): True, + ("z", "swap"): False, + ("z", "iswap"): False, + ("z", "sxdg"): False, + ("z", "tdg"): True, + ("z", "z"): True, +} diff --git a/qiskit/circuit/commutation_checker.py b/qiskit/circuit/commutation_checker.py index ad9d483099..7e025554db 100644 --- a/qiskit/circuit/commutation_checker.py +++ b/qiskit/circuit/commutation_checker.py @@ -13,9 +13,10 @@ """Code from commutative_analysis pass that checks commutation relations between DAG nodes.""" from functools import lru_cache -from typing import List +from typing import List, Union import numpy as np +from qiskit.circuit import Qubit from qiskit.circuit.operation import Operation from qiskit.circuit.controlflow import ControlFlowOp from qiskit.quantum_info.operators import Operator @@ -37,32 +38,20 @@ class CommutationChecker: evicting from the cache less useful entries, etc. """ - def __init__(self): + def __init__(self, standard_gate_commutations: dict = None, cache_max_entries: int = 10**6): super().__init__() - self.cache = {} + if standard_gate_commutations is None: + self._standard_commutations = {} + else: + self._standard_commutations = standard_gate_commutations + self._cache_max_entries = cache_max_entries - def _hashable_parameters(self, params): - """Convert the parameters of a gate into a hashable format for lookup in a dictionary. - - This aims to be fast in common cases, and is not intended to work outside of the lifetime of a - single commutation pass; it does not handle mutable state correctly if the state is actually - changed.""" - try: - hash(params) - return params - except TypeError: - pass - if isinstance(params, (list, tuple)): - return tuple(self._hashable_parameters(x) for x in params) - if isinstance(params, np.ndarray): - # We trust that the arrays will not be mutated during the commutation pass, since nothing - # would work if they were anyway. Using the id can potentially cause some additional cache - # misses if two UnitaryGate instances are being compared that have been separately - # constructed to have the same underlying matrix, but in practice the cost of string-ifying - # the matrix to get a cache key is far more expensive than just doing a small matmul. - return (np.ndarray, id(params)) - # Catch anything else with a slow conversion. - return ("fallback", str(params)) + # self._cached_commutation has the same structure as standard_gate_commutations, i.e. a + # dict[pair of gate names][relative placement][tuple of gate parameters] := True/False + self._cached_commutations = {} + self._current_cache_entries = 0 + self._cache_miss = 0 + self._cache_hit = 0 def commute( self, @@ -93,84 +82,322 @@ class CommutationChecker: Returns: bool: whether two operations commute. """ - # pylint: disable=too-many-return-statements + structural_commutation = _commutation_precheck( + op1, qargs1, cargs1, op2, qargs2, cargs2, max_num_qubits + ) - # We don't support commutation of conditional gates for now due to bugs in - # CommutativeCancellation. See gh-8553. - if ( - getattr(op1, "condition", None) is not None - or getattr(op2, "condition", None) is not None - ): - return False + if structural_commutation is not None: + return structural_commutation - # Commutation of ControlFlow gates also not supported yet. This may be - # pending a control flow graph. - if isinstance(op1, ControlFlowOp) or isinstance(op2, ControlFlowOp): - return False + first_op_tuple, second_op_tuple = _order_operations( + op1, qargs1, cargs1, op2, qargs2, cargs2 + ) + first_op, first_qargs, _ = first_op_tuple + second_op, second_qargs, _ = second_op_tuple + first_params = first_op.params + second_params = second_op.params - # These lines are adapted from dag_dependency and say that two gates over - # different quantum and classical bits necessarily commute. This is more - # permissive that the check from commutation_analysis, as for example it - # allows to commute X(1) and Measure(0, 0). - # Presumably this check was not present in commutation_analysis as - # it was only called on pairs of connected nodes from DagCircuit. - intersection_q = set(qargs1).intersection(set(qargs2)) - intersection_c = set(cargs1).intersection(set(cargs2)) - if not (intersection_q or intersection_c): - return True + commutation_lookup = self.check_commutation_entries( + first_op, first_qargs, second_op, second_qargs + ) - # Skip the check if the number of qubits for either operation is too large - if len(qargs1) > max_num_qubits or len(qargs2) > max_num_qubits: - return False + if commutation_lookup is not None: + return commutation_lookup - # These lines are adapted from commutation_analysis, which is more restrictive than the - # check from dag_dependency when considering nodes with "_directive". It would be nice to - # think which optimizations from dag_dependency can indeed be used. - for op in [op1, op2]: - if ( - getattr(op, "_directive", False) - or op.name in {"measure", "reset", "delay"} - or (getattr(op, "is_parameterized", False) and op.is_parameterized()) - ): - return False + # Compute commutation via matrix multiplication + is_commuting = _commute_matmul(first_op, first_qargs, second_op, second_qargs) - # The main code is adapted from commutative analysis. - # Assign indices to each of the qubits such that all `node1`'s qubits come first, followed by - # any _additional_ qubits `node2` addresses. This helps later when we need to compose one - # operator with the other, since we can easily expand `node1` with a suitable identity. - qarg = {q: i for i, q in enumerate(qargs1)} - num_qubits = len(qarg) - for q in qargs2: - if q not in qarg: - qarg[q] = num_qubits - num_qubits += 1 - qarg1 = tuple(qarg[q] for q in qargs1) - qarg2 = tuple(qarg[q] for q in qargs2) + # Store result in this session's commutation_library + # TODO implement LRU cache or similar + # Rebuild cache if current cache exceeded max size + if self._current_cache_entries >= self._cache_max_entries: + self.clear_cached_commutations() - node1_key = (op1.name, self._hashable_parameters(op1.params), qarg1) - node2_key = (op2.name, self._hashable_parameters(op2.params), qarg2) - try: - # We only need to try one orientation of the keys, since if we've seen the compound key - # before, we've set it in both orientations. - return self.cache[node1_key, node2_key] - except KeyError: - pass - - operator_1 = Operator(op1, input_dims=(2,) * len(qarg1), output_dims=(2,) * len(qarg1)) - operator_2 = Operator(op2, input_dims=(2,) * len(qarg2), output_dims=(2,) * len(qarg2)) - - if qarg1 == qarg2: - # Use full composition if possible to get the fastest matmul paths. - op12 = operator_1.compose(operator_2) - op21 = operator_2.compose(operator_1) + if len(first_params) > 0 or len(second_params) > 0: + self._cached_commutations.setdefault((first_op.name, second_op.name), {}).setdefault( + _get_relative_placement(first_qargs, second_qargs), {} + )[ + (_hashable_parameters(first_params), _hashable_parameters(second_params)) + ] = is_commuting else: - # Expand operator_1 to be large enough to contain operator_2 as well; this relies on qargs1 - # being the lowest possible indices so the identity can be tensored before it. - extra_qarg2 = num_qubits - len(qarg1) - if extra_qarg2: - id_op = _identity_op(extra_qarg2) - operator_1 = id_op.tensor(operator_1) - op12 = operator_1.compose(operator_2, qargs=qarg2, front=False) - op21 = operator_1.compose(operator_2, qargs=qarg2, front=True) - self.cache[node1_key, node2_key] = self.cache[node2_key, node1_key] = ret = op12 == op21 - return ret + self._cached_commutations.setdefault((first_op.name, second_op.name), {})[ + _get_relative_placement(first_qargs, second_qargs) + ] = is_commuting + self._current_cache_entries += 1 + + return is_commuting + + def num_cached_entries(self): + """Returns number of cached entries""" + return self._current_cache_entries + + def clear_cached_commutations(self): + """Clears the dictionary holding cached commutations""" + self._current_cache_entries = 0 + self._cache_miss = 0 + self._cache_hit = 0 + self._cached_commutations = {} + + def check_commutation_entries( + self, + first_op: Operation, + first_qargs: List, + second_op: Operation, + second_qargs: List, + ) -> Union[bool, None]: + """Returns stored commutation relation if any + + Args: + first_op: first operation. + first_qargs: first operation's qubits. + second_op: second operation. + second_qargs: second operation's qubits. + + Return: + bool: True if the gates commute and false if it is not the case. + """ + + # We don't precompute commutations for parameterized gates, yet + commutation = _query_commutation( + first_op, + first_qargs, + second_op, + second_qargs, + self._standard_commutations, + ) + + if commutation is not None: + return commutation + + commutation = _query_commutation( + first_op, + first_qargs, + second_op, + second_qargs, + self._cached_commutations, + ) + if commutation is None: + self._cache_miss += 1 + else: + self._cache_hit += 1 + return commutation + + +def _hashable_parameters(params): + """Convert the parameters of a gate into a hashable format for lookup in a dictionary.""" + try: + hash(params) + return params + except TypeError: + pass + if isinstance(params, (list, tuple)): + return tuple(_hashable_parameters(x) for x in params) + if isinstance(params, np.ndarray): + # Using the bytes of the matrix as key is runtime efficient but requires more space: 128 bits + # times the number of parameters instead of a single 64 bit id. However, by using the bytes as + # an id, we can reuse the cached commutations between different passes. + return (np.ndarray, params.tobytes()) + # Catch anything else with a slow conversion. + return ("fallback", str(params)) + + +_skipped_op_names = {"measure", "reset", "delay"} + + +def _commutation_precheck( + op1: Operation, + qargs1: List, + cargs1: List, + op2: Operation, + qargs2: List, + cargs2: List, + max_num_qubits, +): + # pylint: disable=too-many-return-statements + + # We don't support commutation of conditional gates for now due to bugs in + # CommutativeCancellation. See gh-8553. + if getattr(op1, "condition", None) is not None or getattr(op2, "condition", None) is not None: + return False + + # Commutation of ControlFlow gates also not supported yet. This may be + # pending a control flow graph. + if isinstance(op1, ControlFlowOp) or isinstance(op2, ControlFlowOp): + return False + + # These lines are adapted from dag_dependency and say that two gates over + # different quantum and classical bits necessarily commute. This is more + # permissive that the check from commutation_analysis, as for example it + # allows to commute X(1) and Measure(0, 0). + # Presumably this check was not present in commutation_analysis as + # it was only called on pairs of connected nodes from DagCircuit. + intersection_q = set(qargs1).intersection(set(qargs2)) + intersection_c = set(cargs1).intersection(set(cargs2)) + if not (intersection_q or intersection_c): + return True + + # Skip the check if the number of qubits for either operation is too large + if len(qargs1) > max_num_qubits or len(qargs2) > max_num_qubits: + return False + + # These lines are adapted from commutation_analysis, which is more restrictive than the + # check from dag_dependency when considering nodes with "_directive". It would be nice to + # think which optimizations from dag_dependency can indeed be used. + if op1.name in _skipped_op_names or op2.name in _skipped_op_names: + return False + + if getattr(op1, "_directive", False) or getattr(op2, "_directive", False): + return False + if (getattr(op1, "is_parameterized", False) and op1.is_parameterized()) or ( + getattr(op2, "is_parameterized", False) and op2.is_parameterized() + ): + return False + + return None + + +def _get_relative_placement(first_qargs: List[Qubit], second_qargs: List[Qubit]) -> tuple: + """Determines the relative qubit placement of two gates. Note: this is NOT symmetric. + + Args: + first_qargs (DAGOpNode): first gate + second_qargs (DAGOpNode): second gate + + Return: + A tuple that describes the relative qubit placement. The relative placement is defined by the + gate qubit arrangements as q2^{-1}[q1[i]] where q1[i] is the ith qubit of the first gate and + q2^{-1}[q] returns the qubit index of qubit q in the second gate (possibly 'None'). E.g. + _get_relative_placement(CX(0, 1), CX(1, 2)) would return (None, 0) as there is no overlap on + the first qubit of the first gate but there is an overlap on the second qubit of the first gate, + i.e. qubit 0 of the second gate. Likewise, _get_relative_placement(CX(1, 2), CX(0, 1)) would + return (1, None) + """ + qubits_g2 = {q_g1: i_g1 for i_g1, q_g1 in enumerate(second_qargs)} + return tuple(qubits_g2.get(q_g0, None) for q_g0 in first_qargs) + + +@lru_cache(maxsize=10**3) +def _persistent_id(op_name: str) -> int: + """Returns an integer id of a string that is persistent over different python executions (note that + hash() can not be used, i.e. its value can change over two python executions) + Args: + op_name (str): The string whose integer id should be determined. + Return: + The integer id of the input string. + """ + return int.from_bytes(bytes(op_name, encoding="ascii"), byteorder="big", signed=True) + + +def _order_operations( + op1: Operation, qargs1: List, cargs1: List, op2: Operation, qargs2: List, cargs2: List +): + """Orders two operations in a canonical way that is persistent over + @different python versions and executions + Args: + op1: first operation. + qargs1: first operation's qubits. + cargs1: first operation's clbits. + op2: second operation. + qargs2: second operation's qubits. + cargs2: second operation's clbits. + Return: + The input operations in a persistent, canonical order. + """ + op1_tuple = (op1, qargs1, cargs1) + op2_tuple = (op2, qargs2, cargs2) + least_qubits_op, most_qubits_op = ( + (op1_tuple, op2_tuple) if op1.num_qubits < op2.num_qubits else (op2_tuple, op1_tuple) + ) + # prefer operation with the least number of qubits as first key as this results in shorter keys + if op1.num_qubits != op2.num_qubits: + return least_qubits_op, most_qubits_op + else: + return ( + (op1_tuple, op2_tuple) + if _persistent_id(op1.name) < _persistent_id(op2.name) + else (op2_tuple, op1_tuple) + ) + + +def _query_commutation( + first_op: Operation, + first_qargs: List, + second_op: Operation, + second_qargs: List, + _commutation_lib: dict, +) -> Union[bool, None]: + """Queries and returns the commutation of a pair of operations from a provided commutation library + Args: + first_op: first operation. + first_qargs: first operation's qubits. + first_cargs: first operation's clbits. + second_op: second operation. + second_qargs: second operation's qubits. + second_cargs: second operation's clbits. + _commutation_lib (dict): dictionary of commutation relations + Return: + True if first_op and second_op commute, False if they do not commute and + None if the commutation is not in the library + """ + + commutation = _commutation_lib.get((first_op.name, second_op.name), None) + + # Return here if the commutation is constant over all relative placements of the operations + if commutation is None or isinstance(commutation, bool): + return commutation + + # If we arrive here, there is an entry in the commutation library but it depends on the + # placement of the operations and also possibly on operation parameters + if isinstance(commutation, dict): + commutation_after_placement = commutation.get( + _get_relative_placement(first_qargs, second_qargs), None + ) + # if we have another dict in commutation_after_placement, commutation depends on params + if isinstance(commutation_after_placement, dict): + # Param commutation entry exists and must be a dict + return commutation_after_placement.get( + (_hashable_parameters(first_op.params), _hashable_parameters(second_op.params)), + None, + ) + else: + # queried commutation is True, False or None + return commutation_after_placement + else: + raise ValueError("Expected commutation to be None, bool or a dict") + + +def _commute_matmul( + first_ops: Operation, first_qargs: List, second_op: Operation, second_qargs: List +): + qarg = {q: i for i, q in enumerate(first_qargs)} + num_qubits = len(qarg) + for q in second_qargs: + if q not in qarg: + qarg[q] = num_qubits + num_qubits += 1 + + first_qarg = tuple(qarg[q] for q in first_qargs) + second_qarg = tuple(qarg[q] for q in second_qargs) + + operator_1 = Operator( + first_ops, input_dims=(2,) * len(first_qarg), output_dims=(2,) * len(first_qarg) + ) + operator_2 = Operator( + second_op, input_dims=(2,) * len(second_qarg), output_dims=(2,) * len(second_qarg) + ) + + if first_qarg == second_qarg: + # Use full composition if possible to get the fastest matmul paths. + op12 = operator_1.compose(operator_2) + op21 = operator_2.compose(operator_1) + else: + # Expand operator_1 to be large enough to contain operator_2 as well; this relies on qargs1 + # being the lowest possible indices so the identity can be tensored before it. + extra_qarg2 = num_qubits - len(first_qarg) + if extra_qarg2: + id_op = _identity_op(extra_qarg2) + operator_1 = id_op.tensor(operator_1) + op12 = operator_1.compose(operator_2, qargs=second_qarg, front=False) + op21 = operator_1.compose(operator_2, qargs=second_qarg, front=True) + ret = op12 == op21 + return ret diff --git a/qiskit/circuit/commutation_library.py b/qiskit/circuit/commutation_library.py new file mode 100644 index 0000000000..0af9bd3ed6 --- /dev/null +++ b/qiskit/circuit/commutation_library.py @@ -0,0 +1,20 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Provides a commutation checker that caches the determined commutation results during this session """ + +from qiskit.circuit import CommutationChecker + +from qiskit.circuit._standard_gates_commutations import standard_gates_commutations + +StandardGateCommutations = standard_gates_commutations +SessionCommutationChecker = CommutationChecker(StandardGateCommutations) diff --git a/qiskit/dagcircuit/dagdependency.py b/qiskit/dagcircuit/dagdependency.py index a1e24b4e0b..a519f0b53f 100644 --- a/qiskit/dagcircuit/dagdependency.py +++ b/qiskit/dagcircuit/dagdependency.py @@ -19,12 +19,12 @@ from collections import OrderedDict, defaultdict import rustworkx as rx +from qiskit.circuit.commutation_library import SessionCommutationChecker as scc from qiskit.circuit.controlflow import condition_resources from qiskit.circuit.quantumregister import QuantumRegister, Qubit from qiskit.circuit.classicalregister import ClassicalRegister, Clbit from qiskit.dagcircuit.exceptions import DAGDependencyError from qiskit.dagcircuit.dagdepnode import DAGDepNode -from qiskit.circuit.commutation_checker import CommutationChecker # ToDo: DagDependency needs to be refactored: @@ -112,7 +112,7 @@ class DAGDependency: self.duration = None self.unit = "dt" - self.comm_checker = CommutationChecker() + self.comm_checker = scc @property def global_phase(self): diff --git a/qiskit/transpiler/passes/optimization/commutation_analysis.py b/qiskit/transpiler/passes/optimization/commutation_analysis.py index 8c34c911a6..f729893244 100644 --- a/qiskit/transpiler/passes/optimization/commutation_analysis.py +++ b/qiskit/transpiler/passes/optimization/commutation_analysis.py @@ -14,9 +14,9 @@ from collections import defaultdict +from qiskit.circuit.commutation_library import SessionCommutationChecker as scc from qiskit.dagcircuit import DAGOpNode from qiskit.transpiler.basepasses import AnalysisPass -from qiskit.circuit.commutation_checker import CommutationChecker class CommutationAnalysis(AnalysisPass): @@ -29,7 +29,7 @@ class CommutationAnalysis(AnalysisPass): def __init__(self): super().__init__() - self.comm_checker = CommutationChecker() + self.comm_checker = scc def run(self, dag): """Run the CommutationAnalysis pass on `dag`. diff --git a/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py b/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py index 5ebd3eb8e6..5f9744fc86 100644 --- a/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py +++ b/qiskit/transpiler/passes/optimization/commutative_inverse_cancellation.py @@ -11,13 +11,11 @@ # that they have been altered from the originals. """Cancel pairs of inverse gates exploiting commutation relations.""" - - +from qiskit.circuit.commutation_library import SessionCommutationChecker as scc from qiskit.dagcircuit import DAGCircuit, DAGOpNode from qiskit.quantum_info import Operator from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.transpiler.basepasses import TransformationPass -from qiskit.circuit.commutation_checker import CommutationChecker class CommutativeInverseCancellation(TransformationPass): @@ -94,8 +92,8 @@ class CommutativeInverseCancellation(TransformationPass): removed = [False for _ in range(circ_size)] + cc = scc phase_update = 0 - cc = CommutationChecker() for idx1 in range(0, circ_size): if self._skip_node(topo_sorted_nodes[idx1]): diff --git a/releasenotes/notes/add-commutation-library-88b7ff65b3d35f9a.yaml b/releasenotes/notes/add-commutation-library-88b7ff65b3d35f9a.yaml new file mode 100644 index 0000000000..38df7cb2be --- /dev/null +++ b/releasenotes/notes/add-commutation-library-88b7ff65b3d35f9a.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + Adds a commutation library to the :class:`.CommutationChecker`. The commutation library stores all + commutation relations of unparameterizable standard gates into a dictionary that allows for efficient + lookup at runtime. Furthermore, the :class:`.CommutationChecker` was refactored and an upper limit was + set to the number of cached commutation relations that are not in the commutation library. A session + commutation checker was added, that can be used to cache commutations computed during one qiskit + execution. Addresses `#8020 _` and + `#7101 _` + - | + Adds a `SessionCommutationChecker`, i.e. a commutation checker with commutations that are cached + consistently during the runtime of a python execution. diff --git a/test/python/circuit/test_commutation_checker.py b/test/python/circuit/test_commutation_checker.py index ac212bee40..4a3e6e3b02 100644 --- a/test/python/circuit/test_commutation_checker.py +++ b/test/python/circuit/test_commutation_checker.py @@ -18,7 +18,8 @@ import numpy as np from qiskit import ClassicalRegister from qiskit.circuit import QuantumRegister, Parameter, Qubit -from qiskit.circuit import CommutationChecker +from qiskit.circuit.commutation_library import SessionCommutationChecker as scc + from qiskit.circuit.library import ( ZGate, XGate, @@ -41,42 +42,40 @@ class TestCommutationChecker(QiskitTestCase): """Check simple commutation relations between gates, experimenting with different orders of gates, different orders of qubits, different sets of qubits over which gates are defined, and so on.""" - comm_checker = CommutationChecker() - # should commute - res = comm_checker.commute(ZGate(), [0], [], CXGate(), [0, 1], []) + res = scc.commute(ZGate(), [0], [], CXGate(), [0, 1], []) self.assertTrue(res) # should not commute - res = comm_checker.commute(ZGate(), [1], [], CXGate(), [0, 1], []) + res = scc.commute(ZGate(), [1], [], CXGate(), [0, 1], []) self.assertFalse(res) # should not commute - res = comm_checker.commute(XGate(), [0], [], CXGate(), [0, 1], []) + res = scc.commute(XGate(), [0], [], CXGate(), [0, 1], []) self.assertFalse(res) # should commute - res = comm_checker.commute(XGate(), [1], [], CXGate(), [0, 1], []) + res = scc.commute(XGate(), [1], [], CXGate(), [0, 1], []) self.assertTrue(res) # should not commute - res = comm_checker.commute(XGate(), [1], [], CXGate(), [1, 0], []) + res = scc.commute(XGate(), [1], [], CXGate(), [1, 0], []) self.assertFalse(res) # should commute - res = comm_checker.commute(XGate(), [0], [], CXGate(), [1, 0], []) + res = scc.commute(XGate(), [0], [], CXGate(), [1, 0], []) self.assertTrue(res) # should commute - res = comm_checker.commute(CXGate(), [1, 0], [], XGate(), [0], []) + res = scc.commute(CXGate(), [1, 0], [], XGate(), [0], []) self.assertTrue(res) # should not commute - res = comm_checker.commute(CXGate(), [1, 0], [], XGate(), [1], []) + res = scc.commute(CXGate(), [1, 0], [], XGate(), [1], []) self.assertFalse(res) # should commute - res = comm_checker.commute( + res = scc.commute( CXGate(), [1, 0], [], @@ -87,7 +86,7 @@ class TestCommutationChecker(QiskitTestCase): self.assertTrue(res) # should not commute - res = comm_checker.commute( + res = scc.commute( CXGate(), [1, 0], [], @@ -98,7 +97,7 @@ class TestCommutationChecker(QiskitTestCase): self.assertFalse(res) # should commute - res = comm_checker.commute( + res = scc.commute( CXGate(), [1, 0], [], @@ -109,7 +108,7 @@ class TestCommutationChecker(QiskitTestCase): self.assertTrue(res) # should not commute - res = comm_checker.commute( + res = scc.commute( CXGate(), [1, 0], [], @@ -120,7 +119,7 @@ class TestCommutationChecker(QiskitTestCase): self.assertFalse(res) # should commute - res = comm_checker.commute( + res = scc.commute( CXGate(), [1, 0], [], @@ -130,74 +129,123 @@ class TestCommutationChecker(QiskitTestCase): ) self.assertTrue(res) - res = comm_checker.commute(XGate(), [2], [], CCXGate(), [0, 1, 2], []) + res = scc.commute(XGate(), [2], [], CCXGate(), [0, 1, 2], []) self.assertTrue(res) - res = comm_checker.commute(CCXGate(), [0, 1, 2], [], CCXGate(), [0, 2, 1], []) + res = scc.commute(CCXGate(), [0, 1, 2], [], CCXGate(), [0, 2, 1], []) self.assertFalse(res) def test_passing_quantum_registers(self): """Check that passing QuantumRegisters works correctly.""" - comm_checker = CommutationChecker() - qr = QuantumRegister(4) # should commute - res = comm_checker.commute(CXGate(), [qr[1], qr[0]], [], CXGate(), [qr[1], qr[2]], []) + res = scc.commute(CXGate(), [qr[1], qr[0]], [], CXGate(), [qr[1], qr[2]], []) self.assertTrue(res) # should not commute - res = comm_checker.commute(CXGate(), [qr[0], qr[1]], [], CXGate(), [qr[1], qr[2]], []) + res = scc.commute(CXGate(), [qr[0], qr[1]], [], CXGate(), [qr[1], qr[2]], []) self.assertFalse(res) + def test_standard_gates_commutations(self): + """Check that commutativity checker uses standard gates commutations as expected.""" + scc.clear_cached_commutations() + scc.clear_cached_commutations() + res = scc.commute(ZGate(), [0], [], CXGate(), [0, 1], []) + self.assertTrue(res) + self.assertEqual(scc.num_cached_entries(), 0) + def test_caching_positive_results(self): """Check that hashing positive results in commutativity checker works as expected.""" + scc.clear_cached_commutations() + NewGateCX = type("MyClass", (CXGate,), {"content": {}}) + NewGateCX.name = "cx_new" - comm_checker = CommutationChecker() - res = comm_checker.commute(ZGate(), [0], [], CXGate(), [0, 1], []) + res = scc.commute(ZGate(), [0], [], NewGateCX(), [0, 1], []) self.assertTrue(res) - self.assertGreater(len(comm_checker.cache), 0) + self.assertGreater(len(scc._cached_commutations), 0) + + def test_caching_lookup_with_non_overlapping_qubits(self): + """Check that commutation lookup with non-overlapping qubits works as expected.""" + scc.clear_cached_commutations() + res = scc.commute(CXGate(), [0, 2], [], CXGate(), [0, 1], []) + self.assertTrue(res) + res = scc.commute(CXGate(), [0, 1], [], CXGate(), [1, 2], []) + self.assertFalse(res) + self.assertEqual(len(scc._cached_commutations), 0) + + def test_caching_store_and_lookup_with_non_overlapping_qubits(self): + """Check that commutations storing and lookup with non-overlapping qubits works as expected.""" + cc_lenm = scc.num_cached_entries() + NewGateCX = type("MyClass", (CXGate,), {"content": {}}) + NewGateCX.name = "cx_new" + res = scc.commute(NewGateCX(), [0, 2], [], CXGate(), [0, 1], []) + self.assertTrue(res) + res = scc.commute(NewGateCX(), [0, 1], [], CXGate(), [1, 2], []) + self.assertFalse(res) + res = scc.commute(NewGateCX(), [1, 4], [], CXGate(), [1, 6], []) + self.assertTrue(res) + res = scc.commute(NewGateCX(), [5, 3], [], CXGate(), [3, 1], []) + self.assertFalse(res) + self.assertEqual(scc.num_cached_entries(), cc_lenm + 2) def test_caching_negative_results(self): """Check that hashing negative results in commutativity checker works as expected.""" + scc.clear_cached_commutations() + NewGateCX = type("MyClass", (CXGate,), {"content": {}}) + NewGateCX.name = "cx_new" - comm_checker = CommutationChecker() - res = comm_checker.commute(XGate(), [0], [], CXGate(), [0, 1], []) + res = scc.commute(XGate(), [0], [], NewGateCX(), [0, 1], []) self.assertFalse(res) - self.assertGreater(len(comm_checker.cache), 0) + self.assertGreater(len(scc._cached_commutations), 0) def test_caching_different_qubit_sets(self): """Check that hashing same commutativity results over different qubit sets works as expected.""" - - comm_checker = CommutationChecker() - + scc.clear_cached_commutations() + NewGateCX = type("MyClass", (CXGate,), {"content": {}}) + NewGateCX.name = "cx_new" # All the following should be cached in the same way # though each relation gets cached twice: (A, B) and (B, A) - comm_checker.commute(XGate(), [0], [], CXGate(), [0, 1], []) - comm_checker.commute(XGate(), [10], [], CXGate(), [10, 20], []) - comm_checker.commute(XGate(), [10], [], CXGate(), [10, 5], []) - comm_checker.commute(XGate(), [5], [], CXGate(), [5, 7], []) - self.assertEqual(len(comm_checker.cache), 2) + scc.commute(XGate(), [0], [], NewGateCX(), [0, 1], []) + scc.commute(XGate(), [10], [], NewGateCX(), [10, 20], []) + scc.commute(XGate(), [10], [], NewGateCX(), [10, 5], []) + scc.commute(XGate(), [5], [], NewGateCX(), [5, 7], []) + self.assertEqual(len(scc._cached_commutations), 1) + self.assertEqual(scc._cache_miss, 1) + self.assertEqual(scc._cache_hit, 3) + + def test_cache_with_param_gates(self): + """Check commutativity between (non-parameterized) gates with parameters.""" + scc.clear_cached_commutations() + res = scc.commute(RZGate(0), [0], [], XGate(), [0], []) + self.assertTrue(res) + + res = scc.commute(RZGate(np.pi / 2), [0], [], XGate(), [0], []) + self.assertFalse(res) + + res = scc.commute(RZGate(np.pi / 2), [0], [], RZGate(0), [0], []) + self.assertTrue(res) + + res = scc.commute(RZGate(np.pi / 2), [1], [], XGate(), [1], []) + self.assertFalse(res) + self.assertEqual(scc.num_cached_entries(), 3) + self.assertEqual(scc._cache_miss, 3) + self.assertEqual(scc._cache_hit, 1) def test_gates_with_parameters(self): """Check commutativity between (non-parameterized) gates with parameters.""" - - comm_checker = CommutationChecker() - res = comm_checker.commute(RZGate(0), [0], [], XGate(), [0], []) + res = scc.commute(RZGate(0), [0], [], XGate(), [0], []) self.assertTrue(res) - res = comm_checker.commute(RZGate(np.pi / 2), [0], [], XGate(), [0], []) + res = scc.commute(RZGate(np.pi / 2), [0], [], XGate(), [0], []) self.assertFalse(res) - res = comm_checker.commute(RZGate(np.pi / 2), [0], [], RZGate(0), [0], []) + res = scc.commute(RZGate(np.pi / 2), [0], [], RZGate(0), [0], []) self.assertTrue(res) def test_parameterized_gates(self): """Check commutativity between parameterized gates, both with free and with bound parameters.""" - - comm_checker = CommutationChecker() - # gate that has parameters but is not considered parameterized rz_gate = RZGate(np.pi / 2) self.assertEqual(len(rz_gate.params), 1) @@ -215,23 +263,23 @@ class TestCommutationChecker(QiskitTestCase): self.assertFalse(cx_gate.is_parameterized()) # We should detect that these gates commute - res = comm_checker.commute(rz_gate, [0], [], cx_gate, [0, 1], []) + res = scc.commute(rz_gate, [0], [], cx_gate, [0, 1], []) self.assertTrue(res) # We should detect that these gates commute - res = comm_checker.commute(rz_gate, [0], [], rz_gate, [0], []) + res = scc.commute(rz_gate, [0], [], rz_gate, [0], []) self.assertTrue(res) # We should detect that parameterized gates over disjoint qubit subsets commute - res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate_theta, [1], []) + res = scc.commute(rz_gate_theta, [0], [], rz_gate_theta, [1], []) self.assertTrue(res) # We should detect that parameterized gates over disjoint qubit subsets commute - res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate_phi, [1], []) + res = scc.commute(rz_gate_theta, [0], [], rz_gate_phi, [1], []) self.assertTrue(res) # We should detect that parameterized gates over disjoint qubit subsets commute - res = comm_checker.commute(rz_gate_theta, [2], [], cx_gate, [1, 3], []) + res = scc.commute(rz_gate_theta, [2], [], cx_gate, [1, 3], []) self.assertTrue(res) # However, for now commutativity checker should return False when checking @@ -239,114 +287,93 @@ class TestCommutationChecker(QiskitTestCase): # the two gates are over intersecting qubit subsets. # This check should be changed if commutativity checker is extended to # handle parameterized gates better. - res = comm_checker.commute(rz_gate_theta, [0], [], cx_gate, [0, 1], []) + res = scc.commute(rz_gate_theta, [0], [], cx_gate, [0, 1], []) self.assertFalse(res) - res = comm_checker.commute(rz_gate_theta, [0], [], rz_gate, [0], []) + res = scc.commute(rz_gate_theta, [0], [], rz_gate, [0], []) self.assertFalse(res) def test_measure(self): """Check commutativity involving measures.""" - - comm_checker = CommutationChecker() - # Measure is over qubit 0, while gate is over a disjoint subset of qubits # We should be able to swap these. - res = comm_checker.commute(Measure(), [0], [0], CXGate(), [1, 2], []) + res = scc.commute(Measure(), [0], [0], CXGate(), [1, 2], []) self.assertTrue(res) # Measure and gate have intersecting set of qubits # We should not be able to swap these. - res = comm_checker.commute(Measure(), [0], [0], CXGate(), [0, 2], []) + res = scc.commute(Measure(), [0], [0], CXGate(), [0, 2], []) self.assertFalse(res) # Measures over different qubits and clbits - res = comm_checker.commute(Measure(), [0], [0], Measure(), [1], [1]) + res = scc.commute(Measure(), [0], [0], Measure(), [1], [1]) self.assertTrue(res) # Measures over different qubits but same classical bit # We should not be able to swap these. - res = comm_checker.commute(Measure(), [0], [0], Measure(), [1], [0]) + res = scc.commute(Measure(), [0], [0], Measure(), [1], [0]) self.assertFalse(res) # Measures over same qubits but different classical bit # ToDo: can we swap these? # Currently checker takes the safe approach and returns False. - res = comm_checker.commute(Measure(), [0], [0], Measure(), [0], [1]) + res = scc.commute(Measure(), [0], [0], Measure(), [0], [1]) self.assertFalse(res) def test_barrier(self): """Check commutativity involving barriers.""" - - comm_checker = CommutationChecker() - # A gate should not commute with a barrier # (at least if these are over intersecting qubit sets). - res = comm_checker.commute(Barrier(4), [0, 1, 2, 3], [], CXGate(), [1, 2], []) + res = scc.commute(Barrier(4), [0, 1, 2, 3], [], CXGate(), [1, 2], []) self.assertFalse(res) # Does it even make sense to have a barrier over a subset of qubits? # Though in this case, it probably makes sense to say that barrier and gate can be swapped. - res = comm_checker.commute(Barrier(4), [0, 1, 2, 3], [], CXGate(), [5, 6], []) + res = scc.commute(Barrier(4), [0, 1, 2, 3], [], CXGate(), [5, 6], []) self.assertTrue(res) def test_reset(self): """Check commutativity involving resets.""" - - comm_checker = CommutationChecker() - # A gate should not commute with reset when the qubits intersect. - res = comm_checker.commute(Reset(), [0], [], CXGate(), [0, 2], []) + res = scc.commute(Reset(), [0], [], CXGate(), [0, 2], []) self.assertFalse(res) # A gate should commute with reset when the qubits are disjoint. - res = comm_checker.commute(Reset(), [0], [], CXGate(), [1, 2], []) + res = scc.commute(Reset(), [0], [], CXGate(), [1, 2], []) self.assertTrue(res) def test_conditional_gates(self): """Check commutativity involving conditional gates.""" - - comm_checker = CommutationChecker() - qr = QuantumRegister(3) cr = ClassicalRegister(2) # Currently, in all cases commutativity checker should returns False. # This is definitely suboptimal. - res = comm_checker.commute( - CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[2]], [] - ) + res = scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[2]], []) self.assertFalse(res) - res = comm_checker.commute( - CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[1]], [] - ) + res = scc.commute(CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], XGate(), [qr[1]], []) self.assertFalse(res) - res = comm_checker.commute( + res = scc.commute( CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [], CXGate().c_if(cr[0], 0), [qr[0], qr[1]], [] ) self.assertFalse(res) - res = comm_checker.commute( - XGate().c_if(cr[0], 0), [qr[0]], [], XGate().c_if(cr[0], 1), [qr[0]], [] - ) + res = scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate().c_if(cr[0], 1), [qr[0]], []) self.assertFalse(res) - res = comm_checker.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate(), [qr[0]], []) + res = scc.commute(XGate().c_if(cr[0], 0), [qr[0]], [], XGate(), [qr[0]], []) self.assertFalse(res) def test_complex_gates(self): """Check commutativity involving more complex gates.""" - - comm_checker = CommutationChecker() - lf1 = LinearFunction([[0, 1, 0], [1, 0, 0], [0, 0, 1]]) lf2 = LinearFunction([[1, 0, 0], [0, 0, 1], [0, 1, 0]]) # lf1 is equivalent to swap(0, 1), and lf2 to swap(1, 2). # These do not commute. - res = comm_checker.commute(lf1, [0, 1, 2], [], lf2, [0, 1, 2], []) + res = scc.commute(lf1, [0, 1, 2], [], lf2, [0, 1, 2], []) self.assertFalse(res) lf3 = LinearFunction([[0, 1, 0], [0, 0, 1], [1, 0, 0]]) @@ -354,27 +381,27 @@ class TestCommutationChecker(QiskitTestCase): # lf3 is permutation 1->2, 2->3, 3->1. # lf3 is the inverse permutation 1->3, 2->1, 3->2. # These commute. - res = comm_checker.commute(lf3, [0, 1, 2], [], lf4, [0, 1, 2], []) + res = scc.commute(lf3, [0, 1, 2], [], lf4, [0, 1, 2], []) self.assertTrue(res) def test_c7x_gate(self): """Test wide gate works correctly.""" qargs = [Qubit() for _ in [None] * 8] - res = CommutationChecker().commute(XGate(), qargs[:1], [], XGate().control(7), qargs, []) + res = scc.commute(XGate(), qargs[:1], [], XGate().control(7), qargs, []) self.assertFalse(res) def test_wide_gates_over_nondisjoint_qubits(self): """Test that checking wide gates does not lead to memory problems.""" - res = CommutationChecker().commute(MCXGate(29), list(range(30)), [], XGate(), [0], []) + res = scc.commute(MCXGate(29), list(range(30)), [], XGate(), [0], []) self.assertFalse(res) - res = CommutationChecker().commute(XGate(), [0], [], MCXGate(29), list(range(30)), []) + res = scc.commute(XGate(), [0], [], MCXGate(29), list(range(30)), []) self.assertFalse(res) def test_wide_gates_over_disjoint_qubits(self): """Test that wide gates still commute when they are over disjoint sets of qubits.""" - res = CommutationChecker().commute(MCXGate(29), list(range(30)), [], XGate(), [30], []) + res = scc.commute(MCXGate(29), list(range(30)), [], XGate(), [30], []) self.assertTrue(res) - res = CommutationChecker().commute(XGate(), [30], [], MCXGate(29), list(range(30)), []) + res = scc.commute(XGate(), [30], [], MCXGate(29), list(range(30)), []) self.assertTrue(res) diff --git a/test/python/transpiler/test_template_matching.py b/test/python/transpiler/test_template_matching.py index 23d5601719..1e4da01cb4 100644 --- a/test/python/transpiler/test_template_matching.py +++ b/test/python/transpiler/test_template_matching.py @@ -16,6 +16,7 @@ import unittest import numpy as np +from qiskit.circuit.commutation_library import SessionCommutationChecker as scc from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit import Parameter from qiskit.quantum_info import Operator @@ -779,6 +780,7 @@ class TestTemplateMatching(QiskitTestCase): clifford_3_1(), ] pm = PassManager(TemplateOptimization(template_list=template_list)) + scc.clear_cached_commutations() for seed in range(10): qc = random_clifford_circuit( num_qubits=5, @@ -788,6 +790,8 @@ class TestTemplateMatching(QiskitTestCase): ) qc_opt = pm.run(qc) self.assertTrue(Operator(qc) == Operator(qc_opt)) + # All of these gates are in the commutation library, i.e. the cache should not be used + self.assertEqual(scc.num_cached_entries(), 0) if __name__ == "__main__": diff --git a/tools/build_standard_commutations.py b/tools/build_standard_commutations.py new file mode 100644 index 0000000000..72798f0eb4 --- /dev/null +++ b/tools/build_standard_commutations.py @@ -0,0 +1,164 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Determines a commutation library over the unparameterizable standard gates, i.e. a dictionary for + each pair of parameterizable standard gates and all qubit overlaps that maps to either True or False, + depending on the present commutation relation. +""" + + +import itertools +from functools import lru_cache +from typing import List +from qiskit.circuit.commutation_checker import _get_relative_placement, _order_operations +from qiskit.circuit import Gate, CommutationChecker +import qiskit.circuit.library.standard_gates as stdg +from qiskit.dagcircuit import DAGOpNode + + +@lru_cache(None) +def _get_unparameterizable_gates() -> List[Gate]: + """Retrieve a list of non-parmaterized gates with up to 3 qubits, using the python inspection module + Return: + A list of non-parameterized gates to be considered in the commutation library + """ + # These two gates may require a large runtime in later processing steps + # blocked_types = [C3SXGate, C4XGate] + gates = list(stdg.get_standard_gate_name_mapping().values()) + return [g for g in gates if len(g.params) == 0] + + +def _generate_commutation_dict(considered_gates: List[Gate] = None) -> dict: + """Compute the commutation relation of considered gates + + Args: + considered_gates List[Gate]: a list of gates between which the commutation should be determined + + Return: + A dictionary that includes the commutation relation for each + considered pair of operations and each relative placement + + """ + commutations = {} + cc = CommutationChecker() + for gate0 in considered_gates: + + node0 = DAGOpNode(op=gate0, qargs=list(range(gate0.num_qubits)), cargs=[]) + for gate1 in considered_gates: + + # only consider canonical entries + ( + ( + first_gate, + _, + _, + ), + (second_gate, _, _), + ) = _order_operations(gate0, None, None, gate1, None, None) + if (first_gate, second_gate) != (gate0, gate1) and gate0.name != gate1.name: + continue + + # enumerate all relative gate placements with overlap between gate qubits + gate_placements = itertools.permutations( + range(gate0.num_qubits + gate1.num_qubits - 1), gate0.num_qubits + ) + gate_pair_commutation = {} + for permutation in gate_placements: + permutation_list = list(permutation) + gate1_qargs = [] + + # use idx_non_overlapping qubits to represent qubits on g1 that are not connected to g0 + next_non_overlapping_qubit_idx = gate0.num_qubits + for i in range(gate1.num_qubits): + if i in permutation_list: + gate1_qargs.append(permutation_list.index(i)) + else: + gate1_qargs.append(next_non_overlapping_qubit_idx) + next_non_overlapping_qubit_idx += 1 + + node1 = DAGOpNode(op=gate1, qargs=gate1_qargs, cargs=[]) + + # replace non-overlapping qubits with None to act as a key in the commutation library + relative_placement = _get_relative_placement(node0.qargs, node1.qargs) + + if not gate0.is_parameterized() and not gate1.is_parameterized(): + # if no gate includes parameters, compute commutation relation using + # matrix multiplication + op1 = node0.op + qargs1 = node0.qargs + cargs1 = node0.cargs + op2 = node1.op + qargs2 = node1.qargs + cargs2 = node1.cargs + commutation_relation = cc.commute( + op1, qargs1, cargs1, op2, qargs2, cargs2, max_num_qubits=4 + ) + else: + pass + # TODO + + gate_pair_commutation[relative_placement] = commutation_relation + + commutations[gate0.name, gate1.name] = gate_pair_commutation + return commutations + + +def _simplify_commuting_dict(commuting_dict: dict) -> dict: + """Compress some of the commutation library entries + + Args: + commuting_dict (dict): A commutation dictionary + Return: + commuting_dict (dict): A commutation dictionary with simplified entries + """ + # Remove relative placement key if commutation is independent of relative placement + for ops in commuting_dict.keys(): + gates_commutations = set(commuting_dict[ops].values()) + if len(gates_commutations) == 1: + commuting_dict[ops] = next(iter(gates_commutations)) + + return commuting_dict + + +def _dump_commuting_dict_as_python( + commutations: dict, file_name: str = "../_standard_gates_commutations.py" +): + """Write commutation dictionary as python object to ./qiskit/circuit/_standard_gates_commutations.py. + + Args: + commutations (dict): a dictionary that includes the commutation relation for + each considered pair of operations + + """ + with open(file_name, "w") as fp: + dir_str = "standard_gates_commutations = {\n" + for k, v in commutations.items(): + if not isinstance(v, dict): + dir_str += ' ("{}", "{}"): {},\n'.format(*k, v) + else: + dir_str += ' ("{}", "{}"): {{\n'.format(*k) + + for entry_key, entry_val in v.items(): + dir_str += " {}: {},\n".format(entry_key, entry_val) + + dir_str += " },\n" + dir_str += "}\n" + fp.write(dir_str.replace("'", "")) + + +if __name__ == "__main__": + cgates = [ + g for g in _get_unparameterizable_gates() if g.name not in ["reset", "measure", "delay"] + ] + commutation_dict = _generate_commutation_dict(considered_gates=cgates) + commutation_dict = _simplify_commuting_dict(commutation_dict) + _dump_commuting_dict_as_python(commutation_dict)