def CZ(state, qubits):
    """
    Applies a Controlled-X gate (CNOT) rotation to generators

    This version is best for a large number of qubits (aboa ut >= 150)

    II -> II
    XI -> XZ
    ZI -> ZI
    WI -> WZ
    IX -> ZX
    IZ -> IZ
    IW -> ZW
    XX -> -WW
    XZ -> XI
    XW -> -WX
    ZX -> IX
    ZZ -> ZZ
    ZW -> IW
    WX -> -XW
    WZ -> WI
    WW -> -XX

    II -> II
    XI -> XZ
    ZI -> ZI
    YI -> YZ
    IX -> ZX
    IZ -> IZ
    IY -> ZY
    XX -> YY
    XZ -> XI
    XY -> -YX
    ZX -> IX
    ZZ -> ZZ
    ZY -> IY
    YX -> -XY
    YZ -> YI
    YY -> XX
    """
    qubit1, qubit2 = qubits
    stabs = state.stabs
    stabs.signs_minus ^= stabs.col_x[qubit1] & stabs.col_x[qubit2]
    for gens in state.gen_list:
        old_z1_col = set(gens.col_z[qubit1])
        old_z2_col = set(gens.col_z[qubit2])
        gens.col_z[qubit1] ^= gens.col_x[qubit2]
        gens.col_z[qubit2] ^= gens.col_x[qubit1]
        z1_added = gens.col_z[qubit1] - old_z1_col
        z2_added = gens.col_z[qubit2] - old_z2_col
        for i in z1_added:
            gens.row_z[i].add(qubit1)
        for i in z2_added:
            gens.row_z[i].add(qubit2)
        del z1_added
        del z2_added
        z1_removed = old_z1_col - gens.col_z[qubit1]
        z2_removed = old_z2_col - gens.col_z[qubit2]
        for i in z1_removed:
            gens.row_z[i].discard(qubit1)
        for i in z2_removed:
            gens.row_z[i].discard(qubit2)
