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.38529803+0.j         0.        +0.55576108j 0.        -0.41971478j
 0.60540445+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_ee379cb5733d4200ae1e60c4b20dc17c Circuit block cluster_8b733295bd564580ab16fe6972f5e045 Prep block 2ec9b767a42f42b4a51a31967ed75205 0 7b67e8f4ff3c4d4499c5383ecc9c3423 2ec9b767a42f42b4a51a31967ed75205--7b67e8f4ff3c4d4499c5383ecc9c3423 594b117fe6074d19ab12bb5375632189 1 718459294d2a4956b48da8eebb00c24e RX(theta₀) 7b67e8f4ff3c4d4499c5383ecc9c3423--718459294d2a4956b48da8eebb00c24e 328d31a2e5e344c68a1640f2cb749d2f RY(theta₄) 718459294d2a4956b48da8eebb00c24e--328d31a2e5e344c68a1640f2cb749d2f 90cc7d6f278f48af951bf8c5154a20e0 RX(theta₈) 328d31a2e5e344c68a1640f2cb749d2f--90cc7d6f278f48af951bf8c5154a20e0 bf5c98ab274042f7986732e0e5f9617c 90cc7d6f278f48af951bf8c5154a20e0--bf5c98ab274042f7986732e0e5f9617c 66169f65a39a4cfb878d1631ef5f7b20 bf5c98ab274042f7986732e0e5f9617c--66169f65a39a4cfb878d1631ef5f7b20 8f16f816d8204b348cac61c46d364def RX(theta₁₂) 66169f65a39a4cfb878d1631ef5f7b20--8f16f816d8204b348cac61c46d364def 9ce536e9f96a45248395b55779c2de38 RY(theta₁₆) 8f16f816d8204b348cac61c46d364def--9ce536e9f96a45248395b55779c2de38 c968fc1e51004703909dd2d79ba5218b RX(theta₂₀) 9ce536e9f96a45248395b55779c2de38--c968fc1e51004703909dd2d79ba5218b db4e640eebcd4396a89f68f184404400 c968fc1e51004703909dd2d79ba5218b--db4e640eebcd4396a89f68f184404400 87197e4f52e7427ebf5369a8837dd2e7 db4e640eebcd4396a89f68f184404400--87197e4f52e7427ebf5369a8837dd2e7 65ad8852f02e46bbbc54977b05ffc08d 87197e4f52e7427ebf5369a8837dd2e7--65ad8852f02e46bbbc54977b05ffc08d df95f63070cb4bc18d268a7e7ef0770c 1931cd29ce3b47758e9866fb50f6424f 594b117fe6074d19ab12bb5375632189--1931cd29ce3b47758e9866fb50f6424f 296e7583e1db4c5589b8277cd5716f02 2 1a97db961c6f41f29347eed8526c567e RX(theta₁) 1931cd29ce3b47758e9866fb50f6424f--1a97db961c6f41f29347eed8526c567e fc5e23d44db74da2a53fd7b5974c832f RY(theta₅) 1a97db961c6f41f29347eed8526c567e--fc5e23d44db74da2a53fd7b5974c832f 6bf6c8bc81ce410bb085987eb554f38c RX(theta₉) fc5e23d44db74da2a53fd7b5974c832f--6bf6c8bc81ce410bb085987eb554f38c 5e24d3ee0ffc47e6a45c214cbfa6eb94 X 6bf6c8bc81ce410bb085987eb554f38c--5e24d3ee0ffc47e6a45c214cbfa6eb94 5e24d3ee0ffc47e6a45c214cbfa6eb94--bf5c98ab274042f7986732e0e5f9617c 7534d3732c1440079a9d60e499e5b830 5e24d3ee0ffc47e6a45c214cbfa6eb94--7534d3732c1440079a9d60e499e5b830 0853dee4bf584a0cb4c2b07ba5b24f88 RX(theta₁₃) 7534d3732c1440079a9d60e499e5b830--0853dee4bf584a0cb4c2b07ba5b24f88 aae1d121579342469865202e4cd7a1c2 RY(theta₁₇) 0853dee4bf584a0cb4c2b07ba5b24f88--aae1d121579342469865202e4cd7a1c2 b3f955a854064f6882eb8b14bfc1148d RX(theta₂₁) aae1d121579342469865202e4cd7a1c2--b3f955a854064f6882eb8b14bfc1148d 976aba2a74b34284ac6e7b3ccb1cb5fc X b3f955a854064f6882eb8b14bfc1148d--976aba2a74b34284ac6e7b3ccb1cb5fc 976aba2a74b34284ac6e7b3ccb1cb5fc--db4e640eebcd4396a89f68f184404400 fd7730faa9194d98a353cf1ffccb8c31 976aba2a74b34284ac6e7b3ccb1cb5fc--fd7730faa9194d98a353cf1ffccb8c31 fd7730faa9194d98a353cf1ffccb8c31--df95f63070cb4bc18d268a7e7ef0770c f316814019674fcc93676b209bdaa8bc 52ee22c4133e40728fbf9c563d3d0d95 296e7583e1db4c5589b8277cd5716f02--52ee22c4133e40728fbf9c563d3d0d95 49e3a7b0cb114820a57c5985dd7e112d 3 72c86e4518254623b81ae24563d228cc RX(theta₂) 52ee22c4133e40728fbf9c563d3d0d95--72c86e4518254623b81ae24563d228cc 983e74d8f3654f1ebb4f046f2d061ff4 RY(theta₆) 72c86e4518254623b81ae24563d228cc--983e74d8f3654f1ebb4f046f2d061ff4 63ac253c50934ee291dba47078da84f6 RX(theta₁₀) 983e74d8f3654f1ebb4f046f2d061ff4--63ac253c50934ee291dba47078da84f6 bac982c1bd7f42458cc15d2ea3dc4dd5 63ac253c50934ee291dba47078da84f6--bac982c1bd7f42458cc15d2ea3dc4dd5 b19c8a5ae1724f03976145b0c4cfac00 X bac982c1bd7f42458cc15d2ea3dc4dd5--b19c8a5ae1724f03976145b0c4cfac00 b19c8a5ae1724f03976145b0c4cfac00--7534d3732c1440079a9d60e499e5b830 ca29d780506c45f082e8efd32ac189c1 RX(theta₁₄) b19c8a5ae1724f03976145b0c4cfac00--ca29d780506c45f082e8efd32ac189c1 722b89887586420e8fe38df72a87ded6 RY(theta₁₈) ca29d780506c45f082e8efd32ac189c1--722b89887586420e8fe38df72a87ded6 044ba763bccf457f9fd7804cd68e80e3 RX(theta₂₂) 722b89887586420e8fe38df72a87ded6--044ba763bccf457f9fd7804cd68e80e3 7c4066648a2443c7ae9ba7091c7b7642 044ba763bccf457f9fd7804cd68e80e3--7c4066648a2443c7ae9ba7091c7b7642 1eefff33bcde451d8af53608157d4571 X 7c4066648a2443c7ae9ba7091c7b7642--1eefff33bcde451d8af53608157d4571 1eefff33bcde451d8af53608157d4571--fd7730faa9194d98a353cf1ffccb8c31 1eefff33bcde451d8af53608157d4571--f316814019674fcc93676b209bdaa8bc d78ce26eb9464ff9a1f6a24a3dc7704a 3c3446e57dd049e3bd193dc90738942d X 49e3a7b0cb114820a57c5985dd7e112d--3c3446e57dd049e3bd193dc90738942d cea4aceacbe349228afa624de4ad1337 RX(theta₃) 3c3446e57dd049e3bd193dc90738942d--cea4aceacbe349228afa624de4ad1337 640bf636f8624b649ee39da384d1b665 RY(theta₇) cea4aceacbe349228afa624de4ad1337--640bf636f8624b649ee39da384d1b665 266dab4134124cf7bf8b4eff1cec3ed4 RX(theta₁₁) 640bf636f8624b649ee39da384d1b665--266dab4134124cf7bf8b4eff1cec3ed4 f021d5d76d4c4f60985ccfd831bf095d X 266dab4134124cf7bf8b4eff1cec3ed4--f021d5d76d4c4f60985ccfd831bf095d f021d5d76d4c4f60985ccfd831bf095d--bac982c1bd7f42458cc15d2ea3dc4dd5 17af3ab9e2b94db88ed32b9ed1c0a1ba f021d5d76d4c4f60985ccfd831bf095d--17af3ab9e2b94db88ed32b9ed1c0a1ba 2b9a3e6787314c978fc53f71cdb819b6 RX(theta₁₅) 17af3ab9e2b94db88ed32b9ed1c0a1ba--2b9a3e6787314c978fc53f71cdb819b6 39a05013a8ba468bb64d4e8afe67c509 RY(theta₁₉) 2b9a3e6787314c978fc53f71cdb819b6--39a05013a8ba468bb64d4e8afe67c509 d66e0b09ebdf4cb9980e34e12fc88dde RX(theta₂₃) 39a05013a8ba468bb64d4e8afe67c509--d66e0b09ebdf4cb9980e34e12fc88dde 74a9d5a236a84b838baeee0d5f25d02c X d66e0b09ebdf4cb9980e34e12fc88dde--74a9d5a236a84b838baeee0d5f25d02c 74a9d5a236a84b838baeee0d5f25d02c--7c4066648a2443c7ae9ba7091c7b7642 6406ec8a65a24fd785564006716b234d 74a9d5a236a84b838baeee0d5f25d02c--6406ec8a65a24fd785564006716b234d 6406ec8a65a24fd785564006716b234d--d78ce26eb9464ff9a1f6a24a3dc7704a
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([[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, 0.+0.j, 0.+0.j, 0.+0.j, 1.+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.0418+0.2231j,  0.0432-0.1901j, -0.0964+0.0036j, -0.0887+0.2639j,
         -0.7089-0.0307j, -0.2299-0.2473j,  0.0158+0.3053j,  0.1789+0.2841j],
        [-0.0417+0.1488j, -0.1586+0.4486j,  0.0976+0.3513j, -0.1487-0.2545j,
          0.1258-0.2726j, -0.4855-0.1732j, -0.2351+0.0458j,  0.1482-0.3078j]])

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)
├── X(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]])