Skip to content

Commit 3c8de56

Browse files
merge develop
2 parents c6a8787 + a1359dd commit 3c8de56

File tree

9 files changed

+119
-123
lines changed

9 files changed

+119
-123
lines changed

pygsti/circuits/circuitstructure.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -719,16 +719,6 @@ def _encodeval(typ, val):
719719
if (self.circuit_weights is not None) else None)
720720
})
721721

722-
#state['plaquette_types'] = XXX
723-
#state['datacols'] = {
724-
# 'Circuit': [circuits]
725-
# 'PlaquetteX':
726-
# 'PlaquetteY':
727-
# 'X':
728-
# 'Y':
729-
# 'weight':
730-
# # additional circuits have sentinels for Plaquette coords?
731-
732722
return state
733723

734724
@classmethod

pygsti/circuits/gstcircuits.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,11 @@ def add_to_plaquettes(pkey_dict, plaquette_dict, base_circuit, maxlen, germ, pow
544544
unindexed = filter_ds(lgst_list, dscheck, missing_lgst)
545545
lsgst_structs.append(
546546
_PlaquetteGridCircuitStructure({}, [], germs, "L", "germ", unindexed, op_label_aliases,
547-
circuit_weights_dict=None, additional_circuits_location='start', name=None))
547+
circuit_weights_dict=None, additional_circuits_location='start', name=None)).copy() #HACK
548+
#TODO: Track bug where the ordering of the _circuit attribute of the
549+
#PlaquetteGridCircuitStructure is somehow changing after copying compared
550+
#to how it is constructed here. The hack of bug is meant to mitigate this behavior
551+
#by copying the structure ahead of time (the ordering appears to be stable with multiple copies).
548552

549553
for i, maxLen in enumerate(max_lengths):
550554

@@ -634,7 +638,12 @@ def add_to_plaquettes(pkey_dict, plaquette_dict, base_circuit, maxlen, germ, pow
634638
lsgst_structs.append(
635639
_PlaquetteGridCircuitStructure({pkey[base]:plaq for base, plaq in plaquettes.items()},
636640
maxLens, germs, "L", "germ", unindexed, op_label_aliases,
637-
circuit_weights_dict=None, additional_circuits_location='start', name=None))
641+
circuit_weights_dict=None, additional_circuits_location='start', name=None).copy()) #HACK
642+
#TODO: Track bug where the ordering of the _circuit attribute of the
643+
#PlaquetteGridCircuitStructure is somehow changing after copying compared
644+
#to how it is constructed here. The hack of bug is meant to mitigate this behavior
645+
#by copying the structure ahead of time (the ordering appears to be stable with multiple copies).
646+
638647
tot_circuits += len(lsgst_structs[-1]) # only relevant for non-nested case
639648

640649
if nest: # then totStrs computation about overcounts -- just take string count of final stage

pygsti/modelmembers/povms/__init__.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ def povm_type_from_op_type(op_type):
299299
return povm_type_preferences
300300

301301

302-
def convert(povm, to_type, basis, cp_penalty=1e-7, ideal_povm=None, flatten_structure=False):
302+
def convert(povm, to_type, basis, ideal_povm=None, flatten_structure=False, cp_penalty=1e-7):
303303
"""
304304
TODO: update docstring
305305
Convert a POVM to a new type of parameterization.
@@ -347,7 +347,6 @@ def convert(povm, to_type, basis, cp_penalty=1e-7, ideal_povm=None, flatten_stru
347347
object from the object passed as input.
348348
"""
349349

350-
##TEST CONVERSION BETWEEN LINBLAD TYPES
351350
to_types = to_type if isinstance(to_type, (tuple, list)) else (to_type,) # HACK to support multiple to_type values
352351
error_msgs = {}
353352

