pyGSTi 0.9.12.1__cp38-cp38-win_amd64.whl → 0.9.13__cp38-cp38-win_amd64.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (221) hide show
  1. pyGSTi-0.9.13.dist-info/METADATA +185 -0
  2. {pyGSTi-0.9.12.1.dist-info → pyGSTi-0.9.13.dist-info}/RECORD +207 -217
  3. {pyGSTi-0.9.12.1.dist-info → pyGSTi-0.9.13.dist-info}/WHEEL +1 -1
  4. pygsti/_version.py +2 -2
  5. pygsti/algorithms/contract.py +1 -1
  6. pygsti/algorithms/core.py +42 -28
  7. pygsti/algorithms/fiducialselection.py +17 -8
  8. pygsti/algorithms/gaugeopt.py +2 -2
  9. pygsti/algorithms/germselection.py +87 -77
  10. pygsti/algorithms/mirroring.py +0 -388
  11. pygsti/algorithms/randomcircuit.py +165 -1333
  12. pygsti/algorithms/rbfit.py +0 -234
  13. pygsti/baseobjs/basis.py +94 -396
  14. pygsti/baseobjs/errorgenbasis.py +0 -132
  15. pygsti/baseobjs/errorgenspace.py +0 -10
  16. pygsti/baseobjs/label.py +52 -168
  17. pygsti/baseobjs/opcalc/fastopcalc.cp38-win_amd64.pyd +0 -0
  18. pygsti/baseobjs/opcalc/fastopcalc.pyx +2 -2
  19. pygsti/baseobjs/polynomial.py +13 -595
  20. pygsti/baseobjs/statespace.py +1 -0
  21. pygsti/circuits/__init__.py +1 -1
  22. pygsti/circuits/circuit.py +682 -505
  23. pygsti/circuits/circuitconstruction.py +0 -4
  24. pygsti/circuits/circuitlist.py +47 -5
  25. pygsti/circuits/circuitparser/__init__.py +8 -8
  26. pygsti/circuits/circuitparser/fastcircuitparser.cp38-win_amd64.pyd +0 -0
  27. pygsti/circuits/circuitstructure.py +3 -3
  28. pygsti/circuits/cloudcircuitconstruction.py +1 -1
  29. pygsti/data/datacomparator.py +2 -7
  30. pygsti/data/dataset.py +46 -44
  31. pygsti/data/hypothesistest.py +0 -7
  32. pygsti/drivers/bootstrap.py +0 -49
  33. pygsti/drivers/longsequence.py +2 -1
  34. pygsti/evotypes/basereps_cython.cp38-win_amd64.pyd +0 -0
  35. pygsti/evotypes/chp/opreps.py +0 -61
  36. pygsti/evotypes/chp/statereps.py +0 -32
  37. pygsti/evotypes/densitymx/effectcreps.cpp +9 -10
  38. pygsti/evotypes/densitymx/effectreps.cp38-win_amd64.pyd +0 -0
  39. pygsti/evotypes/densitymx/effectreps.pyx +1 -1
  40. pygsti/evotypes/densitymx/opreps.cp38-win_amd64.pyd +0 -0
  41. pygsti/evotypes/densitymx/opreps.pyx +2 -2
  42. pygsti/evotypes/densitymx/statereps.cp38-win_amd64.pyd +0 -0
  43. pygsti/evotypes/densitymx/statereps.pyx +1 -1
  44. pygsti/evotypes/densitymx_slow/effectreps.py +7 -23
  45. pygsti/evotypes/densitymx_slow/opreps.py +16 -23
  46. pygsti/evotypes/densitymx_slow/statereps.py +10 -3
  47. pygsti/evotypes/evotype.py +39 -2
  48. pygsti/evotypes/stabilizer/effectreps.cp38-win_amd64.pyd +0 -0
  49. pygsti/evotypes/stabilizer/effectreps.pyx +0 -4
  50. pygsti/evotypes/stabilizer/opreps.cp38-win_amd64.pyd +0 -0
  51. pygsti/evotypes/stabilizer/opreps.pyx +0 -4
  52. pygsti/evotypes/stabilizer/statereps.cp38-win_amd64.pyd +0 -0
  53. pygsti/evotypes/stabilizer/statereps.pyx +1 -5
  54. pygsti/evotypes/stabilizer/termreps.cp38-win_amd64.pyd +0 -0
  55. pygsti/evotypes/stabilizer/termreps.pyx +0 -7
  56. pygsti/evotypes/stabilizer_slow/effectreps.py +0 -22
  57. pygsti/evotypes/stabilizer_slow/opreps.py +0 -4
  58. pygsti/evotypes/stabilizer_slow/statereps.py +0 -4
  59. pygsti/evotypes/statevec/effectreps.cp38-win_amd64.pyd +0 -0
  60. pygsti/evotypes/statevec/effectreps.pyx +1 -1
  61. pygsti/evotypes/statevec/opreps.cp38-win_amd64.pyd +0 -0
  62. pygsti/evotypes/statevec/opreps.pyx +2 -2
  63. pygsti/evotypes/statevec/statereps.cp38-win_amd64.pyd +0 -0
  64. pygsti/evotypes/statevec/statereps.pyx +1 -1
  65. pygsti/evotypes/statevec/termreps.cp38-win_amd64.pyd +0 -0
  66. pygsti/evotypes/statevec/termreps.pyx +0 -7
  67. pygsti/evotypes/statevec_slow/effectreps.py +0 -3
  68. pygsti/evotypes/statevec_slow/opreps.py +0 -5
  69. pygsti/extras/__init__.py +0 -1
  70. pygsti/extras/drift/stabilityanalyzer.py +3 -1
  71. pygsti/extras/interpygate/__init__.py +12 -0
  72. pygsti/extras/interpygate/core.py +0 -36
  73. pygsti/extras/interpygate/process_tomography.py +44 -10
  74. pygsti/extras/rpe/rpeconstruction.py +0 -2
  75. pygsti/forwardsims/__init__.py +1 -0
  76. pygsti/forwardsims/forwardsim.py +14 -55
  77. pygsti/forwardsims/mapforwardsim.py +69 -18
  78. pygsti/forwardsims/mapforwardsim_calc_densitymx.cp38-win_amd64.pyd +0 -0
  79. pygsti/forwardsims/mapforwardsim_calc_densitymx.pyx +65 -66
  80. pygsti/forwardsims/mapforwardsim_calc_generic.py +91 -13
  81. pygsti/forwardsims/matrixforwardsim.py +63 -15
  82. pygsti/forwardsims/termforwardsim.py +8 -110
  83. pygsti/forwardsims/termforwardsim_calc_stabilizer.cp38-win_amd64.pyd +0 -0
  84. pygsti/forwardsims/termforwardsim_calc_statevec.cp38-win_amd64.pyd +0 -0
  85. pygsti/forwardsims/termforwardsim_calc_statevec.pyx +0 -651
  86. pygsti/forwardsims/torchfwdsim.py +265 -0
  87. pygsti/forwardsims/weakforwardsim.py +2 -2
  88. pygsti/io/__init__.py +1 -2
  89. pygsti/io/mongodb.py +0 -2
  90. pygsti/io/stdinput.py +6 -22
  91. pygsti/layouts/copalayout.py +10 -12
  92. pygsti/layouts/distlayout.py +0 -40
  93. pygsti/layouts/maplayout.py +103 -25
  94. pygsti/layouts/matrixlayout.py +99 -60
  95. pygsti/layouts/prefixtable.py +1534 -52
  96. pygsti/layouts/termlayout.py +1 -1
  97. pygsti/modelmembers/instruments/instrument.py +3 -3
  98. pygsti/modelmembers/instruments/tpinstrument.py +2 -2
  99. pygsti/modelmembers/modelmember.py +0 -17
  100. pygsti/modelmembers/operations/__init__.py +2 -4
  101. pygsti/modelmembers/operations/affineshiftop.py +1 -0
  102. pygsti/modelmembers/operations/composederrorgen.py +1 -1
  103. pygsti/modelmembers/operations/composedop.py +1 -24
  104. pygsti/modelmembers/operations/denseop.py +5 -5
  105. pygsti/modelmembers/operations/eigpdenseop.py +2 -2
  106. pygsti/modelmembers/operations/embeddederrorgen.py +1 -1
  107. pygsti/modelmembers/operations/embeddedop.py +0 -1
  108. pygsti/modelmembers/operations/experrorgenop.py +2 -2
  109. pygsti/modelmembers/operations/fullarbitraryop.py +1 -0
  110. pygsti/modelmembers/operations/fullcptpop.py +2 -2
  111. pygsti/modelmembers/operations/fulltpop.py +28 -6
  112. pygsti/modelmembers/operations/fullunitaryop.py +5 -4
  113. pygsti/modelmembers/operations/lindbladcoefficients.py +93 -78
  114. pygsti/modelmembers/operations/lindbladerrorgen.py +268 -441
  115. pygsti/modelmembers/operations/linearop.py +7 -27
  116. pygsti/modelmembers/operations/opfactory.py +1 -1
  117. pygsti/modelmembers/operations/repeatedop.py +1 -24
  118. pygsti/modelmembers/operations/staticstdop.py +1 -1
  119. pygsti/modelmembers/povms/__init__.py +3 -3
  120. pygsti/modelmembers/povms/basepovm.py +7 -36
  121. pygsti/modelmembers/povms/complementeffect.py +4 -9
  122. pygsti/modelmembers/povms/composedeffect.py +0 -320
  123. pygsti/modelmembers/povms/computationaleffect.py +1 -1
  124. pygsti/modelmembers/povms/computationalpovm.py +3 -1
  125. pygsti/modelmembers/povms/effect.py +3 -5
  126. pygsti/modelmembers/povms/marginalizedpovm.py +0 -79
  127. pygsti/modelmembers/povms/tppovm.py +74 -2
  128. pygsti/modelmembers/states/__init__.py +2 -5
  129. pygsti/modelmembers/states/composedstate.py +0 -317
  130. pygsti/modelmembers/states/computationalstate.py +3 -3
  131. pygsti/modelmembers/states/cptpstate.py +4 -4
  132. pygsti/modelmembers/states/densestate.py +6 -4
  133. pygsti/modelmembers/states/fullpurestate.py +0 -24
  134. pygsti/modelmembers/states/purestate.py +1 -1
  135. pygsti/modelmembers/states/state.py +5 -6
  136. pygsti/modelmembers/states/tpstate.py +28 -10
  137. pygsti/modelmembers/term.py +3 -6
  138. pygsti/modelmembers/torchable.py +50 -0
  139. pygsti/modelpacks/_modelpack.py +1 -1
  140. pygsti/modelpacks/smq1Q_ZN.py +3 -1
  141. pygsti/modelpacks/smq2Q_XXYYII.py +2 -1
  142. pygsti/modelpacks/smq2Q_XY.py +3 -3
  143. pygsti/modelpacks/smq2Q_XYI.py +2 -2
  144. pygsti/modelpacks/smq2Q_XYICNOT.py +3 -3
  145. pygsti/modelpacks/smq2Q_XYICPHASE.py +3 -3
  146. pygsti/modelpacks/smq2Q_XYXX.py +1 -1
  147. pygsti/modelpacks/smq2Q_XYZICNOT.py +3 -3
  148. pygsti/modelpacks/smq2Q_XYZZ.py +1 -1
  149. pygsti/modelpacks/stdtarget.py +0 -121
  150. pygsti/models/cloudnoisemodel.py +1 -2
  151. pygsti/models/explicitcalc.py +3 -3
  152. pygsti/models/explicitmodel.py +3 -13
  153. pygsti/models/fogistore.py +5 -3
  154. pygsti/models/localnoisemodel.py +1 -2
  155. pygsti/models/memberdict.py +0 -12
  156. pygsti/models/model.py +800 -65
  157. pygsti/models/modelconstruction.py +4 -4
  158. pygsti/models/modelnoise.py +2 -2
  159. pygsti/models/modelparaminterposer.py +1 -1
  160. pygsti/models/oplessmodel.py +1 -1
  161. pygsti/models/qutrit.py +15 -14
  162. pygsti/objectivefns/objectivefns.py +73 -138
  163. pygsti/objectivefns/wildcardbudget.py +2 -7
  164. pygsti/optimize/__init__.py +1 -0
  165. pygsti/optimize/arraysinterface.py +28 -0
  166. pygsti/optimize/customcg.py +0 -12
  167. pygsti/optimize/customlm.py +129 -323
  168. pygsti/optimize/customsolve.py +2 -2
  169. pygsti/optimize/optimize.py +0 -84
  170. pygsti/optimize/simplerlm.py +841 -0
  171. pygsti/optimize/wildcardopt.py +19 -598
  172. pygsti/protocols/confidenceregionfactory.py +28 -14
  173. pygsti/protocols/estimate.py +31 -14
  174. pygsti/protocols/gst.py +142 -68
  175. pygsti/protocols/modeltest.py +6 -10
  176. pygsti/protocols/protocol.py +9 -37
  177. pygsti/protocols/rb.py +450 -79
  178. pygsti/protocols/treenode.py +8 -2
  179. pygsti/protocols/vb.py +108 -206
  180. pygsti/protocols/vbdataframe.py +1 -1
  181. pygsti/report/factory.py +0 -15
  182. pygsti/report/fogidiagram.py +1 -17
  183. pygsti/report/modelfunction.py +12 -3
  184. pygsti/report/mpl_colormaps.py +1 -1
  185. pygsti/report/plothelpers.py +8 -2
  186. pygsti/report/reportables.py +41 -37
  187. pygsti/report/templates/offline/pygsti_dashboard.css +6 -0
  188. pygsti/report/templates/offline/pygsti_dashboard.js +12 -0
  189. pygsti/report/workspace.py +2 -14
  190. pygsti/report/workspaceplots.py +326 -504
  191. pygsti/tools/basistools.py +9 -36
  192. pygsti/tools/edesigntools.py +124 -96
  193. pygsti/tools/fastcalc.cp38-win_amd64.pyd +0 -0
  194. pygsti/tools/fastcalc.pyx +35 -81
  195. pygsti/tools/internalgates.py +151 -15
  196. pygsti/tools/jamiolkowski.py +5 -5
  197. pygsti/tools/lindbladtools.py +19 -11
  198. pygsti/tools/listtools.py +0 -114
  199. pygsti/tools/matrixmod2.py +1 -1
  200. pygsti/tools/matrixtools.py +173 -339
  201. pygsti/tools/nameddict.py +1 -1
  202. pygsti/tools/optools.py +154 -88
  203. pygsti/tools/pdftools.py +0 -25
  204. pygsti/tools/rbtheory.py +3 -320
  205. pygsti/tools/slicetools.py +64 -12
  206. pyGSTi-0.9.12.1.dist-info/METADATA +0 -155
  207. pygsti/algorithms/directx.py +0 -711
  208. pygsti/evotypes/qibo/__init__.py +0 -33
  209. pygsti/evotypes/qibo/effectreps.py +0 -78
  210. pygsti/evotypes/qibo/opreps.py +0 -376
  211. pygsti/evotypes/qibo/povmreps.py +0 -98
  212. pygsti/evotypes/qibo/statereps.py +0 -174
  213. pygsti/extras/rb/__init__.py +0 -13
  214. pygsti/extras/rb/benchmarker.py +0 -957
  215. pygsti/extras/rb/dataset.py +0 -378
  216. pygsti/extras/rb/io.py +0 -814
  217. pygsti/extras/rb/simulate.py +0 -1020
  218. pygsti/io/legacyio.py +0 -385
  219. pygsti/modelmembers/povms/denseeffect.py +0 -142
  220. {pyGSTi-0.9.12.1.dist-info → pyGSTi-0.9.13.dist-info}/LICENSE +0 -0
  221. {pyGSTi-0.9.12.1.dist-info → pyGSTi-0.9.13.dist-info}/top_level.txt +0 -0
