Beispiel #1
0
    def compute_gate_inverse(gate_label):

        if gate_label.name in gate_inverse.keys():
            return _lbl.Label(gate_inverse[gate_label.name], gate_label.qubits)
        else:
            if gate_label.name == 'Gzr' or gate_label.name == 'Gczr':
                return _lbl.Label(gate_label.name,
                                  gate_label.qubits,
                                  args=(str(-1 * float(gate_label.args[0])), ))
            else:
                raise ValueError("Cannot invert gate with name {}".format(
                    gate_label.name))
Beispiel #2
0
 def p_expdstr_expop(p):
     '''expdstr : expable EXPOP INTEGER'''
     plbl = _lbl.Label(p[1])  # just for total sslbls
     if len(p[1]) > 0:
         p[0] = _lbl.CircuitLabel('', p[1], plbl.sslbls, p[3]),
     else:
         p[0] = ()  # special case of {}^power => remain empty
Beispiel #3
0
    def make_label(s):
        if '!' in s:
            s, time = s.split(
                '!')  # must be only two parts (only 1 exclamation pt)
            time = float(time)
        else:
            time = 0.0

        if ';' in s:
            parts = s.split(';')
            parts2 = parts[-1].split(':')
            nm = parts[0]
            args = parts[1:-1] + [parts2[0]]
            sslbls = parts2[1:]
        else:
            parts = s.split(':')
            nm = parts[0]
            args = None
            sslbls = parts[1:]

        if len(sslbls) == 0:
            sslbls = None

        return _lbl.Label(nm, sslbls, time, args)
Beispiel #4
0
 def p_layerable_subcircuit_expop(p):
     '''layerable : subcircuit EXPOP INTEGER'''
     plbl = _lbl.Label(p[1])  # just for total sslbls
     p[0] = _lbl.CircuitLabel('', p[1], plbl.sslbls, p[3]),
Beispiel #5
0
 def p_layerable_subcircuit(p):
     '''layerable : subcircuit '''
     plbl = _lbl.Label((p[1], ))  # just for total sslbls
     p[0] = _lbl.CircuitLabel('', p[1], plbl.sslbls, 1),
