diff --git a/CHANGELOG.md b/CHANGELOG.md index 31fa067f..0d2a4780 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ releases may include breaking changes. - ✨ Use the AWS SDK default credential provider chain when no explicit session credentials are configured ([#150]) ([**@burgholzer**]) +- ✨ Export the stable Amazon Braket device ID and symbol prefix for MQT Core + configuration and runtime packaging ([#147]) ([**@burgholzer**]) ### Changed @@ -22,6 +24,11 @@ releases may include breaking changes. retains the existing `BASEURL` mapping ([#149]) ([**@burgholzer**]) - ⬆️ Update QDMI to version 1.3.2 ([#130]) ([**@denialhaag**]) +### Fixed + +- 🐛 Isolate scikit-build output from the direct CMake build tree ([#147]) + ([**@burgholzer**]) + ## [1.0.1] - 2026-06-26 ### Fixed @@ -48,6 +55,7 @@ _This is the initial release of the `amazon-braket-qdmi-device` project._ [#150]: https://github.com/munich-quantum-software/amazon-braket-qdmi-device/pull/150 [#149]: https://github.com/munich-quantum-software/amazon-braket-qdmi-device/pull/149 +[#147]: https://github.com/munich-quantum-software/amazon-braket-qdmi-device/pull/147 [#130]: https://github.com/munich-quantum-software/amazon-braket-qdmi-device/pull/130 [#117]: https://github.com/munich-quantum-software/amazon-braket-qdmi-device/pull/117 diff --git a/CMakeLists.txt b/CMakeLists.txt index c756bd33..d1c847b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,15 @@ endif() # add test code if(BUILD_AMAZON_BRAKET_TESTS) + get_target_property(AMAZON_BRAKET_QDMI_DEVICE_ID ${QDMI_TARGET_NAME} QDMI_DEVICE_ID) + get_target_property(AMAZON_BRAKET_QDMI_DEVICE_PREFIX ${QDMI_TARGET_NAME} QDMI_DEVICE_PREFIX) + if(NOT AMAZON_BRAKET_QDMI_DEVICE_ID STREQUAL "amazon.braket.default") + message(FATAL_ERROR "The Amazon Braket QDMI target does not export its stable device ID") + endif() + if(NOT AMAZON_BRAKET_QDMI_DEVICE_PREFIX STREQUAL "${QDMI_PREFIX}") + message(FATAL_ERROR "The Amazon Braket QDMI target does not export its symbol prefix") + endif() + enable_testing() include(GoogleTest) add_subdirectory(test) diff --git a/README.md b/README.md index d9c27e0b..465c65e9 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,60 @@ cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/install cmake --build build ``` +### Using the Device with MQT Core + +The installed CMake target exports the stable device ID `amazon.braket.default` +and the `AMAZON_BRAKET` symbol prefix. An application using MQT Core 3.8 or +newer can copy the device library and a relocatable manifest beside its +executable: + +```cmake +find_package(mqt-core 3.8 CONFIG REQUIRED) +find_package(amazon-braket-qdmi-device CONFIG REQUIRED) + +add_executable(my_app main.cpp) +target_link_libraries(my_app PRIVATE MQT::CoreFoMaC) +mqt_copy_qdmi_runtime(my_app amazon-braket-qdmi-device) +``` + +This placement is discovered automatically when the MQT Core Driver is linked +statically into the executable. A dynamically linked Driver searches beside its +own shared library instead; in that case, place the generated manifest there or +register the definition explicitly as shown below. + +Python consumers can use the same metadata to preserve any existing +`amazon.braket.default` configuration and apply credentials or a device ARN to +each fresh session: + +```python +from pathlib import Path + +from amazon.braket.qdmi import ( + AMAZON_BRAKET_QDMI_DEVICE_ID, + AMAZON_BRAKET_QDMI_LIBRARY_PATH, + AMAZON_BRAKET_QDMI_PREFIX, +) +from mqt.core.fomac import DeviceDefinition, open_device, register_device_if_absent + +register_device_if_absent( + DeviceDefinition( + AMAZON_BRAKET_QDMI_DEVICE_ID, + AMAZON_BRAKET_QDMI_LIBRARY_PATH, + AMAZON_BRAKET_QDMI_PREFIX, + ) +) +device = open_device( + AMAZON_BRAKET_QDMI_DEVICE_ID, + base_url="arn:aws:braket:::device/quantum-simulator/amazon/sv1", + auth_file=Path("/path/to/credentials"), + custom2="us-east-1", # AMAZON_BRAKET_QDMI_DEVICE_SESSION_PARAMETER_REGION +) +``` + +`register_device_if_absent` does not replace definitions from `qdmi.json`, +`[tool.qdmi]`, or another higher-precedence configuration source. The explicit +arguments to `open_device` override only that newly opened session. + ### CMake Options | Option | Default | Description | diff --git a/pyproject.toml b/pyproject.toml index e6a5eb92..cbec19a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,6 +65,7 @@ build = [ "scikit-build-core>=0.11.6" ] test = [ + "mqt-core~=3.8.0", "pytest>=9.0.2", "pytest-console-scripts>=1.4.1", "pytest-cov>=7.0.0", @@ -175,6 +176,7 @@ convention = "google" default.extend-ignore-re = [ "(?Rm)^.*(#|//)\\s*spellchecker:disable-line$", # ignore line "(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on", # ignore block + "FoMaC", ] [tool.typos.default.extend-words] diff --git a/python/amazon/braket/qdmi/__init__.py b/python/amazon/braket/qdmi/__init__.py index 82dad4c0..d5ce0c24 100644 --- a/python/amazon/braket/qdmi/__init__.py +++ b/python/amazon/braket/qdmi/__init__.py @@ -25,11 +25,16 @@ __all__ = [ "AMAZON_BRAKET_QDMI_CMAKE_DIR", + "AMAZON_BRAKET_QDMI_DEVICE_ID", "AMAZON_BRAKET_QDMI_INCLUDE_DIR", "AMAZON_BRAKET_QDMI_LIBRARY_PATH", + "AMAZON_BRAKET_QDMI_PREFIX", "__version__", ] +AMAZON_BRAKET_QDMI_DEVICE_ID = "amazon.braket.default" +AMAZON_BRAKET_QDMI_PREFIX = "AMAZON_BRAKET" + def __dir__() -> list[str]: return __all__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f12ebdf9..05a6e279 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -106,6 +106,15 @@ target_sources( target_link_libraries(${QDMI_TARGET_NAME} PRIVATE qdmi::qdmi_project_warnings ${QDMI_DEVICE_OBJECTS} ${QDMI_PARSER_OBJECTS}) +# Publish the stable metadata needed by QDMI driver integrations to package this separately built +# device implementation. +set_target_properties(${QDMI_TARGET_NAME} PROPERTIES QDMI_DEVICE_ID amazon.braket.default + QDMI_DEVICE_PREFIX ${QDMI_PREFIX}) +set_property( + TARGET ${QDMI_TARGET_NAME} + APPEND + PROPERTY EXPORT_PROPERTIES QDMI_DEVICE_ID QDMI_DEVICE_PREFIX) + option(ENABLE_COVERAGE "Enable coverage reporting" OFF) if(ENABLE_COVERAGE) target_link_libraries(${QDMI_DEVICE_OBJECTS} PUBLIC qdmi::qdmi_coverage_flags) diff --git a/test/python/test_init.py b/test/python/test_init.py index a8966f5e..deb8c34b 100644 --- a/test/python/test_init.py +++ b/test/python/test_init.py @@ -23,8 +23,10 @@ from amazon.braket.qdmi import ( AMAZON_BRAKET_QDMI_CMAKE_DIR, + AMAZON_BRAKET_QDMI_DEVICE_ID, AMAZON_BRAKET_QDMI_INCLUDE_DIR, AMAZON_BRAKET_QDMI_LIBRARY_PATH, + AMAZON_BRAKET_QDMI_PREFIX, __version__, ) @@ -36,6 +38,12 @@ def test_version_exists() -> None: assert len(__version__) > 0 +def test_qdmi_device_metadata() -> None: + """Test that the stable device metadata matches the native library.""" + assert AMAZON_BRAKET_QDMI_DEVICE_ID == "amazon.braket.default" + assert AMAZON_BRAKET_QDMI_PREFIX == "AMAZON_BRAKET" + + def test_include_dir_exists() -> None: """Test that AMAZON_BRAKET_QDMI_INCLUDE_DIR exists and is a directory.""" assert AMAZON_BRAKET_QDMI_INCLUDE_DIR.exists() diff --git a/test/python/test_mqt_core.py b/test/python/test_mqt_core.py new file mode 100644 index 00000000..a238cd3c --- /dev/null +++ b/test/python/test_mqt_core.py @@ -0,0 +1,41 @@ +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH +# All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Hermetic integration tests for MQT Core's stable QDMI device registry.""" + +from __future__ import annotations + +from mqt.core.fomac import DeviceDefinition, register_device_if_absent + +from amazon.braket.qdmi import ( + AMAZON_BRAKET_QDMI_LIBRARY_PATH, + AMAZON_BRAKET_QDMI_PREFIX, +) + +TEST_DEVICE_ID = "test.amazon.braket.packaged" + + +def test_register_device_if_absent() -> None: + """Register the packaged device without loading it or contacting AWS.""" + definition = DeviceDefinition( + TEST_DEVICE_ID, + AMAZON_BRAKET_QDMI_LIBRARY_PATH, + AMAZON_BRAKET_QDMI_PREFIX, + ) + + assert register_device_if_absent(definition) + assert not register_device_if_absent(definition) diff --git a/uv.lock b/uv.lock index 49c9859f..9f21686c 100644 --- a/uv.lock +++ b/uv.lock @@ -12,6 +12,7 @@ build = [ { name = "scikit-build-core" }, ] dev = [ + { name = "mqt-core" }, { name = "nox" }, { name = "pytest" }, { name = "pytest-console-scripts" }, @@ -21,6 +22,7 @@ dev = [ { name = "scikit-build-core" }, ] test = [ + { name = "mqt-core" }, { name = "pytest" }, { name = "pytest-console-scripts" }, { name = "pytest-cov" }, @@ -33,6 +35,7 @@ test = [ [package.metadata.requires-dev] build = [{ name = "scikit-build-core", specifier = ">=0.11.6" }] dev = [ + { name = "mqt-core", specifier = "~=3.8.0" }, { name = "nox", specifier = ">=2026.2.9" }, { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-console-scripts", specifier = ">=1.4.1" }, @@ -42,6 +45,7 @@ dev = [ { name = "scikit-build-core", specifier = ">=0.11.6" }, ] test = [ + { name = "mqt-core", specifier = "~=3.8.0" }, { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-console-scripts", specifier = ">=1.4.1" }, { name = "pytest-cov", specifier = ">=7.0.0" }, @@ -261,6 +265,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "mqt-core" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/38/065fb5394e823213e7fbc2185139b8979f77c369d53e2b23ed9faf701a4f/mqt_core-3.8.0.tar.gz", hash = "sha256:d4babb5009b9f4c3d50211caf2cd8fc850c5ba97bfe47627446e2103253462e8", size = 798862, upload-time = "2026-07-30T11:00:04.607Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/6a/4e503b1980861af6d9e8814839231059635fa0bbd5396580b6fffa363b44/mqt_core-3.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:074e6540c8db507b9f2124898681e28e8eac884b7e0651ac404642efe8308fc0", size = 5923197, upload-time = "2026-07-30T10:59:16.188Z" }, + { url = "https://files.pythonhosted.org/packages/88/6a/d23553d0f29694a3af95a993091f7b3e0059a11b8f9f95cef8066021c5e4/mqt_core-3.8.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:9a77fabaa0e1d0dd05d3284f748928445920bf9de98f1784d3be8400d61d9d20", size = 6463531, upload-time = "2026-07-30T10:59:18.15Z" }, + { url = "https://files.pythonhosted.org/packages/9e/41/80338cfef72931f799d50fa13dea06974d91f9ac48097a8551f59be07407/mqt_core-3.8.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2f28eadc8648a81ad8542be5c8b13246893d6acddda8520c76a659397e6b48a", size = 8619340, upload-time = "2026-07-30T10:59:19.829Z" }, + { url = "https://files.pythonhosted.org/packages/b2/4a/cf5957b699c49a574c93622a0d60be7e9be346ba8d2aaa5ac16ed423e47a/mqt_core-3.8.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d060a3cbf7890679e2d195db5889c687393e8db4ada9b4c8eb5f608352ed5d4", size = 9122704, upload-time = "2026-07-30T10:59:21.954Z" }, + { url = "https://files.pythonhosted.org/packages/96/c7/583b7e5b0efaa7970a8cde71b10c78001e635548398ebd1bc112dc5b37a1/mqt_core-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:91bbf8c4669faf9538f33cd05ad2e8ad2bbdcecab8ee387d36e8c5d6ebbbe6f4", size = 8890690, upload-time = "2026-07-30T10:59:23.922Z" }, + { url = "https://files.pythonhosted.org/packages/e7/09/0353203e75450b61e55ea08edf15ba398e9a0def78a4bff5dbaf9836cb9c/mqt_core-3.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:e15a7e2f5af311ed416363e3736914025dbd15fa936c2060d57eac69ebfb76cf", size = 8755273, upload-time = "2026-07-30T10:59:26.032Z" }, + { url = "https://files.pythonhosted.org/packages/7c/1d/019704bc4ef65073b52d6df5334111ef1409d2f9ad31e54162c8a9bfbdd9/mqt_core-3.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f46a302c3835410d381dc67e983c64daebdc3d2e3028c060dd9b6960e3c00084", size = 5922996, upload-time = "2026-07-30T10:59:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f0/6da6f47ca9be2aecd6df1b6a95e51e79963abdee6daf1326ef9bb4ae2bcb/mqt_core-3.8.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:6f955f5915836588912c3bd937f5407f4787dc8700143f0d9cbdbf21d56dcd5d", size = 6463290, upload-time = "2026-07-30T10:59:29.549Z" }, + { url = "https://files.pythonhosted.org/packages/66/fc/d802b81e5f387222906b8fc7bf2a90f2ad35bffcae5d7648cc2f10a6d228/mqt_core-3.8.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6afd9d88164c28c3581b67f1128e6f7a8a1b7caa5254c6c6c7449463896d9469", size = 8618894, upload-time = "2026-07-30T10:59:31.14Z" }, + { url = "https://files.pythonhosted.org/packages/e9/13/623188e259257f207a6304b2983d94b8624b8dd1a7f5ba8db430981cca96/mqt_core-3.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd4f5af2e5025c342c7fafabd3da20760da10259ffdc8ddb94f756649f8407c9", size = 9122519, upload-time = "2026-07-30T10:59:33.271Z" }, + { url = "https://files.pythonhosted.org/packages/64/b1/e51323c61b4f87ec132fa40a97d0bd09a11234db1da1a897e1c154f96cf1/mqt_core-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:07b576db58330df4ebe4f6d1cb1b191b2b51e74001ec2531ecd24b38b4f9caa2", size = 8890796, upload-time = "2026-07-30T10:59:35.47Z" }, + { url = "https://files.pythonhosted.org/packages/ad/62/c00011d1824f5502e82680ecdd41f29db2c939bb03daa892d4d9fc70a781/mqt_core-3.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:9c3890fd9c973c15aa613900c6b09261d7c2a9142b2f4396f993bf5eb91e92ad", size = 8754942, upload-time = "2026-07-30T10:59:38.183Z" }, + { url = "https://files.pythonhosted.org/packages/e6/34/53d1ce1e91f67f04159f150e2b95a56b5218ac57aff34df43c27ae1db297/mqt_core-3.8.0-cp312-abi3-macosx_11_0_arm64.whl", hash = "sha256:95fb79da1b20091e30854a3df7cbe4a9dcf05559e9b8fcc7da9eb2a94532f306", size = 5915266, upload-time = "2026-07-30T10:59:40.084Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3b/cfc424187319a4296f60b284dc2f04dff2cd8675644dcd8baf9299449955/mqt_core-3.8.0-cp312-abi3-macosx_11_0_x86_64.whl", hash = "sha256:5f51bac17c06e9821bc683b6056d8f903cb69da9f49007e51f2437da280681ee", size = 6457283, upload-time = "2026-07-30T10:59:41.819Z" }, + { url = "https://files.pythonhosted.org/packages/23/01/56f07166708741cb42ed5cf2cd20c211cb1d6e6a3defdea02592b36e98f0/mqt_core-3.8.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a6c317672de4d6c2ace8b0ba90d0c315f4dbedb3d6bc545125f7f64060f2f6", size = 8601979, upload-time = "2026-07-30T10:59:43.619Z" }, + { url = "https://files.pythonhosted.org/packages/a2/8c/02b56ae87cba4e13ea647386eb01918aa20df7dcf88be78f061339e8f4b2/mqt_core-3.8.0-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9f7e8229c5b4d16723878a1479e0a72d71540311c9cae778ba0ee92b053e021", size = 9103875, upload-time = "2026-07-30T10:59:45.598Z" }, + { url = "https://files.pythonhosted.org/packages/db/c3/38cca4a2e3b5a3b6c7499a5cafaf2ca9641cea52350ae3af72e92abf71b2/mqt_core-3.8.0-cp312-abi3-win_amd64.whl", hash = "sha256:82285d6c507416c5ac72a774a48b9774076a4b0619009dd3c2cddfdd763e8399", size = 8881574, upload-time = "2026-07-30T10:59:47.61Z" }, + { url = "https://files.pythonhosted.org/packages/97/32/f94326ec648af06ec03446b4ca8c7eb2f95d05f98e8d61cc5c59d1447f7c/mqt_core-3.8.0-cp312-abi3-win_arm64.whl", hash = "sha256:d55203e256ae24eb77de7287b9db70e7414b172894f7d664d8b97b6d646cd759", size = 8743120, upload-time = "2026-07-30T10:59:49.659Z" }, + { url = "https://files.pythonhosted.org/packages/49/6f/d134df84cf31dfb5802f973494ea7cf8e90facf648acf162ae6cd1b31fd9/mqt_core-3.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9982625ee26ae97757e9ff79fd05b51775cfcd21dee661cd4f5b1a806741482a", size = 5929425, upload-time = "2026-07-30T10:59:51.97Z" }, + { url = "https://files.pythonhosted.org/packages/85/b7/98ca3ef20e8d11eb7de6255412542833293e9fb5923a8961049df1ce55f1/mqt_core-3.8.0-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:43869178ba5ba181b80d612ca8b5ee012480c8f63331c7b051bada3740d4aabd", size = 6472802, upload-time = "2026-07-30T10:59:53.731Z" }, + { url = "https://files.pythonhosted.org/packages/09/dd/7e0efe5d8951829be73a462b434d89ed3f11478bf1d4b91095b32c5fdced/mqt_core-3.8.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86a9eda4ddd33a996177c929f2990b47eddbc3998036061e8a4f9daaf9fbb2d7", size = 8620976, upload-time = "2026-07-30T10:59:55.592Z" }, + { url = "https://files.pythonhosted.org/packages/47/7e/73d09410aaef55f013cb7748a1e694e454f142a70f53085adcd2eea8a38b/mqt_core-3.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd9f38caee1f30bf66e3dbbe53fee403817bd3190a02a40b773b09f4de7730b2", size = 9122076, upload-time = "2026-07-30T10:59:57.883Z" }, + { url = "https://files.pythonhosted.org/packages/d7/5c/10edf81250ca314fa01c0ccf7f9cbc94381f9a725b4e770ab4d6924660f3/mqt_core-3.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4679b94e2a166186883708cc937b40567ba42c3d379bfcded3a8cddae33be672", size = 8965499, upload-time = "2026-07-30T11:00:00.139Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b9/017e6ccb3c84312b472bced475499394da3f84390fba3a01d0268340756f/mqt_core-3.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a166fe03fd5b60ebe038c4d17ef450f2583508be86db2f4f084f3d327572a34f", size = 8833359, upload-time = "2026-07-30T11:00:02.474Z" }, +] + [[package]] name = "nox" version = "2026.7.11"