pyGSTi 0.9.12__cp310-cp310-win_amd64.whl → 0.9.13__cp310-cp310-win_amd64.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (225) hide show
  1. pyGSTi-0.9.13.dist-info/METADATA +197 -0
  2. {pyGSTi-0.9.12.dist-info → pyGSTi-0.9.13.dist-info}/RECORD +211 -220
  3. {pyGSTi-0.9.12.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 +62 -35
  7. pygsti/algorithms/fiducialpairreduction.py +95 -110
  8. pygsti/algorithms/fiducialselection.py +17 -8
  9. pygsti/algorithms/gaugeopt.py +2 -2
  10. pygsti/algorithms/germselection.py +87 -77
  11. pygsti/algorithms/mirroring.py +0 -388
  12. pygsti/algorithms/randomcircuit.py +165 -1333
  13. pygsti/algorithms/rbfit.py +0 -234
  14. pygsti/baseobjs/basis.py +94 -396
  15. pygsti/baseobjs/errorgenbasis.py +0 -132
  16. pygsti/baseobjs/errorgenspace.py +0 -10
  17. pygsti/baseobjs/label.py +52 -168
  18. pygsti/baseobjs/opcalc/fastopcalc.cp310-win_amd64.pyd +0 -0
  19. pygsti/baseobjs/opcalc/fastopcalc.pyx +2 -2
  20. pygsti/baseobjs/polynomial.py +13 -595
  21. pygsti/baseobjs/protectedarray.py +72 -132
  22. pygsti/baseobjs/statespace.py +1 -0
  23. pygsti/circuits/__init__.py +1 -1
  24. pygsti/circuits/circuit.py +753 -504
  25. pygsti/circuits/circuitconstruction.py +0 -4
  26. pygsti/circuits/circuitlist.py +47 -5
  27. pygsti/circuits/circuitparser/__init__.py +8 -8
  28. pygsti/circuits/circuitparser/fastcircuitparser.cp310-win_amd64.pyd +0 -0
  29. pygsti/circuits/circuitstructure.py +3 -3
  30. pygsti/circuits/cloudcircuitconstruction.py +27 -14
  31. pygsti/data/datacomparator.py +4 -9
  32. pygsti/data/dataset.py +51 -46
  33. pygsti/data/hypothesistest.py +0 -7
  34. pygsti/drivers/bootstrap.py +0 -49
  35. pygsti/drivers/longsequence.py +46 -10
  36. pygsti/evotypes/basereps_cython.cp310-win_amd64.pyd +0 -0
  37. pygsti/evotypes/chp/opreps.py +0 -61
  38. pygsti/evotypes/chp/statereps.py +0 -32
  39. pygsti/evotypes/densitymx/effectcreps.cpp +9 -10
  40. pygsti/evotypes/densitymx/effectreps.cp310-win_amd64.pyd +0 -0
  41. pygsti/evotypes/densitymx/effectreps.pyx +1 -1
  42. pygsti/evotypes/densitymx/opreps.cp310-win_amd64.pyd +0 -0
  43. pygsti/evotypes/densitymx/opreps.pyx +2 -2
  44. pygsti/evotypes/densitymx/statereps.cp310-win_amd64.pyd +0 -0
  45. pygsti/evotypes/densitymx/statereps.pyx +1 -1
  46. pygsti/evotypes/densitymx_slow/effectreps.py +7 -23
  47. pygsti/evotypes/densitymx_slow/opreps.py +16 -23
  48. pygsti/evotypes/densitymx_slow/statereps.py +10 -3
  49. pygsti/evotypes/evotype.py +39 -2
  50. pygsti/evotypes/stabilizer/effectreps.cp310-win_amd64.pyd +0 -0
  51. pygsti/evotypes/stabilizer/effectreps.pyx +0 -4
  52. pygsti/evotypes/stabilizer/opreps.cp310-win_amd64.pyd +0 -0
  53. pygsti/evotypes/stabilizer/opreps.pyx +0 -4
  54. pygsti/evotypes/stabilizer/statereps.cp310-win_amd64.pyd +0 -0
  55. pygsti/evotypes/stabilizer/statereps.pyx +1 -5
  56. pygsti/evotypes/stabilizer/termreps.cp310-win_amd64.pyd +0 -0
  57. pygsti/evotypes/stabilizer/termreps.pyx +0 -7
  58. pygsti/evotypes/stabilizer_slow/effectreps.py +0 -22
  59. pygsti/evotypes/stabilizer_slow/opreps.py +0 -4
  60. pygsti/evotypes/stabilizer_slow/statereps.py +0 -4
  61. pygsti/evotypes/statevec/effectreps.cp310-win_amd64.pyd +0 -0
  62. pygsti/evotypes/statevec/effectreps.pyx +1 -1
  63. pygsti/evotypes/statevec/opreps.cp310-win_amd64.pyd +0 -0
  64. pygsti/evotypes/statevec/opreps.pyx +2 -2
  65. pygsti/evotypes/statevec/statereps.cp310-win_amd64.pyd +0 -0
  66. pygsti/evotypes/statevec/statereps.pyx +1 -1
  67. pygsti/evotypes/statevec/termreps.cp310-win_amd64.pyd +0 -0
  68. pygsti/evotypes/statevec/termreps.pyx +0 -7
  69. pygsti/evotypes/statevec_slow/effectreps.py +0 -3
  70. pygsti/evotypes/statevec_slow/opreps.py +0 -5
  71. pygsti/extras/__init__.py +0 -1
  72. pygsti/extras/drift/signal.py +1 -1
  73. pygsti/extras/drift/stabilityanalyzer.py +3 -1
  74. pygsti/extras/interpygate/__init__.py +12 -0
  75. pygsti/extras/interpygate/core.py +0 -36
  76. pygsti/extras/interpygate/process_tomography.py +44 -10
  77. pygsti/extras/rpe/rpeconstruction.py +0 -2
  78. pygsti/forwardsims/__init__.py +1 -0
  79. pygsti/forwardsims/forwardsim.py +50 -93
  80. pygsti/forwardsims/mapforwardsim.py +78 -20
  81. pygsti/forwardsims/mapforwardsim_calc_densitymx.cp310-win_amd64.pyd +0 -0
  82. pygsti/forwardsims/mapforwardsim_calc_densitymx.pyx +65 -66
  83. pygsti/forwardsims/mapforwardsim_calc_generic.py +91 -13
  84. pygsti/forwardsims/matrixforwardsim.py +72 -17
  85. pygsti/forwardsims/termforwardsim.py +9 -111
  86. pygsti/forwardsims/termforwardsim_calc_stabilizer.cp310-win_amd64.pyd +0 -0
  87. pygsti/forwardsims/termforwardsim_calc_statevec.cp310-win_amd64.pyd +0 -0
  88. pygsti/forwardsims/termforwardsim_calc_statevec.pyx +0 -651
  89. pygsti/forwardsims/torchfwdsim.py +265 -0
  90. pygsti/forwardsims/weakforwardsim.py +2 -2
  91. pygsti/io/__init__.py +1 -2
  92. pygsti/io/mongodb.py +0 -2
  93. pygsti/io/stdinput.py +6 -22
  94. pygsti/layouts/copalayout.py +10 -12
  95. pygsti/layouts/distlayout.py +0 -40
  96. pygsti/layouts/maplayout.py +103 -25
  97. pygsti/layouts/matrixlayout.py +99 -60
  98. pygsti/layouts/prefixtable.py +1534 -52
  99. pygsti/layouts/termlayout.py +1 -1
  100. pygsti/modelmembers/instruments/instrument.py +3 -3
  101. pygsti/modelmembers/instruments/tpinstrument.py +2 -2
  102. pygsti/modelmembers/modelmember.py +0 -17
  103. pygsti/modelmembers/operations/__init__.py +3 -4
  104. pygsti/modelmembers/operations/affineshiftop.py +206 -0
  105. pygsti/modelmembers/operations/composederrorgen.py +1 -1
  106. pygsti/modelmembers/operations/composedop.py +1 -24
  107. pygsti/modelmembers/operations/denseop.py +5 -5
  108. pygsti/modelmembers/operations/eigpdenseop.py +2 -2
  109. pygsti/modelmembers/operations/embeddederrorgen.py +1 -1
  110. pygsti/modelmembers/operations/embeddedop.py +0 -1
  111. pygsti/modelmembers/operations/experrorgenop.py +5 -2
  112. pygsti/modelmembers/operations/fullarbitraryop.py +1 -0
  113. pygsti/modelmembers/operations/fullcptpop.py +2 -2
  114. pygsti/modelmembers/operations/fulltpop.py +28 -6
  115. pygsti/modelmembers/operations/fullunitaryop.py +5 -4
  116. pygsti/modelmembers/operations/lindbladcoefficients.py +93 -78
  117. pygsti/modelmembers/operations/lindbladerrorgen.py +268 -441
  118. pygsti/modelmembers/operations/linearop.py +7 -27
  119. pygsti/modelmembers/operations/opfactory.py +1 -1
  120. pygsti/modelmembers/operations/repeatedop.py +1 -24
  121. pygsti/modelmembers/operations/staticstdop.py +1 -1
  122. pygsti/modelmembers/povms/__init__.py +3 -3
  123. pygsti/modelmembers/povms/basepovm.py +7 -36
  124. pygsti/modelmembers/povms/complementeffect.py +4 -9
  125. pygsti/modelmembers/povms/composedeffect.py +0 -320
  126. pygsti/modelmembers/povms/computationaleffect.py +1 -1
  127. pygsti/modelmembers/povms/computationalpovm.py +3 -1
  128. pygsti/modelmembers/povms/effect.py +3 -5
  129. pygsti/modelmembers/povms/marginalizedpovm.py +3 -81
  130. pygsti/modelmembers/povms/tppovm.py +74 -2
  131. pygsti/modelmembers/states/__init__.py +2 -5
  132. pygsti/modelmembers/states/composedstate.py +0 -317
  133. pygsti/modelmembers/states/computationalstate.py +3 -3
  134. pygsti/modelmembers/states/cptpstate.py +4 -4
  135. pygsti/modelmembers/states/densestate.py +10 -8
  136. pygsti/modelmembers/states/fullpurestate.py +0 -24
  137. pygsti/modelmembers/states/purestate.py +1 -1
  138. pygsti/modelmembers/states/state.py +5 -6
  139. pygsti/modelmembers/states/tpstate.py +28 -10
  140. pygsti/modelmembers/term.py +3 -6
  141. pygsti/modelmembers/torchable.py +50 -0
  142. pygsti/modelpacks/_modelpack.py +1 -1
  143. pygsti/modelpacks/smq1Q_ZN.py +3 -1
  144. pygsti/modelpacks/smq2Q_XXYYII.py +2 -1
  145. pygsti/modelpacks/smq2Q_XY.py +3 -3
  146. pygsti/modelpacks/smq2Q_XYI.py +2 -2
  147. pygsti/modelpacks/smq2Q_XYICNOT.py +3 -3
  148. pygsti/modelpacks/smq2Q_XYICPHASE.py +3 -3
  149. pygsti/modelpacks/smq2Q_XYXX.py +1 -1
  150. pygsti/modelpacks/smq2Q_XYZICNOT.py +3 -3
  151. pygsti/modelpacks/smq2Q_XYZZ.py +1 -1
  152. pygsti/modelpacks/stdtarget.py +0 -121
  153. pygsti/models/cloudnoisemodel.py +1 -2
  154. pygsti/models/explicitcalc.py +3 -3
  155. pygsti/models/explicitmodel.py +3 -13
  156. pygsti/models/fogistore.py +5 -3
  157. pygsti/models/localnoisemodel.py +1 -2
  158. pygsti/models/memberdict.py +0 -12
  159. pygsti/models/model.py +801 -68
  160. pygsti/models/modelconstruction.py +4 -4
  161. pygsti/models/modelnoise.py +2 -2
  162. pygsti/models/modelparaminterposer.py +1 -1
  163. pygsti/models/oplessmodel.py +1 -1
  164. pygsti/models/qutrit.py +15 -14
  165. pygsti/objectivefns/objectivefns.py +75 -140
  166. pygsti/objectivefns/wildcardbudget.py +2 -7
  167. pygsti/optimize/__init__.py +1 -0
  168. pygsti/optimize/arraysinterface.py +28 -0
  169. pygsti/optimize/customcg.py +0 -12
  170. pygsti/optimize/customlm.py +129 -323
  171. pygsti/optimize/customsolve.py +2 -2
  172. pygsti/optimize/optimize.py +0 -84
  173. pygsti/optimize/simplerlm.py +841 -0
  174. pygsti/optimize/wildcardopt.py +19 -598
  175. pygsti/protocols/confidenceregionfactory.py +28 -14
  176. pygsti/protocols/estimate.py +31 -14
  177. pygsti/protocols/gst.py +238 -142
  178. pygsti/protocols/modeltest.py +19 -12
  179. pygsti/protocols/protocol.py +9 -37
  180. pygsti/protocols/rb.py +450 -79
  181. pygsti/protocols/treenode.py +8 -2
  182. pygsti/protocols/vb.py +108 -206
  183. pygsti/protocols/vbdataframe.py +1 -1
  184. pygsti/report/factory.py +0 -15
  185. pygsti/report/fogidiagram.py +1 -17
  186. pygsti/report/modelfunction.py +12 -3
  187. pygsti/report/mpl_colormaps.py +1 -1
  188. pygsti/report/plothelpers.py +11 -3
  189. pygsti/report/report.py +16 -0
  190. pygsti/report/reportables.py +41 -37
  191. pygsti/report/templates/offline/pygsti_dashboard.css +6 -0
  192. pygsti/report/templates/offline/pygsti_dashboard.js +12 -0
  193. pygsti/report/workspace.py +2 -14
  194. pygsti/report/workspaceplots.py +328 -505
  195. pygsti/tools/basistools.py +9 -36
  196. pygsti/tools/edesigntools.py +124 -96
  197. pygsti/tools/fastcalc.cp310-win_amd64.pyd +0 -0
  198. pygsti/tools/fastcalc.pyx +35 -81
  199. pygsti/tools/internalgates.py +151 -15
  200. pygsti/tools/jamiolkowski.py +5 -5
  201. pygsti/tools/lindbladtools.py +19 -11
  202. pygsti/tools/listtools.py +0 -114
  203. pygsti/tools/matrixmod2.py +1 -1
  204. pygsti/tools/matrixtools.py +173 -339
  205. pygsti/tools/nameddict.py +1 -1
  206. pygsti/tools/optools.py +154 -88
  207. pygsti/tools/pdftools.py +0 -25
  208. pygsti/tools/rbtheory.py +3 -320
  209. pygsti/tools/slicetools.py +64 -12
  210. pyGSTi-0.9.12.dist-info/METADATA +0 -157
  211. pygsti/algorithms/directx.py +0 -711
  212. pygsti/evotypes/qibo/__init__.py +0 -33
  213. pygsti/evotypes/qibo/effectreps.py +0 -78
  214. pygsti/evotypes/qibo/opreps.py +0 -376
  215. pygsti/evotypes/qibo/povmreps.py +0 -98
  216. pygsti/evotypes/qibo/statereps.py +0 -174
  217. pygsti/extras/rb/__init__.py +0 -13
  218. pygsti/extras/rb/benchmarker.py +0 -957
  219. pygsti/extras/rb/dataset.py +0 -378
  220. pygsti/extras/rb/io.py +0 -814
  221. pygsti/extras/rb/simulate.py +0 -1020
  222. pygsti/io/legacyio.py +0 -385
  223. pygsti/modelmembers/povms/denseeffect.py +0 -142
  224. {pyGSTi-0.9.12.dist-info → pyGSTi-0.9.13.dist-info}/LICENSE +0 -0
  225. {pyGSTi-0.9.12.dist-info → pyGSTi-0.9.13.dist-info}/top_level.txt +0 -0
@@ -11,10 +11,12 @@ Defines the MapCOPALayout class.
11
11
  #***************************************************************************************************
12
12
 
13
13
  import collections as _collections
14
+ import importlib as _importlib
15
+ import numpy as _np
14
16
 
15
17
  from pygsti.layouts.distlayout import DistributableCOPALayout as _DistributableCOPALayout
16
18
  from pygsti.layouts.distlayout import _DistributableAtom
17
- from pygsti.layouts.prefixtable import PrefixTable as _PrefixTable
19
+ from pygsti.layouts.prefixtable import PrefixTable as _PrefixTable, PrefixTableJacobian as _PrefixTableJacobian
18
20
  from pygsti.circuits.circuitlist import CircuitList as _CircuitList
19
21
  from pygsti.tools import listtools as _lt
20
22
 
@@ -51,19 +53,39 @@ class _MapCOPALayoutAtom(_DistributableAtom):
51
53
  """
52
54
 
53
55
  def __init__(self, unique_complete_circuits, ds_circuits, group, model,
54
- dataset, max_cache_size):
56
+ dataset, max_cache_size,
57
+ circuit_param_dependencies=None, param_circuit_dependencies=None,
58
+ expanded_complete_circuit_cache = None):
59
+
60
+ expanded_circuit_info_by_unique = dict()
61
+ expanded_circuit_set = dict() # only use SeparatePOVMCircuit keys as ordered set
62
+
63
+ if expanded_complete_circuit_cache is None:
64
+ expanded_complete_circuit_cache = dict()
55
65
 
56
- expanded_circuit_info_by_unique = _collections.OrderedDict()
57
- expanded_circuit_set = _collections.OrderedDict() # only use SeparatePOVMCircuit keys as ordered set
58
66
  for i in group:
59
- observed_outcomes = None if (dataset is None) else dataset[ds_circuits[i]].outcomes
60
- d = unique_complete_circuits[i].expand_instruments_and_separate_povm(model, observed_outcomes)
67
+ d = expanded_complete_circuit_cache.get(unique_complete_circuits[i], None)
68
+ if d is None:
69
+ unique_observed_outcomes = None if (dataset is None) else dataset[ds_circuits[i]].unique_outcomes
70
+ d = model.expand_instruments_and_separate_povm(unique_complete_circuits[i], unique_observed_outcomes)
61
71
  expanded_circuit_info_by_unique[i] = d # a dict of SeparatePOVMCircuits => tuples of outcome labels
62
- expanded_circuit_set.update(d)
72
+ expanded_circuit_set.update(d)
63
73
 
64
74
  expanded_circuits = list(expanded_circuit_set.keys())
75
+
65
76
  self.table = _PrefixTable(expanded_circuits, max_cache_size)
66
77
 
78
+ #only Build the Jacobian prefix table if we are using the generic evotype.
79
+ if model.sim.calclib is _importlib.import_module("pygsti.forwardsims.mapforwardsim_calc_generic"):
80
+ #create a list for storing the model parameter dependencies of expanded circuits
81
+ expanded_param_circuit_depend = [{} for _ in range(len(param_circuit_dependencies))]
82
+ for i in group:
83
+ for param_idx in circuit_param_dependencies[i]:
84
+ expanded_param_circuit_depend[param_idx].update(expanded_circuit_info_by_unique[i])
85
+ expanded_param_circuit_depend = [list(param_circuit_depend_dict.keys()) for param_circuit_depend_dict in expanded_param_circuit_depend]
86
+
87
+ self.jac_table = _PrefixTableJacobian(expanded_circuits, max_cache_size, expanded_param_circuit_depend)
88
+
67
89
  #Create circuit element <=> integer index lookups for speed
68
90
  all_rholabels = set()
69
91
  all_oplabels = set()
@@ -91,8 +113,7 @@ class _MapCOPALayoutAtom(_DistributableAtom):
91
113
  self.outcomes_by_expcircuit = {}
92
114
  self.povm_and_elbls_by_expcircuit = {}
93
115
 
94
- elindex_outcome_tuples = _collections.OrderedDict([
95
- (unique_i, list()) for unique_i in range(len(unique_complete_circuits))])
116
+ elindex_outcome_tuples = {unique_i: list() for unique_i in range(len(unique_complete_circuits))}
96
117
 
97
118
  #Assign element indices, "global" indices starting at `offset`
98
119
  local_offset = 0
@@ -198,42 +219,99 @@ class MapCOPALayout(_DistributableCOPALayout):
198
219
 
199
220
  resource_alloc : ResourceAllocation, optional
200
221
  The resources available for computing circuit outcome probabilities.
222
+
223
+ circuit_partition_cost_functions : tuple of str, optional (default ('size', 'propagations'))
224
+ A tuple of strings denoting cost function to use in each of the two stages of the algorithm
225
+ for determining the partitions of the complete circuit set amongst atoms.
226
+ Allowed options are 'size', which corresponds to balancing the number of circuits,
227
+ and 'propagations', which corresponds to balancing the number of state propagations.
201
228
 
202
229
  verbosity : int or VerbosityPrinter
203
230
  Determines how much output to send to stdout. 0 means no output, higher
204
231
  integers mean more output.
232
+
233
+ layout_creation_circuit_cache : dict, optional (default None)
234
+ An optional dictionary containing pre-computed circuit structures/modifications which
235
+ can be used to reduce the overhead of repeated circuit operations during layout creation.
236
+
237
+ load_balancing_parameters : tuple of floats, optional (default (1.2, .1))
238
+ A tuple of floats used as load balancing parameters when splitting a layout across atoms,
239
+ as in the multi-processor setting when using MPI. These parameters correspond to the `imbalance_threshold`
240
+ and `minimum_improvement_threshold` parameters described in the method `find_splitting_new`
241
+ of the `PrefixTable` class.
205
242
  """
206
243
 
207
244
  def __init__(self, circuits, model, dataset=None, max_cache_size=None,
208
245
  num_sub_tables=None, num_table_processors=1, num_param_dimension_processors=(),
209
- param_dimensions=(), param_dimension_blk_sizes=(), resource_alloc=None, verbosity=0):
246
+ param_dimensions=(), param_dimension_blk_sizes=(), resource_alloc=None,
247
+ circuit_partition_cost_functions=('size', 'propagations'), verbosity=0,
248
+ layout_creation_circuit_cache=None, load_balancing_parameters = (1.2, .1)):
210
249
 
