More features

Zixy collections are suitable for Pauli tableau operations.

from zixy.qubit import Qubits
from zixy.qubit.pauli import Strings, I, X, Y, Z
strings = Strings.from_iterable((
    (X, Y, I, I, X, X, Z, Y),
    (Y, I, I, X, X, Z, X, Y),
    (Y, I, I, X, X, Z, I, Y),
    (X, Z, I, Y, Y, I, I, X),
), Qubits.from_count(8))
strings.compatibility_matrix()
array([[1, 0, 1, 0],
       [0, 1, 1, 1],
       [1, 1, 1, 1],
       [0, 1, 1, 1]], dtype=uint8)

The compatibility_matrix function has efficiently computed the commutivity (1) or anti-commutivity (0) between each Pauli string in the tableau.

Notice that the string with index 2 commutes with all other strings. This is the sole member of the list’s centralizer set.

We have a function to extract this:

centralizer, remainder = strings.centralizer_and_remainder()
assert len(centralizer) == 1
assert centralizer[0] == strings[2]
centralizer
Y0 X3 X4 Z5 Y7

Now as expected the compatibility matrix of the remainder shows no rows that are all 1s

remainder.compatibility_matrix()
array([[1, 0, 0],
       [0, 1, 1],
       [0, 1, 1]], dtype=uint8)

Change of basis (conjugation) by a sequence of Clifford gates is supported.

strings[0].phase_of_mul(strings[1])
+i
strings[0] *= strings[1]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[5], line 1
----> 1 strings[0] *= strings[1]

File ~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/qubit/pauli/_strings.py:185, in String.__imul__(self, rhs)
    183 phase = self.phase_of_mul(rhs)
    184 if phase != ComplexSign():
--> 185     raise _imul_factor_error(self, rhs, phase)
    186 self.imul_ignore_phase(rhs)
    187 return self

ValueError: Cannot multiply-assign left-hand operand X0 Y1 X4 X5 Z6 Y7 with type <class 'zixy.qubit.pauli._strings.String'> by value Y0 X3 X4 Z5 X6 Y7. Result with factor +i is not representable by the left-hand operand type.
strings
X0 Y1 X4 X5 Z6 Y7, Y0 X3 X4 Z5 X6 Y7, Y0 X3 X4 Z5 Y7, X0 Z1 Y3 Y4 X7
from zixy.qubit.clifford import GateList
GateList().s(1).h(2).cx(0, 2).s(3)
S(1), H(2), CX(0, 2), S(3)

An exception is raised if any input is invalid

GateList().s(1).h(2).cx(2, 2).s(3)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[8], line 1
----> 1 GateList().s(1).h(2).cx(2, 2).s(3)

File ~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/qubit/clifford/gate_list.py:74, in GateList.cx(self, i_control, i_target)
     61 def cx(self, i_control: int, i_target: int) -> GateList:
     62     """Push a CNOT gate into the list.
     63 
     64     Args:
   (...)     72         This method operates in-place.
     73     """
---> 74     self._impl.push_cx(i_control, i_target)
     75     return self

ValueError: Value 2 is both elements of what is supposed to be a distinct pair.
from zixy.qubit.pauli import SignTerms
terms = SignTerms.from_iterable((
    (X, Y, I, I, X, X, Z, Y),
    (Y, I, Z, Z, X, Z, X, Y),
    (I, X, I, X, Y, Y, X, Y),
    (Y, Z, Z, Z, X, Z, I, Y),
    (Y, I, I, X, X, X, I, Y),
    (X, Z, I, Y, Y, I, I, X),
), Qubits.from_count(8))
cliffords = GateList().s(1).h(2).cx(0, 2).h(3).cx(6, 3)
terms.conj_clifford_list(cliffords)
terms.to_dataframe()
Component Coefficient
0 X0 X1 X2 X4 X5 Z6 Y7 -1
1 Y0 X4 Z5 X6 Y7 +1
2 Y1 Y3 Y4 Y5 Y6 Y7 -1
3 Y0 Z1 X3 X4 Z5 Y7 +1
4 Y0 X2 Z3 X4 X5 Z6 Y7 +1
5 X0 Z1 X2 Y3 Y4 Z6 X7 -1