qiskit_trebugger.views.cli.cli_pass_pad

  1import curses
  2from datetime import datetime
  3from collections import defaultdict
  4
  5import tabulate
  6
  7
  8class TranspilerPassPad:
  9    def __init__(self, step, circuit, property_set, height, width, pad_obj):
 10        """Pass Pad for the CLI Debugger
 11
 12        Args:
 13            step (TranspilationStep): The transpilation step to be displayed
 14            circuit (): Text circuit diagram
 15            property_set (default dict): The property set to be displayed
 16            height (int)): The height of the pad
 17            width (int): The width of the pad
 18            pad_obj (curses.Window): The curses pad object
 19        """
 20        self.transpiler_pass = step
 21        self.circuit = circuit
 22        self.property_set = property_set
 23        self.log_data = []
 24        self.height = height
 25        self.width = width
 26        self.pad = pad_obj
 27        self._start_row = 0
 28
 29    def _get_center(self, width, string_len, divisor=2):
 30        """Get the center of the pad
 31
 32        Args:
 33            width (int): The width of the pad
 34            string_len (int): The length of the string to be centered
 35            divisor (int, optional): The divisor to be used. Defaults to 2.
 36
 37        """
 38        return max(0, int(width // divisor - string_len // 2 - string_len % 2))
 39
 40    def _display_header(self, string):
 41        """Display a header in the pad
 42
 43        Args:
 44            string (str): The string to be displayed
 45        """
 46        offset = self._get_center(self.width, len(string))
 47        self.pad.addstr(self._start_row, offset, string, curses.A_BOLD)
 48
 49    def _add_title(self):
 50        """Add the title of the pass to the pad
 51
 52        Args:
 53            None
 54        """
 55        pass_name = f"{self.transpiler_pass.index}. {self.transpiler_pass.name}"[
 56            : self.width - 1
 57        ]
 58        title_offset = self._get_center(self.width - 4, len(pass_name))
 59        self.pad.addstr(
 60            self._start_row,
 61            title_offset,
 62            pass_name,
 63            curses.A_BOLD,
 64        )
 65        self._start_row += 1
 66        self.pad.hline(self._start_row, 0, "_", self.width - 4)
 67
 68    def _add_information(self):
 69        """Add the information of the pass to the pad
 70
 71        Args:
 72            None
 73        """
 74        self._start_row += 2
 75        pass_type = self.transpiler_pass.pass_type.value
 76        pass_runtime = self.transpiler_pass.duration
 77        info_string = f"Type : {pass_type} | Runtime (ms) : {pass_runtime}"[
 78            : self.width - 1
 79        ]
 80
 81        self._display_header(info_string)
 82
 83    def _add_statistics(self):
 84        """Add the statistics of the pass to the pad
 85
 86        Args:
 87            None
 88        """
 89
 90        self._start_row += 2
 91
 92        props_string = f"Depth : {self.transpiler_pass.circuit_stats.depth} | "
 93        props_string += f"Width : {self.transpiler_pass.circuit_stats.width} | "
 94        props_string += f"Size : {self.transpiler_pass.circuit_stats.size} | "
 95        props_string += f"1Q Ops : {self.transpiler_pass.circuit_stats.ops_1q} | "
 96        props_string += f"2Q Ops : {self.transpiler_pass.circuit_stats.ops_2q}"
 97
 98        props_string = props_string[: self.width - 1]
 99        props_offset = self._get_center(self.width, len(props_string))
100        self.pad.addstr(self._start_row, props_offset, props_string)
101
102    def _get_property_data(self):
103        """Get the property set data as a list of lists
104
105        Args:
106            None
107        """
108
109        prop_data = []
110        vf2_properties = {
111            "VF2Layout_stop_reason",
112            "VF2PostLayout_stop_reason",
113        }
114
115        for name, property_ in self.property_set.items():
116            changed_prop = True
117            if property_.prop_type not in (int, float, bool, str):
118                if name in vf2_properties:
119                    txt = property_.value.name
120                elif name == "optimization_loop_minimum_point_state":
121                    txt = f"""score : {property_.value.score}, since : {property_.value.since}"""
122                elif name == "commutation_set":
123                    txt = "(dict)"
124                else:
125                    txt = (
126                        "(dict)"
127                        if isinstance(property_.value, defaultdict)
128                        else "(" + property_.prop_type.__name__ + ")"
129                    )
130
131            else:
132                txt = str(property_.value)
133
134            if not property_.state or len(property_.state) == 0:
135                changed_prop = False
136                property_.state = "---"
137
138            data_item = [name, txt, property_.state]
139            if changed_prop:
140                prop_data.insert(0, data_item)
141            else:
142                prop_data.append(data_item)
143
144        return prop_data
145
146    def _add_property_set(self):
147        """Add the property set to the pad
148
149        Args:
150            None
151        """
152
153        self._start_row += 2
154        self._display_header("Property Set"[: self.width - 1])
155        self._start_row += 1
156
157        headers = ["Property", "Value", "State"]
158
159        prop_data = self._get_property_data()
160
161        prop_set_table = tabulate.tabulate(
162            tabular_data=prop_data,
163            headers=headers,
164            tablefmt="simple_grid",
165            stralign="center",
166            numalign="center",
167            showindex=True,
168        ).splitlines()
169
170        props_offset = self._get_center(self.width, len(prop_set_table[0]))
171        for index, row in enumerate(prop_set_table):
172            # 0 is default
173            highlight = 0 if index > 2 else curses.A_BOLD
174            self.pad.addstr(
175                index + self._start_row,
176                props_offset,
177                row[: self.width - 1],
178                highlight,
179            )
180        self._start_row += len(prop_set_table)
181
182    def _add_original_qubits(self):
183        if "original_qubit_indices" not in self.property_set:
184            return
185
186        self._start_row += 2
187        self._display_header("Original Qubit Indices"[: self.width - 1])
188        self._start_row += 1
189
190        original_indices = self.property_set["original_qubit_indices"].value.items()
191
192        index_data = []
193        for qubit, index in original_indices:
194            index_data.append([qubit, index])
195
196        headers = ["Qubit", "Index"]
197
198        indices_table = tabulate.tabulate(
199            tabular_data=index_data,
200            headers=headers,
201            tablefmt="simple_grid",
202            stralign="center",
203            numalign="center",
204            showindex=False,
205        ).splitlines()
206
207        indices_offset = self._get_center(self.width, len(indices_table[0]))
208        for index, row in enumerate(indices_table):
209            # 0 is default
210            highlight = 0 if index > 2 else curses.A_BOLD
211            self.pad.addstr(
212                index + self._start_row,
213                indices_offset,
214                row[: self.width - 1],
215                highlight,
216            )
217        self._start_row += len(indices_table)
218
219    def _add_layout(self, layout_type):
220        if "original_qubit_indices" not in self.property_set:
221            return
222
223        if layout_type not in self.property_set:
224            return
225
226        # total num of physical qubits
227        physical_qubits = len(self.property_set["original_qubit_indices"].value)
228        curr_layout = self.property_set[layout_type].value.get_physical_bits()
229
230        # original map of qubits to indices
231        original_indices = self.property_set["original_qubit_indices"].value
232
233        # add the layout to the pad
234        self._start_row += 2
235        self._display_header(f"{layout_type}"[: self.width - 1])
236        self._start_row += 1
237
238        elements_per_table = 15
239        # multiple tables required
240        num_tables = physical_qubits // elements_per_table
241        num_tables += 1 if physical_qubits % elements_per_table != 0 else 0
242
243        for i in range(num_tables):
244            data = []
245            start = i * elements_per_table
246            end = start + elements_per_table - 1
247
248            if start >= end:
249                break
250
251            data.append(f"Physical Qubits({start}-{min(physical_qubits-1,end)})")
252
253            for qubit in range(start, end + 1):
254                if qubit not in curr_layout:
255                    data.append("--")
256                    continue
257                virtual_qubit = curr_layout[qubit]
258                data.append(original_indices[virtual_qubit])
259
260            # draw this single row table now
261            data_table = tabulate.tabulate(
262                tabular_data=[data],
263                tablefmt="simple_grid",
264                stralign="center",
265                numalign="center",
266                showindex=False,
267            ).splitlines()
268
269            table_offset = self._get_center(self.width, len(data_table[0]))
270            for row, _ in enumerate(data_table):
271                self.pad.addstr(
272                    row + self._start_row,
273                    table_offset,
274                    data_table[row][: self.width - 1],
275                    curses.A_BOLD,
276                )
277            self._start_row += len(data_table) + 1
278
279        self._start_row += 1
280
281    def _add_documentation(self):
282        """Add the documentation to the pad
283
284        Args:
285            None
286        """
287
288        self._start_row += 2
289        self._display_header("Documentation"[: self.width - 1])
290        self._start_row += 1
291        pass_docs = self.transpiler_pass.get_docs()
292
293        if pass_docs and pass_docs.count("\n") > 0:
294            pass_docs = "    " + pass_docs
295        pass_docs = [[pass_docs], [self.transpiler_pass.run_method_docs]]
296
297        docs_table = tabulate.tabulate(
298            tabular_data=pass_docs,
299            tablefmt="simple_grid",
300            stralign="left",
301        ).splitlines()
302
303        docs_offset = self._get_center(self.width, len(docs_table[0]))
304
305        for row in range(len(docs_table)):
306            self.pad.addstr(
307                row + self._start_row,
308                docs_offset,
309                docs_table[row][: self.width - 1],
310            )
311        self._start_row += len(docs_table)
312
313    def _add_circuit(self):
314        """Add the circuit diagram to the pad
315
316        Args:
317            None
318        """
319        self._start_row += 2
320        self._display_header("Circuit Diagram"[: self.width - 1])
321        self._start_row += 1
322        if self.transpiler_pass.circuit_stats.depth < 300:
323            # only if <300 depth, we will get a circuit to draw
324            circ_string = [[self.circuit.draw(output="text", fold=100)]]
325        else:
326            circ_string = [
327                [
328                    f"Circuit depth {self.transpiler_pass.circuit_stats.depth} too large to display"
329                ]
330            ]
331        circ_table = tabulate.tabulate(
332            tabular_data=circ_string,
333            tablefmt="simple_grid",
334            stralign="center",
335            numalign="center",
336        ).splitlines()
337
338        circ_offset = self._get_center(self.width, len(circ_table[0]))
339        for index, row in enumerate(circ_table):
340            self.pad.addstr(index + self._start_row, circ_offset, row)
341
342        self._start_row += len(circ_table)
343
344    def _add_logs(self):
345        """Add the logs to the pad
346
347        Args:
348            None
349        """
350        self._start_row += 2
351        self._display_header("Logs"[: self.width - 1])
352        self._start_row += 1
353
354        if not self.log_data:
355            self.log_data = []
356            for entry in self.transpiler_pass.logs:
357                log_string = f"{datetime.fromtimestamp(entry.time).strftime('%H:%M:%S.%f')[:-3]} | "
358
359                log_string += f"{entry.levelname} \n {entry.msg}" % entry.args
360
361                self.log_data.append([log_string])
362
363        if not self.log_data:
364            self.log_data = [["This pass does not display any Logs."]]
365
366        log_table = tabulate.tabulate(
367            tabular_data=self.log_data,
368            tablefmt="simple_grid",
369            stralign="left",
370            numalign="center",
371        ).splitlines()
372
373        logs_offset = self._get_center(self.width, len(log_table[0]))
374        for index, row in enumerate(log_table):
375            self.pad.addstr(index + self._start_row, logs_offset, row[: self.width - 1])
376        self._start_row += len(log_table)
377
378    def build_pad(self):
379        """Build the pad view"""
380
381        self._add_title()
382        self._add_information()
383        self._add_statistics()
384        self._add_property_set()
385        self._add_original_qubits()
386        self._add_layout("layout")
387        # self._add_layout("final_layout")
388        self._add_circuit()
389        self._add_documentation()
390        self._add_logs()
class TranspilerPassPad:
  9class TranspilerPassPad:
 10    def __init__(self, step, circuit, property_set, height, width, pad_obj):
 11        """Pass Pad for the CLI Debugger
 12
 13        Args:
 14            step (TranspilationStep): The transpilation step to be displayed
 15            circuit (): Text circuit diagram
 16            property_set (default dict): The property set to be displayed
 17            height (int)): The height of the pad
 18            width (int): The width of the pad
 19            pad_obj (curses.Window): The curses pad object
 20        """
 21        self.transpiler_pass = step
 22        self.circuit = circuit
 23        self.property_set = property_set
 24        self.log_data = []
 25        self.height = height
 26        self.width = width
 27        self.pad = pad_obj
 28        self._start_row = 0
 29
 30    def _get_center(self, width, string_len, divisor=2):
 31        """Get the center of the pad
 32
 33        Args:
 34            width (int): The width of the pad
 35            string_len (int): The length of the string to be centered
 36            divisor (int, optional): The divisor to be used. Defaults to 2.
 37
 38        """
 39        return max(0, int(width // divisor - string_len // 2 - string_len % 2))
 40
 41    def _display_header(self, string):
 42        """Display a header in the pad
 43
 44        Args:
 45            string (str): The string to be displayed
 46        """
 47        offset = self._get_center(self.width, len(string))
 48        self.pad.addstr(self._start_row, offset, string, curses.A_BOLD)
 49
 50    def _add_title(self):
 51        """Add the title of the pass to the pad
 52
 53        Args:
 54            None
 55        """
 56        pass_name = f"{self.transpiler_pass.index}. {self.transpiler_pass.name}"[
 57            : self.width - 1
 58        ]
 59        title_offset = self._get_center(self.width - 4, len(pass_name))
 60        self.pad.addstr(
 61            self._start_row,
 62            title_offset,
 63            pass_name,
 64            curses.A_BOLD,
 65        )
 66        self._start_row += 1
 67        self.pad.hline(self._start_row, 0, "_", self.width - 4)
 68
 69    def _add_information(self):
 70        """Add the information of the pass to the pad
 71
 72        Args:
 73            None
 74        """
 75        self._start_row += 2
 76        pass_type = self.transpiler_pass.pass_type.value
 77        pass_runtime = self.transpiler_pass.duration
 78        info_string = f"Type : {pass_type} | Runtime (ms) : {pass_runtime}"[
 79            : self.width - 1
 80        ]
 81
 82        self._display_header(info_string)
 83
 84    def _add_statistics(self):
 85        """Add the statistics of the pass to the pad
 86
 87        Args:
 88            None
 89        """
 90
 91        self._start_row += 2
 92
 93        props_string = f"Depth : {self.transpiler_pass.circuit_stats.depth} | "
 94        props_string += f"Width : {self.transpiler_pass.circuit_stats.width} | "
 95        props_string += f"Size : {self.transpiler_pass.circuit_stats.size} | "
 96        props_string += f"1Q Ops : {self.transpiler_pass.circuit_stats.ops_1q} | "
 97        props_string += f"2Q Ops : {self.transpiler_pass.circuit_stats.ops_2q}"
 98
 99        props_string = props_string[: self.width - 1]
100        props_offset = self._get_center(self.width, len(props_string))
101        self.pad.addstr(self._start_row, props_offset, props_string)
102
103    def _get_property_data(self):
104        """Get the property set data as a list of lists
105
106        Args:
107            None
108        """
109
110        prop_data = []
111        vf2_properties = {
112            "VF2Layout_stop_reason",
113            "VF2PostLayout_stop_reason",
114        }
115
116        for name, property_ in self.property_set.items():
117            changed_prop = True
118            if property_.prop_type not in (int, float, bool, str):
119                if name in vf2_properties:
120                    txt = property_.value.name
121                elif name == "optimization_loop_minimum_point_state":
122                    txt = f"""score : {property_.value.score}, since : {property_.value.since}"""
123                elif name == "commutation_set":
124                    txt = "(dict)"
125                else:
126                    txt = (
127                        "(dict)"
128                        if isinstance(property_.value, defaultdict)
129                        else "(" + property_.prop_type.__name__ + ")"
130                    )
131
132            else:
133                txt = str(property_.value)
134
135            if not property_.state or len(property_.state) == 0:
136                changed_prop = False
137                property_.state = "---"
138
139            data_item = [name, txt, property_.state]
140            if changed_prop:
141                prop_data.insert(0, data_item)
142            else:
143                prop_data.append(data_item)
144
145        return prop_data
146
147    def _add_property_set(self):
148        """Add the property set to the pad
149
150        Args:
151            None
152        """
153
154        self._start_row += 2
155        self._display_header("Property Set"[: self.width - 1])
156        self._start_row += 1
157
158        headers = ["Property", "Value", "State"]
159
160        prop_data = self._get_property_data()
161
162        prop_set_table = tabulate.tabulate(
163            tabular_data=prop_data,
164            headers=headers,
165            tablefmt="simple_grid",
166            stralign="center",
167            numalign="center",
168            showindex=True,
169        ).splitlines()
170
171        props_offset = self._get_center(self.width, len(prop_set_table[0]))
172        for index, row in enumerate(prop_set_table):
173            # 0 is default
174            highlight = 0 if index > 2 else curses.A_BOLD
175            self.pad.addstr(
176                index + self._start_row,
177                props_offset,
178                row[: self.width - 1],
179                highlight,
180            )
181        self._start_row += len(prop_set_table)
182
183    def _add_original_qubits(self):
184        if "original_qubit_indices" not in self.property_set:
185            return
186
187        self._start_row += 2
188        self._display_header("Original Qubit Indices"[: self.width - 1])
189        self._start_row += 1
190
191        original_indices = self.property_set["original_qubit_indices"].value.items()
192
193        index_data = []
194        for qubit, index in original_indices:
195            index_data.append([qubit, index])
196
197        headers = ["Qubit", "Index"]
198
199        indices_table = tabulate.tabulate(
200            tabular_data=index_data,
201            headers=headers,
202            tablefmt="simple_grid",
203            stralign="center",
204            numalign="center",
205            showindex=False,
206        ).splitlines()
207
208        indices_offset = self._get_center(self.width, len(indices_table[0]))
209        for index, row in enumerate(indices_table):
210            # 0 is default
211            highlight = 0 if index > 2 else curses.A_BOLD
212            self.pad.addstr(
213                index + self._start_row,
214                indices_offset,
215                row[: self.width - 1],
216                highlight,
217            )
218        self._start_row += len(indices_table)
219
220    def _add_layout(self, layout_type):
221        if "original_qubit_indices" not in self.property_set:
222            return
223
224        if layout_type not in self.property_set:
225            return
226
227        # total num of physical qubits
228        physical_qubits = len(self.property_set["original_qubit_indices"].value)
229        curr_layout = self.property_set[layout_type].value.get_physical_bits()
230
231        # original map of qubits to indices
232        original_indices = self.property_set["original_qubit_indices"].value
233
234        # add the layout to the pad
235        self._start_row += 2
236        self._display_header(f"{layout_type}"[: self.width - 1])
237        self._start_row += 1
238
239        elements_per_table = 15
240        # multiple tables required
241        num_tables = physical_qubits // elements_per_table
242        num_tables += 1 if physical_qubits % elements_per_table != 0 else 0
243
244        for i in range(num_tables):
245            data = []
246            start = i * elements_per_table
247            end = start + elements_per_table - 1
248
249            if start >= end:
250                break
251
252            data.append(f"Physical Qubits({start}-{min(physical_qubits-1,end)})")
253
254            for qubit in range(start, end + 1):
255                if qubit not in curr_layout:
256                    data.append("--")
257                    continue
258                virtual_qubit = curr_layout[qubit]
259                data.append(original_indices[virtual_qubit])
260
261            # draw this single row table now
262            data_table = tabulate.tabulate(
263                tabular_data=[data],
264                tablefmt="simple_grid",
265                stralign="center",
266                numalign="center",
267                showindex=False,
268            ).splitlines()
269
270            table_offset = self._get_center(self.width, len(data_table[0]))
271            for row, _ in enumerate(data_table):
272                self.pad.addstr(
273                    row + self._start_row,
274                    table_offset,
275                    data_table[row][: self.width - 1],
276                    curses.A_BOLD,
277                )
278            self._start_row += len(data_table) + 1
279
280        self._start_row += 1
281
282    def _add_documentation(self):
283        """Add the documentation to the pad
284
285        Args:
286            None
287        """
288
289        self._start_row += 2
290        self._display_header("Documentation"[: self.width - 1])
291        self._start_row += 1
292        pass_docs = self.transpiler_pass.get_docs()
293
294        if pass_docs and pass_docs.count("\n") > 0:
295            pass_docs = "    " + pass_docs
296        pass_docs = [[pass_docs], [self.transpiler_pass.run_method_docs]]
297
298        docs_table = tabulate.tabulate(
299            tabular_data=pass_docs,
300            tablefmt="simple_grid",
301            stralign="left",
302        ).splitlines()
303
304        docs_offset = self._get_center(self.width, len(docs_table[0]))
305
306        for row in range(len(docs_table)):
307            self.pad.addstr(
308                row + self._start_row,
309                docs_offset,
310                docs_table[row][: self.width - 1],
311            )
312        self._start_row += len(docs_table)
313
314    def _add_circuit(self):
315        """Add the circuit diagram to the pad
316
317        Args:
318            None
319        """
320        self._start_row += 2
321        self._display_header("Circuit Diagram"[: self.width - 1])
322        self._start_row += 1
323        if self.transpiler_pass.circuit_stats.depth < 300:
324            # only if <300 depth, we will get a circuit to draw
325            circ_string = [[self.circuit.draw(output="text", fold=100)]]
326        else:
327            circ_string = [
328                [
329                    f"Circuit depth {self.transpiler_pass.circuit_stats.depth} too large to display"
330                ]
331            ]
332        circ_table = tabulate.tabulate(
333            tabular_data=circ_string,
334            tablefmt="simple_grid",
335            stralign="center",
336            numalign="center",
337        ).splitlines()
338
339        circ_offset = self._get_center(self.width, len(circ_table[0]))
340        for index, row in enumerate(circ_table):
341            self.pad.addstr(index + self._start_row, circ_offset, row)
342
343        self._start_row += len(circ_table)
344
345    def _add_logs(self):
346        """Add the logs to the pad
347
348        Args:
349            None
350        """
351        self._start_row += 2
352        self._display_header("Logs"[: self.width - 1])
353        self._start_row += 1
354
355        if not self.log_data:
356            self.log_data = []
357            for entry in self.transpiler_pass.logs:
358                log_string = f"{datetime.fromtimestamp(entry.time).strftime('%H:%M:%S.%f')[:-3]} | "
359
360                log_string += f"{entry.levelname} \n {entry.msg}" % entry.args
361
362                self.log_data.append([log_string])
363
364        if not self.log_data:
365            self.log_data = [["This pass does not display any Logs."]]
366
367        log_table = tabulate.tabulate(
368            tabular_data=self.log_data,
369            tablefmt="simple_grid",
370            stralign="left",
371            numalign="center",
372        ).splitlines()
373
374        logs_offset = self._get_center(self.width, len(log_table[0]))
375        for index, row in enumerate(log_table):
376            self.pad.addstr(index + self._start_row, logs_offset, row[: self.width - 1])
377        self._start_row += len(log_table)
378
379    def build_pad(self):
380        """Build the pad view"""
381
382        self._add_title()
383        self._add_information()
384        self._add_statistics()
385        self._add_property_set()
386        self._add_original_qubits()
387        self._add_layout("layout")
388        # self._add_layout("final_layout")
389        self._add_circuit()
390        self._add_documentation()
391        self._add_logs()
TranspilerPassPad(step, circuit, property_set, height, width, pad_obj)
10    def __init__(self, step, circuit, property_set, height, width, pad_obj):
11        """Pass Pad for the CLI Debugger
12
13        Args:
14            step (TranspilationStep): The transpilation step to be displayed
15            circuit (): Text circuit diagram
16            property_set (default dict): The property set to be displayed
17            height (int)): The height of the pad
18            width (int): The width of the pad
19            pad_obj (curses.Window): The curses pad object
20        """
21        self.transpiler_pass = step
22        self.circuit = circuit
23        self.property_set = property_set
24        self.log_data = []
25        self.height = height
26        self.width = width
27        self.pad = pad_obj
28        self._start_row = 0

Pass Pad for the CLI Debugger

Args: step (TranspilationStep): The transpilation step to be displayed circuit (): Text circuit diagram property_set (default dict): The property set to be displayed height (int)): The height of the pad width (int): The width of the pad pad_obj (curses.Window): The curses pad object

transpiler_pass
circuit
property_set
log_data
height
width
pad
def build_pad(self):
379    def build_pad(self):
380        """Build the pad view"""
381
382        self._add_title()
383        self._add_information()
384        self._add_statistics()
385        self._add_property_set()
386        self._add_original_qubits()
387        self._add_layout("layout")
388        # self._add_layout("final_layout")
389        self._add_circuit()
390        self._add_documentation()
391        self._add_logs()

Build the pad view