Metadata-Version: 2.4
Name: qxel-saas
Version: 0.1.0
Summary: Python SDK for QXel SaaS — run Amazon Braket circuits on GPU-backed QXel simulators
Author: QubiStack
License-Expression: Apache-2.0
Project-URL: Homepage, https://www.qubistack.com
Project-URL: Repository, https://github.com/QubiStack/QXel-Saas
Keywords: quantum,quantum-computing,braket,amazon-braket,simulator,state-vector,qxel,gpu
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31
Requires-Dist: amazon-braket-sdk>=1.117.0
Dynamic: license-file

# QXel SaaS Python SDK

Python SDK for running Amazon Braket circuits on QXel SaaS.

The SDK handles:

- API key authentication
- Braket `Circuit` to OpenQASM 3 serialization
- input artifact upload
- job submission
- blocking and nonblocking execution
- job lookup, cancellation, and result download

## Current status

QXel SaaS currently runs the QXel state-vector simulator on AWS Batch GPU workers.

Default runtime options:

| Option           | Default       |
| ---------------- | ------------- |
| `instance_type`  | `g4dn.xlarge` |
| `simulator_type` | `sv`          |
| `compute_type`   | `cuda`        |
| `max_fusion`     | `1`           |

## Install

```bash
pip install qxel-saas
```

Or from source:

```bash
pip install .
```

The package depends on:

- `requests`
- `amazon-braket-sdk`

## API key

The SDK expects a QXel SaaS tenant API key.

For local development, store it in an environment variable:

```bash
export QXEL_SAAS_API_KEY="qxel_saas_..."
```

When the dashboard is available, users should create or rotate this key from the dashboard and copy it into their local environment or notebook secret store.

Do not print, commit, or share raw API keys.

## Quickstart: blocking run

```python
import os

from braket.circuits import Circuit
from QXelSaas import QXel

sim = QXel(api_key=os.environ["QXEL_SAAS_API_KEY"])

circuit = Circuit().h(0).cnot(0, 1)
result = sim.run(circuit, shots=256)

print(result)
```

`run()` blocks until the job succeeds, then downloads and returns `result.json`.

## Nonblocking submit

```python
import os

from braket.circuits import Circuit
from QXelSaas import QXel

sim = QXel(api_key=os.environ["QXEL_SAAS_API_KEY"])

circuit = Circuit().h(0).cnot(0, 1)
job = sim.submit(circuit, shots=256)

print(job.job_id, job.status)

job.wait()
result = job.result()
print(result)
```

Use nonblocking submit when you want to save a job id, poll later, or integrate QXel SaaS into a notebook or workflow system.

## Job management

```python
job = sim.submit(circuit, shots=256)
print(job.job_id, job.status, job.created_at)

job.cancel()
job.refresh()

print(job.to_dict())
print(job.is_done())
print(job.is_successful())
print(job.is_failed())
print(job.is_cancelled())

same_job = sim.get_job(job.job_id)
print(same_job.to_dict())

for listed_job in sim.list_jobs():
    print(listed_job.job_id, listed_job.status, listed_job.created_at)
```

Cancellation marks the job `CANCELLED` immediately. If a Batch job was already submitted, a best-effort termination request is sent.

## SDK reference

### `QXel`

```python
QXel(
    api_key,
    instance_type="g4dn.xlarge",
    simulator_type="sv",
    request_timeout_seconds=30,
    verbose=True,
)
```

Constructor behavior:

- validates the API key with `GET /auth/apikey`
- stores the authenticated tenant metadata in `sim.tenant`
- stores service defaults in `sim.defaults`
- prints progress logs by default

Public methods:

| Method                                   | Description                                         |
| ---------------------------------------- | --------------------------------------------------- |
| `run(circuit, shots=1000, **options)`    | Blocking submit/wait/result flow                    |
| `submit(circuit, shots=1000, **options)` | Create, upload, and submit a job; returns `QXelJob` |
| `get_job(job_id)`                        | Fetch one job by id                                 |
| `list_jobs()`                            | List jobs for the authenticated tenant              |
| `wait_job(job_id)`                       | Fetch and wait for one job                          |
| `get_result(job_id)`                     | Fetch and download one successful job result        |
| `cancel_job(job_id)`                     | Fetch and cancel one job                            |

Progress logs can be disabled:

```python
sim = QXel(api_key=os.environ["QXEL_SAAS_API_KEY"], verbose=False)
```

### `QXelJob`

Common properties:

| Property       | Description                                |
| -------------- | ------------------------------------------ |
| `job_id`       | QXel SaaS job id                           |
| `job_status`   | Current job status                         |
| `status`       | Convenience alias for `job_status`         |
| `created_at`   | Job creation timestamp                     |
| `submitted_at` | When the job was submitted to the queue    |
| `started_at`   | When the Batch worker started              |
| `completed_at` | When the job reached a terminal status     |
| `data`         | Raw public job payload returned by the API |

Public methods:

| Method                                                | Description                                   |
| ----------------------------------------------------- | --------------------------------------------- |
| `refresh()`                                           | Reload job state from the API                 |
| `wait(timeout_seconds=3600, poll_interval_seconds=5)` | Wait until success or terminal failure/cancel |
| `result()`                                            | Download result for a `SUCCEEDED` job         |
| `cancel()`                                            | Cancel this job                               |
| `to_dict()`                                           | Return a shallow copy of the job payload      |
| `is_done()`                                           | Return `True` for terminal statuses           |
| `is_successful()`                                     | Return `True` for `SUCCEEDED`                 |
| `is_failed()`                                         | Return `True` for `FAILED`                    |
| `is_cancelled()`                                      | Return `True` for `CANCELLED`                 |

## Job statuses

| Status      | Meaning                                          |
| ----------- | ------------------------------------------------ |
| `PENDING`   | Job created; awaiting input upload + `submit`    |
| `SUBMITTED` | `submit` accepted; queued / provisioning on Batch |
| `RUNNING`   | Batch worker is running the simulation           |
| `SUCCEEDED` | Result is available                              |
| `FAILED`    | Job failed                                        |
| `CANCELLED` | Job was cancelled                                |

## Result shape

A successful state-vector run returns JSON similar to:

```python
{
    "result_type": "qxel_state_vector",
    "simulator": "QXel-sv",
    "shots": 256,
    "measurement_counts": {
        "00": 128,
        "11": 128,
    },
}
```

Exact counts vary because the result is sampled.

## Errors

SDK exceptions are exported from `QXelSaas`:

| Exception          | Raised when                                |
| ------------------ | ------------------------------------------ |
| `QXelInputError`   | SDK input or API response shape is invalid |
| `QXelApiError`     | API, upload, or download request fails     |
| `QXelTimeoutError` | Waiting exceeds the timeout                |
| `QXelJobFailed`    | A waited job reaches `FAILED`              |
| `QXelJobCancelled` | A waited job reaches `CANCELLED`           |

## Security notes

- Raw API keys are only sent in the `Authorization: Bearer ...` header.
- Raw API keys should not be printed in logs.
- Raw API keys should not be committed to source control.
- Result artifacts are accessed through short-lived presigned URLs.
- The SDK does not expose or require a public `base_url` option.
