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.58439368+0.j         0.7511167 +0.j         0.        -0.18857704j
 0.        -0.24237662j]

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_42b987aee0e145599f0505e67416d347 Circuit block cluster_12246ae069bd43fbae47157c07d5c92f Prep block 6e8dd027077d4ec0b48b5111e624cb02 0 69f9a648d295487fb92b8b2fe19eea3f 6e8dd027077d4ec0b48b5111e624cb02--69f9a648d295487fb92b8b2fe19eea3f dda3a8843d644cb8945d8e9b91760247 1 7b6f4cd13b54477b911ea6b76d1b6157 RX(theta₀) 69f9a648d295487fb92b8b2fe19eea3f--7b6f4cd13b54477b911ea6b76d1b6157 ed4bf0305ee6497181b6b61288d74391 RY(theta₄) 7b6f4cd13b54477b911ea6b76d1b6157--ed4bf0305ee6497181b6b61288d74391 44438ce787ef4f9d8e9ef6968f50c13a RX(theta₈) ed4bf0305ee6497181b6b61288d74391--44438ce787ef4f9d8e9ef6968f50c13a 81f6dc1b719e460f9c4ddb6c95dd086b 44438ce787ef4f9d8e9ef6968f50c13a--81f6dc1b719e460f9c4ddb6c95dd086b 417e3d6d26884466bf34068c644e0c48 81f6dc1b719e460f9c4ddb6c95dd086b--417e3d6d26884466bf34068c644e0c48 2348f7d5ac204ed8a422d3283f3a6312 RX(theta₁₂) 417e3d6d26884466bf34068c644e0c48--2348f7d5ac204ed8a422d3283f3a6312 f44b537d748b4294b4337e03f5cc0bb0 RY(theta₁₆) 2348f7d5ac204ed8a422d3283f3a6312--f44b537d748b4294b4337e03f5cc0bb0 2f8162d263d14222b8f355bc6e2c5831 RX(theta₂₀) f44b537d748b4294b4337e03f5cc0bb0--2f8162d263d14222b8f355bc6e2c5831 75aaa75fd28a470c94c087a06c0ee32e 2f8162d263d14222b8f355bc6e2c5831--75aaa75fd28a470c94c087a06c0ee32e 8cb25009e487402ba10b924f1de7ca85 75aaa75fd28a470c94c087a06c0ee32e--8cb25009e487402ba10b924f1de7ca85 1c557cba5a17424ca4d21b2e308b139e 8cb25009e487402ba10b924f1de7ca85--1c557cba5a17424ca4d21b2e308b139e a34f9b9730694ebf87d3725220a407ed 552554a8353b4234b09e11ee2cc7e46b dda3a8843d644cb8945d8e9b91760247--552554a8353b4234b09e11ee2cc7e46b ed2b40f4f87f4dfe9a7b1c2aa4b0cedb 2 1054d4ed07d94c9ba80e206272877043 RX(theta₁) 552554a8353b4234b09e11ee2cc7e46b--1054d4ed07d94c9ba80e206272877043 72b46bb0e7834a3a949df860ce7246c0 RY(theta₅) 1054d4ed07d94c9ba80e206272877043--72b46bb0e7834a3a949df860ce7246c0 06db1a35d7774d8b9f07bfc1abb970b4 RX(theta₉) 72b46bb0e7834a3a949df860ce7246c0--06db1a35d7774d8b9f07bfc1abb970b4 65bbe9a5b3074f0ea1e5fbd2b4b12598 X 06db1a35d7774d8b9f07bfc1abb970b4--65bbe9a5b3074f0ea1e5fbd2b4b12598 65bbe9a5b3074f0ea1e5fbd2b4b12598--81f6dc1b719e460f9c4ddb6c95dd086b 2de8db6ffdc74e078a60c4a7274644c6 65bbe9a5b3074f0ea1e5fbd2b4b12598--2de8db6ffdc74e078a60c4a7274644c6 fae4e93803ab47efb9a77a532d047bc0 RX(theta₁₃) 2de8db6ffdc74e078a60c4a7274644c6--fae4e93803ab47efb9a77a532d047bc0 df6fd412f1b7476994da5599e67d8106 RY(theta₁₇) fae4e93803ab47efb9a77a532d047bc0--df6fd412f1b7476994da5599e67d8106 e493e99481c94c488478c1f574b71402 RX(theta₂₁) df6fd412f1b7476994da5599e67d8106--e493e99481c94c488478c1f574b71402 aba98da96b7442c888f549f0e3dd30d9 X e493e99481c94c488478c1f574b71402--aba98da96b7442c888f549f0e3dd30d9 aba98da96b7442c888f549f0e3dd30d9--75aaa75fd28a470c94c087a06c0ee32e cc656ac9c80d4a4e8623e356cab17c47 aba98da96b7442c888f549f0e3dd30d9--cc656ac9c80d4a4e8623e356cab17c47 cc656ac9c80d4a4e8623e356cab17c47--a34f9b9730694ebf87d3725220a407ed 17eb7d78ca5641e29bc08ceb7bbe6ef1 68f7bea5da0c488380eea4151d6937e5 ed2b40f4f87f4dfe9a7b1c2aa4b0cedb--68f7bea5da0c488380eea4151d6937e5 83de104eadf74ff3a739a80252743a4e 3 313f6b4056fa4a9b870a29f28dca2b2e RX(theta₂) 68f7bea5da0c488380eea4151d6937e5--313f6b4056fa4a9b870a29f28dca2b2e c1e853bd71db4f50aa7bb338d68dd854 RY(theta₆) 313f6b4056fa4a9b870a29f28dca2b2e--c1e853bd71db4f50aa7bb338d68dd854 9260f092030641f0a6ed0e76642ef3c7 RX(theta₁₀) c1e853bd71db4f50aa7bb338d68dd854--9260f092030641f0a6ed0e76642ef3c7 66a452a7865b4f4887d5af6e37f70b0c 9260f092030641f0a6ed0e76642ef3c7--66a452a7865b4f4887d5af6e37f70b0c bca4cf0cbc0a4444ac440f7137d5bc00 X 66a452a7865b4f4887d5af6e37f70b0c--bca4cf0cbc0a4444ac440f7137d5bc00 bca4cf0cbc0a4444ac440f7137d5bc00--2de8db6ffdc74e078a60c4a7274644c6 f7392292edf64e19be23c004927c1687 RX(theta₁₄) bca4cf0cbc0a4444ac440f7137d5bc00--f7392292edf64e19be23c004927c1687 8b8b786b44eb4cee9254d7e5c065a12a RY(theta₁₈) f7392292edf64e19be23c004927c1687--8b8b786b44eb4cee9254d7e5c065a12a c5d0c7fb009c476d80f0db7945cfdbc7 RX(theta₂₂) 8b8b786b44eb4cee9254d7e5c065a12a--c5d0c7fb009c476d80f0db7945cfdbc7 36c9d1372a6e4a358299410d13900e2d c5d0c7fb009c476d80f0db7945cfdbc7--36c9d1372a6e4a358299410d13900e2d df1923a61b8d424fa819b81e4889c577 X 36c9d1372a6e4a358299410d13900e2d--df1923a61b8d424fa819b81e4889c577 df1923a61b8d424fa819b81e4889c577--cc656ac9c80d4a4e8623e356cab17c47 df1923a61b8d424fa819b81e4889c577--17eb7d78ca5641e29bc08ceb7bbe6ef1 d2d641502445424bb078ec310ec0b150 4ea75fa3fb0c4185b759e0465ef49d9e X 83de104eadf74ff3a739a80252743a4e--4ea75fa3fb0c4185b759e0465ef49d9e dd436ce3db97474ea96362920513b52f RX(theta₃) 4ea75fa3fb0c4185b759e0465ef49d9e--dd436ce3db97474ea96362920513b52f e7bb4f4e947b4753a4e11f0fc35952b7 RY(theta₇) dd436ce3db97474ea96362920513b52f--e7bb4f4e947b4753a4e11f0fc35952b7 53e7e930ca8749a893b0d80e64382af0 RX(theta₁₁) e7bb4f4e947b4753a4e11f0fc35952b7--53e7e930ca8749a893b0d80e64382af0 797f95266fda4a8baaa6786b6437bbd0 X 53e7e930ca8749a893b0d80e64382af0--797f95266fda4a8baaa6786b6437bbd0 797f95266fda4a8baaa6786b6437bbd0--66a452a7865b4f4887d5af6e37f70b0c a275d7b5f8794ebd80a22960aad88bc6 797f95266fda4a8baaa6786b6437bbd0--a275d7b5f8794ebd80a22960aad88bc6 7cc158564d2e4441844673fc96d4f75c RX(theta₁₅) a275d7b5f8794ebd80a22960aad88bc6--7cc158564d2e4441844673fc96d4f75c 5d9d42cc0d1c49b8ab381e0ddbb9cae0 RY(theta₁₉) 7cc158564d2e4441844673fc96d4f75c--5d9d42cc0d1c49b8ab381e0ddbb9cae0 8ca63d931bf644bdbe78215bb9d6ccff RX(theta₂₃) 5d9d42cc0d1c49b8ab381e0ddbb9cae0--8ca63d931bf644bdbe78215bb9d6ccff 7ca44b1fa8a94c099ba13401bcae5b54 X 8ca63d931bf644bdbe78215bb9d6ccff--7ca44b1fa8a94c099ba13401bcae5b54 7ca44b1fa8a94c099ba13401bcae5b54--36c9d1372a6e4a358299410d13900e2d 2fae2f9b2b8a4aa1b3b5b52ddefeffdf 7ca44b1fa8a94c099ba13401bcae5b54--2fae2f9b2b8a4aa1b3b5b52ddefeffdf 2fae2f9b2b8a4aa1b3b5b52ddefeffdf--d2d641502445424bb078ec310ec0b150
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, 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.0210+1.3910e-01j,  0.5115-1.8703e-01j, -0.2232-1.9475e-01j,
          0.0546-1.0278e-01j, -0.4123+3.4368e-01j,  0.0442-2.6168e-01j,
         -0.1116+4.3343e-01j,  0.1530-2.6275e-03j],
        [-0.2636+3.1462e-01j,  0.2914-3.6732e-01j, -0.1191-2.6621e-01j,
          0.1736-8.2467e-02j, -0.1679+3.3491e-01j,  0.2364+3.1215e-04j,
          0.0961+3.3518e-01j, -0.3834+1.5768e-01j]])

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