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.HaarMeasureFaststate = 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.90828256+0.j 0.38695419+0.j -0.14630225+0.j -0.06232892+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, QuantumCircuitfrom 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)State vector initialization
Section titled “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 = 3batch_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 staterand_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, 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.0824+0.4787j, -0.0539-0.1946j, 0.1156+0.4589j, 0.1344-0.2154j, -0.2126+0.3212j, -0.0655+0.3265j, 0.0400+0.1130j, 0.3646-0.1686j], [-0.0259-0.2140j, -0.3231-0.1988j, 0.0704-0.4587j, 0.4639-0.0569j, -0.2917-0.2823j, 0.1924+0.2168j, -0.0910+0.0082j, 0.3097-0.1508j]])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
Section titled “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
Section titled “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)└── 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
Section titled “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))TrueOr normalize a state:
import torchfrom 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]])