Skip to content

Commit

Permalink
Updated fix for #408.
Browse files Browse the repository at this point in the history
Generalizes serialization/truncation for attributes
that are "paired" with the circuit lists in BenchmarkingDesigns.
This removes the code duplication in inherited classes
with more "paired" attributes,
such as CliffordRBDesign (with the new native gate info) and
BinaryRBDesign (with measurements/signs).
  • Loading branch information
sserita committed May 21, 2024
1 parent 7857663 commit 9d6339c
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 57 deletions.
51 changes: 9 additions & 42 deletions pygsti/protocols/rb.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def from_existing_circuits(cls, data_by_depth, qubit_labels=None,
ideal_outs = [[x[1] for x in data_by_depth[d]] for d in depths]
try:
num_native_gates = [[x[2] for x in data_by_depth[d]] for d in depths]
except KeyError:
except IndexError:
num_native_gates = None
circuits_per_depth = [len(data_by_depth[d]) for d in depths]
self = cls.__new__(cls)
Expand Down Expand Up @@ -244,8 +244,12 @@ def __init__(self, pspec, clifford_compilations, depths, circuits_per_depth, qub
def _init_foundation(self, depths, circuit_lists, ideal_outs, circuits_per_depth, qubit_labels,
randomizeout, citerations, compilerargs, descriptor, add_default_protocol,
interleaved_circuit, num_native_gates=None):
super().__init__(depths, circuit_lists, ideal_outs, qubit_labels, remove_duplicates=False)
self.num_native_gate_lists = num_native_gates
if self.num_native_gate_lists is not None:
# If we have native gate information, pair this with circuit data so that we serialize/truncate properly
self.paired_with_circuit_attrs = ["num_native_gate_lists"]

super().__init__(depths, circuit_lists, ideal_outs, qubit_labels, remove_duplicates=False)
self.circuits_per_depth = circuits_per_depth
self.randomizeout = randomizeout
self.citerations = citerations
Expand Down Expand Up @@ -348,42 +352,6 @@ def map_qubit_labels(self, mapper):
self.interleaved_circuit, self.descriptor,
add_default_protocol=False)

def _truncate_to_circuits_inplace(self, circuits_to_keep):
if self.num_native_gate_lists is not None:
truncated_circuit_lists = []
truncated_idealout_lists = []
truncated_num_native_gate_lists = []
for circuits, idealouts, nngs in zip(self.circuit_lists, self.idealout_lists, self.num_native_gate_lists):
new_circuits, new_idealouts, new_nngs = zip(*filter(lambda ci: ci[0] in set(circuits_to_keep), zip(circuits, idealouts, nngs)))
truncated_circuit_lists.append(new_circuits)
truncated_idealout_lists.append(new_idealouts)
truncated_num_native_gate_lists.append(new_nngs)

self.circuit_lists = truncated_circuit_lists
self.idealout_lists = truncated_idealout_lists
self.num_native_gate_lists = truncated_num_native_gate_lists
# TODO: What to do about circuit_per_depth, which may no longer be correct, or even uniform
# Should it be a list of ints instead of a single int?
super()._truncate_to_circuits_inplace(circuits_to_keep)

def _truncate_to_design_inplace(self, other_design):
if self.num_native_gate_lists is not None:
truncated_circuit_lists = []
truncated_idealout_lists = []
truncated_num_native_gate_lists = []
for circuits, idealouts, nngs, other_circuits in zip(self.circuit_lists, self.idealout_lists, self.num_native_gate_lists, other_design.circuit_lists):
new_circuits, new_idealouts, new_nngs = zip(*filter(lambda ci: ci[0] in set(other_circuits), zip(circuits, idealouts, nngs)))
truncated_circuit_lists.append(new_circuits)
truncated_idealout_lists.append(new_idealouts)
truncated_num_native_gate_lists.append(new_nngs)

self.circuit_lists = truncated_circuit_lists
self.idealout_lists = truncated_idealout_lists
self.num_native_gate_lists = truncated_num_native_gate_lists
# TODO: What to do about circuit_per_depth, which may no longer be correct, or even uniform
# Should it be a list of ints instead of a single int?
super()._truncate_to_design_inplace(other_design)


class DirectRBDesign(_vb.BenchmarkingDesign):
"""
Expand Down Expand Up @@ -1081,6 +1049,9 @@ def __init__(self, pspec, clifford_compilations, depths, circuits_per_depth, qub
def _init_foundation(self, depths, circuit_lists, measurements, signs, circuits_per_depth, qubit_labels, layer_sampling,
sampler, samplerargs, addlocal, lsargs, descriptor,
add_default_protocol):
# Pair these attributes with circuit data so that we serialize/truncate properly
self.paired_with_circuit_attrs = ["measurements", "signs"]

super().__init__(depths, circuit_lists, signs, qubit_labels, remove_duplicates=False)
self.measurements = measurements
self.signs = signs
Expand All @@ -1097,10 +1068,6 @@ def _init_foundation(self, depths, circuit_lists, measurements, signs, circuits_

defaultfit = 'A-fixed'
self.add_default_protocol(RB(name='RB', defaultfit=defaultfit))

self.auxfile_types['signs'] = 'json' # Makes sure that signs and measurements are saved seperately
self.auxfile_types['measurements'] = 'json'


class RandomizedBenchmarking(_vb.SummaryStatistics):
"""
Expand Down
57 changes: 42 additions & 15 deletions pygsti/protocols/vb.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,27 @@ class BenchmarkingDesign(ByDepthDesign):
Whether to remove duplicates when automatically creating
all the circuits that need data.
"""

paired_with_circuit_attrs = None
"""List of attributes which are paired up with circuit lists
These will be saved as external files during serialization,
and are truncated when circuit lists are truncated.
"""

def __init__(self, depths, circuit_lists, ideal_outs, qubit_labels=None, remove_duplicates=False):
assert(len(depths) == len(ideal_outs))
super().__init__(depths, circuit_lists, qubit_labels, remove_duplicates)

self.idealout_lists = ideal_outs
self.auxfile_types['idealout_lists'] = 'json'

if self.paired_with_circuit_attrs is None:
self.paired_with_circuit_attrs = ['idealout_lists']
else:
self.paired_with_circuit_attrs.insert(0, 'idealout_lists')

for paired_attr in self.paired_with_circuit_attrs:
self.auxfile_types[paired_attr] = 'json'

def _mapped_circuits_and_idealouts_by_depth(self, mapper):
""" Used in derived classes """
Expand Down Expand Up @@ -171,32 +186,44 @@ def truncate_to_lists(self, list_indices_to_keep):
ret = _copy.deepcopy(self) # Works for derived classes too
ret.depths = [self.depths[i] for i in list_indices_to_keep]
ret.circuit_lists = [self.circuit_lists[i] for i in list_indices_to_keep]
ret.idealout_lists = [self.idealout_lists[i] for i in list_indices_to_keep]
for paired_attr in self.paired_with_circuit_attrs:
val = getattr(self, paired_attr)
new_val = [val[i] for i in list_indices_to_keep]
setattr(ret, paired_attr, new_val)
return ret

def _truncate_to_circuits_inplace(self, circuits_to_keep):
truncated_circuit_lists = []
truncated_idealout_lists = []
for circuits, idealouts in zip(self.circuit_lists, self.idealout_lists):
new_circuits, new_idealouts = zip(*filter(lambda ci: ci[0] in set(circuits_to_keep), zip(circuits, idealouts)))
truncated_circuit_lists.append(new_circuits)
truncated_idealout_lists.append(new_idealouts)
paired_attr_lists_list = [getattr(self, paired_attr) for paired_attr in self.paired_with_circuit_attrs]
truncated_paired_attr_lists_list = [[] for _ in range(len(self.paired_with_circuit_attrs))]
for list_idx, circuits in enumerate(self.circuit_lists):
paired_attrs = [pal[list_idx] for pal in paired_attr_lists_list]
# Do the same filtering as CircuitList.truncate, but drag along any paired attributes
new_data = list(zip(*filter(lambda ci: ci[0] in set(circuits_to_keep), zip(circuits, *paired_attrs))))
truncated_circuit_lists.append(new_data[0])
for i, attr_data in enumerate(new_data[1:]):
truncated_paired_attr_lists_list[i].append(attr_data)

self.circuit_lists = truncated_circuit_lists
self.idealout_lists = truncated_idealout_lists
self.nested = False # we're not sure whether the truncated lists are nested
for paired_attr, paired_attr_lists in zip(self.paired_with_circuit_attrs, truncated_paired_attr_lists_list):
setattr(self, paired_attr, paired_attr_lists)
super()._truncate_to_circuits_inplace(circuits_to_keep)

def _truncate_to_design_inplace(self, other_design):
truncated_circuit_lists = []
truncated_idealout_lists = []
for circuits, idealouts, other_circuits in zip(self.circuit_lists, self.idealout_lists, other_design.circuit_lists):
new_circuits, new_idealouts = zip(*filter(lambda ci: ci[0] in set(other_circuits), zip(circuits, idealouts)))
truncated_circuit_lists.append(new_circuits)
truncated_idealout_lists.append(new_idealouts)
paired_attr_lists_list = [getattr(self, paired_attr) for paired_attr in self.paired_with_circuit_attrs]
truncated_paired_attr_lists_list = [[] for _ in range(len(self.paired_with_circuit_attrs))]
for list_idx, circuits in enumerate(self.circuit_lists):
paired_attrs = [pal[list_idx] for pal in paired_attr_lists_list]
# Do the same filtering as CircuitList.truncate, but drag along any paired attributes
new_data = list(zip(*filter(lambda ci: ci[0] in set(other_design.circuit_lists[list_idx]), zip(circuits, *paired_attrs))))
truncated_circuit_lists.append(new_data[0])
for i, attr_data in enumerate(new_data[1:]):
truncated_paired_attr_lists_list[i].append(attr_data)

self.circuit_lists = truncated_circuit_lists
self.idealout_lists = truncated_idealout_lists
for paired_attr, paired_attr_lists in zip(self.paired_with_circuit_attrs, truncated_paired_attr_lists_list):
setattr(self, paired_attr, paired_attr_lists)
super()._truncate_to_design_inplace(other_design)

def _truncate_to_available_data_inplace(self, dataset):
Expand Down

0 comments on commit 9d6339c

Please sign in to comment.