Cram sheet — Section 1: Perform quantum operations
One-screen review layer for this section: short background primers per objective, then the key facts — each backed by an official source or by an executed proof from the question bank.
Define Pauli Operators
A Pauli label reads right-to-left
A Pauli label is read RIGHT to LEFT against qubit indices: the rightmost character acts on qubit 0, the leftmost on the highest-index qubit.
from qiskit.quantum_info import Pauli
Pauli('IX') # X on qubit 0, I on qubit 1
Pauli('XZ').to_matrix() # == np.kron(X, Z): X on qubit 1, Z on qubit 0
Pauli('iXX') # phase prefix: '', 'i', '-', '-i'
The object stores the I/X/Y/Z string plus a phase drawn from +1, i, −1, −i. Writing the phase into the label makes a distinct object, so a plain multi-letter label carries no phase and its matrix is the bare tensor product.
Two families of combination methods, and the whole objective lives on telling them apart.
| Call | Result | Notes |
|---|---|---|
a.tensor(b) | a ⊗ b | caller a goes to the higher-index subsystem, b to qubit 0 |
a.expand(b) | b ⊗ a | the mirror: a sits on subsystem 0 |
a.dot(b) | A·B | plain matrix product |
a.compose(b) | B·A | circuit order — a acts first |
a.compose(b, front=True) | A·B | same as dot |
On Paulis the difference is one character: Pauli('X').dot(Pauli('Y')) is iZ, while Pauli('X').compose(Pauli('Y')) is -iZ. The class never silently drops the phase.
Sources: operators-overview · operator-class · quantum_info
SparsePauliOp, and commutation by counting
Observables are SparsePauliOp — a linear combination of Pauli strings — and that is what the Estimator primitive expects, not a hand-built Pauli.
from qiskit.quantum_info import SparsePauliOp
SparsePauliOp.from_list([("ZZ", 1), ("XX", 0.5)])
SparsePauliOp.from_sparse_list([("ZX", [1, 4], 1)], num_qubits=5) # prints XIIZI
from_list takes (label, coefficient) pairs on full-width labels. from_sparse_list takes (label, qubit_indices, coefficient) plus an explicit num_qubits, and there the label characters pair with the index list left-to-right — the little-endian display rule read back at you. simplify() drops only terms whose coefficient is zero within tolerance.
Commutation is a counting exercise, not a matrix multiplication: two multi-qubit Paulis commute exactly when the number of positions at which their single-qubit factors anticommute is EVEN.
| Pair | Anticommuting positions | a.commutes(b) |
|---|---|---|
Pauli('XX') vs Pauli('ZZ') (X⊗X vs Z⊗Z) | 2 (even) | True |
Pauli('XI') vs Pauli('ZZ') (X⊗I vs Z⊗Z) | 1 (odd) | False |
An identity factor contributes zero to that count — it never buys commutation by itself.
Sources: operators-overview · quantum_info
Must know:
Pauli('IX')is I ⊗ X — X on qubit 0, identity on qubit 1 — and indexing the object returns the qubit-0 factor first. — ⚙️ proven ins1-q012- Pauli products keep their phase: X·Y = iZ, Y·Z = iX, Z·X = iY via
.dot; reversing the operands conjugates it (Y·X =-iZ, Z·Y =-iX). — ⚙️ proven ins1-q014 SparsePauliOp.simplify()SUMS duplicate terms' coefficients (Z with 1 and Z with 2 becomes Z with 3), never averaging them or keeping only the largest. — ⚙️ proven ins1-q018cx(0, 1); rz(θ, 1); cx(0, 1)is exactly the ZZ evolutionPauliEvolutionGate(SparsePauliOp('ZZ'), time=θ/2)—rz(π/3)in the middle equalstime=π/6, because RZ also carries the half-angle convention. — ⚙️ proven ins1-q065
Traps:
- Z on qubit 0 is
Pauli('IZ');Pauli('ZI')is the opposite operator, and.adjoint()does not reverse qubit order (Hermitian labels come back unchanged). — ⚙️ proven ins1-q020 PauliEvolutionGate(op, time=t)is exp(−i·t·op) with NO factor ½, whileRXXGate(θ)andRZZGate(θ)mean exp(−i·θ/2·P): XX evolution for t = π/6 on |00⟩ gives{'00': 0.75, '11': 0.25}, butRXXGate(π/6)only 0.933/0.067. So evolution time t matches an RXX/RZZ angle of 2t. — ⚙️ proven ins1-q064a @ bonSparsePauliOps is the operator PRODUCT, not a tensor product:(X + Z) @ (X + Z)has raw terms I, −iY, +iY, I, and.simplify()leaves2·Ibecause the anticommuting cross terms cancel.a.tensor(a)is the different two-qubitXX + XZ + ZX + ZZ. — ⚙️ proven ins1-q057
Apply quantum operations
Statevector: build it, evolve it, read it
Statevector is the simulator you carry in your head, and it runs UNITARY circuits only.
from qiskit.quantum_info import Statevector
Statevector.from_label('10') # |q1 q0⟩ — amplitude 1 at index 2
Statevector.from_int(2, dims=4) # same state; an int dims is the TOTAL dimension
sv = Statevector(qc) # delegates to Statevector.from_instruction(qc)
sv2 = sv.evolve(op) # returns a NEW Statevector; sv is untouched
from_label also accepts the eigenstate letters '+', '−', 'r', 'l'. Leave a measure in the circuit and both constructors raise QiskitError: Cannot apply instruction with classical bits: measure. evolve accepts a Pauli, an Operator, a gate or a circuit.
Read-out splits into families that questions love to swap.
| Call | Returns | Note |
|---|---|---|
.data | complex amplitudes | in index order |
.probabilities() | ndarray | squared moduli, index order |
.probabilities_dict() | dict keyed by bitstring | keys run q_(n−1)…q_0 |
.probabilities([0]) | ndarray of length 2 — not a dict | marginalizes every qubit not listed; a Bell state gives 0.5/0.5 |
.expectation_value(obs) | number | takes a Pauli or a SparsePauliOp |
For |00⟩ against Z⊗Z + 0.5·(X⊗X) the answer is 1.0: |00⟩ is a +1 eigenstate of ZZ, while XX maps it to the orthogonal |11⟩.
Sources: quantum_info · operator-class
The phase family, and what equality means
The phase gates leave probabilities untouched and differ only in where the phase lands.
| Gate | Matrix | Phase on the 1 amplitude |
|---|---|---|
z | diag(1, −1) | −1 |
s | diag(1, i) | +i — the square root of Z, identical to p(π/2) |
sdg | diag(1, −i) | −i, the conjugate of s |
t | diag(1, e^(iπ/4)) | the square root of S |
tdg | diag(1, e^(−iπ/4)) | the conjugate of t |
p(θ) | diag(1, e^(iθ)) | the whole angle |
rz(θ) | diag(e^(−iθ/2), e^(+iθ/2)) | split symmetrically over both amplitudes |
So after h, s gives (|0⟩ + i|1⟩)/√2 and sdg gives (|0⟩ − i|1⟩)/√2. RZ and P of the same angle differ by a global phase, never by physics: rz(π/2) on |0⟩ records [0.7071−0.7071j, 0] while the probabilities stay [1.0, 0.0]. T on |1⟩ likewise records 0.7071+0.7071j — Qiskit keeps a global phase explicitly. That distinction is exactly what Operator.equiv and == separate. Setting qc.global_phase yourself is unobservable in the same way: exact probabilities and seeded counts come back bit-identical.
Two memorized formulas answer most probability questions without a simulator.
| For RY(θ) on the zero state | Value |
|---|---|
| amplitudes | cos(θ/2) and sin(θ/2) |
| P(outcome 1) | sin²(θ/2) |
| ⟨Z⟩ | cos θ — 0.5 at θ = π/3, not sin or cos of the half angle |
Sources: qiskit.circuit.library.SGate · operator-class
Must know:
Operator(qc)for H on qubit 0 of a two-qubit circuit is I ⊗ H — qubit 0 sits on the RIGHT of the tensor product. — ⚙️ proven ins1-q037efficient_su2(n, reps=r)has2·n·(r+1)free parameters — anryand anrzper qubit, with one rotation layer MORE thanreps:efficient_su2(4, reps=2)gives 24, andskip_final_rotation_layer=Truedrops it to 16. — ⚙️ proven ins1-q048real_amplitudesis the RY-only sibling ofefficient_su2: its gate set is exactlyry+cx, son·(reps+1)parameters (12 at the default reps=3 on 3 qubits).quantum_volumecarries no free parameters at all. — ⚙️ proven ins1-q050QFTGate(n)takes only a qubit count (nodo_swaps, noapproximation_degree). Onedecompose()gives nh, n(n−1)/2cpand ⌊n/2⌋swap— for n = 4count_ops()gives{'cp': 6, 'h': 4, 'swap': 2}(sorted by count), nevercx. — ⚙️ proven ins1-q049- Reading a Bloch drawing:
hputs the arrow on +x and the phase gate then swings it inside the equator —sto +y,sdgto −y. An arrow left at a pole means that qubit was never rotated off the z axis. — ⚙️ proven ins1-q051 - A
plot_state_cityfigure IS the density matrix: left panel Re(ρ), right panel Im(ρ). Afterh(0); cx(0,1); s(1)the two 0.5 populations stay real while the |00⟩⟨11| cell becomes −0.5i, sosis visible only in the imaginary panel. — ⚙️ proven ins1-q052
Traps:
- On a measured circuit,
Statevector(qc)andfrom_instructionoffer noshots/backendparameter, and appending a save instruction does not avoid theQiskitError. — ⚙️ proven ins1-q036 - X on qubit 0 of a two-qubit circuit gives the
probabilities_dict()key'01'with probability 1.0 — never'10', never an integer index. — ⚙️ proven ins1-q031 Operator.equivignores a global phase and==does not: Z versus RZ(π) is equiv-True but ==-False; T versus RZ(π/2) is False both ways (that pairing is S). — ⚙️ proven ins1-q033
- Read every Pauli label right-to-left and name the operator on each qubit before answering.
- Say which of
tensor,expand,dotorcomposea question used, and which operand acts first. - Count anticommuting positions to settle commutation: an even count commutes, an odd count does not.
- Delete every
measurebefore handing a circuit toStatevector. - Check whether the answer wants amplitudes, a probability ndarray, or a bitstring-keyed dict.
- Write out diag(1, i) for S and diag(e^(−iθ/2), e^(+iθ/2)) for RZ before comparing operators.
- Ask whether a difference is only a global phase:
equivignores it,==does not. - Compute
2·n·(reps+1)forefficient_su2andn·(reps+1)forreal_amplitudes.
Every fact above is sourced: 📖 links go to official documentation, ⚙️ marks facts observed by executing code against the pinned Qiskit stack (the linked section page shows the proof evidence on its practice questions).