Beispiel #1
0
def makeOracle(q0, q1, secretFunction):
    """ Gates implementing the secret function f(x)."""
    if secretFunction[0]:
        yield [CNOT(q0, q1), X(q1)]

    if secretFunction[1]:
        yield CNOT(q0, q1)
Beispiel #2
0
def test_simplify_circuit_exponents():
    qreg = LineQubit.range(2)
    circuit = Circuit([H.on(qreg[0]), CNOT.on(*qreg), Z.on(qreg[1])])

    # Invert circuit
    inverse_circuit = cirq.inverse(circuit)
    inverse_repr = inverse_circuit.__repr__()
    inverse_qasm = inverse_circuit._to_qasm_output().__str__()

    # Expected circuit after simplification
    expected_inv = Circuit([Z.on(qreg[1]), CNOT.on(*qreg), H.on(qreg[0])])
    expected_repr = expected_inv.__repr__()
    expected_qasm = expected_inv._to_qasm_output().__str__()

    # Check inverse_circuit is logically equivalent to expected_inverse
    # but they have a different representation
    assert inverse_circuit == expected_inv
    assert inverse_repr != expected_repr
    assert inverse_qasm != expected_qasm

    # Simplify the circuit
    _simplify_circuit_exponents(inverse_circuit)

    # Check inverse_circuit has the expected simplified representation
    simplified_repr = inverse_circuit.__repr__()
    simplified_qasm = inverse_circuit._to_qasm_output().__str__()
    assert inverse_circuit == expected_inv
    assert simplified_repr == expected_repr
    assert simplified_qasm == expected_qasm
Beispiel #3
0
 def __call__(self, qubits):
     q0, q1, q2 = qubits
     yield rx(sympy.Symbol("rad0")).on(q0)
     yield CNOT(q0, q1)
     yield H(q2)
     yield CNOT(q2, q1)
     yield TOFFOLI(q1, q0, q2)
Beispiel #4
0
def test_simple_pauli_deco_dict_CNOT():
    """Tests that the _simple_pauli_deco_dict function returns a decomposition
    dicitonary which is consistent with a local depolarizing noise model.

    The channel acting on the state each qubit is assumed to be:
    D(rho) = = (1 - epsilon) rho + epsilon I/2
    = (1 - p) rho + p/3 (X rho X + Y rho Y^dag + Z rho Z)
    """

    # Deduce epsilon from BASE_NOISE
    epsilon = BASE_NOISE * 4.0 / 3.0
    c_neg = -(1 / 4) * epsilon / (1 - epsilon)
    c_pos = 1 - 3 * c_neg
    qreg = LineQubit.range(2)

    # Get the decomposition of a CNOT gate
    deco = DECO_DICT[CNOT.on(*qreg)]

    # The first term of 'deco' corresponds to no error occurring
    first_coefficient, first_imp_seq = deco[0]
    assert np.isclose(c_pos * c_pos, first_coefficient)
    assert first_imp_seq == [CNOT.on(*qreg)]
    # The second term corresponds to a Pauli X error on one qubit
    second_coefficient, second_imp_seq = deco[1]
    assert np.isclose(c_pos * c_neg, second_coefficient)
    assert second_imp_seq == [CNOT.on(*qreg), X.on(qreg[0])]
    # The last term corresponds to two Pauli Z errors on both qubits
    last_coefficient, last_imp_seq = deco[-1]
    assert np.isclose(c_neg * c_neg, last_coefficient)
    assert last_imp_seq == [CNOT.on(*qreg), Z.on(qreg[0]), Z.on(qreg[1])]
Beispiel #5
0
def _max_ent_state_circuit(num_qubits: int) -> Circuit:
    r"""Generates a circuits which prepares the maximally entangled state
    |\omega\rangle = U |0\rangle  = \sum_i |i\rangle \otimes |i\rangle .

    Args:
        num_qubits: The number of qubits on which the circuit is applied.
            Only 2 or 4 qubits are supported.

    Returns:
        The circuits which prepares the state |\omega\rangle.
    """

    qreg = LineQubit.range(num_qubits)
    circ = Circuit()
    if num_qubits == 2:
        circ.append(H.on(qreg[0]))
        circ.append(CNOT.on(*qreg))
    elif num_qubits == 4:
        # Prepare half of the qubits in a uniform superposition
        circ.append(H.on(qreg[0]))
        circ.append(H.on(qreg[1]))
        # Create a perfect correlation between the two halves of the qubits.
        circ.append(CNOT.on(qreg[0], qreg[2]))
        circ.append(CNOT.on(qreg[1], qreg[3]))
    else:
        raise NotImplementedError(
            "Only 2- or 4-qubit maximally entangling circuits are supported."
        )
    return circ
