Merge branch 'develop' into addDiagnostics
This commit is contained in:
commit
699a912991
|
@ -58,6 +58,17 @@ class Observer:
|
||||||
return (arrived, expected, ready, validated)
|
return (arrived, expected, ready, validated)
|
||||||
|
|
||||||
def getProgress(self, validators):
|
def getProgress(self, validators):
|
||||||
|
"""Calculate current simulation progress with different metrics.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- missingSamples: overall number of sample instances missing in nodes.
|
||||||
|
Sample are counted on both rows and columns, so intersections of interest are counted twice.
|
||||||
|
- sampleProgress: previous expressed as progress ratio
|
||||||
|
- nodeProgress: ratio of nodes having all segments interested in
|
||||||
|
- validatorProgress: same as above, but vpn weighted average. I.e. it counts per validator,
|
||||||
|
but counts a validator only if its support node's all validators see all interesting segments
|
||||||
|
TODO: add real per validator progress counter
|
||||||
|
"""
|
||||||
arrived, expected, ready, validated = self.checkStatus(validators)
|
arrived, expected, ready, validated = self.checkStatus(validators)
|
||||||
missingSamples = expected - arrived
|
missingSamples = expected - arrived
|
||||||
sampleProgress = arrived / expected
|
sampleProgress = arrived / expected
|
||||||
|
@ -68,6 +79,7 @@ class Observer:
|
||||||
return missingSamples, sampleProgress, nodeProgress, validatorProgress
|
return missingSamples, sampleProgress, nodeProgress, validatorProgress
|
||||||
|
|
||||||
def getTrafficStats(self, validators):
|
def getTrafficStats(self, validators):
|
||||||
|
"""Summary statistics of traffic measurements in a timestep."""
|
||||||
def maxOrNan(l):
|
def maxOrNan(l):
|
||||||
return np.max(l) if l else np.NaN
|
return np.max(l) if l else np.NaN
|
||||||
def meanOrNan(l):
|
def meanOrNan(l):
|
||||||
|
|
|
@ -7,35 +7,37 @@ from dicttoxml import dicttoxml
|
||||||
class Result:
|
class Result:
|
||||||
"""This class stores and process/store the results of a simulation."""
|
"""This class stores and process/store the results of a simulation."""
|
||||||
|
|
||||||
def __init__(self, shape):
|
def __init__(self, shape, execID):
|
||||||
"""It initializes the instance with a specific shape."""
|
"""It initializes the instance with a specific shape."""
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
|
self.execID = execID
|
||||||
self.blockAvailable = -1
|
self.blockAvailable = -1
|
||||||
self.tta = -1
|
self.tta = -1
|
||||||
self.missingVector = []
|
self.missingVector = []
|
||||||
self.metrics = {}
|
self.metrics = {}
|
||||||
|
|
||||||
def populate(self, shape, missingVector):
|
def populate(self, shape, config, missingVector):
|
||||||
"""It populates part of the result data inside a vector."""
|
"""It populates part of the result data inside a vector."""
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.missingVector = missingVector
|
self.missingVector = missingVector
|
||||||
missingSamples = missingVector[-1]
|
missingSamples = missingVector[-1]
|
||||||
if missingSamples == 0:
|
if missingSamples == 0:
|
||||||
self.blockAvailable = 1
|
self.blockAvailable = 1
|
||||||
self.tta = len(missingVector)
|
self.tta = len(missingVector) * (1000/config.stepDuration)
|
||||||
else:
|
else:
|
||||||
self.blockAvailable = 0
|
self.blockAvailable = 0
|
||||||
self.tta = -1
|
self.tta = -1
|
||||||
|
|
||||||
def addMetric(self, name, metric):
|
def addMetric(self, name, metric):
|
||||||
|
"""Generic function to add a metric to the results."""
|
||||||
self.metrics[name] = metric
|
self.metrics[name] = metric
|
||||||
|
|
||||||
def dump(self, execID):
|
def dump(self):
|
||||||
"""It dumps the results of the simulation in an XML file."""
|
"""It dumps the results of the simulation in an XML file."""
|
||||||
if not os.path.exists("results"):
|
if not os.path.exists("results"):
|
||||||
os.makedirs("results")
|
os.makedirs("results")
|
||||||
if not os.path.exists("results/"+execID):
|
if not os.path.exists("results/"+self.execID):
|
||||||
os.makedirs("results/"+execID)
|
os.makedirs("results/"+self.execID)
|
||||||
resd1 = self.shape.__dict__
|
resd1 = self.shape.__dict__
|
||||||
resd2 = self.__dict__.copy()
|
resd2 = self.__dict__.copy()
|
||||||
resd2.pop("shape")
|
resd2.pop("shape")
|
||||||
|
@ -43,6 +45,6 @@ class Result:
|
||||||
resXml = dicttoxml(resd1)
|
resXml = dicttoxml(resd1)
|
||||||
xmlstr = minidom.parseString(resXml)
|
xmlstr = minidom.parseString(resXml)
|
||||||
xmlPretty = xmlstr.toprettyxml()
|
xmlPretty = xmlstr.toprettyxml()
|
||||||
filePath = "results/"+execID+"/"+str(self.shape)+".xml"
|
filePath = "results/"+self.execID+"/"+str(self.shape)+".xml"
|
||||||
with open(filePath, "w") as f:
|
with open(filePath, "w") as f:
|
||||||
f.write(xmlPretty)
|
f.write(xmlPretty)
|
||||||
|
|
|
@ -19,7 +19,8 @@ class Simulator:
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.config = config
|
self.config = config
|
||||||
self.format = {"entity": "Simulator"}
|
self.format = {"entity": "Simulator"}
|
||||||
self.result = Result(self.shape)
|
self.execID = execID
|
||||||
|
self.result = Result(self.shape, self.execID)
|
||||||
self.validators = []
|
self.validators = []
|
||||||
self.logger = []
|
self.logger = []
|
||||||
self.logLevel = config.logLevel
|
self.logLevel = config.logLevel
|
||||||
|
@ -279,7 +280,7 @@ class Simulator:
|
||||||
missingVector.append(missingSamples)
|
missingVector.append(missingSamples)
|
||||||
break
|
break
|
||||||
elif missingSamples == 0:
|
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)
|
self.logger.debug("The entire block is available at step %d, with failure rate %d !" % (steps, self.shape.failureRate), extra=self.format)
|
||||||
missingVector.append(missingSamples)
|
missingVector.append(missingSamples)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -288,17 +289,6 @@ class Simulator:
|
||||||
progress = pd.DataFrame(progressVector)
|
progress = pd.DataFrame(progressVector)
|
||||||
if self.config.saveProgress:
|
if self.config.saveProgress:
|
||||||
self.result.addMetric("progress", progress.to_dict(orient='list'))
|
self.result.addMetric("progress", progress.to_dict(orient='list'))
|
||||||
if self.config.plotProgress:
|
self.result.populate(self.shape, self.config, missingVector)
|
||||||
progress.plot.line(subplots = [[cnS, cnN, cnV], [cnT0], [cnT1, cnR1, cnD1], [cnT2, cnR2, cnD2]],
|
|
||||||
title = str(self.shape))
|
|
||||||
if not os.path.exists("results"):
|
|
||||||
os.makedirs("results")
|
|
||||||
if not os.path.exists("results/"+self.execID):
|
|
||||||
os.makedirs("results/"+self.execID)
|
|
||||||
filePath = "results/"+self.execID+"/"+str(self.shape)+".png"
|
|
||||||
matplotlib.pyplot.savefig(filePath)
|
|
||||||
matplotlib.pyplot.close()
|
|
||||||
|
|
||||||
self.result.populate(self.shape, missingVector)
|
|
||||||
return self.result
|
return self.result
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Visualizer:
|
||||||
bwUplinkProd = int(root.find('bwUplinkProd').text)
|
bwUplinkProd = int(root.find('bwUplinkProd').text)
|
||||||
bwUplink1 = int(root.find('bwUplink1').text)
|
bwUplink1 = int(root.find('bwUplink1').text)
|
||||||
bwUplink2 = int(root.find('bwUplink2').text)
|
bwUplink2 = int(root.find('bwUplink2').text)
|
||||||
tta = int(root.find('tta').text)
|
tta = float(root.find('tta').text)
|
||||||
|
|
||||||
# Loop over all possible combinations of of the parameters minus two
|
# Loop over all possible combinations of of the parameters minus two
|
||||||
for combination in combinations(self.parameters, len(self.parameters)-2):
|
for combination in combinations(self.parameters, len(self.parameters)-2):
|
||||||
|
@ -120,7 +120,7 @@ class Visualizer:
|
||||||
hist, xedges, yedges = np.histogram2d(data[key][labels[0]], data[key][labels[1]], bins=(len(xlabels), len(ylabels)), weights=data[key]['ttas'])
|
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
|
hist = hist.T
|
||||||
fig, ax = plt.subplots(figsize=(10, 6))
|
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)
|
sns.heatmap(hist, xticklabels=xlabels, yticklabels=ylabels, cmap='Purples', cbar_kws={'label': 'Time to block availability (ms)'}, linecolor='black', linewidths=0.3, annot=True, fmt=".2f", ax=ax)
|
||||||
plt.xlabel(self.formatLabel(labels[0]))
|
plt.xlabel(self.formatLabel(labels[0]))
|
||||||
plt.ylabel(self.formatLabel(labels[1]))
|
plt.ylabel(self.formatLabel(labels[1]))
|
||||||
filename = ""
|
filename = ""
|
||||||
|
@ -131,6 +131,8 @@ class Visualizer:
|
||||||
filename += f"{key[paramValueCnt]}"
|
filename += f"{key[paramValueCnt]}"
|
||||||
formattedTitle = self.formatTitle(key[paramValueCnt])
|
formattedTitle = self.formatTitle(key[paramValueCnt])
|
||||||
title += formattedTitle
|
title += formattedTitle
|
||||||
|
if (paramValueCnt+1) % 5 == 0:
|
||||||
|
title += "\n"
|
||||||
paramValueCnt += 1
|
paramValueCnt += 1
|
||||||
title_obj = plt.title(title)
|
title_obj = plt.title(title)
|
||||||
font_size = 16 * fig.get_size_inches()[0] / 10
|
font_size = 16 * fig.get_size_inches()[0] / 10
|
||||||
|
|
|
@ -31,14 +31,14 @@ logLevel = logging.INFO
|
||||||
|
|
||||||
# number of parallel workers. -1: all cores; 1: sequential
|
# number of parallel workers. -1: all cores; 1: sequential
|
||||||
# for more details, see joblib.Parallel
|
# for more details, see joblib.Parallel
|
||||||
numJobs = 3
|
numJobs = -1
|
||||||
|
|
||||||
# distribute rows/columns evenly between validators (True)
|
# distribute rows/columns evenly between validators (True)
|
||||||
# or generate it using local randomness (False)
|
# or generate it using local randomness (False)
|
||||||
evenLineDistribution = True
|
evenLineDistribution = True
|
||||||
|
|
||||||
# Number of simulation runs with the same parameters for statistical relevance
|
# Number of simulation runs with the same parameters for statistical relevance
|
||||||
runs = range(10)
|
runs = range(2)
|
||||||
|
|
||||||
# Number of validators
|
# Number of validators
|
||||||
numberNodes = range(256, 513, 128)
|
numberNodes = range(256, 513, 128)
|
||||||
|
@ -53,14 +53,14 @@ blockSizes = range(32,65,16)
|
||||||
netDegrees = range(6, 9, 2)
|
netDegrees = range(6, 9, 2)
|
||||||
|
|
||||||
# number of rows and columns a validator is interested in
|
# number of rows and columns a validator is interested in
|
||||||
chis = range(1, 5, 2)
|
chis = range(2, 5, 2)
|
||||||
|
|
||||||
# ratio of class1 nodes (see below for parameters per class)
|
# ratio of class1 nodes (see below for parameters per class)
|
||||||
class1ratios = np.arange(0, 1, .2)
|
class1ratios = [0.8, 0.9]
|
||||||
|
|
||||||
# Number of validators per beacon node
|
# Number of validators per beacon node
|
||||||
validatorsPerNode1 = [1]
|
validatorsPerNode1 = [1]
|
||||||
validatorsPerNode2 = [2, 4, 8, 16, 32]
|
validatorsPerNode2 = [500]
|
||||||
|
|
||||||
# Set uplink bandwidth. In segments (~560 bytes) per timestep (50ms?)
|
# Set uplink bandwidth. In segments (~560 bytes) per timestep (50ms?)
|
||||||
# 1 Mbps ~= 1e6 / 20 / 8 / 560 ~= 11
|
# 1 Mbps ~= 1e6 / 20 / 8 / 560 ~= 11
|
||||||
|
@ -68,8 +68,11 @@ bwUplinksProd = [2200]
|
||||||
bwUplinks1 = [110]
|
bwUplinks1 = [110]
|
||||||
bwUplinks2 = [2200]
|
bwUplinks2 = [2200]
|
||||||
|
|
||||||
|
# Step duration in miliseconds (Classic RTT is about 100ms)
|
||||||
|
stepDuration = 50
|
||||||
|
|
||||||
# Set to True if you want your run to be deterministic, False if not
|
# Set to True if you want your run to be deterministic, False if not
|
||||||
deterministic = False
|
deterministic = True
|
||||||
|
|
||||||
# If your run is deterministic you can decide the random seed. This is ignore otherwise.
|
# If your run is deterministic you can decide the random seed. This is ignore otherwise.
|
||||||
randomSeed = "DAS"
|
randomSeed = "DAS"
|
||||||
|
|
2
study.py
2
study.py
|
@ -36,7 +36,7 @@ def runOnce(config, shape, execID):
|
||||||
sim.logger.info("Shape: %s ... Block Available: %d in %d steps" % (str(sim.shape.__dict__), result.blockAvailable, len(result.missingVector)), extra=sim.format)
|
sim.logger.info("Shape: %s ... Block Available: %d in %d steps" % (str(sim.shape.__dict__), result.blockAvailable, len(result.missingVector)), extra=sim.format)
|
||||||
|
|
||||||
if config.dumpXML:
|
if config.dumpXML:
|
||||||
result.dump(execID)
|
result.dump()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue