@@ -1095,13 +1095,12 @@ def _proc_lines_arg(self, lines):
10951095    def  _proc_key_arg (self , key ):
10961096        """ Pre-process the key argument used by many methods """ 
10971097        if  isinstance (key , tuple ):
1098-             if  len (key ) !=  2 : return  IndexError ("Index must be of the form <layerIndex>,<lineIndex>" )
1099-             layers  =  key [0 ]
1100-             lines  =  key [1 ]
1098+             if  len (key ) !=  2 : 
1099+                 return  IndexError ("Index must be of the form <layerIndex>,<lineIndex>" )
1100+             else :
1101+                 return  key [0 ], key [1 ]
11011102        else :
1102-             layers  =  key 
1103-             lines  =  None 
1104-         return  layers , lines 
1103+             return  key , None 
11051104
11061105    def  _layer_components (self , ilayer ):
11071106        """ Get the components of the `ilayer`-th layer as a list/tuple. """ 
@@ -1191,22 +1190,41 @@ def extract_labels(self, layers=None, lines=None, strict=True):
11911190            `layers` is a single integer and as a `Circuit` otherwise. 
11921191            Note: if you want a `Circuit` when only selecting one layer, 
11931192            set `layers` to a slice or tuple containing just a single index. 
1193+             Note that the returned circuit doesn't retain any original 
1194+             metadata, such as the compilable layer indices or occurence id. 
11941195        """ 
11951196        nonint_layers  =  not  isinstance (layers , int )
11961197
11971198        #Shortcut for common case when lines == None and when we're only taking a layer slice/index 
1198-         if  lines  is  None :
1199-             assert (layers  is  not None )
1200-             if  nonint_layers  is  False : return  self .layertup [layers ]
1201-             if  isinstance (layers , slice ) and  strict  is  True :  # if strict=False, then need to recompute line labels 
1202-                 return  Circuit ._fastinit (self ._labels [layers ], self ._line_labels , not  self ._static )
1199+         if  lines  is  None  and  layers  is  not None :
1200+             if  self ._static :
1201+                 if  not  nonint_layers :
1202+                     return  self ._labels [layers ]
1203+                 if  isinstance (layers , slice ) and  strict  is  True :  # if strict=False, then need to recompute line labels 
1204+                     #can speed this up a measurably by manually computing the new hashable tuple value and hash 
1205+                     if  not  self ._line_labels  in  (('*' ,), ()):
1206+                         new_hashable_tup  =  self ._labels [layers ] +  ('@' ,) +  self ._line_labels 
1207+                     else :
1208+                         new_hashable_tup  =  self ._labels [layers ]
1209+                     ret  =  Circuit .__new__ (Circuit )
1210+                     return  ret ._copy_init (self ._labels [layers ], self ._line_labels , not  self ._static , hashable_tup =  new_hashable_tup , precomp_hash = hash (new_hashable_tup ))
1211+             else :
1212+                 if  not  nonint_layers :
1213+                     return  self .layertup [layers ]
1214+                 if  isinstance (layers , slice ) and  strict  is  True :  # if strict=False, then need to recompute line labels 
1215+                     return  Circuit ._fastinit (self ._labels [layers ], self ._line_labels , not  self ._static )
1216+         #otherwise assert both are not None: 
1217+ 
12031218
12041219        layers  =  self ._proc_layers_arg (layers )
12051220        lines  =  self ._proc_lines_arg (lines )
12061221        if  len (layers ) ==  0  or  len (lines ) ==  0 :
1207-             return  Circuit ._fastinit (() if  self ._static  else  [],
1208-                                      tuple (lines ) if  self ._static  else  lines ,
1209-                                      not  self ._static ) if  nonint_layers  else  None   # zero-area region 
1222+             if  self ._static :
1223+                 return  Circuit ._fastinit ((), tuple (lines ), False )  # zero-area region 
1224+             else :
1225+                 return  Circuit ._fastinit (() if  self ._static  else  [],
1226+                                         tuple (lines ) if  self ._static  else  lines ,
1227+                                         not  self ._static )  # zero-area region 
12101228
12111229        ret  =  []
12121230        if  self ._static :
@@ -4427,107 +4445,6 @@ def done_editing(self):
44274445        self ._hashable_tup  =  self .tup 
44284446        self ._hash  =  hash (self ._hashable_tup )
44294447
4430-     def  expand_instruments_and_separate_povm (self , model , observed_outcomes = None ):
4431-         """ 
4432-         Creates a dictionary of :class:`SeparatePOVMCircuit` objects from expanding the instruments of this circuit. 
4433- 
4434-         Each key of the returned dictionary replaces the instruments in this circuit with a selection 
4435-         of their members.  (The size of the resulting dictionary is the product of the sizes of 
4436-         each instrument appearing in this circuit when `observed_outcomes is None`).  Keys are stored 
4437-         as :class:`SeparatePOVMCircuit` objects so it's easy to keep track of which POVM outcomes (effects) 
4438-         correspond to observed data.  This function is, for the most part, used internally to process 
4439-         a circuit before computing its outcome probabilities. 
4440- 
4441-         Parameters 
4442-         ---------- 
4443-         model : Model 
4444-             The model used to provide necessary details regarding the expansion, including: 
4445- 
4446-             - default SPAM layers 
4447-             - definitions of instrument-containing layers 
4448-             - expansions of individual instruments and POVMs 
4449- 
4450-         Returns 
4451-         ------- 
4452-         OrderedDict 
4453-             A dict whose keys are :class:`SeparatePOVMCircuit` objects and whose 
4454-             values are tuples of the outcome labels corresponding to this circuit, 
4455-             one per POVM effect held in the key. 
4456-         """ 
4457-         complete_circuit  =  model .complete_circuit (self )
4458-         expanded_circuit_outcomes  =  _collections .OrderedDict ()
4459-         povm_lbl  =  complete_circuit [- 1 ]  # "complete" circuits always end with a POVM label 
4460-         circuit_without_povm  =  complete_circuit [0 :len (complete_circuit ) -  1 ]
4461- 
4462-         def  create_tree (lst ):
4463-             subs  =  _collections .OrderedDict ()
4464-             for  el  in  lst :
4465-                 if  len (el ) >  0 :
4466-                     if  el [0 ] not  in subs : subs [el [0 ]] =  []
4467-                     subs [el [0 ]].append (el [1 :])
4468-             return  _collections .OrderedDict ([(k , create_tree (sub_lst )) for  k , sub_lst  in  subs .items ()])
4469- 
4470-         def  add_expanded_circuit_outcomes (circuit , running_outcomes , ootree , start ):
4471-             """ 
4472-             """ 
4473-             cir  =  circuit  if  start  ==  0  else  circuit [start :]  # for performance, avoid uneeded slicing 
4474-             for  k , layer_label  in  enumerate (cir , start = start ):
4475-                 components  =  layer_label .components 
4476-                 #instrument_inds = _np.nonzero([model._is_primitive_instrument_layer_lbl(component) 
4477-                 #                               for component in components])[0]  # SLOWER than statement below 
4478-                 instrument_inds  =  _np .array ([i  for  i , component  in  enumerate (components )
4479-                                              if  model ._is_primitive_instrument_layer_lbl (component )])
4480-                 if  instrument_inds .size  >  0 :
4481-                     # This layer contains at least one instrument => recurse with instrument(s) replaced with 
4482-                     #  all combinations of their members. 
4483-                     component_lookup  =  {i : comp  for  i , comp  in  enumerate (components )}
4484-                     instrument_members  =  [model ._member_labels_for_instrument (components [i ])
4485-                                           for  i  in  instrument_inds ]  # also components of outcome labels 
4486-                     for  selected_instrmt_members  in  _itertools .product (* instrument_members ):
4487-                         expanded_layer_lbl  =  component_lookup .copy ()
4488-                         expanded_layer_lbl .update ({i : components [i ] +  "_"  +  sel 
4489-                                                    for  i , sel  in  zip (instrument_inds , selected_instrmt_members )})
4490-                         expanded_layer_lbl  =  _Label ([expanded_layer_lbl [i ] for  i  in  range (len (components ))])
4491- 
4492-                         if  ootree  is  not None :
4493-                             new_ootree  =  ootree 
4494-                             for  sel  in  selected_instrmt_members :
4495-                                 new_ootree  =  new_ootree .get (sel , {})
4496-                             if  len (new_ootree ) ==  0 : continue   # no observed outcomes along this outcome-tree path 
4497-                         else :
4498-                             new_ootree  =  None 
4499- 
4500-                         add_expanded_circuit_outcomes (circuit [0 :k ] +  Circuit ((expanded_layer_lbl ,)) +  circuit [k  +  1 :],
4501-                                                       running_outcomes  +  selected_instrmt_members , new_ootree , k  +  1 )
4502-                     break 
4503- 
4504-             else :  # no more instruments to process: `cir` contains no instruments => add an expanded circuit 
4505-                 assert (circuit  not  in expanded_circuit_outcomes )  # shouldn't be possible to generate duplicates... 
4506-                 elabels  =  model ._effect_labels_for_povm (povm_lbl ) if  (observed_outcomes  is  None ) \
4507-                     else  tuple (ootree .keys ())
4508-                 outcomes  =  tuple ((running_outcomes  +  (elabel ,) for  elabel  in  elabels ))
4509-                 expanded_circuit_outcomes [SeparatePOVMCircuit (circuit , povm_lbl , elabels )] =  outcomes 
4510- 
4511-         ootree  =  create_tree (observed_outcomes ) if  observed_outcomes  is  not None  else  None   # tree of observed outcomes 
4512-         # e.g. [('0','00'), ('0','01'), ('1','10')] ==> {'0': {'00': {}, '01': {}}, '1': {'10': {}}} 
4513- 
4514-         if  model ._has_instruments ():
4515-             add_expanded_circuit_outcomes (circuit_without_povm , (), ootree , start = 0 )
4516-         else :
4517-             # It may be helpful to cache the set of elabels for a POVM (maybe within the model?) because 
4518-             # currently the call to _effect_labels_for_povm may be a bottleneck.  It's needed, even when we have 
4519-             # observed outcomes, because there may be some observed outcomes that aren't modeled (e.g. leakage states) 
4520-             if  observed_outcomes  is  None :
4521-                 elabels  =  model ._effect_labels_for_povm (povm_lbl )
4522-             else :
4523-                 possible_lbls  =  set (model ._effect_labels_for_povm (povm_lbl ))
4524-                 elabels  =  tuple ([oo  for  oo  in  ootree .keys () if  oo  in  possible_lbls ])
4525-             outcomes  =  tuple (((elabel ,) for  elabel  in  elabels ))
4526-             expanded_circuit_outcomes [SeparatePOVMCircuit (circuit_without_povm , povm_lbl , elabels )] =  outcomes 
4527- 
4528-         return  expanded_circuit_outcomes 
4529- 
4530- 
45314448class  CompressedCircuit (object ):
45324449    """ 
45334450    A "compressed" Circuit that requires less disk space. 
0 commit comments