Metadata-Version: 2.4
Name: commutator-sdk
Version: 0.3.2
Summary: Commutator Studios Python SDK
Author: Commutator Studios
Project-URL: Homepage, https://commutatorstudios.com
Keywords: quantum,sdk,optimization,physics
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.32.0
Requires-Dist: pydantic<3,>=2.0
Provides-Extra: qiskit
Requires-Dist: qiskit<3,>=1.1; extra == "qiskit"

# Commutator Studios Python SDK

<div align="center">
  <img src="https://img.shields.io/pypi/v/commutator-sdk?color=blue&label=PyPI" alt="PyPI Version">
  <img src="https://img.shields.io/pypi/pyversions/commutator-sdk" alt="Python Versions">
  <img src="https://img.shields.io/badge/License-Proprietary-red" alt="License">
</div>

<br>

**Commutator SDK** is the official Python client for the Commutator Studios quantum ecosystem. It provides a seamless, developer-friendly interface to integrate quantum workloads, optimization engines, and identity management natively into your Python tech stack.

By connecting to the **Centralized API Gateway ([commutator.app](https://commutator.app))**, this SDK handles authentication, rate-limiting, and billing checks automatically, allowing you to focus on building quantum-accelerated applications.

---

## Features

* **Quantum Fleet Management**: Discover, list, and check the status of available quantum backends (e.g., `ibm_berlin`, `ibmq_qasm_simulator`). Automatically synchronizes hardware catalog using the authenticated user's quantum provider credentials.
* **Hardware & Algorithmic Recommendations**: Interface directly with `cs-optimizer` to get smart recommendations for hardware and optimization levels (e.g., VQE) before you run your circuits.
* **Job Execution & Polling**: Submit quantum circuits, execute jobs on actual quantum hardware, and download results natively into Python objects.
* **Standardized Error Handling**: Detailed, structured exceptions (`APIError`, `ValidationError`, `AuthenticationError`) for predictable and easy debugging.
* **...and more!**

---

## Installation

The SDK requires **Python 3.11+**. You can install it directly from PyPI using pip:

```bash
pip install commutator-sdk
```

Install the optional Qiskit interoperability layer when constructing jobs from
native `QuantumCircuit` and `SparsePauliOp` objects:

```bash
pip install "commutator-sdk[qiskit]"
```

---

## Quick Start

Here is a simple example showing how to authenticate, create a job, execute a circuit, and fetch the results.

### 1. Authentication

You can authenticate using your API Key or Username/Password.
```python
from commutator.client import CommutatorClient

# Initialize the client (Environment is auto-detected)
client = CommutatorClient(api_key="your_api_key_here")
```

### 2. Submitting a Quantum Job

Submit your quantum circuit JSON and execute it on a selected backend.

```python
import time

from commutator import (
    ClassicalOptimizer,
    CommutatorClient,
    CSAlgorithmName,
    CSRunOptions,
    CSVQEOptions,
)

client = CommutatorClient()  # Reads COMMUTATOR_API_KEY
backend = client.backends.get("ibm_berlin")

# 1. Create the job with a built-in algorithm enum.
job = client.jobs.create(
    name="My First VQE Run",
    algorithm=CSAlgorithmName.VQE1,
    file_path="circuit.json"
)
print(f"Job created successfully: {job.id}")

# 2. Persist algorithm controls and submit execution settings.
job = job.run(
    execution_options=CSRunOptions(backend=backend, optimization_level=3),
    algorithm_options=CSVQEOptions(
        optimizer=ClassicalOptimizer.COBYLA,
        max_iterations=5,
        max_evaluations=12,
        convergence=0.005,
        optimizer_options={"rhobeg": 0.5},
    ),
)
while not job.done:
    time.sleep(5)
    job = job.refresh()
print(job.results())
```

### 3. Optimizer Recommendations

Not sure which backend or optimization level to use? Ask the Recommendation Engine!

```python
job = job.recommend(
    target_backends=[backend, "ibm_brisbane"],  # CSBackend objects and slugs are accepted.
    timeout=300,
)
print(job.recommendations_table())
execution_options = job.best_recommendation(
    backend=backend,
    dynamic_decoupling=True,
)
if execution_options is None:
    raise RuntimeError("No completed recommendation matches those filters")

# recommendations() returns every completed CSRunOptions choice and always
# reads current platform state without starting another recommendation run.
other_options = job.recommendations()[1]
job = job.run(
    execution_options=execution_options,
    algorithm_options=CSVQEOptions(max_iterations=100, convergence=0.001),
)
```

---

## Error Handling & Exception Reference

When executing quantum simulation jobs, the SDK automatically inspects the API response `errorCode` and raises typed Python exceptions providing human-readable diagnostic remedies:

```python
from commutator.client import CommutatorClient
from commutator.exceptions import (
    SimulationOOMError,
    SimulationTimeoutError,
    ProviderAuthenticationError,
    JobFailedError
)

client = CommutatorClient(api_key="your_api_key")

try:
    job_details = client.jobs.get(job_id="5bacd027-267d-4c8e-94c4-5348c48880b3")
except SimulationOOMError as e:
    print(f"❌ Memory Error [{e.error_code}]: {e.message}")
    print(f"💡 Actionable Remedy: {e.remedy}")
except SimulationTimeoutError as e:
    print(f"⏱️ Job Timed Out [{e.error_code}]: {e.message}")
    print(f"💡 Actionable Remedy: {e.remedy}")
except JobFailedError as e:
    print(f"⚠️ Job Execution Failed [{e.error_code}]: {e.message}")
```

### Supported Exception Mapping

| API `errorCode` Enum | Raised SDK Exception Class | Python Import Path |
| :--- | :--- | :--- |
| `SIMULATION_OOM` | `SimulationOOMError` | `from commutator.exceptions import SimulationOOMError` |
| `SIMULATION_TIMEOUT` | `SimulationTimeoutError` | `from commutator.exceptions import SimulationTimeoutError` |
| `PROVIDER_AUTH_FAILED` | `ProviderAuthenticationError` | `from commutator.exceptions import ProviderAuthenticationError` |
| `OPTIMIZATION_DIVERGED` | `OptimizationDivergedError` | `from commutator.exceptions import OptimizationDivergedError` |
| *Fallback / System Faults* | `JobFailedError` | `from commutator.exceptions import JobFailedError` |

---

## Advanced Usage

### Backend and job resources

The SDK returns validated Commutator Studios resource objects by default:

```python
import time

from commutator import (
    CommutatorClient,
    CSAlgorithm,
    CSAlgorithmName,
    CSBackend,
    CSBackends,
    CSJob,
    CSJobs,
    CSProvider,
    CSRunOptions,
    CSVQEAlgorithm,
    CSVQEOptions,
)

client = CommutatorClient()  # Reads COMMUTATOR_API_KEY
cs_backends: CSBackends = client.backends
algorithms: list[CSAlgorithm] = client.jobs.list_algorithms()

backend: CSBackend = cs_backends.get("ibm_fez")
job: CSJob = client.jobs.create(
    name="My first VQE run",
    algorithm=CSAlgorithmName.VQE1,
    file_path="circuit.json",
)
job = job.run(
    execution_options=CSRunOptions(
        backend=backend,
        optimization_level=2,
        predicted_shots=1024,
    ),
    algorithm_options=CSVQEOptions(max_iterations=100, convergence=0.001),
)
while not job.done:
    time.sleep(5)
    job = job.refresh()
result = job.results()
```

`CSBackends` supports listing, local filters, lookup, refresh, discovery,
and explicit enable/disable operations:

```python
available = client.backends.list(
    provider=CSProvider.IBM,
    enabled=True,
    operational=True,
    min_qubits=100,
)
backend = backend.refresh()
backend = backend.disable()
backend = backend.enable()
backend = client.backends.disable("ibm_fez")
backend = client.backends.enable("ibm_fez")
```

`CSJobs` supports create, get, list, keyword search and destructive deletion.
`list_algorithms()` returns validated `CSAlgorithm` objects with `id`, `name`,
`full_name` and `is_available` fields. Algorithm-specific entries use subclasses:
VQE catalog rows are `CSVQEAlgorithm` objects, while algorithms without a typed
SDK contract remain `CSAlgorithm` objects. `create()` accepts one of these objects,
a `CSAlgorithmName` enum member, or a string identifier.

The public types have distinct responsibilities:

| Type | Responsibility |
| --- | --- |
| `CSAlgorithm` / `CSVQEAlgorithm` | Catalog metadata / VQE input construction, options and result parsing |
| `CSVQEInput` | Ansatz, Hamiltonian and optimizer initial point |
| `CSVQEOptions` | Classical optimization settings |
| `CSRunOptions` | Backend and quantum execution settings |
| `CSVQEResult` | Scientific results and legacy artifact compatibility |

Implementation lives in `algorithms.py`, `inputs.py`, and `results.py`, with
resource snapshots in `models.py`. Existing top-level and `commutator.models`
imports remain supported. Algorithm metadata supplied at creation or read by
`list_algorithms()` remains available to subsequent jobs on that client. Without
catalog metadata, the SDK uses the legacy `VQE*` identifier fallback.

For VQE, the selected algorithm can construct the matching input model directly
from Qiskit objects:

```python
algorithm: CSVQEAlgorithm = next(
    item
    for item in client.jobs.list_algorithms()
    if isinstance(item, CSVQEAlgorithm) and item.id == "VQE1"
)
vqe_input = algorithm.create_input(
    circuit=ansatz,
    hamiltonian=hamiltonian,
    # These values are the optimizer's initial point. Omit them to generate
    # and retain one random point for recommendations and final execution.
    parameters=[0.1, -0.2],
)
job = client.jobs.create(name="Hydrogen VQE", algorithm=algorithm, input=vqe_input)
```

For an ansatz whose parameters are named `theta_1` and `theta_2`, you can
also bind the initial point by name. The mapping must contain every circuit
parameter exactly once; dictionary insertion order does not affect the binding:

```python
vqe_input = algorithm.create_input(
    circuit=ansatz,
    hamiltonian=hamiltonian,
    parameters={
        "theta_1": 0.1,
        "theta_2": -0.2,
    },
)
```

`file_path="circuit.json"` remains supported. Provide either `input` or
`file_path`, but not both. The typed route validates Qiskit object types, qubit
counts, finite real Hamiltonian coefficients and the number of initial parameter
values before creating the job. It generates omitted parameters once when the
input object is constructed; reusing that input reuses the same initial point.
OpenQASM export uses ordered parameter identifiers so engine import preserves
positional binding. `parameter_names` retains the original circuit names in that
same order. A named result binds values by name and rejects a mismatched circuit.
The legacy file route checks the JSON container only; it does not yet provide
the typed route's semantic validation or initialization.

`CSJob` supports `refresh()`, `recommend()`, `recommendations()`,
`recommendations_table()`, filtered `best_recommendation()`, `run()`, `results()` and
`get_backend()`. `recommend()` returns a fresh snapshot of the same remote job,
and both recommendation accessors return `CSRunOptions`. Each bound
`recommendations()` call reads the latest completed rows from the platform
without submitting new optimizer work, so repeated calls can reveal a growing
table without restarting it. Call `recommend()` explicitly to start generation.
Job names cannot
currently be changed after creation because the platform execution endpoint
does not update the circuit name.
Resource methods return new snapshots, so assign their return values.

`best_recommendation()` accepts optional `backend`, `optimization_level`, and
`dynamic_decoupling` filters and returns `None` when no completed option matches.
`recommendations_table()` produces a dependency-free ASCII table ordered by
predicted score.

`CSRunOptions` holds only backend and quantum execution settings. It validates
optimization level (0–3), booleans and positive shot counts. `with_overrides()`
makes a validated copy when you want to adjust a recommended execution option.
`CSAlgorithmOptions` is the base for algorithm-specific settings;
`CSVQEOptions` provides independent positive limits: `max_iterations` caps
classical optimizer iterations and `max_evaluations` caps objective evaluations
submitted to the quantum backend. Optimization stops when either limit or the
selected optimizer's convergence condition is reached first. Omitting
`max_evaluations` leaves evaluations uncapped; the iteration limit and
convergence condition still apply. `convergence` is
the positive convergence tolerance. `optimizer` accepts
`ClassicalOptimizer.COBYLA` or `ClassicalOptimizer.SIMPLEX`; the strings
`"cobyla"` and `"simplex"` are also accepted at runtime.

`optimizer_options` accepts `rhobeg`, `catol`, and `f_target` for COBYLA. For
Simplex it accepts `init_simplex_size`, `tol_std`, `tol_size`, `reflect`, and
`shrink`. `shrink` is a reciprocal scale factor greater than one: expansion
multiplies by its coefficient, while contraction and shrinking divide by their
coefficients. An unset optimizer is omitted from requests so it does not replace
a stored choice.
Pass these options to `job.run(algorithm_options=...)`; the SDK persists them
before it submits `execution_options`. `job.algorithm_options` returns the
effective algorithm-specific model (`CSVQEOptions` for a VQE job).
The two writes are not atomic: if dispatch fails after saving settings, refresh
the job to inspect its current state before retrying.
If future recommendation rows contain VQE controls, they fill only fields that
are unset on the job; explicitly stored values win.

For VQE jobs, `job.results()` returns `CSVQEResult`. It accepts both current
partial/list artifacts and the structured result, so absent fields such
as `optimal_parameters`, `iterations`, or `convergence_reason` remain `None`
instead of breaking older jobs. When parameter names and optimal values are
available, use `result.optimal_parameter_map` or
`result.bind_optimal_parameters(circuit)`.
Unknown explicit result schema versions require an SDK update; additive fields
within the supported version remain available. Full optimizer metrics and optimal
parameters still depend on the service emitting the structured result contract.
Additional service options require the explicit `experimental={...}` escape
hatch and cannot replace validated options, identity, backend or status.

Use `job.refresh()` for read-only status polling and stop when `job.done` is true.
GET requests retain bounded rate-limit retries, while mutating requests are not
replayed automatically. `COMMUTATOR_TIMEOUT` controls the request timeout and
defaults to 300 seconds.

There is no `CSJob.cancel()` yet. The legacy `client.jobs.cancel()` aliases the
destructive job-and-artifact deletion endpoint, so it is not exposed as a
resource convenience method.

Resource snapshots retain additive response fields and support Pydantic serialization.
`model_dump(mode="json", by_alias=True)` uses API field names. A model loaded
from JSON is detached; retrieve it through the client before calling a network
method. Resources from another client are rejected before submission.

`Backend`, `RunOptions`, `BackendsModule`, `JobsModule` and `client.fleet`
remain as compatibility aliases. Explicit `list_raw()` methods are available
for integrations that still need the original response dictionaries.

### Interactive walkthrough

Open [example.ipynb](example.ipynb) from the `python` directory using the SDK
virtual environment. The notebook has short, sequential examples for backend
selection, fleet management, job creation, execution, polling and results. It
uses a local sample API by default; its authentication cell explains how to
switch to staging for a real run.

Install the checkout and notebook dependencies first:

```bash
cd python
.venv/bin/python -m pip install -e . -r requirements-dev.txt
```

The development dependencies include Qiskit and its OpenQASM 3 importer so the
VQE contract tests always run under `python -m pytest tests/`. These remain
optional for SDK users; the base package does not require Qiskit.

For comprehensive documentation, including detailed API reference, Identity Management (`list_api_keys`, `get_audit_logs`), and advanced configuration options, please refer to the official [Commutator Studios Documentation](https://commutator.app/docs/sdk).

## License

Copyright © 2026 Commutator Studios GmbH. All rights reserved.
