pyGSTi 0.9.12__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 (225) hide show
  1. pyGSTi-0.9.13.dist-info/METADATA +185 -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.cp38-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.cp38-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.cp38-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.cp38-win_amd64.pyd +0 -0
  41. pygsti/evotypes/densitymx/effectreps.pyx +1 -1
  42. pygsti/evotypes/densitymx/opreps.cp38-win_amd64.pyd +0 -0
  43. pygsti/evotypes/densitymx/opreps.pyx +2 -2
  44. pygsti/evotypes/densitymx/statereps.cp38-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.cp38-win_amd64.pyd +0 -0
  51. pygsti/evotypes/stabilizer/effectreps.pyx +0 -4
  52. pygsti/evotypes/stabilizer/opreps.cp38-win_amd64.pyd +0 -0
  53. pygsti/evotypes/stabilizer/opreps.pyx +0 -4
  54. pygsti/evotypes/stabilizer/statereps.cp38-win_amd64.pyd +0 -0
  55. pygsti/evotypes/stabilizer/statereps.pyx +1 -5
  56. pygsti/evotypes/stabilizer/termreps.cp38-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.cp38-win_amd64.pyd +0 -0
  62. pygsti/evotypes/statevec/effectreps.pyx +1 -1
  63. pygsti/evotypes/statevec/opreps.cp38-win_amd64.pyd +0 -0
  64. pygsti/evotypes/statevec/opreps.pyx +2 -2
  65. pygsti/evotypes/statevec/statereps.cp38-win_amd64.pyd +0 -0
  66. pygsti/evotypes/statevec/statereps.pyx +1 -1
  67. pygsti/evotypes/statevec/termreps.cp38-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.cp38-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.cp38-win_amd64.pyd +0 -0
  87. pygsti/forwardsims/termforwardsim_calc_statevec.cp38-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.cp38-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
@@ -26,6 +26,8 @@ from pygsti.baseobjs.verbosityprinter import VerbosityPrinter as _VerbosityPrint
26
26
  from pygsti.tools import sharedmemtools as _smt
27
27
  from pygsti.tools import slicetools as _slct
28
28
  from pygsti.tools.matrixtools import _fas
29
+ from pygsti.tools import listtools as _lt
30
+ from pygsti.circuits import CircuitList as _CircuitList
29
31
 
30
32
  _dummy_profiler = _DummyProfiler()
31
33
 
@@ -49,7 +51,7 @@ class SimpleMapForwardSimulator(_ForwardSimulator):
49
51
  # If this is done, then MapForwardSimulator wouldn't need to separately subclass DistributableForwardSimulator
50
52
 
51
53
  def _compute_circuit_outcome_probabilities(self, array_to_fill, circuit, outcomes, resource_alloc, time=None):
52
- expanded_circuit_outcomes = circuit.expand_instruments_and_separate_povm(self.model, outcomes)
54
+ expanded_circuit_outcomes = self.model.expand_instruments_and_separate_povm(circuit, outcomes)
53
55
  outcome_to_index = {outc: i for i, outc in enumerate(outcomes)}
54
56
  for spc, spc_outcomes in expanded_circuit_outcomes.items(): # spc is a SeparatePOVMCircuit
55
57
  # Note: `spc.circuit_without_povm` *always* begins with a prep label.
@@ -157,7 +159,7 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
157
159
  if method_name == 'bulk_fill_timedep_dchi2': return ('p',) # just an additional parameter vector
158
160
  return super()._array_types_for_method(method_name)
159
161
 
160
- def __init__(self, model=None, max_cache_size=0, num_atoms=None, processor_grid=None, param_blk_sizes=None,
162
+ def __init__(self, model=None, max_cache_size=None, num_atoms=None, processor_grid=None, param_blk_sizes=None,
161
163
  derivative_eps=1e-7, hessian_eps=1e-5):
162
164
  #super().__init__(model, num_atoms, processor_grid, param_blk_sizes)
163
165
  _DistributableForwardSimulator.__init__(self, model, num_atoms, processor_grid, param_blk_sizes)
@@ -193,7 +195,9 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
193
195
  self._processor_grid, self._pblk_sizes)
194
196
 
