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.95664017+0.j         0.        -0.00752554j 0.29116612+0.j
 0.        -0.0022905j ]

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_7305264220b3471da02446a1cbbd19ae Circuit block cluster_932422c020144a0ba4163c2a8772a815 Prep block be3925e9ffe94c5cb22f96c8f9b57127 0 dfeaf46a5e164c748651b468cb92d5f7 be3925e9ffe94c5cb22f96c8f9b57127--dfeaf46a5e164c748651b468cb92d5f7 3c8444756f4445859b3c4e5d12433749 1 54f2f108e6114f96992b7adf49ea432d RX(theta₀) dfeaf46a5e164c748651b468cb92d5f7--54f2f108e6114f96992b7adf49ea432d 93b2f10816fb4d7e9f209b7052132f82 RY(theta₄) 54f2f108e6114f96992b7adf49ea432d--93b2f10816fb4d7e9f209b7052132f82 ff462ff7156245658d93d845c838fc11 RX(theta₈) 93b2f10816fb4d7e9f209b7052132f82--ff462ff7156245658d93d845c838fc11 00bdf19df1eb475e8292df67bc77f1dd ff462ff7156245658d93d845c838fc11--00bdf19df1eb475e8292df67bc77f1dd de1adef0b8bb4f7f8f96b899a9cc72c0 00bdf19df1eb475e8292df67bc77f1dd--de1adef0b8bb4f7f8f96b899a9cc72c0 393166091f3f4edbaf4a71753a1cc96f RX(theta₁₂) de1adef0b8bb4f7f8f96b899a9cc72c0--393166091f3f4edbaf4a71753a1cc96f af94045d0dec4c5b8fc42e7808b7a15e RY(theta₁₆) 393166091f3f4edbaf4a71753a1cc96f--af94045d0dec4c5b8fc42e7808b7a15e 55a2bfcbee384542b47c2c8a06e8c93c RX(theta₂₀) af94045d0dec4c5b8fc42e7808b7a15e--55a2bfcbee384542b47c2c8a06e8c93c 7f3721784d4c4d2a82d060fd7ba2c9d5 55a2bfcbee384542b47c2c8a06e8c93c--7f3721784d4c4d2a82d060fd7ba2c9d5 6900dfaa1540404eb44d569154743e1c 7f3721784d4c4d2a82d060fd7ba2c9d5--6900dfaa1540404eb44d569154743e1c efd5fc0422e74f009960cb175ac8fb99 6900dfaa1540404eb44d569154743e1c--efd5fc0422e74f009960cb175ac8fb99 2d58e333d87c4858a977b9d5277138c9 690979b378fc4cf394a1324c0fee9204 3c8444756f4445859b3c4e5d12433749--690979b378fc4cf394a1324c0fee9204 9bfbd9a879514f1685b47381437176ba 2 06464689a2c9436e80b3ce3d049be5b9 RX(theta₁) 690979b378fc4cf394a1324c0fee9204--06464689a2c9436e80b3ce3d049be5b9 82863bdd8a2f4ae9a35a1e6f17e3010e RY(theta₅) 06464689a2c9436e80b3ce3d049be5b9--82863bdd8a2f4ae9a35a1e6f17e3010e e0546681329343439d8833266273b968 RX(theta₉) 82863bdd8a2f4ae9a35a1e6f17e3010e--e0546681329343439d8833266273b968 0f1d27fdcd2e407aa5022e6d1b268915 X e0546681329343439d8833266273b968--0f1d27fdcd2e407aa5022e6d1b268915 0f1d27fdcd2e407aa5022e6d1b268915--00bdf19df1eb475e8292df67bc77f1dd 8a978623409f44b2aa84b9744d203e75 0f1d27fdcd2e407aa5022e6d1b268915--8a978623409f44b2aa84b9744d203e75 b4f9160e328c43f898d5f65d0f816475 RX(theta₁₃) 8a978623409f44b2aa84b9744d203e75--b4f9160e328c43f898d5f65d0f816475 beff56f662b449f8b70d0f86bc625507 RY(theta₁₇) b4f9160e328c43f898d5f65d0f816475--beff56f662b449f8b70d0f86bc625507 9b65a1b135ac4ca9a520f6e0e8c89aec RX(theta₂₁) beff56f662b449f8b70d0f86bc625507--9b65a1b135ac4ca9a520f6e0e8c89aec 2cb2c9358d924839a7666c22b11e08a5 X 9b65a1b135ac4ca9a520f6e0e8c89aec--2cb2c9358d924839a7666c22b11e08a5 2cb2c9358d924839a7666c22b11e08a5--7f3721784d4c4d2a82d060fd7ba2c9d5 5b333f1201b7414bb9997382032b3278 2cb2c9358d924839a7666c22b11e08a5--5b333f1201b7414bb9997382032b3278 5b333f1201b7414bb9997382032b3278--2d58e333d87c4858a977b9d5277138c9 93e3461c61794d869646bb384e834d90 0db1d4dcd8234e5cb49bf768160e1486 9bfbd9a879514f1685b47381437176ba--0db1d4dcd8234e5cb49bf768160e1486 78b79b172d6e4e45b424fbdf916bd6fe 3 35349f902aff412c81940875d8e62b15 RX(theta₂) 0db1d4dcd8234e5cb49bf768160e1486--35349f902aff412c81940875d8e62b15 17d04ae8d6334d37b6466b49ba8a0673 RY(theta₆) 35349f902aff412c81940875d8e62b15--17d04ae8d6334d37b6466b49ba8a0673 53198ce505984818b606584faf9e0945 RX(theta₁₀) 17d04ae8d6334d37b6466b49ba8a0673--53198ce505984818b606584faf9e0945 96f4a10755e541e6abcf95bd12f0d8de 53198ce505984818b606584faf9e0945--96f4a10755e541e6abcf95bd12f0d8de 230dd65e464f45fe808747ba202a64d7 X 96f4a10755e541e6abcf95bd12f0d8de--230dd65e464f45fe808747ba202a64d7 230dd65e464f45fe808747ba202a64d7--8a978623409f44b2aa84b9744d203e75 439b840f6d3d48c7be319796a2027645 RX(theta₁₄) 230dd65e464f45fe808747ba202a64d7--439b840f6d3d48c7be319796a2027645 44bb1595d1ee48339f9b6a6b72f0db2c RY(theta₁₈) 439b840f6d3d48c7be319796a2027645--44bb1595d1ee48339f9b6a6b72f0db2c e5f66dfddbde456c99f499fd016eb7fd RX(theta₂₂) 44bb1595d1ee48339f9b6a6b72f0db2c--e5f66dfddbde456c99f499fd016eb7fd 587013b33b004df6b29b5b5ef40b884e e5f66dfddbde456c99f499fd016eb7fd--587013b33b004df6b29b5b5ef40b884e ef52ba9fa87d406e8fe6c2a891d26f36 X 587013b33b004df6b29b5b5ef40b884e--ef52ba9fa87d406e8fe6c2a891d26f36 ef52ba9fa87d406e8fe6c2a891d26f36--5b333f1201b7414bb9997382032b3278 ef52ba9fa87d406e8fe6c2a891d26f36--93e3461c61794d869646bb384e834d90 9909cdd538a24e11b8e3193747d6c00e 73605968200c474e8186e10fb5eeb65f X 78b79b172d6e4e45b424fbdf916bd6fe--73605968200c474e8186e10fb5eeb65f d10725737f4e4e6a9d406c43be317e5e RX(theta₃) 73605968200c474e8186e10fb5eeb65f--d10725737f4e4e6a9d406c43be317e5e 91aad4b1b6be47edab24e751512c81bc RY(theta₇) d10725737f4e4e6a9d406c43be317e5e--91aad4b1b6be47edab24e751512c81bc 10518f3b900c480cb3b4d8c1f7ddc509 RX(theta₁₁) 91aad4b1b6be47edab24e751512c81bc--10518f3b900c480cb3b4d8c1f7ddc509 4f2e1c29b4474ad0ba794c1b17b6c1ab X 10518f3b900c480cb3b4d8c1f7ddc509--4f2e1c29b4474ad0ba794c1b17b6c1ab 4f2e1c29b4474ad0ba794c1b17b6c1ab--96f4a10755e541e6abcf95bd12f0d8de 90eb1599b79046c3ab9e6df2ca1b191f 4f2e1c29b4474ad0ba794c1b17b6c1ab--90eb1599b79046c3ab9e6df2ca1b191f 800c72e312344f2a9ee222e52e4f3683 RX(theta₁₅) 90eb1599b79046c3ab9e6df2ca1b191f--800c72e312344f2a9ee222e52e4f3683 c487ca091a4842aebd0a4c5b61c44104 RY(theta₁₉) 800c72e312344f2a9ee222e52e4f3683--c487ca091a4842aebd0a4c5b61c44104 6c0038225a8f47339666bcaf7d1c450b RX(theta₂₃) c487ca091a4842aebd0a4c5b61c44104--6c0038225a8f47339666bcaf7d1c450b ac89b4435ede48d6bf2b58601c671bbd X 6c0038225a8f47339666bcaf7d1c450b--ac89b4435ede48d6bf2b58601c671bbd ac89b4435ede48d6bf2b58601c671bbd--587013b33b004df6b29b5b5ef40b884e cc66885dc52f440897d18842eb33a05e ac89b4435ede48d6bf2b58601c671bbd--cc66885dc52f440897d18842eb33a05e cc66885dc52f440897d18842eb33a05e--9909cdd538a24e11b8e3193747d6c00e
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)
├── X(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]])