2323
2424from typing import Union
2525
26- TransformMxPair = tuple [_np .ndarray , Union [_np .ndarray , _mt .IdentityOperator ]]
26+
27+ TransformMxPair = tuple [
28+ Union [_np .ndarray , _mt .IdentityOperator ],
29+ Union [_np .ndarray , _mt .IdentityOperator ]
30+ ]
31+
32+
33+ def to_transform_mx_pair (tmx_arg : Union [None , _np .ndarray , TransformMxPair ]) -> TransformMxPair :
34+ if tmx_arg is None :
35+ P = invP = _mt .IdentityOperator ()
36+ elif isinstance (tmx_arg , _np .ndarray ):
37+ P = tmx_arg
38+ invP = _np .linalg .pinv (P )
39+ else :
40+ P , invP = tmx_arg
41+ assert _mt .is_operatorlike (P )
42+ assert _mt .is_operatorlike (invP )
43+ return P , invP
2744
2845# Tolerace for matrix_rank when finding rank of a *normalized* projection
2946# matrix. This is a legitimate tolerace since a normalized projection matrix
@@ -156,16 +173,7 @@ def frobeniusdist(self, other_calc, transform_mx: Union[None, _np.ndarray, Trans
156173 -------
157174 float
158175 """
159- if transform_mx is None :
160- P , invP = None , None
161- # ^ It would be equivalent to use P = invP = _mt.IdentityOperator()
162- elif isinstance (transform_mx , _np .ndarray ):
163- P = transform_mx
164- invP = _np .linalg .pinv (P )
165- else :
166- P , invP = transform_mx
167- assert _mt .is_operatorlike (P )
168- assert _mt .is_operatorlike (invP )
176+ P , invP = to_transform_mx_pair (transform_mx )
169177
170178 d = 0.0
171179 nSummands = 0.0
@@ -231,14 +239,12 @@ def residuals(self, other_calc, transform_mx=None, item_weights=None):
231239 The (weighted) number of elements accounted for by the residuals.
232240 """
233241 resids = []
234- T = transform_mx
235242 nSummands = 0.0
236243 if item_weights is None : item_weights = {}
237244 sqrt_itemWeights = {k : _np .sqrt (v ) for k , v in item_weights .items ()}
238245 opWeight = sqrt_itemWeights .get ('gates' , 1.0 )
239246 spamWeight = sqrt_itemWeights .get ('spam' , 1.0 )
240- Ti = None if T is None else _np .linalg .pinv (T )
241- # ^ TODO: generalize inverse op (call T.inverse() if T were a "transform" object?)
247+ T , Ti = to_transform_mx_pair (transform_mx )
242248
243249 for opLabel , gate in self .operations .items ():
244250 wt = sqrt_itemWeights .get (opLabel , opWeight )
@@ -293,40 +299,23 @@ def jtracedist(self, other_calc, transform_mx=None, include_spam=True):
293299 -------
294300 float
295301 """
296- T = transform_mx
302+ T , Ti = to_transform_mx_pair ( transform_mx )
297303 d = 0 # spam difference
298304 nSummands = 0 # for spam terms
299305
300- if T is not None :
301- Ti = _np .linalg .inv (T )
302- dists = [gate .jtracedist (other_calc .operations [lbl ], T , Ti )
303- for lbl , gate in self .operations .items ()]
304-
305- #Just use frobenius distance between spam vecs, since jtracedist
306- # doesn't really make sense
307- if include_spam :
308- for lbl , rhoV in self .preps .items ():
309- d += rhoV .frobeniusdist_squared (other_calc .preps [lbl ], T , Ti )
310- nSummands += rhoV .dim
306+ dists = [gate .jtracedist (other_calc .operations [lbl ], T , Ti )
307+ for lbl , gate in self .operations .items ()]
311308
312- for lbl , Evec in self .effects .items ():
313- d += Evec .frobeniusdist_squared (other_calc .effects [lbl ], T , Ti )
314- nSummands += Evec .dim
309+ # Just use frobenius distance between spam vecs, since jtracedist
310+ # doesn't really make sense
311+ if include_spam :
312+ for lbl , rhoV in self .preps .items ():
313+ d += rhoV .frobeniusdist_squared (other_calc .preps [lbl ], T , Ti )
314+ nSummands += rhoV .dim
315315
316- else :
317- dists = [gate .jtracedist (other_calc .operations [lbl ])
318- for lbl , gate in self .operations .items ()]
319-
320- #Just use frobenius distance between spam vecs, since jtracedist
321- # doesn't really make sense
322- if include_spam :
323- for lbl , rhoV in self .preps .items ():
324- d += rhoV .frobeniusdist_squared (other_calc .preps [lbl ])
325- nSummands += rhoV .dim
326-
327- for lbl , Evec in self .effects .items ():
328- d += Evec .frobeniusdist_squared (other_calc .effects [lbl ])
329- nSummands += Evec .dim
316+ for lbl , Evec in self .effects .items ():
317+ d += Evec .frobeniusdist_squared (other_calc .effects [lbl ], T , Ti )
318+ nSummands += Evec .dim
330319
331320 spamVal = _np .sqrt (d / nSummands ) if (nSummands > 0 ) else 0
332321 return max (dists ) + spamVal
@@ -358,41 +347,24 @@ def diamonddist(self, other_calc, transform_mx=None, include_spam=True):
358347 -------
359348 float
360349 """
361- T = transform_mx
350+ T , Ti = to_transform_mx_pair ( transform_mx )
362351 d = 0 # spam difference
363352 nSummands = 0 # for spam terms
364353
365- if T is not None :
366- Ti = _np .linalg .inv (T )
367- dists = [gate .diamonddist (other_calc .operations [lbl ], T , Ti )
368- for lbl , gate in self .operations .items ()]
354+ dists = [gate .diamonddist (other_calc .operations [lbl ], T , Ti )
355+ for lbl , gate in self .operations .items ()]
369356
370- # Just use frobenius distance between spam vecs, since jtracedist
371- # doesn't really make sense
372- if include_spam :
373- for lbl , rhoV in self .preps .items ():
374- d += rhoV .frobeniusdist_squared (other_calc .preps [lbl ], T , Ti )
375- nSummands += rhoV .dim
357+ # Just use frobenius distance between spam vecs, since diamonddist
358+ # doesn't really make sense
359+ if include_spam :
360+ for lbl , rhoV in self .preps .items ():
361+ d += rhoV .frobeniusdist_squared (other_calc .preps [lbl ], T , Ti )
362+ nSummands += rhoV .dim
376363
377- for lbl , Evec in self .effects .items ():
364+ for lbl , Evec in self .effects .items ():
378365 d += Evec .frobeniusdist_squared (other_calc .effects [lbl ], T , Ti )
379366 nSummands += Evec .dim
380367
381- else :
382- dists = [gate .diamonddist (other_calc .operations [lbl ])
383- for lbl , gate in self .operations .items ()]
384-
385- #Just use frobenius distance between spam vecs, since jtracedist
386- # doesn't really make sense
387- if include_spam :
388- for lbl , rhoV in self .preps .items ():
389- d += rhoV .frobeniusdist_squared (other_calc .preps [lbl ])
390- nSummands += rhoV .dim
391-
392- for lbl , Evec in self .effects .items ():
393- d += Evec .frobeniusdist_squared (other_calc .effects [lbl ])
394- nSummands += Evec .dim
395-
396368 spamVal = _np .sqrt (d / nSummands ) if (nSummands > 0 ) else 0
397369 return max (dists ) + spamVal
398370
@@ -420,7 +392,7 @@ def deriv_wrt_params(self):
420392 eo += obj .hilbert_schmidt_size
421393
422394 if self .interposer is not None :
423- deriv = _np . dot ( deriv , self .interposer .deriv_op_params_wrt_model_params () )
395+ deriv = deriv @ self .interposer .deriv_op_params_wrt_model_params ()
424396
425397 return deriv
426398
@@ -499,14 +471,13 @@ def _buildup_dpg(self):
499471 for j in range (dim ): # *generator* mx, not gauge mx itself
500472 unitMx = _bc .mut (i , j , dim )
501473 for lbl , rhoVec in self_preps .items ():
502- mdlDeriv_preps [lbl ] = _np . dot ( unitMx , rhoVec )
474+ mdlDeriv_preps [lbl ] = unitMx @ rhoVec
503475 for lbl , EVec in self_effects .items ():
504- mdlDeriv_effects [lbl ] = - _np . dot (EVec .T , unitMx ).T
476+ mdlDeriv_effects [lbl ] = - (EVec .T @ unitMx ).T
505477
506478 for lbl , gate in self_operations .items ():
507479 #if isinstance(gate,_op.DenseOperator):
508- mdlDeriv_ops [lbl ] = _np .dot (unitMx , gate ) - \
509- _np .dot (gate , unitMx )
480+ mdlDeriv_ops [lbl ] = (unitMx @ gate ) - (gate @ unitMx )
510481 #else:
511482 # #use acton... maybe throw error if dim is too large (maybe above?)
512483 # deriv = _np.zeros((dim,dim),'d')
@@ -566,7 +537,7 @@ def nongauge_and_gauge_spaces(self, item_weights=None, non_gauge_mix_mx=None):
566537 #for each column of gen_dG, which is a gauge direction in model parameter space,
567538 # we add some amount of non-gauge direction, given by coefficients of the
568539 # numNonGaugeParams non-gauge directions.
569- orthog_to = gauge_space + _np . dot ( nonGaugeDirections , non_gauge_mix_mx ) # add non-gauge components in
540+ orthog_to = gauge_space + nonGaugeDirections @ non_gauge_mix_mx # add non-gauge components in
570541 # dims: (nParams,n_gauge_params) + (nParams,n_non_gauge_params) * (n_non_gauge_params,n_gauge_params)
571542 # non_gauge_mix_mx is a (n_non_gauge_params,n_gauge_params) matrix whose i-th column specifies the
572543 # coefficients to multiply each of the non-gauge directions by before adding them to the i-th
@@ -583,7 +554,7 @@ def nongauge_and_gauge_spaces(self, item_weights=None, non_gauge_mix_mx=None):
583554 metric_diag [vec .gpindices ] = item_weights .get (lbl , spamWeight )
584555 metric = _np .diag (metric_diag )
585556 #OLD: gen_ndG = _mt.nullspace(_np.dot(gen_dG.T,metric))
586- orthog_to = _np . dot ( metric .T , gauge_space )
557+ orthog_to = metric .T @ gauge_space
587558
588559 else :
589560 orthog_to = gauge_space
@@ -663,9 +634,9 @@ def _gauge_orbit_curvature(self, item_weights=None, non_gauge_mix_mx=None):
663634 unitMx_j = _bc .mut (j1 , j2 , dim )
664635 antiComm = (unitMx_i @ unitMx_j + unitMx_j @ unitMx_i )
665636 for lbl , rhoVec in self_preps .items ():
666- mdlHess_preps [lbl ] = 0.5 * _np . dot (antiComm , rhoVec )
637+ mdlHess_preps [lbl ] = 0.5 * (antiComm @ rhoVec )
667638 for lbl , EVec in self_effects .items ():
668- mdlHess_effects [lbl ] = 0.5 * _np . dot (EVec .T , antiComm ).T
639+ mdlHess_effects [lbl ] = 0.5 * (EVec .T @ antiComm ).T
669640 for lbl , gate in self_operations .items ():
670641 mdlHess_ops [lbl ] = 0.5 * (antiComm @ gate + gate @ antiComm ) \
671642 - unitMx_i @ gate @ unitMx_j - unitMx_j @ gate @ unitMx_i
@@ -785,8 +756,8 @@ def nongauge_projector(self, item_weights=None, non_gauge_mix_mx=None):
785756
786757 # ORIG WAY: use pseudo-inverse to normalize projector. Ran into problems where
787758 # default rcond == 1e-15 didn't work for 2-qubit case, but still more stable than inv method below
788- P = _np . dot ( gen_ndG , _np . transpose ( gen_ndG )) # almost a projector, but cols of dG are not orthonormal
789- Pp = _np .dot ( _np . linalg .pinv (P , rcond = 1e-7 ), P ) # make P into a true projector (onto gauge space)
759+ P = gen_ndG @ gen_ndG . T # almost a projector, but cols of dG are not orthonormal
760+ Pp = _np .linalg .pinv (P , rcond = 1e-7 ) @ P # make P into a true projector (onto gauge space)
790761
791762 # ALT WAY: use inverse of dG^T*dG to normalize projector (see wikipedia on projectors, dG => A)
792763 # This *should* give the same thing as above, but numerical differences indicate the pinv method
0 commit comments