pyGSTi 0.9.12__cp38-cp38-win32.whl → 0.9.13__cp38-cp38-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 +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-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.cp38-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.cp38-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.cp38-win32.pyd +0 -0
  41. pygsti/evotypes/densitymx/effectreps.pyx +1 -1
  42. pygsti/evotypes/densitymx/opreps.cp38-win32.pyd +0 -0
  43. pygsti/evotypes/densitymx/opreps.pyx +2 -2
  44. pygsti/evotypes/densitymx/statereps.cp38-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.cp38-win32.pyd +0 -0
  51. pygsti/evotypes/stabilizer/effectreps.pyx +0 -4
  52. pygsti/evotypes/stabilizer/opreps.cp38-win32.pyd +0 -0
  53. pygsti/evotypes/stabilizer/opreps.pyx +0 -4
  54. pygsti/evotypes/stabilizer/statereps.cp38-win32.pyd +0 -0
  55. pygsti/evotypes/stabilizer/statereps.pyx +1 -5
  56. pygsti/evotypes/stabilizer/termreps.cp38-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.cp38-win32.pyd +0 -0
  62. pygsti/evotypes/statevec/effectreps.pyx +1 -1
  63. pygsti/evotypes/statevec/opreps.cp38-win32.pyd +0 -0
  64. pygsti/evotypes/statevec/opreps.pyx +2 -2
  65. pygsti/evotypes/statevec/statereps.cp38-win32.pyd +0 -0
  66. pygsti/evotypes/statevec/statereps.pyx +1 -1
  67. pygsti/evotypes/statevec/termreps.cp38-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.cp38-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.cp38-win32.pyd +0 -0
  87. pygsti/forwardsims/termforwardsim_calc_statevec.cp38-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.cp38-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
@@ -14,6 +14,7 @@ import collections as _collections
14
14
 
15
15
  # from .. import modelmember as _mm
16
16
  from pygsti.modelmembers.povms.povm import POVM as _POVM
17
+ from pygsti.modelmembers.povms import ComposedPOVMEffect as _ComposedPOVMEffect
17
18
  from pygsti.modelmembers.povms.staticeffect import StaticPOVMEffect as _StaticPOVMEffect
18
19
  from pygsti.baseobjs.statespace import StateSpace as _StateSpace
19
20
  from pygsti.baseobjs.label import Label as _Label
@@ -193,8 +194,8 @@ class MarginalizedPOVM(_POVM):
193
194
  effect_vec = e.to_dense()
194
195
  else:
195
196
  effect_vec += e.to_dense()
196
- effect = _StaticPOVMEffect(effect_vec, e._basis, self._evotype)
197
- # UNSPECIFIED BASIS -- may need to rename e._basis -> e._rep.basis above if that's the std attribute name?
197
+ rep = e.effect_vec._rep if isinstance(e, _ComposedPOVMEffect) else e._rep
198
+ effect = _StaticPOVMEffect(effect_vec, rep.basis, self._evotype)
198
199
  assert(effect.allocate_gpindices(0, self.parent) == 0) # functional! (do not remove)
199
200
  _collections.OrderedDict.__setitem__(self, key, effect)
200
201
  return effect
@@ -206,85 +207,6 @@ class MarginalizedPOVM(_POVM):
206
207
  self.sslbls_after_marginalizing),
207
208
  {'_gpindices': self._gpindices}) # preserve gpindices (but not parent)
208
209
 
