├── docs ├── blog │ ├── index.md │ ├── posts │ │ ├── 2023 │ │ │ └── bloqade-release.md │ │ └── 2025 │ │ │ ├── flowchart.png │ │ │ └── a-new-journey.md │ └── .authors.yml ├── analog │ ├── home │ │ ├── emulation.md │ │ ├── geometry.md │ │ ├── waveforms.md │ │ ├── submission.md │ │ ├── visualization.md │ │ ├── migration.md │ │ ├── gotchas.md │ │ └── background.md │ ├── assets │ │ ├── favicon.ico │ │ ├── readme-gifs │ │ │ ├── smart-docs.gif │ │ │ ├── graph-select.gif │ │ │ ├── locations-hover.gif │ │ │ ├── hover-bitstrings.gif │ │ │ └── visualize-bitstrings.gif │ │ ├── quick_start │ │ │ ├── vscode-hints.gif │ │ │ ├── ipython-hints.gif │ │ │ ├── jupyter-hints.gif │ │ │ ├── pycharm-hints.gif │ │ │ ├── report-visualization.png │ │ │ ├── geometry-visualization.png │ │ │ └── pulse-sequence-visualization.png │ │ └── index │ │ │ ├── program-visualization.png │ │ │ ├── report-visualization.png │ │ │ ├── geometry-visualization.png │ │ │ └── hardware-program-visualization.png │ ├── contributing │ │ ├── asking-a-question.md │ │ ├── community-slack.md │ │ ├── documentation-issues.md │ │ ├── providing-feedback.md │ │ ├── reporting-a-bug.md │ │ ├── index.md │ │ ├── feature-requests.md │ │ ├── code-of-conduct.md │ │ └── design-philosophy-and-architecture.md │ ├── reference │ │ ├── overview.md │ │ ├── standard.md │ │ └── hardware-capabilities.md │ └── index.md ├── assets │ ├── favicon.ico │ ├── readme-gifs │ │ ├── graph-select.gif │ │ ├── smart-docs.gif │ │ ├── hover-bitstrings.gif │ │ ├── locations-hover.gif │ │ └── visualize-bitstrings.gif │ └── index │ │ ├── report-visualization.png │ │ ├── geometry-visualization.png │ │ ├── program-visualization.png │ │ └── hardware-program-visualization.png ├── quick_start │ ├── analog │ │ ├── vscode-hints.gif │ │ ├── ipython-hints.gif │ │ ├── jupyter-hints.gif │ │ ├── pycharm-hints.gif │ │ ├── geometry-visualization.png │ │ ├── report-visualization.png │ │ └── pulse-sequence-visualization.png │ └── circuits │ │ └── index.md ├── digital │ ├── dialects_and_kernels │ │ ├── qft-qasm2.png │ │ ├── qft-pprint.png │ │ ├── squin-ir-1.png │ │ ├── squin-ir-2.png │ │ ├── stim.md │ │ ├── squin.md │ │ ├── index.md │ │ └── qasm2.md │ ├── tutorials │ │ └── figures │ │ │ ├── hypercube_original.png │ │ │ └── hypercube_parallel.png │ ├── simulator_device │ │ ├── simulator_device.md │ │ └── tasks.md │ ├── cirq_interop │ │ ├── index.md │ │ ├── squin_to_cirq.md │ │ └── cirq_to_squin.md │ ├── examples │ │ ├── qasm2 │ │ │ ├── qft.py │ │ │ ├── pauli_exponentiation.py │ │ │ ├── repeat_until_success.py │ │ │ ├── ghz.py │ │ │ └── qaoa.py │ │ ├── index.md │ │ └── squin │ │ │ ├── deutsch_squin.py │ │ │ └── ghz.py │ ├── index.md │ └── compilation.md ├── javascripts │ └── katex.js ├── reference │ └── index.md ├── stylesheets │ └── extra.css ├── contrib.md ├── index.md ├── install.md ├── scripts │ └── gen_ref_nav.py ├── manifesto.md └── background.md ├── test ├── __init__.py └── test_hello.py ├── .github ├── dependabot.yml ├── workflows │ ├── isort.yml │ ├── lint.yml │ ├── ci.yml │ ├── doc-nightly.yml │ ├── doc.yml │ ├── devdoc.yml │ ├── pub_doc.yml │ ├── dev-doc-nightly.yml │ └── release.yml └── pull_bloqade_submodules │ └── action.yml ├── justfile ├── _typos.toml ├── .pre-commit-config.yaml ├── src └── bloqade │ └── README.md ├── README.md ├── pyproject.toml ├── .gitignore └── mkdocs.yml /docs/blog/index.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/test_hello.py: -------------------------------------------------------------------------------- 1 | def test_hello(): 2 | assert True 3 | -------------------------------------------------------------------------------- /docs/analog/home/emulation.md: -------------------------------------------------------------------------------- 1 | # Emulation 2 | 3 | This page is a work in progress! 4 | -------------------------------------------------------------------------------- /docs/analog/home/geometry.md: -------------------------------------------------------------------------------- 1 | # Geometry 2 | 3 | This page is a work in progress! 4 | -------------------------------------------------------------------------------- /docs/analog/home/waveforms.md: -------------------------------------------------------------------------------- 1 | # Waveforms 2 | 3 | This page is a work in progress! 4 | -------------------------------------------------------------------------------- /docs/analog/home/submission.md: -------------------------------------------------------------------------------- 1 | # Submission 2 | 3 | This page is a work in progress! 4 | -------------------------------------------------------------------------------- /docs/analog/home/visualization.md: -------------------------------------------------------------------------------- 1 | # Visualization 2 | 3 | This page is a work in progress! 4 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/analog/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/favicon.ico -------------------------------------------------------------------------------- /docs/blog/posts/2025/flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/blog/posts/2025/flowchart.png -------------------------------------------------------------------------------- /docs/assets/readme-gifs/graph-select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/readme-gifs/graph-select.gif -------------------------------------------------------------------------------- /docs/assets/readme-gifs/smart-docs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/readme-gifs/smart-docs.gif -------------------------------------------------------------------------------- /docs/quick_start/analog/vscode-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/quick_start/analog/vscode-hints.gif -------------------------------------------------------------------------------- /docs/assets/index/report-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/index/report-visualization.png -------------------------------------------------------------------------------- /docs/quick_start/analog/ipython-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/quick_start/analog/ipython-hints.gif -------------------------------------------------------------------------------- /docs/quick_start/analog/jupyter-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/quick_start/analog/jupyter-hints.gif -------------------------------------------------------------------------------- /docs/quick_start/analog/pycharm-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/quick_start/analog/pycharm-hints.gif -------------------------------------------------------------------------------- /docs/analog/assets/readme-gifs/smart-docs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/readme-gifs/smart-docs.gif -------------------------------------------------------------------------------- /docs/assets/index/geometry-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/index/geometry-visualization.png -------------------------------------------------------------------------------- /docs/assets/index/program-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/index/program-visualization.png -------------------------------------------------------------------------------- /docs/assets/readme-gifs/hover-bitstrings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/readme-gifs/hover-bitstrings.gif -------------------------------------------------------------------------------- /docs/assets/readme-gifs/locations-hover.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/readme-gifs/locations-hover.gif -------------------------------------------------------------------------------- /docs/analog/assets/quick_start/vscode-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/quick_start/vscode-hints.gif -------------------------------------------------------------------------------- /docs/analog/assets/readme-gifs/graph-select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/readme-gifs/graph-select.gif -------------------------------------------------------------------------------- /docs/digital/dialects_and_kernels/qft-qasm2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/digital/dialects_and_kernels/qft-qasm2.png -------------------------------------------------------------------------------- /docs/analog/assets/index/program-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/index/program-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/index/report-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/index/report-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/quick_start/ipython-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/quick_start/ipython-hints.gif -------------------------------------------------------------------------------- /docs/analog/assets/quick_start/jupyter-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/quick_start/jupyter-hints.gif -------------------------------------------------------------------------------- /docs/analog/assets/quick_start/pycharm-hints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/quick_start/pycharm-hints.gif -------------------------------------------------------------------------------- /docs/analog/assets/readme-gifs/locations-hover.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/readme-gifs/locations-hover.gif -------------------------------------------------------------------------------- /docs/assets/readme-gifs/visualize-bitstrings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/readme-gifs/visualize-bitstrings.gif -------------------------------------------------------------------------------- /docs/digital/dialects_and_kernels/qft-pprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/digital/dialects_and_kernels/qft-pprint.png -------------------------------------------------------------------------------- /docs/digital/dialects_and_kernels/squin-ir-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/digital/dialects_and_kernels/squin-ir-1.png -------------------------------------------------------------------------------- /docs/digital/dialects_and_kernels/squin-ir-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/digital/dialects_and_kernels/squin-ir-2.png -------------------------------------------------------------------------------- /docs/quick_start/analog/geometry-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/quick_start/analog/geometry-visualization.png -------------------------------------------------------------------------------- /docs/quick_start/analog/report-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/quick_start/analog/report-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/index/geometry-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/index/geometry-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/readme-gifs/hover-bitstrings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/readme-gifs/hover-bitstrings.gif -------------------------------------------------------------------------------- /docs/assets/index/hardware-program-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/assets/index/hardware-program-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/quick_start/report-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/quick_start/report-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/readme-gifs/visualize-bitstrings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/readme-gifs/visualize-bitstrings.gif -------------------------------------------------------------------------------- /docs/digital/tutorials/figures/hypercube_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/digital/tutorials/figures/hypercube_original.png -------------------------------------------------------------------------------- /docs/digital/tutorials/figures/hypercube_parallel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/digital/tutorials/figures/hypercube_parallel.png -------------------------------------------------------------------------------- /docs/analog/assets/quick_start/geometry-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/quick_start/geometry-visualization.png -------------------------------------------------------------------------------- /docs/quick_start/analog/pulse-sequence-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/quick_start/analog/pulse-sequence-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/index/hardware-program-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/index/hardware-program-visualization.png -------------------------------------------------------------------------------- /docs/analog/assets/quick_start/pulse-sequence-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuEraComputing/bloqade/HEAD/docs/analog/assets/quick_start/pulse-sequence-visualization.png -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/analog/contributing/asking-a-question.md: -------------------------------------------------------------------------------- 1 | # Ask a Question 2 | 3 | If you're interested in contributing to Bloqade, or just want to discuss the project, join the discussion on GitHub Discussions at https://github.com/QuEraComputing/bloqade-analog/discussions 4 | -------------------------------------------------------------------------------- /docs/analog/contributing/community-slack.md: -------------------------------------------------------------------------------- 1 | # Community Slack 2 | 3 | You can join QuEra's Slack workspace with this [link](https://join.slack.com/t/querapublic/shared_invite/zt-1d5jjy2kl-_BxvXJQ4_xs6ZoUclQOTJg). Join the `#bloqade` channel to discuss anything related to Bloqade. 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 FLAGS="": 19 | mkdocs serve {{FLAGS}} 20 | 21 | doc-build FLAGS="": 22 | mkdocs build {{FLAGS}} 23 | 24 | -------------------------------------------------------------------------------- /_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 | 16 | [files] 17 | extend-exclude = [".github/workflows/*"] 18 | -------------------------------------------------------------------------------- /docs/reference/index.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | Here you can find a full API reference grouped by the respective submodules of bloqade: 4 | 5 | * [`bloqade-circuit`](./bloqade-circuit/src/bloqade/types) 6 | * [`bloqade-analog`](./bloqade-analog/src/bloqade/analog/) 7 | 8 | Please note that the reference is auto-generated and therefore follows the structure of the code. 9 | You can use the navigation on the left to browse through the full API reference or use the search functionality. 10 | -------------------------------------------------------------------------------- /.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@v6 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@v6 16 | - uses: chartboost/ruff-action@v1 17 | black: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v6 21 | - uses: psf/black@stable 22 | -------------------------------------------------------------------------------- /docs/analog/contributing/documentation-issues.md: -------------------------------------------------------------------------------- 1 | # Reporting a Documentation Issue 2 | 3 | We are always looking to improve our documentation. If you find 4 | a typo or think something is unclear, please open an issue on 5 | our GitHub page: https://github.com/QuEraComputing/bloqade-analog/issues 6 | 7 | For typos or other minor problems, create an issue that contains 8 | a link to the specific page that includes the problem, along with 9 | a description of the problem and possibly a solution. 10 | 11 | For a request for new documentation content, please open up an 12 | issue and describe what you think is missing from the documentation. 13 | -------------------------------------------------------------------------------- /docs/analog/contributing/providing-feedback.md: -------------------------------------------------------------------------------- 1 | # Providing Feedback 2 | 3 | While Github Issues are a great way for us to better understand any issues your having with Bloqade as well as provide us with feature requests, we're always looking for ways to collect more general feedback about what the user experience with Bloqade is like. 4 | 5 | To do that we have [this form](https://share.hsforms.com/1FJjYan2VQC6NfrQ5IPAcewdrwvd) where you can provide your thoughts after using/experimenting/tinkering/hacking with Bloqade. 6 | 7 | Your feedback will help guide the future of Bloqade's design so be honest and know that you're contributing to the future of Quantum Computing with Neutral Atoms! 8 | -------------------------------------------------------------------------------- /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 | 24 | /* 25 | .md-typeset .grid { 26 | grid-template-columns: 1fr 1fr 1fr 1fr; 27 | } 28 | 29 | .md-typeset .grid.cards { 30 | font-size: small; 31 | } */ 32 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/digital/dialects_and_kernels/stim.md: -------------------------------------------------------------------------------- 1 | # Stim 2 | 3 | [Stim](https://github.com/quantumlib/Stim) is a library for simulating and analyzing quantum stabilizer codes and quantum error correction circuits. 4 | The corresponding dialect allows you to write kernels that can then be emitted as native Stim code. 5 | 6 | Here is a short example: 7 | 8 | ```python 9 | from bloqade import stim 10 | from bloqade.stim.emit import EmitStimMain 11 | 12 | @stim.main 13 | def main(): 14 | stim.x(targets=(0,2)) 15 | stim.cx(controls=(0,3), targets=(1,3)) 16 | 17 | main.print() 18 | 19 | target = EmitStimMain() 20 | target.run(main, args=()) 21 | stim_program = target.get_output() 22 | print(stim_program) 23 | ``` 24 | 25 | 26 | See also the [stim API reference](../../reference/bloqade-circuit/src/bloqade/stim/) 27 | -------------------------------------------------------------------------------- /docs/analog/contributing/reporting-a-bug.md: -------------------------------------------------------------------------------- 1 | # Reporting a Bug 2 | 3 | Bloqade is currently in the **alpha** phase of development, meaning bugs most likely exist in the 4 | current implementation. We are continuously striving to 5 | improve the stability of Bloqade. As such, we encourage 6 | our users to report all bugs they find. To do this, we 7 | ask you to submit an issue to our GitHub page: 8 | https://github.com/QuEraComputing/bloqade-analog/issues 9 | 10 | Please include the following information in your bug report: 11 | 12 | 1. A short, descriptive title. 13 | 14 | 2. A detailed description of the bug, including the 15 | expected behavior and what happened. 16 | 17 | 3. A minimal code example that reproduces the bug. 18 | 19 | 4. The version of Bloqade you are using. 20 | 21 | 5. The version of Python you are using. 22 | 23 | 6. The version of your operating system. 24 | -------------------------------------------------------------------------------- /docs/analog/contributing/index.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing to the project! We welcome all contributions. There are many different ways to contribute to Bloqade, and we are always looking for more help. We accept contributions in the form of bug reports, feature requests, documentation improvements, and code contributions. For more information about how to contribute, please read the following sections. 4 | 5 | 6 | ## Table of Contents 7 | 8 | - [Reporting a Bug ](reporting-a-bug.md) 9 | - [Reporting Documentation Issues](documentation-issues.md) 10 | - [Feature Requests](feature-requests.md) 11 | - [Developing Bloqade](../../contrib.md) 12 | - [Design Philosophy and Architecture](design-philosophy-and-architecture.md) 13 | - [Community Slack](community-slack.md) 14 | - [Ask a Question](asking-a-question.md) 15 | - [Providing Feedback](providing-feedback.md) 16 | -------------------------------------------------------------------------------- /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/analog/contributing/feature-requests.md: -------------------------------------------------------------------------------- 1 | # Requesting new Features 2 | 3 | Given that we are currently at the beginning of the development of 4 | the Bloqade python interface, we are open to suggestions about what 5 | features would be helpful to include in future package iterations. 6 | If you have a request for a new feature, please open an issue on our 7 | GitHub page: https://github.com/QuEraComputing/bloqade-analog/issues 8 | 9 | We ask that the feature requests be as specific as possible. Please 10 | include the following information in your feature request: 11 | 12 | 1. A short, descriptive title. 13 | 14 | 2. A detailed description of the feature, including your attempt 15 | to solve the problem with the current version of Bloqade. 16 | 17 | 3. A minimal code example that demonstrates the need for the feature. 18 | 19 | 4. The version of Bloqade you are using. 20 | 21 | 5. The version of Python you are using. 22 | 23 | 6. The version of your operating system. 24 | -------------------------------------------------------------------------------- /.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@v6 23 | - name: Install uv 24 | uses: astral-sh/setup-uv@v7 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 | -------------------------------------------------------------------------------- /docs/digital/simulator_device/tasks.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | Tasks are generally executed by [devices](./simulator_device.md). 4 | A task can be run locally or remotely. 5 | On the one hand, when running locally, your local machine will execute the task at hand and wait for the result. 6 | On the other hand, when a task is submitted to be executed on a remote device, you will obtain an object similar to a future object in async programming, which can await the result (or not). 7 | 8 | In order to interact with a task, you'll usually want to instantiate a device and create a new task on that device. 9 | For example, 10 | 11 | ```python 12 | from bloqade.pyqrack import StackMemorySimulator 13 | from bloqade import squin 14 | 15 | @squin.kernel 16 | def main(): 17 | q = squin.qalloc(2) 18 | 19 | squin.gate.h(q[0]) 20 | squin.gate.cx(q[0], q[1]) 21 | 22 | return q 23 | 24 | sim = StackMemorySimulator(min_qubits=2) 25 | task = sim.task(main) 26 | result = task.run() 27 | ``` 28 | 29 | !!! info 30 | Most methods that directly execute a kernel on a device are just wrappers for the above: 31 | a new task is created internally and then run on the device. 32 | 33 | 34 | !!! warning "Note" 35 | Currently, there are only local simulation devices available. 36 | However, in the near future, you will also be able to submit tasks to a remote machine and even actual quantum hardware. 37 | -------------------------------------------------------------------------------- /.github/workflows/doc-nightly.yml: -------------------------------------------------------------------------------- 1 | # This workflow builds & deploys the documentation using the latest release versions of dependent packages 2 | # Main use is to detect issues / accidental breaking changes with patch releases of e.g. bloqade-circuit 3 | name: Documentation 4 | on: 5 | schedule: 6 | - cron: 12 4 * * 0 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.headref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | documentation: 13 | name: Build documentation 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v6 17 | - name: Install uv 18 | uses: astral-sh/setup-uv@v7 19 | with: 20 | # Install a specific version of uv. 21 | version: "0.5.1" 22 | enable-cache: true 23 | cache-dependency-glob: "uv.lock" 24 | - name: Install Documentation dependencies 25 | run: uv sync --group doc 26 | 27 | - name: Pull bloqade submodules 28 | uses: ./.github/pull_bloqade_submodules 29 | 30 | - name: Set up build cache 31 | uses: actions/cache@v4 32 | id: cache 33 | with: 34 | key: mkdocs-material-nightly-${{ github.headref }} 35 | path: .cache 36 | restore-keys: | 37 | mkdocs-material- 38 | - name: Build documentation 39 | env: 40 | EXECUTE_NOTEBOOKS: "true" 41 | run: | 42 | uv run mkdocs build 43 | -------------------------------------------------------------------------------- /.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@v6 19 | 20 | - name: Install uv 21 | uses: astral-sh/setup-uv@v7 22 | with: 23 | # Install a specific version of uv. 24 | version: "0.5.1" 25 | enable-cache: true 26 | cache-dependency-glob: "uv.lock" 27 | 28 | - name: Install Documentation dependencies 29 | run: uv sync --group doc 30 | 31 | - name: Pull bloqade submodules 32 | uses: ./.github/pull_bloqade_submodules 33 | 34 | - name: Set up build cache 35 | uses: actions/cache@v4 36 | id: cache 37 | with: 38 | key: mkdocs-material-${{ github.ref }} 39 | path: .cache 40 | restore-keys: | 41 | mkdocs-material- 42 | - name: Depoly documentation 43 | env: 44 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 45 | run: | 46 | uv run mkdocs build 47 | - name: Deploy preview 48 | uses: rossjrw/pr-preview-action@v1 49 | with: 50 | source-dir: ./site 51 | -------------------------------------------------------------------------------- /docs/digital/cirq_interop/index.md: -------------------------------------------------------------------------------- 1 | # Interoperability with cirq 2 | 3 | The [Cirq](https://quantumai.google/cirq) framework is a powerful tool for writing quantum circuits targeting near-term devices. 4 | Instead of reinventing the wheel, Bloqade offers convenient interoperability with Cirq that allows you to jointly use both libraries in order to develop your quantum program. 5 | 6 | Specifically, you can turn a [`cirq.Circuit`](https://quantumai.google/reference/python/cirq/Circuit) object into a [squin](../dialects_and_kernels#squin) kernel function and vice versa. 7 | 8 | For details on each of these, please see the documentation pages below: 9 | 10 | * [Obtaining a squin kernel function from a `cirq.Circuit`](./cirq_to_squin.md) 11 | * [Emitting a `cirq.Circuit` from a squin kernel](./squin_to_cirq.md) 12 | 13 | For the API reference, please see the `cirq_utils` submodule in the API reference, specifically [here](../../reference/bloqade-circuit/src/bloqade/cirq_utils/lowering.md) and [here](../../reference/bloqade-circuit/src/bloqade/cirq_utils/emit/base.md). 14 | 15 | ## TL;DR 16 | 17 | Here's a short example: 18 | 19 | ```python 20 | from bloqade import cirq_utils 21 | import cirq 22 | 23 | q = cirq.LineQubit.range(2) 24 | circuit = cirq.Circuit( 25 | cirq.H(q[0]), 26 | cirq.CX(q[0], q[1]) 27 | ) 28 | print(circuit) 29 | 30 | main = cirq_utils.load_circuit(circuit) 31 | main.print() 32 | 33 | roundtrip_circuit = cirq_utils.emit_circuit(main) 34 | print(roundtrip_circuit) 35 | ``` 36 | -------------------------------------------------------------------------------- /.github/pull_bloqade_submodules/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Pull bloqade submodules' 2 | description: 'Composite action to pull in the git repositories of bloqade-* submodules at the latest version tags compatible with bloqade. They are put in a directory called submodules' 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: extract bloqade-circuit version 7 | id: bloqade_circuit_extract_version 8 | shell: bash 9 | run: | 10 | VERSION=$(uv pip show bloqade-circuit | awk '/^Version: / {print "v"$2}') 11 | echo "version=$VERSION" >> $GITHUB_OUTPUT 12 | 13 | - name: clone the latest bloqade-circuit release 14 | id: clone_bloqade_circuit 15 | uses: actions/checkout@v5 16 | with: 17 | repository: QuEraComputing/bloqade-circuit 18 | path: 'submodules/bloqade-circuit' 19 | ref: ${{ steps.bloqade_circuit_extract_version.outputs.version }} 20 | 21 | - name: Extract bloqade-analog version 22 | id: bloqade_analog_extract_version 23 | shell: bash 24 | run: | 25 | VERSION=$(uv pip show bloqade-analog | awk '/^Version: / {print "v"$2}') 26 | echo "version=$VERSION" >> $GITHUB_OUTPUT 27 | 28 | - name: clone the latest bloqade-analog release 29 | id: clone_bloqade_analog 30 | uses: actions/checkout@v5 31 | with: 32 | repository: QuEraComputing/bloqade-analog 33 | path: 'submodules/bloqade-analog' 34 | ref: ${{ steps.bloqade_analog_extract_version.outputs.version }} 35 | -------------------------------------------------------------------------------- /.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@v6 17 | with: 18 | fetch-depth: 0 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v7 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 | 29 | - name: Pull bloqade submodules 30 | uses: ./.github/pull_bloqade_submodules 31 | 32 | - name: Set up build cache 33 | uses: actions/cache@v4 34 | id: cache 35 | with: 36 | key: mkdocs-material-${{ github.ref }} 37 | path: .cache 38 | restore-keys: | 39 | mkdocs-material- 40 | # derived from: 41 | # https://github.com/RemoteCloud/public-documentation/blob/dev/.github/workflows/build_docs.yml 42 | - name: Configure Git user 43 | run: | 44 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 45 | git config --local user.name "github-actions[bot]" 46 | - name: Deploy documentation 47 | env: 48 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 49 | GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }} 50 | run: | 51 | git fetch origin gh-pages --depth=1 52 | uv run mike deploy -p dev 53 | -------------------------------------------------------------------------------- /.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@v6 17 | with: 18 | fetch-depth: 0 19 | - name: Install uv 20 | uses: astral-sh/setup-uv@v7 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 | 29 | - name: Pull bloqade submodules 30 | uses: ./.github/pull_bloqade_submodules 31 | 32 | - name: Set up build cache 33 | uses: actions/cache@v4 34 | id: cache 35 | with: 36 | key: mkdocs-material-${{ github.ref }} 37 | path: .cache 38 | restore-keys: | 39 | mkdocs-material- 40 | # derived from: 41 | # https://github.com/RemoteCloud/public-documentation/blob/dev/.github/workflows/build_docs.yml 42 | - name: Configure Git user 43 | run: | 44 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 45 | git config --local user.name "github-actions[bot]" 46 | - name: Set release notes tag 47 | run: | 48 | export TAG_PATH=${{ github.ref }} 49 | echo ${TAG_PATH} 50 | echo "TAG_VERSION=${TAG_PATH##*/}" >> $GITHUB_ENV 51 | echo ${TAG_VERSION} 52 | - name: Deploy documentation 53 | env: 54 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 55 | run: | 56 | git fetch origin gh-pages --depth=1 57 | uv run mike deploy --update-alias --push ${TAG_VERSION} latest 58 | -------------------------------------------------------------------------------- /.github/workflows/dev-doc-nightly.yml: -------------------------------------------------------------------------------- 1 | # This workflow pulls in the latest main branches of dependent packages and tries to build the documentation 2 | # so we can stay on top of issues with the examples when introducing changes 3 | name: Documentation 4 | on: 5 | schedule: 6 | - cron: 24 4 * * * 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.headref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | documentation: 13 | name: Build documentation 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v6 17 | - name: Install uv 18 | uses: astral-sh/setup-uv@v7 19 | with: 20 | # Install a specific version of uv. 21 | version: "0.5.1" 22 | enable-cache: true 23 | cache-dependency-glob: "uv.lock" 24 | - name: Install Documentation dependencies 25 | run: uv sync --group doc 26 | 27 | # get latest dependencies from git directly 28 | - name: clone the latest bloqade-circuit 29 | id: clone_bloqade_circuit 30 | uses: actions/checkout@v6 31 | with: 32 | repository: QuEraComputing/bloqade-circuit 33 | path: 'submodules/bloqade-circuit' 34 | 35 | - name: clone the latest bloqade-analog 36 | id: clone_bloqade_analog 37 | uses: actions/checkout@v6 38 | with: 39 | repository: QuEraComputing/bloqade-analog 40 | path: 'submodules/bloqade-analog' 41 | 42 | - name: add local repos as dependencies 43 | run: uv add submodules/bloqade-circuit submodules/bloqade-analog 44 | 45 | - name: Set up build cache 46 | uses: actions/cache@v4 47 | id: cache 48 | with: 49 | key: mkdocs-material-nightly-dev-${{ github.headref }} 50 | path: .cache 51 | restore-keys: | 52 | mkdocs-material- 53 | - name: Build documentation 54 | env: 55 | EXECUTE_NOTEBOOKS: "true" 56 | run: | 57 | uv run mkdocs build 58 | -------------------------------------------------------------------------------- /docs/analog/reference/overview.md: -------------------------------------------------------------------------------- 1 | # Builder Overview 2 | 3 | You may have noticed from the [Quick Start](../home/quick_start.md) and [Tutorials](https://queracomputing.github.io/bloqade-analog-examples/latest/) 4 | that Bloqade uses this interesting, dot-intensive syntax. 5 | 6 | ```python 7 | from bloqade import start 8 | 9 | prog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform.constant(1,1) 10 | ``` 11 | *Exhibit A: Lots of Dots* 12 | 13 | In fact, it might look remniscent of what you see in some gate-based Quantum Computing SDKs: 14 | 15 | ```python 16 | # this is strictly pseudocode 17 | circuit = init_qubits(n_qubits) 18 | # note the dots! 19 | circuit.x(0).z(1).cnot(0, 1)... 20 | ``` 21 | 22 | What's the deal with that? 23 | 24 | ## Syntax Motivations 25 | 26 | We call this syntax the *builder* or *builder syntax* and as its name implies, it is designed to let you build programs for Analog Hamiltonian Simulation hardware as easily and as straightforward as possible. 27 | 28 | The linear structure implies a natural hierarchy in how you think about targeting the various degrees of freedom (detuning, atom positions, Rabi amplitude, etc.) your program will have. In the beginning you have unrestricted access to all these degrees of freedom but in order to do something useful you need to: 29 | 30 | 1. Narrow down and explicitly identify **what** you want to control 31 | 2. Provide the instructions on **how** you want to control what your focused on 32 | 33 | *Context* is a strong component of the builder syntax, as you are both actively restricted from doing certain things that can introduce ambiguity based on where you are in your program and repeating the same action in different parts of the program yields different results. 34 | 35 | ## Visual Guides 36 | 37 | While we hope the Smart Documentation (the ability to instantly see all your next possible steps and their capabilities in your favorite IDE/IPython) is sufficient to get you where you need to go, we understand it's particularly beneficial to get a high-level overview of things before diving in. 38 | 39 | The [Standard Representation](standard.md) is a nice flow chart that gives a high-level overview of the different steps and components in the builder syntax. 40 | -------------------------------------------------------------------------------- /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 extras 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-analog): 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 | -------------------------------------------------------------------------------- /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 contain 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 to 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 managing 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/examples/qasm2/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. 9 | # %% 10 | import math 11 | 12 | from bloqade.pyqrack import StackMemorySimulator 13 | 14 | from bloqade import qasm2 15 | 16 | # %% [markdown] 17 | # In the following, we will define the Quantum Fourier Transform (QFT) circuit using recursion 18 | # inside a kernel function `qft`. The `qft` function takes two arguments: a quantum register `qreg` 19 | # and an integer `n` representing the number of qubits we want to apply the QFT circuit to. 20 | # %% 21 | pi = math.pi 22 | 23 | 24 | @qasm2.extended 25 | def qft(qreg: qasm2.QReg, n: int, k: int): 26 | if k != n: 27 | qasm2.h(qreg[k]) 28 | for i in range(k + 1, n): 29 | qasm2.cu1(qreg[i], qreg[k], 2 * math.pi / 2**i) 30 | qft(qreg, n, k + 1) # recursion 31 | return qreg 32 | 33 | 34 | # %% [markdown] 35 | # Next, we will call this kernel function `qft` inside a `main` function to check if 36 | # the QFT circuit is correctly implemented. We will use a quantum register of size 3. 37 | 38 | 39 | # %% 40 | @qasm2.extended 41 | def main(): 42 | return qft(qasm2.qreg(3), 3, 0) 43 | 44 | 45 | # %% [markdown] 46 | # Finally, we will run the `main` function on the `PyQrack` backend and print the quantum register 47 | # to see the final state of the qubits after applying the QFT circuit. 48 | #
49 | # 50 | # 51 | # 52 | #
53 | 54 | 55 | # %% 56 | device = StackMemorySimulator(min_qubits=3) 57 | qreg = device.run(main) 58 | print(qreg) 59 | 60 | # %% [markdown] 61 | # we can also emit the QASM2 code for the `main` function and print it to see the QASM2 code 62 | # that corresponds to the QFT circuit. 63 | 64 | # %% 65 | from bloqade.qasm2.emit import QASM2 # noqa: E402 66 | from bloqade.qasm2.parse import pprint # noqa: E402 67 | 68 | target = QASM2() 69 | ast = target.emit(main) 70 | pprint(ast) 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/digital/cirq_interop/squin_to_cirq.md: -------------------------------------------------------------------------------- 1 | # Converting squin to Cirq 2 | 3 | You can convert a squin kernel function to a `cirq.Circuit` object. 4 | The output circuit will feature gates that most closely resemble the kernel you put in. 5 | 6 | ## Basic usage 7 | 8 | You can obtain a circuit using the `cirq_utils.emit_circuit` function. 9 | 10 | ```python 11 | from bloqade import squin, cirq_utils 12 | 13 | @squin.kernel 14 | def main(): 15 | q = squin.qalloc(2) 16 | h = squin.op.h() 17 | squin.qubit.apply(h, q[0]) 18 | cx = squin.op.cx() 19 | squin.qubit.apply(cx, q[0], q[1]) 20 | squin.broadcast.measure(q) 21 | 22 | circuit = cirq_utils.emit_circuit(main) 23 | print(circuit) 24 | ``` 25 | 26 | There is one crucial difference between a squin kernel and a cirq circuit: 27 | the qubits are defined inside a kernel, whereas for a circuit they are defined outside. 28 | 29 | The default behavior here is to emit a set of `cirq.LineQubit`, which is of the correct length. 30 | They will be sorted by their `Qid` (position) according to the order they appear in the kernel. 31 | 32 | ## Customizing qubits 33 | 34 | By default, a set of `cirq.LineQubit`s of the appropriate size is created internally, on which the resulting circuit operates. 35 | This may be undesirable sometimes, e.g. when you want to combine multiple circuits or if you want to have qubits of a different type. 36 | 37 | To allow modifications here, you can simply pass in a list of qubits (a sequence of `cirq.Qid`s) into the emit function. 38 | 39 | ```python 40 | import cirq 41 | 42 | qubits = cirq.GridQubit.rect(rows=1, cols=2) 43 | circuit = cirq_utils.emit_circuit(main, qubits=qubits) 44 | print(circuit) 45 | ``` 46 | 47 | Note, that the qubits will be used in the resulting circuit in the order they appear in `squin.qalloc` statements. 48 | 49 | !!! warning 50 | 51 | When passing in a list of qubits, you need to make sure there is sufficiently many qubits. 52 | Otherwise, you may get indexing errors. 53 | 54 | ## Limitations 55 | 56 | Please note that there are some limitations, especially regarding control flow. 57 | Using `if` statements or loops inside a kernel function may lead to errors. 58 | 59 | If you run into an issue that you think should be supported, please [report an issue on the GitHub repository](https://github.com/QuEraComputing/bloqade-circuit/issues). 60 | -------------------------------------------------------------------------------- /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 on writing digital circuits. 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/digital/index.md: -------------------------------------------------------------------------------- 1 | !!! warning "Note" 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 | The digital submodule of Bloqade, called `bloqade-circuit` defines a set of embedded domain-specific languages (eDSLs) that can be used to define digital quantum programs. 10 | These programs are intended for both simulation and to be run on hardware. This package is open source and can be found [on GitHub](https://github.com/QuEraComputing/bloqade-circuit). 11 | 12 | Please refer to the [Dialects and Kernels](./dialects_and_kernels) section of this documentation for an overview over the most important eDSLs. 13 | The infrastructure behind these compilers is built on top of [Kirin](https://queracomputing.github.io/kirin/latest/). 14 | 15 | It is easiest to learn how to use this package by checking out the [examples & tutorials section](./examples/index.md), where we show how you can build and study different quantum programs written in different DSLs. 16 | 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/`. 17 | 18 | Finally, if you want the full details on the API, please refer to the [API reference documentation](../../reference/bloqade-circuit/src/bloqade/device/). 19 | 20 | ## Installation 21 | 22 | The package comes as a submodule of Bloqade, so you can just run 23 | 24 | ``` 25 | pip install bloqade 26 | ``` 27 | 28 | in order to obtain it. 29 | 30 | Sometimes, you may want to reduce the number of dependencies, in which case you can also only install the submodule 31 | 32 | ``` 33 | pip install bloqade-circuit 34 | ``` 35 | 36 | Note, that bloqade-circuit also has some optional dependencies, which you may want to install. 37 | For example 38 | 39 | ``` 40 | pip install bloqade-circuit[cirq,qasm2,stim] 41 | ``` 42 | 43 | ## TL;DR 44 | 45 | Here's a GHZ preparation circuit with a measurement at the end written in the [`squin`](../../reference/bloqade-circuit/src/bloqade/squin/) dialect: 46 | 47 | ```python 48 | from bloqade import squin 49 | 50 | @squin.kernel 51 | def ghz(n: int): 52 | q = squin.qalloc(n) 53 | 54 | squin.gate.h(q[0]) 55 | 56 | for i in range(1, n): 57 | squin.gate.cx(q[i - 1], q[i]) 58 | 59 | return squin.broadcast.measure(q) 60 | ``` 61 | 62 | Here are [some more examples](./examples/index.md). 63 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Bloqade is compatible with Python 3.10+ and available on [PyPI](https://pypi.org/project/bloqade/). 4 | You can install it via [`pip`](https://pypi.org/project/pip/) into your environment: 5 | 6 | 7 | ```bash 8 | pip install bloqade 9 | ``` 10 | 11 | 12 | ## Bloqade and its friends 13 | 14 | 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: 15 | 16 | !!! note 17 | 18 | If you have already installed Bloqade via the instructions above, all the following subpackages are already installed with the exception of the `stim` eDSL which is currently experimental. 19 | 20 | ### `bloqade.qasm2` 21 | 22 | QASM2 and its extensions support for neutral atom quantum computing. Available via: 23 | 24 | ```bash 25 | pip install bloqade[qasm2] 26 | ``` 27 | 28 | ### `bloqade.analog` 29 | 30 | Analog quantum computing eDSL for neutral atom quantum computing (previously `bloqade-python`). Available via: 31 | 32 | ```bash 33 | pip install bloqade-analog 34 | ``` 35 | 36 | ### `bloqade.qbraid` 37 | 38 | Support of the qBraid cloud service as a runtime backend for retrieving noise models and running circuits. 39 | 40 | ```bash 41 | pip install bloqade[qbraid] 42 | ``` 43 | 44 | ### `bloqade.stim` (Experimental) 45 | 46 | Stim and its extensions support for neutral atom quantum computing. Available via: 47 | 48 | ```bash 49 | pip install bloqade[stim] 50 | ``` 51 | 52 | ## Development 53 | 54 | If you want to contribute to Bloqade, you can clone the repository from GitHub: 55 | 56 | ```bash 57 | git clone https://github.com/QuEraComputing/bloqade.git 58 | ``` 59 | 60 | We use [`uv`](https://docs.astral.sh/uv/) to manage the development environment. 61 | 62 | You can install `uv` via the following: 63 | 64 | === "Linux and macOS" 65 | 66 | ```bash 67 | curl -LsSf https://astral.sh/uv/install.sh | sh 68 | ``` 69 | 70 | === "Windows" 71 | 72 | ```cmd 73 | powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 74 | ``` 75 | 76 | Then you can install the development dependencies executing one of the following commands: 77 | 78 | ```bash 79 | # For contributing to code 80 | uv sync --group dev 81 | # For contributions to documentation 82 | uv sync --group doc 83 | # For just getting everything mentioned above 84 | uv sync --all-groups 85 | ``` 86 | 87 | Our code review requires that you pass the tests and linting checks. We recommend 88 | you install `pre-commit` to run the checks before you commit your changes. `pre-commit` 89 | is already specified as a development dependency for bloqade and once installed, 90 | you can setup `pre-commit` using the following command: 91 | 92 | ```bash 93 | pre-commit install 94 | ``` 95 | -------------------------------------------------------------------------------- /.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@v6 12 | - name: Install uv 13 | uses: astral-sh/setup-uv@v7 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@v5 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@v6 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@v6 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.2.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 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "bloqade" 3 | version = "0.32.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,stim]~=0.10.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 | 107 | [tool.ruff.lint.per-file-ignores] 108 | "docs/digital/tutorials/*.py" = ["E402", "F401"] # ignore import not at top and unused 109 | "docs/digital/examples/*.py" = ["E402"] # ignore imports not at top 110 | -------------------------------------------------------------------------------- /docs/analog/home/migration.md: -------------------------------------------------------------------------------- 1 | # Migrating to Bloqade Analog 2 | 3 | ## Introduction 4 | 5 | In order to make room for more features inside the Bloqade ecosystem, we have created a new package to take the place of the old `bloqade` package. The new package is called `bloqade-analog`. The old package `bloqade` will house a namespace package for other features such as our new Bloqade Digital package with support for circuit-based quantum computers! 6 | 7 | ## Installation 8 | 9 | You can install the package with `pip` in your Python environment of choice via: 10 | 11 | ```sh 12 | pip install bloqade-analog 13 | ``` 14 | 15 | ## Migration 16 | 17 | The new package is a drop-in replacement for the old one. You can simply replace `import bloqade` with `import bloqade.analog` or `from bloqade.analog import ...` in your code. Everything else should work as before. 18 | 19 | ## Example 20 | 21 | lets say your header of your python script looks like this: 22 | 23 | ```python 24 | from bloqade import var 25 | from bloqade.atom_arrangement import Square 26 | ... 27 | ``` 28 | You can simply replace it with: 29 | 30 | ```python 31 | from bloqade.analog import var 32 | from bloqade.analog.atom_arrangement import Square 33 | ... 34 | ``` 35 | 36 | ## Migrating old bloqade JSON files 37 | 38 | If you have old bloqade JSON files, you will not be able to directly deserialize them anymore because of the package restructuring. However, we have provided some tools to migrate those JSON files to be compatible with `bloqade-analog`. You can do this by running the following command in the command line for a one or more files: 39 | 40 | ```sh 41 | python -m bloqade.analog.migrate ... 42 | ``` 43 | With default arguments this will create a new file with the same name as the old file, but with `-analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade-analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: 44 | 45 | ```sh 46 | python -m bloqade.analog.migrate --help 47 | ``` 48 | 49 | Another option is to use the migration tool in a python script: 50 | 51 | ```python 52 | from bloqade.analog.migrate import migrate 53 | 54 | # set the indent level for the output file 55 | indent: int = ... 56 | # set to True if you want to overwrite the old file, otherwise the new file will be created with -analog appended to the end of the filename 57 | overwrite: bool = ... 58 | f 59 | or filename in ["file1.json", "file2.json", ...]: 60 | migrate(filename, indent=indent, overwrite=overwrite) 61 | ``` 62 | This will migrate all the files in the list to the new format. 63 | 64 | 65 | ## Having trouble, comments, or concerns? 66 | 67 | Please open an issue on our [GitHub](https://github.com/QuEraComputing/bloqade-analog/issues) 68 | -------------------------------------------------------------------------------- /docs/digital/examples/index.md: -------------------------------------------------------------------------------- 1 | # Tutorials on digital circuits 2 | 3 | In this section you will find a number of tutorials and examples that show how you can use the digital bloqade subpackage, `bloqade-circuit`, in order to write quantum programs. 4 | The examples are split into sub-sections featuring the different [dialects](./dialects_and_kernels) and submodules. 5 | 6 | ## General tutorials 7 | 8 |
9 | 10 | - [Circuits with Bloqade](../tutorials/circuits_with_bloqade/) 11 | 12 | --- 13 | 14 | Learn how to use `bloqade-circuit` to write your quantum programs. 15 | 16 | 17 | - [Automatic Parallelism](../tutorials/auto_parallelism/) 18 | 19 | --- 20 | 21 | Explore the benefits of parallelizing your circuits. 22 | 23 |
24 | 25 | 26 | ## Squin 27 | 28 | Squin is bloqade-circuits central dialect used to build circuits and run them on simulators and hardware. 29 | 30 |
31 | 32 | - [Deutsch-Jozsa Algorithm](../examples/squin/deutsch_squin/) 33 | 34 | --- 35 | 36 | See how you can implement the fundamental Deutsch-Jozsa algorithm with a Squin kernel function. 37 | 38 | 39 | - [GHZ state preparation and noise](../examples/squin/ghz/) 40 | 41 | --- 42 | 43 | Inject noise manually in a simple squin kernel. 44 | 45 | 46 |
47 | 48 | 49 | ## Interoperability with other SDKs 50 | 51 | While bloqade-circuit provides a number of different dialects (eDSLs), it may also be convenient to transpile circuits written using other SDKs. 52 | 53 |
54 | 55 | - [Heuristic noise models applied to GHZ state preparation](../examples/interop/noisy_ghz/) 56 | 57 | --- 58 | 59 | Learn how to apply our heuristic noise models built to work with the cirq SDK. 60 | 61 |
62 | 63 | 64 | ## QASM2 65 | 66 | One of the most central languages used to define quantum programs is QASM2. 67 | You can also write your quantum programs using the QASM2 dialect directly in bloqade-circuit. 68 | 69 | !!! warning 70 | 71 | Some of the examples below use the `qasm2.extended` dialect, which adds more advanced language features, such as control flow. 72 | However, this dialect is deprecated and we recommend using `squin` instead. 73 | 74 | 75 | 76 |
77 | 78 | - [Quantum Fourier Transform](../examples/qasm2/qft/) 79 | 80 | --- 81 | 82 | An example showing how to implement the well-known Quantum Fourier Transform (QFT). 83 | 84 | - [GHZ Preparation and Parallelism](../examples/qasm2/ghz/) 85 | 86 | --- 87 | 88 | Learn how to use parallelism to reduce the circuit (execution) depth. 89 | 90 | - [Pauli Exponentiation for Quantum Simulation](../examples/qasm2/pauli_exponentiation/) 91 | 92 | --- 93 | 94 | Simulating Hamiltonian dynamics by exponentiating Pauli operators. 95 | 96 | 97 | - [Repeat until success with STAR gadget](../examples/qasm2/repeat_until_success/) 98 | 99 | --- 100 | 101 | Here's how to implement a Z phase gate with the repeat-until-success protocol. 102 | 103 |
104 | -------------------------------------------------------------------------------- /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 | dplankensteiner: 48 | name: David Plankensteiner 49 | description: Scientific Software Engineer at QuEra Computing Inc. 50 | avatar: https://github.com/david-pl.png 51 | url: https://github.com/david-pl 52 | lmartinez: 53 | name: Luis A. Martinez 54 | description: Research Scientist at QuEra Computing Inc. 55 | avatar: https://github.com/lamq317.png 56 | url: https://github.com/lamq317 57 | tcochran: 58 | name: Tyler A Cochran 59 | description: Quantum Scientist at QuEra Computing Inc. 60 | avatar: https://ca.slack-edge.com/T011VA51XJL-U08L2D8GMCY-abadd562fee2-512 61 | url: https://github.com/tcochran-quera 62 | -------------------------------------------------------------------------------- /docs/analog/reference/standard.md: -------------------------------------------------------------------------------- 1 | 2 | # Build Workflow 3 | ```mermaid 4 | 5 | flowchart TD 6 | ProgramStart(["start"]) 7 | 8 | Geometry("Geometry or Lattice") 9 | 10 | Coupling["Coupling 11 | ----------- 12 | rydberg 13 | hyperfine"] 14 | 15 | Detuning["detuning"] 16 | Rabi["rabi"] 17 | 18 | Amplitude["amplitude"] 19 | Phase["phase"] 20 | 21 | SpaceModulation("SpatialModulation 22 | ---------------------- 23 | uniform 24 | scale 25 | location 26 | ") 27 | Waveform{"Waveform 28 | ------------ 29 | piecewise_linear 30 | piecewise_constant 31 | constant 32 | linear 33 | poly 34 | fn 35 | "} 36 | 37 | Options(["Options 38 | --------- 39 | assign 40 | batch_assign 41 | args 42 | parallelize 43 | "]) 44 | 45 | Services(["Services 46 | ---------- 47 | bloqade 48 | quera 49 | braket"]) 50 | 51 | QuEraBackends(["Backends 52 | ------------ 53 | mock 54 | cloud_mock 55 | aquila 56 | device"]) 57 | 58 | BraketBackends(["Backends 59 | ------------ 60 | aquila 61 | local_emulator"]) 62 | 63 | BloqadeBackends(["Backends 64 | ------------ 65 | python 66 | julia"]) 67 | 68 | Execution(" 69 | Execution hardware only 70 | ------------------------------- 71 | run_async() 72 | 73 | Hardware and simulation 74 | ------------------------------- 75 | run() 76 | __call__") 77 | 78 | ProgramStart -->|add_position| Geometry; 79 | Geometry --> Coupling; 80 | ProgramStart --> Coupling; 81 | 82 | Coupling --> Detuning; 83 | Coupling --> Rabi; 84 | 85 | Rabi --> Amplitude; 86 | Rabi --> Phase; 87 | 88 | Detuning --> SpaceModulation; 89 | Amplitude --> SpaceModulation; 90 | Phase --> SpaceModulation; 91 | 92 | SpaceModulation --> Waveform; 93 | 94 | Waveform --> Coupling; 95 | Waveform --> Services; 96 | Waveform --> Options; 97 | Options --> Services; 98 | 99 | Services -->|quera| QuEraBackends; 100 | Services -->|braket| BraketBackends; 101 | Services -->|bloqade| BloqadeBackends; 102 | QuEraBackends --> Execution; 103 | BraketBackends --> Execution; 104 | BloqadeBackends --> Execution; 105 | 106 | click ProgramStart "../bloqade/#bloqade.start"; 107 | click Geometry "../bloqade/atom_arrangement/"; 108 | click Coupling "../bloqade/builder/drive/"; 109 | click Detuning "../bloqade/builder/field/#bloqade.builder.field.Detuning"; 110 | click Rabi "../bloqade/builder/field/#bloqade.builder.field.Rabi"; 111 | click Amplitude "../bloqade/builder/field/#bloqade.builder.field.Amplitude"; 112 | click Phase "../bloqade/builder/field/#bloqade.builder.field.Phase"; 113 | click SpaceModulation "../bloqade/builder/spatial/"; 114 | click Waveform "../bloqade/builder/waveform/"; 115 | click Options "../bloqade/builder/pragmas/"; 116 | click Services "../bloqade/builder/backend/"; 117 | click QuEraBackends "../bloqade/builder/backend/quera/#bloqade.builder.backend.quera.QuEraDeviceRoute"; 118 | click BraketBackends "../bloqade/builder/backend/braket/#bloqade.builder.backend.braket.BraketDeviceRoute"; 119 | click BloqadeBackends "../bloqade/builder/backend/bloqade/#bloqade.builder.backend.bloqade.BloqadeBackend"; 120 | click Execution "../bloqade/ir/routine/braket/#bloqade.ir.routine.braket.BraketRoutine"; 121 | 122 | ``` 123 | -------------------------------------------------------------------------------- /docs/digital/examples/qasm2/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/analog/contributing/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Design Philosophy and Architecture 2 | 3 | Given the heterogeneous nature of the hardware we target, 4 | We have decided to use a compiler-based approach to our software 5 | stack, allowing us to target different hardware backends 6 | with the same high-level language. Below is a diagram of the 7 | software stack in Bloqade. 8 | 9 | ```mermaid 10 | graph TD 11 | Builder["Builder Representation"] 12 | PythonAST["Bloqade AST Python"] 13 | JuliaAST["Bloqade AST Julia"] 14 | 15 | EmulatorPy["Emulator IR Python"] 16 | EmulatorJL["Emulator IR Julia"] 17 | 18 | QuEra["QuEra IR"] 19 | Braket["Braket IR"] 20 | JuliaEmulator["Bloqade.jl"] 21 | PythonEmulator["Python Emulator"] 22 | 23 | Aquila["Aquila"] 24 | 25 | Builder -->|parse| PythonAST 26 | PythonAST -->|lower| EmulatorPy 27 | PythonAST -->|lower| QuEra 28 | PythonAST -->|lower| Braket 29 | PythonAST -->|transpile| JuliaAST 30 | 31 | QuEra -->|execute| Aquila 32 | Braket -->|execute| Aquila 33 | 34 | JuliaAST -->|lower| EmulatorJL 35 | EmulatorPy -->|execute| PythonEmulator 36 | EmulatorJL -->|execute| JuliaEmulator 37 | 38 | ``` 39 | 40 | ## High-Level Builder Representation 41 | 42 | When programming Bloqade using the Python API, the user constructs 43 | a representation of an analog quantum circuit. This representation 44 | is a *flattened* version of the actual analog circuit. *Flattened* 45 | means that the user input is a linear sequence of operations where 46 | the context of neighboring nodes in the sequence of instructions 47 | can determine the program tree structure. The Bloqade AST describes 48 | the actual analog circuit. 49 | 50 | ## Bloqade AST 51 | 52 | The Bloqade AST is a representation of a quantum analog circuit for 53 | neutral atom computing. It is a directed acyclic graph (DAG) with nodes 54 | for different hierarchical levels of the circuit. The base node is the 55 | `AnalogCircuit` which contains the geometry of the atoms stored as a 56 | `AtomArragment` or `ParallelRegister` objects. The other part of the 57 | circuit is the `Sequence`, which contains the waveforms that describe 58 | the drives for the Ryberg/Hyperfine transitions of 59 | each Rydberg atom. Each transition is represented by a `Pulse` including 60 | a `Field` for the drive's detuning, Rabi amplitude, and Rabi phase 61 | . A `Field` relates the spatial and temporal dependence 62 | of a drive. The spatial modulates the temporal dependence of the 63 | waveform. A DAG also describes the `Waveform` object. Finally, we 64 | have basic `Scalar` expressions as well for describing the syntax 65 | of real-valued continuous numbers. 66 | 67 | ## Bloqade Compilers and Transpilers 68 | 69 | Given a user program expressed as the Bloqade AST, we can target various 70 | backends by transforming from the Bloqade AST to other kinds of IR. 71 | For example, when submitting a task to QuEra's hardware, we transform the 72 | Bloqade AST to the IR that describes a valid program for the hardware. 73 | 74 | This process is referred to as `lowering`, which in a general sense is a 75 | transformation that takes you from one IR to another where the target IR 76 | is specialized or has a smaller syntactical structure. `Transpiling` 77 | corresponds to a transformation that takes you from 78 | one language to equivalent expressions in another. For example, we 79 | can transpile from the Bloqade AST in Python to the Bloqade AST in Julia. 80 | The generic term for both of these types of transformation in Bloqade is 81 | Code Generation. You will find various code generation implementations 82 | in various `codegen` modules. 83 | -------------------------------------------------------------------------------- /docs/analog/contributing/design-philosophy-and-architecture.md: -------------------------------------------------------------------------------- 1 | # Design Philosophy and Architecture 2 | 3 | Given the heterogeneous nature of the hardware we target, 4 | We have decided to use a compiler-based approach to our software 5 | stack, allowing us to target different hardware backends 6 | with the same high-level language. Below is a diagram of the 7 | software stack in Bloqade. 8 | 9 | ```mermaid 10 | graph TD 11 | Builder["Builder Representation"] 12 | PythonAST["Bloqade AST Python"] 13 | JuliaAST["Bloqade AST Julia"] 14 | 15 | EmulatorPy["Emulator IR Python"] 16 | EmulatorJL["Emulator IR Julia"] 17 | 18 | QuEra["QuEra IR"] 19 | Braket["Braket IR"] 20 | JuliaEmulator["Bloqade.jl"] 21 | PythonEmulator["Python Emulator"] 22 | 23 | Aquila["Aquila"] 24 | 25 | Builder -->|parse| PythonAST 26 | PythonAST -->|lower| EmulatorPy 27 | PythonAST -->|lower| QuEra 28 | PythonAST -->|lower| Braket 29 | PythonAST -->|transpile| JuliaAST 30 | 31 | QuEra -->|execute| Aquila 32 | Braket -->|execute| Aquila 33 | 34 | JuliaAST -->|lower| EmulatorJL 35 | EmulatorPy -->|execute| PythonEmulator 36 | EmulatorJL -->|execute| JuliaEmulator 37 | 38 | ``` 39 | 40 | ## High-Level Builder Representation 41 | 42 | When programming Bloqade using the Python API, the user constructs 43 | a representation of an analog quantum circuit. This representation 44 | is a *flattened* version of the actual analog circuit. *Flattened* 45 | means that the user input is a linear sequence of operations where 46 | the context of neighboring nodes in the sequence of instructions 47 | can determine the program tree structure. The Bloqade AST describes 48 | the actual analog circuit. 49 | 50 | ## Bloqade AST 51 | 52 | The Bloqade AST is a representation of a quantum analog circuit for 53 | neutral atom computing. It is a directed acyclic graph (DAG) with nodes 54 | for different hierarchical levels of the circuit. The base node is the 55 | `AnalogCircuit` which contains the geometry of the atoms stored as a 56 | `AtomArragment` or `ParallelRegister` objects. The other part of the 57 | circuit is the `Sequence`, which contains the waveforms that describe 58 | the drives for the Ryberg/Hyperfine transitions of 59 | each Rydberg atom. Each transition is represented by a `Pulse` including 60 | a `Field` for the drive's detuning, Rabi amplitude, and Rabi phase 61 | . A `Field` relates the spatial and temporal dependence 62 | of a drive. The spatial modulates the temporal dependence of the 63 | waveform. A DAG also describes the `Waveform` object. Finally, we 64 | have basic `Scalar` expressions as well for describing the syntax 65 | of real-valued continuous numbers. 66 | 67 | ## Bloqade Compilers and Transpilers 68 | 69 | Given a user program expressed as the Bloqade AST, we can target various 70 | backends by transforming from the Bloqade AST to other kinds of IR. 71 | For example, when submitting a task to QuEra's hardware, we transform the 72 | Bloqade AST to the IR that describes a valid program for the hardware. 73 | 74 | This process is referred to as `lowering`, which in a general sense is a 75 | transformation that takes you from one IR to another where the target IR 76 | is specialized or has a smaller syntactical structure. `Transpiling` 77 | corresponds to a transformation that takes you from 78 | one language to equivalent expressions in another. For example, we 79 | can transpile from the Bloqade AST in Python to the Bloqade AST in Julia. 80 | The generic term for both of these types of transformation in Bloqade is 81 | Code Generation. You will find various code generation implementations 82 | in various `codegen` modules. 83 | -------------------------------------------------------------------------------- /docs/scripts/gen_ref_nav.py: -------------------------------------------------------------------------------- 1 | """Generate the code reference pages and navigation.""" 2 | 3 | import os 4 | from pathlib import Path 5 | 6 | import mkdocs_gen_files 7 | 8 | if os.getenv("GITHUB_ACTIONS") == "true": 9 | BLOQADE_CIRCUIT_SRC_PATH = "submodules/bloqade-circuit/" 10 | BLOQADE_ANALOG_SRC_PATH = "submodules/bloqade-analog/" 11 | else: 12 | """ 13 | NOTE: we assume the following project structure when building locally: 14 | 15 | ../ 16 | ├── bloqade 17 | ├── bloqade-analog 18 | └── bloqade-circuit 19 | """ 20 | BLOQADE_CIRCUIT_SRC_PATH = "../bloqade-circuit/" 21 | BLOQADE_ANALOG_SRC_PATH = "../bloqade-analog/" 22 | 23 | 24 | skip_keywords = [ 25 | "julia", ## [KHW] skip for now since we didn't have julia codegen rdy 26 | "builder/base", ## hiding from user 27 | "builder/terminate", ## hiding from user 28 | "ir/tree_print", ## hiding from user 29 | "ir/visitor", ## hiding from user 30 | "codegen/", ## hiding from user 31 | "builder/factory", ## hiding from user 32 | "builder_old", ## deprecated from user 33 | "task_old", ## deprecated from user 34 | "visualization", ## hiding from user 35 | "submission/capabilities", ## hiding from user 36 | "submission/quera_api_client", 37 | "test/", 38 | "tests/", 39 | "test_utils", 40 | "docs/", 41 | "debug/", 42 | "squin/cirq/emit/", # NOTE: this fails when included because there is an __init__.py missing, but the files have no docs anyway and it will be moved so safe to ignore 43 | ] 44 | 45 | 46 | def make_nav(bloqade_package_name: str, BLOQADE_PACKAGE_PATH: str): 47 | """ 48 | build the mkdocstrings nav object for the given package 49 | 50 | Arguments: 51 | bloqade_package_name (str): name of the bloqade package. This must match with the mkdocs path as the generated pages are put under reference/ 52 | BLOQADE_PACKAGE_PATH (str): the path to the module. 53 | """ 54 | nav = mkdocs_gen_files.Nav() 55 | for path in sorted(Path(BLOQADE_PACKAGE_PATH).rglob("*.py")): 56 | module_path = Path(path.relative_to(BLOQADE_PACKAGE_PATH).with_suffix("")) 57 | doc_path = Path( 58 | bloqade_package_name, module_path.relative_to(".").with_suffix(".md") 59 | ) 60 | full_doc_path = Path("reference/", doc_path) 61 | 62 | iskip = False 63 | 64 | for kwrd in skip_keywords: 65 | if kwrd in str(doc_path): 66 | iskip = True 67 | break 68 | if iskip: 69 | print("[Ignore]", str(doc_path)) 70 | continue 71 | 72 | print("[>]", str(doc_path)) 73 | 74 | parts = tuple(module_path.parts) 75 | 76 | if parts[-1] == "__init__": 77 | parts = parts[:-1] 78 | doc_path = doc_path.with_name("index.md") 79 | full_doc_path = full_doc_path.with_name("index.md") 80 | elif parts[-1].startswith("_"): 81 | continue 82 | 83 | if len(parts) == 0: 84 | continue 85 | 86 | nav[parts] = doc_path.as_posix() 87 | with mkdocs_gen_files.open(full_doc_path, "w") as fd: 88 | ident = ".".join(parts[1:]) 89 | fd.write(f"::: {ident}") 90 | 91 | mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path) 92 | 93 | return nav 94 | 95 | 96 | bloqade_circuit_nav = make_nav("bloqade-circuit", BLOQADE_CIRCUIT_SRC_PATH) 97 | with mkdocs_gen_files.open("reference/SUMMARY_BLOQADE_CIRCUIT.md", "w") as nav_file: 98 | nav_file.writelines(bloqade_circuit_nav.build_literate_nav()) 99 | 100 | bloqade_analog_nav = make_nav("bloqade-analog", BLOQADE_ANALOG_SRC_PATH) 101 | with mkdocs_gen_files.open("reference/SUMMARY_BLOQADE_ANALOG.md", "w") as nav_file: 102 | nav_file.writelines(bloqade_analog_nav.build_literate_nav()) 103 | -------------------------------------------------------------------------------- /docs/digital/compilation.md: -------------------------------------------------------------------------------- 1 | # Understanding the compilation process 2 | 3 | The compilation process is divided into several stages: 4 | 5 | 1. **Lowering**: a decorator such as `qasm2.extended` takes the Python Abstract Syntax Tree (AST) and lowers it into Kirin's Intermediate Representation (IR) which follows Static Single Assignment (SSA) form. 6 | 2. **Interpretation**: when invoking a backend, such as the PyQrack simulator, the IR code is interpreted by an interpreter featuring the corresponding method tables for the runtime evaluation of the dialect statements. 7 | 3. **Target code generation**: when emitting code, several steps can be involved before the actual code emission, depending on the target. Similar to interpretation, each statement will be translated to one that fits the chosen target. For example, when emitting QASM2, the following steps occur: 8 | 1. The IR code gets aggressively inlined and all constant expressions are evaluated. 9 | 2. All loops and control flow are unrolled. 10 | 3. All compatible Python expressions (e.g `sin`, arithmetics) are translated into QASM2 expressions. 11 | 4. The QASM2 code is emitted as QASM2 AST for pretty printing. 12 | 13 | ### Progressive compilation 14 | 15 | As well as writing circuit executions, you can also progressively transform and compile that circuit. 16 | While it is possible to write your own compiler passes and optimizations - for that, please refer to the [`kirin`](https://queracomputing.github.io/kirin/latest/) documentation - `bloqade-circuit` also offers a number of different, pre-defined optimizations. 17 | 18 | !!! warning 19 | Compiler and optimization passes are currently under development. 20 | While quite a lot of them are used internally, they are not in a user-friendly state. 21 | Please proceed with caution! 22 | 23 | 24 | ## Dialect groups 25 | 26 | Bloqade provides a set of [dialects](../dialects_and_kernels/) for QASM2 and our custom extensions to model parallel gates in neutral atom architectures. The basic QASM2 functionality can be enabled via 27 | 28 | ```bash 29 | pip install bloqade[qasm2] 30 | ``` 31 | 32 | ### Extended QASM 33 | 34 | The decorator `qasm2.extended` is a group of smaller dialects: 35 | 36 | ```python 37 | extended = structural_no_opt.union( 38 | [ 39 | inline, 40 | uop, 41 | glob, 42 | noise, 43 | parallel, 44 | core, 45 | ] 46 | ) 47 | ``` 48 | 49 | where `structural_no_opt` is the base dialect group (defined in [`kirn`](https://queracomputing.github.io/kirin/latest/)) that provides the basic control flow, common Python expressions (but not all), then: 50 | 51 | - `core` provides the core QASM2 operations such as register allocation, measurement and reset. 52 | - `uop` provides the unary operations, such as standard Pauli gates, rotation gates, etc. 53 | 54 | The following dialects are specific to neutral atom quantum computing as an extension: 55 | 56 | - `glob` provides the global gates (Rydberg specific) 57 | - `noise` provides the noise channels 58 | - `parallel` provides the parallel gate support (Rydberg specific). 59 | - `inline` provides the inline QASM string 60 | 61 | ### Strict QASM2 mode 62 | 63 | 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. 64 | Note that `qasm2.main` features all standard QASM2 instructions, whereas `qasm2.gate` adds the functionality for defining custom gate subroutines. 65 | 66 | ```python 67 | @qasm2.main 68 | def main(): 69 | qasm2.h(0) 70 | qasm2.cx(0, 1) 71 | qasm2.measure(0) 72 | qasm2.measure(1) 73 | return qasm2.qreg(2) 74 | ``` 75 | 76 | which corresponds to the following QASM2 code: 77 | 78 | ```qasm 79 | OPENQASM 2.0; 80 | include "qelib1.inc"; 81 | 82 | qreg q[2]; 83 | creg c[2]; 84 | 85 | h q[0]; 86 | cx q[0], q[1]; 87 | measure q[0] -> c[0]; 88 | measure q[1] -> c[1]; 89 | ``` 90 | 91 | Note that `return` is not supported in QASM2 and are therefore omitted in the code above. 92 | -------------------------------------------------------------------------------- /docs/digital/examples/squin/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 | # flip the final (result) qubit -- every bit string is mapped to 1 45 | squin.x(q[-1]) 46 | 47 | 48 | # %% [markdown] 49 | 50 | # 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. 51 | # This means that the result will be $1$ in exactly half the cases. 52 | 53 | 54 | # %% 55 | @squin.kernel 56 | def f_balanced(q: ilist.IList[Qubit, Any]): 57 | squin.cx(q[0], q[-1]) 58 | 59 | 60 | # %% [markdown] 61 | # 62 | # Now, we define the actual algorithm as a kernel, which simply takes one of the other kernels as input. 63 | # In the end, we can infer which function was provided by looking at the resulting measurement of the result qubit. 64 | # %% 65 | @squin.kernel 66 | def deutsch_algorithm(f): 67 | q = squin.qalloc(n_qubits=n_bits + 1) 68 | squin.x(q[-1]) 69 | 70 | # broadcast for parallelism 71 | squin.broadcast.h(q) 72 | 73 | # apply the oracle function 74 | f(q) 75 | 76 | squin.broadcast.h(q[:-1]) 77 | 78 | return squin.broadcast.measure(q[:-1]) 79 | 80 | 81 | # %% [markdown] 82 | # Finally, we actually run the result. 83 | # To do so, we use the `PyQrack` simulation backend in bloqade. 84 | # 85 | # To make things a bit more interesting, we randomly select which function we are running the algorithm with. 86 | 87 | # %% 88 | sim = StackMemorySimulator(min_qubits=n_bits + 1) 89 | 90 | f_choice_idx = random.randint(0, 1) 91 | f_choice = (f_constant, f_balanced)[f_choice_idx] 92 | 93 | # result = sim.run(deutsch_algorithm, args=(f_balanced, n)) 94 | result0 = 0.0 95 | n_shots = 100 96 | for _ in range(n_shots): 97 | res = sim.run(deutsch_algorithm, args=(f_choice,)) 98 | result0 += res[0] / n_shots 99 | 100 | print( 101 | "Oh magic Deutsch-Jozsa algorithm, tell us if our function is constant or balanced:" 102 | ) 103 | print("*drumroll*") 104 | if result0 == 0: 105 | print("It's constant!") 106 | 107 | # let's make sure we actually did the right thing here 108 | assert f_choice_idx == 0 109 | else: 110 | print("It's balanced!") 111 | 112 | # let's make sure we actually did the right thing here 113 | assert f_choice_idx == 1 114 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # random 2 | job.json 3 | out.txt 4 | .DS_Store 5 | tests/data/jobs/ 6 | main.html 7 | debug/ 8 | .idea/ 9 | 10 | # Byte-compiled / optimized / DLL files 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Distribution / packaging 19 | .Python 20 | build/ 21 | develop-eggs/ 22 | dist/ 23 | downloads/ 24 | eggs/ 25 | .eggs/ 26 | lib/ 27 | lib64/ 28 | parts/ 29 | sdist/ 30 | var/ 31 | wheels/ 32 | share/python-wheels/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | MANIFEST 37 | 38 | # PyInstaller 39 | # Usually these files are written by a python script from a template 40 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 41 | *.manifest 42 | *.spec 43 | 44 | # Installer logs 45 | pip-log.txt 46 | pip-delete-this-directory.txt 47 | 48 | # Unit test / coverage reports 49 | htmlcov/ 50 | .tox/ 51 | .nox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *.cover 58 | *.py,cover 59 | .hypothesis/ 60 | .pytest_cache/ 61 | cover/ 62 | 63 | # Translations 64 | *.mo 65 | *.pot 66 | 67 | # Django stuff: 68 | *.log 69 | local_settings.py 70 | db.sqlite3 71 | db.sqlite3-journal 72 | 73 | # Flask stuff: 74 | instance/ 75 | .webassets-cache 76 | 77 | # Scrapy stuff: 78 | .scrapy 79 | 80 | # Sphinx documentation 81 | docs/_build/ 82 | 83 | # doc slides 84 | docs/slides/**/**.pdf 85 | docs/slides/build/**/**.png 86 | **/**/**.pptx 87 | 88 | # mkdocs 89 | site/ 90 | docs/reference/bloqade/ 91 | 92 | # PyBuilder 93 | .pybuilder/ 94 | target/ 95 | 96 | # Jupyter Notebook 97 | .ipynb_checkpoints 98 | 99 | # IPython 100 | profile_default/ 101 | ipython_config.py 102 | 103 | # pyenv 104 | # For a library or package, you might want to ignore these files since the code is 105 | # intended to run in multiple environments; otherwise, check them in: 106 | # .python-version 107 | 108 | # pipenv 109 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 110 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 111 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 112 | # install all needed dependencies. 113 | #Pipfile.lock 114 | 115 | # poetry 116 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 117 | # This is especially recommended for binary packages to ensure reproducibility, and is more 118 | # commonly ignored for libraries. 119 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 120 | #poetry.lock 121 | 122 | # pdm 123 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 124 | #pdm.lock 125 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 126 | # in version control. 127 | # https://pdm.fming.dev/#use-with-ide 128 | .pdm.toml 129 | 130 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 131 | __pypackages__/ 132 | 133 | # Celery stuff 134 | celerybeat-schedule 135 | celerybeat.pid 136 | 137 | # SageMath parsed files 138 | *.sage.py 139 | 140 | # Environments 141 | .env 142 | .venv* 143 | env/ 144 | venv/ 145 | ENV/ 146 | env.bak/ 147 | venv.bak/ 148 | 149 | # Spyder project settings 150 | .spyderproject 151 | .spyproject 152 | 153 | # Rope project settings 154 | .ropeproject 155 | 156 | # mkdocs documentation 157 | /site 158 | 159 | # mypy 160 | .mypy_cache/ 161 | .dmypy.json 162 | dmypy.json 163 | 164 | # Pyre type checker 165 | .pyre/ 166 | 167 | # pytype static type analyzer 168 | .pytype/ 169 | 170 | # Cython debug symbols 171 | cython_debug/ 172 | 173 | # PyCharm 174 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 175 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 176 | # and can be added to the global gitignore or merged into this file. For a more nuclear 177 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 178 | #.idea/ 179 | 180 | .vscode 181 | !.vscode/settings.json 182 | .pdm-python 183 | main.py 184 | *.ipynb 185 | *.json 186 | .ruff_cache 187 | .python-version 188 | !package.json 189 | !src/**/**/main.py 190 | -------------------------------------------------------------------------------- /docs/analog/home/gotchas.md: -------------------------------------------------------------------------------- 1 | # Bloqade *Gotchas*: Common Mistakes in Using Bloqade 2 | 3 | It is tempting when coming from different quantum SDKs and frameworks to apply the same pattern of thought to Bloqade. However, a lot of practices from those prior tools end up being anti-patterns in Bloqade. While you can use those patterns and they can still work, it ends up causing you the developer to write unnecessarily verbose, complex, and hard-to-read code as well as preventing you from reaping the full benefits of what Bloqade has to offer. 4 | 5 | This page is dedicated to cataloguing those anti-patterns and what you can do instead to maximize the benefit Bloqade can offer you. 6 | 7 | ## Redefining Lattices and Common Atom Arrangements 8 | 9 | You might be tempted to define lattice-based geometries through the following means: 10 | 11 | ```python 12 | from bloqade import start 13 | 14 | spacing = 4.0 15 | geometry = start.add_positions( 16 | [(i * spacing, j * spacing) for i in range(4) for j in range(4)] 17 | ) 18 | ``` 19 | 20 | This is quite redundant and verbose, especially considering Bloqade offers a large number of pre-defined lattices you can customize the spacing of in `bloqade.atom_arrangement`. 21 | In the code above, we're just defining a 4x4 square lattice of atoms with 4.0 micrometers of spacing between them. This can be expressed as follows 22 | 23 | ```python 24 | from bloqade.analog.atom_arrangement import Square 25 | 26 | spacing = 4.0 27 | geometry = Square(4, lattice_spacing = spacing) 28 | ``` 29 | 30 | 31 | ## Copying a Program to create New Ones 32 | 33 | Many gate-based SDKs rely on having a mutable object representing your circuit. This means if you want to build on top of some base circuit without mutating it, you have to copy it: 34 | 35 | ```python 36 | import copy 37 | 38 | base_circuit = qubits.x(0).... 39 | # make copy of base circuit 40 | custom_circuit_1 = copy(base_circuit) 41 | # build on top of copy of base circuit 42 | custom_circuit_1.x(0).z(5)... 43 | # create a new circuit by copying the base again 44 | custom_circuit_2 = copy(base_circuit) 45 | # build on top of that copy again 46 | custom_circuit_2.y(5).cz(0,2)... 47 | ``` 48 | 49 | In Bloqade Python this is unnecessary because at every step of your program an immutable object is returned which means you can save it and not have to worry about mutating any internal state. 50 | 51 | ```python 52 | from bloqade import start 53 | base_program = start.add_position((0,0)).rydberg.rabi.amplitude.uniform 54 | # Just recycle your base program! No `copy` needed! 55 | new_program_1 = base_program.constant(duration=5.0, value=5.0) 56 | new_program_2 = base_program.piecewise_linear( 57 | durations=[5.0], values = [0.0, 5.0] 58 | ) 59 | ``` 60 | 61 | ## Creating New Programs Instead of Using `.batch_assign` 62 | 63 | If you have a set of parameters you'd like to test your program on versus a single parameter, don't generate a new program for each value: 64 | 65 | ```python 66 | rabi_values = [2.0, 4.7, 6.1] 67 | programs_with_different_rabi_values = [] 68 | 69 | for rabi_value in rabi_values: 70 | program = start.add_position((0, 0)).rydberg.rabi.amplitude.uniform.constant( 71 | duration=5.0, value=rabi_value 72 | ) 73 | programs_with_different_rabi_values.append(program) 74 | 75 | results = [] 76 | 77 | for program in programs_with_different_rabi_values: 78 | result = program.bloqade.python().run(100) 79 | results.append(result) 80 | ``` 81 | 82 | Instead take advantage of the fact Bloqade has facilities *specifically designed* to make trying out multiple values in your program without needing to make individual copies via `.batch_assign`. The results are also automatically handled for you so each value you test has its own set of results, but all collected in a singular dataframe versus the above where you'd have to keep track of individual results. 83 | 84 | ```python 85 | rabi_values = [2.0, 4.7, 6.1] 86 | # place a variable for the Rabi Value and then batch assign values to it 87 | program_with_rabi_values = start.add_position( 88 | 0, 0 89 | ).rydberg.rabi.amplitude.uniform.constant(duration=5.0, value="rabi_value") 90 | program_with_assignments = program_with_rabi_values.batch_assign( 91 | rabi_value=rabi_values 92 | ) 93 | 94 | # get your results in one dataframe versus having to keep track of a 95 | # bunch of individual programs and their individual results 96 | batch = program_with_assignments.bloqade.python().run(100) 97 | results_dataframe = batch.report().dataframe 98 | ``` 99 | -------------------------------------------------------------------------------- /docs/quick_start/circuits/index.md: -------------------------------------------------------------------------------- 1 | # Digital Quantum Computing with bloqade 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 | ## Pick your frontend: choose a DSL 6 | 7 | bloqade-circuit provides a number of different [domain specific languages (DSLs)](../../digital/dialects_and_kernels/) for writing quantum programs. 8 | If you are unsure which one to choose, head over to the [DSL documentation](../../digital/dialects_and_kernels/) for an overview of all available ones. 9 | 10 | If you are looking to write a circuit, we recommend giving [SQUIN](../../digital/dialects_and_kernels/#squin) a go. 11 | Here's an example of how you would write a simple GHZ preparation circuit: 12 | 13 | ```python 14 | from bloqade import squin 15 | 16 | @squin.kernel 17 | def ghz(n: int): 18 | q = squin.qalloc(n) 19 | squin.h(q[0]) 20 | for i in range(1, n): 21 | squin.cx(q[i - 1], q[i]) 22 | ``` 23 | 24 | One of the features here is that the SQUIN DSL support control flow, such as for loops, which allows you to write your programs in a concise way. 25 | At some point, before execution on hardware, such a loop will have to be unrolled. 26 | However, you can let the compiler worry about that and use it as a high-level feature. 27 | 28 | 29 | ## Optimize your program 30 | 31 | !!! note 32 | This step is optional and you may just skip ahead to choosing your backend. 33 | 34 | When you define a program, such as the one above, it creates an intermediate representation (IR) of that program. 35 | In the above, since `ghz` is annotated with the `@squin.kernel` decorator, it is not a function, but a `Method` object that stores the IR of the GHZ program. 36 | 37 | You can run different optimizations and compiler passes on your IR in order to tailor your program to run optimally on the chosen backend. 38 | 39 | While it is possible to write your own compiler passes and optimizations - for that, please refer to the [kirin](https://queracomputing.github.io/kirin/latest/) documentation - bloqade-circuit also offers a number of different, pre-defined optimizations. 40 | 41 | !!! warning 42 | Compiler and optimization passes are currently under development. 43 | While quite a lot of them are used internally, they are not in a user-friendly state. 44 | Please skip this step for the time being. 45 | 46 | ## Pick your backend: simulation and hardware 47 | 48 | Once you have your program written and optimized to a point at which you are satisfied, it is time to think about execution. 49 | Bloqade Digital is a hardware-first SDK, which means that simulation tries to mirror execution on hardware as closely as possible. 50 | Choosing the hardware you want to run on is therefore mostly interchangeable with simulator backends. 51 | 52 | ### Simulation with PyQrack 53 | 54 | In order to simulate your quantum program, bloqade-circuit integrates with the [Qrack](https://pyqrack.readthedocs.io/en/latest/) simulator via its Python bindings. 55 | Let's run a simulation of the above GHZ program: 56 | 57 | ```python 58 | from bloqade.pyqrack import StackMemorySimulator 59 | sim = StackMemorySimulator(min_qubits=4) 60 | sim.run(ghz, args=(4,)) # need to pass in function arguments separately 61 | ``` 62 | 63 | There are also some things available in the simulator which cannot be obtained when running on hardware, such as the actual state vector of the system: 64 | 65 | ```python 66 | sim.state_vector(ghz, args=(4,)) 67 | ``` 68 | 69 | ### Hardware execution 70 | 71 | !!! note 72 | We're all very excited for this part, but we will have to wait just a bit longer for it to become available. 73 | Stay tuned! 74 | 75 | 76 | ## Further reading and examples 77 | 78 | For more details on domain specific languages available in bloqade-circuits, please refer to the [dedicated documentation section on dialects](../../digital/dialects_and_kernels/). 79 | We also recommend that you check out our [collection of examples](../../digital/examples/), where we show some more advanced usage examples. 80 | 81 | There is also some more documentation available on the [PyQrack simulation backend](../../digital/simulator_device/simulator_device.md). 82 | 83 | Finally, if you want to learn more about compilation and compiler passes, please refer to [this documentation page](../../digital/compilation.md). 84 | We also highly recommend that you have a look at the [kirin framework](https://queracomputing.github.io/kirin/latest/). 85 | -------------------------------------------------------------------------------- /docs/analog/home/background.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | ## Neutral Atom Qubits 4 | 5 | The qubits that QuEra's neutral atom computer *Aquila* and Bloqade are designed to emulate are based on *neutral atoms*. As the name implies they are atoms that are neutrally charged but are also capable of achieving a [Rydberg state](https://en.wikipedia.org/wiki/Rydberg_atom) where a single electron can be excited to an incredibly high energy level without ionizing the atom. 6 | 7 | This incredibly excited electron energy level $|r\rangle$ and its default ground state $|g\rangle$ create a two-level system where superposition can occur. For enabling interaction between two or more qubits and achieving entanglement, when the neutral atoms are in the Rydberg state a phenomenon known as the Rydberg blockade can occur where an atom in the Rydberg state prevents a neighboring atom from also being excited to the same state. 8 | 9 | For a more nuanced and in-depth 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). 10 | 11 | ## Analog vs Digital Quantum Computing 12 | 13 | There are two modes of quantum computation that [neutral atoms](#neutral-atom-qubits) are capable of: [*Analog*](#analog-mode) and [*Digital*](#digital-mode). 14 | 15 | 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) 16 | 17 | ### Analog Mode 18 | 19 | In the analog mode (supported by Bloqade and Aquila) you control your computation through the parameters of a [time-dependent Hamiltonian](#rydberg-many-body-hamiltonian) that influences all the qubits at once. There are options for [local control](#local-control) of the Hamiltonian on certain qubits however. 20 | 21 | 22 | ### Digital Mode 23 | 24 | In the Digital Mode individual or multiple groups of qubits are controlled by applying *gates* (individual unitary operations). For [neutral atoms](#neutral-atom-qubits), this digital mode can be accomplished with the introduction of hyperfine coupling, enabling a quantum state to be stored for long periods of time while also allowing for multi-qubit gates. 25 | 26 | ## Rydberg Many-Body Hamiltonian 27 | 28 | When you emulate a program in Bloqade, you are emulating the time evolution of the Rydberg many-body Hamiltonian which looks like this: 29 | 30 | $$ 31 | i \hbar \dfrac{\partial}{\partial t} | \psi \rangle = \hat{\mathcal{H}}(t) | \psi \rangle, \\ 32 | $$ 33 | 34 | $$ 35 | \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, 36 | $$ 37 | 38 | 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. 39 | 40 | ## Local Control 41 | 42 | The [Rydberg Many-Body Hamiltonian](#rydberg-many-body-hamiltonian) already implies from its subscripts that you can also have local control over your atoms. In Bloqade this local control extends to any term in the Hamiltonian while on *Aquila* this is currently restricted to the $\Delta_j$ laser detuning term. 43 | 44 | *Fields* in Bloqade give you local (single-atom) control over the many-body Rydberg Hamiltonian. 45 | 46 | They are a sum of one or more *spatial modulations*, which allows you to *scale* the amplitude of the waveform across the different sites in the system: 47 | 48 | $$ 49 | F_{i}(t) = \sum_{\alpha} C_{i}^{\alpha}f_{\alpha}(t) 50 | $$ 51 | 52 | $$ 53 | C_{i}^{\alpha} \in \mathbb{R} 54 | $$ 55 | 56 | $$ 57 | f_{\alpha}(t) \colon \mathbb{R} \to \mathbb{R} 58 | $$ 59 | 60 | The $i$-th component of the field is used to generate the *drive* at the $i$-th site. 61 | 62 | Note that the drive is only applied if the $i$-th site is filled with an atom. 63 | 64 | You build fields in Bloqade by first specifying the spatial modulation followed by the waveform 65 | it should be multiplied by. 66 | 67 | In the case of a *uniform* spatial modulation, it can be interpreted as 68 | a constant scaling factor where $C_{i}^{\alpha} = 1.0$. 69 | -------------------------------------------------------------------------------- /docs/digital/cirq_interop/cirq_to_squin.md: -------------------------------------------------------------------------------- 1 | # Converting cirq to squin 2 | 3 | If you want to obtain a squin kernel from a circuit, you can use the `load_circuit` method in the `cirq_utils` submodule. 4 | What you're effectively doing is lowering a circuit to a squin IR. 5 | This IR can then be further lowered to eventually run on hardware. 6 | 7 | ## Basic examples 8 | 9 | Here are some basic usage examples to help you get started. 10 | 11 | ```python 12 | from bloqade import squin, cirq_utils 13 | import cirq 14 | 15 | qubits = cirq.LineQubit.range(2) 16 | circuit = cirq.Circuit( 17 | cirq.H(qubits[0]), 18 | cirq.CX(qubits[0], qubits[1]), 19 | cirq.measure(qubits) 20 | ) 21 | 22 | # let's have a look 23 | print(circuit) 24 | 25 | main_loaded = cirq_utils.load_circuit(circuit, kernel_name="main_loaded") 26 | ``` 27 | 28 | The above is equivalent to writing the following kernel function yourself: 29 | 30 | ```python 31 | @squin.kernel 32 | def main(): 33 | q = squin.qalloc(2) 34 | squin.h(q[0]) 35 | squin.cx(q[0], q[1]) 36 | squin.broadcast.measure(q) 37 | ``` 38 | 39 | You can further inspect the lowered kernel as usual, e.g. by printing the IR. 40 | Let's compare the manually written version and the loaded version: 41 | 42 | ```python 43 | main.print() 44 | main_loaded.print() 45 | ``` 46 | 47 | The resulting IR is equivalent, yet the loaded is a bit longer since the automated loading can make fewer assumptions about the code. 48 | Still, you can use the kernel as any other, e.g. by calling it from another kernel or running it via a simulator. 49 | 50 | ## Noise 51 | 52 | Lowering a noisy circuit to squin is also supported. 53 | All common channels in cirq will be lowered to an equivalent noise statement in squin. 54 | 55 | ```python 56 | from bloqade import cirq_utils 57 | import cirq 58 | 59 | qubits = cirq.LineQubit.range(2) 60 | noisy_circuit = cirq.Circuit( 61 | cirq.H(qubits[0]), 62 | cirq.CX(qubits[0], qubits[1]), 63 | cirq.depolarize(p=0.01).on_each(qubits), 64 | ) 65 | 66 | # let's have a look 67 | print(noisy_circuit) 68 | 69 | noisy_kernel = cirq_utils.load_circuit(noisy_circuit) 70 | noisy_kernel.print() 71 | ``` 72 | 73 | This becomes especially useful when used together with a `cirq.NoiseModel` that automatically adds noise to a circuit via `circuit.with_noise(model)`. 74 | 75 | ## Composability of kernels 76 | 77 | You may also run into a situation, where you define a circuit that is used as part of a larger one, maybe even multiple times. 78 | In order to allow you to do something similar here, you can pass in and / or return the qubit register in a loaded kernel. 79 | Both these options are controlled by simple keyword arguments. 80 | 81 | ### Qubits as argument to the kernel function 82 | 83 | Setting `register_as_argument=True` when loading a kernel, will result in a squin kernel function that accepts (and requires) a single argument of type `IList[Qubit]`. 84 | This means you can use a loaded circuit as part of another kernel function. 85 | Check it out: 86 | 87 | ```python 88 | from bloqade import squin, cirq_utils 89 | import cirq 90 | 91 | qubits = cirq.LineQubit.range(2) 92 | circuit = cirq.Circuit( 93 | cirq.H(qubits[0]), 94 | cirq.CX(qubits[0], qubits[1]), 95 | ) 96 | 97 | sub_kernel = cirq_utils.load_circuit(circuit, register_as_argument=True, kernel_name="sub_kernel") 98 | 99 | 100 | @squin.kernel 101 | def main(): 102 | q = squin.qalloc(4) 103 | 104 | # entangle qubits 1 and 2 105 | sub_kernel([q[0], q[1]]) 106 | 107 | # entangle qubits 3 and 4 108 | sub_kernel([q[2], q[3]]) 109 | 110 | 111 | main.print() 112 | ``` 113 | 114 | Looking at the IR of the resulting kernel, you can see that there is are `invoke sub_kernel` statements present, which call the lowered circuit with the given arguments. 115 | 116 | ### Qubits as return value from the kernel 117 | 118 | Similarly to above, you may also want to return a list of qubits from a loaded kernel. 119 | Let's adapt the above to instantiate and return a pair of entangled qubits using the same circuit: 120 | 121 | ```python 122 | 123 | sub_kernel = cirq_utils.load_circuit(circuit, return_register=True, kernel_name="sub_kernel") 124 | 125 | @squin.kernel 126 | def main(): 127 | # instantiate and entangle a list of two qubits 128 | q1 = sub_kernel() 129 | 130 | # do it again, to get another set 131 | q2 = sub_kernel() 132 | 133 | # now we have 4 qubits to work with 134 | ... 135 | 136 | main.print() 137 | ``` 138 | 139 | 140 | !!! note 141 | You can also mix both options by setting `register_as_argument = True` and `return_register = True` in order to obtain a kernel function that both accepts and returns a list of qubits. 142 | 143 | 144 | ## Limitations 145 | 146 | There are some limitations when loading circuits. 147 | One, for example, is that custom gates are not supported as you can't generally know how to lower them to a squin statement. 148 | 149 | If you find a missing feature, please feel free to [open a GitHub issue](https://github.com/QuEraComputing/bloqade-circuit/issues). 150 | -------------------------------------------------------------------------------- /docs/digital/dialects_and_kernels/squin.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SQUIN 3 | --- 4 | 5 | # Structural Quantum Instructions dialect 6 | 7 | This dialect constitutes the central domain-specific language used in bloqade-circuit. 8 | It allows you define your program in terms of gates applied to qubits, adding powerful control flow, such as `for` loops. 9 | 10 | ## Squin overview 11 | 12 | The SQUIN DSL consists of three sub-groups of dialects: 13 | 14 | * `squin.qubit`, which can be used for manipulating qubits via gate applications and measurements. 15 | * `squin.gate`, which defines a set of gates that can be applied to qubits. 16 | * `squin.noise`, which defines noise channels applied to qubits. 17 | 18 | ## Standard library for gate applications 19 | 20 | Gates are exported using a standard library directly under the `squin` namespace. 21 | This allows you to write programs in a concise way. 22 | Here's a short example: 23 | 24 | ```python 25 | from bloqade import squin 26 | 27 | @squin.kernel 28 | def main(): 29 | q = squin.qalloc(2) 30 | squin.h(q[0]) 31 | squin.cx(q[0], q[1]) 32 | return squin.broadcast.measure(q) 33 | 34 | # have a look at the IR 35 | main.print() 36 | ``` 37 | 38 | The resulting IR looks like this: 39 | 40 | ![main IR](./squin-ir-1.png) 41 | 42 | As you can see, calls such as `squin.h(q[0])` are lowered as `func.invoke` statements, i.e. to function calls into the standard library of SQUIN. 43 | 44 | For a complete list of all available gates, please see the [API reference](../../../reference/bloqade-circuit/src/bloqade/squin/stdlib/simple/gate/). 45 | 46 | 47 | ## Using control flow 48 | 49 | One of the central aspects of SQUIN is that you are also able to use standard control flow such as for loops. 50 | 51 | For example, we can generalize the two-qubit GHZ kernel function from above to an arbitrary numbers of qubits: 52 | 53 | ```python 54 | from bloqade import squin 55 | 56 | @squin.kernel 57 | def ghz(n: int): 58 | q = squin.qalloc(n) 59 | 60 | squin.h(q[0]) 61 | for i in range(n - 1): 62 | squin.cx(q[i], q[i + 1]) 63 | 64 | ``` 65 | 66 | Note that the fact that gate applications are represented by `func.invoke` also shows that it's possible to call user-defined kernel functions in SQUIN! 67 | 68 | For example, we could split the above program into two steps 69 | 70 | ```python 71 | from bloqade import squin 72 | from bloqade.types import Qubit 73 | 74 | from kirin.dialects import ilist 75 | from typing import Any 76 | 77 | @squin.kernel 78 | def allocate_qubits_for_ghz(n: int) -> ilist.IList[Qubit, Any]: 79 | q = squin.qalloc(n) 80 | squin.h(q[0]) 81 | return q 82 | 83 | @squin.kernel 84 | def ghz_split(n: int): 85 | q = allocate_qubits_for_ghz(n) 86 | for i in range(n - 1): 87 | squin.cx(q[i], q[i + 1]) 88 | ``` 89 | 90 | ## Noise 91 | 92 | The squin dialect also includes noise, with a fixed set of noise channels defined. 93 | Just like gates, they are exported under the `squin` namespace. 94 | 95 | For example, we can use this to add noise into the simple kernel from before, which entangles two qubits: 96 | 97 | ```python 98 | from bloqade import squin 99 | 100 | @squin.kernel 101 | def main_noisy(): 102 | q = squin.qalloc(2) 103 | 104 | squin.h(q[0]) 105 | squin.depolarize(p=0.1, qubit=q[0]) 106 | 107 | squin.cx(q[0], q[1]) 108 | squin.depolarize2(0.05, q[0], q[1]) 109 | 110 | return squin.broadcast.measure(q) 111 | 112 | # have a look at the IR 113 | main_noisy.print() 114 | ``` 115 | 116 | The result looks like this: 117 | 118 | ![main_noisy IR](./squin-ir-2.png) 119 | 120 | Note, that you could equivalently write the depolarization error in the above as 121 | 122 | ```python 123 | dpl = squin.noise.depolarize(p=0.1) 124 | squin.qubit.apply(dpl, q[0]) 125 | ``` 126 | 127 | A full list of available noise channels can be found in the [API reference](../../../reference/bloqade-circuit/src/bloqade/squin/stdlib/simple/noise/). 128 | 129 | 130 | ## Parallelizing gate applications 131 | 132 | There is also a standard library available for broadcasting gates, i.e. applying a gate to multiple qubits in parallel. 133 | For example, the following kernel functions apply the same operations to qubits: 134 | 135 | ```python 136 | from bloqade import squin 137 | 138 | @squin.kernel 139 | def sequential(): 140 | q = squin.qalloc(2) 141 | squin.h(q[0]) 142 | squin.h(q[1]) 143 | 144 | 145 | @squin.kernel 146 | def parallel(): 147 | q = squin.qalloc(2) 148 | squin.broadcast.h(q) 149 | 150 | ``` 151 | 152 | Note that noise can also be parallelized, e.g. by calling `squin.broadcast.depolarize(0.1, q)`. 153 | 154 | See the [API reference for broadcast](../../../reference/bloqade-circuit/src/bloqade/squin/stdlib/broadcast) for all the available functionality. 155 | Note that it will be precisely the same functions as for the standard gate application, but applied to lists of qubits rather than single ones. 156 | 157 | ## See also 158 | * [Tutorial: Circuits with Bloqade](../../tutorials/circuits_with_bloqade/) 159 | * [SQUIN API reference](../../../reference/bloqade-circuit/src/bloqade/squin/) 160 | * [Examples & Tutorials](../../examples/) 161 | -------------------------------------------------------------------------------- /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/qasm2/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/digital/dialects_and_kernels/index.md: -------------------------------------------------------------------------------- 1 | # Dialects and kernels 2 | 3 | Bloqade provides a set of pre-defined domain specific languages (DSLs), with which you can write your programs and circuits. 4 | We call these DSLs *dialects*. 5 | For a list of available dialects [see blow](#available-dialects). 6 | 7 | Once you have defined your kernel, you can inspect their Intermediate Representation (IR), apply different optimizations using compiler passes, or run them on a [(simulator) device](../simulator_device/simulator_device.md). 8 | 9 | !!! info "Kernels & dialects in a nutshell" 10 | A **kernel** function is a piece of code that runs on specialized hardware such as a quantum computer or a GPU. 11 | 12 | A **dialect** is a domain-specific language (DSL) with which you can write such a kernel. 13 | Each dialect comes with a specific set of statements and instructions you can use in order to write your program. 14 | 15 | 16 | When running code that targets a specialized execution environment, there are typically several layers involved. 17 | At the surface, the programmer writes functions in a syntax that may resemble a host language (e.g., Python), but is actually expressed in a dialect — a domain-specific variant with its own semantics. 18 | A decorator marks these functions so they can be intercepted before normal host-language execution. 19 | All dialects can be used by decorating a function. 20 | 21 | !!! info "Primer on Python decorators" 22 | A decorator in Python is simply a function (or any callable really) that takes in another function as argument and returns yet another function (callable). 23 | Usually, the returned function will be a modified version of the input. 24 | Decorators are used with the `@` syntax. 25 | 26 | 27 | Instead of running directly, the kernel function body is parsed and translated (lowered) into an intermediate representation (IR). 28 | This IR can be manipulated (e.g. to perform optimizations) and can later be executed by an interpreter that understands the dialect's semantics. 29 | The interpreter uses an internal instruction set to execute the code on the intended backend, which may be a simulator, virtual machine, or physical device. 30 | This separation lets developers write high-level, expressive code while the interpreter ensures it runs correctly in the target environment. 31 | [QuEra's Kirin](https://queracomputing.github.io/kirin/latest/) provides the infrastructure that allows us to define custom dialects tailored towards the needs of programming neutral atom quantum computers in Bloqade 32 | While the dialects are not Python syntax, Kirin still uses the Python interpreter to execute the code. 33 | 34 | 35 | !!! warning "Note" 36 | It is important to understand that when you are writing a kernel function in a dialect you are generally **not writing Python** code, even though it looks a lot like it. 37 | Therefore, kernel functions are not (usually) directly callable. 38 | Think of this as trying to execute another programming language with the Python interpreter: of course, that will error. 39 | 40 | 41 | # Available dialects 42 | 43 | Bloqade offers a few different dialects with which you can write your programs. 44 | All dialects have some advantages for particular applications. 45 | 46 | If you are unsure which dialect best suits your needs, have a look at the high-level overview of the (non-exhaustive) list of use cases below. 47 | Also, we recommend having a look at [the Structural QUantum INstructions (SQUIN) dialect](./squin.md) as it is the most general purpose dialect available and is centrally used in the compilation pipeline. 48 | 49 | While the documentation in this section provides some information on the background and a high-level overview, it is also often convenient to learn from examples. 50 | Have a look at the (growing) [examples collection](../examples/), where you can find different implementations of quantum programs using different dialects. 51 | 52 | 53 | ## [squin](./squin.md) 54 | 55 | This is the central dialect of bloqade-circuit, with which you can write your quantum programs. 56 | Rather than just defining circuits in terms of gates and qubits, this dialect also makes it possible to use control flow. 57 | Have a look at [the dedicated documentation page](./squin.md) and the corresponding [API reference](../../reference/bloqade-circuit/src/bloqade/squin/). 58 | 59 | **Use cases**: 60 | 61 | * Writing a program that represents a circuit. 62 | * If you require control flow (loops and if-statements, ...) and composability (function definitions, recursion, ...). 63 | * Simulation including noise. 64 | 65 | 66 | ## [qasm2](./qasm2.md) 67 | 68 | There are a number of dialects with which you can write kernels that represent programs in the Quantum Assembly Language (QASM2). 69 | More details can be found [here](./qasm2.md). 70 | Also, have a look at the full [qasm2 API reference](../../reference/bloqade-circuit/src/bloqade/qasm2/) 71 | 72 | **Use cases**: 73 | 74 | * Write circuits using the QASM2 standard. 75 | * Composability with other tools that integrate with QASM2, but not with bloqade directly. 76 | * Control flow via the extended dialect (not always compatible with native QASM2). 77 | 78 | 79 | ## [stim](./stim.md) 80 | 81 | For quantum error correction applications, you may want to use this dialect. 82 | See this [documentation page](./stim.md) for more details. 83 | The full API documentation is [available here](../../reference/bloqade-circuit/src/bloqade/stim/). 84 | 85 | **Use cases**: 86 | 87 | * Quantum error correction. 88 | * Stabilizer codes. 89 | -------------------------------------------------------------------------------- /docs/digital/examples/qasm2/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]: 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]: 85 | qasm2.x(ancilla) 86 | else: 87 | ancilla = z_phase_gate_recursive(ancilla, 2 * theta) 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 not creg[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/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 Rubidium 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 | where $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 | -------------------------------------------------------------------------------- /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 | - Bloqade Digital: 18 | - quick_start/circuits/index.md 19 | - Bloqade Analog: 20 | - quick_start/analog/index.md 21 | - Contributing: contrib.md 22 | - Bloqade Digital: 23 | - digital/index.md 24 | - Domain specific languages: digital/dialects_and_kernels/ 25 | - Compilation process: digital/compilation.md 26 | - Simulation: digital/simulator_device/ 27 | - Interoperability with Cirq: digital/cirq_interop/ 28 | - Digital Tutorials: 29 | - digital/examples/index.md 30 | - Tutorials: 31 | - Circuits with Bloqade: "digital/tutorials/circuits_with_bloqade.py" 32 | - Automatic parallelism: "digital/tutorials/auto_parallelism.py" 33 | - QASM2 examples: 34 | - Quantum Fourier Transform: "digital/examples/qasm2/qft.py" 35 | - GHZ state preparation: "digital/examples/qasm2/ghz.py" 36 | - Pauli Exponential: "digital/examples/qasm2/pauli_exponentiation.py" 37 | - Repeat Until Success: "digital/examples/qasm2/repeat_until_success.py" 38 | - QAOA: "digital/examples/qasm2/qaoa.py" 39 | - Squin dialect examples: 40 | - Deutsch-Jozsa Algorithm: digital/examples/squin/deutsch_squin.py 41 | - GHZ state preparation with noise: digital/examples/squin/ghz.py 42 | - Integration with other SDKs: 43 | - Noisy GHZ states with cirq: "digital/examples/interop/noisy_ghz.py" 44 | - Bloqade Analog: 45 | - analog/index.md 46 | - Migration Guide to Bloqade Analog: analog/home/migration.md 47 | - Quickstart: analog/home/quick_start.md 48 | - Background: analog/home/background.md 49 | - Gotchas: analog/home/gotchas.md 50 | - Contributing: analog/contributing/ 51 | - Builder: 52 | - analog/reference/overview.md 53 | - analog/reference/standard.md 54 | - Hardware Reference: analog/reference/hardware-capabilities.md 55 | - Analog Tutorials: 'https://queracomputing.github.io/bloqade-analog-examples/dev/' 56 | - qBook: 'https://qbook.quera.com/' 57 | - API Reference: 58 | - 'reference/index.md' 59 | - Bloqade Digital: 'reference/bloqade-circuit/src/bloqade/' 60 | - Bloqade Analog: 'reference/bloqade-analog/src/bloqade/analog/' 61 | - Blog: 62 | - blog/index.md 63 | - QuEra Computing: 'https://quera.com' 64 | 65 | theme: 66 | name: material 67 | favicon: assets/favicon.ico 68 | logo: assets/logo-dark.svg 69 | font: 70 | text: Lato 71 | palette: 72 | - scheme: default 73 | primary: custom 74 | accent: custom 75 | toggle: 76 | icon: material/brightness-7 77 | name: Switch to dark mode 78 | - scheme: slate 79 | primary: custom 80 | accent: custom 81 | toggle: 82 | icon: material/brightness-4 83 | name: Switch to light mode 84 | features: 85 | - announce.dismiss 86 | - content.action.view 87 | - content.action.edit 88 | - content.code.annotate 89 | - content.code.copy 90 | # - content.tabs.link 91 | - content.tooltips 92 | # - header.autohide 93 | # - navigation.expand 94 | - navigation.indexes 95 | # - navigation.instant 96 | # - navigation.prune 97 | - navigation.sections 98 | - navigation.tabs 99 | # - navigation.tabs.sticky 100 | - navigation.top 101 | - navigation.tracking 102 | - navigation.footer 103 | - search.highlight 104 | - search.suggest 105 | - toc.follow 106 | 107 | plugins: 108 | - autorefs 109 | - mkdocstrings: 110 | handlers: 111 | python: 112 | options: 113 | show_if_no_docstring: false 114 | separate_signature: true 115 | merge_init_into_class: true 116 | extensions: 117 | - griffe_inherited_docstrings 118 | show_inheritance_diagram: true 119 | show_signature_annotations: true 120 | show_symbol_type_heading: true 121 | show_symbol_type_toc: true 122 | docstring_options: 123 | ignore_init_summary: true 124 | - search: 125 | separator: '[\s\-,:!=\[\: )"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' 126 | - gen-files: 127 | scripts: 128 | - docs/scripts/gen_ref_nav.py 129 | - literate-nav: 130 | nav_file: SUMMARY_BLOQADE_CIRCUIT.md 131 | - minify: 132 | minify_html: false 133 | - blog: 134 | post_excerpt_max_authors: 5 135 | - mkdocs-jupyter: 136 | execute: true 137 | allow_errors: false 138 | ignore: ["scripts/*", "digital/examples/qasm2/*"] 139 | - mike 140 | 141 | extra_javascript: 142 | - javascripts/katex.js 143 | - https://unpkg.com/katex@0/dist/katex.min.js 144 | - https://unpkg.com/katex@0/dist/contrib/auto-render.min.js 145 | 146 | extra_css: 147 | - stylesheets/extra.css 148 | - https://unpkg.com/katex@0/dist/katex.min.css 149 | 150 | markdown_extensions: 151 | - abbr 152 | - admonition 153 | - attr_list 154 | - def_list 155 | - footnotes 156 | - md_in_html 157 | - pymdownx.superfences: 158 | custom_fences: 159 | - name: mermaid 160 | class: mermaid 161 | format: !!python/name:pymdownx.superfences.fence_code_format 162 | - pymdownx.caret 163 | - pymdownx.mark 164 | - pymdownx.tilde 165 | - pymdownx.tabbed: 166 | alternate_style: true 167 | - pymdownx.arithmatex: 168 | generic: true 169 | - pymdownx.emoji: 170 | emoji_index: !!python/name:material.extensions.emoji.twemoji 171 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 172 | 173 | copyright: Copyright © 2025 Bloqade contributors 174 | 175 | extra: 176 | version: 177 | provider: mike 178 | social: 179 | - icon: simple/x 180 | link: https://x.com/QueraComputing 181 | - icon: fontawesome/brands/linkedin 182 | link: https://www.linkedin.com/company/quera-computing-inc/ 183 | -------------------------------------------------------------------------------- /docs/analog/reference/hardware-capabilities.md: -------------------------------------------------------------------------------- 1 | # Hardware Capabilities 2 | 3 | During program development, it can be quite handy to know what true hardware capabilities are and incorporate that information programmatically. Bloqade offers the ability to do this via `get_capabilities()`. 4 | 5 | ## Programmatic Access 6 | 7 | `get_capabilities()` (importable directly from `bloqade`) returns a `QuEraCapabilities` object. This object contains all the hardware constraints in [`Decimal`](https://docs.python.org/3/library/decimal.html) format for the *Aquila* machine, our publicly-accessible QPU on AWS Braket. 8 | 9 | An example of using `get_capabilities()` is presented below: 10 | 11 | ```python 12 | from bloqade import get_capabilities, piecewise_linear 13 | 14 | # get capabilities for Aquila 15 | aquila_capabilities = get_capabilities() 16 | 17 | # obtain maximum Rabi frequency as Decimal 18 | max_rabi = aquila_capabilities.capabilities.rydberg.global_.rabi_frequency_max 19 | 20 | # use that value in constructing a neat Rabi waveform 21 | rabi_wf = piecewise_linear(durations = [0.5, 1.0, 0.5], values = [0, max_rabi, max_rabi, 0]) 22 | ``` 23 | 24 | The attribute names for each value have been provided below but will require you to provide the proper prefix like in the example above (e.g. the maximum number of qubits lives under the `number_qubits_max` attribute which can be navigated to via `*your_QuEra_Capabilities_Object*.lattice.number_qubits_max`). 25 | 26 | 27 | ## *Aquila* Capabilities 28 | 29 | ### Task 30 | 31 | * Use prefix `your_capabilities_object.capabilities.task` for: 32 | * minimum number of shots 33 | * maximum number of shots 34 | 35 | | Capability | Attribute | Value | 36 | |:------------------------|:-------------------|:------| 37 | | Minimum Number of Shots | `number_shots_min` | 1 | 38 | | Maximum Number of Shots | `number_shots_max` | 1000 | 39 | 40 | ### Lattice Geometry 41 | 42 | * Use prefix `your_capabilities_object.capabilities.lattice` for: 43 | * maximum number of qubits 44 | * Use prefix `your_capabilities_object.capabilities.lattice.area` for: 45 | * maximum lattice area width 46 | * maximum lattice area height 47 | * Use prefix `your_capabilities_object.capabilities.lattice.geometry` for: 48 | * maximum number of sites 49 | * position resolution 50 | * minimum radial spacing 51 | * minimum vertical spacing 52 | 53 | | Capability | Attribute | Value | 54 | |:----------------------------------------|:-----------------------|:--------| 55 | | Maximum Number of Qubits | `number_qubits_max` | 256 | 56 | | Maximum Lattice Area Width | `width` | 75.0 µm | 57 | | Maximum Lattice Area Height | `height` | 76.0 µm | 58 | | Minimum Radial Spacing between Qubits | `spacing_radial_min` | 4.0 µm | 59 | | Minimum Vertical Spacing between Qubits | `spacing_vertical_min` | 4.0 µm | 60 | | Position Resolution | `position_resolution` | 0.1 µm | 61 | | Maximum Number of Sites | `number_sites_max` | 256 | 62 | 63 | ### Global Rydberg Values 64 | 65 | * Use prefix `your_capabilities_object.capabilities.rydberg` for: 66 | * C6 Coefficient 67 | * Use prefix `your_capabilities_object.capabilities.rydberg.global_` for: 68 | * Everything else related to global (applied to all atom) capabilities 69 | 70 | | Capability | Attribute | Value | 71 | |:---------------------------------|:-------------------------------|:----------------------| 72 | | Rydberg Interaction Constant | `c6_coefficient` | 5.42×10⁶ rad/μs × µm⁶ | 73 | | Minimum Rabi Frequency | `rabi_frequency_min` | 0.00 rad/μs | 74 | | Maximum Rabi Frequency | `rabi_frequency_max` | 15.8 rad/μs | 75 | | Rabi Frequency Resolution | `rabi_frequency_resolution` | 0.0004 rad/μs | 76 | | Maximum Rabi Frequency Slew Rate | `rabi_frequency_slew_rate_max` | 250.0 rad/µs² | 77 | | Minimum Detuning | `detuning_min` | -125.0 rad/μs | 78 | | Maximum Detuning | `detuning_max` | 125.0 rad/μs | 79 | | Detuning Resolution | `detuning_resolution` | 2.0×10⁻⁷ rad/μs | 80 | | Maximum Detuning Slew Rate | `detuning_slew_rate_max` | 2500.0 rad/µs² | 81 | | Minimum Phase | `phase_min` | -99.0 rad | 82 | | Maximum Phase | `phase_max` | 99.0 rad | 83 | | Phase Resolution | `phase_resolution` | 5.0×10⁻⁷ rad | 84 | | Minimum Time | `time_min` | 0.0 µs | 85 | | Maximum Time | `time_max` | 4.0 µs | 86 | | Time Resolution | `time_resolution` | 0.001 µs | 87 | | Minimum Δt | `time_delta_min` | 0.05 µs | 88 | 89 | ### Local Detuning Values 90 | 91 | * Use prefix `your_capabilities_object.capabilities.rydberg.local` for the following values: 92 | 93 | | Capability | Attribute | Value | 94 | |:---------------------------------------|:------------------------------|:---------------| 95 | | Maximum Detuning | `detuning_max` | 125.0 rad/μs | 96 | | Minimum Detuning | `detuning_min` | 0 rad/μs | 97 | | Maximum Detuning Slew Rate | `detuning_slew_rate_max` | 1256.0 rad/µs² | 98 | | Maximum Number of Local Detuning Sites | `number_local_detuning_sites` | 200 | 99 | | Maximum Site Coefficient | `site_coefficient_max` | 1.0 | 100 | | Minimum Site Coefficient | `site_ceofficient_min` | 0.0 | 101 | | Minimum Radial Spacing | `spacing_radial_min` | 5 µm | 102 | | Minimum Δt | `time_delta_min` | 0.05 μs | 103 | | Time Resolution | `time_resolution` | 0.001 µs | 104 | -------------------------------------------------------------------------------- /docs/digital/examples/qasm2/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 kirin.dialects import ilist 15 | 16 | from bloqade import qasm2 17 | 18 | # %% [markdown] 19 | # ## Simple Linear Depth Implementation of a GHZ State Preparation Circuit 20 | # 21 | # A simple GHZ state preparation circuit can be built with $N - 1$ CX gates and $1$ H gate. 22 | # This gives the circuit an execution depth of $N$. 23 | 24 | #
25 | # 26 | # 27 | # 28 | #
29 | 30 | 31 | # %% 32 | def ghz_linear(n: int): 33 | n_qubits = int(2**n) 34 | 35 | @qasm2.extended 36 | def ghz_linear_program(): 37 | 38 | qreg = qasm2.qreg(n_qubits) 39 | # Apply a Hadamard on the first qubit 40 | qasm2.h(qreg[0]) 41 | # Create a cascading sequence of CX gates 42 | # necessary for quantum computers that 43 | # only have nearest-neighbor connectivity between qubits 44 | for i in range(1, n_qubits): 45 | qasm2.cx(qreg[i - 1], qreg[i]) 46 | 47 | return ghz_linear_program 48 | 49 | 50 | # %% [markdown] 51 | # ## Log-depth Implementation of a GHZ State Preparation Circuit 52 | # 53 | # Let's take a look how we can rewrite the circuit to take advantage of QuEra's hardware capabilities. 54 | # We can achieve log(N) circuit depth by [rearranging the CX gates (see *Mooney, White, Hill, Hollenberg* - 2021)](https://arxiv.org/abs/2101.08946). 55 | # 56 | 57 | # %% [markdown] 58 | #
59 | #

