Introduction to qudit quantum computing¶
In this tutorial, users will learn how to use qudit setting in QuAIRKit.
Table of Contents - Review on qubit case - Setup for qutrit case - Setup for general cases - Operations in qudit circuits - Adding custom gates - Adding custom channels - Measurement
[1]:
import quairkit as qkit
from quairkit import Circuit
from quairkit.database import *
from quairkit.loss import *
qkit.set_dtype('complex128')
split_line = '\n' + '-' * 100 + '\n' # a line of '-' for better readability
Review on qubit case¶
The default setting in quantum computing is qubit unless specified otherwise. For detailed instruction on qubit circuits, please refer to the tutorials of states and circuits.
Users can check the number of qubits, qutrits, and systems in the circuit using cir.num_qubits
, cir.num_qutrits
and cir.num_systems
, respectively.
For example, one can create a circuit with 3 qubits:
[2]:
num_qubits = 3 # number of qubits
cir = Circuit(num_qubits) # construct a circuit with specifying the number of qubits
cir.complex_entangled_layer() # add a complex entangled layer
print('The diagram of the quantum circuit:')
cir.plot()
print(split_line) # a line of '-' for better readability
print(f'Are the systems composed of qubits? {cir().are_qubits()}', end=split_line)
print(f'The total number of qubits in the circuit is {cir.num_qubits}', end=split_line)
print(f'The total number of qutrits in the circuit is {cir.num_qutrits}', end=split_line)
print(f'The total number of systems in the circuit is {cir.num_systems}', end=split_line)
The diagram of the quantum circuit:
----------------------------------------------------------------------------------------------------
Are the systems composed of qubits? True
----------------------------------------------------------------------------------------------------
The total number of qubits in the circuit is 3
----------------------------------------------------------------------------------------------------
The total number of qutrits in the circuit is 0
----------------------------------------------------------------------------------------------------
The total number of systems in the circuit is 3
----------------------------------------------------------------------------------------------------
One can also verify the dimension of each system using the System dimension
attribute from the State
class.
[3]:
print(f'The output state of the circuit is {cir()}')
The output state of the circuit is
---------------------------------------------------
Backend: state_vector
System dimension: [2, 2, 2]
System sequence: [2, 0, 1]
[ 0.59+0.j -0.01-0.07j -0.19+0.26j -0.03-0.15j -0.06+0.04j -0.33-0.56j
0.13+0.04j -0.11-0.26j]
---------------------------------------------------
Setup for qutrit case¶
To configure the qudit state and its corresponding circuit, users can set system_dim
to \(d\), where \(d\) represents the dimension of the qudit.
For instance, one can assign system_dim
= \(3\) for qutrit. Then, the following code establishes the qutrit setup:
[4]:
rho = random_state(2, system_dim=3) # generate a random 2-qutrit state
print(f'The number of systems of the state is {rho.num_systems}', end=split_line)
print(f'The dimension of each system of the state is {rho.system_dim}', end=split_line)
print(f'Are the systems composed of qutrits? {rho.are_qutrits()}', split_line)
print(f'Random 2-qutrit states: {rho}')
The number of systems of the state is 2
----------------------------------------------------------------------------------------------------
The dimension of each system of the state is [3, 3]
----------------------------------------------------------------------------------------------------
Are the systems composed of qutrits? True
----------------------------------------------------------------------------------------------------
Random 2-qutrit states:
---------------------------------------------------
Backend: density_matrix
System dimension: [3, 3]
System sequence: [0, 1]
[[ 0.11+0.j -0.04-0.04j 0. -0.04j -0.03+0.01j -0. +0.03j 0. +0.02j
-0. -0.01j -0. -0.02j -0.01-0.03j]
[-0.04+0.04j 0.1 +0.j -0.02-0.06j -0.01-0.j -0. +0.02j 0.02+0.01j
-0.01+0.02j -0.01-0.02j 0.01-0.01j]
[ 0. +0.04j -0.02+0.06j 0.23+0.j 0.04-0.01j -0.03+0.j -0.08+0.03j
-0. +0.01j 0.03-0.02j -0.01-0.03j]
[-0.03-0.01j -0.01+0.j 0.04+0.01j 0.1 +0.j 0.01-0.03j 0.02+0.j
0.01-0.01j 0.04+0.01j -0.03+0.01j]
[-0. -0.03j -0. -0.02j -0.03-0.j 0.01+0.03j 0.11+0.j -0.02-0.02j
0. +0.01j -0.01+0.03j -0.04+0.02j]
[ 0. -0.02j 0.02-0.01j -0.08-0.03j 0.02-0.j -0.02+0.02j 0.11+0.j
0.02-0.01j -0.01-0.01j -0.01-0.01j]
[-0. +0.01j -0.01-0.02j -0. -0.01j 0.01+0.01j 0. -0.01j 0.02+0.01j
0.09+0.j -0. +0.03j -0.03+0.01j]
[-0. +0.02j -0.01+0.02j 0.03+0.02j 0.04-0.01j -0.01-0.03j -0.01+0.01j
-0. -0.03j 0.09+0.j 0.01+0.j ]
[-0.01+0.03j 0.01+0.01j -0.01+0.03j -0.03-0.01j -0.04-0.02j -0.01+0.01j
-0.03-0.01j 0.01-0.j 0.07+0.j ]]
---------------------------------------------------
The corresponding qutrit circuit is as follows:
[5]:
cir = Circuit(num_systems=2, system_dim=3) # create a circuit with 2 systems, each of dimension 3
print(f'Are the systems composed of qutrits? {cir().are_qutrits()}', end=split_line)
print(f'The total number of qubits in the circuit is {cir.num_qubits}', end=split_line)
print(f'The total number of qutrits in the circuit is {cir.num_qutrits}', end=split_line)
print(f'The total number of systems in the circuit is {cir.num_systems}', end=split_line)
Are the systems composed of qutrits? True
----------------------------------------------------------------------------------------------------
The total number of qubits in the circuit is 0
----------------------------------------------------------------------------------------------------
The total number of qutrits in the circuit is 2
----------------------------------------------------------------------------------------------------
The total number of systems in the circuit is 2
----------------------------------------------------------------------------------------------------
Setup for general cases¶
QuAIRKit supports general \(d\)-dimensional setup for qudits, as well as their compound, by setting system_dim
as \([d_0, d_2, d_3, \cdots, d_{n-1}]\).
For example, to configure a 3-qudit system with dimensions \([2, 3, 6]\):
[6]:
rho = random_state(num_systems=3, system_dim=[2, 3, 6]) # generate a random state with 3 systems of dimensions [2, 3, 6]
print(f'The number of systems of the state is {rho.num_systems}', end=split_line)
print(f'The dimension of each system of the state is {rho.system_dim}', end=split_line)
print(f'Random 3-qudit states: {rho}')
The number of systems of the state is 3
----------------------------------------------------------------------------------------------------
The dimension of each system of the state is [2, 3, 6]
----------------------------------------------------------------------------------------------------
Random 3-qudit states:
---------------------------------------------------
Backend: density_matrix
System dimension: [2, 3, 6]
System sequence: [0, 1, 2]
[[ 0.03+0.j 0. -0.01j -0. -0.01j ... 0.02+0.j -0.01-0.02j
0.01+0.j ]
[ 0. +0.01j 0.01+0.j 0. -0.j ... -0.01-0.j 0.01-0.01j
-0.01-0.j ]
[-0. +0.01j 0. +0.j 0.01+0.j ... -0.01+0.01j 0. -0.j
0. +0.j ]
...
[ 0.02-0.j -0.01+0.j -0.01-0.01j ... 0.04+0.j -0. +0.j
0.01-0.01j]
[-0.01+0.02j 0.01+0.01j 0. +0.j ... -0. -0.j 0.03+0.j
-0. -0.01j]
[ 0.01-0.j -0.01+0.j 0. -0.j ... 0.01+0.01j -0. +0.01j
0.03+0.j ]]
---------------------------------------------------
For clarity, the following figure illustrates compound dimensions of qudits in a quantum circuit.
[7]:
cir = Circuit(3, system_dim=[2, 3, 6]) # generate a random state with 3 systems of dimensions [2, 3, 6]
print(f'The total number of qubits in the circuit is {cir.num_qubits}', end=split_line)
print(f'The total number of qutrits in the circuit is {cir.num_qutrits}', end=split_line)
print(f'The total number of systems in the circuit is {cir.num_systems}', end=split_line)
The total number of qubits in the circuit is 1
----------------------------------------------------------------------------------------------------
The total number of qutrits in the circuit is 1
----------------------------------------------------------------------------------------------------
The total number of systems in the circuit is 3
----------------------------------------------------------------------------------------------------
Operations in qudit circuits¶
QuAIRKit offers a variety of operations for qudit circuits, including:
In this section, we will demonstrate how to use these operations.
Adding custom gates¶
One can define and incorporate custom gates into qudit circuits. Here is an example of how to define custom gates in qudit circuits:
[8]:
# generate a random state with 3 systems of dimensions [2, 3, 6]
cir = Circuit(3, system_dim=[2, 3, 6])
# add a 3-dimension gate acting on the system 1
cir.universal_qudits([1])
# add a 6-dimension custom gate acting on the system 2
cir.oracle(random_unitary(1, system_dim=6), system_idx=[2])
# add a custom gate with the system 2 as control qudit acting on the 3 systems
cir.control_oracle(random_unitary(2, system_dim=[3, 2]), system_idx=[2, 1, 0])
print('The diagram of the qudit circuit:')
cir.plot()
The diagram of the qudit circuit:
Adding custom channels¶
One can create and integrate custom channels into qudit circuits. Here’s a simple example of how to create a replacement channel in QuAIRKit:
[9]:
# create a circuit with 3 qudits and dimensions [2, 3, 6] for corresponding qudit systems
cir = Circuit(3, system_dim=[4, 3, 2])
rho = random_state(1, system_dim=4) # create a random state
# generate the replacement channel in Choi representation
replacement_choi_repr = replacement_choi(rho)
# add the replacement channel acting on the system 0
cir.choi_channel(replacement_choi_repr, system_idx=[0])
print('The first system is replaced with state', rho)
print('while the output state of first system is:', cir().trace([1, 2]))
The first system is replaced with state
---------------------------------------------------
Backend: density_matrix
System dimension: [4]
System sequence: [0]
[[ 0.36+0.j -0.05-0.01j -0.12-0.06j -0.02+0.21j]
[-0.05+0.01j 0.3 +0.j -0.1 +0.02j -0.16-0.06j]
[-0.12+0.06j -0.1 -0.02j 0.11+0.j 0.02-0.06j]
[-0.02-0.21j -0.16+0.06j 0.02+0.06j 0.23+0.j ]]
---------------------------------------------------
while the output state of first system is:
---------------------------------------------------
Backend: density_matrix
System dimension: [4]
System sequence: [0]
[[ 0.36+0.j -0.05-0.01j -0.12-0.06j -0.02+0.21j]
[-0.05+0.01j 0.3 +0.j -0.1 +0.02j -0.16-0.06j]
[-0.12+0.06j -0.1 -0.02j 0.11+0.j 0.02-0.06j]
[-0.02-0.21j -0.16+0.06j 0.02+0.06j 0.23+0.j ]]
---------------------------------------------------
Similarly, QuAIRKit also supports batch computation for qudits. The following code demonstrates a simple example of batch computation in circuits:
[10]:
batch_size = 5 # the numbar of batch size
# create a circuit with 3 qudits and dimensions [2, 3, 6] for corresponding qudit systems
cir = Circuit(3, system_dim=[2, 3, 6])
cir.universal_qudits(system_idx=[0, 1]) # add a universal gate to qudit system 0 and 1
cir.oracle(random_unitary(1, size=batch_size, system_dim=6), system_idx=[2]) # add an custom gate to qudit system 2
print('The output state of the second system is:', cir().trace([0, 2]))
The output state of the second system is:
---------------------------------------------------
Backend: density_matrix
System dimension: [3]
System sequence: [0]
Batch size: [5]
# 0:
[[ 0.28+0.j -0.05+0.24j 0.02-0.11j]
[-0.05-0.24j 0.49+0.j -0.04-0.21j]
[ 0.02+0.11j -0.04+0.21j 0.23+0.j ]]
# 1:
[[ 0.28+0.j -0.05+0.24j 0.02-0.11j]
[-0.05-0.24j 0.49+0.j -0.04-0.21j]
[ 0.02+0.11j -0.04+0.21j 0.23+0.j ]]
# 2:
[[ 0.28+0.j -0.05+0.24j 0.02-0.11j]
[-0.05-0.24j 0.49+0.j -0.04-0.21j]
[ 0.02+0.11j -0.04+0.21j 0.23+0.j ]]
# 3:
[[ 0.28+0.j -0.05+0.24j 0.02-0.11j]
[-0.05-0.24j 0.49+0.j -0.04-0.21j]
[ 0.02+0.11j -0.04+0.21j 0.23+0.j ]]
# 4:
[[ 0.28+0.j -0.05+0.24j 0.02-0.11j]
[-0.05-0.24j 0.49+0.j -0.04-0.21j]
[ 0.02+0.11j -0.04+0.21j 0.23+0.j ]]
---------------------------------------------------
Measurement¶
Measurement operations in qudits are similar to those in qubit systems. Users just need to set the measurement basis dimensions to match the corresponding qudit system.
[11]:
op = Measure('x') # define Pauli X basis as the measurement basis
# measure the system 0 in the X basis for obtaining measurement result '1'
prob, state = op(cir(), system_idx=[0], keep_state=True, desired_result='1')
print("The first state for obtaining measurement result '1' for the system 0 is", state[0])
op = Measure() # computational basis as the measurement basis
# measure systems 1 and 2, and obtaining measurement result '2' for the system 1, and '4' for the system 2
prob, state = op(cir(), system_idx=[1, 2], desired_result='24', keep_state=True)
print("The first state for obtaining measurement result '2' for the system 1, and '4' for the system 2 is", state[0])
The first state for obtaining measurement result '1' for the system 0 is
---------------------------------------------------
Backend: state_vector
System dimension: [2, 3, 6]
System sequence: [0, 1, 2]
[ 0.14-0.18j -0.02+0.13j 0.05-0.21j -0. -0.03j 0.05-0.1j -0.05-0.j
0.09-0.18j 0. +0.11j 0.01-0.19j -0. -0.02j 0.03-0.09j -0.04+0.01j
0.22+0.24j -0.18-0.06j 0.28+0.13j 0.04+0.01j 0.13+0.09j 0.01-0.07j
-0.14+0.18j 0.02-0.13j -0.05+0.21j 0. +0.03j -0.05+0.1j 0.05+0.j
-0.09+0.18j -0. -0.11j -0.01+0.19j 0. +0.02j -0.03+0.09j 0.04-0.01j
-0.22-0.24j 0.18+0.06j -0.28-0.13j -0.04-0.01j -0.13-0.09j -0.01+0.07j]
---------------------------------------------------
The first state for obtaining measurement result '2' for the system 1, and '4' for the system 2 is
---------------------------------------------------
Backend: state_vector
System dimension: [2, 3, 6]
System sequence: [0, 1, 2]
[ 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0. +0.j 0.34+0.47j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j 0. +0.j
0. +0.j 0. +0.j 0. +0.j 0. +0.j -0.74-0.33j 0. +0.j ]
---------------------------------------------------
After measuring some qudits, if we want to perform additional operations, users can utilize the cir.collapse
function to collapse the measured qudits. By printing the output state of the circuit, the average probability distribution of the collapsed qudits is obtained.
[12]:
batch_size = 5 # the numbar of batch size
# create a circuit with 3 qudits and dimensions [2, 3, 6] for corresponding qudit systems
cir = Circuit(3, system_dim=[2, 3, 6])
cir.universal_qudits(system_idx=[0, 1]) # add a universal gate to qudit system 0 and 1
cir.oracle(random_unitary(1, size=batch_size, system_dim=6), system_idx=[2]) # add an custom gate to qudit system 2
# Collapse the second qudit to its first eigenstate and the third qudit to its third eigenstate
cir.collapse([1, 2], desired_result='13', if_print=True)
print("\n After collapsing the second qudit to its first eigenstate and the third qudit to its third eigenstate",
"the state of the first system is:", cir().trace([1, 2]))
systems [1, 2] collapse to the state |1>|3> with (average) probability 0.009825415569497007
After collapsing the second qudit to its first eigenstate and the third qudit to its third eigenstate the state of the first system is:
---------------------------------------------------
Backend: density_matrix
System dimension: [2]
System sequence: [0]
Batch size: [5]
# 0:
[[0.95+0.j 0.08+0.19j]
[0.08-0.19j 0.05+0.j ]]
# 1:
[[0.95+0.j 0.08+0.19j]
[0.08-0.19j 0.05+0.j ]]
# 2:
[[0.95+0.j 0.08+0.19j]
[0.08-0.19j 0.05+0.j ]]
# 3:
[[0.95+0.j 0.08+0.19j]
[0.08-0.19j 0.05+0.j ]]
# 4:
[[0.95+0.j 0.08+0.19j]
[0.08-0.19j 0.05+0.j ]]
---------------------------------------------------
[13]:
qkit.print_info()
---------VERSION---------
quairkit: 0.2.0
torch: 2.4.1+cpu
numpy: 1.26.0
scipy: 1.14.1
matplotlib: 3.9.2
---------SYSTEM---------
Python version: 3.10.15
OS: Windows
OS version: 10.0.26100
---------DEVICE---------
CPU: ARMv8 (64-bit) Family 8 Model 1 Revision 201, Qualcomm Technologies Inc