Blog
28
July
,
2022

Thinking Outside the Box on State Preparations - Using Classiq to Generate Log-Normal Distribution Circuits of Depth 1 

Share the article
Our library

This note illustrates how to utilize the Classiq platform to solve the Log-Normal State preparation problem from our recent coding competition. We examine two methods for generating this state preparation as well as the trick necessary for generating a state preparation with a circuit depth of 1. 

Step-1 for All Quantum Algorithms

State preparations are an integral part to quantum computation, with almost all quantum applications beginning with the preparation of a state in a quantum register. In finance, for example, the state may represent the price distribution of certain assets as part of a portfolio optimization algorithm. In chemistry, it may be an initial estimate for the ground state of a molecule, and in quantum machine learning, it is the feature vector to be analyzed.

State preparation is the initialization of qubits with quantum gates so that their measured output corresponds to a desired probability distribution. This initialized state can then be utilized by subsequent quantum circuits to perform calculations. The Log-Normal State preparation we'll discuss is a commonly used initialized state in many finance calculations. 

The Log-Normal State Preparation Problem

In our recent coding competition, competitors were asked to design a circuit that generates a log-normal distribution with a mean (µ) of 0 and standard deviation (σ) of 0.1 using no more than 10 qubits. The circuit must approximate a log-normal distribution with an error of no more than 0.01 and can only contain 'u' and 'CX' gates. The winning entry would be the circuit with the shallowest depth while meeting all of the aforementioned criteria.

This problem was both the most difficult to solve and the simplest quantum circuit to compute of the four competition problems we designed. To provide our competitors with a hint on how to approach this problem, we required them to submit both the quantum circuit and the discretization method they employed.

Solving State Preparations with Classiq

To generate a state preparation with Classiq, we invoke the "StatePreparation" function and specify the circuit's probabilities, qubit count, and upper error bound. We know that the maximum number of qubits is 10, that we need to input a log-normal distribution, and that the total error of the circuit (the mathematical error in approximating a log-normal distribution plus the error in translating it to a quantum circuit) should not exceed 0.01. Once these few parameters are defined, the Classiq platform will implement the quantum circuit. It's as simple as that; below is the code to generate this circuit using Classiq's Python SDK, though an equivalent circuit can be generated with the Classiq extension in Visual Studio Code. 


import classiq
from classiq import ModelDesigner
from classiq.builtin_functions import StatePreparation

# Given variables
TARGET_ERR = 1e-2
SIGMA = 0.1
N_QUBITS = 10

# Calculated probability distribution and error bound
# Set flag to False for unequal partition of the 'x' axis
lognorm_probabilities, _, l2_error_upper_bound, _ = partition(n_qubits=N_QUBITS, flag=True) 

# State preparation parameters
sp_params = StatePreparation(probabilities=lognorm_probabilities, 
                             num_qubits=N_QUBITS, 
                             error_metric={"L2": {"upper_bound": l2_error_upper_bound}})

# Circuit Synthesis
model_designer = ModelDesigner()
model_designer.StatePreparation(params=sp_params)
circuit = model_designer.synthesize()

The key to solving this problem lies in one's approach to implementing this distribution. The straightforward and simple solution to this problem is to select a subset of the domain centered on the mean of the distribution, evenly space our discretization across the available bins within this area (with 10 qubits, we have 210 bins), and disregard the remainder of the domain. The probabilities within these 210 bins of our domain subset can then be modified to match a log-normal distribution. Defining our circuit requires only loading this probability mass function into the Classiq platform and selecting our error threshold. Following this method, the Classiq platform (using version 0.15) implemented a 242-depth circuit for us. The interactive circuit below can be found here.

interactive visualization

Classiq logo

There was some error with the script

The Trick

However, there is another approach to this. Two parameters can be set when performing a state preparation: the probabilities to be loaded and the discretization method used to calculate the results. The previous method's discretization utilized a standard, linear-spacing, for its discretization. Doing so necessitated that we load in probabilities to match the desired log-normal distribution. 

So, what would occur if a non-linear discretization spacing was chosen? By selecting the spacing for our 210 bins intelligently, we can set up a circuit where all of the loaded probabilities are identical. This may make subsequent calculations for this quantum circuit difficult, but it would make setting up the state preparation trivial. We can now freely select the measurement basis for our qubits and prepare them all with the same probability in this basis. If we select the $|+\rangle$ basis, for instance, we can prepare all of our qubits with Hadamard gates, resulting in a circuit with a depth of 1, seen below and available here.

interactive visualization

Classiq logo