@@ -189,9 +189,9 @@ def change_basis(mx, from_basis, to_basis):
189
189
  if isMx:
190
190
  # want ret = toMx.dot( _np.dot(mx, fromMx)) but need to deal
191
191
  # with some/all args being sparse:
192
- ret = _mt.safe_dot(toMx, _mt.safe_dot(mx, fromMx))
192
+ ret = toMx @ (mx @ fromMx)
193
193
  else: # isVec
194
- ret = _mt.safe_dot(toMx, mx)
194
+ ret = toMx @ mx
195
195
 
196
196
  if not to_basis.real:
197
197
  return ret
@@ -199,38 +199,7 @@ def change_basis(mx, from_basis, to_basis):
199
199
  if _mt.safe_norm(ret, 'imag') > 1e-8:
200
200
  raise ValueError("Array has non-zero imaginary part (%g) after basis change (%s to %s)!\n%s" %
201
201
  (_mt.safe_norm(ret, 'imag'), from_basis, to_basis, ret))
202
- return _mt.safe_real(ret)
203
-
204
- #def transform_matrix(from_basis, to_basis, dim_or_block_dims=None, sparse=False):
205
- # '''
206
- # Compute the transformation matrix between two bases
207
- #
208
- # Parameters
209
- # ----------
210
- # from_basis : Basis or str
211
- # Basis being converted from
212
- #
213
- # to_basis : Basis or str
214
- # Basis being converted to
215
- #
216
- # dim_or_block_dims : int or list of ints
217
- # if strings provided as bases, the dimension of basis to use.
218
- #
219
- # sparse : bool, optional
220
- # Whether to construct a sparse or dense transform matrix
221
- # when this isn't specified already by `from_basis` or
222
- # `to_basis` (e.g. when these are both strings).
223
- #
224
- # Returns
225
- # -------
226
- # Basis
227
- # the composite basis created
228
- # '''
229
- # if dim_or_block_dims is None:
230
- # assert isinstance(from_basis, Basis)
231
- # else:
232
- # from_basis = Basis(from_basis, dim_or_block_dims, sparse=sparse)
233
- # return from_basis.transform_matrix(to_basis)
202
+ return ret.real
234
203
 
235
204
 
236
205
  def create_basis_pair(mx, from_basis, to_basis):
@@ -507,7 +476,11 @@ def vec_to_stdmx(v, basis, keep_complex=False):
507
476
  """
508
477
  if not isinstance(basis, _basis.Basis):
509
478
  basis = _basis.BuiltinBasis(basis, len(v))
479
+ v = v.ravel()
510
480
  ret = _np.zeros(basis.elshape, 'complex')
481
+ if v.ndim > 1:
482
+ assert v.size == v.shape[0]
483
+ v = v.ravel()
511
484
  for i, mx in enumerate(basis.elements):
512
485
  if keep_complex:
513
486
  ret += v[i] * mx
@@ -549,9 +522,9 @@ def stdmx_to_vec(m, basis):
549
522
  v = _np.empty((basis.size, 1))
550
523
  for i, mx in enumerate(basis.elements):
551
524
  if basis.real:
552
- v[i, 0] = _np.real(_mt.trace(_np.dot(mx, m)))
525
+ v[i, 0] = _np.real(_np.vdot(mx, m))
553
526
  else:
554
- v[i, 0] = _np.real_if_close(_mt.trace(_np.dot(mx, m)))
527
+ v[i, 0] = _np.real_if_close(_np.vdot(mx, m))
555
528
  return v
556
529
 
557
530
 
@@ -122,7 +122,7 @@ def calculate_edesign_estimated_runtime(edesign, gate_time_dict=None, gate_time_
122
122
  return total_circ_time + total_upload_time
123
123
 
124
124
 
125
- def calculate_fisher_information_per_circuit(regularized_model, circuits, approx=False, verbosity=1, comm = None, mem_limit = None):
125
+ def calculate_fisher_information_per_circuit(model, circuits, approx=False, regularization=1e-8, verbosity=1, comm=None, mem_limit=None):
126
126
  """Helper function to calculate all Fisher information terms for each circuit.