209
- #May need to implement this in future if we allow non-static MarginalizedPOVMs
210
- #def allocate_gpindices(self, starting_index, parent, memo=None):
211
- # """
212
- # Sets gpindices array for this object or any objects it
213
- # contains (i.e. depends upon). Indices may be obtained
214
- # from contained objects which have already been initialized
215
- # (e.g. if a contained object is shared with other
216
- # top-level objects), or given new indices starting with
217
- # `starting_index`.
218
- #
219
- # Parameters
220
- # ----------
221
- # starting_index : int
222
- # The starting index for un-allocated parameters.
223
- #
224
- # parent : Model or ModelMember
225
- # The parent whose parameter array gpindices references.
226
- #
227
- # memo : set, optional
228
- # Used to prevent duplicate calls and self-referencing loops. If
229
- # `memo` contains an object's id (`id(self)`) then this routine
230
- # will exit immediately.
231
- #
232
- # Returns
233
- # -------
234
- # num_new: int
235
- # The number of *new* allocated parameters (so
236
- # the parent should mark as allocated parameter
237
- # indices `starting_index` to `starting_index + new_new`).
238
- # """
239
- # if memo is None: memo = set()
240
- # if id(self) in memo: return 0
241
- # memo.add(id(self))
242
- #
243
- # assert(self.base_povm.num_params == 0) # so no need to do anything w/base_povm
244
- # num_new_params = self.error_map.allocate_gpindices(starting_index, parent, memo) # *same* parent as self
245
- # _mm.ModelMember.set_gpindices(
246
- # self, self.error_map.gpindices, parent)
247
- # return num_new_params
248
-
249
- #def relink_parent(self, parent): # Unnecessary?
250
- # """
251
- # Sets the parent of this object *without* altering its gpindices.
252
- #
253
- # In addition to setting the parent of this object, this method
254
- # sets the parent of any objects this object contains (i.e.
255
- # depends upon) - much like allocate_gpindices. To ensure a valid
256
- # parent is not overwritten, the existing parent *must be None*
257
- # prior to this call.
258
- # """
259
- # self.povm_to_marginalize.relink_parent(parent)
260
- # _mm.ModelMember.relink_parent(self, parent)
261
-
262
- #def set_gpindices(self, gpindices, parent, memo=None):
263
- # """
264
- # Set the parent and indices into the parent's parameter vector that
265
- # are used by this ModelMember object.
266
- #
267
- # Parameters
268
- # ----------
269
- # gpindices : slice or integer ndarray
270
- # The indices of this objects parameters in its parent's array.
271
- #
272
- # parent : Model or ModelMember
273
- # The parent whose parameter array gpindices references.
274
- #
275
- # Returns
276
- # -------
277
- # None
278
- # """
279
- # if memo is None: memo = set()
280
- # elif id(self) in memo: return
281
- # memo.add(id(self))
282
- #
283
- # assert(self.base_povm.num_params == 0) # so no need to do anything w/base_povm
284
- # self.error_map.set_gpindices(gpindices, parent, memo)
285
- # self.terms = {} # clear terms cache since param indices have changed now
286
- # _mm.ModelMember._set_only_my_gpindices(self, gpindices, parent)
287
-
288
210
  def simplify_effects(self, prefix=""):
289
211
  """
290
212
  Creates a dictionary of simplified effect vectors.
@@ -10,11 +10,23 @@ Defines the TPPOVM class
10
10
  # http://www.apache.org/licenses/LICENSE-2.0 or in the LICENSE file in the root pyGSTi directory.
11
11
  #***************************************************************************************************
12
12
 
13
+ from __future__ import annotations
14
+ from typing import Tuple, TYPE_CHECKING
15
+ if TYPE_CHECKING:
16
+ import torch as _torch
17
+ try:
18
+ import torch as _torch
19
+ except ImportError:
20
+ pass
21
+
22
+ import numpy as _np
23
+ from pygsti.modelmembers.torchable import Torchable as _Torchable
13
24
  from pygsti.modelmembers.povms.basepovm import _BasePOVM
14
- from pygsti.modelmembers.povms.effect import POVMEffect as _POVMEffect
25
+ from pygsti.modelmembers.povms.fulleffect import FullPOVMEffect as _FullPOVMEffect
26
+ import warnings
15
27
 
16
28
 
17
- class TPPOVM(_BasePOVM):
29
+ class TPPOVM(_BasePOVM, _Torchable):
18
30
  """
19
31
  A POVM whose sum-of-effects is constrained to what, by definition, we call the "identity".
20
32
 
@@ -37,6 +49,23 @@ class TPPOVM(_BasePOVM):
37
49
  The state space for this POVM. If `None`, the space is inferred
38
50
  from the first effect vector. If `len(effects) == 0` in this case,
39
51
  an error is raised.
