qiskit_trebugger.views.widget.timeline_view

Main Timeline View for the Debugger

  1"""Main Timeline View for the Debugger
  2"""
  3import html
  4import math
  5import warnings
  6from datetime import datetime
  7from collections import defaultdict
  8from IPython.display import HTML
  9import ipywidgets as widgets
 10
 11
 12from qiskit.converters import dag_to_circuit, circuit_to_dag
 13from qiskit.dagcircuit import DAGCircuit
 14
 15
 16from .button_with_value import ButtonWithValue
 17from .timeline_utils import view_circuit, get_spinner_html, get_styles, get_args_panel
 18from ...model.pass_type import PassType
 19from ...model.circuit_stats import CircuitStats
 20from ...model.circuit_comparator import CircuitComparator
 21
 22
 23class TimelineView(widgets.VBox):
 24    """Class to implement the visual debugger.
 25    Inherits from the vertical box widget of
 26    ipywidgets module
 27    """
 28
 29    def __init__(self, *args, **kwargs):
 30        self.layouts = {
 31            "timeline": {
 32                "border": "1px #eee",
 33                "padding": "2px",
 34                "height": "400px",
 35                "overflow": "auto",
 36                "width": "100%",
 37            },
 38            "tabular_data": {
 39                "padding": "5px",
 40                "grid_template_columns": "repeat(2, 50%)",
 41            },
 42        }
 43
 44        style = widgets.HTML(get_styles())
 45        header = widgets.HTML(
 46            '<div class=" widget-gridbox" style="width: 100%; \
 47            grid-template-columns: auto 8%;"><div class=" title">\
 48            <h1>Qiskit Timeline Debugger</h1></div><div class="logo"></div></div>'
 49        )
 50
 51        general_info_panel = widgets.GridBox(children=[], layout={"width": "100%"})
 52        general_info_panel.add_class("options")
 53        # summary panel
 54        summary_heading = widgets.HTML(
 55            "<h2 style = 'margin: 10px 20px 0 30px; \
 56                font-weight: bold;'> Transpilation overview</h2>"
 57        )
 58        summary_panel = widgets.VBox(
 59            [
 60                summary_heading,
 61                widgets.GridBox(
 62                    [],
 63                    layout={
 64                        "width": "100%",
 65                        "padding": "5px",
 66                        "grid_template_columns": "repeat(2, 50%)",
 67                    },
 68                ),
 69            ],
 70            layout={"width": "100%"},
 71        )
 72
 73        # params panel
 74        param_button = widgets.Button(
 75            description="Params set for Transpiler",
 76            icon="caret-right",
 77            tooltip="Params for transpilation",
 78            layout={"width": "auto"},
 79        )
 80        # callback to add the box
 81        param_button.add_class("toggle-button")
 82        param_button.on_click(self._add_args)
 83
 84        params_panel = widgets.VBox([param_button], layout=dict(margin="0 1% 0 1%"))
 85
 86        self.timeline_panel = widgets.VBox([], layout={"width": "100%"})
 87        timeline_wpr = widgets.Box(
 88            [self.timeline_panel], layout=self.layouts["timeline"]
 89        )
 90
 91        stats_title = widgets.Label("Circuit Stats")
 92        stats_title.add_class("stats-title")
 93
 94        self.stats_labels = [
 95            widgets.Label("1q ops"),
 96            widgets.Label(""),
 97            widgets.Label("2q ops"),
 98            widgets.Label(""),
 99            widgets.Label("3+q ops"),
100            widgets.Label(""),
101            widgets.Label("Depth"),
102            widgets.Label(""),
103            widgets.Label("Size"),
104            widgets.Label(""),
105            widgets.Label("Width"),
106            widgets.Label(""),
107        ]
108
109        stats_panel = widgets.GridBox(
110            self.stats_labels, layout=self.layouts["tabular_data"]
111        )
112        stats_panel.add_class("table")
113
114        toggle_pass_button = widgets.Button(
115            description="Transpiler Passes",
116            icon="caret-right",
117            tooltip="Transpiler Passes",
118            layout={"width": "auto"},
119        )
120        toggle_pass_button.add_class("toggle-button")
121        toggle_pass_button.on_click(self._load_passes)
122
123        self.main_panel = widgets.HBox(
124            children=[timeline_wpr], layout={"width": "100%"}
125        )
126
127        pass_panel = widgets.VBox([toggle_pass_button], layout=dict(margin="0 1% 0 1%"))
128
129        super().__init__(*args, **kwargs)
130        self.children = (
131            style,
132            header,
133            general_info_panel,
134            params_panel,
135            summary_panel,
136            pass_panel,
137        )
138        self.layout = {"width": "100%"}
139        self.add_class("tp-widget")
140
141        self.panels = {
142            "general_info": general_info_panel,
143            "summary": summary_panel,
144            "params": params_panel,
145            "pass": pass_panel,
146        }
147
148        self.kwargs_box = None
149
150        self._transpilation_sequence = None
151
152    @property
153    def transpilation_sequence(self):
154        """Returns the transpilation_sequence object"""
155        return self._transpilation_sequence
156
157    @transpilation_sequence.setter
158    def transpilation_sequence(self, transpilation_sequence):
159        self._transpilation_sequence = transpilation_sequence
160
161        # Set general info:
162        items = []
163        general_info = transpilation_sequence.general_info
164        for key, value in general_info.items():
165            items.append(widgets.Label(key + ": " + str(value)))
166        self.panels["general_info"].children = items
167        self.panels["general_info"].layout = {
168            "width": "100%",
169            "grid_template_columns": "repeat(" + str(len(general_info)) + ", auto)",
170        }
171
172    def update_summary(self):
173        """Update the summary panel after the transpilation
174        populates the transpilation sequence
175        """
176        self.panels["summary"].children[1].add_class("summary-panel")
177        self.panels["summary"].children[1].children = self._get_summary_panel()
178
179    def _get_summary_panel(self):
180        # get the total count of passes
181        total_passes = {"T": 0, "A": 0}
182
183        for step in self.transpilation_sequence.steps:
184            if step.pass_type == PassType.TRANSFORMATION:
185                total_passes["T"] += 1
186            else:
187                total_passes["A"] += 1
188
189        transform_head = widgets.HTML(
190            r"""<p class = 'transform-label'>
191                <b> Transformation Passes  </b></p>
192                <p class = 'label-text'>
193                """
194            + str(total_passes["T"])
195            + "</p>"
196        )
197
198        analyse_head = widgets.HTML(
199            r"""<p class = 'analyse-label'>
200                <b> Analysis Passes  </b></p>
201                <p class = 'label-text'>
202                """
203            + str(total_passes["A"])
204            + "</p>"
205        )
206
207        init_step = self.transpilation_sequence.steps[0]
208        final_step = self.transpilation_sequence.steps[-1]
209
210        # build overview
211        overview = {"depths": {"init": 0, "final": 0}, "ops": {"init": 0, "final": 0}}
212
213        # get the depths
214        overview["depths"]["init"] = init_step.circuit_stats.depth
215        overview["depths"]["final"] = final_step.circuit_stats.depth
216
217        # get the op counts
218        overview["ops"]["init"] = (
219            init_step.circuit_stats.ops_1q
220            + init_step.circuit_stats.ops_2q
221            + init_step.circuit_stats.ops_3q
222        )
223
224        overview["ops"]["final"] = (
225            final_step.circuit_stats.ops_1q
226            + final_step.circuit_stats.ops_2q
227            + final_step.circuit_stats.ops_3q
228        )
229
230        init_depth = widgets.HTML(
231            r"<p class = 'label-purple-back'>"
232            + "  Initial depth  </p> <p class = 'label-text'>"
233            + str(overview["depths"]["init"])
234            + "</p>"
235        )
236
237        final_depth = widgets.HTML(
238            r"<p class = 'label-purple-back'>"
239            + "  Final depth  </p> <p class = 'label-text'>"
240            + str(overview["depths"]["final"])
241            + "</p>"
242        )
243
244        init_ops = widgets.HTML(
245            r"<p class = 'label-purple-back'>"
246            + "  Initial Op count </p> <p class = 'label-text'>"
247            + str(overview["ops"]["init"])
248            + "</p>"
249        )
250
251        final_ops = widgets.HTML(
252            r"<p class = 'label-purple-back'>"
253            + "  Final Op count </p> <p class = 'label-text'>"
254            + str(overview["ops"]["final"])
255            + "</p>"
256        )
257
258        overview_children = [
259            transform_head,
260            analyse_head,
261            init_depth,
262            final_depth,
263            init_ops,
264            final_ops,
265        ]
266
267        return overview_children
268
269    def _add_args(self, btn):
270        # here, if the button has been clicked
271        # change the caret and add the child
272        param_children = list(self.panels["params"].children)
273
274        if len(param_children) == 1:
275            param_children.append(self.kwargs_box)
276            btn.icon = "caret-down"
277
278        else:
279            del param_children[-1]
280            btn.icon = "caret-right"
281
282        self.panels["params"].children = param_children
283
284    def update_params(self, **kwargs):
285        """Updates the parameters of the transpilation in
286        debugger
287        """
288        self.kwargs_box = get_args_panel(**kwargs)
289
290    def _load_passes(self, btn):
291        pass_children = list(self.panels["pass"].children)
292
293        if len(pass_children) == 1:
294            pass_children.append(self.main_panel)
295            btn.icon = "caret-down"
296
297        else:
298            del pass_children[-1]
299            btn.icon = "caret-right"
300
301        self.panels["pass"].children = pass_children
302
303    def add_step(self, step):
304        """Add transpilation step into the widget
305
306        Args:
307            step (TranspilationStep): One pass of the transpiler
308                                      modelled as a transpilation
309                                      step"""
310        step_items = []
311
312        _item = ButtonWithValue(
313            value=str(step.index),
314            description="",
315            icon="caret-right",
316            tooltip=step.pass_type.value + " Pass",
317            layout={"width": "11px"},
318        )
319        _item.on_click(self.on_pass)
320        step_items.append(widgets.Box([_item]))
321
322        _item = widgets.HTML(r"<p>" + str(step.index) + " - " + step.name + "</p>")
323        _item.add_class(step.pass_type.value.lower())
324        step_items.append(_item)
325
326        if step.duration > 0:
327            duration_font_size = 10
328            duration_font_size = 10 + round(math.log10(step.duration))
329            _item = widgets.Label(str(round(step.duration, 1)) + " ms")
330            _item.add_class("fs" + str(duration_font_size))
331        else:
332            _item = widgets.Label("")
333        step_items.append(_item)
334
335        # circuit stats:
336        if step.index == 0:
337            prev_stats = CircuitStats()
338        else:
339            prev_stats = self.transpilation_sequence.steps[step.index - 1].circuit_stats
340
341        _item = widgets.HTML(
342            '<span class="stat-name">Depth </span><span class="stat-value">'
343            + str(step.circuit_stats.depth)
344            + "</span>"
345        )
346        if prev_stats.depth != step.circuit_stats.depth:
347            _item.add_class("highlight")
348        step_items.append(_item)
349
350        _item = widgets.HTML(
351            '<span class="stat-name">Size </span><span class="stat-value">'
352            + str(step.circuit_stats.size)
353            + "</span>"
354        )
355        if prev_stats.size != step.circuit_stats.size:
356            _item.add_class("highlight")
357        step_items.append(_item)
358
359        _item = widgets.HTML(
360            '<span class="stat-name">Width </span><span class="stat-value">'
361            + str(step.circuit_stats.width)
362            + "</span>"
363        )
364        if prev_stats.width != step.circuit_stats.width:
365            _item.add_class("highlight")
366        step_items.append(_item)
367
368        _item = widgets.HTML(
369            '<span class="stat-name">1Q ops </span><span class="stat-value">'
370            + str(step.circuit_stats.ops_1q)
371            + "</span>"
372        )
373        if prev_stats.ops_1q != step.circuit_stats.ops_1q:
374            _item.add_class("highlight")
375        step_items.append(_item)
376
377        _item = widgets.HTML(
378            '<span class="stat-name">2Q ops </span><span class="stat-value">'
379            + str(step.circuit_stats.ops_2q)
380            + "</span>"
381        )
382        if prev_stats.ops_2q != step.circuit_stats.ops_2q:
383            _item.add_class("highlight")
384        step_items.append(_item)
385
386        item_wpr = widgets.GridBox(
387            step_items,
388            layout={
389                "width": "100%",
390                "min_height": "47px",
391            },
392        )
393        item_wpr.add_class("transpilation-step")
394
395        details_wpr = widgets.Box(layout={"width": "100%"})
396        details_wpr.add_class("step-details")
397        details_wpr.add_class("step-details-hide")
398
399        self.timeline_panel.children = self.timeline_panel.children + (
400            item_wpr,
401            details_wpr,
402        )
403
404    def show_details(self, step_index, title, content):
405        details_panel = self.timeline_panel.children[2 * step_index + 1]
406        out = widgets.Output(layout={"width": "100%"})
407        details_panel.children = (out,)
408
409        if "step-details-hide" in details_panel._dom_classes:
410            details_panel.remove_class("step-details-hide")
411
412        html_str = """
413        <div class="content-wpr">
414            <div class="content">{content}</div>
415        </div>
416        """.format(
417            content=content
418        )
419
420        out.append_display_data(HTML(html_str))
421
422    def on_pass(self, btn):
423        """Render the pass view on the clicking of the button
424        on the left of the pass panel
425        Args:
426            btn (ButtonWithValue): Button which contains the
427                                   pass index"""
428        step_index = int(btn.value)
429        step = self.transpilation_sequence.steps[step_index]
430
431        # Toggle detailed view:
432        details_panel = self.timeline_panel.children[2 * step_index + 1]
433        if "step-details-hide" not in details_panel._dom_classes:
434            details_panel.add_class("step-details-hide")
435            btn.icon = "caret-right"
436        else:
437            details_panel.remove_class("step-details-hide")
438            btn.icon = "caret-down"
439
440        if len(details_panel.children) == 0:
441            # First time to expand this panel
442            tab_titles = ["Circuit", "Property Set", "Logs", "Help"]
443            children = [
444                widgets.VBox(layout={"width": "100%"}),
445                widgets.HBox(
446                    children=[
447                        widgets.GridBox(
448                            [],
449                            layout={
450                                "width": "50%",
451                                "padding": "5px",
452                                "grid_template_columns": "repeat(2, 50%)",
453                            },
454                        ),
455                        widgets.Output(layout={"width": "50%"}),
456                    ],
457                    layout={"width": "100%"},
458                ),
459                widgets.Output(layout={"width": "100%"}),
460                widgets.Output(layout={"width": "100%"}),
461            ]
462
463            tab = widgets.Tab(model_id=str(step_index), layout={"width": "100%"})
464            tab.children = children
465            for idx, name in enumerate(tab_titles):
466                tab.set_title(idx, name)
467
468            details_panel.children = (tab,)
469            dag = self._get_step_dag(step)
470
471            # this is for the default one
472            # when a tab is clicked, we would need to show something right
473
474            # vars : tab, dag, index that's it
475            # img_thread = Thread(target=self._load_img_view, args=[dag, tab, step_index])
476
477            # img_thread.start()
478            self._load_img_view(dag, tab, step_index)
479
480            tab.observe(self.on_tab_clicked)
481
482            children[1].children[0].add_class("property-set")
483            children[1].children[1].add_class("property-items")
484
485            if len(self._get_step_property_set(step)) == 0:
486                tab.add_class("no-props")
487            if len(step.logs) == 0:
488                tab.add_class("no-logs")
489
490    def _load_img_view(self, dag, tab, step_index):
491        if isinstance(dag, DAGCircuit):
492            img_wpr = widgets.Output(layout={"width": "100%"})
493            img_wpr.append_display_data(HTML(get_spinner_html()))
494
495            circ = dag_to_circuit(dag)
496            img_html = view_circuit(circ, "after_pass_" + str(step_index))
497            img_wpr.outputs = []
498            img_wpr.append_display_data(HTML(img_html))
499
500            diff_chk = widgets.Checkbox(
501                model_id="step:" + str(step_index),
502                value=False,
503                description="Highlight diff",
504                indent=False,
505            )
506            diff_chk.observe(self.on_diff)
507
508            tab.children[0].children = (diff_chk, img_wpr)
509
510        else:
511            message = widgets.Label(
512                value="Displaying circuits with depth larger than 300 is not supported!"
513            )
514            message.add_class("message")
515            tab.children[0].children = (message,)
516
517    def on_tab_clicked(self, change):
518        """Callback to update the information on the debugger view when a
519           tab is clicked from properties, logs or docs
520
521        Args:
522            change (dict): Dict describing the state of the
523                           widget
524        """
525        if change["type"] == "change" and change["name"] == "selected_index":
526            tabs = change.owner
527            step_index = int(tabs.model_id)
528            # get the transpiler pass which is displayed
529            step = self.transpilation_sequence.steps[step_index]
530
531            if change["new"] == 1:
532                properties_panel = tabs.children[1].children[0]
533
534                # If content is already rendered, do nothing:
535                if (
536                    isinstance(tabs.children[1].children[0], widgets.Label)
537                    or len(properties_panel.children) > 0
538                ):
539                    return
540
541                _property_set = self._get_step_property_set(step)
542                if len(_property_set) > 0:
543                    properties_panel.add_class("table")
544                    properties_panel.layout = {
545                        "width": "50%",
546                        "padding": "5px",
547                        "grid_template_columns": "repeat(2, 50%)",
548                        "height": str(33 * len(_property_set)) + "px",
549                    }
550
551                    for prop_name in _property_set:
552                        property_ = _property_set[prop_name]
553                        prop_widget = widgets.Label(value=property_.name)
554                        if property_.prop_type not in (int, float, bool, str):
555                            txt = (
556                                "(dict)"
557                                if isinstance(property_.value, defaultdict)
558                                else "(" + property_.prop_type.__name__ + ")"
559                            )
560                            prop_label = widgets.Label(txt, layout={"width": "80%"})
561                            prop_button = ButtonWithValue(
562                                value=None, description="...", layout={"width": "20%"}
563                            )
564                            prop_button.on_click(self.on_property)
565                            prop_box = widgets.HBox(
566                                [prop_label, prop_button], layout={"width": "100%"}
567                            )
568                        else:
569                            prop_box = widgets.Label(value=str(property_.value))
570
571                        if step.property_set_index == step.index:
572                            if property_.state != "updated":
573                                prop_widget.add_class(property_.state)
574                            prop_box.add_class(property_.state)
575
576                        index = len(properties_panel.children)
577                        properties_panel.children = properties_panel.children + (
578                            prop_widget,
579                            prop_box,
580                        )
581
582                        if property_.prop_type not in (int, float, bool, str):
583                            properties_panel.children[index + 1].children[1].value = (
584                                str(step.index) + "," + property_.name
585                            )
586                        else:
587                            properties_panel.children[index + 1].value = str(
588                                property_.value
589                            )
590
591                    prop_list = list(properties_panel.children)
592                    for p_id in range(int(len(prop_list) / 2)):
593                        if (
594                            properties_panel.children[2 * p_id].value
595                            not in _property_set
596                        ):
597                            properties_panel.children[2 * p_id].add_class("not-exist")
598                            properties_panel.children[2 * p_id + 1].add_class(
599                                "not-exist"
600                            )
601                        else:
602                            properties_panel.children[2 * p_id].remove_class(
603                                "not-exist"
604                            )
605                            properties_panel.children[2 * p_id + 1].remove_class(
606                                "not-exist"
607                            )
608                else:
609                    message = widgets.Label(value="Property set is empty!")
610                    message.add_class("message")
611                    tabs.children[1].children = (message,)
612
613            elif change["new"] == 2:
614                # for the Logs tab of the debugger
615
616                # If content is already rendered, do nothing:
617                if len(tabs.children[2].outputs) > 0:
618                    return
619
620                logs = step.logs
621                if len(logs) > 0:
622                    html_str = '<div class="logs-wpr">'
623                    for entry in logs:
624                        html_str = (
625                            html_str
626                            + "<pre class='date'>{0}</pre>\
627                                <pre class='level {1}'>[{1}]\
628                                </pre><pre class='log-entry {1}'>\
629                                {2}</pre>".format(
630                                datetime.fromtimestamp(entry.time).strftime(
631                                    "%H:%M:%S.%f"
632                                )[:-3],
633                                entry.levelname,
634                                entry.msg % entry.args,
635                            )
636                        )
637                    html_str = html_str + "</div>"
638                    tabs.children[2].append_display_data(HTML(html_str))
639                else:
640                    html_str = '<div class="message">This pass does not \
641                        write any log messages!</div>'
642                    tabs.children[2].append_display_data(HTML(html_str))
643
644            elif change["new"] == 3:
645                # this is the docs tab
646
647                # If content is already rendered, do nothing:
648                if len(tabs.children[3].outputs) > 0:
649                    return
650
651                html_str = '<pre class="help">' + step.docs + "</pre>"
652                html_str = (
653                    html_str
654                    + '<div class="help-header"><span style="color: #e83e8c;">'
655                    + step.name
656                    + '</span>.run(<span style="color: #0072c3;">dag</span>)</div>'
657                )
658                html_str = (
659                    html_str + '<pre class="help">' + step.run_method_docs + "</pre>"
660                )
661                tabs.children[3].append_display_data(HTML(html_str))
662
663    def on_diff(self, change):
664        """Callback to handle the toggling of circuit diff in the debugger
665
666        Args:
667            change (dict): Dict containing the current state of the
668                           widget associated with the callback
669        """
670        if (
671            change["type"] == "change"
672            and isinstance(change["new"], dict)
673            and "value" in change["new"]
674        ):
675            chk = change.owner
676            _, step_index_str = chk.model_id.split(":")
677            step_index = int(step_index_str)
678
679            details_panel = self.timeline_panel.children[2 * int(step_index) + 1]
680            img_wpr = details_panel.children[0].children[0].children[1]
681            img_wpr.outputs = []
682            img_wpr.append_display_data(
683                HTML(get_spinner_html())
684            )  # to get the loader gif
685
686            if change["new"]["value"]:
687                if step_index > 0:
688                    prev_dag = self._get_step_dag(
689                        self.transpilation_sequence.steps[step_index - 1]
690                    )
691                    prev_circ = dag_to_circuit(prev_dag)
692                else:
693                    prev_circ = None
694
695                curr_dag = self._get_step_dag(
696                    self.transpilation_sequence.steps[step_index]
697                )
698                curr_circ = dag_to_circuit(curr_dag)
699
700                # okay so this is basically the circuit diff class
701
702                fully_changed, disp_circ = CircuitComparator.compare(
703                    prev_circ, curr_circ
704                )
705
706                if fully_changed:
707                    chk.description = "Circuit changed fully"
708                    chk.disabled = True
709
710                suffix = "diff_" + str(step_index)
711            else:
712                if not chk.disabled:
713                    dag = self._get_step_dag(
714                        self.transpilation_sequence.steps[step_index]
715                    )
716                    disp_circ = dag_to_circuit(dag)
717                    suffix = "after_pass_" + str(step_index)
718
719            # here, qasm and qpy need the without diff circuits
720            img_html = view_circuit(disp_circ, suffix)
721            img_wpr.outputs = []
722            img_wpr.append_display_data(HTML(img_html))
723
724    def on_property(self, btn):
725        """Callback to handle the toggling of properties in the debugger
726
727        Args:
728            btn (ButtonWithValue): Button associated with the callbacks
729        """
730        warnings.filterwarnings(
731            "ignore",
732            message="Back-references to from Bit instances to \
733            their containing Registers have been deprecated. \
734            Instead, inspect Registers to find their contained Bits.",
735        )
736
737        step_index, property_name = btn.value.split(",")
738
739        details_panel = self.timeline_panel.children[2 * int(step_index) + 1]
740        prop_details_panel = details_panel.children[0].children[1].children[1]
741
742        step = self.transpilation_sequence.steps[int(step_index)]
743        property_set = self._get_step_property_set(step)
744        property_ = property_set[property_name]
745
746        html_str = '<table style="width: 100%">'
747        html_str = (
748            html_str
749            + '<thead><tr><th colspan="'
750            + ("2" if isinstance(property_.value, defaultdict) else "1")
751            + '">'
752            + property_name
753            + "</th></tr></thead>"
754        )
755        if property_name == "block_list":
756            for val in property_.value:
757                v_arr = []
758                for node in val:
759                    qargs = ", ".join(
760                        [
761                            qarg.register.name
762                            + "<small>["
763                            + str(qarg.index)
764                            + "]</small>"
765                            for qarg in node.qargs
766                        ]
767                    )
768                    v_arr.append(
769                        "<strong>" + node.name + "</strong>" + "(" + qargs + ")"
770                    )
771                html_str = html_str + "<tr><td>" + " - ".join(v_arr) + "</td></tr>"
772        elif property_name == "commutation_set":
773            for key, val in property_.value.items():
774                key_str = ""
775                if isinstance(key, tuple):
776                    qargs = ", ".join(
777                        [
778                            qarg.register.name
779                            + "<small>["
780                            + str(qarg.index)
781                            + "]</small>"
782                            for qarg in key[0].qargs
783                        ]
784                    )
785                    key_str = (
786                        "(<strong>"
787                        + (key[0].name if key[0].name is not None else "")
788                        + "</strong>("
789                        + qargs
790                        + "), "
791                    )
792                    key_str = (
793                        key_str
794                        + key[1].register.name
795                        + "<small>["
796                        + str(key[1].index)
797                        + "]</small>"
798                        + ")"
799                    )
800                else:
801                    key_str = (
802                        key.register.name + "<small>[" + str(key.index) + "]</small>"
803                    )
804
805                value_str = ""
806                if isinstance(val, list):
807                    value_str = value_str + "["
808                    for nodes in val:
809                        if isinstance(nodes, list):
810                            nodes_arr = []
811                            for node in nodes:
812                                if node.type == "op":
813                                    qargs = ", ".join(
814                                        [
815                                            qarg.register.name
816                                            + "<small>["
817                                            + str(qarg.index)
818                                            + "]</small>"
819                                            for qarg in node.qargs
820                                        ]
821                                    )
822                                    node_str = (
823                                        "<strong>"
824                                        + (node.name if node.name is not None else "")
825                                        + "</strong>"
826                                        + "("
827                                        + qargs
828                                        + ")"
829                                    )
830                                else:
831                                    node_str = (
832                                        node.type.upper()
833                                        + "(wire="
834                                        + node.wire.register.name
835                                        + "<small>["
836                                        + str(node.wire.index)
837                                        + "]</small>)"
838                                    )
839
840                                nodes_arr.append(node_str)
841
842                            value_str = (
843                                value_str + "[" + (", ".join(nodes_arr)) + "]<br>"
844                            )
845                    value_str = value_str + "]"
846
847                html_str = (
848                    html_str
849                    + '<tr><td style="width:50%">'
850                    + key_str
851                    + "</td><td><pre>"
852                    + value_str
853                    + "</pre></td></tr>"
854                )
855        else:
856            html_str = (
857                html_str
858                + "<tr><td><pre>"
859                + html.escape(str(property_.value))
860                + "</pre></td></tr>"
861            )
862        html_str = html_str + "</table>"
863
864        prop_details_panel.outputs = []
865        prop_details_panel.append_display_data(HTML(html_str))
866
867    def _get_step_dag(self, step):
868        if step.pass_type == PassType.TRANSFORMATION:
869            return step.dag
870
871        idx = step.index
872        # Due to a bug in DAGCircuit.__eq__, we can not use ``step.dag != None``
873
874        found_transform = False
875        while (
876            not isinstance(self.transpilation_sequence.steps[idx].dag, DAGCircuit)
877            and idx > 0
878        ):
879            idx = idx - 1
880            if idx >= 0:
881                found_transform = (
882                    self.transpilation_sequence.steps[idx].pass_type
883                    == PassType.TRANSFORMATION
884                )
885
886        if found_transform is False:
887            return circuit_to_dag(self.transpilation_sequence.original_circuit)
888
889        return self.transpilation_sequence.steps[idx].dag
890
891    def _get_step_property_set(self, step):
892        if step.property_set_index is not None:
893            return self.transpilation_sequence.steps[
894                step.property_set_index
895            ].property_set
896
897        return {}
class TimelineView(ipywidgets.widgets.widget_box.VBox):
 24class TimelineView(widgets.VBox):
 25    """Class to implement the visual debugger.
 26    Inherits from the vertical box widget of
 27    ipywidgets module
 28    """
 29
 30    def __init__(self, *args, **kwargs):
 31        self.layouts = {
 32            "timeline": {
 33                "border": "1px #eee",
 34                "padding": "2px",
 35                "height": "400px",
 36                "overflow": "auto",
 37                "width": "100%",
 38            },
 39            "tabular_data": {
 40                "padding": "5px",
 41                "grid_template_columns": "repeat(2, 50%)",
 42            },
 43        }
 44
 45        style = widgets.HTML(get_styles())
 46        header = widgets.HTML(
 47            '<div class=" widget-gridbox" style="width: 100%; \
 48            grid-template-columns: auto 8%;"><div class=" title">\
 49            <h1>Qiskit Timeline Debugger</h1></div><div class="logo"></div></div>'
 50        )
 51
 52        general_info_panel = widgets.GridBox(children=[], layout={"width": "100%"})
 53        general_info_panel.add_class("options")
 54        # summary panel
 55        summary_heading = widgets.HTML(
 56            "<h2 style = 'margin: 10px 20px 0 30px; \
 57                font-weight: bold;'> Transpilation overview</h2>"
 58        )
 59        summary_panel = widgets.VBox(
 60            [
 61                summary_heading,
 62                widgets.GridBox(
 63                    [],
 64                    layout={
 65                        "width": "100%",
 66                        "padding": "5px",
 67                        "grid_template_columns": "repeat(2, 50%)",
 68                    },
 69                ),
 70            ],
 71            layout={"width": "100%"},
 72        )
 73
 74        # params panel
 75        param_button = widgets.Button(
 76            description="Params set for Transpiler",
 77            icon="caret-right",
 78            tooltip="Params for transpilation",
 79            layout={"width": "auto"},
 80        )
 81        # callback to add the box
 82        param_button.add_class("toggle-button")
 83        param_button.on_click(self._add_args)
 84
 85        params_panel = widgets.VBox([param_button], layout=dict(margin="0 1% 0 1%"))
 86
 87        self.timeline_panel = widgets.VBox([], layout={"width": "100%"})
 88        timeline_wpr = widgets.Box(
 89            [self.timeline_panel], layout=self.layouts["timeline"]
 90        )
 91
 92        stats_title = widgets.Label("Circuit Stats")
 93        stats_title.add_class("stats-title")
 94
 95        self.stats_labels = [
 96            widgets.Label("1q ops"),
 97            widgets.Label(""),
 98            widgets.Label("2q ops"),
 99            widgets.Label(""),
100            widgets.Label("3+q ops"),
101            widgets.Label(""),
102            widgets.Label("Depth"),
103            widgets.Label(""),
104            widgets.Label("Size"),
105            widgets.Label(""),
106            widgets.Label("Width"),
107            widgets.Label(""),
108        ]
109
110        stats_panel = widgets.GridBox(
111            self.stats_labels, layout=self.layouts["tabular_data"]
112        )
113        stats_panel.add_class("table")
114
115        toggle_pass_button = widgets.Button(
116            description="Transpiler Passes",
117            icon="caret-right",
118            tooltip="Transpiler Passes",
119            layout={"width": "auto"},
120        )
121        toggle_pass_button.add_class("toggle-button")
122        toggle_pass_button.on_click(self._load_passes)
123
124        self.main_panel = widgets.HBox(
125            children=[timeline_wpr], layout={"width": "100%"}
126        )
127
128        pass_panel = widgets.VBox([toggle_pass_button], layout=dict(margin="0 1% 0 1%"))
129
130        super().__init__(*args, **kwargs)
131        self.children = (
132            style,
133            header,
134            general_info_panel,
135            params_panel,
136            summary_panel,
137            pass_panel,
138        )
139        self.layout = {"width": "100%"}
140        self.add_class("tp-widget")
141
142        self.panels = {
143            "general_info": general_info_panel,
144            "summary": summary_panel,
145            "params": params_panel,
146            "pass": pass_panel,
147        }
148
149        self.kwargs_box = None
150
151        self._transpilation_sequence = None
152
153    @property
154    def transpilation_sequence(self):
155        """Returns the transpilation_sequence object"""
156        return self._transpilation_sequence
157
158    @transpilation_sequence.setter
159    def transpilation_sequence(self, transpilation_sequence):
160        self._transpilation_sequence = transpilation_sequence
161
162        # Set general info:
163        items = []
164        general_info = transpilation_sequence.general_info
165        for key, value in general_info.items():
166            items.append(widgets.Label(key + ": " + str(value)))
167        self.panels["general_info"].children = items
168        self.panels["general_info"].layout = {
169            "width": "100%",
170            "grid_template_columns": "repeat(" + str(len(general_info)) + ", auto)",
171        }
172
173    def update_summary(self):
174        """Update the summary panel after the transpilation
175        populates the transpilation sequence
176        """
177        self.panels["summary"].children[1].add_class("summary-panel")
178        self.panels["summary"].children[1].children = self._get_summary_panel()
179
180    def _get_summary_panel(self):
181        # get the total count of passes
182        total_passes = {"T": 0, "A": 0}
183
184        for step in self.transpilation_sequence.steps:
185            if step.pass_type == PassType.TRANSFORMATION:
186                total_passes["T"] += 1
187            else:
188                total_passes["A"] += 1
189
190        transform_head = widgets.HTML(
191            r"""<p class = 'transform-label'>
192                <b> Transformation Passes  </b></p>
193                <p class = 'label-text'>
194                """
195            + str(total_passes["T"])
196            + "</p>"
197        )
198
199        analyse_head = widgets.HTML(
200            r"""<p class = 'analyse-label'>
201                <b> Analysis Passes  </b></p>
202                <p class = 'label-text'>
203                """
204            + str(total_passes["A"])
205            + "</p>"
206        )
207
208        init_step = self.transpilation_sequence.steps[0]
209        final_step = self.transpilation_sequence.steps[-1]
210
211        # build overview
212        overview = {"depths": {"init": 0, "final": 0}, "ops": {"init": 0, "final": 0}}
213
214        # get the depths
215        overview["depths"]["init"] = init_step.circuit_stats.depth
216        overview["depths"]["final"] = final_step.circuit_stats.depth
217
218        # get the op counts
219        overview["ops"]["init"] = (
220            init_step.circuit_stats.ops_1q
221            + init_step.circuit_stats.ops_2q
222            + init_step.circuit_stats.ops_3q
223        )
224
225        overview["ops"]["final"] = (
226            final_step.circuit_stats.ops_1q
227            + final_step.circuit_stats.ops_2q
228            + final_step.circuit_stats.ops_3q
229        )
230
231        init_depth = widgets.HTML(
232            r"<p class = 'label-purple-back'>"
233            + "  Initial depth  </p> <p class = 'label-text'>"
234            + str(overview["depths"]["init"])
235            + "</p>"
236        )
237
238        final_depth = widgets.HTML(
239            r"<p class = 'label-purple-back'>"
240            + "  Final depth  </p> <p class = 'label-text'>"
241            + str(overview["depths"]["final"])
242            + "</p>"
243        )
244
245        init_ops = widgets.HTML(
246            r"<p class = 'label-purple-back'>"
247            + "  Initial Op count </p> <p class = 'label-text'>"
248            + str(overview["ops"]["init"])
249            + "</p>"
250        )
251
252        final_ops = widgets.HTML(
253            r"<p class = 'label-purple-back'>"
254            + "  Final Op count </p> <p class = 'label-text'>"
255            + str(overview["ops"]["final"])
256            + "</p>"
257        )
258
259        overview_children = [
260            transform_head,
261            analyse_head,
262            init_depth,
263            final_depth,
264            init_ops,
265            final_ops,
266        ]
267
268        return overview_children
269
270    def _add_args(self, btn):
271        # here, if the button has been clicked
272        # change the caret and add the child
273        param_children = list(self.panels["params"].children)
274
275        if len(param_children) == 1:
276            param_children.append(self.kwargs_box)
277            btn.icon = "caret-down"
278
279        else:
280            del param_children[-1]
281            btn.icon = "caret-right"
282
283        self.panels["params"].children = param_children
284
285    def update_params(self, **kwargs):
286        """Updates the parameters of the transpilation in
287        debugger
288        """
289        self.kwargs_box = get_args_panel(**kwargs)
290
291    def _load_passes(self, btn):
292        pass_children = list(self.panels["pass"].children)
293
294        if len(pass_children) == 1:
295            pass_children.append(self.main_panel)
296            btn.icon = "caret-down"
297
298        else:
299            del pass_children[-1]
300            btn.icon = "caret-right"
301
302        self.panels["pass"].children = pass_children
303
304    def add_step(self, step):
305        """Add transpilation step into the widget
306
307        Args:
308            step (TranspilationStep): One pass of the transpiler
309                                      modelled as a transpilation
310                                      step"""
311        step_items = []
312
313        _item = ButtonWithValue(
314            value=str(step.index),
315            description="",
316            icon="caret-right",
317            tooltip=step.pass_type.value + " Pass",
318            layout={"width": "11px"},
319        )
320        _item.on_click(self.on_pass)
321        step_items.append(widgets.Box([_item]))
322
323        _item = widgets.HTML(r"<p>" + str(step.index) + " - " + step.name + "</p>")
324        _item.add_class(step.pass_type.value.lower())
325        step_items.append(_item)
326
327        if step.duration > 0:
328            duration_font_size = 10
329            duration_font_size = 10 + round(math.log10(step.duration))
330            _item = widgets.Label(str(round(step.duration, 1)) + " ms")
331            _item.add_class("fs" + str(duration_font_size))
332        else:
333            _item = widgets.Label("")
334        step_items.append(_item)
335
336        # circuit stats:
337        if step.index == 0:
338            prev_stats = CircuitStats()
339        else:
340            prev_stats = self.transpilation_sequence.steps[step.index - 1].circuit_stats
341
342        _item = widgets.HTML(
343            '<span class="stat-name">Depth </span><span class="stat-value">'
344            + str(step.circuit_stats.depth)
345            + "</span>"
346        )
347        if prev_stats.depth != step.circuit_stats.depth:
348            _item.add_class("highlight")
349        step_items.append(_item)
350
351        _item = widgets.HTML(
352            '<span class="stat-name">Size </span><span class="stat-value">'
353            + str(step.circuit_stats.size)
354            + "</span>"
355        )
356        if prev_stats.size != step.circuit_stats.size:
357            _item.add_class("highlight")
358        step_items.append(_item)
359
360        _item = widgets.HTML(
361            '<span class="stat-name">Width </span><span class="stat-value">'
362            + str(step.circuit_stats.width)
363            + "</span>"
364        )
365        if prev_stats.width != step.circuit_stats.width:
366            _item.add_class("highlight")
367        step_items.append(_item)
368
369        _item = widgets.HTML(
370            '<span class="stat-name">1Q ops </span><span class="stat-value">'
371            + str(step.circuit_stats.ops_1q)
372            + "</span>"
373        )
374        if prev_stats.ops_1q != step.circuit_stats.ops_1q:
375            _item.add_class("highlight")
376        step_items.append(_item)
377
378        _item = widgets.HTML(
379            '<span class="stat-name">2Q ops </span><span class="stat-value">'
380            + str(step.circuit_stats.ops_2q)
381            + "</span>"
382        )
383        if prev_stats.ops_2q != step.circuit_stats.ops_2q:
384            _item.add_class("highlight")
385        step_items.append(_item)
386
387        item_wpr = widgets.GridBox(
388            step_items,
389            layout={
390                "width": "100%",
391                "min_height": "47px",
392            },
393        )
394        item_wpr.add_class("transpilation-step")
395
396        details_wpr = widgets.Box(layout={"width": "100%"})
397        details_wpr.add_class("step-details")
398        details_wpr.add_class("step-details-hide")
399
400        self.timeline_panel.children = self.timeline_panel.children + (
401            item_wpr,
402            details_wpr,
403        )
404
405    def show_details(self, step_index, title, content):
406        details_panel = self.timeline_panel.children[2 * step_index + 1]
407        out = widgets.Output(layout={"width": "100%"})
408        details_panel.children = (out,)
409
410        if "step-details-hide" in details_panel._dom_classes:
411            details_panel.remove_class("step-details-hide")
412
413        html_str = """
414        <div class="content-wpr">
415            <div class="content">{content}</div>
416        </div>
417        """.format(
418            content=content
419        )
420
421        out.append_display_data(HTML(html_str))
422
423    def on_pass(self, btn):
424        """Render the pass view on the clicking of the button
425        on the left of the pass panel
426        Args:
427            btn (ButtonWithValue): Button which contains the
428                                   pass index"""
429        step_index = int(btn.value)
430        step = self.transpilation_sequence.steps[step_index]
431
432        # Toggle detailed view:
433        details_panel = self.timeline_panel.children[2 * step_index + 1]
434        if "step-details-hide" not in details_panel._dom_classes:
435            details_panel.add_class("step-details-hide")
436            btn.icon = "caret-right"
437        else:
438            details_panel.remove_class("step-details-hide")
439            btn.icon = "caret-down"
440
441        if len(details_panel.children) == 0:
442            # First time to expand this panel
443            tab_titles = ["Circuit", "Property Set", "Logs", "Help"]
444            children = [
445                widgets.VBox(layout={"width": "100%"}),
446                widgets.HBox(
447                    children=[
448                        widgets.GridBox(
449                            [],
450                            layout={
451                                "width": "50%",
452                                "padding": "5px",
453                                "grid_template_columns": "repeat(2, 50%)",
454                            },
455                        ),
456                        widgets.Output(layout={"width": "50%"}),
457                    ],
458                    layout={"width": "100%"},
459                ),
460                widgets.Output(layout={"width": "100%"}),
461                widgets.Output(layout={"width": "100%"}),
462            ]
463
464            tab = widgets.Tab(model_id=str(step_index), layout={"width": "100%"})
465            tab.children = children
466            for idx, name in enumerate(tab_titles):
467                tab.set_title(idx, name)
468
469            details_panel.children = (tab,)
470            dag = self._get_step_dag(step)
471
472            # this is for the default one
473            # when a tab is clicked, we would need to show something right
474
475            # vars : tab, dag, index that's it
476            # img_thread = Thread(target=self._load_img_view, args=[dag, tab, step_index])
477
478            # img_thread.start()
479            self._load_img_view(dag, tab, step_index)
480
481            tab.observe(self.on_tab_clicked)
482
483            children[1].children[0].add_class("property-set")
484            children[1].children[1].add_class("property-items")
485
486            if len(self._get_step_property_set(step)) == 0:
487                tab.add_class("no-props")
488            if len(step.logs) == 0:
489                tab.add_class("no-logs")
490
491    def _load_img_view(self, dag, tab, step_index):
492        if isinstance(dag, DAGCircuit):
493            img_wpr = widgets.Output(layout={"width": "100%"})
494            img_wpr.append_display_data(HTML(get_spinner_html()))
495
496            circ = dag_to_circuit(dag)
497            img_html = view_circuit(circ, "after_pass_" + str(step_index))
498            img_wpr.outputs = []
499            img_wpr.append_display_data(HTML(img_html))
500
501            diff_chk = widgets.Checkbox(
502                model_id="step:" + str(step_index),
503                value=False,
504                description="Highlight diff",
505                indent=False,
506            )
507            diff_chk.observe(self.on_diff)
508
509            tab.children[0].children = (diff_chk, img_wpr)
510
511        else:
512            message = widgets.Label(
513                value="Displaying circuits with depth larger than 300 is not supported!"
514            )
515            message.add_class("message")
516            tab.children[0].children = (message,)
517
518    def on_tab_clicked(self, change):
519        """Callback to update the information on the debugger view when a
520           tab is clicked from properties, logs or docs
521
522        Args:
523            change (dict): Dict describing the state of the
524                           widget
525        """
526        if change["type"] == "change" and change["name"] == "selected_index":
527            tabs = change.owner
528            step_index = int(tabs.model_id)
529            # get the transpiler pass which is displayed
530            step = self.transpilation_sequence.steps[step_index]
531
532            if change["new"] == 1:
533                properties_panel = tabs.children[1].children[0]
534
535                # If content is already rendered, do nothing:
536                if (
537                    isinstance(tabs.children[1].children[0], widgets.Label)
538                    or len(properties_panel.children) > 0
539                ):
540                    return
541
542                _property_set = self._get_step_property_set(step)
543                if len(_property_set) > 0:
544                    properties_panel.add_class("table")
545                    properties_panel.layout = {
546                        "width": "50%",
547                        "padding": "5px",
548                        "grid_template_columns": "repeat(2, 50%)",
549                        "height": str(33 * len(_property_set)) + "px",
550                    }
551
552                    for prop_name in _property_set:
553                        property_ = _property_set[prop_name]
554                        prop_widget = widgets.Label(value=property_.name)
555                        if property_.prop_type not in (int, float, bool, str):
556                            txt = (
557                                "(dict)"
558                                if isinstance(property_.value, defaultdict)
559                                else "(" + property_.prop_type.__name__ + ")"
560                            )
561                            prop_label = widgets.Label(txt, layout={"width": "80%"})
562                            prop_button = ButtonWithValue(
563                                value=None, description="...", layout={"width": "20%"}
564                            )
565                            prop_button.on_click(self.on_property)
566                            prop_box = widgets.HBox(
567                                [prop_label, prop_button], layout={"width": "100%"}
568                            )
569                        else:
570                            prop_box = widgets.Label(value=str(property_.value))
571
572                        if step.property_set_index == step.index:
573                            if property_.state != "updated":
574                                prop_widget.add_class(property_.state)
575                            prop_box.add_class(property_.state)
576
577                        index = len(properties_panel.children)
578                        properties_panel.children = properties_panel.children + (
579                            prop_widget,
580                            prop_box,
581                        )
582
583                        if property_.prop_type not in (int, float, bool, str):
584                            properties_panel.children[index + 1].children[1].value = (
585                                str(step.index) + "," + property_.name
586                            )
587                        else:
588                            properties_panel.children[index + 1].value = str(
589                                property_.value
590                            )
591
592                    prop_list = list(properties_panel.children)
593                    for p_id in range(int(len(prop_list) / 2)):
594                        if (
595                            properties_panel.children[2 * p_id].value
596                            not in _property_set
597                        ):
598                            properties_panel.children[2 * p_id].add_class("not-exist")
599                            properties_panel.children[2 * p_id + 1].add_class(
600                                "not-exist"
601                            )
602                        else:
603                            properties_panel.children[2 * p_id].remove_class(
604                                "not-exist"
605                            )
606                            properties_panel.children[2 * p_id + 1].remove_class(
607                                "not-exist"
608                            )
609                else:
610                    message = widgets.Label(value="Property set is empty!")
611                    message.add_class("message")
612                    tabs.children[1].children = (message,)
613
614            elif change["new"] == 2:
615                # for the Logs tab of the debugger
616
617                # If content is already rendered, do nothing:
618                if len(tabs.children[2].outputs) > 0:
619                    return
620
621                logs = step.logs
622                if len(logs) > 0:
623                    html_str = '<div class="logs-wpr">'
624                    for entry in logs:
625                        html_str = (
626                            html_str
627                            + "<pre class='date'>{0}</pre>\
628                                <pre class='level {1}'>[{1}]\
629                                </pre><pre class='log-entry {1}'>\
630                                {2}</pre>".format(
631                                datetime.fromtimestamp(entry.time).strftime(
632                                    "%H:%M:%S.%f"
633                                )[:-3],
634                                entry.levelname,
635                                entry.msg % entry.args,
636                            )
637                        )
638                    html_str = html_str + "</div>"
639                    tabs.children[2].append_display_data(HTML(html_str))
640                else:
641                    html_str = '<div class="message">This pass does not \
642                        write any log messages!</div>'
643                    tabs.children[2].append_display_data(HTML(html_str))
644
645            elif change["new"] == 3:
646                # this is the docs tab
647
648                # If content is already rendered, do nothing:
649                if len(tabs.children[3].outputs) > 0:
650                    return
651
652                html_str = '<pre class="help">' + step.docs + "</pre>"
653                html_str = (
654                    html_str
655                    + '<div class="help-header"><span style="color: #e83e8c;">'
656                    + step.name
657                    + '</span>.run(<span style="color: #0072c3;">dag</span>)</div>'
658                )
659                html_str = (
660                    html_str + '<pre class="help">' + step.run_method_docs + "</pre>"
661                )
662                tabs.children[3].append_display_data(HTML(html_str))
663
664    def on_diff(self, change):
665        """Callback to handle the toggling of circuit diff in the debugger
666
667        Args:
668            change (dict): Dict containing the current state of the
669                           widget associated with the callback
670        """
671        if (
672            change["type"] == "change"
673            and isinstance(change["new"], dict)
674            and "value" in change["new"]
675        ):
676            chk = change.owner
677            _, step_index_str = chk.model_id.split(":")
678            step_index = int(step_index_str)
679
680            details_panel = self.timeline_panel.children[2 * int(step_index) + 1]
681            img_wpr = details_panel.children[0].children[0].children[1]
682            img_wpr.outputs = []
683            img_wpr.append_display_data(
684                HTML(get_spinner_html())
685            )  # to get the loader gif
686
687            if change["new"]["value"]:
688                if step_index > 0:
689                    prev_dag = self._get_step_dag(
690                        self.transpilation_sequence.steps[step_index - 1]
691                    )
692                    prev_circ = dag_to_circuit(prev_dag)
693                else:
694                    prev_circ = None
695
696                curr_dag = self._get_step_dag(
697                    self.transpilation_sequence.steps[step_index]
698                )
699                curr_circ = dag_to_circuit(curr_dag)
700
701                # okay so this is basically the circuit diff class
702
703                fully_changed, disp_circ = CircuitComparator.compare(
704                    prev_circ, curr_circ
705                )
706
707                if fully_changed:
708                    chk.description = "Circuit changed fully"
709                    chk.disabled = True
710
711                suffix = "diff_" + str(step_index)
712            else:
713                if not chk.disabled:
714                    dag = self._get_step_dag(
715                        self.transpilation_sequence.steps[step_index]
716                    )
717                    disp_circ = dag_to_circuit(dag)
718                    suffix = "after_pass_" + str(step_index)
719
720            # here, qasm and qpy need the without diff circuits
721            img_html = view_circuit(disp_circ, suffix)
722            img_wpr.outputs = []
723            img_wpr.append_display_data(HTML(img_html))
724
725    def on_property(self, btn):
726        """Callback to handle the toggling of properties in the debugger
727
728        Args:
729            btn (ButtonWithValue): Button associated with the callbacks
730        """
731        warnings.filterwarnings(
732            "ignore",
733            message="Back-references to from Bit instances to \
734            their containing Registers have been deprecated. \
735            Instead, inspect Registers to find their contained Bits.",
736        )
737
738        step_index, property_name = btn.value.split(",")
739
740        details_panel = self.timeline_panel.children[2 * int(step_index) + 1]
741        prop_details_panel = details_panel.children[0].children[1].children[1]
742
743        step = self.transpilation_sequence.steps[int(step_index)]
744        property_set = self._get_step_property_set(step)
745        property_ = property_set[property_name]
746
747        html_str = '<table style="width: 100%">'
748        html_str = (
749            html_str
750            + '<thead><tr><th colspan="'
751            + ("2" if isinstance(property_.value, defaultdict) else "1")
752            + '">'
753            + property_name
754            + "</th></tr></thead>"
755        )
756        if property_name == "block_list":
757            for val in property_.value:
758                v_arr = []
759                for node in val:
760                    qargs = ", ".join(
761                        [
762                            qarg.register.name
763                            + "<small>["
764                            + str(qarg.index)
765                            + "]</small>"
766                            for qarg in node.qargs
767                        ]
768                    )
769                    v_arr.append(
770                        "<strong>" + node.name + "</strong>" + "(" + qargs + ")"
771                    )
772                html_str = html_str + "<tr><td>" + " - ".join(v_arr) + "</td></tr>"
773        elif property_name == "commutation_set":
774            for key, val in property_.value.items():
775                key_str = ""
776                if isinstance(key, tuple):
777                    qargs = ", ".join(
778                        [
779                            qarg.register.name
780                            + "<small>["
781                            + str(qarg.index)
782                            + "]</small>"
783                            for qarg in key[0].qargs
784                        ]
785                    )
786                    key_str = (
787                        "(<strong>"
788                        + (key[0].name if key[0].name is not None else "")
789                        + "</strong>("
790                        + qargs
791                        + "), "
792                    )
793                    key_str = (
794                        key_str
795                        + key[1].register.name
796                        + "<small>["
797                        + str(key[1].index)
798                        + "]</small>"
799                        + ")"
800                    )
801                else:
802                    key_str = (
803                        key.register.name + "<small>[" + str(key.index) + "]</small>"
804                    )
805
806                value_str = ""
807                if isinstance(val, list):
808                    value_str = value_str + "["
809                    for nodes in val:
810                        if isinstance(nodes, list):
811                            nodes_arr = []
812                            for node in nodes:
813                                if node.type == "op":
814                                    qargs = ", ".join(
815                                        [
816                                            qarg.register.name
817                                            + "<small>["
818                                            + str(qarg.index)
819                                            + "]</small>"
820                                            for qarg in node.qargs
821                                        ]
822                                    )
823                                    node_str = (
824                                        "<strong>"
825                                        + (node.name if node.name is not None else "")
826                                        + "</strong>"
827                                        + "("
828                                        + qargs
829                                        + ")"
830                                    )
831                                else:
832                                    node_str = (
833                                        node.type.upper()
834                                        + "(wire="
835                                        + node.wire.register.name
836                                        + "<small>["
837                                        + str(node.wire.index)
838                                        + "]</small>)"
839                                    )
840
841                                nodes_arr.append(node_str)
842
843                            value_str = (
844                                value_str + "[" + (", ".join(nodes_arr)) + "]<br>"
845                            )
846                    value_str = value_str + "]"
847
848                html_str = (
849                    html_str
850                    + '<tr><td style="width:50%">'
851                    + key_str
852                    + "</td><td><pre>"
853                    + value_str
854                    + "</pre></td></tr>"
855                )
856        else:
857            html_str = (
858                html_str
859                + "<tr><td><pre>"
860                + html.escape(str(property_.value))
861                + "</pre></td></tr>"
862            )
863        html_str = html_str + "</table>"
864
865        prop_details_panel.outputs = []
866        prop_details_panel.append_display_data(HTML(html_str))
867
868    def _get_step_dag(self, step):
869        if step.pass_type == PassType.TRANSFORMATION:
870            return step.dag
871
872        idx = step.index
873        # Due to a bug in DAGCircuit.__eq__, we can not use ``step.dag != None``
874
875        found_transform = False
876        while (
877            not isinstance(self.transpilation_sequence.steps[idx].dag, DAGCircuit)
878            and idx > 0
879        ):
880            idx = idx - 1
881            if idx >= 0:
882                found_transform = (
883                    self.transpilation_sequence.steps[idx].pass_type
884                    == PassType.TRANSFORMATION
885                )
886
887        if found_transform is False:
888            return circuit_to_dag(self.transpilation_sequence.original_circuit)
889
890        return self.transpilation_sequence.steps[idx].dag
891
892    def _get_step_property_set(self, step):
893        if step.property_set_index is not None:
894            return self.transpilation_sequence.steps[
895                step.property_set_index
896            ].property_set
897
898        return {}

