{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "ec1fdc16-8860-43a8-aff7-0ef7462744df",
      "metadata": {},
      "source": [
        "---\n",
        "title: Ground state energies\n",
        "description: Combine the Hamiltonian, ansatz, and classical optimizer to estimate the ground state of a simple molecule.\n",
        "---\n",
        "\n",
        "{/* cspell:ignore nfev Lipinska webkitallowfullscreen allowfullscreen referrerpolicy Ardle IIIZ IZII IIZI ZIII IZIZ IIZZ ZIIZ IZZI ZZII ZIZI XXYY YYXX disp workstream */}\n",
        "\n",
        "# Bringing it all together with IBM Quantum Compute Service\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cc667e6f-bb13-41b2-8ba5-eb125e193770",
      "metadata": {},
      "source": [
        "## Summary\n",
        "\n",
        "Victoria Lipinska gives a final recap of what we have learned so far.\n",
        "\n",
        "<IBMVideo id=\"132414925\" title=\"Victoria Lipinska gives an overview of what we have learned so far, combining the Hamiltonian, ansatz, and classical optimizer elements into a VQE calculation.\" />\n",
        "\n",
        "### References\n",
        "\n",
        "The following articles are referenced in the above video.\n",
        "\n",
        "* [Quantum Chemistry in the Age of Quantum Computing, Cao, et al](https://arxiv.org/pdf/1812.09976.pdf).\n",
        "* [Quantum computational chemistry, McArdle, et al](https://arxiv.org/pdf/1808.10402.pdf).\n",
        "\n",
        "## VQE with Qiskit patterns\n",
        "\n",
        "We have all the necessary components for a VQE calculation:\n",
        "\n",
        "* Hamiltonian\n",
        "* Ansatz\n",
        "* Classical optimizer\n",
        "\n",
        "Now we just have to bring them together in the Qiskit patterns framework.\n",
        "\n",
        "## Step 1: Map classical inputs to a quantum problem\n",
        "\n",
        "As stated previously, we will assume here that a suitably formatted Hamiltonian of interest has already been generated. If you have questions about that, see the [lesson on Hamiltonians](/learning/courses/quantum-chem-with-vqe/hamiltonian-construction) for guidance. The code block below sets up the components explained in previous lessons. Here we have chosen to model H2 because its Hamiltonian is compact enough to write out.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "f697dac1-8daf-4649-9c01-11c0ac40a676",
      "metadata": {},
      "outputs": [],
      "source": [
        "# General imports\n",
        "import numpy as np\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "# Hamiltonian obtained from a previous lesson\n",
        "\n",
        "H = SparsePauliOp(\n",
        "    [\n",
        "        \"IIII\",\n",
        "        \"IIIZ\",\n",
        "        \"IZII\",\n",
        "        \"IIZI\",\n",
        "        \"ZIII\",\n",
        "        \"IZIZ\",\n",
        "        \"IIZZ\",\n",
        "        \"ZIIZ\",\n",
        "        \"IZZI\",\n",
        "        \"ZZII\",\n",
        "        \"ZIZI\",\n",
        "        \"YYYY\",\n",
        "        \"XXYY\",\n",
        "        \"YYXX\",\n",
        "        \"XXXX\",\n",
        "    ],\n",
        "    coeffs=[\n",
        "        -0.09820182 + 0.0j,\n",
        "        -0.1740751 + 0.0j,\n",
        "        -0.1740751 + 0.0j,\n",
        "        0.2242933 + 0.0j,\n",
        "        0.2242933 + 0.0j,\n",
        "        0.16891402 + 0.0j,\n",
        "        0.1210099 + 0.0j,\n",
        "        0.16631441 + 0.0j,\n",
        "        0.16631441 + 0.0j,\n",
        "        0.1210099 + 0.0j,\n",
        "        0.17504456 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "        0.04530451 + 0.0j,\n",
        "    ],\n",
        ")\n",
        "\n",
        "nuclear_repulsion = 0.7199689944489797"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bbcadd0f-51a6-4dd7-a579-b2903663d3de",
      "metadata": {},
      "source": [
        "We select an efficient\\_su2 circuit, and the optimizer COBYLA, to start.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "28a60667-38f9-46b8-9633-47ceb8e1378f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "5\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-chem-with-vqe/ground-state/extracted-outputs/28a60667-38f9-46b8-9633-47ceb8e1378f-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Pre-defined ansatz circuit\n",
        "from qiskit.circuit.library import efficient_su2\n",
        "\n",
        "# SciPy minimizer routine\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "# Plotting functions\n",
        "\n",
        "# Random initial state and efficient_su2 ansatz\n",
        "ansatz = efficient_su2(H.num_qubits, su2_gates=[\"rx\"], entanglement=\"linear\", reps=1)\n",
        "x0 = 2 * np.pi * np.random.random(ansatz.num_parameters)\n",
        "print(ansatz.decompose().depth())\n",
        "ansatz.decompose().draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "737158af-066a-46e7-a172-9ff402cd2f18",
      "metadata": {},
      "source": [
        "Now we construct our cost function. This is obviously related to the Hamiltonian, but differs in that the Hamiltonian is an operator, and we want a function that returns the expectation value of that operator, using Estimator. Of course, it accomplishes this by using the ansatz and the variational parameters, so all these show up as arguments. Below, we define slightly different versions for use on real hardware or simulators.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "5a027dc0-2a03-45e2-8119-f8943ba49d3b",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func(params, ansatz, H, estimator):\n",
        "    pub = (ansatz, [H], [params])\n",
        "    result = estimator.run(pubs=[pub]).result()\n",
        "    energy = result[0].data.evs[0]\n",
        "    return energy\n",
        "\n",
        "\n",
        "# def cost_func_sim(params, ansatz, H, estimator):\n",
        "#    energy = estimator.run(ansatz, H, parameter_values=params).result().values[0]\n",
        "#    return energy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1fa3ec7-1b7a-4b38-84e9-7bdad95a89ea",
      "metadata": {},
      "source": [
        "## Step 2: Optimize problem for quantum execution.\n",
        "\n",
        "We want our code to run as efficiently as possible on the hardware we use. So we must select a backend to begin the optimization step. The code below selects the least busy backend available to you.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8958c406-0fe4-4e31-a8e0-7586f574ef94",
      "metadata": {},
      "outputs": [],
      "source": [
        "# To run on hardware, select the backend with the fewest number of jobs in the queue\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "\n",
        "service = QiskitRuntimeService(channel=\"ibm_quantum_platform\")\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "backend.name"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d757c3d0-4546-456a-b025-40f3abfc77ec",
      "metadata": {},
      "source": [
        "Optimizing the circuit for running on a real backend is a rich and critical topic. But it is not specific to VQE. For now, we will simply remind you of two important terms:\n",
        "\n",
        "* **[optimization\\_level](/docs/api/qiskit/transpiler#preset-pass-managers)**: This describes how well the circuit is tailored to the layout of the backend selected. The lowest optimization level just does the bare minimum needed to get the circuit running on the device; it maps the circuit qubits to the device qubits and adds swap gates to allow all two-qubit operations. The highest optimization level is much smarter and uses lots of tricks to reduce the overall gate count. Since multi-qubit gates have high error rates and qubits decohere over time, the shorter circuits should give better results.\n",
        "* **[Dynamical Decoupling](/docs/api/qiskit/qiskit.transpiler.passes.PadDynamicalDecoupling)**: We can apply a sequence of gates to idling qubits. This cancels out some unwanted interactions with the environment.\n",
        "  Consult the linked documentation for more information on optimizing circuits. The code below generates a mass manager using preset pass managers from `qiskit.transpiler`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "a5bcdebe-a21c-4dfd-88ba-b6f6b517cd34",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/quantum-chem-with-vqe/ground-state/extracted-outputs/a5bcdebe-a21c-4dfd-88ba-b6f6b517cd34-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 19,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.transpiler.passes import (\n",
        "    ALAPScheduleAnalysis,\n",
        "    PadDynamicalDecoupling,\n",
        "    ConstrainedReschedule,\n",
        ")\n",
        "from qiskit.circuit.library import XGate\n",
        "\n",
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "pm.scheduling = PassManager(\n",
        "    [\n",
        "        ALAPScheduleAnalysis(target=target),\n",
        "        ConstrainedReschedule(\n",
        "            acquire_alignment=target.acquire_alignment,\n",
        "            pulse_alignment=target.pulse_alignment,\n",
        "            target=target,\n",
        "        ),\n",
        "        PadDynamicalDecoupling(\n",
        "            target=target,\n",
        "            dd_sequence=[XGate(), XGate()],\n",
        "            pulse_alignment=target.pulse_alignment,\n",
        "        ),\n",
        "    ]\n",
        ")\n",
        "\n",
        "\n",
        "# Use the pass manager and draw the resulting circuit\n",
        "ansatz_isa = pm.run(ansatz)\n",
        "ansatz_isa.draw(output=\"mpl\", idle_wires=False, style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "88e029df-ffb2-425d-b31c-ff4972af3eae",
      "metadata": {},
      "source": [
        "We must similarly apply device layout characteristics to the Hamiltonian.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "42b6c986-4487-4bd1-bb64-ca99c5ba53de",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYXXII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXXXII'],\n",
              "              coeffs=[-0.09820182+0.j, -0.1740751 +0.j, -0.1740751 +0.j,  0.2242933 +0.j,\n",
              "  0.2242933 +0.j,  0.16891402+0.j,  0.1210099 +0.j,  0.16631441+0.j,\n",
              "  0.16631441+0.j,  0.1210099 +0.j,  0.17504456+0.j,  0.04530451+0.j,\n",
              "  0.04530451+0.j,  0.04530451+0.j,  0.04530451+0.j])"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "hamiltonian_isa = H.apply_layout(ansatz_isa.layout)\n",
        "hamiltonian_isa"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d67777a-6d12-4b03-8506-a3f4981de632",
      "metadata": {},
      "source": [
        "## Step 3: Execute using IBM Quantum primitives.\n",
        "\n",
        "Before we execute on the selected hardware, it is a good idea to use a simulator for cursory debugging, and sometimes for estimates of error. For those reasons, we briefly show how to run VQE on a simulator. But it is critical to note that **no classical computer, simulator, or GPU** can accurately simulate the full functionality of a highly-entangled 127-qubit quantum computer. In the present era of quantum utility, simulators will have limited use.\n",
        "\n",
        "Recall that for each choice of parameters in the variational circuit, an expectation value must be calculated (since that is the value to be minimized). As you may have already guessed, the most efficient way to do that is using the Qiskit SDK primitive, Estimator. We'll start by using a local simulator, which will require that we use the local version of Estimator called BackendEstimator.\n",
        "\n",
        "Keeping the real backend we used for optimization, we can import a model of the noise behavior of that device to then use with the local simulator of our choice. Here, we will use the aer\\_simulator\\_statevector.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "dd54bb0e-c94b-4168-b1e7-2ca036d2c231",
      "metadata": {},
      "outputs": [],
      "source": [
        "# We will start by using a local simulator\n",
        "from qiskit_aer import AerSimulator\n",
        "\n",
        "# Import Estimator, this time from Qiskit (we will import from IBM Quantum for real QPUs)\n",
        "from qiskit.primitives import BackendEstimatorV2\n",
        "\n",
        "# generate a simulator that mimics the real quantum system\n",
        "backend_sim = AerSimulator.from_backend(backend)\n",
        "estimator = BackendEstimatorV2(backend=backend_sim)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4eba199e-3925-4ec3-80ad-b76a08ff0f4a",
      "metadata": {},
      "source": [
        "It is finally time to implement VQE, minimizing the cost function using the selected Hamiltonian, ansatz, classical optimizer, and our BackendEstimator, based on the real backend we selected for subsequent use. Note that here we have chosen a relatively small number for the maximum iterations. This is because we are merely using the simulator to debug. VQE optimization steps often require hundreds of iterations to converge.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "418eac8e-d20b-4819-814a-2417f4baaf0a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Return from COBYLA because the objective function has been evaluated MAXFUN times.\n",
            "Number of function values = 10   Least value of F = -0.11556938907226563\n",
            "The corresponding X is:\n",
            "[4.11796514 4.52126324 0.69570423 4.12781503 6.55507846 1.80713073\n",
            " 0.9645473  6.23812214]\n",
            "\n",
            "-0.8355383835212453\n",
            " message: Return from COBYLA because the objective function has been evaluated MAXFUN times.\n",
            " success: False\n",
            "  status: 3\n",
            "     fun: -0.11556938907226563\n",
            "       x: [ 4.118e+00  4.521e+00  6.957e-01  4.128e+00  6.555e+00\n",
            "            1.807e+00  9.645e-01  6.238e+00]\n",
            "    nfev: 10\n",
            "   maxcv: 0.0\n"
          ]
        }
      ],
      "source": [
        "res = minimize(\n",
        "    cost_func,\n",
        "    x0,\n",
        "    args=(ansatz_isa, hamiltonian_isa, estimator),\n",
        "    method=\"cobyla\",\n",
        "    options={\"maxiter\": 10, \"disp\": True},\n",
        ")\n",
        "\n",
        "print(getattr(res, \"fun\") - nuclear_repulsion)\n",
        "print(res)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f3e80300-1546-4a81-8c29-c0a065766ca6",
      "metadata": {},
      "source": [
        "This code evaluated correctly, though it did not converge, which we expected. We will proceed running the calculation on real hardware, and then discuss the outputs. For real backends, we will use the IBM Quantum Estimator. We will want to execute this inside a session and we will generally want to specify options for that session.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "4fc625a3-4d62-4adc-a7f1-c689d7bf46e1",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService, Session\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "from qiskit_ibm_runtime.options import EstimatorOptions"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a89cf843-e766-4937-b41a-ba9c3d70e665",
      "metadata": {},
      "source": [
        "Among other things, using a session means our job will only wait in the queue once, to begin. Subsequent iterations of the classical optimizer will not be queued. In the session, we can set resilience and optimization levels. These tools are important enough that we include a brief review of each and their importance in VQE, with links to learn more:\n",
        "\n",
        "* Sessions: VQE is inherently iterative, with the classical optimizer selecting new variational parameters, and thus new gates being used, in each subsequent trial. Without using sessions, this could result in additional queue time between each trial circuit. Encapsulating the VQE calculation inside a session results in only one initial queue prior to the job's start, but no additional queue time between variational steps. This strategy was already used in the previous lesson's example, but may play an even more important role when varying geometry. For more about sessions, review the  [execution modes documentation](/docs/guides/execution-modes).\n",
        "* Estimator's built-in optimization: In Estimator there are built-in options for optimizing a calculation. In many contexts (Estimator included), settings are limited to 0 and 1, with 0 indicating no optimization, and 1 (the default) indicating some optimization of your circuit to hardware selected. Some other contexts allow settings of  0, 1, 2, or 3. For more on the specific methods used in different settings, see the [documentation](/docs/api/qiskit-ibm-runtime/options-estimator-options). Here, we will actually set optimization to 0, and use 'skip\\_transpilation = true', because we have already transpiled our circuit using the pass manager above, in the optimization section.\n",
        "* Estimator's built-in resilience: As with optimization, Estimator has built-in settings for resilience against errors, corresponding to different approaches to error mitigation. To learn about resilience level settings, refer to the  [documentation](/docs/guides/error-mitigation-and-suppression-techniques).\n",
        "\n",
        "It is worth noting that error mitigation plays a nuanced role in the convergence of a VQE calculation. The classical optimizer is searching parameter space for those parameters that minimize the energy. When you are very far from the optimal parameters, a steep gradient may be apparent to the classical optimizer even in the presence of errors. But as the calculation converges and you approach the optimal values, the gradient becomes smaller, and more easily washed out by errors. How much error mitigation do you want to use? At what points in the convergence? These are choices you must make for your particular use case.\n",
        "\n",
        "For this first hardware run, we have set resilience to 0 to facilitate a relatively fast run. For any serious application, you will want to use error mitigation. Note that in the cell below there are two sets of options: (1) options for the session, which we have named \"session\\_options\", and (2) options for the classical optimizer, simply called \"options\" here.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "e45da952-08ee-44be-a091-8f5a2a3abeea",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Return from COBYLA because the objective function has been evaluated MAXFUN times.\n",
            "Number of function values = 10   Least value of F = -0.11691688904\n",
            "The corresponding X is:\n",
            "[5.11796514 5.52126324 0.69570423 5.12781503 6.55507846 1.80713073\n",
            " 1.9645473  6.23812214]\n",
            "\n"
          ]
        }
      ],
      "source": [
        "estimator_options = EstimatorOptions(resilience_level=0, default_shots=2000)\n",
        "with Session(backend=backend) as session:\n",
        "    estimator = Estimator(mode=session, options=estimator_options)\n",
        "\n",
        "    res = minimize(\n",
        "        cost_func,\n",
        "        x0,\n",
        "        args=(ansatz_isa, hamiltonian_isa, estimator),\n",
        "        method=\"cobyla\",\n",
        "        options={\"maxiter\": 10, \"disp\": True},\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "deaf4bb5-032e-4d86-a517-29111acaf53f",
      "metadata": {},
      "source": [
        "You can view the progress of your job on the IBM Quantum® Platform under [Workloads](/workloads).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "1092a8b9-9ff8-4c9f-85d8-2d78efed4458",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "-0.8368858834889796\n",
            " message: Return from COBYLA because the objective function has been evaluated MAXFUN times.\n",
            " success: False\n",
            "  status: 3\n",
            "     fun: -0.11691688904\n",
            "       x: [ 5.118e+00  5.521e+00  6.957e-01  5.128e+00  6.555e+00\n",
            "            1.807e+00  1.965e+00  6.238e+00]\n",
            "    nfev: 10\n",
            "   maxcv: 0.0\n"
          ]
        }
      ],
      "source": [
        "print(getattr(res, \"fun\") - nuclear_repulsion)\n",
        "print(res)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "54a7904d-95c8-4ef3-af36-b749c13594f8",
      "metadata": {},
      "source": [
        "## Step 4: Post-process, return result in classical format.\n",
        "\n",
        "Let's take a moment to ensure we understand these outputs. The \"**fun**\" output is the **minimum** value we obtained for the cost function (not necessarily the last value calculated). This is the total energy, including the positive nuclear repulsion, which is why we also defined electron\\_energy.\n",
        "\n",
        "In the case above, we have a message that the maximum number of function evaluations was exceeded, and that the number of function evaluations (**nfev**) was 10. This simply means that other criteria for convergence of the optimization were not met; in other words, there is no reason to think we found the ground state energy. This is also the meaning of **success** being \"False\".\n",
        "\n",
        "Finally, we have **x**. This is the vector of variational parameters. These are the parameters used in the calculation that yielded the minimum cost function (energy expectation value). These eight values correspond to the eight rotation angles in those gates in the ansatz that take variable rotation angles.\n",
        "\n",
        "Congratulations! You have run a VQE calculation on an IBM Quantum QPU!\n",
        "\n",
        "In the next lesson, we will see how to adjust this workstream to include variables in your Hamiltonian. In the context of quantum chemistry problems, this might mean varying geometry to determine shapes of molecules or binding sites.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "c768e5fc-7cda-4edf-8741-d4a159a7d351",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2.1.0\n",
            "0.40.1\n"
          ]
        }
      ],
      "source": [
        "import qiskit\n",
        "import qiskit_ibm_runtime\n",
        "\n",
        "print(qiskit.version.get_version_info())\n",
        "print(qiskit_ibm_runtime.version.get_version_info())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}