52
+
53
+ Notes
54
+ -----
55
+ Just like TPState, we're restricted to the Pauli-product or Gell-Mann basis.
56
+
57
+ We inherit from BasePOVM, which inherits from POVM, which inherits from OrderedDict.
58
+
59
+ A TPPOVM "p" has an attribute p.complement_label that's set during construction.
60
+ This label is such that e = p[p.complement_label] is a ComplementPOVMEffect, with
61
+ an associated FullState object given in e.identity. If v = e.identity.to_vector(),
62
+ then e's vector representation is
63
+
64
+ v - sum(all non-complement effects in p).
65
+
66
+ Under typical conditions v will be proportional to the first standard basis vector,
67
+ and, in fact, if v is length "d," then we'll have v[0] == d ** 0.25. However,
68
+ neither of these conditions is strictly required by the API.
40
69
  """
41
70
 
42
71
  def __init__(self, effects, evotype=None, state_space=None, called_from_reduce=False):
@@ -56,3 +85,46 @@ class TPPOVM(_BasePOVM):
56
85
 
57
86
  return (TPPOVM, (effects, self.evotype, self.state_space, True),
58
87
  {'_gpindices': self._gpindices, '_submember_rpindices': self._submember_rpindices})
88
+
89
+ @property
90
+ def dim(self):
91
+ effect = next(iter(self.values()))
92
+ return effect.dim
93
+
94
+ def to_vector(self):
95
+ effect_vecs = []
96
+ for i, (lbl, effect) in enumerate(self.items()):
97
+ if lbl != self.complement_label:
98
+ assert isinstance(effect, _FullPOVMEffect)
99
+ effect_vecs.append(effect.to_vector())
100
+ else:
101
+ assert i == len(self) - 1
102
+ vec = _np.concatenate(effect_vecs)
103
+ return vec
104
+
105
+ def stateless_data(self) -> Tuple[int, _np.ndarray]:
106
+ num_effects = len(self)
107
+ complement_effect = self[self.complement_label]
108
+ identity = complement_effect.identity.to_vector()
109
+ return (num_effects, identity)
110
+
111
+ @staticmethod
112
+ def torch_base(sd: Tuple[int, _np.ndarray], t_param: _torch.Tensor) -> _torch.Tensor:
113
+ num_effects, identity = sd
114
+ dim = identity.size
115
+
116
+ first_basis_vec = _np.zeros(dim)
117
+ first_basis_vec[0] = dim ** 0.25
118
+ TOL = 1e-15 * _np.sqrt(dim)
119
+ if _np.linalg.norm(first_basis_vec - identity) > TOL:
120
+ # Don't error out. The documentation for the class
121
+ # clearly indicates that the meaning of "identity"
122
+ # can be nonstandard.
123
+ warnings.warn('Unexpected normalization!')
124
+
125
+ identity = identity.reshape((1, -1)) # make into a row vector
126
+ t_identity = _torch.from_numpy(identity)
127
+ t_param_mat = t_param.reshape((num_effects - 1, dim))
128
+ t_func = t_identity - t_param_mat.sum(axis=0, keepdim=True)
129
+ t = _torch.row_stack((t_param_mat, t_func))
130
+ return t
@@ -15,8 +15,6 @@ import scipy.linalg as _spl
15
15
  import scipy.optimize as _spo
16
16
  import warnings as _warnings
17
17
 
18
- from numpy.lib.arraysetops import isin
19
-
20
18
  from pygsti.modelmembers.povms.computationalpovm import ComputationalBasisPOVM
21
19
 
22
20
  from .composedstate import ComposedState
@@ -426,17 +424,16 @@ def optimize_state(vec_to_optimize, target_vec):
426
424
  return
427
425
 
428
426
  from pygsti import optimize as _opt
429
- from pygsti.tools import matrixtools as _mt
430
427
  assert(target_vec.dim == vec_to_optimize.dim) # vectors must have the same overall dimension
431
428
  targetVector = target_vec.to_dense() if isinstance(target_vec, State) else target_vec
432
429
 
433
430
  def _objective_func(param_vec):
434
431
  vec_to_optimize.from_vector(param_vec)
435
- return _mt.frobeniusnorm(vec_to_optimize.to_dense() - targetVector)
432
+ return _np.linalg.norm(vec_to_optimize.to_dense() - targetVector)
436
433
 
437
434
  x0 = vec_to_optimize.to_vector()
438
435
  minSol = _opt.minimize(_objective_func, x0, method='BFGS', maxiter=10000, maxfev=10000,
439
436
  tol=1e-6, callback=None)
440
437
 
441
438
  vec_to_optimize.from_vector(minSol.x)
442
- #print("DEBUG: optimized vector to min frobenius distance %g" % _mt.frobeniusnorm(vec_to_optimize-targetVector))
439
+ return
@@ -43,323 +43,6 @@ class ComposedState(_State): # , _ErrorMapContainer
43
43
  parameters with other gates and spam vectors.)
44
44
  """
45
45
 
