- CsvResult and Result classes for improved iteration and CSV output;
- update SimulationBuilder to include confidence index handling
- rename setRuns to setMaxRuns for clarity
This commit is contained in:
2025-02-10 15:19:40 +01:00
parent 82c237bc16
commit 0ac111d94a
8 changed files with 281 additions and 31 deletions

View File

@@ -44,7 +44,7 @@ public class CsvResult {
try (var writer = new FileWriter(this.file)) {
for (var result : results) {
for (var entry : result.nodes.entrySet()) {
for (var entry : result) {
builder.append(result.seed).append(",");
builder.append(entry.getKey()).append(",");
builder.append(CsvResult.statsToCSV(entry.getValue())).append('\n');

View File

@@ -1,6 +1,7 @@
package net.berack.upo.valpre.sim.stats;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -14,7 +15,11 @@ import org.apache.commons.math3.distribution.TDistribution;
* statistics are updated during simulation events, such as arrivals and
* departures, and can be used to analyze the net's behavior and performance.
*/
public class NodeStats implements Cloneable {
public class NodeStats implements Cloneable, Iterable<Double> {
private static final String[] ORDER_OF_APPLY = { "numArrivals", "numDepartures", "maxQueueLength", "avgQueueLength",
"avgWaitTime", "avgResponse", "busyTime", "waitTime", "unavailableTime", "responseTime", "lastEventTime",
"throughput", "utilization", "unavailable" };
public double numArrivals = 0.0d;
public double numDepartures = 0.0d;
public double maxQueueLength = 0.0d;
@@ -95,8 +100,6 @@ public class NodeStats implements Cloneable {
/**
* Apply a function to ALL the stats in this class.
* The only stats that are not updated with this function are the one that
* starts with max, min (since they are special)
* The input of the function is the current value of the stat.
*
* @param func a function to apply
@@ -106,19 +109,17 @@ public class NodeStats implements Cloneable {
}
/**
* A function used to merge tree stats.
* The only stats that are not updated with this function are the one that
* starts with max, min (since they are special)
* A function used to merge two sets of statistics.
*
* @param other
* @param func
* @param other the other stats to merge
* @param func the function to merge the stats
*/
public NodeStats merge(NodeStats other, BiFunction<Double, Double, Double> func) {
return NodeStats.operation(this, this, other, func);
}
@Override
protected NodeStats clone() {
public NodeStats clone() {
try {
return (NodeStats) super.clone();
} catch (CloneNotSupportedException e) {
@@ -127,6 +128,23 @@ public class NodeStats implements Cloneable {
}
}
@Override
public Iterator<Double> iterator() {
return new Iterator<>() {
private int index = 0;
@Override
public boolean hasNext() {
return this.index < ORDER_OF_APPLY.length;
}
@Override
public Double next() {
return NodeStats.this.of(ORDER_OF_APPLY[this.index++]);
}
};
}
/**
* Get the value of the stat.
*
@@ -159,9 +177,7 @@ public class NodeStats implements Cloneable {
* @return the order of the stats
*/
public static String[] getOrderOfApply() {
return new String[] { "numArrivals", "numDepartures", "avgQueueLength", "avgWaitTime", "avgResponse",
"busyTime", "waitTime", "unavailableTime", "responseTime", "lastEventTime", "throughput", "utilization",
"unavailable" };
return ORDER_OF_APPLY;
}
/**
@@ -185,7 +201,7 @@ public class NodeStats implements Cloneable {
BiFunction<Double, Double, Double> func) {
save.numArrivals = func.apply(val1.numArrivals, val2.numArrivals);
save.numDepartures = func.apply(val1.numDepartures, val2.numDepartures);
// save.maxQueueLength = func.apply(val1.maxQueueLength, val2.maxQueueLength);
save.maxQueueLength = func.apply(val1.maxQueueLength, val2.maxQueueLength);
save.avgQueueLength = func.apply(val1.avgQueueLength, val2.avgQueueLength);
save.avgWaitTime = func.apply(val1.avgWaitTime, val2.avgWaitTime);
save.avgResponse = func.apply(val1.avgResponse, val2.avgResponse);
@@ -264,11 +280,27 @@ public class NodeStats implements Cloneable {
* @return the error of the values
*/
public NodeStats calcError(double alpha) {
return this.calcError(new NodeStats().apply(_ -> alpha));
}
/**
* Calculates the error at the selected alpha level for each NodeStats.
* This method computes the error for the average and standard deviation values,
* considering the sample size and the confidence level (alpha).
* The result is adjusted using a t-distribution to account for the variability
* in smaller sample sizes.
*
* @param distribution the t-distribution to use
* @param stdDev the standard deviation of the values
* @param alpha the alpha values for each statistics
* @return the error of the values
*/
public NodeStats calcError(NodeStats alpha) {
var n = this.stats.size();
var distr = new TDistribution(null, n - 1);
var tValue = distr.inverseCumulativeProbability(alpha);
var tValue = alpha.clone().apply(a -> distr.inverseCumulativeProbability(a));
return this.stdDev().apply(std -> tValue * (std / Math.sqrt(n)));
return this.stdDev().merge(tValue, (std, t) -> t * (std / Math.sqrt(n)));
}
/**

View File

@@ -5,6 +5,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Represents the statistics of a network simulation.
@@ -12,7 +13,7 @@ import java.util.Map;
* nodes, including the number of arrivals and departures, the maximum queue
* length, the busy time, and the response time.
*/
public class Result {
public class Result implements Iterable<Entry<String, NodeStats>> {
public final Map<String, NodeStats> nodes;
public final long seed;
public final double simulationTime;
@@ -39,6 +40,11 @@ public class Result {
return buildPrintable(this.seed, this.simulationTime, this.timeElapsedMS, this.nodes);
}
@Override
public java.util.Iterator<Entry<String, NodeStats>> iterator() {
return this.nodes.entrySet().iterator();
}
private static String buildPrintable(long seed, double simTime, double timeMS, Map<String, NodeStats> nodes) {
var size = (int) Math.ceil(Math.max(Math.log10(simTime), 1));
var iFormat = "%" + size + ".0f";