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 37 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 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
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
136 changes: 75 additions & 61 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,33 @@ def _mock_objective_fn(v):
else:
# non-least-squares case where objective function returns a single float
# and (currently) there's no analytic jacobian
if n_leak > 0:
dim = int(_np.sqrt(mxBasis.dim))
B = _tools.leading_dxd_submatrix_basis_vectors(dim - n_leak, dim, mxBasis)
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 = None

assert gates_metric != "frobeniustt"
assert spam_metric != "frobeniustt"
# ^ 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 +625,83 @@ 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)
# 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.
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
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)
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 += transformed_target.frobeniusdist(model, None, wts)
val = mdl.frobeniusdist(target_model, transform_mx_arg, wts)
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