跳转至

TikzDrawer

QuICT.tools.drawer.tikz_drawer

Barrier module-attribute

Barrier = BasicGate(*GATEINFO_MAP[barrier], is_original_gate=True)

The Barrier Gate.

[How to apply]

Barrier | circuit(0)
Barrier & 0 | circuit

[Graph Representation]

        
    ────░────
        

[QASM Representation]

barrier q[0]

CCRz module-attribute

CCRz = BasicGate(*GATEINFO_MAP[ccrz], is_original_gate=True)

Double-Qubit Controlled Rz Gate, CCRz Gate.

[Matrix Representation]

\[ CCRz_{q0,q1,q2}(\lambda) = \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & e^{\frac{-i\lambda}{2}} & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & e^{\frac{i\lambda}{2}} \\ \end{bmatrix} \]

[How to apply]

CCRz(pi) | circuit([0, 1, 2])
CCRz(pi) & [0, 1, 2] | circuit

[Graph Representation]

    q0:  ─────■──────
              |
              |
    q1:  ─────■──────
              |
           ┌──────┐
    q2:  ──┤ ccrz ├──
           └──────┘

[QASM Representation]

ccrz(pi) q[0], q[1], q[2]

CCX module-attribute

CCX = BasicGate(*GATEINFO_MAP[ccx], is_original_gate=True)

Double-Qubit Controlled X Gate, CCX Gate.

[Matrix Representation]

\[ CCX_{q0,q1,q2} = \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ \end{bmatrix} \]

[How to apply]

CCX | circuit([0, 1, 2])
CCX & [0, 1, 2] | circuit

[Graph Representation]

    q0:  ─────■─────
              |
              |
    q1:  ─────■─────
              |
           ┌─────┐
    q2:  ──┤ ccx ├──
           └─────┘

[QASM Representation]

ccx q[0], q[1], q[2]

CCZ module-attribute

CCZ = BasicGate(*GATEINFO_MAP[ccz], is_original_gate=True)

Double-Qubit Controlled Z Gate, CCZ Gate.

[Matrix Representation]

\[ CCZ_{q0,q1,q2} = \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & -1 \\ \end{bmatrix} \]

[How to apply]

CCZ | circuit([0, 1, 2])
CCZ & [0, 1, 2] | circuit

[Graph Representation]

    q0:  ─────■─────
              |
              |
    q1:  ─────■─────
              |
           ┌─────┐
    q2:  ──┤ ccz ├──
           └─────┘

[QASM Representation]

ccz q[0], q[1], q[2]

CH module-attribute

CH = BasicGate(*GATEINFO_MAP[ch], is_original_gate=True)

Controlled H Gate.

[Matrix Representation]

\[ CH_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \\ 0 & 0 & \frac{1}{\sqrt{2}} & \frac{-1}{\sqrt{2}} \\ \end{bmatrix} \]

[How to apply]

CH | circuit([0, 1])
CH & [0, 1] | circuit

[Graph Representation]

    q0:  ────■────
             |
           ┌────┐
    q1:  ──┤ ch ├──
           └────┘

[QASM Representation]

ch q[0], q[1]

CRy module-attribute

CRy = BasicGate(*GATEINFO_MAP[cry], is_original_gate=True)

Controlled Ry Gate.

[Matrix Representation]

\[ CRy_{q0,q1}(\lambda) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos(\frac{\lambda}{2}) & -\sin(\frac{\lambda}{2}) \\ 0 & 0 & \sin(\frac{\lambda}{2}) & \cos(\frac{\lambda}{2}) \\ \end{bmatrix} \]

[How to apply]

CRy(pi) | circuit([0, 1])
CRy(pi) & [0, 1] | circuit

[Graph Representation]

    q0:  ─────■─────
              |
           ┌─────┐
    q1:  ──┤ cry ├──
           └─────┘

[QASM Representation]

cry(pi) q[0], q[1]

CRz module-attribute

CRz = BasicGate(*GATEINFO_MAP[crz], is_original_gate=True)

Controlled Rz Gate.

[Matrix Representation]

\[ CRz_{q0,q1}(\lambda) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & e^{\frac{-i\lambda}{2}} & 0 \\ 0 & 0 & 0 & e^{\frac{i\lambda}{2}} \\ \end{bmatrix} \]

[How to apply]

CRz(pi) | circuit([0, 1])
CRz(pi) & [0, 1] | circuit

[Graph Representation]

    q0:  ─────■─────
              |
           ┌─────┐
    q1:  ──┤ crz ├──
           └─────┘

[QASM Representation]

crz(pi) q[0], q[1]

CSwap module-attribute

CSwap = BasicGate(*GATEINFO_MAP[cswap], is_original_gate=True)

Controlled Swap Gate.

[Matrix Representation]

\[ CSwap_{q0,q1,q2} = \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ \end{bmatrix} \]

[How to apply]

CSwap | circuit([0, 1, 2])
CSwap & [0, 1, 2] | circuit

[Graph Representation]

    q0:  ──────■──────
               |
               |
    q1:  ──────X──────
               |
               |
    q2:  ──────X──────

[QASM Representation]

cswap q[0], q[1], q[2]

CU1 module-attribute

CU1 = BasicGate(*GATEINFO_MAP[cu1], is_original_gate=True)

Controlled U1 Gate.

[Matrix Representation]

\[ CU1_{q0,q1}(\lambda) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & e^{i\lambda} \\ \end{bmatrix} \]

[How to apply]

CU1(pi) | circuit([0, 1])
CU1(pi) & [0, 1] | circuit

[Graph Representation]

    q0:  ─────■─────
              |
           ┌─────┐
    q1:  ──┤ cu1 ├──
           └─────┘

[QASM Representation]

cu1(pi) q[0], q[1]

CU3 module-attribute

CU3 = BasicGate(*GATEINFO_MAP[cu3], is_original_gate=True)

Controlled U3 Gate.

[Matrix Representation]

\[ CU3_{q0,q1}(\theta, \sigma, \lambda) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & \cos(\frac{\theta}{2}) & -\sin(\frac{\theta}{2}) e^{i\lambda} \\ 0 & 0 & \sin(\frac{\theta}{2}) e^{i\sigma} & \cos(\frac{\theta}{2}) e^{i(\lambda + \sigma)} \\ \end{bmatrix} \]

[How to apply]

CU3(pi, 0, 1) | circuit([0, 1])
CU3(pi, 0, 1) & [0, 1] | circuit

[Graph Representation]

    q0:  ─────■─────
              |
           ┌─────┐
    q1:  ──┤ cu3 ├──
           └─────┘

[QASM Representation]

cu3(pi, 0, 1) q[0], q[1]

CX module-attribute

CX = BasicGate(*GATEINFO_MAP[cx], is_original_gate=True)

Controlled X Gate.

[Matrix Representation]

\[ CX_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \]

[How to apply]

CX | circuit([0, 1])
CX & [0, 1] | circuit

[Graph Representation]

    q0:  ────■────
             |
           ┌────┐
    q1:  ──┤ cx ├──
           └────┘

[QASM Representation]

cx q[0], q[1]

CY module-attribute

CY = BasicGate(*GATEINFO_MAP[cy], is_original_gate=True)

Controlled Y Gate.

[Matrix Representation]

\[ CY_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & -i \\ 0 & 0 & i & 0 \\ \end{bmatrix} \]

[How to apply]

CY | circuit([0, 1])
CY & [0, 1] | circuit

[Graph Representation]

    q0:  ────■────
             |
           ┌────┐
    q1:  ──┤ cy ├──
           └────┘

[QASM Representation]

cy q[0], q[1]

CZ module-attribute

CZ = BasicGate(*GATEINFO_MAP[cz], is_original_gate=True)

Controlled Z Gate.

[Matrix Representation]

\[ CZ_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -1 \\ \end{bmatrix} \]

[How to apply]

CZ | circuit([0, 1])
CZ & [0, 1] | circuit

[Graph Representation]

    q0:  ────■────
             |
           ┌────┐
    q1:  ──┤ cz ├──
           └────┘

[QASM Representation]

cz q[0], q[1]

ECR module-attribute

ECR = BasicGate(*GATEINFO_MAP[ecr], is_original_gate=True)

ECR Quantum Gate.

[Matrix Representation]

\[ ECR_{q0,q1} = \frac{1}{\sqrt{2}} \begin{bmatrix} 0 & 0 & 1 & i \\ 0 & 0 & i & 1 \\ 1 & -i & 0 & 0 \\ -i & 1 & 0 & 0 \\ \end{bmatrix} \]

[How to apply]

ECR | circuit([0, 1])
ECR & [0, 1] | circuit

[Graph Representation]

           ┌──────┐
    q0:  ──┤0     ├──
           |  ECR |
    q1:  ──┤1     ├──
           └──────┘

[QASM Representation]

ecr q[0], q[1]

FSim module-attribute

FSim = BasicGate(*GATEINFO_MAP[fsim], is_original_gate=True)

Fermions Simulation Quantum Gate.

[Matrix Representation]

\[ FSim_{q0,q1}(\theta, \lambda) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\theta) & -i\sin(\theta) & 0 \\ 0 & -i\sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 0 & e^{-i\lambda} \\ \end{bmatrix} \]

[How to apply]

FSim(pi, 0) | circuit([0, 1])
FSim(pi, 0) & [0, 1] | circuit

[Graph Representation]

           ┌──────────────┐
    q0:  ──┤0             ├──
           |  FSim(pi, 0) |
    q1:  ──┤1             ├──
           └──────────────┘

[QASM Representation]

fsim(pi, 0) q[0], q[1]

GPhase module-attribute

GPhase = BasicGate(*GATEINFO_MAP[gphase], is_original_gate=True)

Global Phase Gate.

[Matrix Representation]

\[ GPhase(\lambda) = \begin{bmatrix} e^{i\lambda} & 0 \\ 0 & e^{i\lambda} \\ \end{bmatrix} \]

[How to apply]

GPhase(pi) | circuit(0)
GPhase(pi) & 0 | circuit

[Graph Representation]

      ┌────────────┐
q0: ──┤ gphase(pi) ├──
      └────────────┘

[QASM Representation]

gphase(pi) q[0]

H module-attribute

H = BasicGate(*GATEINFO_MAP[h], is_original_gate=True)

Single-Qubit Hadamard Gate, which apply a pi rotation about the X and Z axis.

[Matrix Representation]

\[ H = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 1 \\ 1 & -1 \\ \end{bmatrix} \]

[How to apply]

H | circuit(0)
H & 0 | circuit

[Graph Representation]

      ┌───┐
q0: ──┤ h ├──
      └───┘

[QASM Representation]

h q[0]

Hy module-attribute

Hy = BasicGate(*GATEINFO_MAP[hy], is_original_gate=True)

Single-Qubit Hy Gate.

[Matrix Representation]

\[ Hy = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & -i \\ i & -1 \\ \end{bmatrix} \]

[How to apply]

Hy | circuit(0)
Hy & 0 | circuit

[Graph Representation]

      ┌────┐
q0: ──┤ hy ├──
      └────┘

[QASM Representation]

hy q[0]

ID module-attribute

ID = BasicGate(*GATEINFO_MAP[id], is_original_gate=True)

Single-Qubit Identity Gate.

[Matrix Representation]

\[ ID = \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ \end{bmatrix} \]

[How to apply]

ID | circuit(0)
ID & 0 | circuit

[Graph Representation]

      ┌────┐
q0: ──┤ id ├──
      └────┘

[QASM Representation]

id q[0]

Measure module-attribute

Measure = BasicGate(*GATEINFO_MAP[measure], is_original_gate=True)

The Quantum Measurement Gate.

[How to apply]

Measure | circuit(0)
Measure & 0 | circuit

[Graph Representation]

       ┌───┐
     ──┤ M ├──
       └───┘

[QASM Representation]

measure q[0] -> c[0]

MeasureX module-attribute

MeasureX = BasicGate(*GATEINFO_MAP[measurex], is_original_gate=True)

The Pauli X Measurement Gate.

[How to apply]

MeasureX | circuit(0)
MeasureX & 0 | circuit

[Graph Representation][not support]

       ┌────┐
     ──┤ Mx ├──
       └────┘

[QASM Representation][not support]

measurex q[0] -> c[0]

MeasureY module-attribute

MeasureY = BasicGate(*GATEINFO_MAP[measurey], is_original_gate=True)

The Quantum Measurement Gate.

[How to apply]

MeasureY | circuit(0)
MeasureY & 0 | circuit

[Graph Representation][not support]

       ┌────┐
     ──┤ My ├──
       └────┘

[QASM Representation][not support]

measurey q[0] -> c[0]

Phase module-attribute

Phase = BasicGate(*GATEINFO_MAP[phase], is_original_gate=True)

Single-Qubit Rotation Gate about Z axis.

[Matrix Representation]

\[ Phase(\lambda) = \begin{bmatrix} 1 & 0 \\ 0 & e^{i\lambda} \\ \end{bmatrix} \]

[How to apply]

Phase(pi) | circuit(0)
Phase(pi) & 0 | circuit

[Graph Representation]

      ┌───────────┐
q0: ──┤ phase(pi) ├──
      └───────────┘

[QASM Representation]

phase(pi) q[0]

RCCX module-attribute

RCCX = BasicGate(*GATEINFO_MAP[rccx], is_original_gate=True)

Simplified Double-Qubit Controlled X Gate, or Margolus gate

[Matrix Representation]

\[ RCCX_{q0,q1,q2} = \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & -1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ \end{bmatrix} \]

[How to apply]

RCCX | circuit([0, 1, 2])
RCCX & [0, 1, 2] | circuit

[Graph Representation]

    q0:  ─────■──────
              |
              |
    q1:  ─────■──────
              |
           ┌──────┐
    q2:  ──┤ rccx ├──
           └──────┘

[QASM Representation]

rccx q[0], q[1], q[2]

Reset module-attribute

Reset = BasicGate(*GATEINFO_MAP[reset], is_original_gate=True)

The Quantum Reset Gate.

[How to apply]

Reset | circuit(0)
Reset & 0 | circuit

[Graph Representation]

    ──┤0>──

[QASM Representation]

reset q[0]

Rx module-attribute

Rx = BasicGate(*GATEINFO_MAP[rx], is_original_gate=True)

Single-Qubit Rotation Gate, which apply an \(\lambda\) rotation about X axis.

[Matrix Representation]

\[ Rx(\lambda) = \begin{bmatrix} \cos(\frac{\lambda}{2}) & -i\sin(\frac{\lambda}{2}) \\ -i\sin(\frac{\lambda}{2}) & \cos(\frac{\lambda}{2}) \\ \end{bmatrix} \]

[How to apply]

Rx(pi) | circuit(0)
Rx(pi) & 0 | circuit

[Graph Representation]

      ┌────────┐
q0: ──┤ rx(pi) ├──
      └────────┘

[QASM Representation]

rx(pi) q[0]

Rxx module-attribute

Rxx = BasicGate(*GATEINFO_MAP[rxx], is_original_gate=True)

Double-Qubits X \(\otimes\) X Gate.

[Matrix Representation]

\[ Rxx_{q0,q1}(\lambda) = \begin{bmatrix} \cos(\frac{\lambda}{2}) & 0 & 0 & -i\sin(\frac{\lambda}{2}) \\ 0 & \cos(\frac{\lambda}{2}) & -i\sin(\frac{\lambda}{2}) & 0 \\ 0 & -i\sin(\frac{\lambda}{2}) & \cos(\frac{\lambda}{2}) & 0 \\ -i\sin(\frac{\lambda}{2}) & 0 & 0 & \cos(\frac{\lambda}{2}) \\ \end{bmatrix} \]

[How to apply]

Rxx(pi) | circuit([0, 1])
Rxx(pi) & [0, 1] | circuit

[Graph Representation]

           ┌───────────┐
    q0:  ──┤0          ├──
           |  rxx(pi)  |
    q1:  ──┤1          ├──
           └───────────┘

[QASM Representation]

rxx(pi) q[0], q[1]

Ry module-attribute

Ry = BasicGate(*GATEINFO_MAP[ry], is_original_gate=True)

Single-Qubit Rotation Gate, which apply an \(\lambda\) rotation about Y axis. [Matrix Representation]

\[ Ry(\lambda) = \begin{bmatrix} \cos(\frac{\lambda}{2}) & -\sin(\frac{\lambda}{2}) \\ \sin(\frac{\lambda}{2}) & \cos(\frac{\lambda}{2}) \\ \end{bmatrix} \]

[How to apply]

Ry(pi) | circuit(0)
Ry(pi) & 0 | circuit

[Graph Representation]

      ┌────────┐
q0: ──┤ ry(pi) ├──
      └────────┘

[QASM Representation]

ry(pi) q[0]

Ryy module-attribute

Ryy = BasicGate(*GATEINFO_MAP[ryy], is_original_gate=True)

Double-Qubits Y \(\otimes\) Y Gate.

[Matrix Representation]

\[ Ryy_{q0,q1}(\lambda) = \begin{bmatrix} \cos(\frac{\lambda}{2}) & 0 & 0 & i\sin(\frac{\lambda}{2}) \\ 0 & \cos(\frac{\lambda}{2}) & -i\sin(\frac{\lambda}{2}) & 0 \\ 0 & -i\sin(\frac{\lambda}{2}) & \cos(\frac{\lambda}{2}) & 0 \\ i\sin(\frac{\lambda}{2}) & 0 & 0 & \cos(\frac{\lambda}{2}) \\ \end{bmatrix} \]

[How to apply]

Ryy(pi) | circuit([0, 1])
Ryy(pi) & [0, 1] | circuit

[Graph Representation]

           ┌───────────┐
    q0:  ──┤0          ├──
           |  ryy(pi)  |
    q1:  ──┤1          ├──
           └───────────┘

[QASM Representation]

ryy(pi) q[0], q[1]

Rz module-attribute

Rz = BasicGate(*GATEINFO_MAP[rz], is_original_gate=True)

Single-Qubit Rotation Gate, which apply an \(\lambda\) rotation about Z axis.

[Matrix Representation]

\[ Rz(\lambda) = \begin{bmatrix} e^{\frac{-i\lambda}{2}} & 0 \\ 0 & e^{\frac{i\lambda}{2}} \\ \end{bmatrix} \]

[How to apply]

Rz(pi) | circuit(0)
Rz(pi) & 0 | circuit

[Graph Representation]

      ┌────────┐
q0: ──┤ rz(pi) ├──
      └────────┘

[QASM Representation]

rz(pi) q[0]

Rzx module-attribute

Rzx = BasicGate(*GATEINFO_MAP[rzx], is_original_gate=True)

Double-Qubits Z \(\otimes\) X Gate.

[Matrix Representation]

\[ Rzx_{q0,q1}(\lambda) = \begin{bmatrix} \cos(\frac{\lambda}{2}) & -i\sin(\frac{\lambda}{2}) & 0 & 0 \\ -i\sin(\frac{\lambda}{2}) & \cos(\frac{\lambda}{2}) & 0 & 0 \\ 0 & 0 & \cos(\frac{\lambda}{2}) & i\sin(\frac{\lambda}{2}) \\ 0 & 0 & i\sin(\frac{\lambda}{2}) & \cos(\frac{\lambda}{2}) \\ \end{bmatrix} \]

[How to apply]

Rzx(pi) | circuit([0, 1])
Rzx(pi) & [0, 1] | circuit

[Graph Representation]

           ┌───────────┐
    q0:  ──┤0          ├──
           |  rzx(pi)  |
    q1:  ──┤1          ├──
           └───────────┘

