Skip to main content

View on GitHub

Open this notebook in GitHub to run it yourself
This tutorial covers the basics of executing a quantum program using Classiq directly through the Python SDK. It is also possible to use the Classiq Platform to execute quantum algorithms. For this, we will start by synthesizing the following example from the synthesis tutorial:

Example 1: Sampling Arithmetics and changing number of shots

from classiq import *


@qfunc
def main(x: Output[QNum[3]], y: Output[QNum]) -> None:
    allocate(x)
    hadamard_transform(x)
    y |= x**2 + 1


qprog = synthesize(main)
show(qprog)
Output:

Quantum program link: https://platform.classiq.io/circuit/3EJKxJJ639O4HKXMbzBJV92B2vJ
  

This quantum program evaluates the function y(x)=x2+1y(x) = x^2 + 1, for all integers x[0,7]x \in [0,7]. To execute a quantum program and sample the states, use sample:
results = sample(qprog)
Output:

Submitting job to simulator
  Job: https://platform.classiq.io/jobs/afa2ebcd-8cb9-4f59-8507-94ed94b802dc
  

The output from sample is a dataframe with information regarding execution:
results
xycountsprobabilitybitstring
07502800.136719110010111
15262730.133301011010101
2122660.129883000010001
3012620.127930000001000
4252500.122070000101010
56372430.118652100101110
63102380.116211001010011
74172360.115234010001100
The information displayed in the dataframe is:
  • counts shows the number of times each state was measured.
  • bitstring is the bitstring that represents each state measured.
  • x and y are the numerical representation of the states associated with the measurement.
  • probability is the probability associated with each measured state.
By default, the number of executions of the quantum program is 20482048. This quantity, called the number of shots, can be modified inside sample. For instance, if we want to execute the same circuit with 10,00010{,}000 shots:
results_more_shots = sample(qprog, num_shots=10000)
Output:

Submitting job to simulator
  Job: https://platform.classiq.io/jobs/5cd7b05e-2751-47a6-9d46-50a2e702244b
  

The number of counts for each state will grow proportionally with the number of shots:
results_more_shots
xycountsprobabilitybitstring
031013080.1308001010011
175012710.1271110010111
241712660.1266010001100
30112560.1256000001000
42512560.1256000101010
563712290.1229100101110
61212140.1214000010001
752612000.1200011010101

Example 2: GHZ States and noise

Many simulators provide noise models that approximate the behavior of real quantum hardware. In this example, we create a GHZ state using the Classiq simulator while emulating the noise profile of IBM Pittsburgh, an IBM backend available through Classiq. We begin by defining the model for the GHZ state:
from classiq import *


@qfunc
def main(x: Output[QArray[QBit, 3]]):
    allocate(x)
    H(x[0])
    CX(x[0], x[1])
    CX(x[1], x[2])


qprog = synthesize(main)
Next, we configure the simulator to use the noise model associated with the IBM Pittsburgh backend. This is done by passing a noise_model entry through the config argument:
cfg = {"noise_model": "ibm_pittsburgh"}
res = sample(qprog, backend="simulator", config=cfg)
res
Output:

Submitting job to simulator
  Job: https://platform.classiq.io/jobs/6f56f8a5-1441-450f-b24c-2d4b7232dafc
  

xcountsprobabilitybitstring
0[1, 1, 1]10140.495117111
1[0, 0, 0]9850.480957000
2[0, 0, 1]100.004883100
3[1, 1, 0]90.004395011
4[0, 1, 1]90.004395110
5[0, 1, 0]80.003906010
6[1, 0, 1]70.003418101
7[1, 0, 0]60.002930001
For an ideal, noiseless GHZ state, the only expected measurement outcomes are [0, 0, 0] and [1, 1, 1], each occurring with approximately equal probability. All other basis states should have zero probability. Here, because the simulation includes a realistic noise model, a small fraction of the measurements appears in other states. The dominant outcomes are still [0, 0, 0] and [1, 1, 1], but the presence of low-probability additional bitstrings reflects the effect of hardware noise on the quantum program execution.

State vector simulation

A state vector simulator returns the amplitudes of the quantum states produced by a quantum program. Unlike sampling, which estimates output probabilities from repeated measurements, state vector simulation gives direct access to the simulated quantum state. On real quantum hardware, these amplitudes are not directly observable. Reconstructing them requires quantum state tomography, which involves measuring the system in different bases to infer the output state. In this example, we calculate the state vector of the quantum program using calculate_state_vector:
res_sv = calculate_state_vector(qprog)
res_sv
Output:

Submitting job to simulator
  Job: https://platform.classiq.io/jobs/2eced471-369f-4048-b515-3a8d83988f80
  

xamplitudemagnitudephaseprobabilitybitstring
0[0, 0, 0]0.707107+0.000000j0.710.00π0.5000
1[1, 1, 1]0.707107+0.000000j0.710.00π0.5111
The information displayed in the dataframe is:
  • amplitude is the complex amplitude associated with each basis state.
  • magnitude is the absolute value of the amplitude.
  • phase is the phase of the amplitude.
  • probability is the probability of measuring the corresponding state.
  • bitstring is the bitstring representation of the basis state.
  • x is the value of the quantum array. It is displayed as a list of 0s and 1s, with each entry corresponding to one qubit in the array.
In this case, the output corresponds to an ideal GHZ state. The only states with nonzero probability are [0, 0, 0] and [1, 1, 1], each with probability 0.5 and amplitude approximately 1/21/\sqrt{2}.

Backend selection

The backend of an execution is the hardware or simulator where the quantum program is executed. To select a specific backend, it is necessary to know its correct name and provider. To do so, run get_backend_details() for a concise list of available backends.
backend_list = get_backend_details()
backend_list.head()
providerbackendtypenum_qubitsis_availablepending_jobsqueue_time
0classiqnvidia_simulatorsimulator29TrueNaNNaT
1classiqsimulatorsimulator28TrueNaNNaT
2classiqsimulator_density_matrixsimulator28TrueNaNNaT
3classiqsimulator_matrix_product_statesimulator28TrueNaNNaT
4alice&bobLOGICAL_EARLYsimulator15TrueNaNNaT
Now, to define a backend, set the backend name following the rule "provider/backend" under the execution function used. For example, you can use the Classiq simulator to realize a state vector simulation of the GHZ state, or the MPS simulator to sample over the same state:
default_backend = "classiq/simulator"
MPS_backend = "classiq/simulator_matrix_product_state"

res_default = calculate_state_vector(qprog, backend=default_backend)
res_MPS = sample(qprog, MPS_backend)
Output:

Submitting job to classiq/simulator
  Job: https://platform.classiq.io/jobs/301d33f4-97a4-46bf-8794-aef3e6da2c6e
  Submitting job to classiq/simulator_matrix_product_state
  Job: https://platform.classiq.io/jobs/b703a7ac-9e4d-4752-8166-4474c5eb8c66
  

res_default
xamplitudemagnitudephaseprobabilitybitstring
0[0, 0, 0]0.707107+0.000000j0.710.00π0.5000
1[1, 1, 1]0.707107+0.000000j0.710.00π0.5111
res_MPS
xcountsprobabilitybitstring
0[0, 0, 0]10620.518555000
1[1, 1, 1]9860.481445111