Skip to content

Commit 850623c

Browse files
authored
Merge pull request #625 from sandialabs/feature-magnus-expansions
Magnus Expansions, Better Tableau Inference, and Errorgens on Paulis
2 parents 2be4aaf + b3abb55 commit 850623c

File tree

7 files changed

+1358
-597
lines changed

7 files changed

+1358
-597
lines changed

pygsti/circuits/circuit.py

Lines changed: 118 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# in compliance with the License. You may obtain a copy of the License at
1010
# http://www.apache.org/licenses/LICENSE-2.0 or in the LICENSE file in the root pyGSTi directory.
1111
#***************************************************************************************************
12-
12+
from __future__ import annotations
1313
import collections as _collections
1414
import itertools as _itertools
1515
import warnings as _warnings
@@ -22,6 +22,10 @@
2222
from pygsti.tools import slicetools as _slct
2323
from pygsti.tools.legacytools import deprecate as _deprecate_fn
2424

25+
from typing import Union, Optional, TYPE_CHECKING
26+
if TYPE_CHECKING:
27+
import stim
28+
2529
#Externally, we'd like to do thinks like:
2630
# c = Circuit( LabelList )
2731
# c.append_line("Q0")
@@ -3762,7 +3766,9 @@ def _write_q_circuit_tex(self, filename): # TODO
37623766
f.close()
37633767

37643768

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]:
37663772
"""
37673773
Converts this circuit to a list of stim tableau layers
37683774
@@ -3773,6 +3779,41 @@ def convert_to_stim_tableau_layers(self, gate_name_conversions=None, num_qubits=
37733779
If None a standard set of gate names is used from
37743780
`pygsti.tools.internalgates`
37753781
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+
37763817
Returns
37773818
-------
37783819
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=
37833824
raise ImportError("Stim is required for this operation, and it does not appear to be installed.")
37843825
if gate_name_conversions is None:
37853826
gate_name_conversions = _itgs.standard_gatenames_stim_conversions()
3827+
line_labels = self._line_labels
37863828

37873829
if num_qubits is None:
3788-
line_labels = self._line_labels
37893830
assert line_labels != ('*',), "Cannot convert circuits with placeholder line label to stim Tableau unless number of qubits is specified."
37903831
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+
37923867
stim_layers=[]
37933868

37943869
if self._static:
@@ -3800,11 +3875,13 @@ def convert_to_stim_tableau_layers(self, gate_name_conversions=None, num_qubits=
38003875
stim_layer = empty_tableau.copy()
38013876
for sub_lbl in layer:
38023877
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])
38043879
stim_layers.append(stim_layer)
38053880
return stim_layers
38063881

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:
38083885
"""
38093886
Converts this circuit to a stim tableau
38103887
@@ -3814,12 +3891,46 @@ def convert_to_stim_tableau(self, gate_name_conversions=None):
38143891
A map from pygsti gatenames to standard stim tableaus.
38153892
If None a standard set of gate names is used from
38163893
`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.
38173928
38183929
Returns
38193930
-------
38203931
A single stim.Tableau representing the entire circuit.
38213932
"""
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)
38233934
if layers:
38243935
tableau=layers[0]
38253936
for layer in layers[1:]:

0 commit comments

Comments
 (0)