[QASM Representation]

rzx(pi) q[0], q[1]

Rzz module-attribute

Rzz = BasicGate(*GATEINFO_MAP[rzz], is_original_gate=True)

Double-Qubits Z \(\otimes\) Z Gate.

[Matrix Representation]

\[ Rzz_{q0,q1}(\lambda) = \begin{bmatrix} e^{\frac{-i\lambda}{2}} & 0 & 0 & 0 \\ 0 & e^{\frac{i\lambda}{2}} & 0 & 0 \\ 0 & 0 & e^{\frac{i\lambda}{2}} & 0 \\ 0 & 0 & 0 & e^{\frac{-i\lambda}{2}} \\ \end{bmatrix} \]

[How to apply]

Rzz(pi) | circuit([0, 1])
Rzz(pi) & [0, 1] | circuit

[Graph Representation]

           ┌───────────┐
    q0:  ──┤0          ├──
           |  rzz(pi)  |
    q1:  ──┤1          ├──
           └───────────┘

[QASM Representation]

rzz(pi) q[0], q[1]

S module-attribute

S = BasicGate(*GATEINFO_MAP[s], is_original_gate=True)

Single-Qubit S Gate, same as \(Z^0.5\) Gate. It produce a phase of pi/2.

[Matrix Representation]

\[ S = \begin{bmatrix} 1 & 0 \\ 0 & i \\ \end{bmatrix} \]

[How to apply]

S | circuit(0)
S & 0 | circuit

[Graph Representation]

      ┌───┐
q0: ──┤ s ├──
      └───┘

[QASM Representation]

s q[0]

SW module-attribute

SW = BasicGate(*GATEINFO_MAP[sw], is_original_gate=True)

Single-Qubit Sqrt-W Gate.

[Matrix Representation]

\[ SW = \begin{bmatrix} \frac{1}{\sqrt{2}} & -\sqrt{\frac{i}{2}} \\ \sqrt{\frac{-i}{2}} & \frac{1}{\sqrt{2}} \\ \end{bmatrix} \]

[How to apply]

SW | circuit(0)
SW & 0 | circuit

[Graph Representation]

      ┌────┐
q0: ──┤ sw ├──
      └────┘

[QASM Representation]

sw q[0]

SX module-attribute

SX = BasicGate(*GATEINFO_MAP[sx], is_original_gate=True)

Single-Qubit Sqrt-X Gate.

[Matrix Representation]

\[ SX = \frac{1}{2} \begin{bmatrix} 1 + i & 1 - i \\ 1 - i & 1 + i \\ \end{bmatrix} \]

[How to apply]

SX | circuit(0)
SX & 0 | circuit

[Graph Representation]

      ┌────┐
q0: ──┤ sx ├──
      └────┘

[QASM Representation]

sx q[0]

SX_dagger module-attribute

SX_dagger = BasicGate(*GATEINFO_MAP[sxdg], is_original_gate=True)

Single-Qubit Sqrt-X Dagger Gate.

[Matrix Representation]

\[ SXdg = \frac{1}{2} \begin{bmatrix} 1 - i & 1 + i \\ 1 + i & 1 - i \\ \end{bmatrix} \]

[How to apply]

SX_dagger | circuit(0)
SX_dagger & 0 | circuit

[Graph Representation]

      ┌──────┐
q0: ──┤ sxdg ├──
      └──────┘

[QASM Representation]

sxdg q[0]

SY module-attribute

SY = BasicGate(*GATEINFO_MAP[sy], is_original_gate=True)

Single-Qubit Sqrt-Y Gate.

[Matrix Representation]

\[ SY = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & -1 \\ 1 & 1 \\ \end{bmatrix} \]

[How to apply]

SY | circuit(0)
SY & 0 | circuit

[Graph Representation]

      ┌────┐
q0: ──┤ sy ├──
      └────┘

[QASM Representation]

sy q[0]

SY_dagger module-attribute

SY_dagger = BasicGate(*GATEINFO_MAP[sydg], is_original_gate=True)

Single-Qubit Sqrt-Y Dagger Gate.

[Matrix Representation]

\[ SYdg = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 1 \\ -1 & 1 \\ \end{bmatrix} \]

[How to apply]

SY_dagger | circuit(0)
SY_dagger & 0 | circuit

[Graph Representation]

      ┌──────┐
q0: ──┤ sydg ├──
      └──────┘

[QASM Representation]

sydg q[0]

S_dagger module-attribute

S_dagger = BasicGate(*GATEINFO_MAP[sdg], is_original_gate=True)

Single-Qubit S_dagger Gate, which produce a phase of -pi / 2.

[Matrix Representation]

\[ Sdg = \begin{bmatrix} 1 & 0 \\ 0 & -i \\ \end{bmatrix} \]

[How to apply]

S_dagger | circuit(0)
S_dagger & 0 | circuit

[Graph Representation]

      ┌─────┐
q0: ──┤ sdg ├──
      └─────┘

[QASM Representation]

sdg q[0]

Swap module-attribute

Swap = BasicGate(*GATEINFO_MAP[swap], is_original_gate=True)

Swap Gate.

[Matrix Representation]

\[ Swap_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \]

[How to apply]

Swap | circuit([0, 1])
Swap & [0, 1] | circuit

[Graph Representation]

    q0:  ─────X─────
              |
    q1:  ─────X─────

[QASM Representation]

swap q[0], q[1]

T module-attribute

T = BasicGate(*GATEINFO_MAP[t], is_original_gate=True)

Single-Qubit T Gate, which produce a \(\frac{pi}{4}\) phase. Its matrix equals \(Rz(\frac{pi}{4})\).

[Matrix Representation]

\[ T = \begin{bmatrix} 1 & 0 \\ 0 & \frac{1}{\sqrt{2}} + \frac{i}{\sqrt{2}} \\ \end{bmatrix} \]

[How to apply]

T | circuit(0)
T & 0 | circuit

[Graph Representation]

      ┌───┐
q0: ──┤ t ├──
      └───┘

[QASM Representation]

t q[0]

T_dagger module-attribute

T_dagger = BasicGate(*GATEINFO_MAP[tdg], is_original_gate=True)

Single-Qubit T Dagger Gate, which produce a -pi/4 phase.

[Matrix Representation]

\[ Tdg = \begin{bmatrix} 1 & 0 \\ 0 & \frac{1}{\sqrt{2}} - \frac{i}{\sqrt{2}} \\ \end{bmatrix} \]

[How to apply]

T_dagger | circuit(0)
T_dagger & 0 | circuit

[Graph Representation]

      ┌─────┐
q0: ──┤ tdg ├──
      └─────┘

[QASM Representation]

tdg q[0]

U1 module-attribute

U1 = BasicGate(*GATEINFO_MAP[u1], is_original_gate=True)

Single-Qubit Rotation Gate, which apply an \(\lambda\) rotation about Z axis.

[Matrix Representation]

\[ U1(\lambda) = \begin{bmatrix} 1 & 0 \\ 0 & e^{i\lambda} \\ \end{bmatrix} \]

[How to apply]

U1(pi) | circuit(0)
U1(pi) & 0 | circuit

[Graph Representation]

      ┌────────┐
q0: ──┤ u1(pi) ├──
      └────────┘

[QASM Representation]

u1(pi) q[0]

U2 module-attribute

U2 = BasicGate(*GATEINFO_MAP[u2], is_original_gate=True)

Single-Qubit Rotation Gate about X and Z axis.

[Matrix Representation]

\[ U2(\sigma, \lambda) = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & -e^{i\lambda} \\ e^{i\sigma} & e^{i(\lambda + \sigma)} \\ \end{bmatrix} \]

[How to apply]

U2(0, pi) | circuit(0)
U2(0, pi) & 0 | circuit

[Graph Representation]

      ┌───────────┐
q0: ──┤ u2(0, pi) ├──
      └───────────┘

[QASM Representation]

u2(0, pi) q[0]

U3 module-attribute

U3 = BasicGate(*GATEINFO_MAP[u3], is_original_gate=True)

Single-Qubit Rotation Gate with three Euler Angles.

[Matrix Representation]

\[ U3(\theta, \sigma, \lambda) = \begin{bmatrix} \cos(\frac{\theta}{2}) & -e^{i\lambda}\sin(\frac{\theta}{2}) \\ e^{i\sigma}\sin(\frac{\theta}{2}) & e^{i(\lambda + \sigma)}\cos(\frac{\theta}{2}) \\ \end{bmatrix} \]

[How to apply]

U3(0, pi, 1) | circuit(0)
U3(0, pi, 1) & 0 | circuit

[Graph Representation]

      ┌──────────────┐
q0: ──┤ u3(0, pi, 1) ├──
      └──────────────┘

[QASM Representation]

u3(0, pi, 1) q[0]

X module-attribute

X = BasicGate(*GATEINFO_MAP[x], is_original_gate=True)

Single-Qubit Pauli-X Gate.

[Matrix Representation]

\[ X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \\ \end{bmatrix} \]

[How to apply]

X | circuit(0)
X & 0 | circuit

[Graph Representation]

       ┌───┐
     ──┤ x ├──
       └───┘

[QASM Representation]

x q[0]

Y module-attribute

Y = BasicGate(*GATEINFO_MAP[y], is_original_gate=True)

Single-Qubit Pauli-Y Gate.

[Matrix Representation]

\[ Y = \begin{bmatrix} 0 & -i \\ i & 0 \\ \end{bmatrix} \]

[How to apply]

Y | circuit(0)
Y & 0 | circuit

[Graph Representation]

      ┌───┐
q0: ──┤ y ├──
      └───┘

[QASM Representation]

y q[0]

Z module-attribute

Z = BasicGate(*GATEINFO_MAP[z], is_original_gate=True)

Single-Qubit Pauli-Z Gate.

[Matrix Representation]

\[ Z = \begin{bmatrix} 1 & 0 \\ 0 & -1 \\ \end{bmatrix} \]

[How to apply]

Z | circuit(0)
Z & 0 | circuit

[Graph Representation]

      ┌───┐
q0: ──┤ z ├──
      └───┘

[QASM Representation]

z q[0]

iSwap module-attribute

iSwap = BasicGate(*GATEINFO_MAP[iswap], is_original_gate=True)

iSwap Gate, a double-qubit XX + YY Gate.

[Matrix Representation]

\[ iSwap_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & i & 0 \\ 0 & i & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \]

[How to apply]

iSwap | circuit([0, 1])
iSwap & [0, 1] | circuit

[Graph Representation]

           ┌───────────┐
    q0:  ──┤0          ├──
           |   iswap   |
    q1:  ──┤1          ├──
           └───────────┘

[QASM Representation]

iswap q[0], q[1]

iSwap_dagger module-attribute

iSwap_dagger = BasicGate(*GATEINFO_MAP[iswapdg], is_original_gate=True)

iSwap Dagger Gate.

[Matrix Representation]

\[ iSwapdg_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & -i & 0 \\ 0 & -i & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \]

[How to apply]

iSwap_dagger | circuit([0, 1])
iSwap_dagger & [0, 1] | circuit

[Graph Representation]

           ┌───────────┐
    q0:  ──┤0          ├──
           |  iswapdg  |
    q1:  ──┤1          ├──
           └───────────┘

[QASM Representation]

iswap_dagger q[0], q[1]

sqiSwap module-attribute

sqiSwap = BasicGate(*GATEINFO_MAP[sqiswap], is_original_gate=True)

Swap Gate.

[Matrix Representation]

\[ sqiSwap_{q0,q1} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \frac{1}{\sqrt{2}} & \frac{i}{\sqrt{2}} & 0 \\ 0 & \frac{i}{\sqrt{2}} & \frac{1}{\sqrt{2}} & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \]

[How to apply]

sqiSwap | circuit([0, 1])
sqiSwap & [0, 1] | circuit

[Graph Representation]

           ┌───────────┐
    q0:  ──┤0          ├──
           |  sqiswap  |
    q1:  ──┤1          ├──
           └───────────┘

[QASM Representation]

sqiswap q[0], q[1]

BasicGate

BasicGate(controls: int, targets: int, params: int, type_: GateType, matrix_type: MatrixType = MatrixType.normal, pargs: list = [], precision: str = 'double', is_original_gate: bool = False)

Bases: object

the abstract SuperClass of all basic quantum gate

All basic quantum gate described in the framework have some common attributes and some common functions which defined in this class