def simulate_data(model_or_dataset,
                  circuit_list,
                  num_samples,
                  sample_error="multinomial",
                  seed=None,
                  rand_state=None,
                  alias_dict=None,
                  collision_action="aggregate",
                  record_zero_counts=True,
                  comm=None,
                  mem_limit=None,
                  times=None):
    """
    Creates a DataSet using the probabilities obtained from a model.

    Parameters
    ----------
    model_or_dataset : Model or DataSet object
        The source of the underlying probabilities used to generate the data.
        If a Model, the model whose probabilities generate the data.
        If a DataSet, the data set whose frequencies generate the data.

    circuit_list : list of (tuples or Circuits) or ExperimentDesign or None
        Each tuple or Circuit contains operation labels and
        specifies a gate sequence whose counts are included
        in the returned DataSet. e.g. ``[ (), ('Gx',), ('Gx','Gy') ]``
        If an :class:`ExperimentDesign`, then the design's `.all_circuits_needing_data`
        list is used as the circuit list.

    num_samples : int or list of ints or None
        The simulated number of samples for each circuit.  This only has
        effect when  ``sample_error == "binomial"`` or ``"multinomial"``.  If an
        integer, all circuits have this number of total samples. If a list,
        integer elements specify the number of samples for the corresponding
        circuit.  If ``None``, then `model_or_dataset` must be a
        :class:`~pygsti.objects.DataSet`, and total counts are taken from it
        (on a per-circuit basis).

    sample_error : string, optional
        What type of sample error is included in the counts.  Can be:

        - "none"  - no sample error: counts are floating point numbers such
          that the exact probabilty can be found by the ratio of count / total.
        - "clip" - no sample error, but clip probabilities to [0,1] so, e.g.,
          counts are always positive.
        - "round" - same as "clip", except counts are rounded to the nearest
          integer.
        - "binomial" - the number of counts is taken from a binomial
          distribution.  Distribution has parameters p = (clipped) probability
          of the circuit and n = number of samples.  This can only be used
          when there are exactly two SPAM labels in model_or_dataset.
        - "multinomial" - counts are taken from a multinomial distribution.
          Distribution has parameters p_k = (clipped) probability of the gate
          string using the k-th SPAM label and n = number of samples.

    seed : int, optional
        If not ``None``, a seed for numpy's random number generator, which
        is used to sample from the binomial or multinomial distribution.

    rand_state : numpy.random.RandomState
        A RandomState object to generate samples from. Can be useful to set
        instead of `seed` if you want reproducible distribution samples across
        multiple random function calls but you don't want to bother with
        manually incrementing seeds between those calls.

    alias_dict : dict, optional
        A dictionary mapping single operation labels into tuples of one or more
        other operation labels which translate the given circuits before values
        are computed using `model_or_dataset`.  The resulting Dataset, however,
        contains the *un-translated* circuits as keys.

    collision_action : {"aggregate", "keepseparate"}
        Determines how duplicate circuits are handled by the resulting
        `DataSet`.  Please see the constructor documentation for `DataSet`.

    record_zero_counts : bool, optional
        Whether zero-counts are actually recorded (stored) in the returned
        DataSet.  If False, then zero counts are ignored, except for
        potentially registering new outcome labels.

    comm : mpi4py.MPI.Comm, optional
        When not ``None``, an MPI communicator for distributing the computation
        across multiple processors and ensuring that the *same* dataset is
        generated on each processor.

    mem_limit : int, optional
        A rough memory limit in bytes which is used to determine job allocation
        when there are multiple processors.

    times : iterable, optional
        When not None, a list of time-stamps at which data should be sampled.
        `num_samples` samples will be simulated at each time value, meaning that
        each circuit in `circuit_list` will be evaluated with the given time
        value as its *start time*.

    Returns
    -------
    DataSet
        A static data set filled with counts for the specified circuits.
    """
    NTOL = 10
    TOL = 10**-NTOL

    if isinstance(model_or_dataset, _ds.DataSet):
        dsGen = model_or_dataset
        gsGen = None
        dataset = _ds.DataSet(
            collision_action=collision_action,
            outcome_label_indices=dsGen.olIndex)  # keep same outcome labels
    else:
        gsGen = model_or_dataset
        dsGen = None
        dataset = _ds.DataSet(collision_action=collision_action)

    if alias_dict:
        alias_dict = {
            _lbl.Label(ky): tuple((_lbl.Label(el) for el in val))
            for ky, val in alias_dict.items()
        }  # convert to use Labels

    from pygsti.protocols import ExperimentDesign as _ExperimentDesign
    if isinstance(circuit_list, _ExperimentDesign):
        circuit_list = circuit_list.all_circuits_needing_data

    if gsGen and times is None:
        if alias_dict is not None:
            trans_circuit_list = [
                _gstrc.translate_circuit(s, alias_dict) for s in circuit_list
            ]
        else:
            trans_circuit_list = circuit_list
        all_probs = gsGen.bulk_probabilities(trans_circuit_list,
                                             comm=comm,
                                             mem_limit=mem_limit)
    else:
        trans_circuit_list = circuit_list

    if comm is None or comm.Get_rank() == 0:  # only root rank computes

        if sample_error in ("binomial", "multinomial") and rand_state is None:
            rndm = _rndm.RandomState(seed)  # ok if seed is None
        else:
            rndm = rand_state  # can be None

        circuit_times = times if times is not None else ["N/A dummy"]
        count_lists = _collections.OrderedDict()

        for tm in circuit_times:
            #print("Time ", tm)

            #It would be nice to be able to do something like this (time dependent calc for all probs at ptic time)
            #if gsGen and times is not None:
            #    all_probs = gsGen.bulk_probabilities(trans_circuit_list, comm=comm, mem_limit=mem_limit, time=tm)

            for k, (s,
                    trans_s) in enumerate(zip(circuit_list,
                                              trans_circuit_list)):

                if gsGen:
                    if times is None:
                        ps = all_probs[trans_s]
                    else:
                        ps = gsGen.probabilities(trans_s, time=tm)

                    if sample_error in ("binomial", "multinomial"):
                        _adjust_probabilities_inbounds(ps, TOL)
                else:
                    ps = _collections.OrderedDict([
                        (ol, frac)
                        for ol, frac in dsGen[trans_s].fractions.items()
                    ])

                if gsGen and sample_error in ("binomial", "multinomial"):
                    _adjust_unit_sum(ps, TOL)

                if num_samples is None and dsGen is not None:
                    N = dsGen[
                        trans_s].total  # use the number of samples from the generating dataset
                    #Note: total() accounts for other intermediate-measurment branches automatically
                else:
                    try:
                        N = num_samples[
                            k]  # try to treat num_samples as a list
                    except:
                        N = num_samples  # if not indexable, num_samples should be a single number

                nWeightedSamples = N
                counts = _sample_distribution(ps, sample_error,
                                              nWeightedSamples, rndm)
                if s not in count_lists: count_lists[s] = []
                count_lists[s].append(counts)

        if times is None:
            for s, counts_list in count_lists.items():
                for counts_dict in counts_list:
                    dataset.add_count_dict(
                        s, counts_dict, record_zero_counts=record_zero_counts)
        else:
            for s, counts_list in count_lists.items():
                dataset.add_series_data(s,
                                        counts_list,
                                        times,
                                        record_zero_counts=record_zero_counts)

        dataset.done_adding_data()

    if comm is not None:  # broadcast to non-root procs
        dataset = comm.bcast(dataset if (comm.Get_rank() == 0) else None,
                             root=0)

    return dataset
