@@ -587,24 +587,45 @@ def _mock_objective_fn(v):
587
587
else :
588
588
# non-least-squares case where objective function returns a single float
589
589
# and (currently) there's no analytic jacobian
590
+ dim = int (_np .sqrt (mxBasis .dim ))
591
+ if n_leak > 0 :
592
+ B = _tools .leading_dxd_submatrix_basis_vectors (dim - n_leak , dim , mxBasis )
593
+ """
594
+ ^ Need to do something else.
595
+
596
+ In a leakage-friendly basis the operation matrices can be partitioned as
597
+ [comp , semileak1]
598
+ [semileak2, fulleak ].
599
+ Instead of projecting only on to comp, we want to keep everything
600
+ except fullleak. ... But I don't think we can implement that just by
601
+ pre-multiplying by a projector.
602
+
603
+ I think this distinction might not be significant under certain idealized
604
+ assumptions, but small deviations from those conditions (which are certain
605
+ to happen in practice) might make it matter.
606
+ """
607
+ P = B @ B .T .conj ()
608
+ if _np .linalg .norm (P .imag ) > 1e-12 :
609
+ raise ValueError ()
610
+ else :
611
+ P = P .real
612
+ transform_mx_arg = (P , None )
613
+ # ^ The semantics of this tuple are defined by the frobeniusdist function
614
+ # in the ExplicitOpModelCalc class. There are intended semantics for
615
+ # the second element of the tuple, but those aren't implemented yet so
616
+ # for now I'm setting the second entry to None. -- Riley
617
+ else :
618
+ transform_mx_arg = (_np .eye (mxBasis .dim ), None )
590
619
591
- assert target_model is not None
592
620
assert gates_metric != "frobeniustt"
593
621
assert spam_metric != "frobeniustt"
622
+ assert spam_metric == gates_metric
623
+ metric = spam_metric
624
+ # assert spam_metric == gates_metric
594
625
# ^ Erik and Corey said these are rarely used. I've removed support for
595
626
# them in this codepath (non-LS optimizer) in order to make it easier to
596
627
# read my updated code for leakage-aware metrics. It wouldn't be hard to
597
628
# add support back, but I just want to keep things simple. -- Riley
598
- assert spam_metric == gates_metric
599
- metric = spam_metric
600
-
601
- dim = int (_np .sqrt (mxBasis .dim ))
602
- B = _tools .leading_dxd_submatrix_basis_vectors (dim - n_leak , dim , mxBasis )
603
- P = B @ B .T .conj ()
604
- assert _np .linalg .norm (P .imag ) <= 1e-12
605
- P = P .real
606
-
607
- log = []
608
629
609
630
def _objective_fn (gauge_group_el , oob_check ):
610
631
mdl = _transform_with_oob_check (model , gauge_group_el , oob_check )
@@ -620,35 +641,25 @@ def _objective_fn(gauge_group_el, oob_check):
620
641
spamPenaltyVec = _spam_penalty (mdl , spam_penalty_factor , mdl .basis )
621
642
ret += _np .sum (spamPenaltyVec )
622
643
644
+ assert target_model is not None
645
+ """
646
+ Leakage-aware metric supported, per implementation in mdl.frobeniusdist.
647
+ Refer to how mdl.frobeniusdist handles the case when transform_mx_arg
648
+ is a tuple in order to understand how the leakage-aware metric is defined.
649
+
650
+ Idea: raise an error if mxBasis isn't leakage-friendly.
651
+ PROBLEM: if we're deep in the code then we end up needing to be
652
+ working in a basis that has the identity matrix as its
653
+ first element. The leakage-friendly basis doesn't even
654
+ have the identity matrix as an element, let alone the first element
655
+
656
+ TODO: dig into function calls below and see where we can
657
+ access the mxBasis object. (I'm sure we can.)
658
+ Looks like it isn't accessible within ExplicitOpModelCalc objects.
659
+ It's most definitely available in ExplicitOpModel.
660
+ """
623
661
if "frobenius" in metric :
624
- d = 0
625
- nSummands = 0.0
626
- for opLabel , gate in mdl .operations .items ():
627
- wt = item_weights .get (opLabel , opWeight )
628
- gate_mx = gate .to_dense ()
629
- other_mx = target_model .operations [opLabel ].to_dense ()
630
- delta = gate_mx - other_mx
631
- delta = delta @ P
632
- val = _np .linalg .norm (delta .flatten ())
633
- d += wt * val ** 2
634
- nSummands += wt * (gate .dim )** 2
635
-
636
- for lbl , rhoV in mdl .preps .items ():
637
- wt = item_weights .get (lbl , spamWeight )
638
- d += wt * rhoV .frobeniusdist_squared (target_model .preps [lbl ], None , None )
639
- nSummands += wt * rhoV .dim
640
-
641
- for lbl , Evec in mdl .effects .items ():
642
- wt = item_weights .get (lbl , spamWeight )
643
- evec = Evec .to_dense ()
644
- other = target_model .effects [lbl ].to_dense ()
645
- delta = evec - other
646
- delta = delta @ P
647
- val = _np .linalg .norm (delta .flatten ())
648
- d += wt * val ** 2
649
- nSummands += wt * Evec .dim
650
-
651
- val = _np .sqrt (d / nSummands )
662
+ val = mdl .frobeniusdist (target_model , transform_mx_arg , item_weights )
652
663
if "squared" in metric :
653
664
val = val ** 2
654
665
ret += val
@@ -696,7 +707,7 @@ def _objective_fn(gauge_group_el, oob_check):
696
707
else :
697
708
raise ValueError ("Invalid metric: %s" % metric )
698
709
# end _objective_fn
699
-
710
+
700
711
_jacobian_fn = None
701
712
702
713
return _objective_fn , _jacobian_fn
0 commit comments