Attributes:

  • type(GateType, (read only) –

    gate's type described by GateType

  • matrix_type(MatrixType, (read only) –

    gate matrix's type described by MatrixType

  • precision(str) (read only) –

    The gate's precision, one of [double, single]

  • qasm_name(str, (read only) –

    gate's name in the OpenQASM 2.0

  • matrix(np.array) (read only) –

    the unitary matrix of the quantum gate act on qubits

  • target_matrix(np.array) (read only) –

    the unitary matrix of the quantum gate act on targets

  • targets(int) (read only) –

    the number of the target bits of the gate

  • targs(list<int>) (read only) –

    the list of the index of target bits in the circuit

  • targ(int, (read only) –

    the first object of targs

  • controls(int) (read only) –

    the number of the control bits of the gate

  • cargs(list<int>) (read only) –

    the list of the index of control bits in the circuit

  • carg(int, (read only) –

    the first object of cargs

  • params(list) (read only) –

    the number of the parameter of the gate

  • pargs(list) (read only) –

    the list of the parameter

  • parg(read (only) –

    the first object of pargs

Parameters:

  • controls (int) –

    The number of control qubits

  • targets (int) –

    The number of target qubits

  • params (int) –

    The number of gate's parameters

  • type_ (GateType) –

    The gate's type

  • matrix_type (MatrixType, default: normal ) –

    The gate matrix's type. Defaults to MatrixType.normal.

  • pargs (list, default: [] ) –

    The gate's parameters. Defaults to [].

  • precision (str, default: 'double' ) –

    The gate's precison, one of [double, single]. Defaults to "double".

  • is_original_gate (bool, default: False ) –

    Whether is the initial quantum gate, such as H. Defaults to False.

Source code in QuICT/core/gate/gate.py
def __init__(
    self,
    controls: int,
    targets: int,
    params: int,
    type_: GateType,
    matrix_type: MatrixType = MatrixType.normal,
    pargs: list = [],
    precision: str = "double",
    is_original_gate: bool = False
):
    """
    Args:
        controls (int): The number of control qubits
        targets (int): The number of target qubits
        params (int): The number of gate's parameters
        type_ (GateType): The gate's type
        matrix_type (MatrixType, optional): The gate matrix's type. Defaults to MatrixType.normal.
        pargs (list, optional): The gate's parameters. Defaults to [].
        precision (str, optional): The gate's precison, one of [double, single]. Defaults to "double".
        is_original_gate (bool, optional): Whether is the initial quantum gate, such as H. Defaults to False.
    """
    assert isinstance(controls, int), TypeError("BasicGate.controls", "int", type(controls))
    assert isinstance(targets, int), TypeError("BasicGate.targets", "int", type(targets))
    assert isinstance(params, int), TypeError("BasicGate.params", "int", type(params))
    self._targets = targets
    self._targs = []    # list of int

    self._controls = controls
    self._cargs = []    # list of int

    self._params = params
    self._pargs = []
    self._symbol_gate = False
    self._required_grad = None
    self._variables = 0
    self._free_symbols = []
    self._symbol_pargs = dict()
    if self._params > 0:
        if len(pargs) > 0:
            pargs = self.permit_element(pargs)
        else:
            pargs = GATE_ARGS_MAP[type_]
        self._pargs = pargs    # list of float/..

    assert isinstance(type_, GateType), TypeError("BasicGate.type", "GateType", type(type_))
    assert isinstance(matrix_type, MatrixType), TypeError("BasicGate.matrixtype", "MatrixType", type(type_))
    self._type = type_
    self._matrix_type = matrix_type

    assert precision in ["double", "single"], \
        ValueError("BasicGate.precision", "not within [double, single]", precision)
    self._precision = precision
    self._matrix = None
    self._target_matrix = None
    self._grad_matrix = None
    self._qasm_name = str(type_.name)
    self._is_matrix_update = False
    self._is_original = is_original_gate
__and__
__and__(targets)

deal the operator '&'

Use the syntax "gate & int" or "gate & list" to set gate's attribute. Special uses when in composite gate's context.

Some Examples are like this: X & 1 CX & [0, 1]

Note that the order of qubits is that control bits first and target bits followed.

Parameters:

  • targets

    the targets the gate acts on, it can have following form, 1) int 2) list

Raise

TypeError: the type of targets is wrong

Source code in QuICT/core/gate/gate.py
def __and__(self, targets):
    """deal the operator '&'

    Use the syntax "gate & int" or "gate & list<int>" to set gate's attribute.
    Special uses when in composite gate's context.

    Some Examples are like this:
    X       & 1
    CX      & [0, 1]

    Note that the order of qubits is that control bits first
    and target bits followed.

    Args:
        targets: the targets the gate acts on, it can have following form,
            1) int
            2) list<int>

    Raise:
        TypeError: the type of targets is wrong
    """
    if isinstance(targets, (np.int32, np.int64)):
        targets = int(targets)

    if isinstance(targets, int):
        targets = [targets]

    if not isinstance(targets, list):
        raise TypeError("BasicGate.&", "int or list<int>", type(targets))

    assert len(targets) == self.controls + self.targets, \
        GateQubitAssignedError("The qubits number should equal to the quantum gate.")

    if self._is_original:
        _gate = self.copy()
    else:
        _gate = self

    _gate.cargs = targets[:_gate.controls]
    _gate.targs = targets[_gate.controls:]

    if CGATE_LIST:
        target_cgate = CGATE_LIST[-1]
        if not CGATE_HOLD[target_cgate.name]:
            CGATE_HOLD[target_cgate.name] = True
            target_cgate.append(_gate)
            CGATE_HOLD[target_cgate.name] = False

    return _gate
__call__
__call__(*args, required_grad=None)

give parameters for the gate, and give parameters by "()", and parameters should be one of int/float/complex

Some Examples are like this:

Rz(np.pi / 2) U3(np.pi / 2, 0, 0)

Important: There is no parameters for some quantum gate.

Returns:

  • BasicGate

    the gate after filled by parameters

Source code in QuICT/core/gate/gate.py
def __call__(self, *args, required_grad=None):
    """ give parameters for the gate, and give parameters by "()", and parameters should be one of int/float/complex

    Some Examples are like this:

    Rz(np.pi / 2)
    U3(np.pi / 2, 0, 0)

    *Important*: There is no parameters for some quantum gate.

    Returns:
        BasicGate: the gate after filled by parameters
    """
    assert len(args) == self.params, \
        GateParametersAssignedError("The number of given parameters not matched the quantum gate.")

    if self._is_original:
        _gate = self.copy()
    else:
        _gate = self

    _gate.pargs = list(args)

    if required_grad is None:
        _gate._required_grad = _gate._symbol_gate
    else:
        if required_grad and not _gate.symbol_gate:
            warnings.warn("Cannot calculate gradients for non-symbol gates.")
            _gate._required_grad = False
        else:
            if not required_grad:
                _gate._variables = 0
            _gate._required_grad = required_grad
    return _gate
__or__
__or__(targets)

deal the operator '|'

Use the syntax "gate | circuit" or "gate | Composite Gate" to add the gate into the circuit or composite gate Some Examples are like this:

X | circuit CX | circuit([0, 1]) Measure | CompositeGate

Note that the order of qubits is that control bits first and target bits followed.

Parameters:

  • targets

    the targets the gate acts on, it can have following form, 1) Circuit 2) CompositeGate

Raise

TypeError: the type of other is wrong

Source code in QuICT/core/gate/gate.py
def __or__(self, targets):
    """deal the operator '|'

    Use the syntax "gate | circuit" or "gate | Composite Gate"
    to add the gate into the circuit or composite gate
    Some Examples are like this:

    X       | circuit
    CX      | circuit([0, 1])
    Measure | CompositeGate

    Note that the order of qubits is that control bits first
    and target bits followed.

    Args:
        targets: the targets the gate acts on, it can have following form,
            1) Circuit
            2) CompositeGate

    Raise:
        TypeError: the type of other is wrong
    """
    try:
        targets.append(self)
    except Exception as e:
        raise GateAppendError(f"Failure to append gate {self} to targets, due to {e}")
build_gate
build_gate(qidxes: list = None)

Gate Decomposition, which divided the current gate with a set of small gates.

Source code in QuICT/core/gate/gate.py
def build_gate(self, qidxes: list = None):
    """ Gate Decomposition, which divided the current gate with a set of small gates. """
    if self.type == GateType.cu3:
        cgate = ComplexGateBuilder.build_gate(self.type, self.parg, self.matrix)
    else:
        gate_list = ComplexGateBuilder.build_gate(self.type, self.pargs)
        if gate_list is None:
            return gate_list

        cgate = self._cgate_generator_from_build_gate(gate_list)

    gate_args = self.cargs + self.targs if qidxes is None else qidxes
    if len(gate_args) > 0:
        cgate & gate_args

    return cgate
commutative
commutative(goal: BasicGate, eps=1e-07)

decide whether gate is commutative with another gate

note when the gate is special gates like Unitary, Permutation, Measure and so on, return False.

Parameters:

  • goal(BasicGate)

    the target gate

  • eps(float)

    the precision of comparision

Return

bool: True if commutative

Source code in QuICT/core/gate/gate.py
def commutative(self, goal: BasicGate, eps=1e-7):
    """ decide whether gate is commutative with another gate

    note when the gate is special gates like Unitary, Permutation, Measure and so on, return False.

    Args:
        goal(BasicGate): the target gate
        eps(float): the precision of comparision

    Return:
        bool: True if commutative
    """
    # Check the affected qubits
    self_controls = set(self.cargs)
    self_targets = set(self.targs)
    goal_controls = set(goal.cargs)
    goal_targets = set(goal.targs)
    # If the affected qubits of the gates are completely different, they must commute
    self_qubits = self_controls | self_targets
    goal_qubits = goal_controls | goal_targets
    if len(self_qubits & goal_qubits) == 0:
        return True

    # Ignore all the special gates except for the unitary gates due to the difficulty of getting the matrices
    if (
        (self.is_special() and self.type != GateType.unitary) or
        (goal.is_special() and self.type != GateType.unitary)
    ):
        return False

    # It means commuting that any of the target matrices is close to identity
    if (self.is_identity() or goal.is_identity()):
        return True

    # Check the target matrices of the gates
    A = self.target_matrix
    B = goal.target_matrix
    # For gates whose number of target qubits is 1, optimized judgment could be used
    if self.targets == 1 and goal.targets == 1:
        # Diagonal target gates commutes with the control qubits
        if (
            (len(self_controls & goal_targets) > 0 and not goal.is_diagonal()) or
            (len(goal_controls & self_targets) > 0 and not self.is_diagonal())
        ):
            return False
        # Compute the target matrix commutation
        if (
            len(goal_targets & self_targets) > 0 and
            not np.allclose(A.dot(B), B.dot(A), rtol=eps, atol=eps)
        ):
            return False

        return True
    # Otherwise, we need to calculate the matrix commutation directly
    else:
        # Collect all the affected qubits and create the converter to the minimal qubits
        qubits = self_qubits | goal_qubits
        qubits_dict = {}
        for idx, x in enumerate(qubits):
            qubits_dict[x] = idx
        # Create two masked gates whose affected qubits are the minimal ones
        self_masked = self.cargs + self.targs
        for i in range(len(self_masked)):
            self_masked[i] = qubits_dict[self_masked[i]]
        goal_masked = goal.cargs + goal.targs
        for i in range(len(goal_masked)):
            goal_masked[i] = qubits_dict[goal_masked[i]]
        # Compute the matrix commutation
        self_matrix = self.expand(list(qubits))
        goal_matrix = goal.expand(list(qubits))
        return np.allclose(self_matrix.dot(goal_matrix), goal_matrix.dot(self_matrix), rtol=eps, atol=eps)
copy
copy()

return a copy of this gate

Returns:

Source code in QuICT/core/gate/gate.py
def copy(self):
    """ return a copy of this gate

    Returns:
        gate(BasicGate): a copy of this gate
    """
    pargs = [parg for parg in self.pargs]
    gate = BasicGate(
        self.controls, self.targets, self.params, self.type,
        self.matrix_type, pargs, self.precision
    )

    if len(self.targs) > 0:
        gate.targs = self.targs[:]

    if self.controls > 0 and len(self.cargs) > 0:
        gate.cargs = self.cargs[:]

    gate._required_grad = self._required_grad
    gate._free_symbols = self._free_symbols.copy()
    gate._symbol_pargs = self._symbol_pargs.copy()
    return gate
expand
expand(qubits: Union[int, list], device: str = 'CPU') -> bool

expand self matrix into the circuit's unitary linear space. If input qubits is integer, please make sure the indexes of current gate is within [0, qubits).

Parameters:

  • qubits (Union[int, list]) –

    the total number of qubits of the target circuit or the indexes of expand qubits.

Source code in QuICT/core/gate/gate.py
def expand(self, qubits: Union[int, list], device: str = "CPU") -> bool:
    """ expand self matrix into the circuit's unitary linear space. If input qubits is integer, please make sure
    the indexes of current gate is within [0, qubits).

    Args:
        qubits Union[int, list]: the total number of qubits of the target circuit or the indexes of expand qubits.
    """
    if isinstance(qubits, int):
        qubits = list(range(qubits))

    qubits_num = len(qubits)
    if qubits_num == self.controls + self.targets:
        return self.matrix

    assert qubits_num > self.controls + self.targets, GateQubitAssignedError(
        "The expand qubits' num should >= gate's qubits num."
    )
    gate_args = self.cargs + self.targs
    if len(gate_args) == 0:     # Deal with not assigned quantum gate
        gate_args = [qubits[i] for i in range(self.controls + self.targets)]

    updated_args = [qubits.index(garg) for garg in gate_args]
    return matrix_product_to_circuit(self.matrix, updated_args, qubits_num, device)
init_pargs
init_pargs(symbols: list, values: Union[list, ndarray])

Initialize the trainable parameters of the gate.

Parameters:

  • symbols (list) –

    The symbols that needs to be assigned values.

  • values (Union[list, ndarray]) –

    The values to be assigned.

Source code in QuICT/core/gate/gate.py
def init_pargs(self, symbols: list, values: Union[list, np.ndarray]):
    """Initialize the trainable parameters of the gate.

    Args:
        symbols (list): The symbols that needs to be assigned values.
        values (Union[list, np.ndarray]): The values to be assigned.
    """
    assert self._symbol_gate, "Only the parameters of symbol gates can be initialized."
    if not isinstance(symbols, list):
        symbols = [symbols]
    if not isinstance(values, (list, np.ndarray)):
        values = [values]
    assert len(set(symbols)) == len(symbols), "There cannot be duplicates in symbols list."
    assert len(symbols) == len(values), "The symbols list and values list must match one-to-one."

    for symbol, value in zip(symbols, values):
        if not isinstance(value, numbers.Number):
            raise TypeError("basicGate.pargs", "Number", type(value))
        if symbol in self._free_symbols:
            for key in self._symbol_pargs.keys():
                if symbol == key.symbol:
                    self._symbol_pargs[key] = value * key.multiplier
inverse
inverse()

the inverse of the quantum gate, if there is no inverse gate, return itself.

Return

BasicGate: the inverse of the gate

Source code in QuICT/core/gate/gate.py
def inverse(self):
    """ the inverse of the quantum gate, if there is no inverse gate, return itself.

    Return:
        BasicGate: the inverse of the gate
    """
    if self.symbol_gate:
        assert self.assigned_value, "The symbol gate must be assigned values before calculation."
        pargs = []
        for i in range(self.params):
            pargs.append(self.symbol_pargs[self.pargs[i]])
    else:
        pargs = self.pargs
    inverse_gargs, inverse_pargs = InverseGate.get_inverse_gate(self.type, pargs)

    # Deal with inverse_gargs
    if inverse_gargs is None:
        return self

    inverse_gate = gate_builder(inverse_gargs, params=inverse_pargs)
    gate_args = self.cargs + self.targs
    if len(gate_args) > 0:
        inverse_gate & gate_args

    return inverse_gate
is_clifford
is_clifford() -> bool

judge whether gate's matrix is a Clifford gate

Returns:

  • bool ( bool ) –

    True if gate's matrix is a Clifford gate

Source code in QuICT/core/gate/gate.py
def is_clifford(self) -> bool:
    """ judge whether gate's matrix is a Clifford gate

    Returns:
        bool: True if gate's matrix is a Clifford gate
    """
    return self.type in CLIFFORD_GATE_SET
is_control_single
is_control_single() -> bool

judge whether gate has one control bit and one target bit

Returns:

  • bool ( bool ) –

    True if it is has one control bit and one target bit

Source code in QuICT/core/gate/gate.py
def is_control_single(self) -> bool:
    """ judge whether gate has one control bit and one target bit

    Returns:
        bool: True if it is has one control bit and one target bit
    """
    return self.controls == 1 and self.targets == 1
is_diagonal
is_diagonal() -> bool

judge whether gate's matrix is diagonal

Returns:

  • bool ( bool ) –

    True if gate's matrix is diagonal

Source code in QuICT/core/gate/gate.py
def is_diagonal(self) -> bool:
    """ judge whether gate's matrix is diagonal

    Returns:
        bool: True if gate's matrix is diagonal
    """
    return self.matrix_type in [MatrixType.diagonal, MatrixType.control]
is_identity
is_identity() -> bool

judge whether gate's matrix is identity matrix

Returns:

  • bool ( bool ) –

    True if gate's matrix is identity

Source code in QuICT/core/gate/gate.py
def is_identity(self) -> bool:
    """ judge whether gate's matrix is identity matrix

    Returns:
        bool: True if gate's matrix is identity
    """
    return self.type == GateType.id or self.matrix_type == MatrixType.identity
is_pauli
is_pauli() -> bool

judge whether gate's matrix is a Pauli gate

Returns:

  • bool ( bool ) –

    True if gate's matrix is a Pauli gate

Source code in QuICT/core/gate/gate.py
def is_pauli(self) -> bool:
    """ judge whether gate's matrix is a Pauli gate

    Returns:
        bool: True if gate's matrix is a Pauli gate
    """
    return self.type in PAULI_GATE_SET
is_single
is_single() -> bool

judge whether gate is a one qubit gate(excluding special gate like measure, reset, custom and so on)

Returns:

  • bool ( bool ) –

    True if it is a one qubit gate

Source code in QuICT/core/gate/gate.py
def is_single(self) -> bool:
    """ judge whether gate is a one qubit gate(excluding special gate like measure, reset, custom and so on)

    Returns:
        bool: True if it is a one qubit gate
    """
    return self.targets + self.controls == 1
is_special
is_special() -> bool

judge whether gate's is special gate, which is one of [Measure, Reset, Barrier, Perm, ...]

Returns:

  • bool ( bool ) –

    True if gate's matrix is special

Source code in QuICT/core/gate/gate.py
def is_special(self) -> bool:
    """ judge whether gate's is special gate, which is one of
    [Measure, Reset, Barrier, Perm, ...]

    Returns:
        bool: True if gate's matrix is special
    """
    return self.matrix_type == MatrixType.special
permit_element
permit_element(element)

judge whether the type of a parameter is int/float/complex

for a quantum gate, the parameter should be int/float/complex

Parameters:

  • element

    the element to be judged

Returns:

  • bool

    True if the type of element is int/float/complex

Source code in QuICT/core/gate/gate.py
def permit_element(self, element):
    """ judge whether the type of a parameter is int/float/complex

    for a quantum gate, the parameter should be int/float/complex

    Args:
        element: the element to be judged

    Returns:
        bool: True if the type of element is int/float/complex
    """
    if not isinstance(element, list):
        element = [element]

    assert len(element) == self.params, \
        ValueError("BasicGate.pargs:length", f"equal to gate's parameter number {self._params}", len(element))

    for i in range(self._params):
        if not isinstance(element[i], (numbers.Number, str, Parameter)):
            raise TypeError("basicGate.pargs", "Number/str/Parameter", type(element[i]))
        if isinstance(element[i], str):
            element[i] = Parameter(element[i])
        if isinstance(element[i], Parameter):
            self._symbol_gate = True
            if element[i].symbol not in self._free_symbols:
                self._free_symbols.append(element[i].symbol)
            self._variables += 1
            self._symbol_pargs[element[i]] = None
    return element
qasm
qasm(targs: list = None)

generator OpenQASM string for the gate

Return

string: the OpenQASM 2.0 describe of the gate

Source code in QuICT/core/gate/gate.py
def qasm(self, targs: list = None):
    """ generator OpenQASM string for the gate

    Return:
        string: the OpenQASM 2.0 describe of the gate
    """
    if self.type in [GateType.perm, GateType.perm_fx]:
        raise QASMError(f"This gate do not support qasm, {self.type}")
    if self.symbol_gate and not self.assigned_value:
        raise QASMError("Symbol gates must be assigned values before generating qasm.")

    qasm_string = self.qasm_name
    if self.params > 0:
        params = []
        for parg in self.pargs:
            parg = self.symbol_pargs[parg] if isinstance(parg, Parameter) else parg
            params.append(str(parg))
        params_string = "(" + ", ".join(params) + ")"

        qasm_string += params_string

    qubit_idxes = self.cargs + self.targs if targs is None else targs
    ctargs = [f"q[{ctarg}]" for ctarg in qubit_idxes]
    ctargs_string = " " + ', '.join(ctargs) + ";\n"
    qasm_string += ctargs_string

    return qasm_string

ComplexGateBuilder

The class of all build_gate functions for BasicGate.

build_gate classmethod
build_gate(gate_type, parg, gate_matrix=None)

Gate Decomposition, divided the current gate with a set of small gates

Parameters:

  • gate_type (GateType) –

    The type of Quantum Gate.

  • parg (list) –

    The parameters of Quantum Gate.

  • gate_matrix (_type_, default: None ) –

    The matrix of Quantum Gate, only use for CU3. Defaults to None.

Returns:

  • List

    List of gate_info(gate_type, qubit_index, parameters)

Source code in QuICT/core/gate/utils/gate_matrix.py
@classmethod
def build_gate(cls, gate_type, parg, gate_matrix=None):
    """ Gate Decomposition, divided the current gate with a set of small gates

    Args:
        gate_type (GateType): The type of Quantum Gate.
        parg (list): The parameters of Quantum Gate.
        gate_matrix (_type_, optional): The matrix of Quantum Gate, only use for CU3. Defaults to None.

    Returns:
        List: List of gate_info(gate_type, qubit_index, parameters)
    """
    if gate_type == GateType.cu3:
        cgate = cls.build_unitary(gate_matrix)
    elif gate_type == GateType.cu1:
        cgate = cls.build_cu1(parg[0])
    elif gate_type == GateType.swap:
        cgate = cls.build_swap()
    elif gate_type == GateType.ccx:
        cgate = cls.build_ccx()
    elif gate_type == GateType.ccz:
        cgate = cls.build_ccz()
    elif gate_type == GateType.ccrz:
        cgate = cls.build_ccrz(parg[0])
    elif gate_type == GateType.cswap:
        cgate = cls.build_cswap()
    elif gate_type == GateType.iswap:
        cgate = cls.build_iswap()
    elif gate_type == GateType.iswapdg:
        cgate = cls.build_iswapdg()
    elif gate_type == GateType.sqiswap:
        cgate = cls.build_sqiswap()
    elif gate_type == GateType.rccx:
        cgate = cls.build_rccx()
    elif gate_type == GateType.rxy:
        cgate = cls.build_rxy(parg)
    elif gate_type in [GateType.xy, GateType.xy2p, GateType.xy2m]:
        cgate = cls.build_xy(gate_type, parg[0])
    else:
        return None

    return cgate

CompositeGate

CompositeGate(name: str = None, gates: List[BasicGate, CompositeGate] = None, precision: str = 'double')

Bases: CircuitBased

Implement a group of gate

Parameters:

  • name (str, default: None ) –

    the name of the composite gate. Defaults to None.

  • gates (List[BasicGate, CompositeGate], default: None ) –

    gates within this composite gate. Defaults to None.

Source code in QuICT/core/gate/composite_gate.py
def __init__(
    self,
    name: str = None,
    gates: List[BasicGate, CompositeGate] = None,
    precision: str = "double"
):
    """
    Args:
        name (str, optional): the name of the composite gate. Defaults to None.
        gates (List[BasicGate, CompositeGate], optional): gates within this composite gate. Defaults to None.
    """
    super().__init__(name, precision=precision)
    if gates is not None:
        for gate in gates:
            if isinstance(gate, CompositeGate):
                self.extend(gate)
            else:
                self.append(gate)
    self._ancilla_qubits = []
__and__
__and__(targets: Union[int, list])

assign indexes for the composite gates

Parameters:

  • targets ([int / list[int]]) –

    qubit describe

Source code in QuICT/core/gate/composite_gate.py
def __and__(self, targets: Union[int, list]):
    """ assign indexes for the composite gates

    Args:
        targets ([int/list[int]]): qubit describe
    """
    if isinstance(targets, int):
        targets = [targets]

    self._qubit_indexes_validation(targets)
    context_hold = False
    if CGATE_LIST:
        target_cgate = CGATE_LIST[-1]
        global CGATE_HOLD
        if not CGATE_HOLD[target_cgate.name]:
            CGATE_HOLD[target_cgate.name] = True
            context_hold = True

    self._gates.remap(targets)

    if CGATE_LIST:
        if context_hold:
            target_cgate.extend(self)
            CGATE_HOLD[target_cgate.name] = False

    return self
__getitem__
__getitem__(item)

get gates from this composite gate

Parameters:

  • item (int / slice) –

    slice passed in.

Return

[BasicGates]: the gates

Source code in QuICT/core/gate/composite_gate.py
def __getitem__(self, item):
    """ get gates from this composite gate

    Args:
        item (int/slice): slice passed in.

    Return:
        [BasicGates]: the gates
    """
    gate = self._gates.gate_list[item]

    return gate.copy()
__or__
__or__(targets)

deal the operator '|'

Use the syntax "CompositeGate | circuit", "CompositeGate | CompositeGate" to add the gate of gateSet into the circuit

Note that the order of qubits is that control bits first and target bits followed.

Parameters:

  • targets

    the targets the gate acts on, it can have the following form, 1) Circuit 2) CompositeGate

Raise: TypeError: the type of other is wrong

Source code in QuICT/core/gate/composite_gate.py
def __or__(self, targets):
    """ deal the operator '|'

    Use the syntax "CompositeGate | circuit", "CompositeGate | CompositeGate"
    to add the gate of gateSet into the circuit

    Note that the order of qubits is that control bits first
    and target bits followed.

    Args:
        targets: the targets the gate acts on, it can have the following form,
            1) Circuit
            2) CompositeGate
    Raise:
        TypeError: the type of other is wrong
    """
    try:
        targets.extend(self)
    except Exception as e:
        raise CompositeGateAppendError(f"Failure to append current CompositeGate, due to {e}.")
