# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
#
# 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.
"""
Test circuits and reference outputs for non-Clifford gate instructions.
"""

import numpy as np
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit

# ==========================================================================
# T-gate
# ==========================================================================


def t_gate_circuits_deterministic(final_measure=True):
    """T-gate test circuits with deterministic counts."""
    circuits = []
    qr = QuantumRegister(1)
    if final_measure:
        cr = ClassicalRegister(1)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # T
    circuit = QuantumCircuit(*regs)
    circuit.t(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # T.X
    circuit = QuantumCircuit(*regs)
    circuit.x(qr)
    circuit.barrier(qr)
    circuit.t(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def t_gate_counts_deterministic(shots, hex_counts=True):
    """T-gate circuits reference counts."""
    targets = []
    if hex_counts:
        # T
        targets.append({"0x0": shots})
        # T.X
        targets.append({"0x1": shots})
    else:
        # T
        targets.append({"0": shots})
        # T.X
        targets.append({"1": shots})
    return targets


def t_gate_statevector_deterministic():
    """T-gate circuits reference statevectors."""
    targets = []
    # T
    targets.append(np.array([1, 0]))
    # T.X
    targets.append(np.array([0, 1 + 1j]) / np.sqrt(2))
    return targets


def t_gate_unitary_deterministic():
    """T-gate circuits reference unitaries."""
    targets = []
    # T
    targets.append(np.diag([1, (1 + 1j) / np.sqrt(2)]))
    # T.X
    targets.append(np.array([[0, 1], [(1 + 1j) / np.sqrt(2), 0]]))
    return targets


def t_gate_circuits_nondeterministic(final_measure=True):
    """T-gate test circuits with non-deterministic counts."""
    circuits = []
    qr = QuantumRegister(1)
    if final_measure:
        cr = ClassicalRegister(1)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # T.H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr)
    circuit.barrier(qr)
    circuit.t(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # X.T.H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr)
    circuit.barrier(qr)
    circuit.t(qr)
    circuit.barrier(qr)
    circuit.x(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H.T.T.H = H.S.H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr)
    circuit.barrier(qr)
    circuit.t(qr)
    circuit.barrier(qr)
    circuit.t(qr)
    circuit.barrier(qr)
    circuit.h(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)
    return circuits


def t_gate_counts_nondeterministic(shots, hex_counts=True):
    """T-gate circuits reference counts."""
    targets = []
    if hex_counts:
        # T.H
        targets.append({"0x0": shots / 2, "0x1": shots / 2})
        # X.T.H
        targets.append({"0x0": shots / 2, "0x1": shots / 2})
        # H.T.T.H = H.S.H
        targets.append({"0x0": shots / 2, "0x1": shots / 2})
    else:
        # T.H
        targets.append({"0": shots / 2, "1": shots / 2})
        # X.T.H
        targets.append({"0": shots / 2, "1": shots / 2})
        # H.T.T.H = H.S.H
        targets.append({"0": shots / 2, "1": shots / 2})
    return targets


def t_gate_statevector_nondeterministic():
    """T-gate circuits reference statevectors."""
    targets = []
    # T.H
    targets.append(np.array([1 / np.sqrt(2), 0.5 + 0.5j]))
    # X.T.H
    targets.append(np.array([0.5 + 0.5j, 1 / np.sqrt(2)]))
    # H.T.T.H = H.S.H
    targets.append(np.array([1 + 1j, 1 - 1j]) / 2)
    return targets


def t_gate_unitary_nondeterministic():
    """T-gate circuits reference unitaries."""
    targets = []
    # T.H
    targets.append(np.array([[1 / np.sqrt(2), 1 / np.sqrt(2)], [0.5 + 0.5j, -0.5 - 0.5j]]))
    # X.T.H
    targets.append(np.array([[0.5 + 0.5j, -0.5 - 0.5j], [1 / np.sqrt(2), 1 / np.sqrt(2)]]))
    # H.T.T.H = H.S.H
    targets.append(np.array([[1 + 1j, 1 - 1j], [1 - 1j, 1 + 1j]]) / 2)
    return targets


# ==========================================================================
# T^dagger-gate
# ==========================================================================


def tdg_gate_circuits_deterministic(final_measure=True):
    """Tdg-gate test circuits with deterministic counts."""
    circuits = []
    qr = QuantumRegister(1)
    if final_measure:
        cr = ClassicalRegister(1)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # Tdg
    circuit = QuantumCircuit(*regs)
    circuit.tdg(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H.Tdg.T.H = I
    circuit = QuantumCircuit(*regs)
    circuit.h(qr)
    circuit.barrier(qr)
    circuit.t(qr)
    circuit.barrier(qr)
    circuit.tdg(qr)
    circuit.barrier(qr)
    circuit.h(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def tdg_gate_counts_deterministic(shots, hex_counts=True):
    """Sdg-gate circuits reference counts."""
    targets = []
    if hex_counts:
        # Tdg
        targets.append({"0x0": shots})
        # H.Tdg.T.H = I
        targets.append({"0x0": shots})
    else:
        # Tdg
        targets.append({"0": shots})
        # H.Tdg.T.H = I
        targets.append({"0": shots})
    return targets


def tdg_gate_statevector_deterministic():
    """Sdg-gate circuits reference statevectors."""
    targets = []
    # Tdg
    targets.append(np.array([1, 0]))
    # H.Tdg.T.H = I
    targets.append(np.array([1, 0]))
    return targets


def tdg_gate_unitary_deterministic():
    """Tdg-gate circuits reference unitaries."""
    targets = []
    # Tdg
    targets.append(np.diag([1, (1 - 1j) / np.sqrt(2)]))
    # H.Tdg.T.H = I
    targets.append(np.eye(2))
    return targets


def tdg_gate_circuits_nondeterministic(final_measure=True):
    """Tdg-gate test circuits with non-deterministic counts."""
    circuits = []
    qr = QuantumRegister(1)
    if final_measure:
        cr = ClassicalRegister(1)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # Tdg.H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr)
    circuit.barrier(qr)
    circuit.tdg(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # X.Tdg.H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr)
    circuit.barrier(qr)
    circuit.tdg(qr)
    circuit.barrier(qr)
    circuit.x(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H.Tdg.Tdg.H = H.Sdg.H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr)
    circuit.barrier(qr)
    circuit.tdg(qr)
    circuit.barrier(qr)
    circuit.tdg(qr)
    circuit.barrier(qr)
    circuit.h(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def tdg_gate_counts_nondeterministic(shots, hex_counts=True):
    """Tdg-gate circuits reference counts."""
    targets = []
    if hex_counts:
        # Tdg.H
        targets.append({"0x0": shots / 2, "0x1": shots / 2})
        # X.Tdg.H
        targets.append({"0x0": shots / 2, "0x1": shots / 2})
        # H.Tdg.Tdg.H = H.Sdg.H
        targets.append({"0x0": shots / 2, "0x1": shots / 2})
    else:
        # Tdg.H
        targets.append({"0": shots / 2, "1": shots / 2})
        # X.Tdg.H
        targets.append({"0": shots / 2, "1": shots / 2})
        # H.Tdg.Tdg.H = H.Sdg.H
        targets.append({"0": shots / 2, "1": shots / 2})
    return targets


def tdg_gate_statevector_nondeterministic():
    """Tdg-gate circuits reference statevectors."""
    targets = []
    # Tdg.H
    targets.append(np.array([1 / np.sqrt(2), 0.5 - 0.5j]))
    # X.Tdg.H
    targets.append(np.array([0.5 - 0.5j, 1 / np.sqrt(2)]))
    # H.Tdg.Tdg.H = H.Sdg.H
    targets.append(np.array([1 - 1j, 1 + 1j]) / 2)
    return targets


def tdg_gate_unitary_nondeterministic():
    """Tdg-gate circuits reference unitaries."""
    targets = []
    # Tdg.H
    targets.append(np.array([[1 / np.sqrt(2), 1 / np.sqrt(2)], [0.5 - 0.5j, -0.5 + 0.5j]]))
    # X.Tdg.H
    targets.append(np.array([[0.5 - 0.5j, -0.5 + 0.5j], [1 / np.sqrt(2), 1 / np.sqrt(2)]]))
    # H.Tdg.Tdg.H = H.Sdg.H
    targets.append(np.array([[1 - 1j, 1 + 1j], [1 + 1j, 1 - 1j]]) / 2)
    return targets


# ==========================================================================
# CCX-gate
# ==========================================================================


def ccx_gate_circuits_deterministic(final_measure=True):
    """CCX-gate test circuits with deterministic counts."""
    circuits = []
    qr = QuantumRegister(3)
    if final_measure:
        cr = ClassicalRegister(3)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # CCX(0,1,2)
    circuit = QuantumCircuit(*regs)
    circuit.ccx(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # (I^X^X).CCX(0,1,2).(I^X^X) -> |100>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.barrier(qr)
    circuit.x(qr[1])
    circuit.barrier(qr)
    circuit.ccx(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    circuit.x(qr[0])
    circuit.barrier(qr)
    circuit.x(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CCX(2,1,0)
    circuit = QuantumCircuit(*regs)
    circuit.ccx(qr[2], qr[1], qr[0])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # (X^X^I).CCX(2,1,0).(X^X^I) -> |001>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[1])
    circuit.barrier(qr)
    circuit.x(qr[2])
    circuit.barrier(qr)
    circuit.ccx(qr[2], qr[1], qr[0])
    circuit.barrier(qr)
    circuit.x(qr[1])
    circuit.barrier(qr)
    circuit.x(qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def ccx_gate_counts_deterministic(shots, hex_counts=True):
    """CCX-gate circuits reference counts."""
    targets = []
    if hex_counts:
        # CCX(0,1,2)
        targets.append({"0x0": shots})
        # (I^X^X).CCX(0,1,2).(I^X^X) -> |100>
        targets.append({"0x4": shots})
        # CCX(2,1,0)
        targets.append({"0x0": shots})
        # (X^X^I).CCX(2,1,0).(X^X^I) -> |001>
        targets.append({"0x1": shots})
    else:
        # CCX(0,1,2)
        targets.append({"000": shots})
        # (I^X^X).CCX(0,1,2).(I^X^X) -> |100>
        targets.append({"000": shots})
        # CCX(2,1,0)
        targets.append({"000": shots})
        # (X^X^I).CCX(2,1,0).(X^X^I) -> |001>
        targets.append({"001": shots})
    return targets


def ccx_gate_statevector_deterministic():
    """CCX-gate circuits reference statevectors."""
    targets = []
    zero_state = np.array([1, 0, 0, 0, 0, 0, 0, 0])
    # CCX(0,1,2)
    targets.append(zero_state)
    # (I^X^X).CCX(0,1,2).(I^X^X) -> |100>
    targets.append(np.array([0, 0, 0, 0, 1, 0, 0, 0]))
    # CCX(2,1,0)
    targets.append(zero_state)
    # (X^X^I).CCX(2,1,0).(X^X^I) -> |001>
    targets.append(np.array([0, 1, 0, 0, 0, 0, 0, 0]))
    return targets


def ccx_gate_unitary_deterministic():
    """CCX-gate circuits reference unitaries."""
    targets = []

    # CCX(0,1,2)
    targets.append(
        np.array(
            [
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
            ]
        )
    )
    # (I^X^X).CCX(0,1,2).(I^X^X) -> |100>
    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
            ]
        )
    )
    # CCX(2,1,0)
    targets.append(
        np.array(
            [
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 0, 0, 1, 0],
            ]
        )
    )
    # (X^X^I).CCX(2,1,0).(X^X^I) -> |001>
    targets.append(
        np.array(
            [
                [0, 1, 0, 0, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
            ]
        )
    )
    return targets


def ccx_gate_circuits_nondeterministic(final_measure=True):
    """CCX-gate test circuits with non-deterministic counts."""
    circuits = []
    qr = QuantumRegister(3)
    if final_measure:
        cr = ClassicalRegister(3)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # (I^X^I).CCX(0,1,2).(I^X^H) -> |000> + |101>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[0])
    circuit.barrier(qr)
    circuit.x(qr[1])
    circuit.barrier(qr)
    circuit.ccx(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    circuit.x(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # (X^I^I).CCX(2,1,0).(X^H^I) -> |000> + |011>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.barrier(qr)
    circuit.x(qr[2])
    circuit.barrier(qr)
    circuit.ccx(qr[2], qr[1], qr[0])
    circuit.barrier(qr)
    circuit.x(qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def ccx_gate_counts_nondeterministic(shots, hex_counts=True):
    """CCX-gate circuits reference counts."""
    targets = []
    if hex_counts:
        # (I^X^I).CCX(0,1,2).(I^X^H) -> |000> + |101>
        targets.append({"0x0": shots / 2, "0x5": shots / 2})
        # (X^I^I).CCX(2,1,0).(X^H^I) -> |000> + |011>
        targets.append({"0x0": shots / 2, "0x3": shots / 2})
    else:
        # (I^X^I).CCX(0,1,2).(I^X^H) -> |000> + |101>
        targets.append({"000": shots / 2, "101": shots / 2})
        # (X^I^I).CCX(2,1,0).(X^H^I) -> |000> + |011>
        targets.append({"000": shots / 2, "011": shots / 2})
    return targets


def ccx_gate_statevector_nondeterministic():
    """CCX-gate circuits reference statevectors."""
    targets = []
    # (I^X^I).CCX(0,1,2).(I^X^H) -> |000> + |101>
    targets.append(np.array([1, 0, 0, 0, 0, 1, 0, 0]) / np.sqrt(2))
    # (X^I^I).CCX(2,1,0).(X^H^I) -> |000> + |011>
    targets.append(np.array([1, 0, 0, 1, 0, 0, 0, 0]) / np.sqrt(2))
    return targets


def ccx_gate_unitary_nondeterministic():
    """CCX-gate circuits reference unitaries."""
    targets = []
    # (I^X^I).CCX(0,1,2).(I^X^H) -> |000> + |101>
    targets.append(
        np.array(
            [
                [1, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, -1, 0, 0],
                [0, 0, 1, 1, 0, 0, 0, 0],
                [0, 0, 1, -1, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 1, 0, 0],
                [1, -1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 1],
                [0, 0, 0, 0, 0, 0, 1, -1],
            ]
        )
        / np.sqrt(2)
    )
    # (X^I^I).CCX(2,1,0).(X^H^I) -> |000> + |011>
    targets.append(
        np.array(
            [
                [1, 0, 1, 0, 0, 0, 0, 0],
                [0, 1, 0, 1, 0, 0, 0, 0],
                [0, 1, 0, -1, 0, 0, 0, 0],
                [1, 0, -1, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 0, 1, 0],
                [0, 0, 0, 0, 0, 1, 0, 1],
                [0, 0, 0, 0, 1, 0, -1, 0],
                [0, 0, 0, 0, 0, 1, 0, -1],
            ]
        )
        / np.sqrt(2)
    )
    return targets


# ==========================================================================
# CSWAP-gate (Fredkin)
# ==========================================================================
def cswap_gate_circuits_deterministic(final_measure):
    """cswap-gate test circuits with deterministic counts."""
    circuits = []
    qr = QuantumRegister(3)
    if final_measure:
        cr = ClassicalRegister(3)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # CSWAP(0,1,2) # -> |000>
    circuit = QuantumCircuit(*regs)
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(X^I^I) -> |100>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[2])
    circuit.barrier(qr)
    circuit.cswap(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(I^X^I) -> |010>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[1])
    circuit.barrier(qr)
    circuit.cswap(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(X^X^I) -> |110>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[1])
    circuit.x(qr[2])
    circuit.barrier(qr)
    circuit.cswap(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(I^I^X) -> |001>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.barrier(qr)
    circuit.cswap(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(I^X^X -> |101>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.x(qr[1])
    circuit.barrier(qr)
    circuit.cswap(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(X^I^X) -> |011>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.x(qr[2])
    circuit.barrier(qr)
    circuit.cswap(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(X^X^X) -> |111>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.x(qr[1])
    circuit.x(qr[2])
    circuit.barrier(qr)
    circuit.cswap(qr[0], qr[1], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(1,0,2).(I^X^X) -> |110>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.x(qr[1])
    circuit.barrier(qr)
    circuit.cswap(qr[1], qr[0], qr[2])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(2,1,0).(X^I^X) -> |110>
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.x(qr[2])
    circuit.barrier(qr)
    circuit.cswap(qr[2], qr[1], qr[0])
    circuit.barrier(qr)
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)
    return circuits


def cswap_gate_counts_deterministic(shots, hex_counts=True):
    """ "cswap-gate  circuits  reference counts."""
    targets = []

    if hex_counts:
        # CSWAP(0,1,2) # -> |000>
        targets.append({"0x0": shots})
        # CSWAP(0,1,2).(X^I^I) -> |100>
        targets.append({"0x4": shots})
        # CSWAP(0,1,2).(I^X^I) -> |010>
        targets.append({"0x2": shots})
        # CSWAP(0,1,2).(X^X^I) -> |110>
        targets.append({"0x6": shots})
        # CSWAP(0,1,2).(I^I^X). -> |001>
        targets.append({"0x1": shots})
        # CSWAP(0,1,2).(I^X^X) -> |101>
        targets.append({"0x5": shots})
        # CSWAP(0,1,2).(X^I^X) -> |011>
        targets.append({"0x3": shots})
        # CSWAP(0,1,2).(X^X^X) -> |111>
        targets.append({"0x7": shots})
        # CSWAP(1,0,2).(I^X^X) -> |110>
        targets.append({"0x6": shots})
        # CSWAP(2,1,0).(X^I^X) -> |110>
        targets.append({"0x6": shots})
    else:
        # CSWAP(0,1,2) # -> |000>
        targets.append({"000": shots})
        # CSWAP(0,1,2).(X^I^I) -> |100>
        targets.append({"100": shots})
        # CSWAP(0,1,2).(I^X^I) -> |010>
        targets.append({"010": shots})
        # CSWAP(0,1,2).(X^X^I) -> |110>
        targets.append({"110": shots})
        # CSWAP(0,1,2).(I^I^X) -> |001>
        targets.append({"001": shots})
        # CSWAP(0,1,2).(I^X^X) -> |101>
        targets.append({"101": shots})
        # CSWAP(0,1,2).(X^I^X) -> |011>
        targets.append({"011": shots})
        # CSWAP(0,1,2).(X^X^X) -> |111>
        targets.append({"111": shots})
        # CSWAP(1,0,2).(I^X^X) -> |110>
        targets.append({"110": shots})
        # CSWAP(2,1,0).(X^I^X) -> |110>
        targets.append({"110": shots})
    return targets


def cswap_gate_circuits_nondeterministic(final_measure=True):
    """cswap-gate test circuits with deterministic counts."""
    circuits = []
    qr = QuantumRegister(3)
    if final_measure:
        cr = ClassicalRegister(3)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # CSWAP(0,1,2).(H^H^H)
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[0])
    circuit.h(qr[1])
    circuit.h(qr[2])
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(X^I^H). -> |100> + |011>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[0])
    circuit.x(qr[2])
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(I^X^H). -> |010> + |101>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[0])
    circuit.x(qr[1])
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(I^H^I)  -> |010>+|000>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(H^I^I)  -> |100>+|000>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[2])
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)
    # CSWAP(0,1,2).(I^I^H)  -> |001>+|000>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[0])
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CSWAP(0,1,2).(X^X^H)  -> |110> + |111>
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[0])
    circuit.x(qr[1])
    circuit.x(qr[2])
    circuit.cswap(qr[0], qr[1], qr[2])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def cswap_gate_counts_nondeterministic(shots, hex_counts=True):
    targets = []

    if hex_counts:
        # CSWAP(0,1,2).(H^H^H)  -> |--->
        targets.append(
            {
                "0x0": shots / 8,
                "0x1": shots / 8,
                "0x2": shots / 8,
                "0x3": shots / 8,
                "0x4": shots / 8,
                "0x5": shots / 8,
                "0x6": shots / 8,
                "0x7": shots / 8,
            }
        )
        # CSWAP(0,1,2).(X^I^H). -> |100> + |011>
        targets.append({"0x3": shots / 2, "0x4": shots / 2})
        # CSWAP(0,1,2).(I^X^H). -> |010> + |101>
        targets.append({"0x2": shots / 2, "0x5": shots / 2})
        # CSWAP(0,1,2).(I^H^I)  -> |0-0>
        targets.append({"0x2": shots / 2, "0x0": shots / 2})
        # CSWAP(0,1,2).(H^I^I)  -> |-00>
        targets.append({"0x4": shots / 2, "0x0": shots / 2})
        # CSWAP(0,1,2).(I^I^H)  -> |00->
        targets.append({"0x0": shots / 2, "0x1": shots / 2})
        # CSWAP(0,1,2).(X^X^H)  -> |110> + |111>
        targets.append({"0x6": shots / 2, "0x7": shots / 2})

    else:
        # CSWAP(0,1,2).(H^H^H)  -> |--->
        targets.append(
            {
                "000": shots / 8,
                "001": shots / 8,
                "010": shots / 8,
                "011": shots / 8,
                "100": shots / 8,
                "101": shots / 8,
                "110": shots / 8,
                "111": shots / 8,
            }
        )
        # CSWAP(0,1,2).(X^I^H). -> |100> + |011>
        targets.append({"011": shots / 2, "100": shots / 2})
        # CSWAP(0,1,2).(I^X^H). -> |010> + |101>
        targets.append({"010": shots / 2, "101": shots / 2})
        # CSWAP(0,1,2).(I^H^I)  -> |0-0>
        targets.append({"010": shots / 2, "000": shots / 2})
        # CSWAP(0,1,2).(H^I^I)  -> |-00>
        targets.append({"100": shots / 2, "000": shots / 2})
        # CSWAP(0,1,2).(I^I^H)  -> |00->
        targets.append({"001": shots / 2, "000": shots / 2})
        # CSWAP(0,1,2).(X^X^H)  -> |110> + |111>
        targets.append({"110": shots / 2, "111": shots / 2})

    return targets


def cswap_gate_statevector_deterministic():
    targets = []
    # CSWAP(0,1,2) # -> |000>
    targets.append(np.array([1, 0, 0, 0, 0, 0, 0, 0]))
    # CSWAP(0,1,2).(X^I^I) -> |100>
    targets.append(np.array([0, 0, 0, 0, 1, 0, 0, 0]))
    # CSWAP(0,1,2).(I^X^I) -> |010>
    targets.append(np.array([0, 0, 1, 0, 0, 0, 0, 0]))
    # CSWAP(0,1,2).(X^X^I) -> |110>
    targets.append(np.array([0, 0, 0, 0, 0, 0, 1, 0]))
    # CSWAP(0,1,2).(I^I^X) -> |001>
    targets.append(np.array([0, 1, 0, 0, 0, 0, 0, 0]))
    # CSWAP(0,1,2).(I^X^X) -> |101>
    targets.append(np.array([0, 0, 0, 0, 0, 1, 0, 0]))
    # CSWAP(0,1,2).(X^I^X) -> |011>
    targets.append(np.array([0, 0, 0, 1, 0, 0, 0, 0]))
    # CSWAP(0,1,2).(X^X^X) -> |111>
    targets.append(np.array([0, 0, 0, 0, 0, 0, 0, 1]))
    # CSWAP(1,0,2).(I^X^X) -> |110>
    targets.append(np.array([0, 0, 0, 0, 0, 0, 1, 0]))
    # CSWAP(2,1,0).(X^I^X) -> |110>
    targets.append(np.array([0, 0, 0, 0, 0, 0, 1, 0]))
    return targets


def cswap_gate_statevector_nondeterministic():
    targets = []
    # CSWAP(0,1,2).(H^H^H)  -> |--->
    targets.append(np.array([1, 1, 1, 1, 1, 1, 1, 1]) / np.sqrt(8))
    # CSWAP(0,1,2).(X^I^H). -> |100> + |011>
    targets.append(np.array([0, 0, 0, 1, 1, 0, 0, 0]) / np.sqrt(2))
    # CSWAP(0,1,2).(I^X^H). -> |010> + |101>
    targets.append(np.array([0, 0, 1, 0, 0, 1, 0, 0]) / np.sqrt(2))
    # CSWAP(0,1,2).(I^H^I)  -> |0-0>
    targets.append(np.array([1, 0, 1, 0, 0, 0, 0, 0]) / np.sqrt(2))
    # CSWAP(0,1,2).(H^I^I)  -> |-00>
    targets.append(np.array([1, 0, 0, 0, 1, 0, 0, 0]) / np.sqrt(2))
    # CSWAP(0,1,2).(I^I^H)  -> |00->
    targets.append(np.array([1, 1, 0, 0, 0, 0, 0, 0]) / np.sqrt(2))
    # CSWAP(0,1,2).(X^X^H)  -> |110> + |111>
    targets.append(np.array([0, 0, 0, 0, 0, 0, 1, 1]) / np.sqrt(2))
    return targets


def cswap_gate_unitary_deterministic():
    """cswap-gate circuits reference unitaries."""
    targets = []

    # CSWAP(0,1,2) # -> |000>
    targets.append(
        np.array(
            [
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
            ]
        )
    )
    # CSWAP(0,1,2).(X^I^I) -> |100>
    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
            ]
        )
    )
    # CSWAP(0,1,2).(I^X^I) -> |010>
    targets.append(
        np.array(
            [
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
            ]
        )
    )
    # CSWAP(0,1,2).(X^X^I) -> |110>
    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
            ]
        )
    )
    # CSWAP(0,1,2).(I^I^X) -> |001>
    targets.append(
        np.array(
            [
                [0, 1, 0, 0, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 0, 0, 1, 0],
            ]
        )
    )
    # CSWAP(0,1,2).(I^X^X) -> |101>
    targets.append(
        np.array(
            [
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
            ]
        )
    )
    # CSWAP(0,1,2).(X^I^X) -> |011>
    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
            ]
        )
    )
    # CSWAP(0,1,2).(X^X^X) -> |111>
    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
            ]
        )
    )
    # CSWAP(1,0,2).(I^X^X) -> |110>
    targets.append(
        np.array(
            [
                [0, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
            ]
        )
    )
    # CSWAP(2,1,0).(X^I^X) -> |110>
    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 0, 1, 0, 0],
                [0, 0, 0, 0, 1, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 0, 0, 0, 0, 1, 0],
                [0, 1, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 1, 0, 0, 0, 0],
                [1, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 1, 0, 0, 0, 0, 0],
            ]
        )
    )
    return targets


