diff --git a/src/main/java/net/berack/upo/valpre/sim/Simulation.java b/src/main/java/net/berack/upo/valpre/sim/Simulation.java index 9f5ba5a..fb1f309 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Simulation.java +++ b/src/main/java/net/berack/upo/valpre/sim/Simulation.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.PriorityQueue; import net.berack.upo.valpre.rand.Rng; -import net.berack.upo.valpre.sim.stats.NetStatistics; -import net.berack.upo.valpre.sim.stats.NetStatistics.Statistics; +import net.berack.upo.valpre.sim.stats.Result; +import net.berack.upo.valpre.sim.stats.Statistics; /** * Process an entire run of the simulation. @@ -53,7 +53,7 @@ public final class Simulation { * * @return The final statistics the network. */ - public NetStatistics.RunResult run() { + public Result run() { while (!this.hasEnded()) this.processNextEvent(); return this.endSimulation(); @@ -97,13 +97,13 @@ public final class Simulation { * * @return The statistics of the network. */ - public NetStatistics.RunResult endSimulation() { + public Result endSimulation() { var elapsed = System.nanoTime() - this.timeStartedNano; var nodes = new HashMap(); for (var entry : this.nodes.entrySet()) nodes.put(entry.getKey(), entry.getValue().stats); - return new NetStatistics.RunResult(this.seed, this.time, elapsed, nodes); + return new Result(this.seed, this.time, elapsed, nodes); } /** diff --git a/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java b/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java index ceae9c2..d20ef6f 100644 --- a/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java +++ b/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java @@ -6,8 +6,8 @@ import java.util.concurrent.Future; import net.berack.upo.valpre.rand.Rng; import net.berack.upo.valpre.rand.Rngs; -import net.berack.upo.valpre.sim.stats.NetStatistics; -import net.berack.upo.valpre.sim.stats.NetStatistics.RunResult; +import net.berack.upo.valpre.sim.stats.ResultMultiple; +import net.berack.upo.valpre.sim.stats.Result; /** * A network simulation that uses a discrete event simulation to model the @@ -32,15 +32,15 @@ public class SimulationMultiple { * events. * @return The statistics the network. */ - public NetStatistics run(long seed, int runs, EndCriteria... criterias) { + public ResultMultiple run(long seed, int runs, EndCriteria... criterias) { var rng = new Rng(seed); - var stats = new RunResult[runs]; + var stats = new Result[runs]; for (int i = 0; i < runs; i++) { var sim = new Simulation(this.net, rng, criterias); stats[i] = sim.run(); } - return new NetStatistics(stats); + return new ResultMultiple(stats); } /** @@ -58,10 +58,10 @@ public class SimulationMultiple { * @throws InterruptedException If the threads are interrupted. * @throws ExecutionException If the one of the threads has been aborted. */ - public NetStatistics runParallel(long seed, int runs, EndCriteria... criterias) + public ResultMultiple runParallel(long seed, int runs, EndCriteria... criterias) throws InterruptedException, ExecutionException { var rngs = new Rngs(seed); - var results = new NetStatistics.RunResult[runs]; + var results = new Result[runs]; var futures = new Future[runs]; var numThreads = Math.min(runs, Runtime.getRuntime().availableProcessors()); @@ -78,7 +78,7 @@ public class SimulationMultiple { futures[i].get(); } - return new NetStatistics(results); + return new ResultMultiple(results); } } diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/NetStatistics.java b/src/main/java/net/berack/upo/valpre/sim/stats/NetStatistics.java deleted file mode 100644 index 03910d4..0000000 --- a/src/main/java/net/berack/upo/valpre/sim/stats/NetStatistics.java +++ /dev/null @@ -1,230 +0,0 @@ -package net.berack.upo.valpre.sim.stats; - -import java.util.HashMap; -import java.util.Map; - -/** - * TODO - */ -public class NetStatistics { - public final RunResult[] runs; - public final RunResult average; - public final RunResult variance; - - /** - * TODO - * - * @param runs - */ - public NetStatistics(RunResult... runs) { - this.runs = runs; - this.average = calcAvg(runs); - this.variance = calcVar(this.average, runs); - } - - /** - * TODO - * - * @param runs - * @return - */ - public static RunResult calcAvg(RunResult... runs) { - var avgTime = 0.0d; - var avgElapsed = 0L; - var nodes = new HashMap(); - - for (var run : runs) { - avgTime += run.simulationTime; - avgElapsed += run.timeElapsedNano; - - for (var entry : run.nodes.entrySet()) { - var stat = nodes.computeIfAbsent(entry.getKey(), _ -> new Statistics()); - var other = entry.getValue(); - stat.numDepartures += other.numDepartures; - stat.numArrivals += other.numArrivals; - stat.busyTime += other.busyTime; - stat.responseTime += other.responseTime; - stat.lastEventTime += other.lastEventTime; - stat.averageQueueLength += other.averageQueueLength; - stat.maxQueueLength = Math.max(stat.maxQueueLength, other.maxQueueLength); - } - } - - avgTime /= runs.length; - avgElapsed /= runs.length; - for (var stat : nodes.values()) { - stat.numDepartures /= runs.length; - stat.numArrivals /= runs.length; - stat.busyTime /= runs.length; - stat.responseTime /= runs.length; - stat.lastEventTime /= runs.length; - stat.averageQueueLength /= runs.length; - } - return new RunResult(runs[0].seed, avgTime, avgElapsed, nodes); - } - - /** - * TODO - * - * @param avg - * @param runs - * @return - */ - public static RunResult calcVar(RunResult avg, RunResult... runs) { - var varTime = 0.0d; - var varElapsed = 0L; - var nodes = new HashMap(); - - for (var run : runs) { - varTime += Math.pow(run.simulationTime - avg.simulationTime, 2); - varElapsed += Math.pow(run.timeElapsedNano - avg.simulationTime, 2); - - for (var entry : run.nodes.entrySet()) { - var stat = nodes.computeIfAbsent(entry.getKey(), _ -> new Statistics()); - var average = avg.nodes.get(entry.getKey()); - var other = entry.getValue(); - stat.numDepartures += Math.pow(other.numDepartures - average.numDepartures, 2); - stat.numArrivals += Math.pow(other.numArrivals - average.numArrivals, 2); - stat.busyTime += Math.pow(other.busyTime - average.busyTime, 2); - stat.responseTime += Math.pow(other.responseTime - average.responseTime, 2); - stat.lastEventTime += Math.pow(other.lastEventTime - average.lastEventTime, 2); - stat.averageQueueLength += Math.pow(other.averageQueueLength - average.averageQueueLength, 2); - } - } - - varTime /= runs.length - 1; - varElapsed /= runs.length - 1; - for (var stat : nodes.values()) { - stat.numDepartures /= runs.length - 1; - stat.numArrivals /= runs.length - 1; - stat.busyTime /= runs.length - 1; - stat.responseTime /= runs.length - 1; - stat.lastEventTime /= runs.length - 1; - stat.averageQueueLength /= runs.length - 1; - } - - return new RunResult(runs[0].seed, varTime, varElapsed, nodes); - } - - /** - * Represents the statistics of a network simulation. - * It is used by the simulation to track the behavior of the network and its - * nodes, including the number of arrivals and departures, the maximum queue - * length, the busy time, and the response time. - */ - public static class RunResult { - public final Map nodes; - public final long seed; - public final double simulationTime; - public final long timeElapsedNano; - - /** - * Creates a new statistics object for the given collection of server nodes and - * random number generator. - * - * @param nodes The collection of server nodes to track. - * @param rng The random number generator to use. - */ - public RunResult(long seed, double time, long elapsed, Map nodes) { - this.seed = seed; - this.simulationTime = time; - this.timeElapsedNano = elapsed; - this.nodes = nodes; - } - - /** - * Print a summary of the statistics to the console. - * The summary includes the seed, the simulation time, the elapsed time, and - * 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"; - var builder = new StringBuilder(); - - for (var entry : this.nodes.entrySet()) { - var stats = entry.getValue(); - var busy = stats.busyTime * 100 / stats.lastEventTime; - var avgResp = stats.responseTime / stats.numDepartures; - - builder.append("===== " + entry.getKey() + " =====\n"); - builder.append(String.format(" Arrivals: \t" + iFormat + "\n", stats.numArrivals)); - builder.append(String.format(" Departures:\t" + iFormat + "\n", stats.numDepartures)); - builder.append(String.format(" Max Queue: \t" + iFormat + "\n", stats.maxQueueLength)); - builder.append(String.format(" Avg Queue: \t" + fFormat + "\n", stats.averageQueueLength)); - builder.append(String.format(" Response: \t" + fFormat + "\n", avgResp)); - builder.append(String.format(" Busy %%: \t" + fFormat + "\n", busy)); - builder.append(String.format(" Last Event:\t" + fFormat + "\n", stats.lastEventTime)); - } - return builder.toString(); - } - - /** - * TODO - */ - public String getSummaryAsTable() { - var size = (int) Math.ceil(Math.log10(this.simulationTime)); - var iFormat = "%" + size + ".0f"; - var fFormat = "%" + (size + 4) + ".3f"; - - String[] h = { "Node", "Arrivals", "Departures", "Max Queue", "Avg Queue", "Response", "Busy %", - "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.numArrivals), - String.format(iFormat, stats.numDepartures), - String.format(iFormat, stats.maxQueueLength), - String.format(fFormat, stats.averageQueueLength), - String.format(fFormat, stats.responseTime / stats.numDepartures), - String.format(fFormat, stats.busyTime * 100 / stats.lastEventTime), - String.format(fFormat, stats.lastEventTime)); - } - return table.toString(); - } - - /** - * TODO - */ - 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.timeElapsedNano / 1e6)); - return builder.toString(); - } - } - - /** - * TODO - */ - public static class Statistics { - public double numArrivals = 0; - public double numDepartures = 0; - public double maxQueueLength = 0; - public double averageQueueLength = 0.0d; - public double busyTime = 0.0d; - public double responseTime = 0.0d; - public double lastEventTime = 0.0d; - - /** - * Resets the statistics to their initial values. - */ - public void reset() { - this.numArrivals = 0; - this.numDepartures = 0; - this.maxQueueLength = 0; - this.averageQueueLength = 0.0d; - this.busyTime = 0.0d; - this.responseTime = 0.0d; - this.lastEventTime = 0.0d; - } - } -} \ No newline at end of file diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/Result.java b/src/main/java/net/berack/upo/valpre/sim/stats/Result.java new file mode 100644 index 0000000..5c6d12c --- /dev/null +++ b/src/main/java/net/berack/upo/valpre/sim/stats/Result.java @@ -0,0 +1,99 @@ +package net.berack.upo.valpre.sim.stats; + +import java.util.Map; + +/** + * Represents the statistics of a network simulation. + * It is used by the simulation to track the behavior of the network and its + * nodes, including the number of arrivals and departures, the maximum queue + * length, the busy time, and the response time. + */ +public class Result { + public final Map nodes; + public final long seed; + public final double simulationTime; + public final long timeElapsedNano; + + /** + * Creates a new statistics object for the given collection of server nodes and + * random number generator. + * + * @param nodes The collection of server nodes to track. + * @param rng The random number generator to use. + */ + public Result(long seed, double time, long elapsed, Map nodes) { + this.seed = seed; + this.simulationTime = time; + this.timeElapsedNano = elapsed; + this.nodes = nodes; + } + + /** + * Print a summary of the statistics to the console. + * The summary includes the seed, the simulation time, the elapsed time, and + * 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"; + var builder = new StringBuilder(); + + for (var entry : this.nodes.entrySet()) { + var stats = entry.getValue(); + var busy = stats.busyTime * 100 / stats.lastEventTime; + var avgResp = stats.responseTime / stats.numDepartures; + + builder.append("===== " + entry.getKey() + " =====\n"); + builder.append(String.format(" Arrivals: \t" + iFormat + "\n", stats.numArrivals)); + builder.append(String.format(" Departures:\t" + iFormat + "\n", stats.numDepartures)); + builder.append(String.format(" Max Queue: \t" + iFormat + "\n", stats.maxQueueLength)); + builder.append(String.format(" Avg Queue: \t" + fFormat + "\n", stats.averageQueueLength)); + builder.append(String.format(" Response: \t" + fFormat + "\n", avgResp)); + builder.append(String.format(" Busy %%: \t" + fFormat + "\n", busy)); + builder.append(String.format(" Last Event:\t" + fFormat + "\n", stats.lastEventTime)); + } + return builder.toString(); + } + + /** + * TODO + */ + public String getSummaryAsTable() { + var size = (int) Math.ceil(Math.log10(this.simulationTime)); + var iFormat = "%" + size + ".0f"; + var fFormat = "%" + (size + 4) + ".3f"; + + String[] h = { "Node", "Arrivals", "Departures", "Max Queue", "Avg Queue", "Response", "Busy %", + "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.numArrivals), + String.format(iFormat, stats.numDepartures), + String.format(iFormat, stats.maxQueueLength), + String.format(fFormat, stats.averageQueueLength), + String.format(fFormat, stats.responseTime / stats.numDepartures), + String.format(fFormat, stats.busyTime * 100 / stats.lastEventTime), + String.format(fFormat, stats.lastEventTime)); + } + return table.toString(); + } + + /** + * TODO + */ + 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.timeElapsedNano / 1e6)); + return builder.toString(); + } +} diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/ResultMultiple.java b/src/main/java/net/berack/upo/valpre/sim/stats/ResultMultiple.java new file mode 100644 index 0000000..9327def --- /dev/null +++ b/src/main/java/net/berack/upo/valpre/sim/stats/ResultMultiple.java @@ -0,0 +1,107 @@ +package net.berack.upo.valpre.sim.stats; + +import java.util.HashMap; + +/** + * TODO + */ +public class ResultMultiple { + public final Result[] runs; + public final Result average; + public final Result variance; + + /** + * TODO + * + * @param runs + */ + public ResultMultiple(Result... runs) { + this.runs = runs; + this.average = calcAvg(runs); + this.variance = calcVar(this.average, runs); + } + + /** + * TODO + * + * @param runs + * @return + */ + public static Result calcAvg(Result... runs) { + var avgTime = 0.0d; + var avgElapsed = 0L; + var nodes = new HashMap(); + + for (var run : runs) { + avgTime += run.simulationTime; + avgElapsed += run.timeElapsedNano; + + for (var entry : run.nodes.entrySet()) { + var stat = nodes.computeIfAbsent(entry.getKey(), _ -> new Statistics()); + var other = entry.getValue(); + stat.numDepartures += other.numDepartures; + stat.numArrivals += other.numArrivals; + stat.busyTime += other.busyTime; + stat.responseTime += other.responseTime; + stat.lastEventTime += other.lastEventTime; + stat.averageQueueLength += other.averageQueueLength; + stat.maxQueueLength = Math.max(stat.maxQueueLength, other.maxQueueLength); + } + } + + avgTime /= runs.length; + avgElapsed /= runs.length; + for (var stat : nodes.values()) { + stat.numDepartures /= runs.length; + stat.numArrivals /= runs.length; + stat.busyTime /= runs.length; + stat.responseTime /= runs.length; + stat.lastEventTime /= runs.length; + stat.averageQueueLength /= runs.length; + } + return new Result(runs[0].seed, avgTime, avgElapsed, nodes); + } + + /** + * TODO + * + * @param avg + * @param runs + * @return + */ + public static Result calcVar(Result avg, Result... runs) { + var varTime = 0.0d; + var varElapsed = 0L; + var nodes = new HashMap(); + + for (var run : runs) { + varTime += Math.pow(run.simulationTime - avg.simulationTime, 2); + varElapsed += Math.pow(run.timeElapsedNano - avg.simulationTime, 2); + + for (var entry : run.nodes.entrySet()) { + var stat = nodes.computeIfAbsent(entry.getKey(), _ -> new Statistics()); + var average = avg.nodes.get(entry.getKey()); + var other = entry.getValue(); + stat.numDepartures += Math.pow(other.numDepartures - average.numDepartures, 2); + stat.numArrivals += Math.pow(other.numArrivals - average.numArrivals, 2); + stat.busyTime += Math.pow(other.busyTime - average.busyTime, 2); + stat.responseTime += Math.pow(other.responseTime - average.responseTime, 2); + stat.lastEventTime += Math.pow(other.lastEventTime - average.lastEventTime, 2); + stat.averageQueueLength += Math.pow(other.averageQueueLength - average.averageQueueLength, 2); + } + } + + varTime /= runs.length - 1; + varElapsed /= runs.length - 1; + for (var stat : nodes.values()) { + stat.numDepartures /= runs.length - 1; + stat.numArrivals /= runs.length - 1; + stat.busyTime /= runs.length - 1; + stat.responseTime /= runs.length - 1; + stat.lastEventTime /= runs.length - 1; + stat.averageQueueLength /= runs.length - 1; + } + + return new Result(runs[0].seed, varTime, varElapsed, nodes); + } +} \ No newline at end of file diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java b/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java new file mode 100644 index 0000000..782766f --- /dev/null +++ b/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java @@ -0,0 +1,55 @@ +package net.berack.upo.valpre.sim.stats; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * TODO + */ +public class Statistics { + public double numArrivals = 0; + public double numDepartures = 0; + public double maxQueueLength = 0; + public double averageQueueLength = 0.0d; + public double busyTime = 0.0d; + public double responseTime = 0.0d; + public double lastEventTime = 0.0d; + + /** + * Resets the statistics to their initial values. + */ + public void reset() { + this.applyToAll(_ -> 0.0d); + } + + /** + * Apply a function to ALL the stats in this class. + * The input of the function is the current value of the stat. + * + * @param func a function to apply + */ + public void applyToAll(Function func) { + this.numArrivals = func.apply(this.numArrivals); + this.numDepartures = func.apply(this.numDepartures); + this.maxQueueLength = func.apply(this.maxQueueLength); + this.averageQueueLength = func.apply(this.averageQueueLength); + this.busyTime = func.apply(this.busyTime); + this.responseTime = func.apply(this.responseTime); + this.lastEventTime = func.apply(this.lastEventTime); + } + + /** + * A function used to merge two stats. + * @param other + * @param func + */ + public void mergeWith(Statistics other, BiFunction func) { + this.numArrivals = func.apply(other.numArrivals, this.numArrivals); + this.numDepartures = func.apply(other.numDepartures, this.numDepartures); + this.maxQueueLength = func.apply(other.maxQueueLength, this.maxQueueLength); + this.averageQueueLength = func.apply(other.averageQueueLength, this.averageQueueLength); + this.busyTime = func.apply(other.busyTime, this.busyTime); + this.responseTime = func.apply(other.responseTime, this.responseTime); + this.lastEventTime = func.apply(other.lastEventTime, this.lastEventTime); + } +} \ No newline at end of file