127
127
 
128
128
  This function can be used to pre-generate a cache for the
@@ -131,10 +131,8 @@ def calculate_fisher_information_per_circuit(regularized_model, circuits, approx
131
131
 
132
132
  Parameters
133
133
  ----------
134
- regularized_model: OpModel
134
+ model: OpModel
135
135
  The model used to calculate the terms of the Fisher information matrix.
136
- This model must already be "regularized" such that there are no small probabilities,
137
- usually by adding a small amount of SPAM error.
138
136
 
139
137
  circuits: list
140
138
  List of circuits to compute Fisher information for.
@@ -142,7 +140,12 @@ def calculate_fisher_information_per_circuit(regularized_model, circuits, approx
142
140
  approx: bool, optional (default False)
143
141
  When set to true use the approximate fisher information where we drop the
144
142
  hessian term. Significantly faster to compute than when including the hessian.
145
-
143
+
144
+ regularization: float, optional (default 1e-8)
145
+ A regularization parameter used to set a minimum probability value for
146
+ circuits. This is needed to avoid division by zero problems in the fisher
147
+ information calculation.
148
+
146
149
  verbosity: int, optional (default 1)
147
150
  Used to control the level of output printed by a VerbosityPrinter object.
148
151
 
@@ -163,18 +166,28 @@ def calculate_fisher_information_per_circuit(regularized_model, circuits, approx
163
166
 
164
167
  printer = _baseobjs.VerbosityPrinter.create_printer(verbosity, comm)
165
168
 
166
- num_params = regularized_model.num_params
167
- outcomes = regularized_model.sim.probs(()).keys()
169
+ num_params = model.num_params
168
170
 
171
+ #pull out the outcomes for each circuit expanding out the instruments if needed.
172
+ expanded_circuit_dict_list = model.bulk_expand_instruments_and_separate_povm(circuits)
173
+ #data structure is awkward so massage this into a nicer format for our current use.
174
+ expanded_circuit_outcomes = [ [val for val in exp_outcome_dict.values()] for exp_outcome_dict in expanded_circuit_dict_list]
175
+ #create a dictionary with circuits as keys, and list of outcome keys as values.
176
+ outcomes = {}
177
+ for exp_ckt_outcomes, ckt in zip(expanded_circuit_outcomes, circuits):
178
+ #exp_ckt_outcomes will be a list of tuples whose entries are outcome label tuples.
179
+ #flatten this into a single list of outcome labels.
180
+ outcomes[ckt] = [outcome for outcome_tuple in exp_ckt_outcomes for outcome in outcome_tuple]
181
+
169
182
  resource_alloc = _baseobjs.ResourceAllocation(comm= comm, mem_limit = mem_limit)
170
183
 
171
184
  printer.log('Calculating Probabilities, Jacobians and Hessians (if not using approx FIM).', 3)
172
- ps = regularized_model.sim.bulk_probs(circuits, resource_alloc)
173
- js = regularized_model.sim.bulk_dprobs(circuits, resource_alloc)
185
+ ps = model.sim.bulk_probs(circuits, resource_alloc)
186
+ js = model.sim.bulk_dprobs(circuits, resource_alloc)
174
187
  #if approx is true we add in the hessian term as well.
175
188
  if not approx:
176
189
  printer.log('Calculating Hessians.', 3)
177
- hs = regularized_model.sim.bulk_hprobs(circuits, resource_alloc)
190
+ hs = model.sim.bulk_hprobs(circuits, resource_alloc)
178
191
 
179
192
  if comm is not None:
180
193
  #divide the job of doing the accumulation among the ranks:
@@ -230,15 +243,14 @@ def calculate_fisher_information_per_circuit(regularized_model, circuits, approx
230
243
  #now calculate the fisher information terms on each rank:
231
244
  printer.log('Distributed calculation of FIM.', 4)
232
245
  if approx:
233
- split_fisher_info_terms = accumulate_fim_matrix_per_circuit(split_circuit_list, num_params,
234
- outcomes, ps, js,
235
- printer,
236
- approx=True)
246
+ split_fisher_info_terms = _accumulate_fim_matrix_per_circuit(split_circuit_list, num_params,
247
+ outcomes, ps, js, printer,
248
+ approx=True, regularization=regularization)
237
249
  else:
238
- split_fisher_info_terms, total_hterm = accumulate_fim_matrix_per_circuit(split_circuit_list, num_params,
250
+ split_fisher_info_terms, total_hterm = _accumulate_fim_matrix_per_circuit(split_circuit_list, num_params,
239
251
  outcomes, ps, js,
240
- printer,
241
- hs, approx=False)
252
+ printer,hs, approx=False,
253
+ regularization=regularization)
242
254
 
243
255
  #gather these back onto rank 0.
244
256
  #This should return a list of dictionaries to rank 0.
@@ -294,15 +306,14 @@ def calculate_fisher_information_per_circuit(regularized_model, circuits, approx
294
306
  #otherwise do things without splitting up among multiple cores.
295
307
  else:
296
308
  if approx:
297
- fisher_info_terms = accumulate_fim_matrix_per_circuit(circuits, num_params,
298
- outcomes, ps, js,
299
- printer,
300
- approx=True)
309
+ fisher_info_terms = _accumulate_fim_matrix_per_circuit(circuits, num_params,
310
+ outcomes, ps, js, printer,
311
+ approx=True, regularization=regularization)
301
312
  else:
302
- fisher_info_terms, total_hterm = accumulate_fim_matrix_per_circuit(circuits, num_params,
313
+ fisher_info_terms, total_hterm = _accumulate_fim_matrix_per_circuit(circuits, num_params,
303
314
  outcomes, ps, js,
304
- printer,
305
- hs, approx=False)
315
+ printer, hs,
316
+ approx=False, regularization=regularization)
306
317
 
307
318
  fisher_info_terms = {ckt: fisher_info_terms[i,:,:] for i, ckt in enumerate(circuits)}
308
319
  if not approx:
@@ -315,13 +326,10 @@ def calculate_fisher_information_per_circuit(regularized_model, circuits, approx
315
326
 
316
327
 
317
328
  def calculate_fisher_information_matrix(model, circuits, num_shots=1, term_cache=None,
318
- regularize_spam=True, approx= False, mem_efficient_mode= False,
329
+ regularization=1e-8, approx= False, mem_efficient_mode= False,
319
330
  circuit_chunk_size = 100, verbosity=1, comm = None, mem_limit = None):
320
331
  """Calculate the Fisher information matrix for a set of circuits and a model.
321
332
 
322
- Note that the model should be regularized so that no probability should be very small
323
- for numerical stability. This is done by default for models with a dense SPAM parameterization,
324
- but must be done manually if this is not the case (e.g. CPTP parameterization).
325
333
 
326
334
  Parameters
327
335
  ----------
@@ -341,10 +349,10 @@ def calculate_fisher_information_matrix(model, circuits, num_shots=1, term_cache
341
349
  will be updated with any additional circuits that need to be calculated in the given
342
350
  circuit list.
343
351
 
344
- regularize_spam: bool
345
- If True, depolarizing SPAM noise is added to prevent 0 probabilities for numerical
346
- stability. Note that this may fail if the model does not have a dense SPAM
347
- paramerization. In that case, pass an already "regularized" model and set this to False.
352
+ regularization: float, optional (default 1e-8)
353
+ A regularization parameter used to set a minimum probability value for
354
+ circuits. This is needed to avoid division by zero problems in the fisher
355
+ information calculation.
348
356
 
349
357
  approx: bool, optional (default False)
350
358
  When set to true use the approximate fisher information where we drop the
@@ -377,11 +385,8 @@ def calculate_fisher_information_matrix(model, circuits, num_shots=1, term_cache
377
385
 
378
386
  printer = _baseobjs.VerbosityPrinter.create_printer(verbosity, comm)
379
387
 
380
- # Regularize model
381
- regularized_model = model.copy()
382
- if regularize_spam:
383
- regularized_model = regularized_model.depolarize(spam_noise=1e-3)
384
- num_params = regularized_model.num_params
388
+ model = model.copy()
389
+ num_params = model.num_params
385
390
 
386
391
  if isinstance(num_shots, dict):
387
392
  assert _np.all([c in num_shots for c in circuits]), \
@@ -400,11 +405,13 @@ def calculate_fisher_information_matrix(model, circuits, num_shots=1, term_cache
400
405
  #might also return hessian terms if approx is False, but we currently aren't using this in
401
406
  #this function.
402
407
  if approx:
403
- new_terms = calculate_fisher_information_per_circuit(regularized_model, needed_circuits,
404
- approx, verbosity=verbosity, comm=comm, mem_limit=mem_limit)
408
+ new_terms = calculate_fisher_information_per_circuit(model, needed_circuits,
409
+ approx, regularization,
410
+ verbosity=verbosity, comm=comm, mem_limit=mem_limit)
405
411
  else:
406
- new_terms, _ = calculate_fisher_information_per_circuit(regularized_model, needed_circuits,
407
- approx, verbosity=verbosity, comm=comm, mem_limit=mem_limit)
412
+ new_terms, _ = calculate_fisher_information_per_circuit(model, needed_circuits,
413
+ approx, regularization,
414
+ verbosity=verbosity, comm=comm, mem_limit=mem_limit)
408
415
  if comm is None or comm.Get_rank()==0:
409
416
  term_cache.update(new_terms)
410
417
 
@@ -435,11 +442,11 @@ def calculate_fisher_information_matrix(model, circuits, num_shots=1, term_cache
435
442
  printer.show_progress(iteration = i, total=len(chunked_circuit_lists), bar_length=50,
436
443
  suffix= f'Circuit chunk {i+1} out of {len(chunked_circuit_lists)}')
437
444
  if approx:
438
- fim_term_for_chunk = _calculate_fisher_information_per_chunk(regularized_model, ckt_chunk,
439
- approx, num_shots, verbosity=verbosity, comm=comm, mem_limit=mem_limit)
445
+ fim_term_for_chunk = _calculate_fisher_information_per_chunk(model, ckt_chunk,
446
+ approx, num_shots, regularization, verbosity=verbosity, comm=comm, mem_limit=mem_limit)
440
447
  else:
441
- fim_term_for_chunk, _ = _calculate_fisher_information_per_chunk(regularized_model, ckt_chunk,
442
- approx, num_shots, verbosity=verbosity, comm=comm, mem_limit=mem_limit)
448
+ fim_term_for_chunk, _ = _calculate_fisher_information_per_chunk(model, ckt_chunk,
449
+ approx, regularization, num_shots, verbosity=verbosity, comm=comm, mem_limit=mem_limit)
443
450
  # Collect all terms, do this on rank zero:
444
451
  if comm is None or comm.Get_rank() == 0:
445
452
  fisher_information += fim_term_for_chunk
@@ -455,7 +462,7 @@ def calculate_fisher_information_matrix(model, circuits, num_shots=1, term_cache
455
462
  return fisher_information
456
463
 
457
464
  def calculate_fisher_information_matrices_by_L(model, circuit_lists, Ls, num_shots=1, term_cache=None,
458
- regularize_spam=True, cumulative=True, approx = False,
465
+ regularization=1e-8, cumulative=True, approx = False,
459
466
  mem_efficient_mode= False, circuit_chunk_size = 100,
460
467
  verbosity= 1,
461
468
  comm = None, mem_limit = None):
@@ -484,10 +491,10 @@ def calculate_fisher_information_matrices_by_L(model, circuit_lists, Ls, num_sho
484
491
  will be updated with any additional circuits that need to be calculated in the given
485
492
  circuit list.
486
493
 
487
- regularize_spam: bool
488
- If True, depolarizing SPAM noise is added to prevent 0 probabilities for numerical
489
- stability. Note that this may fail if the model does not have a dense SPAM
490
- paramerization. In that case, pass an already "regularized" model and set this to False.
494
+ regularization: float, optional (default 1e-8)
495
+ A regularization parameter used to set a minimum probability value for
496
+ circuits. This is needed to avoid division by zero problems in the fisher
497
+ information calculation.
491
498
 
492
499
  cumulative: bool
493
500
  Whether to include Fisher information matrices for lower L (True) or not.
@@ -521,12 +528,8 @@ def calculate_fisher_information_matrices_by_L(model, circuit_lists, Ls, num_sho
521
528
  Dictionary with keys as circuit length L and value as Fisher information matrices
522
529
  """
523
530
 
524
- printer = _baseobjs.VerbosityPrinter.create_printer(verbosity, comm)
525
-
526
- # Regularize model
527
- regularized_model = model.copy()
528
- if regularize_spam:
529
- regularized_model = regularized_model.depolarize(spam_noise=1e-3)
531
+ printer = _baseobjs.VerbosityPrinter.create_printer(verbosity, comm)
532
+ model = model.copy()
530
533
 
531
534
  if isinstance(num_shots, dict):
532
535
  assert _np.all([c in num_shots for ckt_list in circuit_lists for c in ckt_list]), \
@@ -551,10 +554,10 @@ def calculate_fisher_information_matrices_by_L(model, circuit_lists, Ls, num_sho
551
554
  needed_circuits = [c for ckt_list in circuit_lists for c in ckt_list if c not in term_cache]
552
555
  if len(needed_circuits):
553
556
  if approx:
554
- new_terms = calculate_fisher_information_per_circuit(regularized_model, needed_circuits, approx, verbosity=verbosity,
557
+ new_terms = calculate_fisher_information_per_circuit(model, needed_circuits, approx, regularization, verbosity=verbosity,
555
558
  comm=comm, mem_limit=mem_limit)
556
559
  else:
557
- new_terms, _ = calculate_fisher_information_per_circuit(regularized_model, needed_circuits, approx, verbosity=verbosity,
560
+ new_terms, _ = calculate_fisher_information_per_circuit(model, needed_circuits, approx, regularization, verbosity=verbosity,
558
561
  comm=comm, mem_limit=mem_limit)
559
562
  if comm is None or comm.Get_rank()==0:
560
563
  term_cache.update(new_terms)
@@ -567,9 +570,9 @@ def calculate_fisher_information_matrices_by_L(model, circuit_lists, Ls, num_sho
567
570
 
568
571
  for i, (L, ckt_list) in enumerate(zip(Ls, unique_circuit_lists)):
569
572
  printer.log(f'Current length L={L}', 2)
570
- fisher_information_by_L[L] = calculate_fisher_information_matrix(regularized_model, ckt_list, num_shots,
571
- term_cache=term_cache, regularize_spam=False, verbosity=verbosity)
572
- if i!=0:
573
+ fisher_information_by_L[L] = calculate_fisher_information_matrix(model, ckt_list, num_shots,
574
+ term_cache=term_cache, regularization=regularization, verbosity=verbosity)
575
+ if i!=0 and cumulative:
573
576
  #Add previous iteration's FIM on rank 0 (on other ranks this is None which is why we don't do it there).
574
577
  fisher_information_by_L[L]=fisher_information_by_L[L] + fisher_information_by_L[Ls[i-1]]
575
578
 
@@ -583,21 +586,21 @@ def calculate_fisher_information_matrices_by_L(model, circuit_lists, Ls, num_sho
583
586
  fisher_information_by_L = {}
584
587
  for i, (L, ckt_list) in enumerate(zip(Ls, unique_circuit_lists)):
585
588
  printer.log(f'Current length L={L}',2)
586
- fisher_information_by_L[L] = calculate_fisher_information_matrix(regularized_model, ckt_list, num_shots,
587
- term_cache=None, regularize_spam=False,
588
- approx = approx,
589
+ fisher_information_by_L[L] = calculate_fisher_information_matrix(model, ckt_list, num_shots,
590
+ term_cache=None, approx = approx,
591
+ regularization=regularization,
589
592
  mem_efficient_mode=mem_efficient_mode,
590
593
  circuit_chunk_size = circuit_chunk_size,
591
594
  verbosity = verbosity,
592
595
  comm=comm, mem_limit=mem_limit)
593
- if i!=0 and (comm is None or comm.Get_rank()==0):
596
+ if i!=0 and (comm is None or comm.Get_rank()==0) and cumulative:
594
597
  #Add previous iteration's FIM on rank 0 (on other ranks this is None which is why we don't do it there).
595
598
  fisher_information_by_L[L]=fisher_information_by_L[L] + fisher_information_by_L[Ls[i-1]]
596
599
  #In memory efficient mode the fisher information is None on any rank other than 0 when using MPI.
597
600
  return fisher_information_by_L
598
601
 
599
602
  #Helper function for memory efficient MPI implementation that combines the contributions for each circuit chunk together more cleverly
600
- def _calculate_fisher_information_per_chunk(regularized_model, circuits, approx=False, num_shots=None, verbosity=1, comm = None, mem_limit = None):
603
+ def _calculate_fisher_information_per_chunk(model, circuits, approx=False, regularization=1e-8, num_shots=None, verbosity=1, comm = None, mem_limit = None):
601
604
  """Helper function to calculate all Fisher information terms for a chunk of circuits.
602
605
  Used primarily in memory efficient MPI implementation.
603
606
 
@@ -607,10 +610,8 @@ def _calculate_fisher_information_per_chunk(regularized_model, circuits, approx=
607
610
 
608
611
  Parameters
609
612
  ----------
610
- regularized_model: OpModel
613
+ model: OpModel
611
614
  The model used to calculate the terms of the Fisher information matrix.
612
- This model must already be "regularized" such that there are no small probabilities,
613
- usually by adding a small amount of SPAM error.
614
615
 
615
616
  circuits: list
616
617
  List of circuits to compute Fisher information for.
@@ -619,6 +620,11 @@ def _calculate_fisher_information_per_chunk(regularized_model, circuits, approx=
619
620
  When set to true use the approximate fisher information where we drop the
620
621
  hessian term. Significantly faster to compute than when including the hessian.
621
622
 
623
+ regularization: float, optional (default 1e-8)
624
+ A regularization parameter used to set a minimum probability value for
625
+ circuits. This is needed to avoid division by zero problems in the fisher
626
+ information calculation.
627
+
622
628
  num_shots : dict, optional (default None)
623
629
  A dictionary of per circuit shot counts. When None each circuit gets assigned 1 shot.
624
630
 
@@ -642,17 +648,27 @@ def _calculate_fisher_information_per_chunk(regularized_model, circuits, approx=
642
648
 
643
649
  printer = _baseobjs.VerbosityPrinter.create_printer(verbosity, comm)
644
650
 
645
- num_params = regularized_model.num_params
646
- outcomes = regularized_model.sim.probs(()).keys()
651
+ num_params = model.num_params
652
+
653
+ #pull out the outcomes for each circuit expanding out the instruments if needed.
654
+ expanded_circuit_dict_list = model.bulk_expand_instruments_and_separate_povm(circuits)
655
+ #data structure is awkward so massage this into a nicer format for our current use.
656
+ expanded_circuit_outcomes = [list(exp_outcome_dict.values())[0] for exp_outcome_dict in expanded_circuit_dict_list]
657
+ #create a dictionary with circuits as keys, and list of outcome keys as values.
658
+ outcomes = {}
659
+ for exp_ckt_outcomes, ckt in zip(expanded_circuit_outcomes, circuits):
660
+ #exp_ckt_outcomes will be a list of tuples whose entries are outcome label tuples.
661
+ #flatten this into a single list of outcome labels.
662
+ outcomes[ckt] = [outcome for outcome_tuple in exp_ckt_outcomes for outcome in outcome_tuple]
647
663
 
648
664
  resource_alloc = _baseobjs.ResourceAllocation(comm= comm, mem_limit = mem_limit)
649
665
 
650
666
  printer.log('Calculating Probabilities, Jacobians and Hessians (if not using approx FIM).', 3)
651
- ps = regularized_model.sim.bulk_probs(circuits, resource_alloc)
652
- js = regularized_model.sim.bulk_dprobs(circuits, resource_alloc)
667
+ ps = model.sim.bulk_probs(circuits, resource_alloc)
668
+ js = model.sim.bulk_dprobs(circuits, resource_alloc)
653
669
  #if approx is true we add in the hessian term as well.
654
670
  if not approx:
655
- hs = regularized_model.sim.bulk_hprobs(circuits, resource_alloc)
671
+ hs = model.sim.bulk_hprobs(circuits, resource_alloc)
656
672
 
657
673
  if comm is not None:
658
674
  #divide the job of doing the accumulation among the ranks:
@@ -708,15 +724,15 @@ def _calculate_fisher_information_per_chunk(regularized_model, circuits, approx=
708
724
  #now calculate the fisher information terms on each rank:
709
725
  printer.log('Distributed accumulation of FIM.', 3)
710
726
  if approx:
711
- split_fisher_info_terms = accumulate_fim_matrix(split_circuit_list, num_params,
727
+ split_fisher_info_terms = _accumulate_fim_matrix(split_circuit_list, num_params,
712
728
  num_shots, outcomes, ps, js,
713
- printer,
714
- hs=None, approx=True)
729
+ printer,hs=None, approx=True,
730
+ regularization=regularization)
715
731
  else:
716
- split_fisher_info_terms, split_total_hterm = accumulate_fim_matrix(split_circuit_list, num_params,
732
+ split_fisher_info_terms, split_total_hterm = _accumulate_fim_matrix(split_circuit_list, num_params,
717
733
  num_shots, outcomes, ps, js,
718
- printer,
719
- hs, approx=False)
734
+ printer, hs, approx=False,
735
+ regularization=regularization)
720
736
 
721
737
  if comm.Get_rank() == 0:
722
738
  #1D buffer long enough to hold every element, will then reshape this later.
@@ -750,20 +766,20 @@ def _calculate_fisher_information_per_chunk(regularized_model, circuits, approx=
750
766
  #otherwise do things without splitting up among multiple cores.
751
767
  else:
752
768
  if approx:
753
- fisher_info_term = accumulate_fim_matrix(circuits, num_params, num_shots, outcomes,
769
+ fisher_info_term = _accumulate_fim_matrix(circuits, num_params, num_shots, outcomes,
754
770
  ps, js, printer, hs=None,
755
- approx=True)
771
+ approx=True, regularization=regularization)
756
772
  else:
757
- fisher_info_term, total_hterm = accumulate_fim_matrix(circuits, num_params, num_shots, outcomes,
773
+ fisher_info_term, total_hterm = _accumulate_fim_matrix(circuits, num_params, num_shots, outcomes,
758
774
  ps, js, printer, hs,
759
- approx=False)
775
+ approx=False, regularization=regularization)
760
776
  if approx:
761
777
  return fisher_info_term
762
778
  else:
763
779
  return fisher_info_term, total_hterm
764
780
 
765
781
  #helper function for distribution using MPI
766
- def accumulate_fim_matrix(subcircuits, num_params, num_shots, outcomes, ps, js, printer, hs=None, approx=False):
782
+ def _accumulate_fim_matrix(subcircuits, num_params, num_shots, outcomes, ps, js, printer, hs=None, approx=False, regularization=1e-8):
767
783
  printer.log('Accumulating terms for per-circuit FIM.', 4)
768
784
  fisher_info_terms = _np.zeros([num_params, num_params], dtype = _np.double)
769
785
  if not approx:
@@ -775,26 +791,32 @@ def accumulate_fim_matrix(subcircuits, num_params, num_shots, outcomes, ps, js,
775
791
  else:
776
792
  num_shots_for_circuit=1
777
793
  p = ps[circuit]
794
+ #regularize any probabilities that are too small.
795
+ clipped_p = _np.clip(_np.fromiter(p.values(), dtype=_np.double), a_min=regularization, a_max=None)
796
+ #renormalize this vector (probably not necessary, but better to be safe).
797
+ renormalized_clipped_p = clipped_p/_np.linalg.norm(clipped_p)
798
+ regularized_p = {outcome_lbl: val for outcome_lbl, val in zip(p.keys(), renormalized_clipped_p)}
799
+
778
800
  j = js[circuit]
779
801
  if not approx:
780
802
  h = hs[circuit]
781
- for i, outcome in enumerate(outcomes):
803
+ for i, outcome in enumerate(outcomes[circuit]):
782
804
  if not approx:
783
- jvec = _np.sqrt(num_shots_for_circuit/p[outcome])*(j[outcome].reshape(num_params,1))
784
- fisher_info_terms +=_np.dot(jvec, jvec.T) - num_shots_for_circuit*h[outcome]
805
+ jvec = _np.sqrt(num_shots_for_circuit/regularized_p[outcome])*(j[outcome].reshape(num_params,1))
806
+ fisher_info_terms += jvec@jvec.T - num_shots_for_circuit*h[outcome]
785
807
  total_hterm += num_shots_for_circuit*h[outcome]
786
808
  else:
787
809
  #fisher_info_terms += _np.outer(j[outcome], j[outcome]) / p[outcome]
788
810
  #faster outer product
789
- jvec = _np.sqrt(num_shots_for_circuit/p[outcome])*(j[outcome].reshape(num_params,1))
790
- fisher_info_terms +=_np.dot(jvec, jvec.T)
811
+ jvec = _np.sqrt(num_shots_for_circuit/regularized_p[outcome])*(j[outcome].reshape(num_params,1))
812
+ fisher_info_terms += jvec@jvec.T
791
813
  if approx:
792
814
  return fisher_info_terms
793
815
  else:
794
816
  return fisher_info_terms, total_hterm
795
817
 
796
818
  #helper function for distribution using MPI
797
- def accumulate_fim_matrix_per_circuit(subcircuits, num_params, outcomes, ps, js, printer, hs=None, approx=False):
819
+ def _accumulate_fim_matrix_per_circuit(subcircuits, num_params, outcomes, ps, js, printer, hs=None, approx=False, regularization=1e-8):
798
820
  printer.log('Accumulating terms for per-circuit FIM.', 4)
799
821
  fisher_info_terms = _np.zeros([len(subcircuits),num_params, num_params])
800
822
  if not approx:
@@ -802,19 +824,25 @@ def accumulate_fim_matrix_per_circuit(subcircuits, num_params, outcomes, ps, js,
802
824
 
803
825
  for k, circuit in enumerate(subcircuits):
804
826
  p = ps[circuit]
827
+ #regularize any probabilities that are too small.
828
+ clipped_p = _np.clip(_np.fromiter(p.values(), dtype=_np.double), a_min=regularization, a_max=None)
829
+ #renormalize this vector (probably not necessary, but better to be safe).
830
+ renormalized_clipped_p = clipped_p/_np.linalg.norm(clipped_p)
831
+ regularized_p = {outcome_lbl: val for outcome_lbl, val in zip(p.keys(), renormalized_clipped_p)}
832
+
805
833
  j = js[circuit]
806
834
  if not approx:
807
835
  h = hs[circuit]
808
- for i, outcome in enumerate(outcomes):
836
+ for i, outcome in enumerate(outcomes[circuit]):
809
837
  if not approx:
810
- jvec = (1/_np.sqrt(p[outcome]))*(j[outcome].reshape(num_params,1))
811
- fisher_info_terms[k,:,:] +=_np.dot(jvec, jvec.T) - h[outcome]
838
+ jvec = (1/_np.sqrt(regularized_p[outcome]))*(j[outcome].reshape(num_params,1))
839
+ fisher_info_terms[k,:,:] += jvec@jvec.T - h[outcome]
812
840
  total_hterm[k,:,:] += h[outcome]
813
841
  else:
814
842
  #fisher_info_terms[circuit] += _np.outer(j[outcome], j[outcome]) / p[outcome]
815
843
  #faster outer product
816
- jvec = (1/_np.sqrt(p[outcome]))*(j[outcome].reshape(num_params,1))
817
- fisher_info_terms[k,:,:] +=_np.dot(jvec, jvec.T)
844
+ jvec = (1/_np.sqrt(regularized_p[outcome]))*(j[outcome].reshape(num_params,1))
845
+ fisher_info_terms[k,:,:] += jvec@jvec.T
818
846
  if approx:
819
847
  return fisher_info_terms
820
848
  else:
Binary file