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.45600936-0.30375134j 0.        +0.j         0.69621865-0.46375659j
 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_1470f093a2614456bb63468ce533dbf8 Circuit block cluster_7732a9e748b04b2b9cbdcc364bee83d2 Prep block d63a14db489c4bb896ff39c412caaf50 0 fbfd4821c30f48359169c6d13c09ab50 d63a14db489c4bb896ff39c412caaf50--fbfd4821c30f48359169c6d13c09ab50 d92c9688af524a9d947e4831af580c19 1 527e9a5960f64a2fa0e6b80a0287ef5e RX(theta₀) fbfd4821c30f48359169c6d13c09ab50--527e9a5960f64a2fa0e6b80a0287ef5e 4b294d579c5149c59dfff9474fe928dd RY(theta₄) 527e9a5960f64a2fa0e6b80a0287ef5e--4b294d579c5149c59dfff9474fe928dd bdab414212b34eaab6c1f6e237e563a3 RX(theta₈) 4b294d579c5149c59dfff9474fe928dd--bdab414212b34eaab6c1f6e237e563a3 f6e49552e5d64f89b09960b704e5317c bdab414212b34eaab6c1f6e237e563a3--f6e49552e5d64f89b09960b704e5317c 503c69bbcbc54d10a6c69c3def479093 f6e49552e5d64f89b09960b704e5317c--503c69bbcbc54d10a6c69c3def479093 257fa4ce6a664fc3a3e8020294a26624 RX(theta₁₂) 503c69bbcbc54d10a6c69c3def479093--257fa4ce6a664fc3a3e8020294a26624 dff5294bd59b498a89b781eb47b69562 RY(theta₁₆) 257fa4ce6a664fc3a3e8020294a26624--dff5294bd59b498a89b781eb47b69562 022c1410ba1946eaa86002fe39caa0f2 RX(theta₂₀) dff5294bd59b498a89b781eb47b69562--022c1410ba1946eaa86002fe39caa0f2 728ee556f2bb455e824e7dd9bac4c87a 022c1410ba1946eaa86002fe39caa0f2--728ee556f2bb455e824e7dd9bac4c87a 60f54b6068c6427b98e6e06340fe47a9 728ee556f2bb455e824e7dd9bac4c87a--60f54b6068c6427b98e6e06340fe47a9 581628ad3f3942d98df3fed234aa9f8e 60f54b6068c6427b98e6e06340fe47a9--581628ad3f3942d98df3fed234aa9f8e df0be16186ae4472aefebb71425f7784 2385913ddd4c4704bd28cb2d34966821 d92c9688af524a9d947e4831af580c19--2385913ddd4c4704bd28cb2d34966821 d0e22f55040c467cb10c2151ebb6b920 2 854d65ad89e24ac58fbb0d2cd6e053d0 RX(theta₁) 2385913ddd4c4704bd28cb2d34966821--854d65ad89e24ac58fbb0d2cd6e053d0 a338bc3e53414ac2bf1665b5a13de5d7 RY(theta₅) 854d65ad89e24ac58fbb0d2cd6e053d0--a338bc3e53414ac2bf1665b5a13de5d7 08d98fe1db6a4b7b8b60a92cadd06ed7 RX(theta₉) a338bc3e53414ac2bf1665b5a13de5d7--08d98fe1db6a4b7b8b60a92cadd06ed7 c8becf221fbb46978fdc148323cdaa4e X 08d98fe1db6a4b7b8b60a92cadd06ed7--c8becf221fbb46978fdc148323cdaa4e c8becf221fbb46978fdc148323cdaa4e--f6e49552e5d64f89b09960b704e5317c b88d4001c78942b8890e08d5e7aca18a c8becf221fbb46978fdc148323cdaa4e--b88d4001c78942b8890e08d5e7aca18a feee3fc285b945a09903d4b9400fe136 RX(theta₁₃) b88d4001c78942b8890e08d5e7aca18a--feee3fc285b945a09903d4b9400fe136 24239aa7f0d04c9dacda6f19d43058c0 RY(theta₁₇) feee3fc285b945a09903d4b9400fe136--24239aa7f0d04c9dacda6f19d43058c0 5b86f62d2f2e4e4e80d12dcfca7cee4e RX(theta₂₁) 24239aa7f0d04c9dacda6f19d43058c0--5b86f62d2f2e4e4e80d12dcfca7cee4e 6e5cf0e6a1c246878ab79aea96bd5783 X 5b86f62d2f2e4e4e80d12dcfca7cee4e--6e5cf0e6a1c246878ab79aea96bd5783 6e5cf0e6a1c246878ab79aea96bd5783--728ee556f2bb455e824e7dd9bac4c87a 1f51b4b4993a4f9f9f75337814536573 6e5cf0e6a1c246878ab79aea96bd5783--1f51b4b4993a4f9f9f75337814536573 1f51b4b4993a4f9f9f75337814536573--df0be16186ae4472aefebb71425f7784 db03575d38f4462786ff4c843a69e245 d1f1a55907f3410daf10496aa5acbc0d d0e22f55040c467cb10c2151ebb6b920--d1f1a55907f3410daf10496aa5acbc0d e316aba485864a01b4cdb79417dc36cf 3 858e55d5a1a446b0bc1fbf0b2fce419b RX(theta₂) d1f1a55907f3410daf10496aa5acbc0d--858e55d5a1a446b0bc1fbf0b2fce419b c0a8d8cea32744cea47416c593526ac9 RY(theta₆) 858e55d5a1a446b0bc1fbf0b2fce419b--c0a8d8cea32744cea47416c593526ac9 dba1bb30426e4c4095dd4fcfb1f7ae34 RX(theta₁₀) c0a8d8cea32744cea47416c593526ac9--dba1bb30426e4c4095dd4fcfb1f7ae34 d8d72ea272ab484a912441a947bc59b0 dba1bb30426e4c4095dd4fcfb1f7ae34--d8d72ea272ab484a912441a947bc59b0 09a8b1cf087d40debd7fca422b28e77e X d8d72ea272ab484a912441a947bc59b0--09a8b1cf087d40debd7fca422b28e77e 09a8b1cf087d40debd7fca422b28e77e--b88d4001c78942b8890e08d5e7aca18a dbd9824b72db4535b31a682b3ce1dbc6 RX(theta₁₄) 09a8b1cf087d40debd7fca422b28e77e--dbd9824b72db4535b31a682b3ce1dbc6 0741a3129df84c83a35072c44ce20474 RY(theta₁₈) dbd9824b72db4535b31a682b3ce1dbc6--0741a3129df84c83a35072c44ce20474 7c67fb0bf49c43d7b3c86ca5eb50dc52 RX(theta₂₂) 0741a3129df84c83a35072c44ce20474--7c67fb0bf49c43d7b3c86ca5eb50dc52 68e4b1a7155c4872ab8510cca7e48536 7c67fb0bf49c43d7b3c86ca5eb50dc52--68e4b1a7155c4872ab8510cca7e48536 14b4636e27c448af9e869ecd7cc80990 X 68e4b1a7155c4872ab8510cca7e48536--14b4636e27c448af9e869ecd7cc80990 14b4636e27c448af9e869ecd7cc80990--1f51b4b4993a4f9f9f75337814536573 14b4636e27c448af9e869ecd7cc80990--db03575d38f4462786ff4c843a69e245 24fa5775021a43b49b7340508bc19a71 d8d1f2293a2d4bc9a21bdd50276c9829 X e316aba485864a01b4cdb79417dc36cf--d8d1f2293a2d4bc9a21bdd50276c9829 19d994c0bdad4ac8a00f305d8080a566 RX(theta₃) d8d1f2293a2d4bc9a21bdd50276c9829--19d994c0bdad4ac8a00f305d8080a566 071744a6144c4e73be4606787107fc74 RY(theta₇) 19d994c0bdad4ac8a00f305d8080a566--071744a6144c4e73be4606787107fc74 aa40d011e7744173ad3430aa309ab606 RX(theta₁₁) 071744a6144c4e73be4606787107fc74--aa40d011e7744173ad3430aa309ab606 a9fbb69882c249efb63d3b3d5441f671 X aa40d011e7744173ad3430aa309ab606--a9fbb69882c249efb63d3b3d5441f671 a9fbb69882c249efb63d3b3d5441f671--d8d72ea272ab484a912441a947bc59b0 b8469f5de45a4ff29f8e306e7b3e2b49 a9fbb69882c249efb63d3b3d5441f671--b8469f5de45a4ff29f8e306e7b3e2b49 5320789a2e4242e5812b391b6fb572f3 RX(theta₁₅) b8469f5de45a4ff29f8e306e7b3e2b49--5320789a2e4242e5812b391b6fb572f3 04e013bdffe14458be7c27c9dde9ea82 RY(theta₁₉) 5320789a2e4242e5812b391b6fb572f3--04e013bdffe14458be7c27c9dde9ea82 d14ac4cc5ae34f44a56813f374d3bf2e RX(theta₂₃) 04e013bdffe14458be7c27c9dde9ea82--d14ac4cc5ae34f44a56813f374d3bf2e aafa2b924a1b448cb1da43863716e28e X d14ac4cc5ae34f44a56813f374d3bf2e--aafa2b924a1b448cb1da43863716e28e aafa2b924a1b448cb1da43863716e28e--68e4b1a7155c4872ab8510cca7e48536 ebe205a6ffb04f349cfd81d11fae1b96 aafa2b924a1b448cb1da43863716e28e--ebe205a6ffb04f349cfd81d11fae1b96 ebe205a6ffb04f349cfd81d11fae1b96--24fa5775021a43b49b7340508bc19a71
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, 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, 1.+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.1938-0.0520j, -0.2004+0.3661j, -0.0483-0.0577j, -0.2657-0.3253j,
          0.4508+0.0664j,  0.2878+0.0577j, -0.2979+0.1222j, -0.0662+0.4491j],
        [ 0.2315-0.2451j,  0.2807+0.1053j, -0.3644+0.4587j, -0.0791+0.0530j,
         -0.0125-0.1685j,  0.2731-0.3878j, -0.3455-0.2506j, -0.0893-0.0241j]])

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