├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── devdoc.yml │ ├── doc.yml │ ├── isort.yml │ ├── lint.yml │ ├── pub_doc.yml │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── _typos.toml ├── docs ├── assets │ ├── favicon.ico │ ├── index │ │ ├── geometry-visualization.png │ │ ├── hardware-program-visualization.png │ │ ├── program-visualization.png │ │ └── report-visualization.png │ ├── logo-dark.svg │ ├── logo.svg │ └── readme-gifs │ │ ├── graph-select.gif │ │ ├── hover-bitstrings.gif │ │ ├── locations-hover.gif │ │ ├── smart-docs.gif │ │ └── visualize-bitstrings.gif ├── background.md ├── blog │ ├── .authors.yml │ ├── index.md │ └── posts │ │ ├── 2023 │ │ └── bloqade-release.md │ │ └── 2025 │ │ └── a-new-journey.md ├── contrib.md ├── digital │ ├── dialects_and_kernels.md │ ├── examples │ │ ├── deutsch_squin.py │ │ ├── ghz.py │ │ ├── ghz_linear_circuit.svg │ │ ├── ghz_log_circuit.svg │ │ ├── pauli_exponentiation.py │ │ ├── pauli_exponentiation.svg │ │ ├── qaoa.py │ │ ├── qft.py │ │ ├── qft.svg │ │ └── repeat_until_success.py │ ├── index.md │ └── simulator_device │ │ ├── simulator_device.md │ │ └── tasks.md ├── index.md ├── install.md ├── javascripts │ └── katex.js ├── manifesto.md ├── quick_start │ ├── analog │ │ ├── geometry-visualization.png │ │ ├── index.md │ │ ├── ipython-hints.gif │ │ ├── jupyter-hints.gif │ │ ├── pulse-sequence-visualization.png │ │ ├── pycharm-hints.gif │ │ ├── report-visualization.png │ │ └── vscode-hints.gif │ └── circuits │ │ ├── API │ │ ├── qasm2_core.md │ │ ├── qasm2_inline_defunct.md │ │ ├── qasm2_noise.md │ │ ├── qasm2_parallel.md │ │ └── qasm2_uop.md │ │ ├── compiler_passes │ │ ├── asap_parallelism.md │ │ ├── circuit_simplification.md │ │ ├── index.md │ │ └── native_gate_rewrite.md │ │ ├── index.md │ │ ├── interpreters_and_analysis │ │ ├── index.md │ │ ├── pyqrack_emulator.md │ │ └── qasm2_codegen.md │ │ ├── qft-pprint.png │ │ └── qft-qasm2.png ├── reference │ ├── analog.md │ ├── analysis.md │ ├── device.md │ ├── index.md │ ├── pyqrack.md │ ├── qasm2.md │ ├── qbraid.md │ ├── squin.md │ ├── stim.md │ ├── task.md │ ├── types.md │ └── visual.md ├── scripts │ └── gen_ref_nav.py └── stylesheets │ └── extra.css ├── justfile ├── mkdocs.yml ├── pyproject.toml ├── src └── bloqade │ └── README.md └── test ├── __init__.py └── test_hello.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | schedule: 9 | - cron: '00 01 * * *' 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | python-version: ["3.10", "3.11", "3.12"] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Install uv 24 | uses: astral-sh/setup-uv@v6 25 | with: 26 | # Install a specific version of uv. 27 | version: "0.6.14" 28 | enable-cache: true 29 | cache-dependency-glob: "uv.lock" 30 | - name: Set up Python ${{ matrix.python-version }} 31 | run: uv python install ${{ matrix.python-version }} 32 | - name: Install the project 33 | run: uv sync --all-extras --dev 34 | - name: Run tests 35 | # For example, using `pytest` 36 | run: uv run pytest 37 | -------------------------------------------------------------------------------- /.github/workflows/devdoc.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Devopment Branch Docs 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | documentation: 13 | name: Deploy dev documentation 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v6 21 | with: 22 | # Install a specific version of uv. 23 | version: "0.5.1" 24 | enable-cache: true 25 | cache-dependency-glob: "uv.lock" 26 | - name: Install Documentation dependencies 27 | run: uv sync --group doc 28 | - name: Set up build cache 29 | uses: actions/cache@v4 30 | id: cache 31 | with: 32 | key: mkdocs-material-${{ github.ref }} 33 | path: .cache 34 | restore-keys: | 35 | mkdocs-material- 36 | # derived from: 37 | # https://github.com/RemoteCloud/public-documentation/blob/dev/.github/workflows/build_docs.yml 38 | - name: Configure Git user 39 | run: | 40 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 41 | git config --local user.name "github-actions[bot]" 42 | - name: Deploy documentation 43 | env: 44 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 45 | GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }} 46 | run: | 47 | git fetch origin gh-pages --depth=1 48 | uv run mike deploy -p dev 49 | -------------------------------------------------------------------------------- /.github/workflows/doc.yml: -------------------------------------------------------------------------------- 1 | name: Documentation (preview) 2 | on: 3 | pull_request: 4 | types: 5 | - opened 6 | - reopened 7 | - synchronize 8 | - closed 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | documentation: 15 | name: Deploy preview documentation 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v6 21 | with: 22 | # Install a specific version of uv. 23 | version: "0.5.1" 24 | enable-cache: true 25 | cache-dependency-glob: "uv.lock" 26 | - name: Install Documentation dependencies 27 | run: uv sync --group doc 28 | - name: Set up build cache 29 | uses: actions/cache@v4 30 | id: cache 31 | with: 32 | key: mkdocs-material-${{ github.ref }} 33 | path: .cache 34 | restore-keys: | 35 | mkdocs-material- 36 | - name: Depoly documentation 37 | env: 38 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 39 | run: | 40 | uv run mkdocs build 41 | - name: Deploy preview 42 | uses: rossjrw/pr-preview-action@v1 43 | with: 44 | source-dir: ./site 45 | -------------------------------------------------------------------------------- /.github/workflows/isort.yml: -------------------------------------------------------------------------------- 1 | name: Run isort 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: isort/isort-action@v1 17 | with: 18 | sortPaths: "src" # only sort files in the src directory 19 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | ruff: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: chartboost/ruff-action@v1 17 | black: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: psf/black@stable 22 | -------------------------------------------------------------------------------- /.github/workflows/pub_doc.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Release Docs 2 | on: 3 | push: 4 | tags: 5 | - "v*" 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | documentation: 13 | name: Deploy release documentation 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v6 21 | with: 22 | # Install a specific version of uv. 23 | version: "0.5.1" 24 | enable-cache: true 25 | cache-dependency-glob: "uv.lock" 26 | - name: Install Documentation dependencies 27 | run: uv sync --group doc 28 | - name: Set up build cache 29 | uses: actions/cache@v4 30 | id: cache 31 | with: 32 | key: mkdocs-material-${{ github.ref }} 33 | path: .cache 34 | restore-keys: | 35 | mkdocs-material- 36 | # derived from: 37 | # https://github.com/RemoteCloud/public-documentation/blob/dev/.github/workflows/build_docs.yml 38 | - name: Configure Git user 39 | run: | 40 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 41 | git config --local user.name "github-actions[bot]" 42 | - name: Set release notes tag 43 | run: | 44 | export TAG_PATH=${{ github.ref }} 45 | echo ${TAG_PATH} 46 | echo "TAG_VERSION=${TAG_PATH##*/}" >> $GITHUB_ENV 47 | echo ${TAG_VERSION} 48 | - name: Deploy documentation 49 | env: 50 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 51 | run: | 52 | git fetch origin gh-pages --depth=1 53 | uv run mike deploy --update-alias --push ${TAG_VERSION} latest 54 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | name: Build distribution 📦 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Install uv 13 | uses: astral-sh/setup-uv@v6 14 | with: 15 | # Install a specific version of uv. 16 | version: "0.5.5" 17 | - name: Install the project 18 | run: uv sync --all-extras --dev 19 | - name: Build distribution 📦 20 | run: uv build 21 | - name: Store the distribution packages 22 | uses: actions/upload-artifact@v4 23 | with: 24 | name: python-package-distributions 25 | path: dist/ 26 | 27 | publish-to-pypi: 28 | name: >- 29 | Publish Python 🐍 distribution 📦 to PyPI 30 | if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes 31 | needs: 32 | - build 33 | runs-on: ubuntu-latest 34 | environment: 35 | name: pypi 36 | url: https://pypi.org/p/bloqade # Replace with your PyPI project name 37 | permissions: 38 | id-token: write # IMPORTANT: mandatory for trusted publishing 39 | 40 | steps: 41 | - name: Download all the dists 42 | uses: actions/download-artifact@v4 43 | with: 44 | name: python-package-distributions 45 | path: dist/ 46 | - name: Publish distribution 📦 to PyPI 47 | uses: pypa/gh-action-pypi-publish@release/v1 48 | 49 | github-release: 50 | name: >- 51 | Sign the Python 🐍 distribution 📦 with Sigstore 52 | and upload them to GitHub Release 53 | needs: 54 | - publish-to-pypi 55 | runs-on: ubuntu-latest 56 | 57 | permissions: 58 | contents: write # IMPORTANT: mandatory for making GitHub Releases 59 | id-token: write # IMPORTANT: mandatory for sigstore 60 | 61 | steps: 62 | - name: Download all the dists 63 | uses: actions/download-artifact@v4 64 | with: 65 | name: python-package-distributions 66 | path: dist/ 67 | - name: Sign the dists with Sigstore 68 | uses: sigstore/gh-action-sigstore-python@v3.0.0 69 | with: 70 | inputs: >- 71 | ./dist/*.tar.gz 72 | ./dist/*.whl 73 | - name: Create GitHub Release 74 | env: 75 | GITHUB_TOKEN: ${{ github.token }} 76 | run: >- 77 | gh release create 78 | '${{ github.ref_name }}' 79 | --repo '${{ github.repository }}' 80 | --notes "" 81 | - name: Upload artifact signatures to GitHub Release 82 | env: 83 | GITHUB_TOKEN: ${{ github.token }} 84 | # Upload to GitHub Release using the `gh` CLI. 85 | # `dist/` contains the built packages, and the 86 | # sigstore-produced signatures and certificates. 87 | run: >- 88 | gh release upload 89 | '${{ github.ref_name }}' dist/** 90 | --repo '${{ github.repository }}' 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # random 2 | job.json 3 | out.txt 4 | .DS_Store 5 | tests/data/jobs/ 6 | main.html 7 | debug/ 8 | 9 | # Byte-compiled / optimized / DLL files 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | wheels/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .nox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | cover/ 61 | 62 | # Translations 63 | *.mo 64 | *.pot 65 | 66 | # Django stuff: 67 | *.log 68 | local_settings.py 69 | db.sqlite3 70 | db.sqlite3-journal 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # doc slides 83 | docs/slides/**/**.pdf 84 | docs/slides/build/**/**.png 85 | **/**/**.pptx 86 | 87 | # mkdocs 88 | site/ 89 | docs/reference/bloqade/ 90 | 91 | # PyBuilder 92 | .pybuilder/ 93 | target/ 94 | 95 | # Jupyter Notebook 96 | .ipynb_checkpoints 97 | 98 | # IPython 99 | profile_default/ 100 | ipython_config.py 101 | 102 | # pyenv 103 | # For a library or package, you might want to ignore these files since the code is 104 | # intended to run in multiple environments; otherwise, check them in: 105 | # .python-version 106 | 107 | # pipenv 108 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 109 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 110 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 111 | # install all needed dependencies. 112 | #Pipfile.lock 113 | 114 | # poetry 115 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 116 | # This is especially recommended for binary packages to ensure reproducibility, and is more 117 | # commonly ignored for libraries. 118 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 119 | #poetry.lock 120 | 121 | # pdm 122 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 123 | #pdm.lock 124 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 125 | # in version control. 126 | # https://pdm.fming.dev/#use-with-ide 127 | .pdm.toml 128 | 129 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 130 | __pypackages__/ 131 | 132 | # Celery stuff 133 | celerybeat-schedule 134 | celerybeat.pid 135 | 136 | # SageMath parsed files 137 | *.sage.py 138 | 139 | # Environments 140 | .env 141 | .venv* 142 | env/ 143 | venv/ 144 | ENV/ 145 | env.bak/ 146 | venv.bak/ 147 | 148 | # Spyder project settings 149 | .spyderproject 150 | .spyproject 151 | 152 | # Rope project settings 153 | .ropeproject 154 | 155 | # mkdocs documentation 156 | /site 157 | 158 | # mypy 159 | .mypy_cache/ 160 | .dmypy.json 161 | dmypy.json 162 | 163 | # Pyre type checker 164 | .pyre/ 165 | 166 | # pytype static type analyzer 167 | .pytype/ 168 | 169 | # Cython debug symbols 170 | cython_debug/ 171 | 172 | # PyCharm 173 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 174 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 175 | # and can be added to the global gitignore or merged into this file. For a more nuclear 176 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 177 | #.idea/ 178 | 179 | .vscode 180 | !.vscode/settings.json 181 | .pdm-python 182 | main.py 183 | *.ipynb 184 | *.json 185 | .ruff_cache 186 | .python-version 187 | !package.json 188 | !src/**/**/main.py 189 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v5.0.0 6 | hooks: 7 | - id: check-yaml 8 | args: ['--unsafe'] 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | - repo: https://github.com/pycqa/isort 12 | rev: 5.13.2 13 | hooks: 14 | - id: isort 15 | name: isort (python) 16 | - repo: https://github.com/psf/black 17 | rev: 24.10.0 18 | hooks: 19 | - id: black 20 | - repo: https://github.com/charliermarsh/ruff-pre-commit 21 | # Ruff version. 22 | rev: "v0.9.2" 23 | hooks: 24 | - id: ruff 25 | - repo: https://github.com/crate-ci/typos 26 | rev: v1.29.4 27 | hooks: 28 | - id: typos 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | The Bloqade Project is under the Apache License v2.0 with LLVM Exceptions: 3 | ============================================================================== 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | 182 | APPENDIX: How to apply the Apache License to your work. 183 | 184 | To apply the Apache License to your work, attach the following 185 | boilerplate notice, with the fields enclosed by brackets "[]" 186 | replaced with your own identifying information. (Don't include 187 | the brackets!) The text should be enclosed in the appropriate 188 | comment syntax for the file format. We also recommend that a 189 | file or class name and description of purpose be included on the 190 | same "printed page" as the copyright notice for easier 191 | identification within third-party archives. 192 | 193 | Copyright [yyyy] [name of copyright owner] 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | 207 | 208 | ---- LLVM Exceptions to the Apache 2.0 License ---- 209 | 210 | As an exception, if, as a result of your compiling your source code, portions 211 | of this Software are embedded into an Object form of such source code, you 212 | may redistribute such embedded portions in such Object form without complying 213 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 214 | 215 | In addition, if you combine or link compiled forms of this Software with 216 | software that is licensed under the GPLv2 ("Combined Software") and if a 217 | court of competent jurisdiction determines that the patent provision (Section 218 | 3), the indemnity provision (Section 9) or other Section of the License 219 | conflicts with the conditions of the GPLv2, you may retroactively and 220 | prospectively choose to deem waived or otherwise exclude such Section(s) of 221 | the License, but only in their entirety and only with respect to the Combined 222 | Software. 223 | 224 | ============================================================================== 225 | Software from third parties included in the Bloqade Project: 226 | ============================================================================== 227 | The Bloqade Project contains third party software which is under different license 228 | terms. All such code will be identified clearly using at least one of two 229 | mechanisms: 230 | 1) It will be in a separate directory tree with its own `LICENSE.txt` or 231 | `LICENSE` file at the top containing the specific license and restrictions 232 | which apply to that software, or 233 | 2) It will contain specific license and restriction terms at the top of every 234 | file. 235 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to Bloqade -- QuEra's Neutral Atom SDK 2 | 3 | [![CI](https://github.com/QuEraComputing/bloqade/actions/workflows/ci.yml/badge.svg)](https://github.com/QuEraComputing/bloqade/actions/workflows/ci.yml) 4 | [![codecov](https://codecov.io/gh/QuEraComputing/bloqade/graph/badge.svg?token=BpHsAYuzdo)](https://codecov.io/gh/QuEraComputing/bloqade) 5 | [![Supported Python versions](https://img.shields.io/pypi/pyversions/bloqade.svg?color=%2334D058)](https://pypi.org/project/bloqade) 6 | [![Documentation](https://img.shields.io/badge/Documentation-6437FF)](https://bloqade.quera.com/) 7 | [![DOI](https://zenodo.org/badge/629628885.svg)](https://zenodo.org/doi/10.5281/zenodo.11114109) 8 | 9 | 10 | Bloqade is a Python SDK for neutral atom quantum computing. It provides a set of embedded domain-specific languages (eDSLs) for programming neutral atom quantum computers. Bloqade is designed to be a high-level, user-friendly SDK that abstracts away the complexities of neutral atom quantum computing, allowing users to focus on developing quantum algorithms and compilation strategies for neutral atom quantum computers. 11 | 12 | > [!IMPORTANT] 13 | > 14 | > This project is in the early stage of development. API and features are subject to change. 15 | 16 | ## Installation 17 | 18 | ### Install via `uv` (Recommended) 19 | 20 | ```py 21 | uv add bloqade 22 | ``` 23 | 24 | ## Documentation 25 | 26 | The documentation is available at [https://bloqade.quera.com/latest/](https://bloqade.quera.com/latest/). We are at an early stage of completing the documentation with more details and examples, so comments and contributions are most welcome! 27 | 28 | ## Roadmap 29 | 30 | We use github issues to track the roadmap. There are more feature requests and proposals in the issues. Here are some of the most wanted features we wish to implement by 2025 summer (July): 31 | 32 | - [x] QASM2 dialect (dialect, parser, pyqrack backend, ast, codegen) 33 | - [x] QASM2 extensions (e.g. parallel gates, noise, etc.) 34 | - [x] STIM dialect (dialect, codegen) 35 | - [ ] structural gate dialect (language proposal, dialect, passes) 36 | - [ ] atom-move dialect (language proposal, dialect, passes) 37 | - [ ] atom move animation backend 38 | 39 | Proposal for the roadmap and feature requests are welcome! 40 | 41 | ## License 42 | 43 | Apache License 2.0 with LLVM Exceptions 44 | -------------------------------------------------------------------------------- /_typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | extend-ignore-identifiers-re = [ 3 | # *sigh* this just isn't worth the cost of fixing 4 | "AttributeID.*Supress.*", 5 | ] 6 | 7 | [default.extend-identifiers] 8 | # *sigh* this just isn't worth the cost of fixing 9 | AttributeIDSupressMenu = "AttributeIDSupressMenu" 10 | 11 | [default.extend-words] 12 | Braket = "Braket" 13 | mch = "mch" 14 | IY = "IY" 15 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/index/geometry-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/index/geometry-visualization.png -------------------------------------------------------------------------------- /docs/assets/index/hardware-program-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/index/hardware-program-visualization.png -------------------------------------------------------------------------------- /docs/assets/index/program-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/index/program-visualization.png -------------------------------------------------------------------------------- /docs/assets/index/report-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/index/report-visualization.png -------------------------------------------------------------------------------- /docs/assets/logo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /docs/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /docs/assets/readme-gifs/graph-select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/readme-gifs/graph-select.gif -------------------------------------------------------------------------------- /docs/assets/readme-gifs/hover-bitstrings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/readme-gifs/hover-bitstrings.gif -------------------------------------------------------------------------------- /docs/assets/readme-gifs/locations-hover.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/readme-gifs/locations-hover.gif -------------------------------------------------------------------------------- /docs/assets/readme-gifs/smart-docs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/readme-gifs/smart-docs.gif -------------------------------------------------------------------------------- /docs/assets/readme-gifs/visualize-bitstrings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/assets/readme-gifs/visualize-bitstrings.gif -------------------------------------------------------------------------------- /docs/background.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | ## Neutral Atom Qubits 4 | 5 | A key feature of a quantum computer is the ability to physically represent qubits. In neutral atom computers, the qubit is represented in the electronic state of the valence electron of Rubdidium 87. Arrays of individual atoms are held by laser tweezers, and quantum computations are executed by manipulating the electronic state of each atom using lasers and RF fields. Entanglement can be generated using the [Rydberg state](https://en.wikipedia.org/wiki/Rydberg_atom), which is a highly excited state that strongly interacts with adjacent atoms through a $R^{-6}$ power law Van der Waals force. 6 | 7 | 8 | 9 | ## Analog mode Quantum Computing 10 | 11 | There are two modes of quantum computation that [neutral atoms](#neutral-atom-qubits) are capable of: [*Analog*](#analog-mode) and [*Digital*](#digital-mode). In analog mode, the qubit is represented as in a ground state and a Rydberg state of an atom. The atoms are placed in user-specified arbitrary positions in a 2d space, and quantum computations can be enacted by driving the atoms between the ground and Rydberg state. However, adjacent atoms in the Rydberg state are always interacting, so the computation is done through a time evolution of the atoms via the Schrodinger equation 12 | 13 | $$ 14 | i \hbar \dfrac{\partial}{\partial t} | \psi \rangle = \hat{\mathcal{H}}(t) | \psi \rangle, \\ 15 | $$ 16 | 17 | Were $H$ is a time-dependent "Rydberg atom" Hamiltonian. 18 | 19 | $$ 20 | \frac{\mathcal{H}(t)}{\hbar} = \sum_j \frac{\Omega_j(t)}{2} \left( e^{i \phi_j(t) } | g_j \rangle \langle r_j | + e^{-i \phi_j(t) } | r_j \rangle \langle g_j | \right) - \sum_j \Delta_j(t) \hat{n}_j + \sum_{j < k} V_{jk} \hat{n}_j \hat{n}_k, 21 | $$ 22 | 23 | where: $\Omega_j$, $\phi_j$, and $\Delta_j$ denote the Rabi frequency *amplitude*, laser *phase*, and the *detuning* of the driving laser field on atom (qubit) $j$ coupling the two states $| g_j \rangle$ (ground state) and $| r_j \rangle$ (Rydberg state); $\hat{n}_j = |r_j\rangle \langle r_j|$ is the number operator, and $V_{jk} = C_6/|\mathbf{x}_j - \mathbf{x}_k|^6$ describes the Rydberg interaction (van der Waals interaction) between atoms $j$ and $k$ where $\mathbf{x}_j$ denotes the *position* of the atom $j$; $C_6$ is the Rydberg interaction constant that depends on the particular Rydberg state used. For Bloqade, the default $C_6 = 862690 \times 2\pi \text{ MHz μm}^6$ for $|r \rangle = \lvert 70S_{1/2} \rangle$ of the $^{87}$Rb atoms; $\hbar$ is the reduced Planck's constant. 24 | 25 | 26 | For a more nuanced read about the neutral atoms that Bloqade and *Aquila* use, refer to QuEra's qBook section on [Qubits by puffing up atoms](https://qbook.quera.com/learn/?course=6630211af30e7d0013c66147&file=6630211af30e7d0013c66149). 27 | 28 | You can find a brief explanation of the distinction below but for a more in-depth explanation you can refer to QuEra's qBook section on [Analog vs Digital Quantum Computing](https://qbook.quera.com/learn/?course=6630211af30e7d0013c66147&file=6630211af30e7d0013c6614a). For more details on QuEra's cloud-accessible analog mode computer Aquila, please check out the [Aquila whitepaper](https://arxiv.org/abs/2306.11727). 29 | 30 | ### Digital Mode 31 | 32 | In the Digital Mode individual or multiple groups of qubits are controlled by applying *gates* (individual unitary operations). The digital mode qubit is represented in the two hyperfine clock ground states of the Rubidium 87 atom. These two states are extremely weakly interactive with the environment and other adjacent atoms, which leads to a very long coherence time upwards of 1 second. Single-qubit gates can be executed through a Raman laser drive coupling the two states to enact arbitrary rotations. 33 | 34 | Unlike Analog mode where the Rydberg state is persistent as part of the qubit encoding into the electronic states, digital mode only temporarily excites the atoms to the Rydberg state in order to interact with adjacent qubits, a process which typically takes less than ~1usec. Thus, a neutral atom entangling gate is executed by bringing multiple atoms together within the Rydberg blockade radius, and then doing some time-dependent drive between the hyperfine ground states and the Rydberg state, so that the final state returns to the hyperfine ground states. Due to the Rydberg blockade, only one atom can be in the Rydberg state at a time, which creates entanglement between the atoms. For more details see this paper on a [recent demonstration of high fidelity gates](https://www.nature.com/articles/s41586-023-06481-y). 35 | 36 | A unique advantage of reconfigurable neutral atom architectures is parallelism: the same laser can effect many lasers by aiming it in the same plane as the atom array. A single global Raman laser can enact the same parallel single-qubit gate on all qubits at the same time, and a single Rydberg laser (technically, two counter-propagating) can enact the same parallel multi-qubit gate on all cliques of qubits in an entangling region of the array. For more details see this paper on a [recent demonstration of reconfigurable architectures](https://www.nature.com/articles/s41586-023-06927-3). For this reason, it is important to represent quantum executions and circuits to be as parallel as possible. In our qasm2 dialect, we have extended qasm to natively include parallelism-- for example, `qasm2.parallel.cx(controls, targets)` represents a parallel CNOT gate between a list of `controls` on a list of `targets`. 37 | 38 | 39 | ### Reconfigurable architectures and "all to all" connectivity 40 | 41 | A second advantage of reconfigurable neutral atom architectures is reconfigurability: atoms can be moved in parallel between sites in the array. QuEra's devices will have a *zoned architecture*, with distinct storage and entanglement zones and the ability to move atoms between them using a set of dynamic crossed AOD laser tweezers. This mobility can be considered as an *efficient parallel swap* gate, where any qubit can easily be moved to be adjacent to any other to enact entangling gates. For this reason, reconfigurable neutral atoms do not have a "connectivity graph" in the traditional sense-- instead, they have an "all-to-all" connectivity. There are still some technical constraints on this connectivity due to restrictions on the crossed AOD which we will detail when we open-source a move level dialect set in the near future. 42 | -------------------------------------------------------------------------------- /docs/blog/.authors.yml: -------------------------------------------------------------------------------- 1 | authors: 2 | rogerluo: 3 | name: Xiuzhe (Roger) Luo 4 | description: I cast quantum spells. Senior Scientific Software Engineer at QuEra Computing Inc. 5 | avatar: https://github.com/Roger-luo.png 6 | url: https://rogerluo.dev 7 | weinbe58: 8 | name: Phillip Weinberg 9 | description: I build Classical and Quantum software. Senior Scientific Software Engineer at QuEra Computing Inc. 10 | avatar: https://github.com/weinbe58.png 11 | url: https://github.com/weinbe58 12 | kaihsin: 13 | name: Kai-Hsin Wu 14 | description: Scientific Software Engineer at QuEra Computing Inc. 15 | avatar: https://github.com/kaihsin.png 16 | url: https://github.com/kaihsin 17 | johnzl-777: 18 | name: John Long 19 | description: Scientific Software Developer at QuEra Computing Inc. 20 | avatar: https://github.com/johnzl-777.png 21 | url: https://github.com/johnzl-777 22 | yuval: 23 | name: Yuval Boger 24 | description: Chief Commercial Officer at QuEra Computing Inc. 25 | avatar: https://media.licdn.com/dms/image/v2/D5603AQHYz_g9GdF7jQ/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1690639226393?e=1746057600&v=beta&t=Jji90LOhwmtBohp3FtD5peXCqkO_QFzOyt7gzdHNCZI 26 | url: https://www.linkedin.com/in/yuvalboger/ 27 | shengtao: 28 | name: Shengtao Wang 29 | description: Quantum Algorithms & Applications Manager at QuEra Computing Inc. 30 | avatar: https://media.licdn.com/dms/image/v2/C5603AQH8_yNWMmTWbw/profile-displayphoto-shrink_400_400/profile-displayphoto-shrink_400_400/0/1572705692501?e=1746057600&v=beta&t=T_XyG8bvHA4gNIwZvGb0oteEXLO_6GaBx3p2P2e_Llw 31 | url: https://www.linkedin.com/in/shengtao-wang-aa6548178/ 32 | takuya: 33 | name: Takuya Kitagawa 34 | description: President of QuEra Computing Inc. 35 | avatar: https://media.licdn.com/dms/image/v2/C4D03AQH1s7sGmGmfag/profile-displayphoto-shrink_400_400/profile-displayphoto-shrink_400_400/0/1516633791754?e=1746057600&v=beta&t=ClOY_mraAuv0uN7hQZhugcsYqPUlSvdBVG9IgtXb5sw 36 | url: https://www.linkedin.com/in/takuya-kitagawa-2b366117/ 37 | dean: 38 | name: Dean Bogdanovic 39 | description: Discovering new technology applications. Senior Vice President of Engineering at QuEra Computing Inc. 40 | avatar: https://www.sdmmag.com/ext/resources/2022/05/03/Alef_Dean-Bogdanovic_Headshot.jpeg?1651611331 41 | url: https://www.linkedin.com/in/dbogdanovic/ 42 | jwurtz: 43 | name: Jonathan Wurtz 44 | description: Quantum Solutions Lead at QuEra Computing Inc. 45 | avatar: https://github.com/jon-wurtz.png 46 | url: https://www.linkedin.com/in/jonathan-wurtz-7a4855181/ 47 | -------------------------------------------------------------------------------- /docs/blog/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/blog/index.md -------------------------------------------------------------------------------- /docs/blog/posts/2023/bloqade-release.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2023-12-15 3 | authors: 4 | - johnzl-777 5 | - weinbe58 6 | - kaihsin 7 | - rogerluo 8 | --- 9 | 10 | # Introducing Bloqade SDK for Python 11 | 12 | ![Bloqade Logo](../../../assets/logo.svg#only-light) 13 | ![Bloqade Logo](../../../assets/logo-dark.svg#only-dark) 14 | 15 | Greetings Neutral Atom QC experts, enthusiasts, and newcomers! 16 | 17 | We are ~~excited to the Rydberg state~~ thrilled to announce the Python version of our cutting-edge SDK, Bloqade. Originally developed in Julia, Bloqade has been a game-changer in the realm of Neutral Atom quantum computing. With the introduction of the Python version, we aim to make this revolutionary technology more accessible and user-friendly than ever before. 18 | 19 | 20 | ## Why Python? 21 | 22 | Python is one of the most widely used programming languages, especially in the quantum computing community and broader scientific communities. By extending Bloqade to Python, we are opening doors to a broader audience, enabling more developers, researchers, and organizations to harness the power of Neutral Atom quantum computing. 23 | 24 | 25 | ## Neutral Atom Quantum Computing 26 | 27 | Recently, the Neutral Atom platform has come on the QC scene in the form of Analog Hamiltonian Simulators that have a broad set of use cases beyond quantum circuits. Ranging from simulating unique quantum phases of matter, solving combinatorial optimization problems, and machine learning applications, the analog mode provides strong values in solving practical, interesting problems in the near term. 28 | 29 | These advances are crucial milestones on the way towards scalable digital gate-based architecture using atom shuttling. This new technology and its novel applications demand a paradigm shift in the way we not only think about quantum computing, but translate those ideas to real hardware. Enter Bloqade, a next-generation SDK designed to put the power of neutral atoms at your fingertips. 30 | 31 | ## Why Bloqade? 32 | 33 | Bloqade is designed with the primary goal of making it easier to compose programs for QuEra’s hardware and analyze results. 34 | 35 | We've gained valuable insights into how users have used our neutral-atom hardware and with it, their struggles with existing tools. We took advantage of this knowledge to produce a tool that could take the "hard" out of "hardware". Bloqade is precision-balanced in both *flexibility* to empower novices to experiment with ease and *power* to let experts perform cutting-edge work without breaking a sweat. 36 | 37 | ### Highlights 38 | 39 | #### Smart Documentation 40 | 41 | With our commitment to enabling more seamless program development, we've put the relevant documentation you need right *where* and *when* you need it. 42 | 43 | No more obnoxious switching between your favorite coding environment and documentation in a separate window. Let Bloqade guide you where you'd like to go: 44 | 45 | ![](../../../assets/readme-gifs/smart-docs.gif) 46 | 47 | #### Fully Parameterized Analog Programs 48 | 49 | *Parameter sweeps* are a common theme of programs for analog quantum computers, where a user would like to observe differences in output results by varying a value or values in their program. 50 | 51 | You used to have to manually crank out variations of your program with different values and then keep track of all the individual submissions to the emulator and hardware, a mess to keep track of and process the results of afterwards. 52 | 53 | Bloqade eliminates this with its own support for variables that can later be assigned single values or a whole sequence of values for trivial parameter sweeping. This isn't some feature that's constrained to a certain backend, you can take your program with all its variables and submit it to your choice of emulator or our hardware directly. 54 | 55 | ```python 56 | from bloqade import var 57 | from bloqade.atom_arrangement import Square 58 | 59 | import numpy as np 60 | 61 | adiabatic_durations = [0.4, 3.2, 0.4] 62 | 63 | # create variables explicitly... 64 | max_detuning = var("max_detuning") 65 | # ...or implicitly inside the program definition. 66 | adiabatic_program = ( 67 | Square(3, "lattice_spacing") 68 | .rydberg.rabi.amplitude.uniform.piecewise_linear( 69 | durations=adiabatic_durations, values=[0.0, "max_rabi", "max_rabi", 0.0] 70 | ) 71 | .detuning.uniform.piecewise_linear( 72 | durations=adiabatic_durations, 73 | values=[ 74 | -max_detuning, # scalar variables support direct arithmetic operations 75 | -max_detuning, 76 | max_detuning, 77 | max_detuning, 78 | ], 79 | ) 80 | .assign(max_rabi=15.8, max_detuning=16.33) 81 | .batch_assign(lattice_spacing=np.arrange(4.0, 7.0, 0.5)) 82 | ) 83 | 84 | # Launch your program on your choice of Braket or in-house emulator... 85 | emu_results = adiabatic_program.braket.local_emulator().run(10000) 86 | faster_emu_results = adiabatic_program.bloqade.python().run(10000) 87 | # ...as well as hardware without stress 88 | hw_results = adiabatic_program.parallelize(24).braket.aquila().run_async(100) 89 | 90 | ``` 91 | 92 | #### Integrated Visualization Tools 93 | 94 | Instantly understand what your programs are doing faster than you can say "neutral atoms rock!" with Bloqade's built-in visualization tools: 95 | 96 | 97 | ![](../../../assets/readme-gifs/locations-hover.gif) 98 | 99 | ![](../../../assets/readme-gifs/graph-select.gif) 100 | 101 | 102 | For your results, no more obnoxious manual compilation of results across different parameters or wrangling them into more useful forms. Get insights of experiment outcomes in the blink of an eye: 103 | 104 | ![](../../../assets/readme-gifs/visualize-bitstrings.gif) 105 | 106 | ![](../../../assets/readme-gifs/hover-bitstrings.gif) 107 | 108 | Now that's what we call having your cake AND eating it. 109 | 110 | 111 | ## Bloqade Roadmap 112 | 113 | ### Bloqade Alpha Phase 114 | 115 | During the next year, we plan on continuing development of Bloqade's python interface. If you are as excited about Neutral Atom quantum computing as us, or heck, even just quantum physics in general, give Bloqade a try! This is your opportunity to influence the direction of Bloqade and get in on the ground floor of the next Quantum Computing revolution. 116 | 117 | ### But what about Julia? 118 | 119 | ***Don't you guys already HAVE an SDK in Julia? Why do you need two SDKs?*** 120 | 121 | 122 | That's right! However, there's a key motivating factor for the reason we created Bloqade Python that's distinct for Bloqade.jl's existence. 123 | 124 | Bloqade.jl is primarily geared as a *high-performance emulator*. It allows you to design complex neutral-atom algorithms that may not necessarily run on our hardware BUT are excellent if you're exploring novel physical phenonema/algorithms or as a tool for pedagogical purposes. 125 | 126 | Bloqade.jl does have the ability to submit to [*Aquila*](https://www.quera.com/aquila), our flagship quantum computer, but for more complex tasks such as sweeping parameters (e.g. running the same program on hardware with slightly different parameters each time) or advanced post-processing, it becomes cumbersome quite quickly. 127 | 128 | There are no plans to drop support any time soon though. On the contrary, we plan on fully integrating Bloqade.jl into the Python package, which will enable you to program Neutral Atom quantum hardware without having to choose. 129 | 130 | We very much look forward to you trying out Bloqade! 131 | -------------------------------------------------------------------------------- /docs/blog/posts/2025/a-new-journey.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2025-03-01 3 | authors: 4 | - jwurtz 5 | - rogerluo 6 | - kaihsin 7 | - weinbe58 8 | - johnzl-777 9 | --- 10 | # A new journey for Bloqade 11 | 12 | In 2023 we were excited to introduce Bloqade, a python SDK for programming and interfacing with analog mode neutral atom hardware based off feedback from our community as well as a need to make conducting experiments on our hardware easier. Today, we introduce the next generation of Bloqade: as well as programming analog-mode computation, our new bloqade module enables programming gate-based computation, with an eye on near-term NISQ demonstrations and intermediate-term fault tolerant solutions. Don’t worry; all of your favorite features of the previous generation of Bloqade are still there under the `bloqade.analog` namespace, but now you can explore digital-mode computation specialized to reconfigurable neutral atom architectures. 13 | Why have we built this new module? There are plenty of incredible quantum programming packages, such as [Qiskit]( https://www.ibm.com/quantum/qiskit) and [Cirq]( https://quantumai.google/cirq), as well as an entire ecosystem of middleware providers with specialized pipelines to turn abstract problems into circuits. However, these packages may not be everything that is needed for efficient hardware execution on neutral atom hardware: **a circuits-only representation of quantum executions may be an insufficient abstraction for effective hardware-level programs**. This is a challenge: we want to enable everyone to maximally leverage the power of neutral atom quantum computers beyond abstract circuit representations. For this reason, we are building Bloqade to be a hardware-oriented SDK to represent hybrid executions on reconfigurable neutral atom hardware. In this way, Bloqade can be integrated into the larger ecosystem—for example, [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) of QASM from a Bloqade program, but be an SDK specialized to our hardware: **THE SDK for neutral atoms**. 14 | 15 | The vision of Bloqade is to empower quantum scientists, working on things ranging from applications development to algorithmic co-design, to build hybrid quantum-classical programs that leverage the strength of neutral atom quantum computers and have a real chance of demonstrating quantum utility. Bloqade is built on top of [Kirin](https://github.com/QuEraComputing/kirin), an open source compiler infrastructure designed for kernel functions and embedded Domain-Specific Language (eDSL) creation. 16 | 17 | ## Composable quantum programming 18 | 19 | As of today, Bloqade has two objectives: digital and analog quantum computing. `bloqade-analog` is the SDK for analog-mode neutral atom computers and includes several handy utilities ranging from building and analyzing analog programs, to emulation and execution on QuEra's cloud-accessible hardware "Aquila". `bloqade` is the initial iteration to represent digital circuit execution using gate-based quantum computing on reconfigurable neutral atom architecture. It extends the QASM2 language to include extra annotation of circuits that is important for efficient execution, such as parallelism and global gates. As well as being able to construct quantum programs with the full convenience of features found in classical programming languages - such as loops, control flows and closures - `bloqade` also includes basic compiler transformation passes, emulation, and code generation. 20 | 21 | But `bloqade` is not done with just these two components. We envision adding new components (called "dialects") which help you write programs which are tuned for optimal performance in an error corrected era of neutral atom hardware. Stay tuned and help us build the future of quantum computing as we build out new components targeting QEC and atom moving! 22 | 23 | 24 | ## Hardware-oriented programming and co-design 25 | 26 | At its core, Bloqade strives to be the neutral atom SDK for getting the most out of today's and tomorrows' quantum hardware. It is clear that the circuit-level abstraction is not enough to program real quantum hardware; indeed, tomorrows' quantum demonstrations and applications must program at the hardware level and develop special tooling to compile higher-level abstractions to efficient implementations. We call this process **"co-design"**: designing algorithms specialized to near-term hardware, with an eye on nontrivial demonstrations and scalable solutions. Ultimately, this co-design approach requires hardware-specific DSLs which explicitly represent the native executions on neutral atom hardware: in other words, Bloqade. 27 | 28 | 29 | ## Hybrid computing beyond circuits 30 | 31 | Many quantum algorithms are hybrid, requiring both classical and quantum resources to work together in tandem. This could be anything from syndrome extraction and measurement-based computing to variational parameter updates in VQE methods and orbital fragmentation methods in molecular simulation. Through the use of the Kirin compiler infrastructure, Bloqade embraces this philosophy of heterogeneous compute. Kirin programs are written as (compositions of) [kernels](https://en.wikipedia.org/wiki/Compute_kernel) -- subroutines that are intended to run on particular hardware (such as QPUs), or orchestrated to run on heterogeneous compute (such as a real-time classical runtime plus a QPU). These subroutines -- plus the built-in hybrid representations-- enable many key primitives, such as error correction. 32 | 33 | Additionally, the ability to compose functions together and to use typical classical programming structures like `if` and recursions enables many simplifications in writing complex circuits. In fact, recursions and the ability to dynamically allocate new memory (which is not known until runtime) enables many powerful subroutines and is natively enabled with Bloqade's kernel-based representation; for example, see [this implementation](../../../digital/examples/repeat_until_success.py) of a repeat-until-success program. 34 | 35 | ## Analog, digital, logical: towards real quantum utility 36 | 37 | The first step in Bloqade's journey was building out the analog mode SDK, designed to interface with QuEra’s cloud-accessible analog-mode neutral-atom quantum computer Aquila, as well as enable analysis and scientific discovery in analog quantum computing. But the journey should not stop there: real quantum utility is error corrected and requires robust algorithmic exploration and design of quantum primitives, in-depth analysis of near-term hardware performance and benchmarking, and building pipelines and hybrid architectures that are intended not just for today’s demonstrations but also for tomorrow’s utility-scale hardware. By introducing the next generation of Bloqade, we hope to enable this exploration by adding in support for near-term digital and intermediate-term logical representations of hybrid quantum computations. 38 | 39 | ## Learn more 40 | 41 | Bloqade is an open-source project and can be freely downloaded and modified; you can learn how to do so [here](../../../install.md). If you want to see how to write programs with of the new `bloqade` package, check out our examples [here](../../../digital/index.md). If you would like to learn more about QuEra Computing Inc., check out our [webpage](https://quera.com) as well as discover our many [academic publications and demonstrations](https://www.quera.com/news#publications). 42 | -------------------------------------------------------------------------------- /docs/contrib.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please see [Installation](install.md) for instructions on how to set up your development environment. 4 | 5 | ## Pre-commit hooks 6 | 7 | We use `pre-commit` to run the linter checks before you commit your changes. The pre-commit hooks are installed as part of the development dependencies. You can setup `pre-commit` using the following command: 8 | 9 | ```bash 10 | pre-commit install 11 | ``` 12 | 13 | This will run the linter checks before you commit your changes. If the checks fail, the commit will be 14 | rejected. Most of the following sections can be checked by the pre-commit hooks. 15 | 16 | ## Running the tests 17 | 18 | We use `pytest` for testing. To run the tests, simply run: 19 | 20 | ```bash 21 | pytest 22 | ``` 23 | 24 | or for a specific test file with the `-s` flag to show the output of the program: 25 | 26 | ```bash 27 | pytest -s tests/test_program.py 28 | ``` 29 | 30 | lots of tests contains pretty printing of the IR themselves, so it's useful to see the output. 31 | 32 | ## Code style 33 | 34 | We use `black` for code formatting. Besides the linter requirements, we also require the following 35 | good-to-have practices: 36 | 37 | ### Naming 38 | 39 | - try not to use abbreviation as names, unless it's a common abbreviation like `idx` for `index` 40 | - try not create a lot of duplicated name prefix unless the extra information is necessary when accessing the class object. 41 | - try to use `snake_case` for naming variables and functions, and `CamelCase` for classes. 42 | 43 | ### Comments 44 | 45 | - try not to write comments, unless it's really necessary. The code should be self-explanatory. 46 | - if you have to write comments, try to use `NOTE:`, `TODO:` `FIXME:` tags to make it easier to search for them. 47 | 48 | ## Documentation 49 | 50 | We use `just` for mangaging command line tools and scripts. It should be installed when you run `uv sync`. To build the documentation, simply run: 51 | 52 | ```bash 53 | just doc 54 | ``` 55 | 56 | This will launch a local server to preview the documentation. You can also run `just doc-build` to build the documentation without launching the server. 57 | 58 | ## License 59 | 60 | By contributing to this project, you agree to license your contributions under the Apache License 2.0 with LLVM Exceptions. 61 | -------------------------------------------------------------------------------- /docs/digital/dialects_and_kernels.md: -------------------------------------------------------------------------------- 1 | # Dialects and kernels 2 | 3 | !!! info 4 | A **kernel** function is a piece of code that runs on specialized hardware such as a quantum computer. 5 | 6 | A **dialect** is a domain-specific language (DSL) with which you can write such a kernel. 7 | Each dialect comes with a specific set of statements and instructions you can use in order to write your program. 8 | 9 | Bloqade provides a set of pre-defined dialects, with which you can write your programs and circuits. 10 | 11 | Once you have your kernel, you can inspect their intermediate representation (IR), apply different optimizations using [compiler passes](../quick_start/circuits/compiler_passes/index.md), or run them on a [(simulator) device](./simulator_device/simulator_device.md). 12 | 13 | 14 | # Available dialects 15 | 16 | Here's a quick overview of the most important available dialects. 17 | 18 | ## qasm2 19 | 20 | There are a number of dialects with which you can write kernels that represent QASM2 programs. 21 | See also the [qasm2 API reference](../reference/qasm2.md) 22 | 23 | ### qasm2.main 24 | 25 | This dialect allows you to write native QASM2 programs. 26 | As such, it includes definitions gates, measurements and quantum and classical registers, which are part of the QASM2 specification. 27 | For details on the language, see the [specification](https://arxiv.org/abs/1707.03429). 28 | 29 | Here's an example kernel 30 | 31 | ```python 32 | from bloqade import qasm2 33 | 34 | @qasm2.main 35 | def main(): 36 | q = qasm2.qreg(2) 37 | qasm2.h(q[0]) 38 | qasm2.cx(q[0], q[1]) 39 | 40 | c = qasm2.creg(2) 41 | qasm2.measure(q, c) 42 | return c 43 | ``` 44 | 45 | Here's how you can look at the QASM2 program this kernel represents: 46 | 47 | ```python 48 | from bloqade.qasm2.emit import QASM2 49 | from bloqade.qasm2.parse import pprint 50 | 51 | 52 | target = QASM2() 53 | qasm2_program = target.emit(main) 54 | pprint(qasm2_program) 55 | ``` 56 | 57 | ### qasm2.extended 58 | 59 | This dialect can also be used to write QASM2 programs. 60 | However, it adds a couple of statements that makes it easier to write programs. 61 | For example, QASM2 does not support for-loops. 62 | With `qasm2.extended`, however, you can use for-loops and can let the compiler worry about unrolling these loops such that valid QASM2 code is produced. 63 | 64 | ```python 65 | from bloqade import qasm2 66 | 67 | @qasm2.extended 68 | def main(): 69 | n = 2 70 | q = qasm2.qreg(n) 71 | 72 | for i in range(n): 73 | qasm2.h(q[i]) 74 | 75 | qasm2.cx(q[0], q[1]) 76 | c = qasm2.creg(n) 77 | qasm2.measure(q, c) 78 | return c 79 | ``` 80 | 81 | If you run this through the code emission as shown above, you'll see that the for-loop gets unrolled into separate hadamard gate applications for each qubit. 82 | At the same time, if you try to define this kernel using the `qasm2.main` dialect only, you will receive a `BuildError` telling you to take that crazy for-loop out of there as it's not supported. 83 | 84 | 85 | ## noise.native 86 | 87 | Using this dialect, you can represent different noise processes in your kernel. 88 | As of now, there are essentially two different noise channels: 89 | 90 | * A pauli noise channel, which can represent different types of decoherence. 91 | * An atomic loss channel, which can be used to model effects of losing a qubit during the execution of a program. 92 | 93 | Usually, you don't want to write noise statements directly. 94 | Instead, use a [NoisePass][bloqade.qasm2.passes.NoisePass] in order to inject noise statements automatically according to a specific noise model. 95 | 96 | !!! note 97 | Right now, only the `qasm2.extended` dialect fully support noise. 98 | 99 | For example, you may want to do something like this: 100 | 101 | ```python 102 | from bloqade import qasm2 103 | from bloqade.qasm2.passes import NoisePass 104 | 105 | @qasm2.extended 106 | def main(): 107 | n = 2 108 | q = qasm2.qreg(n) 109 | 110 | for i in range(n): 111 | qasm2.h(q[i]) 112 | 113 | qasm2.cx(q[0], q[1]) 114 | c = qasm2.creg(n) 115 | qasm2.measure(q, c) 116 | return c 117 | 118 | # Define the noise pass you want to use 119 | noise_pass = NoisePass(main.dialects) # just use the default noise model for now 120 | 121 | # Inject the noise - note that the main method will be updated in-place 122 | noise_pass(main) 123 | 124 | # Look at the IR and all the glorious noise in there 125 | main.print() 126 | ``` 127 | 128 | 129 | ## squin 130 | 131 | This dialect is, in a sense, more expressive than the qasm2 dialects: it allows you to specify operators rather than just gate applications. 132 | That can be useful if you're trying to e.g. simulate a Hamiltonian time evolution. 133 | 134 | !!! warning 135 | The squin dialect is in an early stage of development. 136 | Expect substantial changes to it in the near future. 137 | 138 | Here's a short example: 139 | 140 | ```python 141 | from bloqade import squin 142 | 143 | @squin.kernel 144 | def main(): 145 | q = squin.qubit.new(2) 146 | h = squin.op.h() 147 | 148 | # apply a hadamard to only the first qubit 149 | h1 = squin.op.kron(h, squin.op.identity(sites=1)) 150 | 151 | squin.qubit.apply(h1, q) 152 | 153 | cx = squin.op.cx() 154 | squin.qubit.apply(cx, q) 155 | 156 | return squin.qubit.measure(q) 157 | 158 | # have a look at the IR 159 | main.print() 160 | ``` 161 | 162 | See also the [squin API reference](../reference/squin.md) 163 | 164 | ## stim 165 | 166 | !!! warning 167 | Sorry folks, still under construction. 168 | 169 | See also the [stim API reference](../reference/stim.md) 170 | -------------------------------------------------------------------------------- /docs/digital/examples/deutsch_squin.py: -------------------------------------------------------------------------------- 1 | # %% [markdown] 2 | # # Deutsch-Jozsa Algorithm 3 | # In this example, we will implement a version of the [Deutsch-Josza algorithm](https://en.wikipedia.org/wiki/Deutsch–Jozsa_algorithm) using bloqade's squin dialect. 4 | # %% [markdown] 5 | 6 | # We start by loading in some stuff and defining some parameters. 7 | 8 | # %% 9 | import random 10 | from typing import Any 11 | 12 | from bloqade.types import Qubit 13 | from kirin.dialects import ilist 14 | from bloqade.pyqrack import StackMemorySimulator 15 | 16 | from bloqade import squin 17 | 18 | n_bits = 2 19 | 20 | # %% [markdown] 21 | # 22 | # Now, before we can implement the actual algorithm, we need to define the oracles, i.e. the functions we want to check for. 23 | # 24 | # The problem is defined as follows: 25 | # Given a bit string of length $n$, $x \in \{0, 1\}^\otimes n$, we have a function that is either constant or balanced. 26 | # 27 | # A constant function is defined as $f_\text{const}(x) = c \forall x$, where $c \in \{0, 1\}$ is some constant value. 28 | # 29 | # A balanced function, on the other hand, is defined by 30 | # 31 | # $f_\text{balanced}(x) = \begin{cases} 0 \, \forall x \in S(x), \\ 1 \text{ else,} \end{cases}$ 32 | # 33 | # where $S(x)$ is an arbitrarily chosen half of all possible bit strings, i.e. $|S(x)| = 2^{n-1}$. 34 | 35 | 36 | # %% [markdown] 37 | # For our example, we will be using $n + 1$ qubits, where $n$ store the bitstring $x$ and the result is stored in the last qubit. 38 | # We'll be writing our oracle functions as squin kernels, which we can then later use in the actual algorithm implementation. 39 | # 40 | # In order to define our oracle functions, we can simply choose for the constant function to always return $1$, which we achieve by flipping the final qubit using an $X$ gate. 41 | # %% 42 | @squin.kernel 43 | def f_constant(q: ilist.IList[Qubit, Any]): 44 | x = squin.op.x() 45 | 46 | # flip the final (result) qubit -- every bit string is mapped to 1 47 | squin.qubit.apply(x, [q[-1]]) 48 | 49 | 50 | # %% [markdown] 51 | 52 | # For the balanced oracle we use the following approach: we use the first qubit as control in a $CX$ gate, which is applied to the resulting qubit. 53 | # This means that the result will be $1$ in exactly half the cases. 54 | 55 | 56 | # %% 57 | @squin.kernel 58 | def f_balanced(q: ilist.IList[Qubit, Any]): 59 | x = squin.op.x() 60 | cn_x = squin.op.control(x, n_controls=1) 61 | squin.qubit.apply(cn_x, [q[0], q[-1]]) 62 | 63 | 64 | # %% [markdown] 65 | # 66 | # Now, we define the actual algorithm as a kernel, which simply takes one of the other kernels as input. 67 | # In the end, we can infer which function was provided by looking at the resulting measurement of the result qubit. 68 | # %% 69 | @squin.kernel 70 | def deutsch_algorithm(f): 71 | q = squin.qubit.new(n_qubits=n_bits + 1) 72 | 73 | x = squin.op.x() 74 | squin.qubit.apply(x, [q[-1]]) 75 | 76 | h = squin.op.h() 77 | for i in range(len(q)): 78 | squin.qubit.apply(h, [q[i]]) 79 | 80 | # apply the oracle function 81 | f(q) 82 | 83 | for i in range(n_bits): 84 | squin.qubit.apply(h, [q[i]]) 85 | 86 | return squin.qubit.measure(q[:-1]) 87 | 88 | 89 | # %% [markdown] 90 | # Finally, we actually run the result. 91 | # To do so, we use the `PyQrack` simulation backend in bloqade. 92 | # 93 | # To make things a bit more interesting, we randomly select which function we are running the algorithm with. 94 | 95 | # %% 96 | sim = StackMemorySimulator(min_qubits=n_bits + 1) 97 | 98 | f_choice_idx = random.randint(0, 1) 99 | f_choice = (f_constant, f_balanced)[f_choice_idx] 100 | 101 | # result = sim.run(deutsch_algorithm, args=(f_balanced, n)) 102 | result0 = 0.0 103 | n_shots = 100 104 | for _ in range(n_shots): 105 | res = sim.run(deutsch_algorithm, args=(f_choice,)) 106 | result0 += res[0] / n_shots 107 | 108 | print( 109 | "Oh magic Deutsch-Jozsa algorithm, tell us if our function is constant or balanced:" 110 | ) 111 | print("*drumroll*") 112 | if result0 == 0: 113 | print("It's constant!") 114 | 115 | # let's make sure we actually did the right thing here 116 | assert f_choice_idx == 0 117 | else: 118 | print("It's balanced!") 119 | 120 | # let's make sure we actually did the right thing here 121 | assert f_choice_idx == 1 122 | -------------------------------------------------------------------------------- /docs/digital/examples/ghz.py: -------------------------------------------------------------------------------- 1 | # %% [markdown] 2 | # # GHZ State Preparation with Parallelism 3 | # In this example, we will implement a *Greenberger-Horne-Zeilinger* (GHZ) state preparation circuit with $N = 2^n$ qubits. 4 | # 5 | # First, we will present the standard linear-depth construction in Bloqade but later we will present a log-depth 6 | # construction that achieves the same result. We then take this one step further and use the fact that Bloqade 7 | # (and QuEra's neutral atom hardware!) support *parallel* gates, allowing for the application of the same gate 8 | # across multiple qubits simultaneously. Combined with the fact that atom *shuttling* allows for arbitrary 9 | # connectivity, we can also decrease the circuit *execution* depth from $N$ to just $n$. 10 | 11 | # %% 12 | import math 13 | 14 | from bloqade import qasm2 15 | from kirin.dialects import ilist 16 | 17 | # %% [markdown] 18 | # ## Simple Linear Depth Implementation of a GHZ State Preparation Circuit 19 | # 20 | # A simple GHZ state preparation circuit can be built with $N - 1$ CX gates and $1$ H gate. 21 | # This gives the circuit an execution depth of $N$. 22 | 23 | #
24 | # 25 | # 26 | # 27 | #
28 | 29 | 30 | # %% 31 | def ghz_linear(n: int): 32 | n_qubits = int(2**n) 33 | 34 | @qasm2.extended 35 | def ghz_linear_program(): 36 | 37 | qreg = qasm2.qreg(n_qubits) 38 | # Apply a Hadamard on the first qubit 39 | qasm2.h(qreg[0]) 40 | # Create a cascading sequence of CX gates 41 | # necessary for quantum computers that 42 | # only have nearest-neighbor connectivity between qubits 43 | for i in range(1, n_qubits): 44 | qasm2.cx(qreg[i - 1], qreg[i]) 45 | 46 | return ghz_linear_program 47 | 48 | 49 | # %% [markdown] 50 | # ## Log-depth Implementation of a GHZ State Preparation Circuit 51 | # 52 | # Let's take a look how we can rewrite the circuit to take advantage of QuEra's hardware capabilities. 53 | # We can achieve log(N) circuit depth by [rearranging the CX gates (see *Mooney, White, Hill, Hollenberg* - 2021)](https://arxiv.org/abs/2101.08946). 54 | # 55 | 56 | # %% [markdown] 57 | #
58 | #

