Skip to main content

Q-Alchemy Python SDK

This is the Python SDK for using the data cybernetics Q-Alchemy API. It provides state preparation, hosted sparse simulation, circuit compression, feasibility assessment, and provider-neutral Quantum I/O execution through PineXQ. The state-preparation API helps quantum computing researchers put classical data into a quantum computer (the loading/encoding problem, sometimes described as a form of QRAM).

Under the hood, Q-Alchemy runs on PineXQ, the hypermedia (Siren) API platform of data cybernetics. You do not need to know anything about it to use this SDK; if you want to work with the API directly, see the PineXQ documentation.

Installation

The SDK is published on PyPI, so you can install it with pip (or poetry, uv, pdm, ...):

pip install q-alchemy-sdk-py
# or
uv add q-alchemy-sdk-py
# or
pdm add q-alchemy-sdk-py

If you want to use the qiskit-integration, please use

pip install q-alchemy-sdk-py[qiskit]

And if you want the PennyLane-integration, please use

pip install q-alchemy-sdk-py[pennylane]

If you would like to run our examples, please use

pip install q-alchemy-sdk-py[examples]

We use uv and have tested this all with Python 3.11 through 3.14. So the way to install it after cloning is simply

uv sync --locked

The SDK requires pinexq-client>=2.1.0,<3. Client 2.1.0 expects PineXQ JobManagement API 10.1. The client checks its protocol major/minor against the server and warns when they differ; the SDK preserves these warnings. A server still running API 10.0 therefore produces a mismatch warning with client 2.1.0. Client package versions and API protocol versions are separate. This client recognizes the server's TenantId and IsMaterialized fields. API-version and schema warnings are no longer suppressed by the SDK; a future mismatch remains visible. Q_ALCHEMY_API_VERSION_WARNING is no longer needed.

Again, for qiskit- or PennyLane-integrations, please add the groups

uv sync --locked --extra qiskit --extra pennylane

And for running our examples,

uv sync --locked --extra examples

Usage

There are examples under the /examples folder, but for those that are eager to find out, here it is. First, you will want to get an API key from the Q-Alchemy Portal. You need to sign up for this, sorry, but this is necessary. Once you have the API key (free of charge of course) you can test it!

Direct Example

import numpy as np
import os
from sklearn.datasets import fetch_openml

from q_alchemy.initialize import q_alchemy_as_qasm

mnist = fetch_openml('mnist_784', version=1, parser="auto")

zero: np.ndarray = mnist.data[mnist.target == "0"].iloc[0].to_numpy()
filler = np.empty(2 ** 10 - zero.shape[0])
filler.fill(0)

zero = np.hstack([zero, filler])
zero = zero / np.linalg.norm(zero)

qasm, summary = q_alchemy_as_qasm(zero, max_fidelity_loss=0.2, 
    api_key=os.environ["Q_ALCHEMY_API_KEY"], return_summary=True)
print(summary)

Qiskit Example

import numpy as np
from sklearn.datasets import fetch_openml
import os

from q_alchemy.qiskit_integration import QAlchemyInitialize, OptParams

mnist = fetch_openml('mnist_784', version=1, parser="auto")

zero: np.ndarray = mnist.data[mnist.target == "0"].iloc[0].to_numpy()
filler = np.empty(2 ** 10 - zero.shape[0])
filler.fill(0)

zero = np.hstack([zero, filler])
zero = zero / np.linalg.norm(zero)

instr = QAlchemyInitialize(
    params=zero.tolist(),
    opt_params=OptParams(
        max_fidelity_loss=0.1,
        basis_gates=["id", "rx", "ry", "rz", "cx"],
        api_key=os.environ["Q_ALCHEMY_API_KEY"]
    )
)
instr.definition.draw(fold=-1)

PennyLane Example

import numpy as np
import pennylane as qml
from sklearn.datasets import fetch_openml
import os

from q_alchemy.pennylane_integration import QAlchemyStatePreparation, OptParams

mnist = fetch_openml('mnist_784', version=1, parser="auto")

zero: np.ndarray = mnist.data[mnist.target == "0"].iloc[0].to_numpy()
filler = np.empty(2 ** 10 - zero.shape[0])
filler.fill(0)

zero = np.hstack([zero, filler])
zero = zero / np.linalg.norm(zero)

dev = qml.device('lightning.qubit', wires=10)

@qml.qnode(dev)
def circuit(state=None):
    QAlchemyStatePreparation(
        state,
        wires=range(10),
        opt_params=OptParams(
            max_fidelity_loss=0.1,
            basis_gates=["id", "rx", "ry", "rz", "cx"],
            api_key=os.environ["Q_ALCHEMY_API_KEY"]
        )
    )
    return qml.state()

print(qml.draw(circuit, level="device", max_length=100)(zero.tolist()))

Broadcasting with PennyLane

PennyLane provides native support for broadcasting, which allows quantum nodes to process batches of inputs efficiently. This is particularly useful in machine learning applications where inputs often come in batches. When broadcasting is used in conjunction with Q-Alchemy, each state in the batch is individually prepared using Q-Alchemy's circuit synthesis capabilities.

⚠️ Note: For simulators or backends that support native state initialization using the StatePrep gate—such as default.qubit, and lightning.qubit—the state vector is injected directly without any decomposition into quantum gates. In this case, Q-Alchemy is not used. This behavior is ideal for rapid prototyping and testing. Switching to a hardware backend (or one without native state prep) will automatically invoke Q-Alchemy for state preparation.

Broadcasting Example

import numpy as np
import pennylane as qml
import os
import torch

from q_alchemy.pennylane_integration import AmplitudeEmbedding, OptParams
from sklearn.datasets import make_moons

# Sample data
X, _ = make_moons(n_samples=5, noise=0.1)
X = X / np.linalg.norm(X, axis=1, keepdims=True)  # Normalize each row for amplitude embedding

# Create PennyLane device
dev = qml.device("qiskit.aer", wires=1)

@qml.qnode(dev, interface="torch")
def circuit(x):
    AmplitudeEmbedding(
        x,
        wires=[0],
        opt_params=OptParams(
            max_fidelity_loss=0.0,
            api_key=os.environ["Q_ALCHEMY_API_KEY"]
        )
    )
    return qml.expval(qml.PauliZ(0))

