qsim_backend
eigenkit.core.backends.qsim_backend
The qsim backend, through Cirq.
qsim is a fast Schrödinger statevector simulator from Google, and qsimcirq
connects it to Cirq. Both are in the optional [qsim] extra
(pip install eigenkit[qsim]) and are imported only when needed, so the rest of
the library works without them.
The circuit is translated once into a parametrised cirq.Circuit, whose
angles are SymPy symbols w{i} and x{i}. Each call sets the values of the
symbols and runs the circuit on a qsimcirq.QSimSimulator. Expectation values
use the native qsim method simulate_expectation_values (each Observable of
the library becomes a cirq.PauliSum), and statevectors come from
simulate(...).final_state_vector.
It declares gradient_method = "parameter_shift": qsim has no cheap analytic
reverse-mode jacobian here, so the gradient code of the library differentiates
with the parameter-shift rule.
Precision. qsim is a single-precision simulator (32-bit floating point),
and the pip wheel has no double-precision (64-bit) build. The outputs are
returned as complex128, but they carry an accumulated error of about 1e-6, so
they agree with a double-precision backend to about 1e-4, not to machine
precision. This difference comes from the precision and is not a bug, as with
the single-precision path of CUDA Quantum. It does mean that qsim is the wrong
choice when you need exact agreement rather than speed.
Bit order: Cirq orders the statevector with the first qubit of qubit_order
as the most significant bit. This backend passes
[LineQubit(0), …, LineQubit(n-1)], so index 0 is qubit 0, the most
significant bit. This is the same convention as in NumPy and PennyLane. No
reordering is needed, and the backend comparison tests check this against the
NumPy reference. The number of threads follows OMP_NUM_THREADS, unless a
configuration sets it.
On macOS, importing this module sets KMP_DUPLICATE_LIB_OK=TRUE (unless it is
already set). qsimcirq and PyTorch each include their own OpenMP runtime, and
without this setting the second one to start aborts the process.
Example
from eigenkit import Circuit, Encoding, Variational, all_z
blocks = [Encoding(in_features=4), Variational(depth=2)]
# by registered name
circuit = Circuit(blocks=blocks, observables=all_z(4), provider="qsim")
# or with the config, when a device setting matters
from eigenkit.configs import QsimConfig
circuit = Circuit(
blocks=blocks,
observables=all_z(4),
provider=QsimConfig(threads=8),
)
qsim is fast and single-precision, with an error of about 1e-6. This
backend needs the qsim extra.
QsimBackend
The provider for qsim, the statevector simulator from Google, through Cirq.
It is registered as "qsim". It is internal: you select this backend with
provider="qsim" or by passing a
QsimConfig, not by
building this class.
capabilities
property
What this backend supports.
It runs the subset of gates that can be translated to Cirq, returns exact
expectation values only, does not evaluate a batch in one call, and has no
limit on the number of qubits. A circuit that uses a gate outside this
subset is rejected before compilation, with a message that names the
gate. See Capabilities.
__init__(config=None, name='qsim')
Build the backend from a device configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
QsimConfig
|
The device settings. Default: |
None
|
name
|
str
|
The registry name, used in error messages. Default:
|
'qsim'
|
compile(circuit, observables)
Prepare circuit for repeated evaluation of observables.
A Circuit calls it once and reuses the result for every call, so the
expensive work belongs here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
CircuitSpec
|
The circuit to prepare. |
required |
observables
|
Sequence[Observable]
|
The observables to measure. |
required |
Returns:
| Type | Description |
|---|---|
QsimCompiled
|
The prepared circuit, as a |
QsimCompiled
Bases: CompiledCircuit
A circuit prepared for qsim, as a Cirq circuit.
It is internal:
QsimBackend.compile()
builds it and returns it, and it is never built directly.
__init__(circuit, observables, config)
Prepare the circuit for repeated evaluation.
QsimBackend.compile() builds this object, so there is no reason to
build one directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
CircuitSpec
|
The circuit to prepare. |
required |
observables
|
Sequence[Observable]
|
The observables to measure. |
required |
config
|
QsimConfig
|
The device settings. |
required |
run(weights, inputs)
Evaluate the circuit and return one expectation value per observable.
It follows the CompiledCircuit contract: the result has shape (n_obs,)
when weights and inputs are both 1-D, and (B, n_obs) otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector: 1-D for one sample, or 2-D with a leading batch axis. |
required |
inputs
|
ndarray
|
The input features, with the same 1-D or 2-D
shape convention as |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The expectation values, one for each observable. |
statevector(weights, inputs, initial_state=None)
Return the full complex statevector after the circuit has run.
The result is a flat array of 2**n_qubits amplitudes, with qubit 0 as
the most significant bit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector (1-D). |
required |
inputs
|
ndarray
|
The input features: 1-D for one sample, or 2-D with a leading batch axis. |
required |
initial_state
|
ndarray | None
|
Must be |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
The statevector, of shape |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If |
zero_state_probability(weights, inputs)
Return the probability of measuring the state |0…0⟩.
Fidelity kernels and swap tests are built on it. It does not go through
Observable, because it is not the expectation value of a Pauli operator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector (1-D). |
required |
inputs
|
ndarray
|
The input features of one sample (1-D). |
required |
Returns:
| Type | Description |
|---|---|
float
|
The probability, a float in |
QsimConfig
dataclass
Device configuration for the qsim backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threads
|
int | None
|
The size of the internal thread pool of qsim. |
None
|
make_backend()
Build the backend that this configuration describes.
The provider layer calls it when a configuration is passed as provider=.
There is no reason to call it directly.
Returns:
| Type | Description |
|---|---|
'QsimBackend'
|
The |