- added parameters to main
- print and/or save
- added more data to the stats
- removed lower and upper bounds for simplier error
This commit is contained in:
2025-01-27 13:45:54 +01:00
parent 71dac2c6d1
commit a729dfb123
7 changed files with 294 additions and 54 deletions

View File

@@ -13,6 +13,9 @@ public class Result {
public final long seed;
public final double simulationTime;
public final double timeElapsedMS;
private int size;
private String iFormat;
private String fFormat;
/**
* Creates a new result object for the given parameters obtained by the
@@ -28,6 +31,9 @@ public class Result {
this.simulationTime = time;
this.timeElapsedMS = elapsed;
this.nodes = nodes;
this.size = (int) Math.ceil(Math.log10(this.simulationTime));
this.iFormat = "%" + this.size + ".0f";
this.fFormat = "%" + (this.size + 4) + ".3f";
}
/**
@@ -36,13 +42,11 @@ public class Result {
* real time
*/
public String getHeader() {
var size = (int) Math.ceil(Math.log10(this.simulationTime));
var format = "%" + (size + 4) + ".3f";
var builder = new StringBuilder();
builder.append("===== Net Stats =====\n");
builder.append(String.format("Seed: \t%d\n", this.seed));
builder.append(String.format("Simulation: \t" + format + "\n", this.simulationTime));
builder.append(String.format("Elapsed: \t" + format + "ms\n", this.timeElapsedMS / 1e6));
builder.append(String.format("Simulation: \t" + fFormat + "\n", this.simulationTime));
builder.append(String.format("Elapsed: \t" + fFormat + "ms\n", this.timeElapsedMS / 1e6));
return builder.toString();
}
@@ -52,24 +56,68 @@ public class Result {
* the statistics for each node in the network.
*/
public String getSummary() {
var size = (int) Math.ceil(Math.log10(this.simulationTime));
var iFormat = "%" + size + ".0f";
var fFormat = "%" + (size + 4) + ".3f";
String[] h = { "Node", "Departures", "Avg Queue", "Avg Response", "Throughput", "Utilization %", "Last Event" };
String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput", "Utilization %",
"Last Event" };
var table = new ConsoleTable(h);
for (var entry : this.nodes.entrySet()) {
var stats = entry.getValue();
table.addRow(
entry.getKey(),
String.format(iFormat, stats.numDepartures),
String.format(fFormat, stats.avgQueueLength),
String.format(fFormat, stats.avgResponse),
String.format(fFormat, stats.troughput),
String.format(fFormat, stats.utilization * 100),
String.format(fFormat, stats.lastEventTime));
iFormat.formatted(stats.numDepartures),
fFormat.formatted(stats.avgQueueLength),
fFormat.formatted(stats.avgWaitTime),
fFormat.formatted(stats.avgResponse),
fFormat.formatted(stats.troughput),
fFormat.formatted(stats.utilization * 100),
fFormat.formatted(stats.lastEventTime));
}
return table.toString();
}
/**
* TODO
*
* @param tableHeader
* @return
*/
public String getSummaryCSV(boolean tableHeader) {
var builder = new StringBuilder();
if (tableHeader)
builder.append(
"Seed,Node,Arrivals,Departures,MaxQueue,AvgQueue,AvgWait,AvgResponse,BusyTime,WaitTime,ResponseTime,LastEventTime,Throughput,Utilization\n");
for (var entry : this.nodes.entrySet()) {
var stats = entry.getValue();
builder.append(this.seed);
builder.append(',');
builder.append(entry.getKey().replace(',', ';').replace('"', '\''));
builder.append(',');
builder.append(stats.numArrivals);
builder.append(',');
builder.append(stats.numDepartures);
builder.append(',');
builder.append(stats.maxQueueLength);
builder.append(',');
builder.append(stats.avgQueueLength);
builder.append(',');
builder.append(stats.avgWaitTime);
builder.append(',');
builder.append(stats.avgResponse);
builder.append(',');
builder.append(stats.busyTime);
builder.append(',');
builder.append(stats.waitTime);
builder.append(',');
builder.append(stats.responseTime);
builder.append(',');
builder.append(stats.lastEventTime);
builder.append(',');
builder.append(stats.troughput);
builder.append(',');
builder.append(stats.utilization);
builder.append('\n');
}
return builder.toString();
}
}

View File

