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 {}
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
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
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.
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
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
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
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))
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
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
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
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