Skip to content

State initialization

Qadence offers convenience routines for preparing initial quantum states. These routines are divided into two approaches:

  • As a dense matrix.
  • From a suitable quantum circuit. This is available for every backend and it should be added in front of the desired quantum circuit to simulate.

Let's illustrate the usage of the state preparation routine.

from qadence import random_state, product_state, is_normalized, StateGeneratorType

# Random initial state.
# the default `type` is StateGeneratorType.HaarMeasureFast
state = random_state(n_qubits=2, type=StateGeneratorType.RANDOM_ROTATIONS)

# Check the normalization.
assert is_normalized(state)

# Product state from a given bitstring.
# NB: Qadence follows the big endian convention.
state = product_state("01")
Random initial state generated with rotations:

state = [ 0.48073329-0.34951263j  0.        +0.j         -0.47290848-0.65045676j
  0.        +0.j        ]

Product state corresponding to bitstring '01':

state = [0.+0.j 1.+0.j 0.+0.j 0.+0.j]

Now we see how to generate the product state corresponding to the one above with a suitable quantum circuit.

from qadence import product_block, tag, hea, QuantumCircuit
from qadence.draw import display

state_prep_block = product_block("01")
# display(state_prep_block)

# Let's now prepare a circuit.
n_qubits = 4

state_prep_block = product_block("0001")
tag(state_prep_block, "Prep block")

circuit_block = tag(hea(n_qubits, depth = 2), "Circuit block")

