def test_simulation_phase():
    """
    Test if the Phase gate is applied correctly to the tableau

    S|0> = |0>

    S|+> = |R>
    """
    prog = Program().inst(S(0))
    qvmstab = QVM_Stabilizer(num_qubits=1)
    qvmstab._apply_phase(prog.instructions[0])
    true_stab = np.array([[1, 1, 0], [0, 1, 0]])
    assert np.allclose(true_stab, qvmstab.tableau)

    prog = Program().inst([H(0), S(0)])
    qvmstab = QVM_Stabilizer(num_qubits=1)
    qvmstab._apply_hadamard(prog.instructions[0])
    qvmstab._apply_phase(prog.instructions[1])
    true_stab = np.array([[0, 1, 0], [1, 1, 0]])
    assert np.allclose(true_stab, qvmstab.tableau)
def test_simulation_cnot():
    """
    Test if the simulation of CNOT is accurate
    :return:
    """
    prog = Program().inst([H(0), CNOT(0, 1)])
    qvmstab = QVM_Stabilizer(num_qubits=2)
    qvmstab._apply_hadamard(prog.instructions[0])
    qvmstab._apply_cnot(prog.instructions[1])

    # assert that ZZ XX stabilizes a bell state
    true_stabilizers = [sX(0) * sX(1), sZ(0) * sZ(1)]
    test_paulis = binary_stabilizer_to_pauli_stabilizer(qvmstab.tableau[2:, :])
    for idx, term in enumerate(test_paulis):
        assert term == true_stabilizers[idx]

    # test that CNOT does nothing to |00> state
    prog = Program().inst([CNOT(0, 1)])
    qvmstab = QVM_Stabilizer(num_qubits=2)
    qvmstab._apply_cnot(prog.instructions[0])
    true_tableau = np.array([
        [1, 1, 0, 0, 0],  # X1 -> X1 X2
        [0, 1, 0, 0, 0],  # X2 -> X2
        [0, 0, 1, 0, 0],  # Z1 -> Z1
        [0, 0, 1, 1, 0]
    ])  # Z2 -> Z1 Z2

    # note that Z1, Z1 Z2 still stabilizees |00>
    assert np.allclose(true_tableau, qvmstab.tableau)

    # test that CNOT produces 11 state after X
    prog = Program().inst([H(0), S(0), S(0), H(0), CNOT(0, 1)])
    qvmstab = QVM_Stabilizer(num_qubits=2)
    qvmstab._apply_hadamard(prog.instructions[0])
    qvmstab._apply_phase(prog.instructions[1])
    qvmstab._apply_phase(prog.instructions[2])
    qvmstab._apply_hadamard(prog.instructions[3])
    qvmstab._apply_cnot(prog.instructions[4])
    true_tableau = np.array([[1, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 1],
                             [0, 0, 1, 1, 0]])

    # note that -Z1, Z1 Z2 still stabilizees |11>
    assert np.allclose(true_tableau, qvmstab.tableau)

    test_paulis = binary_stabilizer_to_pauli_stabilizer(
        qvmstab.stabilizer_tableau())
    state = project_stabilized_state(test_paulis,
                                     qvmstab.num_qubits,
                                     classical_state=[1, 1])
    state_2 = project_stabilized_state(test_paulis, qvmstab.num_qubits)

    assert np.allclose(np.array(state.todense()), np.array(state_2.todense()))
def test_simulation_hadamard():
    """
    Test if Hadamard is applied correctly to the tableau

    The first example will be a 1 qubit example. where we perform H | 0 > = |0> + |1>.
    Therefore we expect the tableau to represent the stablizer X
    """
    prog = Program().inst(H(0))
    qvmstab = QVM_Stabilizer(num_qubits=1)
    qvmstab._apply_hadamard(prog.instructions[0])
    x_stabilizer = np.array([[0, 1, 0], [1, 0, 0]])
    assert np.allclose(x_stabilizer, qvmstab.tableau)

    qvmstab = QVM_Stabilizer(num_qubits=2)
    qvmstab._apply_hadamard(prog.instructions[0])
    x_stabilizer = np.array([[0, 0, 1, 0, 0], [0, 1, 0, 0, 0], [1, 0, 0, 0, 0],
                             [0, 0, 0, 1, 0]])
    assert np.allclose(x_stabilizer, qvmstab.tableau)

    prog = Program().inst(H(1))
    qvmstab = QVM_Stabilizer(num_qubits=2)
    qvmstab._apply_hadamard(prog.instructions[0])
    x_stabilizer = np.array([[1, 0, 0, 0, 0], [0, 0, 0, 1, 0], [0, 0, 1, 0, 0],
                             [0, 1, 0, 0, 0]])
    assert np.allclose(x_stabilizer, qvmstab.tableau)

    prog = Program().inst([H(0), H(1)])
    qvmstab = QVM_Stabilizer(num_qubits=2)
    qvmstab._apply_hadamard(prog.instructions[0])
    qvmstab._apply_hadamard(prog.instructions[1])
    x_stabilizer = np.array([[0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [1, 0, 0, 0, 0],
                             [0, 1, 0, 0, 0]])
    assert np.allclose(x_stabilizer, qvmstab.tableau)

    prog = Program().inst([H(0), H(1), H(0), H(1)])
    qvmstab = QVM_Stabilizer(num_qubits=2)
    qvmstab._apply_hadamard(prog.instructions[0])
    qvmstab._apply_hadamard(prog.instructions[1])
    qvmstab._apply_hadamard(prog.instructions[2])
    qvmstab._apply_hadamard(prog.instructions[3])
    x_stabilizer = np.array([[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0],
                             [0, 0, 0, 1, 0]])
    assert np.allclose(x_stabilizer, qvmstab.tableau)