# Run the circuit on a batch of inputs
X_tensor = torch.tensor(X, dtype=torch.float64)
print(qml.draw(circuit, level="device", max_length=100)(X_tensor))

This example demonstrates how batched data can be processed using broadcasting with AmplitudeEmbedding, and how Q-Alchemy is triggered on simulators like qiskit.aer. When moving to real hardware or gate-based backends that lack StatePrep gate, Q-Alchemy will transparently handle the state preparation.

Advanced options

Every entry point (q_alchemy_as_qasm, QAlchemyInitialize, QAlchemyStatePreparation, AmplitudeEmbedding) takes its settings as an OptParams object, or as a plain dict with the same keys. q_alchemy_as_qasm also accepts them as keyword arguments. The state itself can be a list, a numpy array, a scipy sparse array or, for Qiskit, a Statevector.

Dense inputs are checked for sparsity once before serialization. The SDK uses QTucker's blockwise, early-exit density rule: a vector is sent as sparse when its number of nonzero amplitudes is at most ceil(0.1 * vector_length). Unlike QTucker's numerical thresholding, the transport check counts exact nonzeros and never discards tiny amplitudes or renormalizes the state. This keeps transport lossless and leaves the approximation budget with QTucker.

  • Dense vectors use NumPy .npy serialization (numpy_load for inline input).
  • Sparse vectors use Parquet with basis indices and complex amplitudes. Inputs already supplied as SciPy sparse matrices/arrays skip the density check and remain sparse, even when fully populated.
  • Mixed batches are split into at most two service jobs, one per representation; returned circuits and summaries follow the original input order. Each job uses the usual cleanup policy and timeout. If either job fails, the call raises. Uniform batches still use one job; parallel option sets share one serialization.

The updated ProCon preserves these dense/sparse paths into QTucker. Both formats are supported by older services too, but avoiding server-side conversions requires the updated ProCon deployment. No private QTucker dependency is added to the SDK.

The fields you are most likely to touch:

Field Default Meaning
max_fidelity_loss 0.0 How much fidelity you are willing to give up for a shallower circuit. 0.0 asks for an exact preparation.
basis_gates ["u", "cx"] Gate names used to transpile the returned circuit for all three methods; under AUTO, also used to compare candidates.
api_key $Q_ALCHEMY_API_KEY Your Q-Alchemy API key. Keep it safe!
initialization_method InitializationMethods.AUTO Which algorithm builds the circuit (see below).
extra_kwargs {} Method-specific options, as a dict (see below).
use_qasm3 False Experimental: return OpenQASM 3 instead of OpenQASM 2.
remove_data True Delete the job and its uploaded data once the result is fetched.
job_completion_timeout_sec 300 How long to wait for the job before giving up.

q_alchemy_as_qasm_parallel(state, option_sets, max_workers=4) compares option sets concurrently. Results follow the order of option_sets; a failed worker raises an exception instead of returning an incomplete list. Input serialization is shared, and max_workers bounds concurrent jobs. Already-running jobs finish their normal cleanup if another worker fails. A supplied HTTP client remains caller-owned. Processing-step lookups are cached per client for five minutes; create a new client when changing accounts.

QAlchemyInitialize owns a copy of the input amplitudes, so later changes to the caller's array cannot change the pending initialization. Dense inputs remain NumPy arrays internally. Upload hashing is controlled by assign_data_hash; the instruction no longer computes the unused param_hash attribute.

InitializationMethods lives in q_alchemy.initialize:

  • AUTO (default) runs several Tucker candidates (iterative and hierarchical, as resources allow) at your fidelity budget and keeps the cheapest realized circuit. Trivial inputs such as single-qubit and single-basis states take an exact fast path.
  • ITERATIVE_TUCKER and HIERARCHICAL_TUCKER pin one Tucker variant.

Method-specific options (extra_kwargs)

extra_kwargs is passed through to the chosen method. Pass a dict, and the SDK serializes it for you:

from q_alchemy.initialize import OptParams, InitializationMethods

opt_params = OptParams(
    max_fidelity_loss=0.05,
    initialization_method=InitializationMethods.ITERATIVE_TUCKER,
    extra_kwargs={"max_iterations": 10, "factors_size": 4},
)

⚠️ Options are validated per method. A key that the chosen method does not accept fails the job with Could not build the initialization circuit: ... instead of being silently ignored. In particular, the iterative-Tucker keys below are not valid under AUTO. Pin the method to use them.

Method Accepted extra_kwargs keys
AUTO cost_function ("two_qubit_then_depth" default, "cx_then_depth", "depth_then_cx", "cx+depth", "cx", "depth"), transpile_optimization_level (1), seed_transpiler (0), dominant_basis_fast_path (True), fidelity_tolerance, geometric_entanglement, check_normalization (True)
ITERATIVE_TUCKER max_iterations (≤ 0 picks one from the qubit count), factors_size (0 = automatic), max_stepup (0), fallback (True), perturbation (None), geometric_entanglement (0.0), check_normalization (True), barriers (False; debugging only, hurts transpilation)
HIERARCHICAL_TUCKER geometric_entanglement, check_normalization

Two interactions worth knowing:

  • Set the fidelity with max_fidelity_loss on OptParams, not in extra_kwargs. The service rejects a conflicting duplicate instead of silently replacing the top-level budget. An equivalent duplicate is accepted.
  • Set the gate basis with OptParams.basis_gates for all three methods. Under AUTO, the hosted QAlchemy initializer also uses it when comparing candidates. Do not duplicate basis_gates in extra_kwargs.

All three initialization methods return logical circuits transpiled to the supplied gate basis, without physical placement or routing. Under AUTO, QTucker transpiles candidates to that basis before comparison and returns the selected circuit. For explicit HIERARCHICAL_TUCKER and ITERATIVE_TUCKER requests, the ProCon transpiles the synthesized circuit to that basis at optimization level 1. This applies to single requests and batches; reported circuit costs precede physical placement and routing.

The SDK accepts gate names through OptParams.basis_gates, not Qiskit backend or Target objects. To use a backend's gate basis, supply its supported unitary gate names explicitly. Compile the complete application for the actual backend later. For AUTO's optional transpile_options, QTucker ignores physical layout, coupling-map and scheduling settings and rejects lossy transpilation (approximation_degree must be 1.0).

