diff --git a/.license-tools-config.json b/.license-tools-config.json index 6236193c3..4dee573ae 100644 --- a/.license-tools-config.json +++ b/.license-tools-config.json @@ -14,7 +14,8 @@ ".td": "SLASH_STYLE", ".yaml": "POUND_STYLE", ".toml": "POUND_STYLE", - ".yml": "POUND_STYLE" + ".yml": "POUND_STYLE", + ".nastyle": "SLASH_STYLE" }, "exclude": [ "^\\.[^/]+", diff --git a/eval/na/zoned/README.md b/eval/na/zoned/README.md new file mode 100644 index 000000000..9be6747c2 --- /dev/null +++ b/eval/na/zoned/README.md @@ -0,0 +1,24 @@ +# MQT QMAP's Zoned Neutral Atom Compiler Evaluation + +This document describes how to use the evaluation script for the zoned neutral atom compiler. +The script automates the process of running QMAP on a set of benchmark circuits and collecting performance metrics. + +## Prerequisites + +Before running the evaluation script, ensure you have the following installed: + +- uv 0.9.13+ + +## Usage + +The main evaluation script is `eval_ids_relaxed_routing.py`. You can run it from the repository root via: + +```bash + eval/na/zoned/eval_ids_relaxed_routing.py +``` + +## Output + +The script produces a CSV file named `results.csv` in the directory of the script. +Additionally, all input circuits are written to the `in` directory as QASM files. +Furthermore, the compiled files are written to the `out` directory ordered by compiler and settings. diff --git a/eval/na/zoned/eval_ids_relaxed_routing.py b/eval/na/zoned/eval_ids_relaxed_routing.py new file mode 100755 index 000000000..a4fa8d195 --- /dev/null +++ b/eval/na/zoned/eval_ids_relaxed_routing.py @@ -0,0 +1,700 @@ +#!/usr/bin/env -S uv run --script --quiet +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License +# +# /// script +# dependencies = [ +# "mqt.bench==2.1.0", +# "mqt.qmap @ git+https://github.com/munich-quantum-toolkit/qmap@28bf309dc166d87eed234bfa5dc2192e8900f865", +# ] +# [tool.uv] +# exclude-newer = "2025-12-16T00:00:00Z" +# /// + +"""Script for evaluating the routing-aware zoned neutral atom compiler. + +In particular, it compares the iterative diving search method against +the A* search method. Additionally, it evaluates the impact of relaxed +routing. +""" + +from __future__ import annotations + +import json +import os +import pathlib +import queue +import re +from itertools import chain +from math import sqrt +from multiprocessing import get_context +from typing import TYPE_CHECKING + +from mqt.bench import BenchmarkLevel, get_benchmark +from mqt.core import load +from qiskit import QuantumCircuit, transpile + +from mqt.qmap.na.zoned import PlacementMethod, RoutingAwareCompiler, RoutingMethod, ZonedNeutralAtomArchitecture + +if TYPE_CHECKING: + from collections.abc import Callable, Iterable, Iterator, Mapping + from multiprocessing import Queue + from typing import Any, ParamSpec, TypeVar + + from mqt.core.ir import QuantumComputation + + P = ParamSpec("P") + R = TypeVar("R") + + +"""Timeout for running the benchmark in seconds.""" +TIMEOUT = 15 * 60 # sec + + +def _proc_target(q: Queue, func: Callable[P, R], args: P.args, kwargs: P.kwargs) -> None: + """Target function for the process to run the given function and put the result in the queue. + + Args: + q: The queue to put the result in. + func: The function to run. + args: The positional arguments to pass to the function. + kwargs: The keyword arguments to pass to the function. + """ + try: + q.put(("ok", func(*args, **kwargs))) + except Exception as e: + q.put(("err", e)) + + +def run_with_process_timeout(func: Callable[P, R], timeout: float, *args: P.args, **kwargs: P.kwargs) -> R: + """Run a function in a separate process and timeout after the given timeout. + + Args: + func: The function to run. + timeout: The timeout in seconds. + *args: The positional arguments to pass to the function. + **kwargs: The keyword arguments to pass to the function. + + Returns: + The result of the function. + + Raises: + TimeoutError: If the function times out. + Exception: If the function raises an exception. + """ + # "fork" context avoids pickling bound methods but is Unix/macOS only. + ctx = get_context("fork") # use fork so bound methods don't need to be pickled on macOS/Unix + q = ctx.Queue() + p = ctx.Process(target=_proc_target, args=(q, func, args, kwargs)) + p.start() + try: + status, payload = q.get(block=True, timeout=timeout) + except queue.Empty as e: + msg = f"Timed out after {timeout}s" + raise TimeoutError(msg) from e + finally: + if p.is_alive(): + p.terminate() + p.join(2) + if p.is_alive(): + p.kill() + p.join() + if status == "ok": + return payload + if ( + status == "err" + and isinstance(payload, Exception) + and payload.args + and isinstance(payload.args[0], str) + and "Maximum number of nodes reached" in payload.args[0] + ): + msg = "Out of memory" + raise MemoryError(msg) + raise payload + + +def transpile_benchmark(benchmark: str, circuit: QuantumCircuit) -> QuantumCircuit: + """Transpile the given benchmark circuit to the native gate set. + + Args: + benchmark: Name of the benchmark. + circuit: The benchmark circuit to transpile. + + Returns: + The transpiled benchmark circuit. + """ + print(f"\033[32m[INFO]\033[0m Transpiling {benchmark}...") + flattened = QuantumCircuit(circuit.num_qubits, circuit.num_clbits) + flattened.compose(circuit, inplace=True) + transpiled = transpile( + flattened, basis_gates=["cz", "id", "u2", "u1", "u3"], optimization_level=3, seed_transpiler=0 + ) + stripped = QuantumCircuit(*transpiled.qregs, *transpiled.cregs) + for instr in transpiled.data: + if instr.operation.name not in {"measure", "barrier"}: + stripped.append(instr) + print("\033[32m[INFO]\033[0m Done") + return stripped + + +def benchmarks( + benchmark_dict: Iterable[tuple[str, tuple[BenchmarkLevel, Iterable[int]]]], +) -> Iterator[tuple[str, QuantumComputation]]: + """Yields the benchmark names and their circuits.""" + for benchmark, settings in benchmark_dict: + mode, limits = settings + for qubits in limits: + circuit = get_benchmark(benchmark, mode, qubits) + transpiled = transpile_benchmark(benchmark, circuit) + qc = load(transpiled) + yield benchmark, qc + + +def _compile_wrapper(compiler: RoutingAwareCompiler, qc: QuantumComputation) -> tuple[str, Mapping[str, Any]]: + """Compile and return the compiled code and stats. + + Args: + compiler: The compiler to use. + qc: The circuit to compile. + + Returns: + The compiled code and stats. + """ + return compiler.compile(qc), compiler.stats() + + +def process_benchmark( + compiler: RoutingAwareCompiler, + setting_name: str, + qc: QuantumComputation, + benchmark_name: str, + evaluator: Evaluator, +) -> bool: + """Compile and evaluate the given benchmark circuit. + + Args: + compiler: The compiler to use. + setting_name: Name of the compiler setting. + qc: The benchmark circuit to compile. + benchmark_name: Name of the benchmark. + evaluator: The evaluator to use. + + Returns: + True if compilation succeeded, False otherwise. + """ + compiler_name = type(compiler).__name__ + print(f"\033[32m[INFO]\033[0m Compiling {benchmark_name} with {qc.num_qubits} qubits with {compiler_name}...") + try: + code, stats = run_with_process_timeout(_compile_wrapper, TIMEOUT, compiler, qc) + except TimeoutError as e: + print(f"\033[31m[ERROR]\033[0m Failed ({e})") + evaluator.print_timeout(benchmark_name, qc, setting_name) + return False + except MemoryError as e: + print(f"\033[31m[ERROR]\033[0m Failed ({e})") + evaluator.print_memout(benchmark_name, qc, setting_name) + return False + except RuntimeError as e: + print(f"\033[31m[ERROR]\033[0m Failed ({e})") + evaluator.print_error(benchmark_name, qc, setting_name) + return False + + code = "\n".join(line for line in code.splitlines() if not line.startswith("@+ u")) + pathlib.Path(f"out/{compiler_name}/{setting_name}").mkdir(exist_ok=True, parents=True) + with pathlib.Path(f"out/{compiler_name}/{setting_name}/{benchmark_name}_{qc.num_qubits}.naviz").open( + "w", encoding="utf-8" + ) as f: + f.write(code) + print("\033[32m[INFO]\033[0m Done") + + print(f"\033[32m[INFO]\033[0m Evaluating {benchmark_name} with {qc.num_qubits} qubits...") + evaluator.reset() + evaluator.evaluate(benchmark_name, qc, setting_name, code, stats) + evaluator.print_data() + print("\033[32m[INFO]\033[0m Done") + return True + + +class Evaluator: + """Class for evaluating compiled circuits. + + Attributes: + arch: The architecture dictionary. + filename: The output CSV filename. + circuit_name: Name of the circuit. + setting: Compiler setting name. + num_qubits: Number of qubits. + two_qubit_gates: Number of two-qubit gates. + scheduling_time: Time taken for scheduling. + reuse_analysis_time: Time taken for reuse analysis. + placement_time: Time taken for placement. + routing_time: Time taken for routing. + code_generation_time: Time taken for code generation. + total_time: Total compilation time. + rearrangement_duration: Duration of rearrangement operations. + two_qubit_gate_layer: Number of two-qubit gate layers. + max_two_qubit_gates: Maximum number of two-qubit gates in a layer. + atom_locations: Dictionary of atom locations. + """ + + def __init__(self, arch: Mapping[str, Any], filename: str) -> None: + """Initialize the Evaluator. + + Args: + arch: The architecture dictionary. + filename: The output CSV filename. + """ + self.arch = arch + self.filename = filename + + self.reset() + + def reset(self) -> None: + """Reset the Evaluator.""" + self.circuit_name = "" + self.num_qubits = 0 + self.setting = "" + self.two_qubit_gates = 0 + + self.scheduling_time = 0 + self.reuse_analysis_time = 0 + self.placement_time = 0 + self.routing_time = 0 + self.code_generation_time = 0 + self.total_time = 0 + + self.rearrangement_duration = 0.0 + self.two_qubit_gate_layer = 0 + self.max_two_qubit_gates = 0 + + self.atom_locations = {} + + def _process_load(self, line: str, it: Iterator[str]) -> None: + """Process a load operation. + + Args: + line: The current line being processed. + it: An iterator over the remaining lines. + """ + # Extract atoms from the load operation + atoms = [] + match = re.match(r"@\+ load \[", line) + if match: + # Multi-line load + for next_line in it: + next_line_stripped = next_line.strip() + if next_line_stripped == "]": + break + if next_line_stripped not in self.atom_locations: + msg = f"Atom {next_line_stripped} not found in atom locations" + raise ValueError(msg) + atoms.append(next_line_stripped) + else: + # Single atom load + match = re.match(r"@\+ load (\w+)", line) + if match: + atom = match.group(1) + if atom not in self.atom_locations: + msg = f"Atom {atom} not found in atom locations" + raise ValueError(msg) + atoms.append(atom) + self._apply_load(atoms) + + def _process_move(self, line: str, it: Iterator[str]) -> None: + """Process a move operation. + + Args: + line: The current line being processed. + it: An iterator over the remaining lines. + """ + # Extract atoms and coordinates from the move operation + moves = [] + match = re.match(r"@\+ move \[", line) + if match: + # Multi-line move + for next_line in it: + next_line_stripped = next_line.strip() + if next_line_stripped == "]": + break + move_match = re.match(r"\((-?\d+\.\d+), (-?\d+\.\d+)\) (\w+)", next_line_stripped) + if move_match: + x, y, atom = move_match.groups() + assert atom in self.atom_locations, f"Atom {atom} not found in atom locations" + moves.append((atom, (int(float(x)), int(float(y))))) + else: + # Single atom move + match = re.match(r"@\+ move \((-?\d+\.\d+), (-?\d+\.\d+)\) (\w+)", line) + if match: + x, y, atom = match.groups() + assert atom in self.atom_locations, f"Atom {atom} not found in atom locations" + moves.append((atom, (int(float(x)), int(float(y))))) + self._apply_move(moves) + + def _process_store(self, line: str, it: Iterator[str]) -> None: + """Process a store operation. + + Args: + line: The current line being processed. + it: An iterator over the remaining lines. + """ + # Extract atoms from the store operation + match = re.match(r"@\+ store \[", line) + atoms = [] + if match: + # Multi-line store + for next_line in it: + next_line_stripped = next_line.strip() + if next_line_stripped == "]": + break + assert next_line_stripped in self.atom_locations, ( + f"Atom {next_line_stripped} not found in atom locations" + ) + atoms.append(next_line_stripped) + else: + # Single atom store + match = re.match(r"@\+ store (\w+)", line) + if match: + assert match.group(1) in self.atom_locations, f"Atom {match.group(1)} not found in atom locations" + atoms.append(match.group(1)) + self._apply_store(atoms) + + def _process_cz(self) -> None: + """Process a cz operation.""" + atoms = [] + y_min = self.arch["entanglement_zones"][0]["slms"][0]["location"][1] + for atom, coord in self.atom_locations.items(): + if coord[1] >= y_min: # atom is in the entanglement zone + atoms.append(atom) + assert len(atoms) % 2 == 0, f"Expected even number of atoms in entanglement zone, got {len(atoms)}" + self._apply_cz(atoms) + + def _process_u(self, line: str, it: Iterator[str]) -> None: + """Process a u operation. + + Args: + line: The current line being processed. + it: An iterator over the remaining lines. + """ + # Extract atoms from u operation + atoms = [] + match = re.match(r"@\+ u( \d\.\d+){3} \[", line) + if match: + # Multi-line u + for next_line in it: + next_line_stripped = next_line.strip() + if next_line_stripped == "]": + break + assert next_line_stripped in self.atom_locations, ( + f"Atom {next_line_stripped} not found in atom locations" + ) + atoms.append(next_line_stripped) + else: + # Single atom u + match = re.match(r"@\+ u( \d\.\d+){3} (\w+)", line) + if match: + if match.group(2) not in self.atom_locations: + self._apply_global_u() + return + atoms.append(match.group(2)) + self._apply_u(atoms) + + def _process_rz(self, line: str, it: Iterator[str]) -> None: + """Process a rz operation. + + Args: + line: The current line being processed. + it: An iterator over the remaining lines. + """ + # Extract atoms from u operation + atoms = [] + match = re.match(r"@\+ rz \d\.\d+ \[", line) + if match: + # Multi-line u + for next_line in it: + next_line_stripped = next_line.strip() + if next_line_stripped == "]": + break + assert next_line_stripped in self.atom_locations, ( + f"Atom {next_line_stripped} not found in atom locations" + ) + atoms.append(next_line_stripped) + else: + # Single atom u + match = re.match(r"@\+ rz \d\.\d+ (\w+)", line) + if match: + assert match.group(1) in self.atom_locations, f"Atom {match.group(1)} not found in atom locations" + atoms.append(match.group(1)) + self._apply_rz(atoms) + + def _apply_load(self, _: list[str]) -> None: + """Apply a load operation. + + Args: + _: List of atoms to load. + """ + self.rearrangement_duration += self.arch["operation_duration"]["atom_transfer"] + + def _apply_move(self, moves: list[tuple[str, tuple[int, int]]]) -> None: + """Apply a move operation. + + Args: + moves: List of tuples containing atom names and their target coordinates. + """ + max_distance = 0.0 + for atom, coord in moves: + if atom in self.atom_locations: + distance = sqrt( + (coord[0] - self.atom_locations[atom][0]) ** 2 + (coord[1] - self.atom_locations[atom][1]) ** 2 + ) + max_distance = max(max_distance, distance) + + # Movement timing model parameters (units: um, us) + t_d_max = 200 # Time to traverse max distance (us) + d_max = 110 # Maximum distance for cubic profile (um) + jerk = 32 * d_max / t_d_max**3 # 0.00044, Jerk constant (um/us³) + v_max = d_max / t_d_max * 2 # = 1.1, Maximum velocity (um/us) + + if max_distance <= d_max: + rearrangement_time = 2 * (4 * max_distance / jerk) ** (1 / 3) + else: + rearrangement_time = t_d_max + (max_distance - d_max) / v_max + self.rearrangement_duration += rearrangement_time + # Update atom locations + for atom, coord in moves: + assert atom in self.atom_locations, f"Atom {atom} not found in atom locations" + self.atom_locations[atom] = coord + + def _apply_store(self, _: list[str]) -> None: + """Apply a store operation. + + Args: + _: List of atoms to store. + """ + self.rearrangement_duration += self.arch["operation_duration"]["atom_transfer"] + + def _apply_cz(self, atoms: list[str]) -> None: + """Apply a cz operation. + + Args: + atoms: List of atoms involved in the cz operation. + """ + self.two_qubit_gate_layer += 1 + self.max_two_qubit_gates = max(self.max_two_qubit_gates, len(atoms) // 2) + + def _apply_u(self, atoms: list[str]) -> None: + """Apply an u operation. + + Args: + atoms: List of atoms involved in the u operation. + """ + + def _apply_global_u(self) -> None: + """Apply a global u operation.""" + + def _apply_global_ry(self) -> None: + """Apply a global rydberg gate operation.""" + self._apply_global_u() + + def _apply_rz(self, atoms: list[str]) -> None: + """Apply a rz operation. + + Args: + atoms: List of atoms involved in the rz operation. + """ + self._apply_u(atoms) + + def evaluate(self, name: str, qc: QuantumComputation, setting: str, code: str, stats: Mapping[str, Any]) -> None: + """Evaluate a circuit. + + Args: + name: Name of the circuit. + qc: The quantum circuit. + setting: Compiler setting name. + code: The compiled code. + stats: Compilation statistics. + """ + self.circuit_name = name + self.num_qubits = qc.num_qubits + self.setting = setting + self.two_qubit_gates = sum(len(op.get_used_qubits()) == 2 for op in qc) + + self.scheduling_time = stats["schedulingTime"] + self.reuse_analysis_time = stats["reuseAnalysisTime"] + self.placement_time = stats["layoutSynthesizerStatistics"]["placementTime"] + self.routing_time = stats["layoutSynthesizerStatistics"]["routingTime"] + self.code_generation_time = stats["codeGenerationTime"] + self.total_time = stats["totalTime"] + + it = iter(code.splitlines()) + + for line in it: + match = re.match(r"atom\s+\((-?\d+\.\d+),\s*(-?\d+\.\d+)\)\s+(\w+)", line) + if match: + x, y, atom_name = match.groups() + self.atom_locations[atom_name] = (int(float(x)), int(float(y))) + else: + # put line back on top of iterator + it = chain([line], it) + break + + for line in it: + if line.startswith("@+ load"): + self._process_load(line, it) + elif line.startswith("@+ move"): + self._process_move(line, it) + elif line.startswith("@+ store"): + self._process_store(line, it) + elif line.startswith("@+ cz"): + self._process_cz() + elif line.startswith("@+ u"): + self._process_u(line, it) + elif line.startswith("@+ rz"): + self._process_rz(line, it) + else: + msg = f"Unrecognized operation: {line}" + raise ValueError(msg) + + def print_header(self) -> None: + """Print the header of the CSV file.""" + with pathlib.Path(self.filename).open("w", encoding="utf-8") as csv: + csv.write( + "circuit_name,num_qubits,setting,status,two_qubit_gates,scheduling_time,reuse_analysis_time," + "placement_time,routing_time,code_generation_time,total_time,two_qubit_gate_layer,max_two_qubit_gates," + "rearrangement_duration\n" + ) + + def print_data(self) -> None: + """Print the data of the CSV file.""" + with pathlib.Path(self.filename).open("a", encoding="utf-8") as csv: + csv.write( + f"{self.circuit_name},{self.num_qubits},{self.setting},ok,{self.two_qubit_gates}," + f"{self.scheduling_time},{self.reuse_analysis_time},{self.placement_time}," + f"{self.routing_time},{self.code_generation_time},{self.total_time},{self.two_qubit_gate_layer}," + f"{self.max_two_qubit_gates},{self.rearrangement_duration}\n" + ) + + def print_timeout(self, circuit_name: str, qc: QuantumComputation, setting: str) -> None: + """Print the data of the CSV file. + + Args: + circuit_name: Name of the circuit. + qc: The quantum circuit. + setting: Compiler setting name. + """ + with pathlib.Path(self.filename).open("a", encoding="utf-8") as csv: + csv.write(f"{circuit_name},{qc.num_qubits},{setting},timeout,,,,,,,,,\n") + + def print_memout(self, circuit_name: str, qc: QuantumComputation, setting: str) -> None: + """Print the data of the CSV file. + + Args: + circuit_name: Name of the circuit. + qc: The quantum circuit. + setting: Compiler setting name. + """ + with pathlib.Path(self.filename).open("a", encoding="utf-8") as csv: + csv.write(f"{circuit_name},{qc.num_qubits},{setting},memout,,,,,,,,,\n") + + def print_error(self, circuit_name: str, qc: QuantumComputation, setting: str) -> None: + """Print the data of the CSV file. + + Args: + circuit_name: Name of the circuit. + qc: The quantum circuit. + setting: Compiler setting name. + """ + with pathlib.Path(self.filename).open("a", encoding="utf-8") as csv: + csv.write(f"{circuit_name},{qc.num_qubits},{setting},error,,,,,,,,,\n") + + +def main() -> None: + """Main function for evaluating the fast relaxed compiler.""" + # set working directory to script location + os.chdir(pathlib.Path(pathlib.Path(__file__).resolve()).parent) + print("\033[32m[INFO]\033[0m Reading in architecture...") + with pathlib.Path("square_architecture.json").open(encoding="utf-8") as f: + arch_dict = json.load(f) + arch = ZonedNeutralAtomArchitecture.from_json_file("square_architecture.json") + arch.to_namachine_file("arch.namachine") + print("\033[32m[INFO]\033[0m Done") + astar_compiler = RoutingAwareCompiler( + arch, + log_level="error", + max_filling_factor=0.9, + use_window=True, + window_min_width=16, + window_ratio=1.0, + window_share=0.8, + placement_method=PlacementMethod.astar, + deepening_factor=0.9, + deepening_value=8.0, + lookahead_factor=0.2, + reuse_level=5.0, + max_nodes=int(1e7), + routing_method=RoutingMethod.strict, + warn_unsupported_gates=False, + ) + common_ids_config = { + "log_level": "error", + "max_filling_factor": 0.9, + "use_window": True, + "window_min_width": 16, + "window_ratio": 1.0, + "window_share": 0.8, + "placement_method": PlacementMethod.ids, + "deepening_factor": 0.01, + "deepening_value": 0.0, + "lookahead_factor": 0.4, + "reuse_level": 5.0, + "trials": 4, + "queue_capacity": 100, + "warn_unsupported_gates": False, + } + ids_compiler = RoutingAwareCompiler(arch, **common_ids_config, routing_method=RoutingMethod.strict) + relaxed_compiler = RoutingAwareCompiler( + arch, **common_ids_config, routing_method=RoutingMethod.relaxed, prefer_split=1.0 + ) + + evaluator = Evaluator(arch_dict, "results.csv") + evaluator.print_header() + pathlib.Path("in").mkdir(exist_ok=True) + + benchmark_list = [ + ("graphstate", (BenchmarkLevel.INDEP, [60, 80, 100, 120, 140, 160, 180, 200, 500, 1000, 2000, 5000])), + ("qft", (BenchmarkLevel.INDEP, [500, 1000])), + ("qpeexact", (BenchmarkLevel.INDEP, [500, 1000])), + ("wstate", (BenchmarkLevel.INDEP, [500, 1000])), + ("qaoa", (BenchmarkLevel.INDEP, [50, 100, 150, 200])), + ("vqe_two_local", (BenchmarkLevel.INDEP, [50, 100, 150, 200])), + ] + + for benchmark, qc in benchmarks(benchmark_list): + qc.qasm3(f"in/{benchmark}_n{qc.num_qubits}.qasm") + process_benchmark(astar_compiler, "astar", qc, benchmark, evaluator) + process_benchmark(ids_compiler, "ids", qc, benchmark, evaluator) + process_benchmark(relaxed_compiler, "relaxed", qc, benchmark, evaluator) + + print( + "\033[32m[INFO]\033[0m =============================================================\n" + "\033[32m[INFO]\033[0m Now, \n" + "\033[32m[INFO]\033[0m - the results are located in `results.csv`,\n" + "\033[32m[INFO]\033[0m - the input circuits in the QASM format are located in\n" + "\033[32m[INFO]\033[0m the `in` directory,\n" + "\033[32m[INFO]\033[0m - the compiled circuits in the naviz format are located\n" + "\033[32m[INFO]\033[0m in the `out` directory separated for each compiler and\n" + "\033[32m[INFO]\033[0m setting, and\n" + "\033[32m[INFO]\033[0m - the architecture specification compatible with NAViz is\n" + "\033[32m[INFO]\033[0m located in `arch.namachine`\n" + "\033[32m[INFO]\033[0m \n" + "\033[32m[INFO]\033[0m The generated `.naviz` files can be animated with the\n" + "\033[32m[INFO]\033[0m MQT NAViz tool." + ) + + +if __name__ == "__main__": + main() diff --git a/eval/na/zoned/mqt.nastyle b/eval/na/zoned/mqt.nastyle new file mode 100644 index 000000000..c8ec5a79d --- /dev/null +++ b/eval/na/zoned/mqt.nastyle @@ -0,0 +1,193 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +name: "MQT debug" + +atom { + trapped { + color: #0065BD + } + shuttling { + color: #A2AD00 + } + legend { + name { + ^atom[_-]?(.*)$: "" + ^.*$: "" + } + font { + family: "CMU Sans Serif" + size: 2 + color: #000000 + } + } + radius: 1 + margin: 1 +} + +zone { + config ^(zone[_-]?)?cz.*$ { + color: #e37222 + line { + thickness: 0.5 + dash { + length: 0 + duty: 100% + } + } + name: "CZ" + } + config ^_(zone[_-]?)?(.*)$ { + color: #98c6ea + line { + thickness: 0.5 + dash { + length: 0 + duty: 100% + } + } + name: "" + } + config ^(zone[_-]?)?(.*)$ { + color: #98c6ea + line { + thickness: 0.5 + dash { + length: 0 + duty: 100% + } + } + name: "$2" + } + legend { + display: true + title: "Zones" + } +} + +operation { + config { + ry { + color: #98C6EA + name: "RY" + radius: 150% + } + rz { + color: #A2AD00 + name: "RZ" + radius: 150% + } + cz { + color: #e37222 + name: "CZ" + radius: 150% + } + } + legend { + display: true + title: "Gates" + } +} + +machine { + trap { + color: #7F98AB + radius: 1 + line_width: 0.2 + name: "SLM" + } + shuttle { + color: #003359 + line { + thickness: 0.2 + dash { + length: 5 + duty: 50% + } + } + name: "AOD" + } + legend { + display: true + title: "Atom States" + } +} + +coordinate { + tick { + x: 10 + y: 10 + color: #999999 + line { + thickness: 0.1 + dash { + length: 0 + duty: 100% + } + } + display: true + } + number { + x { + distance: 20 + position: bottom + } + y { + distance: 20 + position: left + } + display: true + font { + family: "CMU Sans Serif" + size: 2 + color: #000000 + } + } + axis { + x: "x" + y: "y" + display: true + font { + family: "CMU Sans Serif" + size: 2 + color: #000000 + } + } + margin: 5 +} + +sidebar { + font { + family: "CMU Sans Serif" + size: 32 + color: #000000 + } + margin: 8 + padding { + color: 16 + heading: 52 + entry: 45 + } + color_radius: 16 +} + +time { + display: true + prefix: "Time: " + precision: 1 + font { + family: "CMU Sans Serif" + size: 12 + color: #000000 + } +} + +viewport { + margin: 4 + color: #ffffff +} diff --git a/eval/na/zoned/square_architecture.json b/eval/na/zoned/square_architecture.json new file mode 100644 index 000000000..20e376415 --- /dev/null +++ b/eval/na/zoned/square_architecture.json @@ -0,0 +1,73 @@ +{ + "name": "full_compute_store_architecture", + "operation_duration": { + "rydberg_gate": 0.36, + "single_qubit_gate": 52, + "atom_transfer": 15 + }, + "operation_fidelity": { + "rydberg_gate": 0.995, + "single_qubit_gate": 0.9997, + "atom_transfer": 0.999 + }, + "qubit_spec": { + "T": 1.5e6 + }, + "storage_zones": [ + { + "zone_id": 0, + "slms": [ + { + "id": 0, + "site_separation": [4, 4], + "r": 73, + "c": 101, + "location": [0, 0] + } + ], + "offset": [0, 0], + "dimension": [400, 288] + } + ], + "entanglement_zones": [ + { + "zone_id": 0, + "slms": [ + { + "id": 1, + "site_separation": [12, 10], + "r": 10, + "c": 34, + "location": [1, 309] + }, + { + "id": 2, + "site_separation": [12, 10], + "r": 10, + "c": 34, + "location": [3, 309] + } + ], + "offset": [1, 309], + "dimension": [398, 90] + } + ], + "aods": [ + { + "id": 0, + "site_separation": 2, + "r": 100, + "c": 100 + } + ], + "arch_range": [ + [0, 0], + [400, 400] + ], + "rydberg_range": [ + [ + [0, 308], + [400, 400] + ] + ] +}