Circuit vs. Execution Depth

59 | #

60 | # Before going any further, it's worth distinguishing between the concept of circuit depth and circuit execution depth. 61 | # For example, in the following implementation, each CX gate instruction inside the for-loop is executed in sequence. 62 | # So even thought the circuit depth is $log(N) = n$, the circuit execution depth is still $N$. 63 | #

64 | #
65 | 66 | #
67 | # 68 | # 69 | # 70 | #
71 | 72 | 73 | # %% 74 | def ghz_log_depth(n: int): 75 | n_qubits = int(2**n) 76 | 77 | @qasm2.extended 78 | def layer_of_cx(i_layer: int, qreg: qasm2.QReg): 79 | step = n_qubits // (2**i_layer) 80 | for j in range(0, n_qubits, step): 81 | qasm2.cx(ctrl=qreg[j], qarg=qreg[j + step // 2]) 82 | 83 | @qasm2.extended 84 | def ghz_log_depth_program(): 85 | 86 | qreg = qasm2.qreg(n_qubits) 87 | 88 | qasm2.h(qreg[0]) 89 | for i in range(n): 90 | layer_of_cx(i_layer=i, qreg=qreg) 91 | 92 | return ghz_log_depth_program 93 | 94 | 95 | # %% [markdown] 96 | # ## Our Native Gate Set and Parallelism 97 | # By nature, our digital quantum computer can execute native gates in parallel in an single instruction/ execution cycle. 98 | # The concept is very similar to the SIMD (Single Instruction, Multiple Data) in classical computing. 99 | # 100 | # On our hardware, there are two important factors to be considered: 101 | # 1. the native gate set allows for arbitrary (parallel) rotations and (parallel) CZ gates. 102 | # 2. Our atom shuttling architecture allows arbitrary qubit connectivity. This means that our parallel instruction is not limited to fixed connectivity (for example nearest neighbor connectivity). 103 | # 104 | # With this in mind, we can rewrite the `layer` subroutine to now use the `qasm2.parallel` dialect in Bloqade. 105 | # We know that the CX gate can be decomposed into a CZ gate with two single-qubit gates $R_y(-\pi/2)$ and $R_y(\pi/2)$ acting on the target qubits. 106 | # With this decomposition in mind, we can now using our parallel gate instructions `parallel.u` and `parallel.cz`. 107 | # With the following modification, we can further reduce the circuit execution depth to just $n$ (log of the total number of qubits $N$) 108 | 109 | 110 | # %% 111 | def ghz_log_simd(n: int): 112 | n_qubits = int(2**n) 113 | 114 | @qasm2.extended 115 | def layer(i_layer: int, qreg: qasm2.QReg): 116 | step = n_qubits // (2**i_layer) 117 | 118 | def get_qubit(x: int): 119 | return qreg[x] 120 | 121 | ctrl_qubits = ilist.Map(fn=get_qubit, collection=range(0, n_qubits, step)) 122 | targ_qubits = ilist.Map( 123 | fn=get_qubit, collection=range(step // 2, n_qubits, step) 124 | ) 125 | 126 | # Ry(-pi/2) 127 | qasm2.parallel.u(qargs=targ_qubits, theta=-math.pi / 2, phi=0.0, lam=0.0) 128 | 129 | # CZ gates 130 | qasm2.parallel.cz(ctrls=ctrl_qubits, qargs=targ_qubits) 131 | 132 | # Ry(pi/2) 133 | qasm2.parallel.u(qargs=targ_qubits, theta=math.pi / 2, phi=0.0, lam=0.0) 134 | 135 | @qasm2.extended 136 | def ghz_log_depth_program(): 137 | 138 | qreg = qasm2.qreg(n_qubits) 139 | 140 | qasm2.h(qreg[0]) 141 | for i in range(n): 142 | layer(i_layer=i, qreg=qreg) 143 | 144 | return ghz_log_depth_program 145 | 146 | 147 | # %% [markdown] 148 | #
149 | #