__xor__
__xor__(targets)

deal the operator '^'

Use the syntax "gateSet ^ circuit", "gateSet ^ gateSet" to add the gate of gateSet's inverse into the circuit

Note that the order of qubits is that control bits first and target bits followed.

Parameters:

  • targets

    the targets the gate acts on, it can have the following form, 1) Circuit 2) CompositeGate

Raise: TypeError: the type of other is wrong

Source code in QuICT/core/gate/composite_gate.py
def __xor__(self, targets):
    """deal the operator '^'

    Use the syntax "gateSet ^ circuit", "gateSet ^ gateSet"
    to add the gate of gateSet's inverse into the circuit

    Note that the order of qubits is that control bits first
    and target bits followed.

    Args:
        targets: the targets the gate acts on, it can have the following form,
            1) Circuit
            2) CompositeGate
    Raise:
        TypeError: the type of other is wrong
    """
    try:
        targets.extend(self.inverse())
    except Exception as e:
        raise CompositeGateAppendError(f"Failure to append the inverse of current CompositeGate, due to {e}.")
append
append(gate: BasicGate)

Add a quantum gate to current CompositeGate.

Parameters:

  • gate (BasicGate) –

    The quantum gate need to append

Source code in QuICT/core/gate/composite_gate.py
def append(self, gate: BasicGate):
    """ Add a quantum gate to current CompositeGate.

    Args:
        gate (BasicGate): The quantum gate need to append
    """
    if type(gate).__name__ in ["Multiply", "DataSwitch", "DeviceTrigger", "SpecialGate"]:
        self._gates.append(gate)
        return

    if not isinstance(gate, BasicGate):
        raise TypeError("CompositeGate.append", "BasicGate", type(gate))

    if self._pointer is not None:
        gate_args = gate.controls + gate.targets
        assert len(self._pointer) == gate_args, \
            GateQubitAssignedError(f"{gate.type} need {gate_args} indexes, but given {len(self._pointer)}")

        qubit_index = self._pointer[:]
        copy_gate = gate.copy() & qubit_index
    else:
        qubit_index = gate.cargs + gate.targs
        if not qubit_index:
            raise GateQubitAssignedError(f"{gate.type} need qubit indexes to add into Composite Gate.")

        self._qubit_indexes_validation(qubit_index)
        copy_gate = gate.copy()

    self._gates.append(copy_gate)
    self._pointer = None
clean
clean()

Remove all quantum gates in current Circuit.

Source code in QuICT/core/gate/composite_gate.py
def clean(self):
    """ Remove all quantum gates in current Circuit. """
    self._gates.reset()
    self._pointer = None
copy
copy() -> CompositeGate

Copy current CompositeGate.

Source code in QuICT/core/gate/composite_gate.py
def copy(self) -> CompositeGate:
    """ Copy current CompositeGate. """
    _gates = CompositeGate(gates=self.gates)
    _gates.name = self.name

    return _gates
depth
depth()

The depth of the circuit.

Returns:

  • int

    the depth

Source code in QuICT/core/gate/composite_gate.py
def depth(self):
    """ The depth of the circuit.

    Returns:
        int: the depth
    """
    return self._gates.depth(max(self.qubits) + 1)
exp2
exp2(n: int) -> CompositeGate

Get a Composite that applys current Composite gate 2^n times

Parameters:

  • n (int) –

    The exponent.

Returns:

  • CompositeGate ( CompositeGate ) –

    a gate that apply the original gate 2^n times.

Source code in QuICT/core/gate/composite_gate.py
def exp2(self, n: int) -> CompositeGate:
    """ Get a Composite that applys current Composite gate 2^n times

    Args:
        n (int): The exponent.

    Returns:
        CompositeGate: a gate that apply the original gate 2^n times.
    """
    if n < 0:
        raise ValueError("The exponent can't be smaller than 0.")

    _gates = CompositeGate(f"{self.name}^(2^{n})")

    for _ in range(1 << n):
        for gate in self.gates:
            gate | _gates

    _gates.set_ancilla(self.ancilla_qubits)
    return _gates
extend
extend(gates: CompositeGate)

Add a CompositeGate to current CompositeGate.

Parameters:

Source code in QuICT/core/gate/composite_gate.py
def extend(self, gates: CompositeGate):
    """ Add a CompositeGate to current CompositeGate.

    Args:
        gates (CompositeGate): The given CompositeGate
    """
    if gates.size() == 0:
        return

    if self._pointer is not None:
        gate_args = gates.width()
        assert gate_args <= len(self._pointer), GateQubitAssignedError(
            f"{gates.name} need at least {gate_args} indexes, but given {len(self._pointer)}"
        )
        if gate_args == len(self._pointer):
            gate_qidxes = self._pointer[:]
        else:
            gate_qidxes = [self._pointer[qidx] for qidx in gates.qubits]

        copy_gates = gates.copy() & gate_qidxes
    else:
        self._qubit_indexes_validation(gates.qubits)
        copy_gates = gates.copy()

    self._gates.extend(copy_gates)
    self._pointer = None
inverse
inverse() -> CompositeGate

the inverse of CompositeGate

Returns:

Source code in QuICT/core/gate/composite_gate.py
def inverse(self) -> CompositeGate:
    # Refactoring later
    """ the inverse of CompositeGate

    Returns:
        CompositeGate: the inverse of the gateSet
    """
    _gates = CompositeGate(name=f"inv({self.name})")
    for gate in self.gates[::-1]:
        gate.inverse() | _gates

    return _gates
matrix
matrix(device: str = 'CPU', local: bool = False, expand_gate: bool = True) -> np.ndarray

matrix of these gates

Parameters:

  • device (str, default: 'CPU' ) –

    The device type for generate circuit's matrix, one of [CPU, GPU]. Defaults to "CPU".

  • local (bool, default: False ) –

    whether consider only about the occupied qubits or not

  • expand_gate (bool, default: True ) –

    whether or not expand each gate to the full system size when calculating circuit's matrix. Default to True.

Returns:

  • ndarray

    np.ndarray: the matrix of the gates

Source code in QuICT/core/gate/composite_gate.py
def matrix(self, device: str = "CPU", local: bool = False, expand_gate: bool = True) -> np.ndarray:
    """ matrix of these gates

    Args:
        device (str, optional): The device type for generate circuit's matrix, one of [CPU, GPU]. Defaults to "CPU".
        local (bool): whether consider only about the occupied qubits or not
        expand_gate (bool, optional): whether or not expand each gate to the full system size when calculating
            circuit's matrix. Default to `True`.

    Returns:
        np.ndarray: the matrix of the gates
    """
    assert device in ["CPU", "GPU"]
    circuit_matrix = CircuitMatrix(device, self._precision)
    if self.size() != self.count_1qubit_gate() + self.count_2qubit_gate():
        based_gates = self.decomposition_gates()
    else:
        based_gates = self.flatten_gates()

    if not local:
        matrix_width = max(self.qubits) + 1
    else:
        matrix_width, based_qubits = self.width(), self.qubits
        for gate in based_gates:
            new_qidx = [based_qubits.index(q) for q in gate.cargs + gate.targs]
            gate & new_qidx

    if not expand_gate:
        return circuit_matrix.get_unitary_matrix_non_expand(based_gates, matrix_width)

    return circuit_matrix.get_unitary_matrix(based_gates, matrix_width)
peel
peel(level: int = -1) -> CompositeGate

Partially flatten the composite gates inside current composite.

Parameters:

  • level (int, default: -1 ) –

    maximum level to flatten the composite gates. lelve = -1 means the composite gates will be fully flattened.

Returns:

  • CompositeGate ( CompositeGate ) –

    a partially flattened composite gate.

Source code in QuICT/core/gate/composite_gate.py
def peel(self, level: int = -1) -> CompositeGate:
    """ Partially flatten the composite gates inside current composite.

    Args:
        level (int): maximum level to flatten the composite gates. `lelve = -1` means
            the composite gates will be fully flattened.

    Returns:
        CompositeGate: a partially flattened composite gate.
    """
    if self._gates is None:
        return self

    _cg_peeled = CompositeGate(precision=self.precision)

    _cg_peeled._gates = self._gates.flatten_by_level(level=level)

    return _cg_peeled
set_ancilla
set_ancilla(ancilla_qubits: List[int]) -> None

Set ancilla qubits' indices.

Parameters:

  • ancilla_qubits (List[int]) –

    list of indices indicating the ancilla qubits.

Source code in QuICT/core/gate/composite_gate.py
def set_ancilla(self, ancilla_qubits: List[int]) -> None:
    """ Set ancilla qubits' indices.

    Args:
        ancilla_qubits (List[int]): list of indices indicating the ancilla qubits.
    """
    if len(ancilla_qubits) < 1:
        return
    self._qubit_indexes_validation(ancilla_qubits)
    if max(ancilla_qubits) > max(self.qubits):
        raise ValueError(f"Ancilla index {max(ancilla_qubits)} is outside the composite "
                         f"gate's current application range. Which is {max(self.qubits)}")
    self._ancilla_qubits = ancilla_qubits
split
split(qubits: list = None, depth: Union[int, list] = None, rescale: bool = True)

Split the CompositeGate by qubits or depth.

Parameters:

  • qubits (List, default: None ) –

    The qubit indexes for one of split CompositeGate.

  • depth (Union[int, List], default: None ) –

    The split depth for current CompositeGate, support split by different

Source code in QuICT/core/gate/composite_gate.py
def split(self, qubits: list = None, depth: Union[int, list] = None, rescale: bool = True):
    """ Split the CompositeGate by qubits or depth.

    Args:
        qubits (List): The qubit indexes for one of split CompositeGate.
        depth (Union[int, List]): The split depth for current CompositeGate, support split by different
        depth for different qubits.
    """
    left_gates, right_gates = self._gates.split(qubits, depth)
    left_cgate, right_cgate = CompositeGate(gates=left_gates), CompositeGate(gates=right_gates)
    if rescale:
        left_cgate & list(range(left_cgate.width()))
        right_cgate & list(range(right_cgate.width()))

    return left_cgate, right_cgate
width
width()

The number of qubits in CompositeGate.

Returns:

  • int

    the number of qubits in circuit

Source code in QuICT/core/gate/composite_gate.py
def width(self):
    """ The number of qubits in CompositeGate.

    Returns:
        int: the number of qubits in circuit
    """
    return len(self.qubits)

DiagonalGate

DiagonalGate(target: int, aux: int = 0, opt: bool = True, keep_phase: bool = True)

Bases: object

Diagonal gate

Reference

https://arxiv.org/abs/2108.06150

Parameters:

  • target (int) –

    number of target qubits

  • aux (int, default: 0 ) –

    number of auxiliary qubits

  • opt (bool, default: True ) –

    optimizer switch, enabled by default

  • keep_phase (bool, default: True ) –

    global phase switch

Source code in QuICT/core/gate/diagonal_gate.py
def __init__(self, target: int, aux: int = 0, opt: bool = True, keep_phase: bool = True):
    """
    Args:
        target (int): number of target qubits
        aux (int, optional): number of auxiliary qubits
        opt (bool): optimizer switch, enabled by default
        keep_phase (bool): global phase switch
    """
    self.target = target
    if np.mod(aux, 2) != 0:
        self._logger.warn(
            'Algorithm serves for even number of auxiliary qubits. One auxiliary qubit is dropped.'
        )
        aux = aux - 1
    self.aux = aux
    self.opt = opt
    self.keep_phase = keep_phase
    self.count_no_aux = [0] * (1 << self.target)
    if self.opt:
        from QuICT.qcda.optimization.cnot_without_ancilla import CnotWithoutAncilla
        self._cnot_optimizer = CnotWithoutAncilla()
S_x classmethod
S_x(x: int, n: int) -> str

Implement the Appendix H, also the construction of sets \(S_x\).

Parameters:

  • x (int) –

    the number from \(0\) to \(2^n-1\)

  • n (int) –

    the length of these binary strings

return

str: an array \(S_x = [x \otimes e_1,x \otimes e_2,...,x \otimes e_n]\)

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def S_x(cls, x: int, n: int) -> str:
    r"""
    Implement the Appendix H, also the construction of sets $S_x$.

    Args:
        x (int): the number from $0$ to $2^n-1$
        n (int): the length of these binary strings

    return:
        str: an array $S_x = [x \otimes e_1,x \otimes e_2,...,x \otimes e_n]$
    """
    sx = [cls.int_to_binary(x, n)] * n
    for i in range(n):
        en = cls.int_to_binary(1 << (n - i - 1), n)
        sx[i] = cls.binary_addition(sx[i], en, n)

    return sx
__call__
__call__(theta: List[float]) -> CompositeGate

Parameters:

  • theta (List[float]) –

    list of (2 ** target) angles of rotation in the diagonal gate

Returns:

Source code in QuICT/core/gate/diagonal_gate.py
def __call__(self, theta: List[float]) -> CompositeGate:
    """
    Args:
        theta (List[float]): list of (2 ** target) angles of rotation in the diagonal gate

    Returns:
        CompositeGate: diagonal gate
    """
    assert len(theta) == 1 << self.target, \
        ValueError('Incorrect number of angles')
    if self.aux == 0:
        self.count_no_aux = [0] * (1 << self.target)
        return self.no_aux_qubit(self.target, theta)
    else:
        return self.with_aux_qubit(theta)
alpha_s classmethod
alpha_s(theta: List[float], s: int, n: int) -> float

Solve Equation 6 \(\sum_s \alpha_s <s, x> = \theta(x)\)

Parameters:

  • theta (List[float]) –

    phase angles of the diagonal gate

  • s (int) –

    key of the solution component

  • n (int) –

    number of qubits in the diagonal gate

Returns:

  • float ( float ) –

    \(\alpha_s\) in Equation 6

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def alpha_s(cls, theta: List[float], s: int, n: int) -> float:
    r"""
    Solve Equation 6
    $\sum_s \alpha_s <s, x> = \theta(x)$

    Args:
        theta (List[float]): phase angles of the diagonal gate
        s (int): key of the solution component
        n (int): number of qubits in the diagonal gate

    Returns:
        float: $\alpha_s$ in Equation 6
    """
    A = np.zeros(1 << n)
    for x in range(1, 1 << n):
        A[x] = cls.binary_inner_prod(s, x, width=n)
    # A_inv = 2^(1-n) (2A - J)
    # A_inv = (2 * A[1:] - 1) / (1 << (n - 1))
    # As size should be matched, we change the code
    A_inv = (2 * A - 1) / (1 << (n - 1))
    return np.dot(A_inv, theta)
binary_addition classmethod
binary_addition(binary_string1: str, binary_string2: str, n: int) -> str

Implement the function: \(x \otimes y = (x1 \otimes y1, x2 \otimes y2, · · · , xn \otimes yn)^T\)

Parameters:

  • binary_string1 (str) –

    binary string like x

  • binary_string2 (str) –

    binary string like y

  • n (int) –

    the length of the binary strings

return

str: a string with bitwise binary addition

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def binary_addition(cls, binary_string1: str, binary_string2: str, n: int) -> str:
    r"""
    Implement the function:
    $x \otimes y = (x1 \otimes y1, x2 \otimes y2, · · · , xn \otimes yn)^T$

    Args:
        binary_string1 (str): binary string like x
        binary_string2 (str): binary string like y
        n (int): the length of the binary strings

    return:
        str: a string with bitwise binary addition
    """
    result = ''
    for i in range(n):
        bit1 = int(binary_string1[i])
        bit2 = int(binary_string2[i])
        sum_bits = (bit1 + bit2) % 2
        result += str(sum_bits)
    return result
binary_inner_prod staticmethod
binary_inner_prod(s: int, x: int, width: int) -> int

Calculate the binary inner product of s_bin and x_bin, where s_bin and x_bin are binary representation of s and x respectively of width n

Parameters:

  • s (int) –

    s in

  • x (int) –

    x in

  • width (int) –

    the width of s_bin and x_bin

Returns:

  • int ( int ) –

    the binary inner product of s and x

Source code in QuICT/core/gate/diagonal_gate.py
@staticmethod
def binary_inner_prod(s: int, x: int, width: int) -> int:
    """
    Calculate the binary inner product of s_bin and x_bin, where s_bin and x_bin
    are binary representation of s and x respectively of width n

    Args:
        s (int): s in <s, x>
        x (int): x in <s, x>
        width (int): the width of s_bin and x_bin

    Returns:
        int: the binary inner product of s and x
    """
    s_bin = np.array(list(np.binary_repr(s, width=width)), dtype=int)
    x_bin = np.array(list(np.binary_repr(x, width=width)), dtype=int)
    return np.mod(np.dot(s_bin, x_bin), 2)
construct_T classmethod
construct_T(n: int) -> Tuple[List[List[str]], int]

Realize the construction of a two-dimensional string array T, each row of the array constitutes a matrix with diagonal elements of 1.

Parameters:

  • n (int) –

    size of the prefixes c

Returns:

  • Tuple[List[List[str]], int]

    Tuple[List[List[str]], int]: 2-dimension T string array, with the number of rows: ell

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def construct_T(cls, n: int) -> Tuple[List[List[str]], int]:
    """
    Realize the construction of a two-dimensional string array T,
    each row of the array constitutes a matrix with diagonal elements of 1.

    Args:
        n (int): size of the prefixes c

    Returns:
        Tuple[List[List[str]], int]: 2-dimension T string array, with the number of rows: ell
    """
    # Initialize the two-dimensional array T
    T = [['0'] * n]

    # Generate 2**n-1 binary strings
    binary_strings = [format(i, f'0{n}b') for i in range(2 ** n - 1, 0, -1)]

    # Fill the 2D array T sequentially
    for st in binary_strings:
        # Iterate over each row
        ltnow = len(T)
        for i in range(ltnow):
            # Iterate over each column
            for j in range(n):
                # If the current position is empty and meets the requirements
                if T[i][j] == '0' and st[j] == '1':
                    # fill the current position
                    T[i][j] = st
                    break

                if i == ltnow - 1 and j == n - 1 and st[j] == '0':
                    # If none of the lines fit, a new line is needed
                    T.append(['0'] * n)

                    for k in range(n):
                        if st[k] == '1':
                            T[ltnow][k] = st
                            break
                    break

    ell = len(T)
    for i in range(ell):
        for j in range(n):
            if T[i][j] == '0':
                T[i][j] = '0' * j + '1' + '0' * (n - j - 1)

    return T, ell
disjoint_families_F classmethod
disjoint_families_F(r_c: int, r_t: int) -> List[Set[str]]

Implement the Eq(15),disjoint families F_1,...,F_ell

Parameters:

  • r_c (int) –

    size of the prefixes c

  • r_t (int) –

    size of the suffix t

Returns:

  • List[Set[str]]

    List[Set[str]]: 2-dimension (r_c + r_t)-bit string array, also the linear independent set F with ell rows and not fixed columns

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def disjoint_families_F(cls, r_c: int, r_t: int) -> List[Set[str]]:
    """
    Implement the Eq(15),disjoint families F_1,...,F_ell

    Args:
        r_c (int): size of the prefixes c
        r_t (int): size of the suffix t

    Returns:
        List[Set[str]]: 2-dimension (r_c + r_t)-bit string array, also the linear independent set F
            with ell rows and not fixed columns
    """
    T, ell = cls.construct_T(r_t)

    F = [set() for _ in range(ell)]

    cset = []
    for i in range(1 << r_c):
        cset.append(cls.int_to_binary(i, r_c))

    # avoid the repeating strings in F_d
    intersection_t = set()

    # implement F_k, 1<= k<= ell
    for k in range(ell):
        for i in range(len(T[k])):
            if T[k][i] not in intersection_t:
                for c in cset:
                    if int(c + T[k][i], 2) != 0:
                        F[k].add(c + T[k][i])
                        intersection_t.add(T[k][i])

    return F
