{ "cells": [ { "cell_type": "markdown", "id": "f0c3815e", "metadata": {}, "source": [ "\n", "\n", "**Download Notebook** - {nb-download}`zixy_phase_estimation.ipynb`\n", "# Trotterized phase estimation with Zixy\n" ] }, { "cell_type": "markdown", "id": "97727c86", "metadata": {}, "source": [ "\n", "This notebook demonstrates trotterized QPE workflows using building blocks\n", "from `guppyalgos`, with classical comparisons kept local for context and debugging.\n", "\n", "Flow:\n", "1. Canonical one-qubit $0.5\\,Z_0$ toy model.\n", "2. Small commuting example.\n", "3. Small noncommuting example.\n", "4. H2 STO-3G Jordan-Wigner demo.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ae3bb887", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import zixy.qubit.pauli as zqp\n", "from scipy.linalg import expm\n", "from typing import no_type_check\n", "\n", "from guppylang import guppy\n", "from guppylang.defs import GuppyFunctionDefinition\n", "from guppylang.std.angles import angle\n", "from guppylang.std.builtins import array, comptime, output, nat\n", "from guppylang.std.quantum import (\n", " discard_array,\n", " measure_array,\n", " qubit,\n", " ry,\n", " x,\n", " collect_measurements\n", ")\n", "\n", "from guppyalgos.algorithms.phase_estimation import qpe\n", "from guppyalgos.primitives.state_preparation import uniform_state\n", "from guppyalgos.algorithms.time_evolution.trotter import cntrl_trotter_first_order\n", "from guppyalgos.utils import (\n", " fixed_point_to_float,\n", " phase_distance_mod_2,\n", " phase_to_energy_qpe,\n", " qarray,\n", ")\n", "from zixy.fermion.mappings import JordanWignerMapper\n", "from zixy.fermion.operator.general import String as FermionString\n", "from zixy.qubit.pauli import RealTermSum as RealPauliOperator" ] }, { "cell_type": "markdown", "id": "cbf21911", "metadata": {}, "source": [ "\n", "## Shared Helpers\n", "\n", "These helpers are used for classical analysis and comparisons in the examples.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "2eb1b3cb", "metadata": {}, "outputs": [], "source": [ "from guppyalgos.utils.python.analysis import (\n", " analyze_qubit_pauli_operator,\n", " dominant_measured_phase,\n", " dominant_trotter_phase,\n", " measurement_dataframe,\n", ")\n" ] }, { "cell_type": "markdown", "id": "ef4ce782", "metadata": {}, "source": [ "\n", "## Shared Helpers (State Prep)\n", "\n", "These helpers only support the noncommuting single\u2011qubit preparation.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "a5f69758", "metadata": {}, "outputs": [], "source": [ "#extra helpers for non-commuting\n", "def canonical_real_single_qubit_state(state: np.ndarray) -> np.ndarray:\n", " \"\"\"Fix the global sign of a real 1-qubit state so an Ry preparation is unique.\"\"\"\n", " state = np.real_if_close(np.asarray(state, dtype=complex))\n", " if np.iscomplexobj(state):\n", " raise ValueError(\"Expected a real 1-qubit state up to global phase\")\n", " state = np.asarray(state, dtype=float)\n", " state /= np.linalg.norm(state)\n", " if state[0] < 0 or (np.isclose(state[0], 0.0) and state[1] < 0):\n", " state = -state\n", " return state\n", "\n", "\n", "def ry_angle_for_real_single_qubit_state(state: np.ndarray) -> float:\n", " \"\"\"Return the single-qubit Ry angle that prepares a real normalized state.\"\"\"\n", " state = canonical_real_single_qubit_state(state)\n", " return float(2 * np.arctan2(state[1], state[0]))" ] }, { "cell_type": "markdown", "id": "1009b6dd", "metadata": {}, "source": [ "\n", "## Toy model\n", "\n", "For $H=\\tfrac{1}{2}Z$ and $t=-4\\phi$,\n", "\n", "$$\n", "e^{-i \\frac{\\pi}{2} t H}\n", "=\n", "e^{i\\pi\\phi Z}\n", "=\n", "\\begin{pmatrix}\n", "e^{i\\pi\\phi} & 0 \\\\\n", "0 & e^{-i\\pi\\phi}\n", "\\end{pmatrix}.\n", "$$\n" ] }, { "cell_type": "code", "execution_count": null, "id": "7fde0c87", "metadata": {}, "outputs": [], "source": [ "# Classical analysis\n", "phi = 0.75 # phase to encode\n", "rz_ham_op = RealPauliOperator.from_str(\"(0.5, Z0)\")\n", "n_rz_qubits = len(rz_ham_op.qubits)\n", "rz_prepared_state = np.array([1.0, 0.0], dtype=complex)\n", "\n", "\n", "### tools for trotter analysis (no error here)\n", "# Under the current pauli_exp / trotter convention, choosing time_step = -4 * phi\n", "# reproduces the same one-qubit unitary as CRz(-2 * pi * phi) in the canonical example.\n", "rz_time_step = -4 * phi\n", "\n", "rz_analysis = analyze_qubit_pauli_operator(\n", " rz_ham_op, rz_time_step, rz_prepared_state, little_endian=True\n", ")\n", "rz_analysis_row = rz_analysis.iloc[0]\n", "rz_target_phase = float(rz_analysis_row[\"target_phase\"])\n", "rz_ham_mat = rz_analysis_row[\"ham_mat\"]\n", "rz_unitary_from_ham = rz_analysis_row[\"exact_step\"]\n", "rz_unitary_direct = np.diag([\n", " np.exp(1j * np.pi * phi),\n", " np.exp(-1j * np.pi * phi),\n", "])\n", "assert np.allclose(rz_unitary_from_ham, rz_unitary_direct)\n" ] }, { "cell_type": "markdown", "id": "1cf66c12", "metadata": {}, "source": [ "\n", "## Phase Kickback (Single Eigenphase)\n", "\n", "For an eigenstate $|\\psi\\rangle$ with phase $\\phi$ (mod 2),\n", "\n", "$$\n", "U^{k}|\\psi\\rangle = e^{i\\pi k\\phi}|\\psi\\rangle\n", "\\quad\\Rightarrow\\quad\n", "\\mathrm{C}U^{k}\\big(|+\\rangle\\otimes|\\psi\\rangle\\big)\n", "=\n", "\\frac{1}{\\sqrt{2}}\\Big(|0\\rangle\\otimes|\\psi\\rangle + e^{i\\pi k\\phi}|1\\rangle\\otimes|\\psi\\rangle\\Big).\n", "$$" ] }, { "cell_type": "markdown", "id": "6a2d6e01", "metadata": {}, "source": [ "\n", "## QPE Wrapper\n", "\n", "Make an abstract QPE program template once, then reuse it for each operator\n", "example below.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "f52f3f9d", "metadata": {}, "outputs": [], "source": [ "from guppylang.std.qsystem.helios import collect_measurements\n", "n_ancilla = 4\n", "\n", "def make_trotter_qpe_program[n_state_q: nat](\n", " n_ancilla: int,\n", " n_state_qubits: int,\n", " state_preparation: GuppyFunctionDefinition[[array[qubit, n_state_q]], None],\n", " power_oracle: GuppyFunctionDefinition[[qubit, array[qubit, n_state_q], int], None],\n", ") -> GuppyFunctionDefinition[[], None]:\n", " \"\"\"Construct a canonical QPE entrypoint for a single system register.\"\"\"\n", " ancilla_prep_function = uniform_state(2**n_ancilla)\n", "\n", " @guppy\n", " @no_type_check\n", " def canonical_trotter_qpe_program() -> None:\n", " state_reg = qarray(n_state_qubits)\n", " phase_reg = qarray(n_ancilla)\n", "\n", " state_preparation(state_reg)\n", " ancilla_prep_function(phase_reg)\n", " qpe(phase_reg, state_reg, power_oracle)\n", "\n", " output(\"qpe_bitstring\", collect_measurements(measure_array(phase_reg)))\n", " discard_array(state_reg)\n", "\n", " return canonical_trotter_qpe_program\n" ] }, { "cell_type": "code", "execution_count": null, "id": "e3da9d2d", "metadata": {}, "outputs": [], "source": [ "# Guppy methods for simple wrapped Rz ham\n", "controlled_rz_trotter_step = cntrl_trotter_first_order(rz_ham_op, n_rz_qubits)\n", "\n", "# Powers of U are implemented as repeated applications of the controlled Trotter steps.\n", "@guppy\n", "def power_oracle(\n", " control: qubit,\n", " state_reg: array[qubit, n_rz_qubits],\n", " power: int,\n", ") -> None:\n", " 'QPE-compatible power oracle built from repeated controlled Trotter steps.'\n", " for _ in range(power):\n", " controlled_rz_trotter_step(control, state_reg, rz_time_step)\n", "\n", "@guppy\n", "def state_preparation(\n", " system_register: array[qubit, n_rz_qubits],\n", ") -> None:\n", " \"\"\"Prepare the |0> eigenstate of the toy Rz Hamiltonian.\"\"\"\n", " ry(system_register[0], angle(0)) #identity!\n", "\n", "\n", "# Assemble the simple QPE program\n", "rz_qpe_program = make_trotter_qpe_program(\n", " n_ancilla=n_ancilla,\n", " n_state_qubits=n_rz_qubits,\n", " state_preparation=state_preparation,\n", " power_oracle=power_oracle,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "8e63239f", "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "target_phase dominant_trotter_phase dominant_trotter_overlap trotter_step_error\n 0.75 0.75 1.0 0.0\n" ] }, { "output_type": "stream", "name": "stdout", "text": [ "bitstring counts phase empirical_probability\n 0110 500 0.75 1.0\n" ] } ], "source": [ "# Run and inspect the simple qpe program\n", "n_shots = 500\n", "\n", "sim_result = (\n", " rz_qpe_program.emulator(n_qubits=n_ancilla + n_rz_qubits)\n", " .with_seed(5)\n", " .with_shots(n_shots)\n", " .run()\n", ")\n", "rz_counts = sim_result.register_counts()[\"qpe_bitstring\"]\n", "dominant_key, dominant_counts, dominant_phase = dominant_measured_phase(rz_counts)\n", "rz_dominant_trotter_phase, rz_dominant_trotter_weight = dominant_trotter_phase(\n", " rz_analysis\n", ")\n", "\n", "rz_analysis_summary = rz_analysis[\n", " [\n", " \"target_phase\",\n", " \"dominant_trotter_phase\",\n", " \"dominant_trotter_overlap\",\n", " \"trotter_step_error\",\n", " ]\n", "]\n", "rz_counts_df = measurement_dataframe(rz_counts)\n", "\n", "print(rz_analysis_summary.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n", "print(rz_counts_df.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n" ] }, { "cell_type": "markdown", "id": "045938b6", "metadata": {}, "source": [ "\n", "## First-Order Trotterization\n", "\n", "We approximate time evolution with a first-order product formula:\n", "\n", "$$\n", "U(t) = e^{-i \\frac{\\pi}{2} t \\sum_k c_k P_k}\n", "\\;\\approx\\;\n", "\\prod_k e^{-i \\frac{\\pi}{2} t\\, c_k P_k},\n", "$$\n", "where each $P_k$ is a Pauli string and $c_k\\in\\mathbb{R}$.\n", "\n", "See the {doc}`Trotterized Hamiltonian simulation notebook ` for more details.\n", "\n", "In this case we do controlled-U using the conjugation pattern. \n" ] }, { "cell_type": "markdown", "id": "a5d9955d", "metadata": {}, "source": [ "## Three Commuting Terms\n", "\n", "We can also use a small Hamiltonian with three commuting Pauli terms,\n", "`Z0`, `Z1`, and `Z0 Z1`. In this case the first-order Trotter step is exact,\n", "so there is no Trotter error to confuse the phase-estimation example.\n" ] }, { "cell_type": "markdown", "id": "3b4eb1fe", "metadata": {}, "source": [ "\n", "### Commuting Case\n", "\n", "If all $P_k$ commute, the product formula is exact:\n", "\n", "$$\n", "\\prod_k e^{-i \\frac{\\pi}{2} t\\, c_k P_k}\n", "=\n", "e^{-i \\frac{\\pi}{2} t \\sum_k c_k P_k}.\n", "$$\n" ] }, { "cell_type": "code", "execution_count": null, "id": "7787721a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.9238135806952475e-16\n" ] } ], "source": [ "# Classical analysis for the commuting ham\n", "commuting_ham_op = RealPauliOperator.from_str(\"(0.5, Z0), (0.25, Z1), (0.25, Z0 Z1)\")\n", "n_commuting_qubits = len(commuting_ham_op.qubits)\n", "commuting_prepared_state = np.array([0.0, 0.0, 0.0, 1.0], dtype=complex)\n", "\n", "commuting_time_step = 1.0\n", "\n", "commuting_analysis = analyze_qubit_pauli_operator(\n", " commuting_ham_op,\n", " commuting_time_step,\n", " commuting_prepared_state,\n", " little_endian=True,\n", ")\n", "commuting_analysis_row = commuting_analysis.iloc[0]\n", "commuting_exact_step = commuting_analysis_row[\"exact_step\"]\n", "commuting_trotter_step = commuting_analysis_row[\"trotter_step\"]\n", "commuting_target_phase = float(commuting_analysis_row[\"target_phase\"])\n", "commuting_energies = commuting_analysis_row[\"energies\"]\n", "\n", "print(commuting_analysis_row[\"trotter_step_error\"])" ] }, { "cell_type": "code", "execution_count": null, "id": "bf29502e", "metadata": {}, "outputs": [], "source": [ "# Guppy methods for simple commuting ham\n", "controlled_commuting_trotter_step = cntrl_trotter_first_order(\n", " commuting_ham_op, n_commuting_qubits\n", ")\n", "\n", "# Powers of U are implemented as repeated applications of the controlled Trotter steps.\n", "@guppy\n", "def power_oracle_commuting(\n", " control: qubit,\n", " state_reg: array[qubit, n_commuting_qubits],\n", " power: int,\n", ") -> None:\n", " for _ in range(power):\n", " controlled_commuting_trotter_step(\n", " control, state_reg, commuting_time_step\n", " )\n", "\n", "@guppy\n", "def commuting_state_preparation(\n", " system_register: array[qubit, n_commuting_qubits],\n", ") -> None:\n", " 'Prepare the |11> eigenstate of the commuting Hamiltonian.'\n", " x(system_register[0])\n", " x(system_register[1])\n", "\n", "# Assemble the commuting hamiltonian QPE program\n", "commuting_qpe_program = make_trotter_qpe_program(\n", " n_ancilla=n_ancilla,\n", " n_state_qubits=n_commuting_qubits,\n", " state_preparation=commuting_state_preparation,\n", " power_oracle=power_oracle_commuting,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "9ce80c1a", "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "target_phase dominant_trotter_phase dominant_trotter_overlap trotter_step_error\n 0.25 0.25 1.0 1.923814e-16\n" ] }, { "output_type": "stream", "name": "stdout", "text": [ "bitstring counts phase empirical_probability\n 0100 500 0.25 1.0\n" ] } ], "source": [ "# Run and inspect commuting hamiltonian qpe program results\n", "sim_result = (\n", " commuting_qpe_program.emulator(n_qubits=n_ancilla + n_commuting_qubits)\n", " .with_seed(5)\n", " .with_shots(n_shots)\n", " .run()\n", ")\n", "commuting_counts = sim_result.register_counts()[\"qpe_bitstring\"]\n", "dominant_key, dominant_counts, dominant_phase = dominant_measured_phase(\n", " commuting_counts\n", ")\n", "commuting_dominant_trotter_phase, commuting_dominant_trotter_weight = dominant_trotter_phase(\n", " commuting_analysis\n", ")\n", "\n", "commuting_summary = commuting_analysis[\n", " [\n", " \"target_phase\",\n", " \"dominant_trotter_phase\",\n", " \"dominant_trotter_overlap\",\n", " \"trotter_step_error\",\n", " ]\n", "]\n", "commuting_counts_df = measurement_dataframe(commuting_counts)\n", "\n", "print(commuting_summary.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n", "print(commuting_counts_df.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n" ] }, { "cell_type": "markdown", "id": "62887511", "metadata": {}, "source": [ "## Noncommuting Terms and Trotter Error\n", "\n", "Now switch to a genuinely noncommuting Hamiltonian. The exact evolution and the\n", "first-order Trotter step no longer match, so the trotterized QPE oracle estimates the\n", "eigenphases of the approximate step, not the exact Hamiltonian phases.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "c1768f0b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.479548026725464\n" ] } ], "source": [ "# Classical analysis of non-commuting ham\n", "noncommuting_ham_op = RealPauliOperator.from_str(\n", " \"(0.6708203932499369, X0), (-1.3416407864998738, Z0)\"\n", ")\n", "n_noncommuting_qubits = len(noncommuting_ham_op.qubits)\n", "\n", "# This keeps the same X:Z ratio as before, but sets the\n", "# trotterized eigenphase near 0.5 (with 4 ancilla bits).\n", "noncommuting_time_step = 2.236\n", "\n", "noncommuting_ham_mat = noncommuting_ham_op.to_sparse_matrix(True).toarray()\n", "noncommuting_energies, noncommuting_eigenvectors = np.linalg.eigh(noncommuting_ham_mat)\n", "noncommuting_prepared_state = canonical_real_single_qubit_state(\n", " noncommuting_eigenvectors[:, 0]\n", ")\n", "noncommuting_state_prep_angle = ry_angle_for_real_single_qubit_state(\n", " noncommuting_prepared_state\n", ")\n", "noncommuting_prepared_state_from_ry = np.array(\n", " [\n", " np.cos(noncommuting_state_prep_angle / 2),\n", " np.sin(noncommuting_state_prep_angle / 2),\n", " ]\n", ")\n", "assert np.allclose(noncommuting_prepared_state_from_ry, noncommuting_prepared_state)\n", "\n", "noncommuting_analysis = analyze_qubit_pauli_operator(\n", " noncommuting_ham_op,\n", " noncommuting_time_step,\n", " noncommuting_prepared_state,\n", " little_endian=True,\n", ")\n", "noncommuting_analysis_row = noncommuting_analysis.iloc[0]\n", "noncommuting_target_phase = float(noncommuting_analysis_row[\"target_phase\"])\n", "noncommuting_exact_step = noncommuting_analysis_row[\"exact_step\"]\n", "noncommuting_trotter_step = noncommuting_analysis_row[\"trotter_step\"]\n", "noncommuting_trotter_phases = noncommuting_analysis_row[\"trotter_phases\"]\n", "noncommuting_trotter_overlaps = noncommuting_analysis_row[\"trotter_overlaps\"]\n", "\n", "assert not np.allclose(noncommuting_trotter_step, noncommuting_exact_step)\n", "print(noncommuting_analysis_row[\"trotter_step_error\"])" ] }, { "cell_type": "code", "execution_count": null, "id": "90ec62d9", "metadata": {}, "outputs": [], "source": [ "# Guppy methods for the non-commuting example\n", "controlled_noncommuting_trotter_step = cntrl_trotter_first_order(\n", " noncommuting_ham_op, n_noncommuting_qubits\n", ")\n", "\n", "@guppy\n", "def noncommuting_state_preparation(\n", " system_register: array[qubit, n_noncommuting_qubits],\n", ") -> None:\n", " 'Prepare the exact ground state of the scaled noncommuting Hamiltonian.'\n", " ry(system_register[0], angle(noncommuting_state_prep_angle))\n", "\n", "@guppy\n", "def power_oracle_noncommuting(\n", " control: qubit,\n", " state_reg: array[qubit, n_noncommuting_qubits],\n", " power: int,\n", ") -> None:\n", " for _ in range(power):\n", " controlled_noncommuting_trotter_step(\n", " control, state_reg, noncommuting_time_step\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "104de23c", "metadata": {}, "outputs": [], "source": [ "# Assemble the non-commuting hamiltonian QPE program\n", "noncommuting_qpe_program = make_trotter_qpe_program(\n", " n_ancilla=n_ancilla,\n", " n_state_qubits=n_noncommuting_qubits,\n", " state_preparation=noncommuting_state_preparation,\n", " power_oracle=power_oracle_noncommuting,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "4dc0bfe2", "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "target_phase trotter_phases trotter_overlaps dominant_trotter_phase dominant_trotter_overlap trotter_step_error\n 1.677 [0.49996775775044966, 1.5000322422495505] [0.816182459549431, 0.18381754045056925] 0.499968 0.816182 2.479548\n" ] }, { "output_type": "stream", "name": "stdout", "text": [ "bitstring counts phase empirical_probability\n 0010 257 0.5 0.514\n 0011 243 1.5 0.486\n" ] } ], "source": [ "# Run and inspect non-commuting hamiltonian qpe program results\n", "sim_result = (\n", " noncommuting_qpe_program.emulator(\n", " n_qubits=n_ancilla + n_noncommuting_qubits\n", " )\n", " .with_seed(5)\n", " .with_shots(n_shots)\n", " .run()\n", ")\n", "noncommuting_counts = sim_result.register_counts()[\"qpe_bitstring\"]\n", "dominant_key, dominant_counts, dominant_phase = dominant_measured_phase(\n", " noncommuting_counts\n", ")\n", "noncommuting_dominant_trotter_phase, noncommuting_dominant_trotter_weight = dominant_trotter_phase(\n", " noncommuting_analysis\n", ")\n", "\n", "noncommuting_summary = noncommuting_analysis[\n", " [\n", " \"target_phase\",\n", " \"trotter_phases\",\n", " \"trotter_overlaps\",\n", " \"dominant_trotter_phase\",\n", " \"dominant_trotter_overlap\",\n", " \"trotter_step_error\",\n", " ]\n", "]\n", "noncommuting_counts_df = measurement_dataframe(noncommuting_counts)\n", "\n", "print(noncommuting_summary.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n", "print(noncommuting_counts_df.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n" ] }, { "cell_type": "markdown", "id": "0b715976", "metadata": {}, "source": [ "## H2 STO-3G Trotterized QPE\n", "\n", "Finally, apply the same trotterized QPE pattern to the full H2 STO-3G Jordan\u2013Wigner\n", "Hamiltonian defined at the start of the notebook. We use the Hartree\u2013Fock basis state\n", "as the input guess and compare it against the exact diagonalization before running QPE.\n" ] }, { "cell_type": "markdown", "id": "e162b2ca", "metadata": {}, "source": [ "\n", "### Phase to Energy\n", "\n", "With $U(t)=e^{-i \\frac{\\pi}{2} t H}$, a measured phase $\\phi$ corresponds to\n", "\n", "$$\n", "E = -\\frac{2\\phi}{t}\\quad (\\text{mod } 2/t).\n", "$$\n" ] }, { "cell_type": "code", "execution_count": null, "id": "6e9600f3", "metadata": {}, "outputs": [], "source": [ "# prepare the trotterized hamiltonian\n", "h2_ham_qubits=4\n", "jw = JordanWignerMapper(h2_ham_qubits, mode_ordering=None)\n", "\n", "H2_STO3G_HF_ENERGY = -1.1175058842043306\n", "H2_STO3G_FCI_ENERGY = -1.136846575472054\n", "\n", "H2_STO3G_FERMION_OPERATOR_ZIXY = [\n", " (0.7430177069924181, ()),\n", "\n", " (-1.2702927243904383, ((0, 1), (0, 0))), # F0^ F0\n", " (-1.2702927243904380, ((1, 1), (1, 0))),\n", " (-0.45680735030941033, ((2, 1), (2, 0))),\n", " (-0.45680735030941020, ((3, 1), (3, 0))),\n", "\n", " (0.6800618575841273, ((0, 1), (0, 0), (1, 1), (1, 0))), # F0^ F0 F1^ F1 #create/annihilate in mode 0, then create/annihilate in mode 1\n", " (0.48890859745047305, ((0, 1), (0, 0), (2, 1), (2, 0))),\n", " (0.6685772770134887, ((0, 1), (0, 0), (3, 1), (3, 0))),\n", " (0.6685772770134887, ((1, 1), (1, 0), (2, 1), (2, 0))),\n", " (0.48890859745047305, ((1, 1), (1, 0), (3, 1), (3, 0))),\n", " (0.7028135332762804, ((2, 1), (2, 0), (3, 1), (3, 0))),\n", "\n", " (-0.35933735912603115, ((0, 1), (1, 1), (2, 0), (3, 0))), # F0^ F1^ F2 F3 #create in modes 0,1, then annihilate in modes 2,3\n", " (-0.35933735912603115, ((0, 1), (1, 0), (3, 1), (2, 0))), # F0^ F1 F3^ F2\n", "]\n", "\n", "h2_ham_op = RealPauliOperator.from_str(\"(0.0, I0)\", h2_ham_qubits)\n", "\n", "for coeff, ops in H2_STO3G_FERMION_OPERATOR_ZIXY:\n", " if len(ops) == 0:\n", " h2_ham_op += RealPauliOperator.from_str(f\"({coeff}, I0)\", h2_ham_qubits)\n", " else:\n", " fermion_string = FermionString(\n", " h2_ham_qubits, [(mode, bool(adj)) for mode, adj in ops]\n", " )\n", " h2_ham_op += jw.encode_real(fermion_string, coeff)\n", "\n", "assert np.isclose(\n", " np.linalg.eigh(h2_ham_op.to_sparse_matrix(False).toarray())[0][0],\n", " H2_STO3G_FCI_ENERGY,\n", ")\n", "\n", "n_h2_qubits = len(h2_ham_op.qubits)\n", "n_h2_ancilla = 6\n", "h2_time_step = 0.27508092748651924 # 0.25\n", "\n", "# The Hartree-Fock computational basis state is |0011> in the big-endian matrix basis.\n", "# In Guppy's little-endian qarray convention, that means flipping q[0] and q[1].\n", "h2_hf_state = np.zeros(2**n_h2_qubits, dtype=complex)\n", "h2_hf_state[3] = 1.0\n", "\n", "h2_analysis = analyze_qubit_pauli_operator(\n", " h2_ham_op,\n", " h2_time_step,\n", " h2_hf_state,\n", " little_endian=False,\n", ")\n", "h2_analysis_row = h2_analysis.iloc[0]\n", "h2_ground_energy = float(h2_analysis_row[\"energies\"][0])\n", "h2_ground_phase = float(h2_analysis_row[\"target_phase\"])\n", "h2_hf_ground_overlap = float(h2_analysis_row[\"exact_overlaps\"][0])\n", "h2_trotter_step_error = float(h2_analysis_row[\"trotter_step_error\"])" ] }, { "cell_type": "code", "execution_count": null, "id": "ae51f18e", "metadata": {}, "outputs": [], "source": [ "# Guppy methods\n", "controlled_h2_trotter_step = cntrl_trotter_first_order(h2_ham_op, n_h2_qubits)\n", "\n", "@guppy\n", "def power_oracle_h2(\n", " control: qubit,\n", " state_reg: array[qubit, n_h2_qubits],\n", " power: int,\n", ") -> None:\n", " for _ in range(power):\n", " controlled_h2_trotter_step(control, state_reg, h2_time_step)\n", "\n", "\n", "@guppy\n", "def h2_hf_state_preparation(\n", " system_register: array[qubit, n_h2_qubits],\n", ") -> None:\n", " 'Prepare the JW Hartree-Fock basis state |0011>.'\n", " x(system_register[0])\n", " x(system_register[1])\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "4efa99e2", "metadata": {}, "outputs": [], "source": [ "# Assemble the H2 trotterized QPE program\n", "h2_qpe_program = make_trotter_qpe_program(\n", " n_ancilla=n_h2_ancilla,\n", " n_state_qubits=n_h2_qubits,\n", " state_preparation=h2_hf_state_preparation,\n", " power_oracle=power_oracle_h2,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "83f98095", "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "target_phase dominant_trotter_phase dominant_trotter_overlap trotter_step_error\n 0.156362 0.15625 0.988019 0.038557\n" ] }, { "output_type": "stream", "name": "stdout", "text": [ "bitstring counts phase empirical_probability approximate energy (negative branch)\n 101000 497 0.15625 0.994 -1.136029\n 011111 2 1.93750 0.004 -14.086764\n 101111 1 1.90625 0.002 -13.859558\n" ] } ], "source": [ "# Run and inspect\n", "sim_result = (\n", " h2_qpe_program.emulator(n_qubits=n_h2_ancilla + n_h2_qubits)\n", " .with_seed(5)\n", " .with_shots(n_shots)\n", " .run()\n", ")\n", "h2_counts = sim_result.register_counts()[\"qpe_bitstring\"]\n", "dominant_key, dominant_counts, dominant_phase = dominant_measured_phase(h2_counts)\n", "h2_dominant_trotter_phase, h2_dominant_trotter_weight = dominant_trotter_phase(\n", " h2_analysis\n", ")\n", "\n", "h2_summary = h2_analysis[\n", " [\n", " \"target_phase\",\n", " \"dominant_trotter_phase\",\n", " \"dominant_trotter_overlap\",\n", " \"trotter_step_error\",\n", " ]\n", "]\n", "h2_counts_df = measurement_dataframe(h2_counts)\n", "h2_counts_df[\"approximate energy (negative branch)\"] = h2_counts_df[\"phase\"].map(\n", " lambda phase: phase_to_energy_qpe(phase, h2_time_step)\n", ")\n", "\n", "print(h2_summary.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n", "print(h2_counts_df.to_string(index=False, float_format=lambda value: f\"{value:.4f}\"))\n" ] } ], "metadata": { "kernelspec": { "display_name": "guppy-algorithms (3.12.3)", "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.3" } }, "nbformat": 4, "nbformat_minor": 5 }