{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "b3e994de-6477-421d-8a9a-6b20d45260ae",
      "metadata": {},
      "source": [
        "---\n",
        "title: Build a Qiskit Function template for Hamiltonian simulation\n",
        "description: How to create a parallel transpilation program and deploy it to IBM Quantum Platform to use as a reusable remote service.\n",
        "---\n",
        "\n",
        "# Build a function template for Hamiltonian simulation\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "31aee42c-1834-4fae-a05f-f78d8e5db7c0",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b51e81bf-0bbf-4f64-af1e-87fcb443d997",
      "metadata": {},
      "source": [
        "This template encapsulates a workflow to simulate the time evolution of an initial state against a user defined spin-based Hamiltonian and returns a set of specified expectation values using the [AQC-Tensor](https://qiskit.github.io/qiskit-addon-aqc-tensor/) Qiskit addon.\n",
        "\n",
        "This template is structured as a Qiskit pattern with the following steps:\n",
        "\n",
        "#### 1. Collecting input and mapping the problem\n",
        "\n",
        "This section takes as an input the Hamiltonian to simulate, an initial state in the form of a `QuantumCircuit`, a set of observables to estimate expectation values, and a specification of options for the AQC addon. This step validates that all required input data is present and that they are in the correct format.\n",
        "\n",
        "The input arguments are then used to construct the relevant quantum circuits and operators for the workflow. A target circuit is created and a matrix product state representation of this circuit is found using the AQC addon. Following this, an ansatz circuit is generated and optimized using tensor network methods, producing a final circuit which executes the remainder of the time evolution.\n",
        "\n",
        "#### 2. Prepare the generated circuits for execution\n",
        "\n",
        "The generated circuits from the AQC addon are then transpiled to execute on a chosen backend. An [`EstimatorV2`](../api/qiskit-ibm-runtime/estimator-v2) instance is created with a default set of error mitigation options to manage the circuit execution.\n",
        "\n",
        "#### 3. Execution\n",
        "\n",
        "Finally, the ansatz circuit is transpiled and executed on a QPU and collects estimates for all of the specified expectation values, which are returned in a serializable format for access by the user.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e451f954-1d8f-4687-a7e9-e4b0dfa170f3",
      "metadata": {},
      "source": [
        "## Write the function template\n",
        "\n",
        "First, write a function template for Hamiltonian simulation that uses the [AQC-Tensor Qiskit addon](https://qiskit.github.io/qiskit-addon-aqc-tensor/) to map the problem description to a reduced-depth circuit for execution on hardware.\n",
        "\n",
        "If you download this page and view it locally in a notebook editor, you will see some of the code cells contain the [magic command](https://ipython.readthedocs.io/en/stable/interactive/magics.html#cellmagic-writefile) `%%writefile`. This magic command saves the code to `./source_files/template_hamiltonian_simulation.py`, which is the function template you can upload to and run remotely with Qiskit Serverless.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "115c14aa-5028-46f9-ab19-b49d47519636",
      "metadata": {},
      "source": [
        "### Collect and validate the inputs\n",
        "\n",
        "Start by getting the inputs for the template. This example has domain-specific inputs relevant for Hamiltonian simulation (such as the Hamiltonian and observable) and capability-specific options (such as how much you want to compress the initial layers of the Trotter circuit using AQC-Tensor, or advanced options for fine-tuning error suppression and mitigation beyond the defaults that are part of this example).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "5e1e974b-feaa-47ce-abd1-65d442e8176e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Writing ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit_serverless import get_arguments, save_result\n",
        "\n",
        "\n",
        "# Extract parameters from arguments\n",
        "#\n",
        "# Do this at the top of the program so it fails early if any required arguments are missing or invalid.\n",
        "\n",
        "arguments = get_arguments()\n",
        "\n",
        "dry_run = arguments.get(\"dry_run\", False)\n",
        "backend_name = arguments[\"backend_name\"]\n",
        "\n",
        "aqc_evolution_time = arguments[\"aqc_evolution_time\"]\n",
        "aqc_ansatz_num_trotter_steps = arguments[\"aqc_ansatz_num_trotter_steps\"]\n",
        "aqc_target_num_trotter_steps = arguments[\"aqc_target_num_trotter_steps\"]\n",
        "\n",
        "remainder_evolution_time = arguments[\"remainder_evolution_time\"]\n",
        "remainder_num_trotter_steps = arguments[\"remainder_num_trotter_steps\"]\n",
        "\n",
        "# Stop if this fidelity is achieved\n",
        "aqc_stopping_fidelity = arguments.get(\"aqc_stopping_fidelity\", 1.0)\n",
        "# Stop after this number of iterations, even if stopping fidelity is not achieved\n",
        "aqc_max_iterations = arguments.get(\"aqc_max_iterations\", 500)\n",
        "\n",
        "hamiltonian = arguments[\"hamiltonian\"]\n",
        "observable = arguments[\"observable\"]\n",
        "initial_state = arguments.get(\"initial_state\", QuantumCircuit(hamiltonian.num_qubits))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "3f2629d5-5183-432a-8802-115a3b2f6ff7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "import numpy as np\n",
        "import json\n",
        "from mergedeep import merge\n",
        "\n",
        "\n",
        "# Configure `EstimatorOptions`, to control the parameters of the hardware experiment\n",
        "#\n",
        "# Set default options\n",
        "estimator_default_options = {\n",
        "    \"resilience\": {\n",
        "        \"measure_mitigation\": True,\n",
        "        \"zne_mitigation\": True,\n",
        "        \"zne\": {\n",
        "            \"amplifier\": \"gate_folding\",\n",
        "            \"noise_factors\": [1, 2, 3],\n",
        "            \"extrapolated_noise_factors\": list(np.linspace(0, 3, 31)),\n",
        "            \"extrapolator\": [\"exponential\", \"linear\", \"fallback\"],\n",
        "        },\n",
        "        \"measure_noise_learning\": {\n",
        "            \"num_randomizations\": 512,\n",
        "            \"shots_per_randomization\": 512,\n",
        "        },\n",
        "    },\n",
        "    \"twirling\": {\n",
        "        \"enable_gates\": True,\n",
        "        \"enable_measure\": True,\n",
        "        \"num_randomizations\": 300,\n",
        "        \"shots_per_randomization\": 100,\n",
        "        \"strategy\": \"active\",\n",
        "    },\n",
        "}\n",
        "# Merge with user-provided options\n",
        "estimator_options = merge(\n",
        "    arguments.get(\"estimator_options\", {}), estimator_default_options\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac4e21bf-ccec-459e-984b-dc6a13ea56c8",
      "metadata": {},
      "source": [
        "When the function template is running, it is helpful to return information in the logs by using print statements, so that you can better evaluate the workload's progress. Following is a simple example of printing the `estimator_options` so  there is a record of the actual Estimator options used. There are many more similar examples throughout the program to report progress during execution, including the value of the objective function during the iterative component of AQC-Tensor, and the two-qubit depth of the final instruction set architecture (ISA) circuit intended for execution on hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "be933b77-fb13-4875-9734-4226067bc8d2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "print(\"estimator_options =\", json.dumps(estimator_options, indent=4))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fb65718c-8837-4610-87b7-59e6dc7abb80",
      "metadata": {},
      "source": [
        "#### Validate the inputs\n",
        "\n",
        "An important aspect of ensuring that the template can be reused across a range of inputs is input validation. The following code is an example of verifying that the stopping fidelity during AQC-Tensor has been specified appropriately and if not, returning an informative error message for how to fix the error.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "0af1aee2-5771-4ae1-82dc-3ec08943de54",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "# Perform parameter validation\n",
        "\n",
        "if not 0.0 < aqc_stopping_fidelity <= 1.0:\n",
        "    raise ValueError(\n",
        "        f\"Invalid stopping fidelity: {aqc_stopping_fidelity}.  It must be a positive float no greater than 1.\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e5495ee-82e2-43dc-bca7-f8e81f8b6302",
      "metadata": {},
      "source": [
        "#### Prepare the function outputs\n",
        "\n",
        "First, prepare a dictionary to hold all of the function template outputs. Keys will be added to this dictionary throughout the workflow, and it is returned at the end of the program.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "1677fa7c-3b4a-4a24-b1e4-e03d9b3c49da",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "output = {}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3acd5522-a880-4de7-9251-2d68efc261ad",
      "metadata": {},
      "source": [
        "### Map the problem and pre-process the circuit with AQC\n",
        "\n",
        "The AQC-Tensor optimization happens in step 1 of a Qiskit pattern.  First, a target state is constructed.  In this example, it is constructed from a target circuit that evolves the same Hamiltonian for the same time period as the AQC portion.  Then, an ansatz is generated from an equivalent circuit but with fewer Trotter steps.  In the main portion of the AQC algorithm, that ansatz is iteratively brought closer to the target state.  Finally, the result is combined with the remainder of the Trotter steps needed to reach the desired evolution time.\n",
        "\n",
        "Note the additional examples of logging incorporated in the following code.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "92b59882-2844-4312-a09b-3da02c63f60b",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "import os\n",
        "os.environ[\"NUMBA_CACHE_DIR\"] = \"/data\"\n",
        "\n",
        "import datetime\n",
        "import quimb.tensor\n",
        "from scipy.optimize import OptimizeResult, minimize\n",
        "from qiskit.synthesis import SuzukiTrotter\n",
        "from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit\n",
        "from qiskit_addon_aqc_tensor.ansatz_generation import (\n",
        "    generate_ansatz_from_circuit,\n",
        "    AnsatzBlock,\n",
        ")\n",
        "from qiskit_addon_aqc_tensor.simulation import (\n",
        "    tensornetwork_from_circuit,\n",
        "    compute_overlap,\n",
        ")\n",
        "from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator\n",
        "from qiskit_addon_aqc_tensor.objective import OneMinusFidelity\n",
        "\n",
        "print(\"Hamiltonian:\", hamiltonian)\n",
        "print(\"Observable:\", observable)\n",
        "simulator_settings = QuimbSimulator(quimb.tensor.CircuitMPS, autodiff_backend=\"jax\")\n",
        "\n",
        "# Construct the AQC target circuit\n",
        "aqc_target_circuit = initial_state.copy()\n",
        "if aqc_evolution_time:\n",
        "    aqc_target_circuit.compose(\n",
        "        generate_time_evolution_circuit(\n",
        "            hamiltonian,\n",
        "            synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),\n",
        "            time=aqc_evolution_time,\n",
        "        ),\n",
        "        inplace=True,\n",
        "    )\n",
        "\n",
        "# Construct matrix-product state representation of the AQC target state\n",
        "aqc_target_mps = tensornetwork_from_circuit(aqc_target_circuit, simulator_settings)\n",
        "print(\"Target MPS maximum bond dimension:\", aqc_target_mps.psi.max_bond())\n",
        "output[\"target_bond_dimension\"] = aqc_target_mps.psi.max_bond()\n",
        "\n",
        "# Generate an ansatz and initial parameters from a Trotter circuit with fewer steps\n",
        "aqc_good_circuit = initial_state.copy()\n",
        "if aqc_evolution_time:\n",
        "    aqc_good_circuit.compose(\n",
        "        generate_time_evolution_circuit(\n",
        "            hamiltonian,\n",
        "            synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),\n",
        "            time=aqc_evolution_time,\n",
        "        ),\n",
        "        inplace=True,\n",
        "    )\n",
        "aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(aqc_good_circuit)\n",
        "print(\"Number of AQC parameters:\", len(aqc_initial_parameters))\n",
        "output[\"num_aqc_parameters\"] = len(aqc_initial_parameters)\n",
        "\n",
        "# Calculate the fidelity of ansatz circuit vs. the target state, before optimization\n",
        "good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)\n",
        "starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2\n",
        "print(\"Starting fidelity of AQC portion:\", starting_fidelity)\n",
        "output[\"aqc_starting_fidelity\"] = starting_fidelity\n",
        "\n",
        "# Optimize the ansatz parameters by using MPS calculations\n",
        "def callback(intermediate_result: OptimizeResult):\n",
        "    fidelity = 1 - intermediate_result.fun\n",
        "    print(f\"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}\")\n",
        "    if intermediate_result.fun < stopping_point:\n",
        "        raise StopIteration\n",
        "\n",
        "\n",
        "objective = OneMinusFidelity(aqc_target_mps, aqc_ansatz, simulator_settings)\n",
        "stopping_point = 1.0 - aqc_stopping_fidelity\n",
        "\n",
        "result = minimize(\n",
        "    objective,\n",
        "    aqc_initial_parameters,\n",
        "    method=\"L-BFGS-B\",\n",
        "    jac=True,\n",
        "    options={\"maxiter\": aqc_max_iterations},\n",
        "    callback=callback,\n",
        ")\n",
        "if result.status not in (\n",
        "    0,\n",
        "    1,\n",
        "    99,\n",
        "):  # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration\n",
        "    raise RuntimeError(\n",
        "        f\"Optimization failed: {result.message} (status={result.status})\"\n",
        "    )\n",
        "print(f\"Done after {result.nit} iterations.\")\n",
        "output[\"num_iterations\"] = result.nit\n",
        "aqc_final_parameters = result.x\n",
        "output[\"aqc_final_parameters\"] = list(aqc_final_parameters)\n",
        "\n",
        "# Construct an optimized circuit for initial portion of time evolution\n",
        "aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)\n",
        "\n",
        "# Calculate fidelity after optimization\n",
        "aqc_final_mps = tensornetwork_from_circuit(aqc_final_circuit, simulator_settings)\n",
        "aqc_fidelity = abs(compute_overlap(aqc_final_mps, aqc_target_mps)) ** 2\n",
        "print(\"Fidelity of AQC portion:\", aqc_fidelity)\n",
        "output[\"aqc_fidelity\"] = aqc_fidelity\n",
        "\n",
        "# Construct final circuit, with remainder of time evolution\n",
        "final_circuit = aqc_final_circuit.copy()\n",
        "if remainder_evolution_time:\n",
        "    remainder_circuit = generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=remainder_num_trotter_steps),\n",
        "        time=remainder_evolution_time,\n",
        "    )\n",
        "    final_circuit.compose(remainder_circuit, inplace=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae8e53a6-2106-4753-ad1a-ebc146cd44ab",
      "metadata": {},
      "source": [
        "### Optimize the final circuit for execution\n",
        "\n",
        "After the AQC portion of the workflow, the `final_circuit` is [transpiled for the hardware](/docs/guides/transpile#instruction-set-architecture) as usual.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "1db2749f-1285-48c6-8ec3-bc9f422686e2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.backend(backend_name)\n",
        "\n",
        "# Transpile PUBs (circuits and observables) to match ISA\n",
        "pass_manager = generate_preset_pass_manager(backend=backend, optimization_level=3)\n",
        "isa_circuit = pass_manager.run(final_circuit)\n",
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "isa_2qubit_depth = isa_circuit.depth(lambda x: x.operation.num_qubits == 2)\n",
        "print(\"ISA circuit two-qubit depth:\", isa_2qubit_depth)\n",
        "output[\"twoqubit_depth\"] = isa_2qubit_depth"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c41b9c0e-1f00-454c-acff-c194cc16e03e",
      "metadata": {},
      "source": [
        "#### Exit early if using dry run mode\n",
        "\n",
        "If dry run mode has been selected, then the program is stopped before executing on hardware. This can be useful if, for example, you want first to inspect the two-qubit depth of the ISA circuit before deciding to execute on hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e7c6c770-0453-4ad7-9a3e-20a47208768a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "# Exit now if dry run; don't execute on hardware\n",
        "if dry_run:\n",
        "    import sys\n",
        "\n",
        "    print(\"Exiting before hardware execution since `dry_run` is True.\")\n",
        "    save_result(output)\n",
        "    sys.exit(0)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3a603817-abaf-4403-beea-cca838a59577",
      "metadata": {},
      "source": [
        "#### Execute the circuit on hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "b7525014-a473-4d9d-b7cb-9c590f2364ae",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "# ## Step 3: Execute quantum experiments on backend\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "\n",
        "\n",
        "estimator = Estimator(backend, options=estimator_options)\n",
        "\n",
        "# Submit the underlying Estimator job. Note that this is not the\n",
        "# actual function job.\n",
        "job = estimator.run([(isa_circuit, isa_observable)])\n",
        "print(\"Job ID:\", job.job_id())\n",
        "output[\"job_id\"] = job.job_id()\n",
        "\n",
        "# Wait until job is complete\n",
        "hw_results = job.result()\n",
        "hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]\n",
        "\n",
        "# Save hardware results to serverless output dictionary\n",
        "output[\"hw_results\"] = hw_results_dicts\n",
        "\n",
        "# Reorganize expectation values\n",
        "hw_expvals = [pub_result_data[\"evs\"].tolist() for pub_result_data in hw_results_dicts]\n",
        "\n",
        "# Save expectation values to Qiskit Serverless\n",
        "print(\"Hardware expectation values\", hw_expvals)\n",
        "output[\"hw_expvals\"] = hw_expvals[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "12df35c4-4f1c-48cb-a323-d0cf33422fab",
      "metadata": {},
      "source": [
        "#### Save the output\n",
        "\n",
        "This function template returns the relevant domain-level output for this Hamiltonian simulation workflow (expectation values) in addition to important metadata generated along the way.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "bfb8ab87-c42d-4993-92bd-8b38364dc443",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Appending to ./source_files/template_hamiltonian_simulation.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile --append ./source_files/template_hamiltonian_simulation.py\n",
        "\n",
        "save_result(output)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "220f8bc8-4f04-41d3-86f1-15e9c2f45a56",
      "metadata": {},
      "source": [
        "## Deploy the function to IBM Quantum Platform\n",
        "\n",
        "The previous section created a program to be run remotely. The code in this section uploads that program to Qiskit Serverless.\n",
        "\n",
        "Use `qiskit-ibm-catalog` to authenticate to `QiskitServerless` with your API key, which you can find on the [IBM Quantum Platform]() dashboard, and upload the program.\n",
        "\n",
        "You can optionally use `save_account()` to save your credentials (see the [Set up your IBM Cloud account](/docs/guides/cloud-setup#cloud-save) guide). Note that this writes your credentials to the same file as [`QiskitRuntimeService.save_account()`](/docs/api/qiskit-ibm-runtime/qiskit-runtime-service#save_account).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "313bd03b-bf9b-4e6c-aa05-1fe8def3868d",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_catalog import QiskitServerless, QiskitFunction\n",
        "\n",
        "# Authenticate to the remote cluster and submit the pattern for remote execution\n",
        "serverless = QiskitServerless()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9c495d4-cdab-4329-b65d-d111029aee64",
      "metadata": {},
      "source": [
        "This program has custom `pip` dependencies.  Add them to a `dependencies` array when constructing the `QiskitFunction` instance:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "ca386323-d92d-4c41-908b-1670324e1264",
      "metadata": {},
      "outputs": [],
      "source": [
        "template = QiskitFunction(\n",
        "    title=\"template_hamiltonian_simulation\",\n",
        "    entrypoint=\"template_hamiltonian_simulation.py\",\n",
        "    working_dir=\"./source_files/\",\n",
        "    dependencies=[\n",
        "        \"qiskit-addon-utils~=0.1.0\",\n",
        "        \"qiskit-addon-aqc-tensor[quimb-jax]~=0.1.2\",\n",
        "        \"mergedeep==1.3.4\",\n",
        "    ],\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "80a2c6b5-1f1e-4e90-9b1f-75907caf1df3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "QiskitFunction(template_hamiltonian_simulation)"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "serverless.upload(template)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "06677105-627a-4948-aac7-071f44327a0b",
      "metadata": {},
      "source": [
        "Finally, check if the program successfully uploaded, use `serverless.list()`:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "11f14088-8eca-4a99-a291-3f37a61b0d26",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              " QiskitFunction(template_hamiltonian_simulation),\n"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "serverless.list()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "385bda3e-73de-413c-8e45-cf7047303219",
      "metadata": {},
      "source": [
        "## Run the function template remotely\n",
        "\n",
        "The function template has been uploaded, so you can run it remotely with Qiskit Serverless. First, load the template by name:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "d4ff8dea-95ab-4cc9-92b9-e28e277e76f2",
      "metadata": {},
      "outputs": [],
      "source": [
        "template = serverless.load(\"template_hamiltonian_simulation\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "57964962-87e9-4c0b-a717-cb620019960e",
      "metadata": {},
      "source": [
        "Next, run the template with the domain-level inputs for Hamiltonian simulation. This example specifies a 50-qubit XXZ model with random couplings, and an initial state and observable.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "e5d1555c-9abb-4fef-b8a2-f8d1b723df01",
      "metadata": {},
      "outputs": [],
      "source": [
        "from itertools import chain\n",
        "import numpy as np\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "L = 50\n",
        "\n",
        "# Generate the edge list for this spin-chain\n",
        "edges = [(i, i + 1) for i in range(L - 1)]\n",
        "# Generate an edge-coloring so we can make hw-efficient circuits\n",
        "edges = edges[::2] + edges[1::2]\n",
        "\n",
        "# Generate random coefficients for our XXZ Hamiltonian\n",
        "np.random.seed(0)\n",
        "Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)\n",
        "\n",
        "hamiltonian = SparsePauliOp.from_sparse_list(\n",
        "    chain.from_iterable(\n",
        "        [\n",
        "            [\n",
        "                (\"XX\", (i, j), Js[i] / 2),\n",
        "                (\"YY\", (i, j), Js[i] / 2),\n",
        "                (\"ZZ\", (i, j), Js[i]),\n",
        "            ]\n",
        "            for i, j in edges\n",
        "        ]\n",
        "    ),\n",
        "    num_qubits=L,\n",
        ")\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"ZZ\", (L // 2 - 1, L // 2), 1.0)], num_qubits=L\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "7ec5ab02-280c-4c04-8e63-e5354eb8ebb9",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "initial_state = QuantumCircuit(L)\n",
        "for i in range(L):\n",
        "    if i % 2:\n",
        "        initial_state.x(i)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "ac8d8fec-0290-4d6b-a18c-afd02acfa850",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "853b0edb-d63f-4629-be71-398b6dcf33cb\n"
          ]
        }
      ],
      "source": [
        "job = template.run(\n",
        "    dry_run=True,\n",
        "    initial_state=initial_state,\n",
        "    hamiltonian=hamiltonian,\n",
        "    observable=observable,\n",
        "    backend_name=\"ibm_fez\",\n",
        "    estimator_options={},\n",
        "    aqc_evolution_time=0.2,\n",
        "    aqc_ansatz_num_trotter_steps=1,\n",
        "    aqc_target_num_trotter_steps=32,\n",
        "    remainder_evolution_time=0.2,\n",
        "    remainder_num_trotter_steps=4,\n",
        "    aqc_max_iterations=300,\n",
        ")\n",
        "print(job.job_id)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2249cabc-6821-4d84-b4d8-e519c2d94a5c",
      "metadata": {},
      "source": [
        "Check the status of the job:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "ed3b744d-cb00-43e0-907c-34dfebe2fa9b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'QUEUED'"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "job.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "83751f45-ab37-4037-bc77-d1aef91ed46c",
      "metadata": {},
      "source": [
        "After the job is running, you can fetch logs created from the `print()` outputs. These can provide actionable information about the progress of the Hamiltonian simulation workflow. For example, the value of the objective function during the iterative component of AQC, or the two-qubit depth of the final ISA circuit intended for execution on hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "3be62464-18b0-4598-b386-0ac0cc9cccb6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "No logs yet.\n"
          ]
        }
      ],
      "source": [
        "print(job.logs())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a8384509-2a9d-44e6-a729-40464ff52bea",
      "metadata": {},
      "source": [
        "Block the rest of the program until a result is available. After the job is done, you can retrieve the results. These include the domain-level output of Hamiltonian simulation (expectation value) and useful metadata.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "fc678bcd-539d-4970-86e0-9a69f0a367ef",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'target_bond_dimension': 5,\n",
              " 'num_aqc_parameters': 816,\n",
              " 'aqc_starting_fidelity': 0.9914382555614002,\n",
              " 'num_iterations': 72,\n",
              " 'aqc_fidelity': 0.9998108844412502,\n",
              " 'twoqubit_depth': 33}"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result = job.result()\n",
        "\n",
        "del result[\n",
        "    \"aqc_final_parameters\"\n",
        "]  # the list is too long to conveniently display here\n",
        "result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "12b0ccfe-325a-4939-81e7-58d94557990d",
      "metadata": {},
      "source": [
        "After the job completes, the entire logging output will be available.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "cb722373-cbfb-45a7-a2b5-d7a97e18ee6c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2024-12-17 14:50:15,580\tINFO job_manager.py:531 -- Runtime env is setting up.\n",
            "estimator_options = {\n",
            "    \"resilience\": {\n",
            "        \"measure_mitigation\": true,\n",
            "        \"zne_mitigation\": true,\n",
            "        \"zne\": {\n",
            "            \"amplifier\": \"gate_folding\",\n",
            "            \"noise_factors\": [\n",
            "                1,\n",
            "                2,\n",
            "                3\n",
            "            ],\n",
            "            \"extrapolated_noise_factors\": [\n",
            "                0.0,\n",
            "                0.1,\n",
            "                0.2,\n",
            "                0.30000000000000004,\n",
            "                0.4,\n",
            "                0.5,\n",
            "                0.6000000000000001,\n",
            "                0.7000000000000001,\n",
            "                0.8,\n",
            "                0.9,\n",
            "                1.0,\n",
            "                1.1,\n",
            "                1.2000000000000002,\n",
            "                1.3,\n",
            "                1.4000000000000001,\n",
            "                1.5,\n",
            "                1.6,\n",
            "                1.7000000000000002,\n",
            "                1.8,\n",
            "                1.9000000000000001,\n",
            "                2.0,\n",
            "                2.1,\n",
            "                2.2,\n",
            "                2.3000000000000003,\n",
            "                2.4000000000000004,\n",
            "                2.5,\n",
            "                2.6,\n",
            "                2.7,\n",
            "                2.8000000000000003,\n",
            "                2.9000000000000004,\n",
            "                3.0\n",
            "            ],\n",
            "            \"extrapolator\": [\n",
            "                \"exponential\",\n",
            "                \"linear\",\n",
            "                \"fallback\"\n",
            "            ]\n",
            "        },\n",
            "        \"measure_noise_learning\": {\n",
            "            \"num_randomizations\": 512,\n",
            "            \"shots_per_randomization\": 512\n",
            "        }\n",
            "    },\n",
            "    \"twirling\": {\n",
            "        \"enable_gates\": true,\n",
            "        \"enable_measure\": true,\n",
            "        \"num_randomizations\": 300,\n",
            "        \"shots_per_randomization\": 100,\n",
            "        \"strategy\": \"active\"\n",
            "    }\n",
            "}\n",
            "Hamiltonian: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYY', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'XXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'YYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IXXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IYYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
            "              coeffs=[0.52440675+0.j, 0.52440675+0.j, 1.0488135 +0.j, 0.55138169+0.j,\n",
            " 0.55138169+0.j, 1.10276338+0.j, 0.4618274 +0.j, 0.4618274 +0.j,\n",
            " 0.9236548 +0.j, 0.46879361+0.j, 0.46879361+0.j, 0.93758721+0.j,\n",
            " 0.73183138+0.j, 0.73183138+0.j, 1.46366276+0.j, 0.64586252+0.j,\n",
            " 0.64586252+0.j, 1.29172504+0.j, 0.53402228+0.j, 0.53402228+0.j,\n",
            " 1.06804456+0.j, 0.28551803+0.j, 0.28551803+0.j, 0.57103606+0.j,\n",
            " 0.2601092 +0.j, 0.2601092 +0.j, 0.5202184 +0.j, 0.63907838+0.j,\n",
            " 0.63907838+0.j, 1.27815675+0.j, 0.73930917+0.j, 0.73930917+0.j,\n",
            " 1.47861834+0.j, 0.48073968+0.j, 0.48073968+0.j, 0.96147936+0.j,\n",
            " 0.30913721+0.j, 0.30913721+0.j, 0.61827443+0.j, 0.32167664+0.j,\n",
            " 0.32167664+0.j, 0.64335329+0.j, 0.51092416+0.j, 0.51092416+0.j,\n",
            " 1.02184832+0.j, 0.38227781+0.j, 0.38227781+0.j, 0.76455561+0.j,\n",
            " 0.47807517+0.j, 0.47807517+0.j, 0.95615033+0.j, 0.2593949 +0.j,\n",
            " 0.2593949 +0.j, 0.5187898 +0.j, 0.55604786+0.j, 0.55604786+0.j,\n",
            " 1.11209572+0.j, 0.72187404+0.j, 0.72187404+0.j, 1.44374808+0.j,\n",
            " 0.42975395+0.j, 0.42975395+0.j, 0.8595079 +0.j, 0.5988156 +0.j,\n",
            " 0.5988156 +0.j, 1.1976312 +0.j, 0.58338336+0.j, 0.58338336+0.j,\n",
            " 1.16676672+0.j, 0.35519128+0.j, 0.35519128+0.j, 0.71038256+0.j,\n",
            " 0.40771418+0.j, 0.40771418+0.j, 0.81542835+0.j, 0.60759468+0.j,\n",
            " 0.60759468+0.j, 1.21518937+0.j, 0.52244159+0.j, 0.52244159+0.j,\n",
            " 1.04488318+0.j, 0.57294706+0.j, 0.57294706+0.j, 1.14589411+0.j,\n",
            " 0.6958865 +0.j, 0.6958865 +0.j, 1.391773  +0.j, 0.44172076+0.j,\n",
            " 0.44172076+0.j, 0.88344152+0.j, 0.51444746+0.j, 0.51444746+0.j,\n",
            " 1.02889492+0.j, 0.71279832+0.j, 0.71279832+0.j, 1.42559664+0.j,\n",
            " 0.29356465+0.j, 0.29356465+0.j, 0.5871293 +0.j, 0.66630992+0.j,\n",
            " 0.66630992+0.j, 1.33261985+0.j, 0.68500607+0.j, 0.68500607+0.j,\n",
            " 1.37001215+0.j, 0.64957928+0.j, 0.64957928+0.j, 1.29915856+0.j,\n",
            " 0.64026459+0.j, 0.64026459+0.j, 1.28052918+0.j, 0.56996051+0.j,\n",
            " 0.56996051+0.j, 1.13992102+0.j, 0.72233446+0.j, 0.72233446+0.j,\n",
            " 1.44466892+0.j, 0.45733097+0.j, 0.45733097+0.j, 0.91466194+0.j,\n",
            " 0.63711684+0.j, 0.63711684+0.j, 1.27423369+0.j, 0.53421697+0.j,\n",
            " 0.53421697+0.j, 1.06843395+0.j, 0.55881775+0.j, 0.55881775+0.j,\n",
            " 1.1176355 +0.j, 0.558467  +0.j, 0.558467  +0.j, 1.116934  +0.j,\n",
            " 0.59091015+0.j, 0.59091015+0.j, 1.1818203 +0.j, 0.46851598+0.j,\n",
            " 0.46851598+0.j, 0.93703195+0.j, 0.28011274+0.j, 0.28011274+0.j,\n",
            " 0.56022547+0.j, 0.58531893+0.j, 0.58531893+0.j, 1.17063787+0.j,\n",
            " 0.31446315+0.j, 0.31446315+0.j, 0.6289263 +0.j])\n",
            "Observable: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIII'],\n",
            "              coeffs=[1.+0.j])\n",
            "Target MPS maximum bond dimension: 5\n",
            "Number of AQC parameters: 816\n",
            "Starting fidelity of AQC portion: 0.9914382555614002\n",
            "2024-12-17 14:52:23.400028 Intermediate result: Fidelity 0.99764093\n",
            "2024-12-17 14:52:23.429669 Intermediate result: Fidelity 0.99788003\n",
            "2024-12-17 14:52:23.459674 Intermediate result: Fidelity 0.99795970\n",
            "2024-12-17 14:52:23.489666 Intermediate result: Fidelity 0.99799067\n",
            "2024-12-17 14:52:23.518545 Intermediate result: Fidelity 0.99803401\n",
            "2024-12-17 14:52:23.546952 Intermediate result: Fidelity 0.99809821\n",
            "2024-12-17 14:52:23.575271 Intermediate result: Fidelity 0.99824660\n",
            "2024-12-17 14:52:23.604049 Intermediate result: Fidelity 0.99845326\n",
            "2024-12-17 14:52:23.632709 Intermediate result: Fidelity 0.99870497\n",
            "2024-12-17 14:52:23.660527 Intermediate result: Fidelity 0.99891442\n",
            "2024-12-17 14:52:23.688273 Intermediate result: Fidelity 0.99904488\n",
            "2024-12-17 14:52:23.716105 Intermediate result: Fidelity 0.99914438\n",
            "2024-12-17 14:52:23.744336 Intermediate result: Fidelity 0.99922827\n",
            "2024-12-17 14:52:23.773399 Intermediate result: Fidelity 0.99929071\n",
            "2024-12-17 14:52:23.801482 Intermediate result: Fidelity 0.99932432\n",
            "2024-12-17 14:52:23.830466 Intermediate result: Fidelity 0.99936460\n",
            "2024-12-17 14:52:23.860738 Intermediate result: Fidelity 0.99938891\n",
            "2024-12-17 14:52:23.889958 Intermediate result: Fidelity 0.99940607\n",
            "2024-12-17 14:52:23.918703 Intermediate result: Fidelity 0.99941965\n",
            "2024-12-17 14:52:23.949744 Intermediate result: Fidelity 0.99944337\n",
            "2024-12-17 14:52:23.980871 Intermediate result: Fidelity 0.99946875\n",
            "2024-12-17 14:52:24.012124 Intermediate result: Fidelity 0.99949009\n",
            "2024-12-17 14:52:24.044359 Intermediate result: Fidelity 0.99952191\n",
            "2024-12-17 14:52:24.075840 Intermediate result: Fidelity 0.99953669\n",
            "2024-12-17 14:52:24.106303 Intermediate result: Fidelity 0.99955242\n",
            "2024-12-17 14:52:24.139329 Intermediate result: Fidelity 0.99958412\n",
            "2024-12-17 14:52:24.169725 Intermediate result: Fidelity 0.99960176\n",
            "2024-12-17 14:52:24.198749 Intermediate result: Fidelity 0.99961606\n",
            "2024-12-17 14:52:24.227874 Intermediate result: Fidelity 0.99963811\n",
            "2024-12-17 14:52:24.256818 Intermediate result: Fidelity 0.99964383\n",
            "2024-12-17 14:52:24.285889 Intermediate result: Fidelity 0.99964717\n",
            "2024-12-17 14:52:24.315228 Intermediate result: Fidelity 0.99966064\n",
            "2024-12-17 14:52:24.345322 Intermediate result: Fidelity 0.99966517\n",
            "2024-12-17 14:52:24.374921 Intermediate result: Fidelity 0.99967089\n",
            "2024-12-17 14:52:24.404309 Intermediate result: Fidelity 0.99968305\n",
            "2024-12-17 14:52:24.432664 Intermediate result: Fidelity 0.99968889\n",
            "2024-12-17 14:52:24.461639 Intermediate result: Fidelity 0.99969997\n",
            "2024-12-17 14:52:24.491244 Intermediate result: Fidelity 0.99971666\n",
            "2024-12-17 14:52:24.520354 Intermediate result: Fidelity 0.99972441\n",
            "2024-12-17 14:52:24.549965 Intermediate result: Fidelity 0.99973561\n",
            "2024-12-17 14:52:24.583464 Intermediate result: Fidelity 0.99973811\n",
            "2024-12-17 14:52:24.617537 Intermediate result: Fidelity 0.99974074\n",
            "2024-12-17 14:52:24.652247 Intermediate result: Fidelity 0.99974467\n",
            "2024-12-17 14:52:24.686831 Intermediate result: Fidelity 0.99974991\n",
            "2024-12-17 14:52:24.725476 Intermediate result: Fidelity 0.99975230\n",
            "2024-12-17 14:52:24.764637 Intermediate result: Fidelity 0.99975373\n",
            "2024-12-17 14:52:24.802499 Intermediate result: Fidelity 0.99975552\n",
            "2024-12-17 14:52:24.839960 Intermediate result: Fidelity 0.99975885\n",
            "2024-12-17 14:52:24.877472 Intermediate result: Fidelity 0.99976469\n",
            "2024-12-17 14:52:24.916233 Intermediate result: Fidelity 0.99976517\n",
            "2024-12-17 14:52:24.993750 Intermediate result: Fidelity 0.99976875\n",
            "2024-12-17 14:52:25.034953 Intermediate result: Fidelity 0.99976887\n",
            "2024-12-17 14:52:25.076197 Intermediate result: Fidelity 0.99977244\n",
            "2024-12-17 14:52:25.112340 Intermediate result: Fidelity 0.99977638\n",
            "2024-12-17 14:52:25.149947 Intermediate result: Fidelity 0.99977828\n",
            "2024-12-17 14:52:25.190049 Intermediate result: Fidelity 0.99978174\n",
            "2024-12-17 14:52:25.310903 Intermediate result: Fidelity 0.99978222\n",
            "2024-12-17 14:52:25.347512 Intermediate result: Fidelity 0.99978508\n",
            "2024-12-17 14:52:25.385201 Intermediate result: Fidelity 0.99978543\n",
            "2024-12-17 14:52:25.457436 Intermediate result: Fidelity 0.99978770\n",
            "2024-12-17 14:52:25.497133 Intermediate result: Fidelity 0.99978818\n",
            "2024-12-17 14:52:25.541179 Intermediate result: Fidelity 0.99978913\n",
            "2024-12-17 14:52:25.584791 Intermediate result: Fidelity 0.99978937\n",
            "2024-12-17 14:52:25.621484 Intermediate result: Fidelity 0.99979068\n",
            "2024-12-17 14:52:25.655847 Intermediate result: Fidelity 0.99979211\n",
            "2024-12-17 14:52:25.691710 Intermediate result: Fidelity 0.99979700\n",
            "2024-12-17 14:52:25.767711 Intermediate result: Fidelity 0.99979759\n",
            "2024-12-17 14:52:25.804517 Intermediate result: Fidelity 0.99979807\n",
            "2024-12-17 14:52:25.839394 Intermediate result: Fidelity 0.99980236\n",
            "2024-12-17 14:52:25.874438 Intermediate result: Fidelity 0.99980296\n",
            "2024-12-17 14:52:25.909900 Intermediate result: Fidelity 0.99980320\n",
            "2024-12-17 14:52:26.713044 Intermediate result: Fidelity 0.99980320\n",
            "Done after 72 iterations.\n",
            "Fidelity of AQC portion: 0.9998108844412502\n",
            "ISA circuit two-qubit depth: 33\n",
            "Exiting before hardware execution since `dry_run` is True.\n",
            "\n"
          ]
        }
      ],
      "source": [
        "print(job.logs())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "196d6261-e26b-4057-ae55-19f003fdc10a",
      "metadata": {},
      "source": [
        "## Next steps\n",
        "\n",
        "<Admonition type=\"info\" title=\"Recommendations\">\n",
        "  For a deeper dive into the AQC-Tensor Qiskit addon, check out the [Improved Trotterized Time Evolution with Approximate Quantum Compilation](/docs/tutorials/approximate-quantum-compilation-for-time-evolution) tutorial or the [qiskit-addon-aqc-tensor repository](https://github.com/Qiskit/qiskit-addon-aqc-tensor).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "20502fe4-7940-40fa-a978-64cc3ff6c1b1",
      "metadata": {
        "tags": [
          "id-full-source"
        ]
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Overwriting ./source_files/template_hamiltonian_simulation_full.py\n"
          ]
        }
      ],
      "source": [
        "%%writefile ./source_files/template_hamiltonian_simulation_full.py\n",
        "\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit_serverless import get_arguments, save_result\n",
        "\n",
        "\n",
        "# Extract parameters from arguments\n",
        "#\n",
        "# Do this at the top of the program so it fails early if any required arguments are missing or invalid.\n",
        "\n",
        "arguments = get_arguments()\n",
        "\n",
        "dry_run = arguments.get(\"dry_run\", False)\n",
        "backend_name = arguments[\"backend_name\"]\n",
        "\n",
        "aqc_evolution_time = arguments[\"aqc_evolution_time\"]\n",
        "aqc_ansatz_num_trotter_steps = arguments[\"aqc_ansatz_num_trotter_steps\"]\n",
        "aqc_target_num_trotter_steps = arguments[\"aqc_target_num_trotter_steps\"]\n",
        "\n",
        "remainder_evolution_time = arguments[\"remainder_evolution_time\"]\n",
        "remainder_num_trotter_steps = arguments[\"remainder_num_trotter_steps\"]\n",
        "\n",
        "# Stop if this fidelity is achieved\n",
        "aqc_stopping_fidelity = arguments.get(\"aqc_stopping_fidelity\", 1.0)\n",
        "# Stop after this number of iterations, even if stopping fidelity is not achieved\n",
        "aqc_max_iterations = arguments.get(\"aqc_max_iterations\", 500)\n",
        "\n",
        "hamiltonian = arguments[\"hamiltonian\"]\n",
        "observable = arguments[\"observable\"]\n",
        "initial_state = arguments.get(\"initial_state\", QuantumCircuit(hamiltonian.num_qubits))\n",
        "\n",
        "import numpy as np\n",
        "import json\n",
        "from mergedeep import merge\n",
        "\n",
        "\n",
        "# Configure `EstimatorOptions`, to control the parameters of the hardware experiment\n",
        "#\n",
        "# Set default options\n",
        "estimator_default_options = {\n",
        "    \"resilience\": {\n",
        "        \"measure_mitigation\": True,\n",
        "        \"zne_mitigation\": True,\n",
        "        \"zne\": {\n",
        "            \"amplifier\": \"gate_folding\",\n",
        "            \"noise_factors\": [1, 2, 3],\n",
        "            \"extrapolated_noise_factors\": list(np.linspace(0, 3, 31)),\n",
        "            \"extrapolator\": [\"exponential\", \"linear\", \"fallback\"],\n",
        "        },\n",
        "        \"measure_noise_learning\": {\n",
        "            \"num_randomizations\": 512,\n",
        "            \"shots_per_randomization\": 512,\n",
        "        },\n",
        "    },\n",
        "    \"twirling\": {\n",
        "        \"enable_gates\": True,\n",
        "        \"enable_measure\": True,\n",
        "        \"num_randomizations\": 300,\n",
        "        \"shots_per_randomization\": 100,\n",
        "        \"strategy\": \"active\",\n",
        "    },\n",
        "}\n",
        "# Merge with user-provided options\n",
        "estimator_options = merge(\n",
        "    arguments.get(\"estimator_options\", {}), estimator_default_options\n",
        ")\n",
        "\n",
        "print(\"estimator_options =\", json.dumps(estimator_options, indent=4))\n",
        "\n",
        "# Perform parameter validation\n",
        "\n",
        "if not 0.0 < aqc_stopping_fidelity <= 1.0:\n",
        "    raise ValueError(\n",
        "        f\"Invalid stopping fidelity: {aqc_stopping_fidelity}.  It must be a positive float no greater than 1.\"\n",
        "    )\n",
        "\n",
        "output = {}\n",
        "\n",
        "import os\n",
        "os.environ[\"NUMBA_CACHE_DIR\"] = \"/data\"\n",
        "\n",
        "import datetime\n",
        "import quimb.tensor\n",
        "from scipy.optimize import OptimizeResult, minimize\n",
        "from qiskit.synthesis import SuzukiTrotter\n",
        "from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit\n",
        "from qiskit_addon_aqc_tensor.ansatz_generation import (\n",
        "    generate_ansatz_from_circuit,\n",
        "    AnsatzBlock,\n",
        ")\n",
        "from qiskit_addon_aqc_tensor.simulation import (\n",
        "    tensornetwork_from_circuit,\n",
        "    compute_overlap,\n",
        ")\n",
        "from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator\n",
        "from qiskit_addon_aqc_tensor.objective import OneMinusFidelity\n",
        "\n",
        "print(\"Hamiltonian:\", hamiltonian)\n",
        "print(\"Observable:\", observable)\n",
        "simulator_settings = QuimbSimulator(quimb.tensor.CircuitMPS, autodiff_backend=\"jax\")\n",
        "\n",
        "# Construct the AQC target circuit\n",
        "aqc_target_circuit = initial_state.copy()\n",
        "if aqc_evolution_time:\n",
        "    aqc_target_circuit.compose(\n",
        "        generate_time_evolution_circuit(\n",
        "            hamiltonian,\n",
        "            synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),\n",
        "            time=aqc_evolution_time,\n",
        "        ),\n",
        "        inplace=True,\n",
        "    )\n",
        "\n",
        "# Construct matrix-product state representation of the AQC target state\n",
        "aqc_target_mps = tensornetwork_from_circuit(aqc_target_circuit, simulator_settings)\n",
        "print(\"Target MPS maximum bond dimension:\", aqc_target_mps.psi.max_bond())\n",
        "output[\"target_bond_dimension\"] = aqc_target_mps.psi.max_bond()\n",
        "\n",
        "# Generate an ansatz and initial parameters from a Trotter circuit with fewer steps\n",
        "aqc_good_circuit = initial_state.copy()\n",
        "if aqc_evolution_time:\n",
        "    aqc_good_circuit.compose(\n",
        "        generate_time_evolution_circuit(\n",
        "            hamiltonian,\n",
        "            synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),\n",
        "            time=aqc_evolution_time,\n",
        "        ),\n",
        "        inplace=True,\n",
        "    )\n",
        "aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(aqc_good_circuit)\n",
        "print(\"Number of AQC parameters:\", len(aqc_initial_parameters))\n",
        "output[\"num_aqc_parameters\"] = len(aqc_initial_parameters)\n",
        "\n",
        "# Calculate the fidelity of ansatz circuit vs. the target state, before optimization\n",
        "good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)\n",
        "starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2\n",
        "print(\"Starting fidelity of AQC portion:\", starting_fidelity)\n",
        "output[\"aqc_starting_fidelity\"] = starting_fidelity\n",
        "\n",
        "# Optimize the ansatz parameters by using MPS calculations\n",
        "def callback(intermediate_result: OptimizeResult):\n",
        "    fidelity = 1 - intermediate_result.fun\n",
        "    print(f\"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}\")\n",
        "    if intermediate_result.fun < stopping_point:\n",
        "        raise StopIteration\n",
        "\n",
        "\n",
        "objective = OneMinusFidelity(aqc_target_mps, aqc_ansatz, simulator_settings)\n",
        "stopping_point = 1.0 - aqc_stopping_fidelity\n",
        "\n",
        "result = minimize(\n",
        "    objective,\n",
        "    aqc_initial_parameters,\n",
        "    method=\"L-BFGS-B\",\n",
        "    jac=True,\n",
        "    options={\"maxiter\": aqc_max_iterations},\n",
        "    callback=callback,\n",
        ")\n",
        "if result.status not in (\n",
        "    0,\n",
        "    1,\n",
        "    99,\n",
        "):  # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration\n",
        "    raise RuntimeError(\n",
        "        f\"Optimization failed: {result.message} (status={result.status})\"\n",
        "    )\n",
        "print(f\"Done after {result.nit} iterations.\")\n",
        "output[\"num_iterations\"] = result.nit\n",
        "aqc_final_parameters = result.x\n",
        "output[\"aqc_final_parameters\"] = list(aqc_final_parameters)\n",
        "\n",
        "# Construct an optimized circuit for initial portion of time evolution\n",
        "aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)\n",
        "\n",
        "# Calculate fidelity after optimization\n",
        "aqc_final_mps = tensornetwork_from_circuit(aqc_final_circuit, simulator_settings)\n",
        "aqc_fidelity = abs(compute_overlap(aqc_final_mps, aqc_target_mps)) ** 2\n",
        "print(\"Fidelity of AQC portion:\", aqc_fidelity)\n",
        "output[\"aqc_fidelity\"] = aqc_fidelity\n",
        "\n",
        "# Construct final circuit, with remainder of time evolution\n",
        "final_circuit = aqc_final_circuit.copy()\n",
        "if remainder_evolution_time:\n",
        "    remainder_circuit = generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=remainder_num_trotter_steps),\n",
        "        time=remainder_evolution_time,\n",
        "    )\n",
        "    final_circuit.compose(remainder_circuit, inplace=True)\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.backend(backend_name)\n",
        "\n",
        "# Transpile PUBs (circuits and observables) to match ISA\n",
        "pass_manager = generate_preset_pass_manager(backend=backend, optimization_level=3)\n",
        "isa_circuit = pass_manager.run(final_circuit)\n",
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "isa_2qubit_depth = isa_circuit.depth(lambda x: x.operation.num_qubits == 2)\n",
        "print(\"ISA circuit two-qubit depth:\", isa_2qubit_depth)\n",
        "output[\"twoqubit_depth\"] = isa_2qubit_depth\n",
        "\n",
        "# Exit now if dry run; don't execute on hardware\n",
        "if dry_run:\n",
        "    import sys\n",
        "\n",
        "    print(\"Exiting before hardware execution since `dry_run` is True.\")\n",
        "    save_result(output)\n",
        "    sys.exit(0)\n",
        "\n",
        "# ## Step 3: Execute quantum experiments on backend\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "\n",
        "\n",
        "estimator = Estimator(backend, options=estimator_options)\n",
        "\n",
        "# Submit the underlying Estimator job. Note that this is not the\n",
        "# actual function job.\n",
        "job = estimator.run([(isa_circuit, isa_observable)])\n",
        "print(\"Job ID:\", job.job_id())\n",
        "output[\"job_id\"] = job.job_id()\n",
        "\n",
        "# Wait until job is complete\n",
        "hw_results = job.result()\n",
        "hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]\n",
        "\n",
        "# Save hardware results to serverless output dictionary\n",
        "output[\"hw_results\"] = hw_results_dicts\n",
        "\n",
        "# Reorganize expectation values\n",
        "hw_expvals = [pub_result_data[\"evs\"].tolist() for pub_result_data in hw_results_dicts]\n",
        "\n",
        "# Save expectation values to Qiskit Serverless\n",
        "output[\"hw_expvals\"] = hw_expvals[0]\n",
        "\n",
        "save_result(output)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d7f1776a-a8f6-43a3-85b7-33975c4eeec0",
      "metadata": {},
      "source": [
        "<Accordion>\n",
        "  <AccordionItem title=\"**Full program source code**\">\n",
        "    Here is the entire source of `./source_files/template_hamiltonian_simulation.py` as one code block.\n",
        "\n",
        "    <CodeCellPlaceholder tag=\"id-full-source\" />\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "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": 4
}