pyGSTi 0.9.12.1__cp310-cp310-win_amd64.whl → 0.9.13__cp310-cp310-win_amd64.whl
Sign up to get free protection for your applications and to get access to all the features.
- pyGSTi-0.9.13.dist-info/METADATA +197 -0
- {pyGSTi-0.9.12.1.dist-info → pyGSTi-0.9.13.dist-info}/RECORD +207 -217
- {pyGSTi-0.9.12.1.dist-info → pyGSTi-0.9.13.dist-info}/WHEEL +1 -1
- pygsti/_version.py +2 -2
- pygsti/algorithms/contract.py +1 -1
- pygsti/algorithms/core.py +42 -28
- pygsti/algorithms/fiducialselection.py +17 -8
- pygsti/algorithms/gaugeopt.py +2 -2
- pygsti/algorithms/germselection.py +87 -77
- pygsti/algorithms/mirroring.py +0 -388
- pygsti/algorithms/randomcircuit.py +165 -1333
- pygsti/algorithms/rbfit.py +0 -234
- pygsti/baseobjs/basis.py +94 -396
- pygsti/baseobjs/errorgenbasis.py +0 -132
- pygsti/baseobjs/errorgenspace.py +0 -10
- pygsti/baseobjs/label.py +52 -168
- pygsti/baseobjs/opcalc/fastopcalc.cp310-win_amd64.pyd +0 -0
- pygsti/baseobjs/opcalc/fastopcalc.pyx +2 -2
- pygsti/baseobjs/polynomial.py +13 -595
- pygsti/baseobjs/statespace.py +1 -0
- pygsti/circuits/__init__.py +1 -1
- pygsti/circuits/circuit.py +682 -505
- pygsti/circuits/circuitconstruction.py +0 -4
- pygsti/circuits/circuitlist.py +47 -5
- pygsti/circuits/circuitparser/__init__.py +8 -8
- pygsti/circuits/circuitparser/fastcircuitparser.cp310-win_amd64.pyd +0 -0
- pygsti/circuits/circuitstructure.py +3 -3
- pygsti/circuits/cloudcircuitconstruction.py +1 -1
- pygsti/data/datacomparator.py +2 -7
- pygsti/data/dataset.py +46 -44
- pygsti/data/hypothesistest.py +0 -7
- pygsti/drivers/bootstrap.py +0 -49
- pygsti/drivers/longsequence.py +2 -1
- pygsti/evotypes/basereps_cython.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/chp/opreps.py +0 -61
- pygsti/evotypes/chp/statereps.py +0 -32
- pygsti/evotypes/densitymx/effectcreps.cpp +9 -10
- pygsti/evotypes/densitymx/effectreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/densitymx/effectreps.pyx +1 -1
- pygsti/evotypes/densitymx/opreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/densitymx/opreps.pyx +2 -2
- pygsti/evotypes/densitymx/statereps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/densitymx/statereps.pyx +1 -1
- pygsti/evotypes/densitymx_slow/effectreps.py +7 -23
- pygsti/evotypes/densitymx_slow/opreps.py +16 -23
- pygsti/evotypes/densitymx_slow/statereps.py +10 -3
- pygsti/evotypes/evotype.py +39 -2
- pygsti/evotypes/stabilizer/effectreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/stabilizer/effectreps.pyx +0 -4
- pygsti/evotypes/stabilizer/opreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/stabilizer/opreps.pyx +0 -4
- pygsti/evotypes/stabilizer/statereps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/stabilizer/statereps.pyx +1 -5
- pygsti/evotypes/stabilizer/termreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/stabilizer/termreps.pyx +0 -7
- pygsti/evotypes/stabilizer_slow/effectreps.py +0 -22
- pygsti/evotypes/stabilizer_slow/opreps.py +0 -4
- pygsti/evotypes/stabilizer_slow/statereps.py +0 -4
- pygsti/evotypes/statevec/effectreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/statevec/effectreps.pyx +1 -1
- pygsti/evotypes/statevec/opreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/statevec/opreps.pyx +2 -2
- pygsti/evotypes/statevec/statereps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/statevec/statereps.pyx +1 -1
- pygsti/evotypes/statevec/termreps.cp310-win_amd64.pyd +0 -0
- pygsti/evotypes/statevec/termreps.pyx +0 -7
- pygsti/evotypes/statevec_slow/effectreps.py +0 -3
- pygsti/evotypes/statevec_slow/opreps.py +0 -5
- pygsti/extras/__init__.py +0 -1
- pygsti/extras/drift/stabilityanalyzer.py +3 -1
- pygsti/extras/interpygate/__init__.py +12 -0
- pygsti/extras/interpygate/core.py +0 -36
- pygsti/extras/interpygate/process_tomography.py +44 -10
- pygsti/extras/rpe/rpeconstruction.py +0 -2
- pygsti/forwardsims/__init__.py +1 -0
- pygsti/forwardsims/forwardsim.py +14 -55
- pygsti/forwardsims/mapforwardsim.py +69 -18
- pygsti/forwardsims/mapforwardsim_calc_densitymx.cp310-win_amd64.pyd +0 -0
- pygsti/forwardsims/mapforwardsim_calc_densitymx.pyx +65 -66
- pygsti/forwardsims/mapforwardsim_calc_generic.py +91 -13
- pygsti/forwardsims/matrixforwardsim.py +63 -15
- pygsti/forwardsims/termforwardsim.py +8 -110
- pygsti/forwardsims/termforwardsim_calc_stabilizer.cp310-win_amd64.pyd +0 -0
- pygsti/forwardsims/termforwardsim_calc_statevec.cp310-win_amd64.pyd +0 -0
- pygsti/forwardsims/termforwardsim_calc_statevec.pyx +0 -651
- pygsti/forwardsims/torchfwdsim.py +265 -0
- pygsti/forwardsims/weakforwardsim.py +2 -2
- pygsti/io/__init__.py +1 -2
- pygsti/io/mongodb.py +0 -2
- pygsti/io/stdinput.py +6 -22
- pygsti/layouts/copalayout.py +10 -12
- pygsti/layouts/distlayout.py +0 -40
- pygsti/layouts/maplayout.py +103 -25
- pygsti/layouts/matrixlayout.py +99 -60
- pygsti/layouts/prefixtable.py +1534 -52
- pygsti/layouts/termlayout.py +1 -1
- pygsti/modelmembers/instruments/instrument.py +3 -3
- pygsti/modelmembers/instruments/tpinstrument.py +2 -2
- pygsti/modelmembers/modelmember.py +0 -17
- pygsti/modelmembers/operations/__init__.py +2 -4
- pygsti/modelmembers/operations/affineshiftop.py +1 -0
- pygsti/modelmembers/operations/composederrorgen.py +1 -1
- pygsti/modelmembers/operations/composedop.py +1 -24
- pygsti/modelmembers/operations/denseop.py +5 -5
- pygsti/modelmembers/operations/eigpdenseop.py +2 -2
- pygsti/modelmembers/operations/embeddederrorgen.py +1 -1
- pygsti/modelmembers/operations/embeddedop.py +0 -1
- pygsti/modelmembers/operations/experrorgenop.py +2 -2
- pygsti/modelmembers/operations/fullarbitraryop.py +1 -0
- pygsti/modelmembers/operations/fullcptpop.py +2 -2
- pygsti/modelmembers/operations/fulltpop.py +28 -6
- pygsti/modelmembers/operations/fullunitaryop.py +5 -4
- pygsti/modelmembers/operations/lindbladcoefficients.py +93 -78
- pygsti/modelmembers/operations/lindbladerrorgen.py +268 -441
- pygsti/modelmembers/operations/linearop.py +7 -27
- pygsti/modelmembers/operations/opfactory.py +1 -1
- pygsti/modelmembers/operations/repeatedop.py +1 -24
- pygsti/modelmembers/operations/staticstdop.py +1 -1
- pygsti/modelmembers/povms/__init__.py +3 -3
- pygsti/modelmembers/povms/basepovm.py +7 -36
- pygsti/modelmembers/povms/complementeffect.py +4 -9
- pygsti/modelmembers/povms/composedeffect.py +0 -320
- pygsti/modelmembers/povms/computationaleffect.py +1 -1
- pygsti/modelmembers/povms/computationalpovm.py +3 -1
- pygsti/modelmembers/povms/effect.py +3 -5
- pygsti/modelmembers/povms/marginalizedpovm.py +0 -79
- pygsti/modelmembers/povms/tppovm.py +74 -2
- pygsti/modelmembers/states/__init__.py +2 -5
- pygsti/modelmembers/states/composedstate.py +0 -317
- pygsti/modelmembers/states/computationalstate.py +3 -3
- pygsti/modelmembers/states/cptpstate.py +4 -4
- pygsti/modelmembers/states/densestate.py +6 -4
- pygsti/modelmembers/states/fullpurestate.py +0 -24
- pygsti/modelmembers/states/purestate.py +1 -1
- pygsti/modelmembers/states/state.py +5 -6
- pygsti/modelmembers/states/tpstate.py +28 -10
- pygsti/modelmembers/term.py +3 -6
- pygsti/modelmembers/torchable.py +50 -0
- pygsti/modelpacks/_modelpack.py +1 -1
- pygsti/modelpacks/smq1Q_ZN.py +3 -1
- pygsti/modelpacks/smq2Q_XXYYII.py +2 -1
- pygsti/modelpacks/smq2Q_XY.py +3 -3
- pygsti/modelpacks/smq2Q_XYI.py +2 -2
- pygsti/modelpacks/smq2Q_XYICNOT.py +3 -3
- pygsti/modelpacks/smq2Q_XYICPHASE.py +3 -3
- pygsti/modelpacks/smq2Q_XYXX.py +1 -1
- pygsti/modelpacks/smq2Q_XYZICNOT.py +3 -3
- pygsti/modelpacks/smq2Q_XYZZ.py +1 -1
- pygsti/modelpacks/stdtarget.py +0 -121
- pygsti/models/cloudnoisemodel.py +1 -2
- pygsti/models/explicitcalc.py +3 -3
- pygsti/models/explicitmodel.py +3 -13
- pygsti/models/fogistore.py +5 -3
- pygsti/models/localnoisemodel.py +1 -2
- pygsti/models/memberdict.py +0 -12
- pygsti/models/model.py +800 -65
- pygsti/models/modelconstruction.py +4 -4
- pygsti/models/modelnoise.py +2 -2
- pygsti/models/modelparaminterposer.py +1 -1
- pygsti/models/oplessmodel.py +1 -1
- pygsti/models/qutrit.py +15 -14
- pygsti/objectivefns/objectivefns.py +73 -138
- pygsti/objectivefns/wildcardbudget.py +2 -7
- pygsti/optimize/__init__.py +1 -0
- pygsti/optimize/arraysinterface.py +28 -0
- pygsti/optimize/customcg.py +0 -12
- pygsti/optimize/customlm.py +129 -323
- pygsti/optimize/customsolve.py +2 -2
- pygsti/optimize/optimize.py +0 -84
- pygsti/optimize/simplerlm.py +841 -0
- pygsti/optimize/wildcardopt.py +19 -598
- pygsti/protocols/confidenceregionfactory.py +28 -14
- pygsti/protocols/estimate.py +31 -14
- pygsti/protocols/gst.py +142 -68
- pygsti/protocols/modeltest.py +6 -10
- pygsti/protocols/protocol.py +9 -37
- pygsti/protocols/rb.py +450 -79
- pygsti/protocols/treenode.py +8 -2
- pygsti/protocols/vb.py +108 -206
- pygsti/protocols/vbdataframe.py +1 -1
- pygsti/report/factory.py +0 -15
- pygsti/report/fogidiagram.py +1 -17
- pygsti/report/modelfunction.py +12 -3
- pygsti/report/mpl_colormaps.py +1 -1
- pygsti/report/plothelpers.py +8 -2
- pygsti/report/reportables.py +41 -37
- pygsti/report/templates/offline/pygsti_dashboard.css +6 -0
- pygsti/report/templates/offline/pygsti_dashboard.js +12 -0
- pygsti/report/workspace.py +2 -14
- pygsti/report/workspaceplots.py +326 -504
- pygsti/tools/basistools.py +9 -36
- pygsti/tools/edesigntools.py +124 -96
- pygsti/tools/fastcalc.cp310-win_amd64.pyd +0 -0
- pygsti/tools/fastcalc.pyx +35 -81
- pygsti/tools/internalgates.py +151 -15
- pygsti/tools/jamiolkowski.py +5 -5
- pygsti/tools/lindbladtools.py +19 -11
- pygsti/tools/listtools.py +0 -114
- pygsti/tools/matrixmod2.py +1 -1
- pygsti/tools/matrixtools.py +173 -339
- pygsti/tools/nameddict.py +1 -1
- pygsti/tools/optools.py +154 -88
- pygsti/tools/pdftools.py +0 -25
- pygsti/tools/rbtheory.py +3 -320
- pygsti/tools/slicetools.py +64 -12
- pyGSTi-0.9.12.1.dist-info/METADATA +0 -155
- pygsti/algorithms/directx.py +0 -711
- pygsti/evotypes/qibo/__init__.py +0 -33
- pygsti/evotypes/qibo/effectreps.py +0 -78
- pygsti/evotypes/qibo/opreps.py +0 -376
- pygsti/evotypes/qibo/povmreps.py +0 -98
- pygsti/evotypes/qibo/statereps.py +0 -174
- pygsti/extras/rb/__init__.py +0 -13
- pygsti/extras/rb/benchmarker.py +0 -957
- pygsti/extras/rb/dataset.py +0 -378
- pygsti/extras/rb/io.py +0 -814
- pygsti/extras/rb/simulate.py +0 -1020
- pygsti/io/legacyio.py +0 -385
- pygsti/modelmembers/povms/denseeffect.py +0 -142
- {pyGSTi-0.9.12.1.dist-info → pyGSTi-0.9.13.dist-info}/LICENSE +0 -0
- {pyGSTi-0.9.12.1.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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
179
|
-
|
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
|
-
|
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', []),
|
pygsti/layouts/copalayout.py
CHANGED
@@ -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 =
|
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
|
-
|
201
|
-
|
202
|
-
indices = set(
|
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 =
|
208
|
-
self._element_indices =
|
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=
|
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).
|
pygsti/layouts/distlayout.py
CHANGED
@@ -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. """
|