/
simple_est.py
260 lines (220 loc) · 9.04 KB
/
simple_est.py
1
2
3
4
5
6
7
8
9
10
11
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
88
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#!/usr/bin/python
# -*- coding: utf-8 -*-
##
# simple_est.py: Simplified estimation functions for common experiments.
##
# © 2017, Chris Ferrie (csferrie@gmail.com) and
# Christopher Granade (cgranade@cgranade.com).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
##
## FEATURES ###################################################################
from __future__ import absolute_import
from __future__ import division
## EXPORTS ###################################################################
__all__ = [
'simple_est_prec',
'simple_est_rb',
# TODO:
# 'simple_est_rabi'
]
## IMPORTS ###################################################################
import numpy as np
from qinfer.smc import SMCUpdater
from qinfer.test_models import SimplePrecessionModel
from qinfer.rb import RandomizedBenchmarkingModel
from qinfer.derived_models import BinomialModel
from qinfer.distributions import UniformDistribution, PostselectedDistribution
from qinfer.resamplers import LiuWestResampler
# We want to be able to support Pandas without requiring it, so a conditional
# import is required.
try:
import pandas as pd
except:
pd = None
## FUNCTIONS #################################################################
def data_to_params(data,
expparams_dtype,
col_outcomes=(0, 'counts'),
cols_expparams=None
):
"""
Given data as a NumPy array, separates out each column either as
the outcomes, or as a field of an expparams array. Columns may be specified
either as indices into a two-axis scalar array, or as field names for a one-axis
record array.
Since scalar arrays are homogenous in type, this may result in loss of precision
due to casting between data types.
"""
BY_IDX, BY_NAME = range(2)
is_exp_scalar = np.issctype(expparams_dtype)
is_data_scalar = np.issctype(data.dtype) and not data.dtype.fields
s_ = (
(lambda idx: np.s_[..., idx[BY_IDX]])
if is_data_scalar else
(lambda idx: np.s_[idx[BY_NAME]])
)
outcomes = data[s_(col_outcomes)].astype(int)
# mk new slicer t
expparams = np.empty(outcomes.shape, dtype=expparams_dtype)
if is_exp_scalar:
expparams[:] = data[s_(cols_expparams)]
else:
for expparams_key, column in cols_expparams.items():
expparams[expparams_key] = data[s_(column)]
return outcomes, expparams
def load_data_or_txt(data, dtype):
if isinstance(data, np.ndarray):
return data
elif pd is not None and isinstance(data, pd.DataFrame):
return data.to_records(index=False)
elif hasattr(data, 'read') or isinstance(data, str):
data = np.loadtxt(data, dtype=dtype, delimiter=',')
return data
else:
raise TypeError("Expected a filename, an array or a file-like object.")
def do_update(model, n_particles, prior, outcomes, expparams, return_all, resampler=None):
updater = SMCUpdater(model, n_particles, prior,
resampler=resampler
)
updater.batch_update(outcomes, expparams, resample_interval=1)
mean = updater.est_mean()
cov = updater.est_covariance_mtx()
if model.n_modelparams == 1:
mean = mean[0]
cov = cov[0, 0]
if not return_all:
return mean, cov
else:
return mean, cov, {
'updater': updater
}
def simple_est_prec(data, freq_min=0.0, freq_max=1.0, n_particles=6000, return_all=False):
"""
Estimates a simple precession (cos²) from experimental data.
Note that this model is mainly for testing purposes, as it does not
consider the phase or amplitude of precession, leaving only the frequency.
:param data: Data to be used in estimating the precession frequency.
:type data: see :ref:`simple_est_data_arg`
:param float freq_min: The minimum feasible frequency to consider.
:param float freq_max: The maximum feasible frequency to consider.
:param int n_particles: The number of particles to be used in estimating
the precession frequency.
:param bool return_all: Controls whether additional return
values are provided, such as the updater.
:column counts (int): How many counts were observed at the sampled
time.
:column t (float): The evolutions time at which the samples
were collected.
:column n_shots (int): How many samples were collected at the
given evolution time.
:return mean: Bayesian mean estimator for the precession frequency.
:return var: Variance of the final posterior over frequency.
:return extra: See :ref:`simple_est_extra_return`. Only returned
if ``return_all`` is `True`.
"""
model = BinomialModel(SimplePrecessionModel(freq_min))
prior = UniformDistribution([0, freq_max])
data = load_data_or_txt(data, [
('counts', 'uint'),
('t', float),
('n_shots', 'uint')
])
outcomes, expparams = data_to_params(data,
model.expparams_dtype,
cols_expparams={
'x': (1, 't'),
'n_meas': (2, 'n_shots')
}
)
return do_update(
model, n_particles, prior, outcomes, expparams,
return_all
)
def simple_est_rb(data, interleaved=False, p_min=0.0, p_max=1.0, n_particles=8000, return_all=False):
r"""
Estimates the fidelity of a gateset from a standard or interleaved randomized benchmarking
experiment.
:param data: Data to be used in estimating the gateset fidelity.
:type data: see :ref:`simple_est_data_arg`
:param float p_min: Minimum value of the parameter :math:`p`
to consider feasible.
:param float p_max: Minimum value of the parameter :math:`p`
to consider feasible.
:param int n_particles: The number of particles to be used in estimating
the randomized benchmarking model.
:param bool return_all: Controls whether additional return
values are provided, such as the updater.
:column counts (int): How many sequences of length :math:`m` were observed to
survive.
:column m (int): How many gates were used for sequences in this row of the data.
:column n_shots (int): How many different sequences of length :math:`m`
were measured.
:column reference (bool): `True` if this row represents reference sequences, or
`False` if the gate of interest is interleaved. Note that this column is omitted
if ``interleaved`` is `False`.
:return mean: Bayesian mean estimator for the model vector
:math:`(p, A, B)`, or :math:`(\tilde{p}, p_{\text{ref}}, A, B)`
for the interleaved case.
:return var: Variance of the final posterior over RB model vectors.
:return extra: See :ref:`simple_est_extra_return`. Only returned
if ``return_all`` is `True`.
"""
model = BinomialModel(RandomizedBenchmarkingModel(interleaved=interleaved))
prior = PostselectedDistribution(UniformDistribution([
[p_min, p_max],
[0, 1],
[0, 1]
] if not interleaved else [
[p_min, p_max],
[p_min, p_max],
[0, 1],
[0, 1]
]),
model
)
data = load_data_or_txt(data, [
('counts', 'uint'),
('m', 'uint'),
('n_shots', 'uint')
] + ([
('reference', 'uint')
] if interleaved else []))
cols_expparams = {
'm': (1, 'm'),
'n_meas': (2, 'n_shots')
}
if interleaved:
cols_expparams['reference'] = (3, 'reference')
outcomes, expparams = data_to_params(data,
model.expparams_dtype,
cols_expparams=cols_expparams
)
return do_update(
model, n_particles, prior, outcomes, expparams,
return_all
)