195
197
  def create_layout(self, circuits, dataset=None, resource_alloc=None, array_types=('E',),
196
- derivative_dimension=None, verbosity=0):
198
+ derivative_dimensions=None, verbosity=0, layout_creation_circuit_cache=None,
199
+ circuit_partition_cost_functions=('size', 'propagations'),
200
+ load_balancing_parameters=(1.15,.1)):
197
201
  """
198
202
  Constructs an circuit-outcome-probability-array (COPA) layout for a list of circuits.
199
203
 
@@ -214,14 +218,32 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
214
218
  array_types : tuple, optional
215
219
  A tuple of string-valued array types. See :meth:`ForwardSimulator.create_layout`.
216
220
 
217
- derivative_dimension : int, optional
221
+ derivative_dimensions : int or tuple[int], optional
218
222
  Optionally, the parameter-space dimension used when taking first
219
223
  and second derivatives with respect to the cirucit outcome probabilities. This must be
220
224
  non-None when `array_types` contains `'ep'` or `'epp'` types.
225
+ If a tuple, then must be length 1.
221
226
 
222
227
  verbosity : int or VerbosityPrinter
223
228
  Determines how much output to send to stdout. 0 means no output, higher
224
229
  integers mean more output.
230
+
231
+ layout_creation_circuit_cache : dict, optional (default None)
232
+ A precomputed dictionary serving as a cache for completed circuits. I.e. circuits
233
+ with prep labels and POVM labels appended. Along with other useful pre-computed
234
+ circuit structures used in layout creation.
235
+
236
+ circuit_partition_cost_functions : tuple of str, optional (default ('size', 'propagations'))
237
+ A tuple of strings denoting cost function to use in each of the two stages of the algorithm
238
+ for determining the partitions of the complete circuit set amongst atoms.
239
+ Allowed options are 'size', which corresponds to balancing the number of circuits,
240
+ and 'propagations', which corresponds to balancing the number of state propagations.
241
+
242
+ load_balancing_parameters : tuple of floats, optional (default (1.15, .1))
243
+ A tuple of floats used as load balancing parameters when splitting a layout across atoms,
244
+ as in the multi-processor setting when using MPI. These parameters correspond to the `imbalance_threshold`
245
+ and `minimum_improvement_threshold` parameters described in the method `find_splitting_new`
246
+ of the `PrefixTable` class.
225
247
 
226
248
  Returns
227
249
  -------
@@ -233,7 +255,13 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
233
255
  if (resource_alloc.mem_limit is not None) else None # *per-processor* memory limit
234
256
  nprocs = resource_alloc.comm_size
235
257
  comm = resource_alloc.comm
236
- num_params = derivative_dimension if (derivative_dimension is not None) else self.model.num_params
258
+ if isinstance(derivative_dimensions, int):
259
+ num_params = derivative_dimensions
260
+ elif isinstance(derivative_dimensions, tuple):
261
+ assert len(derivative_dimensions) == 1
262
+ num_params = derivative_dimensions[0]
263
+ else:
264
+ num_params = self.model.num_params
237
265
  C = 1.0 / (1024.0**3)
238
266
 
239
267
  if mem_limit is not None:
@@ -241,15 +269,15 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
241
269
  raise MemoryError("Attempted layout creation w/memory limit = %g <= 0!" % mem_limit)
242
270
  printer.log("Layout creation w/mem limit = %.2fGB" % (mem_limit * C))
243
271
 
244
- #Start with how we'd like to split processors up (without regard to memory limit):
245
-
246
- # when there are lots of processors, the from_vector calls dominante over the actual fwdsim,
247
- # but we can reduce from_vector calls by having np1, np2 > 0 (each param requires a from_vector
248
- # call when using finite diffs) - so we want to choose nc = Ng < nprocs and np1 > 1 (so nc * np1 = nprocs).
249
- #work_per_proc = self.model.dim**2
272
+ #Start with how we'd like to split processors up (without regard to memory limit):
273
+ #The current implementation of map (should) benefit more from having a matching between the number of atoms
274
+ #and the number of processors, at least for up to around two-qubits.
275
+ default_natoms = nprocs # heuristic
276
+ #TODO: factor in the mem_limit value to more intelligently set the default number of atoms.
250
277
 
251
278
  natoms, na, npp, param_dimensions, param_blk_sizes = self._compute_processor_distribution(
252
- array_types, nprocs, num_params, len(circuits), default_natoms=2 * self.model.dim) # heuristic?
279
+ array_types, nprocs, num_params, len(circuits), default_natoms=default_natoms)
280
+
253
281
  printer.log(f'Num Param Processors {npp}')
254
282
 
255
283
  printer.log("MapLayout: %d processors divided into %s (= %d) grid along circuit and parameter directions." %
@@ -258,7 +286,9 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
258
286
  assert(_np.prod((na,) + npp) <= nprocs), "Processor grid size exceeds available processors!"
259
287
 
260
288
  layout = _MapCOPALayout(circuits, self.model, dataset, self._max_cache_size, natoms, na, npp,
261
- param_dimensions, param_blk_sizes, resource_alloc, verbosity)
289
+ param_dimensions, param_blk_sizes, resource_alloc,circuit_partition_cost_functions,
290
+ verbosity, layout_creation_circuit_cache= layout_creation_circuit_cache,
291
+ load_balancing_parameters=load_balancing_parameters)
262
292
 
263
293
  if mem_limit is not None:
264
294
  loc_nparams1 = num_params / npp[0] if len(npp) > 0 else 0
@@ -284,13 +314,6 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
284
314
  layout._param_dimensions, (loc_nparams1, loc_nparams2),
285
315
  (blk1, blk2), max_atom_cachesize, self.model.dim)
286
316
 
287
- #def approx_mem_estimate(nc, np1, np2):
288
- # approx_cachesize = (num_circuits / nc) * 1.3 # inflate expected # of circuits per atom => cache_size
289
- # return _bytes_for_array_types(array_types, num_elements, num_elements / nc,
290
- # num_circuits, num_circuits / nc,
291
- # (num_params, num_params), (num_params / np1, num_params / np2),
292
- # approx_cachesize, self.model.dim)
293
-
294
317
  GB = 1.0 / 1024.0**3
295
318
  if mem_estimate > mem_limit:
296
319
  raise MemoryError("Not enough memory for desired layout! (limit=%.1fGB, required=%.1fGB)" % (
@@ -299,6 +322,41 @@ class MapForwardSimulator(_DistributableForwardSimulator, SimpleMapForwardSimula
299
322
  printer.log(" Esimated memory required = %.1fGB" % (mem_estimate * GB))
300
323
 
301
324
  return layout
325
+
326
+ @staticmethod
327
+ def create_copa_layout_circuit_cache(circuits, model, dataset=None):
328
+ """
329
+ Helper function for pre-computing/pre-processing circuits structures
330
+ used in matrix layout creation.
331
+ """
332
+ cache = dict()
333
+ completed_circuits = model.complete_circuits(circuits)
334
+
335
+ cache['completed_circuits'] = {ckt: comp_ckt for ckt, comp_ckt in zip(circuits, completed_circuits)}
336
+
337
+ split_circuits = model.split_circuits(completed_circuits, split_prep=False)
338
+ cache['split_circuits'] = {ckt: split_ckt for ckt, split_ckt in zip(circuits, split_circuits)}
339
+
340
+
341
+ if dataset is not None:
342
+ aliases = circuits.op_label_aliases if isinstance(circuits, _CircuitList) else None
343
+ ds_circuits = _lt.apply_aliases_to_circuits(circuits, aliases)
344
+ unique_outcomes_list = []
345
+ for ckt in ds_circuits:
346
+ ds_row = dataset[ckt]
347
+ unique_outcomes_list.append(ds_row.unique_outcomes if ds_row is not None else None)
348
+ else:
349
+ unique_outcomes_list = [None]*len(circuits)
350
+
351
+ expanded_circuit_outcome_list = model.bulk_expand_instruments_and_separate_povm(circuits,
352
+ observed_outcomes_list = unique_outcomes_list,
353
+ completed_circuits= completed_circuits)
354
+
355
+ expanded_circuit_cache = {ckt: expanded_ckt for ckt,expanded_ckt in zip(completed_circuits, expanded_circuit_outcome_list)}
356
+ cache['expanded_and_separated_circuits'] = expanded_circuit_cache
357
+
358
+ return cache
359
+
302
360
 
303
361
  def _bulk_fill_probs_atom(self, array_to_fill, layout_atom, resource_alloc):
304
362
  # Note: *don't* set dest_indices arg = layout.element_slice, as this is already done by caller
@@ -1,6 +1,4 @@
1
1
  # encoding: utf-8
2
- # cython: profile=False
3
- # cython: linetrace=False
4
2
 
5
3
  #***************************************************************************************************
6
4
  # Copyright 2015, 2019 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
@@ -28,12 +26,6 @@ from ..tools import slicetools as _slct
28
26
  #from ..tools import optools as _ot
29
27
  from ..tools.matrixtools import _fas
30
28
 
31
- #DEBUG REMOVE MEMORY PROFILING
32
- #import os, psutil
33
- #process = psutil.Process(os.getpid())
34
- #def print_mem_usage(prefix):
35
- # print("%s: mem usage = %.3f GB" % (prefix, process.memory_info().rss / (1024.0**3)))
36
-
37
29
  #Use 64-bit integers
38
30
  ctypedef long long INT
39
31
  ctypedef unsigned long long UINT
@@ -59,15 +51,16 @@ def propagate_staterep(staterep, operationreps):
59
51
  # Python -> C Conversion functions
60
52
  # -----------------------------------------
61
53
 
62
- cdef vector[vector[INT]] convert_maplayout(layout_atom, operation_lookup, rho_lookup):
54
+ @cython.wraparound(False)
55
+ cdef vector[vector[INT]] convert_maplayout(prefix_table_contents, operation_lookup, rho_lookup):
63
56
  # c_layout :
64
57
  # an array of INT-arrays; each INT-array is [iDest,iStart,iCache,<remainder gate indices>]
65
58
  cdef vector[INT] intarray
66
- cdef vector[vector[INT]] c_layout_atom = vector[vector[INT]](len(layout_atom.table))
67
- for kk, (iDest, iStart, remainder, iCache) in enumerate(layout_atom.table.contents):
59
+ cdef vector[vector[INT]] c_layout_atom = vector[vector[INT]](len(prefix_table_contents))
60
+ for kk, (iDest, iStart, remainder, iCache) in enumerate(prefix_table_contents):
68
61
  if iStart is None: iStart = -1 # so always an int
69
62
  if iCache is None: iCache = -1 # so always an int
70
- remainder = remainder.circuit_without_povm.layertup
63
+ remainder = remainder
71
64
  intarray = vector[INT](3 + len(remainder))
72
65
  intarray[0] = iDest
73
66
  intarray[1] = iStart
@@ -107,6 +100,8 @@ cdef vector[vector[INT]] convert_and_wrap_dict_of_intlists(d, wrapper):
107
100
  ret[i][j] = wrapper[intlist[j]]
108
101
  return ret
109
102
 
103
+ @cython.boundscheck(False)
104
+ @cython.wraparound(False)
110
105
  cdef vector[StateCRep*] create_rhocache(INT cacheSize, INT state_dim):
111
106
  cdef INT i
112
107
  cdef vector[StateCRep*] rho_cache = vector[StateCRep_ptr](cacheSize)
@@ -114,11 +109,14 @@ cdef vector[StateCRep*] create_rhocache(INT cacheSize, INT state_dim):
114
109
  rho_cache[i] = new StateCRep(state_dim)
115
110
  return rho_cache
116
111
 
112
+ @cython.boundscheck(False)
113
+ @cython.wraparound(False)
117
114
  cdef void free_rhocache(vector[StateCRep*] rho_cache):
118
115
  cdef UINT i
119
116
  for i in range(rho_cache.size()): # fill cache with empty but alloc'd states
120
117
  del rho_cache[i]
121
118
 
119
+ @cython.wraparound(False)
122
120
  cdef vector[OpCRep*] convert_opreps(operationreps):
123
121
  # c_opreps : an array of OpCReps
124
122
  cdef vector[OpCRep*] c_opreps = vector[OpCRep_ptr](len(operationreps))
@@ -130,19 +128,20 @@ cdef StateCRep* convert_rhorep(rhorep):
130
128
  # extract c-reps from rhorep and ereps => c_rho and c_ereps
131
129
  return (<StateRep?>rhorep).c_state
132
130
 
131
+ @cython.wraparound(False)
133
132
  cdef vector[StateCRep*] convert_rhoreps(rhoreps):
134
133
  cdef vector[StateCRep*] c_rhoreps = vector[StateCRep_ptr](len(rhoreps))
135
134
  for ii,rrep in rhoreps.items(): # (ii = python variable)
136
135
  c_rhoreps[ii] = (<StateRep?>rrep).c_state
137
136
  return c_rhoreps
138
137
 
138
+ @cython.wraparound(False)
139
139
  cdef vector[EffectCRep*] convert_ereps(ereps):
140
140
  cdef vector[EffectCRep*] c_ereps = vector[EffectCRep_ptr](len(ereps))
141
141
  for i in range(len(ereps)):
142
142
  c_ereps[i] = (<EffectRep>ereps[i]).c_effect
143
143
  return c_ereps
144
144
 
145
-
146
145
  # -----------------------------------------
147
146
  # Mapfill functions
148
147
  # -----------------------------------------
@@ -159,7 +158,6 @@ def mapfill_probs_atom(fwdsim, np.ndarray[double, mode="c", ndim=1] array_to_fil
159
158
  shared_mem_leader = resource_alloc.is_host_leader if (resource_alloc is not None) else True
160
159
 
161
160
  dest_indices = _slct.to_array(dest_indices) # make sure this is an array and not a slice
162
- #dest_indices = np.ascontiguousarray(dest_indices) #unneeded
163
161
 
164
162
  #Get (extension-type) representation objects
165
163
  rho_lookup = { lbl:i for i,lbl in enumerate(layout_atom.rho_labels) } # rho labels -> ints for faster lookup
@@ -169,7 +167,7 @@ def mapfill_probs_atom(fwdsim, np.ndarray[double, mode="c", ndim=1] array_to_fil
169
167
  ereps = [fwdsim.model._circuit_layer_operator(elbl, 'povm')._rep for elbl in layout_atom.full_effect_labels] # cache these in future
170
168
 
171
169
  # convert to C-mode: evaltree, operation_lookup, operationreps
172
- cdef vector[vector[INT]] c_layout_atom = convert_maplayout(layout_atom, operation_lookup, rho_lookup)
170
+ cdef vector[vector[INT]] c_layout_atom = convert_maplayout(layout_atom.table.contents, operation_lookup, rho_lookup)
173
171
  cdef vector[StateCRep*] c_rhos = convert_rhoreps(rhoreps)
174
172
  cdef vector[EffectCRep*] c_ereps = convert_ereps(ereps)
175
173
  cdef vector[OpCRep*] c_opreps = convert_opreps(operationreps)
@@ -182,17 +180,6 @@ def mapfill_probs_atom(fwdsim, np.ndarray[double, mode="c", ndim=1] array_to_fil
182
180
  cdef vector[vector[INT]] final_indices_per_circuit = convert_and_wrap_dict_of_intlists(
183
181
  layout_atom.elindices_by_expcircuit, dest_indices)
184
182
 
185
- #DEBUG REMOVE
186
- #print_mem_usage("MAPFILL PROBS begin")
187
- #for i in [1808, 419509, 691738, 497424]:
188
- # from ..evotypes.densitymx.opreps import OpRepComposed
189
- # op = operationreps[i]
190
- # if isinstance(op.embedded_rep, OpRepComposed):
191
- # extra = " factors = " + ', '.join([str(type(opp)) for opp in op.embedded_rep.factor_reps])
192
- # else:
193
- # extra = ""
194
- # print("ID ",i,str(type(op)),str(type(op.embedded_rep)), extra)
195
-
196
183
  if shared_mem_leader:
197
184
  #Note: dm_mapfill_probs could have taken a resource_alloc to employ multiple cpus to do computation.
198
185
  # Since array_fo_fill is assumed to be shared mem it would need to only update `array_to_fill` *if*
@@ -203,6 +190,7 @@ def mapfill_probs_atom(fwdsim, np.ndarray[double, mode="c", ndim=1] array_to_fil
203
190
  free_rhocache(rho_cache) #delete cache entries
204
191
 
205
192
 
193
+ @cython.wraparound(False)
206
194
  cdef dm_mapfill_probs(double[:] array_to_fill,
207
195
  vector[vector[INT]] c_layout_atom,
208
196
  vector[OpCRep*] c_opreps,
@@ -234,7 +222,7 @@ cdef dm_mapfill_probs(double[:] array_to_fill,
234
222
  # - all rho_cache entries have been allocated via "new"
235
223
  #REMOVE print("MAPFILL PROBS begin cfn")
236
224
  for k in range(<INT>c_layout_atom.size()):
237
- t0 = pytime.time() # DEBUG
225
+ #t0 = pytime.time() # DEBUG
238
226
  intarray = c_layout_atom[k]
239
227
  i = intarray[0]
240
228
  istart = intarray[1]
@@ -298,22 +286,27 @@ cdef dm_mapfill_probs(double[:] array_to_fill,
298
286
  del prop2
299
287
  del shelved
300
288
 
301
-
289
+ @cython.wraparound(False)
302
290
  def mapfill_dprobs_atom(fwdsim,
303
291
  np.ndarray[double, ndim=2] array_to_fill,
304
292
  dest_indices,
305
293
  dest_param_indices,
306
294
  layout_atom, param_indices, resource_alloc, double eps):
307
295
 
308
- #cdef double eps = 1e-7
296
+ cdef int num_params = fwdsim.model.num_params
297
+ cdef int model_dim = fwdsim.model.dim
309
298
 
310
299
  if param_indices is None:
311
- param_indices = list(range(fwdsim.model.num_params))
300
+ param_indices = list(range(num_params))
312
301
  if dest_param_indices is None:
313
302
  dest_param_indices = list(range(_slct.length(param_indices)))
314
303
 
315
- param_indices = _slct.to_array(param_indices)
316
- dest_param_indices = _slct.to_array(dest_param_indices)
304
+ cdef np.ndarray[np.int64_t, ndim=1, mode='c'] param_indices_array = _slct.to_array(param_indices)
305
+ cdef np.ndarray[np.int64_t, ndim=1, mode='c'] dest_param_indices_array = _slct.to_array(dest_param_indices)
306
+
307
+ cdef np.int64_t[::1] param_indices_view = param_indices_array
308
+ cdef np.int64_t[::1] dest_param_indices_view = dest_param_indices_array
309
+
317
310
 
318
311
  #Get (extension-type) representation objects
319
312
  # NOTE: the circuit_layer_operator(lbl) functions cache the returned operation
@@ -327,7 +320,7 @@ def mapfill_dprobs_atom(fwdsim,
327
320
  ereps = [fwdsim.model._circuit_layer_operator(elbl, 'povm')._rep for elbl in layout_atom.full_effect_labels] # cache these in future
328
321
 
329
322
  # convert to C-mode: evaltree, operation_lookup, operationreps
330
- cdef vector[vector[INT]] c_layout_atom = convert_maplayout(layout_atom, operation_lookup, rho_lookup)
323
+ cdef vector[vector[INT]] c_layout_atom = convert_maplayout(layout_atom.table.contents, operation_lookup, rho_lookup)
331
324
  cdef vector[StateCRep*] c_rhos = convert_rhoreps(rhoreps)
332
325
  cdef vector[EffectCRep*] c_ereps = convert_ereps(ereps)
333
326
  cdef vector[OpCRep*] c_opreps = convert_opreps(operationreps)
@@ -335,53 +328,60 @@ def mapfill_dprobs_atom(fwdsim,
335
328
  # create rho_cache = vector of StateCReps
336
329
  #print "DB: creating rho_cache of size %d * %g GB => %g GB" % \
337
330
  # (layout_atom.cache_size, 8.0 * fwdsim.model.dim / 1024.0**3, layout_atom.cache_size * 8.0 * fwdsim.model.dim / 1024.0**3)
338
- cdef vector[StateCRep*] rho_cache = create_rhocache(layout_atom.cache_size, fwdsim.model.dim)
331
+ cdef vector[StateCRep*] rho_cache = create_rhocache(layout_atom.cache_size, model_dim)
339
332
 
340
333
  cdef vector[vector[INT]] elabel_indices_per_circuit = convert_dict_of_intlists(layout_atom.elbl_indices_by_expcircuit)
341
334
  cdef vector[vector[INT]] final_indices_per_circuit = convert_dict_of_intlists(layout_atom.elindices_by_expcircuit)
342
335
 
343
- orig_vec = fwdsim.model.to_vector().copy()
336
+ cdef np.ndarray[np.float64_t, ndim=1, mode='c'] orig_vec = fwdsim.model.to_vector().copy()
337
+ cdef double[::1] orig_vec_view = orig_vec
344
338
  fwdsim.model.from_vector(orig_vec, close=False) # ensure we call with close=False first
345
339
 
346
340
  nEls = layout_atom.num_elements
347
- probs = np.empty(nEls, 'd') #must be contiguous!
348
- probs2 = np.empty(nEls, 'd') #must be contiguous!
341
+ cdef np.ndarray[np.float64_t, ndim=1, mode='c'] probs = np.empty(nEls, dtype=np.float64) #must be contiguous!
342
+ cdef np.ndarray[np.float64_t, ndim=1, mode='c'] probs2 = np.empty(nEls, dtype=np.float64) #must be contiguous!
343
+
344
+ cdef double[::1] probs_view = probs
345
+ cdef double[::1] probs2_view = probs2
349
346
 
350
347
  #if resource_alloc.comm_rank == 0:
351
348
  # print("MAPFILL DPROBS ATOM 1"); t=pytime.time(); t0=pytime.time()
352
- dm_mapfill_probs(probs, c_layout_atom, c_opreps, c_rhos, c_ereps, &rho_cache,
353
- elabel_indices_per_circuit, final_indices_per_circuit, fwdsim.model.dim)
349
+ dm_mapfill_probs(probs_view, c_layout_atom, c_opreps, c_rhos, c_ereps, &rho_cache, elabel_indices_per_circuit, final_indices_per_circuit, model_dim)
354
350
  #if resource_alloc.comm_rank == 0:
355
351
  # print("MAPFILL DPROBS ATOM 2 %.3fs" % (pytime.time() - t)); t=pytime.time()
356
352
 
357
- shared_mem_leader = resource_alloc.is_host_leader
358
-
359
- #Get a map from global parameter indices to the desired
360
- # final index within array_to_fill
361
- iParamToFinal = {i: dest_index for i, dest_index in zip(param_indices, dest_param_indices)}
353
+ cdef bint shared_mem_leader = resource_alloc.is_host_leader
354
+
355
+ #add typing to indices we'll be using below:
356
+ cdef int i
357
+ cdef int first_param_idx
358
+ cdef int iFinal
359
+
360
+ #Split off the first finite difference step, as the pattern I want in the loop with each step
361
+ #is to simultaneously undo the previous update and apply the new one.
362
+ if len(param_indices_view)>0:
363
+ #probs2_view[:] = probs_view[:]
364
+ first_param_idx = param_indices_view[0]
365
+ iFinal = dest_param_indices_view[0]
366
+ fwdsim.model.set_parameter_value(first_param_idx, orig_vec_view[first_param_idx]+eps)
367
+ if shared_mem_leader: # don't fill assumed-shared array-to_fill on non-mem-leaders
368
+ dm_mapfill_probs(probs2_view, c_layout_atom, c_opreps, c_rhos, c_ereps, &rho_cache, elabel_indices_per_circuit, final_indices_per_circuit, model_dim)
369
+ array_to_fill[dest_indices, iFinal] = (probs2 - probs) / eps
370
+
371
+ for i in range(1, len(param_indices_view)):
372
+ #probs2_view[:] = probs_view[:]
373
+ iFinal = dest_param_indices_view[i]
374
+ fwdsim.model.set_parameter_values([param_indices_view[i-1], param_indices_view[i]], [orig_vec_view[param_indices_view[i-1]], orig_vec_view[param_indices_view[i]]+eps])
375
+
376
+ if shared_mem_leader: # don't fill assumed-shared array-to_fill on non-mem-leaders
377
+ dm_mapfill_probs(probs2_view, c_layout_atom, c_opreps, c_rhos, c_ereps, &rho_cache, elabel_indices_per_circuit, final_indices_per_circuit, model_dim)
378
+ array_to_fill[dest_indices, iFinal] = (probs2 - probs) / eps
379
+
380
+ #reset the final model parameter we changed to it's original value.
381
+ fwdsim.model.set_parameter_value(param_indices_view[len(param_indices_view)-1], orig_vec_view[param_indices_view[len(param_indices_view)-1]])
362
382
 
363
- for i in range(fwdsim.model.num_params):
364
- #print("dprobs cache %d of %d" % (i,self.Np))
365
- if i in iParamToFinal:
366
- #if resource_alloc.comm_rank == 0:
367
- # print("MAPFILL DPROBS ATOM 3 (i=%d) %.3fs elapssed=%.1fs" % (i, pytime.time() - t, pytime.time() - t0)); t=pytime.time()
368
- iFinal = iParamToFinal[i]
369
- vec = orig_vec.copy(); vec[i] += eps
370
- fwdsim.model.from_vector(vec, close=True)
371
- #Note: dm_mapfill_probs could have taken a resource_alloc to employ multiple cpus to do computation.
372
- # If probs2 were shared mem (seems not benefit to this?) it would need to only update `probs2` *if*
373
- # it were the host leader.
374
- if shared_mem_leader: # don't fill assumed-shared array-to_fill on non-mem-leaders
375
- dm_mapfill_probs(probs2, c_layout_atom, c_opreps, c_rhos, c_ereps, &rho_cache,
376
- elabel_indices_per_circuit, final_indices_per_circuit, fwdsim.model.dim)
377
- #_fas(array_to_fill, [dest_indices, iFinal], (probs2 - probs) / eps) # I don't think this is needed
378
- array_to_fill[dest_indices, iFinal] = (probs2 - probs) / eps
379
-
380
- #if resource_alloc.comm_rank == 0:
381
- # print("MAPFILL DPROBS ATOM 4 elapsed=%.1fs" % (pytime.time() - t0))
382
- fwdsim.model.from_vector(orig_vec, close=True)
383
383
  free_rhocache(rho_cache) #delete cache entries
384
-
384
+
385
385
 
386
386
  cdef double TDchi2_obj_fn(double p, double f, double n_i, double n, double omitted_p, double min_prob_clip_for_weighting, double extra):
387
387
  cdef double cp, v, omitted_cp
@@ -475,7 +475,6 @@ def mapfill_TDterms(fwdsim, objective, array_to_fill, dest_indices, num_outcomes
475
475
  #comm is currently ignored
476
476
  #TODO: if layout_atom is split, distribute among processors
477
477
  for iDest, iStart, remainder, iCache in layout_atom.table.contents:
478
- remainder = remainder.circuit_without_povm.layertup
479
478
  rholabel = remainder[0]; remainder = remainder[1:]
480
479
  rhoVec = fwdsim.model._circuit_layer_operator(rholabel, 'prep')
481
480
 
@@ -51,7 +51,6 @@ def mapfill_probs_atom(fwdsim, mx_to_fill, dest_indices, layout_atom, resource_a
51
51
 
52
52
  #TODO: if layout_atom is split, distribute somehow among processors(?) instead of punting for all but rank-0 above
53
53
  for iDest, iStart, remainder, iCache in layout_atom.table.contents:
54
- remainder = remainder.circuit_without_povm.layertup
55
54
 
56
55
  if iStart is None: # then first element of remainder is a state prep label
57
56
  rholabel = remainder[0]
@@ -73,7 +72,63 @@ def mapfill_probs_atom(fwdsim, mx_to_fill, dest_indices, layout_atom, resource_a
73
72
  mx_to_fill[final_indices] = povmreps[povm_lbl].probabilities(final_state, None, effect_labels)
74
73
  else:
75
74
  ereps = [effectreps[j] for j in layout_atom.elbl_indices_by_expcircuit[iDest]]
75
+ #print(ereps)
76
+ if shared_mem_leader:
77
+ for j, erep in zip(final_indices, ereps):
78
+ mx_to_fill[j] = erep.probability(final_state) # outcome probability
79
+ #raise Exception
80
+ #Version of the probability calculation that updates circuit probabilities conditionally based on
81
+ #Whether the circuit is sensitive to the parameter. If not we leave that circuit alone.
82
+ def cond_update_probs_atom(fwdsim, mx_to_fill, dest_indices, layout_atom, param_index, resource_alloc):
83
+
84
+ # The required ending condition is that array_to_fill on each processor has been filled. But if
85
+ # memory is being shared and resource_alloc contains multiple processors on a single host, we only
86
+ # want *one* (the rank=0) processor to perform the computation, since array_to_fill will be
87
+ # shared memory that we don't want to have muliple procs using simultaneously to compute the
88
+ # same thing. Thus, we carefully guard any shared mem updates/usage
89
+ # using "if shared_mem_leader" (and barriers, if needed) below.
90
+ shared_mem_leader = resource_alloc.is_host_leader if (resource_alloc is not None) else True
91
+
92
+ dest_indices = _slct.to_array(dest_indices) # make sure this is an array and not a slice
93
+ cacheSize = layout_atom.jac_table.cache_size_by_parameter[param_index]
94
+
95
+ #Create rhoCache
96
+ rho_cache = [None] * cacheSize # so we can store (s,p) tuples in cache
97
+
98
+ #Get operationreps and ereps now so we don't make unnecessary ._rep references
99
+ rhoreps = {rholbl: fwdsim.model._circuit_layer_operator(rholbl, 'prep')._rep for rholbl in layout_atom.rho_labels}
100
+ operationreps = {gl: fwdsim.model._circuit_layer_operator(gl, 'op')._rep for gl in layout_atom.op_labels}
101
+ povmreps = {plbl: fwdsim.model._circuit_layer_operator(plbl, 'povm')._rep for plbl in layout_atom.povm_labels}
102
+ if any([(povmrep is None) for povmrep in povmreps.values()]):
103
+ effectreps = {i: fwdsim.model._circuit_layer_operator(Elbl, 'povm')._rep
104
+ for i, Elbl in enumerate(layout_atom.full_effect_labels)} # cache these in future
105
+ else:
106
+ effectreps = None # not needed, as we use povm reps directly
107
+
108
+
109
+ #TODO: if layout_atom is split, distribute somehow among processors(?) instead of punting for all but rank-0 above
110
+
111
+ for iDest, iStart, remainder, iCache in layout_atom.jac_table.contents_by_parameter[param_index]:
112
+
113
+ if iStart is None: # then first element of remainder is a state prep label
114
+ rholabel = remainder[0]
115
+ init_state = rhoreps[rholabel]
116
+ remainder = remainder[1:]
117
+ else:
118
+ init_state = rho_cache[iStart] # [:,None]
119
+
120
+ final_state = propagate_staterep(init_state, [operationreps[gl] for gl in remainder])
121
+ if iCache is not None: rho_cache[iCache] = final_state # [:,0] #store this state in the cache
122
+
123
+ final_indices = [dest_indices[j] for j in layout_atom.elindices_by_expcircuit[iDest]]
124
+
125
+ if effectreps is None:
126
+ povm_lbl, *effect_labels = layout_atom.povm_and_elbls_by_expcircuit[iDest]
76
127
 
128
+ if shared_mem_leader:
129
+ mx_to_fill[final_indices] = povmreps[povm_lbl].probabilities(final_state, None, effect_labels)
130
+ else:
131
+ ereps = [effectreps[j] for j in layout_atom.elbl_indices_by_expcircuit[iDest]]
77
132
  if shared_mem_leader:
78
133
  for j, erep in zip(final_indices, ereps):
79
134
  mx_to_fill[j] = erep.probability(final_state) # outcome probability
@@ -82,11 +137,10 @@ def mapfill_probs_atom(fwdsim, mx_to_fill, dest_indices, layout_atom, resource_a
82
137
  def mapfill_dprobs_atom(fwdsim, mx_to_fill, dest_indices, dest_param_indices, layout_atom, param_indices,
83
138
  resource_alloc, eps):
84
139
 
85
- #eps = 1e-7
86
- #shared_mem_leader = resource_alloc.is_host_leader if (resource_alloc is not None) else True
140
+ num_params = fwdsim.model.num_params
87
141
 
88
142
  if param_indices is None:
89
- param_indices = list(range(fwdsim.model.num_params))
143
+ param_indices = list(range(num_params))
90
144
  if dest_param_indices is None:
91
145
  dest_param_indices = list(range(_slct.length(param_indices)))
92
146
 
@@ -105,19 +159,43 @@ def mapfill_dprobs_atom(fwdsim, mx_to_fill, dest_indices, dest_param_indices, la
105
159
  nEls = layout_atom.num_elements
106
160
  probs, shm = _smt.create_shared_ndarray(resource_alloc, (nEls,), 'd', memory_tracker=None)
107
161
  probs2, shm2 = _smt.create_shared_ndarray(resource_alloc, (nEls,), 'd', memory_tracker=None)
162
+ #probs2_test, shm2_test = _smt.create_shared_ndarray(resource_alloc, (nEls,), 'd', memory_tracker=None)
163
+
164
+ #mx_to_fill_test = mx_to_fill.copy()
165
+
108
166
  mapfill_probs_atom(fwdsim, probs, slice(0, nEls), layout_atom, resource_alloc) # probs != shared
109
167
 
110
- for i in range(fwdsim.model.num_params):
111
- #print("dprobs cache %d of %d" % (i,self.Np))
112
- if i in iParamToFinal:
113
- iFinal = iParamToFinal[i]
114
- vec = orig_vec.copy(); vec[i] += eps
115
- fwdsim.model.from_vector(vec, close=True)
116
- mapfill_probs_atom(fwdsim, probs2, slice(0, nEls), layout_atom, resource_alloc)
117
- _fas(mx_to_fill, [dest_indices, iFinal], (probs2 - probs) / eps)
118
- fwdsim.model.from_vector(orig_vec, close=True)
168
+ #Split off the first finite difference step, as the pattern I want in the loop with each step
169
+ #is to simultaneously undo the previous update and apply the new one.
170
+ if len(param_indices)>0:
171
+ probs2[:] = probs[:]
172
+ first_param_idx = param_indices[0]
173
+ iFinal = iParamToFinal[first_param_idx]
174
+ fwdsim.model.set_parameter_value(first_param_idx, orig_vec[first_param_idx]+eps)
175
+ #mapfill_probs_atom(fwdsim, probs2, slice(0, nEls), layout_atom, resource_alloc)
176
+ cond_update_probs_atom(fwdsim, probs2, slice(0, nEls), layout_atom, first_param_idx, resource_alloc)
177
+ #assert _np.linalg.norm(probs2_test-probs2) < 1e-10
178
+ #print(f'{_np.linalg.norm(probs2_test-probs2)=}')
179
+ _fas(mx_to_fill, [dest_indices, iFinal], (probs2 - probs) / eps)
180
+
181
+
182
+ for i in range(1, len(param_indices)):
183
+ probs2[:] = probs[:]
184
+ iFinal = iParamToFinal[param_indices[i]]
185
+ fwdsim.model.set_parameter_values([param_indices[i-1], param_indices[i]],
186
+ [orig_vec[param_indices[i-1]], orig_vec[param_indices[i]]+eps])
187
+ #mapfill_probs_atom(fwdsim, probs2, slice(0, nEls), layout_atom, resource_alloc)
188
+ cond_update_probs_atom(fwdsim, probs2, slice(0, nEls), layout_atom, param_indices[i], resource_alloc)
189
+ #assert _np.linalg.norm(probs2_test-probs2) < 1e-10
190
+ #print(f'{_np.linalg.norm(probs2_test-probs2)=}')
191
+ _fas(mx_to_fill, [dest_indices, iFinal], (probs2 - probs) / eps)
192
+
193
+ #reset the final model parameter we changed to it's original value.
194
+ fwdsim.model.set_parameter_value(param_indices[-1], orig_vec[param_indices[-1]])
195
+
119
196
  _smt.cleanup_shared_ndarray(shm)
120
197
  _smt.cleanup_shared_ndarray(shm2)
198
+ #_smt.cleanup_shared_ndarray(shm2_test)
121
199
 
122
200
 
123
201
  def mapfill_TDchi2_terms(fwdsim, array_to_fill, dest_indices, num_outcomes, layout_atom, dataset_rows,