46
- #@classmethod
47
- #def _from_spamvec_obj(cls, spamvec, typ, param_type="GLND", purevec=None,
48
- # proj_basis="pp", mx_basis="pp", truncate=True,
49
- # lazy=False):
50
- # """
51
- # Creates a LindbladSPAMVec from an existing SPAMVec object and some additional information.
52
- #
53
- # This function is different from `from_spam_vector` in that it assumes
54
- # that `spamvec` is a :class:`SPAMVec`-derived object, and if `lazy=True`
55
- # and if `spamvec` is already a matching LindbladSPAMVec, it
56
- # is returned directly. This routine is primarily used in spam vector
57
- # conversion functions, where conversion is desired only when necessary.
58
- #
59
- # Parameters
60
- # ----------
61
- # spamvec : SPAMVec
62
- # The spam vector object to "convert" to a
63
- # `LindbladSPAMVec`.
64
- #
65
- # typ : {"prep","effect"}
66
- # Whether this is a state preparation or POVM effect vector.
67
- #
68
- # param_type : str, optional
69
- # The high-level "parameter type" of the gate to create. This
70
- # specifies both which Lindblad parameters are included and what
71
- # type of evolution is used. Examples of valid values are
72
- # `"CPTP"`, `"H+S"`, `"S terms"`, and `"GLND clifford terms"`.
73
- #
74
- # purevec : numpy array or SPAMVec object, optional
75
- # A SPAM vector which represents a pure-state, taken as the "ideal"
76
- # reference state when constructing the error generator of the
77
- # returned `LindbladSPAMVec`. Note that this vector
78
- # still acts on density matrices (if it's a SPAMVec it should have
79
- # a "densitymx", "svterm", or "cterm" evolution type, and if it's
80
- # a numpy array it should have the same dimension as `spamvec`).
81
- # If None, then it is taken to be `spamvec`, and so `spamvec` must
82
- # represent a pure state in this case.
83
- #
84
- # proj_basis : {'std', 'gm', 'pp', 'qt'}, list of matrices, or Basis object
85
- # The basis used to construct the Lindblad-term error generators onto
86
- # which the SPAM vector's error generator is projected. Allowed values
87
- # are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
88
- # and Qutrit (qt), list of numpy arrays, or a custom basis object.
89
- #
90
- # mx_basis : {'std', 'gm', 'pp', 'qt'} or Basis object
91
- # The source and destination basis, respectively. Allowed
92
- # values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
93
- # and Qutrit (qt) (or a custom basis object).
94
- #
95
- # truncate : bool, optional
96
- # Whether to truncate the projections onto the Lindblad terms in
97
- # order to meet constraints (e.g. to preserve CPTP) when necessary.
98
- # If False, then an error is thrown when the given `spamvec` cannot
99
- # be realized by the specified set of Lindblad projections.
100
- #
101
- # lazy : bool, optional
102
- # If True, then if `spamvec` is already a LindbladSPAMVec
103
- # with the requested details (given by the other arguments), then
104
- # `spamvec` is returned directly and no conversion/copying is
105
- # performed. If False, then a new object is always returned.
106
- #
107
- # Returns
108
- # -------
109
- # LindbladSPAMVec
110
- # """
111
- #
112
- # if not isinstance(spamvec, SPAMVec):
113
- # spamvec = StaticSPAMVec(spamvec, typ=typ) # assume spamvec is just a vector
114
- #
115
- # if purevec is None:
116
- # purevec = spamvec # right now, we don't try to extract a "closest pure vec"
117
- # # to spamvec - below will fail if spamvec isn't pure.
118
- # elif not isinstance(purevec, SPAMVec):
119
- # purevec = StaticSPAMVec(purevec, typ=typ) # assume spamvec is just a vector
120
- #
121
- # #Break param_type in to a "base" type and an evotype
122
- # from .operation import LindbladOp as _LPGMap
123
- # bTyp, evotype, nonham_mode, param_mode = _LPGMap.decomp_paramtype(param_type)
124
- #
125
- # ham_basis = proj_basis if (("H" == bTyp) or ("H+" in bTyp) or bTyp in ("CPTP", "GLND")) else None
126
- # nonham_basis = None if bTyp == "H" else proj_basis
127
- #
128
- # def beq(b1, b2):
129
- # """ Check if bases have equal names """
130
- # b1 = b1.name if isinstance(b1, _Basis) else b1
131
- # b2 = b2.name if isinstance(b2, _Basis) else b2
132
- # return b1 == b2
133
- #
134
- # def normeq(a, b):
135
- # if a is None and b is None: return True
136
- # if a is None or b is None: return False
137
- # return _mt.safe_norm(a - b) < 1e-6 # what about possibility of Clifford gates?
138
- #
139
- # if isinstance(spamvec, LindbladSPAMVec) \
140
- # and spamvec._evotype == evotype and spamvec.typ == typ \
141
- # and beq(ham_basis, spamvec.error_map.ham_basis) and beq(nonham_basis, spamvec.error_map.other_basis) \
142
- # and param_mode == spamvec.error_map.param_mode and nonham_mode == spamvec.error_map.nonham_mode \
143
- # and beq(mx_basis, spamvec.error_map.matrix_basis) and lazy:
144
- # #normeq(gate.pure_state_vec,purevec) \ # TODO: more checks for equality?!
145
- # return spamvec # no creation necessary!
146
- # else:
147
- # #Convert vectors (if possible) to SPAMVecs
148
- # # of the appropriate evotype and 0 params.
149
- # bDiff = spamvec is not purevec
150
- # spamvec = _convert_to_lindblad_base(spamvec, typ, evotype, mx_basis)
151
- # purevec = _convert_to_lindblad_base(purevec, typ, evotype, mx_basis) if bDiff else spamvec
152
- # assert(spamvec._evotype == evotype)
153
- # assert(purevec._evotype == evotype)
154
- #
155
- # return cls.from_spam_vector(
156
- # spamvec, purevec, typ, ham_basis, nonham_basis,
157
- # param_mode, nonham_mode, truncate, mx_basis, evotype)
158
- #
159
- #@classmethod
160
- #def from_spam_vector(cls, spam_vec, pure_vec, typ,
161
- # ham_basis="pp", nonham_basis="pp", param_mode="cptp",
162
- # nonham_mode="all", truncate=True, mx_basis="pp",
163
- # evotype="densitymx"):
164
- # """
165
- # Creates a Lindblad-parameterized spamvec from a state vector and a basis.
166
- #
167
- # The basis specifies how to decompose (project) the vector's error generator.
168
- #
169
- # Parameters
170
- # ----------
171
- # spam_vec : SPAMVec
172
- # the SPAM vector to initialize from. The error generator that
173
- # tranforms `pure_vec` into `spam_vec` forms the parameterization
174
- # of the returned LindbladSPAMVec.
175
- #
176
- # pure_vec : numpy array or SPAMVec
177
- # An array or SPAMVec in the *full* density-matrix space (this
178
- # vector will have the same dimension as `spam_vec` - 4 in the case
179
- # of a single qubit) which represents a pure-state preparation or
180
- # projection. This is used as the "base" preparation/projection
181
- # when computing the error generator that will be parameterized.
182
- # Note that this argument must be specified, as there is no natural
183
- # default value (like the identity in the case of gates).
184
- #
185
- # typ : {"prep","effect"}
186
- # Whether this is a state preparation or POVM effect vector.
187
- #
188
- # ham_basis: {'std', 'gm', 'pp', 'qt'}, list of matrices, or Basis object
189
- # The basis is used to construct the Hamiltonian-type lindblad error
190
- # Allowed values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
191
- # and Qutrit (qt), list of numpy arrays, or a custom basis object.
192
- #
193
- # nonham_basis: {'std', 'gm', 'pp', 'qt'}, list of matrices, or Basis object
194
- # The basis is used to construct the Stochastic-type lindblad error
195
- # Allowed values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
196
- # and Qutrit (qt), list of numpy arrays, or a custom basis object.
197
- #
198
- # param_mode : {"unconstrained", "cptp", "depol", "reldepol"}
199
- # Describes how the Lindblad coefficients/projections relate to the
200
- # SPAM vector's parameter values. Allowed values are:
201
- # `"unconstrained"` (coeffs are independent unconstrained parameters),
202
- # `"cptp"` (independent parameters but constrained so map is CPTP),
203
- # `"reldepol"` (all non-Ham. diagonal coeffs take the *same* value),
204
- # `"depol"` (same as `"reldepol"` but coeffs must be *positive*)
205
- #
206
- # nonham_mode : {"diagonal", "diag_affine", "all"}
207
- # Which non-Hamiltonian Lindblad projections are potentially non-zero.
208
- # Allowed values are: `"diagonal"` (only the diagonal Lind. coeffs.),
209
- # `"diag_affine"` (diagonal coefficients + affine projections), and
210
- # `"all"` (the entire matrix of coefficients is allowed).
211
- #
212
- # truncate : bool, optional
213
- # Whether to truncate the projections onto the Lindblad terms in
214
- # order to meet constraints (e.g. to preserve CPTP) when necessary.
215
- # If False, then an error is thrown when the given `gate` cannot
216
- # be realized by the specified set of Lindblad projections.
217
- #
218
- # mx_basis : {'std', 'gm', 'pp', 'qt'} or Basis object
219
- # The source and destination basis, respectively. Allowed
220
- # values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
221
- # and Qutrit (qt) (or a custom basis object).
222
- #
223
- # evotype : {"densitymx","svterm","cterm"}
224
- # The evolution type of the spamvec being constructed. `"densitymx"` is
225
- # usual Lioville density-matrix-vector propagation via matrix-vector
226
- # products. `"svterm"` denotes state-vector term-based evolution
227
- # (spamvec is obtained by evaluating the rank-1 terms up to
228
- # some order). `"cterm"` is similar but stabilizer states.
229
- #
230
- # Returns
231
- # -------
232
- # LindbladSPAMVec
233
- # """
234
- # #Compute a (errgen, pure_vec) pair from the given
235
- # # (spam_vec, pure_vec) pair.
236
- #
237
- # assert(pure_vec is not None), "Must supply `pure_vec`!" # since there's no good default?
238
- #
239
- # if not isinstance(spam_vec, SPAMVec):
240
- # spam_vec = StaticSPAMVec(spam_vec, evotype, typ) # assume spamvec is just a vector
241
- # if not isinstance(pure_vec, SPAMVec):
242
- # pure_vec = StaticSPAMVec(pure_vec, evotype, typ) # assume spamvec is just a vector
243
- # d2 = pure_vec.dim
244
- #
245
- # #Determine whether we're using sparse bases or not
246
- # sparse = None
247
- # if ham_basis is not None:
248
- # if isinstance(ham_basis, _Basis): sparse = ham_basis.sparse
249
- # elif not isinstance(ham_basis, str) and len(ham_basis) > 0:
250
- # sparse = _sps.issparse(ham_basis[0])
251
- # if sparse is None and nonham_basis is not None:
252
- # if isinstance(nonham_basis, _Basis): sparse = nonham_basis.sparse
253
- # elif not isinstance(nonham_basis, str) and len(nonham_basis) > 0:
254
- # sparse = _sps.issparse(nonham_basis[0])
255
- # if sparse is None: sparse = False # the default
256
- #
257
- # if spam_vec is None or spam_vec is pure_vec:
258
- # if sparse: errgen = _sps.csr_matrix((d2, d2), dtype='d')
259
- # else: errgen = _np.zeros((d2, d2), 'd')
260
- # else:
261
- # #Construct "spam error generator" by comparing *dense* vectors
262
- # pvdense = pure_vec.to_dense()
263
- # svdense = spam_vec.to_dense()
264
- # errgen = _ot.spam_error_generator(svdense, pvdense, mx_basis)
265
- # if sparse: errgen = _sps.csr_matrix(errgen)
266
- #
267
- # assert(pure_vec._evotype == evotype), "`pure_vec` must have evotype == '%s'" % evotype
268
- #
269
- # from .operation import LindbladErrorgen as _LErrorgen
270
- # from .operation import LindbladOp as _LPGMap
271
- # from .operation import LindbladDenseOp as _LPOp
272
- #
273
- # errgen = _LErrorgen.from_error_generator(errgen, ham_basis,
274
- # nonham_basis, param_mode, nonham_mode,
275
- # mx_basis, truncate, evotype)
276
- # errcls = _LPOp if (pure_vec.dim <= 64 and evotype == "densitymx") else _LPGMap
277
- # errmap = errcls(None, errgen)
278
- #
279
- # return cls(pure_vec, errmap, typ)
280
-
281
- #@classmethod
282
- #def from_lindblad_terms(cls, pure_vec, lindblad_term_dict, typ, basisdict=None,
283
- # param_mode="cptp", nonham_mode="all", truncate=True,
284
- # mx_basis="pp", evotype="densitymx"):
285
- # """
286
- # Create a Lindblad-parameterized spamvec with a given set of Lindblad terms.
287
- #
288
- # Parameters
289
- # ----------
290
- # pure_vec : numpy array or SPAMVec
291
- # An array or SPAMVec in the *full* density-matrix space (this
292
- # vector will have dimension 4 in the case of a single qubit) which
293
- # represents a pure-state preparation or projection. This is used as
294
- # the "base" preparation or projection that is followed or preceded
295
- # by, respectively, the parameterized Lindblad-form error generator.
296
- #
297
- # lindblad_term_dict : dict
298
- # A dictionary specifying which Linblad terms are present in the gate
299
- # parameteriztion. Keys are `(termType, basisLabel1, <basisLabel2>)`
300
- # tuples, where `termType` can be `"H"` (Hamiltonian), `"S"`
301
- # (Stochastic), or `"A"` (Affine). Hamiltonian and Affine terms always
302
- # have a single basis label (so key is a 2-tuple) whereas Stochastic
303
- # tuples with 1 basis label indicate a *diagonal* term, and are the
304
- # only types of terms allowed when `nonham_mode != "all"`. Otherwise,
305
- # Stochastic term tuples can include 2 basis labels to specify
306
- # "off-diagonal" non-Hamiltonian Lindblad terms. Basis labels can be
307
- # strings or integers. Values are complex coefficients (error rates).
308
- #
309
- # typ : {"prep","effect"}
310
- # Whether this is a state preparation or POVM effect vector.
311
- #
312
- # basisdict : dict, optional
313
- # A dictionary mapping the basis labels (strings or ints) used in the
314
- # keys of `lindblad_term_dict` to basis matrices (numpy arrays or Scipy sparse
315
- # matrices).
316
- #
317
- # param_mode : {"unconstrained", "cptp", "depol", "reldepol"}
318
- # Describes how the Lindblad coefficients/projections relate to the
319
- # SPAM vector's parameter values. Allowed values are:
320
- # `"unconstrained"` (coeffs are independent unconstrained parameters),
321
- # `"cptp"` (independent parameters but constrained so map is CPTP),
322
- # `"reldepol"` (all non-Ham. diagonal coeffs take the *same* value),
323
- # `"depol"` (same as `"reldepol"` but coeffs must be *positive*)
324
- #
325
- # nonham_mode : {"diagonal", "diag_affine", "all"}
326
- # Which non-Hamiltonian Lindblad projections are potentially non-zero.
327
- # Allowed values are: `"diagonal"` (only the diagonal Lind. coeffs.),
328
- # `"diag_affine"` (diagonal coefficients + affine projections), and
329
- # `"all"` (the entire matrix of coefficients is allowed).
330
- #
331
- # truncate : bool, optional
332
- # Whether to truncate the projections onto the Lindblad terms in
333
- # order to meet constraints (e.g. to preserve CPTP) when necessary.
334
- # If False, then an error is thrown when the given dictionary of
335
- # Lindblad terms doesn't conform to the constrains.
336
- #
337
- # mx_basis : {'std', 'gm', 'pp', 'qt'} or Basis object
338
- # The source and destination basis, respectively. Allowed
339
- # values are Matrix-unit (std), Gell-Mann (gm), Pauli-product (pp),
340
- # and Qutrit (qt) (or a custom basis object).
341
- #
342
- # evotype : {"densitymx","svterm","cterm"}
343
- # The evolution type of the spamvec being constructed. `"densitymx"` is
344
- # usual Lioville density-matrix-vector propagation via matrix-vector
345
- # products. `"svterm"` denotes state-vector term-based evolution
346
- # (spamvec is obtained by evaluating the rank-1 terms up to
347
- # some order). `"cterm"` is similar but stabilizer states.
348
- #
349
- # Returns
350
- # -------
351
- # LindbladOp
352
- # """
353
- # #Need a dimension for error map construction (basisdict could be completely empty)
354
- # if not isinstance(pure_vec, SPAMVec):
355
- # pure_vec = StaticSPAMVec(pure_vec, evotype, typ) # assume spamvec is just a vector
356
- # d2 = pure_vec.dim
357
- #
358
- # from .operation import LindbladOp as _LPGMap
359
- # errmap = _LPGMap(d2, lindblad_term_dict, basisdict, param_mode, nonham_mode,
360
- # truncate, mx_basis, evotype)
361
- # return cls(pure_vec, errmap, typ)
362
-
363
46
  def __init__(self, static_state, errormap):
