{ "cells": [ { "cell_type": "markdown", "id": "direct-operator-averaging-title", "metadata": {}, "source": [ "# Direct operator averaging\n", "\n", "**Download Notebook** - {nb-download}`operator_averaging_direct.ipynb`\n", "\n", "Estimate observables of the H2 STO-3G Hartree-Fock state by direct measurement." ] }, { "cell_type": "code", "execution_count": 1, "id": "b39ec520", "metadata": {}, "outputs": [], "source": [ "\"\"\"Example showing direct 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 h, qubit, x\n", "\n", "from guppyalgos.primitives.measurement import (\n", " estimate_pauli_observable_expectation_from_binary_samples,\n", " estimate_pauli_observable_expectation_from_bitstrings,\n", " make_direct_measure_pauli_simple,\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 # direct measurement has no ancilla\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_direct_program(pauli_string: zqp.String, size: int, prepare_state):\n", " direct_measure_pauli = make_direct_measure_pauli_simple(pauli_string, size)\n", "\n", " @guppy\n", " @no_type_check\n", " def program() -> None:\n", " qreg = qarray(comptime(size))\n", " prepare_state(qreg)\n", " direct_measure_pauli(qreg)\n", "\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_direct_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", "single_bitstrings = [\n", " tuple(bool(bit) for bit in sample[\"bitstring\"][0])\n", " for sample in result.collated_shots()\n", "]\n", "\n", "single_operator = zqp.RealTermSum.from_str(\n", " f\"(1.0, {single_pauli})\", H2_HAM_QUBITS\n", ")\n", "single_estimate = estimate_pauli_observable_expectation_from_bitstrings(\n", " single_operator, {str(single_pauli): single_bitstrings}\n", ").term_estimates[str(single_pauli)]\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.042969 0.001950 0.044153 512 245 267\n", "X0 Y1 Y2 X3 +0.093750 0.001936 0.044000 512 280 232\n", "Y0 X1 X2 Y3 -0.058594 0.001946 0.044118 512 241 271\n", "Y0 Y1 X2 X3 +0.074219 0.001942 0.044072 512 275 237\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.117330 +/- 0.003960\n", "Reference HF energy = -1.117505884204\n" ] } ], "source": [ "operator_samples: dict[str, list[tuple[bool, ...]]] = {}\n", "\n", "for term in operator.to_terms():\n", " if term.string.is_identity():\n", " continue\n", "\n", " term_program = build_direct_program(\n", " term.string,\n", " H2_HAM_QUBITS,\n", " prepare_h2_sto3g_hf,\n", " )\n", " result = (\n", " term_program.emulator(n_qubits=H2_TOTAL_QUBITS)\n", " .with_shots(_shots_for_term(term))\n", " .run()\n", " )\n", " operator_samples[str(term.string)] = [\n", " tuple(bool(bit) for bit in sample[\"bitstring\"][0])\n", " for sample in result.collated_shots()\n", " ]\n", "\n", "\n", "operator_estimate = estimate_pauli_observable_expectation_from_bitstrings(\n", " operator, operator_samples\n", ")\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" ] }, { "cell_type": "code", "execution_count": null, "id": "c15d4c5e", "metadata": {}, "outputs": [], "source": [] } ], "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 }