def _default_primitive_povm_layer_lbl(self, sslbls): """ Gets the default POVM label. This is often used when a circuit is specified without an ending POVM layer. Returns `None` if there is no default and one *must* be specified. Parameters ---------- sslbls : tuple or None The state space labels being measured, and for which a default POVM is desired. Returns ------- Label or None """ if len(self.primitive_povm_labels) == 1: povm_name = next(iter(self.primitive_povm_labels)).name if (self.state_space.num_tensor_product_blocks == 1 and (self.state_space.tensor_product_block_labels(0) == sslbls or sslbls == ('*', ))): return _Label( povm_name) # because sslbls == all of model's sslbls else: return _Label(povm_name, sslbls) else: return None
def simplify_effects(self, prefix=""): """ Creates a dictionary of simplified effect vectors. Returns a dictionary of effect POVMEffects that belong to the POVM's parent `Model` - that is, whose `gpindices` are set to all or a subset of this POVM's gpindices. Such effect vectors are used internally within computations involving the parent `Model`. Parameters ---------- prefix : str A string, usually identitying this POVM, which may be used to prefix the simplified gate keys. Returns ------- OrderedDict of POVMEffects """ if isinstance( prefix, _Label): # Deal with case when prefix isn't just a string simplified = _collections.OrderedDict([ (_Label(prefix.name + '_' + k, prefix.sslbls), self[k]) for k in self.keys() ]) else: if prefix: prefix += "_" simplified = _collections.OrderedDict([(prefix + k, self[k]) for k in self.keys()]) return simplified
def __delitem__(self, key): """Implements `del self[key]`""" if not isinstance(key, _Label): key = _Label(key, None) super(OrderedMemberDict, self).__delitem__(key) if self.parent is not None: #print("DEBUG: rebuilding paramvec after deleting ", key, " : ", list(self.keys())) self.parent._rebuild_paramvec()
def simplify_operations(self, prefix=""): """ Creates a dictionary of simplified instrument operations. Returns a dictionary of operations that belong to the Instrument's parent `Model` - that is, whose `gpindices` are set to all or a subset of this instruments's gpindices. These are used internally within computations involving the parent `Model`. Parameters ---------- prefix : str A string, usually identitying this instrument, which may be used to prefix the simplified gate keys. Returns ------- OrderedDict of Gates """ # Create "simplified" elements, which infer their parent and # gpindices from the set of "param-gates" they're constructed with. if isinstance( prefix, _Label): # Deal with case when prefix isn't just a string simplified = _collections.OrderedDict([ (_Label(prefix.name + "_" + k, prefix.sslbls), v) for k, v in self.items() ]) else: if prefix: prefix += "_" simplified = _collections.OrderedDict([(prefix + k, v) for k, v in self.items()]) return simplified
def simplify_operations(self, prefix=""): """ Creates a dictionary of simplified instrument operations. Returns a dictionary of operations that belong to the Instrument's parent `Model` - that is, whose `gpindices` are set to all or a subset of this instruments's gpindices. These are used internally within computations involving the parent `Model`. Parameters ---------- prefix : str A string, usually identitying this instrument, which may be used to prefix the simplified gate keys. Returns ------- OrderedDict of Gates """ #Create a "simplified" (Model-referencing) set of element gates simplified = _collections.OrderedDict() if isinstance( prefix, _Label): # Deal with case when prefix isn't just a string for k, g in self.items(): simplified[_Label(prefix.name + "_" + k, prefix.sslbls)] = g else: if prefix: prefix += "_" for k, g in self.items(): simplified[prefix + k] = g return simplified
def _iter_parameterized_objs(self): for dictlbl, objdict in _itertools.chain(self.prep_blks.items(), self.povm_blks.items(), self.operation_blks.items(), self.instrument_blks.items(), self.factories.items()): for lbl, obj in objdict.items(): yield (_Label(dictlbl + ":" + lbl.name, lbl.sslbls), obj)
def _init_spam_layers(model, prep_layers, povm_layers): """ Helper function for initializing the .prep_blks and .povm_blks elements of an implicit model""" # SPAM (same as for cloud noise model) if prep_layers is None: pass # no prep layers elif isinstance(prep_layers, dict): for rhoname, layerop in prep_layers.items(): model.prep_blks['layers'][_Label(rhoname)] = layerop elif isinstance(prep_layers, _op.LinearOperator): # just a single layer op model.prep_blks['layers'][_Label('rho0')] = prep_layers else: # assume prep_layers is an iterable of layers, e.g. isinstance(prep_layers, (list,tuple)): for i, layerop in enumerate(prep_layers): model.prep_blks['layers'][_Label("rho%d" % i)] = layerop if povm_layers is None: pass # no povms elif isinstance( povm_layers, _povm.POVM): # just a single povm - must precede 'dict' test! model.povm_blks['layers'][_Label('Mdefault')] = povm_layers elif isinstance(povm_layers, dict): for povmname, layerop in povm_layers.items(): model.povm_blks['layers'][_Label(povmname)] = layerop else: # assume povm_layers is an iterable of layers, e.g. isinstance(povm_layers, (list,tuple)): for i, layerop in enumerate(povm_layers): model.povm_blks['layers'][_Label("M%d" % i)] = layerop
def indices_for_label(lbl): """ Returns a list of the parameter indices corresponding to `lbl` """ if self._alias_dict.get(lbl, lbl) in g_inds: return [g_inds[self._alias_dict.get(lbl, lbl)]] elif self._alias_dict.get( lbl.name, lbl.name ) in g_inds: # allow, e.g. "Gx" to work for Gx:0, Gx:1, etc. return [g_inds[self._alias_dict.get(lbl.name, lbl.name)]] elif self._alias_dict.get(_Label(lbl.name, lbl.sslbls), _Label(lbl.name, lbl.sslbls)) in g_inds: # Allow time/arg stripped labels to match return [ g_inds[self._alias_dict.get(_Label(lbl.name, lbl.sslbls), _Label(lbl.name, lbl.sslbls))] ] else: indices = [] assert (not lbl.is_simple() ), "Cannot find error rate for label: %s" % str(lbl) for component in lbl: indices.extend(indices_for_label(component)) return indices
def _build_explicit_target_model(self, sslbls, gate_names, gate_expressions, **kwargs): """ A helper function for derived classes which create explicit models. Updates gate names and expressions with a given set of state-space labels. """ full_sslbls = [sslbls] # put all sslbls in single tensor product block sslbl_map = {i: sslbl for i, sslbl in enumerate(sslbls)} updated_gatenames = [ _Label(gn).map_state_space_labels(sslbl_map) for gn in gate_names ] updated_gateexps = [gexp.format(*sslbls) for gexp in gate_expressions] return _build_explicit_model(full_sslbls, updated_gatenames, updated_gateexps, **kwargs)
def __setitem__(self, key, value): if not isinstance(key, _Label): key = _Label(key) value = self._auto_embed(key, value) # automatically create an embedded gate if needed self._check_state_space(value) if isinstance(value, _mm.ModelMember): # if we're given an object, just replace #When self has a valid parent (as it usually does, except when first initializing) # we copy and reset the gpindices & parent of ModelMember values *if* they # belong to a different parent (indices would be inapplicable if they exist). # # If a ModelMember's parent is None then we leave the indices alone, as they may # have been initialized to allow the object to fully function in the absence of a # parent model. Still, these indices are inapplicable to us and will prompt a call # to value.allocate_gpindices(...) the next time the Model's parameter vector is rebuilt. # # Alternatively, gpindices==None indicates that the value may not have had # its gpindices allocated yet and so *might* have "latent" (i.e. from-submember) gpindices # that do belong to our parent (self.parent) (and copying a value will reset all # the parents to None). # # Either way, we should only copy the value when its parent is non-None and not our parent. wrongParent = (value.parent is not None) and (value.parent is not self.parent) if self.parent is not None and wrongParent: value = value.copy() # copy value (so we don't mess up other parent) and value.set_gpindices(None, self.parent) # erase gindices that don't apply to us if not hasattr(value, "_evotype"): value._evotype = "densitymx" # for backward compatibility self._check_evotype(value._evotype) if self.parent is not None and key in self: existing = super(OrderedMemberDict, self).__getitem__(key) else: existing = None super(OrderedMemberDict, self).__setitem__(key, value) new_item = value # keep track of newly set item for later # let the now-replaced existing object know it's been # removed from the parent, allowing it to reset (to None) # its parent link if there are no more references to it. if existing is not None and value is not existing: assert(existing.parent is None or existing.parent is self.parent), "Model object not setup correctly" existing.unlink_parent() elif key in self: # if a object already exists... #try to set its value super(OrderedMemberDict, self).__getitem__(key).set_dense(value) new_item = None # keep track of newly set item for later else: #otherwise, we've been given a non-ModelMember-object that doesn't # exist yet, so use default creation flags to make one: obj = self.cast_to_model_member(value) if obj is None: raise ValueError("Cannot set a value of type: ", type(value)) self._check_evotype(obj._evotype) if self.parent is not None: obj.set_gpindices(None, self.parent) super(OrderedMemberDict, self).__setitem__(key, obj) new_item = obj # keep track of newly set item for later #rebuild Model's parameter vector is a new modelmember has been added (*number* of params may need to change) if new_item is not None and self.parent is not None: #print("DEBUG: marking paramvec for rebuild after inserting ", key, " : ", list(self.keys())) # mark the parent's (Model's) paramvec for rebuilding: self.parent._mark_for_rebuild(new_item) if new_item.parent is not self.parent: # de-allocate any items allocated to other models new_item.unlink_parent(force=True)
def __getitem__(self, key): #if self.parent is not None: # #print("DEBUG: cleaning paramvec before getting ", key) # self.parent._clean_paramvec() if not isinstance(key, _Label): key = _Label(key, None) return super(OrderedMemberDict, self).__getitem__(key)
def __contains__(self, key): if not isinstance(key, _Label): key = _Label(key, None) return super(OrderedMemberDict, self).__contains__(key)
def set_idle_errors(nqubits, model, errdict, rand_default=None, hamiltonian=True, stochastic=True, affine=True): """ Set specific or random error terms (typically for a data-generating model) within a noise model (a :class:`CloudNoiseModel` object). Parameters ---------- nqubits : int The number of qubits. model : CloudNoiseModel The model, to set the idle errors of. errdict : dict A dictionary of errors to include. Keys are `"S(<>)"`, `"H(<>)"`, and `"A(<>)"` where <> is a string of 'X','Y','Z',and 'I' (e.g. `"S(XIZ)"`) and values are floating point error rates. rand_default : float or numpy array, optional Random error rates to insert into values not specified in `errdict`. If a floating point number, a random value between 0 and `rand_default` is used. If an array, then values are taken directly and sequentially from this array (typically of random rates). The array must be long enough to provide values for all unspecified rates. hamiltonian, stochastic, affine : bool, optional Whether `model` includes Hamiltonian, Stochastic, and/or Affine errors (e.g. if the model was built with "H+S" parameterization, then only `hamiltonian` and `stochastic` should be set to True). Returns ------- numpy.ndarray The random rates the were used. """ assert ( affine is False ), "Affine errors are no longer supported - must set `affine=False`" rand_rates = [] i_rand_default = 0 v = model.to_vector() #assumes Implicit model w/'globalIdle' as a composed gate... # each factor applies to some set of the qubits (of size 1 to the max-error-weight) if isinstance(model, _CloudNoiseModel ) and model._layer_rules.implicit_idle_mode == "add_global": global_idle_lbl = _Label(()) else: global_idle_lbl = model.processor_spec.global_idle_layer_label global_idle = model.circuit_layer_operator(global_idle_lbl, typ='op') factorops = global_idle.factorops if isinstance( global_idle, _op.ComposedOp) else (global_idle, ) for i, factor in enumerate(factorops): #print("Factor %d: target = %s, gpindices=%s" % (i,str(factor.targetLabels),str(factor.gpindices))) if isinstance(factor, _op.EmbeddedOp): experrgen_op = factor.embedded_op targetLabels = factor.target_labels else: experrgen_op = factor targetLabels = model.state_space.qubit_labels assert(isinstance(experrgen_op, _op.ExpErrorgenOp)), \ "Expected idle op to be a composition of possibly embedded exp(errorgen) gates!" sub_v = v[factor.gpindices] off = 0 slcH = slcO = slice(0, 0, None) for blk in experrgen_op.errorgen.coefficient_blocks: if blk._block_type == 'ham': slcH = slice(off, off + blk.num_params) if blk._block_type == 'other_diagonal': slcO = slice(off, off + blk.num_params) off += blk.num_params #REMOVE bsH = experrgen_op.errorgen.ham_basis_size #REMOVE bsO = experrgen_op.errorgen.other_basis_size if hamiltonian: hamiltonian_sub_v = sub_v[ slcH] # -1s b/c bsH, bsO include identity in basis if stochastic: stochastic_sub_v = sub_v[slcO] #REMOVE if affine: affine_sub_v = sub_v[bsH - 1 + bsO - 1:bsH - 1 + 2 * (bsO - 1)] for k, tup in enumerate(nontrivial_paulis(len(targetLabels))): lst = ['I'] * nqubits for ii, i in enumerate(targetLabels): indx = i if isinstance(i, int) else int( i[1:] ) # i is something like "Q0" so int(i[1:]) extracts the 0 lst[indx] = tup[ii] label = "".join(lst) if "S(%s)" % label in errdict: Srate = errdict["S(%s)" % label] elif rand_default is None: Srate = 0.0 elif isinstance(rand_default, float): Srate = rand_default * _np.random.random() rand_rates.append(Srate) else: # assume rand_default is array-like, and gives default rates Srate = rand_default[i_rand_default] i_rand_default += 1 if "H(%s)" % label in errdict: Hrate = errdict["H(%s)" % label] elif rand_default is None: Hrate = 0.0 elif isinstance(rand_default, float): Hrate = rand_default * _np.random.random() rand_rates.append(Hrate) else: # assume rand_default is array-like, and gives default rates Hrate = rand_default[i_rand_default] i_rand_default += 1 if "A(%s)" % label in errdict: Arate = errdict["A(%s)" % label] elif rand_default is None: Arate = 0.0 elif isinstance(rand_default, float): Arate = rand_default * _np.random.random() rand_rates.append(Arate) else: # assume rand_default is array-like, and gives default rates Arate = rand_default[i_rand_default] i_rand_default += 1 if hamiltonian: hamiltonian_sub_v[k] = Hrate if stochastic: stochastic_sub_v[k] = _np.sqrt(Srate) # b/c param gets squared #if affine: affine_sub_v[k] = Arate model.from_vector(v) return _np.array( rand_rates, 'd' ) # the random rates that were chosen (to keep track of them for later)