Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: leakage-aware gauge optimization #410

Draft
wants to merge 53 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
05d5e4f
bugfix for my attemped efficiency improvement
rileyjmurray Feb 15, 2024
6b6f231
extend basistools to gracefully accomodate objects that derive from L…
rileyjmurray Feb 15, 2024
2e4ee93
readability tweak and comments
rileyjmurray Feb 21, 2024
f2b1daf
update function in reportables to indicate API limitation. Bugfix in …
rileyjmurray Feb 22, 2024
91d0790
update implementation of state fidelity
rileyjmurray Feb 22, 2024
005f56a
comments. About to remove.
rileyjmurray Feb 28, 2024
c57b0a7
extend implementation to support bases other than pauli-product. Add …
rileyjmurray Feb 28, 2024
c8c77e9
remove unused and broken function that I only added for debugging pur…
rileyjmurray Feb 28, 2024
fabe689
comment clarification
rileyjmurray Feb 28, 2024
31f89c9
clean up implementation of ExplicitOpModelCalc.residuals
rileyjmurray Mar 4, 2024
b41e324
slightly simplify minimize(...) function. There was an unnecessary br…
rileyjmurray Mar 4, 2024
851cc98
factor out the logic needed to set up calculation of leaky entangleme…
rileyjmurray Mar 4, 2024
1809942
add implementation of leaky jtracedist
rileyjmurray Mar 4, 2024
4418b06
enable gauge optimization with leakage-aware metrics using non-LS opt…
rileyjmurray Mar 4, 2024
53430e9
support for leakage-aware Frobenius distance with non-LS optimizer. A…
rileyjmurray Mar 4, 2024
26ea09d
left out a function needed for code in the last commit to work
rileyjmurray Mar 4, 2024
0856ffb
tests for the leaky_entanglement_fidelity function that Ive had for a…
rileyjmurray Mar 4, 2024
a360da3
fix syntax mistake
rileyjmurray Mar 4, 2024
d2f3ada
rename test_gauageopt.py file to indicate that its contents only test…
rileyjmurray Mar 4, 2024
e267adc
correctness test for gauge optimization (ls-based and L-BFGS-based)
rileyjmurray Mar 19, 2024
75b1d34
fix typo
rileyjmurray Mar 19, 2024
afd0d0d
remove some awkward casting that it turns out wasnt needed
rileyjmurray Mar 19, 2024
18173ca
remove unnecessary comment changes
rileyjmurray Apr 2, 2024
38072e6
change how leakage affects distances between SPAM model members for F…
rileyjmurray Apr 15, 2024
1c7d5aa
changes in README for MFT_20230125 from Corey
rileyjmurray Apr 15, 2024
3f2d7e9
tiny docstring change
rileyjmurray Apr 19, 2024
615f2cc
very messy support for subspace-restricted error metrics
rileyjmurray May 7, 2024
2f23347
de-nest two lines
rileyjmurray Sep 24, 2024
dbf761f
Add utilities for constructing CVXPY models involving (convex) diamon…
rileyjmurray Sep 27, 2024
10aca43
fix bugs in changes from last commit
rileyjmurray Sep 27, 2024
c864a5e
add ability to return labels from leading_dxd_submatrix_basis_vectors
rileyjmurray Sep 28, 2024
bb55d13
split insanely complicated casting function into simpler functions
rileyjmurray Sep 28, 2024
81ff50c
define new OuterProductBasis class. Unclear if we'll want to keep this.'
rileyjmurray Sep 28, 2024
82e8bc0
rollback change in 1c7d5aaf9d2e45888132c1d6995e3d868a039ba2
rileyjmurray Sep 28, 2024
bde0e5d
bugfixes in earlier changes
rileyjmurray Sep 28, 2024
a83c2e3
remove classical_label argument from Basis.cast
rileyjmurray Sep 28, 2024
4b4635c
remove explicit call to MOSEK
rileyjmurray Sep 28, 2024
a522735
update add_gauge_opt so it can accept a dict specifying a gaugeopt su…
rileyjmurray Nov 18, 2024
828cfa7
REVERT ME
rileyjmurray Nov 18, 2024
6dddc16
comments pointing to possible bugs
rileyjmurray Nov 18, 2024
f580830
note
rileyjmurray Nov 18, 2024
f916247
leave note for myself
rileyjmurray Nov 18, 2024
a138b7b
reduce number of options available in non-LS gauge optimization
rileyjmurray Nov 21, 2024
1f49e43
last commit actually just included comments. Material changes are in …
rileyjmurray Nov 21, 2024
6e9ccb1
simplify gaugeopt in the non-LS case
rileyjmurray Nov 21, 2024
5783263
remove non-Frobenius objectives from gauge optimization
rileyjmurray Nov 21, 2024
dfb1118
status for notebook 2024-11-21a.ipynb
rileyjmurray Nov 21, 2024
85f0a1f
bugfix in leading_dxd_submatrix_basis_vectors helper function
rileyjmurray Nov 22, 2024
82b8c70
Revert "status for notebook 2024-11-21a.ipynb"
rileyjmurray Nov 22, 2024
6ced5a3
Revert "remove non-Frobenius objectives from gauge optimization"
rileyjmurray Nov 22, 2024
85754b7
Revert "simplify gaugeopt in the non-LS case"
rileyjmurray Nov 22, 2024
778b15b
Revert "last commit actually just included comments. Material changes…
rileyjmurray Nov 22, 2024
48eac7d
improved HTML reporting when n_leak=1
rileyjmurray Nov 22, 2024
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
171 changes: 107 additions & 64 deletions pygsti/algorithms/gaugeopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def gaugeopt_to_target(model, target_model, item_weights=None,
gauge_group=None, method='auto', maxiter=100000,
maxfev=None, tol=1e-8, oob_check_interval=0,
convert_model_to=None, return_all=False, comm=None,
verbosity=0, check_jac=False):
verbosity=0, check_jac=False, n_leak=0):
"""
Optimize the gauge degrees of freedom of a model to that of a target.

Expand Down Expand Up @@ -170,7 +170,7 @@ def gaugeopt_to_target(model, target_model, item_weights=None,
objective_fn, jacobian_fn = _create_objective_fn(
model, target_model, item_weights,
cptp_penalty_factor, spam_penalty_factor,
gates_metric, spam_metric, method, comm, check_jac)
gates_metric, spam_metric, method, comm, check_jac, n_leak)

result = gaugeopt_custom(model, objective_fn, gauge_group, method,
maxiter, maxfev, tol, oob_check_interval,
Expand Down Expand Up @@ -303,9 +303,6 @@ def _call_jacobian_fn(gauge_group_el_vec):

printer.log("--- Gauge Optimization (%s method, %s) ---" % (method, str(type(gauge_group))), 2)
if method == 'ls':
#minSol = _opt.least_squares(_call_objective_fn, x0, #jac=_call_jacobian_fn,
# max_nfev=maxfev, ftol=tol)
#solnX = minSol.x
assert(_call_jacobian_fn is not None), "Cannot use 'ls' method unless jacobian is available"
ralloc = _baseobjs.ResourceAllocation(comm) # FUTURE: plumb up a resource alloc object?
test_f = _call_objective_fn(x0)
Expand Down Expand Up @@ -353,7 +350,7 @@ def _call_jacobian_fn(gauge_group_el_vec):
def _create_objective_fn(model, target_model, item_weights=None,
cptp_penalty_factor=0, spam_penalty_factor=0,
gates_metric="frobenius", spam_metric="frobenius",
method=None, comm=None, check_jac=False):
method=None, comm=None, check_jac=False, n_leak=0):
"""
Creates the objective function and jacobian (if available)
for gaugeopt_to_target
Expand Down Expand Up @@ -590,18 +587,48 @@ def _mock_objective_fn(v):
else:
# non-least-squares case where objective function returns a single float
# and (currently) there's no analytic jacobian
dim = int(_np.sqrt(mxBasis.dim))
if n_leak > 0:
B = _tools.leading_dxd_submatrix_basis_vectors(dim - n_leak, dim, mxBasis)
"""
^ Need to do something else.

In a leakage-friendly basis the operation matrices can be partitioned as
[comp , semileak1]
[semileak2, fulleak ].
Instead of projecting only on to comp, we want to keep everything
except fullleak. ... But I don't think we can implement that just by
pre-multiplying by a projector.

I think this distinction might not be significant under certain idealized
assumptions, but small deviations from those conditions (which are certain
to happen in practice) might make it matter.
"""
P = B @ B.T.conj()
if _np.linalg.norm(P.imag) > 1e-12:
raise ValueError()
else:
P = P.real
transform_mx_arg = (P, None)
# ^ The semantics of this tuple are defined by the frobeniusdist function
# in the ExplicitOpModelCalc class. There are intended semantics for
# the second element of the tuple, but those aren't implemented yet so
# for now I'm setting the second entry to None. -- Riley
else:
transform_mx_arg = (_np.eye(mxBasis.dim), None)