Using Closures to Capture Global Variables

150 | #

151 | # While bloqade.qasm2 permits a main program with arguments, standard QASM2 does not. 152 | # To get around this, we need to put the program in a closure. 153 | # Our Kirin compiler toolchain can capture the global variable inside the closure. 154 | # In this case, the n_qubits will be captured upon calling the ghz_log_simd(n_qubits) python function. 155 | # As a result, the returned QASM2 program will not have any arguments. 156 | #

157 | #
158 | 159 | # %% 160 | target = qasm2.emit.QASM2( 161 | allow_parallel=True, 162 | ) 163 | ast = target.emit(ghz_log_simd(4)) 164 | qasm2.parse.pprint(ast) 165 | -------------------------------------------------------------------------------- /docs/digital/examples/ghz_linear_circuit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /docs/digital/examples/pauli_exponentiation.py: -------------------------------------------------------------------------------- 1 | # %% [markdown] 2 | # # Pauli Exponentiation for Quantum Simulation 3 | # In this example, we will consider a simple Pauli Exponentiation circuit. 4 | 5 | # %% 6 | import math 7 | 8 | from bloqade import qasm2 9 | 10 | # %% [markdown] 11 | # First, we define the `zzzz_gadget` function which is a simple implementation of Pauli Z exponentiation 12 | # with a parameterized angle `gamma`. 13 | 14 | 15 | # %% 16 | @qasm2.extended 17 | def zzzz_gadget(targets: tuple[qasm2.Qubit, ...], gamma: float): 18 | for i in range(len(targets) - 1): 19 | qasm2.cx(targets[i], targets[i + 1]) 20 | 21 | qasm2.rz(targets[-1], gamma) 22 | 23 | for j in range(len(targets) - 1): 24 | qasm2.cx(targets[-j - 1], targets[-j - 2]) 25 | 26 | 27 | # %% [markdown] 28 | # Next, we define the `pauli_basis_change` function which is a simple implementation of Pauli basis change 29 | # with a parameterized start and end Pauli basis. 30 | 31 | 32 | # %% 33 | @qasm2.extended 34 | def pauli_basis_change(targets: tuple[qasm2.Qubit, ...], start: str, end: str): 35 | # assert len(targets) == len(start) 36 | # assert len(targets) == len(end) 37 | 38 | # for qubit, start_pauli, end_pauli in zip(targets, start, end): 39 | for i in range(len(targets)): 40 | qubit = targets[i] 41 | start_pauli = start[i] 42 | end_pauli = end[i] 43 | 44 | target = start_pauli + end_pauli 45 | if target == "ZX": 46 | qasm2.ry(qubit, math.pi / 2) 47 | elif target == "ZY": 48 | qasm2.rx(qubit, -math.pi / 2) 49 | # elif target == "ZZ": 50 | # pass 51 | # elif target == "XX": 52 | # pass 53 | elif target == "XY": 54 | qasm2.rz(qubit, math.pi / 2) 55 | elif target == "XZ": 56 | qasm2.ry(qubit, -math.pi / 2) 57 | elif target == "YX": 58 | qasm2.rz(qubit, -math.pi / 2) 59 | # elif target == "YY": 60 | # pass 61 | elif target == "YZ": 62 | qasm2.rx(qubit, math.pi / 2) 63 | 64 | 65 | # %% [markdown] 66 | # Putting it all together, we define the `pauli_exponential` function which is a simple implementation of Pauli Exponentiation 67 | # with a parameterized Pauli basis and angle `gamma`. 68 | # %% 69 | @qasm2.extended 70 | def pauli_exponential(targets: tuple[qasm2.Qubit, ...], pauli: str, gamma: float): 71 | # assert len(targets) == len(pauli) 72 | 73 | pauli_basis_change(targets=targets, start="Z" * len(targets), end=pauli) 74 | zzzz_gadget(targets=targets, gamma=gamma) 75 | pauli_basis_change(targets=targets, start=pauli, end="Z" * len(targets)) 76 | 77 | 78 | # %% [markdown] 79 | # Finally, we define the `main` function as the entry point of the program. 80 | 81 | #
82 | # 83 | # 84 | # 85 | #
86 | 87 | 88 | # %% 89 | @qasm2.extended 90 | def main(): 91 | register = qasm2.qreg(4) 92 | pauli_exponential((register[0], register[1], register[2]), "ZXY", 0.5) 93 | 94 | 95 | # %% [markdown] 96 | # we can now ask the compiler to emit the QASM2 code for the `main` function. 97 | # %% 98 | target = qasm2.emit.QASM2() 99 | ast = target.emit(main) 100 | qasm2.parse.pprint(ast) 101 | -------------------------------------------------------------------------------- /docs/digital/examples/qaoa.py: -------------------------------------------------------------------------------- 1 | # %% [markdown] 2 | # Lets do a simple example of a prototype circuit that benefits from parallelism: QAOA 3 | # solving the MaxCut problem. For more details, see [arXiv:1411.4028](https://arxiv.org/abs/1411.4028) 4 | # and the considerable literature that has developed around this algorithm. 5 | 6 | # %% 7 | import math 8 | from typing import Any 9 | 10 | import kirin 11 | import networkx as nx 12 | from kirin.dialects import ilist 13 | 14 | from bloqade import qasm2 15 | 16 | pi = math.pi 17 | 18 | # %% [markdown] 19 | # MaxCut is a combinatorial graph problem that seeks to bi-partition the nodes of some 20 | # graph G such that the number of edges between the two partitions is maximized. 21 | # Here, we choose a random 3 regular graph with 32 nodes [ref](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.103.042612) 22 | 23 | # %% 24 | N = 32 25 | G = nx.random_regular_graph(3, N, seed=42) 26 | 27 | 28 | # %% [markdown] 29 | # To build the quantum program, we use a builder function and use closure to pass variables 30 | # inside of the kernel function (kirin methods). 31 | # In this case, the two variables that are passed inside are the edges and nodes of the graph. 32 | # 33 | # The QAOA first prepares the |+> state as a superposition of all possible bitstrings, 34 | # then repeats between the (diagonal) cost function and the mixer X with angles gamma and beta. 35 | # It is parameterized by gamma and betas, which are each the p length lists of angles. 36 | # 37 | # Lets first implement the sequential version of the QAOA algorithm, which 38 | # does not inform any parallelism to the compiler. 39 | 40 | 41 | # %% 42 | def qaoa_sequential(G: nx.Graph) -> kirin.ir.Method: 43 | 44 | edges = list(G.edges) 45 | nodes = list(G.nodes) 46 | N = len(nodes) 47 | 48 | @qasm2.extended 49 | def kernel(gamma: ilist.IList[float, Any], beta: ilist.IList[float, Any]): 50 | # Initialize the register in the |+> state 51 | q = qasm2.qreg(N) 52 | for i in range(N): # structural control flow is native to the Kirin compiler 53 | qasm2.h(q[i]) 54 | 55 | # Repeat the cost and mixer layers 56 | for i in range(len(gamma)): 57 | # The cost layer, which corresponds to a ZZ(phase) gate applied 58 | # to each edge of the graph 59 | for j in range(len(edges)): 60 | edge = edges[j] 61 | qasm2.cx(q[edge[0]], q[edge[1]]) 62 | qasm2.rz(q[edge[1]], gamma[i]) 63 | qasm2.cx(q[edge[0]], q[edge[1]]) 64 | # The mixer layer, which corresponds to a X(phase) gate applied 65 | # to each node of the graph 66 | for j in range(N): 67 | qasm2.rx(q[j], beta[i]) 68 | 69 | return q 70 | 71 | return kernel 72 | 73 | 74 | # %% [markdown] 75 | # Next, lets implement a SIMD (Single Instruction, Multiple Data) version of the QAOA algorithm, 76 | # which effectively represents the parallelism in the QAOA algorithm. 77 | # This can be done by coloring the (commuting) ZZ(phase) gates into groups with non-overlapping 78 | # sets of qubits, and then applying each of those groups in parallel. 79 | # By [Vizing's theorem](https://en.wikipedia.org/wiki/Vizing%27s_theorem) the edges of a graph 80 | # can efficiently be colored into $\Delta+1$ colors, where $\Delta$ is the maximum degree of the graph. 81 | # Unfortunately, networkx does not have a native implementation of the algorithm so instead we use 82 | # the lesser [Brooks' theorem]https://en.wikipedia.org/wiki/Brooks%27_theorem) to color the edges 83 | # using an equitable coloring of the line graph. 84 | 85 | 86 | # %% 87 | def qaoa_simd(G: nx.Graph) -> kirin.ir.Method: 88 | 89 | nodes = list(G.nodes) 90 | 91 | # Note that graph computation is happening /outside/ the kernel function: 92 | # this is a computation that occurs on your laptop in Python when you generate 93 | # a program, as opposed to on a piece of quantum hardware, which is what 94 | # occurs inside of the kernel. 95 | Gline = nx.line_graph(G) 96 | colors = nx.algorithms.coloring.equitable_color(Gline, num_colors=5) 97 | left_ids = ilist.IList( 98 | [ 99 | ilist.IList([edge[0] for edge in G.edges if colors[edge] == i]) 100 | for i in range(5) 101 | ] 102 | ) 103 | right_ids = ilist.IList( 104 | [ 105 | ilist.IList([edge[1] for edge in G.edges if colors[edge] == i]) 106 | for i in range(5) 107 | ] 108 | ) 109 | # We can use composition of kernel functions to simplify repeated code. 110 | # Small snippets (say, the CX gate) can be written once and then called 111 | # many times. 112 | 113 | @qasm2.extended 114 | def parallel_h(qargs: ilist.IList[qasm2.Qubit, Any]): 115 | qasm2.parallel.u(qargs=qargs, theta=pi / 2, phi=0.0, lam=pi) 116 | 117 | # A parallel CX gate is equivalently a parallel H gate, followed by a parallel CZ gate, 118 | # followed by another parallel H. the CZ can be done in any order as they permute. 119 | @qasm2.extended 120 | def parallel_cx( 121 | ctrls: ilist.IList[qasm2.Qubit, Any], qargs: ilist.IList[qasm2.Qubit, Any] 122 | ): 123 | parallel_h(qargs) 124 | qasm2.parallel.cz(ctrls, qargs) 125 | parallel_h(qargs) 126 | 127 | @qasm2.extended 128 | def parallel_cz_phase( 129 | ctrls: ilist.IList[qasm2.Qubit, Any], 130 | qargs: ilist.IList[qasm2.Qubit, Any], 131 | gamma: float, 132 | ): 133 | parallel_cx(ctrls, qargs) 134 | qasm2.parallel.rz(qargs, gamma) 135 | parallel_cx(ctrls, qargs) 136 | 137 | @qasm2.extended 138 | def kernel(gamma: ilist.IList[float, Any], beta: ilist.IList[float, Any]): 139 | # Declare the register and set it to the |+> state 140 | q = qasm2.qreg(len(nodes)) 141 | # qasm2.glob.u(theta=pi / 2, phi=0.0, lam=pi,registers=[q]) 142 | 143 | def get_qubit(x: int): 144 | return q[x] 145 | 146 | all_qubits = ilist.map(fn=get_qubit, collection=range(N)) 147 | 148 | parallel_h(all_qubits) 149 | 150 | for i in range(len(gamma)): # For each QAOA layer... 151 | # Do the ZZ phase gates... 152 | for cind in range( 153 | 5 154 | ): # by applying a parallel CZ phase gate in parallel for each color, 155 | ctrls = ilist.map(fn=get_qubit, collection=left_ids[cind]) 156 | qargs = ilist.map(fn=get_qubit, collection=right_ids[cind]) 157 | parallel_cz_phase(ctrls, qargs, gamma[i]) 158 | # ...then, do an X phase gate. Observe that because this happens on every 159 | # qubit, we can do a global rotation, which is higher fidelity than 160 | # parallel local rotations. 161 | # qasm2.glob.u(theta=beta[i],phi=0.0,lam=0.0,registers=[q]) 162 | qasm2.parallel.u(qargs=all_qubits, theta=beta[i], phi=0.0, lam=0.0) 163 | 164 | return q 165 | 166 | return kernel 167 | 168 | 169 | # %% 170 | print("--- Sequential ---") 171 | qaoa_sequential(G).code.print() 172 | 173 | # %% 174 | kernel = qaoa_simd(G) 175 | 176 | print("\n\n--- Simd ---") 177 | kernel.print() 178 | 179 | 180 | # %% 181 | @qasm2.extended 182 | def main(): 183 | kernel([0.1, 0.2], [0.3, 0.4]) 184 | 185 | 186 | # %% 187 | target = qasm2.emit.QASM2() 188 | ast = target.emit(main) 189 | qasm2.parse.pprint(ast) 190 | -------------------------------------------------------------------------------- /docs/digital/examples/qft.py: -------------------------------------------------------------------------------- 1 | # %% [markdown] 2 | # # Quantum Fourier Transform 3 | # In this example, we will explore the Quantum Fourier Transform (QFT) circuit using 4 | # recursion and iteration -- a convenient way to implement the QFT circuit using 5 | # our high-level programming features. 6 | # 7 | # To begin, we will import the `qasm2` module from the `bloqade` package and the `PyQrack` 8 | # backend from the `bloqade.pyqrack` module, which can be installed via 9 | # 10 | # ```bash 11 | # pip install bloqade-pyqrack[backend] 12 | # ``` 13 | # with the `backend` being one of ` pyqrack`, `pyqrack-cpu`, `pyqrack-cuda` depending on 14 | # the hardware and OS you have. see [README](https://github.com/QuEraComputing/bloqade-pyqrack?tab=readme-ov-file#which-extra-do-i-install) 15 | # for mote details. 16 | # %% 17 | import math 18 | 19 | from bloqade import qasm2 20 | from bloqade.pyqrack import PyQrack 21 | 22 | # %% [markdown] 23 | # In the following, we will define the Quantum Fourier Transform (QFT) circuit using recursion 24 | # inside a kernel function `qft`. The `qft` function takes two arguments: a quantum register `qreg` 25 | # and an integer `n` representing the number of qubits we want to apply the QFT circuit to. 26 | # %% 27 | pi = math.pi 28 | 29 | 30 | @qasm2.extended 31 | def qft(qreg: qasm2.QReg, n: int, k: int): 32 | if k == n: 33 | return qreg 34 | 35 | qasm2.h(qreg[k]) 36 | for i in range(k + 1, n): 37 | qasm2.cu1(qreg[i], qreg[k], 2 * math.pi / 2**i) 38 | qft(qreg, n, k + 1) # recursion 39 | return qreg 40 | 41 | 42 | # %% [markdown] 43 | # Next, we will call this kernel function `qft` inside a `main` function to check if 44 | # the QFT circuit is correctly implemented. We will use a quantum register of size 3. 45 | 46 | 47 | # %% 48 | @qasm2.extended 49 | def main(): 50 | return qft(qasm2.qreg(3), 3, 0) 51 | 52 | 53 | # %% [markdown] 54 | # Finally, we will run the `main` function on the `PyQrack` backend and print the quantum register 55 | # to see the final state of the qubits after applying the QFT circuit. 56 | #
57 | # 58 | # 59 | # 60 | #
61 | 62 | 63 | # %% 64 | device = PyQrack() 65 | qreg = device.run(main) 66 | print(qreg) 67 | 68 | # %% [markdown] 69 | # we can also emit the QASM2 code for the `main` function and print it to see the QASM2 code 70 | # that corresponds to the QFT circuit. 71 | 72 | # %% 73 | from bloqade.qasm2.emit import QASM2 # noqa: E402 74 | from bloqade.qasm2.parse import pprint # noqa: E402 75 | 76 | target = QASM2() 77 | ast = target.emit(main) 78 | pprint(ast) 79 | -------------------------------------------------------------------------------- /docs/digital/examples/qft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /docs/digital/examples/repeat_until_success.py: -------------------------------------------------------------------------------- 1 | # %% [markdown] 2 | # # Repeat Until Success with STAR Gadget 3 | # In this example, we will demonstrate a near-term fault tolerant gadget 4 | # which is a repeat-until-success protocol to implement a Z phase gate 5 | # using a resource state (similar to a T state), Pauli gates, and feed-forward measurement. 6 | # 7 | # For more information, see https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.010337, 8 | # especially Fig. 7. 9 | 10 | # %% 11 | from bloqade import qasm2 12 | 13 | # %% [markdown] 14 | # This example highlights a few interesting capabilities of having a full kernel structure with 15 | # runtime control flow. One example is the ability to dynamically allocate qubits, possibly 16 | # based on previous run-time measurement outcomes. 17 | # 18 | # In this case, we prepare a resource state, which is a generalization of the T state with 19 | # an arbitrary Z rotation $|0\rangle + e^{i\theta}|1\rangle$. 20 | 21 | 22 | # %% 23 | @qasm2.extended 24 | def prep_resource_state(theta: float): 25 | qreg = qasm2.qreg(1) 26 | qubit = qreg[0] 27 | qasm2.h(qubit) 28 | qasm2.rz(qubit, theta) 29 | return qubit 30 | 31 | 32 | # %% [markdown] 33 | # Using this resource state, we can teleport the Z phase gate to a target qubit using 34 | # only Clifford gates, which are much easier to implement fault-tolerantly. 35 | # This is implemented by first applying a CNOT gate controlled by the resource state 36 | # on the target qubit, then measuring the target qubit in the computational basis. 37 | # If the measurement outcome is 1 (which occurs with 50% probability), the gadget 38 | # executed a Z(theta) gate on the target qubit and teleported it 39 | # to the new resource state. 40 | # 41 | # However, if the measurement outcome is 0 (which occurs with 50% probability), 42 | # we apply an X gate, and the gadget executed a Z(-theta) gate on the target qubit. 43 | # In order to correct this gate, we must apply a Z(+2*theta) gate on the new target state. 44 | # Of course, we can apply this Z(+2*theta) gate by applying the same gadget with twice 45 | # the angle, and repeat until we get the correct outcome. 46 | 47 | # %% [markdown] 48 | # The simplest way to implement the gadget is to simply post-select the correct measurement outcome 49 | # using an assert statement. This is straightforward, but comes with an exponential overhead in the 50 | # number of resource states: there is a 50% chance of success at each step, so there is only a 51 | # $2^{-n}$ chance of success after $n$ Z phase gates. 52 | 53 | 54 | # %% 55 | @qasm2.extended 56 | def z_phase_gate_postselect(target: qasm2.Qubit, theta: float) -> qasm2.Qubit: 57 | ancilla = prep_resource_state(theta) 58 | qasm2.cx(ancilla, target) 59 | creg = qasm2.creg(1) 60 | qasm2.measure(target, creg[0]) 61 | if creg[0] == 1: 62 | qasm2.x(ancilla) 63 | return ancilla 64 | 65 | 66 | # %% [markdown] 67 | # To (deterministically) implement the gate, we can recursively apply the gadget by correcting 68 | # the angle of the Z gate by applying Z(+2*theta). 69 | # Observe that, while it is efficient to represent this as a composition of kernels, 70 | # there is no equivalent representation as a circuit, as the number of resource qubits and 71 | # total number of gates is not known until runtime. 72 | 73 | 74 | # %% 75 | @qasm2.extended 76 | def z_phase_gate_recursive(target: qasm2.Qubit, theta: float) -> qasm2.Qubit: 77 | """ 78 | https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.010337 Fig. 7 79 | """ 80 | ancilla = prep_resource_state(theta) 81 | qasm2.cx(ancilla, target) 82 | creg = qasm2.creg(1) 83 | qasm2.measure(target, creg[0]) 84 | if creg[0] == 0: 85 | return z_phase_gate_recursive(ancilla, 2 * theta) 86 | if creg[0] == 1: 87 | qasm2.x(ancilla) 88 | return ancilla 89 | 90 | 91 | # %% [markdown] 92 | # An alternative representation uses control flow to 93 | # implement the same gate. If the number of repeats is fixed, this can be represented 94 | # as a static circuit, though it would require a large number of resource qubits and 95 | # may still fail with a small probability $2^{-attempts}$. 96 | 97 | 98 | # %% 99 | @qasm2.extended 100 | def z_phase_gate_loop(target: qasm2.Qubit, theta: float, attempts: int): 101 | """ 102 | https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.010337 Fig. 7 103 | """ 104 | creg = qasm2.creg(1) # Implicitly initialized to 0, thanks qasm... 105 | for ctr in range(attempts): 106 | ancilla = prep_resource_state(theta * (2**ctr)) 107 | if creg[0] == 0: 108 | qasm2.cx(ancilla, target) 109 | qasm2.measure(target, creg[0]) 110 | target = ancilla 111 | qasm2.x(target) 112 | 113 | 114 | # %% [markdown] 115 | # Before we analyze these circuits, we must declare a main function 116 | # which takes no inputs, as qasm2 does not support parameterized circuits or 117 | # subcircuits. 118 | 119 | # %% 120 | theta = 0.1 # Specify some Z rotation angle. Note that this is being defined 121 | 122 | # %% [markdown] 123 | # outside the main function and being used inside the function via closure. 124 | 125 | 126 | # %% 127 | @qasm2.extended 128 | def postselect_main(): 129 | target = qasm2.qreg(1) 130 | z_phase_gate_postselect(target[0], theta) 131 | 132 | 133 | @qasm2.extended 134 | def recursion_main(): 135 | target = qasm2.qreg(1) 136 | z_phase_gate_recursive(target[0], theta) 137 | 138 | 139 | @qasm2.extended 140 | def loop_main(): 141 | target = qasm2.qreg(1) 142 | z_phase_gate_loop(target[0], theta, 5) 143 | 144 | 145 | # %% [markdown] 146 | # Now lets explore running some interpreters on these circuits. 147 | # We support the quantum emulation backend PyQrack, which simulates quantum 148 | # circuits using state vectors. 149 | 150 | # %% 151 | from bloqade.pyqrack import PyQrack # noqa: E402 152 | 153 | device = PyQrack() 154 | device.run(postselect_main) 155 | -------------------------------------------------------------------------------- /docs/digital/index.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | 6 | 7 | # Digital Bloqade 8 | 9 | This page provides some information on digital circuits, their simulation and a collection of quick examples to help you get started with Digital Bloqade. 10 | Each example is a self-contained page that demonstrates a specific feature or use case. 11 | You can copy and paste the code snippets into your own project and modify them as needed. 12 | 13 | You can also find the corresponding scripts in [jupytext format](https://jupytext.readthedocs.io/en/latest/) at the [bloqade repository](https://github.com/QuEraComputing/bloqade) under `docs/digital/examples/`. 14 | -------------------------------------------------------------------------------- /docs/digital/simulator_device/simulator_device.md: -------------------------------------------------------------------------------- 1 | # Simulation devices 2 | 3 | A simulation device can run a [task](./tasks.md), such as executing a kernel. 4 | It acts just like a device that is an actual hardware, but runs everything in a local simulation. 5 | As such, it can also be used to inspect the results of your program beyond what is possible on a QPU. 6 | For example, you can return the `state_vector` of the quantum register at the end of the task execution. 7 | 8 | Here's how you can use it in order to run a simple `qasm2.extended` kernel. 9 | 10 | ```python 11 | from bloqade.pyqrack import StackMemorySimulator 12 | from bloqade import qasm2 13 | 14 | @qasm2.extended 15 | def main(): 16 | q = qasm2.qreg(2) 17 | 18 | qasm2.h(q[0]) 19 | qasm2.cx(q[0], q[1]) 20 | 21 | return q 22 | 23 | sim = StackMemorySimulator(min_qubits=2) 24 | 25 | # get the state vector -- oohh entanglement 26 | state = sim.state_vector(main) 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/digital/simulator_device/tasks.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | !!! warning 4 | Sorry folks, still under construction. 5 | 6 | For now, please check the [API reference](../../reference/task.md). 7 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Bloqade Logo 4 | Bloqade Logo 5 | 6 | 7 |
8 |