qc_with_state_prep = QuantumCircuit(n_qubits, state_prep_block, circuit_block)
%3 cluster_6eaaf7246d2548ec876bab0c650f21d6 Circuit block cluster_116d789419584da68b5830c2e433abf9 Prep block 8b8cb6f58a3f41e18c5b7ef68c3b3cd0 0 a5057b35f44f403ca40d7230dfd77e4c 8b8cb6f58a3f41e18c5b7ef68c3b3cd0--a5057b35f44f403ca40d7230dfd77e4c 0cb10c366021460198ce3ca413cc3c4f 1 8a95f7ca7e204fc1a93e1c5e5192e78a RX(theta₀) a5057b35f44f403ca40d7230dfd77e4c--8a95f7ca7e204fc1a93e1c5e5192e78a ee8eee636b9b401187c59465cbd2936e RY(theta₄) 8a95f7ca7e204fc1a93e1c5e5192e78a--ee8eee636b9b401187c59465cbd2936e 4c030454b2e84a23be8cc0a336a4a6da RX(theta₈) ee8eee636b9b401187c59465cbd2936e--4c030454b2e84a23be8cc0a336a4a6da f856f13fb8ba4966a83538d786712015 4c030454b2e84a23be8cc0a336a4a6da--f856f13fb8ba4966a83538d786712015 e878da0f695a44cb8edf20480f3b715e f856f13fb8ba4966a83538d786712015--e878da0f695a44cb8edf20480f3b715e 9f684aa010344638b0f02a049589ce27 RX(theta₁₂) e878da0f695a44cb8edf20480f3b715e--9f684aa010344638b0f02a049589ce27 ea85df960a1d4290a221c38600f9c803 RY(theta₁₆) 9f684aa010344638b0f02a049589ce27--ea85df960a1d4290a221c38600f9c803 c6068e2cee574e6d99e977c189ee2116 RX(theta₂₀) ea85df960a1d4290a221c38600f9c803--c6068e2cee574e6d99e977c189ee2116 b08a5a3f5b0742e0b225871a537031db c6068e2cee574e6d99e977c189ee2116--b08a5a3f5b0742e0b225871a537031db 4a7025bc1e4446679132aa7e227a2c9e b08a5a3f5b0742e0b225871a537031db--4a7025bc1e4446679132aa7e227a2c9e 3d1c15f96bc340698c95517f2d9ee07a 4a7025bc1e4446679132aa7e227a2c9e--3d1c15f96bc340698c95517f2d9ee07a 12a4cb9a522b4f76a32813bc43e4eb38 69b0f903705e4e6ea8424339a1dc532e 0cb10c366021460198ce3ca413cc3c4f--69b0f903705e4e6ea8424339a1dc532e 970c6a5397dd4cf8950c626711017f6f 2 6f70ac6fc7814ce9acb3083be8859479 RX(theta₁) 69b0f903705e4e6ea8424339a1dc532e--6f70ac6fc7814ce9acb3083be8859479 c94cdf64113a4410a19086573c75927d RY(theta₅) 6f70ac6fc7814ce9acb3083be8859479--c94cdf64113a4410a19086573c75927d 57879d78709241a7a71e64a6fbe225e7 RX(theta₉) c94cdf64113a4410a19086573c75927d--57879d78709241a7a71e64a6fbe225e7 3d9b0fb431bd4323a07e795c5a60b980 X 57879d78709241a7a71e64a6fbe225e7--3d9b0fb431bd4323a07e795c5a60b980 3d9b0fb431bd4323a07e795c5a60b980--f856f13fb8ba4966a83538d786712015 48072dee973946e8b450c7cde99ca38f 3d9b0fb431bd4323a07e795c5a60b980--48072dee973946e8b450c7cde99ca38f 61beccdda9224847a50ed25ffbb4547a RX(theta₁₃) 48072dee973946e8b450c7cde99ca38f--61beccdda9224847a50ed25ffbb4547a 49d5484f2ee54f8d9169b025f753c00b RY(theta₁₇) 61beccdda9224847a50ed25ffbb4547a--49d5484f2ee54f8d9169b025f753c00b 6460dfbf639c44dabe56509b6210dea2 RX(theta₂₁) 49d5484f2ee54f8d9169b025f753c00b--6460dfbf639c44dabe56509b6210dea2 7fc65a3b80ef45d5a1a0587b8f7cfaa9 X 6460dfbf639c44dabe56509b6210dea2--7fc65a3b80ef45d5a1a0587b8f7cfaa9 7fc65a3b80ef45d5a1a0587b8f7cfaa9--b08a5a3f5b0742e0b225871a537031db 633f36b23af4429ea0e1506e879a6c50 7fc65a3b80ef45d5a1a0587b8f7cfaa9--633f36b23af4429ea0e1506e879a6c50 633f36b23af4429ea0e1506e879a6c50--12a4cb9a522b4f76a32813bc43e4eb38 930f5edc75594a378e6814811d99eb79 50ca25ee1fcf48bc81274f839d7e5a41 970c6a5397dd4cf8950c626711017f6f--50ca25ee1fcf48bc81274f839d7e5a41 2e027f33e2b04ff8bdd8869e75ab9350 3 7b974d44e51f470a8456cc2548dd325b RX(theta₂) 50ca25ee1fcf48bc81274f839d7e5a41--7b974d44e51f470a8456cc2548dd325b f6ab36cbfb744767b71e20359b2c6bc3 RY(theta₆) 7b974d44e51f470a8456cc2548dd325b--f6ab36cbfb744767b71e20359b2c6bc3 f4bef8f5a550438e9bc7f6485c983cf0 RX(theta₁₀) f6ab36cbfb744767b71e20359b2c6bc3--f4bef8f5a550438e9bc7f6485c983cf0 cda7eb1b32dd4416b9803f5351154b48 f4bef8f5a550438e9bc7f6485c983cf0--cda7eb1b32dd4416b9803f5351154b48 bec2b372745d494f8094c2164fc7c3a8 X cda7eb1b32dd4416b9803f5351154b48--bec2b372745d494f8094c2164fc7c3a8 bec2b372745d494f8094c2164fc7c3a8--48072dee973946e8b450c7cde99ca38f 13108b0600f14edda75e4abb147358ac RX(theta₁₄) bec2b372745d494f8094c2164fc7c3a8--13108b0600f14edda75e4abb147358ac 7ae3afb060e04222a00c41d546f35924 RY(theta₁₈) 13108b0600f14edda75e4abb147358ac--7ae3afb060e04222a00c41d546f35924 c388a1ba3551483186d93d72a6d90409 RX(theta₂₂) 7ae3afb060e04222a00c41d546f35924--c388a1ba3551483186d93d72a6d90409 ce7e7c40baf34e1aa03314dd5648aa2c c388a1ba3551483186d93d72a6d90409--ce7e7c40baf34e1aa03314dd5648aa2c 59fe0223808b41bcaa540f5251d80040 X ce7e7c40baf34e1aa03314dd5648aa2c--59fe0223808b41bcaa540f5251d80040 59fe0223808b41bcaa540f5251d80040--633f36b23af4429ea0e1506e879a6c50 59fe0223808b41bcaa540f5251d80040--930f5edc75594a378e6814811d99eb79 bbf9015c779d4b4a92f5e7ea9eb4c568 0ba1e0859f35459cb90d04deb6ad788c X 2e027f33e2b04ff8bdd8869e75ab9350--0ba1e0859f35459cb90d04deb6ad788c ca9ee3a599cb4dd1a28a183310f86666 RX(theta₃) 0ba1e0859f35459cb90d04deb6ad788c--ca9ee3a599cb4dd1a28a183310f86666 5695359803b144ca817c952e6c566b61 RY(theta₇) ca9ee3a599cb4dd1a28a183310f86666--5695359803b144ca817c952e6c566b61 a6217ac7790f4e4e97ad3d7cd689d02c RX(theta₁₁) 5695359803b144ca817c952e6c566b61--a6217ac7790f4e4e97ad3d7cd689d02c 5dbeb20b3cc44971916b149b51953802 X a6217ac7790f4e4e97ad3d7cd689d02c--5dbeb20b3cc44971916b149b51953802 5dbeb20b3cc44971916b149b51953802--cda7eb1b32dd4416b9803f5351154b48 e95cb8bc14324fcfa51fc1aa689405bc 5dbeb20b3cc44971916b149b51953802--e95cb8bc14324fcfa51fc1aa689405bc 53226b3fc4384a9f893d05f9974e96f0 RX(theta₁₅) e95cb8bc14324fcfa51fc1aa689405bc--53226b3fc4384a9f893d05f9974e96f0 6fed055e94014b94b764226042de8530 RY(theta₁₉) 53226b3fc4384a9f893d05f9974e96f0--6fed055e94014b94b764226042de8530 fb6c70f706b649348fbc2b449ffe68a7 RX(theta₂₃) 6fed055e94014b94b764226042de8530--fb6c70f706b649348fbc2b449ffe68a7 28efb9f4b86d4fdda7f9ee4cc9c48ef7 X fb6c70f706b649348fbc2b449ffe68a7--28efb9f4b86d4fdda7f9ee4cc9c48ef7 28efb9f4b86d4fdda7f9ee4cc9c48ef7--ce7e7c40baf34e1aa03314dd5648aa2c c138f5308e99485e843f79b56f3da506 28efb9f4b86d4fdda7f9ee4cc9c48ef7--c138f5308e99485e843f79b56f3da506 c138f5308e99485e843f79b56f3da506--bbf9015c779d4b4a92f5e7ea9eb4c568
Several standard quantum states can be conveniently initialized in Qadence, both in statevector form as well as in block form as shown in following.