Class to implement the visual debugger. Inherits from the vertical box widget of ipywidgets module

TimelineView(*args, **kwargs)
 30    def __init__(self, *args, **kwargs):
 31        self.layouts = {
 32            "timeline": {
 33                "border": "1px #eee",
 34                "padding": "2px",
 35                "height": "400px",
 36                "overflow": "auto",
 37                "width": "100%",
 38            },
 39            "tabular_data": {
 40                "padding": "5px",
 41                "grid_template_columns": "repeat(2, 50%)",
 42            },
 43        }
 44
 45        style = widgets.HTML(get_styles())
 46        header = widgets.HTML(
 47            '<div class=" widget-gridbox" style="width: 100%; \
 48            grid-template-columns: auto 8%;"><div class=" title">\
 49            <h1>Qiskit Timeline Debugger</h1></div><div class="logo"></div></div>'
 50        )
 51
 52        general_info_panel = widgets.GridBox(children=[], layout={"width": "100%"})
 53        general_info_panel.add_class("options")
 54        # summary panel
 55        summary_heading = widgets.HTML(
 56            "<h2 style = 'margin: 10px 20px 0 30px; \
 57                font-weight: bold;'> Transpilation overview</h2>"
 58        )
 59        summary_panel = widgets.VBox(
 60            [
 61                summary_heading,
 62                widgets.GridBox(
 63                    [],
 64                    layout={
 65                        "width": "100%",
 66                        "padding": "5px",
 67                        "grid_template_columns": "repeat(2, 50%)",
 68                    },
 69                ),
 70            ],
 71            layout={"width": "100%"},
 72        )
 73
 74        # params panel
 75        param_button = widgets.Button(
 76            description="Params set for Transpiler",
 77            icon="caret-right",
 78            tooltip="Params for transpilation",
 79            layout={"width": "auto"},
 80        )
 81        # callback to add the box
 82        param_button.add_class("toggle-button")
 83        param_button.on_click(self._add_args)
 84
 85        params_panel = widgets.VBox([param_button], layout=dict(margin="0 1% 0 1%"))
 86
 87        self.timeline_panel = widgets.VBox([], layout={"width": "100%"})
 88        timeline_wpr = widgets.Box(
 89            [self.timeline_panel], layout=self.layouts["timeline"]
 90        )
 91
 92        stats_title = widgets.Label("Circuit Stats")
 93        stats_title.add_class("stats-title")
 94
 95        self.stats_labels = [
 96            widgets.Label("1q ops"),
 97            widgets.Label(""),
 98            widgets.Label("2q ops"),
 99            widgets.Label(""),
100            widgets.Label("3+q ops"),
101            widgets.Label(""),
102            widgets.Label("Depth"),
103            widgets.Label(""),
104            widgets.Label("Size"),
105            widgets.Label(""),
106            widgets.Label("Width"),
107            widgets.Label(""),
108        ]
109
110        stats_panel = widgets.GridBox(
111            self.stats_labels, layout=self.layouts["tabular_data"]
112        )
113        stats_panel.add_class("table")
114
115        toggle_pass_button = widgets.Button(
116            description="Transpiler Passes",
117            icon="caret-right",
118            tooltip="Transpiler Passes",
119            layout={"width": "auto"},
120        )
121        toggle_pass_button.add_class("toggle-button")
122        toggle_pass_button.on_click(self._load_passes)
123
124        self.main_panel = widgets.HBox(
125            children=[timeline_wpr], layout={"width": "100%"}
126        )
127
128        pass_panel = widgets.VBox([toggle_pass_button], layout=dict(margin="0 1% 0 1%"))
129
130        super().__init__(*args, **kwargs)
131        self.children = (
132            style,
133            header,
134            general_info_panel,
135            params_panel,
136            summary_panel,
137            pass_panel,
138        )
139        self.layout = {"width": "100%"}
140        self.add_class("tp-widget")
141
142        self.panels = {
143            "general_info": general_info_panel,
144            "summary": summary_panel,
145            "params": params_panel,
146            "pass": pass_panel,
147        }
148
149        self.kwargs_box = None
150
151        self._transpilation_sequence = None

