Skip to content

Commit

Permalink
Merge pull request #283 from pyGSTio/feature-edesign-mapqubitlabels
Browse files Browse the repository at this point in the history
Feature edesign map-qubit-labels
  • Loading branch information
sserita committed Jan 5, 2023
2 parents 2d0053b + 25ccc65 commit 19bc512
Show file tree
Hide file tree
Showing 6 changed files with 381 additions and 5 deletions.
11 changes: 6 additions & 5 deletions pygsti/algorithms/germselection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3683,10 +3683,10 @@ def find_germs_breadthfirst_greedy(model_list, germs_list, randomize=True,
with _np.load(load_cevd_cache_filename) as cevd_cache:
twirledDerivDaggerDerivList=[list(cevd_cache.values())]

else:
else:
twirledDerivDaggerDerivList = \
[_compute_bulk_twirled_ddd_compact(model, germs_list, tol,
evd_tol=evd_tol, float_type=float_type, printer=printer)
evd_tol=evd_tol, float_type=float_type, printer=printer)
for model in model_list]

if save_cevd_cache_filename is not None:
Expand All @@ -3709,13 +3709,14 @@ def find_germs_breadthfirst_greedy(model_list, germs_list, randomize=True,
nonzero_weight_indices= nonzero_weight_indices[0]
for i, derivDaggerDeriv in enumerate(twirledDerivDaggerDerivList):
#reconstruct the needed J^T J matrices
temp_DDD = None
for j, idx in enumerate(nonzero_weight_indices):
if j==0:
temp_DDD = derivDaggerDeriv[idx] @ derivDaggerDeriv[idx].T
else:
temp_DDD += derivDaggerDeriv[idx] @ derivDaggerDeriv[idx].T

currentDDDList.append(temp_DDD)
if temp_DDD is not None:
currentDDDList.append(temp_DDD)

else: # should be unreachable since we set 'mode' internally above
raise ValueError("Invalid mode: %s" % mode) # pragma: no cover
Expand Down Expand Up @@ -4178,4 +4179,4 @@ def stable_pinv(mat):

#new form the psuedoinverse:
pinv= Vh.T@(pinv_s*U.T)
return pinv
return pinv
59 changes: 59 additions & 0 deletions pygsti/protocols/gst.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,29 @@ def __init__(self, processorspec_filename_or_obj, circuit_lists, all_circuits_ne
super().__init__(circuit_lists, all_circuits_needing_data, qubit_labels, nested, remove_duplicates)
HasProcessorSpec.__init__(self, processorspec_filename_or_obj)

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
GateSetTomographyDesign
"""
mapped_processorspec = self.processor_spec.map_qubit_labels(mapper)
mapped_circuits = [c.map_state_space_labels(mapper) for c in self.all_circuits_needing_data]
mapped_circuit_lists = [[c.map_state_space_labels(mapper) for c in circuit_list]
for circuit_list in self.circuit_lists]
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
return GateSetTomographyDesign(mapped_processorspec, mapped_circuit_lists, mapped_circuits,
mapped_qubit_labels, self.nested, remove_duplicates=False)


class StandardGSTDesign(GateSetTomographyDesign):
"""
Expand Down Expand Up @@ -322,6 +345,42 @@ def copy_with_maxlengths(self, max_lengths, germ_length_limits=None,
ret.nested = self.nested # must set nested flag again because truncate_to_design resets to False to be safe
return ret

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
StandardGSTDesign
"""
pspec = self.processor_spec.map_qudit_labels(mapper)
prep_fiducials = [c.map_state_space_labels(mapper) for c in self.prep_fiducials]
meas_fiducials = [c.map_state_space_labels(mapper) for c in self.meas_fiducials]
germs = [c.map_state_space_labels(mapper) for c in self.germs]
qubit_labels = self._mapped_qubit_labels(mapper)
if isinstance(self.fiducial_pairs, dict):
fiducial_pairs = {c.map_state_space_labels(mapper): v for c, v in self.fiducial_pairs.items()}
else:
fiducial_pairs = self.fiducial_pairs

if not (self.circuit_rules is None and self.aliases is None):
raise NotImplementedError(("Mapping qubit labels for a StandardGSTDesign with circuit rules"
" and/or aliases is not implemented yet."))

dscheck = None; action_if_missing = 'raise'; verbosity = 0 # values we could add as arguments later if desired.
return StandardGSTDesign(pspec, prep_fiducials, meas_fiducials,
germs, self.maxlengths, self.germ_length_limits, fiducial_pairs,
self.fpr_keep_fraction, self.fpr_keep_seed, self.include_lgst, self.nested,
self.circuit_rules, self.aliases, dscheck, action_if_missing, qubit_labels,
verbosity, add_default_protocol=False)


class GSTInitialModel(_NicelySerializable):
"""
Expand Down
109 changes: 109 additions & 0 deletions pygsti/protocols/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,34 @@ def promote_to_simultaneous(self):
"""
return SimultaneousExperimentDesign.from_edesign(self)

def _mapped_qubit_labels(self, mapper):
if self.qubit_labels in ("multiple", ('*',)):
mapped_qubit_labels = self.qubit_labels
else:
mapped_qubit_labels = tuple([mapper[ql] for ql in self.qubit_labels]) if isinstance(mapper, dict) \
else tuple(map(mapper, self.qubit_labels))
return mapped_qubit_labels

def map_qubit_labels(self, mapper):
"""
Creates a new ExperimentDesign whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
ExperimentDesign
"""
mapped_circuits = [c.map_state_space_labels(mapper) for c in self.all_circuits_needing_data]
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
mapped_children = {key: child.map_qubit_labels(mapper) for key, child in self._vals.items()}
return ExperimentDesign(mapped_circuits, mapped_qubit_labels, mapped_children, self._dirs)


class CircuitListsDesign(ExperimentDesign):
"""
Expand Down Expand Up @@ -1295,6 +1323,28 @@ def _truncate_to_available_data_inplace(self, dataset):
#self.nested = False
super()._truncate_to_available_data_inplace(dataset)

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
CircuitListsDesign
"""
mapped_circuits = [c.map_state_space_labels(mapper) for c in self.all_circuits_needing_data]
mapped_circuit_lists = [[c.map_state_space_labels(mapper) for c in circuit_list]
for circuit_list in self.circuit_lists]
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
return CircuitListsDesign(mapped_circuit_lists, mapped_circuits, mapped_qubit_labels,
self.nested, remove_duplicates=False) # no need to remove duplicates


class CombinedExperimentDesign(ExperimentDesign): # for multiple designs on the same dataset
"""
Expand Down Expand Up @@ -1479,6 +1529,26 @@ def __setitem__(self, key, val):
self._dirs[key] = self._auto_dirname(key)
self._vals[key] = val

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
CombinedExperimentDesign
"""
mapped_circuits = [c.map_state_space_labels(mapper) for c in self.all_circuits_needing_data]
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
mapped_sub_designs = {key: child.map_qubit_labels(mapper) for key, child in self._vals.items()}
return CombinedExperimentDesign(mapped_sub_designs, mapped_circuits, mapped_qubit_labels, self._dirs)


class SimultaneousExperimentDesign(ExperimentDesign):
"""
Expand Down Expand Up @@ -1666,6 +1736,26 @@ def _create_subdata(self, qubit_labels, dataset):
filtered_ds = filtered_ds.process_circuits(lambda c: actual_to_desired[c], aggregate=False)
return ProtocolData(sub_design, filtered_ds)

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
SimultaneousExperimentDesign
"""
mapped_circuits = [c.map_state_space_labels(mapper) for c in self.all_circuits_needing_data]
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
mapped_edesigns = [child.map_qubit_labels(mapper) for child in self._vals.values()]
return SimultaneousExperimentDesign(mapped_edesigns, mapped_circuits, mapped_qubit_labels)


class FreeformDesign(ExperimentDesign):
"""
Expand Down Expand Up @@ -1753,6 +1843,25 @@ def to_dataframe(self, pivot_valuename=None, pivot_value="Value", drop_columns=F
df = cdict.to_dataframe()
return _process_dataframe(df, pivot_valuename, pivot_value, drop_columns, preserve_order=True)

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
FreeformDesign
"""
mapped_circuits = [c.map_state_space_labels(mapper) for c in self.all_circuits_needing_data]
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
return FreeformDesign(mapped_circuits, mapped_qubit_labels)


class ProtocolData(_TreeNode):
"""
Expand Down
73 changes: 73 additions & 0 deletions pygsti/protocols/rb.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,31 @@ def _init_foundation(self, depths, circuit_lists, ideal_outs, circuits_per_depth
defaultfit = 'full'
self.add_default_protocol(RB(name='RB', defaultfit=defaultfit))

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
CliffordRBDesign
"""
mapped_circuits_and_idealouts_by_depth = self._mapped_circuits_and_idealouts_by_depth(mapper)
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
if self.interleaved_circuit is not None:
raise NotImplementedError("TODO: figure out whether `interleaved_circuit` needs to be mapped!")
return CliffordRBDesign.from_existing_circuits(mapped_circuits_and_idealouts_by_depth,
mapped_qubit_labels,
self.randomizeout, self.citerations, self.compilerargs,
self.interleaved_circuit, self.descriptor,
add_default_protocol=False)


class DirectRBDesign(_vb.BenchmarkingDesign):
"""
Expand Down Expand Up @@ -564,6 +589,30 @@ def _init_foundation(self, depths, circuit_lists, ideal_outs, circuits_per_depth
defaultfit = 'full'
self.add_default_protocol(RB(name='RB', defaultfit=defaultfit))

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
DirectRBDesign
"""
mapped_circuits_and_idealouts_by_depth = self._mapped_circuits_and_idealouts_by_depth(mapper)
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
return DirectRBDesign.from_existing_circuits(mapped_circuits_and_idealouts_by_depth,
mapped_qubit_labels,
self.sampler, self.samplerargs, self.addlocal,
self.lsargs, self.randomizeout, self.cliffordtwirl,
self.conditionaltwirl, self.citerations, self.compilerargs,
self.partitioned, self.descriptor, add_default_protocol=False)


class MirrorRBDesign(_vb.BenchmarkingDesign):
"""
Expand Down Expand Up @@ -779,6 +828,30 @@ def _init_foundation(self, depths, circuit_lists, ideal_outs, circuits_per_depth
if add_default_protocol:
self.add_default_protocol(RB(name='RB', datatype='adjusted_success_probabilities', defaultfit='A-fixed'))

def map_qubit_labels(self, mapper):
"""
Creates a new experiment design whose circuits' qubit labels are updated according to a given mapping.
Parameters
----------
mapper : dict or function
A dictionary whose keys are the existing self.qubit_labels values
and whose value are the new labels, or a function which takes a
single (existing qubit-label) argument and returns a new qubit-label.
Returns
-------
MirrorRBDesign
"""
mapped_circuits_and_idealouts_by_depth = self._mapped_circuits_and_idealouts_by_depth(mapper)
mapped_qubit_labels = self._mapped_qubit_labels(mapper)
return DirectRBDesign.from_existing_circuits(mapped_circuits_and_idealouts_by_depth,
mapped_qubit_labels,
self.circuit_type, self.sampler,
self.samplerargs, self.localclifford,
self.paulirandomize, self.descriptor,
add_default_protocol=False)


class RandomizedBenchmarking(_vb.SummaryStatistics):
"""
Expand Down

0 comments on commit 19bc512

Please sign in to comment.