int_to_binary classmethod
int_to_binary(num: int, n: int) -> str

Parameters:

  • num (int) –

    the number from 0 to 2^n-1

  • n (int) –

    the length of the binary strings

return

str: numeric num converted binary string

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def int_to_binary(cls, num: int, n: int) -> str:
    """
    Args:
        num (int): the number from 0 to 2^n-1
        n (int): the length of the binary strings

    return:
        str: numeric num converted binary string
    """
    binary_str = bin(num)[2:]  # convert to binary without the '0b' prefix
    if len(binary_str) < n:
        binary_str = '0' * (n - len(binary_str)) + binary_str
    elif len(binary_str) > n:
        raise ValueError("Integer is not within the valid range.")
    return binary_str
ket_fjk classmethod
ket_fjk(j: int, k: int, n: int, t: int, target_num: int) -> CompositeGate

Implement the part of unitary U1 for every j: \(|0\rangle -> |<s(j, k), x>\rangle\) by adding the CNOT gates

Parameters:

  • j (int) –

    j is the label of n-bit strings s(j,k)

  • k (int) –

    k is the label of n-bit strings s(j,k)

  • n (int) –

    length of 0-1 string to be partitioned

  • t (int) –

    length of the shared prefix of each row

  • target_num (int) –

    the target label connecting the CNOT gate

Returns:

  • CompositeGate ( CompositeGate ) –

    \(|0\rangle -> |<s(j, k), x>\rangle\)

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def ket_fjk(cls, j: int, k: int, n: int, t: int, target_num: int) -> CompositeGate:
    r"""
    Implement the part of unitary U1 for every j:
    $|0\rangle -> |<s(j, k), x>\rangle$ by adding the CNOT gates

    Args:
        j (int): j is the label of n-bit strings s(j,k)
        k (int): k is the label of n-bit strings s(j,k)
        n (int): length of 0-1 string to be partitioned
        t (int): length of the shared prefix of each row
        target_num (int): the target label connecting the CNOT gate

    Returns:
        CompositeGate: $|0\rangle -> |<s(j, k), x>\rangle$
    """
    s = cls.partitioned_gray_code(n, t)
    st = s[j - 1][k - 1]

    gates = CompositeGate()
    for i in range(len(st)):
        if st[i] == '1':
            CX & [i, target_num] | gates
    return gates
linear_fjk classmethod
linear_fjk(j: int, k: int, x: int, n: int, t: int) -> int

Implement the linear functions \(f_{jk}(x) = <s(j, k), x>\)

Parameters:

  • j (int) –

    j is the label of n-bit strings s(j, k)

  • k (int) –

    k is the label of n-bit strings s(j, k)

  • n (int) –

    length of 0-1 string to be partitioned

  • t (int) –

    length of the shared prefix of each row

  • x (int) –

    the independent variables of the function \(f_{jk}\)

Returns:

  • int ( int ) –

    \(f_{jk}(x)\)

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def linear_fjk(cls, j: int, k: int, x: int, n: int, t: int) -> int:
    r"""
    Implement the linear functions $f_{jk}(x) = <s(j, k), x>$

    Args:
        j (int): j is the label of n-bit strings s(j, k)
        k (int): k is the label of n-bit strings s(j, k)
        n (int): length of 0-1 string to be partitioned
        t (int): length of the shared prefix of each row
        x (int): the independent variables of the function $f_{jk}$

    Returns:
        int: $f_{jk}(x)$
    """
    s = cls.partitioned_gray_code(n, t)
    decimal_integer = int(s[j - 1][k - 1], 2)
    # Convert the binary string s[j - 1][k - 1] to an integer
    return cls.binary_inner_prod(decimal_integer, x, width=n)
linearly_independent_sets_T classmethod
linearly_independent_sets_T(n: int) -> Tuple[List[List[str]], int]

Implement the Appendix H, also the construction of sets T.

Parameters:

  • n (int) –

    the size of each sublist T^(i),i = 1,2,...,ell

return

Tuple[List[List[str]], int]: 2-dimension n-bit string array, also the linear independent set T with ell rows and n columns and the number of T, ell.

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def linearly_independent_sets_T(cls, n: int) -> Tuple[List[List[str]], int]:
    """
    Implement the Appendix H, also the construction of sets T.

    Args:
        n (int): the size of each sublist T^(i),i = 1,2,...,ell

    return:
        Tuple[List[List[str]], int]: 2-dimension n-bit string array,
            also the linear independent set T with ell rows and n columns and the number of T, ell.
    """
    if n == 1:
        return [['1', '0']], 1

    k = int(np.ceil(np.log2(n + 1)))
    H = []
    for i in range(1, n + 1):
        st_k = cls.int_to_binary(i, k)
        st_k = st_k[::-1]
        H.append(st_k)

    L = []

    zero_k = '0' * k
    one_k = '1' * k

    # count the number of the sets T
    ell = 0

    # construct the set L except 0^n.
    for x in range(1, 1 << n):
        if cls.Hx(x, H, n) == zero_k:
            L.append(cls.int_to_binary(x, n))
            ell += 1
        if cls.Hx(x, H, n) == one_k:
            L.append(cls.int_to_binary(x, n))
            ell += 1

    # ell has already equals to the size of set L
    ell = 2 * ell + 1

    # Next we construct the set T
    T = [[]]
    T[0] = cls.S_x(0, n)

    for x in L:
        T_x0 = cls.S_x(int(x, 2), n)
        T_x1 = T_x0.copy()

        if x.count('1') == 1 and '1' in x:
            i = int(np.log2(int(x, 2)))

            T_x0.pop(n - i - 1)
            T_x1.pop(n - i - 1)
            T_x1.append(x)

            if (n - i - 1) != 0:
                T_x0.append('1' + '0' * (n - 1))
            else:
                if n > 1:
                    T_x0.append('01' + '0' * (n - 2))
                if n == 1:
                    T_x0.append('1')

        else:
            for i in range(len(T_x1)):
                if x.count('1') >= T_x1[i].count('1'):
                    T_x1[i] = x
                    break

        sorted_T_x0 = sorted(T_x0, key=lambda x: int(x, 2), reverse=True)
        sorted_T_x1 = sorted(T_x1, key=lambda x: int(x, 2), reverse=True)

        T.append(sorted_T_x0)
        T.append(sorted_T_x1)

    return T, ell
lucal_gray_code staticmethod
lucal_gray_code(k: int, n: int) -> List[str]

Generate the (k, n)-Gray code defined in and following Lemma 7

Parameters:

  • k (int) –

    start the circular modification from the k-th binary code

  • n (int) –

    the length of binary code, that is, the length of Gray code would be 2^n

Returns:

  • List[str]

    List[str]: the (k, n)-Gray code

Source code in QuICT/core/gate/diagonal_gate.py
@staticmethod
def lucal_gray_code(k: int, n: int) -> List[str]:
    """
    Generate the (k, n)-Gray code defined in and following Lemma 7

    Args:
        k (int): start the circular modification from the k-th binary code
        n (int): the length of binary code, that is, the length of Gray code would be 2^n

    Returns:
        List[str]: the (k, n)-Gray code
    """
    def flip(bit):
        if bit == '1':
            return '0'
        if bit == '0':
            return '1'
        raise ValueError('Invalid bit found in gray code generation.')

    def zeta(x):
        """
        For integer x, zeta(x) = max{k: 2^k | x}
        """
        x_bin = np.binary_repr(x)
        return len(x_bin) - len(x_bin.strip('0'))

    gray_code = ['0' for _ in range(n)]
    result = [''.join(gray_code)]
    for i in range(1, 1 << n):
        bit = np.mod(zeta(i) + k, n)
        gray_code[bit] = flip(gray_code[bit])
        result.append(''.join(gray_code))

    return result
no_aux_qubit
no_aux_qubit(n: int, theta: List[float]) -> CompositeGate

Parameters:

  • n (int) –

    number of qubits in the diagonal gate

  • theta (List[float]) –

    list of (2 ** target) angles of rotation in the diagonal gate

Returns:

  • CompositeGate ( CompositeGate ) –

    diagonal gate without auxiliary qubit

Source code in QuICT/core/gate/diagonal_gate.py
def no_aux_qubit(self, n: int, theta: List[float]) -> CompositeGate:
    """
    Args:
        n (int): number of qubits in the diagonal gate
        theta (List[float]): list of (2 ** target) angles of rotation in the diagonal gate

    Returns:
        CompositeGate: diagonal gate without auxiliary qubit
    """
    # nmax is the final size of the diagonal gate
    # nmax can keep the size of theta the same as s of alpha_s
    nmax = self.target
    gates = CompositeGate()
    r_t = int(np.floor(n / 2))
    r_c = n - r_t

    if n == 1:
        s_int = 1 << (nmax - 1)

        if self.count_no_aux[s_int] == 0:
            self.count_no_aux[s_int] += 1
            phase_c0 = self.alpha_s(theta, s_int, nmax)
            U1(phase_c0) | gates(0)

        # Global phase
        if self.keep_phase:
            gates.append(GPhase(theta[0]) & 0)

        return gates

    T, ell = self.construct_T(r_t)
    F = self.disjoint_families_F(r_c, r_t)

    # Implement the G_k, k = 1,...,ell
    # All operations are implemented one target bit at a loop
    for i in range(r_t):
        for k in range(ell):
            # Stage 1
            st = T[k][i]
            cnot_s1 = CompositeGate()
            for j in range(r_t):
                if st[j] == '1' and i != j:
                    CX & [r_c + j, r_c + i] | cnot_s1

            if cnot_s1.size() != 0 and self.opt:
                cnot_s1 = self._cnot_optimizer.execute(cnot_s1).to_compositegate()
            cnot_s1 | gates

            # Stage 2: Gray Path Stage
            # Phase 1
            s = '0' * r_c + T[k][i]
            if s in F[k]:
                if len(s) < nmax:
                    s = s + '0' * (nmax - len(s))
                s_int = int(s, 2)

                if self.count_no_aux[s_int] == 0:
                    self.count_no_aux[s_int] += 1
                    phase_c1 = self.alpha_s(theta, s_int, nmax)
                    U1(phase_c1) | gates(r_c + i)

            # Phase p
            for p in range(1, 1 << r_c):
                # Step p.1
                # give the c^(i) gray code cycle
                c = self.lucal_gray_code(i, r_c)
                cnot_p1 = CompositeGate()
                for index in range(r_c):
                    if c[p - 1][index] != c[p][index]:
                        CX & [index, i + r_c] | cnot_p1
                        break

                if cnot_p1.size() != 0 and self.opt:
                    cnot_p1 = self._cnot_optimizer.execute(cnot_p1).to_compositegate()
                cnot_p1 | gates

                # Step p.2
                sp = c[p] + T[k][i]
                if sp in F[k]:
                    if len(sp) < nmax:
                        sp = sp + '0' * (nmax - len(sp))
                    sp_int = int(sp, 2)

                    if self.count_no_aux[sp_int] == 0:
                        self.count_no_aux[sp_int] += 1
                        phase_cp = self.alpha_s(theta, sp_int, nmax)
                        U1(phase_cp) | gates(r_c + i)

            # Phase 2^r_c + 1
            cnot_rc = CompositeGate()
            for index in range(r_c):
                if c[0][index] != c[2 ** r_c - 1][index]:
                    CX & [index, i + r_c] | cnot_rc
                    break

            if cnot_rc.size() != 0 and self.opt:
                cnot_rc = self._cnot_optimizer.execute(cnot_rc).to_compositegate()
            cnot_rc | gates

            cnot_la = CompositeGate()
            st = T[k][i]
            for j in range(r_t - 1, -1, -1):
                if st[j] == '1' and i != j:
                    CX & [r_c + j, r_c + i] | cnot_la

            if cnot_la.size() != 0 and self.opt:
                cnot_la = self._cnot_optimizer.execute(cnot_la).to_compositegate()
            cnot_la | gates

    # implement the r_c-qubit diagonal unitary matrix recursively.
    self.no_aux_qubit(r_c, theta) | gates

    return gates
partitioned_gray_code classmethod
partitioned_gray_code(n: int, t: int) -> List[List[str]]

Lemma 15 by the construction in Appendix E

Parameters:

  • n (int) –

    length of 0-1 string to be partitioned

  • t (int) –

    length of the shared prefix of each row

Returns:

  • List[List[str]]

    List[List[str]]: partitioned gray code

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def partitioned_gray_code(cls, n: int, t: int) -> List[List[str]]:
    """
    Lemma 15 by the construction in Appendix E

    Args:
        n (int): length of 0-1 string to be partitioned
        t (int): length of the shared prefix of each row

    Returns:
        List[List[str]]: partitioned gray code
    """
    s = [[] for _ in range(1 << t)]
    for j in range(1 << t):
        prefix = np.binary_repr(j, width=t)[::-1]
        for suffix in cls.lucal_gray_code(np.mod(j, n - t), n - t):
            s[j].append(prefix + suffix)
    return s
phase_shift classmethod
phase_shift(theta: List[float], seq: Iterable = None, aux: int = None) -> CompositeGate

Implement the phase shift \(|x\rangle -> \exp(i \theta(x)) |x\rangle\) by solving Equation 6 \(\sum_s \alpha_s <s, x> = \theta(x)\)

Parameters:

  • theta (List[float]) –

    phase angles of the diagonal gate

  • seq (Iterable, default: None ) –

    sequence of s application, numerical order if not assigned

  • aux (int, default: None ) –

    key of auxiliary qubit (if exists)

Returns:

  • CompositeGate ( CompositeGate ) –

    CompositeGate of the diagonal gate

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def phase_shift(cls, theta: List[float], seq: Iterable = None, aux: int = None) -> CompositeGate:
    r"""
    Implement the phase shift
    $|x\rangle -> \exp(i \theta(x)) |x\rangle$
    by solving Equation 6
    $\sum_s \alpha_s <s, x> = \theta(x)$

    Args:
        theta (List[float]): phase angles of the diagonal gate
        seq (Iterable, optional): sequence of s application, numerical order if not assigned
        aux (int, optional): key of auxiliary qubit (if exists)

    Returns:
        CompositeGate: CompositeGate of the diagonal gate
    """
    n = int(np.floor(np.log2(len(theta))))
    if seq is None:
        seq = range(1, 1 << n)
    else:
        assert sorted(list(seq)) == list(range(1, 1 << n)),\
            ValueError('Invalid sequence of s in phase_shift')
    if aux is not None:
        assert aux >= n, \
            ValueError('Invalid auxiliary qubit in phase_shift.')
    # theta(0) = 0
    global_phase = theta[0]
    theta = theta - global_phase

    gates = CompositeGate()
    GPhase(global_phase) & 0 | gates
    # Calculate A_inv row by row (i.e., for different s)
    for s in seq:
        alpha = cls.alpha_s(theta, s, n)
        if aux is not None:
            gates.extend(cls.phase_shift_s(s, n, alpha, aux=aux))
        else:
            gates.extend(cls.phase_shift_s(s, n, alpha, j=0))
    return gates
phase_shift_s classmethod
phase_shift_s(s: int, n: int, alpha: float, aux: int = None, j: int = None) -> CompositeGate

Implement the phase shift for a certain s defined in Equation 5 as Figure 8 \(|x\rangle -> \exp(i \alpha_s <s, x>) |x\rangle\)

Parameters:

  • s (int) –

    whose binary representation stands for the 0-1 string s

  • n (int) –

    the number of qubits in \(|x\rangle\)

  • alpha (float) –

    \(\alpha_s\) in the equation

  • aux (int, default: None ) –

    key of auxiliary qubit (if exists)

  • j (int, default: None ) –

    if no auxiliary qubit, the j-th smallest element in s_idx would be the target qubit

Returns:

  • CompositeGate ( CompositeGate ) –

    CompositeGate for Equation 5 as Figure 8

Source code in QuICT/core/gate/diagonal_gate.py
@classmethod
def phase_shift_s(cls, s: int, n: int, alpha: float, aux: int = None, j: int = None) -> CompositeGate:
    r"""
    Implement the phase shift for a certain s defined in Equation 5 as Figure 8
    $|x\rangle -> \exp(i \alpha_s <s, x>) |x\rangle$

    Args:
        s (int): whose binary representation stands for the 0-1 string s
        n (int): the number of qubits in $|x\rangle$
        alpha (float): $\alpha_s$ in the equation
        aux (int, optional): key of auxiliary qubit (if exists)
        j (int, optional): if no auxiliary qubit, the j-th smallest element in s_idx would be the target qubit

    Returns:
        CompositeGate: CompositeGate for Equation 5 as Figure 8
    """
    gates = CompositeGate()
    s_bin = np.binary_repr(s, width=n)
    s_idx = []
    for i in range(n):
        if s_bin[i] == '1':
            s_idx.append(i)

    # Figure 8 (a)
    if aux is not None:
        if j is not None:
            cls._logger.warn('With auxiliary qubit in phase_shift_s, no i_j is needed.')
        assert aux >= n, ValueError('Invalid auxiliary qubit in phase_shift_s.')
        for i in s_idx:
            CX & [i, aux] | gates
        U1(alpha) & aux | gates
        for i in reversed(s_idx):
            CX & [i, aux] | gates
        return gates

    # Figure 8 (b)
    else:
        assert j < len(s_idx), ValueError('Invalid target in phase_shift without auxiliary qubit.')
        for i in s_idx:
            if i == s_idx[j]:
                continue
            CX & [i, s_idx[j]] | gates
        U1(alpha) & s_idx[j] | gates
        for i in s_idx:
            if i == s_idx[j]:
                continue
            CX & [i, s_idx[j]] | gates
        return gates
with_aux_qubit
with_aux_qubit(theta: List[float]) -> CompositeGate

Parameters:

  • theta (List[float]) –

    list of (2 ** target) angles of rotation in the diagonal gate

Returns:

  • CompositeGate ( CompositeGate ) –

    diagonal gate with auxiliary qubit at the end of qubits

