diff --git a/VS2N.py b/VS2N.py index 9a63eae6e93288d0df661b65858dc0677cfc6295..39a92a2d6ebe6befa0c107d7d0ba5c85c39f42d6 100755 --- a/VS2N.py +++ b/VS2N.py @@ -288,7 +288,7 @@ def processing(): app_vis = dash.Dash( __name__, requests_pathname_prefix='/vis/', - external_stylesheets=[dbc.themes.BOOTSTRAP], + external_stylesheets=[dbc.themes.BOOTSTRAP,dbc.icons.FONT_AWESOME], suppress_callback_exceptions=True, title="VS2N") diff --git a/src/Global_Var.py b/src/Global_Var.py index ce8d739856f5660c0e50e2576f6bed3570d7c9cc..5048ce8d4cec69a698d0b1554bf259899f1a50ea 100755 --- a/src/Global_Var.py +++ b/src/Global_Var.py @@ -9,7 +9,7 @@ from config import config import numpy as np import pymongo import socket -import time +import traceback class Global_Var(): @@ -84,8 +84,8 @@ class Global_Var(): self.client.server_info() # will throw an exception if mongodb is not detected self.db = self.client.list_database_names() return 1 - except Exception as e: - print("mongoConnect:" + str(e)) + except Exception: + print("mongoConnect:" + traceback.format_exc()) return 0 def checkExistance(self, app, component): @@ -175,12 +175,13 @@ class Global_Var(): return x, y - def createHeatMap(self, x, y, data, depth): + def createHeatMap(self, x, y, data, depth, rotation): """ Create a heatmap from a set of synapses values . Args: x , y (int): shape 2D dimensions data (Array): synapses values + rotation: if to rotate the heatmap 90° Returns: Array: Heatmap vector @@ -189,22 +190,27 @@ class Global_Var(): heatmap = np.zeros((x, y)) heatmap[:] = -1 data = data.to_numpy() - if(depth == 0): # single depth for d in data: - heatmap[int(d[0])][int(d[1])] = d[3] + if rotation: + heatmap[int(d[0])][int(d[1])] = d[3] + else: + heatmap[int(d[1])][int(d[0])] = d[3] else: # multiple dimensions for d in data: if(d[2] == 0): #if(heatmap[int(d[0])][int(d[1])] == -1): - heatmap[int(d[0])][int(d[1])] = d[3] + if rotation: + heatmap[int(d[0])][int(d[1])] = d[3] + else: + heatmap[int(d[1])][int(d[0])] = d[3] #else: # heatmap[int(d[0])][int(d[1])] = np.mean(heatmap[int(d[0])][int(d[1])]+d[3]) return heatmap - except Exception as e: - print("createHeatMap: "+str(e)) + except Exception: + print("createHeatMap: "+ traceback.format_exc()) def createVerticalHeatMap(self, data): """ Create a vertical heat map from a given data array . @@ -279,8 +285,8 @@ class Global_Var(): if not self.config.DEBUG: self.sparkSession.sparkContext.setLogLevel("Error") return self.sparkSession.version - except Exception as e: - print("createSparkSession:" + str(e)) + except Exception: + print("createSparkSession:" + traceback.format_exc()) return "" diff --git a/src/Modules/General/layout.py b/src/Modules/General/layout.py index 8809379ed862aa8bb94087dd3461cb58eba32ca9..a73c001478172f6c1e51acaa7074f32177d09459 100755 --- a/src/Modules/General/layout.py +++ b/src/Modules/General/layout.py @@ -308,10 +308,10 @@ class layout(layoutOp): dcc.Tabs(tabs, id="tabs"), # Control Layout dbc.Row([ - html.Div([dbc.Button("<", id="btn-back", className="btn btn-default", style={"marginTop": "12px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"}), + html.Div([dbc.Button(html.I(className="fa-solid fa-angle-left"), id="btn-back", className="btn btn-default", style={"marginTop": "12px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"}), dbc.Button("Start", id="btnControle", className="btn btn-success", style={ "marginTop": "12px", "marginLeft": "5px", "width": "100px", "fontWeight": "500"}), - dbc.Button(">", id="btn-next", className="btn btn-default", style={"marginTop": "12px", "marginLeft": "5px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"})], className="col-md-4 col-sm-12 col-lg-4"), + dbc.Button(html.I(className="fa-solid fa-angle-right"), id="btn-next", className="btn btn-default", style={"marginTop": "12px", "marginLeft": "5px", "fontWeight": "500", "backgroundColor": "rgb(68, 71, 99)"})], className="col-md-4 col-sm-12 col-lg-4"), html.Div([ html.Div([ html.Span("Update Speed (s)", diff --git a/src/Modules/Neuron/callbacks.py b/src/Modules/Neuron/callbacks.py index 96ea315b799782e9641aa65a8be94f880c6eb771..1db92ee0cd253cc788e2632e534aec3fb59822ec 100755 --- a/src/Modules/Neuron/callbacks.py +++ b/src/Modules/Neuron/callbacks.py @@ -360,7 +360,7 @@ class callbacks(callbacksOp): color="#28a745", style={"marginLeft": "10px","marginRight": "10px"} ), - html.Div("−", className="btn-danger btn-circle", style={"fontWeight": "500","fontSize":"16px"}, id={"index": str(index), "type": "DeleteComponent"}),html.Div([html.Span(layer+" N: "+neuron, style={"fontSize": "13px", "paddingTop": "10px", "marginRight": "10px"}, className="badge alert-info")], className="d-flex", style={"direction": "rtl", "width": "100%"})], className="d-flex", style={"height": "35px"}), + html.Button(html.I(className="fa-solid fa-trash"), style={"fontWeight": "500","fontSize":"16px", "marginLeft": "10px","color":"red","backgroundColor":"#00110000","border":"0"}, id={"index": str(index), "type": "DeleteComponent"}),html.Div([html.Span(layer+" N: "+neuron, style={"fontSize": "13px", "paddingTop": "10px", "marginRight": "10px"}, className="badge alert-info")], className="d-flex", style={"direction": "rtl", "width": "100%"})], className="d-flex", style={"height": "35px"}), dcc.Graph(id={"index": str(index), "type": "FreqGraph", "layer": layer, "neuron": neuron}, style={ "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False}), ], className="col-lg-4 col-sm-12 col-xs-12" if(g.finalLabels != None) else "col-lg-6 col-sm-12 col-xs-12"), @@ -387,7 +387,7 @@ class callbacks(callbacksOp): color="#28a745", style={"marginLeft": "10px"})], className="row", style={"height": "35px"}), dcc.Graph(id={"index": str(index), "type": "AccuracyGraph", "layer": layer, "neuron": neuron}, style={ - "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False})], className="col-lg-4 col-sm-6 col-xs-6" if(g.finalLabels != None) else "", style={} if(g.finalLabels != None) else {'display': 'none'})],className="row")], style={"background": "rgb(242, 248, 255)", "paddingBottom": "10px", "marginBottom": "10px", "margin": "5px", "borderRadius": "10px"}, id="VisComponent"+str(index)) + "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False})], className="col-lg-4 col-sm-6 col-xs-6" if(g.finalLabels != None) else "", style={} if(g.finalLabels != None) else {'display': 'none'})],className="row")], style={"background": "rgb(242, 248, 255)", "paddingBottom": "10px", "marginBottom": "10px", "padding": "5px", "borderRadius": "10px"}, id="VisComponent"+str(index)) def addSpaceHolder(app): """ Adds a space holder area when no graphs are selected. diff --git a/src/Modules/Neuron/layout.py b/src/Modules/Neuron/layout.py index 1ac5a3c2d43b0a3906ac47d4b9f190e9beb4b786..81c30ab51ce42e84096259e8b988addb93022dfc 100755 --- a/src/Modules/Neuron/layout.py +++ b/src/Modules/Neuron/layout.py @@ -76,7 +76,7 @@ class layout(layoutOp): options=[], multi=False, style={'width': '150px', "marginLeft": "10px", "textAlign": "start"}), - dbc.Button("+", id="AddComponentNeuron", n_clicks=0, style={ + dbc.Button(html.I(className="fa-solid fa-plus"), id="AddComponentNeuron", n_clicks=0, style={ "fontWeight": "500", "marginLeft": "20px", "height": "36px", "backgroundColor": "rgb(68, 71, 99)", "borderColor": "rgb(68, 71, 99)"}), html.Div(id='clear-Neuron', children="False", style={'display': 'none'}), html.Div(id='display-Neuron', children="False", style={'display': 'none'}) ], className="d-flex"), html.Div(id={'type': "GraphsAreaNeuron"}, children=[html.Div(id={'type': "OutputNeurons"}, children=[dcc.Graph(id="SpikePerNeuronFreq", figure=self.SpikePerNeuron3D(self.g), config={"displaylogo": False}, className="col-6"), diff --git a/src/Modules/Synapse/callbacks.py b/src/Modules/Synapse/callbacks.py index 739dca484918c2832d4cac5e31e45024ab895862..5bc3e670faa9e4770f7a2a3f515a1bbcc19752fe 100755 --- a/src/Modules/Synapse/callbacks.py +++ b/src/Modules/Synapse/callbacks.py @@ -41,9 +41,8 @@ class callbacks(callbacksOp): [Input("AddComponentSynapse", "n_clicks"), Input( {"type": "DeleteComponent", "index": ALL}, "n_clicks")], [State({"type": "GraphsAreaSynapse"}, "children"), State({"type": "GlobalHeatMapAreaSynapse"}, "children"), - State("LayerFilterSynapse", "value"), State( - "NeuronFilterSynapse", "value")]) - def InsertRemoveNewElement(addButtonClick, deleteButtonsClick, graphsArea, GlobalHeatmap, selectedLayer, selectedNeuron): + State("LayerFilterSynapse", "value"), State("NeuronFilterSynapse", "value"),State("heatmapEditInfo", "data")]) + def InsertRemoveNewElement(addButtonClick, deleteButtonsClick, graphsArea, GlobalHeatmap, selectedLayer, selectedNeuron, heatmapEditInfo): """ Insert of remove element. Args: @@ -53,6 +52,7 @@ class callbacks(callbacksOp): GlobalHeatmap (list): list of GLobalHeatma component children selectedLayer (String): name of the selected layer selectedNeuron (int): id of selected neuron + heatmapEditInfo (Array): heatmap edit stats Returns: the updated content of 'GraphsAreaSynapse' and 'GlobalHeatMapAreaSynapse' @@ -80,7 +80,7 @@ class callbacks(callbacksOp): data = self.getGlobalSynapseWeights(g,selectedLayer) super.globalHeatMap[selectedLayer] = data - return [graphsArea, self.AddGlobalHeatmap(g, selectedLayer, data) if not data.empty else []] + return [graphsArea, self.AddGlobalHeatmap(g, selectedLayer, data, heatmapEditInfo) if not data.empty else []] else: if(context[0]['prop_id'] != "."): # Delete item @@ -167,8 +167,8 @@ class callbacks(callbacksOp): [Input("display-Synapse", "children")], [State("v-step", "children"), State("interval", "value"), State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": "SynapseFreqGraph"}, "id"), State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": 'Synapse-frequency-switch'}, 'on'), - State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": 'Synapse-HeatMap-switch'}, 'on')]) - def processSynapseData(displayContent, sliderValue, updateInterval, selectedItem, isOnSynapseFreqGraph, isOnHeatmap): + State({"index": MATCH, "layer": MATCH, "neuron": MATCH, "type": 'Synapse-HeatMap-switch'}, 'on'),State("heatmapEditInfo", "data")]) + def processSynapseData(displayContent, sliderValue, updateInterval, selectedItem, isOnSynapseFreqGraph, isOnHeatmap, heatmapEditInfo): """ Create two graphs using neuron synapses ('SynapseFreqGraph' and 'Heatmap') Args: @@ -178,6 +178,7 @@ class callbacks(callbacksOp): selectedItem (list): selected neuron isOnSynapseFreqGraph (bool): whether SynapseFreq graph is active or not isOnHeatmap (bool): whether Heatmap graph is active or not + heatmapEditInfo (Array): heatmap edit stats Raises: PreventUpdate: in case we don't want to update the content we rise this execption @@ -201,7 +202,7 @@ class callbacks(callbacksOp): output = [self.synapseFreqDrawGraph(g, selectedItem["index"], data, super.xAxisSynapseFreqGraph[selectedItem["index"]], super.xAxisLabel[selectedItem["index"]], super.yAxisSynapseFreqGraph, super.HeatMapSynapseFreqGraph, isOnSynapseFreqGraph), - self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap)] + self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatmapEditInfo)] return output else: @@ -217,7 +218,7 @@ class callbacks(callbacksOp): output = [self.synapseFreqDrawGraph(g, selectedItem["index"], data, super.xAxisSynapseFreqGraph[selectedItem["index"]], super.xAxisLabel[selectedItem["index"]], super.yAxisSynapseFreqGraph, super.HeatMapSynapseFreqGraph, isOnSynapseFreqGraph), - self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap)] + self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatmapEditInfo)] return output else: @@ -237,7 +238,7 @@ class callbacks(callbacksOp): output = [self.synapseFreqDrawGraph(g, selectedItem["index"], data, super.xAxisSynapseFreqGraph[selectedItem["index"]], super.xAxisLabel[selectedItem["index"]], super.yAxisSynapseFreqGraph, super.HeatMapSynapseFreqGraph, isOnSynapseFreqGraph), - self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap)] + self.heatMap(g, selectedItem["index"], data, super.heatmaps, isOnHeatmap, heatmapEditInfo)] return output else: @@ -266,7 +267,34 @@ class callbacks(callbacksOp): return [isTabOpen] except Exception: print("GlobalHeatMapTab: "+ traceback.format_exc()) + + try: + # Callback to handle heatmap flipping if needed + @app.callback([Output("heatmapEditInfo", "data")],[Input('FlipHeatmapH', 'n_clicks'),Input('FlipHeatmapV', 'n_clicks'),Input('RotateHeatmap', 'n_clicks'),State("heatmapEditInfo", "data")]) + def HeatmapFlip(FlipHeatmapH, FlipHeatmapV, RotateHeatmap, heatmapEditInfo): + """ Function called when flip button clicked + + Args: + FlipHeatmapH (int): number of clicks on the FlipHeatmapH button + FlipHeatmapV (int): number of clicks on the FlipHeatmapV button + RotateHeatmap (int): number of clicks on the RotateHeatmap button + heatmapEditInfo (Array): heatmap edit stats + + Returns: + if global heatmaps tab should be opened or closed + """ + context = dash.callback_context.triggered + if "FlipHeatmapH" in context[0]['prop_id']: + heatmapEditInfo[0] = not heatmapEditInfo[0] + elif "FlipHeatmapV" in context[0]['prop_id']: + heatmapEditInfo[1] = not heatmapEditInfo[1] + else: + heatmapEditInfo[2] = not heatmapEditInfo[2] + return [heatmapEditInfo] + except Exception: + print("HeatmapFlip: "+ traceback.format_exc()) + try: # ------------------------------------------------------------ @@ -293,7 +321,7 @@ class callbacks(callbacksOp): id={"index": str(index), "layer": layer, "neuron": neuron, "type": "Synapse-frequency-switch"}, on='True', size=25, color="#28a745", style={"marginLeft": "10px"}), - html.Div("−", className="btn-danger btn-circle", style={"fontWeight": "500","fontSize":"16px", "marginLeft": "10px"}, id={"index": str(index), "type": "DeleteComponent"}), + html.Button(html.I(className="fa-solid fa-trash"), style={"fontWeight": "500","fontSize":"16px", "marginLeft": "10px","color":"red","backgroundColor":"#00110000","border":"0"}, id={"index": str(index), "type": "DeleteComponent"}), html.Div([html.Span(layer+" N: "+neuron, style={"fontSize": "13px", "paddingTop": "10px", "marginRight": "10px"}, className="badge alert-info")], className="d-flex", style={"direction": "rtl", "width": "100%"})], className="d-flex", style={"height": "35px"}), dcc.Graph(id={"index": str(index), "type": "SynapseFreqGraph", "layer": layer, "neuron": neuron}, style={ "width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False}), @@ -309,7 +337,7 @@ class callbacks(callbacksOp): )], className="d-flex", style={"height": "35px", "direction": "rtl", "paddingRight": "30px"}), dcc.Graph(id={"index": str(index), "type": "Heatmap", "layer": layer, "neuron": neuron}, style={"width": "100%", "height": "290px"}, className="col-12", animate=False, config={"displaylogo": False}), ], className="col-lg-6 col-sm-12 col-xs-12") - ], className="row", style={"margin": "10px"}) + ], className="row", style={"padding": "10px"}) ], style={"background": "rgb(242, 248, 255)", "paddingBottom": "10px", "marginBottom": "10px", "margin": "5px", "borderRadius": "10px"}, @@ -331,13 +359,14 @@ class callbacks(callbacksOp): id="spaceHolder") - def AddGlobalHeatmap(self, g, selectedLayer, data): + def AddGlobalHeatmap(self, g, selectedLayer, data, heatmapEditInfo): """ Add global heatmap for selected layer. Args: g (Global_Var): reference to access global variables selectedLayer (String): layer name data (dataframe): synapses weights of all neurons in the layer + heatmapEditInfo (Array): heatmap edit stats Returns: html component that contains the global heatmaps @@ -368,11 +397,14 @@ class callbacks(callbacksOp): zmin=0, zmax=1, z=g.createHeatMap( - heatMapX, heatMapY, data[data.To == index],depth), + heatMapX, heatMapY, data[data.To == index],depth, heatmapEditInfo[2]), colorscale='jet', name=str(index)), row=xx, col=yy) - fig.update_yaxes(autorange="reversed", row=xx, col=yy) + if heatmapEditInfo[0]: + fig.update_xaxes(autorange="reversed") + if heatmapEditInfo[1]: + fig.update_yaxes(autorange="reversed") fig.update_layout(height=80, width=80) index += 1 @@ -545,7 +577,7 @@ class callbacks(callbacksOp): except Exception: print("synapseFreqDrawGraph: "+ traceback.format_exc()) - def heatMap(self, g, index, data, heatmaps, isOn): + def heatMap(self, g, index, data, heatmaps, isOn, heatmapEditInfo): """ Create heatmap for selected neuron synapses. Args: @@ -554,6 +586,7 @@ class callbacks(callbacksOp): data (list): data to be added to the graph heatmaps (list): list of heatmaps created isOn (bool): whether this graph is active or not + heatmapEditInfo (Array): heatmap edit stats Returns: heatmap content (data and layout) @@ -563,8 +596,8 @@ class callbacks(callbacksOp): layout = go.Layout( margin={'l': 0, 'r': 0, 't': 0, 'b': 25}, uirevision='no reset of zoom', - yaxis={"autorange": "reversed"}, - xaxis={'showticklabels': False,"ticks":"inside"}, + xaxis={'showticklabels': False,"ticks":"inside","autorange": "reversed"} if heatmapEditInfo[0] else {'showticklabels': False,"ticks":"inside"}, + yaxis={"autorange": "reversed"} if heatmapEditInfo[1] else {}, width=400, height=300, paper_bgcolor= "rgba(255, 255, 255,0)", plot_bgcolor= "rgba(255, 255, 255,0)") @@ -590,7 +623,7 @@ class callbacks(callbacksOp): print("heatMap1: "+ traceback.format_exc()) return {'data': [ go.Heatmap( - z=g.createHeatMap(heatMapX, heatMapY, pd.DataFrame(heatMapWithIndexs), depth), + z=g.createHeatMap(heatMapX, heatMapY, pd.DataFrame(heatMapWithIndexs), depth, heatmapEditInfo[2]), zmin=0, zmax=1, colorscale="jet")], diff --git a/src/Modules/Synapse/layout.py b/src/Modules/Synapse/layout.py index d73a80f1a61a7370131d41fb13a20ee7d5d66023..6b981d0f78a8ef8cc594865d5616c3de8b4b26bc 100755 --- a/src/Modules/Synapse/layout.py +++ b/src/Modules/Synapse/layout.py @@ -56,6 +56,7 @@ class layout(layoutOp): [ html.Div(id="synapse-vis", children=[ html.Div([ + dcc.Store(id="heatmapEditInfo",data=[False,False,False]), dcc.Dropdown( id='LayerFilterSynapse', options=[{'label': str(i), 'value': str(i)} for i in ( @@ -64,11 +65,14 @@ class layout(layoutOp): dcc.Dropdown( id='NeuronFilterSynapse', options=[], multi=False, style={'width': '150px', "marginLeft": "10px", "textAlign": "start"}), - dbc.Button("+", id="AddComponentSynapse", n_clicks=0, style={ + dbc.Button(html.I(className="fa-solid fa-plus"), id="AddComponentSynapse", n_clicks=0, style={ "fontWeight": "500", "marginLeft": "20px", "height": "36px", - "backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)"}) - ,html.Div(id='clear-Synapse',children="False", style={'display': 'none'}) - ,html.Div(id='display-Synapse',children="False", style={'display': 'none'}) + "backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)"}), + dbc.Button(html.I(className="fa-solid fa-left-right"), id="FlipHeatmapH", n_clicks=0, style={"fontWeight": "500", "marginLeft": "20px", "height": "36px","backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)", "color":"white"}), + dbc.Button(html.I(className="fa-solid fa-up-down"), id="FlipHeatmapV", n_clicks=0, style={"fontWeight": "500", "marginLeft": "20px", "height": "36px","backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)", "color":"white"}), + dbc.Button(html.I(className="fa-solid fa-rotate"), id="RotateHeatmap", n_clicks=0, style={"fontWeight": "500", "marginLeft": "20px", "height": "36px","backgroundColor":"rgb(68, 71, 99)","borderColor":"rgb(68, 71, 99)", "color":"white"}), + html.Div(id='clear-Synapse',children="False", style={'display': 'none'}), + html.Div(id='display-Synapse',children="False", style={'display': 'none'}) ], className="d-flex", style={"paddingBottom": "10px"}), dbc.Card( dbc.CardBody([dbc.Card([dbc.CardHeader( dbc.Button( "Layer HeatMap", color="none",