Quick Start¶
After installing and configuring QESEM, you can create your first QESEM job. Before diving into the API documentation, provided below is a full example of how to use it. This script runs a simple QESEM job on IBM QPU:
Setup¶
!pip install -U --upgrade-strategy eager qedma-api
!pip install qiskit
import qedma_api
import qiskit
import datetime
from qedma_api import ExpectationValues
# configuration
qedma_api_token = "<Qedma api token>"
ibm_instance = "<IBM instance ID>" # e.g."hub/group/project"
qedma_client = qedma_api.Client(api_token = qedma_api_token)
provider = qedma_api.IBMQProvider(
instance = ibm_instance,
)
qedma_client.set_provider(provider)
Create a QESEM job¶
Create a circuit using qiskit
circuit = qiskit.QuantumCircuit(2)
circuit.cx(0, 1)
circuit.cx(1, 0)
circuit.cx(0, 1)
<qiskit.circuit.instructionset.InstructionSet at 0x7efd61934a90>
We will use two observables
$$ \frac{1}{5}\sum_{q=0}^4 Z_{q} \quad \text{and}\quad Z_0 Z_1 + 0.5 X_2 Z_4 $$
We pass them as two Observable
instances
observables = (
qedma_api.Observable(
{f"Z{q}": 1 / 5 for q in range(5)}
),
qedma_api.Observable(
{"Z0,Z1": 1.0, "X2,Z4": 0.5}
),
)
Create a new QESEM job
job = qedma_client.create_job(
circuit = circuit,
observables = observables,
precision = 0.1,
backend = "fake_brisbane",
description = "my qesem job",
)
2025-04-24 12:15:17.159 | INFO | Submitting new job 2025-04-24 12:15:19.348 | INFO | [e63b3275f4fd4944a570f9f921ac069e] New job created
Note
The precision
signifies the acceptable error on the expectation values of the observables in absolute value. Namely, the QPU runtime for mitigation will be determined to provide output values for all the observables of interest that fall within a 1σ
confidence interval of the target precision. If multiple observables are provided, the mitigation will run until the target precision is reached for each of the input observables.
Estimate QPU time¶
Once the job is created, a QPU time estimation is initiated. Use the function qedma_client.wait_for_estimation()
which waits until the estimation is complete and returns the time estimation. (There are two types of time estimation, learn more here)
qedma_client.wait_for_time_estimation(job_id = job.job_id)
2025-04-24 12:16:02.816 | INFO | [e63b3275f4fd4944a570f9f921ac069e] Time estimation: [30.0 minutes]
datetime.timedelta(seconds=1800)
Note
In general, the problem is BQP-hard but QESEM's heuristic time estimation has polynomial scaling. The heuristic estimation is best for a Pauli string observable while for more complex observables it gives an upper bound. During the estimation, job execution, QESEM does not use the heuristic time estimation, but a more reliable estimation based on empirical data to ensure minimal QPU-time usage.
Start Execution¶
Initiate the execution of the QESEM job:
qedma_client.start_job(
job_id = job.job_id,
max_qpu_time = datetime.timedelta(minutes = 30)
)
2025-04-24 12:16:03.436 | INFO | [e63b3275f4fd4944a570f9f921ac069e] Starting job
Note
max_qpu_time
is mandatory when starting a QESEM job. It allows you to limit the QPU time, specified in seconds, to be used for the entire QESEM process. Since the final QPU time required to reach the target accuracy is determined dynamically during the QESEM job, this parameter enables you to limit the cost of the experiment. If the dynamically-determined QPU time is shorter than the time allocated by the user, this parameter will not affect the experiment. After the time limit it reached, QESEM stops sending new circuits. Circuits that have already been sent continue running (so the total time may surpass the limit by up to 30 minutes), and the user receives the processed results from the circuits that ran up to that point.
Even before the QESEM job completes, we let you track the job's progress and access intemadiate mitigation results. The QESEM job executes multiple batches of characterization, calibration and mitigation circuits. During the mitigation stage, QESEM calculates intermediate mitigation results that gradually converge to the final mitigated result. Use the qedma_client.wait_for_job_complete()
function to get all this data printed automatically, it will wait until the job completes.
qedma_client.wait_for_job_complete(job_id = job.job_id)
2025-04-24 12:16:04.633 | INFO | [e63b3275f4fd4944a570f9f921ac069e] step: [Queued] 2025-04-24 12:16:15.366 | INFO | [e63b3275f4fd4944a570f9f921ac069e] step: [Initial Transpilation] 2025-04-24 12:16:15.367 | INFO | [e63b3275f4fd4944a570f9f921ac069e] step: [Qubit Selection] 2025-04-24 12:16:15.368 | INFO | [e63b3275f4fd4944a570f9f921ac069e] step: [Calibration] 2025-04-24 12:16:15.369 | INFO | [e63b3275f4fd4944a570f9f921ac069e] step: [Characterization] 2025-04-24 12:16:47.365 | INFO | [e63b3275f4fd4944a570f9f921ac069e] step: [Batch Mitigation #1] 2025-04-24 12:17:51.427 | INFO | [e63b3275f4fd4944a570f9f921ac069e] Final results: [[{'Z0': 0.2, 'Z1': 0.2, 'Z2': 0.2, 'Z3': 0.2, 'Z4': 0.2}: (1.0011206723573438 ± 0.0035221450351644976), {'Z0,Z1': 1.0, 'X2,Z4': 0.5}: (1.0024692679633742 ± 0.01044551181395117)]]
JobDetails(account_id='910759b8-0f0f-4084-a532-9f74e29a94ec', account_email='[email protected]', job_id='e63b3275f4fd4944a570f9f921ac069e', description='my qesem job', masked_account_token='***cOcw', masked_qpu_token='****1155', qpu_name='fake_brisbane', circuit=None, precision_mode=None, status=<JobStatus.SUCCEEDED: 'SUCCEEDED'>, analytical_qpu_time_estimation=datetime.timedelta(seconds=1800), empirical_qpu_time_estimation=None, total_execution_time=datetime.timedelta(seconds=120), created_at=datetime.datetime(2025, 4, 24, 9, 15, 18, 32942), updated_at=datetime.datetime(2025, 4, 24, 9, 17, 50, 279509), qpu_time={'execution': datetime.timedelta(0)}, qpu_time_limit=datetime.timedelta(seconds=1800), warnings=None, errors=None, intermediate_results=None, results=ExpectationValues([Observable({'Z0': 0.2, 'Z1': 0.2, 'Z2': 0.2, 'Z3': 0.2, 'Z4': 0.2}): ExpectationValue(value=1.0011206723573438, error_bar=0.0035221450351644976),Observable({'Z0,Z1': 1.0, 'X2,Z4': 0.5}): ExpectationValue(value=1.0024692679633742, error_bar=0.01044551181395117)]), noisy_results=ExpectationValues([Observable({'Z0': 0.2, 'Z1': 0.2, 'Z2': 0.2, 'Z3': 0.2, 'Z4': 0.2}): ExpectationValue(value=0.9726857142857144, error_bar=0.0017250941320754633),Observable({'Z0,Z1': 1.0, 'X2,Z4': 0.5}): ExpectationValue(value=0.9358571428571428, error_bar=0.006736693961128319)]), execution_details=ExecutionDetails(total_shots=294000, mitigation_shots=180000, gate_fidelities={'CNOT': 0.9903584251993565, 'ID1Q': 0.9991085123629162}, transpiled_circuits=None), progress=JobProgress(steps=[JobStep(name='Queued'), JobStep(name='Initial Transpilation'), JobStep(name='Qubit Selection'), JobStep(name='Calibration'), JobStep(name='Characterization'), JobStep(name='Batch Mitigation #1')]))
Note
The QESEM results include an expectation value and an error bar for each requested observable. These values are updated throughout the QESEM process and finalized when the job status is SUCCEEDED
. The error bars are derived empirically from the standard deviation of the QESEM mitigation circuits executed on the QPU. They represent the statistical uncertainty of the QESEM result within the allocated QPU time – specifically, the standard deviation of its distribution around the ideal expectation value.
Next Steps¶
- Learn about QESEM's Advanced Settings, which allow you to control QESEM execution to better fit your use case.
- Learn how to access QESEM job Execution Metrics, such as noisy results and the total number of shots used during execution.