assert gates_metric != "frobeniustt"
assert spam_metric != "frobeniustt"
# assert spam_metric == gates_metric
# ^ Erik and Corey said these are rarely used. I've removed support for
# them in this codepath (non-LS optimizer) in order to make it easier to
# read my updated code for leakage-aware metrics. It wouldn't be hard to
# add support back, but I just want to keep things simple. -- Riley

def _objective_fn(gauge_group_el, oob_check):
mdl = _transform_with_oob_check(model, gauge_group_el, oob_check)
ret = 0

if gates_metric == "frobeniustt" or spam_metric == "frobeniustt":
full_target_model = target_model.copy()
full_target_model.convert_members_inplace("full") # so we can gauge-transform the target model.
transformed_target = _transform_with_oob_check(full_target_model, gauge_group_el.inverse(), oob_check)
else:
transformed_target = None

if cptp_penalty_factor > 0:
mdl.basis = mxBasis # set basis for jamiolkowski iso
cpPenaltyVec = _cptp_penalty(mdl, cptp_penalty_factor, mdl.basis)
Expand All @@ -613,81 +640,97 @@ def _objective_fn(gauge_group_el, oob_check):
ret += _np.sum(spamPenaltyVec)

if target_model is not None:
if gates_metric == "frobenius":
if spam_metric == "frobenius":
ret += mdl.frobeniusdist(target_model, None, item_weights)
else:
wts = item_weights.copy(); wts['spam'] = 0.0
for k in wts:
if k in mdl.preps or \
k in mdl.povms: wts[k] = 0.0
ret += mdl.frobeniusdist(target_model, None, wts)

