Quantum Cloud Computing
This tutorial covers how to connect to OriginQ's quantum cloud platform, submit quantum programs to real quantum processors and cloud simulators, track job status, and retrieve results using the pyqpanda3.qcloud module.
Prerequisites: Simulation -- you should be comfortable building quantum programs and running them on a local simulator before moving to cloud execution.
Table of Contents
- 1. Introduction to Quantum Cloud Computing
- 2. QCloudService — Connecting to the Cloud
- 3. QCloudBackend — The Execution Target
- 4. QCloudJob — Tracking Execution
- 5. QCloudResult — Processing Output
- 6. QCloudOptions — Configuring Execution
- 7. QCloudNoiseModel — Cloud Noise Simulation
- 8. Enums Reference
- 9. Practical Workflow
- 10. Advanced Patterns
- 11. Summary
1. Introduction to Quantum Cloud Computing
1.1 Why Use Cloud Quantum Computing
Local simulators are excellent for learning, debugging, and developing algorithms. However, they face fundamental limitations:
- The memory required to simulate
qubits grows as , making it impractical to simulate more than roughly 30 qubits on a typical workstation. - Simulators model ideal quantum behavior, which differs significantly from real quantum processors affected by noise, decoherence, and gate errors.
Cloud quantum computing addresses both problems:
| Aspect | Local Simulator | Cloud Quantum Computing |
|---|---|---|
| Qubit count | Limited by RAM (typically ~25-30 qubits) | Up to 72+ qubits on real hardware |
| Noise model | Artificial, user-specified | Real, inherent to the physical device |
| Speed | Fast for small circuits, exponential scaling | Constant for any circuit depth on real QPU |
| Cost | Free | Consumes cloud credits or subscription |
| Availability | Always available | Subject to queue times and maintenance |
| Fidelity | Perfect (noiseless) by default | Limited by hardware quality |
Cloud platforms also provide cloud-hosted simulators that can handle larger circuits than your local machine, along with specialized backends for full-amplitude simulation, partial amplitude computation, and expectation value estimation.
1.2 OriginQ Cloud Platform Overview
The OriginQ cloud platform provides access to several categories of quantum backends:
- Real quantum processors (QPUs) -- Superconducting quantum chips with 6 to 72+ qubits. These are physical devices that execute your circuit on actual quantum hardware.
- Full-amplitude simulators -- Cloud-hosted statevector simulators that compute all
amplitudes. Useful for verification and algorithm development. - Partial amplitude simulators -- Compute only specific amplitudes of the statevector, enabling simulation of larger circuits.
- Specialized backends -- Backends for state tomography, expectation value computation, and other analysis tasks.
The cloud platform is accessed through the pyqpanda3.qcloud module, which provides a Pythonic interface for authentication, backend discovery, job submission, and result retrieval.
2. QCloudService — Connecting to the Cloud
The QCloudService class is the main entry point for interacting with the OriginQ cloud platform. It handles authentication, backend discovery, and logging configuration.
2.1 Creating a Service Instance
To create a QCloudService, you need an API key. The optional url parameter defaults to the OriginQ cloud server.
from pyqpanda3.qcloud import QCloudService
# Connect using the default OriginQ cloud URL
service = QCloudService("your_api_key_here")
# Or specify a custom URL (e.g., for a private cloud deployment)
# service = QCloudService("your_api_key_here", url="https://custom-cloud.example.com")The API key is a string that authenticates your account. You can obtain one by registering on the OriginQ cloud platform. Treat your API key like a password -- do not share it or commit it to version control.
A recommended pattern is to store the API key in an environment variable:
import os
from pyqpanda3.qcloud import QCloudService
# Read the API key from an environment variable
api_key = os.environ["QPANDA3_API_KEY"]
service = QCloudService(api_key)Then set the environment variable before running your script:
export QPANDA3_API_KEY="your_api_key_here"
python your_script.py2.2 Configuring Logging
The cloud service supports configurable logging for debugging and monitoring. Use setup_logging to enable it:
from pyqpanda3.qcloud import QCloudService, LogOutput, LogLevel
service = QCloudService("your_api_key_here")
# Log to the console (default)
service.setup_logging(output=LogOutput.CONSOLE)
# Log to a file instead
service.setup_logging(output=LogOutput.FILE, file_path="cloud_debug.log")The setup_logging method accepts two parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
output | LogOutput | LogOutput.CONSOLE | Where to send log output |
file_path | str | "" | File path when output=LogOutput.FILE |
Logging is optional. If you do not call setup_logging, the service operates silently.
2.3 Listing Available Backends
The backends() method returns a dict mapping backend names to availability flags:
from pyqpanda3.qcloud import QCloudService
service = QCloudService("your_api_key_here")
backends = service.backends()
print("Available backends:")
for name, available in backends.items():
status = "available" if available else "unavailable"
print(f" - {name}: {status}")Example output:
Available backends:
- origin_wukong: available
- origin_72: available
- full_amplitude: available
- partial_amplitude: unavailableThe available backends depend on your account permissions and the current state of the cloud platform. Real quantum processors may be temporarily unavailable during calibration or maintenance.
2.4 Getting a Specific Backend
Once you know the name of a backend, retrieve it with backend():
from pyqpanda3.qcloud import QCloudService
service = QCloudService("your_api_key_here")
# Get a real quantum processor backend
qpu_backend = service.backend("origin_wukong")
# Get a cloud simulator backend
sim_backend = service.backend("full_amplitude")The returned QCloudBackend object provides methods for submitting jobs, querying chip information, and computing expectation values. We explore it in detail in the next section.
3. QCloudBackend — The Execution Target
A QCloudBackend represents a specific quantum computing resource -- either a real quantum processor or a cloud-hosted simulator. You obtain one through QCloudService.backend().
3.1 Backend Overview
from pyqpanda3.qcloud import QCloudService
service = QCloudService("your_api_key_here")
backend = service.backend("origin_wukong")
# Get the backend name
print(f"Backend name: {backend.name()}")3.2 Running a Quantum Program
The simplest way to run a program is with the run() method, which takes a QProg and a shot count:
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService
# Build a Bell state circuit
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
# Connect and submit
service = QCloudService("your_api_key_here")
backend = service.backend("full_amplitude")
# Submit the job with 3000 shots
job = backend.run(prog, shots=3000)
print(f"Submitted job: {job.job_id()}")The run() method returns a QCloudJob object immediately. The actual execution happens asynchronously on the cloud server.
3.3 Running with Options
For more control over compilation and execution, use QCloudOptions:
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService, QCloudOptions
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
service = QCloudService("your_api_key_here")
backend = service.backend("origin_wukong")
# Configure execution options
options = QCloudOptions()
options.set_amend(True) # Enable amendment (error mitigation)
options.set_mapping(True) # Enable qubit mapping
options.set_optimization(True) # Enable circuit optimization
# Submit with options
job = backend.run(prog, shots=3000, options=options)The QCloudOptions class (covered in detail in Section 6) lets you control compilation behavior on the server side.
3.4 Running with Noise Model
When submitting to a cloud simulator (not a real QPU), you can specify a noise model to simulate realistic hardware behavior:
from pyqpanda3 import core
from pyqpanda3.qcloud import (
QCloudService, QCloudNoiseModel, NOISE_MODEL
)
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
service = QCloudService("your_api_key_here")
backend = service.backend("full_amplitude")
# Create a depolarizing noise model
noise = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001], # single-qubit gate noise parameter
[0.01] # two-qubit gate noise parameter
)
# Submit with noise model
job = backend.run(prog, shots=3000, model=noise)Note that noise models are only relevant for cloud simulators. Real quantum processors already have inherent noise -- you cannot add a software noise model on top.
3.5 Chip Information
For real QPU backends, you can query detailed information about the chip's physical characteristics:
from pyqpanda3.qcloud import QCloudService
service = QCloudService("your_api_key_here")
backend = service.backend("origin_wukong")
# Get chip information
chip_info = backend.chip_info()
# Number of qubits on the chip
print(f"Qubits: {chip_info.qubits_num()}")
# Chip identifier
print(f"Chip ID: {chip_info.chip_id()}")
# Available qubits (not all physical qubits may be usable)
available = chip_info.available_qubits()
print(f"Available qubits: {available}")
# High-frequency qubits
hf_qubits = chip_info.high_frequency_qubits()
print(f"High-frequency qubits: {hf_qubits}")
# Basic gate set supported by this chip
gates = chip_info.get_basic_gates()
print(f"Basic gates: {gates}")
# Chip topology (connectivity between qubits)
topology = chip_info.get_chip_topology()
print(f"Topology edges ({len(topology)} connections):")
for edge in topology[:5]: # Show first 5 edges
print(f" {edge[0]} <-> {edge[1]}")Each qubit on the chip has individual characteristics:
# Get per-qubit information
single_info_list = chip_info.single_qubit_info()
for info in single_info_list[:3]: # Show first 3 qubits
print(f"Qubit {info.get_qubit_id()}:")
print(f" T1 (relaxation): {info.get_t1():.2f} us")
print(f" T2 (coherence): {info.get_t2():.2f} us")
print(f" Gate fidelity: {info.get_single_gate_fidelity():.4f}")
print(f" Readout fidelity: {info.get_readout_fidelity():.4f}")
print(f" Frequency: {info.get_frequency():.2f} GHz")Two-qubit gate information is also available:
# Get two-qubit gate information
double_info_list = chip_info.double_qubits_info()
for info in double_info_list[:3]:
qubits = info.get_qubits()
print(f"Qubits ({qubits[0]}, {qubits[1]}): "
f"fidelity = {info.get_fidelity():.4f}")This information is valuable for:
- Choosing which qubits to use for your circuit
- Understanding the connectivity constraints (which qubit pairs support two-qubit gates)
- Estimating the expected fidelity of your circuit on the real hardware
3.6 Expectation Values
The cloud backend can directly compute expectation values of Hamiltonians or Pauli operators without requiring you to manually parse measurement results:
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService, QCloudOptions
from pyqpanda3.hamiltonian import Hamiltonian, PauliOperator
# Build a circuit
prog = core.QProg()
prog << core.X(0)
prog << core.CNOT(0, 1)
prog << core.SWAP(1, 2)
# Define a Hamiltonian
hamiltonian = Hamiltonian({"X0 Y1": 1.1, "X0 Z1 I2": 2.1, "I1 Z2": 3.1})
# Or equivalently, a Pauli operator
pauli_op = PauliOperator({"X0 Y1": 1.1, "X0 Z1 I2": 2.1, "I1 Z2": 3.1})
service = QCloudService("your_api_key_here")
# Compute expectation on a real QPU
backend = service.backend("origin_wukong")
expval = backend.expval_hamiltonian(prog, hamiltonian, QCloudOptions())
print(f"Hamiltonian expectation (QPU): {expval}")
# Compute on cloud simulator
sim_backend = service.backend("full_amplitude")
expval_sim = sim_backend.expval_hamiltonian(prog, hamiltonian)
print(f"Hamiltonian expectation (sim): {expval_sim}")
# Pauli operator expectation
expval_pauli = backend.expval_pauli_operator(prog, pauli_op, QCloudOptions())
print(f"Pauli operator expectation: {expval_pauli}")The expval_hamiltonian and expval_pauli_operator methods also accept a shots parameter and an optional noise model:
# With explicit shots and noise model on simulator
from pyqpanda3.qcloud import QCloudNoiseModel, NOISE_MODEL
noise = QCloudNoiseModel(NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR, [0.001], [0.01])
expval = sim_backend.expval_hamiltonian(prog, hamiltonian, shots=5000, noise_model=noise)4. QCloudJob — Tracking Execution
After submitting a quantum program to the cloud, you receive a QCloudJob object that represents the asynchronous computation. The job goes through several states before completing.
4.1 Job Lifecycle
When you submit a job, it progresses through the following states:
| State | Description |
|---|---|
QUEUING | The job has been received and is waiting in the execution queue |
WAITING | The job is waiting to be assigned to a compute resource |
COMPUTING | The job is actively being executed on the quantum processor or simulator |
FINISHED | The job has completed successfully and results are available |
FAILED | The job encountered an error and did not complete |
4.2 Creating a Job Reference
A QCloudJob is created in two ways:
From a submission -- the backend.run() method returns a QCloudJob:
job = backend.run(prog, shots=3000)From a job ID -- if you saved a job ID from a previous session, you can reconstruct the job reference:
from pyqpanda3.qcloud import QCloudJob
# Reconstruct a job reference from a known job ID
job = QCloudJob("ABC123DEF456")This is useful when you submitted a long-running job and want to check its status in a new Python session.
4.3 Checking Job Status
Use the status() method to check the current state of a job:
from pyqpanda3.qcloud import JobStatus
status = job.status()
print(f"Current status: {status}")
# Compare against known states
if status == JobStatus.FINISHED:
print("Job completed successfully!")
elif status == JobStatus.FAILED:
print("Job failed. Check error messages.")
elif status == JobStatus.QUEUING:
print("Job is in the queue. Please wait.")
elif status == JobStatus.COMPUTING:
print("Job is currently executing.")
elif status == JobStatus.WAITING:
print("Job is waiting to be processed.")4.4 Querying Job Information
The query() method retrieves detailed information about the job without fetching the full result. It returns a QCloudResult with metadata:
result_info = job.query()
print(f"Job ID: {result_info.job_id()}")
print(f"Job status: {result_info.job_status()}")If the job has failed, you can retrieve the error message:
if result_info.job_status() == JobStatus.FAILED:
print(f"Error: {result_info.error_message()}")4.5 Retrieving Job Results
The result() method blocks until the job completes (or the server reports a final status) and returns a QCloudResult:
# Wait for completion and get results
result = job.result()
# Access the data
probs = result.get_probs()
counts = result.get_counts()
print(f"Probabilities: {probs}")
print(f"Counts: {counts}")If the job has not finished, result() may return a result with a non-FINISHED status. Always check the status:
result = job.result()
if result.job_status() == JobStatus.FINISHED:
counts = result.get_counts()
print(f"Measurement counts: {counts}")
else:
print(f"Job not finished. Status: {result.job_status()}")5. QCloudResult — Processing Output
The QCloudResult class provides multiple ways to access the output of a completed cloud job.
5.1 Getting Probabilities
The get_probs() method returns the estimated probability distribution over measurement outcomes:
from pyqpanda3.qcloud import DataBase
result = job.result()
# Get probabilities as binary strings (default)
probs_binary = result.get_probs(base=DataBase.Binary)
print("Probabilities (binary):", probs_binary)
# Example: {'00': 0.4987, '11': 0.5013}
# Get probabilities as hexadecimal strings
probs_hex = result.get_probs(base=DataBase.Hex)
print("Probabilities (hex):", probs_hex)
# Example: {'0x0': 0.4987, '0x3': 0.5013}For batch submissions (multiple circuits), use get_probs_list() which returns a list of probability dictionaries:
# When running multiple programs in a batch
probs_list = result.get_probs_list()
for i, probs in enumerate(probs_list):
print(f"Circuit {i} probabilities: {probs}")5.2 Getting Measurement Counts
The get_counts() method returns the raw measurement counts from each shot:
from pyqpanda3.qcloud import DataBase
result = job.result()
# Counts as binary strings (default)
counts = result.get_counts(base=DataBase.Binary)
print("Counts (binary):", counts)
# Example: {'00': 1496, '11': 1504}
# Counts as hexadecimal strings
counts_hex = result.get_counts(base=DataBase.Hex)
print("Counts (hex):", counts_hex)
# Example: {'0x0': 1496, '0x3': 1504}Similarly, get_counts_list() returns results for batch submissions (only available when using batch run() with multiple circuits):
counts_list = result.get_counts_list(base=DataBase.Binary)
for i, counts in enumerate(counts_list):
total = sum(counts.values())
print(f"Circuit {i}: {total} total shots, counts = {counts}")5.3 Accessing Raw Data
The origin_data() method returns the raw JSON response from the cloud server. This is useful for debugging or accessing fields not exposed through the Python API:
raw_json = result.origin_data()
print("Raw server response:")
print(raw_json)5.4 Additional Result Methods
The QCloudResult class provides several additional methods for specialized output types:
result = job.result()
# Job ID from the result
job_id = result.job_id()
print(f"Job ID: {job_id}")
# Job status from the result
status = result.job_status()
print(f"Status: {status}")
# Error message (if job failed)
error = result.error_message()
if error:
print(f"Error: {error}")
# Amplitudes (for partial amplitude backend)
amplitudes = result.get_amplitudes()
print(f"Amplitudes: {amplitudes}")
# State fidelity (for state tomography backend)
fidelity = result.get_state_fidelity()
print(f"State fidelity: {fidelity}")
# State tomography density matrix (returns list[list[complex]])
density = result.get_state_tomography_density()
print(f"Density matrix rows: {len(density)}, cols: {len(density[0])}")Not all methods are available for all job types. For example, get_state_fidelity() and get_state_tomography_density() are only available when the job was submitted using run_quantum_state_tomography(). Similarly, get_amplitudes() is only available for partial amplitude jobs.
6. QCloudOptions — Configuring Execution
The QCloudOptions class provides fine-grained control over how the cloud server compiles and executes your circuit.
6.1 Creating Options
from pyqpanda3.qcloud import QCloudOptions
# Create with all defaults
options = QCloudOptions()
# Print current settings
options.print()6.2 Compilation Options
When submitting to a real QPU, the cloud server performs several compilation steps. You can enable or disable each one:
from pyqpanda3.qcloud import QCloudOptions
options = QCloudOptions()
# Amendment: applies error mitigation based on calibration data
options.set_amend(True)
# Mapping: maps logical qubits to physical qubits based on chip topology
options.set_mapping(True)
# Optimization: simplifies the circuit (gate cancellation, decomposition)
options.set_optimization(True)
# Point label: specifies the calibration point to use
options.set_point_label(0)
# Specified block: restricts execution to a specific block on the chip
options.set_specified_block([1])
# Probability vs counts: controls the output format
options.set_is_prob_counts(True)Query the current settings:
print(f"Amendment: {options.is_amend()}")
print(f"Mapping: {options.is_mapping()}")
print(f"Optimization: {options.is_optimization()}")
print(f"Point label: {options.point_label()}")
print(f"Is prob counts: {options.is_prob_counts()}")
print(f"Specified block: {options.specified_block()}")6.3 Custom Options
For advanced use cases, you can set arbitrary key-value options:
from pyqpanda3.qcloud import QCloudOptions
options = QCloudOptions()
# Set custom options of various types
options.set_custom_option("repetitions", 10) # int
options.set_custom_option("threshold", 0.95) # double
options.set_custom_option("label", "experiment_1") # string
options.set_custom_option("verbose", True) # bool
# Check if a custom option exists
if options.has_custom_option("threshold"):
print("threshold exists")
# Get all custom options as a dictionary
all_options = options.get_custom_options()
print(f"All custom options: {all_options}")
print(f"Threshold: {all_options['threshold']}")
print(f"Repetitions: {all_options['repetitions']}")
print(f"Label: {all_options['label']}")
print(f"Verbose: {all_options['verbose']}")7. QCloudNoiseModel — Cloud Noise Simulation
When running on cloud simulators, you can attach a noise model to simulate the behavior of a real quantum processor. The QCloudNoiseModel class defines a noise model with separate parameters for single-qubit and two-qubit gates.
7.1 Creating a Noise Model
from pyqpanda3.qcloud import QCloudNoiseModel, NOISE_MODEL
# Default constructor -- no noise (disabled)
clean_model = QCloudNoiseModel()
print(f"Clean model enabled: {clean_model.is_enabled()}")
# Output: Clean model enabled: False
# Construct with a specific noise model and parameters
noisy_model = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001], # single-qubit gate error rate
[0.01] # two-qubit gate error rate
)
print(f"Noisy model enabled: {noisy_model.is_enabled()}")
# Output: Noisy model enabled: TrueThe constructor takes three arguments:
| Parameter | Type | Description |
|---|---|---|
model | NOISE_MODEL | The type of noise channel |
single_p | list[float] | Noise parameters for single-qubit gates |
double_p | list[float] | Noise parameters for two-qubit gates |
7.2 Inspecting and Modifying Parameters
from pyqpanda3.qcloud import QCloudNoiseModel, NOISE_MODEL
noise = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001],
[0.01]
)
# Get the noise model type
model_type = noise.get_noise_model()
print(f"Noise model: {model_type}")
# Get current parameters
single_params = noise.get_single_params()
double_params = noise.get_double_params()
print(f"Single-qubit params: {single_params}")
print(f"Two-qubit params: {double_params}")
# Update parameters
noise.set_single_params([0.002])
noise.set_double_params([0.02])
print(f"Updated single params: {noise.get_single_params()}")
# Print a human-readable summary
noise.print()7.3 Comparing Noise Models
Two noise models can be compared for equality and inequality:
from pyqpanda3.qcloud import QCloudNoiseModel, NOISE_MODEL
model_a = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001], [0.01]
)
model_b = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001], [0.01]
)
model_c = QCloudNoiseModel(
NOISE_MODEL.BITFLIP_KRAUS_OPERATOR,
[0.005], [0.05]
)
print(f"model_a == model_b: {model_a == model_b}") # True
print(f"model_a != model_c: {model_a != model_c}") # True8. Enums Reference
The qcloud module defines several enumerations that control job behavior and data formatting.
8.1 JobStatus
Represents the lifecycle state of a cloud job.
| Value | Description |
|---|---|
JobStatus.WAITING | The job is waiting to be processed |
JobStatus.COMPUTING | The job is actively executing |
JobStatus.FINISHED | The job completed successfully |
JobStatus.FAILED | The job failed with an error |
JobStatus.QUEUING | The job is in the submission queue |
from pyqpanda3.qcloud import JobStatus
# Status values can be compared directly
status = job.status()
if status == JobStatus.FINISHED:
result = job.result()8.2 DataFormat
Specifies how the quantum circuit is encoded when sent to the cloud.
| Value | Description |
|---|---|
DataFormat.DEFAULT | Default format, depends on backend implementation |
DataFormat.BINARY | Compact binary format for efficient transmission |
DataFormat.INSTRUCTION_SET | Explicit gate instruction sequence |
8.3 DataBase
Controls how measurement outcomes are represented in the result.
| Value | Description |
|---|---|
DataBase.Binary | Binary representation (e.g., "00", "11") |
DataBase.Hex | Hexadecimal representation (e.g., "0x0", "0x3") |
from pyqpanda3.qcloud import DataBase
result = job.result()
# Binary representation (human-readable)
probs_bin = result.get_probs(base=DataBase.Binary)
# {'00': 0.4987, '11': 0.5013}
# Hexadecimal representation (compact for many qubits)
probs_hex = result.get_probs(base=DataBase.Hex)
# {'0x0': 0.4987, '0x3': 0.5013}For circuits with many qubits, hex representation is more compact. A 10-qubit outcome that takes 10 characters in binary ("0010110101") takes only 4 characters in hex ("0xB5").
8.4 NOISE_MODEL
Defines the type of noise channel for cloud noise simulation.
| Value | Description |
|---|---|
NOISE_MODEL.BITFLIP_KRAUS_OPERATOR | Random X (bit-flip) errors |
NOISE_MODEL.BIT_PHASE_FLIP_OPERATOR | Combined bit and phase flip errors |
NOISE_MODEL.DAMPING_KRAUS_OPERATOR | Amplitude damping (T1 decay) |
NOISE_MODEL.DECOHERENCE_KRAUS_OPERATOR | Combined T1 and T2 decoherence |
NOISE_MODEL.DEPHASING_KRAUS_OPERATOR | Phase damping (T2 decay) |
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR | Uniform depolarizing noise |
NOISE_MODEL.PHASE_DAMPING_OPERATOR | Pure dephasing errors |
from pyqpanda3.qcloud import NOISE_MODEL, QCloudNoiseModel
# Common choice: depolarizing noise (uniform error model)
noise = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001], # 0.1% error on single-qubit gates
[0.01] # 1% error on two-qubit gates
)
# More realistic: decoherence model (T1 + T2)
decoherence_noise = QCloudNoiseModel(
NOISE_MODEL.DECOHERENCE_KRAUS_OPERATOR,
[0.0005], # single-qubit T1/T2 parameters
[0.005] # two-qubit T1/T2 parameters
)8.5 LogLevel and LogOutput
Control the verbosity and destination of cloud service logs.
LogLevel:
| Value | Description |
|---|---|
LogLevel.CLOUD_INFO | Informational messages |
LogLevel.CLOUD_DEBUG | Detailed debugging messages |
LogLevel.CLOUD_WARNING | Warning messages |
LogLevel.CLOUD_ERROR | Error messages only |
LogOutput:
| Value | Description |
|---|---|
LogOutput.CONSOLE | Print logs to standard output |
LogOutput.FILE | Write logs to a file |
from pyqpanda3.qcloud import QCloudService, LogOutput
service = QCloudService("your_api_key_here")
# Console logging for development
service.setup_logging(output=LogOutput.CONSOLE)
# File logging for production
service.setup_logging(output=LogOutput.FILE, file_path="/var/log/qcloud.log")9. Practical Workflow
This section walks through complete end-to-end examples of using the cloud service.
9.1 Step-by-Step Bell State on Cloud
Here is the complete workflow for running a Bell state on a cloud simulator, from initialization to result analysis.
import os
from pyqpanda3 import core
from pyqpanda3.qcloud import (
QCloudService, QCloudOptions,
LogOutput, JobStatus, DataBase
)
# ------------------------------------------------
# Step 1: Authenticate with the cloud platform
# ------------------------------------------------
api_key = os.environ["QPANDA3_API_KEY"]
service = QCloudService(api_key)
service.setup_logging(output=LogOutput.CONSOLE)
# ------------------------------------------------
# Step 2: Discover available backends
# ------------------------------------------------
backends = service.backends()
print(f"Available backends: {backends}")
# ------------------------------------------------
# Step 3: Select a backend
# ------------------------------------------------
# Use the full-amplitude simulator for this example
backend = service.backend("full_amplitude")
# ------------------------------------------------
# Step 4: Build the quantum circuit
# ------------------------------------------------
prog = core.QProg()
prog << core.H(0) # Superposition on qubit 0
prog << core.CNOT(0, 1) # Entangle qubits 0 and 1
prog << core.measure([0, 1], [0, 1]) # Measure both qubits
print("Circuit:")
print(prog)
# ------------------------------------------------
# Step 5: Submit the job
# ------------------------------------------------
# Note: full_amplitude simulator uses run(prog, shots) or run(prog, shots, model)
# QCloudOptions is only for real QPU backends
job = backend.run(prog, shots=3000)
job_id = job.job_id()
print(f"Job submitted. ID: {job_id}")
# ------------------------------------------------
# Step 6: Monitor job status
# ------------------------------------------------
import time
while True:
status = job.status()
print(f" Status: {status}")
if status == JobStatus.FINISHED:
break
elif status == JobStatus.FAILED:
print("Job failed!")
result = job.result()
print(f"Error: {result.error_message()}")
exit(1)
time.sleep(2) # Wait before polling again
# ------------------------------------------------
# Step 7: Retrieve and analyze results
# ------------------------------------------------
result = job.result()
# Get probabilities
probs = result.get_probs(base=DataBase.Binary)
print("\nProbabilities:")
for state, prob in sorted(probs.items()):
print(f" |{state}>: {prob:.4f}")
# Get raw counts
counts = result.get_counts(base=DataBase.Binary)
print("\nMeasurement counts:")
for state, count in sorted(counts.items()):
print(f" |{state}>: {count}")
# Expected output for Bell state:
# |00>: ~0.50
# |11>: ~0.50
# (01 and 10 should be absent or near zero)9.2 Monitoring a Long-Running Job
When submitting to a real QPU, jobs may spend considerable time in the queue. Here is a robust monitoring pattern:
import time
from pyqpanda3.qcloud import QCloudJob, JobStatus
# Reconstruct job reference from a saved ID
saved_job_id = "ABC123DEF456"
job = QCloudJob(saved_job_id)
# Poll with exponential backoff
wait_times = [2, 4, 8, 15, 30, 60] # seconds
attempt = 0
while True:
status = job.status()
print(f"[{time.strftime('%H:%M:%S')}] Job {saved_job_id}: {status}")
if status == JobStatus.FINISHED:
result = job.result()
counts = result.get_counts()
print(f"Final counts: {counts}")
break
if status == JobStatus.FAILED:
result = job.result()
print(f"Job failed: {result.error_message()}")
break
# Wait with increasing delay
wait = wait_times[min(attempt, len(wait_times) - 1)]
attempt += 1
time.sleep(wait)9.3 Cloud Computing Workflow Diagram
The following diagram shows the complete workflow from authentication to result retrieval:
9.4 Job Lifecycle Diagram
This diagram shows the state transitions a job undergoes from submission to completion:
10. Advanced Patterns
10.1 Batch Job Submission
You can submit multiple quantum programs in a single batch, which is more efficient than submitting them one at a time:
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService, QCloudOptions
service = QCloudService("your_api_key_here")
backend = service.backend("origin_wukong")
# Build multiple circuits
progs = []
for angle_idx in range(4):
prog = core.QProg()
prog << core.H(0)
prog << core.RZ(1, angle_idx * 3.14159 / 4)
prog << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
progs.append(prog)
# Submit all at once
options = QCloudOptions()
options.set_amend(True)
job = backend.run(progs, shots=2000, options=options)
# Get results for all circuits
result = job.result()
probs_list = result.get_probs_list()
for i, probs in enumerate(probs_list):
print(f"Circuit {i}: {probs}")10.2 Transpiling Before Cloud Submission
When targeting a real QPU, the circuit must be compatible with the chip's native gate set and qubit connectivity. While the cloud server can handle this automatically via QCloudOptions.set_mapping(True), you may want to transpile locally for more control:
import time
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService, QCloudOptions
from pyqpanda3.transpilation import Transpiler
service = QCloudService("your_api_key_here")
backend = service.backend("origin_wukong")
# Get the chip's physical configuration
chip_info = backend.chip_info()
chip_backend = chip_info.get_chip_backend()
# Build a circuit
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.CNOT(1, 2)
prog << core.SWAP(0, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])
# Transpile to the chip's native gate set and topology
transpiler = Transpiler()
basic_gates = chip_info.get_basic_gates() # e.g., ["U3", "CZ"]
topo = chip_info.get_chip_topology()
start = time.time()
transpiled_prog = transpiler.transpile(
prog,
chip_backend,
{}, # compensate angle map
2 # optimization level
)
elapsed = (time.time() - start) * 1000
print(f"Transpilation took {elapsed:.2f} ms")
# Convert to instruction format for submission
instruction_json = transpiled_prog.to_instruction(chip_backend)
# Submit the transpiled instruction directly
from pyqpanda3.qcloud import QCloudOptions
options = QCloudOptions()
options.set_amend(False) # Already transpiled, no need for server-side mapping
job = backend.run_instruction(instruction_json, 3000, options)
result = job.result()
print(result.get_probs())10.3 Partial Amplitude and State Tomography
The cloud platform supports specialized execution modes beyond standard circuit simulation.
Partial amplitude computation -- Compute only specific amplitudes of the statevector:
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService
service = QCloudService("your_api_key_here")
backend = service.backend("partial_amplitude")
prog = core.QProg()
prog << core.H(0)
prog << core.H(1)
prog << core.H(2)
prog << core.CNOT(0, 1)
prog << core.CNOT(1, 2)
# Request specific amplitudes
job = backend.run(prog, ["000", "111"])
result = job.result()
amplitudes = result.get_amplitudes()
print(f"Amplitudes: {amplitudes}")State tomography -- Reconstruct the full density matrix from measurements (get_state_tomography_density() returns list[list[complex]]):
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService, QCloudOptions
service = QCloudService("your_api_key_here")
backend = service.backend("origin_wukong")
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
# Run state tomography
options = QCloudOptions()
job = backend.run_quantum_state_tomography(prog, 5000, options)
result = job.result()
fidelity = result.get_state_fidelity()
density = result.get_state_tomography_density()
print(f"State fidelity: {fidelity}")
print(f"Density matrix ({len(density)}x{len(density[0])}):")
for row in density:
print(row)11. Summary
This tutorial covered the complete cloud computing workflow in pyqpanda3:
| Component | Purpose | Key Methods |
|---|---|---|
QCloudService | Authenticate and discover backends | .backends() / .backend() / .setup_logging() |
QCloudBackend | Execute circuits and query chip info | .run() / .chip_info() / .expval_hamiltonian() |
QCloudJob | Track and retrieve job status | .status() / .result() / .query() / .job_id() |
QCloudResult | Access measurement data | .get_probs() / .get_counts() / .origin_data() |
QCloudOptions | Configure compilation and execution | .set_amend() / .set_mapping() / .set_optimization() |
QCloudNoiseModel | Define noise for cloud simulators | Constructor with NOISE_MODEL and parameters |
Key takeaways:
- Authenticate first -- Create a
QCloudServicewith your API key before doing anything else. - Choose the right backend -- Use cloud simulators for development and verification; use real QPUs when you need actual quantum hardware.
- Jobs are asynchronous -- After submission, poll
job.status()untilFINISHEDorFAILED. - Results have multiple formats -- Use
DataBase.Binaryfor human-readable output andDataBase.Hexfor compact representation. - Noise models are for simulators only -- Real hardware has inherent noise; software noise models apply only to cloud simulators.
- Transpile locally for more control -- Use the
Transpilerclass andChipInfowhen you need fine-grained control over circuit compilation.
Next steps:
- Transpilation -- Learn how to optimize circuits for specific hardware topologies
- Noise Simulation -- Understand noise channels and their effects on circuits
- Hamiltonian & Pauli Operators -- Use expectation values for variational algorithms
- API Reference: qcloud -- Complete API documentation for the cloud module
Quick Reference
from pyqpanda3 import core
from pyqpanda3.qcloud import (
QCloudService, QCloudJob, QCloudOptions,
QCloudNoiseModel, QCloudResult,
JobStatus, DataBase, NOISE_MODEL,
LogOutput, LogLevel,
)
import os
# ===========================
# Authentication
# ===========================
service = QCloudService(os.environ["QPANDA3_API_KEY"])
service.setup_logging(output=LogOutput.CONSOLE)
# ===========================
# Backend Discovery
# ===========================
backends = service.backends() # dict[str, bool] mapping names to availability
qpu_backend = service.backend("origin_wukong") # Real QPU
sim_backend = service.backend("full_amplitude") # Cloud simulator
# ===========================
# Chip Information (QPU only)
# ===========================
chip_info = qpu_backend.chip_info()
print(f"Qubits: {chip_info.qubits_num()}")
print(f"Available: {chip_info.available_qubits()}")
print(f"Topology: {chip_info.get_chip_topology()}")
# ===========================
# Submit a Job
# ===========================
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
# Simulator: run(prog, shots) or run(prog, shots, model)
job = sim_backend.run(prog, shots=3000)
# QPU: run(prog, shots, options)
options = QCloudOptions()
options.set_amend(True)
options.set_mapping(True)
job = qpu_backend.run(prog, shots=3000, options=options)
# Simulator with noise model
noise = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001], [0.01]
)
job = sim_backend.run(prog, shots=3000, model=noise)
# ===========================
# Monitor Job
# ===========================
job_id = job.job_id()
status = job.status()
# Reconstruct from saved job ID
job = QCloudJob(job_id)
# ===========================
# Get Results
# ===========================
result = job.result()
probs = result.get_probs(base=DataBase.Binary)
counts = result.get_counts(base=DataBase.Binary)
raw = result.origin_data()
# ===========================
# Expectation Values
# ===========================
from pyqpanda3.hamiltonian import Hamiltonian
h = Hamiltonian({"X0 Y1": 1.0, "Z0 Z1": 0.5})
expval = qpu_backend.expval_hamiltonian(prog, h, QCloudOptions())
# ===========================
# Noise Model
# ===========================
noise = QCloudNoiseModel(
NOISE_MODEL.DEPOLARIZING_KRAUS_OPERATOR,
[0.001], [0.01]
)
noise.is_enabled() # True
noise.get_noise_model() # DEPOLARIZING_KRAUS_OPERATOR
noise.get_single_params() # [0.001]
noise.get_double_params() # [0.01]
noise.set_single_params([0.002])
noise.print()