{ "cells": [ { "cell_type": "markdown", "id": "83cb2363", "metadata": {}, "source": [ "# Circuits, amplitudes and the phase you cannot see in a histogram\n", "\n", "**Level:** Core · **Book connections:** P01, P03, P04, P05, P06, P11, F04, F11\n", "\n", "After this notebook you can:\n", "\n", "- Build H, X, Y and Z circuits from a known initial state.\n", "- Read complex amplitudes as well as probabilities.\n", "- Use a second gate to turn a relative phase into an observable difference.\n", "\n", "Run cells from top to bottom in the pinned environment described in `README.md`. All default execution is local. Exact amplitudes are simulator information; a QPU supplies sampled outcomes." ] }, { "cell_type": "code", "execution_count": null, "id": "7b09d981", "metadata": {}, "outputs": [], "source": [ "from importlib.metadata import version\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from IPython.display import display\n", "from qiskit import QuantumCircuit\n", "from qiskit.quantum_info import Statevector\n", "\n", "EXPECTED = {\"qiskit\": \"2.6.1\", \"qiskit-aer\": \"0.18.2\", \"qiskit-ibm-runtime\": \"0.51.0\"}\n", "for package, expected in EXPECTED.items():\n", " actual = version(package)\n", " assert actual == expected, f\"Use the supplied environment: {package}={actual}, expected {expected}\"\n", "print(\"Verified package versions:\", EXPECTED)\n", "plt.rcParams.update({\"figure.figsize\": (8, 3.5), \"font.size\": 11, \"axes.spines.top\": False,\n", " \"axes.spines.right\": False, \"axes.prop_cycle\": plt.cycler(color=[\"#006DAE\", \"#00A6B2\", \"#9E4A78\"])})\n", "SEED = 20261106\n", "\n", "def draw(circuit):\n", " \"\"\"Render a real circuit; each wire is a qubit, time runs left to right.\"\"\"\n", " fig = circuit.draw(output=\"mpl\", fold=90, style=\"iqp\")\n", " display(fig)\n", " plt.close(fig)\n", "\n", "def book_label(qiskit_label):\n", " \"\"\"For a single register, show q0 first as in the book.\"\"\"\n", " return qiskit_label[::-1]" ] }, { "cell_type": "markdown", "id": "38a27569", "metadata": {}, "source": [ "**Read the wires and bit strings carefully.** Qiskit draws `q0` on the top wire, but a\n", "standard statevector basis label or measured single-register count key is written\n", "`q(n−1)…q1q0`: `q0` has integer weight $2^0$. The book often writes `q0` first.\n", "These notebooks keep native Qiskit labels in calculations and use `book_label`\n", "only for an explicitly labelled display. Reversing a label changes the display;\n", "it does not apply a SWAP gate. All count examples here use one classical register\n", "with measurement `qj → cj`; do not reverse arbitrary multi-register strings." ] }, { "cell_type": "markdown", "id": "6fd4f47b", "metadata": {}, "source": [ "## 1. Prepare, transform, inspect\n", "\n", "A new qubit starts in $|0\\rangle$. A Hadamard gate makes equal amplitudes.\n", "Starting in $|1\\rangle$ changes the relative sign:\n", "\n", "$$H|0\\rangle=\\frac{|0\\rangle+|1\\rangle}{\\sqrt2},\\qquad\n", " H|1\\rangle=\\frac{|0\\rangle-|1\\rangle}{\\sqrt2}.$$\n", "\n", "Both have $P(0)=P(1)=1/2$. A histogram alone therefore loses information we\n", "need to explain interference. Predict the sign of the second amplitude, then\n", "run the cell. You can explore immediately; there is no text-entry gate." ] }, { "cell_type": "code", "execution_count": null, "id": "35306bf7", "metadata": {}, "outputs": [], "source": [ "plus_circuit = QuantumCircuit(1, name=\"H|0>\")\n", "plus_circuit.h(0)\n", "minus_circuit = QuantumCircuit(1, name=\"H|1>\")\n", "minus_circuit.x(0)\n", "minus_circuit.h(0)\n", "plus = Statevector.from_instruction(plus_circuit)\n", "minus = Statevector.from_instruction(minus_circuit)\n", "draw(minus_circuit)\n", "for label, state in [(\"H|0>\", plus), (\"H|1>\", minus)]:\n", " print(label, \"amplitudes:\", state.data, \"probabilities:\", state.probabilities())\n", "np.testing.assert_allclose(plus.data, [1/np.sqrt(2), 1/np.sqrt(2)], atol=1e-12)\n", "np.testing.assert_allclose(minus.data, [1/np.sqrt(2), -1/np.sqrt(2)], atol=1e-12)\n", "np.testing.assert_allclose(plus.probabilities(), minus.probabilities(), atol=1e-12)" ] }, { "cell_type": "markdown", "id": "4fead36c", "metadata": {}, "source": [ "## 2. Draw the amplitudes\n", "\n", "The height is an amplitude, not a probability. Squaring its magnitude gives a\n", "probability. Negative bars are meaningful; probabilities themselves stay\n", "non-negative. For complex amplitudes, show both real and imaginary parts." ] }, { "cell_type": "code", "execution_count": null, "id": "f5b8412d", "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(1, 2, figsize=(9, 3), sharey=True)\n", "for ax, state, title in zip(axes, [plus, minus], [\"H|0>: same signs\", \"H|1>: opposite signs\"]):\n", " ax.bar([0, 1], state.data.real, width=.35, label=\"Real\")\n", " ax.bar(np.array([0, 1])+.35, state.data.imag, width=.35, label=\"Imaginary\")\n", " ax.axhline(0, color=\"black\", linewidth=.7)\n", " ax.set(xticks=[.175, 1.175], xticklabels=[\"|0>\", \"|1>\"], ylim=(-1, 1), title=title, ylabel=\"Amplitude\")\n", "axes[0].legend()\n", "plt.tight_layout()\n", "plt.show()\n", "\n", "plus_recombined = plus.evolve(plus_circuit)\n", "minus_recombined = minus.evolve(plus_circuit)\n", "np.testing.assert_allclose(plus_recombined.probabilities(), [1, 0], atol=1e-12)\n", "np.testing.assert_allclose(minus_recombined.probabilities(), [0, 1], atol=1e-12)\n", "print(\"After another H:\", plus_recombined.probabilities(), minus_recombined.probabilities())" ] }, { "cell_type": "markdown", "id": "feea7094", "metadata": {}, "source": [ "## 3. Include Y, and separate global from relative phase\n", "\n", "$Y$ flips the computational basis and introduces an imaginary factor:\n", "\n", "$$Y=\\begin{pmatrix}0&-i\\\\i&0\\end{pmatrix},\\qquad Y|0\\rangle=i|1\\rangle.$$\n", "\n", "The entire state $i|1\\rangle$ differs from $|1\\rangle$ by a global phase;\n", "they predict the same results for every measurement. The minus sign between\n", "components in $|{-}\\rangle$ is a relative phase, which the previous H exposed." ] }, { "cell_type": "code", "execution_count": null, "id": "9e4ef0ce", "metadata": {}, "outputs": [], "source": [ "for name in [\"h\", \"x\", \"y\", \"z\"]:\n", " qc = QuantumCircuit(1, name=name.upper())\n", " getattr(qc, name)(0)\n", " state = Statevector.from_instruction(qc)\n", " print(f\"{name.upper()}|0>: {state.data}\")\n", "y_circuit = QuantumCircuit(1)\n", "y_circuit.y(0)\n", "y_state = Statevector.from_instruction(y_circuit)\n", "np.testing.assert_allclose(y_state.data, [0, 1j], atol=1e-12)\n", "assert y_state.equiv(Statevector.from_label(\"1\"))\n", "assert not plus.equiv(minus)" ] }, { "cell_type": "markdown", "id": "c8c3d696", "metadata": {}, "source": [ "## Checkpoint and practice\n", "\n", "Choose before running: after `X → H → H`, is the result **0**, **1**, or\n", "**50/50**? The result is **1**: the two H gates undo one another, leaving X.\n", "A 50/50 answer describes the intermediate state after the first H.\n", "\n", "1. Replace the first X with Y. Which amplitudes change? Which final measurement changes?\n", "2. Build `H → Z → H` and explain why its output matches X on $|0\\rangle$.\n", "3. Write an assertion that catches a missing Z even though the distribution\n", " immediately before the final H remains 50/50.\n", "\n", "**Takeaway:** keep amplitudes available while debugging a simulator; use a\n", "suitable measurement basis when testing phase on hardware." ] }, { "cell_type": "markdown", "id": "0e71475e", "metadata": {}, "source": [ "## Sources\n", "\n", "- [IBM: bit ordering](https://quantum.cloud.ibm.com/docs/en/guides/bit-ordering)\n", "- [IBM: Statevector API](https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.quantum_info.Statevector)\n", "- [IBM: Operator API](https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.quantum_info.Operator)" ] } ], "metadata": { "kernelspec": { "display_name": "Quantum book (pinned Python)", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.13.2" }, "quantum_book": { "chapters": [ "P01", "P03", "P04", "P05", "P06", "P11", "F04", "F11" ], "expected_packages": { "qiskit": "2.6.1", "qiskit-aer": "0.18.2", "qiskit-ibm-runtime": "0.51.0" }, "level": "Core", "network_on_default_run": false, "verified_release_date": "2026-11-06" } }, "nbformat": 4, "nbformat_minor": 5 }