##############################################################################bl # MIT License # # Copyright (c) 2021 - 2024 Advanced Micro Devices, Inc. All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ##############################################################################el import sys import pandas as pd from dash import html, dash_table import plotly.express as px import colorlover from utils import schema from utils.utils import console_error pd.set_option( "mode.chained_assignment", None ) # ignore SettingWithCopyWarning pandas warning IS_DARK = True # TODO: Remove hardcoded in favor of class property ################## # HELPER FUNCTIONS ################## def filter_df(column, df, filt): filt_df = df if filt != []: filt_df = df.loc[df[schema.pmc_perf_file_prefix][column].astype(str).isin(filt)] return filt_df def multi_bar_chart(table_id, display_df): if table_id == 1604: nested_bar = {"NC": {}, "UC": {}, "RW": {}, "CC": {}} for index, row in display_df.iterrows(): nested_bar[row["Coherency"]][row["Xfer"]] = row["Avg"] if table_id == 1704: nested_bar = {"Read": {}, "Write": {}} for index, row in display_df.iterrows(): nested_bar[row["Transaction"]][row["Type"]] = row["Avg"] return nested_bar def discrete_background_color_bins(df, n_bins=5, columns="all"): bounds = [i * (1.0 / n_bins) for i in range(n_bins + 1)] if columns == "all": if "id" in df: df_numeric_columns = df.select_dtypes("number").drop(["id"], axis=1) else: df_numeric_columns = df.select_dtypes("number") else: df_numeric_columns = df[columns] df_max = df_numeric_columns.max().max() df_min = df_numeric_columns.min().min() ranges = [((df_max - df_min) * i) + df_min for i in bounds] styles = [] legend = [] for i in range(1, len(bounds)): min_bound = ranges[i - 1] max_bound = ranges[i] backgroundColor = colorlover.scales[str(n_bins)]["seq"]["Blues"][i - 1] color = "white" if i > len(bounds) / 2.0 else "inherit" for column in df_numeric_columns: styles.append( { "if": { "filter_query": ( "{{{column}}} >= {min_bound}" + ( " && {{{column}}} < {max_bound}" if (i < len(bounds) - 1) else "" ) ).format(column=column, min_bound=min_bound, max_bound=max_bound), "column_id": column, }, "backgroundColor": backgroundColor, "color": color, } ) legend.append( html.Div( style={"display": "inline-block", "width": "60px"}, children=[ html.Div( style={ "backgroundColor": backgroundColor, "borderLeft": "1px rgb(50, 50, 50) solid", "height": "10px", } ), html.Small(round(min_bound, 2), style={"paddingLeft": "2px"}), ], ) ) return (styles, html.Div(legend, style={"padding": "5px 0 5px 0"})) #################### # GRAPHICAL ELEMENTS #################### def build_bar_chart(display_df, table_config, barchart_elements, norm_filt): """ Read data into a bar chart. ID will determine which subtype of barchart. """ d_figs = [] # Insr Mix bar chart if table_config["id"] in barchart_elements["instr_mix"]: display_df["Avg"] = [ x.astype(int) if x != "" else int(0) for x in display_df["Avg"] ] df_unit = display_df["Unit"][0] d_figs.append( px.bar( display_df, x="Avg", y="Metric", color="Avg", labels={"Avg": "# of {}".format(df_unit.lower())}, height=400, orientation="h", ) ) # Multi bar chart elif table_config["id"] in barchart_elements["multi_bar"]: display_df["Avg"] = [ x.astype(int) if x != "" else int(0) for x in display_df["Avg"] ] df_unit = display_df["Unit"][0] nested_bar = multi_bar_chart(table_config["id"], display_df) # generate chart for each coherency for group, metric in nested_bar.items(): d_figs.append( px.bar( title=group, x=metric.values(), y=metric.keys(), labels={"x": df_unit, "y": ""}, text=metric.values(), orientation="h", height=200, ) .update_xaxes(showgrid=False, rangemode="nonnegative") .update_yaxes(showgrid=False) .update_layout(title_x=0.5) ) # L2 Cache per channel # elif table_config["id"] in barchart_elements["l2_cache_per_chan"]: # nested_bar = {} # channels = [] # for colName, colData in display_df.items(): # if colName == "Channel": # channels = list(colData.values) # else: # display_df[colName] = [ # x.astype(float) if x != "" and x != None else float(0) # for x in display_df[colName] # ] # nested_bar[colName] = list(display_df[colName]) # for group, metric in nested_bar.items(): # d_figs.append( # px.bar( # title=group[0 : group.rfind("(")], # x=channels, # y=metric, # labels={ # "x": "Channel", # "y": group[group.rfind("(") + 1 : len(group) - 1].replace( # "per", norm_filt # ), # }, # ).update_yaxes(rangemode="nonnegative") # ) # Speed-of-light bar chart elif table_config["id"] in barchart_elements["sol"]: display_df["Avg"] = [ x.astype(float) if x != "" else float(0) for x in display_df["Avg"] ] if table_config["id"] == 1701: # special layout for L2 Cache SOL d_figs.append( px.bar( display_df[display_df["Unit"] == "Pct"], x="Avg", y="Metric", color="Avg", range_color=[0, 100], labels={"Avg": "%"}, height=220, orientation="h", ).update_xaxes(range=[0, 110], ticks="inside", title="%") ) # append first % chart d_figs.append( px.bar( display_df[display_df["Unit"] == "Gb/s"], x="Avg", y="Metric", color="Avg", range_color=[0, 1638], labels={"Avg": "GB/s"}, height=220, orientation="h", ).update_xaxes(range=[0, 1638]) ) # append second GB/s chart else: key = "Avg" if table_config["id"] in [1101]: key = "Pct of Peak" d_figs.append( px.bar( display_df, x=key, y="Metric", color="Avg", range_color=[0, 100], labels={"Avg": "%"}, height=400, orientation="h", ).update_xaxes(range=[0, 110]) ) else: console_error("Table id %s. Cannot determine barchart type." % table_config["id"]) # update layout for each of the charts for fig in d_figs: fig.update_layout( margin=dict(l=50, r=50, b=50, t=50, pad=4), paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font={"color": "#ffffff"}, ) return d_figs def build_table_chart( display_df, table_config, original_df, display_columns, comparable_columns, decimal ): """ Read data into a DashTable """ d_figs = [] # build comlumns/header with formatting formatted_columns = [] for col in display_df.columns: if ( str(col).lower() == "pct" or str(col).lower() == "pop" or str(col).lower() == "percentage" ): formatted_columns.append( dict( id=col, name=col, type="numeric", format={"specifier": ".{}f".format(decimal)}, ) ) elif col in comparable_columns: formatted_columns.append( dict( id=col, name=col, type="numeric", format={"specifier": ".{}f".format(decimal)}, ) ) else: formatted_columns.append(dict(id=col, name=col, type="text")) # tooltip shows only on the 1st col for now if 'Tips' available table_tooltip = ( [ { column: { "value": ( str(row["Tips"]) if column == display_columns[0] and row["Tips"] else "" ), "type": "markdown", } for column, value in row.items() } for row in original_df.to_dict("records") ] if "Tips" in original_df.columns.values.tolist() else None ) # build data table with columns, tooltip, df and other properties d_t = dash_table.DataTable( id=str(table_config["id"]), sort_action="native", sort_mode="multi", columns=formatted_columns, tooltip_data=table_tooltip, # left-aligning the text of the 1st col style_cell_conditional=[ {"if": {"column_id": display_columns[0]}, "textAlign": "left"} ], # style cell style_cell={"maxWidth": "500px"}, # display style style_header=( { "backgroundColor": "rgb(30, 30, 30)", "color": "white", "fontWeight": "bold", } if IS_DARK else {} ), style_data=( { "backgroundColor": "rgb(50, 50, 50)", "color": "white", "whiteSpace": "normal", "height": "auto", } if IS_DARK else {} ), style_data_conditional=( [ {"if": {"row_index": "odd"}, "backgroundColor": "rgb(60, 60, 60)"}, { "if": {"column_id": "PoP", "filter_query": "{PoP} > 50"}, "backgroundColor": "#ffa90a", "color": "white", }, { "if": {"column_id": "PoP", "filter_query": "{PoP} > 80"}, "backgroundColor": "#ff120a", "color": "white", }, { "if": { "column_id": "Avg", "filter_query": "{Unit} = Pct && {Avg} > 50", }, "backgroundColor": "#ffa90a", "color": "white", }, { "if": { "column_id": "Avg", "filter_query": "{Unit} = Pct && {Avg} > 80", }, "backgroundColor": "#ff120a", "color": "white", }, ] if IS_DARK else [] ), # the df to display data=display_df.to_dict("records"), ) # print("DATA: \n", display_df.to_dict('records')) d_figs.append(d_t) return d_figs # print(d_t.columns)