# State Preparation¶

Most quantum applications start with preparing a state in a quantum register. For example, in finance the state may represent price distribution of some assets. In chemistry, it may be an initial guess for the ground state of a molecule and in a quantum machine learning, feature vector to be analyzed.

The state preparation function creates a quantum circuit which outputs the desired probability distribution $$p_{i}$$ in the computational basis, with $$i$$ denoting the corresponding basis state. The distribution can be in the form of a list or a list of pairs of numbers for the first and second moments of a gaussian mixture. The resulting wave function is given by

$\left|\psi\right\rangle = \sum_{i}\sqrt{p_{i}} \left|i\right\rangle,$

In general, state preparation is hard. Only a very small portion of the Hilbert space can be prepared efficiently (in $$O(poly(n))$$ steps) on a quantum circuit. Therefore, in practice, an approximation is often used to lower the complexity. The approximation is specified by an error metric and error range.

For a circuit consisting of error-less gates, there are five different options for the error metric:

• Kullback–Leibler divergence (KL)
• $$L_p$$ norms: $$L_1$$, $$L_2$$, and $$L_\infty$$(max probability)
• Loss of fidelity (FL), defined by $$\rm{FL} = 1 - \left|\left\langle \tilde{\psi} \right|\left.\vphantom {\tilde{\psi}}\psi\right\rangle \right|^{2}$$, where $${\psi}$$, $$\tilde {\psi}$$ is the exact, approximated state, respectively. Note: for hardware-aware synthesis (described below), only the loss of fidelity error metric is allowed.

The higher the specified error tolerance, the smaller the output circuit will be. Not specifying an error will result in an attempt to build the exact circuit.

The function also supports hardware-aware circuit synthesis. Imperfections in the two-qubit gates, such as CX or CZ, can be taken into account by specifying the gate error - the average loss of fidelity, defined below.

$\rm{FL}\left(\mathcal{E},U\right) = 1 - \int d\psi\bra{\psi} U^{\dagger}\mathcal{E}\left(\ket{\psi} \bra{\psi}\right)U\ket{\psi}$

With $$U$$ referring to an ideal gate and $$\mathcal{E}$$ the quantum operation corresponding to the noisy gate.

The hardware error and the approximation error are combined to give the upper bound for an overall circuit error, and the synthesis process will attempt to generate a circuit within the user-specified error range. The synthesis operates under the assumption that the error probabilities for each gate pair are independent. Then, the overall error upper bound is calculated by adding the Bures angles (arccos of the square root fidelity) corresponding to the hardware and approximation errors. Since the Bures angle is a distance metric, it obeys the triangle inequality and the upper bound is strict [1].

## Syntax¶

Function: StatePreparation

Parameters:

• probabilities: [pmf, gaussian_moment_list]
• depth_range: Optional[NonNegativeFloatRange]
• cnot_count_range: Optional[NonNegativeFloatRange]
• error_metric: Optional[[KL, L1, L2, MAX_PROBABILITY, LOSS_OF_FIDELITY], NonNegativeFloatRange]]
• hardware_constraints: Optional[HardwareConstraints]
• two_qubit_gate_error: Optional[float]
• num_qubits: Optional[int]
• is_uniform_start: Optional[bool] = true
{
"function": "StatePreparation",
"function_params": {
"probabilities": [0.05, 0.11, 0.13, 0.23, 0.27, 0.12, 0.03, 0.06],
"depth_range": 87,
"error_metric": { "KL": { "upper_bound": 0.01 } }
}
}


{
"constraints": {
"max_width": 4,
"max_depth": 91
},
"logic_flow": [{
"function": "StatePreparation",
"function_params": {
"probabilities": [0.05, 0.11, 0.13, 0.23, 0.27, 0.12, 0.03, 0.06],
"depth_range": 91,
"error_metric": {"KL": {"upper_bound": 0.01}}
}
}]
}

from classiq import ModelDesigner
from classiq.builtin_functions import StatePreparation

probabilities = (0.05, 0.11, 0.13, 0.23, 0.27, 0.12, 0.03, 0.06)
params = StatePreparation(
probabilities=probabilities,
depth_range=91,
error_metric={"KL": {"upper_bound": 0.01}},
)

model_designer = ModelDesigner()
model_designer.StatePreparation(params)
circuit = model_designer.synthesize()


This example generates a circuit whose output state probabilities are an approximation to the pmf given. That is, the probability to measure the state $$\ket{000}$$ is 0.05, $$\ket{001}$$ is 0.11,... ,and the probability to measure $$\ket{111}$$ is 0.06. The error metric used is Kullback–Leibler.

To execute the circuit, you may run the following code:

Create the following file: execution_preferences.exct

{
"preferences": {
"num_shots": 4000,
"backend_preferences": {
"backend_service_provider": "IBMQ",
"backend_name": "aer_simulator"
}
}
}

Run Classiq execute command (Classiq: Execute Quantum Program), choose a file containing the program and pick it's instruction set (e.g. QASM or ionq) and the outptut path.

from classiq import Executor
from classiq.interface.backend import backend_preferences
from classiq.interface.executor import execution_preferences

res = Executor(
num_shots=4000,
backend_preferences=backend_preferences.IBMBackendPreferences(
backend_name="aer_simulator"
),
).execute(circuit)