211
250
  unique_circuits, to_unique = self._compute_unique_circuits(circuits)
212
251
  aliases = circuits.op_label_aliases if isinstance(circuits, _CircuitList) else None
213
252
  ds_circuits = _lt.apply_aliases_to_circuits(unique_circuits, aliases)
214
- unique_complete_circuits = [model.complete_circuit(c) for c in unique_circuits]
215
- unique_povmless_circuits = [model.split_circuit(c, split_prep=False)[1] for c in unique_complete_circuits]
253
+
254
+ #extract subcaches from layout_creation_circuit_cache:
255
+ if layout_creation_circuit_cache is None:
256
+ layout_creation_circuit_cache = dict()
257
+ self.completed_circuit_cache = layout_creation_circuit_cache.get('completed_circuits', None)
258
+ self.split_circuit_cache = layout_creation_circuit_cache.get('split_circuits', None)
259
+ self.expanded_and_separated_circuits_cache = layout_creation_circuit_cache.get('expanded_and_separated_circuits', None)
260
+
261
+ if self.completed_circuit_cache is None:
262
+ unique_complete_circuits = model.complete_circuits(unique_circuits)
263
+ split_circuits = model.split_circuits(unique_complete_circuits, split_prep=False)
264
+ else:
265
+ unique_complete_circuits = []
266
+ for c in unique_circuits:
267
+ comp_ckt = self.completed_circuit_cache.get(c, None)
268
+ if comp_ckt is not None:
269
+ unique_complete_circuits.append(comp_ckt)
270
+ else:
271
+ unique_complete_circuits.append(model.complete_circuit(c))
272
+ split_circuits = []
273
+ for c, c_complete in zip(unique_circuits,unique_complete_circuits):
274
+ split_ckt = self.split_circuit_cache.get(c, None)
275
+ if split_ckt is not None:
276
+ split_circuits.append(split_ckt)
277
+ else:
278
+ split_circuits.append(model.split_circuit(c_complete, split_prep=False))
279
+
280
+ #construct a map for the parameter dependence for each of the unique_complete_circuits.
281
+ #returns a dictionary who's keys are the unique completed circuits, and whose
282
+ #values are lists of model parameters upon which that circuit depends.
283
+ if model.sim.calclib is _importlib.import_module("pygsti.forwardsims.mapforwardsim_calc_generic") and model.param_interposer is None:
284
+ circ_param_map, param_circ_map = model.circuit_parameter_dependence(unique_complete_circuits, return_param_circ_map=True)
285
+ uniq_comp_circs_param_depend = list(circ_param_map.values())
286
+ uniq_comp_param_circs_depend = param_circ_map
287
+ else :
288
+ circ_param_map = None
289
+ param_circ_map = None
290
+ uniq_comp_circs_param_depend = None
291
+ uniq_comp_param_circs_depend = None
292
+ #construct list of unique POVM-less circuits.
293
+ unique_povmless_circuits = [ckt_tup[1] for ckt_tup in split_circuits]
216
294
 
