quairkit.circuit

The source file of the Circuit class.

class quairkit.circuit.Circuit(num_systems=None, system_dim=2, physical_idx=None)

Class for quantum circuit.

Parameters:
num_systems : int | None

number of systems in the circuit. Defaults to None. Alias of num_qubits.

system_dim : int | List[int] | None

dimension of systems of this circuit. Can be a list of system dimensions or an int representing the dimension of all systems. Defaults to be qubit case.

physical_idx : List[int] | None

physical indices of systems. Defaults to be the same as the logical indices.

Note

when the number of system is unknown and system_dim is an int, the circuit is a dynamic quantum circuit.

Examples

qc = Circuit(1)  # A quantum circuit with 1 qubit
qc.h()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{H}
to_latex(style='standard', decimal=2)

The LaTeX representation of the circuit, written in Quantikz format.

Parameters:
style : str

the style of the plot, can be ‘standard’, ‘compact’ or ‘detailed’. Defaults to standard.

decimal : int

number of decimal places to display. Defaults to 2.

Examples

qc = Circuit(2)
qc.h()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{H} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{H} & {} & {}
plot(style='standard', decimal=2, dpi=300, print_code=False, show_plot=True, include_empty=False, latex=True, **kwargs)

Display the circuit using Quantikz if latex is True, otherwise using matplotlib.

Parameters:
style : str

the style of the plot, can be ‘standard’, ‘compact’ or ‘detailed’. Defaults to standard.

decimal : int

number of decimal places to display. Defaults to 2.

dpi : int

dots per inches of plot image. Defaults to 300.

print_code : bool

whether print the LaTeX code of the circuit, default to False.

show_plot : bool

whether show the plotted circuit, default to True.

include_empty : bool

whether include empty lines, default to False.

latex : bool

whether use Quantikz, a LaTeX package, to plot circuits , default to True.

**kwargs

additional parameters for matplotlib plot.

Returns:

None, or a matplotlib.figure.Figure instance depending on latex and output.

Return type:

Figure | None

Notes

If latex is True, the circuit will be displayed in LaTeX format; if latex is False, the circuit will be displayed in matplotlib format, in which case we have three additional parameters: - output_plot: whether output the plot instance, default to False. - save_path: the save path of image. Defaults to None. - scale: scale coefficient of figure. Default to 1.0.

Examples

qc = Circuit(2)
qc.h()
qc.plot()
(Displays the plotted circuit using Quantikz or matplotlib)

Warning

Starting from QuAIRKit 2.5.0, Circuit.plot now defaults to using QuantiKz, a LaTeX package powered by TikZ, for rendering quantum circuit diagrams commonly used in academic publications. Support for plotting circuits with Matplotlib will be deprecated in the near future.