def cswap_gate_unitary_nondeterministic():
    """cswap-gate circuits reference unitaries."""
    targets = []

    targets.append(
        np.array(
            [
                [
                    0.35355339,
                    0.35355339,
                    0.35355339,
                    0.35355339,
                    0.35355339,
                    0.35355339,
                    0.35355339,
                    0.35355339,
                ],
                [
                    0.35355339,
                    -0.35355339,
                    0.35355339,
                    -0.35355339,
                    0.35355339,
                    -0.35355339,
                    0.35355339,
                    -0.35355339,
                ],
                [
                    0.35355339,
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                    0.35355339,
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                ],
                [
                    0.35355339,
                    -0.35355339,
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                    0.35355339,
                    -0.35355339,
                    0.35355339,
                ],
                [
                    0.35355339,
                    0.35355339,
                    0.35355339,
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                    -0.35355339,
                    -0.35355339,
                ],
                [
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                    0.35355339,
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                    0.35355339,
                ],
                [
                    0.35355339,
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                    -0.35355339,
                    -0.35355339,
                    0.35355339,
                    0.35355339,
                ],
                [
                    0.35355339,
                    -0.35355339,
                    -0.35355339,
                    0.35355339,
                    -0.35355339,
                    0.35355339,
                    0.35355339,
                    -0.35355339,
                ],
            ]
        )
    )

    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 0.70710678, 0.70710678, 0, 0],
                [0, 0, 0, 0, 0.70710678, -0.70710678, 0, 0],
                [0, 0, 0, 0, 0, 0, 0.70710678, 0.70710678],
                [0.70710678, -0.70710678, 0, 0, 0, 0, 0, 0],
                [0.70710678, 0.70710678, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0.70710678, -0.70710678],
                [0, 0, 0.70710678, 0.70710678, 0, 0, 0, 0],
                [0, 0, 0.70710678, -0.70710678, 0, 0, 0, 0],
            ]
        )
    )

    targets.append(
        np.array(
            [
                [0, 0, 0.70710678, 0.70710678, 0, 0, 0, 0],
                [0, 0, 0.70710678, -0.70710678, 0, 0, 0, 0],
                [0.70710678, 0.70710678, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0.70710678, -0.70710678],
                [0, 0, 0, 0, 0, 0, 0.70710678, 0.70710678],
                [0.70710678, -0.70710678, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0.70710678, 0.70710678, 0, 0],
                [0, 0, 0, 0, 0.70710678, -0.70710678, 0, 0],
            ]
        )
    )

    targets.append(
        np.array(
            [
                [0.70710678, 0, 0.70710678, 0, 0, 0, 0, 0],
                [0, 0.70710678, 0, 0.70710678, 0, 0, 0, 0],
                [0.70710678, 0, -0.70710678, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0.70710678, 0, 0.70710678],
                [0, 0, 0, 0, 0.70710678, 0, 0.70710678, 0],
                [0, 0.70710678, 0, -0.70710678, 0, 0, 0, 0],
                [0, 0, 0, 0, 0.70710678, 0, -0.70710678, 0],
                [0, 0, 0, 0, 0, 0.70710678, 0, -0.70710678],
            ]
        )
    )

    targets.append(
        np.array(
            [
                [0.70710678, 0, 0, 0, 0.70710678, 0, 0, 0],
                [0, 0.70710678, 0, 0, 0, 0.70710678, 0, 0],
                [0, 0, 0.70710678, 0, 0, 0, 0.70710678, 0],
                [0, 0.70710678, 0, 0, 0, -0.70710678, 0, 0],
                [0.70710678, 0, 0, 0, -0.70710678, 0, 0, 0],
                [0, 0, 0, 0.70710678, 0, 0, 0, 0.70710678],
                [0, 0, 0.70710678, 0, 0, 0, -0.70710678, 0],
                [0, 0, 0, 0.70710678, 0, 0, 0, -0.70710678],
            ]
        )
    )

    targets.append(
        np.array(
            [
                [0.70710678, 0.70710678, 0, 0, 0, 0, 0, 0],
                [0.70710678, -0.70710678, 0, 0, 0, 0, 0, 0],
                [0, 0, 0.70710678, 0.70710678, 0, 0, 0, 0],
                [0, 0, 0, 0, 0.70710678, -0.70710678, 0, 0],
                [0, 0, 0, 0, 0.70710678, 0.70710678, 0, 0],
                [0, 0, 0.70710678, -0.70710678, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0.70710678, 0.70710678],
                [0, 0, 0, 0, 0, 0, 0.70710678, -0.70710678],
            ]
        )
    )

    targets.append(
        np.array(
            [
                [0, 0, 0, 0, 0, 0, 0.70710678, 0.70710678],
                [0, 0, 0, 0, 0, 0, 0.70710678, -0.70710678],
                [0, 0, 0, 0, 0.70710678, 0.70710678, 0, 0],
                [0, 0, 0.70710678, -0.70710678, 0, 0, 0, 0],
                [0, 0, 0.70710678, 0.70710678, 0, 0, 0, 0],
                [0, 0, 0, 0, 0.70710678, -0.70710678, 0, 0],
                [0.70710678, 0.70710678, 0, 0, 0, 0, 0, 0],
                [0.70710678, -0.70710678, 0, 0, 0, 0, 0, 0],
            ]
        )
    )
    return targets