elif gates_metric == "frobeniustt":
if spam_metric == "frobeniustt":
ret += transformed_target.frobeniusdist(model, None, item_weights)
"""
Leakage-aware metric supported, per implementation in mdl.frobeniusdist.
Refer to how mdl.frobeniusdist handles the case when transform_mx_arg
is a tuple in order to understand how the leakage-aware metric is defined.

Idea: raise an error if mxBasis isn't leakage-friendly.
PROBLEM: if we're deep in the code then we end up needing to be
working in a basis that has the identity matrix as its
first element. The leakage-friendly basis doesn't even
have the identity matrix as an element, let alone the first element

TODO: dig into function calls below and see where we can
access the mxBasis object. (I'm sure we can.)
Looks like it isn't accessible within ExplicitOpModelCalc objects.
It's most definitely available in ExplicitOpModel.
"""
if "frobenius" in gates_metric:
if spam_metric == gates_metric:
val = mdl.frobeniusdist(target_model, transform_mx_arg, item_weights)
else:
wts = item_weights.copy(); wts['spam'] = 0.0
wts = item_weights.copy()
wts['spam'] = 0.0
for k in wts:
if k in mdl.preps or \
k in mdl.povms: wts[k] = 0.0
ret += transformed_target.frobeniusdist(model, None, wts)
if k in mdl.preps or k in mdl.povms:
wts[k] = 0.0
val = mdl.frobeniusdist(target_model, transform_mx_arg, wts, n_leak)
if "squared" in gates_metric:
val = val ** 2
ret += val

elif gates_metric == "fidelity":
# Leakage-aware metric supported, using leaky_entanglement_fidelity.
for opLbl in mdl.operations:
wt = item_weights.get(opLbl, opWeight)
ret += wt * (1.0 - _tools.entanglement_fidelity(
target_model.operations[opLbl], mdl.operations[opLbl]))**2
top = target_model.operations[opLbl].to_dense()
mop = mdl.operations[opLbl].to_dense()
ret += wt * (1.0 - _tools.leaky_entanglement_fidelity(top, mop, mxBasis, n_leak))**2

