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.71347553-0.58459751j 0.        +0.j         0.29877691-0.24480761j
 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_47cd3c7fcacb4ae48e7427389cd3def6 Circuit block cluster_cbc479d0ea4443d4bb04808fff2b2dec Prep block 708fce5cbc5a4871ac3c815852f91261 0 bd0050a1457542fe80b70cfe6184d04c 708fce5cbc5a4871ac3c815852f91261--bd0050a1457542fe80b70cfe6184d04c 1fe996c41c214730b1362a51b6ed2762 1 d24a18af2c2c4225be7db3bd32d8e2c7 RX(theta₀) bd0050a1457542fe80b70cfe6184d04c--d24a18af2c2c4225be7db3bd32d8e2c7 249d380cc0754c38a8baf5137b2cf566 RY(theta₄) d24a18af2c2c4225be7db3bd32d8e2c7--249d380cc0754c38a8baf5137b2cf566 e8a2a9ecd385428d91f43a5f59b0a669 RX(theta₈) 249d380cc0754c38a8baf5137b2cf566--e8a2a9ecd385428d91f43a5f59b0a669 769d3b08447a43ac9c15edd523fe0afb e8a2a9ecd385428d91f43a5f59b0a669--769d3b08447a43ac9c15edd523fe0afb 5710f59e881a432fa85c7cf16e5df0c4 769d3b08447a43ac9c15edd523fe0afb--5710f59e881a432fa85c7cf16e5df0c4 b61529445e5245c1af744c31bca65007 RX(theta₁₂) 5710f59e881a432fa85c7cf16e5df0c4--b61529445e5245c1af744c31bca65007 54ba1f96088d49d2894c520aca38336e RY(theta₁₆) b61529445e5245c1af744c31bca65007--54ba1f96088d49d2894c520aca38336e 8bce27203a4f47f2b56156877c5e102f RX(theta₂₀) 54ba1f96088d49d2894c520aca38336e--8bce27203a4f47f2b56156877c5e102f 69ce68e9954349cdb7b7349147f1939d 8bce27203a4f47f2b56156877c5e102f--69ce68e9954349cdb7b7349147f1939d d106d71dd0824d449cd875f0427812db 69ce68e9954349cdb7b7349147f1939d--d106d71dd0824d449cd875f0427812db ef47da833443470c885aefd4faa0ca9a d106d71dd0824d449cd875f0427812db--ef47da833443470c885aefd4faa0ca9a d3b9e97f30594023903b02ec6619bc78 759d29cec9394cada2d63de1ae1c4797 1fe996c41c214730b1362a51b6ed2762--759d29cec9394cada2d63de1ae1c4797 04fa3c9469f84395847cd0dcceb63eab 2 7e1a8d0dc23240f384f83b0b16eee13d RX(theta₁) 759d29cec9394cada2d63de1ae1c4797--7e1a8d0dc23240f384f83b0b16eee13d 32711a67f2de48a6b1dee820489b80ec RY(theta₅) 7e1a8d0dc23240f384f83b0b16eee13d--32711a67f2de48a6b1dee820489b80ec 4d2e3eab95bc4119a29ac0289ee60996 RX(theta₉) 32711a67f2de48a6b1dee820489b80ec--4d2e3eab95bc4119a29ac0289ee60996 0c3122669dd84092b8b38881e5717ce7 X 4d2e3eab95bc4119a29ac0289ee60996--0c3122669dd84092b8b38881e5717ce7 0c3122669dd84092b8b38881e5717ce7--769d3b08447a43ac9c15edd523fe0afb 0fbf9e41e03348d78652e286f7bfae37 0c3122669dd84092b8b38881e5717ce7--0fbf9e41e03348d78652e286f7bfae37 bd46e2a2f7764579944376d7d58181b3 RX(theta₁₃) 0fbf9e41e03348d78652e286f7bfae37--bd46e2a2f7764579944376d7d58181b3 38e3e13148ce469582610931405124e5 RY(theta₁₇) bd46e2a2f7764579944376d7d58181b3--38e3e13148ce469582610931405124e5 492ca8df17774cb5ba91452c5f1ed3b7 RX(theta₂₁) 38e3e13148ce469582610931405124e5--492ca8df17774cb5ba91452c5f1ed3b7 fd98e1791c6141a6a6059cb17c36b3b2 X 492ca8df17774cb5ba91452c5f1ed3b7--fd98e1791c6141a6a6059cb17c36b3b2 fd98e1791c6141a6a6059cb17c36b3b2--69ce68e9954349cdb7b7349147f1939d d13a90009bd34210a7f8486aec101c9a fd98e1791c6141a6a6059cb17c36b3b2--d13a90009bd34210a7f8486aec101c9a d13a90009bd34210a7f8486aec101c9a--d3b9e97f30594023903b02ec6619bc78 018e6d7ebf4c4dbb9f015d707600512d 94cc3f73a32941b395854d72c2f94157 04fa3c9469f84395847cd0dcceb63eab--94cc3f73a32941b395854d72c2f94157 a12d4805c9c14f1cbf7f8b7ec466b973 3 553f111c5e9544e1a0fb10a49a4bfe00 RX(theta₂) 94cc3f73a32941b395854d72c2f94157--553f111c5e9544e1a0fb10a49a4bfe00 52911733ae6f43029e71041d064542b3 RY(theta₆) 553f111c5e9544e1a0fb10a49a4bfe00--52911733ae6f43029e71041d064542b3 0ceba898d4fa419fa8d55b8c90aa8376 RX(theta₁₀) 52911733ae6f43029e71041d064542b3--0ceba898d4fa419fa8d55b8c90aa8376 00a3642d290047daa2edd23f323036b1 0ceba898d4fa419fa8d55b8c90aa8376--00a3642d290047daa2edd23f323036b1 dd0dabacc9c143a08cc822159e2b1969 X 00a3642d290047daa2edd23f323036b1--dd0dabacc9c143a08cc822159e2b1969 dd0dabacc9c143a08cc822159e2b1969--0fbf9e41e03348d78652e286f7bfae37 95a3ef89cba048eaaaa902012a10cbf2 RX(theta₁₄) dd0dabacc9c143a08cc822159e2b1969--95a3ef89cba048eaaaa902012a10cbf2 273f986544b445e48aa6206c845b01ec RY(theta₁₈) 95a3ef89cba048eaaaa902012a10cbf2--273f986544b445e48aa6206c845b01ec 6f7cc16f064e4c2e8fe76063aa537831 RX(theta₂₂) 273f986544b445e48aa6206c845b01ec--6f7cc16f064e4c2e8fe76063aa537831 a9ae34712048490fa8f0d6aecac6b499 6f7cc16f064e4c2e8fe76063aa537831--a9ae34712048490fa8f0d6aecac6b499 1f1eb1dc9f1943a6b909f6a1ddd9dc87 X a9ae34712048490fa8f0d6aecac6b499--1f1eb1dc9f1943a6b909f6a1ddd9dc87 1f1eb1dc9f1943a6b909f6a1ddd9dc87--d13a90009bd34210a7f8486aec101c9a 1f1eb1dc9f1943a6b909f6a1ddd9dc87--018e6d7ebf4c4dbb9f015d707600512d 3b8135f367444fe0bc0e16ec877e0a3b 27514f51de4448728701728f7500dfb0 X a12d4805c9c14f1cbf7f8b7ec466b973--27514f51de4448728701728f7500dfb0 b8e0e70b8ba5485ea11f9b41e8afcc5e RX(theta₃) 27514f51de4448728701728f7500dfb0--b8e0e70b8ba5485ea11f9b41e8afcc5e adf8bdfdf215484f8f1e8cb0b24f7e1f RY(theta₇) b8e0e70b8ba5485ea11f9b41e8afcc5e--adf8bdfdf215484f8f1e8cb0b24f7e1f e49b3f85038a4a96988bfb01679415f3 RX(theta₁₁) adf8bdfdf215484f8f1e8cb0b24f7e1f--e49b3f85038a4a96988bfb01679415f3 c0b3f8fb30a54fb6b89935fcea3bd4ef X e49b3f85038a4a96988bfb01679415f3--c0b3f8fb30a54fb6b89935fcea3bd4ef c0b3f8fb30a54fb6b89935fcea3bd4ef--00a3642d290047daa2edd23f323036b1 47d63a05b9cb480c88eb770df1fccd58 c0b3f8fb30a54fb6b89935fcea3bd4ef--47d63a05b9cb480c88eb770df1fccd58 69a77766d19e49e79b9eadc8282cb3be RX(theta₁₅) 47d63a05b9cb480c88eb770df1fccd58--69a77766d19e49e79b9eadc8282cb3be 1a6723c0b2fe452eabfa378fc5f82b9d RY(theta₁₉) 69a77766d19e49e79b9eadc8282cb3be--1a6723c0b2fe452eabfa378fc5f82b9d aac5bd1d880e48aeabc614073a90e661 RX(theta₂₃) 1a6723c0b2fe452eabfa378fc5f82b9d--aac5bd1d880e48aeabc614073a90e661 ae02238819984f62bb885c2f685f317e X aac5bd1d880e48aeabc614073a90e661--ae02238819984f62bb885c2f685f317e ae02238819984f62bb885c2f685f317e--a9ae34712048490fa8f0d6aecac6b499 808ef8bfe0104fbb820c4bb9c8d704e2 ae02238819984f62bb885c2f685f317e--808ef8bfe0104fbb820c4bb9c8d704e2 808ef8bfe0104fbb820c4bb9c8d704e2--3b8135f367444fe0bc0e16ec877e0a3b
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],
        [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, 1.+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]])

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.2561-0.1032j,  0.0494+0.0587j,  0.3258+0.1880j,  0.2518-0.0606j,
         -0.3047-0.3365j, -0.0829+0.4560j,  0.2562+0.1226j, -0.1817+0.4180j],
        [ 0.3139+0.4935j, -0.2357+0.3002j,  0.4257+0.0696j,  0.0326+0.0448j,
          0.1226-0.1125j, -0.2873+0.1443j, -0.1797-0.3972j, -0.0041-0.0452j]])

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

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