Qiskit for Curious Programmers
This book teaches Qiskit the way many programmers actually learn difficult technical subjects: one mental model at a time, with short circuits, repeated patterns, and deliberate practice.
The official Qiskit documentation is excellent reference material. It is not optimized for a first pass through the subject. QCoder, on the other hand, is unusually good at forcing beginners to turn vague understanding into circuits that actually work. This book tries to combine the strengths of both:
- the precision of Qiskit’s APIs
- the progression and pressure of QCoder problems
- the pacing of a beginner-friendly programming book
The target reader is comfortable with Python and new to quantum programming. A physics background helps, but it is not required.
What you should gain from this book
By the end, you should be able to:
- read and write nontrivial circuits in Qiskit
- reason about amplitudes, phases, and measurement outcomes
- debug circuits with
Statevectorand sampling primitives - build state-preparation routines, oracles, reflections, QFT blocks, and simple arithmetic circuits
- recognize the circuit patterns behind major QCoder problem families
- write starter quantum machine learning code without getting lost in the circuit layer
That is a realistic goal. It is also a strong one.
What this book does not promise
No single book makes someone instantly strong at every quantum algorithm.
What this book can do is bring you to the point where:
- official Qiskit tutorials stop feeling hostile
- QCoder problems look structured instead of mysterious
- you can design and test your own circuits instead of only copying them
The teaching philosophy
This book is built on a few strong opinions.
Circuits before hardware
You should first understand what a circuit does mathematically. Hardware noise, transpilation, and backend management matter, but they are distractions if you still confuse state preparation with measurement.
Exact simulation before repeated sampling
Before asking what outcomes appear, ask what state you created.
That is why the book keeps coming back to:
Statevector.from_instruction- basis labels
- relative phase
- reversible mappings
Patterns before prestige algorithms
It is better to deeply understand:
- basis preparation
- superposition
- entanglement
- oracles
- reflections
than to vaguely memorize Grover, QFT, or QML buzzwords.
Those bigger topics become approachable only after the small patterns are solid.
Before you begin
This book works best when you treat it like a lab manual rather than a novel.
The concrete workflow for reading chapters, running code, and using the exercises is in How To Use This Book.
How To Use This Book
This book is meant to be worked through, not merely read.
Every chapter is built around the same loop:
- learn one mental model
- type one or two short Qiskit examples
- inspect the exact state before measuring
- solve the chapter exercises
- try one or two linked QCoder problems while the idea is still fresh
The chapter pattern
Most chapters follow four layers.
Core idea
This is the conceptual layer. Examples:
- a Hadamard is a basis change, not just a gate symbol
cx(control, target)is a conditional action on the target- a phase can be invisible in immediate measurement counts but still matter later
If you skip this layer, Qiskit becomes syntax memorization.
Qiskit move
This is the API layer. You will repeatedly use:
QuantumCircuitto write circuitsStatevector.from_instructionto inspect exact amplitudesStatevectorSamplerto test measurement distributionscompose,inverse, andcontrolto assemble larger circuits
The book deliberately reuses these tools until they feel routine.
Worked pattern
This is the design layer. You should learn to recognize patterns such as:
- prepare a basis state
- create a uniform superposition
- turn a condition into a phase flip
- convert a bit-flip oracle into a phase oracle
- prepare, reflect, unprepare
Once you can name a pattern, many QCoder problems stop feeling unique.
Exercises
The exercises are where the chapter becomes useful.
Each chapter ends with:
- checkpoint exercises that force you to restate the idea in your own code
- a short set of linked QCoder problems that use the same pattern in a less guided setting
A practical reading strategy
Use this rhythm for each chapter:
- read the chapter once without writing code
- type every code block yourself
- predict the state or counts before running anything
- change one gate and explain what changed
- solve at least one linked QCoder problem before moving on
That last step matters. If you only read, the material will feel clearer than it really is.
What to do when you get stuck
When a circuit does not behave as expected, check these in order:
- did I misunderstand qubit order?
- am I looking at amplitudes or only at counts?
- did I accidentally measure too early?
- did I forget an inverse or uncompute step?
- am I confusing relative phase with global phase?
That checklist will save you a surprising amount of time.
Setup
This project uses mdBook for the text and uv for Python environments and dependencies.
Tools you need
Install:
- Python 3.11 or newer
uvmdbook
The Python dependency metadata lives in pyproject.toml.
Install the project dependencies
From the project root, run:
uv sync
That creates a local environment and installs the pinned dependencies for this book.
If you want to add more packages later, use uv add, not pip:
uv add qiskit
uv add qiskit-machine-learning
uv add jupyter
Run Python through uv so you are using the project environment:
uv run python
Smoke test
This is the smallest useful Qiskit test:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(1)
qc.h(0)
print(qc)
print(Statevector.from_instruction(qc))
If that prints a one-qubit circuit and a state with equal amplitudes on |0> and |1>, your environment is ready for the early chapters.
Build the book locally
To build the static site:
mdbook build
To serve it locally with live reload:
mdbook serve --open
Why this book uses exact simulation first
Beginners often measure too early and then wonder why nothing makes sense.
For the first half of the book, your main tools are:
Statevectorfor exact amplitudesStatevectorSamplerfor shot-based sampling without backend setup
That order is deliberate:
- inspect the state
- predict the distribution
- sample the distribution
If you reverse that order, debugging becomes much harder.
Optional extras
A few later sections mention additional packages and workflows.
- For notebooks, add
jupyter - For quantum machine learning examples, add
qiskit-machine-learning - For experimentation, keep a small
examples/or notebook folder outsidesrc/so your book pages stay clean
Your First Circuit
The first Qiskit object you need is QuantumCircuit.
from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
qc.h(0)
print(qc)
This says:
- create a one-qubit circuit
- apply a Hadamard gate to qubit
0
Starting from |0>, the Hadamard creates the state
\[ \frac{|0\rangle + |1\rangle}{\sqrt{2}} \]
That state is often called the plus state.
A circuit is a recipe, not a state
This distinction is worth learning immediately.
- the circuit is the recipe
- the statevector is the state produced by the recipe
- measurement turns the state into classical data
Beginners often blend these together and then get confused about what a gate is changing.
Look at the state before measuring
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(1)
qc.h(0)
state = Statevector.from_instruction(qc)
print(state)
print(state.draw("text"))
This is the most important beginner debugging move in Qiskit. Before asking what you will observe, ask what state you built.
Two tiny examples that already matter
Preparing |1>:
qc = QuantumCircuit(1)
qc.x(0)
print(Statevector.from_instruction(qc))
Preparing the plus state:
qc = QuantumCircuit(1)
qc.h(0)
print(Statevector.from_instruction(qc))
The point is not that these circuits are hard. The point is that you are starting to connect gate actions to states.
Learn to predict before you run
Try this:
qc = QuantumCircuit(1)
qc.x(0)
qc.h(0)
print(Statevector.from_instruction(qc))
Before running it, ask:
- what state does
xcreate? - what does
hdo to that state?
That habit will scale all the way to Grover, QFT, and QML circuits.
Checkpoint Exercises
- Prepare
|1>. - Prepare the plus state.
- Apply
xthenhand inspect the resulting state. - Build two different circuits that end in the same state.
Try These On QCoder
- QPC001 A1, Generate State |1>
- QPC001 A2, Generate Plus state
- QPC002 B1, Generate State e^(i theta)|0>
Measurement And Sampling
Quantum states are not classical values. Measurement turns amplitudes into sampled outcomes.
Exact amplitudes versus observed counts
These questions are different:
- what state is the circuit in right now?
- what bitstrings appear if I measure repeatedly?
Qiskit gives you tools for both, and you should get used to using both.
Sampling with StatevectorSampler
from qiskit import QuantumCircuit
from qiskit.primitives import StatevectorSampler
qc = QuantumCircuit(1)
qc.h(0)
qc.measure_all()
sampler = StatevectorSampler()
result = sampler.run([qc], shots=1000).result()
counts = result[0].data.meas.get_counts()
print(counts)
For the plus state, the counts should be close to half "0" and half "1".
Counts are empirical data. They fluctuate with the number of shots. The statevector does not.
Why beginners should not measure too early
If you measure too soon, you destroy the information you were trying to understand.
A better workflow is:
- inspect the exact state
- predict the measurement distribution
- sample counts to confirm the prediction
That order is especially important once relative phase enters the story.
A first phase surprise
These circuits produce the same one-shot measurement distribution if you measure immediately:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc1 = QuantumCircuit(1)
qc1.h(0)
qc2 = QuantumCircuit(1)
qc2.h(0)
qc2.z(0)
print(Statevector.from_instruction(qc1))
print(Statevector.from_instruction(qc2))
Their states are:
\[ \frac{|0\rangle + |1\rangle}{\sqrt{2}} \quad \text{versus} \quad \frac{|0\rangle - |1\rangle}{\sqrt{2}} \]
If you measure right away, both give 50-50 counts. But they are not the same state, and later gates can expose that difference.
A two-qubit example
from qiskit import QuantumCircuit
from qiskit.primitives import StatevectorSampler
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
sampler = StatevectorSampler()
result = sampler.run([qc], shots=1000).result()
print(result[0].data.meas.get_counts())
You should only see "00" and "11".
This is your first example of a state whose measurement outcomes are correlated.
What counts can and cannot tell you
Counts are good for:
- checking whether impossible outcomes really are impossible
- estimating probabilities
- validating the final behavior of a circuit
Counts are bad for:
- identifying relative phase directly
- explaining why a circuit works
- debugging intermediate structure
That is why statevector inspection stays so central in this book.
Checkpoint Exercises
- Prepare a circuit that always measures
1. - Prepare a circuit with a 50-50 split between
0and1. - Build two different circuits with the same counts but different states.
- Measure a Bell state and list which two outcomes appear.
Try These On QCoder
- QPC001 A3, Generate Minus state
- QPC003 B2, Convert Bit-Flip into Phase-Flip I
- QPC005 B1, Action in the Fourier Basis
Basis States, Bitstrings, And Qubit Order
This chapter exists because many beginners lose hours to bit ordering.
The rule to remember
Qiskit uses little-endian ordering for basis states.
That means qubit 0 is the least significant bit in a computational-basis label.
For example, the label |10> means:
- qubit
1is1 - qubit
0is0
This feels backwards at first. You still need to internalize it.
A quick sanity check
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
qc.x(1)
print(Statevector.from_instruction(qc))
This prepares |10>, not |01>.
Why this matters
All of these depend on qubit order:
- reading statevectors
- interpreting counts
- preparing a target basis state
- writing arithmetic circuits
- solving QCoder problems with explicit target strings
When a circuit looks almost right, ordering is one of the first things to check.
State labels and measurement strings
The same bit-ordering convention shows up in measurement output.
If you do:
from qiskit import QuantumCircuit
from qiskit.primitives import StatevectorSampler
qc = QuantumCircuit(2)
qc.x(0)
qc.measure_all()
sampler = StatevectorSampler()
result = sampler.run([qc], shots=16).result()
print(result[0].data.meas.get_counts())
the counts show "01", because qubit 1 is the left bit and qubit 0 is the right bit.
A debugging habit that pays off
Whenever you target a basis state, write down both:
- which qubits you are changing
- which basis label you expect to see
For example, if you want |101>, say it out loud:
- qubit
2should be1 - qubit
1should be0 - qubit
0should be1
That kind of explicitness prevents a large class of mistakes.
A small table worth memorizing
For two qubits:
|00>meansq1=0, q0=0|01>meansq1=0, q0=1|10>meansq1=1, q0=0|11>meansq1=1, q0=1
If this table feels natural, later arithmetic and QFT chapters become much easier.
Checkpoint Exercises
- Prepare
|10>on two qubits. - Prepare
|101>on three qubits. - Explain why
qc.x(0)on two qubits gives|01>. - Create a small table for all 2-qubit basis states and the qubit values they represent.
Try These On QCoder
- QPC001 A4, Generate State (|10> + |11>)/sqrt(2)
- QPC003 A2, Generate State (|10> + |01>)/sqrt(2)
- QPC003 A3, Generate State (|100> + |010> + |001>)/sqrt(3)
The Single-Qubit Toolbox
Most early QCoder problems are really asking one question:
“Can you control one qubit well?”
That sounds small, but one-qubit fluency carries a lot of the book.
The first gates to master
x: swaps|0>and|1>z: flips the sign of|1>h: changes between the computational basis and the plus/minus basisrx,ry,rz: continuous rotationsp: adds a phase to|1>
You do not need every gate in the library yet. You do need to know what these do without hesitation.
ry as a state-preparation tool
For real amplitudes, ry(theta) is the cleanest gate to learn first:
\[ R_y(\theta)|0\rangle = \cos(\theta/2)|0\rangle + \sin(\theta/2)|1\rangle \]
This equation matters because it lets you design a target probability instead of guessing.
For example, if you want probability 3/4 on |1>, then you want:
\[ \sin^2(\theta/2) = 3/4 \]
so one valid choice is theta = 2 * asin(sqrt(3) / 2) = 2pi/3.
from math import pi
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(1)
qc.ry(2 * pi / 3, 0)
print(Statevector.from_instruction(qc))
Why z feels useless until it does not
If your qubit is definitely |0>, then z appears to do nothing.
But after a Hadamard, z changes relative phase:
\[ Z\frac{|0\rangle + |1\rangle}{\sqrt{2}} = \frac{|0\rangle - |1\rangle}{\sqrt{2}} \]
That is enough to change later interference.
The plus/minus basis is not optional
The states
\[ |+\rangle = \frac{|0\rangle + |1\rangle}{\sqrt{2}}, \qquad |-\rangle = \frac{|0\rangle - |1\rangle}{\sqrt{2}} \]
show up constantly:
|+>is whathcreates from|0>|->is whathcreates from|1>|->is the standard ancilla state for phase kickback tricks
You do not need to worship this notation. You do need to recognize it instantly.
Learn one tiny identity well
\[ H Z H = X \]
This is not just algebra. It is the smallest example of a powerful idea:
- phase information exists in one basis
- change basis
- the same action looks like a bit flip
You can check it directly:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Operator
lhs_circuit = QuantumCircuit(1)
lhs_circuit.h(0)
lhs_circuit.z(0)
lhs_circuit.h(0)
rhs_circuit = QuantumCircuit(1)
rhs_circuit.x(0)
lhs = Operator(lhs_circuit)
rhs = Operator(rhs_circuit)
print(lhs == rhs)
If you prefer not to rely on operator equality, test both circuits on |0> and |1> with Statevector.
Relative phase versus global phase
This distinction becomes important very early.
- multiplying the entire state by
-1does not change physical behavior - changing only one branch by
-1does change interference
So -|1> and |1> are physically equivalent, but (|0> + |1>)/sqrt(2) and (|0> - |1>)/sqrt(2) are not.
Checkpoint Exercises
- Prepare the minus state.
- Prepare a state with
P(1)=3/4. - Verify
H Z H = Xon|0>and|1>. - Build three different circuits for
-|1>and confirm they differ only by global phase.
Try These On QCoder
- QPC001 A3, Generate Minus state
- QPC002 B1, Generate State e^(i theta)|0>
- QPC003 B3, Generate State tensor_i (cos T_i |0> + sin T_i |1>)
Controlled Gates, Correlation, And Entanglement
The cx gate is where many circuits stop feeling like independent one-qubit manipulations.
Read cx correctly
qc.cx(control, target)
means:
“flip the target if the control is 1.”
That sentence is enough to understand most beginner two-qubit circuits.
A first controlled-gate sanity check
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
qc.x(0)
qc.cx(0, 1)
print(Statevector.from_instruction(qc))
Because qubit 0 starts as 1, the target qubit 1 is flipped, and the final state is |11>.
The Bell state
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
print(Statevector.from_instruction(qc))
This prepares:
\[ \frac{|00\rangle + |11\rangle}{\sqrt{2}} \]
If you sample it, you only see 00 and 11.
Correlation versus entanglement
At a beginner level, the practical rule is:
- a correlated measurement pattern is easy to observe
- entanglement is the quantum structure underneath some of those patterns
For now, you mainly need to become fluent with constructing Bell states, measuring them, and modifying their signs with z or cz.
Order matters
These are not the same:
qc1.h(0)
qc1.cx(0, 1)
qc2.cx(0, 1)
qc2.h(0)
Quantum circuits are ordered transformations, not unordered gate bags.
Control and target are not symmetric
These are also not the same:
qc1.cx(0, 1)
qc2.cx(1, 0)
On a basis state where only one qubit is 1, the outcomes can be completely different. That is why naming control and target clearly matters.
A sign-flipped Bell state
Once you have the Bell state, one z gate changes it to:
\[ \frac{|00\rangle - |11\rangle}{\sqrt{2}} \]
That single sign change is invisible if you measure immediately, but it matters for later interference and basis changes.
Checkpoint Exercises
- Prepare
( |01> + |10> ) / sqrt(2). - Prepare
|10>using exactly onexgate. - Build a circuit that creates a Bell state and then maps it to
( |00> - |11> ) / sqrt(2). - Compare
cx(0, 1)andcx(1, 0)on the same input state.
Try These On QCoder
- QPC001 A4, Generate State (|10> + |11>)/sqrt(2)
- QPC002 B3, SWAP Qubits
- QPC003 A2, Generate State (|10> + |01>)/sqrt(2)
State Preparation Patterns
QCoder loves state-preparation tasks because they expose whether you understand gates as transformations rather than names.
This chapter is about designing states on purpose.
Pattern 1: basis states
To prepare |101>, flip the right qubits:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(3)
qc.x(0)
qc.x(2)
print(Statevector.from_instruction(qc))
This is the easiest pattern, but it still forces you to respect qubit ordering.
Pattern 2: equal superposition
To create an equal superposition over all n-qubit basis states:
from qiskit import QuantumCircuit
n = 3
qc = QuantumCircuit(n)
for i in range(n):
qc.h(i)
This is the most common starting state in search-style algorithms.
Pattern 3: amplitudes first, routing second
Many hand-built two- and three-qubit states are easiest to construct by:
- setting amplitudes on one qubit
- using controlled gates to route amplitude into the desired basis states
- fixing signs after the magnitudes are correct
That order matters because sign mistakes are easier to fix after the amplitude layout is already right.
Pattern 4: signs after probabilities
Two states can share the same measurement distribution and still differ meaningfully:
\[ \frac{|00\rangle + |11\rangle}{\sqrt{2}} \quad \text{and} \quad \frac{|00\rangle - |11\rangle}{\sqrt{2}} \]
That is when z, cz, and basis changes matter.
Pattern 5: build symmetry deliberately
States like
\[ \frac{|100\rangle + |010\rangle + |001\rangle}{\sqrt{3}} \]
are good training because they force you to think in amplitudes, not just bit flips.
They also start teaching you a useful habit: name the target state in math before you write any gates.
A concrete two-qubit construction
Suppose you want:
\[ \sqrt{\frac{3}{4}}|00\rangle + \frac{1}{2}|11\rangle \]
One clean strategy is:
- use
ryon qubit0to split amplitude between a0branch and a1branch - use
cx(0, 1)to copy the branch label into qubit1
from math import pi
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
qc.ry(pi / 3, 0)
qc.cx(0, 1)
print(Statevector.from_instruction(qc))
This does not solve every state-preparation problem, but it shows a pattern that comes up constantly: prepare one branch variable, then route it with controls.
Manual design versus prepare_state
Qiskit has convenience methods that can load arbitrary states. Those are useful tools, but they are bad teachers.
Early in your learning, prefer manual constructions because they teach:
- qubit order
- amplitude planning
- basis changes
- sign correction
Later, when you already understand the circuit, a convenience routine can be fine.
Checkpoint Exercises
- Prepare the Bell state.
- Prepare the sign-flipped Bell state.
- Prepare the 3-qubit one-hot superposition above.
- Prepare a uniform superposition on four qubits.
Try These On QCoder
- QPC001 A5, Generate State (sqrt(3)/2)|100> + (1/2)|011>
- QPC003 A3, Generate State (|100> + |010> + |001>)/sqrt(3)
- QPC003 A4, A5, and A6
Reversible Logic And Oracles
Quantum circuits cannot freely erase information. That is why reversible logic shows up everywhere.
A useful mindset
When a classical step says “compute a value,” a quantum version often has to say:
- compute it reversibly
- use it
- uncompute temporary garbage
This matters for arithmetic, oracles, and many QCoder implementation tasks.
Bit-flip oracles
A bit-flip oracle changes an ancilla when a condition is true:
\[ |x\rangle|b\rangle \mapsto |x\rangle|b \oplus f(x)\rangle \]
These are often easier to think about first because they look more classical.
For example, to flip an ancilla when both data qubits are 1:
from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
If qubits 0 and 1 store the input and qubit 2 is the ancilla, this computes logical AND into the ancilla.
Phase oracles
A phase oracle marks good states by a sign:
\[ |x\rangle \mapsto (-1)^{f(x)}|x\rangle \]
Phase oracles are central in Grover-style algorithms because they mark states without needing a classical output wire in the final description.
For |11>, the simplest phase oracle is just:
qc = QuantumCircuit(2)
qc.cz(0, 1)
Turning one kind into the other
If you prepare an ancilla in the minus state
\[ \frac{|0\rangle - |1\rangle}{\sqrt{2}} \]
then a bit-flip on that ancilla acts like a phase flip on the control condition. This is the practical core of phase kickback.
A minimal bit-flip to phase-flip conversion
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(3)
# Prepare a superposition on the two data qubits.
qc.h([0, 1])
# Prepare the ancilla in |->
qc.x(2)
qc.h(2)
# Flip ancilla iff the input is 11.
qc.ccx(0, 1, 2)
print(Statevector.from_instruction(qc))
If you inspect the resulting amplitudes, the |11> branch on the data register picks up a minus sign.
Uncomputation is not optional
Suppose you compute a helper value into a work qubit and then keep going. If you never uncompute, that work qubit can stay entangled with your data and quietly break the intended algorithm.
The safe pattern is:
- compute
- use
- uncompute
That pattern will appear again in arithmetic, reflections, and Grover-style blocks.
Checkpoint Exercises
- Write a phase oracle that marks
|11>. - Write a phase oracle that marks
|10>. - Write a bit-flip oracle for the same condition and compare the two.
- Add temporary work qubits to a toy reversible computation and then uncompute them.
Try These On QCoder
- QPC002 B2, Phase Shift Oracle
- QPC003 B2, Convert Bit-Flip into Phase-Flip I
- QPC003 Ex1, Convert Bit-Flip into Phase-Flip II
Phase Kickback And Basis Changes
Phase kickback is the point where many circuits stop looking like gate puzzles and start looking algorithmic.
The shortest useful explanation
If a controlled operation targets a state that is an eigenstate of the target operation, then the control path can pick up phase information.
For beginners, the practical meaning is simpler:
- you can encode information in phase
- later basis changes can reveal it
The key identity again
\[ H Z H = X \]
This is the smallest example of a powerful pattern:
- phase information exists in one basis
- switch basis
- it becomes bit-flip information
The standard |-> ancilla trick
The state
\[ |-\rangle = \frac{|0\rangle - |1\rangle}{\sqrt{2}} \]
is special because X|-\rangle = -|-\rangle.
That means a controlled-X aimed at a |-> ancilla can transfer a sign back to the control condition.
A concrete example
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
# Put the data qubit in superposition.
qc.h(0)
# Prepare ancilla in |->
qc.x(1)
qc.h(1)
# Controlled-X from data to ancilla
qc.cx(0, 1)
print(Statevector.from_instruction(qc))
If you inspect the final state carefully, the branch where the control qubit is 1 picks up a minus sign.
Why basis changes matter so much
A phase can be hard to see if you stay in the computational basis. A Hadamard often makes the effect visible.
For example:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
print(Statevector.from_instruction(qc))
This ends in |1>. The middle z looked like a phase operation, but after basis changes it behaves like a bit flip.
Why QCoder likes this topic
Several QCoder problems are really asking whether you can move cleanly between:
- bit-flip language
- phase-flip language
- reflection language
If you can do that, Grover and Fourier-basis problems become much easier.
Checkpoint Exercises
- Show with Qiskit that
H Z HandXact the same on|0>and|1>. - Prepare a minus-state ancilla and test a controlled bit-flip against a phase oracle.
- Explain in one paragraph why equal measurement counts can still hide useful phase structure.
- Build a circuit that converts a phase condition into a measurable bit condition.
Try These On QCoder
- QPC003 B2, Convert Bit-Flip into Phase-Flip I
- QPC003 Ex1, Convert Bit-Flip into Phase-Flip II
- QPC005 B1, Action in the Fourier Basis
Reflections And Amplitude Amplification
Once you understand phase flips, the next step is reflections.
Reflection about a state
A reflection operator is usually written as
\[ I - 2|\psi\rangle\langle\psi| \]
Geometrically, it flips the sign of the component along a chosen direction and leaves the orthogonal subspace alone.
You do not need full linear-algebra fluency to use this idea productively. You do need to recognize when a circuit is implementing a reflection.
Why reflections matter
Grover’s algorithm is built from two reflections:
- one that marks good states
- one that reflects about the prepared starting state
If you understand reflections, Grover stops being a memorized recipe.
Reflection about |11>
On two qubits, a reflection about |11> is just a selective sign flip on that basis state:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Operator
qc = QuantumCircuit(2)
qc.cz(0, 1)
print(Operator(qc).data)
This leaves |00>, |01>, and |10> alone and flips the sign of |11>.
Reflection about |0...0>
A common building block is a reflection about the all-zero state. In small examples, you can think of it as:
- flip the sign of
|0...0> - leave the orthogonal basis states alone
By surrounding that operation with state preparation and its inverse, you can reflect about a much more interesting state.
The prepare-reflect-unprepare pattern
If you know how to prepare |\psi\rangle from |0...0\rangle, then you can build a reflection about |\psi\rangle by:
- preparing
|\psi\rangle - reflecting about
|0...0\rangle - unpreparing
In math:
\[ U\left(I - 2|0…0\rangle\langle 0…0|\right)U^\dagger = I - 2|\psi\rangle\langle\psi| \]
where U|0...0> = |\psi>.
A one-qubit example
To reflect about |+>:
from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
Because h prepares |+> from |0>, the sequence h -> z -> h gives a reflection about |+>, which is also the operator x.
Why this matters for debugging
When a Grover-like circuit fails, one of the most useful questions is:
- is my oracle really a reflection?
- is my diffusion step really a reflection about the start state?
That framing is much more robust than staring at a gate list.
Checkpoint Exercises
- Build a reflection about
|11>. - Build a reflection about the plus state on one qubit.
- Build a reflection about a Bell state using prepare-reflect-unprepare.
- Explain why reflections are natural ingredients for search algorithms.
Try These On QCoder
- QPC003 B4, Reflection Operator I
- QPC003 B5, Reflection Operator II
- QPC003 B7, Reflection Operator III
Grover Search
Grover is a perfect beginner algorithm because it is both important and readable.
It is also a good test of whether you actually understand the earlier chapters. Grover combines:
- uniform superposition
- phase marking
- reflections
- iteration count
If any one of those ideas is weak, Grover becomes a ritual instead of an algorithm.
The two-step loop
For a small search space:
- mark the good state with an oracle
- apply the diffusion operator
That combination amplifies the marked state’s amplitude.
A minimal 2-qubit example
from qiskit import QuantumCircuit
from qiskit.primitives import StatevectorSampler
qc = QuantumCircuit(2)
qc.h([0, 1])
# Oracle: mark |11>
qc.cz(0, 1)
# Diffusion
qc.h([0, 1])
qc.x([0, 1])
qc.cz(0, 1)
qc.x([0, 1])
qc.h([0, 1])
qc.measure_all()
sampler = StatevectorSampler()
result = sampler.run([qc], shots=256).result()
print(result[0].data.meas.get_counts())
For this case, "11" should dominate.
What the diffusion step is really doing
The diffusion block is often described as “reflection about the average.”
That is not poetry. It is the right mental model.
Starting from the uniform superposition, the oracle makes the marked branch negative. The diffusion step then reflects amplitudes around their mean value, which increases the marked amplitude and decreases the others.
Inspecting Grover without measurement
You should also look at Grover with exact simulation:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
qc.h([0, 1])
qc.cz(0, 1)
qc.h([0, 1])
qc.x([0, 1])
qc.cz(0, 1)
qc.x([0, 1])
qc.h([0, 1])
print(Statevector.from_instruction(qc))
This lets you see the amplitude amplification directly instead of only as a final histogram.
One iteration is not a universal rule
For tiny examples, one iteration can be enough. In larger spaces, the best number of iterations depends on the fraction of marked states.
The high-level idea is:
- too few iterations and the marked amplitude is still small
- too many iterations and you rotate past the target
Even if you do not study the full derivation yet, you should know that the iteration count is part of the algorithm design.
A good debugging decomposition
When Grover fails, test each component separately:
- does the oracle flip only the intended branch?
- does the diffusion step preserve the start state subspace correctly?
- after one iteration, do the amplitudes move in the direction you expect?
That decomposition is much more useful than staring at the final counts.
Checkpoint Exercises
- Change the oracle so the marked state is
|00>. - Do the same for
|10>. - Build a 3-qubit oracle for one marked state and run one Grover iteration.
- Compare the statevector before and after the diffusion step.
Try These On QCoder
Quantum Fourier Transform
QFT is the next big milestone after Grover because it teaches you to think in phase structure rather than only marked states.
What QFT does
At a high level, the Quantum Fourier Transform changes basis so that periodic or phase-encoded structure becomes easier to read.
That sentence can sound abstract, so keep one simpler description nearby:
QFT is a structured basis change built from Hadamards and controlled phase rotations.
The 2-qubit QFT by hand
A small QFT circuit is the best place to start.
from qiskit import QuantumCircuit
def qft_2():
qc = QuantumCircuit(2)
qc.h(1)
qc.cp(3.141592653589793 / 2, 0, 1)
qc.h(0)
qc.swap(0, 1)
return qc
print(qft_2())
This already shows the structure:
- apply a Hadamard
- add controlled phase rotations with decreasing angles
- repeat on the next qubit
- optionally reverse qubit order with swaps
Why controlled phases appear
In the Fourier basis, basis states are distinguished by phase patterns. Controlled phase rotations are the local mechanism that builds those patterns one qubit at a time.
You do not need the full matrix formula to understand the circuit behavior:
- Hadamards create local superposition
- controlled phases correlate branches by angle
- swaps restore the output qubit order you usually want
QFT and qubit order are tightly linked
Many QFT confusions are not about Fourier analysis. They are about:
- little-endian ordering
- where the swaps belong
- whether the inverse QFT is being used
That is one reason the earlier qubit-order chapter matters so much.
QFT followed by inverse QFT
The most important first verification is that QFT is invertible and that your implementation is correct.
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
qc.x(0)
qc.compose(qft_2(), inplace=True)
qc.compose(qft_2().inverse(), inplace=True)
print(Statevector.from_instruction(qc))
If the implementation is correct, you recover the original input state.
Looking at phases directly
To get a more useful feel for QFT, inspect the transformed state of a basis vector:
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(2)
qc.x(0)
qc.compose(qft_2(), inplace=True)
print(Statevector.from_instruction(qc))
You will see that the output is not a single basis state. It is a superposition whose amplitudes differ by phase.
That is the key shift in thinking:
- Grover highlights marked branches
- QFT highlights structured phase relationships
When the final swaps matter
Some derivations and library routines omit the trailing swaps and treat the reversed order as acceptable.
That is mathematically fine if you track the ordering consistently. It is a common beginner mistake if you do not.
So the practical rule is:
- keep the swaps while learning
- remove them only when you are very clear about the resulting bit order
Where QFT shows up later
QFT is a building block for:
- phase estimation
- period finding
- arithmetic in the Fourier basis
- shift and modular-action circuits
Even if you do not implement all of those immediately, QFT is worth understanding as a reusable basis-change pattern.
Checkpoint Exercises
- Write a 2-qubit QFT circuit by hand.
- Remove the final swaps and describe exactly what changes.
- Apply QFT and inverse QFT in sequence and verify you recover the input state.
- Inspect the QFT of each 2-qubit basis state and describe the phase pattern you see.
Try These On QCoder
- QPC002 B4, Quantum Fourier Transform
- QPC005 B1, Action in the Fourier Basis
- QPC005 B2, Cyclic Shift in the Fourier Basis
Arithmetic And Modular Thinking
A surprising number of contest problems stop being scary once you reframe them as reversible arithmetic.
Why arithmetic matters
Arithmetic circuits show up in:
- modular algorithms
- phase-estimation-style constructions
- hidden-number tasks
- more advanced QCoder problems
At first, you learn gates.
Then you learn state preparation and oracles.
Then you learn that many harder circuits are really structured data movement:
- add a constant
- shift a register
- swap positions
- apply a permutation reversibly
Start with basis-state mappings
Before you write any gates, write the intended action on basis states.
Examples:
|x, y> -> |y, x>for a swap|x> -> |x + 1 mod 4>for a cyclic shift on two qubits|x, 0> -> |x, f(x)>for a reversible compute step
This habit is more important than any individual gate trick.
Example 1: swap
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.swap(0, 1)
print(qc)
This is a tiny arithmetic circuit because it permutes basis states reversibly.
Example 2: exhaustive testing on small registers
For arithmetic-like circuits, exhaustive testing is often possible and should be routine.
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
def basis_state_circuit(bits: str) -> QuantumCircuit:
qc = QuantumCircuit(len(bits))
for i, bit in enumerate(reversed(bits)):
if bit == "1":
qc.x(i)
return qc
swap = QuantumCircuit(2)
swap.swap(0, 1)
for bits in ["00", "01", "10", "11"]:
qc = basis_state_circuit(bits)
qc.compose(swap, inplace=True)
print(bits, "->", Statevector.from_instruction(qc))
This is exactly the kind of harness you should build for small arithmetic and oracle circuits.
Example 3: cyclic shift as a permutation
A cyclic shift problem is often easier to understand if you forget “quantum” for a moment and ask:
which basis state should each input basis state map to?
If you can answer that cleanly, you can often synthesize the circuit using swaps, x gates, controls, or Fourier-basis tricks depending on the problem statement.
Modular thinking
Many quantum arithmetic tasks are modular by default. Registers have finite size, so operations wrap around.
For a two-qubit register:
3 + 1 mod 4 = 00 - 1 mod 4 = 3
If you forget the modulus, your intended mapping will not even be reversible.
The design checklist
When building arithmetic circuits:
- define the register layout explicitly
- state the intended mapping on basis states
- test small inputs exhaustively
- uncompute any temporary work registers
- only then optimize the circuit
This is much more reliable than guessing from circuit diagrams.
Arithmetic and QFT are connected
Some problems are easiest in the computational basis.
Others become cleaner in the Fourier basis because shifts and additions can become phase operations. That is why the QFT chapter sits nearby in the book. The real subject is not “another algorithm.” It is another way of representing the same transformation.
Checkpoint Exercises
- Implement a 2-qubit swap and verify it on all basis states.
- Implement a cyclic shift on a small register.
- Define a reversible map for adding a known constant to a tiny register.
- Write tests that compare the output basis state for every input.
Try These On QCoder
A Productive Qiskit Workflow
Writing circuits is only half the job. The other half is debugging, testing, and structuring them so you can keep going.
A good local workflow
For small and medium circuits:
- write a circuit constructor function
- inspect the exact state with
Statevector - sample with
StatevectorSampler - compare against the intended mathematical action
That is the core loop of this book because it works.
Write functions, not only loose notebook cells
Even for tiny experiments, it helps to write:
from qiskit import QuantumCircuit
def bell_state() -> QuantumCircuit:
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
return qc
This gives you something you can test, compose, invert, and control.
Use exact simulation aggressively
from qiskit.quantum_info import Statevector
state = Statevector.from_instruction(bell_state())
print(state)
Do this early and often. If the exact state is wrong, repeated sampling will only hide the reason.
Sample only after you know what you expect
from qiskit.primitives import StatevectorSampler
qc = bell_state()
qc.measure_all()
sampler = StatevectorSampler()
result = sampler.run([qc], shots=256).result()
print(result[0].data.meas.get_counts())
Sampling is a confirmation step, not your primary reasoning tool.
Reusable building blocks
As your circuits grow, use:
- helper functions
- named subcircuits
composeinverse- controlled versions of existing subcircuits
This matters for QFT blocks, oracles, reflections, and variational ansatzes.
Parameters keep circuits honest
Many useful circuits should not hard-code every angle.
Use parameters when:
- the same circuit shape is reused with many angles
- you are doing variational optimization
- a QCoder problem gives symbolic or input-dependent rotations
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
theta = Parameter("theta")
qc = QuantumCircuit(1)
qc.ry(theta, 0)
print(qc)
print(qc.assign_parameters({theta: 1.234}))
This is the beginning of a good workflow for QML and more advanced algorithm design.
Testing small reversible circuits
For arithmetic and oracle circuits, a tiny test harness is often enough:
- prepare each small basis input
- apply the circuit
- inspect the resulting basis state or phase
- compare with the intended mapping
Do not wait until a contest submission to discover that your qubit order was wrong.
What to inspect when something is wrong
Check these in order:
- qubit ordering
- missing inverse or uncompute
- wrong control or target
- phase sign mistakes
- measurement added too early
That checklist catches a large fraction of beginner bugs.
Checkpoint Exercises
- Refactor one previous chapter’s solution into a reusable helper.
- Write a small test harness that checks all 2-qubit basis inputs.
- Parameterize a one-qubit rotation circuit and evaluate it at two angles.
- Build a prepare-reflect-unprepare helper and reuse it for two targets.
Quantum Machine Learning With Qiskit
If you want to write quantum machine learning code after this book, you need a realistic starting point, not marketing.
What QML usually looks like in practice
In Qiskit, beginner-friendly QML work usually falls into two families:
- quantum kernels
- variational quantum models
The relevant package is qiskit-machine-learning.
Install the extra package
If you want to run the package-specific examples in this chapter:
uv add qiskit-machine-learning
The circuit design ideas, however, are mostly plain Qiskit ideas you already know.
Quantum kernels
The workflow is usually:
- encode classical data with a feature map
- compare encoded states through a kernel
- feed the kernel to a classical learner such as an SVM
This is conceptually close to the circuit skills you already built:
- data encoding is state preparation
- similarity is controlled by phase and basis structure
A tiny feature-map example
from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
x = ParameterVector("x", 2)
feature_map = QuantumCircuit(2)
feature_map.h([0, 1])
feature_map.rz(x[0], 0)
feature_map.rz(x[1], 1)
feature_map.cx(0, 1)
feature_map.rz((x[0] + x[1]), 1)
feature_map.cx(0, 1)
print(feature_map)
Do not worry yet about whether this is the best feature map. The point is to see what QML circuits look like: parameterized state-preparation blocks.
Variational models
A variational model usually has:
- a feature map
- a parameterized ansatz
- an optimizer
- a loss function
The circuit layer is something like this:
from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
theta = ParameterVector("theta", 4)
ansatz = QuantumCircuit(2)
ansatz.ry(theta[0], 0)
ansatz.ry(theta[1], 1)
ansatz.cx(0, 1)
ansatz.rz(theta[2], 0)
ansatz.ry(theta[3], 1)
print(ansatz)
This is where parameterized circuits and clean Qiskit workflow really matter.
How the earlier chapters support QML
Kernel methods lean on:
- state preparation
- basis changes
- phase reasoning
Variational methods lean on:
- parameterized gates
- entangling layers
- measurement design
- systematic debugging
So QML is not a separate island. It is built from the same circuit vocabulary you have already been learning.
A realistic beginner goal
You do not need to become a QML researcher immediately. A good beginner goal is:
- read a kernel tutorial without getting lost in the circuit layer
- write a small parameterized ansatz
- understand where trainable parameters live
- know how to inspect a circuit before wrapping it in a training loop
If you can do that, the official Qiskit Machine Learning tutorials become much more approachable.
What to watch out for
QML examples can become confusing for reasons that are not specifically “machine learning”:
- feature-map parameters versus trainable parameters
- classical preprocessing versus quantum encoding
- shot noise versus exact simulation
- overcomplicated ansatzes that are hard to debug
Keep the circuit part small until the workflow is stable.
Checkpoint Exercises
- Install
qiskit-machine-learningwithuv add qiskit-machine-learning. - Write a tiny parameterized feature map on 2 qubits.
- Write a simple variational ansatz with a few trainable angles.
- Explain which earlier chapters in this book support kernel methods and which support variational methods.
Next Steps
When you finish this book, do not immediately jump to the hardest paper or the largest tutorial.
Pick a lane and keep your practice deliberate.
If you want to become strong at QCoder
Repeat this loop:
- classify the problem family
- write the intended basis-state or phase action
- implement the smallest correct circuit
- test with exact simulation first
- only then optimize or submit
Then work through contest archives in order instead of randomly.
A useful progression is:
- QPC001 and the
Aproblems in QPC003 for state preparation - QPC002 B2 and QPC003 B2/Ex1 for oracle conversion and phase kickback
- QPC003 B4 to B8 for reflections and Grover
- QPC002 B4 to B8 and QPC005 for QFT and arithmetic thinking
If you want algorithm depth
Study next:
- phase estimation
- Hamiltonian simulation
- amplitude estimation
- fault-tolerant algorithm building blocks
At that point, QFT and reversible arithmetic stop being isolated chapters and start becoming reusable subroutines.
If you want QML depth
Study next:
- richer feature maps
- trainable kernels
- variational classifiers and regressors
- hybrid integrations with classical ML frameworks
Do not skip the workflow discipline from this book. QML code gets messy very quickly if the circuit layer is not under control.
A practical standard
The useful goal is not “I read a quantum book.”
It is:
- I can state the intended transformation
- I can implement it in Qiskit
- I can test it
- I can explain why it works
That standard is enough to keep improving after the book ends.