Request return_summary=True from q_alchemy_as_qasm or q_alchemy_as_qasm_parallel_states to inspect the preparation outcome: status="OK" means execution completed; fidelity_requirement_met=False means AUTO returned its best-effort circuit without meeting the requested budget. The decision is supplied by QTucker. It is None for pinned methods and may be absent in older services. fidelity_loss remains an unsimulated estimate; fidelity_loss_source identifies its source and selection_reason explains the AUTO outcome. These fields pass through unchanged. Batch functions always return lists, including a one-state batch. A synthesis failure stops a batch with the original error and the failing state's zero-based index.

Compressing circuits through PineXQ

CircuitCompressionService calls the compress_circuit ProcessingStep from q-alchemy-circuit-compressor-pinexq. The SDK accepts a native Qiskit circuit, the existing SDK Circuit type, or a schema-1 quantum-circuit envelope (including feasibility_report.raw["quantum_circuit"]). It returns a typed report with the compressed circuit, exactness semantics, input/baseline/output metrics, accepted regions, and optional diagnostics. The local SDK does not install or run the compression engine or a simulator.

from qiskit import QuantumCircuit
from q_alchemy import CircuitCompressionRequest, CircuitCompressionService

circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(0, 1)

with CircuitCompressionService() as service:
    report = service.compress(
        circuit, CircuitCompressionRequest(options={"collect_report": True})
    ).result()

print(report.format_summary())
compressed = report.to_qiskit()

The default assumes the complete circuit starts in |0...0> (equivalence="reachable_subspace"). For a subroutine with arbitrary input states, use CircuitCompressionRequest(options={"equivalence": "operator"}). report.exact applies to that declared equivalence; it does not imply that a reachable-subspace result preserves every possible input. Qiskit input conversion and output transport preserve the top-level global phase, which matters for subsequent controlled-subroutine use. Bind symbolic global phases before export.

Compression option names and resource limits are validated by the deployed core. Omitting options sends {} and retains its defaults. The SDK adds no duplicate memory or synthesis ceilings. Transport admission checks are owned by the PineXQ adapter; region indices in the report refer to its normalized baseline circuit.

CircuitCompressionParams follows the other service clients: api_key defaults to Q_ALCHEMY_API_KEY (then PINEXQ_API_KEY), and host defaults to Q_ALCHEMY_HOST or jobs.api.q-alchemy.com. Set step_version="0.1.7" to pin a deployed step, or omit it to discover the visible version. Successful result retrieval removes SDK-created jobs and WorkData by default. Set remove_data=False to retain them. A failure or malformed report preserves the job for diagnosis; CircuitCompressionExecutionError exposes the remote error and its original exception. Repeating job.result() retries retrieval, or returns the cached report and retries any unfinished cleanup. job.raw_job remains available until deletion.

job_completion_timeout_sec (default 300) and job.result(timeout=...) only limit the client's wait. They do not cancel the job or set a server computation deadline. Use the service context manager to close SDK-owned HTTP connections; injected clients remain owned by the caller.

Install the qiskit extra for native circuit conversion. Portable usage needs no Qiskit: pass Circuit.qasm3(program) and consume report.circuit.payload or report.to_dict(). See the runnable example.

For the 12-qubit chemistry workload, run:

python examples/circuit_compression_quantum_chemistry.py

This example reuses build_experiment() from feasibility_quantum_chemistry.py. It builds the full Hartree--Fock preparation plus correlated evolution circuit (71 one-qubit gates and 34 CX gates), submits it directly to the compression service, and prints the input/baseline/compressed metrics and two-qubit gate reduction. The complete circuit starts in |0...0>, so default reachable-subspace compression is used. It requires the SDK's qiskit extra and a Q-Alchemy API key; it does not run the Feasibility workflow, select a quantum backend, or require IBM credentials.

Live circuit-compression tests

tests/test_circuit_compression.py uses mocked PineXQ transport. Its optional ProCon check invokes a locally installed adapter; it does not contact PineXQ.

The separate live suite submits two real jobs to the deployed service. With Q_ALCHEMY_API_KEY (or PINEXQ_API_KEY) already exported and the SDK's qiskit extra installed, run:

Q_ALCHEMY_RUN_LIVE_CIRCUIT_COMPRESSION=1 \
  pytest -v -s tests/test_circuit_compression_live_integration.py

Optionally set Q_ALCHEMY_CIRCUIT_COMPRESSION_STEP_VERSION=0.1.1 to pin the ProcessingStep, and Q_ALCHEMY_HOST to select a different PineXQ host. Both a key and the explicit live-test flag are required; otherwise these tests skip.

The first test sends a two-qubit circuit with two CX gates, checks that compression reduces its two-qubit gate count below both the input and the O0 baseline, and compares the returned statevector with the original. The current compressor reduces this example from two CX gates to one. The second test checks operator equivalence and global phase, including reuse as a controlled subroutine. Both exercise report retrieval, cached results and cleanup of their own jobs/WorkData. The small local statevector/operator calculations verify the results; compression itself runs on PineXQ. Successful jobs are removed, while remote execution or retrieval failures preserve their jobs and data for diagnosis.

Running experiments with Quantum I/O

For a complete molecular example, run the 12-qubit LiH reconstruction notebook. It includes the original sparse CISD target and offers direct ideal simulation, Aer simulation with an imported IBM device noise model, or an IBM QPU, followed by reconstruction and sparse-result export in one service request.

For a system initialized in an arbitrary state with a bath starting in its ground state, see the system–bath example. It evolves both groups and evaluates one observable on the system, without state reconstruction.

State widths, sparse indices, qubit selections and shot counts require integers; booleans and fractional values are rejected locally. Sparse indices in JSON remain decimal strings so large basis indices retain their full precision.

Quantum I/O uses typed Python objects throughout the public SDK. Users construct State, Circuit, MeasurementPlan, QuantumExperiment, Runtime, and ExecutionPlan objects and receive a typed ExperimentReport. The SDK handles the schema-3 JSON serialization used by PineXQ internally; application code does not need to assemble contract dictionaries or encode complex amplitudes manually. ExperimentReport.format_summary() uses the same human-readable section layout and number formatting as the Quantum I/O runtime report, so local/service output remains consistent at the client boundary.