# ==========================================================================
# CU1
# ==========================================================================
def cu1_gate_circuits_nondeterministic(final_measure):
    circuits = []
    qr = QuantumRegister(2)
    if final_measure:
        cr = ClassicalRegister(2)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # H^X.CU1(0,0,1).H^X
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.x(qr[0])
    circuit.cp(0, qr[0], qr[1])
    circuit.x(qr[0])
    circuit.h(qr[1])

    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^I.CU1(pi,0,1).H^I
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.cp(np.pi, qr[0], qr[1])
    circuit.h(qr[1])

    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^X.CU1(pi/4,0,1).H^X
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.x(qr[0])
    circuit.cp(np.pi / 4, qr[0], qr[1])
    circuit.x(qr[0])
    circuit.h(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^X.CU1(pi/2,0,1).H^X
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.x(qr[0])
    circuit.cp(np.pi / 2, qr[0], qr[1])
    circuit.x(qr[0])
    circuit.h(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^X.CU1(pi,0,1).H^X
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.x(qr[0])
    circuit.cp(np.pi, qr[0], qr[1])
    circuit.x(qr[0])
    circuit.h(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^H.CU1(0,0,1).H^H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.h(qr[0])
    circuit.cp(0, qr[0], qr[1])
    circuit.h(qr[0])
    circuit.h(qr[1])

    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^H.CU1(pi/2,0,1).H^H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.h(qr[0])
    circuit.cp(np.pi / 2, qr[0], qr[1])
    circuit.h(qr[0])
    circuit.h(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^H.CU1(pi,0,1).H^H
    circuit = QuantumCircuit(*regs)
    circuit.h(qr[1])
    circuit.h(qr[0])
    circuit.cp(np.pi, qr[0], qr[1])
    circuit.h(qr[0])
    circuit.h(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def cu1_gate_counts_nondeterministic(shots, hex_counts=True):
    """CU1-gate circuits reference counts."""
    targets = []
    if hex_counts:
        # H^X.CU1(0,0,1).H^X
        targets.append({"0x0": shots})
        # H^I.CU1(pi,0,1).H^I
        targets.append({"0x0": shots})
        # H^X.CU1(pi/4,0,1).H^X
        targets.append(
            {"0x0": shots * (0.25 * (2 + np.sqrt(2))), "0x2": shots * (0.25 * (2 - np.sqrt(2)))}
        )
        # H^X.CU1(pi/2,0,1).H^X
        targets.append({"0x0": shots * 0.5, "0x2": shots * 0.5})
        # H^X.CU1(pi,0,1).H^X
        targets.append({"0x2": shots})
        # H^H.CU1(0,0,1).H^H
        targets.append({"0x0": shots})
        # H^H.CU1(pi/2,0,1).H^H
        targets.append(
            {"0x0": shots * 0.625, "0x1": shots * 0.125, "0x2": shots * 0.125, "0x3": shots * 0.125}
        )
        # H^H.CU1(pi,0,1).H^H
        targets.append(
            {"0x0": shots * 0.25, "0x1": shots * 0.25, "0x2": shots * 0.25, "0x3": shots * 0.25}
        )
    else:
        # H^X.CU1(0,0,1).H^X
        targets.append({"00": shots})
        # H^I.CU1(pi,0,1).H^I
        targets.append({"00": shots})
        # H^X.CU1(pi/4,0,1).H^X
        targets.append({"00": shots * 0.85, "10": shots * 0.15})
        # H^X.CU1(pi/2,0,1).H^X
        targets.append({"00": shots * 0.5, "10": shots * 0.5})
        # H^X.CU1(pi,0,1).H^X
        targets.append({"10": shots})
        # H^H.CU1(0,0,1).H^H
        targets.append({"00": shots})
        # H^H.CU1(pi/2,0,1).H^H
        targets.append(
            {"00": shots * 0.5125, "01": shots * 0.125, "10": shots * 0.125, "11": shots * 0.125}
        )
        # H^H.CU1(pi,0,1).H^H
        targets.append(
            {"00": shots * 0.25, "01": shots * 0.25, "10": shots * 0.25, "11": shots * 0.25}
        )
    return targets


def cu1_gate_statevector_nondeterministic():
    targets = []
    # H^X.CU1(0,0,1).H^X
    targets.append(np.array([1, 0, 0, 0]))
    # H^I.CU1(pi,0,1).H^I
    targets.append(np.array([1, 0, 0, 0]))
    # H^X.CU1(pi/4,0,1).H^X
    targets.append(
        np.array(
            [
                (0.25 * (2 + np.sqrt(2))) + (1 / (2 * np.sqrt(2))) * 1j,
                0,
                (0.25 * (2 - np.sqrt(2))) - (1 / (2 * np.sqrt(2))) * 1j,
                0,
            ]
        )
    )
    # H^X.CU1(pi/2,0,1).H^X
    targets.append(np.array([0.5 + 0.5j, 0, 0.5 - 0.5j, 0]))
    # H^X.CU1(pi,0,1).H^X
    targets.append(np.array([0, 0, 1, 0]))
    # H^H.CU1(0,0,1).H^H
    targets.append(np.array([1, 0, 0, 0]))
    # H^H.CU1(pi/2,0,1).H^H
    targets.append(np.array([0.75 + 0.25j, 0.25 - 0.25j, 0.25 - 0.25j, -0.25 + 0.25j]))
    # H^H.CU1(pi,0,1).H^H
    targets.append(np.array([0.5, 0.5, 0.5, -0.5]))
    return targets


def cu1_gate_unitary_nondeterministic():
    targets = []
    # H^X.CU1(0,0,1).H^X
    targets.append(np.eye(4))
    # H^I.CU1(pi,0,1).H^I
    targets.append(np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]))
    # H^X.CU1(pi/4,0,1).H^X
    targets.append(
        np.array(
            [
                [
                    (0.25 * (2 + np.sqrt(2))) + (1 / (2 * np.sqrt(2))) * 1j,
                    0,
                    (0.25 * (2 - np.sqrt(2))) - (1 / (2 * np.sqrt(2))) * 1j,
                    0,
                ],
                [0, 1, 0, 0],
                [
                    (0.25 * (2 - np.sqrt(2))) - (1 / (2 * np.sqrt(2))) * 1j,
                    0,
                    (0.25 * (2 + np.sqrt(2))) + (1 / (2 * np.sqrt(2))) * 1j,
                    0,
                ],
                [0, 0, 0, 1],
            ]
        )
    )
    # H^X.CU1(pi/2,0,1).H^X
    targets.append(
        np.array(
            [
                [0.5 + 0.5j, 0, 0.5 - 0.5j, 0],
                [0, 1, 0, 0],
                [0.5 - 0.5j, 0, 0.5 + 0.5j, 0],
                [0, 0, 0, 1],
            ]
        )
    )
    # H^X.CU1(pi,0,1).H^X
    targets.append(np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]))
    # H^H.CU1(0,0,1).H^H
    targets.append(np.eye(4))
    # H^H.CU1(pi/2,0,1).H^H
    targets.append(
        (0.75 + 0.25j) * np.eye(4)
        + (0.25 - 0.25j) * np.array([[0, 1, 1, -1], [1, 0, -1, 1], [1, -1, 0, 1], [-1, 1, 1, 0]])
    )
    # H^H.CU1(pi,0,1).H^H
    targets.append(0.5 * np.array([[1, 1, 1, -1], [1, 1, -1, 1], [1, -1, 1, 1], [-1, 1, 1, 1]]))
    return targets


# ==========================================================================
# CU3
# ==========================================================================
def cu3_gate_circuits_deterministic(final_measure):
    circuits = []
    qr = QuantumRegister(2)
    if final_measure:
        cr = ClassicalRegister(2)
        regs = (qr, cr)
    else:
        regs = (qr,)

    # I^X.CI.I^X
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.cu(0, 0, 0, 0, qr[0], qr[1])
    circuit.x(qr[0])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # CX
    circuit = QuantumCircuit(*regs)
    circuit.cu(np.pi, 0, np.pi, 0, qr[0], qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # I^X.CX.I^X
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.cu(np.pi, 0, np.pi, 0, qr[0], qr[1])
    circuit.x(qr[0])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # H^X.CH.I^X
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.cu(np.pi / 2, 0, np.pi, 0, qr[0], qr[1])
    circuit.x(qr[0])
    circuit.h(qr[1])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # I^X.CRX(pi).I^X
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.cu(np.pi, -np.pi / 2, np.pi / 2, 0, qr[0], qr[1])
    circuit.x(qr[0])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    # I^X.CRY(pi).I^X
    circuit = QuantumCircuit(*regs)
    circuit.x(qr[0])
    circuit.cu(np.pi, 0, 0, 0, qr[0], qr[1])
    circuit.x(qr[0])
    if final_measure:
        circuit.barrier(qr)
        circuit.measure(qr, cr)
    circuits.append(circuit)

    return circuits


def cu3_gate_unitary_deterministic():
    targets = []

    # I^X.CI.I^X
    targets.append(np.eye(4))

    # CX
    targets.append(np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]))

    # I^X.CX.I^X
    targets.append(np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]))

    # H^X.CH.I^X
    targets.append(
        np.array(
            [
                [1, 0, 0, 0],
                [0, 1 / np.sqrt(2), 0, 1 / np.sqrt(2)],
                [0, 0, 1, 0],
                [0, 1 / np.sqrt(2), 0, -1 / np.sqrt(2)],
            ]
        )
    )

    # I^X.CRX(pi).I^X
    targets.append(np.array([[0, 0, -1j, 0], [0, 1, 0, 0], [-1j, 0, 0, 0], [0, 0, 0, 1]]))

    # I^X.CRY(pi).I^X
    targets.append(np.array([[0, 0, -1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]))

    return targets


def cu3_gate_statevector_deterministic():
    init_state = np.array([1, 0, 0, 0])
    targets = [mat.dot(init_state) for mat in cu3_gate_unitary_deterministic()]

    return targets


def cu3_gate_counts_deterministic(shots, hex_counts=True):
    """CU2-gate circuits reference counts."""
    probs = [np.abs(vec) ** 2 for vec in cu3_gate_statevector_deterministic()]
    targets = []
    for prob in probs:
        if hex_counts:
            targets.append({hex(i): shots * p for i, p in enumerate(prob)})
        else:
            counts = {}
            for i, p in enumerate(prob):
                key = bin(i)[2:]
                key = (2 - len(key)) * "0" + key
                counts[key] = shots * p
            targets.append(counts)
    return targets