elif gates_metric == "tracedist":
# Leakage-aware metric supported, using leaky_jtracedist.
for opLbl in mdl.operations:
wt = item_weights.get(opLbl, opWeight)
ret += opWeight * _tools.jtracedist(
target_model.operations[opLbl], mdl.operations[opLbl])
top = target_model.operations[opLbl].to_dense()
mop = mdl.operations[opLbl].to_dense()
ret += wt * _tools.leaky_jtracedist(top, mop, mxBasis, n_leak)

else: raise ValueError("Invalid gates_metric: %s" % gates_metric)

if spam_metric == "frobenius":
if gates_metric != "frobenius": # otherwise handled above to match normalization in frobeniusdist
wts = item_weights.copy(); wts['gates'] = 0.0
for k in wts:
if k in mdl.operations or \
k in mdl.instruments: wts[k] = 0.0
ret += mdl.frobeniusdist(target_model, None, wts)

elif spam_metric == "frobeniustt":
if gates_metric != "frobeniustt": # otherwise handled above to match normalization in frobeniusdist
wts = item_weights.copy(); wts['gates'] = 0.0
for k in wts:
if k in mdl.operations or \
k in mdl.instruments: wts[k] = 0.0
ret += transformed_target.frobeniusdist(model, None, wts)
if "frobenius" in spam_metric and gates_metric == spam_metric:
# We already handled SPAM error in this case. Just return.
return ret

elif "frobenius" in spam_metric:
# Leakage-aware metric supported in principle via implementation in
# mdl.frobeniusdist (check implementation to see how it handles the
# case when transform_mx_arg is a tuple).
wts = item_weights.copy(); wts['gates'] = 0.0
for k in wts:
if k in mdl.operations or \
k in mdl.instruments: wts[k] = 0.0
val = mdl.frobeniusdist(target_model, transform_mx_arg, wts)
if "squared" in spam_metric:
val = val ** 2
ret += val

elif spam_metric == "fidelity":
for preplabel, prep in mdl.preps.items():
# Leakage-aware metrics NOT available
for preplabel, m_prep in mdl.preps.items():
wt = item_weights.get(preplabel, spamWeight)
rhoMx1 = _tools.vec_to_stdmx(prep, mxBasis)
rhoMx2 = _tools.vec_to_stdmx(
target_model.preps[preplabel], mxBasis)
rhoMx1 = _tools.vec_to_stdmx(m_prep.to_dense(), mxBasis)
t_prep = target_model.preps[preplabel]
rhoMx2 = _tools.vec_to_stdmx(t_prep.to_dense(), mxBasis)
ret += wt * (1.0 - _tools.fidelity(rhoMx1, rhoMx2))**2

for povmlabel, povm in mdl.povms.items():
for povmlabel in mdl.povms.keys():
wt = item_weights.get(povmlabel, spamWeight)
ret += wt * (1.0 - _tools.povm_fidelity(
mdl, target_model, povmlabel))**2
fidelity = _tools.povm_fidelity(mdl, target_model, povmlabel)
ret += wt * (1.0 - fidelity)**2

elif spam_metric == "tracedist":
for preplabel, prep in mdl.preps.items():
# Leakage-aware metrics NOT available.
for preplabel, m_prep in mdl.preps.items():
wt = item_weights.get(preplabel, spamWeight)
rhoMx1 = _tools.vec_to_stdmx(prep, mxBasis)
rhoMx2 = _tools.vec_to_stdmx(
target_model.preps[preplabel], mxBasis)
rhoMx1 = _tools.vec_to_stdmx(m_prep.to_dense(), mxBasis)
t_prep = target_model.preps[preplabel]
rhoMx2 = _tools.vec_to_stdmx(t_prep.to_dense(), mxBasis)
ret += wt * _tools.tracedist(rhoMx1, rhoMx2)

for povmlabel, povm in mdl.povms.items():
for povmlabel in mdl.povms.keys():
wt = item_weights.get(povmlabel, spamWeight)
ret += wt * (1.0 - _tools.povm_jtracedist(
mdl, target_model, povmlabel))**2
ret += wt * _tools.povm_jtracedist(mdl, target_model, povmlabel)

else: raise ValueError("Invalid spam_metric: %s" % spam_metric)

Expand Down
Loading
Loading