217
295
  max_sub_table_size = None # was an argument but never used; remove in future
218
296
  if (num_sub_tables is not None and num_sub_tables > 1) or max_sub_table_size is not None:
219
297
  circuit_table = _PrefixTable(unique_povmless_circuits, max_cache_size)
220
- groups = circuit_table.find_splitting(max_sub_table_size, num_sub_tables, verbosity=verbosity)
298
+ self.complete_circuit_table = circuit_table
299
+ groups = circuit_table.find_splitting_new(max_sub_table_size, num_sub_tables, verbosity=verbosity,
300
+ initial_cost_metric=circuit_partition_cost_functions[0],
301
+ rebalancing_cost_metric=circuit_partition_cost_functions[1],
302
+ imbalance_threshold = load_balancing_parameters[0],
303
+ minimum_improvement_threshold = load_balancing_parameters[1])
304
+ #groups = circuit_table.find_splitting(max_sub_table_size, num_sub_tables, verbosity=verbosity)
221
305
  else:
222
- groups = [set(range(len(unique_complete_circuits)))]
223
-
224
- #atoms = []
225
- #elindex_outcome_tuples = _collections.OrderedDict(
226
- # [(unique_i, list()) for unique_i in range(len(unique_circuits))])
227
-
228
- #offset = 0
229
- #for group in groups:
230
- # atoms.append(_MapCOPALayoutAtom(unique_complete_circuits, ds_circuits, to_orig, group,
231
- # model, dataset, offset, elindex_outcome_tuples, max_cache_size))
232
- # offset += atoms[-1].num_elements
306
+ groups = [list(range(len(unique_complete_circuits)))]
307
+
233
308
 