364
47
  evotype = errormap._evotype
365
48
  #from .operation import LindbladOp as _LPGMap
@@ -88,7 +88,7 @@ class ComputationalBasisState(_State, _NoErrorGeneratorInterface):
88
88
 
89
89
  for zvals in _itertools.product(*([(0, 1)] * nqubits)):
90
90
  testvec = _functools.reduce(_np.kron, [v[i] for i in zvals])
91
- if _np.allclose(testvec, vec.flatten()):
91
+ if _np.allclose(testvec, vec.ravel()):
92
92
  return cls(zvals, basis, evotype, state_space)
93
93
  raise ValueError(("Given `vec` is not a z-basis product state - "
94
94
  "cannot construct ComputationalBasisState"))
@@ -128,7 +128,7 @@ class ComputationalBasisState(_State, _NoErrorGeneratorInterface):
128
128
  v = (_np.array([1, 0], 'd'), _np.array([0, 1], 'd')) # (v0,v1)
129
129
  for zvals in _itertools.product(*([(0, 1)] * nqubits)):
130
130
  testvec = _functools.reduce(_np.kron, [v[i] for i in zvals])
131
- if _np.allclose(testvec, purevec.flatten()):
131
+ if _np.allclose(testvec, purevec.ravel()):
132
132
  return cls(zvals, basis, evotype, state_space)
