9
9
# in compliance with the License. You may obtain a copy of the License at
10
10
# http://www.apache.org/licenses/LICENSE-2.0 or in the LICENSE file in the root pyGSTi directory.
11
11
#***************************************************************************************************
12
-
12
+ from __future__ import annotations
13
13
import collections as _collections
14
14
import itertools as _itertools
15
15
import warnings as _warnings
22
22
from pygsti .tools import slicetools as _slct
23
23
from pygsti .tools .legacytools import deprecate as _deprecate_fn
24
24
25
+ from typing import Union , Optional , TYPE_CHECKING
26
+ if TYPE_CHECKING :
27
+ import stim
28
+
25
29
#Externally, we'd like to do thinks like:
26
30
# c = Circuit( LabelList )
27
31
# c.append_line("Q0")
@@ -3762,7 +3766,9 @@ def _write_q_circuit_tex(self, filename): # TODO
3762
3766
f .close ()
3763
3767
3764
3768
3765
- def convert_to_stim_tableau_layers (self , gate_name_conversions = None , num_qubits = None ):
3769
+ def convert_to_stim_tableau_layers (self , gate_name_conversions : Optional [dict [str , stim .Tableau ]] = None ,
3770
+ num_qubits : Optional [int ] = None ,
3771
+ qubit_label_conversions : Optional [dict [Union [str , int ], int ]] = None ) -> list [stim .Tableau ]:
3766
3772
"""
3767
3773
Converts this circuit to a list of stim tableau layers
3768
3774
@@ -3773,6 +3779,41 @@ def convert_to_stim_tableau_layers(self, gate_name_conversions=None, num_qubits=
3773
3779
If None a standard set of gate names is used from
3774
3780
`pygsti.tools.internalgates`
3775
3781
3782
+ num_qubits : int, optional (default None)
3783
+ Number of qubits which should be included in the each Tableau.
3784
+ If None this value will be attempted to be inferred from the
3785
+ Circuit's line_labels.
3786
+
3787
+ qubit_label_conversions : dict, optional (default None)
3788
+ A map from the circuit's qubit labels into integers in the range 0 to N-1,
3789
+ where N is the number of qubits, where the integer indices indicate
3790
+ which qubit in the stim Tableau to map a given circuit operation into.
3791
+ If not specified an attempt will be made to infer this mapping based on the
3792
+ circuit's qubit labels and an exception will be made if this inference is not possible.
3793
+
3794
+ Note: in the following line_labels = self.line_labels.
3795
+
3796
+ Inference is supported for line labels with the following format.
3797
+
3798
+ - Qubit labels that are either integers, or string representations of integers, prefixed by
3799
+ the character 'Q' or 'q', that are monotonically increasing.
3800
+
3801
+ If num_qubits is not specified, then these qubit labels will be mapped into the range [0,N-1] based
3802
+ on their index.
3803
+
3804
+ If num_qubits is specified then there are different subcases:
3805
+ If num_qubits == len(line_labels) then these qubit labels will be mapped into the range [0,N-1] based
3806
+ on their index.
3807
+ If num_qubit > len(line_labels) we have two possibilities:
3808
+ 1. If max(line_labels) <= num_qubits then the line_labels are mapped directly into their value
3809
+ in the range [0, N-1].
3810
+ 2. If max(line_labels) > num_qubits then the line_labels are mapped into [0,N-1] using their index.
3811
+ Note if num_qubits<len(line_labels) then an exception is raised.
3812
+
3813
+ If the default conversion behavior doesn't suit your needs, or doesn't support your label format then
3814
+ a manual dictionary should be specified.
3815
+
3816
+
3776
3817
Returns
3777
3818
-------
3778
3819
A layer by layer list of stim tableaus
@@ -3783,12 +3824,46 @@ def convert_to_stim_tableau_layers(self, gate_name_conversions=None, num_qubits=
3783
3824
raise ImportError ("Stim is required for this operation, and it does not appear to be installed." )
3784
3825
if gate_name_conversions is None :
3785
3826
gate_name_conversions = _itgs .standard_gatenames_stim_conversions ()
3827
+ line_labels = self ._line_labels
3786
3828
3787
3829
if num_qubits is None :
3788
- line_labels = self ._line_labels
3789
3830
assert line_labels != ('*' ,), "Cannot convert circuits with placeholder line label to stim Tableau unless number of qubits is specified."
3790
3831
num_qubits = len (line_labels )
3791
-
3832
+
3833
+ if qubit_label_conversions is None : #attempt to infer this.
3834
+ assert len (line_labels )<= num_qubits , 'More line labels specified than qubits, cannot infer qubit label conversion mapping.'
3835
+
3836
+ #case 1: qubit labels are monotonically increasing integers and span a range of length num_qubits.
3837
+ if all ([isinstance (lbl , int ) for lbl in line_labels ]) and all ([(line_labels [i + 1 ]- line_labels [i ])> 0 for i in range (len (line_labels )- 1 )]):
3838
+ if len (line_labels ) == num_qubits or max (line_labels )> num_qubits :
3839
+ qubit_label_conversions = {lbl :i for i , lbl in enumerate (line_labels )}
3840
+ elif max (line_labels )<= num_qubits :
3841
+ qubit_label_conversions = {lbl :lbl for lbl in line_labels }
3842
+
3843
+ #Case 2: qubit labels are strings of the form 'Qi' or 'qi' where the i's are string representations of integers otherwise matching the constraints of
3844
+ #case 1.
3845
+ elif all ([isinstance (lbl ,str ) for lbl in line_labels ]):
3846
+ if all ([lbl [0 ]== 'Q' or lbl [0 ]== 'q' for lbl in line_labels ]) and all ([lbl [1 :].isnumeric () for lbl in line_labels ]):
3847
+ int_line_labels = [int (lbl [1 :]) for lbl in line_labels ]
3848
+ if all ([(int_line_labels [i + 1 ] - int_line_labels [i ])> 0 for i in range (len (line_labels )- 1 )]):
3849
+ if len (int_line_labels ) == num_qubits or max (int_line_labels )> num_qubits :
3850
+ qubit_label_conversions = {lbl :i for i , lbl in enumerate (line_labels )}
3851
+ elif max (int_line_labels )<= num_qubits :
3852
+ qubit_label_conversions = {str (lbl ):lbl for lbl in int_line_labels }
3853
+ else :
3854
+ raise ValueError (f'Unsupported line_label type { type (line_labels [0 ])} , only str or int supported.' )
3855
+
3856
+ if qubit_label_conversions is None : #then no automatic conversion was inferred.
3857
+ msg = 'Unable to infer a mapping from line labels of this circuit into integers in the range [0,N-1] ' \
3858
+ + 'as required for conversion to a stim.Tableau object. Please see the docstring for this method for the ' \
3859
+ + 'supported automatic inferencing. Please manually specify a qubit_label_conversions dictionary.'
3860
+ raise RuntimeError (msg )
3861
+ else : #validate qubit_label_conversion dictionary has the keys we need.
3862
+ msg = 'qubit_label_conversions does not contain keys for all of the line_labels in this circuit.'
3863
+ assert all ([lbl in qubit_label_conversions for lbl in line_labels ]), msg
3864
+ msg1 = 'All qubit_label_conversions values should be ints in the range [0, num_qubits-1]'
3865
+ assert all ([isinstance (val ,int ) and (0 <= val <= num_qubits - 1 ) for val in qubit_label_conversions .values ()]), msg1
3866
+
3792
3867
stim_layers = []
3793
3868
3794
3869
if self ._static :
@@ -3800,11 +3875,13 @@ def convert_to_stim_tableau_layers(self, gate_name_conversions=None, num_qubits=
3800
3875
stim_layer = empty_tableau .copy ()
3801
3876
for sub_lbl in layer :
3802
3877
temp = gate_name_conversions [sub_lbl .name ]
3803
- stim_layer .append (temp , sub_lbl .qubits )
3878
+ stim_layer .append (temp , [ qubit_label_conversions [ qubit_lbl ] for qubit_lbl in sub_lbl .qubits ] )
3804
3879
stim_layers .append (stim_layer )
3805
3880
return stim_layers
3806
3881
3807
- def convert_to_stim_tableau (self , gate_name_conversions = None ):
3882
+ def convert_to_stim_tableau (self , gate_name_conversions : Optional [dict [str , stim .Tableau ]] = None ,
3883
+ num_qubits : Optional [int ] = None ,
3884
+ qubit_label_conversions : Optional [dict [Union [str , int ], int ]] = None ) -> stim .Tableau :
3808
3885
"""
3809
3886
Converts this circuit to a stim tableau
3810
3887
@@ -3814,12 +3891,46 @@ def convert_to_stim_tableau(self, gate_name_conversions=None):
3814
3891
A map from pygsti gatenames to standard stim tableaus.
3815
3892
If None a standard set of gate names is used from
3816
3893
`pygsti.tools.internalgates`
3894
+
3895
+ num_qubits : int, optional (default None)
3896
+ Number of qubits which should be included in the overall Tableau.
3897
+ If None this value will be attempted to be inferred from the
3898
+ Circuit's line_labels.
3899
+
3900
+ qubit_label_conversions : dict, optional (default None)
3901
+ A map from the circuit's qubit labels into integers in the range 0 to N-1,
3902
+ where N is the number of qubits, where the integer indices indicate
3903
+ which qubit in the stim Tableau to map a given circuit operation into.
3904
+ If not specified an attempt will be made to infer this mapping based on the
3905
+ circuit's qubit labels and an exception will be made if this inference is not possible.
3906
+
3907
+ Note: in the following line_labels = self.line_labels.
3908
+
3909
+ Inference is supported for line labels with the following format.
3910
+
3911
+ - Qubit labels that are either integers, or string representations of integers, prefixed by
3912
+ the character 'Q' or 'q', that are monotonically increasing.
3913
+
3914
+ If num_qubits is not specified, then these qubit labels will be mapped into the range [0,N-1] based
3915
+ on their index.
3916
+
3917
+ If num_qubits is specified then there are different subcases:
3918
+ If num_qubits == len(line_labels) then these qubit labels will be mapped into the range [0,N-1] based
3919
+ on their index.
3920
+ If num_qubit > len(line_labels) we have two possibilities:
3921
+ 1. If max(line_labels) <= num_qubits then the line_labels are mapped directly into their value
3922
+ in the range [0, N-1].
3923
+ 2. If max(line_labels) > num_qubits then the line_labels are mapped into [0,N-1] using their index.
3924
+ Note if num_qubits<len(line_labels) then an exception is raised.
3925
+
3926
+ If the default conversion behavior doesn't suit your needs, or doesn't support your label format then
3927
+ a manual dictionary should be specified.
3817
3928
3818
3929
Returns
3819
3930
-------
3820
3931
A single stim.Tableau representing the entire circuit.
3821
3932
"""
3822
- layers = self .convert_to_stim_tableau_layers (gate_name_conversions )
3933
+ layers = self .convert_to_stim_tableau_layers (gate_name_conversions , num_qubits , qubit_label_conversions )
3823
3934
if layers :
3824
3935
tableau = layers [0 ]
3825
3936
for layer in layers [1 :]:
0 commit comments