Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 Statevector and 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:

  1. learn one mental model
  2. type one or two short Qiskit examples
  3. inspect the exact state before measuring
  4. solve the chapter exercises
  5. 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:

  • QuantumCircuit to write circuits
  • Statevector.from_instruction to inspect exact amplitudes
  • StatevectorSampler to test measurement distributions
  • compose, inverse, and control to 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:

  1. read the chapter once without writing code
  2. type every code block yourself
  3. predict the state or counts before running anything
  4. change one gate and explain what changed
  5. 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:

  1. did I misunderstand qubit order?
  2. am I looking at amplitudes or only at counts?
  3. did I accidentally measure too early?
  4. did I forget an inverse or uncompute step?
  5. 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
  • uv
  • mdbook

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:

  • Statevector for exact amplitudes
  • StatevectorSampler for shot-based sampling without backend setup

That order is deliberate:

  1. inspect the state
  2. predict the distribution
  3. 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 outside src/ 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 x create?
  • what does h do to that state?

That habit will scale all the way to Grover, QFT, and QML circuits.

Checkpoint Exercises

  1. Prepare |1>.
  2. Prepare the plus state.
  3. Apply x then h and inspect the resulting state.
  4. Build two different circuits that end in the same state.

Try These On QCoder

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:

  1. inspect the exact state
  2. predict the measurement distribution
  3. 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

  1. Prepare a circuit that always measures 1.
  2. Prepare a circuit with a 50-50 split between 0 and 1.
  3. Build two different circuits with the same counts but different states.
  4. Measure a Bell state and list which two outcomes appear.

Try These On QCoder

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 1 is 1
  • qubit 0 is 0

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 2 should be 1
  • qubit 1 should be 0
  • qubit 0 should be 1

That kind of explicitness prevents a large class of mistakes.

A small table worth memorizing

For two qubits:

  • |00> means q1=0, q0=0
  • |01> means q1=0, q0=1
  • |10> means q1=1, q0=0
  • |11> means q1=1, q0=1

If this table feels natural, later arithmetic and QFT chapters become much easier.

Checkpoint Exercises

  1. Prepare |10> on two qubits.
  2. Prepare |101> on three qubits.
  3. Explain why qc.x(0) on two qubits gives |01>.
  4. Create a small table for all 2-qubit basis states and the qubit values they represent.

Try These On QCoder

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 basis
  • rx, ry, rz: continuous rotations
  • p: 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 what h creates from |0>
  • |-> is what h creates 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:

  1. phase information exists in one basis
  2. change basis
  3. 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 -1 does not change physical behavior
  • changing only one branch by -1 does change interference

So -|1> and |1> are physically equivalent, but (|0> + |1>)/sqrt(2) and (|0> - |1>)/sqrt(2) are not.

Checkpoint Exercises

  1. Prepare the minus state.
  2. Prepare a state with P(1)=3/4.
  3. Verify H Z H = X on |0> and |1>.
  4. Build three different circuits for -|1> and confirm they differ only by global phase.

Try These On QCoder

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

  1. Prepare ( |01> + |10> ) / sqrt(2).
  2. Prepare |10> using exactly one x gate.
  3. Build a circuit that creates a Bell state and then maps it to ( |00> - |11> ) / sqrt(2).
  4. Compare cx(0, 1) and cx(1, 0) on the same input state.

Try These On QCoder

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:

  1. setting amplitudes on one qubit
  2. using controlled gates to route amplitude into the desired basis states
  3. 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:

  1. use ry on qubit 0 to split amplitude between a 0 branch and a 1 branch
  2. use cx(0, 1) to copy the branch label into qubit 1
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

  1. Prepare the Bell state.
  2. Prepare the sign-flipped Bell state.
  3. Prepare the 3-qubit one-hot superposition above.
  4. Prepare a uniform superposition on four qubits.

Try These On QCoder

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:

  1. compute
  2. use
  3. uncompute

That pattern will appear again in arithmetic, reflections, and Grover-style blocks.

Checkpoint Exercises

  1. Write a phase oracle that marks |11>.
  2. Write a phase oracle that marks |10>.
  3. Write a bit-flip oracle for the same condition and compare the two.
  4. Add temporary work qubits to a toy reversible computation and then uncompute them.

Try These On QCoder

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:

  1. phase information exists in one basis
  2. switch basis
  3. 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

  1. Show with Qiskit that H Z H and X act the same on |0> and |1>.
  2. Prepare a minus-state ancilla and test a controlled bit-flip against a phase oracle.
  3. Explain in one paragraph why equal measurement counts can still hide useful phase structure.
  4. Build a circuit that converts a phase condition into a measurable bit condition.

Try These On QCoder

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:

  1. preparing |\psi\rangle
  2. reflecting about |0...0\rangle
  3. 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

  1. Build a reflection about |11>.
  2. Build a reflection about the plus state on one qubit.
  3. Build a reflection about a Bell state using prepare-reflect-unprepare.
  4. Explain why reflections are natural ingredients for search algorithms.

Try These On QCoder

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:

  1. mark the good state with an oracle
  2. 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:

  1. does the oracle flip only the intended branch?
  2. does the diffusion step preserve the start state subspace correctly?
  3. 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

  1. Change the oracle so the marked state is |00>.
  2. Do the same for |10>.
  3. Build a 3-qubit oracle for one marked state and run one Grover iteration.
  4. 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

  1. Write a 2-qubit QFT circuit by hand.
  2. Remove the final swaps and describe exactly what changes.
  3. Apply QFT and inverse QFT in sequence and verify you recover the input state.
  4. Inspect the QFT of each 2-qubit basis state and describe the phase pattern you see.

Try These On QCoder

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 = 0
  • 0 - 1 mod 4 = 3

If you forget the modulus, your intended mapping will not even be reversible.

The design checklist

When building arithmetic circuits:

  1. define the register layout explicitly
  2. state the intended mapping on basis states
  3. test small inputs exhaustively
  4. uncompute any temporary work registers
  5. 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

  1. Implement a 2-qubit swap and verify it on all basis states.
  2. Implement a cyclic shift on a small register.
  3. Define a reversible map for adding a known constant to a tiny register.
  4. 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:

  1. write a circuit constructor function
  2. inspect the exact state with Statevector
  3. sample with StatevectorSampler
  4. 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
  • compose
  • inverse
  • 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:

  1. prepare each small basis input
  2. apply the circuit
  3. inspect the resulting basis state or phase
  4. 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:

  1. qubit ordering
  2. missing inverse or uncompute
  3. wrong control or target
  4. phase sign mistakes
  5. measurement added too early

That checklist catches a large fraction of beginner bugs.

Checkpoint Exercises

  1. Refactor one previous chapter’s solution into a reusable helper.
  2. Write a small test harness that checks all 2-qubit basis inputs.
  3. Parameterize a one-qubit rotation circuit and evaluate it at two angles.
  4. 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:

  1. encode classical data with a feature map
  2. compare encoded states through a kernel
  3. 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

  1. Install qiskit-machine-learning with uv add qiskit-machine-learning.
  2. Write a tiny parameterized feature map on 2 qubits.
  3. Write a simple variational ansatz with a few trainable angles.
  4. 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:

  1. classify the problem family
  2. write the intended basis-state or phase action
  3. implement the smallest correct circuit
  4. test with exact simulation first
  5. only then optimize or submit

Then work through contest archives in order instead of randomly.

A useful progression is:

  • QPC001 and the A problems 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.