234
309
  def _create_atom(group):
235
310
  return _MapCOPALayoutAtom(unique_complete_circuits, ds_circuits, group,
236
- model, dataset, max_cache_size)
311
+ model, dataset, max_cache_size,
312
+ circuit_param_dependencies= uniq_comp_circs_param_depend,
313
+ param_circuit_dependencies= uniq_comp_param_circs_depend,
314
+ expanded_complete_circuit_cache=self.expanded_and_separated_circuits_cache)
237
315
 
238
316
  super().__init__(circuits, unique_circuits, to_unique, unique_complete_circuits,
239
317
  _create_atom, groups, num_table_processors,
@@ -61,6 +61,9 @@ class _MatrixCOPALayoutAtom(_DistributableAtom):
61
61
  model : Model
62
62
  The model being used to construct this layout. Used for expanding instruments
63
63
  within the circuits.
64
+
65
+ unique_circuits : list of Circuits
66
+ A list of the unique :class:`Circuit` objects representing the circuits this layout will include.
64
67
 
65
68
  dataset : DataSet
66
69
  The dataset, used to include only observed circuit outcomes in this atom
@@ -68,7 +71,11 @@ class _MatrixCOPALayoutAtom(_DistributableAtom):
68
71
  """
69
72
 
70
73
  def __init__(self, unique_complete_circuits, unique_nospam_circuits, circuits_by_unique_nospam_circuits,
71
- ds_circuits, group, helpful_scratch, model, dataset):
74
+ ds_circuits, group, helpful_scratch, model, unique_circuits, dataset=None, expanded_and_separated_circuit_cache=None,
75
+ double_expanded_nospam_circuits_cache = None):
76
+
77
+ if expanded_and_separated_circuit_cache is None:
78
+ expanded_and_separated_circuit_cache = dict()
72
79
 
73
80
  #Note: group gives unique_nospam_circuits indices, which circuits_by_unique_nospam_circuits
74
81
  # turns into "unique complete circuit" indices, which the layout via it's to_unique can map
@@ -78,24 +85,24 @@ class _MatrixCOPALayoutAtom(_DistributableAtom):
78
85
  for i in indices:
79
86
  nospam_c = unique_nospam_circuits[i]
80
87
  for unique_i in circuits_by_unique_nospam_circuits[nospam_c]: # "unique" circuits: add SPAM to nospam_c
81
- observed_outcomes = None if (dataset is None) else dataset[ds_circuits[unique_i]].unique_outcomes
82
- expc_outcomes = unique_complete_circuits[unique_i].expand_instruments_and_separate_povm(
83
- model, observed_outcomes)
84
- #Note: unique_complete_circuits may have duplicates (they're only unique *pre*-completion)
85
-
88
+ #the cache is indexed into using the (potentially) incomplete circuits
89
+ expc_outcomes = expanded_and_separated_circuit_cache.get(unique_circuits[unique_i], None)
90
+ if expc_outcomes is None: #fall back on original non-cache behavior.
91
+ observed_outcomes = None if (dataset is None) else dataset[ds_circuits[unique_i]].unique_outcomes
92
+ expc_outcomes = model.expand_instruments_and_separate_povm(unique_complete_circuits[unique_i], observed_outcomes)
93
+ #and add this new value to the cache.
94
+ expanded_and_separated_circuit_cache[unique_circuits[unique_i]] = expc_outcomes
86
95
  for sep_povm_c, outcomes in expc_outcomes.items(): # for each expanded cir from unique_i-th circuit
87
96
  prep_lbl = sep_povm_c.circuit_without_povm[0]
88
97
  exp_nospam_c = sep_povm_c.circuit_without_povm[1:] # sep_povm_c *always* has prep lbl
89
98
  spam_tuples = [(prep_lbl, elabel) for elabel in sep_povm_c.full_effect_labels]
90
- outcome_by_spamtuple = _collections.OrderedDict([(st, outcome)
91
- for st, outcome in zip(spam_tuples, outcomes)])
99
+ outcome_by_spamtuple = {st:outcome for st, outcome in zip(spam_tuples, outcomes)}
92
100
 
93
101
  #Now add these outcomes to `expanded_nospam_circuit_outcomes` - note that multiple "unique_i"'s
94
102
  # may exist for the same expanded & without-spam circuit (exp_nospam_c) and so we need to
95
103
  # keep track of a list of unique_i indices for each circut and spam tuple below.
96
104
  if exp_nospam_c not in _expanded_nospam_circuit_outcomes:
97
- _expanded_nospam_circuit_outcomes[exp_nospam_c] = _collections.OrderedDict(
98
- [(st, (outcome, [unique_i])) for st, outcome in zip(spam_tuples, outcomes)])
105
+ _expanded_nospam_circuit_outcomes[exp_nospam_c] = {st:(outcome, [unique_i]) for st, outcome in zip(spam_tuples, outcomes)}
99
106
  else:
100
107
  for st, outcome in outcome_by_spamtuple.items():
101
108
  if st in _expanded_nospam_circuit_outcomes[exp_nospam_c]:
@@ -109,25 +116,28 @@ class _MatrixCOPALayoutAtom(_DistributableAtom):
109
116
 
110
117
  # keys = expanded circuits w/out SPAM layers; values = spamtuple => (outcome, unique_is) dictionary that
111
118
  # keeps track of which "unique" circuit indices having each spamtuple / outcome.
112
- expanded_nospam_circuit_outcomes = _collections.OrderedDict()
119
+ expanded_nospam_circuit_outcomes = dict()
113
120
  add_expanded_circuits(group, expanded_nospam_circuit_outcomes)
114
- expanded_nospam_circuits = _collections.OrderedDict(
115
- [(i, cir) for i, cir in enumerate(expanded_nospam_circuit_outcomes.keys())])
121
+ expanded_nospam_circuits = {i:cir for i, cir in enumerate(expanded_nospam_circuit_outcomes.keys())}
116
122
 
117
123
  # add suggested scratch to the "final" elements as far as the tree creation is concerned
118
124
  # - this allows these scratch element to help balance the tree.
119
- expanded_nospam_circuit_outcomes_plus_scratch = expanded_nospam_circuit_outcomes.copy()
120
- add_expanded_circuits(helpful_scratch, expanded_nospam_circuit_outcomes_plus_scratch)
121
- expanded_nospam_circuits_plus_scratch = _collections.OrderedDict(
122
- [(i, cir) for i, cir in enumerate(expanded_nospam_circuit_outcomes_plus_scratch.keys())])
123
-
124
- double_expanded_nospam_circuits_plus_scratch = _collections.OrderedDict()
125
+ if helpful_scratch:
126
+ expanded_nospam_circuit_outcomes_plus_scratch = expanded_nospam_circuit_outcomes.copy()
127
+ add_expanded_circuits(helpful_scratch, expanded_nospam_circuit_outcomes_plus_scratch)
128
+ expanded_nospam_circuits_plus_scratch = {i:cir for i, cir in enumerate(expanded_nospam_circuit_outcomes_plus_scratch.keys())}
129
+ else:
130
+ expanded_nospam_circuits_plus_scratch = expanded_nospam_circuits.copy()
131
+
132
+ if double_expanded_nospam_circuits_cache is None:
133
+ double_expanded_nospam_circuits_cache = dict()
134
+ double_expanded_nospam_circuits_plus_scratch = dict()
125
135
  for i, cir in expanded_nospam_circuits_plus_scratch.items():
126
- cir = cir.copy(editable=True)
127
- cir.expand_subcircuits() # expand sub-circuits for a more efficient tree
128
- cir.done_editing()
129
- double_expanded_nospam_circuits_plus_scratch[i] = cir
130
-
136
+ # expand sub-circuits for a more efficient tree
137
+ double_expanded_ckt = double_expanded_nospam_circuits_cache.get(cir, None)
138
+ if double_expanded_ckt is None: #Fall back to standard behavior and do expansion.
139
+ double_expanded_ckt = cir.expand_subcircuits()
140
+ double_expanded_nospam_circuits_plus_scratch[i] = double_expanded_ckt
131
141
  self.tree = _EvalTree.create(double_expanded_nospam_circuits_plus_scratch)
132
142
  #print("Atom tree: %d circuits => tree of size %d" % (len(expanded_nospam_circuits), len(self.tree)))
133
143
 
@@ -138,7 +148,7 @@ class _MatrixCOPALayoutAtom(_DistributableAtom):
138
148
  # quantity plus a spam-tuple. We order the final indices so that all the outcomes corresponding to a
139
149
  # given spam-tuple are contiguous.
140
150
 
141
- tree_indices_by_spamtuple = _collections.OrderedDict() # "tree" indices index expanded_nospam_circuits
151
+ tree_indices_by_spamtuple = dict() # "tree" indices index expanded_nospam_circuits
142
152
  for i, c in expanded_nospam_circuits.items():
143
153
  for spam_tuple in expanded_nospam_circuit_outcomes[c].keys():
144
154
  if spam_tuple not in tree_indices_by_spamtuple: tree_indices_by_spamtuple[spam_tuple] = []
@@ -147,7 +157,7 @@ class _MatrixCOPALayoutAtom(_DistributableAtom):
147
157
  #Assign element indices, starting at `offset`
148
158
  # now that we know how many of each spamtuple there are, assign final element indices.
149
159
  local_offset = 0
150
- self.indices_by_spamtuple = _collections.OrderedDict() # values are (element_indices, tree_indices) tuples.
160
+ self.indices_by_spamtuple = dict() # values are (element_indices, tree_indices) tuples.
151
161
  for spam_tuple, tree_indices in tree_indices_by_spamtuple.items():
152
162
  self.indices_by_spamtuple[spam_tuple] = (slice(local_offset, local_offset + len(tree_indices)),
153
163
  _slct.list_to_slice(tree_indices, array_ok=True))
@@ -157,8 +167,7 @@ class _MatrixCOPALayoutAtom(_DistributableAtom):
157
167
  element_slice = None # slice(offset, offset + local_offset) # *global* (of parent layout) element-index slice
158
168
  num_elements = local_offset
159
169
 
160
- elindex_outcome_tuples = _collections.OrderedDict([
161
- (unique_i, list()) for unique_i in range(len(unique_complete_circuits))])
170
+ elindex_outcome_tuples = {unique_i: list() for unique_i in range(len(unique_complete_circuits))}
162
171
 
163
172
  for spam_tuple, (element_indices, tree_indices) in self.indices_by_spamtuple.items():
164
173
  for elindex, tree_index in zip(_slct.indices(element_indices), _slct.to_array(tree_indices)):
@@ -231,7 +240,7 @@ class MatrixCOPALayout(_DistributableCOPALayout):
231
240
  Parameters
232
241
  ----------
233
242
  circuits : list
234
- A list of:class:`Circuit` objects representing the circuits this layout will include.
243
+ A list of :class:`Circuit` objects representing the circuits this layout will include.
235
244
 
236
245
  model : Model
237
246
  The model that will be used to compute circuit outcome probabilities using this layout.
@@ -271,11 +280,16 @@ class MatrixCOPALayout(_DistributableCOPALayout):
271
280
  verbosity : int or VerbosityPrinter
272
281
  Determines how much output to send to stdout. 0 means no output, higher
273
282
  integers mean more output.
283
+
284
+ layout_creation_circuit_cache : dict, optional (default None)
285
+ A precomputed dictionary serving as a cache for completed
286
+ circuits. I.e. circuits with prep labels and POVM labels appended.
274
287
  """
275
288
 
276
289
  def __init__(self, circuits, model, dataset=None, num_sub_trees=None, num_tree_processors=1,
277
290
  num_param_dimension_processors=(), param_dimensions=(),
278
- param_dimension_blk_sizes=(), resource_alloc=None, verbosity=0):
291
+ param_dimension_blk_sizes=(), resource_alloc=None, verbosity=0,
292
+ layout_creation_circuit_cache = None):
279
293
 
