diff --git a/src/Modules/General/callbacks.py b/src/Modules/General/callbacks.py index 7a967ec0bd0e9b7344f787b1e085fbae8af34976..2b26e3e9d5d61b420f1cb0b801775efa5f61e21b 100755 --- a/src/Modules/General/callbacks.py +++ b/src/Modules/General/callbacks.py @@ -547,16 +547,16 @@ class callbacks(callbacksOp): # Callback to handle the 2D view spiking visualization @app.callback( Output("cytoscape-compound", "elements"),Output("cytoscape-compound", "layout"), Output('spikes_info', 'children'),Output('2DView-heatmap','figure'),Output("StoredData", "data"), - Input("vis-update", "n_intervals"),Input("v-step", "children"),Input('cytoscape-compound', 'tapNodeData'), + Input("vis-update", "n_intervals"),Input("v-step", "children"),Input('cytoscape-compound', 'mouseoverNodeData'), State("interval", "value"),State('cytoscape-compound', 'elements'),State("StoredData", "data")) - def animation2DView(visUpdateInterval,sliderValue, tapNodeData, updateInterval, elements, StoredData): + def animation2DView(visUpdateInterval,sliderValue, mouseOverNodeData, updateInterval, elements, StoredData): """ Function called each step to update the 2D view Args: sliderValue (int): value of the slider updateInterval (int): update Interval (s) visUpdateInterval : interval instance that will cause this function to be called each step - tapNodeData : contains data of the clickde node + mouseOverNodeData : contains data of the hovered node elements : nodes description StoredData : data stored for shared access heatmapData : heatmap data @@ -566,8 +566,8 @@ class callbacks(callbacksOp): if information tab should be opened or closed """ try: - if dash.callback_context.triggered[0]['prop_id'].split('.')[0] == "v-step": - elements = StoredData[0] + elements = StoredData[0] + if dash.callback_context.triggered[0]['prop_id'].split('.')[0] in ["v-step","vis-update"]: spikes = getSpike(int(sliderValue)*float(updateInterval), g.updateInterval,["Layer1"],True) if spikes: maxSpike = max([list(list(s.values())[0].values())[0] for s in spikes]) @@ -579,14 +579,14 @@ class callbacks(callbacksOp): if (element["data"]["id"] == "Layer1_"+str(list(list(spike.values())[0].keys())[0])) and (element["data"]["label"] == str(list(list(spike.values())[0].keys())[0])): element["data"]["spiked"] = round(list(list(spike.values())[0].values())[0] / maxSpike,2) element["data"]["spikes"] = list(list(spike.values())[0].values())[0] - StoredData[1][i] = list(list(spike.values())[0].values())[0] + StoredData[1][i] += list(list(spike.values())[0].values())[0] i+=1 - return [elements,{'name': 'grid','animate': False},[],{"data":[go.Heatmap(z = super.toMatrix(StoredData[1],5), zsmooth= 'best', colorscale= 'Portland')],"layout":{"yaxis":dict(autorange='reversed')}},StoredData] + return [elements,{'name': 'grid','animate': False},[],{"data":[go.Heatmap(z = super.toMatrix(StoredData[1],5), zsmooth= 'best', colorscale= 'Reds',showscale= False, hovertemplate=('Neuron: %{x}+%{y}*5 <br>''Spikes: %{z} <extra></extra>'))],"layout":{"xaxis":dict(showgrid = False, zeroline = False),"yaxis":dict(autorange = 'reversed',scaleanchor = 'x',showgrid = False, zeroline = False),"margin":{'l': 0, 'r': 0, 't': 10, 'b': 0},"uirevision":'no reset of zoom', "hoverlabel_align": 'right'}},StoredData] else: try: - return [elements,{'name': 'grid','animate': False},f"Neuron {tapNodeData['label']} : {tapNodeData['spikes']}", {"data":[go.Heatmap(z = super.toMatrix(StoredData[1],5), zsmooth= 'best', colorscale= 'Portland')],"layout":{"yaxis":dict(autorange='reversed')}},StoredData] + return [elements,{'name': 'grid','animate': False},f"Neuron {mouseOverNodeData['label']} : {mouseOverNodeData['spikes']}", {"data":[go.Heatmap(z = super.toMatrix(StoredData[1],5), zsmooth= 'best', colorscale= 'Reds',showscale= False, hovertemplate=('Neuron: %{x}+%{y}*5 <br>''Spikes: %{z} <extra></extra>'))],"layout":{"xaxis":dict(showgrid = False, zeroline = False),"yaxis":dict(autorange = 'reversed',scaleanchor = 'x',showgrid = False, zeroline = False),"margin":{'l': 0, 'r': 0, 't': 10, 'b': 0},"uirevision":'no reset of zoom', "hoverlabel_align": 'right'}},StoredData] except Exception as e: - return [elements,{'name': 'grid','animate': False},[],{"data":[go.Heatmap(z = super.toMatrix(StoredData[1],5), zsmooth= 'best', colorscale= 'Portland')],"layout":{"yaxis":dict(autorange='reversed')}},StoredData] + return [elements,{'name': 'grid','animate': False},[],{"data":[go.Heatmap(z = super.toMatrix(StoredData[1],5), zsmooth= 'best', colorscale= 'Reds',showscale= False, hovertemplate=('Neuron: %{x}+%{y}*5 <br>''Spikes: %{z} <extra></extra>'))],"layout":{"xaxis":dict(showgrid = False, zeroline = False),"yaxis":dict(autorange = 'reversed',scaleanchor = 'x',showgrid = False, zeroline = False),"margin":{'l': 0, 'r': 0, 't': 10, 'b': 0},"uirevision":'no reset of zoom', "hoverlabel_align": 'right'}},StoredData] except Exception as e: print("animation2DViewController:" + str(e)) diff --git a/src/Modules/General/layout.py b/src/Modules/General/layout.py index 8c861ff6fb0bedf79540b1cfcc0246cab57974f7..9f6d242b15f027b253018ce064b33f8074d0dcc3 100755 --- a/src/Modules/General/layout.py +++ b/src/Modules/General/layout.py @@ -42,20 +42,21 @@ class layout(layoutOp): # 2D view -------------------------------------------------------- - def generate2DView(self, Nodes, g): + def generate2DView(self, g): """ Generates a 2D View of the neural network Args: - Nodes (list): neurons list g (Global_Var): reference to access global variables """ + Nodes = [] # Create the neurones and layers for L in g.LayersNeuronsInfo: Nodes.append({'data': {'id': L["layer"], 'label': L["layer"], 'spiked': -1}}) for i in range(L["neuronNbr"]): - Nodes.append({'classes': 'neuron', 'data': {'id': L["layer"]+"_"+str(i), 'label': str(i), 'parent': L["layer"], 'spiked': 0.0, 'spikes': 0},'position': {'x': (i % 5) * 50, 'y': (i // 5) * 50}}) + Nodes.append({'classes': 'neuron', 'data': {'id': L["layer"]+"_"+str(i), 'label': str(i), 'parent': L["layer"], 'spiked': 0.0, 'spikes': 0},'position': {'x': (i % 5) * 70, 'y': (i // 5) * 70},'height': 20,'width': 20}) # Add connections + return Nodes def toMatrix(self, l,n): """ 1D array to 2D @@ -101,7 +102,7 @@ class layout(layoutOp): ] # Generate 2D View ------------------------------------------- - self.generate2DView(self.Nodes, self.g) + self.Nodes = self.generate2DView(self.g) # Tabs content ----------------------------------------------- info_vis = dbc.Card( @@ -124,13 +125,15 @@ class layout(layoutOp): html.Div([html.Div([ html.Div([ html.Div([ - html.Div([daq.PowerButton( - id="general-graph-switch", - on='True', - size=30, - color="#28a745", - style={"marginLeft": "10px"} - ), + html.Div([daq.PowerButton( + id="general-graph-switch", + on='True', + size=30, + color="#28a745", + style={"marginLeft": "10px"} + ), + html.P("Graphs: ", style={ + "textAlign": "start", "marginLeft": "10px", "marginTop": "4px"}), # Graphs filter dcc.Dropdown( id='GeneralGraphFilter', @@ -138,8 +141,10 @@ class layout(layoutOp): 'label': "Neurons potential", 'value': "Potentials"}], value=["Spikes","Synapses","Potentials"], multi=True, - style={"minWidth": "20%", "marginLeft": "10px", "textAlign": "start"}), + style={"minWidth": "20%", "marginLeft": "5px", "textAlign": "start"}), # Layers filter + html.P("Layers: ", style={ + "textAlign": "start", "marginLeft": "20px", "marginTop": "4px"}), dcc.Dropdown( id='GeneralLayerFilter', options=[{'label': str(i), 'value': str(i)} for i in ( @@ -147,28 +152,44 @@ class layout(layoutOp): value=[str(i) for i in ( i for i in self.g.Layer_Neuron if ("Input" not in i and "pool" not in i))], multi=True, - style={"minWidth": "20%", "marginLeft": "15px", "textAlign": "start"})], className="d-flex", style={"paddingLeft": "20px", 'width': '100%'}) + style={"minWidth": "20%","marginLeft": "5px", "textAlign": "start"})], className="d-flex", style={"paddingLeft": "20px", 'width': '100%'}) ], className="col-12") ], className="d-flex"), html.Div([dcc.Graph(id='general-graph', config={"displaylogo": False})])], className="col-lg-9 col-sm-12 col-xs-12" if(self.g.labelsExistance) else "col-lg-12 col-sm-12 col-xs-12"), html.Div([ html.Div([ - html.P("Inputs", style={ - "textAlign": "start", "marginLeft": "20px", "marginTop": "4px"}), daq.PowerButton( id="label-graph-switch", on='True', size=30, color="#28a745", - style={"marginLeft": "10px"} - )], className="d-flex"), + style={"marginLeft": "20px"} + ), + html.P("Inputs", style={"textAlign": "start", "marginLeft": "10px", "marginTop": "4px"})], className="d-flex"), dcc.Graph(id='label-graph', config={"displaylogo": False})], className="col-lg-3 col-sm-12 col-xs-12") if(self.g.labelsExistance) else []], className="row") ], style={"padding": "5px"})), label="General information", value="General information"), # 2D view dcc.Tab(dbc.Card( dbc.CardBody([ + html.Div([ + html.Div([ + daq.PowerButton( + id="2DView-global-switch", + on='True', + size=30, + color="#28a745", + style={"marginLeft": "20px"} + ), + html.P("Accumulated Spikes", style={ + "textAlign": "start", "marginLeft": "10px", "marginTop": "4px"})], className="d-flex"), + # Accumulated Spikes HeatMap + dcc.Graph(id='2DView-heatmap', config={"displaylogo": False} + ), + ], style={"textAlign": "start", "padding": "8px"}, className="col-lg-3 col-sm-12 col-xs-12") + , html.Div( - [dcc.Store(id="StoredData",data=[self.Nodes,[0 for n in self.Nodes if n["data"]["spiked"] != -1]]), + [ + dcc.Store(id="StoredData",data=[self.Nodes,[0 for n in self.Nodes if n["data"]["spiked"] != -1]]), cyto.Cytoscape( id='cytoscape-compound', layout={'name': 'grid','animate': False}, @@ -182,37 +203,49 @@ class layout(layoutOp): { 'selector': '[spiked <= 1.0]', 'style': { - 'background-color': 'rgb(70,227,70)', + 'background-color': 'rgb(227,70,70)', + 'height': 45, + 'width': 45 } }, { 'selector': '[spiked < 0.8]', 'style': { - 'background-color': 'rgb(100,227,100)' + 'background-color': 'rgb(227,100,100)', + 'height': 40, + 'width': 40 } }, { 'selector': '[spiked < 0.6]', 'style': { - 'background-color': 'rgb(130,227,130)' + 'background-color': 'rgb(227,130,130)', + 'height': 35, + 'width': 35 } }, { 'selector': '[spiked < 0.4]', 'style': { - 'background-color': 'rgb(160,227,160)' + 'background-color': 'rgb(227,160,160)', + 'height': 30, + 'width': 30 } }, { 'selector': '[spiked < 0.2]', 'style': { - 'background-color': 'rgb(190,227,190)' + 'background-color': 'rgb(227,190,190)', + 'height': 25, + 'width': 25 } }, { 'selector': '[spiked = 0.0]', 'style': { - 'background-color': 'rgb(199,197,197)' + 'background-color': 'rgb(199,197,197)', + 'height': 20, + 'width': 20 } }, { @@ -226,7 +259,7 @@ class layout(layoutOp): ), html.P(id="spikes_info", style={"padding": "8px"}) - ], style={"background": "rgb(227, 245, 251)", "height": "50vh", "textAlign": "start", "padding": "0px","paddingBottom":"12px", "width":"70%"}), + ], style={"background": "rgba(68, 71, 99, 0.05)", "height": "50vh", "textAlign": "start", "padding": "0px","marginBottom":"12px"},className="col-lg-6 col-sm-12 col-xs-12"), html.Div([ # Layers filter @@ -238,10 +271,7 @@ class layout(layoutOp): i for i in self.g.Layer_Neuron if ("Input" not in i and "pool" not in i))], multi=True, style={"minWidth": "20%", "textAlign": "start"}), - # HeatMap - dcc.Graph(id='2DView-heatmap', config={"displaylogo": False} - ), - ], style={"height": "50vh", "textAlign": "start", "padding": "8px", "width":"30%"}) + ], style={"textAlign": "start", "padding": "8px"},className="col-lg-3 col-sm-12 col-xs-12") ], className="row")), label="2D view", value="2Dview")], id="tabinfo", value="General information"), diff --git a/src/static/css/app.css b/src/static/css/app.css index 84e0d9b84bc80642a94116909e30b03d3a63d0d0..0c22653bba4dc3926fae18988e1b2e5e56c784bd 100755 --- a/src/static/css/app.css +++ b/src/static/css/app.css @@ -23,14 +23,14 @@ background-color: red !important; background-color: rgb(219, 232, 246) !important; } .row-sim{ - background-color: rgb(242, 248, 255); + border: 1px solid rgba(0,0,0,.125); margin: 30px; border-radius: 10px; text-align: center; padding: 50px; } .panel{ - background-color: rgb(242, 248, 255); + background-color: rgba(68, 71, 99, 0.2); margin: 5%; border-radius: 10px; text-align: center; diff --git a/src/templates/index.html b/src/templates/index.html index 70d434cfd3eff0c1d3fda8f6f029defcee367ed9..0e20e87ab6a278eae33b2531ddb1207b875dfbd3 100755 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -43,7 +43,7 @@ </div> <div class="row d-flex justify-content-center"> <div class="col-md-10 col-lg-5 col-sm-10 row-sim"> - <h5 style="text-align: start;color: rgb(68, 71, 99);">Settings</h5> + <h5 style="text-align: start;">Settings</h5> <div class="input-group" style="padding-top: 18px;"> <div class="input-group-append" style="width: 180px;"> <span class="input-group-text" style="width: 180px;">Simulation</span> @@ -66,7 +66,7 @@ </div> <div class="col-md-10 col-lg-5 col-sm-10 row-sim"> - <h5 style="text-align: start;color: rgb(68, 71, 99);">Modules</h5> + <h5 style="text-align: start;">Modules</h5> <div class="col-12 btn-group-vertical" data-toggle="buttons" style="padding-left: 0px;padding-right: 0px;"> {% for module in modules %}