pyGSTi 0.9.12.1__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.
- pyGSTi-0.9.13.dist-info/METADATA +185 -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.cp38-win32.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.cp38-win32.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.cp38-win32.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-win32.pyd +0 -0
- pygsti/evotypes/densitymx/effectreps.pyx +1 -1
- pygsti/evotypes/densitymx/opreps.cp38-win32.pyd +0 -0
- pygsti/evotypes/densitymx/opreps.pyx +2 -2
- pygsti/evotypes/densitymx/statereps.cp38-win32.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-win32.pyd +0 -0
- pygsti/evotypes/stabilizer/effectreps.pyx +0 -4
- pygsti/evotypes/stabilizer/opreps.cp38-win32.pyd +0 -0
- pygsti/evotypes/stabilizer/opreps.pyx +0 -4
- pygsti/evotypes/stabilizer/statereps.cp38-win32.pyd +0 -0
- pygsti/evotypes/stabilizer/statereps.pyx +1 -5
- pygsti/evotypes/stabilizer/termreps.cp38-win32.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-win32.pyd +0 -0
- pygsti/evotypes/statevec/effectreps.pyx +1 -1
- pygsti/evotypes/statevec/opreps.cp38-win32.pyd +0 -0
- pygsti/evotypes/statevec/opreps.pyx +2 -2
- pygsti/evotypes/statevec/statereps.cp38-win32.pyd +0 -0
- pygsti/evotypes/statevec/statereps.pyx +1 -1
- pygsti/evotypes/statevec/termreps.cp38-win32.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.cp38-win32.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.cp38-win32.pyd +0 -0
- pygsti/forwardsims/termforwardsim_calc_statevec.cp38-win32.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.cp38-win32.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
pygsti/circuits/circuit.py
CHANGED
@@ -20,12 +20,7 @@ from pygsti.baseobjs.label import Label as _Label, CircuitLabel as _CircuitLabel
|
|
20
20
|
from pygsti.baseobjs import outcomelabeldict as _ld, _compatibility as _compat
|
21
21
|
from pygsti.tools import internalgates as _itgs
|
22
22
|
from pygsti.tools import slicetools as _slct
|
23
|
-
|
24
|
-
|
25
|
-
#Internally:
|
26
|
-
# when static: a tuple of Label objects labelling each top-level circuit layer
|
27
|
-
# when editable: a list of lists, one per top-level layer, holding just
|
28
|
-
# the non-LabelTupTup (non-compound) labels.
|
23
|
+
from pygsti.tools.legacytools import deprecate as _deprecate_fn
|
29
24
|
|
30
25
|
#Externally, we'd like to do thinks like:
|
31
26
|
# c = Circuit( LabelList )
|
@@ -43,6 +38,11 @@ from pygsti.tools import slicetools as _slct
|
|
43
38
|
# c[1:3,'Q0'] = ('Gx','Gy') # assigns to a part of the Q0 line
|
44
39
|
|
45
40
|
|
41
|
+
#Add warning filter
|
42
|
+
msg = 'Could not find matching standard gate name in provided dictionary. Falling back to try and find a'\
|
43
|
+
+' unitary from standard_gatename_unitaries which matches up to a global phase.'
|
44
|
+
_warnings.filterwarnings('module', message=msg, category=UserWarning)
|
45
|
+
|
46
46
|
def _np_to_quil_def_str(name, input_array):
|
47
47
|
"""
|
48
48
|
Write a DEFGATE block for RQC quil for an arbitrary one- or two-qubit unitary gate.
|
@@ -75,7 +75,7 @@ def _np_to_quil_def_str(name, input_array):
|
|
75
75
|
def _num_to_rqc_str(num):
|
76
76
|
"""Convert float to string to be included in RQC quil DEFGATE block
|
77
77
|
(as written by _np_to_quil_def_str)."""
|
78
|
-
num = _np.
|
78
|
+
num = _np.complex128(_np.real_if_close(num))
|
79
79
|
if _np.imag(num) == 0:
|
80
80
|
output = str(_np.real(num))
|
81
81
|
return output
|
@@ -96,14 +96,13 @@ def _label_to_nested_lists_of_simple_labels(lbl, default_sslbls=None, always_ret
|
|
96
96
|
""" Convert lbl into nested lists of *simple* labels """
|
97
97
|
if not isinstance(lbl, _Label): # if not a Label, make into a label,
|
98
98
|
lbl = _Label(lbl) # e.g. a string or list/tuple of labels, etc.
|
99
|
-
if lbl.
|
99
|
+
if lbl.IS_SIMPLE: # a *simple* label - the elements of our lists
|
100
100
|
if lbl.sslbls is None and default_sslbls is not None:
|
101
101
|
lbl = _Label(lbl.name, default_sslbls)
|
102
102
|
return [lbl] if always_return_list else lbl
|
103
103
|
return [_label_to_nested_lists_of_simple_labels(l, default_sslbls, False)
|
104
104
|
for l in lbl.components] # a *list*
|
105
105
|
|
106
|
-
|
107
106
|
def _sslbls_of_nested_lists_of_simple_labels(obj, labels_to_ignore=None):
|
108
107
|
""" Get state space labels from a nested lists of simple (not compound) Labels. """
|
109
108
|
if isinstance(obj, _Label):
|
@@ -114,7 +113,6 @@ def _sslbls_of_nested_lists_of_simple_labels(obj, labels_to_ignore=None):
|
|
114
113
|
sub_sslbls = [_sslbls_of_nested_lists_of_simple_labels(sub, labels_to_ignore) for sub in obj]
|
115
114
|
return None if (None in sub_sslbls) else set(_itertools.chain(*sub_sslbls))
|
116
115
|
|
117
|
-
|
118
116
|
def _accumulate_explicit_sslbls(obj):
|
119
117
|
"""
|
120
118
|
Get all the explicitly given state-space labels within `obj`,
|
@@ -122,7 +120,7 @@ def _accumulate_explicit_sslbls(obj):
|
|
122
120
|
"""
|
123
121
|
ret = set()
|
124
122
|
if isinstance(obj, _Label):
|
125
|
-
if not obj.
|
123
|
+
if not obj.IS_SIMPLE:
|
126
124
|
for lbl in obj.components:
|
127
125
|
ret.update(_accumulate_explicit_sslbls(lbl))
|
128
126
|
else: # a simple label
|
@@ -200,87 +198,6 @@ class Circuit(object):
|
|
200
198
|
construct the circuit in place, after which `done_editing()` should be
|
201
199
|
called so that the Circuit can be properly hashed as needed.
|
202
200
|
|
203
|
-
Parameters
|
204
|
-
----------
|
205
|
-
layer_labels : iterable of Labels or str
|
206
|
-
This argument provides a list of the layer labels specifying the
|
207
|
-
state preparations, gates, and measurements for the circuit. This
|
208
|
-
argument can also be a :class:`Circuit` or a string, in which case
|
209
|
-
it is parsed as a text-formatted circuit. Internally this will
|
210
|
-
eventually be converted to a list of `Label` objects, one per layer,
|
211
|
-
but it may be specified using anything that can be readily converted
|
212
|
-
to a Label objects. For example, any of the following are allowed:
|
213
|
-
|
214
|
-
- `['Gx','Gx']` : X gate on each of 2 layers
|
215
|
-
- `[Label('Gx'),Label('Gx')]` : same as above
|
216
|
-
- `[('Gx',0),('Gy',0)]` : X then Y on qubit 0 (2 layers)
|
217
|
-
- `[[('Gx',0),('Gx',1)],[('Gy',0),('Gy',1)]]` : parallel X then Y on qubits 0 & 1
|
218
|
-
|
219
|
-
line_labels : iterable, optional
|
220
|
-
The (string valued) label for each circuit line. If `'auto'`, then
|
221
|
-
`line_labels` is taken to be the list of all state-space labels
|
222
|
-
present within `layer_labels`. If there are no such labels (e.g.
|
223
|
-
if `layer_labels` contains just gate names like `('Gx','Gy')`), then
|
224
|
-
the special value `'*'` is used as a single line label.
|
225
|
-
|
226
|
-
num_lines : int, optional
|
227
|
-
Specify this instead of `line_labels` to set the latter to the
|
228
|
-
integers between 0 and `num_lines-1`.
|
229
|
-
|
230
|
-
editable : bool, optional
|
231
|
-
Whether the created `Circuit` is created in able to be modified. If
|
232
|
-
`True`, then you should call `done_editing()` once the circuit is
|
233
|
-
completely assembled, as this makes the circuit read-only and
|
234
|
-
allows it to be hashed.
|
235
|
-
|
236
|
-
stringrep : string, optional
|
237
|
-
A string representation for the circuit. If `None` (the default),
|
238
|
-
then this will be generated automatically when needed. One
|
239
|
-
reason you'd want to specify this is if you know of a nice compact
|
240
|
-
string representation that you'd rather use, e.g. `"Gx^4"` instead
|
241
|
-
of the automatically generated `"GxGxGxGx"`. If you want to
|
242
|
-
initialize a `Circuit` entirely from a string representation you
|
243
|
-
can either specify the string in as `layer_labels` or set
|
244
|
-
`layer_labels` to `None` and `stringrep` to any valid (one-line)
|
245
|
-
circuit string.
|
246
|
-
|
247
|
-
name : str, optional
|
248
|
-
A name for this circuit (useful if/when used as a block within
|
249
|
-
larger circuits).
|
250
|
-
|
251
|
-
check : bool, optional
|
252
|
-
Whether `stringrep` should be checked against `layer_labels` to
|
253
|
-
ensure they are consistent, and whether the labels in `layer_labels`
|
254
|
-
are a subset of `line_labels`. The only reason you'd want to set
|
255
|
-
this to `False` is if you're absolutely sure `stringrep` and
|
256
|
-
`line_labels` are consistent and want to save computation time.
|
257
|
-
|
258
|
-
expand_subcircuits : bool or "default"
|
259
|
-
If `"default"`, then the value of `Circuit.default_expand_subcircuits`
|
260
|
-
is used. If True, then any sub-circuits (e.g. anything exponentiated
|
261
|
-
like "(GxGy)^4") will be expanded when it is stored within the created
|
262
|
-
Circuit. If False, then such sub-circuits will be left as-is. It's
|
263
|
-
typically more robust to expand sub-circuits as this facilitates
|
264
|
-
comparison (e.g. so "GxGx" == "Gx^2"), but in cases when you have
|
265
|
-
massive exponents (e.g. "Gx^8192") it may improve performance to
|
266
|
-
set `expand_subcircuits=False`.
|
267
|
-
|
268
|
-
occurrence : hashable, optional
|
269
|
-
A value to set as the "occurrence id" for this circuit. This
|
270
|
-
value doesn't affect the circuit an any way except by affecting
|
271
|
-
it's hashing and equivalence testing. Circuits with different
|
272
|
-
occurrence ids are *not* equivalent. Occurrence values effectively
|
273
|
-
allow multiple copies of the same ciruit to be stored in a
|
274
|
-
dictionary or :class:`DataSet`.
|
275
|
-
|
276
|
-
compilable_layer_indices : tuple, optional
|
277
|
-
The circuit-layer indices that may be internally altered (but retaining the
|
278
|
-
same target operation) and/or combined with the following circuit layer
|
279
|
-
by a hardware compiler.when executing this circuit. Layers that are
|
280
|
-
not "compilable" are effectively followed by a *barrier* which prevents
|
281
|
-
the hardward compiler from restructuring the circuit across the layer
|
282
|
-
boundary.
|
283
|
-
|
284
201
|
Attributes
|
285
202
|
----------
|
286
203
|
default_expand_subcircuits : bool
|
@@ -297,6 +214,17 @@ class Circuit(object):
|
|
297
214
|
|
298
215
|
str : str
|
299
216
|
The Python string representation of this Circuit.
|
217
|
+
|
218
|
+
layer_labels :
|
219
|
+
When static: a tuple of Label objects labelling each top-level circuit layer
|
220
|
+
When editable: a list of lists, one per top-level layer, holding just
|
221
|
+
the non-LabelTupTup (non-compound) labels. I.e. in the static case a LabelTupTup
|
222
|
+
which specifies a complete circuit layer is assumed to contain no LabelTupTups as
|
223
|
+
sub-components. Similarly, in the editable case a nested sublist which
|
224
|
+
contains a set of Labels for a complete circuit layer is assumed to contain
|
225
|
+
no further nested sublists as elements. For more complicated nested
|
226
|
+
circuit structures, if required, circuits can contain CircuitLabel objects as elements.
|
227
|
+
see :class:pygsti.baseobjs.label.CircuitLabel.
|
300
228
|
"""
|
301
229
|
default_expand_subcircuits = True
|
302
230
|
|
@@ -466,7 +394,6 @@ class Circuit(object):
|
|
466
394
|
expand_subcircuits = Circuit.default_expand_subcircuits
|
467
395
|
if expand_subcircuits and layer_labels is not None:
|
468
396
|
layer_labels_objs = tuple(_itertools.chain(*[x.expand_subcircuits() for x in map(to_label, layer_labels)]))
|
469
|
-
#print("DB: Layer labels = ",layer_labels_objs)
|
470
397
|
|
471
398
|
#Parse stringrep if needed
|
472
399
|
if stringrep is not None and (layer_labels is None or check):
|
@@ -475,7 +402,6 @@ class Circuit(object):
|
|
475
402
|
chk, chk_labels, chk_occurrence, chk_compilable_inds = cparser.parse(stringrep) # tuple of Labels
|
476
403
|
if expand_subcircuits and chk is not None:
|
477
404
|
chk = tuple(_itertools.chain(*[x.expand_subcircuits() for x in map(to_label, chk)]))
|
478
|
-
#print("DB: Check Layer labels = ",chk)
|
479
405
|
|
480
406
|
if layer_labels is None:
|
481
407
|
layer_labels = chk
|
@@ -483,7 +409,6 @@ class Circuit(object):
|
|
483
409
|
if layer_labels_objs is None:
|
484
410
|
layer_labels_objs = tuple(map(to_label, layer_labels))
|
485
411
|
if layer_labels_objs != tuple(chk):
|
486
|
-
#print("DB: ",layer_labels_objs,"VS",tuple(chk))
|
487
412
|
raise ValueError(("Error intializing Circuit: "
|
488
413
|
" `layer_labels` and `stringrep` do not match: %s != %s\n"
|
489
414
|
"(set `layer_labels` to None to infer it from `stringrep`)")
|
@@ -568,51 +493,80 @@ class Circuit(object):
|
|
568
493
|
if compilable_layer_indices is not None:
|
569
494
|
max_layer_index = len(labels) - 1
|
570
495
|
if any([(i < 0 or i > max_layer_index) for i in compilable_layer_indices]):
|
571
|
-
raise ValueError("Entry
|
572
|
-
|
496
|
+
raise ValueError("Entry out of range in `compilable_layer_indices`!")
|
497
|
+
compilable_layer_indices_tup = tuple(compilable_layer_indices)
|
498
|
+
else:
|
499
|
+
compilable_layer_indices_tup = ()
|
573
500
|
|
574
501
|
#Set *all* class attributes (separated so can call bare_init separately for fast internal creation)
|
575
|
-
self._bare_init(labels, my_line_labels, editable, name, stringrep,
|
576
|
-
|
577
|
-
|
578
|
-
# # (Note: a Circuit would work just fine, as a list of layers, but this performs some extra checks)
|
579
|
-
# isCircuit = isinstance(layer_labels, _Circuit)
|
580
|
-
# isCircuitLabel = isinstance(layer_labels, _CircuitLabel)
|
581
|
-
# if isCircuitLabel:
|
582
|
-
# assert(line_labels is None or line_labels == "auto" or line_labels == expected_line_labels), \
|
583
|
-
# "Given `line_labels` (%s) are inconsistent with CircuitLabel's sslbls (%s)" \
|
584
|
-
# % (str(line_labels),str(layer_labels.sslbls))
|
585
|
-
# assert(num_lines is None or layer_labels.sslbls == tuple(range(num_lines))), \
|
586
|
-
# "Given `num_lines` (%d) is inconsistend with CircuitLabel's sslbls (%s)" \
|
587
|
-
# % (num_lines,str(layer_labels.sslbls))
|
588
|
-
# if name is None: name = layer_labels.name # Note: `name` can be used to rename a CircuitLabel
|
589
|
-
|
590
|
-
# self._line_labels = layer_labels.sslbls
|
591
|
-
# self._reps = layer_labels.reps
|
592
|
-
# self._name = name
|
593
|
-
# self._static = not editable
|
502
|
+
self._bare_init(labels, my_line_labels, editable, name, stringrep,
|
503
|
+
occurrence, compilable_layer_indices_tup)
|
504
|
+
|
594
505
|
|
595
506
|
@classmethod
|
596
507
|
def _fastinit(cls, labels, line_labels, editable, name='', stringrep=None, occurrence=None,
|
597
|
-
|
508
|
+
compilable_layer_indices_tup=()):
|
598
509
|
ret = cls.__new__(cls)
|
599
|
-
ret._bare_init(labels, line_labels, editable, name, stringrep, occurrence)
|
510
|
+
ret._bare_init(labels, line_labels, editable, name, stringrep, occurrence, compilable_layer_indices_tup)
|
600
511
|
return ret
|
601
512
|
|
513
|
+
#Note: If editing _bare_init one should also check _copy_init in case changes must be propagated.
|
602
514
|
def _bare_init(self, labels, line_labels, editable, name='', stringrep=None, occurrence=None,
|
603
|
-
|
515
|
+
compilable_layer_indices_tup=()):
|
516
|
+
self._labels = labels
|
517
|
+
self._line_labels = tuple(line_labels)
|
518
|
+
self._occurrence_id = occurrence
|
519
|
+
self._compilable_layer_indices_tup = compilable_layer_indices_tup # always a tuple, but can be empty.
|
520
|
+
self._static = not editable
|
521
|
+
if self._static:
|
522
|
+
self._hashable_tup = self.tup #if static precompute and cache the hashable circuit tuple.
|
523
|
+
self._hash = hash(self._hashable_tup)
|
524
|
+
self._str = stringrep
|
525
|
+
else:
|
526
|
+
self._str = None # can be None (lazy generation)
|
527
|
+
self._name = name # can be None
|
528
|
+
#self._times = None # for FUTURE expansion
|
529
|
+
self.auxinfo = {} # for FUTURE expansion / user metadata
|
530
|
+
|
531
|
+
#Note: If editing _copy_init one should also check _bare_init in case changes must be propagated.
|
532
|
+
#specialized codepath for copying
|
533
|
+
def _copy_init(self, labels, line_labels, editable, name='', stringrep=None, occurrence=None,
|
534
|
+
compilable_layer_indices_tup=(), hashable_tup=None, precomp_hash=None):
|
604
535
|
self._labels = labels
|
605
536
|
self._line_labels = line_labels
|
606
537
|
self._occurrence_id = occurrence
|
607
|
-
self._compilable_layer_indices_tup =
|
608
|
-
if (compilable_layer_indices is not None) else () # always a tuple, but can be empty.
|
538
|
+
self._compilable_layer_indices_tup = compilable_layer_indices_tup # always a tuple, but can be empty.
|
609
539
|
self._static = not editable
|
610
|
-
|
540
|
+
if self._static:
|
541
|
+
self._hashable_tup = hashable_tup #if static we have already precomputed and cached the hashable circuit tuple.
|
542
|
+
self._hash = precomp_hash #Same as previous comment. Only meant to be used in settings where we're explicitly checking for self._static.
|
543
|
+
self._str = stringrep
|
544
|
+
else:
|
545
|
+
self._str = None # can be None (lazy generation)
|
611
546
|
self._name = name # can be None
|
612
|
-
self.
|
613
|
-
self._times = None # for FUTURE expansion
|
547
|
+
#self._times = None # for FUTURE expansion
|
614
548
|
self.auxinfo = {} # for FUTURE expansion / user metadata
|
615
|
-
|
549
|
+
|
550
|
+
return self
|
551
|
+
|
552
|
+
#pickle management functions
|
553
|
+
def __getstate__(self):
|
554
|
+
state_dict = self.__dict__
|
555
|
+
#if state_dict.get('_hash', None) is not None:
|
556
|
+
# del state_dict['_hash'] #don't store the hash, recompute at unpickling time
|
557
|
+
return state_dict
|
558
|
+
|
559
|
+
def __setstate__(self, state_dict):
|
560
|
+
for k, v in state_dict.items():
|
561
|
+
self.__dict__[k] = v
|
562
|
+
if self.__dict__['_static']:
|
563
|
+
#reinitialize the hash
|
564
|
+
if self.__dict__.get('_hashable_tup', None) is not None:
|
565
|
+
self._hash = hash(self._hashable_tup)
|
566
|
+
else: #legacy support
|
567
|
+
self._hashable_tup = self.tup
|
568
|
+
self._hash = hash(self._hashable_tup)
|
569
|
+
|
616
570
|
|
617
571
|
def to_label(self, nreps=1):
|
618
572
|
"""
|
@@ -648,17 +602,15 @@ class Circuit(object):
|
|
648
602
|
"""
|
649
603
|
The line labels (often qubit labels) of this circuit.
|
650
604
|
"""
|
605
|
+
assert(not self._static), \
|
606
|
+
("Cannot edit a read-only circuit! "
|
607
|
+
"Set editable=True when calling pygsti.baseobjs.Circuit to create editable circuit.")
|
651
608
|
if value == self._line_labels: return
|
652
|
-
#added_line_labels = set(value) - set(self._line_labels) # it's always OK to add lines
|
653
609
|
removed_line_labels = set(self._line_labels) - set(value)
|
654
610
|
if removed_line_labels:
|
655
611
|
idling_line_labels = set(self.idling_lines())
|
656
612
|
removed_not_idling = removed_line_labels - idling_line_labels
|
657
|
-
|
658
|
-
raise ValueError("Cannot remove non-idling lines %s from a read-only circuit!" %
|
659
|
-
str(removed_not_idling))
|
660
|
-
else:
|
661
|
-
self.delete_lines(tuple(removed_not_idling))
|
613
|
+
self.delete_lines(tuple(removed_not_idling))
|
662
614
|
self._line_labels = tuple(value)
|
663
615
|
self._str = None # regenerate string rep (it may have updated)
|
664
616
|
|
@@ -684,6 +636,9 @@ class Circuit(object):
|
|
684
636
|
"""
|
685
637
|
The occurrence id of this circuit.
|
686
638
|
"""
|
639
|
+
assert(not self._static), \
|
640
|
+
("Cannot edit a read-only circuit! "
|
641
|
+
"Set editable=True when calling pygsti.baseobjs.Circuit to create editable circuit.")
|
687
642
|
self._occurrence_id = value
|
688
643
|
self._str = None # regenerate string rep (it may have updated)
|
689
644
|
|
@@ -699,8 +654,8 @@ class Circuit(object):
|
|
699
654
|
if self._static:
|
700
655
|
return self._labels
|
701
656
|
else:
|
702
|
-
return tuple([
|
703
|
-
|
657
|
+
return tuple([layer_lbl if isinstance(layer_lbl, _Label)
|
658
|
+
else _Label(layer_lbl) for layer_lbl in self._labels])
|
704
659
|
@property
|
705
660
|
def tup(self):
|
706
661
|
"""
|
@@ -710,35 +665,66 @@ class Circuit(object):
|
|
710
665
|
-------
|
711
666
|
tuple
|
712
667
|
"""
|
668
|
+
comp_lbl_flag = ('__CMPLBL__',) if self._compilable_layer_indices_tup else ()
|
669
|
+
layertup = self._labels if self._static else self.layertup
|
670
|
+
|
713
671
|
if self._occurrence_id is None:
|
714
672
|
if self._line_labels in (('*',), ()): # No line labels
|
715
|
-
return
|
673
|
+
return layertup + comp_lbl_flag + self._compilable_layer_indices_tup
|
716
674
|
else:
|
717
|
-
return
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
+ self.
|
675
|
+
return layertup + ('@',) + self._line_labels + comp_lbl_flag\
|
676
|
+
+ self._compilable_layer_indices_tup
|
677
|
+
else:
|
678
|
+
if self._line_labels in (('*',), ()):
|
679
|
+
return layertup + ('@',) + ('@', self._occurrence_id) \
|
680
|
+
+ comp_lbl_flag + self._compilable_layer_indices_tup
|
681
|
+
else:
|
682
|
+
return layertup + ('@',) + self._line_labels + ('@', self._occurrence_id) \
|
683
|
+
+ comp_lbl_flag + self._compilable_layer_indices_tup
|
684
|
+
# Note: we *always* need line labels (even if they're empty) when using occurrence id
|
685
|
+
|
686
|
+
def _tup_copy(self, labels):
|
687
|
+
"""
|
688
|
+
This Circuit as a standard Python tuple of layer Labels and line labels.
|
689
|
+
This version takes as input a precomputed set of static layer labels
|
690
|
+
and uses this to avoid double computing this during copy operations.
|
691
|
+
Only presently intended for expediting copy operations.
|
692
|
+
Returns
|
693
|
+
-------
|
694
|
+
tuple
|
695
|
+
"""
|
696
|
+
comp_lbl_flag = ('__CMPLBL__',) if self._compilable_layer_indices_tup else ()
|
697
|
+
if self._occurrence_id is None:
|
698
|
+
if self._line_labels in (('*',), ()): # No line labels
|
699
|
+
return labels + comp_lbl_flag + self._compilable_layer_indices_tup
|
700
|
+
else:
|
701
|
+
return labels + ('@',) + self._line_labels + comp_lbl_flag + self._compilable_layer_indices_tup
|
702
|
+
else:
|
703
|
+
if self._line_labels in (('*',), ()):
|
704
|
+
return labels + ('@',) + ('@', self._occurrence_id) \
|
705
|
+
+ comp_lbl_flag + self._compilable_layer_indices_tup
|
706
|
+
else:
|
707
|
+
return labels + ('@',) + self._line_labels + ('@', self._occurrence_id) \
|
708
|
+
+ comp_lbl_flag + self._compilable_layer_indices_tup
|
722
709
|
# Note: we *always* need line labels (even if they're empty) when using occurrence id
|
723
710
|
|
724
711
|
@property
|
725
712
|
def compilable_layer_indices(self):
|
726
713
|
""" Tuple of the layer indices corresponding to "compilable" layers."""
|
727
|
-
|
728
|
-
return self._compilable_layer_indices_tup[1:]
|
729
|
-
else:
|
730
|
-
return ()
|
714
|
+
return self._compilable_layer_indices_tup
|
731
715
|
|
732
716
|
@compilable_layer_indices.setter
|
733
717
|
def compilable_layer_indices(self, val):
|
734
|
-
self.
|
735
|
-
|
718
|
+
assert(not self._static), \
|
719
|
+
("Cannot edit a read-only circuit! "
|
720
|
+
"Set editable=True when calling pygsti.baseobjs.Circuit to create editable circuit.")
|
721
|
+
self._compilable_layer_indices_tup = tuple(val) if (val is not None) else () # always a tuple, but can be empty.
|
736
722
|
|
737
723
|
@property
|
738
724
|
def compilable_by_layer(self):
|
739
725
|
""" Boolean array indicating whether each layer is "compilable" or not."""
|
740
726
|
ret = _np.zeros(self.depth, dtype=bool)
|
741
|
-
ret[list(self.
|
727
|
+
ret[list(self._compilable_layer_indices_tup)] = True
|
742
728
|
return ret
|
743
729
|
|
744
730
|
@property
|
@@ -751,8 +737,8 @@ class Circuit(object):
|
|
751
737
|
str
|
752
738
|
"""
|
753
739
|
if self._str is None:
|
754
|
-
generated_str = _op_seq_to_str(self._labels, self.
|
755
|
-
self.
|
740
|
+
generated_str = _op_seq_to_str(self._labels, self._line_labels, self._occurrence_id,
|
741
|
+
self._compilable_layer_indices_tup) # lazy generation
|
756
742
|
if self._static: # if we're read-only then cache the string one and for all,
|
757
743
|
self._str = generated_str # otherwise keep generating it as needed (unless it's set by the user?)
|
758
744
|
return generated_str
|
@@ -797,20 +783,20 @@ class Circuit(object):
|
|
797
783
|
" evaluate to %s (this circuit)") %
|
798
784
|
(value, self.str))
|
799
785
|
if chk_labels is not None:
|
800
|
-
if tuple(self.
|
786
|
+
if tuple(self._line_labels) != chk_labels:
|
801
787
|
raise ValueError(("Cannot set .str to %s because line labels evaluate to"
|
802
788
|
" %s which is != this circuit's line labels (%s).") %
|
803
|
-
(value, chk_labels, str(self.
|
789
|
+
(value, chk_labels, str(self._line_labels)))
|
804
790
|
if chk_occurrence is not None:
|
805
791
|
if self._occurrence_id != chk_occurrence:
|
806
792
|
raise ValueError(("Cannot set .str to %s because occurrence evaluates to"
|
807
793
|
" %s which is != this circuit's occurrence (%s).") %
|
808
794
|
(value, str(chk_occurrence), str(self._occurrence_id)))
|
809
795
|
if chk_compilable_inds is not None:
|
810
|
-
if self.
|
796
|
+
if self._compilable_layer_indices_tup != chk_compilable_inds:
|
811
797
|
raise ValueError(("Cannot set .str to %s because compilable layer indices eval to"
|
812
798
|
" %s which is != this circuit's indices (%s).") %
|
813
|
-
(value, str(chk_compilable_inds), str(self.
|
799
|
+
(value, str(chk_compilable_inds), str(self._compilable_layer_indices_tup)))
|
814
800
|
|
815
801
|
self._str = value
|
816
802
|
|
@@ -820,11 +806,7 @@ class Circuit(object):
|
|
820
806
|
" mode in order to hash it. You should call"
|
821
807
|
" circuit.done_editing() beforehand."))
|
822
808
|
self.done_editing()
|
823
|
-
return
|
824
|
-
#if self._line_labels in (('*',),()): #No line labels
|
825
|
-
# return hash(self._labels)
|
826
|
-
#else:
|
827
|
-
# return hash(self._labels + ('@',) + self._line_labels)
|
809
|
+
return self._hash
|
828
810
|
|
829
811
|
def __len__(self):
|
830
812
|
return len(self._labels)
|
@@ -839,7 +821,7 @@ class Circuit(object):
|
|
839
821
|
def __radd__(self, x):
|
840
822
|
if not isinstance(x, Circuit):
|
841
823
|
assert(all([isinstance(l, _Label) for l in x])), "Only Circuits and Label-tuples can be added to Circuits!"
|
842
|
-
return Circuit._fastinit(x + self.layertup, self.
|
824
|
+
return Circuit._fastinit(x + self.layertup, self._line_labels, editable=False)
|
843
825
|
return x.__add__(self)
|
844
826
|
|
845
827
|
def __add__(self, x):
|
@@ -862,24 +844,28 @@ class Circuit(object):
|
|
862
844
|
|
863
845
|
if not isinstance(x, Circuit):
|
864
846
|
assert(all([isinstance(l, _Label) for l in x])), "Only Circuits and Label-tuples can be added to Circuits!"
|
865
|
-
|
847
|
+
new_line_labels = set(sum([l.sslbls for l in x if l.sslbls is not None],
|
848
|
+
self._line_labels)) #trick for concatenating multiple tuples
|
849
|
+
#new_line_labels.update(self._line_labels)
|
850
|
+
new_line_labels = sorted(new_line_labels)
|
851
|
+
return Circuit._fastinit(self.layertup + x, new_line_labels, editable=False)
|
866
852
|
|
867
853
|
#Add special line label handling to deal with the special global idle circuits (which have no line labels
|
868
854
|
# associated with them typically).
|
869
855
|
#Check if a the circuit or labels being added are all global idles, if so inherit the
|
870
856
|
#line labels from the circuit being added to. Otherwise, enforce compatibility.
|
871
|
-
layertup_x = x.layertup
|
857
|
+
layertup_x = x.layertup
|
872
858
|
gbl_idle_x= all([lbl == _Label(()) for lbl in layertup_x])
|
873
859
|
gbl_idle_self= all([lbl == _Label(()) for lbl in self.layertup])
|
874
860
|
|
875
861
|
if not (gbl_idle_x or gbl_idle_self):
|
876
|
-
combined_labels = {x.
|
862
|
+
combined_labels = {x._line_labels, self._line_labels}
|
877
863
|
elif not gbl_idle_x and gbl_idle_self:
|
878
|
-
combined_labels = {x.
|
864
|
+
combined_labels = {x._line_labels}
|
879
865
|
elif gbl_idle_x and not gbl_idle_self:
|
880
|
-
combined_labels = {self.
|
866
|
+
combined_labels = {self._line_labels}
|
881
867
|
else: #both are all global idles so it doesn't matter which we take.
|
882
|
-
combined_labels = {self.
|
868
|
+
combined_labels = {self._line_labels}
|
883
869
|
|
884
870
|
#check that the line labels are compatible between circuits.
|
885
871
|
#i.e. raise error if adding circuit with * line label to one with
|
@@ -913,7 +899,7 @@ class Circuit(object):
|
|
913
899
|
#unpack all of the different sets of labels and make sure there are no duplicates
|
914
900
|
combined_labels_unpacked = {el for tup in combined_labels for el in tup}
|
915
901
|
try:
|
916
|
-
new_line_labels = tuple(sorted(
|
902
|
+
new_line_labels = tuple(sorted(combined_labels_unpacked))
|
917
903
|
except TypeError:
|
918
904
|
new_line_labels = tuple(combined_labels_unpacked)
|
919
905
|
|
@@ -922,6 +908,33 @@ class Circuit(object):
|
|
922
908
|
|
923
909
|
return Circuit._fastinit(self.layertup + x.layertup, new_line_labels, editable=False, name='',
|
924
910
|
stringrep=s, occurrence=None)
|
911
|
+
|
912
|
+
|
913
|
+
def sandwich(self, x, y):
|
914
|
+
"""
|
915
|
+
Method for sandwiching labels around this circuit.
|
916
|
+
|
917
|
+
Parameters
|
918
|
+
----------
|
919
|
+
x : tuple of `Label` objects
|
920
|
+
Tuple of Labels to prepend to this
|
921
|
+
Circuit.
|
922
|
+
|
923
|
+
y: tuple of `Label` objects
|
924
|
+
Same as `x`, but appended instead.
|
925
|
+
|
926
|
+
Returns
|
927
|
+
-------
|
928
|
+
Circuit
|
929
|
+
"""
|
930
|
+
|
931
|
+
assert(isinstance(x, tuple) and isinstance(y, tuple)), 'Only tuples of labels are currently supported by `sandwich` method.'
|
932
|
+
combined_sandwich_labels = x + y
|
933
|
+
assert(all([isinstance(l, _Label) for l in combined_sandwich_labels])), "Only Circuits and Label-tuples can be added to Circuits!"
|
934
|
+
new_line_labels = set(sum([l.sslbls for l in combined_sandwich_labels if l.sslbls is not None],
|
935
|
+
self._line_labels)) #trick for concatenating multiple tuples
|
936
|
+
new_line_labels = tuple(sorted(new_line_labels))
|
937
|
+
return Circuit._fastinit(x + self.layertup + y, new_line_labels, editable=False)
|
925
938
|
|
926
939
|
def repeat(self, ntimes, expand="default"):
|
927
940
|
"""
|
@@ -948,10 +961,10 @@ class Circuit(object):
|
|
948
961
|
s += "@" + mylines # add line labels
|
949
962
|
if ntimes >= 1 and expand is False:
|
950
963
|
reppedCircuitLbl = self.to_label(nreps=ntimes)
|
951
|
-
return Circuit((reppedCircuitLbl,), self.
|
964
|
+
return Circuit((reppedCircuitLbl,), self._line_labels, None, not self._static, s, check=False)
|
952
965
|
else:
|
953
966
|
# just adds parens to string rep & copies
|
954
|
-
return Circuit(self.layertup * ntimes, self.
|
967
|
+
return Circuit(self.layertup * ntimes, self._line_labels, None, not self._static, s, check=False)
|
955
968
|
|
956
969
|
def __mul__(self, x):
|
957
970
|
return self.repeat(x)
|
@@ -960,21 +973,39 @@ class Circuit(object):
|
|
960
973
|
return self.__mul__(x)
|
961
974
|
|
962
975
|
def __eq__(self, x):
|
963
|
-
|
976
|
+
|
964
977
|
if isinstance(x, Circuit):
|
965
|
-
|
978
|
+
if len(self) != len(x):
|
979
|
+
return False
|
980
|
+
else:
|
981
|
+
if self._static and x._static:
|
982
|
+
return self._hash == x._hash
|
983
|
+
else:
|
984
|
+
return self.tup == x.tup
|
985
|
+
elif x is None:
|
986
|
+
return False
|
966
987
|
else:
|
967
|
-
|
988
|
+
tup_x = tuple(x)
|
989
|
+
if len(self.layertup) != len(tup_x):
|
990
|
+
return False
|
991
|
+
else:
|
992
|
+
return self.layertup == tup_x # equality with non-circuits is just based on *labels*
|
968
993
|
|
969
994
|
def __lt__(self, x):
|
970
995
|
if isinstance(x, Circuit):
|
971
|
-
|
996
|
+
if self._static and x._static:
|
997
|
+
return self._hashable_tup.__lt__(x._hashable_tup)
|
998
|
+
else:
|
999
|
+
return self.tup.__lt__(x.tup)
|
972
1000
|
else:
|
973
1001
|
return self.layertup < tuple(x) # comparison with non-circuits is just based on *labels*
|
974
1002
|
|
975
1003
|
def __gt__(self, x):
|
976
1004
|
if isinstance(x, Circuit):
|
977
|
-
|
1005
|
+
if self._static and x._static:
|
1006
|
+
return self._hashable_tup.__gt__(x._hashable_tup)
|
1007
|
+
else:
|
1008
|
+
return self.tup.__gt__(x.tup)
|
978
1009
|
else:
|
979
1010
|
return self.layertup > tuple(x) # comparison with non-circuits is just based on *labels*
|
980
1011
|
|
@@ -987,9 +1018,9 @@ class Circuit(object):
|
|
987
1018
|
-------
|
988
1019
|
int
|
989
1020
|
"""
|
990
|
-
return len(self.
|
991
|
-
|
992
|
-
def copy(self, editable=
|
1021
|
+
return len(self._line_labels)
|
1022
|
+
|
1023
|
+
def copy(self, editable='auto'):
|
993
1024
|
"""
|
994
1025
|
Returns a copy of the circuit.
|
995
1026
|
|
@@ -1003,9 +1034,43 @@ class Circuit(object):
|
|
1003
1034
|
-------
|
1004
1035
|
Circuit
|
1005
1036
|
"""
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1037
|
+
|
1038
|
+
if editable == "auto":
|
1039
|
+
editable = not self._static
|
1040
|
+
|
1041
|
+
#inline new circuit creation.
|
1042
|
+
ret = Circuit.__new__(Circuit)
|
1043
|
+
|
1044
|
+
if editable:
|
1045
|
+
if self._static:
|
1046
|
+
#static and editable circuits have different conventions for _labels.
|
1047
|
+
editable_labels =[[lbl] if lbl.IS_SIMPLE else list(lbl.components) for lbl in self._labels]
|
1048
|
+
return ret._copy_init(editable_labels, self._line_labels, editable,
|
1049
|
+
self._name, self._str, self._occurrence_id,
|
1050
|
+
self._compilable_layer_indices_tup)
|
1051
|
+
else:
|
1052
|
+
#copy the editable labels (avoiding shallow copy issues)
|
1053
|
+
editable_labels = [sublist.copy() for sublist in self._labels]
|
1054
|
+
return ret._copy_init(editable_labels, self._line_labels, editable,
|
1055
|
+
self._name, self._str, self._occurrence_id,
|
1056
|
+
self._compilable_layer_indices_tup)
|
1057
|
+
else: #create static copy
|
1058
|
+
if self._static:
|
1059
|
+
#if presently static leverage precomputed hashable_tup and hash.
|
1060
|
+
#These values are only used by _copy_init if the circuit being
|
1061
|
+
#created is static, and are ignored otherwise.
|
1062
|
+
return ret._copy_init(self._labels, self._line_labels, editable,
|
1063
|
+
self._name, self._str, self._occurrence_id,
|
1064
|
+
self._compilable_layer_indices_tup,
|
1065
|
+
self._hashable_tup, self._hash)
|
1066
|
+
else:
|
1067
|
+
static_labels = tuple([layer_lbl if isinstance(layer_lbl, _Label) else _Label(layer_lbl)
|
1068
|
+
for layer_lbl in self._labels])
|
1069
|
+
hashable_tup = self._tup_copy(static_labels)
|
1070
|
+
return ret._copy_init(static_labels, self._line_labels,
|
1071
|
+
editable, self._name, self._str, self._occurrence_id,
|
1072
|
+
self._compilable_layer_indices_tup,
|
1073
|
+
hashable_tup, hash(hashable_tup))
|
1009
1074
|
|
1010
1075
|
def clear(self):
|
1011
1076
|
"""
|
@@ -1034,10 +1099,10 @@ class Circuit(object):
|
|
1034
1099
|
def _proc_lines_arg(self, lines):
|
1035
1100
|
""" Pre-process the lines argument used by many methods """
|
1036
1101
|
if lines is None:
|
1037
|
-
lines = self.
|
1102
|
+
lines = self._line_labels
|
1038
1103
|
elif isinstance(lines, slice):
|
1039
1104
|
if lines.start is None and lines.stop is None:
|
1040
|
-
lines = self.
|
1105
|
+
lines = self._line_labels
|
1041
1106
|
else:
|
1042
1107
|
lines = _slct.indices(lines)
|
1043
1108
|
elif not isinstance(lines, (list, tuple)):
|
@@ -1047,19 +1112,18 @@ class Circuit(object):
|
|
1047
1112
|
def _proc_key_arg(self, key):
|
1048
1113
|
""" Pre-process the key argument used by many methods """
|
1049
1114
|
if isinstance(key, tuple):
|
1050
|
-
if len(key) != 2:
|
1051
|
-
|
1052
|
-
|
1115
|
+
if len(key) != 2:
|
1116
|
+
return IndexError("Index must be of the form <layerIndex>,<lineIndex>")
|
1117
|
+
else:
|
1118
|
+
return key[0], key[1]
|
1053
1119
|
else:
|
1054
|
-
|
1055
|
-
lines = None
|
1056
|
-
return layers, lines
|
1120
|
+
return key, None
|
1057
1121
|
|
1058
1122
|
def _layer_components(self, ilayer):
|
1059
1123
|
""" Get the components of the `ilayer`-th layer as a list/tuple. """
|
1060
1124
|
#(works for static and non-static Circuits)
|
1061
1125
|
if self._static:
|
1062
|
-
if self._labels[ilayer].
|
1126
|
+
if self._labels[ilayer].IS_SIMPLE: return [self._labels[ilayer]]
|
1063
1127
|
else: return self._labels[ilayer].components
|
1064
1128
|
else:
|
1065
1129
|
return self._labels[ilayer] if isinstance(self._labels[ilayer], list) \
|
@@ -1143,21 +1207,41 @@ class Circuit(object):
|
|
1143
1207
|
`layers` is a single integer and as a `Circuit` otherwise.
|
1144
1208
|
Note: if you want a `Circuit` when only selecting one layer,
|
1145
1209
|
set `layers` to a slice or tuple containing just a single index.
|
1210
|
+
Note that the returned circuit doesn't retain any original
|
1211
|
+
metadata, such as the compilable layer indices or occurence id.
|
1146
1212
|
"""
|
1147
1213
|
nonint_layers = not isinstance(layers, int)
|
1148
1214
|
|
1149
1215
|
#Shortcut for common case when lines == None and when we're only taking a layer slice/index
|
1150
|
-
if lines is None:
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1216
|
+
if lines is None and layers is not None:
|
1217
|
+
if self._static:
|
1218
|
+
if not nonint_layers:
|
1219
|
+
return self._labels[layers]
|
1220
|
+
if isinstance(layers, slice) and strict is True: # if strict=False, then need to recompute line labels
|
1221
|
+
#can speed this up a measurably by manually computing the new hashable tuple value and hash
|
1222
|
+
if not self._line_labels in (('*',), ()):
|
1223
|
+
new_hashable_tup = self._labels[layers] + ('@',) + self._line_labels
|
1224
|
+
else:
|
1225
|
+
new_hashable_tup = self._labels[layers]
|
1226
|
+
ret = Circuit.__new__(Circuit)
|
1227
|
+
return ret._copy_init(self._labels[layers], self._line_labels, not self._static, hashable_tup= new_hashable_tup, precomp_hash=hash(new_hashable_tup))
|
1228
|
+
else:
|
1229
|
+
if not nonint_layers:
|
1230
|
+
return self.layertup[layers]
|
1231
|
+
if isinstance(layers, slice) and strict is True: # if strict=False, then need to recompute line labels
|
1232
|
+
return Circuit._fastinit(self._labels[layers], self._line_labels, not self._static)
|
1233
|
+
#otherwise assert both are not None:
|
1234
|
+
|
1155
1235
|
|
1156
1236
|
layers = self._proc_layers_arg(layers)
|
1157
1237
|
lines = self._proc_lines_arg(lines)
|
1158
1238
|
if len(layers) == 0 or len(lines) == 0:
|
1159
|
-
|
1160
|
-
|
1239
|
+
if self._static:
|
1240
|
+
return Circuit._fastinit((), tuple(lines), False) # zero-area region
|
1241
|
+
else:
|
1242
|
+
return Circuit._fastinit(() if self._static else [],
|
1243
|
+
tuple(lines) if self._static else lines,
|
1244
|
+
not self._static) # zero-area region
|
1161
1245
|
|
1162
1246
|
ret = []
|
1163
1247
|
if self._static:
|
@@ -1173,7 +1257,7 @@ class Circuit(object):
|
|
1173
1257
|
## add in special case of identity layer
|
1174
1258
|
#if (isinstance(l,_Label) and l.name == self.identity): # ~ is_identity_layer(l)
|
1175
1259
|
# ret_layer.append(l); continue
|
1176
|
-
sslbls = set(self.
|
1260
|
+
sslbls = set(self._line_labels) # otherwise, treat None sslbs as *all* labels
|
1177
1261
|
else:
|
1178
1262
|
sslbls = set(sslbls)
|
1179
1263
|
if (strict and sslbls.issubset(lines)) or \
|
@@ -1184,7 +1268,10 @@ class Circuit(object):
|
|
1184
1268
|
if nonint_layers:
|
1185
1269
|
if not strict: lines = "auto" # since we may have included lbls on other lines
|
1186
1270
|
# don't worry about string rep for now...
|
1187
|
-
|
1271
|
+
|
1272
|
+
return Circuit._fastinit(tuple(ret) if self._static else ret,
|
1273
|
+
tuple(lines) if self._static else lines,
|
1274
|
+
not self._static)
|
1188
1275
|
else:
|
1189
1276
|
return _Label(ret[0])
|
1190
1277
|
|
@@ -1248,9 +1335,9 @@ class Circuit(object):
|
|
1248
1335
|
lbls_sslbls = None if (lbls.sslbls is None) else set(lbls.sslbls)
|
1249
1336
|
else:
|
1250
1337
|
if isinstance(lbls, Circuit):
|
1251
|
-
assert(set(lbls.
|
1338
|
+
assert(set(lbls._line_labels).issubset(self._line_labels)), \
|
1252
1339
|
"Assigned circuit has lines (%s) not contained in this circuit (%s)!" \
|
1253
|
-
% (str(lbls.
|
1340
|
+
% (str(lbls._line_labels), str(self._line_labels))
|
1254
1341
|
lbls = lbls.layertup # circuit layer labels as a tuple
|
1255
1342
|
assert(isinstance(lbls, (tuple, list))), \
|
1256
1343
|
("When assigning to a layer range (even w/len=1) `lbls` "
|
@@ -1273,18 +1360,18 @@ class Circuit(object):
|
|
1273
1360
|
# the lines being assigned. If sslbl != None, then the labels must be
|
1274
1361
|
# contained within the line labels being assigned (unless we're allowed to expand)
|
1275
1362
|
if lbls_sslbls is not None:
|
1276
|
-
new_line_labels = set(lbls_sslbls) - set(self.
|
1363
|
+
new_line_labels = set(lbls_sslbls) - set(self._line_labels)
|
1277
1364
|
if all_lines: # then allow new lines to be added
|
1278
1365
|
if len(new_line_labels) > 0:
|
1279
|
-
self._line_labels = self.
|
1366
|
+
self._line_labels = self._line_labels + tuple(sorted(new_line_labels)) # sort?
|
1280
1367
|
else:
|
1281
1368
|
assert(len(new_line_labels) == 0), "Cannot add new lines %s" % str(new_line_labels)
|
1282
1369
|
assert(set(lbls_sslbls).issubset(lines)), \
|
1283
1370
|
"Unallowed state space labels: %s" % str(set(lbls_sslbls) - set(lines))
|
1284
1371
|
|
1285
|
-
assert(set(lines).issubset(self.
|
1372
|
+
assert(set(lines).issubset(self._line_labels)), \
|
1286
1373
|
("Specified lines (%s) must be a subset of this circuit's lines"
|
1287
|
-
" (%s).") % (str(lines), str(self.
|
1374
|
+
" (%s).") % (str(lines), str(self._line_labels))
|
1288
1375
|
|
1289
1376
|
#remove all labels in block to be assigned
|
1290
1377
|
self._clear_labels(layers, lines)
|
@@ -1367,10 +1454,10 @@ class Circuit(object):
|
|
1367
1454
|
self._labels.insert(insert_before, [])
|
1368
1455
|
|
1369
1456
|
#Shift compilable layer indices as needed
|
1370
|
-
if
|
1457
|
+
if self._compilable_layer_indices_tup:
|
1371
1458
|
shifted_inds = [i if (i < insert_before) else (i + num_to_insert)
|
1372
|
-
for i in self._compilable_layer_indices_tup
|
1373
|
-
self._compilable_layer_indices_tup =
|
1459
|
+
for i in self._compilable_layer_indices_tup]
|
1460
|
+
self._compilable_layer_indices_tup = tuple(shifted_inds)
|
1374
1461
|
|
1375
1462
|
else: # insert layers only on given lines - shift existing labels to right
|
1376
1463
|
for i in range(num_to_insert):
|
@@ -1413,6 +1500,7 @@ class Circuit(object):
|
|
1413
1500
|
-------
|
1414
1501
|
None
|
1415
1502
|
"""
|
1503
|
+
assert(not self._static), "Cannot edit a read-only circuit!"
|
1416
1504
|
self.insert_idling_layers_inplace(None, num_to_insert, lines)
|
1417
1505
|
|
1418
1506
|
def insert_labels_into_layers(self, lbls, layer_to_insert_before, lines=None):
|
@@ -1481,6 +1569,7 @@ class Circuit(object):
|
|
1481
1569
|
-------
|
1482
1570
|
None
|
1483
1571
|
"""
|
1572
|
+
assert(not self._static), "Cannot edit a read-only circuit!"
|
1484
1573
|
if isinstance(lbls, Circuit): lbls = tuple(lbls)
|
1485
1574
|
# lbls is expected to be a list/tuple of Label-like items, one per inserted layer
|
1486
1575
|
lbls = tuple(map(to_label, lbls))
|
@@ -1530,13 +1619,12 @@ class Circuit(object):
|
|
1530
1619
|
-------
|
1531
1620
|
None
|
1532
1621
|
"""
|
1533
|
-
|
1534
|
-
# Actually, this is OK even for static circuits because it won't affect the hashed value (labels only)
|
1622
|
+
assert(not self._static),"Cannot edit a read-only circuit!"
|
1535
1623
|
if insert_before is None:
|
1536
|
-
i = len(self.
|
1624
|
+
i = len(self._line_labels)
|
1537
1625
|
else:
|
1538
|
-
i = self.
|
1539
|
-
self._line_labels = self.
|
1626
|
+
i = self._line_labels.index(insert_before)
|
1627
|
+
self._line_labels = self._line_labels[0:i] + tuple(line_labels) + self._line_labels[i:]
|
1540
1628
|
|
1541
1629
|
def _append_idling_lines(self, line_labels):
|
1542
1630
|
"""
|
@@ -1589,6 +1677,8 @@ class Circuit(object):
|
|
1589
1677
|
-------
|
1590
1678
|
None
|
1591
1679
|
"""
|
1680
|
+
assert(not self._static), "Cannot edit a read-only circuit!"
|
1681
|
+
|
1592
1682
|
if layer_to_insert_before is None: layer_to_insert_before = 0
|
1593
1683
|
elif layer_to_insert_before < 0: layer_to_insert_before = len(self._labels) + layer_to_insert_before
|
1594
1684
|
|
@@ -1598,7 +1688,7 @@ class Circuit(object):
|
|
1598
1688
|
elif line_labels == "auto":
|
1599
1689
|
line_labels = tuple(sorted(_accumulate_explicit_sslbls(lbls)))
|
1600
1690
|
|
1601
|
-
existing_labels = set(line_labels).intersection(self.
|
1691
|
+
existing_labels = set(line_labels).intersection(self._line_labels)
|
1602
1692
|
if len(existing_labels) > 0:
|
1603
1693
|
raise ValueError("Cannot insert line(s) labeled %s - they already exist!" % str(existing_labels))
|
1604
1694
|
|
@@ -1692,7 +1782,7 @@ class Circuit(object):
|
|
1692
1782
|
new_layer = []
|
1693
1783
|
for l in self._layer_components(i): # loop over labels in this layer
|
1694
1784
|
sslbls = _sslbls_of_nested_lists_of_simple_labels(l)
|
1695
|
-
sslbls = set(self.
|
1785
|
+
sslbls = set(self._line_labels) if (sslbls is None) else set(sslbls)
|
1696
1786
|
if len(sslbls.intersection(lines)) == 0:
|
1697
1787
|
new_layer.append(l)
|
1698
1788
|
elif not clear_straddlers and not sslbls.issubset(lines):
|
@@ -1747,12 +1837,12 @@ class Circuit(object):
|
|
1747
1837
|
del self._labels[i]
|
1748
1838
|
|
1749
1839
|
#Shift compilable layer indices as needed
|
1750
|
-
if
|
1840
|
+
if self._compilable_layer_indices_tup:
|
1751
1841
|
deleted_indices = set(layers)
|
1752
|
-
new_inds =
|
1842
|
+
new_inds = [x for x in self._compilable_layer_indices_tup if x not in deleted_indices]
|
1753
1843
|
for deleted_i in reversed(sorted(deleted_indices)):
|
1754
1844
|
new_inds = [i if (i < deleted_i) else (i - 1) for i in new_inds] # Note i never == deleted_i (filtered)
|
1755
|
-
self._compilable_layer_indices_tup =
|
1845
|
+
self._compilable_layer_indices_tup = tuple(new_inds)
|
1756
1846
|
|
1757
1847
|
def delete_lines(self, lines, delete_straddlers=False):
|
1758
1848
|
"""
|
@@ -1784,7 +1874,7 @@ class Circuit(object):
|
|
1784
1874
|
raise ValueError(("Cannot remove a block that is straddled by "
|
1785
1875
|
"%s when `delete_straddlers` == False!") % _Label(l))
|
1786
1876
|
self._labels[i] = new_layer
|
1787
|
-
self._line_labels = tuple([x for x in self.
|
1877
|
+
self._line_labels = tuple([x for x in self._line_labels if x not in lines])
|
1788
1878
|
|
1789
1879
|
def __getitem__(self, key):
|
1790
1880
|
layers, lines = self._proc_key_arg(key)
|
@@ -1898,7 +1988,7 @@ class Circuit(object):
|
|
1898
1988
|
if len(lbl.components) == 0: # special case of an empty-layer label,
|
1899
1989
|
serial_lbls.append(lbl) # which we serialize as an atomic object
|
1900
1990
|
serial_lbls.extend(list(lbl.components) * lbl.reps)
|
1901
|
-
return Circuit._fastinit(tuple(serial_lbls), self.
|
1991
|
+
return Circuit._fastinit(tuple(serial_lbls), self._line_labels, editable=False, occurrence=self.occurrence)
|
1902
1992
|
|
1903
1993
|
def parallelize(self, can_break_labels=True, adjacent_only=False):
|
1904
1994
|
"""
|
@@ -1984,7 +2074,7 @@ class Circuit(object):
|
|
1984
2074
|
|
1985
2075
|
# Convert elements of `parallel_lbls` into Labels (needed b/c we use _fastinit below)
|
1986
2076
|
parallel_lbls = [_Label(lbl_list) if len(lbl_list) != 1 else lbl_list[0] for lbl_list in parallel_lbls]
|
1987
|
-
return Circuit._fastinit(tuple(parallel_lbls), self.
|
2077
|
+
return Circuit._fastinit(tuple(parallel_lbls), self._line_labels, editable=False, occurrence=self._occurrence_id)
|
1988
2078
|
|
1989
2079
|
def expand_subcircuits_inplace(self):
|
1990
2080
|
"""
|
@@ -1998,25 +2088,41 @@ class Circuit(object):
|
|
1998
2088
|
None
|
1999
2089
|
"""
|
2000
2090
|
assert(not self._static), "Cannot edit a read-only circuit!"
|
2001
|
-
|
2002
|
-
#
|
2003
|
-
#
|
2091
|
+
|
2092
|
+
#_subcircuits_to_expand returns list of tuples
|
2093
|
+
#with the circuits to expand. The first entry of each tuple
|
2094
|
+
#is the index of the layer, with the rest of the entries the
|
2095
|
+
#CircuitLabels to expand. And these indices are given in descending
|
2096
|
+
#order.
|
2097
|
+
subcircs_to_expand = self._subcircuits_to_expand()
|
2098
|
+
while subcircs_to_expand:
|
2099
|
+
for subcirc_tup in subcircs_to_expand:
|
2100
|
+
layer_idx = subcirc_tup[0]
|
2101
|
+
subcircs = subcirc_tup[1:]
|
2102
|
+
#want a different notion of depth than that of CircuitLabel, since that depth
|
2103
|
+
#is calculated recursively, and we're handling the recursion manually.
|
2104
|
+
length_components = [len(l.components)*l.reps for l in subcircs]
|
2105
|
+
layers_to_add = max(0, *[comp_len - 1 for comp_len in length_components])
|
2106
|
+
if layers_to_add:
|
2107
|
+
self.insert_idling_layers_inplace(layer_idx + 1, layers_to_add)
|
2108
|
+
for depth, subc in zip(length_components, subcircs):
|
2109
|
+
self.clear_labels(slice(layer_idx, layer_idx + depth), subc.sslbls) # remove the CircuitLabel
|
2110
|
+
self.set_labels(subc.components * subc.reps, slice(layer_idx, layer_idx + depth), subc.sslbls) # dump in the contents
|
2111
|
+
#loop back through the circuit and see if we need to take another pass.
|
2112
|
+
subcircs_to_expand = self._subcircuits_to_expand()
|
2113
|
+
|
2114
|
+
def _subcircuits_to_expand(self):
|
2115
|
+
#Return this as a list of sparse list of tuples, giving only the layers which
|
2116
|
+
#contain CircuitLabels to be expanded. The first entry of the tuple will be the
|
2117
|
+
#original layer index, and the will be ordered in descending value to perform
|
2118
|
+
#expansion in reverse.
|
2119
|
+
subckts_to_expand = []
|
2004
2120
|
for i in reversed(range(len(self._labels))):
|
2005
|
-
|
2006
|
-
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
circuits_to_expand.append(l)
|
2011
|
-
layers_to_add = max(layers_to_add, l.depth - 1)
|
2012
|
-
|
2013
|
-
if layers_to_add > 0:
|
2014
|
-
self.insert_idling_layers_inplace(i + 1, layers_to_add)
|
2015
|
-
for subc in circuits_to_expand:
|
2016
|
-
self.clear_labels(slice(i, i + subc.depth), subc.sslbls) # remove the CircuitLabel
|
2017
|
-
self.set_labels(subc.components * subc.reps, slice(i, i + subc.depth),
|
2018
|
-
subc.sslbls) # dump in the contents
|
2019
|
-
|
2121
|
+
subckts_to_expand_for_layer = [l for l in self._labels[i] if isinstance(l, _CircuitLabel)]
|
2122
|
+
if subckts_to_expand_for_layer:
|
2123
|
+
subckts_to_expand.append(tuple([i]+subckts_to_expand_for_layer))
|
2124
|
+
return subckts_to_expand
|
2125
|
+
|
2020
2126
|
def expand_subcircuits(self):
|
2021
2127
|
"""
|
2022
2128
|
Returns a new circuit with :class:`CircuitLabel` labels expanded.
|
@@ -2054,13 +2160,11 @@ class Circuit(object):
|
|
2054
2160
|
while iEnd < nLayers and self._labels[iStart] == self._labels[iEnd]:
|
2055
2161
|
iEnd += 1
|
2056
2162
|
nreps = iEnd - iStart
|
2057
|
-
#print("Start,End = ",iStart,iEnd)
|
2058
2163
|
if nreps <= 1: # just move to next layer
|
2059
2164
|
iStart += 1; continue # nothing to do
|
2060
2165
|
|
2061
2166
|
#Construct a sub-circuit label that repeats layer[iStart] nreps times
|
2062
2167
|
# and stick it at layer iStart
|
2063
|
-
#print("Constructing %d reps at %d" % (nreps, iStart))
|
2064
2168
|
repCircuit = _CircuitLabel('', self._labels[iStart], None, nreps)
|
2065
2169
|
self.clear_labels(iStart, None) # remove existing labels (unnecessary?)
|
2066
2170
|
self.set_labels(repCircuit, iStart, None)
|
@@ -2068,7 +2172,6 @@ class Circuit(object):
|
|
2068
2172
|
iStart += nreps # advance iStart to next unprocessed layer inde
|
2069
2173
|
|
2070
2174
|
if len(iLayersToRemove) > 0:
|
2071
|
-
#print("Removing layers: ",iLayersToRemove)
|
2072
2175
|
self.delete_layers(iLayersToRemove)
|
2073
2176
|
|
2074
2177
|
def insert_layer(self, circuit_layer, j):
|
@@ -2121,7 +2224,7 @@ class Circuit(object):
|
|
2121
2224
|
None
|
2122
2225
|
"""
|
2123
2226
|
assert(not self._static), "Cannot edit a read-only circuit!"
|
2124
|
-
if self.
|
2227
|
+
if self._line_labels is None or self._line_labels == ():
|
2125
2228
|
#Allow insertion of a layer into an empty circuit to update the circuit's line_labels
|
2126
2229
|
layer_lbl = to_label(circuit_layer)
|
2127
2230
|
self.line_labels = layer_lbl.sslbls if (layer_lbl.sslbls is not None) else ('*',)
|
@@ -2181,8 +2284,8 @@ class Circuit(object):
|
|
2181
2284
|
"""
|
2182
2285
|
assert(not self._static), "Cannot edit a read-only circuit!"
|
2183
2286
|
lines_to_insert = []
|
2184
|
-
for line_lbl in circuit.
|
2185
|
-
if line_lbl in self.
|
2287
|
+
for line_lbl in circuit._line_labels:
|
2288
|
+
if line_lbl in self._line_labels:
|
2186
2289
|
lines_to_insert.append(line_lbl)
|
2187
2290
|
else:
|
2188
2291
|
assert(circuit._is_line_idling(line_lbl)), \
|
@@ -2225,6 +2328,7 @@ class Circuit(object):
|
|
2225
2328
|
-------
|
2226
2329
|
None
|
2227
2330
|
"""
|
2331
|
+
assert(not self._static), "Cannot edit a read-only circuit!"
|
2228
2332
|
self.insert_circuit_inplace(circuit, self.num_layers)
|
2229
2333
|
|
2230
2334
|
def prefix_circuit(self, circuit):
|
@@ -2261,6 +2365,7 @@ class Circuit(object):
|
|
2261
2365
|
-------
|
2262
2366
|
None
|
2263
2367
|
"""
|
2368
|
+
assert(not self._static), "Cannot edit a read-only circuit!"
|
2264
2369
|
self.insert_circuit_inplace(circuit, 0)
|
2265
2370
|
|
2266
2371
|
def tensor_circuit_inplace(self, circuit, line_order=None):
|
@@ -2290,12 +2395,12 @@ class Circuit(object):
|
|
2290
2395
|
#assert(self.identity == circuit.identity), "The identity labels must be the same!"
|
2291
2396
|
|
2292
2397
|
#Construct new line labels (of final circuit)
|
2293
|
-
overlap = set(self.
|
2398
|
+
overlap = set(self._line_labels).intersection(circuit._line_labels)
|
2294
2399
|
if len(overlap) > 0:
|
2295
2400
|
raise ValueError(
|
2296
2401
|
"The line labels of `circuit` and this Circuit must be distinct, but overlap = %s!" % str(overlap))
|
2297
2402
|
|
2298
|
-
all_line_labels = set(self.
|
2403
|
+
all_line_labels = set(self._line_labels + circuit._line_labels)
|
2299
2404
|
if line_order is not None:
|
2300
2405
|
line_order_set = set(line_order)
|
2301
2406
|
if len(line_order_set) != len(line_order):
|
@@ -2311,7 +2416,7 @@ class Circuit(object):
|
|
2311
2416
|
|
2312
2417
|
new_line_labels = line_order
|
2313
2418
|
else:
|
2314
|
-
new_line_labels = self.
|
2419
|
+
new_line_labels = self._line_labels + circuit._line_labels
|
2315
2420
|
|
2316
2421
|
#Add circuit's labels into this circuit
|
2317
2422
|
self.insert_labels_as_lines_inplace(circuit._labels, line_labels=circuit.line_labels)
|
@@ -2361,6 +2466,7 @@ class Circuit(object):
|
|
2361
2466
|
-------
|
2362
2467
|
None
|
2363
2468
|
"""
|
2469
|
+
assert(not self._static), "Cannot edit a read-only circuit!"
|
2364
2470
|
del self[j]
|
2365
2471
|
self.insert_labels_into_layers_inplace(circuit, j)
|
2366
2472
|
|
@@ -2449,7 +2555,7 @@ class Circuit(object):
|
|
2449
2555
|
return cpy
|
2450
2556
|
else: # static case: so self._labels is a tuple of Labels
|
2451
2557
|
return Circuit([lbl.replace_name(old_gatename, new_gatename)
|
2452
|
-
for lbl in self._labels], self.
|
2558
|
+
for lbl in self._labels], self._line_labels, occurrence=self._occurrence_id)
|
2453
2559
|
|
2454
2560
|
def replace_gatename_with_idle_inplace(self, gatename):
|
2455
2561
|
"""
|
@@ -2525,12 +2631,14 @@ class Circuit(object):
|
|
2525
2631
|
#Could to this in both cases, but is slow for large static circuits
|
2526
2632
|
cpy = self.copy(editable=False) # convert our layers to Labels
|
2527
2633
|
return Circuit._fastinit(tuple([new_layer if lbl == old_layer else lbl
|
2528
|
-
for lbl in cpy._labels]), self.
|
2529
|
-
occurrence=self.
|
2634
|
+
for lbl in cpy._labels]), self._line_labels, editable=False,
|
2635
|
+
occurrence=self._occurrence_id,
|
2636
|
+
compilable_layer_indices_tup=self._compilable_layer_indices_tup)
|
2530
2637
|
else: # static case: so self._labels is a tuple of Labels
|
2531
2638
|
return Circuit(tuple([new_layer if lbl == old_layer else lbl
|
2532
|
-
for lbl in self._labels]), self.
|
2533
|
-
occurrence=self.
|
2639
|
+
for lbl in self._labels]), self._line_labels, editable=False,
|
2640
|
+
occurrence=self._occurrence_id,
|
2641
|
+
compilable_layer_indices=self._compilable_layer_indices_tup)
|
2534
2642
|
|
2535
2643
|
def replace_layers_with_aliases(self, alias_dict):
|
2536
2644
|
"""
|
@@ -2557,34 +2665,7 @@ class Circuit(object):
|
|
2557
2665
|
while label in layers:
|
2558
2666
|
i = layers.index(label)
|
2559
2667
|
layers = layers[:i] + c._labels + layers[i + 1:]
|
2560
|
-
return Circuit._fastinit(layers, self.
|
2561
|
-
|
2562
|
-
#def replace_identity(self, identity, convert_identity_gates = True): # THIS module only
|
2563
|
-
# """
|
2564
|
-
# Changes the *name* of the idle/identity gate in the circuit. This replaces
|
2565
|
-
# the name of the identity element in the circuit by setting self.identity = identity.
|
2566
|
-
# If `convert_identity_gates` is True, this also changes the names of all the gates that
|
2567
|
-
# had the old self.identity name.
|
2568
|
-
#
|
2569
|
-
# Parameters
|
2570
|
-
# ----------
|
2571
|
-
# identity : string
|
2572
|
-
# The new name for the identity gate.
|
2573
|
-
#
|
2574
|
-
# convert_identity_gates : bool, optional
|
2575
|
-
# If True, all gates that had the old identity name are converted to the new identity
|
2576
|
-
# name. Otherwise, they keep the old name, and the circuit nolonger considers them to
|
2577
|
-
# be identity gates.
|
2578
|
-
#
|
2579
|
-
# Returns
|
2580
|
-
# -------
|
2581
|
-
# None
|
2582
|
-
# """
|
2583
|
-
# if convert_identity_gates:
|
2584
|
-
# self.replace_gatename(self.identity, identity)
|
2585
|
-
#
|
2586
|
-
# self._tup_dirty = self._str_dirty = True
|
2587
|
-
# self.identity = identity
|
2668
|
+
return Circuit._fastinit(layers, self._line_labels, editable=False, occurrence=self._occurrence_id)
|
2588
2669
|
|
2589
2670
|
def change_gate_library(self, compilation, allowed_filter=None, allow_unchanged_gates=False, depth_compression=True,
|
2590
2671
|
one_q_gate_relations=None):
|
@@ -2715,7 +2796,7 @@ class Circuit(object):
|
|
2715
2796
|
|
2716
2797
|
def map_names(obj): # obj is either a simple label or a list
|
2717
2798
|
if isinstance(obj, _Label):
|
2718
|
-
if obj.
|
2799
|
+
if obj.IS_SIMPLE: # *simple* label
|
2719
2800
|
new_name = mapper_func(obj.name)
|
2720
2801
|
newobj = _Label(new_name, obj.sslbls) \
|
2721
2802
|
if (new_name is not None) else obj
|
@@ -2747,7 +2828,7 @@ class Circuit(object):
|
|
2747
2828
|
def mapper_func(line_label): return mapper[line_label] \
|
2748
2829
|
if isinstance(mapper, dict) else mapper
|
2749
2830
|
|
2750
|
-
self._line_labels = tuple((mapper_func(l) for l in self.
|
2831
|
+
self._line_labels = tuple((mapper_func(l) for l in self._line_labels))
|
2751
2832
|
|
2752
2833
|
def map_sslbls(obj): # obj is either a simple label or a list
|
2753
2834
|
if isinstance(obj, _Label):
|
@@ -2776,9 +2857,9 @@ class Circuit(object):
|
|
2776
2857
|
"""
|
2777
2858
|
def mapper_func(line_label): return mapper[line_label] \
|
2778
2859
|
if isinstance(mapper, dict) else mapper(line_label)
|
2779
|
-
mapped_line_labels = tuple(map(mapper_func, self.
|
2860
|
+
mapped_line_labels = tuple(map(mapper_func, self._line_labels))
|
2780
2861
|
return Circuit([l.map_state_space_labels(mapper_func) for l in self.layertup],
|
2781
|
-
mapped_line_labels, None, not self._static, occurrence=self.
|
2862
|
+
mapped_line_labels, None, not self._static, occurrence=self._occurrence_id)
|
2782
2863
|
|
2783
2864
|
def reorder_lines_inplace(self, order):
|
2784
2865
|
"""
|
@@ -2797,7 +2878,7 @@ class Circuit(object):
|
|
2797
2878
|
None
|
2798
2879
|
"""
|
2799
2880
|
assert(not self._static), "Cannot edit a read-only circuit!"
|
2800
|
-
assert(set(order) == set(self.
|
2881
|
+
assert(set(order) == set(self._line_labels)), "The line labels must be the same!"
|
2801
2882
|
self._line_labels = tuple(order)
|
2802
2883
|
|
2803
2884
|
def reorder_lines(self, order):
|
@@ -2842,10 +2923,9 @@ class Circuit(object):
|
|
2842
2923
|
True if the line is idling. False otherwise.
|
2843
2924
|
"""
|
2844
2925
|
if self._static:
|
2845
|
-
layers =
|
2846
|
-
if idle_layer_labels else self._labels
|
2926
|
+
layers = [x for x in self._labels if x not in idle_layer_labels] if idle_layer_labels else self._labels
|
2847
2927
|
all_sslbls = None if any([layer.sslbls is None for layer in layers]) \
|
2848
|
-
else set(
|
2928
|
+
else set([sslbl for layer in layers for sslbl in layer.sslbls])
|
2849
2929
|
else:
|
2850
2930
|
all_sslbls = _sslbls_of_nested_lists_of_simple_labels(self._labels, idle_layer_labels) # None or a set
|
2851
2931
|
|
@@ -2870,17 +2950,16 @@ class Circuit(object):
|
|
2870
2950
|
tuple
|
2871
2951
|
"""
|
2872
2952
|
if self._static:
|
2873
|
-
layers =
|
2874
|
-
if idle_layer_labels else self._labels
|
2953
|
+
layers = [x for x in self._labels if x not in idle_layer_labels] if idle_layer_labels else self._labels
|
2875
2954
|
all_sslbls = None if any([layer.sslbls is None for layer in layers]) \
|
2876
|
-
else set(
|
2955
|
+
else set([sslbl for layer in layers for sslbl in layer.sslbls])
|
2877
2956
|
else:
|
2878
2957
|
all_sslbls = _sslbls_of_nested_lists_of_simple_labels(self._labels, idle_layer_labels) # None or a set
|
2879
2958
|
|
2880
2959
|
if all_sslbls is None:
|
2881
2960
|
return ()
|
2882
2961
|
else:
|
2883
|
-
return tuple([x for x in self.
|
2962
|
+
return tuple([x for x in self._line_labels
|
2884
2963
|
if x not in all_sslbls]) # preserve order
|
2885
2964
|
|
2886
2965
|
def delete_idling_lines_inplace(self, idle_layer_labels=None):
|
@@ -2899,17 +2978,15 @@ class Circuit(object):
|
|
2899
2978
|
-------
|
2900
2979
|
None
|
2901
2980
|
"""
|
2902
|
-
|
2903
|
-
# Actually, this is OK even for static circuits because it won't affect the hashed value (labels only)
|
2981
|
+
assert(not self._static),"Cannot edit a read-only circuit!"
|
2904
2982
|
|
2905
2983
|
if idle_layer_labels:
|
2906
2984
|
assert(all([to_label(x).sslbls is None for x in idle_layer_labels])), "Idle layer labels must be *global*"
|
2907
2985
|
|
2908
2986
|
if self._static:
|
2909
|
-
layers =
|
2910
|
-
if idle_layer_labels else self._labels
|
2987
|
+
layers = [x for x in self._labels if x not in idle_layer_labels] if idle_layer_labels else self._labels
|
2911
2988
|
all_sslbls = None if any([layer.sslbls is None for layer in layers]) \
|
2912
|
-
else set(
|
2989
|
+
else set([sslbl for layer in layers for sslbl in layer.sslbls])
|
2913
2990
|
else:
|
2914
2991
|
all_sslbls = _sslbls_of_nested_lists_of_simple_labels(self._labels, idle_layer_labels) # None or a set
|
2915
2992
|
|
@@ -2918,7 +2995,7 @@ class Circuit(object):
|
|
2918
2995
|
|
2919
2996
|
#All we need to do is update line_labels since there aren't any labels
|
2920
2997
|
# to remove in self._labels (as all the lines are idling)
|
2921
|
-
self._line_labels = tuple([x for x in self.
|
2998
|
+
self._line_labels = tuple([x for x in self._line_labels
|
2922
2999
|
if x in all_sslbls]) # preserve order
|
2923
3000
|
|
2924
3001
|
def delete_idling_lines(self, idle_layer_labels=None):
|
@@ -2964,6 +3041,7 @@ class Circuit(object):
|
|
2964
3041
|
-------
|
2965
3042
|
None
|
2966
3043
|
"""
|
3044
|
+
assert(not self._static), "Cannot edit a read-only circuit!"
|
2967
3045
|
self.clear_labels(lines=line_label, clear_straddlers=clear_straddlers)
|
2968
3046
|
|
2969
3047
|
def reverse_inplace(self):
|
@@ -2978,10 +3056,10 @@ class Circuit(object):
|
|
2978
3056
|
self._labels = list(reversed(self._labels)) # reverses the layer order
|
2979
3057
|
#FUTURE: would need to reverse_inplace each layer too, if layer can have *sublayers*
|
2980
3058
|
|
2981
|
-
if
|
3059
|
+
if self._compilable_layer_indices_tup:
|
2982
3060
|
depth = len(self._labels)
|
2983
|
-
self._compilable_layer_indices_tup =
|
2984
|
-
|
3061
|
+
self._compilable_layer_indices_tup = \
|
3062
|
+
tuple([(depth - 1 - i) for i in self._compilable_layer_indices_tup])
|
2985
3063
|
|
2986
3064
|
def _combine_one_q_gates_inplace(self, one_q_gate_relations):
|
2987
3065
|
"""
|
@@ -3027,7 +3105,6 @@ class Circuit(object):
|
|
3027
3105
|
productive = True
|
3028
3106
|
|
3029
3107
|
while productive: # keep iterating
|
3030
|
-
#print("BEGIN ITER")
|
3031
3108
|
productive = False
|
3032
3109
|
# Loop through all the qubits, to try and compress squences of 1-qubit gates on the qubit in question.
|
3033
3110
|
for ilayer in range(0, len(self._labels) - 1):
|
@@ -3042,7 +3119,6 @@ class Circuit(object):
|
|
3042
3119
|
for b, lblB in enumerate(layerB_comps):
|
3043
3120
|
if isinstance(lblB, _Label) and lblB.sslbls == lblA.sslbls:
|
3044
3121
|
#queue an apply rule if one exists
|
3045
|
-
#print("CHECK for: ", (lblA.name,lblB.name))
|
3046
3122
|
if (lblA.name, lblB.name) in one_q_gate_relations:
|
3047
3123
|
new_Aname = one_q_gate_relations[lblA.name, lblB.name]
|
3048
3124
|
applies.append((a, b, new_Aname, lblA.sslbls))
|
@@ -3095,17 +3171,14 @@ class Circuit(object):
|
|
3095
3171
|
# Keeps track of whether any changes have been made to the circuit.
|
3096
3172
|
compression_implemented = False
|
3097
3173
|
|
3098
|
-
#print("BEGIN")
|
3099
3174
|
used_lines = {}
|
3100
3175
|
for icurlayer in range(len(self._labels)):
|
3101
|
-
#print("LAYER ",icurlayer)
|
3102
3176
|
#Slide labels in current layer to left ("forward")
|
3103
3177
|
icomps_to_remove = []; used_lines[icurlayer] = set()
|
3104
3178
|
for icomp, lbl in enumerate(self._layer_components(icurlayer)):
|
3105
3179
|
#see if we can move this label forward
|
3106
|
-
#print("COMP%d: %s" % (icomp,str(lbl)))
|
3107
3180
|
sslbls = _sslbls_of_nested_lists_of_simple_labels(lbl)
|
3108
|
-
if sslbls is None: sslbls = self.
|
3181
|
+
if sslbls is None: sslbls = self._line_labels
|
3109
3182
|
|
3110
3183
|
dest_layer = icurlayer
|
3111
3184
|
while dest_layer > 0 and len(used_lines[dest_layer - 1].intersection(sslbls)) == 0:
|
@@ -3114,14 +3187,11 @@ class Circuit(object):
|
|
3114
3187
|
icomps_to_remove.append(icomp) # remove this label from current layer
|
3115
3188
|
self._append_layer_component(dest_layer, lbl) # add it to the destination layer
|
3116
3189
|
used_lines[dest_layer].update(sslbls) # update used_lines at dest layer
|
3117
|
-
#print(" <-- layer %d (used=%s)" % (dest_layer,str(used_lines[dest_layer])))
|
3118
3190
|
else:
|
3119
3191
|
#can't move this label forward - update used_lines of current layer
|
3120
3192
|
used_lines[icurlayer].update(sslbls) # update used_lines at dest layer
|
3121
|
-
|
3122
|
-
|
3193
|
+
|
3123
3194
|
#Remove components in current layer which were pushed forward
|
3124
|
-
#print("Removing ",icomps_to_remove," from layer ",icurlayer)
|
3125
3195
|
for icomp in reversed(icomps_to_remove):
|
3126
3196
|
self._remove_layer_component(icurlayer, icomp)
|
3127
3197
|
|
@@ -3291,11 +3361,11 @@ class Circuit(object):
|
|
3291
3361
|
layer_lbl = self.layer_label(j) # (a Label)
|
3292
3362
|
if layer_lbl.sslbls is None:
|
3293
3363
|
if layer_lbl == (): # special case - the completely empty layer: sslbls=None but needs padding
|
3294
|
-
return _Label([_Label(idle_gate_name, line_lbl) for line_lbl in self.
|
3364
|
+
return _Label([_Label(idle_gate_name, line_lbl) for line_lbl in self._line_labels])
|
3295
3365
|
return layer_lbl # all qubits used - no idles to pad
|
3296
3366
|
|
3297
3367
|
components = list(layer_lbl.components)
|
3298
|
-
for line_lbl in self.
|
3368
|
+
for line_lbl in self._line_labels:
|
3299
3369
|
if line_lbl not in layer_lbl.sslbls:
|
3300
3370
|
components.append(_Label(idle_gate_name, line_lbl))
|
3301
3371
|
return _Label(components)
|
@@ -3346,7 +3416,7 @@ class Circuit(object):
|
|
3346
3416
|
-------
|
3347
3417
|
int
|
3348
3418
|
"""
|
3349
|
-
return len(self.
|
3419
|
+
return len(self._line_labels)
|
3350
3420
|
|
3351
3421
|
@property
|
3352
3422
|
def size(self):
|
@@ -3365,14 +3435,14 @@ class Circuit(object):
|
|
3365
3435
|
#TODO HERE -update from here down b/c of sub-circuit blocks
|
3366
3436
|
if self._static:
|
3367
3437
|
def size(lbl): # obj a Label, perhaps compound
|
3368
|
-
if lbl.
|
3369
|
-
return len(lbl.sslbls) if (lbl.sslbls is not None) else len(self.
|
3438
|
+
if lbl.IS_SIMPLE: # a simple label
|
3439
|
+
return len(lbl.sslbls) if (lbl.sslbls is not None) else len(self._line_labels)
|
3370
3440
|
else:
|
3371
3441
|
return sum([size(sublbl) for sublbl in lbl.components])
|
3372
3442
|
else:
|
3373
3443
|
def size(obj): # obj is either a simple label or a list
|
3374
|
-
if isinstance(obj, _Label): # all Labels are simple labels
|
3375
|
-
return len(obj.sslbls) if (obj.sslbls is not None) else len(self.
|
3444
|
+
if isinstance(obj, _Label): # all Labels in editable format are simple labels
|
3445
|
+
return len(obj.sslbls) if (obj.sslbls is not None) else len(self._line_labels)
|
3376
3446
|
else:
|
3377
3447
|
return sum([size(sub) for sub in obj])
|
3378
3448
|
|
@@ -3400,6 +3470,30 @@ class Circuit(object):
|
|
3400
3470
|
"""
|
3401
3471
|
return self.num_nq_gates(2)
|
3402
3472
|
|
3473
|
+
@property
|
3474
|
+
def num_gates(self):
|
3475
|
+
"""
|
3476
|
+
The number of gates in the circuit.
|
3477
|
+
|
3478
|
+
Returns
|
3479
|
+
-------
|
3480
|
+
int
|
3481
|
+
"""
|
3482
|
+
if self._static:
|
3483
|
+
def cnt(lbl): # obj a Label, perhaps compound
|
3484
|
+
if lbl.IS_SIMPLE: # a simple label
|
3485
|
+
return 1 if (lbl.sslbls is not None) else 0
|
3486
|
+
else:
|
3487
|
+
return sum([cnt(sublbl) for sublbl in lbl.components])
|
3488
|
+
else:
|
3489
|
+
def cnt(obj): # obj is either a simple label or a list
|
3490
|
+
if isinstance(obj, _Label): # all Labels are simple labels
|
3491
|
+
return 1 if (obj.sslbls is not None) else 0
|
3492
|
+
else:
|
3493
|
+
return sum([cnt(sub) for sub in obj])
|
3494
|
+
|
3495
|
+
return sum([cnt(layer_lbl) for layer_lbl in self._labels])
|
3496
|
+
|
3403
3497
|
def num_nq_gates(self, nq):
|
3404
3498
|
"""
|
3405
3499
|
The number of `nq`-qubit gates in the circuit.
|
@@ -3420,7 +3514,7 @@ class Circuit(object):
|
|
3420
3514
|
"""
|
3421
3515
|
if self._static:
|
3422
3516
|
def cnt(lbl): # obj a Label, perhaps compound
|
3423
|
-
if lbl.
|
3517
|
+
if lbl.IS_SIMPLE: # a simple label
|
3424
3518
|
return 1 if (lbl.sslbls is not None) and (len(lbl.sslbls) == nq) else 0
|
3425
3519
|
else:
|
3426
3520
|
return sum([cnt(sublbl) for sublbl in lbl.components])
|
@@ -3448,7 +3542,7 @@ class Circuit(object):
|
|
3448
3542
|
"""
|
3449
3543
|
if self._static:
|
3450
3544
|
def cnt(lbl): # obj a Label, perhaps compound
|
3451
|
-
if lbl.
|
3545
|
+
if lbl.IS_SIMPLE: # a simple label
|
3452
3546
|
return 1 if (lbl.sslbls is not None) and (len(lbl.sslbls) >= 2) else 0
|
3453
3547
|
else:
|
3454
3548
|
return sum([cnt(sublbl) for sublbl in lbl.components])
|
@@ -3460,52 +3554,17 @@ class Circuit(object):
|
|
3460
3554
|
return sum([cnt(sub) for sub in obj])
|
3461
3555
|
|
3462
3556
|
return sum([cnt(layer_lbl) for layer_lbl in self._labels])
|
3463
|
-
|
3464
|
-
# UNUSED
|
3465
|
-
#def predicted_error_probability(self, gate_error_probabilities):
|
3466
|
-
# """
|
3467
|
-
# Predicts the probability that one or more errors occur in the circuit
|
3468
|
-
# if the gates have the error probabilities specified by in the input
|
3469
|
-
# dictionary. Given correct error rates for the gates and stochastic errors,
|
3470
|
-
# this is predictive of the probability of an error in the circuit. But note
|
3471
|
-
# that that is generally *not* the same as the probability that the circuit
|
3472
|
-
# implemented is incorrect (e.g., stochastic errors can cancel).
|
3473
|
-
#
|
3474
|
-
# Parameters
|
3475
|
-
# ----------
|
3476
|
-
# gate_error_probabilities : dict
|
3477
|
-
# A dictionary where the keys are the labels that appear in the circuit, and
|
3478
|
-
# the value is the error probability for that gate.
|
3479
|
-
#
|
3480
|
-
# Returns
|
3481
|
-
# -------
|
3482
|
-
# float
|
3483
|
-
# The probability that there is one or more errors in the circuit.
|
3484
|
-
# """
|
3485
|
-
# f = 1.
|
3486
|
-
# depth = self.num_layers
|
3487
|
-
# for i in range(0,self.num_lines):
|
3488
|
-
# for j in range(0,depth):
|
3489
|
-
# gatelbl = self.line_items[i][j]
|
3490
|
-
#
|
3491
|
-
# # So that we don't include multi-qubit gates more than once.
|
3492
|
-
# if gatelbl.qubits is None:
|
3493
|
-
# if i == 0:
|
3494
|
-
# f = f*(1-gate_error_probabilities[gatelbl])
|
3495
|
-
# elif gatelbl.qubits[0] == self.line_labels[i]:
|
3496
|
-
# f = f*(1-gate_error_probabilities[gatelbl])
|
3497
|
-
# return 1 - f
|
3498
|
-
|
3557
|
+
|
3499
3558
|
def _togrid(self, identity_name):
|
3500
3559
|
""" return a list-of-lists rep? """
|
3501
3560
|
d = self.num_layers
|
3502
|
-
line_items = [[_Label(identity_name, ll)] * d for ll in self.
|
3561
|
+
line_items = [[_Label(identity_name, ll)] * d for ll in self._line_labels]
|
3503
3562
|
|
3504
3563
|
for ilayer in range(len(self._labels)):
|
3505
3564
|
for layercomp in self._layer_components(ilayer):
|
3506
3565
|
if isinstance(layercomp, _Label):
|
3507
3566
|
comp_label = layercomp
|
3508
|
-
if layercomp.
|
3567
|
+
if layercomp.IS_SIMPLE:
|
3509
3568
|
comp_sslbls = layercomp.sslbls
|
3510
3569
|
else:
|
3511
3570
|
#We can't intelligently flatten compound labels that occur within a layer-label yet...
|
@@ -3513,9 +3572,9 @@ class Circuit(object):
|
|
3513
3572
|
else: # layercomp must be a list (and _static == False)
|
3514
3573
|
comp_label = _Label(layercomp)
|
3515
3574
|
comp_sslbls = _sslbls_of_nested_lists_of_simple_labels(layercomp)
|
3516
|
-
if comp_sslbls is None: comp_sslbls = self.
|
3575
|
+
if comp_sslbls is None: comp_sslbls = self._line_labels
|
3517
3576
|
for sslbl in comp_sslbls:
|
3518
|
-
lineIndx = self.
|
3577
|
+
lineIndx = self._line_labels.index(sslbl) # replace w/dict for speed...
|
3519
3578
|
line_items[lineIndx][ilayer] = comp_label
|
3520
3579
|
return line_items
|
3521
3580
|
|
@@ -3534,7 +3593,7 @@ class Circuit(object):
|
|
3534
3593
|
|
3535
3594
|
def abbrev(lbl, k): # assumes a simple label w/ name & qubits
|
3536
3595
|
""" Returns what to print on line 'k' for label 'lbl' """
|
3537
|
-
lbl_qubits = lbl.qubits if (lbl.qubits is not None) else self.
|
3596
|
+
lbl_qubits = lbl.qubits if (lbl.qubits is not None) else self._line_labels
|
3538
3597
|
nqubits = len(lbl_qubits)
|
3539
3598
|
if nqubits == 1 and lbl.name is not None:
|
3540
3599
|
if isinstance(lbl, _CircuitLabel): # HACK
|
@@ -3544,12 +3603,12 @@ class Circuit(object):
|
|
3544
3603
|
else:
|
3545
3604
|
return lbl.name
|
3546
3605
|
elif lbl.name in ('CNOT', 'Gcnot') and nqubits == 2: # qubit indices = (control,target)
|
3547
|
-
if k == self.
|
3606
|
+
if k == self._line_labels.index(lbl_qubits[0]):
|
3548
3607
|
return Ctxt + str(lbl_qubits[1])
|
3549
3608
|
else:
|
3550
3609
|
return Ttxt + str(lbl_qubits[0])
|
3551
3610
|
elif lbl.name in ('CPHASE', 'Gcphase') and nqubits == 2:
|
3552
|
-
if k == self.
|
3611
|
+
if k == self._line_labels.index(lbl_qubits[0]):
|
3553
3612
|
otherqubit = lbl_qubits[1]
|
3554
3613
|
else:
|
3555
3614
|
otherqubit = lbl_qubits[0]
|
@@ -3564,11 +3623,11 @@ class Circuit(object):
|
|
3564
3623
|
for i in range(0, self.num_lines)])
|
3565
3624
|
for j in range(0, self.num_layers)]
|
3566
3625
|
|
3567
|
-
max_linelabellen = max([len(str(llabel)) for llabel in self.
|
3626
|
+
max_linelabellen = max([len(str(llabel)) for llabel in self._line_labels])
|
3568
3627
|
|
3569
3628
|
for i in range(self.num_lines):
|
3570
|
-
s += 'Qubit {} '.format(self.
|
3571
|
-
(max_linelabellen - len(str(self.
|
3629
|
+
s += 'Qubit {} '.format(self._line_labels[i]) + ' ' * \
|
3630
|
+
(max_linelabellen - len(str(self._line_labels[i]))) + '---'
|
3572
3631
|
for j, maxlbllen in enumerate(max_labellen):
|
3573
3632
|
if line_items[i][j].name == identityName:
|
3574
3633
|
# Replace with special idle print at some point
|
@@ -3677,7 +3736,7 @@ class Circuit(object):
|
|
3677
3736
|
# The quantum wire for qubit q
|
3678
3737
|
circuit_for_q = self.line_items[q]
|
3679
3738
|
for gate in circuit_for_q:
|
3680
|
-
gate_qubits = gate.qubits if (gate.qubits is not None) else self.
|
3739
|
+
gate_qubits = gate.qubits if (gate.qubits is not None) else self._line_labels
|
3681
3740
|
nqubits = len(gate_qubits)
|
3682
3741
|
if gate.name == self.identity:
|
3683
3742
|
qstring += r' \qw &'
|
@@ -3744,6 +3803,12 @@ class Circuit(object):
|
|
3744
3803
|
gatename_conversion = _itgs.standard_gatenames_cirq_conversions()
|
3745
3804
|
if wait_duration is not None:
|
3746
3805
|
gatename_conversion[idle_gate_name] = cirq.WaitGate(wait_duration)
|
3806
|
+
#conversion does not work is the line labels are none, or the line labels are not a subset
|
3807
|
+
#of the keys for qubit_conversion (indicating there isn't a corresponding mapping into cirq objects).
|
3808
|
+
msg1 = 'Conversion to cirq does not work with circuits w/placeholder * line label.'
|
3809
|
+
msg2 = 'Missing qubit conversions, some line labels have no corresponding cirq conversion in qubit_conversions.'
|
3810
|
+
assert self._line_labels != ('*',), msg1
|
3811
|
+
assert set(self._line_labels).issubset(set(qubit_conversion.keys())), msg2
|
3747
3812
|
|
3748
3813
|
moments = []
|
3749
3814
|
for i in range(self.num_layers):
|
@@ -3751,15 +3816,195 @@ class Circuit(object):
|
|
3751
3816
|
operations = []
|
3752
3817
|
for gate in layer:
|
3753
3818
|
operation = gatename_conversion[gate.name]
|
3754
|
-
if operation is None:
|
3755
|
-
# This happens if no idle gate it specified because
|
3756
|
-
# standard_gatenames_cirq_conversions maps 'Gi' to `None`
|
3757
|
-
continue
|
3758
3819
|
qubits = map(qubit_conversion.get, gate.qubits)
|
3759
3820
|
operations.append(operation.on(*qubits))
|
3760
3821
|
moments.append(cirq.Moment(operations))
|
3761
3822
|
|
3762
3823
|
return cirq.Circuit(moments)
|
3824
|
+
|
3825
|
+
@classmethod
|
3826
|
+
def from_cirq(cls, circuit, qubit_conversion=None, cirq_gate_conversion= None,
|
3827
|
+
remove_implied_idles = True, global_idle_replacement_label = 'auto'):
|
3828
|
+
"""
|
3829
|
+
Converts and instantiates a pyGSTi Circuit object from a Cirq Circuit object.
|
3830
|
+
|
3831
|
+
Parameters
|
3832
|
+
----------
|
3833
|
+
circuit : cirq Circuit
|
3834
|
+
The cirq Circuit object to parse into a pyGSTi circuit.
|
3835
|
+
|
3836
|
+
qubit_conversion : dict, optional (default None)
|
3837
|
+
A dictionary specifying a mapping between cirq qubit objects and
|
3838
|
+
pyGSTi qubit labels (either integers or strings).
|
3839
|
+
If None, then a default mapping is created.
|
3840
|
+
|
3841
|
+
cirq_gate_conversion : dict, optional (default None)
|
3842
|
+
If specified a dictionary with keys given by cirq gate objects,
|
3843
|
+
and values given by pygsti gate names which overrides the built-in
|
3844
|
+
conversion dictionary used by default.
|
3845
|
+
|
3846
|
+
remove_implied_idles : bool, optional (default True)
|
3847
|
+
A flag indicating whether to remove explicit idles
|
3848
|
+
that are part of a circuit layer containing
|
3849
|
+
other explicitly specified gates
|
3850
|
+
(i.e., whether to abide by the normal pyGSTi implicit idle convention).
|
3851
|
+
|
3852
|
+
global_idle_replacement_label : string or Label or None, optional (default 'auto')
|
3853
|
+
An option specified for the handling of global idle layers.
|
3854
|
+
If None, no replacement of global idle layers is performed and a verbatim
|
3855
|
+
conversion from the cirq layer is performed.
|
3856
|
+
If the string 'auto', then the behavior is to replace global idle layers with
|
3857
|
+
the gate label Label(()), which is the special syntax for the global
|
3858
|
+
idle layer, stylized typically as '[]'. If another string then replace with a
|
3859
|
+
gate label with the specified name acting on all of the qubits
|
3860
|
+
appearing in the cirq circuit. If a Label object, use this directly,
|
3861
|
+
this does not check for compatibility so it is up to the user to ensure
|
3862
|
+
the labels are compatible.
|
3863
|
+
|
3864
|
+
Returns
|
3865
|
+
-------
|
3866
|
+
pygsti_circuit
|
3867
|
+
A pyGSTi Circuit instance equivalent to the specified Cirq one.
|
3868
|
+
"""
|
3869
|
+
|
3870
|
+
try:
|
3871
|
+
import cirq
|
3872
|
+
except ImportError:
|
3873
|
+
raise ImportError("Cirq is required for this operation, and it does not appear to be installed.")
|
3874
|
+
|
3875
|
+
#mapping between cirq gates and pygsti gate names:
|
3876
|
+
if cirq_gate_conversion is not None:
|
3877
|
+
cirq_to_gate_name_mapping = cirq_gate_conversion
|
3878
|
+
else:
|
3879
|
+
cirq_to_gate_name_mapping = _itgs.cirq_gatenames_standard_conversions()
|
3880
|
+
|
3881
|
+
#get all of the qubits in the cirq Circuit
|
3882
|
+
all_cirq_qubits = circuit.all_qubits()
|
3883
|
+
|
3884
|
+
#ensure all of these have a conversion available.
|
3885
|
+
if qubit_conversion is not None:
|
3886
|
+
assert set(all_cirq_qubits).issubset(set(qubit_conversion.keys())), 'Missing cirq to pygsti conversions for some qubit label(s).'
|
3887
|
+
#if it is None, build a default mapping.
|
3888
|
+
else:
|
3889
|
+
#default mapping is currently hardcoded for the conventions of either cirwq's
|
3890
|
+
#NamedQubit, LineQubit or GridQubit classes, other types will raise an error.
|
3891
|
+
qubit_conversion = {}
|
3892
|
+
for qubit in all_cirq_qubits:
|
3893
|
+
if isinstance(qubit, cirq.NamedQubit):
|
3894
|
+
qubit_conversion[qubit] = f'Q{qubit.name}'
|
3895
|
+
elif isinstance(qubit, cirq.LineQubit):
|
3896
|
+
qubit_conversion[qubit] = f'Q{qubit.x}'
|
3897
|
+
elif isinstance(qubit, cirq.GridQubit):
|
3898
|
+
qubit_conversion[qubit] = f'Q{qubit.row}_{qubit.col}'
|
3899
|
+
else:
|
3900
|
+
msg = 'Unsupported cirq qubit type. Currently only support for automatically creating'\
|
3901
|
+
+'a default cirq qubit to pygsti qubit label mapping for NamedQubit, LineQubit and GridQubit.'
|
3902
|
+
raise ValueError(msg)
|
3903
|
+
|
3904
|
+
#In cirq the equivalent concept to a layer in a pygsti circuit is a Moment.
|
3905
|
+
#Circuits consist of ordered lists of moments corresponding to a set of
|
3906
|
+
#operations applied at that abstract time slice.
|
3907
|
+
#cirq Circuits can be sliced and iterated over. Iterating returns each contained
|
3908
|
+
#Moment in sequence. Slicing returns a new circuit corresponding to the
|
3909
|
+
#selected layers.
|
3910
|
+
|
3911
|
+
#initialize empty list of pygsti circuit layers
|
3912
|
+
circuit_layers = []
|
3913
|
+
|
3914
|
+
#initialize a flag for indicating that we've seen a global idle to use later.
|
3915
|
+
seen_global_idle = False
|
3916
|
+
|
3917
|
+
#Iterate through each of the moments and build up layers Moment by Moment.
|
3918
|
+
for moment in circuit:
|
3919
|
+
#if the length of the tuple of operations for this moment in
|
3920
|
+
#moment.operations is length 1, then we'll add the operation to
|
3921
|
+
#the pygsti circuit as a bare gate label (i.e. not wrapped in a layer label
|
3922
|
+
#indicating parallel gates). Otherwise, we'll iterate through and add them
|
3923
|
+
#as a layer label.
|
3924
|
+
if len(moment.operations) == 1:
|
3925
|
+
op = moment.operations[0]
|
3926
|
+
try:
|
3927
|
+
name = cirq_to_gate_name_mapping[op.gate]
|
3928
|
+
except KeyError:
|
3929
|
+
msg = 'Could not find matching standard gate name in provided dictionary. Falling back to try and find a'\
|
3930
|
+
+' unitary from standard_gatename_unitaries which matches up to a global phase.'
|
3931
|
+
_warnings.warn(msg)
|
3932
|
+
name = _itgs.unitary_to_standard_gatename(op.gate._unitary_(), up_to_phase=True)
|
3933
|
+
assert name is not None, 'Could not find a matching standard gate name for conversion.'
|
3934
|
+
sslbls = tuple(qubit_conversion[qubit] for qubit in op.qubits)
|
3935
|
+
#global idle handling:
|
3936
|
+
if name == 'Gi' and global_idle_replacement_label:
|
3937
|
+
#set a flag indicating that we've seen a global idle to use later.
|
3938
|
+
seen_global_idle = True
|
3939
|
+
if isinstance(global_idle_replacement_label, str):
|
3940
|
+
if global_idle_replacement_label == 'auto':
|
3941
|
+
#append the default.
|
3942
|
+
circuit_layers.append(_Label(()))
|
3943
|
+
else:
|
3944
|
+
circuit_layers.append(_Label(global_idle_replacement_label,
|
3945
|
+
tuple(sorted([qubit_conversion[qubit] for qubit in all_cirq_qubits]))))
|
3946
|
+
elif isinstance(global_idle_replacement_label, _Label):
|
3947
|
+
circuit_layers.append(global_idle_replacement_label)
|
3948
|
+
else:
|
3949
|
+
circuit_layers.append(_Label(name, state_space_labels = sslbls))
|
3950
|
+
|
3951
|
+
else:
|
3952
|
+
#initialize sublist for layer label elements
|
3953
|
+
layer_label_elems = []
|
3954
|
+
#iterate through each of the operations in this moment
|
3955
|
+
for op in moment.operations:
|
3956
|
+
try:
|
3957
|
+
name = cirq_to_gate_name_mapping[op.gate]
|
3958
|
+
except KeyError:
|
3959
|
+
msg = 'Could not find matching standard gate name in provided dictionary. Falling back to try and find a'\
|
3960
|
+
+' unitary from standard_gatename_unitaries which matches up to a global phase.'
|
3961
|
+
_warnings.warn(msg)
|
3962
|
+
name = _itgs.unitary_to_standard_gatename(op.gate._unitary_(), up_to_phase=True)
|
3963
|
+
assert name is not None, 'Could not find a matching standard gate name for conversion.'
|
3964
|
+
sslbls = tuple(qubit_conversion[qubit] for qubit in op.qubits)
|
3965
|
+
layer_label_elems.append(_Label(name, state_space_labels = sslbls))
|
3966
|
+
|
3967
|
+
#add special handling for global idle circuits and implied idels based on flags.
|
3968
|
+
layer_label_elem_names = [elem.name for elem in layer_label_elems]
|
3969
|
+
all_idles = all([name == 'Gi' for name in layer_label_elem_names])
|
3970
|
+
|
3971
|
+
if global_idle_replacement_label and all_idles:
|
3972
|
+
#set a flag indicating that we've seen a global idle to use later.
|
3973
|
+
seen_global_idle = True
|
3974
|
+
#if global idle is a string, replace this layer with the user specified one:
|
3975
|
+
if isinstance(global_idle_replacement_label, str):
|
3976
|
+
if global_idle_replacement_label == 'auto':
|
3977
|
+
#append the default.
|
3978
|
+
circuit_layers.append(_Label(()))
|
3979
|
+
else:
|
3980
|
+
circuit_layers.append(_Label(global_idle_replacement_label,
|
3981
|
+
tuple(sorted([qubit_conversion[qubit] for qubit in all_cirq_qubits]))))
|
3982
|
+
elif isinstance(global_idle_replacement_label, _Label):
|
3983
|
+
circuit_layers.append(global_idle_replacement_label)
|
3984
|
+
#check whether any of the elements are implied idles, and if so use flag
|
3985
|
+
#to determine whether to include them. We have already checked if this layer
|
3986
|
+
#is a global idle, so if not then we only need to check if any of the layer
|
3987
|
+
#elements are implied idles.
|
3988
|
+
elif remove_implied_idles and 'Gi' in layer_label_elem_names and not all_idles:
|
3989
|
+
stripped_layer_label_elems = [elem for elem in layer_label_elems
|
3990
|
+
if not elem.name == 'Gi']
|
3991
|
+
#if this is length one then add this to the circuit as a bare label, otherwise
|
3992
|
+
#add as a layer label.
|
3993
|
+
if len(stripped_layer_label_elems)==1:
|
3994
|
+
circuit_layers.append(stripped_layer_label_elems[0])
|
3995
|
+
else:
|
3996
|
+
circuit_layers.append(_Label(stripped_layer_label_elems))
|
3997
|
+
#otherwise, just add this layer as-is.
|
3998
|
+
else:
|
3999
|
+
circuit_layers.append(_Label(layer_label_elems))
|
4000
|
+
|
4001
|
+
#if any of the circuit layers are global idles, then we'll force the circuit line
|
4002
|
+
#labels to include all of the qubits appearing in the cirq circuit, otherwise
|
4003
|
+
#we'll let the Circuit constructor figure this out.
|
4004
|
+
if seen_global_idle:
|
4005
|
+
return cls(circuit_layers, line_labels = tuple(sorted([qubit_conversion[qubit] for qubit in all_cirq_qubits])))
|
4006
|
+
else:
|
4007
|
+
return cls(circuit_layers)
|
3763
4008
|
|
3764
4009
|
def convert_to_quil(self,
|
3765
4010
|
num_qubits=None,
|
@@ -3826,19 +4071,19 @@ class Circuit(object):
|
|
3826
4071
|
# To tell us whether we have found a standard qubit labelling type.
|
3827
4072
|
standardtype = False
|
3828
4073
|
# Must first check they are strings, because cannot query q[0] for int q.
|
3829
|
-
if all([isinstance(q, str) for q in self.
|
3830
|
-
if all([q[0] == 'Q' for q in self.
|
4074
|
+
if all([isinstance(q, str) for q in self._line_labels]):
|
4075
|
+
if all([q[0] == 'Q' for q in self._line_labels]):
|
3831
4076
|
standardtype = True
|
3832
|
-
qubit_conversion = {llabel: int(llabel[1:]) for llabel in self.
|
3833
|
-
if all([isinstance(q, int) for q in self.
|
3834
|
-
qubit_conversion = {q: q for q in self.
|
4077
|
+
qubit_conversion = {llabel: int(llabel[1:]) for llabel in self._line_labels}
|
4078
|
+
if all([isinstance(q, int) for q in self._line_labels]):
|
4079
|
+
qubit_conversion = {q: q for q in self._line_labels}
|
3835
4080
|
standardtype = True
|
3836
4081
|
if not standardtype:
|
3837
4082
|
raise ValueError(
|
3838
4083
|
"No standard qubit labelling conversion is available! Please provide `qubit_conversion`.")
|
3839
4084
|
|
3840
4085
|
if num_qubits is None:
|
3841
|
-
num_qubits = len(self.
|
4086
|
+
num_qubits = len(self._line_labels)
|
3842
4087
|
|
3843
4088
|
# Init the quil string.
|
3844
4089
|
quil = ''
|
@@ -3866,7 +4111,7 @@ class Circuit(object):
|
|
3866
4111
|
|
3867
4112
|
# Go through the (non-self.identity) gates in the layer and convert them to quil
|
3868
4113
|
for gate in layer.components:
|
3869
|
-
gate_qubits = gate.qubits if (gate.qubits is not None) else self.
|
4114
|
+
gate_qubits = gate.qubits if (gate.qubits is not None) else self._line_labels
|
3870
4115
|
assert(len(gate_qubits) <= 2 or gate.qubits is None), \
|
3871
4116
|
'Gate on more than 2 qubits given; this is currently not supported!'
|
3872
4117
|
|
@@ -3904,7 +4149,7 @@ class Circuit(object):
|
|
3904
4149
|
qubits_used.extend(gate_qubits)
|
3905
4150
|
|
3906
4151
|
# All gates that don't have a non-idle gate acting on them get an idle in the layer.
|
3907
|
-
for q in self.
|
4152
|
+
for q in self._line_labels:
|
3908
4153
|
if q not in qubits_used:
|
3909
4154
|
quil += 'I' + ' ' + str(qubit_conversion[q]) + '\n'
|
3910
4155
|
|
@@ -3919,11 +4164,11 @@ class Circuit(object):
|
|
3919
4164
|
|
3920
4165
|
# Add in a measurement at the end.
|
3921
4166
|
if readout_conversion is None:
|
3922
|
-
for q in self.
|
4167
|
+
for q in self._line_labels:
|
3923
4168
|
# quil += "MEASURE {0} [{1}]\n".format(str(qubit_conversion[q]),str(qubit_conversion[q]))
|
3924
4169
|
quil += "MEASURE {0} ro[{1}]\n".format(str(qubit_conversion[q]), str(qubit_conversion[q]))
|
3925
4170
|
else:
|
3926
|
-
for q in self.
|
4171
|
+
for q in self._line_labels:
|
3927
4172
|
quil += "MEASURE {0} ro[{1}]\n".format(str(qubit_conversion[q]), str(readout_conversion[q]))
|
3928
4173
|
|
3929
4174
|
return quil
|
@@ -4003,22 +4248,19 @@ class Circuit(object):
|
|
4003
4248
|
# To tell us whether we have found a standard qubit labelling type.
|
4004
4249
|
standardtype = False
|
4005
4250
|
# Must first check they are strings, because cannot query q[0] for int q.
|
4006
|
-
if all([isinstance(q, str) for q in self.
|
4007
|
-
if all([q[0] == 'Q' for q in self.
|
4251
|
+
if all([isinstance(q, str) for q in self._line_labels]):
|
4252
|
+
if all([q[0] == 'Q' for q in self._line_labels]):
|
4008
4253
|
standardtype = True
|
4009
|
-
qubit_conversion = {llabel: int(llabel[1:]) for llabel in self.
|
4010
|
-
if all([isinstance(q, int) for q in self.
|
4011
|
-
qubit_conversion = {q: q for q in self.
|
4254
|
+
qubit_conversion = {llabel: int(llabel[1:]) for llabel in self._line_labels}
|
4255
|
+
if all([isinstance(q, int) for q in self._line_labels]):
|
4256
|
+
qubit_conversion = {q: q for q in self._line_labels}
|
4012
4257
|
standardtype = True
|
4013
4258
|
if not standardtype:
|
4014
4259
|
raise ValueError(
|
4015
4260
|
"No standard qubit labelling conversion is available! Please provide `qubit_conversion`.")
|
4016
4261
|
|
4017
4262
|
if num_qubits is None:
|
4018
|
-
num_qubits = len(self.
|
4019
|
-
|
4020
|
-
# if gateargs_map is None:
|
4021
|
-
# gateargs_map = {}
|
4263
|
+
num_qubits = len(self._line_labels)
|
4022
4264
|
|
4023
4265
|
#Currently only using 'Iz' as valid intermediate measurement ('IM') label.
|
4024
4266
|
#Todo: Expand to all intermediate measurements.
|
@@ -4037,6 +4279,9 @@ class Circuit(object):
|
|
4037
4279
|
# Include a delay instruction
|
4038
4280
|
openqasm += 'opaque delay(t) q;\n\n'
|
4039
4281
|
|
4282
|
+
# Add a template for ECR commands that we will replace/remove later
|
4283
|
+
openqasm += "ECRPLACEHOLDER"
|
4284
|
+
|
4040
4285
|
openqasm += 'qreg q[{0}];\n'.format(str(num_qubits))
|
4041
4286
|
# openqasm += 'creg cr[{0}];\n'.format(str(num_qubits))
|
4042
4287
|
openqasm += 'creg cr[{0}];\n'.format(str(num_qubits + num_IMs))
|
@@ -4054,7 +4299,7 @@ class Circuit(object):
|
|
4054
4299
|
|
4055
4300
|
# Go through the (non-self.identity) gates in the layer and convert them to openqasm
|
4056
4301
|
for gate in layer.components:
|
4057
|
-
gate_qubits = gate.qubits if (gate.qubits is not None) else self.
|
4302
|
+
gate_qubits = gate.qubits if (gate.qubits is not None) else self._line_labels
|
4058
4303
|
assert(len(gate_qubits) <= 2), 'Gates on more than 2 qubits given; this is currently not supported!'
|
4059
4304
|
|
4060
4305
|
# Find the openqasm for the gate.
|
@@ -4077,9 +4322,9 @@ class Circuit(object):
|
|
4077
4322
|
openqasm_for_gate += subopenqasm_for_gate + ' q[' + str(qubit_conversion[q]) + '];\n'
|
4078
4323
|
if block_between_gates:
|
4079
4324
|
openqasm_for_gate += 'barrier '
|
4080
|
-
for q in self.
|
4325
|
+
for q in self._line_labels[:-1]:
|
4081
4326
|
openqasm_for_gate += 'q[{0}], '.format(str(qubit_conversion[q]))
|
4082
|
-
openqasm_for_gate += 'q[{0}];\n'.format(str(qubit_conversion[self.
|
4327
|
+
openqasm_for_gate += 'q[{0}];\n'.format(str(qubit_conversion[self._line_labels[-1]]))
|
4083
4328
|
|
4084
4329
|
else:
|
4085
4330
|
openqasm_for_gate += subopenqasm_for_gate
|
@@ -4090,9 +4335,9 @@ class Circuit(object):
|
|
4090
4335
|
openqasm_for_gate += ';\n'
|
4091
4336
|
if block_between_gates:
|
4092
4337
|
openqasm_for_gate += 'barrier '
|
4093
|
-
for q in self.
|
4338
|
+
for q in self._line_labels[:-1]:
|
4094
4339
|
openqasm_for_gate += 'q[{0}], '.format(str(qubit_conversion[q]))
|
4095
|
-
openqasm_for_gate += 'q[{0}];\n'.format(str(qubit_conversion[self.
|
4340
|
+
openqasm_for_gate += 'q[{0}];\n'.format(str(qubit_conversion[self._line_labels[-1]]))
|
4096
4341
|
|
4097
4342
|
else:
|
4098
4343
|
assert len(gate.qubits) == 1
|
@@ -4112,7 +4357,7 @@ class Circuit(object):
|
|
4112
4357
|
|
4113
4358
|
# All gates that don't have a non-idle gate acting on them get an idle in the layer.
|
4114
4359
|
if not block_between_gates and include_delay_on_idle:
|
4115
|
-
for q in self.
|
4360
|
+
for q in self._line_labels:
|
4116
4361
|
if q not in qubits_used:
|
4117
4362
|
# Delay 0 works because of the barrier
|
4118
4363
|
# In OpenQASM3, this should probably be a stretch instead
|
@@ -4126,19 +4371,27 @@ class Circuit(object):
|
|
4126
4371
|
# where pragma blocks should be.
|
4127
4372
|
if block_between_layers:
|
4128
4373
|
openqasm += 'barrier '
|
4129
|
-
for q in self.
|
4374
|
+
for q in self._line_labels[:-1]:
|
4130
4375
|
openqasm += 'q[{0}], '.format(str(qubit_conversion[q]))
|
4131
|
-
openqasm += 'q[{0}];\n'.format(str(qubit_conversion[self.
|
4376
|
+
openqasm += 'q[{0}];\n'.format(str(qubit_conversion[self._line_labels[-1]]))
|
4132
4377
|
# openqasm += ';'
|
4133
4378
|
|
4134
4379
|
# Add in a measurement at the end.
|
4135
|
-
for q in self.
|
4380
|
+
for q in self._line_labels:
|
4136
4381
|
# openqasm += "measure q[{0}] -> cr[{1}];\n".format(str(qubit_conversion[q]), str(qubit_conversion[q]))
|
4137
4382
|
openqasm += "measure q[{0}] -> cr[{1}];\n".format(str(qubit_conversion[q]),
|
4138
4383
|
str(num_IMs_used + qubit_conversion[q]))
|
4384
|
+
|
4385
|
+
# Replace ECR placeholder
|
4386
|
+
ecr_replace_str = ""
|
4387
|
+
if 'ecr' in openqasm:
|
4388
|
+
ecr_replace_str = "gate rzx(param0) q0,q1 { h q1; cx q0,q1; rz(param0) q1; cx q0,q1; h q1; }\n"
|
4389
|
+
ecr_replace_str += "gate ecr q0,q1 { rzx(pi/4) q0,q1; x q0; rzx(-pi/4) q0,q1; }\n\n"
|
4390
|
+
openqasm = openqasm.replace("ECRPLACEHOLDER", ecr_replace_str)
|
4139
4391
|
|
4140
4392
|
return openqasm
|
4141
|
-
|
4393
|
+
|
4394
|
+
@_deprecate_fn('Model.probabilites or Model.sim.probs')
|
4142
4395
|
def simulate(self, model, return_all_outcomes=False):
|
4143
4396
|
"""
|
4144
4397
|
Compute the outcome probabilities of this Circuit using `model` as a model for the gates.
|
@@ -4202,108 +4455,10 @@ class Circuit(object):
|
|
4202
4455
|
"""
|
4203
4456
|
if not self._static:
|
4204
4457
|
self._static = True
|
4205
|
-
self._labels = tuple([
|
4206
|
-
|
4207
|
-
|
4208
|
-
|
4209
|
-
Creates a dictionary of :class:`SeparatePOVMCircuit` objects from expanding the instruments of this circuit.
|
4210
|
-
|
4211
|
-
Each key of the returned dictionary replaces the instruments in this circuit with a selection
|
4212
|
-
of their members. (The size of the resulting dictionary is the product of the sizes of
|
4213
|
-
each instrument appearing in this circuit when `observed_outcomes is None`). Keys are stored
|
4214
|
-
as :class:`SeparatePOVMCircuit` objects so it's easy to keep track of which POVM outcomes (effects)
|
4215
|
-
correspond to observed data. This function is, for the most part, used internally to process
|
4216
|
-
a circuit before computing its outcome probabilities.
|
4217
|
-
|
4218
|
-
Parameters
|
4219
|
-
----------
|
4220
|
-
model : Model
|
4221
|
-
The model used to provide necessary details regarding the expansion, including:
|
4222
|
-
|
4223
|
-
- default SPAM layers
|
4224
|
-
- definitions of instrument-containing layers
|
4225
|
-
- expansions of individual instruments and POVMs
|
4226
|
-
|
4227
|
-
Returns
|
4228
|
-
-------
|
4229
|
-
OrderedDict
|
4230
|
-
A dict whose keys are :class:`SeparatePOVMCircuit` objects and whose
|
4231
|
-
values are tuples of the outcome labels corresponding to this circuit,
|
4232
|
-
one per POVM effect held in the key.
|
4233
|
-
"""
|
4234
|
-
complete_circuit = model.complete_circuit(self)
|
4235
|
-
expanded_circuit_outcomes = _collections.OrderedDict()
|
4236
|
-
povm_lbl = complete_circuit[-1] # "complete" circuits always end with a POVM label
|
4237
|
-
circuit_without_povm = complete_circuit[0:len(complete_circuit) - 1]
|
4238
|
-
|
4239
|
-
def create_tree(lst):
|
4240
|
-
subs = _collections.OrderedDict()
|
4241
|
-
for el in lst:
|
4242
|
-
if len(el) > 0:
|
4243
|
-
if el[0] not in subs: subs[el[0]] = []
|
4244
|
-
subs[el[0]].append(el[1:])
|
4245
|
-
return _collections.OrderedDict([(k, create_tree(sub_lst)) for k, sub_lst in subs.items()])
|
4246
|
-
|
4247
|
-
def add_expanded_circuit_outcomes(circuit, running_outcomes, ootree, start):
|
4248
|
-
"""
|
4249
|
-
"""
|
4250
|
-
cir = circuit if start == 0 else circuit[start:] # for performance, avoid uneeded slicing
|
4251
|
-
for k, layer_label in enumerate(cir, start=start):
|
4252
|
-
components = layer_label.components
|
4253
|
-
#instrument_inds = _np.nonzero([model._is_primitive_instrument_layer_lbl(component)
|
4254
|
-
# for component in components])[0] # SLOWER than statement below
|
4255
|
-
instrument_inds = _np.array([i for i, component in enumerate(components)
|
4256
|
-
if model._is_primitive_instrument_layer_lbl(component)])
|
4257
|
-
if instrument_inds.size > 0:
|
4258
|
-
# This layer contains at least one instrument => recurse with instrument(s) replaced with
|
4259
|
-
# all combinations of their members.
|
4260
|
-
component_lookup = {i: comp for i, comp in enumerate(components)}
|
4261
|
-
instrument_members = [model._member_labels_for_instrument(components[i])
|
4262
|
-
for i in instrument_inds] # also components of outcome labels
|
4263
|
-
for selected_instrmt_members in _itertools.product(*instrument_members):
|
4264
|
-
expanded_layer_lbl = component_lookup.copy()
|
4265
|
-
expanded_layer_lbl.update({i: components[i] + "_" + sel
|
4266
|
-
for i, sel in zip(instrument_inds, selected_instrmt_members)})
|
4267
|
-
expanded_layer_lbl = _Label([expanded_layer_lbl[i] for i in range(len(components))])
|
4268
|
-
|
4269
|
-
if ootree is not None:
|
4270
|
-
new_ootree = ootree
|
4271
|
-
for sel in selected_instrmt_members:
|
4272
|
-
new_ootree = new_ootree.get(sel, {})
|
4273
|
-
if len(new_ootree) == 0: continue # no observed outcomes along this outcome-tree path
|
4274
|
-
else:
|
4275
|
-
new_ootree = None
|
4276
|
-
|
4277
|
-
add_expanded_circuit_outcomes(circuit[0:k] + Circuit((expanded_layer_lbl,)) + circuit[k + 1:],
|
4278
|
-
running_outcomes + selected_instrmt_members, new_ootree, k + 1)
|
4279
|
-
break
|
4280
|
-
|
4281
|
-
else: # no more instruments to process: `cir` contains no instruments => add an expanded circuit
|
4282
|
-
assert(circuit not in expanded_circuit_outcomes) # shouldn't be possible to generate duplicates...
|
4283
|
-
elabels = model._effect_labels_for_povm(povm_lbl) if (observed_outcomes is None) \
|
4284
|
-
else tuple(ootree.keys())
|
4285
|
-
outcomes = tuple((running_outcomes + (elabel,) for elabel in elabels))
|
4286
|
-
expanded_circuit_outcomes[SeparatePOVMCircuit(circuit, povm_lbl, elabels)] = outcomes
|
4287
|
-
|
4288
|
-
ootree = create_tree(observed_outcomes) if observed_outcomes is not None else None # tree of observed outcomes
|
4289
|
-
# e.g. [('0','00'), ('0','01'), ('1','10')] ==> {'0': {'00': {}, '01': {}}, '1': {'10': {}}}
|
4290
|
-
|
4291
|
-
if model._has_instruments():
|
4292
|
-
add_expanded_circuit_outcomes(circuit_without_povm, (), ootree, start=0)
|
4293
|
-
else:
|
4294
|
-
# It may be helpful to cache the set of elabels for a POVM (maybe within the model?) because
|
4295
|
-
# currently the call to _effect_labels_for_povm may be a bottleneck. It's needed, even when we have
|
4296
|
-
# observed outcomes, because there may be some observed outcomes that aren't modeled (e.g. leakage states)
|
4297
|
-
if observed_outcomes is None:
|
4298
|
-
elabels = model._effect_labels_for_povm(povm_lbl)
|
4299
|
-
else:
|
4300
|
-
possible_lbls = set(model._effect_labels_for_povm(povm_lbl))
|
4301
|
-
elabels = tuple([oo for oo in ootree.keys() if oo in possible_lbls])
|
4302
|
-
outcomes = tuple(((elabel,) for elabel in elabels))
|
4303
|
-
expanded_circuit_outcomes[SeparatePOVMCircuit(circuit_without_povm, povm_lbl, elabels)] = outcomes
|
4304
|
-
|
4305
|
-
return expanded_circuit_outcomes
|
4306
|
-
|
4458
|
+
self._labels = tuple([layer_lbl if isinstance(layer_lbl, _Label)
|
4459
|
+
else _Label(layer_lbl) for layer_lbl in self._labels])
|
4460
|
+
self._hashable_tup = self.tup
|
4461
|
+
self._hash = hash(self._hashable_tup)
|
4307
4462
|
|
4308
4463
|
class CompressedCircuit(object):
|
4309
4464
|
"""
|
@@ -4358,7 +4513,7 @@ class CompressedCircuit(object):
|
|
4358
4513
|
self._tup = CompressedCircuit.compress_op_label_tuple(
|
4359
4514
|
circuit.layertup, min_len_to_compress, max_period_to_look_for)
|
4360
4515
|
self._str = circuit.str
|
4361
|
-
self._line_labels = circuit.
|
4516
|
+
self._line_labels = circuit._line_labels
|
4362
4517
|
self._occurrence_id = circuit.occurrence
|
4363
4518
|
|
4364
4519
|
def __getstate__(self):
|
@@ -4492,12 +4647,35 @@ class SeparatePOVMCircuit(object):
|
|
4492
4647
|
"""
|
4493
4648
|
def __init__(self, circuit_without_povm, povm_label, effect_labels):
|
4494
4649
|
self.circuit_without_povm = circuit_without_povm
|
4495
|
-
self.
|
4496
|
-
self.
|
4650
|
+
self._povm_label = povm_label
|
4651
|
+
self._effect_labels = effect_labels
|
4652
|
+
self._full_effect_labels = tuple([(self.povm_label + "_" + el) for el in self._effect_labels])
|
4497
4653
|
|
4498
4654
|
@property
|
4499
4655
|
def full_effect_labels(self):
|
4500
|
-
return
|
4656
|
+
return self._full_effect_labels
|
4657
|
+
|
4658
|
+
@property
|
4659
|
+
def effect_labels(self):
|
4660
|
+
return self._effect_labels
|
4661
|
+
|
4662
|
+
@property
|
4663
|
+
def povm_label(self):
|
4664
|
+
return self._povm_label
|
4665
|
+
|
4666
|
+
@effect_labels.setter
|
4667
|
+
def effect_labels(self, value):
|
4668
|
+
self._effect_labels = value
|
4669
|
+
self._full_effect_labels = tuple([(self._povm_label + "_" + el) for el in value])
|
4670
|
+
|
4671
|
+
@povm_label.setter
|
4672
|
+
def povm_label(self, value):
|
4673
|
+
self._povm_label = value
|
4674
|
+
self._full_effect_labels = tuple([(value + "_" + el) for el in self._effect_labels])
|
4675
|
+
|
4676
|
+
@full_effect_labels.setter
|
4677
|
+
def full_effect_labels(self, value):
|
4678
|
+
self._full_effect_labels = value
|
4501
4679
|
|
4502
4680
|
def __len__(self):
|
4503
4681
|
return len(self.circuit_without_povm) # don't count POVM in length, so slicing works as expected
|
@@ -4512,4 +4690,3 @@ class SeparatePOVMCircuit(object):
|
|
4512
4690
|
return "SeparatePOVM(" + self.circuit_without_povm.str + "," \
|
4513
4691
|
+ str(self.povm_label) + "," + str(self.effect_labels) + ")"
|
4514
4692
|
|
4515
|
-
#LATER: add a method for getting the "POVM_effect" labels?
|