From 67e0402b9753d0d7d11896376da4029367326451 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:14:51 +0100 Subject: [PATCH 01/58] =?UTF-8?q?=E2=9C=A8=20Implement=20SC=20device?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 270 ++++++++++++++ include/mqt-core/qdmi/sc/Generator.hpp | 106 ++++++ json/sc/device.json | 186 ++++++++++ src/qdmi/CMakeLists.txt | 1 + src/qdmi/sc/App.cpp | 448 +++++++++++++++++++++++ src/qdmi/sc/CMakeLists.txt | 206 +++++++++++ src/qdmi/sc/Device.cpp | 470 +++++++++++++++++++++++++ src/qdmi/sc/DynDevice.cpp | 155 ++++++++ src/qdmi/sc/Generator.cpp | 170 +++++++++ 9 files changed, 2012 insertions(+) create mode 100644 include/mqt-core/qdmi/sc/Device.hpp create mode 100644 include/mqt-core/qdmi/sc/Generator.hpp create mode 100644 json/sc/device.json create mode 100644 src/qdmi/sc/App.cpp create mode 100644 src/qdmi/sc/CMakeLists.txt create mode 100644 src/qdmi/sc/Device.cpp create mode 100644 src/qdmi/sc/DynDevice.cpp create mode 100644 src/qdmi/sc/Generator.cpp diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp new file mode 100644 index 0000000000..b61c8f58dd --- /dev/null +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -0,0 +1,270 @@ +/* + * 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 + */ + +#pragma once + +/** @file + * @brief The MQT QDMI device implementation for superconducting devices. + */ + +#include "mqt_sc_qdmi/device.h" + +#include +#include +#include +#include + +namespace qdmi { +class Device final { + /// @brief Provides access to the device name. + std::string name_; + + /// @brief The number of qubits in the device. + size_t qubitsNum_ = 0; + + /// @brief The list of sites. + std::vector> sites_; + + /// @brief The list of couplings, i.e., qubit pairs + std::vector> couplingMap_; + + /// @brief The list of operations. + std::vector> operations_; + + /// @brief The list of device sessions. + std::unordered_map> + sessions_; + + /// @brief Private constructor to enforce the singleton pattern. + Device(); + +public: + // Default move constructor and move assignment operator. + Device(Device&&) = default; + Device& operator=(Device&&) = default; + // Delete copy constructor and assignment operator to enforce singleton. + Device(const Device&) = delete; + Device& operator=(const Device&) = delete; + + /// @returns the singleton instance of the Device class. + [[nodiscard]] static Device& get() { + static Device instance; + return instance; + } + + /// @brief Destructor for the Device class. + ~Device() = default; + + /** + * @brief Allocates a new device session. + * @see MQT_SC_QDMI_device_session_alloc + */ + auto sessionAlloc(MQT_SC_QDMI_Device_Session* session) -> int; + + /** + * @brief Frees a device session. + * @see MQT_SC_QDMI_device_session_free + */ + auto sessionFree(MQT_SC_QDMI_Device_Session session) -> void; + + /** + * @brief Query a device property. + * @see MQT_SC_QDMI_device_session_query_device_property + */ + auto queryProperty(QDMI_Device_Property prop, size_t size, void* value, + size_t* sizeRet) -> int; +}; +} // namespace qdmi + +/** + * @brief Implementation of the MQT_SC_QDMI_Device_Session structure. + */ +struct MQT_SC_QDMI_Device_Session_impl_d { +private: + /// The status of the session. + enum class Status : uint8_t { + ALLOCATED, ///< The session has been allocated but not initialized + INITIALIZED, ///< The session has been initialized and is ready for use + }; + /// @brief The current status of the session. + Status status_ = Status::ALLOCATED; + /// @brief The device jobs associated with this session. + std::unordered_map> + jobs_; + +public: + /** + * @brief Initializes the device session. + * @see MQT_SC_QDMI_device_session_init + */ + auto init() -> int; + + /** + * @brief Sets a parameter for the device session. + * @see MQT_SC_QDMI_device_session_set_parameter + */ + auto setParameter(QDMI_Device_Session_Parameter param, size_t size, + const void* value) const -> int; + + /** + * @brief Create a new device job. + * @see MQT_SC_QDMI_device_session_create_device_job + */ + auto createDeviceJob(MQT_SC_QDMI_Device_Job* job) -> int; + + /** + * @brief Frees the device job. + * @see MQT_SC_QDMI_device_job_free + */ + auto freeDeviceJob(MQT_SC_QDMI_Device_Job job) -> void; + + /** + * @brief Forwards a query of a device property to the device. + * @see MQT_SC_QDMI_device_session_query_device_property + */ + auto queryDeviceProperty(QDMI_Device_Property prop, size_t size, void* value, + size_t* sizeRet) const -> int; + + /** + * @brief Forwards a query of a site property to the site. + * @see MQT_SC_QDMI_device_session_query_site_property + */ + auto querySiteProperty(MQT_SC_QDMI_Site site, QDMI_Site_Property prop, + size_t size, void* value, size_t* sizeRet) const + -> int; + + /** + * @brief Forwards a query of an operation property to the operation. + * @see MQT_SC_QDMI_device_session_query_operation_property + */ + auto queryOperationProperty(MQT_SC_QDMI_Operation operation, size_t numSites, + const MQT_SC_QDMI_Site* sites, size_t numParams, + const double* params, + QDMI_Operation_Property prop, size_t size, + void* value, size_t* sizeRet) const -> int; +}; + +/** + * @brief Implementation of the MQT_SC_QDMI_Device_Job structure. + */ +struct MQT_SC_QDMI_Device_Job_impl_d { +private: + /// @brief The device session associated with the job. + MQT_SC_QDMI_Device_Session_impl_d* session_; + +public: + /// @brief Constructor for the MQT_SC_QDMI_Device_Job_impl_d. + explicit MQT_SC_QDMI_Device_Job_impl_d( + MQT_SC_QDMI_Device_Session_impl_d* session) + : session_(session) {} + /** + * @brief Frees the device job. + * @note This function just forwards to the session's @ref freeDeviceJob + * function. This function is needed because the interface only provides the + * job handle to the @ref QDMI_job_free function and the job's session handle + * is private. + * @see QDMI_job_free + */ + auto free() -> void; + + /** + * @brief Sets a parameter for the job. + * @see MQT_SC_QDMI_device_job_set_parameter + */ + static auto setParameter(QDMI_Device_Job_Parameter param, size_t size, + const void* value) -> int; + + /** + * @brief Queries a property of the job. + * @see MQT_SC_QDMI_device_job_query_property + */ + static auto queryProperty(QDMI_Device_Job_Property prop, size_t size, + void* value, size_t* sizeRet) -> int; + + /** + * @brief Submits the job to the device. + * @see MQT_SC_QDMI_device_job_submit + */ + static auto submit() -> int; + + /** + * @brief Cancels the job. + * @see MQT_SC_QDMI_device_job_cancel + */ + static auto cancel() -> int; + + /** + * @brief Checks the status of the job. + * @see MQT_SC_QDMI_device_job_check + */ + static auto check(QDMI_Job_Status* status) -> int; + + /** + * @brief Waits for the job to complete but at most for the specified timeout. + * @see MQT_SC_QDMI_device_job_wait + */ + static auto wait(size_t timeout) -> int; + + /** + * @brief Gets the results of the job. + * @see MQT_SC_QDMI_device_job_get_results + */ + static auto getResults(QDMI_Job_Result result, size_t size, void* data, + [[maybe_unused]] size_t* sizeRet) -> int; +}; + +/** + * @brief Implementation of the MQT_SC_QDMI_Device_Site structure. + */ +struct MQT_SC_QDMI_Site_impl_d { + friend MQT_SC_QDMI_Operation_impl_d; + +private: + uint64_t id_ = 0; ///< Unique identifier of the site + + /// @brief Constructor for regular sites. + MQT_SC_QDMI_Site_impl_d(uint64_t id) : id_(id) {} + +public: + /// @brief Factory function for regular sites. + [[nodiscard]] static auto makeUniqueSite(uint64_t id) + -> std::unique_ptr; + /** + * @brief Queries a property of the site. + * @see MQT_SC_QDMI_device_session_query_site_property + */ + auto queryProperty(QDMI_Site_Property prop, size_t size, void* value, + size_t* sizeRet) const -> int; +}; + +/** + * @brief Implementation of the MQT_SC_QDMI_Device_Operation structure. + */ +struct MQT_SC_QDMI_Operation_impl_d { +private: + /// @brief Constructor for an empty operation. + MQT_SC_QDMI_Operation_impl_d() = default; + +public: + /// @brief Factory function empty operations. + [[nodiscard]] static auto makeUnique() + -> std::unique_ptr; + + /** + * @brief Queries a property of the operation. + * @see MQT_SC_QDMI_device_session_query_operation_property + */ + auto queryProperty(size_t numSites, const MQT_SC_QDMI_Site* sites, + size_t numParams, const double* params, + QDMI_Operation_Property prop, size_t size, void* value, + size_t* sizeRet) const -> int; +}; diff --git a/include/mqt-core/qdmi/sc/Generator.hpp b/include/mqt-core/qdmi/sc/Generator.hpp new file mode 100644 index 0000000000..d62339037e --- /dev/null +++ b/include/mqt-core/qdmi/sc/Generator.hpp @@ -0,0 +1,106 @@ +/* + * 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 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +// NOLINTNEXTLINE(misc-include-cleaner) +#include +#include +#include +#include +#include +#include + +namespace qdmi::sc { +/** + * @brief Represents a superconducting device configuration. + * @details This struct defines the schema for the JSON representation of a + * superconducting device configuration. This struct, including all its + * sub-structs, implements functions to serialize and deserialize to and from + * JSON using the nlohmann::json library. + * @note All duration and length values are in multiples of the time unit and + * the length unit, respectively. + */ +struct Device { + /// @brief The name of the device. + std::string name; + /// @brief The number of qubits in the device. + uint64_t numQubits = 0; + /// @brief The list of couplings the device supports. + std::vector> couplings; + + // NOLINTNEXTLINE(misc-include-cleaner) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Device, name, numQubits, + couplings) +}; + +/** + * @brief Writes a JSON schema with default values for the device configuration + * to the specified output stream. + * @param os is the output stream to write the JSON schema to. + * @throws std::runtime_error if the JSON conversion fails. + */ +auto writeJSONSchema(std::ostream& os) -> void; + +/** + * @brief Writes a JSON schema with default values for the device configuration + * to the specified path. + * @param path The path to write the JSON schema to. + * @throws std::runtime_error if the JSON conversion fails or the file cannot be + * opened. + */ +auto writeJSONSchema(const std::string& path) -> void; + +/** + * @brief Parses the device configuration from an input stream. + * @param is is the input stream containing the JSON representation of the + * device configuration. + * @returns The parsed device configuration as a Protobuf message. + * @throws std::runtime_error if the JSON cannot be parsed. + */ +[[nodiscard]] auto readJSON(std::istream& is) -> Device; + +/** + * @brief Parses the device configuration from a JSON file. + * @param path is the path to the JSON file containing the device configuration. + * @returns The parsed device configuration as a Protobuf message. + * @throws std::runtime_error if the JSON file does not exist, or the JSON file + * cannot be parsed. + */ +[[nodiscard]] auto readJSON(const std::string& path) -> Device; + +/** + * @brief Writes a header file with the device configuration to the specified + * output stream. + * @param device is the protobuf representation of the device. + * @param os is the output stream to write the header file to. + * @throws std::runtime_error if the file cannot be opened or written to. + * @note This implementation only supports multi-qubit gates up to two + * qubits. + */ +auto writeHeader(const Device& device, std::ostream& os) -> void; + +/** + * @brief Writes a header file with the device configuration to the specified + * path. + * @param device is the protobuf representation of the device. + * @param path is the path to write the header file to. + * @throws std::runtime_error if the file cannot be opened or written to. + * @note This implementation only supports multi-qubit gates up to two + * qubits. + */ +auto writeHeader(const Device& device, const std::string& path) -> void; +} // namespace qdmi::sc diff --git a/json/sc/device.json b/json/sc/device.json new file mode 100644 index 0000000000..abc9e25c2e --- /dev/null +++ b/json/sc/device.json @@ -0,0 +1,186 @@ +{ + "name": "MQT SC Default QDMI Device", + "numQubits": 100, + "couplings": [ + [0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], + [8, 9], + [10, 11], + [11, 12], + [12, 13], + [13, 14], + [14, 15], + [15, 16], + [16, 17], + [17, 18], + [18, 19], + [20, 21], + [21, 22], + [22, 23], + [23, 24], + [24, 25], + [25, 26], + [26, 27], + [27, 28], + [28, 29], + [30, 31], + [31, 32], + [32, 33], + [33, 34], + [34, 35], + [35, 36], + [36, 37], + [37, 38], + [38, 39], + [40, 41], + [41, 42], + [42, 43], + [43, 44], + [44, 45], + [45, 46], + [46, 47], + [47, 48], + [48, 49], + [50, 51], + [51, 52], + [52, 53], + [53, 54], + [54, 55], + [55, 56], + [56, 57], + [57, 58], + [58, 59], + [60, 61], + [61, 62], + [62, 63], + [63, 64], + [64, 65], + [65, 66], + [66, 67], + [67, 68], + [68, 69], + [70, 71], + [71, 72], + [72, 73], + [73, 74], + [74, 75], + [75, 76], + [76, 77], + [77, 78], + [78, 79], + [80, 81], + [81, 82], + [82, 83], + [83, 84], + [84, 85], + [85, 86], + [86, 87], + [87, 88], + [88, 89], + [90, 91], + [91, 92], + [92, 93], + [93, 94], + [94, 95], + [95, 96], + [96, 97], + [97, 98], + [98, 99], + [0, 10], + [1, 11], + [2, 12], + [3, 13], + [4, 14], + [5, 15], + [6, 16], + [7, 17], + [8, 18], + [9, 19], + [10, 20], + [11, 21], + [12, 22], + [13, 23], + [14, 24], + [15, 25], + [16, 26], + [17, 27], + [18, 28], + [19, 29], + [20, 30], + [21, 31], + [22, 32], + [23, 33], + [24, 34], + [25, 35], + [26, 36], + [27, 37], + [28, 38], + [29, 39], + [30, 40], + [31, 41], + [32, 42], + [33, 43], + [34, 44], + [35, 45], + [36, 46], + [37, 47], + [38, 48], + [39, 49], + [40, 50], + [41, 51], + [42, 52], + [43, 53], + [44, 54], + [45, 55], + [46, 56], + [47, 57], + [48, 58], + [49, 59], + [50, 60], + [51, 61], + [52, 62], + [53, 63], + [54, 64], + [55, 65], + [56, 66], + [57, 67], + [58, 68], + [59, 69], + [60, 70], + [61, 71], + [62, 72], + [63, 73], + [64, 74], + [65, 75], + [66, 76], + [67, 77], + [68, 78], + [69, 79], + [70, 80], + [71, 81], + [72, 82], + [73, 83], + [74, 84], + [75, 85], + [76, 86], + [77, 87], + [78, 88], + [79, 89], + [80, 90], + [81, 91], + [82, 92], + [83, 93], + [84, 94], + [85, 95], + [86, 96], + [87, 97], + [88, 98], + [89, 99] + ] +} diff --git a/src/qdmi/CMakeLists.txt b/src/qdmi/CMakeLists.txt index cf77b3e7fd..9b66ed5b9a 100644 --- a/src/qdmi/CMakeLists.txt +++ b/src/qdmi/CMakeLists.txt @@ -7,6 +7,7 @@ # Licensed under the MIT License add_subdirectory(dd) +add_subdirectory(sc) set(TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-driver) diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp new file mode 100644 index 0000000000..ceca2c0142 --- /dev/null +++ b/src/qdmi/sc/App.cpp @@ -0,0 +1,448 @@ +/* + * 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 + */ + +#include "qdmi/sc/Generator.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +/** + * Prints the usage information for the command line tool. + * @param programName is the name of the program executable. + */ +auto printUsage(const std::string& programName) -> void { + std::cout + << "Generator for turning superconducting computer JSON specifications " + "into " + "header files to be used as part of a superconducting QDMI device " + "implementation.\n" + "\n" + "Usage: " + << programName + << " [OPTIONS] [ARGS]\n" + "\n" + "Commands:\n" + " schema Generate a default JSON schema.\n" + " validate Validate a JSON specification.\n" + " generate Generate a header file from a JSON specification.\n" + "\n" + "Options:\n" + " -h, --help Show this help message and exit.\n" + " -v, --version Show version information and exit.\n"; +} + +/** + * Prints the usage information for the schema sub-command. + * @param programName is the name of the program executable. + */ +auto printSchemaUsage(const std::string& programName) -> void { + std::cout << "Generates a JSON schema with default values.\n" + "\n" + "Usage: " + << programName + << " schema [options]\n" + "\n" + "Options:\n" + " -h, --help Show this help message and exit.\n" + " -o, --output Specify the output file. If no output " + " file is " + " specified, the schema is printed to " + " stdout.\n"; +} + +/** + * Prints the usage information for the validate sub-command. + * @param programName is the name of the program executable. + */ +auto printValidateUsage(const std::string& programName) -> void { + std::cout << "Validates a JSON specification against the schema.\n" + "\n" + "Usage: " + << programName + << " validate [options] []\n" + "\n" + "Arguments:\n" + " json_file the path to the JSON file to validate. If\n" + " not specified, the JSON is read from stdin.\n" + "\n" + "Options:\n" + " -h, --help Show this help message and exit.\n"; +} + +/** + * Prints the usage information for the generate sub-command. + * @param programName is the name of the program executable. + */ +auto printGenerateUsage(const std::string& programName) -> void { + std::cout << "Generates a header file from a JSON specification.\n" + "\n" + "Usage: " + << programName + << " generate [options] \n" + "\n" + "Arguments:\n" + " json_file the path to the JSON file to generate the\n" + " header file from. If not specified, the\n" + " JSON is read from stdin.\n" + "\n" + "Options:\n" + " -h, --help Show this help message and exit.\n" + " -o, --output Specify the output file for the\n" + " generated header file. If no output\n" + " file is specified, the header file is\n" + " printed to stdout.\n"; +} + +/** + * Prints the version information for the command line tool. + */ +auto printVersion() -> void { + // NOLINTNEXTLINE(misc-include-cleaner) + std::cout << "MQT QDMI SC Device Generator (MQT Version " MQT_CORE_VERSION + ")\n"; +} + +/// Enum to represent the different commands that can be executed. +enum class Command : uint8_t { + Schema, ///< Command to generate a JSON schema + Validate, ///< Command to validate a JSON specification + Generate ///< Command to generate a header file from a JSON specification +}; + +/// Struct to hold the parsed command line arguments. +struct Arguments { + std::string programName; ///< Name of the program executable + bool help = false; ///< Flag to indicate if help is requested + /// Flag to indicate if version information is requested + bool version = false; + std::optional command; ///< Command to execute +}; + +/// Struct to hold the parsed schema command line arguments. +struct SchemaArguments { + bool help = false; ///< Flag to indicate if help is requested + /// Optional output file for the schema + std::optional outputFile; +}; + +/// Struct to hold the parsed validate command line arguments. +struct ValidateArguments { + bool help = false; ///< Flag to indicate if help is requested + /// Optional JSON file to validate + std::optional jsonFile; +}; + +/// Struct to hold the parsed generate command line arguments. +struct GenerateArguments { + bool help = false; ///< Flag to indicate if help is requested + /// Optional output file for the generated header file + std::optional outputFile; + /// Optional JSON file to parse the device configuration + std::optional jsonFile; +}; + +/** + * Parses the command line arguments and returns an Arguments struct. + * @param args is the vector of command line arguments. + * @returns the parsed arguments as an Arguments struct and an index indicating + * the position of the first sub-command argument. + * @throws std::invalid_argument if the value after an option is missing. + */ +auto parseArguments(const std::vector& args) + -> std::pair { + Arguments arguments; + arguments.programName = + args.empty() ? "mqt-core-sc-device-gen" : args.front(); + size_t i = 1; + while (i < args.size()) { + if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") { + arguments.help = true; + } else if (arg == "-v" || arg == "--version") { + arguments.version = true; + } else if (arg == "schema") { + arguments.command = Command::Schema; + break; // No more arguments for schema command + } else if (arg == "validate") { + arguments.command = Command::Validate; + break; // No more arguments for validate command + } else if (arg == "generate") { + arguments.command = Command::Generate; + break; // No more arguments for generate command + } else { + throw std::invalid_argument("Unknown argument: " + arg); + } + ++i; + } + return {arguments, i + 1}; +} + +/** + * Parses the command line arguments for the schema command and returns a + * SchemaArguments struct. + * @param args is the vector of command line arguments. + * @param i is the index to the first sub-command argument within @p args + * @return Parsed schema arguments as a SchemaArguments struct. + */ +auto parseSchemaArguments(const std::vector& args, size_t i) + -> SchemaArguments { + SchemaArguments schemaArgs; + while (i < args.size()) { + if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") { + schemaArgs.help = true; + } else if (arg == "-o" || arg == "--output") { + if (++i >= args.size()) { + throw std::invalid_argument("Missing value for output option."); + } + schemaArgs.outputFile = args.at(i); + } else { + throw std::invalid_argument("Unknown argument: " + arg); + } + ++i; + } + return schemaArgs; +} + +/** + * Parses the command line arguments for the validate command and returns a + * ValidateArguments struct. + * @param args is the vector of command line arguments. + * @param i is the index to the first sub-command argument within @p args + * @return Parsed validate arguments as a ValidateArguments struct. + */ +auto parseValidateArguments(const std::vector& args, size_t i) + -> ValidateArguments { + ValidateArguments validateArgs; + while (i < args.size()) { + if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") { + validateArgs.help = true; + } else { + validateArgs.jsonFile = arg; + } + ++i; + } + return validateArgs; +} + +/** + * Parses the command line arguments for the generate command and returns a + * GenerateArguments struct. + * @param args is the vector of command line arguments. + * @param i is the index to the first sub-command argument within @p args + * @return Parsed generate arguments as a GenerateArguments struct. + */ +auto parseGenerateArguments(const std::vector& args, size_t i) + -> GenerateArguments { + GenerateArguments generateArgs; + while (i < args.size()) { + if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") { + generateArgs.help = true; + } else if (arg == "-o" || arg == "--output") { + if (++i >= args.size()) { + throw std::invalid_argument("Missing value for output option."); + } + generateArgs.outputFile = args.at(i); + } else { + generateArgs.jsonFile = arg; + } + ++i; + } + return generateArgs; +} + +/** + * Executes the schema command, generating a JSON schema and writing it to the + * specified output file or stdout. + * @param progName is the name of the program executable. + * @param argVec is the vector of command line arguments. + * @param i is the index to the first sub-command argument within @p argVec + * @return 0 on success, 1 on error. + */ +auto executeSchemaCommand(const std::string& progName, + const std::vector& argVec, + const size_t i) -> int { + SchemaArguments schemaArgs; + // parse the rest of the command line arguments for the schema command + try { + schemaArgs = parseSchemaArguments(argVec, i); + } catch (const std::exception& e) { + SPDLOG_ERROR("Error parsing schema arguments: {}", e.what()); + printSchemaUsage(progName); + return 1; + } + // if the help flag is set, print the schema usage information and exit + if (schemaArgs.help) { + printSchemaUsage(progName); + return 0; + } + // generate the JSON schema and write it to the output file or stdout + try { + if (schemaArgs.outputFile.has_value()) { + qdmi::sc::writeJSONSchema(schemaArgs.outputFile.value()); + } else { + qdmi::sc::writeJSONSchema(std::cout); + } + } catch (const std::exception& e) { + SPDLOG_ERROR("Error generating JSON schema: {}", e.what()); + return 1; + } + return 0; +} + +/** + * Executes the validate command, validating a JSON file or JSON string from + * stdin. + * @param progName is the name of the program executable. + * @param argVec is the vector of command line arguments. + * @param i is the index to the first sub-command argument within @p argVec + * @return 0 on success, 1 on error. + */ +auto executeValidateCommand(const std::string& progName, + const std::vector& argVec, + const size_t i) -> int { + // parse the rest of the command line arguments for the validate command + const ValidateArguments validateArgs = parseValidateArguments(argVec, i); + // + if (validateArgs.help) { + printValidateUsage(progName); + return 0; + } + // validate the JSON file or the JSON string from stdin + try { + if (validateArgs.jsonFile.has_value()) { + std::ignore = qdmi::sc::readJSON(validateArgs.jsonFile.value()); + } else { + std::ignore = qdmi::sc::readJSON(std::cin); + } + } catch (const std::exception& e) { + SPDLOG_ERROR("Error validating JSON: {}", e.what()); + return 1; + } + return 0; +} + +/** + * Executes the generate command, generating a header file from a JSON file or + * JSON string from stdin. + * @param progName is the name of the program executable. + * @param argVec is the vector of command line arguments. + * @param i is the index to the first sub-command argument within @p argVec + * @return 0 on success, 1 on error. + */ +auto executeGenerateCommand(const std::string& progName, + const std::vector& argVec, + const size_t i) -> int { + GenerateArguments generateArgs; + // parse the rest of the command line arguments for the generate command + try { + generateArgs = parseGenerateArguments(argVec, i); + } catch (const std::exception& e) { + SPDLOG_ERROR("Error parsing generate arguments: {}", e.what()); + printGenerateUsage(progName); + return 1; + } + // if the help flag is set, print the 'generate' usage information and exit + if (generateArgs.help) { + printGenerateUsage(progName); + return 0; + } + // generate the header file from the JSON specification + try { + qdmi::sc::Device device; + // read the JSON file or the JSON string from stdin + if (generateArgs.jsonFile.has_value()) { + device = qdmi::sc::readJSON(generateArgs.jsonFile.value()); + } else { + device = qdmi::sc::readJSON(std::cin); + } + // write the header file to the output file or stdout + if (generateArgs.outputFile.has_value()) { + qdmi::sc::writeHeader(device, generateArgs.outputFile.value()); + } else { + qdmi::sc::writeHeader(device, std::cout); + } + } catch (const std::exception& e) { + SPDLOG_ERROR("Error generating header file: {}", e.what()); + return 1; + } + return 0; +} +} // namespace + +/** + * @brief Main function that parses command-line-arguments and processes the + * JSON. + * @details This function handles the command line arguments, checks for help + * and version flags, and processes the JSON file or schema file as specified by + * the user. Either a JSON file or a schema file must be provided. If no output + * file is specified, the JSON file is parsed but no header file is generated. + * + * @param argc is the number of command line arguments. + * @param argv is the array of command line arguments. + */ +int main(int argc, char* argv[]) { + std::vector argVec; + std::pair parsedArgs; + // `main` functions should not throw exceptions. Apparently, the + // initialization of a vector can throw exceptions, so we catch them here. + try { + argVec.reserve(argc); + for (const auto& arg : std::span(argv, argc)) { + argVec.emplace_back(arg); + } + } catch (std::exception& e) { + SPDLOG_ERROR("Error parsing arguments into vector: {}", e.what()); + return 1; + } + // parse the command line arguments up to the first sub-command + try { + parsedArgs = parseArguments(argVec); + } catch (const std::exception& e) { + SPDLOG_ERROR("Error parsing arguments: {}", e.what()); + printUsage(argVec.empty() ? "mqt-core-sc-device-gen" : argVec.front()); + return 1; + } + // unpack the parsed arguments and the index of the first sub-command here + // because structured bindings only work with fresh variables + const auto& [args, i] = parsedArgs; + // print help or version information if requested + if (args.help) { + printUsage(args.programName); + return 0; + } + // if the version flag is set, print the version information and exit + if (args.version) { + printVersion(); + return 0; + } + // if no command is specified, print the usage information + if (!args.command.has_value()) { + printUsage(args.programName); + return 1; + } + switch (*args.command) { + case Command::Schema: + return executeSchemaCommand(args.programName, argVec, i); + case Command::Validate: + return executeValidateCommand(args.programName, argVec, i); + case Command::Generate: + return executeGenerateCommand(args.programName, argVec, i); + } + return 0; +} diff --git a/src/qdmi/sc/CMakeLists.txt b/src/qdmi/sc/CMakeLists.txt new file mode 100644 index 0000000000..f83c70bbac --- /dev/null +++ b/src/qdmi/sc/CMakeLists.txt @@ -0,0 +1,206 @@ +# 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 + +# Set target name +set(TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-sc-device-gen) + +# If the target is not already defined +if(NOT TARGET ${TARGET_NAME}) + + # Add library for device generation + # + # Note: We use a static library here to avoid issues with RPATH and finding the executable during + # the build process in Python builds + add_library(${TARGET_NAME} STATIC) + add_library(MQT::CoreQDMIScDeviceGen ALIAS ${TARGET_NAME}) + + # add sources to target + target_sources(${TARGET_NAME} PRIVATE Generator.cpp) + + # add headers using file sets + target_sources(${TARGET_NAME} PUBLIC FILE_SET HEADERS BASE_DIRS ${MQT_CORE_INCLUDE_BUILD_DIR} + FILES ${MQT_CORE_INCLUDE_BUILD_DIR}/qdmi/sc/Generator.hpp) + + # Link nlohmann_json, spdlog + target_link_libraries( + ${TARGET_NAME} + PUBLIC nlohmann_json::nlohmann_json + PRIVATE spdlog::spdlog MQT::ProjectOptions MQT::ProjectWarnings) + + # set versioning information + set_target_properties( + ${TARGET_NAME} + PROPERTIES VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + EXPORT_NAME CoreScDeviceGen) + + # set c++ standard + target_compile_features(${TARGET_NAME} PRIVATE cxx_std_20) + + # add to list of MQT core targets + set(MQT_CORE_TARGETS ${MQT_CORE_TARGETS} ${TARGET_NAME}) + + # Make version available + target_compile_definitions(${TARGET_NAME} PRIVATE MQT_CORE_VERSION="${MQT_CORE_VERSION}") +endif() + +# Set target name +set(TARGET_NAME mqt-core-qdmi-sc-device-generator) + +# If the target is not already defined +if(NOT TARGET ${TARGET_NAME}) + # Add executable for device generation + add_executable(${TARGET_NAME}) + + # add sources to target + target_sources(${TARGET_NAME} PRIVATE App.cpp) + + # Link Generator library + target_link_libraries(${TARGET_NAME} PRIVATE MQT::CoreQDMIScDeviceGen spdlog::spdlog) + + # set versioning information + set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${PROJECT_VERSION} EXPORT_NAME + CoreScDeviceGen) + # place in the bin directory + set_target_properties(${TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/bin") + + # set c++ standard + target_compile_features(${TARGET_NAME} PRIVATE cxx_std_20) + + # Make version available + target_compile_definitions(${TARGET_NAME} PRIVATE MQT_CORE_VERSION="${MQT_CORE_VERSION}") + + # Create an alias for the target + add_executable(MQT::CoreQDMIScDeviceGenerator ALIAS ${TARGET_NAME}) +endif() + +# Set target name +set(TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-sc-device) + +# Set prefix for QDMI +set(QDMI_PREFIX "MQT_SC") + +# If the target is not already defined +if(NOT TARGET ${TARGET_NAME}) + + # Set paths + set(JSON_FILE ${PROJECT_SOURCE_DIR}/json/sc/device.json) + set(DEVICE_HDR ${CMAKE_CURRENT_BINARY_DIR}/include/qdmi/sc/DeviceMemberInitializers.hpp) + + # Create include directory + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/qdmi/sc) + + # Generate definitions for device + add_custom_command( + OUTPUT ${DEVICE_HDR} + COMMAND MQT::CoreQDMIScDeviceGenerator ARGS generate --output ${DEVICE_HDR} ${JSON_FILE} + DEPENDS ${JSON_FILE} MQT::CoreQDMIScDeviceGenerator + COMMENT "Generating C++ header from ${JSON_FILE}") + add_custom_target(generate_qdmi_sc_device_header DEPENDS ${DEVICE_HDR}) + + # Add library + add_mqt_core_library(${TARGET_NAME} ALIAS_NAME QDMIScDevice) + add_dependencies(${TARGET_NAME} generate_qdmi_sc_device_header) + + # Generate prefixed QDMI headers + generate_prefixed_qdmi_headers(${QDMI_PREFIX}) + file(GLOB_RECURSE QDMI_HDRS ${CMAKE_CURRENT_BINARY_DIR}/include/mqt_sc_qdmi/**.h) + + # add sources to target + target_sources(${TARGET_NAME} PRIVATE Device.cpp) + + # add headers using file sets + target_sources( + ${TARGET_NAME} + PUBLIC FILE_SET + HEADERS + BASE_DIRS + ${MQT_CORE_INCLUDE_BUILD_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/include + FILES + ${DEVICE_HDR} + ${MQT_CORE_INCLUDE_BUILD_DIR}/qdmi/sc/Device.hpp + ${QDMI_HDRS}) + + # add link libraries + target_link_libraries( + ${TARGET_NAME} + PUBLIC qdmi::qdmi + PRIVATE MQT::ProjectOptions MQT::ProjectWarnings spdlog::spdlog) + + # Always compile with position independent code such that the library can be used in shared + # libraries + set_target_properties(${TARGET_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) + + # set c++ standard + target_compile_features(${TARGET_NAME} PRIVATE cxx_std_20) + + # set versioning information + set_target_properties( + ${TARGET_NAME} + PROPERTIES VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + EXPORT_NAME CoreQDMIScDevice) + + # add to list of MQT core targets + set(MQT_CORE_TARGETS + ${MQT_CORE_TARGETS} ${TARGET_NAME} + PARENT_SCOPE) + + # Make QDMI and MQT Core Version available + target_compile_definitions(${TARGET_NAME} PRIVATE QDMI_VERSION="${QDMI_VERSION}" + MQT_CORE_VERSION="${MQT_CORE_VERSION}") + + # Generate additional alias for the target required for generate_device_defs_executable function + # in the tests + add_library(qdmi::mqt_sc_device ALIAS ${TARGET_NAME}) + + # Do not build dynamic SC device on Windows because it cannot be used anyways in the current setup + if(NOT WIN32) + set(DYN_TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-sc-device-dyn) + if(NOT TARGET ${DYN_TARGET_NAME}) + # Set prefix for QDMI + set(QDMI_PREFIX "MQT_SC_DYN") + # Generate prefixed QDMI headers + generate_prefixed_qdmi_headers(${QDMI_PREFIX}) + file(GLOB_RECURSE QDMI_HDRS ${CMAKE_CURRENT_BINARY_DIR}/include/mqt_sc_dyn_qdmi/**.hpp) + # Add dynamic library target + add_library(${DYN_TARGET_NAME} SHARED) + add_dependencies(${DYN_TARGET_NAME} ${TARGET_NAME}) + # add sources to target + target_sources(${DYN_TARGET_NAME} PRIVATE DynDevice.cpp) + # add headers using file sets + target_sources( + ${DYN_TARGET_NAME} + PUBLIC FILE_SET + HEADERS + BASE_DIRS + ${MQT_CORE_INCLUDE_BUILD_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/include + FILES + ${DEVICE_HDR} + ${MQT_CORE_INCLUDE_BUILD_DIR}/qdmi/sc/Device.hpp + ${QDMI_HDRS}) + # add link libraries + target_link_libraries( + ${DYN_TARGET_NAME} + PUBLIC qdmi::qdmi + PRIVATE ${TARGET_NAME} MQT::ProjectOptions MQT::ProjectWarnings spdlog::spdlog) + # set c++ standard + target_compile_features(${DYN_TARGET_NAME} PRIVATE cxx_std_20) + # set versioning information + set_target_properties( + ${DYN_TARGET_NAME} + PROPERTIES VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + EXPORT_NAME CoreQDMIScDeviceDyn) + add_library(MQT::CoreQDMIScDeviceDyn ALIAS ${DYN_TARGET_NAME}) + endif() + endif() +endif() diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp new file mode 100644 index 0000000000..f384aa6329 --- /dev/null +++ b/src/qdmi/sc/Device.cpp @@ -0,0 +1,470 @@ +/* + * 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 + */ + +/** @file + * @brief The MQT QDMI device implementation for superconducting devices. + */ + +#include "qdmi/sc/Device.hpp" + +#include "mqt_sc_qdmi/device.h" +#include "qdmi/sc/DeviceMemberInitializers.hpp" + +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(bugprone-macro-parentheses) +#define ADD_SINGLE_VALUE_PROPERTY(prop_name, prop_type, prop_value, prop, \ + size, value, size_ret) \ + { \ + if ((prop) == (prop_name)) { \ + if ((value) != nullptr) { \ + if ((size) < sizeof(prop_type)) { \ + return QDMI_ERROR_INVALIDARGUMENT; \ + } \ + *static_cast(value) = prop_value; \ + } \ + if ((size_ret) != nullptr) { \ + *size_ret = sizeof(prop_type); \ + } \ + return QDMI_SUCCESS; \ + } \ + } + +#ifdef _WIN32 +#define STRNCPY(dest, src, size) \ + strncpy_s(static_cast(dest), size, src, size); +#else +#define STRNCPY(dest, src, size) strncpy(static_cast(dest), src, size); +#endif + +#define ADD_STRING_PROPERTY(prop_name, prop_value, prop, size, value, \ + size_ret) \ + { \ + if ((prop) == (prop_name)) { \ + if ((value) != nullptr) { \ + if ((size) < strlen(prop_value) + 1) { \ + return QDMI_ERROR_INVALIDARGUMENT; \ + } \ + STRNCPY(value, prop_value, size); \ + /* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ \ + static_cast(value)[size - 1] = '\0'; \ + } \ + if ((size_ret) != nullptr) { \ + *size_ret = strlen(prop_value) + 1; \ + } \ + return QDMI_SUCCESS; \ + } \ + } + +#define ADD_LIST_PROPERTY(prop_name, prop_type, prop_values, prop, size, \ + value, size_ret) \ + { \ + if ((prop) == (prop_name)) { \ + if ((value) != nullptr) { \ + if ((size) < (prop_values).size() * sizeof(prop_type)) { \ + return QDMI_ERROR_INVALIDARGUMENT; \ + } \ + memcpy(static_cast(value), \ + static_cast((prop_values).data()), \ + (prop_values).size() * sizeof(prop_type)); \ + } \ + if ((size_ret) != nullptr) { \ + *size_ret = (prop_values).size() * sizeof(prop_type); \ + } \ + return QDMI_SUCCESS; \ + } \ + } +// NOLINTEND(bugprone-macro-parentheses) + +namespace qdmi { +Device::Device() { + // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer) + INITIALIZE_NAME(name_); + INITIALIZE_QUBITSNUM(qubitsNum_); + // NOLINTEND(cppcoreguidelines-prefer-member-initializer) + // NOLINTNEXTLINE(misc-const-correctness) + INITIALIZE_SITES(sites_); + INITIALIZE_COUPLINGMAP(couplingMap_); +} +auto Device::sessionAlloc(MQT_SC_QDMI_Device_Session* session) -> int { + if (session == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + auto uniqueSession = std::make_unique(); + const auto& it = + sessions_.emplace(uniqueSession.get(), std::move(uniqueSession)).first; + // get the key, i.e., the raw pointer to the session from the map iterator + *session = it->first; + return QDMI_SUCCESS; +} +auto Device::sessionFree(MQT_SC_QDMI_Device_Session session) -> void { + if (session != nullptr) { + if (const auto& it = sessions_.find(session); it != sessions_.end()) { + sessions_.erase(it); + } + } +} +auto Device::queryProperty(const QDMI_Device_Property prop, const size_t size, + void* value, size_t* sizeRet) -> int { + if ((value != nullptr && size == 0) || prop >= QDMI_DEVICE_PROPERTY_MAX) { + return QDMI_ERROR_INVALIDARGUMENT; + } + ADD_STRING_PROPERTY(QDMI_DEVICE_PROPERTY_NAME, name_.c_str(), prop, size, + value, sizeRet) + // NOLINTNEXTLINE(misc-include-cleaner) + ADD_STRING_PROPERTY(QDMI_DEVICE_PROPERTY_VERSION, MQT_CORE_VERSION, prop, + size, value, sizeRet) + // NOLINTNEXTLINE(misc-include-cleaner) + ADD_STRING_PROPERTY(QDMI_DEVICE_PROPERTY_LIBRARYVERSION, QDMI_VERSION, prop, + size, value, sizeRet) + ADD_SINGLE_VALUE_PROPERTY(QDMI_DEVICE_PROPERTY_STATUS, QDMI_Device_Status, + QDMI_DEVICE_STATUS_IDLE, prop, size, value, sizeRet) + ADD_SINGLE_VALUE_PROPERTY(QDMI_DEVICE_PROPERTY_QUBITSNUM, size_t, qubitsNum_, + prop, size, value, sizeRet) + // This device never needs calibration + ADD_SINGLE_VALUE_PROPERTY(QDMI_DEVICE_PROPERTY_NEEDSCALIBRATION, size_t, 0, + prop, size, value, sizeRet) + // This device does not support pulse-level control + ADD_SINGLE_VALUE_PROPERTY( + QDMI_DEVICE_PROPERTY_PULSESUPPORT, QDMI_Device_Pulse_Support_Level, + QDMI_DEVICE_PULSE_SUPPORT_LEVEL_NONE, prop, size, value, sizeRet) + ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_SITES, MQT_SC_QDMI_Site, sites_, prop, + size, value, sizeRet) + ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_COUPLINGMAP, MQT_SC_QDMI_Site, + couplingMap_, prop, size, value, sizeRet) + ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_OPERATIONS, MQT_SC_QDMI_Operation, + operations_, prop, size, value, sizeRet) + return QDMI_ERROR_NOTSUPPORTED; +} +} // namespace qdmi + +auto MQT_SC_QDMI_Device_Session_impl_d::init() -> int { + if (status_ != Status::ALLOCATED) { + return QDMI_ERROR_BADSTATE; + } + status_ = Status::INITIALIZED; + return QDMI_SUCCESS; +} +auto MQT_SC_QDMI_Device_Session_impl_d::setParameter( + QDMI_Device_Session_Parameter param, const size_t size, + const void* value) const -> int { + if ((value != nullptr && size == 0) || + param >= QDMI_DEVICE_SESSION_PARAMETER_MAX) { + return QDMI_ERROR_INVALIDARGUMENT; + } + if (status_ != Status::ALLOCATED) { + return QDMI_ERROR_BADSTATE; + } + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Device_Session_impl_d::createDeviceJob( + // NOLINTNEXTLINE(readability-non-const-parameter) + MQT_SC_QDMI_Device_Job* job) -> int { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + if (status_ == Status::ALLOCATED) { + return QDMI_ERROR_BADSTATE; + } + auto uniqueJob = std::make_unique(this); + *job = jobs_.emplace(uniqueJob.get(), std::move(uniqueJob)).first->first; + return QDMI_SUCCESS; +} +auto MQT_SC_QDMI_Device_Session_impl_d::freeDeviceJob( + MQT_SC_QDMI_Device_Job job) -> void { + if (job != nullptr) { + jobs_.erase(job); + } +} +auto MQT_SC_QDMI_Device_Session_impl_d::queryDeviceProperty( + const QDMI_Device_Property prop, const size_t size, void* value, + size_t* sizeRet) const -> int { + if (status_ != Status::INITIALIZED) { + return QDMI_ERROR_BADSTATE; + } + return qdmi::Device::get().queryProperty(prop, size, value, sizeRet); +} +auto MQT_SC_QDMI_Device_Session_impl_d::querySiteProperty( + MQT_SC_QDMI_Site site, const QDMI_Site_Property prop, const size_t size, + void* value, size_t* sizeRet) const -> int { + if (site == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + if (status_ != Status::INITIALIZED) { + return QDMI_ERROR_BADSTATE; + } + return site->queryProperty(prop, size, value, sizeRet); +} +auto MQT_SC_QDMI_Device_Session_impl_d::queryOperationProperty( + MQT_SC_QDMI_Operation operation, const size_t numSites, + const MQT_SC_QDMI_Site* sites, const size_t numParams, const double* params, + const QDMI_Operation_Property prop, const size_t size, void* value, + size_t* sizeRet) const -> int { + if (operation == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + if (status_ != Status::INITIALIZED) { + return QDMI_ERROR_BADSTATE; + } + return operation->queryProperty(numSites, sites, numParams, params, prop, + size, value, sizeRet); +} +auto MQT_SC_QDMI_Device_Job_impl_d::free() -> void { + session_->freeDeviceJob(this); +} +auto MQT_SC_QDMI_Device_Job_impl_d::setParameter( + const QDMI_Device_Job_Parameter param, const size_t size, const void* value) + -> int { + if ((value != nullptr && size == 0) || + param >= QDMI_DEVICE_JOB_PARAMETER_MAX) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Device_Job_impl_d::queryProperty( + // NOLINTNEXTLINE(readability-non-const-parameter) + const QDMI_Device_Job_Property prop, const size_t size, void* value, + [[maybe_unused]] size_t* sizeRet) -> int { + if ((value != nullptr && size == 0) || prop >= QDMI_DEVICE_JOB_PROPERTY_MAX) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Device_Job_impl_d::submit() -> int { + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Device_Job_impl_d::cancel() -> int { + return QDMI_ERROR_NOTSUPPORTED; +} +// NOLINTNEXTLINE(readability-non-const-parameter) +auto MQT_SC_QDMI_Device_Job_impl_d::check(QDMI_Job_Status* status) -> int { + if (status == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Device_Job_impl_d::wait([[maybe_unused]] const size_t timeout) + -> int { + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Device_Job_impl_d::getResults( + QDMI_Job_Result result, + // NOLINTNEXTLINE(readability-non-const-parameter) + const size_t size, void* data, [[maybe_unused]] size_t* sizeRet) -> int { + if ((data != nullptr && size == 0) || result >= QDMI_JOB_RESULT_MAX) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Site_impl_d::makeUniqueSite(const uint64_t id) + -> std::unique_ptr { + const MQT_SC_QDMI_Site_impl_d site(id); + return std::make_unique(site); +} +auto MQT_SC_QDMI_Site_impl_d::queryProperty(const QDMI_Site_Property prop, + const size_t size, void* value, + size_t* sizeRet) const -> int { + if ((value != nullptr && size == 0) || prop >= QDMI_SITE_PROPERTY_MAX) { + return QDMI_ERROR_INVALIDARGUMENT; + } + ADD_SINGLE_VALUE_PROPERTY(QDMI_SITE_PROPERTY_INDEX, uint64_t, id_, prop, size, + value, sizeRet) + return QDMI_ERROR_NOTSUPPORTED; +} +auto MQT_SC_QDMI_Operation_impl_d::makeUnique() + -> std::unique_ptr { + MQT_SC_QDMI_Operation_impl_d op{}; + return std::make_unique(std::move(op)); +} +auto MQT_SC_QDMI_Operation_impl_d::queryProperty( + const size_t numSites, const MQT_SC_QDMI_Site* sites, + const size_t numParams, const double* params, + const QDMI_Operation_Property prop, const size_t size, void* value, + size_t* /* unused */) const -> int { + if ((sites != nullptr && numSites == 0) || + (params != nullptr && numParams == 0) || + (value != nullptr && size == 0) || prop >= QDMI_OPERATION_PROPERTY_MAX) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return QDMI_ERROR_NOTSUPPORTED; +} + +int MQT_SC_QDMI_device_initialize() { + std::ignore = qdmi::Device::get(); // Ensure the singleton is created + return QDMI_SUCCESS; +} + +int MQT_SC_QDMI_device_finalize() { return QDMI_SUCCESS; } + +int MQT_SC_QDMI_device_session_alloc(MQT_SC_QDMI_Device_Session* session) { + return qdmi::Device::get().sessionAlloc(session); +} + +int MQT_SC_QDMI_device_session_init(MQT_SC_QDMI_Device_Session session) { + if (session == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return session->init(); +} + +void MQT_SC_QDMI_device_session_free(MQT_SC_QDMI_Device_Session session) { + qdmi::Device::get().sessionFree(session); +} + +int MQT_SC_QDMI_device_session_set_parameter( + MQT_SC_QDMI_Device_Session session, QDMI_Device_Session_Parameter param, + const size_t size, const void* value) { + if (session == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return session->setParameter(param, size, value); +} + +int MQT_SC_QDMI_device_session_create_device_job( + MQT_SC_QDMI_Device_Session session, MQT_SC_QDMI_Device_Job* job) { + if (session == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return session->createDeviceJob(job); +} + +void MQT_SC_QDMI_device_job_free(MQT_SC_QDMI_Device_Job job) { job->free(); } + +int MQT_SC_QDMI_device_job_set_parameter(MQT_SC_QDMI_Device_Job job, + const QDMI_Device_Job_Parameter param, + const size_t size, const void* value) { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + // The called function is only static because jobs are not supported. However, + // we keep the function call for a complete implementation of QDMI and silence + // the respective warning. + //===--------------------------------------------------------------------===// + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->setParameter(param, size, value); +} + +int MQT_SC_QDMI_device_job_query_property(MQT_SC_QDMI_Device_Job job, + const QDMI_Device_Job_Property prop, + const size_t size, void* value, + size_t* sizeRet) { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + // The called function is only static because jobs are not supported. We keep + // the function call, however, as it is necessary to free the job when the + // jobs are supported. + //===--------------------------------------------------------------------===// + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->queryProperty(prop, size, value, sizeRet); +} + +int MQT_SC_QDMI_device_job_submit(MQT_SC_QDMI_Device_Job job) { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + // The called function is only static because jobs are not supported. We keep + // the function call, however, as it is necessary to free the job when the + // jobs are supported. + //===--------------------------------------------------------------------===// + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->submit(); +} + +int MQT_SC_QDMI_device_job_cancel(MQT_SC_QDMI_Device_Job job) { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + // The called function is only static because jobs are not supported. We keep + // the function call, however, as it is necessary to free the job when the + // jobs are supported. + //===--------------------------------------------------------------------===// + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->cancel(); +} + +int MQT_SC_QDMI_device_job_check(MQT_SC_QDMI_Device_Job job, + QDMI_Job_Status* status) { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + // The called function is only static because jobs are not supported. We keep + // the function call, however, as it is necessary to free the job when the + // jobs are supported. + //===--------------------------------------------------------------------===// + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->check(status); +} + +int MQT_SC_QDMI_device_job_wait(MQT_SC_QDMI_Device_Job job, + const size_t timeout) { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + // The called function is only static because jobs are not supported. We keep + // the function call, however, as it is necessary to free the job when the + // jobs are supported. + //===--------------------------------------------------------------------===// + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->wait(timeout); +} + +int MQT_SC_QDMI_device_job_get_results(MQT_SC_QDMI_Device_Job job, + QDMI_Job_Result result, + const size_t size, void* data, + size_t* sizeRet) { + if (job == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + // The called function is only static because jobs are not supported. We keep + // the function call, however, as it is necessary to free the job when the + // jobs are supported. + //===--------------------------------------------------------------------===// + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->getResults(result, size, data, sizeRet); +} + +int MQT_SC_QDMI_device_session_query_device_property( + MQT_SC_QDMI_Device_Session session, const QDMI_Device_Property prop, + const size_t size, void* value, size_t* sizeRet) { + if (session == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return session->queryDeviceProperty(prop, size, value, sizeRet); +} + +int MQT_SC_QDMI_device_session_query_site_property( + MQT_SC_QDMI_Device_Session session, MQT_SC_QDMI_Site site, + const QDMI_Site_Property prop, const size_t size, void* value, + size_t* sizeRet) { + if (session == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return session->querySiteProperty(site, prop, size, value, sizeRet); +} + +int MQT_SC_QDMI_device_session_query_operation_property( + MQT_SC_QDMI_Device_Session session, MQT_SC_QDMI_Operation operation, + const size_t numSites, const MQT_SC_QDMI_Site* sites, + const size_t numParams, const double* params, + const QDMI_Operation_Property prop, const size_t size, void* value, + size_t* sizeRet) { + if (session == nullptr) { + return QDMI_ERROR_INVALIDARGUMENT; + } + return session->queryOperationProperty(operation, numSites, sites, numParams, + params, prop, size, value, sizeRet); +} diff --git a/src/qdmi/sc/DynDevice.cpp b/src/qdmi/sc/DynDevice.cpp new file mode 100644 index 0000000000..8427704dc9 --- /dev/null +++ b/src/qdmi/sc/DynDevice.cpp @@ -0,0 +1,155 @@ +/* + * 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 + */ + +/** + * @file This file is a thin wrapper around MQT's Neutral Atom QDMI Device with + * another prefix. + */ + +#include "mqt_sc_dyn_qdmi/device.h" +#include "mqt_sc_qdmi/device.h" + +#include +#include + +int MQT_SC_DYN_QDMI_device_initialize() { + return MQT_SC_QDMI_device_initialize(); +} + +int MQT_SC_DYN_QDMI_device_finalize() { return MQT_SC_QDMI_device_finalize(); } + +int MQT_SC_DYN_QDMI_device_session_alloc( + MQT_SC_DYN_QDMI_Device_Session* session) { + return MQT_SC_QDMI_device_session_alloc( + reinterpret_cast(session)); +} + +int MQT_SC_DYN_QDMI_device_session_init( + MQT_SC_DYN_QDMI_Device_Session session) { + return MQT_SC_QDMI_device_session_init( + reinterpret_cast(session)); +} + +void MQT_SC_DYN_QDMI_device_session_free( + MQT_SC_DYN_QDMI_Device_Session session) { + MQT_SC_QDMI_device_session_free( + reinterpret_cast(session)); +} + +int MQT_SC_DYN_QDMI_device_session_set_parameter( + MQT_SC_DYN_QDMI_Device_Session session, QDMI_Device_Session_Parameter param, + const size_t size, const void* value) { + return MQT_SC_QDMI_device_session_set_parameter( + reinterpret_cast(session), param, size, + value); +} + +int MQT_SC_DYN_QDMI_device_session_create_device_job( + MQT_SC_DYN_QDMI_Device_Session session, MQT_SC_DYN_QDMI_Device_Job* job) { + return MQT_SC_QDMI_device_session_create_device_job( + reinterpret_cast(session), + reinterpret_cast(job)); +} + +void MQT_SC_DYN_QDMI_device_job_free(MQT_SC_DYN_QDMI_Device_Job job) { + MQT_SC_QDMI_device_job_free(reinterpret_cast(job)); +} + +int MQT_SC_DYN_QDMI_device_job_set_parameter( + MQT_SC_DYN_QDMI_Device_Job job, const QDMI_Device_Job_Parameter param, + const size_t size, const void* value) { + return MQT_SC_QDMI_device_job_set_parameter( + reinterpret_cast(job), param, size, value); +} + +int MQT_SC_DYN_QDMI_device_job_query_property( + MQT_SC_DYN_QDMI_Device_Job job, const QDMI_Device_Job_Property prop, + const size_t size, void* value, size_t* sizeRet) { + return MQT_SC_QDMI_device_job_query_property( + reinterpret_cast(job), prop, size, value, + sizeRet); +} + +int MQT_SC_DYN_QDMI_device_job_submit(MQT_SC_DYN_QDMI_Device_Job job) { + return MQT_SC_QDMI_device_job_submit( + reinterpret_cast(job)); +} + +int MQT_SC_DYN_QDMI_device_job_cancel(MQT_SC_DYN_QDMI_Device_Job job) { + return MQT_SC_QDMI_device_job_cancel( + reinterpret_cast(job)); +} + +int MQT_SC_DYN_QDMI_device_job_check(MQT_SC_DYN_QDMI_Device_Job job, + QDMI_Job_Status* status) { + return MQT_SC_QDMI_device_job_check( + reinterpret_cast(job), status); +} + +int MQT_SC_DYN_QDMI_device_job_wait(MQT_SC_DYN_QDMI_Device_Job job, + const size_t timeout) { + return MQT_SC_QDMI_device_job_wait( + reinterpret_cast(job), timeout); +} + +int MQT_SC_DYN_QDMI_device_job_get_results(MQT_SC_DYN_QDMI_Device_Job job, + QDMI_Job_Result result, + const size_t size, void* data, + size_t* sizeRet) { + return MQT_SC_QDMI_device_job_get_results( + reinterpret_cast(job), result, size, data, + sizeRet); +} + +int MQT_SC_DYN_QDMI_device_session_query_device_property( + MQT_SC_DYN_QDMI_Device_Session session, const QDMI_Device_Property prop, + const size_t size, void* value, size_t* sizeRet) { + const auto result = MQT_SC_QDMI_device_session_query_device_property( + reinterpret_cast(session), prop, size, value, + sizeRet); + // let the proper device implementation do the error handling and check for + // the name property afterward + if (result == QDMI_SUCCESS && prop == QDMI_DEVICE_PROPERTY_NAME) { + if (value != nullptr) { + if (size < 27) { + return QDMI_ERROR_INVALIDARGUMENT; + } + strncpy(static_cast(value), "MQT NA Dynamic QDMI Device", size); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + static_cast(value)[size - 1] = '\0'; + } + if ((sizeRet) != nullptr) { + *sizeRet = 27; + } + } + return result; +} + +int MQT_SC_DYN_QDMI_device_session_query_site_property( + MQT_SC_DYN_QDMI_Device_Session session, MQT_SC_DYN_QDMI_Site site, + const QDMI_Site_Property prop, const size_t size, void* value, + size_t* sizeRet) { + return MQT_SC_QDMI_device_session_query_site_property( + reinterpret_cast(session), + reinterpret_cast(site), prop, size, value, sizeRet); +} + +int MQT_SC_DYN_QDMI_device_session_query_operation_property( + MQT_SC_DYN_QDMI_Device_Session session, MQT_SC_DYN_QDMI_Operation operation, + const size_t numSites, const MQT_SC_DYN_QDMI_Site* sites, + const size_t numParams, const double* params, + const QDMI_Operation_Property prop, const size_t size, void* value, + size_t* sizeRet) { + return MQT_SC_QDMI_device_session_query_operation_property( + reinterpret_cast(session), + reinterpret_cast(operation), numSites, + reinterpret_cast(sites), numParams, params, prop, + size, value, sizeRet); +} diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp new file mode 100644 index 0000000000..ed5189d99b --- /dev/null +++ b/src/qdmi/sc/Generator.cpp @@ -0,0 +1,170 @@ +/* + * 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 + */ + +/** @file + * @brief The MQT QDMI device generator for neutral atom devices. + */ + +#include "qdmi/sc/Generator.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qdmi::sc { +namespace { +/** + * @brief Populates all array fields in the device object with default values. + * @param device is the device object to populate. + * @note This is a recursive auxiliary function used by @ref writeJSONSchema. + */ +auto populateArrayFields(Device& device) -> void { + device.couplings.emplace_back(); +} + +/** + * @brief Writes the name from the device object. + * @param device is the device object containing the name. + * @param os is the output stream to write the name to. + */ +auto writeName(const Device& device, std::ostream& os) -> void { + os << "#define INITIALIZE_NAME(var) var = \"" << device.name << "\"\n"; +} + +/** + * @brief Writes the qubits number from the device object. + * @param device is the device object containing the number of qubits. + * @param os is the output stream to write the qubits number to. + */ +auto writeQubitsNum(const Device& device, std::ostream& os) -> void { + os << "#define INITIALIZE_QUBITSNUM(var) var = " << device.numQubits + << "ULL\n"; +} + +/** + * @brief Writes the sites from the device object. + * @param device is the device object containing the number of sites. + * @param os is the output stream to write the sites to. + */ +auto writeSites(const Device& device, std::ostream& os) -> void { + os << "#define INITIALIZE_SITES(var) var.clear()"; + for (uint64_t id = 0; id < device.numQubits; ++id) { + os << ";\\\n " + "var.emplace_back(MQT_SC_QDMI_Site_impl_d::makeUniqueSite(" + << id << "ULL))"; + } + os << ";\\\n std::vector> " + "_couplings;"; + os << ";\\\n _couplings.reserve(" << device.couplings.size() << ");"; + for (const auto& [i1, i2] : device.couplings) { + os << ";\\\n " + "_couplings.emplace_back(var.at(" + << i1 << ").get(), var.at(" << i2 << ").get())"; + } + os << "\n"; +} + +/** + * @brief Writes the sites from the device object. + * @param os is the output stream to write the sites to. + */ +auto writeCouplingMap(const Device& /* unused */, std::ostream& os) -> void { + os << "#define INITIALIZE_COUPLINGMAP(var) var = std::move(_couplings)\n"; +} +} // namespace + +auto writeJSONSchema(std::ostream& os) -> void { + // Create a default device configuration + Device device; + + // Fill each array field with default values + populateArrayFields(device); + + // Convert the device configuration to a JSON object + // NOLINTNEXTLINE(misc-include-cleaner) + const nlohmann::json json = device; + + // Write to the output stream + os << json; +} + +auto writeJSONSchema(const std::string& path) -> void { + // Write to a file + std::ofstream ofs(path); + if (!ofs.good()) { + std::stringstream ss; + ss << "Failed to open file for writing: " << path; + throw std::runtime_error(ss.str()); + } + writeJSONSchema(ofs); + ofs.close(); + SPDLOG_INFO("JSON template written to {}", path); +} + +[[nodiscard]] auto readJSON(std::istream& is) -> Device { + // Read the device configuration from the input stream + nlohmann::json json; + try { + is >> json; + // NOLINTNEXTLINE(misc-include-cleaner) + } catch (const nlohmann::detail::parse_error& e) { + std::stringstream ss; + ss << "Failed to parse JSON string: " << e.what(); + throw std::runtime_error(ss.str()); + } + return json; +} + +[[nodiscard]] auto readJSON(const std::string& path) -> Device { + // Read the device configuration from a JSON file + std::ifstream ifs(path); + if (!ifs.good()) { + throw std::runtime_error("Failed to open JSON file: " + std::string(path)); + } + const auto& device = readJSON(ifs); + ifs.close(); + return device; +} + +auto writeHeader(const Device& device, std::ostream& os) -> void { + os << "#pragma once\n\n"; + writeName(device, os); + writeQubitsNum(device, os); + writeSites(device, os); + writeCouplingMap(device, os); +} + +auto writeHeader(const Device& device, const std::string& path) -> void { + std::ofstream ofs(path); + if (!ofs.good()) { + std::stringstream ss; + ss << "Failed to open header file for writing: " << path; + throw std::runtime_error(ss.str()); + } + writeHeader(device, ofs); + ofs.close(); + SPDLOG_INFO("Header file written to {}", path); +} +} // namespace qdmi::sc From 2812858212b84a9ef1c5b70cf1d3766967380eb5 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:13:36 +0100 Subject: [PATCH 02/58] =?UTF-8?q?=E2=9C=85=20Setup=20tests=20for=20SC=20de?= =?UTF-8?q?vice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/na/device/Device.hpp | 4 +- include/mqt-core/qdmi/Driver.hpp | 1 + include/mqt-core/qdmi/sc/Device.hpp | 4 +- src/na/device/Device.cpp | 12 +- src/qdmi/CMakeLists.txt | 3 +- src/qdmi/Driver.cpp | 4 + src/qdmi/sc/Device.cpp | 12 +- test/qdmi/CMakeLists.txt | 8 +- test/qdmi/sc/CMakeLists.txt | 31 +++ test/qdmi/sc/test_app.cpp | 371 ++++++++++++++++++++++++++ test/qdmi/sc/test_device.cpp | 348 ++++++++++++++++++++++++ test/qdmi/sc/test_generator.cpp | 56 ++++ test/qdmi/test_driver.cpp | 9 +- 13 files changed, 841 insertions(+), 22 deletions(-) create mode 100644 test/qdmi/sc/CMakeLists.txt create mode 100644 test/qdmi/sc/test_app.cpp create mode 100644 test/qdmi/sc/test_device.cpp create mode 100644 test/qdmi/sc/test_generator.cpp diff --git a/include/mqt-core/na/device/Device.hpp b/include/mqt-core/na/device/Device.hpp index 886ae6acff..99c55c76e1 100644 --- a/include/mqt-core/na/device/Device.hpp +++ b/include/mqt-core/na/device/Device.hpp @@ -26,7 +26,7 @@ #include #include -namespace qdmi { +namespace qdmi::na { class Device final { /// @brief Provides access to the device name. std::string name_; @@ -104,7 +104,7 @@ class Device final { auto queryProperty(QDMI_Device_Property prop, size_t size, void* value, size_t* sizeRet) -> int; }; -} // namespace qdmi +} // namespace qdmi::na /** * @brief Implementation of the MQT_NA_QDMI_Device_Session structure. diff --git a/include/mqt-core/qdmi/Driver.hpp b/include/mqt-core/qdmi/Driver.hpp index 995dabfaf0..2d0f7b778f 100644 --- a/include/mqt-core/qdmi/Driver.hpp +++ b/include/mqt-core/qdmi/Driver.hpp @@ -140,6 +140,7 @@ class DynamicDeviceLibrary final : public DeviceLibrary { // Call the above macro for all static libraries that we want to support. DECLARE_STATIC_LIBRARY(MQT_NA) DECLARE_STATIC_LIBRARY(MQT_DDSIM) +DECLARE_STATIC_LIBRARY(MQT_SC) /** * @brief The status of a session. diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index b61c8f58dd..ec40497404 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -21,7 +21,7 @@ #include #include -namespace qdmi { +namespace qdmi::sc { class Device final { /// @brief Provides access to the device name. std::string name_; @@ -82,7 +82,7 @@ class Device final { auto queryProperty(QDMI_Device_Property prop, size_t size, void* value, size_t* sizeRet) -> int; }; -} // namespace qdmi +} // namespace qdmi::sc /** * @brief Implementation of the MQT_SC_QDMI_Device_Session structure. diff --git a/src/na/device/Device.cpp b/src/na/device/Device.cpp index 62208b4748..0e5780b89a 100644 --- a/src/na/device/Device.cpp +++ b/src/na/device/Device.cpp @@ -89,7 +89,7 @@ } // NOLINTEND(bugprone-macro-parentheses) -namespace qdmi { +namespace qdmi::na { Device::Device() { // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer) INITIALIZE_NAME(name_); @@ -161,7 +161,7 @@ auto Device::queryProperty(const QDMI_Device_Property prop, const size_t size, operations_, prop, size, value, sizeRet) return QDMI_ERROR_NOTSUPPORTED; } -} // namespace qdmi +} // namespace qdmi::na auto MQT_NA_QDMI_Device_Session_impl_d::init() -> int { if (status_ != Status::ALLOCATED) { @@ -207,7 +207,7 @@ auto MQT_NA_QDMI_Device_Session_impl_d::queryDeviceProperty( if (status_ != Status::INITIALIZED) { return QDMI_ERROR_BADSTATE; } - return qdmi::Device::get().queryProperty(prop, size, value, sizeRet); + return qdmi::na::Device::get().queryProperty(prop, size, value, sizeRet); } auto MQT_NA_QDMI_Device_Session_impl_d::querySiteProperty( MQT_NA_QDMI_Site site, const QDMI_Site_Property prop, const size_t size, @@ -559,14 +559,14 @@ auto MQT_NA_QDMI_Operation_impl_d::queryProperty( } int MQT_NA_QDMI_device_initialize() { - std::ignore = qdmi::Device::get(); // Ensure the singleton is created + std::ignore = qdmi::na::Device::get(); // Ensure the singleton is created return QDMI_SUCCESS; } int MQT_NA_QDMI_device_finalize() { return QDMI_SUCCESS; } int MQT_NA_QDMI_device_session_alloc(MQT_NA_QDMI_Device_Session* session) { - return qdmi::Device::get().sessionAlloc(session); + return qdmi::na::Device::get().sessionAlloc(session); } int MQT_NA_QDMI_device_session_init(MQT_NA_QDMI_Device_Session session) { @@ -577,7 +577,7 @@ int MQT_NA_QDMI_device_session_init(MQT_NA_QDMI_Device_Session session) { } void MQT_NA_QDMI_device_session_free(MQT_NA_QDMI_Device_Session session) { - qdmi::Device::get().sessionFree(session); + qdmi::na::Device::get().sessionFree(session); } int MQT_NA_QDMI_device_session_set_parameter( diff --git a/src/qdmi/CMakeLists.txt b/src/qdmi/CMakeLists.txt index 9b66ed5b9a..2a74069adf 100644 --- a/src/qdmi/CMakeLists.txt +++ b/src/qdmi/CMakeLists.txt @@ -26,7 +26,8 @@ if(NOT TARGET ${TARGET_NAME}) target_link_libraries( ${TARGET_NAME} PUBLIC qdmi::qdmi - PRIVATE MQT::CoreQDMINaDevice MQT::CoreQDMI_DDSIM_Device qdmi::project_warnings) + PRIVATE MQT::CoreQDMINaDevice MQT::CoreQDMIScDevice MQT::CoreQDMI_DDSIM_Device + qdmi::project_warnings) # add to list of MQT core targets set(MQT_CORE_TARGETS diff --git a/src/qdmi/Driver.cpp b/src/qdmi/Driver.cpp index 73d03bd821..5f6f28bd2a 100644 --- a/src/qdmi/Driver.cpp +++ b/src/qdmi/Driver.cpp @@ -12,6 +12,7 @@ #include "mqt_ddsim_qdmi/device.h" #include "mqt_na_qdmi/device.h" +#include "mqt_sc_qdmi/device.h" #include #include @@ -76,6 +77,7 @@ namespace qdmi { } DEFINE_STATIC_LIBRARY(MQT_NA) DEFINE_STATIC_LIBRARY(MQT_DDSIM) +DEFINE_STATIC_LIBRARY(MQT_SC) #ifndef _WIN32 DynamicDeviceLibrary::DynamicDeviceLibrary(const std::string& libName, @@ -336,6 +338,8 @@ Driver::Driver() { std::make_unique())); devices_.emplace_back(std::make_unique( std::make_unique())); + devices_.emplace_back(std::make_unique( + std::make_unique())); } Driver::~Driver() { diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index f384aa6329..f73a4f96be 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -89,7 +89,7 @@ } // NOLINTEND(bugprone-macro-parentheses) -namespace qdmi { +namespace qdmi::sc { Device::Device() { // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer) INITIALIZE_NAME(name_); @@ -149,7 +149,7 @@ auto Device::queryProperty(const QDMI_Device_Property prop, const size_t size, operations_, prop, size, value, sizeRet) return QDMI_ERROR_NOTSUPPORTED; } -} // namespace qdmi +} // namespace qdmi::sc auto MQT_SC_QDMI_Device_Session_impl_d::init() -> int { if (status_ != Status::ALLOCATED) { @@ -195,7 +195,7 @@ auto MQT_SC_QDMI_Device_Session_impl_d::queryDeviceProperty( if (status_ != Status::INITIALIZED) { return QDMI_ERROR_BADSTATE; } - return qdmi::Device::get().queryProperty(prop, size, value, sizeRet); + return qdmi::sc::Device::get().queryProperty(prop, size, value, sizeRet); } auto MQT_SC_QDMI_Device_Session_impl_d::querySiteProperty( MQT_SC_QDMI_Site site, const QDMI_Site_Property prop, const size_t size, @@ -303,14 +303,14 @@ auto MQT_SC_QDMI_Operation_impl_d::queryProperty( } int MQT_SC_QDMI_device_initialize() { - std::ignore = qdmi::Device::get(); // Ensure the singleton is created + std::ignore = qdmi::sc::Device::get(); // Ensure the singleton is created return QDMI_SUCCESS; } int MQT_SC_QDMI_device_finalize() { return QDMI_SUCCESS; } int MQT_SC_QDMI_device_session_alloc(MQT_SC_QDMI_Device_Session* session) { - return qdmi::Device::get().sessionAlloc(session); + return qdmi::sc::Device::get().sessionAlloc(session); } int MQT_SC_QDMI_device_session_init(MQT_SC_QDMI_Device_Session session) { @@ -321,7 +321,7 @@ int MQT_SC_QDMI_device_session_init(MQT_SC_QDMI_Device_Session session) { } void MQT_SC_QDMI_device_session_free(MQT_SC_QDMI_Device_Session session) { - qdmi::Device::get().sessionFree(session); + qdmi::sc::Device::get().sessionFree(session); } int MQT_SC_QDMI_device_session_set_parameter( diff --git a/test/qdmi/CMakeLists.txt b/test/qdmi/CMakeLists.txt index 833d3b2c11..b2e3e30748 100644 --- a/test/qdmi/CMakeLists.txt +++ b/test/qdmi/CMakeLists.txt @@ -7,6 +7,7 @@ # Licensed under the MIT License add_subdirectory(dd) +add_subdirectory(sc) set(TARGET_NAME mqt-core-qdmi-driver-test) @@ -14,7 +15,10 @@ if(TARGET MQT::CoreQDMIDriver) package_add_test(${TARGET_NAME} MQT::CoreQDMIDriver test_driver.cpp) if(NOT WIN32) add_dependencies(${TARGET_NAME} MQT::CoreQDMINaDeviceDyn) - target_compile_definitions(${TARGET_NAME} - PRIVATE DYN_DEV_LIB="$") + target_compile_definitions( + ${TARGET_NAME} + PRIVATE + "DYN_DEV_LIBS=std::array{ std::pair{\"$\", \"MQT_NA_DYN\"}, std::pair{\"$\", \"MQT_SC_DYN\"} }" + ) endif() endif() diff --git a/test/qdmi/sc/CMakeLists.txt b/test/qdmi/sc/CMakeLists.txt new file mode 100644 index 0000000000..81c5ace0d2 --- /dev/null +++ b/test/qdmi/sc/CMakeLists.txt @@ -0,0 +1,31 @@ +# 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 + +if(TARGET MQT::CoreQDMIScDeviceGen + AND TARGET MQT::CoreQDMIScDeviceGenerator + AND TARGET MQT::CoreQDMIScDevice) + # Set QDMI prefix again for the tests + set(QDMI_PREFIX "MQT_SC") + generate_device_defs_executable(${QDMI_PREFIX}) + # Set test target name + set(TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-sc-device-test) + # Add the test executable + package_add_test(${TARGET_NAME} MQT::CoreQDMIScDevice test_generator.cpp test_app.cpp + test_device.cpp) + # Add dependency to enforce implementation of all interface functions + add_dependencies(${TARGET_NAME} qdmi_test_mqt_sc_device_defs) + # Link further libraries + target_link_libraries(${TARGET_NAME} PRIVATE MQT::CoreQDMIScDeviceGen + nlohmann_json::nlohmann_json) + # Set the executable path and version information + target_compile_definitions( + ${TARGET_NAME} + PRIVATE EXECUTABLE_PATH="$" + MQT_CORE_VERSION="${MQT_CORE_VERSION}" + SC_DEVICE_JSON="${PROJECT_SOURCE_DIR}/json/sc/device.json") +endif() diff --git a/test/qdmi/sc/test_app.cpp b/test/qdmi/sc/test_app.cpp new file mode 100644 index 0000000000..cba5afb734 --- /dev/null +++ b/test/qdmi/sc/test_app.cpp @@ -0,0 +1,371 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define PLATFORM_POPEN _popen +#define PLATFORM_PCLOSE _pclose +#else +// The following are included via but the direct include are platform- +// specific, so ignore the corresponding warning for platform-agnostic includes. +// NOLINTBEGIN(misc-include-cleaner) +#define PLATFORM_POPEN popen +#define PLATFORM_PCLOSE pclose +// NOLINTEND(misc-include-cleaner) +#endif // _WIN32 + +TEST(ExecutableTest, Version) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " --version"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + ASSERT_EQ(returnCode, 0) << "Executable failed with return code: " + << returnCode; + // Validate the output + EXPECT_EQ(output.str(), + // NOLINTNEXTLINE(misc-include-cleaner) + "MQT QDMI SC Device Generator (MQT Version " MQT_CORE_VERSION + ")\n"); +} + +TEST(ExecutableTest, MissingSubcommand) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + EXPECT_NE(returnCode, 0); +} + +TEST(ExecutableTest, UnknownSubcommand) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " unknown"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + EXPECT_NE(returnCode, 0); +} + +TEST(ExecutableTest, SchemaUnknownOption) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " schema --unknown-option"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + EXPECT_NE(returnCode, 0); +} + +TEST(ExecutableTest, SchemaMissingFile) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " schema --output"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + EXPECT_NE(returnCode, 0); +} + +TEST(ExecutableTest, ValidateInvalidJson) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " validate"; + // Open a pipe to the executable + FILE* pipe = PLATFORM_POPEN(command.c_str(), "w"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Write the schema to the executable's stdin + fwrite("{", sizeof(char), 2, pipe); + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + EXPECT_NE(returnCode, 0) << "Executable failed with return code: " + << returnCode; +} + +TEST(ExecutableTest, GenerateMissingFile) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " generate --output"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + EXPECT_NE(returnCode, 0); +} + +TEST(ExecutableTest, Usage) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " --help"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + ASSERT_EQ(returnCode, 0) << "Executable failed with return code: " + << returnCode; + EXPECT_FALSE(output.str().empty()); +} + +TEST(ExecutableTest, SchemaUsage) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " schema --help"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + ASSERT_EQ(returnCode, 0) << "Executable failed with return code: " + << returnCode; + EXPECT_TRUE(output.str().rfind("Generates a JSON schema", 0) == 0); +} + +TEST(ExecutableTest, ValidateUsage) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " validate --help"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + ASSERT_EQ(returnCode, 0) << "Executable failed with return code: " + << returnCode; + EXPECT_TRUE(output.str().rfind("Validates", 0) == 0); +} + +TEST(ExecutableTest, GenerateUsage) { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " generate --help"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + ASSERT_EQ(returnCode, 0) << "Executable failed with return code: " + << returnCode; + EXPECT_TRUE(output.str().rfind("Generates a header file", 0) == 0); +} + +TEST(ExecutableTest, RoundTrip) { + std::string schema; + // Capture the output of the schema command + { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " schema"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + schema = output.str(); + // Print the captured output + std::cout << "Captured Output:\n" << schema << "\n"; + ASSERT_EQ(returnCode, 0) + << "Executable failed with return code: " << returnCode; + } + // Validate the output + { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " validate"; + // Open a pipe to the executable with write mode + FILE* pipe = PLATFORM_POPEN(command.c_str(), "w"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Write the schema to the executable's stdin + fwrite(schema.c_str(), sizeof(char), schema.size(), pipe); + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + ASSERT_EQ(returnCode, 0) + << "Executable failed with return code: " << returnCode; + } +} + +TEST(ExecutableTest, RoundTripFile) { + // Write schema to a file + { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " schema --output schema.json"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + ASSERT_EQ(returnCode, 0) + << "Executable failed with return code: " << returnCode; + } + // Validate the output + { + // Command to execute + // NOLINTNEXTLINE(misc-include-cleaner) + const std::string command = EXECUTABLE_PATH " validate schema.json"; + // Open a pipe to capture the output + FILE* pipe = PLATFORM_POPEN(command.c_str(), "r"); + ASSERT_NE(pipe, nullptr) << "Failed to open pipe"; + // Read the output + std::array buffer{}; + buffer.fill('\0'); + std::stringstream output; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output << buffer.data(); + } + // Close the pipe + const int returnCode = PLATFORM_PCLOSE(pipe); + // Print the captured output + std::cout << "Captured Output:\n" << output.str() << "\n"; + EXPECT_EQ(returnCode, 0) + << "Executable failed with return code: " << returnCode; + } + // Remove created file + std::remove("schema.json"); +} diff --git a/test/qdmi/sc/test_device.cpp b/test/qdmi/sc/test_device.cpp new file mode 100644 index 0000000000..be7d5d627b --- /dev/null +++ b/test/qdmi/sc/test_device.cpp @@ -0,0 +1,348 @@ +/* + * 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 + */ + +#include "mqt_sc_qdmi/device.h" +#include "qdmi/sc/Generator.hpp" + +#include +#include +#include +#include +#include +#include +#include +// NOLINTNEXTLINE(misc-include-cleaner) +#include +#include +#include +#include +#include +#include +#include + +namespace { +[[nodiscard]] auto querySites(MQT_SC_QDMI_Device_Session session) + -> std::vector { + size_t size = 0; + if (MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_SITES, 0, nullptr, &size) != + QDMI_SUCCESS) { + throw std::runtime_error("Failed to query sites"); + } + if (size == 0) { + throw std::runtime_error("No sites available"); + } + std::vector sites(size / sizeof(MQT_SC_QDMI_Site)); + if (MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_SITES, size, + static_cast(sites.data()), nullptr) != QDMI_SUCCESS) { + throw std::runtime_error("Failed to query sites"); + } + return sites; +} +} // namespace + +class QDMISpecificationTest : public ::testing::Test { +protected: + MQT_SC_QDMI_Device_Session session = nullptr; + + void SetUp() override { + ASSERT_EQ(MQT_SC_QDMI_device_initialize(), QDMI_SUCCESS) + << "Failed to initialize the device"; + + ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&session), QDMI_SUCCESS) + << "Failed to allocate a session"; + + ASSERT_EQ(MQT_SC_QDMI_device_session_init(session), QDMI_SUCCESS) + << "Failed to initialize a session. Potential errors: Wrong or missing " + "authentication information, device status is offline, or in " + "maintenance. To provide credentials, take a look in " __FILE__ + << (__LINE__ - 4); + } + + void TearDown() override { + if (session != nullptr) { + MQT_SC_QDMI_device_session_free(session); + session = nullptr; + } + MQT_SC_QDMI_device_finalize(); + } +}; + +class QDMIJobSpecificationTest : public QDMISpecificationTest { +protected: + MQT_SC_QDMI_Device_Job job = nullptr; + + void SetUp() override { + QDMISpecificationTest::SetUp(); + ASSERT_EQ(MQT_SC_QDMI_device_session_create_device_job(session, &job), + QDMI_SUCCESS) + << "Failed to create a device job."; + } + + void TearDown() override { + if (job != nullptr) { + MQT_SC_QDMI_device_job_free(job); + job = nullptr; + } + QDMISpecificationTest::TearDown(); + } +}; + +TEST_F(QDMISpecificationTest, SessionAlloc) { + EXPECT_EQ(MQT_SC_QDMI_device_session_alloc(nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMISpecificationTest, SessionInit) { + EXPECT_EQ(MQT_SC_QDMI_device_session_init(session), QDMI_ERROR_BADSTATE); + EXPECT_EQ(MQT_SC_QDMI_device_session_init(nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMISpecificationTest, SessionSetParameter) { + MQT_SC_QDMI_Device_Session uninitializedSession = nullptr; + ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&uninitializedSession), + QDMI_SUCCESS); + EXPECT_THAT(MQT_SC_QDMI_device_session_set_parameter( + uninitializedSession, QDMI_DEVICE_SESSION_PARAMETER_BASEURL, + 20, "https://example.com"), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED, + QDMI_ERROR_INVALIDARGUMENT)); + EXPECT_EQ(MQT_SC_QDMI_device_session_set_parameter( + session, QDMI_DEVICE_SESSION_PARAMETER_BASEURL, 20, + "https://example.com"), + QDMI_ERROR_BADSTATE); + EXPECT_EQ(MQT_SC_QDMI_device_session_set_parameter( + session, QDMI_DEVICE_SESSION_PARAMETER_MAX, 0, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMISpecificationTest, JobCreate) { + MQT_SC_QDMI_Device_Session uninitializedSession = nullptr; + MQT_SC_QDMI_Device_Job job = nullptr; + ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&uninitializedSession), + QDMI_SUCCESS); + EXPECT_EQ( + MQT_SC_QDMI_device_session_create_device_job(uninitializedSession, &job), + QDMI_ERROR_BADSTATE); + EXPECT_EQ(MQT_SC_QDMI_device_session_create_device_job(session, nullptr), + QDMI_ERROR_INVALIDARGUMENT); + EXPECT_EQ(MQT_SC_QDMI_device_session_create_device_job(nullptr, &job), + QDMI_ERROR_INVALIDARGUMENT); + EXPECT_THAT(MQT_SC_QDMI_device_session_create_device_job(session, &job), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); + MQT_SC_QDMI_device_job_free(job); +} + +TEST_F(QDMISpecificationTest, JobSetParameter) { + EXPECT_EQ(MQT_SC_QDMI_device_job_set_parameter( + nullptr, QDMI_DEVICE_JOB_PARAMETER_MAX, 0, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, JobSetParameter) { + QDMI_Program_Format value = QDMI_PROGRAM_FORMAT_QASM2; + EXPECT_THAT(MQT_SC_QDMI_device_job_set_parameter( + job, QDMI_DEVICE_JOB_PARAMETER_PROGRAMFORMAT, + sizeof(QDMI_Program_Format), &value), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); + EXPECT_EQ(MQT_SC_QDMI_device_job_set_parameter( + job, QDMI_DEVICE_JOB_PARAMETER_MAX, 0, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMISpecificationTest, JobQueryProperty) { + EXPECT_EQ(MQT_SC_QDMI_device_job_query_property( + nullptr, QDMI_DEVICE_JOB_PROPERTY_MAX, 0, nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, JobQueryProperty) { + EXPECT_THAT(MQT_SC_QDMI_device_job_query_property( + job, QDMI_DEVICE_JOB_PROPERTY_ID, 0, nullptr, nullptr), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); + EXPECT_EQ(MQT_SC_QDMI_device_job_query_property( + job, QDMI_DEVICE_JOB_PROPERTY_MAX, 0, nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, QueryJobId) { + size_t size = 0; + const auto status = MQT_SC_QDMI_device_job_query_property( + job, QDMI_DEVICE_JOB_PROPERTY_ID, 0, nullptr, &size); + ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); + if (status == QDMI_ERROR_NOTSUPPORTED) { + GTEST_SKIP() << "Job ID property is not supported by the device"; + } + ASSERT_GT(size, 0); + std::string id(size - 1, '\0'); + EXPECT_THAT(MQT_SC_QDMI_device_job_query_property( + job, QDMI_DEVICE_JOB_PROPERTY_ID, size, id.data(), nullptr), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); +} + +TEST_F(QDMISpecificationTest, JobSubmit) { + EXPECT_EQ(MQT_SC_QDMI_device_job_submit(nullptr), QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, JobSubmit) { + const auto status = MQT_SC_QDMI_device_job_submit(job); + ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); +} + +TEST_F(QDMISpecificationTest, JobCancel) { + EXPECT_EQ(MQT_SC_QDMI_device_job_cancel(nullptr), QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, JobCancel) { + const auto status = MQT_SC_QDMI_device_job_cancel(job); + ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_INVALIDARGUMENT, + QDMI_ERROR_NOTSUPPORTED)); +} + +TEST_F(QDMISpecificationTest, JobCheck) { + EXPECT_EQ(MQT_SC_QDMI_device_job_check(nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, JobCheck) { + QDMI_Job_Status jobStatus = QDMI_JOB_STATUS_RUNNING; + const auto status = MQT_SC_QDMI_device_job_check(job, &jobStatus); + ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); +} + +TEST_F(QDMISpecificationTest, JobWait) { + EXPECT_EQ(MQT_SC_QDMI_device_job_wait(nullptr, 0), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, JobWait) { + const auto status = MQT_SC_QDMI_device_job_wait(job, 1); + ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED, + QDMI_ERROR_TIMEOUT)); +} + +TEST_F(QDMISpecificationTest, JobGetResults) { + EXPECT_EQ(MQT_SC_QDMI_device_job_get_results(nullptr, QDMI_JOB_RESULT_MAX, 0, + nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMIJobSpecificationTest, JobGetResults) { + EXPECT_THAT(MQT_SC_QDMI_device_job_get_results(job, QDMI_JOB_RESULT_SHOTS, 0, + nullptr, nullptr), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); + EXPECT_EQ(MQT_SC_QDMI_device_job_get_results(job, QDMI_JOB_RESULT_MAX, 0, + nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); +} + +TEST_F(QDMISpecificationTest, QueryDeviceProperty) { + MQT_SC_QDMI_Device_Session uninitializedSession = nullptr; + ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&uninitializedSession), + QDMI_SUCCESS); + EXPECT_EQ( + MQT_SC_QDMI_device_session_query_device_property( + uninitializedSession, QDMI_DEVICE_PROPERTY_NAME, 0, nullptr, nullptr), + QDMI_ERROR_BADSTATE); + EXPECT_EQ(MQT_SC_QDMI_device_session_query_device_property( + nullptr, QDMI_DEVICE_PROPERTY_NAME, 0, nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); + EXPECT_EQ(MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_MAX, 0, nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); + EXPECT_THAT( + MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_COUPLINGMAP, 0, nullptr, nullptr), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); +} + +TEST_F(QDMISpecificationTest, QuerySiteProperty) { + MQT_SC_QDMI_Site site = querySites(session).front(); + EXPECT_EQ( + MQT_SC_QDMI_device_session_query_site_property( + session, nullptr, QDMI_SITE_PROPERTY_INDEX, 0, nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); + EXPECT_EQ(MQT_SC_QDMI_device_session_query_site_property( + nullptr, site, QDMI_SITE_PROPERTY_INDEX, 0, nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); + EXPECT_EQ(MQT_SC_QDMI_device_session_query_site_property( + session, site, QDMI_SITE_PROPERTY_MAX, 0, nullptr, nullptr), + QDMI_ERROR_INVALIDARGUMENT); + EXPECT_THAT(MQT_SC_QDMI_device_session_query_site_property( + session, site, QDMI_SITE_PROPERTY_NAME, 0, nullptr, nullptr), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); +} + +TEST_F(QDMISpecificationTest, QueryDeviceName) { + size_t size = 0; + ASSERT_EQ(MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_NAME, 0, nullptr, &size), + QDMI_SUCCESS) + << "Devices must provide a name"; + std::string value(size - 1, '\0'); + ASSERT_EQ( + MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_NAME, size, value.data(), nullptr), + QDMI_SUCCESS) + << "Devices must provide a name"; + EXPECT_FALSE(value.empty()) << "Devices must provide a name"; +} + +TEST_F(QDMISpecificationTest, QueryDeviceVersion) { + size_t size = 0; + ASSERT_EQ(MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_VERSION, 0, nullptr, &size), + QDMI_SUCCESS) + << "Devices must provide a version"; + std::string value(size - 1, '\0'); + ASSERT_EQ( + MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_VERSION, size, value.data(), nullptr), + QDMI_SUCCESS) + << "Devices must provide a version"; + EXPECT_FALSE(value.empty()) << "Devices must provide a version"; +} + +TEST_F(QDMISpecificationTest, QueryDeviceLibraryVersion) { + size_t size = 0; + ASSERT_EQ( + MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_LIBRARYVERSION, 0, nullptr, &size), + QDMI_SUCCESS) + << "Devices must provide a library version"; + std::string value(size - 1, '\0'); + ASSERT_EQ(MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_LIBRARYVERSION, size, + value.data(), nullptr), + QDMI_SUCCESS) + << "Devices must provide a library version"; + EXPECT_FALSE(value.empty()) << "Devices must provide a library version"; +} + +TEST_F(QDMISpecificationTest, QuerySiteIndex) { + size_t id = 0; + EXPECT_NO_THROW(for (auto* site : querySites(session)) { + EXPECT_EQ(MQT_SC_QDMI_device_session_query_site_property( + session, site, QDMI_SITE_PROPERTY_INDEX, sizeof(size_t), &id, + nullptr), + QDMI_SUCCESS) + << "Devices must provide a site id"; + }) << "Devices must provide a list of sites"; +} + +TEST_F(QDMISpecificationTest, QueryDeviceQubitNum) { + size_t numQubits = 0; + EXPECT_EQ(MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_QUBITSNUM, sizeof(size_t), + &numQubits, nullptr), + QDMI_SUCCESS); +} diff --git a/test/qdmi/sc/test_generator.cpp b/test/qdmi/sc/test_generator.cpp new file mode 100644 index 0000000000..1f25aaba14 --- /dev/null +++ b/test/qdmi/sc/test_generator.cpp @@ -0,0 +1,56 @@ +/* + * 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 + */ + +#include "qdmi/sc/Generator.hpp" + +#include +#include +// clang-tidy wants to include the forward header, but we need the full +// NOLINTNEXTLINE(misc-include-cleaner) +#include +#include +#include + +namespace qdmi::sc { +namespace { +// clang-tidy wants to include the forward header, but we have the full +// NOLINTNEXTLINE(misc-include-cleaner) +auto testPopulation(const nlohmann::json& json) -> void { + for (const auto& [key, value] : json.items()) { + if (value.is_array()) { + // Array field should have at least one default entry + EXPECT_GT(value.size(), 0) << "Array field '" << key + << "' should have at least one default entry"; + for (const auto& item : value) { + // Each entry in the array should not be null + EXPECT_FALSE(item.is_null()) + << "Array field '" << key << "' should not have null entries"; + testPopulation(item); + } + } else if (value.is_object()) { + testPopulation(value); + } + } +} +} // namespace + +TEST(GeneratorTest, WriteJSONSchema) { + std::ostringstream os; + EXPECT_NO_THROW(writeJSONSchema(os)); + // clang-tidy wants to include the forward header, but we have the full + // NOLINTNEXTLINE(misc-include-cleaner) + nlohmann::json json; + EXPECT_NO_THROW(json = nlohmann::json::parse(os.str())); + EXPECT_TRUE(json.is_object()); + EXPECT_GT(json.size(), 0); + testPopulation(json); +} + +} // namespace qdmi::sc diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index da0357226c..b9dac9695e 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -48,7 +48,9 @@ class DriverTest : public testing::TestWithParam { QDMI_Device device = nullptr; #ifndef _WIN32 static void SetUpTestSuite() { - qdmi::Driver::get().addDynamicDeviceLibrary(DYN_DEV_LIB, "MQT_NA_DYN"); + for (const auto& [lib, prefix] : DYN_DEV_LIBS) { + qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); + } } #endif // _WIN32 @@ -122,8 +124,9 @@ class DriverJobTest : public DriverTest { #ifndef _WIN32 TEST_P(DriverTest, LoadLibraryTwice) { - EXPECT_NO_THROW( - qdmi::Driver::get().addDynamicDeviceLibrary(DYN_DEV_LIB, "MQT_NA_DYN")); + EXPECT_NO_THROW(for (const auto& [lib, prefix] : DYN_DEV_LIBS) { + qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); + }); } #endif // _WIN32 From ab883c3696cd993d900dfa98aea65a850a91a6a2 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:33:52 +0100 Subject: [PATCH 03/58] =?UTF-8?q?=F0=9F=90=9B=20Fix=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Generator.hpp | 4 ++-- src/qdmi/sc/App.cpp | 18 +++++++++--------- src/qdmi/sc/Generator.cpp | 4 ++-- test/na/device/test_generator.cpp | 10 +++++----- test/qdmi/sc/test_generator.cpp | 6 +++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Generator.hpp b/include/mqt-core/qdmi/sc/Generator.hpp index d62339037e..ab162ba657 100644 --- a/include/mqt-core/qdmi/sc/Generator.hpp +++ b/include/mqt-core/qdmi/sc/Generator.hpp @@ -24,7 +24,7 @@ #include #include -namespace qdmi::sc { +namespace sc { /** * @brief Represents a superconducting device configuration. * @details This struct defines the schema for the JSON representation of a @@ -103,4 +103,4 @@ auto writeHeader(const Device& device, std::ostream& os) -> void; * qubits. */ auto writeHeader(const Device& device, const std::string& path) -> void; -} // namespace qdmi::sc +} // namespace sc diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index ceca2c0142..2b92b23658 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -294,9 +294,9 @@ auto executeSchemaCommand(const std::string& progName, // generate the JSON schema and write it to the output file or stdout try { if (schemaArgs.outputFile.has_value()) { - qdmi::sc::writeJSONSchema(schemaArgs.outputFile.value()); + sc::writeJSONSchema(schemaArgs.outputFile.value()); } else { - qdmi::sc::writeJSONSchema(std::cout); + sc::writeJSONSchema(std::cout); } } catch (const std::exception& e) { SPDLOG_ERROR("Error generating JSON schema: {}", e.what()); @@ -326,9 +326,9 @@ auto executeValidateCommand(const std::string& progName, // validate the JSON file or the JSON string from stdin try { if (validateArgs.jsonFile.has_value()) { - std::ignore = qdmi::sc::readJSON(validateArgs.jsonFile.value()); + std::ignore = sc::readJSON(validateArgs.jsonFile.value()); } else { - std::ignore = qdmi::sc::readJSON(std::cin); + std::ignore = sc::readJSON(std::cin); } } catch (const std::exception& e) { SPDLOG_ERROR("Error validating JSON: {}", e.what()); @@ -364,18 +364,18 @@ auto executeGenerateCommand(const std::string& progName, } // generate the header file from the JSON specification try { - qdmi::sc::Device device; + sc::Device device; // read the JSON file or the JSON string from stdin if (generateArgs.jsonFile.has_value()) { - device = qdmi::sc::readJSON(generateArgs.jsonFile.value()); + device = sc::readJSON(generateArgs.jsonFile.value()); } else { - device = qdmi::sc::readJSON(std::cin); + device = sc::readJSON(std::cin); } // write the header file to the output file or stdout if (generateArgs.outputFile.has_value()) { - qdmi::sc::writeHeader(device, generateArgs.outputFile.value()); + sc::writeHeader(device, generateArgs.outputFile.value()); } else { - qdmi::sc::writeHeader(device, std::cout); + sc::writeHeader(device, std::cout); } } catch (const std::exception& e) { SPDLOG_ERROR("Error generating header file: {}", e.what()); diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index ed5189d99b..2e0458e0b7 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -33,7 +33,7 @@ #include #include -namespace qdmi::sc { +namespace sc { namespace { /** * @brief Populates all array fields in the device object with default values. @@ -167,4 +167,4 @@ auto writeHeader(const Device& device, const std::string& path) -> void { ofs.close(); SPDLOG_INFO("Header file written to {}", path); } -} // namespace qdmi::sc +} // namespace sc diff --git a/test/na/device/test_generator.cpp b/test/na/device/test_generator.cpp index 157141666d..f66b5e9193 100644 --- a/test/na/device/test_generator.cpp +++ b/test/na/device/test_generator.cpp @@ -41,7 +41,7 @@ auto testPopulation(const nlohmann::json& json) -> void { } } // namespace -TEST(GeneratorTest, WriteJSONSchema) { +TEST(NaGeneratorTest, WriteJSONSchema) { std::ostringstream os; EXPECT_NO_THROW(writeJSONSchema(os)); // clang-tidy wants to include the forward header, but we have the full @@ -53,7 +53,7 @@ TEST(GeneratorTest, WriteJSONSchema) { testPopulation(json); } -TEST(GeneratorTest, DurationUnitNanosecond) { +TEST(NaGeneratorTest, DurationUnitNanosecond) { std::istringstream is(R"({ "durationUnit": { "scaleFactor": 5, @@ -66,7 +66,7 @@ TEST(GeneratorTest, DurationUnitNanosecond) { EXPECT_EQ(device.durationUnit.unit, "ns"); } -TEST(GeneratorTest, DurationUnitInvalid) { +TEST(NaGeneratorTest, DurationUnitInvalid) { std::istringstream is(R"({ "durationUnit": { "scaleFactor": 1, @@ -76,7 +76,7 @@ TEST(GeneratorTest, DurationUnitInvalid) { EXPECT_THROW(std::ignore = readJSON(is), std::runtime_error); } -TEST(GeneratorTest, LengthUnitNanometer) { +TEST(NaGeneratorTest, LengthUnitNanometer) { std::istringstream is(R"({ "lengthUnit": { "scaleFactor": 5, @@ -89,7 +89,7 @@ TEST(GeneratorTest, LengthUnitNanometer) { EXPECT_EQ(device.lengthUnit.unit, "nm"); } -TEST(GeneratorTest, LengthUnitInvalid) { +TEST(NaGeneratorTest, LengthUnitInvalid) { std::istringstream is(R"({ "lengthUnit": { "scaleFactor": 1, diff --git a/test/qdmi/sc/test_generator.cpp b/test/qdmi/sc/test_generator.cpp index 1f25aaba14..9f5421fb33 100644 --- a/test/qdmi/sc/test_generator.cpp +++ b/test/qdmi/sc/test_generator.cpp @@ -18,7 +18,7 @@ #include #include -namespace qdmi::sc { +namespace sc { namespace { // clang-tidy wants to include the forward header, but we have the full // NOLINTNEXTLINE(misc-include-cleaner) @@ -41,7 +41,7 @@ auto testPopulation(const nlohmann::json& json) -> void { } } // namespace -TEST(GeneratorTest, WriteJSONSchema) { +TEST(ScGeneratorTest, WriteJSONSchema) { std::ostringstream os; EXPECT_NO_THROW(writeJSONSchema(os)); // clang-tidy wants to include the forward header, but we have the full @@ -53,4 +53,4 @@ TEST(GeneratorTest, WriteJSONSchema) { testPopulation(json); } -} // namespace qdmi::sc +} // namespace sc From 8553ccba0e8d7c3f10d9676706615dd171b6f97c Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:57:15 +0100 Subject: [PATCH 04/58] =?UTF-8?q?=F0=9F=90=9B=20Fix=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/na/device/test_app.cpp | 26 ++++++------ test/na/device/test_device.cpp | 76 +++++++++++++++++----------------- test/qdmi/CMakeLists.txt | 2 +- test/qdmi/sc/test_app.cpp | 26 ++++++------ test/qdmi/sc/test_device.cpp | 60 +++++++++++++-------------- 5 files changed, 95 insertions(+), 95 deletions(-) diff --git a/test/na/device/test_app.cpp b/test/na/device/test_app.cpp index 23493955e7..dae2b8cf4c 100644 --- a/test/na/device/test_app.cpp +++ b/test/na/device/test_app.cpp @@ -27,7 +27,7 @@ // NOLINTEND(misc-include-cleaner) #endif // _WIN32 -TEST(ExecutableTest, Version) { +TEST(NaExecutableTest, Version) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " --version"; @@ -55,7 +55,7 @@ TEST(ExecutableTest, Version) { ")\n"); } -TEST(ExecutableTest, MissingSubcommand) { +TEST(NaExecutableTest, MissingSubcommand) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH; @@ -77,7 +77,7 @@ TEST(ExecutableTest, MissingSubcommand) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, UnknownSubcommand) { +TEST(NaExecutableTest, UnknownSubcommand) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " unknown"; @@ -99,7 +99,7 @@ TEST(ExecutableTest, UnknownSubcommand) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, SchemaUnknownOption) { +TEST(NaExecutableTest, SchemaUnknownOption) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " schema --unknown-option"; @@ -121,7 +121,7 @@ TEST(ExecutableTest, SchemaUnknownOption) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, SchemaMissingFile) { +TEST(NaExecutableTest, SchemaMissingFile) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " schema --output"; @@ -143,7 +143,7 @@ TEST(ExecutableTest, SchemaMissingFile) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, ValidateInvalidJson) { +TEST(NaExecutableTest, ValidateInvalidJson) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " validate"; @@ -158,7 +158,7 @@ TEST(ExecutableTest, ValidateInvalidJson) { << returnCode; } -TEST(ExecutableTest, GenerateMissingFile) { +TEST(NaExecutableTest, GenerateMissingFile) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " generate --output"; @@ -180,7 +180,7 @@ TEST(ExecutableTest, GenerateMissingFile) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, Usage) { +TEST(NaExecutableTest, Usage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " --help"; @@ -204,7 +204,7 @@ TEST(ExecutableTest, Usage) { EXPECT_FALSE(output.str().empty()); } -TEST(ExecutableTest, SchemaUsage) { +TEST(NaExecutableTest, SchemaUsage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " schema --help"; @@ -228,7 +228,7 @@ TEST(ExecutableTest, SchemaUsage) { EXPECT_TRUE(output.str().rfind("Generates a JSON schema", 0) == 0); } -TEST(ExecutableTest, ValidateUsage) { +TEST(NaExecutableTest, ValidateUsage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " validate --help"; @@ -252,7 +252,7 @@ TEST(ExecutableTest, ValidateUsage) { EXPECT_TRUE(output.str().rfind("Validates", 0) == 0); } -TEST(ExecutableTest, GenerateUsage) { +TEST(NaExecutableTest, GenerateUsage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " generate --help"; @@ -276,7 +276,7 @@ TEST(ExecutableTest, GenerateUsage) { EXPECT_TRUE(output.str().rfind("Generates a header file", 0) == 0); } -TEST(ExecutableTest, RoundTrip) { +TEST(NaExecutableTest, RoundTrip) { std::string schema; // Capture the output of the schema command { @@ -319,7 +319,7 @@ TEST(ExecutableTest, RoundTrip) { } } -TEST(ExecutableTest, RoundTripFile) { +TEST(NaExecutableTest, RoundTripFile) { // Write schema to a file { // Command to execute diff --git a/test/na/device/test_device.cpp b/test/na/device/test_device.cpp index 490b1609e0..abc07fa292 100644 --- a/test/na/device/test_device.cpp +++ b/test/na/device/test_device.cpp @@ -96,7 +96,7 @@ struct PairHash { } } // namespace -class QDMISpecificationTest : public ::testing::Test { +class NaQDMISpecificationTest : public ::testing::Test { protected: MQT_NA_QDMI_Device_Session session = nullptr; @@ -123,12 +123,12 @@ class QDMISpecificationTest : public ::testing::Test { } }; -class QDMIJobSpecificationTest : public QDMISpecificationTest { +class NaQDMIJobSpecificationTest : public NaQDMISpecificationTest { protected: MQT_NA_QDMI_Device_Job job = nullptr; void SetUp() override { - QDMISpecificationTest::SetUp(); + NaQDMISpecificationTest::SetUp(); ASSERT_EQ(MQT_NA_QDMI_device_session_create_device_job(session, &job), QDMI_SUCCESS) << "Failed to create a device job."; @@ -139,22 +139,22 @@ class QDMIJobSpecificationTest : public QDMISpecificationTest { MQT_NA_QDMI_device_job_free(job); job = nullptr; } - QDMISpecificationTest::TearDown(); + NaQDMISpecificationTest::TearDown(); } }; -TEST_F(QDMISpecificationTest, SessionAlloc) { +TEST_F(NaQDMISpecificationTest, SessionAlloc) { EXPECT_EQ(MQT_NA_QDMI_device_session_alloc(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, SessionInit) { +TEST_F(NaQDMISpecificationTest, SessionInit) { EXPECT_EQ(MQT_NA_QDMI_device_session_init(session), QDMI_ERROR_BADSTATE); EXPECT_EQ(MQT_NA_QDMI_device_session_init(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, SessionSetParameter) { +TEST_F(NaQDMISpecificationTest, SessionSetParameter) { MQT_NA_QDMI_Device_Session uninitializedSession = nullptr; ASSERT_EQ(MQT_NA_QDMI_device_session_alloc(&uninitializedSession), QDMI_SUCCESS); @@ -172,7 +172,7 @@ TEST_F(QDMISpecificationTest, SessionSetParameter) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, JobCreate) { +TEST_F(NaQDMISpecificationTest, JobCreate) { MQT_NA_QDMI_Device_Session uninitializedSession = nullptr; MQT_NA_QDMI_Device_Job job = nullptr; ASSERT_EQ(MQT_NA_QDMI_device_session_alloc(&uninitializedSession), @@ -189,13 +189,13 @@ TEST_F(QDMISpecificationTest, JobCreate) { MQT_NA_QDMI_device_job_free(job); } -TEST_F(QDMISpecificationTest, JobSetParameter) { +TEST_F(NaQDMISpecificationTest, JobSetParameter) { EXPECT_EQ(MQT_NA_QDMI_device_job_set_parameter( nullptr, QDMI_DEVICE_JOB_PARAMETER_MAX, 0, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobSetParameter) { +TEST_F(NaQDMIJobSpecificationTest, JobSetParameter) { QDMI_Program_Format value = QDMI_PROGRAM_FORMAT_QASM2; EXPECT_THAT(MQT_NA_QDMI_device_job_set_parameter( job, QDMI_DEVICE_JOB_PARAMETER_PROGRAMFORMAT, @@ -206,13 +206,13 @@ TEST_F(QDMIJobSpecificationTest, JobSetParameter) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, JobQueryProperty) { +TEST_F(NaQDMISpecificationTest, JobQueryProperty) { EXPECT_EQ(MQT_NA_QDMI_device_job_query_property( nullptr, QDMI_DEVICE_JOB_PROPERTY_MAX, 0, nullptr, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobQueryProperty) { +TEST_F(NaQDMIJobSpecificationTest, JobQueryProperty) { EXPECT_THAT(MQT_NA_QDMI_device_job_query_property( job, QDMI_DEVICE_JOB_PROPERTY_ID, 0, nullptr, nullptr), testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); @@ -221,7 +221,7 @@ TEST_F(QDMIJobSpecificationTest, JobQueryProperty) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, QueryJobId) { +TEST_F(NaQDMIJobSpecificationTest, QueryJobId) { size_t size = 0; const auto status = MQT_NA_QDMI_device_job_query_property( job, QDMI_DEVICE_JOB_PROPERTY_ID, 0, nullptr, &size); @@ -236,54 +236,54 @@ TEST_F(QDMIJobSpecificationTest, QueryJobId) { testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobSubmit) { +TEST_F(NaQDMISpecificationTest, JobSubmit) { EXPECT_EQ(MQT_NA_QDMI_device_job_submit(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobSubmit) { +TEST_F(NaQDMIJobSpecificationTest, JobSubmit) { const auto status = MQT_NA_QDMI_device_job_submit(job); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobCancel) { +TEST_F(NaQDMISpecificationTest, JobCancel) { EXPECT_EQ(MQT_NA_QDMI_device_job_cancel(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobCancel) { +TEST_F(NaQDMIJobSpecificationTest, JobCancel) { const auto status = MQT_NA_QDMI_device_job_cancel(job); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_INVALIDARGUMENT, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobCheck) { +TEST_F(NaQDMISpecificationTest, JobCheck) { EXPECT_EQ(MQT_NA_QDMI_device_job_check(nullptr, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobCheck) { +TEST_F(NaQDMIJobSpecificationTest, JobCheck) { QDMI_Job_Status jobStatus = QDMI_JOB_STATUS_RUNNING; const auto status = MQT_NA_QDMI_device_job_check(job, &jobStatus); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobWait) { +TEST_F(NaQDMISpecificationTest, JobWait) { EXPECT_EQ(MQT_NA_QDMI_device_job_wait(nullptr, 0), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobWait) { +TEST_F(NaQDMIJobSpecificationTest, JobWait) { const auto status = MQT_NA_QDMI_device_job_wait(job, 1); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED, QDMI_ERROR_TIMEOUT)); } -TEST_F(QDMISpecificationTest, JobGetResults) { +TEST_F(NaQDMISpecificationTest, JobGetResults) { EXPECT_EQ(MQT_NA_QDMI_device_job_get_results(nullptr, QDMI_JOB_RESULT_MAX, 0, nullptr, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobGetResults) { +TEST_F(NaQDMIJobSpecificationTest, JobGetResults) { EXPECT_THAT(MQT_NA_QDMI_device_job_get_results(job, QDMI_JOB_RESULT_SHOTS, 0, nullptr, nullptr), testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); @@ -292,7 +292,7 @@ TEST_F(QDMIJobSpecificationTest, JobGetResults) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, QueryDeviceProperty) { +TEST_F(NaQDMISpecificationTest, QueryDeviceProperty) { MQT_NA_QDMI_Device_Session uninitializedSession = nullptr; ASSERT_EQ(MQT_NA_QDMI_device_session_alloc(&uninitializedSession), QDMI_SUCCESS); @@ -312,7 +312,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceProperty) { testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, QuerySiteProperty) { +TEST_F(NaQDMISpecificationTest, QuerySiteProperty) { MQT_NA_QDMI_Site site = querySites(session).front(); EXPECT_EQ( MQT_NA_QDMI_device_session_query_site_property( @@ -329,7 +329,7 @@ TEST_F(QDMISpecificationTest, QuerySiteProperty) { testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, QueryOperationProperty) { +TEST_F(NaQDMISpecificationTest, QueryOperationProperty) { MQT_NA_QDMI_Operation operation = queryOperations(session).front(); EXPECT_EQ(MQT_NA_QDMI_device_session_query_operation_property( nullptr, operation, 0, nullptr, 0, nullptr, @@ -345,7 +345,7 @@ TEST_F(QDMISpecificationTest, QueryOperationProperty) { testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, QueryDeviceName) { +TEST_F(NaQDMISpecificationTest, QueryDeviceName) { size_t size = 0; ASSERT_EQ(MQT_NA_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_NAME, 0, nullptr, &size), @@ -360,7 +360,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceName) { EXPECT_FALSE(value.empty()) << "Devices must provide a name"; } -TEST_F(QDMISpecificationTest, QueryDeviceVersion) { +TEST_F(NaQDMISpecificationTest, QueryDeviceVersion) { size_t size = 0; ASSERT_EQ(MQT_NA_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_VERSION, 0, nullptr, &size), @@ -375,7 +375,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceVersion) { EXPECT_FALSE(value.empty()) << "Devices must provide a version"; } -TEST_F(QDMISpecificationTest, QueryDeviceLibraryVersion) { +TEST_F(NaQDMISpecificationTest, QueryDeviceLibraryVersion) { size_t size = 0; ASSERT_EQ( MQT_NA_QDMI_device_session_query_device_property( @@ -391,7 +391,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceLibraryVersion) { EXPECT_FALSE(value.empty()) << "Devices must provide a library version"; } -TEST_F(QDMISpecificationTest, QueryDeviceLengthUnit) { +TEST_F(NaQDMISpecificationTest, QueryDeviceLengthUnit) { size_t size = 0; ASSERT_EQ(MQT_NA_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_LENGTHUNIT, 0, nullptr, &size), @@ -412,7 +412,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceLengthUnit) { } } -TEST_F(QDMISpecificationTest, QueryDeviceDurationUnit) { +TEST_F(NaQDMISpecificationTest, QueryDeviceDurationUnit) { size_t size = 0; ASSERT_EQ(MQT_NA_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_DURATIONUNIT, 0, nullptr, &size), @@ -433,7 +433,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceDurationUnit) { } } -TEST_F(QDMISpecificationTest, QueryDeviceMinAtomDistance) { +TEST_F(NaQDMISpecificationTest, QueryDeviceMinAtomDistance) { uint64_t minAtomDistance = 0; EXPECT_EQ(MQT_NA_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_MINATOMDISTANCE, sizeof(uint64_t), @@ -441,7 +441,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceMinAtomDistance) { QDMI_SUCCESS); } -TEST_F(QDMISpecificationTest, QuerySiteIndex) { +TEST_F(NaQDMISpecificationTest, QuerySiteIndex) { size_t id = 0; EXPECT_NO_THROW(for (auto* site : querySites(session)) { EXPECT_EQ(MQT_NA_QDMI_device_session_query_site_property( @@ -452,7 +452,7 @@ TEST_F(QDMISpecificationTest, QuerySiteIndex) { }) << "Devices must provide a list of sites"; } -TEST_F(QDMISpecificationTest, QueryOperationName) { +TEST_F(NaQDMISpecificationTest, QueryOperationName) { size_t nameSize = 0; EXPECT_NO_THROW(for (auto* operation : queryOperations(session)) { EXPECT_EQ(MQT_NA_QDMI_device_session_query_operation_property( @@ -469,7 +469,7 @@ TEST_F(QDMISpecificationTest, QueryOperationName) { }) << "Devices must provide a list of operations"; } -TEST_F(QDMISpecificationTest, QueryDeviceQubitNum) { +TEST_F(NaQDMISpecificationTest, QueryDeviceQubitNum) { size_t numQubits = 0; EXPECT_EQ(MQT_NA_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_QUBITSNUM, sizeof(size_t), @@ -477,13 +477,13 @@ TEST_F(QDMISpecificationTest, QueryDeviceQubitNum) { QDMI_SUCCESS); } -class NADeviceTest : public QDMISpecificationTest { +class NADeviceTest : public NaQDMISpecificationTest { protected: // NOLINTNEXTLINE(misc-include-cleaner) na::Device device; void SetUp() override { - QDMISpecificationTest::SetUp(); + NaQDMISpecificationTest::SetUp(); // Open the file // NOLINTNEXTLINE(misc-include-cleaner) std::ifstream file(NA_DEVICE_JSON); @@ -498,7 +498,7 @@ class NADeviceTest : public QDMISpecificationTest { } } - void TearDown() override { QDMISpecificationTest::TearDown(); } + void TearDown() override { NaQDMISpecificationTest::TearDown(); } }; TEST_F(NADeviceTest, QuerySiteData) { diff --git a/test/qdmi/CMakeLists.txt b/test/qdmi/CMakeLists.txt index b2e3e30748..6934a23d54 100644 --- a/test/qdmi/CMakeLists.txt +++ b/test/qdmi/CMakeLists.txt @@ -14,7 +14,7 @@ set(TARGET_NAME mqt-core-qdmi-driver-test) if(TARGET MQT::CoreQDMIDriver) package_add_test(${TARGET_NAME} MQT::CoreQDMIDriver test_driver.cpp) if(NOT WIN32) - add_dependencies(${TARGET_NAME} MQT::CoreQDMINaDeviceDyn) + add_dependencies(${TARGET_NAME} MQT::CoreQDMINaDeviceDyn MQT::CoreQDMIScDeviceDyn) target_compile_definitions( ${TARGET_NAME} PRIVATE diff --git a/test/qdmi/sc/test_app.cpp b/test/qdmi/sc/test_app.cpp index cba5afb734..52aa7bd536 100644 --- a/test/qdmi/sc/test_app.cpp +++ b/test/qdmi/sc/test_app.cpp @@ -27,7 +27,7 @@ // NOLINTEND(misc-include-cleaner) #endif // _WIN32 -TEST(ExecutableTest, Version) { +TEST(ScExecutableTest, Version) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " --version"; @@ -55,7 +55,7 @@ TEST(ExecutableTest, Version) { ")\n"); } -TEST(ExecutableTest, MissingSubcommand) { +TEST(ScExecutableTest, MissingSubcommand) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH; @@ -77,7 +77,7 @@ TEST(ExecutableTest, MissingSubcommand) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, UnknownSubcommand) { +TEST(ScExecutableTest, UnknownSubcommand) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " unknown"; @@ -99,7 +99,7 @@ TEST(ExecutableTest, UnknownSubcommand) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, SchemaUnknownOption) { +TEST(ScExecutableTest, SchemaUnknownOption) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " schema --unknown-option"; @@ -121,7 +121,7 @@ TEST(ExecutableTest, SchemaUnknownOption) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, SchemaMissingFile) { +TEST(ScExecutableTest, SchemaMissingFile) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " schema --output"; @@ -143,7 +143,7 @@ TEST(ExecutableTest, SchemaMissingFile) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, ValidateInvalidJson) { +TEST(ScExecutableTest, ValidateInvalidJson) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " validate"; @@ -158,7 +158,7 @@ TEST(ExecutableTest, ValidateInvalidJson) { << returnCode; } -TEST(ExecutableTest, GenerateMissingFile) { +TEST(ScExecutableTest, GenerateMissingFile) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " generate --output"; @@ -180,7 +180,7 @@ TEST(ExecutableTest, GenerateMissingFile) { EXPECT_NE(returnCode, 0); } -TEST(ExecutableTest, Usage) { +TEST(ScExecutableTest, Usage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " --help"; @@ -204,7 +204,7 @@ TEST(ExecutableTest, Usage) { EXPECT_FALSE(output.str().empty()); } -TEST(ExecutableTest, SchemaUsage) { +TEST(ScExecutableTest, SchemaUsage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " schema --help"; @@ -228,7 +228,7 @@ TEST(ExecutableTest, SchemaUsage) { EXPECT_TRUE(output.str().rfind("Generates a JSON schema", 0) == 0); } -TEST(ExecutableTest, ValidateUsage) { +TEST(ScExecutableTest, ValidateUsage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " validate --help"; @@ -252,7 +252,7 @@ TEST(ExecutableTest, ValidateUsage) { EXPECT_TRUE(output.str().rfind("Validates", 0) == 0); } -TEST(ExecutableTest, GenerateUsage) { +TEST(ScExecutableTest, GenerateUsage) { // Command to execute // NOLINTNEXTLINE(misc-include-cleaner) const std::string command = EXECUTABLE_PATH " generate --help"; @@ -276,7 +276,7 @@ TEST(ExecutableTest, GenerateUsage) { EXPECT_TRUE(output.str().rfind("Generates a header file", 0) == 0); } -TEST(ExecutableTest, RoundTrip) { +TEST(ScExecutableTest, RoundTrip) { std::string schema; // Capture the output of the schema command { @@ -319,7 +319,7 @@ TEST(ExecutableTest, RoundTrip) { } } -TEST(ExecutableTest, RoundTripFile) { +TEST(ScExecutableTest, RoundTripFile) { // Write schema to a file { // Command to execute diff --git a/test/qdmi/sc/test_device.cpp b/test/qdmi/sc/test_device.cpp index be7d5d627b..068da4bde4 100644 --- a/test/qdmi/sc/test_device.cpp +++ b/test/qdmi/sc/test_device.cpp @@ -49,7 +49,7 @@ namespace { } } // namespace -class QDMISpecificationTest : public ::testing::Test { +class ScQDMISpecificationTest : public ::testing::Test { protected: MQT_SC_QDMI_Device_Session session = nullptr; @@ -76,12 +76,12 @@ class QDMISpecificationTest : public ::testing::Test { } }; -class QDMIJobSpecificationTest : public QDMISpecificationTest { +class ScQDMIJobSpecificationTest : public ScQDMISpecificationTest { protected: MQT_SC_QDMI_Device_Job job = nullptr; void SetUp() override { - QDMISpecificationTest::SetUp(); + ScQDMISpecificationTest::SetUp(); ASSERT_EQ(MQT_SC_QDMI_device_session_create_device_job(session, &job), QDMI_SUCCESS) << "Failed to create a device job."; @@ -92,22 +92,22 @@ class QDMIJobSpecificationTest : public QDMISpecificationTest { MQT_SC_QDMI_device_job_free(job); job = nullptr; } - QDMISpecificationTest::TearDown(); + ScQDMISpecificationTest::TearDown(); } }; -TEST_F(QDMISpecificationTest, SessionAlloc) { +TEST_F(ScQDMISpecificationTest, SessionAlloc) { EXPECT_EQ(MQT_SC_QDMI_device_session_alloc(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, SessionInit) { +TEST_F(ScQDMISpecificationTest, SessionInit) { EXPECT_EQ(MQT_SC_QDMI_device_session_init(session), QDMI_ERROR_BADSTATE); EXPECT_EQ(MQT_SC_QDMI_device_session_init(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, SessionSetParameter) { +TEST_F(ScQDMISpecificationTest, SessionSetParameter) { MQT_SC_QDMI_Device_Session uninitializedSession = nullptr; ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&uninitializedSession), QDMI_SUCCESS); @@ -125,7 +125,7 @@ TEST_F(QDMISpecificationTest, SessionSetParameter) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, JobCreate) { +TEST_F(ScQDMISpecificationTest, JobCreate) { MQT_SC_QDMI_Device_Session uninitializedSession = nullptr; MQT_SC_QDMI_Device_Job job = nullptr; ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&uninitializedSession), @@ -142,13 +142,13 @@ TEST_F(QDMISpecificationTest, JobCreate) { MQT_SC_QDMI_device_job_free(job); } -TEST_F(QDMISpecificationTest, JobSetParameter) { +TEST_F(ScQDMISpecificationTest, JobSetParameter) { EXPECT_EQ(MQT_SC_QDMI_device_job_set_parameter( nullptr, QDMI_DEVICE_JOB_PARAMETER_MAX, 0, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobSetParameter) { +TEST_F(ScQDMIJobSpecificationTest, JobSetParameter) { QDMI_Program_Format value = QDMI_PROGRAM_FORMAT_QASM2; EXPECT_THAT(MQT_SC_QDMI_device_job_set_parameter( job, QDMI_DEVICE_JOB_PARAMETER_PROGRAMFORMAT, @@ -159,13 +159,13 @@ TEST_F(QDMIJobSpecificationTest, JobSetParameter) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, JobQueryProperty) { +TEST_F(ScQDMISpecificationTest, JobQueryProperty) { EXPECT_EQ(MQT_SC_QDMI_device_job_query_property( nullptr, QDMI_DEVICE_JOB_PROPERTY_MAX, 0, nullptr, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobQueryProperty) { +TEST_F(ScQDMIJobSpecificationTest, JobQueryProperty) { EXPECT_THAT(MQT_SC_QDMI_device_job_query_property( job, QDMI_DEVICE_JOB_PROPERTY_ID, 0, nullptr, nullptr), testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); @@ -174,7 +174,7 @@ TEST_F(QDMIJobSpecificationTest, JobQueryProperty) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, QueryJobId) { +TEST_F(ScQDMIJobSpecificationTest, QueryJobId) { size_t size = 0; const auto status = MQT_SC_QDMI_device_job_query_property( job, QDMI_DEVICE_JOB_PROPERTY_ID, 0, nullptr, &size); @@ -189,54 +189,54 @@ TEST_F(QDMIJobSpecificationTest, QueryJobId) { testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobSubmit) { +TEST_F(ScQDMISpecificationTest, JobSubmit) { EXPECT_EQ(MQT_SC_QDMI_device_job_submit(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobSubmit) { +TEST_F(ScQDMIJobSpecificationTest, JobSubmit) { const auto status = MQT_SC_QDMI_device_job_submit(job); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobCancel) { +TEST_F(ScQDMISpecificationTest, JobCancel) { EXPECT_EQ(MQT_SC_QDMI_device_job_cancel(nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobCancel) { +TEST_F(ScQDMIJobSpecificationTest, JobCancel) { const auto status = MQT_SC_QDMI_device_job_cancel(job); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_INVALIDARGUMENT, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobCheck) { +TEST_F(ScQDMISpecificationTest, JobCheck) { EXPECT_EQ(MQT_SC_QDMI_device_job_check(nullptr, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobCheck) { +TEST_F(ScQDMIJobSpecificationTest, JobCheck) { QDMI_Job_Status jobStatus = QDMI_JOB_STATUS_RUNNING; const auto status = MQT_SC_QDMI_device_job_check(job, &jobStatus); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, JobWait) { +TEST_F(ScQDMISpecificationTest, JobWait) { EXPECT_EQ(MQT_SC_QDMI_device_job_wait(nullptr, 0), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobWait) { +TEST_F(ScQDMIJobSpecificationTest, JobWait) { const auto status = MQT_SC_QDMI_device_job_wait(job, 1); ASSERT_THAT(status, testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED, QDMI_ERROR_TIMEOUT)); } -TEST_F(QDMISpecificationTest, JobGetResults) { +TEST_F(ScQDMISpecificationTest, JobGetResults) { EXPECT_EQ(MQT_SC_QDMI_device_job_get_results(nullptr, QDMI_JOB_RESULT_MAX, 0, nullptr, nullptr), QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMIJobSpecificationTest, JobGetResults) { +TEST_F(ScQDMIJobSpecificationTest, JobGetResults) { EXPECT_THAT(MQT_SC_QDMI_device_job_get_results(job, QDMI_JOB_RESULT_SHOTS, 0, nullptr, nullptr), testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); @@ -245,7 +245,7 @@ TEST_F(QDMIJobSpecificationTest, JobGetResults) { QDMI_ERROR_INVALIDARGUMENT); } -TEST_F(QDMISpecificationTest, QueryDeviceProperty) { +TEST_F(ScQDMISpecificationTest, QueryDeviceProperty) { MQT_SC_QDMI_Device_Session uninitializedSession = nullptr; ASSERT_EQ(MQT_SC_QDMI_device_session_alloc(&uninitializedSession), QDMI_SUCCESS); @@ -265,7 +265,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceProperty) { testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, QuerySiteProperty) { +TEST_F(ScQDMISpecificationTest, QuerySiteProperty) { MQT_SC_QDMI_Site site = querySites(session).front(); EXPECT_EQ( MQT_SC_QDMI_device_session_query_site_property( @@ -282,7 +282,7 @@ TEST_F(QDMISpecificationTest, QuerySiteProperty) { testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); } -TEST_F(QDMISpecificationTest, QueryDeviceName) { +TEST_F(ScQDMISpecificationTest, QueryDeviceName) { size_t size = 0; ASSERT_EQ(MQT_SC_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_NAME, 0, nullptr, &size), @@ -297,7 +297,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceName) { EXPECT_FALSE(value.empty()) << "Devices must provide a name"; } -TEST_F(QDMISpecificationTest, QueryDeviceVersion) { +TEST_F(ScQDMISpecificationTest, QueryDeviceVersion) { size_t size = 0; ASSERT_EQ(MQT_SC_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_VERSION, 0, nullptr, &size), @@ -312,7 +312,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceVersion) { EXPECT_FALSE(value.empty()) << "Devices must provide a version"; } -TEST_F(QDMISpecificationTest, QueryDeviceLibraryVersion) { +TEST_F(ScQDMISpecificationTest, QueryDeviceLibraryVersion) { size_t size = 0; ASSERT_EQ( MQT_SC_QDMI_device_session_query_device_property( @@ -328,7 +328,7 @@ TEST_F(QDMISpecificationTest, QueryDeviceLibraryVersion) { EXPECT_FALSE(value.empty()) << "Devices must provide a library version"; } -TEST_F(QDMISpecificationTest, QuerySiteIndex) { +TEST_F(ScQDMISpecificationTest, QuerySiteIndex) { size_t id = 0; EXPECT_NO_THROW(for (auto* site : querySites(session)) { EXPECT_EQ(MQT_SC_QDMI_device_session_query_site_property( @@ -339,7 +339,7 @@ TEST_F(QDMISpecificationTest, QuerySiteIndex) { }) << "Devices must provide a list of sites"; } -TEST_F(QDMISpecificationTest, QueryDeviceQubitNum) { +TEST_F(ScQDMISpecificationTest, QueryDeviceQubitNum) { size_t numQubits = 0; EXPECT_EQ(MQT_SC_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_QUBITSNUM, sizeof(size_t), From 19e699bc150f42722f873ec4ae8ecee8799add96 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:14:29 +0100 Subject: [PATCH 05/58] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`y?= =?UTF-8?q?stade/qdmi-sc-device`=20(#1329)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @ystade. * https://github.com/munich-quantum-toolkit/core/pull/1328#issuecomment-3570353148 The following files were modified: * `include/mqt-core/qdmi/sc/Device.hpp` * `src/na/device/Device.cpp` * `src/qdmi/Driver.cpp` * `src/qdmi/sc/App.cpp` * `src/qdmi/sc/Device.cpp` * `src/qdmi/sc/DynDevice.cpp` * `src/qdmi/sc/Generator.cpp` * `test/na/device/test_app.cpp` * `test/na/device/test_device.cpp` * `test/na/device/test_generator.cpp` * `test/qdmi/sc/test_app.cpp` * `test/qdmi/sc/test_device.cpp` * `test/qdmi/sc/test_generator.cpp` * `test/qdmi/test_driver.cpp`
These file types are not supported * `json/sc/device.json` * `src/qdmi/CMakeLists.txt` * `src/qdmi/sc/CMakeLists.txt` * `test/qdmi/CMakeLists.txt` * `test/qdmi/sc/CMakeLists.txt`
ℹ️ Note
CodeRabbit cannot perform edits on its own pull requests yet.
Co-authored-by: Yannick Stade <100073938+ystade@users.noreply.github.com> --- include/mqt-core/qdmi/sc/Device.hpp | 19 ++++- src/qdmi/sc/App.cpp | 122 ++++++++++++++++++---------- src/qdmi/sc/Generator.cpp | 94 +++++++++++++++++---- 3 files changed, 175 insertions(+), 60 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index ec40497404..1bc7880437 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -54,7 +54,11 @@ class Device final { Device(const Device&) = delete; Device& operator=(const Device&) = delete; - /// @returns the singleton instance of the Device class. + /** + * @brief Accesses the singleton Device instance. + * + * @return Device& Reference to the singleton Device instance. + */ [[nodiscard]] static Device& get() { static Device instance; return instance; @@ -162,7 +166,12 @@ struct MQT_SC_QDMI_Device_Job_impl_d { MQT_SC_QDMI_Device_Session_impl_d* session_; public: - /// @brief Constructor for the MQT_SC_QDMI_Device_Job_impl_d. + /** + * @brief Initializes a device job implementation bound to the given session. + * + * @param session Pointer to the owning MQT_SC_QDMI_Device_Session_impl_d. The + * session must remain valid for the job's lifetime. + */ explicit MQT_SC_QDMI_Device_Job_impl_d( MQT_SC_QDMI_Device_Session_impl_d* session) : session_(session) {} @@ -231,7 +240,11 @@ struct MQT_SC_QDMI_Site_impl_d { private: uint64_t id_ = 0; ///< Unique identifier of the site - /// @brief Constructor for regular sites. + /** + * @brief Initializes a site implementation with the given unique identifier. + * + * @param id Unique identifier for the site. + */ MQT_SC_QDMI_Site_impl_d(uint64_t id) : id_(id) {} public: diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index 2b92b23658..9d5503d2e6 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -24,8 +24,9 @@ namespace { /** - * Prints the usage information for the command line tool. - * @param programName is the name of the program executable. + * @brief Writes usage information and available commands and options to stdout. + * + * @param programName Program executable name inserted into the Usage line. */ auto printUsage(const std::string& programName) -> void { std::cout @@ -111,7 +112,9 @@ auto printGenerateUsage(const std::string& programName) -> void { } /** - * Prints the version information for the command line tool. + * @brief Writes the tool's version string to standard output. + * + * Prints the program name and the embedded MQT core version to stdout. */ auto printVersion() -> void { // NOLINTNEXTLINE(misc-include-cleaner) @@ -159,11 +162,17 @@ struct GenerateArguments { }; /** - * Parses the command line arguments and returns an Arguments struct. - * @param args is the vector of command line arguments. - * @returns the parsed arguments as an Arguments struct and an index indicating - * the position of the first sub-command argument. - * @throws std::invalid_argument if the value after an option is missing. + * @brief Parse top-level command-line options and locate the chosen + * sub-command. + * + * @param args Vector of command-line tokens (typically argv converted to + * std::string), where args[0] is the program name. + * @return std::pair The first element is the parsed + * top-level Arguments; the second element is the index in `args` of the + * detected sub-command token (the first argument after top-level options). If + * no sub-command is present, the returned index will be `args.size() + 1`. + * @throws std::invalid_argument If an unknown top-level option or token is + * encountered. */ auto parseArguments(const std::vector& args) -> std::pair { @@ -194,11 +203,15 @@ auto parseArguments(const std::vector& args) } /** - * Parses the command line arguments for the schema command and returns a - * SchemaArguments struct. - * @param args is the vector of command line arguments. - * @param i is the index to the first sub-command argument within @p args - * @return Parsed schema arguments as a SchemaArguments struct. + * @brief Parse arguments for the "schema" sub-command. + * + * Parses options for the schema command and produces a SchemaArguments value + * describing whether help was requested and which output file (if any) was set. + * + * @param args Vector of all command-line arguments. + * @param i Index of the first argument belonging to the schema sub-command. + * @return SchemaArguments Struct with `help` set if help was requested and + * `outputFile` containing the path provided with `-o|--output`, if any. */ auto parseSchemaArguments(const std::vector& args, size_t i) -> SchemaArguments { @@ -220,11 +233,13 @@ auto parseSchemaArguments(const std::vector& args, size_t i) } /** - * Parses the command line arguments for the validate command and returns a - * ValidateArguments struct. - * @param args is the vector of command line arguments. - * @param i is the index to the first sub-command argument within @p args - * @return Parsed validate arguments as a ValidateArguments struct. + * @brief Parses arguments for the "validate" subcommand. + * + * @param args Vector of command-line arguments. + * @param i Index of the first validate subcommand argument within @p args. + * @return ValidateArguments Parsed flags and optional JSON input file path: + * `help` is set if -h/--help was present, `jsonFile` contains the positional + * JSON file if provided. */ auto parseValidateArguments(const std::vector& args, size_t i) -> ValidateArguments { @@ -241,11 +256,21 @@ auto parseValidateArguments(const std::vector& args, size_t i) } /** - * Parses the command line arguments for the generate command and returns a - * GenerateArguments struct. - * @param args is the vector of command line arguments. - * @param i is the index to the first sub-command argument within @p args - * @return Parsed generate arguments as a GenerateArguments struct. + * Parse arguments for the "generate" subcommand. + * + * Recognizes the following arguments: + * - `-h`, `--help`: sets the help flag. + * - `-o `, `--output `: sets the output header file path. + * - `` (positional): sets the input JSON file; if omitted, input is + * read from stdin. + * + * @param args Vector of command-line arguments. + * @param i Index of the first argument belonging to the subcommand within + * `args`. + * @return GenerateArguments Structure with `help`, optional `outputFile`, and + * optional `jsonFile` populated. + * @throws std::invalid_argument If an `-o`/`--output` option is provided + * without a following value. */ auto parseGenerateArguments(const std::vector& args, size_t i) -> GenerateArguments { @@ -306,12 +331,17 @@ auto executeSchemaCommand(const std::string& progName, } /** - * Executes the validate command, validating a JSON file or JSON string from - * stdin. - * @param progName is the name of the program executable. - * @param argVec is the vector of command line arguments. - * @param i is the index to the first sub-command argument within @p argVec - * @return 0 on success, 1 on error. + * @brief Run the "validate" subcommand to validate a JSON input. + * + * Parses validate-specific arguments, prints subcommand usage if the help + * flag is set, and validates JSON read from the provided file path or from + * standard input. + * + * @param progName Name of the program executable (used for usage output). + * @param argVec Full command-line argument vector. + * @param i Index of the first argument belonging to the validate subcommand. + * @return int `0` on successful validation or when help was printed, `1` on + * error. */ auto executeValidateCommand(const std::string& progName, const std::vector& argVec, @@ -338,12 +368,17 @@ auto executeValidateCommand(const std::string& progName, } /** - * Executes the generate command, generating a header file from a JSON file or - * JSON string from stdin. - * @param progName is the name of the program executable. - * @param argVec is the vector of command line arguments. - * @param i is the index to the first sub-command argument within @p argVec - * @return 0 on success, 1 on error. + * @brief Generates a C++ header from a device JSON specification (file or + * stdin). + * + * Parses generate-specific arguments from argVec starting at index i, reads a + * sc::Device from the specified JSON file or from stdin, and writes a header to + * the specified output file or to stdout. + * + * @param progName Program executable name (used for usage/help output). + * @param argVec Full command-line argument vector. + * @param i Index in argVec of the first generate sub-command argument. + * @return int 0 on success, 1 on error. */ auto executeGenerateCommand(const std::string& progName, const std::vector& argVec, @@ -386,15 +421,16 @@ auto executeGenerateCommand(const std::string& progName, } // namespace /** - * @brief Main function that parses command-line-arguments and processes the - * JSON. - * @details This function handles the command line arguments, checks for help - * and version flags, and processes the JSON file or schema file as specified by - * the user. Either a JSON file or a schema file must be provided. If no output - * file is specified, the JSON file is parsed but no header file is generated. + * @brief Parses command-line arguments, dispatches the selected subcommand + * (schema, validate, generate), and performs the requested operation. + * + * The function handles global flags (help, version), prints usage/version + * information when requested, and forwards remaining arguments to the + * appropriate subcommand executor which performs IO and error handling. * - * @param argc is the number of command line arguments. - * @param argv is the array of command line arguments. + * @param argc Number of command-line arguments. + * @param argv Array of command-line argument strings. + * @return int Exit code: `0` on success, `1` on error. */ int main(int argc, char* argv[]) { std::vector argVec; diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 2e0458e0b7..4aba2a5c3e 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -36,27 +36,32 @@ namespace sc { namespace { /** - * @brief Populates all array fields in the device object with default values. - * @param device is the device object to populate. - * @note This is a recursive auxiliary function used by @ref writeJSONSchema. + * @brief Ensure array fields of a Device contain default entries. + * + * Ensures arrays that must not be empty have default elements; in particular, + * appends a default (empty) coupling to device.couplings. + * + * @param device Device instance whose array fields will be populated. */ auto populateArrayFields(Device& device) -> void { device.couplings.emplace_back(); } /** - * @brief Writes the name from the device object. - * @param device is the device object containing the name. - * @param os is the output stream to write the name to. + * @brief Writes a C preprocessor macro that initializes a variable with the + * device's name. + * + * The macro emitted is `#define INITIALIZE_NAME(var) var = ""`. */ auto writeName(const Device& device, std::ostream& os) -> void { os << "#define INITIALIZE_NAME(var) var = \"" << device.name << "\"\n"; } /** - * @brief Writes the qubits number from the device object. - * @param device is the device object containing the number of qubits. - * @param os is the output stream to write the qubits number to. + * @brief Emits a C macro that initializes the device's qubit count. + * + * @param device Device whose `numQubits` value will be embedded in the macro. + * @param os Output stream to which the macro definition is written. */ auto writeQubitsNum(const Device& device, std::ostream& os) -> void { os << "#define INITIALIZE_QUBITSNUM(var) var = " << device.numQubits @@ -64,9 +69,16 @@ auto writeQubitsNum(const Device& device, std::ostream& os) -> void { } /** - * @brief Writes the sites from the device object. - * @param device is the device object containing the number of sites. - * @param os is the output stream to write the sites to. + * @brief Generates a C preprocessor macro that initializes the device's qubit + * sites and coupling map. + * + * Writes a macro `INITIALIZE_SITES(var)` which clears `var`, appends + * `numQubits` unique sites (by index), and constructs a `_couplings` vector + * reserved to the number of couplings and populated with pairs of site pointers + * corresponding to `device.couplings`. + * + * @param device Device containing `numQubits` and `couplings`. + * @param os Output stream to which the macro definition is written. */ auto writeSites(const Device& device, std::ostream& os) -> void { os << "#define INITIALIZE_SITES(var) var.clear()"; @@ -87,8 +99,12 @@ auto writeSites(const Device& device, std::ostream& os) -> void { } /** - * @brief Writes the sites from the device object. - * @param os is the output stream to write the sites to. + * @brief Emits a macro to initialize the device coupling map. + * + * Writes the C preprocessor macro `INITIALIZE_COUPLINGMAP(var)` which assigns + * `var = std::move(_couplings)`. + * + * @param os Output stream to write the macro definition to. */ auto writeCouplingMap(const Device& /* unused */, std::ostream& os) -> void { os << "#define INITIALIZE_COUPLINGMAP(var) var = std::move(_couplings)\n"; @@ -110,6 +126,16 @@ auto writeJSONSchema(std::ostream& os) -> void { os << json; } +/** + * @brief Write a default device JSON schema to the specified file path. + * + * Opens the file at `path` for writing and writes a JSON template representing + * a default Device configuration. The function closes the file on completion. + * + * @param path Filesystem path where the JSON template will be written. + * @throws std::runtime_error If the file at `path` cannot be opened for + * writing. + */ auto writeJSONSchema(const std::string& path) -> void { // Write to a file std::ofstream ofs(path); @@ -123,6 +149,16 @@ auto writeJSONSchema(const std::string& path) -> void { SPDLOG_INFO("JSON template written to {}", path); } +/** + * @brief Parses a Device configuration from an input stream containing JSON. + * + * Reads JSON from the provided input stream and converts it into a Device. + * + * @param is Input stream that supplies the JSON representation of the Device. + * @return Device Device constructed from the parsed JSON. + * @throws std::runtime_error If JSON parsing fails; the exception message + * contains parser error details. + */ [[nodiscard]] auto readJSON(std::istream& is) -> Device { // Read the device configuration from the input stream nlohmann::json json; @@ -137,6 +173,15 @@ auto writeJSONSchema(const std::string& path) -> void { return json; } +/** + * @brief Read a Device configuration from a JSON file. + * + * @param path Filesystem path to the JSON file containing the device + * configuration. + * @return Device Device parsed from the JSON file. + * @throws std::runtime_error If the file cannot be opened or if parsing the + * JSON fails. + */ [[nodiscard]] auto readJSON(const std::string& path) -> Device { // Read the device configuration from a JSON file std::ifstream ifs(path); @@ -148,6 +193,17 @@ auto writeJSONSchema(const std::string& path) -> void { return device; } +/** + * @brief Writes a C++ header snippet that initializes the provided Device as C + * macros. + * + * Writes macros defining the device name, qubit count, site initializers, and + * coupling map to the given output stream; the header begins with a pragma once + * guard. + * + * @param device Device to serialize into header macros. + * @param os Output stream to write the header content to. + */ auto writeHeader(const Device& device, std::ostream& os) -> void { os << "#pragma once\n\n"; writeName(device, os); @@ -156,6 +212,16 @@ auto writeHeader(const Device& device, std::ostream& os) -> void { writeCouplingMap(device, os); } +/** + * @brief Write a C++ header file that defines macros to initialize the given + * Device. + * + * @param device Device to serialize into initialization macros (name, qubit + * sites, coupling map). + * @param path Filesystem path where the header will be created/overwritten. + * @throws std::runtime_error if the file at `path` cannot be opened for + * writing. + */ auto writeHeader(const Device& device, const std::string& path) -> void { std::ofstream ofs(path); if (!ofs.good()) { From 39d47cb8f7a1da39298dbb8491f1220a0552ec6b Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:22:11 +0100 Subject: [PATCH 06/58] =?UTF-8?q?=F0=9F=93=9D=20Fix=20docstring=20in=20gen?= =?UTF-8?q?erator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Generator.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Generator.hpp b/include/mqt-core/qdmi/sc/Generator.hpp index ab162ba657..e123f36367 100644 --- a/include/mqt-core/qdmi/sc/Generator.hpp +++ b/include/mqt-core/qdmi/sc/Generator.hpp @@ -31,8 +31,6 @@ namespace sc { * superconducting device configuration. This struct, including all its * sub-structs, implements functions to serialize and deserialize to and from * JSON using the nlohmann::json library. - * @note All duration and length values are in multiples of the time unit and - * the length unit, respectively. */ struct Device { /// @brief The name of the device. @@ -68,7 +66,7 @@ auto writeJSONSchema(const std::string& path) -> void; * @brief Parses the device configuration from an input stream. * @param is is the input stream containing the JSON representation of the * device configuration. - * @returns The parsed device configuration as a Protobuf message. + * @returns The parsed device configuration as a @ref sc::Device object. * @throws std::runtime_error if the JSON cannot be parsed. */ [[nodiscard]] auto readJSON(std::istream& is) -> Device; @@ -76,7 +74,7 @@ auto writeJSONSchema(const std::string& path) -> void; /** * @brief Parses the device configuration from a JSON file. * @param path is the path to the JSON file containing the device configuration. - * @returns The parsed device configuration as a Protobuf message. + * @returns The parsed device configuration as a @ref sc::Device object. * @throws std::runtime_error if the JSON file does not exist, or the JSON file * cannot be parsed. */ @@ -85,7 +83,7 @@ auto writeJSONSchema(const std::string& path) -> void; /** * @brief Writes a header file with the device configuration to the specified * output stream. - * @param device is the protobuf representation of the device. + * @param device is a parsed and in-memory representation of the device. * @param os is the output stream to write the header file to. * @throws std::runtime_error if the file cannot be opened or written to. * @note This implementation only supports multi-qubit gates up to two @@ -96,7 +94,7 @@ auto writeHeader(const Device& device, std::ostream& os) -> void; /** * @brief Writes a header file with the device configuration to the specified * path. - * @param device is the protobuf representation of the device. + * @param device is a parsed and in-memory representation of the device. * @param path is the path to write the header file to. * @throws std::runtime_error if the file cannot be opened or written to. * @note This implementation only supports multi-qubit gates up to two From 66201712967e9cc55555f09342aca8c859444b56 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:39:12 +0100 Subject: [PATCH 07/58] =?UTF-8?q?=F0=9F=8E=A8=20WOrk=20on=20rabbit's=20sug?= =?UTF-8?q?gestions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/qdmi/sc/App.cpp | 2 +- src/qdmi/sc/Device.cpp | 13 +++++++------ src/qdmi/sc/DynDevice.cpp | 6 +++--- src/qdmi/sc/Generator.cpp | 8 +------- test/qdmi/sc/test_device.cpp | 9 ++++----- test/qdmi/test_driver.cpp | 5 +++-- 6 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index 9d5503d2e6..e0bfb507a1 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -96,7 +96,7 @@ auto printGenerateUsage(const std::string& programName) -> void { "\n" "Usage: " << programName - << " generate [options] \n" + << " generate [options] []\n" "\n" "Arguments:\n" " json_file the path to the JSON file to generate the\n" diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index f73a4f96be..e40965587a 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -17,7 +17,6 @@ #include "mqt_sc_qdmi/device.h" #include "qdmi/sc/DeviceMemberInitializers.hpp" -#include #include #include #include @@ -45,9 +44,10 @@ #ifdef _WIN32 #define STRNCPY(dest, src, size) \ - strncpy_s(static_cast(dest), size, src, size); + strncpy_s(static_cast(dest), size, src, (size) - 1); #else -#define STRNCPY(dest, src, size) strncpy(static_cast(dest), src, size); +#define STRNCPY(dest, src, size) \ + strncpy(static_cast(dest), src, (size) - 1); #endif #define ADD_STRING_PROPERTY(prop_name, prop_value, prop, size, value, \ @@ -176,7 +176,7 @@ auto MQT_SC_QDMI_Device_Session_impl_d::createDeviceJob( if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - if (status_ == Status::ALLOCATED) { + if (status_ != Status::INITIALIZED) { return QDMI_ERROR_BADSTATE; } auto uniqueJob = std::make_unique(this); @@ -286,9 +286,10 @@ auto MQT_SC_QDMI_Site_impl_d::queryProperty(const QDMI_Site_Property prop, } auto MQT_SC_QDMI_Operation_impl_d::makeUnique() -> std::unique_ptr { - MQT_SC_QDMI_Operation_impl_d op{}; - return std::make_unique(std::move(op)); + const MQT_SC_QDMI_Operation_impl_d op{}; + return std::make_unique(op); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Operation_impl_d::queryProperty( const size_t numSites, const MQT_SC_QDMI_Site* sites, const size_t numParams, const double* params, diff --git a/src/qdmi/sc/DynDevice.cpp b/src/qdmi/sc/DynDevice.cpp index 8427704dc9..91552f9c44 100644 --- a/src/qdmi/sc/DynDevice.cpp +++ b/src/qdmi/sc/DynDevice.cpp @@ -9,8 +9,8 @@ */ /** - * @file This file is a thin wrapper around MQT's Neutral Atom QDMI Device with - * another prefix. + * @file This file is a thin wrapper around MQT's Superconducting QDMI Device + * with another prefix. */ #include "mqt_sc_dyn_qdmi/device.h" @@ -121,7 +121,7 @@ int MQT_SC_DYN_QDMI_device_session_query_device_property( if (size < 27) { return QDMI_ERROR_INVALIDARGUMENT; } - strncpy(static_cast(value), "MQT NA Dynamic QDMI Device", size); + strncpy(static_cast(value), "MQT SC Dynamic QDMI Device", size); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) static_cast(value)[size - 1] = '\0'; } diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 4aba2a5c3e..202e0eb075 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -9,28 +9,22 @@ */ /** @file - * @brief The MQT QDMI device generator for neutral atom devices. + * @brief The MQT QDMI device generator for superconducting devices. */ #include "qdmi/sc/Generator.hpp" -#include #include #include -#include #include #include -#include #include #include #include -#include #include #include #include #include -#include -#include #include namespace sc { diff --git a/test/qdmi/sc/test_device.cpp b/test/qdmi/sc/test_device.cpp index 068da4bde4..3b4f4a834b 100644 --- a/test/qdmi/sc/test_device.cpp +++ b/test/qdmi/sc/test_device.cpp @@ -9,7 +9,6 @@ */ #include "mqt_sc_qdmi/device.h" -#include "qdmi/sc/Generator.hpp" #include #include @@ -140,6 +139,7 @@ TEST_F(ScQDMISpecificationTest, JobCreate) { EXPECT_THAT(MQT_SC_QDMI_device_session_create_device_job(session, &job), testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); MQT_SC_QDMI_device_job_free(job); + MQT_SC_QDMI_device_session_free(uninitializedSession); } TEST_F(ScQDMISpecificationTest, JobSetParameter) { @@ -259,10 +259,9 @@ TEST_F(ScQDMISpecificationTest, QueryDeviceProperty) { EXPECT_EQ(MQT_SC_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_MAX, 0, nullptr, nullptr), QDMI_ERROR_INVALIDARGUMENT); - EXPECT_THAT( - MQT_SC_QDMI_device_session_query_device_property( - session, QDMI_DEVICE_PROPERTY_COUPLINGMAP, 0, nullptr, nullptr), - testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); + EXPECT_EQ(MQT_SC_QDMI_device_session_query_device_property( + session, QDMI_DEVICE_PROPERTY_COUPLINGMAP, 0, nullptr, nullptr), + QDMI_SUCCESS); } TEST_F(ScQDMISpecificationTest, QuerySiteProperty) { diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index b9dac9695e..2efec3d947 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace testing { @@ -48,9 +49,9 @@ class DriverTest : public testing::TestWithParam { QDMI_Device device = nullptr; #ifndef _WIN32 static void SetUpTestSuite() { - for (const auto& [lib, prefix] : DYN_DEV_LIBS) { + EXPECT_NO_THROW(for (const auto& [lib, prefix] : DYN_DEV_LIBS) { qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); - } + }); } #endif // _WIN32 From 332c3e1149df628ba920d7cf78268fe688de9c4a Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:15:02 +0100 Subject: [PATCH 08/58] =?UTF-8?q?=F0=9F=8E=A8=20Add=20missing=20free=20for?= =?UTF-8?q?=20sessions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/qdmi/sc/test_device.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/qdmi/sc/test_device.cpp b/test/qdmi/sc/test_device.cpp index 3b4f4a834b..af742279b5 100644 --- a/test/qdmi/sc/test_device.cpp +++ b/test/qdmi/sc/test_device.cpp @@ -122,6 +122,7 @@ TEST_F(ScQDMISpecificationTest, SessionSetParameter) { EXPECT_EQ(MQT_SC_QDMI_device_session_set_parameter( session, QDMI_DEVICE_SESSION_PARAMETER_MAX, 0, nullptr), QDMI_ERROR_INVALIDARGUMENT); + MQT_SC_QDMI_device_session_free(uninitializedSession); } TEST_F(ScQDMISpecificationTest, JobCreate) { @@ -262,6 +263,7 @@ TEST_F(ScQDMISpecificationTest, QueryDeviceProperty) { EXPECT_EQ(MQT_SC_QDMI_device_session_query_device_property( session, QDMI_DEVICE_PROPERTY_COUPLINGMAP, 0, nullptr, nullptr), QDMI_SUCCESS); + MQT_SC_QDMI_device_session_free(uninitializedSession); } TEST_F(ScQDMISpecificationTest, QuerySiteProperty) { From 204ad85953663531037470f051195e3de2a581b0 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:36:40 +0100 Subject: [PATCH 09/58] =?UTF-8?q?=F0=9F=8E=A8=20Make=20memebr=20functions?= =?UTF-8?q?=20non=20static?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 28 +++++++++++++++------------- src/qdmi/sc/Device.cpp | 5 +++-- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index 1bc7880437..4664169fe3 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -16,6 +16,8 @@ #include "mqt_sc_qdmi/device.h" +#include +#include #include #include #include @@ -65,7 +67,7 @@ class Device final { } /// @brief Destructor for the Device class. - ~Device() = default; + ~Device(); /** * @brief Allocates a new device session. @@ -117,7 +119,7 @@ struct MQT_SC_QDMI_Device_Session_impl_d { * @see MQT_SC_QDMI_device_session_set_parameter */ auto setParameter(QDMI_Device_Session_Parameter param, size_t size, - const void* value) const -> int; + const void* value) -> int; /** * @brief Create a new device job. @@ -189,46 +191,46 @@ struct MQT_SC_QDMI_Device_Job_impl_d { * @brief Sets a parameter for the job. * @see MQT_SC_QDMI_device_job_set_parameter */ - static auto setParameter(QDMI_Device_Job_Parameter param, size_t size, - const void* value) -> int; + auto setParameter(QDMI_Device_Job_Parameter param, size_t size, + const void* value) -> int; /** * @brief Queries a property of the job. * @see MQT_SC_QDMI_device_job_query_property */ - static auto queryProperty(QDMI_Device_Job_Property prop, size_t size, - void* value, size_t* sizeRet) -> int; + auto queryProperty(QDMI_Device_Job_Property prop, size_t size, void* value, + size_t* sizeRet) -> int; /** * @brief Submits the job to the device. * @see MQT_SC_QDMI_device_job_submit */ - static auto submit() -> int; + auto submit() -> int; /** * @brief Cancels the job. * @see MQT_SC_QDMI_device_job_cancel */ - static auto cancel() -> int; + auto cancel() -> int; /** * @brief Checks the status of the job. * @see MQT_SC_QDMI_device_job_check */ - static auto check(QDMI_Job_Status* status) -> int; + auto check(QDMI_Job_Status* status) -> int; /** * @brief Waits for the job to complete but at most for the specified timeout. * @see MQT_SC_QDMI_device_job_wait */ - static auto wait(size_t timeout) -> int; + auto wait(size_t timeout) -> int; /** * @brief Gets the results of the job. * @see MQT_SC_QDMI_device_job_get_results */ - static auto getResults(QDMI_Job_Result result, size_t size, void* data, - [[maybe_unused]] size_t* sizeRet) -> int; + auto getResults(QDMI_Job_Result result, size_t size, void* data, + [[maybe_unused]] size_t* sizeRet) -> int; }; /** @@ -245,7 +247,7 @@ struct MQT_SC_QDMI_Site_impl_d { * * @param id Unique identifier for the site. */ - MQT_SC_QDMI_Site_impl_d(uint64_t id) : id_(id) {} + explicit MQT_SC_QDMI_Site_impl_d(uint64_t id) : id_(id) {} public: /// @brief Factory function for regular sites. diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index e40965587a..b2e519e8c0 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -99,6 +99,7 @@ Device::Device() { INITIALIZE_SITES(sites_); INITIALIZE_COUPLINGMAP(couplingMap_); } +Device::~Device() = default; auto Device::sessionAlloc(MQT_SC_QDMI_Device_Session* session) -> int { if (session == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; @@ -159,8 +160,8 @@ auto MQT_SC_QDMI_Device_Session_impl_d::init() -> int { return QDMI_SUCCESS; } auto MQT_SC_QDMI_Device_Session_impl_d::setParameter( - QDMI_Device_Session_Parameter param, const size_t size, - const void* value) const -> int { + QDMI_Device_Session_Parameter param, const size_t size, const void* value) + -> int { if ((value != nullptr && size == 0) || param >= QDMI_DEVICE_SESSION_PARAMETER_MAX) { return QDMI_ERROR_INVALIDARGUMENT; From 6e4500af5cf286a0e1dff64bd8bcbd2c9bc2401b Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:42:49 +0100 Subject: [PATCH 10/58] =?UTF-8?q?=F0=9F=8E=A8=20Work=20on=20more=20feedbac?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 4 ++-- src/qdmi/sc/Generator.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index 4664169fe3..d3f1e14b7d 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -234,7 +234,7 @@ struct MQT_SC_QDMI_Device_Job_impl_d { }; /** - * @brief Implementation of the MQT_SC_QDMI_Device_Site structure. + * @brief Implementation of the MQT_SC_QDMI_Site structure. */ struct MQT_SC_QDMI_Site_impl_d { friend MQT_SC_QDMI_Operation_impl_d; @@ -262,7 +262,7 @@ struct MQT_SC_QDMI_Site_impl_d { }; /** - * @brief Implementation of the MQT_SC_QDMI_Device_Operation structure. + * @brief Implementation of the MQT_SC_QDMI_Operation structure. */ struct MQT_SC_QDMI_Operation_impl_d { private: diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 202e0eb075..597698ff7c 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -182,7 +182,7 @@ auto writeJSONSchema(const std::string& path) -> void { if (!ifs.good()) { throw std::runtime_error("Failed to open JSON file: " + std::string(path)); } - const auto& device = readJSON(ifs); + auto device = readJSON(ifs); ifs.close(); return device; } From 9a9a734678e9972f2033e0b75dfab08654384b2f Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:52:25 +0100 Subject: [PATCH 11/58] =?UTF-8?q?=F0=9F=8E=A8=20Work=20on=20more=20commetn?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 6 +++--- src/qdmi/sc/App.cpp | 16 +++++++++++++--- src/qdmi/sc/Generator.cpp | 4 ++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index d3f1e14b7d..b079426e72 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -49,9 +49,9 @@ class Device final { Device(); public: - // Default move constructor and move assignment operator. - Device(Device&&) = default; - Device& operator=(Device&&) = default; + // Delete move constructor and move assignment operator. + Device(Device&&) = delete; + Device& operator=(Device&&) = delete; // Delete copy constructor and assignment operator to enforce singleton. Device(const Device&) = delete; Device& operator=(const Device&) = delete; diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index e0bfb507a1..30d265ba47 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -187,13 +187,15 @@ auto parseArguments(const std::vector& args) arguments.version = true; } else if (arg == "schema") { arguments.command = Command::Schema; - break; // No more arguments for schema command + break; // Stop top-level parsing; remaining args handled by schema parser } else if (arg == "validate") { arguments.command = Command::Validate; - break; // No more arguments for validate command + // Stop top-level parsing; remaining args handled by validate parser + break; } else if (arg == "generate") { arguments.command = Command::Generate; - break; // No more arguments for generate command + // Stop top-level parsing; remaining args handled by generate parser + break; } else { throw std::invalid_argument("Unknown argument: " + arg); } @@ -240,6 +242,7 @@ auto parseSchemaArguments(const std::vector& args, size_t i) * @return ValidateArguments Parsed flags and optional JSON input file path: * `help` is set if -h/--help was present, `jsonFile` contains the positional * JSON file if provided. + * @throws std::invalid_argument if multiple JSON files are specified. */ auto parseValidateArguments(const std::vector& args, size_t i) -> ValidateArguments { @@ -248,6 +251,9 @@ auto parseValidateArguments(const std::vector& args, size_t i) if (const std::string& arg = args.at(i); arg == "-h" || arg == "--help") { validateArgs.help = true; } else { + if (validateArgs.jsonFile.has_value()) { + throw std::invalid_argument("Multiple JSON files specified"); + } validateArgs.jsonFile = arg; } ++i; @@ -271,6 +277,7 @@ auto parseValidateArguments(const std::vector& args, size_t i) * optional `jsonFile` populated. * @throws std::invalid_argument If an `-o`/`--output` option is provided * without a following value. + * @throws std::invalid_argument if multiple JSON files are specified. */ auto parseGenerateArguments(const std::vector& args, size_t i) -> GenerateArguments { @@ -284,6 +291,9 @@ auto parseGenerateArguments(const std::vector& args, size_t i) } generateArgs.outputFile = args.at(i); } else { + if (generateArgs.jsonFile.has_value()) { + throw std::invalid_argument("Multiple JSON files specified"); + } generateArgs.jsonFile = arg; } ++i; diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 597698ff7c..0ba372f533 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -98,6 +98,10 @@ auto writeSites(const Device& device, std::ostream& os) -> void { * Writes the C preprocessor macro `INITIALIZE_COUPLINGMAP(var)` which assigns * `var = std::move(_couplings)`. * + * @note This macro depends on the `_couplings` variable created by + * the INITIALIZE_SITES macro from writeSites(). The macro + * INITIALIZE_SITES must be invoked before INITIALIZE_COUPLINGMAP. + * * @param os Output stream to write the macro definition to. */ auto writeCouplingMap(const Device& /* unused */, std::ostream& os) -> void { From 6c1b974af7b20c54148cc3cb9adf72c4db71f385 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Wed, 26 Nov 2025 08:45:21 +0100 Subject: [PATCH 12/58] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20more=20suggestions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Generator.hpp | 1 + src/qdmi/sc/App.cpp | 12 ++++++++++-- test/qdmi/sc/test_device.cpp | 2 -- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Generator.hpp b/include/mqt-core/qdmi/sc/Generator.hpp index e123f36367..88dcc00c8a 100644 --- a/include/mqt-core/qdmi/sc/Generator.hpp +++ b/include/mqt-core/qdmi/sc/Generator.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace sc { diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index 30d265ba47..56c78e1fa0 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -356,9 +356,17 @@ auto executeSchemaCommand(const std::string& progName, auto executeValidateCommand(const std::string& progName, const std::vector& argVec, const size_t i) -> int { + ValidateArguments validateArgs; // parse the rest of the command line arguments for the validate command - const ValidateArguments validateArgs = parseValidateArguments(argVec, i); - // + try { + validateArgs = parseValidateArguments(argVec, i); + } catch (const std::exception& e) { + SPDLOG_ERROR("Error parsing validate arguments: {}", e.what()); + printValidateUsage(progName); + return 1; + } + + // if the help flag is set, print the validate usage information and exit if (validateArgs.help) { printValidateUsage(progName); return 0; diff --git a/test/qdmi/sc/test_device.cpp b/test/qdmi/sc/test_device.cpp index af742279b5..dd457d4ae5 100644 --- a/test/qdmi/sc/test_device.cpp +++ b/test/qdmi/sc/test_device.cpp @@ -10,9 +10,7 @@ #include "mqt_sc_qdmi/device.h" -#include #include -#include #include #include #include From 9aa2ba8e5d4ce9f7de07349417e2bd47154229c7 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Wed, 26 Nov 2025 08:58:10 +0100 Subject: [PATCH 13/58] =?UTF-8?q?=F0=9F=8E=A8=20Adopt=20test=20suite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/fomac/test_fomac.cpp | 78 +++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index 3f12ea8720..d367726a01 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -29,16 +29,26 @@ class DeviceTest : public testing::TestWithParam { class SiteTest : public DeviceTest { protected: - FoMaC::Device::Site site; + std::optional site; - SiteTest() : site(device.getSites().front()) {} + void SetUp() override { + const auto& sites = device.getSites(); + ASSERT_FALSE(sites.empty()); + site = sites.front(); + } }; class OperationTest : public DeviceTest { protected: - FoMaC::Device::Operation operation; + std::optional operation; - OperationTest() : operation(device.getOperations().front()) {} + void SetUp() override { + const auto& operations = device.getOperations(); + if (operations.empty()) { + GTEST_SKIP(); + } + operation = operations.front(); + } }; TEST(FoMaCTest, StatusToString) { @@ -174,10 +184,6 @@ TEST_P(DeviceTest, Sites) { EXPECT_NO_THROW(EXPECT_FALSE(device.getSites().empty())); } -TEST_P(DeviceTest, Operations) { - EXPECT_NO_THROW(EXPECT_FALSE(device.getOperations().empty())); -} - TEST_P(DeviceTest, CouplingMap) { EXPECT_NO_THROW(std::ignore = device.getCouplingMap()); } @@ -206,91 +212,91 @@ TEST_P(DeviceTest, MinAtomDistance) { EXPECT_NO_THROW(std::ignore = device.getMinAtomDistance()); } -TEST_P(SiteTest, Index) { EXPECT_NO_THROW(std::ignore = site.getIndex()); } +TEST_P(SiteTest, Index) { EXPECT_NO_THROW(std::ignore = site->getIndex()); } -TEST_P(SiteTest, T1) { EXPECT_NO_THROW(std::ignore = site.getT1()); } +TEST_P(SiteTest, T1) { EXPECT_NO_THROW(std::ignore = site->getT1()); } -TEST_P(SiteTest, T2) { EXPECT_NO_THROW(std::ignore = site.getT2()); } +TEST_P(SiteTest, T2) { EXPECT_NO_THROW(std::ignore = site->getT2()); } -TEST_P(SiteTest, Name) { EXPECT_NO_THROW(std::ignore = site.getName()); } +TEST_P(SiteTest, Name) { EXPECT_NO_THROW(std::ignore = site->getName()); } TEST_P(SiteTest, XCoordinate) { - EXPECT_NO_THROW(std::ignore = site.getXCoordinate()); + EXPECT_NO_THROW(std::ignore = site->getXCoordinate()); } TEST_P(SiteTest, YCoordinate) { - EXPECT_NO_THROW(std::ignore = site.getYCoordinate()); + EXPECT_NO_THROW(std::ignore = site->getYCoordinate()); } TEST_P(SiteTest, ZCoordinate) { - EXPECT_NO_THROW(std::ignore = site.getZCoordinate()); + EXPECT_NO_THROW(std::ignore = site->getZCoordinate()); } -TEST_P(SiteTest, IsZone) { EXPECT_NO_THROW(std::ignore = site.isZone()); } +TEST_P(SiteTest, IsZone) { EXPECT_NO_THROW(std::ignore = site->isZone()); } -TEST_P(SiteTest, XExtent) { EXPECT_NO_THROW(std::ignore = site.getXExtent()); } +TEST_P(SiteTest, XExtent) { EXPECT_NO_THROW(std::ignore = site->getXExtent()); } -TEST_P(SiteTest, YExtent) { EXPECT_NO_THROW(std::ignore = site.getYExtent()); } +TEST_P(SiteTest, YExtent) { EXPECT_NO_THROW(std::ignore = site->getYExtent()); } -TEST_P(SiteTest, ZExtent) { EXPECT_NO_THROW(std::ignore = site.getZExtent()); } +TEST_P(SiteTest, ZExtent) { EXPECT_NO_THROW(std::ignore = site->getZExtent()); } TEST_P(SiteTest, ModuleIndex) { - EXPECT_NO_THROW(std::ignore = site.getModuleIndex()); + EXPECT_NO_THROW(std::ignore = site->getModuleIndex()); } TEST_P(SiteTest, SubmoduleIndex) { - EXPECT_NO_THROW(std::ignore = site.getSubmoduleIndex()); + EXPECT_NO_THROW(std::ignore = site->getSubmoduleIndex()); } TEST_P(OperationTest, Name) { - EXPECT_NO_THROW(EXPECT_FALSE(operation.getName().empty());); + EXPECT_NO_THROW(EXPECT_FALSE(operation->getName().empty());); } TEST_P(OperationTest, QubitsNum) { - EXPECT_NO_THROW(std::ignore = operation.getQubitsNum()); + EXPECT_NO_THROW(std::ignore = operation->getQubitsNum()); } TEST_P(OperationTest, ParametersNum) { - EXPECT_NO_THROW(std::ignore = operation.getParametersNum()); + EXPECT_NO_THROW(std::ignore = operation->getParametersNum()); } TEST_P(OperationTest, Duration) { - EXPECT_NO_THROW(std::ignore = operation.getDuration()); + EXPECT_NO_THROW(std::ignore = operation->getDuration()); } TEST_P(OperationTest, Fidelity) { - EXPECT_NO_THROW(std::ignore = operation.getFidelity()); + EXPECT_NO_THROW(std::ignore = operation->getFidelity()); } TEST_P(OperationTest, InteractionRadius) { - EXPECT_NO_THROW(std::ignore = operation.getInteractionRadius()); + EXPECT_NO_THROW(std::ignore = operation->getInteractionRadius()); } TEST_P(OperationTest, BlockingRadius) { - EXPECT_NO_THROW(std::ignore = operation.getBlockingRadius()); + EXPECT_NO_THROW(std::ignore = operation->getBlockingRadius()); } TEST_P(OperationTest, IdlingFidelity) { - EXPECT_NO_THROW(std::ignore = operation.getIdlingFidelity()); + EXPECT_NO_THROW(std::ignore = operation->getIdlingFidelity()); } TEST_P(OperationTest, oned) { - EXPECT_NO_THROW(std::ignore = operation.isZoned()); + EXPECT_NO_THROW(std::ignore = operation->isZoned()); } TEST_P(OperationTest, Sites) { - EXPECT_NO_THROW(std::ignore = operation.getSites()); + EXPECT_NO_THROW(std::ignore = operation->getSites()); } TEST_P(OperationTest, SitePairs) { - const auto sitePairs = operation.getSitePairs(); - const auto qubitsNum = operation.getQubitsNum(); - const auto isZonedOp = operation.isZoned(); + const auto sitePairs = operation->getSitePairs(); + const auto qubitsNum = operation->getQubitsNum(); + const auto isZonedOp = operation->isZoned(); if (!qubitsNum.has_value() || *qubitsNum != 2 || isZonedOp) { EXPECT_FALSE(sitePairs.has_value()); } else { - const auto sites = operation.getSites(); + const auto sites = operation->getSites(); if (!sites.has_value() || sites->empty() || sites->size() % 2 != 0) { EXPECT_FALSE(sitePairs.has_value()); } else { @@ -303,7 +309,7 @@ TEST_P(OperationTest, SitePairs) { } TEST_P(OperationTest, MeanShuttlingSpeed) { - EXPECT_NO_THROW(std::ignore = operation.getMeanShuttlingSpeed()); + EXPECT_NO_THROW(std::ignore = operation->getMeanShuttlingSpeed()); } TEST_P(DeviceTest, RegularSitesAndZones) { From 72faea08cfa123f309e5a17ba4d1373cce4bfd5c Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Wed, 26 Nov 2025 14:29:51 +0100 Subject: [PATCH 14/58] =?UTF-8?q?=F0=9F=8E=A8=20Accumulate=20a=20couple=20?= =?UTF-8?q?of=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/Driver.hpp | 19 +++++++++++++++---- src/qdmi/Driver.cpp | 2 ++ src/qdmi/sc/Device.cpp | 5 ++++- test/qdmi/test_driver.cpp | 15 ++++++++++----- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/include/mqt-core/qdmi/Driver.hpp b/include/mqt-core/qdmi/Driver.hpp index 2d0f7b778f..d78e53374d 100644 --- a/include/mqt-core/qdmi/Driver.hpp +++ b/include/mqt-core/qdmi/Driver.hpp @@ -408,11 +408,22 @@ class Driver final { #ifndef _WIN32 /** * @brief Adds a dynamic device library to the driver. - * @param libName is the path to the dynamic library to load. - * @param prefix is the prefix used for the device interface functions. - * @returns `true` if the library was successfully loaded, `false` - * if it was already loaded. + * @details This function attempts to load a dynamic library containing + * QDMI device interface functions. If the library is already loaded, the + * function returns `false`. Otherwise, it loads the library, initializes + * the device, and adds it to the list of devices. + * + * @param libName The path to the dynamic library to load. + * @param prefix The prefix used for the device interface functions in the + * library. + * @returns `true` if the library was successfully loaded, `false` if it was + * already loaded. + * * @note This function is only available on non-Windows platforms. + * + * @throws std::runtime_error If the library fails to load or the device + * cannot be initialized. + * @throws std::bad_alloc If memory allocation fails during the process. */ auto addDynamicDeviceLibrary(const std::string& libName, const std::string& prefix) -> bool; diff --git a/src/qdmi/Driver.cpp b/src/qdmi/Driver.cpp index 5f6f28bd2a..3438a48f17 100644 --- a/src/qdmi/Driver.cpp +++ b/src/qdmi/Driver.cpp @@ -359,6 +359,8 @@ auto Driver::addDynamicDeviceLibrary(const std::string& libName, // false. return false; } + // Re-throw other exception + throw; } return true; } diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index b2e519e8c0..d5e214b379 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -99,7 +99,10 @@ Device::Device() { INITIALIZE_SITES(sites_); INITIALIZE_COUPLINGMAP(couplingMap_); } -Device::~Device() = default; +Device::~Device() { + // Explicitly clear sessions before destruction to avoid spurious segfaults + sessions_.clear(); +} auto Device::sessionAlloc(MQT_SC_QDMI_Device_Session* session) -> int { if (session == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index 2efec3d947..239039872f 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -49,7 +49,7 @@ class DriverTest : public testing::TestWithParam { QDMI_Device device = nullptr; #ifndef _WIN32 static void SetUpTestSuite() { - EXPECT_NO_THROW(for (const auto& [lib, prefix] : DYN_DEV_LIBS) { + ASSERT_NO_THROW(for (const auto& [lib, prefix] : DYN_DEV_LIBS) { qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); }); } @@ -124,9 +124,10 @@ class DriverJobTest : public DriverTest { }; #ifndef _WIN32 -TEST_P(DriverTest, LoadLibraryTwice) { +TEST(DriverTest, LoadLibraryTwice) { EXPECT_NO_THROW(for (const auto& [lib, prefix] : DYN_DEV_LIBS) { qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); + EXPECT_FALSE(qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix)); }); } #endif // _WIN32 @@ -505,10 +506,14 @@ TEST_P(DriverTest, QueryNeedsCalibration) { EXPECT_THAT(needsCalibration, testing::AnyOf(0, 1)); } #ifdef _WIN32 -const std::array DEVICES{"MQT NA Default QDMI Device"}; +const std::array DEVICES{"MQT NA Default QDMI Device", + "MQT Core DDSIM QDMI Device", + "MQT SC Default QDMI Device"}; #else -const std::array DEVICES{"MQT NA Default QDMI Device", - "MQT NA Dynamic QDMI Device"}; +const std::array DEVICES{ + "MQT NA Default QDMI Device", "MQT NA Dynamic QDMI Device", + "MQT Core DDSIM QDMI Device", "MQT SC Default QDMI Device", + "MQT SC Dynamic QDMI Device"}; #endif // Instantiate the test suite with different parameters INSTANTIATE_TEST_SUITE_P( From ac0c32d69517b1250bde328df8c1db3f94952c41 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 03:06:22 +0000 Subject: [PATCH 15/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=91=A8=E2=80=8D?= =?UTF-8?q?=F0=9F=92=BB=20Update=20munich-quantum-toolkit/workflows=20acti?= =?UTF-8?q?on=20to=20v1.17.3=20(#1335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [munich-quantum-toolkit/workflows](https://redirect.github.com/munich-quantum-toolkit/workflows) | action | patch | `v1.17.2` -> `v1.17.3` | --- ### Release Notes
munich-quantum-toolkit/workflows (munich-quantum-toolkit/workflows) ### [`v1.17.3`](https://redirect.github.com/munich-quantum-toolkit/workflows/blob/HEAD/CHANGELOG.md#181---2025-04-04) [Compare Source](https://redirect.github.com/munich-quantum-toolkit/workflows/compare/v1.17.2...v1.17.3) *📚 Refer to the [GitHub Release Notes] for previous changelogs.* [unreleased]: https://redirect.github.com/munich-quantum-toolkit/workflows/compare/v1.17.3...HEAD [1.17.3]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.17.3 [1.17.2]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.17.2 [1.17.1]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.17.1 [1.17.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.17.0 [1.16.2]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.16.2 [1.16.1]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.16.1 [1.16.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.16.0 [1.15.1]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.15.1 [1.15.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.15.0 [1.14.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.14.0 [1.13.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.13.0 [1.12.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.12.0 [1.11.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.11.0 [1.10.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.10.0 [1.9.0]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.9.0 [1.8.1]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases/tag/v1.8.1 [#​255]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/255 [#​254]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/254 [#​247]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/247 [#​241]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/241 [#​206]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/206 [#​188]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/188 [#​184]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/184 [#​160]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/160 [#​157]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/157 [#​151]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/151 [#​150]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/150 [#​146]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/146 [#​142]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/142 [#​128]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/128 [#​126]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/126 [#​119]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/119 [#​114]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/114 [#​102]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/102 [#​100]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/100 [#​96]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/96 [#​95]: https://redirect.github.com/munich-quantum-toolkit/workflows/pull/95 [**@​burgholzer**]: https://redirect.github.com/burgholzer [**@​ystade**]: https://redirect.github.com/ystade [**@​denialhaag**]: https://redirect.github.com/denialhaag [**@​flowerthrower**]: https://redirect.github.com/flowerthrower [Keep a Changelog]: https://keepachangelog.com/en/1.1.0/ [Common Changelog]: https://common-changelog.org [Semantic Versioning]: https://semver.org/spec/v2.0.0.html [GitHub Release Notes]: https://redirect.github.com/munich-quantum-toolkit/workflows/releases
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/cd.yml | 4 ++-- .github/workflows/ci.yml | 30 +++++++++++++++--------------- .github/workflows/upstream.yml | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 3697f882e2..f14896b765 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -12,7 +12,7 @@ permissions: jobs: build-sdist: name: 🐍 Packaging - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 # Builds wheels on all supported platforms using cibuildwheel. # The wheels are uploaded as GitHub artifacts `dev-cibw-*` or `cibw-*`, depending on whether @@ -31,7 +31,7 @@ jobs: windows-2022, windows-11-arm, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 051af3bf7a..ca29d43c29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: change-detection: name: 🔍 Change - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 cpp-tests-ubuntu: name: 🇨‌ Test 🐧 @@ -30,7 +30,7 @@ jobs: - runs-on: ubuntu-24.04 compiler: gcc config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -50,7 +50,7 @@ jobs: - runs-on: macos-14 compiler: clang config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -71,7 +71,7 @@ jobs: - runs-on: windows-2022 compiler: msvc config: Debug - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -95,7 +95,7 @@ jobs: compiler: clang - runs-on: ubuntu-22.04-arm compiler: clang - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-ubuntu.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -118,7 +118,7 @@ jobs: compiler: gcc-14 - runs-on: macos-15 compiler: gcc-15 - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-macos.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -136,7 +136,7 @@ jobs: runs-on: [windows-2022, windows-2025, windows-11-arm] compiler: [msvc, clang] config: [Release] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-tests-windows.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} compiler: ${{ matrix.compiler }} @@ -146,7 +146,7 @@ jobs: name: 🇨‌ Coverage needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cpp-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-coverage.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-coverage.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 permissions: contents: read id-token: write @@ -155,7 +155,7 @@ jobs: name: 🇨‌ Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cpp-linter) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: cmake-args: -DBUILD_MQT_CORE_BENCHMARKS=ON -DBUILD_MQT_CORE_MLIR=ON -DBUILD_MQT_CORE_BINDINGS=ON clang-version: 21 @@ -180,7 +180,7 @@ jobs: macos-14, windows-2022, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} @@ -188,7 +188,7 @@ jobs: name: 🐍 Coverage needs: [change-detection, python-tests] if: fromJSON(needs.change-detection.outputs.run-python-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 permissions: contents: read id-token: write @@ -202,7 +202,7 @@ jobs: fail-fast: false matrix: runs-on: [ubuntu-22.04, ubuntu-22.04-arm, macos-15, windows-2025] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} @@ -210,7 +210,7 @@ jobs: name: 🐍 Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-python-tests) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: enable-ty: true @@ -218,7 +218,7 @@ jobs: name: 🚀 CD needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cd) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 build-wheel: name: 🚀 CD @@ -236,7 +236,7 @@ jobs: windows-2022, windows-11-arm, ] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} diff --git a/.github/workflows/upstream.yml b/.github/workflows/upstream.yml index f42f88218d..55ead7eece 100644 --- a/.github/workflows/upstream.yml +++ b/.github/workflows/upstream.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: runs-on: [ubuntu-24.04, macos-14, windows-2022] - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-tests.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-tests.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: runs-on: ${{ matrix.runs-on }} setup-z3: true @@ -28,7 +28,7 @@ jobs: name: Create issue on failure needs: qiskit-upstream-tests if: ${{ always() }} - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-issue.yml@ea4fccbb432596cbb6840a9608d6376b6b2c8ff7 # v1.17.2 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-issue.yml@dbcd4474154dda0794838207274a3bccd4550de0 # v1.17.3 with: tests-result: ${{ needs.qiskit-upstream-tests.result }} permissions: From d759783fee9cad5f78d1bf46defe4b67bcf89bdd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 06:17:43 +0100 Subject: [PATCH 16/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=91=A8=E2=80=8D?= =?UTF-8?q?=F0=9F=92=BB=20Pin=20munich-quantum-software/setup-mlir=20actio?= =?UTF-8?q?n=20to=202f40b6e=20(#1334)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [munich-quantum-software/setup-mlir](https://redirect.github.com/munich-quantum-software/setup-mlir) | action | pinDigest | -> `2f40b6e` | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/reusable-mlir-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-mlir-tests.yml b/.github/workflows/reusable-mlir-tests.yml index 44f34f5e9a..5875780a00 100644 --- a/.github/workflows/reusable-mlir-tests.yml +++ b/.github/workflows/reusable-mlir-tests.yml @@ -38,7 +38,7 @@ jobs: fetch-depth: 0 - name: Set up MLIR - uses: munich-quantum-software/setup-mlir@2025.11.25 + uses: munich-quantum-software/setup-mlir@2f40b6e56a9bda0f539d7a251dfc6de2dbfeeb14 # 2025.11.25 with: tag: 2025.11.25 From 8a991291526f5fecb4e7ef1ffcd43ddaeb7efb5f Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:49:35 +0100 Subject: [PATCH 17/58] =?UTF-8?q?=F0=9F=94=A7=20Streamline=20DX=20(#1337)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR includes some changes to streamline the DX across repositories. ## Checklist: - [x] The pull request only contains commits that are focused and relevant to this change. - [x] ~~I have added appropriate tests that cover the new/changed functionality.~~ - [x] ~~I have updated the documentation to reflect these changes.~~ - [x] ~~I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.~~ - [x] ~~I have added migration instructions to the upgrade guide (if needed).~~ - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --------- Signed-off-by: burgholzer Co-authored-by: burgholzer --- .github/workflows/ci.yml | 2 +- .pre-commit-config.yaml | 30 +++++++++++------------------- cmake/ExternalDependencies.cmake | 2 +- pyproject.toml | 3 +-- uv.lock | 30 +----------------------------- 5 files changed, 15 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca29d43c29..ab5a9142a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -162,7 +162,7 @@ jobs: build-project: true files-changed-only: true setup-python: true - install-pkgs: "pybind11==3.0.0" + install-pkgs: "pybind11==3.0.1" cpp-linter-extra-args: "-std=c++20" python-tests: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7f6e42d793..7708e08624 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,20 +14,6 @@ ci: skip: [mypy, ty-check] repos: - # Ensure uv lock file is up-to-date - - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.11 - hooks: - - id: uv-lock - - # Check for merge conflicts - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v6.0.0 - hooks: - - id: check-merge-conflict - args: ["--assume-in-merge"] - fail_fast: true - # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 @@ -36,6 +22,7 @@ repos: args: ["--maxkb=2048"] - id: check-case-conflict - id: check-vcs-permalinks + - id: check-merge-conflict - id: check-symlinks - id: check-json - id: check-toml @@ -43,7 +30,6 @@ repos: - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - - id: requirements-txt-fixer - id: trailing-whitespace # Clean jupyter notebooks @@ -72,6 +58,12 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal + # Ensure uv lock file is up-to-date + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.9.12 + hooks: + - id: uv-lock + # Python linting using ruff - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.6 @@ -96,10 +88,10 @@ repos: hooks: - id: ty-check name: ty check - entry: uvx ty check . - language: system - pass_filenames: false - always_run: true + entry: python -c "import subprocess; import sys; subprocess.run(['uv', 'sync', '--no-install-project']); sys.exit(subprocess.run(['uv', 'run', '--no-sync', 'ty', 'check']).returncode)" + language: unsupported + require_serial: true + types_or: [python, pyi, jupyter] # Also run Black on examples in the documentation - repo: https://github.com/adamchainz/blacken-docs diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index 9d0042448d..af6a48449d 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -25,7 +25,7 @@ if(BUILD_MQT_CORE_BINDINGS) message(STATUS "Python executable: ${Python_EXECUTABLE}") # add pybind11 library - find_package(pybind11 3.0.0 CONFIG REQUIRED) + find_package(pybind11 3.0.1 CONFIG REQUIRED) endif() set(JSON_VERSION diff --git a/pyproject.toml b/pyproject.toml index 6c1de33f83..3f4c255c73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -348,6 +348,5 @@ dev = [ {include-group = "test"}, "lit>=18.1.8", "nox>=2025.11.12", - "ruff>=0.14.6", - "ty>=0.0.1a27", + "ty==0.0.1a27", ] diff --git a/uv.lock b/uv.lock index e0b775b872..b2e525a941 100644 --- a/uv.lock +++ b/uv.lock @@ -1518,7 +1518,6 @@ dev = [ { name = "pytest-sugar" }, { name = "pytest-xdist" }, { name = "qiskit", extra = ["qasm3-import"] }, - { name = "ruff" }, { name = "scikit-build-core" }, { name = "setuptools-scm" }, { name = "ty" }, @@ -1573,10 +1572,9 @@ dev = [ { name = "pytest-sugar", specifier = ">=1.1.1" }, { name = "pytest-xdist", specifier = ">=3.8.0" }, { name = "qiskit", extras = ["qasm3-import"], specifier = ">=1.0.0" }, - { name = "ruff", specifier = ">=0.14.6" }, { name = "scikit-build-core", specifier = ">=0.11.6" }, { name = "setuptools-scm", specifier = ">=9.2.2" }, - { name = "ty", specifier = ">=0.0.1a27" }, + { name = "ty", specifier = "==0.0.1a27" }, ] docs = [ { name = "breathe", specifier = ">=4.36.0" }, @@ -2676,32 +2674,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/87/f4/09ffb3ebd0cbb9e2c7c9b84d252557ecf434cd71584ee1e32f66013824df/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f", size = 564054, upload-time = "2025-11-16T14:50:37.733Z" }, ] -[[package]] -name = "ruff" -version = "0.14.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, - { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, - { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, - { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, - { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, - { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, - { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, - { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, - { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, -] - [[package]] name = "rustworkx" version = "0.17.1" From 2e2d44058c90727f14f4d822a6937ba7bffbd442 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 10:19:19 +0000 Subject: [PATCH 18/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=AA=9D=20Update?= =?UTF-8?q?=20pre-commit=20hook=20astral-sh/ruff-pre-commit=20to=20v0.14.7?= =?UTF-8?q?=20(#1339)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit) | repository | patch | `v0.14.6` -> `v0.14.7` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit) ### [`v0.14.7`](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.14.6...v0.14.7) [Compare Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.14.6...v0.14.7)
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7708e08624..6bb0de2f2e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: # Python linting using ruff - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.6 + rev: v0.14.7 hooks: - id: ruff-check - id: ruff-format From 2f2bb5a707eca2b2fa981368a3a07cfa27ad3c4a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 10:39:43 +0000 Subject: [PATCH 19/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=AA=9D=20Update?= =?UTF-8?q?=20pre-commit=20hook=20astral-sh/uv-pre-commit=20to=20v0.9.13?= =?UTF-8?q?=20(#1340)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [astral-sh/uv-pre-commit](https://redirect.github.com/astral-sh/uv-pre-commit) | repository | patch | `0.9.12` -> `0.9.13` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
astral-sh/uv-pre-commit (astral-sh/uv-pre-commit) ### [`v0.9.13`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.9.13) [Compare Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.9.12...0.9.13) See:
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6bb0de2f2e..25f5fdb9c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,7 +60,7 @@ repos: # Ensure uv lock file is up-to-date - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.12 + rev: 0.9.13 hooks: - id: uv-lock From 080b81353a564e96e22cce9aac67afe1648d9d8c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 11:36:56 +0000 Subject: [PATCH 20/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=AA=9D=20Update?= =?UTF-8?q?=20pre-commit=20hook=20crate-ci/typos=20to=20v1.40.0=20(#1341)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [crate-ci/typos](https://redirect.github.com/crate-ci/typos) | repository | minor | `v1.39.2` -> `v1.40.0` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
crate-ci/typos (crate-ci/typos) ### [`v1.40.0`](https://redirect.github.com/crate-ci/typos/blob/HEAD/CHANGELOG.md#014---2019-11-03) [Compare Source](https://redirect.github.com/crate-ci/typos/compare/v1.39.2...v1.40.0) ##### Bug Fixes - Ignore numbers as identifiers ([a00831c8](https://redirect.github.com/crate-ci/typos/commit/a00831c847b7efd81be520ea9b5d02f70555351f)) - Improve the organization of --help ([a48a457c](https://redirect.github.com/crate-ci/typos/commit/a48a457cc3ca817850118e2a2fb8b20fecdd40b8)) ##### Features - Dump files, identifiers, and words ([ce365ae1](https://redirect.github.com/crate-ci/typos/commit/ce365ae12e12fddfb6fc42a7f1e5ea71834d6051), closes [#​41](https://redirect.github.com/crate-ci/typos/issues/41)) - Give control over allowed identifier characters for leading vs rest ([107308a6](https://redirect.github.com/crate-ci/typos/commit/107308a655a425eb593bf5e4928572c16e6a9bdd)) ##### Performance - Use standard identifier rules to avoid doing umber checks ([107308a6](https://redirect.github.com/crate-ci/typos/commit/107308a655a425eb593bf5e4928572c16e6a9bdd)) - Only do hex check if digits are in identifiers ([68cd36d0](https://redirect.github.com/crate-ci/typos/commit/68cd36d0de90226dbc9d31c2ce6d8bf6b69adb5c)) [Unreleased]: https://redirect.github.com/crate-ci/typos/compare/v1.40.0...HEAD [1.40.0]: https://redirect.github.com/crate-ci/typos/compare/v1.39.2...v1.40.0 [1.39.2]: https://redirect.github.com/crate-ci/typos/compare/v1.39.1...v1.39.2 [1.39.1]: https://redirect.github.com/crate-ci/typos/compare/v1.39.0...v1.39.1 [1.39.0]: https://redirect.github.com/crate-ci/typos/compare/v1.38.1...v1.39.0 [1.38.1]: https://redirect.github.com/crate-ci/typos/compare/v1.38.0...v1.38.1 [1.38.0]: https://redirect.github.com/crate-ci/typos/compare/v1.37.3...v1.38.0 [1.37.3]: https://redirect.github.com/crate-ci/typos/compare/v1.37.2...v1.37.3 [1.37.2]: https://redirect.github.com/crate-ci/typos/compare/v1.37.1...v1.37.2 [1.37.1]: https://redirect.github.com/crate-ci/typos/compare/v1.37.0...v1.37.1 [1.37.0]: https://redirect.github.com/crate-ci/typos/compare/v1.36.3...v1.37.0 [1.36.3]: https://redirect.github.com/crate-ci/typos/compare/v1.36.2...v1.36.3 [1.36.2]: https://redirect.github.com/crate-ci/typos/compare/v1.36.1...v1.36.2 [1.36.1]: https://redirect.github.com/crate-ci/typos/compare/v1.36.0...v1.36.1 [1.36.0]: https://redirect.github.com/crate-ci/typos/compare/v1.35.8...v1.36.0 [1.35.8]: https://redirect.github.com/crate-ci/typos/compare/v1.35.7...v1.35.8 [1.35.7]: https://redirect.github.com/crate-ci/typos/compare/v1.35.6...v1.35.7 [1.35.6]: https://redirect.github.com/crate-ci/typos/compare/v1.35.5...v1.35.6 [1.35.5]: https://redirect.github.com/crate-ci/typos/compare/v1.35.4...v1.35.5 [1.35.4]: https://redirect.github.com/crate-ci/typos/compare/v1.35.3...v1.35.4 [1.35.3]: https://redirect.github.com/crate-ci/typos/compare/v1.35.2...v1.35.3 [1.35.2]: https://redirect.github.com/crate-ci/typos/compare/v1.35.1...v1.35.2 [1.35.1]: https://redirect.github.com/crate-ci/typos/compare/v1.35.0...v1.35.1 [1.35.0]: https://redirect.github.com/crate-ci/typos/compare/v1.34.0...v1.35.0 [1.34.0]: https://redirect.github.com/crate-ci/typos/compare/v1.33.1...v1.34.0 [1.33.1]: https://redirect.github.com/crate-ci/typos/compare/v1.33.0...v1.33.1 [1.33.0]: https://redirect.github.com/crate-ci/typos/compare/v1.32.0...v1.33.0 [1.32.0]: https://redirect.github.com/crate-ci/typos/compare/v1.31.2...v1.32.0 [1.31.2]: https://redirect.github.com/crate-ci/typos/compare/v1.31.1...v1.31.2 [1.31.1]: https://redirect.github.com/crate-ci/typos/compare/v1.31.0...v1.31.1 [1.31.0]: https://redirect.github.com/crate-ci/typos/compare/v1.30.3...v1.31.0 [1.30.3]: https://redirect.github.com/crate-ci/typos/compare/v1.30.2...v1.30.3 [1.30.2]: https://redirect.github.com/crate-ci/typos/compare/v1.30.1...v1.30.2 [1.30.1]: https://redirect.github.com/crate-ci/typos/compare/v1.30.0...v1.30.1 [1.30.0]: https://redirect.github.com/crate-ci/typos/compare/v1.29.10...v1.30.0 [1.29.10]: https://redirect.github.com/crate-ci/typos/compare/v1.29.9...v1.29.10 [1.29.9]: https://redirect.github.com/crate-ci/typos/compare/v1.29.8...v1.29.9 [1.29.8]: https://redirect.github.com/crate-ci/typos/compare/v1.29.7...v1.29.8 [1.29.7]: https://redirect.github.com/crate-ci/typos/compare/v1.29.6...v1.29.7 [1.29.6]: https://redirect.github.com/crate-ci/typos/compare/v1.29.5...v1.29.6 [1.29.5]: https://redirect.github.com/crate-ci/typos/compare/v1.29.4...v1.29.5 [1.29.4]: https://redirect.github.com/crate-ci/typos/compare/v1.29.3...v1.29.4 [1.29.3]: https://redirect.github.com/crate-ci/typos/compare/v1.29.2...v1.29.3 [1.29.2]: https://redirect.github.com/crate-ci/typos/compare/v1.29.1...v1.29.2 [1.29.1]: https://redirect.github.com/crate-ci/typos/compare/v1.29.0...v1.29.1 [1.29.0]: https://redirect.github.com/crate-ci/typos/compare/v1.28.4...v1.29.0 [1.28.4]: https://redirect.github.com/crate-ci/typos/compare/v1.28.3...v1.28.4 [1.28.3]: https://redirect.github.com/crate-ci/typos/compare/v1.28.2...v1.28.3 [1.28.2]: https://redirect.github.com/crate-ci/typos/compare/v1.28.1...v1.28.2 [1.28.1]: https://redirect.github.com/crate-ci/typos/compare/v1.28.0...v1.28.1 [1.28.0]: https://redirect.github.com/crate-ci/typos/compare/v1.27.3...v1.28.0 [1.27.3]: https://redirect.github.com/crate-ci/typos/compare/v1.27.2...v1.27.3 [1.27.2]: https://redirect.github.com/crate-ci/typos/compare/v1.27.1...v1.27.2 [1.27.1]: https://redirect.github.com/crate-ci/typos/compare/v1.27.0...v1.27.1 [1.27.0]: https://redirect.github.com/crate-ci/typos/compare/v1.26.8...v1.27.0 [1.26.8]: https://redirect.github.com/crate-ci/typos/compare/v1.26.7...v1.26.8 [1.26.7]: https://redirect.github.com/crate-ci/typos/compare/v1.26.6...v1.26.7 [1.26.6]: https://redirect.github.com/crate-ci/typos/compare/v1.26.5...v1.26.6 [1.26.5]: https://redirect.github.com/crate-ci/typos/compare/v1.26.4...v1.26.5 [1.26.4]: https://redirect.github.com/crate-ci/typos/compare/v1.26.3...v1.26.4 [1.26.3]: https://redirect.github.com/crate-ci/typos/compare/v1.26.2...v1.26.3 [1.26.2]: https://redirect.github.com/crate-ci/typos/compare/v1.26.1...v1.26.2 [1.26.1]: https://redirect.github.com/crate-ci/typos/compare/v1.26.0...v1.26.1 [1.26.0]: https://redirect.github.com/crate-ci/typos/compare/v1.25.0...v1.26.0 [1.25.0]: https://redirect.github.com/crate-ci/typos/compare/v1.24.6...v1.25.0 [1.24.6]: https://redirect.github.com/crate-ci/typos/compare/v1.24.5...v1.24.6 [1.24.5]: https://redirect.github.com/crate-ci/typos/compare/v1.24.4...v1.24.5 [1.24.4]: https://redirect.github.com/crate-ci/typos/compare/v1.24.3...v1.24.4 [1.24.3]: https://redirect.github.com/crate-ci/typos/compare/v1.24.2...v1.24.3 [1.24.2]: https://redirect.github.com/crate-ci/typos/compare/v1.24.1...v1.24.2 [1.24.1]: https://redirect.github.com/crate-ci/typos/compare/v1.24.0...v1.24.1 [1.24.0]: https://redirect.github.com/crate-ci/typos/compare/v1.23.7...v1.24.0 [1.23.7]: https://redirect.github.com/crate-ci/typos/compare/v1.23.6...v1.23.7 [1.23.6]: https://redirect.github.com/crate-ci/typos/compare/v1.23.5...v1.23.6 [1.23.5]: https://redirect.github.com/crate-ci/typos/compare/v1.23.4...v1.23.5 [1.23.4]: https://redirect.github.com/crate-ci/typos/compare/v1.23.3...v1.23.4 [1.23.3]: https://redirect.github.com/crate-ci/typos/compare/v1.23.2...v1.23.3 [1.23.2]: https://redirect.github.com/crate-ci/typos/compare/v1.23.1...v1.23.2 [1.23.1]: https://redirect.github.com/crate-ci/typos/compare/v1.23.0...v1.23.1 [1.23.0]: https://redirect.github.com/crate-ci/typos/compare/v1.22.9...v1.23.0 [1.22.9]: https://redirect.github.com/crate-ci/typos/compare/v1.22.8...v1.22.9 [1.22.8]: https://redirect.github.com/crate-ci/typos/compare/v1.22.7...v1.22.8 [1.22.7]: https://redirect.github.com/crate-ci/typos/compare/v1.22.6...v1.22.7 [1.22.6]: https://redirect.github.com/crate-ci/typos/compare/v1.22.5...v1.22.6 [1.22.5]: https://redirect.github.com/crate-ci/typos/compare/v1.22.4...v1.22.5 [1.22.4]: https://redirect.github.com/crate-ci/typos/compare/v1.22.3...v1.22.4 [1.22.3]: https://redirect.github.com/crate-ci/typos/compare/v1.22.2...v1.22.3 [1.22.2]: https://redirect.github.com/crate-ci/typos/compare/v1.22.1...v1.22.2 [1.22.1]: https://redirect.github.com/crate-ci/typos/compare/v1.22.0...v1.22.1 [1.22.0]: https://redirect.github.com/crate-ci/typos/compare/v1.21.0...v1.22.0 [1.21.0]: https://redirect.github.com/crate-ci/typos/compare/v1.20.10...v1.21.0 [1.20.10]: https://redirect.github.com/crate-ci/typos/compare/v1.20.9...v1.20.10 [1.20.9]: https://redirect.github.com/crate-ci/typos/compare/v1.20.8...v1.20.9 [1.20.8]: https://redirect.github.com/crate-ci/typos/compare/v1.20.7...v1.20.8 [1.20.7]: https://redirect.github.com/crate-ci/typos/compare/v1.20.6...v1.20.7 [1.20.6]: https://redirect.github.com/crate-ci/typos/compare/v1.20.5...v1.20.6 [1.20.5]: https://redirect.github.com/crate-ci/typos/compare/v1.20.4...v1.20.5 [1.20.4]: https://redirect.github.com/crate-ci/typos/compare/v1.20.3...v1.20.4 [1.20.3]: https://redirect.github.com/crate-ci/typos/compare/v1.20.2...v1.20.3 [1.20.2]: https://redirect.github.com/crate-ci/typos/compare/v1.20.1...v1.20.2 [1.20.1]: https://redirect.github.com/crate-ci/typos/compare/v1.20.0...v1.20.1 [1.20.0]: https://redirect.github.com/crate-ci/typos/compare/v1.19.0...v1.20.0 [1.19.0]: https://redirect.github.com/crate-ci/typos/compare/v1.18.2...v1.19.0 [1.18.2]: https://redirect.github.com/crate-ci/typos/compare/v1.18.1...v1.18.2 [1.18.1]: https://redirect.github.com/crate-ci/typos/compare/v1.18.0...v1.18.1 [1.18.0]: https://redirect.github.com/crate-ci/typos/compare/v1.17.2...v1.18.0 [1.17.2]: https://redirect.github.com/crate-ci/typos/compare/v1.17.1...v1.17.2 [1.17.1]: https://redirect.github.com/crate-ci/typos/compare/v1.17.0...v1.17.1 [1.17.0]: https://redirect.github.com/crate-ci/typos/compare/v1.16.26...v1.17.0 [1.16.26]: https://redirect.github.com/crate-ci/typos/compare/v1.16.25...v1.16.26 [1.16.25]: https://redirect.github.com/crate-ci/typos/compare/v1.16.24...v1.16.25 [1.16.24]: https://redirect.github.com/crate-ci/typos/compare/v1.16.23...v1.16.24 [1.16.23]: https://redirect.github.com/crate-ci/typos/compare/v1.16.22...v1.16.23 [1.16.22]: https://redirect.github.com/crate-ci/typos/compare/v1.16.21...v1.16.22 [1.16.21]: https://redirect.github.com/crate-ci/typos/compare/v1.16.20...v1.16.21 [1.16.20]: https://redirect.github.com/crate-ci/typos/compare/v1.16.19...v1.16.20 [1.16.19]: https://redirect.github.com/crate-ci/typos/compare/v1.16.18...v1.16.19 [1.16.18]: https://redirect.github.com/crate-ci/typos/compare/v1.16.17...v1.16.18 [1.16.17]: https://redirect.github.com/crate-ci/typos/compare/v1.16.16...v1.16.17 [1.16.16]: https://redirect.github.com/crate-ci/typos/compare/v1.16.15...v1.16.16 [1.16.15]: https://redirect.github.com/crate-ci/typos/compare/v1.16.14...v1.16.15 [1.16.14]: https://redirect.github.com/crate-ci/typos/compare/v1.16.13...v1.16.14 [1.16.13]: https://redirect.github.com/crate-ci/typos/compare/v1.16.12...v1.16.13 [1.16.12]: https://redirect.github.com/crate-ci/typos/compare/v1.16.11...v1.16.12 [1.16.11]: https://redirect.github.com/crate-ci/typos/compare/v1.16.10...v1.16.11 [1.16.10]: https://redirect.github.com/crate-ci/typos/compare/v1.16.9...v1.16.10 [1.16.9]: https://redirect.github.com/crate-ci/typos/compare/v1.16.8...v1.16.9 [1.16.8]: https://redirect.github.com/crate-ci/typos/compare/v1.16.7...v1.16.8 [1.16.7]: https://redirect.github.com/crate-ci/typos/compare/v1.16.6...v1.16.7 [1.16.6]: https://redirect.github.com/crate-ci/typos/compare/v1.16.5...v1.16.6 [1.16.5]: https://redirect.github.com/crate-ci/typos/compare/v1.16.4...v1.16.5 [1.16.4]: https://redirect.github.com/crate-ci/typos/compare/v1.16.3...v1.16.4 [1.16.3]: https://redirect.github.com/crate-ci/typos/compare/v1.16.2...v1.16.3 [1.16.2]: https://redirect.github.com/crate-ci/typos/compare/v1.16.1...v1.16.2 [1.16.1]: https://redirect.github.com/crate-ci/typos/compare/v1.16.0...v1.16.1 [1.16.0]: https://redirect.github.com/crate-ci/typos/compare/v1.15.10...v1.16.0 [1.15.10]: https://redirect.github.com/crate-ci/typos/compare/v1.15.9...v1.15.10 [1.15.9]: https://redirect.github.com/crate-ci/typos/compare/v1.15.8...v1.15.9 [1.15.8]: https://redirect.github.com/crate-ci/typos/compare/v1.15.7...v1.15.8 [1.15.7]: https://redirect.github.com/crate-ci/typos/compare/v1.15.6...v1.15.7 [1.15.6]: https://redirect.github.com/crate-ci/typos/compare/v1.15.5...v1.15.6 [1.15.5]: https://redirect.github.com/crate-ci/typos/compare/v1.15.4...v1.15.5 [1.15.4]: https://redirect.github.com/crate-ci/typos/compare/v1.15.3...v1.15.4 [1.15.3]: https://redirect.github.com/crate-ci/typos/compare/v1.15.2...v1.15.3 [1.15.2]: https://redirect.github.com/crate-ci/typos/compare/v1.15.1...v1.15.2 [1.15.1]: https://redirect.github.com/crate-ci/typos/compare/v1.15.0...v1.15.1 [1.15.0]: https://redirect.github.com/crate-ci/typos/compare/v1.14.12...v1.15.0 [1.14.12]: https://redirect.github.com/crate-ci/typos/compare/v1.14.11...v1.14.12 [1.14.11]: https://redirect.github.com/crate-ci/typos/compare/v1.14.10...v1.14.11 [1.14.10]: https://redirect.github.com/crate-ci/typos/compare/v1.14.9...v1.14.10 [1.14.9]: https://redirect.github.com/crate-ci/typos/compare/v1.14.8...v1.14.9 [1.14.8]: https://redirect.github.com/crate-ci/typos/compare/v1.14.7...v1.14.8 [1.14.7]: https://redirect.github.com/crate-ci/typos/compare/v1.14.6...v1.14.7 [1.14.6]: https://redirect.github.com/crate-ci/typos/compare/v1.14.5...v1.14.6 [1.14.5]: https://redirect.github.com/crate-ci/typos/compare/v1.14.4...v1.14.5 [1.14.4]: https://redirect.github.com/crate-ci/typos/compare/v1.14.3...v1.14.4 [1.14.3]: https://redirect.github.com/crate-ci/typos/compare/v1.14.2...v1.14.3 [1.14.2]: https://redirect.github.com/crate-ci/typos/compare/v1.14.1...v1.14.2 [1.14.1]: https://redirect.github.com/crate-ci/typos/compare/v1.14.0...v1.14.1 [1.14.0]: https://redirect.github.com/crate-ci/typos/compare/v1.13.26...v1.14.0 [1.13.26]: https://redirect.github.com/crate-ci/typos/compare/v1.13.25...v1.13.26 [1.13.25]: https://redirect.github.com/crate-ci/typos/compare/v1.13.24...v1.13.25 [1.13.24]: https://redirect.github.com/crate-ci/typos/compare/v1.13.23...v1.13.24 [1.13.23]: https://redirect.github.com/crate-ci/typos/compare/v1.13.22...v1.13.23 [1.13.22]: https://redirect.github.com/crate-ci/typos/compare/v1.13.21...v1.13.22 [1.13.21]: https://redirect.github.com/crate-ci/typos/compare/v1.13.20...v1.13.21 [1.13.20]: https://redirect.github.com/crate-ci/typos/compare/v1.13.19...v1.13.20 [1.13.19]: https://redirect.github.com/crate-ci/typos/compare/v1.13.18...v1.13.19 [1.13.18]: https://redirect.github.com/crate-ci/typos/compare/v1.13.17...v1.13.18 [1.13.17]: https://redirect.github.com/crate-ci/typos/compare/v1.13.16...v1.13.17 [1.13.16]: https://redirect.github.com/crate-ci/typos/compare/v1.13.15...v1.13.16 [1.13.15]: https://redirect.github.com/crate-ci/typos/compare/v1.13.14...v1.13.15 [1.13.14]: https://redirect.github.com/crate-ci/typos/compare/v1.13.13...v1.13.14 [1.13.13]: https://redirect.github.com/crate-ci/typos/compare/v1.13.12...v1.13.13 [1.13.12]: https://redirect.github.com/crate-ci/typos/compare/v1.13.11...v1.13.12 [1.13.11]: https://redirect.github.com/crate-ci/typos/compare/v1.13.10...v1.13.11 [1.13.10]: https://redirect.github.com/crate-ci/typos/compare/v1.13.9...v1.13.10 [1.13.9]: https://redirect.github.com/crate-ci/typos/compare/v1.13.8...v1.13.9 [1.13.8]: https://redirect.github.com/crate-ci/typos/compare/v1.13.7...v1.13.8 [1.13.7]: https://redirect.github.com/crate-ci/typos/compare/v1.13.6...v1.13.7 [1.13.6]: https://redirect.github.com/crate-ci/typos/compare/v1.13.5...v1.13.6 [1.13.5]: https://redirect.github.com/crate-ci/typos/compare/v1.13.4...v1.13.5 [1.13.4]: https://redirect.github.com/crate-ci/typos/compare/v1.13.3...v1.13.4 [1.13.3]: https://redirect.github.com/crate-ci/typos/compare/v1.13.2...v1.13.3 [1.13.2]: https://redirect.github.com/crate-ci/typos/compare/v1.13.1...v1.13.2 [1.13.1]: https://redirect.github.com/crate-ci/typos/compare/v1.13.0...v1.13.1 [1.13.0]: https://redirect.github.com/crate-ci/typos/compare/v1.12.14...v1.13.0 [1.12.14]: https://redirect.github.com/crate-ci/typos/compare/v1.12.13...v1.12.14 [1.12.13]: https://redirect.github.com/crate-ci/typos/compare/v1.12.12...v1.12.13 [1.12.12]: https://redirect.github.com/crate-ci/typos/compare/v1.12.11...v1.12.12 [1.12.11]: https://redirect.github.com/crate-ci/typos/compare/v1.12.10...v1.12.11 [1.12.10]: https://redirect.github.com/crate-ci/typos/compare/v1.12.9...v1.12.10 [1.12.9]: https://redirect.github.com/crate-ci/typos/compare/v1.12.8...v1.12.9 [1.12.8]: https://redirect.github.com/crate-ci/typos/compare/v1.12.7...v1.12.8 [1.12.7]: https://redirect.github.com/crate-ci/typos/compare/v1.12.6...v1.12.7 [1.12.6]: https://redirect.github.com/crate-ci/typos/compare/v1.12.5...v1.12.6 [1.12.5]: https://redirect.github.com/crate-ci/typos/compare/v1.12.4...v1.12.5 [1.12.4]: https://redirect.github.com/crate-ci/typos/compare/v1.12.3...v1.12.4 [1.12.3]: https://redirect.github.com/crate-ci/typos/compare/v1.12.2...v1.12.3 [1.12.2]: https://redirect.github.com/crate-ci/typos/compare/v1.12.1...v1.12.2 [1.12.1]: https://redirect.github.com/crate-ci/typos/compare/v1.12.0...v1.12.1 [1.12.0]: https://redirect.github.com/crate-ci/typos/compare/v1.11.5...v1.12.0 [1.11.5]: https://redirect.github.com/crate-ci/typos/compare/v1.11.4...v1.11.5 [1.11.4]: https://redirect.github.com/crate-ci/typos/compare/v1.11.3...v1.11.4 [1.11.3]: https://redirect.github.com/crate-ci/typos/compare/v1.11.2...v1.11.3 [1.11.2]: https://redirect.github.com/crate-ci/typos/compare/v1.11.1...v1.11.2 [1.11.1]: https://redirect.github.com/crate-ci/typos/compare/v1.11.0...v1.11.1 [1.11.0]: https://redirect.github.com/crate-ci/typos/compare/v1.10.3...v1.11.0 [1.10.3]: https://redirect.github.com/crate-ci/typos/compare/v1.10.2...v1.10.3 [1.10.2]: https://redirect.github.com/crate-ci/typos/compare/v1.10.1...v1.10.2 [1.10.1]: https://redirect.github.com/crate-ci/typos/compare/v1.10.0...v1.10.1 [1.10.0]: https://redirect.github.com/crate-ci/typos/compare/v1.9.0...v1.10.0 [1.9.0]: https://redirect.github.com/crate-ci/typos/compare/v1.8.1...v1.9.0 [1.8.1]: https://redirect.github.com/crate-ci/typos/compare/v1.8.0...v1.8.1 [1.8.0]: https://redirect.github.com/crate-ci/typos/compare/v1.7.3...v1.8.0 [1.7.3]: https://redirect.github.com/crate-ci/typos/compare/v1.7.2...v1.7.3 [1.7.2]: https://redirect.github.com/crate-ci/typos/compare/v1.7.1...v1.7.2 [1.7.1]: https://redirect.github.com/crate-ci/typos/compare/v1.7.0...v1.7.1 [1.7.0]: https://redirect.github.com/crate-ci/typos/compare/v1.6.0...v1.7.0 [1.6.0]: https://redirect.github.com/crate-ci/typos/compare/v1.5.0...v1.6.0 [1.5.0]: https://redirect.github.com/crate-ci/typos/compare/v1.4.1...v1.5.0 [1.4.1]: https://redirect.github.com/crate-ci/typos/compare/v1.4.0...v1.4.1 [1.4.0]: https://redirect.github.com/crate-ci/typos/compare/v1.3.9...v1.4.0 [1.3.9]: https://redirect.github.com/crate-ci/typos/compare/v1.3.8...v1.3.9 [1.3.8]: https://redirect.github.com/crate-ci/typos/compare/v1.3.7...v1.3.8 [1.3.7]: https://redirect.github.com/crate-ci/typos/compare/v1.3.6...v1.3.7 [1.3.6]: https://redirect.github.com/crate-ci/typos/compare/v1.3.5...v1.3.6 [1.3.5]: https://redirect.github.com/crate-ci/typos/compare/v1.3.4...v1.3.5 [1.3.4]: https://redirect.github.com/crate-ci/typos/compare/v1.3.3...v1.3.4 [1.3.3]: https://redirect.github.com/crate-ci/typos/compare/v1.3.2...v1.3.3 [1.3.2]: https://redirect.github.com/crate-ci/typos/compare/v1.3.1...v1.3.2 [1.3.1]: https://redirect.github.com/crate-ci/typos/compare/v1.3.0...v1.3.1 [1.3.0]: https://redirect.github.com/crate-ci/typos/compare/v1.2.1...v1.3.0 [1.2.1]: https://redirect.github.com/crate-ci/typos/compare/v1.2.0...v1.2.1 [1.2.0]: https://redirect.github.com/crate-ci/typos/compare/v1.1.9...v1.2.0 [1.1.9]: https://redirect.github.com/crate-ci/typos/compare/v1.1.8...v1.1.9 [1.1.8]: https://redirect.github.com/crate-ci/typos/compare/v1.1.7...v1.1.8 [1.1.7]: https://redirect.github.com/crate-ci/typos/compare/v1.1.6...v1.1.7 [1.1.6]: https://redirect.github.com/crate-ci/typos/compare/v1.1.5...v1.1.6 [1.1.5]: https://redirect.github.com/crate-ci/typos/compare/v1.1.4...v1.1.5 [1.1.4]: https://redirect.github.com/crate-ci/typos/compare/v1.1.3...v1.1.4 [1.1.3]: https://redirect.github.com/crate-ci/typos/compare/v1.1.2...v1.1.3 [1.1.2]: https://redirect.github.com/crate-ci/typos/compare/v1.1.1...v1.1.2 [1.1.1]: https://redirect.github.com/crate-ci/typos/compare/v1.1.0...v1.1.1 [1.1.0]: https://redirect.github.com/crate-ci/typos/compare/v1.0.11...v1.1.0 [1.0.11]: https://redirect.github.com/crate-ci/typos/compare/v1.0.10...v1.0.11 [1.0.10]: https://redirect.github.com/crate-ci/typos/compare/v1.0.9...v1.0.10 [1.0.9]: https://redirect.github.com/crate-ci/typos/compare/v1.0.8...v1.0.9 [1.0.8]: https://redirect.github.com/crate-ci/typos/compare/v1.0.7...v1.0.8 [1.0.7]: https://redirect.github.com/crate-ci/typos/compare/v1.0.6...v1.0.7 [1.0.6]: https://redirect.github.com/crate-ci/typos/compare/v1.0.5...v1.0.6 [1.0.5]: https://redirect.github.com/crate-ci/typos/compare/v1.0.4...v1.0.5 [1.0.4]: https://redirect.github.com/crate-ci/typos/compare/v1.0.3...v1.0.4 [1.0.3]: https://redirect.github.com/crate-ci/typos/compare/v1.0.2...v1.0.3 [1.0.2]: https://redirect.github.com/crate-ci/typos/compare/v1.0.1...v1.0.2 [1.0.1]: https://redirect.github.com/crate-ci/typos/compare/v1.0.0...v1.0.1 [1.0.0]: https://redirect.github.com/crate-ci/typos/compare/v0.4.0...v1.0.0 [0.4.0]: https://redirect.github.com/crate-ci/typos/compare/v0.3.0...v0.4.0 [0.3.0]: https://redirect.github.com/crate-ci/typos/compare/v0.2.0...v0.3.0 [0.2.0]: https://redirect.github.com/crate-ci/typos/compare/v0.1.4...v0.2.0
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25f5fdb9c1..e5364ede86 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -131,7 +131,7 @@ repos: # Check for spelling - repo: https://github.com/crate-ci/typos - rev: v1.39.2 + rev: v1.40.0 hooks: - id: typos From aa441bc4416e654e933b18d11c863efecda5e72d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 21:28:32 +0000 Subject: [PATCH 21/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=AA=9D=20Update?= =?UTF-8?q?=20pre-commit=20hook=20emzeat/mz-lictools=20to=20v2.9.0=20(#134?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [emzeat/mz-lictools](https://redirect.github.com/emzeat/mz-lictools) | repository | minor | `v2.8.0` -> `v2.9.0` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
emzeat/mz-lictools (emzeat/mz-lictools) ### [`v2.9.0`](https://redirect.github.com/emzeat/mz-lictools/blob/HEAD/Changelog.md#v290) [Compare Source](https://redirect.github.com/emzeat/mz-lictools/compare/v2.8.0...v2.9.0) *29.11.2025* - Added support for Go text/template commenting style, used e.g. by Helm charts - Fixed issue causing relative config paths to get resolved incorrectly - Made sure to discover config for each file individually
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5364ede86..0a05b65be0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -102,7 +102,7 @@ repos: # Check for license headers - repo: https://github.com/emzeat/mz-lictools - rev: v2.8.0 + rev: v2.9.0 hooks: - id: license-tools From bde5874d817527be6f0c6597464a8d8011df5d05 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:57:09 +0000 Subject: [PATCH 22/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=AA=9D=20Update?= =?UTF-8?q?=20pre-commit=20hook=20rbubley/mirrors-prettier=20to=20v3.7.2?= =?UTF-8?q?=20(#1342)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [rbubley/mirrors-prettier](https://redirect.github.com/rbubley/mirrors-prettier) | repository | minor | `v3.6.2` -> `v3.7.2` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
rbubley/mirrors-prettier (rbubley/mirrors-prettier) ### [`v3.7.2`](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.7.1...v3.7.2) [Compare Source](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.7.1...v3.7.2) ### [`v3.7.1`](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.7.0...v3.7.1) [Compare Source](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.7.0...v3.7.1) ### [`v3.7.0`](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.6.2...v3.7.0) [Compare Source](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.6.2...v3.7.0)
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0a05b65be0..9503e06e1a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -124,7 +124,7 @@ repos: # Format configuration files with prettier - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.6.2 + rev: v3.7.2 hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json] From eac1c9ec82f4827716e07b5c56a6bcc2e1269cfd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 30 Nov 2025 04:13:54 +0000 Subject: [PATCH 23/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=AA=9D=20Update?= =?UTF-8?q?=20pre-commit=20hook=20pre-commit/mirrors-mypy=20to=20v1.19.0?= =?UTF-8?q?=20(#1343)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [pre-commit/mirrors-mypy](https://redirect.github.com/pre-commit/mirrors-mypy) | repository | minor | `v1.18.2` -> `v1.19.0` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
pre-commit/mirrors-mypy (pre-commit/mirrors-mypy) ### [`v1.19.0`](https://redirect.github.com/pre-commit/mirrors-mypy/compare/v1.18.2...v1.19.0) [Compare Source](https://redirect.github.com/pre-commit/mirrors-mypy/compare/v1.18.2...v1.19.0)
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9503e06e1a..7e856a1824 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -73,7 +73,7 @@ repos: # Static type checking using mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.18.2 + rev: v1.19.0 hooks: - id: mypy files: ^(python/mqt|test/python|noxfile.py) From 8c638ca270888a5cb50a4dc63fb751c7ca9c6ecf Mon Sep 17 00:00:00 2001 From: matthias Date: Sun, 30 Nov 2025 09:41:35 +0100 Subject: [PATCH 24/58] =?UTF-8?q?=E2=9C=A8=20Add=20`WireIterator`=20(#1310?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/reusable-mlir-tests.yml | 4 +- CHANGELOG.md | 5 + .../mlir/Dialect/MQTOpt/IR/WireIterator.h | 340 +++++++++++++++++ mlir/unittests/CMakeLists.txt | 1 + mlir/unittests/dialect/CMakeLists.txt | 21 ++ mlir/unittests/dialect/test_wireiterator.cpp | 354 ++++++++++++++++++ 6 files changed, 723 insertions(+), 2 deletions(-) create mode 100644 mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h create mode 100644 mlir/unittests/dialect/CMakeLists.txt create mode 100644 mlir/unittests/dialect/test_wireiterator.cpp diff --git a/.github/workflows/reusable-mlir-tests.yml b/.github/workflows/reusable-mlir-tests.yml index 5875780a00..02327778a7 100644 --- a/.github/workflows/reusable-mlir-tests.yml +++ b/.github/workflows/reusable-mlir-tests.yml @@ -83,8 +83,8 @@ jobs: - name: Build MLIR lit target run: cmake --build build --config ${{ matrix.coverage && 'Debug' || 'Release' }} --target mqt-core-mlir-lit-test-build-only - - name: Build MLIR unittests - run: cmake --build build --config ${{ matrix.coverage && 'Debug' || 'Release' }} --target mqt-core-mlir-translation-test + - name: Build MLIR translation unittests + run: cmake --build build --config ${{ matrix.coverage && 'Debug' || 'Release' }} --target mqt-core-mlir-translation-test --target mqt-core-mlir-wireiterator-test # Test - name: Run lit tests diff --git a/CHANGELOG.md b/CHANGELOG.md index dcf32d9932..6b478dc03c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ## [Unreleased] +### Added + +- ✨ Add bi-directional iterator that traverses the def-use chain of a qubit value ([#1310]) ([**@MatthiasReumann**]) + ### Changed - 👷 Use `munich-quantum-software/setup-mlir` to set up MLIR ([#1294]) ([**@denialhaag**]) @@ -250,6 +254,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1327]: https://github.com/munich-quantum-toolkit/core/pull/1327 +[#1310]: https://github.com/munich-quantum-toolkit/core/pull/1310 [#1300]: https://github.com/munich-quantum-toolkit/core/pull/1300 [#1299]: https://github.com/munich-quantum-toolkit/core/pull/1299 [#1294]: https://github.com/munich-quantum-toolkit/core/pull/1294 diff --git a/mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h b/mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h new file mode 100644 index 0000000000..79b664a31d --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h @@ -0,0 +1,340 @@ +/* + * 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 + */ + +#pragma once + +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mqt::ir::opt { + +/** + * @brief A bidirectional_iterator traversing the def-use chain of a qubit wire. + * + * The iterator follows the flow of a qubit through a sequence of quantum + * operations in a given region. It respects the semantics of the respective + * quantum operation including control flow constructs (scf::ForOp and + * scf::IfOp). + * + * It treats control flow constructs as a single operation that consumes and + * yields a corresponding number of qubits, without descending into their nested + * regions. + */ +class WireIterator { + /// @returns a view of all input qubits. + [[nodiscard]] static auto getAllInQubits(UnitaryInterface op) { + return llvm::concat(op.getInQubits(), op.getPosCtrlInQubits(), + op.getNegCtrlInQubits()); + } + + /// @returns a view of all output qubits. + [[nodiscard]] static auto getAllOutQubits(UnitaryInterface op) { + return llvm::concat( + op.getOutQubits(), op.getPosCtrlOutQubits(), op.getNegCtrlOutQubits()); + } + + /** + * @brief Find corresponding output from input value for a unitary (Forward). + * + * @note That we don't use the interface method here because + * it creates temporary std::vectors instead of using views. + */ + [[nodiscard]] static mlir::Value findOutput(UnitaryInterface op, + mlir::Value in) { + const auto ins = getAllInQubits(op); + const auto outs = getAllOutQubits(op); + const auto it = llvm::find(ins, in); + assert(it != ins.end() && "input qubit not found in operation"); + const auto index = std::distance(ins.begin(), it); + return *(std::next(outs.begin(), index)); + } + + /** + * @brief Find corresponding input from output value for a unitary (Backward). + * + * @note That we don't use the interface method here because + * it creates temporary std::vectors instead of using views. + */ + [[nodiscard]] static mlir::Value findInput(UnitaryInterface op, + mlir::Value out) { + const auto ins = getAllInQubits(op); + const auto outs = getAllOutQubits(op); + const auto it = llvm::find(outs, out); + assert(it != outs.end() && "output qubit not found in operation"); + const auto index = std::distance(outs.begin(), it); + return *(std::next(ins.begin(), index)); + } + + /** + * @brief Find corresponding result from init argument value (Forward). + */ + [[nodiscard]] static mlir::Value findResult(mlir::scf::ForOp op, + mlir::Value initArg) { + const auto initArgs = op.getInitArgs(); + const auto it = llvm::find(initArgs, initArg); + assert(it != initArgs.end() && "init arg qubit not found in operation"); + const auto index = std::distance(initArgs.begin(), it); + return op->getResult(index); + } + + /** + * @brief Find corresponding init argument from result value (Backward). + */ + [[nodiscard]] static mlir::Value findInitArg(mlir::scf::ForOp op, + mlir::Value res) { + return op.getInitArgs()[cast(res).getResultNumber()]; + } + + /** + * @brief Find corresponding result value from input qubit value (Forward). + * + * @details Recursively traverses the IR "downwards" until the respective + * yield is found. Requires that each branch takes and returns the same + * (possibly modified) qubits. Hence, we can just traverse the then-branch. + */ + [[nodiscard]] static mlir::Value findResult(mlir::scf::IfOp op, + mlir::Value q) { + /// Use the branch with fewer ops. + /// Note: LLVM doesn't guarantee that range_size is in O(1). + /// Might effect performance. + const auto szThen = llvm::range_size(op.getThenRegion().getOps()); + const auto szElse = llvm::range_size(op.getElseRegion().getOps()); + mlir::Region& region = + szElse >= szThen ? op.getThenRegion() : op.getElseRegion(); + + WireIterator it(q, ®ion); + + /// Assumptions: + /// First, there must be a yield. + /// Second, yield is a sentinel. + /// Then: Advance until the yield before the sentinel. + + it = std::prev(std::ranges::next(it, std::default_sentinel)); + assert(isa(*it) && "expected yield op"); + auto yield = cast(*it); + + /// Get the corresponding result. + + const auto results = yield.getResults(); + const auto yieldIt = llvm::find(results, it.q); + assert(yieldIt != results.end() && "yielded qubit not found in operation"); + const auto index = std::distance(results.begin(), yieldIt); + return op->getResult(index); + } + + /** + * @brief Find the first value outside the branch region for a given result + * value (Backward). + * + * @details Recursively traverses the IR "upwards" until a value outside the + * branch region is found. If the iterator's operation does not change during + * backward traversal, it indicates that the def-use chain starts within the + * branch region and does not extend into the parent region. + */ + [[nodiscard]] static mlir::Value findValue(mlir::scf::IfOp op, + mlir::Value q) { + const auto num = cast(q).getResultNumber(); + mlir::Operation* term = op.thenBlock()->getTerminator(); + mlir::scf::YieldOp yield = llvm::cast(term); + mlir::Value v = yield.getResults()[num]; + assert(v != nullptr && "expected yielded value"); + + mlir::Operation* prev{}; + WireIterator it(v, &op.getThenRegion()); + while (it.qubit().getParentRegion() != op->getParentRegion()) { + /// Since the definingOp of q might be a nullptr (BlockArgument), don't + /// immediately dereference the iterator here. + mlir::Operation* curr = it.qubit().getDefiningOp(); + if (curr == prev || curr == nullptr) { + break; + } + prev = *it; + --it; + } + + return it.qubit(); + } + + /** + * @brief Return the first user of a value in a given region. + * @param v The value. + * @param region The targeted region. + * @return A pointer to the user, or nullptr if none exists. + */ + [[nodiscard]] static mlir::Operation* getUserInRegion(mlir::Value v, + mlir::Region* region) { + for (mlir::Operation* user : v.getUsers()) { + if (user->getParentRegion() == region) { + return user; + } + } + return nullptr; + } + +public: + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = mlir::Operation*; + + explicit WireIterator() = default; + explicit WireIterator(mlir::Value q, mlir::Region* region) + : currOp(q.getDefiningOp()), q(q), region(region) {} + + [[nodiscard]] mlir::Operation* operator*() const { + assert(!sentinel && "Dereferencing sentinel iterator"); + assert(currOp && "Dereferencing null operation"); + return currOp; + } + + [[nodiscard]] mlir::Value qubit() const { return q; } + + WireIterator& operator++() { + advanceForward(); + return *this; + } + + WireIterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + + WireIterator& operator--() { + advanceBackward(); + return *this; + } + + WireIterator operator--(int) { + auto tmp = *this; + --*this; + return tmp; + } + + bool operator==(const WireIterator& other) const { + return other.q == q && other.currOp == currOp && other.sentinel == sentinel; + } + + bool operator==([[maybe_unused]] std::default_sentinel_t s) const { + return sentinel; + } + +private: + void advanceForward() { + /// If we are already at the sentinel, there is nothing to do. + if (sentinel) { + return; + } + + /// Find output from input qubit. + /// If there is no output qubit, set `sentinel` to true. + if (q.getDefiningOp() != currOp) { + mlir::TypeSwitch(currOp) + .Case( + [&](UnitaryInterface op) { q = findOutput(op, q); }) + .Case([&](ResetOp op) { q = op.getOutQubit(); }) + .Case([&](MeasureOp op) { q = op.getOutQubit(); }) + .Case( + [&](mlir::scf::ForOp op) { q = findResult(op, q); }) + .Case( + [&](mlir::scf::IfOp op) { q = findResult(op, q); }) + .Case( + [&](auto) { sentinel = true; }) + .Default([&](mlir::Operation* op) { + report_fatal_error("unknown op in def-use chain: " + + op->getName().getStringRef()); + }); + } + + /// Find the next operation. + /// If it is a sentinel there are no more ops. + if (sentinel) { + return; + } + + /// If there are no more uses, set `sentinel` to true. + if (q.use_empty()) { + sentinel = true; + return; + } + + /// Otherwise, search the user in the targeted region. + currOp = getUserInRegion(q, getRegion()); + if (currOp == nullptr) { + /// Since !q.use_empty: must be a branching op. + currOp = q.getUsers().begin()->getParentOp(); + /// For now, just check if it's a scf::IfOp. + /// Theoretically this could also be an scf::IndexSwitch, etc. + assert(isa(currOp)); + } + } + + void advanceBackward() { + /// If we are at the sentinel and move backwards, "revive" the + /// qubit value and operation. + if (sentinel) { + sentinel = false; + return; + } + + /// Get the operation that produces the qubit value. + currOp = q.getDefiningOp(); + + /// If q is a BlockArgument (no defining op), hold. + if (currOp == nullptr) { + return; + } + + /// Find input from output qubit. + /// If there is no input qubit, hold. + mlir::TypeSwitch(currOp) + .Case( + [&](UnitaryInterface op) { q = findInput(op, q); }) + .Case([&](auto op) { q = op.getInQubit(); }) + .Case([&](DeallocQubitOp op) { q = op.getQubit(); }) + .Case( + [&](mlir::scf::ForOp op) { q = findInitArg(op, q); }) + .Case( + [&](mlir::scf::IfOp op) { q = findValue(op, q); }) + .Case([&](auto) { /* hold (no-op) */ }) + .Default([&](mlir::Operation* op) { + report_fatal_error("unknown op in def-use chain: " + + op->getName().getStringRef()); + }); + } + + /** + * @brief Return the active region this iterator uses. + * @return A pointer to the region. + */ + [[nodiscard]] mlir::Region* getRegion() { + return region != nullptr ? region : q.getParentRegion(); + } + + mlir::Operation* currOp{}; + mlir::Value q; + mlir::Region* region{}; + bool sentinel{false}; +}; + +static_assert(std::bidirectional_iterator); +static_assert(std::sentinel_for, + "std::default_sentinel_t must be a sentinel for WireIterator."); +} // namespace mqt::ir::opt diff --git a/mlir/unittests/CMakeLists.txt b/mlir/unittests/CMakeLists.txt index fb4775eaa4..46421d9bc1 100644 --- a/mlir/unittests/CMakeLists.txt +++ b/mlir/unittests/CMakeLists.txt @@ -7,3 +7,4 @@ # Licensed under the MIT License add_subdirectory(translation) +add_subdirectory(dialect) diff --git a/mlir/unittests/dialect/CMakeLists.txt b/mlir/unittests/dialect/CMakeLists.txt new file mode 100644 index 0000000000..db26229f6f --- /dev/null +++ b/mlir/unittests/dialect/CMakeLists.txt @@ -0,0 +1,21 @@ +# 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 + +set(testname "mqt-core-mlir-wireiterator-test") +file(GLOB_RECURSE WIREITERATOR_TEST_SOURCES *.cpp) + +if(NOT TARGET ${testname}) + # create an executable in which the tests will be stored + add_executable(${testname} ${WIREITERATOR_TEST_SOURCES}) + # link the Google test infrastructure and a default main function to the test executable. + target_link_libraries(${testname} PRIVATE GTest::gtest_main MLIRParser MLIRMQTOpt MLIRSCFDialect + MLIRArithDialect MLIRIndexDialect) + # discover tests + gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) + set_target_properties(${testname} PROPERTIES FOLDER unittests) +endif() diff --git a/mlir/unittests/dialect/test_wireiterator.cpp b/mlir/unittests/dialect/test_wireiterator.cpp new file mode 100644 index 0000000000..562dd4a535 --- /dev/null +++ b/mlir/unittests/dialect/test_wireiterator.cpp @@ -0,0 +1,354 @@ +/* + * 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 + */ + +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Dialect/MQTOpt/IR/WireIterator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace mlir; +using namespace mqt::ir::opt; + +namespace { +/** @returns a module containing the circuit from the "Tackling the Qubit + * Mapping Problem for NISQ-Era Quantum Devices" paper by Li et al. + */ +OwningOpRef getModule(MLIRContext& ctx) { + const char* ir = R"mlir( +module { + %0 = mqtopt.allocQubit + %1 = mqtopt.allocQubit + %out_qubits = mqtopt.h() %0 : !mqtopt.Qubit + %out_qubits_0 = mqtopt.h() %1 : !mqtopt.Qubit + %out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit + %out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %out_qubits_3 = mqtopt.h() %out_qubits_2 : !mqtopt.Qubit + %out_qubits_4, %pos_ctrl_out_qubits_5 = mqtopt.x() %pos_ctrl_out_qubits ctrl %out_qubits_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %false = arith.constant false + %2:2 = scf.if %false -> (!mqtopt.Qubit, !mqtopt.Qubit) { + %out_qubits_7 = mqtopt.y() %out_qubits_4 : !mqtopt.Qubit + scf.yield %out_qubits_7, %pos_ctrl_out_qubits_5 : !mqtopt.Qubit, !mqtopt.Qubit + } else { + scf.yield %out_qubits_4, %pos_ctrl_out_qubits_5 : !mqtopt.Qubit, !mqtopt.Qubit + } + %idx0 = index.constant 0 + %idx8 = index.constant 8 + %idx1 = index.constant 1 + %3:2 = scf.for %arg0 = %idx0 to %idx8 step %idx1 iter_args(%arg1 = %2#0, %arg2 = %2#1) -> (!mqtopt.Qubit, !mqtopt.Qubit) { + %out_qubits_7 = mqtopt.h() %arg1 : !mqtopt.Qubit + %out_qubits_8 = mqtopt.h() %arg2 : !mqtopt.Qubit + scf.yield %out_qubits_7, %out_qubits_8 : !mqtopt.Qubit, !mqtopt.Qubit + } + mqtopt.deallocQubit %3#0 + mqtopt.deallocQubit %3#1 + + %4 = mqtopt.qubit 42 + %5 = mqtopt.reset %4 + %out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit +} +)mlir"; + return parseSourceString(ir, &ctx); +} + +std::string toString(Operation* op) { + std::string opStr; + llvm::raw_string_ostream os(opStr); + os << *op; + os.flush(); + return opStr; +} + +void checkOperationEqual(Operation* op, const std::string& expected) { + ASSERT_EQ(expected, toString(op)); +} + +void checkOperationStartsWith(Operation* op, const std::string& prefix) { + ASSERT_TRUE(toString(op).starts_with(prefix)); +} +} // namespace + +class WireIteratorTest : public ::testing::Test { +protected: + std::unique_ptr context; + + void SetUp() override { + DialectRegistry registry; + registry.insert(); + registry.insert(); + registry.insert(); + registry.insert(); + + context = std::make_unique(); + context->appendDialectRegistry(registry); + context->loadAllAvailableDialects(); + } +}; + +TEST_F(WireIteratorTest, TestForward) { + + /// + /// Test the forward iteration. + /// + + auto module = getModule(*context); + auto alloc = *(module->getOps().begin()); + auto q = alloc.getQubit(); + WireIterator it(q, q.getParentRegion()); + + checkOperationEqual(*it, "%0 = mqtopt.allocQubit"); + + ++it; + checkOperationEqual(*it, "%out_qubits = mqtopt.h() %0 : !mqtopt.Qubit"); + + ++it; + checkOperationEqual(*it, + "%out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit"); + + ++it; + checkOperationEqual( + *it, "%out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 " + "ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); + + ++it; + checkOperationEqual( + *it, + "%out_qubits_4, %pos_ctrl_out_qubits_5 = mqtopt.x() %pos_ctrl_out_qubits " + "ctrl %out_qubits_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); + + ++it; + checkOperationStartsWith( + *it, "%2:2 = scf.if %false -> (!mqtopt.Qubit, !mqtopt.Qubit)"); + + ++it; + checkOperationStartsWith(*it, + "%3:2 = scf.for %arg0 = %idx0 to %idx8 step %idx1"); + + ++it; + checkOperationEqual(*it, "mqtopt.deallocQubit %3#0"); + + ++it; + ASSERT_EQ(it, std::default_sentinel); + + ++it; + ASSERT_EQ(it, std::default_sentinel); +} + +TEST_F(WireIteratorTest, TestBackward) { + + /// + /// Test the backward iteration. + /// + + auto module = getModule(*context); + auto allocs = module->getOps(); + const auto allocRng = llvm::make_range(allocs.begin(), allocs.end()); + const auto allocVec = llvm::to_vector(allocRng); + auto alloc = allocVec[1]; + auto q = alloc.getQubit(); + WireIterator it(q, q.getParentRegion()); + const WireIterator begin(it); + + ASSERT_EQ(it, begin); + + for (; it != std::default_sentinel; ++it) { + llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. + } + + ASSERT_EQ(it, std::default_sentinel); + + --it; + checkOperationEqual(*it, "mqtopt.deallocQubit %3#1"); + + --it; + checkOperationStartsWith(*it, + "%3:2 = scf.for %arg0 = %idx0 to %idx8 step %idx1"); + + --it; + checkOperationStartsWith( + *it, "%2:2 = scf.if %false -> (!mqtopt.Qubit, !mqtopt.Qubit)"); + + --it; + checkOperationEqual( + *it, + "%out_qubits_4, %pos_ctrl_out_qubits_5 = mqtopt.x() %pos_ctrl_out_qubits " + "ctrl %out_qubits_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); + + --it; + checkOperationEqual( + *it, "%out_qubits_3 = mqtopt.h() %out_qubits_2 : !mqtopt.Qubit"); + + --it; + checkOperationEqual( + *it, "%out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 " + "ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); + + --it; + checkOperationEqual(*it, "%out_qubits_0 = mqtopt.h() %1 : !mqtopt.Qubit"); + + --it; + checkOperationEqual(*it, "%1 = mqtopt.allocQubit"); + + ASSERT_EQ(it, begin); + + --it; + checkOperationEqual(*it, "%1 = mqtopt.allocQubit"); + + ASSERT_EQ(it, begin); +} + +TEST_F(WireIteratorTest, TestForwardAndBackward) { + + /// + /// Test the forward as well as the backward iteration. + /// + + auto module = getModule(*context); + auto alloc = *(module->getOps().begin()); + auto q = alloc.getQubit(); + WireIterator it(q, q.getParentRegion()); + const WireIterator begin(it); + + checkOperationEqual(*it, "%0 = mqtopt.allocQubit"); + + ++it; + checkOperationEqual(*it, "%out_qubits = mqtopt.h() %0 : !mqtopt.Qubit"); + + ++it; + checkOperationEqual(*it, + "%out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit"); + + ++it; + checkOperationEqual( + *it, "%out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 " + "ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); + + --it; + checkOperationEqual(*it, + "%out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit"); + + --it; + checkOperationEqual(*it, "%out_qubits = mqtopt.h() %0 : !mqtopt.Qubit"); + + --it; + checkOperationEqual(*it, "%0 = mqtopt.allocQubit"); + + ASSERT_EQ(it, begin); + + for (; it != std::default_sentinel; ++it) { + llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. + } + + ASSERT_EQ(it, std::default_sentinel); + + it = std::prev(it); // Back to last non-sentinel item. + + for (; it != begin; --it) { + llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. + } + + ASSERT_EQ(it, begin); +} + +TEST_F(WireIteratorTest, TestRecursiveUse) { + + /// + /// Test the recursive use of the iterator. + /// + + auto module = getModule(*context); + auto alloc = *(module->getOps().begin()); + auto q = alloc.getQubit(); + WireIterator it(q, q.getParentRegion()); + + /// Advance until 'scf.for'. + for (; it != std::default_sentinel; ++it) { + if (isa(*it)) { + break; + } + llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. + } + + auto loop = cast(*it); + for (auto [iter, init] : + llvm::zip(loop.getRegionIterArgs(), loop.getInitArgs())) { + if (init == it.qubit()) { + WireIterator rec(iter, &loop.getRegion()); + const WireIterator recBegin(rec); + rec--; + + ASSERT_EQ(rec, recBegin); // Test blockargument handling. + + rec++; + checkOperationEqual(*rec, + "%out_qubits_7 = mqtopt.h() %arg1 : !mqtopt.Qubit"); + + rec++; + checkOperationEqual(*rec, "scf.yield %out_qubits_7, %out_qubits_8 : " + "!mqtopt.Qubit, !mqtopt.Qubit"); + } + } +} + +TEST_F(WireIteratorTest, TestStaticQubit) { + + /// + /// Test the iteration with a static qubit. + /// + + auto module = getModule(*context); + auto qubit = *(module->getOps().begin()); + auto q = qubit.getQubit(); + WireIterator it(q, q.getParentRegion()); + const WireIterator begin(it); + + checkOperationEqual(*it, "%4 = mqtopt.qubit 42"); + + ++it; + checkOperationEqual(*it, "%5 = mqtopt.reset %4"); + + ++it; + checkOperationEqual(*it, "%out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit"); + + ++it; + ASSERT_EQ(it, std::default_sentinel); + + --it; + checkOperationEqual(*it, "%out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit"); + ASSERT_EQ(it.qubit(), (*it)->getResult(0)); // q = %out_qubits_6 + + --it; + checkOperationEqual(*it, "%out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit"); + ASSERT_EQ(it.qubit(), (*it)->getOperand(0)); // q = %5 + + --it; + checkOperationEqual(*it, "%5 = mqtopt.reset %4"); + + --it; + checkOperationEqual(*it, "%4 = mqtopt.qubit 42"); + + ASSERT_EQ(it, begin); +} From 8edd8239f95dfcc6760e5a69b42b0478e58d07d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 30 Nov 2025 09:33:28 +0000 Subject: [PATCH 25/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=AA=9D=20Update?= =?UTF-8?q?=20pre-commit=20hook=20rbubley/mirrors-prettier=20to=20v3.7.3?= =?UTF-8?q?=20(#1345)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [rbubley/mirrors-prettier](https://redirect.github.com/rbubley/mirrors-prettier) | repository | patch | `v3.7.2` -> `v3.7.3` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
rbubley/mirrors-prettier (rbubley/mirrors-prettier) ### [`v3.7.3`](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.7.2...v3.7.3) [Compare Source](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.7.2...v3.7.3)
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e856a1824..250b078e43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -124,7 +124,7 @@ repos: # Format configuration files with prettier - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.7.2 + rev: v3.7.3 hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json] From 302c1991eceb66fde32f70b0bf420646ba5744d6 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Sun, 30 Nov 2025 17:59:23 +0100 Subject: [PATCH 26/58] =?UTF-8?q?=F0=9F=90=9B=20Fix=20dynamic=20QDMI=20Dev?= =?UTF-8?q?ice=20(#1336)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR refactors the singleton pattern for QDMI Device implementations to support explicit initialization and finalization, enabling proper cleanup when device libraries are loaded and unloaded dynamically. - Replaces Meyer's singleton pattern (function-local static) with manual initialization using raw pointers - Adds explicit initialize() and finalize() methods to control Device singleton lifecycle - Includes the DDSIM device in cross-platform test coverage ## Checklist: - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] I have added migration instructions to the upgrade guide (if needed). - [x] The changes follow the project's style guidelines and introduce no new warnings. - [ ] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --------- Signed-off-by: burgholzer Co-authored-by: burgholzer # Conflicts: # src/na/device/Device.cpp # test/qdmi/test_driver.cpp --- CHANGELOG.md | 5 +++ include/mqt-core/na/device/Device.hpp | 27 ++++++++++++---- include/mqt-core/qdmi/Driver.hpp | 12 +++---- include/mqt-core/qdmi/dd/Device.hpp | 26 +++++++++++---- src/na/device/Device.cpp | 38 ++++++++++++++++++++-- src/qdmi/dd/Device.cpp | 46 ++++++++++++++++++++++++--- test/qdmi/dd/error_handling_test.cpp | 22 ++++++++----- test/qdmi/dd/job_lifecycle_test.cpp | 3 +- test/qdmi/test_driver.cpp | 17 +++++----- 9 files changed, 151 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b478dc03c..4fe3dcbccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ♻️ Preserve tuple structure and improve site type clarity of the MQT NA Default QDMI Device ([#1299]) ([**@marcelwa**]) - ♻️ Move DD package evaluation module to standalone script ([#1327]) ([**@burgholzer**]) +### Fixed + +- 🐛 Fix memory management in dynamic QDMI device by making it explicit ([#1336]) ([**@ystade**]) + ### Removed - 🔥 Remove the `evaluation` extra from the MQT Core Python package ([#1327]) ([**@burgholzer**]) @@ -253,6 +257,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1336]: https://github.com/munich-quantum-toolkit/core/pull/1336 [#1327]: https://github.com/munich-quantum-toolkit/core/pull/1327 [#1310]: https://github.com/munich-quantum-toolkit/core/pull/1310 [#1300]: https://github.com/munich-quantum-toolkit/core/pull/1300 diff --git a/include/mqt-core/na/device/Device.hpp b/include/mqt-core/na/device/Device.hpp index d7e48bed68..0ad132ef67 100644 --- a/include/mqt-core/na/device/Device.hpp +++ b/include/mqt-core/na/device/Device.hpp @@ -16,7 +16,7 @@ #include "mqt_na_qdmi/device.h" -#include +#include #include #include #include @@ -69,6 +69,9 @@ class Device final { /// @brief Private constructor to enforce the singleton pattern. Device(); + /// @brief The singleton instance. + static std::atomic instance; + public: // Default move constructor and move assignment operator. Device(Device&&) = default; @@ -77,15 +80,25 @@ class Device final { Device(const Device&) = delete; Device& operator=(const Device&) = delete; - /// @returns the singleton instance of the Device class. - [[nodiscard]] static Device& get() { - static Device instance; - return instance; - } - /// @brief Destructor for the Device class. ~Device() = default; + /** + * @brief Initializes the singleton instance. + * @details Must be called before `get()`. + */ + static void initialize(); + + /** + * @brief Destroys the singleton instance. + * @details After this call, `get()` must not be called until a new + * `initialize()` call. + */ + static void finalize(); + + /// @returns the singleton instance of the Device class. + [[nodiscard]] static auto get() -> Device&; + /** * @brief Allocates a new device session. * @see MQT_NA_QDMI_device_session_alloc diff --git a/include/mqt-core/qdmi/Driver.hpp b/include/mqt-core/qdmi/Driver.hpp index d78e53374d..9a8735a3ce 100644 --- a/include/mqt-core/qdmi/Driver.hpp +++ b/include/mqt-core/qdmi/Driver.hpp @@ -191,7 +191,7 @@ struct QDMI_Device_impl_d { */ ~QDMI_Device_impl_d() { jobs_.clear(); - if (deviceSession_ != nullptr) { + if (library_ && deviceSession_ != nullptr) { library_->device_session_free(deviceSession_); } } @@ -377,6 +377,11 @@ class Driver final { /// @brief Private constructor to enforce the singleton pattern. Driver(); + /** + * @brief Vector of unique pointers to QDMI_Device_impl_d objects. + */ + std::vector> devices_; + /** * @brief Map of sessions to their corresponding unique pointers to * QDMI_Session_impl_d objects. @@ -384,11 +389,6 @@ class Driver final { std::unordered_map> sessions_; - /** - * @brief Vector of unique pointers to QDMI_Device_impl_d objects. - */ - std::vector> devices_; - public: // Delete copy constructors and assignment operators to prevent copying the // singleton instance. diff --git a/include/mqt-core/qdmi/dd/Device.hpp b/include/mqt-core/qdmi/dd/Device.hpp index adb98364d8..0b1307ef97 100644 --- a/include/mqt-core/qdmi/dd/Device.hpp +++ b/include/mqt-core/qdmi/dd/Device.hpp @@ -29,7 +29,6 @@ #include #include #include -#include namespace qdmi::dd { class Device final { @@ -64,6 +63,9 @@ class Device final { /// @brief Private constructor to enforce the singleton pattern. Device(); + /// @brief The singleton instance. + static std::atomic instance; + public: // Default move constructor and move assignment operator. Device(Device&&) = delete; @@ -72,15 +74,25 @@ class Device final { Device(const Device&) = delete; Device& operator=(const Device&) = delete; - /// @returns the singleton instance of the Device class. - [[nodiscard]] static Device& get() { - static Device instance; - return instance; - } - /// @brief Destructor for the Device class. ~Device() = default; + /** + * @brief Initializes the singleton instance. + * @details Must be called before `get()`. + */ + static void initialize(); + + /** + * @brief Destroys the singleton instance. + * @details After this call, `get()` must not be called until a new + * `initialize()` call. + */ + static void finalize(); + + /// @returns the singleton instance of the Device class. + [[nodiscard]] static auto get() -> Device&; + /** * @brief Allocates a new device session. * @see MQT_DDSIM_QDMI_device_session_alloc diff --git a/src/na/device/Device.cpp b/src/na/device/Device.cpp index 78dbf69b74..8f5b918446 100644 --- a/src/na/device/Device.cpp +++ b/src/na/device/Device.cpp @@ -18,6 +18,8 @@ #include "na/device/DeviceMemberInitializers.hpp" #include +#include +#include #include #include #include @@ -94,6 +96,7 @@ // NOLINTEND(bugprone-macro-parentheses) namespace qdmi::na { +std::atomic Device::instance = nullptr; Device::Device() { // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer) INITIALIZE_NAME(name_); @@ -106,6 +109,34 @@ Device::Device() { INITIALIZE_SITES(sites_); INITIALIZE_OPERATIONS(operations_); } + +void Device::initialize() { + // NOLINTNEXTLINE(misc-const-correctness) + Device* expected = nullptr; + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + auto* newInstance = new Device(); + if (!instance.compare_exchange_strong(expected, newInstance)) { + // Another thread won the race, so delete the instance we created. + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete newInstance; + } +} + +void Device::finalize() { + // Atomically swap the instance pointer with nullptr and get the old value. + const Device* oldInstance = instance.exchange(nullptr); + // Delete the old instance if it existed. + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete oldInstance; +} + +auto Device::get() -> Device& { + auto* loadedInstance = instance.load(); + assert(loadedInstance != nullptr && + "Device not initialized. Call `initialize()` first."); + return *loadedInstance; +} + auto Device::sessionAlloc(MQT_NA_QDMI_Device_Session* session) -> int { if (session == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; @@ -607,11 +638,14 @@ auto MQT_NA_QDMI_Operation_impl_d::queryProperty( } int MQT_NA_QDMI_device_initialize() { - std::ignore = qdmi::na::Device::get(); // Ensure the singleton is created + qdmi::na::Device::initialize(); return QDMI_SUCCESS; } -int MQT_NA_QDMI_device_finalize() { return QDMI_SUCCESS; } +int MQT_NA_QDMI_device_finalize() { + qdmi::Device::finalize(); + return QDMI_SUCCESS; +} int MQT_NA_QDMI_device_session_alloc(MQT_NA_QDMI_Device_Session* session) { return qdmi::na::Device::get().sessionAlloc(session); diff --git a/src/qdmi/dd/Device.cpp b/src/qdmi/dd/Device.cpp index f6d359ce4d..0ad109e080 100644 --- a/src/qdmi/dd/Device.cpp +++ b/src/qdmi/dd/Device.cpp @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include #include @@ -205,10 +207,39 @@ constexpr auto OPERATION_ADDRESSES = makeOperationAddresses(OPERATIONS); namespace qdmi::dd { +std::atomic Device::instance = nullptr; + Device::Device() : name_("MQT Core DDSIM QDMI Device"), qubitsNum_(std::numeric_limits<::dd::Qubit>::max()) {} +void Device::initialize() { + // NOLINTNEXTLINE(misc-const-correctness) + Device* expected = nullptr; + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + auto* newInstance = new Device(); + if (!instance.compare_exchange_strong(expected, newInstance)) { + // Another thread won the race, so delete the instance we created. + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete newInstance; + } +} + +void Device::finalize() { + // Atomically swap the instance pointer with nullptr and get the old value. + const Device* oldInstance = instance.exchange(nullptr); + // Delete the old instance if it existed. + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete oldInstance; +} + +auto Device::get() -> Device& { + auto* loadedInstance = instance.load(); + assert(loadedInstance != nullptr && + "Device not initialized. Call `initialize()` first."); + return *loadedInstance; +} + auto Device::sessionAlloc(MQT_DDSIM_QDMI_Device_Session* session) -> QDMI_STATUS { if (session == nullptr) { @@ -560,7 +591,7 @@ auto MQT_DDSIM_QDMI_Device_Job_impl_d::wait(const size_t timeout) const if (!jobHandle_.valid() || (s != QDMI_JOB_STATUS_SUBMITTED && s != QDMI_JOB_STATUS_QUEUED && s != QDMI_JOB_STATUS_RUNNING)) { - return QDMI_ERROR_INVALIDARGUMENT; + return QDMI_ERROR_BADSTATE; } if (timeout > 0) { @@ -734,14 +765,16 @@ auto MQT_DDSIM_QDMI_Device_Job_impl_d::getResults(const QDMI_Job_Result result, const size_t size, void* data, size_t* sizeRet) -> QDMI_STATUS { - if (status_.load() != QDMI_JOB_STATUS_DONE || - (data != nullptr && size == 0) || + if ((data != nullptr && size == 0) || (result >= QDMI_JOB_RESULT_MAX && result != QDMI_JOB_RESULT_CUSTOM1 && result != QDMI_JOB_RESULT_CUSTOM2 && result != QDMI_JOB_RESULT_CUSTOM3 && result != QDMI_JOB_RESULT_CUSTOM4 && result != QDMI_JOB_RESULT_CUSTOM5)) { return QDMI_ERROR_INVALIDARGUMENT; } + if (status_.load() != QDMI_JOB_STATUS_DONE) { + return QDMI_ERROR_BADSTATE; + } switch (result) { case QDMI_JOB_RESULT_HIST_KEYS: case QDMI_JOB_RESULT_HIST_VALUES: @@ -775,11 +808,14 @@ auto MQT_DDSIM_QDMI_Device_Job_impl_d::getResults(const QDMI_Job_Result result, // QDMI uses a different naming convention for its C interface functions // NOLINTBEGIN(readability-identifier-naming) int MQT_DDSIM_QDMI_device_initialize() { - std::ignore = qdmi::dd::Device::get(); // Ensure the singleton is created + qdmi::dd::Device::initialize(); return QDMI_SUCCESS; } -int MQT_DDSIM_QDMI_device_finalize() { return QDMI_SUCCESS; } +int MQT_DDSIM_QDMI_device_finalize() { + qdmi::dd::Device::finalize(); + return QDMI_SUCCESS; +} int MQT_DDSIM_QDMI_device_session_alloc( MQT_DDSIM_QDMI_Device_Session* session) { diff --git a/test/qdmi/dd/error_handling_test.cpp b/test/qdmi/dd/error_handling_test.cpp index 2d573e3d6b..bb4935f481 100644 --- a/test/qdmi/dd/error_handling_test.cpp +++ b/test/qdmi/dd/error_handling_test.cpp @@ -18,7 +18,13 @@ #include #include -TEST(ErrorHandling, NullptrArguments) { +class ErrorHandling : public ::testing::Test { +protected: + static auto SetUpTestSuite() -> void { MQT_DDSIM_QDMI_device_initialize(); } + static auto TearDownTestSuite() -> void { MQT_DDSIM_QDMI_device_finalize(); } +}; + +TEST_F(ErrorHandling, NullptrArguments) { EXPECT_EQ(MQT_DDSIM_QDMI_device_session_alloc(nullptr), QDMI_ERROR_INVALIDARGUMENT); EXPECT_EQ(MQT_DDSIM_QDMI_device_session_query_device_property( @@ -61,7 +67,7 @@ TEST(ErrorHandling, NullptrArguments) { QDMI_ERROR_INVALIDARGUMENT); } -TEST(ErrorHandling, GetResultsBeforeDone) { +TEST_F(ErrorHandling, GetResultsBeforeDone) { const qdmi_test::SessionGuard s{}; const qdmi_test::JobGuard j{s.session}; ASSERT_EQ(qdmi_test::setProgram(j.job, QDMI_PROGRAM_FORMAT_QASM3, @@ -71,17 +77,17 @@ TEST(ErrorHandling, GetResultsBeforeDone) { // Before submit → invalid EXPECT_EQ(MQT_DDSIM_QDMI_device_job_get_results( j.job, QDMI_JOB_RESULT_HIST_KEYS, 0, nullptr, nullptr), - QDMI_ERROR_INVALIDARGUMENT); + QDMI_ERROR_BADSTATE); ASSERT_EQ(MQT_DDSIM_QDMI_device_job_submit(j.job), QDMI_SUCCESS); // After submit but not necessarily done → still invalid or waits; contract // says invalid EXPECT_EQ(MQT_DDSIM_QDMI_device_job_get_results( j.job, QDMI_JOB_RESULT_HIST_KEYS, 0, nullptr, nullptr), - QDMI_ERROR_INVALIDARGUMENT); + QDMI_ERROR_BADSTATE); ASSERT_EQ(MQT_DDSIM_QDMI_device_job_wait(j.job, 0), QDMI_SUCCESS); } -TEST(ErrorHandling, MaxEnums) { +TEST_F(ErrorHandling, MaxEnums) { const qdmi_test::SessionGuard s{}; const qdmi_test::JobGuard j{s.session}; @@ -126,7 +132,7 @@ TEST(ErrorHandling, MaxEnums) { QDMI_ERROR_INVALIDARGUMENT); } -TEST(ErrorHandling, CustomEnums) { +TEST_F(ErrorHandling, CustomEnums) { const qdmi_test::SessionGuard s{}; const qdmi_test::JobGuard j{s.session}; @@ -250,7 +256,7 @@ TEST(ErrorHandling, CustomEnums) { QDMI_ERROR_NOTSUPPORTED); } -TEST(ErrorHandling, BadState) { +TEST_F(ErrorHandling, BadState) { MQT_DDSIM_QDMI_Device_Session session = nullptr; EXPECT_EQ(MQT_DDSIM_QDMI_device_session_alloc(&session), QDMI_SUCCESS); EXPECT_EQ(MQT_DDSIM_QDMI_device_session_query_device_property( @@ -278,7 +284,7 @@ TEST(ErrorHandling, BadState) { EXPECT_EQ(MQT_DDSIM_QDMI_device_job_submit(j.job), QDMI_ERROR_BADSTATE); } -TEST(ErrorHandling, MalformedProgramFailsForBothModes) { +TEST_F(ErrorHandling, MalformedProgramFailsForBothModes) { const qdmi_test::SessionGuard s{}; // Sampling mode { diff --git a/test/qdmi/dd/job_lifecycle_test.cpp b/test/qdmi/dd/job_lifecycle_test.cpp index 570d7b409f..9ae5e83af1 100644 --- a/test/qdmi/dd/job_lifecycle_test.cpp +++ b/test/qdmi/dd/job_lifecycle_test.cpp @@ -45,8 +45,7 @@ TEST(JobLifecycle, WaitInvalidBeforeSubmitAndIdempotentAfterDone) { const qdmi_test::SessionGuard s{}; const qdmi_test::JobGuard j{s.session}; // wait before submit is invalid - EXPECT_EQ(MQT_DDSIM_QDMI_device_job_wait(j.job, 0), - QDMI_ERROR_INVALIDARGUMENT); + EXPECT_EQ(MQT_DDSIM_QDMI_device_job_wait(j.job, 0), QDMI_ERROR_BADSTATE); // now run a quick job ASSERT_EQ(qdmi_test::setProgram(j.job, QDMI_PROGRAM_FORMAT_QASM3, qdmi_test::QASM3_BELL_SAMPLING), diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index 239039872f..506db50b3c 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -43,7 +43,7 @@ MATCHER_P2(IsBetween, a, b, } // namespace } // namespace testing namespace qc { -class DriverTest : public testing::TestWithParam { +class DriverTest : public testing::TestWithParam { protected: QDMI_Session session = nullptr; QDMI_Device device = nullptr; @@ -165,9 +165,9 @@ TEST_P(DriverTest, JobSetParameter) { } TEST_P(DriverJobTest, JobSetParameter) { - EXPECT_EQ(QDMI_job_set_parameter(job, QDMI_JOB_PARAMETER_PROGRAM, - sizeof(QDMI_Program_Format), nullptr), - QDMI_ERROR_NOTSUPPORTED); + EXPECT_THAT(QDMI_job_set_parameter(job, QDMI_JOB_PARAMETER_PROGRAM, + sizeof(QDMI_Program_Format), nullptr), + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); const QDMI_Program_Format value = QDMI_PROGRAM_FORMAT_QASM2; EXPECT_THAT(QDMI_job_set_parameter(job, QDMI_JOB_PARAMETER_PROGRAMFORMAT, sizeof(QDMI_Program_Format), &value), @@ -256,7 +256,7 @@ TEST_P(DriverTest, JobWait) { TEST_P(DriverJobTest, JobWait) { EXPECT_THAT(QDMI_job_wait(job, 1), testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED, - QDMI_ERROR_TIMEOUT)); + QDMI_ERROR_TIMEOUT, QDMI_ERROR_BADSTATE)); } TEST_P(DriverTest, JobGetResults) { @@ -268,7 +268,8 @@ TEST_P(DriverTest, JobGetResults) { TEST_P(DriverJobTest, JobGetResults) { EXPECT_THAT( QDMI_job_get_results(job, QDMI_JOB_RESULT_SHOTS, 0, nullptr, nullptr), - testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED)); + testing::AnyOf(QDMI_SUCCESS, QDMI_ERROR_NOTSUPPORTED, + QDMI_ERROR_BADSTATE)); } TEST_P(DriverTest, QueryDeviceProperty) { @@ -523,7 +524,7 @@ INSTANTIATE_TEST_SUITE_P( DriverTest, // Parameters to test with testing::ValuesIn(DEVICES), - [](const testing::TestParamInfo& paramInfo) { + [](const testing::TestParamInfo& paramInfo) { std::string name = paramInfo.param; // Replace spaces with underscores for valid test names std::ranges::replace(name, ' ', '_'); @@ -539,7 +540,7 @@ INSTANTIATE_TEST_SUITE_P( DriverJobTest, // Parameters to test with testing::ValuesIn(DEVICES), - [](const testing::TestParamInfo& paramInfo) { + [](const testing::TestParamInfo& paramInfo) { std::string name = paramInfo.param; // Replace spaces with underscores for valid test names std::ranges::replace(name, ' ', '_'); From a0b3ee3f3098bb71761b176828fefa06d2d66666 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 04:35:38 +0000 Subject: [PATCH 27/58] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=94=92=EF=B8=8F?= =?UTF-8?q?=20Lock=20file=20maintenance=20(#1346)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - "before 4am on monday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- uv.lock | 348 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 174 insertions(+), 174 deletions(-) diff --git a/uv.lock b/uv.lock index b2e525a941..239de6a094 100644 --- a/uv.lock +++ b/uv.lock @@ -116,15 +116,15 @@ wheels = [ [[package]] name = "beautifulsoup4" -version = "4.14.2" +version = "4.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] [[package]] @@ -761,59 +761,59 @@ wheels = [ [[package]] name = "fonttools" -version = "4.60.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/42/97a13e47a1e51a5a7142475bbcf5107fe3a68fc34aef331c897d5fb98ad0/fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9", size = 3559823, upload-time = "2025-09-29T21:13:27.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/70/03e9d89a053caff6ae46053890eba8e4a5665a7c5638279ed4492e6d4b8b/fonttools-4.60.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9a52f254ce051e196b8fe2af4634c2d2f02c981756c6464dc192f1b6050b4e28", size = 2810747, upload-time = "2025-09-29T21:10:59.653Z" }, - { url = "https://files.pythonhosted.org/packages/6f/41/449ad5aff9670ab0df0f61ee593906b67a36d7e0b4d0cd7fa41ac0325bf5/fonttools-4.60.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7420a2696a44650120cdd269a5d2e56a477e2bfa9d95e86229059beb1c19e15", size = 2346909, upload-time = "2025-09-29T21:11:02.882Z" }, - { url = "https://files.pythonhosted.org/packages/9a/18/e5970aa96c8fad1cb19a9479cc3b7602c0c98d250fcdc06a5da994309c50/fonttools-4.60.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee0c0b3b35b34f782afc673d503167157094a16f442ace7c6c5e0ca80b08f50c", size = 4864572, upload-time = "2025-09-29T21:11:05.096Z" }, - { url = "https://files.pythonhosted.org/packages/ce/20/9b2b4051b6ec6689480787d506b5003f72648f50972a92d04527a456192c/fonttools-4.60.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:282dafa55f9659e8999110bd8ed422ebe1c8aecd0dc396550b038e6c9a08b8ea", size = 4794635, upload-time = "2025-09-29T21:11:08.651Z" }, - { url = "https://files.pythonhosted.org/packages/10/52/c791f57347c1be98f8345e3dca4ac483eb97666dd7c47f3059aeffab8b59/fonttools-4.60.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4ba4bd646e86de16160f0fb72e31c3b9b7d0721c3e5b26b9fa2fc931dfdb2652", size = 4843878, upload-time = "2025-09-29T21:11:10.893Z" }, - { url = "https://files.pythonhosted.org/packages/69/e9/35c24a8d01644cee8c090a22fad34d5b61d1e0a8ecbc9945ad785ebf2e9e/fonttools-4.60.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0b0835ed15dd5b40d726bb61c846a688f5b4ce2208ec68779bc81860adb5851a", size = 4954555, upload-time = "2025-09-29T21:11:13.24Z" }, - { url = "https://files.pythonhosted.org/packages/f7/86/fb1e994971be4bdfe3a307de6373ef69a9df83fb66e3faa9c8114893d4cc/fonttools-4.60.1-cp310-cp310-win32.whl", hash = "sha256:1525796c3ffe27bb6268ed2a1bb0dcf214d561dfaf04728abf01489eb5339dce", size = 2232019, upload-time = "2025-09-29T21:11:15.73Z" }, - { url = "https://files.pythonhosted.org/packages/40/84/62a19e2bd56f0e9fb347486a5b26376bade4bf6bbba64dda2c103bd08c94/fonttools-4.60.1-cp310-cp310-win_amd64.whl", hash = "sha256:268ecda8ca6cb5c4f044b1fb9b3b376e8cd1b361cef275082429dc4174907038", size = 2276803, upload-time = "2025-09-29T21:11:18.152Z" }, - { url = "https://files.pythonhosted.org/packages/ea/85/639aa9bface1537e0fb0f643690672dde0695a5bbbc90736bc571b0b1941/fonttools-4.60.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7b4c32e232a71f63a5d00259ca3d88345ce2a43295bb049d21061f338124246f", size = 2831872, upload-time = "2025-09-29T21:11:20.329Z" }, - { url = "https://files.pythonhosted.org/packages/6b/47/3c63158459c95093be9618794acb1067b3f4d30dcc5c3e8114b70e67a092/fonttools-4.60.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3630e86c484263eaac71d117085d509cbcf7b18f677906824e4bace598fb70d2", size = 2356990, upload-time = "2025-09-29T21:11:22.754Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/1934b537c86fcf99f9761823f1fc37a98fbd54568e8e613f29a90fed95a9/fonttools-4.60.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c1015318e4fec75dd4943ad5f6a206d9727adf97410d58b7e32ab644a807914", size = 5042189, upload-time = "2025-09-29T21:11:25.061Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d2/9f4e4c4374dd1daa8367784e1bd910f18ba886db1d6b825b12edf6db3edc/fonttools-4.60.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e6c58beb17380f7c2ea181ea11e7db8c0ceb474c9dd45f48e71e2cb577d146a1", size = 4978683, upload-time = "2025-09-29T21:11:27.693Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c4/0fb2dfd1ecbe9a07954cc13414713ed1eab17b1c0214ef07fc93df234a47/fonttools-4.60.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec3681a0cb34c255d76dd9d865a55f260164adb9fa02628415cdc2d43ee2c05d", size = 5021372, upload-time = "2025-09-29T21:11:30.257Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d5/495fc7ae2fab20223cc87179a8f50f40f9a6f821f271ba8301ae12bb580f/fonttools-4.60.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4b5c37a5f40e4d733d3bbaaef082149bee5a5ea3156a785ff64d949bd1353fa", size = 5132562, upload-time = "2025-09-29T21:11:32.737Z" }, - { url = "https://files.pythonhosted.org/packages/bc/fa/021dab618526323c744e0206b3f5c8596a2e7ae9aa38db5948a131123e83/fonttools-4.60.1-cp311-cp311-win32.whl", hash = "sha256:398447f3d8c0c786cbf1209711e79080a40761eb44b27cdafffb48f52bcec258", size = 2230288, upload-time = "2025-09-29T21:11:35.015Z" }, - { url = "https://files.pythonhosted.org/packages/bb/78/0e1a6d22b427579ea5c8273e1c07def2f325b977faaf60bb7ddc01456cb1/fonttools-4.60.1-cp311-cp311-win_amd64.whl", hash = "sha256:d066ea419f719ed87bc2c99a4a4bfd77c2e5949cb724588b9dd58f3fd90b92bf", size = 2278184, upload-time = "2025-09-29T21:11:37.434Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f7/a10b101b7a6f8836a5adb47f2791f2075d044a6ca123f35985c42edc82d8/fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc", size = 2832953, upload-time = "2025-09-29T21:11:39.616Z" }, - { url = "https://files.pythonhosted.org/packages/ed/fe/7bd094b59c926acf2304d2151354ddbeb74b94812f3dc943c231db09cb41/fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877", size = 2352706, upload-time = "2025-09-29T21:11:41.826Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ca/4bb48a26ed95a1e7eba175535fe5805887682140ee0a0d10a88e1de84208/fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c", size = 4923716, upload-time = "2025-09-29T21:11:43.893Z" }, - { url = "https://files.pythonhosted.org/packages/b8/9f/2cb82999f686c1d1ddf06f6ae1a9117a880adbec113611cc9d22b2fdd465/fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401", size = 4968175, upload-time = "2025-09-29T21:11:46.439Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/be569699e37d166b78e6218f2cde8c550204f2505038cdd83b42edc469b9/fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903", size = 4911031, upload-time = "2025-09-29T21:11:48.977Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9f/89411cc116effaec5260ad519162f64f9c150e5522a27cbb05eb62d0c05b/fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed", size = 5062966, upload-time = "2025-09-29T21:11:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/62/a1/f888221934b5731d46cb9991c7a71f30cb1f97c0ef5fcf37f8da8fce6c8e/fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6", size = 2218750, upload-time = "2025-09-29T21:11:56.601Z" }, - { url = "https://files.pythonhosted.org/packages/88/8f/a55b5550cd33cd1028601df41acd057d4be20efa5c958f417b0c0613924d/fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383", size = 2267026, upload-time = "2025-09-29T21:11:58.852Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5b/cdd2c612277b7ac7ec8c0c9bc41812c43dc7b2d5f2b0897e15fdf5a1f915/fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb", size = 2825777, upload-time = "2025-09-29T21:12:01.22Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8a/de9cc0540f542963ba5e8f3a1f6ad48fa211badc3177783b9d5cadf79b5d/fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4", size = 2348080, upload-time = "2025-09-29T21:12:03.785Z" }, - { url = "https://files.pythonhosted.org/packages/2d/8b/371ab3cec97ee3fe1126b3406b7abd60c8fec8975fd79a3c75cdea0c3d83/fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c", size = 4903082, upload-time = "2025-09-29T21:12:06.382Z" }, - { url = "https://files.pythonhosted.org/packages/04/05/06b1455e4bc653fcb2117ac3ef5fa3a8a14919b93c60742d04440605d058/fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77", size = 4960125, upload-time = "2025-09-29T21:12:09.314Z" }, - { url = "https://files.pythonhosted.org/packages/8e/37/f3b840fcb2666f6cb97038793606bdd83488dca2d0b0fc542ccc20afa668/fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199", size = 4901454, upload-time = "2025-09-29T21:12:11.931Z" }, - { url = "https://files.pythonhosted.org/packages/fd/9e/eb76f77e82f8d4a46420aadff12cec6237751b0fb9ef1de373186dcffb5f/fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c", size = 5044495, upload-time = "2025-09-29T21:12:15.241Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b3/cede8f8235d42ff7ae891bae8d619d02c8ac9fd0cfc450c5927a6200c70d/fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272", size = 2217028, upload-time = "2025-09-29T21:12:17.96Z" }, - { url = "https://files.pythonhosted.org/packages/75/4d/b022c1577807ce8b31ffe055306ec13a866f2337ecee96e75b24b9b753ea/fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac", size = 2266200, upload-time = "2025-09-29T21:12:20.14Z" }, - { url = "https://files.pythonhosted.org/packages/9a/83/752ca11c1aa9a899b793a130f2e466b79ea0cf7279c8d79c178fc954a07b/fonttools-4.60.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a884aef09d45ba1206712c7dbda5829562d3fea7726935d3289d343232ecb0d3", size = 2822830, upload-time = "2025-09-29T21:12:24.406Z" }, - { url = "https://files.pythonhosted.org/packages/57/17/bbeab391100331950a96ce55cfbbff27d781c1b85ebafb4167eae50d9fe3/fonttools-4.60.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8a44788d9d91df72d1a5eac49b31aeb887a5f4aab761b4cffc4196c74907ea85", size = 2345524, upload-time = "2025-09-29T21:12:26.819Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2e/d4831caa96d85a84dd0da1d9f90d81cec081f551e0ea216df684092c6c97/fonttools-4.60.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e852d9dda9f93ad3651ae1e3bb770eac544ec93c3807888798eccddf84596537", size = 4843490, upload-time = "2025-09-29T21:12:29.123Z" }, - { url = "https://files.pythonhosted.org/packages/49/13/5e2ea7c7a101b6fc3941be65307ef8df92cbbfa6ec4804032baf1893b434/fonttools-4.60.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:154cb6ee417e417bf5f7c42fe25858c9140c26f647c7347c06f0cc2d47eff003", size = 4944184, upload-time = "2025-09-29T21:12:31.414Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2b/cf9603551c525b73fc47c52ee0b82a891579a93d9651ed694e4e2cd08bb8/fonttools-4.60.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5664fd1a9ea7f244487ac8f10340c4e37664675e8667d6fee420766e0fb3cf08", size = 4890218, upload-time = "2025-09-29T21:12:33.936Z" }, - { url = "https://files.pythonhosted.org/packages/fd/2f/933d2352422e25f2376aae74f79eaa882a50fb3bfef3c0d4f50501267101/fonttools-4.60.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583b7f8e3c49486e4d489ad1deacfb8d5be54a8ef34d6df824f6a171f8511d99", size = 4999324, upload-time = "2025-09-29T21:12:36.637Z" }, - { url = "https://files.pythonhosted.org/packages/38/99/234594c0391221f66216bc2c886923513b3399a148defaccf81dc3be6560/fonttools-4.60.1-cp314-cp314-win32.whl", hash = "sha256:66929e2ea2810c6533a5184f938502cfdaea4bc3efb7130d8cc02e1c1b4108d6", size = 2220861, upload-time = "2025-09-29T21:12:39.108Z" }, - { url = "https://files.pythonhosted.org/packages/3e/1d/edb5b23726dde50fc4068e1493e4fc7658eeefcaf75d4c5ffce067d07ae5/fonttools-4.60.1-cp314-cp314-win_amd64.whl", hash = "sha256:f3d5be054c461d6a2268831f04091dc82753176f6ea06dc6047a5e168265a987", size = 2270934, upload-time = "2025-09-29T21:12:41.339Z" }, - { url = "https://files.pythonhosted.org/packages/fb/da/1392aaa2170adc7071fe7f9cfd181a5684a7afcde605aebddf1fb4d76df5/fonttools-4.60.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b6379e7546ba4ae4b18f8ae2b9bc5960936007a1c0e30b342f662577e8bc3299", size = 2894340, upload-time = "2025-09-29T21:12:43.774Z" }, - { url = "https://files.pythonhosted.org/packages/bf/a7/3b9f16e010d536ce567058b931a20b590d8f3177b2eda09edd92e392375d/fonttools-4.60.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9d0ced62b59e0430b3690dbc5373df1c2aa7585e9a8ce38eff87f0fd993c5b01", size = 2375073, upload-time = "2025-09-29T21:12:46.437Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b5/e9bcf51980f98e59bb5bb7c382a63c6f6cac0eec5f67de6d8f2322382065/fonttools-4.60.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:875cb7764708b3132637f6c5fb385b16eeba0f7ac9fa45a69d35e09b47045801", size = 4849758, upload-time = "2025-09-29T21:12:48.694Z" }, - { url = "https://files.pythonhosted.org/packages/e3/dc/1d2cf7d1cba82264b2f8385db3f5960e3d8ce756b4dc65b700d2c496f7e9/fonttools-4.60.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a184b2ea57b13680ab6d5fbde99ccef152c95c06746cb7718c583abd8f945ccc", size = 5085598, upload-time = "2025-09-29T21:12:51.081Z" }, - { url = "https://files.pythonhosted.org/packages/5d/4d/279e28ba87fb20e0c69baf72b60bbf1c4d873af1476806a7b5f2b7fac1ff/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:026290e4ec76583881763fac284aca67365e0be9f13a7fb137257096114cb3bc", size = 4957603, upload-time = "2025-09-29T21:12:53.423Z" }, - { url = "https://files.pythonhosted.org/packages/78/d4/ff19976305e0c05aa3340c805475abb00224c954d3c65e82c0a69633d55d/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0e8817c7d1a0c2eedebf57ef9a9896f3ea23324769a9a2061a80fe8852705ed", size = 4974184, upload-time = "2025-09-29T21:12:55.962Z" }, - { url = "https://files.pythonhosted.org/packages/63/22/8553ff6166f5cd21cfaa115aaacaa0dc73b91c079a8cfd54a482cbc0f4f5/fonttools-4.60.1-cp314-cp314t-win32.whl", hash = "sha256:1410155d0e764a4615774e5c2c6fc516259fe3eca5882f034eb9bfdbee056259", size = 2282241, upload-time = "2025-09-29T21:12:58.179Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cb/fa7b4d148e11d5a72761a22e595344133e83a9507a4c231df972e657579b/fonttools-4.60.1-cp314-cp314t-win_amd64.whl", hash = "sha256:022beaea4b73a70295b688f817ddc24ed3e3418b5036ffcd5658141184ef0d0c", size = 2345760, upload-time = "2025-09-29T21:13:00.375Z" }, - { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" }, +version = "4.61.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/f9/0e84d593c0e12244150280a630999835a64f2852276161b62a0f98318de0/fonttools-4.61.0.tar.gz", hash = "sha256:ec520a1f0c7758d7a858a00f090c1745f6cde6a7c5e76fb70ea4044a15f712e7", size = 3561884, upload-time = "2025-11-28T17:05:49.491Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/f3/91bba2721fb173fc68e09d15b6ccf3ad4f83d127fbff579be7e5984888a6/fonttools-4.61.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dc25a4a9c1225653e4431a9413d0381b1c62317b0f543bdcec24e1991f612f33", size = 2850151, upload-time = "2025-11-28T17:04:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/f5/8c/a1691dec01038ac7e7bb3ab83300dcc5087b11d8f48640928c02a873eb92/fonttools-4.61.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b493c32d2555e9944ec1b911ea649ff8f01a649ad9cba6c118d6798e932b3f0", size = 2389769, upload-time = "2025-11-28T17:04:16.443Z" }, + { url = "https://files.pythonhosted.org/packages/2d/dd/5bb369a44319d92ba25612511eb8ed2a6fa75239979e0388907525626902/fonttools-4.61.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad751319dc532a79bdf628b8439af167181b4210a0cd28a8935ca615d9fdd727", size = 4893189, upload-time = "2025-11-28T17:04:18.398Z" }, + { url = "https://files.pythonhosted.org/packages/5e/02/51373fa8846bd22bb54e5efb30a824b417b058083f775a194a432f21a45f/fonttools-4.61.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2de14557d113faa5fb519f7f29c3abe4d69c17fe6a5a2595cc8cda7338029219", size = 4854415, upload-time = "2025-11-28T17:04:20.421Z" }, + { url = "https://files.pythonhosted.org/packages/8b/64/9cdbbb804577a7e6191448851c57e6a36eb02aa4bf6a9668b528c968e44e/fonttools-4.61.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:59587bbe455dbdf75354a9dbca1697a35a8903e01fab4248d6b98a17032cee52", size = 4870927, upload-time = "2025-11-28T17:04:22.625Z" }, + { url = "https://files.pythonhosted.org/packages/92/68/e40b22919dc96dc30a70b58fec609ab85112de950bdecfadf8dd478c5a88/fonttools-4.61.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:46cb3d9279f758ac0cf671dc3482da877104b65682679f01b246515db03dbb72", size = 4988674, upload-time = "2025-11-28T17:04:24.675Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5c/e857349ce8aedb2451b9448282e86544b2b7f1c8b10ea0fe49b7cb369b72/fonttools-4.61.0-cp310-cp310-win32.whl", hash = "sha256:58b4f1b78dfbfe855bb8a6801b31b8cdcca0e2847ec769ad8e0b0b692832dd3b", size = 1497663, upload-time = "2025-11-28T17:04:26.598Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0c/62961d5fe6f764d6cbc387ef2c001f5f610808c7aded837409836c0b3e7c/fonttools-4.61.0-cp310-cp310-win_amd64.whl", hash = "sha256:68704a8bbe0b61976262b255e90cde593dc0fe3676542d9b4d846bad2a890a76", size = 1546143, upload-time = "2025-11-28T17:04:28.432Z" }, + { url = "https://files.pythonhosted.org/packages/fd/be/5aa89cdddf2863d8afbdc19eb8ec5d8d35d40eeeb8e6cf52c5ff1c2dbd33/fonttools-4.61.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a32a16951cbf113d38f1dd8551b277b6e06e0f6f776fece0f99f746d739e1be3", size = 2847553, upload-time = "2025-11-28T17:04:30.539Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3e/6ff643b07cead1236a534f51291ae2981721cf419135af5b740c002a66dd/fonttools-4.61.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:328a9c227984bebaf69f3ac9062265f8f6acc7ddf2e4e344c63358579af0aa3d", size = 2388298, upload-time = "2025-11-28T17:04:32.161Z" }, + { url = "https://files.pythonhosted.org/packages/c3/15/fca8dfbe7b482e6f240b1aad0ed7c6e2e75e7a28efa3d3a03b570617b5e5/fonttools-4.61.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f0bafc8a3b3749c69cc610e5aa3da832d39c2a37a68f03d18ec9a02ecaac04a", size = 5054133, upload-time = "2025-11-28T17:04:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a2/821c61c691b21fd09e07528a9a499cc2b075ac83ddb644aa16c9875a64bc/fonttools-4.61.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b5ca59b7417d149cf24e4c1933c9f44b2957424fc03536f132346d5242e0ebe5", size = 5031410, upload-time = "2025-11-28T17:04:36.141Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/8b16339e93d03c732c8a23edefe3061b17a5f9107ddc47a3215ecd054cac/fonttools-4.61.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:df8cbce85cf482eb01f4551edca978c719f099c623277bda8332e5dbe7dba09d", size = 5030005, upload-time = "2025-11-28T17:04:38.314Z" }, + { url = "https://files.pythonhosted.org/packages/ac/eb/d4e150427bdaa147755239c931bbce829a88149ade5bfd8a327afe565567/fonttools-4.61.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7fb5b84f48a6a733ca3d7f41aa9551908ccabe8669ffe79586560abcc00a9cfd", size = 5154026, upload-time = "2025-11-28T17:04:40.34Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5f/3dd00ce0dba6759943c707b1830af8c0bcf6f8f1a9fe46cb82e7ac2aaa74/fonttools-4.61.0-cp311-cp311-win32.whl", hash = "sha256:787ef9dfd1ea9fe49573c272412ae5f479d78e671981819538143bec65863865", size = 2276035, upload-time = "2025-11-28T17:04:42.59Z" }, + { url = "https://files.pythonhosted.org/packages/4e/44/798c472f096ddf12955eddb98f4f7c906e7497695d04ce073ddf7161d134/fonttools-4.61.0-cp311-cp311-win_amd64.whl", hash = "sha256:14fafda386377b6131d9e448af42d0926bad47e038de0e5ba1d58c25d621f028", size = 2327290, upload-time = "2025-11-28T17:04:44.57Z" }, + { url = "https://files.pythonhosted.org/packages/00/5d/19e5939f773c7cb05480fe2e881d63870b63ee2b4bdb9a77d55b1d36c7b9/fonttools-4.61.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e24a1565c4e57111ec7f4915f8981ecbb61adf66a55f378fdc00e206059fcfef", size = 2846930, upload-time = "2025-11-28T17:04:46.639Z" }, + { url = "https://files.pythonhosted.org/packages/25/b2/0658faf66f705293bd7e739a4f038302d188d424926be9c59bdad945664b/fonttools-4.61.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2bfacb5351303cae9f072ccf3fc6ecb437a6f359c0606bae4b1ab6715201d87", size = 2383016, upload-time = "2025-11-28T17:04:48.525Z" }, + { url = "https://files.pythonhosted.org/packages/29/a3/1fa90b95b690f0d7541f48850adc40e9019374d896c1b8148d15012b2458/fonttools-4.61.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0bdcf2e29d65c26299cc3d502f4612365e8b90a939f46cd92d037b6cb7bb544a", size = 4949425, upload-time = "2025-11-28T17:04:50.482Z" }, + { url = "https://files.pythonhosted.org/packages/af/00/acf18c00f6c501bd6e05ee930f926186f8a8e268265407065688820f1c94/fonttools-4.61.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6cd0d9051b8ddaf7385f99dd82ec2a058e2b46cf1f1961e68e1ff20fcbb61af", size = 4999632, upload-time = "2025-11-28T17:04:52.508Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e0/19a2b86e54109b1d2ee8743c96a1d297238ae03243897bc5345c0365f34d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e074bc07c31406f45c418e17c1722e83560f181d122c412fa9e815df0ff74810", size = 4939438, upload-time = "2025-11-28T17:04:54.437Z" }, + { url = "https://files.pythonhosted.org/packages/04/35/7b57a5f57d46286360355eff8d6b88c64ab6331107f37a273a71c803798d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a9b78da5d5faa17e63b2404b77feeae105c1b7e75f26020ab7a27b76e02039f", size = 5088960, upload-time = "2025-11-28T17:04:56.348Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0e/6c5023eb2e0fe5d1ababc7e221e44acd3ff668781489cc1937a6f83d620a/fonttools-4.61.0-cp312-cp312-win32.whl", hash = "sha256:9821ed77bb676736b88fa87a737c97b6af06e8109667e625a4f00158540ce044", size = 2264404, upload-time = "2025-11-28T17:04:58.149Z" }, + { url = "https://files.pythonhosted.org/packages/36/0b/63273128c7c5df19b1e4cd92e0a1e6ea5bb74a400c4905054c96ad60a675/fonttools-4.61.0-cp312-cp312-win_amd64.whl", hash = "sha256:0011d640afa61053bc6590f9a3394bd222de7cfde19346588beabac374e9d8ac", size = 2314427, upload-time = "2025-11-28T17:04:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/17/45/334f0d7f181e5473cfb757e1b60f4e60e7fc64f28d406e5d364a952718c0/fonttools-4.61.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba774b8cbd8754f54b8eb58124e8bd45f736b2743325ab1a5229698942b9b433", size = 2841801, upload-time = "2025-11-28T17:05:01.621Z" }, + { url = "https://files.pythonhosted.org/packages/cc/63/97b9c78e1f79bc741d4efe6e51f13872d8edb2b36e1b9fb2bab0d4491bb7/fonttools-4.61.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c84b430616ed73ce46e9cafd0bf0800e366a3e02fb7e1ad7c1e214dbe3862b1f", size = 2379024, upload-time = "2025-11-28T17:05:03.668Z" }, + { url = "https://files.pythonhosted.org/packages/4e/80/c87bc524a90dbeb2a390eea23eae448286983da59b7e02c67fa0ca96a8c5/fonttools-4.61.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b2b734d8391afe3c682320840c8191de9bd24e7eb85768dd4dc06ed1b63dbb1b", size = 4923706, upload-time = "2025-11-28T17:05:05.494Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f6/a3b0374811a1de8c3f9207ec88f61ad1bb96f938ed89babae26c065c2e46/fonttools-4.61.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5c5fff72bf31b0e558ed085e4fd7ed96eb85881404ecc39ed2a779e7cf724eb", size = 4979751, upload-time = "2025-11-28T17:05:07.665Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3b/30f63b4308b449091573285f9d27619563a84f399946bca3eadc9554afbe/fonttools-4.61.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:14a290c5c93fcab76b7f451e6a4b7721b712d90b3b5ed6908f1abcf794e90d6d", size = 4921113, upload-time = "2025-11-28T17:05:09.551Z" }, + { url = "https://files.pythonhosted.org/packages/41/6c/58e6e9b7d9d8bf2d7010bd7bb493060b39b02a12d1cda64a8bfb116ce760/fonttools-4.61.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:13e3e20a5463bfeb77b3557d04b30bd6a96a6bb5c15c7b2e7908903e69d437a0", size = 5063183, upload-time = "2025-11-28T17:05:11.677Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e3/52c790ab2b07492df059947a1fd7778e105aac5848c0473029a4d20481a2/fonttools-4.61.0-cp313-cp313-win32.whl", hash = "sha256:6781e7a4bb010be1cd69a29927b0305c86b843395f2613bdabe115f7d6ea7f34", size = 2263159, upload-time = "2025-11-28T17:05:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1f/116013b200fbeba871046554d5d2a45fefa69a05c40e9cdfd0d4fff53edc/fonttools-4.61.0-cp313-cp313-win_amd64.whl", hash = "sha256:c53b47834ae41e8e4829171cc44fec0fdf125545a15f6da41776b926b9645a9a", size = 2313530, upload-time = "2025-11-28T17:05:14.848Z" }, + { url = "https://files.pythonhosted.org/packages/d3/99/59b1e25987787cb714aa9457cee4c9301b7c2153f0b673e2b8679d37669d/fonttools-4.61.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:96dfc9bc1f2302224e48e6ee37e656eddbab810b724b52e9d9c13a57a6abad01", size = 2841429, upload-time = "2025-11-28T17:05:16.671Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/4c1911d4332c8a144bb3b44416e274ccca0e297157c971ea1b3fbb855590/fonttools-4.61.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3b2065d94e5d63aafc2591c8b6ccbdb511001d9619f1bca8ad39b745ebeb5efa", size = 2378987, upload-time = "2025-11-28T17:05:18.69Z" }, + { url = "https://files.pythonhosted.org/packages/24/b0/f442e90fde5d2af2ae0cb54008ab6411edc557ee33b824e13e1d04925ac9/fonttools-4.61.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e0d87e81e4d869549585ba0beb3f033718501c1095004f5e6aef598d13ebc216", size = 4873270, upload-time = "2025-11-28T17:05:20.625Z" }, + { url = "https://files.pythonhosted.org/packages/bb/04/f5d5990e33053c8a59b90b1d7e10ad9b97a73f42c745304da0e709635fab/fonttools-4.61.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cfa2eb9bae650e58f0e8ad53c49d19a844d6034d6b259f30f197238abc1ccee", size = 4968270, upload-time = "2025-11-28T17:05:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/94/9f/2091402e0d27c9c8c4bab5de0e5cd146d9609a2d7d1c666bbb75c0011c1a/fonttools-4.61.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4238120002e68296d55e091411c09eab94e111c8ce64716d17df53fd0eb3bb3d", size = 4919799, upload-time = "2025-11-28T17:05:24.437Z" }, + { url = "https://files.pythonhosted.org/packages/a8/72/86adab22fde710b829f8ffbc8f264df01928e5b7a8f6177fa29979ebf256/fonttools-4.61.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b6ceac262cc62bec01b3bb59abccf41b24ef6580869e306a4e88b7e56bb4bdda", size = 5030966, upload-time = "2025-11-28T17:05:26.115Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a7/7c8e31b003349e845b853f5e0a67b95ff6b052fa4f5224f8b72624f5ac69/fonttools-4.61.0-cp314-cp314-win32.whl", hash = "sha256:adbb4ecee1a779469a77377bbe490565effe8fce6fb2e6f95f064de58f8bac85", size = 2267243, upload-time = "2025-11-28T17:05:27.807Z" }, + { url = "https://files.pythonhosted.org/packages/20/ee/f434fe7749360497c52b7dcbcfdbccdaab0a71c59f19d572576066717122/fonttools-4.61.0-cp314-cp314-win_amd64.whl", hash = "sha256:02bdf8e04d1a70476564b8640380f04bb4ac74edc1fc71f1bacb840b3e398ee9", size = 2318822, upload-time = "2025-11-28T17:05:29.882Z" }, + { url = "https://files.pythonhosted.org/packages/33/b3/c16255320255e5c1863ca2b2599bb61a46e2f566db0bbb9948615a8fe692/fonttools-4.61.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:627216062d90ab0d98215176d8b9562c4dd5b61271d35f130bcd30f6a8aaa33a", size = 2924917, upload-time = "2025-11-28T17:05:31.46Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b8/08067ae21de705a817777c02ef36ab0b953cbe91d8adf134f9c2da75ed6d/fonttools-4.61.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7b446623c9cd5f14a59493818eaa80255eec2468c27d2c01b56e05357c263195", size = 2413576, upload-time = "2025-11-28T17:05:33.343Z" }, + { url = "https://files.pythonhosted.org/packages/42/f1/96ff43f92addce2356780fdc203f2966206f3d22ea20e242c27826fd7442/fonttools-4.61.0-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70e2a0c0182ee75e493ef33061bfebf140ea57e035481d2f95aa03b66c7a0e05", size = 4877447, upload-time = "2025-11-28T17:05:35.278Z" }, + { url = "https://files.pythonhosted.org/packages/d0/1e/a3d8e51ed9ccfd7385e239ae374b78d258a0fb82d82cab99160a014a45d1/fonttools-4.61.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9064b0f55b947e929ac669af5311ab1f26f750214db6dd9a0c97e091e918f486", size = 5095681, upload-time = "2025-11-28T17:05:37.142Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f6/d256bd6c1065c146a0bdddf1c62f542e08ae5b3405dbf3fcc52be272f674/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5e45a824ce14b90510024d0d39dae51bd4fbb54c42a9334ea8c8cf4d95cbe", size = 4974140, upload-time = "2025-11-28T17:05:39.5Z" }, + { url = "https://files.pythonhosted.org/packages/5d/0c/96633eb4b26f138cc48561c6e0c44b4ea48acea56b20b507d6b14f8e80ce/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e5ca8c62efdec7972dfdfd454415c4db49b89aeaefaaacada432f3b7eea9866", size = 5001741, upload-time = "2025-11-28T17:05:41.424Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/3b536bad3be4f26186f296e749ff17bad3e6d57232c104d752d24b2e265b/fonttools-4.61.0-cp314-cp314t-win32.whl", hash = "sha256:63c7125d31abe3e61d7bb917329b5543c5b3448db95f24081a13aaf064360fc8", size = 2330707, upload-time = "2025-11-28T17:05:43.548Z" }, + { url = "https://files.pythonhosted.org/packages/18/ea/e6b9ac610451ee9f04477c311ad126de971f6112cb579fa391d2a8edb00b/fonttools-4.61.0-cp314-cp314t-win_amd64.whl", hash = "sha256:67d841aa272be5500de7f447c40d1d8452783af33b4c3599899319f6ef9ad3c1", size = 2395950, upload-time = "2025-11-28T17:05:45.638Z" }, + { url = "https://files.pythonhosted.org/packages/0c/14/634f7daea5ffe6a5f7a0322ba8e1a0e23c9257b80aa91458107896d1dfc7/fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635", size = 1144485, upload-time = "2025-11-28T17:05:47.573Z" }, ] [[package]] @@ -2554,124 +2554,124 @@ wheels = [ [[package]] name = "rpds-py" -version = "0.29.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/33/23b3b3419b6a3e0f559c7c0d2ca8fc1b9448382b25245033788785921332/rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359", size = 69359, upload-time = "2025-11-16T14:50:39.532Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/7a/c5b2ff381b74bc742768e8d870f26babac4ef256ba160bdbf8d57af56461/rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113", size = 372385, upload-time = "2025-11-16T14:47:36.287Z" }, - { url = "https://files.pythonhosted.org/packages/28/36/531f1eb4d5bed4a9c150f363a7ec4a98d2dc746151bba5473bc38ee85dec/rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9", size = 362869, upload-time = "2025-11-16T14:47:38.196Z" }, - { url = "https://files.pythonhosted.org/packages/54/df/7e9c0493a2015d9c82807a2d5f023ea9774e27a4c15b33ef1cdb7456138d/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f", size = 391582, upload-time = "2025-11-16T14:47:39.746Z" }, - { url = "https://files.pythonhosted.org/packages/15/38/42a981c3592ef46fbd7e17adbf8730cc5ec87e6aa1770c658c44bbb52960/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545", size = 405685, upload-time = "2025-11-16T14:47:41.472Z" }, - { url = "https://files.pythonhosted.org/packages/12/45/628b8c15856c3849c3f52ec6dac93c046ed5faeed4a435af03b70525fd29/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d", size = 527067, upload-time = "2025-11-16T14:47:43.036Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ba/6b56d09badeabd95098016d72a437d4a0fd82d4672ce92a7607df5d70a42/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae", size = 412532, upload-time = "2025-11-16T14:47:44.484Z" }, - { url = "https://files.pythonhosted.org/packages/f1/39/2f1f3db92888314b50b8f9641f679188bd24b3665a8cb9923b7201ae8011/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756", size = 392736, upload-time = "2025-11-16T14:47:46.053Z" }, - { url = "https://files.pythonhosted.org/packages/60/43/3c3b1dcd827e50f2ae28786d846b8a351080d8a69a3b49bc10ae44cc39b1/rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea", size = 406300, upload-time = "2025-11-16T14:47:47.268Z" }, - { url = "https://files.pythonhosted.org/packages/da/02/bc96021b67f8525e6bcdd68935c4543ada61e1f3dcb067ed037d68b8c6d2/rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2", size = 423641, upload-time = "2025-11-16T14:47:48.878Z" }, - { url = "https://files.pythonhosted.org/packages/38/e9/c435ddb602ced19a80b8277a41371734f33ad3f91cc4ceb4d82596800a3c/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2", size = 574153, upload-time = "2025-11-16T14:47:50.435Z" }, - { url = "https://files.pythonhosted.org/packages/84/82/dc3c32e1f89ecba8a59600d4cd65fe0ad81b6c636ccdbf6cd177fd6a7bac/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b", size = 600304, upload-time = "2025-11-16T14:47:51.599Z" }, - { url = "https://files.pythonhosted.org/packages/35/98/785290e0b7142470735dc1b1f68fb33aae29e5296f062c88396eedf796c8/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a", size = 562211, upload-time = "2025-11-16T14:47:53.094Z" }, - { url = "https://files.pythonhosted.org/packages/30/58/4eeddcb0737c6875f3e30c65dc9d7e7a10dfd5779646a990fa602c6d56c5/rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef", size = 221803, upload-time = "2025-11-16T14:47:54.404Z" }, - { url = "https://files.pythonhosted.org/packages/54/77/b35a8dbdcbeb32505500547cdafaa9f8863e85f8faac50ef34464ec5a256/rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950", size = 235530, upload-time = "2025-11-16T14:47:56.061Z" }, - { url = "https://files.pythonhosted.org/packages/36/ab/7fb95163a53ab122c74a7c42d2d2f012819af2cf3deb43fb0d5acf45cc1a/rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437", size = 372344, upload-time = "2025-11-16T14:47:57.279Z" }, - { url = "https://files.pythonhosted.org/packages/b3/45/f3c30084c03b0d0f918cb4c5ae2c20b0a148b51ba2b3f6456765b629bedd/rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383", size = 363041, upload-time = "2025-11-16T14:47:58.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e9/4d044a1662608c47a87cbb37b999d4d5af54c6d6ebdda93a4d8bbf8b2a10/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c", size = 391775, upload-time = "2025-11-16T14:48:00.197Z" }, - { url = "https://files.pythonhosted.org/packages/50/c9/7616d3ace4e6731aeb6e3cd85123e03aec58e439044e214b9c5c60fd8eb1/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b", size = 405624, upload-time = "2025-11-16T14:48:01.496Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e2/6d7d6941ca0843609fd2d72c966a438d6f22617baf22d46c3d2156c31350/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311", size = 527894, upload-time = "2025-11-16T14:48:03.167Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f7/aee14dc2db61bb2ae1e3068f134ca9da5f28c586120889a70ff504bb026f/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588", size = 412720, upload-time = "2025-11-16T14:48:04.413Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e2/2293f236e887c0360c2723d90c00d48dee296406994d6271faf1712e94ec/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed", size = 392945, upload-time = "2025-11-16T14:48:06.252Z" }, - { url = "https://files.pythonhosted.org/packages/14/cd/ceea6147acd3bd1fd028d1975228f08ff19d62098078d5ec3eed49703797/rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63", size = 406385, upload-time = "2025-11-16T14:48:07.575Z" }, - { url = "https://files.pythonhosted.org/packages/52/36/fe4dead19e45eb77a0524acfdbf51e6cda597b26fc5b6dddbff55fbbb1a5/rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2", size = 423943, upload-time = "2025-11-16T14:48:10.175Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7b/4551510803b582fa4abbc8645441a2d15aa0c962c3b21ebb380b7e74f6a1/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f", size = 574204, upload-time = "2025-11-16T14:48:11.499Z" }, - { url = "https://files.pythonhosted.org/packages/64/ba/071ccdd7b171e727a6ae079f02c26f75790b41555f12ca8f1151336d2124/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca", size = 600587, upload-time = "2025-11-16T14:48:12.822Z" }, - { url = "https://files.pythonhosted.org/packages/03/09/96983d48c8cf5a1e03c7d9cc1f4b48266adfb858ae48c7c2ce978dbba349/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95", size = 562287, upload-time = "2025-11-16T14:48:14.108Z" }, - { url = "https://files.pythonhosted.org/packages/40/f0/8c01aaedc0fa92156f0391f39ea93b5952bc0ec56b897763858f95da8168/rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4", size = 221394, upload-time = "2025-11-16T14:48:15.374Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/a8b21c54c7d234efdc83dc034a4d7cd9668e3613b6316876a29b49dece71/rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60", size = 235713, upload-time = "2025-11-16T14:48:16.636Z" }, - { url = "https://files.pythonhosted.org/packages/a7/1f/df3c56219523947b1be402fa12e6323fe6d61d883cf35d6cb5d5bb6db9d9/rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c", size = 229157, upload-time = "2025-11-16T14:48:17.891Z" }, - { url = "https://files.pythonhosted.org/packages/3c/50/bc0e6e736d94e420df79be4deb5c9476b63165c87bb8f19ef75d100d21b3/rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954", size = 376000, upload-time = "2025-11-16T14:48:19.141Z" }, - { url = "https://files.pythonhosted.org/packages/3e/3a/46676277160f014ae95f24de53bed0e3b7ea66c235e7de0b9df7bd5d68ba/rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c", size = 360575, upload-time = "2025-11-16T14:48:20.443Z" }, - { url = "https://files.pythonhosted.org/packages/75/ba/411d414ed99ea1afdd185bbabeeaac00624bd1e4b22840b5e9967ade6337/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d", size = 392159, upload-time = "2025-11-16T14:48:22.12Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b1/e18aa3a331f705467a48d0296778dc1fea9d7f6cf675bd261f9a846c7e90/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5", size = 410602, upload-time = "2025-11-16T14:48:23.563Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6c/04f27f0c9f2299274c76612ac9d2c36c5048bb2c6c2e52c38c60bf3868d9/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e", size = 515808, upload-time = "2025-11-16T14:48:24.949Z" }, - { url = "https://files.pythonhosted.org/packages/83/56/a8412aa464fb151f8bc0d91fb0bb888adc9039bd41c1c6ba8d94990d8cf8/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83", size = 416015, upload-time = "2025-11-16T14:48:26.782Z" }, - { url = "https://files.pythonhosted.org/packages/04/4c/f9b8a05faca3d9e0a6397c90d13acb9307c9792b2bff621430c58b1d6e76/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949", size = 395325, upload-time = "2025-11-16T14:48:28.055Z" }, - { url = "https://files.pythonhosted.org/packages/34/60/869f3bfbf8ed7b54f1ad9a5543e0fdffdd40b5a8f587fe300ee7b4f19340/rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181", size = 410160, upload-time = "2025-11-16T14:48:29.338Z" }, - { url = "https://files.pythonhosted.org/packages/91/aa/e5b496334e3aba4fe4c8a80187b89f3c1294c5c36f2a926da74338fa5a73/rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c", size = 425309, upload-time = "2025-11-16T14:48:30.691Z" }, - { url = "https://files.pythonhosted.org/packages/85/68/4e24a34189751ceb6d66b28f18159922828dd84155876551f7ca5b25f14f/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7", size = 574644, upload-time = "2025-11-16T14:48:31.964Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/474a005ea4ea9c3b4f17b6108b6b13cebfc98ebaff11d6e1b193204b3a93/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19", size = 601605, upload-time = "2025-11-16T14:48:33.252Z" }, - { url = "https://files.pythonhosted.org/packages/f4/b1/c56f6a9ab8c5f6bb5c65c4b5f8229167a3a525245b0773f2c0896686b64e/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0", size = 564593, upload-time = "2025-11-16T14:48:34.643Z" }, - { url = "https://files.pythonhosted.org/packages/b3/13/0494cecce4848f68501e0a229432620b4b57022388b071eeff95f3e1e75b/rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7", size = 223853, upload-time = "2025-11-16T14:48:36.419Z" }, - { url = "https://files.pythonhosted.org/packages/1f/6a/51e9aeb444a00cdc520b032a28b07e5f8dc7bc328b57760c53e7f96997b4/rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977", size = 239895, upload-time = "2025-11-16T14:48:37.956Z" }, - { url = "https://files.pythonhosted.org/packages/d1/d4/8bce56cdad1ab873e3f27cb31c6a51d8f384d66b022b820525b879f8bed1/rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7", size = 230321, upload-time = "2025-11-16T14:48:39.71Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d9/c5de60d9d371bbb186c3e9bf75f4fc5665e11117a25a06a6b2e0afb7380e/rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61", size = 375710, upload-time = "2025-11-16T14:48:41.063Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b3/0860cdd012291dc21272895ce107f1e98e335509ba986dd83d72658b82b9/rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154", size = 360582, upload-time = "2025-11-16T14:48:42.423Z" }, - { url = "https://files.pythonhosted.org/packages/92/8a/a18c2f4a61b3407e56175f6aab6deacdf9d360191a3d6f38566e1eaf7266/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014", size = 391172, upload-time = "2025-11-16T14:48:43.75Z" }, - { url = "https://files.pythonhosted.org/packages/fd/49/e93354258508c50abc15cdcd5fcf7ac4117f67bb6233ad7859f75e7372a0/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6", size = 409586, upload-time = "2025-11-16T14:48:45.498Z" }, - { url = "https://files.pythonhosted.org/packages/5a/8d/a27860dae1c19a6bdc901f90c81f0d581df1943355802961a57cdb5b6cd1/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c", size = 516339, upload-time = "2025-11-16T14:48:47.308Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ad/a75e603161e79b7110c647163d130872b271c6b28712c803c65d492100f7/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866", size = 416201, upload-time = "2025-11-16T14:48:48.615Z" }, - { url = "https://files.pythonhosted.org/packages/b9/42/555b4ee17508beafac135c8b450816ace5a96194ce97fefc49d58e5652ea/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295", size = 395095, upload-time = "2025-11-16T14:48:50.027Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f0/c90b671b9031e800ec45112be42ea9f027f94f9ac25faaac8770596a16a1/rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b", size = 410077, upload-time = "2025-11-16T14:48:51.515Z" }, - { url = "https://files.pythonhosted.org/packages/3d/80/9af8b640b81fe21e6f718e9dec36c0b5f670332747243130a5490f292245/rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55", size = 424548, upload-time = "2025-11-16T14:48:53.237Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0b/b5647446e991736e6a495ef510e6710df91e880575a586e763baeb0aa770/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd", size = 573661, upload-time = "2025-11-16T14:48:54.769Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b3/1b1c9576839ff583d1428efbf59f9ee70498d8ce6c0b328ac02f1e470879/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea", size = 600937, upload-time = "2025-11-16T14:48:56.247Z" }, - { url = "https://files.pythonhosted.org/packages/6c/7b/b6cfca2f9fee4c4494ce54f7fb1b9f578867495a9aa9fc0d44f5f735c8e0/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22", size = 564496, upload-time = "2025-11-16T14:48:57.691Z" }, - { url = "https://files.pythonhosted.org/packages/b9/fb/ba29ec7f0f06eb801bac5a23057a9ff7670623b5e8013bd59bec4aa09de8/rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7", size = 223126, upload-time = "2025-11-16T14:48:59.058Z" }, - { url = "https://files.pythonhosted.org/packages/3c/6b/0229d3bed4ddaa409e6d90b0ae967ed4380e4bdd0dad6e59b92c17d42457/rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e", size = 239771, upload-time = "2025-11-16T14:49:00.872Z" }, - { url = "https://files.pythonhosted.org/packages/e4/38/d2868f058b164f8efd89754d85d7b1c08b454f5c07ac2e6cc2e9bd4bd05b/rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2", size = 229994, upload-time = "2025-11-16T14:49:02.673Z" }, - { url = "https://files.pythonhosted.org/packages/52/91/5de91c5ec7d41759beec9b251630824dbb8e32d20c3756da1a9a9d309709/rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c", size = 365886, upload-time = "2025-11-16T14:49:04.133Z" }, - { url = "https://files.pythonhosted.org/packages/85/7c/415d8c1b016d5f47ecec5145d9d6d21002d39dce8761b30f6c88810b455a/rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b", size = 355262, upload-time = "2025-11-16T14:49:05.543Z" }, - { url = "https://files.pythonhosted.org/packages/3d/14/bf83e2daa4f980e4dc848aed9299792a8b84af95e12541d9e7562f84a6ef/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0", size = 384826, upload-time = "2025-11-16T14:49:07.301Z" }, - { url = "https://files.pythonhosted.org/packages/33/b8/53330c50a810ae22b4fbba5e6cf961b68b9d72d9bd6780a7c0a79b070857/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4", size = 394234, upload-time = "2025-11-16T14:49:08.782Z" }, - { url = "https://files.pythonhosted.org/packages/cc/32/01e2e9645cef0e584f518cfde4567563e57db2257244632b603f61b40e50/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688", size = 520008, upload-time = "2025-11-16T14:49:10.253Z" }, - { url = "https://files.pythonhosted.org/packages/98/c3/0d1b95a81affae2b10f950782e33a1fd2edd6ce2a479966cac98c9a66f57/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d", size = 409569, upload-time = "2025-11-16T14:49:12.478Z" }, - { url = "https://files.pythonhosted.org/packages/fa/60/aa3b8678f3f009f675b99174fa2754302a7fbfe749162e8043d111de2d88/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee", size = 385188, upload-time = "2025-11-16T14:49:13.88Z" }, - { url = "https://files.pythonhosted.org/packages/92/02/5546c1c8aa89c18d40c1fcffdcc957ba730dee53fb7c3ca3a46f114761d2/rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e", size = 398587, upload-time = "2025-11-16T14:49:15.339Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e0/ad6eeaf47e236eba052fa34c4073078b9e092bd44da6bbb35aaae9580669/rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb", size = 416641, upload-time = "2025-11-16T14:49:16.832Z" }, - { url = "https://files.pythonhosted.org/packages/1a/93/0acedfd50ad9cdd3879c615a6dc8c5f1ce78d2fdf8b87727468bb5bb4077/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967", size = 566683, upload-time = "2025-11-16T14:49:18.342Z" }, - { url = "https://files.pythonhosted.org/packages/62/53/8c64e0f340a9e801459fc6456821abc15b3582cb5dc3932d48705a9d9ac7/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e", size = 592730, upload-time = "2025-11-16T14:49:19.767Z" }, - { url = "https://files.pythonhosted.org/packages/85/ef/3109b6584f8c4b0d2490747c916df833c127ecfa82be04d9a40a376f2090/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a", size = 557361, upload-time = "2025-11-16T14:49:21.574Z" }, - { url = "https://files.pythonhosted.org/packages/ff/3b/61586475e82d57f01da2c16edb9115a618afe00ce86fe1b58936880b15af/rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb", size = 211227, upload-time = "2025-11-16T14:49:23.03Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3a/12dc43f13594a54ea0c9d7e9d43002116557330e3ad45bc56097ddf266e2/rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352", size = 225248, upload-time = "2025-11-16T14:49:24.841Z" }, - { url = "https://files.pythonhosted.org/packages/89/b1/0b1474e7899371d9540d3bbb2a499a3427ae1fc39c998563fe9035a1073b/rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1", size = 363731, upload-time = "2025-11-16T14:49:26.683Z" }, - { url = "https://files.pythonhosted.org/packages/28/12/3b7cf2068d0a334ed1d7b385a9c3c8509f4c2bcba3d4648ea71369de0881/rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8", size = 354343, upload-time = "2025-11-16T14:49:28.24Z" }, - { url = "https://files.pythonhosted.org/packages/eb/73/5afcf8924bc02a749416eda64e17ac9c9b28f825f4737385295a0e99b0c1/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626", size = 385406, upload-time = "2025-11-16T14:49:29.943Z" }, - { url = "https://files.pythonhosted.org/packages/c8/37/5db736730662508535221737a21563591b6f43c77f2e388951c42f143242/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7", size = 396162, upload-time = "2025-11-16T14:49:31.833Z" }, - { url = "https://files.pythonhosted.org/packages/70/0d/491c1017d14f62ce7bac07c32768d209a50ec567d76d9f383b4cfad19b80/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244", size = 517719, upload-time = "2025-11-16T14:49:33.804Z" }, - { url = "https://files.pythonhosted.org/packages/d7/25/b11132afcb17cd5d82db173f0c8dab270ffdfaba43e5ce7a591837ae9649/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17", size = 409498, upload-time = "2025-11-16T14:49:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/0f/7d/e6543cedfb2e6403a1845710a5ab0e0ccf8fc288e0b5af9a70bfe2c12053/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32", size = 382743, upload-time = "2025-11-16T14:49:36.704Z" }, - { url = "https://files.pythonhosted.org/packages/75/11/a4ebc9f654293ae9fefb83b2b6be7f3253e85ea42a5db2f77d50ad19aaeb/rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c", size = 400317, upload-time = "2025-11-16T14:49:39.132Z" }, - { url = "https://files.pythonhosted.org/packages/52/18/97677a60a81c7f0e5f64e51fb3f8271c5c8fcabf3a2df18e97af53d7c2bf/rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318", size = 416979, upload-time = "2025-11-16T14:49:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/f0/69/28ab391a9968f6c746b2a2db181eaa4d16afaa859fedc9c2f682d19f7e18/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212", size = 567288, upload-time = "2025-11-16T14:49:42.24Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d3/0c7afdcdb830eee94f5611b64e71354ffe6ac8df82d00c2faf2bfffd1d4e/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94", size = 593157, upload-time = "2025-11-16T14:49:43.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ac/a0fcbc2feed4241cf26d32268c195eb88ddd4bd862adfc9d4b25edfba535/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d", size = 554741, upload-time = "2025-11-16T14:49:45.557Z" }, - { url = "https://files.pythonhosted.org/packages/0f/f1/fcc24137c470df8588674a677f33719d5800ec053aaacd1de8a5d5d84d9e/rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1", size = 215508, upload-time = "2025-11-16T14:49:47.562Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c7/1d169b2045512eac019918fc1021ea07c30e84a4343f9f344e3e0aa8c788/rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b", size = 228125, upload-time = "2025-11-16T14:49:49.064Z" }, - { url = "https://files.pythonhosted.org/packages/be/36/0cec88aaba70ec4a6e381c444b0d916738497d27f0c30406e3d9fcbd3bc2/rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9", size = 221992, upload-time = "2025-11-16T14:49:50.777Z" }, - { url = "https://files.pythonhosted.org/packages/b1/fa/a2e524631717c9c0eb5d90d30f648cfba6b731047821c994acacb618406c/rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10", size = 366425, upload-time = "2025-11-16T14:49:52.691Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a4/6d43ebe0746ff694a30233f63f454aed1677bd50ab7a59ff6b2bb5ac61f2/rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a", size = 355282, upload-time = "2025-11-16T14:49:54.292Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a7/52fd8270e0320b09eaf295766ae81dd175f65394687906709b3e75c71d06/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79", size = 384968, upload-time = "2025-11-16T14:49:55.857Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7d/e6bc526b7a14e1ef80579a52c1d4ad39260a058a51d66c6039035d14db9d/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a", size = 394714, upload-time = "2025-11-16T14:49:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/f0ade3954e7db95c791e7eaf978aa7e08a756d2046e8bdd04d08146ed188/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310", size = 520136, upload-time = "2025-11-16T14:49:59.162Z" }, - { url = "https://files.pythonhosted.org/packages/87/b3/07122ead1b97009715ab9d4082be6d9bd9546099b2b03fae37c3116f72be/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b", size = 409250, upload-time = "2025-11-16T14:50:00.698Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c6/dcbee61fd1dc892aedcb1b489ba661313101aa82ec84b1a015d4c63ebfda/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808", size = 384940, upload-time = "2025-11-16T14:50:02.312Z" }, - { url = "https://files.pythonhosted.org/packages/47/11/914ecb6f3574cf9bf8b38aced4063e0f787d6e1eb30b181a7efbc6c1da9a/rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761", size = 399392, upload-time = "2025-11-16T14:50:03.829Z" }, - { url = "https://files.pythonhosted.org/packages/f5/fd/2f4bd9433f58f816434bb934313584caa47dbc6f03ce5484df8ac8980561/rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3", size = 416796, upload-time = "2025-11-16T14:50:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/79/a5/449f0281af33efa29d5c71014399d74842342ae908d8cd38260320167692/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9", size = 566843, upload-time = "2025-11-16T14:50:07.243Z" }, - { url = "https://files.pythonhosted.org/packages/ab/32/0a6a1ccee2e37fcb1b7ba9afde762b77182dbb57937352a729c6cd3cf2bb/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8", size = 593956, upload-time = "2025-11-16T14:50:09.029Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3d/eb820f95dce4306f07a495ede02fb61bef36ea201d9137d4fcd5ab94ec1e/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a", size = 557288, upload-time = "2025-11-16T14:50:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f8/b8ff786f40470462a252918e0836e0db903c28e88e3eec66bc4a7856ee5d/rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5", size = 211382, upload-time = "2025-11-16T14:50:12.827Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7f/1a65ae870bc9d0576aebb0c501ea5dccf1ae2178fe2821042150ebd2e707/rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2", size = 225919, upload-time = "2025-11-16T14:50:14.734Z" }, - { url = "https://files.pythonhosted.org/packages/f2/ac/b97e80bf107159e5b9ba9c91df1ab95f69e5e41b435f27bdd737f0d583ac/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d", size = 373963, upload-time = "2025-11-16T14:50:16.205Z" }, - { url = "https://files.pythonhosted.org/packages/40/5a/55e72962d5d29bd912f40c594e68880d3c7a52774b0f75542775f9250712/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3", size = 364644, upload-time = "2025-11-16T14:50:18.22Z" }, - { url = "https://files.pythonhosted.org/packages/99/2a/6b6524d0191b7fc1351c3c0840baac42250515afb48ae40c7ed15499a6a2/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43", size = 393847, upload-time = "2025-11-16T14:50:20.012Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b8/c5692a7df577b3c0c7faed7ac01ee3c608b81750fc5d89f84529229b6873/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf", size = 407281, upload-time = "2025-11-16T14:50:21.64Z" }, - { url = "https://files.pythonhosted.org/packages/f0/57/0546c6f84031b7ea08b76646a8e33e45607cc6bd879ff1917dc077bb881e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe", size = 529213, upload-time = "2025-11-16T14:50:23.219Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c1/01dd5f444233605555bc11fe5fed6a5c18f379f02013870c176c8e630a23/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760", size = 413808, upload-time = "2025-11-16T14:50:25.262Z" }, - { url = "https://files.pythonhosted.org/packages/aa/0a/60f98b06156ea2a7af849fb148e00fbcfdb540909a5174a5ed10c93745c7/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a", size = 394600, upload-time = "2025-11-16T14:50:26.956Z" }, - { url = "https://files.pythonhosted.org/packages/37/f1/dc9312fc9bec040ece08396429f2bd9e0977924ba7a11c5ad7056428465e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0", size = 408634, upload-time = "2025-11-16T14:50:28.989Z" }, - { url = "https://files.pythonhosted.org/packages/ed/41/65024c9fd40c89bb7d604cf73beda4cbdbcebe92d8765345dd65855b6449/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce", size = 426064, upload-time = "2025-11-16T14:50:30.674Z" }, - { url = "https://files.pythonhosted.org/packages/a2/e0/cf95478881fc88ca2fdbf56381d7df36567cccc39a05394beac72182cd62/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec", size = 575871, upload-time = "2025-11-16T14:50:33.428Z" }, - { url = "https://files.pythonhosted.org/packages/ea/c0/df88097e64339a0218b57bd5f9ca49898e4c394db756c67fccc64add850a/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed", size = 601702, upload-time = "2025-11-16T14:50:36.051Z" }, - { url = "https://files.pythonhosted.org/packages/87/f4/09ffb3ebd0cbb9e2c7c9b84d252557ecf434cd71584ee1e32f66013824df/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f", size = 564054, upload-time = "2025-11-16T14:50:37.733Z" }, +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] [[package]] From 6f768be1c713e355bbc8c4b8c73768c491d4bb99 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 1 Dec 2025 08:57:49 +0100 Subject: [PATCH 28/58] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20changes=20from=20#?= =?UTF-8?q?1336=20also=20in=20new=20SC=20device?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 28 +++++++++++++++-------- src/na/device/App.cpp | 1 + src/na/device/Device.cpp | 2 +- src/qdmi/sc/Device.cpp | 35 +++++++++++++++++++++++++++-- test/qdmi/test_driver.cpp | 6 ++--- 5 files changed, 57 insertions(+), 15 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index b079426e72..7853afc977 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -16,6 +16,7 @@ #include "mqt_sc_qdmi/device.h" +#include #include #include #include @@ -48,6 +49,9 @@ class Device final { /// @brief Private constructor to enforce the singleton pattern. Device(); + /// @brief The singleton instance. + static std::atomic instance; + public: // Delete move constructor and move assignment operator. Device(Device&&) = delete; @@ -56,18 +60,24 @@ class Device final { Device(const Device&) = delete; Device& operator=(const Device&) = delete; + /// @brief Destructor for the Device class. + ~Device(); + /** - * @brief Accesses the singleton Device instance. - * - * @return Device& Reference to the singleton Device instance. + * @brief Initializes the singleton instance. + * @details Must be called before `get()`. */ - [[nodiscard]] static Device& get() { - static Device instance; - return instance; - } + static void initialize(); - /// @brief Destructor for the Device class. - ~Device(); + /** + * @brief Destroys the singleton instance. + * @details After this call, `get()` must not be called until a new + * `initialize()` call. + */ + static void finalize(); + + /// @returns the singleton instance of the Device class. + [[nodiscard]] static auto get() -> Device&; /** * @brief Allocates a new device session. diff --git a/src/na/device/App.cpp b/src/na/device/App.cpp index 7b7e5c25e1..4783d4ffad 100644 --- a/src/na/device/App.cpp +++ b/src/na/device/App.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/src/na/device/Device.cpp b/src/na/device/Device.cpp index 8f5b918446..f8cce115b9 100644 --- a/src/na/device/Device.cpp +++ b/src/na/device/Device.cpp @@ -643,7 +643,7 @@ int MQT_NA_QDMI_device_initialize() { } int MQT_NA_QDMI_device_finalize() { - qdmi::Device::finalize(); + qdmi::na::Device::finalize(); return QDMI_SUCCESS; } diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index d5e214b379..a75d2c980d 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -17,6 +17,8 @@ #include "mqt_sc_qdmi/device.h" #include "qdmi/sc/DeviceMemberInitializers.hpp" +#include +#include #include #include #include @@ -90,6 +92,8 @@ // NOLINTEND(bugprone-macro-parentheses) namespace qdmi::sc { +std::atomic Device::instance = nullptr; + Device::Device() { // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer) INITIALIZE_NAME(name_); @@ -103,6 +107,30 @@ Device::~Device() { // Explicitly clear sessions before destruction to avoid spurious segfaults sessions_.clear(); } +void Device::initialize() { + // NOLINTNEXTLINE(misc-const-correctness) + Device* expected = nullptr; + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + auto* newInstance = new Device(); + if (!instance.compare_exchange_strong(expected, newInstance)) { + // Another thread won the race, so delete the instance we created. + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete newInstance; + } +} +void Device::finalize() { + // Atomically swap the instance pointer with nullptr and get the old value. + const Device* oldInstance = instance.exchange(nullptr); + // Delete the old instance if it existed. + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete oldInstance; +} +auto Device::get() -> Device& { + auto* loadedInstance = instance.load(); + assert(loadedInstance != nullptr && + "Device not initialized. Call `initialize()` first."); + return *loadedInstance; +} auto Device::sessionAlloc(MQT_SC_QDMI_Device_Session* session) -> int { if (session == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; @@ -308,11 +336,14 @@ auto MQT_SC_QDMI_Operation_impl_d::queryProperty( } int MQT_SC_QDMI_device_initialize() { - std::ignore = qdmi::sc::Device::get(); // Ensure the singleton is created + qdmi::sc::Device::initialize(); return QDMI_SUCCESS; } -int MQT_SC_QDMI_device_finalize() { return QDMI_SUCCESS; } +int MQT_SC_QDMI_device_finalize() { + qdmi::sc::Device::finalize(); + return QDMI_SUCCESS; +} int MQT_SC_QDMI_device_session_alloc(MQT_SC_QDMI_Device_Session* session) { return qdmi::sc::Device::get().sessionAlloc(session); diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index 506db50b3c..4af4c43571 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -43,7 +43,7 @@ MATCHER_P2(IsBetween, a, b, } // namespace } // namespace testing namespace qc { -class DriverTest : public testing::TestWithParam { +class DriverTest : public testing::TestWithParam { protected: QDMI_Session session = nullptr; QDMI_Device device = nullptr; @@ -524,7 +524,7 @@ INSTANTIATE_TEST_SUITE_P( DriverTest, // Parameters to test with testing::ValuesIn(DEVICES), - [](const testing::TestParamInfo& paramInfo) { + [](const testing::TestParamInfo& paramInfo) { std::string name = paramInfo.param; // Replace spaces with underscores for valid test names std::ranges::replace(name, ' ', '_'); @@ -540,7 +540,7 @@ INSTANTIATE_TEST_SUITE_P( DriverJobTest, // Parameters to test with testing::ValuesIn(DEVICES), - [](const testing::TestParamInfo& paramInfo) { + [](const testing::TestParamInfo& paramInfo) { std::string name = paramInfo.param; // Replace spaces with underscores for valid test names std::ranges::replace(name, ' ', '_'); From 4e458a6e9b96cf0aca891c131f54e0d52ef15e04 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:09:37 +0100 Subject: [PATCH 29/58] =?UTF-8?q?=F0=9F=90=9B=20Fix=20driver=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/qdmi/test_driver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index 506db50b3c..dcb5ecb514 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -507,11 +507,11 @@ TEST_P(DriverTest, QueryNeedsCalibration) { EXPECT_THAT(needsCalibration, testing::AnyOf(0, 1)); } #ifdef _WIN32 -const std::array DEVICES{"MQT NA Default QDMI Device", +const std::array DEVICES{"MQT NA Default QDMI Device", "MQT Core DDSIM QDMI Device", "MQT SC Default QDMI Device"}; #else -const std::array DEVICES{ +const std::array DEVICES{ "MQT NA Default QDMI Device", "MQT NA Dynamic QDMI Device", "MQT Core DDSIM QDMI Device", "MQT SC Default QDMI Device", "MQT SC Dynamic QDMI Device"}; From a3bdab63e37c162ba9736534ff610f15cbe03d94 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:19:12 +0100 Subject: [PATCH 30/58] =?UTF-8?q?=F0=9F=8E=A8=20Some=20clean-up?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/na/device/Device.cpp | 36 +----------------------------------- src/qdmi/sc/App.cpp | 7 +++---- src/qdmi/sc/Device.cpp | 35 ----------------------------------- test/fomac/test_fomac.cpp | 3 ++- test/qdmi/sc/test_device.cpp | 5 ----- 5 files changed, 6 insertions(+), 80 deletions(-) diff --git a/src/na/device/Device.cpp b/src/na/device/Device.cpp index b06ad088d5..8dfeb15416 100644 --- a/src/na/device/Device.cpp +++ b/src/na/device/Device.cpp @@ -688,11 +688,6 @@ int MQT_NA_QDMI_device_job_set_parameter(MQT_NA_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. However, - // we keep the function call for a complete implementation of QDMI and silence - // the respective warning. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->setParameter(param, size, value); } @@ -703,11 +698,6 @@ int MQT_NA_QDMI_device_job_query_property(MQT_NA_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is needed to free the job when the jobs - // are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->queryProperty(prop, size, value, sizeRet); } @@ -715,11 +705,7 @@ int MQT_NA_QDMI_device_job_submit(MQT_NA_QDMI_Device_Job job) { if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is needed to free the job when the jobs - // are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) + return job->submit(); } @@ -727,11 +713,6 @@ int MQT_NA_QDMI_device_job_cancel(MQT_NA_QDMI_Device_Job job) { if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is needed to free the job when the jobs - // are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->cancel(); } @@ -740,11 +721,6 @@ int MQT_NA_QDMI_device_job_check(MQT_NA_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is needed to free the job when the jobs - // are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->check(status); } @@ -753,11 +729,6 @@ int MQT_NA_QDMI_device_job_wait(MQT_NA_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is needed to free the job when the jobs - // are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->wait(timeout); } @@ -768,11 +739,6 @@ int MQT_NA_QDMI_device_job_get_results(MQT_NA_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is needed to free the job when the jobs - // are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->getResults(result, size, data, sizeRet); } diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index 56c78e1fa0..36ca81835c 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -169,10 +169,9 @@ struct GenerateArguments { * std::string), where args[0] is the program name. * @return std::pair The first element is the parsed * top-level Arguments; the second element is the index in `args` of the - * detected sub-command token (the first argument after top-level options). If - * no sub-command is present, the returned index will be `args.size() + 1`. - * @throws std::invalid_argument If an unknown top-level option or token is - * encountered. + * first argument belonging to the chosen sub-command (i.e., one past the + * sub-command token). If no sub-command is present, the returned index + * will be `args.size() + 1`. */ auto parseArguments(const std::vector& args) -> std::pair { diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index a75d2c980d..df109a29dd 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -385,11 +385,6 @@ int MQT_SC_QDMI_device_job_set_parameter(MQT_SC_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. However, - // we keep the function call for a complete implementation of QDMI and silence - // the respective warning. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->setParameter(param, size, value); } @@ -400,11 +395,6 @@ int MQT_SC_QDMI_device_job_query_property(MQT_SC_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is necessary to free the job when the - // jobs are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->queryProperty(prop, size, value, sizeRet); } @@ -412,11 +402,6 @@ int MQT_SC_QDMI_device_job_submit(MQT_SC_QDMI_Device_Job job) { if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is necessary to free the job when the - // jobs are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->submit(); } @@ -424,11 +409,6 @@ int MQT_SC_QDMI_device_job_cancel(MQT_SC_QDMI_Device_Job job) { if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is necessary to free the job when the - // jobs are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->cancel(); } @@ -437,11 +417,6 @@ int MQT_SC_QDMI_device_job_check(MQT_SC_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is necessary to free the job when the - // jobs are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->check(status); } @@ -450,11 +425,6 @@ int MQT_SC_QDMI_device_job_wait(MQT_SC_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is necessary to free the job when the - // jobs are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->wait(timeout); } @@ -465,11 +435,6 @@ int MQT_SC_QDMI_device_job_get_results(MQT_SC_QDMI_Device_Job job, if (job == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } - // The called function is only static because jobs are not supported. We keep - // the function call, however, as it is necessary to free the job when the - // jobs are supported. - //===--------------------------------------------------------------------===// - // NOLINTNEXTLINE(readability-static-accessed-through-instance) return job->getResults(result, size, data, sizeRet); } diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index d367726a01..feff12549a 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -249,7 +250,7 @@ TEST_P(SiteTest, SubmoduleIndex) { } TEST_P(OperationTest, Name) { - EXPECT_NO_THROW(EXPECT_FALSE(operation->getName().empty());); + EXPECT_NO_THROW(EXPECT_FALSE(operation->getName().empty())); } TEST_P(OperationTest, QubitsNum) { diff --git a/test/qdmi/sc/test_device.cpp b/test/qdmi/sc/test_device.cpp index dd457d4ae5..4ba8122d60 100644 --- a/test/qdmi/sc/test_device.cpp +++ b/test/qdmi/sc/test_device.cpp @@ -11,17 +11,12 @@ #include "mqt_sc_qdmi/device.h" #include -#include -#include #include #include // NOLINTNEXTLINE(misc-include-cleaner) #include -#include #include #include -#include -#include #include namespace { From 3cd5dbaf63570c802e5921bd336a414c7a4f615a Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:50:49 +0100 Subject: [PATCH 31/58] =?UTF-8?q?=E2=9C=85=20Skip=20failing=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/python/fomac/test_fomac.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/python/fomac/test_fomac.py b/test/python/fomac/test_fomac.py index 5424dfd904..c9d48dc956 100644 --- a/test/python/fomac/test_fomac.py +++ b/test/python/fomac/test_fomac.py @@ -47,7 +47,13 @@ def device_and_operation(request: pytest.FixtureRequest) -> tuple[Device, Device tuple[Device, Device.Operation]: A tuple containing a quantum device instance and one of its operations. """ device = request.param - operation = device.operations()[0] + + # If the device has no operations, skip tests that use this fixture. + ops = device.operations() + if not ops: + pytest.skip(f"Device '{device.name()}' has no operations.") + + operation = ops[0] return device, operation @@ -94,10 +100,9 @@ def test_device_sites(device: Device) -> None: def test_device_operations(device: Device) -> None: - """Test that the device operations is a non-empty list of Device.Operation objects.""" + """Test that the device operations is a list of Device.Operation objects.""" operations = device.operations() assert isinstance(operations, list) - assert len(operations) > 0 assert all(isinstance(op, Device.Operation) for op in operations) From 0ac07d2000e4367a9f242d5d6d9703df847b1168 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:32:05 +0100 Subject: [PATCH 32/58] =?UTF-8?q?=F0=9F=8E=A8=20Add=20missing=20include?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/qdmi/sc/App.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index 36ca81835c..39ebadef1f 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include From ca6f8bad5a6f75f815b0ee831ec20759099c45d7 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:37:01 +0100 Subject: [PATCH 33/58] =?UTF-8?q?=F0=9F=8E=A8=20Fix=20akward=20wrapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/na/device/App.cpp | 6 ++---- src/qdmi/sc/App.cpp | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/na/device/App.cpp b/src/na/device/App.cpp index 4783d4ffad..9901218b2a 100644 --- a/src/na/device/App.cpp +++ b/src/na/device/App.cpp @@ -61,10 +61,8 @@ auto printSchemaUsage(const std::string& programName) -> void { "\n" "Options:\n" " -h, --help Show this help message and exit.\n" - " -o, --output Specify the output file. If no output " - " file is " - " specified, the schema is printed to " - " stdout.\n"; + " -o, --output Specify the output file. If not\n" + " specified, prints to stdout.\n"; } /** diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index 39ebadef1f..2e7751512b 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -63,10 +63,8 @@ auto printSchemaUsage(const std::string& programName) -> void { "\n" "Options:\n" " -h, --help Show this help message and exit.\n" - " -o, --output Specify the output file. If no output " - " file is " - " specified, the schema is printed to " - " stdout.\n"; + " -o, --output Specify the output file. If not\n" + " specified, prints to stdout.\n"; } /** From cdc1cd25034a20af2ecb14593c46809439670d6c Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:49:03 +0100 Subject: [PATCH 34/58] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20some=20suggestions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 3 ++- src/qdmi/sc/App.cpp | 8 +++++++- src/qdmi/sc/Device.cpp | 10 ++++++++-- test/fomac/test_fomac.cpp | 4 +++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index 7853afc977..daffb2dc73 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace qdmi::sc { @@ -96,7 +97,7 @@ class Device final { * @see MQT_SC_QDMI_device_session_query_device_property */ auto queryProperty(QDMI_Device_Property prop, size_t size, void* value, - size_t* sizeRet) -> int; + size_t* sizeRet) const -> int; }; } // namespace qdmi::sc diff --git a/src/qdmi/sc/App.cpp b/src/qdmi/sc/App.cpp index 2e7751512b..c9c33f493f 100644 --- a/src/qdmi/sc/App.cpp +++ b/src/qdmi/sc/App.cpp @@ -496,5 +496,11 @@ int main(int argc, char* argv[]) { case Command::Generate: return executeGenerateCommand(args.programName, argVec, i); } - return 0; + // LCOV_EXCL_START +#ifdef __GNUC__ // GCC, Clang, ICC + __builtin_unreachable(); +#elif defined(_MSC_VER) // MSVC + __assume(false); +#endif + // LCOV_EXCL_STOP } diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index df109a29dd..0ef50f848b 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -150,7 +150,7 @@ auto Device::sessionFree(MQT_SC_QDMI_Device_Session session) -> void { } } auto Device::queryProperty(const QDMI_Device_Property prop, const size_t size, - void* value, size_t* sizeRet) -> int { + void* value, size_t* sizeRet) const -> int { if ((value != nullptr && size == 0) || prop >= QDMI_DEVICE_PROPERTY_MAX) { return QDMI_ERROR_INVALIDARGUMENT; } @@ -257,6 +257,7 @@ auto MQT_SC_QDMI_Device_Session_impl_d::queryOperationProperty( auto MQT_SC_QDMI_Device_Job_impl_d::free() -> void { session_->freeDeviceJob(this); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Device_Job_impl_d::setParameter( const QDMI_Device_Job_Parameter param, const size_t size, const void* value) -> int { @@ -266,6 +267,7 @@ auto MQT_SC_QDMI_Device_Job_impl_d::setParameter( } return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Device_Job_impl_d::queryProperty( // NOLINTNEXTLINE(readability-non-const-parameter) const QDMI_Device_Job_Property prop, const size_t size, void* value, @@ -275,23 +277,27 @@ auto MQT_SC_QDMI_Device_Job_impl_d::queryProperty( } return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Device_Job_impl_d::submit() -> int { return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Device_Job_impl_d::cancel() -> int { return QDMI_ERROR_NOTSUPPORTED; } -// NOLINTNEXTLINE(readability-non-const-parameter) +// NOLINTNEXTLINE(readability-convert-member-functions-to-static,readability-non-const-parameter) auto MQT_SC_QDMI_Device_Job_impl_d::check(QDMI_Job_Status* status) -> int { if (status == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Device_Job_impl_d::wait([[maybe_unused]] const size_t timeout) -> int { return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Device_Job_impl_d::getResults( QDMI_Job_Result result, // NOLINTNEXTLINE(readability-non-const-parameter) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index feff12549a..de99d4775e 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -34,7 +34,9 @@ class SiteTest : public DeviceTest { void SetUp() override { const auto& sites = device.getSites(); - ASSERT_FALSE(sites.empty()); + if (sites.empty()) { + GTEST_SKIP(); + } site = sites.front(); } }; From 5049d82168e3b1e27b3cef85e13b4f695ee030c1 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:33:12 +0100 Subject: [PATCH 35/58] =?UTF-8?q?=F0=9F=92=9A=20Fix=20linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/na/device/Device.hpp | 20 ++++++++++---------- include/mqt-core/qdmi/sc/Device.hpp | 2 +- src/na/device/Device.cpp | 8 +++++++- src/qdmi/sc/Device.cpp | 4 ++-- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/include/mqt-core/na/device/Device.hpp b/include/mqt-core/na/device/Device.hpp index 0ad132ef67..7f1ed958df 100644 --- a/include/mqt-core/na/device/Device.hpp +++ b/include/mqt-core/na/device/Device.hpp @@ -216,46 +216,46 @@ struct MQT_NA_QDMI_Device_Job_impl_d { * @brief Sets a parameter for the job. * @see MQT_NA_QDMI_device_job_set_parameter */ - static auto setParameter(QDMI_Device_Job_Parameter param, size_t size, - const void* value) -> int; + auto setParameter(QDMI_Device_Job_Parameter param, size_t size, + const void* value) -> int; /** * @brief Queries a property of the job. * @see MQT_NA_QDMI_device_job_query_property */ - static auto queryProperty(QDMI_Device_Job_Property prop, size_t size, - void* value, size_t* sizeRet) -> int; + auto queryProperty(QDMI_Device_Job_Property prop, size_t size, void* value, + size_t* sizeRet) -> int; /** * @brief Submits the job to the device. * @see MQT_NA_QDMI_device_job_submit */ - static auto submit() -> int; + auto submit() -> int; /** * @brief Cancels the job. * @see MQT_NA_QDMI_device_job_cancel */ - static auto cancel() -> int; + auto cancel() -> int; /** * @brief Checks the status of the job. * @see MQT_NA_QDMI_device_job_check */ - static auto check(QDMI_Job_Status* status) -> int; + auto check(QDMI_Job_Status* status) -> int; /** * @brief Waits for the job to complete but at most for the specified timeout. * @see MQT_NA_QDMI_device_job_wait */ - static auto wait(size_t timeout) -> int; + auto wait(size_t timeout) -> int; /** * @brief Gets the results of the job. * @see MQT_NA_QDMI_device_job_get_results */ - static auto getResults(QDMI_Job_Result result, size_t size, void* data, - [[maybe_unused]] size_t* sizeRet) -> int; + auto getResults(QDMI_Job_Result result, size_t size, void* data, + [[maybe_unused]] size_t* sizeRet) -> int; }; /** diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index daffb2dc73..e497fb296a 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -130,7 +130,7 @@ struct MQT_SC_QDMI_Device_Session_impl_d { * @see MQT_SC_QDMI_device_session_set_parameter */ auto setParameter(QDMI_Device_Session_Parameter param, size_t size, - const void* value) -> int; + const void* value) const -> int; /** * @brief Create a new device job. diff --git a/src/na/device/Device.cpp b/src/na/device/Device.cpp index 8dfeb15416..99fa19e9b3 100644 --- a/src/na/device/Device.cpp +++ b/src/na/device/Device.cpp @@ -273,6 +273,7 @@ auto MQT_NA_QDMI_Device_Session_impl_d::queryOperationProperty( auto MQT_NA_QDMI_Device_Job_impl_d::free() -> void { session_->freeDeviceJob(this); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_NA_QDMI_Device_Job_impl_d::setParameter( const QDMI_Device_Job_Parameter param, const size_t size, const void* value) -> int { @@ -282,6 +283,7 @@ auto MQT_NA_QDMI_Device_Job_impl_d::setParameter( } return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_NA_QDMI_Device_Job_impl_d::queryProperty( // NOLINTNEXTLINE(readability-non-const-parameter) const QDMI_Device_Job_Property prop, const size_t size, void* value, @@ -291,23 +293,27 @@ auto MQT_NA_QDMI_Device_Job_impl_d::queryProperty( } return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_NA_QDMI_Device_Job_impl_d::submit() -> int { return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_NA_QDMI_Device_Job_impl_d::cancel() -> int { return QDMI_ERROR_NOTSUPPORTED; } -// NOLINTNEXTLINE(readability-non-const-parameter) +// NOLINTNEXTLINE(readability-non-const-parameter,readability-convert-member-functions-to-static) auto MQT_NA_QDMI_Device_Job_impl_d::check(QDMI_Job_Status* status) -> int { if (status == nullptr) { return QDMI_ERROR_INVALIDARGUMENT; } return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_NA_QDMI_Device_Job_impl_d::wait([[maybe_unused]] const size_t timeout) -> int { return QDMI_ERROR_NOTSUPPORTED; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_NA_QDMI_Device_Job_impl_d::getResults( QDMI_Job_Result result, // NOLINTNEXTLINE(readability-non-const-parameter) diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index 0ef50f848b..b4ed79ef41 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -191,8 +191,8 @@ auto MQT_SC_QDMI_Device_Session_impl_d::init() -> int { return QDMI_SUCCESS; } auto MQT_SC_QDMI_Device_Session_impl_d::setParameter( - QDMI_Device_Session_Parameter param, const size_t size, const void* value) - -> int { + QDMI_Device_Session_Parameter param, const size_t size, + const void* value) const -> int { if ((value != nullptr && size == 0) || param >= QDMI_DEVICE_SESSION_PARAMETER_MAX) { return QDMI_ERROR_INVALIDARGUMENT; From e9d2e62608eac3aa2493baf919eca94700a09b89 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Wed, 3 Dec 2025 08:55:44 +0100 Subject: [PATCH 36/58] =?UTF-8?q?=F0=9F=92=9A=20Remove=20unecessary=20incl?= =?UTF-8?q?udes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/qdmi/sc/test_generator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/qdmi/sc/test_generator.cpp b/test/qdmi/sc/test_generator.cpp index 9f5421fb33..0ba5e72342 100644 --- a/test/qdmi/sc/test_generator.cpp +++ b/test/qdmi/sc/test_generator.cpp @@ -15,8 +15,6 @@ // clang-tidy wants to include the forward header, but we need the full // NOLINTNEXTLINE(misc-include-cleaner) #include -#include -#include namespace sc { namespace { From 21d30c680ffea9cc626d420c809c831e3ae2dea7 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:50:56 +0100 Subject: [PATCH 37/58] =?UTF-8?q?=F0=9F=8E=A8=20Revert=20change=20to=20con?= =?UTF-8?q?stexpr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/qdmi/test_driver.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index dcb5ecb514..dc64fae54c 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -507,11 +507,11 @@ TEST_P(DriverTest, QueryNeedsCalibration) { EXPECT_THAT(needsCalibration, testing::AnyOf(0, 1)); } #ifdef _WIN32 -const std::array DEVICES{"MQT NA Default QDMI Device", - "MQT Core DDSIM QDMI Device", - "MQT SC Default QDMI Device"}; +constexpr std::array DEVICES{"MQT NA Default QDMI Device", + "MQT Core DDSIM QDMI Device", + "MQT SC Default QDMI Device"}; #else -const std::array DEVICES{ +constexpr std::array DEVICES{ "MQT NA Default QDMI Device", "MQT NA Dynamic QDMI Device", "MQT Core DDSIM QDMI Device", "MQT SC Default QDMI Device", "MQT SC Dynamic QDMI Device"}; From c394f74b5c0dc2331f177ae8e1926ee14f31a10a Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:56:36 +0100 Subject: [PATCH 38/58] =?UTF-8?q?=F0=9F=8E=A8=20Enforce=20existence=20of?= =?UTF-8?q?=20sites=20and=20operations=20but=20with=20proper=20error=20han?= =?UTF-8?q?dling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/fomac/test_fomac.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index 444ebeaf1f..2589c8fcab 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -34,27 +35,23 @@ class DeviceTest : public testing::TestWithParam { class SiteTest : public DeviceTest { protected: - std::optional site; + std::unique_ptr site; void SetUp() override { const auto& sites = device.getSites(); - if (sites.empty()) { - GTEST_SKIP(); - } - site = sites.front(); + ASSERT_FALSE(sites.empty()); + *site = sites.front(); } }; class OperationTest : public DeviceTest { protected: - std::optional operation; + std::unique_ptr operation; void SetUp() override { const auto& operations = device.getOperations(); - if (operations.empty()) { - GTEST_SKIP(); - } - operation = operations.front(); + ASSERT_FALSE(operations.empty()); + *operation = operations.front(); } }; From d6bc57d46f305af9a372680f01d5484728397a10 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Wed, 3 Dec 2025 17:12:59 +0100 Subject: [PATCH 39/58] =?UTF-8?q?=F0=9F=90=9B=20Fix=20segfault?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/fomac/test_fomac.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index 2589c8fcab..ddff3f8af2 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -40,7 +40,7 @@ class SiteTest : public DeviceTest { void SetUp() override { const auto& sites = device.getSites(); ASSERT_FALSE(sites.empty()); - *site = sites.front(); + site = std::make_unique(sites.front()); } }; @@ -51,7 +51,7 @@ class OperationTest : public DeviceTest { void SetUp() override { const auto& operations = device.getOperations(); ASSERT_FALSE(operations.empty()); - *operation = operations.front(); + operation = std::make_unique(operations.front()); } }; From 06c2c2bd573164c674368612f1be7ce1f746d92f Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:33:06 +0100 Subject: [PATCH 40/58] =?UTF-8?q?=F0=9F=8E=A8=20Add=20minimal=20informatio?= =?UTF-8?q?n=20for=20operatiosn=20to=20SC=20device?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 13 +++++++++++-- include/mqt-core/qdmi/sc/Generator.hpp | 18 ++++++++++++++++++ json/sc/device.json | 4 ++++ src/qdmi/sc/Device.cpp | 23 +++++++++++++++++++---- src/qdmi/sc/Generator.cpp | 19 +++++++++++++++++++ 5 files changed, 71 insertions(+), 6 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index e497fb296a..eb2548c4dc 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -277,12 +278,20 @@ struct MQT_SC_QDMI_Site_impl_d { */ struct MQT_SC_QDMI_Operation_impl_d { private: + std::string name_; ///< Name of the operation + size_t numParameters_; ///< Number of parameters for the operation + /** + * @brief Number of qubits involved in the operation + */ + std::optional numQubits_ = std::nullopt; /// @brief Constructor for an empty operation. - MQT_SC_QDMI_Operation_impl_d() = default; + MQT_SC_QDMI_Operation_impl_d(std::string name, size_t numParameters, + size_t numQubits); public: /// @brief Factory function empty operations. - [[nodiscard]] static auto makeUnique() + [[nodiscard]] static auto makeUnique(std::string name, size_t numParameters, + size_t numQubits) -> std::unique_ptr; /** diff --git a/include/mqt-core/qdmi/sc/Generator.hpp b/include/mqt-core/qdmi/sc/Generator.hpp index 88dcc00c8a..8ffe6cd0ac 100644 --- a/include/mqt-core/qdmi/sc/Generator.hpp +++ b/include/mqt-core/qdmi/sc/Generator.hpp @@ -41,6 +41,24 @@ struct Device { /// @brief The list of couplings the device supports. std::vector> couplings; +private: + struct Operation { + /// @brief The name of the operation. + std::string name; + /// @brief The number of parameters the operation takes. + uint64_t numParameters = 0; + /// @brief The number of qubits the operation takes. + uint64_t numQubits = 0; + + // NOLINTNEXTLINE(misc-include-cleaner) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Operation, name, numParameters, + numQubits) + }; + +public: + /// @brief The list of operations the device supports. + std::vector operations; + // NOLINTNEXTLINE(misc-include-cleaner) NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Device, name, numQubits, couplings) diff --git a/json/sc/device.json b/json/sc/device.json index abc9e25c2e..d642b4427b 100644 --- a/json/sc/device.json +++ b/json/sc/device.json @@ -182,5 +182,9 @@ [87, 97], [88, 98], [89, 99] + ], + "operations": [ + { "name": "x", "num_qubits": 1 }, + { "name": "cx", "num_qubits": 2 } ] } diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index b4ed79ef41..b25a484514 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -102,6 +102,7 @@ Device::Device() { // NOLINTNEXTLINE(misc-const-correctness) INITIALIZE_SITES(sites_); INITIALIZE_COUPLINGMAP(couplingMap_); + INITIALIZE_OPERATIONS(operations_); } Device::~Device() { // Explicitly clear sessions before destruction to avoid spurious segfaults @@ -322,22 +323,36 @@ auto MQT_SC_QDMI_Site_impl_d::queryProperty(const QDMI_Site_Property prop, value, sizeRet) return QDMI_ERROR_NOTSUPPORTED; } -auto MQT_SC_QDMI_Operation_impl_d::makeUnique() +MQT_SC_QDMI_Operation_impl_d::MQT_SC_QDMI_Operation_impl_d( + std::string name, const size_t numParameters, const size_t numQubits) + : name_(std::move(name)), numParameters_(numParameters), + numQubits_(numQubits) {} +auto MQT_SC_QDMI_Operation_impl_d::makeUnique(std::string name, + const size_t numParameters, + const size_t numQubits) -> std::unique_ptr { - const MQT_SC_QDMI_Operation_impl_d op{}; + const MQT_SC_QDMI_Operation_impl_d op(std::move(name), numParameters, + numQubits); return std::make_unique(op); } -// NOLINTNEXTLINE(readability-convert-member-functions-to-static) auto MQT_SC_QDMI_Operation_impl_d::queryProperty( const size_t numSites, const MQT_SC_QDMI_Site* sites, const size_t numParams, const double* params, const QDMI_Operation_Property prop, const size_t size, void* value, - size_t* /* unused */) const -> int { + size_t* sizeRet) const -> int { if ((sites != nullptr && numSites == 0) || (params != nullptr && numParams == 0) || (value != nullptr && size == 0) || prop >= QDMI_OPERATION_PROPERTY_MAX) { return QDMI_ERROR_INVALIDARGUMENT; } + ADD_STRING_PROPERTY(QDMI_OPERATION_PROPERTY_NAME, name_.c_str(), prop, size, + value, sizeRet) + ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_PARAMETERSNUM, size_t, + numParameters_, prop, size, value, sizeRet) + if (numQubits_) { + ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_QUBITSNUM, size_t, + *numQubits_, prop, size, value, sizeRet) + } return QDMI_ERROR_NOTSUPPORTED; } diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 0ba372f533..f82416a953 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -92,6 +92,24 @@ auto writeSites(const Device& device, std::ostream& os) -> void { os << "\n"; } +/** + * @brief Writes the operations from the device object. + * @param device is the device object containing the operations. + * @param os is the output stream to write the operations to. + */ +auto writeOperations(const Device& device, std::ostream& os) -> void { + os << "#define INITIALIZE_OPERATIONS(var) var.clear()"; + for (const auto& operation : device.operations) { + os << ";\\\n" + " " + "var.emplace_back(MQT_SC_QDMI_Operation_impl_d::" + "makeUnique(\"" + << operation.name << "\", " << operation.numParameters << ", " + << operation.numQubits << "))"; + } + os << "\n"; +} + /** * @brief Emits a macro to initialize the device coupling map. * @@ -208,6 +226,7 @@ auto writeHeader(const Device& device, std::ostream& os) -> void { writeQubitsNum(device, os); writeSites(device, os); writeCouplingMap(device, os); + writeOperations(device, os); } /** From cc74fa6b677f04e6cd9b43134bdb337a2c881e4c Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:36:14 +0100 Subject: [PATCH 41/58] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20suggestion=20from?= =?UTF-8?q?=20coderabbitai?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/qdmi/test_driver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index dc64fae54c..393d652b92 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -49,8 +49,10 @@ class DriverTest : public testing::TestWithParam { QDMI_Device device = nullptr; #ifndef _WIN32 static void SetUpTestSuite() { - ASSERT_NO_THROW(for (const auto& [lib, prefix] : DYN_DEV_LIBS) { - qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); + ASSERT_NO_THROW({ + for (const auto& [lib, prefix] : DYN_DEV_LIBS) { + qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); + } }); } #endif // _WIN32 From 18f217b2238e0442cf3808f46a6664c289190e85 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:38:20 +0100 Subject: [PATCH 42/58] =?UTF-8?q?=F0=9F=8E=A8=20Remove=20superfluous=20inc?= =?UTF-8?q?lude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/fomac/test_fomac.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index ddff3f8af2..a42abb8d18 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include From f5a99674f0bc8a453660434cbbd421dac0339a57 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:43:59 +0100 Subject: [PATCH 43/58] =?UTF-8?q?=F0=9F=8E=A8=20Fix=20generator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Generator.hpp | 2 +- src/qdmi/sc/Device.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/mqt-core/qdmi/sc/Generator.hpp b/include/mqt-core/qdmi/sc/Generator.hpp index 8ffe6cd0ac..19c86964d8 100644 --- a/include/mqt-core/qdmi/sc/Generator.hpp +++ b/include/mqt-core/qdmi/sc/Generator.hpp @@ -61,7 +61,7 @@ struct Device { // NOLINTNEXTLINE(misc-include-cleaner) NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Device, name, numQubits, - couplings) + couplings, operations) }; /** diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index b25a484514..3809ff39ec 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -180,6 +180,15 @@ auto Device::queryProperty(const QDMI_Device_Property prop, const size_t size, couplingMap_, prop, size, value, sizeRet) ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_OPERATIONS, MQT_SC_QDMI_Operation, operations_, prop, size, value, sizeRet) + if (prop == (QDMI_DEVICE_PROPERTY_SUPPORTEDPROGRAMFORMATS)) { + if (value != nullptr && size > 0) { + return QDMI_ERROR_INVALIDARGUMENT; + } + if (sizeRet != nullptr) { + *sizeRet = 0; + } + return QDMI_SUCCESS; + } return QDMI_ERROR_NOTSUPPORTED; } } // namespace qdmi::sc From b9f77221e2611e89a133be2d4ea865d15dca141a Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:45:52 +0100 Subject: [PATCH 44/58] =?UTF-8?q?=F0=9F=90=9B=20Fix=20field=20population?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/qdmi/sc/Generator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index f82416a953..268b51a03e 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -39,6 +39,7 @@ namespace { */ auto populateArrayFields(Device& device) -> void { device.couplings.emplace_back(); + device.operations.emplace_back(); } /** From e37e4e2ed0a5414e3d19360f4bdd47e5fef0b353 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 09:10:56 +0100 Subject: [PATCH 45/58] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ test/qdmi/test_driver.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 173a0c3d6b..c9dc66e400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added +- ✨ Add a new QDMI device that represents a superconducting architecture featuring a coupling map ([#1328]) ([**@ystade**]) - ✨ Add bi-directional iterator that traverses the def-use chain of a qubit value ([#1310]) ([**@MatthiasReumann**]) - ✨ Add `OptionalDependencyTester` to lazily handle optional Python dependencies like Qiskit ([#1243]) ([**@marcelwa**], [**@burgholzer**]) - ✨ Expose the QDMI job interface through FoMaC ([#1243]) ([**@marcelwa**], [**@burgholzer**]) @@ -264,6 +265,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1336]: https://github.com/munich-quantum-toolkit/core/pull/1336 +[#1328]: https://github.com/munich-quantum-toolkit/core/pull/1328 [#1327]: https://github.com/munich-quantum-toolkit/core/pull/1327 [#1310]: https://github.com/munich-quantum-toolkit/core/pull/1310 [#1300]: https://github.com/munich-quantum-toolkit/core/pull/1300 diff --git a/test/qdmi/test_driver.cpp b/test/qdmi/test_driver.cpp index 393d652b92..3473c05edd 100644 --- a/test/qdmi/test_driver.cpp +++ b/test/qdmi/test_driver.cpp @@ -127,6 +127,10 @@ class DriverJobTest : public DriverTest { #ifndef _WIN32 TEST(DriverTest, LoadLibraryTwice) { + // Verify that attempting to load already-loaded libraries returns false. + // Note: SetUpTestSuite may have already loaded these libraries, so the first + // call here might also return false. This test validates that duplicate loads + // are safely handled and consistently return false (idempotent behavior). EXPECT_NO_THROW(for (const auto& [lib, prefix] : DYN_DEV_LIBS) { qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix); EXPECT_FALSE(qdmi::Driver::get().addDynamicDeviceLibrary(lib, prefix)); From bcf7f87542821e030c9e8409aba3ff41d695bce7 Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 09:27:05 +0100 Subject: [PATCH 46/58] =?UTF-8?q?=F0=9F=8E=A8=20COmplete=20json=20spec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- json/sc/device.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json/sc/device.json b/json/sc/device.json index d642b4427b..d443526830 100644 --- a/json/sc/device.json +++ b/json/sc/device.json @@ -184,7 +184,7 @@ [89, 99] ], "operations": [ - { "name": "x", "num_qubits": 1 }, - { "name": "cx", "num_qubits": 2 } + { "name": "r", "num_parameters": 3, "num_qubits": 1 }, + { "name": "cz", "num_parameters": 0, "num_qubits": 2 } ] } From 724e82590525581ef8b0483eb1699637bf431e7c Mon Sep 17 00:00:00 2001 From: Yannick Stade <100073938+ystade@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:26:53 +0100 Subject: [PATCH 47/58] =?UTF-8?q?=F0=9F=8E=A8=20Address=20feedback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqt-core/qdmi/sc/Device.hpp | 43 +++++++-- json/sc/device.json | 4 +- src/qdmi/sc/Device.cpp | 139 +++++++++++++++++++++++++--- src/qdmi/sc/Generator.cpp | 40 ++++++-- 4 files changed, 196 insertions(+), 30 deletions(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index eb2548c4dc..f1cf994e3b 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace qdmi::sc { @@ -283,15 +284,45 @@ struct MQT_SC_QDMI_Operation_impl_d { /** * @brief Number of qubits involved in the operation */ - std::optional numQubits_ = std::nullopt; - /// @brief Constructor for an empty operation. + size_t numQubits_ = 1; + /** + * @brief Storage for individual sites and site pairs. + * @details Uses std::variant to preserve the tuple structure of the operation + * sites: + * - Single-qubit and zoned operations: vector + * - Local two-qubit operations: vector> + * This maintains type safety and QDMI specification compliance, which states + * that operation sites should be "a list of tuples" for local multi-qubit + * operations. + */ + using SitesStorage = + std::variant, + std::vector>>; + + /// The operation's supported sites + SitesStorage supportedSites_; + /// @brief Constructor for a single-qubit operation. MQT_SC_QDMI_Operation_impl_d(std::string name, size_t numParameters, - size_t numQubits); + const std::vector& sites); + /// @brief Constructor for a two-qubit operation. + MQT_SC_QDMI_Operation_impl_d( + std::string name, size_t numParameters, + const std::vector>& sites); + + /// @brief Sort the sites such that the occurrence of a given site can be + /// determined in O(log n) time. + auto sortSites() -> void; public: - /// @brief Factory function empty operations. - [[nodiscard]] static auto makeUnique(std::string name, size_t numParameters, - size_t numQubits) + /// @brief Factory function a single-qubit operation. + [[nodiscard]] static auto + makeUniqueSingleQubit(std::string name, size_t numParameters, + const std::vector& sites) + -> std::unique_ptr; + /// @brief Factory function a two-qubit operation. + [[nodiscard]] static auto makeUniqueTwoQubit( + std::string name, size_t numParameters, + const std::vector>& sites) -> std::unique_ptr; /** diff --git a/json/sc/device.json b/json/sc/device.json index d443526830..c798742d54 100644 --- a/json/sc/device.json +++ b/json/sc/device.json @@ -184,7 +184,7 @@ [89, 99] ], "operations": [ - { "name": "r", "num_parameters": 3, "num_qubits": 1 }, - { "name": "cz", "num_parameters": 0, "num_qubits": 2 } + { "name": "r", "numParameters": 3, "numQubits": 1 }, + { "name": "cz", "numParameters": 0, "numQubits": 2 } ] } diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index 3809ff39ec..fba6c45894 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -17,13 +17,17 @@ #include "mqt_sc_qdmi/device.h" #include "qdmi/sc/DeviceMemberInitializers.hpp" -#include +#include #include #include #include #include +#include #include +#include +#include #include +#include #include // NOLINTBEGIN(bugprone-macro-parentheses) @@ -332,16 +336,56 @@ auto MQT_SC_QDMI_Site_impl_d::queryProperty(const QDMI_Site_Property prop, value, sizeRet) return QDMI_ERROR_NOTSUPPORTED; } +auto MQT_SC_QDMI_Operation_impl_d::sortSites() -> void { + std::visit( + [](auto& sites) { + using T = std::decay_t; + if constexpr (std::is_same_v>) { + // Single-qubit: sort flat list by pointer address + std::ranges::sort(sites, std::less{}); + } else if constexpr (std::is_same_v< + T, std::vector>>) { + // Two-qubit: normalize each pair (first < second) + // Use std::less for proper total order (pointer comparison with + // operator> invokes undefined behavior) + std::ranges::for_each(sites, [](auto& p) { + if (std::less{}(p.second, p.first)) { + std::swap(p.first, p.second); + } + }); + std::ranges::sort(sites); + } + // more cases go here if needed in the future + }, + supportedSites_); +} +MQT_SC_QDMI_Operation_impl_d::MQT_SC_QDMI_Operation_impl_d( + std::string name, const size_t numParameters, + const std::vector& sites) + : name_(std::move(name)), numParameters_(numParameters), numQubits_(1), + supportedSites_(sites) { + sortSites(); +} MQT_SC_QDMI_Operation_impl_d::MQT_SC_QDMI_Operation_impl_d( - std::string name, const size_t numParameters, const size_t numQubits) - : name_(std::move(name)), numParameters_(numParameters), - numQubits_(numQubits) {} -auto MQT_SC_QDMI_Operation_impl_d::makeUnique(std::string name, - const size_t numParameters, - const size_t numQubits) + std::string name, const size_t numParameters, + const std::vector>& sites) + : name_(std::move(name)), numParameters_(numParameters), numQubits_(2), + supportedSites_(sites) { + sortSites(); +} +auto MQT_SC_QDMI_Operation_impl_d::makeUniqueSingleQubit( + std::string name, const size_t numParameters, + const std::vector& sites) -> std::unique_ptr { - const MQT_SC_QDMI_Operation_impl_d op(std::move(name), numParameters, - numQubits); + const MQT_SC_QDMI_Operation_impl_d op(std::move(name), numParameters, sites); + return std::make_unique(op); +} +auto MQT_SC_QDMI_Operation_impl_d::makeUniqueTwoQubit( + std::string name, const size_t numParameters, + const std::vector>& sites) + -> std::unique_ptr { + const MQT_SC_QDMI_Operation_impl_d op(std::move(name), numParameters, sites); return std::make_unique(op); } auto MQT_SC_QDMI_Operation_impl_d::queryProperty( @@ -354,13 +398,84 @@ auto MQT_SC_QDMI_Operation_impl_d::queryProperty( (value != nullptr && size == 0) || prop >= QDMI_OPERATION_PROPERTY_MAX) { return QDMI_ERROR_INVALIDARGUMENT; } + if (sites != nullptr) { + // If numQubits_ == 1 or isZoned_ == true + if (numSites == 1) { + // If the (single) site is not supported, return with an error + const bool found = std::visit( + [sites](const S& storedSites) -> bool { + using T = std::decay_t; + if constexpr (std::is_same_v>) { + return std::ranges::binary_search(storedSites, *sites, + std::less{}); + } + return false; // Wrong variant type + }, + supportedSites_); + if (!found) { + return QDMI_ERROR_NOTSUPPORTED; + } + } else if (numSites == 2) { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const std::pair needle = std::less{}(sites[0], sites[1]) + ? std::make_pair(sites[0], sites[1]) + : std::make_pair(sites[1], sites[0]); + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // if the pair of sites is not supported, return with an error + const bool found = std::visit( + [&needle](const S& storedSites) -> bool { + using T = std::decay_t; + if constexpr (std::is_same_v< + T, std::vector>>) { + return std::ranges::binary_search(storedSites, needle); + } + return false; // Wrong variant type + }, + supportedSites_); + if (!found) { + return QDMI_ERROR_NOTSUPPORTED; + } + } // this device does not support operations with more than two qubits + } ADD_STRING_PROPERTY(QDMI_OPERATION_PROPERTY_NAME, name_.c_str(), prop, size, value, sizeRet) ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_PARAMETERSNUM, size_t, numParameters_, prop, size, value, sizeRet) - if (numQubits_) { - ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_QUBITSNUM, size_t, - *numQubits_, prop, size, value, sizeRet) + ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_QUBITSNUM, size_t, + numQubits_, prop, size, value, sizeRet) + if (prop == QDMI_OPERATION_PROPERTY_SITES) { + return std::visit( + [&](const S& storedSites) -> int { + using T = std::decay_t; + if constexpr (std::is_same_v>) { + // Single-qubit: return flat array + ADD_LIST_PROPERTY(QDMI_OPERATION_PROPERTY_SITES, MQT_SC_QDMI_Site, + storedSites, prop, size, value, sizeRet) + } else if constexpr (std::is_same_v< + T, + std::vector>>) { + // Ensure std::pair has standard layout and expected size + static_assert(std::is_standard_layout_v< + std::pair>); + static_assert( + sizeof(std::pair) == + 2 * sizeof(MQT_SC_QDMI_Site)); + // Two-qubit: reinterpret as flat array of sites using std::span + // std::pair has standard layout, so the memory layout of + // vector> is equivalent to Site[2*N] + const auto flatView = std::span( + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(storedSites.data()), + storedSites.size() * 2); + ADD_LIST_PROPERTY(QDMI_OPERATION_PROPERTY_SITES, MQT_SC_QDMI_Site, + flatView, prop, size, value, sizeRet) + } + // more cases go here if needed in the future + return QDMI_ERROR_NOTSUPPORTED; + }, + supportedSites_); } return QDMI_ERROR_NOTSUPPORTED; } diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 268b51a03e..bcc846cd08 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -82,9 +82,15 @@ auto writeSites(const Device& device, std::ostream& os) -> void { "var.emplace_back(MQT_SC_QDMI_Site_impl_d::makeUniqueSite(" << id << "ULL))"; } + os << ";\\\n std::vector _singleQubitSites"; + os << ";\\\n _singleQubitSites.reserve(var.size())"; + os << ";\\\n std::ranges::transform(var, " + "std::back_inserter(_singleQubitSites), [](const " + "std::unique_ptr& site) { return site.get(); " + "})"; os << ";\\\n std::vector> " - "_couplings;"; - os << ";\\\n _couplings.reserve(" << device.couplings.size() << ");"; + "_couplings"; + os << ";\\\n _couplings.reserve(" << device.couplings.size() << ")"; for (const auto& [i1, i2] : device.couplings) { os << ";\\\n " "_couplings.emplace_back(var.at(" @@ -101,12 +107,26 @@ auto writeSites(const Device& device, std::ostream& os) -> void { auto writeOperations(const Device& device, std::ostream& os) -> void { os << "#define INITIALIZE_OPERATIONS(var) var.clear()"; for (const auto& operation : device.operations) { - os << ";\\\n" - " " - "var.emplace_back(MQT_SC_QDMI_Operation_impl_d::" - "makeUnique(\"" - << operation.name << "\", " << operation.numParameters << ", " - << operation.numQubits << "))"; + if (operation.numQubits == 1) { + os << ";\\\n" + " " + "var.emplace_back(MQT_SC_QDMI_Operation_impl_d::" + "makeUniqueSingleQubit(\"" + << operation.name << "\", " << operation.numParameters + << ", _singleQubitSites))"; + } else if (operation.numQubits == 2) { + os << ";\\\n" + " " + "var.emplace_back(MQT_SC_QDMI_Operation_impl_d::" + "makeUniqueTwoQubit(\"" + << operation.name << "\", " << operation.numParameters + << ", _couplings))"; + } else { + std::ostringstream ss; + ss << "Got operation with " << operation.numQubits << " qubits but only " + << "single- and two-qubit operations are supported."; + throw std::runtime_error(ss.str()); + } } os << "\n"; } @@ -172,7 +192,7 @@ auto writeJSONSchema(const std::string& path) -> void { * Reads JSON from the provided input stream and converts it into a Device. * * @param is Input stream that supplies the JSON representation of the Device. - * @return Device Device constructed from the parsed JSON. + * @return Device constructed from the parsed JSON. * @throws std::runtime_error If JSON parsing fails; the exception message * contains parser error details. */ @@ -195,7 +215,7 @@ auto writeJSONSchema(const std::string& path) -> void { * * @param path Filesystem path to the JSON file containing the device * configuration. - * @return Device Device parsed from the JSON file. + * @return Device parsed from the JSON file. * @throws std::runtime_error If the file cannot be opened or if parsing the * JSON fails. */ From f72c042472ff76cfe984c4d1c612024e05e103d7 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 18:05:54 +0100 Subject: [PATCH 48/58] =?UTF-8?q?=F0=9F=A9=B9=20fix=20number=20of=20parame?= =?UTF-8?q?ters=20in=20`R`=20gate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- json/sc/device.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json/sc/device.json b/json/sc/device.json index c798742d54..8ebbc0ef8b 100644 --- a/json/sc/device.json +++ b/json/sc/device.json @@ -184,7 +184,7 @@ [89, 99] ], "operations": [ - { "name": "r", "numParameters": 3, "numQubits": 1 }, + { "name": "r", "numParameters": 2, "numQubits": 1 }, { "name": "cz", "numParameters": 0, "numQubits": 2 } ] } From 06ec254236c0837624bbe06109a128b1d4b36104 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 18:07:48 +0100 Subject: [PATCH 49/58] =?UTF-8?q?=F0=9F=9A=A8=20fix=20the=20obvious=20clan?= =?UTF-8?q?g-tidy=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- include/mqt-core/qdmi/sc/Device.hpp | 2 +- src/qdmi/sc/Device.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mqt-core/qdmi/sc/Device.hpp b/include/mqt-core/qdmi/sc/Device.hpp index f1cf994e3b..3376b6a06e 100644 --- a/include/mqt-core/qdmi/sc/Device.hpp +++ b/include/mqt-core/qdmi/sc/Device.hpp @@ -284,7 +284,7 @@ struct MQT_SC_QDMI_Operation_impl_d { /** * @brief Number of qubits involved in the operation */ - size_t numQubits_ = 1; + size_t numQubits_{}; /** * @brief Storage for individual sites and site pairs. * @details Uses std::variant to preserve the tuple structure of the operation diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index fba6c45894..3b248e2263 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -18,6 +18,7 @@ #include "qdmi/sc/DeviceMemberInitializers.hpp" #include +#include #include #include #include From 156b4ed17050b28e006983d9c5bc8f2e7022f9f0 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 18:14:37 +0100 Subject: [PATCH 50/58] =?UTF-8?q?=F0=9F=90=9B=20fix=20use=20of=20moved-fro?= =?UTF-8?q?m=20`=5Fcouplings`=20by=20moving=20at=20second=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- src/qdmi/sc/Generator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index bcc846cd08..6c7f6961c1 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -120,7 +120,7 @@ auto writeOperations(const Device& device, std::ostream& os) -> void { "var.emplace_back(MQT_SC_QDMI_Operation_impl_d::" "makeUniqueTwoQubit(\"" << operation.name << "\", " << operation.numParameters - << ", _couplings))"; + << ", std::move(_couplings)))"; } else { std::ostringstream ss; ss << "Got operation with " << operation.numQubits << " qubits but only " @@ -144,7 +144,7 @@ auto writeOperations(const Device& device, std::ostream& os) -> void { * @param os Output stream to write the macro definition to. */ auto writeCouplingMap(const Device& /* unused */, std::ostream& os) -> void { - os << "#define INITIALIZE_COUPLINGMAP(var) var = std::move(_couplings)\n"; + os << "#define INITIALIZE_COUPLINGMAP(var) var = _couplings\n"; } } // namespace From 69f28168ade114764c19cd97bdd854cb4e8c4f8e Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 20:05:03 +0100 Subject: [PATCH 51/58] =?UTF-8?q?=F0=9F=8F=81=20add=20potentially=20missin?= =?UTF-8?q?g=20include=20for=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- src/qdmi/sc/Device.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index 3b248e2263..bc1f3a3be8 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include From ed694b85d1369f6f4169c6349bc5f06c370050bf Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 20:38:03 +0100 Subject: [PATCH 52/58] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20directly=20add=20req?= =?UTF-8?q?uired=20headers=20to=20the=20generated=20header=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- src/qdmi/sc/Device.cpp | 1 - src/qdmi/sc/Generator.cpp | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qdmi/sc/Device.cpp b/src/qdmi/sc/Device.cpp index bc1f3a3be8..3b248e2263 100644 --- a/src/qdmi/sc/Device.cpp +++ b/src/qdmi/sc/Device.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 6c7f6961c1..1a3efda323 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -242,7 +242,12 @@ auto writeJSONSchema(const std::string& path) -> void { * @param os Output stream to write the header content to. */ auto writeHeader(const Device& device, std::ostream& os) -> void { - os << "#pragma once\n\n"; + os << "#pragma once\n\n" + << "#include \n" + << "#include \n" + << "#include \n" + << "#include \n" + << "#include \n\n"; writeName(device, os); writeQubitsNum(device, os); writeSites(device, os); From 9b912f75f8d41f7dbbf9791cf31be047c2db0358 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 21:04:18 +0100 Subject: [PATCH 53/58] =?UTF-8?q?=F0=9F=91=8C=20address=20CodeRabbit=20com?= =?UTF-8?q?ments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- src/qdmi/sc/Generator.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/qdmi/sc/Generator.cpp b/src/qdmi/sc/Generator.cpp index 1a3efda323..09c83d5fb8 100644 --- a/src/qdmi/sc/Generator.cpp +++ b/src/qdmi/sc/Generator.cpp @@ -14,8 +14,6 @@ #include "qdmi/sc/Generator.hpp" -#include -#include #include #include #include @@ -120,7 +118,7 @@ auto writeOperations(const Device& device, std::ostream& os) -> void { "var.emplace_back(MQT_SC_QDMI_Operation_impl_d::" "makeUniqueTwoQubit(\"" << operation.name << "\", " << operation.numParameters - << ", std::move(_couplings)))"; + << ", _couplings))"; } else { std::ostringstream ss; ss << "Got operation with " << operation.numQubits << " qubits but only " @@ -135,7 +133,7 @@ auto writeOperations(const Device& device, std::ostream& os) -> void { * @brief Emits a macro to initialize the device coupling map. * * Writes the C preprocessor macro `INITIALIZE_COUPLINGMAP(var)` which assigns - * `var = std::move(_couplings)`. + * `var = _couplings`. * * @note This macro depends on the `_couplings` variable created by * the INITIALIZE_SITES macro from writeSites(). The macro From 3f1e7bdfc12f864240c0146142c22f357bd9b9e7 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 21:53:53 +0100 Subject: [PATCH 54/58] =?UTF-8?q?=E2=9C=85=20run=20FoMaC=20tests=20on=20al?= =?UTF-8?q?l=20sites=20and=20operations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- test/fomac/test_fomac.cpp | 152 +++++++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 43 deletions(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index a42abb8d18..af9c440941 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -34,23 +34,21 @@ class DeviceTest : public testing::TestWithParam { class SiteTest : public DeviceTest { protected: - std::unique_ptr site; + std::vector sites; void SetUp() override { - const auto& sites = device.getSites(); + sites = device.getSites(); ASSERT_FALSE(sites.empty()); - site = std::make_unique(sites.front()); } }; class OperationTest : public DeviceTest { protected: - std::unique_ptr operation; + std::vector operations; void SetUp() override { - const auto& operations = device.getOperations(); + operations = device.getOperations(); ASSERT_FALSE(operations.empty()); - operation = std::make_unique(operations.front()); } }; @@ -271,104 +269,172 @@ TEST_P(DeviceTest, SupportedProgramFormats) { EXPECT_NO_THROW(std::ignore = device.getSupportedProgramFormats()); } -TEST_P(SiteTest, Index) { EXPECT_NO_THROW(std::ignore = site->getIndex()); } +TEST_P(SiteTest, Index) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getIndex()); + } +} -TEST_P(SiteTest, T1) { EXPECT_NO_THROW(std::ignore = site->getT1()); } +TEST_P(SiteTest, T1) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getT1()); + } +} -TEST_P(SiteTest, T2) { EXPECT_NO_THROW(std::ignore = site->getT2()); } +TEST_P(SiteTest, T2) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getT2()); + } +} -TEST_P(SiteTest, Name) { EXPECT_NO_THROW(std::ignore = site->getName()); } +TEST_P(SiteTest, Name) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getName()); + } +} TEST_P(SiteTest, XCoordinate) { - EXPECT_NO_THROW(std::ignore = site->getXCoordinate()); + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getXCoordinate()); + } } TEST_P(SiteTest, YCoordinate) { - EXPECT_NO_THROW(std::ignore = site->getYCoordinate()); + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getYCoordinate()); + } } TEST_P(SiteTest, ZCoordinate) { - EXPECT_NO_THROW(std::ignore = site->getZCoordinate()); + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getZCoordinate()); + } } -TEST_P(SiteTest, IsZone) { EXPECT_NO_THROW(std::ignore = site->isZone()); } +TEST_P(SiteTest, IsZone) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.isZone()); + } +} -TEST_P(SiteTest, XExtent) { EXPECT_NO_THROW(std::ignore = site->getXExtent()); } +TEST_P(SiteTest, XExtent) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getXExtent()); + } +} -TEST_P(SiteTest, YExtent) { EXPECT_NO_THROW(std::ignore = site->getYExtent()); } +TEST_P(SiteTest, YExtent) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getYExtent()); + } +} -TEST_P(SiteTest, ZExtent) { EXPECT_NO_THROW(std::ignore = site->getZExtent()); } +TEST_P(SiteTest, ZExtent) { + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getZExtent()); + } +} TEST_P(SiteTest, ModuleIndex) { - EXPECT_NO_THROW(std::ignore = site->getModuleIndex()); + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getModuleIndex()); + } } TEST_P(SiteTest, SubmoduleIndex) { - EXPECT_NO_THROW(std::ignore = site->getSubmoduleIndex()); + for (const auto& site : sites) { + EXPECT_NO_THROW(std::ignore = site.getSubmoduleIndex()); + } } TEST_P(OperationTest, Name) { - EXPECT_NO_THROW(EXPECT_FALSE(operation->getName().empty())); + for (const auto& operation : operations) { + EXPECT_NO_THROW(EXPECT_FALSE(operation.getName().empty())); + } } TEST_P(OperationTest, QubitsNum) { - EXPECT_NO_THROW(std::ignore = operation->getQubitsNum()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getQubitsNum()); + } } TEST_P(OperationTest, ParametersNum) { - EXPECT_NO_THROW(std::ignore = operation->getParametersNum()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getParametersNum()); + } } TEST_P(OperationTest, Duration) { - EXPECT_NO_THROW(std::ignore = operation->getDuration()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getDuration()); + } } TEST_P(OperationTest, Fidelity) { - EXPECT_NO_THROW(std::ignore = operation->getFidelity()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getFidelity()); + } } TEST_P(OperationTest, InteractionRadius) { - EXPECT_NO_THROW(std::ignore = operation->getInteractionRadius()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getInteractionRadius()); + } } TEST_P(OperationTest, BlockingRadius) { - EXPECT_NO_THROW(std::ignore = operation->getBlockingRadius()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getBlockingRadius()); + } } TEST_P(OperationTest, IdlingFidelity) { - EXPECT_NO_THROW(std::ignore = operation->getIdlingFidelity()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getIdlingFidelity()); + } } TEST_P(OperationTest, oned) { - EXPECT_NO_THROW(std::ignore = operation->isZoned()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.isZoned()); + } } TEST_P(OperationTest, Sites) { - EXPECT_NO_THROW(std::ignore = operation->getSites()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getSites()); + } } TEST_P(OperationTest, SitePairs) { - const auto sitePairs = operation->getSitePairs(); - const auto qubitsNum = operation->getQubitsNum(); - const auto isZonedOp = operation->isZoned(); - - if (!qubitsNum.has_value() || *qubitsNum != 2 || isZonedOp) { - EXPECT_FALSE(sitePairs.has_value()); - } else { - const auto sites = operation->getSites(); + for (const auto& operation : operations) { + const auto sitePairs = operation.getSitePairs(); + const auto qubitsNum = operation.getQubitsNum(); + const auto isZonedOp = operation.isZoned(); + + if (!qubitsNum.has_value() || *qubitsNum != 2 || isZonedOp) { + EXPECT_FALSE(sitePairs.has_value()); + continue; + } + + const auto sites = operation.getSites(); if (!sites.has_value() || sites->empty() || sites->size() % 2 != 0) { EXPECT_FALSE(sitePairs.has_value()); - } else { - EXPECT_TRUE(sitePairs.has_value()); - if (sitePairs.has_value()) { - EXPECT_EQ(sitePairs->size(), sites->size() / 2); - } + continue; + } + + EXPECT_TRUE(sitePairs.has_value()); + if (sitePairs.has_value()) { + EXPECT_EQ(sitePairs->size(), sites->size() / 2); } } } TEST_P(OperationTest, MeanShuttlingSpeed) { - EXPECT_NO_THROW(std::ignore = operation->getMeanShuttlingSpeed()); + for (const auto& operation : operations) { + EXPECT_NO_THROW(std::ignore = operation.getMeanShuttlingSpeed()); + } } TEST_P(DeviceTest, RegularSitesAndZones) { From d47bbb5030a1aadf87442da98aa383568dd5de5e Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 22:02:52 +0100 Subject: [PATCH 55/58] =?UTF-8?q?=E2=9C=85=20extend=20the=20operation=20qu?= =?UTF-8?q?ery=20tests=20for=20duration=20and=20fidelity=20to=20take=20sit?= =?UTF-8?q?es=20into=20account?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- test/fomac/test_fomac.cpp | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index af9c440941..c7af7ad6ae 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -367,12 +367,72 @@ TEST_P(OperationTest, ParametersNum) { TEST_P(OperationTest, Duration) { for (const auto& operation : operations) { + const auto qubitsNum = operation.getQubitsNum(); + if (!qubitsNum.has_value()) { + EXPECT_NO_THROW(std::ignore = operation.getDuration()); + continue; + } + const auto numQubits = *qubitsNum; + if (numQubits == 1) { + const auto sites = operation.getSites(); + if (!sites.has_value()) { + EXPECT_NO_THROW(std::ignore = operation.getDuration()); + continue; + } + for (const auto& site : *sites) { + EXPECT_NO_THROW(std::ignore = operation.getDuration({site})); + } + continue; + } + + if (numQubits == 2) { + const auto sitePairs = operation.getSitePairs(); + if (!sitePairs.has_value()) { + EXPECT_NO_THROW(std::ignore = operation.getDuration()); + continue; + } + for (const auto& [site1, site2] : *sitePairs) { + EXPECT_NO_THROW(std::ignore = operation.getDuration({site1, site2})); + } + continue; + } + EXPECT_NO_THROW(std::ignore = operation.getDuration()); } } TEST_P(OperationTest, Fidelity) { for (const auto& operation : operations) { + const auto qubitsNum = operation.getQubitsNum(); + if (!qubitsNum.has_value()) { + EXPECT_NO_THROW(std::ignore = operation.getFidelity()); + continue; + } + const auto numQubits = *qubitsNum; + if (numQubits == 1) { + const auto sites = operation.getSites(); + if (!sites.has_value()) { + EXPECT_NO_THROW(std::ignore = operation.getFidelity()); + continue; + } + for (const auto& site : *sites) { + EXPECT_NO_THROW(std::ignore = operation.getFidelity({site})); + } + continue; + } + + if (numQubits == 2) { + const auto sitePairs = operation.getSitePairs(); + if (!sitePairs.has_value()) { + EXPECT_NO_THROW(std::ignore = operation.getFidelity()); + continue; + } + for (const auto& [site1, site2] : *sitePairs) { + EXPECT_NO_THROW(std::ignore = operation.getFidelity({site1, site2})); + } + continue; + } + EXPECT_NO_THROW(std::ignore = operation.getFidelity()); } } From 66b7938a67e6195901d9ab94cd315fb9c7afa270 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 22:10:17 +0100 Subject: [PATCH 56/58] =?UTF-8?q?=F0=9F=9A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- test/fomac/test_fomac.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index c7af7ad6ae..c9a1653484 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include From 3d53e41956d11cfaf925ae1aa76fc99ddc6c4eea Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 23:17:51 +0100 Subject: [PATCH 57/58] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20fix=20testname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- test/fomac/test_fomac.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index c9a1653484..2d4232e637 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -454,7 +454,7 @@ TEST_P(OperationTest, IdlingFidelity) { } } -TEST_P(OperationTest, oned) { +TEST_P(OperationTest, IsZoned) { for (const auto& operation : operations) { EXPECT_NO_THROW(std::ignore = operation.isZoned()); } From 6434dd9af66b5818bbc0d5d5129ba5ebefea0f69 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 4 Dec 2025 23:18:15 +0100 Subject: [PATCH 58/58] =?UTF-8?q?=F0=9F=A9=B9=20do=20not=20require=20sites?= =?UTF-8?q?=20or=20operations=20to=20be=20present=20in=20fomac=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- test/fomac/test_fomac.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/fomac/test_fomac.cpp b/test/fomac/test_fomac.cpp index 2d4232e637..8ab717e673 100644 --- a/test/fomac/test_fomac.cpp +++ b/test/fomac/test_fomac.cpp @@ -35,20 +35,14 @@ class SiteTest : public DeviceTest { protected: std::vector sites; - void SetUp() override { - sites = device.getSites(); - ASSERT_FALSE(sites.empty()); - } + void SetUp() override { sites = device.getSites(); } }; class OperationTest : public DeviceTest { protected: std::vector operations; - void SetUp() override { - operations = device.getOperations(); - ASSERT_FALSE(operations.empty()); - } + void SetUp() override { operations = device.getOperations(); } }; class DDSimulatorDeviceTest : public testing::Test {