Circuit vs. Execution Depth

60 | #

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

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

Using Closures to Capture Global Variables

151 | #

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

158 | #
159 | 160 | # %% 161 | target = qasm2.emit.QASM2( 162 | allow_parallel=True, 163 | ) 164 | ast = target.emit(ghz_log_simd(4)) 165 | qasm2.parse.pprint(ast) 166 | -------------------------------------------------------------------------------- /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/qasm2/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 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/digital/dialects_and_kernels/qasm2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: QASM2 3 | --- 4 | 5 | # Open Quantum Assembly Language and beyond 6 | 7 | We have chosen to closely mirror the semantics of the Open Quantum Assembly Language (QASM2) in bloqade-circuits. 8 | For details on the language, see the [specification](https://arxiv.org/abs/1707.03429). 9 | 10 | ## qasm2.main 11 | 12 | This dialect allows you to write native QASM2 programs, with all its features and restricitions. 13 | As such, it includes definitions for gates, measurements and registers (quantum and classical), which are part of the QASM2 specification. 14 | 15 | Here's an example kernel 16 | 17 | ```python 18 | from bloqade import qasm2 19 | 20 | @qasm2.main 21 | def main(): 22 | q = qasm2.qreg(2) 23 | qasm2.h(q[0]) 24 | qasm2.cx(q[0], q[1]) 25 | 26 | c = qasm2.creg(2) 27 | qasm2.measure(q, c) 28 | return c 29 | ``` 30 | 31 | You can also look at the QASM2 program this kernel represents by emitting QASM2 code from it: 32 | 33 | ```python 34 | from bloqade.qasm2.emit import QASM2 35 | from bloqade.qasm2.parse import pprint 36 | 37 | 38 | target = QASM2() 39 | qasm2_program = target.emit(main) 40 | pprint(qasm2_program) 41 | ``` 42 | 43 | 44 | ## qasm2.extended 45 | 46 | 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. 47 | 48 | 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. 49 | 50 | Indeed, `bloqade-circuit` 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. 51 | 52 | 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 53 | 54 | ```python 55 | from bloqade import qasm2 56 | from kirin.dialects import ilist 57 | from typing import Any 58 | 59 | @qasm2.extended 60 | def parallel_cz(controls: ilist.IList[qasm2.Qubit, Any], targets: ilist.IList[qasm2.Qubit, Any]): 61 | for ctr in range(len(controls)): 62 | qasm2.cz(ctrl=controls[0],qarg=targets[1]) 63 | ``` 64 | 65 | or equivalently use a SIMD (Single Instruction Multiple Data)-like instruction to explicitly flag the parallelism 66 | 67 | ```python 68 | @qasm2.extended 69 | def simd_cz(controls: ilist.IList[qasm2.Qubit, Any], targets: ilist.IList[qasm2.Qubit, Any]): 70 | qasm2.parallel.cz(ctrls=controls,qargs=targets) 71 | ``` 72 | 73 | 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. 74 | 75 | !!! warning 76 | Since `qasm2.extended` has more advanced features that QASM2 in general, it is not always possible to emit a valid QASM2 program from a `qasm2.extended` kernel. 77 | You have to make sure that the control flow is simple enough it can be unrolled. See below for an example of such a case. 78 | Alternatively, a sure-fire, but restrictive, way is to stick to writing your kernel using `qasm2.main`. 79 | 80 | 81 | ### Quick Example 82 | 83 | You can program kernels and quantum programs using the `qasm2.extended` decorator, such as the following Quantum Fourier Transform (QFT) circuit: 84 | 85 | ```python 86 | import math 87 | from bloqade import qasm2 88 | 89 | @qasm2.extended 90 | def qft(qreg: qasm2.QReg, n: int, k: int): 91 | if k == n: 92 | return qreg 93 | 94 | qasm2.h(qreg[k]) 95 | for i in range(k + 1, n): 96 | qasm2.cu1(qreg[i], qreg[k], math.pi / 2**i) 97 | qft(qreg, n, k + 1) # recursion 98 | return qreg 99 | 100 | qft.print() 101 | ``` 102 | 103 | While the syntax is similar to Python, the `qasm2.extended` decorator actually compiles the `qft` function 104 | 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. 105 | 106 | You can inspect the initial IR code by calling the pretty printer: 107 | 108 | ```python 109 | qft.print() 110 | ``` 111 | 112 | ![QFT IR](qft-pprint.png) 113 | 114 | We can also emit QASM2 code from it. 115 | Note that native QASM2 does not support arguments or return values. 116 | Therefore, we wrap the `qft` kernel from above in another one, that simply invokes `qft` for a specific set of arguments. 117 | Then, we emit this new kernel as a QASM2 program. 118 | 119 | ```python 120 | from bloqade.qasm2.emit import QASM2 # the QASM2 target 121 | from bloqade.qasm2.parse import pprint # the QASM2 pretty printer 122 | 123 | # NOTE: we wrap the qft kernel calling it with a set of arguments 124 | @qasm2.extended 125 | def main(): 126 | n = 3 127 | q = qasm2.qreg(n) 128 | qft(q, n, 0) 129 | 130 | target = QASM2() 131 | ast = target.emit(main) 132 | pprint(ast) 133 | ``` 134 | 135 | ![QFT QASM2](qft-qasm2.png) 136 | 137 | 138 | ## Noise 139 | 140 | You can represent different noise processes in your QASM2 kernel. 141 | As of now, there are essentially two different noise channels: 142 | 143 | * A Pauli noise channel, which can represent different types of decoherence. 144 | * An atomic loss channel, which can be used to model effects of losing a qubit during the execution of a program. 145 | 146 | Usually, you don't want to write noise statements directly. 147 | Instead, use a [NoisePass][bloqade.qasm2.passes.NoisePass] in order to inject noise statements automatically according to a specific noise model. 148 | 149 | !!! note 150 | Only the `qasm2.extended` dialect supports noise. 151 | 152 | For example, you may want to do something like this: 153 | 154 | ```python 155 | from bloqade import qasm2 156 | from bloqade.qasm2.passes import NoisePass 157 | 158 | @qasm2.extended 159 | def main(): 160 | n = 2 161 | q = qasm2.qreg(n) 162 | 163 | for i in range(n): 164 | qasm2.h(q[i]) 165 | 166 | qasm2.cx(q[0], q[1]) 167 | c = qasm2.creg(n) 168 | qasm2.measure(q, c) 169 | return c 170 | 171 | # Define the noise pass you want to use 172 | noise_pass = NoisePass(main.dialects) # just use the default noise model for now 173 | 174 | # Inject the noise - note that the main method will be updated in-place 175 | noise_pass(main) 176 | 177 | # Look at the IR and all the glorious noise in there 178 | main.print() 179 | ``` 180 | -------------------------------------------------------------------------------- /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.arange(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 phenomena/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/digital/examples/qasm2/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/squin/ghz.py: -------------------------------------------------------------------------------- 1 | # --- 2 | # jupyter: 3 | # jupytext: 4 | # cell_metadata_filter: -all 5 | # custom_cell_magics: kql 6 | # text_representation: 7 | # extension: .py 8 | # format_name: percent 9 | # format_version: '1.3' 10 | # jupytext_version: 1.17.2 11 | # kernelspec: 12 | # display_name: kirin-workspace (3.12.10) 13 | # language: python 14 | # name: python3 15 | # --- 16 | 17 | # %% [markdown] 18 | # # GHZ State Preparation with Squin 19 | # 20 | # In this example, we will show (yet again) how to implement a program that prepares a GHZ state. 21 | # We will do so with a simple linear algorithm and show how to manually insert noise. 22 | 23 | # %% [markdown] 24 | # The circuit we will implement is displayed below: 25 | # 26 | # ![GHZ linear circuit](../../ghz_linear_circuit.svg) 27 | 28 | # %% [markdown] 29 | # Let's start by importing Squin and writing our circuit for an arbitrary number of qubits. 30 | 31 | # %% 32 | from bloqade.pyqrack import StackMemorySimulator # we'll need that later 33 | 34 | from bloqade import squin 35 | 36 | 37 | @squin.kernel 38 | def ghz_linear(n: int): 39 | q = squin.qalloc(n) 40 | squin.h(q[0]) 41 | for i in range(1, n): 42 | squin.cx(q[i - 1], q[i]) 43 | 44 | 45 | # ghz_linear.print() 46 | 47 | # %% [markdown] 48 | # As you can see, writing basic circuits in squin is rather straightforward. 49 | 50 | 51 | # %% [markdown] 52 | # ## Simulating the kernel 53 | # 54 | # You can simulate a kernel such as the above using bloqade's PyQrack backend. 55 | # 56 | # There are two basic simulators, that act like "devices" that you run your program on: 57 | # 58 | # * The `StackMemorySimulator`, which initializes its memory with a fixed number of qubits. The number is either set via the `min_qubits` argument or inferred automatically. Note, that automatic inference is not always possible in which case you will be required to set the argument accordingly. 59 | # * The `DynamicMemorySimulator`, which, as the name suggests, allocates memory as required throughout the circuit. Generally, you should prefer the `StackMemorySimulator` over this one unless the number of qubits can only be known at runtime. 60 | # 61 | # Let's go ahead and use the `StackMemorySimulator` together with a fixed number of qubits to simulate our GHZ preparation program from above. 62 | 63 | # %% 64 | sim = StackMemorySimulator(min_qubits=2) 65 | result = sim.run(ghz_linear, args=(2,)) 66 | print(result) 67 | 68 | # %% [markdown] 69 | # As you can see, the result of our simulation is `None`. 70 | # That is because we are not returning anything from the kernel function. 71 | # 72 | # Note, how we're passing in the arguments of the kernel function as a separate tuple in the call to `run`. 73 | # This signature is required since `ghz_linear(2)` would not return another kernel function, but rather attempt to run the kernel function as a Python function. 74 | # As the function is written in squin rather than Python, this would fail. 75 | # To provide a little more detail here: the `PyQrack` backend in bloqade-circuit actually has its own method table which tells it how to interpret the statements encountered in the squin kernel function. 76 | # 77 | # Since we are only simulating the circuit, however, we are able to fetch information that would otherwise not be attainable. 78 | # For example, you can obtain the state vector from the simulator: 79 | 80 | # %% 81 | print(sim.state_vector(ghz_linear, args=(2,))) 82 | 83 | 84 | # %% [markdown] 85 | # Looking at the output, we can see that we indeed prepared a two-qubit GHZ state (up to a global phase). 86 | # 87 | # Note, that you can also add a return value to the kernel, which is then returned by `sim.run`. 88 | # Again, this is not generally possible when running on hardware, but only during simulation. 89 | # 90 | # A realistic kernel function will return (a list of) measurement results. 91 | # That is precisely what we will do in the following. 92 | 93 | # %% [markdown] 94 | # ## Inserting noise 95 | # 96 | # The above is rather basic, so let's try to do something that is a little more interesting. 97 | # Let's write the same program as before, but now we assume that noise processes occur whenever a gate is applied. 98 | # 99 | # We will make use of Squin's noise submodule in order to do that. 100 | # 101 | # Our "noise model" will be quite simple: 102 | # * Whenever a single-qubit gate is applied, that qubit undergoes a depolarization error with probability `p_single`. 103 | # * Whenever a two-qubit (controlled) gate is applied, both qubits undergo a joint depolarization error with probability `p_paired`. 104 | # 105 | # Note, that a depolarization error with probability $p$ on a single qubit means that randomly chosen Pauli operators (one of $X, Y, Z$) is applied to the qubit with probability $p$. 106 | # Similarly, a two-qubit depolarization error applies one of the 15 operators $IX, IY, IZ, XI, XX, ...$ with a given probability. 107 | 108 | 109 | # %% 110 | @squin.kernel 111 | def noisy_linear_ghz(n: int, p_single: float, p_paired: float): 112 | q = squin.qalloc(n) 113 | 114 | squin.h(q[0]) 115 | squin.depolarize(p_single, q[0]) 116 | 117 | for i in range(1, n): 118 | squin.cx(q[i - 1], q[i]) 119 | squin.depolarize2(p_paired, q[i - 1], q[i]) 120 | 121 | return squin.broadcast.measure(q) 122 | 123 | 124 | # %% 125 | # noisy_linear_ghz.print() 126 | 127 | # %% [markdown] 128 | #
129 | #

