pyGSTi 0.9.12__cp39-cp39-win32.whl → 0.9.13__cp39-cp39-win32.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.cp39-win32.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.cp39-win32.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.cp39-win32.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.cp39-win32.pyd +0 -0
  41. pygsti/evotypes/densitymx/effectreps.pyx +1 -1
  42. pygsti/evotypes/densitymx/opreps.cp39-win32.pyd +0 -0
  43. pygsti/evotypes/densitymx/opreps.pyx +2 -2
  44. pygsti/evotypes/densitymx/statereps.cp39-win32.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.cp39-win32.pyd +0 -0
  51. pygsti/evotypes/stabilizer/effectreps.pyx +0 -4
  52. pygsti/evotypes/stabilizer/opreps.cp39-win32.pyd +0 -0
  53. pygsti/evotypes/stabilizer/opreps.pyx +0 -4
  54. pygsti/evotypes/stabilizer/statereps.cp39-win32.pyd +0 -0
  55. pygsti/evotypes/stabilizer/statereps.pyx +1 -5
  56. pygsti/evotypes/stabilizer/termreps.cp39-win32.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.cp39-win32.pyd +0 -0
  62. pygsti/evotypes/statevec/effectreps.pyx +1 -1
  63. pygsti/evotypes/statevec/opreps.cp39-win32.pyd +0 -0
  64. pygsti/evotypes/statevec/opreps.pyx +2 -2
  65. pygsti/evotypes/statevec/statereps.cp39-win32.pyd +0 -0
  66. pygsti/evotypes/statevec/statereps.pyx +1 -1
  67. pygsti/evotypes/statevec/termreps.cp39-win32.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.cp39-win32.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.cp39-win32.pyd +0 -0
  87. pygsti/forwardsims/termforwardsim_calc_statevec.cp39-win32.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.cp39-win32.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