Public constructor

layouts
timeline_panel
stats_labels
main_panel
children

List of widget children

layout

An instance trait which coerces a dict to an instance.

This lets the instance be specified as a dict, which is used to initialize the instance.

Also, we default to a trivial instance, even if args and kwargs is not specified.

panels
kwargs_box
transpilation_sequence

Returns the transpilation_sequence object

def update_summary(self):
173    def update_summary(self):
174        """Update the summary panel after the transpilation
175        populates the transpilation sequence
176        """
177        self.panels["summary"].children[1].add_class("summary-panel")
178        self.panels["summary"].children[1].children = self._get_summary_panel()

Update the summary panel after the transpilation populates the transpilation sequence

def update_params(self, **kwargs):
285    def update_params(self, **kwargs):
286        """Updates the parameters of the transpilation in
287        debugger
288        """
289        self.kwargs_box = get_args_panel(**kwargs)

Updates the parameters of the transpilation in debugger

def add_step(self, step):
304    def add_step(self, step):
305        """Add transpilation step into the widget
306
307        Args:
308            step (TranspilationStep): One pass of the transpiler
309                                      modelled as a transpilation
310                                      step"""
311        step_items = []
312
313        _item = ButtonWithValue(
314            value=str(step.index),
315            description="",
316            icon="caret-right",
317            tooltip=step.pass_type.value + " Pass",
318            layout={"width": "11px"},
319        )
320        _item.on_click(self.on_pass)
321        step_items.append(widgets.Box([_item]))
322
323        _item = widgets.HTML(r"<p>" + str(step.index) + " - " + step.name + "</p>")
324        _item.add_class(step.pass_type.value.lower())
325        step_items.append(_item)
326
327        if step.duration > 0:
328            duration_font_size = 10
329            duration_font_size = 10 + round(math.log10(step.duration))
330            _item = widgets.Label(str(round(step.duration, 1)) + " ms")
331            _item.add_class("fs" + str(duration_font_size))
332        else:
333            _item = widgets.Label("")
334        step_items.append(_item)
335
336        # circuit stats:
337        if step.index == 0:
338            prev_stats = CircuitStats()
339        else:
340            prev_stats = self.transpilation_sequence.steps[step.index - 1].circuit_stats
341
342        _item = widgets.HTML(
343            '<span class="stat-name">Depth </span><span class="stat-value">'
344            + str(step.circuit_stats.depth)
345            + "</span>"
346        )
347        if prev_stats.depth != step.circuit_stats.depth:
348            _item.add_class("highlight")
349        step_items.append(_item)
350
351        _item = widgets.HTML(
352            '<span class="stat-name">Size </span><span class="stat-value">'
353            + str(step.circuit_stats.size)
354            + "</span>"
355        )
356        if prev_stats.size != step.circuit_stats.size:
357            _item.add_class("highlight")
358        step_items.append(_item)
359
360        _item = widgets.HTML(
361            '<span class="stat-name">Width </span><span class="stat-value">'
362            + str(step.circuit_stats.width)
363            + "</span>"
364        )
365        if prev_stats.width != step.circuit_stats.width:
366            _item.add_class("highlight")
367        step_items.append(_item)
368
369        _item = widgets.HTML(
370            '<span class="stat-name">1Q ops </span><span class="stat-value">'
371            + str(step.circuit_stats.ops_1q)
372            + "</span>"
373        )
374        if prev_stats.ops_1q != step.circuit_stats.ops_1q:
375            _item.add_class("highlight")
376        step_items.append(_item)
377
378        _item = widgets.HTML(
379            '<span class="stat-name">2Q ops </span><span class="stat-value">'
380            + str(step.circuit_stats.ops_2q)
381            + "</span>"
382        )
383        if prev_stats.ops_2q != step.circuit_stats.ops_2q:
384            _item.add_class("highlight")
385        step_items.append(_item)
386
387        item_wpr = widgets.GridBox(
388            step_items,
389            layout={
390                "width": "100%",
391                "min_height": "47px",
392            },
393        )
394        item_wpr.add_class("transpilation-step")
395
396        details_wpr = widgets.Box(layout={"width": "100%"})
397        details_wpr.add_class("step-details")
398        details_wpr.add_class("step-details-hide")
399
400        self.timeline_panel.children = self.timeline_panel.children + (
401            item_wpr,
402            details_wpr,
403        )