280
294
  #OUTDATED: TODO - revise this:
281
295
  # 1. pre-process => get complete circuits => spam-tuples list for each no-spam circuit (no expanding yet)
@@ -290,57 +304,82 @@ class MatrixCOPALayout(_DistributableCOPALayout):
290
304
  unique_circuits, to_unique = self._compute_unique_circuits(circuits)
291
305
  aliases = circuits.op_label_aliases if isinstance(circuits, _CircuitList) else None
292
306
  ds_circuits = _lt.apply_aliases_to_circuits(unique_circuits, aliases)
293
- unique_complete_circuits = [model.complete_circuit(c) for c in unique_circuits]
307
+
308
+ #extract subcaches from layout_creation_circuit_cache:
309
+ if layout_creation_circuit_cache is None:
310
+ layout_creation_circuit_cache = dict()
311
+ self.completed_circuit_cache = layout_creation_circuit_cache.get('completed_circuits', None)
312
+ self.split_circuit_cache = layout_creation_circuit_cache.get('split_circuits', None)
313
+ self.expanded_and_separated_circuits_cache = layout_creation_circuit_cache.get('expanded_and_separated_circuits', None)
314
+ self.expanded_subcircuits_no_spam_cache = layout_creation_circuit_cache.get('expanded_subcircuits_no_spam', None)
315
+
316
+ if self.completed_circuit_cache is None:
317
+ unique_complete_circuits, split_unique_circuits = model.complete_circuits(unique_circuits, return_split=True)
318
+ else:
319
+ unique_complete_circuits = []
320
+ for c in unique_circuits:
321
+ comp_ckt = self.completed_circuit_cache.get(c, None)
322
+ if comp_ckt is not None:
323
+ unique_complete_circuits.append(comp_ckt)
324
+ else:
325
+ unique_complete_circuits.append(model.complete_circuit(c))
294
326
  #Note: "unique" means a unique circuit *before* circuit-completion, so there could be duplicate
