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.96341538+0.j         0.        +0.16494296j 0.20821588+0.j
 0.        +0.03564791j]

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_690708cd681745b991df8fe0a55bce7d Circuit block cluster_315b03922c2249148d446608ea3bc381 Prep block 3b123206424a4306a75636cda2947546 0 554c63db03fa4e6b9c8c4c7eeac10060 3b123206424a4306a75636cda2947546--554c63db03fa4e6b9c8c4c7eeac10060 0bc32aa0e6714588ac60b936cf22f6fe 1 20fb8369265c4df2b6552da407674c3f RX(theta₀) 554c63db03fa4e6b9c8c4c7eeac10060--20fb8369265c4df2b6552da407674c3f b82d500c7cf346c1a28844b9ccea7e58 RY(theta₄) 20fb8369265c4df2b6552da407674c3f--b82d500c7cf346c1a28844b9ccea7e58 84abced678a14876b7a312a3805a32b2 RX(theta₈) b82d500c7cf346c1a28844b9ccea7e58--84abced678a14876b7a312a3805a32b2 c78b47b213c24958b80ac9453cc29b1d 84abced678a14876b7a312a3805a32b2--c78b47b213c24958b80ac9453cc29b1d f2a6ea3184334acdb3e7d364fa0afb37 c78b47b213c24958b80ac9453cc29b1d--f2a6ea3184334acdb3e7d364fa0afb37 43f3c3c2b8c84cb2a3ebcc47f1189e2b RX(theta₁₂) f2a6ea3184334acdb3e7d364fa0afb37--43f3c3c2b8c84cb2a3ebcc47f1189e2b 4d272b32c52a4bf4969d2423e7235da6 RY(theta₁₆) 43f3c3c2b8c84cb2a3ebcc47f1189e2b--4d272b32c52a4bf4969d2423e7235da6 56300d89bc744a1990a2bed12c236379 RX(theta₂₀) 4d272b32c52a4bf4969d2423e7235da6--56300d89bc744a1990a2bed12c236379 c3655607f7f94d3386971417470df767 56300d89bc744a1990a2bed12c236379--c3655607f7f94d3386971417470df767 4741f3d03e23441d927908442f73fec1 c3655607f7f94d3386971417470df767--4741f3d03e23441d927908442f73fec1 b1111219546049d9af62be8b05fd7cb3 4741f3d03e23441d927908442f73fec1--b1111219546049d9af62be8b05fd7cb3 60023fe8d1244128a306ac4b716f1513 a99facd950ef4e2bb0050da04c9c29e4 0bc32aa0e6714588ac60b936cf22f6fe--a99facd950ef4e2bb0050da04c9c29e4 bc868f0a6183420e86b9f362c677b44b 2 e961ff777a0b43a68c41819014f63eed RX(theta₁) a99facd950ef4e2bb0050da04c9c29e4--e961ff777a0b43a68c41819014f63eed a112cf43472c4e37a9c70f910fbe2fb7 RY(theta₅) e961ff777a0b43a68c41819014f63eed--a112cf43472c4e37a9c70f910fbe2fb7 56bcd4e3d93748a4bf5b93b7bed7f7e6 RX(theta₉) a112cf43472c4e37a9c70f910fbe2fb7--56bcd4e3d93748a4bf5b93b7bed7f7e6 950a22da1238401d808b6e9b0157d8c0 X 56bcd4e3d93748a4bf5b93b7bed7f7e6--950a22da1238401d808b6e9b0157d8c0 950a22da1238401d808b6e9b0157d8c0--c78b47b213c24958b80ac9453cc29b1d d32626d8752e449b80eecd904b0f53de 950a22da1238401d808b6e9b0157d8c0--d32626d8752e449b80eecd904b0f53de 5ca7eb2b39ca4af4b181a102b51c3d40 RX(theta₁₃) d32626d8752e449b80eecd904b0f53de--5ca7eb2b39ca4af4b181a102b51c3d40 0e1913fa89354f4a8d9cd1a622a61770 RY(theta₁₇) 5ca7eb2b39ca4af4b181a102b51c3d40--0e1913fa89354f4a8d9cd1a622a61770 994d7397e0cd4c819512ee3f0683823c RX(theta₂₁) 0e1913fa89354f4a8d9cd1a622a61770--994d7397e0cd4c819512ee3f0683823c ae2adbb9bf7a4a27a3d4ed6f92a34fdc X 994d7397e0cd4c819512ee3f0683823c--ae2adbb9bf7a4a27a3d4ed6f92a34fdc ae2adbb9bf7a4a27a3d4ed6f92a34fdc--c3655607f7f94d3386971417470df767 99989b1082f4482d9372e7f18c75175a ae2adbb9bf7a4a27a3d4ed6f92a34fdc--99989b1082f4482d9372e7f18c75175a 99989b1082f4482d9372e7f18c75175a--60023fe8d1244128a306ac4b716f1513 da079a9fb13946e9b30995e305778100 dd68e0fc02f345bf91f02685fd8d4fa4 bc868f0a6183420e86b9f362c677b44b--dd68e0fc02f345bf91f02685fd8d4fa4 652bbbe731df4eda9bf814b30740e3dc 3 c9b1bacde9c04f77856234dbd1f37112 RX(theta₂) dd68e0fc02f345bf91f02685fd8d4fa4--c9b1bacde9c04f77856234dbd1f37112 da2b6f9561ea43ecb624a0abad7a2d97 RY(theta₆) c9b1bacde9c04f77856234dbd1f37112--da2b6f9561ea43ecb624a0abad7a2d97 5356fa8bac094c068ce51fa29346fe83 RX(theta₁₀) da2b6f9561ea43ecb624a0abad7a2d97--5356fa8bac094c068ce51fa29346fe83 af449c1928c0401fb02b1397a32c9da1 5356fa8bac094c068ce51fa29346fe83--af449c1928c0401fb02b1397a32c9da1 072770291ade4e5e8dd92e756db94199 X af449c1928c0401fb02b1397a32c9da1--072770291ade4e5e8dd92e756db94199 072770291ade4e5e8dd92e756db94199--d32626d8752e449b80eecd904b0f53de f55c80e86ddf4cfb9caa0bd7948806c2 RX(theta₁₄) 072770291ade4e5e8dd92e756db94199--f55c80e86ddf4cfb9caa0bd7948806c2 1696e2cda28e4f5cb78b4ee5442d9cf7 RY(theta₁₈) f55c80e86ddf4cfb9caa0bd7948806c2--1696e2cda28e4f5cb78b4ee5442d9cf7 e87d020df4b142e093eae80628947343 RX(theta₂₂) 1696e2cda28e4f5cb78b4ee5442d9cf7--e87d020df4b142e093eae80628947343 47eba224cc7444c7a60eee34d336ac4d e87d020df4b142e093eae80628947343--47eba224cc7444c7a60eee34d336ac4d a085d7bba17447c2abe814735839ab06 X 47eba224cc7444c7a60eee34d336ac4d--a085d7bba17447c2abe814735839ab06 a085d7bba17447c2abe814735839ab06--99989b1082f4482d9372e7f18c75175a a085d7bba17447c2abe814735839ab06--da079a9fb13946e9b30995e305778100 df310504f1be414ebb1a93d14055c86f e51f8b1409144271812eca1c966ac197 X 652bbbe731df4eda9bf814b30740e3dc--e51f8b1409144271812eca1c966ac197 20c041d45e454c04bf413a38fadcb972 RX(theta₃) e51f8b1409144271812eca1c966ac197--20c041d45e454c04bf413a38fadcb972 f4268f58a0fd4477a75c6092377f7ad6 RY(theta₇) 20c041d45e454c04bf413a38fadcb972--f4268f58a0fd4477a75c6092377f7ad6 15fafac5f3a64263bcbad5bcedd5cc57 RX(theta₁₁) f4268f58a0fd4477a75c6092377f7ad6--15fafac5f3a64263bcbad5bcedd5cc57 450e5c9b5b024a218180b3acc07802ed X 15fafac5f3a64263bcbad5bcedd5cc57--450e5c9b5b024a218180b3acc07802ed 450e5c9b5b024a218180b3acc07802ed--af449c1928c0401fb02b1397a32c9da1 f29696139f704453b82f4d6c3c6e8ab1 450e5c9b5b024a218180b3acc07802ed--f29696139f704453b82f4d6c3c6e8ab1 ff08004940ad43eb8ac5a2570d93eca1 RX(theta₁₅) f29696139f704453b82f4d6c3c6e8ab1--ff08004940ad43eb8ac5a2570d93eca1 ee728db15d0443aab851529d5a42aeb9 RY(theta₁₉) ff08004940ad43eb8ac5a2570d93eca1--ee728db15d0443aab851529d5a42aeb9 7ab6e23e8e944395b55fbe207fc002a9 RX(theta₂₃) ee728db15d0443aab851529d5a42aeb9--7ab6e23e8e944395b55fbe207fc002a9 f2a9cfe6a3d44de9948106349b55b88b X 7ab6e23e8e944395b55fbe207fc002a9--f2a9cfe6a3d44de9948106349b55b88b f2a9cfe6a3d44de9948106349b55b88b--47eba224cc7444c7a60eee34d336ac4d 464331225d1e4b7e91ef62f8954ea339 f2a9cfe6a3d44de9948106349b55b88b--464331225d1e4b7e91ef62f8954ea339 464331225d1e4b7e91ef62f8954ea339--df310504f1be414ebb1a93d14055c86f
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, 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, 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.2142+0.1216j, -0.0990+0.2213j,  0.1671+0.4044j, -0.2842-0.4005j,
          0.0661-0.0810j, -0.2227-0.4556j, -0.1442-0.0752j, -0.3208-0.2246j],
        [-0.0095+0.6175j,  0.4006-0.0110j, -0.0621-0.1283j, -0.0106-0.2222j,
         -0.3444-0.4034j, -0.0364+0.0958j, -0.0517-0.1274j,  0.0586+0.2720j]])

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