Add transpilation step into the widget

Args: step (TranspilationStep): One pass of the transpiler modelled as a transpilation step

def show_details(self, step_index, title, content):
405    def show_details(self, step_index, title, content):
406        details_panel = self.timeline_panel.children[2 * step_index + 1]
407        out = widgets.Output(layout={"width": "100%"})
408        details_panel.children = (out,)
409
410        if "step-details-hide" in details_panel._dom_classes:
411            details_panel.remove_class("step-details-hide")
412
413        html_str = """
414        <div class="content-wpr">
415            <div class="content">{content}</div>
416        </div>
417        """.format(
418            content=content
419        )
420
421        out.append_display_data(HTML(html_str))
def on_pass(self, btn):
423    def on_pass(self, btn):
424        """Render the pass view on the clicking of the button
425        on the left of the pass panel
426        Args:
427            btn (ButtonWithValue): Button which contains the
428                                   pass index"""
429        step_index = int(btn.value)
430        step = self.transpilation_sequence.steps[step_index]
431
432        # Toggle detailed view:
433        details_panel = self.timeline_panel.children[2 * step_index + 1]
434        if "step-details-hide" not in details_panel._dom_classes:
435            details_panel.add_class("step-details-hide")
436            btn.icon = "caret-right"
437        else:
438            details_panel.remove_class("step-details-hide")
439            btn.icon = "caret-down"
440
441        if len(details_panel.children) == 0:
442            # First time to expand this panel
443            tab_titles = ["Circuit", "Property Set", "Logs", "Help"]
444            children = [
445                widgets.VBox(layout={"width": "100%"}),
446                widgets.HBox(
447                    children=[
448                        widgets.GridBox(
449                            [],
450                            layout={
451                                "width": "50%",
452                                "padding": "5px",
453                                "grid_template_columns": "repeat(2, 50%)",
454                            },
455                        ),
456                        widgets.Output(layout={"width": "50%"}),
457                    ],
458                    layout={"width": "100%"},
459                ),
460                widgets.Output(layout={"width": "100%"}),
461                widgets.Output(layout={"width": "100%"}),
462            ]
463
464            tab = widgets.Tab(model_id=str(step_index), layout={"width": "100%"})
465            tab.children = children
466            for idx, name in enumerate(tab_titles):
467                tab.set_title(idx, name)
468
469            details_panel.children = (tab,)
470            dag = self._get_step_dag(step)
471
472            # this is for the default one
473            # when a tab is clicked, we would need to show something right
474
475            # vars : tab, dag, index that's it
476            # img_thread = Thread(target=self._load_img_view, args=[dag, tab, step_index])
477
478            # img_thread.start()
479            self._load_img_view(dag, tab, step_index)
480
481            tab.observe(self.on_tab_clicked)
482
483            children[1].children[0].add_class("property-set")
484            children[1].children[1].add_class("property-items")
485
486            if len(self._get_step_property_set(step)) == 0:
487                tab.add_class("no-props")
488            if len(step.logs) == 0:
489                tab.add_class("no-logs")