133
133
  raise ValueError(("Given `purevec` must be a z-basis product state - "
134
134
  "cannot construct ComputationalBasisState"))
@@ -140,7 +140,7 @@ class ComputationalBasisState(_State, _NoErrorGeneratorInterface):
140
140
  else _statespace.StateSpace.cast(state_space)
141
141
  basis = _Basis.cast(basis, state_space) # basis for Hilbert-Schmidt (superop) space
142
142
 
143
- evotype = _Evotype.cast(evotype)
143
+ evotype = _Evotype.cast(evotype, state_space=state_space)
144
144
  self._evotype = evotype # set this before call to _State.__init__ so self.to_dense() can work...
145
145
  rep = evotype.create_computational_state_rep(self._zvals, basis, state_space)
146
146
  _State.__init__(self, rep, evotype)
@@ -84,7 +84,7 @@ class CPTPState(_DenseState):
84
84
  state_space = _statespace.default_space_for_dim(len(vector)) if (state_space is None) \
85
85
  else _statespace.StateSpace.cast(state_space)
86
86
 
87
- evotype = _Evotype.cast(evotype)
87
+ evotype = _Evotype.cast(evotype, state_space=state_space)
88
88
  _DenseState.__init__(self, vector, basis, evotype, state_space)
89
89
  self._paramlbls = _np.array(labels, dtype=object)
90
90
 
@@ -150,7 +150,7 @@ class CPTPState(_DenseState):
150
150
  Lmx = _np.linalg.cholesky(density_mx)
151
151
 
152
152
  #check TP condition: that diagonal els of Lmx squared add to 1.0
153
- Lmx_norm = _np.trace(_np.dot(Lmx.T.conjugate(), Lmx)) # sum of magnitude^2 of all els
153
+ Lmx_norm = _np.linalg.norm(Lmx) # = sqrt(tr(Lmx' Lmx))
154
154
  assert(_np.isclose(Lmx_norm, 1.0)), \
155
155
  "Cholesky decomp didn't preserve trace=1!"
156
156
 
@@ -180,7 +180,7 @@ class CPTPState(_DenseState):
180
180
  for j in range(i):
181
181
  self.Lmx[i, j] = (self.params[i * dmDim + j] + 1j * self.params[j * dmDim + i]) / paramNorm
182
182
 
183
- Lmx_norm = _np.trace(_np.dot(self.Lmx.T.conjugate(), self.Lmx)) # sum of magnitude^2 of all els
183
+ Lmx_norm = _np.linalg.norm(self.Lmx) # = sqrt(tr(Lmx' Lmx))
184
184
  assert(_np.isclose(Lmx_norm, 1.0)), "Violated trace=1 condition!"
185
185
 
186
186
  #The (complex, Hermitian) density matrix is build by
@@ -192,7 +192,7 @@ class CPTPState(_DenseState):
192
192
  # write density matrix in given basis: = sum_i alpha_i B_i
193
193
  # ASSUME that basis is orthogonal, i.e. Tr(Bi^dag*Bj) = delta_ij
194
194
  basis_mxs = _np.rollaxis(self.basis_mxs, 2) # shape (dmDim, dmDim, len(vec))
195
- vec = _np.array([_np.trace(_np.dot(M.T.conjugate(), density_mx)) for M in basis_mxs])
195
+ vec = _np.array([_np.vdot(M, density_mx) for M in basis_mxs])
196
196
 
197
197
  #for now, assume Liouville vector should always be real (TODO: add 'real' flag later?)
198
198
  assert(_np.linalg.norm(_np.imag(vec)) < IMAG_TOL)
@@ -166,9 +166,11 @@ class DenseState(DenseStateInterface, _State):
166
166
 
167
167
  def __init__(self, vec, basis, evotype, state_space):
168
168
  vec = _State._to_vector(vec)
169
- state_space = _statespace.default_space_for_dim(vec.shape[0]) if (state_space is None) \
170
- else _statespace.StateSpace.cast(state_space)
171
- evotype = _Evotype.cast(evotype)
169
+ if state_space is None:
170
+ state_space = _statespace.default_space_for_dim(vec.shape[0])
171
+ else:
172
+ state_space = _statespace.StateSpace.cast(state_space)
173
+ evotype = _Evotype.cast(evotype, state_space=state_space)
172
174
  self._basis = _Basis.cast(basis, state_space.dim)
173
175
  rep = evotype.create_dense_state_rep(vec, self._basis, state_space)
174
176
 
@@ -263,15 +265,16 @@ class DensePureState(DenseStateInterface, _State):
263
265
  purevec = purevec.astype(complex)
264
266
  state_space = _statespace.default_space_for_udim(purevec.shape[0]) if (state_space is None) \
265
267
  else _statespace.StateSpace.cast(state_space)
266
- evotype = _Evotype.cast(evotype)
268
+ evotype = _Evotype.cast(evotype, state_space=state_space)
267
269
  basis = _Basis.cast(basis, state_space.dim) # basis for Hilbert-Schmidt (superop) space
268
-
269
- #Try to create a dense pure rep. If this fails, see if a dense superkey rep
270
+
271
+ #Try to create a dense pure rep. If this fails, see if a dense superket rep
270
272
  # can be created, as this type of rep can also hold arbitrary pure states.
271
273
  try:
272
274
  rep = evotype.create_pure_state_rep(purevec, basis, state_space)
273
275
  self._reptype = 'pure'
274
- self._purevec = self._basis = None
276
+ self._purevec = None
277
+ self._basis = basis #this was previously being set as None, not sure why.
275
278
  except Exception:
276
279
  if len(purevec) == basis.dim and _np.linalg.norm(purevec.imag) < 1e-10:
277
280
  # Special case when a *superket* was provided instead of a purevec
@@ -350,7 +353,6 @@ class DensePureState(DenseStateInterface, _State):
350
353
 
351
354
  mm_dict['dense_state_vector'] = self._encodemx(self.to_dense('Hilbert'))
352
355
  mm_dict['basis'] = self._basis.to_nice_serialization() if (self._basis is not None) else None
353
-
354
356
  return mm_dict
355
357
 
356
358
  @classmethod