:py:mod:`pygsti.tools.leakage`
==============================
.. py:module:: pygsti.tools.leakage
Module Contents
---------------
Functions
~~~~~~~~~
.. autoapisummary::
pygsti.tools.leakage.set_docstring
pygsti.tools.leakage.tensorized_teststate_density
pygsti.tools.leakage.apply_tensorized_to_teststate
pygsti.tools.leakage.leading_dxd_submatrix_basis_vectors
pygsti.tools.leakage.subspace_entanglement_fidelity
pygsti.tools.leakage.subspace_jtracedist
pygsti.tools.leakage.subspace_superop_fro_dist
pygsti.tools.leakage.leaky_qubit_model_from_pspec
pygsti.tools.leakage.lagoified_gopparams_dicts
pygsti.tools.leakage.std_lago_gopsuite
pygsti.tools.leakage.add_lago_models
pygsti.tools.leakage.construct_leakage_report
Attributes
~~~~~~~~~~
.. autoapisummary::
pygsti.tools.leakage.NOTATION
pygsti.tools.leakage.CHOI_INDUCED_METRIC
.. py:data:: NOTATION
:value: Multiline-String
.. raw:: html
Show Value
.. code-block:: python
""""""
Default notation (deferential to text above)
--------------------------------------------
* H is a complex Hilbert space equipped with the standard basis.
* C, the computational subspace, is the complex-linear span of the first dim(C)
standard basis vectors of H.
* Given a complex Hilbert space, U, we write M[U] to denote the space of linear
operators from U to U. Elements of M[U] have natural matrix representations.
* Given a space of linear operators, L, we write S[L] for the set of linear
transformations ("superoperators") from L to L.
Matrix representations for elements of S[L] are only meaningful in the
presence of a designated basis for L.
If elements of L are naturally expressed as matrices, then a basis for L
lets us identify elements of L with vectors of length dim(L).
* If U denotes a complex Hilbert space (e.g., U=H or U=C), then we abbreviate
S[M[U]] by S[U].
""""""
.. raw:: html
.. py:function:: set_docstring(docstr)
.. py:function:: tensorized_teststate_density(dim: int, n_leak: int) -> numpy.ndarray
.. py:function:: apply_tensorized_to_teststate(op_x: numpy.ndarray, op_y, op_basis: numpy.ndarray, n_leak: int = 0) -> tuple[pygsti.baseobjs.basis.TensorProdBasis, numpy.ndarray, numpy.ndarray]
.. py:function:: leading_dxd_submatrix_basis_vectors(d: int, n: int, current_basis: pygsti.baseobjs.basis.Basis)
Let "H" denote n^2 dimensional Hilbert-Schdmit space, and let "U" denote the d^2
dimensional subspace of H spanned by vectors whose Hermitian matrix representations
are zero outside the leading d-by-d submatrix.
This function returns a column-unitary matrix "B" where P = B B^{\dagger} is the
orthogonal projector from H to U with respect to current_basis. We return B rather
than P only because it's simpler to get P from B than it is to get B from P.
See below for this function's original use-case.
Raison d'etre
-------------
Suppose we canonically measure the distance between two process matrices (M1, M2) by
D(M1, M2; H) = max || (M1 - M2) v ||
v is in H, (Eq. 1)
tr(v) = 1,
v is positive
for some norm || * ||. Suppose also that we want an analog of this distance when
(M1, M2) are restricted to the linear subspace U consisting of all vectors in H
whose matrix representations are zero outside of their leading d-by-d submatrix.
One natural way to do this is via the function D(M1, M2; U) -- i.e., just replace
H in (Eq. 1) with the subspace U. Using P to denote the orthogonal projector onto U,
we claim that we can evaluate this function via the identity
D(M1, M2; U) = D(M1 P, M2 P; H). (Eq. 2)
To see why this is the case, consider a positive vector v and its projection u = P v.
Since a vector is called positive whenever its Hermitian matrix representation is positive
semidefinite (PSD), we need to show that u is positive. This can be seen by considering
block 2-by-2 partitions of the matrix representations of (u,v), where the leading block
is d-by-d:
mat(v) = [x11, x12] and mat(u) = [x11, 0]
[x21, x22] [ 0, 0].
In particular, u is positive if and only if x11 is PSD, and x11 must be PSD for v
to be positive. Furthermore, positivity of v requires that x22 is PSD, which implies
0 <= tr(u) = tr(x11) <= tr(v).
Given this, it is easy to establish (Eq 2.) by considering how the following pair
of problems have the same optimal objective function value
max || (M1 - M2) P v || and max || (M1 - M2) P v ||
mat(v) = [x11, x12] mat(v) = [x11, x12]
[x21, x22] [x21, x22]
mat(v) is PSD x11 is PSD
tr(x11) + tr(x22) = 1 tr(x11) <= 1.
In fact, this can be taken a little further! The whole argument goes through unchanged
if, instead of starting with the objective function || (M1 - M2) v ||, we started with
f((M1 - M2) v) and f satisfied the property that f(c v) >= f(v) whenever c is a scalar
greater than or equal to one.
.. py:data:: CHOI_INDUCED_METRIC
:value: Multiline-String
.. raw:: html
Show Value
.. code-block:: python
""""""
The pair (op_x, op_y) represent some superoperators (X, Y) in S[H], using op_basis.
Let rho_t = tensorized_teststate_density(dim(H), n_leak), and set I to the identity
operator in S[H].
This function returns the %s between X⨂I(rho_t) and Y⨂I(rho_t).
*Warning!* At present, this function can only be used for gates over a single system
(e.g., a single qubit), not for tensor products of such systems.
Default notation (deferential to text above)
--------------------------------------------
* H is a complex Hilbert space equipped with the standard basis.
* C, the computational subspace, is the complex-linear span of the first dim(C)
standard basis vectors of H.
* Given a complex Hilbert space, U, we write M[U] to denote the space of linear
operators from U to U. Elements of M[U] have natural matrix representations.
* Given a space of linear operators, L, we write S[L] for the set of linear
transformations ("superoperators") from L to L.
Matrix representations for elements of S[L] are only meaningful in the
presence of a designated basis for L.
If elements of L are naturally expressed as matrices, then a basis for L
lets us identify elements of L with vectors of length dim(L).
* If U denotes a complex Hilbert space (e.g., U=H or U=C), then we abbreviate
S[M[U]] by S[U].
""""""
.. raw:: html
.. py:function:: subspace_entanglement_fidelity(op_x, op_y, op_basis, n_leak=0)
.. py:function:: subspace_jtracedist(op_x, op_y, op_basis, n_leak=0)
.. py:function:: subspace_superop_fro_dist(op_x, op_y, op_basis, n_leak=0)
.. py:function:: leaky_qubit_model_from_pspec(ps_2level: pygsti.processors.QubitProcessorSpec, mx_basis: Union[str, pygsti.baseobjs.basis.Basis] = 'l2p1', levels_readout_zero=(0, ), default_idle_gatename: pygsti.baseobjs.Label = Label(())) -> pygsti.models.explicitmodel.ExplicitOpModel
Return an ExplicitOpModel `m` whose (ideal) gates act on three-dimensional Hilbert space and whose members
are represented in `mx_basis`, constructed as follows:
The Hermitian matrix representation of m['rho0'] is the 3-by-3 matrix with a 1 in the upper-left
corner and all other entries equal to zero.
Operations in `m` are defined by taking each 2-by-2 unitary `u2` from ps_2level, and promoting it
to a 3-by-3 unitary according to
u3 = [u2[0, 0], u2[0, 1], 0]
[u2[1, 0], u2[1, 1], 0]
[ 0, 0, 1]
m['Mdefault'] has two effects, labeled "0" and "1". If E0 is the Hermitian matrix representation of
effect "0", then E0[i,i]=1 for all i in levels_readout_zero, and E0 is zero in all other components.
This function might be called in a workflow like the following:
from pygsti.models import create_explicit_model
from pygsti.algorithms import find_fiducials, find_germs
from pygsti.protocols import StandardGST, StandardGSTDesign, ProtocolData
# Step 1: Make the experiment design for the 1-qubit system.
tm_2level = create_explicit_model( ps_2level, ideal_spam_type='CPTPLND', ideal_gate_type='CPTPLND' )
fids = find_fiducials( tm_2level )
germs = find_germs( tm_2level )
lengths = [1, 2, 4, 8, 16, 32]
design = StandardGSTDesign( tm_2level, fids[0], fids[1], germs, lengths )
# Step 2: ... run the experiment specified by "design"; store results in a directory "dir" ...
# Step 3: read in the experimental data and run GST.
pd = ProtocolData.from_dir(dir)
tm_3level = leaky_qubit_model_from_pspec( ps_2level, basis='l2p1' )
gst = StandardGST( modes=('CPTPLND',), target_model=tm_3level, verbosity=4 )
res = gst.run(pd)
.. py:function:: lagoified_gopparams_dicts(gopparams_dicts: List[Dict]) -> List[Dict]
goppparams_dicts is a list-of-dicts (LoDs) representation of a gauge optimization suite
suitable for models without leakage (e.g., a model of a 2-level system).
This function returns a new gauge optimization suite (also in the LoDs representation)
by applying leakage-specific modifications to a deep-copy of gopparams_dicts.
Example
-------
Suppose we have a ModelEstimateResults object called `results` that includes a
CPTPLND estimate, and we want to update the models of that estimate to include
two types of leakage-aware gauge optimization.
#
# Step 1: get the input to this function
#
estimate = results.estimates['CPTPLND']
model = estimate.models['target']
stdsuite = GSTGaugeOptSuite(gaugeopt_suite_names=('stdgaugeopt',))
gopparams_dicts = stdsuite.to_dictionary(model)['stdgaugeopt']
#
# Step 2: use this function to build our GSTGaugeOptSuite.
#
s = lagoified_gopparams_dicts(gopparams_dicts)
c = lagoified_gopparams_dicts(gopparams_dicts)
c[-1]['gates_metric'] = 'fidelity'
c[-1]['spam_metric'] = 'fidelity'
# ^ this example's "custom" leakage gauge optimization uses an infidelity loss,
# rather than the default of squared Frobenius loss.
specification = {'LAGO-std': s,'LAGO-custom': c}
gos = GSTGaugeOptSuite(gaugeopt_argument_dicts=specification)
#
# Step 3: updating `estimate` requires that we modify `results`.
#
add_lago_models(results, 'CPTPLND', gos)
After those lines execute, the `estimate.models` dict will have two new
key-value pairs, where the keys are 'LAGO-std' and 'LAGO-custom'.
.. py:function:: std_lago_gopsuite(model: pygsti.models.ExplicitOpModel) -> dict[str, list[dict]]
Return a dictionary of the form {'LAGO': v}, where v is a
list-of-dicts representation of a gauge optimization suite.
We construct v by getting the list-of-dicts representation of the
"stdgaugeopt" suite for `model`, and then changing some of its
options to be suitable for leakage-aware gauge optimization. These
changes are made in the `lagoified_gopparams_dicts` function.
.. py:function:: add_lago_models(results: pygsti.protocols.gst.ModelEstimateResults, est_key: Optional[str] = None, gos: Optional[pygsti.protocols.gst.GSTGaugeOptSuite] = None, verbosity: int = 0)
Update each estimate in results.estimates (or just results.estimates[est_key],
if est_key is not None) with a model obtained by parameterization-preserving
leakage-aware gauge optimization.
If no gauge optimization suite is provided, then we construct one by making
appropriate modifications to either the estimate's existing 'stdgaugeopt' suite
(if that exists) or to the 'stdgaugeopt' suite induced by the target model.
.. py:function:: construct_leakage_report(results: pygsti.protocols.gst.ModelEstimateResults, title: str = 'auto', extra_report_kwargs: Optional[dict[str, Any]] = None, gaugeopt_verbosity: int = 0)
This is a small wrapper around construct_standard_report. It generates a Report object
with leakage analysis, and returns that object along with a copy of ``results`` which
contains gauge-optimized models created during leakage analysis.
Notes
-----
The special gauge optimization performed by this function uses the unitary gauge group,
and uses a modified version of the Frobenius distance loss function. The modification
reflects how the target gates in a leakage model are only _really_ defined on the
computational subspace.