Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ Each generated endpoint module exposes four callables: `sync`, `sync_detailed`,

For options (`api_key`, `base_url`, `max_retries`, `timeout`, `extension`), error classes, retry behavior, pagination, polling, sessions, and downstream-SDK extension hooks, see the [API reference](https://ionq.github.io/ionq-core-python/).

## Examples

See [`examples/`](examples/) for direct API examples, including a Hamiltonian Energy Quantum Function workload with client-side parameter optimization.

## Versioning

This package follows [SemVer 2.0](https://semver.org/spec/v2.0.0.html), independent of the upstream REST API version - pass an explicit `base_url` to `IonQClient` to pin against a different API. Print the installed version with:
Expand Down
35 changes: 35 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Examples

These examples show direct `ionq-core` usage close to the IonQ REST API.

## Hamiltonian Energy Optimization

`hamiltonian_energy_optimization.py` builds a Hamiltonian Energy Quantum
Function with typed `ionq_core.models`, submits each energy evaluation with
`create_job`, waits with `wait_for_job`, and minimizes the returned energy with
a small dependency-free coordinate-search optimizer.

The example uses:

- Hamiltonian: `-ZI - IZ + 0.5 XX`
- Ansatz: a two-parameter OpenQASM 3 circuit
- Backend: `simulator`
- Optimizer: local coordinate search

Install and run:

```sh
pip install ionq-core
export IONQ_API_KEY=your-api-key
python examples/hamiltonian_energy_optimization.py
```

On Windows PowerShell, set the API key with:

```powershell
$env:IONQ_API_KEY = "your-api-key"
python examples/hamiltonian_energy_optimization.py
```

The script prints each submitted job id, the returned energy, and the best
parameters found so far.
165 changes: 165 additions & 0 deletions examples/hamiltonian_energy_optimization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# SPDX-FileCopyrightText: 2026 IonQ, Inc.
# SPDX-License-Identifier: Apache-2.0

"""Optimize a Hamiltonian-energy quantum function with ionq-core.

This example uses a two-qubit OpenQASM 3 ansatz with two parameters, the
Hamiltonian ``-ZI - IZ + 0.5 XX``, and a dependency-free coordinate-search
optimizer. Each objective evaluation submits a Hamiltonian Energy Quantum
Function job to the free ``simulator`` backend and waits for completion.
"""

from __future__ import annotations

from collections.abc import Callable, Mapping, Sequence
from dataclasses import dataclass
from typing import Any

from ionq_core import IonQClient, wait_for_job
from ionq_core.api.default import create_job
from ionq_core.client import AuthenticatedClient
from ionq_core.models.ansatz import Ansatz
from ionq_core.models.get_job_response import GetJobResponse
from ionq_core.models.hamiltonian_energy_data import HamiltonianEnergyData
from ionq_core.models.hamiltonian_energy_input import HamiltonianEnergyInput
from ionq_core.models.hamiltonian_energy_input_data import HamiltonianEnergyInputData
from ionq_core.models.hamiltonian_pauli_term import HamiltonianPauliTerm
from ionq_core.models.job_creation_response import JobCreationResponse
from ionq_core.models.quantum_function_job_creation_payload import QuantumFunctionJobCreationPayload

ANSATZ_OPENQASM = """OPENQASM 3.0;
include "stdgates.inc";

input float theta0;
input float theta1;
qubit[2] q;

ry(theta0) q[0];
ry(theta1) q[1];
cx q[0], q[1];
"""

INITIAL_PARAMETERS = (0.3, -0.2)
HAMILTONIAN = (
HamiltonianPauliTerm(pauli_string="ZI", coefficient=-1.0),
HamiltonianPauliTerm(pauli_string="IZ", coefficient=-1.0),
HamiltonianPauliTerm(pauli_string="XX", coefficient=0.5),
)


@dataclass(frozen=True)
class OptimizationResult:
"""Best parameters and energy found by the local optimizer."""

params: tuple[float, ...]
energy: float


def build_hamiltonian_energy_payload(
params: Sequence[float],
*,
backend: str = "simulator",
shots: int = 100,
) -> QuantumFunctionJobCreationPayload:
"""Build a typed Hamiltonian Energy Quantum Function payload."""
hamiltonian_data = HamiltonianEnergyData(
hamiltonian=list(HAMILTONIAN),
ansatz=Ansatz(data=ANSATZ_OPENQASM),
)
return QuantumFunctionJobCreationPayload(
backend=backend,
type_="quantum-function",
name="Hamiltonian energy optimization example",
shots=shots,
input_=HamiltonianEnergyInput(
data=HamiltonianEnergyInputData(type_="hamiltonian-energy", data=hamiltonian_data),
params=list(params),
),
)


def submit_energy_job(
client: AuthenticatedClient,
params: Sequence[float],
*,
backend: str = "simulator",
shots: int = 100,
timeout: float = 300,
) -> tuple[str, float]:
"""Submit one energy-evaluation job and return ``(job_id, energy)``."""
payload = build_hamiltonian_energy_payload(params, backend=backend, shots=shots)
created = create_job.sync(client=client, body=payload)
if not isinstance(created, JobCreationResponse):
raise RuntimeError("IonQ API did not return a job creation response")

completed = wait_for_job(client, created.id, timeout=timeout)
return created.id, extract_energy(completed)


def extract_energy(job: GetJobResponse) -> float:
"""Extract a numeric energy from a completed Quantum Function job response."""
output = job.output.additional_properties
for candidate in _energy_candidates(output):
if isinstance(candidate, int | float):
return float(candidate)
raise ValueError(f"Could not find a numeric energy in job output: {output!r}")


def coordinate_search(
objective: Callable[[Sequence[float]], float],
initial_params: Sequence[float],
*,
iterations: int = 4,
initial_step: float = 0.4,
) -> OptimizationResult:
"""Minimize ``objective`` with a small coordinate search."""
params = list(initial_params)
best_energy = objective(params)
step = initial_step

for iteration in range(1, iterations + 1):
for axis in range(len(params)):
best_candidate = params[:]
for direction in (-1.0, 1.0):
candidate = params[:]
candidate[axis] += direction * step
energy = objective(candidate)
if energy < best_energy:
best_energy = energy
best_candidate = candidate
params = best_candidate
print(f"iteration={iteration} step={step:.3f} energy={best_energy:.8f} params={params}")
step *= 0.5

return OptimizationResult(params=tuple(params), energy=best_energy)


def _energy_candidates(value: Any) -> list[Any]:
if not isinstance(value, Mapping):
return [value]

candidates: list[Any] = []
for key in ("energy", "expectation", "expectation_value", "value", "minimum_value"):
if key in value:
candidates.append(value[key])
for nested_key in ("result", "results", "solution", "output"):
if nested_key in value:
candidates.extend(_energy_candidates(value[nested_key]))
return candidates


def main() -> None:
"""Run the example against the configured IonQ account."""
client = IonQClient(additional_user_agent="ionq-core-example/hamiltonian-energy")

def objective(params: Sequence[float]) -> float:
job_id, energy = submit_energy_job(client, params)
print(f"job_id={job_id} energy={energy:.8f} params={list(params)}")
return energy

result = coordinate_search(objective, INITIAL_PARAMETERS)
print(f"best_energy={result.energy:.8f} best_params={list(result.params)}")


if __name__ == "__main__":
main()