> ## Agent Instructions
> Prefer the qBraid CLI for programmatic platform actions: pip install 'qbraid-cli>=0.12', then run `qbraid configure` once with an API key from https://account.qbraid.com/account/api-keys.
> Always install the latest packages (pip install -U qbraid qbraid-cli); do not pin versions from memory. qbraid-cli below 0.12.0 is incompatible with the current API.
> Device IDs use the QRN format vendor:provider:type:name (e.g. qbraid:qbraid:sim:qir-sv, rigetti:rigetti:qpu:cepheus-1-108q). Legacy underscore IDs are deprecated.
> The REST API base URL is https://api-v2.qbraid.com/api/v1, authenticated with an X-API-Key header.
> Free simulators cost no credits; QPU and GPU jobs consume credits. Surface the estimated cost to the user before submitting a paid job.
> For account signup, API keys, credits, and end-to-end action recipes, see https://qbraid.com/llms.txt.
# API Keys
Source: https://docs.qbraid.com/v2/account/api-keys
Create, view, rotate, and revoke API keys for programmatic access to qBraid.
Every qBraid user gets unique API keys for programmatic access. Use them with [qBraid Runtime](/v2/sdk/user-guide/runtime), the [REST API](/v2/api-reference/user-guide), the [CLI](/v2/cli/api-reference/qbraid), or [Quantum Jobs](/v2/lab/user-guide/quantum-jobs) from Lab.
Open the API Keys page from the sidebar under **Account > API Keys**.
## Create an API key
Click **Account > API Keys** in the sidebar.
The button is in the top-right.
Name the key and pick any scopes if shown.
Click create. The new key value is shown once.
The key value is shown **only once**, right after creation. Copy it
immediately and store it somewhere secure. If you lose it, you'll have to
revoke and create a new one.
## View key details
Click **Account > API Keys** in the sidebar.
The row expands inline.
You'll see:
* Creation date.
* Last used date.
* Status (`active` or `inactive`).
* Permissions and usage stats.
## Rotate a key
Rotation generates a new key value with the same name and permissions, and immediately invalidates the old one.
Click **Account > API Keys** in the sidebar.
Locate it in the list.
The refresh icon sits next to the Copy icon on each row.
Click **Regenerate Key** in the dialog. The new value is shown once.
Once you regenerate, the old key stops working straight away. Update every
service using it **before** rotating, or production will break.
## Revoke a key
Click **Account > API Keys** in the sidebar.
Locate it in the list.
The trash icon on the row.
Confirm in the dialog.
The key is permanently removed.
Revoked keys cannot be recovered. Make sure nothing in production is still
using the key before you revoke.
## Security best practices
API keys grant access to your credits and resources. Treat them like passwords.
Every 90 days is a reasonable baseline. Rotate sooner if you suspect a leak.
Use environment variables or a secrets manager (1Password, Vault, AWS
Secrets Manager, etc.).
Send via a password manager or vault link. Screenshots leak through bug
reports and tickets.
Review the **Last used** column periodically. If a key is being used when it
shouldn't be, revoke it.
If you discover a vulnerability, report it via our [security advisory form](https://github.com/qBraid/community/security/advisories). For the full security policy, see [qBraid Security Policy](https://github.com/qBraid/community/security/policy).
# Partner Guide
Source: https://docs.qbraid.com/v2/account/explore
How to add your solution to the qBraid Explore page.
# Adding Your Project
This guide walks partners through contributing a solution or project to the qBraid [Explore page](https://account.qbraid.com/explore) so users can discover it and run it out-of-the-box on the qBraid Lab platform.
## Step 1: Create a Public GitHub Repository
Create a public GitHub repository with the code for your solution or project. Content can take many forms: example notebooks, executable Python files, or code in other languages (e.g., C++ or Julia). Include a detailed **README** that describes the project and explains how to use it. See existing examples or reach out for guidance.
We'll review your README and give feedback. If it isn't a good fit for the Explore page (e.g., the README doesn't clearly describe the project's content, or it contains setup instructions that aren't applicable to qBraid), we may ask you to add a separate markdown file with qBraid-specific content. We can link to that file so it's what users see when they open your project on the platform.
## Step 2: Send Us Environment and Project Details
Send all of the following in a single email to [contact@qbraid.com](mailto:contact@qbraid.com) or other qBraid point of contact. We'll use it to create your environment and add your project listing.
### Environment details
* **Dependencies** — A minimal list of required packages (e.g., as you would put in a `requirements.txt` or `pyproject.toml`). Do not send a full `pip list`; we need only what's minimally required to run your code and examples.
* **Logos** — Square logos in light and dark versions. We'll use them for both the environment sidebar image and the kernel logo.
* **Non-Python or GPU?** — If your project needs a different runtime (e.g., not Python) or GPUs, say so in the email. We can set up a custom Docker container or GPU accommodations and will align with you on requirements.
### Project details
* **Short description** — 1–2 sentences for the Explore page.
* **Tags** — Terms to make the project searchable.
* **GitHub repository URL** — The public repo from Step 1.
## Step 3: What Happens Next
We will create the environment and share it with your qBraid account. Test it (discoverable in **Shared environments** on Lab), and tell us if anything needs to change. Once you're happy with it, we'll add your project to the Explore page. Review the listing and let us know if any final modifications are needed.
## Questions?
Reach out to [contact@qbraid.com](mailto:contact@qbraid.com) or your qBraid point of contact.
# Open Quantum
Source: https://docs.qbraid.com/v2/account/integrations/openquantum
Connect your Open Quantum account to qBraid to run quantum jobs on your own Open Quantum credits.
The Open Quantum integration lets you run quantum jobs on Open Quantum devices through qBraid, paid for with your own [Open Quantum](https://www.openquantum.com/) credits. Open Quantum devices can only be billed to Open Quantum credits, so you need to connect your Open Quantum account before you can run jobs on them.
**"Free" means free of qBraid credits, not free of charge.** Open Quantum
devices are listed as free on qBraid because running a job on one costs you no
qBraid credits. The job is instead billed to the Open Quantum account you
connect below, drawing down that account's credit balance. You can top that
balance up by buying credits, or claim [free Spark
credits](#claim-your-free-spark-credits) through Open Quantum's promotional
offerings.
You manage the connection from the **Integrations** tile on your [Profile page](https://account.qbraid.com/account/profile).
You need an [Open Quantum](https://www.openquantum.com/) account before you
can connect. It doesn't have to match the email you use for qBraid, any Open
Quantum account works.
## Create your Open Quantum account
Set up Open Quantum first, so the account exists when you connect it to qBraid.
Open [openquantum.com](https://www.openquantum.com/).
Sign up for an Open Quantum account. Google sign-in is supported.
Complete any remaining onboarding steps on the Open Quantum side.
## Claim your free Spark credits
When you sign in to your new Open Quantum account, a welcome banner greets you
with an offer of free compute credits, while supplies last. Click **Claim \$50
Free Credit** to add them to your Open Quantum account. These arrive as **Spark**
credits, Open Quantum's free and promotional credits, which
[expire](https://docs.openquantum.com/concepts/credits/) 90 days after they're
granted.
Once you connect your account to qBraid below, jobs you submit to Open Quantum
devices through qBraid are billed against these credits.
## Connect Open Quantum to qBraid
Go to [account.qbraid.com](https://account.qbraid.com/) and sign in.
Click your name and avatar at the bottom of the sidebar, or use **Account >
Profile**.
Scroll down to the **Integrations** tile, below Personal Information. A grey
dot next to **Open Quantum** means it isn't connected yet.
This opens the Open Quantum authentication window.
Sign in with your Open Quantum account. The window closes automatically once
you're authenticated.
## Confirm the connection
Back on the Profile page, the **Open Quantum** entry now shows a green
**Connected** dot along with the account you're connected as. That's it, jobs
submitted to Open Quantum devices will now run on your Open Quantum credits.
## Disconnect Open Quantum
To end the integration, open the **Integrations** tile and click **Disconnect**
next to **Open Quantum**. Once disconnected, you can no longer run jobs on
Open Quantum devices until you connect again, since those jobs can only be paid
for with your Open Quantum credits.
## Troubleshooting
This is the most common error. It means your Open Quantum balance can't
cover the job, usually for one of these reasons:
* You never claimed the \$50 of free Spark credits offered when you signed up.
* You've spent the credits you had.
* Your credits expired before you used them.
* You're waiting on your next Promotional Credits refill.
Check your balance on the [Open Quantum billing
page](https://www.openquantum.com/settings/billing). **Credit Balance**
shows your **Spark** and **Full** credits separately, along with the date
your earliest Spark credits expire. Spark credits are the free and
promotional ones, and expire 90 days after they're granted; Full credits are
the ones you purchase, and expire after 12 months. Jobs spend your Spark
credits first, then your Full credits. **Credit Transactions** below it
itemizes every consumption, promotion, and expiration, so you can see
exactly where your credits went.
To top up, click **Add Credits** to reach the [Buy
Credits](https://www.openquantum.com/buy-credits) page, where you can
purchase Full credits in bundles. Otherwise, wait for your next Promotional
Credits refill.
Open the **Integrations** tile and confirm **Open Quantum** shows a green
**Connected** dot. If it's grey, click **Connect** and finish signing in on
the Open Quantum side. Also check that your Open Quantum account has credits
available.
Check that your browser isn't blocking pop-ups for `account.qbraid.com`,
then try again.
Reach out from the **Support** link in the sidebar and we'll take a look at
your job history.
## Related links
* [qBraid on Open Quantum](https://www.openquantum.com/frameworks/qbraid)
* [Open Quantum qBraid documentation](https://docs.openquantum.com/qbraid/overview/)
* [Open Quantum credits: Spark vs Full](https://docs.openquantum.com/concepts/credits/)
# Creating an Organization
Source: https://docs.qbraid.com/v2/account/organizations/creating-an-organization
Request a new qBraid organization for your team, lab, or company.
qBraid Organizations are created on request. Here's how to ask for one and what happens next.
## Submit a request
Open [account.qbraid.com](https://account.qbraid.com/) and sign in to your
qBraid Default workspace.
Click **Organizations** in the sidebar.
The button is in the top-right (or go directly to
[account.qbraid.com/organizations/new](https://account.qbraid.com/organizations/new)).
What people see in invites and the dashboard.
A short summary of what your group does.
Your company, lab, or institution.
Used for invoicing.
What you'll use qBraid for. Specific use cases get reviewed faster than
vague ones.
Click **Submit Request**.
You can have one pending request at a time. If you've already submitted one,
the form shows its current status instead.
## What happens next
Our team reviews requests within 1 to 2 business days. You'll get an email when it's approved or declined.
Once approved, the new organization shows up in the workspace switcher in the
top-left of the dashboard. You're the Owner automatically.
## Switching between workspaces
Click the dropdown in the top-left of the dashboard.
Choose qBraid Default or any organization you belong to.
The dashboard updates to show that workspace's resources.
If you belong to many organizations, type any part of an org name in the
switcher to filter the list.
Everything you do (Wallet, Members, Billing) applies to whichever workspace is
selected. Check the switcher before you transfer credits or cancel a
subscription so you don't act on the wrong account.
# Credit Requests
Source: https://docs.qbraid.com/v2/account/organizations/credit-requests
Ask an admin for credits when you can't buy them directly from your org.
If you joined an organization and don't have permission to buy credits, you can request them from an admin instead.
Credit requests aren't available in the qBraid Default workspace. They only
work inside an organization where you're a member without purchase
permissions.
## Submit a request
Open the workspace switcher in the top-left of the dashboard and select your
organization (not qBraid Default).
Click **Account > Wallet** in the sidebar.
The button sits where **Buy credits** would be in qBraid Default.
How many credits you need.
A project, a deadline, anything that helps the admin decide.
Click **Submit**.
The request goes to the org's admins. You can have one pending request at a time.
## Track the request
Open the workspace switcher and select your organization.
Click **Account > Wallet** in the sidebar.
While a request is pending, this button replaces **Request credits**.
The status modal shows:
* The amount you asked for.
* The status: `pending`, `approved`, or `rejected`.
* Any note the admin left when responding.
When an admin approves it, the credits arrive in your wallet right away. If they reject it, the wallet shows the reason so you know what to ask differently next time.
## Need credits faster?
Admins see requests in the **Credit Requests** tab of their billing dashboard. If it's urgent, ping them directly. Admins can also push credits to your wallet manually without waiting for a request. See [Organizations > Credits](/v2/account/organizations/credits) for how that works.
# Credits
Source: https://docs.qbraid.com/v2/account/organizations/credits
Buy credits for the org, transfer them to members one by one or in bulk, and approve member requests.
The org wallet is a shared pool of credits admins use to fund pay-as-you-go work across the team. You buy credits in bulk, then distribute them to specific members however you want.
The org wallet balance and credit actions live on the [Billing page](https://account.qbraid.com/organizations/billing) (Balance card and **Credits** tab).
## Buy credits for the org
Open the workspace switcher in the top-left of the dashboard and select your
organization.
Click **Billing** in the sidebar.
In the top-right of the Billing page.
Type the dollar amount you want to spend. The credit count updates as you
type.
Click **Purchase** and finish checkout on Stripe.
The credits land in the **org wallet** (not in any individual member's
wallet).
## Transfer credits to one member
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
On the Balance card at the top of the Billing page.
Select from the dropdown.
Org-allocated, promo, etc. (depending on what's available in your wallet).
Add a short note explaining what the credits are for.
Review the preview and click **Transfer Credits**.
The credits leave the org wallet and show up in the member's qBraid Default wallet right away.
## Transfer to many members at once
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
On the Balance card at the top of the Billing page.
**Selected** to pick specific people from a list, or **All** to send to
everyone in the org.
The total updates as you type.
Something like "Monthly allocation" or "Project kickoff."
Review the total and click **Transfer**.
Your org wallet needs to hold at least `amount × number of members` in
credits, or the transfer is blocked.
## Approve a credit request
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
The second tab on the Billing page.
On the request row.
You can grant less than what was requested.
The credits move from the org wallet to the member's wallet immediately.
## Reject a credit request
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
The second tab.
On the request row.
The member sees it in the status panel on their wallet.
The request is removed from pending.
Set a sensible default credit allocation in [Settings > Member
defaults](/v2/account/organizations/settings#member-settings) so new members
get funded without you having to push credits manually each time.
## Where do these credits come from?
There are three sources that fill the org wallet:
Buying credits from the **Buy Credits** button on the Billing page.
When you assign a seat, the configured per-seat credits move from the org
wallet to the member's wallet automatically. Configure in
[Settings](/v2/account/organizations/settings#member-settings).
When you remove a member, their org-allocated credits flow back to the org
wallet.
Every movement is logged in [Transactions](/v2/account/organizations/transactions).
# Members & Invites
Source: https://docs.qbraid.com/v2/account/organizations/members
Invite teammates, send them credits with the invite, change roles, and remove people.
The [Members page](https://account.qbraid.com/organizations/members) is where admins handle everyone in the organization: active members, pending invites, and anyone who's been suspended.
Filters at the top let you narrow by **name**, **role**, or **status** (`active`, `pending`, `suspended`).
## Roles
Every member has one role. The **Owner** is set automatically when the organization is created. There's exactly one owner and that role can't be reassigned.
| Role | What they can do |
| ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| **Admin** | Everything. Manage settings, billing, members, invites, credits, seats. |
| **Manager** | Manage devices, jobs, projects, and members' resource access. Can view billing. Can't change settings or assign seats. |
| **Member** | Use org resources. Can't manage anyone else, billing, or settings. |
## Invite one person
Open the workspace switcher in the top-left of the dashboard and select your
organization.
Click **Members** in the sidebar.
The button is in the top-right of the Members page.
The invitee doesn't need a qBraid account yet. If they don't have one,
they'll create it when they accept.
Choose **Admin**, **Manager**, or **Member**. See [Roles](#roles) above.
Credits move from the org wallet to the new member's wallet when they
accept. The org balance updates live as you type.
A short personal note included in the invite email.
Click **Send Invite**. The invitee appears in the Members list with status
`pending` until they accept.
If you put credits on the invite and the user never accepts (or you cancel
it), the credits stay in the org pool. Credits only actually move when the
invite is accepted.
## Invite many at once
Open the workspace switcher and select your organization.
Click **Members** in the sidebar.
The button is next to **Invite Member** in the top-right.
Either type them in manually or upload a CSV (see tabs below).
Click **Send Invitations**. You get per-row results so you know exactly
which invites went out and which had problems.
Type Email, Role, and (optionally) Credits, then click **Add to Queue**. Repeat for each invite.
Drop in a file that looks like this:
```csv theme={"dark"}
email,role,credits
alice@example.com,member,500
bob@example.com,admin,
carol@example.com,member,1000
```
The `credits` column is optional. Rows with bad emails or unknown roles get flagged before anything sends.
Bulk invites are capped at 100 users per upload. The total credits across all
rows can't exceed your org's wallet balance.
## Manage existing members
Every action below starts the same way: open **Members**, find the row, and click the actions menu (⋯). Then pick the action:
Click **Resend Invitation**. A new invite email is sent. Only available on `pending` rows.
Click **Cancel Invitation** and confirm. The user can no longer accept; any attached credits stay in the org pool. Only available on `pending` rows.
Click **Suspend Member** and confirm. Status flips to `suspended` and they lose access to org compute, jobs, and credits immediately.
Click **Activate Member** and confirm. Their previous role and credit balance come back exactly as they were.
Click **Remove Member** and confirm. When you remove someone:
* They lose access to all org resources right away.
* Their seat (if they had one) goes back to the org pool. You can reassign it.
* Credits the org gave them return to the org wallet.
Removal can't be undone. If they need access again, you'll have to send a fresh invite.
Click **Edit Role**, pick Admin, Manager, or Member, and confirm. The change takes effect immediately. If they're already signed in somewhere, they'll see the new permissions next time they load a page.
The **Remove Member** action does two different things depending on status. On
a `pending` row it cancels the invite. On an `active` or `suspended` row it
removes the member.
# Overview
Source: https://docs.qbraid.com/v2/account/organizations/overview
Use quantum systems within your team, lab, or company with shared resources and centralized billing.
A qBraid Organization is a shared workspace for your team. Instead of every person managing their own plan and credits, the org pays once, and admins hand out access, seats, and credits as needed.
It's built for research groups, university courses, and companies running quantum workloads at scale.
## What you get
* **Team management.** Invite people, give them roles, and control what each person can do.
* **Shared credits.** Buy credits in bulk and transfer them to specific members (one at a time or in bulk).
* **Shared subscriptions.** One team plan with seats you assign to whoever needs the benefits.
* **Permission controls.** Three roles (Admin, Manager, Member) plus a single Owner per org.
* **Usage visibility.** See how the team is spending compute and credits in one place.
## Who it's for
* **Research groups** running shared experiments and pooling compute.
* **Universities** managing student access for courses, with caps so no one accidentally burns the budget.
* **Companies** deploying qBraid across teams with centralized billing and clean expense reports.
## Explore the docs
Submit a request to create your team workspace.
Invite teammates, manage roles, suspend or remove access.
Assign or revoke subscription seats for specific members.
Buy credits, transfer to members, approve requests.
Buy a team plan, change tier, or cancel.
Audit log with CSV export.
Logo, member defaults, danger zone.
New to qBraid? Start with the [qBraid Default
Wallet](/v2/account/wallet/overview) to understand how credits and personal
subscriptions work. Team subscriptions follow the same idea but apply per
seat.
# Seats
Source: https://docs.qbraid.com/v2/account/organizations/seats
Assign your subscription's seats to specific members and revoke them when people move on.
A seat is what gives a member the benefits of your team subscription tier. Seats you haven't assigned just sit in the org pool until you hand them out.
The Seat Management table lives on the **Subscription** tab of the [Billing page](https://account.qbraid.com/organizations/billing).
## Assign a seat
Open the workspace switcher in the top-left of the dashboard and select your
organization.
Click **Billing** in the sidebar.
It's the default tab.
The table sits below the subscription card.
Click **Assign** on an unassigned seat row (or **Assign Seat** on a member's
row).
Choose them from the dropdown.
Click **Assign Seat**.
The member gets tier benefits right away. If you've turned on
credits-on-assign in [Settings](/v2/account/organizations/settings), they also
receive a credit transfer at the same time.
Turn on **Auto-assign Seats** in
[Settings](/v2/account/organizations/settings) and new members get a seat
automatically when they accept their invite (as long as one is available).
## Revoke a seat
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
It's the default tab.
The table sits below the subscription card.
On the member's row.
Confirm in the dialog.
The seat goes back to the pool and you can give it to someone else. The member loses tier benefits but keeps any credits already in their wallet.
## How seats and members interact
| Action | Effect on seat |
| -------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Member accepts invite (with Auto-assign Seats on) | A seat is assigned automatically if one is available. |
| Member accepts invite (with Auto-assign Seats off) | They join without a seat. Assign manually later. |
| Admin removes a member | Their seat returns to the pool. |
| Admin suspends a member | Their seat returns to the pool. Reassigning when they're reactivated isn't automatic. |
| Subscription is canceled (period ends) | All assigned seats are revoked at the same time. |
| Admin adds seats to the subscription | New unassigned seats land in the pool. |
## Need more seats?
Adding seats is a billing action, not a member action. See [Subscriptions > Add more seats](/v2/account/organizations/subscriptions#add-more-seats).
# Settings
Source: https://docs.qbraid.com/v2/account/organizations/settings
Set your org's name and logo, control what happens when new members join, and find owner-only actions.
The [Settings page](https://account.qbraid.com/organizations/settings) is where admins configure how the organization looks and behaves. It has three sections: **General**, **Member Settings**, and **Danger Zone**.
## General
This is the public face of your organization. It's what shows up in invitations, the switcher, and the dashboard.
| Field | Required | Notes |
| ----------------- | -------- | ---------------------------------------------------- |
| Organization Name | Yes | What people see everywhere your org shows up. |
| Description | No | A short summary of what your group does. |
| Website | Yes | Must be a valid URL. |
| Address | No | Used for invoices and contracts. |
| Logo (Light) | No | For light-themed UIs. PNG, JPG, or WEBP. Max 200 KB. |
| Logo (Dark) | No | For dark-themed UIs. Same formats and size limit. |
### Update general settings
Open the workspace switcher in the top-left of the dashboard and select your
organization.
Click **Settings** in the sidebar.
Update any field. Required fields are marked.
Click **Save Changes** at the bottom of the section. An "Unsaved changes"
reminder appears next to the button until you save.
Upload both a light and a dark logo if you have versions for each theme.
qBraid picks the right one automatically based on the viewer's theme.
## Member settings
These are the defaults applied when **new** people join. Changing them doesn't affect anyone who's already in the org.
| Setting | What it does |
| ------------------------ | --------------------------------------------------------------------------------------------------------- |
| Auto-assign Default Role | When **Enabled**, new members get the default role no matter what you set on the invite. |
| Default Role | The role to apply. Pick Member, Manager, or Admin. |
| Auto-assign Seats | When **Enabled**, new members get a seat automatically (as long as one is available). |
| Seat Assignment Credits | Credits to transfer from the org wallet whenever a seat gets assigned (manual or automatic). Max 100,000. |
### Update member settings
Open the workspace switcher and select your organization.
Click **Settings** in the sidebar.
Below the General section.
Change auto-assign behavior, default role, or seat credits.
Click **Save Changes**.
If Auto-assign Seats is on but you're out of seats, the new member joins
without one. Assign manually when a seat opens up.
## Danger zone
Only the **Owner** sees this section. These actions are permanent.
* **Transfer Ownership** *(coming soon).* Hand the Owner role to another admin. You'll lose owner privileges.
* **Delete Organization** *(coming soon).* Permanently destroys the org, members, settings, and history. Cancel subscriptions first.
Both actions, once they ship, can't be undone. Treat them like dropping a
production database.
# Team Subscriptions
Source: https://docs.qbraid.com/v2/account/organizations/subscriptions
Buy a team plan, change tier or interval, add capacity, and handle cancellations.
A team subscription gives your organization a tier (Standard or Pro) and a pool of seats you assign to specific members. Members with a seat get the tier's benefits. Members without one stay on Free-level access.
Team subscriptions are different from personal ones. They're priced per seat,
managed inside qBraid (not Stripe), and let you give benefits to specific
members. See [Personal vs Team
comparison](/v2/account/wallet/subscriptions#personal-vs-team).
## Plans
| Plan | Monthly per seat | Annual per seat | Compute per seat | Storage per seat |
| -------- | ---------------- | --------------- | --------------------- | ---------------- |
| Standard | \$20 | \$16 | 400 CPU hrs / month | 50 GB |
| Pro | \$100 | \$80 | 2,000 CPU hrs / month | 150 GB |
Standard includes seat assignment control.
## Create a team subscription
Open the workspace switcher in the top-left of the dashboard and select your
organization.
Click **Billing** in the sidebar.
In the top-right of the Billing page, click **Create Subscription**.
Choose **Standard** or **Pro**.
Monthly or Annual. Annual is about 20% cheaper.
Minimum is 2 seats.
Click **Subscribe** and finish checkout on Stripe.
When you return to the Billing page, the subscription is live.
## Upgrade
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
It's the default tab.
Click **Manage Team Subscription** on the subscription card.
It's the default tab in the dialog.
Click **Upgrade to Pro Now** (or Standard, if you're starting from Free).
The confirmation dialog shows the prorated cost. Click **Confirm**.
The new tier is live immediately. Stripe prorates the difference for the
current period.
## Downgrade
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
On the Subscription tab, click **Manage Team Subscription**.
It's the default tab.
The dialog lists exactly what you're giving up (fewer credits per seat,
fewer concurrent jobs).
Click **Schedule Downgrade** and confirm. You keep Pro features until the
end of the current billing period.
You can't change tier while a cancellation is scheduled. Reactivate the
subscription first.
## Switch between monthly and annual
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
On the Subscription tab, click **Manage Team Subscription**.
Click the **Billing Interval** tab inside the dialog.
Switch between Monthly and Annual.
* **Monthly to annual** applies right away with proration.
* **Annual to monthly** is scheduled for the end of your annual term.
## Add more seats
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
It's the default tab.
On the subscription card.
Type the number of seats to add.
Stripe charges a prorated amount for the rest of the period.
The new seats land in the pool immediately. See
[Seats](/v2/account/organizations/seats) for how to assign them.
## Cancel
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
On the Subscription tab, click **Manage Team Subscription**.
Scroll to the bottom of the dialog and click into the **Cancel Subscription** section.
All assigned seats are revoked at the end of the current period. Members keep any credits already in their wallets.
*"I understand this will revoke all `` assigned seats at end of period and cannot be undone."*
Click **Cancel Team Subscription**.
After that, the subscription goes into a **canceling** state with a banner showing the end date. You can resubscribe any time before that date and the cancellation goes away.
Once the period ends, every assigned seat is revoked and members drop to
Free-tier access. Credits sitting in member wallets are untouched.
# Transactions
Source: https://docs.qbraid.com/v2/account/organizations/transactions
Audit every credit movement in your organization, page through the history, and report issues on individual entries.
The **Transactions** tab on the [Billing page](https://account.qbraid.com/organizations/billing) logs every credit movement in the organization: purchases, transfers in and out, member-to-member transfers, seat-assignment credits, refunds, and deductions.
## Open the transactions table
Open the workspace switcher in the top-left of the dashboard and select your
organization.
Click **Billing** in the sidebar.
The third tab on the Billing page.
## What the table shows
Four columns: **Date**, **Type**, **Description**, and **Amount**.
The header shows the total transaction count next to the title (e.g. *"1,247 transactions"*). 20 rows are displayed per page; use the pagination controls at the bottom to move through them.
The org transactions tab is a paginated audit log. There aren't filter
dropdowns or a date-range picker here today. To narrow by user or transaction
type, page through or use the wallet's [Payment
History](/v2/account/wallet/payment-history) for finer filtering of your
qBraid Default workspace view.
## See more detail
A side panel opens with the full transaction details.
You'll see:
* The exact date and time.
* The full description.
* Amount and the running balance after the transaction.
* A **View Receipt** link if it's a Stripe purchase.
* A **Report Issue** button to flag the transaction with our team.
## Refresh
Click **Refresh** in the top-right to re-fetch the latest transactions without reloading the page.
## Report a problem with a transaction
Open the workspace switcher and select your organization.
Click **Billing** in the sidebar.
The third tab on the Billing page.
Page through the table to locate it.
Click the row to open the detail panel.
A pre-filled dialog opens with the transaction ID, type, amount, and date
already attached.
Explain what looks wrong.
Our team reviews and follows up.
For a CSV export of credit movements, run the [Payment
History](/v2/account/wallet/payment-history) export from the wallet of the
org's owner or admin (which uses the org context when you're switched into
it).
# Account Overview
Source: https://docs.qbraid.com/v2/account/overview
Your qBraid account at a glance: where to find profile, API keys, usage, billing, and organization settings.
Sign in at [account.qbraid.com](https://account.qbraid.com/). Your account dashboard is the central hub for everything that's not part of the product itself.
## qBraid Default area
Avatar, username, email, notification preferences, account deletion.
Create, rotate, and revoke programmatic access keys.
Credits, subscriptions, and payment history for your individual account.
Compute hours, storage, AI chat quota, and credit consumption.
## Organization area
Available when you've switched into an org from the workspace switcher.
Invite, manage, and remove people. Change roles and suspend access.
Assign and revoke team subscription seats.
Buy credits, transfer to members, approve requests.
Buy a team plan, change tier, or cancel.
Audit log of every credit movement with CSV export.
Logo, member defaults, danger zone.
## qBraid Default vs organizations
qBraid gives every user one **qBraid Default** workspace and access to any **organizations** they've created or been invited to.
* **qBraid Default** is your individual workspace. You buy credits for yourself, hold a personal subscription, and manage everything through Stripe.
* **Organizations** are team workspaces. The org pays for a team subscription, the org admin distributes seats and credits to members, and members can request credits.
The same Wallet, Profile, and Usage pages look different depending on which workspace is active. For example, the Wallet shows:
| In qBraid Default | In an organization |
| ------------------------------------------------ | ---------------------------------------------------------------------------------- |
| Buy credits panel | (hidden; admins buy from [Org Billing](/v2/account/organizations/credits) instead) |
| Personal subscription tiers and Subscribe button | (hidden; see [Team Subscriptions](/v2/account/organizations/subscriptions)) |
| "Manage Plan" link to Stripe portal | (not applicable) |
| (no request flow) | Request credits button |
## Switching between workspaces
Use the workspace switcher in the top-left of the dashboard to move between qBraid Default and any organizations you belong to. Everything on the page (Wallet, Members, Billing, Settings) applies to whichever workspace is selected, so double-check before destructive actions.
# Profile
Source: https://docs.qbraid.com/v2/account/profile
Edit your name, photo, notification preferences; manage your organizations; or request to delete your account.
The [Profile page](https://account.qbraid.com/account/profile) is where you manage everything about you as a user (separate from your billing and your API keys).
Open it from the sidebar under **Account > Profile**.
The page is laid out as a grid of cards. Each card handles one job.
## Profile hero
Top of the page. Shows your avatar, display name, email, and username. If your email is verified, you'll see a green **Verified** badge next to your name.
## Account status card
A summary of your account state:
* **Status.** `Active` or `Inactive`.
* **Organizations.** How many orgs you're a member of.
* **Email.** Whether your email is verified.
## Personal Information
### Edit your username
Click **Account > Profile** in the sidebar.
The button is in the Personal Information card.
Your `@handle`. Must be unique.
Click **Save Changes**. Click **Cancel** if you want to revert.
Email is read-only on this page. To change it, contact support.
## Organizations card
Lists every organization you belong to. Each card shows:
* The org name and initials.
* Your role(s) in that org (Owner, Admin, Manager, Member).
* The org's subscription tier (if any).
### Switch to an org
Click **Account > Profile** in the sidebar.
Lists every org you're in.
You'll see a toast confirming the switch. The rest of the dashboard updates
to that org's context.
### Leave an org
Click **Account > Profile** in the sidebar.
In the Organizations card.
The small link below each org card. Owners don't see this option.
Your access is revoked immediately.
Owners can't leave their own org. They need to transfer ownership or delete the org first.
Leaving an org is immediate. You lose access to its resources right away.
Org-allocated credits in your wallet return to the org pool.
### Search
If you're in more than 5 orgs, a search field appears above the list. Type any part of an org name to filter.
## Integrations
Connect external accounts to enable extra features. Currently shows:
* **Open Quantum.** Run quantum jobs on your own Open Quantum credits. See [Open Quantum integration](/v2/account/integrations/openquantum) for the full setup.
## Notification Preferences
Toggle which kinds of emails you want from qBraid (announcements, billing receipts, security alerts, etc.). Changes save automatically.
## Danger Zone: Delete Account
At the very bottom of the page, in a red-bordered card.
### Submit a deletion request
Open the workspace switcher and select **qBraid Default**.
Click **Account > Profile** in the sidebar.
At the very bottom of the page, with a red border.
Type why you're deleting your account in the **Reason** field (max 500
characters).
Tick the box: *"I understand this action cannot be undone."*
Click **Submit Deletion Request**.
Your request goes into a **Pending Review** state. Our team reviews and processes deletions within 2 to 3 business days.
### Cancel a pending request
Open the workspace switcher and select **qBraid Default**.
Click **Account > Profile** in the sidebar.
It now shows the pending request's date and reason.
The request is removed and your account stays active.
Once a deletion request is processed, your account, data, and any organization
you're the sole owner of are permanently removed. There's no recovery after
that point.
# Usage
Source: https://docs.qbraid.com/v2/account/usage
Track your compute hours, storage, AI chat quota, and credit consumption.
The [Usage page](https://account.qbraid.com/account/usage) shows how much you've consumed across all qBraid resources: compute hours, storage, AI chat, and credits.
Open it from the sidebar under **Account > Usage**.
The page has two tabs: **Overview** and **Session History**. A **Refresh** button in the top-right pulls the latest data on demand.
## Understanding Your Usage
A blue info card at the top explains the two main resource types:
* **Compute hours.** Allocated monthly with your subscription. Calculated from vCPU count × session duration on CPU instances. When exhausted, you can auto-switch to credits or stop sessions.
* **Credits.** Used for GPU instances always, and for CPU sessions when compute hours are exhausted. GPU sessions charge credits directly based on the compute profile rate.
For full details on how compute hours work, see [Usage Limits](/v2/lab/user-guide/usage-limits).
## Overview tab
A grid of four stat cards across the top:
| Card | What it shows |
| --------------------- | ------------------------------------------------------------------ |
| **Available Credits** | Your current credit balance. |
| **Compute Hours** | Used vs quota, with a progress bar and renewal countdown. |
| **Storage** | Used in GB vs quota. Bar turns yellow above 75% and red above 90%. |
| **Credits Charged** | Total credits spent over the selected time range. |
Below the stat cards you'll find:
* **Time-range filter.** Switch between 7, 14, 30, or 90 days for the charts and totals.
* **Compute hour usage chart.** Daily compute hours over time.
* **Credit charge breakdown.** What you spent credits on (compute, GPUs, jobs, AI).
* **Resource usage table.** Per-service breakdown of credits charged.
### Outstanding Balance alert
If your usage went past your balance, a red alert appears at the top of the Overview tab:
> You have an outstanding balance of `` Cr. Please add credits to continue using compute resources. Your sessions may be terminated until this balance is cleared.
Settle it by purchasing credits from the [Wallet](/v2/account/wallet/buy-credits).
## Download your files
Your Lab files live in cloud storage, and you can download them straight from the dashboard — no Lab session required. This is the way to retrieve your work when you can't launch Lab, for example when your [compute hours are exhausted](/v2/lab/user-guide/troubleshooting#launch-failed-compute-hours-exhausted).
1. Click **Dashboard** in the left sidebar.
2. Scroll to the **Usage Overview** section at the bottom of the page.
3. Click **Download Files** in the top-right of that section.
## Session History tab
A chronological list of every Lab session you've started: when it ran, what instance type, how long it lasted, and how many credits/compute hours it consumed. Useful for spotting:
* Sessions you forgot to stop.
* Spikes that drained your quota.
* Per-session breakdowns when you're reconciling charges.
## AI Chat usage
If you use AI chat in Lab, your AI quota tracking shows up alongside the compute and storage cards. The quota resets monthly with your subscription. Going over the included quota either pauses AI chat until reset or charges credits, depending on your plan tier.
For AI quota details, see [AI > Usage Quotas](/v2/ai/user-guide/usage-quotas).
## Refresh and exports
* Click **Refresh** in the top-right to pull the latest data without a full page reload.
* For a deeper audit (every individual charge, with Stripe IDs), use [Wallet > Payment History](/v2/account/wallet/payment-history) or [Organizations > Transactions](/v2/account/organizations/transactions).
# Buy Credits
Source: https://docs.qbraid.com/v2/account/wallet/buy-credits
Top up your credit balance with a preset or custom amount, and apply a promo code at checkout.
You can buy credits any time as a one-time top-up. They're priced at \$0.01 per credit and work across every pay-as-you-go resource on qBraid.
Buying credits is only available in your **qBraid Default** workspace (the
default). If you're working inside an organization, the Buy credits section is
hidden and you'll see a [Request
credits](/v2/account/organizations/credit-requests) button instead. Org admins
buy credits for the team from [Organizations >
Credits](/v2/account/organizations/credits#buy-credits-for-the-org).
## How to buy
Open the workspace switcher in the top-left of the dashboard and select
**qBraid Default**.
Click **Account > Wallet** in the sidebar, or open
[account.qbraid.com/account/wallet](https://account.qbraid.com/account/wallet)
directly.
The section sits below the Credits Overview card.
Click a preset card, or type a dollar amount in **Custom**. The credit count
updates as you type.
Drop the code into the field next to **Purchase**.
Stripe handles checkout in a new tab.
When you come back to the wallet, the credits are already in your balance.
The exchange rate (like `$1 = 100 credits`) appears next to the **Buy
credits** heading. If qBraid changes the rate later, credits you already own
keep their original value.
## Apply a promo code on the wallet
Open the workspace switcher and select **qBraid Default**.
Click **Account > Wallet** in the sidebar.
Pick a preset or enter a custom amount.
Drop it in the **Promo code** field next to **Purchase**.
Stripe applies the discount at checkout.
## Apply a promo code on Stripe Checkout
Follow the steps above without entering a promo code on the wallet.
A small text field appears.
The discount appears on the order summary.
Click **Complete Order**.
If the discount zeros out your total, the credits land in your wallet right away without a card charge.
## If you can't see the Purchase button
If you joined an organization and don't have purchase permissions, the **Purchase** button is replaced with **Request credits**. See [Credit Requests](/v2/account/organizations/credit-requests).
## Outstanding balance
If your usage went past your balance, a red banner appears at the top of the wallet with the amount you owe. Your next purchase covers that first, then any leftover credits get added to your wallet.
# Overview
Source: https://docs.qbraid.com/v2/account/wallet/overview
What you see when you open your wallet: balance, usage stats, and where to go next.
Your wallet is where you track credits, monitor usage, and find every other billing-related page.
Open it from the sidebar under **Account > Wallet**, or visit [account.qbraid.com/account/wallet](https://account.qbraid.com/account/wallet).
The wallet looks different depending on which workspace you're in. In **qBraid
Default** (the default workspace), you see Buy credits and personal
subscription tiers. In an **organization**, those sections are hidden and you
instead see a **Request credits** button. See [qBraid Default vs
organizations](/v2/account/overview#qbraid-default-vs-organizations) for the
full breakdown.
## Credits Overview card
The card at the top of the page is the snapshot.
* **qBraid credits.** Your balance. One credit is worth \$0.01. Use them on any pay-as-you-go resource (QPUs, GPUs, simulators, AI).
* **Current Plan.** Your subscription tier and billing interval. A **Canceling** badge appears here if you've scheduled a cancellation.
* **Last 7 days usage.** A small chart of recent credit burn.
## Usage stats (30 days)
Below the balance you'll see three numbers.
| Stat | What it means |
| ------------- | ---------------------------------------------------------------------------------------------------------- |
| **Burn rate** | Credits used in the last 30 days divided by 30, shown as a per-day number. |
| **Added** | Credits added in the last 30 days from purchases, transfers, or promo codes. |
| **Runway** | How many days your balance will last at your current burn rate. Red under 30, amber under 90, green above. |
## All-time totals
The bottom row shows lifetime numbers: total added, total deducted, and net change. Useful for big-picture audits.
## Outstanding balance
If your usage went past your balance, a red banner appears at the top with the amount you owe. Your next purchase or transfer pays off that balance first, and any leftover credits land in your wallet.
## Where to next
* Add funds: [Buy Credits](/v2/account/wallet/buy-credits).
* Pick a plan: [Personal Subscriptions](/v2/account/wallet/subscriptions).
* Ask your org admin for credits: [Credit Requests](/v2/account/organizations/credit-requests).
* Audit your charges: [Payment History](/v2/account/wallet/payment-history).
# Payment History
Source: https://docs.qbraid.com/v2/account/wallet/payment-history
See every transaction on your account, filter by type or category, search, and export to CSV.
The bottom of the wallet page lists every transaction in this workspace: purchases, transfers in and out, deductions, promo redemptions, and refunds.
## Filter the table
Three controls sit above the table:
* **Search.** Free-text search across descriptions.
* **Filter by type.** Purchase, transfer, deduction, promo, refund, etc.
* **Filter by category.** Narrow further to specific business categories.
Active filters show as removable badges below the inputs (with a **Clear all** button when more than one is set). The total row count updates as you filter, so you can see at a glance how many transactions match.
There's no date-range filter today. Use search or the Type and Category
dropdowns to narrow what you see.
## See more detail
Click any row to expand it. You'll see:
* The Stripe invoice ID (linked) for purchases.
* The related user for transfers.
* The reason or note attached at the time.
## Refresh
Click **Refresh** above the table to pull the latest data without a full page reload.
## Export CSV
Click **Export CSV** to download the currently sorted and filtered rows. The file uses the current page's transactions and is named `transactions-.csv`. Columns: Date, Type, Category, Description, Amount, Balance.
The export is a one-page snapshot. If you have thousands of transactions and
need everything, scroll page-by-page or page through the table and export each
page.
## Legacy transactions
If you have transactions from before the current billing system, an **Old Transactions** button appears alongside the regular export. It downloads those older records in their original format.
Legacy and current transactions don't combine in the same file. Download both
if you need a complete history.
# Personal Subscriptions
Source: https://docs.qbraid.com/v2/account/wallet/subscriptions
Pick a plan for your own account, manage it through Stripe, or cancel anytime.
A personal subscription covers one user (you). It's completely separate from any team subscription your organization might have. For a side-by-side comparison, see [Personal vs Team](#personal-vs-team) at the bottom of this page.
Personal subscriptions are only available in your **qBraid Default** workspace
(the default). The **Available Plans** section is hidden when you're inside an
organization. To buy a team plan, go to [Organizations > Team
Subscriptions](/v2/account/organizations/subscriptions).
The **Available Plans** section of the wallet shows the plans you can buy.
## Free
*Explore quantum computing at your own pace.*
* **Price.** \$0
* **Compute.** 100 CPU hours / month
* **Storage.** 15 GB
## Standard · Popular
*More compute, more storage.*
* **Price.** \$20 / month, or \$192 / year (\$16 / month, save 20%)
* **Compute.** 400 CPU hours / month
* **Storage.** 50 GB
## Pro · Best value
*Unlock higher limits for serious research.*
* **Price.** \$100 / month, or \$960 / year (\$80 / month, save 20%)
* **Compute.** 2,000 CPU hours / month
* **Storage.** 150 GB
Use the **Monthly / Annual** toggle at the top of **Available Plans** to see
each price in either interval. Annual is about 20% cheaper.
## Subscribe
Open the workspace switcher in the top-left of the dashboard and select
**qBraid Default**.
Click **Account > Wallet** in the sidebar.
Below the Buy credits section.
Set the switch to **Monthly** or **Annual**.
On the plan you want.
Finish on Stripe Checkout in the new tab.
Back on the wallet, the new plan shows under **Current Plan** immediately.
## Manage your subscription
The Stripe Customer Portal handles every change to your plan (payment method, tier, interval, cancellation).
Open the workspace switcher and select **qBraid Default**.
Click **Account > Wallet** in the sidebar.
Under **Current Plan** at the top of the wallet. The Stripe portal opens in
a new tab.
Update payment method, download invoices, switch tier, change interval, or
cancel.
Personal plans are managed entirely through Stripe. There's no in-app dialog
for changing tier or canceling. Everything happens in the Stripe portal.
## Cancel
Open the workspace switcher and select **qBraid Default**.
Click **Account > Wallet** in the sidebar.
Under **Current Plan**. The Stripe portal opens in a new tab.
Inside the Stripe portal.
You keep all the benefits of your plan until the end of the period you've
paid for.
A **Canceling** badge shows up next to your plan name on the wallet until then. After that, you drop back to Free automatically.
## Personal vs Team
| | Personal (this page) | Team ([Organizations > Subscriptions](/v2/account/organizations/subscriptions)) |
| --------------------- | -------------------- | ------------------------------------------------------------------------------- |
| Who pays | You | The organization |
| Who gets the benefits | You | Anyone the org assigns a seat to |
| How it's priced | Per account | Per seat (minimum 2 seats) |
| Free tier | Yes, automatic | No, orgs start as Free until they subscribe |
| Compute included | Per account | Per seat |
| Storage included | Per account | Per seat |
| How to change a plan | Stripe portal | Manage Team Subscription dialog inside qBraid |
| How to cancel | Stripe portal | Cancel section in Manage Team Subscription |
| How to add capacity | Switch tiers | Add Seats |
| Distribute to others | N/A | Assign Seat, Transfer Credits |
You can hold a personal subscription and a seat in your org's team subscription at the same time. They run independently. Compute and storage allowances don't add up across plans, each tracks its own usage.
# AI Gateway
Source: https://docs.qbraid.com/v2/ai/integrations/ai-gateway
Call GPT and Claude models through the qBraid API with one API key.
The AI Gateway is an LLM proxy inside the qBraid API. It speaks the OpenAI
wire format and the Anthropic wire format. You keep your existing SDK and
change two things: the base URL and the key.
```text theme={"dark"}
https://api-v2.qbraid.com/api/v1/ai
```
Every request authenticates with your qBraid API key in the `X-API-Key`
header. Create a key at
[account.qbraid.com/account/api-keys](https://account.qbraid.com/account/api-keys).
The [API Keys guide](/v2/account/api-keys) explains how to manage and
rotate keys.
## Endpoints
| Endpoint | Wire format | Use with |
| -------------------------------- | ----------------------- | ------------------------------------ |
| `POST /chat/completions` | OpenAI Chat Completions | `openai` SDKs, LangChain, most tools |
| `POST /responses` | OpenAI Responses | newer `openai` SDKs, Codex CLI |
| `GET /models` | OpenAI | model discovery |
| `POST /v1/messages` | Anthropic Messages | `anthropic` SDKs, Claude Code |
| `POST /v1/messages/count_tokens` | Anthropic | token counting |
| `GET /quota` | qBraid | your remaining LLM quota |
Why the double `v1` on the Anthropic routes: Anthropic SDKs append
`/v1/messages` to whatever base URL you give them. The gateway matches that
shape, so the same base URL works for both wire formats.
## Quickstart
The Anthropic SDK sends the `x-api-key` header on its own, so its `api_key`
argument takes your qBraid key directly. The OpenAI SDK only sends a bearer
header, so give it the key through `default_headers` instead.
```bash curl theme={"dark"}
curl https://api-v2.qbraid.com/api/v1/ai/chat/completions \
-H "X-API-Key: $QBRAID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [{ "role": "user", "content": "Explain the GHZ state in two sentences." }]
}'
```
```python Python (OpenAI SDK) theme={"dark"}
from openai import OpenAI
client = OpenAI(
base_url="https://api-v2.qbraid.com/api/v1/ai",
api_key="unused", # the gateway reads X-API-Key below
default_headers={"X-API-Key": QBRAID_API_KEY},
)
response = client.chat.completions.create(
model="gpt-5.5",
messages=[{"role": "user", "content": "Explain the GHZ state in two sentences."}],
)
print(response.choices[0].message.content)
```
```typescript TypeScript (OpenAI SDK) theme={"dark"}
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api-v2.qbraid.com/api/v1/ai",
apiKey: "unused", // the gateway reads X-API-Key below
defaultHeaders: { "X-API-Key": process.env.QBRAID_API_KEY },
});
const response = await client.chat.completions.create({
model: "gpt-5.5",
messages: [
{ role: "user", content: "Explain the GHZ state in two sentences." },
],
});
console.log(response.choices[0].message.content);
```
```python Python (Anthropic SDK) theme={"dark"}
from anthropic import Anthropic
client = Anthropic(
base_url="https://api-v2.qbraid.com/api/v1/ai",
api_key=QBRAID_API_KEY, # the SDK sends this as x-api-key
)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain the GHZ state in two sentences."}],
)
print(response.content[0].text)
```
## Use with Claude Code
Claude Code talks the Anthropic wire format, so it can run on your qBraid
quota. Set the base URL and your key, then start it:
```bash theme={"dark"}
export ANTHROPIC_BASE_URL="https://api-v2.qbraid.com/api/v1/ai"
export ANTHROPIC_API_KEY="$QBRAID_API_KEY"
claude
```
`ANTHROPIC_API_KEY` reaches the gateway as the `x-api-key` header.
## Available models
The `/models` endpoint is the source of truth. Model lineups change, so
query it rather than trusting a list you read somewhere:
```bash theme={"dark"}
curl https://api-v2.qbraid.com/api/v1/ai/models \
-H "X-API-Key: $QBRAID_API_KEY"
```
The lineup and pay-as-you-go pricing as of August 2026, in USD per
million tokens:
| Model | Served by | Input \$/M | Output \$/M | Notes |
| ------------------- | ------------ | ---------- | ----------- | -------------------- |
| `gpt-5.6-sol` | Azure OpenAI | 5.00 | 30.00 | cutting-edge default |
| `gpt-5.6-terra` | Azure OpenAI | 2.50 | 15.00 | experimental |
| `gpt-5.6-luna` | Azure OpenAI | 1.00 | 6.00 | experimental |
| `gpt-5.5` | Azure OpenAI | 5.00 | 30.00 | strong default |
| `gpt-5.4` | Azure OpenAI | 2.50 | 15.00 | balanced |
| `gpt-5.4-mini` | Azure OpenAI | 0.75 | 4.50 | fast |
| `gpt-5.4-nano` | Azure OpenAI | 0.20 | 1.25 | fastest, cheapest |
| `gpt-5.3-codex` | Azure OpenAI | 1.75 | 14.00 | code-tuned |
| `claude-opus-5` | AWS Bedrock | 5.00 | 25.00 | most capable |
| `claude-opus-4-8` | AWS Bedrock | 5.00 | 25.00 | capable |
| `claude-sonnet-4-6` | AWS Bedrock | 3.00 | 15.00 | balanced |
| `claude-haiku-4-5` | AWS Bedrock | 1.00 | 5.00 | fastest |
Costs draw from your plan's monthly AI quota first, then from credits at
100 credits = \$1. [Usage Quotas](/v2/ai/user-guide/usage-quotas) explains
the switch-over.
Aliases such as `openai/gpt-5.5` and `qbraid/claude-opus-5` resolve to the
same models. Configs written for other gateways usually work unchanged.
## Check your quota
```bash theme={"dark"}
curl https://api-v2.qbraid.com/api/v1/ai/quota \
-H "X-API-Key: $QBRAID_API_KEY"
```
Returns your LLM subscription state and remaining quota. Streaming
responses also carry standard rate-limit headers.
The gateway gives your code raw model access. If you want an AI client to act
on the qBraid platform itself, with tools for devices, jobs, and credits,
connect the [MCP Server](/v2/ai/integrations/mcp-server). The two work well
together.
# MCP Server
Source: https://docs.qbraid.com/v2/ai/integrations/mcp-server
Manage quantum devices, jobs, environments, and credits from Claude, Claude Code, ChatGPT, or Perplexity.
The qBraid MCP server gives AI clients live access to the qBraid platform
through the [Model Context Protocol](https://modelcontextprotocol.io). Connect
it once and your AI assistant can browse quantum devices, estimate costs,
submit and track jobs, and manage your account.
```text theme={"dark"}
https://mcp.qbraid.com/mcp
```
The server uses Streamable HTTP transport and authenticates through OAuth
with your qBraid account. No API key is needed.
## Connect your client
Custom connectors need a Pro, Max, Team, or Enterprise plan.
Click [**Add qBraid to Claude**](https://claude.ai/new?modal=add-custom-connector\&connectorName=qBraid\&connectorUrl=https%3A%2F%2Fmcp.qbraid.com%2Fmcp#settings/customize-connectors).
The **Add custom connector** dialog opens with the name and server
URL already filled in. Claude shows a notice that an external link
suggested the connector. That is expected. Review the URL and click
**Add**.
If the dialog doesn't open, go to **Settings → Connectors → Add
custom connector** and paste the URL manually:
```text theme={"dark"}
https://mcp.qbraid.com/mcp
```
Click **Connect** and sign in with your qBraid account.
In any conversation, open the **search and tools** menu and toggle
**qBraid** on. The same connector also works in the Claude desktop
app.
Add the server from your terminal:
```bash theme={"dark"}
claude mcp add --transport http qbraid https://mcp.qbraid.com/mcp
```
Inside Claude Code, run `/mcp`, select **qbraid**, and complete the OAuth
sign-in in your browser.
Verify the connection:
```bash theme={"dark"}
claude mcp list
```
Add `--scope user` to share the server across all your projects.
Custom connectors require developer mode on a paid plan.
Open [developer mode settings](https://chatgpt.com/#settings/Security?section=developer-mode)
and turn on **Developer mode**.
Click [**create a connector**](https://chatgpt.com/plugins#settings/Connectors?create-connector=true\&redirectAfter=%2Fplugins)
to open the creation dialog, name it `qBraid`, and paste the MCP
server URL:
```text theme={"dark"}
https://mcp.qbraid.com/mcp
```
Choose **OAuth** as the authentication method, save, and sign in with
your qBraid account.
Custom connectors need a Pro, Max, or Enterprise plan.
Go to [Perplexity connector settings](https://www.perplexity.ai/account/connectors)
and click **+ Custom connector**, then select **Remote**.
Name it `qBraid` and paste the MCP server URL:
```text theme={"dark"}
https://mcp.qbraid.com/mcp
```
Choose **OAuth** authentication and **Streamable HTTP** transport.
Save, then sign in with your qBraid account.
Click [**Add qBraid to Cursor**](cursor://anysphere.cursor-deeplink/mcp/install?name=qbraid\&config=eyJ1cmwiOiJodHRwczovL21jcC5xYnJhaWQuY29tL21jcCJ9)
to install in one click, or add this to `~/.cursor/mcp.json` yourself:
```json theme={"dark"}
{
"mcpServers": {
"qbraid": { "url": "https://mcp.qbraid.com/mcp" }
}
}
```
Cursor detects the server, runs the OAuth sign-in, and picks up the tools
automatically.
Click [**Add qBraid to VS Code**](https://vscode.dev/redirect/mcp/install?name=qbraid\&config=%7B%22name%22%3A%22qbraid%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.qbraid.com%2Fmcp%22%7D)
to install in one click, or add this to `.vscode/mcp.json` in your
workspace:
```json theme={"dark"}
{
"servers": {
"qbraid": { "type": "http", "url": "https://mcp.qbraid.com/mcp" }
}
}
```
VS Code prompts for the OAuth sign-in the first time Copilot uses the
server.
Add the server, then sign in:
```bash theme={"dark"}
codex mcp add qbraid --url https://mcp.qbraid.com/mcp
codex mcp login qbraid
```
The second command opens the OAuth sign-in in your browser. Verify with
`codex mcp list`.
Any MCP client that supports remote servers over Streamable HTTP with
OAuth can connect. Point it at:
```text theme={"dark"}
https://mcp.qbraid.com/mcp
```
The server supports OAuth dynamic client registration, so most clients
complete sign-in automatically after you add the URL.
## Available tools
| Category | Example tools |
| ----------------- | ------------------------------------------------------------------------------------------ |
| Quantum devices | `list_quantum_devices`, `get_device_details`, `get_device_calibrations` |
| Quantum jobs | `submit_quantum_job`, `estimate_job_cost`, `get_job_result`, `get_job_circuit_diagram` |
| Circuits | `validate_qasm`, `parse_qasm`, `convert_qasm`, `simulate_circuit` |
| Compute | `list_compute_profiles`, `start_server`, `stop_server`, `list_compute_instances` |
| Environments | `list_environments`, `get_environment_details`, `build_kernel` |
| Credits & billing | `get_my_credit_balance`, `list_billing_transactions`, `purchase_personal_credits` |
| Organizations | `get_organization_details`, `invite_organization_members`, `transfer_organization_credits` |
| Learning (qBook) | `list_qbook_articles`, `create_qbook_course`, `enroll_in_qbook_course` |
| Files | `list_files`, `read_file` |
| Support | `create_support_ticket`, `list_support_tickets` |
Connected clients list the full catalog automatically. Read-only tools run
without confirmation; tools that spend credits or change state report
clearly what they will do first.
## Example prompts
Once connected, try:
* "List the QPUs online right now and estimate the cost of 1000 shots of a 5-qubit GHZ circuit on the cheapest one."
* "Show my quantum jobs from this week and explain why any failed."
* "How many credits do I have left, and what did I spend the most on this month?"
* "Validate this QASM circuit and draw its diagram."
Prefer working inside the qBraid platform? [AI
Chat](/v2/ai/user-guide/ai-chat) and [Agent
Mode](/v2/ai/user-guide/agent-mode) use this same MCP server with no setup.
# Agent Mode
Source: https://docs.qbraid.com/v2/ai/user-guide/agent-mode
Execute quantum computing workflows through natural language
Agent Mode transforms qBraid AI from a conversational assistant into an **execution engine**. Instead of just answering questions, the AI takes direct action — writing and running code, submitting quantum jobs to real hardware, and analyzing results. Tell it what you want to accomplish, and it handles the implementation while keeping you in control with approval checkpoints for critical actions.
## What is Agent Mode?
Agent Mode gives the AI the ability to act within your qBraid environment. It connects directly to your qBraid VS Code instance, where it can write code, execute scripts, install packages, and run quantum experiments — all driven by your natural language instructions. For sensitive operations like submitting jobs to quantum hardware, the agent asks for your approval before proceeding.
**With Agent Mode, the AI can:**
* Write and execute Python code in your VS Code environment
* Build quantum circuits using any supported framework (Qiskit, Cirq, PennyLane, etc.)
* Submit jobs to QPUs and simulators (with your approval), then analyze the results
* Debug errors, refactor code, and iterate until the task is complete
* Chain multiple operations into end-to-end quantum workflows
## Enabling Agent Mode
To enable Agent Mode:
1. Open the AI Chat panel in qBraid Lab
2. Select the **Agent Mode** toggle at the top of the chat interface
3. Click on **Connect to Agent**
4. The chat will indicate when Agent Mode is active
Agent Mode actions consume additional AI credits compared to standard chat
queries. See [Usage Quotas](/v2/ai/user-guide/usage-quotas) for details.
## Complete Workflow Example
Let's walk through a complete quantum computing workflow using Agent Mode — from exploring devices to retrieving job results.
### Step 1: Query Device Details
Start by asking the AI about available quantum devices.
**Your prompt:**
```
What simulators are currently available on qBraid?
I want something fast for testing a 2-qubit circuit.
```
**The AI will:**
* Query the qBraid device API in real-time
* Filter for available simulators
* Recommend options based on your requirements
* Display pricing and expected execution times
### Step 2: Select a Device
Based on the AI's recommendations, specify which device you want to use.
**Your prompt:**
```
Let's use the AWS state vector simulator for this.
```
**The AI will:**
* Confirm the device selection
* Display the device ID for reference
* Prepare for job submission
### Step 3: Submit a Bell State Circuit
Now instruct the AI to submit a quantum circuit to the selected device.
**Your prompt:**
```
Submit a Bell state circuit to this device.
Use 1024 shots and let me know when it's done.
```
**The AI will:**
* Generate the Bell state circuit (H gate on qubit 0, CNOT on qubits 0-1)
* Submit the job to the selected simulator
* Return the job ID for tracking
* Optionally wait for completion
### Step 4: Verify Job in Sidebar
The submitted job will appear in the Quantum Jobs sidebar for visual tracking.
You can:
* View the job status in the **Jobs** sidebar
* Click on the job for detailed metadata
* Use the agent to cancel if needed
### Step 5: Retrieve Results
Once the job completes, ask the AI for the results.
**Your prompt:**
```
What are the results of my Bell state job?
```
**The AI will:**
* Fetch the job results from the qBraid API
* Display measurement counts
* Provide analysis of the results
Agent Mode capabilities are continuously expanding. Check back for new actions
or request features through [qBraid support](mailto:contact@qbraid.com).
## Best Practices
Begin with basic queries before chaining complex workflows. This helps you understand how the AI interprets your requests.
Use action words like "submit," "run," "get," or "cancel" to make your intent
clear.
Include details like shot count, device preferences, and circuit
specifications in your prompts.
Reference job IDs when asking about specific jobs to avoid ambiguity.
## What's Next
Learn about [Usage Quotas](/v2/ai/user-guide/usage-quotas) to understand how AI Chat and Agent Mode usage is tracked and how to manage your monthly allocation.
# AI Chat
Source: https://docs.qbraid.com/v2/ai/user-guide/ai-chat
Your intelligent assistant for all things qBraid and quantum computing
qBraid AI Chat is the most capable AI assistant for quantum computing on the qBraid platform. Whether you're exploring quantum devices, tracking your jobs, or planning your learning journey, the AI Chat provides intelligent, context-aware responses powered by our in-house MCP server.
## How It Works
The AI Chat is powered by qBraid's [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) server, which provides the underlying language model with crucial real-time context about:
* Live quantum device statuses and specifications
* Your quantum job history and results
* qBraid platform capabilities and documentation
* Quantum computing concepts and best practices
This means you get accurate, up-to-date answers rather than responses based on potentially outdated training data.
## Accessing AI Chat
1. Login to [account.qbraid.com](https://account.qbraid.com/)
2. Click the **AI Chat** icon in the left sidebar
3. Type your question and press Enter
## Example Use Cases
Explore what you can accomplish with AI Chat through these example prompts:
### Exploring Quantum Devices
Ask the AI about available quantum devices, their specifications, and current status.
**Example prompt:**
```
What quantum devices are currently available on qBraid?
I'm looking for a device with at least 20 qubits that supports
native two-qubit gates. Can you compare the options and their pricing?
```
The AI will provide:
* List of matching devices with specifications
* Current online/offline status
* Queue depths and estimated wait times
* Pricing in qBraid credits
* Recommendations based on your requirements
### Managing Quantum Jobs
Get insights into your quantum job history, check statuses, and retrieve results.
**Example prompt:**
```
Show me my recent quantum jobs from the past week.
Which ones have completed successfully and what were their results?
Were there any failures I should investigate?
```
The AI will provide:
* Summary of your recent jobs
* Status breakdown (completed, failed, running, queued)
* Results and measurement counts for completed jobs
* Error analysis for failed jobs
* Cost breakdown in credits
### Personalized Learning
Get customized recommendations for learning quantum computing based on your background and goals.
**Example prompt:**
```
I'm a software developer with Python experience but new to quantum computing.
I want to learn how to run variational algorithms on real hardware.
Can you create a learning pathway for me using qBraid resources?
```
The AI will provide:
* Structured learning path tailored to your experience
* Recommended qBraid tutorials and notebooks
* Suggested environments to install
* Hands-on exercises with increasing complexity
* Resources for specific topics (VQE, QAOA, etc.)
## Tips for Effective Prompts
Instead of "Tell me about devices," try "What's the qubit count and connectivity of the IonQ Aria device?"
Mention your experience level, the quantum framework you're using (Qiskit,
Cirq, etc.), and your end goal.
The AI maintains conversation context, so you can ask clarifying questions or
dive deeper into any topic.
Ask for code snippets in your preferred framework — the AI can generate working examples for qBraid Lab.
## What's Next
Ready to go beyond chat? Learn how [Agent Mode](/v2/ai/user-guide/agent-mode) enables you to execute complete quantum workflows directly from natural language commands.
# Overview
Source: https://docs.qbraid.com/v2/ai/user-guide/overview
*AI-powered quantum computing with qBraid*
At qBraid, we believe that the future of quantum computing should be accessible to everyone. Our AI integration embodies this philosophy by removing barriers between researchers, developers, and the powerful quantum resources they need.
qBraid AI serves as your intelligent companion throughout the quantum computing journey:
* **Explore** — Discover quantum devices, compare specifications, and understand pricing without navigating complex documentation.
* **Learn** — Get personalized guidance on quantum concepts, algorithms, and best practices tailored to your experience level.
* **Execute** — Run quantum workflows directly from natural language, from circuit design to job submission and result analysis.
## Getting Started
Access qBraid AI directly from qBraid account dashboard:
1. Login to your [qBraid account](https://account.qbraid.com/)
2. Click on the **AI** icon in the left sidebar
3. Start a conversation with your quantum AI assistant
## Key Features
Conversational AI assistant with deep knowledge of quantum computing and
qBraid's ecosystem.
Execute complete quantum workflows through natural language commands.
## Powered by qBraid's MCP Server
Our AI is powered by an in-house [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) server that provides the underlying model with rich, real-time context about:
* **Quantum Devices** — Live status, specifications, queue depths, and pricing for 20+ QPUs and simulators
* **Quantum Jobs** — Full visibility into your quantum job history, statuses, and results coupled with job execution capabilities
* **qBraid Learning** - Access to qBraid courses, tutorials, and documentation
* **qBraid Ecosystem** — Deep knowledge of environments, SDKs, and platform capabilities
This architecture ensures that every response is grounded in accurate, up-to-date information rather than stale training data.
New to qBraid? Check out our [Getting
Started](/v2/lab/user-guide/getting-started) guide to launch your first Lab
instance.
# Usage Quotas
Source: https://docs.qbraid.com/v2/ai/user-guide/usage-quotas
Understanding AI usage limits and credit consumption on qBraid
qBraid provides a default monthly quota for AI chats, powered by qBraid credits. This guide explains how your AI quota works and how to track your usage.
## Plan Quotas
Your monthly AI quota depends on your subscription plan:
| Plan | Monthly AI quota | Auto-switch default |
| ------------ | -------------------- | ------------------- |
| **Free** | \$5 (500 credits) | off |
| **Standard** | \$15 (1,500 credits) | on |
| **Pro** | \$50 (5,000 credits) | on |
Auto-switch starts off on the Free plan on purpose: a free user should never
burn credits on AI by surprise. Turn it on yourself if you want messages to
keep working past the quota.
Complex queries with longer responses consume more input / output tokens and
will fill up your quota faster.
## Usage Costs
AI usage is metered by tokens, not by message. Each model has a price per
million input and output tokens, and each message costs the tokens it
actually uses. Three things raise the cost of a message:
* **Longer prompts and longer answers.** More tokens in, more tokens out.
* **Bigger models.** Frontier models price higher per token than fast models.
* **Agent Mode.** Tool calls add context to the conversation, so agent runs
use more tokens than plain chat.
Token costs convert to credits at 100 credits = \$1.
## When the quota runs out
Every message draws from your monthly quota first. What happens next depends
on one toggle, **Auto-switch to Credits**, and your credit balance:
1. **Quota remaining.** The message is covered by your plan. Credits are
untouched.
2. **Quota exhausted, auto-switch on, credits available.** The message bills
your credit balance instead. A message that lands exactly on the boundary
splits: the remainder of the quota covers part, credits cover the rest.
3. **Quota exhausted, auto-switch off.** The request fails with "AI chat
quota exhausted. Enable credit spending to continue." Turn the toggle on
or wait for the monthly reset.
4. **Quota exhausted, auto-switch on, no credits.** The request fails with
"AI chat quota exhausted and no credits available." Buy credits or wait
for the reset.
Two details worth knowing:
* A leftover below one cent counts as exhausted. The panel then shows 100%
used, and the next message already follows the rules above.
* In a team organization, the admin's auto-switch setting wins. A member
cannot spend org credits that an admin has locked, and the usage panel
shows the effective policy, not your own toggle.
### A worked example
A Free-plan user has used 80% of the monthly quota. The usage panel shows
80% used and "can send". Both are true. 20% remains, so the next message is
allowed.
Now the user pastes a long paper and asks `gpt-5.5` for a detailed summary.
At this model's token rates, the message works out to:
* Input: 200,000 tokens = 20% of the monthly quota
* Output: 15,000 tokens = 9%
* Message cost: 29% of the quota, against the 20% that remains
Here is the part that surprises people. The quota check runs before the
message. The cost is only known after the model answers. The remaining 20%
is enough to admit the message, so it runs, and the bill comes to 29%. The
extra 9% has to land somewhere.
With auto-switch on and credits in the wallet, the quota covers its last
20% and the wallet pays the 9% overflow as credits. The message succeeds.
The panel moves to 100% used, and later messages run on credits alone.
With auto-switch off, or with an empty wallet, the request fails. Free
accounts start with auto-switch off, so this is the default experience.
The fix is the same either way: turn on **Auto-switch to Credits** and keep
some credits, or wait for the monthly reset.
The lesson: one long message can cost more than days of short ones. Cost
follows tokens, so a single long paper on a frontier model can outweigh a
week of ordinary chat.
## Tracking Your Usage
Monitor your AI usage in real-time from your account dashboard:
1. Navigate to your [account dashboard](https://account.qbraid.com/)
2. Go to **Account > Usage > AI Chat Usage**
3. View your current consumption and remaining quota
The usage panel displays:
* **Monthly Quota** — Progress bar showing percentage of your monthly AI quota used
* **Status** — Whether you're currently using your quota or credits
* **Renews** — The date when your monthly quota resets
* **Can Send** — Whether you can currently send AI agent messages
* **Auto-switch to Credits** — Toggle to continue chatting with credits when your quota runs out
## Tips for Efficient Usage
Ask multiple related questions in a single prompt rather than separate messages to reduce credit consumption.
Save Agent Mode for execution tasks. Use regular AI Chat for exploration and
learning.
The AI remembers your conversation, so you don't need to repeat context in follow-up questions.
## Upgrading Your Plan
Need more AI usage? Visit your [subscription settings](https://account.qbraid.com/account/wallet) to upgrade your plan, or [contact us](mailto:contact@qbraid.com) for enterprise plans with custom AI allocations.
***
For complete details on all qBraid usage limits including compute hours and
disk storage, see [Usage Limits](/v2/lab/user-guide/usage-limits).
# qbraid-algorithms
Source: https://docs.qbraid.com/v2/algorithms/api-reference/qbraid-algorithms
# CLI
Source: https://docs.qbraid.com/v2/algorithms/user-guide/cli
## CLI Usage
qBraid Algorithms includes a command-line interface (CLI) for generating quantum algorithm subroutines.
These subroutines can either be saved in `.qasm` files or displayed directly in the terminal, providing
users with a convenient way to access and utilize quantum algorithms.
### Installation
To use the CLI, install with CLI dependencies:
```bash theme={"dark"}
pip install "qbraid-algorithms[cli]"
```
Or install from source:
```bash theme={"dark"}
pip install -e ".[cli]"
```
### Generate Subroutines
Generate quantum algorithm subroutines that can be included in other circuits:
* Generate QFT subroutine for 4 qubits
```bash Command theme={"dark"}
qbraid-algorithms generate qft --qubits 4 --show
```
```output Output theme={"dark"}
QFT subroutine for 4 qubits generated successfully.
Output: /path/to/qft.qasm
Generated QASM:
--------------------------------------------------
OPENQASM 3.0;
include "stdgates.inc";
def qft(qubit[4] q) {
int n = 4;
for int[16] i in [0:n - 1] {
h q[i];
for int[16] j in [i + 1:n - 1] {
int[16] k = j - i;
cp(2 * pi / (1 << (k + 1))) q[j], q[i];
}
}
for int[16] i in [0:(n >> 1) - 1] {
swap q[i], q[n - i - 1];
}
}
--------------------------------------------------
```
* Generate IQFT subroutine for 3 qubits with custom name and show the circuit
```bash Command theme={"dark"}
qbraid-algorithms generate iqft -q 3 -o my_iqft.qasm --gate-name my_iqft --show
```
```output Output theme={"dark"}
QASM:
--------------------------------------------------
OPENQASM 3.0;
include "stdgates.inc";
def my_iqft(qubit[3] q) {
int n = 3;
for int[16] i in [0:n-1] {
int[16] target = n - i - 1;
for int[16] j in [0:(n - target - 2)] {
int[16] control = n - j - 1;
int[16] k = control - target;
cp(-2 * pi / (1 << (k + 1))) q[control], q[target];
}
h q[target];
}
for int[16] i in [0:(n >> 1) - 1] {
swap q[i], q[n - i - 1];
}
}
--------------------------------------------------
```
* Generate only the oracle for Bernstein-Vazirani
```bash Command theme={"dark"}
qbraid-algorithms generate bernvaz -s "1001" --oracle-only --show
```
```output Output theme={"dark"}
Generating Bernstein-Vazirani oracle for secret '1001'...
Bernstein-Vazirani oracle generated successfully.
Output: /path/to/oracle.qasm
Secret string: 1001
Qubits needed: 4 + 1 ancilla
Generated QASM:
--------------------------------------------------
OPENQASM 3.0;
include "stdgates.inc";
def oracle(qubit[4] q, qubit[1] ancilla) {
int[32] s = 9;
int[16] n = 4;
for int i in [0:n - 1] {
if ((s >> i) & 1) {
cx q[i], ancilla[0];
}
}
}
--------------------------------------------------
```
* Generate a QPE subroutine for phase estimation:
```bash Command theme={"dark"}
qbraid-algorithms generate qpe --unitary-file gate.qasm --qubits 3 --show
```
```output Output theme={"dark"}
QPE subroutine for 3 qubits generated successfully.
Unitary file: /path/to/gate.qasm
Output: /path/to/qpe.qasm
Generated QASM:
--------------------------------------------------
OPENQASM 3.0;
include "stdgates.inc";
include "iqft.qasm";
gate custom_t q {
p(pi / 4) q;
}
gate CU a, b {
ctrl @ custom_t a, b;
}
def qpe(qubit[3] q, qubit[1] psi) {
int n = 3;
for int i in [0:n-1] {
h q[i];
}
for int j in [0:n-1] {
int[16] k = 1 << j;
for int m in [0:k-1] {
CU q[j], psi[0];
}
}
iqft(q);
}
--------------------------------------------------
```
### Help
Get help for any command:
```bash theme={"dark"}
qbraid-algorithms --help
qbraid-algorithms generate --help
qbraid-algorithms generate qft --help
qbraid-algorithms generate iqft --help
qbraid-algorithms generate bernvaz --help
```
### Examples
#### Complete Workflow
1. Generate a QFT subroutine:
```bash theme={"dark"}
qbraid-algorithms generate qft --qubits 3
```
2. Generate a Bernstein-Vazirani oracle and view it:
```bash theme={"dark"}
qbraid-algorithms generate bernvaz --secret "101" --oracle-only --show
```
3. Generate an IQFT circuit with custom output:
```bash theme={"dark"}
qbraid-algorithms generate iqft --qubits 4 --output my_iqft_4.qasm --show
```
# Usage Examples
Source: https://docs.qbraid.com/v2/algorithms/user-guide/examples
## Key Features: Load algorithms as PyQASM modules and QASM files
qBraid Algorithms provides a collection of quantum algorithms that can be loaded
as [PyQASM](/v2/pyqasm/user-guide/overview) modules, or
you can generate .qasm files to use them as subroutines in your own circuits.
### Loading Algorithms as PyQASM Modules
To load an algorithm as a [PyQASM module](/v2/pyqasm/user-guide/overview#the-qasmmodule-object), use the `load_algorithm` function from the `qbraid_algorithms` package, passing algorithm-specific parameters. For example, to load the Quantum Fourier Transform (QFT) algorithm:
```python theme={"dark"}
from qbraid_algorithms import qft
qft_module = qft.load_algorithm(3) # Load QFT for 3 qubits
```
Now, you can perform operations with the PyQASM module, such as [unrolling](/v2/pyqasm/user-guide/examples#inlining-and-unrolling), and
converting back to a QASM string:
```python theme={"dark"}
qft_module.unroll()
qasm_str = pyqasm.dumps(qft_module)
```
### Loading Algorithms as `.qasm` Files
In order to utilize algorithms as subroutines in your own circuits, use the
`generate_subroutine` function for your desired algorithm. By passing algorithm-specific parameters, and optionally a desired output path, you can
generate a .qasm file containing a subroutine for the parameterized circuit. For
example, to generate a QFT subroutine for 4 qubits:
```python theme={"dark"}
from qbraid_algorithms import qft, iqft
path = "path/to/output" # Specify your desired output path
qft.generate_subroutine(4) # Generate 4-qubit QFT in the current directory
iqft.generate_subroutine(4, path=path) # Generate 4-qubit IQFT in specified path
```
To utilize the generated subroutine in your own circuit, include the generated
.qasm file, and call the subroutine on a qubit register of the size specified
when generating the subroutine. For example, after running
```python theme={"dark"}
qft.generate_subroutine(4)
```
you can append `include "qft.qasm";` to your OpenQASM file, and call the
subroutine. For example:
```qasm theme={"dark"}
OPENQASM 3.0;
include "qft.qasm";
qubit[4] q;
bit[4] c;
qft(q);
measure q -> c;
```
# Overview
Source: https://docs.qbraid.com/v2/algorithms/user-guide/overview
*Quantum Algorithms Library.*
Python package for building, simulating, and benchmarking hybrid quantum-classical algorithms.
## Installation
qbraid-algorithms requires Python 3.11 or greater, and can be installed with pip as follows:
```bash theme={"dark"}
pip install qbraid-algorithms
```
### Install from source
You can also install from source by cloning this repository and running a pip install command
in the root directory of the repository:
```bash theme={"dark"}
git clone https://github.com/qBraid/qbraid-algorithms.git
cd qbraid-algorithms
pip3 install .
```
## Check version
You can view the version of qbraid-algorithms you have installed within a Python shell as follows:
```python theme={"dark"}
import qbraid_algorithms
qbraid_algorithms.__version__
```
## Supported Algorithms
```python Usage theme={"dark"}
from qbraid_algorithms import bernstein_vazirani
secret_key = '01001'
algo = bernstein_vazirani.generate_program(secret_key)
print(algo)
```
```output Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
def bernvaz(qubit[5] q, qubit[1] ancilla) {
int[32] s = 18;
int[16] n = 5;
for int i in [0:n - 1] {
h q[i];
}
x ancilla[0];
h ancilla[0];
for int i in [0:n - 1] {
if (s >> i & 1) {
cx q[i], ancilla[0];
}
}
for int i in [0:n - 1] {
h q[i];
}
}
qubit[5] q;
qubit[1] ancilla;
bit[5] b;
bernvaz(q, ancilla);
b = measure q;
```
```python Usage theme={"dark"}
from qbraid_algorithms import qft
algo = qft.generate_program(4)
print(algo)
```
```output Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
def qft(qubit[4] q) {
int n = 4;
for int[16] i in [0:n - 1] {
h q[i];
for int[16] j in [i + 1:n - 1] {
int[16] k = j - i;
cp(2 * pi / (1 << k + 1)) q[j], q[i];
}
}
for int[16] i in [0:(n >> 1) - 1] {
swap q[i], q[n - i - 1];
}
}
qubit[4] q;
bit[4] b;
qft(q);
b = measure q;
```
```python Usage theme={"dark"}
from qbraid_algorithms import iqft
algo = iqft.generate_program(4)
print(algo)
```
```output Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
def iqft(qubit[4] q) {
int n = 4;
for int[16] i in [0:n - 1] {
int[16] target = n - i - 1;
for int[16] j in [0:n - target - 2] {
int[16] control = n - j - 1;
int[16] k = control - target;
cp(-2 * pi / (1 << k + 1)) q[control], q[target];
}
h q[target];
}
for int[16] i in [0:(n >> 1) - 1] {
swap q[i], q[n - i - 1];
}
}
qubit[4] q;
bit[4] b;
iqft(q);
b = measure q;
```
```python Usage theme={"dark"}
from qbraid_algorithms import qpe
"""
Path to a qasm file defining the unitary gate U.
Eg. -
OPENQASM 3.0;
include "stdgates.inc";
gate custom_t q {
p(pi/4) q;
}
"""
unitary_filepath = "gate.qasm"
"""
Path to a qasm file defining the eigenstate preparation gate.
Eg. -
OPENQASM 3.0;
include "stdgates.inc";
gate prep q {
x q;
}
"""
eigen_state_filepath = "eigen_state.qasm"
algo = qpe.generate_program(num_qubits = 4, unitary_filepath=unitary_filepath,
psi_filepath=eigen_state_filepath)
print(algo)
```
```output Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
def iqft(qubit[4] q) {
int n = 4;
for int[16] i in [0:n - 1] {
int[16] target = n - i - 1;
for int[16] j in [0:n - target - 2] {
int[16] control = n - j - 1;
int[16] k = control - target;
cp(-2 * pi / (1 << k + 1)) q[control], q[target];
}
h q[target];
}
for int[16] i in [0:(n >> 1) - 1] {
swap q[i], q[n - i - 1];
}
}
gate custom_t q {
p(pi / 4) q;
}
gate CU a, b {
ctrl @ custom_t a, b;
}
def qpe(qubit[4] q, qubit[1] psi) {
int n = 4;
for int i in [0:n - 1] {
h q[i];
}
for int j in [0:n - 1] {
int[16] k = 1 << j;
for int m in [0:k - 1] {
CU q[j], psi[0];
}
}
iqft(q);
}
qubit[4] q;
qubit[1] psi;
bit[4] b;
gate prep_eigenstate q {
x q;
}
qpe(q, psi);
b = measure q;
```
See [API Reference](https://qbraid.github.io/qbraid-algorithms/api/qbraid_algorithms.html) for complete functionality of each algorithm module.
# qbraid
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid
The qBraid CLI — A unified interface for AI agents, quantum computing, Python environments, and cloud compute.
Version: [0.13.2](https://pypi.org/project/qbraid-cli/0.13.2)
Whether you're running AI coding agents locally or on cloud servers,
managing quantum experiments across providers, or setting up reproducible
Python environments — qBraid brings it all into one CLI.
**Usage**:
```console theme={"dark"}
$ qbraid [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `-v, --version`: Show the version and exit.
* `--install-completion`: Install completion for the current shell.
* `--show-completion`: Show completion for the current shell, to copy it or customize the installation.
* `--help`: Show this message and exit.
**Commands**:
* `admin`: CI/CD commands for qBraid maintainers.
* `agents`: Manage AI coding agent sessions.
* `compute`: Manage cloud compute and SSH.
* `lab`: Launch qBraid Lab (JupyterLab).
* `configure`: Configure CLI settings.
* `account`: Manage qBraid account.
* `devices`: Browse quantum devices.
* `jobs`: Submit and track quantum computing jobs.
* `projects`: Browse and install qBraid Hub projects.
* `skills`: Create, install, and share AI coding skills.
* `troubleshoot`: Diagnose qBraid platform issues.
* `envs`: Manage Python environments.
* `kernels`: Add, remove, and repair Jupyter kernels.
* `pip`: Run pip command in active qBraid environment.
# Configuration Files
Source: https://docs.qbraid.com/v2/cli/user-guide/config-files
Manage your qBraid account credentials and settings
qBraid uses configuration files to store your account credentials and settings. The main configuration file is called `qbraidrc`.
## The qbraidrc File
The `qbraidrc` file is an INI-format configuration file that stores your qBraid account credentials and settings. It is located in your home directory at:
* Linux/macOS: `~/.qbraid/qbraidrc`
* Windows: `C:\Users\\.qbraid\qbraidrc`
This configuration file is used by various qBraid tools including:
* qBraid-SDK
* qBraid-CLI
* qBraid Jupyter Lab extensions
* qBraid VS Code extensions
### File Structure
A typical `qbraidrc` file contains the following:
```ini theme={"dark"}
[default]
url = https://api-v2.qbraid.com/api/v1
api-key =
```
### Creating New Configuration File
To create a new `qbraidrc` file:
```
qbraid configure
```
This interactive command will:
1. Prompt you to enter your API key
2. Automatically set the qBraid API URL
3. Create the configuration file in the correct location
### Updating Configuration Values
To update a specific configuration value:
```
qbraid configure set
```
For example, to update your API key:
```
qbraid configure set api-key your-new-api-key
```
### Viewing Configuration Values
To view a specific configuration value:
```
qbraid configure get
```
For example:
```
qbraid configure get api-key
```
To view all configuration values in your default profile:
```
qbraid configure list
```
### Finding Your Configuration File
To locate your `qbraidrc` file programmatically:
```python theme={"dark"}
from pathlib import Path
config_path = Path.home() / ".qbraid" / "qbraidrc"
print(config_path)
```
## Security Note
Your API key provides access to your qBraid account. Keep it secure and never share it with others. If you believe your API key has been compromised, you can generate a new one from your qBraid account settings and update it using the CLI commands above.
See [API key security](/v2/account/api-keys#security-best-practices) for more information.
# Environments
Source: https://docs.qbraid.com/v2/cli/user-guide/environments
qBraid environments are simply [Python virtual environments](https://docs.python.org/3/library/venv.html) in disguise.
The qBraid CLI provides convenient short-cuts to activate and install packages into your qBraid environments, however all
of the traditional commands to activate and manipulate Python venvs still apply. For example, you can activate an
environment using the [source command](https://docs.python.org/3/library/venv.html#how-venvs-work):
```bash theme={"dark"}
$ source ~/.qbraid/environments//pyenv/bin/activate
```
If you're working in terminal, you don't specifically need to activate a qBraid environment, as you can just specify the full path
to that environment's Python interpreter when invoking Python, e.g. `//bin/python`. The same is true for `pip` commands.
So if you are attempting to install new packages into a qBraid environment from terminal or inside a notebook, make sure to use the full
pip path.
Simply running `pip install ...` will default to `/opt/conda/bin/pip`, which will cause dependency conflicts, and the installed packages
will be overridden the next time you launch lab.
### Install package
From Launcher, under “Other”, click on Terminal. By default, your qBraid terminal opens using Python (and pip) from
`/opt/conda/bin`. Packages that are installed directly at this top-level will *not* persist between sessions. Instead,
use the qBraid CLI to list your environments,
```bash theme={"dark"}
$ qbraid envs list
# qbraid environments:
#
default /opt/.qbraid/environments/qbraid_000000
qsharp /opt/.qbraid/environments/qsharp_b54crn
qbraid_sdk /home/jovyan/.qbraid/environments/qbraid_sdk_9j9sjy
qiskit /home/jovyan/.qbraid/environments/qiskit_9y9siy
```
activate an environment, and perform your pip installs from there. For example, installing pytket into the qiskit environment:
```bash theme={"dark"}
$ qbraid envs activate qiskit
$ python -m pip install pytket
$ deactivate
```
# Magic Commands
Source: https://docs.qbraid.com/v2/cli/user-guide/magic-commands
You can access the CLI directly from within [Notebooks](/v2/lab/user-guide/notebooks)
using IPython [magic commands](https://ipython.readthedocs.io/en/stable/interactive/magics.html). First, configure
the qBraid magic command extension using:
```shell theme={"dark"}
$ qbraid configure magic
```
The above command can also be executed from within a Jupyter notebook using the `!` operator. Then, from within a
notebook cell, load the qBraid magic IPython extension using:
```python theme={"dark"}
In [1]: %load_ext qbraid_magic
```
Now you can continue to use the qBraid-CLI as normal from within your Jupyter notebook using the magic `%` operator, e.g.
```python theme={"dark"}
In [2]: %qbraid
In [3]: %qbraid --version
```
# Overview
Source: https://docs.qbraid.com/v2/cli/user-guide/overview
*Universal command line interface for interacting with all parts of the qBraid
platform.*
The **qBraid CLI** is a versatile command-line interface tool designed for seamless interaction
with qBraid cloud services and quantum software management tools.
## Getting Started
The qBraid-CLI comes pre-installed and pre-configured in qBraid Lab:
* [Launch qBraid Lab →](https://lab.qbraid.com/)
* [Make an account →](https://account.qbraid.com/)
For help, see qBraid Lab User Guide: [Getting Started](/v2/lab/user-guide/getting-started).
### Local Setup
If you prefer to use qBraid-CLI on your local machine, you can install it from PyPI with:
```bash theme={"dark"}
pip install qbraid-cli
```
qbraid-cli versions \<0.12 are *not* compatible with qBraid API V2. See [migration guide](/v2/api-reference/rest/migration).
To ensure compatibility with the new platform, use **qbraid-cli** ≥ 0.12.0.
To manage qBraid [environments](/v2/lab/user-guide/environments) using the CLI, you must also install the `envs` extra:
```bash theme={"dark"}
pip install 'qbraid-cli[envs]'
```
After installation, you must configure your account credentials to use the CLI locally:
1. Create a qBraid account or log in to your existing account by visiting
[account.qbraid.com](https://account.qbraid.com/)
2. Navigate to **Account** > **API Keys** in the left-sidebar, and then click "Create API Key".
3. Save your API key from step 2 in local configuration file `~/.qbraid/qbraidrc` using:
```bash theme={"dark"}
$ qbraid configure
```
**Important for Windows Users:**
If you encounter a warning like:
```bash theme={"dark"}
WARNING: The script qbraid.exe is installed in 'C:\Users\\AppData\Local\Packages\PythonSoftwareFoundation.Python.\LocalCache\local-packages\Python\Scripts' which is not on PATH.
```
You need to add the directory containing qbraid.exe to your system's PATH.
1. Copy the directory path provided in the warning.
2. Follow these steps:
* Open the Start Menu, search for "Environment Variables," and select "Edit the system environment variables."
* In the System Properties window, click the Environment Variables button.
* Under "User variables," locate the Path variable, click Edit, and add the copied directory.
3. Save the changes and restart your terminal.
## Basic Commands
```bash theme={"dark"}
$ qbraid
----------------------------------
* Welcome to the qBraid CLI! *
----------------------------------
____ _ _
__ _| __ ) _ __ __ _(_) __| |
/ _` | _ \| '__/ _` | |/ _` |
| (_| | |_) | | | (_| | | (_| |
\__,_|____/|_| \__,_|_|\__,_|
|_|
- Use 'qbraid --help' to see available commands.
- Use 'qbraid --version' to see the current version.
Reference Docs: https://docs.qbraid.com/v2/cli/api-reference/qbraid
```
A qBraid CLI command has the following structure:
```bash theme={"dark"}
$ qbraid [options and parameters]
```
For example, to list installed environments, the command would be:
```bash theme={"dark"}
$ qbraid envs list
```
To view help documentation, use one of the following:
```bash theme={"dark"}
$ qbraid --help
$ qbraid --help
$ qbraid --help
```
For example:
```bash theme={"dark"}
$ qbraid --help
Usage: qbraid [OPTIONS] COMMAND [ARGS]...
The qBraid CLI — A unified interface for AI agents, quantum computing,
Python environments, and cloud compute.
Options
--version -v Show the version and exit.
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help -h Show this message and exit.
Commands
admin CI/CD commands for qBraid maintainers.
agents Manage AI coding agent sessions.
compute Manage cloud compute and SSH.
lab Launch qBraid Lab (JupyterLab).
configure Configure CLI settings.
account Manage qBraid account.
devices Browse quantum devices.
jobs Submit and track quantum computing jobs.
projects Browse and install qBraid Hub projects.
skills Create, install, and share AI coding skills.
troubleshoot Diagnose qBraid platform issues.
envs Manage Python environments.
kernels Add, remove, and repair Jupyter kernels.
pip Run pip command in active qBraid environment.
```
To get the version of the qBraid CLI:
```bash theme={"dark"}
$ qbraid --version
```
# qbraid-core
Source: https://docs.qbraid.com/v2/core/api-reference/qbraid-core
# qbraid-core-js
Source: https://docs.qbraid.com/v2/core/api-reference/qbraid-core-js
# Overview
Source: https://docs.qbraid.com/v2/core/user-guide/overview
*Python client for developing software with qBraid cloud services*
qBraid-CORE is a Python library providing core abstractions for software development within the qBraid ecosystem, and a low-level interface to a growing array of qBraid cloud services. The qbraid-core package forms the foundational base for the [qBraid CLI](https://pypi.org/project/qbraid-cli/), the [qBraid SDK](https://pypi.org/project/qbraid/), and the
[qBraid Lab](https://pypi.org/project/qbraid-lab/).
You can find the latest, most up to date, documentation [here](https://sdk.qbraid.com/qbraid-core/), including a list of services that are supported.
## Getting Started
You can install qbraid-core from PyPI with:
```bash theme={"dark"}
pip install qbraid-core
```
qbraid-core versions \<0.2.0 are *not* compatible with qBraid API V2. See [migration guide](/v2/api-reference/rest/migration).
To ensure compatibility with the new platform, use **qbraid-core** ≥ 0.2.0.
### Local configuration
After installing qbraid-core, you must configure your account credentials:
1. Create a qBraid account or log in to your existing account by visiting
[account.qbraid.com](https://account.qbraid.com/)
2. Navigate to **Account** > **API Keys** in the left-sidebar, and then click "Create API Key".
3. Save your API key from step 2 in local [configuration file](/v2/cli/user-guide/config-files) `~/.qbraid/qbraidrc`, where `~` corresponds to your home (`$HOME`) directory:
```ini theme={"dark"}
[default]
api-key = YOUR_KEY
url = https://api-v2.qbraid.com/api/v1
```
Or generate your `~/.qbraid/qbraidrc` file via the qbraid-core Python interface:
```python theme={"dark"}
>>> from qbraid_core import QbraidSessionV1
>>> session = QbraidSessionV1(api_key='API_KEY')
>>> session.save_config()
```
Other credential configuration methods can be found [here](/v2/cli/user-guide/overview#local-setup).
### Verify setup
After configuring your qBraid credentials, verify your setup by running the following from a Python interpreter:
```python theme={"dark"}
>>> from qbraid_core.services.runtime import QuantumRuntimeClient
>>> quantum_client = QuantumRuntimeClient()
>>> device_list = quantum_client.list_devices()
>>> for device in device_list:
... print(device.qrn)
```
## Community
* For feature requests and bug reports: [Submit an issue](https://github.com/qBraid/qBraid-Lab/issues)
* For discussions and/or specific questions about qBraid services, [join our discord community](https://discord.gg/KugF6Cnncm)
* For questions that are more suited for a forum, post to [Stack Overflow](https://stackoverflow.com/) with the [`qbraid`](https://stackoverflow.com/questions/tagged/qbraid) tag.
# FAQ
Source: https://docs.qbraid.com/v2/home/faq
Frequently asked questions about the V2 platform.
Only credits purchased with a credit card or transferred to you within an organization were migrated from the legacy platform. Promotional credits (e.g., those redeemed via access codes) did not carry over and have expired.
If your credits lived in an organization (purchased or transferred there), check that you're viewing that organization. "qBraid Default" in the upper left means you're in your default workspace. Click the organization name to open the dropdown, switch to the correct organization, and the balance will update.
Quantum jobs data was not migrated from the legacy platform. To get your old
jobs data, go to **Jobs** on your [account page](https://account.qbraid.com/)
and click **Download Old Jobs Data**. You can work with that data using the
qBraid-SDK. See [Legacy Jobs Retrieval](/v2/home/legacy-jobs) for details.
Lab uses a [file sync](/v2/lab/user-guide/file-sync) mechanism, so your files
can take a few minutes to appear after you launch. Once you're in Lab, check
the file sync indicator in the bottom toolbar to see progress. You can keep
working while files populate in the background; after a few minutes, your
files should be fully restored.
CPU hours measure usage as (number of vCPUs) × (hours used). For example, a
2-vCPU instance for 3 hours uses 6 CPU hours; an 8-vCPU instance for 2 hours
uses 16 CPU hours. See [Usage Limits](/v2/lab/user-guide/usage-limits) for
more.
You've used all the compute hours included with your plan for this month, and
you don't have credits set up to cover the session instead. Either [upgrade
your subscription](/v2/account/wallet/subscriptions#subscribe) for a larger
monthly allocation, or [buy
credits](/v2/account/wallet/buy-credits#how-to-buy) and turn on [auto-switch
to credits](/v2/lab/user-guide/usage-limits#auto-switch-to-credits) — both
steps are needed, since credits only cover CPU sessions when auto-switch is
on. Full walkthrough in
[Troubleshooting](/v2/lab/user-guide/troubleshooting#launch-failed-compute-hours-exhausted).
Your qBraid account has usage limits for AI chat and CPU hours. If you reach a
limit, you can "switch to credits" and keep using that service. Usage is then
billed in qBraid credits instead of counting against the quota. Those charges
appear on your account under **Transactions**.
After you launch Lab, [files can take a few minutes to sync](/v2/lab/user-guide/file-sync). When sync finishes, refresh the Environment Manager in the sidebar and your environments should appear.
If sync has completed and you've refreshed Environment Manager but it still says "Workspace Empty," you don't have any environments installed yet. Click **Add** in the Environment Manager, then [install an environment](/v2/lab/user-guide/environments#install-environment) to get started.
It means all GPU instances of that type are currently in use due to high
demand. Try again later or choose another instance type.
On qBraid, you can belong to multiple organizations (e.g., courses, teams, or your own qBraid Default workspace). Credits and context are per-organization—the name in the upper left shows which organization you're in, and switching it changes which credits and resources you see.
**qBraid Default** is your own workspace: your own credits and work, separate from any course or other org you've joined. Credits here are yours (purchased or held in your name), and you have full control with no shared management. Think of it as your individual sandbox on qBraid.
API keys authenticate you with qBraid services. When you launch Lab, we check
for credentials in `~/.qbraid/qbraidrc`. If none are found, we prompt you to
create an API key and save it there so Lab, extensions, and the qBraid-SDK
work without extra setup. For more on creating and managing keys, see [API
Keys](/v2/account/api-keys).
When you exhaust your CPU hours, "Launch Lab" is blocked, so it can feel like
you've lost access to your files. **Your files are still there**, and you
don't need to launch Lab to get them: go to **Dashboard** on your [account
page](https://account.qbraid.com/), scroll to **Usage Overview** at the
bottom, and click **Download Files**. See [Download your
files](/v2/account/usage#download-your-files). To start launching again,
either upgrade your plan or set up credits, as described in
[Troubleshooting](/v2/lab/user-guide/troubleshooting#launch-failed-compute-hours-exhausted).
No. To run jobs on IBM devices you must use your own IBM credentials. qBraid
does support direct access to other leading QPUs, including Rigetti, IQM, AQT,
IonQ, Pasqal, QuEra, and more. See the [full list of
devices](https://account.qbraid.com/devices) on your account page.
# qBraid Docs
Source: https://docs.qbraid.com/v2/home/introduction
Developer documentation and resources to fuel your quantum journey.
Web-based IDE optimized for quantum computing.
Quantum software extensions for code editors.
AI-enabled quantum computing tools.
Platform-agnostic quantum runtime framework.
Universal endpoints for submitting + managing quantum jobs.
Command-line tool for interacting with the qBraid platform.
qBraid-SDK extension leveraging LLVM-based quantum compiler.
Toolkit for OpenQASM program validation and compilation.
Low-level interface to a growing array of qBraid cloud services.
Library of quantum algorithm resources and tools.
Manage credits, API keys, organizations, and more.
Compute resources and pricing for CPUs, GPUs, and QPUs.
# Legacy Jobs Retrieval
Source: https://docs.qbraid.com/v2/home/legacy-jobs
Following the migration to qBraid's V2 platform, quantum jobs from the legacy platform are no longer directly accessible through the new API. However, you can download and work with your legacy jobs data using the instructions below.
**Schema Changes**
Legacy jobs will **not** match the schema of the new runtime jobs in API V2. If you're working with both legacy and new jobs, be aware that their data structures and field names will differ.
For more details, see the [API Reference for V2 Jobs](/v2/api-reference/rest/get-quantum-jobs).
## Downloading legacy jobs data
1. Log in to your qBraid account at [account.qbraid.com](https://account.qbraid.com/)
2. Navigate to the **Jobs** page
3. Look for the **Download Old Jobs** option
4. Click to export all your legacy quantum jobs as a JSON file
5. Save the JSON file to your local machine
## Displaying legacy jobs with qBraid-SDK
Once you've downloaded your legacy jobs JSON file, you can view the jobs data using the qBraid-SDK.
### Prerequisites
Ensure you have qBraid-SDK version `0.10.2` installed:
```bash theme={"dark"}
pip install 'qbraid==0.10.2'
```
### Loading the data
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
# Path to the legacy jobs JSON file
legacy_jobs_path = 'legacy_jobs.json'
# Initialize the provider with the legacy jobs path
provider = QbraidProvider(legacy_jobs_path=legacy_jobs_path)
# Display the first 5 jobs
print(provider.display_jobs(max_results=5))
```
```python theme={"dark"}
Displaying 5/5 jobs matching query:
Job ID | Submitted | Status
--------- | ------------------------- | ---------------
| 2025-12-31 T17:52:46.850Z | COMPLETED
| 2025-11-06 T13:29:16.031Z | FAILED
| 2025-10-31 T17:52:44.674Z | CANCELLED
| 2025-10-31 T17:52:42.551Z | COMPLETED
| 2025-10-28 T09:32:11.508Z | COMPLETED
```
### Functionality and limitations
Once the `QbraidProvider` is initialized with the legacy jobs JSON path, you can only use the `display_jobs()` method to display the jobs.
Methods that interface with the API, such as `get_device()`, will **not** work when using legacy jobs. This is because device information is not available through the old API endpoints.
For full functionality including device access and job submission, upgrade to
qBraid-SDK version **0.11.0 or greater** and use the new V2 API endpoints. See
the [V2 SDK documentation](/v2/sdk/user-guide/overview) for details.
## Retrieving job details with qbraid-core
For more detailed information about individual jobs, you can use the **qbraid-core** library, which provides direct access to job metadata and results.
### Prerequisites
Install qbraid-core version`0.1.50`:
```bash theme={"dark"}
pip install 'qbraid-core==0.1.50'
```
### Using QuantumClient
The `QuantumClient` accepts the same legacy jobs JSON path parameter and provides methods for retrieving detailed job information:
```python theme={"dark"}
from qbraid_core.services.quantum import QuantumClient
# Initialize the client with the legacy jobs JSON path
legacy_jobs_path = 'legacy_jobs.json'
client = QuantumClient(legacy_jobs_path=legacy_jobs_path)
# List all jobs
jobs_list = client.search_jobs()
# display the first 5 jobs
print(jobs_list[:5])
# Get detailed information for a specific job
job_id = jobs_list[0]['qbraidJobId']
job_details = client.get_job(job_id)
print(job_details)
```
### Available methods
* **`search_jobs()`** - Returns a list of all legacy jobs
* **`get_job(job_id)`** - Retrieves detailed metadata for a specific job
* **`get_job_result(job_id)`** - Fetches the results and measurements for a completed job
## Need help?
If you encounter any issues retrieving or working with your legacy jobs data, contact [contact@qbraid.com](mailto:contact@qbraid.com).
## Related
* [Migration Guide](/v2/home/migration) - Learn about the platform migration
* [V2 Documentation](/v2/home/introduction) - Get started with the new platform
* [qBraid-SDK Documentation](/v2/sdk) - Full SDK reference
# Migration Guide
Source: https://docs.qbraid.com/v2/home/migration
qBraid is migrating to a new Generation-2 platform. Below is what you need to know and what to do before and after the migration.
## Migration window
* **When:** Sunday, February 8th, 10:00 AM–2:00 PM CT (4 hours)
* **Impact:** The platform and all associated services will be unavailable during this window.
## What carries over
* **Files** — All of your files from the legacy platform will be migrated.
* **Credits** — All qBraid credits will carry over **except** promotional credits (e.g., credits from access codes). Only credits purchased with a credit card or transferred to you within an organization will be migrated.
* **Organizations** — Organizations will carry over with no service interruption.
## What does not carry over (and what to do)
### Quantum jobs
Quantum jobs data will **not** be migrated. On the new account **Jobs** page you will see a download option to export all legacy quantum jobs data as a JSON file. You can then load and work with that data (results and metadata) using the qBraid-SDK as usual.
For detailed instructions on retrieving and working with your legacy jobs, see the [Legacy Jobs Retrieval](/home/legacy-jobs) guide.
### Transactions
Transactions will **not** be migrated. You can download your legacy transactions as a JSON file from **Account** > **Wallet** on the new platform.
### Submitting jobs after migration
To submit quantum jobs on the new platform, use either the updated [quantum jobs and devices API endpoints](/v2/api-reference/user-guide/overview) or:
* **qBraid-SDK** version 0.11.0 or higher
* **qBraid-CORE** version 0.2.0 or higher
Older versions of the SDK and CORE are not compatible with the new API.
## Subscribers (Standard and Pro)
You will need to renew your qBraid subscription to keep access to Standard or Pro.
1. Log in to the new [account page](https://account.qbraid.com/).
2. Go to **Account** > **Wallet**.
3. Select your previous subscription tier and complete checkout.
Previous subscribers will receive an email with a promotional code for **1 month free** when they resubscribe.
## Beta users (V2 early access)
If you participated in the qBraid V2 beta: **all data on the current beta platform will be wiped** at migration (jobs, transactions, credits, and files). None of it will persist.
* Download any data you need before the migration.
* Use any remaining credits, or they will be lost.
## Questions
For questions about the migration or any of the above, contact [contact@qbraid.com](mailto:contact@qbraid.com).
# On-Demand Pricing
Source: https://docs.qbraid.com/v2/home/pricing
## Credits
Access to both classical and quantum computing resources is managed through the qBraid credits system, which operates on a pay-as-you-go basis.
Each qBraid credit is worth \$0.01 USD, so a quantum job costing \$3.80 would subtract 380 credits from your qBraid balance. Credits can be
[purchased](https://account.qbraid.com/account/wallet) from your account page, or [redeemed](/v2/account/wallet/buy-credits#apply-a-promo-code-on-the-wallet)
using a promo code. You can check your current credit balance on your [account page](https://account.qbraid.com/account/wallet), under
the "Profile" drop-down in the top right of [qBraid Lab](/v2/lab/user-guide/overview), or using the [qBraid-CLI](/v2/cli/api-reference/qbraid_account#qbraid-account-credits).
Prices for both classical and quantum computing resources are subject to change.
## Quantum Computers
There are three pricing components for on-demand use of a quantum computer, or quantum processing unit (QPU), on qBraid: a per-minute fee, per-task fee, and per-shot fee.
A shot is a single execution of a quantum algorithm on a QPU. For example, a shot is a single pass through each stage of a complete quantum circuit on a gate-based QPU from
IonQ, Rigetti, IQM, or AQT, or the time evolution of a Hamiltonian on a QPU from QuEra. A task is a sequence of repeated shots based on the same circuit design or Hamiltonian.
You define how many shots you want included in a task when you submit it.
### Pricing Structure
* Per-minute pricing: Applies to specific QPUs (e.g., Rigetti Cepheus-1-108Q, Pasqal Fresnel) where execution time is billed
* Per-task pricing: Consistent across most QPUs (typically 30 credits)
* Per-shot pricing: Varies by QPU type (\<1 to 8 credits for current offerings)
For gate-based QPUs, neither per-shot nor per-minute prices are affected by the number or type of gates used.
**Per-minute prices are a rate, not a minimum charge.** Execution time is
billed *pro rata* for the exact time your program runs — down to the
microsecond — so you are never rounded up to a whole minute, and there is no
minimum billable duration.
You are billed for the time your program actually executes, whatever that is. On
Rigetti's Cepheus-1-108Q, the 12,000 credits/minute rate works out to:
| Execution time | Cost |
| :------------- | :------------------------------ |
| 1 microsecond | 0.0002 credits (\$0.000002 USD) |
| 1 millisecond | 0.2 credits (\$0.002 USD) |
| 1 second | 200 credits (\$2.00 USD) |
| 1 minute | 12,000 credits (\$120.00 USD) |
In practice, jobs on Cepheus-1-108Q execute in tens to hundreds of milliseconds,
costing roughly \$0.05 to \$0.50. Cost scales with execution time — not with shot
count or the number of qubits used.
| Hardware Provider | QPU | Per-minute price | Per-task price | Per-shot price |
| :---------------- | :----------------- | :--------------- | :------------- | :------------- |
| AQT | IBEX Q1 | 0 credits | 30 credits | 2.35 credit |
| QuEra | Aquila | 0 credits | 30 credits | 1 credit |
| IQM | Garnet | 0 credits | 30 credits | 0.145 credits |
| IQM | Emerald | 0 credits | 30 credits | 0.16 credits |
| IonQ | Forte-1 | 0 credits | 30 credits | 8 credits |
| IonQ | Forte-Enterprise-1 | 0 credits | 30 credits | 8 credits |
| Rigetti (AWS) | Cepheus-1-108Q | 0 credits | 30 credits | 0.0425 credits |
| Rigetti (Direct) | Cepheus-1-108Q | 12000 credits | 0 credits | 0 credits |
| Pasqal | Fresnel | 500 credits | 0 credits | 0 credits |
### Run these QPUs for free with Open Quantum
IBEX Q1, Garnet, Emerald, Forte 1, Forte Enterprise 1, and Cepheus-1-108Q all cost **0 qBraid credits** through Open Quantum, and new accounts can claim **\$50 in free Spark credits**. See the [Open Quantum integration](/v2/account/integrations/openquantum) guide to create an account, claim the credits, and connect.
## Quantum Circuit Simulators
Verify your circuits using a simulator before you run it on a QPU, so you can fine-tune your circuit without incurring charges for QPU usage.
Although the results from running the circuit on a simulator may not be identical to the results from running the circuit on a QPU, you can
identify coding errors or configuration issues using a simulator.
| Provider | Name | Per-minute price | Access |
| ---------- | :------------------- | :--------------- | :------ |
| qBraid | QIR Simulator | 0 credits | public |
| AWS | DM1 | 7.5 credits | public |
| AWS | SV1 | 7.5 credits | public |
| Rigetti | QVM Simulator | 0 credits | public |
| IonQ | Simulator | 0 credits | public |
| Quantinuum | H2-1 Syntax checker | 0 credits | public |
| Quantinuum | H2-1 Emulator | 0 credits | public |
| Pasqal | Emulator | 25 credits | public |
| Quobly | Alloy Forge Emulator | 0 credits | limited |
| Equal1 | Bell-1 Simulator | 12.5 credits | limited |
qBraid offers ten on-demand quantum circuit simulators for testing quantum algorithms:
* qBraid QIR Simulator: employs sparse state vector simulation based on [arXiv:2105.01533](https://arxiv.org/abs/2105.01533).
* AWS State Vector 1 (SV1): serves as a general purpose quantum circuit simulator.
* AWS Density Matrix 1 (DM1): enables simulating the effect of noise on your circuits.
* Rigetti QVM Simulator: Quantum Virtual Machine (QVM) open-source simulator.
* IonQ Simulator: supports "ideal" simulations as well as noise models for harmony (legacy), aria-1, aria-2, forte-1, and forte-enterprise-1.
* Quantinuum H2-1 Syntax Checker: Validates quantum programs against the H2-1 compiler.
* Quantinuum H2-1 Emulator: Emulation tool containing a realistic noise model of the actual System Model H2 hardware.
* Pasqal Emulator: Neutral atom tensor network emulator. Runs on a cluster of DGX nodes with NVIDIA A100 GPUs.
* Equal1 Bell-1 Simulator: State-vector simulator supporting Equal1 Bell-1 QPU noise models; GPU acceleration available
* Quobly Alloy Forge Emulator: Emulator of the Quobly Alloy Pioneer 10 qubit QPU.
All simulators automatically scale resources to ensure high-performance. Billing is based on a fixed per-task cost plus the duration
of each simulation, charged per minute in one-millisecond increments, with a minimum billing duration of three seconds per simulation.
The QPU and simulator lists above are subject to change and may be incomplete
or include options that are no longer available. For the most complete and
up-to-date list of quantum devices, visit your [account
page](https://account.qbraid.com/devices).
## CPUs and GPUs
qBraid Lab offers two ways to access compute: **subscription instances** billed against your monthly CPU hours, and **on-demand instances** billed per minute in credits.
### Subscription Instances
Your subscription plan includes a monthly allocation of [CPU hours](/v2/lab/user-guide/usage-limits#compute-hours). The following CPU instances draw from that quota:
| Instance | vCPU | RAM | Credits/Min (Overdraft) |
| :------- | :--- | :--- | :---------------------- |
| Small | 2 | 4GB | 0.20 |
| Medium | 4 | 8GB | 0.40 |
| Large | 8 | 25GB | 0.80 |
If you exceed your monthly CPU hours, you can [auto-switch to credits](/v2/lab/user-guide/usage-limits#auto-switch-to-credits) — usage then bills at the per-minute credit rates shown above. These rates are consistent with on-demand pricing, so there is no difference in cost between overdraft and on-demand for the same compute. See [usage limits](/v2/lab/user-guide/usage-limits) for more details on compute hour tracking.
**Comparing CPU hours and credits.** A CPU hour counts vCPU × wall-clock time,
so a 2-vCPU Small for 3 hours and an 8-vCPU Large for 45 minutes each use 6
CPU hours. While you are within your monthly allocation, that usage costs no
credits. Once you are in overdraft, the rates above work out to **6 credits
per CPU hour** at every instance size — useful for estimating how far a credit
balance goes, or for weighing extra credits against a larger plan allocation.
Credits also cover QPUs, simulators, and GPU instances, none of which draw on
plan CPU hours.
### On-Demand Instances
On-demand instances are billed per minute in [credits](/v2/home/pricing#credits), with no subscription commitment required. This includes all GPU instances and additional high-spec CPU configurations. See [On-Demand Instances](/v2/lab/user-guide/on-demand-instances) for the full lifecycle guide.
**Subscription discounts on on-demand pricing:**
| Plan | Discount |
| :------- | :------- |
| Free | — |
| Standard | 10% off |
| Pro | 18% off |
Standard and Pro subscribers receive an automatic discount on all on-demand instances (GPUs and on-demand CPUs). The discount is applied at billing time — the effective rate is shown on your dashboard alongside the list price.
The **Slug** column is the profile identifier used with the [qBraid CLI](/v2/cli/api-reference/qbraid_compute). For example:
```console theme={"dark"}
$ qbraid compute up gpu-h100-sxm
```
#### GPU Instances
See [GPUs](/v2/lab/user-guide/gpus) for more details on launching and using GPU instances.
| Slug | GPU Type | VRAM | Credits/Min |
| :----------------- | :--------------------- | :----- | :---------- |
| `gpu-b200-8x` | 8x NVIDIA B200 | 1440GB | 111.50 |
| `gpu-b200-4x` | 4x NVIDIA B200 | 720GB | 56.58 |
| `gpu-b200-2x` | 2x NVIDIA B200 | 360GB | 28.72 |
| `gpu-b200` | 1x NVIDIA B200 | 180GB | 14.57 |
| `gpu-h200` | 1x NVIDIA H200 | 141GB | 9.15 |
| `gpu-h100-8x` | 8x NVIDIA H100 | 640GB | 66.50 |
| `gpu-h100-4x` | 4x NVIDIA H100 | 320GB | 34.08 |
| `gpu-h100-2x` | 2x NVIDIA H100 | 160GB | 17.47 |
| `gpu-h100-sxm` | 1x NVIDIA H100 SXM | 80GB | 8.95 |
| `gpu-gh200` | 1x NVIDIA GH200 | 96GB | 4.78 |
| `gpu-a100-8x` | 8x NVIDIA A100 | 320GB | 46.50 |
| `gpu-a100-4x` | 4x NVIDIA A100 | 160GB | 16.58 |
| `gpu-a100-2x` | 2x NVIDIA A100 | 80GB | 8.30 |
| `gpu-a100-sxm` | 1x NVIDIA A100 SXM | 80GB | 4.15 |
| `gpu-l40s` | 1x NVIDIA L40S | 48GB | 3.80 |
| `gpu-rtx-5090` | 1x NVIDIA RTX 5090 | 32GB | 2.07 |
| `gpu-a10` | 1x NVIDIA A10 | 24GB | 2.70 |
| `gpu-rtx-4090` | 1x NVIDIA RTX 4090 | 24GB | 1.45 |
| `gpu-rtx-6000-ada` | 1x NVIDIA RTX 6000 Ada | 48GB | 1.53 |
| `gpu-l4` | 1x NVIDIA L4 | 24GB | 0.82 |
#### CPU Instances
On-demand CPU instances offer higher-spec configurations beyond what subscription instances provide.
| Slug | vCPU | RAM | Credits/Min |
| :------------- | ---: | :---- | :---------- |
| `cpu-64v-256g` | 64 | 256GB | 6.40 |
| `cpu-32v-128g` | 32 | 128GB | 3.20 |
| `cpu-8v-32g` | 8 | 32GB | 0.80 |
| `cpu-4v-16g` | 4 | 16GB | 0.40 |
| `cpu-4v-6g` | 4 | 6GB | 0.25 |
Additional configurations may be available. Visit your [account
page](https://account.qbraid.com/dashboard) to see the full list of options.
# lab.qbraid.com
Source: https://docs.qbraid.com/v2/lab/launch
# Environment Manager
Source: https://docs.qbraid.com/v2/lab/user-guide/environments
Install, create, share, and manage Python virtual environments for quantum computing.
The [Environment Manager](https://youtu.be/LyavbzSkvRo) is a streamlined quantum software package and virtual
environment management system provided to qBraid end-users through a simple, intuitive graphical user interface.
It offers curated development environments over a range of quantum applications.
Click the **Environments** icon in the top-right of the Lab console to open the Environment Manager sidebar and view
your currently installed environments.
## Install environment
1. In the Environment Manager sidebar, click **Add** to view the environments available to install.
2. Environments are organized into **groups** (e.g. Qiskit, CUDA-Q, Pennylane). Click a group to view the environments it contains.
3. Choose an environment, expand its panel, and click **Install**. Once the installation has started, the panel is moved to the **Environments** tab. Click **Browse Environments** to return to the **Environments** tab and view its progress.
4. When the installation is complete, the environment panel's action button will switch from **Installing…** to **Add kernel**.
5. Click on the expanded environment tile to view its description, tags, package count, and Python version.
From here you can **Add kernel** or **Remove kernel**, and click **More…** to open the full environment editor.
### Filter environments
Use the toggle buttons at the top of the environment browser to switch between filter modes:
* **All** — show all available environment groups
* **Package** — filter by the primary quantum software package (Qiskit, Pennylane, Cirq, etc.)
* **Subject Matter** — filter by quantum computing domain (Chemistry, Machine Learning, Error Correction, etc.)
You can also type in the search bar to find environments by name or keyword. Use the **Discover via access code** field to add privately shared environments.
### Browse an environment group
Click on any environment group to view the environments inside it. Each environment tile shows its name,
description, tags, package count, and an **Install** button.
### Discover via access code
Environments shared via access code are not publicly listed. To install one of these private environments, first enter the access code
in the "Discover via access code" field and click the "**+**" button. This will add the environment to your sidebar. You can then enter
the environment name in the search bar or scroll to find it, and proceed with step 2 above to install it as usual.
If the environment doesn't appear in search results after entering the access code, click **Browse Environments** to go back to the
main sidebar, click the **Refresh** icon, and then click **Add** to return to the search page and browse for the environment again. If the
issue persists, ensure that you've entered the correct access code and are searching for the correct environment name. If you continue to
experience problems, please [contact our support team](mailto:contact@qbraid.com).
## Create environment
1. In the Environment Manager sidebar, click **Add**, then click **Create Environment**.
2. Give your custom environment a name, description, add tags, upload a logo, and specify any packages to install using
a `requirements.txt` format.
After clicking **Create**, a new environment panel is created for your custom environment. You can view the environment's
install progress by returning to **My Environments**.
3. Once the environment has been created and any package installations have finished, the environment panel's action button
will switch from **Installing…** to **Add kernel**, and the installed packages number will be updated.
4. Click on **More** to verify/view the environment's list of installed packages. You can use the search bar or scroll through
the Python package (pip) list to find the exact versions of all packages and package dependencies that were installed. From the
**More** pop-out, you can also install additional packages, remove packages, add/delete tags, and edit the environment's description.
### Clone environment
You can create a custom environment based on an existing one by cloning it. Clicking the **Clone** button will automatically populate the
`requirements.txt` file and other environment metadata in the "Custom Environment" form. This is useful, for example, if you want to make
and share changes to an environment you don't own.
## Environment editor
Click **More…** on any environment tile to open the full environment editor. From here you can:
* View and edit environment **metadata** (name, description, tags)
* Browse the full **packages list** with search, version info, and remove buttons
* **Add a package** — search PyPI and install the latest version
* **Cloud Sync** — sync the environment to the qBraid cloud for sharing
* **Share** the environment with other users via access code
## Upload to cloud
Before you can share or publish an environment, you must first upload it to the qBraid cloud.
In the environment editor, scroll to the **Cloud Sync** section and click **Upload to Cloud**.
Once uploaded, the status changes to "Environment is in the cloud" and the button becomes **Sync with Cloud**
for pushing future updates.
## Share environment
In qBraid Lab, you can share your custom environments with other users. This will make your environment visible under their **Add**
environment list. They can then install it, and run code using an exact copy of your environment. This includes support for quantum jobs
and any additional configurations.
To share a custom environment, first click the **More** button in the drop-down to open the environment editor. Then, under 'Share Environment',
select **Generate Access Code** to create a unique access code. Anyone with this code will be able to
[discover](#discover-via-access-code) and [install](#install-environment) the environment with read-only access.
When you share an environment via access code, you're creating a snapshot of your Python virtual environment
with its specific configurations and installed packages. This snapshot is uploaded to the qBraid cloud, creating a static version accessible
to other users. This shared version remains unchanged, even if you make updates to your local environment. To reflect any changes, you'd need
to generate a new access code and overwrite the existing version in the cloud.
Overwriting a shared environment doesn't affect versions that other users have already downloaded. Instead, an indicator notifies them
of the availability of an updated version. Currently, users must manually check for this indicator. Also, before installing an updated version,
users must uninstall their current one as maintaining multiple versions of a shared environment isn't supported.
## Publish environment
Make your custom environment accessible to the entire qBraid ecosystem by submitting a publish request. Once you've configured your environment to
meet your specifications, open the environment editor using the **More** button and select **Request to publish**. A qBraid admin will review and
test your environment as needed, providing feedback if adjustments are required. Once approved, the environment will be made public, and ownership
will be transferred to qBraid.
## Install new package
Find the environment into which you want to install the package, expand the environment panel using the carrot drop-down, and then click **More**.
At the bottom of the Python packages list, click **Add a package**. Type in the *exact* name of the package you wish to install into the "Add package…"
search bar, and then hit `Enter` (or click the search icon on the right). This will do a direct search through [PyPI](https://pypi.org/), and return the
*latest* version available to install. To accept, click on the package, and then click **Add**.
While installing, the environment action button will indicate **Installing…**. Once complete, Lab will notify with a pop-up.
Package installation is blocked while file sync is in progress. Wait for sync
to complete before installing or removing packages.
## Uninstall / cancel install environment
To uninstall an environment or cancel the installation of an environment, click on **More**, and then **Uninstall** or **Cancel Installation**.
Uninstalling an environment also removes its kernel from the launcher. Any
notebooks using that kernel will need to be switched to a different kernel.
## Activate environment (kernel)
Clicking **Add kernel** creates a new ipykernel, see [Add/remove kernels](/v2/lab/user-guide/kernels#add-and-remove-kernels) for more.
## Permissions
Some features, such as [publishing environments](#publish-environment), require an Enterprise-level subscription or partnership.
For more information, visit our [subscriptions page](https://account.qbraid.com/account/wallet) or contact us at [contact@qbraid.com](mailto:contact@qbraid.com).
# Intel® Quantum SDK
Source: https://docs.qbraid.com/v2/lab/user-guide/featured-environments/intel-quantum-sdk
How to set up and use the Intel® Quantum SDK in qBraid Lab
## Overview
The Intel® Quantum SDK is a C++-based platform for writing quantum programs as *quantum kernels* and
running them on Intel's high-performance quantum simulators. On qBraid Lab it is available as a
self-contained, pre-built environment: the `intel-quantum-compiler` (LLVM/Clang based), the Intel
Quantum Simulator and other backends, the Python interface (`intelqsdk.cbindings`), and all of the
required Intel runtime libraries are bundled together, so there is nothing to build or configure
yourself.
To cite the Intel® Quantum SDK, please reference:
> Khalate, P., Wu, X.-C., Premaratne, S., Hogaboam, J., Holmes, A., Schmitz, A., Guerreschi, G. G., Zou, X. & Matsuura, A. Y., [arXiv:2202.11142 (2022)](https://doi.org/10.48550/arXiv.2202.11142)
## Getting Started
The Intel® Quantum SDK environment is only available on the dedicated Intel Lab image. You must
launch that image first; the environment then becomes visible in the Environment Manager, where you
install it to get the Python virtual environment, the compiler, and the Jupyter kernel.
### Launch the Intel image
On your qBraid **Dashboard**, find the **Launch qBraid Lab** card and select the
**Small · Intel Quantum SDK** profile, then click **Launch**.
Pulling the Intel image may take 2-3 minutes the first time. The next time you launch Lab, it will
load much more quickly.
### Install the environment
1. In Lab, open the [Environment Manager](/v2/lab/user-guide/environments) sidebar and click **Add**
to browse the environments available to install.
2. Environments are organized into **groups**. Select the **Intel Quantum SDK** group — the
environment does not appear in the top-level list until you open its group.
3. Inside the group, expand the **intelqsdk** environment panel and click **Install**. Installation
adds a dedicated Python virtual environment under `~/.qbraid/environments/`.
Prefer the terminal? The same environment can be installed with the qBraid CLI. Use
`qbraid envs available` to look up the environment (listed as `intelqsdk`) and its slug, then install
it:
```bash theme={"dark"}
qbraid envs available # find the intelqsdk environment and its slug
qbraid envs install intelq_r7zabv # install by slug
```
Once installed, the environment is fully self-contained: the `intel-quantum-compiler`, the simulator
backends, the Intel MKL / MPI / OpenMP runtime libraries, and a dedicated Python interpreter all live
inside it. There is no separate `pip install` or `source` step, and nothing is drawn from a
system-wide Intel installation.
### Add the kernel
When the installation completes, the **intelqsdk** panel moves to the **Environments** tab and its
action button switches from **Installing…** to **Add kernel**. Click it to register the
**Python 3 \[Intel Quantum SDK]** Jupyter kernel, which you will select when running notebooks.
From the terminal, the kernel can instead be added with the CLI (find the environment ID with
`qbraid envs list`):
```bash theme={"dark"}
qbraid envs list # note the intelqsdk environment ID
qbraid kernels add
```
## Python Interface
The Python interface (`intelqsdk.cbindings`) lets you write a `quantum_kernel` in C++, compile it, and
drive it entirely from Python. When you use the **Python 3 \[Intel Quantum SDK]** kernel — or the
environment's `python3` — the compiler is placed on your `PATH` and the `IQSDK_SHARE` environment
variable points at the SDK installation, so you can locate the compiler without hard-coding any paths.
Before running a notebook, make sure the Intel® Quantum SDK kernel is
[active](/v2/lab/user-guide/kernels), and that it is
[selected](/v2/lab/user-guide/notebooks#switch-notebook-kernel) for the current notebook via the
kernel picker in the top-right of the menu bar (`Python 3 [Intel Quantum SDK]`).
The following example creates a two-qubit Bell state and prints the resulting probabilities:
```python theme={"dark"}
import os
import intelqsdk.cbindings as iqsdk
# The environment exposes the compiler location via IQSDK_SHARE.
compiler = os.path.join(os.environ["IQSDK_SHARE"], "intel-quantum-compiler")
num_qubits = 2
# Python-interpolated C++ source defining a Bell-state quantum kernel.
bell_source = f"""
#include
qbit q[{num_qubits}];
quantum_kernel void bell() {{
PrepZ(q[0]);
PrepZ(q[1]);
H(q[0]);
CNOT(q[0], q[1]);
}}
"""
with open("bell.cpp", "w", encoding="utf-8") as output_file:
output_file.write(bell_source)
# Compile to a shared object and register it under the name "bell_sdk".
iqsdk.compileProgram(compiler, "bell.cpp", "-s", "bell_sdk")
# Configure and start the full-state Intel Quantum Simulator.
iqs_config = iqsdk.IqsConfig()
iqs_config.num_qubits = num_qubits
iqs_config.simulation_type = "noiseless"
iqs_device = iqsdk.FullStateSimulator(iqs_config)
iqs_device.ready()
# Run the "bell" kernel defined in the C++ source above.
iqsdk.callCppFunction("bell", "bell_sdk")
# Collect references to the qubits and print their probabilities.
qbit_ref = iqsdk.RefVec()
for i in range(num_qubits):
qbit_ref.append(iqsdk.QbitRef("q", i, "bell_sdk").get_ref())
probabilities = iqs_device.getProbabilities(qbit_ref)
iqsdk.FullStateSimulator.displayProbabilities(probabilities, qbit_ref)
# Printing probability register of size 4
# |00> : 0.5 |10> : 0
# |01> : 0 |11> : 0.5
```
Ready-to-run examples are bundled inside the environment at `$IQSDK_SHARE/python-quantum-examples/`
(`run_ghz.py`, `run_qft.py`, `run_tfd_demo.py`). Copy one into your workspace to try it:
```python theme={"dark"}
import os
import shutil
shutil.copy(
os.path.join(os.environ["IQSDK_SHARE"], "python-quantum-examples", "run_ghz.py"),
".",
)
```
### OpenQASM support
The environment ships a source-to-source converter that turns OpenQASM 2.0 into Intel® Quantum SDK
C++. The translator requires Python >= 3.10 and currently supports OpenQASM 2.0 as described in
[arXiv:1707.03429](https://arxiv.org/abs/1707.03429).
From Python, use the `openqasm_bridge` to translate a circuit into a C++ `quantum_kernel` source that
you can compile exactly as above:
```python theme={"dark"}
from openqasm_bridge.v2 import translate
qasm_source = f"""
OPENQASM 2.0;
qreg q[{num_qubits}];
creg c[{num_qubits}];
h q[0];
cx q[0],q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
# Returns C++ source lines; join them and write out a .cpp file to compile.
translated = translate(qasm_source, kernel_name="bell")
with open("bell.cpp", "w", encoding="utf-8") as output_file:
output_file.write("\n".join(translated))
```
Alternatively, translate an OpenQASM file directly from the terminal with the compiler's `-B` flag,
which writes the corresponding C++ `quantum_kernel` source:
```bash theme={"dark"}
intel-quantum-compiler -B bell.qasm
```
If you start from a [Qiskit](https://www.ibm.com/quantum/qiskit) circuit, export it to OpenQASM 2.0
first (`from qiskit.qasm2 import dumps; qasm_source = dumps(circuit)`), then feed the result to the
translator. This requires Qiskit to be installed in your working environment.
## Advanced: Compiler and C++ Interface
Advanced users can work directly in C++ and drive the `intel-quantum-compiler` from a terminal. A
`quantum_kernel` program can be compiled to a standalone executable and run without Python.
### Set up a terminal session
The environment's `python3` wires up the compiler and runtime libraries automatically, but a plain
terminal session does not. For a pure C++ workflow, add the compiler to your `PATH` and the Intel
runtime libraries to your `LD_LIBRARY_PATH`. Find your environment's path with `qbraid envs list` and
substitute its ID below:
```bash theme={"dark"}
ENV=~/.qbraid/environments/intelq_xxxx/pyenv
export PATH="$ENV/share/iqsdk:$PATH"
export LD_LIBRARY_PATH="$ENV/lib:$ENV/share/iqsdk/lib:$ENV/share/iqsdk/iqc/lib:$LD_LIBRARY_PATH"
```
### Compile and run a C++ quantum kernel
Save the following as `bell.cpp`. Unlike the Python interface, a standalone program provides its own
`main()` that configures the simulator and reads out results:
```cpp theme={"dark"}
#include
#include
#include
#include
qbit q[2];
quantum_kernel void bell() {
PrepZ(q[0]);
PrepZ(q[1]);
H(q[0]);
CNOT(q[0], q[1]);
}
int main() {
iqsdk::IqsConfig config(/*num_qubits=*/2, "noiseless");
iqsdk::FullStateSimulator sim(config);
if (iqsdk::QRT_ERROR_SUCCESS != sim.ready())
return 1;
bell();
std::vector> qids;
for (int i = 0; i < 2; ++i)
qids.push_back(std::ref(q[i]));
auto probabilities = sim.getProbabilities(qids);
iqsdk::FullStateSimulator::displayProbabilities(probabilities, qids);
return 0;
}
```
Compile it to an executable and run it:
```bash theme={"dark"}
intel-quantum-compiler -o . bell.cpp # -o names an output DIRECTORY, not a file
./bell
```
```text theme={"dark"}
Printing probability register of size 4
|00> : 0.5 |10> : 0
|01> : 0 |11> : 0.5
```
### Compiler options
The `intel-quantum-compiler` differs from a standard C++ compiler in a few important ways:
* `-o ` — write output to a **directory** (not a file name). The executable or shared object is
named after the input file.
* `-s` — build a shared object (`.so`) instead of an executable. This is what the Python interface
uses under the hood.
* `-B` — translate an OpenQASM 2.0 input file into a C++ `quantum_kernel` source file.
* `-f ` — forward a flag to the underlying Clang compilation, e.g. `-f -DMY_MACRO`.
Run `intel-quantum-compiler --help` for the full list of options. Additional ready-to-run C++
examples are bundled under `$IQSDK_SHARE/quantum-examples/`, and the Intel® Quantum SDK reference
documentation (PDFs and Doxygen API HTML) is under `$IQSDK_SHARE/docs/`.
Enjoy exploring the possibilities of quantum computing with the Intel® Quantum SDK.
# Files
Source: https://docs.qbraid.com/v2/lab/user-guide/files
Open, create, delete, rename, and download files and directories in the qBraid Lab file browser, and learn how file sync works across instances.
## File Browser
The file browser and Files menu enable you to work with files and directories on your system. This includes opening, creating,
deleting, renaming, downloading, and copying files and directories. To open the file browser, click on the "Folder"
icon in the left sidebar (or use shortcut `Cmd+Shift+F`).
The file system can be navigated by double-clicking on folders in the listing or clicking on the folders at the top of the
directory listing. To open any file, double-click on its name, or drag the file into the main work area to create a new tab.
### Show hidden files
Hidden files are those whose names start with a "`.`" (e.g., `.bashrc`, `.gitignore`). These are hidden by default in the file browser to reduce clutter.
From the top-bar menu, click on **View**, and then select **Show Hidden Files** to toggle the visibility of hidden files in
the file browser.
### Download files
To download a file, open the file browser, right click on the file, and select **Download** from the context menu.
If you can't launch Lab, you can still [download your files from the account
dashboard](/v2/account/usage#download-your-files) without starting a session.
### Delete files and directories
To delete a file or directory, open the file browser, right-click on the item, and select **Delete** from the context menu.
You can also delete files and directories from the terminal using the `rm` command:
```bash theme={"dark"}
rm -rf [file_or_directory]
```
Deleting a file or directory with `rm -rf` is permanent and cannot be undone.
Double-check the path before running the command.
If you are deleting files to free up disk space, see [Disk Usage Management](/v2/lab/user-guide/usage-limits#disk-usage-management) for how to find large files and directories with `gdu` and how uninstalling unused environments can reclaim the most space.
### Download directories
Downloading directories is not currently supported by the JupyterLab file browser. However, as a workaround, you can use the
terminal to zip the directory and then download the zip file.
First, open terminal in JupyterLab by opening a Launcher tab and selecting **Terminal** under the **Other** section.
Then, compress the directory using the `zip` command, specifying both the target directory's path and the desired name for the
resulting zip file. The `-r` option is essential as it instructs zip to include all subdirectories and their contents
recursively. The general command format is as follows:
```bash theme={"dark"}
$ zip -r output_filename.zip /path/to/directory
```
Finally, download the zip file using the file browser. See [Download files](#download-files).
## File Sync (Subscription Instances)
If your subscription Lab instance has been inactive for a few days, your files will be restored from cloud storage when you start a new session. A progress indicator appears above the file browser in the left sidebar while the sync is in progress.
Please be patient as your files repopulate — this may take a few moments
depending on the size of your directory. Once the sync is complete, refresh
the **Environments** sidebar to see all of your environments again.
## Files on On-Demand Instances
On-demand instances (GPUs and high-spec CPUs) handle file persistence differently from subscription instances:
* **No automatic sync**: On-demand instances do not automatically sync files from cloud storage when launched. Each instance has its own independent disk.
* **Download available**: You can download files from your cloud storage into an on-demand instance using the Files tab, but changes are **not synced back** to cloud storage.
* **Stop preserves files**: Stopping an instance keeps your disk and all files intact. When you resume, everything is where you left it.
* **Terminate deletes files**: Terminating an instance permanently deletes the disk and all files on it.
Before terminating an on-demand instance, download any important files to your
local machine or push them to a remote repository. See [On-Demand
Instances](/v2/lab/user-guide/on-demand-instances#file-persistence) for more
details.
# Getting Started
Source: https://docs.qbraid.com/v2/lab/user-guide/getting-started
This section will walk you through the basics of using the Lab interface and managing your server instance.
## Launch Lab
### Starting an Instance
From your [account page](https://account.qbraid.com/), select a compute profile and click **Launch** to start your quantum environment.
qBraid Lab offers two types of instances:
* **Subscription instances** (CPU): Included with your plan, billed against your monthly compute hours. Available in Small (2 vCPU), Medium (4 vCPU), and Large (8 vCPU) configurations. Your files are automatically synced from cloud storage on each launch.
* **On-demand instances** (GPU + high-spec CPU): Pay-as-you-go with [qBraid credits](/v2/home/pricing#credits), billed per minute. Includes 20+ GPU configurations and high-spec CPU instances. See [On-Demand Instances](/v2/lab/user-guide/on-demand-instances) for the full lifecycle guide.
[Paid subscription tiers](https://www.qbraid.com/pricing) provide additional benefits including higher disk storage limits and compute hours.
Depending on its size, pulling an image may take up to 2 minutes for the first time. The next time you launch Lab, it will load much more quickly.
If launching fails with `Compute hours exhausted and insufficient credits`,
you've used your monthly compute hour allocation. See
[Troubleshooting](/v2/lab/user-guide/troubleshooting#launch-failed-compute-hours-exhausted)
for how to upgrade your plan or switch the session over to credits.
### Stopping Lab
To stop your running Lab instance:
1. Navigate to your [account dashboard](https://account.qbraid.com/)
2. Click **Stop Running Server** to stop the running instance
Stopping your server will not delete your data, but will stop it from running and consuming compute hours.
On-demand instances (GPUs, high-spec CPUs) have an additional **Terminate**
action that permanently deletes the instance and its disk. See [On-Demand
Instances](/v2/lab/user-guide/on-demand-instances#stop-vs-terminate) for
details on the stop vs terminate distinction.
## Lab interface
Once your image is pulled, you will be taken to the qBraid Lab interface. Here are a few helpful resources to get started:
* **Interactive tour**: Click **Start Tour** (pop-up at first launch, or **Help** drop-down) to begin a guided tour of all of the Lab extensions and their key features.
* **Help drop-down**: The top-bar menu **Help** drop-down contains links to qBraid user guides, demos, and other reference materials.
# GPUs
Source: https://docs.qbraid.com/v2/lab/user-guide/gpus
The qBraid Lab GPU server is tailored for researchers and developers requiring enhanced computational capabilities. This high-performance Lab instance allows users to leverage GPUs for accelerated circuit simulation, to explore quantum machine learning applications with GPU-enabled quantum gradients, and more.
## Available GPU Configurations
qBraid offers a broad range of NVIDIA GPU instances spanning Blackwell, Hopper, Ampere, and Ada Lovelace architectures:
* **NVIDIA B200 (Blackwell)** — Available in 1x, 2x, 4x, and 8x configurations
* **NVIDIA H200, H100 (Hopper)** — Available up to 8x for large-scale workloads
* **NVIDIA GH200 (Grace Hopper)** — Unified CPU-GPU superchip
* **NVIDIA A100 (Ampere)** — Available up to 8x
* **NVIDIA L4, L40S, RTX 4090, RTX 5090, RTX 6000 Ada** — Cost-effective options for development and inference
Billing is in credits/minute with rates shown in your account launcher. See [GPU pricing](/v2/home/pricing#gpu-instances) for the full rate table.
## Launch a GPU Instance
From your [account dashboard](https://account.qbraid.com/dashboard), switch to the **On-Demand** tab. Each GPU profile shows its per-minute credit rate and a real-time availability indicator. Click **Launch** next to an available profile to start the instance.
GPU instances may take a few minutes to provision as resources are allocated
on-demand.
Once the instance is running, you can open it in JupyterLab, VS Code, or connect via the built-in terminal — all from the browser. See [Accessing Your Instance](/v2/lab/user-guide/on-demand-instances#accessing-your-instance) for details.
## Managing GPU Instances
GPU instances are on-demand instances with full start, stop, and terminate support:
* **Stop** — Pauses billing for compute while preserving your disk and files. Resume anytime.
* **Terminate** — Permanently deletes the instance and its data. Stops all billing.
See [On-Demand Instances](/v2/lab/user-guide/on-demand-instances) for the complete lifecycle guide, including file persistence, billing details, and concurrent instance limits.
## GPU Utilities
Further information about your GPU hardware can be retrieved using the
[NVIDIA System Management Interface](https://developer.nvidia.com/nvidia-system-management-interface) (`nvidia-smi`) and
[NVIDIA CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit) (`nvcc`) command line utilities.
Visit your [account page](https://account.qbraid.com/dashboard) to see the
full list of GPU options including real-time availability.
# Usage Guidelines
Source: https://docs.qbraid.com/v2/lab/user-guide/guidelines
Instructions to help you navigate, manage, and customize your lab environment effectively.
## Customizing Your Environment
Your `.bashrc` file contains a specific delimiter to delineate which sections of the file can be safely modified to persist between sessions. Here is the structure of the delimiter:
```bash theme={"dark"}
# ==================== DO NOT MODIFY CODE ABOVE THIS LINE ====================
# ANY CHANGES ABOVE THIS LINE WILL BE OVERWRITTEN DURING QBRAID SYSTEM STARTUP.
# You may add your custom configurations below this line.
```
Please ensure that **any customizations** you wish to retain are added **below** this delimiter line. Changes made above this line will not be saved when your session ends.
## Suppressing the Terminal Banner
If you prefer not to see the qBraid terminal banner during startup, you can suppress it by setting the `QBRAID_SHOW_BANNER` environment variable to `false`. This adjustment can be made by executing the following command in terminal:
```bash theme={"dark"}
echo "export QBRAID_SHOW_BANNER=false" >> "$HOME/.bashrc"
```
This command appends the setting to disable the banner to the end of your `.bashrc` file, ensuring it takes effect in all future terminal sessions.
# Kernels
Source: https://docs.qbraid.com/v2/lab/user-guide/kernels
## Add and remove kernels
The IPython kernel is the Python execution backend for Jupyter.
Under **Environments**, choose an environment, and expand its panel. Click **Add kernel** to create an ipykernel for the environment.
This is a necessary step before running code in a Jupyter Notebook using a given environment.
Once "active", click **Remove kernel** to remove the environment's ipykernel from the launcher.
# Notebooks
Source: https://docs.qbraid.com/v2/lab/user-guide/notebooks
Jupyter Notebooks (`.ipynb` files) are a community standard for communicating and performing interactive computing. They are documents that combine live runnable code with narrative text (Markdown), equations (LaTeX), images, interactive visualizations and other rich output.
qBraid Lab provides an interface to create and interact with Jupyter Notebooks, and includes a number of additional key features and integrations to enhance the quantum developer's experience.
## Create notebook
Create a notebook by clicking the `+` button in the file browser and then selecting a kernel in the new Launcher tab. In the Launcher tab, under **Notebooks**, clicking on an ipykernel associated with an activated environment will automatically launch a Jupyter notebook (`.ipynb` file) using that kernel.
The notebook document format used in qBraid Lab is the same as in the classic
Jupyter Notebook. For more on how to use Jupyter Notebooks, see [Jupyter
Notebooks](https://jupyter-notebook.readthedocs.io/en/stable/) and [Jupyter
Lab:
Notebooks](https://jupyterlab.readthedocs.io/en/stable/user/notebook.html).
## Switch notebook kernel
In the upper-right of any open notebook, you can see which kernel is in use. Clicking on the name of the current kernel, will open the kernel selector, and allow you switch to any other active kernel.
## Pip (magic) commands
From inside a notebook, you must run all pip commands using the “magic” `%` operator.
For example, to install a new package from within a notebook cell, use:
```python theme={"dark"}
[ ] %pip install
```
WARNING: **Do not** use the `!` operator for pip install commands within
Jupyter notebooks on qBraid. Doing so will install packages at the
system-level, where they will *not* be discoverable by your current notebook
environment/kernel.
After installing any new packages directly from a notebook, you *must* restart the kernel to see the changes take effect.
# On-Demand Instances
Source: https://docs.qbraid.com/v2/lab/user-guide/on-demand-instances
Launch, manage, and understand the lifecycle of on-demand GPU and CPU instances on qBraid Lab.
On-demand instances give you access to high-performance GPUs and large CPU configurations with pay-as-you-go credit billing. No subscription commitment or reserved capacity is required — just pick an instance, launch it, and start working.
## Launching an Instance
From your [dashboard](https://account.qbraid.com/dashboard), switch to the **On-Demand** tab to see available GPU and CPU profiles. Each profile shows its pricing in credits per minute and a capacity tag indicating current availability: **High**, **Medium**, or **Low**. Profiles tagged **No Capacity** cannot be launched until resources free up. Profiles with no tag means capacity data is not currently available.
Click **Launch** next to any available profile to start an instance.
You can run up to 5 concurrent on-demand instances at once, depending on your
credit balance. See [pricing](/v2/home/pricing#gpu-instances) for per-minute
rates.
## Accessing Your Instance
Once an instance is running, it appears under your **My Instances** tab.
From there you have two quick actions:
* **Arrow icon** — Opens the instance directly in JupyterLab.
* **Pencil (Edit) icon** — Opens the instance detail page where you can choose how to connect, all from your browser:
* **JupyterLab** — Notebook-first environment for interactive computing
* **VS Code** — Full IDE with extensions and debugging
* **Terminal** — Direct shell access to the instance
You can also connect to running instances remotely via SSH using the [qBraid CLI](https://docs.qbraid.com/v2/cli/api-reference/qbraid_compute).
## Instance Lifecycle
On-demand instances have three states you control: **Running**, **Stopped**, and **Terminated**.
```
Launch → Running → Stop → Stopped → Resume → Running
│ │
└── Terminate ◄────┘
```
### Running
Your instance is active and accessible. You are billed for both compute and storage at the rate shown on the instance tile (credits per minute).
### Stopped
Compute is released, but your instance's disk is preserved — all your files, installed packages, and work remain intact. You are billed a small storage-only fee (credits per minute) while the instance is stopped.
You can resume a stopped instance at any time. When you do, your files will be exactly as you left them.
### Terminated
The instance and its disk are **permanently deleted**. All files on the instance are lost and cannot be recovered. Billing stops completely.
## Stop vs Terminate
| | Stop | Terminate |
| :--------------- | :------------------------------ | :------------------------------ |
| **Compute** | Released (no compute charges) | Deleted |
| **Disk / Files** | Preserved | Permanently deleted |
| **Billing** | Small storage fee (credits/min) | No further charges |
| **Resumable** | Yes — resume anytime | No — must launch a new instance |
**When to stop**: You want to take a break but come back to the same environment later. Your files, packages, and configuration stay intact.
**When to terminate**: You are done with the instance and don't need the data. Terminating frees the storage slot and stops all billing.
Some instance types only support terminate — stop and resume is not available.
For these instances, the Stop button will not appear; only Terminate is shown.
Terminating an instance permanently deletes all files on it. Download or
export any important work before terminating. To keep your data, **stop** the
instance instead — it stays resumable.
## File Persistence
On-demand instances handle files differently from subscription instances:
* **No automatic file sync**: Unlike subscription instances, on-demand instances do not automatically sync files from cloud storage on launch. Each on-demand instance has its own independent disk.
* **Download from cloud storage**: You can use the Files tab to download files from your cloud storage into an on-demand instance. However, changes you make are **not synced back** to cloud storage.
* **Stop preserves files**: When you stop an instance, its disk is preserved. The next time you resume, your files will be exactly where you left them.
* **Terminate deletes files**: When you terminate an instance, the disk and all files on it are permanently deleted.
If you're working on important files in an on-demand instance, download them
to your local machine or push them to a Git repository before terminating.
## Billing
On-demand instances are billed per minute in [qBraid credits](/v2/home/pricing#credits) (100 credits = \$1 USD).
| State | What you're charged | Example |
| :------------- | :------------------ | :-------------------- |
| **Running** | Compute + storage | 8.95 cr/min (NV H100) |
| **Stopped** | Storage only | \~0.02 cr/min |
| **Terminated** | Nothing | \$0 |
Compute rates vary by instance type. See the [pricing page](/v2/home/pricing#gpu-instances) for the full rate table. Storage rates are a small pass-through cost based on disk size.
Your credit balance also determines how many concurrent instances you can run:
| Credit Balance | Concurrent Instances |
| :--------------------- | :------------------- |
| 100+ credits (\$1+) | 1 |
| 1,000+ credits (\$10+) | 2 |
| 2,500+ credits (\$25+) | 5 |
# Overview
Source: https://docs.qbraid.com/v2/lab/user-guide/overview
*A web-based IDE optimized for quantum computing.*
[qBraid Lab](https://lab.qbraid.com/) is a web-based IDE providing curated software tools for researchers and developers in quantum computing.
* Hosts **30+** configurable virtual environments spanning quantum machine learning, quantum chemistry, quantum error correction, and more.
* Integrates with **GitHub**, **Slack**, and AI coding assistants like **Claude Code** and **Codex**.
* **Collaborative tools** enable sharing custom environments.
* Provides direct access to a robust suite of computing resources, encompassing scalable **CPUs**, **GPUs**, and **QPUs**.
## Operating System
qBraid offers several specialized Lab instances tailored to various applications and use cases. However, the
standard and most widely used qBraid Lab instance is built on [Ubuntu 22.04.4 LTS (Jammy Jellyfish)](https://releases.ubuntu.com/22.04/)
and utilizes an [x86\_64](https://en.wikipedia.org/wiki/X86-64) architecture.
To discuss projects with specific system requirements, please [contact us](https://www.qbraid.com/contact).
# Quantum Devices
Source: https://docs.qbraid.com/v2/lab/user-guide/quantum-devices
Monitor availability, status, and specifications of quantum devices and simulators.
The Quantum Devices sidebar provides a real-time view of quantum hardware and simulators available through qBraid.
Open it by clicking the **Devices** icon in the top-right of the Lab console.
## Device list
The sidebar displays all available quantum devices with their provider, name, and current status.
Click the **Search devices** bar to filter by device name.
## Status indicators
The colored dot next to each device name indicates its current status:
| Color | Status | Meaning |
| ----- | --------------- | --------------------------------------------------------------------------------------- |
| Green | **ONLINE** | Fully operational — accepting and executing jobs |
| Grey | **UNAVAILABLE** | Online but not currently executing — jobs are queued until the next availability window |
| Red | **OFFLINE** | Not operational or not accepting jobs — expected to return |
| Red | **RETIRED** | Permanently decommissioned |
Hover over the status dot to see a tooltip with the exact status label.
## Pricing
For detailed pricing information on available devices, visit the [Pricing](/v2/home/pricing) page.
qBraid does not provide direct access to IBM devices. To run quantum jobs on
IBM hardware, you must use your own IBM credentials. Jobs submitted to IBM
devices will not appear in the Quantum Jobs sidebar.
For more details on submitting and managing quantum jobs, visit the [Quantum
Jobs](/v2/lab/user-guide/quantum-jobs) page.
# Quantum Jobs
Source: https://docs.qbraid.com/v2/lab/user-guide/quantum-jobs
Submit, monitor, cancel, and delete quantum jobs across multiple providers.
qBraid Quantum Jobs give Lab direct access to QPU devices from IonQ, AQT, QuEra, Rigetti, and IQM, as well as on-demand simulators
from qBraid, AWS, Pasqal, Quantinuum, and more. No additional access keys required.
Open the Quantum Jobs sidebar by clicking the **Jobs** icon in the top-right of the Lab console.
## Submit quantum jobs
You can submit quantum jobs using the qBraid-SDK in a Jupyter Notebook using the pre-configured qBraid SDK environment. First, ensure that the `qbraid-sdk` kernel is selected in your notebook.
Next, you can use the following code snippet to submit a quantum job to a device or simulator of your choice:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
# See available devices
provider = QbraidProvider()
devices = provider.get_devices()
print("Available devices:")
for device in devices:
print(device.id, device.status())
# Select a device
device = provider.get_device("aws:rigetti:qpu:cepheus-1-108q")
print("\nSelected device:", device)
# Define your quantum circuit (example using Qiskit)
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
# Run the quantum job
job = device.run(qc, shots=1024)
print("Job ID:", job.id)
print("Job submitted successfully.")
# Wait for the job to complete
job.wait_for_final_state()
# Retrieve and print the results
result = job.result()
print("Counts: ", result.data.get_counts())
```
Submitted jobs appear in the Quantum Jobs sidebar where you can monitor their status in real time.
## Jobs sidebar
The sidebar displays your job history with provider logos, status indicators, timestamps, and device IDs.
### Provider tabs
Switch between job sources using the tabs at the top of the sidebar:
* **qBraid** — jobs submitted to qBraid-managed devices via the SDK, REST API, or any client using qBraid as a backend. See [Pricing](/v2/home/pricing) for details.
* **IBM Cloud** — jobs submitted to IBM Quantum (requires your own IBM credentials)
* **AWS Braket** — jobs submitted through Amazon Braket (requires your own AWS credentials)
## Devices and pricing
For available devices, visit [Quantum Devices](/v2/lab/user-guide/quantum-devices). For pricing details,
visit [Pricing](/v2/home/pricing).
Access to IBM QPUs requires your own IBM Cloud credentials. qBraid does not
currently offer managed access to IBM QPUs.
# Settings
Source: https://docs.qbraid.com/v2/lab/user-guide/settings
Monitor usage, configure session behavior, and manage notifications from the qBraid Lab Settings extension.
The Settings extension provides an overview of your usage and preferences directly within qBraid Lab. To open it, click the **Settings** icon in the bottom-left sidebar.
All settings are stored in `~/.qbraid/qbraidrc`.
## Usage & Compute
The top section displays your current resource consumption:
* **Compute Hours**: Shows your cumulative compute hours used against your monthly quota, with a progress bar.
* **Storage**: Displays your current disk usage in GB against your plan's storage quota. See [Disk Usage](/v2/lab/user-guide/usage-limits#disk-usage) for quota details by plan.
A daily usage chart shows your compute activity over a selectable time window (7, 14, 30, 90, or 365 days). Below the chart, summary stats show **Total Hours**, **CPU Sessions**, and **GPU Sessions** for the selected period.
## Session Management
Three settings control how your Lab session is managed. Each has a minute input and a toggle to enable or disable it.
### Session check-ins
Prompts you at a recurring interval (e.g. every 60 minutes) to confirm you want to keep the session running. If you don't respond, the session will shut down. This prevents idle sessions from consuming compute hours unnecessarily.
* **Default**: 60 minutes, enabled
* **Range**: 10–480 minutes
### Auto shutdown
Automatically shuts down your session after the specified total runtime, regardless of activity. Turn off for no limit.
* **Default**: 120 minutes, enabled
* **Range**: 30–1440 minutes
### Idle timeout
Shuts down your session after a period of inactivity. Turn off for no limit.
* **Default**: 15 minutes, enabled
* **Range**: 10–120 minutes
## Notifications
### Slack notifications
Enable this to receive session alerts via a connected Slack workspace. This is disabled by default.
# Troubleshooting
Source: https://docs.qbraid.com/v2/lab/user-guide/troubleshooting
[](https://github.com/qBraid/community/issues)
Check the [qBraid Status Page](https://status.qbraid.com/) to see if there are any ongoing incidents or maintenance windows affecting services.
If you encounter an error that isn't included on this page or if the solution provided doesn't work, please [submit a support request](https://account.qbraid.com/support), [email us](mailto:contact@qbraid.com), or [open a bug report](https://github.com/qBraid/community/issues/new?assignees=\&labels=bug).
Also see our [FAQs](/v2/home/faq).
## Launch failed: Compute hours exhausted
When you click **Launch** on a subscription (CPU) profile and get:
```text theme={"dark"}
Launch failed: Compute hours exhausted and insufficient credits.
Required: $0.00/hour, available: $0.00.
```
you have used all of the [compute hours](/v2/lab/user-guide/usage-limits#compute-hours) included with
your plan for the current month, and you do not have credits set up to cover the session instead. The
amounts shown in the message vary with the profile you tried to launch. Nothing is wrong with your
account, and none of your files or environments are affected.
Compute hours reset at the start of each month. Until then you can still download your files, and you
can restore the ability to launch by upgrading your plan or by switching sessions over to credits.
### Get your files without launching
If you only need your files back, you do not have to launch Lab at all — download them from the
**Usage Overview** section of your account dashboard. See
[Download your files](/v2/account/usage#download-your-files).
### Upgrade your subscription
A higher tier comes with a larger monthly compute hour allocation (Free 100, Standard 400, Pro 2000).
See [Personal Subscriptions](/v2/account/wallet/subscriptions#subscribe) to change your plan, or
[Organization Subscriptions](/v2/account/organizations/subscriptions) if your seat is managed by an
organization.
### Buy credits and enable auto-switch
Credits cover CPU sessions once your compute hours run out, but only when auto-switch is turned on.
Both steps are required — adding credits on its own will not resolve the error.
1. [Buy credits](/v2/account/wallet/buy-credits#how-to-buy) from **Account > Wallet**. If you are in
an organization without purchase permissions,
[request credits from an admin](/v2/account/organizations/credit-requests) instead.
2. Enable [Auto-switch to credits](/v2/lab/user-guide/usage-limits#auto-switch-to-credits) under
**Account > Usage > Compute Settings**.
Sessions then bill against your credit balance at the per-minute rates listed in
[Pricing](/v2/home/pricing#cpu-instances). You can confirm your remaining compute hours and credit
balance at any time on the [Usage page](/v2/account/usage).
[On-demand instances](/v2/lab/user-guide/on-demand-instances) (GPUs and
high-spec CPUs) always bill credits directly and never draw on compute hours,
so they remain available even when your monthly allocation is exhausted.
## Launch failed: JupyterHub is temporarily unavailable
When you click **Launch** and get an error like:
```text theme={"dark"}
Launch failed: JupyterHub API error
(user: user-email@example.com, cluster: labX, operation: get_user_info):
Server error - JupyterHub is temporarily unavailable
```
this is a temporary problem on qBraid's side, not something wrong with your account. It is unrelated
to your quota, credits, subscription, or anything you did — your files, environments, and balances
are all unaffected, and no compute hours are consumed by the failed launch. The user, cluster, and
operation named in the message are diagnostic details, not settings you need to change.
The service often recovers on its own, so it is worth waiting a few minutes and trying again:
1. Check the [qBraid Status Page](https://status.qbraid.com/) for an ongoing incident.
2. Wait a few minutes, then click **Launch** again.
3. If it still fails, [submit a support request](https://account.qbraid.com/support) from your
account page. Include the full error message — the user, cluster, and operation it names help us
pinpoint the problem.
4. Once we confirm the service is restored, launch again as normal.
Repeatedly clicking **Launch** will not speed up recovery. Give it a few
minutes between attempts.
## Lab server errors
If you get one of the following errors from inside Lab,
* `Service unavailable or unreachable`
* `File Save Error`
For these types of errors, refreshing the page won't work and you will need to restart your qBraid Lab server.
You can stop your running server from your [account dashboard](https://account.qbraid.com/), and then launch a new instance.
## ModuleNotFoundError
While running a notebook, if you get a `ModuleNotFoundError` after an import statement:
1. Check to make sure you are using the correct notebook kernel for your environment, see [Switch notebook kernel](/v2/lab/user-guide/notebooks/#switch-notebook-kernel).
2. If you are using the correct kernel, the package you are trying to import may not be installed in that environment. See [Install new package](/v2/lab/user-guide/environments#install-new-package).
## No space left on device
If running a notebook cell produces errors like:
```text theme={"dark"}
The history saving thread hit an unexpected error
(OperationalError('database or disk is full')).
History will not be written to the database.
mkdir -p failed for path /home/jovyan/.cache/matplotlib:
[Errno 28] No space left on device
Fontconfig error: No writable cache directories
```
you have run out of [disk space](/v2/lab/user-guide/usage-limits#disk-usage). The exact wording
varies depending on which library tried to write to disk first, but the giveaways are
`database or disk is full`, `[Errno 28] No space left on device`, and complaints about cache
directories that cannot be written. There is nothing wrong with your code — there is simply nowhere
left to save anything.
Check your current usage against your quota from the **Storage** card in the
[Settings](/v2/lab/user-guide/settings#usage-&-compute) extension, opened with the **Settings** icon
in the bottom-left of Lab, or from the **Storage** card on the
[Usage page](/v2/account/usage#overview-tab) under **Account > Usage**. Then either free up space or
raise your quota:
* **Free up space.** Installed environments are usually the biggest consumers, so uninstalling ones
you no longer use frees the most space. You can also delete files from the
[file browser](/v2/lab/user-guide/files) or track down large directories from the terminal. See
[Disk Usage Management](/v2/lab/user-guide/usage-limits#disk-usage-management).
* **Upgrade your subscription.** Storage scales with your plan — 15 GB on Free, 50 GB on Standard,
150 GB on Pro. See [Personal Subscriptions](/v2/account/wallet/subscriptions#subscribe).
After clearing space, restart the kernel (**Kernel > Restart Kernel**) so the affected libraries can
recreate their caches.
If you stay over your disk quota, your Lab session will eventually be
terminated, and you will need to reduce your usage before you can resume. See
[Exceeding Disk Usage
Quotas](/v2/lab/user-guide/usage-limits#exceeding-disk-usage-quotas).
# Usage Limits
Source: https://docs.qbraid.com/v2/lab/user-guide/usage-limits
Understanding compute hours, disk usage, and AI chat limits on qBraid Lab
qBraid Lab Platform V2 introduces usage limits to help you manage your resources effectively. These limits vary based on your subscription plan and can be monitored from your account dashboard.
## Compute Hours
Each user is provided a set number of compute hours per month, which varies depending on your subscription plan. Compute hours track the time your Lab instances are actively running.
Compute hours are a unit of measure that combines the number of CPU cores used
multiplied by the number of hours used. For example, running a 2-vCPU instance
for 3 hours uses 6 CPU hours, and running an 8-vCPU instance for 2 hours uses
16 CPU hours.
**How it works:**
* Usage is tracked across all running Lab instances
* Compute hours reset at the end of each month
* Monthly allocations by plan:
| Plan | Compute Hours/Month |
| ------------ | ------------------- |
| **Free** | 100 CPU hours |
| **Standard** | 400 CPU hours |
| **Pro** | 2000 CPU hours |
Detailed statistics are available in your account dashboard under **Account > Usage > Compute Settings**. These include number of sessions opened in the last 30 days, daily session usage and total compute hours used.
Once the monthly allocation is used up, launching a subscription (CPU) instance fails with
`Launch failed: Compute hours exhausted and insufficient credits`. See
[Troubleshooting](/v2/lab/user-guide/troubleshooting#launch-failed-compute-hours-exhausted) for how to
resolve it.
### Auto-switch to Credits
If your compute hours are exhausted before the month ends, you can enable automatic switching to qBraid credits:
1. Navigate to **Account > Usage > Compute Settings**
2. Enable the **Auto-switch to credits** option
3. Credits will be consumed automatically when
compute hours run out
This ensures uninterrupted access to your Lab instances throughout the month. See [Pricing](/v2/home/pricing#cpu-instances) for the per-minute credit rates when auto-switch is enabled.
## Disk Usage
Users are allocated a limited amount of disk storage based on their subscription tier. It is the user's responsibility to
monitor their storage usage and ensure compliance with their assigned quota.
| Plan | Disk Storage |
| ------------ | ------------ |
| **Free** | 15 GB |
| **Standard** | 50 GB |
| **Pro** | 150 GB |
Your disk usage includes all files, notebooks, environments, and data stored in your Lab instance. Monitor your disk usage from the account dashboard to ensure you stay within your allocation.
To do so, click **Dashboard** in the left sidebar and scroll to the **Usage Overview** section at the bottom of the page. For more details, go to **Account** > **Usage** in the left sidebar.
To check their current disk usage and quota from within qBraid Lab, users can use the [Settings](/v2/lab/user-guide/settings) extension on qBraid Lab.
### Exceeding Disk Usage Quotas
Once the disk is full, notebook cells start failing with errors such as
`[Errno 28] No space left on device` or `database or disk is full`. See
[No space left on device](/v2/lab/user-guide/troubleshooting#no-space-left-on-device) for how to
recognize and resolve this.
* If a user exceeds their allocated disk space, they will receive a notification prompting them to delete files to stay within their quota.
* If the user does not reduce their storage usage within the allotted time, their qBraid Lab session will be terminated.
* Upon logging back in, the user must reduce their disk usage before resuming services.
* This process will repeat until the user meets the disk usage quota.
### Disk Usage Management
Your installed qBraid Lab environments often occupy the most disk space. To free up space, consider uninstalling any environments you no longer use.
To analyze your disk usage in detail, open the Terminal and run the `gdu` command:
```bash theme={"dark"}
gdu [flags] [directory_to_scan]
```
For more information on `gdu`, refer to the [official documentation](https://github.com/dundee/gdu).
Once you've identified large or unwanted files and directories, you can delete them using the `rm` command in the Terminal:
```bash theme={"dark"}
rm -rf [directory_to_delete]
```
Alternatively, you can remove files and directories from the [File Browser](/v2/lab/user-guide/files) by right-clicking and selecting **Delete**.
## On-Demand Instance Billing
On-demand instances (GPUs and high-spec CPUs) are billed per minute in credits, separate from your subscription compute hours.
* **Running**: Charged a compute rate (credits/min) plus a small storage fee. Rates vary by instance type — see [pricing](/v2/home/pricing#gpu-instances).
* **Stopped**: Charged a storage-only fee (credits/min) to preserve your disk and files. Much smaller than the running rate.
* **Terminated**: No further charges. The instance and its disk are permanently deleted.
Your credit balance determines how many concurrent on-demand instances you can run (up to 5). See [On-Demand Instances](/v2/lab/user-guide/on-demand-instances#billing) for the full breakdown.
## AI Chat Usage
Each user receives a monthly quota for AI Chat prompts, which varies by subscription plan. This quota covers interactions with qBraid's AI assistant features.
**Auto-switch to Credits:**
Similar to compute hours, you can enable automatic credit consumption when your AI chat quota is exhausted:
1. Navigate to **Account > Usage > AI Chat Usage**
2. Enable the **Auto-switch to credits** option
3. Credits will be used for additional AI chat prompts once your quota is exhausted
This feature ensures continuous access to AI assistance throughout the month, even after your base quota is used.
## Monitoring Usage
All usage metrics can be monitored in real-time from your [account dashboard](https://account.qbraid.com/). Navigate to **Account > Usage** to view detailed breakdowns of your compute hours, disk storage, and AI chat consumption.
# pyqasm
Source: https://docs.qbraid.com/v2/pyqasm/api-reference/pyqasm
# Advanced Features
Source: https://docs.qbraid.com/v2/pyqasm/user-guide/advanced-features
## Branch Optimization
We optimize the conditional branching statements using the following techniques:
1. **Branch Elimination**: We evaluate the outcome of the branch condition, if it is known at compile time
and does not contain measurement results of qubits. We remove the branch statement
and if it evaluates to a true value, attach the corresponding block of code to the main program.
```python Elimination theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
bit[1] c;
int[32] a = 0;
if(a > 0){
h q[0];
}
if(a < 0){
x q[0];
}
if(a == 0){
y q[0];
measure q -> c;
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
bit[1] c;
y q[0];
c[0] = measure q[0];
```
2. **Branch Unfolding**: If the branch condition contains measurement results of qubits, we unfold the classical
registers into individual bits and insert equivalent conditional statements for each bit. This method
is particularly useful for systems which do not support multi-bit classical registers in conditional
statements.
```python Unfolding theme={"dark"}
import pyqasm
qasm_code = """OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
bit[4] c;
if(c == 3){
h q[0];
}
if(c >= 3){
h q[0];
} else {
x q[0];
}
if(c <= 3){
h q[0];
} else {
x q[0];
}
if(c < 4){
h q[0];
} else {
x q[0];
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
bit[4] c;
if (c[0] == false) {
if (c[1] == false) {
if (c[2] == true) {
if (c[3] == true) {
h q[0];
}
}
}
}
if (c[2] == true) {
if (c[3] == true) {
h q[0];
} else {
x q[0];
}
} else {
x q[0];
}
if (c[0] == false) {
if (c[1] == false) {
h q[0];
} else {
x q[0];
}
} else {
x q[0];
}
if (c[0] == false) {
if (c[1] == false) {
h q[0];
} else {
x q[0];
}
} else {
x q[0];
}
```
## Switch Case Optimization
The openqasm reference enforces the switch targets to be of type int and the cases to be unique integer
literals or constant integer expressions. This implies that the switch case statements can be optimized
at compile time as long as the target variable is not dependent on a measurement result.
Once the target variable is evaluated at compile time, the `switch` statement is removed and the
corresponding `switch` case code block is attached to the main program.
```python Switch Case theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
const int i = 1;
qubit q;
switch(i) {
case 1,3,5,7 {
int j = 4; // definition inside scope
switch(j) {
case 1,3,5,7 {
x q;
}
case 2,4,6,8 {
j = 5; // assignment inside scope
y q; // this will be executed
}
default {
z q;
}
}
}
case 2,4,6,8 {
y q;
}
default {
z q;
}
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
y q[0];
```
## Subroutine Inlining
Subroutine inlining is the process of replacing a subroutine call with the actual code of the subroutine.
Since quantum devices do not support subroutines, it is essential to inline the subroutine code against
the call to the subroutine. Here, the formal parameters of the subroutine are replaced with the actual
parameters used in the subroutine call. Moreover, the subroutine code is inserted at the location of the
subroutine call with careful consideration of the scope of the variables used in the subroutine.
```python Inlining theme={"dark"}
import pyqasm
qasm_code = """OPENQASM 3.0;
include "stdgates.inc";
gate my_gate(a) q2 {
rx(a) q2;
}
def my_function(qubit a, float[32] b) {
float[64] c = 2*b;
my_gate(b) a;
my_gate(c) a;
return;
}
qubit q;
float[32] r = 3.14;
my_function(q, r);
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
rx(3.14) q[0];
rx(6.28) q[0];
```
## Loop Unrolling
Loop unrolling is the process of unfolding a loop with a list of equivalent instructions.
This is particularly important for quantum programs as quantum computers do not support direct execution
of loops. We provide support for unrolling `while` and `for` loops -
1. **`while` Loop**
* We evaluate the loop conditions at *compile time* and visit the loop body
till the condition becomes false.
```python Unroll while theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
qubit[4] q;
int i = 0;
while (i < 3) {
h q[i];
cx q[i], q[i+1];
i += 1;
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
qubit[4] q;
h q[0];
cx q[0], q[1];
h q[1];
cx q[1], q[2];
h q[2];
cx q[2], q[3];
```
* To ensure that the unrolling process does not go into an infinite
loop, we have introduced a `max_loop_iters` parameter. It has a default value of `1e9` and allows
the user to control the max number of loop iterations -
```python Max Loop Iterations theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
qubit[100] q;
int i = 0;
while (i < 50) {
h q[i];
cx q[i], q[i+1];
i += 1;
}
"""
module = pyqasm.loads(qasm_code)
# set max loop iterations to 10
module.unroll(max_loop_iters=10)
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
ERROR:pyqasm: Error at line 5, column 0 in QASM file
>>>>>> while (i < 50) {
h q[i];
cx q[i], q[i + 1];
i += 1;
}
...
LoopLimitExceededError: Loop exceeded max allowed iterations
```
* This unrolling process is not applicable in a case when the `while` condition is *dependent on a
quantum measurement result*. Since the truth value of the condition can only be known at runtime,
an exception is raised while unrolling such loops -
```python Unroll while theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
qubit q;
bit c;
c = measure q;
while (c) {
h q;
c = measure q;
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
ERROR:pyqasm: Error at line 6, column 4 in QASM file
>>>>>> while (c) {
h q;
c = measure q;
}
....
ValidationError: Cannot unroll while-loop with condition depending on quantum measurement
result.
```
Unrolling `while` loops which contain quantum measurements is, in fact, an active area of development.
Some researchers have proposed to identify such loops and convert them into a [native `while`
loop instructions which can be executed during runtime](https://github.com/qBraid/pyqasm/pull/199#issuecomment-2922055327). However, this approach has not been
fully implemented yet.
2. **`for` Loop**
* The process is similar to the `while` loop unrolling process, but the loop counter is
chosen from the range provided by the user. The loop body is executed as many times as
the range value -
```python Unroll for theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
h q;
for int i in [0:2]{
cx q[i], q[i+1];
}
c = measure q;
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
h q[0];
h q[1];
h q[2];
h q[3];
cx q[0], q[1];
cx q[1], q[2];
cx q[2], q[3];
c[0] = measure q[0];
c[1] = measure q[1];
c[2] = measure q[2];
c[3] = measure q[3];
```
## Qubit Register Consolidation
This feature enables the transformation of multiple named qubit registers into a single unified register named `__PYQASM_QUBITS__`. This simplifies register management and prepares the circuit for hardware-specific or simulator-specific backends that expect a flat register structure.
**Usage**
```python theme={"dark"}
qc = pyqasm.loads(qasm_file)
qc.unroll(consolidate_qubits = True)
```
**Key Features:**
* If `device_qubits` is provided while loading the QASM file, enforces a qubit limit and validates register size.
```python theme={"dark"}
pyqasm.loads(qasm_file, device_qubits = 10)
```
* If not provided, the unified register size equals the total number of qubits across all original registers.
* Updates all qubit references in the code to align with the new unified register.
**Examples:**
```python QASM-Code theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
qubit[2] q1;
qubit[3] q2;
cx q1[0], q2[2];
"""
module = pyqasm.loads(qasm_code)
module.unroll(consolidate_qubits=True)
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
qubit[5] __PYQASM_QUBITS__;
cx __PYQASM_QUBITS__[0], __PYQASM_QUBITS__[4];
```
```python QASM-Code theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
qubit[2] q1;
qubit[2] q2;
cx q1[1], q2[0];
"""
module = pyqasm.loads(qasm_code, device_qubits = 5)
module.unroll(consolidate_qubits=True)
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
qubit[5] __PYQASM_QUBITS__;
cx __PYQASM_QUBITS__[1], __PYQASM_QUBITS__[2];
```
## Circuit Timing Operations
In OpenQASM 3, circuit execution is not only determined by gate order but also by explicit timing primitives that define time and duration of operations. PyQASM now supports the following timing operations:
**Operations:**
* **duration** - A typed literal or variable representing an explicit length of time, expressed in units such as `ns`, `us`, `ms`, or hardware clock cycles (`dt`).
* **stretch** - A symbolic non-negative duration that is resolved at compile time into a concrete value chosen to satisfy timing constraints.
* **delay** - An identity operation applied to one or more qubits for a given duration, used to pin timing and prevent unintended commutation.
* **box** - A timing context that groups statements together with an enforced total duration, ensuring well-defined scheduling boundaries.
* **angle** - A fixed-width representation of phase values, useful for encoding rotations and phase shifts in hardware-specific bitstring formats.
* **extern** - User or backend defined functions that can be declared in QASM and validated in PyQASM for tasks such as cycle alignment or calibration.
* **complex** - Support for complex literals and standard functions (`abs`, `real`, `imag`, `sin`, `cos`, etc.) to describe calibrated amplitudes, phases, or control laws.
**Example:**
```python QASM-Code theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
// Duration examples
duration t1 = 100ns;
const duration t2 = 50us;
const duration t3 = 200dt;
// Stretch examples
const stretch s1 = 75ns;
const stretch s2 = t2 + t3;
// Delay examples
qubit[2] q;
delay[t1] q[0];
delay[s1] q[1];
delay[25ns] q[0], q[1];
// Box examples
box[150ns] {
delay[50ns] q[0];
h q[1];
delay[50ns] q[0];
cx q[0], q[1];
delay[50ns] q[1];
}
box[t1] {
delay[25ns] q[0];
x q[1];
delay[25ns] q[0];
y q[1];
delay[50ns] q[0], q[1];
}
// Angle examples
angle[8] ang1 = pi/2;
angle[4] ang2 = "1010";
const angle[8] ang3 = 3*pi/4;
// Extern examples
extern calibrate(angle, duration) -> complex;
extern measure_fidelity(int, complex) -> float[64];
complex[float[64]] result;
float[64] fidelity;
result = calibrate(ang1, t1);
fidelity = measure_fidelity(2, 1.0 + 2.0im);
// Complex examples
complex c1 = 1.0 + 2.0im;
complex c2 = -3.5 - 1.5im;
complex[float[64]] c3 = 2.5 + 3.7im;
complex c4 = c1 + c2;
complex c5 = c1 * c2;
float mag = abs(c1);
complex c6 = sqrt(c1);
"""
module = pyqasm.loads(qasm_code)
module.validate()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
duration t1 = 100.0ns;
const duration t2 = 50.0us;
const duration t3 = 200.0dt;
const stretch s1 = 75.0ns;
const stretch s2 = t2 + t3;
qubit[2] q;
delay[100.0ns] q[0];
delay[75.0ns] q[1];
delay[25.0ns] q[0], q[1];
box[150.0ns] {
delay[50.0ns] q[0];
h q[1];
delay[50.0ns] q[0];
cx q[0], q[1];
delay[50.0ns] q[1];
}
box[100.0ns] {
delay[25.0ns] q[0];
x q[1];
delay[25.0ns] q[0];
y q[1];
delay[50.0ns] q[0], q[1];
}
angle[8] ang1 = pi / 2;
angle[4] ang2 = "1010";
const angle[8] ang3 = 3 * pi / 4;
extern calibrate(angle, duration) -> complex;
extern measure_fidelity(int, complex) -> float[64];
complex[float[64]] result;
float[64] fidelity;
result = calibrate(1.5707963267948966, 100.0ns);
fidelity = measure_fidelity(2, 1.0 + 2.0im);
complex c1 = 1.0 + 2.0im;
complex c2 = -3.5 - 1.5im;
complex[float[64]] c3 = 2.5 + 3.7im;
complex c4 = -2.5 + 0.5im;
complex c5 = -0.5 - 8.5im;
float mag = 2.23606797749979;
complex c6 = 1.272019649514069 + 0.7861513777574233im;
```
# Circuit Drawer
Source: https://docs.qbraid.com/v2/pyqasm/user-guide/circuit-drawer
To use the circuit drawer tool, install the `visualization` extra:
```shell theme={"dark"}
pip install 'pyqasm[visualization]'
```
Draw a quantum circuit directly from any OpenQASM string:
```python theme={"dark"}
from pyqasm import draw
qasm = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
bit[3] b;
h q[0];
z q[1];
rz(pi/1.1) q[0];
cx q[0], q[1];
swap q[0], q[1];
ccx q[0], q[1], q[2];
b = measure q;
"""
draw(qasm, output='mpl')
```
Currently, only the `mpl` (matplotlib) output format is supported.
## Input Formats
All draw methods accepts either a `str` (QASM source) or a [pyqasm.QasmModule](https://sdk.qbraid.com/pyqasm/stubs/pyqasm.QasmModule.html#pyqasm.QasmModule) object. The following are equivalent:
```python theme={"dark"}
from pyqasm import loads, draw
from pyqasm.printer import mpl_draw
module = loads(qasm_str)
draw(module, output='mpl')
draw(qasm_str, output='mpl')
mpl_draw(module)
mpl_draw(qasm_str)
```
## Exclude Idle Wires
Use the `idle_wires=False` option to exclude idle qubits (i.e. wires with no circuit elements) in the output circuit diagram:
```python theme={"dark"}
from pyqasm import draw
qasm = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
bit[2] b;
h q[0];
h q[1];
cx q[0], q[1];
b[0] = measure q[0];
b[1] = measure q[1];
"""
draw(qasm, idle_wires=False, output='mpl')
```
## Save Diagram
Save the circuit diagram to a file by specifying `output='mpl'` and a `filename`:
```python theme={"dark"}
draw(qasm, output='mpl', filename='/path/to/circuit.png')
```
# PyQASM CLI
Source: https://docs.qbraid.com/v2/pyqasm/user-guide/cli
To provide a more interactive experience for developers, PyQASM offers a
command-line interface (CLI) tool. The CLI tool can be installed as an extra
dependency by running the following command:
```shell theme={"dark"}
pip install 'pyqasm[cli]'
```
This feature is still in active development and may not be stable for
production use
Users can then verify their installation by running the following command:
```shell Verify installation theme={"dark"}
pyqasm --help
```
```shell Output theme={"dark"}
Usage: pyqasm [OPTIONS] COMMAND [ARGS]...
The PyQASM CLI.
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --version -v Show the version and exit. │
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to copy it or customize the │
│ installation. │
│ --help -h Show this message and exit. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ─────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ validate Validate OpenQASM files. |
| unroll Unroll OpenQASM files. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
**Validate**
The `validate` functionality can be used to check QASM files. You can provide the path to
the QASM file or a directory as an argument to the `validate` command. The tool will then check the
semantic validity of the file(s) and return the results.
```shell help command theme={"dark"}
pyqasm validate --help
```
```shell Output theme={"dark"}
Usage: pyqasm validate [OPTIONS] SRC_PATHS...
Validate OpenQASM files.
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ * src_paths SRC_PATHS... Source file or directory paths to validate. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --skip -s TEXT Files to skip during validation. │
│ --help -h Show this message and exit. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
```shell Valid QASM file theme={"dark"}
$ pyqasm validate tests/cli/resources/valid1.qasm
Success: no issues found in 1 source file
```
```shell Invalid QASM file inside a dir theme={"dark"}
$ pyqasm validate tests/cli/resources/
tests/cli/resources/invalid1.qasm: error: Index 2 out of range for register of size 1 in qubit [validation]
Found errors in 1 file (checked 3 source files)
```
**Unroll**
The `unroll` functionality is used to expand all macros and modular structures in OpenQASM files into flat, low-level instructions that are directly executable by quantum devices or emulators. You can provide either the path to a specific QASM file or a directory containing multiple QASM files as an argument to the `unroll` command. By default, the unrolled file is saved with the following naming convention: `_unrolled`
```shell help command theme={"dark"}
pyqasm unroll --help
```
```shell Output theme={"dark"}
Usage: pyqasm unroll [OPTIONS] SRC_PATHS...
Unroll OpenQASM files.
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ * src_paths SRC_PATHS... Source file or directory paths to unroll. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --skip -s TEXT Files to skip during unrolling. │
│ --overwrite Overwrite original files instead of creating new ones. │
│ --output -o TEXT Output file path (can only be used with a single input file). │
│ --help -h Show this message and exit. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
```shell Unroll single QASM file theme={"dark"}
$ pyqasm unroll tests/cli/resources/valid1.qasm
Successfully unrolled 1 file
Checked 1 source file
```
```shell Unroll multiple QASM file inside a dir theme={"dark"}
$ pyqasm unroll tests/cli/resources/
Successfully unrolled 2 files
----------------------------------------------------------------------------------------------------
Failed to unroll: tests/cli/resources/invalid1.qasm
[validation-error] -> Index 2 out of range for register of size 1 in qubit
Error at line 8, column 0 in QASM file
>>>>>> h q[2];
----------------------------------------------------------------------------------------------------
Failed to unroll 1 file
Checked 3 source files
```
# Usage Examples
Source: https://docs.qbraid.com/v2/pyqasm/user-guide/examples
## Compilation
### Inlining and Unrolling
This example demonstrates the capabilities of the PyQASM tool by unrolling a complex OpenQASM program into a simplified form.
In PyQASM, “unrolling” refers to the process of simplifying a quantum program by expanding custom gate definitions and flattening
complex language constructs like subroutines, loops, and conditional statements into basic operations. This technique, also known
as **program flattening** or **inlining**, transforms nested and recursive structures into a linear sequence of operations consisting solely
of qubit and classical bit declarations, gate operations, and measurement operations. By converting the program into this simplified
format, it becomes easier to perform subsequent transpilation or compilation steps before executing the program on a quantum device.
```qasm example.qasm theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
// Define custom gates
gate hgate q {
h q;
}
gate xgate q {
x q;
}
gate cxgate q1, q2 {
cx q1, q2;
}
// Define a subroutine for creating a Bell state
def create_bell_state(qubit[2] q) {
hgate q[0];
cxgate q[0], q[1];
return;
}
// Define a subroutine for a generic quantum operation
def generic_operation(qubit[N] q) {
for int i in [0:N-1] {
hgate q[i];
xgate q[i];
y q[i];
rx(pi) q[i];
}
return;
}
// Main program
const int[32] N = 4;
qubit[N] q;
bit[N] c;
// Create a Bell state using the alias
create_bell_state(q[0:2]);
measure q[0:1] -> c[0:1];
// Perform a generic operation on all qubits
generic_operation(q);
// Classical control flow
if (c[0]) {
hgate q[0];
} else {
xgate q[0];
}
// Define an array of angles for parameterized gates
array[float[64], N] angles = {pi/2, pi/4, pi/8, pi/16};
// Apply parameterized rotations
for int i in [0:N-1] {
rx(angles[i]) q[i];
}
// Measure the qubits
c = measure q;
```
```python example.py theme={"dark"}
import pyqasm
module = pyasm.load("example.qasm")
module.validate()
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
h q[0];
cx q[0], q[1];
c[0] = measure q[0];
h q[0];
x q[0];
y q[0];
rx(3.141592653589793) q[0];
h q[1];
x q[1];
y q[1];
rx(3.141592653589793) q[1];
h q[2];
x q[2];
y q[2];
rx(3.141592653589793) q[2];
h q[3];
x q[3];
y q[3];
rx(3.141592653589793) q[3];
if (c[0] == true) {
h q[0];
} else {
x q[0];
}
rx(1.5707963267948966) q[0];
rx(0.7853981633974483) q[1];
rx(0.39269908169872414) q[2];
rx(0.19634954084936207) q[3];
c[0] = measure q[0];
c[1] = measure q[1];
c[2] = measure q[2];
c[3] = measure q[3];
```
For complete details about the supported operations, see [PyQASM Supported Operations](https://github.com/qBraid/pyqasm/blob/main/src/README.md#supported-operations).
## Analysis
### `num_qubits`, `num_clbits`, and `depth`
This example demonstrates how to use the `num_qubits`, `num_clbits`, and `depth` methods.
```python example.py theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
bit[3] c;
h q[0];
cx q[0], q[1];
c[0] = measure q[0];
"""
# Load QASM code from a string into a QasmModule object
module = pyqasm.loads(qasm_code)
# Get the number of qubits and classical bits
num_qubits = module.num_qubits
num_clbits = module.num_clbits
# Calculate the depth of the circuit
depth = module.depth()
print(f"Number of qubits: {num_qubits}")
print(f"Number of classical bits: {num_clbits}")
print(f"Depth of the circuit: {depth}")
```
```bash Output theme={"dark"}
Number of qubits: 3
Number of classical bits: 3
Depth of the circuit: 3
```
## Transformations
### `has_measurements` and `remove_measurements`
This example demonstrates how to check for measurements and remove them.
```python example.py theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
bit[3] c;
h q[0];
cx q[0], q[1];
c[0] = measure q[0];
"""
# Load QASM code from a string into a QasmModule object
module = pyqasm.loads(qasm_code)
# Check if the module has measurements
print(f"Has measurements: {module.has_measurements()}")
# Remove measurements
module.remove_measurements()
print("\nModule without measurements: ")
print(pyqasm.dumps(module))
# Check if the module has measurements
print(f"Has measurements: {module.has_measurements()}")
```
```bash Output theme={"dark"}
Has measurements: True
Module without measurements:
OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
bit[3] c;
h q[0];
cx q[0], q[1];
Has measurements: False
```
Very similar to the previous example, we also have `has_barriers` and `remove_barriers` methods
to check for barriers and remove them, respectively.
### `populate_idle_qubits`
This example demonstrates how to populate idle qubits with identity gates -
```python example.py theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
h q[0];
cx q[0], q[1];
"""
# Load QASM code from a string into a QasmModule object
module = pyqasm.loads(qasm_code)
# Populate idle qubits with identity gates
module.populate_idle_qubits()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
h q[0];
cx q[0], q[1];
id q[2];
id q[3];
id q[4];
```
### `remove_idle_qubits`
This example demonstrates how to remove idle qubits from the module and shows the qubit count before and after.
```python example.py theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
h q[0];
"""
# Load QASM code from a string into a QasmModule object
module = pyqasm.loads(qasm_code)
# Print the number of qubits before removing idle qubits
print(f"Number of qubits before: {module.num_qubits}")
# Remove idle qubits
module.remove_idle_qubits()
# Print the number of qubits after removing idle qubits
print(f"Number of qubits after: {module.num_qubits}")
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
Number of qubits before: 5
Number of qubits after: 1
OPENQASM 3.0;
include "stdgates.inc";
qubit[1] q;
h q[0];
```
### `reverse_qubit_order`
This example demonstrates how to reverse the order of qubits in the module with a more involved program.
```python example.py theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
h q[0];
cx q[0], q[1];
cx q[1], q[2];
cx q[2], q[3];
cx q[3], q[4];
"""
# Load QASM code from a string into a QasmModule object
module = pyqasm.loads(qasm_code)
# Reverse the order of qubits
module.reverse_qubit_order()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
h q[4];
cx q[4], q[3];
cx q[3], q[2];
cx q[2], q[1];
cx q[1], q[0];
```
### `rebase`
Unroll and convert an QASM program to a specified basis gate set.
The target basis set can be chosen from the options present in the `pyqasm.elements.BasisSet` class. Currently we
support conversion to the following basis sets:
1. `BasisSet.CLIFFORD_T` : `{"h", "t", "s", "cx", "tdg", "sdg"}`
2. `BasisSet.ROTATIONAL_CX` : `{"rx", "ry", "rz", "cx"}`
For example -
```python example.py theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
// Operations
h q;
x q;
cz q[0], q[1];
c = measure q;
"""
module = pyqasm.loads(qasm_code)
module.rebase(pyqasm.elements.BasisSet.ROTATIONAL_CX)
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
ry(1.5707963267948966) q[0];
rx(3.141592653589793) q[0];
ry(1.5707963267948966) q[1];
rx(3.141592653589793) q[1];
rx(3.141592653589793) q[0];
rx(3.141592653589793) q[1];
ry(1.5707963267948966) q[1];
rx(3.141592653589793) q[1];
cx q[0], q[1];
ry(1.5707963267948966) q[1];
rx(3.141592653589793) q[1];
c[0] = measure q[0];
c[1] = measure q[1];
```
The `rebase` functionality is useful for converting a program to a basis set that is supported by a specific
quantum device. This conversion can be extended to new custom basis sets by adding the required gate decompositions
to the `pyqasm.maps.decomposition_rules` module.
Currently we have complete conversion support for the `BasisSet.ROTATIONAL_CX` basis set,
whereas only non-parameterized gate conversion is available for `BasisSet.CLIFFORD_T`.
Refer to the [PyQASM API Documentation](https://qbraid.github.io/pyqasm/api/pyqasm.html)
for more details on the available methods and their usage.
# OpenPulse
Source: https://docs.qbraid.com/v2/pyqasm/user-guide/openpulse
A low-level pulse programming extension of OpenQASM 3, giving developers direct control over the physical signals that manipulate qubits.
## Overview
**OpenPulse** extends **OpenQASM3** beyond the gate model by introducing constructs for **ports**, **frames**, and **waveforms** that describe the actual control signals sent to quantum hardware. This allows programmers to implement custom calibrated gates, adjust frequencies and phases dynamically, and align timing at the hardware level. With **OpenPulse**, quantum programs can bridge the gap between high-level circuits and the physical pulses that drive qubit operations.
## PyQASM 🤝 OpenPulse
**PyQASM** now understands and validates **OpenPulse** constructs embedded in `OpenQASM3` programs. This includes parsing `cal` blocks and `defcal` definitions and validating pulse-level statements.
While most existing tools focus on gate-level `OpenQASM`, few provide strong support for pulse-level semantics and most of them lack comprehensive semantic analysis -- such as validating scope rules, deterministic durations in defcal, and hardware-level constraints
**PyQASM** aims to fill this gap by:
* Providing a unified parser and validator for both `OpenQASM3` and `OpenPulse`.
* Enforcing semantic correctness of pulse operations, not just syntax.
* Allowing users to configure `backend-specific` rules (duration tracking, frame limits, etc.).
* Building towards a complete analysis and compilation toolkit for pulse-level programs, bridging high-level circuits with low-level control.
This makes **PyQASM** a foundation for researchers and hardware providers who need **validation** and **compilation** of **OpenPulse** programs.
## Core Constructs
The OpenPulse grammar—activated by `defcal grammar "openpulse";` — adds these core constructs to OpenQASM3.
* [**Ports**](https://openqasm.com/versions/3.0/language/openpulse.html#ports) (`port`): Abstract I/O channels representing hardware control lines.
* Real hardware often restricts the number of active frames per physical port. pyqasm introduces a `frame_limit_per_port` option that users can set when loading a program.
```python QASM-Code theme={"dark"}
import pyqasm
qasm = """
OPENQASM 3.0;
defcalgrammar "openpulse";
cal {
port d0;
frame fr1 = newframe(d0, 100.0, 5.0);
frame fr2 = newframe(d0, 100.0, 5.0);
}
"""
module = pyqasm.loads(qasm, frame_limit_per_port = 1)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
ERROR:pyqasm: Error at line 4, column 16 in QASM file
> > > > > > newframe(d0, 100.0, 5.0)
pyqasm.exceptions.ValidationError: Port 'd0' has exceeded the 'frame-limit' of '1'
```
* [**Frames**](https://openqasm.com/versions/3.0/language/openpulse.html#frames) (`frame`): Stateful carriers with port, timing, frequency, and phase.
* Users can configure whether frames are allowed in `defcal` blocks via the `frame_in_defcal` option when loading a program.
```python QASM-Code theme={"dark"}
import pyqasm
qasm = """
OPENQASM 3.0;
defcalgrammar "openpulse";
cal {
port d0;
frame fr1 = newframe(d0, 100.0, 5.0);
}
defcal gate $0{
frame fr2 = newframe(d0, 100.0, 5.0);
}
"""
module = pyqasm.loads(qasm, frame_in_defcal = False)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
ERROR:pyqasm: Error at line 2, column 16 in QASM file
> > > > > > newframe(d0, 100.0, 5.0)
pyqasm.exceptions.ValidationError: 'Frame' initialization in 'defcal' block is not allowed
```
* pyqasm also supports the full set of [**frame manipulators**](https://openqasm.com/versions/3.0/language/openpulse.html#phase-tracking) defined in the OpenPulse grammar
* `set_frequency()` / `shift_frequency()` / `shift_phase()` / `get_frequency()` / `get_phase()`.
* [**Waveforms**](https://openqasm.com/versions/3.0/language/openpulse.html#waveforms) (`waveform`): Envelopes modulating signals on ports.
* pyqasm supports all `waveform` types defined in the OpenPulse specification -- `gaussian`, `sech`, `gaussian_square`, `drag`, `constant`, `sine`, `mix`, `sum`, `phase_shift`, `scale`.
* validates all declarations conform to the OpenPulse grammar, including parameter correctness and waveform compatibility.
* [**Capture**](https://openqasm.com/versions/3.0/language/openpulse.html#capture-instruction) (`capture`): Measurement/capture operations.
* pyqasm also supports the `capture` operations defined in the OpenPulse grammar -- `capture_v1()`, `capture_v2()`, `capture_v3()`, `capture_v4()`.
* **Operations** within `cal` / `defcal`:
* [**play()**](https://openqasm.com/versions/3.0/language/openpulse.html#play-instruction) — apply a waveform via a frame.
* By default, `play` is intended for use inside `defcal` blocks. pyqasm introduces a configurable option, `play_in_cal_block`, which allows users to enable or restrict the use of `play` instructions inside `cal` blocks as well.
```python QASM-Code theme={"dark"}
import pyqasm
qasm = """
OPENQASM 3.0;
defcalgrammar "openpulse";
cal {
port d0;
frame fr1 = newframe(d0, 100.0, 5.0);
waveform wf = constant(1.0+2.0im, 100.0ns);
play(fr1, wf);
}
"""
module = pyqasm.loads(qasm, play_in_cal_block = False)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
ERROR:pyqasm: Error at line 5, column 11 in QASM file
> > > > > > play(fr1, wf)
pyqasm.exceptions.ValidationError: 'Play' function is only allowed in 'defcal' block
```
* [**delay()**](https://openqasm.com/versions/3.0/language/openpulse.html#delay) — Advance the `time-cursor` of a frame by a given `duration`.
* [**barrier()**](https://openqasm.com/versions/3.0/language/openpulse.html#barrier) — `synchronize` the time-cursor of multiple frames to the same point in time.
## Capabilities
* [**Duration Tracking**](https://openqasm.com/versions/3.0/language/openpulse.html#timing)
* Each frame carries an internal **time-cursor** that advances as operations (`play`, `delay`, `capture`) are applied.
* pyqasm performs semantic tracking of `durations`, ensuring that timing across frames remains consistent throughout the program.
* This enables correct validation of `defcal` blocks, which must have deterministic total durations across all execution paths.
* **PyQASM** Supports [**Frames-Collisions**](https://openqasm.com/versions/3.0/language/openpulse.html#collisions) validation in `defcal` blocks.
* **PyQASM** also unrolls `cal` blocks, which is useful for debugging and visualization. Since, `defcal` are acted as functions, we are not unrolling them.
- **Currently, pyqasm does not support `extern` declarations in OpenPulse
blocks**.
For more details, please refer to the [OpenPulse
specification](https://openqasm.com/versions/3.0/language/openpulse.html).
## Examples
```python QASM-Code theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
defcalgrammar "openpulse";
complex[float[32]] amp = 1.0 + 2.0im;
cal {
port d0;
frame driveframe = newframe(d0, 5.0e9, 0.0);
waveform wf = gaussian(amp, 16ns, 4ns);
}
const float frequency_start = 4.5e9;
const float frequency_step = 1e6;
const int frequency_num_steps = 3;
defcal saturation_pulse $0 {
play(driveframe, constant(amp, 100e-6s));
}
cal {
set_frequency(driveframe, frequency_start);
}
for int i in [1:frequency_num_steps] {
cal {
shift_frequency(driveframe, frequency_step);
}
saturation_pulse $0;
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
qubit[1] __PYQASM_QUBITS__;
defcalgrammar "openpulse";
cal {
port d0;
frame driveframe = newframe(d0, 5000000000.0, 0.0, 0ns);
waveform wf = gaussian(1.0 + 2.0im, 16.0ns, 4.0ns);
}
defcal saturation_pulse() $0 {
play(driveframe, constant(amp, 0.0001s));
}
cal {
set_frequency(driveframe, 4500000000.0);
}
cal {
shift_frequency(driveframe, 4501000000.0);
}
saturation_pulse __PYQASM_QUBITS__[0];
cal {
shift_frequency(driveframe, 4502000000.0);
}
saturation_pulse __PYQASM_QUBITS__[0];
cal {
shift_frequency(driveframe, 4503000000.0);
}
saturation_pulse __PYQASM_QUBITS__[0];
```
```python QASM-Code theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
defcalgrammar "openpulse";
qubit[4] q;
const duration pulse_length_start = 20ns;
const duration pulse_length_step = 1ns;
const int pulse_length_num_steps = 3;
cal {
port d0;
frame driveframe = newframe(d0, 5.0e9, 0.0);
}
for int i in [1:pulse_length_num_steps] {
duration pulse_length = pulse_length_start + (i-1)*pulse_length_step;
duration sigma = pulse_length / 4;
// since we are manipulating pulse lengths it is easier to define and play the waveform in a `cal` block
cal {
waveform wf = gaussian(1.0 + 2.0im, pulse_length, sigma);
// assume frame can be linked from a vendor supplied `cal` block
play(driveframe, wf);
}
measure $0;
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
qubit[4] __PYQASM_QUBITS__;
defcalgrammar "openpulse";
cal {
port d0;
frame driveframe = newframe(d0, 5000000000.0, 0.0, 0ns);
}
cal {
waveform wf = gaussian(1.0 + 2.0im, 20.0ns, 5.0ns);
play(driveframe, wf);
}
measure __PYQASM_QUBITS__[0];
cal {
waveform wf = gaussian(1.0 + 2.0im, 21.0ns, 5.25ns);
play(driveframe, wf);
}
measure __PYQASM_QUBITS__[0];
cal {
waveform wf = gaussian(1.0 + 2.0im, 22.0ns, 5.5ns);
play(driveframe, wf);
}
measure __PYQASM_QUBITS__[0];
```
```python QASM-Code theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
defcalgrammar "openpulse";
// Raman transition detuning delta from the 5S1/2 to 5P1/2 transition
const float delta = 100e6;
// Hyperfine qubit frequency
const float qubit_freq = 6.0e9;
// Positional frequencies for the AODS to target the specific qubit
const float q1_pos_freq = 5.0e9;
const float q2_pos_freq = 5.0e9;
const float q3_pos_freq = 5.0e9;
// Calibrated amplitudes and durations for the Raman pulses supplied via the AOD envelopes
const complex[float[32]] q1*π_half_amp = 1.0 + 2.0im;
const complex[float[32]] q2*π*half_amp = 1.0 + 2.0im;
const complex[float[32]] q3*π_half_amp = 1.0 + 2.0im;
const duration pi_half_time = 10.0ns;
// Time-proportional phase increment
const float tppi_1 = 1.0;
const float tppi_2 = 1.0;
const float tppi_3 = 1.0;
cal {
port eom_a_port;
port eom_b_port;
port aod_port;
// Define the Raman frames, which are detuned by an amount delta from the 5S1/2 to 5P1/2 transition
// and offset from each other by the qubit_freq
frame raman_a_frame = newframe(eom_a_port, delta, 0.0);
frame raman_b_frame = newframe(eom_b_port, delta-qubit_freq, 0.0);
const complex[float[32]] raman_a_amp = 1.0 + 2.0im;
const complex[float[32]] raman_b_amp = 1.0 + 2.0im;
// Three frames to phase track each qubit's rotating frame of reference at it's frequency
frame q1_frame = newframe(aod_port, qubit_freq, 0.0);
frame q2_frame = newframe(aod_port, qubit_freq, 0.0);
frame q3_frame = newframe(aod_port, qubit_freq, 0.0);
// Generic gaussian envelope
waveform pi_half_sig = gaussian(1.0 + 2.0im, pi_half_time, 100ns);
// Waveforms ultimately supplied to the AODs. We mix our general Gaussian pulse with a sine wave to
// put a sideband on the outgoing pulse. This helps us target the qubit position while maintainig the
// desired Rabi rate.
waveform q1_pi_half_sig = mix(pi_half_sig, sine(q1_π_half_amp, pi_half_time, q1_pos_freq-qubit_freq, 0.0));
waveform q2_pi_half_sig = mix(pi_half_sig, sine(q2_π_half_amp, pi_half_time, q2_pos_freq-qubit_freq, 0.0));
waveform q3_pi_half_sig = mix(pi_half_sig, sine(q3_π_half_amp, pi_half_time, q3_pos_freq-qubit_freq, 0.0));
}
// π/2 pulses on all three qubits
defcal rx(angle theta) $1, $2, $3 {
// Simultaneous π/2 pulses
play(raman_a_frame, constant(raman_a_amp, pi_half_time));
play(raman_b_frame, constant(raman_b_amp, pi_half_time));
play(q1_frame, q1_pi_half_sig);
play(q2_frame, q2_pi_half_sig);
play(q3_frame, q3_pi_half_sig);
}
// π/2 pulse on only qubit $2
defcal rx(angle theta) $2 {
play(raman_a_frame, constant(raman_a_amp, pi_half_time));
play(raman_b_frame, constant(raman_b_amp, pi_half_time));
play(q2_frame, q2_pi_half_sig);
}
// Ramsey sequence on qubit 1 and 3, Hahn echo on qubit 2
for duration tau_val in [1us:1us:2us] {
// First π/2 pulse
rx(pi/2) $1, $2, $3;
// First half of evolution time
cal {
delay[tau_val/2] raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
}
// Hahn echo π pulse composed of two π/2 pulses
for int ct in [0:1]{
rx(π/2) $2;
}
cal {
// Align all frames
barrier raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
// Second half of evolution time
delay[tau_val/2] raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
// Time-proportional phase increment signals different amount
shift_phase(q1_frame, tppi_1 * tau_val);
shift_phase(q2_frame, tppi_2 * tau_val);
shift_phase(q3_frame, tppi_3 * tau_val);
}
// Second π/2 pulse
rx(π/2) $1, $2, $3;
}
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
qubit[4] __PYQASM_QUBITS__;
defcalgrammar "openpulse";
cal {
port eom_a_port;
port eom_b_port;
port aod_port;
frame raman_a_frame = newframe(eom_a_port, 100000000.0, 0.0, 0ns);
frame raman_b_frame = newframe(eom_b_port, -5900000000.0, 0.0, 0ns);
const complex[float[32]] raman_a_amp = 1.0 + 2.0im;
const complex[float[32]] raman_b_amp = 1.0 + 2.0im;
frame q1_frame = newframe(aod_port, 6000000000.0, 0.0, 0ns);
frame q2_frame = newframe(aod_port, 6000000000.0, 0.0, 0ns);
frame q3_frame = newframe(aod_port, 6000000000.0, 0.0, 0ns);
waveform pi_half_sig = gaussian(1.0 + 2.0im, 10.0ns, 100.0ns);
waveform q1_pi_half_sig = mix(pi_half_sig, sine(1.0 + 2.0im, 10.0ns, -1000000000.0, 0.0));
waveform q2_pi_half_sig = mix(pi_half_sig, sine(1.0 + 2.0im, 10.0ns, -1000000000.0, 0.0));
waveform q3_pi_half_sig = mix(pi_half_sig, sine(1.0 + 2.0im, 10.0ns, -1000000000.0, 0.0));
}
defcal rx(angle theta) $1, $2, $3 {
play(raman_a_frame, constant(raman_a_amp, pi_half_time));
play(raman_b_frame, constant(raman_b_amp, pi_half_time));
play(q1_frame, q1_pi_half_sig);
play(q2_frame, q2_pi_half_sig);
play(q3_frame, q3_pi_half_sig);
}
defcal rx(angle theta) $2 {
play(raman_a_frame, constant(raman_a_amp, pi_half_time));
play(raman_b_frame, constant(raman_b_amp, pi_half_time));
play(q2_frame, q2_pi_half_sig);
}
rx(1.5707963267948966) __PYQASM_QUBITS__[1], __PYQASM_QUBITS__[2], __PYQASM_QUBITS__[3];
cal {
delay[500.0ns] raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
}
rx(1.5707963267948966) __PYQASM_QUBITS__[2];
rx(1.5707963267948966) __PYQASM_QUBITS__[2];
cal {
barrier raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
delay[500.0ns] raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
shift_phase(q1_frame, 0.9735361584457891);
shift_phase(q2_frame, 0.9735361584457891);
shift_phase(q3_frame, 0.9735361584457891);
}
rx(1.5707963267948966) __PYQASM_QUBITS__[1], __PYQASM_QUBITS__[2], __PYQASM_QUBITS__[3];
rx(1.5707963267948966) __PYQASM_QUBITS__[1], __PYQASM_QUBITS__[2], __PYQASM_QUBITS__[3];
cal {
delay[1000.0ns] raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
}
rx(1.5707963267948966) __PYQASM_QUBITS__[2];
rx(1.5707963267948966) __PYQASM_QUBITS__[2];
cal {
barrier raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
delay[1000.0ns] raman_a_frame, raman_b_frame, q1_frame, q2_frame, q3_frame;
shift_phase(q1_frame, 2.920608475337346);
shift_phase(q2_frame, 2.920608475337346);
shift_phase(q3_frame, 2.920608475337346);
}
rx(1.5707963267948966) __PYQASM_QUBITS__[1], __PYQASM_QUBITS__[2], __PYQASM_QUBITS__[3];
```
```python QASM-Code theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
defcalgrammar "openpulse";
const duration electrical_delay = 100ns;
const float q0_ro_freq = 5.0e9;
const float q1_ro_freq = 6.0e9;
cal {
// the transmission/captures ports are the same for $0 and $1
port ro_tx;
port ro_rx;
// readout stimulus and capture frames of different frequencies
frame q0_stimulus_frame = newframe(ro_tx, q0_ro_freq, 0.0);
frame q0_capture_frame = newframe(ro_rx, q0_ro_freq, 0.0);
frame q1_stimulus_frame = newframe(ro_tx, q1_ro_freq, 0.0);
frame q1_capture_frame = newframe(ro_rx, q1_ro_freq, 0.0);
}
defcal multiplexed_readout_and_capture $0, $1 -> bit[2] {
bit[2] b;
int sairam;
waveform q0_ro_wf = constant(1.0 + 2.0im, 100ns);
waveform q1_ro_wf = constant(1.0 + 2.0im, 100ns);
// multiplexed readout
play(q0_stimulus_frame, q0_ro_wf);
play(q1_stimulus_frame, q1_ro_wf);
// simple boxcar kernel
waveform ro_kernel = constant(1.0 + 2.0im, 100ns);
barrier q0_stimulus_frame, q1_stimulus_frame, q0_capture_frame, q1_capture_frame;
delay[electrical_delay] q0_capture_frame, q1_capture_frame;
b[0] = capture_v2(q0_capture_frame, ro_kernel);
b[1] = capture_v2(q1_capture_frame, ro_kernel);
return b;
}
multiplexed_readout_and_capture $0, $1;
"""
module = pyqasm.loads(qasm_code)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm output theme={"dark"}
OPENQASM 3.0;
qubit[2] __PYQASM_QUBITS__;
defcalgrammar "openpulse";
cal {
port ro_tx;
port ro_rx;
frame q0_stimulus_frame = newframe(ro_tx, 5000000000.0, 0.0, 0ns);
frame q0_capture_frame = newframe(ro_rx, 5000000000.0, 0.0, 0ns);
frame q1_stimulus_frame = newframe(ro_tx, 6000000000.0, 0.0, 0ns);
frame q1_capture_frame = newframe(ro_rx, 6000000000.0, 0.0, 0ns);
}
defcal multiplexed_readout_and_capture() $0, $1 -> bit[2] {
bit[2] b;
int sairam;
waveform q0_ro_wf = constant(1.0 + 2.0im, 100.0ns);
waveform q1_ro_wf = constant(1.0 + 2.0im, 100.0ns);
play(q0_stimulus_frame, q0_ro_wf);
play(q1_stimulus_frame, q1_ro_wf);
waveform ro_kernel = constant(1.0 + 2.0im, 100.0ns);
barrier q0_stimulus_frame, q1_stimulus_frame, q0_capture_frame, q1_capture_frame;
delay[electrical_delay] q0_capture_frame, q1_capture_frame;
b[0] = capture_v2(q0_capture_frame, ro_kernel);
b[1] = capture_v2(q1_capture_frame, ro_kernel);
return b;
}
multiplexed_readout_and_capture __PYQASM_QUBITS__[0], __PYQASM_QUBITS__[1];
```
# Overview
Source: https://docs.qbraid.com/v2/pyqasm/user-guide/overview
*Python toolkit for OpenQASM program analysis, validation and compilation.*
## Motivation
[OpenQASM](https://openqasm.com/) is a powerful language for expressing hybrid quantum-classical programs,
but it lacks a comprehensive tool supporting the full capabilities of the language. PyQASM aims to fill this
gap by building upon the [`openqasm3.parser`](https://github.com/openqasm/openqasm/blob/ast-py/v1.0.1/source/openqasm/openqasm3/parser.py),
and providing support for semantic analysis and utilities for program compilation.
## Installation
PyQASM requires Python 3.10 or greater, and can be installed with pip as follows:
```shell theme={"dark"}
pip install pyqasm
```
### Optional dependencies
PyQASM offers integrations that require extra (optional) dependencies, which can be installed as follows:
To use the [CLI tool](/v2/pyqasm/user-guide/cli), install the `cli` extra:
```shell theme={"dark"}
pip install 'pyqasm[cli]'
```
To use the [circuit drawer tool](/v2/pyqasm/user-guide/circuit-drawer), install the `visualization` extra:
```shell theme={"dark"}
pip install 'pyqasm[visualization]'
```
### Install from source
You can also install from source by cloning this repository and running a pip install command
in the root directory of the repository:
```shell theme={"dark"}
git clone https://github.com/qBraid/pyqasm.git
cd pyqasm
pip install .
```
To include optional dependencies when installing from source, use the same "extras\_require" format, e.g.
```shell theme={"dark"}
pip install '.[cli,visualization]'
```
### Check version
You can view the version of pyqasm you have installed within a Python shell as follows:
```python theme={"dark"}
In [1]: import pyqasm
In [2]: pyqasm.__version__
```
## Using PyQASM
We will use simple QASM strings for demonstrating the core functionalities of `pyqasm` API.
A detailed overview of the supported QASM features can be found in the [Usage Examples](/v2/pyqasm/user-guide/examples) section.
### `load` and `dump`
The `load` and `dump` functions are used to read QASM code from a file and write QASM code to a file, respectively.
* **`load`**: Reads QASM code from a file and returns a `QasmModule` object.
* **`dump`**: Writes QASM code from a `QasmModule` object to a file.
```python theme={"dark"}
import pyqasm
# ensure that the file exists and the path is correct
file_path = "example.qasm"
# Load QASM code from the file into a QasmModule object
module = pyqasm.load(file_path)
# Write QASM code from a QasmModule object to a file
pyqasm.dump(module, "output.qasm")
```
### `loads` and `dumps`
The `loads` and `dumps` functions are used to read QASM code from a string and write QASM code to a string, respectively.
* **`loads`**: Reads QASM code from a string and returns a `QasmModule` object.
* **`dumps`**: Writes QASM code from a `QasmModule` object to a string.
```python Using loads and dumps theme={"dark"}
import pyqasm
qasm_code = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c = measure q;
"""
# Load QASM code from a string into a QasmModule object
module = pyqasm.loads(qasm_code)
# Write QASM code from a QasmModule object to a string
qasm_code = pyqasm.dumps(module)
print(qasm_code)
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c = measure q;
```
### The `QasmModule` object
The `QasmModule` object is the main data structure used to represent a QASM program. The two important
methods of the `QasmModule` object are `validate` and `unroll` -
* `validate` : Used to check the semantic validity of the QASM program represented by the `QasmModule` object.
```python Using validate method theme={"dark"}
import pyqasm
qasm_str = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c = measure q;
"""
module = pyqasm.loads(qasm_str)
module.validate()
```
```python Output theme={"dark"}
None
```
Raises an exception if the program is not semantically valid and points out the error in the program.
```python Using validate method with invalid QASM code theme={"dark"}
import pyqasm
qasm_str = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c = measure q[3];
"""
module = pyqasm.loads(qasm_str)
module.validate()
```
```bash Output theme={"dark"}
Traceback (most recent call last):
...
ValidationError: Index 3 out of range for register of size 2 in qubit
```
To see the full capabilities of the semantic validation, refer to the [Usage Examples](/v2/pyqasm/user-guide/examples) section.
* `unroll` : Used to unroll the QASM program represented by the `QasmModule` object. Also performs semantic validation
while unrolling the program.
```python Using unroll method theme={"dark"}
import pyqasm
qasm_str = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
bit[5] c;
h q;
cx q[0], q[1];
c = measure q;
"""
module = pyqasm.loads(qasm_str)
module.unroll()
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
bit[5] c;
h q[0];
h q[1];
h q[2];
h q[3];
h q[4];
cx q[0], q[1];
c[0] = measure q[0];
c[1] = measure q[1];
c[2] = measure q[2];
c[3] = measure q[3];
c[4] = measure q[4];
```
A useful argument for the method is `external_gates` that takes in a list of gate names
considered external to the program. Only the number of parameters and qubit arguments must
be declared for validation but the body of the gate is not required in the program.
```python Using external_gates theme={"dark"}
import pyqasm
qasm_str = """
OPENQASM 3.0;
include "stdgates.inc";
gate custom (p1) q1, q2, q3 {
// empty body
}
qubit[4] q;
custom(pi / 2) q[0], q[1], q[2];
cx q[1], q[2];
"""
module = pyqasm.loads(qasm_str)
module.unroll(external_gates=["custom"])
print(pyqasm.dumps(module))
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
custom(1.5707963267948966) q[0], q[1], q[2];
cx q[1], q[2];
```
For more details about the `QasmModule` and its features, please refer to our
[API Reference](https://qbraid.github.io/pyqasm/stubs/pyqasm.QasmModule.html#pyqasm.QasmModule).
[Currently Supported Operations](https://github.com/qBraid/pyqasm/blob/main/src/README.md) for OpenQASM language
features supported, in progress, and planned for future support.
## Contributing
* Interested in contributing code, or making a PR? See
[CONTRIBUTING.md](https://github.com/qBraid/pyqasm/blob/main/CONTRIBUTING.md)
* For feature requests and bug reports:
[Submit an issue](https://github.com/qBraid/pyqasm/issues)
* For discussions, and specific questions about pyqasm, or
other topics, [join our discord community](https://discord.gg/TPBU2sa8Et)
* For questions that are more suited for a forum, post to
[Quantum Computing Stack Exchange](https://quantumcomputing.stackexchange.com/)
with the [`pyqasm`](https://quantumcomputing.stackexchange.com/questions/tagged/pyqasm) tag.
## Citation
If you use PyQASM in your research, we kindly request that you cite it appropriately.
The BibTeX entry below is aligned with the latest stable release. For the most up-to-date
citation details, please refer to [CITATION.cff](https://github.com/qBraid/pyqasm/blob/main/CITATION.cff).
```tex theme={"dark"}
@software{Gupta_PyQASM_Python_package_2026,
author = {Gupta, Harshit and Hill, Ryan James},
license = {Apache-2.0},
month = jul,
title = {{PyQASM: Python toolkit for OpenQASM program analysis and compilation.}},
url = {https://github.com/qBraid/pyqasm},
version = {1.0.4},
year = {2026}
}
```
## License
[Apache-2.0 License](https://www.apache.org/licenses/LICENSE-2.0)
# qbraid-qir
Source: https://docs.qbraid.com/v2/qir/api-reference/qbraid-qir
# Cirq conversions
Source: https://docs.qbraid.com/v2/qir/user-guide/cirq-qir
## Example Usage
Convert a `Cirq` circuit to `QIR` code:
```python Code theme={"dark"}
import cirq
from qbraid_qir import dumps
from qbraid_qir.cirq import cirq_to_qir
# create a test circuit
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.measure(q0, q1))
# convert to QIR
module = cirq_to_qir(circuit, name="bell")
# saves to .ll and .bc files in working directory
dumps(module)
print(module)
```
```output Output theme={"dark"}
; ModuleID = 'bell'
source_filename = "bell"
%Qubit = type opaque
%Result = type opaque
define void @main() #0 {
entry:
call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
ret void
}
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)
declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="custom" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
```
### Note
Circuits constructed using qubits of type `cirq.NameQubit` or `cirq.GridQubit` will be transformed to type `cirq.LineQubit` before conversion to QIR.
Execute the QIR program using the [qir-runner](https://github.com/qir-alliance/qir-runner) command line tool:
```bash theme={"dark"}
$ qir-runner -f bell.bc
```
[https://github.com/qBraid/qbraid-qir/tree/main/docker/linux-amd64](https://github.com/qBraid/qbraid-qir/tree/main/docker/linux-amd64)
# Overview
Source: https://docs.qbraid.com/v2/qir/user-guide/overview
*qBraid-SDK extension providing support for QIR conversions.*
## Motivation
This project aims to make QIR
representations accessible via the qBraid-SDK
transpiler, and by doing so, open the
door to language-specific conversions from any and all high-level quantum
languages [supported](/v2/sdk/user-guide/overview/#supported-frontends) by
qbraid.
See QIR Alliance: Why do we need it?
## Installation
qBraid-QIR requires Python 3.10 or greater, and can be installed with pip as follows:
```shell theme={"dark"}
pip install qbraid-qir
```
### Optional dependencies
qBraid-QIR offers integrations that require extra (optional) dependencies, which can be installed as follows:
For Cirq to QIR conversions, install the `cirq` extra:
```shell theme={"dark"}
pip install 'qbraid-qir[cirq]'
```
For OpenQASM 3 to QIR conversions, install the `qasm3` extra:
```shell theme={"dark"}
pip install 'qbraid-qir[qasm3]'
```
For Qiskit to QIR conversions, install the `qiskit` extra:
```shell theme={"dark"}
pip install 'qbraid-qir[qiskit]'
```
For Squin to QIR conversions, install the `squin` extra:
```shell theme={"dark"}
pip install 'qbraid-qir[squin]'
```
### Install from source
You can also install from source by cloning this repository and running a pip install command
in the root directory of the repository:
```shell theme={"dark"}
git clone https://github.com/qBraid/qbraid-qir.git
cd qbraid-qir
pip install .
```
To include optional dependencies when installing from source, use the same "extras\_require" format, e.g.
```shell theme={"dark"}
pip install '.[qiskit,cirq,qasm3,squin]'
```
## Check version
You can view the version of qbraid-qir you have installed within a Python shell as follows:
```python theme={"dark"}
In [1]: import qbraid_qir
In [2]: qbraid_qir.__version__
```
## Usage examples
### Qiskit conversions
```python theme={"dark"}
from qiskit import QuantumCircuit
from qbraid_qir.qiskit import qiskit_to_qir
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
module = qiskit_to_qir(qc, name="bell")
ir = str(module)
```
### Cirq conversions
```python theme={"dark"}
import cirq
from qbraid_qir.cirq import cirq_to_qir
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
cirq.H(q0),
cirq.CNOT(q0, q1),
cirq.measure(q0, q1)
)
module = cirq_to_qir(circuit, name="my-circuit")
ir = str(module)
```
### OpenQASM 3 conversions
```python theme={"dark"}
from qbraid_qir.qasm3 import qasm3_to_qir
program = """
OPENQASM 3;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
module = qasm3_to_qir(program, name="my-program")
ir = str(module)
```
[Currently Supported Operations](https://github.com/qBraid/qbraid-qir/tree/main/qbraid_qir/qasm3/README.md)
### QIR to Squin conversions
```python theme={"dark"}
from qbraid_qir.squin import load
from pyqir import BasicQisBuilder, SimpleModule
mod = SimpleModule("bell", num_qubits=2, num_results=2)
qis = BasicQisBuilder(mod.builder)
qis.h(mod.qubits[0])
qis.cnot(mod.qubits[0], mod.qubits[1])
squin_kernel = load(mod._module)
squin_kernel.print()
```
## Architecture diagram
qBraid-SDK transpiler hub-and-spokes architecture with
qbraid-qir integration (left) mapped to language specific conversion step in QIR abstraction
[layers](https://www.qir-alliance.org/qir-book/concepts/why-do-we-need.html) (right).
## Contributing
* Interested in contributing code, or making a PR? See
[CONTRIBUTING.md](https://github.com/qBraid/qbraid-qir/blob/main/CONTRIBUTING.md)
* For feature requests and bug reports:
[Submit an issue](https://github.com/qBraid/qbraid-qir/issues)
* For discussions, and specific questions about qBraid-QIR [join our discord community](https://discord.gg/TPBU2sa8Et)
* For questions that are more suited for a forum, post to
[Quantum Computing Stack Exchange](https://quantumcomputing.stackexchange.com/)
with the [`qbraid`](https://quantumcomputing.stackexchange.com/questions/tagged/qbraid) tag.
## Citation
If you use qBraid-QIR in your research, we kindly request that you cite it appropriately.
The BibTeX entry below is aligned with the latest stable release. For the most up-to-date
citation details, please refer to [CITATION.cff](https://github.com/qBraid/qbraid-qir/blob/main/CITATION.cff).
```tex theme={"dark"}
@software{Gupta_qBraid-QIR_Python_package_2026,
author = {Gupta, Harshit and Hill, Ryan James},
license = {Apache-2.0},
month = may,
title = {{qBraid-QIR: Python package for QIR conversions, integrations, and utilities.}},
url = {https://github.com/qBraid/qbraid-qir},
version = {0.6.0},
year = {2026}
}
```
## Acknowledgements
This project was conceived in cooperation with the Quantum Open Source Foundation ([QOSF](https://qosf.org/)).
## License
[Apache-2.0 License](https://www.apache.org/licenses/LICENSE-2.0)
# QIR Profiles
Source: https://docs.qbraid.com/v2/qir/user-guide/profiles
QIR (Quantum Intermediate Representation) profiles in `qbraid-qir` define the rules, restrictions,
and capabilities that govern how OpenQASM 3 programs are converted to QIR. When converting a QASM
program to QIR using `qbraid-qir`, you must specify a profile—either **Base** or **Adaptive**—which
determines the allowed quantum operations, branching, measurement handling, and output recording.
This ensures that the generated QIR is compliant with the requirements of the target quantum backend
or workflow.
## Available Profiles
### Base Profile
* **Name:** `Base`
* **Capabilities:**
* Conditional execution (`if` statements) is supported.
* Only sequential output recording is supported.
* **Restrictions:**
* No forward branching (no `goto` or loops).
* Qubit reuse after measurement is **not** allowed.
* Measurement tracking is **not** enabled.
* No grouped output recording.
* No qubit reuse after measurement.
* Barriers must cover **all** qubits (no subset barriers).
* Only a fixed set of gates is supported.
### Adaptive Profile
* **Name:** `AdaptiveExecution`
* **Capabilities:**
* Conditional execution and forward branching are supported.
* Qubit reuse after measurement is **allowed**.
* Measurement tracking is enabled.
* Grouped output recording is supported (register structure is preserved).
* Barriers must cover **all** qubits.
* Supports a broader set of gates and more flexible control flow.
* **Restrictions:**
* Subset barriers are **not** allowed (barriers must cover all qubits).
* Only constant parameters for parameterized gates.
## How to Use
When converting QASM to QIR, specify the profile using the `profile` argument:
```python converting qasm to qir theme={"dark"}
from qbraid_qir.qasm3 import qasm3_to_qir
qasm3_code = """
OPENQASM 3;
qubit[2] q;
bit[2] c;
h q[0];
c[0] = measure q[0];
if (c[0]) {
x q[1];
}
c[1] = measure q[1];
"""
# Convert using the Base profile
qir_base = qasm3_to_qir(qasm3_code, profile="base")
print(qir_base)
# Convert using the Adaptive profile
qir_adaptive = qasm3_to_qir(qasm3_code, profile="adaptive")
print(qir_adaptive)
```
```llvm base profile output theme={"dark"}
; ModuleID = 'program-b27e5a7a-4b58-11f0-824a-cae339f58535'
source_filename = "program-b27e5a7a-4b58-11f0-824a-cae339f58535"
%Qubit = type opaque
%Result = type opaque
define void @program-b27e5a7a-4b58-11f0-824a-cae339f58535() #0 {
entry:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__h__body(%Qubit* null)
call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)
%0 = call i1 @__quantum__qis__read_result__body(%Result* null)
br i1 %0, label %then, label %else
then: ; preds = %entry
call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
br label %continue
else: ; preds = %entry
br label %continue
continue: ; preds = %else, %then
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
call void @__quantum__rt__result_record_output(%Result* null, i8* null)
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
ret void
}
declare void @__quantum__rt__initialize(i8*)
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1
declare i1 @__quantum__qis__read_result__body(%Result*)
declare void @__quantum__qis__x__body(%Qubit*)
declare void @__quantum__rt__result_record_output(%Result*, i8*)
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
```
```llvm adaptive profile output theme={"dark"}
; ModuleID = 'program-b27f2086-4b58-11f0-824a-cae339f58535'
source_filename = "program-b27f2086-4b58-11f0-824a-cae339f58535"
%Qubit = type opaque
%Result = type opaque
define void @program-b27f2086-4b58-11f0-824a-cae339f58535() #0 {
entry:
call void @__quantum__rt__initialize(i8* null)
call void @__quantum__qis__h__body(%Qubit* null)
call void @__quantum__qis__mz__body(%Qubit* null, %Result* null)
%0 = call i1 @__quantum__qis__read_result__body(%Result* null)
br i1 %0, label %then, label %else
then: ; preds = %entry
call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
br label %continue
else: ; preds = %entry
br label %continue
continue: ; preds = %else, %then
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
call void @__quantum__rt__array_record_output(i64 2, i8* null)
call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
call void @__quantum__rt__result_record_output(%Result* null, i8* null)
ret void
}
declare void @__quantum__rt__initialize(i8*)
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1
declare i1 @__quantum__qis__read_result__body(%Result*)
declare void @__quantum__qis__x__body(%Qubit*)
declare void @__quantum__rt__array_record_output(i64, i8*)
declare void @__quantum__rt__result_record_output(%Result*, i8*)
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
```
The `qir_base` and `qir_adaptive` variables will contain the generated QIR code for the specified profile.
## Example QASM Programs
```qasm theme={"dark"}
OPENQASM 3;
qubit[2] q;
bit[2] c;
h q[0];
c[0] = measure q[0];
x q[1]; // Allowed
reset q[0]; // Not allowed in Base profile (qubit reuse forbidden)
```
The last line will cause an error in the Base profile because qubit reuse after measurement is not
permitted
```qasm theme={"dark"}
OPENQASM 3;
qubit[2] q;
bit[2] c;
h q[0];
c[0] = measure q[0];
reset q[0]; // Allowed in Adaptive profile
x q[0]; // Allowed in Adaptive profile
```
This program is valid in the Adaptive profile, which allows qubit reuse after measurement and reset.
```qasm theme={"dark"}
OPENQASM 3;
qubit[2] q;
bit[2] c;
h q[0];
c[0] = measure q[0];
if (c[0]) {
x q[1];
}
```
Both profiles support conditional execution, but only the `Adaptive` profile supports more advanced
branching and measurement-based control flow.
## Conclusion
When converting QASM to QIR with `qbraid-qir`, always choose the profile that **matches your target
backend's requirements** The `Base` profile is strict and suitable for simple, hardware-oriented
workflows, while the `Adaptive` profile enables more advanced quantum programming features such as
qubit reuse, grouped output, and flexible control flow.
# OpenQASM 3 conversions
Source: https://docs.qbraid.com/v2/qir/user-guide/qasm3-qir
```python Code theme={"dark"}
from qbraid_qir.qasm3 import qasm3_to_qir
program = """
OPENQASM 3;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];
"""
module = qasm3_to_qir(program, name="bell")
ir = str(module)
```
```output Output theme={"dark"}
; ModuleID = 'bell'
source_filename = "bell"
%Qubit = type opaque
%Result = type opaque
define void @main() #0 {
entry:
call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
ret void
}
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)
declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="custom" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
```
Execute the QIR program using the qir-runner command line tool:
```bash theme={"dark"}
$ qir-runner -f bell.bc
```
## Currently Supported Constructs
The `qbraid_qir.qasm3.qasm3_to_qir()` converter supports the following OpenQASM 3 constructs:
### 1. [Register Declarations](https://openqasm.com/language/types.html#quantum-types)
OpenQASM declarations of all forms `qreg`, `qubit`, `bit`, and `creg` are supported.
```qasm theme={"dark"}
OPENQASM 3;
// qubit declaration
qubit q1;
qubit[2] q2;
qreg q3[3];
qubit[1] q4;
// bit declaration
bit c1;
bit[2] c2;
creg c3[3];
bit[1] c4;
```
### 2. [Quantum Measurements](https://openqasm.com/language/insts.html#measurement)
OpenQASM measurements are supported which involve single qubit measurement and full register measurements.
Range based measurements are not supported currently.
```qasm theme={"dark"}
OPENQASM 3;
qubit[2] q1;
qubit[5] q2;
qubit q3;
bit[2] c1;
bit c2;
// supported
c1 = measure q1;
measure q1 -> c1;
c2[0] = measure q3[0];
//ERROR: not supported
c1[0:2] = measure q1[0:2];
```
### 3. [Quantum Reset](https://openqasm.com/language/insts.html#initialization)
Resets are supported on declared quantum registers in all forms.
```qasm theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
// qubit declarations
qubit q1;
qubit[2] q2;
qreg q3[3];
// reset operations
reset q1;
reset q2[1];
reset q3[2];
reset q3[:2];
```
### 4. Quantum Gates
* `pyqir._native` gates are supported along with support for `U3` and U2 gates. The `U[x]` gates are defined in terms of existing `rx` and `rz` gates according to the decomposition present on the [Qiskit UGate documentation](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.UGate) and [PhaseGate documentation](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.PhaseGate).
* Full set of the openqasm3 ["stdgates.inc" gates](https://openqasm.com/language/standard_library.html) are supported. These gates are decomposed with the help of the `pyqir._native` gate set and subsequently converted to QIR.
### 5. [Quantum Barriers](https://openqasm.com/language/delays.html#barrier-instruction)
Barriers are supported only if they are placed on ALL the qubits in the circuit. For example:
```qasm theme={"dark"}
qubit q[2];
U(0.1, 0.2, 0.3) q[0];
// Barrier on all qubits
barrier q;
// ERROR: Barrier on a subset of qubits
barrier q[0];
```
### 6. [Custom Quantum Gates](https://openqasm.com/language/gates.html#defining-gates)
Gates defined by users are supported as long as they are defined in terms of `pyqir._native` gate set. Identifier mapping in gate parameter expressions is not supported at the moment. Example:
```qasm theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
// Supported
gate custom2(g) s {
h s;
rz(g) s;
}
// Supported
gate custom(a,b,c) p, q {
custom2(a) q;
h p;
cx p,q;
rx(a) q;
ry(0.5/0.1) q;
}
qubit[2] q;
custom(2 + 3 - 1/5, 0.1, 0.3) q[0], q[1];
```
### 7. [Simple Branching Statements](https://openqasm.com/language/classical.html#if-else-statements)
Since QIR supports branching on a measurement result, **single bit branching** statements are supported at the moment. General boolean expressions and support for branching on full registers will be added in future. For example:
```qasm theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
h q;
measure q -> c;
// supported
if (c[0]) {
x q[0];
cx q[0], q[1];
}
if (c[1] == 1) {
cx q[1], q[2];
}
if (!c[2]) {
h q[2];
}
// ERROR: not supported
if (c == 8) {
x q[0];
}
// ERROR: not supported
int[4] element;
if (element > 5) {
y q[1];
}
```
### 8. [Expressions](https://openqasm.com/language/classical.html#low-level-classical-instructions)
* General expression evaluation involving literals and constants is supported.
* Expressions involving constants and variables are fully supported.
* Expressions with arrays are also supported (in global scopes).
```qasm expression with literals theme={"dark"}
OPENQASM 3;
qubit q;
// supported
rx(1.57) q;
rz(3-2*3) q;
rz(3-2*3\*(8/2)) q;
rx(-1.57) q;
rx(4%2) q;
```
```qasm expression with scalar variables theme={"dark"}
OPENQASM 3;
// scalar value assignment and declaration
int[32] a = 5;
float[32] b;
b = 3.14;
// constant value assignment and declaration
const int[32] c_int = 5;
const float[32] c_float = 3.141592;
// arbitary expressions
int[32] x = a + 5*c_int;
```
```qasm expression with arrays theme={"dark"}
OPENQASM 3;
// static initialization
array[int[32], 3, 2] arr_int = { {1, 2}, {3, 4}, {5, 6} };
array[float[32], 3, 2] arr_float32 = { {1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0} };
// assignment of individual elements
int a = 2;
arr_int[1][1] = a; // arr_int = { {1, 2}, {3, 2}, {5, 6} }
// array elements in expressions
float[32] b = arr_float32[1][1] * 3.1415; // b = 4.0*3.1415 = 12.566
qubit q;
rx(b) q; // rx(12.566) is applied on qubit q
```
### 9. [Variables and Types](https://openqasm.com/language/types.html)
* Scalar types `int`, `uint`, `float`, `bool` and `bit` with arbitrary sizes are supported.
* `array` type is supported for `int`, `uint`, `float`, `bool` and `bit`.
* `complex` and `angle` types are not supported yet.
```qasm scalar variables theme={"dark"}
OPENQASM 3;
// declarations
int a;
uint b;
int[2] c = 1;
uint[13] d = 2;
float[32] my_pi = 3.14;
// assignments and expressions
a = 5 \* c;
b = 6 - d;
```
```qasm array variables theme={"dark"}
OPENQASM 3;
// declarations
array[int[32], 3, 2] arr_int = { {1, 2}, {3, 4}, {5, 6} };
array[float[32], 3, 2] arr_float32;
// element assignment
float[32] c = 4.5;
arr_float32[1][1] = c; // arr_float32 = { {None, None}, {None, 4.5}, {None, None} }
arr_int[1][1] = 5; // arr_int = { {1, 2}, {3, 5}, {5, 6} }
// expressions
float[32] d = arr_float32[1][1] * 3.1415; // d = 4.5*3.1415 = 14.13775
```
### 10. [Loops](https://openqasm.com/language/classical.html#for-loops)
Both `for` and `while` loops are supported at the moment. For example:
```qasm simple for loop theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
int[32] i;
for int[32] i in [0:2] {
h q[i];
}
```
```qasm complex for loop theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
int j = 0;
h q;
for int i in [0:2]{
cx q[i], q[i+1];
h q[j];
j += 1;
}
h q[j];
measure q -> c;
```
```qasm simple while loop theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
int i = 0;
while (i < 3) {
h q[i];
cx q[i], q[i+1];
i += 1;
}
```
```qasm combined loops theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
int i = 0;
while (i < 2) {
for int j in {0, 1} {
h q[j];
}
i += 1;
}
```
### 11. [**Aliasing**](https://openqasm.com/language/types.html#aliasing)
* Aliasing quantum registers is supported and can be used to refer to the same quantum register with different names.
* Aliases are currently supported in global scope of the qasm3 program, with support for function scopes coming soon.
```qasm simple aliasing theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
qubit[5] q;
let myqreg0 = q;
let myqreg1 = q[1];
let myqreg2 = q[1:];
let myqreg3 = q[:4];
let myqreg4 = q[1:4];
let myqreg5 = q[1:2:4];
let myqreg6 = q[{0, 1}];
x myqreg0[0];
h myqreg1;
cx myqreg2[0], myqreg2[1];
cx myqreg3[2], myqreg3[3];
ccx myqreg4;
swap myqreg5[0], myqreg5[1];
cz myqreg6;
```
```qasm aliasing in scope theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
qubit[4] q;
bit[4] c;
h q;
measure q -> c;
if (c[0]) {
let alias = q[0:2];
x alias[0];
cx alias[0], alias[1];
}
if (c[1] == 1) {
cx q[1], q[2];
}
if (!c[2]) {
h q[2];
}
```
### 12. [Subroutines](https://openqasm.com/language/subroutines.html)
We support QASM3 subroutines in the QIR converter
Subroutines are supported in QASM3 and can be defined using the `def` keyword and are only allowed in the global scope.
```qasm simple definition theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) { // void function
h q;
return;
}
```
```qasm definition with return type theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) -> int[32] {
h q;
return 1;
}
```
Subroutines can be called from within the main program or from other subroutines.
```qasm simple call theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
def my_function(qubit a, float[32] b) {
rx(b) a;
float[64] c = 2*b;
rx(c) a;
return;
}
qubit q;
bit c;
float[32] r = 3.14;
my_function(q, r);
```
```qasm call in expression theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) -> bool{
h q;
return true;
}
qubit q;
bool b = my_function(q);
```
```qasm call from another subroutine theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) {
h q;
return;
}
def my_function_2(qubit[2] q) {
my_function(q[1]);
return;
}
qubit[2] q;
my_function_2(q);
```
* Quantum arguments of type `qubit` are supported in subroutines.
* Classical arguments are supported for any scalar type and can return values of any scalar type.
* All argument validations mentioned in the OpenQASM3 specification are supported.
```qasm mixed arguments theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q, int[32] a, float[32] b) {
rz(a) q;
rx(b) q;
return;
}
qubit q;
int[32] n = 5;
float[32] b = 3.14;
my_function(q, n, b);
```
```qasm simple return type theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) -> bool {
h q;
return true;
}
qubit q;
bool b = my_function(q);
```
```qasm testing return values theme={"dark"}
OPENQASM 3;
include "stdgates.inc";
def my_function(qubit q) -> float[32] {
h q;
return 3.14;
}
def my_function_2(qubit q, float[32] r) {
rx(r) q;
return;
}
qubit[2] q;
float[32] r1 = my_function(q[0]);
my_function_2(q[0], r1);
array[float[32], 1, 1] r2 = {{3.14}};
my_function_2(q[1], r2[0,0]);
```
* Add `mutable` and `readonly` arrays as classical arguments in a subroutines
* Support looping statements inside a subroutine - Support switch statements
inside a subroutine - Support for aliasing in a subroutine
### 13. [Switch](https://openqasm.com/language/classical.html#the-switch-statement)
Switch statements in QASM3 are supported in the QIR converter. These are defined according to the OpenQASM3 spec
and support all the features mentioned in the specification.
```qasm simple switch theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
const int i = 4;
const int j = 4;
qubit q;
switch(i) {
case 6, j {
x q;
}
default {
z q;
}
}
```
```qasm nested switch theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
const int i = 1;
qubit q;
switch(i) {
case 1,3,5,7 {
int j = 4; // definition inside scope
switch(j) {
case 1,3,5,7 {
x q;
}
case 2,4,6,8 {
y q; // this will be executed
}
default {
z q;
}
}
}
case 2,4,6,8 {
y q;
}
default {
z q;
}
}
```
# Squin conversions
Source: https://docs.qbraid.com/v2/qir/user-guide/qir-squin
This module enables conversion from Quantum Intermediate Representation ([PyQIR](https://github.com/qir-alliance/pyqir)) to Squin kernels, allowing you to execute QIR circuits using the [Bloqade](https://github.com/QuEraComputing/bloqade) SDK's Squin execution engine. [Squin](https://bloqade.quera.com/dev/digital/dialects_and_kernels/squin/) provides a high-performance quantum circuit execution environment, and this conversion opens the door to running QIR circuits on Bloqade simulators and hardware backends.
## Installation
```shell theme={"dark"}
pip install 'qbraid-qir[squin]'
```
## Conversions
This section highlights the different ways in which the converter `load()` can be used with various formats and input types. Each conversion method is demonstrated with practical examples below.
### PyQIR String to Squin kernel
Convert PyQIR `IR string` to a Squin kernel:
```python Code theme={"dark"}
from qbraid_qir.squin import load
qir_str = """
; ModuleID = 'bell'
source_filename = "bell"
%Qubit = type opaque
define void @main() #0 {
entry:
call void @__quantum__qis__h__body(%Qubit* null)
call void @__quantum__qis__cnot__body(%Qubit* null, %Qubit* inttoptr (i64 1 to %Qubit*))
ret void
}
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="custom" "required_num_qubits"="2" "required_num_results"="2" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
"""
squin_kernel = load(qir_str, kernel_name="bell")
squin_kernel.print()
```
```output Output theme={"dark"}
func.func @bell() -> !py.NoneType {
^0(%bell_self):
│ %0 = func.invoke new() : !py.Qubit maybe_pure=False
│ %1 = func.invoke new() : !py.Qubit maybe_pure=False
│ %2 = func.invoke h(%0) : !py.NoneType maybe_pure=False
│ %3 = func.invoke cx(%0, %1) : !py.NoneType maybe_pure=False
│ %4 = func.const.none() : !py.NoneType
│ func.return %4
} // func.func bell
```
### PyQIR Module to Squin kernel
Convert a PyQIR `Module` object to a Squin kernel:
```python Code theme={"dark"}
from qbraid_qir.squin import load
from pyqir import BasicQisBuilder, SimpleModule
mod = SimpleModule("ghz", num_qubits=3, num_results=3)
qis = BasicQisBuilder(mod.builder)
qis.h(mod.qubits[0])
qis.cx(mod.qubits[0], mod.qubits[1])
qis.cx(mod.qubits[1], mod.qubits[2])
squin_kernel = load(mod._module)
squin_kernel.print()
```
```output Output theme={"dark"}
func.func @main() -> !py.NoneType {
^0(%main_self):
│ %0 = func.invoke new() : !py.Qubit maybe_pure=False
│ %1 = func.invoke new() : !py.Qubit maybe_pure=False
│ %2 = func.invoke new() : !py.Qubit maybe_pure=False
│ %3 = func.invoke h(%0) : !py.NoneType maybe_pure=False
│ %4 = func.invoke cx(%0, %1) : !py.NoneType maybe_pure=False
│ %5 = func.invoke cx(%1, %2) : !py.NoneType maybe_pure=False
│ %6 = func.const.none() : !py.NoneType
│ func.return %6
} // func.func main
```
### Conversion from File
Load PyQIR IR from a `file` (`.ll` for LLVM IR text or `.bc` for LLVM bitcode):
```python Code theme={"dark"}
from qbraid_qir.squin import load
# Load from .ll file (LLVM IR text)
kernel = load("path/to/circuit.ll", kernel_name="my_circuit")
# Load from .bc file (LLVM bitcode)
kernel = load("path/to/circuit.bc", kernel_name="my_circuit")
```
### Integration with OpenQASM 3
Convert `OpenQASM3` programs to Squin kernels using `qbraid-qir`:
```python Code theme={"dark"}
from qbraid_qir.qasm3 import qasm3_to_qir
from qbraid_qir.squin import load
program = """
OPENQASM 3;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
x q[0], q[1];
rx(pi/2) q[0];
"""
module = qasm3_to_qir(program, name="my_program")
squin_kernel = load(str(module))
squin_kernel.print()
```
```output Output theme={"dark"}
func.func @main() -> !py.NoneType {
^0(%main_self):
│ %0 = func.invoke new() : !py.Qubit maybe_pure=False
│ %1 = func.invoke new() : !py.Qubit maybe_pure=False
│ %2 = func.invoke h(%0) : !py.NoneType maybe_pure=False
│ %3 = func.invoke x(%0) : !py.NoneType maybe_pure=False
│ %4 = func.invoke x(%1) : !py.NoneType maybe_pure=False
│ %5 = py.constant.constant 1.5707963267948966 : !py.float
│ %6 = func.invoke rx(%5, %0) : !py.NoneType maybe_pure=False
│ %7 = func.const.none() : !py.NoneType
│ func.return %7
} // func.func main
```
### Integration with CUDA-Q
Convert `CUDA-Q` kernels to Squin kernels
```python Code theme={"dark"}
import cudaq
from qbraid_qir.squin import load
@cudaq.kernel
def bell():
q = cudaq.qvector(2)
h(q[0])
cx(q[0], q[1])
# Generate QIR from kernel
qir_str = cudaq.translate(bell, format="qir-base")
squin_kernel = load(qir_str, kernel_name="bell")
squin_kernel.print()
```
**CUDA-Q** is capable of exporting QIR using multiple profiles (such
as`qir-base, qir-adaptive,` etc.). Currently, the qbraid-qir Squin integration
supports only QIR generated with the **qir-base** profile. Ensure that when
using **cudaq.translate**, you specify **format="qir-base"** for
compatibility.
### Using Qubit Register as Argument
Create a kernel that accepts a qubit register as an argument, enabling composition of kernel functions:
```python Code theme={"dark"}
from qbraid_qir.squin import load
from pyqir import BasicQisBuilder, SimpleModule
mod = SimpleModule("main", num_qubits=3, num_results=3)
qis = BasicQisBuilder(mod.builder)
qis.h(mod.qubits[0])
qis.cx(mod.qubits[0], mod.qubits[1])
squin_kernel = load(
module=mod._module,
kernel_name="bell",
register_as_argument=True,
register_argument_name="qreg"
)
squin_kernel.print()
```
```output Output theme={"dark"}
func.func @bell(qreg : !py.IList[!py.Qubit, Literal(2,int)]) -> !py.NoneType {
^0(%bell_self, %qreg):
│ %0 = py.constant.constant 0 : !py.int
│ %1 = py.indexing.getitem(%qreg : !py.IList[!py.Qubit, Literal(2,int)], %0) : !py.Qubit
│ %2 = py.constant.constant 1 : !py.int
│ %3 = py.indexing.getitem(%qreg : !py.IList[!py.Qubit, Literal(2,int)], %2) : !py.Qubit
│ %4 = func.invoke h(%1) : !py.NoneType maybe_pure=False
│ %5 = func.invoke cx(%1, %3) : !py.NoneType maybe_pure=False
│ %6 = func.const.none() : !py.NoneType
│ func.return %6
} // func.func bell
```
## Supported Gates
The following gates are currently supported for conversion from PyQIR to Squin kernels:
| QIR Gate | Squin Gate | Description |
| ---------------------------- | ------------- | ---------------------- |
| `__quantum__qis__h__body` | `squin.h` | Hadamard gate |
| `__quantum__qis__x__body` | `squin.x` | Pauli-X gate |
| `__quantum__qis__y__body` | `squin.y` | Pauli-Y gate |
| `__quantum__qis__z__body` | `squin.z` | Pauli-Z gate |
| `__quantum__qis__s__body` | `squin.s` | S gate (π/2 phase) |
| `__quantum__qis__t__body` | `squin.t` | T gate (π/4 phase) |
| `__quantum__qis__s__adj` | `squin.s_adj` | S† gate (adjoint S) |
| `__quantum__qis__t__adj` | `squin.t_adj` | T† gate (adjoint T) |
| `__quantum__qis__rx__body` | `squin.rx` | Rotation around X-axis |
| `__quantum__qis__ry__body` | `squin.ry` | Rotation around Y-axis |
| `__quantum__qis__rz__body` | `squin.rz` | Rotation around Z-axis |
| `__quantum__qis__cnot__body` | `squin.cx` | Controlled-NOT gate |
| `__quantum__qis__cz__body` | `squin.cz` | Controlled-Z gate |
## Next Steps
Our current implementation provides comprehensive support for standard QIR gates and circuit conversion. We are actively working on expanding functionality in the following areas:
* **Measurement operations**: Support for measurement operations is planned for a future release.
* **Classical control flow**: Conditional gates and classical control flow are on our roadmap.
* **Custom gates**: We currently support all standard QIR gates listed in the [Supported Gates](#supported-gates) table, with plans to extend support for custom gate definitions.
# Qiskit conversions
Source: https://docs.qbraid.com/v2/qir/user-guide/qiskit-qir
## Installation
Install the `qiskit` extra:
```shell theme={"dark"}
pip install 'qbraid-qir[qiskit]'
```
Requires Qiskit >= 2.0.
## Example Usage
Convert a Qiskit `QuantumCircuit` to QIR:
```python Code theme={"dark"}
from qiskit import QuantumCircuit
from qbraid_qir import dumps
from qbraid_qir.qiskit import qiskit_to_qir
# create a test circuit
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
# convert to QIR
module = qiskit_to_qir(qc, name="bell")
# saves to .ll and .bc files in working directory
dumps(module)
print(module)
```
```output Output theme={"dark"}
; ModuleID = 'bell'
source_filename = "bell"
%Qubit = type opaque
%Result = type opaque
define void @main() #0 {
entry:
call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
ret void
}
declare void @__quantum__qis__h__body(%Qubit*)
declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)
declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1
attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="custom" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }
!llvm.module.flags = !{!0, !1, !2, !3}
!0 = !{i32 1, !"qir_major_version", i32 1}
!1 = !{i32 7, !"qir_minor_version", i32 0}
!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
!3 = !{i32 1, !"dynamic_result_management", i1 false}
```
## Transpilation
If your circuit contains gates not directly supported by QIR, you can set `transpile=True` to automatically decompose them into the supported basis gate set before conversion:
```python theme={"dark"}
module = qiskit_to_qir(qc, name="my-circuit", transpile=True)
```
## Supported Operations
| Category | Gates |
| ---------------- | --------------------------------------------------------- |
| **Single-qubit** | `h`, `x`, `y`, `z`, `s`, `sdg`, `t`, `tdg`, `id`, `reset` |
| **Rotations** | `rx`, `ry`, `rz` |
| **Two-qubit** | `cx`, `cz`, `swap` |
| **Three-qubit** | `ccx` |
| **Measurement** | `measure` |
| **Other** | `barrier`, `delay` |
# qbraid
Source: https://docs.qbraid.com/v2/sdk/api-reference/qbraid
# Interface
Source: https://docs.qbraid.com/v2/sdk/user-guide/interface
Generate random circuits, unitary matrices, and test circuit equivalence.
API Reference:
[qbraid.interface](https://qbraid.github.io/qBraid/api/qbraid.interface.html)
## Generate random circuits
Generate a random quantum circuit object of any registered program type from
`qbraid.NATIVE_REGISTRY`. For example:
```python Input theme={"dark"}
from qbraid import random_circuit
circuit = random_circuit("qiskit") # Swap "cirq", "braket", "qasm3", etc.
print(circuit)
```
```text Sample Output theme={"dark"}
q_0: ─────────────■──────────────■─
│ │
q_1: ─────────────■──────────────X─
┌─────────────────────────┐ │
q_2: ┤ U(3.8035,5.0708,2.3633) ├─X─
└─────────────────────────┘
```
Optionally specify number of qubits, circuit depth, and whether to measure over all qubits.
```python Input theme={"dark"}
circuit = random_circuit("cirq", num_qubits=2, depth=2, measure=True)
print(circuit)
```
```text Sample Output theme={"dark"}
q_0: ───×───Rz(1.34π)───────────M('c_0')───
│
q_1: ───×───U3(0.0, 0.0, 0.0)───M('c_1')───
```
## Generate random unitaries
Generate random unitary matrices of any specified dimension (`dim`):
```python Input theme={"dark"}
from qbraid.interface import random_unitary_matrix
unitary = random_unitary_matrix(2)
print(unitary)
```
```text Sample Output theme={"dark"}
[[-0.46060623-0.33349006j -0.82238991+0.01735275j]
[ 0.48450787-0.66473935j 0.01019327+0.56856822j]]
```
## Check circuit unitary equivalence
Check whether two quantum circuits have equivalent unitary representations, regardless of their types.
All input circuits must be registered in `qbraid.programs.NATIVE_REGISTRY`. Below is an example demonstrating
the comparison between two circuits that each create a Bell state using different quantum programming libraries
(Cirq and Qiskit).
```python theme={"dark"}
import cirq
import qiskit
from qbraid.interface import circuits_allclose
def cirq_bell():
circuit = cirq.Circuit()
q0, q1 = cirq.LineQubit.range(2)
circuit.append(cirq.H(q0))
circuit.append(cirq.CNOT(q0, q1))
return circuit
def qiskit_bell():
circuit = qiskit.QuantumCircuit(2)
circuit.h(0)
circuit.cx(0,1)
return circuit
# Returns True if the unitary matrices of the two circuits are equivalent
print(circuits_allclose(cirq_bell(), qiskit_bell())) # Output: True
```
This function also supports several optional keyword arguments that allow customization of the equivalence checking process:
* `index_contig`: If set to True, maps both circuits to use sequential qubit indexing prior to computing their unitaries. Defaults to False.
* `allow_rev_qubits`: If set to True, allows the function to consider circuits as equivalent even if their qubits are in reversed order. Defaults to False.
* `strict_gphase`: If set to False, ignores differences in global phase between the circuits. Defaults to True.
* `atol`: Sets the absolute tolerance level for the numerical comparison of unitaries, using np.allclose. Defaults to 1e-7.
Here are some examples of how these arguments could be used:
```python theme={"dark"}
# ignore global phase and use an increased error margin
circuits_allclose(circuit0, circuit1, strict_gphase=False, atol=1e-6)
# allow for non-sequential qubit indexing and reversed qubit order
circuits_allclose(circuit2, circuit3, index_contig=True, allow_rev_qubits=True)
```
# Overview
Source: https://docs.qbraid.com/v2/sdk/user-guide/overview
*A platform-agnostic quantum runtime framework.*
The qBraid-SDK is a platform-agnostic quantum runtime framework designed for both quantum software and hardware providers.
This Python-based tool streamlines the full lifecycle management of quantum jobs—from defining program specifications to job
submission, and through to the post-processing and visualization of results.
Distinguishing itself through a streamlined and
highly-configurable approach to cross-platform integration, the qBraid-SDK *does not assume a fixed target software framework*.
Instead, it allows providers to dynamically register any desired run input program type as the target, depending on their specific needs.
These program types are interconnected via a graph-based transpiler, where each program type is represented as a node and supported
conversions as edges. The breadth, depth, and connectivity of this `ConversionGraph` can be customized by the provider.
The framework also facilitates the insertion of additional program validations, circuit transformations, and transpiler/compiler steps
into its modular pipeline through a comprehensive `TargetProfile`. This profile encapsulates both device properties (such as number of
qubits, maximum shots, native gate set) and the software requirements (`ProgramSpec`) needed to submit a job, vastly reducing the overhead
and redundancy typically associated with cross-platform integrations in quantum computing.
## Key Features
### 1. Quantum Program Integration
Offers native support for 10+ major quantum programming libraries including 20+ inter-library conversions with the ability to
dynamically register new program types and conversions on the fly. This enables flexible program submissions to cater to the unique capabilities and constraints of your preferred framework, facilitated by a unique conversion map that automatically adapts quantum programs during runtime according to the given specifications.
### 2. Modular Design
* `qbraid.programs`: Extracts and manages metadata from supported quantum program types, with the flexibility to introduce new types.
* `qbraid.transpiler`: Bridges different quantum programming IRs through native and customizable circuit conversions.
* `qbraid.passes`: Ensures quantum programs conform to hardware specifications through essential runtime transformations.
* `qbraid.runtime`: Defines essential abstractions for providers, devices, jobs, and results, integrated through a coherent runtime profile.
* `qbraid.visualization`: Provides tools for visualizing quantum circuits and experimental data, enhancing data interpretation.
### 3. Extensibility and Customization
The framework encourages community contributions and extensions, supporting an evolving ecosystem of program types and conversions, adaptable to specific provider needs. By providing a comprehensive runtime solution, the qBraid-SDK offers significant advantages to *both hardware and software providers*:
* **Reduces Overhead**: Minimizes the effort required to develop client-side applications for securely submitting and managing quantum experiments remotely.
* **Enhances Integration**: Facilitates seamless integration and interoperability of quantum software tools across all layers of the stack.
* **Broad Compatibility**: Supports a diverse range of API complexities, catering to both established players like IBM and AWS as well as emerging providers.
## Installation and Setup
For the best experience, install the qBraid SDK on [lab.qbraid.com](https://lab.qbraid.com/). Login (or create an account) on
[account.qbraid.com](https://account.qbraid.com/) and then follow the steps to [install an environment](/v2/lab/user-guide/environments/#install-environment).
Using the SDK on qBraid Lab means direct, pre-configured access to QPUs from IonQ, Oxford Quantum Circuits, QuEra, Rigetti, IQM, Pasqal, and Quantinuum
as well as on-demand simulators from AWS, IonQ, QuEra, qBraid, Quantinuum, Pasqal, NEC, and Equal-1 (access varies based on account, see [pricing](/v2/home/pricing)).
See [qBraid Quantum Jobs](/v2/lab/user-guide/quantum-jobs) for more.
### Local Installation
The qBraid-SDK, and all of its dependencies, can be installed using [pip](https://pypi.org/project/qbraid/):
```bash theme={"dark"}
pip install qbraid
```
You can also install from source by cloning the [GitHub repository](https://github.com/qBraid/qBraid) and running a
pip install command in the root directory:
```bash theme={"dark"}
git clone https://github.com/qBraid/qBraid.git
cd qBraid
pip install .
```
*Note*: The qBraid-SDK requires Python 3.10 or greater.
To use qBraid runtime locally, you must also install the necessary extras and configure your
account credentials according to the device(s) that you are targeting. Follow the linked,
provider-specific, instructions for the [QbraidProvider](/v2/sdk/user-guide/providers/native),
[BraketProvider](/v2/sdk/user-guide/providers/aws), [QiskitRuntimeProvider](/v2/sdk/user-guide/providers/ibm),
[IonQProvider](/v2/sdk/user-guide/providers/ionq), [OQCProvider](/v2/sdk/user-guide/providers/oqc),
[AzureQuantumProvider](/v2/sdk/user-guide/providers/azure), [QuantinuumProvider](/v2/sdk/user-guide/providers/quantinuum),
[RigettiProvider](/v2/sdk/user-guide/providers/rigetti), and [OriginProvider](/v2/sdk/user-guide/providers/origin) as applicable.
## Quickstart
### Transpiler
Construct a quantum program of any supported program type:
Below, `QPROGRAM_REGISTRY` maps shorthand identifiers for supported quantum programs, each corresponding to a type in the typed
`QPROGRAM` Union. For example, 'qiskit' maps to `qiskit.QuantumCircuit` in `QPROGRAM`. Notably, 'qasm2' and 'qasm3' both represent
raw OpenQASM strings. This arrangement simplifies targeting and transpiling between different quantum programming frameworks.
```python theme={"dark"}
>>> from qbraid import QPROGRAM_REGISTRY
>>> QPROGRAM_REGISTRY
{'cirq': cirq.circuits.circuit.Circuit,
'qiskit': qiskit.circuit.quantumcircuit.QuantumCircuit,
'pennylane': pennylane.tape.tape.QuantumTape,
'pyquil': pyquil.quil.Program,
'pytket': pytket._tket.circuit.Circuit,
'braket': braket.circuits.circuit.Circuit,
'braket_ahs': braket.ahs.analog_hamiltonian_simulation.AnalogHamiltonianSimulation,
'openqasm3': openqasm3.ast.Program,
'pyqir': pyqir.Module,
'cpp_pyqubo': cpp_pyqubo.Model,
'qasm2': str,
'qasm3': str,
'qasm2_kirin': str,
'ionq': qbraid.programs.typer.IonQDict,
'qubo': qbraid.programs.typer.QuboCoefficientsDict,
'bloqade': bloqade.analog.builder.assign.BatchAssign,
'cudaq': cudaq.kernel.kernel_builder.PyKernel,
'qibo': qibo.models.circuit.Circuit,
'stim': stim._stim_sse2.Circuit,
'pulser': pulser.sequence.sequence.Sequence,
'pyqpanda3': pyqpanda3.core.QProg,
'autoqasm': autoqasm.program.program.Program,
'qrisp': qrisp.circuit.quantum_circuit.QuantumCircuit,
'qat': qat.core.wrappers.circuit.Circuit}
```
Pass any registered quantum program along with a target package from
`QPROGRAM_REGISTRY` to "transpile" your circuit to a new program type:
```python Input theme={"dark"}
from qbraid import random_circuit, transpile
from qbraid.interface import circuits_allclose
qiskit_ckt = random_circuit("qiskit")
cirq_ckt = transpile(qiskit_ckt, "cirq")
allclose = circuits_allclose(qiskit_ckt, cirq_ckt)
print(f"{type(qiskit_ckt)}\n{qiskit_ckt}\n")
print(f"{type(cirq_ckt)}\n{cirq_ckt}\n")
print(f"Circuits All Close: {allclose}")
```
```text Output theme={"dark"}
┌────────────┐
q_0: ──■──┤ Rx(3.0353) ├
┌─┴─┐└───┬────┬───┘
q_1: ┤ H ├────┤ √X ├────
└───┘ └────┘
0: ───@───Rx(0.966π)───
│
1: ───H───X^0.5────────
Circuits All Close: True
```
### Conversions
Behind the scenes, the qBraid-SDK uses [rustworkx](https://www.rustworkx.org/) to create a directional graph that maps all
possible conversions between supported program types:
```python theme={"dark"}
from qbraid import ConversionGraph, ExperimentType
# Loads native conversions from QPROGRAM_REGISTRY
graph = ConversionGraph()
# Plot GATE_MODEL nodes and "Conversion" edges
graph.plot(experiment_type=ExperimentType.GATE_MODEL, legend=True)
```
You can use the native conversions supported by qBraid, or define your own custom nodes and/or edges. For example:
```python theme={"dark"}
from unittest.mock import Mock
from qbraid import register_program_type, Conversion
# replace with any program type
register_program_type(Mock, alias="mock")
# replace with your custom conversion function
example_qasm3_to_mock_func = lambda x: x
conversion = Conversion("qasm3", "mock", example_qasm3_to_mock_func)
graph.add_conversion(conversion)
# filter ConversionGraph to include only GATE_MODEL nodes
graph = graph.subplot(ExperimentType.GATE_MODEL)
# using a seed is helpful to ensure reproducibility
graph.plot(seed=20, k=3, legend=True)
```
### QbraidProvider
Run experiments using on-demand simulators provided by qBraid using the `qbraid.runtime.QbraidProvider`. You can get a
Python list of device objects using:
```python Input theme={"dark"}
from qbraid import QbraidProvider
provider = QbraidProvider()
devices = provider.get_devices()
print(devices)
```
```python Output theme={"dark"}
[,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
]
```
Or, instantiate a known device by ID via the `QbraidProvider.get_device()` method,
and submit quantum jobs from any supported program type:
```python theme={"dark"}
device = provider.get_device("qbraid:qbraid:sim:qir-sv")
jobs = device.run([qiskit_ckt, braket_ckt, cirq_ckt, qasm3_str], shots=1000)
results = [job.result() for job in jobs]
print(results[0].data.get_counts())
# {'00': 483, '01': 14, '10': 486, '11': 17}
```
### Async results
All `QuantumJob` subclasses support `async_result()` for non-blocking result retrieval using asyncio.
This is useful in async applications where blocking the event loop is undesirable:
```python theme={"dark"}
result = await job.async_result(timeout=120, poll_interval=5)
print(result.data.get_counts())
```
## Supported Frontends
Featured circuit-building frameworks supported by the qBraid-SDK (non-exhaustive list).
## Citation
If you use qBraid-SDK in your research, we kindly request that you cite it appropriately.
The BibTeX entry below is aligned with the latest stable release. For the most up-to-date
citation details, please refer to [CITATION.cff](https://github.com/qBraid/qBraid/blob/main/CITATION.cff) or
[Zenodo](https://doi.org/10.5281/zenodo.12627596).
```tex theme={"dark"}
@software{Hill_qBraid-SDK_Platform-agnostic_quantum_2026,
author = {Hill, Ryan James and Gupta, Harshit and Young, Ricky and Setia, Kanav},
doi = {10.5281/zenodo.12627596},
license = {Apache-2.0},
month = jul,
title = {{qBraid-SDK: Platform-agnostic quantum runtime framework.}},
url = {https://github.com/qBraid/qBraid},
version = {0.12.2},
year = {2026}
}
```
# Programs
Source: https://docs.qbraid.com/v2/sdk/user-guide/programs
Extract and manage metadata from supported quantum program types, with the flexibility to introduce new types.
In this module, you will learn how to use the qBraid-SDK to interface with quantum circuits across various frontends.
We will demonstrate how to load quantum programs, register new quantum program types, and highlight a few other circuit-level
convenience features.
API Reference:
[qbraid.programs](https://qbraid.github.io/qBraid/api/qbraid.programs.html)
## Quantum Program Registry
The `QPROGRAM_REGISTRY` contains dictionary mappings of shorthand identifiers for "registered" quantum program types.
By default (i.e. without any optional dependencies installed) this simply includes three variations of OpenQASM programs:
```python theme={"dark"}
>>> from qbraid import QPROGRAM_REGISTRY
>>> QPROGRAM_REGISTRY
{'openqasm3': openqasm3.ast.Program,
'qasm2': str,
'qasm3': str}
```
In this example, 'openqasm3' serves as a type alias for an `openqasm3.ast.Program` object, while 'qasm2' and 'qasm3' are aliases
for raw OpenQASM 2 and OpenQASM 3 strings, respectively.
For each of its 10 natively supported program type aliases, the qBraid-SDK will automatically detect whether the corresponding library is installed,
and add that program type along with its type alias to the `QPROGRAM_REGISTRY`. For example, after installing the following optional dependencies,
```bash theme={"dark"}
pip install cirq-core qiskit amazon-braket-sdk pennylane pyquil pytket pyqir
```
the `QPROGRAM_REGISTRY` is automatically updated:
```python theme={"dark"}
>>> from qbraid import QPROGRAM_REGISTRY
>>> QPROGRAM_REGISTRY
{'cirq': cirq.circuits.circuit.Circuit,
'qiskit': qiskit.circuit.quantumcircuit.QuantumCircuit,
'pennylane': pennylane.tape.tape.QuantumTape,
'pyquil': pyquil.quil.Program,
'pytket': pytket._tket.circuit.Circuit,
'braket': braket.circuits.circuit.Circuit,
'openqasm3': openqasm3.ast.Program,
'pyqir': Module,
'qasm2': str,
'qasm3': str}
```
This arrangement simplifies the process of targeting and transpiling between various quantum programming frameworks,
which will be discussed in detail in the transpiler, interface, and runtime tutorials.
### Register New Program Types
You can register new quantum program types using either custom or default type aliases, and overwrite
existing type aliases when necessary. For example:
```python theme={"dark"}
import cirq
import stim
from qbraid import register_program_type
# register new program type with default module alias
register_program_type(stim.Circuit)
# register native program type with custom alias
register_program_type(cirq.Circuit, alias="myalias")
# overwrite existing type alias with new program type
register_program_type(dict, alias="qasm2", overwrite=True)
```
## Load Program
Any quantum program of a natively supported type can be encapsulated within a `qbraid.programs.QbraidProgram`
object. This encapsulation provides a unified framework for circuit manipulation—such as removing idle qubits
and reversing qubit order—and for extracting valuable metadata, including the number of qubits, circuit depth,
and the unitary representation.
As an example, we'll start with a simple qiskit circuit that creates the bell state,
load the quantum circuit object into the qBraid representation, verify the
circuit depth number of qubits, calculate the circuit's unitary representation,
and draw the circuit.
```python Input theme={"dark"}
from qbraid import load_program
from qbraid.visualization import circuit_drawer
qprogram = load_program(qiskit_circuit)
assert qprogram.num_qubits == 2
assert qprogram.depth == 2
assert qprogram.unitary().shape == (4, 4)
circuit_drawer(qprogram.program)
```
```text Output theme={"dark"}
┌───┐
q_0: ┤ H ├──■──
└───┘┌─┴─┐
q_1: ─────┤ X ├
└───┘
```
Now, let's try reversing the qubit order, and drawing the circuit again:
```python Input theme={"dark"}
qprogram.reverse_qubit_order()
circuit_drawer(qprogram.program)
```
```text Ouput theme={"dark"}
┌───┐
q_0: ─────┤ X ├
┌───┐└─┬─┘
q_1: ┤ H ├──■──
└───┘
```
Any quantum program type listed in the `qbraid.NATIVE_REGISTRY` can be loaded and utilized in this manner.
Each `QbraidProgram` object has `num_qubits` and `depth` attributes and `unitary()` method, regardless of the input circuit type.
For example, we could load an Amazon Braket circuit, and repeat similar steps:
```python Input theme={"dark"}
from qbraid import load_program
from qbraid.visualization import circuit_drawer
qprogram = load_program(braket_circuit)
assert qprogram.unitary().shape == ((1 << qprogram.num_qubits),) * 2
circuit_drawer(qprogram.program)
```
```text Output theme={"dark"}
T : │ 0 │ 1 │ 2 │
┌───┐
q0 : ─┤ H ├───●─────────
└───┘ │
┌─┴─┐
q2 : ───────┤ X ├───●───
└───┘ │
┌─┴─┐
q4 : ─────────────┤ X ├─
└───┘
T : │ 0 │ 1 │ 2 │
```
Some wrapped program types support additional methods that can help resolve compatibility issues, such as differences in
qubit indexing rules across frontends. For instance, the `remove_idle_qubits()` method allows you to remap qubit indices
without having to reconstruct the circuit:
```python Input theme={"dark"}
qprogram.remove_idle_qubits()
circuit_drawer(qprogram.program)
```
```text Output theme={"dark"}
T : │ 0 │ 1 │ 2 │
┌───┐
q0 : ─┤ H ├───●─────────
└───┘ │
┌─┴─┐
q1 : ───────┤ X ├───●───
└───┘ │
┌─┴─┐
q2 : ─────────────┤ X ├─
└───┘
T : │ 0 │ 1 │ 2 │
```
Using the `remove_idle_qubits` method, we mapped the qubit indices of the
Amazon Braket circuit from `[0, 2, 4]` to the contiguous `[0, 1, 2]` convention.
# AQTProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/aqt
Runtime integration for direct access to AQT trapped-ion quantum processors via the arnica cloud.
API Reference:
[qbraid.runtime.aqt](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.aqt.html)
## Overview
The `qbraid.runtime.AQTProvider` provides direct access to
[Alpine Quantum Technologies (AQT)](https://www.aqt.eu/) trapped-ion quantum systems through the
[arnica cloud](https://www.aqt.eu/products/arnica/). You can write quantum circuits in
[Qiskit](https://www.ibm.com/quantum/qiskit) (or any other qBraid-supported framework); the provider
transpiles them to AQT's native gate set and executes them on AQT hardware such as the **IBEX** QPU,
all from within the [qBraid Runtime framework](/v2/sdk/user-guide/runtime/components).
AQT devices are also available through the **qBraid platform** — accessed with
`QbraidProvider` and billed in qBraid credits — as well as through Amazon
Braket and Open Quantum. Each is a separate access point with its own device
ids.
## Getting started
Before you begin, make sure you have:
1. An **AQT arnica** account with machine-to-machine (OIDC client-credentials) access.
2. Your arnica **client ID** and **client secret**.
3. Python >= 3.10
### Set up the qBraid-SDK
Install qBraid with the `aqt` extra from [PyPI](https://pypi.org/project/qbraid/) using pip:
```bash theme={"dark"}
pip install 'qbraid[aqt]'
```
This installs the required dependencies: `aqt-connector` and `qiskit`.
*Note*: The qBraid-SDK requires Python 3.10 or greater. You can check your
Python version by running `python --version` from the command line.
## Authentication
The `AQTProvider` authenticates to the arnica cloud non-interactively using OIDC **client
credentials** (machine-to-machine) — the interactive device/login flow is never triggered. Provide
your credentials via environment variables:
```bash theme={"dark"}
export AQT_CLIENT_ID="your-client-id"
export AQT_CLIENT_SECRET="your-client-secret"
```
Then initialize the provider:
```python theme={"dark"}
from qbraid.runtime.aqt import AQTProvider
provider = AQTProvider()
```
You can also pass credentials — or a pre-obtained access token — directly instead of using
environment variables:
```python theme={"dark"}
# client-credentials
provider = AQTProvider(client_id="your-client-id", client_secret="your-client-secret")
# or a pre-obtained bearer token
provider = AQTProvider(access_token="your-access-token")
```
## List available devices
Use the `AQTProvider` to list the devices to which you have access:
```python theme={"dark"}
from qbraid.runtime.aqt import AQTProvider
provider = AQTProvider()
devices = provider.get_devices()
print(devices)
```
AQT devices are addressed by a `"/"` identifier. Get a specific device by its id:
```python theme={"dark"}
device = provider.get_device("/")
print(device.status())
#
```
Use `get_devices()` to discover the `"/"` ids you have access to — for
example, the **IBEX** QPU is exposed under its arnica workspace and resource.
## Submitting jobs
The `AQTProvider` accepts circuits written in any qBraid-supported framework. `device.run()`
transpiles the circuit to AQT's native basis (`RZ`, `R`, `RXX`) via the `qiskit -> aqt_connector`
conversion, then submits it.
AQT enforces per-job limits: up to **2000 shots** per job and up to **2000
operations** (gates) per circuit. Jobs that exceed either limit are rejected.
The **IBEX** QPU provides **12 qubits**; a circuit wider than the device is
rejected before submission.
### Create a circuit
```python theme={"dark"}
from qiskit import QuantumCircuit
# Bell state circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
```
### Run a job
Use `device.run()` to transpile and submit a circuit:
```python theme={"dark"}
from qiskit import QuantumCircuit
from qbraid.runtime.aqt import AQTProvider
provider = AQTProvider()
device = provider.get_device("/")
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
job = device.run(circuit, shots=1000)
print(f"Job ID: {job.id}")
```
### Batch submission
Submit multiple circuits in a single call. All circuits are bundled into one arnica job:
```python theme={"dark"}
from qiskit import QuantumCircuit
from qbraid.runtime.aqt import AQTProvider
provider = AQTProvider()
device = provider.get_device("/")
qc1 = QuantumCircuit(1)
qc1.h(0)
qc1.measure_all()
qc2 = QuantumCircuit(2)
qc2.h(0)
qc2.cx(0, 1)
qc2.measure_all()
job = device.run([qc1, qc2], shots=500)
print(f"Job ID: {job.id}")
```
When submitting a list of circuits, all are executed as a single batch job on
arnica. The returned measurement counts will be a list of dictionaries, one
per circuit.
## Retrieving results
```python theme={"dark"}
result = job.result()
# Measurement counts (little-endian: qubit 0 is the rightmost bit)
print(result.data.get_counts())
# {'00': 512, '11': 488}
# Job metadata
print(f"Device: {result.device_id}")
print(f"Job ID: {result.job_id}")
print(f"Success: {result.success}")
```
### Check job status
```python theme={"dark"}
from qbraid.runtime.enums import JobStatus
status = job.status()
print(status)
#
```
### Cancel a job
```python theme={"dark"}
job.cancel()
```
Cancellation targets queued or ongoing jobs. Jobs already in a terminal state
(`COMPLETED`, `FAILED`, `CANCELLED`) cannot be cancelled.
### Execution time
Retrieve the wall-clock run time (in seconds) for a completed job:
```python theme={"dark"}
print(f"Execution time: {job.execution_time_s()} seconds")
```
`execution_time_s()` is derived from the arnica `timing_data` as the `ongoing`
→ `finished` span (the time the job spent running, excluding queue wait). It
returns `None` until the job has completed, and raises `AQTJobError` if a
completed job's timing data is incomplete.
## Configuration options
The `device.run()` method accepts the following keyword arguments:
| Parameter | Type | Default | Description |
| --------- | ----- | ---------- | --------------------------------------------------- |
| `shots` | `int` | `100` | Number of measurement shots per circuit (max 2000). |
| `name` | `str` | `"qbraid"` | Optional human-readable job label. |
Credentials are configured via environment variables:
| Variable | Description |
| ------------------- | ---------------------------------------------------------- |
| `AQT_CLIENT_ID` | OIDC client ID for the arnica client-credentials flow. |
| `AQT_CLIENT_SECRET` | OIDC client secret for the arnica client-credentials flow. |
## Full example
A complete end-to-end workflow submitting a GHZ state to an AQT device:
```python theme={"dark"}
from qiskit import QuantumCircuit
from qbraid.runtime.aqt import AQTProvider
# 1. Initialize provider (reads AQT_CLIENT_ID / AQT_CLIENT_SECRET from the environment)
provider = AQTProvider()
# 2. Get a device by its "/" id
device = provider.get_device("/")
print(f"Device status: {device.status()}")
# 3. Define a GHZ state circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure_all()
# 4. Submit the job
job = device.run(circuit, shots=1000)
print(f"Submitted job: {job.id}")
# 5. Retrieve results
result = job.result()
print(f"Counts: {result.data.get_counts()}")
# Expected output (approximate): {'000': ~500, '111': ~500}
```
## Related links
* [Alpine Quantum Technologies (AQT)](https://www.aqt.eu/)
* [AQT arnica Cloud](https://www.aqt.eu/products/arnica/)
* [AQT arnica API Reference](https://arnica.aqt.eu/api/v1/docs)
* [AQT Quantum SDK Connectors](https://www.aqt.eu/quantum-sdk-connectors/)
# BraketProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/aws
Runtime integration for streamlined access to Amazon Braket supported devices.
API Reference:
[qbraid.runtime.aws](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.aws.html)
## Installation & Setup
To interface with Amazon Braket [supported devices](https://docs.aws.amazon.com/braket/latest/developerguide/braket-devices.html),
install the `braket` extra:
```bash theme={"dark"}
pip install 'qbraid[braket]'
```
Then, follow [instructions](https://github.com/aws/amazon-braket-sdk-python#boto3-and-setting-up-aws-credentials)
to configure your AWS credentials.
## Basic Usage
Submit a Quantum Task to an AWS device using the `BraketProvider`:
```python theme={"dark"}
from qbraid.runtime import BraketProvider
provider = BraketProvider()
provider.get_devices()
# [,
# ,
# ,
# ,
# ,
# ,
# ,
# ,
# ,
# ]
device = provider.get_device("arn:aws:braket:::device/quantum-simulator/amazon/sv1")
type(device)
# qbraid.runtime.aws.device.BraketDevice
device.metadata()
# {'device_id': 'arn:aws:braket:::device/quantum-simulator/amazon/sv1',
# 'device_type': 'SIMULATOR',
# 'num_qubits': 34,
# 'provider_name': 'Amazon Braket',
# 'status': 'ONLINE',
# 'queue_depth': 0}
```
Now that we've instantiated our device, in this case the AWS SV1 simulator, we can construct a quantum circuit and submit a task using the `.run` method:
```python theme={"dark"}
from braket.circuits import Circuit
circuit = Circuit().h(0).cnot(0, 1)
job = device.run(circuit, shots=10)
```
We now have `job` which is of type `BraketQuantumTask`, which inherits from `QuantumJob`. To see the results,
we can do the following:
```python theme={"dark"}
res = job.result()
res.data.get_counts()
# {'00': 6, '11': 4}
res.data.measurements
# array([[0, 0],
# [0, 0],
# [1, 1],
# [1, 1],
# [0, 0],
# [0, 0],
# [0, 0],
# [1, 1],
# [0, 0],
# [0, 0]])
```
See how to visualize these results in the [Visualization](/v2/sdk/user-guide/visualization#plot-experimental-results) section.
## Runtime Options
The `BraketDevice.run()` method accepts additional keyword arguments that are passed through
to the underlying Amazon Braket SDK's `AwsDevice.run()`. These options let you control
device-specific behavior such as qubit rewiring, error mitigation, experimental capabilities, and more.
### Disable Qubit Rewiring
By default, Braket may remap the logical qubits in your circuit to physical qubits on the device.
To force the use of the exact qubits specified in your circuit, disable qubit rewiring:
```python theme={"dark"}
job = device.run(circuit, shots=1000, disable_qubit_rewiring=True)
```
This is required when using [verbatim compilation](https://docs.aws.amazon.com/braket/latest/developerguide/braket-openqasm-verbatim-compilation.html)
or pulse-level gate definitions.
### Error Mitigation (IonQ)
IonQ devices on Braket support error mitigation through debiasing, which creates
variations of your circuit to reduce the effects of hardware noise:
```python theme={"dark"}
from braket.error_mitigation import Debias
device = provider.get_device("arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1")
job = device.run(
circuit,
shots=2500, # Minimum 2500 shots required for debiasing
device_parameters={"errorMitigation": Debias()},
)
```
### Experimental Capabilities
Some devices offer experimental features that must be explicitly enabled. For example,
QuEra Aquila supports tight atom geometries and local detuning, and IQM devices support
dynamic circuits with mid-circuit measurement:
```python theme={"dark"}
device = provider.get_device("arn:aws:braket:us-east-1::device/qpu/quera/Aquila")
job = device.run(
program,
shots=1000,
experimental_capabilities="ALL",
)
```
See [Experimental Capabilities](https://docs.aws.amazon.com/braket/latest/developerguide/braket-experimental-capabilities.html) for a full list of available features per device.
### Parametric Circuits
For OpenQASM programs with input parameters, pass values at runtime:
```python theme={"dark"}
job = device.run(
openqasm_program,
shots=1000,
inputs={"theta": 1.57, "phi": 0.785},
)
```
### Reservation ARN
If you have a [Braket Direct](https://aws.amazon.com/braket/direct/) reservation for
exclusive device access, provide the reservation ARN:
```python theme={"dark"}
job = device.run(
circuit,
shots=1000,
reservation_arn="arn:aws:braket:us-east-1:123456789012:reservation/abc123",
)
```
### Tags
Attach metadata tags to your quantum tasks for tracking and organization:
```python theme={"dark"}
job = device.run(
circuit,
shots=1000,
tags={"experiment": "bell-state", "team": "quantum-research"},
)
```
### Options Reference
| Parameter | Type | Description |
| --------------------------- | ------------------ | ---------------------------------------------- |
| `disable_qubit_rewiring` | `bool` | Force use of exact qubits without remapping |
| `device_parameters` | `dict` | Device-specific config (e.g. error mitigation) |
| `experimental_capabilities` | `str` | Set to `"ALL"` to enable experimental features |
| `inputs` | `dict[str, float]` | Parameter values for parametric circuits |
| `reservation_arn` | `str` | Braket Direct reservation ARN |
| `tags` | `dict[str, str]` | Metadata tags for the quantum task |
### Via qBraid Runtime API
When submitting jobs through the [QbraidProvider](/v2/sdk/user-guide/providers/native), these same
options can be passed via the `runtimeOptions` field. The runtime API forwards them as keyword
arguments to the Braket `device.run()` call:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("aws:quera:qpu:aquila")
job = device.run(
program,
shots=1000,
runtime_options={"experimental_capabilities": "ALL"},
)
```
```python theme={"dark"}
device = provider.get_device("aws:ionq:qpu:aria-1")
job = device.run(
circuit,
shots=2500,
runtime_options={"device_parameters": {"errorMitigation": "Debias"}},
)
```
# AzureQuantumProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/azure
Runtime integration for streamlined access to Azure Quantum supported devices.
API Reference:
[qbraid.runtime.azure](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.azure.html)
## Installation & Setup
To interface with Azure Quantum supported devices, install the `azure` extra:
```bash theme={"dark"}
pip install 'qbraid[azure]'
```
Then, follow the [Azure Quantum setup instructions](https://learn.microsoft.com/en-us/azure/quantum/how-to-create-workspace) to create a workspace and get your credentials.
## Authentication Methods
The `AzureQuantumProvider` integrates with Azure Quantum via the `azure-quantum` package. It connects to an [Azure Quantum Workspace](https://learn.microsoft.com/en-us/python/azure-quantum/azure.quantum.workspace), which manages your quantum resources.
### Using a Connection String
You can authenticate using an **Azure connection string**, which provides a direct way to access your workspace. First, retrieve your connection string by following [these instructions](https://learn.microsoft.com/en-us/azure/quantum/how-to-connect-workspace#copy-the-connection-string). Then, use it to initialize a `Workspace` object and pass it to `AzureQuantumProvider`:
```python theme={"dark"}
# Authenticate using a connection string
from azure.quantum import Workspace
from qbraid.runtime import AzureQuantumProvider
connection_string = "[Your connection string here]"
workspace = Workspace.from_connection_string(connection_string)
provider = AzureQuantumProvider(workspace=workspace)
```
### Using Environment Variables
To avoid hardcoding credentials in your code, you can store the connection string as an environment variable instead:
```bash theme={"dark"}
export AZURE_QUANTUM_CONNECTION_STRING="your-connection-string"
```
Then, initialize `AzureQuantumProvider` without passing explicit credentials:
```python theme={"dark"}
from qbraid.runtime import AzureQuantumProvider
provider = AzureQuantumProvider() # Uses the environment variable
```
### Related Content
* [Access Keys - Azure Quantum | Microsoft Learn](https://learn.microsoft.com/en-us/azure/quantum/security-manage-access-keys?tabs=tabid-portal#connect-to-your-azure-quantum-workspace-with-a-connection-string)
* [Connect to your Azure Quantum workspace with the azure-quantum Python package](https://learn.microsoft.com/en-us/azure/quantum/how-to-connect-workspace?tabs=tabid-python1%2Ctabid-python)
* [Authenticate Python apps to Azure services by using the Azure SDK for Python](https://learn.microsoft.com/en-us/azure/developer/python/azure-sdk-authenticate)
## Basic Usage
Submit a Quantum Task to an Azure Quantum device using the `AzureQuantumProvider`:
```python theme={"dark"}
from qbraid.runtime import AzureQuantumProvider
provider = AzureQuantumProvider() # Uses environment variables
# List available devices
provider.get_devices()
# [,
# ,
# ,
# ,
# ]
# Get a specific device
device = provider.get_device("ionq.simulator")
type(device)
# qbraid.runtime.azure.device.AzureDevice
device.metadata()
# {'device_id': 'ionq.simulator',
# 'device_type': 'SIMULATOR',
# 'num_qubits': 29,
# 'provider_name': 'IonQ',
# 'status': 'ONLINE'}
```
Now that we've instantiated our device, in this case the IonQ simulator on Azure Quantum, we can construct a quantum circuit and submit a task using the `.run` method:
```python theme={"dark"}
from qiskit import QuantumCircuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
job = device.run(circuit, shots=10)
```
We now have `job` which is of type `AzureQuantumTask`, which inherits from `QuantumJob`. To see the results:
```python theme={"dark"}
res = job.result()
res.data.get_counts()
# {'00': 5, '11': 5}
res.data.measurements
# array([[0, 0],
# [1, 1],
# [0, 0],
# [1, 1],
# [0, 0],
# [1, 1],
# [0, 0],
# [1, 1],
# [0, 0],
# [1, 1]])
```
See how to visualize these results in the [Visualization](/v2/sdk/user-guide/visualization#plot-experimental-results) section.
## Supported Providers
Azure Quantum provides access to quantum hardware and simulators from several providers:
* IonQ
* Quantinuum
* Rigetti
* Pasqal
Each provider may have different requirements and capabilities. Refer to the [Azure Quantum documentation](https://learn.microsoft.com/en-us/azure/quantum/) for more details about specific providers.
## Runtime Options
The `AzureQuantumDevice.run()` and `.submit()` methods accept an `input_params` keyword argument
that is passed through to the Azure Quantum backend. Each hardware provider supports different
options.
### Quantinuum
Quantinuum emulators and hardware support several input parameters for controlling
simulation type, noise models, and compiler optimization.
#### Simulator type
Quantinuum emulators support both state-vector (default) and stabilizer simulation:
```python theme={"dark"}
device = provider.get_device("quantinuum.sim.h2-1e")
# Use the stabilizer simulator (Clifford circuits only)
job = device.run(circuit, shots=100, input_params={"simulator": "stabilizer"})
```
#### Noise model
Disable the emulator's noise model for ideal simulation:
```python theme={"dark"}
job = device.run(circuit, shots=100, input_params={"error-model": False})
```
Customize individual noise parameters:
```python theme={"dark"}
job = device.run(
circuit,
shots=100,
input_params={
"error-params": {
"p1": 4e-5,
"p2": 3e-3,
"p_meas": [3e-3, 3e-3],
"p_init": 4e-5,
"p_crosstalk_meas": 1e-5,
"p_crosstalk_init": 3e-5,
}
},
)
```
#### Compiler options
Control TKET optimization level or disable optimization entirely:
```python theme={"dark"}
# Set optimization level (0, 1, or 2; default is 2)
job = device.run(circuit, shots=100, input_params={"tket-opt-level": 1})
# Disable optimization
job = device.run(circuit, shots=100, input_params={"no-opt": True})
```
See [Quantinuum provider](https://learn.microsoft.com/en-us/azure/quantum/provider-quantinuum) for full details.
### IonQ
IonQ devices on Azure support error mitigation and noise model simulation.
#### Error mitigation
Debiasing is enabled by default on Aria and Forte systems. To explicitly control it:
```python theme={"dark"}
device = provider.get_device("ionq.qpu.aria-1")
# Disable debiasing
job = device.run(circuit, shots=1000, input_params={"error-mitigation": {"debias": False}})
```
#### Noise model simulation
Run on the IonQ simulator with a hardware noise profile:
```python theme={"dark"}
device = provider.get_device("ionq.simulator")
job = device.run(
circuit,
shots=1000,
input_params={
"noise": {
"model": "aria-1",
"seed": 42,
}
},
)
```
Set `"model": "ideal"` for noiseless simulation (up to 29 qubits).
See [IonQ provider](https://learn.microsoft.com/en-us/azure/quantum/provider-ionq) for full details.
### Rigetti
Rigetti devices on Azure support compiler control and parameter substitutions.
#### Skip quilc compilation
When using native Quil or Quil-T programs, you can bypass the quilc compiler:
```python theme={"dark"}
device = provider.get_device("rigetti.qpu.ankaa-3")
job = device.submit(quil_program, shots=100, input_params={"skipQuilc": True})
```
#### Parameter substitutions
For parametrized Quil programs, provide values for declared variables:
```python theme={"dark"}
quil_program = """
DECLARE ro BIT[2]
DECLARE theta REAL[2]
RX(theta[0]) 0
RX(theta[1]) 1
MEASURE 0 ro[0]
MEASURE 1 ro[1]
"""
job = device.submit(
quil_program,
shots=100,
input_params={
"substitutions": {
"theta": [[0.0, 3.14], [1.57, 3.14]]
}
},
)
```
See [Rigetti provider](https://learn.microsoft.com/en-us/azure/quantum/provider-rigetti) for full details.
### Options Reference
| Provider | Parameter | Type | Description |
| ---------- | ------------------ | ------ | -------------------------------------------- |
| Quantinuum | `simulator` | `str` | `"stabilizer"` or `"state-vector"` (default) |
| Quantinuum | `error-model` | `bool` | Enable/disable noise model (default `True`) |
| Quantinuum | `error-params` | `dict` | Custom noise parameters |
| Quantinuum | `tket-opt-level` | `int` | TKET optimization level (0-2) |
| Quantinuum | `no-opt` | `bool` | Disable compiler optimization |
| IonQ | `error-mitigation` | `dict` | `{"debias": True/False}` |
| IonQ | `noise` | `dict` | `{"model": "aria-1", "seed": int}` |
| Rigetti | `skipQuilc` | `bool` | Skip quilc compiler |
| Rigetti | `substitutions` | `dict` | Parameter values for Quil programs |
### Via qBraid Runtime API
When submitting jobs through the [QbraidProvider](/v2/sdk/user-guide/providers/native), these same
options can be passed via the `runtimeOptions` field. The runtime API forwards them as `input_params`
to the Azure `device.run()` or `device.submit()` call:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("azure:quantinuum:sim:h2-1e")
# Use stabilizer simulator with no noise
job = device.run(
circuit,
shots=100,
runtime_options={"simulator": "stabilizer", "error-model": False},
)
```
```python theme={"dark"}
device = provider.get_device("azure:ionq:sim:simulator")
# Run with hardware noise model
job = device.run(
circuit,
shots=1000,
runtime_options={"noise": {"model": "aria-1", "seed": 42}},
)
```
# QiskitRuntimeProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/ibm
Runtime integration for streamlined access to IBM Quantum hardware.
API Reference:
[qbraid.runtime.ibm](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.ibm.html)
## Installation & Setup
To interface with IBM Quantum backends, install the `qiskit` extra,
```bash theme={"dark"}
pip install 'qbraid[qiskit]'
```
and configure your [IBM credentials](https://github.com/Qiskit/qiskit-ibm-runtime?tab=readme-ov-file#account-setup).
## Basic Usage
The `QiskitRuntimeProvider` allows you to easily submit jobs to IBM Quantum devices. There are several ways to initialize the provider:
1. Direct authentication with IBM Cloud:
```python theme={"dark"}
from qbraid.runtime import QiskitRuntimeProvider
provider = QiskitRuntimeProvider(
channel="ibm_cloud",
token="MY_IBM_CLOUD_API_KEY",
instance="MY_IBM_CLOUD_CRN"
)
```
2. Authentication with IBM Quantum:
```python theme={"dark"}
provider = QiskitRuntimeProvider(
channel="ibm_quantum",
token="MY_IBM_QUANTUM_TOKEN"
)
```
3. Using environment variables:
```shell theme={"dark"}
export QISKIT_IBM_TOKEN="MY_IBM_CLOUD_API_KEY"
export QISKIT_IBM_INSTANCE="MY_IBM_CLOUD_CRN"
export QISKIT_IBM_CHANNEL="ibm_cloud"
```
4. Using credentials stored in `$HOME/.qiskit/qiskit-ibm.json`
When using environment variables or stored credentials, you can initialize the provider without any arguments:
```python theme={"dark"}
provider = QiskitRuntimeProvider()
```
Once initialized, you can list available devices and access their information:
```python theme={"dark"}
# Get list of available devices
provider.get_devices()
# [,
# ,
# ,
# ,
# ,
# ]
# Select a specific device
device = provider.get_device('ibm_brisbane')
# View device metadata
device.metadata()
# {'device_id': 'ibm_brisbane',
# 'device_type': 'QPU',
# 'num_qubits': 127,
# 'instance': 'ibm-q/open/main',
# 'max_shots': 100000,
# 'status': 'ONLINE',
# 'queue_depth': 37}
```
Our chosen device is the IBM Osaka Backend, which we will now submit a Qiskit job to.
```python theme={"dark"}
from qiskit import QuantumCircuit
circuit = QuantumCircuit(2)
circuit.h(0)
job = device.run(circuit, shots=10)
```
So now we have `job` which is of type `QiskitJob`, which inherits from `QuantumJob`. To see the results, we can do the following:
```python theme={"dark"}
res = job.result()
res.data.get_counts()
# {'00': 6, '11': 4}
res.data.measurements
# array([[1, 1],
# [0, 0],
# [0, 0],
# [1, 1],
# [0, 0],
# [1, 1],
# [0, 0],
# [1, 1],
# [0, 0],
# [0, 0]])
```
See how to visualize these results in the [Visualization](/v2/sdk/user-guide/visualization#plot-experimental-results) section.
# IonQProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/ionq
Runtime integration for streamlined access to IonQ simulators and QPUs.
API Reference:
[qbraid.runtime.ionq](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.ionq.html)
## Overview
The `qbraid.runtime.IonQProvider` provides support for IonQ's trapped-ion systems. This means that you can write quantum circuits in
Qiskit, Cirq, Amazon Braket, PennyLane, PyTKET, or any other library compatible with [OpenQASM](https://openqasm.com/) (version 2 or 3),
and run them on IonQ's simulators and trapped-ion quantum computers, all from within the [qBraid Runtime framework](/v2/sdk/user-guide/runtime/components).
## Getting started
Before you begin, make sure you have an [IonQ Quantum Cloud](https://cloud.ionq.com/) account and API key.
For help, see IonQ's guide on [creating and managing API keys](https://docs.ionq.com/guides/managing-api-keys).
### Set up the qBraid-SDK
Install qBraid with the `ionq` extra from [PyPI](https://pypi.org/project/qbraid/) using pip:
```bash theme={"dark"}
pip install 'qbraid[ionq]'
```
*Note*: The qBraid-SDK requires Python 3.10 or greater. You can check your
Python version by running `python --version` from the command line.
We encourage doing this inside an environment management system, such as [virtualenv](https://virtualenv.pypa.io/en/latest/) or
[conda](https://docs.conda.io/en/latest/). Alternatively, you can bypass this step by using a pre-configured
[qBraid Lab environment](/v2/lab/user-guide/environments).
See [qBraid-SDK installation and setup](/v2/sdk/user-guide/overview#installation-and-setup) for more.
### Set up your environment
By default, qBraid will look in your local environment for a variable named `IONQ_API_KEY`, so if you've already followed IonQ's guide on
[setting up and managing your API keys](https://docs.ionq.com/guides/managing-api-keys), qBraid will automatically find it.
Alternatively, you can set a "temporary" environment variable from your command line:
```bash theme={"dark"}
export IONQ_API_KEY="your_api_key_here"
```
While we recommend setting an environment variable so that qBraid can find your API key, you can also pass in your API key explicitly
within your Python code, when creating the IonQ Provider object that authenticates your connection to the IonQ Cloud Platform. This might
be necessary if you've named your environment variable something other than `IONQ_API_KEY`, or if you are working from a Python environment
where accessing environment variables is not straightforward. You can import your key explicitly or load it from a file, and pass it into
the `IonQProvider()` object directly:
```python theme={"dark"}
import os
from qbraid.runtime import IonQProvider
# Load your API key from an environment variable named MY_IONQ_API_KEY
my_api_key = os.getenv("MY_IONQ_API_KEY")
provider = IonQProvider(my_api_key)
```
In the examples below, we show `IonQProvider()` initialized with no arguments and assume that qBraid will automatically find your API key,
but you can always use this approach instead.
## List available devices
Use the `IonQProvider` to list all of the devices to which you have access:
```python theme={"dark"}
from qbraid.runtime import IonQProvider
provider = IonQProvider()
devices = provider.get_devices()
```
Running this script should print the results below—something like this:
```python theme={"dark"}
[,
,
,
,
]
```
If this works correctly then your qBraid-SDK installation is correct, your IonQ API key is valid, and you have access to the IonQ devices!
## Submit a circuit to the simulator
First, let’s try running a simple Bell state circuit on the ideal simulator. Here, we'll use 1000 shots, and give the circuit a name that
will show up in the [IonQ Cloud Console](https://cloud.ionq.com/jobs).
Your input OpenQASM program should not include measurement statements.
Measurement will be applied over all qubits at the end of the circuit,
automatically. Mid-circuit measurements and partial measurements are not
supported.
```python theme={"dark"}
from qbraid.runtime import IonQProvider
provider = IonQProvider()
device = provider.get_device("simulator")
# Define a Bell state circuit in qasm
qasm = """
OPENQASM 3.0;
qubit[2] q;
h q[0];
cx q[0], q[1];
"""
job = device.run(qasm, name="Hello many worlds!", shots=1000)
result = job.result()
print(result.data.get_counts())
```
This returns:
```
{'00': 500, '11': 500}
```
As expected, the ideal simulator creates a quantum state with a 50-50 probability of being measured as “00” or “11”.
To view the calculated probabilities for a circuit run on the simulator, use `result.data.get_probabilities()`.
You can return histogram data in decimal format for either of these `result.data` methods using argument `decimal=True`.
### Submit a circuit with noise
Get a list of all noise models supported by the IonQ simulator:
```python theme={"dark"}
print(device.profile.noise_models)
```
Then, to run a noisy simulation, simply specify a noise model and an optional random seed:
```python theme={"dark"}
job = device.run(qasm, shots=1000, noise={"model" : "aria-1", "seed": 42})
```
You can read more about simulation with noise models [here](https://docs.ionq.com/guides/simulation-with-noise-models).
## Submit a circuit to a QPU
You can view your access to IonQ systems in the “Backends” tab of the IonQ Cloud Console. Before submitting to any QPU,
we recommend testing your code on a simulator (including with noise model) and following the other steps in the
[QPU submission checklist](https://docs.ionq.com/guides/qpu-submission-checklist.mdx) to confirm your access and the QPU availability.
To run a circuit on a QPU, use the same setup as before, just with a different device ID:
```python theme={"dark"}
from qbraid.runtime import IonQProvider
provider = IonQProvider()
device = provider.get_device("qpu.aria-1")
```
Verify that the device is `ONLINE`:
```python theme={"dark"}
print(device.status())
```
View device characterization data including the connectivity, fidelity, timings, and more:
```python theme={"dark"}
print(device.profile.characterization)
```
Next, construct your quantum program, ensuring it aligns with the device’s [supported gateset](#supported-gates).
In this example, we'll define GHZ state circuit using OpenQASM 3:
```python theme={"dark"}
qasm = """
OPENQASM 3.0;
qubit[3] q;
h q[0];
cx q[0], q[1];
cx q[0], q[2];
"""
```
### Estimate Cost
You can use the `device.run()` method with the `dry_run=True` option to obtain a cost estimate for the anticipated job
without actually executing the program on a QPU.
```python theme={"dark"}
job = device.run(qasm, name="GHZ (Dry run)", shots=100, dry_run=True)
job.wait_for_final_state()
metadata = job.metadata()
cost_usd = metadata["cost_usd"]
print(f"Cost Estimate (USD): {cost_usd}")
```
If you plan to run your job with [debiasing](https://ionq.com/resources/debiasing-and-sharpening), be sure to include the
`error_mitigation` parameter in the preflight submission to ensure it is accounted for in the cost estimate:
```python theme={"dark"}
job = device.run(..., preflight=True, error_mitigation={"debias": True})
```
### Submit to QPU
When you are ready to submit the job on the QPU, simply set `preflight=False`, or don't specify any `preflight` value.
Optionally include job tags or other metadata for your convenience, specifying up to 10 key-value pairs.
```python theme={"dark"}
job = device.run(qasm, name="GHZ", shots=100, metadata={"key": "str_value"})
```
When submitting jobs to a QPU, your job may need to wait in the queue. You can print and record the job’s unique ID,
which can be used to retrieve the job (including its status and results) at a later time, see [Retrieve a job](#retrieve-a-job).
```python theme={"dark"}
print(job.id)
```
Check the status of your job:
```python theme={"dark"}
print(job.status())
```
Once the job is `COMPLETED`, you can retrieve the results:
```python theme={"dark"}
result = job.result()
counts = result.data.get_counts(decimal=True)
print(f"Counts: {counts}")
print(f"Details: {result.details}")
```
## Submit a multi-circuit job
The flexibility of qBraid-SDK allows you to write your circuit not just in OpenQASM, but in Qiskit, Cirq, Amazon Braket, PyTKET,
or any other [registered program type](/v2/sdk/user-guide/programs#quantum-program-registry) that can be
[transpiled](/v2/sdk/user-guide/transpiler) to qBraid's `qasm2` or `qasm3` format.
In this example, we'll install the `qiskit` and `cirq` extras,
```bash theme={"dark"}
pip install 'qbraid[qiskit,cirq]'
```
and submit a [multi-circuit job](https://docs.ionq.com/api-reference/v0.3/multicircuit-jobs) containing one of each program type:
```python theme={"dark"}
import cirq
from qbraid.runtime import IonQProvider
from qiskit import QuantumCircuit
provider = IonQProvider()
device = provider.get_device("simulator")
# Define a bell state in qiskit
qiskit_bell = QuantumCircuit(2)
qiskit_bell.h(0)
qiskit_bell.cx(0, 1)
# Define a bell state in cirq
cirq_bell = cirq.Circuit()
q0, q1 = cirq.LineQubit.range(2)
cirq_bell.append(cirq.H(q0))
cirq_bell.append(cirq.CNOT(q0, q1))
circuit_batch = [qiskit_bell, cirq_bell]
job = device.run(circuit_batch, shots=1000, noise={"model": "aria-1"})
result = job.result()
counts_batch = result.data.get_counts()
print("\n".join(f"Circuit {i}: {c}" for i, c in enumerate(counts_batch)))
```
This script submits two quantum circuits in a single job. When the job completes, it prints the counts for each circuit:
```bash theme={"dark"}
Circuit 0: {'00': 492, '01': 4, '10': 3, '11': 501}
Circuit 1: {'00': 482, '01': 6, '10': 6, '11': 506}
```
All of the necessary program type conversions are carried out automatically within the `device.run()` method. However, you can also perform
this "transpile" step manually, if needed (e.g. to inspect the ultimate OpenQASM submission format). This can be done as follows:
```python theme={"dark"}
from qbraid import transpile
qasm_batch = [transpile(circuit, "qasm2") for circuit in circuit_batch]
jobs = device.run(qasm_batch, shots=1000, ...)
```
qBraid offers native support for 10 major quantum programming libraries including 20+ inter-library conversions. For more details, including
how to configure your own custom conversions, refer to the [qBraid transpiler documentation](/v2/sdk/user-guide/transpiler).
## Manage jobs
### Retrieve a job
You can retrieve results for a previously run job using its job ID. You can save the job ID after submitting a job
(as in the QPU example above) or copy it from the “ID” column in the “My Jobs” tab on the [IonQ Cloud Console](https://cloud.ionq.com/jobs).
```python theme={"dark"}
from qbraid.runtime import IonQJob, IonQProvider
job_id = "..."
provider = IonQProvider()
job = IonQJob(job_id, provider.session)
result = job.result()
print(result.data.get_counts())
```
### Cancel a job
You can cancel a job while it’s waiting in the queue:
```python theme={"dark"}
job.cancel()
```
## Visualize Results
To plot the results of a job, first, install the qBraid visualization extra:
```bash theme={"dark"}
pip install 'qbraid[visualization]'
```
You can visualize the histogram counts data using `plot_histogram`, or display the probability distribution with `plot_distribution`.
```python theme={"dark"}
from qbraid.visualization import plot_histogram
plot_histogram(counts)
```
The "counts" input may be either a single dictionary or a list of dictionaries for multi-circuit jobs. In the latter case, histogram data
for each circuit will be plotted side-by-side. The X-axis labels will display in decimal if you set `decimal=True` when retrieving measurement
counts with `get_counts()`. If `decimal=False` or left unspecified, the default quantum state labels in hexadecimal will be used.
See [Plot Experimental Results](/v2/sdk/user-guide/visualization#plot-experimental-results) for more.
## Supported gates
For actual execution, gates will be compiled into optimal operations for our trapped ion hardware. For convenience,
IonQ provide a more expressive gateset for programming. However, not all gates supported by the OpenQASM 3 standard library
are accepted by IonQ backends. See full list of [supported gates](https://docs.ionq.com/api-reference/v0.3/writing-quantum-programs#supported-gates).
You can also view a list of the OpenQASM gates supported by a given device directly from an `IonQDevice`. For example:
```python theme={"dark"}
device = provider.get_device("qpu.forte-1")
print(device.profile.basis_gates)
```
Which would return:
```bash theme={"dark"}
{'swap', 'tdg', 't', 'gpi2', 'gpi', 'rz', 'sx', 'z', 's', 'sdg', 'zz', 'cx', 'rx', 'y', 'h', 'ry', 'x', 'sxdg'}
```
Note: the returned gateset includes both the abstract QIS and IonQ-native gates supported by the device; however, circuits must be constructed
using either exclusively QIS gates or exclusively native gates—you cannot mix the two within a single circuit. In the next section, we’ll
explore using IonQ native gates in greater detail.
## Native Gates
Building and submitting circuits using IonQ's hardware-native gateset enables you to bypass our compiler and optimizer, providing more control
and transparency than the default abstract gateset (though often at the cost of performance and convenience).
Before working with native gates in qBraid, we recommend reviewing our guides on [Getting Started with Native Gates](https://docs.ionq.com/guides/getting-started-with-native-gates)
and the [IonQ Native Gates API](https://docs.ionq.com/api-reference/v0.3/native-gates-api).
This is an advanced-level feature. Using the hardware-native gate interface
without a thorough understanding of quantum circuits is likely to result in
less-optimal circuit structure and worse algorithmic performance overall than
using our abstract gate interface.
IonQ's native gates are incorporated as a natural extension of the OpenQASM standard library within the `qbraid[ionq]` runtime integration.
qBraid supports the following IonQ native gates:
* `gpi(phi)`
* `gpi2(phi)`
* `ms(phi0, phi1, theta=0.25)` for Aria systems
* `zz(theta)` for Forte systems
For more details about these gate definitions and parameters, refer to the [native gates guide](https://docs.ionq.com/guides/getting-started-with-native-gates#introducing-the-native-gates).
The parameters in the IonQ native gate specification are always defined in
*turns*, not in radians. One turn is 2π radians.
Native gate circuits can then be built and executed as follows:
```python theme={"dark"}
from qbraid.runtime import IonQProvider
provider = IonQProvider(api_key="YOUR_API_KEY")
device = provider.get_device("simulator")
qasm = """
OPENQASM 3.0;
qubit[3] q;
gpi(0.5) q[0];
gpi2(0) q[1];
ms(0,0.5) q[1], q[2];
"""
job = device.run(qasm, shots=1000)
```
Each quantum circuit submitted to the IonQ Cloud must use a consistent gateset
throughout--you cannot mix and match native gates and abstract gates in the
same circuit.
The `qbraid[ionq]` runtime integration does not currently support automatic transpilation from abstract to native gates, but we may add this
capability in the future. For now, we recommend following this general procedure (also described in IonQ's main [native gates guide](https://docs.ionq.com/guides/getting-started-with-native-gates#converting-to-native-gates))
or using a different SDK.
***
## Additional resources
*Great work!* You successfully ran your first quantum circuits - *what next?*
Explore more resources for using qBraid:
* [API Reference](https://qbraid.github.io/qBraid/)
* [Source Code on GitHub](https://github.com/qBraid/qBraid)
* [Example Notebooks on GitHub](https://github.com/qBraid/qbraid-lab-demo)
Find examples for using IonQ systems with other quantum programming libraries:
* [IonQ Runtime Provider Notebook on GitHub](https://github.com/qBraid/qbraid-lab-demo/blob/main/Provider-Jobs/qbraid_runtime_ionq_provider.ipynb)
* [IonQ Samples Library on GitHub](https://github.com/ionq-samples/getting-started)
# QbraidProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/native
Runtime integration for streamlined access to quantum devices supported by qBraid.
API Reference:
[qbraid.runtime.native](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.native.html)
## Installation & Setup
To interface with the qBraid QIR simulator or any of the 10+ quantum devices supported by qBraid's managed access,
install the relevant `qbraid` runtime extra(s) based on your device(s) of choice:
```bash theme={"dark"}
pip install qbraid
```
qbraid versions \<0.11 are *not* compatible with qBraid API V2. See [migration guide](/v2/api-reference/rest/migration).
To ensure compatibility with the new platform, use **qbraid** ≥ 0.11.0.
Next, obtain your qBraid API key:
1. Login or create an account at [account.qbraid.com](https://account.qbraid.com/).
2. Navigate to **Account** > **API Keys** in the left-sidebar, and then click "Create API Key".
See also: [Account - API Keys](/v2/account/api-keys)
### Save account to disk
Once you have your API key, you can save it locally in a configuration file `~/.qbraid/qbraidrc`,
where `~` corresponds to your home (`$HOME`) directory:
Account credentials are saved in plain text, so only do so if you are using a
trusted device.
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
provider = QbraidProvider(api_key='API_KEY')
provider.save_config()
```
Once the account is saved on disk, you can instantiate the provider without any arguments:
```python theme={"dark"}
provider = QbraidProvider()
```
### Load account from environment variables
Alternatively, the qBraid-SDK can discover credentials from environment variables:
```bash theme={"dark"}
export QBRAID_API_KEY='QBRAID_API_KEY'
```
## Basic Usage
Given a device "QRN" (qBraid Resource Name), a `QbraidDevice` object can be created as follows:
```python theme={"dark"}
from qbraid import QbraidProvider
provider = QbraidProvider()
provider.get_devices()
# []
device = provider.get_device('qbraid:qbraid:sim:qir-sv')
type(device)
#
```
From here, class methods are available to get information about the device, execute quantum programs,
access the wrapped device object directly, and more.
```python theme={"dark"}
device.metadata()
# {'device_id': 'qbraid:qbraid:sim:qir-sv',
# 'device_type': 'SIMULATOR',
# 'num_qubits': 30,
# 'status': 'ONLINE',
# 'queue_depth': 0}
```
Then you can submit quantum jobs to the device.
```python theme={"dark"}
run_input = [qiskit_circuit, braket_circuit, cirq_circuit, qasm3_str]
jobs = device.run(run_input, shots=100)
results = [job.result() for job in jobs]
print(results[0].data.get_counts())
# {'00': 50, '01': 2, '10': 47, '11': 1}
```
See how to visualize these results in the [Visualization](/v2/sdk/user-guide/visualization#plot-experimental-results) section.
## Runtime Options
When submitting jobs through the `QbraidProvider`, you can pass provider-specific options using
the `runtime_options` keyword argument. These options are forwarded directly to the underlying
cloud provider's submission API, giving you access to device-specific features without
needing to configure provider credentials yourself.
```python theme={"dark"}
job = device.run(circuit, shots=100, runtime_options={"key": "value"})
```
The `runtime_options` dictionary is passed through as-is to the provider backend:
* **Amazon Braket** devices: options are unpacked as keyword arguments to the Braket `device.run()` call
* **Azure Quantum** devices: options are passed as `input_params` to the Azure `device.run()` or `device.submit()` call
* **qBraid** devices: options are merged into the job submission payload
### qBraid Simulator Examples
Pass a `seed` to make a simulation reproducible. Two runs of the same circuit with the same
seed return identical measurement counts:
```python theme={"dark"}
device = provider.get_device("qbraid:qbraid:sim:qir-sv")
job = device.run(circuit, shots=100, runtime_options={"seed": 42})
print(job.result().data.get_counts())
# {'00': 48, '11': 52}
```
Submitting the same circuit again with `"seed": 42` returns those same counts. Change the
seed, or leave it out, and the simulator draws a fresh sample on every run.
This is useful for tutorials and course material where the output in the text should match
what the reader sees, for regression tests that assert on exact counts, and for sharing a
result someone else can reproduce.
### Amazon Braket Examples
Enable experimental capabilities on supported devices:
```python theme={"dark"}
device = provider.get_device("aws:quera:qpu:aquila")
job = device.run(
program,
shots=1000,
runtime_options={"experimental_capabilities": "ALL"},
)
```
Disable qubit rewiring for verbatim compilation on Rigetti:
```python theme={"dark"}
device = provider.get_device("aws:rigetti:qpu:cepheus-1-108q")
job = device.run(
circuit,
shots=1000,
runtime_options={"disable_qubit_rewiring": True},
)
```
See [BraketProvider - Runtime Options](/v2/sdk/user-guide/providers/aws#runtime-options) for the full list of supported options.
### Azure Quantum Examples
Use the stabilizer simulator on Quantinuum emulators:
```python theme={"dark"}
device = provider.get_device("azure:quantinuum:sim:h2-1e")
job = device.run(
circuit,
shots=100,
runtime_options={"simulator": "stabilizer"},
)
```
Disable noise model and compiler optimization:
```python theme={"dark"}
job = device.run(
circuit,
shots=100,
runtime_options={"error-model": False, "no-opt": True},
)
```
See [AzureQuantumProvider - Runtime Options](/v2/sdk/user-guide/providers/azure#runtime-options) for provider-specific options.
### IonQ Examples
Run with a hardware noise profile on the IonQ simulator:
```python theme={"dark"}
device = provider.get_device("ionq:ionq:sim:simulator")
job = device.run(
circuit,
shots=1000,
runtime_options={"noise": {"model": "aria-1", "seed": 42}},
)
```
"Noise" options include: `ideal`, `harmony`, `harmony-1`, `harmony-2`, `aria-1`, `aria-2`, `forte-1`, `forte-enterprise-1`
## Next Steps
See [Job Execution](/v2/sdk/user-guide/providers/native/jobs) for single job submission, group jobs, and cross-device workflows.
# Device calibrations and connectivity
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/native/calibrations
Fetch live coupling maps and calibration data for QPUs on qBraid, plot connectivity graphs, and use the data to place circuits on the best-calibrated qubits before submitting.
Requires the latest `qbraid` pre-release: `pip install --upgrade --pre
qbraid`. Calibration data is refreshed from each hardware provider roughly
hourly.
Superconducting QPUs are not uniform: qubits differ in readout fidelity, and
two-qubit gates only exist between physically coupled pairs, each with its own
error rate. The [QbraidProvider](/v2/sdk/user-guide/providers/native) exposes
both facts programmatically, so you can inspect a device before you submit to
it, and place your circuit on the qubits where it will perform best.
## Fetching calibration data
Every `QbraidDevice` has two entry points:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("rigetti:rigetti:qpu:cepheus-1-108q")
device.coupling_map
# ((0, 1), (0, 9), (1, 2), (1, 10), (2, 3), ...) 193 physical qubit pairs
calibration = device.get_calibrations()
calibration.last_calibrated
# '2026-07-21T20:05:45+00:00'
```
`coupling_map` is the device's physical connectivity: sorted, deduplicated
`(source, target)` qubit pairs, derived from the calibrated two-qubit gate
edges. It is cached on the device instance, since hardware topology does not
change between calibrations.
`get_calibrations()` returns the full live snapshot and always fetches fresh
data. The two most useful fields:
```python theme={"dark"}
# Per-edge two-qubit gate error, keyed by gate name
calibration.edges["gateError"]["cz"][0]
# EdgeEntry(source=0, target=1, value=0.0246)
# Per-qubit metrics
calibration.qubits["0"]
# QubitCalibration(readout_error=0.04, gate_error={'rb': 0.0019}, ...)
```
Both return `None` for devices without published calibration data, such as
simulators.
### Devices with calibration data
| Physical device | Qubits | Edges | Example device IDs |
| ---------------------- | ------ | ----- | ---------------------------------------------------------------------- |
| Rigetti Cepheus-1-108Q | 107 | 193 | `rigetti:rigetti:qpu:cepheus-1-108q`, `aws:rigetti:qpu:cepheus-1-108q` |
| IQM Garnet | 20 | 30 | `aws:iqm:qpu:garnet` |
| IQM Emerald | 54 | 85 | `aws:iqm:qpu:emerald` |
| AQT Ibex-Q1 | 12 | 0 | `aws:aqt:qpu:ibex-q1` |
All device IDs that map to the same physical hardware share one calibration
snapshot, so `rigetti:rigetti:qpu:cepheus-1-108q` and
`aws:rigetti:qpu:cepheus-1-108q` return identical data.
A device can have calibration data with an empty edge list. AQT's Ibex-Q1 is a
trapped-ion system with all-to-all connectivity, so there are no discrete
coupling edges to report: `coupling_map` returns an empty tuple, not `None`.
For IonQ trapped-ion devices, see `device.profile.characterization` instead.
You can also query the underlying REST endpoint directly, which works for any
device ID including those not available for direct submission:
```bash theme={"dark"}
curl -H "X-API-Key: $QBRAID_API_KEY" \
https://api.qbraid.com/api/v1/devices/rigetti:rigetti:qpu:cepheus-1-108q/calibrations
```
## Plotting the connectivity graph
The `qbraid.visualization` module renders the graph in one call, colored by
live calibration data. Edges are colored by two-qubit gate error and nodes by
readout error, on a single-hue scale where darker is better, matching the
topology view in qBraid Lab:
```python theme={"dark"}
from qbraid.visualization import plot_connectivity_graph
plot_connectivity_graph(device)
```
The dotted outline is a qubit in the lattice footprint with no working
couplings. Light qubits and edges are the ones to avoid.
The layout comes from the device document's `topology` config, so square
lattices (Rigetti) and clipped lattices (IQM) both render their true physical
geometry; devices without a lattice config fall back to a force-directed
layout. To build a custom plot, the `lattice_positions` helper maps qubit ids
to grid coordinates from the same config:
```python theme={"dark"}
from qbraid.visualization import lattice_positions
topology = device.client.get_device(device.id).topology
# {'type': 'square-lattice', 'rows': 12, 'cols': 9}
positions = lattice_positions(topology, range(108))
# {0: (0, 0), 1: (1, 0), ..., 107: (8, -11)}
```
The same call works unchanged on any device with calibration data. IQM's
Garnet renders its clipped diamond lattice:
```python theme={"dark"}
garnet = provider.get_device("aws:iqm:qpu:garnet")
plot_connectivity_graph(garnet)
```
## Choosing the best qubits
With the calibration data in hand, qubit selection becomes a graph problem.
Build a weighted graph from the coupling map, then the single best-calibrated
pair is one line:
```python theme={"dark"}
import networkx as nx
calibration = device.get_calibrations()
edge_error = {
(e.source, e.target): e.value
for e in calibration.edges["gateError"]["cz"]
}
graph = nx.Graph()
for q0, q1 in device.coupling_map:
graph.add_edge(q0, q1, error=edge_error.get((q0, q1), edge_error.get((q1, q0))))
```
```python theme={"dark"}
best_edge = min(edge_error, key=edge_error.get)
# (88, 89), CZ error 0.0037 -- 2.3x better than the device median
```
The lowest CZ error is not always the best place to run. Readout error varies
by an order of magnitude across the lattice, and on some snapshots the best-CZ
edge sits on a qubit with weak readout. For two-qubit circuits, score edges on
the product of CZ, readout, and single-qubit fidelities; see [Scoring
edges](/v2/sdk/user-guide/providers/native/rigetti#scoring-edges) for a worked
example where the two rankings disagree on hardware.
For a linear circuit on `n` qubits, search for the connected chain that
minimizes the summed two-qubit error:
```python theme={"dark"}
def best_chain(graph, length):
"""Find the simple path of `length` qubits minimizing summed edge error."""
best = (float("inf"), None)
def extend(path, total):
nonlocal best
if total >= best[0]:
return
if len(path) == length:
best = (total, list(path))
return
for nbr in graph.neighbors(path[-1]):
if nbr not in path:
path.append(nbr)
extend(path, total + graph.edges[path[-2], nbr]["error"])
path.pop()
for start in graph.nodes:
extend([start], 0.0)
return best
total, chain = best_chain(graph, 5)
# chain = [88, 89, 98, 97, 96], summed CZ error 0.0214
```
Calibrations shift with every refresh, so re-run the selection shortly before
you submit. The best chain this hour is not always the best chain tonight.
## Running on the qubits you chose
### Rigetti direct: hand-placed native gates
On the [Rigetti direct path](/v2/sdk/user-guide/providers/native/rigetti),
programs that bypass quilc must already use
[native gates](/v2/sdk/user-guide/providers/native/rigetti#native-gates) on
physical qubits, which is exactly what the coupling map enables. A Bell pair
on the best-calibrated edge:
```python theme={"dark"}
import pyquil
q0, q1 = best_edge # (88, 89)
program = pyquil.Program(f"""
DECLARE ro BIT[2]
RX(pi/2) {q0}
RX(pi/2) {q1}
CZ {q0} {q1}
RX(-pi/2) {q1}
MEASURE {q0} ro[0]
MEASURE {q1} ro[1]
""")
job = device.run(program, shots=100)
job.wait_for_final_state()
job.result().data.get_counts()
# {'00': 44, '11': 44, '01': 7, '10': 5} -- run on Cepheus-1-108Q, 2026-07-21
```
The correlated outcomes (`00` and `11`) came back at 88 percent on hardware,
consistent with the roughly 0.4 percent CZ error and few-percent readout error
of the chosen pair.
This program contains no timing instruction, so it takes the **compiled**
path. The placement survives because its qubits already fit the topology,
which makes quilc default to identity (`NAIVE`) rewiring; quilc is not
obligated to keep it in general. To make placement a guarantee rather than a
default, add a clock-aligned delay to take the direct path, or pin the
compiled path with `PRAGMA INITIAL_REWIRING "NAIVE"`. See [Pinning the
circuit](/v2/sdk/user-guide/providers/native/rigetti#pinning-the-circuit).
### Qiskit: constrain transpilation to the real topology
If you would rather let a transpiler do the routing, feed the coupling map to
Qiskit and pin your circuit to the chain you selected. This works for any
gate-model device on qBraid:
```python theme={"dark"}
from qiskit import QuantumCircuit, transpile
from qiskit.transpiler import CouplingMap
ghz = QuantumCircuit(5, 5)
ghz.h(0)
for i in range(4):
ghz.cx(i, i + 1)
ghz.measure(range(5), range(5))
coupling = CouplingMap(
[list(edge) for edge in garnet.coupling_map]
+ [[b, a] for a, b in garnet.coupling_map]
)
total, chain = best_chain(garnet_graph, 5) # [6, 11, 16, 15, 10] on Garnet
transpiled = transpile(
ghz,
coupling_map=coupling,
initial_layout=chain,
basis_gates=["r", "cz"],
optimization_level=3,
)
job = garnet.run(transpiled, shots=1000)
```
Every two-qubit gate in the transpiled circuit now acts on a physically
coupled, well-calibrated pair, and no SWAP overhead is silently inserted for
qubits you did not choose.
Transpile against the coupling map of the exact device ID you will submit to.
Vendor-routed and direct device IDs share hardware and calibrations, but a
circuit laid out for one device will not fit another.
## Related links
* [QbraidProvider](/v2/sdk/user-guide/providers/native): setup and credentials
* [Rigetti Cepheus-1-108Q](/v2/sdk/user-guide/providers/native/rigetti): native
gates, Quil-T timing, and the two compilation paths
* [Job execution](/v2/sdk/user-guide/providers/native/jobs): single, batch, and
group submission
* [Visualization](/v2/sdk/user-guide/visualization): histograms and other
built-in plotting utilities
# Job Execution
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/native/jobs
Submit single quantum jobs, batch multiple circuits to one device, or group jobs across providers.
API Reference:
[qbraid.runtime.native](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.native.html)
This page covers how to submit quantum jobs through the `QbraidProvider` — from single
one-off submissions, to batching multiple circuits in a single job, to grouped workflows
that span multiple devices and providers.
For provider setup and authentication, see [QbraidProvider - Usage](/v2/sdk/user-guide/providers/native).
## Single Job Submission
Submit a quantum program to any qBraid-supported device and retrieve results:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("qbraid:qbraid:sim:qir-sv")
# Define a quantum program (QASM, Qiskit, Cirq, Braket, etc.)
bell = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c = measure q;
"""
# Submit and wait for results
job = device.run(bell, shots=100)
result = job.result()
print(result.data.get_counts())
# {'00': 52, '11': 48}
```
For async applications, you can use [`async_result()`](/v2/sdk/user-guide/overview#async-results) to await job completion without blocking the event loop:
```python theme={"dark"}
result = await job.async_result(timeout=120, poll_interval=5)
print(result.data.get_counts())
```
You can also submit multiple programs at once — each is executed as a separate job:
```python theme={"dark"}
programs = [bell_circuit, ghz_circuit, qft_circuit]
jobs = device.run(programs, shots=100)
results = [job.result() for job in jobs]
```
## Group Jobs
### Why Group?
When running related quantum experiments — parameter sweeps, algorithm comparisons,
cross-device benchmarks — you often end up with many independent jobs that are logically
part of the same workflow. Without grouping, these jobs are scattered across your job history
with no way to track or retrieve them together.
**`GroupJobSession`** solves this by grouping any number of jobs under a single group ID.
Key benefits:
* **Cross-device, cross-provider**: submit jobs to different backends (AWS SV1, IonQ, qBraid simulators) within the same group
* **Unified tracking**: all jobs share a group QRN visible in the qBraid dashboard
* **Aggregated results**: retrieve all results at once with `group.results()`
* **Lifecycle management**: auto-close with TTL, cancellation, completion callbacks
### Context Manager
The simplest way to use group jobs. All jobs submitted inside the `with` block are
automatically tagged with the group ID. The group closes when the block exits.
```python theme={"dark"}
from qbraid.runtime import GroupJobSession, QbraidProvider
provider = QbraidProvider()
bell = """
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c = measure q;
"""
# All jobs inside this block belong to the same group
with GroupJobSession(name="Bell State Sweep") as group:
# Submit to different devices within the same group
sv1 = provider.get_device("aws:aws:sim:sv1")
job1 = sv1.run(bell, shots=100)
tn1 = provider.get_device("aws:aws:sim:tn1")
job2 = tn1.run(bell, shots=100)
print(f"Group ID: {group.group_id}")
print(f"Jobs in group: {len(group.jobs)}")
# Group ID: group:abc-123456
# Jobs in group: 2
```
### Manual Open / Close
For interactive workflows like Jupyter notebooks, you can open and close the group
manually across multiple cells.
```python theme={"dark"}
from qbraid.runtime import GroupJobSession, QbraidProvider
provider = QbraidProvider()
# Create and open the group manually
group = GroupJobSession(name="Notebook Experiment")
group.open()
# Cell 2: submit jobs (can be in separate notebook cells)
device = provider.get_device("qbraid:qbraid:sim:qir-sv")
job1 = device.run(circuit_1, shots=100)
job2 = device.run(circuit_2, shots=100)
# Cell 3: close when done submitting
group.close()
```
### Auto-Close with TTL
Set a time-to-live so the group automatically closes after a duration, even if you
forget to call `close()` or your kernel crashes. Defaults to 1 hour (3600s) if not specified.
```python theme={"dark"}
# Group auto-closes after 60 seconds (default: 3600s / 1 hour)
with GroupJobSession(name="Quick Sweep", max_ttl=60) as group:
device = provider.get_device("aws:aws:sim:sv1")
job = device.run(bell, shots=10)
print(f"TTL: {group.max_ttl}s")
```
The `max_ttl` parameter accepts values from 1 to 86400 seconds (24 hours). If
not specified, the backend defaults to 3600 seconds (1 hour).
### Retrieving Results
After closing a group, retrieve all job results at once. `group.results()` blocks until
every job reaches a terminal state (completed, failed, or cancelled).
```python Submission theme={"dark"}
with GroupJobSession(name="Result Demo") as group:
device = provider.get_device("qbraid:qbraid:sim:qir-sv")
job1 = device.run(bell, shots=100)
job2 = device.run(bell, shots=100)
# Wait for all jobs and collect results
results = group.results(timeout=300)
print(results)
for job_id, result in results.results.items():
print(f"{job_id}: {result.data.get_counts()}")
```
```text Results theme={"dark"}
GroupResult(group_id='group:ghi-345678', total=2, successful=2, failed=0)
job:aaa-111: {'00': 48, '11': 52}
job:bbb-222: {'00': 51, '11': 49}
```
You can also filter results by outcome:
```python theme={"dark"}
# Only successful results
successful = results.successful()
# Only failed results
failed = results.failed()
```
### Completion Callback
Register a callback that fires automatically when all jobs complete. The callback
runs at context exit, after the group is closed.
```python Callback Setup theme={"dark"}
def analyze(results):
"""Process results when all jobs finish."""
for job_id, result in results.items():
counts = result.data.get_counts()
print(f"{job_id}: {counts}")
with GroupJobSession(name="With Callback") as group:
device = provider.get_device("qbraid:qbraid:sim:qir-sv")
device.run(bell, shots=100)
device.run(bell, shots=100)
# Callback fires automatically when the context exits
group.on_all_complete(analyze, timeout=600)
```
```text Callback Output theme={"dark"}
job:ccc-333: {'00': 47, '11': 53}
job:ddd-444: {'00': 50, '11': 50}
```
### Cancellation
Cancel a group and all its non-terminal jobs:
```python theme={"dark"}
group = GroupJobSession(name="Cancellable")
group.open()
device = provider.get_device("aws:aws:sim:sv1")
job1 = device.run(bell, shots=1000)
job2 = device.run(bell, shots=1000)
# Cancel the entire group and reset the session
group.cancel()
print(group.status())
# GroupStatus.CANCELLED
```
## Batch Jobs
Submit multiple circuits as a **single job** to a device that supports batched execution.
Unlike [group jobs](#group-jobs), which coordinate independent jobs across devices, batch
jobs send all circuits in one request and return a unified result.
### Submitting a Batch
Pass a list of programs with `as_batch=True` to submit them as a single job:
```python theme={"dark"}
from qiskit import QuantumCircuit
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("qbraid:equal1:sim:bell-1")
# Define multiple circuits
bell = QuantumCircuit(2, 2)
bell.h(0)
bell.cx(0, 1)
bell.measure([0, 1], [0, 1])
ghz = QuantumCircuit(3, 3)
ghz.h(0)
ghz.cx(0, 1)
ghz.cx(1, 2)
ghz.measure([0, 1, 2], [0, 1, 2])
x_gate = QuantumCircuit(1, 1)
x_gate.x(0)
x_gate.measure(0, 0)
# Submit all three circuits as a single batch job
job = device.run([bell, ghz, x_gate], shots=1000, as_batch=True)
print(type(job))
#
```
A batch submission returns a single `QbraidJob`, not a list. All circuits share the same
job ID, device, and shot count.
`as_batch=True` requires a device that supports batch execution. You can check
this via the device profile's `batch_job_support` flag. Passing
`as_batch=True` to an unsupported device raises a `ValueError`.
### Retrieving Batch Results
Calling `job.result()` on a batch job returns a `BatchResult` object:
```python Retrieval theme={"dark"}
result = job.result()
print(type(result))
print(result.num_circuits)
```
```text Output theme={"dark"}
3
```
`BatchResult` provides two ways to access measurement data:
**Aggregate access** — indexed by circuit position:
```python Aggregate theme={"dark"}
# All circuit counts as a list
all_counts = result.data.get_counts()
for i, counts in enumerate(all_counts):
print(f"Circuit {i}: {counts}")
```
```text Output theme={"dark"}
Circuit 0: {'00': 512, '11': 488}
Circuit 1: {'000': 498, '111': 502}
Circuit 2: {'1': 1000}
```
**Per-circuit access** — each circuit's `Result` is a full object with its own status and metadata:
```python Per-Circuit theme={"dark"}
for i, circuit_result in enumerate(result.results):
print(f"Circuit {i}: status={circuit_result.success}, counts={circuit_result.data.get_counts()}")
```
```text Output theme={"dark"}
Circuit 0: status=True, counts={'00': 512, '11': 488}
Circuit 1: status=True, counts={'000': 498, '111': 502}
Circuit 2: status=True, counts={'1': 1000}
```
### BatchResult Properties
| Property | Type | Description |
| -------------- | -------------- | ----------------------------------------------------------------------- |
| `num_circuits` | `int` | Number of circuits in the batch |
| `results` | `list[Result]` | Per-circuit `Result` objects |
| `data` | `ResultData` | Aggregated data — `get_counts()` and `get_probabilities()` return lists |
| `details` | `list[dict]` | Per-circuit metadata (e.g. `compiledOutput`) |
| `success` | `bool` | Whether the overall job completed successfully |
| `device_id` | `str` | Device QRN |
| `job_id` | `str` | Job QRN |
### Per-Circuit Error Handling
In a batch job, individual circuits can fail while others succeed. Failed circuits
carry their own error message, accessible through their per-circuit result:
```python theme={"dark"}
result = job.result()
for i, circuit_result in enumerate(result.results):
if not circuit_result.success:
print(f"Circuit {i} failed: {circuit_result.details.get('statusMsg')}")
else:
print(f"Circuit {i}: {circuit_result.data.get_counts()}")
```
### Batch Jobs vs Group Jobs
| | Batch Jobs | Group Jobs |
| --------------------- | ----------------------------------------------- | ------------------------------------ |
| **Scope** | Multiple circuits, one device | Multiple jobs, any device |
| **Submission** | `device.run(programs, as_batch=True)` | `GroupJobSession` context |
| **Return type** | Single `QbraidJob` → `BatchResult` | Multiple `QbraidJob` → `GroupResult` |
| **Use case** | Parameterized circuits, algorithm variants | Cross-device benchmarks, sweeps |
| **Supported devices** | Devices with `device.profile.batch_job_support` | All devices |
# Rigetti Cepheus-1-108Q
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/native/rigetti
Submitting gate-model and timing-aware programs to Rigetti's Cepheus-1-108Q through the QbraidProvider, from any qBraid-supported framework.
Device ID: `rigetti:rigetti:qpu:cepheus-1-108q` · 108 qubits
· superconducting · accessed through
[`QbraidProvider`](/v2/sdk/user-guide/providers/native)
Cepheus-1-108Q is Rigetti's 108-qubit superconducting processor, available on qBraid with no
Rigetti account of your own. You write a circuit in whichever framework you already use, hand it
to `device.run()`, and qBraid handles conversion, compilation, submission and results.
This page covers the one thing about Cepheus that surprises people: **it has two different
compilation paths, and which one your program takes depends on whether it contains timing
instructions.** Everything else follows from that.
This is the **direct** Rigetti device, billed per minute of execution. Cepheus-1-108Q is also
offered through Amazon Braket (`aws:rigetti:qpu:cepheus-1-108q`), which is billed per task +
per shot and has different compilation behavior. If you are working with delays or other
timing operations, you want the direct device documented here, since Braket has no gate-level delay.
For direct QCS access using your own Rigetti credentials, see
[RigettiProvider](/v2/sdk/user-guide/providers/rigetti) instead.
## Quick start
A plain gate-model circuit needs nothing special. Write it however you like and submit:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
from qiskit import QuantumCircuit
provider = QbraidProvider()
device = provider.get_device("rigetti:rigetti:qpu:cepheus-1-108q")
circuit = QuantumCircuit(2, 2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure([0, 1], [0, 1])
job = device.run(circuit, shots=10)
job.wait_for_final_state()
print(job.result().data.get_counts())
# {'00': 2, '01': 1, '11': 7}
```
`H` and `CX` are not gates Cepheus can execute. You did not have to care, because this program
took the **compiled path**.
## The two compilation paths
Whether your program is compiled for you, or has to arrive ready to run, depends entirely on
whether it contains **Quil-T** instructions: Rigetti's timing and pulse-level operations, of
which `DELAY` is the one most people meet first.
| Your program | Path | Compiler | You must write |
| :------------------------------------------- | :----------- | :------------------------------------------------------- | :-------------------- |
| Gates only | **compiled** | [quilc](https://github.com/quil-lang/quilc) nativizes it | any gates you like |
| Contains a delay or other Quil-T instruction | **direct** | quilc is skipped | **native gates only** |
The reason is not a qBraid limitation. quilc is a gate-model compiler, and as Rigetti's docs put
it plainly:
> Quil-T instructions are not supported by quilc or the QVM.
So a program containing `DELAY` cannot go through quilc at all. qBraid detects Quil-T
automatically and routes such programs straight to the QCS translation service, which does accept
timing instructions. Nothing is dropped and you do not set a flag. But because quilc is what
would normally have nativized your gates, **that job is now yours**.
If a program contains a delay and uses a non-native gate, it fails at translation. Rigetti
names the instruction it could not execute:
```
Translation failed for quantum processor 'Cepheus-1-108Q': ... message:
"input program error: Failed to schedule Quil program: at instruction 0 ("X 0"):
this instruction must be replaced or decomposed prior to compilation"
```
This is the single most common error on this device: your timing program reached the QPU with a
gate it cannot execute, here `X 0`. See [Native gates](#native-gates) below.
## Native gates
When you take the direct path, these are the only instructions Cepheus-1-108Q accepts. The set
comes from the device's own ISA:
| Instruction | Accepts | Notes |
| :---------- | :------------------- | :---------------------------------- |
| `RX(θ)` | **only ±π/2 and ±π** | any other angle has no calibration |
| `RZ(θ)` | **any angle** | parametric, so no angle restriction |
| `CZ` | — | the only two-qubit gate |
| `I` | — | identity |
| `MEASURE` | — | readout |
The `RX` restriction is the one that catches people, and it follows from the calibrations: Rigetti
publishes `DEFCAL`s for `RX` at exactly four angles (`±π/2` and `±π`), while `RZ` is defined
parametrically (`DEFCAL RZ(%theta)`) and so takes any angle. A pulse only exists for those `RX`
angles, which is why `RX(0)`, `RX(2π)` and `RX(π/4)` are rejected.
`RX(π)` is an `X`; `RX(π/2)` is a half rotation. Arbitrary single-qubit rotations are built from
these plus `RZ`, which is exactly the decomposition quilc performs for you on the compiled path.
Reaching for `X`? Use `RX(π)`: same operation, and it is native:
```python theme={"dark"}
circuit.rx(np.pi, 0) # native
circuit.x(0) # NOT native: fails at translation if the program has a delay
```
### Nativizing a Qiskit circuit automatically
For anything beyond a few gates, let Qiskit do the decomposition. The basis
choice is the whole trick: use `["rz", "sx", "x", "cz"]`, because `sx` and `x`
map exactly onto the calibrated `RX(π/2)` and `RX(π)` pulses. The seemingly
natural `["rx", "rz", "cz"]` basis does not work, since Qiskit then emits
`RX` at arbitrary angles, which the QPU rejects. Delays pass through
transpilation untouched:
```python theme={"dark"}
from qiskit import QuantumCircuit, transpile
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.delay(50, 0, unit="us")
qc.ry(0.7, 1)
qc.measure([0, 1], [0, 1])
native = transpile(qc, basis_gates=["rz", "sx", "x", "cz"], optimization_level=3)
job = device.run(native, shots=100) # conversion to Quil happens automatically
```
Every gate in the transpiled circuit now lands on a calibrated pulse, so the
program passes translation on the direct path with the delay intact. To
inspect the exact Quil that will run, convert it yourself first:
`qbraid.transpile(native, "pyquil")`.
## Choosing qubits with calibration data
Cepheus is not uniform. On a recent calibration snapshot, two-qubit CZ error ranged from 0.4% on
the best edge to 50% on the worst, and readout error varied by an order of magnitude across the
lattice. Which physical qubits your program lands on is worth controlling, and the
[calibrations API](/v2/sdk/user-guide/providers/native/calibrations) provides the data to control
it with.
What happens to the qubit indices you wrote depends, once again, on the compilation path:
* **Compiled path:** quilc is free to move your circuit. If the indices you wrote already satisfy
the device's connectivity, quilc's default is `NAIVE` rewiring, which keeps them where they
are. If they do not, it falls back to `PARTIAL` rewiring, which chooses a placement using the
gate fidelities Rigetti publishes in the device ISA. An unpinned circuit therefore already gets
fidelity-aware placement; manual selection is for when you want it deterministic, or want to
fold in metrics quilc does not weigh the way you do.
* **Direct path:** no rewiring of any kind. A Quil-T program executes on exactly the physical
qubits it names, which makes it the strongest placement guarantee available.
### Scoring edges
`device.get_calibrations()` reports readout error and single-qubit randomized-benchmarking error
per qubit, plus two-qubit gate error per edge. Fold all three into the score, because the edge
with the lowest CZ error is often not the best place to run:
```python theme={"dark"}
calibration = device.get_calibrations()
qubits = calibration.qubits
def score(edge):
"""Estimated success probability of a two-qubit circuit on this edge."""
a, b = qubits[str(edge.source)], qubits[str(edge.target)]
return (
(1 - edge.value)
* (1 - a.readout_error) * (1 - b.readout_error)
* (1 - a.gate_error["rb"]) ** 4 * (1 - b.gate_error["rb"]) ** 4
)
best = max(calibration.edges["gateError"]["cz"], key=score)
q0, q1 = best.source, best.target
```
On the snapshot used for the run below, the readout-aware score mattered: the edge with the
lowest raw CZ error (0.40%) sat on a qubit with weak readout, and the score selected a different
edge, (92, 101), with a slightly higher CZ error (0.89%) but readout errors of 1.4% and 1.0%. For
chains longer than a pair, and for plotting the graph you are choosing from, see
[Choosing the best qubits](/v2/sdk/user-guide/providers/native/calibrations#choosing-the-best-qubits).
### Pinning the circuit
Three ways to make the placement stick, in decreasing order of guarantee:
```python pyQuil, direct path theme={"dark"}
# A clock-aligned DELAY makes this Quil-T, so quilc is bypassed and the
# program runs on exactly the qubits it names. Native gates required.
from pyquil import Program
program = Program(f"""DECLARE ro BIT[2]
RZ(pi/2) {q0}
RX(pi/2) {q0}
RZ(pi/2) {q0}
RZ(pi/2) {q1}
RX(pi/2) {q1}
RZ(pi/2) {q1}
CZ {q0} {q1}
RZ(pi/2) {q1}
RX(pi/2) {q1}
RZ(pi/2) {q1}
DELAY {q1} 3.2e-8
MEASURE {q0} ro[0]
MEASURE {q1} ro[1]
""")
job = device.run(program, shots=1000)
```
```python Qiskit, direct path theme={"dark"}
# Physical indices survive conversion when the circuit has a single
# register: qubit i maps to physical qubit i. The 32 ns delay makes the
# converted program Quil-T, same guarantee as the pyQuil version.
from qiskit import QuantumCircuit, transpile
qc = QuantumCircuit(max(q0, q1) + 1, 2)
qc.h(q0)
qc.cx(q0, q1)
qc.delay(32, q1, unit="ns")
qc.measure(q0, 0)
qc.measure(q1, 1)
native = transpile(qc, basis_gates=["rz", "sx", "x", "cz"], optimization_level=3)
job = device.run(native, shots=1000)
```
```python pyQuil, compiled path theme={"dark"}
# PRAGMA INITIAL_REWIRING pins placement without leaving the compiled
# path: quilc still nativizes your gates but keeps the identity mapping,
# inserting SWAPs only if you request a pair that is not coupled. Must be
# the first instruction. For a hard no-touch guarantee on a region, wrap
# it in PRAGMA PRESERVE_BLOCK ... PRAGMA END_PRESERVE_BLOCK instead.
from pyquil import Program
program = Program(f"""PRAGMA INITIAL_REWIRING "NAIVE"
DECLARE ro BIT[2]
H {q0}
CNOT {q0} {q1}
MEASURE {q0} ro[0]
MEASURE {q1} ro[1]
""")
job = device.run(program, shots=1000)
```
`DELAY` durations must line up with the QPU's sequencer clock, or translation
rejects the program with `duration not aligned to sequencer clock: 1e-8`. A 10
ns delay is rejected; 32 ns is accepted. When using a delay purely as a Quil-T
marker, 3.2e-8 seconds is a safe choice.
Run on hardware, the Bell pair on the score-selected edge (92, 101) returned
```python theme={"dark"}
job.result().data.get_counts()
# {'00': 480, '11': 423, '01': 58, '10': 39}
```
a correlated fraction of 90.3% at 1000 shots, against roughly 95% predicted from the calibration
data alone. The gap is decoherence and crosstalk the snapshot does not capture, which is the
right way to read these scores: a ranking, not a forecast.
Calibration data is refreshed roughly hourly and the ranking moves with it, so
select your qubits shortly before you submit, not once per project.
## Timing operations
A delay is what makes a program Quil-T. This is a T1 (energy relaxation) measurement: excite the
qubit, wait, and see whether it is still excited:
```python Qiskit theme={"dark"}
import numpy as np
from qbraid.runtime import QbraidProvider
from qiskit import QuantumCircuit
provider = QbraidProvider()
device = provider.get_device("rigetti:rigetti:qpu:cepheus-1-108q")
circuit = QuantumCircuit(1, 1)
circuit.rx(np.pi, 0) # native, NOT circuit.x(0)
circuit.delay(500, 0, unit="us") # makes this a Quil-T program
circuit.measure(0, 0)
job = device.run(circuit, shots=10)
job.wait_for_final_state()
print(job.result().data.get_counts())
# {'0': 9, '1': 1} -- mostly relaxed to |0> during the 500 us delay
```
```python pyQuil theme={"dark"}
from pyquil import Program
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("rigetti:rigetti:qpu:cepheus-1-108q")
program = Program(
"DECLARE ro BIT[1]",
"RX(pi) 0", # native, NOT "X 0"
"DELAY 0 0.0005", # 0.0005 seconds = 500 us
"MEASURE 0 ro[0]",
)
job = device.run(program, shots=10)
job.wait_for_final_state()
print(job.result().data.get_counts())
# {'0': 9, '1': 1}
```
Both submit the same thing. In Quil, `DELAY 0 0.0005` delays qubit 0 by 0.0005 **seconds**;
Qiskit's `delay(500, 0, unit="us")` converts to exactly that.
Relaxation is probabilistic, so your counts will differ run to run. A stray `'1'` at 500 us is
the physics, not a bug. That variation is exactly what a T1 sweep measures.
Passing each instruction as its own string, as above, sidesteps a common trap: pyQuil rejects a
flat instruction that starts with whitespace (`expected a command or a gate`), so an indented
triple-quoted block of plain instructions fails even though it looks fine.
The exception is a block that is *meant* to be indented, such as a `DEFCAL` or `DEFFRAME` body. Those
must arrive as **one** string, indentation included; passed as separate arguments they fail with
`failed to parse arguments for DEFCAL`. See
[prepend\_default\_calibrations](#prepend_default_calibrations) for that shape.
To sweep T1, vary the delay and submit one job per point:
```python theme={"dark"}
delays_us = [0, 50, 100, 200, 400, 800]
jobs = []
for t in delays_us:
circuit = QuantumCircuit(1, 1)
circuit.rx(np.pi, 0)
circuit.delay(t, 0, unit="us") # keep this even at t=0 -- see below
circuit.measure(0, 0)
jobs.append((t, device.run(circuit, shots=1000)))
for t, job in jobs:
job.wait_for_final_state()
counts = job.result().data.get_counts()
p_excited = counts.get("1", 0) / sum(counts.values())
print(f"{t:>4} us -> P(1) = {p_excited:.3f}")
```
Keep the `delay` call at `t = 0` rather than skipping it. A circuit with no
delay is not a Quil-T program, so your zero point would be nativized by quilc
while every other point bypasses it, leaving a baseline compiled differently
from the data it anchors. `delay(0)` emits `DELAY 0 0` and keeps every point
on the same path.
Cepheus-1-108Q is billed **per minute of execution**, prorated with no
minimum. A delay is execution time, so a long delay costs more than a short
one. Individual jobs on this device typically run for tens to hundreds of
milliseconds. See [Pricing](/v2/home/pricing).
## Which framework?
The device itself accepts three run input types: **pyQuil**, **OpenQASM 2** and **OpenQASM 3**.
Qiskit appears throughout this page only because it is the most widely used, not because it is
special. It is converted to QASM like anything else.
That means **any framework qBraid can convert to QASM works here**, not just the two shown. Cirq,
Amazon Braket, PennyLane, pytket and the rest all reach `qasm2`/`qasm3` on the
[ConversionGraph](/v2/sdk/user-guide/transpiler), and `device.run()` handles the conversion:
```python Cirq theme={"dark"}
import cirq
q = cirq.LineQubit.range(2)
circuit = cirq.Circuit([cirq.H(q[0]), cirq.CNOT(q[0], q[1]), cirq.measure(*q, key="m")])
job = device.run(circuit, shots=10)
```
```python Braket theme={"dark"}
from braket.circuits import Circuit
# .measure() is required here -- see the note below
circuit = Circuit().h(0).cnot(0, 1).measure(0).measure(1)
job = device.run(circuit, shots=10)
```
```python PennyLane theme={"dark"}
import pennylane as qml
with qml.tape.QuantumTape() as circuit:
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
qml.sample(wires=[0, 1])
job = device.run(circuit, shots=10)
```
If your framework has a path to `qasm2` or `qasm3` on the ConversionGraph, it will run on Cepheus.
Write in whichever one you already use.
**Measure explicitly.** Some frameworks measure implicitly when run on their own simulators, and
that implicitness does not survive conversion. An Amazon Braket circuit written as
`Circuit().h(0).cnot(0, 1)` converts to QASM with no `measure` statement, so the program reaches
the QPU with nothing to read out. It runs, then fails when the results are parsed:
```
No declared registers found in ro_sources. ro_sources keys: []
```
Add the measurements yourself and the job returns counts as expected.
`QPROGRAM_REGISTRY` is the authoritative answer to "what type do I pass?". It maps each
framework to the exact program type `device.run()` accepts, which is not always the object you
would guess (PennyLane, for instance, takes a `QuantumTape` rather than a `QNode`):
```python theme={"dark"}
from qbraid.programs import QPROGRAM_REGISTRY
print(QPROGRAM_REGISTRY["pennylane"]) #
print(QPROGRAM_REGISTRY["cirq"]) #
print(sorted(QPROGRAM_REGISTRY)) # every framework qBraid knows
```
### When to reach for pyQuil
The one thing QASM cannot express is **Quil-T beyond a simple delay**: `FENCE`, `PULSE`,
frame-level `DELAY 0 "rf" 1e-6`, or your own `DEFCAL` calibrations. Those have no vocabulary in
Qiskit or QASM, so there is nothing for the transpiler to convert. pyQuil is Rigetti's native
language, so nothing is translated and those features have first-class syntax.
An ordinary `delay` is the exception: it converts cleanly from Qiskit, so a T1 or T2 experiment
does not require pyQuil.
Not sure which path your program will take? Ask before you spend a job:
```python theme={"dark"}
from qbraid.runtime.rigetti.device import contains_quil_t
from pyquil import Program
contains_quil_t(Program("RX(pi) 0\nDELAY 0 0.0005")) # True -> direct path, native gates required
contains_quil_t(Program("H 0\nCNOT 0 1")) # False -> compiled path, quilc handles it
```
## Runtime options
Rigetti's [translation service](https://docs.rigetti.com/qcs/guides/the-lifecycle-of-a-program),
the stage that turns your native-gate program into pulses, takes two options, passed as a dict
through `runtime_options`. Unrecognized keys are silently ignored.
| Option | Type | Default | Effect |
| :----------------------------- | :---- | :------ | :---------------------------------------------------------------- |
| `passive_reset_delay_seconds` | float | Rigetti | How long to wait for qubits to relax to \|0⟩ before each shot. |
| `prepend_default_calibrations` | bool | `True` | If `False`, Rigetti's default calibrations are **not** prepended. |
### passive\_reset\_delay\_seconds
Between shots, qubits are left to relax back to |0⟩ on their own, a **passive** reset. This
option sets how long the QPU waits for that.
Rigetti documents it as "the delay between passive resets, in seconds" and does not publish a
default or a recommended range, so the figures below are **measurements from a single run on one
qubit**, included to show the shape of the trade-off. Treat them as illustrative; your own numbers
will differ with the qubit, the calibration and the day.
**The delay is paid once per shot, and you are billed for it.** The same 20-shot program, changing
nothing else:
| `passive_reset_delay_seconds` | Execution observed | Cost observed |
| :---------------------------- | :----------------- | :------------ |
| `0.000001` (1 µs) | 15 ms | \~3 credits |
| `0.01` (10 ms) | 215 ms | \~43 credits |
The 200 ms difference is exactly 20 shots × 10 ms, which is the part that generalizes: **the delay
is per shot**, and this device is billed [per minute of execution](/v2/home/pricing). Multiply by
your shot count before raising it.
What the wait buys is a cleaner starting state. Relaxation is what clears the previous shot.
Measuring a qubit at shot start, after a program that deliberately leaves it excited:
| Reset before each shot | Still excited at shot start (observed) |
| :--------------------- | :---------------------------------------- |
| 1 µs | \~5% (the previous shot bleeding through) |
| 1 ms | \~1.5% (indistinguishable from baseline) |
A control that never excited the qubit read 0%, so the 5% was leftover state rather than readout
error. Again: one qubit, one run. The effect is the point, not the percentages.
So the trade runs both ways:
* **Raise it** for circuits sensitive to initialization, where a few percent of shots starting in
the wrong state would matter.
* **Lower it** to finish sooner and pay less. The floor is what your circuit can tolerate.
```python theme={"dark"}
job = device.run(
circuit,
shots=1000,
runtime_options={"passive_reset_delay_seconds": 0.0001}, # 100 us
)
```
The translation service rejects delays above roughly **67 ms**, reporting
`waveform duration of 1e-1s exceeds maximum allowed duration of
6.7108864e-2s`. That ceiling comes from the service's own error rather than
published documentation, so do not rely on the exact figure.
### prepend\_default\_calibrations
By default Rigetti prepends its calibration set to every program: the `DEFFRAME` and `DEFCAL` definitions that
give `RX`, `RZ`, `CZ` and `MEASURE` their actual pulse shapes. Those definitions are the only
reason a gate means anything on hardware.
**Leave this one alone.** It enables nothing, and setting it to `False` only breaks things.
To **override a calibration**, include your own `DEFCAL` and keep this option at its default.
Your definition is used in place of Rigetti's. A `DEFCAL` that turns `RX(pi)` into a no-op shows
this plainly: the qubit is left in |0⟩ rather than excited.
```python theme={"dark"}
from pyquil import Program
# A DEFCAL body must be indented, so pass the block as ONE string --
# not as separate arguments (see the note under Timing operations).
program = Program(
"""DEFCAL RX(pi) 0:
NOP
DECLARE ro BIT[1]
RX(pi) 0
DELAY 0 0
MEASURE 0 ro[0]
"""
)
job = device.run(program, shots=20) # no runtime_options needed
job.wait_for_final_state()
print(job.result().data.get_counts())
# {'0': 19, '1': 1} -- the RX did nothing.
# Without the custom DEFCAL the same program reads {'0': 1, '1': 19}.
```
Setting `prepend_default_calibrations=False` only *removes* the defaults. Your program must then
carry a complete calibration set of its own, and there is no practical way to do that here:
Rigetti's set defines frames across far more qubits than Cepheus exposes, so submitting it is
rejected before it reaches the QPU.
```
ProgramValidationError: Number of qubits in the circuit (300) exceeds the device's capacity (108)
```
Without a calibration set, translation fails instead, with Rigetti's reason:
```
input program error: program has no defined frames
```
Custom `DEFFRAME`s are constrained regardless: only `INITIAL-FREQUENCY` and
`CHANNEL-DELAY` may differ from Rigetti's defaults. Changing anything else,
such as `SAMPLE-RATE` or `HARDWARE-OBJECT`, is rejected (`frame 0
"Transmon-0_charge_tx" differs from Rigetti-provided definition`). Rigetti
restricts the flag that lifts this to [certain
users](https://docs.rs/qcs/latest/qcs/qpu/translation/struct.TranslationOptions.html),
and it is not enabled for qBraid. Treat the default frames as fixed and
confine your customization to `DEFCAL`s.
## Troubleshooting
Every failure at the translation stage arrives as a `RigettiJobError` naming the processor,
followed by Rigetti's own reason, which is the part that tells you what to fix:
```
Translation failed for quantum processor 'Cepheus-1-108Q': ... message:
"input program error: Failed to schedule Quil program: at instruction 0 ("X 0"):
this instruction must be replaced or decomposed prior to compilation"
```
Read to the end of the line. Rigetti names the offending instruction, here `X 0`, which is not
native.
| Rigetti's reason | What happened |
| :------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `at instruction N (...): must be replaced or decomposed` | A non-native gate reached the QPU, because your program contains a delay and so skipped quilc. Replace `X` with `RX(π)`, `H`/`CX` with `RX`/`RZ`/`CZ` decompositions, or drop the delay to get the compiled path back. Also covers `RX` at a non-native angle. |
| `program has no defined frames` | You set `prepend_default_calibrations=False` without supplying a calibration set. Drop the option; see [above](#prepend_default_calibrations). |
| `duration not aligned to sequencer clock` | A `DELAY` duration the hardware clock cannot represent, such as 10 ns. Use a coarser value; 32 ns (`3.2e-8`) is a safe marker delay. See [Pinning the circuit](#pinning-the-circuit). |
| `waveform duration ... exceeds maximum allowed duration` | `passive_reset_delay_seconds` is above the 67.1 ms cap. |
| `frame ... differs from Rigetti-provided definition` | A `DEFFRAME` of yours changes something other than `INITIAL-FREQUENCY` or `CHANNEL-DELAY`. |
Failures elsewhere in the pipeline:
| Message | Cause |
| :-------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
| `quilc failed to compile the program ...` | The gate-model path could not nativize your circuit. The message carries quilc's own reason. |
| `Number of qubits in the circuit (300) exceeds the device's capacity` | You are submitting Rigetti's full calibration set. See [prepend\_default\_calibrations](#prepend_default_calibrations). |
| Delay silently missing from results | You are on `aws:rigetti:qpu:cepheus-1-108q`, not the direct device. Braket has no gate-level delay. Use `rigetti:rigetti:qpu:cepheus-1-108q`. |
## Related links
* [Pricing](/v2/home/pricing): Cepheus-1-108Q is billed per minute of execution
* [Device calibrations](/v2/sdk/user-guide/providers/native/calibrations): coupling maps, calibration data, and best-chain search
* [QbraidProvider](/v2/sdk/user-guide/providers/native): installation, authentication, runtime options
* [RigettiProvider](/v2/sdk/user-guide/providers/rigetti): direct QCS access with your own credentials
* [Rigetti: Quil-T](https://pyquil-docs.rigetti.com/en/stable/quilt.html): the timing/pulse extension to Quil
* [Rigetti: Getting started with Quil-T](https://pyquil-docs.rigetti.com/en/stable/quilt_getting_started.html): why Quil-T programs must be nativized by hand
* [Rigetti: The Quil compiler](https://pyquil-docs.rigetti.com/en/stable/compiler.html): what quilc does, and qubit rewiring
* [Rigetti: The lifecycle of a program](https://docs.rigetti.com/qcs/guides/the-lifecycle-of-a-program): compilation vs translation on QCS
* [Rigetti: TranslationOptions](https://docs.rs/qcs/latest/qcs/qpu/translation/struct.TranslationOptions.html): the full set of translation options
* [quilc](https://github.com/quil-lang/quilc): the open-source Quil compiler
# OQCProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/oqc
Runtime integration for streamlined access to OQC simulators and QPUs.
API Reference:
[qbraid.runtime.oqc](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.oqc.html)
This page offers a high-level guide on using qBraid's `OQCProvider`, `OQCDevice`, and `OQCJob` classes to submit and manage jobs on Oxford Quantum Circuits (OQC) QPUs and simulators.
## Installation & Setup
To interface with OQC devices, install the `oqc` extra:
```bash theme={"dark"}
pip install 'qbraid[oqc]'
```
You wil also need an [OQC access token](https://docs.oqc.app/account_management.html#access-tokens) to authenticate your connection to OQC's cloud services.
To obtain an access token, log in or create an account at [https://cloud.oqc.app/](https://cloud.oqc.app/), and follow the instructions on the "My Account" page to "Add Auth Token".
## Initializing the Provider
Begin by importing the `OQCProvider` and initializing it with your API token.
This token authenticates your access to the OQC cloud platform:
```python theme={"dark"}
from qbraid.runtime import OQCProvider
provider = OQCProvider("")
```
By default, the endpoint URL is set to `"https://cloud.oqc.app/"`. If needed, you can specify a custom endpoint:
```python theme={"dark"}
provider = OQCProvider(token="", url="https://example.cloud.oqc.app/")
```
## Listing Available Devices
Fetch a list of devices accessible through the provider. This provides an overview of the QPUs and simulators available to you:
```python theme={"dark"}
provider.get_devices()
# [,
# ]
```
## Accessing Device Details
Retrieve a specific device using its unique ID and examine details such as its profile, status, and scheduling information:
```python theme={"dark"}
device = provider.get_device("qpu:jp:3:673b1ad43c")
print(f"Profile: {device.profile}")
print(f"Status: {device.status().name}")
if not device.profile.simulator:
print(f"Queue Depth: {device.queue_depth()}")
print(f"Next Window: {device.get_next_window()}")
print(f"All Metadata: {device.metadata()}")
```
## Metrics, Optimizations, and Other Configuration Options
Tune your job submission with a variety of parameters, including shot count, repetition period, result formatting, and program optimizations.
These options allow precise control over job execution and output. Below is a detailed breakdown of each parameter:
* `shots`: The number of times the circuit is ran. The default value is 1000.
* `repetition_period`: The length of time in between shots. The default value is 200e-6 seconds and OQC strongly recommends that you use the default unless you thoroughly understand the potential impact.
* `results_format`: The two options for this are `'raw'` and `'binary'`.
* `metrics`: The type of metrics you want to be recorded. The only metrics related to a task are its optimized circuit and the corresponding instruction count. So, there are four options:
1. `'default'`: Returns both metrics.
2. `'empty'`: Returns no metrics.
3. `'optimized_circuit'`: Returns the optimized qasm2 circuit.
4. `'optimized_instruction_count'`: Returns the instruction count of the optimized circuit.
* `optimizations`: These are the optimizations you want to perform on the input program(s). OQC uses the [tKet optimization compiler](https://oqc.tech/oqc-integrates-tket), as it is integrated into their compiler pipeline. Documention on these are in the [pytket.passes documentation](https://tket.quantinuum.com/api-docs/passes.html), and below are the instructions to access them through `OQCDevice.run()`:
| Option | TKET pass |
| :----------------------------- | :------------------------------------ |
| `'clifford_simplify'` | `CliffordSimp` |
| `'context_simplify'` | `ContextSimp` |
| `'decompose_controlled_gates'` | `DecomposeArbitrarilyControlledGates` |
| `'default_mapping_pass'` | `DefaultMappingPass` |
| `'empty'` | `Empty` |
| `'full_peephole_optimise'` | `FullPeepholeOptimise` |
| `'globalise_phased_x'` | `GlobalisePhasedX` |
| `'kak_decomposition'` | `KAKDecomposition` |
| `'one'` | `One` |
| `'peephole_optimise_2q'` | `PeepholeOptimise2Q` |
| `'remove_barriers'` | `RemoveBarriers` |
| `'remove_discarded'` | `RemoveDiscarded` |
| `'remove_redundancies'` | `RemoveRedundancies` |
| `'simplify_measured'` | `SimplifyMeasured` |
| `'three_qubit_squash'` | `ThreeQubitSquash` |
| `'two'` | `Two` |
## Program Submission Specifications and Device Compatibility
* Any program type compatible with OpenQASM 3 (i.e. can be converted to OpenQASM 3 via `qbraid.transpile(program, "qasm3")`) can be passed directly to the `OQCDevice.run()` method.
* All `include` statements will be removed from transpiled OpenQASM 3 program upon submission, as they are not recognized by OQC.
* The default supported gates for all OQC devices include the entire OpenQASM 3 `stdgates.inc` gate set plus the `ecr` gate.
* For QPUs, submissions do not need to use "native gates."
* The Lucy Simulator is noiseless and does not support error mitigation.
* OpenQASM 3 programs with classical control operations are *not supported*.
* OpenQASM 3 programs containing pulse-level descriptions of gates and measurements (e.g. OpenPulse grammar) are *supported*.
## Bell Circuit Job Example
Submit an OpenQASM 3 bell circuit to the Lucy Simulator device:
```python theme={"dark"}
from qbraid.runtime import OQCProvider
# Token loaded automatically from "OQC_AUTH_TOKEN" environment variable
provider = OQCProvider()
device = provider.get_device("qpu:uk:2:d865b5a184")
program = """
OPENQASM 3;
qubit[2] q;
bit[2] c;
h q[0];
h q[1];
cx q[0], q[1];
c = measure q;
"""
job = device.run(
program,
shots=1000,
repetition_period=100e-6,
metrics='optimized_circuit',
optimizations='default_mapping_pass'
)
print(job.id)
```
## Monitor Job Status
Inspect the job status, metrics, timings, and other metadata:
```python theme={"dark"}
>>> job.status()
>>> job.metadata()
{'status': ,
'allow_support_access': False,
'created_at': 'Wed, 27 Nov 2024 18:32:46 GMT',
'qpu_id': 'qpu:uk:2:d865b5a184',
'tag': None,
'shots': 1000,
'repetition_period': 0.0001,
'results_format': 'binary',
'metrics': 'optimized_circuit',
'active_calibrations': [],
'optimizations': 'default_mapping_pass',
'error_mitigation': None,
'errors': None,
'timings': {'RECEIVER_DEQUEUED': '2024-11-27 18:32:47.142509+00:00',
'RECEIVER_ENQUEUED': '2024-11-27 18:32:47.371945+00:00',
'RECEIVER_FROM_COMPILER': '2024-11-27 18:32:47.371836+00:00',
'RECEIVER_TO_COMPILER': '2024-11-27 18:32:47.142769+00:00',
'SERVER_DEQUEUED': '2024-11-27 18:32:47.384944+00:00',
'SERVER_ENQUEUED': '2024-11-27 18:32:47.123478+00:00',
'SERVER_RECEIVED': '2024-11-27 18:32:47.079319+00:00'}}
>>> job.metrics()
{'optimized_circuit': 'OPENQASM 3;\nqubit[2] q;\nbit[2] c;\nh q[0];\nh q[1];\ncx q[0], q[1];\nc = measure q;\n',
'optimized_instruction_count': None}
```
## Gather Job Results
Retrieve and analyze the results of your job:
```python theme={"dark"}
result = job.result()
result.data.get_counts()
# {'00': 250, '01': 235, '10': 264, '11': 251}
```
To plot the histogram counts and probability distribution, refer to the [Plot Experimental Results](/v2/sdk/user-guide/visualization#plot-experimental-results) section in the visualization guide.
# OriginProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/origin
Runtime integration for access to OriginQ quantum simulators and processors via QCloud.
API Reference:
[qbraid.runtime.origin](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.origin.html)
## Overview
The `qbraid.runtime.OriginProvider` provides support for OriginQ's quantum computing systems
through the [OriginQ QCloud](https://qcloud.originqc.com.cn/) platform. This means you can write quantum programs using
[PyQPanda3](https://pyqpanda-toturial.readthedocs.io/), and run them on OriginQ's cloud-based simulators and quantum processors,
all from within the [qBraid Runtime framework](/v2/sdk/user-guide/runtime/components).
## Getting started
Before you begin, make sure you have:
1. An **OriginQ QCloud** account and API key.
2. Python >= 3.10
### Set up the qBraid-SDK
Install qBraid with the `origin` extra from [PyPI](https://pypi.org/project/qbraid/) using pip:
```bash theme={"dark"}
pip install 'qbraid[origin]'
```
This installs the required dependency: `pyqpanda3`.
*Note*: The qBraid-SDK requires Python 3.10 or greater. You can check your
Python version by running `python --version` from the command line.
## Authentication
By default, qBraid will look for an environment variable named `ORIGIN_API_KEY`.
Set it from your command line:
```bash theme={"dark"}
export ORIGIN_API_KEY="your_api_key_here"
```
Alternatively, you can pass your API key directly when creating the provider:
```python theme={"dark"}
from qbraid.runtime.origin import OriginProvider
provider = OriginProvider(api_key="your_api_key_here")
```
In the examples below, we initialize `OriginProvider()` with no arguments and assume that qBraid
will automatically find your API key from the environment.
## List available devices
Use the `OriginProvider` to list all devices to which you have access:
```python theme={"dark"}
from qbraid.runtime.origin import OriginProvider
provider = OriginProvider()
devices = provider.get_devices()
print(devices)
```
You can also filter to show only hardware (QPU) devices:
```python theme={"dark"}
devices = provider.get_devices(hardware_only=True)
```
Get a specific device by its ID:
```python theme={"dark"}
device = provider.get_device("full_amplitude")
print(device.status())
#
```
### Available simulators
OriginQ provides several cloud-based quantum simulators:
| Simulator ID | Max Qubits | Description |
| ------------------- | ---------- | ----------------------------- |
| `full_amplitude` | 35 | Full amplitude simulation. |
| `partial_amplitude` | 68 | Partial amplitude simulation. |
| `single_amplitude` | 200 | Single amplitude simulation. |
## Submitting jobs
The `OriginProvider` accepts quantum programs as PyQPanda3 `QProg` objects.
### Create a program
```python theme={"dark"}
from pyqpanda3.core import QProg, H, CNOT, measure
# Bell state circuit
prog = QProg(2)
q = prog.qubits()
prog << H(q[0]) << CNOT(q[0], q[1]) << measure(0, 0) << measure(1, 1)
```
### Run a job
Use `device.run()` to submit a program:
```python theme={"dark"}
from pyqpanda3.core import QProg, H, CNOT, measure
from qbraid.runtime.origin import OriginProvider
provider = OriginProvider()
device = provider.get_device("full_amplitude")
prog = QProg(2)
q = prog.qubits()
prog << H(q[0]) << CNOT(q[0], q[1]) << measure(0, 0) << measure(1, 1)
job = device.run(prog, shots=1000)
print(f"Job ID: {job.id}")
```
### Batch submission
Submit multiple programs in a single call:
```python theme={"dark"}
from pyqpanda3.core import QProg, H, X, measure
from qbraid.runtime.origin import OriginProvider
provider = OriginProvider()
device = provider.get_device("full_amplitude")
prog1 = QProg(1)
prog1 << H(prog1.qubits()[0]) << measure(0, 0)
prog2 = QProg(1)
prog2 << X(prog2.qubits()[0]) << measure(0, 0)
jobs = device.run([prog1, prog2], shots=500)
for job in jobs:
print(f"Job ID: {job.id}")
```
For simulator backends, batch inputs are submitted as individual jobs. For QPU
backends, batch inputs are submitted as a single job using the native batch
API.
## Retrieving results
```python theme={"dark"}
result = job.result()
# Measurement probabilities
print(result.data.get_probabilities())
# {'00': 0.5, '11': 0.5}
# Measurement counts (when available)
print(result.data.measurement_counts)
# Job metadata
print(f"Device: {result.device_id}")
print(f"Job ID: {result.job_id}")
print(f"Success: {result.success}")
```
Depending on the backend, results may be returned as probabilities,
measurement counts, or both. Use `result.data.get_probabilities()` to retrieve
probability distributions and `result.data.measurement_counts` for shot-based
counts.
### Check job status
```python theme={"dark"}
from qbraid.runtime.enums import JobStatus
status = job.status()
print(status)
#
```
Job cancellation is not supported by the OriginQ provider. Calling
`job.cancel()` will raise an error.
## Full example
A complete end-to-end workflow targeting the full amplitude simulator:
```python theme={"dark"}
import os
from pyqpanda3.core import QProg, H, CNOT, measure
from qbraid.runtime.origin import OriginProvider
# 1. Set credentials (or export as env var beforehand)
os.environ["ORIGIN_API_KEY"] = "your_api_key_here"
# 2. Initialize provider
provider = OriginProvider()
# 3. Get the target device
device = provider.get_device("full_amplitude")
print(f"Device status: {device.status()}")
# 4. Define a GHZ state circuit
prog = QProg(3)
q = prog.qubits()
prog << H(q[0]) << CNOT(q[0], q[1]) << CNOT(q[1], q[2])
prog << measure(0, 0) << measure(1, 1) << measure(2, 2)
# 5. Submit the job
job = device.run(prog, shots=1000)
print(f"Submitted job: {job.id}")
# 6. Retrieve results
result = job.result()
print(f"Probabilities: {result.data.get_probabilities()}")
# Expected output (approximate): {'000': 0.5, '111': 0.5}
```
## Related links
* [OriginQ QCloud Platform](https://qcloud.originqc.com.cn/)
* [PyQPanda3 Documentation](https://pyqpanda-toturial.readthedocs.io/)
# PasqalProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/pasqal
Runtime integration for running Pulser sequences on Pasqal neutral-atom QPUs and emulators.
API Reference:
[qbraid.runtime.pasqal](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.pasqal.html)
## Overview
The `qbraid.runtime.PasqalProvider` provides support for Pasqal's neutral-atom quantum computers and emulators via
[Pasqal Cloud Services](https://docs.pasqal.com/cloud/). Unlike gate-model providers, Pasqal devices run *analog*
quantum programs: pulse sequences built with [Pulser](https://pulser.readthedocs.io/), Pasqal's open-source framework
for programming neutral-atom arrays. You write a `pulser.Sequence`, and qBraid serializes it, submits it as a Pasqal
Cloud batch, and returns measurement counts—all from within the
[qBraid Runtime framework](/v2/sdk/user-guide/runtime/components).
## Getting started
Before you begin, you'll need a [Pasqal Cloud](https://portal.pasqal.cloud/) account and a **project ID**. Jobs
(batches) are billed and organized per project—you can find your project ID in the Pasqal Cloud portal.
### Set up the qBraid-SDK
Install qBraid with the `pasqal` extra from [PyPI](https://pypi.org/project/qbraid/) using pip:
```bash theme={"dark"}
pip install 'qbraid[pasqal]'
```
This installs the [pasqal-cloud](https://github.com/pasqal-io/pasqal-cloud) client and
[pulser-core](https://pypi.org/project/pulser-core/) alongside qBraid.
*Note*: The qBraid-SDK requires Python 3.10 or greater. You can check your
Python version by running `python --version` from the command line.
We encourage doing this inside an environment management system, such as [virtualenv](https://virtualenv.pypa.io/en/latest/) or
[conda](https://docs.conda.io/en/latest/). Alternatively, you can bypass this step by using a pre-configured
[qBraid Lab environment](/v2/lab/user-guide/environments).
See [qBraid-SDK installation and setup](/v2/sdk/user-guide/overview#installation-and-setup) for more.
### Set up your environment
By default, qBraid will look in your local environment for variables named `PASQAL_USERNAME`, `PASQAL_PASSWORD`,
and `PASQAL_PROJECT_ID`:
```bash theme={"dark"}
export PASQAL_USERNAME="your_username_here"
export PASQAL_PASSWORD="your_password_here"
export PASQAL_PROJECT_ID="your_project_id_here"
```
Alternatively, you can pass your credentials explicitly when creating the provider object:
```python theme={"dark"}
from qbraid.runtime import PasqalProvider
provider = PasqalProvider(
username="me@example.com",
password="...",
project_id="...",
)
```
If you provide a `username` but leave `password` unset, `pasqal-cloud` will prompt for it interactively. For
machine-to-machine authentication with a pre-issued token, you can pass a custom `pasqal_cloud.TokenProvider`
via the `token_provider` argument instead.
In the examples below, we show `PasqalProvider()` initialized with no arguments and assume that qBraid will
automatically find your credentials in the environment.
## List available devices
Use the `PasqalProvider` to list the available Pasqal devices:
```python theme={"dark"}
from qbraid.runtime import PasqalProvider
provider = PasqalProvider()
devices = provider.get_devices()
```
Running this script should print something like this:
```python theme={"dark"}
[,
,
,
,
,
,
]
```
If this works correctly, then your qBraid-SDK installation is correct and your Pasqal credentials are valid!
Device IDs starting with `EMU_` are emulators; the rest are QPUs:
| Device ID | Type | Description |
| -------------- | -------- | --------------------------------------------------- |
| `FRESNEL` | QPU | Pasqal's neutral-atom quantum processor |
| `FRESNEL_CAN1` | QPU | FRESNEL-class QPU (Canada) |
| `EMU_FREE` | Emulator | Free-tier emulator (limited resources) |
| `EMU_FRESNEL` | Emulator | Emulates the FRESNEL QPU, including its constraints |
| `EMU_SV` | Emulator | State-vector emulator |
| `EMU_MPS` | Emulator | Matrix-product-state emulator |
| `EMU_TN` | Emulator | Tensor-network emulator |
You can confirm whether a device is a simulator, and inspect its runtime profile, directly from the device object:
```python theme={"dark"}
device = provider.get_device("EMU_FREE")
print(device.profile.simulator) # True
print(device.profile.experiment_type) # ExperimentType.ANALOG
```
## Build a Pulser sequence
Pasqal devices execute Pulser sequences rather than gate-model circuits. A sequence specifies a *register* (the
positions of the atoms, in µm) and a series of *pulses* applied through the device's channels. For an introduction,
see the [Pulser documentation](https://pulser.readthedocs.io/).
Here, we place two atoms 10 µm apart and drive them with a single constant global pulse:
```python theme={"dark"}
from pulser import Pulse, Register, Sequence
from pulser.devices import AnalogDevice
register = Register({"q0": (0, 0), "q1": (0, 10)})
sequence = Sequence(register, AnalogDevice)
sequence.declare_channel("rydberg_global", "rydberg_global")
pulse = Pulse.ConstantPulse(duration=1000, amplitude=5.0, detuning=0, phase=0)
sequence.add(pulse, "rydberg_global")
sequence.measure("ground-rydberg")
```
Remember to end your sequence with a `measure()` call. Measurement in the
`"ground-rydberg"` basis maps each atom to a classical bit: `1` if the atom
was excited to the Rydberg state, `0` otherwise.
## Submit a sequence to an emulator
Let's run the sequence on `EMU_FREE` with 100 shots:
```python theme={"dark"}
from qbraid.runtime import PasqalProvider
provider = PasqalProvider()
device = provider.get_device("EMU_FREE")
job = device.run(sequence, shots=100)
print(job.id) # the Pasqal Cloud batch ID, e.g. 'c2bbfadf-e2d1-...'
print(job.status()) # JobStatus.QUEUED
```
Each submission creates a Pasqal Cloud *batch*, and `job.id` is the batch ID—you can use it to look the job up
later, both through qBraid and in the Pasqal Cloud portal. Calling `job.result()` blocks until the batch reaches
a terminal state, then returns the measurement counts:
```python theme={"dark"}
result = job.result()
print(result.data.get_counts())
```
This returns a dictionary of bitstring counts, for example:
```
{'00': 38, '11': 13, '10': 29, '01': 20}
```
Since this is an analog experiment, the result data is a qBraid `AnalogResultData` instance, keyed by
measurement bitstrings in register order (`q0`, `q1`, ...).
## Submit a multi-sequence batch
You can submit several sequences in a single batch by passing a list to `device.run()`. Each sequence becomes its
own job within the Pasqal Cloud batch, and the results are returned as a list of counts dictionaries in
submission order. Given two sequences `sequence_a` and `sequence_b`, built as in the example above:
```python theme={"dark"}
batch_job = device.run([sequence_a, sequence_b], shots=50)
batch_result = batch_job.result()
counts_list = batch_result.data.get_counts()
print("\n".join(f"Sequence {i}: {c}" for i, c in enumerate(counts_list)))
```
```
Sequence 0: {'10': 12, '00': 19, '01': 14, '11': 5}
Sequence 1: {'10': 11, '00': 19, '11': 7, '01': 13}
```
## Submit a sequence to a QPU
Submitting to a QPU follows the exact same pattern—just use a QPU device ID:
```python theme={"dark"}
device = provider.get_device("FRESNEL")
job = device.run(sequence, shots=100)
```
Build your sequence against a Pulser device specification that matches the
target QPU's constraints (register geometry, channels, pulse limits), and test
it on an emulator first—`EMU_FRESNEL` mirrors the FRESNEL QPU's constraints.
QPU access is subject to your Pasqal Cloud project's permissions and quota.
QPU jobs may wait in the queue, so record `job.id` and retrieve the job later rather than blocking on
`job.result()`.
## Manage jobs
### Retrieve a job
You can retrieve a previously submitted job from its batch ID:
```python theme={"dark"}
from qbraid.runtime import PasqalJob, PasqalProvider
provider = PasqalProvider()
job = PasqalJob("your_batch_id_here", sdk=provider.sdk)
print(job.status())
result = job.result()
print(result.data.get_counts())
```
### Cancel a job
You can cancel a job (batch) while it's waiting in the queue:
```python theme={"dark"}
job.cancel()
```
## Visualize results
To plot the results of a job, first, install the qBraid visualization extra:
```bash theme={"dark"}
pip install 'qbraid[visualization]'
```
You can then visualize the measurement counts using `plot_histogram`, or display the probability distribution
with `plot_distribution`:
```python theme={"dark"}
from qbraid.visualization import plot_histogram
counts = result.data.get_counts()
plot_histogram(counts)
```
The "counts" input may be either a single dictionary or a list of dictionaries for multi-sequence batches.
See [Plot Experimental Results](/v2/sdk/user-guide/visualization#plot-experimental-results) for more.
***
## Additional resources
*Great work!* You successfully ran your first analog quantum program - *what next?*
Explore more resources for using qBraid:
* [API Reference](https://qbraid.github.io/qBraid/)
* [Source Code on GitHub](https://github.com/qBraid/qBraid)
* [Example Notebooks on GitHub](https://github.com/qBraid/qbraid-lab-demo)
And for going deeper with Pasqal and Pulser:
* [Pasqal Runtime Provider Notebook on GitHub](https://github.com/qBraid/qbraid-lab-demo/blob/main/Provider-Jobs/qbraid_runtime_pasqal_provider.ipynb)
* [Pulser on Pasqal Devices Notebook on GitHub](https://github.com/qBraid/qbraid-lab-demo/blob/main/qBraid-Runtime/qbraid_pulser_pasqal.ipynb)
* [Pasqal Cloud documentation](https://docs.pasqal.com/cloud/)
* [Pulser documentation](https://pulser.readthedocs.io/)
* [pasqal-cloud on GitHub](https://github.com/pasqal-io/pasqal-cloud)
# QuantinuumProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/quantinuum
Runtime integration for access to Quantinuum trapped-ion quantum processors via Quantinuum Nexus.
API Reference:
[qbraid.runtime.quantinuum](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.quantinuum.html)
## Overview
The `qbraid.runtime.QuantinuumProvider` provides support for Quantinuum's trapped-ion quantum systems
through the [Quantinuum Nexus](https://nexus.quantinuum.com/) platform. This means you can write quantum circuits using
[pytket](https://tket.quantinuum.com/), compile and execute them on Quantinuum's H-series processors and emulators,
all from within the [qBraid Runtime framework](/v2/sdk/user-guide/runtime/components).
## Getting started
Before you begin, make sure you have:
1. A **Quantinuum Nexus** account with valid credentials.
2. The **qnexus** CLI tool installed and authenticated (see [Authentication](#authentication) below).
3. Python >= 3.10
### Set up the qBraid-SDK
Install qBraid with the `quantinuum` extra from [PyPI](https://pypi.org/project/qbraid/) using pip:
```bash theme={"dark"}
pip install 'qbraid[quantinuum]'
```
This installs the required dependencies: `pytket`, `pytket-quantinuum`, and `qnexus`.
*Note*: The qBraid-SDK requires Python 3.10 or greater. You can check your
Python version by running `python --version` from the command line.
## Authentication
The `QuantinuumProvider` authenticates through the `qnexus` library. Before using the provider, you must
log in using the `qnexus` CLI:
```bash theme={"dark"}
qnx login
```
This opens a browser-based authentication flow and stores your credentials locally. Once authenticated,
the provider will automatically use your stored credentials — no API keys or environment variables are needed.
Then initialize the provider:
```python theme={"dark"}
from qbraid.runtime.quantinuum import QuantinuumProvider
provider = QuantinuumProvider()
```
## List available devices
Use the `QuantinuumProvider` to list all devices to which you have access:
```python theme={"dark"}
from qbraid.runtime.quantinuum import QuantinuumProvider
provider = QuantinuumProvider()
devices = provider.get_devices()
print(devices)
```
Get a specific device by its ID:
```python theme={"dark"}
device = provider.get_device("H1-1")
print(device.status())
#
```
Devices with an "E" suffix (e.g., `H1-1E`) are emulators (simulators) that
model the behavior of the corresponding hardware device.
## Submitting jobs
The `QuantinuumProvider` accepts quantum programs written as pytket `Circuit` objects.
### Create a circuit
```python theme={"dark"}
from pytket.circuit import Circuit
# Bell state circuit
circuit = Circuit(2)
circuit.H(0)
circuit.CX(0, 1)
circuit.measure_all()
```
### Run a job
Use `device.run()` to compile and submit a circuit:
```python theme={"dark"}
from pytket.circuit import Circuit
from qbraid.runtime.quantinuum import QuantinuumProvider
provider = QuantinuumProvider()
device = provider.get_device("H1-1E")
circuit = Circuit(2)
circuit.H(0)
circuit.CX(0, 1)
circuit.measure_all()
job = device.run(circuit, shots=1000)
print(f"Job ID: {job.id}")
```
### Batch submission
Submit multiple circuits in a single call. All circuits are bundled into one Nexus job:
```python theme={"dark"}
from pytket.circuit import Circuit
from qbraid.runtime.quantinuum import QuantinuumProvider
provider = QuantinuumProvider()
device = provider.get_device("H1-1E")
circuits = [
Circuit(1).H(0).measure_all(),
Circuit(1).X(0).measure_all(),
Circuit(2).H(0).CX(0, 1).measure_all(),
]
job = device.run(circuits, shots=500)
print(f"Job ID: {job.id}")
```
When submitting a list of circuits, all circuits are compiled and executed as
a single batch job on Quantinuum Nexus. The returned `measurement_counts` will
be a list of dictionaries, one per circuit.
## Retrieving results
```python theme={"dark"}
result = job.result()
# Measurement counts
print(result.data.measurement_counts)
# {'00': 512, '11': 488}
# Job metadata
print(f"Device: {result.device_id}")
print(f"Job ID: {result.job_id}")
print(f"Success: {result.success}")
```
### Check job status
```python theme={"dark"}
from qbraid.runtime.enums import JobStatus
status = job.status()
print(status)
#
```
### Cancel a job
```python theme={"dark"}
job.cancel()
```
Cancellation is best-effort. Jobs already in a terminal state (`COMPLETED`,
`FAILED`, `CANCELLED`) cannot be cancelled.
### Execution time
You can retrieve the wall-clock execution time (in seconds) for a completed job:
```python theme={"dark"}
print(f"Execution time: {job.execution_time_s()} seconds")
```
## Configuration options
The `device.run()` method accepts additional keyword arguments to control compilation and project scoping:
| Parameter | Type | Default | Description |
| -------------------- | ----- | ---------- | ------------------------------------------------- |
| `shots` | `int` | `1000` | Number of measurement shots per circuit. |
| `project_name` | `str` | `"qbraid"` | Nexus project name to scope compile/execute jobs. |
| `optimisation_level` | `int` | `1` | pytket optimization level (0-2). |
The `project_name` and `optimisation_level` parameters can also be set via environment variables:
| Variable | Description |
| ------------------------------- | ---------------------------------- |
| `QUANTINUUM_NEXUS_PROJECT_NAME` | Default Nexus project name. |
| `QUANTINUUM_NEXUS_OPT_LEVEL` | Default pytket optimization level. |
## Full example
A complete end-to-end workflow targeting a Quantinuum emulator:
```python theme={"dark"}
from pytket.circuit import Circuit
from qbraid.runtime.quantinuum import QuantinuumProvider
# 1. Initialize provider (assumes qnx login was run)
provider = QuantinuumProvider()
# 2. Get an emulator device
device = provider.get_device("H1-1E")
print(f"Device status: {device.status()}")
# 3. Define a GHZ state circuit
circuit = Circuit(3)
circuit.H(0)
circuit.CX(0, 1)
circuit.CX(1, 2)
circuit.measure_all()
# 4. Submit the job
job = device.run(circuit, shots=1000, optimisation_level=2)
print(f"Submitted job: {job.id}")
# 5. Retrieve results
result = job.result()
print(f"Counts: {result.data.measurement_counts}")
# Expected output (approximate): {'000': ~500, '111': ~500}
```
## Related links
* [Quantinuum Nexus](https://nexus.quantinuum.com/)
* [pytket Documentation](https://tket.quantinuum.com/)
* [pytket-quantinuum Extension](https://tket.quantinuum.com/extensions/pytket-quantinuum/)
# RigettiProvider
Source: https://docs.qbraid.com/v2/sdk/user-guide/providers/rigetti
Runtime integration for direct access to Rigetti quantum processors via QCS.
API Reference:
[qbraid.runtime.rigetti](https://qbraid.github.io/qBraid/stubs/qbraid.runtime.rigetti.html)
On-demand access to Rigetti quantum systems (Ankaa, Cepheus) is restricted.
You must contact [Rigetti](https://www.rigetti.com/contact) to request access
credentials before using this provider.
## Prerequisites
Before getting started, ensure you have:
1. A **Rigetti QCS account** with an active refresh token (contact Rigetti to obtain access).
2. The **Forest SDK** installed locally — specifically the `quilc` compiler. The `qvm` simulator is optional.
3. Python > 3.10 and \< 3.13
### Install the Forest SDK
The `quilc` compiler is required to compile Quil programs into the native gate set of the target QPU. Install it for your platform:
* **macOS / Linux**: Download from [qcs.rigetti.com/sdk-downloads](https://qcs.rigetti.com/sdk-downloads)
* **Docker**: `docker run --rm -p 5555:5555 rigetti/quilc -S`
Verify the installation:
```bash theme={"dark"}
quilc --version
```
## Installation & Setup
Install the `rigetti` extra:
```bash theme={"dark"}
pip install 'qbraid[rigetti]'
```
This installs the required dependencies: `pyquil` and `qcs-sdk-python`.
## Authentication
The `RigettiProvider` authenticates with Rigetti QCS using an **OAuth refresh token**. Set it as an environment variable (required):
```bash theme={"dark"}
export RIGETTI_REFRESH_TOKEN="your-refresh-token"
```
Then initialize the provider:
```python theme={"dark"}
from qbraid.runtime.rigetti import RigettiProvider
provider = RigettiProvider()
```
### Optional Environment Variables
| Variable | Description | Default |
| -------------------- | ----------------------- | ------------------------------ |
| `RIGETTI_CLIENT_ID` | Custom OAuth client ID | Rigetti default |
| `RIGETTI_ISSUER` | Custom OAuth issuer URL | Rigetti default |
| `QCS_GRPC_ENDPOINT` | QCS gRPC API endpoint | `https://grpc.qcs.rigetti.com` |
| `QCS_QUILC_ENDPOINT` | quilc compiler endpoint | `tcp://127.0.0.1:5555` |
| `QCS_QVM_ENDPOINT` | QVM simulator endpoint | `http://127.0.0.1:5000` |
### Provider Setup
After initializing the provider, call `setup()` to manage the local quilc compiler process. This ensures quilc is running and ready to compile programs:
```python theme={"dark"}
provider = RigettiProvider()
# Start a local quilc process (default behavior)
provider.setup()
# Or, point to an existing quilc endpoint
provider.setup(quilc_endpoint="tcp://my-quilc-host:5555")
```
The `setup()` method accepts the following keyword arguments:
| Parameter | Type | Default | Description |
| ---------------- | ------ | ------- | ------------------------------------------------------- |
| `quilc_endpoint` | `str` | `None` | Pre-existing quilc endpoint. Skips local process start. |
| `qvm_endpoint` | `str` | `None` | Pre-existing QVM endpoint. Skips local process start. |
| `grpc_endpoint` | `str` | `None` | Override for the QCS gRPC endpoint. |
| `start_quilc` | `bool` | `True` | Auto-start local quilc if not already running. |
| `start_qvm` | `bool` | `False` | Auto-start local QVM if not already running. |
## Getting Available Devices
List all quantum processors available through your QCS account:
```python theme={"dark"}
from qbraid.runtime.rigetti import RigettiProvider
provider = RigettiProvider()
provider.setup()
devices = provider.get_devices()
print(devices)
# [RigettiDevice('ankaa-3'), RigettiDevice('cepheus-108')]
```
Get a specific device by its processor ID:
```python theme={"dark"}
device = provider.get_device("ankaa-3")
print(device.status())
#
# Get the list of live qubit IDs on the device
qubits = device.live_qubits()
print(f"Live qubits ({len(qubits)}): {qubits[:10]}...")
```
## Submitting Jobs
The `RigettiProvider` accepts programs written in [Quil](https://quil-lang.github.io/) using the `pyquil` library. Programs are compiled via `quilc` before submission to the QPU.
### Create a Program
```python theme={"dark"}
from pyquil import Program
# Bell state circuit
program = Program("""
DECLARE ro BIT[2]
H 0
CNOT 0 1
MEASURE 0 ro[0]
MEASURE 1 ro[1]
""")
```
### Run a Job
Use `device.run()` to compile and submit in one step:
```python theme={"dark"}
from pyquil import Program
from qbraid.runtime.rigetti import RigettiProvider
provider = RigettiProvider()
provider.setup()
device = provider.get_device("ankaa-3")
program = Program("""
DECLARE ro BIT[2]
H 0
CNOT 0 1
MEASURE 0 ro[0]
MEASURE 1 ro[1]
""")
job = device.run(program, shots=1000)
print(f"Job ID: {job.id}")
```
### Batch Submission
Submit multiple programs in a single call:
```python theme={"dark"}
from pyquil import Program
from qbraid.runtime.rigetti import RigettiProvider
provider = RigettiProvider()
provider.setup()
device = provider.get_device("ankaa-3")
programs = [
Program("DECLARE ro BIT[1]\nH 0\nMEASURE 0 ro[0]"),
Program("DECLARE ro BIT[1]\nX 0\nMEASURE 0 ro[0]"),
Program("DECLARE ro BIT[1]\nH 0\nS 0\nMEASURE 0 ro[0]"),
]
jobs = device.run(programs, shots=500)
for job in jobs:
print(f"Job ID: {job.id}")
```
## Retrieving Results
```python theme={"dark"}
result = job.result()
# Measurement counts as a dictionary
print(result.data.measurement_counts)
# {'00': 512, '11': 488}
# Job metadata
print(f"Device: {result.device_id}")
print(f"Job ID: {result.job_id}")
print(f"Success: {result.success}")
print(f"Duration (us): {result.execution_duration_microseconds}")
```
### Check Job Status
```python theme={"dark"}
from qbraid.runtime.enums import JobStatus
status = job.status()
print(status)
#
```
### Cancel a Job
```python theme={"dark"}
job.cancel()
```
Cancellation is best-effort. Jobs already in a terminal state (`COMPLETED`,
`FAILED`, `CANCELLED`) cannot be cancelled.
## Runtime Options
The `device.run()` method accepts an optional `runtime_options` dict to control compilation
and translation behavior:
```python theme={"dark"}
job = device.run(
program,
shots=1000,
runtime_options={
"compiler_timeout": 60,
"prepend_default_calibrations": True,
"passive_reset_delay_seconds": 1.0,
},
)
```
### Options Reference
| Parameter | Type | Description |
| ------------------------------------ | ------- | ------------------------------------------------------------------------------ |
| `compiler_timeout` | `float` | quilc compilation timeout in seconds. |
| `prepend_default_calibrations` | `bool` | Prepend default calibration pulses during translation. |
| `passive_reset_delay_seconds` | `float` | Delay between passive resets. |
| `allow_unchecked_pointer_arithmetic` | `bool` | Disable runtime memory bounds checking (authorized access). |
| `allow_frame_redefinition` | `bool` | Allow frame definitions that differ from Rigetti defaults (authorized access). |
## Full Example
A complete end-to-end workflow targeting the Ankaa-3 processor:
```python theme={"dark"}
import os
from pyquil import Program
from qbraid.runtime.rigetti import RigettiProvider
# 1. Set credentials (or export as env var beforehand)
os.environ["RIGETTI_REFRESH_TOKEN"] = "your-refresh-token"
# 2. Initialize provider and start quilc
provider = RigettiProvider()
provider.setup()
# 3. Get the target device
device = provider.get_device("ankaa-3")
print(f"Device status: {device.status()}")
print(f"Number of qubits: {len(device.live_qubits())}")
# 4. Define a GHZ state circuit
program = Program("""
DECLARE ro BIT[3]
H 0
CNOT 0 1
CNOT 1 2
MEASURE 0 ro[0]
MEASURE 1 ro[1]
MEASURE 2 ro[2]
""")
# 5. Submit the job
job = device.run(program, shots=1000)
print(f"Submitted job: {job.id}")
# 6. Retrieve results
result = job.result()
print(f"Counts: {result.data.measurement_counts}")
# Expected output (approximate): {'000': ~500, '111': ~500}
```
## Related Links
* [Rigetti QCS Documentation](https://docs.rigetti.com/)
* [PyQuil Documentation](https://pyquil-docs.rigetti.com/)
* [Quil Language Specification](https://quil-lang.github.io/)
* [Forest SDK Downloads](https://qcs.rigetti.com/sdk-downloads)
# Runtime Components
Source: https://docs.qbraid.com/v2/sdk/user-guide/runtime/components
Universal building blocks for constructing client-side quantum software pipelines.
## Runtime Overview
The qBraid Runtime framework defines essential abstractions for sessions, providers, devices, jobs, and results,
integrating them into a complete client-side software pipeline. Leveraging device and domain-specific requirements
outlined by each provider, it generates coherent target "profiles," program "specs," and compilation "schemes".
These elements, referred to as Universal Pipeline Components (UPCs), form the foundational building blocks for constructing scalable
quantum software pipelines. Each UPC plays a unique and distinct role, utilizing quantum and/or classical resources. Collectively,
these components optimize the management of the entire quantum job lifecycle—from defining quantum programs to dispatching jobs to
remote servers to post-processing results—and ensure the efficient production of outputs for diverse computational tasks.
Distinguishing itself through a streamlined and highly-configurable approach to cross-platform integration,
qBraid Runtime does not adhere to a fixed circuit-building library, or quantum program representation. Instead,
it empowers providers to dynamically register any desired input program type as the target based on their specific needs.
This flexibility is extended by the framework's modular pipeline, which facilitates any number of additional program
validation, transpilation, and compilation steps.
Unlike existing runtime frameworks that focus their automation and abstractions primarily on quantum components—such as
adapting the input quantum program to the supported operations and connectivity of the target device—qBraid Runtime adds an
*extra* layer of functionalities. This layer considers the ultimate IR needed to encode the quantum program and securely submit
it to a remote API. By addressing the full scope of client-side software requirements necessary for secure submission and
management of quantum jobs, qBraid Runtime significantly streamlines the development process, enhancing the agility with which
quantum hardware providers can construct a complete stack and facilitate access to their devices.
## Building Pipelines
The `qbraid.runtime` module provides the following Universal Pipeline Components (UPCs):
* [TargetProfile](/v2/sdk/user-guide/runtime/new-provider#targetprofile): Specifies domain and device-specific runtime instructions to tailor quantum executions to particular hardware configurations.
* [QuantumProvider](/v2/sdk/user-guide/runtime/new-provider#quantumprovider): Converts device metadata into accessible Python objects, facilitating interaction with diverse quantum computing resources.
* [QuantumDevice](/v2/sdk/user-guide/runtime/new-provider#quantumdevice): Describes the unique parameters and operational settings necessary for executing quantum programs on specific hardware.
* [QuantumJob](/v2/sdk/user-guide/runtime/new-provider#quantumjob): Represents the transitional states of quantum programs, managing both ongoing and completed quantum computations.
* [Result](/v2/sdk/user-guide/visualization#plot-experimental-results): Captures and structures the output from quantum computations, including histograms and raw data, for analysis and interpretation.
In the subsequent sections, we will demonstrate how these UPCs enable quantum software and hardware providers to configure a
comprehensive client-side quantum software pipeline using just three essential REST API endpoints:
1. `GET` `/devices` (or similar) - To retrieve metadata about available quantum devices.
2. `POST` `/jobs` (or similar) - To submit a quantum job for execution on a specified device.
3. `GET` `/jobs` (or similar) - To retrieve the status and results of an executed quantum job.
## Quantum Job Submission Process
When a `QuantumDevice.run` method is called, the following steps are performed to process a quantum program and submit it as a job to a remote API:
1. **Transpile**: Performs a "program type conversion" according to the `ProgramSpec`(s) specified by the device's `TargetProfile`
2. **Transform**: Applies any device-specific passes (e.g., gate decompositions to map the circuit to a `TargetProfile.basis_gates` set).
3. **Validate**: Verify that the run input satisfies all criteria for submitting to the given device (e.g., ensuring the number of qubits in the circuit does not exceed the device's capacity).
4. **Prepare**: Serializes the run input or converts it to any lower-level IR required for submission to the remote API (e.g., string, JSON, or bytecode format).
5. **Submit**: Sends `POST` request (or equivalent) with program and all runtime parameters to the remote API, and returns a `QuantumJob` instance.
For a server-side view of how this flow integrates with the qBraid API, see the [API overview](/v2/api-reference/user-guide/overview#quantum-job-flow).
# Writing a New Provider
Source: https://docs.qbraid.com/v2/sdk/user-guide/runtime/new-provider
A step-by-step guide to implementing a new provider class.
## Overview
To integrate a quantum device or simulator with qBraid Runtime, you'll need to create a custom provider class. A provider includes a
collection of devices and runtime specifications. Each `QuantumProvider` should implement a `get_devices` method to retrieve
`QuantumDevice` objects, which describe the device's supported operations, the program types accepted as run input, and other
dynamic runtime instructions. Finally, devices must implement a `submit()` method to execute or
queue programs, returning a `QuantumJob` object. This standardization allows users and APIs to uniformly submit jobs and retrieve
results, ensuring compatibility across different providers and devices.
Here are the high-level components required to establish a complete runtime implementation:
Implement a `QuantumProvider` subclass that manages authentication and
remote access to the available device(s).
Implement a `QuantumDevice` subclass and its `submit()` method. Optionally
incorporate a custom `ConversionScheme` to broaden the range of quantum
program types that can be accepted as run input.
Implement a `QuantumJob` subclass that handles interactions with a running
job. Process and present data collected from job executions in unified
`Result` class.
## QuantumProvider
The provider class is responsible for managing interactions with various quantum services and for converting device metadata
into accessible Python objects.
For a simple provider implementation example, see qbraid.runtime.ionq.provider.py ↗
### Server Requests
Creating a provider class requires the following REST API endpoints:
1. `GET` `/devices` (or similar) - To retrieve metadata about available quantum devices.
2. `POST` `/job` (or similar) - To submit a quantum job for execution on a specified device.
3. `GET` `/job` (or similar) - To retrieve the status and results of an executed quantum job.
If you already have a Python client for interacting with your REST API server, you can skip this section and go directly to
[Provider Setup](/v2/sdk/user-guide/runtime/new-provider#provider-setup).
For those who need to set up a Python client, the `qbraid.runtime.Session`, a subclass of [requests.Session](https://requests.readthedocs.io/en/latest/user/advanced/#session-objects),
is designed to manage secure HTTP connections to custom endpoints. It ensures encrypted and authenticated communication for quantum
providers and offers customizations for management of headers and secret keys, configurable retries for `5xx` errors, and more.
Below is a minimal example demonstrating how one could set up authenticated requests to the previously mentioned endpoints:
```python theme={"dark"}
from typing import Any
from qbraid.runtime import Session
class MySession(Session):
def __init__(self, api_key: str):
super().__init__(
base_url="https://api.example.com/fake-endpoint",
headers={"Content-Type": "application/json"},
auth_headers={"apiKey": api_key},
)
self.api_key = api_key
def get_device(self, device_id: str) -> dict[str, Any]:
devices = self.get_devices(device_id=device_id)
if not devices:
raise ValueError(f"Device {device_id} not found")
return devices[0]
def get_devices(self, **kwargs) -> list[dict[str, Any]]:
return self.get("/devices", **kwargs).json()
def create_job(self, data: dict[str, Any]) -> dict[str, Any]:
return self.post("/jobs", json=data).json()
def get_job(self, job_id: str) -> dict[str, Any]:
return self.get(f"/jobs/{job_id}").json()
```
In the above example, `base_url` corresponds to your API endpoint. The qBraid `Session` class distinguishes between `headers`
and `auth_headers`, ensuring that values in `auth_headers` are masked in all responses to safeguard against the inadvertent
exposure of secret keys.
The qBraid `Session` class offers additional customizable options such as the total number of retries for requests, the number
of connection retries, the backoff factor between retry attempts, and more. For further details, refer to the linked
API Reference below.
API Reference:
qbraid.runtime.Session ↗
### TargetProfile
The `qbraid.runtime.TargetProfile` encapsulates the configuration settings and runtime protocol(s) for a quantum device, presenting
them as a read-only dictionary. This class plays a crucial role in orchestrating the processes required for the submission of
quantum jobs in the current environment.
Specifically, the `TargetProfile` class specifies domain and device-specific instructions to tailor quantum programs to the
intermediate representation (IR) required for submission through the provider's API and execution on the quantum backend. This
includes compilation steps, type conversions, data mappings, and other essential runtime transformations.
Below is an example implementation of a TargetProfile:
```python theme={"dark"}
from unittest.mock import Mock
from qbraid.programs import ProgramSpec, ExperimentType
from qbraid.runtime import DeviceType, TargetProfile
profile = TargetProfile(
device_id="abc123",
num_qubits=7,
device_type=DeviceType.QPU,
experiment_type=ExperimentType.GATE_MODEL,
program_spec=ProgramSpec(Mock, alias="mock"),
basis_gates=["h", "x", "z", "cx", "s", "t"],
provider_name="myprovider",
)
```
API Reference:
qbraid.runtime.TargetProfile ↗
### Provider Setup
Each `QuantumProvider` subclass must implement both a `get_device` and a `get_devices` method. These methods process raw device data,
adapting it into a `TargetProfile` for each device, and return either a single `QuantumDevice` object or a list of them. In this example,
we use the `MySession` class to handle API requests; however, this is illustrative and not mandatory. API interactions can also be managed
directly through other means.
In cases where the API data does not directly conform to the format needed to instantiate a `TargetProfile`, additional mappings and
adaptations will typically be necessary. Below is an example implementation of a provider using `MySession` to construct a `MyDevice`
object. We will explore implementations of `QuantumDevice` subclasses in the next section.
```python theme={"dark"}
from qbraid.runtime import QuantumProvider, TargetProfile
class MyProvider(QuantumProvider):
def __init__(self, api_key: str):
super().__init__()
self.session = MySession(api_key)
def _build_profile(self, data: dict[str, Any]) -> TargetProfile:
return TargetProfile(**data)
def get_device(self, device_id: str) -> MyDevice:
data = self.get_device(device_id=device_id)
profile = self._build_profile(data)
return MyDevice(profile, self.session)
def get_devices(self, **kwargs) -> list[MyDevice]:
data = self.session.get_devices(**kwargs)
profiles = [self._build_profile(item) for item in data]
return [MyDevice(profile, self.session) for profile in profiles]
```
API Reference:
qbraid.runtime.QuantumProvider ↗
## QuantumDevice
The `qbraid.runtime.QuantumDevice` class describes the unique parameters and operational settings necessary for executing quantum
programs on specific hardware.
API Reference:
qbraid.runtime.QuantumDevice ↗
The device objects are the core component of the providers. These objects are how users can interface between
quantum computing frameworks and hardware/simulators to execute circuits. Any `QuantumDevice` subclass must
implement both a `status` and `submit` method.
A minimum working example could look like:
```python theme={"dark"}
from qbraid.runtime import DeviceStatus, QuantumDevice
class MyDevice(QuantumDevice):
def __init__(self, profile: TargetProfile, session: MySession):
super.__init__(profile=profile)
self.session = session
def status(self) -> DeviceStatus:
data = self.session.get_device(self.id)
status = data.get("status")
if status == "online":
return DeviceStatus.ONLINE
return DeviceStatus.OFFLINE
def transform(self, run_input: Mock) -> str:
program_ir = str(run_input)
return program_ir
def submit(self, run_input: str, shots=1000) -> MyJob:
job_data = {"target": self.id, "inpput": run_input, "shots": shots}
job_data = self.session.create_job(job_data)
job_id = job_data["job_id"]
return MyJob(job_id, session=self.session, device=self, shots=shots)
```
## QuantumJob
The `qbraid.runtime.QuantumJob` class represents the transitional states of quantum programs, managing both ongoing and completed
quantum computations.
API Reference:
qbraid.runtime.QuantumJob ↗
```python theme={"dark"}
from qbraid.runtime import Result, GateModeResultData, JobStatus, QuantumJob
class MyJob(QuantumJob):
def __init__(self, job_id: str, session: MySession, **kwargs):
super().__init__(job_id=job_id, **kwargs)
self.session = session
def status(self):
data = self.session.get_job(self.id)
status = data.get("status")
if status == "completed":
return JobStatus.COMPLETED
if status == "failed":
return JobStatus.FAILED
return JobStatus.QUEUED
def result(self) -> Result:
self.wait_for_final_state()
job_data = self.session.get_job(self.id)
success = job_data.pop("status") == "completed"
counts = job_data.pop("counts")
result_data = GateModeResultData(measurement_counts=counts)
return Result(
device_id=self.device.id,
job_id=self.id,
success=success,
data=result_data,
**job_data
)
```
API Reference:
qbraid.runtime.Result ↗
qbraid.runtime.GateModelResultData ↗
qbraid.runtime.AhsResultData ↗
qbraid.runtime.AnnealingResultData ↗
# Runtime Options
Source: https://docs.qbraid.com/v2/sdk/user-guide/runtime/options
Settings applied at the device level that control the behavior of a device.run call
## Overview
Every `QuantumDevice` has a set of runtime options that control the behavior of the `device.run()` call.
These options govern which steps in the [job submission pipeline](/v2/sdk/user-guide/runtime/components#quantum-job-submission-process)
are enabled, and can be updated using `device.set_options()`.
The four default options correspond to the pipeline steps that precede job submission:
| Option | Default | Type | Description |
| ----------- | ----------------------- | -------------------------- | ------------------------------------------------------------------------ |
| `transpile` | `True` | `bool` | Convert the program to the device's target program type |
| `transform` | `True` | `bool` | Apply device-specific passes (e.g. gate decomposition, topology mapping) |
| `validate` | `ValidationLevel.RAISE` | `ValidationLevel` or `int` | Verify the program satisfies device constraints |
| `prepare` | `True` | `bool` | Serialize the program to the submission IR format |
```python theme={"dark"}
device.set_options(transform=False)
device.set_options(validate=False)
```
Each option corresponds to a step in the `apply_runtime_profile` pipeline invoked by `device.run()`:
```
run_input ─> transpile ─> transform ─> validate ─> prepare ─> submit
```
Disabling an option skips that step entirely. For example, setting `transform=False` means the program
will not undergo any device-specific gate set transformations before validation.
API Reference:
qbraid.runtime.RuntimeOptions ↗
## Transform
The `QuantumDevice.transform` step applies device-specific transformations to the program. This is where
gate decompositions, topology mapping, and other hardware-aware compilation passes are performed. The base
class implementation is a no-op — each provider overrides `transform()` with its own logic.
Setting `transform=False` skips this step:
```python theme={"dark"}
device.set_options(transform=False)
```
### IonQ
The `IonQDevice.transform` method decomposes the input OpenQASM program into the IonQ native gate set.
It loads the program, applies gate mappings (single-qubit, two-qubit, and three-qubit), and falls back to
`pyqasm.unroll()` if the initial transformation fails:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("ionq:ionq:sim:simulator")
# By default, transform=True, so the circuit is automatically
# decomposed into the IonQ native gate set (gpi, gpi2, ms, zz, etc.)
job = device.run(qasm_program, shots=100)
```
To skip the IonQ gate decomposition and submit the program as-is:
```python theme={"dark"}
device.set_options(transform=False)
job = device.run(qasm_program, shots=100)
```
### IBM
The `QiskitBackend.transform` method runs the program through a Qiskit `PassManager`
to produce an ISA (Instruction Set Architecture) circuit compatible with the target backend.
By default, it uses `generate_preset_pass_manager(backend=...)`, but you can supply your own:
```python theme={"dark"}
from qiskit.transpiler import PassManager
device.set_options(pass_manager=my_custom_pass_manager)
```
See the [Provider-Specific Options](#provider-specific-options) section for more details on the `pass_manager` option.
### AWS
The `BraketDevice.transform` method applies provider-aware transformations. For IonQ devices accessed
through Amazon Braket, it routes the circuit through pytket for an expanded gate set, then applies
Braket-specific transformations. For other Braket-supported devices, it applies standard device transformations
through the `load_program` interface.
## Validate
The validate step verifies that the run input satisfies all criteria for submission to the target device. This
includes checks such as ensuring the number of qubits in the circuit does not exceed the device's capacity, and
that the program conforms to the device's `ProgramSpec`.
Validation behavior is controlled by the `ValidationLevel` enum:
| Level | Value | Behavior |
| ----------------------- | ----- | --------------------------------------------------- |
| `ValidationLevel.NONE` | `0` | No validation is performed |
| `ValidationLevel.WARN` | `1` | Warnings are issued if validation fails |
| `ValidationLevel.RAISE` | `2` | Exceptions are raised if validation fails (default) |
API Reference:
qbraid.runtime.ValidationLevel ↗
### Validation in action
By default, validation is set to `RAISE`. If a program violates device constraints, a
`ProgramValidationError` is raised:
```python theme={"dark"}
from qbraid.runtime import QbraidProvider
provider = QbraidProvider()
device = provider.get_device("aws:rigetti:qpu:cepheus-1-108q")
job = device.run(program, shots=100)
# ProgramValidationError: Number of qubits in the circuit (100) exceeds the device's capacity (84).
```
### Disabling validation
To skip validation entirely, set the validation level to `NONE`. You can use either the enum or
pass `False` as a shorthand:
```python theme={"dark"}
from qbraid.runtime import ValidationLevel
# Using the enum
device.set_options(validate=ValidationLevel.NONE)
# Or using the bool shorthand
device.set_options(validate=False)
job = device.run(program, shots=100)
```
### Downgrading to warnings
To receive warnings instead of errors, set the validation level to `WARN`:
```python theme={"dark"}
device.set_options(validate=ValidationLevel.WARN)
job = device.run(program, shots=100)
# UserWarning: Number of qubits in the circuit (100) exceeds the device's capacity (84).
```
### Re-enabling validation
To restore the default strict validation behavior:
```python theme={"dark"}
# Using the enum
device.set_options(validate=ValidationLevel.RAISE)
# Or using the bool shorthand
device.set_options(validate=True)
```
## Prepare
The prepare step serializes the quantum program into the intermediate representation (IR) required
for submission to the target device's API. This might produce a JSON payload, an OpenQASM string,
bytecode, or any other format expected by the provider.
The base class implementation delegates to `ProgramSpec.serialize()`, which is defined per-provider.
For example, Pasqal Pulser sequences are serialized into a JSON-based abstract representation, and
OpenQASM programs may be wrapped into a `Program` object with format and data fields.
Setting `prepare=False` skips this step, leaving the program in its current in-memory representation:
```python theme={"dark"}
device.set_options(prepare=False)
```
This can be useful during debugging or when you want to inspect the transformed program before
it gets serialized:
```python theme={"dark"}
# Disable prepare and submit to inspect the post-transform program
device.set_options(prepare=False)
# apply_runtime_profile runs transpile, transform, and validate
# but skips serialization
processed = device.apply_runtime_profile(program)
print(type(processed)) # Still a circuit object, not serialized IR
```
## Provider-Specific Options
Providers can extend the default options with their own fields. These additional options
are merged into the device's configuration at initialization and follow the same `set_options` interface.
### IBM `pass_manager`
The `QiskitBackend` adds a `pass_manager` option that accepts a Qiskit `PassManager` instance (or `None`).
When set, the custom pass manager is used during the transform step instead of the default preset pass manager:
```python theme={"dark"}
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Optimize1qGates, CXCancellation
# Create a custom pass manager
custom_pm = PassManager([Optimize1qGates(), CXCancellation()])
device.set_options(pass_manager=custom_pm)
job = device.run(circuit, shots=100)
```
Reset to the default behavior by setting `pass_manager` back to `None`:
```python theme={"dark"}
device.set_options(pass_manager=None)
```
The `pass_manager` option includes a built-in validator that only accepts `None` or a `PassManager` instance.
Attempting to set it to any other type raises a `ValueError`.
## Advanced: RuntimeOptions
Under the hood, device options are managed by the `RuntimeOptions` class, a dataclass-like container
with dictionary-style access, dynamic field support, and custom validators. While `device.set_options()`
is the primary interface for users, understanding `RuntimeOptions` can be useful when
[writing a new provider](/v2/sdk/user-guide/runtime/new-provider).
### Initializing with custom fields
```python theme={"dark"}
from qbraid.runtime.options import RuntimeOptions
options = RuntimeOptions(transpile=True, custom_field=42)
options["custom_field"]
# 42
```
### Adding dynamic fields
Fields can be added at any time using attribute or dictionary syntax:
```python theme={"dark"}
options["new_field"] = "hello"
options.another_field = 99
print(options)
# RuntimeOptions(transpile=True, custom_field=42, new_field='hello', another_field=99)
```
### Removing dynamic fields
Dynamic (non-default) fields can be deleted. Default fields cannot be removed:
```python theme={"dark"}
del options["new_field"] # OK - dynamic field
del options["another_field"] # OK - dynamic field
del options["transpile"] # KeyError: Cannot delete default field 'transpile'.
```
### Setting validators
Validators are callables that return `True` for valid values. Once set, every update to the field is
checked against the validator:
```python theme={"dark"}
options = RuntimeOptions(threshold=0.5)
options.set_validator("threshold", lambda x: isinstance(x, float) and 0 <= x <= 1)
options.threshold = 0.8 # OK
options.threshold = 1.5 # ValueError: Value '1.5' is not valid for field 'threshold'.
options.threshold = "abc" # ValueError
```
If a validator already exists and the current value does not satisfy the new validator, a `ValueError`
is raised. You must update or delete the field first before replacing the validator:
```python theme={"dark"}
options = RuntimeOptions(count=5)
options.set_validator("count", lambda x: x > 0)
# This will raise because the current value 5 does not satisfy the new validator
options.set_validator("count", lambda x: x > 10)
# ValueError: Existing value '5' for field 'count' is not valid for the new validator.
```
### Merging options
Providers use `merge()` to combine custom options with the device defaults. The `override_validators`
parameter controls whether validators from the merged options replace existing ones:
```python theme={"dark"}
defaults = RuntimeOptions(transform=True, validate=2)
custom = RuntimeOptions(validate=0)
defaults.merge(custom, override_validators=False)
defaults["validate"]
# 0
```
This is what happens internally when you pass `options` to a `QuantumDevice` constructor, as
shown in the [IBM `pass_manager` example](#ibm-pass_manager).
# Transforms
Source: https://docs.qbraid.com/v2/sdk/user-guide/transforms
Adapt quantum programs to specific hardware requirements.
In the qBraid-SDK, a "transpile" action refers to converting a quantum program from one type to another,
such as from Qiskit to Amazon Braket. In contrast, a "transform" action involves modifying a quantum program to meet specific
requirements dictated by the target device or API through which the program will be executed.
Let's delve into the various "transforms" employed to prepare a quantum program for execution on an IonQ backend.
We begin by constructing a simple Qiskit circuit that executes a controlled Y rotation:
```python theme={"dark"}
import numpy as np
from qiskit import QuantumCircuit
circuit = QuantumCircuit(2, 2)
circuit.cry(np.pi/4, 0, 1)
circuit.measure_all()
```
We then connect to the `IonQProvider` and retrieve the specifications for the IonQ Harmony device:
```python theme={"dark"}
from qbraid.runtime import IonQProvider
provider = IonQProvider()
device = provider.get_device("qpu.harmony")
device.profile.get("program_spec")
#
device.profile.get("basis_gates")
# { "x","y","z","rx","ry","rz","h","cx","s","sdg","t","tdg","sx","sxdg","swap" }
```
By reviewing the device's `TargetProfile`, we understand that in order to run a program on IonQ Harmony,
qBraid runtime requires that it be expressed as an `openqasm3.ast.Program` and utilize only the gates from
its defined basis gate set. Consequently, our first step is to transpile our `qiskit` program to the `openqasm3`
program type:
```python theme={"dark"}
from qbraid import transpile
qasm3_program = transpile(circuit, 'openqasm3')
type(qasm3_program)
# openqasm3.ast.Program
```
Next, we load the program into a qBraid `QuantumProgram` object and examine its string representation:
```python Input theme={"dark"}
from qbraid import load_program
qprogram = load_program(qasm3_program)
print(qprogram.program)
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
include "stdgates.inc";
bit[2] c;
bit[2] meas;
qubit[2] q;
cry(pi / 4) q[0], q[1];
barrier q[0], q[1];
meas[0] = measure q[0];
meas[1] = measure q[1];
```
We can now apply a "transform" to this program, using our device object as argument,
ensuring it is represented exclusively in terms of gates supported by IonQ Harmony:
```python Input theme={"dark"}
program.transform(device)
print(program.program)
```
```qasm Output theme={"dark"}
OPENQASM 3.0;
bit[2] b;
qubit[2] q;
ry(0.39269908169872414) q[1];
cx q[0], q[1];
ry(-0.39269908169872414) q[1];
cx q[0], q[1];
b[0] = measure q[0];
b[1] = measure q[1];
```
Device-specific transforms are a crucial component of the qBraid runtime framework, applied as the final step
before a program is executed on a quantum backend.
We've just demonstrated how one can utilize these transforms independently, outside of a runtime protocol, which
may be beneficial for testing or local simulations. However, for qBraid runtime end-users, these steps are automatically
performed behind the scenes whenever the `QuantumDevice.run` method is invoked.
# Transpiler
Source: https://docs.qbraid.com/v2/sdk/user-guide/transpiler
Graph-based approach to quantum program type conversions.
The qBraid-SDK transpiler allows converting quantum programs from one type to another, for any two program types for which
a "conversion path" exists. Registered program types are interconnected via a directed graph, where each program type is
represented as a node and supported conversions as edges. The breadth, depth, and connectivity of this `ConversionGraph`
can be customized by the user.
API Reference:
[qbraid.transpiler](https://qbraid.github.io/qBraid/api/qbraid.transpiler.html)
## Conversion Graph
To view what conversions are available, you can plot the `ConversionGraph`:
```python theme={"dark"}
from qbraid import ConversionGraph
graph = ConversionGraph()
len(graph.nodes()) # 10
len(graph.edges()) # 25
graph.plot(legend=True)
```
Gray arrows denote conversions implemented and managed by qBraid. Red arrows represent conversions implemented
by external packages or extensions. These are automatically incorporated into the conversion graph upon
installing the specified 'extra'.
You can use the native conversions supported by qBraid, or define your own custom nodes and/or edges. For example:
```python theme={"dark"}
from unittest.mock import Mock
from qbraid import register_program_type, Conversion
# replace with any program type
register_program_type(Mock, alias="mock")
# replace with your custom conversion function
example_qasm3_to_mock_func = lambda x: x
conversion = Conversion("qasm3", "mock", example_qasm3_to_mock_func)
graph.add_conversion(conversion)
# using a seed is helpful to ensure reproducibility
graph.plot(seed=20, k=3, legend=True)
```
Blue arrows indicate conversions added by the user during their current session.
Check if a conversion is supported between two given program types, and if so, what "conversion path(s)" would be used:
```python theme={"dark"}
graph.has_path("qiskit", "braket") # True
for p in graph.all_paths("qiskit", "braket"): print(p)
# qiskit -> braket
# qiskit -> qasm3 -> braket
# qiskit -> qasm2 -> qasm3 -> braket
# qiskit -> qasm2 -> pytket -> braket
# qiskit -> qasm2 -> cirq -> braket
graph.shortest_path("qiskit", "braket") # qiskit -> braket
```
Scope the conversions available to the transpiler by specifying your own `conversions` list:
```python theme={"dark"}
from qbraid import ConversionGraph, Conversion
from qbraid.transpiler.conversions.qiskit import qiskit_to_braket
conv = Conversion("qiskit", "braket", qiskit_to_braket)
graph = ConversionGraph(conversions=[conv])
graph.all_paths("qiskit", "braket")
# ["qiskit -> qasm3 -> braket"]
```
## Conversion Scheme
The `ConversionScheme` data class details information about conversion configurations, such as numbers of nodes and edges, as well as max depth.
```python theme={"dark"}
from qbraid import ConversionGraph, ConversionScheme
scheme = ConversionScheme(max_path_attempts=3, max_path_depth=None)
custom_graph = ConversionGraph(...)
scheme.update_scheme(conversion_graph=custom_graph)
scheme.to_dict()
```
## Transpile
Using the `qbraid.transpile` function, simply pass in the name of the target package from one of `qbraid.QPROGRAM_ALIASES`. For example, use input `"cirq"` to return a `cirq.Circuit`:
```python theme={"dark"}
from qiskit import QuantumCircuit
def bell():
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0,1)
return circuit
qiskit_circuit = bell()
```
```python Input theme={"dark"}
from qbraid import transpile
cirq_circuit = transpile(qiskit_circuit, "cirq")
print(cirq_circuit)
```
```text Output theme={"dark"}
q_0: ───H───@───
│
q_1: ───────X───
```
This time, using the same origin circuit, we'll input `"pyquil"` to return a `pyquil.quil.Program`:
```python Input theme={"dark"}
pyquil_program = transpile(qiskit_circuit, "pyquil")
print(pyquil_program)
```
```text Output theme={"dark"}
H 0
CNOT 0 1
```
See also:
* Transpiler Demo Notebook: General Usage
# Visualization
Source: https://docs.qbraid.com/v2/sdk/user-guide/visualization
Draw circuit diagrams and plot experimental results, conversion graphs, and more.
API Reference:
[qbraid.visualization](https://qbraid.github.io/qBraid/api/qbraid.visualization.html)
## Draw Circuit Diagrams
qBraid's `circuit_drawer` function takes in any type of supported quantum circuit and draws the corresponding visualization.
Here's an example using `braket` and `cirq`:
```python theme={"dark"}
from qbraid import random_circuit
from qbraid.visualization import circuit_drawer
circuit = random_circuit("braket")
circuit_drawer(circuit)
# T : |0| 1 |2|
#
# q0 : -C-C---S-
# | |
# q1 : -Z-|-Z---
# |
# q2 : -Z-X-----
#
# T : |0| 1 |2|
circuit = random_circuit("cirq")
circuit_drawer(circuit)
# 0: ───────────×───Z───
# │
# 1: ───iSwap───×───────
# │
# 2: ───iSwap───Y───H───
```
### Draw OpenQASM 3 circuits
The `circuit_drawer` function accepts a string of OpenQASM 3 code and returns a matplotlib figure of the circuit:
```python theme={"dark"}
from qbraid.visualization import circuit_drawer
program = """
OPENQASM 3;
include "stdgates.inc";
qubit[2] q;
h q[0];
cx q[0], q[1];
"""
circuit_drawer(program)
```
**Note**: The above is the equivalent to calling `pyqasm.draw(..., output="mpl")`
## Plot Experimental Results
Gather the measurement counts and plot the histogram data for any `Result` constructed from `ResultData` of type `qbraid.runtime.GateModelResultData`:
```python theme={"dark"}
from qbraid.visualization import plot_histogram
counts = result.data.get_counts()
# {'00': 483, '01': 14, '10': 486, '11': 17}
plot_histogram(counts)
```
Or, using the same measurement counts data, plot the probability distribution:
```python theme={"dark"}
from qbraid.visualization plot_distribution
plot_distribution(counts)
```
Or, plot a batch of measurement counts for any `list[qbraid.runtime.GateModeResultData]`:
```python theme={"dark"}
batch_jobs = device.run_batch([circuit0, circuit1], shots=1000)
batch_results = [job.result() for job in batch_jobs]
batch_counts = [result.data.get_counts() for result in batch_results]
# e.g. [{'0': 136, '1': 864}, {'0': 166, '1': 834}]
plot_histogram(batch_counts)
```
Using the qBraid runtime job and results primitives, experimental data is returned in a standardized format,
facilitating straightforward comparisons and benchmarking of results across different providers and backends.
## Plot Transpiler Conversions
Plot all supported conversions between registered program types available through the `qbraid.transpiler`:
```python theme={"dark"}
from qbraid import ConversionGraph
from qbraid.visualization import plot_conversion_graph
graph = ConversionGraph()
plot_conversion_graph(graph, legend=True)
```
## Plot Runtime Conversion Scheme
Plot the runtime conversion scheme for any `qbraid.runtime.QuantumDevice`.
The `QuantumDevice.scheme` defines the `qbraid.transpiler.ConversionScheme` that will be used to carry out the "transpile" step of the [Quantum Job Submission Process](/v2/sdk/user-guide/runtime/components#quantum-job-submission-process).
```python theme={"dark"}
from qbraid import QbraidProvider
from qbraid.visualization import plot_runtime_conversion_scheme
provider = QbraidProvider()
device = provider.get_device("qbraid:qbraid:sim:qir-sv")
print(device.scheme)
```
By setting the maximum path depth to 1, we ensure the `ConversionGraph` includes only program types (nodes) that are directly connected to the target program type by a single conversion step (edge).
```python theme={"dark"}
device.update_scheme(max_path_depth=1)
plot_runtime_conversion_scheme(device)
```
By setting the maximum path depth to 2, we can see how the `ConversionGraph` is expanded to include program types (nodes) that are at most two conversion steps (edges) away from the target program type.
```python theme={"dark"}
device.update_scheme(max_path_depth=2)
plot_runtime_conversion_scheme(device)
```
# qBraid VS Code
Source: https://docs.qbraid.com/v2/vscode/user-guide/code-editor
Directly connect to an online VS Code environment with pre-installed quantum extensions.
## Overview
qBraid VS Code is a cloud-hosted deployment of Visual Studio Code, designed for quantum computing and available to all qBraid users. It offers a browser-based coding environment with direct access to quantum resources, pre-configured environments, and seamless integration with qBraid Lab. Users can develop, run, and manage quantum and classical code from anywhere, without the need for local setup or installation.
## How to Launch
1. **Login to qBraid:**\
Go to [qbraid.com](https://qbraid.com) and sign in with your account.
2. **Choose VS Code Instance:**\
Navigate to the Dashboard section and select the VS Code option from your available compute environments. You will have an option to choose instances with varying resource allocations based on your needs.
3. **Launch:**\
Click the "Launch New Instance" button to start your cloud VS Code session. The editor will open in your browser, ready to use with all quantum tools pre-installed.
## Features
* **Quantum Console:**\
Manage quantum jobs, devices, and providers (including IBM and qBraid) directly from the VS Code interface.
* **Environment Manager:**\
Create, activate, and manage Python environments for quantum development, both locally and in the cloud.
* **Pre-configured Quantum Lab:**\
Access Jupyter notebooks, quantum SDKs, and all necessary dependencies without manual setup.
* **Seamless Integration:**\
Move between code, notebooks, and quantum resources with a unified experience, leveraging qBraid’s authentication and resource management.
qBraid VS Code streamlines quantum development, making it easy to start coding, experimenting, and running jobs on real quantum hardware — all
within the same environment.
# Environment Manager
Source: https://docs.qbraid.com/v2/vscode/user-guide/environment-manager
Manage your quantum environments directly within Visual Studio Code.
The qBraid Environment Manager extension allows you to manage your Python quantum environments directly within Visual Studio Code. This includes creating, viewing, using, removing, editing, and sharing environments both locally and in the cloud.
### Installation
The Environment Manager extension can be installed directly through the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=qBraid.qbraid-environment-manager) -
## Managing Environments
### Create
* Create a new environment by clicking on the `Add` icon in the Environment Manager panel.
* Click on the `Create Environment` button to open the environment creation dialog.
* Enter the name and description for the environment, select the Python version, tags, and packages you want to include.
* Click on the `Create` button to create the environment.
* The new environment will be created and added to the Environment Manager panel.
* You can also choose to install an environment from the pre-configured groups such as Qiskit, qBraid, etc. -
### View
* You can view the created Python environments on the Environment Manager panel.
* Click on the `Environment Manager` icon in the Activity Bar on the side of the VS Code window. This will open up the Environment Manager panel where you can see all of your current installed environments.
* You can expand the dropdown pertaining to each environment to see more details, such as the Python version, packages installed, and whether it is currently active.
* Clicking on the `More` button will open up a separate panel with additional options for that environment, such as viewing and updating installed Python packages,
or sharing the environment with others.
### Use
* Expand the dropdown for the environment you want to use and click on the `Activate` button.
* Once activated, click on the `Select Kernel` icon. This will open the selection dialog for choosing the Python kernel.
* Select the kernel corresponding to the activated environment.
* You can now run Python commands in this terminal using the selected environment.
### Edit
* Clicking on the `More` button will open up a separate panel with additional options for that environment.
* You can edit the environment general info by clicking on the `Edit` button. This will allow you to change the name, description, and other details of the environment.
* You can also add/remove packages from the environment by clicking on the `Packages` tab in the same panel:
* **Add**: Search for the package you want to add, select a version, and click on the `Add` button
* **Remove**: Click on the `Remove` button next to the package.
* Once you have made all the changes, click on the `Update` button to apply them
### Uninstall
* Click on the `More` button and select the `Uninstall Environment` option.
* A confirmation dialog will appear asking you to confirm the removal of the environment. Once confirmed,
the environment will be removed from your local machine or qBraid Lab instance (if it is a cloud environment).
### Share
* Click on the `More` button and select the `Share Info` option.
* You can choose to share the environment via:
* **Email**: Enter the email address of the recipient and click on the `Share` button.
* **Access Code**: Click on the `Generate Access Code` button. This will generate a unique access code that can be shared with others to access the environment.
The recipient can then use this code to access the environment in their own qBraid account.
## Managing Compute Resources
* As part of the Environment Manager is the **Compute Manager** extension, which allows you to connect to and manage remote qBraid Lab compute resources, including CPUs and NVIDIA GPUs.
* The Compute Manager allows you to:
* View available compute resources in the qBraid Lab.
* Activate or deactivate compute resources.
* View the status of your compute resources.
* Manage your compute instances directly from Visual Studio Code.
* To utilize the `Cloud` functionality of the Environment Manager, you must have a running instance of the [qBraid Lab](/v2/lab/user-guide/overview).
# Overview
Source: https://docs.qbraid.com/v2/vscode/user-guide/overview
Get an overview of the qBraid VS Code extension pack and our custom VS Code deployment.
# qBraid and VS Code
The qBraid VS Code Extension Pack brings together a curated set of extensions
designed to streamline quantum software development, device access, and
environment management—all within the familiar Visual Studio Code interface.
This pack is available for both local installations and as part of qBraid’s
custom, cloud-hosted VS Code deployment.
## Extension Packs
The extension pack includes:
* [**Quantum Console:**](/v2/vscode/user-guide/quantum-console) Manage quantum jobs, devices, and providers like qBraid, and IBM directly from VS Code.
* [**Environment Manager:**](/v2/vscode/user-guide/environment-manager) Easily switch between and manage Python environments, essential for quantum software development.
## VS Code Deployment
qBraid offers a custom deployment of Visual Studio Code, which provides:
* **Pre-installed Quantum Extensions:** All core qBraid extensions and dependencies are ready to use—no setup required.
* **Cloud-based Development:** Access your development environment from anywhere, with persistent storage and scalable compute resources.
* **Integrated Authentication:** Secure access to qBraid and IBM Quantum services, with easy API key management.
* **Consistent Experience:** Enjoy the same interface and features whether working locally or in the cloud.
Navigate to account [Lab Instances](https://account.qbraid.com/dashboard/lab-instances), choose **VS Code** as your IDE, select a compute profile, and launch.
## Local & Cloud Flexibility
The qBraid extension pack can be installed in your local VS Code or used directly within the custom cloud deployment. This ensures a consistent, powerful quantum development experience—no matter where you work.
Explore the following pages for detailed guides on each of our extensions and deployment features, including screenshots and step-by-step usage instructions.
# Quantum Console
Source: https://docs.qbraid.com/v2/vscode/user-guide/quantum-console
Manage qBraid jobs and devices directly within Visual Studio Code.
The Quantum Console VS Code extension pack provides a powerful interface for quantum computing workflows, enabling seamless management of quantum jobs and devices through qBraid's [public APIs](/v2/api-reference/user-guide/overview).
### Installation
The Quantum Console extension can be installed directly through the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=qBraid.quantum-console) -
## Extensions
### Cloud Providers Panel
Manage your quantum cloud providers directly from VS Code.
* **Enable/Disable Providers:** Easily toggle access to supported providers.
* **Supported Providers:** `qBraid`, `IBM`
* **Seamless Switching:** Instantly switch between providers to manage jobs and devices from multiple platforms in one place.
### Quantum Devices
Monitor the status, queue depth, capabilities, and other details of quantum devices accessible through both IBM and qBraid.
**IBM Devices**
* View available IBM QPUs and simulators.
* Inspect detailed device properties: qubit count, connectivity, error rates, and more.
* Monitor real-time queue depth and device status.
* Seamlessly switch to IBM as your active provider to submit jobs and manage devices.
**qBraid Devices**
* Access QPUs from IonQ, Oxford Quantum Circuits, QuEra, Rigetti, and IQM.
* Use on-demand simulators from qBraid, AWS, IonQ, QuEra, and NEC.
* Monitor device status, queue depth, and capabilities.
* Filter and sort devices by provider, type, and other attributes.
* For more details on access and costs, see the [pricing page](https://docs.qbraid.com/home/pricing).

### Quantum Jobs
The **Quantum Jobs** sidebar is where you can view and manage all your quantum jobs. Jobs submitted via the [qbraid.runtime.QbraidProvider](https://docs.qbraid.com/sdk/user-guide/providers/native) will automatically appear in the **qBraid** tab. Moreover, jobs submitted directly to IBM Quantum via [Qiskit](https://quantum.cloud.ibm.com/docs/en/guides#qiskit-runtime) or [qbraid.runtime.ibm.QiskitRuntimeProvider](https://docs.qbraid.com/sdk/user-guide/providers/ibm) will appear in the **IBM** tab. The sidebar allows you to:
* *View Jobs* – Browse all jobs submitted to qBraid and IBM Quantum.
* *Monitor Progress* – Track job status and queue position in real-time.
* *Manage Jobs* – Cancel or delete jobs as needed.
* *Search & Filter* – Find jobs by ID, status, tags, and other attributes.
* *Inspect Details* – View job's qasm representation, cost estimates, and job metadata.
* *Retrieve Results* – View measurement counts and execution data.

## Authentication
The Quantum Console extension pack requires authentication for both qBraid and IBM Quantum providers:
**qBraid Access**
1. Login or create a free account at [account.qbraid.com](https://account.qbraid.com).
2. Copy your API Key from the **Plan** info card on the left side of your account page.
3. Enter the API Key in the qBraid provider dropdown.
4. Click *Save* to complete the authentication process.
For more information, refer to the [qBraid API Keys documentation](https://docs.qbraid.com/home/account#api-keys).
**IBM Quantum Access**
1. Login or create a free account at [quantum.cloud.ibm.com](https://quantum.cloud.ibm.com/).
2. Obtain your IBM Cloud API Key and the Service Cloud Resource Name (CRN) for your IBM Quantum instance from the [IBM Cloud Console](https://quantum.cloud.ibm.com/).
3. Enter both the API Key and Service CRN in the IBM provider dropdown.
4. Click *Save* to complete the authentication process.
For more information, refer to the [Qiskit Runtime documentation](https://quantum.cloud.ibm.com/docs/en/api/qiskit-runtime-rest#authentication).
# Cancel Job
Source: https://docs.qbraid.com/v2/api-reference/rest/cancel-quantum-job
post /jobs/{job_qrn}/cancel
Cancel a quantum job.
**Args:**
- **job_qrn** (string): Job QRN (qBraid Resource Name)
**Returns:**
- Object containing a confirmation message, the job QRN, and the updated status
**Raises:**
- **400**: Job is in an invalid state for cancellation or vendor is not supported
- **404**: Job is not found
- **422**: Validation error if the job_qrn parameter is invalid
```json 202 theme={"dark"}
{
"success": true,
"data": {
"message": "Job cancellation initiated successfully",
"jobQrn": "qbraid:job:abc123xyz",
"status": "CANCELLING"
}
}
```
# Delete Job
Source: https://docs.qbraid.com/v2/api-reference/rest/delete-quantum-job
delete /jobs/{job_qrn}
Delete a quantum job by its job QRN.
**Args:**
- **job_qrn** (string): Job QRN (qBraid Resource Name)
**Returns:**
- Success message confirming job deletion
**Raises:**
- **403**: You do not have permission to delete this job
- **404**: Job is not found
- **422**: Validation error if the job_qrn parameter is invalid
```json 200 theme={"dark"}
{
"success": true,
"data": {
"message": "Job deleted successfully"
}
}
```
# Delete Multiple Jobs
Source: https://docs.qbraid.com/v2/api-reference/rest/delete-quantum-jobs
delete /jobs
Delete multiple quantum jobs by their job QRNs.
**Query Parameters:**
- **qrns** (string, required): JSON array of job QRNs to delete (e.g., ["qrn1", "qrn2"])
**Returns:**
- Object containing arrays of successfully deleted and failed job QRNs
**Raises:**
- **422**: Validation error if the qrns parameter is invalid
```json 200 theme={"dark"}
{
"success": true,
"data": {
"successfulIds": ["qbraid:job:abc123xyz", "qbraid:job:def456uvw"],
"failedIds": {
"qbraid:job:invalid123": "Job not found"
}
}
}
```
# Get Device
Source: https://docs.qbraid.com/v2/api-reference/rest/get-device
get /devices/{device_qrn}
Retrieve a device by its device QRN.
**Args:**
- **device_qrn** (string): Device QRN (qBraid Resource Name)
**Returns:**
- RuntimeDevice object containing the device details
**Raises:**
- **422**: Validation error if the device_qrn parameter is invalid
```json 200 theme={"dark"}
{
"success": true,
"data": {
"runInputTypes": ["pyqir", "qasm3"],
"numberQubits": 64,
"noiseModels": [],
"statusMsg": null,
"nextAvailable": null,
"avgQueueTime": null,
"qrn": "qbraid:qbraid:sim:qir-sv",
"modality": null,
"name": "QIR Simulator",
"paradigm": "gate_model",
"pricing": {
"perTask": 0,
"perShot": 0,
"perMinute": 0
},
"status": "ONLINE",
"vendor": "qbraid",
"description": "Sparse state vector simulator using QIR compiler. Free of cost.",
"deviceType": "SIMULATOR",
"queueDepth": 0,
"directAccess": true,
"pricingModel": "fixed"
}
}
```
# Get Job Program
Source: https://docs.qbraid.com/v2/api-reference/rest/get-job-program
get /jobs/{job_qrn}/program
Retrieve the quantum program data for a job.
**Args:**
- **job_qrn** (string): Job QRN (qBraid Resource Name)
**Returns:**
- Program object containing the quantum program with format and data fields
**Raises:**
- **404**: Job is not found
- **409**: Job program is not yet available or is not valid JSON
- **422**: Validation error if the job_qrn parameter is invalid
```json 200 theme={"dark"}
{
"success": true,
"data": {
"format": "qasm3",
"data": "OPENQASM 3;\ninclude \"stdgates.inc\";\nqubit[2] q;\nh q[0];\ncx q[0], q[1];\nmeasure q;"
}
}
```
# Get All Devices
Source: https://docs.qbraid.com/v2/api-reference/rest/get-quantum-devices
get /devices
Retrieve a list of quantum devices available to the authenticated user.
**Query Parameters:**
- **qrn** (string, optional): Filter by device QRN
- **vendor** (string, optional): Filter by vendor
- **providerId** (string, optional): Filter by provider ID
- **deviceType** (string, optional): Filter by device type (`QPU` or `SIMULATOR`)
- **status** (string, optional): Filter by operational status
- **retired** (boolean, optional): Include or exclude retired devices
- **private** (boolean, optional): Filter by visibility (`true` = private, `false` = public)
- **verified** (boolean, optional): Filter by verification status
- **directAccess** (boolean, optional): Filter by direct access availability
- **search** (string, optional): Search by name, description, or about (1–255 characters)
- **sortBy** (string, optional): Field to sort by
- **sortOrder** (string, optional): Sort direction (`asc` or `desc`)
- **page** (integer, optional): Page number (default: 1)
- **limit** (integer, optional): Items per page, max 100 (default: 20)
**Returns:**
- Array of RuntimeDevice objects containing device specifications and pricing information
**Raises:**
- **401**: Authentication required
- **422**: Validation error if query parameters are invalid
```json 200 theme={"dark"}
{
"success": true,
"data": [
{
"name": "QIR sparse simulator",
"qrn": "qbraid:qbraid:sim:qir-sv",
"vendor": "qbraid",
"deviceType": "SIMULATOR",
"runInputTypes": ["qasm2", "qasm3", "qir.bc", "qir.ll"],
"status": "ONLINE",
"statusMsg": null,
"nextAvailable": null,
"queueDepth": 0,
"avgQueueTime": null,
"numberQubits": 64,
"paradigm": "gate_model",
"modality": "sparse_simulator",
"noiseModels": null,
"pricingModel": "fixed",
"pricing": {
"perTask": 0.5,
"perShot": 0,
"perMinute": 7.5
},
"directAccess": true
}
]
}
```
# Get Job
Source: https://docs.qbraid.com/v2/api-reference/rest/get-quantum-jobs
get /jobs/{job_qrn}
Retrieve a quantum job by its job QRN.
**Args:**
- **job_qrn** (string): Job QRN (qBraid Resource Name)
**Returns:**
- RuntimeJob object containing the job details
**Raises:**
- **403**: You do not have permission to access this job
- **404**: Job is not found
- **422**: Validation error if the job_qrn parameter is invalid
```json 200 theme={"dark"}
{
"success": true,
"data": {
"name": "My quantum job",
"shots": 1000,
"deviceQrn": "qbraid:qbraid:sim:qir-sv",
"tags": {},
"runtimeOptions": {},
"jobQrn": "qbraid:job:abc123xyz",
"batchJobQrn": null,
"vendor": "qbraid",
"provider": "qbraid",
"status": "COMPLETED",
"statusMsg": null,
"experimentType": "gate_model",
"queuePosition": null,
"timeStamps": {
"createdAt": "2025-01-15T10:30:00Z",
"endedAt": "2025-01-15T10:30:05Z",
"executionDuration": 5000
},
"cost": 0.501875,
"estimatedCost": 0.5,
"metadata": {}
}
}
```
# Get All Jobs
Source: https://docs.qbraid.com/v2/api-reference/rest/get-quantum-jobs-list
get /jobs
Retrieve a paginated list of quantum jobs for the authenticated user.
**Query Parameters:**
- **vendor** (string, optional): Filter by vendor (e.g., `aws`, `ibm`, `qbraid`, or `all`)
- **provider** (string, optional): Filter by provider name
- **statusGroup** (string, optional): Filter by status group — `pending`, `returned`, or `all`
- **status** (string, optional): Filter by individual job status (takes priority over `statusGroup`)
- **search** (string, optional): Search jobs by keyword (1–255 characters)
- **tags** (string, optional): Filter by tags as a JSON object (e.g., `{"env":"prod"}`)
- **page** (integer, optional): Page number (default: 1)
- **limit** (integer, optional): Items per page, max 100 (default: 50)
**Returns:**
- Paginated array of RuntimeJob objects with pagination metadata
**Raises:**
- **401**: Authentication required
- **422**: Validation error if query parameters are invalid
```json 200 theme={"dark"}
{
"success": true,
"data": [
{
"name": "Bell state job",
"shots": 1000,
"deviceQrn": "qbraid:qbraid:sim:qir-sv",
"tags": { "experiment": "bell-state" },
"runtimeOptions": {},
"jobQrn": "qbraid:job:abc123xyz",
"batchJobQrn": null,
"vendor": "qbraid",
"provider": "qbraid",
"status": "COMPLETED",
"statusMsg": null,
"experimentType": "gate_model",
"queuePosition": null,
"timeStamps": {
"createdAt": "2025-01-15T10:30:00Z",
"endedAt": "2025-01-15T10:30:05Z",
"executionDuration": 5000
},
"cost": 0.501875,
"estimatedCost": 0.5,
"metadata": {}
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 1,
"totalPages": 1,
"hasNext": false,
"hasPrev": false
},
"meta": {
"timestamp": "2025-01-15T10:30:06.000Z"
}
}
```
# Get Job Result
Source: https://docs.qbraid.com/v2/api-reference/rest/get-quantum-jobs-result
get /jobs/{job_qrn}/result
Retrieve the result data for a quantum job.
**Args:**
- **job_qrn** (string): Job QRN (qBraid Resource Name)
**Returns:**
- Result object containing job result data including measurement counts and timestamps
**Raises:**
- **404**: Job is not found
- **409**: Job results are not yet available or are not valid JSON
- **422**: Validation error if the job_qrn parameter is invalid
```json 200 theme={"dark"}
{
"success": true,
"data": {
"status": "COMPLETED",
"cost": 0.501875,
"timeStamps": {
"createdAt": "2025-01-15T10:30:00Z",
"endedAt": "2025-01-15T10:30:05Z",
"executionDuration": 5000
},
"resultData": {
"measurementCounts": { "00": 504, "11": 496 }
}
}
}
```
# Migrating from V1
Source: https://docs.qbraid.com/v2/api-reference/rest/migration
This guide covers breaking changes when migrating from the qBraid V1 API to V2.
## What's Changed
### Authentication and Response Structure
#### Base URL
| Version | Base URL |
| ------- | ---------------------------------- |
| V1 | `https://api.qbraid.com/api` |
| V2 | `https://api-v2.qbraid.com/api/v1` |
#### Authentication Header
| Version | Header |
| ------- | ----------- |
| V1 | `api-key` |
| V2 | `X-API-KEY` |
#### Response Envelope
V2 responses are wrapped in a standard envelope:
```json theme={"dark"}
{
"success": boolean,
"data": { ... }
}
```
V1 returned raw payloads without this wrapper.
### Devices
The new API v2 makes the device objects more descriptive and standardized. We include fields like `paradigm`, `modality`, and `pricingModel` to better categorize devices. We have also standardized the device identifiers to use **QRNs** (qBraid Resource Names) for consistency across the platform.
#### Routes
| Operation | V1 Route | V2 Route |
| ------------ | ---------------------- | --------------------------- |
| List devices | `GET /quantum-devices` | `GET /devices` |
| Get device | — | `GET /devices/{device_qrn}` |
#### Identifier
| Version | Field | Example |
| ------- | ------------------ | -------------------------- |
| V1 | `qbraidDeviceId` | `qbraid_qir_simulator` |
| V2 | `qrn` (QRN format) | `qbraid:qbraid:sim:qir-sv` |
#### Response Fields
V2 adds:
| Field | Description |
| --------------- | --------------------------------------------- |
| `qrn` | QRN identifier |
| `paradigm` | `gate_model`, `analog`, `annealing`, `other` |
| `modality` | Device modality (e.g., `sparse_simulator`) |
| `pricingModel` | `fixed` or `dynamic` |
| `pricing` | Object with `perTask`, `perShot`, `perMinute` |
| `directAccess` | Boolean for direct device access |
| `runInputTypes` | Array of supported input formats |
V2 renames:
* `type` → `deviceType` (values: `SIMULATOR`, `QPU`)
### Jobs
Quantum jobs responses have been re-organized and use **QRNs** as identifiers. The job submission format has also changed to explicitly specify the program format rather than using the `openQasm` or `bitcode` fields.
#### Routes
| Operation | V1 Route | V2 Route |
| -------------------- | ------------------------------ | ----------------------------- |
| Create job | `POST /quantum-jobs` | `POST /jobs` |
| Get job | `GET /quantum-jobs` | `GET /jobs/{job_qrn}` |
| Get result | `GET /quantum-jobs/result/:id` | `GET /jobs/{job_qrn}/result` |
| Get program | — | `GET /jobs/{job_qrn}/program` |
| Cancel job | `PUT /quantum-jobs/cancel/:id` | `POST /jobs/{job_qrn}/cancel` |
| Delete job | — | `DELETE /jobs/{job_qrn}` |
| Delete multiple jobs | `DELETE /quantum-jobs/:ids` | `DELETE /jobs?qrns=[...]` |
#### Request Body
**V1:**
```json theme={"dark"}
{
"qbraidDeviceId": "qbraid_qir_simulator",
"openQasm": "OPENQASM 3; ...",
"shots": 1000,
"tags": {}
}
```
**V2:**
```json theme={"dark"}
{
"deviceQrn": "qbraid:qbraid:sim:qir-sv",
"program": {
"format": "qasm3",
"data": "OPENQASM 3; ..."
},
"shots": 1000,
"tags": {}
}
```
Key differences:
* `qbraidDeviceId` → `deviceQrn` (QRN format)
* `openQasm`/`bitcode` → `program.data` with explicit `program.format`
* `circuitNumQubits` removed (inferred from program)
#### Response Fields
V2 adds these fields to job responses:
| Field | Description |
| ---------------- | ------------------------------------------------------- |
| `jobQrn` | QRN identifier (replaces V1's `_id`) |
| `batchJobQrn` | Batch job reference |
| `experimentType` | `gate_model`, `analog`, `annealing`, `other` |
| `estimatedCost` | Pre-execution cost estimate |
| `timeStamps` | Object with `createdAt`, `endedAt`, `executionDuration` |
| `metadata` | Extensible metadata object |
## What's the Same
* API key authentication model (header-based)
* Core job lifecycle: create → poll status → get result
* `shots`, `tags`, `status` fields
* Job statuses: `INITIALIZING`, `QUEUED`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`
* Device statuses: `ONLINE`, `OFFLINE`, `UNAVAILABLE`
## Deprecated Endpoints
The following V1 endpoints are **not available** in V2:
| Endpoint | Notes |
| ------------------ | ------------------------------- |
| `POST /chat` | Chat completions — deprecated |
| `GET /chat/models` | Chat model listing — deprecated |
## Quick Migration Checklist
1. Update base URL to `https://api-v2.qbraid.com/api/v1`
2. Change auth header from `api-key` to `X-API-KEY`
3. Replace `qbraidDeviceId` with `deviceQrn` using QRN format
4. Wrap program in `{ "format": "...", "data": "..." }` object
5. Update route paths (`/quantum-jobs` → `/jobs`, `/quantum-devices` → `/devices`)
6. Handle new response envelope (`response.data` instead of raw payload)
7. Remove any chat endpoint integrations
# Create Job
Source: https://docs.qbraid.com/v2/api-reference/rest/post-quantum-jobs
post /jobs
Submit a quantum job to the specified device.
**Request Body:**
- **program** (object, required): Quantum program with format and data fields
- **shots** (integer, required): Number of shots to execute
- **deviceQrn** (string, required): Device QRN (qBraid Resource Name)
- **runtimeOptions** (object, optional): Runtime options
- **tags** (object, optional): Job tags
- **name** (string, optional): Job name
**Headers:**
- **X-API-KEY** (string, required): Your qBraid API key
**Returns:**
- Object containing the new job QRN and initial status
**Raises:**
- **422**: Validation error if required fields are missing or invalid
```json 201 theme={"dark"}
{
"success": true,
"data": {
"jobQrn": "aws:aws:sim:dm1-a1b2-qjob-1234567890abcdef",
"status": "INITIALIZING"
}
}
```
# Overview
Source: https://docs.qbraid.com/v2/api-reference/user-guide/overview
Learn to work with qBraid APIs
Our API employs a [RESTful](https://en.wikipedia.org/wiki/REST) architecture, featuring intuitive,
resource-oriented URLs and leveraging inherent HTTP capabilities such as response codes, authentication,
and verbs.
We support cross-site requests from any domain and return responses in JSON format.
## Quantum Job Flow
The diagram below gives a high-level view of how a quantum job moves through an application that integrates with the qBraid API: from a high-level quantum program, to an intermediate representation (IR), to the qBraid API server, then to execution on a quantum backend, with results relayed back to the user.
For a client-side view of how the qBraid-SDK integrates with the qBraid API see the [Runtime overview](/v2/sdk/user-guide/runtime/components#runtime-overview) in the SDK docs.
## Rate limiting
API requests are rate-limited per IP (unauthenticated) or per user (authenticated) within a rolling 15-minute window.
| Tier | Window | Max requests |
| -------------------- | ------ | ------------ |
| Unauthenticated (IP) | 15 min | 100 |
| Authenticated user | 15 min | 1,000 |
Exceeding the limit returns an HTTP 429 (Too Many Requests) response. Authenticate with a valid API key to use the higher authenticated limit.
Higher rate limits for your [organization](/v2/account/organizations/overview)
are available upon request.
# qbraid account
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_account
Manage qBraid account.
View account information and check remaining credits.
**Usage**:
```console theme={"dark"}
$ qbraid account [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `credits`: Get number of qBraid credits remaining.
## qbraid account credits
Get number of qBraid credits remaining.
**Usage**:
```console theme={"dark"}
$ qbraid account credits [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
# qbraid admin
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_admin
CI/CD commands for qBraid maintainers.
**Usage**:
```console theme={"dark"}
$ qbraid admin [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `headers`: Verify and fix qBraid file headers.
## qbraid admin headers
Verify and fix qBraid file headers.
Verifies and optionally fixes qBraid headers in specified files and directories.
**Usage**:
```console theme={"dark"}
$ qbraid admin headers [OPTIONS] {src_paths}...
```
**Arguments**:
* `src_paths...`: Source file or directory paths to verify. \[required]
**Options**:
* `-t, --type `: Type of header to use. \[default: default]
* `-s, --skip `: Files to skip during verification.
* `-f, --fix`: Whether to fix the headers instead of just verifying.
* `-p, --project `: Name of the project to use in the header.
* `--help`: Show this message and exit.
# qbraid agents
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_agents
Manage AI coding agent sessions.
Launch, monitor, and control AI coding agents.
Run Claude Code, OpenCode, or Codex in managed tmux sessions. Send
instructions, stream transcripts, handle permissions, and orchestrate
multiple agents — all from one place. Works locally and on remote
compute servers via SSH.
Use this when you want to:
* Spin up agents on specific repos or projects
* Watch what agents are doing in real-time
* Approve or deny tool-use permissions remotely
* Manage agents running on cloud compute machines
* Resume previous coding sessions
Getting started:
```bash theme={"dark"}
qbraid agents launch # Interactive launch
qbraid agents list # Show active sessions
qbraid agents watch # Stream live activity
qbraid agents send --text "..." # Send instructions
```
**Usage**:
```console theme={"dark"}
$ qbraid agents [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `register`: Register an agent session.
* `deregister`: Deregister an agent session.
* `list`: List agent sessions.
* `discover`: Discover new agent sessions.
* `status`: Show detailed status for an agent session.
* `update`: Update metadata fields on an agent session.
* `kill`: Stop an agent by name, session ID, or PID.
* `launch`: Launch an AI coding agent.
* `resume`: Resume a previous agent session.
* `send`: Send text to a running agent's terminal.
* `read`: Read recent terminal output from an agent.
* `approve`: Approve a tool-use prompt.
* `watch`: Stream agent transcript.
* `attach`: Attach to an agent's terminal.
* `clean`: Clean up orphaned sessions.
* `send-to`: Send a message to another agent's inbox.
* `broadcast`: Broadcast to multiple agent inboxes.
* `team`: Show a team roster with unread counts.
* `hooks`: Manage agent lifecycle hooks.
* `inbox`: Read messages sent to this agent's inbox.
## qbraid agents register
Register an agent session.
Reads session data from stdin JSON (used by hooks).
**Usage**:
```console theme={"dark"}
$ qbraid agents register [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
## qbraid agents deregister
Deregister an agent session.
Reads session data from stdin JSON (used by hooks).
**Usage**:
```console theme={"dark"}
$ qbraid agents deregister [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
## qbraid agents list
List agent sessions.
Use --compute to list on a remote server.
**Usage**:
```console theme={"dark"}
$ qbraid agents list [OPTIONS]
```
**Options**:
* `-t, --tool `: Filter by tool (claude, codeq, codex)
* `--status `: Filter by status
* `--tag `: Filter by tag (repeatable)
* `--since `: Time window (e.g., '1h', '30m', '2d')
* `-c, --compute `: SSH alias for remote compute (runs list on remote)
* `-s, --search `: Text search across name/cwd/tags
* `-a, --all`: Include stopped sessions
* `-l, --limit `: Entries per page \[default: 25; x>=1]
* `-p, --page `: Page number (non-interactive mode) \[default: 1; x>=1]
* `--sort `: Sort by: recent, oldest, name, tool, status, cwd, model \[default: recent]
* `--no-interactive`: Disable interactive pagination
* `--json`: Output as JSON array (for programmatic use)
* `--help`: Show this message and exit.
## qbraid agents discover
Discover new agent sessions.
Scans transcript files and registers new agent sessions.
**Usage**:
```console theme={"dark"}
$ qbraid agents discover [OPTIONS]
```
**Options**:
* `-f, --full`: Full data enrichment for all sessions (slower)
* `-c, --compute `: Sync sessions from remote compute server (SSH alias)
* `--help`: Show this message and exit.
## qbraid agents status
Show detailed status for an agent session.
**Usage**:
```console theme={"dark"}
$ qbraid agents status [OPTIONS] {session_id}
```
**Arguments**:
* `session_id`: Session ID (full or prefix) \[required]
**Options**:
* `--help`: Show this message and exit.
## qbraid agents update
Update metadata fields on an agent session.
**Usage**:
```console theme={"dark"}
$ qbraid agents update [OPTIONS] {session_id}
```
**Arguments**:
* `session_id`: Session ID (full or prefix) \[required]
**Options**:
* `--tag `: Tag to add (can be repeated)
* `--name `: Set terminal name for the session
* `--model `: Set model name (e.g. claude-sonnet-4-20250514)
* `--cwd `: Set working directory for the session
* `--status `: Set session status (working, waiting, idle, error, stopped)
* `--help`: Show this message and exit.
## qbraid agents kill
Stop an agent by name, session ID, or PID.
**Usage**:
```console theme={"dark"}
$ qbraid agents kill [OPTIONS] {target}
```
**Arguments**:
* `target`: Agent name, ID prefix, or PID \[required]
**Options**:
* `-f, --force`: Use SIGKILL instead of SIGTERM
* `--help`: Show this message and exit.
## qbraid agents launch
Launch an AI coding agent.
Starts the agent in a managed tmux session.
**Usage**:
```console theme={"dark"}
$ qbraid agents launch [OPTIONS]
```
**Options**:
* `-t, --tool `: Agent tool (claude, claude-auto, codeq, codex)
* `--cwd `: Working directory
* `-n, --name `: Agent name
* `-i, --instructions `: Initial instructions
* `-r, --repo `: GitHub repo URL, org/repo, or issue/PR URL
* `--type `: Agent type: leader, worker, observer, reviewer
* `--parent `: Parent session ID
* `--team `: Team ID
* `--tag `: Tags (repeatable)
* `-c, --compute `: SSH alias for remote compute server
* `--help`: Show this message and exit.
## qbraid agents resume
Resume a previous agent session.
Resumes in a managed tmux window.
**Usage**:
```console theme={"dark"}
$ qbraid agents resume [OPTIONS] [session_id]
```
**Arguments**:
* `session_id`: Session ID, prefix, or name. Omit for interactive picker.
**Options**:
* `-n, --name `: Agent name for the resumed session
* `--help`: Show this message and exit.
## qbraid agents send
Send text to a running agent's terminal.
**Usage**:
```console theme={"dark"}
$ qbraid agents send [OPTIONS] {session_id}
```
**Arguments**:
* `session_id`: Session ID (prefix match) \[required]
**Options**:
* `-m, --text `: Text to send \[required]
* `-c, --compute `: SSH alias for remote compute
* `--help`: Show this message and exit.
## qbraid agents read
Read recent terminal output from an agent.
**Usage**:
```console theme={"dark"}
$ qbraid agents read [OPTIONS] {session_id}
```
**Arguments**:
* `session_id`: Session ID (prefix match) \[required]
**Options**:
* `-n, --lines `: Number of lines to capture \[default: 50]
* `-c, --compute `: SSH alias for remote compute
* `--help`: Show this message and exit.
## qbraid agents approve
Approve a tool-use prompt.
Sends 'y' + Enter to the agent's terminal.
**Usage**:
```console theme={"dark"}
$ qbraid agents approve [OPTIONS] {session_id}
```
**Arguments**:
* `session_id`: Session ID (prefix match) \[required]
**Options**:
* `--help`: Show this message and exit.
## qbraid agents watch
Stream agent transcript.
Streams live transcript from an agent session.
**Usage**:
```console theme={"dark"}
$ qbraid agents watch [OPTIONS] {session_id}
```
**Arguments**:
* `session_id`: Session ID (prefix match) \[required]
**Options**:
* `-n, --lines `: Initial history lines \[default: 20]
* `--interactive / --no-interactive`: Prompt for permission decisions \[default: interactive]
* `--stream`: Stream-only mode: emit JSON events, no interactive prompts (for frontends)
* `-c, --compute `: SSH alias for remote compute
* `--help`: Show this message and exit.
## qbraid agents attach
Attach to an agent's terminal.
Opens an interactive tmux window for the agent session.
**Usage**:
```console theme={"dark"}
$ qbraid agents attach [OPTIONS] [session_id]
```
**Arguments**:
* `session_id`: Session ID (prefix match). Omit for interactive picker.
**Options**:
* `-c, --compute `: SSH alias for remote compute
* `--help`: Show this message and exit.
## qbraid agents clean
Clean up orphaned sessions.
Removes orphaned tmux windows and stopped sessions.
**Usage**:
```console theme={"dark"}
$ qbraid agents clean [OPTIONS]
```
**Options**:
* `-y, --yes`: Skip confirmation
* `--help`: Show this message and exit.
## qbraid agents send-to
Send a message to another agent's inbox.
For multi-recipient / team broadcast, use `qbraid agents broadcast`.
Delivery is a JSONL append at \~/.qbraid/inboxes/\.jsonl
(local host only — cross-host messaging isn't yet supported and will raise
a clear error). Permission rules (leader/worker/observer/reviewer) are
enforced by default.
**Usage**:
```console theme={"dark"}
$ qbraid agents send-to [OPTIONS] {to} {text}
```
**Arguments**:
* `to`: Target session id (full or prefix), tmux window name, or terminal name. \[required]
* `text`: Message body. \[required]
**Options**:
* `--from `: Sender session id. Defaults to \$QBRAID\_SESSION\_ID (set by the qBraid launcher for every managed agent shell).
* `--type `: One of: message, task\_assignment, status\_update, shutdown\_request. Controls how the recipient's UI/hooks categorize it. \[default: message]
* `--no-permission-check`: Skip the sender→target permission check (agent-type rules from qbraid\_core.services.agents.types.can\_message). Use for testing.
* `--help`: Show this message and exit.
## qbraid agents broadcast
Broadcast to multiple agent inboxes.
Two selectors — pick exactly one:
* `--team <team_id>` — every session with that team\_id (except sender)
* `--to A,B,C` — explicit id/prefix/name list
Delivery is all-or-nothing on target resolution: if any target can't be
resolved, NOTHING is sent (avoids partial-delivery surprises). Once
resolved, per-target send failures are reported individually; a partial
delivery exits with code 3.
**Usage**:
```console theme={"dark"}
$ qbraid agents broadcast [OPTIONS] {text}
```
**Arguments**:
* `text`: Message body — delivered to every target. \[required]
**Options**:
* `--team `: Deliver to every session with this team\_id (excluding the sender). Use one of --team or --to.
* `--to `: Comma-separated list of target session ids/prefixes/names for multi-recipient send. Use one of --team or --to.
* `--from `: Sender session id. Defaults to \$QBRAID\_SESSION\_ID.
* `--type `: One of: message, task\_assignment, status\_update, shutdown\_request. Controls how the recipient's UI/hooks categorize it. \[default: message]
* `--no-permission-check`: Skip the sender→target permission check. Use for testing.
* `--help`: Show this message and exit.
## qbraid agents team
Show a team roster with unread counts.
Answers the leader-orchestrator question "who is on my team, how many
unread messages do they have, and what was the last thing they heard from
anyone?" — which today requires peeking each worker's inbox individually.
Rows are sorted with the current session first (identified via
\$QBRAID\_SESSION\_ID), then by agent\_type (leader → worker → observer →
reviewer), then by session\_id.
**Usage**:
```console theme={"dark"}
$ qbraid agents team [OPTIONS] {team_id}
```
**Arguments**:
* `team_id`: Team identifier (matches AgentSession.team\_id). \[required]
**Options**:
* `-w, --wide`: Show full session ids instead of the 12-char prefix.
* `--help`: Show this message and exit.
## qbraid agents hooks
Manage agent lifecycle hooks.
**Usage**:
```console theme={"dark"}
$ qbraid agents hooks [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `install`: Install agent lifecycle hooks.
* `uninstall`: Remove agent lifecycle hooks.
### qbraid agents hooks install
Install agent lifecycle hooks.
Installs hooks for AI coding tools.
**Usage**:
```console theme={"dark"}
$ qbraid agents hooks install [OPTIONS]
```
**Options**:
* `-t, --tool `: Install hooks for a specific tool (claude, codeq, codex)
* `--help`: Show this message and exit.
### qbraid agents hooks uninstall
Remove agent lifecycle hooks.
Removes hooks from all supported tools.
**Usage**:
```console theme={"dark"}
$ qbraid agents hooks uninstall [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
## qbraid agents inbox
Read messages sent to this agent's inbox.
Follow, filter unread, and mark-read messages sent to this agent
session's inbox. Inboxes are per-session JSONL files at
\~/.qbraid/inboxes/. Every agent session has one; peers use
`qbraid agents send-to` to write to it.
**Usage**:
```console theme={"dark"}
$ qbraid agents inbox [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `-s, --session `: Session whose inbox to read. Accepts full id, prefix, or tmux/terminal name (resolved via the registry). Defaults to \$QBRAID\_SESSION\_ID.
* `-f, --follow`: Stream new messages as they arrive.
* `-u, --unread`: Show only unread messages.
* `-n, --limit `: Max messages to show (most recent). \[default: 50]
* `--mark-read / --no-mark-read`: Mark shown messages as read after display. Default on. \[default: mark-read]
* `-w, --wide`: Show full session ids in the From column and title (not the 12-char prefix). Use when multiple senders share a prefix.
* `--help`: Show this message and exit.
**Commands**:
* `mark-read`: Mark inbox messages as read.
### qbraid agents inbox mark-read
Mark inbox messages as read.
Without --message-id, marks every unread message in the session's inbox.
**Usage**:
```console theme={"dark"}
$ qbraid agents inbox mark-read [OPTIONS]
```
**Options**:
* `-s, --session `: Session whose inbox to update. Accepts full id, prefix, or tmux/terminal name. Defaults to \$QBRAID\_SESSION\_ID.
* `-m, --message-id `: Specific message id(s) to mark. May be passed multiple times. Omit to mark ALL unread messages in the inbox.
* `--help`: Show this message and exit.
# qbraid compute
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_compute
Manage cloud compute and SSH.
Manage cloud compute servers, profiles, and SSH access.
Start GPU or CPU servers on qBraid infrastructure. SSH in from any
terminal, connect your IDE (VS Code, Cursor), or launch AI agents
on remote hardware. Persistent storage across sessions.
Use this when you want to:
* Run heavy workloads on cloud GPUs without local setup
* SSH into a cloud dev environment from your terminal
* Connect VS Code or Cursor to a remote server
* Launch AI agents on more powerful hardware
Quick start:
```bash theme={"dark"}
qbraid compute up # Start server + configure SSH
qbraid compute status # Check server status
qbraid compute ssh connect # SSH into running server
qbraid compute down # Stop server
```
**Usage**:
```console theme={"dark"}
$ qbraid compute [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `up`: Start server and configure SSH.
* `down`: Stop the running server.
* `status`: Check server status.
* `list`: List compute profiles.
* `sessions`: List compute sessions.
* `usage`: Show compute usage summary.
* `profiles`: Manage compute profiles.
* `server`: Manage compute servers.
* `ssh`: SSH access to compute servers.
## qbraid compute up
Start server and configure SSH.
Waits for the server to be ready. This is a convenience command that combines:
* qbraid compute server start \ --wait
* qbraid compute ssh setup
Example:
```bash theme={"dark"}
qbraid compute up 2vCPU_4GB
```
**Usage**:
```console theme={"dark"}
$ qbraid compute up [OPTIONS] {profile_slug}
```
**Arguments**:
* `profile_slug`: Profile slug to start \[required]
**Options**:
* `-c, --cluster `: Cluster ID
* `-t, --timeout `: Server start timeout \[default: 120]
* `--no-ssh`: Skip SSH configuration
* `--help`: Show this message and exit.
## qbraid compute down
Stop the running server.
Example:
```bash theme={"dark"}
qbraid compute down
qbraid compute down --cleanup-ssh
```
**Usage**:
```console theme={"dark"}
$ qbraid compute down [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `-f, --force`: Skip confirmation
* `--cleanup-ssh`: Remove SSH config
* `--help`: Show this message and exit.
## qbraid compute status
Check server status.
Quick status check (alias for 'server status').
**Usage**:
```console theme={"dark"}
$ qbraid compute status [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `--help`: Show this message and exit.
## qbraid compute list
List compute profiles.
Quick list profiles (alias for 'profiles list').
**Usage**:
```console theme={"dark"}
$ qbraid compute list [OPTIONS]
```
**Options**:
* `-g, --gpu`: Show only GPU profiles
* `-a, --available`: Show only available profiles
* `-l, --limit `: Number of results \[default: 20]
* `--help`: Show this message and exit.
## qbraid compute sessions
List compute sessions.
**Usage**:
```console theme={"dark"}
$ qbraid compute sessions [OPTIONS]
```
**Options**:
* `-l, --limit `: Number of sessions to show \[default: 10]
* `-s, --status `: Filter by status (active, ended)
* `-t, --type `: Filter by type (cpu, gpu)
* `-d, --days `: Show sessions from last N days
* `--help`: Show this message and exit.
## qbraid compute usage
Show compute usage summary.
**Usage**:
```console theme={"dark"}
$ qbraid compute usage [OPTIONS]
```
**Options**:
* `-d, --days `: Number of days (default: 30)
* `--tz `: Timezone (e.g. America/New\_York)
* `--daily`: Show daily breakdown
* `--help`: Show this message and exit.
## qbraid compute profiles
Manage compute profiles.
**Usage**:
```console theme={"dark"}
$ qbraid compute profiles [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `list`: List available compute profiles.
* `info`: Get detailed profile information.
* `create-try`: Create a try-mode profile.
* `create-save`: Create a save-mode profile.
* `build-status`: Check build status for a save-mode profile.
* `delete`: Delete a user-owned profile.
### qbraid compute profiles list
List available compute profiles.
**Usage**:
```console theme={"dark"}
$ qbraid compute profiles list [OPTIONS]
```
**Options**:
* `-p, --plan `: Filter by plan tier
* `-g, --gpu`: Filter GPU profiles
* `-c, --has-capacity`: Filter by capacity
* `--cluster `: Filter by cluster
* `-l, --limit `: Results per page \[default: 50]
* `--help`: Show this message and exit.
### qbraid compute profiles info
Get detailed profile information.
**Usage**:
```console theme={"dark"}
$ qbraid compute profiles info [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Profile slug \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid compute profiles create-try
Create a try-mode profile.
Standard+ plan required.
Try-mode profiles use on-the-fly overlay injection - no build step required.
**Usage**:
```console theme={"dark"}
$ qbraid compute profiles create-try [OPTIONS]
```
**Options**:
* `-i, --image `: Base Docker image (required) \[required]
* `-n, --name `: Display name
* `-d, --description `: Description
* `--cpu `: CPU cores (guarantee and limit)
* `-m, --mem `: Memory (e.g., '8G')
* `-g, --gpu`: Enable GPU
* `--ide `: IDE type: lab, vscode, ssh \[default: lab]
* `-f, --features `: Features (e.g., 'claude,codeq')
* `--help`: Show this message and exit.
### qbraid compute profiles create-save
Create a save-mode profile.
Pro+ plan required.
Save-mode profiles trigger a Cloud Build to create a permanent overlay image.
Use --wait to wait for the build to complete.
**Usage**:
```console theme={"dark"}
$ qbraid compute profiles create-save [OPTIONS]
```
**Options**:
* `-i, --image `: Base Docker image (required) \[required]
* `-n, --name `: Display name
* `-d, --description `: Description
* `--cpu `: CPU cores (guarantee and limit)
* `-m, --mem `: Memory (e.g., '8G')
* `-g, --gpu`: Enable GPU
* `--ide `: IDE type: lab, vscode, ssh \[default: lab]
* `-f, --features `: Features (e.g., 'claude,codeq')
* `-w, --wait`: Wait for build to complete
* `-t, --timeout `: Build timeout in seconds \[default: 600]
* `--help`: Show this message and exit.
### qbraid compute profiles build-status
Check build status for a save-mode profile.
**Usage**:
```console theme={"dark"}
$ qbraid compute profiles build-status [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Profile slug \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid compute profiles delete
Delete a user-owned profile.
**Usage**:
```console theme={"dark"}
$ qbraid compute profiles delete [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Profile slug to delete \[required]
**Options**:
* `-f, --force`: Skip confirmation
* `--help`: Show this message and exit.
## qbraid compute server
Manage compute servers.
**Usage**:
```console theme={"dark"}
$ qbraid compute server [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `status`: Check server status.
* `start`: Start a server.
* `stop`: Stop a running server or on-demand instance.
* `terminate`: Terminate (delete) an on-demand instance.
* `url`: Print the full server URL with auth token.
* `stop-all`: Stop all servers across all organizations.
### qbraid compute server status
Check server status.
**Usage**:
```console theme={"dark"}
$ qbraid compute server status [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `--show-url`: Show full server URL with auth token
* `--help`: Show this message and exit.
### qbraid compute server start
Start a server.
Pass a profile slug to provision a new instance, or an existing on-demand
instance ID to resume a stopped one.
**Usage**:
```console theme={"dark"}
$ qbraid compute server start [OPTIONS] {target}
```
**Arguments**:
* `target`: Profile slug (new instance) or instance ID (resume a stopped one) \[required]
**Options**:
* `-c, --cluster `: Cluster ID
* `-w, --wait`: Wait for server to be ready
* `-t, --timeout `: Wait timeout in seconds \[default: 120]
* `--ssh`: Configure SSH after start
* `--help`: Show this message and exit.
### qbraid compute server stop
Stop a running server or on-demand instance.
On-demand BMA instances are detected automatically. If more than one
is running, you'll be prompted to choose; pass --instance to target one
directly. With no BMA instances, stops the legacy cluster server.
**Usage**:
```console theme={"dark"}
$ qbraid compute server stop [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `-i, --instance `: BMA instance ID to stop (auto-detected if omitted)
* `-f, --force`: Skip confirmation
* `--help`: Show this message and exit.
### qbraid compute server terminate
Terminate (delete) an on-demand instance.
DESTRUCTIVE: unlike 'stop' (which preserves the disk and is resumable),
terminate permanently deletes the instance AND its disk — data cannot be
recovered. Applies to on-demand BMA instances only.
**Usage**:
```console theme={"dark"}
$ qbraid compute server terminate [OPTIONS]
```
**Options**:
* `-i, --instance `: On-demand instance ID to terminate
* `-f, --force`: Skip confirmations (DANGEROUS)
* `--help`: Show this message and exit.
### qbraid compute server url
Print the full server URL with auth token.
Use this to get a clickable URL for browser access or to pass
the token to other tools. The token is intentionally shown in full.
**Usage**:
```console theme={"dark"}
$ qbraid compute server url [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `--help`: Show this message and exit.
### qbraid compute server stop-all
Stop all servers across all organizations.
**Usage**:
```console theme={"dark"}
$ qbraid compute server stop-all [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `-f, --force`: Skip confirmation
* `--help`: Show this message and exit.
## qbraid compute ssh
SSH access to compute servers.
**Usage**:
```console theme={"dark"}
$ qbraid compute ssh [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `setup`: Configure SSH for the server.
* `connect`: SSH into the running server.
* `token`: Get SSH session token.
* `config`: Show or edit SSH configuration.
* `remove`: Remove SSH configuration for a host.
### qbraid compute ssh setup
Configure SSH for the server.
Sets up SSH config with a ProxyCommand that tunnels SSH through
the jupyter-server-proxy WebSocket endpoint. No websocat required.
On-demand BMA instances are handled automatically: if one is running
it's configured under its own alias; pass --instance to target a specific
one. Each instance gets an independent alias so several can be reachable at
once.
**Usage**:
```console theme={"dark"}
$ qbraid compute ssh setup [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `-i, --instance `: On-demand instance ID (auto-detected if omitted)
* `-t, --tool `: Configure for specific tool: vscode, cursor, claude-code, codex
* `-a, --alias `: SSH config alias (default: qbraid-\{cluster})
* `--help`: Show this message and exit.
### qbraid compute ssh connect
SSH into the running server.
This opens an interactive SSH session to the server. Pass --instance to
connect to a specific on-demand instance (resolves to its per-instance
alias from 'ssh setup --instance').
**Usage**:
```console theme={"dark"}
$ qbraid compute ssh connect [OPTIONS]
```
**Options**:
* `-a, --alias `: SSH config alias (auto-detected if not set)
* `-i, --instance `: On-demand instance ID to connect to
* `--help`: Show this message and exit.
### qbraid compute ssh token
Get SSH session token.
Retrieves a JupyterHub session token for SSH access.
**Usage**:
```console theme={"dark"}
$ qbraid compute ssh token [OPTIONS]
```
**Options**:
* `-c, --cluster `: Cluster ID
* `-r, --raw`: Output raw token only
* `--help`: Show this message and exit.
### qbraid compute ssh config
Show or edit SSH configuration.
**Usage**:
```console theme={"dark"}
$ qbraid compute ssh config [OPTIONS]
```
**Options**:
* `-e, --edit`: Open config in editor
* `--help`: Show this message and exit.
### qbraid compute ssh remove
Remove SSH configuration for a host.
**Usage**:
```console theme={"dark"}
$ qbraid compute ssh remove [OPTIONS] {alias}
```
**Arguments**:
* `alias`: SSH alias to remove \[required]
**Options**:
* `-f, --force`: Skip confirmation
* `--help`: Show this message and exit.
# qbraid configure
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_configure
Configure CLI settings.
Set API keys and config values for your qBraid CLI.
Use this when you want to:
* Set your qBraid API key
* Enable qBraid magic commands in Jupyter
**Usage**:
```console theme={"dark"}
$ qbraid configure [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `set`: Set configuration value in qbraidrc file.
* `get`: Get configuration value from qbraidrc file.
* `list`: List all configuration values in qbraidrc.
* `magic`: Enable qBraid IPython magic commands.
## qbraid configure set
Set configuration value in qbraidrc file.
**Usage**:
```console theme={"dark"}
$ qbraid configure set [OPTIONS] {name} {value}
```
**Arguments**:
* `name`: Config name \[required]
* `value`: Config value \[required]
**Options**:
* `-p, --profile `: Profile name \[default: default]
* `--help`: Show this message and exit.
## qbraid configure get
Get configuration value from qbraidrc file.
**Usage**:
```console theme={"dark"}
$ qbraid configure get [OPTIONS] {name}
```
**Arguments**:
* `name`: Config name \[required]
**Options**:
* `-p, --profile `: Profile name \[default: default]
* `--help`: Show this message and exit.
## qbraid configure list
List all configuration values in qbraidrc.
**Usage**:
```console theme={"dark"}
$ qbraid configure list [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
## qbraid configure magic
Enable qBraid IPython magic commands.
**Usage**:
```console theme={"dark"}
$ qbraid configure magic [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
# qbraid devices
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_devices
Browse quantum devices.
Browse and query quantum devices across providers.
Access quantum hardware and simulators from IonQ, IBM, AWS Braket,
Rigetti, QuEra, Pasqal, and more through the unified qBraid runtime.
Use this when you want to:
* Find available quantum devices and their current status
* Compare pricing, qubit counts, and queue depths
* Check device specifications before submitting jobs
**Usage**:
```console theme={"dark"}
$ qbraid devices [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `list`: List qBraid quantum devices.
* `get`: Get a qBraid quantum device.
## qbraid devices list
List qBraid quantum devices.
**Usage**:
```console theme={"dark"}
$ qbraid devices list [OPTIONS]
```
**Options**:
* `-s, --status `: Filter by status: ONLINE, UNAVAILABLE, OFFLINE, RETIRED
* `-t, --type `: 'QPU'|'SIMULATOR'
* `-v, --vendor `: Filter by vendor: AWS, AZURE, IBM, IONQ, QBRAID
* `-p, --provider `: Filter by provider: AQT, AWS, AZURE, EQUAL1, IBM, IQM, IONQ, NEC, OQC, PASQAL, QUANTINUUM, QUERA, RIGETTI, QBRAID
* `--help`: Show this message and exit.
## qbraid devices get
Get a qBraid quantum device.
**Usage**:
```console theme={"dark"}
$ qbraid devices get [OPTIONS] {device_id}
```
**Arguments**:
* `device_id`: The QRN or ID of the device to get. \[required]
**Options**:
* `--no-fmt`: Disable rich console formatting (output raw data) \[default: True]
* `--help`: Show this message and exit.
# qbraid envs
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_envs
Manage Python environments.
Create, install, upload, and share Python environments.
Isolated Python environments with automatic Jupyter kernel integration.
Create environments locally, upload them to the cloud, share with
teammates, or install pre-built environments from the catalog. Works
identically on qBraid Lab and local machines.
Use this when you want to:
* Set up a reproducible Python environment for a project
* Share your environment with collaborators
* Install a pre-configured environment (e.g., qBraid SDK, Qiskit)
* Publish an environment to the public catalog
Quick start:
```bash theme={"dark"}
qbraid envs create # Create new environment
qbraid envs available # Browse installable environments
qbraid envs install # Install from cloud
qbraid envs list # List local environments
qbraid envs upload # Upload to cloud
```
**Usage**:
```console theme={"dark"}
$ qbraid envs [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `create`: Create a new qBraid environment.
* `uninstall`: Remove a local environment.
* `delete`: (Admin) Delete a cloud environment.
* `list`: List installed qBraid environments.
* `activate`: Activate qBraid environment.
* `available`: List installable environments.
* `info`: Show environment details.
* `install`: Install an environment.
* `upload`: Upload an environment.
* `health`: Check the health of an environment.
* `repair`: Repair a broken environment.
* `pythons`: List available Python versions.
* `config`: View or update environment configuration.
* `publish`: Manage environment publishing.
* `share`: Manage environment sharing via share codes.
* `groups`: Manage environment groups.
* `registry`: Manage the local environment registry.
## qbraid envs create
Create a new qBraid environment.
Run without options for an interactive walkthrough, or provide options via flags.
**Usage**:
```console theme={"dark"}
$ qbraid envs create [OPTIONS]
```
**Options**:
* `-n, --name `: Name of the environment to create
* `-d, --description `: Short description of the environment
* `-l, --logo `: Company name for logo (e.g., 'ibm', 'google')
* `-r, --requirements `: Path to requirements.txt file
* `-k, --kernel-name `: Display name for Jupyter kernel
* `-t, --tags `: Comma-separated tags
* `-y, --yes`: Automatically answer 'yes' to all prompts
* `--help`: Show this message and exit.
## qbraid envs uninstall
Remove a local environment.
By default this removes only the local copy — the cloud environment is
preserved so you can reinstall it later. Pass `--delete-cloud` to also
retire the cloud environment (requires ownership).
**Usage**:
```console theme={"dark"}
$ qbraid envs uninstall [OPTIONS]
```
**Options**:
* `-n, --name `: Name of the environment to remove \[required]
* `-c, --delete-cloud`: Also delete the environment from qBraid cloud (requires ownership). By default only the local copy is removed and the cloud slug is preserved, so you can reinstall it later.
* `-y, --yes`: Automatically answer 'yes' to all prompts
* `--help`: Show this message and exit.
## qbraid envs delete
(Admin) Delete a cloud environment.
Removes the DB entry and storage files.
**Usage**:
```console theme={"dark"}
$ qbraid envs delete [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Slug of the cloud environment to delete \[required]
**Options**:
* `-y, --yes`: Automatically answer 'yes' to all prompts
* `--help`: Show this message and exit.
## qbraid envs list
List installed qBraid environments.
**Usage**:
```console theme={"dark"}
$ qbraid envs list [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
## qbraid envs activate
Activate qBraid environment.
NOTE: Currently only works on qBraid Lab platform, and select few other OS types.
**Usage**:
```console theme={"dark"}
$ qbraid envs activate [OPTIONS] {name}
```
**Arguments**:
* `name`: Name of the environment. Values from 'qbraid envs list'. \[required]
**Options**:
* `--help`: Show this message and exit.
## qbraid envs available
List installable environments.
Shows available pre-built environments for installation.
**Usage**:
```console theme={"dark"}
$ qbraid envs available [OPTIONS]
```
**Options**:
* `-p, --page `: Page number for pagination (default: 1)
* `-l, --limit `: Number of environments per page (default: 20)
* `--help`: Show this message and exit.
## qbraid envs info
Show environment details.
Get detailed information about an environment.
Accepts either a local env\_id or a cloud slug. Shows information from
both local registry and cloud catalog when available.
Examples:
```bash theme={"dark"}
$ qbraid envs info qiskit_1.0 # By slug (cloud + local if installed)
$ qbraid envs info a1b2 # By env_id (local only)
$ qbraid envs info my_custom_abc123 # Custom environment
```
**Usage**:
```console theme={"dark"}
$ qbraid envs info [OPTIONS] {identifier}
```
**Arguments**:
* `identifier`: Environment identifier (env\_id or slug) \[required]
**Options**:
* `--help`: Show this message and exit.
## qbraid envs install
Install an environment.
Installs a pre-built environment from cloud storage.
If the environment was built for a different platform or requires a Python
version not available on your system, you'll be offered options to either
create a similar local environment or proceed with the download anyway.
**Usage**:
```console theme={"dark"}
$ qbraid envs install [OPTIONS] {env_slug}
```
**Arguments**:
* `env_slug`: Environment slug to install (from 'qbraid envs available') \[required]
**Options**:
* `-t, --temp`: Install as temporary environment (faster, non-persistent)
* `--target `: Custom target directory (defaults to \~/.qbraid/environments)
* `-y, --yes`: Automatically proceed with default options without prompting
* `--help`: Show this message and exit.
## qbraid envs upload
Upload an environment.
Upload environment to qBraid cloud storage (private by default).
This uploads your local environment to make it available for sharing
with specific users. Use 'qbraid envs publish request' after upload to request
public visibility.
Examples:
```bash theme={"dark"}
$ qbraid envs upload my_custom_env
$ qbraid envs upload a1b2 --overwrite
$ qbraid envs upload my_env --group quantum_sdk
$ qbraid envs upload my_env --slug existing_slug_abc123 # Re-upload
```
**Usage**:
```console theme={"dark"}
$ qbraid envs upload [OPTIONS] {identifier}
```
**Arguments**:
* `identifier`: Environment name (if unique) or env\_id to upload \[required]
**Options**:
* `-s, --slug `: Re-upload to existing slug (replaces previous upload)
* `-g, --group `: Environment group to add this environment to
* `-o, --overwrite`: Overwrite existing uploaded environment
* `-y, --yes`: Skip confirmation prompts
* `--help`: Show this message and exit.
## qbraid envs health
Check the health of an environment.
This performs comprehensive checks on the virtual environment:
* Python symlinks and executables
* pyvenv.cfg configuration
* Activate scripts
* Kernel configuration (if present)
Examples:
```bash theme={"dark"}
$ qbraid envs health my_env
$ qbraid envs health a1b2
```
**Usage**:
```console theme={"dark"}
$ qbraid envs health [OPTIONS] {identifier}
```
**Arguments**:
* `identifier`: Environment name or env\_id to check \[required]
**Options**:
* `--help`: Show this message and exit.
## qbraid envs repair
Repair a broken environment.
This attempts to fix common issues:
* Broken Python symlinks
* Invalid pyvenv.cfg paths
* Broken activate scripts
* Invalid kernel.json paths
Use --auto-install to automatically install Python via uv if the
required version is not available.
Examples:
```bash theme={"dark"}
$ qbraid envs repair my_env
$ qbraid envs repair a1b2 --auto-install
```
**Usage**:
```console theme={"dark"}
$ qbraid envs repair [OPTIONS] {identifier}
```
**Arguments**:
* `identifier`: Environment name or env\_id to repair \[required]
**Options**:
* `-a, --auto-install`: Automatically install Python via uv if needed
* `--help`: Show this message and exit.
## qbraid envs pythons
List available Python versions.
Shows all Python installations found on the system, including:
* System Python (PATH)
* uv-managed Python
* Conda environments
* Homebrew (macOS)
* pyenv installations
Use this to see which Python versions are available for creating
new environments.
Examples:
```bash theme={"dark"}
$ qbraid envs pythons
```
**Usage**:
```console theme={"dark"}
$ qbraid envs pythons [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
## qbraid envs config
View or update environment configuration.
Without flags, prints the current config. With flags, applies the updates
and prints the new state. Multiple update flags can be combined in one call.
Examples:
```bash theme={"dark"}
$ qbraid envs config 5dcc
$ qbraid envs config myenv --description "Quantum sim env"
$ qbraid envs config myenv --tags "quantum,sim,gpu"
$ qbraid envs config myenv --add-tag gpu --add-tag cuda
$ qbraid envs config myenv --remove-tag deprecated
$ qbraid envs config myenv --system-site-packages true
```
**Usage**:
```console theme={"dark"}
$ qbraid envs config [OPTIONS] {environment}
```
**Arguments**:
* `environment`: Environment name, env\_id, or slug \[required]
**Options**:
* `-d, --description `: Update environment description
* `--tags `: Replace tags (comma-separated, use '' to clear)
* `--add-tag `: Append a tag (repeatable)
* `--remove-tag `: Remove a tag (repeatable)
* `--kernel-name `: Update Jupyter kernel display name
* `--system-site-packages `: Enable/disable inheriting packages from parent Python (true/false)
* `--help`: Show this message and exit.
## qbraid envs publish
Manage environment publishing.
**Usage**:
```console theme={"dark"}
$ qbraid envs publish [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `request`: Request to publish environment.
* `status`: Check publish status.
* `cancel`: Cancel a pending publish request.
* `review`: (Admin) Mark as under review.
* `approve`: (Admin) Approve and publish.
* `deny`: (Admin) Deny a publish request.
### qbraid envs publish request
Request to publish environment.
Submits your uploaded environment to the public catalog for admin review. Once approved,
it will be publicly visible and installable by all users.
The environment must be uploaded first with 'qbraid envs upload'.
Examples:
```bash theme={"dark"}
$ qbraid envs publish request my_env_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs publish request [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug (from upload) \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs publish status
Check publish status.
Check the publishing status of an environment.
Status values:
* none: Not submitted for publishing
* requested: Waiting for admin review
* pending: Under admin review
* approved: Published to public catalog
* denied: Publish request was denied
Examples:
```bash theme={"dark"}
$ qbraid envs publish status my_env_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs publish status [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug to check \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs publish cancel
Cancel a pending publish request.
This withdraws your publish request before it's approved.
You can resubmit later with 'qbraid envs publish request'.
Examples:
```bash theme={"dark"}
$ qbraid envs publish cancel my_env_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs publish cancel [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs publish review
(Admin) Mark as under review.
Transitions the environment from 'requested' to 'pending' status,
indicating an admin is actively reviewing it.
Examples:
```bash theme={"dark"}
$ qbraid envs publish review my_env_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs publish review [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug to review \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs publish approve
(Admin) Approve and publish.
Approve and publish an environment to the public catalog.
This makes the environment publicly visible and installable by all users.
After approval, you'll be prompted to add the environment to a group
if it's not already in one.
Examples:
```bash theme={"dark"}
$ qbraid envs publish approve my_env_abc123
$ qbraid envs publish approve my_env_abc123 --group qiskit
$ qbraid envs publish approve my_env_abc123 --no-group
```
**Usage**:
```console theme={"dark"}
$ qbraid envs publish approve [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug to approve \[required]
**Options**:
* `-g, --group `: Add environment to this group after approval
* `--no-group`: Skip the group assignment prompt
* `--help`: Show this message and exit.
### qbraid envs publish deny
(Admin) Deny a publish request.
The owner will be notified with the denial reason.
Examples:
```bash theme={"dark"}
$ qbraid envs publish deny my_env_abc123
$ qbraid envs publish deny my_env_abc123 --message "Missing documentation"
```
**Usage**:
```console theme={"dark"}
$ qbraid envs publish deny [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug to deny \[required]
**Options**:
* `-m, --message `: Reason for denial (sent to owner)
* `--help`: Show this message and exit.
## qbraid envs share
Manage environment sharing via share codes.
**Usage**:
```console theme={"dark"}
$ qbraid envs share [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `list`: List users with access.
* `create-code`: Generate a share code.
* `delete-code`: Delete the share code for an environment.
* `redeem-code`: Redeem a share code.
### qbraid envs share list
List users with access.
Shows all users with read or write access to the specified environment.
Examples:
```bash theme={"dark"}
$ qbraid envs share list my_env_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs share list [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug to check \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs share create-code
Generate a share code.
Creates an 8-character code that others can redeem with
'qbraid envs share redeem-code' to gain read access to your environment.
Only the owner can generate codes.
Generating a new code invalidates any previous code for this environment.
Examples:
```bash theme={"dark"}
$ qbraid envs share create-code my_env_abc123
Share code: ABC12XYZ
# Share the code with collaborators, they can redeem it with:
$ qbraid envs share redeem-code ABC12XYZ
```
**Usage**:
```console theme={"dark"}
$ qbraid envs share create-code [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug to generate share code for \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs share delete-code
Delete the share code for an environment.
Removes the share code, preventing new users from redeeming it.
Users who have already redeemed the code keep their access.
Examples:
```bash theme={"dark"}
$ qbraid envs share delete-code my_env_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs share delete-code [OPTIONS] {slug}
```
**Arguments**:
* `slug`: Environment slug to delete share code for \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs share redeem-code
Redeem a share code.
Redeem a share code to gain access to an environment.
Enter a share code received from an environment owner to gain read access.
The code is case-insensitive.
Examples:
```bash theme={"dark"}
$ qbraid envs share redeem-code ABC12XYZ
Access granted to: My Environment (my_env_abc123)
# After redeeming, you can install the environment:
$ qbraid envs install my_env_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs share redeem-code [OPTIONS] {code}
```
**Arguments**:
* `code`: Share code to redeem \[required]
**Options**:
* `--help`: Show this message and exit.
## qbraid envs groups
Manage environment groups.
**Usage**:
```console theme={"dark"}
$ qbraid envs groups [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `list`: List all environment groups.
* `info`: Show environment group details.
* `create`: (Admin) Create a new environment group.
* `update`: (Admin) Update an environment group.
* `delete`: (Admin) Delete an environment group.
### qbraid envs groups list
List all environment groups.
Environment groups are collections of related environments. Use the group
slug when uploading an environment to add it to a group.
Examples:
```bash theme={"dark"}
$ qbraid envs groups list
```
**Usage**:
```console theme={"dark"}
$ qbraid envs groups list [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
### qbraid envs groups info
Show environment group details.
Examples:
```bash theme={"dark"}
$ qbraid envs groups info quantum_sdk
```
**Usage**:
```console theme={"dark"}
$ qbraid envs groups info [OPTIONS] {group_slug}
```
**Arguments**:
* `group_slug`: Group slug to get details for \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid envs groups create
(Admin) Create a new environment group.
Environment groups are collections of related environments, typically
different versions of the same package or tool.
Examples:
```bash theme={"dark"}
$ qbraid envs groups create "Qiskit" --description "IBM Qiskit environments"
$ qbraid envs groups create "Cirq" --category package --visibility public
$ qbraid envs groups create "PennyLane" --envs "pen_1x0,pen_0x9" --latest pen_1x0
```
**Usage**:
```console theme={"dark"}
$ qbraid envs groups create [OPTIONS] {display_name}
```
**Arguments**:
* `display_name`: Display name for the group (1-100 chars) \[required]
**Options**:
* `-d, --description `: Group description (max 500 chars)
* `-t, --tags `: Comma-separated tags
* `-c, --category `: Category: 'package' or 'subject-matter'
* `-v, --visibility `: Visibility: 'private' or 'public'
* `-e, --envs `: Comma-separated environment slugs to include
* `-l, --latest `: Environment slug to set as the current version
* `--help`: Show this message and exit.
### qbraid envs groups update
(Admin) Update an environment group.
Use --add and --remove for delta updates to the environment list.
Use --latest to set which environment is the "current" version.
Examples:
```bash theme={"dark"}
$ qbraid envs groups update qiskit --add qiskit_1x0abc --latest qiskit_1x0abc
$ qbraid envs groups update cirq --remove cirq_0x8xyz,cirq_0x7def
$ qbraid envs groups update pennylane --name "PennyLane SDK" --visibility public
```
**Usage**:
```console theme={"dark"}
$ qbraid envs groups update [OPTIONS] {group_slug}
```
**Arguments**:
* `group_slug`: Slug of the group to update \[required]
**Options**:
* `-n, --name `: New display name
* `-d, --description `: New description
* `-a, --add `: Comma-separated environment slugs to add
* `-r, --remove `: Comma-separated environment slugs to remove
* `-l, --latest `: Set the current version environment slug
* `-c, --category `: Category: 'package' or 'subject-matter'
* `-v, --visibility `: Visibility: 'private' or 'public'
* `--help`: Show this message and exit.
### qbraid envs groups delete
(Admin) Delete an environment group.
This removes the group but does not delete the environments in it.
Examples:
```bash theme={"dark"}
$ qbraid envs groups delete old_group
$ qbraid envs groups delete old_group --yes
```
**Usage**:
```console theme={"dark"}
$ qbraid envs groups delete [OPTIONS] {group_slug}
```
**Arguments**:
* `group_slug`: Slug of the group to delete \[required]
**Options**:
* `-y, --yes`: Skip confirmation prompt
* `--help`: Show this message and exit.
## qbraid envs registry
Manage the local environment registry.
**Usage**:
```console theme={"dark"}
$ qbraid envs registry [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `add`: Register an external environment.
* `remove`: Unregister an external environment.
* `sync`: Sync the environment registry.
### qbraid envs registry add
Register an external environment.
Allows you to use existing Python environments (conda, venv, etc.)
with qBraid commands like kernel management and activation.
Examples:
```bash theme={"dark"}
$ qbraid envs registry add /path/to/my_env --alias myenv
$ qbraid envs registry add ~/conda/envs/quantum --name quantum_abc123
```
**Usage**:
```console theme={"dark"}
$ qbraid envs registry add [OPTIONS] {path}
```
**Arguments**:
* `path`: \[required]
**Options**:
* `-a, --alias `: Alias for the environment
* `-n, --name `: Name/slug for the environment
* `-y, --yes`
* `--help`: Show this message and exit.
### qbraid envs registry remove
Unregister an external environment.
This only removes the environment from qBraid's registry.
The actual environment files are NOT deleted.
**Usage**:
```console theme={"dark"}
$ qbraid envs registry remove [OPTIONS] {name}
```
**Arguments**:
* `name`: Name or alias of environment to unregister \[required]
**Options**:
* `-y, --yes`
* `--help`: Show this message and exit.
### qbraid envs registry sync
Sync the environment registry.
This will:
* Remove registry entries for deleted environments
* Auto-discover new environments in default paths
* Verify all registered paths still exist
**Usage**:
```console theme={"dark"}
$ qbraid envs registry sync [OPTIONS]
```
**Options**:
* `--help`: Show this message and exit.
# qbraid jobs
Source: https://docs.qbraid.com/v2/cli/api-reference/qbraid_jobs
Submit and track quantum computing jobs.
Monitor quantum jobs across all providers from a single interface.
View job status, results, and costs.
Use this when you want to:
* Check the status of submitted quantum jobs
* View results from completed experiments
* Track costs across providers
* Bundle related jobs into groups (qbraid jobs groups)
**Usage**:
```console theme={"dark"}
$ qbraid jobs [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `submit`: Submit a quantum job.
* `list`: List quantum jobs.
* `get`: Get a qBraid Quantum Job.
* `groups`: Manage job groups.
## qbraid jobs submit
Submit a quantum job.
Submit a quantum program to a device on the qBraid platform.
The program can be provided as a file path or piped via stdin.
Examples:
```bash theme={"dark"}
qbraid jobs submit circuit.qasm -d qbraid:qbraid:sim:qir-sv --shots 100
cat circuit.qasm | qbraid jobs submit - -d qbraid:qbraid:sim:qir-sv -f qasm2
qbraid jobs submit program.ll -d qbraid:qbraid:sim:qir-sv --json
```
**Usage**:
```console theme={"dark"}
$ qbraid jobs submit [OPTIONS] {program}
```
**Arguments**:
* `program`: Path to a quantum program file, or '-' to read from stdin. \[required]
**Options**:
* `-d, --device `: Target device QRN (e.g. 'qbraid:qbraid:sim:qir-sv'). \[required]
* `-f, --format `: Program format. Required when reading from stdin. Auto-detected from file extension when omitted. Options: qasm2, qasm3, qir.bc, qir.ll, analog, pulser.sequence, quil, ionq.circuit.v0, problem
* `-s, --shots `: Number of shots to execute. \[x>=1]
* `-n, --name `: Job name.
* `-t, --tags `: Job tags as a JSON object (e.g. '\{"experiment":"bell"}').
* `--options `: Runtime options as a JSON object.
* `--group `: Group job QRN to associate this job with.
* `--json`: Output full job response as JSON (for programmatic use).
* `--help`: Show this message and exit.
## qbraid jobs list
List quantum jobs.
Lists quantum jobs submitted through the qBraid platform.
**Usage**:
```console theme={"dark"}
$ qbraid jobs list [OPTIONS]
```
**Options**:
* `-l, --limit `: Limit the maximum number of results returned \[default: 10]
* `-s, --status `: Filter by status: INITIALIZING, QUEUED, VALIDATING, RUNNING, CANCELLING, CANCELLED, COMPLETED, FAILED, UNKNOWN, HOLD
* `-v, --vendor `: Filter by vendor: AWS, AZURE, IBM, IONQ, QBRAID
* `-p, --provider `: Filter by provider: AQT, AWS, AZURE, EQUAL1, IBM, IQM, IONQ, NEC, OQC, PASQAL, QUANTINUUM, QUERA, RIGETTI, QBRAID
* `--page `: Page number for pagination \[default: 1]
* `--help`: Show this message and exit.
## qbraid jobs get
Get a qBraid Quantum Job.
**Usage**:
```console theme={"dark"}
$ qbraid jobs get [OPTIONS] {job_id}
```
**Arguments**:
* `job_id`: The QRN or ID of the job to get. \[required]
**Options**:
* `--no-fmt`: Disable rich console formatting (output raw data) \[default: True]
* `--help`: Show this message and exit.
## qbraid jobs groups
Manage job groups.
A group bundles related jobs into one session so the runtime can
track aggregate cost and status. Submit into a group with
`qbraid jobs submit --group <qrn>`.
Quick start:
```bash theme={"dark"}
qbraid jobs groups create -n my-batch
qbraid jobs groups list
qbraid jobs groups info
qbraid jobs groups jobs
qbraid jobs groups close # no more submissions, jobs run
qbraid jobs groups cancel # cancel group + non-terminal jobs
qbraid jobs groups delete # remove the record
```
**Usage**:
```console theme={"dark"}
$ qbraid jobs groups [OPTIONS] COMMAND [ARGS]...
```
**Options**:
* `--help`: Show this message and exit.
**Commands**:
* `list`: List your job groups.
* `info`: Show full details for one group.
* `create`: Create a new group.
* `close`: Close a group to new submissions.
* `cancel`: Cancel a group and its non-terminal jobs.
* `jobs`: List the jobs in a group.
* `delete`: Delete a group record.
### qbraid jobs groups list
List your job groups.
**Usage**:
```console theme={"dark"}
$ qbraid jobs groups list [OPTIONS]
```
**Options**:
* `-s, --status `: Filter by status (OPEN/CLOSED/COMPLETED/FAILED/CANCELLED)
* `-p, --page `: Page number \[x>=1]
* `-l, --limit `: Results per page \[x>=1]
* `--help`: Show this message and exit.
### qbraid jobs groups info
Show full details for one group.
**Usage**:
```console theme={"dark"}
$ qbraid jobs groups info [OPTIONS] {qrn}
```
**Arguments**:
* `qrn`: Group QRN \[required]
**Options**:
* `--help`: Show this message and exit.
### qbraid jobs groups create
Create a new group.
Returns the new group's QRN — pass it to `qbraid jobs submit --group`
to attach jobs into the group.
**Usage**:
```console theme={"dark"}
$ qbraid jobs groups create [OPTIONS]
```
**Options**:
* `-n, --name `: Human-readable group name
* `-t, --tags `: Comma-separated key=value tags (values stored as strings)
* `-m, --metadata `: Comma-separated key=value metadata (values stored as strings)
* `--max-ttl