2 Prepare quantum states
This chapter explains how quantum algorithms begin with deliberate state preparation, then evolve and measure to extract answers with high probability. It motivates why preparing specific superpositions matters across algorithms such as Deutsch–Jozsa, Grover and its variants, HHL, and variational methods, and connects the topic to practical needs like loading classical data via qRAM. The focus is on preparing real-amplitude states, progressing from single-qubit to arbitrary multi-qubit constructions, while simultaneously introducing how to program, run, and observe these behaviors in Qiskit and Q# using simulators and lightweight debugging tools.
The single-qubit problem is solved by choosing a rotation around the Y axis: apply Ry(theta) to |0>, with theta computed as 2 * atan2(beta, alpha) to get the correct signs of sine and cosine and avoid unintended phases. The chapter highlights why atan2 is safer than alternatives and notes how global phase can matter when controlled variants are embedded in larger routines. It then shows how to inspect states on simulators without measurements: in Qiskit via Aer’s save_statevector/get_statevector, and in Q# via DumpMachine. Practical language nuances are covered, including Q#’s requirement to Reset qubits before release and differences in endianness conventions that affect how amplitudes map to basis-state indices.
To scale up, the chapter builds a constructive recipe. For two qubits, first prepare the least-significant qubit with amplitudes proportional to the norms of the even and odd branches, then refine the most-significant qubit conditionally using controlled and controlled-on-zero variants of the single-qubit routine. This pattern generalizes recursively: split amplitudes into even and odd groups, compute m0 and m1, prepare the first qubit with those weights, and invoke controlled sub-preparations on the remaining qubits, avoiding explicit normalization thanks to atan2’s use of relative values. Implementations use custom gates and control patterns in Qiskit (to_gate, control with ctrl_state) and Q# operations with adjoint/controlled specializations and ApplyControlledOnInt. The chapter closes by emphasizing unit testing on simulators—pytest for Qiskit and Python-hosted tests for Q# that fetch simulator state—to validate correctness across diverse cases, while noting that more efficient state-preparation methods exist for larger systems.
Any quantum algorithm can be broken down into several steps: prepare the initial state, evolve it following the algorithm, and measure the end state to get the result. The goal of many algorithms is to end up in a state in which the result of the final measurement is a correct answer with high probability. (Each block of eight bars represents the eight amplitudes of the basis states in a three-qubit quantum state.)
Different inverse trigonometric functions handle the signs of sine and cosine differently. The angle returned by the function \(\mathrm{atan2}\) has correct signs of both sine and cosine. The function \(\mathrm{atan}\) preserves only their relative sign, always returning an angle with a non-negative cosine. Functions \(\arccos\) and \(\arcsin\) ignore the sign of sine and cosine, respectively, assuming it is always non-negative.
An example of code actions provided by Azure Quantum Development Kit extension.
Preparing a two-qubit state. Each set of four bars represents the amplitudes of the basis states: from left to right, \(\ket{0}\), \(\ket{1}\), \(\ket{2}\) and \(\ket{3}\). Light bars indicate zero amplitudes (basis states not present in the superposition state), dark bars — non-zero amplitudes (positive or negative). We prepare the least significant qubit in a superposition state and then adjust the state of the most significant qubit depending on the state of the least significant qubit using controlled gates. The left path handles the case of the least significant qubit being in the \(\ket{0}\) state, the right one — the \(\ket{1}\) state. Striped bars represent the amplitudes of the basis states that are involved in each path.
To prepare an \(n\)-qubit state, we prepare the least significant qubit in a superposition state and then adjust the state of the remaining \(n-1\) qubits. Same as in the two-qubit case, controlled variants of the state preparation operation allow us to adjust the states of the most significant \(n-1\) qubits conditionally, with separate handling of the least significant qubit being in \(\ket{0}\) and \(\ket{1}\) states.
Summary
- Any quantum algorithm can be represented as several steps: preparing the initial state, evolve it following the specific algorithm, and measure the final state to get the result. The goal of the algorithm is either to get the correct answer as a result of the final measurement with high probability or to estimate the probabilities of the outcomes.
- The goal of state preparation task is: given a freshly initialized system in the \(\ket{0}\) state, change its state to the superposition state described by the given list of amplitudes.
- State preparation operation can be implemented recursively, by preparing one of the qubits in a certain superposition state and then using it to control state preparation operations applied to the remaining qubits.
- Quantum simulators are classical programs that allow you to run small quantum programs without access to quantum hardware.
- Using simulators during quantum software development allows you to inspect the state of the program mid-execution, which is not possible when running the program on a quantum device, and to test your code easily.
FAQ
What is “state preparation” and why is it central to quantum algorithms?
State preparation is the step that transforms freshly initialized qubits (typically all in |0>) into a target superposition. Many quantum algorithms follow the flow: prepare → evolve → measure. They aim to amplify amplitudes of basis states encoding correct answers so that a final measurement yields the right result with high probability. Even algorithms focused on estimating probabilities or phases still depend on preparing suitable initial states.How do I prepare a single-qubit state α|0> + β|1> (real amplitudes)?
Use a Y-rotation: apply Ry(θ) to |0>, where θ = 2 · atan2(β, α). This yields cos(θ/2)=α and sin(θ/2)=β, producing the desired state without introducing unintended phases.Why use atan2 instead of atan/arccos/arcsin for rotation angles?
- atan2(β, α) preserves the correct signs of both sine and cosine over (−π, π], avoiding quadrant errors.- atan(β/α) loses information when α ≤ 0 and needs special handling for α=0.
- arccos(α) and arcsin(β) restrict angles to limited ranges and ignore the sign of the other component, often producing a wrong relative phase.
How can I see the prepared state on a simulator in Qiskit and Q#?
- Qiskit: use AerSimulator(method='statevector'), call circuit.save_statevector(), run the circuit, then extract with result.get_statevector().- Q#: call DumpMachine() (Std.Diagnostics) to print amplitudes, probabilities, and phases; when driving from Python, use qsharp.dump_machine().as_dense_state().
What endian conventions do Qiskit and Q# use for basis-state ordering?
There’s no universal standard. Qiskit uses little-endian ordering (index 0 is least significant qubit), while Q# typically orders states as big-endian in printed outputs. Be mindful when mapping amplitude arrays to basis states and when interpreting printed state vectors.How do I prepare an arbitrary two-qubit state from amplitudes [a0, a1, a2, a3]?
- Prepare the least significant qubit in state: [sqrt(a0²+a2²)]|0> + [sqrt(a1²+a3²)]|1> using the single-qubit routine.- Controlled-on-0 (by the LSB), prepare the most significant qubit to turn |0,0> into a0|00> + a2|01>.
- Controlled-on-1 (by the LSB), prepare the MSB to turn |1,0> into a1|10> + a3|11>.
Use controlled variants of the same single-qubit prep; no explicit renormalization is needed because the angles use atan2.
What is a “controlled-on-zero” gate and how do I implement it in Qiskit and Q#?
- Concept: apply a gate only when all control qubits are |0> (an “open control”).- Qiskit: gate.control(num_ctrls, ctrl_state=0) and then append with controls first in the qubit list.
- Q#: use ApplyControlledOnInt(0, Op, controls, (targets, ...)) or equivalently X on controls, Controlled Op, then X again (manual sandwich). Ensure the operation has a Controlled specialization (is Adj + Ctl when appropriate).
How do I scale state preparation to n qubits efficiently (real amplitudes)?
Use a recursive scheme:- Split amplitudes into even and odd indices (by LSB): even → a0, a2, …; odd → a1, a3, …
- Compute m0 = ||even||₂ and m1 = ||odd||₂; prepare LSB as m0|0> + m1|1> via single-qubit prep.
- Controlled-on-0, recursively prepare the (n−1)-qubit state from even amplitudes on the remaining qubits.
- Controlled-on-1, recursively prepare the (n−1)-qubit state from odd amplitudes.
Because angles are computed with atan2, you don’t need to pre-normalize subarrays.
How do I test state-preparation code in Qiskit and Q#?
- Qiskit: with pytest, save_statevector() and compare result.get_statevector().data to the target amplitudes using approximate float comparison. Test diverse cases: basis states, zeros present, distinct amplitudes, negative signs.- Q#: from Python, use qsharp.init(), qsharp.eval() to allocate and call your Q# op, then qsharp.dump_machine().as_dense_state() and compare to expected amplitudes. Favor property-driven cases over just equal superpositions, which can mask bugs.
Quantum Programming in Depth ebook for free