Source code in QuICT/core/gate/diagonal_gate.py
def with_aux_qubit(self, theta: List[float]) -> CompositeGate:
    """
    Args:
        theta (List[float]): list of (2 ** target) angles of rotation in the diagonal gate

    Returns:
        CompositeGate: diagonal gate with auxiliary qubit at the end of qubits
    """
    # Pay attention:All arrays and qubit is 0 as the starting point,
    # but begins with 1 in the paper.
    n = self.target
    m = self.aux
    gates = CompositeGate()

    # Stage 1: Prefix Copy
    t = int(np.floor(np.log2(m / 2)))
    copies = int(np.floor(m / (2 * t)))
    # the round of the copy process
    r = int(np.floor(np.log2(copies + 1)))

    # define a cnot gate set
    cnot_s1 = CompositeGate()

    # Consider the parallelism of the copy process.
    for i in range(1, r + 1):
        for j in range(t):
            CX & [j, n + (2 ** (i - 1) - 1) * t + j] | cnot_s1
        for j in range((2 ** (i - 1) - 1) * t):
            CX & [n + j, n + j + (2 ** (i - 1)) * t] | cnot_s1
    if 2 ** r - 1 < copies:
        rest = copies - 2 ** r + 1
        for j in range(t):
            CX & [j, n + (2 ** r - 1) * t + j] | cnot_s1
        if rest != 1:
            for j in range((rest - 1) * t):
                CX & [n + j, n + (2 ** r) * t + j] | cnot_s1

    # Stage 2: Gray Initial
    ell = 2 ** t
    ini_star = n + int(m / 2)
    # To ensure parallelism, the group number of the control bit is given.
    visit1 = [0 for _ in range(n)]
    s = self.partitioned_gray_code(n, t)
    # 1.implement U1
    for j in range(1, 1 + ell):
        st = s[j - 1][0]
        for i in range(len(st)):
            if st[i] == '1':
                CX & [n + t * (visit1[i] % copies) + i, ini_star + j - 1] | cnot_s1
                visit1[i] += 1

    # We use the optimization for cnot gates

    if cnot_s1.size() != 0 and self.opt:
        cnot_s1 = self._cnot_optimizer.execute(cnot_s1).to_compositegate()
    cnot_s1 | gates

    # 2.implement R1
    for j in range(1, 1 + ell):
        sj1_int = int(s[j - 1][0], 2)
        phase = self.alpha_s(theta, sj1_int, n)
        U1(phase) | gates(ini_star + j - 1)

    # Stage 3:Suffix Copy
    cnot_s3 = CompositeGate()
    # 1.U^{\dagger}_{copy,1}
    if 2 ** r - 1 < copies:
        rest = copies - 2 ** r + 1
        if rest != 1:
            for j in range((rest - 1) * t - 1, -1, -1):
                CX & [n + j, n + (2 ** r) * t + j] | cnot_s3

        for j in range(t - 1, -1, -1):
            CX & [j, n + (2 ** r - 1) * t + j] | cnot_s3

    for i in range(r, 0, -1):
        for j in range((2 ** (i - 1) - 1) * t - 1, -1, -1):
            CX & [n + j, n + j + (2 ** (i - 1)) * t] | cnot_s3

        for j in range(t - 1, -1, -1):
            CX & [j, n + (2 ** (i - 1) - 1) * t + j] | cnot_s3

    # 2.U_{copy,2}
    copies3 = int(np.floor(m / (2 * (n - t))))
    r3 = int(np.floor(np.log2(copies3 + 1)))
    for i in range(1, r3 + 1):
        for j in range(t, n):
            CX & [j, n + (2 ** (i - 1) - 1) * (n - t) + j - t] | cnot_s3
        for j in range((2 ** (i - 1) - 1) * (n - t)):
            CX & [n + j, n + j + (2 ** (i - 1)) * (n - t)] | cnot_s3
    if 2 ** r3 - 1 < copies3:
        rest = copies3 - 2 ** r3 + 1
        for j in range(t, n):
            CX & [j, n + (2 ** r3 - 1) * (n - t) + j - t] | cnot_s3
        if rest != 1:
            for j in range((rest - 1) * (n - t)):
                CX & [n + j, n + (2 ** r3) * (n - t) + j] | cnot_s3

    # Optimization for cnot gates
    if cnot_s3.size() != 0 and self.opt:
        cnot_s3 = self._cnot_optimizer.execute(cnot_s3).to_compositegate()
    cnot_s3 | gates

    # Stage 4: Gray Path
    num_phases = int((2 ** n) / ell)
    path_star = n + int(m / 2)
    for k in range(2, num_phases + 1):
        visit2 = [0 for _ in range(n)]
        # Step k.1: U_k
        for j in range(1, ell + 1):
            s = self.partitioned_gray_code(n, t)
            s1 = s[j - 1][k - 2]
            s2 = s[j - 1][k - 1]

            for i in range(len(s1)):
                if s1[i] != s2[i]:
                    CX & [n + i - t + (n - t) * (visit2[i] % copies3), path_star + j - 1] | gates
                    visit2[i] += 1

        # Step k.2: R_k
        for j in range(1, ell + 1):
            s = self.partitioned_gray_code(n, t)
            sjk_int = int(s[j - 1][k - 1], 2)
            phase_k = self.alpha_s(theta, sjk_int, n)
            U1(phase_k) | gates(j - 1 + path_star)

    # Stage 5:Inverse
    cnot_s5 = CompositeGate()
    # clear the CNOT gates in Stage 4:Gray Path Stage k.1 U1
    visit3 = [0 for _ in range(n)]
    for j in range(1, ell + 1):
        s = self.partitioned_gray_code(n, t)
        s2 = s[j - 1][num_phases - 1]

        for i in range(len(s2)):
            if s2[i] != '0':
                if i >= t:
                    copies3 = int(np.floor(m / (2 * (n - t))))
                    CX & [n + i - t + (n - t) * (visit3[i] % copies3), path_star + j - 1] | cnot_s5
                    visit3[i] += 1

    # clear the copy process in Stage 3
    # (Write the copy process in reverse order)
    if 2 ** r3 - 1 < copies3:
        rest = copies3 - 2 ** r3 + 1
        if rest != 1:
            for j in range((rest - 1) * (n - t) - 1, -1, -1):
                CX & [n + j, n + (2 ** r3) * (n - t) + j] | cnot_s5
        for j in range(n - 1, t - 1, -1):
            CX & [j, n + (2 ** r3 - 1) * (n - t) + j - t] | cnot_s5
    for i in range(r3, 0, -1):
        for j in range((2 ** (i - 1) - 1) * (n - t) - 1, -1, -1):
            CX & [n + j, n + j + (2 ** (i - 1)) * (n - t)] | cnot_s5
        for j in range(n - 1, t - 1, -1):
            CX & [j, n + (2 ** (i - 1) - 1) * (n - t) + j - t] | cnot_s5

    # clear the Stage 1:copy prefix
    t = int(np.floor(np.log2(m / 2)))
    copies = int(np.floor(m / (2 * t)))
    r = int(np.floor(np.log2(copies + 1)))
    for i in range(1, r + 1):
        for j in range(t):
            CX & [j, n + (2 ** (i - 1) - 1) * t + j] | cnot_s5
        for j in range((2 ** (i - 1) - 1) * t):
            CX & [n + j, n + j + (2 ** (i - 1)) * t] | cnot_s5
    if 2 ** r - 1 < copies:
        rest = copies - 2 ** r + 1
        for j in range(t):
            CX & [j, n + (2 ** r - 1) * t + j] | cnot_s5
        if rest != 1:
            for j in range((rest - 1) * t):
                CX & [n + j, n + (2 ** r) * t + j] | cnot_s5

    # clear the Stage 2:U1
    visit4 = [0 for _ in range(n)]
    s = self.partitioned_gray_code(n, t)
    for j in range(1, 1 + ell):
        st = s[j - 1][0]
        for i in range(len(st)):
            if st[i] == '1':
                CX & [n + t * (visit4[i] % copies) + i, ini_star + j - 1] | cnot_s5
                visit4[i] += 1

    # clear the Stage 1:prefix copy
    # (Write the copy process in reverse order)
    if 2 ** r - 1 < copies:
        rest = copies - 2 ** r + 1
        if rest != 1:
            for j in range((rest - 1) * t - 1, -1, -1):
                CX & [n + j, n + (2 ** r) * t + j] | cnot_s5
        for j in range(t - 1, -1, -1):
            CX & [j, n + (2 ** r - 1) * t + j] | cnot_s5
    for i in range(r, 0, -1):
        for j in range((2 ** (i - 1) - 1) * t - 1, -1, -1):
            CX & [n + j, n + j + (2 ** (i - 1)) * t] | cnot_s5

        for j in range(t - 1, -1, -1):
            CX & [j, n + (2 ** (i - 1) - 1) * t + j] | cnot_s5

    # Optimization for cnot gates
    if cnot_s5.size() != 0 and self.opt:
        cnot_s5 = self._cnot_optimizer.execute(cnot_s5).to_compositegate()
    cnot_s5 | gates

    # Global phase
    if self.keep_phase:
        gates.append(GPhase(theta[0]) & 0)
    return gates

GateAppendError

GateAppendError(msg: str = None)

Bases: QuICTException

BasicGate append error.

Source code in QuICT/tools/exception/core/gate_exception.py
def __init__(self, msg: str = None):
    super().__init__(1018, msg)

GateMatrixError

GateMatrixError(msg: str = None)

Bases: QuICTException

BasicGate matrix error.

Source code in QuICT/tools/exception/core/gate_exception.py
def __init__(self, msg: str = None):
    super().__init__(1019, msg)

GateMatrixGenerator

Generator the Quantum Gates' Matrix.

based_matrix
based_matrix(gate_type: GateType, precision: complex)

Return the no-parameter Quantum Gates' matrix.

Parameters:

  • gate_type (GateType) –

    The type of Quantum Gate

  • precision (complex) –

    The precision of Quantum Gate

Returns:

  • np.ndarray: The Quantum Gate's matrix

Source code in QuICT/core/gate/utils/gate_matrix.py
def based_matrix(self, gate_type: GateType, precision: complex):
    """ Return the no-parameter Quantum Gates' matrix.

    Args:
        gate_type (GateType): The type of Quantum Gate
        precision (complex): The precision of Quantum Gate

    Returns:
        np.ndarray: The Quantum Gate's matrix
    """
    if gate_type in [GateType.h, GateType.ch]:
        return np.array([
            [1 / np.sqrt(2), 1 / np.sqrt(2)],
            [1 / np.sqrt(2), -1 / np.sqrt(2)]
        ], dtype=precision)
    elif gate_type == GateType.hy:
        return np.array([
            [1 / np.sqrt(2), -1j / np.sqrt(2)],
            [1j / np.sqrt(2), -1 / np.sqrt(2)]
        ], dtype=precision)
    elif gate_type == GateType.s:
        return np.array([
            [1, 0],
            [0, 1j]
        ], dtype=precision)
    elif gate_type == GateType.sdg:
        return np.array([
            [1, 0],
            [0, -1j]
        ], dtype=precision)
    elif gate_type in [GateType.x, GateType.cx, GateType.ccx]:
        return np.array([
            [0, 1],
            [1, 0]
        ], dtype=precision)
    elif gate_type in [GateType.y, GateType.cy]:
        return np.array([
            [0, -1j],
            [1j, 0]
        ], dtype=precision)
    elif gate_type in [GateType.z, GateType.cz, GateType.ccz]:
        return np.array([
            [1, 0],
            [0, -1]
        ], dtype=precision)
    elif gate_type == GateType.sx:
        return np.array([
            [0.5 + 0.5j, 0.5 - 0.5j],
            [0.5 - 0.5j, 0.5 + 0.5j]
        ], dtype=precision)
    elif gate_type == GateType.sxdg:
        return np.array([
            [0.5 - 0.5j, 0.5 + 0.5j],
            [0.5 + 0.5j, 0.5 - 0.5j]
        ], dtype=precision)
    elif gate_type == GateType.sy:
        return np.array([
            [1 / np.sqrt(2), -1 / np.sqrt(2)],
            [1 / np.sqrt(2), 1 / np.sqrt(2)]
        ], dtype=precision)
    elif gate_type == GateType.sydg:
        return np.array([
            [1 / np.sqrt(2), 1 / np.sqrt(2)],
            [-1 / np.sqrt(2), 1 / np.sqrt(2)]
        ], dtype=precision)
    elif gate_type == GateType.sw:
        return np.array([
            [1 / np.sqrt(2), -np.sqrt(1j / 2)],
            [np.sqrt(-1j / 2), 1 / np.sqrt(2)]
        ], dtype=precision)
    elif gate_type == GateType.id:
        return np.array([
            [1, 0],
            [0, 1]
        ], dtype=precision)
    elif gate_type == GateType.t:
        return np.array([
            [1, 0],
            [0, 1 / np.sqrt(2) + 1j * 1 / np.sqrt(2)]
        ], dtype=precision)
    elif gate_type == GateType.tdg:
        return np.array([
            [1, 0],
            [0, 1 / np.sqrt(2) + 1j * -1 / np.sqrt(2)]
        ], dtype=precision)
    elif gate_type in [GateType.swap, GateType.cswap]:
        return np.array([
            [1, 0, 0, 0],
            [0, 0, 1, 0],
            [0, 1, 0, 0],
            [0, 0, 0, 1]
        ], dtype=precision)
    elif gate_type == GateType.iswap:
        return np.array([
            [1, 0, 0, 0],
            [0, 0, 1j, 0],
            [0, 1j, 0, 0],
            [0, 0, 0, 1]
        ], dtype=precision)
    elif gate_type == GateType.iswapdg:
        return np.array([
            [1, 0, 0, 0],
            [0, 0, -1j, 0],
            [0, -1j, 0, 0],
            [0, 0, 0, 1]
        ], dtype=precision)
    elif gate_type == GateType.sqiswap:
        return np.array([
            [1, 0, 0, 0],
            [0, 1 / np.sqrt(2), 1j / np.sqrt(2), 0],
            [0, 1j / np.sqrt(2), 1 / np.sqrt(2), 0],
            [0, 0, 0, 1]
        ], dtype=precision)
    elif gate_type == GateType.ecr:
        return np.array([
            [0, 0, 1 / np.sqrt(2), 1j / np.sqrt(2)],
            [0, 0, 1j / np.sqrt(2), 1 / np.sqrt(2)],
            [1 / np.sqrt(2), -1j / np.sqrt(2), 0, 0],
            [-1j / np.sqrt(2), 1 / np.sqrt(2), 0, 0]
        ], dtype=precision)
    elif gate_type == GateType.rccx:
        return np.array([
            [1, 0, 0, 0],
            [0, -1, 0, 0],
            [0, 0, 0, 1],
            [0, 0, 1, 0]
        ], dtype=precision)
    else:
        raise TypeError(gate_type)
get_matrix
get_matrix(gate, precision: str = None, is_get_target: bool = False) -> np.ndarray

Return the given BasicGate's matrix.

Parameters:

  • gate (BasicGate) –

    The Quantum Gate

  • precision (str, default: None ) –

    The precision of Quantum Gate. Defaults to None.

  • is_get_target (bool, default: False ) –

    Whether return the completed BasicGate's matrix, or only return the target

Returns:

  • ndarray

    np.ndarray: The Quantum Gates' matrix.

Source code in QuICT/core/gate/utils/gate_matrix.py
def get_matrix(self, gate, precision: str = None, is_get_target: bool = False) -> np.ndarray:
    """ Return the given BasicGate's matrix.

    Args:
        gate (BasicGate): The Quantum Gate
        precision (str, optional): The precision of Quantum Gate. Defaults to None.
        is_get_target (bool, optional): Whether return the completed BasicGate's matrix, or only return the target
        qubits part. Defaults to False.

    Returns:
        np.ndarray: The Quantum Gates' matrix.
    """
    # Step 1: Get based matrix's value
    gate_type = gate.type
    _precision = gate.precision if precision is None else precision
    gate_precision = np.complex128 if _precision == "double" else np.complex64
    gate_params = gate.params
    if gate_params == 0:
        based_matrix = self.based_matrix(gate_type, gate_precision)
    else:
        based_matrix = self.matrix_with_param(
            gate_type,
            gate.pargs,
            gate.symbol_gate,
            gate.symbol_pargs,
            gate_precision,
        )

    if is_get_target:
        return based_matrix

    # Step 2: Depending on controlled_by, generate final matrix
    if gate.controls > 0:
        if gate_type == GateType.rccx:
            control_args = gate.controls - 1
            target_args = gate.targets + 1
        else:
            control_args = gate.controls
            target_args = gate.targets

        controlled_matrix = np.identity(
            1 << (control_args + target_args), dtype=gate_precision
        )
        target_border = 1 << target_args
        controlled_matrix[-target_border:, -target_border:] = based_matrix

        return controlled_matrix

    return based_matrix
grad_for_param
grad_for_param(gate_type: GateType, gate_pargs: list, symbol_pargs: dict, precision: str)

Return the Parameterized Quantum Gates' gradient matrices.

Parameters:

  • gate_type (GateType) –

    The type of Quantum Gate.

  • gate_pargs (List) –

    The Quantum Gate's parameters.

  • symbol_pargs (dict) –

    The Quantum Gate's symbol parameters.

  • precision (str) –

    The precision of Quantum Gate, one of [double, single]

Returns:

  • list

    Parameterized Quantum Gates' gradient matrices.

Source code in QuICT/core/gate/utils/gate_matrix.py
def grad_for_param(self, gate_type: GateType, gate_pargs: list, symbol_pargs: dict, precision: str):
    """Return the Parameterized Quantum Gates' gradient matrices.

    Args:
        gate_type (GateType): The type of Quantum Gate.
        gate_pargs (List): The Quantum Gate's parameters.
        symbol_pargs (dict): The Quantum Gate's symbol parameters.
        precision (str): The precision of Quantum Gate, one of [double, single]

    Returns:
        list: Parameterized Quantum Gates' gradient matrices.
    """
    pargs = []
    assert len(symbol_pargs) > 0, "The symbol gate must be assigned values before calculation."
    for i in range(len(gate_pargs)):
        pargs.append(symbol_pargs[gate_pargs[i]])

    precision = np.complex128 if precision == "double" else np.complex64

    if gate_type in [GateType.u1, GateType.cu1]:
        return [np.array([
            [1, 0],
            [0, np.exp(1j * pargs[0])]
        ], dtype=precision)]

    elif gate_type == GateType.u2:
        sqrt2 = 1 / np.sqrt(2)
        grad1 = np.array([
            [1 * sqrt2,
             -np.exp(1j * pargs[1]) * sqrt2],
            [1j * np.exp(1j * pargs[0]) * sqrt2,
             1j * np.exp(1j * (pargs[0] + pargs[1])) * sqrt2]
        ], dtype=precision)
        grad2 = np.array([
            [1 * sqrt2,
             -1j * np.exp(1j * pargs[1]) * sqrt2],
            [np.exp(1j * pargs[0]) * sqrt2,
             1j * np.exp(1j * (pargs[0] + pargs[1])) * sqrt2]
        ], dtype=precision)
        return [grad1, grad2]

    elif gate_type in [GateType.u3, GateType.cu3]:
        sin_v = np.sin(pargs[0] / 2)
        cos_v = np.cos(pargs[0] / 2)
        grad1 = np.array([
            [-sin_v / 2,
             -np.exp(1j * pargs[2]) * cos_v / 2],
            [np.exp(1j * pargs[1]) * cos_v / 2,
             -np.exp(1j * (pargs[1] + pargs[2])) * sin_v / 2]
        ], dtype=precision)
        grad2 = np.array([
            [cos_v,
             -np.exp(1j * pargs[2]) * sin_v],
            [1j * np.exp(1j * pargs[1]) * sin_v,
             1j * np.exp(1j * (pargs[1] + pargs[2])) * cos_v]
        ], dtype=precision)
        grad3 = np.array([
            [cos_v,
             -1j * np.exp(1j * pargs[2]) * sin_v],
            [np.exp(1j * pargs[1]) * sin_v,
             1j * np.exp(1j * (pargs[1] + pargs[2])) * cos_v]
        ], dtype=precision)
        return [grad1, grad2, grad3]

    elif gate_type == GateType.rx:
        cos_v = -np.cos(pargs[0] / 2) / 2
        sin_v = -np.sin(pargs[0] / 2) / 2
        return [np.array([
            [sin_v, 1j * cos_v],
            [1j * cos_v, sin_v]
        ], dtype=precision)]

    elif gate_type in [GateType.ry, GateType.cry]:
        cos_v = np.cos(pargs[0] / 2) / 2
        sin_v = np.sin(pargs[0] / 2) / 2
        return [np.array([
            [-sin_v, -cos_v],
            [cos_v, -sin_v]
        ], dtype=precision)]

    elif gate_type in [GateType.rz, GateType.crz, GateType.ccrz]:
        return [np.array([
            [-1j * np.exp(-pargs[0] / 2 * 1j) / 2, 0],
            [0, 1j * np.exp(pargs[0] / 2 * 1j) / 2]
        ], dtype=precision)]

    elif gate_type == GateType.phase:
        return [np.array([
            [1, 0],
            [0, 1j * np.exp(pargs[0] * 1j)]
        ], dtype=precision)]

    elif gate_type == GateType.gphase:
        return [np.array([
            [1j * np.exp(pargs[0] * 1j), 0],
            [0, 1j * np.exp(pargs[0] * 1j)]
        ], dtype=precision)]

    elif gate_type == GateType.fsim:
        costh = np.cos(pargs[0])
        sinth = np.sin(pargs[0])
        phi = pargs[1]
        grad1 = np.array([
            [1, 0, 0, 0],
            [0, -sinth, -1j * costh, 0],
            [0, -1j * costh, -sinth, 0],
            [0, 0, 0, np.exp(-1j * phi)]
        ], dtype=precision)
        grad2 = np.array([
            [1, 0, 0, 0],
            [0, costh, -1j * sinth, 0],
            [0, -1j * sinth, costh, 0],
            [0, 0, 0, -1j * np.exp(-1j * phi)]
        ], dtype=precision)
        return [grad1, grad2]

    elif gate_type == GateType.rxx:
        costh = np.cos(pargs[0] / 2) / 2
        sinth = np.sin(pargs[0] / 2) / 2

        return [np.array([
            [-sinth, 0, 0, -1j * costh],
            [0, -sinth, -1j * costh, 0],
            [0, -1j * costh, -sinth, 0],
            [-1j * costh, 0, 0, -sinth]
        ], dtype=precision)]

    elif gate_type == GateType.ryy:
        costh = np.cos(pargs[0] / 2) / 2
        sinth = np.sin(pargs[0] / 2) / 2

        return [np.array([
            [-sinth, 0, 0, 1j * costh],
            [0, -sinth, -1j * costh, 0],
            [0, -1j * costh, -sinth, 0],
            [1j * costh, 0, 0, -sinth]
        ], dtype=precision)]

    elif gate_type == GateType.rzz:
        expth = 0.5j * np.exp(0.5j * pargs[0])
        sexpth = -0.5j * np.exp(-0.5j * pargs[0])

        return [np.array([
            [sexpth, 0, 0, 0],
            [0, expth, 0, 0],
            [0, 0, expth, 0],
            [0, 0, 0, sexpth]
        ], dtype=precision)]

    elif gate_type == GateType.rzx:
        costh = np.cos(pargs[0] / 2) / 2
        sinth = np.sin(pargs[0] / 2) / 2

        return [np.array([
            [-sinth, -1j * costh, 0, 0],
            [-1j * costh, -sinth, 0, 0],
            [0, 0, -sinth, 1j * costh],
            [0, 0, 1j * costh, -sinth]
        ], dtype=precision)]

    elif gate_type == GateType.rxy:
        costh = np.cos(pargs[1] / 2)
        sinth = np.sin(pargs[1] / 2)

        grad1 = np.array([
            [costh, -np.exp(-1j * pargs[0]) * sinth],
            [np.exp(1j * pargs[0]) * sinth, costh]
        ], dtype=precision)

        grad2 = np.array([
            [-sinth / 2, -1j * np.exp(-1j * pargs[0]) * costh / 2],
            [-1j * np.exp(1j * pargs[0]) * costh / 2, -sinth / 2]
        ], dtype=precision)
        return [grad1, grad2]

    elif gate_type == GateType.xy:
        return [np.array([
            [0, -np.exp(-1j * pargs[0])],
            [np.exp(1j * pargs[0]), 0]
        ], dtype=precision)]

    elif gate_type == GateType.xy2p:
        return [np.array([
            [1 / np.sqrt(2), -1 / np.sqrt(2) * np.exp(-1j * pargs[0])],
            [1 / np.sqrt(2) * np.exp(1j * pargs[0]), 1 / np.sqrt(2)]
        ], dtype=precision)]

    elif gate_type == GateType.xy2m:
        return [np.array([
            [1 / np.sqrt(2), 1 / np.sqrt(2) * np.exp(-1j * pargs[0])],
            [-1 / np.sqrt(2) * np.exp(1j * pargs[0]), 1 / np.sqrt(2)]
        ], dtype=precision)]

    else:
        TypeError(gate_type)
matrix_with_param
matrix_with_param(gate_type: GateType, gate_pargs: list, symbol_gate: bool, symbol_pargs: dict, precision: complex)

Return the Quantum Gates' matrix, which has parameters.

Parameters:

  • gate_type (GateType) –

    The type of Quantum Gate.

  • gate_pargs (List) –

    The Quantum Gate's parameters.

  • symbol_gate (bool) –

    Whether the Quantum Gate is a symbol gate.

  • symbol_pargs (dict) –

    The Quantum Gate's symbol parameters.

  • precision (complex) –

    The precision of Quantum Gate.

Returns:

  • np.ndarray: The Quantum Gate's matrix.

Source code in QuICT/core/gate/utils/gate_matrix.py
def matrix_with_param(
    self,
    gate_type: GateType,
    gate_pargs: list,
    symbol_gate: bool,
    symbol_pargs: dict,
    precision: complex,
):
    """ Return the Quantum Gates' matrix, which has parameters.

    Args:
        gate_type (GateType): The type of Quantum Gate.
        gate_pargs (List): The Quantum Gate's parameters.
        symbol_gate (bool): Whether the Quantum Gate is a symbol gate.
        symbol_pargs (dict): The Quantum Gate's symbol parameters.
        precision (complex): The precision of Quantum Gate.

    Returns:
        np.ndarray: The Quantum Gate's matrix.
    """
    pargs = []
    if symbol_gate:
        assert len(symbol_pargs) > 0, "The symbol gate must be assigned values before calculation."
        for i in range(len(gate_pargs)):
            pargs.append(symbol_pargs[gate_pargs[i]])
    else:
        pargs = gate_pargs

    if gate_type in [GateType.u1, GateType.cu1]:
        return np.array([
            [1, 0],
            [0, np.exp(1j * pargs[0])]
        ], dtype=precision)

    elif gate_type == GateType.u2:
        sqrt2 = 1 / np.sqrt(2)
        return np.array([
            [1 * sqrt2,
             -np.exp(1j * pargs[1]) * sqrt2],
            [np.exp(1j * pargs[0]) * sqrt2,
             np.exp(1j * (pargs[0] + pargs[1])) * sqrt2]
        ], dtype=precision)

    elif gate_type in [GateType.u3, GateType.cu3]:
        return np.array([
            [np.cos(pargs[0] / 2),
             -np.exp(1j * pargs[2]) * np.sin(pargs[0] / 2)],
            [np.exp(1j * pargs[1]) * np.sin(pargs[0] / 2),
             np.exp(1j * (pargs[1] + pargs[2])) * np.cos(pargs[0] / 2)]
        ], dtype=precision)

    elif gate_type == GateType.rx:
        cos_v = np.cos(pargs[0] / 2)
        sin_v = -np.sin(pargs[0] / 2)
        return np.array([
            [cos_v, 1j * sin_v],
            [1j * sin_v, cos_v]
        ], dtype=precision)

    elif gate_type in [GateType.ry, GateType.cry]:
        cos_v = np.cos(pargs[0] / 2)
        sin_v = np.sin(pargs[0] / 2)
        return np.array([
            [cos_v, -sin_v],
            [sin_v, cos_v]
        ], dtype=precision)

    elif gate_type in [GateType.rz, GateType.crz, GateType.ccrz]:
        return np.array([
            [np.exp(-pargs[0] / 2 * 1j), 0],
            [0, np.exp(pargs[0] / 2 * 1j)]
        ], dtype=precision)

    elif gate_type == GateType.phase:
        return np.array([
            [1, 0],
            [0, np.exp(pargs[0] * 1j)]
        ], dtype=precision)

    elif gate_type == GateType.gphase:
        return np.array([
            [np.exp(pargs[0] * 1j), 0],
            [0, np.exp(pargs[0] * 1j)]
        ], dtype=precision)

    elif gate_type == GateType.fsim:
        costh = np.cos(pargs[0])
        sinth = np.sin(pargs[0])
        phi = pargs[1]
        return np.array([
            [1, 0, 0, 0],
            [0, costh, -1j * sinth, 0],
            [0, -1j * sinth, costh, 0],
            [0, 0, 0, np.exp(-1j * phi)]
        ], dtype=precision)

    elif gate_type == GateType.rxx:
        costh = np.cos(pargs[0] / 2)
        sinth = np.sin(pargs[0] / 2)

        return np.array([
            [costh, 0, 0, -1j * sinth],
            [0, costh, -1j * sinth, 0],
            [0, -1j * sinth, costh, 0],
            [-1j * sinth, 0, 0, costh]
        ], dtype=precision)

    elif gate_type == GateType.ryy:
        costh = np.cos(pargs[0] / 2)
        sinth = np.sin(pargs[0] / 2)

        return np.array([
            [costh, 0, 0, 1j * sinth],
            [0, costh, -1j * sinth, 0],
            [0, -1j * sinth, costh, 0],
            [1j * sinth, 0, 0, costh]
        ], dtype=precision)

    elif gate_type == GateType.rzz:
        expth = np.exp(0.5j * pargs[0])
        sexpth = np.exp(-0.5j * pargs[0])

        return np.array([
            [sexpth, 0, 0, 0],
            [0, expth, 0, 0],
            [0, 0, expth, 0],
            [0, 0, 0, sexpth]
        ], dtype=precision)

    elif gate_type == GateType.rzx:
        costh = np.cos(pargs[0] / 2)
        sinth = np.sin(pargs[0] / 2)

        return np.array([
            [costh, -1j * sinth, 0, 0],
            [-1j * sinth, costh, 0, 0],
            [0, 0, costh, 1j * sinth],
            [0, 0, 1j * sinth, costh]
        ], dtype=precision)

    elif gate_type == GateType.rxy:
        costh = np.cos(pargs[1] / 2)
        sinth = np.sin(pargs[1] / 2)

        return np.array([
            [costh, -1j * np.exp(-1j * pargs[0]) * sinth],
            [-1j * np.exp(1j * pargs[0]) * sinth, costh]
        ], dtype=precision)

    elif gate_type == GateType.xy:
        return np.array([
            [0, -1j * np.exp(-1j * pargs[0])],
            [-1j * np.exp(1j * pargs[0]), 0]
        ], dtype=precision)

    elif gate_type == GateType.xy2p:
        return np.array([
            [1 / np.sqrt(2), -1j / np.sqrt(2) * np.exp(-1j * pargs[0])],
            [-1j / np.sqrt(2) * np.exp(1j * pargs[0]), 1 / np.sqrt(2)]
        ], dtype=precision)

    elif gate_type == GateType.xy2m:
        return np.array([
            [1 / np.sqrt(2), 1j / np.sqrt(2) * np.exp(-1j * pargs[0])],
            [1j / np.sqrt(2) * np.exp(1j * pargs[0]), 1 / np.sqrt(2)]
        ], dtype=precision)

    else:
        TypeError(gate_type)

GateParametersAssignedError

GateParametersAssignedError(msg: str = None)

Bases: QuICTException

Gate's Parameter Error.

Source code in QuICT/tools/exception/core/gate_exception.py
def __init__(self, msg: str = None):
    super().__init__(1017, msg)

GateQubitAssignedError

GateQubitAssignedError(msg: str = None)

Bases: QuICTException

Gate's qubit assign Error.

Source code in QuICT/tools/exception/core/gate_exception.py
def __init__(self, msg: str = None):
    super().__init__(1016, msg)

InverseGate

The class of all Inverse functions for Quantum Gate.

get_inverse_gate classmethod
get_inverse_gate(gate_type: GateType, pargs: list) -> tuple

Get Inverse Quantum Gate Information.

Parameters:

  • gate_type (GateType) –

    The type of Quantum Gate.

  • pargs (list) –

    The parameters of Quantum Gate.

Returns:

  • tuple ( tuple ) –

    The inverse gate info.

Source code in QuICT/core/gate/utils/gate_matrix.py
@classmethod
def get_inverse_gate(cls, gate_type: GateType, pargs: list) -> tuple:
    """ Get Inverse Quantum Gate Information.

    Args:
        gate_type (GateType): The type of Quantum Gate.
        pargs (list): The parameters of Quantum Gate.

    Returns:
        tuple: The inverse gate info.
    """
    if len(pargs) == 0:
        if gate_type in cls.__GATE_INVERSE_MAP.keys():
            inverse_args = cls.__GATE_INVERSE_MAP[gate_type]

            return inverse_args if isinstance(inverse_args, tuple) else (inverse_args, pargs)
    else:
        inv_params = None
        if gate_type in cls.__INVERSE_GATE_WITH_NEGATIVE_PARAMS:
            inv_params = [p * -1 for p in pargs]
        elif gate_type == GateType.u2:
            inv_params = [np.pi - pargs[1], np.pi - pargs[0]]
        elif gate_type in [GateType.u3, GateType.cu3]:
            inv_params = [pargs[0], np.pi - pargs[2], np.pi - pargs[1]]
        elif gate_type in [GateType.xy, GateType.xy2p, GateType.xy2m]:
            inv_params = [pargs[0] + np.pi]
        elif gate_type == GateType.rxy:
            inv_params = [pargs[0] + np.pi, pargs[1]]

        if inv_params is not None:
            return (gate_type, inv_params)

    return None, pargs

Kraus

Kraus(matrix: ndarray)

Bases: BasicGate

Source code in QuICT/core/gate/gate.py
def __init__(self, matrix: np.ndarray):
    super().__init__(
        controls=0, targets=1, params=0, type_=GateType.kraus
    )
    self._matrix = matrix
expand
expand(qubits: Union[int, list], device: str = 'CPU') -> bool

expand self matrix into the circuit's unitary linear space. If input qubits is integer, please make sure the indexes of current gate is within [0, qubits).

Parameters:

  • qubits (Union[int, list]) –

    the total number of qubits of the target circuit or the indexes of expand qubits.

Source code in QuICT/core/gate/gate.py
def expand(self, qubits: Union[int, list], device: str = "CPU") -> bool:
    """ expand self matrix into the circuit's unitary linear space. If input qubits is integer, please make sure
    the indexes of current gate is within [0, qubits).

    Args:
        qubits Union[int, list]: the total number of qubits of the target circuit or the indexes of expand qubits.
    """
    if isinstance(qubits, int):
        qubits = list(range(qubits))

    qubits_num = len(qubits)
    if qubits_num == self.controls + self.targets:
        return self.matrix

    assert qubits_num > self.controls + self.targets, GateQubitAssignedError(
        "The expand qubits' num should >= gate's qubits num."
    )
    gate_args = self.cargs + self.targs
    if len(gate_args) == 0:     # Deal with not assigned quantum gate
        gate_args = [qubits[i] for i in range(self.controls + self.targets)]

    updated_args = [qubits.index(garg) for garg in gate_args]
    expand_matrix = [
        matrix_product_to_circuit(mat, updated_args, qubits_num, device) for mat in self._matrix
    ]

    return expand_matrix

MatrixType

Bases: Enum

Different Type of quantum gates' matrix

  • normal: based type of matrix

    Single Qubit:

    \[ \begin{bmatrix} v_{00} & v_{01} \\ v_{10} & v_{11} \\ \end{bmatrix} \]

    Bi-Qubits (1 control + 1 target):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & v_{00} & v_{01} \\ 0 & 0 & v_{10} & v_{11} \\ \end{bmatrix} \]
  • diagonal: diagonal matrix

    Single Qubit:

    \[ \begin{bmatrix} v_{00} & 0 \\ 0 & v_{11} \\ \end{bmatrix} \]

    Bi-Qubits (1 control + 1 target):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & v_{00} & 0 \\ 0 & 0 & 0 & v_{11} \\ \end{bmatrix} \]

    Bi-Qubits (2 targets):

    \[ \begin{bmatrix} v_{00} & 0 & 0 & 0 \\ 0 & v_{11} & 0 & 0 \\ 0 & 0 & v_{22} & 0 \\ 0 & 0 & 0 & v_{33} \\ \end{bmatrix} \]

    Tri-Qubits (2 controls + 1 target):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & v_{00} & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & v_{11} \\ \end{bmatrix} \]
  • control: control diagonal matrix

    Single Qubit:

    \[ \begin{bmatrix} 1 & 0 \\ 0 & v_{00} \\ \end{bmatrix} \]

    Bi-Qubits (1 control + 1 target):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & v_{00} \\ \end{bmatrix} \]
  • swap: swap quantum gates' matrix

    Single Qubit:

    \[ \begin{bmatrix} 0 & 1 \\ 1 & 0 \\ \end{bmatrix} \]

    Bi-Qubits (2 targets):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \]

    Tri-Qubits (1 controls + 2 targets):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \\ \end{bmatrix} \]
  • reverse; reverse matrix

    Single Qubit:

    \[ \begin{bmatrix} 0 & v_{01} \\ v_{10} & 0 \\ \end{bmatrix} \]

    Bi-Qubits (1 control + 1 target):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & v_{01} \\ 0 & 0 & v_{10} & 0 \\ \end{bmatrix} \]

    Tri-Qubits (2 controls + 1 target):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & v_{01} \\ 0 & 0 & 0 & 0 & 0 & 0 & v_{10} & 0 \\ \end{bmatrix} \]
  • special: no matrix Gate, such as \(Measure, Reset, Barrier, Perm\)

  • diag_diag: 2-qubits diagonal matrix [TODO: Remove in open_test]

    Bi-Qubits (2 targets):

    \[ \begin{bmatrix} v_{00} & 0 & 0 & 0 \\ 0 & v_{11} & 0 & 0 \\ 0 & 0 & v_{22} & 0 \\ 0 & 0 & 0 & v_{33} \\ \end{bmatrix} \]
  • ctrl_normal: control-normal mixed quantum gate's matrix Bi-Qubits (2 targets):

    \[ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & v_{00} & v_{01} & 0 \\ 0 & v_{10} & v_{11} & 0 \\ 0 & 0 & 0 & v_{33} \\ \end{bmatrix} \]
  • normal-normal: normal-normal mixed quantum gate's matrix Bi-Qubits (2 targets):

    \[ \begin{bmatrix} v_{00} & 0 & 0 & v_{03} \\ 0 & v_{11} & v_{12} & 0 \\ 0 & v_{21} & v_{22} & 0 \\ v_{30} & 0 & 0 & v_{33} \\ \end{bmatrix} \]
  • diagonal-normal: diagonal-normal mixed quantum gate's matrix Bi-Qubits (2 targets):

    \[ \begin{bmatrix} v_{00} & v_{01} & 0 & 0 \\ v_{10} & v_{11} & 0 & 0 \\ 0 & 0 & v_{22} & v_{23} \\ 0 & 0 & v_{32} & v_{33} \\ \end{bmatrix} \]

MultiControlGate

MultiControlGate(controls: int, gate_type: GateType, precision: str = 'double', params: list = [])

Bases: BasicGate

The multi-control qubits quantum gate.

