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.90828256+0.j  0.38695419+0.j -0.14630225+0.j -0.06232892+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_01d1b066e778484ea674561155bf1f8f Circuit block cluster_4eaf5ad3872b4396845c424ee25bbab1 Prep block a57fb329edb1431d9ffbb41d3078b8f7 0 bae1df517ae24c13b99af9fd546acc53 a57fb329edb1431d9ffbb41d3078b8f7--bae1df517ae24c13b99af9fd546acc53 64ef8db7e3904cde9aea2017f007767f 1 ad2d61dd501742669656bddf25160190 RX(theta₀) bae1df517ae24c13b99af9fd546acc53--ad2d61dd501742669656bddf25160190 b635e95e9b2c4447a32ce3b602a720c9 RY(theta₄) ad2d61dd501742669656bddf25160190--b635e95e9b2c4447a32ce3b602a720c9 8e90952768d2494699682485dcaea8cf RX(theta₈) b635e95e9b2c4447a32ce3b602a720c9--8e90952768d2494699682485dcaea8cf cf572c032cca4d40bd9349acd5688749 8e90952768d2494699682485dcaea8cf--cf572c032cca4d40bd9349acd5688749 8d183f0ad4f241c99bde8b7f399e5019 cf572c032cca4d40bd9349acd5688749--8d183f0ad4f241c99bde8b7f399e5019 d8d6f270f68d499390c8b4869e25d447 RX(theta₁₂) 8d183f0ad4f241c99bde8b7f399e5019--d8d6f270f68d499390c8b4869e25d447 f7e70f1cc88e4774a9e7c72d0ab77cf7 RY(theta₁₆) d8d6f270f68d499390c8b4869e25d447--f7e70f1cc88e4774a9e7c72d0ab77cf7 e114418a76164b00aaf0d769e4ea0d7c RX(theta₂₀) f7e70f1cc88e4774a9e7c72d0ab77cf7--e114418a76164b00aaf0d769e4ea0d7c de8dfe8691a34843b329c144393bd61b e114418a76164b00aaf0d769e4ea0d7c--de8dfe8691a34843b329c144393bd61b d3b06b9aff5f405cae36f5bb11008451 de8dfe8691a34843b329c144393bd61b--d3b06b9aff5f405cae36f5bb11008451 f8bb563e42ce42dba3da4b543d6ce5a3 d3b06b9aff5f405cae36f5bb11008451--f8bb563e42ce42dba3da4b543d6ce5a3 fc875c4e1b034bd9bb3ba73af3a3deb3 5e3028319019422a9dc6df9aa519c150 64ef8db7e3904cde9aea2017f007767f--5e3028319019422a9dc6df9aa519c150 7c7a4e8daf224351890428b191b0a31e 2 5ecca52c92c540a2aa1a673f40b03259 RX(theta₁) 5e3028319019422a9dc6df9aa519c150--5ecca52c92c540a2aa1a673f40b03259 ad44d3f03f334be0b4b672ee8a940d30 RY(theta₅) 5ecca52c92c540a2aa1a673f40b03259--ad44d3f03f334be0b4b672ee8a940d30 f5aaeda0e0c840ecb7522f5921df40af RX(theta₉) ad44d3f03f334be0b4b672ee8a940d30--f5aaeda0e0c840ecb7522f5921df40af 5bbd61a7036c4d28aa60c62bc7d8a87f X f5aaeda0e0c840ecb7522f5921df40af--5bbd61a7036c4d28aa60c62bc7d8a87f 5bbd61a7036c4d28aa60c62bc7d8a87f--cf572c032cca4d40bd9349acd5688749 ca3f8d3531e04a558d744da0015e560a 5bbd61a7036c4d28aa60c62bc7d8a87f--ca3f8d3531e04a558d744da0015e560a 39c4b2ae3a364718a74d5e6a1d3e5f18 RX(theta₁₃) ca3f8d3531e04a558d744da0015e560a--39c4b2ae3a364718a74d5e6a1d3e5f18 949e448f5eda4550b9d997ccd4d0b320 RY(theta₁₇) 39c4b2ae3a364718a74d5e6a1d3e5f18--949e448f5eda4550b9d997ccd4d0b320 0c36cbf4fc2542b280f4d9a573795135 RX(theta₂₁) 949e448f5eda4550b9d997ccd4d0b320--0c36cbf4fc2542b280f4d9a573795135 ccbcc66e0dc04b269a05371c595ce78b X 0c36cbf4fc2542b280f4d9a573795135--ccbcc66e0dc04b269a05371c595ce78b ccbcc66e0dc04b269a05371c595ce78b--de8dfe8691a34843b329c144393bd61b 6083a122dec74fc388f6c97edc91d683 ccbcc66e0dc04b269a05371c595ce78b--6083a122dec74fc388f6c97edc91d683 6083a122dec74fc388f6c97edc91d683--fc875c4e1b034bd9bb3ba73af3a3deb3 75c8c5a367754b2c83cd96356312d6a7 3b03a925bad54162968644c357c3c75a 7c7a4e8daf224351890428b191b0a31e--3b03a925bad54162968644c357c3c75a 3b38a25c1c5f4f10b3284518655c4e26 3 98f6494a498c457a9e11ff7e1046058c RX(theta₂) 3b03a925bad54162968644c357c3c75a--98f6494a498c457a9e11ff7e1046058c 7ae4fc215d2b403a91803bdd5c485aa9 RY(theta₆) 98f6494a498c457a9e11ff7e1046058c--7ae4fc215d2b403a91803bdd5c485aa9 63812114966746a98a58ba165af995c9 RX(theta₁₀) 7ae4fc215d2b403a91803bdd5c485aa9--63812114966746a98a58ba165af995c9 e055b4556b6c4c4ab333404c92be8b9d 63812114966746a98a58ba165af995c9--e055b4556b6c4c4ab333404c92be8b9d 8c791f150ad04a948fcf0eed008c43fa X e055b4556b6c4c4ab333404c92be8b9d--8c791f150ad04a948fcf0eed008c43fa 8c791f150ad04a948fcf0eed008c43fa--ca3f8d3531e04a558d744da0015e560a 127d602ca1a8403ca10604b51f988f33 RX(theta₁₄) 8c791f150ad04a948fcf0eed008c43fa--127d602ca1a8403ca10604b51f988f33 5b54a0c766194c18a68aac94c9e7da66 RY(theta₁₈) 127d602ca1a8403ca10604b51f988f33--5b54a0c766194c18a68aac94c9e7da66 a97a517b292c41a7987eb5420707c2ee RX(theta₂₂) 5b54a0c766194c18a68aac94c9e7da66--a97a517b292c41a7987eb5420707c2ee dd126008ac41469da6b45ab022db0b9e a97a517b292c41a7987eb5420707c2ee--dd126008ac41469da6b45ab022db0b9e 417979b55cae4e45bb46914730c6ebd8 X dd126008ac41469da6b45ab022db0b9e--417979b55cae4e45bb46914730c6ebd8 417979b55cae4e45bb46914730c6ebd8--6083a122dec74fc388f6c97edc91d683 417979b55cae4e45bb46914730c6ebd8--75c8c5a367754b2c83cd96356312d6a7 bbf98e41c0d94129a12c59690057c670 a2a2529117a74561a246585eeb32e434 X 3b38a25c1c5f4f10b3284518655c4e26--a2a2529117a74561a246585eeb32e434 0e232c19111a480b98fd81246cd03194 RX(theta₃) a2a2529117a74561a246585eeb32e434--0e232c19111a480b98fd81246cd03194 b56959160c35414d8cc6617572e66173 RY(theta₇) 0e232c19111a480b98fd81246cd03194--b56959160c35414d8cc6617572e66173 8e45e17bee88445cbdd25a6f41cd7a9d RX(theta₁₁) b56959160c35414d8cc6617572e66173--8e45e17bee88445cbdd25a6f41cd7a9d 4fabc53316864a13a48be799f0bfc476 X 8e45e17bee88445cbdd25a6f41cd7a9d--4fabc53316864a13a48be799f0bfc476 4fabc53316864a13a48be799f0bfc476--e055b4556b6c4c4ab333404c92be8b9d 69df454064974e52a3103ae2cc2aa643 4fabc53316864a13a48be799f0bfc476--69df454064974e52a3103ae2cc2aa643 9cfe76535f5c4144a36f6f0fd7bb0fec RX(theta₁₅) 69df454064974e52a3103ae2cc2aa643--9cfe76535f5c4144a36f6f0fd7bb0fec 71b1632163f547e9abb820049eb9ff01 RY(theta₁₉) 9cfe76535f5c4144a36f6f0fd7bb0fec--71b1632163f547e9abb820049eb9ff01 742924a57a404a6bb3b7c099b7e51718 RX(theta₂₃) 71b1632163f547e9abb820049eb9ff01--742924a57a404a6bb3b7c099b7e51718 c045ad218622423e995e2b5672cee9d1 X 742924a57a404a6bb3b7c099b7e51718--c045ad218622423e995e2b5672cee9d1 c045ad218622423e995e2b5672cee9d1--dd126008ac41469da6b45ab022db0b9e 3fa50c272603409d9407e97f87ce6070 c045ad218622423e995e2b5672cee9d1--3fa50c272603409d9407e97f87ce6070 3fa50c272603409d9407e97f87ce6070--bbf98e41c0d94129a12c59690057c670
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, 0.+0.j, 0.+0.j, 0.+0.j, 1.+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.0824+0.4787j, -0.0539-0.1946j,  0.1156+0.4589j,  0.1344-0.2154j,
         -0.2126+0.3212j, -0.0655+0.3265j,  0.0400+0.1130j,  0.3646-0.1686j],
        [-0.0259-0.2140j, -0.3231-0.1988j,  0.0704-0.4587j,  0.4639-0.0569j,
         -0.2917-0.2823j,  0.1924+0.2168j, -0.0910+0.0082j,  0.3097-0.1508j]])

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