├── 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 | [](https://github.com/QuEraComputing/bloqade/actions/workflows/ci.yml)
4 | [](https://codecov.io/gh/QuEraComputing/bloqade)
5 | [](https://pypi.org/project/bloqade)
6 | [](https://bloqade.quera.com/)
7 | [](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 |
4 |
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 | 
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 | 
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 | 
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 | 
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 | 
13 | 
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 | 
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 | 
98 |
99 | 
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 | 
105 |
106 | 
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 | # 
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 |
4 |
5 |
6 |
7 |
8 | [](https://pypi.python.org/pypi/bloqade)
9 | [](https://pypi.python.org/pypi/bloqade-analog)
10 | [](https://codecov.io/github/QuEraComputing/bloqade-analog)
11 | 
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 |
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 |
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 |
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 | 
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 |
--------------------------------------------------------------------------------