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.90980227+0.41255036j -0.01875312+0.04135647j  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_319e0f06eeae4b52949d37bf659a66da Circuit block cluster_40f83db81ba34683bfa0f28e300d4c10 Prep block 99d8d3752a514b29827916a0bd953426 0 ffd16920480e4f6cbb4fdeafd6b50835 99d8d3752a514b29827916a0bd953426--ffd16920480e4f6cbb4fdeafd6b50835 d4ba2acdff794cb5bf55a1acbf1f3740 1 66f3fc9f9386473088295dbb6bdfc840 RX(theta₀) ffd16920480e4f6cbb4fdeafd6b50835--66f3fc9f9386473088295dbb6bdfc840 1b823b85db794c4cb15a8da4de3ca1a4 RY(theta₄) 66f3fc9f9386473088295dbb6bdfc840--1b823b85db794c4cb15a8da4de3ca1a4 47fd2798d69f491f9d87eb1d3031f7b8 RX(theta₈) 1b823b85db794c4cb15a8da4de3ca1a4--47fd2798d69f491f9d87eb1d3031f7b8 f16ca87506774de2b8ad45123e98f0e9 47fd2798d69f491f9d87eb1d3031f7b8--f16ca87506774de2b8ad45123e98f0e9 b789cd8e7c394e62bd00a78c87e9e3d1 f16ca87506774de2b8ad45123e98f0e9--b789cd8e7c394e62bd00a78c87e9e3d1 bb360e6d577243b6b130995890e3b77a RX(theta₁₂) b789cd8e7c394e62bd00a78c87e9e3d1--bb360e6d577243b6b130995890e3b77a 10a4c62e3d07418392e13de9a8ec3040 RY(theta₁₆) bb360e6d577243b6b130995890e3b77a--10a4c62e3d07418392e13de9a8ec3040 d8165703aef844018220700cde8c4254 RX(theta₂₀) 10a4c62e3d07418392e13de9a8ec3040--d8165703aef844018220700cde8c4254 0022c80b8ad44b5d988c9cb957d007fc d8165703aef844018220700cde8c4254--0022c80b8ad44b5d988c9cb957d007fc cea43c9152d94c9e8d2b32878ec9aed6 0022c80b8ad44b5d988c9cb957d007fc--cea43c9152d94c9e8d2b32878ec9aed6 28242d28cb8443adab5ed23c5badc79f cea43c9152d94c9e8d2b32878ec9aed6--28242d28cb8443adab5ed23c5badc79f b8ab4c116ce54f15a64233f96bf04ad6 44bd313d7dfb4dbf9d0de6ef0fd1eb33 d4ba2acdff794cb5bf55a1acbf1f3740--44bd313d7dfb4dbf9d0de6ef0fd1eb33 1879728bae0f495b8b7007e779685cc5 2 fd4b416daa9947719c16235799625612 RX(theta₁) 44bd313d7dfb4dbf9d0de6ef0fd1eb33--fd4b416daa9947719c16235799625612 1364a52156e749d59e9f8e53d979d657 RY(theta₅) fd4b416daa9947719c16235799625612--1364a52156e749d59e9f8e53d979d657 e5117ca6ff7f4c209db20ef516cdf4f1 RX(theta₉) 1364a52156e749d59e9f8e53d979d657--e5117ca6ff7f4c209db20ef516cdf4f1 709773ef65124ae1b104db36048db709 X e5117ca6ff7f4c209db20ef516cdf4f1--709773ef65124ae1b104db36048db709 709773ef65124ae1b104db36048db709--f16ca87506774de2b8ad45123e98f0e9 93d7aad433cd45a1bf3b4a63bdc6e5af 709773ef65124ae1b104db36048db709--93d7aad433cd45a1bf3b4a63bdc6e5af d5595cae4f06429488b508127a4c2d09 RX(theta₁₃) 93d7aad433cd45a1bf3b4a63bdc6e5af--d5595cae4f06429488b508127a4c2d09 9dbdeb423c444686b51d0e2c08f25ff1 RY(theta₁₇) d5595cae4f06429488b508127a4c2d09--9dbdeb423c444686b51d0e2c08f25ff1 90906089debc42648be33eb41abb5840 RX(theta₂₁) 9dbdeb423c444686b51d0e2c08f25ff1--90906089debc42648be33eb41abb5840 32de24098db74c2b997401c72db5e3ce X 90906089debc42648be33eb41abb5840--32de24098db74c2b997401c72db5e3ce 32de24098db74c2b997401c72db5e3ce--0022c80b8ad44b5d988c9cb957d007fc c11ef100bff5450792799c09c73d9417 32de24098db74c2b997401c72db5e3ce--c11ef100bff5450792799c09c73d9417 c11ef100bff5450792799c09c73d9417--b8ab4c116ce54f15a64233f96bf04ad6 3cea6e794f2a403cbebcaf9b448ec709 b57d6c60c94b4551bd6ffe397252d745 1879728bae0f495b8b7007e779685cc5--b57d6c60c94b4551bd6ffe397252d745 4afb61d4f51b4d56a7796952fc9578a3 3 83d8ae6221db4f528a49e655ee2a0e45 RX(theta₂) b57d6c60c94b4551bd6ffe397252d745--83d8ae6221db4f528a49e655ee2a0e45 76239bd5cc024a2690b4b3a61d34adf2 RY(theta₆) 83d8ae6221db4f528a49e655ee2a0e45--76239bd5cc024a2690b4b3a61d34adf2 d970f4abf9414e58ac84a2303e136be1 RX(theta₁₀) 76239bd5cc024a2690b4b3a61d34adf2--d970f4abf9414e58ac84a2303e136be1 f222df8ce47b48b29a1f1b28f61ed662 d970f4abf9414e58ac84a2303e136be1--f222df8ce47b48b29a1f1b28f61ed662 bdb136e404ea44028160438d35b0e00b X f222df8ce47b48b29a1f1b28f61ed662--bdb136e404ea44028160438d35b0e00b bdb136e404ea44028160438d35b0e00b--93d7aad433cd45a1bf3b4a63bdc6e5af dfcbd11c12b74e68afa217f8f68a734e RX(theta₁₄) bdb136e404ea44028160438d35b0e00b--dfcbd11c12b74e68afa217f8f68a734e 2ca575f45cb24299af5e998ae58ed1a7 RY(theta₁₈) dfcbd11c12b74e68afa217f8f68a734e--2ca575f45cb24299af5e998ae58ed1a7 e5a08dbc0e00429994bcae0e0e45ee79 RX(theta₂₂) 2ca575f45cb24299af5e998ae58ed1a7--e5a08dbc0e00429994bcae0e0e45ee79 cc5a11b084534bc7a086434b22ed424f e5a08dbc0e00429994bcae0e0e45ee79--cc5a11b084534bc7a086434b22ed424f d74032f13e5d4c4c9432ad52a4522563 X cc5a11b084534bc7a086434b22ed424f--d74032f13e5d4c4c9432ad52a4522563 d74032f13e5d4c4c9432ad52a4522563--c11ef100bff5450792799c09c73d9417 d74032f13e5d4c4c9432ad52a4522563--3cea6e794f2a403cbebcaf9b448ec709 3b351ad30929473ba86bc830e9de58fb 29bcedbfb4c845e6b31f26aae4396843 X 4afb61d4f51b4d56a7796952fc9578a3--29bcedbfb4c845e6b31f26aae4396843 dbac4d04e8b94814ac40027ea324b19b RX(theta₃) 29bcedbfb4c845e6b31f26aae4396843--dbac4d04e8b94814ac40027ea324b19b cb3b068cdc0a44b09f3e9c48f1f77b41 RY(theta₇) dbac4d04e8b94814ac40027ea324b19b--cb3b068cdc0a44b09f3e9c48f1f77b41 a040c173de6b4eea9df805ab8d1d55c3 RX(theta₁₁) cb3b068cdc0a44b09f3e9c48f1f77b41--a040c173de6b4eea9df805ab8d1d55c3 ddf3b67564624471bd77c3e7df2905f0 X a040c173de6b4eea9df805ab8d1d55c3--ddf3b67564624471bd77c3e7df2905f0 ddf3b67564624471bd77c3e7df2905f0--f222df8ce47b48b29a1f1b28f61ed662 ecfb1dfe67ac4c18a6f77eafc15ef3a9 ddf3b67564624471bd77c3e7df2905f0--ecfb1dfe67ac4c18a6f77eafc15ef3a9 f35dc61d95244440978542e517632bda RX(theta₁₅) ecfb1dfe67ac4c18a6f77eafc15ef3a9--f35dc61d95244440978542e517632bda df1f7cc6f9b048389d2a8dedffab7469 RY(theta₁₉) f35dc61d95244440978542e517632bda--df1f7cc6f9b048389d2a8dedffab7469 c6b4cd23ddd549398a261061c14e775b RX(theta₂₃) df1f7cc6f9b048389d2a8dedffab7469--c6b4cd23ddd549398a261061c14e775b 2014f80c1b5d41af883f84eba4e731f7 X c6b4cd23ddd549398a261061c14e775b--2014f80c1b5d41af883f84eba4e731f7 2014f80c1b5d41af883f84eba4e731f7--cc5a11b084534bc7a086434b22ed424f 166720af94f04d93b16a77c0d112f65b 2014f80c1b5d41af883f84eba4e731f7--166720af94f04d93b16a77c0d112f65b 166720af94f04d93b16a77c0d112f65b--3b351ad30929473ba86bc830e9de58fb
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, 1.+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]])

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.2361+0.3535j,  0.3359+0.0865j, -0.1484-0.0287j, -0.2160+0.3155j,
          0.1786-0.3860j,  0.2846-0.2259j,  0.2467-0.0175j, -0.2484-0.3068j],
        [-0.1810+0.2598j, -0.3196-0.1320j, -0.1667-0.2685j, -0.0097+0.0318j,
          0.5178+0.0470j,  0.2647-0.2489j, -0.2764+0.3005j, -0.0550-0.3274j]])

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