Quantum I/O removes its SDK-created PineXQ Job and input/output WorkData by default once result() has successfully returned a report. Provider credentials are uploaded separately as Secret WorkData before they are attached to a Job and never become part of QuantumExperiment or ExecutionPlan. Set QuantumIOParams(remove_data=False) when you explicitly want to preserve the complete PineXQ execution lineage.

Execution-lineage cleanup happens after successful result retrieval. If the job fails, times out, or returns a report the SDK cannot parse, the Job and its WorkData are left in place so the failure can be diagnosed and result() retried. If submission fails before PineXQ creates a Job, unreferenced WorkData created by that attempt is removed when remove_data=True. If cleanup itself fails after the report was cached, another result() call retries the remaining cleanup. After a successful automatic cleanup the PineXQ resources are gone: QuantumIOJob.raw_job then raises, and QuantumIOJob.removed is True. Use remove_data=False if you need to inspect the Job afterwards.

Preflight

Preflight is the simplest Quantum I/O workflow. It prepares the target state and, by default, independently verifies the preparation with Q-Alchemy's sparse simulator. It does not run a reference experiment, noisy simulation, or QPU acquisition.

from math import sqrt

from q_alchemy import QuantumExperiment, QuantumIOService, State

a = 1 / sqrt(2)
experiment = QuantumExperiment(
    target=State.dense([a, 0, 0, a]),
)

report = QuantumIOService().preflight(experiment).result()

print(report.mode)  # preflight
print(report.preparation.method)
print(report.preparation.metrics.cx_count)
print(report.preparation_preflight.target_to_prepared_fidelity)

See examples/quantum_io_preflight.py for the complete preflight example.

Ideal simulation

With no explicit execution plan, run() uses the deployed Q-Alchemy sparse simulator as an ideal acquisition source:

from math import sqrt

from q_alchemy import (
    BasisMeasurement,
    MeasurementPlan,
    QuantumExperiment,
    QuantumIOService,
    State,
)

a = 1 / sqrt(2)
experiment = QuantumExperiment(
    target=State.dense([a, 0, 0, a]),
    measurement_plan=MeasurementPlan(
        basis_measurements=(BasisMeasurement("q0-q1", (0, 1)),),
    ),
)

report = QuantumIOService().run(experiment, shots=256).result()
print(report.mode)                       # ideal-simulation
print(report.execution.source_kind)      # ideal-simulator
print(report.execution.basis_distributions[0].probabilities)

See examples/quantum_io_service.py for a runnable version.

Noisy backend simulation

A noisy simulation can use the topology and calibration of a real IBM backend without submitting a QPU job. The PineXQ runtime constructs AerSimulator.from_backend(...) and reports source_kind="noisy-simulator". An ideal sparse reference can be run in the same experiment so the report includes observable and distribution error metrics.

import os
from qiskit import QuantumCircuit

from q_alchemy import (
    BasisMeasurement,
    Circuit,
    IBMQuantumCredentials,
    MeasurementPlan,
    PauliObservable,
    QuantumExperiment,
    QuantumIOService,
    State,
    noisy_backend_execution_plan,
)

credentials = IBMQuantumCredentials(token=os.environ["IBM_QUANTUM_TOKEN"])
service = QuantumIOService(ibm_credentials=credentials)
backends = service.backends(provider="ibm", min_num_qubits=2)
backend = min(
    backends,
    key=lambda item: (item.num_qubits, item.pending_jobs or 0, item.name),
)

evolution = QuantumCircuit(2)
evolution.h(0)
evolution.cx(0, 1)

experiment = QuantumExperiment(
    target=State.dense([1, 0, 0, 0]),
    evolution=Circuit.from_qiskit(evolution),
    measurement_plan=MeasurementPlan(
        training=(
            PauliObservable.pauli("ZI", "ZI"),
            PauliObservable.pauli("IZ", "IZ"),
        ),
        validation=(
            PauliObservable.pauli("XX", "XX"),
            PauliObservable.pauli("ZZ", "ZZ"),
        ),
        basis_measurements=(BasisMeasurement("computational", (0, 1)),),
    ),
)

plan = noisy_backend_execution_plan(
    provider="ibm",
    backend=backend.name,
    shots=4096,
    ideal_reference=True,
    estimator=True,
    execution_options={"transpile": True},
)
report = service.run(experiment, execution_plan=plan).result()

print(report.mode)                       # noisy-simulation
print(report.execution.source_kind)      # noisy-simulator
print(report.observable_error)
print(report.distribution_errors)
print(report.held_out_verification_error)

No QPU execution occurs in this workflow. IBM credentials are used only to discover the backend and read the device information needed to construct the calibrated simulator. The explicit execution_options={"transpile": True} compiles the logical preparation, evolution and measurement circuits to the selected device's native gates and connectivity. Omit it only when the circuits already satisfy that Target. examples/quantum_io_noisy_simulation.py demonstrates the full typed report, including preparation diagnostics, complete-circuit metrics, ideal reference, noisy observations, basis distributions, error metrics, state-estimation output, held-out verification, and warnings.

Quantum hardware

When hardware is needed, backend discovery and execution follow the familiar service/backend pattern. The experiment object is exactly the same one used for simulation:

backend = service.backends(provider="ibm", min_num_qubits=2)[0]
report = backend.run(experiment, shots=1024).result()
print(report.mode)                       # qpu
print(report.execution.source_kind)      # qpu

For advanced workflows, construct a typed ExecutionPlan explicitly. Runtime.resource selects PineXQ runtime resources without exposing credentials in the experiment contract.

Compact sparse reconstruction

SDK 0.5.0 can submit preparation, reconstruction-observable generation, acquisition, fitting, and sparse model export in one Quantum I/O job. Requires deployed q-alchemy-quantum-io-pinexq 0.8.0 with Quantum I/O core 0.11.0.

from q_alchemy import (
    ExecutionPlan, MeasurementPlan, QuantumExperiment, QuantumIOParams,
    QuantumIOService, Runtime, State,
)