@@ -0,0 +1,265 @@
1
+ """
2
+ Defines a ForwardSimulator class called "TorchForwardSimulator" that can leverage the automatic
3
+ differentation features of PyTorch.
4
+
5
+ This file also defines two helper classes: StatelessCircuit and StatelessModel.
6
+
7
+ See also: pyGSTi/modelmembers/torchable.py.
8
+ """
9
+ #***************************************************************************************************
10
+ # Copyright 2024, National Technology & Engineering Solutions of Sandia, LLC (NTESS).
11
+ # Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain rights
12
+ # in this software.
13
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
14
+ # in compliance with the License. You may obtain a copy of the License at
15
+ # http://www.apache.org/licenses/LICENSE-2.0 or in the LICENSE file in the root pyGSTi directory.
16
+ #***************************************************************************************************
17
+
18
+
19
+ from __future__ import annotations
20
+ from typing import Tuple, Optional, Dict, TYPE_CHECKING
21
+ if TYPE_CHECKING:
22
+ from pygsti.baseobjs.label import Label
23
+ from pygsti.models.explicitmodel import ExplicitOpModel
24
+ from pygsti.circuits.circuit import SeparatePOVMCircuit
25
+ from pygsti.layouts.copalayout import CircuitOutcomeProbabilityArrayLayout
26
+ import torch
27
+
28
+ import warnings as warnings
29
+ from pygsti.modelmembers.torchable import Torchable
30
+ from pygsti.forwardsims.forwardsim import ForwardSimulator
31
+
32
+ try:
33
+ import torch
34
+ TORCH_ENABLED = True
35
+ except ImportError:
36
+ TORCH_ENABLED = False
37
+ pass
38
+
39
+
40
+ class StatelessCircuit:
41
+ """
42
+ Helper data structure for specifying a quantum circuit (consisting of prep,
43
+ applying a sequence of gates, and applying a POVM to the output of the last gate).
44
+ """
45
+
46
+ def __init__(self, spc: SeparatePOVMCircuit):
47
+ self.prep_label = spc.circuit_without_povm[0]
48
+ self.op_labels = spc.circuit_without_povm[1:]
49
+ self.povm_label = spc.povm_label
50
+ self.outcome_probs_dim = len(spc.effect_labels)
51
+ # ^ This definition of outcome_probs_dim will need to be changed if/when
52
+ # we extend any Instrument class to be Torchable.
53
+ return
54
+
55
+
56
+ class StatelessModel:
57
+ """
58
+ A container for the information in an ExplicitOpModel that's "stateless" in the sense of
59
+ object-oriented programming:
60
+
61
+ * A list of StatelessCircuits
62
+ * Metadata for parameterized ModelMembers
63
+
64
+ StatelessModels have instance functions to facilitate computation of (differentable!)
65
+ circuit outcome probabilities.
66
+
67
+ Design notes
68
+ ------------
69
+ Much of this functionality could be packed into the TorchForwardSimulator class.
70
+ Keeping it separate from TorchForwardSimulator helps clarify that it uses none of
71
+ the sophiciated machinery in TorchForwardSimulator's base class.
72
+ """
73
+
74
+ def __init__(self, model: ExplicitOpModel, layout: CircuitOutcomeProbabilityArrayLayout):
75
+ circuits = []
76
+ self.outcome_probs_dim = 0
77
+ #TODO: Refactor this to use the bulk_expand_instruments_and_separate_povm codepath
78
+ for _, circuit, outcomes in layout.iter_unique_circuits():
79
+ expanded_circuits = model.expand_instruments_and_separate_povm(circuit, outcomes)
80
+ if len(expanded_circuits) > 1:
81
+ raise NotImplementedError("I don't know what to do with this.")
82
+ spc = next(iter(expanded_circuits))
83
+ c = StatelessCircuit(spc)
84
+ circuits.append(c)
85
+ self.outcome_probs_dim += c.outcome_probs_dim
86
+ self.circuits = circuits
87
+
88
+ # We need to verify assumptions on what layout.iter_unique_circuits() returns.
89
+ # Looking at the implementation of that function, the assumptions can be
90
+ # framed in terms of the "layout._element_indicies" dict.
91
+ eind = layout._element_indices
92
+ assert isinstance(eind, dict)
93
+ items = iter(eind.items())
94
+ k_prev, v_prev = next(items)
95
+ assert k_prev == 0
96
+ assert v_prev.start == 0
97
+ for k, v in items:
98
+ assert k == k_prev + 1
99
+ assert v.start == v_prev.stop
100
+ k_prev = k
101
+ v_prev = v
102
+ assert self.outcome_probs_dim == v_prev.stop
103
+
104
+ self.param_metadata = []
105
+ for lbl, obj in model._iter_parameterized_objs():
106
+ assert isinstance(obj, Torchable), f"{type(obj)} does not subclass {Torchable}."
107
+ param_type = type(obj)
108
+ param_data = (lbl, param_type) + (obj.stateless_data(),)
109
+ self.param_metadata.append(param_data)
110
+ self.params_dim = None
111
+ # ^ That's set in get_free_params.
112
+
113
+ self.default_to_reverse_ad = None
114
+ # ^ That'll be set to a boolean the next time that get_free_params is called.
115
+ return
116
+
117
+ def get_free_params(self, model: ExplicitOpModel) -> Tuple[torch.Tensor]:
118
+ """
119
+ Return a tuple of Tensors that encode the states of the provided model's ModelMembers
120
+ (where "state" in meant the sense of object-oriented programming).
121
+
122
+ We compare the labels of the input model's ModelMembers to those of the model provided
123
+ to StatelessModel.__init__(...). We raise an error if an inconsistency is detected.
124
+ """
125
+ free_params = []
126
+ prev_idx = 0
127
+ for i, (lbl, obj) in enumerate(model._iter_parameterized_objs()):
128
+ gpind = obj.gpindices_as_array()
129
+ vec = obj.to_vector()
130
+ vec_size = vec.size
131
+ vec = torch.from_numpy(vec)
132
+ assert gpind[0] == prev_idx and gpind[-1] == prev_idx + vec_size - 1
133
+ # ^ We should have gpind = (prev_idx, prev_idx + 1, ..., prev_idx + vec.size - 1).
134
+ # That assert checks a cheap necessary condition that this holds.
135
+ prev_idx += vec_size
136
+ if self.param_metadata[i][0] != lbl:
137
+ message = """
138
+ The model passed to get_free_params has a qualitatively different structure from
139
+ the model used to construct this StatelessModel. Specifically, the two models have
140
+ qualitative differences in the output of "model._iter_parameterized_objs()".
141
+
142
+ The presence of this structral difference essentially gaurantees that a subsequent
143
+ call to get_torch_bases would silently fail, so we're forced to raise an error here.
144
+ """
145
+ raise ValueError(message)
146
+ free_params.append(vec)
147
+ self.params_dim = prev_idx
148
+ self.default_to_reverse_ad = self.outcome_probs_dim < self.params_dim
149
+ return tuple(free_params)
150
+
151
+ def get_torch_bases(self, free_params: Tuple[torch.Tensor]) -> Dict[Label, torch.Tensor]:
152
+ """
153
+ Take data of the kind produced by get_free_params and format it in the way required by
154
+ circuit_probs_from_torch_bases.
155
+
156
+ Note
157
+ ----
158
+ If you want to use the returned dict to build a PyTorch Tensor that supports the
159
+ .backward() method, then you need to make sure that fp.requires_grad is True for all
160
+ fp in free_params. This can be done by calling fp._requires_grad(True) before calling
161
+ this function.
162
+ """
163
+ assert len(free_params) == len(self.param_metadata)
164
+ # ^ A sanity check that we're being called with the correct number of arguments.
165
+ torch_bases = dict()
166
+ for i, val in enumerate(free_params):
167
+
168
+ label, type_handle, stateless_data = self.param_metadata[i]
169
+ param_t = type_handle.torch_base(stateless_data, val)
170
+ torch_bases[label] = param_t
171
+
172
+ return torch_bases
173
+
174
+ def circuit_probs_from_torch_bases(self, torch_bases: Dict[Label, torch.Tensor]) -> torch.Tensor:
175
+ """
176
+ Compute the circuit outcome probabilities that result when all of this StatelessModel's
177
+ StatelessCircuits are run with data in torch_bases.
178
+
179
+ Return the results as a single (vectorized) torch Tensor.
180
+ """
181
+ probs = []
182
+ for c in self.circuits:
183
+ superket = torch_bases[c.prep_label]
184
+ superops = [torch_bases[ol] for ol in c.op_labels]
185
+ povm_mat = torch_bases[c.povm_label]
186
+ for superop in superops:
187
+ superket = superop @ superket
188
+ circuit_probs = povm_mat @ superket
189
+ probs.append(circuit_probs)
190
+ probs = torch.concat(probs)
191
+ return probs
192
+
193
+ def circuit_probs_from_free_params(self, *free_params: Tuple[torch.Tensor], enable_backward=False) -> torch.Tensor:
194
+ """
195
+ This is the basic function we expose to pytorch for automatic differentiation. It returns the circuit
196
+ outcome probabilities resulting when the states of ModelMembers associated with this StatelessModel
197
+ are set based on free_params.
198
+
199
+ If you want to call PyTorch's .backward() on the returned Tensor (or a function of that Tensor), then
200
+ you should set enable_backward=True. Keep the default value of enable_backward=False in all other
201
+ situations, including when using PyTorch's jacrev function.
202
+ """
203
+ if enable_backward:
204
+ for fp in free_params:
205
+ fp._requires_grad(True)
206
+ torch_bases = self.get_torch_bases(free_params)
207
+ probs = self.circuit_probs_from_torch_bases(torch_bases)
208
+ return probs
209
+
210
+
211
+ class TorchForwardSimulator(ForwardSimulator):
212
+ """
213
+ A forward simulator that leverages automatic differentiation in PyTorch.
214
+ """
215
+
216
+ ENABLED = TORCH_ENABLED
217
+
218
+ def __init__(self, model : Optional[ExplicitOpModel] = None):
219
+ if not self.ENABLED:
220
+ raise RuntimeError('PyTorch could not be imported.')
221
+ self.model = model
222
+ super(ForwardSimulator, self).__init__(model)
223
+
224
+ def _bulk_fill_probs(self, array_to_fill, layout, split_model = None) -> None:
225
+ if split_model is None:
226
+ slm = StatelessModel(self.model, layout)
227
+ free_params = slm.get_free_params(self.model)
228
+ torch_bases = slm.get_torch_bases(free_params)
229
+ else:
230
+ slm, torch_bases = split_model
231
+
232
+ probs = slm.circuit_probs_from_torch_bases(torch_bases)
233
+ array_to_fill[:slm.outcome_probs_dim] = probs.cpu().detach().numpy().ravel()
234
+ return
235
+
236
+ def _bulk_fill_dprobs(self, array_to_fill, layout, pr_array_to_fill) -> None:
237
+ slm = StatelessModel(self.model, layout)
238
+ # ^ TODO: figure out how to safely recycle StatelessModel objects from one
239
+ # call to another. The current implementation is wasteful if we need to
240
+ # compute many jacobians without structural changes to layout or self.model.
241
+ free_params = slm.get_free_params(self.model)
242
+
243
+ if pr_array_to_fill is not None:
244
+ torch_bases = slm.get_torch_bases(free_params)
245
+ splitm = (slm, torch_bases)
246
+ self._bulk_fill_probs(pr_array_to_fill, layout, splitm)
247
+
248
+ argnums = tuple(range(len(slm.param_metadata)))
249
+ if slm.default_to_reverse_ad:
250
+ # Then slm.circuit_probs_from_free_params will automatically construct the
251
+ # torch_base dict to support reverse-mode AD.
252
+ J_func = torch.func.jacrev(slm.circuit_probs_from_free_params, argnums=argnums)
253
+ else:
254
+ # Then slm.circuit_probs_from_free_params will automatically skip the extra
255
+ # steps needed for torch_base to support reverse-mode AD.
256
+ J_func = torch.func.jacfwd(slm.circuit_probs_from_free_params, argnums=argnums)
257
+ # ^ Note that this _bulk_fill_dprobs function doesn't accept parameters that
258
+ # could be used to override the default behavior of the StatelessModel. If we
259
+ # have a need to override the default in the future then we'd need to override
260
+ # the ForwardSimulator function(s) that call self._bulk_fill_dprobs(...).
261
+
262
+ J_val = J_func(*free_params)
263
+ J_val = torch.column_stack(J_val)
264
+ array_to_fill[:] = J_val.cpu().detach().numpy()
265
+ return
@@ -55,7 +55,7 @@ class WeakForwardSimulator(_ForwardSimulator):
55
55
  circuit : Circuit
