Skip to content

Commit

Permalink
Hamiltonian simulation function: Display entire source file at bottom (
Browse files Browse the repository at this point in the history
…#2504)

I originally wanted to be able to validate that this is the same as the
code displayed above. I had planned to do this using a cell with a
`%%writefile` directive, like is above in this file. Given this, I could
have asserted that the two files are equivalent. Unfortunately I don't
think it is possible to use Jupyter directives like this within an HTML
block in a notebook.
  • Loading branch information
garrison authored Dec 18, 2024
1 parent d5d5791 commit 0da2064
Showing 1 changed file with 262 additions and 0 deletions.
262 changes: 262 additions & 0 deletions docs/guides/serverless-template-hamiltonian-simulation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,268 @@
"</Admonition>"
]
},
{
"cell_type": "markdown",
"id": "20502fe4-7940-40fa-a978-64cc3ff6c1b1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": [
"version-info"
]
},
"source": [
"<details>\n",
"<summary><b>Full program source code</b></summary>\n",
"\n",
"Here is the entire source of `./source_files/template_hamiltonian_simulation.py` as one code block.\n",
"\n",
"```python\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 = {\"metadata\": arguments}\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)\n",
"```\n",
"</details>"
]
},
{
"cell_type": "code",
"execution_count": 24,
Expand Down

0 comments on commit 0da2064

Please sign in to comment.