@@ -1,5 +1,7 @@
package net.berack.upo.valpre.sim.stats;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
/**
@@ -9,8 +11,7 @@ public class ResultMultiple {
public final Result[] runs;
public final Result average;
public final Result variance;
public final Result lowerBound;
public final Result upperBound;
public final Result error95;
/**
* TODO
@@ -18,13 +19,31 @@ public class ResultMultiple {
* @param runs
*/
public ResultMultiple(Result... runs) {
if (runs == null || runs.length <= 1)
throw new IllegalArgumentException("Sample size must be > 1");
this.runs = runs;
this.average = ResultMultiple.calcAvg(runs);
this.variance = ResultMultiple.calcVar(this.average, runs);
this.error95 = calcError(this.average, this.variance, runs.length, 0.95);
}
var temp = calcInterval(this.average, this.variance, runs.length, 0.95);
this.lowerBound = temp[0];
this.upperBound = temp[1];
/**
* TODO
*
* @param filename
* @throws IOException
*/
public void saveCSV(String filename) throws IOException {
try (var file = new FileWriter(filename)) {
var first = true;
var builder = new StringBuilder();
for (var run : this.runs) {
builder.append(run.getSummaryCSV(first));
first = false;
}
file.write(builder.toString());
}
}
/**
@@ -98,10 +117,7 @@ public class ResultMultiple {
* @param alpha
* @return
*/
public static Result[] calcInterval(Result avg, Result stdDev, int sampleSize, double alpha) {
if (sampleSize <= 1)
throw new IllegalArgumentException("Il numero di campioni deve essere maggiore di 1.");
public static Result calcError(Result avg, Result stdDev, int sampleSize, double alpha) {
// Getting the correct values for the percentile
var distr = new org.apache.commons.math3.distribution.TDistribution(sampleSize - 1);
var percentile = distr.inverseCumulativeProbability(alpha);
@@ -117,31 +133,6 @@ public class ResultMultiple {
stat.merge(entry.getValue(), (_, val) -> percentile * (val / sqrtSample));
error.nodes.put(entry.getKey(), stat);
}
// Calculating the lower and the upper bound
var lowerBound = new Result(avg.seed,
avg.simulationTime - error.simulationTime,
avg.timeElapsedMS - error.timeElapsedMS,
new HashMap<>());
var upperBound = new Result(avg.seed,
avg.simulationTime + error.simulationTime,
avg.timeElapsedMS + error.timeElapsedMS,
new HashMap<>());
error.nodes.entrySet().forEach(entry -> {
var key = entry.getKey();
var errStat = entry.getValue();
var avgStat = avg.nodes.get(key);
var lower = new Statistics();
var upper = new Statistics();
Statistics.apply(lower, avgStat, errStat, (a, e) -> a - e);
Statistics.apply(upper, avgStat, errStat, (a, e) -> a + e);
lowerBound.nodes.put(key, lower);
upperBound.nodes.put(key, lower);
});
return new Result[] { lowerBound, upperBound };
return error;
}
}

View File

@@ -12,10 +12,12 @@ public class Statistics {
public double maxQueueLength = 0.0d;
public double avgQueueLength = 0.0d;
public double busyTime = 0.0d;
public double waitTime = 0.0d;
public double responseTime = 0.0d;
public double lastEventTime = 0.0d;
// derived stats, you can calculate them even at the end
public double avgWaitTime = 0.0d;
public double avgResponse = 0.0d;
public double troughput = 0.0d;
public double utilization = 0.0d;
@@ -50,7 +52,9 @@ public class Statistics {
this.responseTime += response;
this.busyTime += time - this.lastEventTime;
this.lastEventTime = time;
this.waitTime = this.responseTime - this.busyTime;
this.avgWaitTime = this.waitTime / this.numDepartures;
this.avgResponse = this.responseTime / this.numDepartures;
this.troughput = this.numDepartures / this.lastEventTime;
this.utilization = this.busyTime / this.lastEventTime;
@@ -102,10 +106,12 @@ public class Statistics {
save.avgQueueLength = func.apply(val1.avgQueueLength, val2.avgQueueLength);
save.busyTime = func.apply(val1.busyTime, val2.busyTime);
save.responseTime = func.apply(val1.responseTime, val2.responseTime);
save.waitTime = func.apply(val1.waitTime, val2.waitTime);
save.lastEventTime = func.apply(val1.lastEventTime, val2.lastEventTime);
// derived stats
save.avgWaitTime = func.apply(val1.avgWaitTime, val2.avgWaitTime);
save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse);
save.troughput = func.apply(val1.troughput, val2.troughput);
save.utilization = func.apply(val1.utilization, val2.utilization);
save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse);
}
}