跳转至

utility

QuICT.qcda.synthesis.unitary_decomposition.utility

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

Circuit

Circuit(wires, name: str = None, topology: Layout = None, ancilla_qubits: List[int] = None, precision: bool = 'double')

Bases: CircuitBased

Implement a Quantum Circuit. Circuit is the core part of the framework.

Parameters:

  • wires (Union[Qureg, int]) –

    the number of qubits for the circuit.

  • name (str, default: None ) –

    the name of the circuit.

  • topology (Layout, default: None ) –

    The topology of the circuit. If it is empty, it will be seemed as fully connected.

  • ancilla_qubits (list<int>, default: None ) –

    The indexes of ancilla qubits for current circuit.

Source code in QuICT/core/circuit/circuit.py
def __init__(
    self,
    wires,
    name: str = None,
    topology: Layout = None,
    ancilla_qubits: List[int] = None,
    precision: bool = "double"
):
    """
    Args:
        wires (Union[Qureg, int]): the number of qubits for the circuit.
        name (str): the name of the circuit.
        topology (Layout): The topology of the circuit. If it is empty, it will be seemed as fully connected. \n
        ancilla_qubits (list<int>): The indexes of ancilla qubits for current circuit.
    """
    if isinstance(wires, int):
        wires = Qureg(wires)

    super().__init__(name, wires, precision=precision)
    self._ancillae_qubits = []
    self._topology = None
    self._checkpoints = []
    self._logger = _logger
    self._logger.debug(f"Initial Quantum Circuit {name} with {len(self._qubits)} qubits.")

    if ancilla_qubits is not None:
        self.ancilla_qubits = ancilla_qubits
        self._logger.debug(f"The Indexes of Ancilla Qubits are {self.ancilla_qubits}.")

    if topology is not None:
        assert topology.qubit_number <= len(self._qubits), \
            TypeError("The qubits for Layout", f"less than {len(self._qubits)}", f"{topology.qubit_number}")
        self.topology = topology
        self._logger.debug(f"The Layout for Quantum Circuit is {self.topology}.")
ancilla_qubits property writable
ancilla_qubits: List[int]

Return the list of ancilla qubits indexes.

qubits property
qubits: Qureg

Return the Qureg of current Circuit.

topology property writable
topology: Layout

Return the Topology of current Circuit.

__call__
__call__(indexes: object)

assigned a smaller qureg for this circuit

Parameters:

  • indexes (object) –

    the indexes passed in, it can have follow form: 1) int 2) list 3) Qubit 4) Qureg

Returns:

  • Qureg

    the qureg correspond to the indexes

Raises:

  • TypeError

    the type of indexes is error.

Source code in QuICT/core/circuit/circuit.py
def __call__(self, indexes: object):
    """ assigned a smaller qureg for this circuit

    Args:
        indexes: the indexes passed in, it can have follow form:
            1) int
            2) list<int>
            3) Qubit
            4) Qureg

    Returns:
        Qureg: the qureg correspond to the indexes

    Exceptions:
        TypeError: the type of indexes is error.
    """
    if isinstance(indexes, (Qubit, Qureg)):
        indexes = self.qubits.index(indexes)

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

    self._qubit_indexes_validation(indexes)
    self._pointer = indexes
    return self
__del__
__del__()

release the memory

Source code in QuICT/core/circuit/circuit.py
def __del__(self):
    """ release the memory """
    self._gates = None
    self._qubits = None
    self._ancillae_qubits = None
    self._logger = None
    self._topology = None
__getitem__
__getitem__(item)

to fit the slice operator, overloaded this function.

get a smaller qureg/qubit from this circuit

Parameters:

  • item (int / slice) –

    slice passed in.

Return

Qubit/Qureg: the result or slice

Source code in QuICT/core/circuit/circuit.py
def __getitem__(self, item):
    """ to fit the slice operator, overloaded this function.

    get a smaller qureg/qubit from this circuit

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

    Return:
        Qubit/Qureg: the result or slice
    """
    return self.qubits[item]
__or__
__or__(targets)

Deal the operator '|', Use the syntax "circuit/CompositeGate | circuit" to add a Quantum Circuit into the other one.

Note that if not assigned the target qubits, it will depending on the Qureg to match the Quantum Circuit.

For the Qureg which not in the target Quantum Circuit, they will be treated as new extra qubits add into the target Quantum Circuit.

Example

circuit_a = Circuit(3) \ circuit_a | circuit([1, 3, 4]) Add a 3-qubits Quantum Circuit into "circuit" with qubit index [1, 3, 4]

Parameters:

  • targets (Circuit) –

    the targets Quantum Circuit acts on.

Raise

TypeError: the type of targets is wrong

Source code in QuICT/core/circuit/circuit.py
def __or__(self, targets):
    """ Deal the operator '|', Use the syntax "circuit/CompositeGate | circuit" to add a Quantum Circuit into
    the other one.

        Note that if not assigned the target qubits, it will depending on the Qureg to match the Quantum Circuit.
    For the Qureg which not in the target Quantum Circuit, they will be treated as new extra qubits add into the
    target Quantum Circuit.

    Example:
        circuit_a = Circuit(3) \\
        circuit_a | circuit([1, 3, 4])   Add a 3-qubits Quantum Circuit into "circuit" with qubit index [1, 3, 4]

    Args:
        targets (Circuit): the targets Quantum Circuit acts on.

    Raise:
        TypeError: the type of targets is wrong
    """
    assert isinstance(targets, Circuit), TypeError("Circuit.or", "Circuit", type(targets))

    targets.extend(self)
add_qubit
add_qubit(qubits: Union[Qureg, Qubit, int], is_ancillary_qubit: bool = False)

add the extra qubits in circuit.

Parameters:

  • qubits (Union[Qureg, Qubit, int]) –

    The new qubits, if it is int, means the number of new qubits.

  • is_ancillae_qubit (bool) –

    whether the given qubits is ancillae, default to False.

Source code in QuICT/core/circuit/circuit.py
def add_qubit(self, qubits: Union[Qureg, Qubit, int], is_ancillary_qubit: bool = False):
    """ add the extra qubits in circuit.

    Args:
        qubits Union[Qureg, Qubit, int]: The new qubits, if it is int, means the number of new qubits. \n
        is_ancillae_qubit (bool, optional): whether the given qubits is ancillae, default to False.
    """
    assert isinstance(qubits, (Qureg, Qubit, int)), \
        TypeError("Circuit.add_qubit", "[Qureg, Qubit, int]", type(qubits))
    if isinstance(qubits, int):
        assert qubits > 0, IndexExceedError("Circuit.add_qubit", ">= 0", {qubits})
        qubits = Qureg(qubits)
    elif isinstance(qubits, Qubit):
        qubits = Qureg(qubits)

    self._qubits = self._qubits + qubits
    if is_ancillary_qubit:
        self._ancillae_qubits += list(range(self.width() - len(qubits), self.width()))

    self._logger.debug(f"Quantum Circuit {self._name} add {len(qubits)} qubits.")
append
append(op: Union[BasicGate, Operator])

Add a Quantum Gate or Operator into current circuit.

Parameters:

Source code in QuICT/core/circuit/circuit.py
def append(self, op: Union[BasicGate, Operator]):
    """ Add a Quantum Gate or Operator into current circuit.

    Args:
        op (Union[BasicGate, Operator]): The Quantum Gate or Operator
    """
    if isinstance(op, BasicGate):
        self._add_gate(op)
    elif isinstance(op, Operator):
        self._add_operator(op)
    else:
        raise TypeError(
            "Circuit.append.gate", "BasicGate/Operator", {type(op)}
        )

    self._pointer = None
depth
depth()

The depth of the circuit.

Returns:

  • int

    the depth

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

    Returns:
        int: the depth
    """
    return self._gates.depth(self.width())
extend
extend(gates: Union[Circuit, CompositeGate])

Add a CompositeGate/Circuit to the circuit.

Parameters:

Source code in QuICT/core/circuit/circuit.py
def extend(self, gates: Union[Circuit, CompositeGate]):
    """ Add a CompositeGate/Circuit to the circuit.

    Args:
        gates (Union[BasicGate, CompositeGate]): the compositegate or circuit to be added to the circuit
    """
    if gates.size() == 0:
        return

    assert isinstance(gates, (Circuit, CompositeGate)), \
        "The circuit extend method only accept CompositeGate or Circuit."
    if gates.size() == 0:
        return

    if isinstance(gates, Circuit):
        gates = gates.to_compositegate()

    if self._pointer is not None:
        gate_args = gates.width()
        if len(self._pointer) > gate_args:
            self._pointer = [self._pointer[qidx] for qidx in gates.qubits]

        assert gate_args == len(self._pointer), GateQubitAssignedError(
            f"{gates.name} need {gate_args} indexes, but given {len(self._pointer)}"
        )

        gate_qidxes = self._pointer[:]
        copy_gate = gates.copy() & gate_qidxes
    else:
        gate_qidxes = gates.qubits
        self._qubit_indexes_validation(gate_qidxes)
        copy_gate = gates.copy()

    self._gates.extend(copy_gate)
    self._pointer = None
get_DAG_circuit
get_DAG_circuit() -> DAGCircuit

Translate a quantum circuit to a directed acyclic graph via quantum gates dependencies (The commutation of quantum gates).

The nodes in the graph represented the quantum gates, and the edges means the two quantum gates is non-commutation. In other words, a directed edge between node A with quantum gate GA and node B with quantum gate GB, the quantum gate GA does not commute with GB.

The nodes in the graph have the following attributes: 'name', 'gate', 'cargs', 'targs', 'qargs', 'successors', 'predecessors'.

Reference:

[1] Iten, R., Moyard, R., Metger, T., Sutter, D. and Woerner, S., 2020. Exact and practical pattern matching for quantum circuit optimization. arXiv:1909.05270 <https://arxiv.org/abs/1909.05270>_

Returns:

  • DAGCircuit ( DAGCircuit ) –

    A directed acyclic graph represent current quantum circuit

Source code in QuICT/core/circuit/circuit.py
def get_DAG_circuit(self) -> DAGCircuit:
    """
    Translate a quantum circuit to a directed acyclic graph
    via quantum gates dependencies (The commutation of quantum gates).

    The nodes in the graph represented the quantum gates, and the edges means the two quantum
    gates is non-commutation. In other words, a directed edge between node A with quantum gate GA
    and node B with quantum gate GB, the quantum gate GA does not commute with GB.

    The nodes in the graph have the following attributes:
    'name', 'gate', 'cargs', 'targs', 'qargs', 'successors', 'predecessors'.

    **Reference:**

    [1] Iten, R., Moyard, R., Metger, T., Sutter, D. and Woerner, S., 2020.
    Exact and practical pattern matching for quantum circuit optimization.
    `arXiv:1909.05270 <https://arxiv.org/abs/1909.05270>`_

    Returns:
        DAGCircuit: A directed acyclic graph represent current quantum circuit
    """
    try:
        return DAGCircuit(self)
    except Exception as e:
        raise CircuitDAGError(e)
inverse
inverse() -> Circuit

the inverse of all Quantum Gates in current Circuit.

Returns:

  • Circuit ( Circuit ) –

    the Quantum Circuit with the inverse of the gateSet

Source code in QuICT/core/circuit/circuit.py
def inverse(self) -> Circuit:
    """ the inverse of all Quantum Gates in current Circuit.

    Returns:
        Circuit: the Quantum Circuit with the inverse of the gateSet
    """
    _cir = Circuit(self.width())
    for gate in self.gates[::-1]:
        if not isinstance(gate, (CompositeGate, BasicGate)):
            raise ValueError("Inverse only for Quantum Gate, not for Trigger or NoiseGate.")

        gate.inverse() | _cir

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

Generate the circuit's unitary matrix which compose by all quantum gates' matrix in current circuit.

Parameters:

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

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

  • expand_gate (bool, default: True ) –

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

Return

ndarray: The combined unitary matrix of current Quantum Circuit.

Source code in QuICT/core/circuit/circuit.py
def matrix(self, device: str = "CPU", expand_gate: bool = True) -> np.ndarray:
    """ Generate the circuit's unitary matrix which compose by all quantum gates' matrix in current circuit.

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

    Return:
        ndarray: The combined unitary matrix of current Quantum Circuit.
    """
    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(no_copy=True)

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

    return circuit_matrix.get_unitary_matrix(based_gates, self.width())
peel
peel(level: int = -1)

Partially flatten the composite gates inside current circuit.

Parameters:

  • level (int, default: -1 ) –

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

Returns:

  • Circuit

    a partially flattened circuit.

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

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

    Returns:
        Circuit: a partially flattened circuit.
    """
    if self._gates is None:
        return self

    new_circ = Circuit(
        wires=self.width(),
        name=None,
        topology=self.topology,
        ancilla_qubits=self.ancilla_qubits,
        precision=self.precision
    )

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

    return new_circ
random_append
random_append(rand_size: int = 10, typelist: list = None, random_params: bool = False, probabilities: list = None, seed: int = None)

add some random gate to the circuit, not include Unitary, Permutation and Permutation_FX Gate.

Parameters:

  • rand_size (int, default: 10 ) –

    the number of the gate added to the circuit.

  • typelist (list<GateType>, default: None ) –

    the type of gate, default contains. Rx, Ry, Rz, Cx, Cy, Cz, CRz, Ch, Rxx, Ryy, Rzz and FSim

  • random_params (bool, default: False ) –

    whether using random parameters for all quantum gates with parameters.

  • probabilities (list, default: None ) –

    The probability of append for each gates.

  • seed (int, default: None ) –

    The random seed for fixed Quantum Circuit.

Source code in QuICT/core/circuit/circuit.py
def random_append(
    self,
    rand_size: int = 10,
    typelist: list = None,
    random_params: bool = False,
    probabilities: list = None,
    seed: int = None
):
    """ add some random gate to the circuit, not include Unitary, Permutation and Permutation_FX Gate.

    Args:
        rand_size (int): the number of the gate added to the circuit. \n
        typelist (list<GateType>): the type of gate, default contains.
            Rx, Ry, Rz, Cx, Cy, Cz, CRz, Ch, Rxx, Ryy, Rzz and FSim \n
        random_params (bool): whether using random parameters for all quantum gates with parameters. \n
        probabilities (list): The probability of append for each gates. \n
        seed (int): The random seed for fixed Quantum Circuit.
    """
    if seed is not None:
        np.random.seed(seed)
        random.seed(seed)
    if typelist is None:
        typelist = [
            GateType.rx, GateType.ry, GateType.rz,
            GateType.cx, GateType.cy, GateType.crz,
            GateType.ch, GateType.cz, GateType.rxx,
            GateType.ryy, GateType.rzz, GateType.fsim
        ]

    unsupported_gate_type = [GateType.unitary, GateType.perm, GateType.perm_fx]
    if len(set(typelist) & set(unsupported_gate_type)) != 0:
        raise CircuitSpecialAppendError(
            f"{set(typelist) & set(unsupported_gate_type)} is not support in random append."
        )

    if probabilities is not None:
        if not np.isclose(sum(probabilities), 1, atol=1e-6):
            raise ValueError("Circuit.random_append.probabilities", "sum to 1", sum(probabilities))

        if len(probabilities) != len(typelist):
            raise CircuitSpecialAppendError(
                "The length of probabilities should equal to the length of Gate Typelist."
            )

    self._logger.debug(f"Random append {rand_size} quantum gates from {typelist} with probability {probabilities}.")
    gate_prob = probabilities
    gate_indexes = list(range(len(typelist)))
    for _ in range(rand_size):
        gate_type = typelist[np.random.choice(gate_indexes, p=gate_prob)]
        r_gate = gate_builder(gate_type, random_params=random_params)
        gsize = r_gate.controls + r_gate.targets
        if gsize == 2 and self._topology is not None:
            layout_list = self._topology.edge_list
            insert_layout = random.choice(layout_list)
            random_assigned_qubits = [insert_layout.u, insert_layout.v] if np.random.randint(0, 2) else \
                [insert_layout.v, insert_layout.u]
        else:
            random_assigned_qubits = random.sample(range(self.width()), gsize)

        r_gate & random_assigned_qubits
        self._gates.append(r_gate)
reset_qubits
reset_qubits()

Reset all qubits in current Quantum Circuit, clean the measured result for each qubit.

Source code in QuICT/core/circuit/circuit.py
def reset_qubits(self):
    """ Reset all qubits in current Quantum Circuit, clean the measured result for each qubit. """
    self._qubits.reset_qubits()
    self._logger.debug(f"Reset qubits' measured result in the Quantum Circuit {self._name}.")
split
split(qubits: list = None, depth: Union[int, list] = None)

Split the Circuit 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/circuit/circuit.py
def split(self, qubits: list = None, depth: Union[int, list] = None):
    """ Split the Circuit 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, self.width())
    if qubits is not None:
        left_cir, right_cir = Circuit(len(qubits)), Circuit(self.width() - len(qubits))
    else:
        left_cir, right_cir = Circuit(self.width()), Circuit(self.width())

    # need remap to add into circuit
    for lgate in left_gates:
        if qubits is not None:
            if isinstance(lgate, CompositeGate):
                remap_idx = [qubits.index(q) for q in lgate.qubits]
            else:
                remap_idx = [qubits.index(q) for q in lgate.cargs + lgate.targs]

            lgate | left_cir(remap_idx)
        else:
            lgate | left_cir

    if qubits is not None:
        inverse_qubits = []
        for inv_q in range(self.width()):
            if inv_q not in qubits:
                inverse_qubits.append(inv_q)

    for rgate in right_gates:
        if qubits is not None:
            if isinstance(rgate, CompositeGate):
                remap_idx = [inverse_qubits.index(q) for q in rgate.qubits]
            else:
                remap_idx = [inverse_qubits.index(q) for q in rgate.cargs + rgate.targs]

            rgate | right_cir(remap_idx)
        else:
            rgate | right_cir

    return left_cir, right_cir
sub_circuit
sub_circuit(start: int = 0, max_size: int = -1, qubit_limit: Union[int, List[int], Qureg] = [], gate_limit: List[GateType] = [])

Get a sub-part circuit from the current Quantum Circuit with target GateSet and Qureg limitation.

Parameters:

  • start (int, default: 0 ) –

    the start gate's index, default 0.

  • max_size (int, default: -1 ) –

    max size of the sub circuit, default -1 without limit.

  • qubit_limit (int/list<int>/Qubit/Qureg, default: [] ) –

    the required qubits' indexes, if [], accept all qubits. default to be [].

  • gate_limit (List[GateType], default: [] ) –

    list of required gate's type, if [], accept all quantum gate. default to be [].

Return

Circuit: the sub circuit

Source code in QuICT/core/circuit/circuit.py
def sub_circuit(
    self,
    start: int = 0,
    max_size: int = -1,
    qubit_limit: Union[int, List[int], Qureg] = [],
    gate_limit: List[GateType] = []
):
    """ Get a sub-part circuit from the current Quantum Circuit with target GateSet and Qureg limitation.

    Args:
        start (int): the start gate's index, default 0.  \n
        max_size (int): max size of the sub circuit, default -1 without limit.   \n
        qubit_limit (int/list<int>/Qubit/Qureg): the required qubits' indexes, if [], accept all qubits.
            default to be [].   \n
        gate_limit (List[GateType]): list of required gate's type, if [], accept all quantum gate. default to be [].

    Return:
        Circuit: the sub circuit
    """
    max_size_for_logger = self.size() if max_size == -1 else max_size
    self._logger.debug(
        f"Get {max_size_for_logger} gates from gate index {start}" +
        f" with target qubits {qubit_limit} and gate limit {gate_limit}."
    )
    if qubit_limit:
        if isinstance(qubit_limit, Qureg):
            qubit_limit = [self._qubits.index(qubit) for qubit in qubit_limit]
        elif isinstance(qubit_limit, Qubit):
            qubit_limit = [self._qubits.index(qubit_limit)]
        elif isinstance(qubit_limit, int):
            qubit_limit = [qubit_limit]

        self._qubit_indexes_validation(qubit_limit)
        set_tqubits = set(qubit_limit)

    sub_circuit = Circuit(self.width()) if not qubit_limit else Circuit(len(qubit_limit))
    flatten_gates = self.flatten_gates()
    temp_size = 0
    for gate_index in range(start, len(flatten_gates)):
        gate = flatten_gates[gate_index]
        if isinstance(gate, Operator):
            continue

        qidxes = gate.cargs + gate.targs
        gate_args = set(qidxes)
        is_append_in_subc = True
        if (qubit_limit and gate_args & set(set_tqubits) != gate_args):
            is_append_in_subc = False

        if (gate_limit and gate.type not in gate_limit):
            is_append_in_subc = False

        if is_append_in_subc:
            new_qidxes = [qubit_limit.index(q) for q in qidxes] if qubit_limit else qidxes
            gate | sub_circuit(new_qidxes)
            temp_size += 1

        if max_size != -1 and temp_size >= max_size:
            break

    return sub_circuit
supremacy_append
supremacy_append(repeat: int = 1, pattern: str = 'ABCDCDAB', random_parameters: bool = False)

Add a supremacy circuit to the circuit

Parameters:

  • repeat (int, default: 1 ) –

    the number of two-qubit gates' sequence

  • pattern (str, default: 'ABCDCDAB' ) –

    indicate the two-qubit gates' sequence

  • random_parameters (bool, default: False ) –

    whether using random parameters for FSim Gate, or not.

Source code in QuICT/core/circuit/circuit.py
def supremacy_append(self, repeat: int = 1, pattern: str = "ABCDCDAB", random_parameters: bool = False):
    """
    Add a supremacy circuit to the circuit

    Args:
        repeat (int): the number of two-qubit gates' sequence
        pattern (str): indicate the two-qubit gates' sequence
        random_parameters (bool): whether using random parameters for FSim Gate, or not.
    """
    qubits = len(self.qubits)
    supremacy_layout = SupremacyLayout(qubits)
    supremacy_typelist = [GateType.sx, GateType.sy, GateType.sw]
    self._logger.debug(
        f"Append Supremacy Circuit with mapping pattern sequence {pattern} and repeat {repeat} times."
    )

    self._add_gate_to_all_qubits(H)

    for i in range(repeat * len(pattern)):
        for q in range(qubits):
            gate_type = supremacy_typelist[np.random.randint(0, 3)]
            fgate = gate_builder(gate_type) & q
            self._gates.append(fgate)

        current_pattern = pattern[i % (len(pattern))]
        if current_pattern not in "ABCD":
            raise ValueError("Circuit.append_supremacy.pattern", "[A, B, C, D]", current_pattern)

        edges = supremacy_layout.get_edges_by_pattern(current_pattern)
        for e in edges:
            gate_params = [np.pi / 2, np.pi / 6]
            gate_args = [int(e[0]), int(e[1])]
            fgate = gate_builder(GateType.fsim, params=gate_params, random_params=random_parameters) & gate_args
            self._gates.append(fgate)

    self._add_gate_to_all_qubits(Measure)
to_compositegate
to_compositegate() -> CompositeGate

Transfer Current Circuit to CompositeGate.

Source code in QuICT/core/circuit/circuit.py
def to_compositegate(self) -> CompositeGate:
    """ Transfer Current Circuit to CompositeGate. """
    return CompositeGate(gates=self.gates)
width
width()

The number of qubits in Circuit.

Returns:

  • int

    the number of qubits in circuit

Source code in QuICT/core/circuit/circuit.py
def width(self):
    """ The number of qubits in Circuit.

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

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)

Hamiltonian

Hamiltonian(pauli_terms: list, coefficients: list)

The Hamiltonian class.

Note

Coefficients are required. And each Pauli Gate should act on different qubit.

Some examples are like this:

[[0.4, 'Y0', 'X1', 'Z2', 'I5'], [0.6]][[1, 'X0', 'I5'], [-3, 'Y3'], [0.01, 'Z5', 'Y0']]

Parameters:

  • pauli_terms (list) –

    A list of pauli terms in the Hamiltonian.

  • coefficients (list) –

    A list of coefficients of each pauli term in the Hamiltonian.

Examples:

>>> from QuICT.algorithm.quantum_machine_learning.utils import Hamiltonian
>>> ham = Hamiltonian([["Y0", "X1"], []], [0.4, 0.6])
>>> ham
[0.6]
[0.4, 'Y0', 'X1']
>>> ham_matrix = ham.get_hamilton_matrix(2)
>>> ham_matrix
[[0.6+0.j  0. +0.j  0. +0.j  0. -0.4j]
[0. +0.j  0.6+0.j  0. -0.4j 0. +0.j ]
[0. +0.j  0. +0.4j 0.6+0.j  0. +0.j ]
[0. +0.4j 0. +0.j  0. +0.j  0.6+0.j ]]

Instantiate the Hamiltonian class instance with pauli terms and coefficients.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def __init__(self, pauli_terms: list, coefficients: list):
    """Instantiate the Hamiltonian class instance with pauli terms and coefficients."""
    assert len(pauli_terms) == len(coefficients), HamiltonianError(
        "The number of pauli terms and coefficients shoule be same."
    )
    self._operators = self._get_operator_dict(pauli_terms, coefficients)
    self._del_zeros()
    self._coefficients = list(self._operators.values())
    self._pauli_str = self._get_pauli_str()
coefficients property
coefficients

The coefficient of each term in the Hamiltonian, i.e. [0.4, 0.6].

operators property
operators

The pauli operators of the Hamiltonian, i.e. {'X1 Y0': 0.4, '': 0.6}

pauli_str property
pauli_str

The pauli string of the Hamiltonian, i.e. [[0.4, 'Y0', 'X1'], [0.6]].

__add__
__add__(other)

Concatenate two Pauli Strings.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def __add__(self, other):
    """Concatenate two Pauli Strings."""
    assert isinstance(other, Hamiltonian), TypeError(
        "Hamiltonian.__add__.other", "Hamiltonian", type(other)
    )
    return Hamiltonian.from_pauli_str(self.pauli_str + other.pauli_str)
__eq__
__eq__(other)

Determine whether two Hamiltonians are the same.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def __eq__(self, other):
    """Determine whether two Hamiltonians are the same."""
    assert isinstance(other, Hamiltonian), TypeError(
        "Hamiltonian.__eq__.other", "Hamiltonian", type(other)
    )
    if len(self.pauli_str) != len(other.pauli_str):
        return False
    for op1, op2 in zip(self.pauli_str, other.pauli_str):
        if abs(op1[0] - op1[0]) > 1e-8:
            return False
        if op1[1:] != op2[1:]:
            return False
    return True
__getitem__
__getitem__(indexes)

Get slice according to the indexes.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def __getitem__(self, indexes):
    """Get slice according to the indexes."""
    pauli_str = []
    if isinstance(indexes, int):
        indexes = [indexes]
    for idx in indexes:
        pauli_str.append(self.pauli_str[idx])
    return Hamiltonian(pauli_str)
__mul__
__mul__(multiplier)

Number multiplication operation for coefficients or multiplication between Hamiltonians.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def __mul__(self, multiplier):
    """Number multiplication operation for coefficients or multiplication between Hamiltonians."""
    if isinstance(multiplier, numbers.Number):
        pauli_terms = list(self.operators.keys())
        coefficients = [coeff * multiplier for coeff in self.coefficients]
        return Hamiltonian(pauli_terms, coefficients)
    elif isinstance(multiplier, Hamiltonian):
        new_operators = dict()
        for left_term, left_coeff in self.operators.items():
            for right_term, right_coeff in multiplier.operators.items():
                new_coeff = left_coeff * right_coeff
                if not left_term or not right_term:
                    new_term = left_term + right_term
                else:
                    new_term = left_term + " " + right_term
                    new_term, new_coeff = self._simplify(new_term, new_coeff)

                # Update result dict.
                if new_term in new_operators:
                    new_operators[new_term] += new_coeff
                else:
                    new_operators[new_term] = new_coeff
        return Hamiltonian(list(new_operators.keys()), list(new_operators.values()))
    else:
        raise TypeError(
            "Hamiltonian.__mul__.multiplier",
            "[numbers.Number, Hamiltonian]",
            type(multiplier),
        )
__repr__
__repr__()

Return a sorted pauli string of the Hamiltonian.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def __repr__(self):
    """Return a sorted pauli string of the Hamiltonian."""
    ham_str = ""
    pauli_str = sorted(self._pauli_str, key=lambda factor: factor[1:])
    for factor in pauli_str:
        ham_str += str(factor) + "\n"
    return ham_str[:-1]
__sub__
__sub__(other)

Concatenate two Pauli strings after the coefficients of the subtrahend term become the opposite.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def __sub__(self, other):
    """Concatenate two Pauli strings after the coefficients of the subtrahend term become the opposite."""
    return self.__add__(other.__mul__(-1))
construct_hamilton_circuit
construct_hamilton_circuit(n_qubits)

Construct a circuit form of the Hamiltonian.

Parameters:

  • n_qubits (int) –

    The number of qubits.

Returns:

  • list: A list of circuits corresponding to the Hamiltonian.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def construct_hamilton_circuit(self, n_qubits):
    """Construct a circuit form of the Hamiltonian.

    Args:
        n_qubits (int): The number of qubits.

    Returns:
        list<Circuit>: A list of circuits corresponding to the Hamiltonian.
    """
    hamilton_circuits = []
    gate_dict = {"X": X, "Y": Y, "Z": Z}
    for term in self._operators.keys():
        circuit = Circuit(n_qubits)
        if term == "":
            hamilton_circuits.append(circuit)
            continue
        pauli_gates = [gate for gate in re.sub("[0-9]", "", term).split(" ")]
        qubit_indexes = [
            int(index) for index in re.sub("[a-zA-Z]", "", term).split(" ")
        ]
        for qid, gate in zip(qubit_indexes, pauli_gates):
            if gate not in gate_dict.keys():
                raise HamiltonianError("Invalid Pauli gate.")
            gate_dict[gate] | circuit(qid)
        hamilton_circuits.append(circuit)
    return hamilton_circuits
from_pauli_str classmethod
from_pauli_str(pauli_str: list)

Instantiate the Hamiltonian class instance with a Pauli string.

Parameters:

  • pauli_str (list) –

    A list of Hamiltonian information.

Source code in QuICT/core/hamiltonian/hamiltonian.py
@classmethod
def from_pauli_str(cls, pauli_str: list):
    """Instantiate the Hamiltonian class instance with a Pauli string.

    Args:
        pauli_str (list): A list of Hamiltonian information.
    """
    pauli_terms = []
    coefficients = []
    for pauli_operator in pauli_str:
        coefficients.append(pauli_operator[0])
        pauli_terms.append(pauli_operator[1:])
    return cls(pauli_terms, coefficients)
get_hamilton_matrix
get_hamilton_matrix(n_qubits)

Construct a matrix form of the Hamiltonian.

Parameters:

  • n_qubits (int) –

    The number of qubits.

Returns:

  • np.array: The Hamiltonian matrix.

Source code in QuICT/core/hamiltonian/hamiltonian.py
def get_hamilton_matrix(self, n_qubits):
    """Construct a matrix form of the Hamiltonian.

    Args:
        n_qubits (int): The number of qubits.

    Returns:
        np.array: The Hamiltonian matrix.
    """
    hamilton_matrix = np.zeros((1 << n_qubits, 1 << n_qubits), dtype=np.complex128)
    hamilton_circuits = self.construct_hamilton_circuit(n_qubits)
    for coeff, circuit in zip(self._coefficients, hamilton_circuits):
        hamilton_matrix += coeff * circuit.matrix()

    return hamilton_matrix

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

Layout

Layout(qubit_number: int, name: str = 'unknown', layout_type: LayoutType = LayoutType.normal, unreachable_nodes: list = None, layout_width: int = -1)

Implement a topology in a physical device

Parameters:

  • qubit_number(int)

    the number of qubits

  • name(string)

    the name of the topology

Source code in QuICT/core/layout/layout.py
def __init__(
    self,
    qubit_number: int,
    name: str = "unknown",
    layout_type: LayoutType = LayoutType.normal,
    unreachable_nodes: list = None,
    layout_width: int = -1
):
    """
    Args:
        qubit_number(int): the number of qubits
        name(string): the name of the topology
    """
    assert qubit_number >= 2, "qubits number should greater than 1"
    self._qubit_number = qubit_number
    self._name = name
    self._edges: Dict[Tuple[int, int], LayoutEdge] = {}
    self._directionalized = None
    self._type = layout_type
    self._unreachable_nodes = unreachable_nodes
    self._double_gate_fidelity = None
    self._width = layout_width
    self._rhombus_shape = 0
directionalized property
directionalized: Layout

Return a copy of current layout with all undirected edges replaced with 2 reversed directional edges.

add_edge
add_edge(u, v, directional=DIRECTIONAL_DEFAULT, error_rate=ERROR_RATE_DEFAULT)

add an edge in the layout

Parameters:

  • u(int)

    Edge endpoint u

  • v(int)

    Edge endpoint v

  • directional(bool)

    Whether the edge is directional

  • error_rate(float)

    Error rate, default 1.0

Source code in QuICT/core/layout/layout.py
def add_edge(
    self,
    u,
    v,
    directional=DIRECTIONAL_DEFAULT,
    error_rate=ERROR_RATE_DEFAULT,
):
    """add an edge in the layout

    Args:
        u(int): Edge endpoint u
        v(int): Edge endpoint v
        directional(bool): Whether the edge is directional
        error_rate(float): Error rate, default 1.0
    """
    assert u != v, "Endpoints cannot be the same"
    assert isinstance(u, (int, int32, int64)) and isinstance(v, (int, int32, int64)), \
        "Endpoints should be integer."
    assert (u >= 0 and u < self._qubit_number) and (u >= 0 and u < self._qubit_number), \
        f"Endpoints should between [0, {self._qubit_number})"
    if self.check_edge(u, v):
        return

    edge = LayoutEdge(u, v, directional, error_rate)
    key = (u, v) if directional or u < v else (v, u)
    self._edges[key] = edge
    # Reset cache
    self._directionalized = None
build_layout_by_double_gate_fidelity
build_layout_by_double_gate_fidelity(double_gate_fidelity: list)

build layout by double_gate_fidelity form virtual quantum machine.

Source code in QuICT/core/layout/layout.py
def build_layout_by_double_gate_fidelity(self, double_gate_fidelity: list):
    """build layout by double_gate_fidelity form virtual quantum machine. """
    assert isinstance(double_gate_fidelity, list), "Please input the double_gate_fidelity infos"

    for edge in double_gate_fidelity:
        u, v, error_rate = edge
        if (u, v) in self._edges:
            self._edges[(u, v)].error_rate = error_rate
        elif (v, u) in self._edges and not self._edges[(v, u)].directional:
            self._edges[(v, u)].error_rate = error_rate
        else:
            self.add_edge(
                u, v, directional=False, error_rate=error_rate
            )
check_edge
check_edge(u, v) -> bool

Check whether layout contain u->v

Parameters:

  • u(int)

    the edge endpoint u

  • v(int)

    the edge endpoint v

Return: bool: whether layout contain u->v

Source code in QuICT/core/layout/layout.py
def check_edge(self, u, v) -> bool:
    """Check whether layout contain u->v

    Args:
        u(int): the edge endpoint u
        v(int): the edge endpoint v
    Return:
        bool: whether layout contain u->v
    """
    return ((u, v) in self._edges) or (
        (v, u) in self._edges and not self._edges[(v, u)].directional
    )
draw
draw(mode: str = None)

Visualization of the topology structure.

Parameters:

  • mode (str, default: None ) –

    The mode of topology, should be one of [random, grid, linear, circuit, rhombus]; default to be grid.

Source code in QuICT/core/layout/layout.py
def draw(self, mode: str = None):
    """ Visualization of the topology structure.

    Args:
        mode (str): The mode of topology, should be one of [random, grid, linear, circuit, rhombus];
            default to be grid.
    """
    from QuICT.tools.display import layout_drawer

    if mode is not None:
        mode_list = ["random", "grid", "linear", "circuit", "rhombus"]
        if mode not in mode_list:
            raise ValueError(
                f"unsupport layout mode {mode}. Please use one of [random, grid, linear, circuit, rhombus]."
            )
    else:
        mode = "random" if self.layout_type == LayoutType.normal else self.layout_type.name

    layout_drawer(self, mode)
get_sublayout_edges
get_sublayout_edges(qubits: list) -> List[LayoutEdge]

Get list of edges with target qubits from current Layout.

Parameters:

  • qubits (list) –

    The target qubits

Returns:

  • list ( List[LayoutEdge] ) –

    The list of LayoutEdge

Source code in QuICT/core/layout/layout.py
def get_sublayout_edges(self, qubits: list) -> List[LayoutEdge]:
    """ Get list of edges with target qubits from current Layout.

    Args:
        qubits (list): The target qubits

    Returns:
        list: The list of LayoutEdge
    """
    # Validate qubits:
    for q in qubits:
        assert isinstance(q, int), "The qubit index should be integer."
        assert q >= 0 and q < self._qubit_number, f"The qubit index should between [0, {self._qubit_number})"

    sub_edges = []
    for ledge in self._edges.values():
        if ledge.v not in qubits and ledge.u not in qubits:
            continue

        sub_edges.append(ledge)

    return sub_edges
grid_layout staticmethod
grid_layout(qubit_number: int, width: int = None, unreachable_nodes: list = [], directional: bool = DIRECTIONAL_DEFAULT, error_rate: list = None)

Get Grid Structure Topology.

Parameters:

  • qubit_number(int)

    the number of qubits

  • width (int, default: None ) –

    The width of grid layout. Defaults to None.

  • unreachable_nodes (list, default: [] ) –

    The nodes which are not work. Defaults to [].

  • directional (bool, default: DIRECTIONAL_DEFAULT ) –

    Whether the edge is directional. Defaults to DIRECTIONAL_DEFAULT.

  • error_rate (list, default: None ) –

    Error rate for each edge, default 1.0. Defaults to []. WARNING: The error rate is for each valid edges from top to bottom, left to right. Please make sure you know exactly every edges' position and rate.

Returns:

  • Layout

    The layout with grid topology

Source code in QuICT/core/layout/layout.py
@staticmethod
def grid_layout(
    qubit_number: int,
    width: int = None,
    unreachable_nodes: list = [],
    directional: bool = DIRECTIONAL_DEFAULT,
    error_rate: list = None
):
    """ Get Grid Structure Topology.

    Args:
        qubit_number(int): the number of qubits
        width (int, optional): The width of grid layout. Defaults to None.
        unreachable_nodes (list, optional): The nodes which are not work. Defaults to [].
        directional (bool, optional): Whether the edge is directional. Defaults to DIRECTIONAL_DEFAULT.
        error_rate (list, optional): Error rate for each edge, default 1.0. Defaults to [].
            WARNING: The error rate is for each valid edges from top to bottom, left to right. Please make sure
            you know exactly every edges' position and rate.

    Returns:
        Layout: The layout with grid topology
    """
    assert qubit_number >= 2, "qubits number should greater than 1"
    grid_layout = Layout(qubit_number, layout_type=LayoutType.grid, unreachable_nodes=unreachable_nodes)
    exist_unreachable_nodes = len(unreachable_nodes) != 0
    grid_width = int(log2(qubit_number)) if width is None else width
    error_rate = [1] * (qubit_number * 4) if error_rate is None else error_rate
    edge_idx = 0
    for s in range(0, qubit_number - 1):
        horizontal_exist, vertical_exist = True, True
        u, hv, vv = s, s + 1, s + grid_width
        if exist_unreachable_nodes:
            if u in unreachable_nodes:
                continue

            if hv in unreachable_nodes:
                horizontal_exist = False

            if vv in unreachable_nodes:
                vertical_exist = False

        # horizontal line draw
        if hv % grid_width != 0 and horizontal_exist:
            grid_layout.add_edge(u, hv, directional, error_rate[edge_idx])
            edge_idx += 1

        # vertical line draw
        if vv < qubit_number and vertical_exist:
            grid_layout.add_edge(u, vv, directional, error_rate[edge_idx])
            edge_idx += 1

    # TODO: not finish the shape
    grid_layout._width = grid_width
    return grid_layout
linear_layout staticmethod
linear_layout(qubit_number: int, directional: bool = DIRECTIONAL_DEFAULT, error_rate: list = None)

Return the layout with linearly topology.

Parameters:

  • qubit_number(int)

    the number of qubits

  • directional (_type_, default: DIRECTIONAL_DEFAULT ) –

    Whether the edge is directional. Defaults to DIRECTIONAL_DEFAULT.

  • error_rate (list, default: None ) –

    Error rate for each edges, default 1.0. Defaults to [].

Returns:

  • Layout

    The layout with linearly topology

Source code in QuICT/core/layout/layout.py
@staticmethod
def linear_layout(qubit_number: int, directional: bool = DIRECTIONAL_DEFAULT, error_rate: list = None):
    """ Return the layout with linearly topology.

    Args:
        qubit_number(int): the number of qubits
        directional (_type_, optional): Whether the edge is directional. Defaults to DIRECTIONAL_DEFAULT.
        error_rate (list, optional): Error rate for each edges, default 1.0. Defaults to [].

    Returns:
        Layout: The layout with linearly topology
    """
    assert qubit_number >= 2, "qubits number should greater than 1"
    linear_layout = Layout(qubit_number, layout_type=LayoutType.linear)
    if error_rate is None:
        error_rate = [1.0] * (qubit_number - 1)

    assert len(error_rate) == (qubit_number - 1)
    for i in range(qubit_number - 1):
        linear_layout.add_edge(i, i + 1, directional, error_rate[i])

    return linear_layout
load_file classmethod
load_file(file_path: str) -> Layout

Load layout from file. Args: file_path(str): Path of layout file. Return: Layout: Layout parsed from file.

Source code in QuICT/core/layout/layout.py
@classmethod
def load_file(cls, file_path: str) -> Layout:
    """Load layout from file.
    Args:
        file_path(str): Path of layout file.
    Return:
        Layout: Layout parsed from file.
    """
    with open(file_path) as f:
        return cls.from_json(f.read())
out_edges
out_edges(begin_point: int, node_index_only: bool = False) -> List[LayoutEdge]

edges begin from begin_point

Parameters:

  • begin_point(int)

    the index of begin node

Return

list: edges begin from begin_point

Source code in QuICT/core/layout/layout.py
def out_edges(self, begin_point: int, node_index_only: bool = False) -> List[LayoutEdge]:
    """edges begin from begin_point

    Args:
        begin_point(int): the index of begin node

    Return:
        list<LayoutEdge>: edges begin from begin_point
    """
    prior_list = []
    for edge in self.edge_list:
        if edge.u == begin_point:
            if node_index_only:
                prior_list.append(edge.v)
            else:
                prior_list.append(edge)
        elif edge.v == begin_point and not edge.directional:
            if node_index_only:
                prior_list.append(edge.u)
            else:
                prior_list.append(edge)

    return prior_list
remove_edge
remove_edge(u, v, directional=DIRECTIONAL_DEFAULT)

remove an edge in the layout

Parameters:

  • u(int)

    Edge endpoint u

  • v(int)

    Edge endpoint v

  • directional(bool)

    Whether the edge is directional

Source code in QuICT/core/layout/layout.py
def remove_edge(self, u, v, directional=DIRECTIONAL_DEFAULT):
    """remove an edge in the layout

    Args:
        u(int): Edge endpoint u
        v(int): Edge endpoint v
        directional(bool): Whether the edge is directional
    """
    assert u != v, "Endpoints cannot be the same"
    assert isinstance(u, (int, int32, int64)) and isinstance(v, (int, int32, int64)), \
        "Endpoints should be integer."
    assert (u >= 0 and u < self._qubit_number) and (u >= 0 and u < self._qubit_number), \
        f"Endpoints should between [0, {self._qubit_number})"
    if not self.check_edge(u, v):
        return

    key = (u, v) if directional or u < v else (v, u)
    del self._edges[key]
rhombus_layout staticmethod
rhombus_layout(qubit_number: int, width: int = None, unreachable_nodes: list = [], directional: bool = DIRECTIONAL_DEFAULT, error_rate: list = None, shape: int = 0)

Get Rhombus Structure Topology.

Parameters:

  • qubit_number(int)

    the number of qubits

  • width (int, default: None ) –

    The width of grid layout. Defaults to None.

  • unreachable_nodes (list, default: [] ) –

    The nodes which are not work. Defaults to [].

  • directional (bool, default: DIRECTIONAL_DEFAULT ) –

    Whether the edge is directional. Defaults to DIRECTIONAL_DEFAULT.

  • error_rate (list, default: None ) –

    Error rate for each edge, default 1.0. Defaults to []. WARNING: The error rate is arranged in order of valid edges, from top to bottom, left to right. Please make sure you know exactly every edges' position and rate.

  • shape (int, default: 0 ) –

    The shape of rhombus; if 0, it will be close shape first; otherwise it will be open shape. default to close shape.

Returns:

  • Layout

    The layout with rhombus topology

Source code in QuICT/core/layout/layout.py
@staticmethod
def rhombus_layout(
    qubit_number: int,
    width: int = None,
    unreachable_nodes: list = [],
    directional: bool = DIRECTIONAL_DEFAULT,
    error_rate: list = None,
    shape: int = 0
):
    """ Get Rhombus Structure Topology.

    Args:
        qubit_number(int): the number of qubits
        width (int, optional): The width of grid layout. Defaults to None.
        unreachable_nodes (list, optional): The nodes which are not work. Defaults to [].
        directional (bool, optional): Whether the edge is directional. Defaults to DIRECTIONAL_DEFAULT.
        error_rate (list, optional): Error rate for each edge, default 1.0. Defaults to [].
            WARNING: The error rate is arranged in order of valid edges, from top to bottom, left to right.
            Please make sure you know exactly every edges' position and rate.
        shape (int, optional): The shape of rhombus; if 0, it will be close shape first; otherwise it will
            be open shape. default to close shape.

    Returns:
        Layout: The layout with rhombus topology
    """
    assert qubit_number >= 2, "qubits number should greater than 1"
    rhombus_layout = Layout(qubit_number, layout_type=LayoutType.rhombus, unreachable_nodes=unreachable_nodes)
    exist_unreachable_nodes = len(unreachable_nodes) != 0
    grid_width = int(log2(qubit_number)) if width is None else width
    error_rate = [1] * (qubit_number * 4) if error_rate is None else error_rate
    edge_idx = 0
    for s in range(0, qubit_number - grid_width + 1):
        vertical_exist, rhombus_exist = True, True
        # horizontal line draw
        u, lv, rv = s, s + grid_width, s + grid_width + (-1) ** (s // grid_width)
        if exist_unreachable_nodes:
            if u in unreachable_nodes:
                continue

            if lv in unreachable_nodes:
                vertical_exist = False

            if rv in unreachable_nodes:
                rhombus_exist = False

        # vertical line draw
        if lv < qubit_number and vertical_exist:
            rhombus_layout.add_edge(u, lv, directional, error_rate[edge_idx])
            edge_idx += 1

        # rhombus line draw
        if rv < qubit_number and u // grid_width + 1 == rv // grid_width and rhombus_exist:
            rhombus_layout.add_edge(u, rv, directional, error_rate[edge_idx])
            edge_idx += 1

    rhombus_layout._width = grid_width
    rhombus_layout._rhombus_shape = shape
    return rhombus_layout
set_double_gate_fidelity
set_double_gate_fidelity(double_gate_fidelity: list)

Set the coupling strength between qubits

Parameters:

  • double_gate_fidelity (List[Tuple(idx, idx, float)]) –

    The strength of the interaction between two qubits in a quantum computing system.

Source code in QuICT/core/layout/layout.py
def set_double_gate_fidelity(self, double_gate_fidelity: list):
    """ Set the coupling strength between qubits

    Args:
        double_gate_fidelity List[Tuple(idx, idx, float)]: The strength of the interaction between two qubits in a
            quantum computing system.
    """
    # Reset coupling strength
    self._double_gate_fidelity = defaultdict(dict)

    for start, end, val in double_gate_fidelity:
        assert start != end and start >= 0 and end >= 0 and val >= 0 and val <= 1
        assert self.check_edge(start, end), f"inexistance edge ({start}, {end}) in current layout."

        self._double_gate_fidelity[start][end] = val
        self._double_gate_fidelity[end][start] = val
        target_edge = self._edges[(start, end)] if (start, end) in self._edges else self._edges[(end, start)]
        target_edge.error_rate = val
sub_layout
sub_layout(qubits: list) -> Layout

Get partial layout. Only working for undirected layout.

Parameters:

  • qubits_number (list) –

    The qubit indexes for sub-layout

Returns:

  • Layout ( Layout ) –

    The sub-layout

Source code in QuICT/core/layout/layout.py
def sub_layout(self, qubits: list) -> Layout:
    """ Get partial layout. Only working for undirected layout.

    Args:
        qubits_number (list): The qubit indexes for sub-layout

    Returns:
        Layout: The sub-layout
    """
    assert len(qubits) == len(set(qubits)), "Repeatted qubit indexes."
    for q in qubits:
        assert q >= 0 and q < self._qubit_number, "The qubit index should in current Layout."

    sub_layout = Layout(len(qubits))
    sorted_qidx = sorted(qubits)
    for edge in self._edges.values():
        if edge.u not in qubits or edge.v not in qubits:
            continue

        sub_layout.add_edge(
            sorted_qidx.index(edge.u), sorted_qidx.index(edge.v), edge.directional, edge.error_rate
        )

    return sub_layout
to_json
to_json() -> str

Serialize current layout into json string.

Source code in QuICT/core/layout/layout.py
def to_json(self) -> str:
    """Serialize current layout into json string."""
    data = {}
    data["name"] = self._name
    data["qubit_number"] = self._qubit_number
    edges = [edge.to_dict() for edge in self]
    data["edges"] = edges
    return json.dumps(data)
valid_circuit
valid_circuit(circuit) -> bool

Validate the given Circuit/CompositeGate is valid with current Layout.

Parameters:

  • circuit (Union[Circuit, CompositeGate]) –

    The given Circuit/CompositeGate

Returns:

  • bool ( bool ) –

    Whether is valid for current layout.

Source code in QuICT/core/layout/layout.py
def valid_circuit(self, circuit) -> bool:
    """ Validate the given Circuit/CompositeGate is valid with current Layout.

    Args:
        circuit (Union[Circuit, CompositeGate]): The given Circuit/CompositeGate

    Returns:
        bool: Whether is valid for current layout.
    """
    if circuit.width() > self._qubit_number:
        return False

    for gate in circuit.gates:
        if type(gate).__name__ == "CompositeGate":
            if not self.valid_circuit(gate):
                return False

            continue

        qidxes = gate.cargs + gate.targs
        if len(qidxes) > 2:
            return False
        elif len(qidxes) == 2:
            if not self.check_edge(qidxes[0], qidxes[1]) or not self.check_edge(qidxes[1], qidxes[0]):
                return False

    return True
write_file
write_file(directory='./')

Write layout into file.

Parameters:

  • directory(str)

    Directory to store layout file in, default "./"

Source code in QuICT/core/layout/layout.py
def write_file(self, directory="./"):
    """Write layout into file.

    Args:
        directory(str): Directory to store layout file in, default "./"
    """
    with open(f"{directory}{self.name}.layout", "w") as f:
        f.write(self.to_json())

LayoutEdge

LayoutEdge(u: int, v: int, directional: bool, error_rate: float)

Implement a physical connection between physical qubits

Attributes:

  • u(int)

    Node u of edge

  • v(int)

    Node v of edge

  • directional(bool)

    Whether the layout is directional

  • error_rate(float)

    Error_rate between u and v, default 1.0

Source code in QuICT/core/layout/layout.py
def __init__(self, u: int, v: int, directional: bool, error_rate: float):
    self._u = u
    self._v = v
    self._error_rate = error_rate
    self._directional = directional

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)

Qubit

Qubit(fidelity: Union[float, tuple] = 1.0, preparation_fidelity: float = 1.0, gate_fidelity: Union[float, dict] = 1.0, T1: float = 0.0, T2: float = 0.0, work_frequency: float = 0.0, readout_frequency: float = 0.0, gate_duration: float = 0.0)

Bases: object

Implement a Quantum bit

Qubit is the basic unit of Quantum Compute.

Parameters:

  • fidelity (Union[float, tuple], default: 1.0 ) –

    The qubit's measured fidelity, where the fidelity of a quantum qubit is the overlap between the ideal theoretical operation and the actual experimental operation. if it is list, it represent the measured fidelity for state 0 and state 1.

  • preparation_fidelity (float, default: 1.0 ) –

    The qubit's state preparation fidelity refers to the degree of accuracy with which a quantum bit (qubit) can be prepared in a specific state.

  • gate_fidelity (Union[float, dict], default: 1.0 ) –

    The fidelity for applying single-qubit quantum gate in this qubit. e.g. {GateType.h: 0.993, GateType.x: 0.989}

  • T1 ((float, μs), default: 0.0 ) –

    The longitudinal coherence time, which refers to the time it takes for the qubit to decay back to its ground state from an excited state. Default to None.

  • T2 ((float, μs), default: 0.0 ) –

    the transverse coherence time, which refers to the time it takes for the qubit to lose its coherence when subjected to unwanted phase or amplitude fluctuations. Default to None.

  • work_frequency (Union[float, list], default: 0.0 ) –

    The working frequency in current Qubit.

  • readout_frequency (Union[float, list], default: 0.0 ) –

    The frequency when measured qubit in current Qubit.

  • gate_duration (Union[float, list], default: 0.0 ) –

    The amount of time that a Quantum Gate operators on a Qubit.

Source code in QuICT/core/qubit/qubit.py
def __init__(
    self,
    fidelity: Union[float, tuple] = 1.0,
    preparation_fidelity: float = 1.0,
    gate_fidelity: Union[float, dict] = 1.0,
    T1: float = 0.0,
    T2: float = 0.0,
    work_frequency: float = 0.0,
    readout_frequency: float = 0.0,
    gate_duration: float = 0.0,
):
    """
    Args:
        fidelity (Union[float, tuple]): The qubit's measured fidelity, where the fidelity of a quantum qubit is the
            overlap between the ideal theoretical operation and the actual experimental operation. if it is list,
            it represent the measured fidelity for state 0 and state 1.
        preparation_fidelity (float): The qubit's state preparation fidelity refers to the degree of accuracy with
            which a quantum bit (qubit) can be prepared in a specific state.
        gate_fidelity (Union[float, dict]): The fidelity for applying single-qubit quantum gate in this qubit.
            e.g. {GateType.h: 0.993, GateType.x: 0.989}
        T1 (float, μs): The longitudinal coherence time, which refers to the time it takes for the qubit to decay
            back to its ground state from an excited state. Default to None.
        T2 (float, μs): the transverse coherence time, which refers to the time it takes for the qubit to lose its
            coherence when subjected to unwanted phase or amplitude fluctuations. Default to None.
        work_frequency (Union[float, list]): The working frequency in current Qubit.
        readout_frequency (Union[float, list]): The frequency when measured qubit in current Qubit.
        gate_duration (Union[float, list]): The amount of time that a Quantum Gate operators on a Qubit.
    """
    self._id = unique_id_generator()
    self.fidelity = fidelity
    self.preparation_fidelity = preparation_fidelity
    self.gate_fidelity = gate_fidelity
    self.T1 = T1
    self.T2 = T2
    self.work_frequency = work_frequency
    self.readout_frequency = readout_frequency
    self.gate_duration = gate_duration

    self._measured = None
    self._probability = None    # The prob of measured 0
    self._historical_measured = []
__bool__
__bool__()

int value of the qubit(measure result)

Returns:

  • bool

    measure result

Source code in QuICT/core/qubit/qubit.py
def __bool__(self):
    """ int value of the qubit(measure result)

    Returns:
        bool: measure result

    Raises:
        The qubit has not be measured.
    """
    return bool(int(self))
__int__
__int__()

int value of the qubit(measure result)

Returns:

  • int

    measure result

Source code in QuICT/core/qubit/qubit.py
def __int__(self):
    """ int value of the qubit(measure result)

    Returns:
        int: measure result

    Raises:
        The qubit has not be measured.
    """
    if self._measured is None:
        raise QubitMeasureError(f"The qubit {self.id} has not be measured")

    return self.measured
__str__
__str__()

string describe of the qubit

Returns:

  • str

    a simple describe

Source code in QuICT/core/qubit/qubit.py
def __str__(self):
    """ string describe of the qubit

    Returns:
        str: a simple describe
    """
    return f"qubit id: {self.id}; fidelity: {self.fidelity}; QSP_fidelity: {self.preparation_fidelity}; " \
        + f"Gate_fidelity: {self.gate_fidelity}; Coherence time: T1: {self._t1}; T2: {self._t2}; " \
        + f"Work Frequency: {self.work_frequency}; Readout Frequency: {self.readout_frequency}; " \
        + f"Gate Duration: {self.gate_duration}"
reset
reset()

Reset self qubit status.

Source code in QuICT/core/qubit/qubit.py
def reset(self):
    """ Reset self qubit status. """
    self._measured = None
    self._historical_measured = []

Qureg

Qureg(qubits: Union[int, Qubit, Qureg] = None)

Bases: list

Implement a Quantum Register

Qureg is a list of Qubits, which is a subClass of list.

initial a qureg with qubit(s)

Parameters:

  • qubits (Union[int, Qubit, Qureg], default: None ) –

    the qubits which make up the qureg, it can have below form, 1) int 2) qubit 3) [qubits/quregs]

Source code in QuICT/core/qubit/qubit.py
def __init__(self, qubits: Union[int, Qubit, Qureg] = None):
    """ initial a qureg with qubit(s)

    Args:
        qubits: the qubits which make up the qureg, it can have below form,
            1) int
            2) qubit
            3) [qubits/quregs]
    """
    super().__init__()
    if qubits is None:
        return

    if isinstance(qubits, int):
        for _ in range(qubits):
            self.append(Qubit())
    elif isinstance(qubits, Qubit):
        self.append(qubits)
    elif isinstance(qubits, list):
        for qubit in qubits:
            if isinstance(qubit, Qubit):
                self.append(qubit)
            elif isinstance(qubit, Qureg):
                self.extend(qubit)
            else:
                raise TypeError("Qureg.qubits", "int/Qubit/list<Qubit/Qureg>", type(qubit))
    else:
        raise TypeError("Qureg.qubits", "int/Qubit/list<Qubit/Qureg>", type(qubits))
__add__
__add__(other)

get a combined qureg with this qureg and other qureg

Parameters:

  • other(Qureg)

    qureg to be added.

Return

Qureg: the result or slice

Source code in QuICT/core/qubit/qubit.py
def __add__(self, other):
    """ get a combined qureg with this qureg and other qureg

    Args:
        other(Qureg): qureg to be added.

    Return:
        Qureg: the result or slice
    """
    return Qureg([self, other])
__call__
__call__(indexes: object)

get a smaller qureg from this qureg

Parameters:

  • indexes (object) –

    the indexes passed in, it can have follow form: 1) int 2) list

Returns: Qubit[s]: the qureg correspond to the indexes

Source code in QuICT/core/qubit/qubit.py
def __call__(self, indexes: object):
    """ get a smaller qureg from this qureg

    Args:
        indexes: the indexes passed in, it can have follow form:
            1) int
            2) list<int>
    Returns:
        Qubit[s]: the qureg correspond to the indexes
    """
    if isinstance(indexes, int):
        return Qureg(self[indexes])

    return self[indexes]
__eq__
__eq__(other)

check two qureg is same or not. Iff all qubits in two qureg are same will return True; otherwise, return False.

Parameters:

  • other(Qureg)

    qureg to be checked.

Source code in QuICT/core/qubit/qubit.py
def __eq__(self, other):
    """
    check two qureg is same or not. Iff all qubits in two qureg are same will
    return True; otherwise, return False.

    Args:
        other(Qureg): qureg to be checked.
    """
    assert isinstance(other, Qureg), TypeError("Qureg.eq", "Qureg", type(other))
    if not len(other) == len(self):
        return False

    current_qubit_ids = [qubit.id for qubit in self]
    for qubit in other:
        if qubit.id not in current_qubit_ids:
            return False

    return True
__getitem__
__getitem__(item)

to fit the slice operator, overloaded this function.

get a smaller qureg/qubit from this qureg

Parameters:

  • item(int/slice)

    slice passed in.

Return

Qubit/Qureg: the result or slice

Source code in QuICT/core/qubit/qubit.py
def __getitem__(self, item):
    """ to fit the slice operator, overloaded this function.

    get a smaller qureg/qubit from this qureg

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

    Return:
        Qubit/Qureg: the result or slice
    """
    if isinstance(item, int):
        return super().__getitem__(item)

    qureg = Qureg()
    if isinstance(item, slice):
        qureg_list = super().__getitem__(item)
        for qubit in qureg_list:
            qureg.append(qubit)
    elif isinstance(item, list) or isinstance(item, tuple):
        for idx in item:
            assert isinstance(idx, int), TypeError("Qureg.getitem.item.value", "int", type(idx))
            if idx < 0 or idx > len(self):
                raise IndexExceedError("Qureg.getitem", [0, len(self)], idx)

            qureg.append(self[idx])
    else:
        raise TypeError("Qureg.getitem", "int/list[int]/slice", type(item))

    return qureg
__iadd__
__iadd__(other)

get a combined qureg with this qureg and other qureg

Parameters:

  • other(Qureg)

    qureg to be added.

Return

Qureg: the result or slice

Source code in QuICT/core/qubit/qubit.py
def __iadd__(self, other):
    """ get a combined qureg with this qureg and other qureg

    Args:
        other(Qureg): qureg to be added.

    Return:
        Qureg: the result or slice
    """
    if isinstance(other, Qubit):
        self.append(other)
    elif isinstance(other, Qureg):
        self.extend(other)
    else:
        raise TypeError("Qureg.iadd", "Qureg/Qubit", type(other))

    return self
__int__
__int__()

the value of the register

Return the value of the register if all qubits have been measured. Note that the compute mode is BigEndian.

Returns:

  • int

    the value of the register

Raises:

  • Exception

    some qubit has not be measured

Source code in QuICT/core/qubit/qubit.py
def __int__(self):
    """ the value of the register

    Return the value of the register if all qubits have been measured.
    Note that the compute mode is BigEndian.

    Returns:
        int: the value of the register

    Raises:
        Exception: some qubit has not be measured
    """
    value = 0
    for qubit in self:
        value <<= 1
        value += int(qubit)

    return value
__str__
__str__()

the string of the value of the register

Returns:

  • str

    the value of the qureg

Source code in QuICT/core/qubit/qubit.py
def __str__(self):
    """ the string of the value of the register

    Returns:
        str: the value of the qureg
    """
    bit_idx = "{0:0b}".format(self.__int__())
    bit_idx = bit_idx.zfill(len(self))

    return bit_idx
index
index(qubit: Union[List[Qubit], Qubit]) -> Union[int, list]

Return the index of given qubits.

Parameters:

  • qubit (Union[List[Qubit], Qubit]) –

    The given qubits

Returns:

  • Union[int, list]

    Union[int, list]: The index of given qubits in current qureg.

Source code in QuICT/core/qubit/qubit.py
def index(self, qubit: Union[List[Qubit], Qubit]) -> Union[int, list]:
    """ Return the index of given qubits.

    Args:
        qubit (Union[List[Qubit], Qubit]): The given qubits

    Returns:
        Union[int, list]: The index of given qubits in current qureg.
    """
    if isinstance(qubit, Qubit):
        return super().index(qubit)

    if isinstance(qubit, list):
        idxes = []
        for q in qubit:
            idxes.append(super().index(q))

        return idxes

    raise ValueError("Qureg.index.qubit", "within current Qureg", "qubit is not")
reset_qubits
reset_qubits()

Reset all qubits' status.

Source code in QuICT/core/qubit/qubit.py
def reset_qubits(self):
    """ Reset all qubits' status. """
    for qubit in self:
        qubit.reset()
set_fidelity
set_fidelity(fidelity: Union[float, tuple, list])

Set the fidelity for each qubits

Parameters:

  • fidelity (list) –

    The list of fidelity for each qubits, should equal to len(qureg).

Source code in QuICT/core/qubit/qubit.py
def set_fidelity(self, fidelity: Union[float, tuple, list]):
    """ Set the fidelity for each qubits

    Args:
        fidelity (list): The list of fidelity for each qubits, should equal to len(qureg).
    """
    fidelity = self._normalized_parameters(fidelity, "fidelity")
    for idx, qubit in enumerate(self):
        qubit.fidelity = fidelity[idx]
set_gate_fidelity
set_gate_fidelity(gate_fidelity: Union[float, list])

Set the Single-Qubit Gate Fidelity for each qubits

Parameters:

  • gate_fidelity (list) –

    The list of gate fidelity for each qubits, should equal to len(qureg).

Source code in QuICT/core/qubit/qubit.py
def set_gate_fidelity(self, gate_fidelity: Union[float, list]):
    """ Set the Single-Qubit Gate Fidelity for each qubits

    Args:
        gate_fidelity (list): The list of gate fidelity for each qubits, should equal to len(qureg).
    """
    gate_fidelity = self._normalized_parameters(gate_fidelity, "gate_fidelity")
    for idx, qubit in enumerate(self):
        qubit.gate_fidelity = gate_fidelity[idx]
set_preparation_fidelity
set_preparation_fidelity(fidelity: Union[float, list])

Set the QSP fidelity for each qubits

Parameters:

  • fidelity (list) –

    The list of fidelity for each qubits, should equal to len(qureg).

Source code in QuICT/core/qubit/qubit.py
def set_preparation_fidelity(self, fidelity: Union[float, list]):
    """ Set the QSP fidelity for each qubits

    Args:
        fidelity (list): The list of fidelity for each qubits, should equal to len(qureg).
    """
    fidelity = self._normalized_parameters(fidelity, "preparation_fidelity")
    for idx, qubit in enumerate(self):
        qubit.preparation_fidelity = fidelity[idx]
set_t1_time
set_t1_time(t1_time: list)

Set the T1 coherence time for each qubit

Parameters:

  • t1_time (list) –

    The T1 time for each qubit

Source code in QuICT/core/qubit/qubit.py
def set_t1_time(self, t1_time: list):
    """ Set the T1 coherence time for each qubit

    Args:
        t1_time (list): The T1 time for each qubit
    """
    t1_time = self._normalized_parameters(t1_time, "t1")
    for idx, qubit in enumerate(self):
        qubit.T1 = t1_time[idx]
set_t2_time
set_t2_time(t2_time: list)

Set the T2 coherence time for each qubit

Parameters:

  • t2_time (list) –

    The T2 time for each qubit

Source code in QuICT/core/qubit/qubit.py
def set_t2_time(self, t2_time: list):
    """ Set the T2 coherence time for each qubit

    Args:
        t2_time (list): The T2 time for each qubit
    """
    t2_time = self._normalized_parameters(t2_time, "t2")
    for idx, qubit in enumerate(self):
        qubit.T2 = t2_time[idx]

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

diagonal_ury_gate

diagonal_ury_gate(alpha, ancilla: int = 0, opt: bool = False, include_phase_gate: bool = False)

Parameters:

  • qubit_num (int) –

    the number of qubits in the circuit

  • alpha (list) –

    the list of rotation angles

  • ancilla (int, default: 0 ) –

    the number of ancillary qubits

  • opt (bool, default: False ) –

    the switch of cnot optimizer

  • include_phase_gate (bool, default: False ) –

    the switch of phase holder

Returns:

  • Tuple[CompositeGate, list]:

  • gates are the diagonal equivalent of URy gates;

  • qubit[:dg.width()] is the position of all qubits

Source code in QuICT/qcda/synthesis/unitary_decomposition/utility.py
def diagonal_ury_gate(alpha,
                      ancilla: int = 0,
                      opt: bool = False,
                      include_phase_gate: bool = False):
    """
    Args:
        qubit_num (int):the  number of qubits in the circuit
        alpha (list): the list of rotation angles
        ancilla (int): the number of ancillary qubits
        opt (bool): the switch of cnot optimizer
        include_phase_gate (bool): the switch of phase holder

    Returns:
        Tuple[CompositeGate, list]:
        gates are the diagonal equivalent of URy gates;
        qubit[:dg.width()] is the position of all qubits
    """
    gates = CompositeGate()
    # qubit_num - k - 1 = posi, Here k is from QSP method
    posi = int(np.floor(np.log2(len(alpha))))

    S_dagger | gates(posi)
    H | gates(posi)

    gate_dg, qubit_list = diagonal_urz_gate(alpha, ancilla, opt, include_phase_gate)
    gate_dg | gates

    H | gates(posi)
    S | gates(posi)

    return gates, qubit_list

diagonal_urz_gate

diagonal_urz_gate(alpha, ancilla: int = 0, opt: bool = False, include_phase_gate: bool = False)

Parameters:

  • qubit_num (int) –

    the number of qubits in the circuit

  • alpha (list) –

    the list of rotation angles

  • ancilla (int, default: 0 ) –

    the number of ancillary qubits

  • opt (bool, default: False ) –

    the switch of cnot optimizer

  • include_phase_gate (bool, default: False ) –

    the switch of phase holder

Returns:

  • Tuple[CompositeGate, list]:

  • gates are the diagonal equivalent of URz gates;

  • qubit[:dg.width()] is the position of all qubits

Source code in QuICT/qcda/synthesis/unitary_decomposition/utility.py
def diagonal_urz_gate(alpha,
                      ancilla: int = 0,
                      opt: bool = False,
                      include_phase_gate: bool = False):
    """
    Args:
        qubit_num (int):the  number of qubits in the circuit
        alpha (list): the list of rotation angles
        ancilla (int): the number of ancillary qubits
        opt (bool): the switch of cnot optimizer
        include_phase_gate (bool): the switch of phase holder

    Returns:
        Tuple[CompositeGate, list]:
        gates are the diagonal equivalent of URz gates;
        qubit[:dg.width()] is the position of all qubits
    """

    gates = CompositeGate()
    angles = np.vstack((-alpha / 2, alpha / 2)).T.flatten()

    n = int(round(np.log2(angles.size)))
    # TODO(2023.12.22): the choice of m
    m = min((1 << n), ancilla)
    m = m - 1 if m % 2 != 0 else m
    if m != 0:
        t = int(np.floor(np.log2(m / 2)))
        if t == 0 or n - t == 0:
            m = 0
        elif int(np.floor(m / (2 * (n - t)))) <= 0:
            m = 0

    diag = DiagonalGate(n, m, opt, include_phase_gate)
    qubit = list(range(n)) + list(range(n, n + m))

    dg = diag(angles)
    dg & qubit[:dg.width()] | gates

    return gates, qubit[:dg.width()]

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

quantum_shannon_decompose

quantum_shannon_decompose(u1: ndarray, u2: ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]

Decompose a block diagonal even-size unitary matrix. block_diag(u1,u2) == block_diag(v, v) @ block_diag(d, d_dagger) @ block_diag(w, w)

Parameters:

  • u1 (ndarray) –

    upper-left block

  • u2 (ndarray) –

    right-bottom block

Returns:

  • Tuple[ndarray, ndarray, ndarray]

    Tuple[np.ndarray,np.ndarray,np.ndarray]

Source code in QuICT/qcda/synthesis/unitary_decomposition/utility.py
def quantum_shannon_decompose(
        u1: np.ndarray,
        u2: np.ndarray
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    Decompose a block diagonal even-size unitary matrix.
    block_diag(u1,u2) == block_diag(v, v) @ block_diag(d, d_dagger) @ block_diag(w, w)

    Args:
        u1 (np.ndarray): upper-left block
        u2 (np.ndarray): right-bottom block

    Returns:
        Tuple[np.ndarray,np.ndarray,np.ndarray]
    """
    s = u1 @ u2.conj().T

    d, v = sp.linalg.schur(s)
    v_dagger = v.conj().T
    d = np.sqrt(np.diag(np.diagonal(d)))
    w = d @ v_dagger @ u2

    return v, d, w

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:]