-
Notifications
You must be signed in to change notification settings - Fork 9
185 official device simulations #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
jordandsullivan
wants to merge
7
commits into
main
Choose a base branch
from
185-device-simulations
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
cb2d4c2
Add compilation benchmarks for two device emulators.
jordandsullivan 5adc9fd
Simplify formatting of toml files. Currently failing on some classica…
jordandsullivan 94613a3
Update QAOA observable to map onto qubit count < 10 (may choose to ju…
jordandsullivan 3710327
Added logic to remove measurements from circuits where we calculate e…
jordandsullivan 5d49898
Update data loading to use benchmark suite name
jordandsullivan 53cac44
Remove duplicate QFT entry
jordandsullivan 8be52a1
Use simpler QFT, Remove unnecessary strip measurements logic.
jordandsullivan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # Version of the specification language/format, to manage future changes | ||
| spec_version = "1.0" | ||
| # Version of this particular benchmark suite, to manage any important changes | ||
| suite_version = "0.1" | ||
| # Unique identifier for this benchmark suite | ||
| id = "fake_backend_5Q_benchmarks" | ||
| # Human readable description | ||
| description = "Compiler benchmarks running in small quantum device emulators." | ||
|
|
||
| # --------- Compilers ---------- | ||
| # For now, restricting to qiskit derived compilers to work with the same target-device options | ||
| [[compilers]] | ||
| id = "ucc" | ||
| [[compilers]] | ||
| id = "qiskit-default" | ||
|
|
||
| # --------- Target Devices ---------- | ||
| [[target_devices]] | ||
| # 5 qubits | ||
| id = "ibm_fake_manila" | ||
|
|
||
| # --------- Benchmarks ---------- | ||
|
|
||
| [[benchmarks]] | ||
| id = "mqt:qaoa_N5" | ||
| description = "mqt:qaoa circuit (N=5)" | ||
| simulate.measurement = "qaoa" | ||
| generator.name = "mqt:qaoa" | ||
| generator.params.N = 5 | ||
|
|
||
|
|
||
|
|
||
| [[benchmarks]] | ||
| id = "qft_N5" | ||
| description = "qft circuit (N=5)" | ||
| simulate.measurement = "uniform_superposition_projector" | ||
| generator.name = "qft" | ||
| generator.params.N = 5 | ||
|
|
||
|
|
||
|
|
||
| [[benchmarks]] | ||
| id = "prep_select_N5" | ||
| description = "prep_select circuit (N=5)" | ||
| simulate.measurement = "prep_select_all_ones" | ||
| generator.name = "prep_select" | ||
| generator.params.N = 5 | ||
|
|
||
|
|
||
|
|
||
| [[benchmarks]] | ||
| id = "qcnn_N5" | ||
| description = "qcnn circuit (N=5)" | ||
| simulate.measurement = "qcnn" | ||
| generator.name = "qcnn" | ||
| generator.params.N = 5 | ||
|
|
||
|
|
||
|
|
||
| [[benchmarks]] | ||
| id = "qv_N5" | ||
| description = "qv circuit (N=5)" | ||
| simulate.measurement = "heavy_output" | ||
| generator.name = "qv" | ||
| generator.params.N = 5 | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # Version of the specification language/format, to manage future changes | ||
| spec_version = "1.0" | ||
| # Version of this particular benchmark suite, to manage any important changes | ||
| suite_version = "0.1" | ||
| # Unique identifier for this benchmark suite | ||
| id = "fake_backend_7Q_benchmarks" | ||
| # Human readable description | ||
| description = "Compiler benchmarks running in small quantum device emulators." | ||
|
|
||
| # --------- Compilers ---------- | ||
| # For now, restricting to qiskit derived compilers to work with the same target-device options | ||
| [[compilers]] | ||
| id = "ucc" | ||
| [[compilers]] | ||
| id = "qiskit-default" | ||
|
|
||
| # --------- Target Devices ---------- | ||
| [[target_devices]] | ||
| # 7 qubits | ||
| id = "ibm_fake_jakarta" | ||
|
|
||
|
|
||
| # --------- Benchmarks ---------- | ||
| # Change all these to N=7 versions of the existing 5Q benchmarks | ||
|
|
||
| # --------- Benchmarks ---------- | ||
| [[benchmarks]] | ||
| id = "mqt:qaoa_N7" | ||
| description = "mqt:qaoa circuit (N=7)" | ||
| simulate.measurement = "qaoa" | ||
| generator.name = "mqt:qaoa" | ||
| generator.params.N = 7 | ||
|
|
||
| [[benchmarks]] | ||
| id = "prep_select_N7" | ||
| description = "prep_select circuit (N=7)" | ||
| simulate.measurement = "prep_select_all_ones" | ||
| generator.name = "prep_select" | ||
| generator.params.N = 7 | ||
|
|
||
| [[benchmarks]] | ||
| id = "qcnn_N7" | ||
| description = "qcnn circuit (N=7)" | ||
| simulate.measurement = "qcnn" | ||
| generator.name = "qcnn" | ||
| generator.params.N = 7 | ||
|
|
||
| [[benchmarks]] | ||
| id = "qft_N7" | ||
| description = "qft circuit (N=7)" | ||
| simulate.measurement = "uniform_superposition_projector" | ||
| generator.name = "qft" | ||
| generator.params.N = 7 | ||
|
|
||
| [[benchmarks]] | ||
| id = "qv_N7" | ||
| description = "qv circuit (N=7)" | ||
| simulate.measurement = "heavy_output" | ||
| generator.name = "qv" | ||
| generator.params.N = 7 | ||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -139,9 +139,16 @@ def main(): | |
|
|
||
| # --- Plot Compilation Benchmarks --- | ||
| if args.plot in ["all", "compilation"]: | ||
| db = SuiteResultsDatabase.from_root( | ||
| args.root_dir, args.runner_name, "compilation_benchmarks" | ||
| ) | ||
| # If runner_name matches a known suite, use it directly | ||
| suite_dir = args.root_dir / args.runner_name | ||
| if (suite_dir / "compilation_benchmarks").exists(): | ||
| db = SuiteResultsDatabase.from_root( | ||
| args.root_dir, args.runner_name, "compilation_benchmarks" | ||
| ) | ||
| else: | ||
| db = SuiteResultsDatabase.from_root( | ||
| args.root_dir, args.runner_name, args.runner_name | ||
| ) | ||
|
|
||
| suite_results = db.from_uid(args.uid) if args.uid else db.get_latest() | ||
| if suite_results is None: | ||
|
|
@@ -164,18 +171,35 @@ def main(): | |
|
|
||
| # --- Plot Simulation Benchmarks --- | ||
| if args.plot in ["all", "simulation"]: | ||
| suite_dir = args.root_dir / args.runner_name | ||
| # Use the suite name as the keyword for database and CSV lookup | ||
| db = SuiteResultsDatabase.from_root( | ||
| args.root_dir, args.runner_name, "simulation_benchmarks" | ||
| args.root_dir, args.runner_name, args.runner_name | ||
| ) | ||
|
|
||
| suite_results = db.from_uid(args.uid) if args.uid else db.get_latest() | ||
| if suite_results is None: | ||
| print(f"No simulation data found for UID {args.uid}") | ||
| sys.exit(1) | ||
|
|
||
| latest_date = suite_results.metadata.uid_timestamp.strftime("%Y-%m-%d") | ||
|
|
||
| df = to_df_simulation(suite_results) | ||
| if suite_results is not None: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Messy, need to remove, this was part of debugging. |
||
| latest_date = suite_results.metadata.uid_timestamp.strftime("%Y-%m-%d") | ||
| df = to_df_simulation(suite_results) | ||
| else: | ||
| # Fallback: load latest simulation.csv directly | ||
| import glob | ||
| import os | ||
|
|
||
| csv_dir = str(suite_dir) | ||
| csv_files = glob.glob( | ||
| os.path.join(csv_dir, "**", "*.simulation.csv"), recursive=True | ||
| ) | ||
| if not csv_files: | ||
| print(f"No simulation CSV files found in {csv_dir}") | ||
| sys.exit(1) | ||
| latest_csv = max(csv_files, key=os.path.getmtime) | ||
| print(f"Loading simulation data from {latest_csv}") | ||
| df = pd.read_csv(latest_csv) | ||
| # Try to extract date from filename or column | ||
| if "uid_timestamp" in df.columns: | ||
| latest_date = str(df["uid_timestamp"].iloc[0]).split()[0] | ||
| else: | ||
| latest_date = "unknown" | ||
|
|
||
| file_ext = "pdf" if args.pdf else "png" | ||
| out_path = ( | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,60 @@ | ||
| from typing import Union | ||
| from math import sqrt | ||
| from qiskit import QuantumCircuit, ClassicalRegister | ||
| from qiskit.quantum_info import Operator, Statevector, SparsePauliOp | ||
| from qiskit_aer import AerSimulator | ||
| import numpy as np | ||
| from qiskit.result import Counts | ||
| from ..registry import register | ||
| from ..results import SimulationMetrics | ||
|
|
||
|
|
||
| def calc_computational_basis_expectation( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these are no longer used; they were part of debugging. |
||
| uncompiled_circuit: QuantumCircuit, | ||
| compiled_circuit: QuantumCircuit, | ||
| simulator: AerSimulator, | ||
| ) -> SimulationMetrics: | ||
| # Ensure classical bits and measurement | ||
| def ensure_classical_bits_and_measurement(circuit): | ||
| num_qubits = circuit.num_qubits | ||
| if circuit.num_clbits < num_qubits: | ||
| cr = ClassicalRegister(num_qubits - circuit.num_clbits) | ||
| circuit.add_register(cr) | ||
| if not any(instr[0].name == "measure" for instr in circuit.data): | ||
| circuit.measure(range(num_qubits), range(num_qubits)) | ||
|
|
||
| ensure_classical_bits_and_measurement(uncompiled_circuit) | ||
| ensure_classical_bits_and_measurement(compiled_circuit) | ||
|
|
||
| shots = 1024 | ||
| ideal_result = simulator.run(uncompiled_circuit, shots=shots).result() | ||
| ideal_counts = ideal_result.get_counts() | ||
| noisy_result = simulator.run(compiled_circuit, shots=shots).result() | ||
| noisy_counts = noisy_result.get_counts() | ||
|
|
||
| def z_expectation(counts: Counts, num_qubits: int): | ||
| total = 0 | ||
| shots = sum(counts.values()) | ||
| for bitstring, count in counts.items(): | ||
| val = 1 if bitstring.count("1") % 2 == 0 else -1 | ||
| total += val * count | ||
| return total / shots if shots > 0 else 0.0 | ||
|
|
||
| uncompiled_ideal = z_expectation(ideal_counts, uncompiled_circuit.num_qubits) | ||
| compiled_noisy = z_expectation(noisy_counts, compiled_circuit.num_qubits) | ||
|
|
||
| import math | ||
|
|
||
| return SimulationMetrics( | ||
| uncompiled_ideal=uncompiled_ideal, | ||
| compiled_ideal=math.nan, | ||
| uncompiled_noisy=math.nan, | ||
| compiled_noisy=compiled_noisy, | ||
| ) | ||
|
|
||
|
|
||
| register.output_metric("computational_basis")(calc_computational_basis_expectation) | ||
|
|
||
| """ | ||
| This module provides functionality calculating expectation values of compiled circuits | ||
| with and without noise. It has some common functions for calculating these values given an | ||
|
|
@@ -8,15 +65,6 @@ | |
| in the circuit as an argument and return a Qiskit Operator representing the observable to measure. | ||
| """ | ||
|
|
||
| from typing import Union | ||
| from math import sqrt | ||
| from qiskit import QuantumCircuit | ||
| from qiskit.quantum_info import Operator, Statevector, SparsePauliOp | ||
| from qiskit_aer import AerSimulator | ||
| import numpy as np | ||
| from ..registry import register | ||
| from ..results import SimulationMetrics | ||
|
|
||
| # ---------------------------------------------------- | ||
| # Simulation functions to calculate expectation values | ||
| # ---------------------------------------------------- | ||
|
|
@@ -151,14 +199,25 @@ def generate_square_heisenberg_observable(num_qubits): | |
|
|
||
| @register.observable("qaoa") | ||
| def generate_qaoa_observable(num_qubits): | ||
| """Generates the problem Hamiltonian as the observable for the QAOA | ||
| benchmarking circuits, based on the binary encoding described in | ||
| Franz G. Fuchs, Herman Øie Kolden, Niels Henrik Aase, and Giorgio | ||
| Sartor "Efficient encoding of the weighted MAX k-CUT on a quantum computer | ||
| using QAOA". (2020) arXiv 2009.01095 (https://arxiv.org/abs/2009.01095). | ||
| The weights of the edges between vertices and of the resulting unitary | ||
| evolution come from the 10-vertex Barabasi-Albert graph in Fig 4(c) | ||
| of the paper. | ||
| """Generate the problem Hamiltonian observable for QAOA benchmarks. | ||
|
|
||
| Notes | ||
| ----- | ||
| The original reference graph (Fuchs et al. 2020, arXiv:2009.01095) uses a | ||
| 10-vertex Barabasi-Albert instance. The hard-coded edge list below encodes | ||
| that graph. Some benchmark suites in this project request QAOA circuits | ||
| with fewer than 10 qubits (e.g. N=5 or N=7). In those cases, the original | ||
| edge list contains vertex indices that exceed ``num_qubits - 1`` which | ||
| previously caused an ``IndexError: list assignment index out of range``. | ||
|
|
||
| To make the observable generation work for ``num_qubits < 10`` we filter | ||
| out edges that touch vertices outside the available range. | ||
|
|
||
| If fewer than 2 valid vertices remain after filtering (i.e. ``num_qubits < 2``) | ||
| we raise a ValueError because no meaningful 2-local ZZ Hamiltonian can be | ||
| constructed. | ||
| TODO: In general, we should define an observable for QAOA which allows an arbitrary number of qubits and generates the corresponding graph structure. | ||
|
|
||
| """ | ||
| pauli_strings = [] | ||
| # Weights of edges between vertices and of the resulting unitary evolution | ||
|
|
@@ -188,15 +247,20 @@ def generate_qaoa_observable(num_qubits): | |
| (7, 9, 4.265), | ||
| (8, 9, 1.690), | ||
| ] | ||
| for i, j, _ in weighted_edges: | ||
| # Start with identity string | ||
| if num_qubits < 2: | ||
| raise ValueError( | ||
| f"QAOA observable requires at least 2 qubits; got num_qubits={num_qubits}" | ||
| ) | ||
| # Filter edges to those within the available qubit index range | ||
| filtered_edges = [ | ||
| (i, j, w) for (i, j, w) in weighted_edges if i < num_qubits and j < num_qubits | ||
| ] | ||
| for i, j, weight in filtered_edges: | ||
| pauli_string = ["I"] * num_qubits | ||
| # Place Z operators on the chosen qubits | ||
| pauli_string[i] = "Z" | ||
| pauli_string[j] = "Z" | ||
| # Convert to PauliSumOp | ||
| pauli_strings.append("".join(pauli_string)) | ||
| coeffs = [weight for _, _, weight in weighted_edges] | ||
| coeffs = [weight for _, _, weight in filtered_edges] | ||
| observable = SparsePauliOp(pauli_strings, coeffs) | ||
| return observable | ||
|
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixing the bug I have encountered multiple times in not being able to properly load local data (e.g. #129 ).