Render the pass view on the clicking of the button on the left of the pass panel Args: btn (ButtonWithValue): Button which contains the pass index

def on_tab_clicked(self, change):
518    def on_tab_clicked(self, change):
519        """Callback to update the information on the debugger view when a
520           tab is clicked from properties, logs or docs
521
522        Args:
523            change (dict): Dict describing the state of the
524                           widget
525        """
526        if change["type"] == "change" and change["name"] == "selected_index":
527            tabs = change.owner
528            step_index = int(tabs.model_id)
529            # get the transpiler pass which is displayed
530            step = self.transpilation_sequence.steps[step_index]
531
532            if change["new"] == 1:
533                properties_panel = tabs.children[1].children[0]
534
535                # If content is already rendered, do nothing:
536                if (
537                    isinstance(tabs.children[1].children[0], widgets.Label)
538                    or len(properties_panel.children) > 0
539                ):
540                    return
541
542                _property_set = self._get_step_property_set(step)
543                if len(_property_set) > 0:
544                    properties_panel.add_class("table")
545                    properties_panel.layout = {
546                        "width": "50%",
547                        "padding": "5px",
548                        "grid_template_columns": "repeat(2, 50%)",
549                        "height": str(33 * len(_property_set)) + "px",
550                    }
551
552                    for prop_name in _property_set:
553                        property_ = _property_set[prop_name]
554                        prop_widget = widgets.Label(value=property_.name)
555                        if property_.prop_type not in (int, float, bool, str):
556                            txt = (
557                                "(dict)"
558                                if isinstance(property_.value, defaultdict)
559                                else "(" + property_.prop_type.__name__ + ")"
560                            )
561                            prop_label = widgets.Label(txt, layout={"width": "80%"})
562                            prop_button = ButtonWithValue(
563                                value=None, description="...", layout={"width": "20%"}
564                            )
565                            prop_button.on_click(self.on_property)
566                            prop_box = widgets.HBox(
567                                [prop_label, prop_button], layout={"width": "100%"}
568                            )
569                        else:
570                            prop_box = widgets.Label(value=str(property_.value))
571
572                        if step.property_set_index == step.index:
573                            if property_.state != "updated":
574                                prop_widget.add_class(property_.state)
575                            prop_box.add_class(property_.state)
576
577                        index = len(properties_panel.children)
578                        properties_panel.children = properties_panel.children + (
579                            prop_widget,
580                            prop_box,
581                        )
582
583                        if property_.prop_type not in (int, float, bool, str):
584                            properties_panel.children[index + 1].children[1].value = (
585                                str(step.index) + "," + property_.name
586                            )
587                        else:
588                            properties_panel.children[index + 1].value = str(
589                                property_.value
590                            )
591
592                    prop_list = list(properties_panel.children)
593                    for p_id in range(int(len(prop_list) / 2)):
594                        if (
595                            properties_panel.children[2 * p_id].value
596                            not in _property_set
597                        ):
598                            properties_panel.children[2 * p_id].add_class("not-exist")
599                            properties_panel.children[2 * p_id + 1].add_class(
600                                "not-exist"
601                            )
602                        else:
603                            properties_panel.children[2 * p_id].remove_class(
604                                "not-exist"
605                            )
606                            properties_panel.children[2 * p_id + 1].remove_class(
607                                "not-exist"
608                            )
609                else:
610                    message = widgets.Label(value="Property set is empty!")
611                    message.add_class("message")
612                    tabs.children[1].children = (message,)
613
614            elif change["new"] == 2:
615                # for the Logs tab of the debugger
616
617                # If content is already rendered, do nothing:
618                if len(tabs.children[2].outputs) > 0:
619                    return
620
621                logs = step.logs
622                if len(logs) > 0:
623                    html_str = '<div class="logs-wpr">'
624                    for entry in logs:
625                        html_str = (
626                            html_str
627                            + "<pre class='date'>{0}</pre>\
628                                <pre class='level {1}'>[{1}]\
629                                </pre><pre class='log-entry {1}'>\
630                                {2}</pre>".format(
631                                datetime.fromtimestamp(entry.time).strftime(
632                                    "%H:%M:%S.%f"
633                                )[:-3],
634                                entry.levelname,
635                                entry.msg % entry.args,
636                            )
637                        )
638                    html_str = html_str + "</div>"
639                    tabs.children[2].append_display_data(HTML(html_str))
640                else:
641                    html_str = '<div class="message">This pass does not \
642                        write any log messages!</div>'
643                    tabs.children[2].append_display_data(HTML(html_str))
644
645            elif change["new"] == 3:
646                # this is the docs tab
647
648                # If content is already rendered, do nothing:
649                if len(tabs.children[3].outputs) > 0:
650                    return
651
652                html_str = '<pre class="help">' + step.docs + "</pre>"
653                html_str = (
654                    html_str
655                    + '<div class="help-header"><span style="color: #e83e8c;">'
656                    + step.name
657                    + '</span>.run(<span style="color: #0072c3;">dag</span>)</div>'
658                )
659                html_str = (
660                    html_str + '<pre class="help">' + step.run_method_docs + "</pre>"
661                )
662                tabs.children[3].append_display_data(HTML(html_str))

