Source code for mimiqcircuits.backends.fidelity
#
# Copyright © 2023-2026 QPerfect. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Typed fidelity values returned by :meth:`Backend.evolve`.
Different simulators report fidelity with different semantics: a
state-vector backend can claim exactness, an MPS backend can only
return a truncation lower bound, and a randomised-benchmarking
backend can return a sample-based estimate with a standard error.
A plain ``float`` return would silently lose those distinctions.
The tagged variants below preserve them:
- :class:`ExactFidelity`
- :class:`UnknownFidelity`
- :class:`TruncationLowerBound`
- :class:`LowerBoundPerStep`
- :class:`EstimatedFidelity`
Reduce to plain numbers via :func:`as_lower_bound` (conservative),
:func:`as_expected` (central), or :func:`as_interval` (``(lo, hi)``).
"""
from __future__ import annotations
from dataclasses import dataclass
import math
[docs]
class Fidelity:
"""Base class for typed fidelity values. See the module docstring
for the list of variants and the reducer functions."""
[docs]
@dataclass(frozen=True)
class ExactFidelity(Fidelity):
"""The simulator believes the state is exact.
BLAS and SIMD round-off are not tracked; this is a structural
claim about the algorithm, not a bit-perfect guarantee.
"""
[docs]
@dataclass(frozen=True)
class UnknownFidelity(Fidelity):
"""The backend does not track fidelity.
Reducers return ``nan``. Prefer this variant over inventing a
placeholder value when you genuinely do not know.
"""
[docs]
@dataclass(frozen=True)
class TruncationLowerBound(Fidelity):
"""A single scalar lower bound on the whole-circuit fidelity.
Typical for MPS / MPO simulators, where ``value`` is the product
of singular-value tails discarded during compression.
"""
value: float
[docs]
@dataclass(frozen=True)
class LowerBoundPerStep(Fidelity):
"""Per-step lower-bound contributions.
``as_lower_bound`` returns ``prod(clip(values, 0, 1))``, which is
a lower bound on the circuit fidelity *only when successive
truncation errors are uncorrelated*. If your backend cannot make
that assumption, collapse to a single
:class:`TruncationLowerBound` at construction time.
"""
values: tuple[float, ...]
[docs]
@dataclass(frozen=True)
class EstimatedFidelity(Fidelity):
"""Sample-based fidelity estimate with a standard error.
Use for randomised benchmarking, direct fidelity estimation, or
cross-entropy benchmarking. ``as_lower_bound`` returns
``max(0, mean − 3·stderr)``; ``as_interval`` returns the ±1σ band.
"""
mean: float
stderr: float
# ──────────────────────────────────────────────────────────────────────────
# Reducers
# ──────────────────────────────────────────────────────────────────────────
[docs]
def as_lower_bound(f: Fidelity) -> float:
"""Reduce ``f`` to a conservative scalar lower bound.
Returns ``nan`` when ``f`` is :class:`UnknownFidelity` so that
statistics over mixed-type fidelities do not silently treat
"unknown" as "good".
"""
if isinstance(f, ExactFidelity):
return 1.0
if isinstance(f, UnknownFidelity):
return math.nan
if isinstance(f, TruncationLowerBound):
return f.value
if isinstance(f, LowerBoundPerStep):
prod = 1.0
for v in f.values:
prod *= max(0.0, min(1.0, v))
return prod
if isinstance(f, EstimatedFidelity):
return max(0.0, f.mean - 3 * f.stderr)
raise TypeError(f"Unknown Fidelity subtype: {type(f).__name__}")
[docs]
def as_expected(f: Fidelity) -> float:
"""Reduce ``f`` to a central / expected scalar.
For :class:`TruncationLowerBound` the lower bound *is* the
estimate; for :class:`EstimatedFidelity` it is the sample mean.
Returns ``nan`` for :class:`UnknownFidelity`.
"""
if isinstance(f, ExactFidelity):
return 1.0
if isinstance(f, UnknownFidelity):
return math.nan
if isinstance(f, TruncationLowerBound):
return f.value
if isinstance(f, LowerBoundPerStep):
prod = 1.0
for v in f.values:
prod *= max(0.0, min(1.0, v))
return prod
if isinstance(f, EstimatedFidelity):
return f.mean
raise TypeError(f"Unknown Fidelity subtype: {type(f).__name__}")
[docs]
def as_interval(f: Fidelity) -> tuple[float, float]:
"""``(lo, hi)`` band. ±1σ for :class:`EstimatedFidelity`; otherwise
``(as_lower_bound, as_expected)``.
"""
if isinstance(f, EstimatedFidelity):
return (f.mean - f.stderr, f.mean + f.stderr)
return (as_lower_bound(f), as_expected(f))
# ──────────────────────────────────────────────────────────────────────────
# Coercion helper (private)
# ──────────────────────────────────────────────────────────────────────────
def _to_fidelity(x) -> Fidelity:
"""Coerce a stand-in scalar into a typed :class:`Fidelity`.
- ``None`` → :class:`UnknownFidelity`
- ``1.0`` → :class:`ExactFidelity`
- other float-likes → :class:`TruncationLowerBound`
- existing :class:`Fidelity` instances pass through unchanged.
Transitional adapter for backends still returning plain floats.
WARNING: a value of exactly ``1.0`` re-types as
:class:`ExactFidelity`. MPS-style truncation lower bounds that
happen to land at 1.0 should be wrapped explicitly with
:class:`TruncationLowerBound` to keep their semantics.
"""
if x is None:
return UnknownFidelity()
if isinstance(x, Fidelity):
return x
try:
v = float(x)
except (TypeError, ValueError):
raise TypeError(f"cannot coerce {x!r} to Fidelity")
return ExactFidelity() if v == 1.0 else TruncationLowerBound(v)