@@ -421,45 +420,46 @@ def convert(povm, to_type, basis, cp_penalty=1e-7, ideal_povm=None, flatten_stru
421420
dense_effects.append(dense_effect.reshape((1,len(dense_effect))))
422421

423422
dense_povm = _np.concatenate(dense_effects, axis=0)
424-
degrees_of_freedom = (dense_ideal_povm.shape[0] - 1) * dense_ideal_povm.shape[1]
425-
423+
426424
#It is often the case that there are more error generators than physical degrees of freedom in the POVM
427425
#We define a function which finds linear comb. of errgens that span these degrees of freedom.
428426
#This has been called "the trivial gauge", and this function is meant to avoid it
429-
def calc_physical_subspace(dense_ideal_povm, epsilon = 1e-9):
430-
427+
def calc_physical_subspace(dense_ideal_povm, epsilon = 1e-4):
428+
429+
degrees_of_freedom = (dense_ideal_povm.shape[0] - 1) * dense_ideal_povm.shape[1]
431430
errgen = _LindbladErrorgen.from_error_generator(povm.state_space.dim, parameterization=to_type)
431+
432432
if degrees_of_freedom > errgen.num_params:
433433
warnings.warn("POVM has more degrees of freedom than the available number of parameters, representation in this parameterization is not guaranteed")
434434
exp_errgen = _ExpErrorgenOp(errgen)
435435

436436
num_errgens = errgen.num_params
437437
#TODO: Maybe we can use the num of params instead of number of matrix entries, as some of them are linearly dependent.
438438
#i.e E0 completely determines E1 if those are the only two povm elements (E0 + E1 = Identity)
439-
num_entries = dense_ideal_povm.shape[0]*dense_ideal_povm.shape[1]
440-
#assert num_errgens >= povm.num_params, "POVM has too many elements, error generator parameterization is not possible"
441-
442-
ideal_vec = _np.zeros(num_errgens)
439+
num_entries = dense_ideal_povm.size
443440

444441
#Compute the jacobian with respect to the error generators. This will allow us to see which
445442
#error generators change the POVM entries
446443
J = _np.zeros((num_entries,num_errgens))
447-
448-
for i in range(len(ideal_vec)):
449-
new_vec = ideal_vec.copy()
444+
new_vec = _np.zeros(num_errgens)
445+
for i in range(num_errgens):
446+
450447
new_vec[i] = epsilon
451448
exp_errgen.from_vector(new_vec)
449+
new_vec[i] = 0
452450
vectorized_povm = _np.zeros(num_entries)
453451
perturbed_povm = (dense_ideal_povm @ exp_errgen.to_dense() - dense_ideal_povm)/epsilon
454452

455-
perturbed_povm_t = perturbed_povm.transpose()
456-
for j, column in enumerate(perturbed_povm_t):
457-
vectorized_povm[j*len(perturbed_povm_t[0]):(j+1)*len(perturbed_povm_t[0])] = column
453+
vectorized_povm = perturbed_povm.flatten(order='F')
458454

459-
J[:,i] = vectorized_povm.transpose()
455+
J[:,i] = vectorized_povm
460456

461-
_,S,V = _np.linalg.svd(J)
462-
return V[:len(S),]
457+
_,S,Vt = _np.linalg.svd(J, full_matrices=False)
458+
459+
#Only return nontrivial singular vectors
460+
Vt = Vt[S > 1e-13, :].reshape((-1, Vt.shape[1]))
461+
return Vt
462+
463463

464464
phys_directions = calc_physical_subspace(dense_ideal_povm)
465465

@@ -482,10 +482,10 @@ def _objfn(v):
482482
return _np.linalg.norm(dense_povm - dense_ideal_povm @ proc_matrix) + cp_penalty * sum_of_negative_choi_eigenvalues_gate(proc_matrix, basis)
483483

484484
soln = _spo.minimize(_objfn, _np.zeros(len(phys_directions), 'd'), method="Nelder-Mead", options={},
485-
tol=1e-13) # , callback=callback)
485+
tol=1e-13)
486486
if not soln.success and soln.fun > 1e-6: # not "or" because success is often not set correctly
487487
raise ValueError("Failed to find an errorgen such that <ideal|exp(errorgen) = <effect|")
488-
errgen_vec = _np.linalg.pinv(phys_directions) @ soln.x
488+
errgen_vec = _np.linalg.lstsq(phys_directions, soln.x)[0]
489489
errorgen.from_vector(errgen_vec)
490490

491491
EffectiveExpErrorgen = _IdentityPlusErrorgenOp if lndtype.meta == '1+' else _ExpErrorgenOp

pygsti/modelmembers/states/__init__.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def state_type_from_op_type(op_type):
187187
return state_type_preferences
188188

189189

