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.93701774+0.j          0.        +0.05709562j  0.        +0.34394566j
 -0.02095776+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_692e3a20f9a746739bed164e21b4b066 Circuit block cluster_d17f02db10ed46ffb06c97d3ee2283a7 Prep block 71fce36cd79f4715a3b74b50647be0e7 0 9325d50ea8994f618998fe4be575981a 71fce36cd79f4715a3b74b50647be0e7--9325d50ea8994f618998fe4be575981a a8e1dd121aeb445d829f37cb74d2adaa 1 8a9704b1c5834765b79ae570479d36f6 RX(theta₀) 9325d50ea8994f618998fe4be575981a--8a9704b1c5834765b79ae570479d36f6 8d0f2a4e8fca4238957a10069dbbd7e6 RY(theta₄) 8a9704b1c5834765b79ae570479d36f6--8d0f2a4e8fca4238957a10069dbbd7e6 6b389b2cd28d41adb3bd971c616d285b RX(theta₈) 8d0f2a4e8fca4238957a10069dbbd7e6--6b389b2cd28d41adb3bd971c616d285b 6f1acf190f324e0aaca5c7ca4685528b 6b389b2cd28d41adb3bd971c616d285b--6f1acf190f324e0aaca5c7ca4685528b 9279766e892148398d52d3d176526d06 6f1acf190f324e0aaca5c7ca4685528b--9279766e892148398d52d3d176526d06 1f9e03a47f1f41bcb2423721d9cb93db RX(theta₁₂) 9279766e892148398d52d3d176526d06--1f9e03a47f1f41bcb2423721d9cb93db 05691c8cca454bc583a0f2ea6403a8d1 RY(theta₁₆) 1f9e03a47f1f41bcb2423721d9cb93db--05691c8cca454bc583a0f2ea6403a8d1 ec42476ce1d1436895de56bdbe8df581 RX(theta₂₀) 05691c8cca454bc583a0f2ea6403a8d1--ec42476ce1d1436895de56bdbe8df581 2bf9fd97edbb4e5dac5fb43d37f09d10 ec42476ce1d1436895de56bdbe8df581--2bf9fd97edbb4e5dac5fb43d37f09d10 179c1004f092431f8ab33affa9bfdaa6 2bf9fd97edbb4e5dac5fb43d37f09d10--179c1004f092431f8ab33affa9bfdaa6 abd1dd3564b54bec8538f52f602c7a7e 179c1004f092431f8ab33affa9bfdaa6--abd1dd3564b54bec8538f52f602c7a7e 884df4d2ee6f4d8abd14b3bd467da353 b7d980e672ed4a35bbd3b4fa06b10540 a8e1dd121aeb445d829f37cb74d2adaa--b7d980e672ed4a35bbd3b4fa06b10540 f9eb2bce9f724f75be7a982118a1c2fc 2 1264f4f5174443488cf1d8e57cd7a942 RX(theta₁) b7d980e672ed4a35bbd3b4fa06b10540--1264f4f5174443488cf1d8e57cd7a942 5f194d9676484e04a62402f23a17c4d4 RY(theta₅) 1264f4f5174443488cf1d8e57cd7a942--5f194d9676484e04a62402f23a17c4d4 f367402551dd4d278c7b2ae698eb052f RX(theta₉) 5f194d9676484e04a62402f23a17c4d4--f367402551dd4d278c7b2ae698eb052f 96ebdaf6d5964c2f838a2e63630c855a X f367402551dd4d278c7b2ae698eb052f--96ebdaf6d5964c2f838a2e63630c855a 96ebdaf6d5964c2f838a2e63630c855a--6f1acf190f324e0aaca5c7ca4685528b a2b886e8822c4c6d955d3c7e34f792e4 96ebdaf6d5964c2f838a2e63630c855a--a2b886e8822c4c6d955d3c7e34f792e4 736d0fde014c42f399684b6bc7767262 RX(theta₁₃) a2b886e8822c4c6d955d3c7e34f792e4--736d0fde014c42f399684b6bc7767262 4ba3e9646223431fa1286b427af73976 RY(theta₁₇) 736d0fde014c42f399684b6bc7767262--4ba3e9646223431fa1286b427af73976 5d5bb260b7424543b211bae3a3370944 RX(theta₂₁) 4ba3e9646223431fa1286b427af73976--5d5bb260b7424543b211bae3a3370944 14f841117584454bb125207ef40def22 X 5d5bb260b7424543b211bae3a3370944--14f841117584454bb125207ef40def22 14f841117584454bb125207ef40def22--2bf9fd97edbb4e5dac5fb43d37f09d10 ad12b4a1e614426d933cf4700bb1e0bb 14f841117584454bb125207ef40def22--ad12b4a1e614426d933cf4700bb1e0bb ad12b4a1e614426d933cf4700bb1e0bb--884df4d2ee6f4d8abd14b3bd467da353 9da1560cb28a4963b03287243b414fe3 99dafceb7efe40a696172c0734b25fe1 f9eb2bce9f724f75be7a982118a1c2fc--99dafceb7efe40a696172c0734b25fe1 89312e3058b54863b211fa5adfa9668b 3 1b478fe902c148d2be2c47fe1d7617bb RX(theta₂) 99dafceb7efe40a696172c0734b25fe1--1b478fe902c148d2be2c47fe1d7617bb 3f7e5337d77a468dab3af76badf2100f RY(theta₆) 1b478fe902c148d2be2c47fe1d7617bb--3f7e5337d77a468dab3af76badf2100f 7f9b5da1032f4330b4893c5608895796 RX(theta₁₀) 3f7e5337d77a468dab3af76badf2100f--7f9b5da1032f4330b4893c5608895796 50a0d747e682413ab37185908a573d97 7f9b5da1032f4330b4893c5608895796--50a0d747e682413ab37185908a573d97 8fffcaf3ae364400a9e3e581e4875ff1 X 50a0d747e682413ab37185908a573d97--8fffcaf3ae364400a9e3e581e4875ff1 8fffcaf3ae364400a9e3e581e4875ff1--a2b886e8822c4c6d955d3c7e34f792e4 521a7166b8bc4e808e3fa06b5ae77e1c RX(theta₁₄) 8fffcaf3ae364400a9e3e581e4875ff1--521a7166b8bc4e808e3fa06b5ae77e1c 8037c83e579549f1a230e0d5f1e005fc RY(theta₁₈) 521a7166b8bc4e808e3fa06b5ae77e1c--8037c83e579549f1a230e0d5f1e005fc b3b45eb57c094318b65ca8f825606290 RX(theta₂₂) 8037c83e579549f1a230e0d5f1e005fc--b3b45eb57c094318b65ca8f825606290 667d6356c0d244e4af1dd80d58ede61d b3b45eb57c094318b65ca8f825606290--667d6356c0d244e4af1dd80d58ede61d 9220e66889184bf59696811ce62c1609 X 667d6356c0d244e4af1dd80d58ede61d--9220e66889184bf59696811ce62c1609 9220e66889184bf59696811ce62c1609--ad12b4a1e614426d933cf4700bb1e0bb 9220e66889184bf59696811ce62c1609--9da1560cb28a4963b03287243b414fe3 dec3813a5bfe439f9a41e6792d1d6f90 0319b50a9f254e73ba4d1af8a970382d X 89312e3058b54863b211fa5adfa9668b--0319b50a9f254e73ba4d1af8a970382d 850862a9ef15475e8aa77c7d8306d215 RX(theta₃) 0319b50a9f254e73ba4d1af8a970382d--850862a9ef15475e8aa77c7d8306d215 6e10c055675a460599356814187b9b91 RY(theta₇) 850862a9ef15475e8aa77c7d8306d215--6e10c055675a460599356814187b9b91 54358b6098ac40b684db89493131147f RX(theta₁₁) 6e10c055675a460599356814187b9b91--54358b6098ac40b684db89493131147f fa04187c977f4baaadb901c27e3c362a X 54358b6098ac40b684db89493131147f--fa04187c977f4baaadb901c27e3c362a fa04187c977f4baaadb901c27e3c362a--50a0d747e682413ab37185908a573d97 730f8efeb2ba497dbb6fbf9492209773 fa04187c977f4baaadb901c27e3c362a--730f8efeb2ba497dbb6fbf9492209773 8ab8e10e179449629028505f1dc92ad4 RX(theta₁₅) 730f8efeb2ba497dbb6fbf9492209773--8ab8e10e179449629028505f1dc92ad4 4bb3f2b97fa84146a21c7b0ba2882ddd RY(theta₁₉) 8ab8e10e179449629028505f1dc92ad4--4bb3f2b97fa84146a21c7b0ba2882ddd 7d90e3797f3d42ea8e9e2b4fbed30613 RX(theta₂₃) 4bb3f2b97fa84146a21c7b0ba2882ddd--7d90e3797f3d42ea8e9e2b4fbed30613 ef17da18cd684088b51d04bb31cdf826 X 7d90e3797f3d42ea8e9e2b4fbed30613--ef17da18cd684088b51d04bb31cdf826 ef17da18cd684088b51d04bb31cdf826--667d6356c0d244e4af1dd80d58ede61d 1cf5b1d065ad4fef9de90bef62683bb6 ef17da18cd684088b51d04bb31cdf826--1cf5b1d065ad4fef9de90bef62683bb6 1cf5b1d065ad4fef9de90bef62683bb6--dec3813a5bfe439f9a41e6792d1d6f90
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, 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]])

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.2714+0.0785j, -0.1087+0.2429j, -0.4963+0.3840j,  0.1460+0.4261j,
         -0.0952-0.0776j,  0.1443+0.4171j,  0.0775-0.0652j, -0.1783-0.0284j],
        [ 0.0303+0.0078j, -0.1696-0.1015j,  0.6217-0.2615j,  0.1677-0.1973j,
          0.2207-0.1543j,  0.4491+0.2522j, -0.1606+0.1907j, -0.0576+0.1862j]])

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