Exemple #1
0
    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
Exemple #2
0
    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
Exemple #3
0
 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()
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
 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)
Exemple #7
0
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
Exemple #8
0
 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
Exemple #9
0
 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)
Exemple #10
0
    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)
Exemple #11
0
 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)
Exemple #12
0
 def __contains__(self, key):
     if not isinstance(key, _Label): key = _Label(key, None)
     return super(OrderedMemberDict, self).__contains__(key)
Exemple #13
0
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)