Beispiel #6
0
def make_oracle(q0, q1, secret_function):
    """ Gates implementing the secret function f(x)."""

    # coverage: ignore
    if secret_function[0]:
        yield [CNOT(q0, q1), X(q1)]

    if secret_function[1]:
        yield CNOT(q0, q1)
Beispiel #7
0
def test_aqt_device_wrong_op_str():
    circuit = Circuit()
    q0, q1 = LineQubit.range(2)
    circuit.append(CNOT(q0, q1)**1.0)
    for op in circuit.all_operations():
        with pytest.raises(ValueError):
            _result = get_op_string(op)
Beispiel #8
0
def _max_ent_state_circuit(num_qubits: int) -> Circuit:
    r"""Generates a circuit which prepares the maximally entangled state
    |\omega\rangle = U |0\rangle  = \sum_i |i\rangle \otimes |i\rangle .

    Args:
        num_qubits: The number of qubits on which the circuit is applied.
            It must be an even number because of the structure of a
            maximally entangled state.

    Returns:
        The circuits which prepares the state |\omega\rangle.

    Raises:
        Value error: if num_qubits is not an even positive integer.
    """

    if not isinstance(num_qubits, int) or num_qubits % 2 or num_qubits == 0:
        raise ValueError(
            "The argument 'num_qubits' must be an even and positive integer.")

    alice_reg = LineQubit.range(num_qubits // 2)
    bob_reg = LineQubit.range(num_qubits // 2, num_qubits)

    return Circuit(
        # Prepare alice_register in a uniform superposition
        H.on_each(*alice_reg),
        # Correlate alice_register with bob_register
        [CNOT.on(alice_reg[i], bob_reg[i]) for i in range(num_qubits // 2)],
    )
Beispiel #9
0
def twoLevelControlledRy(circuit, l, angle, k, externalControl, reg, workReg):
    """
    Implements two level Ry rotation from state |0> to |k>, if externalControl qubit is on
    for reference: http://www.physics.udel.edu/~msafrono/650/Lecture%206.pdf
    """
    grayList = generateGrayList(l, k)
    # handle the case where l=0 or 1
    if k == 0:
        return
    if l == 1 and k == 1:
        circuit.append(cirq.ry(angle).controlled().on(externalControl, reg[0]))
        return

    # swap states according to Gray Code until one step before the end
    for element in grayList:
        targetQub = element[0]
        number = element[1]
        number = number[0:targetQub] + number[targetQub + 1:]
        controlQub = numberControl(circuit, l - 1, number,
                                   reg[0:targetQub] + reg[targetQub + 1:],
                                   workReg)
        if element == grayList[-1]:  # reached end
            circuit.append(TOFFOLI(controlQub, externalControl,
                                   workReg[l - 2]),
                           strategy=new)
            circuit.append(
                cirq.ry(angle).controlled().on(workReg[l - 2], reg[targetQub]))
            circuit.append(TOFFOLI(controlQub, externalControl,
                                   workReg[l - 2]),
                           strategy=new)
        else:  # swap states
            circuit.append(CNOT(controlQub, reg[targetQub]), strategy=new)
        numberControlT(circuit, l - 1, number,
                       reg[0:targetQub] + reg[targetQub + 1:], workReg)

    # undo
    for element in reverse(grayList[:-1]):
        targetQub = element[0]
        number = element[1]
        number = number[0:targetQub] + number[targetQub + 1:]
        controlQub = numberControl(circuit, l - 1, number,
                                   reg[0:targetQub] + reg[targetQub + 1:],
                                   workReg)
        circuit.append(CNOT(controlQub, reg[targetQub]), strategy=new)
        numberControlT(circuit, l - 1, number,
                       reg[0:targetQub] + reg[targetQub + 1:], workReg)
    return
Beispiel #10
0
def U_h(circuit, l, n_i, m, n_phiReg, w_phiReg, n_aReg, w_aReg, n_bReg, w_bReg,
        wReg, eReg, pReg, hReg, w_hReg, P_phi, P_a, P_b):
    """Implement U_h from paper"""
    # for k in range(n_i + m):
    for k in range(1):
        countsList = generateParticleCounts(
            n_i, m, k)  # reduce the available number of particles
        for counts in [countsList[0]]:
            n_phi, n_a, n_b = counts[0], counts[1], counts[2]
            # controlled R-y from |0> to |k> on all qubits with all possible angles depending on n_phi, n_a, n_b, and flavor
            for flavor in ['phi']:
                # for flavor in ['phi', 'a', 'b']:
                angle = U_hAngle(flavor, n_phi, n_a, n_b, P_phi, P_a, P_b)
                # add in x gates for checking

                phiControl = numberControl(circuit, l, n_phi, n_phiReg,
                                           w_phiReg)
                print("cirq phiControl: ", phiControl)
                aControl = numberControl(circuit, l, n_a, n_aReg, w_aReg)
                print("cirq aControl: ", aControl)
                # bControl = numberControl(circuit, l, n_b, n_bReg, w_bReg)
                print("cirq wReg[0]: ", wReg[0])
                # circuit.append(TOFFOLI(phiControl, aControl, wReg[0]), strategy=new)
                #                 circuit.append(TOFFOLI(bControl, wReg[0], wReg[1]), strategy=new)
                flavorControl(
                    circuit, flavor, pReg[k], wReg[2],
                    wReg[4])  # wReg[4] is work qubit but is reset to 0


#                 circuit.append(TOFFOLI(wReg[1], wReg[2], wReg[3]), strategy=new)
#                 circuit.append(TOFFOLI(eReg[0], wReg[3], wReg[4]), strategy=new)
#
# #                 print("m ", m)
# #                 print("hReg[m]: ",hReg[m])
#
#                 twoLevelControlledRy(circuit, l, angle, k+1, wReg[4], hReg[m], w_hReg)
#
#                 circuit.append(TOFFOLI(eReg[0], wReg[3], wReg[4]), strategy=new)  # next steps undo work qubits
#                 circuit.append(TOFFOLI(wReg[1], wReg[2], wReg[3]), strategy=new)
#                 flavorControl(circuit, flavor, pReg[k], wReg[2], wReg[4])
#                 circuit.append(TOFFOLI(bControl, wReg[0], wReg[1]), strategy=new)
#                 circuit.append(TOFFOLI(phiControl, aControl, wReg[0]), strategy=new)
#                 numberControlT(circuit, l, n_b, n_bReg, w_bReg)
#                 numberControlT(circuit, l, n_a, n_aReg, w_aReg)
#                 numberControlT(circuit, l, n_phi, n_phiReg, w_phiReg)

# subtract from the counts register depending on which flavor particle emitted
        for flavor, countReg, workReg in zip(['phi', 'a', 'b'],
                                             [n_phiReg, n_aReg, n_bReg],
                                             [w_phiReg, w_aReg, w_bReg]):
            flavorControl(circuit, flavor, pReg[k], wReg[0], wReg[1])
            minus1(circuit, l, countReg, workReg, wReg[0], wReg[1], 0)
            flavorControl(circuit, flavor, pReg[k], wReg[0], wReg[1])

    # apply x on eReg if hReg[m] = 0, apply another x so we essentially control on not 0 instead of 0
    isZeroControl = numberControl(circuit, l, 0, hReg[m], w_hReg)
    circuit.append(CNOT(isZeroControl, eReg[0]))
    circuit.append(X(eReg[0]), strategy=new)
    numberControlT(circuit, l, 0, hReg[m], w_hReg)
Beispiel #11
0
def test_represent_operations_in_circuit_local(circuit_type: str):
    """Tests all operation representations are created."""
    qreg = LineQubit.range(2)
    circ_mitiq = Circuit([CNOT(*qreg), H(qreg[0]), Y(qreg[1]), CNOT(*qreg)])
    circ = convert_from_mitiq(circ_mitiq, circuit_type)

    reps = represent_operations_in_circuit_with_local_depolarizing_noise(
        ideal_circuit=circ, noise_level=0.1,
    )

    for op in convert_to_mitiq(circ)[0].all_operations():
        found = False
        for rep in reps:
            if _equal(rep.ideal, Circuit(op), require_qubit_equality=True):
                found = True
        assert found

    # The number of reps. should match the number of unique operations
    assert len(reps) == 3
Beispiel #12
0
def plus1(circuit, l, countReg, workReg, control, ancilla, level):
    """
    Recursively add 1 to the LSB of a register and carries to all bits, if control == 1
    l: number of qubits in count register
    countReg, workReg: count register and associated work register
    control: control qubit to determine if plus1 should be executed
    ancilla: extra work qubit
    level: current qubit we are operating on, recursively travels from qubit 0 to l-1
    """
    # apply X to LSB
    if level == 0:
        circuit.append(CNOT(control, countReg[0]), strategy=new)
    if level < l - 1:
        # first level uses CNOT instead of TOFFOLI gate
        if level == 0:
            # move all X gates to first step to avoid unnecesarry gates
            circuit.append([X(qubit) for qubit in countReg], strategy=new)
            circuit.append(TOFFOLI(countReg[0], control, workReg[0]),
                           strategy=new)
        else:
            circuit.append(TOFFOLI(countReg[level], workReg[level - 1],
                                   ancilla),
                           strategy=new)
            circuit.append(TOFFOLI(ancilla, control, workReg[level]),
                           strategy=new)
            circuit.append(TOFFOLI(countReg[level], workReg[level - 1],
                                   ancilla),
                           strategy=new)

        circuit.append(TOFFOLI(workReg[level], control, countReg[level + 1]),
                       strategy=new)
        # recursively call next layer
        print("countReg ", countReg)
        plus1(circuit, l, countReg, workReg, control, ancilla, level + 1)
        # undo work qubits (exact opposite of first 7 lines - undoes calculation)
        if level == 0:
            circuit.append(TOFFOLI(countReg[0], control, workReg[0]),
                           strategy=new)
            circuit.append([X(qubit) for qubit in countReg], strategy=new)
        else:
            circuit.append(TOFFOLI(countReg[level], workReg[level - 1],
                                   ancilla),
                           strategy=new)
            circuit.append(TOFFOLI(ancilla, control, workReg[level]),
                           strategy=new)
            circuit.append(TOFFOLI(countReg[level], workReg[level - 1],
                                   ancilla),
                           strategy=new)
Beispiel #13
0
def oracle_method(first_param, second_param, secret_function):
    if secret_function[0]:
        yield CNOT(first_param, second_param), X(second_param)

    if secret_function[1]:
        yield CNOT(first_param, second_param)
Beispiel #14
0
    return noisy_simulation(
        circuit,
        BASE_NOISE,
        obs,
    )


# Simple identity 1-qubit circuit for testing
q = LineQubit(1)
oneq_circ = Circuit(Z.on(q), Z.on(q))

# Simple identity 2-qubit circuit for testing
qreg = LineQubit.range(2)
twoq_circ = Circuit(
    Y.on(qreg[1]),
    CNOT.on(*qreg),
    Y.on(qreg[1]),
)


@mark.parametrize("circuit", [oneq_circ, twoq_circ])
@mark.parametrize("decomposition_dict",
                  [NOISELESS_DECO_DICT, DECO_DICT_SIMP, DECO_DICT])
def test_execute_with_pec_one_qubit(circuit: Circuit,
                                    decomposition_dict: DecompositionDict):
    """Tests that execute_with_pec mitigates the error of a noisy
    expectation value.
    """
    unmitigated = executor(circuit)
    mitigated = execute_with_pec(circuit,
                                 executor,
Beispiel #15
0
    DecompositionDict,
    _operation_to_choi,
    _circuit_to_choi,
)
from mitiq.pec.sampling import sample_sequence, sample_circuit

BASE_NOISE = 0.02
DECO_DICT = _simple_pauli_deco_dict(BASE_NOISE)
DECO_DICT_SIMP = _simple_pauli_deco_dict(BASE_NOISE, simplify_paulis=True)
NOISELESS_DECO_DICT = _simple_pauli_deco_dict(0)

# Simple 2-qubit circuit
qreg = LineQubit.range(2)
twoq_circ = Circuit(
    X.on(qreg[0]),
    CNOT.on(*qreg),
)


@pytest.mark.parametrize("gate", [X, Y, Z, CNOT])
def test_sample_sequence_types(gate: Gate):
    num_qubits = gate.num_qubits()
    qreg = LineQubit.range(num_qubits)
    for _ in range(1000):
        imp_seq, sign, norm = sample_sequence(gate.on(*qreg), DECO_DICT)
        assert all([isinstance(op, Operation) for op in imp_seq])
        assert sign in {1, -1}
        assert norm > 1


@pytest.mark.parametrize("seed", (1, 2, 3, 5))
def exponentiate_general_case(pauli_string, param):
    """
    Returns a Circuit object corresponding to the exponential of the `pauli_string` object, 
    i.e. exp(-1.0j * param * pauli_string)

    Parameters:
    ----------
    pauli_string    :   (PauliString object) represents the Pauli term to exponentiate
    param           :   (float) scalar, non-complex, value

    Returns:
    -------
    circuit         :   (Circuit) represents a parameterized circuit corresponding to the exponential of 
                        the input `pauli_string` and parameter `param`
    """
    def reverse_circuit_operations(c):
        reverse_circuit = Circuit()
        operations_in_c = []
        reverse_operations_in_c = []
        for operation in c.all_operations():
            operations_in_c.append(operation)

        reverse_operations_in_c = inverse(operations_in_c)

        for operation in reverse_operations_in_c:
            reverse_circuit.append(
                [operation], strategy=InsertStrategy.EARLIEST)
        return reverse_circuit
    circuit = Circuit()
    change_to_z_basis = Circuit()
    change_to_original_basis = Circuit()
    cnot_seq = Circuit()
    prev_qubit = None
    highest_target_qubit = None

    for qubit, pauli in pauli_string.items():
        if pauli == X:
            change_to_z_basis.append(
                [H(qubit)], strategy=InsertStrategy.EARLIEST)
            change_to_original_basis.append(
                [H(qubit)], strategy=InsertStrategy.EARLIEST)

        elif pauli == Y:
            RX = Rx(np.pi/2.0)
            change_to_z_basis.append(
                [RX(qubit)], strategy=InsertStrategy.EARLIEST)
            RX = inverse(RX)
            change_to_original_basis.append(
                [RX(qubit)], strategy=InsertStrategy.EARLIEST)

        if prev_qubit is not None:
            cnot_seq.append([CNOT(prev_qubit, qubit)],
                            strategy=InsertStrategy.EARLIEST)

        prev_qubit = qubit
        highest_target_qubit = qubit

    circuit += change_to_z_basis
    circuit += cnot_seq
    RZ = Rz(2.0 * pauli_string.coefficient *
            param)
    circuit.append([RZ(highest_target_qubit)],
                   strategy=InsertStrategy.EARLIEST)
    circuit += reverse_circuit_operations(cnot_seq)
    circuit += change_to_original_basis

    return circuit
Beispiel #17
0
def _simple_pauli_deco_dict(base_noise: float,
                            simplify_paulis: bool = False
                            ) -> DecompositionDict:
    """Returns a simple hard-coded decomposition
    dictionary to be used for testing and protoptyping.

    The decomposition is compatible with one-qubit or
    two-qubit circuits involving only Pauli and CNOT gates.

    The keys of the output dictionary are Pauli and CNOT operations.

    The decomposition assumes that Pauli and CNOT operations,
    followed by local depolarizing noise, are implementable.

    Args:
        base_noise: The depolarizing noise level.
        simplify_paulis: If True, products of Paulis are simplified to a
            single Pauli. If False, Pauli sequences are not simplified.

    Returns:
        decomposition_dict: The decomposition dictionary.

    """
    # Initialize two qubits
    qreg = LineQubit.range(2)

    # Single-qubit Pauli operations
    i0 = I.on(qreg[0])
    x0 = X.on(qreg[0])
    y0 = Y.on(qreg[0])
    z0 = Z.on(qreg[0])
    i1 = I.on(qreg[1])
    x1 = X.on(qreg[1])
    y1 = Y.on(qreg[1])
    z1 = Z.on(qreg[1])
    single_paulis = [x0, y0, z0, x1, y1, z1]

    # Single-qubit decomposition coefficients
    epsilon = base_noise * 4 / 3
    c_neg = -(1 / 4) * epsilon / (1 - epsilon)
    c_pos = 1 - 3 * c_neg
    assert np.isclose(c_pos + 3 * c_neg, 1.0)

    # Single-qubit decomposition dictionary
    decomposition_dict = {}
    if simplify_paulis:
        # Hard-coded simplified gates
        decomposition_dict = {
            x0: [(c_pos, [x0]), (c_neg, [i0]), (c_neg, [z0]), (c_neg, [y0])],
            y0: [(c_pos, [y0]), (c_neg, [z0]), (c_neg, [i0]), (c_neg, [x0])],
            z0: [(c_pos, [z0]), (c_neg, [y0]), (c_neg, [x0]), (c_neg, [i0])],
            x1: [(c_pos, [x1]), (c_neg, [i1]), (c_neg, [z1]), (c_neg, [y1])],
            y1: [(c_pos, [y1]), (c_neg, [z1]), (c_neg, [i1]), (c_neg, [x1])],
            z1: [(c_pos, [z1]), (c_neg, [y1]), (c_neg, [x1]), (c_neg, [i1])],
        }
    else:
        for local_paulis in [[x0, y0, z0], [x1, y1, z1]]:
            for key in local_paulis:
                key_deco_pos = [(c_pos, [key])]
                key_deco_neg = [(c_neg, [key, op]) for op in local_paulis]
                decomposition_dict[key] = (
                    key_deco_pos + key_deco_neg  # type: ignore
                )

    # Two-qubit Paulis
    xx = [x0, x1]
    xy = [x0, y1]
    xz = [x0, z1]
    yx = [y0, x1]
    yy = [y0, y1]
    yz = [y0, z1]
    zx = [z0, x1]
    zy = [z0, y1]
    zz = [z0, z1]
    double_paulis = [xx, xy, xz, yx, yy, yz, zx, zy, zz]

    # Two-qubit decomposition coefficients (assuming local noise)
    c_pos_pos = c_pos * c_pos
    c_pos_neg = c_neg * c_pos
    c_neg_neg = c_neg * c_neg
    assert np.isclose(c_pos_pos + 6 * c_pos_neg + 9 * c_neg_neg, 1.0)

    cnot = CNOT.on(qreg[0], qreg[1])
    cnot_decomposition = [(c_pos_pos, [cnot])]
    for p in single_paulis:
        cnot_decomposition.append((c_pos_neg, [cnot] + [p]))
    for pp in double_paulis:
        cnot_decomposition.append((c_neg_neg, [cnot] + pp))  # type: ignore

    decomposition_dict[cnot] = cnot_decomposition  # type: ignore

    return decomposition_dict  # type: ignore
Beispiel #18
0
def makeOracle(q0, q1, secretFunction):
    if secretFunction[0]:
        yield [CNOT(q0, q1), X(q1)]

    if secretFunction[1]:
        yield CNOT(q0, q1)
Beispiel #19
0
 def get_cnot_circuit(control_line, target_line):
     cnot_circuit = Circuit()
     cnot_circuit.append([CNOT(control_line, target_line)],
                         strategy=InsertStrategy.EARLIEST)
     return cnot_circuit
Beispiel #20
0
        obs = np.array(
            [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
        )
    else:
        raise ValueError("The input must be a circuit with 1 or 2 qubits.")

    return noisy_simulation(circuit, BASE_NOISE, obs,)


# Simple identity 1-qubit circuit for testing
q = LineQubit(1)
oneq_circ = Circuit(Z.on(q), Z.on(q))

# Simple identity 2-qubit circuit for testing
qreg = LineQubit.range(2)
twoq_circ = Circuit(Y.on(qreg[1]), CNOT.on(*qreg), Y.on(qreg[1]),)


@mark.parametrize("circuit", [oneq_circ, twoq_circ])
@mark.parametrize(
    "decomposition_dict", [NOISELESS_DECO_DICT, DECO_DICT_SIMP, DECO_DICT]
)
def test_execute_with_pec_one_qubit(
    circuit: Circuit, decomposition_dict: DecompositionDict
):
    """Tests that execute_with_pec mitigates the error of a noisy
    expectation value.
    """
    unmitigated = executor(circuit)
    mitigated = execute_with_pec(
        circuit, executor, decomposition_dict=decomposition_dict
import unittest

import ddt
from cirq import rx, Z, CNOT

from paulicirq.linear_combinations import *

q0, q1, q2 = cirq.LineQubit.range(3)

op_tuple_1 = (rx(sympy.Symbol("θ"))(q0), CNOT(q2, q1))
op_tuple_2 = (CNOT(q0, q2), Z(q1))

m1 = cirq.Moment(op_tuple_1)
m2 = cirq.Moment(op_tuple_2)


@ddt.ddt
class LinearSymbolicDictTest(unittest.TestCase):
    """
    A test class for `LinearSymbolicDict`, `LinearCombinationOfMoments`, and
    `LinearCombinationOfOperations` in `paulicirq.gates.linear_combinations`.

    """
    @ddt.unpack
    @ddt.data([LinearCombinationOfOperations, op_tuple_1, op_tuple_2],
              [LinearCombinationOfMoments, m1, m2])
    def test_numerical(self, _type, term1, term2):
        c1 = 0.123
        c2 = 0.589

        lc1 = _type({term1: c1, term2: c2})