56
56
  A tuple-like object of *simplified* gates (e.g. may include
57
57
  instrument elements like 'Imyinst_0') generated by
58
- Circuit.expand_instruments_and_separate_povm()
58
+ OpModel.expand_instruments_and_separate_povm()
59
59
 
60
60
  resource_alloc: ResourceAlloc
61
61
  Currently not used
@@ -77,7 +77,7 @@ class WeakForwardSimulator(_ForwardSimulator):
77
77
  assert(resource_alloc is None), "WeakForwardSimulator cannot use a resource_alloc for one shot."
78
78
 
79
79
  #prep_label, op_labels, povm_label = self.model.split_circuit(spc_circuit)
80
- spc_dict = circuit.expand_instruments_and_separate_povm(self.model,
80
+ spc_dict = self.model.expand_instruments_and_separate_povm(circuit,
81
81
  observed_outcomes=None) # FUTURE: observed outcomes?
82
82
  assert(len(spc_dict) == 1), "Circuits with instruments are not supported by weak forward simulator (yet)"
83
83
  spc = next(iter(spc_dict.keys())) # first & only SeparatePOVMCircuit
pygsti/io/__init__.py CHANGED
@@ -12,8 +12,7 @@ pyGSTi Input/Output Python Package
12
12
 
13
13
  # Import the most important/useful routines of each module into
14
14
  # the package namespace
15
- #from .legacyio import enable_no_cython_unpickling
16
- #from .legacyio import enable_old_object_unpickling # , disable_old_object_unpickling
15
+
17
16
  from .readers import *
18
17
  from .metadir import *
19
18
  from .stdinput import *
pygsti/io/mongodb.py CHANGED
@@ -158,7 +158,6 @@ def read_auxtree_from_mongodb_doc(mongodb, doc, auxfile_types_member='auxfile_ty
158
158
 
159
159
 
160
160
  def _load_auxdoc_member(mongodb, member_name, typ, metadata, quick_load):
161
- from pymongo import ASCENDING, DESCENDING
162
161
  subtypes = typ.split(':')
163
162
  cur_typ = subtypes[0]
164
163
  next_typ = ':'.join(subtypes[1:])
@@ -809,7 +808,6 @@ def remove_auxtree_from_mongodb(mongodb, collection_name, doc_id, auxfile_types_
809
808
 
810
809
 
811
810
  def _remove_auxdoc_member(mongodb, member_name, typ, metadata, session, recursive):
812
- from pymongo import ASCENDING, DESCENDING
813
811
  subtypes = typ.split(':')
814
812
  cur_typ = subtypes[0]
815
813
  next_typ = ':'.join(subtypes[1:])
pygsti/io/stdinput.py CHANGED
@@ -128,7 +128,7 @@ class StdInputParser(object):
128
128
  else:
129
129
  circuit = _Circuit._fastinit(layer_tuple, line_lbls, editable=False,
130
130
  name='', stringrep=s, occurrence=occurrence_id,
131
- compilable_layer_indices=compilable_indices)
131
+ compilable_layer_indices_tup=compilable_indices)
132
132
 
133
133
  if self.use_global_parse_cache:
134
134
  _global_parse_cache[create_subcircuits][s] = circuit
@@ -164,10 +164,10 @@ class StdInputParser(object):
164
164
  occurrence_id: int or None
165
165
  The "occurence id" - an integer following a second '@' symbol that identifies a particular
166
166
  copy of this circuit.
167
- compilable_indices : tuple or None
167
+ compilable_indices : tuple
168
168
  A tuple of layer indices (into `label_tuple`) marking the layers that can be "compiled",
169
169
  and are *not* followed by a barrier so they can be compiled with following layers. This
170
- is non-`None` only when there are explicit markers within the circuit string indicating
170
+ is non-empty only when there are explicit markers within the circuit string indicating
171
171
  the presence or absence of barriers.
172
172
  """
173
173
  if lookup is None:
@@ -175,8 +175,8 @@ class StdInputParser(object):
175
175
  self._circuit_parser.lookup = lookup
176
176
  circuit_tuple, circuit_labels, occurrence_id, compilable_indices = \
177
177
  self._circuit_parser.parse(s, create_subcircuits)
178
- # print "DB: result = ",result
179
- # print "DB: stack = ",self.exprStack
178
+ compilable_indices = compilable_indices if compilable_indices is not None else ()
179
+
180
180
  return circuit_tuple, circuit_labels, occurrence_id, compilable_indices
181
181
 
182
182
  def parse_dataline(self, s, lookup=None, expected_counts=-1, create_subcircuits=True,
@@ -326,14 +326,9 @@ class StdInputParser(object):
326
326
  self.parse_circuit_raw(line, {}, create_subcircuits)
327
327
  if parsed_line_lbls is None:
328
328
  parsed_line_lbls = line_labels # default to the passed-in argument
329
- #nlines = num_lines
330
- #else: nlines = None # b/c we've got a valid line_lbls
331
329
  circuit = _Circuit._fastinit(layer_lbls, parsed_line_lbls, editable=False,
332
330
  name='', stringrep=line.strip(), occurrence=occurrence_id,
333
- compilable_layer_indices=compilable_indices)
334
- #circuit = _Circuit(layer_lbls, stringrep=line.strip(),
335
- # line_labels=parsed_line_lbls, num_lines=nlines,
336
- # expand_subcircuits=False, check=False, occurrence=occurrence_id)
331
+ compilable_layer_indices_tup=compilable_indices)
337
332
  ##Note: never expand subcircuits since parse_circuit_raw already does this w/create_subcircuits arg
338
333
  circuit_list.append(circuit)
339
334
  return circuit_list
@@ -507,11 +502,6 @@ class StdInputParser(object):
507
502
 
508
503
  last_circuit = last_commentDict = None
509
504
 
510
- #REMOVE DEBUG
511
- #from mpi4py import MPI
512
- #comm = MPI.COMM_WORLD
513
- #debug_circuit_elements = 0; debug_test_simple_dict = {}; circuit_bytes = 0; sizeof_bytes = 0
514
-
515
505
  with open(filename, 'r') as inputfile:
516
506
  for (iLine, line) in enumerate(inputfile):
517
507
  if iLine % nSkip == 0 or iLine + 1 == nLines: display_progress(iLine + 1, nLines, filename)
@@ -593,11 +583,6 @@ class StdInputParser(object):
593
583
  warnings.append("Dataline for circuit '%s' has zero counts and will be ignored" % s)
594
584
  continue # skip lines in dataset file with zero counts (no experiments done)
595
585
  else:
596
- #if not bBad:
597
- # s = circuitStr if len(circuitStr) < 40 else circuitStr[0:37] + "..."
598
- # warnings.append("Dataline for circuit '%s' has zero counts." % s)
599
- # don't make a fuss if we don't ignore the lines (needed for
600
- # fill_in_empty_dataset_with_fake_data).
601
586
  pass
602
587
 
603
588
  #Call this low-level function for performance, so need to construct outcome *index* arrays above
@@ -639,7 +624,6 @@ class StdInputParser(object):
639
624
  else:
640
625
  raise ValueError("Invalid circuit data-line prefix: '%s'" % parts[0])
641
626
 
642
- #REMOVE print("Rank %d DONE load loop. circuit bytes = %g" % (comm.rank, circuit_bytes))
643
627
  if looking_for in ("circuit_data", "circuit_data_or_line") and current_item:
644
628
  #add final circuit info (no blank line at end of file)
645
629
  dataset.add_raw_series_data(current_item['circuit'], current_item.get('outcomes', []),
@@ -190,24 +190,26 @@ class CircuitOutcomeProbabilityArrayLayout(_NicelySerializable):
190
190
  if unique_circuits is None and to_unique is None:
191
191
  unique_circuits, to_unique = self._compute_unique_circuits(circuits)
192
192
  self._unique_circuits = unique_circuits
193
- self._unique_circuit_index = _collections.OrderedDict(
194
- [(c, i) for i, c in enumerate(self._unique_circuits)]) # original circuits => unique circuit indices
193
+ self._unique_circuit_index = {c:i for i, c in enumerate(self._unique_circuits)} # original circuits => unique circuit indices
195
194
  self._to_unique = to_unique # original indices => unique circuit indices
196
195
  self._unique_complete_circuits = unique_complete_circuits # Note: can be None
197
196
  self._param_dimensions = param_dimensions
198
197
  self._resource_alloc = _ResourceAllocation.cast(resource_alloc)
199
198
 
200
- max_element_index = max(_it.chain(*[[ei for ei, _ in pairs] for pairs in elindex_outcome_tuples.values()])) \
201
- if len(elindex_outcome_tuples) > 0 else -1 # -1 makes _size = 0 below
202
- indices = set(i for tuples in elindex_outcome_tuples.values() for i, o in tuples)
199
+ indices = [i for tuples in elindex_outcome_tuples.values() for i, _ in tuples]
200
+ max_element_index = max(indices) if len(elindex_outcome_tuples) > 0 else -1 # -1 makes _size = 0 below
201
+ indices = set(indices)
202
+
203
+
203
204
  self._size = max_element_index + 1
204
205
  assert(len(indices) == self._size), \
205
206
  "Inconsistency: %d distinct indices but max index + 1 is %d!" % (len(indices), self._size)
206
207
 
207
- self._outcomes = _collections.OrderedDict()
208
- self._element_indices = _collections.OrderedDict()
208
+ self._outcomes = dict()
209
+ self._element_indices = dict()
210
+ sort_idx_func = lambda x: x[0]
209
211
  for i_unique, tuples in elindex_outcome_tuples.items():
210
- sorted_tuples = sorted(tuples, key=lambda x: x[0]) # sort by element index
212
+ sorted_tuples = sorted(tuples, key=sort_idx_func) # sort by element index
211
213
  elindices, outcomes = zip(*sorted_tuples) # sorted by elindex so we make slices whenever possible
212
214
  self._outcomes[i_unique] = tuple(outcomes)
213
215
  self._element_indices[i_unique] = _slct.list_to_slice(elindices, array_ok=True)
@@ -598,10 +600,6 @@ class CircuitOutcomeProbabilityArrayLayout(_NicelySerializable):
598
600
  """
599
601
  jtj[:] = _np.dot(j.T, j)
600
602
 
601
- #Not needed
602
- #def allocate_jtj_shared_mem_buf(self):
603
- # return _np.empty((self._param_dimensions[0], self._param_dimensions[0]), 'd'), None
604
-
605
603
  def memory_estimate(self, array_type, dtype='d'):
606
604
  """
607
605
  Memory required to allocate an array of a given type (in bytes).
@@ -360,9 +360,6 @@ class DistributableCOPALayout(_CircuitOutcomeProbabilityArrayLayout):
360
360
  to_send = 0 # default = contribute nothing to MPI.SUM below
361
361
 
362
362
  if i in atoms_dict:
363
- #print("DB (%d): updating elindex_outcome_tuples w/Atom %d:\n%s"
364
- # % (rank, i, "\n".join(["%d: %s" % (indx, str(tups))
365
- # for indx, tups in atoms_dict[i].elindex_outcome_tuples.items()])))
366
363
  if start is None:
367
364
  start = stop = offset
368
365
  assert(stop == offset) # This should be checked by _assert_sequential(myAtomIndices) above
@@ -810,43 +807,6 @@ class DistributableCOPALayout(_CircuitOutcomeProbabilityArrayLayout):
810
807
  super().__init__(local_circuits, local_unique_circuits, local_to_unique, local_elindex_outcome_tuples,
811
808
  local_unique_complete_circuits, param_dimensions, resource_alloc)
812
809
 
813
- #DEBUG LAYOUT PRINTING
814
- #def cnt_str(cnt):
815
- # if cnt is None: return "%4s" % '-'
816
- # return "%4d" % cnt
817
- #def slc_str(slc):
818
- # if slc is None: return "%14s" % '--'
819
- # return "%3d->%3d (%3d)" % (slc.start, slc.stop, slc.stop - slc.start) \
820
- # if isinstance(slc, slice) else "%14s" % str(slc)
821
- #shm = bool(resource_alloc.host_comm is not None) # shared mem?
822
- #if rank == 0:
823
- # print("%11s %-14s %-14s %-14s %-14s %-4s %-14s %-4s %-14s %-4s" % (
824
- # '#', 'g-elements', 'g-params', 'g-param2s',
825
- # 'h-elements','tot', 'h-params','tot', 'h-params2','tot'),
826
- # flush=True)
827
- #resource_alloc.comm.barrier()
828
- #for r in range(resource_alloc.comm.size):
829
- # if r == rank:
830
- # my_desc = ("%3d (%2d.%2d)" % (rank, resource_alloc.host_index, resource_alloc.host_comm.rank)) \
831
- # if shm else ("%11d" % rank)
832
- # print(my_desc, slc_str(self.global_element_slice), slc_str(self.global_param_slice),
833
- # slc_str(self.global_param2_slice), ' ',
834
- # slc_str(self.host_element_slice), cnt_str(self.host_num_elements),
835
- # slc_str(self.host_param_slice), cnt_str(self.host_num_params),
836
- # slc_str(self.host_param2_slice), cnt_str(self.host_num_params2), flush=True)
837
- # resource_alloc.comm.barrier()
838
- #
839
- #if rank == 0:
840
- # print("%11s %-14s %-14s %-4s" % ('#', 'g-pfine', 'h-pfine', 'tot'), flush=True)
841
- #resource_alloc.comm.barrier()
842
- #for r in range(resource_alloc.comm.size):
843
- # if r == rank:
844
- # my_desc = ("%3d (%2d.%2d)" % (rank, resource_alloc.host_index, resource_alloc.host_comm.rank)) \
845
- # if shm else ("%11d" % rank)
846
- # print(my_desc, slc_str(self.global_param_fine_slice), slc_str(self.host_param_fine_slice),
847
- # cnt_str(self.host_num_params_fine), flush=True)
848
- # resource_alloc.comm.barrier()
849
-
850
810
  @property
851
811
  def max_atom_elements(self):
852
812
  """ The most elements owned by a single atom. """