def make_rpe_alpha_str_lists_gx_gz(k_list): """ Make alpha cosine and sine circuit lists for (approx) X pi/4 and Z pi/2 gates. These circuits are used to estimate alpha (Z rotation angle). Parameters ---------- k_list : list of ints The list of "germ powers" to be used. Typically successive powers of two; e.g. [1,2,4,8,16]. Returns ------- cosStrList : list of Circuits The list of "cosine strings" to be used for alpha estimation. sinStrList : list of Circuits The list of "sine strings" to be used for alpha estimation. """ cosStrList = [] sinStrList = [] for k in k_list: cosStrList += [ _Circuit(('Gi', 'Gx', 'Gx', 'Gz') + ('Gz', ) * k + ('Gz', 'Gz', 'Gz', 'Gx', 'Gx'), 'GiGxGxGzGz^' + str(k) + 'GzGzGzGxGx') ] sinStrList += [ _Circuit(('Gx', 'Gx', 'Gz', 'Gz') + ('Gz', ) * k + ('Gz', 'Gz', 'Gz', 'Gx', 'Gx'), 'GxGxGzGzGz^' + str(k) + 'GzGzGzGxGx') ] #From RPEToolsNewNew.py ##cosStrList += [_Circuit(('Gi','Gx','Gx')+ ## ('Gz',)*k + ## ('Gx','Gx'), ## 'GiGxGxGz^'+str(k)+'GxGx')] # # #cosStrList += [_Circuit(('Gx','Gx')+ # ('Gz',)*k + # ('Gx','Gx'), # 'GxGxGz^'+str(k)+'GxGx')] # # #sinStrList += [_Circuit(('Gx','Gx')+ # ('Gz',)*k + # ('Gz','Gx','Gx'), # 'GxGxGz^'+str(k)+'GzGxGx')] return cosStrList, sinStrList
def make_rpe_theta_str_lists_gx_gz(k_list): """ Make theta cosine and sine circuit lists for (approx) X pi/4 and Z pi/2 gates. These circuits are used to estimate theta (X-Z axes angle). Parameters ---------- k_list : list of ints The list of "germ powers" to be used. Typically successive powers of two; e.g. [1,2,4,8,16]. Returns ------- thetaCosStrList : list of Circuits The list of "cosine strings" to be used for theta estimation. thetaSinStrList : list of Circuits The list of "sine strings" to be used for theta estimation. """ thetaCosStrList = [] thetaSinStrList = [] for k in k_list: thetaCosStrList += [ _Circuit(('Gz', 'Gx', 'Gx', 'Gx', 'Gx', 'Gz', 'Gz', 'Gx', 'Gx', 'Gx', 'Gx', 'Gz') * k + ('Gx', ) * 4, '(GzGxGxGxGxGzGzGxGxGxGxGz)^' + str(k) + 'GxGxGxGx') ] thetaSinStrList += [ _Circuit( ('Gx', 'Gx', 'Gz', 'Gz') + ('Gz', 'Gx', 'Gx', 'Gx', 'Gx', 'Gz', 'Gz', 'Gx', 'Gx', 'Gx', 'Gx', 'Gz') * k + ('Gx', ) * 4, '(GxGxGzGz)(GzGxGxGxGxGzGzGxGxGxGxGz)^' + str(k) + 'GxGxGxGx') ] #From RPEToolsNewNew.py #thetaCosStrList += [_Circuit( # ('Gz','Gx','Gx','Gx','Gx','Gz','Gz','Gx','Gx','Gx','Gx','Gz')*k, # '(GzGxGxGxGxGzGzGxGxGxGxGz)^'+str(k))] # #thetaSinStrList += [_Circuit( # ('Gx','Gx')+ # ('Gz','Gx','Gx','Gx','Gx','Gz','Gz','Gx','Gx','Gx','Gx','Gz')*k, # 'GxGx(GzGxGxGxGxGzGzGxGxGxGxGz)^'+str(k))] return thetaCosStrList, thetaSinStrList
def clifford_compilation(self, qubit_labels=None): """ Return the Clifford-compilation dictionary for this model pack. This is a dictionary whose keys are all the n-qubit Clifford gates, `"GcX"`, where `X` is an integer, and whose values are circuits (given as tuples of labels) specifying how to compile that Clifford out of the native gates. Parameters ---------- qubit_labels : tuple, optional If not None, a tuple of the qubit labels to use in the returned circuits. If None, then the default labels are used, which are often the integers beginning with 0. Returns ------- dict """ if qubit_labels is None: qubit_labels = self._sslbls assert (len(qubit_labels) == len( self._sslbls)), "Wrong number of labels in: %s" % str(qubit_labels) return { clifford_name: _Circuit(_transform_circuit_tup(circuittup_of_native_gates, qubit_labels), line_labels=qubit_labels) for clifford_name, circuittup_of_native_gates in self._clifford_compilation.items() }
def _indexed_circuitdict(self, prototype, index): if index is None: index = self._sslbls assert (len(index) == len( self._sslbls)), "Wrong number of labels in: %s" % str(index) if prototype is not None: return { _Circuit(_transform_circuit_tup(k, index), line_labels=index): val for k, val in prototype.items() }
def make_rpe_epsilon_str_lists_gx_gz(k_list): """ Make epsilon cosine and sine circuit lists for (approx) X pi/4 and Z pi/2 gates. These circuits are used to estimate epsilon (X rotation angle). Parameters ---------- k_list : list of ints The list of "germ powers" to be used. Typically successive powers of two; e.g. [1,2,4,8,16]. Returns ------- epsilonCosStrList : list of Circuits The list of "cosine strings" to be used for epsilon estimation. epsilonSinStrList : list of Circuits The list of "sine strings" to be used for epsilon estimation. """ epsilonCosStrList = [] epsilonSinStrList = [] for k in k_list: epsilonCosStrList += [ _Circuit(('Gx', ) * k + ('Gx', ) * 4, 'Gx^' + str(k) + 'GxGxGxGx') ] epsilonSinStrList += [ _Circuit(('Gx', 'Gx', 'Gz', 'Gz') + ('Gx', ) * k + ('Gx', ) * 4, 'GxGxGzGzGx^' + str(k) + 'GxGxGxGx') ] #From RPEToolsNewNew.py #epsilonCosStrList += [_Circuit(('Gx',)*k, # 'Gx^'+str(k))] # #epsilonSinStrList += [_Circuit(('Gx','Gx')+('Gx',)*k, # 'GxGxGx^'+str(k))] return epsilonCosStrList, epsilonSinStrList
def _compute_unique_circuits(cls, circuits): first_copy = _collections.OrderedDict() to_unique = {} nUnique = 0 for i, c in enumerate(circuits): if not isinstance(c, _Circuit): c = _Circuit(c) # ensure all returned circuits are Circuits if c not in first_copy: first_copy[c] = to_unique[i] = nUnique nUnique += 1 else: to_unique[i] = first_copy[c] unique_circuits = list(first_copy.keys( )) # unique_circuits is in same order as in `circuits` return unique_circuits, to_unique
def create_rpe_angle_circuit_lists(k_list, angle_name, rpeconfig_inst): """ Make cosine and sine circuit lists. These operation sequences are used to estimate the angle specified by angle_name ('alpha', 'epsilon', or 'theta') Parameters ---------- k_list : list of ints The list of "germ powers" to be used. Typically successive powers of two; e.g. [1,2,4,8,16]. angle_name : string The angle to be deduced from these operation sequences. (Choices are 'alpha', 'epsilon', or 'theta') rpeconfig_inst : RPEconfig object Declares which model configuration RPE should be trying to fit; determines particular functions and values to be used. Returns ------- cosStrList : list of Circuits The list of "cosine strings" to be used for alpha estimation. sinStrList : list of Circuits The list of "sine strings" to be used for alpha estimation. """ # rpeconfig_inst = rpeInstanceDict[rpeconfig_inst] if angle_name == 'alpha': cos_prep_tuple = rpeconfig_inst.alpha_cos_prep_tuple cos_prep_str = rpeconfig_inst.alpha_cos_prep_str cos_germ_tuple = rpeconfig_inst.alpha_cos_germ_tuple cos_germ_str = rpeconfig_inst.alpha_cos_germ_str cos_meas_tuple = rpeconfig_inst.alpha_cos_meas_tuple cos_meas_str = rpeconfig_inst.alpha_cos_meas_str sin_prep_tuple = rpeconfig_inst.alpha_sin_prep_tuple sin_prep_str = rpeconfig_inst.alpha_sin_prep_str sin_germ_tuple = rpeconfig_inst.alpha_sin_germ_tuple sin_germ_str = rpeconfig_inst.alpha_sin_germ_str sin_meas_tuple = rpeconfig_inst.alpha_sin_meas_tuple sin_meas_str = rpeconfig_inst.alpha_sin_meas_str elif angle_name == 'epsilon': cos_prep_tuple = rpeconfig_inst.epsilon_cos_prep_tuple cos_prep_str = rpeconfig_inst.epsilon_cos_prep_str cos_germ_tuple = rpeconfig_inst.epsilon_cos_germ_tuple cos_germ_str = rpeconfig_inst.epsilon_cos_germ_str cos_meas_tuple = rpeconfig_inst.epsilon_cos_meas_tuple cos_meas_str = rpeconfig_inst.epsilon_cos_meas_str sin_prep_tuple = rpeconfig_inst.epsilon_sin_prep_tuple sin_prep_str = rpeconfig_inst.epsilon_sin_prep_str sin_germ_tuple = rpeconfig_inst.epsilon_sin_germ_tuple sin_germ_str = rpeconfig_inst.epsilon_sin_germ_str sin_meas_tuple = rpeconfig_inst.epsilon_sin_meas_tuple sin_meas_str = rpeconfig_inst.epsilon_sin_meas_str elif angle_name == 'theta': cos_prep_tuple = rpeconfig_inst.theta_cos_prep_tuple cos_prep_str = rpeconfig_inst.theta_cos_prep_str cos_germ_tuple = rpeconfig_inst.theta_cos_germ_tuple cos_germ_str = rpeconfig_inst.theta_cos_germ_str cos_meas_tuple = rpeconfig_inst.theta_cos_meas_tuple cos_meas_str = rpeconfig_inst.theta_cos_meas_str sin_prep_tuple = rpeconfig_inst.theta_sin_prep_tuple sin_prep_str = rpeconfig_inst.theta_sin_prep_str sin_germ_tuple = rpeconfig_inst.theta_sin_germ_tuple sin_germ_str = rpeconfig_inst.theta_sin_germ_str sin_meas_tuple = rpeconfig_inst.theta_sin_meas_tuple sin_meas_str = rpeconfig_inst.theta_sin_meas_str else: raise Exception("Need valid angle!") cosStrList = [] sinStrList = [] for k in k_list: cosStrList += [ _Circuit(cos_prep_tuple + cos_germ_tuple * k + cos_meas_tuple, stringrep=cos_prep_str + '(' + cos_germ_str + ')^' + str(k) + cos_meas_str) ] sinStrList += [ _Circuit(sin_prep_tuple + sin_germ_tuple * k + sin_meas_tuple, stringrep=sin_prep_str + '(' + sin_germ_str + ')^' + str(k) + sin_meas_str) ] return cosStrList, sinStrList
def crosstalk_detection_experiment2( pspec, lengths, circuits_per_length, circuit_population_sz, multiplier=3, idle_prob=0.1, structure='1Q', descriptor='A set of crosstalk detections experiments', verbosity=1): experiment_dict = {} experiment_dict['spec'] = {} experiment_dict['spec']['lengths'] = lengths experiment_dict['spec']['circuit_population_sz'] = circuit_population_sz experiment_dict['spec']['multiplier'] = multiplier experiment_dict['spec']['idle_prob'] = idle_prob experiment_dict['spec']['descriptor'] = descriptor experiment_dict['spec'][ 'createdby'] = 'extras.crosstalk.crosstalk_detection_experiment2' if isinstance(structure, str): assert (structure == '1Q' ), "The only default `structure` option is the string '1Q'" structure = tuple([(q, ) for q in pspec.qubit_labels]) n = pspec.num_qubits else: assert(isinstance(structure, list) or isinstance(structure, tuple)), \ "If not a string, `structure` must be a list or tuple." qubits_used = [] for subsetQs in structure: assert (isinstance(subsetQs, list) or isinstance( subsetQs, tuple)), "SubsetQs must be a list or a tuple!" qubits_used = qubits_used + list(subsetQs) assert(len(set(qubits_used)) == len(qubits_used)), \ "The qubits in the tuples/lists of `structure must all be unique!" assert(set(qubits_used).issubset(set(pspec.qubit_labels))), \ "The qubits to benchmark must all be in the QubitProcessorSpec `pspec`!" n = len(qubits_used) experiment_dict['spec'][ 'circuits_per_length'] = circuits_per_length * multiplier * n experiment_dict['spec']['structure'] = structure experiment_dict['circuits'] = {} experiment_dict['settings'] = {} gates_available = list(pspec.models['target'].primitive_op_labels) gates_by_qubit = [[] for _ in range(0, n)] for i in range(0, len(gates_available)): for q in range(0, n): if gates_available[i].qubits == (q, ): gates_by_qubit[q].append(gates_available[i]) for lnum, l in enumerate(lengths): # generate menu of circuits for each qubit circuit_menu = [[] for _ in range(0, n)] for q in range(0, n): d = len(gates_by_qubit[q]) if d**l < circuit_population_sz: print(( '- Warning: circuit population specified too large for qubit {}' ' -- there will be redundant circuits').format(q)) for rep in range(0, circuit_population_sz): singleQcirc = [] for j in range(0, l): r = _np.random.randint(0, d) singleQcirc.append(gates_by_qubit[q][r]) circuit_menu[q].append(singleQcirc) if verbosity > 0: print( '- Sampling {} random circuits per qubit at length {} ({} of {} lengths)' .format(circuits_per_length, l, lnum + 1, len(lengths))) # loop over qubits (should generalize this to regions) cnt = 0 for q in range(0, n): print(' - Qubit {} = '.format(q)) # need circuits_per_length number of settings for this qubit for j in range(circuits_per_length): if verbosity > 0: print(' Circuit {}: ('.format(j), end='') # draw a setting for the central qubit (q) #qr = _np.random.randint(0,circuit_population_sz) # instead of randomly drawing a setting for the central qubit, # iteratively choose each of the circuits in the menu qr = j # generate "multiplier" number of random circuits on the other qubits with qr setting # on the central qubit for m in range(0, multiplier): circuit = _Circuit(num_lines=0, editable=True) settings = {} for q1 in range(0, n): if q1 == q: # the setting for the central qubit is fixed r = qr else: # draw a setting r = _np.random.randint(0, circuit_population_sz) settings[( q1, )] = lnum * (circuit_population_sz + 1) + r + 1 singleQcircuit = _Circuit(num_lines=1, line_labels=[q1], editable=True) for layer in range(0, l): singleQcircuit.insert_layer( circuit_menu[q1][r][layer], layer) singleQcircuit.done_editing() circuit.tensor_circuit(singleQcircuit) experiment_dict['settings'][l, cnt] = settings # for each line, except the central qubit, replace sequence with an idle # independently according to idle_prob if idle_prob > 0: for q1 in range(0, n): if q1 != q: idle = bool(_np.random.binomial(1, idle_prob)) if idle: circuit.replace_with_idling_line_inplace( q1) # Update the setting on that qubit to the idling setting # (denoted by the length index) experiment_dict['settings'][l, cnt][( q1, )] = lnum * (circuit_population_sz + 1) if verbosity > 0: print('(Idled {}) '.format(q1), end='') circuit.done_editing() experiment_dict['circuits'][l, cnt] = circuit cnt += 1 if verbosity > 0: print('{}, '.format(m), end='') if verbosity > 0: print(')') # How are settings labeled? # For each circuit length, we label the random circuits of that length by consecutive integers, with the all # idle being the first number in the range for that circuit length. # # So if circuit_population_sz is 10, and lengths are [10,20,30] then the the labels are: # # 0 (idle of length 10), 1, ...., 10 [all length 10 circuits] # 11 (idle of length 20), 12, ..., 21 [all length 20 circuits] # 22 (idle of length 30), 23, ..., 32 [all length 30 circuits] # print('cnt: {}'.format(cnt)) return experiment_dict
def create_lsgst_circuit_lists(op_label_src, prep_fiducials, meas_fiducials, germs, max_lengths, fid_pairs=None, trunc_scheme="whole germ powers", nest=True, keep_fraction=1, keep_seed=None, include_lgst=True, op_label_aliases=None, circuit_rules=None, dscheck=None, action_if_missing="raise", germ_length_limits=None, verbosity=0): """ Create a set of long-sequence GST circuit lists (including structure). Constructs a series (a list) of circuit structures used by long-sequence GST (LSGST) algorithms. If `include_lgst == True` then the starting structure contains the LGST strings, otherwise the starting structure is empty. For each nonzero element of max_length_list, call it L, a set of circuits is created with the form: Case: trunc_scheme == 'whole germ powers': prep_fiducial + pygsti.circuits.repeat_with_max_length(germ,L) + meas_fiducial Case: trunc_scheme == 'truncated germ powers': prep_fiducial + pygsti.circuits.repeat_and_truncate(germ,L) + meas_fiducial Case: trunc_scheme == 'length as exponent': prep_fiducial + germ^L + meas_fiducial If nest == True, the above set is iteratively *added* (w/duplicates removed) to the current circuit structure to form a final structure for the given L. This results in successively larger structures, each of which contains all the elements of previous-L structures. If nest == False then the above set *is* the final structure for the given L. Parameters ---------- op_label_src : list or Model List of operation labels to determine needed LGST circuits. If a Model, then the model's gate and instrument labels are used. Only relevant when `include_lgst == True`. prep_fiducials : list of Circuits List of the preparation fiducial circuits, which follow state preparation. effect_fiducials : list of Circuits List of the measurement fiducial circuits, which precede measurement. germs : list of Circuits List of the germ circuits. max_lengths : list of ints List of maximum lengths. A zero value in this list has special meaning, and corresponds to the LGST circuits. fid_pairs : list of 2-tuples or dict, optional Specifies a subset of all fiducial string pairs (prepStr, effectStr) to be used in the circuit lists. If a list, each element of fid_pairs is a (iPrepStr, iEffectStr) 2-tuple of integers, each indexing a string within prep_strs and effect_strs, respectively, so that prepStr = prep_strs[iPrepStr] and effectStr = effect_strs[iEffectStr]. If a dictionary, keys are germs (elements of germ_list) and values are lists of 2-tuples specifying the pairs to use for that germ. trunc_scheme : str, optional Truncation scheme used to interpret what the list of maximum lengths means. If unsure, leave as default. Allowed values are: - 'whole germ powers' -- germs are repeated an integer number of times such that the length is less than or equal to the max. - 'truncated germ powers' -- repeated germ string is truncated to be exactly equal to the max (partial germ at end is ok). - 'length as exponent' -- max. length is instead interpreted as the germ exponent (the number of germ repetitions). nest : boolean, optional If True, the returned circuit lists are "nested", meaning that each successive list of circuits contains all the gate strings found in previous lists (and usually some additional new ones). If False, then the returned string list for maximum length == L contains *only* those circuits specified in the description above, and *not* those for previous values of L. keep_fraction : float, optional The fraction of fiducial pairs selected for each germ-power base string. The default includes all fiducial pairs. Note that for each germ-power the selected pairs are *different* random sets of all possible pairs (unlike fid_pairs, which specifies the *same* fiducial pairs for *all* same-germ base strings). If fid_pairs is used in conjuction with keep_fraction, the pairs specified by fid_pairs are always selected, and any additional pairs are randomly selected. keep_seed : int, optional The seed used for random fiducial pair selection (only relevant when keep_fraction < 1). include_lgst : boolean, optional If true, then the starting list (only applicable when `nest == True`) is the list of LGST strings rather than the empty list. This means that when `nest == True`, the LGST circuits will be included in all the lists. op_label_aliases : dictionary, optional Dictionary whose keys are operation label "aliases" and whose values are tuples corresponding to what that operation label should be expanded into before querying the dataset. This information is stored within the returned circuit structures. Defaults to the empty dictionary (no aliases defined) e.g. op_label_aliases['Gx^3'] = ('Gx','Gx','Gx') circuit_rules : list, optional A list of `(find,replace)` 2-tuples which specify string replacement rules. Both `find` and `replace` are tuples of operation labels (or `Circuit` objects). dscheck : DataSet, optional A data set which is checked for each of the generated circuits. When a generated circuit is missing from this `DataSet`, action is taken according to `action_if_missing`. action_if_missing : {"raise","drop"}, optional The action to take when a generated circuit is missing from `dscheck` (only relevant when `dscheck` is not None). "raise" causes a ValueError to be raised; "drop" causes the missing circuits to be dropped from the returned set. germ_length_limits : dict, optional A dictionary limiting the max-length values used for specific germs. Keys are germ circuits and values are integers. For example, if this argument is `{('Gx',): 4}` and `max_length_list = [1,2,4,8,16]`, then the germ `('Gx',)` is only repeated using max-lengths of 1, 2, and 4 (whereas other germs use all the values in `max_length_list`). verbosity : int, optional The level of output to print to stdout. Returns ------- list of PlaquetteGridCircuitStructure objects The i-th object corresponds to a circuit list containing repeated germs limited to length max_length_list[i]. If nest == True, then repeated germs limited to previous max-lengths are also included. Note that a "0" maximum-length corresponds to the LGST strings. """ # ensure circuit lists have computed their string reps so addition produces "nice" strings for printing [germ.str for germ in germs] [c.str for c in prep_fiducials] [c.str for c in meas_fiducials] #print('Germs: ', germs) def filter_ds(circuits, ds, missing_lgst): if ds is None: return circuits[:] filtered_circuits = [] for opstr in circuits: trans_opstr = _gsc.translate_circuit(opstr, op_label_aliases) if trans_opstr not in ds: missing_lgst.append(opstr) else: filtered_circuits.append(opstr) return filtered_circuits def add_to_plaquettes(pkey_dict, plaquette_dict, base_circuit, maxlen, germ, power, fidpair_indices, ds, missing_list): """ Only create a new plaquette for a new base circuit; otherwise add to existing """ if ds is not None: inds_to_remove = [] for k, (i, j) in enumerate(fidpair_indices): el = prep_fiducials[i] + base_circuit + meas_fiducials[j] trans_el = _gsc.translate_circuit(el, op_label_aliases) if trans_el not in ds: missing_list.append((prep_fiducials[i], germ, maxlen, meas_fiducials[j], el)) inds_to_remove.append(k) if len(inds_to_remove) > 0: fidpair_indices = fidpair_indices[:] # copy for i in reversed(inds_to_remove): del fidpair_indices[i] fidpairs = _collections.OrderedDict([((j, i), (prep_fiducials[i], meas_fiducials[j])) for i, j in fidpair_indices]) if base_circuit not in plaquette_dict: pkey_dict[base_circuit] = (maxlen, germ) if power is None: # no well-defined power, so just make a fiducial-pair plaquette plaq = _FiducialPairPlaquette(base_circuit, fidpairs, len(meas_fiducials), len(prep_fiducials), op_label_aliases, circuit_rules) else: plaq = _GermFiducialPairPlaquette(germ, power, fidpairs, len(meas_fiducials), len(prep_fiducials), op_label_aliases, circuit_rules) plaquette_dict[base_circuit] = plaq else: #Add to existing plaquette (assume we don't need to change number of rows/cols of plaquette) existing_plaq = plaquette_dict[base_circuit] existing_circuits = set(existing_plaq.circuits) new_fidpairs = existing_plaq.fidpairs.copy() for (j, i), (prep, meas) in fidpairs.items(): if prep + base_circuit + meas not in existing_circuits: new_fidpairs[(j, i)] = (prep, meas) if power is None: # no well-defined power, so just make a fiducial-pair plaquette plaquette_dict[base_circuit] = _FiducialPairPlaquette( base_circuit, new_fidpairs, len(meas_fiducials), len(prep_fiducials), op_label_aliases, circuit_rules) else: plaquette_dict[base_circuit] = _GermFiducialPairPlaquette( germ, power, new_fidpairs, len(meas_fiducials), len(prep_fiducials), op_label_aliases, circuit_rules) printer = _VerbosityPrinter.create_printer(verbosity) if germ_length_limits is None: germ_length_limits = {} if nest and include_lgst and len(max_lengths) > 0 and max_lengths[0] == 0: _warnings.warn( "Setting the first element of a max-length list to zero" + " to ensure the inclusion of LGST circuits has been" + " replaced by the `include_lgst` parameter which" + " defaults to `True`. Thus, in most cases, you can" + " simply remove the leading 0 and start your" + " max-length list at 1 now." + "") from pygsti.processors.processorspec import QuditProcessorSpec as _QuditProcessorSpec from pygsti.models.model import OpModel as _OpModel if isinstance(op_label_src, _QuditProcessorSpec): opLabels = op_label_src.primitive_op_labels elif isinstance(op_label_src, _OpModel): opLabels = op_label_src.primitive_op_labels + op_label_src.primitive_instrument_labels else: opLabels = op_label_src lgst_list = _gsc.create_lgst_circuits(prep_fiducials, meas_fiducials, opLabels) if circuit_rules is not None: lgst_list = _manipulate_circuits(lgst_list, circuit_rules) allPossiblePairs = list( _itertools.product(range(len(prep_fiducials)), range(len(meas_fiducials)))) if keep_fraction < 1.0: rndm = _rndm.RandomState(keep_seed) # ok if seed is None nPairs = len(prep_fiducials) * len(meas_fiducials) nPairsToKeep = int(round(float(keep_fraction) * nPairs)) else: rndm = None fidpair_germ_power_keys = False if isinstance(fid_pairs, dict) or hasattr(fid_pairs, "keys"): fidPairDict = fid_pairs # assume a dict of per-germ pairs if isinstance(list(fidPairDict.keys())[0], tuple): fidpair_germ_power_keys = True else: if fid_pairs is not None: # assume fid_pairs is a list fidPairDict = {germ: fid_pairs for germ in germs} else: fidPairDict = None truncFn = _get_trunc_function(trunc_scheme) line_labels = germs[0].line_labels if len(germs) > 0 \ else (prep_fiducials + meas_fiducials)[0].line_labels # if an empty germ list, base line_labels off fiducials empty_germ = _Circuit(( ), line_labels) # , stringrep="{}@(%s)" % ','.join(map(str, line_labels))) if include_lgst and empty_germ not in germs: germs = [empty_germ] + germs if nest: #keep track of running quantities used to build circuit structures running_plaquette_keys = { } # base-circuit => (maxlength, germ) key for final plaquette dict running_plaquettes = _collections.OrderedDict( ) # keep consistent ordering in produced circuit list. running_unindexed = [] running_maxLens = [] lsgst_structs = [] # list of circuit structures to return missing_list = [] # keep track of missing data if dscheck is given missing_lgst = [ ] # keep track of missing LGST circuits separately (for better error msgs) tot_circuits = 0 if include_lgst and len(max_lengths) == 0: # Then we won't add LGST circuits during first iteration of loop below, so add them now unindexed = filter_ds(lgst_list, dscheck, missing_lgst) lsgst_structs.append( _PlaquetteGridCircuitStructure( {}, [], germs, "L", "germ", unindexed, op_label_aliases, circuit_weights_dict=None, additional_circuits_location='start', name=None)) for i, maxLen in enumerate(max_lengths): if nest: # add to running_* variables and pinch off a copy later on running_maxLens.append(maxLen) pkey = running_plaquette_keys plaquettes = running_plaquettes maxLens = running_maxLens unindexed = running_unindexed else: # create a new cs for just this maxLen pkey = { } # base-circuit => (maxlength, germ) key for final plaquette dict plaquettes = _collections.OrderedDict() maxLens = [maxLen] unindexed = [] if maxLen == 0: # Special LGST case unindexed.extend(filter_ds( lgst_list, dscheck, missing_lgst)) # overlap w/plaquettes ok (removed later) else: if include_lgst and i == 0: # first maxlen, so add LGST seqs as empty germ #Add LGST circuits as an empty-germ plaquette (and as unindexed circuits to include everything) #Note: no FPR on LGST strings add_to_plaquettes(pkey, plaquettes, empty_germ, maxLen, empty_germ, 1, allPossiblePairs, dscheck, missing_list) unindexed.extend(filter_ds( lgst_list, dscheck, missing_lgst)) # overlap w/plaquettes ok (removed later) #Typical case of germs repeated to maxLen using r_fn for ii, germ in enumerate(germs): if germ == empty_germ: continue # handled specially above if maxLen > germ_length_limits.get(germ, 1e100): continue germ_power = truncFn(germ, maxLen) power = len(germ_power) // len( germ) # this *could* be the germ power if germ_power != germ * power: power = None # Signals there is no well-defined power if power == 0 and len(germ) != 0: continue # Switch on fidpair dicts with germ or (germ, L) keys key = germ if fidpair_germ_power_keys: key = (germ, maxLen) if rndm is None: fiducialPairsThisIter = fidPairDict.get(key, allPossiblePairs) \ if fidPairDict is not None else allPossiblePairs #if fiducialPairsThisIter==allPossiblePairs: # print('Couldn\'t find ', key, ' using allPossiblePairs') #print('FiducialPairsThisIter: ', fiducialPairsThisIter) elif fidPairDict is not None: pair_indx_tups = fidPairDict.get(key, allPossiblePairs) remainingPairs = [(i, j) for i in range(len(prep_fiducials)) for j in range(len(meas_fiducials)) if (i, j) not in pair_indx_tups] nPairsRemaining = len(remainingPairs) nPairsToChoose = nPairsToKeep - len(pair_indx_tups) nPairsToChoose = max(0, min(nPairsToChoose, nPairsRemaining)) assert (0 <= nPairsToChoose <= nPairsRemaining) # FUTURE: issue warnings when clipping nPairsToChoose? fiducialPairsThisIter = fidPairDict[key] + \ [remainingPairs[k] for k in sorted(rndm.choice(nPairsRemaining, nPairsToChoose, replace=False))] else: # rndm is not None and fidPairDict is None assert (nPairsToKeep <= nPairs ) # keep_fraction must be <= 1.0 fiducialPairsThisIter = \ [allPossiblePairs[k] for k in sorted(rndm.choice(nPairs, nPairsToKeep, replace=False))] add_to_plaquettes(pkey, plaquettes, germ_power, maxLen, germ, power, fiducialPairsThisIter, dscheck, missing_list) if nest: # pinch off a copy of variables that were left as the running variables above maxLens = maxLens[:] plaquettes = plaquettes.copy() unindexed = unindexed[:] lsgst_structs.append( _PlaquetteGridCircuitStructure( _collections.OrderedDict([ (pkey[base], plaq) for base, plaq in plaquettes.items() ]), maxLens, germs, "L", "germ", unindexed, op_label_aliases, circuit_weights_dict=None, additional_circuits_location='start', name=None)) tot_circuits += len( lsgst_structs[-1]) # only relevant for non-nested case if nest: # then totStrs computation about overcounts -- just take string count of final stage tot_circuits = len(lsgst_structs[-1]) if len(lsgst_structs) > 0 else 0 printer.log("--- Circuit Creation ---", 1) printer.log(" %d circuits created" % tot_circuits, 2) #print("Total Number of Circuits: ", tot_circuits) if dscheck: printer.log( " Dataset has %d entries: %d utilized, %d requested circuits were missing" % (len(dscheck), tot_circuits, len(missing_list)), 2) #print(len(missing_lgst)) if len(missing_list) > 0 or len(missing_lgst) > 0: MAX = 10 # Maximum missing-seq messages to display missing_msgs = [("Prep: %s, Germ: %s, L: %d, Meas: %s, Circuit: %s" % tup) for tup in missing_list[0:MAX + 1]] \ + ["LGST Seq: %s" % opstr for opstr in missing_lgst[0:MAX + 1]] if len(missing_list) > MAX or len(missing_lgst) > MAX: missing_msgs.append(" ... (more missing circuits not show) ... ") printer.log("The following circuits were missing from the dataset:", 4) printer.log("\n".join(missing_msgs), 4) if action_if_missing == "raise": raise ValueError("Missing data! %d missing circuits" % len(missing_msgs)) elif action_if_missing == "drop": pass else: raise ValueError("Invalid `action_if_missing` argument: %s" % action_if_missing) for i, struct in enumerate(lsgst_structs): if nest: assert (struct.xs == max_lengths[0:i + 1] ) # Make sure lengths are correct! else: assert (struct.xs == max_lengths[i:i + 1] ) # Make sure lengths are correct! return lsgst_structs