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.83300843+0.55326029j 0.        +0.j         0.        +0.j
 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_d31ab7486d7b441cb77e00a88521fd53 Circuit block cluster_01a6ecac350c4faf9346ebfd214221c8 Prep block 391c02ae9cd249b19ca5eeb158c5244c 0 e398909da8ef4c73834aac9686ba5c80 391c02ae9cd249b19ca5eeb158c5244c--e398909da8ef4c73834aac9686ba5c80 53496836ade943d4b9067363410fb8e3 1 1ef774169b714ab9bae09717c09d08e6 RX(theta₀) e398909da8ef4c73834aac9686ba5c80--1ef774169b714ab9bae09717c09d08e6 99b3b356733e46f4ad7e47a2aee562cd RY(theta₄) 1ef774169b714ab9bae09717c09d08e6--99b3b356733e46f4ad7e47a2aee562cd 4d85a87ce08c43c7bc85954adbc755e3 RX(theta₈) 99b3b356733e46f4ad7e47a2aee562cd--4d85a87ce08c43c7bc85954adbc755e3 5cb5b5050e224c1aa56650911ee172ee 4d85a87ce08c43c7bc85954adbc755e3--5cb5b5050e224c1aa56650911ee172ee 2d08747f234e45188e833ba6f1992490 5cb5b5050e224c1aa56650911ee172ee--2d08747f234e45188e833ba6f1992490 866d00ef365c46b197d4166f1b19fa7d RX(theta₁₂) 2d08747f234e45188e833ba6f1992490--866d00ef365c46b197d4166f1b19fa7d 5e820a5a27d645a2893a85440e5148ac RY(theta₁₆) 866d00ef365c46b197d4166f1b19fa7d--5e820a5a27d645a2893a85440e5148ac 28103abf492745aa82649c24fd34ae11 RX(theta₂₀) 5e820a5a27d645a2893a85440e5148ac--28103abf492745aa82649c24fd34ae11 f4e176f871b346fe8ba369408b6d0d47 28103abf492745aa82649c24fd34ae11--f4e176f871b346fe8ba369408b6d0d47 002021072c5a4cacbb57be78081f9b43 f4e176f871b346fe8ba369408b6d0d47--002021072c5a4cacbb57be78081f9b43 f047e4c9a7c24e63b582f14a27aba55b 002021072c5a4cacbb57be78081f9b43--f047e4c9a7c24e63b582f14a27aba55b 691521a825764976b6c3d6e241cef878 9a128514f6614107b8b0dd7ce43d9d86 53496836ade943d4b9067363410fb8e3--9a128514f6614107b8b0dd7ce43d9d86 3109adeace314fc4927b8211eaf7261f 2 1420710c7e2e419d9c2f820c52db7426 RX(theta₁) 9a128514f6614107b8b0dd7ce43d9d86--1420710c7e2e419d9c2f820c52db7426 a84c68d2df9e4a8ca60e58511059837f RY(theta₅) 1420710c7e2e419d9c2f820c52db7426--a84c68d2df9e4a8ca60e58511059837f 083c74d743bf49e393266df3e3873f65 RX(theta₉) a84c68d2df9e4a8ca60e58511059837f--083c74d743bf49e393266df3e3873f65 3efec2fc19f845028ee86fdb4fa5051b X 083c74d743bf49e393266df3e3873f65--3efec2fc19f845028ee86fdb4fa5051b 3efec2fc19f845028ee86fdb4fa5051b--5cb5b5050e224c1aa56650911ee172ee efc4a11ae0654b51a7d9e51017549433 3efec2fc19f845028ee86fdb4fa5051b--efc4a11ae0654b51a7d9e51017549433 182db63b2f404b8bb2b6cfe955748ab0 RX(theta₁₃) efc4a11ae0654b51a7d9e51017549433--182db63b2f404b8bb2b6cfe955748ab0 8d2e05a8db3b45e9a9c65a465a7949bb RY(theta₁₇) 182db63b2f404b8bb2b6cfe955748ab0--8d2e05a8db3b45e9a9c65a465a7949bb 82cf6e6e156248c69ff8579bc8c9ce9c RX(theta₂₁) 8d2e05a8db3b45e9a9c65a465a7949bb--82cf6e6e156248c69ff8579bc8c9ce9c 08ecbb2db27b43039756e26eaa259722 X 82cf6e6e156248c69ff8579bc8c9ce9c--08ecbb2db27b43039756e26eaa259722 08ecbb2db27b43039756e26eaa259722--f4e176f871b346fe8ba369408b6d0d47 68edca74172a4b36aa6e9bd15c90f9fd 08ecbb2db27b43039756e26eaa259722--68edca74172a4b36aa6e9bd15c90f9fd 68edca74172a4b36aa6e9bd15c90f9fd--691521a825764976b6c3d6e241cef878 19e964cfe6354f7d8559bbb8ca04e7f9 39521d370d044c999e47bce930f9df67 3109adeace314fc4927b8211eaf7261f--39521d370d044c999e47bce930f9df67 3b6f529525fd45a0b3187358319a4763 3 cf29b67a2e094ee49f1c43f3aa7d62e1 RX(theta₂) 39521d370d044c999e47bce930f9df67--cf29b67a2e094ee49f1c43f3aa7d62e1 428b060f62af40c2af25a5d8fa98bca9 RY(theta₆) cf29b67a2e094ee49f1c43f3aa7d62e1--428b060f62af40c2af25a5d8fa98bca9 fca06176b40244c08780a9adde5739c1 RX(theta₁₀) 428b060f62af40c2af25a5d8fa98bca9--fca06176b40244c08780a9adde5739c1 48f8a9f3b5af48a1be82925e2b157950 fca06176b40244c08780a9adde5739c1--48f8a9f3b5af48a1be82925e2b157950 ce7648c1dcc446b9ba89f148435e1cfb X 48f8a9f3b5af48a1be82925e2b157950--ce7648c1dcc446b9ba89f148435e1cfb ce7648c1dcc446b9ba89f148435e1cfb--efc4a11ae0654b51a7d9e51017549433 8c2400260e52444684bdd48f21f0c7cd RX(theta₁₄) ce7648c1dcc446b9ba89f148435e1cfb--8c2400260e52444684bdd48f21f0c7cd 716d566aa50d4fada9794bc062863dd3 RY(theta₁₈) 8c2400260e52444684bdd48f21f0c7cd--716d566aa50d4fada9794bc062863dd3 30488e25e2a14181a54f6b83d2fb9d2c RX(theta₂₂) 716d566aa50d4fada9794bc062863dd3--30488e25e2a14181a54f6b83d2fb9d2c a79f36beef3447faa98a85260012c8eb 30488e25e2a14181a54f6b83d2fb9d2c--a79f36beef3447faa98a85260012c8eb 6835c40f82ef4d5b86e5f23f1146bed8 X a79f36beef3447faa98a85260012c8eb--6835c40f82ef4d5b86e5f23f1146bed8 6835c40f82ef4d5b86e5f23f1146bed8--68edca74172a4b36aa6e9bd15c90f9fd 6835c40f82ef4d5b86e5f23f1146bed8--19e964cfe6354f7d8559bbb8ca04e7f9 fa3314338a0f44bea2c8019b1d713e54 1c4afdfbd2294818b4d42cce26e0635c X 3b6f529525fd45a0b3187358319a4763--1c4afdfbd2294818b4d42cce26e0635c 3af788b200e1460695e8dc38512cfe85 RX(theta₃) 1c4afdfbd2294818b4d42cce26e0635c--3af788b200e1460695e8dc38512cfe85 8356d2b57c79485795466718fdd8d0e3 RY(theta₇) 3af788b200e1460695e8dc38512cfe85--8356d2b57c79485795466718fdd8d0e3 566e4512d3a5498f8dd61c9f18f9a186 RX(theta₁₁) 8356d2b57c79485795466718fdd8d0e3--566e4512d3a5498f8dd61c9f18f9a186 265299cd986b476cb75d16f83776071c X 566e4512d3a5498f8dd61c9f18f9a186--265299cd986b476cb75d16f83776071c 265299cd986b476cb75d16f83776071c--48f8a9f3b5af48a1be82925e2b157950 604bc14d686c4bacb26c37a08655c4f0 265299cd986b476cb75d16f83776071c--604bc14d686c4bacb26c37a08655c4f0 451301126b3a48e5b304bb08cf23b349 RX(theta₁₅) 604bc14d686c4bacb26c37a08655c4f0--451301126b3a48e5b304bb08cf23b349 8649c3669d134a8984022a85596a4057 RY(theta₁₉) 451301126b3a48e5b304bb08cf23b349--8649c3669d134a8984022a85596a4057 c5f479ee4fe44bf8a3f512256ebadf2b RX(theta₂₃) 8649c3669d134a8984022a85596a4057--c5f479ee4fe44bf8a3f512256ebadf2b b9b0486ab589441ba85312a0f5a6ca68 X c5f479ee4fe44bf8a3f512256ebadf2b--b9b0486ab589441ba85312a0f5a6ca68 b9b0486ab589441ba85312a0f5a6ca68--a79f36beef3447faa98a85260012c8eb 3ee92655224e4619aa665c16980822cf b9b0486ab589441ba85312a0f5a6ca68--3ee92655224e4619aa665c16980822cf 3ee92655224e4619aa665c16980822cf--fa3314338a0f44bea2c8019b1d713e54
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, 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, 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.4778-0.3032j, -0.2188+0.0063j, -0.5854+0.1054j, -0.0209+0.1171j,
         -0.1233+0.1146j,  0.2949-0.1529j,  0.2178+0.2514j,  0.0053+0.1206j],
        [ 0.0060+0.1147j,  0.1033+0.1432j, -0.2309+0.3088j, -0.2681+0.2402j,
         -0.2283-0.2065j, -0.4066+0.1532j, -0.5098-0.1379j,  0.2512-0.2275j]])

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)
├── I(0)
├── X(1)
└── I(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]])