import sys
sys.path.insert(0, '..')
import numpy as np
import qutip as qt
import src
from src import (utils, paulialg, stabilizer, circuit)

1.4. Pauli Algebra#

1.4.1. Dot Productor (Matrix Multiplication)#

Dot productor (composition) of Pauli operators is implemented as the matrix multiplication matmul, which can be implemented using the operand @.

paulialg.pauli('X') @ paulialg.pauli('Y'), paulialg.pauli('Y') @ paulialg.pauli('X')
(+iZ, -iZ)

Dot product of Pauli polynomials will be expanded.

poly = paulialg.pauli('XX') + paulialg.pauli('YY') - 0.5 * paulialg.pauli('ZZ')
poly @ poly
0.25000 II +0.50000 YY +0.50000 XX +0.50000 YY +1 II -1 ZZ +0.50000 XX -1 ZZ +1 II

Terms will not be combined automatically. To combine them, the .reduce() method should be explicitly called.

(poly @ poly).reduce()
2.25000 II -2 ZZ +1 XX +1 YY

1.4.2. Trace of Pauli operators#

  • Pauli.trace() will return the trace of a Pauli operator

  • PauliList.trace() will return the trace of a list of Pauli operators

  • PauliPolynomial.trace() will return the trace of a Pauli polynomial

paulialg.pauli('II').trace()
4
paulialg.paulis('II','YY').trace()
array([4, 0])
(3*paulialg.pauli('II')).trace()
(12+0j)
(3*paulialg.pauli('II')+(3+2.5j)*paulialg.pauli('II')).trace()
(24+10j)

1.4.3. Weight (number of non-identity support)#

  • Pauli.weight() will return the weight (the support of non identity operators) of a Pauli operator

  • PauliList.weight() will return the weight of a list of Pauli operators

paulialg.pauli('IXIYZII').weight()
3
paulialg.paulis('IXIYZII','IXIIIII').weight()
array([3, 1])

1.4.4. Type conversion#

Automatic type conversion enables the algebra to be carried out among different classes with great flexibiliity.

  • When Pauli is multiplied (*) by a generic number (beyond powers of the imaginary unit), it is converted to PauliMonomial.

  • When Pauli or PauliMonomial is added (+) or subtracted (-) with other Pauli objects, they are converted to PauliPolynomial.

  • The dot product (@) generally returns PauliPolynomial, unless the two Pauli objects are both Pauli, in which case it returns Pauli.

1.5. Clifford Transformation#

PauliList provides useful methods to implement Clifford transformations efficiently on all Pauli operators together. The same methods are available to all its subclasses (including PauliPolynomial, CliffordMap, StabilizerState).

1.5.1. Clifford Rotation#

A Clifford rotation is a \(\mathbb{Z}_4\) rotation in the Clifford group generated by a single Pauli operator, which takes the form of $\( U=e^{\frac{i\pi}{4}\sigma}=\frac{1}{\sqrt{2}}(1+i \sigma) \)\( Every Pauli operator is transformed by \)\sigma \to U^\dagger \sigma U$. The Clifford rotation can be applied by the method .rotate_by(gen) (given the generator gen). The operation is in-place (meaning that the operators in the Pauli list will be modified).

paulialg.paulis('II','XX','YY','ZZ').rotate_by(paulialg.pauli('XI'))
 +II
 +XX
 +ZY
 -YZ

1.5.2. Clifford Map#

A Clifford map is a generic clifford transformation by specifying how each single Pauli operator gets mapped to. It can be listed as a table

cmap = stabilizer.random_clifford_map(2)
cmap
CliffordMap(
  X0-> -IZ
  Z0-> +YY
  X1-> -ZZ
  Z1-> -XZ)

It can be applied by the method .transform_by(cmap) (given the Clifford map cmap).

paulialg.paulis('II','XX','YY','ZZ').transform_by(cmap)
 +II
 +ZI
 +IX
 -ZX

1.5.3. Masked Transformation#

Clifford transformation can be applied to a subsystem of qubits specified by a mask.

mask = np.array([True,False,False,True])
paulialg.paulis('IIII','XXXX','YYYY','ZZZZ').rotate_by(paulialg.pauli('XY'), mask)
 +IIII
 -IXXZ
 +ZYYI
 +ZZZZ
mask = np.array([True,False,False,True])
paulialg.paulis('IIII','XXXX','YYYY','ZZZZ').transform_by(cmap, mask)
 +IIII
 +ZXXI
 +IYYX
 -ZZZX