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 | Commute? |
|---|---|---|
| X⊗X vs Z⊗Z | 2 (even) | yes |
| X⊗I vs Z⊗Z | 1 (odd) | no |
An identity factor contributes zero to that count — it never buys commutation by itself.
Sources: operators-overview · quantum_info
Must know:
- Pauli labels are little-endian:
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 Pauli('XX').commutes(Pauli('ZZ'))is True (two anticommuting positions, even) andPauli('XI').commutes(Pauli('ZZ'))is False (one position, odd). — ⚙️ proven ins1-q017SparsePauliOp.simplify()SUMS the coefficients of duplicate terms (Z with 1 and Z with 2 becomes Z with 3) — it never averages them and never keeps only the largest. — ⚙️ proven ins1-q018
Traps:
Pauli('XZ').to_matrix()is exactlynp.kron(X, Z), so label order and tensor order coincide — the convention stays invisible until a question asks which qubit an operator acts on. — ⚙️ proven ins1-q010- To place Z on qubit 0 you must write
Pauli('IZ')—Pauli('ZI')is the opposite operator, and.adjoint()does not reverse qubit order (Hermitian labels come back unchanged). — ⚙️ proven ins1-q020
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 | marginalizes every qubit not listed |
.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 θ |
Sources: qiskit.circuit.library.SGate · operator-class
Must know:
Operator(qc)for an H on qubit 0 of a two-qubit circuit is the 4×4 matrix I ⊗ H — qubit 0 sits on the RIGHT of the tensor product, not the left. — ⚙️ 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 = 4 that is{'h': 4, 'cp': 6, 'swap': 2}, 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:
Statevector.from_instructionandStatevector(qc)both raiseQiskitError: Cannot apply instruction with classical bits: measureon a measured circuit; there is noshots/backendparameter and appending a save instruction does not help. — ⚙️ proven ins1-q036probabilities_dict()keys are q_(n−1)…q_0 bitstrings: applying X to qubit 0 of a two-qubit circuit gives the key'01'with probability 1.0, never'10'and never an integer index. — ⚙️ proven ins1-q031sv.probabilities([0])marginalizes over the other qubits and returns an ndarray of length 2 (a Bell state gives 0.5/0.5) — not a dict. — ⚙️ proven ins1-q042Operator.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- For RY(θ) on |0⟩ the expectation value is ⟨Z⟩ = cos θ — at θ = π/3 that is 0.5, not sin or cos of the half angle. — ⚙️ proven in
s1-q040
- 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).