295
327
  # "unique circuits" after completion, e.g. "rho0Gx" and "Gx" could both complete to "rho0GxMdefault_0".
296
328
 
297
- circuits_by_unique_nospam_circuits = _collections.OrderedDict()
298
- for i, c in enumerate(unique_complete_circuits):
299
- _, nospam_c, _ = model.split_circuit(c)
300
- if nospam_c in circuits_by_unique_nospam_circuits:
301
- circuits_by_unique_nospam_circuits[nospam_c].append(i)
302
- else:
303
- circuits_by_unique_nospam_circuits[nospam_c] = [i]
304
- unique_nospam_circuits = list(circuits_by_unique_nospam_circuits.keys())
329
+ circuits_by_unique_nospam_circuits = dict()
330
+ if self.completed_circuit_cache is None:
331
+ for i, (_, nospam_c, _) in enumerate(split_unique_circuits):
332
+ if nospam_c in circuits_by_unique_nospam_circuits:
333
+ circuits_by_unique_nospam_circuits[nospam_c].append(i)
334
+ else:
335
+ circuits_by_unique_nospam_circuits[nospam_c] = [i]
336
+ #also create the split circuit cache at this point for future use.
337
+ if self.split_circuit_cache is None:
338
+ self.split_circuit_cache = {unique_ckt:split_ckt for unique_ckt, split_ckt in zip(unique_circuits, split_unique_circuits)}
305
339
 