Callback to update the information on the debugger view when a tab is clicked from properties, logs or docs

Args: change (dict): Dict describing the state of the widget

def on_diff(self, change):
664    def on_diff(self, change):
665        """Callback to handle the toggling of circuit diff in the debugger
666
667        Args:
668            change (dict): Dict containing the current state of the
669                           widget associated with the callback
670        """
671        if (
672            change["type"] == "change"
673            and isinstance(change["new"], dict)
674            and "value" in change["new"]
675        ):
676            chk = change.owner
677            _, step_index_str = chk.model_id.split(":")
678            step_index = int(step_index_str)
679
680            details_panel = self.timeline_panel.children[2 * int(step_index) + 1]
681            img_wpr = details_panel.children[0].children[0].children[1]
682            img_wpr.outputs = []
683            img_wpr.append_display_data(
684                HTML(get_spinner_html())
685            )  # to get the loader gif
686
687            if change["new"]["value"]:
688                if step_index > 0:
689                    prev_dag = self._get_step_dag(
690                        self.transpilation_sequence.steps[step_index - 1]
691                    )
692                    prev_circ = dag_to_circuit(prev_dag)
693                else:
694                    prev_circ = None
695
696                curr_dag = self._get_step_dag(
697                    self.transpilation_sequence.steps[step_index]
698                )
699                curr_circ = dag_to_circuit(curr_dag)
700
701                # okay so this is basically the circuit diff class
702
703                fully_changed, disp_circ = CircuitComparator.compare(
704                    prev_circ, curr_circ
705                )
706
707                if fully_changed:
708                    chk.description = "Circuit changed fully"
709                    chk.disabled = True
710
711                suffix = "diff_" + str(step_index)
712            else:
713                if not chk.disabled:
714                    dag = self._get_step_dag(
715                        self.transpilation_sequence.steps[step_index]
716                    )
717                    disp_circ = dag_to_circuit(dag)
718                    suffix = "after_pass_" + str(step_index)
719
720            # here, qasm and qpy need the without diff circuits
721            img_html = view_circuit(disp_circ, suffix)
722            img_wpr.outputs = []
723            img_wpr.append_display_data(HTML(img_html))