190-
def convert(state, to_type, basis, cp_penalty=1e-7, ideal_state=None, flatten_structure=False):
190+
def convert(state, to_type, basis, ideal_state=None, flatten_structure=False, cp_penalty=1e-7):
191191
"""
192192
TODO: update docstring
193193
Convert SPAM vector to a new type of parameterization.
@@ -266,28 +266,31 @@ def convert(state, to_type, basis, cp_penalty=1e-7, ideal_state=None, flatten_st
266266
dense_state = state.to_dense()
267267
num_qubits = st.state_space.num_qubits
268268

269-
errgen = _LindbladErrorgen.from_error_generator(2**(2*num_qubits), parameterization=to_type)
270-
num_errgens = errgen.num_params
269+
271270

272271
#GLND for states suffers from "trivial gauge" freedom. This function identifies
273272
#the physical directions to avoid this gauge.
274-
def calc_physical_subspace(ideal_prep, epsilon = 1e-9):
273+
def calc_physical_subspace(ideal_prep, epsilon = 1e-4):
274+
errgen = _LindbladErrorgen.from_error_generator(2**(2*num_qubits), parameterization=to_type)
275+
num_errgens = errgen.num_params
275276

276277
exp_errgen = _ExpErrorgenOp(errgen)
277-
ideal_vec = _np.zeros(num_errgens)
278278

279279
#Compute the jacobian with respect to the error generators. This will allow us to see which
280280
#error generators change the POVM entries
281281
J = _np.zeros((state.num_params, num_errgens))
282282

283-
for i in range(len(ideal_vec)):
284-
new_vec = ideal_vec.copy()
283+
for i in range(num_errgens):
284+
new_vec = _np.zeros(num_errgens)
285285
new_vec[i] = epsilon
286286
exp_errgen.from_vector(new_vec)
287287
J[:,i] = (exp_errgen.to_dense() @ ideal_prep - ideal_prep)[1:]/epsilon
288288

289-
_,S,V = _np.linalg.svd(J)
290-
return V[:len(S),]
289+
_,S,Vt = _np.linalg.svd(J, full_matrices=False)
290+
291+
#Only return nontrivial singular vectors
292+
Vt = Vt[S > 1e-13, :].reshape((-1, Vt.shape[1]))
293+
return Vt
291294

292295
phys_directions = calc_physical_subspace(dense_state)
293296

@@ -302,12 +305,12 @@ def _objfn(v):
302305
return _np.linalg.norm(proc_matrix @ dense_st - dense_state) + cp_penalty * sum_of_negative_choi_eigenvalues_gate(proc_matrix, basis)
303306

304307
soln = _spo.minimize(_objfn, _np.zeros(len(phys_directions), 'd'), method="Nelder-Mead", options={},
305-
tol=1e-13) # , callback=callback)
306-
#print("DEBUG: opt done: ",soln.success, soln.fun, soln.x) # REMOVE
308+
tol=1e-13)
309+
307310
if not soln.success and soln.fun > 1e-6: # not "or" because success is often not set correctly
308311
raise ValueError("Failed to find an errorgen such that exp(errorgen)|ideal> = |state>")
309312

310-
errgen_vec = _np.linalg.pinv(phys_directions) @ soln.x
313+
errgen_vec = _np.linalg.lstsq(phys_directions, soln.x)[0]
311314
errorgen.from_vector(errgen_vec)
312315

313316
EffectiveExpErrorgen = _IdentityPlusErrorgenOp if lndtype.meta == '1+' else _ExpErrorgenOp
@@ -322,7 +325,6 @@ def _objfn(v):
322325
return create_from_dmvec(vec, to_type, basis, state.evotype, state.state_space)
323326

324327
except ValueError as e:
325-
#_warnings.warn('Failed to convert state to type %s with error: %s' % (to_type, e))
326328
error_msgs[to_type] = str(e) # try next to_type
327329

328330
raise ValueError("Could not convert state to to type(s): %s\n%s" % (str(to_types), str(error_msgs)))

pygsti/models/explicitmodel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,12 @@ def convert_members_inplace(self, to_type, categories_to_convert='all', labels_t
360360
for lbl, prep in self.preps.items():
361361
if labels_to_convert == 'all' or lbl in labels_to_convert:
362362
ideal = ideal_model.preps.get(lbl, None) if (ideal_model is not None) else None
363-
self.preps[lbl] = _state.convert(prep, to_type, self.basis, spam_cp_penalty, ideal, flatten_structure)
363+
self.preps[lbl] = _state.convert(prep, to_type, self.basis, ideal, flatten_structure, cp_penalty=spam_cp_penalty)
364364
if any([c in categories_to_convert for c in ('all', 'povms')]):
365365
for lbl, povm in self.povms.items():
366366
if labels_to_convert == 'all' or lbl in labels_to_convert:
367367
ideal = ideal_model.povms.get(lbl, None) if (ideal_model is not None) else None
368-
self.povms[lbl] = _povm.convert(povm, to_type, self.basis, spam_cp_penalty, ideal, flatten_structure)
368+
self.povms[lbl] = _povm.convert (povm, to_type, self.basis, ideal, flatten_structure, cp_penalty=spam_cp_penalty)
369369

370370
self._clean_paramvec() # param indices were probabaly updated
371371
if set_default_gauge_group:

pygsti/models/modelparaminterposer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ def __init__(self, transform_matrix):
7070
self.full_span_transform_matrix = self.transform_matrix.copy()
7171
super().__init__(transform_matrix.shape[1], transform_matrix.shape[0])
7272
def model_paramvec_to_ops_paramvec(self, v):
73-
return _np.dot(self.transform_matrix, v)
73+
return self.transform_matrix @ v
7474

7575
def ops_paramvec_to_model_paramvec(self, w):
76-
return _np.dot(self.inv_transform_matrix, w)
76+
return self.inv_transform_matrix @ w
7777

7878
def ops_paramlbls_to_model_paramlbls(self, wl):
7979
# This can and should be improved later - particularly this will be awful when labels (els of wl) are tuples.

0 commit comments

Comments
 (0)