diff --git a/docs/guides/_toc.json b/docs/guides/_toc.json
index 6bb134fde50..4aa1a0a321b 100644
--- a/docs/guides/_toc.json
+++ b/docs/guides/_toc.json
@@ -512,6 +512,10 @@
{
"title": "Port code to Qiskit Serverless",
"url": "/guides/serverless-port-code"
+ },
+ {
+ "title": "Build a Qiskit Function template for Hamiltonian simulation",
+ "url": "/guides/function-template-hamiltonian-simulation"
}
]
},
diff --git a/docs/guides/function-template-hamiltonian-simulation.ipynb b/docs/guides/function-template-hamiltonian-simulation.ipynb
new file mode 100644
index 00000000000..8835061e4c1
--- /dev/null
+++ b/docs/guides/function-template-hamiltonian-simulation.ipynb
@@ -0,0 +1,1441 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "b3e994de-6477-421d-8a9a-6b20d45260ae",
+ "metadata": {},
+ "source": [
+ "# Build a function template for Hamiltonian simulation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "31aee42c-1834-4fae-a05f-f78d8e5db7c0",
+ "metadata": {
+ "tags": [
+ "version-info"
+ ]
+ },
+ "source": [
+ "\n",
+ "Package versions
\n",
+ "\n",
+ "The code on this page was developed using the following requirements.\n",
+ "We recommend using these versions or newer.\n",
+ "\n",
+ "```\n",
+ "qiskit[all]~=1.2.4\n",
+ "qiskit-ibm-runtime~=0.32.0\n",
+ "qiskit-serverless~=0.18.0\n",
+ "qiskit-ibm-catalog~=0.2.0\n",
+ "```\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5103c621-809d-48e9-ac86-2bf02916e2af",
+ "metadata": {},
+ "source": [
+ "By now you've seen a basic example of how to get started writing, uploading, and running a program with Qiskit Serverless. If you haven't, start with [What is Qiskit Serverless?](/guides/serverless) for some background information first.\n",
+ "\n",
+ "The workloads you are building for your own use cases are likely not as simple as the examples previously shown. For example, you might need to carefully consider what domain-level inputs and outputs are relevant for your application, how to make sure your program can be reusable across a range of those inputs, and what kind of information you need returned during the execution of the job so you can better evaluate the progress of your workload. You might even want to incorporate a \"dry run\" mode so you can test the impact of a set of particular inputs before sending the resulting circuits to hardware.\n",
+ "\n",
+ "A function template is an example of a realistic workload that uses a specific application domain to contextualize these aspects. It is meant to be a starting point for you to modify for your own needs so you don't have to start from scratch.\n",
+ "\n",
+ "This guide demonstrates creating a function template for addressing Hamiltonian simulation problems."
+ ]
+ },
+ {
+ "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://docs.quantum.ibm.com/guides/qiskit-addons-aqc) to map the problem description to a reduced-depth circuit for execution on hardware.\n",
+ "\n",
+ "Throughout, the code is saved to `./source_files/template_hamiltonian_simulation.py`. This file is the function template you can upload to and run remotely with Qiskit Serverless."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "35f5800c-163d-47a6-9fcf-13377be57282",
+ "metadata": {
+ "tags": [
+ "remove-cell"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# This cell is hidden from users, it just creates a new folder\n",
+ "from pathlib import Path\n",
+ "\n",
+ "Path(\"./source_files\").mkdir(exist_ok=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "115c14aa-5028-46f9-ab19-b49d47519636",
+ "metadata": {},
+ "source": [
+ "### Get the domain-level 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)."
+ ]
+ },
+ {
+ "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."
+ ]
+ },
+ {
+ "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."
+ ]
+ },
+ {
+ "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": [
+ "### Perform the Hamiltonian simulation workflow using AQC-Tensor\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."
+ ]
+ },
+ {
+ "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": [
+ "#### Step 1: Map\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."
+ ]
+ },
+ {
+ "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": [
+ "#### Step 2: Optimize\n",
+ "\n",
+ "After the AQC portion of the workflow, the `final_circuit` is [transpiled for the hardware](/guides/transpile#instruction-set-architecture) as usual."
+ ]
+ },
+ {
+ "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.preset_passmanagers 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."
+ ]
+ },
+ {
+ "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": [
+ "#### Step 3: Execute on hardware"
+ ]
+ },
+ {
+ "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."
+ ]
+ },
+ {
+ "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 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 token, which you can find in your [IBM Quantum™ account](https://quantum.ibm.com/account), and upload the program.\n",
+ "\n",
+ "You can optionally use `save_account()` to save your credentials (see the [\"Authenticate to the service\" step](/guides/setup-channel#save-account)) in the Set up to use IBM Quantum Platform guide. Note that this writes your credentials to the same file as [`QiskitRuntimeService.save_account()`.](/api/qiskit-ibm-runtime/qiskit_ibm_runtime.QiskitRuntimeService#save_account)"
+ ]
+ },
+ {
+ "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:"
+ ]
+ },
+ {
+ "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": [
+ "To check if the program successfully uploaded, use `serverless.list()`:"
+ ]
+ },
+ {
+ "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:"
+ ]
+ },
+ {
+ "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."
+ ]
+ },
+ {
+ "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:"
+ ]
+ },
+ {
+ "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."
+ ]
+ },
+ {
+ "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."
+ ]
+ },
+ {
+ "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."
+ ]
+ },
+ {
+ "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",
+ "\n",
+ "\n",
+ "For a deeper dive into the AQC-Tensor Qiskit addon, check out the [Improved Trotterized Time Evolution with Approximate Quantum Compilation](https://learning.quantum.ibm.com/tutorial/improved-trotterized-time-evolution-with-approximate-quantum-compilation) tutorial or the [qiskit-addon-aqc-tensor repository](https://github.com/Qiskit/qiskit-addon-aqc-tensor).\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "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.preset_passmanagers 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",
+ "print(\"Hardware expectation values\", hw_expvals)\n",
+ "output[\"hw_expvals\"] = hw_expvals[0]\n",
+ "\n",
+ "save_result(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d7f1776a-a8f6-43a3-85b7-33975c4eeec0",
+ "metadata": {},
+ "source": [
+ "\n",
+ "Full program source code
\n",
+ "\n",
+ "Here is the entire source of `./source_files/template_hamiltonian_simulation.py` as one code block.\n",
+ "\n",
+ "\n",
+ "\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "0a5ab26b-baea-48d0-979e-485df3118ac6",
+ "metadata": {
+ "tags": [
+ "remove-cell"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "# This cell is hidden from users. It verifies both source listings are identical then deletes the working folder we created\n",
+ "import shutil\n",
+ "\n",
+ "with open(\"./source_files/template_hamiltonian_simulation.py\") as f1:\n",
+ " with open(\"./source_files/template_hamiltonian_simulation_full.py\") as f2:\n",
+ " assert f1.read() == f2.read()\n",
+ "\n",
+ "shutil.rmtree(\"./source_files/\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "description": "How to create a parallel transpilation program and deploy it to IBM Quantum Platform to use as a reusable remote service.",
+ "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"
+ },
+ "title": "Build a Qiskit Function template for Hamiltonian simulation"
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/qiskit_bot.yaml b/qiskit_bot.yaml
index 4aa43ee27da..fd36d64455c 100644
--- a/qiskit_bot.yaml
+++ b/qiskit_bot.yaml
@@ -133,6 +133,10 @@ notifications:
"docs/guides/monitor-job":
- "@jyu00"
- "@beckykd"
+ "docs/guides/function-template-hamiltonian-simulation":
+ - "@jenglick"
+ - "@garrison"
+ - "@beckykd"
"docs/guides/operator-class":
- "`@mtreinish`"
"docs/guides/online-lab-environments":
diff --git a/scripts/config/cspell/dictionaries/qiskit.txt b/scripts/config/cspell/dictionaries/qiskit.txt
index e0cdc29274d..29d4ef63978 100644
--- a/scripts/config/cspell/dictionaries/qiskit.txt
+++ b/scripts/config/cspell/dictionaries/qiskit.txt
@@ -56,6 +56,7 @@ Qedma
Qiskit
Qiskit
Quna
+quimb
RCCX
SLSQP
SPSA
diff --git a/scripts/config/notebook-testing.toml b/scripts/config/notebook-testing.toml
index cd5d110f458..7c28adf1e24 100644
--- a/scripts/config/notebook-testing.toml
+++ b/scripts/config/notebook-testing.toml
@@ -117,4 +117,5 @@ notebooks = [
notebooks = [
# This notebook contains undefined variables so can't run at all.
"docs/guides/multiverse-computing-singularity.ipynb",
+ "docs/guides/function-template-hamiltonian-simulation.ipynb",
]
diff --git a/scripts/js/commands/checkPatternsIndex.ts b/scripts/js/commands/checkPatternsIndex.ts
index ad5363673e0..10afdf6f5fb 100644
--- a/scripts/js/commands/checkPatternsIndex.ts
+++ b/scripts/js/commands/checkPatternsIndex.ts
@@ -22,6 +22,7 @@ const ALLOWLIST_MISSING_FROM_INDEX: Set = new Set([
"/guides/qiskit-code-assistant-vscode",
"/guides/qiskit-code-assistant-local",
"/guides/addons",
+ "/guides/function-template-hamiltonian-simulation",
]);
// URLs that show up in the INDEX_PAGES, but are not in the left ToC under