diff --git a/.gitignore b/.gitignore index b948985..026d904 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.swp *.pyc +results/* +!results/plots.py \ No newline at end of file diff --git a/DAS/__init__.py b/DAS/__init__.py index 8dd11bf..c7fc7c0 100644 --- a/DAS/__init__.py +++ b/DAS/__init__.py @@ -1,3 +1,4 @@ from DAS.simulator import * from DAS.configuration import * from DAS.shape import * +from DAS.visualizer import * diff --git a/DAS/configuration.py b/DAS/configuration.py index 91df16f..a2e614e 100644 --- a/DAS/configuration.py +++ b/DAS/configuration.py @@ -33,6 +33,7 @@ class Configuration: self.numberRuns = int(config.get("Advanced", "numberRuns")) self.deterministic = config.get("Advanced", "deterministic") + self.dumpXML = config.get("Advanced", "dumpXML") if self.nvStop < (self.blockSizeStart*4): print("ERROR: The number of validators cannot be lower than the block size * 4") diff --git a/DAS/results.py b/DAS/results.py index 8468b09..48b6cbc 100644 --- a/DAS/results.py +++ b/DAS/results.py @@ -1,17 +1,50 @@ #!/bin/python3 +import os +from xml.dom import minidom +from dicttoxml import dicttoxml class Result: - config = [] + shape = [] missingVector = [] blockAvailable = -1 + tta = -1 - def __init__(self, config): - self.config = config + def __init__(self, shape): + self.shape = shape self.blockAvailable = -1 + self.tta = -1 self.missingVector = [] - - def addMissing(self, missingVector): + def populate(self, shape, missingVector): + self.shape = shape self.missingVector = missingVector + missingSamples = missingVector[-1] + if missingSamples == 0: + self.blockAvailable = 1 + self.tta = len(missingVector) + else: + self.blockAvailable = 0 + self.tta = -1 + + def dump(self, execID): + if not os.path.exists("results"): + os.makedirs("results") + if not os.path.exists("results/"+execID): + os.makedirs("results/"+execID) + resd1 = self.shape.__dict__ + resd2 = self.__dict__.copy() + resd2.pop("shape") + resd1.update(resd2) + resXml = dicttoxml(resd1) + xmlstr = minidom.parseString(resXml) + xmlPretty = xmlstr.toprettyxml() + filePath = "results/"+execID+"/nbv-"+str(self.shape.numberValidators)+\ + "-bs-"+str(self.shape.blockSize)+\ + "-nd-"+str(self.shape.netDegree)+\ + "-fr-"+str(self.shape.failureRate)+\ + "-chi-"+str(self.shape.chi)+\ + "-r-"+str(self.shape.run)+".xml" + with open(filePath, "w") as f: + f.write(xmlPretty) diff --git a/DAS/shape.py b/DAS/shape.py index 2918422..243ae8e 100644 --- a/DAS/shape.py +++ b/DAS/shape.py @@ -1,16 +1,18 @@ #!/bin/python3 class Shape: + run = 0 numberValidators = 0 - failureRate = 0 blockSize = 0 + failureRate = 0 netDegree = 0 chi = 0 - def __init__(self, blockSize, numberValidators, failureRate, chi, netDegree): + def __init__(self, blockSize, numberValidators, failureRate, chi, netDegree, run): + self.run = run self.numberValidators = numberValidators - self.failureRate = failureRate self.blockSize = blockSize + self.failureRate = failureRate self.netDegree = netDegree self.chi = chi diff --git a/DAS/simulator.py b/DAS/simulator.py index 676b891..20ecb19 100644 --- a/DAS/simulator.py +++ b/DAS/simulator.py @@ -42,7 +42,6 @@ class Simulator: self.validators.append(val) def initNetwork(self): - self.shape.netDegree = 6 rowChannels = [[] for i in range(self.shape.blockSize)] columnChannels = [[] for i in range(self.shape.blockSize)] for v in self.validators: @@ -87,6 +86,7 @@ class Simulator: def resetShape(self, shape): self.shape = shape + self.result = Result(self.shape) for val in self.validators: val.shape.failureRate = shape.failureRate val.shape.chi = shape.chi @@ -120,19 +120,16 @@ class Simulator: missingRate = missingSamples*100/expected self.logger.debug("step %d, missing %d of %d (%0.02f %%)" % (steps, missingSamples, expected, missingRate), extra=self.format) if missingSamples == oldMissingSamples: + #self.logger.info("The block cannot be recovered, failure rate %d!" % self.shape.failureRate, extra=self.format) + missingVector.append(missingSamples) break elif missingSamples == 0: + #self.logger.info("The entire block is available at step %d, with failure rate %d !" % (steps, self.shape.failureRate), extra=self.format) + missingVector.append(missingSamples) break else: steps += 1 - self.result.addMissing(missingVector) - if missingSamples == 0: - self.result.blockAvailable = 1 - self.logger.debug("The entire block is available at step %d, with failure rate %d !" % (steps, self.shape.failureRate), extra=self.format) - return self.result - else: - self.result.blockAvailable = 0 - self.logger.debug("The block cannot be recovered, failure rate %d!" % self.shape.failureRate, extra=self.format) - return self.result + self.result.populate(self.shape, missingVector) + return self.result diff --git a/DAS/visualizer.py b/DAS/visualizer.py new file mode 100644 index 0000000..9ab37c2 --- /dev/null +++ b/DAS/visualizer.py @@ -0,0 +1,130 @@ +#!/bin/python3 +import os, sys +import time +import xml.etree.ElementTree as ET +import matplotlib.pyplot as plt +import numpy as np +import seaborn as sns +from itertools import combinations + +class Visualizer: + + def __init__(self, execID): + self.execID = execID + self.folderPath = "results/"+self.execID + self.parameters = ['run', 'blockSize', 'failureRate', 'numberValidators', 'netDegree', 'chi'] + + #Store data with a unique key for each params combination + def plottingData(self): + data = {} + #Loop over the xml files in the folder + for filename in os.listdir(self.folderPath): + #Loop over the xmls and store the data in variables + if filename.endswith('.xml'): + tree = ET.parse(os.path.join(self.folderPath, filename)) + root = tree.getroot() + run = int(root.find('run').text) + blockSize = int(root.find('blockSize').text) + failureRate = int(root.find('failureRate').text) + numberValidators = int(root.find('numberValidators').text) + netDegree = int(root.find('netDegree').text) + chi = int(root.find('chi').text) + tta = int(root.find('tta').text) + + # Loop over all possible combinations of length 4 of the parameters + for combination in combinations(self.parameters, 4): + # Get the indices and values of the parameters in the combination + indices = [self.parameters.index(element) for element in combination] + selectedValues = [run, blockSize, failureRate, numberValidators, netDegree, chi] + 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)] + key = tuple(keyComponents[:4]) + #Get the names of the other 2 parameters that are not included in the key + otherParams = [self.parameters[i] for i in range(6) if i not in indices] + #Append the values of the other 2 parameters and the ttas to the lists for the key + otherIndices = [i for i in range(len(self.parameters)) if i not in indices] + + #Initialize the dictionary for the key if it doesn't exist yet + if key not in data: + data[key] = {} + #Initialize lists for the other 2 parameters and the ttas with the key + data[key][otherParams[0]] = [] + data[key][otherParams[1]] = [] + data[key]['ttas'] = [] + + if otherParams[0] in data[key]: + data[key][otherParams[0]].append(selectedValues[otherIndices[0]]) + else: + data[key][otherParams[0]] = [selectedValues[otherIndices[0]]] + if otherParams[1] in data[key]: + data[key][otherParams[1]].append(selectedValues[otherIndices[1]]) + else: + data[key][otherParams[1]] = [selectedValues[otherIndices[1]]] + data[key]['ttas'].append(tta) + print("Getting data from the folder...") + return data + + #Get the keys for all data with the same x and y labels + def similarKeys(self, data): + filteredKeys = {} + for key1, value1 in data.items(): + subKeys1 = list(value1.keys()) + filteredKeys[(subKeys1[0], subKeys1[1])] = [key1] + for key2, value2 in data.items(): + subKeys2 = list(value2.keys()) + if key1 != key2 and subKeys1[0] == subKeys2[0] and subKeys1[1] == subKeys2[1]: + try: + filteredKeys[(subKeys1[0], subKeys1[1])].append(key2) + except KeyError: + filteredKeys[(subKeys1[0], subKeys1[1])] = [key2] + print("Getting filtered keys from data...") + return filteredKeys + + #Title formatting for the figures + def formatTitle(self, key): + name = ''.join([f" {char}" if char.isupper() else char for char in key.split('_')[0]]) + number = key.split('_')[1] + return f"{name.title()}: {number} " + + #Plot and store the 2D heatmaps in subfolders + def plotHeatmaps(self): + data = self.plottingData() + filteredKeys = self.similarKeys(data) + print("Plotting heatmaps...") + + #Create the directory if it doesn't exist already + heatmapsFolder = self.folderPath + '/heatmaps' + if not os.path.exists(heatmapsFolder): + os.makedirs(heatmapsFolder) + + #Plot + for labels, keys in filteredKeys.items(): + for key in keys: + xlabels = np.sort(np.unique(data[key][labels[0]])) + ylabels = np.sort(np.unique(data[key][labels[1]])) + hist, xedges, yedges = np.histogram2d(data[key][labels[0]], data[key][labels[1]], bins=(len(xlabels), len(ylabels)), weights=data[key]['ttas']) + hist = hist.T + fig, ax = plt.subplots(figsize=(10, 6)) + sns.heatmap(hist, xticklabels=xlabels, yticklabels=ylabels, cmap='Purples', cbar_kws={'label': 'Time to block availability'}, linecolor='black', linewidths=0.3, annot=True, fmt=".2f", ax=ax) + plt.xlabel(labels[0]) + plt.ylabel(labels[1]) + filename = "" + title = "" + paramValueCnt = 0 + for param in self.parameters: + if param != labels[0] and param != labels[1]: + filename += f"{key[paramValueCnt]}" + formattedTitle = self.formatTitle(key[paramValueCnt]) + title += formattedTitle + paramValueCnt += 1 + title_obj = plt.title(title) + font_size = 16 * fig.get_size_inches()[0] / 10 + title_obj.set_fontsize(font_size) + filename = filename + ".png" + targetFolder = os.path.join(heatmapsFolder, f"{labels[0]}Vs{labels[1]}") + if not os.path.exists(targetFolder): + os.makedirs(targetFolder) + plt.savefig(os.path.join(targetFolder, filename)) + plt.close() + plt.clf() diff --git a/Frontend/Imgs/logo.png b/Frontend/Imgs/logo.png new file mode 100644 index 0000000..6e90de2 Binary files /dev/null and b/Frontend/Imgs/logo.png differ diff --git a/Frontend/Plots/plot1.png b/Frontend/Plots/plot1.png new file mode 100644 index 0000000..e6ac529 Binary files /dev/null and b/Frontend/Plots/plot1.png differ diff --git a/Frontend/Plots/plot2_10_8192.png b/Frontend/Plots/plot2_10_8192.png new file mode 100644 index 0000000..fedae43 Binary files /dev/null and b/Frontend/Plots/plot2_10_8192.png differ diff --git a/Frontend/Plots/plot3_70_8192.png b/Frontend/Plots/plot3_70_8192.png new file mode 100644 index 0000000..b0f328e Binary files /dev/null and b/Frontend/Plots/plot3_70_8192.png differ diff --git a/Frontend/Plots/plot4_80_8192.png b/Frontend/Plots/plot4_80_8192.png new file mode 100644 index 0000000..d42fb8c Binary files /dev/null and b/Frontend/Plots/plot4_80_8192.png differ diff --git a/Frontend/Plots/plot5_90_8192.png b/Frontend/Plots/plot5_90_8192.png new file mode 100644 index 0000000..d42fb8c Binary files /dev/null and b/Frontend/Plots/plot5_90_8192.png differ diff --git a/Frontend/Plots/plot6_100_8192.png b/Frontend/Plots/plot6_100_8192.png new file mode 100644 index 0000000..52942fa Binary files /dev/null and b/Frontend/Plots/plot6_100_8192.png differ diff --git a/Frontend/Plots/plot7_110_8192.png b/Frontend/Plots/plot7_110_8192.png new file mode 100644 index 0000000..d42fb8c Binary files /dev/null and b/Frontend/Plots/plot7_110_8192.png differ diff --git a/Frontend/Plots/plot8_120_8192.png b/Frontend/Plots/plot8_120_8192.png new file mode 100644 index 0000000..52942fa Binary files /dev/null and b/Frontend/Plots/plot8_120_8192.png differ diff --git a/Frontend/Plots/plot9_130_8192.png b/Frontend/Plots/plot9_130_8192.png new file mode 100644 index 0000000..e6ac529 Binary files /dev/null and b/Frontend/Plots/plot9_130_8192.png differ diff --git a/Frontend/Plots/plt1.png b/Frontend/Plots/plt1.png new file mode 100644 index 0000000..85ca6e9 Binary files /dev/null and b/Frontend/Plots/plt1.png differ diff --git a/Frontend/css/style.css b/Frontend/css/style.css new file mode 100644 index 0000000..1f3b174 --- /dev/null +++ b/Frontend/css/style.css @@ -0,0 +1,85 @@ +*{ + text-decoration: none; + margin: 0; + padding: 0; + box-sizing: border-box; + list-style: none; +} + +body{ + display: flex; +} + +.navbar{ + width: 7vw; + height: 100vh; + padding-left: 5px; + display: flex; + flex-direction: column; + align-items: center; + background-color: #7f5a83; + background-image: linear-gradient(280deg, #3f305e 0%, #00060a 74%); +} + +#logo{ + margin-top: 25px; + width: 3vw; + opacity: 0.9; +} + +.navbar-bar{ + color: aliceblue; + margin-top: 15vh; +} + +.navbar-bar li{ + margin-top: 10px; + height: 10vh; + width: 7vw; + text-align: center; + line-height: 10vh; +} + +.navbar li:hover, .navbar li:focus, .navbar li:active{ + background: #eee5fdea; + border-top-left-radius: 50%; + border-bottom-left-radius: 50%; + cursor: pointer; + transition: all 0.5s ease-in-out; +} + +.navbar li:hover .fa-solid{ + color: #160f25; +} + +.fa-solid{ + color: #b9aecf; + opacity: 0.7; + cursor: pointer; + transition: all 0.5s ease-in-out; +} + +.fa-solid:hover{ + opacity: 1; + color: #160f25; +} + +.content{ + width: 93vw; + height: 100vh; + background-color: #eee5fdea; +} + +.p1, .p2, .p3, .p4{ + width: 93vw; + height: 100vh; +} + +.p2, .p3, .p4{ + display: none; +} + +.plot1{ + margin: auto; + display: block; +} diff --git a/Frontend/index.html b/Frontend/index.html new file mode 100644 index 0000000..6c85859 --- /dev/null +++ b/Frontend/index.html @@ -0,0 +1,32 @@ + + + + + + + + DAS Dashboard + + + +
+
+ +
+
Text2
+
Text3
+
Text4
+
+ + + \ No newline at end of file diff --git a/Frontend/script.js b/Frontend/script.js new file mode 100644 index 0000000..123cf36 --- /dev/null +++ b/Frontend/script.js @@ -0,0 +1,33 @@ +let section1 = document.querySelector('.sec1'); +let section2 = document.querySelector('.sec2'); +let section3 = document.querySelector('.sec3'); +let section4 = document.querySelector('.sec4'); +let sections = [section1, section2, section3, section4]; + +let icon1 = document.querySelector(".sec1 i"); +let icon2 = document.querySelector(".sec2 i"); +let icon3 = document.querySelector(".sec3 i"); +let icon4 = document.querySelector(".sec4 i"); +let icons = [icon1, icon2, icon3, icon4]; + +let par1 = document.querySelector(".p1"); +let par2 = document.querySelector(".p2"); +let par3 = document.querySelector(".p3"); +let par4 = document.querySelector(".p4"); +let paragraphs = [par1, par2, par3, par4]; + +section1.style.cssText = "background: #eee5fdea; border-top-left-radius: 50%; border-bottom-left-radius: 50%; cursor: pointer; transition: all 0.5s ease-in-out;" +icon1.style.cssText = "opacity: 1; color: #160f25;" + +sections.forEach(section =>{ + section.addEventListener("click", function(){ + sections.forEach(s =>{ + s.style.cssText = "background: none; border-top-left-radius: 0%; border-bottom-left-radius: 0%;" + icons[sections.indexOf(s)].style.cssText = "color: #b9aecf; opacity: 0.7;" + paragraphs[sections.indexOf(s)].style.display = "none"; + }); + section.style.cssText = "background: #eee5fdea; border-top-left-radius: 50%; border-bottom-left-radius: 50%; cursor: pointer; transition: all 0.5s ease-in-out;" + icons[sections.indexOf(section)].style.cssText = "opacity: 1; color: #160f25;" + paragraphs[sections.indexOf(section)].style.display = "block"; + }); +}); \ No newline at end of file diff --git a/config.das b/config.das index f5d608e..052f754 100644 --- a/config.das +++ b/config.das @@ -25,3 +25,4 @@ chiStep = 2 deterministic = 0 numberRuns = 2 +dumpXML = 1 diff --git a/results/plots.py b/results/plots.py new file mode 100644 index 0000000..b22dd2f --- /dev/null +++ b/results/plots.py @@ -0,0 +1,138 @@ +import os, sys +import time +import xml.etree.ElementTree as ET +import matplotlib.pyplot as plt +import numpy as np +import seaborn as sns +from itertools import combinations + +parameters = ['run', 'blockSize', 'failureRate', 'numberValidators', 'netDegree', 'chi'] + +#Title formatting for the figures +def formatTitle(key): + name = ''.join([f" {char}" if char.isupper() else char for char in key.split('_')[0]]) + number = key.split('_')[1] + return f"{name.title()}: {number} " + +def getLatestDirectory(): + resultsFolder = os.getcwd() + + #Get all folders and store their time info and sort + directories = [d for d in os.listdir(resultsFolder) if os.path.isdir(os.path.join(resultsFolder, d))] + directoriesTime = [(d, os.path.getctime(os.path.join(resultsFolder, d))) for d in directories] + directoriesTime.sort(key=lambda x: x[1], reverse=True) + + #Get the path of the latest created folder + latestDirectory = directoriesTime[0][0] + folderPath = os.path.join(resultsFolder, latestDirectory) + return folderPath + +def plottingData(folderPath): + #Store data with a unique key for each params combination + data = {} + plotInfo = {} + + #Loop over the xml files in the folder + for filename in os.listdir(folderPath): + #Loop over the xmls and store the data in variables + if filename.endswith('.xml'): + tree = ET.parse(os.path.join(folderPath, filename)) + root = tree.getroot() + run = int(root.find('run').text) + blockSize = int(root.find('blockSize').text) + failureRate = int(root.find('failureRate').text) + numberValidators = int(root.find('numberValidators').text) + netDegree = int(root.find('netDegree').text) + chi = int(root.find('chi').text) + tta = int(root.find('tta').text) + + # Loop over all possible combinations of length 4 of the parameters + for combination in combinations(parameters, 4): + # Get the indices and values of the parameters in the combination + indices = [parameters.index(element) for element in combination] + selectedValues = [run, blockSize, failureRate, numberValidators, netDegree, chi] + values = [selectedValues[index] for index in indices] + names = [parameters[i] for i in indices] + keyComponents = [f"{name}_{value}" for name, value in zip(names, values)] + key = tuple(keyComponents[:4]) + #Get the names of the other 2 parameters that are not included in the key + otherParams = [parameters[i] for i in range(6) if i not in indices] + #Append the values of the other 2 parameters and the ttas to the lists for the key + otherIndices = [i for i in range(len(parameters)) if i not in indices] + + #Initialize the dictionary for the key if it doesn't exist yet + if key not in data: + data[key] = {} + #Initialize lists for the other 2 parameters and the ttas with the key + data[key][otherParams[0]] = [] + data[key][otherParams[1]] = [] + data[key]['ttas'] = [] + + if otherParams[0] in data[key]: + data[key][otherParams[0]].append(selectedValues[otherIndices[0]]) + else: + data[key][otherParams[0]] = [selectedValues[otherIndices[0]]] + if otherParams[1] in data[key]: + data[key][otherParams[1]].append(selectedValues[otherIndices[1]]) + else: + data[key][otherParams[1]] = [selectedValues[otherIndices[1]]] + data[key]['ttas'].append(tta) + return data + +def similarKeys(data): + #Get the keys for all data with the same x and y labels + filteredKeys = {} + for key1, value1 in data.items(): + subKeys1 = list(value1.keys()) + filteredKeys[(subKeys1[0], subKeys1[1])] = [key1] + for key2, value2 in data.items(): + subKeys2 = list(value2.keys()) + if key1 != key2 and subKeys1[0] == subKeys2[0] and subKeys1[1] == subKeys2[1]: + try: + filteredKeys[(subKeys1[0], subKeys1[1])].append(key2) + except KeyError: + filteredKeys[(subKeys1[0], subKeys1[1])] = [key2] + return filteredKeys + +def plotHeatmaps(folderPath, filteredKeys, data): + #Store the 2D heatmaps in a folder + heatmapsFolder = folderPath+'/heatmaps' + if not os.path.exists(heatmapsFolder): + os.makedirs(heatmapsFolder) + + for labels, keys in filteredKeys.items(): + for key in keys: + xlabels = np.sort(np.unique(data[key][labels[0]])) + ylabels = np.sort(np.unique(data[key][labels[1]])) + hist, xedges, yedges = np.histogram2d(data[key][labels[0]], data[key][labels[1]], bins=(len(xlabels), len(ylabels)), weights=data[key]['ttas']) + hist = hist.T + fig, ax = plt.subplots(figsize=(10, 6)) + sns.heatmap(hist, xticklabels=xlabels, yticklabels=ylabels, cmap='Purples', cbar_kws={'label': 'Time to block availability'}, linecolor='black', linewidths=0.3, annot=True, fmt=".2f", ax=ax) + plt.xlabel(labels[0]) + plt.ylabel(labels[1]) + filename = "" + title = "" + paramValueCnt = 0 + for param in parameters: + if param != labels[0] and param != labels[1]: + filename += f"{key[paramValueCnt]}" + title += formatTitle(key[paramValueCnt]) + paramValueCnt += 1 + title_obj = plt.title(title) + font_size = 16 * fig.get_size_inches()[0] / 10 + title_obj.set_fontsize(font_size) + filename = filename + ".png" + targetFolder = os.path.join(heatmapsFolder, f"{labels[0]}Vs{labels[1]}") + if not os.path.exists(targetFolder): + os.makedirs(targetFolder) + plt.savefig(os.path.join(targetFolder, filename)) + plt.close() + plt.clf() + +def generateHeatmaps(folderPath): + #folderPath = getLatestDirectory() + data = plottingData(folderPath) + filteredKeys = similarKeys(data) + plotHeatmaps(folderPath, filteredKeys, data) + +generateHeatmaps(sys.argv[1]) diff --git a/study.py b/study.py index ae27e5c..438fba4 100644 --- a/study.py +++ b/study.py @@ -1,6 +1,6 @@ #! /bin/python3 -import time, sys +import time, sys, random, copy from DAS import * @@ -10,36 +10,51 @@ def study(): exit(1) config = Configuration(sys.argv[1]) - sim = Simulator(config) + shape = Shape(0, 0, 0, 0, 0, 0) + sim = Simulator(shape) sim.initLogger() results = [] simCnt = 0 + now = datetime.now() + execID = now.strftime("%Y-%m-%d_%H-%M-%S_")+str(random.randint(100,999)) + sim.logger.info("Starting simulations:", extra=sim.format) start = time.time() for run in range(config.numberRuns): - for fr in range(config.failureRateStart, config.failureRateStop+1, config.failureRateStep): - for chi in range(config.chiStart, config.chiStop+1, config.chiStep): - for blockSize in range(config.blockSizeStart, config.blockSizeStop+1, config.blockSizeStep): - for nv in range(config.nvStart, config.nvStop+1, config.nvStep): - for netDegree in range(config.netDegreeStart, config.netDegreeStop+1, config.netDegreeStep): + for nv in range(config.nvStart, config.nvStop+1, config.nvStep): + for blockSize in range(config.blockSizeStart, config.blockSizeStop+1, config.blockSizeStep): + for fr in range(config.failureRateStart, config.failureRateStop+1, config.failureRateStep): + for netDegree in range(config.netDegreeStart, config.netDegreeStop+1, config.netDegreeStep): + for chi in range(config.chiStart, config.chiStop+1, config.chiStep): if not config.deterministic: random.seed(datetime.now()) - shape = Shape(blockSize, nv, fr, chi, netDegree) - sim.resetShape(shape) - sim.initValidators() - sim.initNetwork() - result = sim.run() - sim.logger.info("Run %d, FR: %d %%, Chi: %d, BlockSize: %d, Nb.Val: %d, netDegree: %d ... Block Available: %d" % (run, fr, chi, blockSize, nv, netDegree, result.blockAvailable), extra=sim.format) - results.append(result) - simCnt += 1 + # Network Degree has to be an even number + if netDegree % 2 == 0: + shape = Shape(blockSize, nv, fr, chi, netDegree, run) + sim.resetShape(shape) + sim.initValidators() + sim.initNetwork() + result = sim.run() + sim.logger.info("Shape: %s ... Block Available: %d" % (str(sim.shape.__dict__), result.blockAvailable), extra=sim.format) + results.append(copy.deepcopy(result)) + simCnt += 1 end = time.time() sim.logger.info("A total of %d simulations ran in %d seconds" % (simCnt, end-start), extra=sim.format) + if config.dumpXML: + for res in results: + res.dump(execID) + sim.logger.info("Results dumped into results/%s/" % (execID), extra=sim.format) + + visualization = 1 + if visualization: + vis = Visualizer(execID) + vis.plotHeatmaps() study()