From 575c55480fb5ec9d0abda225a357ab0a4b5bc173 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Wed, 28 Feb 2024 22:14:55 +0530 Subject: [PATCH] add boxen & ecdf plots Signed-off-by: Arunima Chaudhuri --- DAS/visualizer.py | 15 --- DAS/visualizor.py | 268 ++++++++++++++++++++++++++++++++++++++++++++++ smallConf.py | 26 ++--- 3 files changed, 281 insertions(+), 28 deletions(-) diff --git a/DAS/visualizer.py b/DAS/visualizer.py index 12b3d31..db2b021 100644 --- a/DAS/visualizer.py +++ b/DAS/visualizer.py @@ -241,21 +241,6 @@ class Visualizer: plt.savefig(filename) plt.clf() - def plotHist(self, bandwidth): - """Plot Bandwidth Frequency Histogram""" - plt.hist(bandwidth, bins=5) - plt.xlabel('Bandwidth') - plt.ylabel('Frequency') - plt.title('Bandwidth Histogram') - - """Create the directory if it doesn't exist already""" - histogramFolder = self.folderPath + '/histogram' - if not os.path.exists(histogramFolder): - os.makedirs(histogramFolder) - filename = os.path.join(histogramFolder, 'histogram.png') - plt.savefig(filename) - plt.clf() - def plotCandleStick(self, TX_prod, TX_avg, TX_max): #x-axis corresponding to steps steps = range(len(TX_prod)) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 71cd0f6..0bd1721 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -1,6 +1,7 @@ #!/bin/python3 import matplotlib.pyplot as plt +import seaborn as sns import numpy as np import os @@ -104,9 +105,276 @@ class Visualizor: self.plotRecvData(result, plotPath) self.plotDupData(result, plotPath) self.plotSamplesRepaired(result, plotPath) + self.plotBoxSamplesRepaired(result, plotPath) + self.plotBoxRowCol(result, plotPath) + self.plotBoxenMessagesRecv(result, plotPath) + self.plotBoxenMessagesSent(result, plotPath) + self.plotBoxenSamplesRecv(result, plotPath) + self.plotBoxenSamplesRepaired(result, plotPath) + self.plotBoxenRowColDist(result, plotPath) + + self.plotECDFSamplesRepaired(result, plotPath) + self.plotECDFRowColDist(result, plotPath) + self.plotECDFSamplesReceived(result, plotPath) + self.plotECDFMessagesRecv(result, plotPath) + self.plotECDFMessagesSent(result, plotPath) if self.config.saveRCdist: self.plotRowCol(result, plotPath) + + def plotECDFMessagesSent(self, result, plotPath): + """Plots the ECDF of messages sent by all nodes using seaborn's ecdfplot""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+" \nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "ECDF of Messages Sent by Nodes" + conf["xlabel"] = "Number of Messages Sent" + conf["ylabel"] = "ECDF" + sns.ecdfplot(data=result.msgSentCount) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.xlim(left=0, right=max(result.msgSentCount) * 1.1) + plt.savefig(plotPath + "/ecdf_messagesSent.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/ecdf_messagesSent.png")) + + def plotECDFMessagesRecv(self, result, plotPath): + """Plots the ECDF of messages received by all nodes using seaborn's ecdfplot""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "ECDF of Messages Received by Nodes" + conf["xlabel"] = "Number of Messages Received" + conf["ylabel"] = "ECDF" + sns.ecdfplot(data=result.msgRecvCount) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.xlim(left=0, right=max(result.msgRecvCount) * 1.1) + plt.savefig(plotPath + "/ecdf_messagesRecv.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/ecdf_messagesRecv.png")) + + def plotECDFSamplesReceived(self, result, plotPath): + """Plots the ECDF of samples received by all nodes using seaborn's ecdfplot""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "ECDF of Samples Received by Nodes" + conf["xlabel"] = "Number of Samples Received" + conf["ylabel"] = "ECDF" + sns.ecdfplot(data=result.sampleRecvCount) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.xlim(left=0, right=max(result.sampleRecvCount) * 1.1) + plt.savefig(plotPath + "/ecdf_samplesReceived.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/ecdf_samplesReceived.png")) + + def plotECDFRowColDist(self, result, plotPath): + """Plots the ECDF of row col distance by all nodes using seaborn's ecdfplot""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "ECDF of Row-Col Distance by Nodes" + conf["xlabel"] = "Row-Col Distance" + conf["ylabel"] = "ECDF" + vector1 = result.metrics["rowDist"] + vector2 = result.metrics["columnDist"] + if len(vector1) > len(vector2): + vector2 += [np.nan] * (len(vector1) - len(vector2)) + elif len(vector1) < len(vector2): + vector1 += [np.nan] * (len(vector2) - len(vector1)) + n1 = int(result.numberNodes * result.class1ratio) + conf["data"] = [vector1, vector2] + sns.ecdfplot(data=conf["data"]) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.xlim(left=0, right=max(max(vector1), max(vector2)) * 1.1) + plt.savefig(plotPath + "/ecdf_rowColDist.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/ecdf_rowColDist.png")) + + def plotECDFSamplesRepaired(self, result, plotPath): + """Plots the ECDF of samples repaired by all nodes using seaborn's ecdfplot""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "ECDF of Samples Repaired by Nodes" + conf["xlabel"] = "Number of Samples Repaired" + conf["ylabel"] = "ECDF" + sns.ecdfplot(data=result.repairedSampleCount) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.xlim(left=0, right=max(result.repairedSampleCount) * 1.1) + plt.savefig(plotPath + "/ecdf_samplesRepaired.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/ecdf_samplesRepaired.png")) + + + + + def plotBoxenSamplesRecv(self, result, plotPath): + """Boxen Plot of the number of samples received by all nodes""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "Number of Samples Received by Nodes" + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Number of Samples Received" + n1 = int(result.numberNodes * result.class1ratio) + data = [result.sampleRecvCount[1: n1], result.sampleRecvCount[n1: ]] + plt.figure(figsize=(8, 6)) + sns.boxenplot(data=data, width=0.8) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.tight_layout() + plt.savefig(plotPath + "/boxen_samplesRecv.png") + plt.close() + print("Plot %s created." % (plotPath + "/boxen_samplesRecv.png")) + + def plotBoxenSamplesRepaired(self, result, plotPath): + """Boxen Plot of the number of samples repaired by all nodes""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "Number of Samples Repaired by Nodes" + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Number of Samples Repaired" + n1 = int(result.numberNodes * result.class1ratio) + data = [result.repairedSampleCount[1: n1], result.repairedSampleCount[n1: ]] + plt.figure(figsize=(8, 6)) + sns.boxenplot(data=data, width=0.8) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.tight_layout() + plt.savefig(plotPath + "/boxen_samplesRepaired.png") + plt.close() + print("Plot %s created." % (plotPath + "/boxen_samplesRepaired.png")) + + def plotBoxenRowColDist(self, result, plotPath): + """Boxen Plot of the Row/Column distribution""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+" \nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "Row/Column Distribution" + conf["xlabel"] = "Row/Column Type" + conf["ylabel"] = "Validators Subscribed" + vector1 = result.metrics["rowDist"] + vector2 = result.metrics["columnDist"] + if len(vector1) > len(vector2): + vector2 += [np.nan] * (len(vector1) - len(vector2)) + elif len(vector1) < len(vector2): + vector1 += [np.nan] * (len(vector2) - len(vector1)) + data = [vector1, vector2] + plt.figure(figsize=(8, 6)) + sns.boxenplot(data=data, width=0.8) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.tight_layout() + plt.savefig(plotPath + "/boxen_rowColDist.png") + plt.close() + print("Plot %s created." % (plotPath + "/boxen_rowColDist.png")) + + def plotBoxenMessagesSent(self, result, plotPath): + """Plots the number of messages sent by all nodes using seaborn's boxenplot""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+" \nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "Number of Messages Sent by Nodes" + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Number of Messages Sent" + n1 = int(result.numberNodes * result.class1ratio) + data = [result.msgSentCount[1: n1], result.msgSentCount[n1: ]] + labels = ["Class 1", "Class 2"] + sns.boxenplot(data=data, palette="Set2", ax=plt.gca()) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.savefig(plotPath + "/boxen_messagesSent.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/boxen_messagesSent.png")) + + def plotBoxenMessagesRecv(self, result, plotPath): + """Plots the number of messages received by all nodes using seaborn's boxenplot""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "Number of Messages Received by Nodes" + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Number of Messages Received" + n1 = int(result.numberNodes * result.class1ratio) + data = [result.msgRecvCount[1: n1], result.msgRecvCount[n1: ]] + labels = ["Class 1", "Class 2"] + sns.boxenplot(data=data, palette="Set2", ax=plt.gca()) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.savefig(plotPath + "/boxen_messagesRecv.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/boxen_messagesRecv.png")) + + def plotBoxSamplesRepaired(self, result, plotPath): + """Box Plot of the number of samples repaired by all nodes""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "Number of Samples Repaired by Nodes" + conf["type"] = "individual_bar" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Number of Samples Repaired" + n1 = int(result.numberNodes * result.class1ratio) + conf["data"] = [result.repairedSampleCount[1: n1], result.repairedSampleCount[n1: ]] + conf["path"] = plotPath + "/box_samplesRepaired.png" + plotBoxData(conf) + print("Plot %s created." % conf["path"]) + + def plotBoxRowCol(self, result, plotPath): + """Box Plot of the Row/Column distribution""" + plt.clf() + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Row Size: "+text[2]+"\nColumn Size: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+" \nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" + conf["title"] = "Row/Column Distribution" + conf["xlabel"] = "" + conf["ylabel"] = "Validators Subscribed" + vector1 = result.metrics["rowDist"] + vector2 = result.metrics["columnDist"] + if len(vector1) > len(vector2): + vector2 += [np.nan] * (len(vector1) - len(vector2)) + elif len(vector1) < len(vector2): + vector1 += [np.nan] * (len(vector2) - len(vector1)) + n1 = int(result.numberNodes * result.class1ratio) + conf["data"] = [vector1, vector2] + conf["path"] = plotPath + "/box_rowColDist.png" + plotBoxData(conf) + print("Plot %s created." % conf["path"]) + def plotRestoreRowCount(self, result, plotPath): """Plots the restoreRowCount for each node""" conf = {} diff --git a/smallConf.py b/smallConf.py index e4c91aa..3348a14 100644 --- a/smallConf.py +++ b/smallConf.py @@ -41,33 +41,33 @@ logLevel = logging.INFO numJobs = -1 # Number of simulation runs with the same parameters for statistical relevance -runs = [1] +runs = range(3) # Number of validators -numberNodes = [1024] +numberNodes = range(128, 513, 128) # select failure model between: "random, sequential, MEP, MEP+1, DEP, DEP+1, MREP, MREP-1" failureModels = ["random"] # Percentage of block not released by producer -failureRates = [0] +failureRates = range(40, 81, 20) # Percentage of nodes that are considered malicious -maliciousNodes = [0] +maliciousNodes = range(40,41,20) # Parameter to determine whether to randomly assign malicious nodes or not # If True, the malicious nodes will be assigned randomly; if False, a predefined pattern may be used randomizeMaliciousNodes = True # Per-topic mesh neighborhood size -netDegrees = [8] +netDegrees = range(8, 9, 2) # ratio of class1 nodes (see below for parameters per class) class1ratios = [0.8] # Number of validators per beacon node -validatorsPerNode1 = [1] -validatorsPerNode2 = [1] +validatorsPerNode1 = [10] +validatorsPerNode2 = [50] # Set uplink bandwidth in megabits/second bwUplinksProd = [200] @@ -98,12 +98,12 @@ diagnostics = False # True to save git diff and git commit saveGit = False -blockSizeR = [128] -blockSizeC = [64] -blockSizeRK = [64] -blockSizeCK = [64] -chiR = [2] -chiC = [2] +blockSizeR = range(64, 113, 128) +blockSizeC = range(32, 113, 128) +blockSizeRK = range(32, 65, 128) +blockSizeCK = range(32, 65, 128) +chiR = range(2, 3, 2) +chiC = range(2, 3, 2) def nextShape(): for blckSizeR, blckSizeRK, blckSizeC, blckSizeCK, run, fm, fr, mn, class1ratio, chR, chC, vpn1, vpn2, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product(