def test_rowsum_phase_accumulator():
    """
    Test the accuracy of the phase accumulator.  This subroutine keeps track of the power that $i$ is raised to
    when multiplying two PauliTerms together.  PauliTerms are now composed of multi single-qubit Pauli's.

    The formula is phase_accumulator(h, i) = 2 * rh + 2 * ri + \sum_{j}^{n}g(x_{ij}, z_{ij}, x_{hj}, z_{hj}

    The returned value is presented mod 4.

    notice that since r_h indicates +1 or -1 this corresponds to i^{0} or i^{2}.  Given r_{h/i} takes on {0, 1} then
    we need to multiply by 2 get the correct power of $i$ for the Pauli Term to account for multiplication.

    In order to test we will generate random elements of P_{n} and then multiply them together keeping track of the
    $i$ power.  We will then compare this to the phase_accumulator subroutine.

    We have to load in the stabilizer into an empty qvmstab object because the subroutine needs a tableau as a reference
    """
    num_qubits = 2
    pauli_terms = [sX(0) * sX(1), sZ(0) * sZ(1)]
    stab_mat = pauli_stabilizer_to_binary_stabilizer(pauli_terms)
    qvmstab = QVM_Stabilizer(num_qubits=num_qubits)
    qvmstab.tableau[num_qubits:, :] = stab_mat
    exp_on_i = qvmstab._rowsum_phase_accumulator(2, 3)
    assert exp_on_i == 2

    num_qubits = 2
    pauli_terms = [sZ(0) * sI(1), sZ(0) * sZ(1)]
    stab_mat = pauli_stabilizer_to_binary_stabilizer(pauli_terms)
    qvmstab = QVM_Stabilizer(num_qubits=num_qubits)
    qvmstab.tableau[num_qubits:, :] = stab_mat
    exp_on_i = qvmstab._rowsum_phase_accumulator(2, 3)
    assert exp_on_i == 0

    # now try generating random valid elements from 2-qubit group
    for _ in range(100):
        num_qubits = 6
        pauli_terms = []
        for _ in range(num_qubits):
            pauli_terms.append(
                reduce(lambda x, y: x * y, [
                    pauli_subgroup[x](idx) for idx, x in enumerate(
                        np.random.randint(1, 4, num_qubits))
                ]))
        stab_mat = pauli_stabilizer_to_binary_stabilizer(pauli_terms)
        qvmstab = QVM_Stabilizer(num_qubits=num_qubits)
        qvmstab.tableau[num_qubits:, :] = stab_mat
        p_on_i = qvmstab._rowsum_phase_accumulator(num_qubits, num_qubits + 1)
        coeff_test = pauli_terms[1] * pauli_terms[0]
        assert np.isclose(coeff_test.coefficient, (1j)**p_on_i)
def test_rowsum_full():
    """
    Test the rowsum subroutine.

    This routine replaces row h with P(i) * P(h) where P is the pauli operator represented by the row
    given by the index.  It uses the summation to accumulate whether the phase is +1 or -1 and then
    xors together the elements of the rows replacing row h
    """
    # now try generating random valid elements from 2-qubit group
    for _ in range(100):
        num_qubits = 6
        pauli_terms = []
        for _ in range(num_qubits):
            pauli_terms.append(
                reduce(lambda x, y: x * y, [
                    pauli_subgroup[x](idx) for idx, x in enumerate(
                        np.random.randint(1, 4, num_qubits))
                ]))
        try:
            stab_mat = pauli_stabilizer_to_binary_stabilizer(pauli_terms)
        except:
            # we have to do this because I'm not strictly making valid n-qubit
            # stabilizers
            continue
        qvmstab = QVM_Stabilizer(num_qubits=num_qubits)
        qvmstab.tableau[num_qubits:, :] = stab_mat
        p_on_i = qvmstab._rowsum_phase_accumulator(num_qubits, num_qubits + 1)
        if p_on_i not in [1, 3]:
            coeff_test = pauli_terms[1] * pauli_terms[0]
            assert np.isclose(coeff_test.coefficient, (1j)**p_on_i)
            qvmstab._rowsum(num_qubits, num_qubits + 1)
            try:
                pauli_op = binary_stabilizer_to_pauli_stabilizer(
                    qvmstab.tableau[[num_qubits], :])[0]
            except:
                continue
            true_pauli_op = pauli_terms[1] * pauli_terms[0]
            assert pauli_op == true_pauli_op