Parameters:

  • controls (int) –

    The number of control qubits

  • gate_type (GateType) –

    The based quantum gate

  • precision (str, default: 'double' ) –

    The precison for quantum gate. Defaults to "double".

  • params (list, default: [] ) –

    The parameters for based quantum gate. Defaults to [].

Source code in QuICT/core/gate/gate.py
def __init__(self, controls: int, gate_type: GateType, precision: str = "double", params: list = []):
    """
    Args:
        controls (int): The number of control qubits
        gate_type (GateType): The based quantum gate
        precision (str, optional): The precison for quantum gate. Defaults to "double".
        params (list, optional): The parameters for based quantum gate. Defaults to [].
    """
    assert controls >= 0, ValueError("MultiControlGate.controls", ">= 0", controls)
    self._multi_controls = controls
    if gate_type not in GATEINFO_MAP.keys():
        raise TypeError("MultiControlGate.gate_type", "only support for QuICT Gate", gate_type)

    gate_info = list(GATEINFO_MAP[gate_type])
    gate_info[0] += controls
    super().__init__(*gate_info, params, precision)
inverse
inverse()

the inverse of the quantum gate, if there is no inverse gate, return itself.

Return

BasicGate: the inverse of the gate

Source code in QuICT/core/gate/gate.py
def inverse(self):
    """ the inverse of the quantum gate, if there is no inverse gate, return itself.

    Return:
        BasicGate: the inverse of the gate
    """
    if self.symbol_gate:
        assert self.assigned_value, "The symbol gate must be assigned values before calculation."
        pargs = []
        for i in range(self.params):
            pargs.append(self.symbol_pargs[self.pargs[i]])
    else:
        pargs = self.pargs
    inverse_gargs, inverse_pargs = InverseGate.get_inverse_gate(self.type, pargs)

    # Deal with inverse_gargs
    if inverse_gargs is None:
        return self

    inverse_gate = MultiControlGate(self._multi_controls, inverse_gargs, self.precision, params=inverse_pargs)
    gate_args = self.cargs + self.targs
    if len(gate_args) > 0:
        inverse_gate & gate_args

    return inverse_gate

MultiControlToffoli

MultiControlToffoli(aux_usage='no_aux')

Bases: object

Divided by the usages of auxiliary qubits, here are 4 implementations of multi-control Toffoli gates.

Detailed description of each synthesis algorithm could be found in the backend.

Parameters:

  • aux_usage (str, default: 'no_aux' ) –

    4 different usages of auxiliary qubits could be chosen, as listed 'no_aux': No auxiliary qubits are used

    'one_clean_aux': 1 clean auxiliary qubit is used

    'one_dirty_aux': 1 dirty auxiliary qubit is used

    'half_dirty_aux': more than half of all qubits are used as auxiliary qubits, which could be dirty

Source code in QuICT/core/gate/multicontrol_toffoli.py
def __init__(self, aux_usage='no_aux'):
    """
    Args:
        aux_usage (str): 4 different usages of auxiliary qubits could be chosen, as listed
            'no_aux': No auxiliary qubits are used  \n
            'one_clean_aux': 1 clean auxiliary qubit is used  \n
            'one_dirty_aux': 1 dirty auxiliary qubit is used  \n
            'half_dirty_aux': more than half of all qubits are used as auxiliary qubits, which could be dirty
    """
    assert aux_usage in self.__AUX_USAGES, TypeError("MultiControlToffoli.aux_usage", self.__AUX_USAGES, aux_usage)

    self.aux_usage = aux_usage
__call__
__call__(control, aux=0) -> CompositeGate

By default, the qubit arrangement would be control qubits, target qubit, auxiliary qubits (if exists)

Parameters:

  • control (int) –

    the number of control qubits

  • aux (int, default: 0 ) –

    only works when 'half_dirty_aux' is chosen, the number of auxiliary qubits

Returns:

Source code in QuICT/core/gate/multicontrol_toffoli.py
def __call__(self, control, aux=0) -> CompositeGate:
    """
    By default, the qubit arrangement would be control qubits, target qubit, auxiliary qubits (if exists)

    Args:
        control (int): the number of control qubits
        aux (int, optional): only works when 'half_dirty_aux' is chosen, the number of auxiliary qubits

    Returns:
        CompositeGate: the mct gates
    """
    # Special cases, no MCT would be executed.
    if control == 0:
        gates = CompositeGate()
        X | gates(0)
        return gates
    if control == 1:
        gates = CompositeGate()
        CX | gates([0, 1])
        return gates
    if control == 2:
        gates = CompositeGate()
        CCX | gates([0, 1, 2])
        return gates

    # Otherwise
    if self.aux_usage == 'no_aux':
        return MCTWithoutAux().execute(control + 1)
    elif self.aux_usage == 'one_clean_aux':
        return MCTOneAux().execute(control + 2)
    elif self.aux_usage == 'one_dirty_aux':
        return MCTLinearOneDirtyAux().execute(control + 2)
    else:
        qubit = control + aux + 1
        if control > (qubit // 2) + (1 if qubit % 2 == 1 else 0):
            raise ValueError("MultiControlToffoli.control", f"<= ceil({qubit}/2)", control)
        controls = [i for i in range(control)]
        auxs = [i for i in range(control + 1, qubit)]
        return MCTLinearHalfDirtyAux.assign_qubits(qubit, control, controls, auxs, control)

Parameter

Parameter(symbol: str)

Used to represent trainable parameters of parameterized quantum gates or parameterized quantum circuits.

Note

Only supports number multiplication operation.

Parameters:

  • symbol (str) –

    The symbol.

Examples:

>>> from QuICT.core.circuit import Circuit
>>> from QuICT.core.gate import *
>>> cir = Circuit(3)
>>> H | cir
>>> Rx("x") | cir(0)
>>> Rx(Parameter("x") * 0.3) | cir(2)
>>> Rzz("y") | cir([0, 1])
>>> cir.draw("command")
        ┌───┐ ┌───────┐
q_0: |0>┤ h ├─┤ rx(x) ├───■──────
        ├───┤ └───────┘   │ZZ(y)
q_1: |0>┤ h ├─────────────■──────
        ├───┤┌──────────┐
q_2: |0>┤ h ├┤ rx(0.3x) ├────────
        └───┘└──────────┘

Initialize a Parameter instance.

Source code in QuICT/core/utils/parameter.py
def __init__(self, symbol: str):
    """Initialize a Parameter instance."""
    self._symbol = symbol
    self._expr = symbol
    self._multiplier = 1.0
    self._dx = 1.0
dx property
dx: float

The derivative of the parameter on the symbol.

Returns:

  • float ( float ) –

    The derivative.

expr property
expr: str

The expression of the parameter.

Returns:

  • str ( str ) –

    The expression of the parameter.

multiplier property
multiplier: float

The multiplier of the symbol.

Returns:

  • float ( float ) –

    The multiplier.

symbol property
symbol: str

The symbol in the parameter.

Returns:

  • str ( str ) –

    The symbol.

__lt__
__lt__(other)

Determine whether the current parameter is smaller than another parameter.

Parameters:

Returns:

  • bool

    If True, the current parameter is smaller than another parameter.

Source code in QuICT/core/utils/parameter.py
def __lt__(self, other):
    """Determine whether the current parameter is smaller than another parameter.

    Args:
        other (Parameter): Another parameter.

    Returns:
        bool: If True, the current parameter is smaller than another parameter.
    """
    assert isinstance(other, Parameter), TypeError(
        "Parameter.__lt__.other", "Parameter", type(other)
    )
    if self._symbol == other._symbol:
        if self._multiplier < other._multiplier:
            return True
    else:
        if self._expr < other._expr:
            return True
    return False
__mul__
__mul__(other)

Multiply a number.

Parameters:

  • other (Number) –

    The multiplier.

Returns:

  • Parameter

    The new parameter after number multiplication operation.

Source code in QuICT/core/utils/parameter.py
def __mul__(self, other):
    """Multiply a number.

    Args:
        other (numbers.Number): The multiplier.

    Returns:
        Parameter: The new parameter after number multiplication operation.
    """
    assert isinstance(other, numbers.Number), TypeError(
        "Parameter.__mul__.other", "numbers.Number", type(other)
    )
    new_param = Parameter(self._symbol)
    new_param._expr = str(other) + self._symbol
    new_param._multiplier = self._multiplier * other
    new_param._dx = self._dx * other
    return new_param
__rmul__
__rmul__(other)

Multiply a number.

Parameters:

  • other (Number) –

    The multiplier.

Returns:

  • Parameter

    The new parameter after number multiplication operation.

Source code in QuICT/core/utils/parameter.py
def __rmul__(self, other):
    """Multiply a number.

    Args:
        other (numbers.Number): The multiplier.

    Returns:
        Parameter: The new parameter after number multiplication operation.
    """
    return self.__mul__(other)
__str__
__str__() -> str

Return the expression of the parameter.

Source code in QuICT/core/utils/parameter.py
def __str__(self) -> str:
    """Return the expression of the parameter."""
    return self._expr

Perm

Perm(targets: int, params: list)

Bases: BasicGate

Parameters:

  • n (int) –

    the number of target qubits

  • params (list[int]) –

    the list of index, and the index represent which should be 1.

Returns:

  • PermGate

    the Perm Gate.

Source code in QuICT/core/gate/gate.py
def __init__(self, targets: int, params: list):
    """
    Args:
        n (int): the number of target qubits
        params (list[int]): the list of index, and the index represent which should be 1.

    Returns:
        PermGate: the Perm Gate.
    """
    if not isinstance(params, list) or not isinstance(targets, int):
        raise TypeError(f"targets must be int {type(targets)}, params must be list {type(params)}")

    assert len(params) == targets, GateParametersAssignedError("the length of params must equal to targets")
    assert len(set(params)) == targets, ValueError("PermGate.params", "have no duplicated value", params)

    pargs = []
    for parg in params:
        if not isinstance(parg, int):
            raise TypeError("PermGate.params.values", "int", type(parg))

        if parg < 0 or parg >= targets:
            raise ValueError("PermGate.params.values", f"[0, {targets}]", parg)

        pargs.append(parg)

    super().__init__(0, targets, targets, GateType.perm, MatrixType.normal, pargs)

QASMError

QASMError(other: str = None, line: str = None, file: str = None)

Bases: QuICTException

QASM Error.

Source code in QuICT/tools/exception/core/circuit_exception.py
def __init__(self, other: str = None, line: str = None, file: str = None):
    msg = "Qasm error:{} \n in line:{} \n error file:{}".format(other, line, file)
    super().__init__(1009, msg)

TypeError

TypeError(locate: str, require: str, given: str)

Bases: QuICTException

Type Error in Core Module, including Layout, Fidelity, Qureg, ...

Source code in QuICT/tools/exception/core/core_exception.py
6
7
8
def __init__(self, locate: str, require: str, given: str):
    msg = f"{locate}'s type should be {require}, but given {given}."
    super().__init__(1001, msg)

UniformlyControlGate

UniformlyControlGate(target_gate: GateType = GateType.unitary)

Bases: object

Uniformly Ry, Rz or one-qubit Unitary gate

Detailed description of each synthesis algorithm could be found in the backend.

Parameters:

  • target_gate (GateType, default: unitary ) –

    type of target gate, could be Ry, Rz or Unitary

Source code in QuICT/core/gate/uniformly_control_gate.py
def __init__(self, target_gate: GateType = GateType.unitary):
    """
    Args:
        target_gate (GateType): type of target gate, could be Ry, Rz or Unitary
    """
    assert target_gate in [GateType.ry, GateType.rz, GateType.unitary], \
        TypeError("UniformlyControlGate.target_gate", [GateType.ry, GateType.rz, GateType.unitary], target_gate)
    self.target_gate = target_gate
__call__
__call__(arg_list) -> CompositeGate

Parameters:

  • arg_list (list) –

    a list of angles for Ry and Rz or a list of 2*2 unitaries for Unitary

Returns:

  • CompositeGate ( CompositeGate ) –

    uniformly control target gate

Source code in QuICT/core/gate/uniformly_control_gate.py
def __call__(self, arg_list) -> CompositeGate:
    """
    Args:
        arg_list (list): a list of angles for Ry and Rz or a list of 2*2 unitaries for Unitary

    Returns:
        CompositeGate: uniformly control target gate
    """
    if self.target_gate in [GateType.ry, GateType.rz]:
        return UniformlyRotation(self.target_gate).execute(arg_list)
    else:
        return UniformlyUnitary().execute(arg_list)

Unitary

Unitary(matrix: Union[list, ndarray], matrix_type: MatrixType = None, name: str = None, is_kraus: bool = False)

Bases: BasicGate

The class about the Unitary Quantum Gate

Parameters:

  • matrix (Union[list, ndarray]) –

    The unitary matrix.

  • matrix_type (MatrixType, default: None ) –

    The matrix's type. Defaults to None.

  • name (str, default: None ) –

    The unitary gate's name

Source code in QuICT/core/gate/gate.py
def __init__(
    self,
    matrix: Union[list, np.ndarray],
    matrix_type: MatrixType = None,
    name: str = None,
    is_kraus: bool = False
):
    """
    Args:
        matrix (Union[list, np.ndarray]): The unitary matrix.
        matrix_type (MatrixType, optional): The matrix's type. Defaults to None.
        name (str): The unitary gate's name
    """
    self._name = name if name is not None else "U_" + unique_id_generator()
    self._is_kraus = is_kraus

    # Validate matrix type
    self.validate_matrix_shape(matrix)

    # Validate Matrix Type
    n = int(np.log2(matrix.shape[0]))
    validate_matrix_type, targets = self.validate_matrix_type(matrix)
    if matrix_type is not None:
        validate_matrix_type = matrix_type

    precision = "double" if matrix.dtype == np.complex128 else "single"
    super().__init__(
        controls=n - targets, targets=targets, params=0,
        type_=GateType.unitary, matrix_type=validate_matrix_type, precision=precision
    )
    self._matrix = matrix
validate_matrix_type staticmethod
validate_matrix_type(matrix: ndarray) -> MatrixType

Check the matrix's type about given unitary matrix

Parameters:

  • matrix (ndarry) –

    The given unitary matrix

Returns:

Source code in QuICT/core/gate/gate.py
@staticmethod
def validate_matrix_type(matrix: np.ndarray) -> MatrixType:
    """ Check the matrix's type about given unitary matrix

    Args:
        matrix (np.ndarry): The given unitary matrix

    Returns:
        MatrixType: The matrix type
    """
    length = matrix.shape[0]
    diagonal_value = np.diag(matrix)
    reverse_diag_value = np.diag(np.fliplr(matrix))

    if np.allclose(matrix, np.diag(diagonal_value)):
        matrix_type = MatrixType.diagonal
    elif np.allclose(np.fliplr(matrix), np.diag(reverse_diag_value)):
        matrix_type = MatrixType.reverse
    else:
        matrix_type = MatrixType.normal

    if matrix_type == MatrixType.reverse:
        return matrix_type, int(np.log2(length))

    target_value = reverse_diag_value if matrix_type == MatrixType.reverse else diagonal_value
    controls = (target_value != 1).argmax()
    # Validate Identity Matrix or Control Matrix
    if controls == 0 and target_value[0] == 1 and matrix_type == MatrixType.diagonal:
        return MatrixType.identity, int(np.log2(length))

    if (
        matrix_type == MatrixType.normal and
        not np.allclose(matrix[:controls, :controls], np.diag(target_value[:controls]))
    ):
        controls = 0

    targets = length - controls
    if targets == 1 and matrix_type == MatrixType.diagonal:
        return MatrixType.control, 1

    # Get Control qubits and target qubits
    target_number = int(np.ceil(np.log2(length - controls)))
    return matrix_type, target_number

gate_builder

gate_builder(gate_type, precision: str = 'double', params: list = [], random_params: bool = False) -> BasicGate

Build the target Quantum Gate.

Parameters:

  • gate_type (GateType) –

    The gate's type.

  • precision (str, default: 'double' ) –

    The gate's precision. Defaults to "double".

  • params (list, default: [] ) –

    The gate's parameters. Defaults to [].

  • random_params (bool, default: False ) –

    Whether using random parameters. Defaults to False.

Returns:

  • BasicGate ( BasicGate ) –

    The class of target quantum gate

Source code in QuICT/core/gate/gate.py
def gate_builder(gate_type, precision: str = "double", params: list = [], random_params: bool = False) -> BasicGate:
    """ Build the target Quantum Gate.

    Args:
        gate_type (GateType): The gate's type.  \n
        precision (str, optional): The gate's precision. Defaults to "double".  \n
        params (list, optional): The gate's parameters. Defaults to [].  \n
        random_params (bool, optional): Whether using random parameters. Defaults to False.

    Returns:
        BasicGate: The class of target quantum gate
    """
    if gate_type not in GATEINFO_MAP.keys():
        raise TypeError("gate_builder.gate_type", "only support for fixed qubits gate", gate_type)

    gate_info = GATEINFO_MAP[gate_type]
    if random_params:
        params = list(np.random.uniform(0, 2 * np.pi, gate_info[2]))

    return BasicGate(
        *gate_info, params, precision
    )

matrix_product_to_circuit

matrix_product_to_circuit(gate_matrix: ndarray, gate_args: Union[int, list], qubits: int, device: str = 'CPU')

Expand gate matrix with the number of qubits

Parameters:

  • gate_matrix (ndarray) –

    The gate's matrix.

  • gate_args (Union[int, list]) –

    The gate's qubit indexes.

  • qubits (int) –

    The qubits' number

  • device(str, (optional) –

    Generate matrix in GPU or not. Default to CPU.

Returns:

  • np.array: the expanded gate's 2-D matrix

Source code in QuICT/core/utils/utils.py
def matrix_product_to_circuit(
    gate_matrix: np.ndarray,
    gate_args: Union[int, list],
    qubits: int,
    device: str = "CPU"
):
    """ Expand gate matrix with the number of qubits

    Args:
        gate_matrix (np.ndarray): The gate's matrix.
        gate_args Union[int, list]: The gate's qubit indexes.
        qubits (int): The qubits' number
        device(str, optional): Generate matrix in GPU or not. Default to CPU.

    Returns:
        np.array: the expanded gate's 2-D matrix
    """
    if isinstance(gate_args, int):
        gate_args = [gate_args]

    n = 1 << qubits
    xor = n - 1
    new_values = np.zeros((n, n), dtype=gate_matrix.dtype)
    assert gate_matrix.shape == (1 << len(gate_args), 1 << len(gate_args))
    for arg in gate_args:
        assert arg >= 0 and arg < qubits and isinstance(arg, int)

    datas = np.zeros(n, dtype=int)
    for i in range(n):
        nowi = 0
        for t_idx, targ in enumerate(gate_args):
            assert targ >= 0 and targ < qubits
            k = qubits - 1 - targ
            if (1 << k) & i != 0:
                nowi += (1 << (len(gate_args) - 1 - t_idx))

        datas[i] = nowi

    for i in gate_args:
        xor = xor ^ (1 << (qubits - 1 - i))

    for i in range(n):
        nowi = datas[i]
        for j in range(n):
            if (i & xor) == (j & xor):
                nowj = datas[j]
                new_values[i][j] = gate_matrix[nowi][nowj]

    if device == "GPU":
        import cupy as cp
        new_values = cp.array(new_values)

    return new_values

unique_id_generator

unique_id_generator()

Generate unique ID for result.

Source code in QuICT/core/utils/id_generator.py
4
5
6
7
8
9
def unique_id_generator():
    """ Generate unique ID for result. """
    u_id = uuid.uuid4()
    u_id = str(u_id).replace("-", "")

    return u_id[-6:]