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

In this section, we will go through the basic construction of Pauli operators

1.1. Pauli operator#

1.1.1. Construct Pauli operator with its expression#

A Pauli operator can be constructed using the pauli() constructor.

paulialg.pauli('XXIYZ')
 +XXIYZ

By default the operator has a \(+1\) phase factor in the front. To specify other phase factors(\(\pm1\) or \(\pm \mathrm{i}\)), use '+', '-', 'i' indicators before the Pauli string.

paulialg.pauli('-X'), paulialg.pauli('iX'), paulialg.pauli('-iX')
( -X, +iX, -iX)

It is also possible to assign the phase factor by scalar mutiplication.

-paulialg.pauli('X'), 1j*paulialg.pauli('X')
( -X, +iX)

1.1.2. Other methods to construct a Pauli operator#

You can construct a Pauli operator from a tuple / list / array of indices (0 = I, 1 = X, 2 = Y, 3 = Z)

paulialg.pauli((0,1,2,3)), paulialg.pauli([0,1,2,3]), paulialg.pauli(np.array([0,1,2,3]))
( +IXYZ,  +IXYZ,  +IXYZ)

Or you can construct a Pauli operator from a dictionary that maps positions to indices. (Note: using this method must also provide the total number of qubits as the second argument, because the qubit number can not be infered from the dictionary alone.)

paulialg.pauli({1:'X', 4:'Y', 5:'Z'}, 6), paulialg.pauli({1:1, 4:2, 5:3}, 6)
( +IXIIYZ,  +IXIIYZ)

1.1.3. Size information#

For Pauli operator, .N returns the number of qubits (size of system) that the operator acts on.

paulialg.pauli('IXYIXI').N
6

1.2. Pauli operator list#

A list of Pauli operators can be constructed by the paulis() constructor.

paulialg.paulis('iX', '-iY', 'Z')
+iX
-iY
 +Z

It can also take a generator and iterate through its elements to construct a list of Pauli operators.

paulialg.paulis(paulialg.pauli({i:'Z'}, 4) for i in range(4))
 +ZIII
 +IZII
 +IIZI
 +IIIZ

It can also take a iterable (tuple / list / set) and convert it to a list of Pauli operators.

lists = ['iX', '-iY', 'Z']
paulialg.paulis(lists)
+iX
-iY
 +Z

1.2.1. Size information#

For Pauli operator list, .L returns the number of operators in the list and .N returns of the number fo qubits in the system.

plst = paulialg.paulis('II','XX','YY','ZZ')
plst.L, plst.N
(4, 2)

We can also return the number of operators in the list by naive python len() function

len(plst)
4

1.2.2. Selection and Slicing of Pauli List#

Select a single element in the Pauli operator list.

plst[1]
 +XX

Select a range of operators in the Pauli operator list: the slicing is the same as python array

plst[0:3]
 +II
 +XX
 +YY
plst[::2]
 +II
 +YY

It is also allow to be selected by a index array or a boolean mask.

plst[np.array([2,1,1,0,3])]
 +YY
 +XX
 +XX
 +II
 +ZZ
plst[np.array([True,False,False,True])]
 +II
 +ZZ

1.3. Pauli Polynomials#

Pauli operators can be linearly combined in to a Pauli polynomial.

paulialg.pauli('XX') + paulialg.pauli('YY') - 0.5 * paulialg.pauli('ZZ')
-0.50000 ZZ +1 XX +1 YY
Adding Pauli operators with any number, the number will be promoted to the number times identity operator automatically. For example, a projection operator can be written as
(paulialg.pauli('ZZ') + 1)/2
0.50000 II +0.50000 ZZ

Operators can be summed up with python built-in function sum().

sum(paulialg.paulis('II','XX','YY','ZZ'))
1 II +1 ZZ +1 XX +1 YY