diff --git a/docs/_toc.yml b/docs/_toc.yml index 16c4da3..ff2049b 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -11,6 +11,7 @@ parts: - file: tutorials/generating_lattices - file: tutorials/pulser-calc-example - file: tutorials/finite-geometries + - file: tutorials/vqe_qiskit - caption: Use-Cases chapters: - file: use-cases/ssh_model diff --git a/docs/tutorials/vqe_qiskit.ipynb b/docs/tutorials/vqe_qiskit.ipynb new file mode 100644 index 0000000..d630b68 --- /dev/null +++ b/docs/tutorials/vqe_qiskit.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "56fc15e4", + "metadata": {}, + "source": [ + "# VQE for a spin model using `qse` Operators\n", + "\n", + "This notebook builds a spin model using the `qse` `Operator` / `Operators` classes,\n", + "converts it to a Qiskit `SparsePauliOp` via `to_qiskit()` method, and runs VQE to find the ground state energy." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "116e033e", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import qse\n", + "from qse.operator.operator import Operator\n", + "from qse.operator.operators import Operators" + ] + }, + { + "cell_type": "markdown", + "id": "7b142901", + "metadata": {}, + "source": [ + "## 1. Build the spin model.\n", + "\n", + "We build a 1D **transverse-field Ising model** (TFIM) on $N=10$ qubits:\n", + "\n", + "$$H = -J \\sum_{i=0}^{N-2} Z_i Z_{i+1} - h \\sum_{i=0}^{N-1} X_i$$" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b1ec8bad", + "metadata": {}, + "outputs": [], + "source": [ + "N = 10\n", + "J = 1.0\n", + "h = 0.5\n", + "spacing = 1.0\n", + "\n", + "qbits = qse.lattices.chain(spacing, N)\n", + "\n", + "# ZZ interaction terms between neighbouring qubits\n", + "hamiltonian = qbits.compute_interaction_hamiltonian(\n", + " distance_func=lambda d: -J * np.isclose(d, spacing),\n", + " interaction=\"Z\",\n", + ")\n", + "\n", + "# Transverse field X terms on each qubit\n", + "for i in range(N):\n", + " hamiltonian += qse.Operator(\"X\", i, N, coef=-h)" + ] + }, + { + "cell_type": "markdown", + "id": "be80c5c1", + "metadata": {}, + "source": [ + "## 2. Convert to Qiskit `SparsePauliOp`\n", + "\n", + "Once we have the Hamiltonian $H$, we can use the `to_qiskit()` method to transform it to a Qiskit Sparse Pauli Operator. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "51b961bb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SparsePauliOp(['ZZIIIIIIII', 'IZZIIIIIII', 'IIZZIIIIII', 'IIIZZIIIII', 'IIIIZZIIII', 'IIIIIZZIII', 'IIIIIIZZII', 'IIIIIIIZZI', 'IIIIIIIIZZ', 'XIIIIIIIII', 'IXIIIIIIII', 'IIXIIIIIII', 'IIIXIIIIII', 'IIIIXIIIII', 'IIIIIXIIII', 'IIIIIIXIII', 'IIIIIIIXII', 'IIIIIIIIXI', 'IIIIIIIIIX'],\n", + " coeffs=[-1. +0.j, -1. +0.j, -1. +0.j, -1. +0.j, -1. +0.j, -1. +0.j, -1. +0.j,\n", + " -1. +0.j, -1. +0.j, -0.5+0.j, -0.5+0.j, -0.5+0.j, -0.5+0.j, -0.5+0.j,\n", + " -0.5+0.j, -0.5+0.j, -0.5+0.j, -0.5+0.j, -0.5+0.j])\n" + ] + } + ], + "source": [ + "pauli_op = hamiltonian.to_qiskit()\n", + "print(pauli_op)" + ] + }, + { + "cell_type": "markdown", + "id": "a006d641", + "metadata": {}, + "source": [ + "## 3. Set up and run VQE.\n", + "\n", + "We set up and run the VQE in qiskit. " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7e8986ad", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.primitives import StatevectorEstimator\n", + "from qiskit.circuit.library import efficient_su2\n", + "\n", + "import numpy as np\n", + "from scipy.optimize import minimize" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "55201685", + "metadata": {}, + "outputs": [], + "source": [ + "ansatz = efficient_su2(num_qubits=N, reps=2, entanglement=\"linear\")\n", + "estimator = StatevectorEstimator()\n", + "energies_list = []\n", + "\n", + "\n", + "def cost_function(params):\n", + " result = estimator.run([(ansatz, pauli_op, params)]).result()\n", + " energy = result[0].data.evs\n", + " energies_list.append(float(energy))\n", + " return float(energy)\n", + "\n", + "\n", + "# Random initial parameters\n", + "rng = np.random.default_rng(42)\n", + "x0 = rng.uniform(-np.pi, np.pi, ansatz.num_parameters)\n", + "\n", + "result = minimize(\n", + " cost_function, x0, method=\"COBYLA\", options={\"maxiter\": 2000, \"rhobeg\": 0.5}\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "94b26131", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "VQE ground state energy : -9.745759\n", + "Exact ground state energy: -9.76550395792718\n" + ] + } + ], + "source": [ + "ground_state = np.linalg.eigh(pauli_op.to_matrix())[0][0]\n", + "print(f\"VQE ground state energy : {result.fun:.6f}\")\n", + "print(f\"Exact ground state energy: {ground_state}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ab8e9a6d", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87c3414d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "env_qse", + "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.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml index 69b06a7..779d1a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ docs = [ "ipywidgets", "ipympl", "pulser", # required for creating the docs + "qiskit" ] [tool.black]