Quantum Computing Basics
This tutorial introduces the fundamental building blocks of quantum computing: qubits, quantum gates, and measurement. You will learn the mathematical foundations and how to apply them using pyqpanda3.
Prerequisites: Getting Started -- ensure pyqpanda3 is installed and working.
Table of Contents
1. What is a Qubit?
1.1 Classical Bit vs Qubit
A classical bit can be in one of two states: 0 or 1. A quantum bit (qubit) also has two basis states, written in Dirac notation as
| Property | Classical Bit | Qubit |
|---|---|---|
| States | 0 or 1 | |
| Notation | Binary digit | Ket vector |
| Simultaneous states | One at a time | Both at once (superposition) |
| Measurement | Does not change state | Collapses to basis state |
| Representation | Voltage level | Vector in Hilbert space |
1.2 Superposition
A qubit in an arbitrary state is described by:
where
The probability of measuring the qubit in state
In column vector form, the basis states are:
So a general qubit state can be written as:
The key insight is that the qubit holds both possibilities simultaneously until measured. This is not the same as "being in an unknown state" -- the amplitudes
from pyqpanda3 import core
# The |0> state is the default when a qubit is allocated
# Let's verify by measuring a fresh qubit
prog = core.QProg()
prog << core.measure(0, 0)
machine = core.CPUQVM()
machine.run(prog, shots=1000)
counts = machine.result().get_counts()
print(counts)
# Output: {'0': 1000} -- always |0> since no gates were applied运行结果:
{'0': 1000}1.3 The Bloch Sphere
Any single-qubit pure state can be parameterized by two angles
where
Key points on the Bloch sphere:
- North pole (
): - South pole (
): - Positive X (
): - Negative X (
): - Positive Y (
): - Negative Y (
):
graph TB
subgraph "Bloch Sphere Concept"
B["Bloch Sphere<br/>Unit sphere in 3D"]
N["North Pole: |0⟩<br/>(θ = 0)"]
S["South Pole: |1⟩<br/>(θ = π)"]
PX["+X axis: |+⟩<br/>(θ = π/2, φ = 0)"]
NX["-X axis: |-⟩<br/>(θ = π/2, φ = π)"]
PY["+Y axis: |+i⟩<br/>(θ = π/2, φ = π/2)"]
NY["-Y axis: |-i⟩<br/>(θ = π/2, φ = 3π/2)"]
P["Any point on surface<br/>represents a pure qubit state"]
B --- N
B --- S
B --- PX
B --- NX
B --- PY
B --- NY
B --- P
endThe Bloch vector
pyqpanda3 provides Bloch sphere visualization through the visualization module:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from unittest.mock import patch
# pyqpanda3's bloch_plot module calls plt.switch_backend('TKAgg') at import time,
# which requires an interactive display. We patch it to a no-op since Agg is sufficient.
# Alternatively, set the MPLBACKEND environment variable before running:
# import os; os.environ['MPLBACKEND'] = 'Agg'
with patch('matplotlib.pyplot.switch_backend'):
from pyqpanda3.visualization import plot_bloch_vector
# Plot the |+> state (pointing along +X axis)
# Bloch vector: [sin(pi/2)*cos(0), sin(pi/2)*sin(0), cos(pi/2)] = [1, 0, 0]
fig = plot_bloch_vector([1, 0, 0], title="|+> state")
print('Bloch sphere figure created successfully')
print(f'Figure type: {type(fig).__name__}')
plt.close(fig)运行结果:
Bloch sphere figure created successfully
Figure type: Figure2. Quantum Gates
2.1 Unitary Transformations
Quantum gates are unitary transformations that evolve qubit states. A matrix
where
- The total probability is preserved (
) - The gate is reversible (every gate has an inverse)
When a gate
For multi-qubit systems, gates acting on specific qubits are represented by the tensor product of the gate matrix with identity matrices on the other qubits.
2.2 Single-Qubit Gates
pyqpanda3 provides the following single-qubit gates with no parameters:
Pauli X Gate (NOT Gate)
The X gate flips the computational basis states, analogous to a classical NOT gate.
On the Bloch sphere, X is a
Pauli Y Gate
On the Bloch sphere, Y is a
Pauli Z Gate
The Z gate applies a phase flip to
Hadamard Gate (H)
The Hadamard gate creates equal superpositions:
This is the most commonly used gate for creating superposition. Applying H twice returns to the original state:
S Gate (Phase Gate)
The S gate is the square root of Z. It applies a
T Gate ( Gate)
The T gate is the fourth root of Z. It is non-Clifford and essential for universal quantum computation.
I Gate (Identity)
The identity gate leaves the state unchanged. It is useful for padding circuits or as a placeholder.
from pyqpanda3 import core
# Apply single-qubit gates to qubit 0
prog = core.QProg()
prog << core.H(0) # Hadamard on qubit 0
prog << core.X(0) # Pauli-X on qubit 0
prog << core.Y(0) # Pauli-Y on qubit 0
prog << core.Z(0) # Pauli-Z on qubit 0
prog << core.S(0) # S gate on qubit 0
prog << core.T(0) # T gate on qubit 0
prog << core.I(0) # Identity on qubit 0
print(prog)运行结果:
┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
q_0: |0>─┤H├ ┤X├ ┤Y├ ┤Z├ ┤S├ ┤T├ ┤I├
└─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘
c : / ════════════════════════════Summary of Non-Parameterized Single-Qubit Gates
| Gate | Matrix | Effect on | Effect on |
|---|---|---|---|
| X | |||
| Y | |||
| Z | |||
| H | |||
| S | |||
| T | |||
| I |
2.3 Rotation Gates
Rotation gates are parameterized by an angle
RX Gate (Rotation around X axis)
RY Gate (Rotation around Y axis)
RZ Gate (Rotation around Z axis)
P Gate (Phase Gate)
Note that
import math
from pyqpanda3 import core
# Rotation gates take (qubit_index, angle_in_radians)
prog = core.QProg()
prog << core.RX(0, math.pi / 4) # Rotate pi/4 around X
prog << core.RY(1, math.pi / 2) # Rotate pi/2 around Y
prog << core.RZ(2, math.pi) # Rotate pi around Z (= Z gate up to phase)
prog << core.P(0, math.pi / 4) # Phase gate with angle pi/4 (= T gate)
# Verify that RX(pi) behaves like X (up to global phase)
# RX(pi) = [[0, -i], [-i, 0]] -- multiplies by -i relative to X
prog_rx = core.QProg()
prog_rx << core.RX(0, math.pi)
prog_rx << core.measure(0, 0)
machine = core.CPUQVM()
machine.run(prog_rx, shots=1000)
print(machine.result().get_counts())
# Output: {'1': 1000} -- same measurement statistics as X|0>运行结果:
{'1': 1000}2.4 Two-Qubit Gates
Two-qubit gates act on pairs of qubits and can create entanglement, a uniquely quantum phenomenon where the states of two qubits become correlated in a way that cannot be described classically.
CNOT Gate (Controlled-NOT)
The most fundamental two-qubit gate. It flips the target qubit if and only if the control qubit is
Basis state action (first qubit is control, second is target):
In pyqpanda3: core.CNOT(control, target).
CZ Gate (Controlled-Z)
Applies a Z gate to the target qubit if the control is
In pyqpanda3: core.CZ(qubit1, qubit2).
SWAP Gate
Exchanges the states of two qubits.
In pyqpanda3: core.SWAP(qubit1, qubit2).
Parameterized Two-Qubit Gates
pyqpanda3 also provides parameterized two-qubit gates:
| Gate | Syntax | Description |
|---|---|---|
| CRX | core.CRX(control, target, theta) | Controlled RX rotation |
| CRY | core.CRY(control, target, theta) | Controlled RY rotation |
| CRZ | core.CRZ(control, target, theta) | Controlled RZ rotation |
| CP | core.CP(control, target, theta) | Controlled phase |
| CU | core.CU(control, target, theta, phi, lam, gamma) | General controlled unitary |
| RXX | core.RXX(q1, q2, theta) | XX interaction |
| RYY | core.RYY(q1, q2, theta) | YY interaction |
| RZZ | core.RZZ(q1, q2, theta) | ZZ interaction |
| RZX | core.RZX(q1, q2, theta) | ZX interaction |
from pyqpanda3 import core
# Two-qubit gate examples
prog = core.QProg()
prog << core.CNOT(0, 1) # CNOT: control=0, target=1
prog << core.CZ(1, 2) # CZ between qubits 1 and 2
prog << core.SWAP(0, 1) # SWAP qubits 0 and 1
# Parameterized two-qubit gates
import math
prog << core.CRX(0, 1, math.pi / 4) # Controlled-RX with angle pi/4
prog << core.RZZ(0, 1, math.pi / 2) # ZZ rotation with angle pi/2
print(prog)运行结果:
┌───────────────────┐
q_0: |0>────*── ──── X ────────*─────── ┤0 ├
┌──┴─┐ │ ┌───────┴──────┐ │ RZZ(1.57079633) │
q_1: |0>─┤CNOT├ ──*─ X ┤RX(0.78539816)├ ┤1 ├
└────┘ ┌─┴┐ └──────────────┘ └───────────────────┘
q_2: |0>─────── ┤CZ├ ─ ──────────────── ─────────────────────
└──┘
c : / ═════════════════════════════════════════════════════2.5 Multi-Qubit Gates
TOFFOLI Gate (CCNOT)
The TOFFOLI gate is a three-qubit gate with two control qubits and one target. It flips the target if and only if both controls are
In matrix form (8x8), it acts as identity except for swapping the last two rows/columns:
In pyqpanda3: core.TOFFOLI(control1, control2, target).
from pyqpanda3 import core
# TOFFOLI: flips qubit 2 only when qubits 0 and 1 are both |1>
prog = core.QProg()
prog << core.X(0) # Set control qubit 0 to |1>
prog << core.X(1) # Set control qubit 1 to |1>
prog << core.TOFFOLI(0, 1, 2) # Target qubit 2 flips to |1>
prog << core.measure([0, 1, 2], [0, 1, 2])
machine = core.CPUQVM()
machine.run(prog, shots=1000)
print(machine.result().get_counts())
# Output: {'111': 1000} -- all three qubits are |1>运行结果:
{'111': 1000}Gate Taxonomy
graph TD
QG["Quantum Gates"]
QG --> SQ["Single-Qubit Gates"]
QG --> TQ["Two-Qubit Gates"]
QG --> MQ["Multi-Qubit Gates"]
SQ --> NP1["Non-Parameterized"]
SQ --> P1["Parameterized"]
NP1 --> X["X (NOT)"]
NP1 --> Y["Y"]
NP1 --> Z["Z"]
NP1 --> H["H (Hadamard)"]
NP1 --> S["S (Phase)"]
NP1 --> T["T (π/8)"]
NP1 --> I["I (Identity)"]
NP1 --> X1["X1"]
NP1 --> Y1["Y1"]
NP1 --> Z1["Z1"]
P1 --> RX["RX(theta)"]
P1 --> RY["RY(theta)"]
P1 --> RZ["RZ(theta)"]
P1 --> P["P(theta)"]
P1 --> U1["U1(theta)"]
P1 --> U2["U2(theta, phi)"]
P1 --> U3["U3(theta, phi, lambda)"]
P1 --> U4["U4(theta, phi, lambda, gamma)"]
P1 --> RPHI["RPHI(theta, phi)"]
TQ --> NP2["Non-Parameterized"]
TQ --> P2["Parameterized"]
NP2 --> CNOT["CNOT"]
NP2 --> CZ["CZ"]
NP2 --> SWAP["SWAP"]
NP2 --> ISWAP["ISWAP"]
NP2 --> SQISWAP["SQISWAP"]
NP2 --> MS["MS"]
P2 --> CRX["CRX(theta)"]
P2 --> CRY["CRY(theta)"]
P2 --> CRZ["CRZ(theta)"]
P2 --> CP["CP(theta)"]
P2 --> CU["CU(theta, phi, lambda, gamma)"]
P2 --> RXX["RXX(theta)"]
P2 --> RYY["RYY(theta)"]
P2 --> RZZ["RZZ(theta)"]
P2 --> RZX["RZX(theta)"]
MQ --> TOFFOLI["TOFFOLI (CCNOT)"]
style QG fill:#e1f5fe
style SQ fill:#fff3e0
style TQ fill:#e8f5e9
style MQ fill:#fce4ec3. Gate Operations in pyqpanda3
pyqpanda3 gates support four powerful operations: adjoint, control, power, and matrix extraction. These operations allow you to construct complex circuits from simple building blocks.
3.1 Adjoint (.dagger())
The .dagger() method returns the adjoint (conjugate transpose) of a gate. For unitary gates, the adjoint is the inverse. This is useful for:
- Uncomputing intermediate results
- Implementing quantum algorithms that require backward steps
- Constructing reflection operators
from pyqpanda3 import core
import numpy as np
# The dagger of H is H itself (H is self-adjoint)
h_gate = core.H(0)
h_dagger = h_gate.dagger()
# Verify: H.dagger() matrix should equal H matrix
h_mat = np.array(h_gate.matrix())
hd_mat = np.array(h_dagger.matrix())
print("H == H.dagger():", np.allclose(h_mat, hd_mat))
# Output: H == H.dagger(): True
# Verify: H·H = I (applying H twice returns to original state)
h_h = np.array(h_gate.matrix()) @ np.array(h_gate.matrix())
print("H·H == I:", np.allclose(h_h, np.eye(2)))
# Output: H·H == I: True
# Practical use: apply S then uncompute with S.dagger()
prog = core.QProg()
prog << core.H(0) # Create superposition |+>
prog << core.S(0) # Apply S gate
prog << core.S(0).dagger() # Undo S gate (returns to |+>)
prog << core.H(0) # Return to |0>
prog << core.measure(0, 0)
machine = core.CPUQVM()
machine.run(prog, shots=1000)
print(machine.result().get_counts())
# Output: {'0': 1000} -- S and S.dagger() cancel out运行结果:
H == H.dagger(): True
H·H == I: True
{'0': 1000}3.2 Controlled Gates (.control())
The .control(qubit) method transforms any gate into its controlled version. The original gate is applied to its target qubits only if the specified control qubit is in state
This is extremely powerful: you can create controlled versions of any gate, including gates that already have controls.
from pyqpanda3 import core
# Create a controlled-X (equivalent to CNOT) from X
x_gate = core.X(1)
controlled_x = x_gate.control(0) # Control on qubit 0
# Use in a circuit
prog = core.QProg()
prog << core.X(0) # Set control to |1>
prog << controlled_x # X on qubit 1 is activated
prog << core.measure([0, 1], [0, 1])
machine = core.CPUQVM()
machine.run(prog, shots=1000)
print(machine.result().get_counts())
# Output: {'11': 1000}
# Create a controlled-Hadamard
h_gate = core.H(1)
controlled_h = h_gate.control(0)
# Create a controlled-SWAP (Fredkin gate)
swap_gate = core.SWAP(1, 2)
fredkin = swap_gate.control(0) # SWAP qubits 1,2 only if qubit 0 is |1>
# Create a doubly-controlled gate (like TOFFOLI from X)
x_gate = core.X(2)
toffoli = x_gate.control(0).control(1) # X on qubit 2 if qubits 0 AND 1 are |1>
# Verify the doubly-controlled gate
prog2 = core.QProg()
prog2 << core.X(0) # Set first control to |1>
prog2 << core.X(1) # Set second control to |1>
prog2 << toffoli # X on qubit 2 activated
prog2 << core.measure([0, 1, 2], [0, 1, 2])
machine2 = core.CPUQVM()
machine2.run(prog2, shots=1000)
print(machine2.result().get_counts())
# Output: {'111': 1000}运行结果:
{'11': 1000}
{'111': 1000}3.3 Power (.power())
The .power(k) method raises a gate to the k-th power:
This is especially useful for fractional powers of gates:
from pyqpanda3 import core
import numpy as np
# X^2 = I (applying NOT twice returns to original)
x_gate = core.X(0)
x_squared = x_gate.power(2)
# Verify: X^2 should equal the identity matrix I
identity = np.eye(2, dtype=complex)
print("X^2 == I:", np.allclose(np.array(x_squared.matrix()), identity))
# Output: X^2 == I: True
# Z^{1/2} = S gate
z_gate = core.Z(0)
z_half = z_gate.power(0.5)
# Verify: Z^{1/2} should match S gate matrix
z_half_mat = np.array(z_half.matrix())
s_mat = np.array(core.S(0).matrix())
print("Z^{1/2} == S:", np.allclose(z_half_mat, s_mat))
# Output: Z^{1/2} == S: True
# Z^{1/4} = T gate
z_quarter = z_gate.power(0.25)
z_quarter_mat = np.array(z_quarter.matrix())
t_mat = np.array(core.T(0).matrix())
print("Z^{1/4} == T:", np.allclose(z_quarter_mat, t_mat))
# Output: Z^{1/4} == T: True运行结果:
X^2 == I: True
Z^{1/2} == S: True
Z^{1/4} == T: True3.4 Matrix Representation (.matrix())
The .matrix() method returns the unitary matrix of a gate as a 2D array. This is useful for debugging, verification, and educational purposes.
from pyqpanda3 import core
import numpy as np
# Get the matrix of any gate
h_matrix = core.H(0).matrix()
print("H matrix:")
print(np.array(h_matrix))
# [[ 0.707 0.707]
# [ 0.707 -0.707]]
x_matrix = core.X(0).matrix()
print("\nX matrix:")
print(np.array(x_matrix))
# [[0 1]
# [1 0]]
# Two-qubit gate matrices are 4x4
cnot_matrix = core.CNOT(0, 1).matrix()
print("\nCNOT matrix:")
print(np.array(cnot_matrix))
# [[1 0 0 0]
# [0 1 0 0]
# [0 0 0 1]
# [0 0 1 0]]
# Verify unitarity: U^\dagger U = I
h_arr = np.array(h_matrix)
should_be_identity = h_arr.conj().T @ h_arr
print("\nH^\dagger H (should be I):")
print(np.round(should_be_identity, 10))
# The matrix of a controlled gate
controlled_h = core.H(0).control(2)
ch_matrix = np.array(controlled_h.matrix())
print("\nControlled-H matrix:")
print(np.round(ch_matrix, 3))
# Note: .control().matrix() returns the base gate matrix without
# expanding for control qubits (2x2 for a single-qubit gate).
# [[ 0.707+0.j 0.707+0.j]
# [ 0.707+0.j -0.707+0.j]]运行结果:
H matrix:
[[ 0.70710678+0.j 0.70710678+0.j]
[ 0.70710678+0.j -0.70710678+0.j]]
X matrix:
[[0.+0.j 1.+0.j]
[1.+0.j 0.+0.j]]
CNOT matrix:
[[1.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 1.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 1.+0.j]
[0.+0.j 0.+0.j 1.+0.j 0.+0.j]]
H^\dagger H (should be I):
[[1.+0.j 0.+0.j]
[0.+0.j 1.+0.j]]
Controlled-H matrix:
[[ 0.707+0.j 0.707+0.j]
[ 0.707+0.j -0.707+0.j]]4. Measurement
4.1 Projective Measurement
Measurement is the process of observing a quantum state, which collapses it from a superposition into a definite classical outcome. In the computational basis, measuring a qubit in state
- Outcome
0with probability - Outcome
1with probability
After measurement, the qubit is in the measured basis state. This collapse is irreversible -- the superposition information is lost.
For an
measuring all qubits yields the bitstring
4.2 Measuring in pyqpanda3
pyqpanda3 provides two forms of measurement:
Single qubit measurement:
from pyqpanda3 import core
# Syntax: core.measure(qubit_index, classical_bit_index)
# Minimal runnable example: measure qubit 0 after H gate
p = core.QProg()
p << core.H(0) << core.measure(0, 0)
m = core.CPUQVM()
m.run(p, shots=100)
print(m.result().get_counts())运行结果:
{'0': 54, '1': 46}Note: Quantum measurement results are probabilistic. The exact counts shown above are example values from one run. Your actual output will differ each time you run the code.
Multi-qubit measurement:
from pyqpanda3 import core
# Syntax: core.measure([qubit_0, qubit_1, ...], [cbit_0, cbit_1, ...])
# Minimal runnable example: Bell state measurement
p = core.QProg()
p << core.H(0) << core.CNOT(0, 1)
p << core.measure(0, 0) << core.measure(1, 1)
m = core.CPUQVM()
m.run(p, shots=100)
print(m.result().get_counts())运行结果:
{'00': 58, '11': 42}Each qubit measurement result is stored in a corresponding classical bit (cbit).
from pyqpanda3 import core
# Single qubit measurement
prog1 = core.QProg()
prog1 << core.H(0) # Create superposition
prog1 << core.measure(0, 0) # Measure qubit 0 into cbit 0
machine = core.CPUQVM()
machine.run(prog1, shots=1000)
print("Single qubit:", machine.result().get_counts())
# Output: {'0': 498, '1': 502}
# Multi-qubit measurement
prog2 = core.QProg()
prog2 << core.H(0) # Superposition on qubit 0
prog2 << core.X(1) # Qubit 1 in |1>
prog2 << core.measure([0, 1], [0, 1]) # Measure both
machine.run(prog2, shots=1000)
print("Two qubits:", machine.result().get_counts())
# Output: {'10': ~500, '11': ~500}
# Qubit 0 is random, qubit 1 is always 1运行结果:
Single qubit: {'0': 496, '1': 504}
Two qubits: {'10': 490, '11': 510}4.3 Shot-Based Measurement
Quantum measurement is inherently probabilistic. To build up statistics, we run the same circuit many times (called "shots"). The law of large numbers ensures that the observed frequencies converge to the true probabilities as the number of shots increases.
from pyqpanda3 import core
# Demonstrate shot-based measurement convergence
prog = core.QProg()
prog << core.H(0) # Creates |+> = (|0> + |1>)/sqrt(2)
prog << core.measure(0, 0)
machine = core.CPUQVM()
# Few shots: high variance
machine.run(prog, shots=10)
counts_10 = machine.result().get_counts()
print(f"10 shots: {counts_10}")
# Moderate shots
machine.run(prog, shots=100)
counts_100 = machine.result().get_counts()
print(f"100 shots: {counts_100}")
# Many shots: closer to 50/50
machine.run(prog, shots=10000)
counts_10000 = machine.result().get_counts()
print(f"10000 shots: {counts_10000}")
# The probabilities approach 0.5 for each outcome
total = sum(counts_10000.values())
for key, val in sorted(counts_10000.items()):
print(f" P({key}) = {val/total:.4f}")运行结果:
10 shots: {'0': 5, '1': 5}
100 shots: {'0': 46, '1': 54}
10000 shots: {'0': 4876, '1': 5124}
P(0) = 0.4876
P(1) = 0.5124The following diagram illustrates the measurement process:
flowchart TD
A["Quantum Circuit"] --> B["Run on Simulator<br/>shots=N"]
B --> C["Execute N times"]
C --> D["Shot 1:<br/>collapse to bitstring"]
C --> E["Shot 2:<br/>collapse to bitstring"]
C --> F["Shot ..."]
C --> G["Shot N:<br/>collapse to bitstring"]
D --> H["Aggregate Results<br/>get_counts()"]
E --> H
F --> H
G --> H
H --> I["Output: dict<br/>{'00': 248, '01': 253,<br/>'10': 251, '11': 248}"]
style A fill:#e1f5fe
style B fill:#fff3e0
style H fill:#e8f5e9
style I fill:#fce4ec4.4 Complete Example: Bell State
Let us put everything together to create a Bell state -- one of the simplest entangled states:
The circuit is: H on qubit 0, then CNOT with control=0 and target=1.
from pyqpanda3 import core
# Step 1: Build the Bell state circuit
prog = core.QProg()
prog << core.H(0) # Hadamard creates superposition on qubit 0
prog << core.CNOT(0, 1) # Entangle qubits 0 and 1
prog << core.measure([0, 1], [0, 1]) # Measure both qubits
# Step 2: Run on the CPU simulator
machine = core.CPUQVM()
machine.run(prog, shots=10000)
# Step 3: Get the measurement results
counts = machine.result().get_counts()
print("Bell state results:")
print(counts)
# Expected output: {'00': ~5000, '11': ~5000}
# Note: '01' and '10' should not appear (or very rarely due to numerical precision)
# Step 4: Analyze the results
total = sum(counts.values())
for bitstring in ['00', '01', '10', '11']:
count = counts.get(bitstring, 0)
probability = count / total
print(f" |{bitstring}>: {count} counts ({probability:.4f})")运行结果:
Bell state results:
{'00': 4961, '11': 5039}
|00>: 4961 counts (0.4961)
|01>: 0 counts (0.0000)
|10>: 0 counts (0.0000)
|11>: 5039 counts (0.5039)Why only 00 and 11? The H gate puts qubit 0 in
The two qubits are now entangled: measuring one immediately determines the other, regardless of the physical distance between them. This is the resource that powers quantum teleportation, superdense coding, and quantum error correction.
5. Summary
This tutorial covered the three foundational concepts of quantum computing:
| Concept | Key Idea | pyqpanda3 API |
|---|---|---|
| Qubit | Superposition of | Default state when allocated |
| Quantum Gate | Unitary transformation of qubit state | core.H(0), core.X(0), core.CNOT(0,1), etc. |
| Measurement | Collapses superposition to classical bit | core.measure(qubit, cbit) |
Gate operations extend the gate library:
| Operation | Method | Description |
|---|---|---|
| Adjoint | .dagger() | Returns the inverse gate |
| Control | .control(q) | Adds a control qubit |
| Power | .power(k) | Computes |
| Matrix | .matrix() | Returns the unitary matrix |
Next steps:
- Circuit Construction -- learn how to compose gates into complex circuits using
QProg,QCircuit, and the<<operator - Simulation -- explore the different simulators (CPUQVM, DensityMatrixSimulator, Stabilizer)
- Visualization -- learn to draw circuits and plot quantum states on the Bloch sphere
Quick Reference
from pyqpanda3 import core
import math
# ===================
# Single-Qubit Gates
# ===================
core.H(0) # Hadamard
core.X(0) # Pauli-X (NOT)
core.Y(0) # Pauli-Y
core.Z(0) # Pauli-Z
core.S(0) # S gate (sqrt of Z)
core.T(0) # T gate (4th root of Z)
core.I(0) # Identity
# ===================
# Rotation Gates
# ===================
core.RX(0, math.pi / 4) # Rotate around X
core.RY(0, math.pi / 2) # Rotate around Y
core.RZ(0, math.pi) # Rotate around Z
core.P(0, math.pi / 4) # Phase gate
# ===================
# Two-Qubit Gates
# ===================
core.CNOT(0, 1) # Controlled-NOT
core.CZ(0, 1) # Controlled-Z
core.SWAP(0, 1) # SWAP
# ===================
# Multi-Qubit Gates
# ===================
core.TOFFOLI(0, 1, 2) # Doubly-controlled NOT
# ===================
# Gate Operations
# ===================
gate = core.H(0)
gate.dagger() # Adjoint (inverse)
gate.control(2) # Controlled version (control on qubit 2)
gate.power(0.5) # Fractional power
gate.matrix() # Unitary matrix
# ===================
# Measurement
# ===================
core.measure(0, 0) # Single qubit
core.measure([0, 1], [0, 1]) # Multiple qubits
# ===================
# Run Circuit
# ===================
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
machine = core.CPUQVM()
machine.run(prog, shots=1000)
counts = machine.result().get_counts()
print(counts)运行结果:
{'00': 490, '11': 510}Knowledge Check
Test your understanding of quantum computing basics with pyqpanda3.
Q1: What is the normalization constraint on a qubit state
A1: The amplitudes must satisfy
Q2: What does the Hadamard gate do to the basis states
A2:
Q3: State the Born rule. If a qubit is in state
A3: The Born rule states
Q4: What is the difference between a product state and an entangled state?
A4: A product state can be factored as
Q5: What is the relationship between the S gate, T gate, and Z gate?
A5:
Q6: Write the matrix for
A6:
Q7: Explain quantum interference in the context of applying two Hadamard gates in sequence.
A7:
Exercise 1: State Preparation
Write a pyqpanda3 program that prepares the state
Solution:
import numpy as np
from pyqpanda3 import core
qvm = core.CPUQVM()
# The state cos(θ/2)|0⟩ + sin(θ/2)|1⟩ is prepared by RY(θ)
# We want cos(π/6)|0⟩ + sin(π/6)|1⟩
# So θ/2 = π/6 → θ = π/3
theta = np.pi / 3
prog = core.QProg()
prog << core.RY(0, theta)
prog << core.measure([0], [0])
qvm.run(prog, shots=10000)
result = qvm.result()
counts = result.get_counts()
p0_measured = counts.get("0", 0) / 10000
p1_measured = counts.get("1", 0) / 10000
p0_expected = np.cos(np.pi/6)**2 # 0.75
p1_expected = np.sin(np.pi/6)**2 # 0.25
print(f"P(|0⟩) measured: {p0_measured:.4f}, expected: {p0_expected:.4f}")
print(f"P(|1⟩) measured: {p1_measured:.4f}, expected: {p1_expected:.4f}")
assert abs(p0_measured - p0_expected) < 0.03
assert abs(p1_measured - p1_expected) < 0.03
print("Verification passed!")运行结果:
P(|0⟩) measured: 0.7503, expected: 0.7500
P(|1⟩) measured: 0.2497, expected: 0.2500
Verification passed!Exercise 2: Entanglement Verification
Prepare all four Bell states and verify their measurement statistics. The four Bell states are:
Solution:
from pyqpanda3 import core
qvm = core.CPUQVM()
shots = 5000
def run_and_print(name, prog):
prog << core.measure([0, 1], [0, 1])
qvm.run(prog, shots=shots)
counts = qvm.result().get_counts()
print(f"{name}: {counts}")
return counts
# |Φ+⟩ = (|00⟩ + |11⟩) / √2
prog_pp = core.QProg()
prog_pp << core.H(0) << core.CNOT(0, 1)
c1 = run_and_print("|Φ+⟩", prog_pp)
assert c1.get("01", 0) < 100 and c1.get("10", 0) < 100
# |Φ-⟩ = (|00⟩ - |11⟩) / √2
prog_pm = core.QProg()
prog_pm << core.H(0) << core.Z(0) << core.CNOT(0, 1)
c2 = run_and_print("|Φ-⟩", prog_pm)
assert c2.get("01", 0) < 100 and c2.get("10", 0) < 100
# |Ψ+⟩ = (|01⟩ + |10⟩) / √2
prog_mp = core.QProg()
prog_mp << core.H(0) << core.X(1) << core.CNOT(0, 1)
c3 = run_and_print("|Ψ+⟩", prog_mp)
assert c3.get("00", 0) < 100 and c3.get("11", 0) < 100
# |Ψ-⟩ = (|01⟩ - |10⟩) / √2
prog_mm = core.QProg()
prog_mm << core.X(0) << core.H(0) << core.X(1) << core.CNOT(0, 1)
c4 = run_and_print("|Ψ-⟩", prog_mm)
assert c4.get("00", 0) < 100 and c4.get("11", 0) < 100
print("\nAll four Bell states verified!")运行结果:
|Φ+⟩: {'00': 2519, '11': 2481}
|Φ-⟩: {'00': 2529, '11': 2471}
|Ψ+⟩: {'01': 2463, '10': 2537}
|Ψ-⟩: {'01': 2474, '10': 2526}
All four Bell states verified!Exercise 3: Custom Gate Matrix Verification
Create a custom unitary gate from a
Solution:
import numpy as np
from pyqpanda3 import core
# Define a custom unitary: RY(π/4) matrix
theta = np.pi / 4
matrix = np.array([
[np.cos(theta/2), -np.sin(theta/2)],
[np.sin(theta/2), np.cos(theta/2)]
], dtype=complex)
# Create oracle from matrix and verify
qvm = core.CPUQVM()
# Apply to |0⟩: expect cos(π/8)|0⟩ + sin(π/8)|1⟩
prog = core.QProg()
oracle = core.Oracle([0], matrix)
prog << oracle << core.measure([0], [0])
qvm.run(prog, shots=10000)
counts = qvm.result().get_counts()
p0 = counts.get("0", 0) / 10000
expected_p0 = np.cos(np.pi/8)**2
print(f"P(|0⟩) measured: {p0:.4f}, expected: {expected_p0:.4f}")
assert abs(p0 - expected_p0) < 0.03
print("Custom gate verified!")运行结果:
P(|0⟩) measured: 0.8566, expected: 0.8536
Custom gate verified!