We were pleased to see that the winners of this competition problem were able to identify this trick, each employing their own discretization selection method. This problem exists to demonstrate that it takes more than technical knowledge to generate efficient quantum circuits; creativity is also required when generating efficient quantum circuits. To generate our discretization, we define two functions: one to calculate the L2 error and the other to generate the discretization in consideration of the L2 error. Our code has been added below.


from scipy.stats import lognorm
from scipy.interpolate import interp1d
import numpy as np

# L2 error calculation
def l2_error(pmf: np.array, x_grid: np.array, sigma=0.1):
    pmf = np.array(pmf)
    x_grid = np.array(x_grid)
    assert all(pmf >= 0)
    assert np.isclose(sum(pmf), 1)
    assert all(x_grid >= 0)
    assert all(np.diff(x_grid) > 0)
    assert len(pmf) + 1 == len(x_grid)

    n_point = 2 ** 22
    tail_value = (TARGET_ERR / 100) ** 2
    min_x = lognorm.ppf(tail_value, sigma)
    max_x = lognorm.ppf(1 - tail_value, sigma)
    x_middle = np.linspace(min_x, max_x, n_point)
    x_lower_tail = np.linspace(0, min_x, n_point // 1000)
    x_upper_tail = np.linspace(max_x, x_grid[-1], n_point // 1000) if x_grid[-1] > max_x else np.array([])

    x_approx = np.diff(x_grid) / 2 + x_grid[:-1]
    x_approx = np.concatenate(([x_approx[0]], x_approx, [x_approx[-1]]))
    pdf_approx = pmf / np.diff(x_grid)
    pdf_approx = np.concatenate(([pdf_approx[0]], pdf_approx, [pdf_approx[-1]]))

    fy = interp1d(x_approx, pdf_approx, kind='nearest', assume_sorted=True, fill_value=(0, 0), bounds_error=False)
    x_full = np.concatenate((x_lower_tail[:-1], x_middle, x_upper_tail[1:]))
    approx_pdf = fy(x_full)

    full_pdf = lognorm.pdf(x_full, sigma)
    dx = np.diff(x_full)
    dx = np.append(dx, 0)

    upper_tail_err_2_approx = lognorm.sf(x_full[-1], sigma)
    main_err_2 = sum((full_pdf - approx_pdf) ** 2 * dx)
    err = (upper_tail_err_2_approx + main_err_2) ** 0.5
    return err

# Probability distribution calculation based on chosen discretization method
def partition(n_qubits=10, flag=True):
    bins = 2 ** n_qubits
    tail_value = (TARGET_ERR / 10) ** 2
    if flag:
        x_discrete = np.linspace(lognorm.ppf(tail_value, SIGMA),
                               lognorm.ppf(1 - tail_value, SIGMA), bins + 1)
    else:
        x_discrete = lognorm.ppf(np.linspace(tail_value, 1 - tail_value, bins + 1), SIGMA)
    pmf = np.diff(lognorm.cdf(x_discrete, SIGMA))
    pmf = pmf / sum(pmf)
    ERR = l2_error(pmf, x_discrete, sigma=SIGMA)
    margin = TARGET_ERR - ERR
    assert margin > 0
    return tuple(pmf), x_discrete, margin, ERR

To see how this compares to the solutions provided by competitors, see here.

Beyond Log-Normal Distributions

Classiq makes creating a state preparation simple. Define the probabilities in which you want your circuit to be initiated, and the platform will take care of the rest. Classiq enables the flexible definition of probabilities as either a probability mass function or a gaussian mixture and provides multiple methods for defining the allowed error for the circuit, such as KL, L1, L2, Maximum Probability, and Loss of Fidelity. Here is an example of Classiq used to implement a state preparation using Gaussian moments, and here is an example of a Credit Risk Analysis algorithm using a Gaussian moment for its state preparation. 

Classiq's state preparation is flexible, easy to program, and generates your desired circuit automatically, whether you're replicating the findings of an academic paper or creating novel quantum algorithms. As we say at Classiq, "defining what you want is easy, but implementing it is difficult."  So, let the Classiq platform implement your defined circuits. Classiq customers work at a higher level. Specify the desired functionality of the circuit as well as the applicable constraints, and the Classiq platform automatically finds the right implementation from billions of design space options.

Schedule a live demonstration of the Classiq platform to see it in action, or contact us to learn how you can create industry-leading quantum circuits in minutes.

This note illustrates how to utilize the Classiq platform to solve the Log-Normal State preparation problem from our recent coding competition. We examine two methods for generating this state preparation as well as the trick necessary for generating a state preparation with a circuit depth of 1. 

Step-1 for All Quantum Algorithms

State preparations are an integral part to quantum computation, with almost all quantum applications beginning with the preparation of a state in a quantum register. In finance, for example, the state may represent the price distribution of certain assets as part of a portfolio optimization algorithm. In chemistry, it may be an initial estimate for the ground state of a molecule, and in quantum machine learning, it is the feature vector to be analyzed.

State preparation is the initialization of qubits with quantum gates so that their measured output corresponds to a desired probability distribution. This initialized state can then be utilized by subsequent quantum circuits to perform calculations. The Log-Normal State preparation we'll discuss is a commonly used initialized state in many finance calculations. 

The Log-Normal State Preparation Problem

In our recent coding competition, competitors were asked to design a circuit that generates a log-normal distribution with a mean (µ) of 0 and standard deviation (σ) of 0.1 using no more than 10 qubits. The circuit must approximate a log-normal distribution with an error of no more than 0.01 and can only contain 'u' and 'CX' gates. The winning entry would be the circuit with the shallowest depth while meeting all of the aforementioned criteria.

This problem was both the most difficult to solve and the simplest quantum circuit to compute of the four competition problems we designed. To provide our competitors with a hint on how to approach this problem, we required them to submit both the quantum circuit and the discretization method they employed.

Solving State Preparations with Classiq

To generate a state preparation with Classiq, we invoke the "StatePreparation" function and specify the circuit's probabilities, qubit count, and upper error bound. We know that the maximum number of qubits is 10, that we need to input a log-normal distribution, and that the total error of the circuit (the mathematical error in approximating a log-normal distribution plus the error in translating it to a quantum circuit) should not exceed 0.01. Once these few parameters are defined, the Classiq platform will implement the quantum circuit. It's as simple as that; below is the code to generate this circuit using Classiq's Python SDK, though an equivalent circuit can be generated with the Classiq extension in Visual Studio Code. 


import classiq
from classiq import ModelDesigner
from classiq.builtin_functions import StatePreparation

# Given variables
TARGET_ERR = 1e-2
SIGMA = 0.1
N_QUBITS = 10

# Calculated probability distribution and error bound
# Set flag to False for unequal partition of the 'x' axis
lognorm_probabilities, _, l2_error_upper_bound, _ = partition(n_qubits=N_QUBITS, flag=True) 

# State preparation parameters
sp_params = StatePreparation(probabilities=lognorm_probabilities, 
                             num_qubits=N_QUBITS, 
                             error_metric={"L2": {"upper_bound": l2_error_upper_bound}})

# Circuit Synthesis
model_designer = ModelDesigner()
model_designer.StatePreparation(params=sp_params)
circuit = model_designer.synthesize()

The key to solving this problem lies in one's approach to implementing this distribution. The straightforward and simple solution to this problem is to select a subset of the domain centered on the mean of the distribution, evenly space our discretization across the available bins within this area (with 10 qubits, we have 210 bins), and disregard the remainder of the domain. The probabilities within these 210 bins of our domain subset can then be modified to match a log-normal distribution. Defining our circuit requires only loading this probability mass function into the Classiq platform and selecting our error threshold. Following this method, the Classiq platform (using version 0.15) implemented a 242-depth circuit for us. The interactive circuit below can be found here.

interactive visualization

Classiq logo

There was some error with the script

The Trick

However, there is another approach to this. Two parameters can be set when performing a state preparation: the probabilities to be loaded and the discretization method used to calculate the results. The previous method's discretization utilized a standard, linear-spacing, for its discretization. Doing so necessitated that we load in probabilities to match the desired log-normal distribution. 

So, what would occur if a non-linear discretization spacing was chosen? By selecting the spacing for our 210 bins intelligently, we can set up a circuit where all of the loaded probabilities are identical. This may make subsequent calculations for this quantum circuit difficult, but it would make setting up the state preparation trivial. We can now freely select the measurement basis for our qubits and prepare them all with the same probability in this basis. If we select the $|+\rangle$ basis, for instance, we can prepare all of our qubits with Hadamard gates, resulting in a circuit with a depth of 1, seen below and available here.

interactive visualization

Classiq logo

We were pleased to see that the winners of this competition problem were able to identify this trick, each employing their own discretization selection method. This problem exists to demonstrate that it takes more than technical knowledge to generate efficient quantum circuits; creativity is also required when generating efficient quantum circuits. To generate our discretization, we define two functions: one to calculate the L2 error and the other to generate the discretization in consideration of the L2 error. Our code has been added below.


from scipy.stats import lognorm
from scipy.interpolate import interp1d
import numpy as np

# L2 error calculation
def l2_error(pmf: np.array, x_grid: np.array, sigma=0.1):
    pmf = np.array(pmf)
    x_grid = np.array(x_grid)
    assert all(pmf >= 0)
    assert np.isclose(sum(pmf), 1)
    assert all(x_grid >= 0)
    assert all(np.diff(x_grid) > 0)
    assert len(pmf) + 1 == len(x_grid)

    n_point = 2 ** 22
    tail_value = (TARGET_ERR / 100) ** 2
    min_x = lognorm.ppf(tail_value, sigma)
    max_x = lognorm.ppf(1 - tail_value, sigma)
    x_middle = np.linspace(min_x, max_x, n_point)
    x_lower_tail = np.linspace(0, min_x, n_point // 1000)
    x_upper_tail = np.linspace(max_x, x_grid[-1], n_point // 1000) if x_grid[-1] > max_x else np.array([])

    x_approx = np.diff(x_grid) / 2 + x_grid[:-1]
    x_approx = np.concatenate(([x_approx[0]], x_approx, [x_approx[-1]]))
    pdf_approx = pmf / np.diff(x_grid)
    pdf_approx = np.concatenate(([pdf_approx[0]], pdf_approx, [pdf_approx[-1]]))

    fy = interp1d(x_approx, pdf_approx, kind='nearest', assume_sorted=True, fill_value=(0, 0), bounds_error=False)
    x_full = np.concatenate((x_lower_tail[:-1], x_middle, x_upper_tail[1:]))
    approx_pdf = fy(x_full)

    full_pdf = lognorm.pdf(x_full, sigma)
    dx = np.diff(x_full)
    dx = np.append(dx, 0)

    upper_tail_err_2_approx = lognorm.sf(x_full[-1], sigma)
    main_err_2 = sum((full_pdf - approx_pdf) ** 2 * dx)
    err = (upper_tail_err_2_approx + main_err_2) ** 0.5
    return err

# Probability distribution calculation based on chosen discretization method
def partition(n_qubits=10, flag=True):
    bins = 2 ** n_qubits
    tail_value = (TARGET_ERR / 10) ** 2
    if flag:
        x_discrete = np.linspace(lognorm.ppf(tail_value, SIGMA),
                               lognorm.ppf(1 - tail_value, SIGMA), bins + 1)
    else:
        x_discrete = lognorm.ppf(np.linspace(tail_value, 1 - tail_value, bins + 1), SIGMA)
    pmf = np.diff(lognorm.cdf(x_discrete, SIGMA))
    pmf = pmf / sum(pmf)
    ERR = l2_error(pmf, x_discrete, sigma=SIGMA)
    margin = TARGET_ERR - ERR
    assert margin > 0
    return tuple(pmf), x_discrete, margin, ERR

To see how this compares to the solutions provided by competitors, see here.

Beyond Log-Normal Distributions

Classiq makes creating a state preparation simple. Define the probabilities in which you want your circuit to be initiated, and the platform will take care of the rest. Classiq enables the flexible definition of probabilities as either a probability mass function or a gaussian mixture and provides multiple methods for defining the allowed error for the circuit, such as KL, L1, L2, Maximum Probability, and Loss of Fidelity. Here is an example of Classiq used to implement a state preparation using Gaussian moments, and here is an example of a Credit Risk Analysis algorithm using a Gaussian moment for its state preparation. 

Classiq's state preparation is flexible, easy to program, and generates your desired circuit automatically, whether you're replicating the findings of an academic paper or creating novel quantum algorithms. As we say at Classiq, "defining what you want is easy, but implementing it is difficult."  So, let the Classiq platform implement your defined circuits. Classiq customers work at a higher level. Specify the desired functionality of the circuit as well as the applicable constraints, and the Classiq platform automatically finds the right implementation from billions of design space options.

Schedule a live demonstration of the Classiq platform to see it in action, or contact us to learn how you can create industry-leading quantum circuits in minutes.

About "The Qubit Guy's Podcast"

Hosted by The Qubit Guy (Yuval Boger, our Chief Marketing Officer), the podcast hosts thought leaders in quantum computing to discuss business and technical questions that impact the quantum computing ecosystem. Our guests provide interesting insights about quantum computer software and algorithm, quantum computer hardware, key applications for quantum computing, market studies of the quantum industry and more.

If you would like to suggest a guest for the podcast, please contact us.

Start Creating Quantum Software Without Limits

contact us