340
+ else:
341
+ if self.split_circuit_cache is None:
342
+ self.split_circuit_cache = dict()
343
+ for i, (c_unique_complete, c_unique) in enumerate(zip(unique_complete_circuits, unique_circuits)):
344
+ split_ckt_tup = self.split_circuit_cache.get(c_unique, None)
345
+ nospam_c= split_ckt_tup[1] if split_ckt_tup is not None else None
346
+ if nospam_c is None:
347
+ split_ckt_tup = model.split_circuit(c_unique_complete)
348
+ nospam_c= split_ckt_tup[1]
349
+ #also add this missing circuit to the cache for future use.
350
+ self.split_circuit_cache[c_unique] = split_ckt_tup
351
+ if nospam_c in circuits_by_unique_nospam_circuits:
352
+ circuits_by_unique_nospam_circuits[nospam_c].append(i)
353
+ else:
354
+ circuits_by_unique_nospam_circuits[nospam_c] = [i]
355
+
356
+ unique_nospam_circuits = list(circuits_by_unique_nospam_circuits.keys())
357
+
306
358
  # Split circuits into groups that will make good subtrees (all procs do this)
307
359
  max_sub_tree_size = None # removed from being an argument (unused)
308
360
  if (num_sub_trees is not None and num_sub_trees > 1) or max_sub_tree_size is not None:
