+ 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 @@ + + +
+ + + + +
+