the Software Development Kit for neutral atom quantum computers

9 |
10 |
11 | 12 | Bloqade is [QuEra Computing](https://quera.com)'s software development kit (SDK) for neutral atom quantum computers. It is designed to be a hub of embedded domain-specific languages (eDSLs) for neutral atom quantum computing. Bloqade is built on top of [Kirin](https://github.com/QuEraComputing/kirin), the Kernel Intermediate Representation Infrastructure. 13 | 14 | !!! warning 15 | Bloqade is currently in the early stages of development. The APIs and features are subject to change. While we do not promise stability and backward compatibility at the moment, we will try to minimize breaking changes as much as possible. If you are concerned about the stability of the APIs, consider pin the version of Bloqade in your project. 16 | 17 | !!! info 18 | The old version (<= 0.15) of Bloqade is still available as a sub-package `bloqade-analog`. You can keep using it via `bloqade.analog` module. For example `from bloqade import start` becomes `from bloqade.analog import start`. See [Installation](install.md) for more information. 19 | 20 | ## Installation 21 | 22 | To install Bloqade, you can use the following command: 23 | 24 | ```bash 25 | pip install bloqade 26 | ``` 27 | 28 | To install the extensions or extras for Bloqade and to setup the development environment, please refer to the [installation guide](install.md). 29 | 30 | ## Getting Started 31 | 32 | To get started with Bloqade, you can refer to the following tutorials: 33 | 34 | - [Background](background.md): Background information on neutral atom quantum computing. 35 | - [Digital quick start](quick_start/circuits/index.md): A quick start guide for QASM2 and its extensions. 36 | - [Analog quick start](quick_start/analog/index.md): A quick start guide for the analog quantum computing eDSL (same as older `bloqade` versions). 37 | 38 | ## Contributing 39 | 40 | We welcome contributions to Bloqade. Please refer to the [contribution guide](contrib.md) for more information. 41 | 42 | ## License 43 | 44 | Bloqade is licensed under the Apache License 2.0. 45 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Bloqade is available in [PyPI](https://pypi.org/) and 4 | thus can be installed via [`pip`](https://pypi.org/project/pip/). 5 | Install Bloqade using the following command: 6 | 7 | ```bash 8 | pip install bloqade 9 | ``` 10 | 11 | Bloqade support python 3.10+. 12 | 13 | We strongly recommend developing your compiler project using [`uv`](https://docs.astral.sh/uv/), 14 | which is the official development environment for Bloqade. You can install `uv` using the following command: 15 | 16 | 17 | === "Linux and macOS" 18 | 19 | ```bash 20 | curl -LsSf https://astral.sh/uv/install.sh | sh 21 | ``` 22 | 23 | then 24 | 25 | ```bash 26 | uv add kirin-toolchain 27 | ``` 28 | 29 | === "Windows" 30 | 31 | ```cmd 32 | powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 33 | ``` 34 | 35 | then 36 | 37 | ```cmd 38 | uv add kirin-toolchain 39 | ``` 40 | 41 | 42 | ## Bloqade and its friends 43 | 44 | Bloqade is a Python namespace package, we officially provide several sub-packages, each of which is an eDSL for neutral atom quantum computing. The following is a list of the sub-packages in Bloqade: 45 | 46 | ### `bloqade.qasm2` 47 | 48 | QASM2 and its extensions support for neutral atom quantum computing. Available via: 49 | 50 | ```bash 51 | pip install bloqade[qasm2] 52 | ``` 53 | 54 | ### `bloqade.analog` 55 | 56 | Analog quantum computing eDSL for neutral atom quantum computing (previously `bloqade-python`). Available via: 57 | 58 | ```bash 59 | pip install bloqade-analog 60 | ``` 61 | 62 | ### `bloqade.pyqrack` 63 | 64 | Support of the PyQrack simulator as a runtime backend for QASM2 and extensions. 65 | 66 | ```bash 67 | pip install bloqade-pyqrack 68 | ``` 69 | 70 | ### `bloqade.qbraid` 71 | 72 | Support of the qBraid cloud service as a runtime backend for retrieving noise models and running circuits. 73 | 74 | ```bash 75 | pip install bloqade[qbraid] 76 | ``` 77 | 78 | ### `bloqade.stim` (Experimental) 79 | 80 | STIM and its extensions support for neutral atom quantum computing. Available via: 81 | 82 | ```bash 83 | pip install bloqade[stim] 84 | ``` 85 | 86 | ## Development 87 | 88 | If you want to contribute to Bloqade, you can clone the repository from GitHub: 89 | 90 | ```bash 91 | git clone https://github.com/QuEraComputing/bloqade.git 92 | ``` 93 | 94 | We use `uv` to manage the development environment, after you install `uv`, you can install the development dependencies using the following command: 95 | 96 | ```bash 97 | uv sync 98 | ``` 99 | 100 | Our code review requires that you pass the tests and the linting checks. We recommend 101 | you to install `pre-commit` to run the checks before you commit your changes, the command line 102 | tool `pre-commit` has been installed as part of the development dependencies. You can setup 103 | `pre-commit` using the following command: 104 | 105 | ```bash 106 | pre-commit install 107 | ``` 108 | -------------------------------------------------------------------------------- /docs/javascripts/katex.js: -------------------------------------------------------------------------------- 1 | document$.subscribe(({ body }) => { 2 | renderMathInElement(body, { 3 | delimiters: [ 4 | { left: "$$", right: "$$", display: true }, 5 | { left: "$", right: "$", display: false }, 6 | { left: "\\(", right: "\\)", display: false }, 7 | { left: "\\[", right: "\\]", display: true } 8 | ], 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /docs/manifesto.md: -------------------------------------------------------------------------------- 1 | # Bloqade Manifesto 2 | 3 | The vision of Bloqade is to empower quantum scientists, from applications development to algorithmic co-design, to build hybrid quantum-classical programs that leverage the strength of neutral atom quantum computers and have a real chance of demonstrating quantum utility. Bloqade is built on top of [Kirin](https://github.com/QuEraComputing/kirin/), an open source compiler infrastructure designed for kernel functions and composable representations. 4 | 5 | ## Composable quantum programming 6 | 7 | Today Bloqade becomes a [namespace package](https://packaging.python.org/en/latest/guides/packaging-namespace-packages/) of multiple eDSLs (embedded domain-specific languages) around digital and analog quantum computation. `bloqade.analog` is the module for analog-mode neutral atom computers and includes several handy utilities ranging from building or analyzing analog programs, to emulation or executing on QuEra's cloud-accessible hardware "Aquila". 8 | 9 | Other submodules such as `bloqade.qasm2`, `bloqade.pyqrack` and `bloqade.stim` are the initial iteration to represent digital circuit execution using gate-based quantum computing on reconfigurable neutral atoms. It extends the QASM2 language to include extra annotation of circuits that is important for efficient execution, such as parallelism and global gates. As well as being able to construct quantum programs with the full convenience of typical classical programming within hardware kernels -- such as loops and control flow -- Bloqade also includes basic compiler transformation passes, emulation, and code generation. 10 | 11 | But Bloqade is not done with just these modules. We envision adding new modules (called "dialects") which help you write programs which are tuned for optimal performance in an error corrected era, and on neutral atom hardware. Stay tuned and help us build the future of quantum computing as we build out new components, such as QEC and atom moving dialects. 12 | 13 | 14 | ## Hardware-oriented programming and co-design 15 | 16 | At its core, Bloqade strives to be the neutral atom SDK for getting the most out of today's and tomorrows' quantum hardware. It is clear that the circuit-level abstraction is not enough to program real quantum hardware; indeed, tomorrows' quantum demonstrations and applications must program at the hardware level and develop special tooling to compile higher-level abstractions to efficient implementations. We call this process **"co-design"**: designing algorithms specialized to near-term hardware, with an eye on nontrivial demonstrations and scalable solutions. Ultimately, this co-design approach requires hardware-specific DSLs which explicitly represent the native executions on neutral atom hardware: in other words, Bloqade. 17 | 18 | 19 | ## Hybrid computing beyond circuits 20 | Many quantum algorithms are hybrid, requiring both classical and quantum resources to work together in a hybrid computation architecture. This could be anything from syndrome extraction and measurement-based computing to variational parameter updates in VQE methods and orbital fragmentation methods in molecular simulation. Through the use of the Kirin compiler infrastructure, Bloqade embraces this philosophy of heterogeneous compute. Kirin programs are written as (compositions of) [kernels](https://en.wikipedia.org/wiki/Compute_kernel)-- subroutines that are intended to run on particular hardware (such as QPUs), or orchestrated to run on heterogeneous compute (such as a real-time classical runtime plus a QPU). These subroutines-- plus the built-in hybrid representations-- enable many key primitives, such as error correction. 21 | 22 | Additionally, the ability to compose functions together and to use typical classical programming structures like `if` and recursion enables many simplifications in writing raw circuit executions. In fact, recursion and the ability to dynamically allocate new memory (which is not known until runtime) enables many powerful subroutines and is natively enabled with Bloqade's kernel-based representation; for example, see [this implementation](digital/examples/repeat_until_success.py) of a repeat-until-success program. 23 | 24 | ## Analog, digital, logical: towards real quantum utility 25 | 26 | The first step in Bloqade was building out the analog mode SDK, designed to interface with QuEra’s cloud-accessible analog-mode neutral-atom quantum computer Aquila, as well as enable analysis and scientific discovery in analog quantum computing. But the journey should not stop there: real quantum utility is error corrected and requires robust algorithmic exploration and design of quantum primitives, in-depth analysis of near-term hardware performance and benchmarking, and building pipelines and hybrid architectures that are intended not just for today’s demonstrators but also for tomorrow’s utility-scale hardware. By introducing the next generation of Bloqade, we hope to enable this exploration by adding in support for near-term digital and intermediate-term logical representations of hybrid quantum computations. 27 | 28 | ## Join us! 29 | 30 | If you are interested in contributing, please see the contribution page [here](contrib.md). If you are interested in exploring more about neutral atom quantum computing, check out some analog tutorials [here](https://queracomputing.github.io/bloqade-analog-examples/dev/), and some circuit tutorials [here](https://bloqade.quera.com/latest/digital/). If you wish to work closer with QuEra, please feel free to reach out! 31 | -------------------------------------------------------------------------------- /docs/quick_start/analog/geometry-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/analog/geometry-visualization.png -------------------------------------------------------------------------------- /docs/quick_start/analog/index.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | 6 | # Quick Start 7 | 8 | All the sections below are self-contained, you can click on the links in the Table of Contents to read the relevant parts. 9 | 10 | ## Defining Atom Geometry 11 | 12 | You can import pre-defined geometries based on [Bravais lattices](https://en.wikipedia.org/wiki/Bravais_lattice) from `bloqade.analog.atom_arrangement`. You may also specify a lattice spacing which dictates the spacing between the atoms as well as the number of atom sites in a certain direction. 13 | 14 | ```python 15 | from bloqade.analog.atom_arrangement import Square, Kagome 16 | 17 | simple_geometry = Square(2, 4, lattice_spacing = 4.0) 18 | more_complex_geometry = Kagome(2, 2, lattice_spacing = 2.0) 19 | ``` 20 | 21 | You can easily visualize your geometries as well with `.show()`: 22 | 23 | ```python 24 | more_complex_geometry.show() 25 | ``` 26 |
27 | ![IPython Hints](geometry-visualization.png){ width="50%" } 28 |
29 | 30 | 31 | You can also add positions to a pre-defined geometry: 32 | 33 | ```python 34 | from bloqade.analog.atom_arrangement import Square 35 | 36 | base_geometry = Square(2) 37 | geometry_with_my_positions = base_geometry.add_position([(10,10), (20,20)]) 38 | ``` 39 | 40 | As well as apply defects via `.apply_defect_density`. In the example below we apply a defect with a probability of 0.2: 41 | 42 | ```python 43 | from bloqade.analog.atom_arrangement import Square, Kagome 44 | 45 | more_complex_geometry = Kagome(2, 2, lattice_spacing = 2.0) 46 | defective_geometry = more_complex_geometry.apply_defect_density(0.2) 47 | ``` 48 | 49 | Or if you want to completely roll out your own atom geometry from scratch just use `add_position` by itself: 50 | 51 | ```python 52 | from bloqade.analog import start 53 | 54 | my_geometry = start.add_position([(1,2), (3,4), (5,6)]) 55 | ``` 56 | 57 | ## Building Waveforms 58 | 59 | After you've [defined a geometry](#defining-atom-geometry) you: 60 | 61 | * Specify which level coupling to drive: `rydberg` or `hyperfine` 62 | * Specify `detuning`, `rabi.amplitude` or `rabi.phase` 63 | * Specify the [spatial modulation][local-control] 64 | 65 | Which then leads you to the ability to specify a waveform of interest and begin constructing your pulse sequence. 66 | In the example below, we target the ground-Rydberg level coupling to drive with uniform [spatial modulation][local-control] for the Rabi amplitude. Our waveform is a piecewise linear one which ramps from $0$ to $5 \,\text{rad/us}$, holds that value for $1 \,\text{us}$ and then ramps back down to $0 \,\text{rad/us}$. 67 | 68 | ```python 69 | from bloqade.analog import start 70 | 71 | geometry = start.add_position((0,0)) 72 | target_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform 73 | waveform_applied = ( 74 | target_rabi_amplitude 75 | .piecewise_linear(durations = [0.06, 1, 0.06], values = [0, 5, 5, 0]) 76 | ) 77 | ``` 78 | 79 | You aren't restricted to just piecewise linear waveforms however, you can also specify: 80 | 81 | * [`linear`]() - Define a transition from one value to another over a duration 82 | * [`constant`]() - Define a fixed value over a duration 83 | * [`piecewise_constant`]() - Define a step-wise function with specific durations for each step 84 | * [`poly`]() - Define a polynomial waveform using coefficients over a duration 85 | 86 | 87 | ## Arbitrary Functions as Waveforms 88 | 89 | For more complex waveforms it may provide to be tedious trying to chain together a large number of [`piecewise_constant`]() or [`piecewise_linear`]() methods and instead easier to just define the waveform as a function of time. 90 | 91 | Bloqade lets you easily plug in an arbitrary function with `.fn`: 92 | 93 | ```python 94 | from bloqade.analog import start 95 | from math import sin 96 | 97 | geometry = start.add_position((0,0)) 98 | target_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform 99 | 100 | def custom_waveform(t): 101 | return 2.0 * sin(t) 102 | 103 | custom_waveform_applied = ( 104 | target_rabi_amplitude 105 | .fn(custom_waveform, duration = 3.0) 106 | ) 107 | ``` 108 | 109 | In this form you can immediately [emulate](#emulation) it if you'd like but to run this on [hardware](#submitting-to-hardware) you need to discretize it. The waveform on hardware has to either be: 110 | 111 | * Piecewise linear for Rabi amplitude and detuning terms of the Hamiltonian 112 | * Piecewise constant for the Phase term of the Hamiltonian 113 | 114 | Bloqade can automatically perform this conversion with `sample()`, all you need to do is specify the kind of interpolation and the size of the discretization step in time. Below we set the step duration to be $0.05 \,\text{us}$ with `"linear"` interpolation to give us a resulting piecewise linear waveform. 115 | 116 | ```python 117 | custom_discretized_waveform_applied = ( 118 | target_rabi_amplitude 119 | .fn(custom_waveform, duration = 3.0) 120 | .sample(0.05, "linear") 121 | ) 122 | ``` 123 | 124 | !!! note 125 | 126 | Programs that have custom functions as waveforms are not fully serializable. This means that when you are [saving and reloading results](#saving-and-loading-results), the original embedded program will be missing that custom waveform. You will still be able to analyze the saved results! 127 | 128 | 129 | ## Slicing and Recording Waveforms 130 | 131 | When you conduct [parameter sweeps](#parameter-sweeps) with your program, you may want to sweep over your program across time. This will require "slicing" your waveforms, where you define the waveform of interest and then, using a variable with `.slice`, indicate the times at which the waveform duration should be cut short. 132 | 133 | In the example below we define a simple piecewise linear waveform but slice it starting from a time duration of $0 \,\text{us}$ to values between $1$ to $2 \,\text{us}$. 134 | 135 | 136 | ```python 137 | from bloqade.analog import start 138 | import numpy as np 139 | 140 | sliced_program = ( 141 | start.add_position((0, 0)) 142 | .rydberg.rabi.amplitude.uniform.piecewise_linear( 143 | durations=[0.5, 2.5, 0.5], values=[0, 3.0, 3.0, 0] 144 | ).slice(start=0, stop="run_time") 145 | ) 146 | 147 | run_times = np.linspace(1.0, 2.0, 10) 148 | vars_assigned_program = sliced_program.batch_assign(run_time=run_times) 149 | ``` 150 | 151 | This program will run fine in [emulation](#emulation) but due to hardware constraints certain waveforms (such as those targeting the Rabi Amplitude), the waveform needs to start and end at $0 \,\text{rad}/\text{us}$. Thus, there needs to be a way to slice our waveform but also add an end component to that waveform. `.record` in Bloqade lets you literally "record" the value at the end of a `.slice` and then use it to construct further parts of the waveform. 152 | 153 | In the program below the waveform is still sliced but with the help of `.record` a linear segment that pulls the waveform down to $0.0 \,\text{rad}/\text{us}$ from whatever its current value at the slice is in $0.7 \,\text{us}$ is added. 154 | 155 | ```python 156 | from bloqade.analog import start 157 | import numpy as np 158 | 159 | sliced_program = ( 160 | start.add_position((0, 0)) 161 | .rydberg.rabi.amplitude.uniform.piecewise_linear( 162 | durations=[0.5, 2.5, 0.5], values=[0, 3.0, 3.0, 0] 163 | ).slice(start=0, stop="run_time") 164 | .record("waveform_value") 165 | .linear("rabi_value", 0.0, 0.7) 166 | ) 167 | 168 | run_times = np.linspace(1.0, 2.0, 10) 169 | vars_assigned_program = sliced_program.batch_assign(run_time=run_times) 170 | ``` 171 | 172 | 173 | ## Waveforms with No Geometry 174 | 175 | If you have multiple [atom geometries](#defining-atom-geometry) you'd like to apply a [pulse sequence](#building-waveforms) to or you simply don't want to worry about what atom geometry to start with, you can just build straight off of `start`: 176 | 177 | ```python 178 | from bloqade.analog import start 179 | 180 | pulse_sequence = ( 181 | start 182 | .rydberg.rabi.amplitude.uniform 183 | .constant(value=1.0, duration=1.0) 184 | .parse_sequence() 185 | ) 186 | ``` 187 | 188 | You can visualize your sequence as well with `.show()`: 189 | 190 | ```python 191 | pulse_sequence.show() 192 | ``` 193 | 194 | ![](pulse-sequence-visualization.png) 195 | 196 | And when you're content with it you just `.apply()` it on the geometries of your choice: 197 | 198 | ```python 199 | from bloqade.analog.atom_arrangement import Honeycomb, Kagome 200 | 201 | geometry_1 = Honeycomb(2, lattice_spacing = 6.0) 202 | geometry_2 = Kagome(2, lattice_spacing = 6.0) 203 | 204 | program_1 = geometry_1.apply(pulse_sequence) 205 | program_2 = geometry_2.apply(pulse_sequence) 206 | ``` 207 | 208 | ## Emulation 209 | 210 | When you've completed the definition of your program you can use Bloqade's own emulator to get results. 211 | The emulation performs the [time evolution][rydberg-hamiltonian] of the [analog Rydberg Hamiltonian][rydberg-hamiltonian]. 212 | Here we say we want to the program to be run and measurements obtained 1000 times. 213 | 214 | ```python 215 | results = your_program.bloqade.python().run(1000) 216 | ``` 217 | 218 | !!! note 219 | If your atoms are particularly close together or the ODE solver gives you the following message: 220 | 221 | ``` 222 | RuntimeError: DOP853/DOPRI5: Problem is probably stiff (interrupted). 223 | ``` 224 | 225 | In which case you will need to specify the `interaction_picture=True` argument: 226 | 227 | ```python 228 | results = your_program.bloqade.python().run(1000, interaction_picture=True) 229 | ``` 230 | 231 | ## Submitting to Hardware 232 | 233 | To submit your program to hardware ensure you have your AWS Braket credentials loaded. You will need to use the [AWS CLI]() to do this. 234 | 235 | Then it's just a matter of selecting the *Aquila* on Braket backend. Before going any further Bloqade provides two options for running your program on actual hardware: 236 | 237 | * Using `.run` is blocking, meaning you will not be able to execute anything else while Bloqade waits for results 238 | * Using `.run_async` lets you submit to hardware and continue any further execution, while also letting you query the status of your program in the queue. 239 | 240 | In the example below we use `.run_async` to specify the program should be run and measurements obtained 1000 times. 241 | 242 | ```python 243 | async_results = your_program.braket.aquila().run_async(1000) 244 | ``` 245 | 246 | We can see the status of our program via: 247 | 248 | ```python 249 | async_results.fetch() 250 | ``` 251 | Which gives us the Task ID, a unique identifier for the task as well as the status of the task. In the example below the task is `Enqueued` meaning it has been successfully created and is awaiting execution on the cloud. When the task is actually running on hardware, the status will change to `Running`. 252 | ``` 253 | task ID status shots 254 | 0 arn:aws:braket:us-east-1:XXXXXXXXXXXX:quantum-... Enqueued 100 255 | ``` 256 | 257 | 258 | ## Analyzing Results 259 | 260 | When you've retrieved your results from either [emulation](#emulation) or [hardware](#submitting-to-hardware) you can generate a `.report()`: 261 | 262 | ```python 263 | report = results.report() 264 | ``` 265 | 266 | For the examples below we analyze the results of a two atom program. 267 | 268 | The report contains useful information such as: 269 | 270 | * The raw bitstrings measured per each execution of the program 271 | ```python 272 | report.bitstrings() 273 | ``` 274 | ``` 275 | [array([[1, 1], 276 | [1, 1], 277 | [1, 1], 278 | ..., 279 | [1, 1], 280 | [1, 1], 281 | [1, 0]], dtype=int8)] 282 | ``` 283 | 284 | * The number of times each unique bitstring occurred: 285 | ```python 286 | report.counts() 287 | ``` 288 | ``` 289 | [OrderedDict([('11', 892), ('10', 59), ('01', 49)])] 290 | ``` 291 | 292 | * The Rydberg Density for each atom 293 | ```python 294 | report.rydberg_densities() 295 | ``` 296 | ``` 297 | 0 1 298 | task_number 299 | 0 0.053 0.054 300 | ``` 301 | 302 | And can also provide useful visual information such as the state of your atoms and the bitstring distribution via: 303 | 304 | ```python 305 | report.show() 306 | ``` 307 | 308 | ![](report-visualization.png) 309 | 310 | ## Parameter Sweeps 311 | 312 | You can easily do parameter sweeps in emulation and on *Aquila* with variables. Bloqade automatically detects strings in your program as variables that you can later assign singular or multiple values to. 313 | 314 | In the example below, we define a program with a singular variable that controls the amplitude of the waveform. 315 | 316 | ```python 317 | from bloqade.analog import start 318 | 319 | rabi_oscillations_program = ( 320 | start.add_position((0, 0)) 321 | .rydberg.rabi.amplitude.uniform.piecewise_linear( 322 | durations=[0.06, 3, 0.06], 323 | values=[0, "rabi_amplitude", "rabi_amplitude", 0] 324 | ) 325 | ) 326 | ``` 327 | 328 | We can assign a single fixed value to the variable: 329 | 330 | ```python 331 | single_value_assignment = rabi_oscillations_program.assign(rabi_amplitude=3.5) 332 | ``` 333 | 334 | Or, to perform a sweep, we use `.batch_assign`: 335 | 336 | ```python 337 | import numpy as np 338 | rabi_amplitudes = np.linspace(1.0, 2.0, 20) 339 | 340 | multiple_value_assignment = rabi_oscillations_program.batch_assign(rabi_amplitude=rabi_amplitudes) 341 | ``` 342 | 343 | This will actually create multiple versions of the program internally, with each program assigned a fixed value from the sweep. Bloqade will automatically handle the compilation of results from these multiple programs in order, meaning there is no major departure from what you saw in [analyzing the results](#analyzing-results) of your program. 344 | 345 | You can also delay assignment of a value to a variable by first declaring it in `.args()` and then passing a value when you call `run`: 346 | 347 | ```python 348 | delayed_assignment_program = rabi_oscillations_program.args(["rabi_amplitude"]) 349 | results = delayed_assignment_program.bloqade.python().run(100, args=(1.0,)) 350 | ``` 351 | 352 | You can alternatively treat the program as a callable after using `.args()` (note the inverted order of arguments in the call!): 353 | 354 | ```python 355 | delayed_assignment_program = rabi_oscillations_program.args(["rabi_amplitude"]) 356 | callable_program = delayed_assignment_program.bloqade.python() 357 | results = callable_program(1.0, shots=100) 358 | ``` 359 | 360 | Variables aren't just restricted to having values assigned to them, you can also [symbolically manipulate](#symbolic-parameters) them! 361 | 362 | ## Symbolic Parameters 363 | 364 | [Variables](#parameter-sweeps) in Bloqade can also be symbolically manipulated, giving you even more flexibility when you construct your program. 365 | 366 | In the example below, we externally declare a variable `my_var` that then has some arithmetic done on it to allow it to have a different value in a later part of the program: 367 | 368 | ```python 369 | from bloqade.analog import start, var 370 | 371 | my_var = var("my_variable") 372 | waveform_durations = [0.6, 1.0, 0.6] 373 | 374 | geometry = start.add_position((0,0)) 375 | target_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform 376 | rabi_waveform = ( 377 | target_rabi_amplitude 378 | .piecewise_linear(durations=waveform_durations, 379 | values=[0.0, my_var, my_var, 0.0]) 380 | ) 381 | target_detuning = rabi_waveform.detuning.uniform 382 | detuning_waveform = ( 383 | target_detuning 384 | .piecewise_linear(durations=waveform_durations, 385 | values=[my_var-1.0, my_var*0.5, my_var/2, my_var+1.0 ]) 386 | ) 387 | ``` 388 | 389 | You still perform variable assignment just like you normally would: 390 | 391 | ```python 392 | program = detuning_waveform.assign(my_variable=1.0) 393 | ``` 394 | 395 | You can also use Python's built-in `sum` if you want the sum of multiple variables as a value in your program. This is quite useful when it comes to needing to indicate a full duration for a waveform that doesn't need to be split up: 396 | 397 | ```python 398 | from bloqade.analog import start, var 399 | 400 | variable_durations = var(["a", "b", "c"]) 401 | 402 | geometry = start.add_position((0,0)) 403 | target_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform 404 | rabi_waveform = ( 405 | target_rabi_amplitude 406 | .piecewise_linear(durations=variable_durations, 407 | values=[0.0, 1.5, 1.5, 0.0]) 408 | ) 409 | target_detuning = rabi_waveform.detuning.uniform 410 | detuning_waveform = ( 411 | target_detuning 412 | .constant(duration=sum(variable_durations), 413 | value=16.2) 414 | ) 415 | ``` 416 | We later assign values and Bloqade will automatically handle the summation: 417 | 418 | ```python 419 | program = detuning_waveform.assign(a=0.5, b=1.2, c=0.5) 420 | ``` 421 | 422 | 423 | ## Saving and Loading Results 424 | 425 | You can save your results in JSON format using Bloqade's `save` function: 426 | 427 | ```python 428 | from bloqade.analog import start, save 429 | 430 | your_program = ... 431 | emulation_results = your_program.bloqade.python().run(100) 432 | hardware_results = your_program.braket.aquila.run_async(100) 433 | 434 | save(emulation_results, "emulation_results.json") 435 | save(hardware_results, "hardware_results.json") 436 | ``` 437 | 438 | And later reload them into Python using the `load` function: 439 | 440 | ```python 441 | from bloqade.analog import load 442 | emulation_results = load("emulation_results.json") 443 | hardware_results = load("hardware_results.json") 444 | ``` 445 | 446 | [local-control]: ../../background.md#local-control 447 | [rydberg-hamiltonian]: ../../background.md#rydberg-many-body-hamiltonian 448 | -------------------------------------------------------------------------------- /docs/quick_start/analog/ipython-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/analog/ipython-hints.gif -------------------------------------------------------------------------------- /docs/quick_start/analog/jupyter-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/analog/jupyter-hints.gif -------------------------------------------------------------------------------- /docs/quick_start/analog/pulse-sequence-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/analog/pulse-sequence-visualization.png -------------------------------------------------------------------------------- /docs/quick_start/analog/pycharm-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/analog/pycharm-hints.gif -------------------------------------------------------------------------------- /docs/quick_start/analog/report-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/analog/report-visualization.png -------------------------------------------------------------------------------- /docs/quick_start/analog/vscode-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/analog/vscode-hints.gif -------------------------------------------------------------------------------- /docs/quick_start/circuits/API/qasm2_core.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/API/qasm2_inline_defunct.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | 6 | ?? Merge with qasm2 core API to cut down on pages? 7 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/API/qasm2_noise.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/API/qasm2_parallel.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/API/qasm2_uop.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | 6 | ?? Merge with qasm2 core API to cut down on pages? 7 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/compiler_passes/asap_parallelism.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/compiler_passes/circuit_simplification.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/compiler_passes/index.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/compiler_passes/native_gate_rewrite.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/index.md: -------------------------------------------------------------------------------- 1 | # Digital Quantum Computing with circuits 2 | 3 | This section provides the quick start guide for developing quantum programs represented by circuits using Bloqade. Circuits are a general-purpose and powerful way of representing arbitrary computations. For a few examples please refer to our [examples](../../digital/index.md). 4 | 5 | 6 | ## Open Quantum Assembly Language (QASM2) and beyond 7 | 8 | We have chosen to closely mirror the semantics of the Open Quantum Assembly Language (QASM2) in bloqade.circuits. The QASM2 dialect is a simple quantum assembly language that allows you to write quantum circuits in a human-readable format. However, one should note that QASM2 is a very restricted language and does not support all the features of a high-level language. 9 | 10 | For example, there is a separation of **gate routines** declared with `gate` and main program written as a sequence of gate applications. While the gate routine is similar to a function in many ways, it does not support high-level features such as recursion (due to lack of `if` statement support inside) or control flows. 11 | 12 | Indeed, bloqade.circuits is designed with the notion of [kernels](https://queracomputing.github.io/kirin/latest/blog/2025/02/28/introducing-kirin-a-new-open-source-software-development-tool-for-fault-tolerant-quantum-computing/?h=kernel#what-are-kernel-functions) in mind by decorating functions with a `@qasm2.extended` decorator. The python code is interpreted and parsed by the [Kirin](https://queracomputing.github.io/kirin/latest/) compiler toolchain and lowered to an abstract representation of the program. These kernels can include classical computation and the usual programming structures-- if/else, for and while loops, function inputs, and the like, as one is used to in Python. 13 | 14 | Additionally, the QASM2 representations of bloqade.circuits have been extended to include a key advantage of reconfigurable neutral atom hardware: parallelism. For example, one can represent a CZ gate applied to many qubit pairs at once as 15 | 16 | ```python 17 | @qasm2.extended 18 | def parallel_cz(controls:ilist[qasm2.Qubit],targets:ilist[qasm2.Qubit]): 19 | for ctr in range(len(controls)): 20 | qasm2.cz(ctrl=controls[0],qarg=controls[1]) 21 | ``` 22 | or equivalently use a SIMD (single instruction multiple data)-like instruction to explicitly flag the parallelism 23 | ```python 24 | @qasm2.extended 25 | def simd_cz(controls:ilist[qasm2.Qubit],targets:ilist[qasm2.Qubit]): 26 | qasm2.parallel.cz(ctrls=controls,qargs=targets) 27 | ``` 28 | Both will ultimately emit the exact same QASM code, but the latter snippet represents the kind of parallelism that can be leveraged by reconfigurable neutral atom hardware to more efficiently execute a program. 29 | 30 | While in our initial release we support QASM2 as the first eDSL, we plan to use it as a compilation target instead of a programming language for long-term development. We are working on a more expressive language that will be more suitable for quantum programming in the error-corrected era. 31 | 32 | 33 | ## Quick Example 34 | 35 | You can program kernels and quantum programs using the `qasm2.extended` decorator, such as the following Quantum Fourier Transform (QFT) circuit: 36 | 37 | ```python 38 | import math 39 | from bloqade import qasm2 40 | 41 | @qasm2.extended 42 | def qft(qreg: qasm2.QReg, n: int): 43 | if n == 0: 44 | return qreg 45 | 46 | qasm2.h(qreg[0]) 47 | for i in range(1, n): 48 | qasm2.cu1(qreg[i], qreg[0], 2 * math.pi / 2**i) 49 | qft(qreg, n - 1) 50 | return qreg 51 | ``` 52 | 53 | While the syntax is similar to Python, the `qasm2.extended` decorator actually compiles the `qft` function 54 | into lower-level intermediate representation (IR) code that can be later interpreted, analyzed, or executed on quantum hardware. Observe that this function cannot immediately compile down to QASM as it takes parametrized inputs, and is called recursively. 55 | 56 | You can inspect the initial IR code by calling the pretty printer: 57 | 58 | ```python 59 | qft.print() 60 | ``` 61 | 62 | ![QFT IR](qft-pprint.png) 63 | 64 | And emit QASM2 code 65 | 66 | ```python 67 | from bloqade.qasm2.emit import QASM2 # the QASM2 target 68 | from bloqade.qasm2.parse import pprint # the QASM2 pretty printer 69 | 70 | target = QASM2() 71 | ast = target.emit(main) 72 | pprint(ast) 73 | ``` 74 | 75 | ![QFT QASM2](qft-qasm2.png) 76 | 77 | 78 | ## Understanding the compilation process 79 | 80 | The compilation process is divided into several stages: 81 | 82 | 1. **Lowering**: the decorator `qasm2.extended` takes the Python Abstract Syntax Tree (AST) and lowers it into Kirin IR in the Static Single Assignment (SSA) form. 83 | 2. **Interpretation**: when invoking the PyQrack backend, the IR code is interpreted via Kirin's IR interpreter (missing link) with the PyQrack runtime backend. 84 | 3. **Target code generation**: when emitting QASM2 code: 85 | 1. The IR code gets aggressively inlined and all constant expressions are evaluated. 86 | 2. All loops and control flow are unrolled. 87 | 3. All compatible Python expressions (e.g `sin`, arithmetics) are translated into QASM2 expression. 88 | 4. The QASM2 code is emitted as QASM2 AST for pretty printing. 89 | 90 | ### Progressive compilation 91 | 92 | As well as writing circuit executions, you can also progressively transform and compile that circuit. For example, you may want to lower arbitrary single qubit unitaries into hardware-specific unitaries, as is done in [this example](../circuits/compiler_passes/native_gate_rewrite.md). For more details on the kinds of circuit-level compiler passes and how to write your own, see [here](../circuits/compiler_passes/index.md) 93 | 94 | ## Dialect groups 95 | 96 | Bloqade provides a set of [dialects (missing link)]() for QASM2 and our custom extensions to model parallel gates in neutral atom architectures. The basic QASM2 functionality can be enabled via 97 | 98 | ```bash 99 | pip install bloqade[qasm2] 100 | ``` 101 | 102 | ### Extended QASM 103 | 104 | The decorator `qasm2.extended` is a group of smaller dialects: 105 | 106 | ```python 107 | extended = structural_no_opt.union( 108 | [ 109 | inline, 110 | uop, 111 | glob, 112 | noise, 113 | parallel, 114 | core, 115 | ] 116 | ) 117 | ``` 118 | 119 | where `structural_no_opt` is the base dialect group that provides the basic control flow, common Python expressions (but not all), then: 120 | 121 | - `core` provides the core QASM2 operations such as register allocation, measurement and reset. 122 | - `uop` provides the unary operations, such as standard Pauli gates, rotation gates, etc. 123 | 124 | The following dialects are specific to neutral atom quantum computing as an extension: 125 | 126 | - `glob` provides the global gates (Rydberg specific) 127 | - `noise` provides the noise channels 128 | - `parallel` provides the parallel gate support (Rydberg specific). 129 | - `inline` dialect provides the inline QASM string 130 | 131 | ### Strict QASM2 mode 132 | 133 | While the `qasm2.extended` decorator provides a lot of high-level features as an extension of QASM2, you may want to program in strict QASM2 mode for compatibility reasons. You can do this by using the `qasm2.main` and `qasm2.gate` decorators: 134 | 135 | ```python 136 | @qasm2.main 137 | def main(): 138 | qasm2.h(0) 139 | qasm2.cx(0, 1) 140 | qasm2.measure(0) 141 | qasm2.measure(1) 142 | return qasm2.qreg(2) 143 | ``` 144 | 145 | which corresponding to the following QASM2 code: 146 | 147 | ```qasm 148 | OPENQASM 2.0; 149 | include "qelib1.inc"; 150 | 151 | qreg q[2]; 152 | creg c[2]; 153 | 154 | h q[0]; 155 | cx q[0], q[1]; 156 | measure q[0] -> c[0]; 157 | measure q[1] -> c[1]; 158 | ``` 159 | 160 | Note that the `return` values are all ignored due to lack of equivalent in QASM2. 161 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/interpreters_and_analysis/index.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/interpreters_and_analysis/pyqrack_emulator.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | 6 | ## Running simulations 7 | 8 | The program can be executed via a simulator backend, e.g. PyQrack, you can install it via 9 | 10 | 11 | ```bash 12 | pip install bloqade-pyqrack[backend] 13 | ``` 14 | 15 | with the `backend` being one of ` pyqrack`, `pyqrack-cpu`, `pyqrack-cuda` depending on 16 | the hardware and OS you have. See [README](https://github.com/QuEraComputing/bloqade-pyqrack?tab=readme-ov-file#which-extra-do-i-install) for mote details. 17 | 18 | ```python 19 | @qasm2.extended 20 | def main(): 21 | return qft(qasm2.qreg(3), 3) 22 | 23 | device = PyQrack() 24 | qreg = device.run(main) 25 | print(qreg) 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/interpreters_and_analysis/qasm2_codegen.md: -------------------------------------------------------------------------------- 1 | !!! warning 2 | This page is under construction. The content may be incomplete or incorrect. Submit an issue 3 | on [GitHub](https://github.com/QuEraComputing/bloqade/issues/new) if you need help or want to 4 | contribute. 5 | 6 | ## Emitting QASM2 code 7 | 8 | You can also emit QASM2 code from the IR code: 9 | 10 | ```python 11 | from bloqade.qasm2.emit import QASM2 # the QASM2 target 12 | from bloqade.qasm2.parse import pprint # the QASM2 pretty printer 13 | 14 | target = QASM2() 15 | ast = target.emit(main) 16 | pprint(ast) 17 | ``` 18 | 19 | ![QFT QASM2](../qft-qasm2.png) 20 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/qft-pprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/circuits/qft-pprint.png -------------------------------------------------------------------------------- /docs/quick_start/circuits/qft-qasm2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/docs/quick_start/circuits/qft-qasm2.png -------------------------------------------------------------------------------- /docs/reference/analog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: analog 3 | --- 4 | 5 | # ::: bloqade.analog 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/analysis.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: analysis 3 | --- 4 | 5 | # ::: bloqade.analysis 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/device.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: device 3 | --- 4 | 5 | # ::: bloqade.device 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | --- 4 | 5 | Use the navigation on the left to browse the full API reference of the bloqade package. 6 | The documentation is separated into all submodules of bloqade. 7 | -------------------------------------------------------------------------------- /docs/reference/pyqrack.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pyqrack 3 | --- 4 | 5 | # ::: bloqade.pyqrack 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/qasm2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: qasm2 3 | --- 4 | 5 | # ::: bloqade.qasm2 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/qbraid.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: qbraid 3 | --- 4 | 5 | # ::: bloqade.qbraid 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/squin.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: squin 3 | --- 4 | 5 | # ::: bloqade.squin 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/stim.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: stim 3 | --- 4 | 5 | # ::: bloqade.stim 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/task.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: task 3 | --- 4 | 5 | # ::: bloqade.task 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/types.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: types 3 | --- 4 | 5 | # ::: bloqade.types 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/reference/visual.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: visual 3 | --- 4 | 5 | # ::: bloqade.visual 6 | options: 7 | show_submodules: true 8 | -------------------------------------------------------------------------------- /docs/scripts/gen_ref_nav.py: -------------------------------------------------------------------------------- 1 | """Generate the code reference pages and navigation.""" 2 | 3 | from pathlib import Path 4 | 5 | import mkdocs_gen_files 6 | 7 | SRC_PATH = "src" 8 | 9 | skip_keywords = [ 10 | "julia", ## [KHW] skip for now since we didn't have julia codegen rdy 11 | "builder/base", ## hiding from user 12 | "builder/terminate", ## hiding from user 13 | "ir/tree_print", ## hiding from user 14 | "ir/visitor", ## hiding from user 15 | "codegen/", ## hiding from user 16 | "builder/factory", ## hiding from user 17 | "builder_old", ## deprecated from user 18 | "task_old", ## deprecated from user 19 | "visualization", ## hiding from user 20 | "submission/capabilities", ## hiding from user 21 | "submission/quera_api_client", 22 | ] 23 | 24 | nav = mkdocs_gen_files.Nav() 25 | for path in sorted(Path(SRC_PATH).rglob("*.py")): 26 | module_path = path.relative_to(SRC_PATH).with_suffix("") 27 | doc_path = path.relative_to(SRC_PATH).with_suffix(".md") 28 | full_doc_path = Path("reference", doc_path) 29 | 30 | iskip = False 31 | 32 | for kwrd in skip_keywords: 33 | if kwrd in str(doc_path): 34 | iskip = True 35 | break 36 | if iskip: 37 | print("[Ignore]", str(doc_path)) 38 | continue 39 | 40 | print("[>]", str(doc_path)) 41 | 42 | parts = tuple(module_path.parts) 43 | 44 | if parts[-1] == "__init__": 45 | parts = parts[:-1] 46 | doc_path = doc_path.with_name("index.md") 47 | full_doc_path = full_doc_path.with_name("index.md") 48 | elif parts[-1].startswith("_"): 49 | continue 50 | 51 | nav[parts] = doc_path.as_posix() 52 | with mkdocs_gen_files.open(full_doc_path, "w") as fd: 53 | ident = ".".join(parts) 54 | fd.write(f"::: {ident}") 55 | 56 | mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path) 57 | 58 | with mkdocs_gen_files.open("reference/SUMMARY.txt", "w") as nav_file: 59 | nav_file.writelines(nav.build_literate_nav()) 60 | -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --md-primary-fg-color: #6437FF; 3 | --md-accent-fg-color: #6437FF; 4 | } 5 | 6 | #logo_light_mode { 7 | display: var(--md-footer-logo-light-mode); 8 | } 9 | 10 | #logo_dark_mode { 11 | display: var(--md-footer-logo-dark-mode); 12 | } 13 | 14 | [data-md-color-scheme="slate"] { 15 | --md-footer-logo-dark-mode: block; 16 | --md-footer-logo-light-mode: none; 17 | } 18 | 19 | [data-md-color-scheme="default"] { 20 | --md-footer-logo-dark-mode: none; 21 | --md-footer-logo-light-mode: block; 22 | } 23 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | coverage-run: 2 | coverage run -m pytest test 3 | 4 | coverage-xml: 5 | coverage xml 6 | 7 | coverage-html: 8 | coverage html 9 | 10 | coverage-report: 11 | coverage report 12 | 13 | coverage-open: 14 | open htmlcov/index.html 15 | 16 | coverage: coverage-run coverage-xml coverage-report 17 | 18 | doc: 19 | mkdocs serve 20 | 21 | doc-build: 22 | mkdocs build 23 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: "The Neutral Atom SDK" 2 | repo_name: bloqade 3 | repo_url: https://github.com/QuEraComputing/bloqade 4 | site_description: >- 5 | Bloqade - the neutral atom SDK 6 | edit_uri: "edit/main/docs/" 7 | site_url: https://bloqade.quera.com/ 8 | 9 | # Page tree 10 | nav: 11 | - Home: 12 | - index.md 13 | - Installation: install.md 14 | - Background: background.md 15 | - Manifesto: manifesto.md 16 | - Quick Start: 17 | - Circuits: 18 | - quick_start/circuits/index.md 19 | - Examples: 20 | - Quantum Fourier Transform: "digital/examples/qft.py" 21 | - GHZ state preparation: "digital/examples/ghz.py" 22 | - Pauli Exponential: "digital/examples/pauli_exponentiation.py" 23 | - Repeat Until Success: "digital/examples/repeat_until_success.py" 24 | - QAOA: "digital/examples/qaoa.py" 25 | - Compiler passes: 26 | - quick_start/circuits/compiler_passes/index.md 27 | - Native gate rewrite: quick_start/circuits/compiler_passes/native_gate_rewrite.md 28 | - ASAP Parallelism: quick_start/circuits/compiler_passes/asap_parallelism.md 29 | - Circuit simplification: quick_start/circuits/compiler_passes/circuit_simplification.md 30 | - Interpreters and analysis: 31 | - quick_start/circuits/interpreters_and_analysis/index.md 32 | - QASM2 Codegen: quick_start/circuits/interpreters_and_analysis/qasm2_codegen.md 33 | - PyQrack emulator: quick_start/circuits/interpreters_and_analysis/pyqrack_emulator.md 34 | - Circuit dialect API: 35 | - QASM2 Core: quick_start/circuits/API/qasm2_core.md 36 | - QASM2 Noise: quick_start/circuits/API/qasm2_noise.md 37 | - QASM2 Parallel: quick_start/circuits/API/qasm2_parallel.md 38 | - QASM2 Uop: quick_start/circuits/API/qasm2_uop.md 39 | - Analog: 40 | - quick_start/analog/index.md 41 | - Qourier: 42 | - quick_start/qourier/index.md 43 | - Contributing: contrib.md 44 | - Digital Tutorials: 45 | - digital/index.md 46 | - digital/dialects_and_kernels.md 47 | - Simulator Device: 48 | - digital/simulator_device/simulator_device.md 49 | - digital/simulator_device/tasks.md 50 | - Circuits: 51 | - Quantum Fourier Transform: "digital/examples/qft.py" 52 | - GHZ state preparation: "digital/examples/ghz.py" 53 | - Pauli Exponential: "digital/examples/pauli_exponentiation.py" 54 | - Repeat Until Success: "digital/examples/repeat_until_success.py" 55 | - QAOA: "digital/examples/qaoa.py" 56 | - Deutsch-Jozsa Algorithm: "digital/examples/deutsch_squin.py" 57 | - Analog Tutorials: 'https://queracomputing.github.io/bloqade-analog-examples/dev/' 58 | - Bloqade Analog: 'https://queracomputing.github.io/bloqade-analog/latest/' 59 | - Analog Reference: 'https://queracomputing.github.io/bloqade-analog/latest/reference/overview/' 60 | - API Reference: 61 | - reference/index.md 62 | - reference/analog.md 63 | - reference/analysis.md 64 | - reference/device.md 65 | - reference/pyqrack.md 66 | - reference/qasm2.md 67 | - reference/qbraid.md 68 | - reference/squin.md 69 | - reference/stim.md 70 | - reference/task.md 71 | - reference/types.md 72 | - reference/visual.md 73 | - Blog: 74 | - blog/index.md 75 | - QuEra Computing: 'https://quera.com' 76 | # - QASM2: reference/bloqade/qasm2/ 77 | 78 | theme: 79 | name: material 80 | favicon: assets/favicon.ico 81 | logo: assets/logo-dark.svg 82 | font: 83 | text: Lato 84 | palette: 85 | - scheme: default 86 | primary: custom 87 | accent: custom 88 | toggle: 89 | icon: material/brightness-7 90 | name: Switch to dark mode 91 | - scheme: slate 92 | primary: custom 93 | accent: custom 94 | toggle: 95 | icon: material/brightness-4 96 | name: Switch to light mode 97 | features: 98 | - announce.dismiss 99 | - content.action.view 100 | - content.action.edit 101 | - content.code.annotate 102 | - content.code.copy 103 | # - content.tabs.link 104 | - content.tooltips 105 | # - header.autohide 106 | # - navigation.expand 107 | - navigation.indexes 108 | # - navigation.instant 109 | # - navigation.prune 110 | - navigation.sections 111 | - navigation.tabs 112 | # - navigation.tabs.sticky 113 | - navigation.top 114 | - navigation.tracking 115 | - navigation.footer 116 | - search.highlight 117 | - search.suggest 118 | - toc.follow 119 | 120 | plugins: 121 | - mkdocstrings: 122 | handlers: 123 | python: 124 | options: 125 | show_if_no_docstring: false 126 | separate_signature: true 127 | merge_init_into_class: true 128 | extensions: 129 | - griffe_inherited_docstrings 130 | show_inheritance_diagram: true 131 | show_signature_annotations: true 132 | show_symbol_type_heading: true 133 | show_symbol_type_toc: true 134 | docstring_options: 135 | ignore_init_summary: true 136 | - search: 137 | separator: '[\s\-,:!=\[\: )"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' 138 | - gen-files: 139 | scripts: 140 | - docs/scripts/gen_ref_nav.py 141 | - literate-nav: 142 | nav_file: SUMMARY.txt 143 | - minify: 144 | minify_html: false 145 | - blog 146 | - mkdocs-jupyter: 147 | execute: !ENV ["EXECUTE_NOTEBOOKS", false] 148 | allow_errors: false 149 | ignore: ["scripts/*"] 150 | 151 | extra_javascript: 152 | - javascripts/katex.js 153 | - https://unpkg.com/katex@0/dist/katex.min.js 154 | - https://unpkg.com/katex@0/dist/contrib/auto-render.min.js 155 | 156 | extra_css: 157 | - stylesheets/extra.css 158 | - https://unpkg.com/katex@0/dist/katex.min.css 159 | 160 | markdown_extensions: 161 | - abbr 162 | - admonition 163 | - attr_list 164 | - def_list 165 | - footnotes 166 | - md_in_html 167 | - pymdownx.superfences 168 | - pymdownx.caret 169 | - pymdownx.mark 170 | - pymdownx.tilde 171 | - pymdownx.tabbed: 172 | alternate_style: true 173 | - pymdownx.arithmatex: 174 | generic: true 175 | 176 | copyright: Copyright © 2025 Bloqade contributors 177 | 178 | extra: 179 | version: 180 | provider: mike 181 | social: 182 | - icon: simple/x 183 | link: https://x.com/QueraComputing 184 | - icon: fontawesome/brands/linkedin 185 | link: https://www.linkedin.com/company/quera-computing-inc/ 186 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "bloqade" 3 | version = "0.27.0-dev" 4 | description = "The software development toolkit for neutral atom arrays." 5 | readme = "README.md" 6 | authors = [ 7 | { name = "Roger-luo", email = "rluo@quera.com" }, 8 | { name = "kaihsin", email="khwu@quera.com" }, 9 | { name = "weinbe58", email="pweinberg@quera.com"}, 10 | { name = "johnzl-777", email="jlong@quera.com"}, 11 | ] 12 | classifiers = [ 13 | "Development Status :: 3 - Alpha", 14 | "Operating System :: OS Independent", 15 | "Programming Language :: Python", 16 | "Programming Language :: Python :: 3.10", 17 | "Programming Language :: Python :: 3.11", 18 | "Programming Language :: Python :: 3.12" 19 | ] 20 | requires-python = ">=3.10" 21 | dependencies = [ 22 | "bloqade-circuit[cirq,qasm2,qbraid,vis]~=0.4.0", 23 | "bloqade-analog~=0.16.3", 24 | ] 25 | 26 | [build-system] 27 | requires = ["hatchling"] 28 | build-backend = "hatchling.build" 29 | 30 | [tool.hatch.metadata] 31 | allow-direct-references = true 32 | 33 | [tool.hatch.build.targets.wheel] 34 | packages = ["src/bloqade"] 35 | 36 | [dependency-groups] 37 | dev = [ 38 | "black>=24.10.0", 39 | "coverage>=7.6.4", 40 | "ipython>=8.29.0", 41 | "isort>=5.13.2", 42 | "mypy>=1.13.0", 43 | "numpy>=1.26.4", 44 | "pre-commit>=4.0.1", 45 | "pyright>=1.1.388", 46 | "pytest>=8.3.3", 47 | "ruff>=0.7.3", 48 | "rust-just>=1.36.0", 49 | "tomlkit>=0.13.2", 50 | ] 51 | doc = [ 52 | "griffe-inherited-docstrings>=1.1.1", 53 | "mike>=2.1.3", 54 | "mkdocs>=1.6.1", 55 | "mkdocs-gen-files>=0.5.0", 56 | "mkdocs-literate-nav>=0.6.1", 57 | "mkdocs-material>=9.5.44", 58 | "mkdocs-minify-plugin>=0.8.0", 59 | "mkdocstrings[python]>=0.27.0", 60 | "mkdocs-jupyter>=0.25.1", 61 | ] 62 | examples = [ 63 | "networkx>=3.4.2", 64 | ] 65 | 66 | [tool.isort] 67 | profile = "black" 68 | combine_as_imports = true 69 | multi_line_output = 3 70 | length_sort = true 71 | src_paths = ["src/bloqade"] 72 | 73 | [tool.black] 74 | line-length = 88 75 | 76 | [tool.ruff] 77 | target-version = "py310" 78 | exclude = [ 79 | ".bzr", 80 | ".direnv", 81 | ".eggs", 82 | ".git", 83 | ".hg", 84 | ".mypy_cache", 85 | ".nox", 86 | ".pants.d", 87 | ".pytype", 88 | ".ruff_cache", 89 | ".svn", 90 | ".tox", 91 | ".venv", 92 | "__pypackages__", 93 | "_build", 94 | "buck-out", 95 | "build", 96 | "dist", 97 | "node_modules", 98 | "venv", 99 | ] 100 | 101 | [tool.ruff.lint] 102 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 103 | 104 | [tool.coverage.run] 105 | include = ["src/bloqade/*"] 106 | -------------------------------------------------------------------------------- /src/bloqade/README.md: -------------------------------------------------------------------------------- 1 | # Surprise! Where are all the source codes?! 2 | 3 | the `bloqade` repository is a Python namespace package[^1] [^2] that does not contain any source code. This repository only contains the examples and documentation for the `bloqade` package. This allows us to provide better installation experience for new users (they just install everything in bloqade universe), and also allows us to have a more modularized codebase. 4 | 5 | See also the discussion in [#213](https://github.com/QuEraComputing/bloqade/issues/213) regarding the design decision of using namespace package vs. mono-repo. 6 | 7 | ## Sub-packages 8 | 9 | When you install `bloqade`, you will get the following sub-packages: 10 | 11 | ### [`bloqade-circuit`](https://github.com/QuEraComputing/bloqade-circuit): the sub-package for quantum circuits. 12 | 13 | You can install it with: 14 | 15 | ```bash 16 | pip install bloqade-circuit 17 | ``` 18 | 19 | there are some extra you can choose to install: 20 | 21 | - `qasm2`: features that allow you to convert QASM files or program with our extended QASM2 eDSL in Python. 22 | - `vis`: features that allow you to use visualization. 23 | - `qbraid`: features that allow you to use Qbraid. 24 | - `cirq`: features that allow you to use Cirq. 25 | 26 | For example, you can enable `qasm2` and `vis` by running: 27 | 28 | ```bash 29 | pip install bloqade-circuit[qasm2,vis] 30 | ``` 31 | 32 | ### [`bloqade-analog`](https://github.com/QuEraComputing/bloqade-circuit): the sub-package for analog quantum computing. 33 | 34 | You can install it with: 35 | 36 | ```bash 37 | pip install bloqade-analog 38 | ``` 39 | 40 | This is actually the older version of `bloqade` when we only have analog quantum devices. If you have older codebase just change `from bloqade import ...` to `from bloqade.analog import ...` and it should work. 41 | 42 | 43 | ### More to come! 44 | 45 | We are working on one more sub-package for lower-level programming functionality of 46 | neutral atom quantum computers. Stay tuned for more updates! 47 | 48 | ## References 49 | 50 | [^1]: [PEP 420 – Implicit Namespace Packages](https://peps.python.org/pep-0420/) 51 | [^2]: [Real Python: What's a Python Namespace Package, and What's It For?](https://realpython.com/python-namespace-package/) 52 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/756dedadf5823b047e84ef25903f327863884969/test/__init__.py -------------------------------------------------------------------------------- /test/test_hello.py: -------------------------------------------------------------------------------- 1 | def test_hello(): 2 | assert True 3 | --------------------------------------------------------------------------------