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