From eb8588df9f9a8fbaad7d28f8364fa3477a46372a Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Sun, 21 Jan 2024 01:27:17 +0530 Subject: [PATCH 01/32] Introduce 'maliciousNode' parameter Signed-off-by: Arunima Chaudhuri --- DAS/results.py | 12 +++++++ DAS/shape.py | 4 ++- DAS/simulator.py | 21 ++++++++++-- DAS/validator.py | 62 ++++++++++++++++++++++++----------- DAS/visualizor.py | 83 +++++++++++++++++++++++++++++++++++++++++++---- smallConf.py | 21 +++++++----- 6 files changed, 165 insertions(+), 38 deletions(-) diff --git a/DAS/results.py b/DAS/results.py index 76d96d1..628e8a8 100644 --- a/DAS/results.py +++ b/DAS/results.py @@ -16,6 +16,18 @@ class Result: self.tta = -1 self.missingVector = [] self.metrics = {} + self.amImalicious = [0] * shape.numberNodes + self.msgSentCount = [0] * shape.numberNodes + self.msgRecvCount = [0] * shape.numberNodes + self.sampleRecvCount = [0] * shape.numberNodes + + def copyValidators(self, validators): + """Copy information from simulator.validators to result.""" + for i in range(0,self.shape.numberNodes): + self.amImalicious[i] = validators[i].amImalicious + self.msgSentCount[i] = validators[i].msgSentCount + self.msgRecvCount[i] = validators[i].msgRecvCount + self.sampleRecvCount[i] = validators[i].sampleRecvCount def populate(self, shape, config, missingVector): """It populates part of the result data inside a vector.""" diff --git a/DAS/shape.py b/DAS/shape.py index 9f6d573..109bf97 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -3,13 +3,14 @@ class Shape: """This class represents a set of parameters for a specific simulation.""" - def __init__(self, blockSize, numberNodes, failureModel, failureRate, class1ratio, chi, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): + def __init__(self, blockSize, numberNodes, failureModel, failureRate, maliciousNodes, class1ratio, chi, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): """Initializes the shape with the parameters passed in argument.""" self.run = run self.numberNodes = numberNodes self.blockSize = blockSize self.failureModel = failureModel self.failureRate = failureRate + self.maliciousNodes = maliciousNodes self.netDegree = netDegree self.class1ratio = class1ratio self.chi = chi @@ -36,6 +37,7 @@ class Shape: shastr += "-bwup2-"+str(self.bwUplink2) shastr += "-nd-"+str(self.netDegree) shastr += "-r-"+str(self.run) + shastr += "-mn-"+str(self.maliciousNodes) return shastr def setSeed(self, seed): diff --git a/DAS/simulator.py b/DAS/simulator.py index 147d499..faffdff 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -70,7 +70,14 @@ class Simulator: assignedRows = [] assignedCols = [] + maliciousNodesCount = int((self.shape.maliciousNodes / 100) * self.shape.numberNodes) for i in range(self.shape.numberNodes): + if i==0: + amImalicious_value = 0 + elif i < maliciousNodesCount+1: + amImalicious_value = 1 + else: + amImalicious_value = 0 if self.config.evenLineDistribution: if i < int(lightVal/self.shape.vpn1): # First start with the light nodes start = i *self.shape.chi*self.shape.vpn1 @@ -81,7 +88,7 @@ class Simulator: end = offset+((j+1)*self.shape.chi*self.shape.vpn2) r = rows[start:end] c = columns[start:end] - val = Validator(i, int(not i!=0), self.logger, self.shape, self.config, r, c) + val = Validator(i, int(not i!=0), amImalicious_value, self.logger, self.shape, self.config, r, c) self.logger.debug("Node %d has row IDs: %s" % (val.ID, val.rowIDs), extra=self.format) self.logger.debug("Node %d has column IDs: %s" % (val.ID, val.columnIDs), extra=self.format) assignedRows = assignedRows + list(r) @@ -90,7 +97,7 @@ class Simulator: self.nodeColumns.append(val.columnIDs) else: - val = Validator(i, int(not i!=0), self.logger, self.shape, self.config) + val = Validator(i, int(not i!=0), amImalicious_value, self.logger, self.shape, self.config) if i == self.proposerID: val.initBlock() else: @@ -229,6 +236,7 @@ class Simulator: missingVector = [] progressVector = [] trafficStatsVector = [] + malicious_nodes_not_added_count = 0 steps = 0 while(True): missingVector.append(missingSamples) @@ -298,6 +306,14 @@ class Simulator: break steps += 1 + for i in range(0,self.shape.numberNodes): + if not self.validators[i].amIaddedToQueue : + malicious_nodes_not_added_count += 1 + + self.logger.debug("Number of malicious nodes not added to the send queue: %d" % malicious_nodes_not_added_count, extra=self.format) + malicious_nodes_not_added_percentage = (malicious_nodes_not_added_count * 100)/(self.shape.numberNodes) + self.logger.debug("Percentage of malicious nodes not added to the send queue: %d" % malicious_nodes_not_added_percentage, extra=self.format) + progress = pd.DataFrame(progressVector) if self.config.saveRCdist: self.result.addMetric("rowDist", self.distR) @@ -305,5 +321,6 @@ class Simulator: if self.config.saveProgress: self.result.addMetric("progress", progress.to_dict(orient='list')) self.result.populate(self.shape, self.config, missingVector) + self.result.copyValidators(self.validators) return self.result diff --git a/DAS/validator.py b/DAS/validator.py index 4e8d350..eaf7686 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -38,7 +38,7 @@ class Validator: """It returns the validator ID.""" return str(self.ID) - def __init__(self, ID, amIproposer, logger, shape, config, rows = None, columns = None): + def __init__(self, ID, amIproposer, amImalicious, logger, shape, config, rows = None, columns = None): """It initializes the validator with the logger shape and rows/columns. If rows/columns are specified these are observed, otherwise (default) @@ -54,6 +54,12 @@ class Validator: self.receivedQueue = deque() self.sendQueue = deque() self.amIproposer = amIproposer + self.amImalicious = amImalicious + self.amIaddedToQueue = 0 + self.msgSentCount = 0 + self.msgRecvCount = 0 + self.sampleSentCount = 0 + self.sampleRecvCount = 0 self.logger = logger if self.shape.chi < 1: self.logger.error("Chi has to be greater than 0", extra=self.format) @@ -197,8 +203,10 @@ class Validator: if not self.receivedBlock.getSegment(rID, cID): self.logger.trace("Recv new: %d->%d: %d,%d", src, self.ID, rID, cID, extra=self.format) self.receivedBlock.setSegment(rID, cID) + self.sampleRecvCount += 1 if self.perNodeQueue or self.perNeighborQueue: self.receivedQueue.append((rID, cID)) + self.msgRecvCount += 1 else: self.logger.trace("Recv DUP: %d->%d: %d,%d", src, self.ID, rID, cID, extra=self.format) self.statsRxDupInSlot += 1 @@ -206,17 +214,23 @@ class Validator: def addToSendQueue(self, rID, cID): """Queue a segment for forwarding.""" - if self.perNodeQueue: + if self.perNodeQueue and not self.amImalicious: self.sendQueue.append((rID, cID)) + self.amIaddedToQueue = 1 + self.msgSentCount += 1 - if self.perNeighborQueue: + if self.perNeighborQueue and not self.amImalicious: if rID in self.rowIDs: for neigh in self.rowNeighbors[rID].values(): neigh.sendQueue.append(cID) + self.amIaddedToQueue = 1 + self.msgSentCount += 1 if cID in self.columnIDs: for neigh in self.columnNeighbors[cID].values(): neigh.sendQueue.append(rID) + self.amIaddedToQueue = 1 + self.msgSentCount += 1 def receiveRowsColumns(self): """Finalize time step by merging newly received segments in state.""" @@ -233,11 +247,14 @@ class Validator: neigh.received |= neigh.receiving neigh.receiving.setall(0) + for rID, cID in self.receivedQueue: + self.msgRecvCount += 1 # add newly received segments to the send queue if self.perNodeQueue or self.perNeighborQueue: while self.receivedQueue: (rID, cID) = self.receivedQueue.popleft() - self.addToSendQueue(rID, cID) + if not self.amImalicious: + self.addToSendQueue(rID, cID) def updateStats(self): """It updates the stats related to sent and received data.""" @@ -269,7 +286,7 @@ class Validator: def checkSendSegmentToNeigh(self, rID, cID, neigh): """Check and send a segment to a neighbor if needed.""" - if self.checkSegmentToNeigh(rID, cID, neigh): + if self.checkSegmentToNeigh(rID, cID, neigh) and not self.amImalicious: self.sendSegmentToNeigh(rID, cID, neigh) return True else: @@ -286,14 +303,16 @@ class Validator: if rID in self.rowIDs: for _, neigh in shuffledDict(self.rowNeighbors[rID], self.shuffleNeighbors): - self.checkSendSegmentToNeigh(rID, cID, neigh) + if not self.amImalicious: + self.checkSendSegmentToNeigh(rID, cID, neigh) if self.statsTxInSlot >= self.bwUplink: return if cID in self.columnIDs: for _, neigh in shuffledDict(self.columnNeighbors[cID], self.shuffleNeighbors): - self.checkSendSegmentToNeigh(rID, cID, neigh) + if not self.amImalicious: + self.checkSendSegmentToNeigh(rID, cID, neigh) if self.statsTxInSlot >= self.bwUplink: return @@ -318,19 +337,20 @@ class Validator: # collect and shuffle for rID, neighs in self.rowNeighbors.items(): for neigh in neighs.values(): - if (neigh.sendQueue): + if (neigh.sendQueue) and not self.amImalicious: queues.append((0, rID, neigh)) for cID, neighs in self.columnNeighbors.items(): for neigh in neighs.values(): - if (neigh.sendQueue): + if (neigh.sendQueue) and not self.amImalicious: queues.append((1, cID, neigh)) for dim, lineID, neigh in shuffled(queues, self.shuffleQueues): - if dim == 0: - self.checkSendSegmentToNeigh(lineID, neigh.sendQueue.popleft(), neigh) - else: - self.checkSendSegmentToNeigh(neigh.sendQueue.popleft(), lineID, neigh) + if not self.amImalicious: + if dim == 0: + self.checkSendSegmentToNeigh(lineID, neigh.sendQueue.popleft(), neigh) + else: + self.checkSendSegmentToNeigh(neigh.sendQueue.popleft(), lineID, neigh) progress = True if self.statsTxInSlot >= self.bwUplink: return @@ -450,22 +470,24 @@ class Validator: """ Send as much as we can in the timestep, limited by bwUplink.""" # process node level send queue - self.processSendQueue() + if not self.amImalicious: + self.processSendQueue() if self.statsTxInSlot >= self.bwUplink: return # process neighbor level send queues in shuffled breadth-first order - self.processPerNeighborSendQueue() + if not self.amImalicious: + self.processPerNeighborSendQueue() if self.statsTxInSlot >= self.bwUplink: return # process possible segments to send in shuffled breadth-first order - if self.segmentShuffleScheduler: + if self.segmentShuffleScheduler and not self.amImalicious: self.runSegmentShuffleScheduler() if self.statsTxInSlot >= self.bwUplink: return - if self.dumbRandomScheduler: + if self.dumbRandomScheduler and not self.amImalicious: self.runDumbRandomScheduler() if self.statsTxInSlot >= self.bwUplink: return @@ -497,7 +519,8 @@ class Validator: for i in range(len(rep)): if rep[i]: self.logger.trace("Rep: %d,%d", id, i, extra=self.format) - self.addToSendQueue(id, i) + if not self.amImalicious: + self.addToSendQueue(id, i) # self.statsRepairInSlot += rep.count(1) def restoreColumns(self): @@ -515,7 +538,8 @@ class Validator: for i in range(len(rep)): if rep[i]: self.logger.trace("Rep: %d,%d", i, id, extra=self.format) - self.addToSendQueue(i, id) + if not self.amImalicious: + self.addToSendQueue(i, id) # self.statsRepairInSlot += rep.count(1) def checkStatus(self): diff --git a/DAS/visualizor.py b/DAS/visualizor.py index cb3c5d9..fb582e9 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -6,16 +6,20 @@ import os def plotData(conf): plt.clf() fig = plt.figure("9, 3") + plt.grid(True) if conf["desLoc"] == 1: xDes = 0 else: xDes = conf["xdots"][-1] * 0.6 props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) plt.text(xDes, conf["yaxismax"]/4, conf["textBox"], fontsize=10, verticalalignment='top', bbox=props) - for i in range(len(conf["data"])): - if conf["type"] == "plot": + if conf["type"] == "plot": + for i in range(len(conf["data"])): plt.plot(conf["xdots"], conf["data"][i], conf["colors"][i], label=conf["labels"][i]) - if conf["type"] == "bar": + elif conf["type"] == "individual_bar": + plt.bar(conf["xdots"], conf["data"]) + elif conf["type"] == "grouped_bar": + for i in range(len(conf["data"])): plt.bar(conf["xdots"], conf["data"][i], label=conf["labels"][i]) plt.title(conf["title"]) plt.ylabel(conf["ylabel"]) @@ -66,6 +70,9 @@ class Visualizor: for result in self.results: plotPath = "results/"+self.execID+"/plots/"+str(result.shape) os.makedirs(plotPath, exist_ok=True) + self.plotMessagesSent(result, plotPath) + self.plotMessagesRecv(result, plotPath) + self.plotSampleRecv(result, plotPath) self.plotMissingSamples(result, plotPath) self.plotProgress(result, plotPath) self.plotSentData(result, plotPath) @@ -74,6 +81,28 @@ class Visualizor: if self.config.saveRCdist: self.plotRowCol(result, plotPath) + def plotSampleRecv(self, result, plotPath): + """Plots the percentage sampleRecv for each node""" + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + conf["title"] = "Percentage of Samples Received by Nodes" + conf["type"] = "individual_bar" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Nodes" + conf["ylabel"] = "Percentage of samples received (%)" + total_samples = result.shape.blockSize * result.shape.blockSize + percentage_data = [(count / total_samples) * 100 for count in result.sampleRecvCount] + conf["data"] = percentage_data + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/sampleRecv.png" + maxi = max(conf["data"]) + conf["yaxismax"] = maxi + plotData(conf) + print("Plot %s created." % conf["path"]) + def plotMissingSamples(self, result, plotPath): """Plots the missing samples in the network""" conf = {} @@ -101,9 +130,9 @@ class Visualizor: def plotProgress(self, result, plotPath): """Plots the percentage of nodes ready in the network""" - vector1 = result.metrics["progress"]["nodes ready"] - vector2 = result.metrics["progress"]["validators ready"] - vector3 = result.metrics["progress"]["samples received"] + vector1 = [x * 100 for x in result.metrics["progress"]["nodes ready"]] + vector2 = [x * 100 for x in result.metrics["progress"]["validators ready"]] + vector3 = [x * 100 for x in result.metrics["progress"]["samples received"]] conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ @@ -228,7 +257,7 @@ class Visualizor: conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" conf["title"] = "Row/Column distribution" - conf["type"] = "bar" + conf["type"] = "grouped_bar" conf["legLoc"] = 2 conf["desLoc"] = 2 conf["colors"] = ["r+", "b+"] @@ -246,3 +275,43 @@ class Visualizor: plotData(conf) print("Plot %s created." % conf["path"]) + def plotMessagesSent(self, result, plotPath): + """Plots the number of messages sent by all nodes""" + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + conf["title"] = "Number of Messages Sent by Nodes" + conf["type"] = "individual_bar" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Nodes" + conf["ylabel"] = "Number of Messages Sent" + conf["data"] = result.msgSentCount + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/messagesSent.png" + maxi = max(conf["data"]) + conf["yaxismax"] = maxi + plotData(conf) + print("Plot %s created." % conf["path"]) + + def plotMessagesRecv(self, result, plotPath): + """Plots the number of messages received by all nodes""" + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + conf["title"] = "Number of Messages Received by Nodes" + conf["type"] = "individual_bar" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Nodes" + conf["ylabel"] = "Number of Messages Received" + conf["data"] = result.msgRecvCount + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/messagesRecv.png" + maxi = max(conf["data"]) + conf["yaxismax"] = maxi + plotData(conf) + print("Plot %s created." % conf["path"]) + diff --git a/smallConf.py b/smallConf.py index 7ab3f44..e1c9125 100644 --- a/smallConf.py +++ b/smallConf.py @@ -45,19 +45,22 @@ numJobs = -1 evenLineDistribution = True # Number of simulation runs with the same parameters for statistical relevance -runs = range(3) +runs = [1] # Number of validators -numberNodes = range(128, 513, 128) +numberNodes = [1024] # 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 = range(40, 81, 20) +failureRates = [0] + +# Percentage of nodes that are considered malicious +maliciousNodes = [95] # Block size in one dimension in segments. Block is blockSizes * blockSizes segments. -blockSizes = range(64, 113, 128) +blockSizes = [128] # Per-topic mesh neighborhood size netDegrees = range(8, 9, 2) @@ -69,8 +72,8 @@ chis = range(2, 3, 2) class1ratios = [0.8] # Number of validators per beacon node -validatorsPerNode1 = [1] -validatorsPerNode2 = [500] +validatorsPerNode1 = [10] +validatorsPerNode2 = [250] # Set uplink bandwidth in megabits/second bwUplinksProd = [200] @@ -102,9 +105,9 @@ diagnostics = False saveGit = False def nextShape(): - for run, fm, fr, class1ratio, chi, vpn1, vpn2, blockSize, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product( - runs, failureModels, failureRates, class1ratios, chis, validatorsPerNode1, validatorsPerNode2, blockSizes, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): + for run, fm, fr, mn, class1ratio, chi, vpn1, vpn2, blockSize, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product( + runs, failureModels, failureRates, maliciousNodes, class1ratios, chis, validatorsPerNode1, validatorsPerNode2, blockSizes, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): # Network Degree has to be an even number if netDegree % 2 == 0: - shape = Shape(blockSize, nn, fm, fr, class1ratio, chi, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) + shape = Shape(blockSize, nn, fm, fr, mn, class1ratio, chi, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) yield shape From f2a51eb8e1dfd5b49146ba02fb2004a101f154d7 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Tue, 23 Jan 2024 01:46:18 +0530 Subject: [PATCH 02/32] Introduce randomization for malicious nodes Signed-off-by: Arunima Chaudhuri --- DAS/simulator.py | 21 +++++++++++++++++---- DAS/visualizor.py | 20 ++++++++++---------- smallConf.py | 4 ++++ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/DAS/simulator.py b/DAS/simulator.py index faffdff..00ba951 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -71,13 +71,26 @@ class Simulator: assignedRows = [] assignedCols = [] maliciousNodesCount = int((self.shape.maliciousNodes / 100) * self.shape.numberNodes) + remainingMaliciousNodes = maliciousNodesCount + for i in range(self.shape.numberNodes): - if i==0: + if i == 0: amImalicious_value = 0 - elif i < maliciousNodesCount+1: - amImalicious_value = 1 else: - amImalicious_value = 0 + if not self.config.randomizeMaliciousNodes: + # Assign based on predefined pattern when randomization is turned off + if i < maliciousNodesCount + 1: + amImalicious_value = 1 + else: + amImalicious_value = 0 + else: + # Randomly assign amImalicious_value when randomization is turned on + if remainingMaliciousNodes > 0 and random.random() < (self.shape.maliciousNodes / 100): + amImalicious_value = 1 + remainingMaliciousNodes -= 1 + else: + amImalicious_value = 0 + if self.config.evenLineDistribution: if i < int(lightVal/self.shape.vpn1): # First start with the light nodes start = i *self.shape.chi*self.shape.vpn1 diff --git a/DAS/visualizor.py b/DAS/visualizor.py index fb582e9..ab1e57f 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -12,7 +12,7 @@ def plotData(conf): else: xDes = conf["xdots"][-1] * 0.6 props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) - plt.text(xDes, conf["yaxismax"]/4, conf["textBox"], fontsize=10, verticalalignment='top', bbox=props) + plt.text(xDes, conf["yaxismax"]/3, conf["textBox"], fontsize=10, verticalalignment='top', bbox=props) if conf["type"] == "plot": for i in range(len(conf["data"])): plt.plot(conf["xdots"], conf["data"][i], conf["colors"][i], label=conf["labels"][i]) @@ -86,7 +86,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Percentage of Samples Received by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -108,7 +108,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Missing Samples" conf["type"] = "plot" conf["legLoc"] = 1 @@ -136,7 +136,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Nodes/validators ready" conf["type"] = "plot" conf["legLoc"] = 2 @@ -168,7 +168,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Sent data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -198,7 +198,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Received data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -228,7 +228,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Duplicated data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -255,7 +255,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Row/Column distribution" conf["type"] = "grouped_bar" conf["legLoc"] = 2 @@ -280,7 +280,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Number of Messages Sent by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -300,7 +300,7 @@ class Visualizor: conf = {} text = str(result.shape).split("-") conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns" + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" conf["title"] = "Number of Messages Received by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 diff --git a/smallConf.py b/smallConf.py index e1c9125..e5f6ccd 100644 --- a/smallConf.py +++ b/smallConf.py @@ -59,6 +59,10 @@ failureRates = [0] # Percentage of nodes that are considered malicious maliciousNodes = [95] +# 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 + # Block size in one dimension in segments. Block is blockSizes * blockSizes segments. blockSizes = [128] From 2fc4a963fbedd8b930a7d797cfb7b24d246bf88b Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Mon, 29 Jan 2024 19:50:01 +0530 Subject: [PATCH 03/32] add graphs for number of rows and columns repaired Signed-off-by: Arunima Chaudhuri --- DAS/results.py | 4 ++++ DAS/validator.py | 26 ++++++++++++++++---------- DAS/visualizor.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/DAS/results.py b/DAS/results.py index 628e8a8..6fbf3a4 100644 --- a/DAS/results.py +++ b/DAS/results.py @@ -20,6 +20,8 @@ class Result: self.msgSentCount = [0] * shape.numberNodes self.msgRecvCount = [0] * shape.numberNodes self.sampleRecvCount = [0] * shape.numberNodes + self.restoreRowCount = [0] * shape.numberNodes + self.restoreColumnCount = [0] * shape.numberNodes def copyValidators(self, validators): """Copy information from simulator.validators to result.""" @@ -28,6 +30,8 @@ class Result: self.msgSentCount[i] = validators[i].msgSentCount self.msgRecvCount[i] = validators[i].msgRecvCount self.sampleRecvCount[i] = validators[i].sampleRecvCount + self.restoreRowCount[i] = validators[i].restoreRowCount + self.restoreColumnCount[i] = validators[i].restoreColumnCount def populate(self, shape, config, missingVector): """It populates part of the result data inside a vector.""" diff --git a/DAS/validator.py b/DAS/validator.py index eaf7686..f2d8e93 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -60,6 +60,8 @@ class Validator: self.msgRecvCount = 0 self.sampleSentCount = 0 self.sampleRecvCount = 0 + self.restoreRowCount = 0 + self.restoreColumnCount = 0 self.logger = logger if self.shape.chi < 1: self.logger.error("Chi has to be greater than 0", extra=self.format) @@ -268,21 +270,23 @@ class Validator: def checkSegmentToNeigh(self, rID, cID, neigh): """Check if a segment should be sent to a neighbor.""" - if (neigh.sent | neigh.received).count(1) >= self.sendLineUntil: - return False # sent enough, other side can restore - i = rID if neigh.dim else cID - if not neigh.sent[i] and not neigh.received[i] : - return True + if not self.amImalicious: + if (neigh.sent | neigh.received).count(1) >= self.sendLineUntil: + return False # sent enough, other side can restore + i = rID if neigh.dim else cID + if not neigh.sent[i] and not neigh.received[i] : + return True else: return False # received or already sent def sendSegmentToNeigh(self, rID, cID, neigh): """Send segment to a neighbor (without checks).""" - self.logger.trace("sending %d/%d to %d", rID, cID, neigh.node.ID, extra=self.format) - i = rID if neigh.dim else cID - neigh.sent[i] = 1 - neigh.node.receiveSegment(rID, cID, self.ID) - self.statsTxInSlot += 1 + if not self.amImalicious: + self.logger.trace("sending %d/%d to %d", rID, cID, neigh.node.ID, extra=self.format) + i = rID if neigh.dim else cID + neigh.sent[i] = 1 + neigh.node.receiveSegment(rID, cID, self.ID) + self.statsTxInSlot += 1 def checkSendSegmentToNeigh(self, rID, cID, neigh): """Check and send a segment to a neighbor if needed.""" @@ -516,6 +520,7 @@ class Validator: if (rep.any()): # If operation is based on send queues, segments should # be queued after successful repair. + self.restoreRowCount += 1 for i in range(len(rep)): if rep[i]: self.logger.trace("Rep: %d,%d", id, i, extra=self.format) @@ -535,6 +540,7 @@ class Validator: if (rep.any()): # If operation is based on send queues, segments should # be queued after successful repair. + self.restoreColumnCount += 1 for i in range(len(rep)): if rep[i]: self.logger.trace("Rep: %d,%d", i, id, extra=self.format) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index ab1e57f..50ed89f 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -70,6 +70,8 @@ class Visualizor: for result in self.results: plotPath = "results/"+self.execID+"/plots/"+str(result.shape) os.makedirs(plotPath, exist_ok=True) + self.plotRestoreRowCount(result, plotPath) + self.plotRestoreColumnCount(result, plotPath) self.plotMessagesSent(result, plotPath) self.plotMessagesRecv(result, plotPath) self.plotSampleRecv(result, plotPath) @@ -81,6 +83,46 @@ class Visualizor: if self.config.saveRCdist: self.plotRowCol(result, plotPath) + def plotRestoreRowCount(self, result, plotPath): + """Plots the restoreRowCount for each node""" + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["title"] = "Restore Row Count for Each Node" + conf["type"] = "individual_bar" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Nodes" + conf["ylabel"] = "Restore Row Count" + conf["data"] = result.restoreRowCount + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/restoreRowCount.png" + maxi = max(conf["data"]) + conf["yaxismax"] = maxi + plotData(conf) + print("Plot %s created." % conf["path"]) + + def plotRestoreColumnCount(self, result, plotPath): + """Plots the restoreColumnCount for each node""" + conf = {} + text = str(result.shape).split("-") + conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ + +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["title"] = "Restore Column Count for Each Node" + conf["type"] = "individual_bar" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Nodes" + conf["ylabel"] = "Restore Column Count" + conf["data"] = result.restoreColumnCount + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/restoreColumnCount.png" + maxi = max(conf["data"]) + conf["yaxismax"] = maxi + plotData(conf) + print("Plot %s created." % conf["path"]) + def plotSampleRecv(self, result, plotPath): """Plots the percentage sampleRecv for each node""" conf = {} From 7ed3d8c690f411b8dcf21ebdefc63abc9757ff30 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Fri, 2 Feb 2024 23:27:38 +0530 Subject: [PATCH 04/32] Debugged to ensure accurate results Signed-off-by: Arunima Chaudhuri --- DAS/simulator.py | 17 ++++++++++-- DAS/validator.py | 67 +++++++++++++++++++++++++----------------------- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/DAS/simulator.py b/DAS/simulator.py index 00ba951..d1b6abe 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -241,7 +241,8 @@ class Simulator: self.glob.checkRowsColumns(self.validators) for i in range(0,self.shape.numberNodes): if i == self.proposerID: - self.validators[i].initBlock() + # self.validators[i].initBlock() + self.logger.warning("I am a block proposer.", extra=self.format) else: self.validators[i].logIDs() arrived, expected, ready, validatedall, validated = self.glob.checkStatus(self.validators) @@ -256,7 +257,8 @@ class Simulator: oldMissingSamples = missingSamples self.logger.debug("PHASE SEND %d" % steps, extra=self.format) for i in range(0,self.shape.numberNodes): - self.validators[i].send() + if not self.validators[i].amImalicious: + self.validators[i].send() self.logger.debug("PHASE RECEIVE %d" % steps, extra=self.format) for i in range(1,self.shape.numberNodes): self.validators[i].receiveRowsColumns() @@ -323,6 +325,17 @@ class Simulator: if not self.validators[i].amIaddedToQueue : malicious_nodes_not_added_count += 1 + for i in range(0,self.shape.numberNodes): + column_ids = [] + row_ids = [] + for rID in self.validators[i].rowIDs: + row_ids.append(rID) + for cID in self.validators[i].columnIDs: + column_ids.append(cID) + + self.logger.debug("List of columnIDs for %d node: %s", i, column_ids, extra=self.format) + self.logger.debug("List of rowIDs for %d node: %s", i, row_ids, extra=self.format) + self.logger.debug("Number of malicious nodes not added to the send queue: %d" % malicious_nodes_not_added_count, extra=self.format) malicious_nodes_not_added_percentage = (malicious_nodes_not_added_count * 100)/(self.shape.numberNodes) self.logger.debug("Percentage of malicious nodes not added to the send queue: %d" % malicious_nodes_not_added_percentage, extra=self.format) diff --git a/DAS/validator.py b/DAS/validator.py index f2d8e93..86e2107 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -305,7 +305,7 @@ class Validator: while self.sendQueue: (rID, cID) = self.sendQueue[0] - if rID in self.rowIDs: + if rID in self.rowIDs and not self.amImalicious: for _, neigh in shuffledDict(self.rowNeighbors[rID], self.shuffleNeighbors): if not self.amImalicious: self.checkSendSegmentToNeigh(rID, cID, neigh) @@ -313,7 +313,7 @@ class Validator: if self.statsTxInSlot >= self.bwUplink: return - if cID in self.columnIDs: + if cID in self.columnIDs and not self.amImalicious: for _, neigh in shuffledDict(self.columnNeighbors[cID], self.shuffleNeighbors): if not self.amImalicious: self.checkSendSegmentToNeigh(rID, cID, neigh) @@ -370,31 +370,32 @@ class Validator: def collectSegmentsToSend(): # yields list of segments to send as (dim, lineID, id) segmentsToSend = [] - for rID, neighs in self.rowNeighbors.items(): - line = self.getRow(rID) - needed = zeros(self.shape.blockSize) - for neigh in neighs.values(): - sentOrReceived = neigh.received | neigh.sent - if sentOrReceived.count(1) < self.sendLineUntil: - needed |= ~sentOrReceived - needed &= line - if (needed).any(): - for i in range(len(needed)): - if needed[i]: - segmentsToSend.append((0, rID, i)) + if not self.amImalicious: + for rID, neighs in self.rowNeighbors.items(): + line = self.getRow(rID) + needed = zeros(self.shape.blockSize) + for neigh in neighs.values(): + sentOrReceived = neigh.received | neigh.sent + if sentOrReceived.count(1) < self.sendLineUntil: + needed |= ~sentOrReceived + needed &= line + if (needed).any(): + for i in range(len(needed)): + if needed[i]: + segmentsToSend.append((0, rID, i)) - for cID, neighs in self.columnNeighbors.items(): - line = self.getColumn(cID) - needed = zeros(self.shape.blockSize) - for neigh in neighs.values(): - sentOrReceived = neigh.received | neigh.sent - if sentOrReceived.count(1) < self.sendLineUntil: - needed |= ~sentOrReceived - needed &= line - if (needed).any(): - for i in range(len(needed)): - if needed[i]: - segmentsToSend.append((1, cID, i)) + for cID, neighs in self.columnNeighbors.items(): + line = self.getColumn(cID) + needed = zeros(self.shape.blockSize) + for neigh in neighs.values(): + sentOrReceived = neigh.received | neigh.sent + if sentOrReceived.count(1) < self.sendLineUntil: + needed |= ~sentOrReceived + needed &= line + if (needed).any(): + for i in range(len(needed)): + if needed[i]: + segmentsToSend.append((1, cID, i)) return segmentsToSend @@ -405,12 +406,12 @@ class Validator: for dim, lineID, id in self.segmentShuffleGen: if dim == 0: for _, neigh in shuffledDict(self.rowNeighbors[lineID], self.shuffleNeighbors): - if self.checkSegmentToNeigh(lineID, id, neigh): + if self.checkSegmentToNeigh(lineID, id, neigh) and not self.amImalicious: yield((lineID, id, neigh)) break else: for _, neigh in shuffledDict(self.columnNeighbors[lineID], self.shuffleNeighbors): - if self.checkSegmentToNeigh(id, lineID, neigh): + if self.checkSegmentToNeigh(id, lineID, neigh) and not self.amImalicious: yield((id, lineID, neigh)) break @@ -425,7 +426,8 @@ class Validator: for rid, cid, neigh in nextSegment(): # segments are checked just before yield, so we can send directly - self.sendSegmentToNeigh(rid, cid, neigh) + if not self.amImalicious: + self.sendSegmentToNeigh(rid, cid, neigh) if self.statsTxInSlot >= self.bwUplink: if not self.segmentShuffleSchedulerPersist: @@ -450,7 +452,7 @@ class Validator: cID = random.randrange(0, self.shape.blockSize) if self.block.getSegment(rID, cID) : neigh = random.choice(list(self.rowNeighbors[rID].values())) - if self.checkSegmentToNeigh(rID, cID, neigh): + if self.checkSegmentToNeigh(rID, cID, neigh) and not self.amImalicious: yield(rID, cID, neigh) t = tries if self.columnIDs: @@ -458,14 +460,15 @@ class Validator: rID = random.randrange(0, self.shape.blockSize) if self.block.getSegment(rID, cID) : neigh = random.choice(list(self.columnNeighbors[cID].values())) - if self.checkSegmentToNeigh(rID, cID, neigh): + if self.checkSegmentToNeigh(rID, cID, neigh) and not self.amImalicious: yield(rID, cID, neigh) t = tries t -= 1 for rid, cid, neigh in nextSegment(): # segments are checked just before yield, so we can send directly - self.sendSegmentToNeigh(rid, cid, neigh) + if not self.amImalicious: + self.sendSegmentToNeigh(rid, cid, neigh) if self.statsTxInSlot >= self.bwUplink: return From 5951a90056574ab1680822e0dca28d669d378907 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 7 Dec 2023 12:37:12 +0100 Subject: [PATCH 05/32] renaming Validator object to Node In the SubnetDas model Nodes behave as previous validators, getting samples (whole columns) from GossipSub. Signed-off-by: Csaba Kiraly --- DAS/simulator.py | 4 ++-- DAS/validator.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DAS/simulator.py b/DAS/simulator.py index f3d881d..ae295fc 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -88,7 +88,7 @@ class Simulator: endC = offsetC+((j+1)*self.shape.chiC*self.shape.vpn2) r = rows[startR:endR] c = columns[startC:endC] - val = Validator(i, int(not i!=0), self.logger, self.shape, self.config, r, c) + val = Node(i, int(not i!=0), self.logger, self.shape, self.config, r, c) self.logger.debug("Node %d has row IDs: %s" % (val.ID, val.rowIDs), extra=self.format) self.logger.debug("Node %d has column IDs: %s" % (val.ID, val.columnIDs), extra=self.format) assignedRows = assignedRows + list(r) @@ -97,7 +97,7 @@ class Simulator: self.nodeColumns.append(val.columnIDs) else: - val = Validator(i, int(not i!=0), self.logger, self.shape, self.config) + val = Node(i, int(not i!=0), self.logger, self.shape, self.config) if i == self.proposerID: val.initBlock() else: diff --git a/DAS/validator.py b/DAS/validator.py index 2b489b8..e545e28 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -31,7 +31,7 @@ class Neighbor: self.sendQueue = deque() -class Validator: +class Node: """This class implements a validator/node in the network.""" def __repr__(self): From 7ed441362440d63fd4a5dc8f494fa077275cf4f1 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 7 Dec 2023 12:39:15 +0100 Subject: [PATCH 06/32] not all nodes sample in both dimensions Signed-off-by: Csaba Kiraly --- DAS/validator.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/DAS/validator.py b/DAS/validator.py index e545e28..bdb1ac3 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -55,9 +55,7 @@ class Node: self.sendQueue = deque() self.amIproposer = amIproposer self.logger = logger - if self.shape.chiR < 1 and self.shape.chiC < 1: - self.logger.error("Chi has to be greater than 0", extra=self.format) - elif self.shape.chiC > self.shape.blockSizeR: + if self.shape.chiC > self.shape.blockSizeR: self.logger.error("ChiC has to be smaller than %d" % self.shape.blockSizeR, extra=self.format) elif self.shape.chiR > self.shape.blockSizeC: self.logger.error("ChiR has to be smaller than %d" % self.shape.blockSizeC, extra=self.format) From d782e9c5ab70e572d58634eb48ccaf2f62b8fc84 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 7 Dec 2023 12:48:56 +0100 Subject: [PATCH 07/32] fixup: renaming --- DAS/{validator.py => node.py} | 0 DAS/simulator.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename DAS/{validator.py => node.py} (100%) diff --git a/DAS/validator.py b/DAS/node.py similarity index 100% rename from DAS/validator.py rename to DAS/node.py diff --git a/DAS/simulator.py b/DAS/simulator.py index ae295fc..80c5676 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -8,7 +8,7 @@ from datetime import datetime from DAS.tools import * from DAS.results import * from DAS.observer import * -from DAS.validator import * +from DAS.node import * class Simulator: """This class implements the main DAS simulator.""" From 5a249fe23836f8ef370598da1a10572f282d6c9d Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 12 Dec 2023 13:31:39 +0100 Subject: [PATCH 08/32] adding individual Validator objects behind Node Signed-off-by: Csaba Kiraly --- DAS/node.py | 60 ++++++++++++++++++++++++++---------------------- DAS/simulator.py | 12 +++++++++- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/DAS/node.py b/DAS/node.py index bdb1ac3..6bd5bfd 100644 --- a/DAS/node.py +++ b/DAS/node.py @@ -31,15 +31,26 @@ class Neighbor: self.sendQueue = deque() +class Validator: + def __init__(self, rowIDs, columnIDs): + self.rowIDs = rowIDs + self.columnIDs = columnIDs + +def initValidator(blockSizeC, chiR, blockSizeR, chiC): + rowIDs = set(random.sample(range(blockSizeC), chiR)) + columnIDs = set(random.sample(range(blockSizeR), chiC)) + return Validator(rowIDs, columnIDs) + class Node: - """This class implements a validator/node in the network.""" + """This class implements a node in the network.""" def __repr__(self): - """It returns the validator ID.""" + """It returns the node ID.""" return str(self.ID) - def __init__(self, ID, amIproposer, logger, shape, config, rows = None, columns = None): - """It initializes the validator with the logger shape and rows/columns. + def __init__(self, ID, amIproposer, logger, shape, config, + validators, rows = set(), columns = set()): + """It initializes the node, and eventual validators, following the simulation configuration in shape and config. If rows/columns are specified these are observed, otherwise (default) chiR rows and chiC columns are selected randomly. @@ -55,27 +66,22 @@ class Node: self.sendQueue = deque() self.amIproposer = amIproposer self.logger = logger - if self.shape.chiC > self.shape.blockSizeR: - self.logger.error("ChiC has to be smaller than %d" % self.shape.blockSizeR, extra=self.format) - elif self.shape.chiR > self.shape.blockSizeC: - self.logger.error("ChiR has to be smaller than %d" % self.shape.blockSizeC, extra=self.format) + self.validators = validators + + if amIproposer: + self.nodeClass = 0 + self.rowIDs = range(shape.blockSizeC) + self.columnIDs = range(shape.blockSizeR) else: - if amIproposer: - self.nodeClass = 0 - self.rowIDs = range(shape.blockSizeC) - self.columnIDs = range(shape.blockSizeR) - else: - #if shape.deterministic: - # random.seed(self.ID) - self.nodeClass = 1 if (self.ID <= shape.numberNodes * shape.class1ratio) else 2 - self.vpn = self.shape.vpn1 if (self.nodeClass == 1) else self.shape.vpn2 - self.vRowIDs = [] - self.vColumnIDs = [] - for i in range(self.vpn): - self.vRowIDs.append(set(rows[i*self.shape.chiR:(i+1)*self.shape.chiR]) if rows else set(random.sample(range(self.shape.blockSizeC), self.shape.chiR))) - self.vColumnIDs.append(set(columns[i*self.shape.chiC:(i+1)*self.shape.chiC]) if columns else set(random.sample(range(self.shape.blockSizeR), self.shape.chiC))) - self.rowIDs = set.union(*self.vRowIDs) - self.columnIDs = set.union(*self.vColumnIDs) + self.nodeClass = 1 if (self.ID <= shape.numberNodes * shape.class1ratio) else 2 + self.vpn = len(validators) #TODO: needed by old code, change to fn + + self.rowIDs = set(rows) + self.columnIDs = set(columns) + for v in validators: + self.rowIDs = self.rowIDs.union(v.rowIDs) + self.columnIDs = self.columnIDs.union(v.columnIDs) + self.rowNeighbors = collections.defaultdict(dict) self.columnNeighbors = collections.defaultdict(dict) @@ -113,7 +119,7 @@ class Node: def logIDs(self): """It logs the assigned rows and columns.""" if self.amIproposer == 1: - self.logger.warning("I am a block proposer."% self.ID) + self.logger.warning("I am a block proposer.", extra=self.format) else: self.logger.debug("Selected rows: "+str(self.rowIDs), extra=self.format) self.logger.debug("Selected columns: "+str(self.columnIDs), extra=self.format) @@ -535,8 +541,8 @@ class Node: self.logger.debug("status: %d / %d", arrived, expected, extra=self.format) validated = 0 - for i in range(self.vpn): - a, e = checkStatus(self.vColumnIDs[i], self.vRowIDs[i]) + for v in self.validators: + a, e = checkStatus(v.columnIDs, v.rowIDs) if a == e: validated+=1 diff --git a/DAS/simulator.py b/DAS/simulator.py index 80c5676..8dc5d98 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -97,7 +97,17 @@ class Simulator: self.nodeColumns.append(val.columnIDs) else: - val = Node(i, int(not i!=0), self.logger, self.shape, self.config) + if self.shape.chiC > self.shape.blockSizeR: + self.logger.error("ChiC has to be smaller than %d" % self.shape.blockSizeR) + elif self.shape.chiR > self.shape.blockSizeC: + self.logger.error("ChiR has to be smaller than %d" % self.shape.blockSizeC) + + vs = [] + nodeClass = 1 if (i <= self.shape.numberNodes * self.shape.class1ratio) else 2 + vpn = self.shape.vpn1 if (nodeClass == 1) else self.shape.vpn2 + for v in range(vpn): + vs.append(initValidator(self.shape.blockSizeC, self.shape.chiR, self.shape.blockSizeR, self.shape.chiC)) + val = Node(i, int(not i!=0), self.logger, self.shape, self.config, vs) if i == self.proposerID: val.initBlock() else: From 3db9eda5ea30d2bd1e6fe280a1ceba0bc3e2ecda Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 6 Feb 2024 12:12:56 +0100 Subject: [PATCH 09/32] smallConf: changing evenLineDistribution to False Changing default here, since a generic version of even line distribution is not yet implemented. Signed-off-by: Csaba Kiraly --- smallConf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smallConf.py b/smallConf.py index a166cec..b1434af 100644 --- a/smallConf.py +++ b/smallConf.py @@ -42,7 +42,7 @@ numJobs = -1 # distribute rows/columns evenly between validators (True) # or generate it using local randomness (False) -evenLineDistribution = True +evenLineDistribution = False # Number of simulation runs with the same parameters for statistical relevance runs = range(3) From 7875023ef0ff8ada7ff9aee385dffee9fa4e1f2f Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Thu, 15 Feb 2024 23:48:30 +0530 Subject: [PATCH 10/32] Independent dimensions Signed-off-by: Arunima Chaudhuri --- .gitignore | 2 +- DAS/block.py | 57 ++++++++++++++++------------- DAS/observer.py | 15 ++++---- DAS/shape.py | 20 +++++++---- DAS/simulator.py | 52 +++++++++++++++------------ DAS/validator.py | 91 +++++++++++++++++++++++------------------------ DAS/visualizer.py | 6 ++-- DAS/visualizor.py | 54 +++++++++++++++------------- smallConf.py | 27 +++++++------- 9 files changed, 176 insertions(+), 148 deletions(-) diff --git a/.gitignore b/.gitignore index 492044a..43965e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ *.swp *.pyc results/* -myenv +myenv*/ doc/_build !results/plots.py Frontend/ diff --git a/DAS/block.py b/DAS/block.py index f76a944..ef3f4f5 100644 --- a/DAS/block.py +++ b/DAS/block.py @@ -7,10 +7,18 @@ from bitarray.util import zeros class Block: """This class represents a block in the Ethereum blockchain.""" - def __init__(self, blockSize): - """Initialize the block with a data array of blocksize^2 zeros.""" - self.blockSize = blockSize - self.data = zeros(self.blockSize*self.blockSize) + def __init__(self, blockSizeR, blockSizeRK=0, blockSizeC=0, blockSizeCK=0): + """Initialize the block with a data array of blocksize^2 zeros. + BlockSizeR: row size + BlockSizeRK: original row size, before erasure coding to BlocksSizeR + BlockSizeC: column size (i.e. number of rows) + BlockSizeCK: original column size, before erasure coding to BlocksSizeR + """ + self.blockSizeR = blockSizeR + self.blockSizeRK = blockSizeRK if blockSizeRK else blockSizeR/2 + self.blockSizeC = blockSizeC if blockSizeC else blockSizeR + self.blockSizeCK = blockSizeCK if blockSizeCK else blockSizeRK + self.data = zeros(self.blockSizeR*self.blockSizeC) def fill(self): """It fills the block data with ones.""" @@ -22,62 +30,61 @@ class Block: def getSegment(self, rowID, columnID): """Check whether a segment is included""" - return self.data[rowID*self.blockSize + columnID] + return self.data[rowID*self.blockSizeR + columnID] def setSegment(self, rowID, columnID, value = 1): """Set value for a segment (default 1)""" - self.data[rowID*self.blockSize + columnID] = value + self.data[rowID*self.blockSizeR + columnID] = value def getColumn(self, columnID): """It returns the block column corresponding to columnID.""" - return self.data[columnID::self.blockSize] + return self.data[columnID::self.blockSizeR] def mergeColumn(self, columnID, column): """It merges (OR) the existing column with the received one.""" - self.data[columnID::self.blockSize] |= column + self.data[columnID::self.blockSizeR] |= column def repairColumn(self, id): - """It repairs the entire column if it has at least blockSize/2 ones. + """It repairs the entire column if it has at least blockSizeCK ones. Returns: list of repaired segments """ - line = self.data[id::self.blockSize] + line = self.data[id::self.blockSizeR] success = line.count(1) - if success >= self.blockSize/2: + if success >= self.blockSizeCK: ret = ~line - self.data[id::self.blockSize] = 1 + self.data[id::self.blockSizeR] = 1 else: - ret = zeros(self.blockSize) + ret = zeros(self.blockSizeC) return ret def getRow(self, rowID): """It returns the block row corresponding to rowID.""" - return self.data[rowID*self.blockSize:(rowID+1)*self.blockSize] + return self.data[rowID*self.blockSizeR:(rowID+1)*self.blockSizeR] def mergeRow(self, rowID, row): """It merges (OR) the existing row with the received one.""" - self.data[rowID*self.blockSize:(rowID+1)*self.blockSize] |= row + self.data[rowID*self.blockSizeR:(rowID+1)*self.blockSizeR] |= row def repairRow(self, id): - """It repairs the entire row if it has at least blockSize/2 ones. + """It repairs the entire row if it has at least blockSizeRK ones. Returns: list of repaired segments. """ - line = self.data[id*self.blockSize:(id+1)*self.blockSize] + line = self.data[id*self.blockSizeR:(id+1)*self.blockSizeR] success = line.count(1) - if success >= self.blockSize/2: + if success >= self.blockSizeRK: ret = ~line - self.data[id*self.blockSize:(id+1)*self.blockSize] = 1 + self.data[id*self.blockSizeR:(id+1)*self.blockSizeR] = 1 else: - ret = zeros(self.blockSize) + ret = zeros(self.blockSizeR) return ret def print(self): """It prints the block in the terminal (outside of the logger rules)).""" - dash = "-" * (self.blockSize+2) + dash = "-" * (self.blockSizeR+2) print(dash) - for i in range(self.blockSize): + for i in range(self.blockSizeC): line = "|" - for j in range(self.blockSize): - line += "%i" % self.data[(i*self.blockSize)+j] + for j in range(self.blockSizeR): + line += "%i" % self.data[(i*self.blockSizeR)+j] print(line+"|") print(dash) - diff --git a/DAS/observer.py b/DAS/observer.py index beba4ad..328a11d 100644 --- a/DAS/observer.py +++ b/DAS/observer.py @@ -11,10 +11,11 @@ class Observer: self.config = config self.format = {"entity": "Observer"} self.logger = logger - self.block = [0] * self.config.blockSize * self.config.blockSize - self.rows = [0] * self.config.blockSize - self.columns = [0] * self.config.blockSize - self.broadcasted = Block(self.config.blockSize) + self.block = [0] * self.config.blockSizeR * self.config.blockSizeC + self.rows = [0] * self.config.blockSizeC + self.columns = [0] * self.config.blockSizeR + self.broadcasted = Block(self.config.blockSizeR, self.config.blockSizeRK, + self.config.blockSizeC, self.config.blockSizeCK) def checkRowsColumns(self, validators): @@ -26,7 +27,7 @@ class Observer: for c in val.columnIDs: self.columns[c] += 1 - for i in range(self.config.blockSize): + for i in range(self.config.blockSizeC): self.logger.debug("Row/Column %d have %d and %d validators assigned." % (i, self.rows[i], self.columns[i]), extra=self.format) if self.rows[i] == 0 or self.columns[i] == 0: self.logger.warning("There is a row/column that has not been assigned", extra=self.format) @@ -34,7 +35,7 @@ class Observer: def checkBroadcasted(self): """It checks how many broadcasted samples are still missing in the network.""" zeros = 0 - for i in range(self.blockSize * self.blockSize): + for i in range(self.blockSizeR * self.blockSizeC): if self.broadcasted.data[i] == 0: zeros += 1 if zeros > 0: @@ -99,4 +100,4 @@ class Observer: "RxDup": {"mean": meanOrNan(RxDup), "max": maxOrNan(RxDup)}, } - return trafficStats + return trafficStats \ No newline at end of file diff --git a/DAS/shape.py b/DAS/shape.py index 109bf97..c2a1ff7 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -3,17 +3,22 @@ class Shape: """This class represents a set of parameters for a specific simulation.""" - def __init__(self, blockSize, numberNodes, failureModel, failureRate, maliciousNodes, class1ratio, chi, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): + def __init__(self, blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, + numberNodes, failureModel, failureRate, maliciousNodes, class1ratio, chiR, chiC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): """Initializes the shape with the parameters passed in argument.""" self.run = run self.numberNodes = numberNodes - self.blockSize = blockSize + self.blockSizeR = blockSizeR + self.blockSizeRK = blockSizeRK + self.blockSizeC = blockSizeC + self.blockSizeCK = blockSizeCK self.failureModel = failureModel self.failureRate = failureRate self.maliciousNodes = maliciousNodes self.netDegree = netDegree self.class1ratio = class1ratio - self.chi = chi + self.chiR = chiR + self.chiC = chiC self.vpn1 = vpn1 self.vpn2 = vpn2 self.bwUplinkProd = bwUplinkProd @@ -24,12 +29,16 @@ class Shape: def __repr__(self): """Returns a printable representation of the shape""" shastr = "" - shastr += "bs-"+str(self.blockSize) + shastr += "-bsrn-"+str(self.blockSizeR) + shastr += "-bsrk-"+str(self.blockSizeRK) + shastr += "-bscn-"+str(self.blockSizeC) + shastr += "-bsck-"+str(self.blockSizeCK) shastr += "-nn-"+str(self.numberNodes) shastr += "-fm-"+str(self.failureModel) shastr += "-fr-"+str(self.failureRate) shastr += "-c1r-"+str(self.class1ratio) - shastr += "-chi-"+str(self.chi) + shastr += "-chir-"+str(self.chiR) + shastr += "-chic-"+str(self.chiC) shastr += "-vpn1-"+str(self.vpn1) shastr += "-vpn2-"+str(self.vpn2) shastr += "-bwupprod-"+str(self.bwUplinkProd) @@ -43,4 +52,3 @@ class Shape: def setSeed(self, seed): """Adds the random seed to the shape""" self.randomSeed = seed - diff --git a/DAS/simulator.py b/DAS/simulator.py index d1b6abe..50b7289 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -41,7 +41,7 @@ class Simulator: # pushed out by the proposer. # 1: the data is sent out exactly once on rows and once on columns (2 copies in total) # self.shape.netDegree: default behavior similar (but not same) to previous code - self.proposerPublishTo = self.shape.netDegree + self.proposerPublishTo = self.shape.netDegree # TODO: make this an external parameter def initValidators(self): """It initializes all the validators in the network.""" @@ -54,17 +54,20 @@ class Simulator: lightVal = lightNodes * self.shape.vpn1 heavyVal = heavyNodes * self.shape.vpn2 totalValidators = lightVal + heavyVal - totalRows = totalValidators * self.shape.chi - rows = list(range(self.shape.blockSize)) * (int(totalRows/self.shape.blockSize)+1) - columns = list(range(self.shape.blockSize)) * (int(totalRows/self.shape.blockSize)+1) + totalRows = totalValidators * self.shape.chiR + totalColumns = totalValidators * self.shape.chiC + rows = list(range(self.shape.blockSizeC)) * (int(totalRows/self.shape.blockSizeC)+1) + columns = list(range(self.shape.blockSizeR)) * (int(totalColumns/self.shape.blockSizeR)+1) rows = rows[0:totalRows] columns = columns[0:totalRows] random.shuffle(rows) random.shuffle(columns) - offset = lightVal*self.shape.chi + offsetR = lightVal*self.shape.chiR + offsetC = lightVal*self.shape.chiC self.logger.debug("There is a total of %d nodes, %d light and %d heavy." % (self.shape.numberNodes, lightNodes, heavyNodes), extra=self.format) self.logger.debug("There is a total of %d validators, %d in light nodes and %d in heavy nodes" % (totalValidators, lightVal, heavyVal), extra=self.format) - self.logger.debug("Shuffling a total of %d rows/columns to be assigned (X=%d)" % (len(rows), self.shape.chi), extra=self.format) + self.logger.debug("Shuffling a total of %d rows to be assigned (X=%d)" % (len(rows), self.shape.chiR), extra=self.format) + self.logger.debug("Shuffling a total of %d columns to be assigned (X=%d)" % (len(columns), self.shape.chiC), extra=self.format) self.logger.debug("Shuffled rows: %s" % str(rows), extra=self.format) self.logger.debug("Shuffled columns: %s" % str(columns), extra=self.format) @@ -93,14 +96,18 @@ class Simulator: if self.config.evenLineDistribution: if i < int(lightVal/self.shape.vpn1): # First start with the light nodes - start = i *self.shape.chi*self.shape.vpn1 - end = (i+1)*self.shape.chi*self.shape.vpn1 + startR = i *self.shape.chiR*self.shape.vpn1 + endR = (i+1)*self.shape.chiR*self.shape.vpn1 + startC = i *self.shape.chiC*self.shape.vpn1 + endC = (i+1)*self.shape.chiC*self.shape.vpn1 else: j = i - int(lightVal/self.shape.vpn1) - start = offset+( j *self.shape.chi*self.shape.vpn2) - end = offset+((j+1)*self.shape.chi*self.shape.vpn2) - r = rows[start:end] - c = columns[start:end] + startR = offsetR+( j *self.shape.chiR*self.shape.vpn2) + endR = offsetR+((j+1)*self.shape.chiR*self.shape.vpn2) + startC = offsetC+( j *self.shape.chiC*self.shape.vpn2) + endC = offsetC+((j+1)*self.shape.chiC*self.shape.vpn2) + r = rows[startR:endR] + c = columns[startC:endC] val = Validator(i, int(not i!=0), amImalicious_value, self.logger, self.shape, self.config, r, c) self.logger.debug("Node %d has row IDs: %s" % (val.ID, val.rowIDs), extra=self.format) self.logger.debug("Node %d has column IDs: %s" % (val.ID, val.columnIDs), extra=self.format) @@ -125,8 +132,8 @@ class Simulator: def initNetwork(self): """It initializes the simulated network.""" - rowChannels = [[] for i in range(self.shape.blockSize)] - columnChannels = [[] for i in range(self.shape.blockSize)] + rowChannels = [[] for i in range(self.shape.blockSizeC)] + columnChannels = [[] for i in range(self.shape.blockSizeR)] for v in self.validators: if not (self.proposerPublishOnly and v.amIproposer): for id in v.rowIDs: @@ -142,7 +149,7 @@ class Simulator: self.logger.debug("Number of validators per row; Min: %d, Max: %d" % (min(self.distR), max(self.distR)), extra=self.format) self.logger.debug("Number of validators per column; Min: %d, Max: %d" % (min(self.distC), max(self.distC)), extra=self.format) - for id in range(self.shape.blockSize): + for id in range(self.shape.blockSizeC): # If the number of nodes in a channel is smaller or equal to the # requested degree, a fully connected graph is used. For n>d, a random @@ -160,9 +167,11 @@ class Simulator: for u, v in G.edges: val1=rowChannels[id][u] val2=rowChannels[id][v] - val1.rowNeighbors[id].update({val2.ID : Neighbor(val2, 0, self.shape.blockSize)}) - val2.rowNeighbors[id].update({val1.ID : Neighbor(val1, 0, self.shape.blockSize)}) + val1.rowNeighbors[id].update({val2.ID : Neighbor(val2, 0, self.shape.blockSizeR)}) + val2.rowNeighbors[id].update({val1.ID : Neighbor(val1, 0, self.shape.blockSizeR)}) + for id in range(self.shape.blockSizeR): + if not columnChannels[id]: self.logger.error("No nodes for column %d !" % id, extra=self.format) continue @@ -176,8 +185,8 @@ class Simulator: for u, v in G.edges: val1=columnChannels[id][u] val2=columnChannels[id][v] - val1.columnNeighbors[id].update({val2.ID : Neighbor(val2, 1, self.shape.blockSize)}) - val2.columnNeighbors[id].update({val1.ID : Neighbor(val1, 1, self.shape.blockSize)}) + val1.columnNeighbors[id].update({val2.ID : Neighbor(val2, 1, self.shape.blockSizeC)}) + val2.columnNeighbors[id].update({val1.ID : Neighbor(val1, 1, self.shape.blockSizeC)}) for v in self.validators: if (self.proposerPublishOnly and v.amIproposer): @@ -185,12 +194,12 @@ class Simulator: count = min(self.proposerPublishTo, len(rowChannels[id])) publishTo = random.sample(rowChannels[id], count) for vi in publishTo: - v.rowNeighbors[id].update({vi.ID : Neighbor(vi, 0, self.shape.blockSize)}) + v.rowNeighbors[id].update({vi.ID : Neighbor(vi, 0, self.shape.blockSizeR)}) for id in v.columnIDs: count = min(self.proposerPublishTo, len(columnChannels[id])) publishTo = random.sample(columnChannels[id], count) for vi in publishTo: - v.columnNeighbors[id].update({vi.ID : Neighbor(vi, 1, self.shape.blockSize)}) + v.columnNeighbors[id].update({vi.ID : Neighbor(vi, 1, self.shape.blockSizeC)}) if self.logger.isEnabledFor(logging.DEBUG): for i in range(0, self.shape.numberNodes): @@ -349,4 +358,3 @@ class Simulator: self.result.populate(self.shape, self.config, missingVector) self.result.copyValidators(self.validators) return self.result - diff --git a/DAS/validator.py b/DAS/validator.py index 86e2107..ae99d6f 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -42,15 +42,15 @@ class Validator: """It initializes the validator with the logger shape and rows/columns. If rows/columns are specified these are observed, otherwise (default) - chi rows and columns are selected randomly. + chiR rows and chiC columns are selected randomly. """ self.shape = shape FORMAT = "%(levelname)s : %(entity)s : %(message)s" self.ID = ID self.format = {"entity": "Val "+str(self.ID)} - self.block = Block(self.shape.blockSize) - self.receivedBlock = Block(self.shape.blockSize) + self.block = Block(self.shape.blockSizeR, self.shape.blockSizeRK, self.shape.blockSizeC, self.shape.blockSizeCK) + self.receivedBlock = Block(self.shape.blockSizeR, self.shape.blockSizeRK, self.shape.blockSizeC, self.shape.blockSizeCK) self.receivedQueue = deque() self.sendQueue = deque() self.amIproposer = amIproposer @@ -63,15 +63,17 @@ class Validator: self.restoreRowCount = 0 self.restoreColumnCount = 0 self.logger = logger - if self.shape.chi < 1: + if self.shape.chiR < 1 and self.shape.chiC < 1: self.logger.error("Chi has to be greater than 0", extra=self.format) - elif self.shape.chi > self.shape.blockSize: - self.logger.error("Chi has to be smaller than %d" % self.shape.blockSize, extra=self.format) + elif self.shape.chiC > self.shape.blockSizeR: + self.logger.error("ChiC has to be smaller than %d" % self.shape.blockSizeR, extra=self.format) + elif self.shape.chiR > self.shape.blockSizeC: + self.logger.error("ChiR has to be smaller than %d" % self.shape.blockSizeC, extra=self.format) else: if amIproposer: self.nodeClass = 0 - self.rowIDs = range(shape.blockSize) - self.columnIDs = range(shape.blockSize) + self.rowIDs = range(shape.blockSizeC) + self.columnIDs = range(shape.blockSizeR) else: #if shape.deterministic: # random.seed(self.ID) @@ -80,8 +82,8 @@ class Validator: self.vRowIDs = [] self.vColumnIDs = [] for i in range(self.vpn): - self.vRowIDs.append(set(rows[i*self.shape.chi:(i+1)*self.shape.chi]) if rows else set(random.sample(range(self.shape.blockSize), self.shape.chi))) - self.vColumnIDs.append(set(columns[i*self.shape.chi:(i+1)*self.shape.chi]) if columns else set(random.sample(range(self.shape.blockSize), self.shape.chi))) + self.vRowIDs.append(set(rows[i*self.shape.chiR:(i+1)*self.shape.chiR]) if rows else set(random.sample(range(self.shape.blockSizeC), self.shape.chiR))) + self.vColumnIDs.append(set(columns[i*self.shape.chiC:(i+1)*self.shape.chiC]) if columns else set(random.sample(range(self.shape.blockSizeR), self.shape.chiC))) self.rowIDs = set.union(*self.vRowIDs) self.columnIDs = set.union(*self.vColumnIDs) self.rowNeighbors = collections.defaultdict(dict) @@ -107,7 +109,8 @@ class Validator: self.bwUplink *= 1e3 / 8 * config.stepDuration / config.segmentSize self.repairOnTheFly = True - self.sendLineUntil = (self.shape.blockSize + 1) // 2 # stop sending on a p2p link if at least this amount of samples passed + self.sendLineUntilR = self.shape.blockSizeRK # stop sending on a p2p link if at least this amount of samples passed + self.sendLineUntilC = self.shape.blockSizeCK # stop sending on a p2p link if at least this amount of samples passed self.perNeighborQueue = True # queue incoming messages to outgoing connections on arrival (as typical GossipSub impl) self.shuffleQueues = True # shuffle the order of picking from active queues of a sender node self.perNodeQueue = False # keep a global queue of incoming messages for later sequential dispatch @@ -132,57 +135,53 @@ class Validator: else: self.logger.debug("Creating block...", extra=self.format) if self.shape.failureModel == "random": - order = [i for i in range(self.shape.blockSize * self.shape.blockSize)] + order = [i for i in range(self.shape.blockSizeR * self.shape.blockSizeC)] order = random.sample(order, int((1 - self.shape.failureRate/100) * len(order))) for i in order: self.block.data[i] = 1 elif self.shape.failureModel == "sequential": - order = [i for i in range(self.shape.blockSize * self.shape.blockSize)] + order = [i for i in range(self.shape.blockSizeR * self.shape.blockSizeC)] order = order[:int((1 - self.shape.failureRate/100) * len(order))] for i in order: self.block.data[i] = 1 elif self.shape.failureModel == "MEP": # Minimal size non-recoverable Erasure Pattern - for r in range(self.shape.blockSize): - for c in range(self.shape.blockSize): - k = self.shape.blockSize/2 - if r > k or c > k: + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + if r > self.shape.blockSizeRK or c > self.shape.blockSizeCK: self.block.setSegment(r,c) elif self.shape.failureModel == "MEP+1": # MEP +1 segment to make it recoverable - for r in range(self.shape.blockSize): - for c in range(self.shape.blockSize): - k = self.shape.blockSize/2 - if r > k or c > k: + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + if r > self.shape.blockSizeRK or c > self.shape.blockSizeCK: self.block.setSegment(r,c) self.block.setSegment(0, 0) elif self.shape.failureModel == "DEP": - for r in range(self.shape.blockSize): - for c in range(self.shape.blockSize): - k = self.shape.blockSize/2 - if (r+c) % self.shape.blockSize > k: + assert(self.shape.blockSizeR == self.shape.blockSizeC and self.shape.blockSizeRK == self.shape.blockSizeCK) + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + if (r+c) % self.shape.blockSizeR > self.shape.blockSizeRK: self.block.setSegment(r,c) elif self.shape.failureModel == "DEP+1": - for r in range(self.shape.blockSize): - for c in range(self.shape.blockSize): - k = self.shape.blockSize/2 - if (r+c) % self.shape.blockSize > k: + assert(self.shape.blockSizeR == self.shape.blockSizeC and self.shape.blockSizeRK == self.shape.blockSizeCK) + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + if (r+c) % self.shape.blockSizeR > self.shape.blockSizeRK: self.block.setSegment(r,c) self.block.setSegment(0, 0) elif self.shape.failureModel == "MREP": # Minimum size Recoverable Erasure Pattern - for r in range(self.shape.blockSize): - for c in range(self.shape.blockSize): - k = self.shape.blockSize/2 - if r < k and c < k: + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + if r < self.shape.blockSizeRK or c < self.shape.blockSizeCK: self.block.setSegment(r,c) elif self.shape.failureModel == "MREP-1": # make MREP non-recoverable - for r in range(self.shape.blockSize): - for c in range(self.shape.blockSize): - k = self.shape.blockSize/2 - if r < k and c < k: + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + if r < self.shape.blockSizeRK or c < self.shape.blockSizeCK: self.block.setSegment(r,c) self.block.setSegment(0, 0, 0) nbFailures = self.block.data.count(0) - measuredFailureRate = nbFailures * 100 / (self.shape.blockSize * self.shape.blockSize) + measuredFailureRate = nbFailures * 100 / (self.shape.blockSizeR * self.shape.blockSizeC) self.logger.debug("Number of failures: %d (%0.02f %%)", nbFailures, measuredFailureRate, extra=self.format) def getColumn(self, index): @@ -271,7 +270,7 @@ class Validator: def checkSegmentToNeigh(self, rID, cID, neigh): """Check if a segment should be sent to a neighbor.""" if not self.amImalicious: - if (neigh.sent | neigh.received).count(1) >= self.sendLineUntil: + if (neigh.sent | neigh.received).count(1) >= (self.sendLineUntilC if neigh.dim else self.sendLineUntilR): return False # sent enough, other side can restore i = rID if neigh.dim else cID if not neigh.sent[i] and not neigh.received[i] : @@ -373,10 +372,10 @@ class Validator: if not self.amImalicious: for rID, neighs in self.rowNeighbors.items(): line = self.getRow(rID) - needed = zeros(self.shape.blockSize) + needed = zeros(self.shape.blockSizeR) for neigh in neighs.values(): sentOrReceived = neigh.received | neigh.sent - if sentOrReceived.count(1) < self.sendLineUntil: + if sentOrReceived.count(1) < self.sendLineUntilR: needed |= ~sentOrReceived needed &= line if (needed).any(): @@ -386,10 +385,10 @@ class Validator: for cID, neighs in self.columnNeighbors.items(): line = self.getColumn(cID) - needed = zeros(self.shape.blockSize) + needed = zeros(self.shape.blockSizeC) for neigh in neighs.values(): sentOrReceived = neigh.received | neigh.sent - if sentOrReceived.count(1) < self.sendLineUntil: + if sentOrReceived.count(1) < self.sendLineUntilC: needed |= ~sentOrReceived needed &= line if (needed).any(): @@ -449,7 +448,7 @@ class Validator: while t: if self.rowIDs: rID = random.choice(self.rowIDs) - cID = random.randrange(0, self.shape.blockSize) + cID = random.randrange(0, self.shape.blockSizeR) if self.block.getSegment(rID, cID) : neigh = random.choice(list(self.rowNeighbors[rID].values())) if self.checkSegmentToNeigh(rID, cID, neigh) and not self.amImalicious: @@ -457,7 +456,7 @@ class Validator: t = tries if self.columnIDs: cID = random.choice(self.columnIDs) - rID = random.randrange(0, self.shape.blockSize) + rID = random.randrange(0, self.shape.blockSizeC) if self.block.getSegment(rID, cID) : neigh = random.choice(list(self.columnNeighbors[cID].values())) if self.checkSegmentToNeigh(rID, cID, neigh) and not self.amImalicious: @@ -576,4 +575,4 @@ class Validator: if a == e: validated+=1 - return arrived, expected, validated + return arrived, expected, validated \ No newline at end of file diff --git a/DAS/visualizer.py b/DAS/visualizer.py index 02bb432..12b3d31 100644 --- a/DAS/visualizer.py +++ b/DAS/visualizer.py @@ -33,12 +33,12 @@ class Visualizer: tree = ET.parse(os.path.join(self.folderPath, filename)) root = tree.getroot() run = int(root.find('run').text) - blockSize = int(root.find('blockSize').text) + blockSize = int(root.find('blockSizeR').text) # TODO: maybe we want both dimensions failureRate = int(root.find('failureRate').text) numberNodes = int(root.find('numberNodes').text) class1ratio = float(root.find('class1ratio').text) netDegree = int(root.find('netDegree').text) - chi = int(root.find('chi').text) + chi = int(root.find('chiR').text) # TODO: maybe we want both dimensions vpn1 = int(root.find('vpn1').text) vpn2 = int(root.find('vpn2').text) bwUplinkProd = int(root.find('bwUplinkProd').text) @@ -274,4 +274,4 @@ class Visualizer: plt.ylabel('Price') #Test - plt.show() + plt.show() \ No newline at end of file diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 50ed89f..fa4676d 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -1,6 +1,7 @@ #!/bin/python3 import matplotlib.pyplot as plt +import numpy as np import os def plotData(conf): @@ -87,8 +88,8 @@ class Visualizor: """Plots the restoreRowCount for each node""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Restore Row Count for Each Node" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -107,8 +108,8 @@ class Visualizor: """Plots the restoreColumnCount for each node""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Restore Column Count for Each Node" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -127,15 +128,15 @@ class Visualizor: """Plots the percentage sampleRecv for each node""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Percentage of Samples Received by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 conf["desLoc"] = 1 conf["xlabel"] = "Nodes" conf["ylabel"] = "Percentage of samples received (%)" - total_samples = result.shape.blockSize * result.shape.blockSize + total_samples = result.shape.blockSizeR * result.shape.blockSizeC percentage_data = [(count / total_samples) * 100 for count in result.sampleRecvCount] conf["data"] = percentage_data conf["xdots"] = range(result.shape.numberNodes) @@ -149,8 +150,8 @@ class Visualizor: """Plots the missing samples in the network""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Missing Samples" conf["type"] = "plot" conf["legLoc"] = 1 @@ -177,8 +178,8 @@ class Visualizor: vector3 = [x * 100 for x in result.metrics["progress"]["samples received"]] conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Nodes/validators ready" conf["type"] = "plot" conf["legLoc"] = 2 @@ -209,8 +210,8 @@ class Visualizor: vector3[i] = (vector3[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+"\nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Sent data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -239,8 +240,8 @@ class Visualizor: vector2[i] = (vector2[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+" \nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Received data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -269,8 +270,8 @@ class Visualizor: vector2[i] = (vector2[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + +"\nFailure rate: "+text[14]+"%"+" \nNetwork degree: "+text[32]+"\nMalicious Nodes: "+text[36]+"%" conf["title"] = "Duplicated data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -294,10 +295,14 @@ class Visualizor: """Plots the percentage of nodes ready in the network""" 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)) conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+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["type"] = "grouped_bar" conf["legLoc"] = 2 @@ -311,7 +316,7 @@ class Visualizor: conf["path"] = plotPath+"/RowColDist.png" maxi = 0 for v in conf["data"]: - if max(v) > maxi: + if np.nanmax(v) > maxi: maxi = max(v) conf["yaxismax"] = maxi plotData(conf) @@ -321,8 +326,8 @@ class Visualizor: """Plots the number of messages sent by all nodes""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+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["type"] = "individual_bar" conf["legLoc"] = 1 @@ -341,8 +346,8 @@ class Visualizor: """Plots the number of messages received by all nodes""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block Size: "+text[1]+"\nNumber of nodes: "+text[3]\ - +"\nFailure rate: "+text[7]+" \nNetwork degree: "+text[23]+"\nX: "+text[11]+" rows/columns"+"\nMalicious Nodes: "+text[27]+"%" + conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+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["type"] = "individual_bar" conf["legLoc"] = 1 @@ -356,4 +361,3 @@ class Visualizor: conf["yaxismax"] = maxi plotData(conf) print("Plot %s created." % conf["path"]) - diff --git a/smallConf.py b/smallConf.py index e5f6ccd..a92f76d 100644 --- a/smallConf.py +++ b/smallConf.py @@ -57,27 +57,21 @@ failureModels = ["random"] failureRates = [0] # Percentage of nodes that are considered malicious -maliciousNodes = [95] +maliciousNodes = [0] # 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 -# Block size in one dimension in segments. Block is blockSizes * blockSizes segments. -blockSizes = [128] - # Per-topic mesh neighborhood size netDegrees = range(8, 9, 2) -# number of rows and columns a validator is interested in -chis = range(2, 3, 2) - # ratio of class1 nodes (see below for parameters per class) class1ratios = [0.8] # Number of validators per beacon node -validatorsPerNode1 = [10] -validatorsPerNode2 = [250] +validatorsPerNode1 = [1] +validatorsPerNode2 = [1] # Set uplink bandwidth in megabits/second bwUplinksProd = [200] @@ -108,10 +102,17 @@ diagnostics = False # True to save git diff and git commit saveGit = False +blockSizeR =[128] +blockSizeC = [64] +blockSizeRK = [64] +blockSizeCK = [64] +chiR = [2] +chiC = [2] + def nextShape(): - for run, fm, fr, mn, class1ratio, chi, vpn1, vpn2, blockSize, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product( - runs, failureModels, failureRates, maliciousNodes, class1ratios, chis, validatorsPerNode1, validatorsPerNode2, blockSizes, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): + for blckSizeR, blckSizeRK, blckSizeC, blckSizeCK, run, fm, fr, mn, class1ratio, chR, chC, vpn1, vpn2, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product( + blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, runs, failureModels, failureRates, maliciousNodes, class1ratios, chiR, chiC, validatorsPerNode1, validatorsPerNode2, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): # Network Degree has to be an even number if netDegree % 2 == 0: - shape = Shape(blockSize, nn, fm, fr, mn, class1ratio, chi, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) - yield shape + shape = Shape(blckSizeR, blckSizeRK, blckSizeC, blckSizeCK, nn, fm, fr, mn, class1ratio, chR, chC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) + yield shape \ No newline at end of file From 6acf8a8285649a03f598e8de1eb8a2a24df2f908 Mon Sep 17 00:00:00 2001 From: Sudipta Basak Date: Sun, 18 Feb 2024 14:22:57 +0100 Subject: [PATCH 11/32] fixed columns array size --- DAS/simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DAS/simulator.py b/DAS/simulator.py index 50b7289..f4be995 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -59,7 +59,7 @@ class Simulator: rows = list(range(self.shape.blockSizeC)) * (int(totalRows/self.shape.blockSizeC)+1) columns = list(range(self.shape.blockSizeR)) * (int(totalColumns/self.shape.blockSizeR)+1) rows = rows[0:totalRows] - columns = columns[0:totalRows] + columns = columns[0:totalColumns] random.shuffle(rows) random.shuffle(columns) offsetR = lightVal*self.shape.chiR From 88f8d1f1c4b771614874251382f4116c459a10d5 Mon Sep 17 00:00:00 2001 From: Sudipta Basak Date: Sun, 18 Feb 2024 16:31:10 +0100 Subject: [PATCH 12/32] README.md Fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97846ce..e4e9841 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,5 @@ $ pip3 install -r DAS/requirements.txt ## Run the simulator ``` -$ python3 study.py config.das +$ python3 study.py smallConf.py ``` From 94b9f4bdaeef26629540f1bd79fe3125ce5b2592 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Sun, 18 Feb 2024 22:16:16 +0530 Subject: [PATCH 13/32] add expectd line in graph&rem evenLineDist in conf Signed-off-by: Arunima Chaudhuri --- DAS/simulator.py | 7 +++++-- DAS/visualizor.py | 47 +++++++++++++++++++++++++++++++---------------- smallConf.py | 8 ++------ 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/DAS/simulator.py b/DAS/simulator.py index 50b7289..fddaaa9 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -47,7 +47,8 @@ class Simulator: """It initializes all the validators in the network.""" self.glob = Observer(self.logger, self.shape) self.validators = [] - if self.config.evenLineDistribution: + evenLineDistribution = False + if evenLineDistribution: lightNodes = int(self.shape.numberNodes * self.shape.class1ratio) heavyNodes = self.shape.numberNodes - lightNodes @@ -94,7 +95,7 @@ class Simulator: else: amImalicious_value = 0 - if self.config.evenLineDistribution: + if evenLineDistribution: if i < int(lightVal/self.shape.vpn1): # First start with the light nodes startR = i *self.shape.chiR*self.shape.vpn1 endR = (i+1)*self.shape.chiR*self.shape.vpn1 @@ -263,6 +264,8 @@ class Simulator: steps = 0 while(True): missingVector.append(missingSamples) + self.logger.debug("Expected Samples: %d" % expected, extra=self.format) + self.logger.debug("Missing Samples: %d" % missingSamples, extra=self.format) oldMissingSamples = missingSamples self.logger.debug("PHASE SEND %d" % steps, extra=self.format) for i in range(0,self.shape.numberNodes): diff --git a/DAS/visualizor.py b/DAS/visualizor.py index fa4676d..6543b30 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -14,14 +14,19 @@ def plotData(conf): xDes = conf["xdots"][-1] * 0.6 props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) plt.text(xDes, conf["yaxismax"]/3, conf["textBox"], fontsize=10, verticalalignment='top', bbox=props) - if conf["type"] == "plot": + if conf["type"] == "plot" or conf["type"] == "plot_with_1line": for i in range(len(conf["data"])): plt.plot(conf["xdots"], conf["data"][i], conf["colors"][i], label=conf["labels"][i]) - elif conf["type"] == "individual_bar": + elif conf["type"] == "individual_bar" or conf["type"] == "individual_bar_with_2line": plt.bar(conf["xdots"], conf["data"]) elif conf["type"] == "grouped_bar": for i in range(len(conf["data"])): plt.bar(conf["xdots"], conf["data"][i], label=conf["labels"][i]) + if conf["type"] == "individual_bar_with_2line": + plt.axhline(y = conf["expected_value1"], color='r', linestyle='--', label='Expected Value for class 1 nodes') + plt.axhline(y = conf["expected_value2"], color='g', linestyle='--', label='Expected Value for class 2 nodes') + if conf["type"] == "plot_with_1line": + plt.axhline(y = conf["expected_value"], color='g', linestyle='--', label='Expected Value') plt.title(conf["title"]) plt.ylabel(conf["ylabel"]) plt.xlabel(conf["xlabel"]) @@ -88,7 +93,7 @@ class Visualizor: """Plots the restoreRowCount for each node""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Restore Row Count for Each Node" conf["type"] = "individual_bar" @@ -108,7 +113,7 @@ class Visualizor: """Plots the restoreColumnCount for each node""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Restore Column Count for Each Node" conf["type"] = "individual_bar" @@ -128,10 +133,10 @@ class Visualizor: """Plots the percentage sampleRecv for each node""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Percentage of Samples Received by Nodes" - conf["type"] = "individual_bar" + conf["type"] = "individual_bar_with_2line" conf["legLoc"] = 1 conf["desLoc"] = 1 conf["xlabel"] = "Nodes" @@ -142,7 +147,15 @@ class Visualizor: conf["xdots"] = range(result.shape.numberNodes) conf["path"] = plotPath + "/sampleRecv.png" maxi = max(conf["data"]) - conf["yaxismax"] = maxi + conf["yaxismax"] = maxi * 1.1 + expected_percentage1 = (result.shape.vpn1 * (result.shape.blockSizeR * result.shape.chiR + result.shape.blockSizeC * result.shape.chiC) * 100)/total_samples + expected_percentage2 = (result.shape.vpn2 * (result.shape.blockSizeR * result.shape.chiR + result.shape.blockSizeC * result.shape.chiC) * 100)/total_samples + if expected_percentage1 > 100: + expected_percentage1 = 100 + if expected_percentage2 > 100: + expected_percentage2 = 100 + conf["expected_value1"] = expected_percentage1 + conf["expected_value2"] = expected_percentage2 plotData(conf) print("Plot %s created." % conf["path"]) @@ -150,10 +163,10 @@ class Visualizor: """Plots the missing samples in the network""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Missing Samples" - conf["type"] = "plot" + conf["type"] = "plot_with_1line" conf["legLoc"] = 1 conf["desLoc"] = 1 conf["colors"] = ["m-"] @@ -168,6 +181,8 @@ class Visualizor: if max(v) > maxi: maxi = max(v) conf["yaxismax"] = maxi + x = result.shape.blockSizeR * result.shape.chiR + result.shape.blockSizeC * result.shape.chiC + conf["expected_value"] = (result.shape.numberNodes - 1) * (result.shape.class1ratio * result.shape.vpn1 * x + (1 - result.shape.class1ratio) * result.shape.vpn2 * x) plotData(conf) print("Plot %s created." % conf["path"]) @@ -178,7 +193,7 @@ class Visualizor: vector3 = [x * 100 for x in result.metrics["progress"]["samples received"]] conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Nodes/validators ready" conf["type"] = "plot" @@ -210,7 +225,7 @@ class Visualizor: vector3[i] = (vector3[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Sent data" conf["type"] = "plot" @@ -240,7 +255,7 @@ class Visualizor: vector2[i] = (vector2[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Received data" conf["type"] = "plot" @@ -270,7 +285,7 @@ class Visualizor: vector2[i] = (vector2[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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"] = "Duplicated data" conf["type"] = "plot" @@ -301,7 +316,7 @@ class Visualizor: vector1 += [np.nan] * (len(vector2) - len(vector1)) conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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["type"] = "grouped_bar" @@ -326,7 +341,7 @@ class Visualizor: """Plots the number of messages sent by all nodes""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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["type"] = "individual_bar" @@ -346,7 +361,7 @@ class Visualizor: """Plots the number of messages received by all nodes""" conf = {} text = str(result.shape).split("-") - conf["textBox"] = "Block SizeR: "+text[2]+"\nBlock SizeC: "+text[6]+"\nNumber of nodes: "+text[10]\ + 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["type"] = "individual_bar" diff --git a/smallConf.py b/smallConf.py index a92f76d..e4c91aa 100644 --- a/smallConf.py +++ b/smallConf.py @@ -40,10 +40,6 @@ logLevel = logging.INFO # for more details, see joblib.Parallel numJobs = -1 -# distribute rows/columns evenly between validators (True) -# or generate it using local randomness (False) -evenLineDistribution = True - # Number of simulation runs with the same parameters for statistical relevance runs = [1] @@ -64,7 +60,7 @@ maliciousNodes = [0] randomizeMaliciousNodes = True # Per-topic mesh neighborhood size -netDegrees = range(8, 9, 2) +netDegrees = [8] # ratio of class1 nodes (see below for parameters per class) class1ratios = [0.8] @@ -102,7 +98,7 @@ diagnostics = False # True to save git diff and git commit saveGit = False -blockSizeR =[128] +blockSizeR = [128] blockSizeC = [64] blockSizeRK = [64] blockSizeCK = [64] From d81a8c25374d08042a2446e33e3c98efba043f57 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Sun, 18 Feb 2024 22:51:19 +0530 Subject: [PATCH 14/32] fix label Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 6543b30..e706ae4 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -23,10 +23,10 @@ def plotData(conf): for i in range(len(conf["data"])): plt.bar(conf["xdots"], conf["data"][i], label=conf["labels"][i]) if conf["type"] == "individual_bar_with_2line": - plt.axhline(y = conf["expected_value1"], color='r', linestyle='--', label='Expected Value for class 1 nodes') - plt.axhline(y = conf["expected_value2"], color='g', linestyle='--', label='Expected Value for class 2 nodes') + plt.axhline(y = conf["expected_value1"], color='r', linestyle='--', label=conf["line_label1"]) + plt.axhline(y = conf["expected_value2"], color='g', linestyle='--', label=conf["line_label2"]) if conf["type"] == "plot_with_1line": - plt.axhline(y = conf["expected_value"], color='g', linestyle='--', label='Expected Value') + plt.axhline(y = conf["expected_value"], color='g', linestyle='--', label=conf["line_label"]) plt.title(conf["title"]) plt.ylabel(conf["ylabel"]) plt.xlabel(conf["xlabel"]) @@ -147,7 +147,7 @@ class Visualizor: conf["xdots"] = range(result.shape.numberNodes) conf["path"] = plotPath + "/sampleRecv.png" maxi = max(conf["data"]) - conf["yaxismax"] = maxi * 1.1 + # conf["yaxismax"] = maxi * 1.1 expected_percentage1 = (result.shape.vpn1 * (result.shape.blockSizeR * result.shape.chiR + result.shape.blockSizeC * result.shape.chiC) * 100)/total_samples expected_percentage2 = (result.shape.vpn2 * (result.shape.blockSizeR * result.shape.chiR + result.shape.blockSizeC * result.shape.chiC) * 100)/total_samples if expected_percentage1 > 100: @@ -156,6 +156,9 @@ class Visualizor: expected_percentage2 = 100 conf["expected_value1"] = expected_percentage1 conf["expected_value2"] = expected_percentage2 + conf["line_label1"] = "Expected Value for class 1 nodes" + conf["line_label2"] = "Expected Value for class 1 nodes" + conf["yaxismax"] = max(expected_percentage1, expected_percentage2) * 1.05 plotData(conf) print("Plot %s created." % conf["path"]) @@ -183,6 +186,7 @@ class Visualizor: conf["yaxismax"] = maxi x = result.shape.blockSizeR * result.shape.chiR + result.shape.blockSizeC * result.shape.chiC conf["expected_value"] = (result.shape.numberNodes - 1) * (result.shape.class1ratio * result.shape.vpn1 * x + (1 - result.shape.class1ratio) * result.shape.vpn2 * x) + conf["line_label"] = "Total samples to deliver" plotData(conf) print("Plot %s created." % conf["path"]) From 4ef26176748397f5c2c17ba0150cf33c886d64fd Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Mon, 19 Feb 2024 14:29:23 +0530 Subject: [PATCH 15/32] correct class label in graph Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index e706ae4..fbc62d2 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -157,7 +157,7 @@ class Visualizor: conf["expected_value1"] = expected_percentage1 conf["expected_value2"] = expected_percentage2 conf["line_label1"] = "Expected Value for class 1 nodes" - conf["line_label2"] = "Expected Value for class 1 nodes" + conf["line_label2"] = "Expected Value for class 2 nodes" conf["yaxismax"] = max(expected_percentage1, expected_percentage2) * 1.05 plotData(conf) print("Plot %s created." % conf["path"]) From be0ce303c6f7f152c0e1b4e5da31d1957ddd02a8 Mon Sep 17 00:00:00 2001 From: Sudipta Basak Date: Mon, 19 Feb 2024 23:20:53 +0100 Subject: [PATCH 16/32] Added New Plot 'No. of Repaired Samples by each Node' --- DAS/block.py | 8 ++++++-- DAS/results.py | 2 ++ DAS/validator.py | 7 +++++-- DAS/visualizor.py | 21 +++++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/DAS/block.py b/DAS/block.py index ef3f4f5..6d5c1d3 100644 --- a/DAS/block.py +++ b/DAS/block.py @@ -50,12 +50,14 @@ class Block: """ line = self.data[id::self.blockSizeR] success = line.count(1) + repairedSamples = 0 if success >= self.blockSizeCK: ret = ~line self.data[id::self.blockSizeR] = 1 + repairedSamples = len(line) - success else: ret = zeros(self.blockSizeC) - return ret + return ret, repairedSamples def getRow(self, rowID): """It returns the block row corresponding to rowID.""" @@ -71,12 +73,14 @@ class Block: """ line = self.data[id*self.blockSizeR:(id+1)*self.blockSizeR] success = line.count(1) + repairedSamples = 0 if success >= self.blockSizeRK: ret = ~line self.data[id*self.blockSizeR:(id+1)*self.blockSizeR] = 1 + repairedSamples = len(line) - success else: ret = zeros(self.blockSizeR) - return ret + return ret, repairedSamples def print(self): """It prints the block in the terminal (outside of the logger rules)).""" diff --git a/DAS/results.py b/DAS/results.py index 6fbf3a4..2cab701 100644 --- a/DAS/results.py +++ b/DAS/results.py @@ -22,6 +22,7 @@ class Result: self.sampleRecvCount = [0] * shape.numberNodes self.restoreRowCount = [0] * shape.numberNodes self.restoreColumnCount = [0] * shape.numberNodes + self.repairedSampleCount = [0] * shape.numberNodes def copyValidators(self, validators): """Copy information from simulator.validators to result.""" @@ -32,6 +33,7 @@ class Result: self.sampleRecvCount[i] = validators[i].sampleRecvCount self.restoreRowCount[i] = validators[i].restoreRowCount self.restoreColumnCount[i] = validators[i].restoreColumnCount + self.repairedSampleCount[i] = validators[i].repairedSampleCount def populate(self, shape, config, missingVector): """It populates part of the result data inside a vector.""" diff --git a/DAS/validator.py b/DAS/validator.py index ae99d6f..82bb050 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -62,6 +62,7 @@ class Validator: self.sampleRecvCount = 0 self.restoreRowCount = 0 self.restoreColumnCount = 0 + self.repairedSampleCount = 0 self.logger = logger if self.shape.chiR < 1 and self.shape.chiC < 1: self.logger.error("Chi has to be greater than 0", extra=self.format) @@ -518,7 +519,8 @@ class Validator: def restoreRow(self, id): """Restore a given row if repairable.""" - rep = self.block.repairRow(id) + rep, repairedSamples = self.block.repairRow(id) + self.repairedSampleCount += repairedSamples if (rep.any()): # If operation is based on send queues, segments should # be queued after successful repair. @@ -538,7 +540,8 @@ class Validator: def restoreColumn(self, id): """Restore a given column if repairable.""" - rep = self.block.repairColumn(id) + rep, repairedSamples = self.block.repairColumn(id) + self.repairedSampleCount += repairedSamples if (rep.any()): # If operation is based on send queues, segments should # be queued after successful repair. diff --git a/DAS/visualizor.py b/DAS/visualizor.py index fbc62d2..60ddfe3 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -86,6 +86,7 @@ class Visualizor: self.plotSentData(result, plotPath) self.plotRecvData(result, plotPath) self.plotDupData(result, plotPath) + self.plotSamplesRepaired(result, plotPath) if self.config.saveRCdist: self.plotRowCol(result, plotPath) @@ -380,3 +381,23 @@ class Visualizor: conf["yaxismax"] = maxi plotData(conf) print("Plot %s created." % conf["path"]) + + def plotSamplesRepaired(self, result, plotPath): + """Plots the number of samples repaired by all nodes""" + 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"] = "Nodes" + conf["ylabel"] = "Number of Samples Repaired" + conf["data"] = result.repairedSampleCount + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/repairedSampleCount.png" + maxi = max(conf["data"]) + conf["yaxismax"] = maxi + plotData(conf) + print("Plot %s created." % conf["path"]) From 11afac03b993cd9c83cc11f8dabafe6fa29e7881 Mon Sep 17 00:00:00 2001 From: Sudipta Basak Date: Wed, 21 Feb 2024 12:26:38 +0100 Subject: [PATCH 17/32] Added box plot for messages received & samples received by nodes --- DAS/results.py | 2 ++ DAS/visualizor.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/DAS/results.py b/DAS/results.py index 2cab701..f679702 100644 --- a/DAS/results.py +++ b/DAS/results.py @@ -23,6 +23,8 @@ class Result: self.restoreRowCount = [0] * shape.numberNodes self.restoreColumnCount = [0] * shape.numberNodes self.repairedSampleCount = [0] * shape.numberNodes + self.numberNodes = shape.numberNodes + self.class1ratio = shape.class1ratio def copyValidators(self, validators): """Copy information from simulator.validators to result.""" diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 60ddfe3..8ab83a0 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -34,6 +34,20 @@ def plotData(conf): plt.legend(loc=conf["legLoc"]) plt.savefig(conf["path"], bbox_inches="tight") +def plotBoxData(conf): + plt.clf() + plt.grid(True) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + num_boxes = len(conf["data"]) + positions = np.arange(num_boxes) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) + plt.boxplot(conf["data"], patch_artist=True, showmeans=True, meanline=True, positions=positions) + plt.title(conf["title"], fontsize=14) + plt.ylabel(conf["ylabel"], fontsize=12) + plt.xlabel(conf["xlabel"], fontsize=12) + plt.xticks(fontsize=10) + plt.yticks(fontsize=10) + plt.savefig(conf["path"], bbox_inches="tight") class Visualizor: """This class helps the visualization of the results""" @@ -80,7 +94,9 @@ class Visualizor: self.plotRestoreColumnCount(result, plotPath) self.plotMessagesSent(result, plotPath) self.plotMessagesRecv(result, plotPath) + self.plotBoxMessagesRecv(result, plotPath) self.plotSampleRecv(result, plotPath) + self.plotBoxSampleRecv(result, plotPath) self.plotMissingSamples(result, plotPath) self.plotProgress(result, plotPath) self.plotSentData(result, plotPath) @@ -163,6 +179,25 @@ class Visualizor: plotData(conf) print("Plot %s created." % conf["path"]) + def plotBoxSampleRecv(self, result, plotPath): + """Box Plot of the sampleRecv for each node""" + 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["type"] = "individual_bar_with_2line" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Number of samples received (%)" + n1 = int(result.numberNodes * result.class1ratio) + conf["data"] = [result.sampleRecvCount[1: n1], result.sampleRecvCount[n1: ]] + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/box_sampleRecv.png" + plotBoxData(conf) + print("Plot %s created." % conf["path"]) + def plotMissingSamples(self, result, plotPath): """Plots the missing samples in the network""" conf = {} @@ -381,6 +416,27 @@ class Visualizor: conf["yaxismax"] = maxi plotData(conf) print("Plot %s created." % conf["path"]) + + def plotBoxMessagesRecv(self, result, plotPath): + """Plots the number of messages received by all nodes""" + 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["type"] = "individual_bar" + conf["legLoc"] = 1 + conf["desLoc"] = 1 + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Number of Messages Received" + n1 = int(result.numberNodes * result.class1ratio) + conf["data"] = [result.msgRecvCount[1: n1], result.msgRecvCount[n1: ]] + conf["xdots"] = range(result.shape.numberNodes) + conf["path"] = plotPath + "/box_messagesRecv.png" + maxi = max(conf["data"]) + conf["yaxismax"] = maxi + plotBoxData(conf) + print("Plot %s created." % conf["path"]) def plotSamplesRepaired(self, result, plotPath): """Plots the number of samples repaired by all nodes""" From 7c33fec8da34e2fd3094132f9c5793f6442608d0 Mon Sep 17 00:00:00 2001 From: Sudipta Basak Date: Wed, 21 Feb 2024 12:54:02 +0100 Subject: [PATCH 18/32] Added Box Plot for Messages Sent by Each Node --- DAS/visualizor.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 8ab83a0..71cd0f6 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -93,6 +93,7 @@ class Visualizor: self.plotRestoreRowCount(result, plotPath) self.plotRestoreColumnCount(result, plotPath) self.plotMessagesSent(result, plotPath) + self.plotBoxMessagesSent(result, plotPath) self.plotMessagesRecv(result, plotPath) self.plotBoxMessagesRecv(result, plotPath) self.plotSampleRecv(result, plotPath) @@ -396,6 +397,21 @@ class Visualizor: conf["yaxismax"] = maxi plotData(conf) print("Plot %s created." % conf["path"]) + + def plotBoxMessagesSent(self, result, plotPath): + """Box Plot of the number of messages sent by all nodes""" + 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) + conf["data"] = [result.msgSentCount[1: n1], result.msgSentCount[n1: ]] + conf["path"] = plotPath + "/box_messagesSent.png" + plotBoxData(conf) + print("Plot %s created." % conf["path"]) def plotMessagesRecv(self, result, plotPath): """Plots the number of messages received by all nodes""" From d1d81a23cfca283050580452405fa5292d1e75e9 Mon Sep 17 00:00:00 2001 From: Leonardo Bautista-Gomez Date: Tue, 27 Feb 2024 20:37:38 +0100 Subject: [PATCH 19/32] Change chi to custody --- DAS/node.py | 10 +++++----- smallConf.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/DAS/node.py b/DAS/node.py index 6bd5bfd..a66636a 100644 --- a/DAS/node.py +++ b/DAS/node.py @@ -36,9 +36,9 @@ class Validator: self.rowIDs = rowIDs self.columnIDs = columnIDs -def initValidator(blockSizeC, chiR, blockSizeR, chiC): - rowIDs = set(random.sample(range(blockSizeC), chiR)) - columnIDs = set(random.sample(range(blockSizeR), chiC)) +def initValidator(blockSizeC, custodyRows, blockSizeR, custodyCols): + rowIDs = set(random.sample(range(blockSizeC), custodyRows)) + columnIDs = set(random.sample(range(blockSizeR), custodyCols)) return Validator(rowIDs, columnIDs) class Node: @@ -53,7 +53,7 @@ class Node: """It initializes the node, and eventual validators, following the simulation configuration in shape and config. If rows/columns are specified these are observed, otherwise (default) - chiR rows and chiC columns are selected randomly. + custodyRows rows and custodyCols columns are selected randomly. """ self.shape = shape @@ -93,7 +93,7 @@ class Node: self.statsRxDupInSlot = 0 self.statsRxDupPerSlot = [] - # Set uplink bandwidth. + # Set uplink bandwidth. # Assuming segments of ~560 bytes and timesteps of 50ms, we get # 1 Mbps ~= 1e6 mbps * 0.050 s / (560*8) bits ~= 11 segments/timestep if self.amIproposer: diff --git a/smallConf.py b/smallConf.py index b1434af..8ab5a32 100644 --- a/smallConf.py +++ b/smallConf.py @@ -63,7 +63,7 @@ blockSizes = range(64, 113, 128) netDegrees = range(8, 9, 2) # number of rows and columns a validator is interested in -chis = range(2, 3, 2) +custody = range(2, 3, 2) # ratio of class1 nodes (see below for parameters per class) class1ratios = [0.8] @@ -102,12 +102,12 @@ diagnostics = False saveGit = False def nextShape(): - for run, fm, fr, class1ratio, chi, vpn1, vpn2, blockSize, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product( - runs, failureModels, failureRates, class1ratios, chis, validatorsPerNode1, validatorsPerNode2, blockSizes, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): + for run, fm, fr, class1ratio, cust, vpn1, vpn2, blockSize, nn, netDegree, bwUplinkProd, bwUplink1, bwUplink2 in itertools.product( + runs, failureModels, failureRates, class1ratios, custody, validatorsPerNode1, validatorsPerNode2, blockSizes, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): # Network Degree has to be an even number if netDegree % 2 == 0: blockSizeR = blockSizeC = blockSize blockSizeRK = blockSizeCK = blockSize // 2 - chiR = chiC = chi - shape = Shape(blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, nn, fm, fr, class1ratio, chiR, chiC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) + custodyRows = custodyCols = cust + shape = Shape(blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, nn, fm, fr, class1ratio, custodyRows, custodyCols, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) yield shape From a1f43578dbdbda06bf653fbb60636ed39c0a243b Mon Sep 17 00:00:00 2001 From: Leonardo Bautista-Gomez Date: Tue, 27 Feb 2024 21:35:51 +0100 Subject: [PATCH 20/32] Change row and column custody. Rename blockSizeR and blockSizeC --- DAS/node.py | 90 +++++++++++++++++++++++++++-------------------- DAS/observer.py | 14 ++++---- DAS/shape.py | 28 +++++++-------- DAS/simulator.py | 62 ++++++++++++++++---------------- DAS/visualizer.py | 8 ++--- smallConf.py | 8 ++--- 6 files changed, 111 insertions(+), 99 deletions(-) diff --git a/DAS/node.py b/DAS/node.py index a66636a..8d160f3 100644 --- a/DAS/node.py +++ b/DAS/node.py @@ -36,9 +36,9 @@ class Validator: self.rowIDs = rowIDs self.columnIDs = columnIDs -def initValidator(blockSizeC, custodyRows, blockSizeR, custodyCols): - rowIDs = set(random.sample(range(blockSizeC), custodyRows)) - columnIDs = set(random.sample(range(blockSizeR), custodyCols)) +def initValidator(nbRows, custodyRows, nbCols, custodyCols): + rowIDs = set(random.sample(range(nbRows), custodyRows)) + columnIDs = set(random.sample(range(nbCols), custodyCols)) return Validator(rowIDs, columnIDs) class Node: @@ -60,8 +60,8 @@ class Node: FORMAT = "%(levelname)s : %(entity)s : %(message)s" self.ID = ID self.format = {"entity": "Val "+str(self.ID)} - self.block = Block(self.shape.blockSizeR, self.shape.blockSizeRK, self.shape.blockSizeC, self.shape.blockSizeCK) - self.receivedBlock = Block(self.shape.blockSizeR, self.shape.blockSizeRK, self.shape.blockSizeC, self.shape.blockSizeCK) + self.block = Block(self.shape.nbCols, self.shape.nbColsK, self.shape.nbRows, self.shape.nbRowsK) + self.receivedBlock = Block(self.shape.nbCols, self.shape.nbColsK, self.shape.nbRows, self.shape.nbRowsK) self.receivedQueue = deque() self.sendQueue = deque() self.amIproposer = amIproposer @@ -70,17 +70,29 @@ class Node: if amIproposer: self.nodeClass = 0 - self.rowIDs = range(shape.blockSizeC) - self.columnIDs = range(shape.blockSizeR) + self.rowIDs = range(shape.nbRows) + self.columnIDs = range(shape.nbCols) else: self.nodeClass = 1 if (self.ID <= shape.numberNodes * shape.class1ratio) else 2 self.vpn = len(validators) #TODO: needed by old code, change to fn self.rowIDs = set(rows) self.columnIDs = set(columns) - for v in validators: - self.rowIDs = self.rowIDs.union(v.rowIDs) - self.columnIDs = self.columnIDs.union(v.columnIDs) + if (self.vpn * self.shape.custodyRows) > self.shape.nbRows: + self.logger.warning("Row custody (*vpn) larger than number of rows!", extra=self.format) + self.rowIDs = range(nbRows) + else: + self.rowIDs = set(random.sample(range(self.shape.nbRows), self.vpn*self.shape.custodyRows)) + + if (self.vpn * self.shape.custodyCols) > self.shape.nbCols: + self.logger.warning("Column custody (*vpn) larger than number of columns!", extra=self.format) + self.columnIDs = range(nbCols) + else: + self.columnIDs = set(random.sample(range(self.shape.nbCols), self.vpn*self.shape.custodyCols)) + + #for v in validators: + # self.rowIDs = self.rowIDs.union(v.rowIDs) + # self.columnIDs = self.columnIDs.union(v.columnIDs) self.rowNeighbors = collections.defaultdict(dict) self.columnNeighbors = collections.defaultdict(dict) @@ -105,8 +117,8 @@ class Node: self.bwUplink *= 1e3 / 8 * config.stepDuration / config.segmentSize self.repairOnTheFly = True - self.sendLineUntilR = self.shape.blockSizeRK # stop sending on a p2p link if at least this amount of samples passed - self.sendLineUntilC = self.shape.blockSizeCK # stop sending on a p2p link if at least this amount of samples passed + self.sendLineUntilR = self.shape.nbColsK # stop sending on a p2p link if at least this amount of samples passed + self.sendLineUntilC = self.shape.nbRowsK # stop sending on a p2p link if at least this amount of samples passed self.perNeighborQueue = True # queue incoming messages to outgoing connections on arrival (as typical GossipSub impl) self.shuffleQueues = True # shuffle the order of picking from active queues of a sender node self.perNodeQueue = False # keep a global queue of incoming messages for later sequential dispatch @@ -131,53 +143,53 @@ class Node: else: self.logger.debug("Creating block...", extra=self.format) if self.shape.failureModel == "random": - order = [i for i in range(self.shape.blockSizeR * self.shape.blockSizeC)] + order = [i for i in range(self.shape.nbCols * self.shape.nbRows)] order = random.sample(order, int((1 - self.shape.failureRate/100) * len(order))) for i in order: self.block.data[i] = 1 elif self.shape.failureModel == "sequential": - order = [i for i in range(self.shape.blockSizeR * self.shape.blockSizeC)] + order = [i for i in range(self.shape.nbCols * self.shape.nbRows)] order = order[:int((1 - self.shape.failureRate/100) * len(order))] for i in order: self.block.data[i] = 1 elif self.shape.failureModel == "MEP": # Minimal size non-recoverable Erasure Pattern - for r in range(self.shape.blockSizeR): - for c in range(self.shape.blockSizeC): - if r > self.shape.blockSizeRK or c > self.shape.blockSizeCK: + for r in range(self.shape.nbCols): + for c in range(self.shape.nbRows): + if r > self.shape.nbColsK or c > self.shape.nbRowsK: self.block.setSegment(r,c) elif self.shape.failureModel == "MEP+1": # MEP +1 segment to make it recoverable - for r in range(self.shape.blockSizeR): - for c in range(self.shape.blockSizeC): - if r > self.shape.blockSizeRK or c > self.shape.blockSizeCK: + for r in range(self.shape.nbCols): + for c in range(self.shape.nbRows): + if r > self.shape.nbColsK or c > self.shape.nbRowsK: self.block.setSegment(r,c) self.block.setSegment(0, 0) elif self.shape.failureModel == "DEP": - assert(self.shape.blockSizeR == self.shape.blockSizeC and self.shape.blockSizeRK == self.shape.blockSizeCK) - for r in range(self.shape.blockSizeR): - for c in range(self.shape.blockSizeC): - if (r+c) % self.shape.blockSizeR > self.shape.blockSizeRK: + assert(self.shape.nbCols == self.shape.nbRows and self.shape.nbColsK == self.shape.nbRowsK) + for r in range(self.shape.nbCols): + for c in range(self.shape.nbRows): + if (r+c) % self.shape.nbCols > self.shape.nbColsK: self.block.setSegment(r,c) elif self.shape.failureModel == "DEP+1": - assert(self.shape.blockSizeR == self.shape.blockSizeC and self.shape.blockSizeRK == self.shape.blockSizeCK) - for r in range(self.shape.blockSizeR): - for c in range(self.shape.blockSizeC): - if (r+c) % self.shape.blockSizeR > self.shape.blockSizeRK: + assert(self.shape.nbCols == self.shape.nbRows and self.shape.nbColsK == self.shape.nbRowsK) + for r in range(self.shape.nbCols): + for c in range(self.shape.nbRows): + if (r+c) % self.shape.nbCols > self.shape.nbColsK: self.block.setSegment(r,c) self.block.setSegment(0, 0) elif self.shape.failureModel == "MREP": # Minimum size Recoverable Erasure Pattern - for r in range(self.shape.blockSizeR): - for c in range(self.shape.blockSizeC): - if r < self.shape.blockSizeRK or c < self.shape.blockSizeCK: + for r in range(self.shape.nbCols): + for c in range(self.shape.nbRows): + if r < self.shape.nbColsK or c < self.shape.nbRowsK: self.block.setSegment(r,c) elif self.shape.failureModel == "MREP-1": # make MREP non-recoverable - for r in range(self.shape.blockSizeR): - for c in range(self.shape.blockSizeC): - if r < self.shape.blockSizeRK or c < self.shape.blockSizeCK: + for r in range(self.shape.nbCols): + for c in range(self.shape.nbRows): + if r < self.shape.nbColsK or c < self.shape.nbRowsK: self.block.setSegment(r,c) self.block.setSegment(0, 0, 0) nbFailures = self.block.data.count(0) - measuredFailureRate = nbFailures * 100 / (self.shape.blockSizeR * self.shape.blockSizeC) + measuredFailureRate = nbFailures * 100 / (self.shape.nbCols * self.shape.nbRows) self.logger.debug("Number of failures: %d (%0.02f %%)", nbFailures, measuredFailureRate, extra=self.format) def getColumn(self, index): @@ -351,7 +363,7 @@ class Node: segmentsToSend = [] for rID, neighs in self.rowNeighbors.items(): line = self.getRow(rID) - needed = zeros(self.shape.blockSizeR) + needed = zeros(self.shape.nbCols) for neigh in neighs.values(): sentOrReceived = neigh.received | neigh.sent if sentOrReceived.count(1) < self.sendLineUntilR: @@ -364,7 +376,7 @@ class Node: for cID, neighs in self.columnNeighbors.items(): line = self.getColumn(cID) - needed = zeros(self.shape.blockSizeC) + needed = zeros(self.shape.nbRows) for neigh in neighs.values(): sentOrReceived = neigh.received | neigh.sent if sentOrReceived.count(1) < self.sendLineUntilC: @@ -426,7 +438,7 @@ class Node: while t: if self.rowIDs: rID = random.choice(self.rowIDs) - cID = random.randrange(0, self.shape.blockSizeR) + cID = random.randrange(0, self.shape.nbCols) if self.block.getSegment(rID, cID) : neigh = random.choice(list(self.rowNeighbors[rID].values())) if self.checkSegmentToNeigh(rID, cID, neigh): @@ -434,7 +446,7 @@ class Node: t = tries if self.columnIDs: cID = random.choice(self.columnIDs) - rID = random.randrange(0, self.shape.blockSizeC) + rID = random.randrange(0, self.shape.nbRows) if self.block.getSegment(rID, cID) : neigh = random.choice(list(self.columnNeighbors[cID].values())) if self.checkSegmentToNeigh(rID, cID, neigh): diff --git a/DAS/observer.py b/DAS/observer.py index f848837..ba94dad 100644 --- a/DAS/observer.py +++ b/DAS/observer.py @@ -11,11 +11,11 @@ class Observer: self.config = config self.format = {"entity": "Observer"} self.logger = logger - self.block = [0] * self.config.blockSizeR * self.config.blockSizeC - self.rows = [0] * self.config.blockSizeC - self.columns = [0] * self.config.blockSizeR - self.broadcasted = Block(self.config.blockSizeR, self.config.blockSizeRK, - self.config.blockSizeC, self.config.blockSizeCK) + self.block = [0] * self.config.nbCols * self.config.nbRows + self.rows = [0] * self.config.nbRows + self.columns = [0] * self.config.nbCols + self.broadcasted = Block(self.config.nbCols, self.config.nbColsK, + self.config.nbRows, self.config.nbRowsK) def checkRowsColumns(self, validators): @@ -27,7 +27,7 @@ class Observer: for c in val.columnIDs: self.columns[c] += 1 - for i in range(self.config.blockSizeC): + for i in range(self.config.nbRows): self.logger.debug("Row/Column %d have %d and %d validators assigned." % (i, self.rows[i], self.columns[i]), extra=self.format) if self.rows[i] == 0 or self.columns[i] == 0: self.logger.warning("There is a row/column that has not been assigned", extra=self.format) @@ -35,7 +35,7 @@ class Observer: def checkBroadcasted(self): """It checks how many broadcasted samples are still missing in the network.""" zeros = 0 - for i in range(self.blockSizeR * self.blockSizeC): + for i in range(self.nbCols * self.nbRows): if self.broadcasted.data[i] == 0: zeros += 1 if zeros > 0: diff --git a/DAS/shape.py b/DAS/shape.py index 0af4c10..8453945 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -3,21 +3,21 @@ class Shape: """This class represents a set of parameters for a specific simulation.""" - def __init__(self, blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, - numberNodes, failureModel, failureRate, class1ratio, chiR, chiC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): + def __init__(self, nbCols, nbColsK, nbRows, nbRowsK, + numberNodes, failureModel, failureRate, class1ratio, custodyRows, custodyCols, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): """Initializes the shape with the parameters passed in argument.""" self.run = run self.numberNodes = numberNodes - self.blockSizeR = blockSizeR - self.blockSizeRK = blockSizeRK - self.blockSizeC = blockSizeC - self.blockSizeCK = blockSizeCK + self.nbCols = nbCols + self.nbColsK = nbColsK + self.nbRows = nbRows + self.nbRowsK = nbRowsK self.failureModel = failureModel self.failureRate = failureRate self.netDegree = netDegree self.class1ratio = class1ratio - self.chiR = chiR - self.chiC = chiC + self.custodyRows = custodyRows + self.custodyCols = custodyCols self.vpn1 = vpn1 self.vpn2 = vpn2 self.bwUplinkProd = bwUplinkProd @@ -28,16 +28,16 @@ class Shape: def __repr__(self): """Returns a printable representation of the shape""" shastr = "" - shastr += "bsrn-"+str(self.blockSizeR) - shastr += "bsrk-"+str(self.blockSizeRK) - shastr += "bscn-"+str(self.blockSizeC) - shastr += "bsck-"+str(self.blockSizeCK) + shastr += "bsrn-"+str(self.nbCols) + shastr += "bsrk-"+str(self.nbColsK) + shastr += "bscn-"+str(self.nbRows) + shastr += "bsck-"+str(self.nbRowsK) shastr += "-nn-"+str(self.numberNodes) shastr += "-fm-"+str(self.failureModel) shastr += "-fr-"+str(self.failureRate) shastr += "-c1r-"+str(self.class1ratio) - shastr += "-chir-"+str(self.chiR) - shastr += "-chic-"+str(self.chiC) + shastr += "-cusr-"+str(self.custodyRows) + shastr += "-cusc-"+str(self.custodyCols) shastr += "-vpn1-"+str(self.vpn1) shastr += "-vpn2-"+str(self.vpn2) shastr += "-bwupprod-"+str(self.bwUplinkProd) diff --git a/DAS/simulator.py b/DAS/simulator.py index 8dc5d98..b11ad2d 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -54,20 +54,20 @@ class Simulator: lightVal = lightNodes * self.shape.vpn1 heavyVal = heavyNodes * self.shape.vpn2 totalValidators = lightVal + heavyVal - totalRows = totalValidators * self.shape.chiR - totalColumns = totalValidators * self.shape.chiC - rows = list(range(self.shape.blockSizeC)) * (int(totalRows/self.shape.blockSizeC)+1) - columns = list(range(self.shape.blockSizeR)) * (int(totalColumns/self.shape.blockSizeR)+1) + totalRows = totalValidators * self.shape.custodyRows + totalColumns = totalValidators * self.shape.custodyCols + rows = list(range(self.shape.nbRows)) * (int(totalRows/self.shape.nbRows)+1) + columns = list(range(self.shape.nbCols)) * (int(totalColumns/self.shape.nbCols)+1) rows = rows[0:totalRows] columns = columns[0:totalRows] random.shuffle(rows) random.shuffle(columns) - offsetR = lightVal*self.shape.chiR - offsetC = lightVal*self.shape.chiC + offsetR = lightVal*self.shape.custodyRows + offsetC = lightVal*self.shape.custodyCols self.logger.debug("There is a total of %d nodes, %d light and %d heavy." % (self.shape.numberNodes, lightNodes, heavyNodes), extra=self.format) self.logger.debug("There is a total of %d validators, %d in light nodes and %d in heavy nodes" % (totalValidators, lightVal, heavyVal), extra=self.format) - self.logger.debug("Shuffling a total of %d rows to be assigned (X=%d)" % (len(rows), self.shape.chiR), extra=self.format) - self.logger.debug("Shuffling a total of %d columns to be assigned (X=%d)" % (len(columns), self.shape.chiC), extra=self.format) + self.logger.debug("Shuffling a total of %d rows to be assigned (X=%d)" % (len(rows), self.shape.custodyRows), extra=self.format) + self.logger.debug("Shuffling a total of %d columns to be assigned (X=%d)" % (len(columns), self.shape.custodyCols), extra=self.format) self.logger.debug("Shuffled rows: %s" % str(rows), extra=self.format) self.logger.debug("Shuffled columns: %s" % str(columns), extra=self.format) @@ -76,16 +76,16 @@ class Simulator: for i in range(self.shape.numberNodes): if self.config.evenLineDistribution: if i < int(lightVal/self.shape.vpn1): # First start with the light nodes - startR = i *self.shape.chiR*self.shape.vpn1 - endR = (i+1)*self.shape.chiR*self.shape.vpn1 - startC = i *self.shape.chiC*self.shape.vpn1 - endC = (i+1)*self.shape.chiC*self.shape.vpn1 + startR = i *self.shape.custodyRows*self.shape.vpn1 + endR = (i+1)*self.shape.custodyRows*self.shape.vpn1 + startC = i *self.shape.custodyCols*self.shape.vpn1 + endC = (i+1)*self.shape.custodyCols*self.shape.vpn1 else: j = i - int(lightVal/self.shape.vpn1) - startR = offsetR+( j *self.shape.chiR*self.shape.vpn2) - endR = offsetR+((j+1)*self.shape.chiR*self.shape.vpn2) - startC = offsetC+( j *self.shape.chiC*self.shape.vpn2) - endC = offsetC+((j+1)*self.shape.chiC*self.shape.vpn2) + startR = offsetR+( j *self.shape.custodyRows*self.shape.vpn2) + endR = offsetR+((j+1)*self.shape.custodyRows*self.shape.vpn2) + startC = offsetC+( j *self.shape.custodyCols*self.shape.vpn2) + endC = offsetC+((j+1)*self.shape.custodyCols*self.shape.vpn2) r = rows[startR:endR] c = columns[startC:endC] val = Node(i, int(not i!=0), self.logger, self.shape, self.config, r, c) @@ -97,16 +97,16 @@ class Simulator: self.nodeColumns.append(val.columnIDs) else: - if self.shape.chiC > self.shape.blockSizeR: - self.logger.error("ChiC has to be smaller than %d" % self.shape.blockSizeR) - elif self.shape.chiR > self.shape.blockSizeC: - self.logger.error("ChiR has to be smaller than %d" % self.shape.blockSizeC) + if self.shape.custodyCols > self.shape.nbCols: + self.logger.error("custodyCols has to be smaller than %d" % self.shape.nbCols) + elif self.shape.custodyRows > self.shape.nbRows: + self.logger.error("custodyRows has to be smaller than %d" % self.shape.nbRows) vs = [] nodeClass = 1 if (i <= self.shape.numberNodes * self.shape.class1ratio) else 2 vpn = self.shape.vpn1 if (nodeClass == 1) else self.shape.vpn2 for v in range(vpn): - vs.append(initValidator(self.shape.blockSizeC, self.shape.chiR, self.shape.blockSizeR, self.shape.chiC)) + vs.append(initValidator(self.shape.nbRows, self.shape.custodyRows, self.shape.nbCols, self.shape.custodyCols)) val = Node(i, int(not i!=0), self.logger, self.shape, self.config, vs) if i == self.proposerID: val.initBlock() @@ -122,8 +122,8 @@ class Simulator: def initNetwork(self): """It initializes the simulated network.""" - rowChannels = [[] for i in range(self.shape.blockSizeC)] - columnChannels = [[] for i in range(self.shape.blockSizeR)] + rowChannels = [[] for i in range(self.shape.nbRows)] + columnChannels = [[] for i in range(self.shape.nbCols)] for v in self.validators: if not (self.proposerPublishOnly and v.amIproposer): for id in v.rowIDs: @@ -139,7 +139,7 @@ class Simulator: self.logger.debug("Number of validators per row; Min: %d, Max: %d" % (min(self.distR), max(self.distR)), extra=self.format) self.logger.debug("Number of validators per column; Min: %d, Max: %d" % (min(self.distC), max(self.distC)), extra=self.format) - for id in range(self.shape.blockSizeC): + for id in range(self.shape.nbRows): # If the number of nodes in a channel is smaller or equal to the # requested degree, a fully connected graph is used. For n>d, a random @@ -157,10 +157,10 @@ class Simulator: for u, v in G.edges: val1=rowChannels[id][u] val2=rowChannels[id][v] - val1.rowNeighbors[id].update({val2.ID : Neighbor(val2, 0, self.shape.blockSizeR)}) - val2.rowNeighbors[id].update({val1.ID : Neighbor(val1, 0, self.shape.blockSizeR)}) + val1.rowNeighbors[id].update({val2.ID : Neighbor(val2, 0, self.shape.nbCols)}) + val2.rowNeighbors[id].update({val1.ID : Neighbor(val1, 0, self.shape.nbCols)}) - for id in range(self.shape.blockSizeR): + for id in range(self.shape.nbCols): if not columnChannels[id]: self.logger.error("No nodes for column %d !" % id, extra=self.format) @@ -175,8 +175,8 @@ class Simulator: for u, v in G.edges: val1=columnChannels[id][u] val2=columnChannels[id][v] - val1.columnNeighbors[id].update({val2.ID : Neighbor(val2, 1, self.shape.blockSizeC)}) - val2.columnNeighbors[id].update({val1.ID : Neighbor(val1, 1, self.shape.blockSizeC)}) + val1.columnNeighbors[id].update({val2.ID : Neighbor(val2, 1, self.shape.nbRows)}) + val2.columnNeighbors[id].update({val1.ID : Neighbor(val1, 1, self.shape.nbRows)}) for v in self.validators: if (self.proposerPublishOnly and v.amIproposer): @@ -184,12 +184,12 @@ class Simulator: count = min(self.proposerPublishTo, len(rowChannels[id])) publishTo = random.sample(rowChannels[id], count) for vi in publishTo: - v.rowNeighbors[id].update({vi.ID : Neighbor(vi, 0, self.shape.blockSizeR)}) + v.rowNeighbors[id].update({vi.ID : Neighbor(vi, 0, self.shape.nbCols)}) for id in v.columnIDs: count = min(self.proposerPublishTo, len(columnChannels[id])) publishTo = random.sample(columnChannels[id], count) for vi in publishTo: - v.columnNeighbors[id].update({vi.ID : Neighbor(vi, 1, self.shape.blockSizeC)}) + v.columnNeighbors[id].update({vi.ID : Neighbor(vi, 1, self.shape.nbRows)}) if self.logger.isEnabledFor(logging.DEBUG): for i in range(0, self.shape.numberNodes): diff --git a/DAS/visualizer.py b/DAS/visualizer.py index 8aa0c91..bf285ae 100644 --- a/DAS/visualizer.py +++ b/DAS/visualizer.py @@ -16,7 +16,7 @@ class Visualizer: self.execID = execID self.config = config self.folderPath = "results/"+self.execID - self.parameters = ['run', 'blockSize', 'failureRate', 'numberNodes', 'netDegree', 'chi', 'vpn1', 'vpn2', 'class1ratio', 'bwUplinkProd', 'bwUplink1', 'bwUplink2'] + self.parameters = ['run', 'blockSize', 'failureRate', 'numberNodes', 'netDegree', 'cus', 'vpn1', 'vpn2', 'class1ratio', 'bwUplinkProd', 'bwUplink1', 'bwUplink2'] self.minimumDataPoints = 2 self.maxTTA = 11000 @@ -32,12 +32,12 @@ class Visualizer: tree = ET.parse(os.path.join(self.folderPath, filename)) root = tree.getroot() run = int(root.find('run').text) - blockSize = int(root.find('blockSizeR').text) # TODO: maybe we want both dimensions + blockSize = int(root.find('nbCols').text) # TODO: maybe we want both dimensions failureRate = int(root.find('failureRate').text) numberNodes = int(root.find('numberNodes').text) class1ratio = float(root.find('class1ratio').text) netDegree = int(root.find('netDegree').text) - chi = int(root.find('chiR').text) # TODO: maybe we want both dimensions + custodyRows = int(root.find('custodyRows').text) # TODO: maybe we want both dimensions vpn1 = int(root.find('vpn1').text) vpn2 = int(root.find('vpn2').text) bwUplinkProd = int(root.find('bwUplinkProd').text) @@ -53,7 +53,7 @@ class Visualizer: # Get the indices and values of the parameters in the combination indices = [self.parameters.index(element) for element in combination] - selectedValues = [run, blockSize, failureRate, numberNodes, netDegree, chi, vpn1, vpn2, class1ratio, bwUplinkProd, bwUplink1, bwUplink2] + selectedValues = [run, blockSize, failureRate, numberNodes, netDegree, custodyRows, vpn1, vpn2, class1ratio, bwUplinkProd, bwUplink1, bwUplink2] values = [selectedValues[index] for index in indices] names = [self.parameters[i] for i in indices] keyComponents = [f"{name}_{value}" for name, value in zip(names, values)] diff --git a/smallConf.py b/smallConf.py index 8ab5a32..02703b6 100644 --- a/smallConf.py +++ b/smallConf.py @@ -63,7 +63,7 @@ blockSizes = range(64, 113, 128) netDegrees = range(8, 9, 2) # number of rows and columns a validator is interested in -custody = range(2, 3, 2) +custody = [2] # ratio of class1 nodes (see below for parameters per class) class1ratios = [0.8] @@ -106,8 +106,8 @@ def nextShape(): runs, failureModels, failureRates, class1ratios, custody, validatorsPerNode1, validatorsPerNode2, blockSizes, numberNodes, netDegrees, bwUplinksProd, bwUplinks1, bwUplinks2): # Network Degree has to be an even number if netDegree % 2 == 0: - blockSizeR = blockSizeC = blockSize - blockSizeRK = blockSizeCK = blockSize // 2 + nbCols = nbRows = blockSize + nbColsK = nbRowsK = blockSize // 2 custodyRows = custodyCols = cust - shape = Shape(blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, nn, fm, fr, class1ratio, custodyRows, custodyCols, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) + shape = Shape(nbCols, nbColsK, nbRows, nbRowsK, nn, fm, fr, class1ratio, custodyRows, custodyCols, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) yield shape From 3292d70c1a1b7f6e4d9341ead2939c7eaa7b462c Mon Sep 17 00:00:00 2001 From: Leonardo Bautista-Gomez Date: Tue, 27 Feb 2024 21:55:46 +0100 Subject: [PATCH 21/32] Fix shape nbCols and nbRows --- DAS/node.py | 4 ++-- smallConf.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DAS/node.py b/DAS/node.py index 8d160f3..cd8d433 100644 --- a/DAS/node.py +++ b/DAS/node.py @@ -80,13 +80,13 @@ class Node: self.columnIDs = set(columns) if (self.vpn * self.shape.custodyRows) > self.shape.nbRows: self.logger.warning("Row custody (*vpn) larger than number of rows!", extra=self.format) - self.rowIDs = range(nbRows) + self.rowIDs = range(self.shape.nbRows) else: self.rowIDs = set(random.sample(range(self.shape.nbRows), self.vpn*self.shape.custodyRows)) if (self.vpn * self.shape.custodyCols) > self.shape.nbCols: self.logger.warning("Column custody (*vpn) larger than number of columns!", extra=self.format) - self.columnIDs = range(nbCols) + self.columnIDs = range(self.shape.nbCols) else: self.columnIDs = set(random.sample(range(self.shape.nbCols), self.vpn*self.shape.custodyCols)) diff --git a/smallConf.py b/smallConf.py index 02703b6..9a204f0 100644 --- a/smallConf.py +++ b/smallConf.py @@ -70,7 +70,7 @@ class1ratios = [0.8] # Number of validators per beacon node validatorsPerNode1 = [1] -validatorsPerNode2 = [500] +validatorsPerNode2 = [5] # Set uplink bandwidth in megabits/second bwUplinksProd = [200] From 575c55480fb5ec9d0abda225a357ab0a4b5bc173 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Wed, 28 Feb 2024 22:14:55 +0530 Subject: [PATCH 22/32] 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( From a634aa07e03013a99b6894a8e4e9170d41c0e37f Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Thu, 29 Feb 2024 14:28:13 +0100 Subject: [PATCH 23/32] add config.validatorBasedCustody the overall number of row/columns taken into custody by a node is determined by a base number (custody) and a class specific multiplier (validatorsPerNode). We support two models: - validatorsBasedCustody: each validator has a unique subset of size custody, and custody is the union of these. I.e. VPN is a "probabilistic multiplier" - !validatorsBasedCustody: VPN is interpreted as a simple custody multiplier Signed-off-by: Csaba Kiraly --- DAS/node.py | 27 ++++++++++++++------------- smallConf.py | 8 +++++++- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/DAS/node.py b/DAS/node.py index cd8d433..505067d 100644 --- a/DAS/node.py +++ b/DAS/node.py @@ -78,21 +78,22 @@ class Node: self.rowIDs = set(rows) self.columnIDs = set(columns) - if (self.vpn * self.shape.custodyRows) > self.shape.nbRows: - self.logger.warning("Row custody (*vpn) larger than number of rows!", extra=self.format) - self.rowIDs = range(self.shape.nbRows) + if config.validatorBasedCustody: + for v in validators: + self.rowIDs = self.rowIDs.union(v.rowIDs) + self.columnIDs = self.columnIDs.union(v.columnIDs) else: - self.rowIDs = set(random.sample(range(self.shape.nbRows), self.vpn*self.shape.custodyRows)) + if (self.vpn * self.shape.custodyRows) > self.shape.nbRows: + self.logger.warning("Row custody (*vpn) larger than number of rows!", extra=self.format) + self.rowIDs = range(self.shape.nbRows) + else: + self.rowIDs = set(random.sample(range(self.shape.nbRows), self.vpn*self.shape.custodyRows)) - if (self.vpn * self.shape.custodyCols) > self.shape.nbCols: - self.logger.warning("Column custody (*vpn) larger than number of columns!", extra=self.format) - self.columnIDs = range(self.shape.nbCols) - else: - self.columnIDs = set(random.sample(range(self.shape.nbCols), self.vpn*self.shape.custodyCols)) - - #for v in validators: - # self.rowIDs = self.rowIDs.union(v.rowIDs) - # self.columnIDs = self.columnIDs.union(v.columnIDs) + if (self.vpn * self.shape.custodyCols) > self.shape.nbCols: + self.logger.warning("Column custody (*vpn) larger than number of columns!", extra=self.format) + self.columnIDs = range(self.shape.nbCols) + else: + self.columnIDs = set(random.sample(range(self.shape.nbCols), self.vpn*self.shape.custodyCols)) self.rowNeighbors = collections.defaultdict(dict) self.columnNeighbors = collections.defaultdict(dict) diff --git a/smallConf.py b/smallConf.py index 9a204f0..841e6a9 100644 --- a/smallConf.py +++ b/smallConf.py @@ -62,7 +62,13 @@ blockSizes = range(64, 113, 128) # Per-topic mesh neighborhood size netDegrees = range(8, 9, 2) -# number of rows and columns a validator is interested in +# the overall number of row/columns taken into custody by a node is determined by +# a base number (custody) and a class specific multiplier (validatorsPerNode). +# We support two models: +# - validatorsBasedCustody: each validator has a unique subset of size custody, +# and custody is the union of these. I.e. VPN is a "probabilistic multiplier" +# - !validatorsBasedCustody: VPN is interpreted as a simple custody multiplier +validatorBasedCustody = False custody = [2] # ratio of class1 nodes (see below for parameters per class) From 14dc9543d7071abfae16c91c20e5d14879862aaa Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Thu, 29 Feb 2024 20:09:46 +0100 Subject: [PATCH 24/32] correcting class ration in graphs Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 0bd1721..973cd72 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -219,9 +219,6 @@ class Visualizor: 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""" @@ -234,7 +231,7 @@ class Visualizor: conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Samples Received" n1 = int(result.numberNodes * result.class1ratio) - data = [result.sampleRecvCount[1: n1], result.sampleRecvCount[n1: ]] + data = [result.sampleRecvCount[1: n1], result.sampleRecvCount[n1+1: ]] plt.figure(figsize=(8, 6)) sns.boxenplot(data=data, width=0.8) plt.xlabel(conf["xlabel"]) @@ -256,7 +253,7 @@ class Visualizor: conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Samples Repaired" n1 = int(result.numberNodes * result.class1ratio) - data = [result.repairedSampleCount[1: n1], result.repairedSampleCount[n1: ]] + data = [result.repairedSampleCount[1: n1], result.repairedSampleCount[n1+1: ]] plt.figure(figsize=(8, 6)) sns.boxenplot(data=data, width=0.8) plt.xlabel(conf["xlabel"]) @@ -305,7 +302,7 @@ class Visualizor: conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Messages Sent" n1 = int(result.numberNodes * result.class1ratio) - data = [result.msgSentCount[1: n1], result.msgSentCount[n1: ]] + data = [result.msgSentCount[1: n1], result.msgSentCount[n1+1: ]] labels = ["Class 1", "Class 2"] sns.boxenplot(data=data, palette="Set2", ax=plt.gca()) plt.xlabel(conf["xlabel"]) @@ -325,7 +322,7 @@ class Visualizor: conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Messages Received" n1 = int(result.numberNodes * result.class1ratio) - data = [result.msgRecvCount[1: n1], result.msgRecvCount[n1: ]] + data = [result.msgRecvCount[1: n1], result.msgRecvCount[n1+1: ]] labels = ["Class 1", "Class 2"] sns.boxenplot(data=data, palette="Set2", ax=plt.gca()) plt.xlabel(conf["xlabel"]) @@ -348,7 +345,7 @@ class Visualizor: 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["data"] = [result.repairedSampleCount[1: n1], result.repairedSampleCount[n1+1: ]] conf["path"] = plotPath + "/box_samplesRepaired.png" plotBoxData(conf) print("Plot %s created." % conf["path"]) @@ -461,7 +458,7 @@ class Visualizor: conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of samples received (%)" n1 = int(result.numberNodes * result.class1ratio) - conf["data"] = [result.sampleRecvCount[1: n1], result.sampleRecvCount[n1: ]] + conf["data"] = [result.sampleRecvCount[1: n1], result.sampleRecvCount[n1+1: ]] conf["xdots"] = range(result.shape.numberNodes) conf["path"] = plotPath + "/box_sampleRecv.png" plotBoxData(conf) @@ -676,7 +673,7 @@ class Visualizor: conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Messages Sent" n1 = int(result.numberNodes * result.class1ratio) - conf["data"] = [result.msgSentCount[1: n1], result.msgSentCount[n1: ]] + conf["data"] = [result.msgSentCount[1: n1], result.msgSentCount[n1+1: ]] conf["path"] = plotPath + "/box_messagesSent.png" plotBoxData(conf) print("Plot %s created." % conf["path"]) @@ -714,7 +711,7 @@ class Visualizor: conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Messages Received" n1 = int(result.numberNodes * result.class1ratio) - conf["data"] = [result.msgRecvCount[1: n1], result.msgRecvCount[n1: ]] + conf["data"] = [result.msgRecvCount[1: n1], result.msgRecvCount[n1+1: ]] conf["xdots"] = range(result.shape.numberNodes) conf["path"] = plotPath + "/box_messagesRecv.png" maxi = max(conf["data"]) From 911ee6b6e4ad9b36b695b39a3b7d768d2b170101 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Fri, 1 Mar 2024 10:46:24 +0100 Subject: [PATCH 25/32] fix validator progress counter if custody is based on the requirements of underlying individual validators, we can get detailed data on how many validated. Otherwise, we can only use the weighted average. Signed-off-by: Csaba Kiraly --- DAS/simulator.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/DAS/simulator.py b/DAS/simulator.py index b11ad2d..adae044 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -290,10 +290,18 @@ class Simulator: cnD1 = "Dup class1 mean" cnD2 = "Dup class2 mean" + # if custody is based on the requirements of underlying individual + # validators, we can get detailed data on how many validated. + # Otherwise, we can only use the weighted average. + if self.config.validatorBasedCustody: + cnVv = validatorProgress + else: + cnVv = validatorAllProgress + progressVector.append({ cnS:sampleProgress, cnN:nodeProgress, - cnV:validatorProgress, + cnV:cnVv, cnT0: trafficStats[0]["Tx"]["mean"], cnT1: trafficStats[1]["Tx"]["mean"], cnT2: trafficStats[2]["Tx"]["mean"], From bf064e40f306618becf34e4bc10f91073fe423e1 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Fri, 1 Mar 2024 19:55:21 +0100 Subject: [PATCH 26/32] Updated ECDF plots to plot data classwise Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 973cd72..025fc21 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -112,7 +112,6 @@ class Visualizor: self.plotBoxenSamplesRecv(result, plotPath) self.plotBoxenSamplesRepaired(result, plotPath) self.plotBoxenRowColDist(result, plotPath) - self.plotECDFSamplesRepaired(result, plotPath) self.plotECDFRowColDist(result, plotPath) self.plotECDFSamplesReceived(result, plotPath) @@ -132,11 +131,18 @@ class Visualizor: conf["title"] = "ECDF of Messages Sent by Nodes" conf["xlabel"] = "Number of Messages Sent" conf["ylabel"] = "ECDF" - sns.ecdfplot(data=result.msgSentCount) + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.msgSentCount[1: n1] + class2_data = result.msgSentCount[n1+1: ] + sns.ecdfplot(data=class1_data, label='Class 1 Nodes') + sns.ecdfplot(data=class2_data, label='Class 2 Nodes') + plt.legend(title='Node Class', labels=['Class 1 Nodes', 'Class 2 Nodes'], loc=1) plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) plt.xlim(left=0, right=max(result.msgSentCount) * 1.1) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/ecdf_messagesSent.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/ecdf_messagesSent.png")) @@ -150,11 +156,18 @@ class Visualizor: conf["title"] = "ECDF of Messages Received by Nodes" conf["xlabel"] = "Number of Messages Received" conf["ylabel"] = "ECDF" - sns.ecdfplot(data=result.msgRecvCount) + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.msgRecvCount[1: n1] + class2_data = result.msgRecvCount[n1+1: ] + sns.ecdfplot(data=class1_data, label='Class 1 Nodes') + sns.ecdfplot(data=class2_data, label='Class 2 Nodes') + plt.legend(title='Node Class', labels=['Class 1 Nodes', 'Class 2 Nodes'], loc=1) plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) plt.xlim(left=0, right=max(result.msgRecvCount) * 1.1) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/ecdf_messagesRecv.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/ecdf_messagesRecv.png")) @@ -168,11 +181,18 @@ class Visualizor: conf["title"] = "ECDF of Samples Received by Nodes" conf["xlabel"] = "Number of Samples Received" conf["ylabel"] = "ECDF" - sns.ecdfplot(data=result.sampleRecvCount) + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.sampleRecvCount[1: n1] + class2_data = result.sampleRecvCount[n1+1: ] + sns.ecdfplot(data=class1_data, label='Class 1 Nodes') + sns.ecdfplot(data=class2_data, label='Class 2 Nodes') + plt.legend(title='Node Class', labels=['Class 1 Nodes', 'Class 2 Nodes'], loc=1) plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) plt.xlim(left=0, right=max(result.sampleRecvCount) * 1.1) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/ecdf_samplesReceived.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/ecdf_samplesReceived.png")) @@ -188,17 +208,17 @@ class Visualizor: 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"]) + sns.ecdfplot(data=vector1, label='Rows') + sns.ecdfplot(data=vector2, label='Columns') 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.legend(labels=['Row Dist', 'Column Dist'], loc=1) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/ecdf_rowColDist.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/ecdf_rowColDist.png")) @@ -212,11 +232,18 @@ class Visualizor: conf["title"] = "ECDF of Samples Repaired by Nodes" conf["xlabel"] = "Number of Samples Repaired" conf["ylabel"] = "ECDF" - sns.ecdfplot(data=result.repairedSampleCount) + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.repairedSampleCount[1: n1] + class2_data = result.repairedSampleCount[n1+1: ] + sns.ecdfplot(data=class1_data, label='Class 1 Nodes') + sns.ecdfplot(data=class2_data, label='Class 2 Nodes') + plt.legend(title='Node Class', labels=['Class 1 Nodes', 'Class 2 Nodes']) plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) plt.xlim(left=0, right=max(result.repairedSampleCount) * 1.1) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/ecdf_samplesRepaired.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/ecdf_samplesRepaired.png")) @@ -237,6 +264,8 @@ class Visualizor: plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.tight_layout() plt.savefig(plotPath + "/boxen_samplesRecv.png") plt.close() @@ -259,6 +288,8 @@ class Visualizor: plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.tight_layout() plt.savefig(plotPath + "/boxen_samplesRepaired.png") plt.close() @@ -286,6 +317,8 @@ class Visualizor: plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.tight_layout() plt.savefig(plotPath + "/boxen_rowColDist.png") plt.close() @@ -308,6 +341,8 @@ class Visualizor: plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/boxen_messagesSent.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/boxen_messagesSent.png")) @@ -328,6 +363,8 @@ class Visualizor: plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/boxen_messagesRecv.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/boxen_messagesRecv.png")) From 1893cc7bd0e2f89df8a6e3941605bc0e44457d60 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Fri, 1 Mar 2024 20:11:49 +0100 Subject: [PATCH 27/32] comment out bar plots & box plots Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 50 ++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 025fc21..51634ea 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -91,34 +91,41 @@ class Visualizor: for result in self.results: plotPath = "results/"+self.execID+"/plots/"+str(result.shape) os.makedirs(plotPath, exist_ok=True) - self.plotRestoreRowCount(result, plotPath) - self.plotRestoreColumnCount(result, plotPath) - self.plotMessagesSent(result, plotPath) - self.plotBoxMessagesSent(result, plotPath) - self.plotMessagesRecv(result, plotPath) - self.plotBoxMessagesRecv(result, plotPath) - self.plotSampleRecv(result, plotPath) - self.plotBoxSampleRecv(result, plotPath) self.plotMissingSamples(result, plotPath) self.plotProgress(result, plotPath) self.plotSentData(result, plotPath) 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.plotRestoreRowCount(result, plotPath) + self.plotRestoreColumnCount(result, plotPath) + + # self.plotSamplesRepaired(result, plotPath) + # self.plotMessagesSent(result, plotPath) + # self.plotMessagesRecv(result, plotPath) + # self.plotSampleRecv(result, plotPath) + # if self.config.saveRCdist: + # self.plotRowCol(result, plotPath) + + # self.plotBoxSamplesRepaired(result, plotPath) + # self.plotBoxMessagesSent(result, plotPath) + # self.plotBoxMessagesRecv(result, plotPath) + # self.plotBoxSampleRecv(result, plotPath) + # if self.config.saveRCdist: + # self.plotBoxRowCol(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) + self.plotBoxenMessagesSent(result, plotPath) + self.plotBoxenMessagesRecv(result, plotPath) + self.plotBoxenSamplesRecv(result, plotPath) if self.config.saveRCdist: - self.plotRowCol(result, plotPath) + self.plotBoxenRowColDist(result, plotPath) + + self.plotECDFSamplesRepaired(result, plotPath) + self.plotECDFMessagesSent(result, plotPath) + self.plotECDFMessagesRecv(result, plotPath) + self.plotECDFSamplesReceived(result, plotPath) + if self.config.saveRCdist: + self.plotECDFRowColDist(result, plotPath) def plotECDFMessagesSent(self, result, plotPath): @@ -209,7 +216,6 @@ class Visualizor: vector1 = result.metrics["rowDist"] vector2 = result.metrics["columnDist"] n1 = int(result.numberNodes * result.class1ratio) - conf["data"] = [vector1, vector2] sns.ecdfplot(data=vector1, label='Rows') sns.ecdfplot(data=vector2, label='Columns') plt.xlabel(conf["xlabel"]) From 62a2116434bcf74b0779aca5169fa79548ca75e9 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Sat, 2 Mar 2024 13:05:24 +0000 Subject: [PATCH 28/32] add ecdf, boxen, box plots for restore row & col Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 148 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 2 deletions(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 51634ea..799e203 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -96,13 +96,13 @@ class Visualizor: self.plotSentData(result, plotPath) self.plotRecvData(result, plotPath) self.plotDupData(result, plotPath) - self.plotRestoreRowCount(result, plotPath) - self.plotRestoreColumnCount(result, plotPath) # self.plotSamplesRepaired(result, plotPath) # self.plotMessagesSent(result, plotPath) # self.plotMessagesRecv(result, plotPath) # self.plotSampleRecv(result, plotPath) + # self.plotRestoreRowCount(result, plotPath) + # self.plotRestoreColumnCount(result, plotPath) # if self.config.saveRCdist: # self.plotRowCol(result, plotPath) @@ -110,6 +110,8 @@ class Visualizor: # self.plotBoxMessagesSent(result, plotPath) # self.plotBoxMessagesRecv(result, plotPath) # self.plotBoxSampleRecv(result, plotPath) + # self.plotBoxRestoreColumnCount(result, plotPath) + # self.plotBoxRestoreRowCount(result, plotPath) # if self.config.saveRCdist: # self.plotBoxRowCol(result, plotPath) @@ -117,6 +119,8 @@ class Visualizor: self.plotBoxenMessagesSent(result, plotPath) self.plotBoxenMessagesRecv(result, plotPath) self.plotBoxenSamplesRecv(result, plotPath) + self.plotBoxenRestoreRowCount(result, plotPath) + self.plotBoxenRestoreColumnCount(result, plotPath) if self.config.saveRCdist: self.plotBoxenRowColDist(result, plotPath) @@ -124,10 +128,150 @@ class Visualizor: self.plotECDFMessagesSent(result, plotPath) self.plotECDFMessagesRecv(result, plotPath) self.plotECDFSamplesReceived(result, plotPath) + self.plotECDFRestoreRowCount(result, plotPath) + self.plotECDFRestoreColumnCount(result, plotPath) if self.config.saveRCdist: self.plotECDFRowColDist(result, plotPath) + def plotBoxRestoreRowCount(self, result, plotPath): + """Box Plot of restoreRowCount for 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"] = "Box Plot of Restore Row Count by Nodes" + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Restore Row Count" + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.restoreRowCount[1: n1] + class2_data = result.restoreRowCount[n1+1: ] + data = [class1_data, class2_data] + plt.boxplot(data) + plt.xticks([1, 2], ['Class 1 Nodes', 'Class 2 Nodes']) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.savefig(plotPath + "/box_restoreRowCount.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/box_restoreRowCount.png")) + + def plotBoxRestoreColumnCount(self, result, plotPath): + """Box Plot of restoreColumnCount for 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"] = "Box Plot of Restore Column Count by Nodes" + conf["xlabel"] = "Node Type" + conf["ylabel"] = "Restore Column Count" + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.restoreColumnCount[1: n1] + class2_data = result.restoreColumnCount[n1+1: ] + data = [class1_data, class2_data] + plt.boxplot(data) + plt.xticks([1, 2], ['Class 1 Nodes', 'Class 2 Nodes']) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.savefig(plotPath + "/box_restoreColumnCount.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/box_restoreColumnCount.png")) + + def plotBoxenRestoreRowCount(self, result, plotPath): + """Plots the Boxen plot of restoreRowCount for 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"] = "Boxen Plot of Restore Row Count by Nodes" + conf["xlabel"] = "Restore Row Count" + conf["ylabel"] = "Nodes" + n1 = int(result.numberNodes * result.class1ratio) + data = [result.restoreRowCount[1: n1], result.restoreRowCount[n1+1: ]] + plt.figure(figsize=(8, 6)) + sns.boxenplot(data=data, width=0.8) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) + plt.savefig(plotPath + "/boxen_restoreRowCount.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/boxen_restoreRowCount.png")) + + def plotBoxenRestoreColumnCount(self, result, plotPath): + """Plots the Boxen plot of restoreColumnCount for 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"] = "Boxen Plot of Restore Column Count by Nodes" + conf["xlabel"] = "Restore Column Count" + conf["ylabel"] = "Nodes" + n1 = int(result.numberNodes * result.class1ratio) + data = [result.restoreColumnCount[1: n1], result.restoreColumnCount[n1+1: ]] + plt.figure(figsize=(8, 6)) + sns.boxenplot(data=data, width=0.8) + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) + plt.savefig(plotPath + "/boxen_restoreColumnCount.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/boxen_restoreColumnCount.png")) + + def plotECDFRestoreRowCount(self, result, plotPath): + """Plots the ECDF of restoreRowCount for 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 Restore Row Count by Nodes" + conf["xlabel"] = "Restore Row Count" + conf["ylabel"] = "ECDF" + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.restoreRowCount[1: n1] + class2_data = result.restoreRowCount[n1+1: ] + sns.ecdfplot(data=class1_data, label='Class 1 Nodes') + sns.ecdfplot(data=class2_data, label='Class 2 Nodes') + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.xlim(left=0, right=max(result.restoreRowCount) * 1.1) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) + plt.legend(title='Node Class', labels=['Class 1 Nodes', 'Class 2 Nodes'], loc=1) + plt.savefig(plotPath + "/ecdf_restoreRowCount.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/ecdf_restoreRowCount.png")) + + def plotECDFRestoreColumnCount(self, result, plotPath): + """Plots the ECDF of restoreColumnCount for 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 Restore Column Count by Nodes" + conf["xlabel"] = "Restore Column Count" + conf["ylabel"] = "ECDF" + n1 = int(result.numberNodes * result.class1ratio) + class1_data = result.restoreColumnCount[1: n1] + class2_data = result.restoreColumnCount[n1+1: ] + sns.ecdfplot(data=class1_data, label='Class 1 Nodes') + sns.ecdfplot(data=class2_data, label='Class 2 Nodes') + plt.xlabel(conf["xlabel"]) + plt.ylabel(conf["ylabel"]) + plt.title(conf["title"]) + plt.xlim(left=0, right=max(result.restoreColumnCount) * 1.1) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) + plt.legend(title='Node Class', labels=['Class 1 Nodes', 'Class 2 Nodes'], loc=1) + plt.savefig(plotPath + "/ecdf_restoreColumnCount.png", bbox_inches="tight") + print("Plot %s created." % (plotPath + "/ecdf_restoreColumnCount.png")) + def plotECDFMessagesSent(self, result, plotPath): """Plots the ECDF of messages sent by all nodes using seaborn's ecdfplot""" plt.clf() From 08883e560f990b0a024b8a2112c5bda67c8922d5 Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Sat, 2 Mar 2024 13:10:25 +0000 Subject: [PATCH 29/32] add plt.text Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 799e203..85a7b4e 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -153,6 +153,8 @@ class Visualizor: plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/box_restoreRowCount.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/box_restoreRowCount.png")) @@ -175,6 +177,8 @@ class Visualizor: plt.xlabel(conf["xlabel"]) plt.ylabel(conf["ylabel"]) plt.title(conf["title"]) + props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) + plt.text(0.05, 0.05, conf["textBox"], fontsize=10, verticalalignment='bottom', transform=plt.gca().transAxes, bbox=props) plt.savefig(plotPath + "/box_restoreColumnCount.png", bbox_inches="tight") print("Plot %s created." % (plotPath + "/box_restoreColumnCount.png")) From d92468b69b82aafe244be5d7cb145a8271f5464e Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Sun, 3 Mar 2024 02:25:07 +0530 Subject: [PATCH 30/32] correct index Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 2f250a3..dcca2a3 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -63,7 +63,7 @@ class Visualizor: def __get_attrbs__(self, result): text = str(result.shape).split("-") d = dict() - for i in range(0, len(text), 2): + for i in range(1, len(text), 2): d[text[i]] = text[i + 1] return d From 2add749fb5acda8c9aec4bfac37511536d47b35c Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Sun, 3 Mar 2024 18:34:16 +0530 Subject: [PATCH 31/32] clean code of graphs Signed-off-by: Arunima Chaudhuri --- DAS/visualizor.py | 178 +++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index dcca2a3..b57cfd7 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -171,9 +171,9 @@ class Visualizor: """Box Plot of restoreRowCount for 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Box Plot of Restore Row Count by Nodes" conf["xlabel"] = "Node Type" conf["ylabel"] = "Restore Row Count" @@ -195,9 +195,9 @@ class Visualizor: """Box Plot of restoreColumnCount for 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Box Plot of Restore Column Count by Nodes" conf["xlabel"] = "Node Type" conf["ylabel"] = "Restore Column Count" @@ -219,9 +219,9 @@ class Visualizor: """Plots the Boxen plot of restoreRowCount for 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Boxen Plot of Restore Row Count by Nodes" conf["xlabel"] = "Restore Row Count" conf["ylabel"] = "Nodes" @@ -241,9 +241,9 @@ class Visualizor: """Plots the Boxen plot of restoreColumnCount for 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Boxen Plot of Restore Column Count by Nodes" conf["xlabel"] = "Restore Column Count" conf["ylabel"] = "Nodes" @@ -263,9 +263,9 @@ class Visualizor: """Plots the ECDF of restoreRowCount for 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "ECDF of Restore Row Count by Nodes" conf["xlabel"] = "Restore Row Count" conf["ylabel"] = "ECDF" @@ -288,9 +288,9 @@ class Visualizor: """Plots the ECDF of restoreColumnCount for 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "ECDF of Restore Column Count by Nodes" conf["xlabel"] = "Restore Column Count" conf["ylabel"] = "ECDF" @@ -313,9 +313,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "ECDF of Messages Sent by Nodes" conf["xlabel"] = "Number of Messages Sent" conf["ylabel"] = "ECDF" @@ -338,9 +338,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "ECDF of Messages Received by Nodes" conf["xlabel"] = "Number of Messages Received" conf["ylabel"] = "ECDF" @@ -363,9 +363,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "ECDF of Samples Received by Nodes" conf["xlabel"] = "Number of Samples Received" conf["ylabel"] = "ECDF" @@ -388,9 +388,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "ECDF of Row-Col Distance by Nodes" conf["xlabel"] = "Row-Col Distance" conf["ylabel"] = "ECDF" @@ -413,9 +413,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "ECDF of Samples Repaired by Nodes" conf["xlabel"] = "Number of Samples Repaired" conf["ylabel"] = "ECDF" @@ -438,9 +438,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Samples Received by Nodes" conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Samples Received" @@ -462,9 +462,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Samples Repaired by Nodes" conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Samples Repaired" @@ -486,9 +486,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Row/Column Distribution" conf["xlabel"] = "Row/Column Type" conf["ylabel"] = "Validators Subscribed" @@ -515,9 +515,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Messages Sent by Nodes" conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Messages Sent" @@ -537,9 +537,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Messages Received by Nodes" conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Messages Received" @@ -559,9 +559,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Samples Repaired by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -578,9 +578,9 @@ class Visualizor: """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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Row/Column Distribution" conf["xlabel"] = "" conf["ylabel"] = "Validators Subscribed" @@ -599,9 +599,9 @@ class Visualizor: def plotRestoreRowCount(self, result, plotPath): """Plots the restoreRowCount for each node""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Restore Row Count for Each Node" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -619,9 +619,9 @@ class Visualizor: def plotRestoreColumnCount(self, result, plotPath): """Plots the restoreColumnCount for each node""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Restore Column Count for Each Node" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -639,9 +639,9 @@ class Visualizor: def plotSampleRecv(self, result, plotPath): """Plots the percentage sampleRecv for each node""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Percentage of Samples Received by Nodes" conf["type"] = "individual_bar_with_2line" conf["legLoc"] = 1 @@ -672,9 +672,9 @@ class Visualizor: def plotBoxSampleRecv(self, result, plotPath): """Box Plot of the sampleRecv for each node""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Samples Received by Nodes" conf["type"] = "individual_bar_with_2line" conf["legLoc"] = 1 @@ -691,9 +691,9 @@ class Visualizor: def plotMissingSamples(self, result, plotPath): """Plots the missing samples in the network""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Missing Samples" conf["type"] = "plot_with_1line" conf["legLoc"] = 1 @@ -724,7 +724,7 @@ class Visualizor: conf = {} attrbs = self.__get_attrbs__(result) conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ - +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+" \nNetwork degree: "+attrbs['nd'] + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Nodes/validators ready" conf["type"] = "plot" conf["legLoc"] = 2 @@ -756,7 +756,7 @@ class Visualizor: conf = {} attrbs = self.__get_attrbs__(result) conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ - +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+" \nNetwork degree: "+attrbs['nd'] + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Sent data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -786,7 +786,7 @@ class Visualizor: conf = {} attrbs = self.__get_attrbs__(result) conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ - +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+" \nNetwork degree: "+attrbs['nd'] + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Received data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -816,7 +816,7 @@ class Visualizor: conf = {} attrbs = self.__get_attrbs__(result) conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ - +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+" \nNetwork degree: "+attrbs['nd'] + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Duplicated data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -847,7 +847,7 @@ class Visualizor: conf = {} attrbs = self.__get_attrbs__(result) conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ - +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+" \nNetwork degree: "+attrbs['nd'] + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Row/Column distribution" conf["type"] = "grouped_bar" conf["legLoc"] = 2 @@ -870,9 +870,9 @@ class Visualizor: def plotMessagesSent(self, result, plotPath): """Plots the number of messages sent by all nodes""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Messages Sent by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -890,9 +890,9 @@ class Visualizor: def plotBoxMessagesSent(self, result, plotPath): """Box Plot of the number of messages sent by all nodes""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Messages Sent by Nodes" conf["xlabel"] = "Node Type" conf["ylabel"] = "Number of Messages Sent" @@ -905,9 +905,9 @@ class Visualizor: def plotMessagesRecv(self, result, plotPath): """Plots the number of messages received by all nodes""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Messages Received by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -925,9 +925,9 @@ class Visualizor: def plotBoxMessagesRecv(self, result, plotPath): """Plots the number of messages received by all nodes""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Messages Received by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 @@ -946,9 +946,9 @@ class Visualizor: def plotSamplesRepaired(self, result, plotPath): """Plots the number of samples repaired by all nodes""" 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]+"%" + attrbs = self.__get_attrbs__(result) + conf["textBox"] = "Block Size R: "+attrbs['bsrn']+"\nBlock Size C: "+attrbs['bscn']\ + +"\nNumber of nodes: "+attrbs['nn']+"\nFailure rate: "+attrbs['fr']+"\nMalicious Node: "+attrbs['mn']+"\nNetwork degree: "+attrbs['nd'] conf["title"] = "Number of Samples Repaired by Nodes" conf["type"] = "individual_bar" conf["legLoc"] = 1 From 04b43b67d16a3569100d3df2f6ccc41cadf24ea9 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 4 Mar 2024 16:40:18 +0100 Subject: [PATCH 32/32] fixup results file name Signed-off-by: Csaba Kiraly --- DAS/shape.py | 2 +- DAS/visualizor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DAS/shape.py b/DAS/shape.py index 1047988..bff6485 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -28,7 +28,7 @@ class Shape: def __repr__(self): """Returns a printable representation of the shape""" shastr = "" - shastr += "-bsrn-"+str(self.blockSizeR) + shastr += "bsrn-"+str(self.blockSizeR) shastr += "-bsrk-"+str(self.blockSizeRK) shastr += "-bscn-"+str(self.blockSizeC) shastr += "-bsck-"+str(self.blockSizeCK) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index b57cfd7..db56dfb 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -63,7 +63,7 @@ class Visualizor: def __get_attrbs__(self, result): text = str(result.shape).split("-") d = dict() - for i in range(1, len(text), 2): + for i in range(0, len(text), 2): d[text[i]] = text[i + 1] return d