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.86909607+0.j  0.48486511+0.j -0.08546558+0.j -0.0476809 +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_73c1e9b3c71c4e02973a86ab24c31b28 Circuit block cluster_7a0e1a60565649339e4980880e7966ec Prep block afcbe6bbab054ff0bff29bbdc4a296b0 0 8fa9be296be84173bdf1836cc2f573a2 afcbe6bbab054ff0bff29bbdc4a296b0--8fa9be296be84173bdf1836cc2f573a2 899410a8ad17425eb442348c0135f437 1 9304be08ee8d4d12ba2f46dc8867377f RX(theta₀) 8fa9be296be84173bdf1836cc2f573a2--9304be08ee8d4d12ba2f46dc8867377f 92a547a033cb48bb9a3a7210749b321e RY(theta₄) 9304be08ee8d4d12ba2f46dc8867377f--92a547a033cb48bb9a3a7210749b321e 9589523dfb644e4eb4ed919c9138750e RX(theta₈) 92a547a033cb48bb9a3a7210749b321e--9589523dfb644e4eb4ed919c9138750e 439c4fafb3994212959c721127431ef2 9589523dfb644e4eb4ed919c9138750e--439c4fafb3994212959c721127431ef2 b91a4a066e6e468680d06ae52c871640 439c4fafb3994212959c721127431ef2--b91a4a066e6e468680d06ae52c871640 bfe153fcaf724999af5e9bfcb3b467e3 RX(theta₁₂) b91a4a066e6e468680d06ae52c871640--bfe153fcaf724999af5e9bfcb3b467e3 168762f885c84451bee4665779e29dae RY(theta₁₆) bfe153fcaf724999af5e9bfcb3b467e3--168762f885c84451bee4665779e29dae 49ddc56bae9f4662b2fbe2f3695dad71 RX(theta₂₀) 168762f885c84451bee4665779e29dae--49ddc56bae9f4662b2fbe2f3695dad71 bbb3b3af8e73499fbb29ed168d07c4d4 49ddc56bae9f4662b2fbe2f3695dad71--bbb3b3af8e73499fbb29ed168d07c4d4 0cd41a185a774a428508fc74081fc86b bbb3b3af8e73499fbb29ed168d07c4d4--0cd41a185a774a428508fc74081fc86b 2b74daf7c77b4b0f9674b767b9af732e 0cd41a185a774a428508fc74081fc86b--2b74daf7c77b4b0f9674b767b9af732e e6d61d9bf069406ba22b7dc6ee84a434 9993d886346a4500a017c3061af59639 899410a8ad17425eb442348c0135f437--9993d886346a4500a017c3061af59639 1a9b2533b2d24b39a38eccf74fd67332 2 e0c419b74c314436a26f8fd4f0b2091d RX(theta₁) 9993d886346a4500a017c3061af59639--e0c419b74c314436a26f8fd4f0b2091d 0bdeccf339344fda9f02c9ab6360d8ba RY(theta₅) e0c419b74c314436a26f8fd4f0b2091d--0bdeccf339344fda9f02c9ab6360d8ba 94e4a6402fde48b080c50be4339540ca RX(theta₉) 0bdeccf339344fda9f02c9ab6360d8ba--94e4a6402fde48b080c50be4339540ca cc615c31046f48ca9f636dd6fa20a866 X 94e4a6402fde48b080c50be4339540ca--cc615c31046f48ca9f636dd6fa20a866 cc615c31046f48ca9f636dd6fa20a866--439c4fafb3994212959c721127431ef2 1dd6688fea024c398adf6470f06f7447 cc615c31046f48ca9f636dd6fa20a866--1dd6688fea024c398adf6470f06f7447 5807187dd5fa4b6abbd5354504d9cab2 RX(theta₁₃) 1dd6688fea024c398adf6470f06f7447--5807187dd5fa4b6abbd5354504d9cab2 793c7059d6b64961b34956406e8323e2 RY(theta₁₇) 5807187dd5fa4b6abbd5354504d9cab2--793c7059d6b64961b34956406e8323e2 8d1ab062ab874e90b3d2d0c582404357 RX(theta₂₁) 793c7059d6b64961b34956406e8323e2--8d1ab062ab874e90b3d2d0c582404357 125a7681a01046c491e3fab4c65f80d1 X 8d1ab062ab874e90b3d2d0c582404357--125a7681a01046c491e3fab4c65f80d1 125a7681a01046c491e3fab4c65f80d1--bbb3b3af8e73499fbb29ed168d07c4d4 6b10607c6f8b43a29b005493d20717c8 125a7681a01046c491e3fab4c65f80d1--6b10607c6f8b43a29b005493d20717c8 6b10607c6f8b43a29b005493d20717c8--e6d61d9bf069406ba22b7dc6ee84a434 c710e97d09b54afea1f709f86543de43 b01d498ef6fd472c822200683bde7c6f 1a9b2533b2d24b39a38eccf74fd67332--b01d498ef6fd472c822200683bde7c6f 60211833675045f4b0f545e5dc7615c9 3 14b7fd69226e463a94f04a74f5bc0729 RX(theta₂) b01d498ef6fd472c822200683bde7c6f--14b7fd69226e463a94f04a74f5bc0729 f0ddcac3e1aa4c78bba36073113678b8 RY(theta₆) 14b7fd69226e463a94f04a74f5bc0729--f0ddcac3e1aa4c78bba36073113678b8 0aae466d5ef146f8a2940065e28ed9d7 RX(theta₁₀) f0ddcac3e1aa4c78bba36073113678b8--0aae466d5ef146f8a2940065e28ed9d7 a534d2c655624393b88af57f5b05e280 0aae466d5ef146f8a2940065e28ed9d7--a534d2c655624393b88af57f5b05e280 1782855c511f4426aeb03b011e6c95ed X a534d2c655624393b88af57f5b05e280--1782855c511f4426aeb03b011e6c95ed 1782855c511f4426aeb03b011e6c95ed--1dd6688fea024c398adf6470f06f7447 a54d1fb7a63b43aa83aab1d3ace36768 RX(theta₁₄) 1782855c511f4426aeb03b011e6c95ed--a54d1fb7a63b43aa83aab1d3ace36768 7c6343d236544da1847d0c5f5587addd RY(theta₁₈) a54d1fb7a63b43aa83aab1d3ace36768--7c6343d236544da1847d0c5f5587addd 19060c44bb9c45df883cdd01f5208a1e RX(theta₂₂) 7c6343d236544da1847d0c5f5587addd--19060c44bb9c45df883cdd01f5208a1e a26664b2bd734f9e84d4e795754b0c3b 19060c44bb9c45df883cdd01f5208a1e--a26664b2bd734f9e84d4e795754b0c3b fa22feb660cc48fea5b24e34031d7098 X a26664b2bd734f9e84d4e795754b0c3b--fa22feb660cc48fea5b24e34031d7098 fa22feb660cc48fea5b24e34031d7098--6b10607c6f8b43a29b005493d20717c8 fa22feb660cc48fea5b24e34031d7098--c710e97d09b54afea1f709f86543de43 9c962614efc6470693772856c105b69d 6c9b127801744440b1f060be8eac5e1c X 60211833675045f4b0f545e5dc7615c9--6c9b127801744440b1f060be8eac5e1c 206ef64717ac4653a46eadf6b3894757 RX(theta₃) 6c9b127801744440b1f060be8eac5e1c--206ef64717ac4653a46eadf6b3894757 d7239c8fdbf84aababc8e0fd6c67285a RY(theta₇) 206ef64717ac4653a46eadf6b3894757--d7239c8fdbf84aababc8e0fd6c67285a 308c6d9aa1864508ad0441eeab4eca12 RX(theta₁₁) d7239c8fdbf84aababc8e0fd6c67285a--308c6d9aa1864508ad0441eeab4eca12 11ba1e3274d64e17b67de868c17d0fca X 308c6d9aa1864508ad0441eeab4eca12--11ba1e3274d64e17b67de868c17d0fca 11ba1e3274d64e17b67de868c17d0fca--a534d2c655624393b88af57f5b05e280 f1d215b304e649cc85f9d4c434c0ef30 11ba1e3274d64e17b67de868c17d0fca--f1d215b304e649cc85f9d4c434c0ef30 9761bd09490b4be88a1bbc90a2453b4e RX(theta₁₅) f1d215b304e649cc85f9d4c434c0ef30--9761bd09490b4be88a1bbc90a2453b4e 535fa61903b34b5f80a5ace6a4b101b4 RY(theta₁₉) 9761bd09490b4be88a1bbc90a2453b4e--535fa61903b34b5f80a5ace6a4b101b4 3abd46ad66474dc9878b1fb2638b2d50 RX(theta₂₃) 535fa61903b34b5f80a5ace6a4b101b4--3abd46ad66474dc9878b1fb2638b2d50 078e6d29e1cd4888843f995bc7efcf5a X 3abd46ad66474dc9878b1fb2638b2d50--078e6d29e1cd4888843f995bc7efcf5a 078e6d29e1cd4888843f995bc7efcf5a--a26664b2bd734f9e84d4e795754b0c3b 92f6703d38234325b41336acb848a500 078e6d29e1cd4888843f995bc7efcf5a--92f6703d38234325b41336acb848a500 92f6703d38234325b41336acb848a500--9c962614efc6470693772856c105b69d
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, 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, 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.2770+0.1293j, -0.0215-0.1989j, -0.0059-0.1722j, -0.0701-0.5453j,
          0.1952-0.2482j,  0.0533-0.1674j,  0.3690+0.2146j,  0.3263+0.3397j],
        [ 0.3077+0.1175j, -0.0426-0.1098j,  0.0193-0.5347j,  0.3126-0.4919j,
         -0.0170+0.0276j, -0.2911+0.3312j, -0.1317-0.0877j, -0.0230+0.1749j]])

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