Noise operators

130 | #

131 | # As opposed to standard gates, there is no standard library for noise statements as of now. 132 | # While we plan to add that in the future, also note how it can be convenient to separate the operator 133 | # from the gate application: we define the paired noise operator only once and apply it to different 134 | # pairs of qubits in the loop. 135 | #

136 | #
137 | 138 | # %% [markdown] 139 | # This kernel function can be simulated in the exact same way as before. 140 | # The only difference is that we now need to provide additional arguments for the noise probabilities. 141 | 142 | # %% 143 | result = sim.run(noisy_linear_ghz, args=(2, 1e-2, 2e-2)) 144 | print(result) 145 | 146 | # %% [markdown] 147 | # Now that we actually return something, we also obtain a result from running the simulation. 148 | # This result is just a list of measurement results (boolean values corresponding to 0 and 1). 149 | # We can also obtain the bit string: 150 | 151 | # %% 152 | result_bitstring = [int(res) for res in result] 153 | print(result_bitstring) 154 | 155 | # %% [markdown] 156 | # Ideally, the two values should always be correlated since we want to prepare a GHZ state. 157 | # However, now that we've added noise, this is not always the case. 158 | # 159 | # We can actually use this fact to define a "fidelity" measure for the circuit: when repeatedly executing the circuit, uncorrelated results lower the fidelity. 160 | # 161 | # Mathematically, let's define the fidelity $F$ as 162 | # 163 | # $F = 1 - \sum_{i=1}^n \frac{\text{err}(i)}{n}$, 164 | # 165 | # where $n$ is the number of shots we take and 166 | # 167 | # $ \text{err}(i) = \begin{cases} 0 \text{ if run }i \text{ is correct} \\ 1 \text{ else } \end{cases}$ 168 | # 169 | # In this case, "correct" means the measurement outcome is fully correlated. 170 | 171 | # %% 172 | n_shots = 1000 173 | n_qubits = 4 174 | p_single = 1e-2 175 | p_paired = 2 * p_single 176 | fidelity = 1.0 177 | sim = StackMemorySimulator(min_qubits=n_qubits) 178 | for _ in range(n_shots): 179 | result = sim.run(noisy_linear_ghz, args=(n_qubits, p_single, p_paired)) 180 | measured_one_state = all(result) 181 | measured_zero_state = not any(result) 182 | is_correlated = measured_one_state or measured_zero_state 183 | if not is_correlated: 184 | fidelity -= 1 / n_shots 185 | 186 | print(fidelity) 187 | 188 | # %% [markdown] 189 | # Note, that this is actually a poor measure for fidelity as it only counts fully correlated states and treats everything else as an equivalent error. 190 | # If you have many qubits, you could argue that only flipping a single bit is a much lower error than flipping many, and that this should be weighed in here. 191 | # Or, you can simply use the simulator to obtain the state vector and compute the overlap. 192 | # Or, define whatever measure of fidelity you see fit here, but we'll end this tutorial here. 193 | -------------------------------------------------------------------------------- /docs/analog/index.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Bloqade Logo 4 | Bloqade Logo 5 | 6 |
7 | 8 | [![Latest Version](https://img.shields.io/pypi/v/bloqade.svg)](https://pypi.python.org/pypi/bloqade) 9 | [![Supported Python Versions](https://img.shields.io/pypi/pyversions/bloqade-analog.svg)](https://pypi.python.org/pypi/bloqade-analog) 10 | [![codecov](https://codecov.io/github/QuEraComputing/bloqade-analog/graph/badge.svg?token=4YJFc45Jyl)](https://codecov.io/github/QuEraComputing/bloqade-analog) 11 | ![CI](https://github.com/QuEraComputing/bloqade-analog/actions/workflows/ci.yml/badge.svg) 12 | 13 | !!! note 14 | 15 | Bloqade has been restructured to make room for new features and improvements. 16 | Please refer to the [migration guide](./home/migration/) for more information. 17 | 18 | 19 | ## What is Bloqade Analog? 20 | 21 | Bloqade Analog is a Python SDK for [QuEra's](https://www.quera.com/) neutral atom quantum computer *Aquila* (check out our [paper](https://arxiv.org/abs/2306.11727)!). It's designed to make writing and analyzing the results of [analog quantum programs](home/background.md#analog-vs-digital-quantum-computing) on *Aquila* as easy as possible. It features [custom atom geometries](index.md#customizable-atom-geometries) and [flexible waveform definitions](index.md#flexible-pulse-sequence-construction) in both [emulation and real hardware](index.md#hardware-and-emulation-backends). Bloqade interfaces with the [AWS Braket](https://aws.amazon.com/braket/) cloud service where *Aquila* is hosted, enabling you to submit programs as well as retrieve and analyze real hardware results all-in-one. 22 | 23 | ## Installation 24 | 25 | You can install the package with [`pip`](https://pip.pypa.io/en/stable/) in your Python environment of choice via: 26 | 27 | ```sh 28 | pip install bloqade 29 | ``` 30 | 31 | ## A Glimpse of Bloqade Analog 32 | 33 | Let's try a simple example where we drive a [Rabi oscillation][rabi-oscillation-wiki] on a single [neutral atom][neutral-atom-qubits]. Don't worry if you're unfamiliar with [neutral atom][neutral-atom-qubits] physics, (you can check out our Background for more information!) the goal here is to just give you a taste of what Bloqade can do. 34 | 35 | We start by defining where our atoms go, otherwise known as the *atom geometry*. In this particular example we will use a small Honeycomb lattice: 36 | 37 | ```python 38 | from bloqade.analog.atom_arrangement import Honeycomb 39 | 40 | geometry = Honeycomb(2, lattice_spacing = 10.0) 41 | ``` 42 | 43 | We can verify what the atom geometry looks like by `.show()`'ing it: 44 | 45 | ```python 46 | geometry.show() 47 | ``` 48 | 49 |
50 | 51 | Geometry Visualization 52 | 53 |
54 | 55 | We now define what the [time evolution][rydberg-hamiltonian] looks like using a *pulse sequence*. The pulse sequence here is the time profile of the Rabi Drive targeting the ground-Rydberg two level transition, which causes the [Rabi oscillations][rabi-oscillation-wiki]. We choose a constant waveform with a value of $\frac{\pi}{2} \text{rad}/\text{us}$ and a duration of $1.0 \,\text{us}$. 56 | This produces a $\frac{\pi}{2}$ rotation on the [Bloch sphere](https://en.wikipedia.org/wiki/Bloch_sphere) meaning our final measurements should be split 50/50 between the ground and Rydberg state. 57 | 58 | ```python 59 | from math import pi 60 | rabi_program = ( 61 | geometry 62 | .rydberg.rabi.amplitude.uniform 63 | .constant(value=pi/2, duration=1.0) 64 | ) 65 | ``` 66 | 67 | Here `rabi.amplitude` means exactly what it is, the Rabi amplitude term of the [Hamiltonian][rydberg-hamiltonian]. `uniform` refers to applying the waveform uniformly across all the atom locations. 68 | 69 | We can visualize what our program looks like again with `.show()`: 70 | 71 |
72 | 73 | Program Visualization 74 | 75 |
76 | 77 | We can now run the program through Bloqade's built-in emulator to get some results. We designate that we want the program to be run and measurements performed 100 times: 78 | 79 | ```python 80 | emulation_results = rabi_program.bloqade.python().run(100) 81 | ``` 82 | 83 | With the results we can generate a report object that contains a number of methods for analyzing our data, including the number of counts per unique bitstring: 84 | 85 | ```python 86 | bitstring_counts = emulation_results.report().counts() 87 | ``` 88 | 89 | Which gives us: 90 | 91 | ``` 92 | [OrderedDict([('0', 55), ('1', 45)])] 93 | ``` 94 | 95 | If we want to submit our program to hardware we'll need to adjust the waveform as there is a constraint the Rabi amplitude waveform must start and end at zero. 96 | This is easy to do as we can build off the atom geometry we saved previously but apply a piecewise linear waveform: 97 | 98 | ```python 99 | hardware_rabi_program = ( 100 | geometry 101 | .rydberg.rabi.amplitude.uniform 102 | .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06]) 103 | ) 104 | 105 | hardware_rabi_program.show() 106 | ``` 107 |
108 | 109 | Hardware Program Visualization 110 | 111 |
112 | 113 | Now instead of using the built-in Bloqade emulator we submit the program to *Aquila*. You will need to use the [AWS CLI](https://aws.amazon.com/cli/) to obtain credentials from your AWS account 114 | or set the proper environment variables before hand. 115 | 116 | ```python 117 | hardware_results = hardware_rabi_program.braket.aquila.run_async(100) 118 | ``` 119 | 120 | `.run_async` is a non-blocking version of the standard `.run` method, allowing you to continue work while waiting for results from *Aquila*. `.run_async` immediately returns an object you can query for the status of your tasks in the queue as well. 121 | 122 | You can do the exact same analysis you do on emulation results with hardware results too: 123 | 124 | ```python 125 | hardware_bitstring_counts = hardware_results.report().counts() 126 | ``` 127 | 128 | If you want to try the above at once, we collected the above steps into the snippet below: 129 | 130 | ```python 131 | from math import pi 132 | from bloqade.analog.atom_arrangement import Honeycomb 133 | 134 | geometry = Honeycomb(2, lattice_spacing = 10.0) 135 | rabi_program = ( 136 | geometry 137 | .rydberg.rabi.amplitude.uniform 138 | .constant(value=pi/2, duration=1.0) 139 | ) 140 | emulation_results = rabi_program.bloqade.python().run(100) 141 | bitstring_counts = emulation_results.report().counts() 142 | 143 | hardware_rabi_program = ( 144 | geometry 145 | .rydberg.rabi.amplitude.uniform 146 | .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06]) 147 | ) 148 | hardware_results = hardware_rabi_program.braket.aquila.run_async(100) 149 | hardware_bitstring_counts = hardware_results.report().counts() 150 | ``` 151 | 152 |
153 |
154 | 155 | ## Features 156 | 157 | 158 | ### Customizable Atom Geometries 159 | 160 | You can easily explore a number of common geometric lattices with Bloqade's `atom_arrangement`'s: 161 | 162 | ```python 163 | from bloqade.analog.atom_arrangement import Lieb, Square, Chain, Kagome 164 | 165 | geometry_1 = Lieb(3) 166 | geometry_2 = Square(2) 167 | geometry_3 = Chain(5) 168 | geometry_4 = Kagome(3) 169 | ``` 170 | 171 | 172 | If you're not satisfied with the [Bravais lattices](https://en.wikipedia.org/wiki/Bravais_lattice) we also allow you to modify existing Bravais lattices as follows: 173 | 174 | ```python 175 | geometry_5 = Kagome(3).add_position((10,11)) 176 | ``` 177 | 178 | You can also build your geometry completely from scratch: 179 | 180 | ```python 181 | from bloqade import start 182 | 183 | geometry = start.add_positions([(0,0), (6,0), (12,0)]) 184 | ``` 185 | 186 | ### Flexible Pulse Sequence Construction 187 | 188 | Define waveforms for pulse sequences any way you like by either building (and chaining!) them immediately as part of your program: 189 | 190 | ```python 191 | from bloqade.analog.atom_arrangement import Square 192 | 193 | geometry = Square(2) 194 | target_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform 195 | custom_rabi_amp_waveform = ( 196 | target_rabi_amplitude 197 | .piecewise_linear(values=[0, 10, 10, 0], durations=[0.1, 3.5, 0.1]) 198 | .piecewise_linear(values=[0, 5, 3, 0], durations=[0.2, 2.0, 0.2]) 199 | ) 200 | ``` 201 | 202 | Or building them separately and applying them later: 203 | 204 | ```python 205 | from bloqade.analog.atom_arrangement import Square, Chain 206 | 207 | geometry_1 = Square(3) 208 | geometry_2 = Chain(5) 209 | 210 | target_rabi_amplitude = start.rydberg.rabi.amplitude.uniform 211 | pulse_sequence = target_rabi_amplitude.uniform.constant(value=2.0, duration=1.5).parse_sequence() 212 | 213 | program_1 = geometry_1.apply(pulse_sequence) 214 | program_2 = geometry_2.apply(pulse_sequence) 215 | ``` 216 | 217 | ### Hardware and Emulation Backends 218 | 219 | Go from a fast and powerful emulator: 220 | 221 | ```python 222 | from bloqade.analog.atom_arrangement import Square 223 | from math import pi 224 | 225 | geometry = Square(3, lattice_spacing = 6.5) 226 | target_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform 227 | program = ( 228 | target_rabi_amplitude 229 | .piecewise_linear(values = [0, pi/2, pi/2, 0], durations = [0.06, 1.0, 0.06]) 230 | ) 231 | emulation_results = program.bloqade.python().run(100) 232 | ``` 233 | 234 | To real quantum hardware in a snap: 235 | 236 | ```python 237 | hardware_results = program.braket.aquila().run_async(100) 238 | ``` 239 | 240 | ### Simple Parameter Sweeps 241 | 242 | Use variables to make parameter sweeps easy on both emulation and hardware: 243 | 244 | ```python 245 | from bloqade import start 246 | import numpy as np 247 | 248 | geometry = start.add_position((0,0)) 249 | target_rabi_amplitude = geometry.rydberg.rabi.amplitude.uniform 250 | rabi_oscillation_program = ( 251 | target_rabi_amplitude 252 | .piecewise_linear(durations = [0.06, "run_time", 0.06], values = [0, 15, 15, 0]) 253 | ) 254 | rabi_oscillation_job = rabi_oscillation_program.batch_assign(run_time=np.linspace(0, 3, 101)) 255 | 256 | emulation_results = rabi_oscillation_job.bloqade.python().run(100) 257 | hardware_results = rabi_oscillation_job.braket.aquila().run(100) 258 | ``` 259 | 260 | ``` 261 | emulation_results.report().rydberg_densities() 262 | 0 263 | task_number 264 | 0 0.16 265 | 1 0.35 266 | 2 0.59 267 | 3 0.78 268 | 4 0.96 269 | ... ... 270 | 96 0.01 271 | 97 0.09 272 | 98 0.24 273 | 99 0.49 274 | 100 0.68 275 | 276 | [101 rows x 1 columns] 277 | ``` 278 | 279 | ### Quick Results Analysis 280 | 281 | Want to just see some plots of your results? `.show()` will show you the way! 282 | 283 | ```python 284 | from bloqade.analog.atom_arrangement import Square 285 | 286 | rabi_amplitude_values = [0.0, 15.8, 15.8, 0.0] 287 | rabi_detuning_values = [-16.33, -16.33, 42.66, 42.66] 288 | durations = [0.8, 2.4, 0.8] 289 | 290 | geometry = Square(3, lattice_spacing=5.9) 291 | rabi_amplitude_waveform = ( 292 | geometry 293 | .rydberg.rabi.amplitude.uniform.piecewise_linear(durations, rabi_amplitude_values) 294 | ) 295 | program = ( 296 | rabi_amplitude_waveform 297 | .detuning.uniform.piecewise_linear(durations, rabi_detuning_values) 298 | ) 299 | emulation_results = program.bloqade.python().run(100) 300 | emulation_results.report().show() 301 | ``` 302 | ![](/assets/index/report-visualization.png) 303 | 304 | ## Contributing to Bloqade 305 | 306 | Bloqade is released under the [Apache License, Version 2.0](https://github.com/QuEraComputing/bloqade-analog/blob/main/LICENSE). If you'd like the chance to shape the future of [neutral atom][neutral-atom-qubits] quantum computation, see our [Contributing Guide](contributing/index.md) for more info! 307 | 308 | [rabi-oscillation-wiki]: https://en.wikipedia.org/wiki/Rabi_cycle 309 | [neutral-atom-qubits]: home/background.md#neutral-atom-qubits 310 | [rydberg-hamiltonian]: home/background.md#rydberg-many-body-hamiltonian 311 | --------------------------------------------------------------------------------