Beispiel #7
0
def create_mirror_circuit(circ, pspec, circ_type='clifford+zxzxz'):
    """

    circ_type : clifford+zxzxz, cz(theta)+zxzxz
    """

    n = circ.width
    d = circ.depth

    pauli_labels = ['I', 'X', 'Y', 'Z']
    qubits = circ.line_labels

    _, gate_inverse = pspec.compute_one_qubit_gate_relations()
    gate_inverse.update(
        pspec.compute_multiqubit_inversion_relations())  # add multiQ inverse

    assert (circ_type in (
        'clifford+zxzxz',
        'cz(theta)+zxzxz')), '{} not a valid circ_type!'.format(circ_type)

    def compute_gate_inverse(gate_label):

        if gate_label.name in gate_inverse.keys():
            return _lbl.Label(gate_inverse[gate_label.name], gate_label.qubits)
        else:
            if gate_label.name == 'Gzr' or gate_label.name == 'Gczr':
                return _lbl.Label(gate_label.name,
                                  gate_label.qubits,
                                  args=(str(-1 * float(gate_label.args[0])), ))
            else:
                raise ValueError("Cannot invert gate with name {}".format(
                    gate_label.name))

    srep_dict = _symp.compute_internal_gate_symplectic_representations(
        gllist=['I', 'X', 'Y', 'Z'])
    # the `callable` part is a workaround to remove gates with args, defined by functions.
    srep_dict.update(
        pspec.compute_clifford_symplectic_reps(
            tuple((gn for gn, u in pspec.gate_unitaries.items()
                   if not callable(u)))))

    if 'Gxpi2' in pspec.gate_names:
        xname = 'Gxpi2'
    elif 'Gc16' in pspec.gate_names:
        xname = 'Gc16'
    else:
        raise ValueError(
            ("There must be an X(pi/2) gate in the processor spec's gate set,"
             " and it must be called Gxpi2 or Gc16!"))

    assert('Gzr' in pspec.gate_names), \
        "There must be an Z(theta) gate in the processor spec's gate set, and it must be called Gzr!"
    zrotname = 'Gzr'

    if circ_type == 'cz(theta)+zxzxz':
        assert('Gczr' in pspec.gate_names), \
            "There must be an controlled-Z(theta) gate in the processor spec's gate set, and it must be called Gczr!"
        czrotname = 'Gczr'

    Xpi2layer = [_lbl.Label(xname, q) for q in qubits]

    #make an editable copy of the circuit to add the inverse on to
    c = circ.copy(editable=True)

    #build the inverse
    d_ind = 0
    while d_ind < d:
        layer = circ.layer(d - d_ind - 1)
        if len(layer) > 0 and layer[
                0].name == zrotname:  # ask if it's a Zrot layer.
            # It's necessary for the whole layer to have Zrot gates
            #get the entire arbitrary 1q unitaries: Zrot-Xpi/2-Zrot-Xpi/2-Zrot
            current_layers = circ[d - d_ind - 5:d - d_ind]
            #recompile inverse of current layer
            for i in range(n):
                if n == 1:
                    old_params = [(float(current_layers[0].args[0]),
                                   float(current_layers[2].args[0]),
                                   float(current_layers[4].args[0]))
                                  for i in range(n)]
                else:
                    old_params = [(float(current_layers[0][i].args[0]),
                                   float(current_layers[2][i].args[0]),
                                   float(current_layers[4][i].args[0]))
                                  for i in range(n)]
                layer_new_params = [
                    _comp.inv_recompile_unitary(*p) for p in old_params
                ]
                theta1_layer = [
                    _lbl.Label(zrotname,
                               qubits[i],
                               args=(str(layer_new_params[i][0]), ))
                    for i in range(len(layer_new_params))
                ]
                theta2_layer = [
                    _lbl.Label(zrotname,
                               qubits[i],
                               args=(str(layer_new_params[i][1]), ))
                    for i in range(len(layer_new_params))
                ]
                theta3_layer = [
                    _lbl.Label(zrotname,
                               qubits[i],
                               args=(str(layer_new_params[i][2]), ))
                    for i in range(len(layer_new_params))
                ]

            #add to mirror circuit
            c.append_circuit_inplace(
                _cir.Circuit([theta3_layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([Xpi2layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([theta2_layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([Xpi2layer], line_labels=circ.line_labels))
            c.append_circuit_inplace(
                _cir.Circuit([theta1_layer], line_labels=circ.line_labels))

            d_ind += 5

        else:
            inverse_layer = [
                compute_gate_inverse(gate_label) for gate_label in layer
            ]
            c.append_circuit_inplace(
                _cir.Circuit([inverse_layer], line_labels=circ.line_labels))
            d_ind += 1

    #now that we've built the simple mirror circuit, let's add pauli frame randomization
    d_ind = 0
    mc = []
    net_paulis = {q: 0 for q in qubits}
    d = c.depth
    correction_angles = {
        q: 0
        for q in qubits
    }  # corrections used in the cz(theta) case, which do nothing otherwise.

    while d_ind < d:
        layer = c.layer(d_ind)
        if len(layer) > 0 and layer[
                0].name == zrotname:  # ask if it's a Zrot layer.
            #It's necessary for the whole layer to have Zrot gates
            #if the layer is 1Q unitaries, pauli randomize
            current_layers = c[d_ind:d_ind + 5]

            #generate random pauli
            new_paulis = {q: _np.random.randint(0, 4) for q in qubits}
            new_paulis_as_layer = [
                _lbl.Label(pauli_labels[new_paulis[q]], q) for q in qubits
            ]

            net_paulis_as_layer = [
                _lbl.Label(pauli_labels[net_paulis[q]], q) for q in qubits
            ]
            #compute new net pauli based on previous pauli
            net_pauli_numbers = _symp.find_pauli_number(
                _symp.symplectic_rep_of_clifford_circuit(
                    _cir.Circuit(new_paulis_as_layer + net_paulis_as_layer,
                                 line_labels=circ.line_labels),
                    srep_dict=srep_dict)[1])

            # THIS WAS THE (THETA) VERSIONS
            #net_paulis_as_layer = [_lbl.Label(pauli_labels[net_paulis[q]], q) for q in qubits]
            #net_pauli_numbers = _symp.find_pauli_number(_symp.symplectic_rep_of_clifford_circuit(_cir.Circuit(
            #                                        new_paulis_as_layer+net_paulis_as_layer), pspec=pspec)[1])
            net_paulis = {qubits[i]: net_pauli_numbers[i] for i in range(n)}

            #depending on what the net pauli before the U gate is, might need to change parameters on the U gate
            # to commute the pauli through
            #recompile current layer to account for this and recompile with these paulis
            if n == 1:
                old_params_and_paulis = [
                    (float(current_layers[0].args[0]),
                     float(current_layers[2].args[0]),
                     float(current_layers[4].args[0]), net_paulis[qubits[i]],
                     new_paulis[qubits[i]]) for i in range(n)
                ]
            else:
                old_params_and_paulis = [
                    (float(current_layers[0][i].args[0]),
                     float(current_layers[2][i].args[0]),
                     float(current_layers[4][i].args[0]),
                     net_paulis[qubits[i]], new_paulis[qubits[i]])
                    for i in range(n)
                ]

            layer_new_params = [
                _comp.pauli_frame_randomize_unitary(*p)
                for p in old_params_and_paulis
            ]
            #recompile any zrotation corrections from the previous Czr into the first zr of this layer. This correction
            # will be zero if there are no Czr gates (when it's clifford+zxzxz)
            theta1_layer = [
                _lbl.Label(zrotname,
                           qubits[i],
                           args=(str(layer_new_params[i][0] +
                                     correction_angles[qubits[i]]), ))
                for i in range(len(layer_new_params))
            ]
            theta2_layer = [
                _lbl.Label(zrotname,
                           qubits[i],
                           args=(str(layer_new_params[i][1]), ))
                for i in range(len(layer_new_params))
            ]
            theta3_layer = [
                _lbl.Label(zrotname,
                           qubits[i],
                           args=(str(layer_new_params[i][2]), ))
                for i in range(len(layer_new_params))
            ]

            #add to mirror circuit
            mc.append([theta1_layer])
            mc.append([Xpi2layer])
            mc.append([theta2_layer])
            mc.append([Xpi2layer])
            mc.append([theta3_layer])

            d_ind += 5
            # reset the correction angles.
            correction_angles = {q: 0 for q in qubits}

        else:
            if circ_type == 'clifford+zxzxz':
                net_paulis_as_layer = [
                    _lbl.Label(pauli_labels[net_paulis[qubits[i]]], qubits[i])
                    for i in range(n)
                ]
                circ_sandwich = _cir.Circuit(
                    [layer, net_paulis_as_layer, layer],
                    line_labels=circ.line_labels)
                net_paulis = {
                    qubits[i]: pn
                    for i, pn in enumerate(
                        _symp.find_pauli_number(
                            _symp.symplectic_rep_of_clifford_circuit(
                                circ_sandwich, srep_dict=srep_dict)[1]))
                }
                mc.append(layer)
                #we need to account for how the net pauli changes when it gets passed through the clifford layers

            if circ_type == 'cz(theta)+zxzxz':
                quasi_inv_layer = []
                #recompile layer taking into acount paulis
                for g in layer:
                    if g.name == czrotname:
                        #get the qubits, figure out net pauli on those qubits
                        gate_qubits = g.qubits
                        net_paulis_for_gate = (net_paulis[gate_qubits[0]],
                                               net_paulis[gate_qubits[1]])
                        theta = float(g.args[0])
                        if ((net_paulis_for_gate[0] % 3 != 0
                             and net_paulis_for_gate[1] % 3 == 0)
                                or (net_paulis_for_gate[0] % 3 == 0
                                    and net_paulis_for_gate[1] % 3 != 0)):
                            theta *= -1
                        quasi_inv_layer.append(
                            _lbl.Label(czrotname,
                                       gate_qubits,
                                       args=(str(theta), )))
                        #for each X or Y, do a Zrotation by -theta on the other qubit after the 2Q gate.
                        for q in gate_qubits:
                            if net_paulis[q] == 1 or net_paulis[q] == 2:
                                for q2 in gate_qubits:
                                    if q2 != q:
                                        correction_angles[q2] += -1 * theta
                    else:
                        quasi_inv_layer.append(
                            _lbl.Label(compute_gate_inverse(g)))
                    #add to circuit
                    mc.append([quasi_inv_layer])

            #increment position in circuit
            d_ind += 1

    #update the target pauli
    #pauli_layer = [_lbl.Label(pauli_labels[net_paulis[i]], qubits[i]) for i in range(len(qubits))]
    # The version from (THETA)
    pauli_layer = [_lbl.Label(pauli_labels[net_paulis[q]], q) for q in qubits]
    conjugation_circ = _cir.Circuit([pauli_layer],
                                    line_labels=circ.line_labels)
    telp_s, telp_p = _symp.symplectic_rep_of_clifford_circuit(
        conjugation_circ, srep_dict=srep_dict)

    # Calculate the bit string that this mirror circuit should output, from the final telescoped Pauli.
    target_bitstring = ''.join(['1' if p == 2 else '0' for p in telp_p[n:]])
    mirror_circuit = _cir.Circuit(mc, line_labels=circ.line_labels)

    return mirror_circuit, target_bitstring
Beispiel #8
0
def _get_next_lbls(s, start, end, create_subcircuits, integerize_sslbls,
                   segment, interlayer_marker):
    if s[start] == "(":
        i = start + 1
        lbls_list = []
        interlayer_marker_inds = []
        while i < end and s[i] != ")":
            if s[i] == interlayer_marker:
                interlayer_marker_inds.append(len(lbls_list) - 1)
                i += 1
                if i == end or s[i] == ")": break

            lbls, i, segment, _ = _get_next_lbls(
                s, i, end, create_subcircuits, integerize_sslbls, segment,
                interlayer_marker
            )  # don't recursively look for interlayer markers
            lbls_list.extend(lbls)

        if i == end: raise ValueError("mismatched parenthesis")
        i += 1
        exponent, i = _parse_exponent(s, i, end)

        if exponent != 1 and len(interlayer_marker_inds) > 0:
            if exponent == 0: interlayer_marker_inds = ()
            else:  # exponent > 1
                base_marker_inds = interlayer_marker_inds[:]  # a new list
                for k in range(1, exponent):
                    offset = len(lbls_list) * k
                    interlayer_marker_inds.extend(
                        map(lambda x: x + offset, base_marker_inds))

        if create_subcircuits:
            if len(lbls_list) == 0:  # special case of {}^power => remain empty
                return [], i, segment, ()
            else:
                tmp = _lbl.Label(
                    lbls_list
                )  # just for total sslbs - should probably do something faster
                return [
                    _lbl.CircuitLabel('', lbls_list, tmp.sslbls, exponent)
                ], i, segment, ()
        else:
            return lbls_list * exponent, i, segment, interlayer_marker_inds

    elif s[start] == "[":  # layer
        i = start + 1
        lbls_list = []
        while i < end and s[i] != "]":
            lbls, i, segment, _ = _get_next_lbls(
                s, i, end, create_subcircuits, integerize_sslbls, segment,
                interlayer_marker)  # but don't actually look for marker
            lbls_list.extend(lbls)
        if i == end: raise ValueError("mismatched parenthesis")
        i += 1
        exponent, i = _parse_exponent(s, i, end)

        if len(lbls_list) == 0:
            to_exponentiate = _lbl.LabelTupTup(())
        elif len(lbls_list) > 1:
            time = max([l.time for l in lbls_list])
            # create a layer label - a label of the labels within square brackets
            to_exponentiate = _lbl.LabelTupTup(tuple(lbls_list)) if (time == 0.0) \
                else _lbl.LabelTupTupWithTime(tuple(lbls_list), time)
        else:
            to_exponentiate = lbls_list[0]
        return [to_exponentiate] * exponent, i, segment, ()

    else:
        lbls, i, segment = _get_next_simple_lbl(s, start, end,
                                                integerize_sslbls, segment)
        exponent, i = _parse_exponent(s, i, end)
        return lbls * exponent, i, segment, ()