Skip to content

Commit 2cecfe8

Browse files
authored
Merge pull request #572 from sandialabs/feature-report-tweaks
HTML Report Updates
2 parents 262acb8 + e59aed4 commit 2cecfe8

32 files changed

+1907
-475
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,29 @@ Questions?
197197
----------
198198
For help and support with pyGSTi, please contact the authors at
199199
pygsti@sandia.gov.
200+
201+
How To Cite pyGSTi
202+
------------------
203+
204+
If you've used pyGSTi in the your research and are interested in citing
205+
us, please consider the following software design paper from some of the
206+
members of our development team (bibtex below):
207+
208+
```
209+
@ARTICLE{Nielsen2020-rd,
210+
title = "Probing quantum processor performance with {py{GST}i}",
211+
author = "Nielsen, Erik and Rudinger, Kenneth and Proctor, Timothy and
212+
Russo, Antonio and Young, Kevin and Blume-Kohout, Robin",
213+
journal = "Quantum Sci. Technol.",
214+
publisher = "IOP Publishing",
215+
volume = 5,
216+
number = 4,
217+
pages = "044002",
218+
month = jul,
219+
year = 2020,
220+
url = "https://iopscience.iop.org/article/10.1088/2058-9565/ab8aa4",
221+
copyright = "http://iopscience.iop.org/page/copyright",
222+
issn = "2058-9565",
223+
doi = "10.1088/2058-9565/ab8aa4"
224+
}
225+
```

pygsti/algorithms/core.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -765,19 +765,21 @@ def run_iterative_gst(dataset, start_model, circuit_lists,
765765

766766
models = []
767767
optimums = []
768+
mdc_store_list = []
768769

769770
for i in range(len(circuit_lists)):
770771
#then do the final iteration slightly differently since the generator should
771772
#give three return values.
772773
if i==len(circuit_lists)-1:
773-
mdl_iter, opt_iter, final_objfn = next(gst_iter_gen)
774+
mdl_iter, opt_iter, mdc_store_iter, final_objfn = next(gst_iter_gen)
774775
else:
775-
mdl_iter, opt_iter = next(gst_iter_gen)
776+
mdl_iter, opt_iter, mdc_store_iter = next(gst_iter_gen)
776777

777778
models.append(mdl_iter)
778779
optimums.append(opt_iter)
780+
mdc_store_list.append(mdc_store_iter)
779781

780-
return models, optimums, final_objfn
782+
return models, optimums, final_objfn, mdc_store_list
781783

782784
def iterative_gst_generator(dataset, start_model, circuit_lists,
783785
optimizer, iteration_objfn_builders, final_objfn_builders,
@@ -927,7 +929,6 @@ def _max_array_types(artypes_list): # get the maximum number of each array type
927929
first_iter_optimizer = _copy.deepcopy(optimizer) # use a separate copy of optimizer, as it
928930
first_iter_optimizer.fditer = optimizer.first_fditer # is a persistent object (so don't modify!)
929931
opt_result, mdc_store = run_gst_fit(mdc_store, first_iter_optimizer, obj_fn_builder, printer - 1)
930-
931932
else:
932933
opt_result, mdc_store = run_gst_fit(mdc_store, optimizer, obj_fn_builder, printer - 1)
933934
profiler.add_time('run_iterative_gst: iter %d %s-opt' % (i + 1, obj_fn_builder.name), tNxt)
@@ -944,7 +945,6 @@ def _max_array_types(artypes_list): # get the maximum number of each array type
944945
mdl.basis = start_model.basis
945946
opt_result, mdc_store = run_gst_fit(mdc_store, optimizer, obj_fn_builder, printer - 1)
946947
profiler.add_time('run_iterative_gst: final %s opt' % obj_fn_builder.name, tNxt)
947-
948948
tNxt = _time.time()
949949
printer.log("Final optimization took %.1fs\n" % (tNxt - tRef), 2)
950950
tRef = tNxt
@@ -954,11 +954,13 @@ def _max_array_types(artypes_list): # get the maximum number of each array type
954954
# Note: initial_mdc_store is *not* an objective fn (it's just a store) so don't send it back.
955955
if mdc_store is not initial_mdc_store:
956956
final_objfn = mdc_store
957-
958-
yield (mdc_store.model, opt_result, final_objfn)
957+
yield (mdc_store.model, opt_result, mdc_store, final_objfn)
959958
else:
960-
#If not the final iteration then only send back a copy of the model and the optimizer results
961-
yield (mdc_store.model.copy(), opt_result)
959+
#If not the final iteration then send back a copy of the model and the optimizer results
960+
#mdc_store gets re-initialized at the start of each circuit list iteration, and doesn't appear
961+
#to get propagated beyond that point so sending it back directly without copying (which would
962+
#probably require implementing a custom method for MDC store objects) should be fairly safe.
963+
yield (mdc_store.model.copy(), opt_result, mdc_store)
962964

963965
printer.log('Iterative GST Total Time: %.1fs' % (_time.time() - tStart))
964966
profiler.add_time('run_iterative_gst: total time', tStart)

pygsti/algorithms/gaugeopt.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ def gaugeopt_custom(model, objective_fn, gauge_group=None,
265265
printer = _baseobjs.VerbosityPrinter.create_printer(verbosity, comm)
266266
tStart = _time.time()
267267

268+
#replace model with a new copy of itself so as to not propagate the conversion back to the
269+
#instance of the model object we are gauge optimizing.
270+
model = model.copy()
271+
268272
if comm is not None:
269273
mdl_cmp = comm.bcast(model if (comm.Get_rank() == 0) else None, root=0)
270274
try:

pygsti/circuits/circuitlist.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,7 @@ def elementvec_to_array(self, elementvec, layout, mergeop="sum"):
225225
Dictates how to combine the `elementvec` components corresponding to a single
226226
plaquette entry (circuit). If "sum", the returned array contains summed
227227
values. If a format string, e.g. `"%.2f"`, then the so-formatted components
228-
are joined together with separating commas, and the resulting array contains
229-
string (object-type) entries.
228+
are joined together, and the resulting array contains string (object-type) entries.
230229
231230
Returns
232231
-------
@@ -241,7 +240,7 @@ def elementvec_to_array(self, elementvec, layout, mergeop="sum"):
241240
fmt = mergeop
242241
ret = _np.nan * _np.ones(len(self), dtype=_np.object_)
243242
for i,ckt in enumerate(self._circuits):
244-
ret[i] = ", ".join(["NaN" if _np.isnan(x) else
243+
ret[i] = "".join(["NaN" if _np.isnan(x) else
245244
(fmt % x) for x in elementvec[layout.indices(ckt)]])
246245
else:
247246
raise ValueError("Invalid `mergeop` arg: %s" % str(mergeop))

pygsti/circuits/circuitstructure.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ def elementvec_to_array(self, elementvec, layout, mergeop="sum"):
140140
Dictates how to combine the `elementvec` components corresponding to a single
141141
plaquette entry (circuit). If "sum", the returned array contains summed
142142
values. If a format string, e.g. `"%.2f"`, then the so-formatted components
143-
are joined together with separating commas, and the resulting array contains
144-
string (object-type) entries.
143+
are joined, and the resulting array contains string (object-type) entries.
145144
146145
Returns
147146
-------
@@ -155,7 +154,7 @@ def elementvec_to_array(self, elementvec, layout, mergeop="sum"):
155154
fmt = mergeop
156155
ret = _np.nan * _np.ones((self.num_rows, self.num_cols), dtype=_np.object_)
157156
for (i, j), opstr in self.elements.items():
158-
ret[i, j] = ", ".join(["NaN" if _np.isnan(x) else
157+
ret[i, j] = "".join(["NaN" if _np.isnan(x) else
159158
(fmt % x) for x in elementvec[layout.indices(opstr)]])
160159
else:
161160
raise ValueError("Invalid `mergeop` arg: %s" % str(mergeop))

pygsti/modelmembers/operations/lindbladerrorgen.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,20 @@ def from_error_generator(cls, errgen_or_dim, parameterization="CPTPLND", element
259259
terms up to some order). `"cterm"` is similar but uses Clifford operation
260260
action on stabilizer states.
261261
262-
state_space : `StateSpace` or castable to `StateSpace`
263-
The state space upon which this error generator acts.
262+
state_space : StateSpace, optional (default None)
263+
StateSpace object to use in construction of this LindbladErrorgen.
264+
If None we use the function `pygsti.baseobjs.statespace.default_space_for_dim`
265+
to infer the correct state space from the dimensions of the passed in
266+
error generator.
264267
265268
Returns
266269
-------
267270
`LindbladErrorgen`
268271
"""
269-
errgen = _np.zeros((errgen_or_dim, errgen_or_dim), 'd') \
270-
if isinstance(errgen_or_dim, (int, _np.int64)) else errgen_or_dim
272+
if isinstance(errgen_or_dim, (int, _np.int64)):
273+
errgen = _np.zeros((errgen_or_dim, errgen_or_dim), 'd')
274+
else:
275+
errgen = errgen_or_dim
271276
return cls._from_error_generator(errgen, parameterization, elementary_errorgen_basis,
272277
mx_basis, truncate, evotype, state_space)
273278

@@ -321,8 +326,11 @@ def from_error_generator_and_blocks(cls, errgen_or_dim, lindblad_coefficient_blo
321326
terms up to some order). `"cterm"` is similar but uses Clifford operation
322327
action on stabilizer states.
323328
324-
state_space : `StateSpace` or castable to `StateSpace`
325-
The state space upon which this error generator acts.
329+
state_space : StateSpace, optional (default None)
330+
StateSpace object to use in construction of this LindbladErrorgen.
331+
If None we use the function `pygsti.baseobjs.statespace.default_space_for_dim`
332+
to infer the correct state space from the dimensions of the passed in
333+
error generator.
326334
327335
Returns
328336
-------

pygsti/protocols/estimate.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ def __init__(self, parent, models=None, parameters=None, extra_parameters=None):
165165
self._final_objfn_cache = parameters.get('final_objfn_cache', None)
166166
self.final_objfn_builder = parameters.get('final_objfn_builder', _objfns.PoissonPicDeltaLogLFunction.builder())
167167
self._final_objfn = parameters.get('final_objfn', None)
168+
self._per_iter_mdc_store = parameters.get('per_iter_mdc_store', None)
169+
168170

169171
self.extra_parameters = extra_parameters if (extra_parameters is not None) else {}
170172

@@ -193,20 +195,22 @@ def __init__(self, parent, models=None, parameters=None, extra_parameters=None):
193195
'_final_objfn_cache': 'dir-serialized-object',
194196
'final_objfn_builder': 'serialized-object',
195197
'_final_objfn': 'reset',
196-
'_gaugeopt_suite': 'serialized-object'
198+
'_gaugeopt_suite': 'serialized-object',
199+
'_per_iter_mdc_store': 'reset'
197200
}
198201

199202
@property
200203
def parameters(self):
201204
#HACK for now, until we can remove references that access these parameters
202-
parameters = _collections.OrderedDict()
205+
parameters = dict()
203206
parameters['protocol'] = self.protocol # Estimates can hold sub-Protocols <=> sub-results
204207
parameters['profiler'] = self.profiler
205208
parameters['final_mdc_store'] = self._final_mdc_store
206209
parameters['final_objfn'] = self._final_objfn
207210
parameters['final_objfn_cache'] = self._final_objfn_cache
208211
parameters['final_objfn_builder'] = self.final_objfn_builder
209212
parameters['weights'] = self.circuit_weights
213+
parameters['per_iter_mdc_store'] = self._per_iter_mdc_store
210214
parameters.update(self.extra_parameters)
211215
#parameters['raw_objective_values']
212216
#parameters['model_test_values']

pygsti/protocols/gst.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ def run(self, data, memlimit=None, comm=None, checkpoint=None, checkpoint_path=N
13571357
if disable_checkpointing:
13581358
seed_model = mdl_start.copy()
13591359
mdl_lsgst_list = []
1360+
mdc_store_list = []
13601361
starting_idx = 0
13611362
else:
13621363
# Set the checkpoint_path variable if None
@@ -1376,6 +1377,7 @@ def run(self, data, memlimit=None, comm=None, checkpoint=None, checkpoint_path=N
13761377
if checkpoint is None:
13771378
seed_model = mdl_start.copy()
13781379
mdl_lsgst_list = []
1380+
mdc_store_list = []
13791381
checkpoint = GateSetTomographyCheckpoint()
13801382
elif isinstance(checkpoint, GateSetTomographyCheckpoint):
13811383
# if the checkpoint's last completed iteration is non-negative
@@ -1393,8 +1395,12 @@ def run(self, data, memlimit=None, comm=None, checkpoint=None, checkpoint_path=N
13931395
# left set to None. There looks to be some logic for handling this and it looks
13941396
# like the serialization routines effectively do this already, as the value
13951397
# of this is lost between writing and reading.
1398+
mdc_store_list = [None]*len(mdl_lsgst_list) #We don't presently have serialization support for
1399+
#MDC store objects, so for now we'll skip serializing this and re-initialize previous iterations
1400+
#to None. Given how this is currently used the only downside to this should be inefficiency
1401+
#rebuilding the needed MDC stores in the report generation.
13961402
else:
1397-
NotImplementedError(
1403+
raise NotImplementedError(
13981404
'The only currently valid checkpoint inputs are None and GateSetTomographyCheckpoint.')
13991405

14001406
# note the last_completed_iter value is initialized to -1 so the below line
@@ -1420,10 +1426,11 @@ def run(self, data, memlimit=None, comm=None, checkpoint=None, checkpoint_path=N
14201426
#then do the final iteration slightly differently since the generator should
14211427
#give three return values.
14221428
if i==len(bulk_circuit_lists)-1:
1423-
mdl_iter, opt_iter, final_objfn = next(gst_iter_generator)
1429+
mdl_iter, opt_iter, mdc_store_iter, final_objfn = next(gst_iter_generator)
14241430
else:
1425-
mdl_iter, opt_iter = next(gst_iter_generator)
1431+
mdl_iter, opt_iter, mdc_store_iter = next(gst_iter_generator)
14261432
mdl_lsgst_list.append(mdl_iter)
1433+
mdc_store_list.append(mdc_store_iter)
14271434
optima_list.append(opt_iter)
14281435

14291436
if not disable_checkpointing:
@@ -1436,15 +1443,15 @@ def run(self, data, memlimit=None, comm=None, checkpoint=None, checkpoint_path=N
14361443
checkpoint.write(f'{checkpoint_path}_iteration_{i}.json')
14371444

14381445
tnxt = _time.time(); profiler.add_time('GST: total iterative optimization', tref); tref = tnxt
1439-
14401446
#set parameters
1441-
parameters = _collections.OrderedDict()
1447+
parameters = dict()
14421448
parameters['protocol'] = self # Estimates can hold sub-Protocols <=> sub-results
14431449
parameters['final_objfn_builder'] = self.objfn_builders.final_builders[-1] \
14441450
if len(self.objfn_builders.final_builders) > 0 else self.objfn_builders.iteration_builders[-1]
14451451
parameters['final_objfn'] = final_objfn # Final obj. function evaluated at best-fit point (cache too)
14461452
parameters['final_mdc_store'] = final_objfn # Final obj. function is also a "MDC store"
14471453
parameters['profiler'] = profiler
1454+
parameters['per_iter_mdc_store'] = mdc_store_list #list of the MDC stores for each iteration.
14481455
# Note: we associate 'final_cache' with the Estimate, which means we assume that *all*
14491456
# of the models in the estimate can use same evaltree, have the same default prep/POVMs, etc.
14501457

0 commit comments

Comments
 (0)