{ "cells": [ { "cell_type": "markdown", "id": "23c5d052", "metadata": {}, "source": [ "# Operator averaging for molecular Hamiltonians\n", "**Download Notebook** - {nb-download}`operator_averaging.ipynb`\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "b39ec520", "metadata": {}, "outputs": [], "source": [ "\"\"\"Example showing operator averaging on the H2 STO-3G Hartree-Fock state.\"\"\"\n", "\n", "from __future__ import annotations\n", "\n", "from typing import no_type_check\n", "\n", "import zixy.qubit.pauli as zqp\n", "from zixy.fermion.mappings import JordanWignerMapper\n", "from zixy.fermion.operator.general import String as FermionString\n", "from guppylang import guppy\n", "from guppylang.std.builtins import array, comptime\n", "from guppylang.std.quantum import discard_array, qubit, x\n", "\n", "from guppyalgos.primitives.measurement import (\n", " estimate_expectation_from_binary_samples,\n", " estimate_pauli_observable_expectation_from_binary_samples,\n", " make_hadamard_test_pauli,\n", ")\n", "from guppyalgos.utils import qarray" ] }, { "cell_type": "code", "execution_count": 2, "id": "62c4231b", "metadata": {}, "outputs": [], "source": [ "H2_STO3G_HF_ENERGY = -1.1175058842043306\n", "H2_STO3G_XY_SHOTS = 512\n", "H2_STO3G_SHOTS = 64\n", "H2_HAM_QUBITS = 4 # for parsing paulis\n", "H2_TOTAL_QUBITS = H2_HAM_QUBITS + 1 # for emulator\n", "\n", "H2_STO3G_FERMION_OPERATOR_ZIXY = [\n", " # is mode-indexed\n", " (0.7430177069924181, ()),\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", " (0.6800618575841273, ((0, 1), (0, 0), (1, 1), (1, 0))), # F0^ F0 F1^ F1\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", " (-0.35933735912603115, ((0, 1), (1, 1), (2, 0), (3, 0))), # F0^ F1^ F2 F3\n", " (-0.35933735912603115, ((0, 1), (1, 0), (3, 1), (2, 0))), # F0^ F1 F3^ F2\n", "]" ] }, { "cell_type": "code", "execution_count": 3, "id": "82e20889", "metadata": {}, "outputs": [], "source": [ "@guppy\n", "@no_type_check\n", "def prepare_h2_sto3g_hf(qreg: array[qubit, comptime(4)]) -> None:\n", " \"\"\"Prepare the H2 STO-3G Hartree-Fock reference state.\n", "\n", " The spin-orbitals are ordered from lowest to highest orbital energy,\n", " matching `H2_STO3G_FERMION_OPERATOR_ZIXY`. Modes 0 and 1 are occupied.\n", " \"\"\"\n", " x(qreg[0])\n", " x(qreg[1])\n", "\n", "\n", "def build_hadamard_program(pauli_string: zqp.String, size: int, prepare_state):\n", " hadamard_test_pauli = make_hadamard_test_pauli(pauli_string, size)\n", "\n", " @guppy\n", " @no_type_check\n", " def program() -> None:\n", " qreg = qarray(comptime(size))\n", " prepare_state(qreg)\n", " hadamard_test_pauli(qreg)\n", " discard_array(qreg)\n", "\n", " return program" ] }, { "cell_type": "code", "execution_count": 4, "id": "21405689", "metadata": {}, "outputs": [], "source": [ "def _shots_for_term(term: zqp.RealTerm) -> int:\n", " \"\"\"Use a larger shot budget for the off-diagonal exchange terms.\"\"\"\n", " if any(label in (zqp.X, zqp.Y) for label in term.string.get_dict().values()):\n", " return H2_STO3G_XY_SHOTS\n", " return H2_STO3G_SHOTS" ] }, { "cell_type": "code", "execution_count": 5, "id": "80b79b57", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " on HF 1100 = -1.000000 +/- 0.000000\n" ] } ], "source": [ "\"\"\"Run the operator averaging demo.\"\"\"\n", "single_pauli = zqp.String.from_str(\"Z0\", H2_HAM_QUBITS)\n", "single_program = build_hadamard_program(\n", " single_pauli,\n", " H2_HAM_QUBITS,\n", " prepare_h2_sto3g_hf,\n", ")\n", "result = single_program.emulator(n_qubits=H2_TOTAL_QUBITS).with_shots(10).run()\n", "ancilla_samples = [bool(sample[\"ancilla\"][0]) for sample in result.collated_shots()]\n", "single_estimate = estimate_expectation_from_binary_samples(ancilla_samples)\n", "\n", "print(\n", " f\"<{single_pauli}> on HF 1100 = {single_estimate.expectation:.6f} \"\n", " f\"+/- {single_estimate.standard_error:.6f}\"\n", ")" ] }, { "cell_type": "code", "execution_count": 6, "id": "31d199b0", "metadata": {}, "outputs": [], "source": [ "jw = JordanWignerMapper(H2_HAM_QUBITS, mode_ordering=None)\n", "h2_ham_op = zqp.RealTermSum.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 += zqp.RealTermSum.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", "operator = h2_ham_op\n", "\n", "#print(operator)" ] }, { "cell_type": "code", "execution_count": 7, "id": "0416f70d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "term exp var stderr shots + -\n", "I +1.000000 0.000000 0.000000 0 0 0\n", "X0 X1 Y2 Y3 +0.023438 0.001952 0.044182 512 262 250\n", "X0 Y1 Y2 X3 -0.027344 0.001952 0.044178 512 249 263\n", "Y0 X1 X2 Y3 +0.019531 0.001952 0.044186 512 261 251\n", "Y0 Y1 X2 X3 +0.031250 0.001951 0.044173 512 264 248\n", "Z0 -1.000000 0.000000 0.000000 64 0 64\n", "Z0 Z1 +1.000000 0.000000 0.000000 64 64 0\n", "Z0 Z2 -1.000000 0.000000 0.000000 64 0 64\n", "Z0 Z3 -1.000000 0.000000 0.000000 64 0 64\n", "Z1 -1.000000 0.000000 0.000000 64 0 64\n", "Z1 Z2 -1.000000 0.000000 0.000000 64 0 64\n", "Z1 Z3 -1.000000 0.000000 0.000000 64 0 64\n", "Z2 +1.000000 0.000000 0.000000 64 64 0\n", "Z2 Z3 +1.000000 0.000000 0.000000 64 64 0\n", "Z3 +1.000000 0.000000 0.000000 64 64 0\n", "\n", "H2 STO-3G energy on HF 1100 = -1.120313 +/- 0.003969\n", "Reference HF energy = -1.117505884204\n" ] } ], "source": [ "operator_samples: dict[str, list[bool]] = {}\n", "\n", "for term in operator.to_terms():\n", " if term.string.is_identity():\n", " continue\n", "\n", " term_program = build_hadamard_program(\n", " term.string,\n", " H2_HAM_QUBITS,\n", " prepare_h2_sto3g_hf,\n", " )\n", " result = term_program.emulator(n_qubits=H2_TOTAL_QUBITS).with_shots(_shots_for_term(term)).run()\n", " operator_samples[str(term.string)] = [bool(sample[\"ancilla\"][0]) for sample in result.collated_shots()]\n", "\n", "\n", "operator_estimate = estimate_pauli_observable_expectation_from_binary_samples(operator, operator_samples)\n", "operator_estimate.print_terms()\n", "print(\n", " f\"\\nH2 STO-3G energy on HF 1100 = {operator_estimate.expectation:.6f} \"\n", " f\"+/- {operator_estimate.standard_error:.6f}\"\n", ")\n", "print(f\"Reference HF energy = {H2_STO3G_HF_ENERGY:.12f}\")\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 }