State vector initialization

Qadence offers a number of constructor functions for state vector preparation.

from qadence import uniform_state, zero_state, one_state

n_qubits = 3
batch_size = 2

uniform_state = uniform_state(n_qubits, batch_size)
zero_state = zero_state(n_qubits, batch_size)
one_state = one_state(n_qubits, batch_size)
Uniform state = 

tensor([[0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j,
         0.3536+0.j],
        [0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j, 0.3536+0.j,
         0.3536+0.j]])
Zero state = 

tensor([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
        [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])
One state = 

tensor([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
        [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]])

As already seen, product states can be easily created, even in batches:

from qadence import product_state, rand_product_state

# From a bitsring "100"
prod_state = product_state("100", batch_size)

# Or a random product state
rand_state = rand_product_state(n_qubits, batch_size)
Product state = 

tensor([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])

Random state = 

tensor([[1.+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, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])

Creating a GHZ state:

from qadence import ghz_state

ghz = ghz_state(n_qubits, batch_size)
GHZ state = 

tensor([[0.7071+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j,
         0.7071+0.j],
        [0.7071+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j, 0.0000+0.j,
         0.7071+0.j]])

Creating a random state uniformly sampled from a Haar measure:

from qadence import random_state

rand_haar_state = random_state(n_qubits, batch_size)
Random state from Haar = 

tensor([[-0.3557+0.3074j,  0.3065-0.3146j,  0.2787-0.3765j, -0.1341-0.2752j,
          0.0773+0.1036j,  0.2299-0.2545j,  0.3054+0.0909j, -0.0257-0.1907j],
        [-0.1873+0.2601j, -0.2972-0.3651j,  0.0140-0.1523j,  0.0253-0.2675j,
         -0.1018+0.2061j,  0.1370-0.2704j, -0.5043+0.4027j,  0.0351-0.1327j]])

Custom initial states can then be passed to either run, sample and expectation through the state argument

from qadence import random_state, product_state, CNOT, run

init_state = product_state("10")
final_state = run(CNOT(0, 1), state=init_state)
Final state = tensor([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]])

Density matrices conversion

It is also possible to obtain density matrices from statevectors. They can be passed as inputs to quantum programs performing density matrix based operations such as noisy simulations, when the backend allows such as PyQTorch.

from qadence import product_state, density_mat

init_state = product_state("10")
init_density_matrix = density_mat(init_state)

final_density_matrix = run(CNOT(0, 1), state=init_density_matrix)
Initial = DensityMatrix([[[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, 1.+0.j, 0.+0.j],
                [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]]])
Final = DensityMatrix([[[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, 1.-0.j]]])

Block initialization

Not all backends support custom statevector initialization, however previous utility functions have their counterparts to initialize the respective blocks:

from qadence import uniform_block, one_block

n_qubits = 3

uniform_block = uniform_block(n_qubits)

one_block = one_block(n_qubits)
KronBlock(0,1,2)
├── H(0)
├── H(1)
└── H(2)
KronBlock(0,1,2)
├── X(0)
├── X(1)
└── X(2)

Similarly, for product states:

from qadence import product_block, rand_product_block

product_block = product_block("100")

rand_product_block = rand_product_block(n_qubits)
KronBlock(0,1,2)
├── X(0)
├── I(1)
└── I(2)
KronBlock(0,1,2)
├── I(0)
├── I(1)
└── X(2)

And GHZ states:

from qadence import ghz_block

ghz_block = ghz_block(n_qubits)
ChainBlock(0,1,2)
├── H(0)
└── ChainBlock(0,1,2)
    ├── CNOT(0, 1)
    └── CNOT(1, 2)

Initial state blocks can simply be chained at the start of a given circuit.

Utility functions

Some state vector utility functions are also available. We can easily create the probability mass function of a given statevector using torch.distributions.Categorical

from qadence import random_state, pmf

n_qubits = 3

state = random_state(n_qubits)
distribution = pmf(state)
Categorical(probs: torch.Size([1, 8]))

We can also check if a state is normalized:

from qadence import random_state, is_normalized

state = random_state(n_qubits)
print(is_normalized(state))
True

Or normalize a state:

import torch
from qadence import normalize, is_normalized

state = torch.tensor([[1, 1, 1, 1]], dtype = torch.cdouble)
print(normalize(state))
tensor([[0.5000+0.j, 0.5000+0.j, 0.5000+0.j, 0.5000+0.j]])