diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 000000000..07c1a5c35 --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,48 @@ +name: Publish qcs-sdk-python documentation + +on: + push: + branches: + - main + pull_request: + types: + - opened + - reopened + - synchronize + - closed + +jobs: + publish-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + - name: Install poetry + uses: snok/install-poetry@v1 + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-make + uses: actions-rs/cargo@v1 + with: + command: install + args: --debug cargo-make + - name: Build qcs-sdk-python documentation + uses: actions-rs/cargo@v1 + with: + command: make + args: --cwd crates/python --makefile Makefile.toml docs + - name: Deploy preview + if: ${{ github.event_name == 'pull_request' }} + uses: rossjrw/pr-preview-action@v1 + with: + source-dir: crates/python/build/docs + preview-branch: qcs-sdk-python-docs + - name: Deploy docs + if: ${{ github.event_name == 'push' }} + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: crates/python/build/docs + publish_branch: qcs-sdk-python-docs + diff --git a/.gitignore b/.gitignore index 00cc29e11..ea4fde45d 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,7 @@ qcs-api/docs # Python artifacts **/__pycache__ +**/null-ls-cache* crates/python/.hypothesis crates/python/qcs_sdk/*.so +crates/python/build diff --git a/Cargo.toml b/Cargo.toml index 657395a8a..cddd8b1b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ numpy = "0.20.0" pyo3 = "0.20.0" pyo3-asyncio = { version = "0.20", features = ["tokio-runtime"] } pyo3-build-config = "0.20.0" -rigetti-pyo3 = { version = "0.3.0", features = ["complex"] } +rigetti-pyo3 = { version = "0.3.1", features = ["complex"] } # The primary intent of these options is to reduce the binary size for Python wheels # since PyPi has limits on how much storage a project can use. diff --git a/crates/python/Makefile.toml b/crates/python/Makefile.toml index 7ca333bac..753bc3845 100644 --- a/crates/python/Makefile.toml +++ b/crates/python/Makefile.toml @@ -57,3 +57,9 @@ alias = "dev-flow" [tasks.ci-flow] dependencies = ["pytest-flow", "stubtest"] + +[tasks.docs] +dependencies = ["poetry-install", "install-lib"] +command = "poetry" +args = ["run", "pdoc", "-o", "build/docs", "qcs_sdk", "!qcs_sdk.qcs_sdk", "--logo", "https://qcs.rigetti.com/static/img/rigetti-logo.svg"] + diff --git a/crates/python/README.md b/crates/python/README.md index 544e6a40a..251337e0d 100644 --- a/crates/python/README.md +++ b/crates/python/README.md @@ -8,6 +8,10 @@ to compile and run Quil programs on Rigetti quantum processors. Internally, it i While this package can be used directly, [pyQuil](https://pypi.org/project/pyquil/) offers more functionality and a higher-level interface for building and executing Quil programs. This package is still in early development and breaking changes should be expected between minor versions. +# Documentation + +Documentation for the current release of `qcs_sdk` is published [here](https://rigetti.github.io/qcs-sdk-rust/qcs_sdk.html). Every version of `qcs_sdk` ships [with type stubs](https://github.com/rigetti/qcs-sdk-rust/tree/main/crates/python/qcs_sdk) that can provide type hints and documentation to Python tooling and editors. + ## Troubleshooting ### Enabling Debug logging diff --git a/crates/python/poetry.lock b/crates/python/poetry.lock index 79c1c04e1..6c2679056 100644 --- a/crates/python/poetry.lock +++ b/crates/python/poetry.lock @@ -1,5 +1,20 @@ # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +[[package]] +name = "astunparse" +version = "1.6.3" +description = "An AST unparser for Python" +optional = false +python-versions = "*" +files = [ + {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, + {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, +] + +[package.dependencies] +six = ">=1.6.1,<2.0" +wheel = ">=0.23.0,<1.0" + [[package]] name = "attrs" version = "22.2.0" @@ -113,6 +128,92 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + [[package]] name = "maturin" version = "1.2.3" @@ -258,6 +359,26 @@ files = [ {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, ] +[[package]] +name = "pdoc" +version = "14.1.0" +description = "API Documentation for Python Projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pdoc-14.1.0-py3-none-any.whl", hash = "sha256:e8869dffe21296b3bd5545b28e7f07cae0656082aca43f8915323187e541b126"}, + {file = "pdoc-14.1.0.tar.gz", hash = "sha256:3a0bd921a05c39a82b1505089eb6dc99d857b71b856aa60d1aca4d9086d0e18c"}, +] + +[package.dependencies] +astunparse = {version = "*", markers = "python_version < \"3.9\""} +Jinja2 = ">=2.11.0" +MarkupSafe = "*" +pygments = ">=2.12.0" + +[package.extras] +dev = ["black", "hypothesis", "mypy", "pygments (>=2.14.0)", "pytest", "pytest-cov", "pytest-timeout", "ruff", "tox", "types-pygments"] + [[package]] name = "platformdirs" version = "2.6.2" @@ -288,6 +409,21 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pytest" version = "7.2.1" @@ -328,6 +464,17 @@ pytest = ">=6.1.0" [package.extras] testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "syrupy" version = "3.0.6" @@ -365,7 +512,21 @@ files = [ {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] +[[package]] +name = "wheel" +version = "0.42.0" +description = "A built-package format for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wheel-0.42.0-py3-none-any.whl", hash = "sha256:177f9c9b0d45c47873b619f5b650346d632cdc35fb5e4d25058e09c9e581433d"}, + {file = "wheel-0.42.0.tar.gz", hash = "sha256:c45be39f7882c9d34243236f2d63cbd58039e360f85d0913425fbd7ceea617a8"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)", "setuptools (>=65)"] + [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "72be4757174daad25e0f031ae8180ce690da39923ad6291bf95c2a2d0caf2931" +content-hash = "52cf69b189260044546aed90a42b51c72173bf6253ed35008a078aee9bb60e68" diff --git a/crates/python/pyproject.toml b/crates/python/pyproject.toml index e80f8b23d..27c5d92ac 100644 --- a/crates/python/pyproject.toml +++ b/crates/python/pyproject.toml @@ -53,6 +53,7 @@ pytest-asyncio = "^0.19.0" black = "^22.8.0" syrupy = "^3.0.6" mypy = "^1.4.1" +pdoc = "^14.1.0" [build-system] requires = ["maturin>=1.0.0,<2.0.0"] diff --git a/crates/python/qcs_sdk/__init__.pyi b/crates/python/qcs_sdk/__init__.pyi index 0b278e45b..193d98f06 100644 --- a/crates/python/qcs_sdk/__init__.pyi +++ b/crates/python/qcs_sdk/__init__.pyi @@ -1,3 +1,10 @@ +""" +The `qcs_sdk` package provides a Python interface to the Rigetti Quantum Cloud Services (QCS) platform. + +For more information about QCS, see [the QCS documentation](https://docs.rigetti.com/qcs/). + +⚠️ This package is still in early development and breaking changes should be expected between minor versions. +""" import datetime from enum import Enum from typing import Dict, Iterable, List, Optional, Sequence, Tuple, Union, final @@ -12,10 +19,10 @@ from qcs_sdk.qpu.api import ExecutionOptions from qcs_sdk.qpu.translation import TranslationOptions from qcs_sdk.qvm import QVMClient, QVMResultData, RawQVMReadoutData -from . import client as client -from . import compiler as compiler -from . import qpu as qpu -from . import qvm as qvm +from qcs_sdk import client as client +from qcs_sdk import compiler as compiler +from qcs_sdk import qpu as qpu +from qcs_sdk import qvm as qvm class ExecutionError(RuntimeError): """Error encountered when executing a program.""" @@ -42,7 +49,7 @@ class Executable: """ Execute on a QVM which is accessible via the provided client. - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... async def execute_on_qvm_async(self, client: QVMClient) -> ExecutionData: @@ -50,7 +57,7 @@ class Executable: Execute on a QVM which is accessible via the provided client. (async analog of ``Executable.execute_on_qvm``.) - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... def execute_on_qpu( @@ -66,7 +73,7 @@ class Executable: :param endpoint_id: execute the compiled program against an explicitly provided endpoint. If `None`, the default endpoint for the given quantum_processor_id is used. - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... async def execute_on_qpu_async( @@ -78,12 +85,12 @@ class Executable: ) -> ExecutionData: """ Compile the program and execute it on a QPU, waiting for results. - (async analog of ``Executable.execute_on_qpu``) + (async analog of `Executable.execute_on_qpu`) :param endpoint_id: execute the compiled program against an explicitly provided endpoint. If `None`, the default endpoint for the given quantum_processor_id is used. - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... def submit_to_qpu( @@ -99,7 +106,7 @@ class Executable: :param endpoint_id: execute the compiled program against an explicitly provided endpoint. If `None`, the default endpoint for the given quantum_processor_id is used. - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... async def submit_to_qpu_async( @@ -111,27 +118,27 @@ class Executable: ) -> JobHandle: """ Compile the program and execute it on a QPU, without waiting for results. - (async analog of ``Executable.execute_on_qpu``) + (async analog of `Executable.execute_on_qpu`) :param endpoint_id: execute the compiled program against an explicitly provided endpoint. If `None`, the default endpoint for the given quantum_processor_id is used. - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... def retrieve_results(self, job_handle: JobHandle) -> ExecutionData: """ Wait for the results of a job to complete. - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... async def retrieve_results_async(self, job_handle: JobHandle) -> ExecutionData: """ Wait for the results of a job to complete. - (async analog of ``Executable.retrieve_results``) + (async analog of `Executable.retrieve_results`) - :raises ExecutionError: If the job fails to execute. + :raises `ExecutionError`: If the job fails to execute. """ ... @@ -152,7 +159,8 @@ class JobHandle: @property def readout_map(self) -> Dict[str, str]: """ - The readout map from source readout memory locations to the filter pipeline node which publishes the data. + The readout map from source readout memory locations to the filter pipeline node + which publishes the data. """ ... @@ -191,7 +199,7 @@ class Service(Enum): QPU = "QPU" class RegisterMatrixConversionError(ValueError): - """Error that may occur when building a ``RegisterMatrix`` from execution data.""" + """Error that may occur when building a `RegisterMatrix` from execution data.""" ... @@ -201,16 +209,16 @@ class RegisterMatrix: Values in a 2-dimensional ``ndarray`` representing the final shot value in each memory reference across all shots. Each variant corresponds to the possible data types a register can contain. - Variants: - ``integer``: Corresponds to the Quil `BIT`, `OCTET`, or `INTEGER` types. - ``real``: Corresponds to the Quil `REAL` type. - ``complex``: Registers containing complex numbers. + ## Variants: + - ``integer``: Corresponds to the Quil ``BIT``, ``OCTET``, or ``INTEGER`` types. + - ``real``: Corresponds to the Quil ``REAL`` type. + - ``complex``: Registers containing complex numbers. - Methods (each per variant): - - ``is_*``: if the underlying values are that type. - - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. - - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. - - ``from_*``: wrap underlying values as this enum type. + ### Methods (each per variant): + - ``is_*``: if the underlying values are that type. + - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. + - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. + - ``from_*``: wrap underlying values as this enum type. """ @@ -238,7 +246,7 @@ class RegisterMatrix: @final class RegisterMap: - """A map of register names (ie. "ro") to a ``RegisterMatrix`` containing the values of the register.""" + """A map of register names (ie. "ro") to a `RegisterMatrix` containing the values of the register.""" def get_register_matrix(self, register_name: str) -> Optional[RegisterMatrix]: """Get the ``RegisterMatrix`` for the given register. Returns `None` if the register doesn't exist.""" @@ -258,14 +266,13 @@ class ResultData: Represents the two possible types of data returned from either the QVM or a real QPU. Each variant contains the original data returned from its respective executor. - Usage - ----- + ## Usage - Your usage of ``ResultData`` will depend on the types of programs you are running and where. - The `to_register_map()` method will attempt to build ``RegisterMap`` out of the data, where each - register name is mapped to a 2-dimensional rectangular ``RegisterMatrix`` where each row + Your usage of `ResultData` will depend on the types of programs you are running and where. + The `ResultData.to_register_map()` method will attempt to build `RegisterMap` out of the data, where each + register name is mapped to a 2-dimensional rectangular `RegisterMatrix` where each row represents the final values in each register index for a particular shot. This is often the - desired form of the data and it is _probably_ what you want. This transformation isn't always + desired form of the data and it is probably what you want. This transformation isn't always possible, in which case `to_register_map()` will return an error. To understand why this transformation can fail, we need to understand a bit about how readout data is @@ -275,8 +282,8 @@ class ResultData: as one might expect it to. It is zero-initalized, and subsequent writes to the same region overwrite the previous value. The QVM returns memory at the end of every shot. This means we get the last value in every memory reference for each shot, which is exactly the - representation we want for a ``RegisterMatrix``. For this reason, `to_register_map()` should - always succeed for ``ResultData::Qvm``. + representation we want for a `RegisterMatrix`. For this reason, `to_register_map()` should + always succeed for `ResultData::Qvm`. The QPU on the other hand doesn't use the same memory model as the QVM. Each memory reference (ie. "ro[0]") is more like a stream than a value in memory. Every `MEASURE` to a memory @@ -288,15 +295,18 @@ class ResultData: ``RegisterMatrix`` you need from the inner ``QPUResultData`` data using the knowledge of your program to choose the correct readout values for each shot. - Variants: - - ``qvm``: Data returned from the QVM, stored as ``QVMResultData`` - - ``qpu``: Data returned from the QPU, stored as ``QPUResultData`` + For more information on QPU readout data, refer to the + [QCS Documentation](https://docs.rigetti.com/qcs/guides/qpus-vs-qvms#qpu-readout-data). - Methods (each per variant): - - ``is_*``: if the underlying values are that type. - - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. - - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. - - ``from_*``: wrap underlying values as this enum type. + ### Variants: + - ``qvm``: Data returned from the QVM, stored as ``QVMResultData`` + - ``qpu``: Data returned from the QPU, stored as ``QPUResultData`` + + ### Methods (each per variant): + - ``is_*``: if the underlying values are that type. + - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. + - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. + - ``from_*``: wrap underlying values as this enum type. """ def __new__(cls, inner: Union[QPUResultData, QVMResultData]) -> "ResultData": @@ -306,28 +316,27 @@ class ResultData: ... def to_register_map(self) -> RegisterMap: """ - Convert ``ResultData`` from its inner representation as ``QVMResultData`` or - ``QPUResultData`` into a ``RegisterMap``. The ``RegisterMatrix`` for each register will be + Convert `ResultData` from its inner representation as `QVMResultData` or + `QPUResultData` into a `RegisterMap`. The `RegisterMatrix` for each register will be constructed such that each row contains all the final values in the register for a single shot. - Errors - ------ + ## Errors - Raises a ``RegisterMatrixConversionError`` if the inner execution data for any of the - registers would result in a jagged matrix. ``QPUResultData`` data is captured per measure, + Raises a `RegisterMatrixConversionError` if the inner execution data for any of the + registers would result in a jagged matrix. `QPUResultData` data is captured per measure, meaning a value is returned for every measure to a memory reference, not just once per shot. This is often the case in programs that use mid-circuit measurement or dynamic control flow, where measurements to the same memory reference might occur multiple times in a shot, or be - skipped conditionally. In these cases, building a rectangular ``RegisterMatrix`` would + skipped conditionally. In these cases, building a rectangular `RegisterMatrix` would necessitate making assumptions about the data that could skew the data in undesirable ways. - Instead, it's recommended to manually build a matrix from ``QPUResultData`` that accurately + Instead, it's recommended to manually build a matrix from `QPUResultData` that accurately selects the last value per-shot based on the program that was run. """ ... def to_raw_readout_data(self) -> Union[RawQPUReadoutData, RawQVMReadoutData]: """ - Get the raw data returned from the QVM or QPU. See ``RawQPUReadoutData`` and - ``RawQVMReadoutData`` for more information. + Get the raw data returned from the QVM or QPU. See `RawQPUReadoutData` and + `RawQVMReadoutData` for more information. """ def inner( self, @@ -347,6 +356,10 @@ class ResultData: @final class ExecutionData: + """ + Contains the `ResultData` and the duration of the execution. + """ + def __new__(cls, result_data: ResultData, duration: Optional[datetime.timedelta] = None): ... @property def result_data(self) -> ResultData: ... @@ -363,17 +376,16 @@ class RegisterData: Values present in a register that are one of a set of variants. Variants: - - ``i8``: Corresponds to the Quil `BIT` or `OCTET` types. - - ``i16``: Corresponds to the Quil `INTEGER` type. - - ``f64``: Corresponds to the Quil `REAL` type. - - ``complex32``: Results containing complex numbers. + - ``i8``: Corresponds to the Quil ``BIT`` or ``OCTET`` types. + - ``i16``: Corresponds to the Quil ``INTEGER`` type. + - ``f64``: Corresponds to the Quil ``REAL`` type. + - ``complex32``: Results containing complex numbers. Methods (each per variant): - - ``is_*``: if the underlying values are that type. - - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. - - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. - - ``from_*``: wrap underlying values as this enum type. - + - ``is_*``: if the underlying values are that type. + - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. + - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. + - ``from_*``: wrap underlying values as this enum type. """ def __new__(cls, inner: Union[List[List[int]], List[List[float]], List[List[complex]]]) -> "RegisterData": ... diff --git a/crates/python/qcs_sdk/client.py b/crates/python/qcs_sdk/client.py new file mode 100644 index 000000000..425e901be --- /dev/null +++ b/crates/python/qcs_sdk/client.py @@ -0,0 +1 @@ +from qcs_sdk.client import * diff --git a/crates/python/qcs_sdk/client.pyi b/crates/python/qcs_sdk/client.pyi index 6386548a6..2cf0e4fdb 100644 --- a/crates/python/qcs_sdk/client.pyi +++ b/crates/python/qcs_sdk/client.pyi @@ -26,9 +26,9 @@ class QCSClient: qvm_url: Optional[str] = None, ) -> "QCSClient": """ - Manually construct a ``QCSClient``. + Manually construct a `QCSClient`. - Prefer to use ``QCSClient.load`` to construct an environment-based profile. + Prefer to use `QCSClient.load` to construct an environment-based profile. """ ... @staticmethod @@ -36,13 +36,14 @@ class QCSClient: profile_name: Optional[str] = None, ) -> "QCSClient": """ - Create a ``QCSClient`` configuration using an environment-based configuration. + Create a `QCSClient` configuration using an environment-based configuration. :param profile_name: The QCS setting's profile name to use. If ``None``, the default value configured in your environment is used. - :raises LoadClientError: If there is an issue loading the profile defails from the environment. + :raises `LoadClientError`: If there is an issue loading the profile defails from the environment. - See for details: https://docs.rigetti.com/qcs/references/qcs-client-configuration#environment-variables-and-configuration-files + See the [QCS documentation](https://docs.rigetti.com/qcs/references/qcs-client-configuration#environment-variables-and-configuration-files) + for more details. """ ... @staticmethod @@ -50,14 +51,15 @@ class QCSClient: profile_name: Optional[str] = None, ) -> "QCSClient": """ - Create a ``QCSClient`` configuration using an environment-based configuration. - (async analog of ``QCSClient.load``) + Create a `QCSClient` configuration using an environment-based configuration. + (async analog of `QCSClient.load`) - :param profile_name: The QCS setting's profile name to use. If ``None``, the default value configured in your environment is used. + :param profile_name: The QCS setting's profile name to use. If `None`, the default value configured in your environment is used. - :raises LoadClientError: If there is an issue loading the profile defails from the environment. + :raises `LoadClientError`: If there is an issue loading the profile defails from the environment. - See for details: https://docs.rigetti.com/qcs/references/qcs-client-configuration#environment-variables-and-configuration-files + See the [QCS documentation](https://docs.rigetti.com/qcs/references/qcs-client-configuration#environment-variables-and-configuration-files) + for more details. """ ... @property @@ -66,11 +68,11 @@ class QCSClient: ... @property def grpc_api_url(self) -> str: - """URL to access the gRPC API.""" + """URL to access the QCS gRPC API.""" ... @property def quilc_url(self) -> str: - """URL to access the `quilc` compiler.""" + """URL to access the ``quilc`1 compiler.""" ... @property def qvm_url(self) -> str: @@ -85,8 +87,8 @@ class QCSClientAuthServer: """ Manually define authentication server parameters. - :param client_id: The OAuth application client ID. If ``None``, a default value is used. - :param issuer: The OAuth token issuer url. If ``None``, a default value is used. + :param client_id: The OAuth application client ID. If `None`, a default value is used. + :param issuer: The OAuth token issuer url. If `None`, a default value is used. """ ... @property diff --git a/crates/python/qcs_sdk/compiler/__init__.pyi b/crates/python/qcs_sdk/compiler/__init__.pyi index cb084f95d..96b75c966 100644 --- a/crates/python/qcs_sdk/compiler/__init__.pyi +++ b/crates/python/qcs_sdk/compiler/__init__.pyi @@ -1 +1,8 @@ -from . import quilc +""" +Module for interacting with the Quil Compiler (quilc). + +🔨 This page is under construction. In the meantime, you can find documentation +in the [the type hints](https://github.com/rigetti/qcs-sdk-rust/blob/main/crates/python/qcs_sdk/compiler/quilc.pyi). +""" + +from qcs_sdk.compiler import quilc diff --git a/crates/python/qcs_sdk/qpu/__init__.pyi b/crates/python/qcs_sdk/qpu/__init__.pyi index 837e42bef..195f57c92 100644 --- a/crates/python/qcs_sdk/qpu/__init__.pyi +++ b/crates/python/qcs_sdk/qpu/__init__.pyi @@ -1,8 +1,14 @@ +""" +The QPU module contains the API for interacting with Rigetti Quantum Processing Units (QPUs). + +🔨 This page is under construction and the documentation for some submodules is missing. +In the meantime, you can find documentation in the [the type hints](https://github.com/rigetti/qcs-sdk-rust/tree/main/crates/python/qcs_sdk/qpu). +""" from typing import Dict, List, Mapping, Sequence, Optional, Union, final from qcs_sdk.client import QCSClient -from . import ( +from qcs_sdk.qpu import ( api as api, isa as isa, rewrite_arithmetic as rewrite_arithmetic, @@ -16,21 +22,21 @@ class ReadoutValues: to a memory reference across all shots. There is a variant for each possible type the list of readout values could be. - Variants: - - ``integer``: Corresponds to the Quil `BIT`, `OCTET`, or `INTEGER` types. - - ``real``: Corresponds to the Quil `REAL` type. - - ``complex``: Corresponds to readout values containing complex numbers + ## Variants: + - ``integer``: Corresponds to the Quil ``BIT``, ``OCTET``, or ``INTEGER`` types. + - ``real``: Corresponds to the Quil ``REAL`` type. + - ``complex``: Corresponds to readout values containing complex numbers - Methods (each per variant): + ## Methods (each per variant): - ``is_*``: if the underlying values are that type. - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. - - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. + - ``to_*``: the underlying values as that type, raises `ValueError` if they are not. - ``from_*``: wrap underlying values as this enum type. """ def __new__(cls, values: Union[List[int], List[float], List[complex]]): - """Construct a new ReadoutValues from a list of values.""" + """Construct a new `ReadoutValues` from a list of values.""" ... def inner(self) -> Union[List[int], List[float], List[complex]]: """Return the inner list of readout values.""" @@ -57,19 +63,20 @@ class MemoryValues: A row of data containing the contents of a memory region at the end of a job. There is a variant for each possible type the memory region could be. - Variants: - - ``binary``: Corresponds to the Quil `BIT` and `OCTET` types. - - ``integer``: Corresponds to the Quil `INTEGER` types. - - ``real``: Corresponds to the Quil `REAL` type. + ## Variants: + - ``binary``: Corresponds to the Quil ``BIT`` and ``OCTET`` types. + - ``integer``: Corresponds to the Quil ``INTEGER`` types. + - ``real``: Corresponds to the Quil `RE`AL`` type. Methods (each per variant): - - ``is_*``: if the underlying values are that type. - - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. - - ``to_*``: the underlying values as that type, raises ``ValueError`` if they are not. - - ``from_*``: wrap underlying values as this enum type. + - ``is_*``: if the underlying values are that type. + - ``as_*``: if the underlying values are that type, then those values, otherwise ``None``. + - ``to_*``: the underlying values as that type, raises `ValueError` if they are not. + - ``from_*``: wrap underlying values as this enum type. """ + def __new__(cls, values: Union[List[int], List[float]]): - """Construct a new ReadoutValues from a list of values.""" + """Construct a new `ReadoutValues` from a list of values.""" ... def inner(self) -> Union[List[int], List[float]]: """Return the inner list of readout values.""" @@ -90,29 +97,36 @@ class MemoryValues: @staticmethod def from_real(inner: Sequence[float]) -> "ReadoutValues": ... - @final class QPUResultData: """ Encapsulates data returned from the QPU after executing a job. - ``QPUResultData`` contains "mappings", which map declared memory regions - in a program (ie. "ro[0]") to that regions readout key in "readout_values". - "readout_values" maps those readout keys to the values emitted for that region + `QPUResultData` contains "mappings", which map declared memory regions + in a program (ie. "ro[0]") to that regions readout key in `QPUResultData.readout_values`. + `readout_values` maps those readout keys to the values emitted for that region across all shots. + + Read more about QPU readout data in the + [QCS Documentation](https://docs.rigetti.com/qcs/guides/qpus-vs-qvms#qpu-readout-data) """ - def __new__(cls, mappings: Mapping[str, str], readout_values: Mapping[str, ReadoutValues], memory_values: Mapping[str, MemoryValues]): ... + def __new__( + cls, + mappings: Mapping[str, str], + readout_values: Mapping[str, ReadoutValues], + memory_values: Mapping[str, MemoryValues], + ): ... @property def mappings(self) -> Dict[str, str]: """ - Get the mappings of a memory region (ie. "ro[0]") to it's key name in readout_values + Get the mappings of a memory region (ie. "ro[0]") to it's key name in `QPUResultData.readout_values` """ ... @property def readout_values(self) -> Dict[str, ReadoutValues]: """ - Get the mappings of a readout values identifier (ie. "q0") to a set of ``ReadoutValues`` + Get the mappings of a readout values identifier (ie. "q0") to a set of `ReadoutValues` """ ... @property @@ -125,9 +139,9 @@ class QPUResultData: self, ) -> RawQPUReadoutData: """ - Get a copy of this result data flattened into a ``RawQPUReadoutData``. This reduces + Get a copy of this result data flattened into a `RawQPUReadoutData`. This reduces the contained data down to primitive types, offering a simpler structure at the - cost of the type safety provided by ``ReadoutValues`` and ``MemoryValues``. + cost of the type safety provided by `ReadoutValues` and `MemoryValues`. """ ... @@ -135,8 +149,8 @@ class QPUResultData: class RawQPUReadoutData: """ Encapsulates data returned from the QPU after executing a job. Compared to - ``QPUReadoutData``, the readout values in this class are returned as lists - of numbers instead of values wrapped by the ``ReadoutValues`` class. + `QPUResultData.readout_values`, the readout values in this class are returned as lists + of numbers instead of values wrapped by the `ReadoutValues` class. """ @property @@ -170,7 +184,7 @@ def list_quantum_processors( """ Returns all available Quantum Processor (QPU) IDs. - :param client: The ``QCSClient`` to use. Creates one using environment configuration if unset - see https://docs.rigetti.com/qcs/references/qcs-client-configuration + :param client: The `qcs_sdk.client.QCSClient` to use. Creates one using environment configuration if unset - see https://docs.rigetti.com/qcs/references/qcs-client-configuration :param timeout: Maximum duration to wait for API calls to complete, in seconds. :raises ListQuantumProcessorsError: If the request to list available QPU IDs failed. @@ -183,9 +197,9 @@ async def list_quantum_processors_async( ) -> List[str]: """ Returns all available Quantum Processor IDs. - (async analog of ``list_quantum_processors``) + (async analog of `list_quantum_processors`) - :param client: The ``QCSClient`` to use. Creates one using environment configuration if unset - see https://docs.rigetti.com/qcs/references/qcs-client-configuration + :param client: The `qcs_sdk.client.QCSClient` to use. Creates one using environment configuration if unset - see https://docs.rigetti.com/qcs/references/qcs-client-configuration :param timeout: Maximum duration to wait for API calls to complete, in seconds. :raises ListQuantumProcessorsError: If the request to list available QPU IDs failed. diff --git a/crates/python/qcs_sdk/qvm/__init__.pyi b/crates/python/qcs_sdk/qvm/__init__.pyi index a1cc5b797..b3ca265eb 100644 --- a/crates/python/qcs_sdk/qvm/__init__.pyi +++ b/crates/python/qcs_sdk/qvm/__init__.pyi @@ -1,9 +1,15 @@ +""" +The QVM module contains the API for interacting with the Quantum Virtual Machine (QVM). + +🔨 This page is under construction and the documentation for some submodules is missing. +In the meantime, you can find documentation in the [the type hints](https://github.com/rigetti/qcs-sdk-rust/tree/main/crates/python/qcs_sdk/qvm). +""" from typing import Dict, List, Mapping, Optional, Sequence, Tuple, Union, final -from qcs_sdk import QCSClient, RegisterData +from qcs_sdk import RegisterData -from . import api as api -from .api import AddressRequest +from qcs_sdk.qvm import api as api +from qcs_sdk.qvm.api import AddressRequest @final class QVMClient: