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.90080003+0.j         -0.4339478 +0.j          0.        +0.01420496j
  0.        -0.00684304j]

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_734219934fbb4504ba0fcdc320f3e10a Circuit block cluster_ed16e682cc4d4dab8ecd0c2cd17763ed Prep block 24f6e23f04984d0b9dd9907d26bd9c38 0 a1860d7d8f444531a809b17e3ac4acd2 24f6e23f04984d0b9dd9907d26bd9c38--a1860d7d8f444531a809b17e3ac4acd2 de5fbc8b34bb4761b3719d4e94cada9f 1 2fe9b3a40663427bb7ce78b88e88f964 RX(theta₀) a1860d7d8f444531a809b17e3ac4acd2--2fe9b3a40663427bb7ce78b88e88f964 06d44b8d2469410a90823210af43f3ef RY(theta₄) 2fe9b3a40663427bb7ce78b88e88f964--06d44b8d2469410a90823210af43f3ef 5750aab0dd2f47c893c8d7d7b3302655 RX(theta₈) 06d44b8d2469410a90823210af43f3ef--5750aab0dd2f47c893c8d7d7b3302655 e9c49566339147888545c99963513037 5750aab0dd2f47c893c8d7d7b3302655--e9c49566339147888545c99963513037 36b45805a848485c92d2ed6bfaa91ce2 e9c49566339147888545c99963513037--36b45805a848485c92d2ed6bfaa91ce2 e7659fd149394fc0af57a6eafac86a95 RX(theta₁₂) 36b45805a848485c92d2ed6bfaa91ce2--e7659fd149394fc0af57a6eafac86a95 94a96ba395ec4b89ba4bef2d86fd1e82 RY(theta₁₆) e7659fd149394fc0af57a6eafac86a95--94a96ba395ec4b89ba4bef2d86fd1e82 7d80947938954e40b6df225d962dde80 RX(theta₂₀) 94a96ba395ec4b89ba4bef2d86fd1e82--7d80947938954e40b6df225d962dde80 5f680224c3704295909e6e7308a38644 7d80947938954e40b6df225d962dde80--5f680224c3704295909e6e7308a38644 580bba694708467fa5e433da5298eb91 5f680224c3704295909e6e7308a38644--580bba694708467fa5e433da5298eb91 b1b8f78e8e0f4385b1cc0f73c45bb6a8 580bba694708467fa5e433da5298eb91--b1b8f78e8e0f4385b1cc0f73c45bb6a8 a3c9281bb7364d2f9d50de0fa9b6a6e8 ff40136af5144f3a820a6c8f5dba90be de5fbc8b34bb4761b3719d4e94cada9f--ff40136af5144f3a820a6c8f5dba90be aefcd584517845e4884870c17b5ab335 2 a786c3c1b3ac4415ab46de43f7e25d18 RX(theta₁) ff40136af5144f3a820a6c8f5dba90be--a786c3c1b3ac4415ab46de43f7e25d18 9562b22fb3904bf180d94bb96c963f5e RY(theta₅) a786c3c1b3ac4415ab46de43f7e25d18--9562b22fb3904bf180d94bb96c963f5e 787900a5ee4440be8cb3ffc3ffba7e17 RX(theta₉) 9562b22fb3904bf180d94bb96c963f5e--787900a5ee4440be8cb3ffc3ffba7e17 a95ac0051643451a811d6a89ae8b62f5 X 787900a5ee4440be8cb3ffc3ffba7e17--a95ac0051643451a811d6a89ae8b62f5 a95ac0051643451a811d6a89ae8b62f5--e9c49566339147888545c99963513037 1b2db72f25e44e1897e064700b86e0f2 a95ac0051643451a811d6a89ae8b62f5--1b2db72f25e44e1897e064700b86e0f2 516fce1c918a4a3198f466fe28e66684 RX(theta₁₃) 1b2db72f25e44e1897e064700b86e0f2--516fce1c918a4a3198f466fe28e66684 f8bf71c1ffb148ec9798dfb32ef9c30c RY(theta₁₇) 516fce1c918a4a3198f466fe28e66684--f8bf71c1ffb148ec9798dfb32ef9c30c 33a07ccc755d421b8583b173c43dd140 RX(theta₂₁) f8bf71c1ffb148ec9798dfb32ef9c30c--33a07ccc755d421b8583b173c43dd140 c4cbad772bab465ebfc9a590990262f0 X 33a07ccc755d421b8583b173c43dd140--c4cbad772bab465ebfc9a590990262f0 c4cbad772bab465ebfc9a590990262f0--5f680224c3704295909e6e7308a38644 77e631f9585248d7afa4eeaf1777699a c4cbad772bab465ebfc9a590990262f0--77e631f9585248d7afa4eeaf1777699a 77e631f9585248d7afa4eeaf1777699a--a3c9281bb7364d2f9d50de0fa9b6a6e8 6f7d7cf23a3d4314aed8c5154c0f777a 29a721ceb7c24caeaa745ba6023df682 aefcd584517845e4884870c17b5ab335--29a721ceb7c24caeaa745ba6023df682 c88c110b2ef546d2828065e398269995 3 53a50100731e4352842805c79c29ce7d RX(theta₂) 29a721ceb7c24caeaa745ba6023df682--53a50100731e4352842805c79c29ce7d 89bd8710a1be4903acf4a8d9c41cb5da RY(theta₆) 53a50100731e4352842805c79c29ce7d--89bd8710a1be4903acf4a8d9c41cb5da 0c37c20847f74c408878556eb0a4efba RX(theta₁₀) 89bd8710a1be4903acf4a8d9c41cb5da--0c37c20847f74c408878556eb0a4efba da6c49bec7704af392791959d5180ee4 0c37c20847f74c408878556eb0a4efba--da6c49bec7704af392791959d5180ee4 cf0b067cd281425f8d82e9496eaf345f X da6c49bec7704af392791959d5180ee4--cf0b067cd281425f8d82e9496eaf345f cf0b067cd281425f8d82e9496eaf345f--1b2db72f25e44e1897e064700b86e0f2 7f919045da524a96be493fa20f1a0816 RX(theta₁₄) cf0b067cd281425f8d82e9496eaf345f--7f919045da524a96be493fa20f1a0816 716b01decf414e3296dd05e17b1cac1e RY(theta₁₈) 7f919045da524a96be493fa20f1a0816--716b01decf414e3296dd05e17b1cac1e fcb4579550c94550a6e81f1f4d96910f RX(theta₂₂) 716b01decf414e3296dd05e17b1cac1e--fcb4579550c94550a6e81f1f4d96910f b166277368f644c2980576b3c80a9e99 fcb4579550c94550a6e81f1f4d96910f--b166277368f644c2980576b3c80a9e99 0873e6baf76a4030a47f222909b175f9 X b166277368f644c2980576b3c80a9e99--0873e6baf76a4030a47f222909b175f9 0873e6baf76a4030a47f222909b175f9--77e631f9585248d7afa4eeaf1777699a 0873e6baf76a4030a47f222909b175f9--6f7d7cf23a3d4314aed8c5154c0f777a d9fd8ecabf8c445b822ab952385ad1ab 5a4072dd7d5e40e78adc9e9825defc58 X c88c110b2ef546d2828065e398269995--5a4072dd7d5e40e78adc9e9825defc58 4de7ab77a3b04150bf153d48d55227aa RX(theta₃) 5a4072dd7d5e40e78adc9e9825defc58--4de7ab77a3b04150bf153d48d55227aa fca1f73a584f4538bba0e7d19f5fea51 RY(theta₇) 4de7ab77a3b04150bf153d48d55227aa--fca1f73a584f4538bba0e7d19f5fea51 e286116af2424595b561f5e09ea4c36c RX(theta₁₁) fca1f73a584f4538bba0e7d19f5fea51--e286116af2424595b561f5e09ea4c36c 40a4aea9a62f4c5fb45f9746d20e844a X e286116af2424595b561f5e09ea4c36c--40a4aea9a62f4c5fb45f9746d20e844a 40a4aea9a62f4c5fb45f9746d20e844a--da6c49bec7704af392791959d5180ee4 9b9aeb25736346d8b07e3dbc501029b0 40a4aea9a62f4c5fb45f9746d20e844a--9b9aeb25736346d8b07e3dbc501029b0 30d184b3f1164c40b4ab79dcdfb04625 RX(theta₁₅) 9b9aeb25736346d8b07e3dbc501029b0--30d184b3f1164c40b4ab79dcdfb04625 af505f221db74f9fbdea9a2d2801ab11 RY(theta₁₉) 30d184b3f1164c40b4ab79dcdfb04625--af505f221db74f9fbdea9a2d2801ab11 dc8a3f16bdd54feba027337d538b353b RX(theta₂₃) af505f221db74f9fbdea9a2d2801ab11--dc8a3f16bdd54feba027337d538b353b 255dcaf332e44962b2c8c7e24515f60d X dc8a3f16bdd54feba027337d538b353b--255dcaf332e44962b2c8c7e24515f60d 255dcaf332e44962b2c8c7e24515f60d--b166277368f644c2980576b3c80a9e99 39c809405bc14766831926c2f2be4e23 255dcaf332e44962b2c8c7e24515f60d--39c809405bc14766831926c2f2be4e23 39c809405bc14766831926c2f2be4e23--d9fd8ecabf8c445b822ab952385ad1ab
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, 1.+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.1637+0.0958j, -0.0597-0.1402j, -0.0572+0.1872j, -0.6421+0.3065j,
          0.2309-0.0577j, -0.2093-0.4496j, -0.0153-0.1012j,  0.0643+0.2813j],
        [-0.2104+0.3493j, -0.1238+0.3375j,  0.2100-0.0803j, -0.1789+0.1848j,
          0.0055-0.0008j,  0.1813-0.0599j, -0.0892-0.3952j, -0.2575+0.5664j]])

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