target = State.sparse(num_qubits=2, indices=[0, 3], amplitudes=[2**-.5, 2**-.5])
experiment = QuantumExperiment(
    target,
    measurement_plan=MeasurementPlan.qtucker_reconstruction(block_size=2, rank=1),
)
plan = ExecutionPlan(
    preparation_method="iterative_tucker",
    preparation_options={"basis_gates": ["u", "cx"], "max_fidelity_loss": 1e-3},
    acquisition=Runtime.qalchemy_sparse(),
    estimator=Runtime.qtucker(
        block_size=2, rank=1, n_restarts=4,
        fit_config={"epochs": 600, "learning_rate": .03, "stderr_floor": .01,
                    "seed": 123, "verbose": False},
    ),
    estimation_output={"support": "target", "target_fidelity": True},
)
with QuantumIOService(QuantumIOParams(step_version="0.8.0")) as service:
    report = service.run(experiment, plan).result()
print(report.format_summary())
report.estimate.sparse_state.save_npz("reconstructed.npz")

preparation_method accepts auto (default), hierarchical_tucker, and iterative_tucker. QTucker/core own initialization and logical basis compilation; the SDK forwards the request. Inspect preparation found, claimed fidelity loss, and preflight diagnostics when using options such as fallback=False.

qtucker_reconstruction uses the complete native training family. Defaults allow training-only fitting; validation=True requests an independent disjoint validation family (optionally validation_edges and validation_paulis). No held-out RMSE is claimed when validation is absent.

Direct Runtime.qalchemy_sparse() acquisition evaluates expectations without sampling, uses zero pruning by default, reuses matching preflight/reference simulation, and reports capping/pruning/exactness. Its configurable stderr_floor (default 1e-12) is a numerical fitting floor, not sampling uncertainty or a bound on approximation bias. The plan's positive shots value is recorded but unused for sampling.

Sparse export queries the implicit fitted QTucker model without dense materialization. estimation_output also accepts explicit support="indices", indices=[...], threshold (default 0), and max_entries (default None). It does not renormalize the subset: sparse_state.norm_squared and metadata support_probability / returned_probability reveal retained mass. Zero survivors are valid. target_fidelity uses the entire original input target support before filtering/capping, so export options cannot inflate it. It compares the input target with a pure fitted surrogate, rather than certifying a noisy mixed state or an evolved target. Existing reference-to-estimated surrogate metrics remain separate. Both wire JSON and NPZ preserve basis indices above 64 bits. Leave materialize_statevector_max_qubits unset to keep fitting output implicit.

Molecule/CISD construction stays local. Supply its sparse amplitudes as the target; the hosted workflow replaces manual QASM/observable/fit handoffs. The SDK requires no private Quantum I/O or QTucker package locally.

Choosing classical or quantum execution with feasibility

The feasibility API is a separate hosted service. The SDK is only its remote client: it uploads a QuantumExperiment and a portable FeasibilityRequest, submits one PineXQ assess_feasibility Job, and returns the final FeasibilityReport. The classical-first decision loop, execution of the configured quantum target (a simulator or a physical QPU), and evidence collection happen on the service side rather than in the SDK process.

from q_alchemy import (
    FeasibilityRequest,
    FeasibilityService,
    QuantumExperiment,
    SolutionCriteria,
    State,
)

experiment = QuantumExperiment(target=State.dense([1, 0]))
request = FeasibilityRequest(
    criteria=SolutionCriteria.common(max_observable_rmse=0.01),
)

report = FeasibilityService().analyze(experiment, request).result()
print(report.format_summary())

FeasibilityReport.format_summary() mirrors the canonical human-readable formatter in q-alchemy-feasibility, including classical and quantum status, resource/quality criteria, backend/model evidence, recommendation, and next-evidence requests. The SDK formatter is aligned with Feasibility 0.6.50, the PineXQ adapter 0.2.11, and Quantum I/O 0.11.0. Serialized core reports and their canonical summaries are kept as regression fixtures so later core changes can be checked without installing the server runtime in the SDK environment.

Classical Status describes resource feasibility; Quality is a separate assessment. Quality based on quantum-io:exact-reference is explicitly labeled as relative to the simulated circuit reference, not necessarily the intended target state. When Quantum I/O reports a contradicted preparation estimate, the summary displays its warning. The raw evidence and report.warnings remain available as well.

The embedded computation's preparation_preflight.claim_contradicted is tri-state: True means a certified comparison found more preparation loss than the initializer estimated; False means the comparison passed; None (including absent legacy fields) means no certified comparison is available. preparation.claimed_fidelity_loss is the initializer's estimate, target_to_prepared_fidelity is the measured fidelity, and preparation_approximation_infidelity is the measured loss. Quantum I/O owns the exactness and comparison-tolerance checks. The SDK preserves these fields; it does not simulate circuits, recompute the verdict, or infer target-relative energy error from a fidelity discrepancy.

Execution settings use EvidenceCollectionConfig(execution_options=...), separate from backend_options. The default is {"transpile": True}. For example:

from q_alchemy import EvidenceCollectionConfig

evidence = EvidenceCollectionConfig(
    provider="aer",
    execution_options={
        "transpile": True,
        "transpile_options": {"optimization_level": 3},
        "estimator_options": {"seed_simulator": 7},
    },
)

Use execution_options={"transpile": False} only for circuits already compatible with the selected backend. Legacy execution keys inside backend_options are moved into execution_options; conflicting values are rejected locally. Use estimator_options for Pauli-observable execution. backend_run_options (and its legacy alias run_options) applies to basis-measurement/count execution. Retired resource-sweep and attribution controls are no longer constructor parameters. Their keys in old request JSON are ignored, matching the service. classical_first=False is rejected locally; use quantum_execution=COMPARE when both classical and quantum paths are wanted.

To check formatter parity after a core upgrade, run tests/generate_feasibility_fixtures.py --check using a Python environment with q-alchemy-feasibility installed and without the SDK on its import path. If the core intentionally changes, rerun without --check, review the fixture diff, and run pytest tests/test_feasibility_contract_alignment.py in the SDK environment. The fixture generator only analyzes synthetic evidence; it submits no PineXQ jobs.

