From f21a9ddb0195c4738abb43694890dbd57fb15106 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 11 Jul 2023 12:48:56 +0200 Subject: [PATCH 01/13] block: handle 2 dimensions separately Signed-off-by: Csaba Kiraly --- DAS/block.py | 53 ++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/DAS/block.py b/DAS/block.py index f76a944..de56d49 100644 --- a/DAS/block.py +++ b/DAS/block.py @@ -7,10 +7,15 @@ 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, blockSizeC=0): + """Initialize the block with a data array of blocksize^2 zeros. + + BlockSizeR: row size + BlockSizeC: column size (i.e. number of rows) + """ + self.blockSizeR = blockSizeR + self.blockSizeC = blockSizeC if blockSizeC else blockSizeR + self.data = zeros(self.blockSizeR*self.blockSizeC) def fill(self): """It fills the block data with ones.""" @@ -22,62 +27,62 @@ 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 blockSizeC/2 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.blockSizeC/2: 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 blockSizeR/2 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.blockSizeR/2: 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) From e18822b8aa2241766f6551e3e1aaab0d430035e4 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 12 Jul 2023 13:21:27 +0200 Subject: [PATCH 02/13] handle 2 dimensions separately (except visualizer) Signed-off-by: Csaba Kiraly # Conflicts: # DAS/simulator.py # DAS/validator.py --- DAS/observer.py | 12 +++---- DAS/shape.py | 8 +++-- DAS/simulator.py | 24 +++++++------- DAS/validator.py | 81 +++++++++++++++++++++++++----------------------- 4 files changed, 66 insertions(+), 59 deletions(-) diff --git a/DAS/observer.py b/DAS/observer.py index beba4ad..d57c69a 100644 --- a/DAS/observer.py +++ b/DAS/observer.py @@ -11,10 +11,10 @@ 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.blockSizeC) def checkRowsColumns(self, validators): @@ -26,7 +26,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 +34,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: diff --git a/DAS/shape.py b/DAS/shape.py index 9f6d573..4723258 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -3,11 +3,12 @@ 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, blockSizeR, blockSizeC, numberNodes, failureModel, failureRate, 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.blockSizeR = blockSizeR + self.blockSizeC = blockSizeC self.failureModel = failureModel self.failureRate = failureRate self.netDegree = netDegree @@ -23,7 +24,8 @@ class Shape: def __repr__(self): """Returns a printable representation of the shape""" shastr = "" - shastr += "bs-"+str(self.blockSize) + shastr += "bsr-"+str(self.blockSizeR) + shastr += "bsc-"+str(self.blockSizeC) shastr += "-nn-"+str(self.numberNodes) shastr += "-fm-"+str(self.failureModel) shastr += "-fr-"+str(self.failureRate) diff --git a/DAS/simulator.py b/DAS/simulator.py index 174a9b2..1925063 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -55,8 +55,8 @@ class Simulator: 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) + rows = list(range(self.shape.blockSizeC)) * (int(totalRows/self.shape.blockSizeC)+1) + columns = list(range(self.shape.blockSizeR)) * (int(totalRows/self.shape.blockSizeR)+1) rows = rows[0:totalRows] columns = columns[0:totalRows] random.shuffle(rows) @@ -105,8 +105,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: @@ -122,7 +122,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 @@ -140,8 +140,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.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) @@ -156,8 +158,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): @@ -165,12 +167,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): diff --git a/DAS/validator.py b/DAS/validator.py index 4e8d350..e75cb7d 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -49,21 +49,23 @@ class Validator: 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.blockSizeC) + self.receivedBlock = Block(self.shape.blockSizeR, self.shape.blockSizeC) self.receivedQueue = deque() self.sendQueue = deque() self.amIproposer = amIproposer self.logger = logger if self.shape.chi < 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.chi > self.shape.blockSizeR: + self.logger.error("Chi has to be smaller than %d" % self.shape.blockSizeR, extra=self.format) + elif self.shape.chi > self.shape.blockSizeC: + self.logger.error("Chi 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) @@ -72,8 +74,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.chi:(i+1)*self.shape.chi]) if rows else set(random.sample(range(self.shape.blockSizeC), 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.blockSizeR), self.shape.chi))) self.rowIDs = set.union(*self.vRowIDs) self.columnIDs = set.union(*self.vColumnIDs) self.rowNeighbors = collections.defaultdict(dict) @@ -99,7 +101,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.blockSizeR + 1) // 2 # stop sending on a p2p link if at least this amount of samples passed + self.sendLineUntilC = (self.shape.blockSizeC + 1) // 2 # 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 @@ -124,57 +127,57 @@ 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 + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + k = self.shape.blockSizeR/2 if r > k or c > k: 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 + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + k = self.shape.blockSizeR/2 if r > k or c > k: 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: + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + k = self.shape.blockSizeR/2 + if (r+c) % self.shape.blockSizeR > k: 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: + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + k = self.shape.blockSizeR/2 + if (r+c) % self.shape.blockSizeR > k: 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 + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + k = self.shape.blockSizeR/2 if r < k and c < k: 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 + for r in range(self.shape.blockSizeR): + for c in range(self.shape.blockSizeC): + k = self.shape.blockSizeR/2 if r < k and c < k: 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): @@ -251,7 +254,7 @@ 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: + 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] : @@ -348,10 +351,10 @@ class Validator: segmentsToSend = [] 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(): @@ -361,10 +364,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(): @@ -423,7 +426,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): @@ -431,7 +434,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): From b49be374898487dac6a5516081c60f03b9b451ff Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 12 Jul 2023 13:44:23 +0200 Subject: [PATCH 03/13] handle two dimensions on Chi separately Signed-off-by: Csaba Kiraly # Conflicts: # DAS/simulator.py # DAS/validator.py --- DAS/shape.py | 8 +++++--- DAS/simulator.py | 27 +++++++++++++++++---------- DAS/validator.py | 16 ++++++++-------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/DAS/shape.py b/DAS/shape.py index 4723258..93bec3d 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -3,7 +3,7 @@ class Shape: """This class represents a set of parameters for a specific simulation.""" - def __init__(self, blockSizeR, blockSizeC, numberNodes, failureModel, failureRate, class1ratio, chi, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): + def __init__(self, blockSizeR, blockSizeC, numberNodes, failureModel, failureRate, 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 @@ -13,7 +13,8 @@ class Shape: self.failureRate = failureRate self.netDegree = netDegree self.class1ratio = class1ratio - self.chi = chi + self.chiR = chiR + self.chiC = chiC self.vpn1 = vpn1 self.vpn2 = vpn2 self.bwUplinkProd = bwUplinkProd @@ -30,7 +31,8 @@ class Shape: 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) diff --git a/DAS/simulator.py b/DAS/simulator.py index 1925063..f3d881d 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -54,17 +54,20 @@ class Simulator: lightVal = lightNodes * self.shape.vpn1 heavyVal = heavyNodes * self.shape.vpn2 totalValidators = lightVal + heavyVal - totalRows = totalValidators * self.shape.chi + 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(totalRows/self.shape.blockSizeR)+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) @@ -73,14 +76,18 @@ 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 - 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), 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) diff --git a/DAS/validator.py b/DAS/validator.py index e75cb7d..651f4a7 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -42,7 +42,7 @@ 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 @@ -55,12 +55,12 @@ class Validator: self.sendQueue = deque() self.amIproposer = amIproposer 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.blockSizeR: - self.logger.error("Chi has to be smaller than %d" % self.shape.blockSizeR, extra=self.format) - elif self.shape.chi > self.shape.blockSizeC: - self.logger.error("Chi has to be smaller than %d" % self.shape.blockSizeC, 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 @@ -74,8 +74,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.blockSizeC), 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.blockSizeR), 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) From 9bd3a698e4abb679be1d47cf88fb9214d6472b01 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 12 Jul 2023 14:03:32 +0200 Subject: [PATCH 04/13] visualizer: workaround for R/C separation Signed-off-by: Csaba Kiraly --- DAS/visualizer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DAS/visualizer.py b/DAS/visualizer.py index cf095c1..8aa0c91 100644 --- a/DAS/visualizer.py +++ b/DAS/visualizer.py @@ -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('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) From e805dd9582b13787eb67da5ddc654965058b8e86 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 12 Jul 2023 13:54:17 +0200 Subject: [PATCH 05/13] smallConf: handle row/column dimensions Signed-off-by: Csaba Kiraly --- smallConf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/smallConf.py b/smallConf.py index 7ab3f44..3d75446 100644 --- a/smallConf.py +++ b/smallConf.py @@ -106,5 +106,7 @@ def nextShape(): runs, failureModels, failureRates, 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) + blockSizeR = blockSizeC = blockSize + chiR = chiC = chi + shape = Shape(blockSizeR, blockSizeC, nn, fm, fr, class1ratio, chiR, chiC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) yield shape From d2a2c8b1376d5328300904a87eaf581ffff7b199 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Sat, 15 Jul 2023 02:13:33 +0200 Subject: [PATCH 06/13] block: introduce N and K in both EC dimensions Signed-off-by: Csaba Kiraly --- DAS/block.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/DAS/block.py b/DAS/block.py index de56d49..d002345 100644 --- a/DAS/block.py +++ b/DAS/block.py @@ -7,14 +7,18 @@ from bitarray.util import zeros class Block: """This class represents a block in the Ethereum blockchain.""" - def __init__(self, blockSizeR, blockSizeC=0): + 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): @@ -42,12 +46,12 @@ class Block: self.data[columnID::self.blockSizeR] |= column def repairColumn(self, id): - """It repairs the entire column if it has at least blockSizeC/2 ones. + """It repairs the entire column if it has at least blockSizeCK ones. Returns: list of repaired segments """ line = self.data[id::self.blockSizeR] success = line.count(1) - if success >= self.blockSizeC/2: + if success >= self.blockSizeCK: ret = ~line self.data[id::self.blockSizeR] = 1 else: @@ -63,12 +67,12 @@ class Block: self.data[rowID*self.blockSizeR:(rowID+1)*self.blockSizeR] |= row def repairRow(self, id): - """It repairs the entire row if it has at least blockSizeR/2 ones. + """It repairs the entire row if it has at least blockSizeRK ones. Returns: list of repaired segments. """ line = self.data[id*self.blockSizeR:(id+1)*self.blockSizeR] success = line.count(1) - if success >= self.blockSizeR/2: + if success >= self.blockSizeRK: ret = ~line self.data[id*self.blockSizeR:(id+1)*self.blockSizeR] = 1 else: From cb9616af142252fa27443f1e55a84e8d64a23543 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Sat, 15 Jul 2023 02:18:30 +0200 Subject: [PATCH 07/13] Add N/K to shape Signed-off-by: Csaba Kiraly --- DAS/shape.py | 11 ++++++++--- DAS/validator.py | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/DAS/shape.py b/DAS/shape.py index 93bec3d..0af4c10 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -3,12 +3,15 @@ class Shape: """This class represents a set of parameters for a specific simulation.""" - def __init__(self, blockSizeR, blockSizeC, numberNodes, failureModel, failureRate, class1ratio, chiR, chiC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run): + def __init__(self, blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, + numberNodes, failureModel, failureRate, 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.blockSizeR = blockSizeR + self.blockSizeRK = blockSizeRK self.blockSizeC = blockSizeC + self.blockSizeCK = blockSizeCK self.failureModel = failureModel self.failureRate = failureRate self.netDegree = netDegree @@ -25,8 +28,10 @@ class Shape: def __repr__(self): """Returns a printable representation of the shape""" shastr = "" - shastr += "bsr-"+str(self.blockSizeR) - shastr += "bsc-"+str(self.blockSizeC) + 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) diff --git a/DAS/validator.py b/DAS/validator.py index 651f4a7..2e8312b 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -49,8 +49,8 @@ class Validator: 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.blockSizeC) - self.receivedBlock = Block(self.shape.blockSizeR, self.shape.blockSizeC) + 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 @@ -101,8 +101,8 @@ class Validator: self.bwUplink *= 1e3 / 8 * config.stepDuration / config.segmentSize self.repairOnTheFly = True - self.sendLineUntilR = (self.shape.blockSizeR + 1) // 2 # stop sending on a p2p link if at least this amount of samples passed - self.sendLineUntilC = (self.shape.blockSizeC + 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 From 14cdec54cb10d451124dc75b1581610ba9724278 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Sat, 15 Jul 2023 02:19:07 +0200 Subject: [PATCH 08/13] fix N/K in error patterns Signed-off-by: Csaba Kiraly --- DAS/validator.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/DAS/validator.py b/DAS/validator.py index 2e8312b..2b489b8 100644 --- a/DAS/validator.py +++ b/DAS/validator.py @@ -139,40 +139,36 @@ class Validator: 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): - k = self.shape.blockSizeR/2 - if r > k or c > k: + 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.blockSizeR): for c in range(self.shape.blockSizeC): - k = self.shape.blockSizeR/2 - if r > k or c > k: + 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": + 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): - k = self.shape.blockSizeR/2 - if (r+c) % self.shape.blockSizeR > k: + if (r+c) % self.shape.blockSizeR > self.shape.blockSizeRK: 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): - k = self.shape.blockSizeR/2 - if (r+c) % self.shape.blockSizeR > k: + 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.blockSizeR): for c in range(self.shape.blockSizeC): - k = self.shape.blockSizeR/2 - if r < k and c < k: + 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.blockSizeR): for c in range(self.shape.blockSizeC): - k = self.shape.blockSizeR/2 - if r < k and c < k: + if r < self.shape.blockSizeRK or c < self.shape.blockSizeCK: self.block.setSegment(r,c) self.block.setSegment(0, 0, 0) From fbb73265c137268bc6bad91fd74aa0aeef39f5fa Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Sat, 15 Jul 2023 02:29:51 +0200 Subject: [PATCH 09/13] fixup: set N/K in observer Signed-off-by: Csaba Kiraly --- DAS/observer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DAS/observer.py b/DAS/observer.py index d57c69a..f848837 100644 --- a/DAS/observer.py +++ b/DAS/observer.py @@ -14,7 +14,8 @@ class Observer: 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.blockSizeC) + self.broadcasted = Block(self.config.blockSizeR, self.config.blockSizeRK, + self.config.blockSizeC, self.config.blockSizeCK) def checkRowsColumns(self, validators): From 9fb2f6cfe67017ea59d48b085f7a3b374e3842c6 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 5 Dec 2023 10:37:09 +0100 Subject: [PATCH 10/13] update example smallConf with N/K Signed-off-by: Csaba Kiraly --- smallConf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smallConf.py b/smallConf.py index 3d75446..a166cec 100644 --- a/smallConf.py +++ b/smallConf.py @@ -107,6 +107,7 @@ def nextShape(): # 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, blockSizeC, nn, fm, fr, class1ratio, chiR, chiC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) + shape = Shape(blockSizeR, blockSizeRK, blockSizeC, blockSizeCK, nn, fm, fr, class1ratio, chiR, chiC, vpn1, vpn2, netDegree, bwUplinkProd, bwUplink1, bwUplink2, run) yield shape From cc51b2ba45a48623d39f46cbc2319fa1b47d91a1 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Wed, 24 Jan 2024 16:14:27 +0100 Subject: [PATCH 11/13] fix plotting if row/column sizes differ Signed-off-by: Csaba Kiraly --- DAS/visualizor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index b3147f2..31d3fea 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): @@ -194,6 +195,10 @@ 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]\ @@ -211,7 +216,7 @@ class Visualizor: conf["path"] = "results/"+self.execID+"/plots/RowColDist-"+str(result.shape)+".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) From e6b33cb542c2edfbf2be64a0aca169f18477fd58 Mon Sep 17 00:00:00 2001 From: Sudipta Basak <41054678+sudiptab2100@users.noreply.github.com> Date: Fri, 16 Feb 2024 15:42:52 +0530 Subject: [PATCH 12/13] Fixed results string in shape.py --- DAS/shape.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DAS/shape.py b/DAS/shape.py index 0af4c10..c8ace30 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -29,9 +29,9 @@ class Shape: """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 += "-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) From 00c3b3fe54b1ce51f913a8a53c22de09953843db Mon Sep 17 00:00:00 2001 From: Sudipta Basak Date: Sat, 17 Feb 2024 12:40:17 +0100 Subject: [PATCH 13/13] plot textBox attribute value fixed --- DAS/visualizor.py | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/DAS/visualizor.py b/DAS/visualizor.py index 31d3fea..dbebfb0 100644 --- a/DAS/visualizor.py +++ b/DAS/visualizor.py @@ -35,6 +35,13 @@ class Visualizor: self.config = config self.results = results os.makedirs("results/"+self.execID+"/plots", exist_ok=True) + + def __get_attrbs__(self, result): + text = str(result.shape).split("-") + d = dict() + for i in range(0, len(text), 2): + d[text[i]] = text[i + 1] + return d def plotAll(self): """Plot all the important elements of each result""" @@ -49,9 +56,9 @@ class Visualizor: def plotMissingSamples(self, result): """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" + 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'] conf["title"] = "Missing Samples" conf["type"] = "plot" conf["legLoc"] = 1 @@ -77,9 +84,9 @@ class Visualizor: vector2 = result.metrics["progress"]["validators ready"] vector3 = 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" + 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'] conf["title"] = "Nodes/validators ready" conf["type"] = "plot" conf["legLoc"] = 2 @@ -109,9 +116,9 @@ class Visualizor: vector2[i] = (vector2[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 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" + 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'] conf["title"] = "Sent data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -139,9 +146,9 @@ class Visualizor: vector1[i] = (vector1[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 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" + 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'] conf["title"] = "Received data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -169,9 +176,9 @@ class Visualizor: vector1[i] = (vector1[i] * 8 * (1000/self.config.stepDuration) * self.config.segmentSize) / 1000000 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" + 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'] conf["title"] = "Duplicated data" conf["type"] = "plot" conf["legLoc"] = 2 @@ -200,9 +207,9 @@ class Visualizor: 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" + 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'] conf["title"] = "Row/Column distribution" conf["type"] = "bar" conf["legLoc"] = 2