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.
graph TD
USER["Your Python Code<br/>pyqpanda3.qcloud"] --> SERVICE["QCloudService<br/>API Authentication"]
SERVICE --> BACKENDS["Available Backends"]
BACKENDS --> QPU["Real QPUs<br/>WK_C180_2, PQPUMESH8, ..."]
BACKENDS --> SIM["Cloud Simulators<br/>full_amplitude, partial_amplitude"]
BACKENDS --> SPEC["Specialized<br/>state_tomography, expectation"]
QPU --> JOB["QCloudJob<br/>Submitted to Queue"]
SIM --> JOB
SPEC --> JOB
JOB --> RESULT["QCloudResult<br/>Probabilities, Counts"]
style USER fill:#4a90d9,color:#fff
style SERVICE fill:#7b68ee,color:#fff
style BACKENDS fill:#50c878,color:#fff
style JOB fill:#e8913a,color:#fff
style RESULT fill:#c0392b,color:#fff2. 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 single-argument constructor QCloudService(api_key) resolves the service domain from a new-format API key; legacy keys fall back to the historical default endpoint. For testing or private deployments, pass QCloudService(api_key, url=...) to override it explicitly.
from pyqpanda3.qcloud import QCloudService
# New-format key: the service domain is resolved from the key
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["QPANDA_QCLOUD_API_KEY"]
service = QCloudService(api_key)Then set the environment variable before running your script:
export QPANDA_QCLOUD_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
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:
- WK_C180_2: available
- PQPUMESH8: available
- full_amplitude: available
- partial_amplitude: availableThe 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("WK_C180_2")
# 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("WK_C180_2")
# 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("WK_C180_2")
# Configure execution options
options = QCloudOptions()
options.set_amend(True) # Enable amendment (error mitigation)
options.set_mapping(True) # Enable qubit mapping to physical qubits
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("WK_C180_2")
# 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("WK_C180_2")
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 |
stateDiagram-v2
[*] --> QUEUING: Submit job
QUEUING --> WAITING: Dequeue
WAITING --> COMPUTING: Resource assigned
COMPUTING --> FINISHED: Execution complete
COMPUTING --> FAILED: Execution error
WAITING --> FAILED: Resource error
QUEUING --> FAILED: Submission error
FINISHED --> [*]: Retrieve results
FAILED --> [*]: Check error message4.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}Note: on a real QPU backend, the base argument controls the key format as shown above (DataBase.Binary → '00', DataBase.Hex → '0x0'). On the full_amplitude cloud simulator, results are probability-type and always returned with hexadecimal keys regardless of base, and get_counts() returns {} (read outcomes via get_probs() instead).
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:
Important: for probability-type results (the default, is_prob_counts=True), get_counts() returns an empty dictionary {}. The count examples below show the shape of a raw-counts result only -- to obtain raw counts on a real QPU you must request them explicitly via QCloudOptions.set_is_prob_counts(False) (cloud simulators do not accept QCloudOptions and raise RuntimeError: Run with QCloudOptions is only for QPU). For probability-type results, read probabilities with get_probs() in the previous section.
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 (raw-counts result): {'00': 1496, '11': 1504}
# Note: with default is_prob_counts=True this prints {}
# Counts as hexadecimal strings
counts_hex = result.get_counts(base=DataBase.Hex)
print("Counts (hex):", counts_hex)
# Example (raw-counts result): {'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()Example output (default values):
is_prob_counts: true
amend_flag: 1
use_cz_pattern: false
mapping: true
optimization: true
point_label: 16.2 Compilation Options
When submitting to a real QPU, the cloud server performs several compilation steps. You can configure each one through QCloudOptions. All options have sensible defaults that work well for most cases:
| Option | Default | Description |
|---|---|---|
amend | True | Legacy boolean switch, equivalent to amend_flag 0/1 (error mitigation based on calibration data) |
amend_flag | 1 | Correction mode: 0 off, 1 global sampling correction, 3 local real-time correction |
use_cz_pattern | False | Crosstalk noise suppression |
mapping | True | Logical-to-physical qubit mapping based on chip topology |
optimization | True | Circuit simplification (gate cancellation, decomposition) |
point_label | 1 | Calibration point level identifier |
specified_block | [] | Restrict execution to a specific block (empty = no restriction) |
is_prob_counts | True | Output format: probabilities vs raw counts |
You only need to call the setters if you want to change a value from its default:
from pyqpanda3.qcloud import QCloudOptions
options = QCloudOptions()
# Disable amendment (enabled by default)
options.set_amend(False)
# Specify calibration point (default is 1)
options.set_point_label(0)
# Restrict execution to a specific block on the chip
options.set_specified_block([1])
# Output raw counts instead of probabilities (default is True for probabilities)
options.set_is_prob_counts(False)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 positional-only 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 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"). On a real QPU, base selects this binary/hex format as expected; on the full_amplitude simulator the keys are always hexadecimal regardless of base.
8.3 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)
# Note: DECOHERENCE_KRAUS_OPERATOR requires exactly 3 parameters each
# (T1 relaxation time, T2 dephasing time, gate time)
decoherence_noise = QCloudNoiseModel(
NOISE_MODEL.DECOHERENCE_KRAUS_OPERATOR,
[0.0005, 0.0, 0.0], # single-qubit: T1, T2, gate time
[0.005, 0.0, 0.0] # two-qubit: T1, T2, gate time
)8.4 LogOutput
Controls the destination of cloud service log output.
| 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,
LogOutput, JobStatus, DataBase
)
# ------------------------------------------------
# Step 1: Authenticate with the cloud platform
# ------------------------------------------------
api_key = os.environ["QPANDA_QCLOUD_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 probabilities for Bell state (hex keys):
# 0x0: ~0.50 (|00>)
# 0x3: ~0.50 (|11>)
# Counts on the full_amplitude simulator print {} because
# the default is_prob_counts=True returns probabilities only.This example uses the full_amplitude simulator, where run() returns probability-type results with hexadecimal keys (0x0, 0x3). The probability loop prints the Bell-state distribution; the counts loop prints nothing because get_counts() is empty under the default is_prob_counts=True. To obtain raw counts instead, submit to a real QPU with QCloudOptions.set_is_prob_counts(False) (simulators reject QCloudOptions with RuntimeError: Run with QCloudOptions is only for QPU).
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:
flowchart TD
START["Start"] --> AUTH["1. Create QCloudService<br/>with API key"]
AUTH --> LOG["2. Optional: setup_logging()"]
LOG --> LIST["3. List backends<br/>service.backends()"]
LIST --> SELECT["4. Select backend<br/>service.backend(name)"]
SELECT --> BUILD["5. Build QProg<br/>with gates and measurement"]
BUILD --> OPTIONS["6. Configure QCloudOptions<br/>(optional)"]
OPTIONS --> SUBMIT["7. Submit job<br/>backend.run(prog, shots)"]
SUBMIT --> POLL{"8. Check status<br/>job.status()"}
POLL -->|QUEUING/WAITING| WAIT["Wait 2-60 seconds"]
WAIT --> POLL
POLL -->|COMPUTING| WAIT2["Wait 2-30 seconds"]
WAIT2 --> POLL
POLL -->|FINISHED| RETRIEVE["9. Get results<br/>job.result()"]
POLL -->|FAILED| ERROR["Check error<br/>result.error_message()"]
RETRIEVE --> ANALYZE["10. Analyze<br/>get_probs() / get_counts()"]
ERROR --> FIX["Fix circuit or options"]
FIX --> BUILD
ANALYZE --> DONE["Done"]
style START fill:#4a90d9,color:#fff
style SUBMIT fill:#e8913a,color:#fff
style RETRIEVE fill:#50c878,color:#fff
style DONE fill:#50c878,color:#fff
style ERROR fill:#c0392b,color:#fff9.4 Job Lifecycle Diagram
This diagram shows the state transitions a job undergoes from submission to completion:
flowchart LR
SUBMIT["Submit Job"] --> QUEUING["QUEUING<br/>Job enters queue"]
QUEUING --> WAITING["WAITING<br/>Awaiting resource"]
WAITING --> COMPUTING["COMPUTING<br/>Executing on QPU/Sim"]
COMPUTING --> FINISHED["FINISHED<br/>Results available"]
QUEUING -.->|error| FAILED["FAILED"]
WAITING -.->|error| FAILED
COMPUTING -.->|error| FAILED
FINISHED --> PROBS["get_probs()"]
FINISHED --> COUNTS["get_counts()"]
FINISHED --> RAW["origin_data()"]
FAILED --> ERRMSG["error_message()"]
style SUBMIT fill:#4a90d9,color:#fff
style QUEUING fill:#f39c12,color:#fff
style WAITING fill:#f39c12,color:#fff
style COMPUTING fill:#3498db,color:#fff
style FINISHED fill:#27ae60,color:#fff
style FAILED fill:#c0392b,color:#fff10. 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("WK_C180_2")
# 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. The cloud server handles this automatically via compilation options in QCloudOptions. You can query chip information to understand hardware constraints and configure server-side compilation:
from pyqpanda3 import core
from pyqpanda3.qcloud import QCloudService, QCloudOptions
service = QCloudService("your_api_key_here")
backend = service.backend("WK_C180_2")
# Query chip information to understand hardware constraints
chip_info = backend.chip_info()
print(f"Number of qubits: {chip_info.qubits_num()}")
print(f"Available qubits: {chip_info.available_qubits()}")
print(f"Basic gates: {chip_info.get_basic_gates()}")
print(f"Topology: {chip_info.get_chip_topology()}")
# Build a circuit
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.CNOT(1, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])
# Configure server-side compilation via QCloudOptions
options = QCloudOptions()
options.set_mapping(True) # Enable automatic qubit mapping to chip topology
options.set_optimization(True) # Enable circuit optimization
options.set_amend(True) # Enable error mitigation
# Submit with compilation options -- the server handles transpilation
job = backend.run(prog, shots=3000, options=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("WK_C180_2")
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)10.4 Source-Checked Raw Fields, ChipBackend, and Instruction Submission
This section combines the pyqpanda3 0.4.0 source bindings with live QCloud metadata. QCloudJob.query() and QCloudJob.result() accept keys= or key= for extra raw fields such as mappingQprog, mappingQubit, and probCount. QCloudResult exposes helpers such as mapping_qprog(), mapping_qubit(), and prob_count_raw(), plus membership/index access. QCloudService.best_qubit_blocks() and QCloudBackend.best_qubit_blocks() are both exposed in the 0.4.0 source; when pinning qubits manually, still check the live topology first.
import os
from pyqpanda3.core import CZ, H, QProg, measure
from pyqpanda3.qcloud import QCloudOptions, QCloudService
from pyqpanda3.transpilation import Transpiler, decompose
from pyqpanda3.visualization import draw_time_scheduling
service = QCloudService(os.environ["QPANDA_QCLOUD_API_KEY"])
backend = service.backend(os.environ.get("QPANDA_QCLOUD_BACKEND", "WK_C180_2"))
chip_info = backend.chip_info()
chip_backend = backend.chip_backend([0, 10])
prog = QProg()
prog << H(0) << CZ(0, 1) << measure(0, 0) << measure(1, 1)
decomposed = decompose(prog, ["U3", "CZ"])
transpiled = Transpiler().transpile(decomposed, chip_backend, {}, 2, "fidelity")
instruction = transpiled.to_instruction(
chip_backend,
offset=1,
is_scheduling=True,
compensate_pulse=False,
)
figures = draw_time_scheduling(instruction, backend=chip_backend, output="plotly")
options = QCloudOptions()
options.set_mapping(False)
options.set_optimization(False)
options.set_amend(False)
options.set_specified_block([0, 10])
job = backend.run_instruction([instruction], shots=1000, options=options)
result = job.result(keys=["mappingQprog", "mappingQubit", "probCount"])
print(chip_backend.basic_gates)
print(chip_backend.single_gate_timing, chip_backend.double_gate_timing)
print(type(figures).__name__)
print(result.mapping_qubit() if "mappingQubit" in result else "")
print(result.timing_info())
print(result.origin_data()[:200])Example output shape:
['RPhi', 'CZ']
25 40
Figure
{"0": 10, ...}
{'totalTime': ..., 'pulseTime': ..., ...}
{"success":true,"code":10000,...This workflow uses WK_C180_2. Live QCloud metadata reported it as available, with 180 qubits, native RPhi / CZ gates, single-gate timing 25, double-gate timing 40, and topology edges beginning with [0, 9], [0, 10], [1, 10], and so on.
10.5 BestQubitBlocks — Optimal Qubit Selection
When targeting a real QPU, you often want the chip's best-performing physical qubits for your circuit. The best_qubit_blocks API queries the cloud service for optimal qubit block recommendations ranked by calibration quality.
from pyqpanda3.qcloud import QCloudService
service = QCloudService("your_api_key")
backend = service.backend("WK_C180_2")
# Query via QCloudService (needs chip_id)
blocks = service.best_qubit_blocks(
chip_id="WK_C180_2",
qubit_num=3, # qubits per block
label=1, # optimization label
qubit_block_num=2, # number of blocks to return
)
print(blocks.chip_id) # "WK_C180_2"
print(blocks.qubit_blocks) # [[73, 82, 83], [90, 99, 108]]
print(blocks[0]) # [73, 82, 83]
# Query via QCloudBackend (chip_id inferred)
blocks2 = backend.best_qubit_blocks(qubit_num=2, label=1, qubit_block_num=3)
print(blocks2.qubit_blocks) # [[59, 69], [99, 108], ...]The returned blocks can be passed directly to QCloudOptions.set_specified_block() to pin physical qubits for a job:
from pyqpanda3.qcloud import QCloudOptions
options = QCloudOptions()
options.set_mapping(False) # let the transpiler handle mapping
options.set_optimization(False) # skip server-side optimization
options.set_specified_block(blocks[0]) # pin to the best block
job = backend.run(prog, shots=1000, options=options)Combining BestQubitBlocks with Fidelity Layout
The recommended workflow chains optimal block selection into fidelity-aware transpilation:
from pyqpanda3.core import H, CZ, QProg, measure
from pyqpanda3.qcloud import QCloudService, QCloudOptions
from pyqpanda3.transpilation import Transpiler, decompose
from pyqpanda3.visualization import draw_time_scheduling
service = QCloudService("your_api_key")
backend = service.backend("WK_C180_2")
# 1. Get the best qubit block
blocks = backend.best_qubit_blocks(qubit_num=3, qubit_block_num=1)
best_block = blocks[0]
# 2. Build ChipBackend for the selected qubits
chip_backend = backend.chip_backend(best_block)
# 3. Build and transpile
prog = QProg()
prog << H(0) << CZ(0, 1) << CZ(1, 2) << measure(0, 0) << measure(1, 1) << measure(2, 2)
decomposed = decompose(prog, ["U3", "CZ"])
transpiled = Transpiler().transpile(decomposed, chip_backend, {}, 2, "fidelity")
# 4. Export and visualize
instruction = transpiled.to_instruction(
chip_backend, offset=1, is_scheduling=True, compensate_pulse=False
)
fig = draw_time_scheduling(instruction, backend=chip_backend, title="Best-Block Schedule")
fig.savefig("best_block_schedule.png", dpi=150)
# 5. Submit to QPU
options = QCloudOptions()
options.set_mapping(False)
options.set_optimization(False)
options.set_amend(False)
options.set_specified_block(best_block)
job = backend.run_instruction([instruction], shots=2000, options=options)
result = job.result()
print(result.get_probs())This end-to-end pattern ensures that qubit selection, transpilation, timing, and submission all reference the same optimal physical qubits.
10.6 OriginIR → Communication Protocol Encoding & Instruction Recovery
The previous sections used to_instruction(is_scheduling=True) to export a transpiled program to an instruction JSON, then run_instruction() to submit it. But the real hardware path has two more common "serialization" routes, introduced in 0.4.0 and frequently used together with batch cloud submission.
OriginIR string / file → binary communication protocol
comm_protocol_encode_originir_string() and comm_protocol_encode_originir_file() skip building a QProg and encode a piece of OriginIR text (or a file) directly into the binary communication protocol byte stream used by the quantum cloud. Configuration is controlled by CommProtocolConfig:
from pyqpanda3.utils import CommProtocolConfig, comm_protocol_encode_originir_string
config = CommProtocolConfig()
config.shots = 2048
config.open_mapping = True
config.optimization_level = 4
originir = "QINIT 1\nCREG 1\nH q[0]\nMEASURE q[0],c[0]\n"
payload = comm_protocol_encode_originir_string(originir, config)
print(type(payload).__name__)
print(len(payload) > 0)Output:
bytes
True| Field | Default | Meaning |
|---|---|---|
shots | 1000 | Number of shots. |
circuits_num | 100 | Number of circuits in a batch. |
open_mapping | True | Whether to enable mapping. |
open_error_mitigation | True | Whether to enable error mitigation. |
optimization_level | 4 | Cloud-side compile optimization level. |
If you already hold a QProg object, the equivalent is to encode it with comm_protocol_encode([prog], config) — it produces the same byte stream as the OriginIR direct encoding above:
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog
from pyqpanda3.utils import CommProtocolConfig, comm_protocol_encode
originir = "QINIT 1\nCREG 1\nH q[0]\nMEASURE q[0],c[0]\n"
config = CommProtocolConfig()
config.shots = 2048
prog = convert_originir_string_to_qprog(originir)
payload_from_qprog = comm_protocol_encode([prog], config)
print(type(payload_from_qprog).__name__)
print(len(payload_from_qprog) > 0)Output:
bytes
TrueInstruction JSON → QProg recovery
The inverse of to_instruction() is instruction_to_qprog(): it turns an instruction JSON back into a local QProg. The convertQProg field returned by the cloud (requested with result(keys=["convertQProg"]) and read via QCloudResult.instructions()) is exactly such an instruction JSON:
from pyqpanda3.core import instruction_to_qprog
# Suppose result is from a run_instruction + result(keys=["convertQProg"]) call
# recovered = instruction_to_qprog(result.instructions(), qubit_offset=1)
# print(type(recovered).__name__) # QProgFor multi-stream instruction JSON (a JSON array containing several instruction streams), instruction_to_qprog() handles it too — when given a multi-stream payload it returns a list[QProg], one program per stream (a single stream returns a single QProg). See the Circuit API reference.
11. Summary
This tutorial covered the complete cloud computing workflow in pyqpanda3:
| Component | Purpose | Key Methods |
|---|---|---|
QCloudService | Authenticate and discover backends | .backends() / .backend() / .best_qubit_blocks() / .setup_logging() |
QCloudBackend | Execute circuits and query chip info | .run() / .chip_info() / .best_qubit_blocks() / .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.
- Control server-side compilation via QCloudOptions -- Use
set_mapping(),set_optimization(), andset_amend()to configure how the cloud server compiles your circuit for the target chip. - Select optimal qubits with BestQubitBlocks -- Query
best_qubit_blocks()for calibration-ranked physical qubit blocks, then pin them withQCloudOptions.set_specified_block().
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, BestQubitBlocks,
DataBase, NOISE_MODEL,
LogOutput,
)
import os
# ===========================
# Authentication
# ===========================
service = QCloudService(os.environ["QPANDA_QCLOUD_API_KEY"])
service.setup_logging(output=LogOutput.CONSOLE)
# ===========================
# Backend Discovery
# ===========================
backends = service.backends() # dict[str, bool] mapping names to availability
qpu_backend = service.backend("WK_C180_2") # 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()}")
# ===========================
# Optimal Qubit Selection
# ===========================
# Via QCloudService
blocks = service.best_qubit_blocks("WK_C180_2", qubit_num=3, qubit_block_num=1)
best_block = blocks[0] # e.g., [73, 82, 83]
# Via QCloudBackend (chip_id inferred)
blocks = qpu_backend.best_qubit_blocks(qubit_num=2, qubit_block_num=2)
print(blocks.qubit_blocks)
# ===========================
# 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)
# Note: run() returns probability-type results by default (is_prob_counts=True),
# so get_counts() returns {} unless you set is_prob_counts=False on a real QPU.
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()