Full-circuit compression is enabled by default by the feasibility service. The typed SDK contract exposes the same control when callers need to disable it or pass compressor options:

from q_alchemy import CircuitCompressionConfig, EvidenceCollectionConfig

evidence = EvidenceCollectionConfig(
    circuit_compression=CircuitCompressionConfig(
        enabled=True,
        options={"optimization_level": 2},
    )
)

When a criterion requires QTucker state-estimation evidence and the experiment does not supply an explicit estimator training plan, the service can generate the reconstruction family automatically. qtucker_config controls the fitted Tucker model, while qtucker_observable_config controls observable generation and independent validation:

evidence = EvidenceCollectionConfig(
    qtucker_config={
        "blocks": [[0, 1], [2, 3]],
        "ranks": [2, 2],
    },
    qtucker_observable_config={
        "within_block_mode": "all_paulis",
        "cross_block_mode": None,
        "extra_edges": [[0, 2]],
        "validation_edges": [[1, 3]],
        "validation_paulis": "XYZ",
    },
)

Every observable returned by QTucker's native reconstruction-observable generator remains in the training set. Validation is generated separately and is never carved out of that training family. Direct application diagnostics belong in MeasurementPlan.observables; training and validation should be used only for an explicit estimator plan.

The default QuantumExecutionPolicy.WHEN_NEEDED stops once the classical path is already sufficient. Estimator-only criteria do not force quantum execution merely so they can be evaluated. Use QuantumExecutionPolicy.COMPARE when the quantum path should run even when classical execution is feasible. The SDK only serializes this policy; the feasibility service owns the routing decision.

For an opt-in end-to-end check of the SDK -> PineXQ -> feasibility transport, the repository includes a live integration test that submits two small Bell-state jobs: one with compression explicitly disabled and one using the default enabled compression path. Ordinary test runs skip it. Run it explicitly with:

Q_ALCHEMY_RUN_LIVE_FEASIBILITY=1 \
Q_ALCHEMY_API_KEY=... \
pytest -v tests/test_feasibility_live_integration.py

Set Q_ALCHEMY_FEASIBILITY_STEP_VERSION as well when a specific published ProcessingStep version should be tested; otherwise the SDK selects the latest available version. The live test does not require IBM credentials because the two-qubit workload is intentionally classical-first.

The SDK preserves the complete portable report returned by the service. report.computation_result is the feasibility computation-result envelope: it records which compute path ran, execution mode/source, whether the requested criteria were met, and whether the result was selected by the recommendation. If feasibility ran a classical, simulated, or QPU computation, report.computation_experiment_report deserializes the embedded Quantum I/O ExperimentReport; the SDK does not rerun that computation. The nested report uses the same formatter as a directly submitted Quantum I/O experiment:

computation = report.computation_experiment_report
if computation is not None:
    print(computation.format_summary())

Quantum circuits are not returned by default because generated state-preparation circuits can be large. Request the logical experiment circuit (P + U) explicitly on the SDK call:

report = FeasibilityService().analyze(
    experiment,
    request,
    include_quantum_circuits=True,
).result()

circuit = report.quantum_circuit
if circuit is not None:
    print(circuit.draw(output="text"))

The service returns the selected logical circuit as portable QASM 3. The SDK reconstructs the native Qiskit circuit and delegates parsing/rendering to Qiskit; it does not implement a second circuit parser or drawer. Internal measurement and backend-transpiled circuits are not returned.

Feasibility core also returns the renderer-neutral experiment graph that describes which steps ran, were skipped, were unavailable, or were not needed. The SDK never reconstructs that graph from statuses. With the optional visualization extra installed, report.experiment_diagram deserializes the service representation into the shared q-alchemy-visualization ExperimentDiagram type:

diagram = report.experiment_diagram
if diagram is not None:
    print(diagram.draw(output="text"))

The SDK similarly exposes a requested logical circuit as a native Qiskit QuantumCircuit through report.quantum_circuit. Raw diagram and circuit payloads are transport details retained by report.to_dict() rather than separate user-facing APIs.

The visualization package is optional so the public SDK remains installable without the private Q-Alchemy package feed. Graphical Matplotlib output additionally requires the mpl extra of q-alchemy-visualization.

IBM credentials are optional at submission time because the server may prove that the problem is classically feasible and stop before any quantum-backend evidence is needed. When credentials are supplied, the SDK transports them as separate Secret WorkData; they are never serialized into the experiment or feasibility request.

Noisy simulation is not an automatic pre-QPU stage. When simulation is the desired quantum target, select a simulator explicitly in EvidenceCollectionConfig, for example provider="aer", backend="aer_simulator", optionally with portable backend_options. When IBM is selected, feasibility proceeds from static target checks to physical-QPU execution when the policy requires quantum evidence.

Use QuantumIOService when you explicitly want to execute a particular Quantum I/O plan. Use FeasibilityService when the question is which compute path is sufficient under the stated solution criteria.

A complete hosted example is available in examples/feasibility_service.py. It submits a small Bell-state workload, prints the canonical feasibility summary and embedded Quantum I/O report, draws the returned logical experiment circuit with Qiskit when available, and renders the server-provided experiment diagram when the optional visualization extra is installed.

For a notebook-oriented introduction, see examples/feasibility_h2_dynamics.ipynb. It uses the published two-qubit parity-reduced H2 electronic Hamiltonian at 0.735 Angstrom in STO-3G, starts from the Hartree-Fock molecular reference state, applies one first-order Trotter step, and runs feasibility in COMPARE mode against an Aer target. The final cells display the canonical format_summary(), the server-provided experiment diagram, and the returned logical P + U circuit.

A quantum-chemistry example is available in examples/feasibility_quantum_chemistry.py. It uses an illustrative 12-spin-orbital correlated active-space workload and selects the quantum target from the environment:

  • with IBM_QUANTUM_TOKEN, it uses a physical IBM Quantum device;
  • with both IBM_QUANTUM_TOKEN and IBM_QUANTUM_BACKEND, it uses that backend;
  • with an IBM token but no backend name, it requests the least-busy suitable backend;
  • without an IBM token, it uses provider="aer", backend="aer_simulator" with an illustrative portable noise model.

