Skip to content

qubic.counts

CircuitCounts

Object for storing shot count results (i.e. number of '000', '100', etc) from a batch of experiments. Intended to only store counts from a single circuit, but possibly with multiple reads.

CircuitCounts are summed along the nshots dimension, so bitstring count values have shape (reads_per_shot,)

The keys of count_dict and bitstring_dict have results in the same order as qubits.

Attributes:

Name Type Description
qubits List[str]

list of qubits in dataset

count_dict Dict[Tuple, int]

dictionary of counts indexed by bitstring formatted as tuple of sequential gmm labels. Tuple is indexed in the same order as qubits

bitstring_dict Dict[str, int]

dictionary of counts indexed by bitstring literal

TODO

add heralded bitstring dict

Source code in qubic/counts.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class CircuitCounts:
    """
    Object for storing shot count results (i.e. number of '000', '100', etc) from a batch of 
    experiments. Intended to only store counts from a single circuit, but possibly with multiple
    reads.

    CircuitCounts are summed along the nshots dimension, so bitstring count values have shape 
    (reads_per_shot,)

    The keys of `count_dict` and `bitstring_dict` have results in the same order as `qubits`.

    Attributes
    ----------
    qubits: List[str]
        list of qubits in dataset
    count_dict: Dict[Tuple, int] 
        dictionary of counts indexed by bitstring formatted as tuple of sequential gmm labels.
        Tuple is indexed in the same order as `qubits`
    bitstring_dict: Dict[str, int]
        dictionary of counts indexed by bitstring literal

    TODO:
        add heralded bitstring dict
    """

    def __init__(self, shot_dict: Dict[str, np.ndarray], gmm_labels: list = [0, 1]):
        """
        Parameters
        ----------
        shot_dict : Dict[np.ndarray]
            dictionary of classified shots, indexed by qubit
            values have shape (nshots, reads_per_shot)

        gmm_labels : list
            list of possible shot classification values
        """
        self.qubits = sorted(shot_dict.keys())
        self.shot_dict = OrderedDict()
        for qubit in self.qubits:
            self.shot_dict[qubit] = shot_dict[qubit]

        self.bit_tuples = [bittuple for bittuple in itertools.product(*[gmm_labels for i in range(len(self.qubits))])]

        self._generate_counts()

        self._bitstring_dict = None

    def _generate_counts(self):
        count_dict = {}
        shot_array = np.array([shots for shots in self.shot_dict.values()]) #this should have dims (nqubits, nshots, reads_per_shot)
        for bit_tuple in self.bit_tuples:
            #shape (n_circuits, nshots, reads_per_shot); i.e. does this measurement satisfy the current bit tuple?
            bitstring_sat_mask = np.asarray([c for c in (shot_array[i] == bit_tuple[i] for i in range(len(self.qubits)))])
            bitstring_sat = np.prod(bitstring_sat_mask, axis=0) 
            count_dict[bit_tuple] = np.sum(bitstring_sat, axis=0) #sum over nshots, shape is (reads_per_shot,)

        self.count_dict = count_dict

    @property 
    def bitstring_dict(self):
        """
        lazy generation + store
        """
        if self._bitstring_dict is None:
            self._bitstring_dict = {}
            for bit_tuple in self.bit_tuples:
                bitstring = ''.join([str(bit) for bit in bit_tuple])
                self._bitstring_dict[bitstring] = self.count_dict[bit_tuple]

        return self._bitstring_dict

    def __str__(self):
        return str(self.bitstring_dict)

    def __repr__(self):
        return f'CircuitCounts(qubits: {self.qubits}, counts: {self.count_dict})'

bitstring_dict property

lazy generation + store

__init__(shot_dict, gmm_labels=[0, 1])

Parameters:

Name Type Description Default
shot_dict Dict[ndarray]

dictionary of classified shots, indexed by qubit values have shape (nshots, reads_per_shot)

required
gmm_labels list

list of possible shot classification values

[0, 1]
Source code in qubic/counts.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def __init__(self, shot_dict: Dict[str, np.ndarray], gmm_labels: list = [0, 1]):
    """
    Parameters
    ----------
    shot_dict : Dict[np.ndarray]
        dictionary of classified shots, indexed by qubit
        values have shape (nshots, reads_per_shot)

    gmm_labels : list
        list of possible shot classification values
    """
    self.qubits = sorted(shot_dict.keys())
    self.shot_dict = OrderedDict()
    for qubit in self.qubits:
        self.shot_dict[qubit] = shot_dict[qubit]

    self.bit_tuples = [bittuple for bittuple in itertools.product(*[gmm_labels for i in range(len(self.qubits))])]

    self._generate_counts()

    self._bitstring_dict = None

shots_to_batchcounts(shot_dict, gmm_labels=[0, 1])

Given a dictionary of classified shots from a batch, compute bitstring results (CircuitCounts) for each circuit.

Parameters:

Name Type Description Default
shot_dict Dict[ndarray]

dictionary of classified shots, indexed by qubit values have shape (n_circuits, n_shots, reads_per_shot)

required
gmm_labels list

list of possible shot classification values

[0, 1]

Returns:

Type Description
List[CircuitCounts]

list of CircuitCounts objects, one per circuit in batch

Source code in qubic/counts.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def shots_to_batchcounts(shot_dict: Dict[str, np.ndarray], gmm_labels: List = [0, 1]) -> List[CircuitCounts]:
    """
    Given a dictionary of classified shots from a batch, compute bitstring results 
    (`CircuitCounts`) for each circuit.

    Parameters
    ----------
    shot_dict : Dict[np.ndarray]
        dictionary of classified shots, indexed by qubit
        values have shape (n_circuits, n_shots, reads_per_shot)
    gmm_labels : list
        list of possible shot classification values

    Returns
    -------
        List[CircuitCounts]
            list of CircuitCounts objects, one per circuit in batch
    """
    n_circuits = len(list(shot_dict.values())[0])
    batched_counts = []

    for i in range(n_circuits):
        shot_dict_i = {}
        for qubit in shot_dict.keys():
            if not np.any(np.isnan(shot_dict[qubit][i])):
                shot_dict_i[qubit] = shot_dict[qubit][i]
            else:
                if not np.all(np.isnan(shot_dict[qubit][i])):
                    logging.getLogger(__name__).warning(f'qubit: {qubit}, circuit: {i}: \
                            partial data found, skipping bitstring')

        batched_counts.append(CircuitCounts(shot_dict_i, gmm_labels))

    return batched_counts