To fully utilize this feature, please ensure that a TeX distribution, such as [TeX Live](https://www.tug.org/texlive), is installed on your system. This will enhance your experience with QuAIRKit and quantum computing visualization.

h(qubits_idx='full')

Add single-qubit Hadamard gates.

The matrix form of such a gate is:

\[\begin{split}H = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.h()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{H} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{H} & {} & {}
s(qubits_idx='full')

Add single-qubit S gates.

The matrix form of such a gate is:

\[\begin{split}S = \begin{bmatrix} 1 & 0 \\ 0 & i \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.s()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{S} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{S} & {} & {}
sdg(qubits_idx='full')

Add single-qubit S dagger (S inverse) gates.

The matrix form of such a gate is:

\[\begin{split}S^\dagger = \begin{bmatrix} 1 & 0 \\ 0 & -i \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to 'full'.

Examples

qc = Circuit(2)
qc.sdg()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{S^\dagger} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{S^\dagger} & {} & {}
t(qubits_idx='full')

Add single-qubit T gates.

The matrix form of such a gate is:

\[\begin{split}T = \begin{bmatrix} 1 & 0 \\ 0 & e^{i\pi/4} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.t()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{T} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{T} & {} & {}
tdg(qubits_idx='full')

Add single-qubit T dagger (T inverse) gates.

The matrix form of such a gate is:

\[\begin{split}T^\dagger = \begin{bmatrix} 1 & 0 \\ 0 & e^{-i\pi/4} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to 'full'.

Examples

qc = Circuit(2)
qc.tdg()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{T^\dagger} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{T^\dagger} & {} & {}
x(qubits_idx='full')

Add single-qubit X gates.

The matrix form of such a gate is:

\[\begin{split}X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.x()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{X} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{X} & {} & {}
y(qubits_idx='full')

Add single-qubit Y gates.

The matrix form of such a gate is:

\[\begin{split}Y = \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.y()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{Y} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{Y} & {} & {}
z(qubits_idx='full')

Add single-qubit Z gates.

The matrix form of such a gate is:

\[\begin{split}Z = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.z()
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{Z} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{Z} & {} & {}
p(qubits_idx='full', param=None, param_sharing=False)

Add single-qubit P gates.

The matrix form of such a gate is:

\[\begin{split}P(\theta) = \begin{bmatrix} 1 & 0 \\ 0 & e^{i\theta} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.p([0], torch.pi/2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{P(1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
rx(qubits_idx='full', param=None, param_sharing=False)

Add single-qubit rotation gates about the x-axis.

The matrix form of such a gate is:

\[\begin{split}R_X(\theta) = \begin{bmatrix} \cos\frac{\theta}{2} & -i\sin\frac{\theta}{2} \\ -i\sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.rx([0], torch.pi/2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{R_{x}(1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
ry(qubits_idx='full', param=None, param_sharing=False)

Add single-qubit rotation gates about the y-axis.

The matrix form of such a gate is:

\[\begin{split}R_Y(\theta) = \begin{bmatrix} \cos\frac{\theta}{2} & -\sin\frac{\theta}{2} \\ \sin\frac{\theta}{2} & \cos\frac{\theta}{2} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.ry([0], torch.pi/2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{R_{y}(1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
rz(qubits_idx='full', param=None, param_sharing=False)

Add single-qubit rotation gates about the z-axis.

The matrix form of such a gate is:

\[\begin{split}R_Z(\theta) = \begin{bmatrix} e^{-i\theta/2} & 0 \\ 0 & e^{i\theta/2} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.rz([0], torch.pi/2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{R_{z}(1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
u3(qubits_idx='full', param=None, param_sharing=False)

Add single-qubit rotation gates.

The matrix form of such a gate is:

\[\begin{split}U_3(\theta, \phi, \lambda) = \begin{bmatrix} \cos\frac\theta2 & -e^{i\lambda}\sin\frac\theta2 \\ e^{i\phi}\sin\frac\theta2 & e^{i(\phi+\lambda)}\cos\frac\theta2 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | int | str

Indices of the qubits on which the gates are applied. Defaults to ‘full’.

param : Tensor | Iterable[float]

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.u3([0], torch.pi/2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{U(1.57, 1.57, 1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
cnot(qubits_idx='cycle')

Add CNOT gates.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is:

\[\mathit{CNOT} = |0\rangle \langle 0|\otimes I + |1\rangle \langle 1|\otimes X\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

Examples

qc = Circuit(2)
qc.cnot([0, 1])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \targ{} & {} & {}
cy(qubits_idx='cycle')

Add controlled Y gates.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is:

\[\mathit{CY} = |0\rangle \langle 0|\otimes I + |1\rangle \langle 1|\otimes Y\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

Examples

qc = Circuit(2)
qc.cy([0, 1])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{Y} & {} & {}
cz(qubits_idx='linear')

Add controlled Z gates.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is:

\[\mathit{CZ} = |0\rangle \langle 0|\otimes I + |1\rangle \langle 1|\otimes Z\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘linear’.

Examples

qc = Circuit(2)
qc.cz([0, 1])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{Z} & {} & {}
swap(qubits_idx='linear')

Add SWAP gates.

The matrix form is:

\[\begin{split}\mathit{SWAP} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘linear’.

Examples

qc = Circuit(2)
qc.swap([0, 1])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[2,style={draw=none}]{\permute{2,1}} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
cp(qubits_idx='cycle', param=None, param_sharing=False)

Add controlled P gates.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is:

\[\begin{split}\mathit{CP}(\theta) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & e^{i\theta} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.cp([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{P(1.57)} & {} & {}
crx(qubits_idx='cycle', param=None, param_sharing=False)

Add controlled rotation gates about the x-axis.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is:

\[\mathit{CR_X} = |0\rangle \langle 0|\otimes I + |1\rangle \langle 1|\otimes R_X\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.crx([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{R_{x}(1.57)} & {} & {}
cry(qubits_idx='cycle', param=None, param_sharing=False)

Add controlled rotation gates about the y-axis.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is:

\[\mathit{CR_Y} = |0\rangle \langle 0|\otimes I + |1\rangle \langle 1|\otimes R_Y\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.cry([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{R_{y}(1.57)} & {} & {}
crz(qubits_idx='cycle', param=None, param_sharing=False)

Add controlled rotation gates about the z-axis.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is:

\[\mathit{CR_Z} = |0\rangle \langle 0|\otimes I + |1\rangle \langle 1|\otimes R_Z\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.crz([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{R_{z}(1.57)} & {} & {}
cu(qubits_idx='cycle', param=None, param_sharing=False)

Add controlled single-qubit rotation gates.

For a 2-qubit circuit, when qubits_idx is [0, 1], the matrix form is given by a controlled-U gate.

Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

param : Tensor | float

Parameters of the gate. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.cu([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{U(1.57, ...)} & {} & {}
rxx(qubits_idx='linear', param=None, param_sharing=False)

Add RXX gates.

The matrix form is:

\[\begin{split}R_{XX}(\theta) = \begin{bmatrix} \cos\frac{\theta}{2} & 0 & 0 & -i\sin\frac{\theta}{2} \\ 0 & \cos\frac{\theta}{2} & -i\sin\frac{\theta}{2} & 0 \\ 0 & -i\sin\frac{\theta}{2} & \cos\frac{\theta}{2} & 0 \\ -i\sin\frac{\theta}{2} & 0 & 0 & \cos\frac{\theta}{2} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘linear’.

param : Tensor | float

Parameters of the gate. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.rxx([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[2]{R_{xx}(1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
ryy(qubits_idx='linear', param=None, param_sharing=False)

Add RYY gates.

The matrix form is:

\[\begin{split}R_{YY}(\theta) = \begin{bmatrix} \cos\frac{\theta}{2} & 0 & 0 & i\sin\frac{\theta}{2} \\ 0 & \cos\frac{\theta}{2} & -i\sin\frac{\theta}{2} & 0 \\ 0 & -i\sin\frac{\theta}{2} & \cos\frac{\theta}{2} & 0 \\ i\sin\frac{\theta}{2} & 0 & 0 & \cos\frac{\theta}{2} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘linear’.

param : Tensor | float

Parameters of the gate. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.ryy([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[2]{R_{yy}(1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
rzz(qubits_idx='linear', param=None, param_sharing=False)

Add RZZ gates.

The matrix form is:

\[\begin{split}R_{ZZ}(\theta) = \begin{bmatrix} e^{-i\theta/2} & 0 & 0 & 0 \\ 0 & e^{i\theta/2} & 0 & 0 \\ 0 & 0 & e^{i\theta/2} & 0 \\ 0 & 0 & 0 & e^{-i\theta/2} \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘linear’.

param : Tensor | float

Parameters of the gate. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(2)
qc.rzz([0, 1], torch.pi / 2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[2]{R_{zz}(1.57)} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
ms(qubits_idx='cycle')

Add Mølmer-Sørensen (MS) gates.

The matrix form is:

\[\begin{split}\mathit{MS} = R_{XX}(-\pi/2) = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 0 & 0 & i \\ 0 & 1 & i & 0 \\ 0 & i & 1 & 0 \\ i & 0 & 0 & 1 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

Examples

qc = Circuit(2)
qc.ms([0, 1])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[2]{\text{MS}} & \meter[2]{} & {} \\
\lstick{} & {} & {} & {}
cswap(qubits_idx='cycle')

Add CSWAP (Fredkin) gates.

The matrix form is:

\[\begin{split}\mathit{CSWAP} = \begin{bmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & & 0 \\ \vdots & & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

Examples

qc = Circuit(3)
qc.cswap([0, 1, 2])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{1} & \meter[3]{} & {} \\
\lstick{} & \gate[2,style={draw=gray, dashed}]{\permute{2,1}} & {} & {} \\
\lstick{} & {} & {} & {}
ccx(qubits_idx='cycle')

Add CCX (Toffoli) gates.

The matrix form is:

\[\begin{split}\mathit{CCX} = \begin{bmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & & 0 \\ \vdots & & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{bmatrix}\end{split}\]
Parameters:
qubits_idx : Iterable[int] | str

Indices of the qubits on which the gates are applied. Defaults to ‘cycle’.

Examples

qc = Circuit(3)
qc.ccx([0, 1, 2])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \ctrl[]{2} & \meter[3]{} & {} \\
\lstick{} & \control{} & {} & {} \\
\lstick{} & \targ{} & {} & {}
universal_two_qubits(qubits_idx=None, param=None)

Add universal two-qubit gates. One such gate requires 15 parameters.

Parameters:
qubits_idx : List[int] | str

Indices of the qubits on which the gates are applied.

param : Tensor | float

Parameters of the gates. Defaults to None.

Examples

qc = Circuit(2)
qc.universal_two_qubits([0, 1])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
(The output latex code shows a universal 2-qubit layer.)
universal_three_qubits(qubits_idx=None, param=None)

Add universal three-qubit gates. One such gate requires 81 parameters.

Parameters:
qubits_idx : List[int] | None

Indices of the qubits on which the gates are applied.

param : Tensor | float

Parameters of the gates. Defaults to None.

Examples

qc = Circuit(3)
qc.universal_three_qubits([0, 1, 2])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
(The output latex code shows a universal 3-qubit layer.)
universal_qudits(system_idx, param=None, param_sharing=False)

Add universal qudit gates. One such gate requires \(d^2 - 1\) parameters, where \(d\) is the gate dimension.

Parameters:
system_idx : List[int]

Indices of the systems on which the gates are applied.

param : Tensor | float

Parameters of the gates. Defaults to None.

param_sharing : bool

Whether gates in the same layer share a parameter. Defaults to False.

Examples

qc = Circuit(1, 3)
qc.universal_qudits([0])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\operatorname{UNI}_{3}} & \meter[1]{} & {}
permute(perm, system_idx)

Add a permutation gate.

Parameters:
perm : List[int]

A list representing the permutation of subsystems.

system_idx : List[int]

Indices of the systems on which the gates are applied.

Examples

qc = Circuit(3)
qc.permute([1, 0, 2], [0, 1, 2])
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[3,style={draw=none}]{\permute{2,1,3}} & \meter[3]{} & {} \\
\lstick{} & {} & {} & {} \\
\lstick{} & {} & {} & {}
control_permute(perm, system_idx, control_idx=-1)

Add a controlled permutation gate.

Parameters:
perm : List[int]

A list representing the permutation of subsystems.

system_idx : List[int]

Indices of the systems on which the gates are applied.

control_idx : int

the index that controls the oracle. Defaults to -1, meaning the highest index.

Examples

qc = Circuit(4)
qc.control_permute([1, 0, 2], [3, 0, 1, 2], 1)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[3,style={draw=gray, dashed}]{\permute{2,1,3}} & \meter[4]{} & {} \\
\lstick{} & {} & {} & {} \\
\lstick{} & {} & {} & {} \\
\lstick{} & \ctrl[]{-1} & {} & {}
oracle(oracle, system_idx, control_idx=None, gate_name='oracle', latex_name=None)

Add an oracle gate.

Parameters:
oracle : Tensor

Unitary oracle.

system_idx : List[int | List[int]] | int

Indices of the systems on which the gate is applied.

control_idx : int | None

the index that controls the oracle. Defaults to None.

gate_name : str | None

name of the oracle.

latex_name : str | None

LaTeX name of the gate. Defaults to gate_name.

Examples

qc = Circuit(2)
qc.oracle(oracle=eye(2), system_idx=[0], latex_name=r'$Identity$')
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{$Identity$}
control_oracle(oracle, system_idx, control_idx=-1, gate_name='oracle', latex_name=None)

Add a controlled oracle gate.

Parameters:
oracle : Tensor

Unitary oracle.

system_idx : List[int | List[int]]

Indices of the systems on which the gate is applied.

control_idx : int

the index that controls the oracle. Defaults to -1.

gate_name : str | None

name of the oracle.

latex_name : str | None

LaTeX name of the gate.

Examples

qc = Circuit(2)
qc.control_oracle(oracle=eye(2), system_idx=[0, 1], control_idx=0, latex_name=r'$Identity$')
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \octrl[]{1} & \meter[2]{} & {} \\
\lstick{} & \gate[1]{$Identity$} & {} & {}
param_oracle(generator, num_acted_param, system_idx, control_idx=None, param=None, gate_name='param-oracle', latex_name=None, support_batch=False)

Add a parameterized oracle gate.

Parameters:
generator : Callable[[Tensor], Tensor]

Function to generate the oracle.

num_acted_param : int

Number of parameters required.

system_idx : List[int] | int

Indices of the systems on which the gate acts.

control_idx : int | None

The index that controls the oracle. Defaults to None.

param : Tensor | float

Input parameters for the gate. Defaults to None.

gate_name : str | None

Name of the oracle.

latex_name : str | None

LaTeX name of the gate.

support_batch : bool

Whether generator supports batched input.

Examples

def rotation_generator(params: torch.Tensor) -> torch.Tensor:
    theta = params[..., 0]
    cos_theta = torch.cos(theta / 2).unsqueeze(-1)
    sin_theta = torch.sin(theta / 2).unsqueeze(-1)
    matrix = torch.cat([
        torch.cat([cos_theta, -1j * sin_theta], dim=-1),
        torch.cat([-1j * sin_theta, cos_theta], dim=-1)
    ], dim=-2)
    return matrix

qc = Circuit(2)
qc.param_oracle(
    generator=rotation_generator,
    num_acted_param=1,
    system_idx=[0, 1],
    control_idx=0,
    param=None,
    gate_name="ControlledRotation",
    support_batch=True
)
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \octrl[]{1} \\
\lstick{} & \gate[1]{ControlledRotation(0.36)}
measure(system_idx=None, post_selection=None, if_print=False, measure_basis=None)

Perform a measurement on the specified systems.

Parameters:
system_idx : Iterable[int] | int | str

Systems to measure. Defaults to all.

post_selection : int | str

The post-selection result. Defaults to None.

if_print : bool

Whether to print collapse info. Defaults to False.

measure_basis : Tensor | None

Measurement basis.

Examples

qc = Circuit(2)
qc.measure()
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \meter[2]{} & {} \\
\lstick{} & {} & {}
locc(local_unitary, system_idx, label='M', latex_name='O')

Add a one-way LOCC protocol comprised of unitary operations.

Parameters:
local_unitary : Tensor

The local unitary operation.

system_idx : List[int | List[int]]

Systems on which the protocol is applied. The first element indicates the measure system.

label : str

Label for measurement. Defaults to ‘M’.

latex_name : str

LaTeX name for the applied operator. Defaults to ‘O’.

Examples

qc = Circuit(2)
qc.locc(local_unitary=x(), system_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \meter{} & \push{M} \wireoverride{c} & \ctrl[vertical wire=c]{1}\wireoverride{c}  \\
\lstick{} & {} & {} & \gate[1]{O^{(M)}}
param_locc(generator, num_acted_param, system_idx, param=None, label='M', latex_name='U', support_batch=False)

Add a one-way LOCC protocol comprised of unitary operations, where the applied unitary is parameterized.

Parameters:
generator : Callable[[Tensor], Tensor]

Function to generate the oracle.

num_acted_param : int

Number of parameters required.

system_idx : List[int | List[int]]

Systems on which the protocol is applied. The first element indicates the measure system.

param : Tensor | float

Input parameters for the gate. Defaults to None.

label : str

Label for measurement. Defaults to ‘M’.

latex_name : str

LaTeX name for the applied operator. Defaults to ‘U’.

support_batch : bool

Whether generator supports batched input.

linear_entangled_layer(qubits_idx=None, depth=1, param=None)

Add linear entangled layers consisting of Ry gates, Rz gates, and CNOT gates.

Parameters:
qubits_idx : List[int] | None

Systems to apply the layer on. Defaults to all.

depth : int

Number of layers. Defaults to 1.

param : Tensor | float

Parameters for the layer. Defaults to self-generated.

Examples

qc = Circuit(2)
qc.linear_entangled_layer([0, 1], depth=4)
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
(The output latex code shows a linear entangled layer.)
real_entangled_layer(qubits_idx=None, depth=1, param=None)

Add strongly entangled layers consisting of Ry gates and CNOT gates.

Parameters:
qubits_idx : List[int] | None

Systems to apply the layer on. Defaults to all.

depth : int

Number of layers. Defaults to 1.

param : Tensor | float

Layer parameters. Defaults to self-generated.

Examples

qc = Circuit(2)
qc.real_entangled_layer([0, 1], depth=4)
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
(The output latex code shows a real entangled layer.)
complex_entangled_layer(qubits_idx=None, depth=1, param=None)

Add strongly entangled layers consisting of single-qubit rotation gates and CNOT gates.

Parameters:
qubits_idx : List[int] | None

Systems to apply the layer on. Defaults to all.

depth : int

Number of layers. Defaults to 1.

param : Tensor | float

Layer parameters. Defaults to self-generated.

Examples

qc = Circuit(2)
qc.complex_entangled_layer([0, 1], depth=4)
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
(The output latex code shows a complex entangled layer.)
real_block_layer(qubits_idx=None, depth=1, param=None)

Add weakly entangled layers consisting of Ry gates and CNOT gates.

Parameters:
qubits_idx : List[int] | None

Systems to apply the layer on. Defaults to all.

depth : int

Number of layers. Defaults to 1.

param : Tensor | float

Layer parameters. Defaults to self-generated.

Examples

qc = Circuit(2)
qc.real_block_layer([0, 1], depth=4)
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
(The output latex code shows a real block layer.)
complex_block_layer(qubits_idx=None, depth=1, param=None)

Add weakly entangled layers consisting of single-qubit rotation gates and CNOT gates.

Parameters:
qubits_idx : List[int] | None

Systems to apply the layer on. Defaults to all.

depth : int

Number of layers. Defaults to 1.

param : Tensor | float

Layer parameters. Defaults to self-generated.

Examples

qc = Circuit(2)
qc.complex_block_layer([0, 1], depth=4)
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
(The output latex code shows a complex block layer.)
trotter(hamiltonian, time, qubits_idx=None, num_steps=1, order=1, name='H')

Add Trotter decompositions of a Hamiltonian evolution operator.

Parameters:
hamiltonian : Hamiltonian

Hamiltonian of the system whose time evolution is to be simulated.

time : float

Total evolution time.

qubits_idx : List[int] | None

Indices of the qubits on which the layer is applied. Defaults to None.

num_steps : int

Number of trotter blocks. Defaults to 1.

order : int

Order of the Trotter-Suzuki decomposition. Defaults to 1.

name : str

Name of the Hamiltonian. Defaults to ‘H’.

bit_flip(prob, qubits_idx='full')

Add bit flip channels.

Parameters:
prob : Tensor | float

Probability of a bit flip.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.bit_flip(prob=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{E}_{p = 0.5}^{\textrm{\tiny{(BF)}}}} \\
\lstick{} & \gate[1]{\mathcal{E}_{p = 0.5}^{\textrm{\tiny{(BF)}}}}
phase_flip(prob, qubits_idx='full')

Add phase flip channels.

Parameters:
prob : Tensor | float

Probability of a phase flip.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.phase_flip(prob=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{E}_{p = 0.5}^{\textrm{\tiny{(PF)}}}} \\
\lstick{} & \gate[1]{\mathcal{E}_{p = 0.5}^{\textrm{\tiny{(PF)}}}}
bit_phase_flip(prob, qubits_idx='full')

Add bit phase flip channels.

Parameters:
prob : Tensor | float

Probability of a bit phase flip.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.bit_phase_flip(prob=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{E}_{p = 0.5}^{\textrm{\tiny{(BPF)}}}} \\
\lstick{} & \gate[1]{\mathcal{E}_{p = 0.5}^{\textrm{\tiny{(BPF)}}}}
amplitude_damping(gamma, qubits_idx='full')

Add amplitude damping channels.

Parameters:
gamma : Tensor | float

Damping probability.

qubits_idx : Iterable[int] | int | str

Systems to apply the damping on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.amplitude_damping(gamma=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{E}_{\gamma = 0.5}^{\textrm{\tiny{(AD)}}}} \\
\lstick{} & \gate[1]{\mathcal{E}_{\gamma = 0.5}^{\textrm{\tiny{(AD)}}}}
generalized_amplitude_damping(gamma, prob, qubits_idx='full')

Add generalized amplitude damping channels.

Parameters:
gamma : Tensor | float

Damping probability.

prob : Tensor | float

Excitation probability.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.generalized_amplitude_damping(gamma=0.5, prob=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{E}_{\gamma = 0.5, p = 0.5}^{\textrm{\tiny{(GAD)}}}} \\
\lstick{} & \gate[1]{\mathcal{E}_{\gamma = 0.5, p = 0.5}^{\textrm{\tiny{(GAD)}}}}
phase_damping(gamma, qubits_idx='full')

Add phase damping channels.

Parameters:
gamma : Tensor | float

Phase damping parameter.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.phase_damping(gamma=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{E}_{\gamma = 0.5}^{\textrm{\tiny{(PD)}}}} \\
\lstick{} & \gate[1]{\mathcal{E}_{\gamma = 0.5}^{\textrm{\tiny{(PD)}}}}
depolarizing(prob, qubits_idx='full')

Add depolarizing channels.

Parameters:
prob : Tensor | float

Depolarizing probability.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.depolarizing(prob=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{D}_{p = 0.5}} \\
\lstick{} & \gate[1]{\mathcal{D}_{p = 0.5}}
generalized_depolarizing(prob, qubits_idx)

Add a general depolarizing channel.

Parameters:
prob : Tensor | float

Probabilities for the Pauli basis.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on.

Examples

qc = Circuit(2)
qc.generalized_depolarizing(prob=0.5, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[2]{\mathcal{D}_{p = 0.5}} \\
\lstick{} & {}
pauli_channel(prob, qubits_idx='full')

Add Pauli channels.

Parameters:
prob : Tensor | float

Probabilities for the Pauli X, Y, and Z operators.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.pauli_channel(prob=[0.1, 0.3, 0.5])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{N}} \\
\lstick{} & \gate[1]{\mathcal{N}}
reset_channel(prob, qubits_idx='full')

Add reset channels.

Parameters:
prob : Tensor | float

Probabilities for resetting to the basis states.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.reset_channel(prob=[0.5, 0.4], qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{N}} \\
\lstick{} & \gate[1]{\mathcal{N}}
thermal_relaxation(const_t, exec_time, qubits_idx='full')

Add thermal relaxation channels.

Parameters:
const_t : Tensor | Iterable[float]

The T1 and T2 relaxation times.

exec_time : Tensor | float

Gate execution time.

qubits_idx : Iterable[int] | int | str

Systems to apply the channel on. Defaults to ‘full’.

Examples

qc = Circuit(2)
qc.thermal_relaxation(const_t=[600, 300], exec_time=500, qubits_idx=[0, 1])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{N}} \\
\lstick{} & \gate[1]{\mathcal{N}}
choi_channel(choi_repr, system_idx)

Add custom channels in the Choi representation.

Parameters:
choi_repr : Iterable[Tensor]

Choi representation.

system_idx : Iterable[Iterable[int]] | Iterable[int] | int

Systems to apply the channel on.

Examples

qc = Circuit(2)
X = torch.tensor(x(), dtype=torch.complex64)
choi = X.kron(X) / 2
qc.choi_channel(choi_repr=choi, system_idx=[0])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{N}}
kraus_channel(kraus_oper, system_idx)

Add custom channels in the Kraus representation.

Parameters:
kraus_oper : Iterable[Tensor]

Kraus operators.

system_idx : Iterable[Iterable[int]] | Iterable[int] | int

Systems to apply the channel on.

Examples

qc = Circuit(2)
qc.kraus_channel(kraus_oper=eye(2), system_idx=[0])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{N}}
stinespring_channel(stinespring_repr, system_idx)

Add custom channels in the Stinespring representation.

Parameters:
stinespring_repr : Iterable[Tensor]

Stinespring representation.

system_idx : Iterable[Iterable[int]] | Iterable[int] | int

Systems to apply the channel on.

Examples

qc = Circuit(2)
qc.stinespring_channel(stinespring_repr=eye(2), system_idx=[0])
print(f'The latex code of this circuit is:\n{qc.to_latex()}')
The latex code of this circuit is:
\lstick{} & \gate[1]{\mathcal{N}}