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.99800534-0.06312957j 0.        +0.j         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_6439ebfaaee547869e3c8fa74081380d Circuit block cluster_d214eab1b2d8495280501bc8b7bd88cc Prep block 116ea58b89044aaa837504f0d539cac7 0 337752d18fd24102a9b112e77137fd45 116ea58b89044aaa837504f0d539cac7--337752d18fd24102a9b112e77137fd45 d31685fb0c094bf28e807a592c8bd2ff 1 6877809f33e94767b3946f2fa3a67596 RX(theta₀) 337752d18fd24102a9b112e77137fd45--6877809f33e94767b3946f2fa3a67596 638f762a01c2449aa011802a7ade3093 RY(theta₄) 6877809f33e94767b3946f2fa3a67596--638f762a01c2449aa011802a7ade3093 f24d2910ff6746e981834c5686dea3cc RX(theta₈) 638f762a01c2449aa011802a7ade3093--f24d2910ff6746e981834c5686dea3cc ecb7df6630ef4800b3868cecac9a34ad f24d2910ff6746e981834c5686dea3cc--ecb7df6630ef4800b3868cecac9a34ad 4a4b5fce589d48a09e1321df65f257c7 ecb7df6630ef4800b3868cecac9a34ad--4a4b5fce589d48a09e1321df65f257c7 4db95bf4de4948928e008a90c1096dc5 RX(theta₁₂) 4a4b5fce589d48a09e1321df65f257c7--4db95bf4de4948928e008a90c1096dc5 71dc472694bb444eb0bfa6d3b7870e7d RY(theta₁₆) 4db95bf4de4948928e008a90c1096dc5--71dc472694bb444eb0bfa6d3b7870e7d bc3d426b365548eeb7cee81d8a05e4cf RX(theta₂₀) 71dc472694bb444eb0bfa6d3b7870e7d--bc3d426b365548eeb7cee81d8a05e4cf 5be4933c610e4310b9d0a76e137427a5 bc3d426b365548eeb7cee81d8a05e4cf--5be4933c610e4310b9d0a76e137427a5 b2569b512d5c491aaa7023d9e771d48a 5be4933c610e4310b9d0a76e137427a5--b2569b512d5c491aaa7023d9e771d48a 80e87a35473e45bdb6b11a98f6584b2d b2569b512d5c491aaa7023d9e771d48a--80e87a35473e45bdb6b11a98f6584b2d 7547a3c2feb04e3ea268e88cb8084f5a 2316561c62c6416ca619724029f660ad d31685fb0c094bf28e807a592c8bd2ff--2316561c62c6416ca619724029f660ad e2cc2c5281d1464c85655c9a12ceb656 2 fa368489e0574c8d8694b4e35892996a RX(theta₁) 2316561c62c6416ca619724029f660ad--fa368489e0574c8d8694b4e35892996a bd177c68d4b14524aaffc6468ebff58b RY(theta₅) fa368489e0574c8d8694b4e35892996a--bd177c68d4b14524aaffc6468ebff58b 08237cec9afe4361a60f125d1aa6bf71 RX(theta₉) bd177c68d4b14524aaffc6468ebff58b--08237cec9afe4361a60f125d1aa6bf71 b99286c67e3e4c5583b137f70d6b8e8e X 08237cec9afe4361a60f125d1aa6bf71--b99286c67e3e4c5583b137f70d6b8e8e b99286c67e3e4c5583b137f70d6b8e8e--ecb7df6630ef4800b3868cecac9a34ad 77d01aeda232485c8be377192aca5f8a b99286c67e3e4c5583b137f70d6b8e8e--77d01aeda232485c8be377192aca5f8a 8591dbca0d74435bb8965527d42899a0 RX(theta₁₃) 77d01aeda232485c8be377192aca5f8a--8591dbca0d74435bb8965527d42899a0 f202464a388c49ccb5c8dff59eda1c48 RY(theta₁₇) 8591dbca0d74435bb8965527d42899a0--f202464a388c49ccb5c8dff59eda1c48 29c96dee34bd4269a0ebc42acb220917 RX(theta₂₁) f202464a388c49ccb5c8dff59eda1c48--29c96dee34bd4269a0ebc42acb220917 6be09711cfad44bab25fde69f884b944 X 29c96dee34bd4269a0ebc42acb220917--6be09711cfad44bab25fde69f884b944 6be09711cfad44bab25fde69f884b944--5be4933c610e4310b9d0a76e137427a5 98242f450f7a40ebbf9ac95bdc205c89 6be09711cfad44bab25fde69f884b944--98242f450f7a40ebbf9ac95bdc205c89 98242f450f7a40ebbf9ac95bdc205c89--7547a3c2feb04e3ea268e88cb8084f5a 70f303f351ec47129ba46ac68eded7cc f9783f7e3f084b91b67717c9387049a0 e2cc2c5281d1464c85655c9a12ceb656--f9783f7e3f084b91b67717c9387049a0 be5fb09c7ce74c53bc4b06ea71d0611b 3 03493f3a1d3849d7bf8d06f409a1e79f RX(theta₂) f9783f7e3f084b91b67717c9387049a0--03493f3a1d3849d7bf8d06f409a1e79f c99da835fb34430698c7f8facafe2ffb RY(theta₆) 03493f3a1d3849d7bf8d06f409a1e79f--c99da835fb34430698c7f8facafe2ffb 5820f6f71858462f82173c64099430d3 RX(theta₁₀) c99da835fb34430698c7f8facafe2ffb--5820f6f71858462f82173c64099430d3 a8d13243cda4407b9b549e89e6cc1775 5820f6f71858462f82173c64099430d3--a8d13243cda4407b9b549e89e6cc1775 235e7f4ef8e0495ebaa1b83ef601f67f X a8d13243cda4407b9b549e89e6cc1775--235e7f4ef8e0495ebaa1b83ef601f67f 235e7f4ef8e0495ebaa1b83ef601f67f--77d01aeda232485c8be377192aca5f8a 27daf186c40e4fb1ba40852da56a9de5 RX(theta₁₄) 235e7f4ef8e0495ebaa1b83ef601f67f--27daf186c40e4fb1ba40852da56a9de5 d82d692492f64690ac6cfd17e2289a72 RY(theta₁₈) 27daf186c40e4fb1ba40852da56a9de5--d82d692492f64690ac6cfd17e2289a72 1ed91d19e1c343cea3637c34c0cf36dd RX(theta₂₂) d82d692492f64690ac6cfd17e2289a72--1ed91d19e1c343cea3637c34c0cf36dd 4094b634ca784212b0b516524a0cb354 1ed91d19e1c343cea3637c34c0cf36dd--4094b634ca784212b0b516524a0cb354 ec7630b73fc745679eeed29d066a7eee X 4094b634ca784212b0b516524a0cb354--ec7630b73fc745679eeed29d066a7eee ec7630b73fc745679eeed29d066a7eee--98242f450f7a40ebbf9ac95bdc205c89 ec7630b73fc745679eeed29d066a7eee--70f303f351ec47129ba46ac68eded7cc 56f560d0fe534e1fb3e21cd097758677 cc450f2849bd4a2286a58e2177d4f3de X be5fb09c7ce74c53bc4b06ea71d0611b--cc450f2849bd4a2286a58e2177d4f3de c0f622c8bcf9459fafd51c628394d238 RX(theta₃) cc450f2849bd4a2286a58e2177d4f3de--c0f622c8bcf9459fafd51c628394d238 a37f358c994146ac9b4cfc059c8c65ef RY(theta₇) c0f622c8bcf9459fafd51c628394d238--a37f358c994146ac9b4cfc059c8c65ef 934a50000dcf4426b90d610fb78ad7e2 RX(theta₁₁) a37f358c994146ac9b4cfc059c8c65ef--934a50000dcf4426b90d610fb78ad7e2 fbb776ff15174223a28a12bc7d9e1431 X 934a50000dcf4426b90d610fb78ad7e2--fbb776ff15174223a28a12bc7d9e1431 fbb776ff15174223a28a12bc7d9e1431--a8d13243cda4407b9b549e89e6cc1775 e2fda4757e7c4675b2f7eec45b1f8f61 fbb776ff15174223a28a12bc7d9e1431--e2fda4757e7c4675b2f7eec45b1f8f61 49fe0ee201984a9bb3291dc61767927f RX(theta₁₅) e2fda4757e7c4675b2f7eec45b1f8f61--49fe0ee201984a9bb3291dc61767927f 79040d6a0c6b44259be4594cca8f483f RY(theta₁₉) 49fe0ee201984a9bb3291dc61767927f--79040d6a0c6b44259be4594cca8f483f 820e622199674e96811e184f741c8544 RX(theta₂₃) 79040d6a0c6b44259be4594cca8f483f--820e622199674e96811e184f741c8544 4e75eec39b554eb885237683704218b8 X 820e622199674e96811e184f741c8544--4e75eec39b554eb885237683704218b8 4e75eec39b554eb885237683704218b8--4094b634ca784212b0b516524a0cb354 196bc80424a04cc89c88dab4973d22eb 4e75eec39b554eb885237683704218b8--196bc80424a04cc89c88dab4973d22eb 196bc80424a04cc89c88dab4973d22eb--56f560d0fe534e1fb3e21cd097758677
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, 1.+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]])

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.0327-0.1023j, -0.2824+0.1998j, -0.1383+0.4078j,  0.2924+0.2990j,
         -0.0830+0.2405j,  0.2116+0.4120j,  0.3971+0.1071j,  0.0651-0.2363j],
        [ 0.0665-0.0104j,  0.1020+0.0727j, -0.0617-0.5360j,  0.3180+0.3258j,
          0.1026+0.2581j,  0.2748+0.1173j, -0.5009-0.1343j, -0.1208-0.1774j]])

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