To print the result on the SDK, use the code below:

counts = sorted(res.counts.items())
print(
f"probabilities are:\n{dict([(bit_string, count/NUM_SHOT) for bit_string, count in counts])}"
)


which results in the following values:

probabilities are:
{'0000': 0.048, '0001': 0.133, '0010': 0.129, '0011': 0.228, '0100': 0.255, '0101': 0.114, '0110': 0.047, '0111': 0.046}


{
"constraints": {
"max_width": 8,
"max_depth": 100
},
"logic_flow": [
{
"function": "StatePreparation",
"function_params": {
"probabilities": {
"gaussian_moment_list": [
{
"mu": 1,
"sigma": 1
},
{
"mu": 3,
"sigma": 1
},
{
"mu": -3,
"sigma": 1
}
]
},
"error_metric": {
"L2": {
"upper_bound": 0.023
}
},
"num_qubits": 8
}
}
]
}

from classiq import ModelDesigner
from classiq.builtin_functions import StatePreparation
from classiq.interface.generator.state_preparation import (
GaussianMixture,
GaussianMoments,
)

params = StatePreparation(
probabilities=GaussianMixture(
gaussian_moment_list=(
GaussianMoments(mu=1, sigma=1),
GaussianMoments(mu=3, sigma=1),
GaussianMoments(mu=-3, sigma=1),
)
),
depth_range=91,
error_metric={"L2": {"upper_bound": 0.023}},
num_qubits=8,
)

model_designer = ModelDesigner()
model_designer.StatePreparation(params)
circuit = model_designer.synthesize()


This example generates a circuit whose output state probabilities correspond to a gaussian mixture. The underlying pmf is calculated in following way: first the support of the gaussian mixture cdf is truncated at 5 sigma from the gaussian at each edge, second, the support is divided into an equal size grid containing $$2^8+ 1$$ points and lastly the pmf is calculated by taking the difference between the cdf values of consecutive grid points. The error metric used is L2. Note that 4 qubits do not undergo any operation. This is due to the selected error bound (these qubits correspond to the least significant bits). A tighter error bound would result in a circuit operating on more qubits.

To execute the circuit, you may run the following code:

Create the following file: execution_preferences.exct

{
"preferences": {
"num_shots": 4000,
"backend_preferences": {
"backend_service_provider": "IBMQ",
"backend_name": "aer_simulator"
}
}
}

Run Classiq execute command (Classiq: Execute Quantum Program), choose a file containing the program and pick it's instruction set (e.g. QASM or ionq) and the outptut path.

from classiq import Executor
from classiq.interface.backend import backend_preferences
from classiq.interface.executor import execution_preferences

res = Executor(
num_shots=4000,
backend_preferences=backend_preferences.IBMBackendPreferences(
backend_name="aer_simulator"
),
).execute(circuit)


The resulting plot and the code to generate it using the SDK are shown below.

from matplotlib import pyplot as plt

sorted_counts = dict(sorted(res.counts.items()))
bit_strings, counts = sorted_counts.keys(), sorted_counts.values()
plt.title("Gaussian Mixtures graph")
plt.xlabel("State")
plt.ylabel("Measurement Probability [%]")
plt.plot(
[int(bit_str, 2) for bit_str in bit_strings], [count / NUM_SHOT for count in counts]
)
plt.show()


### Example 3 - Hardware-aware Synthesis¶

{
"constraints": {
"max_width":4
},
"preferences": {
"custom_hardware_settings": {
"basis_gates":["cx", "sx", "rz", "x"]
}
},
"logic_flow":[
{
"function":"StatePreparation",
"function_params":{
"probabilities":[0.05, 0.11, 0.13, 0.23, 0.27, 0.12, 0.03, 0.06],
"error_metric":{
"LOSS_OF_FIDELITY":{
"upper_bound":0.01
}
},
"hardware_constraints":{
"two_qubit_gate_error":0.00015
}
}
}
]
}

from classiq.interface.generator.model import Preferences
from classiq.interface.generator.model.preferences.preferences import (
CustomHardwareSettings,
)

from classiq import ModelDesigner
from classiq.builtin_functions import StatePreparation

params = StatePreparation(
probabilities=(0.05, 0.11, 0.13, 0.23, 0.27, 0.12, 0.03, 0.06),
error_metric={"LOSS_OF_FIDELITY": {"upper_bound": 0.01}},
hardware_constraints={"two_qubit_gate_error": 0.00015},
)

custom_hardware_settings = CustomHardwareSettings(basis_gates=["cx", "sx", "rz", "x"])
preferences = Preferences(custom_hardware_settings=custom_hardware_settings)

model_designer = ModelDesigner(preferences=preferences)
model_designer.StatePreparation(params)

circuit = model_designer.synthesize()


This example generates the same distribution as in Example 1, now with synthesis constraints due to hardware. As expected, for fidelity of 99%, the required two-qubit gate error is about two orders of magnitude below values achievable by current state-of-the-art architectures.

[1] Nielsen, Michael A., and Isaac L. Chuang. Quantum Computation and Quantum Information. 10th anniversary ed. Cambridge; New York: Cambridge University Press, 2010. .