309
361
  circuit_tree = _EvalTree.create(unique_nospam_circuits)
310
362
  groups, helpful_scratch = circuit_tree.find_splitting(len(unique_nospam_circuits),
311
363
  max_sub_tree_size, num_sub_trees, verbosity - 1)
312
- #print("%d circuits => tree of size %d" % (len(unique_nospam_circuits), len(circuit_tree)))
313
364
  else:
314
365
  groups = [set(range(len(unique_nospam_circuits)))]
315
366
  helpful_scratch = [set()]
316
367
  # (elements of `groups` contain indices into `unique_nospam_circuits`)
317
368
 
318
- # Divide `groups` into num_tree_processors roughly equal sets (each containing
319
- # potentially multiple groups)
320
- #my_group_indices, group_owners, grp_subcomm = self._distribute(num_tree_processors, len(groups),
321
- # resource_alloc, verbosity)
322
- #my_group_indices = set(my_group_indices)
323
-
324
- #my_atoms = []
325
- #elindex_outcome_tuples = _collections.OrderedDict([
326
- # (orig_i, list()) for orig_i in range(len(unique_circuits))])
327
- #
328
- #offset = 0
329
- #for i, (group, helpful_scratch_group) in enumerate(zip(groups, helpful_scratch)):
330
- # if i not in my_group_indices: continue
331
- # my_atoms.append(_MatrixCOPALayoutAtom(unique_complete_circuits, unique_nospam_circuits,
332
- # circuits_by_unique_nospam_circuits, ds_circuits,
333
- # group, helpful_scratch_group, model, dataset, offset,
334
- # elindex_outcome_tuples))
335
- # offset += my_atoms[-1].num_elements
336
-
337
369
  def _create_atom(args):
338
370
  group, helpful_scratch_group = args
339
371
  return _MatrixCOPALayoutAtom(unique_complete_circuits, unique_nospam_circuits,
340
372
  circuits_by_unique_nospam_circuits, ds_circuits,
341
- group, helpful_scratch_group, model, dataset)
373
+ group, helpful_scratch_group, model,
374
+ unique_circuits, dataset,
375
+ self.expanded_and_separated_circuits_cache,
376
+ self.expanded_subcircuits_no_spam_cache)
342
377
 
343
378
  super().__init__(circuits, unique_circuits, to_unique, unique_complete_circuits,
344
379
  _create_atom, list(zip(groups, helpful_scratch)), num_tree_processors,
345
380
  num_param_dimension_processors, param_dimensions,
346
381
  param_dimension_blk_sizes, resource_alloc, verbosity)
382
+
383
+
384
+
385
+