IBM_QUANTUM_INSTANCE is optional and is forwarded when present. The example uses QuantumExecutionPolicy.COMPARE deliberately so the selected quantum target is actually executed even if the small classical reference is feasible. Normal feasibility requests can keep the default when-needed policy. Run it with:

# Required for the hosted Q-Alchemy service.
export Q_ALCHEMY_API_KEY=...

# Optional: physical IBM execution. Omit IBM_QUANTUM_TOKEN for noisy Aer.
export IBM_QUANTUM_TOKEN=...
export IBM_QUANTUM_BACKEND=ibm_example   # optional; otherwise least busy
export IBM_QUANTUM_INSTANCE=...          # optional

 python examples/feasibility_quantum_chemistry.py

Verifying preparation circuits with the sparse simulator

Simulator jobs and their WorkData are preserved on execution, timeout or result download failure for diagnosis. After a successful download, remove_data=True requests cleanup; a cleanup failure logs a warning and still returns the result.

Q-Alchemy also hosts a sparse state-vector simulator so you can verify that a preparation circuit really produces your target state. The typical loop is prepare → simulate → verify:

from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector, state_fidelity
from q_alchemy import q_alchemy_as_qasm, SparseSimulator

qasm = q_alchemy_as_qasm(target, max_fidelity_loss=0.0)   # prepare
prep = QuantumCircuit.from_qasm_str(qasm)

sim = SparseSimulator()                                    # verify
sv = sim.sparse_statevector(prep)
print("fidelity:", state_fidelity(Statevector(sv.to_dense()), Statevector(target)))

SparseSimulator also exposes .counts(...) and .tomography(...), and it auto-selects your resource tier from your plan (Standard/Medium for everyone, XLarge for enterprise).

⚠️ The free plan is strongly limited — state preparation is capped (currently ~12 qubits, batches up to 100) and runs on the Medium simulator tier. Enterprise plans get larger circuits and the XLarge tier. See the limits table in the guide.

📖 Full guide: docs/initialize-and-verify.md · 🧪 Runnable notebook: examples/simulator_vs_initializer.ipynb

Using the simulator as a Qiskit backend

The hosted simulator is also exposed through Qiskit's standard BackendV2 interface — the same way IBM backends are used — so it drops straight into the Qiskit ecosystem (transpiler, Sampler, etc.):

from qiskit import QuantumCircuit, transpile
from q_alchemy import QAlchemyBackend

backend = QAlchemyBackend()                         # reads Q_ALCHEMY_API_KEY from env

qc = QuantumCircuit(2, 2)
qc.h(0); qc.cx(0, 1); qc.measure([0, 1], [0, 1])

job = backend.run(transpile(qc, backend), shots=4096)
print(job.result().get_counts())                    # {'00': ~2048, '11': ~2048}

backend.run(...) accepts Aer-style options (shots, seed_simulator, save_sparse_statevector, save_statevector, sparse_index_format, ...), and there's an IBM-style QAlchemyProvider().get_backend() for discovery. The resource tier (Medium vs the enterprise XLarge) is selected automatically from your plan — see the guide.

save_statevector vs save_sparse_statevector

Both ask for the state the circuit prepares, and both are served by the same remote call — the difference is what you get back, and how big it is.

save_statevector is the option you already know from Aer. It hands you the familiar dense 2**n vector, so Statevector, state_fidelity and friends all work unchanged:

qc = QuantumCircuit(2)
qc.h(0); qc.cx(0, 1)                                # no measurement: this is a state export

result = backend.run(qc, save_statevector=True).result()
print(result.data(0)["statevector"])
# [0.70710678+0.j 0.        +0.j 0.        +0.j 0.70710678+0.j]   -> length 2**n

Dense exports default to max_dense_qubits=26 (up to 1 GiB of amplitudes). Larger requests fail before submission. SparseStatevectorResult.to_dense() enforces the same default; pass a larger max_dense_qubits explicitly only when sufficient memory is available. Backend run() options apply only to that job; use backend.set_options() to change defaults for future jobs.

save_sparse_statevector is Q-Alchemy's own, and it is the one that scales. It returns only the amplitudes the circuit actually populates, so nothing of size 2**n is ever built:

result = backend.run(qc, save_sparse_statevector=True).result()
print(result.data(0)["sparse_statevector"])
# {'format': 'sparse_statevector_v1', 'num_qubits': 2, 'nnz': 2,
#  'index_format': 'hex', 'index_convention': 'little_endian',
#  'indices': ['0x0', '0x3'],
#  'amplitudes': [[0.7071067811865476, 0.0], [0.7071067811865476, 0.0]]}

Note the amplitudes are [real, imag] pairs: this entry is the simulator's own JSON payload, passed through untouched. If you would rather have parsed complex values — plus to_coo(), to_arrow() and amplitudes_dict() — go through the client instead of the backend, which returns a typed SparseStatevectorResult:

from q_alchemy import SparseSimulator

sv = SparseSimulator().sparse_statevector(qc)
print(sv.amplitudes)          # [(0.7071067811865476+0j), (0.7071067811865476+0j)]
print(sv.amplitudes_dict())   # {'0x0': (0.707...+0j), '0x3': (0.707...+0j)}

indices and amplitudes line up element by element, and nnz is how many were stored — two here, not four. That gap is the whole point, and it widens fast: a 40-qubit state-preparation circuit populating a thousand basis states returns a thousand amplitudes, while the dense form would need 2**40 complex numbers, or roughly 17 TB. A dense export is impossible in that regime; a sparse one is a few hundred kilobytes. Standard Qiskit backends offer no equivalent.

That only helps if the state is sparse, and nothing forces it to be. By default the simulator keeps every amplitude above 1e-10, so the result is exact, but a circuit that populates most of its basis states needs as much memory as the dense form and can exhaust the simulator. Pass max_nnz=N (to backend.run or SparseSimulator.sparse_statevector) to cap it: after every gate only the N largest amplitudes are kept and renormalised. The result is then approximate, and nothing in it marks that it was truncated.

Ask for both together and you pay for one simulation:

result = backend.run(qc, save_statevector=True, save_sparse_statevector=True).result()
dense = result.data(0)["statevector"]               # 2**n numpy array
sparse = result.data(0)["sparse_statevector"]       # nnz entries

