{ "cells": [ { "cell_type": "markdown", "id": "pauli-00", "metadata": {}, "source": [ "# Pauli exponentials\n", "\n", "**Download Notebook** - {nb-download}`pauli_exponential.ipynb`\n", "\n", "- A Pauli exponential rotates a register about a Pauli string $P$, such as $Z_0X_1Y_2$.\n", "- `angle(theta)` uses half-turns, so the implemented unitary is\n", "\n", "$$\n", "U_P(\\theta)=e^{-i\\pi\\theta P/2}\n", "=\\cos(\\pi\\theta/2)I-i\\sin(\\pi\\theta/2)P.\n", "$$\n", "\n", "- These rotations form the steps of Trotterized Hamiltonian simulation. For $H=\\sum_j a_jP_j$, the library's time convention gives\n", "\n", "$$\n", "e^{-i\\pi tH/2}\\approx\\left[\\prod_j U_{P_j}(ta_j/r)\\right]^r.\n", "$$\n", "\n", "- This notebook builds a rotation, checks its matrix, and substitutes a different $R_z$ implementation.\n", "- Run from a source checkout with development dependencies installed. The repository default is little endian." ] }, { "cell_type": "code", "execution_count": 1, "id": "pauli-01", "metadata": { "tags": [ "hide-input" ] }, "outputs": [], "source": [ "import numpy as np\n", "import zixy.qubit.pauli as zqp\n", "from scipy.linalg import expm\n", "from guppylang import guppy\n", "from guppylang.std.builtins import array\n", "from guppylang.std.quantum import rz, qubit\n", "from guppylang.std.angles import angle\n", "from guppyalgos.primitives.pauli.pauli_exp import pauli_exp\n", "from guppyalgos.primitives.subroutines.ladders import CXLadderLinear\n", "from guppyalgos.tests.helpers import get_unitary, assert_allclose_ignorephase" ] }, { "cell_type": "markdown", "id": "pauli-02", "metadata": {}, "source": [ "## 1. Build the rotation\n", "\n", "- Describe the operator with `zqp.String`; use `zqp.RealTermSum` for a weighted Hamiltonian.\n", "- `pauli_exp` builds a [guppy](https://docs.quantinuum.com/guppy/language_guide/language_guide_index.html) function for that string. Its angle is supplied when the function is called.\n", "- The circuit changes to the Z basis, collects parity with a CX ladder, applies $R_z$, then reverses the ladder and basis changes.\n", "- Here we choose `CXLadderLinear`. Omitting the optional arguments uses `CXLadderLog` and `rz`." ] }, { "cell_type": "code", "execution_count": 2, "id": "pauli-03", "metadata": {}, "outputs": [], "source": [ "n_state_qubits = 3\n", "pauli_string = zqp.String.from_str(\"Z0 X1 Y2\", n_state_qubits)\n", "cx_ladder_method = CXLadderLinear\n", "pauli_gadget = pauli_exp(pauli_string, n_state_qubits, cx_ladder_method, rz)\n", "theta = 0.7\n", "\n", "\n", "@guppy\n", "def rotation(qreg: array[qubit, n_state_qubits]) -> None:\n", " pauli_gadget(qreg, angle(theta))" ] }, { "cell_type": "markdown", "id": "pauli-04", "metadata": {}, "source": [ "## 2. Check the full unitary\n", "\n", "- `get_unitary` simulates each computational-basis input and collects the output columns.\n", "- Compare with the matrix exponential above, allowing an overall global phase. This checks relative phases as well as probabilities." ] }, { "cell_type": "code", "execution_count": 3, "id": "pauli-05", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "All 8 basis inputs match the Pauli exponential, up to global phase.\n" ] } ], "source": [ "guppy_u = get_unitary(rotation, n_state_qubits)\n", "pauli_mat = np.asarray(pauli_string.to_sparse_matrix(True).todense())\n", "u_mat = expm(-0.5j * np.pi * theta * pauli_mat)\n", "assert_allclose_ignorephase(u_mat, guppy_u)\n", "print(f\"All {2**n_state_qubits} basis inputs match the Pauli exponential, up to global phase.\")" ] }, { "cell_type": "markdown", "id": "pauli-06", "metadata": {}, "source": [ "## 3. Swap in a repeat-until-success rotation\n", "\n", "- `rz_method` accepts another function with the same qubit-and-angle interface. The surrounding Pauli gadget stays unchanged.\n", "- `repeat_until_success_rz` uses a resource qubit and measurement feedback. This demonstration uses `dummy_theta_resource_state`, which prepares its resource with a direct rotation; it does not demonstrate a rotation-synthesis resource saving.\n", "- Replay three failures followed by success and verify the output for $|+\\rangle^{\\otimes3}$:\n", "\n", "$$\n", "|\\psi_{\\mathrm{out}}\\rangle=U_P(0.7)|+\\rangle^{\\otimes3}.\n", "$$" ] }, { "cell_type": "code", "execution_count": 4, "id": "pauli-07", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Three forced failures followed by success: output state matches the target.\n" ] } ], "source": [ "from guppyalgos.primitives.rotations import repeat_until_success_rz, dummy_theta_resource_state\n", "from guppyalgos.utils import qarray, transversal\n", "from guppylang.std.debug import state_output\n", "from guppylang.std.quantum import discard_array, h\n", "from selene_sim import QuantumReplay, Quest\n", "rus_rz = repeat_until_success_rz(dummy_theta_resource_state)\n", "\n", "rus_gadget = pauli_exp(pauli_string, n_state_qubits, cx_ladder_method, rus_rz)\n", "\n", "theta = 0.7\n", "\n", "@guppy\n", "def main() -> None:\n", " qreg = qarray(n_state_qubits)\n", " transversal(h, qreg)\n", " rus_gadget(qreg, angle(theta))\n", " state_output(\"result_state\", qreg)\n", " discard_array(qreg)\n", "\n", "n_repeats = 3\n", "# fail 3 times and then succeed\n", "desired_rus_measurements = [[False] * n_repeats + [True]]\n", "\n", "rus_replay_sim = QuantumReplay(simulator=Quest(), measurements=desired_rus_measurements)\n", "em_result = (\n", " main.emulator(n_state_qubits+1).with_simulator(rus_replay_sim).with_shots(1).run()\n", ")\n", "expected_state = u_mat @ (np.ones(2**n_state_qubits) / np.sqrt(2**n_state_qubits))\n", "for shot_result in em_result.results:\n", " actual_state = Quest.extract_states_dict(shot_result)[\"result_state\"].get_single_state()\n", " assert_allclose_ignorephase(expected_state, actual_state)\n", "print(\"Three forced failures followed by success: output state matches the target.\")" ] }, { "cell_type": "markdown", "id": "pauli-08", "metadata": {}, "source": [ "- Continue with the {doc}`measurement-based Pauli exponential ` to replace the ladder construction.\n", "- The {doc}`phase-estimation demo ` shows how controlled Trotter steps built from Pauli exponentials estimate an energy." ] } ], "metadata": { "kernelspec": { "display_name": ".venv (3.13.1)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.13" } }, "nbformat": 4, "nbformat_minor": 5 }