Metadata-Version: 2.4
Name: openquantum-sdk-cirq
Version: 0.5.0
Summary: Cirq Python SDK for Open Quantum
License: Apache-2.0
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: openquantum-sdk>=1.0.0
Requires-Dist: cirq-core>=1.4
Requires-Dist: numpy>=1.22
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: pytest-mock>=3.10; extra == "dev"

# Cirq-Open Quantum Plugin

> **Beta.** This plugin is in beta; its API and defaults may still change. Feedback is welcome in the [Quantum Rings community](https://www.quantumrings.com/community).

A [Cirq](https://quantumai.google/cirq) sampler that submits circuits to the
[Open Quantum](https://www.openquantum.com) platform. Write circuits with Cirq
and run them on real quantum hardware.

## How it works

Cirq exports OpenQASM natively (`cirq.Circuit.to_qasm()`), so this plugin does
not depend on the Qiskit plugin. `OpenQuantumSampler` is a thin
[`cirq.Sampler`](https://quantumai.google/reference/python/cirq/Sampler):

- `OpenQuantumClient.from_env` (Core SDK) handles authentication and backend
  discovery.
- Each resolved circuit is serialized to OpenQASM and submitted through the
  framework-agnostic `SchedulerClient`.
- Platform counts are reconstructed into `cirq.Result` objects.

Because it implements `run_sweep`, the standard `cirq.Sampler` methods —
`run`, `sample`, `run_sweep`, and `run_batch` — all work.

## Installation

```bash
# Plugin (and core SDK)
pip install openquantum-sdk-cirq

# or via extras:
pip install "openquantum-sdk[cirq]"
```

This pulls in `cirq-core` and `openquantum-sdk`.

## Quick Start

```python
import cirq
from cirq_openquantum import OpenQuantumSampler

sampler = OpenQuantumSampler(
    backend="ionq:forte-1",
    client_id="your_client_id",
    client_secret="your_client_secret",
)

q = cirq.LineQubit.range(2)
circuit = cirq.Circuit([
    cirq.H(q[0]),
    cirq.CNOT(q[0], q[1]),
    cirq.measure(*q, key="m"),
])

result = sampler.run(circuit, repetitions=1024)
print(result.histogram(key="m"))
# Counter({0: ~512, 3: ~512})   # 00 and 11
```

Circuits must contain at least one `cirq.measure(...)`; the QASM exporter does
not add measurements for you.

## Authentication

Resolved in this order:

1. **Pre-built client** — pass `client=OpenQuantumClient(...)` for full control
   (saved accounts, custom URLs, keyring).
2. **Bearer token** — pass `token=` or set `OPENQUANTUM_TOKEN`.
3. **Client credentials** — pass `client_id=` + `client_secret=` or set
   `OPENQUANTUM_CLIENT_ID` + `OPENQUANTUM_CLIENT_SECRET`.

| Parameter | Environment Variable | Description |
|-----------|---------------------|-------------|
| `token` | `OPENQUANTUM_TOKEN` | Bearer token |
| `client_id` | `OPENQUANTUM_CLIENT_ID` | OAuth2 client ID |
| `client_secret` | `OPENQUANTUM_CLIENT_SECRET` | OAuth2 client secret |
| | `OPENQUANTUM_SDK_KEY` | Path to an SDK-key JSON file (`{client_id, client_secret}`) |
| `organization_id` | `OPENQUANTUM_ORGANIZATION_ID` | Organization UUID (optional if single org) |
| `scheduler_url` | `OPENQUANTUM_SCHEDULER_URL` | Scheduler API base, when the argument is omitted |
| `management_url` | `OPENQUANTUM_MANAGEMENT_URL` | Management API base, when the argument is omitted |
| `keycloak_base` | `OPENQUANTUM_KEYCLOAK_BASE` | Keycloak base, when the argument is omitted |
| `realm` | `OPENQUANTUM_KEYCLOAK_REALM` | Keycloak realm, when the argument is omitted |

`from_env()` raises `AuthenticationError` when no credential resolves. The older `OPENQUANTUM_*_BASE` URL names are not read here; those remain an MCP-only fallback.

## Configuration

| Parameter | Default | Description |
|-----------|---------|-------------|
| `backend` | required | Backend class ID or short_code (e.g. `"ionq:forte-1"`) |
| `job_subcategory_id` | `"oth:oth"` | Workload subcategory (set the most specific match) |
| `execution_plan` | `"auto"` | `"auto"`, `"public"`, `"private"`, an enum, or a plan UUID. `"auto"` picks the cheapest plan |
| `queue_priority` | `"auto"` | `"auto"`, `"standard"`, `"priority"`, `"instant"`, an enum, or a priority UUID. `"auto"` picks the cheapest priority |
| `export_format` | `"qasm3"` | `"qasm2"` or `"qasm3"` |
| `configuration_data` | `{}` | Backend-specific config passthrough (e.g. `{"engine": "amber_quantum_rings"}`) |
| `reverse_bits` | `True` | Reverse platform bitstrings to Cirq's big-endian order |
| `verbose` | `False` | Print submission progress and credit cost to stdout |
| `job_timeout_seconds` | `86400` | Max wait for job completion (1 day) |

Shots are set per run via `repetitions` and injected into `configuration_data`.

## Backend-specific configuration

Some backends require extra fields. The free Quantum Rings simulator
(`qr:gpu-gb10`) requires an `engine`:

```python
sampler = OpenQuantumSampler(
    backend="qr:gpu-gb10",
    configuration_data={"engine": "amber_quantum_rings"},
)
```

## Bit ordering

The platform returns counts little-endian (the first classical bit is the
rightmost character, Qiskit-style), while Cirq is big-endian (the first
measured qubit is the most-significant bit). The sampler reverses each
bitstring by default (`reverse_bits=True`) so the mapping is correct, verified
live against an asymmetric `|10>` state. Set `reverse_bits=False` only for a
backend that returns counts big-endian.

## License

Apache License 2.0