Use the dense form for small circuits you want to compare against Qiskit directly; use the sparse form whenever 2**n would not fit — which is exactly the regime this simulator exists for. Reach for sparse_index_format (hex/bitstring) and sparse_index_convention (little_endian/big_endian) to control how the indices are written, and see the guide for feeding a sparse result straight back into the loader via to_coo().

From PennyLane

Because it's a standard Qiskit backend, you can use it as a PennyLane device via the PennyLane–Qiskit plugin (whose original version was written by this SDK's author, Carsten Blank):

import pennylane as qml
from q_alchemy import QAlchemyBackend

dev = qml.device("qiskit.remote", wires=2, backend=QAlchemyBackend(), shots=4096)

@qml.qnode(dev)
def circuit():
    qml.Hadamard(0)
    qml.CNOT([0, 1])
    return qml.counts()

Contributions

We welcome contributions - simply fork the repository of this plugin, and then make a pull request containing your contribution. All contributers to this plugin will be listed as authors on the releases.

We also encourage bug reports, suggestions for new features and enhancements!

Authors

Carsten Blank

License

The q-alchemy-sdk-py is free and open source, released under the Apache License, Version 2.0.

Optional Quantum I/O circuit compression

Enable full logical P+U compression through the execution plan. The public SDK transports these settings and requires no private compressor or Quantum I/O runtime installation:

from dataclasses import replace
from q_alchemy.quantum_io import CircuitCompressionConfig, local_simulator_execution_plan

plan = replace(local_simulator_execution_plan(),
               circuit_compression=CircuitCompressionConfig(enabled=True))
# report = service.run(experiment, plan).result()
# print(report.circuit_compression)
# print(report.format_summary())

Quantum I/O compression defaults to disabled; Feasibility keeps its enabled default. Reports include exactness semantics and input/output metrics, and older reports remain readable. Preparation claim diagnostics still describe original P.

Quantum I/O validation and reporting

Execution-plan helpers require positive integer shots; booleans and fractional values are rejected instead of truncated. The qiskit-aer provider alias, like aer, is local simulation and does not require remote-provider credentials. State serialization rejects zero and nonfinite amplitudes before upload, during the existing serialization pass. Wire schema versions must be integers, excluding booleans. Training and held-out Pauli operators must be distinct even when their labels differ.

Quantum I/O and Feasibility compression summaries use baseline_metrics when available, comparing the compiled baseline with the compressed circuit. Original logical input_metrics remain available; reports from older deployments still use those metrics when a baseline is absent.

The unused initialization helpers hash_state_vector and encode_statevector remain callable for compatibility but emit DeprecationWarning. Use the regular SDK state-upload/initialization API, which manages serialization and identity.

Feasibility request validation

Feasibility request schema versions must be integer 1; boolean options and criterion required must be actual booleans, and classical resource counts must be non-negative integers. Strings such as "false" and fractional counts are rejected instead of silently changing the request. Zero remains a valid explicit memory budget. These checks are local and require no private Feasibility dependency. The SDK displays the returned resource and quality conclusions separately.

Initialization transport and failure recovery

Initialization inline transport is selected by serialized/base64 payload size (at most 1 MiB), not qubit count; larger payloads use WorkData. Recoverable wait/download/validation failures preserve jobs and data. Exceptions expose initialization_job and, when available, initialization_job_url. With a caller-owned open client, retry run_job on that job; after an SDK-owned client closes, reconstruct Job.from_url using a fresh authenticated client and the saved URL. Supply expected_states for batches. Validated results are cached for cleanup retries; cleanup errors do not discard a successful result. Compression reports validate metrics and accepted-region fields before cleanup. The SDK continues to use public Qiskit.

Optional logical circuit return

Request the logical experiment circuit with QuantumIOService.run(experiment, plan, include_quantum_circuits=True). The default is False, with no circuit export cost. The service exports the existing logical preparation-plus-evolution circuit after compression, as report.quantum_circuit in the portable report's inner JSON object. Its envelope uses kind="quantum-circuit", schema_version=1, role="logical-experiment-circuit", format="qasm3", and a qasm program. It preserves global phase and excludes physical routing and measurement circuits. No preparation, compression or simulation is repeated to export the circuit.

In the SDK, report.quantum_circuit reconstructs a Qiskit QuantumCircuit on access; install the SDK's Qiskit extra. Use print(report.quantum_circuit.draw(output="text")) to print it. The downloaded payload remains available after job cleanup. Reports without a circuit return None, and ordinary report parsing does not require Qiskit.

PineXQ consumes service_options.include_quantum_circuits from the execution-plan input before parsing the core plan. This is a service option, not an ExecutionPlan field. Direct core callers can use run_quantum_experiment(..., include_quantum_circuits=True) for portable export; run_quantum_experiment_report(..., include_quantum_circuits=True) continues to retain runtime circuit objects locally without exporting them automatically.

Metadata

Release files for q-alchemy-sdk-py 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for q-alchemy-sdk-py 0.4.0
File Size Uploaded Provenance
q_alchemy_sdk_py-0.4.0.tar.gz 182.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for q-alchemy-sdk-py 0.4.0
File Interpreter ABI Platform
q_alchemy_sdk_py-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 287.3 kB

Release files / q_alchemy_sdk_py-0.4.0.tar.gz

Download URL q_alchemy_sdk_py-0.4.0.tar.gz
Size 182.4 kB
Tags Source
SHA-256 checksum
How to use checksums
c55bab651c537a513492ed2f7ab9a8d384946baa62f555b03de50f3396ff09ad
BLAKE2b-256 checksum
How to use checksums
04519809b787ae71434ad5f92c261eafa498e9e452f1650bd191ce730bc057c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / q_alchemy_sdk_py-0.4.0-py3-none-any.whl

Download URL q_alchemy_sdk_py-0.4.0-py3-none-any.whl
Size 104.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7f749d7d4d1b8f1a1fab48d285aa4d7d88ee50f228abc7bf8aefdc009c100305
BLAKE2b-256 checksum
How to use checksums
2ad2878d7b2bb52cd2c0bc45d14d16bd24193ee7a46bf65696e076842e0d6fd3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Security for q-alchemy-sdk-py 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for q_alchemy_sdk_py-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page