Callback to handle the toggling of circuit diff in the debugger

Args: change (dict): Dict containing the current state of the widget associated with the callback

def on_property(self, btn):
725    def on_property(self, btn):
726        """Callback to handle the toggling of properties in the debugger
727
728        Args:
729            btn (ButtonWithValue): Button associated with the callbacks
730        """
731        warnings.filterwarnings(
732            "ignore",
733            message="Back-references to from Bit instances to \
734            their containing Registers have been deprecated. \
735            Instead, inspect Registers to find their contained Bits.",
736        )
737
738        step_index, property_name = btn.value.split(",")
739
740        details_panel = self.timeline_panel.children[2 * int(step_index) + 1]
741        prop_details_panel = details_panel.children[0].children[1].children[1]
742
743        step = self.transpilation_sequence.steps[int(step_index)]
744        property_set = self._get_step_property_set(step)
745        property_ = property_set[property_name]
746
747        html_str = '<table style="width: 100%">'
748        html_str = (
749            html_str
750            + '<thead><tr><th colspan="'
751            + ("2" if isinstance(property_.value, defaultdict) else "1")
752            + '">'
753            + property_name
754            + "</th></tr></thead>"
755        )
756        if property_name == "block_list":
757            for val in property_.value:
758                v_arr = []
759                for node in val:
760                    qargs = ", ".join(
761                        [
762                            qarg.register.name
763                            + "<small>["
764                            + str(qarg.index)
765                            + "]</small>"
766                            for qarg in node.qargs
767                        ]
768                    )
769                    v_arr.append(
770                        "<strong>" + node.name + "</strong>" + "(" + qargs + ")"
771                    )
772                html_str = html_str + "<tr><td>" + " - ".join(v_arr) + "</td></tr>"
773        elif property_name == "commutation_set":
774            for key, val in property_.value.items():
775                key_str = ""
776                if isinstance(key, tuple):
777                    qargs = ", ".join(
778                        [
779                            qarg.register.name
780                            + "<small>["
781                            + str(qarg.index)
782                            + "]</small>"
783                            for qarg in key[0].qargs
784                        ]
785                    )
786                    key_str = (
787                        "(<strong>"
788                        + (key[0].name if key[0].name is not None else "")
789                        + "</strong>("
790                        + qargs
791                        + "), "
792                    )
793                    key_str = (
794                        key_str
795                        + key[1].register.name
796                        + "<small>["
797                        + str(key[1].index)
798                        + "]</small>"
799                        + ")"
800                    )
801                else:
802                    key_str = (
803                        key.register.name + "<small>[" + str(key.index) + "]</small>"
804                    )
805
806                value_str = ""
807                if isinstance(val, list):
808                    value_str = value_str + "["
809                    for nodes in val:
810                        if isinstance(nodes, list):
811                            nodes_arr = []
812                            for node in nodes:
813                                if node.type == "op":
814                                    qargs = ", ".join(
815                                        [
816                                            qarg.register.name
817                                            + "<small>["
818                                            + str(qarg.index)
819                                            + "]</small>"
820                                            for qarg in node.qargs
821                                        ]
822                                    )
823                                    node_str = (
824                                        "<strong>"
825                                        + (node.name if node.name is not None else "")
826                                        + "</strong>"
827                                        + "("
828                                        + qargs
829                                        + ")"
830                                    )
831                                else:
832                                    node_str = (
833                                        node.type.upper()
834                                        + "(wire="
835                                        + node.wire.register.name
836                                        + "<small>["
837                                        + str(node.wire.index)
838                                        + "]</small>)"
839                                    )
840
841                                nodes_arr.append(node_str)
842
843                            value_str = (
844                                value_str + "[" + (", ".join(nodes_arr)) + "]<br>"
845                            )
846                    value_str = value_str + "]"
847
848                html_str = (
849                    html_str
850                    + '<tr><td style="width:50%">'
851                    + key_str
852                    + "</td><td><pre>"
853                    + value_str
854                    + "</pre></td></tr>"
855                )
856        else:
857            html_str = (
858                html_str
859                + "<tr><td><pre>"
860                + html.escape(str(property_.value))
861                + "</pre></td></tr>"
862            )
863        html_str = html_str + "</table>"
864
865        prop_details_panel.outputs = []
866        prop_details_panel.append_display_data(HTML(html_str))

Callback to handle the toggling of properties in the debugger

Args: btn (ButtonWithValue): Button associated with the callbacks

Inherited Members
ipywidgets.widgets.widget_box.Box
box_style
ipywidgets.widgets.domwidget.DOMWidget
add_class
remove_class
ipywidgets.widgets.widget.Widget
widgets
widget_types
close_all
on_widget_constructed
handle_comm_opened
get_manager_state
get_view_spec
comm
keys
open
model_id
close
send_state
get_state
set_state
send
on_msg
on_displayed
add_traits
notify_change
hold_sync
ipywidgets.widgets.widget.LoggingHasTraits
log
traitlets.traitlets.HasTraits
setup_instance
cross_validation_lock
hold_trait_notifications
on_trait_change
observe
unobserve
unobserve_all
set_trait
class_trait_names
class_traits
class_own_traits
has_trait
trait_has_value
trait_values
trait_defaults
trait_names
traits
trait_metadata
class_own_trait_events
trait_events