End Simulation Criteria

This commit is contained in:
2025-01-17 19:39:51 +01:00
parent ccde6a9668
commit 7b46054b70
5 changed files with 196 additions and 43 deletions

View File

@@ -0,0 +1,93 @@
package net.berack.upo.valpre;
import java.util.Map;
import net.berack.upo.valpre.NetSimulation.Statistics;
/**
* Criteria to determine when to end the simulation.
*/
public interface EndSimulationCriteria {
/**
* Determines if the simulation should end based on the statistics of the nodes.
*
* @param stats The statistics of the nodes in the network.
* @return True if the simulation should end, false otherwise.
*/
public boolean shouldEnd(Map<String, Statistics> stats);
/**
* Ends the simulation when the given node has reached the specified number of
* arrivals.
*/
public static class MaxArrivals implements EndSimulationCriteria {
private final String nodeName;
private final int maxArrivals;
/**
* Creates a new criteria to end the simulation when the given node has reached
* the specified number of arrivals.
*
* @param nodeName The name of the node to check.
* @param maxArrivals The maximum number of arrivals to wait for.
*/
public MaxArrivals(String nodeName, int maxArrivals) {
this.nodeName = nodeName;
this.maxArrivals = maxArrivals;
}
@Override
public boolean shouldEnd(Map<String, Statistics> stats) {
return stats.get(nodeName).numArrivals >= this.maxArrivals;
}
}
/**
* Ends the simulation when the given node has reached the specified number of
* departures.
*/
public static class MaxDepartures implements EndSimulationCriteria {
private final String nodeName;
private final int maxDepartures;
/**
* Creates a new criteria to end the simulation when the given node has reached
* the specified number of departures.
*
* @param nodeName The name of the node to check.
* @param maxDepartures The maximum number of departures to wait for.
*/
public MaxDepartures(String nodeName, int maxDepartures) {
this.nodeName = nodeName;
this.maxDepartures = maxDepartures;
}
@Override
public boolean shouldEnd(Map<String, Statistics> stats) {
return stats.get(nodeName).numDepartures >= this.maxDepartures;
}
}
/**
* Ends the simulation when the given node has reached the specified number of
* departures.
*/
public static class MaxTime implements EndSimulationCriteria {
private final double maxTime;
/**
* Creates a new criteria to end the simulation when the given node has reached
* the specified number of departures.
*
* @param maxTime The maximum time to wait for.
*/
public MaxTime(double maxTime) {
this.maxTime = maxTime;
}
@Override
public boolean shouldEnd(Map<String, Statistics> stats) {
return stats.values().stream().anyMatch(s -> s.lastEventTime >= this.maxTime);
}
}
}

View File

@@ -4,16 +4,26 @@ import net.berack.upo.valpre.rand.Distribution;
public class Main {
public static void main(String[] args) {
// Parameters for the simulation
var seed = System.nanoTime();
var total = 1000;
var lambda = 1.0 / 4.5;
var mu = 3.2;
var sigma = 0.6;
// Build the network
var node1 = ServerNode.createSource("Source", new Distribution.Exponential(1.0 / 4.5));
var node2 = ServerNode.createQueue("Queue", 1, new Distribution.NormalBoxMuller(3.2, 0.6));
var node1 = ServerNode.createLimitedSource("Source", new Distribution.Exponential(lambda), total);
var node2 = ServerNode.createQueue("Queue", 1, new Distribution.NormalBoxMuller(mu, sigma));
node1.addChild(node2, 1.0);
/// Run the simulation
var sim = new NetSimulation(System.nanoTime());
var sim = new NetSimulation(seed);
sim.addNode(node1);
sim.addNode(node2);
var results = sim.run(1000, "Queue");
var maxDepartures = new EndSimulationCriteria.MaxDepartures("Queue", total);
var maxTime = new EndSimulationCriteria.MaxTime(1000.0);
var results = sim.run(maxDepartures, maxTime);
// Display the results
for (var entry : results.entrySet()) {

View File

@@ -12,7 +12,6 @@ import net.berack.upo.valpre.rand.Rng;
*/
public class NetSimulation {
public final long seed;
private final Rng rng;
private final Map<String, ServerNode> servers = new HashMap<>();
/**
@@ -22,7 +21,6 @@ public class NetSimulation {
*/
public NetSimulation(long seed) {
this.seed = seed;
this.rng = new Rng(seed);
}
/**
@@ -36,34 +34,31 @@ public class NetSimulation {
/**
* Runs the simulation for the given number of total arrivals, stopping when the
* given node has reached the
* specified number of departures.
* given node has reached the specified number of departures.
* If needed the run method can be called by multiple threads.
*
* @param total The total number of arrivals to simulate.
* @param untilDepartureNode The name of the node to stop at.
* @return A map of statistics for each server node in the network.
* @param criteria The criteria to determine when to end the simulation. If null
* then the simulation will run until there are no more events.
* @return The statistics of the nodes in the network.
*/
public Map<String, Statistics> run(long total, String untilDepartureNode) {
public Map<String, Statistics> run(EndSimulationCriteria... criteria) {
// Initialization
var timeNow = 0.0d;
var stats = new HashMap<String, Statistics>();
var rng = new Rng(this.seed); // TODO change here for thread variance (use Rngs with ids)
var fel = new PriorityQueue<Event>();
var stats = new HashMap<String, Statistics>();
for (var node : this.servers.values()) {
var s = new Statistics(this.rng);
s.addArrivalIf(node.isSource, node, timeNow, fel);
var s = new Statistics(rng);
s.addArrivalIf(node.shouldSpawnArrival(s.numArrivals), node, timeNow, fel);
stats.put(node.name, s);
}
// Main Simulation Loop
var nodeStop = stats.get(untilDepartureNode);
while (nodeStop.numDepartures < total) {
while (!fel.isEmpty() && !this.shouldEnd(criteria, stats)) {
var event = fel.poll();
if (event == null) {
break;
}
timeNow = event.time;
var statsNode = stats.get(event.node.name);
timeNow = event.time;
switch (event.type) {
case ARRIVAL -> statsNode.processArrival(event, timeNow, fel);
case DEPARTURE -> statsNode.processDeparture(event, timeNow, fel);
@@ -72,6 +67,15 @@ public class NetSimulation {
return stats;
}
private boolean shouldEnd(EndSimulationCriteria[] criteria, Map<String, Statistics> stats) {
for (var c : criteria) {
if (c.shouldEnd(stats)) {
return true;
}
}
return false;
}
/**
* Represents a statistical summary of the behavior of a server node in the
* network.
@@ -137,7 +141,7 @@ public class NetSimulation {
}
this.lastEventTime = timeNow;
this.addArrivalIf(event.node.isSource, event.node, timeNow, fel);
this.addArrivalIf(event.node.shouldSpawnArrival(this.numArrivals), event.node, timeNow, fel);
}
/**
@@ -170,7 +174,7 @@ public class NetSimulation {
this.lastEventTime = timeNow;
var next = event.node.getChild(rng);
this.addArrivalIf(!event.node.isSink, next, timeNow, fel);
this.addArrivalIf(!event.node.shouldSinkDeparture(this.numDepartures), next, timeNow, fel);
}
/**

View File

@@ -12,52 +12,72 @@ import net.berack.upo.valpre.rand.Rng;
public class ServerNode {
public final String name;
public final int maxServers;
public final int sinkDepartures;
public final int spawnArrivals;
public final Distribution distribution;
public final boolean isSink;
public final boolean isSource;
private final List<NodeChild> children = new ArrayList<>();
private double sumProbabilities = 0.0;
/**
* Creates a source node with the given name and distribution.
* @param name The name of the node.
* It swpawns infinite arrivals (Integer.MAX_VALUE) that are served by infinite
* servers (Integer.MAX_VALUE).
*
* @param name The name of the node.
* @param distribution The distribution of the inter-arrival times.
* @return The created source node.
*/
public static ServerNode createSource(String name, Distribution distribution) {
return new ServerNode(name, Integer.MAX_VALUE, distribution, false, true);
return new ServerNode(name, Integer.MAX_VALUE, distribution, Integer.MAX_VALUE, 0);
}
/**
* Creates a queue node with the given name, maximum number of servers, and distribution.
* @param name The name of the node.
* @param maxServers The maximum number of servers in the queue.
* Creates a source node with the given name, distribution, and number of
* arrivals to spawn that are served by infinite servers (Integer.MAX_VALUE).
*
* @param name The name of the node.
* @param distribution The distribution of the inter-arrival times.
* @param spawnArrivals The number of arrivals to spawn.
* @return The created source node.
*/
public static ServerNode createLimitedSource(String name, Distribution distribution, int spawnArrivals) {
return new ServerNode(name, Integer.MAX_VALUE, distribution, spawnArrivals, 0);
}
/**
* Creates a queue node with the given name, maximum number of servers, and
* distribution.
*
* @param name The name of the node.
* @param maxServers The maximum number of servers in the queue.
* @param distribution The distribution of the service times.
* @return The created queue node.
*/
public static ServerNode createQueue(String name, int maxServers, Distribution distribution) {
return new ServerNode(name, maxServers, distribution, false, false);
return new ServerNode(name, maxServers, distribution, 0, 0);
}
/**
* Creates a generic node with the given name and distribution.
* @param name The name of the node.
* @param maxServers The maximum number of servers in the queue.
* @param distribution The distribution of the service times.
* @param isSink Whether the node is a sink.
* @param isSource Whether the node is a source.
*
* @param name The name of the node.
* @param maxServers The maximum number of servers in the queue.
* @param distribution The distribution of the service times.
* @param spawnArrivals The number of arrivals to spawn.
* @param sinkDepartures The number of departures to sink.
*/
public ServerNode(String name, int maxServers, Distribution distribution, boolean isSink, boolean isSource) {
public ServerNode(String name, int maxServers, Distribution distribution, int spawnArrivals, int sinkDepartures) {
this.name = name;
this.maxServers = maxServers;
this.distribution = distribution;
this.isSink = isSink;
this.isSource = isSource;
this.spawnArrivals = spawnArrivals;
this.sinkDepartures = sinkDepartures;
}
/**
* Adds a child node with the given probability to select it.
* @param node The child node to add.
*
* @param node The child node to add.
* @param probability The probability of the child node.
*/
public void addChild(ServerNode node, double probability) {
@@ -67,6 +87,7 @@ public class ServerNode {
/**
* Gets a child node based on the given random number generator.
*
* @param rng The random number generator to use.
* @return The child node selected based on the probabilities.
*/
@@ -81,6 +102,28 @@ public class ServerNode {
return null;
}
/**
* Determines if the node should spawn an arrival based on the number of
* arrivals.
*
* @param numArrivals The number of arrivals to check against.
* @return True if the node should spawn an arrival, false otherwise.
*/
public boolean shouldSpawnArrival(int numArrivals) {
return this.spawnArrivals > numArrivals;
}
/**
* Determines if the node should sink a departure based on the number of
* departures.
*
* @param numDepartures The number of departures to check against.
* @return True if the node should sink a departure, false otherwise.
*/
public boolean shouldSinkDeparture(int numDepartures) {
return this.sinkDepartures > numDepartures;
}
/**
* Represents a child node with a probability to select it.
*/

View File

@@ -14,6 +14,7 @@ public interface Distribution {
/**
* Creates a new exponential distribution with the given rate.
*
* @param lambda The rate of the distribution.
*/
public Exponential(double lambda) {
@@ -35,7 +36,8 @@ public interface Distribution {
/**
* Creates a new normal distribution with the given mean and standard deviation.
* @param mean The mean of the distribution.
*
* @param mean The mean of the distribution.
* @param sigma The standard deviation of the distribution.
*/
public Normal(double mean, double sigma) {
@@ -60,7 +62,8 @@ public interface Distribution {
/**
* Creates a new normal distribution with the given mean and standard deviation.
* @param mean The mean of the distribution.
*
* @param mean The mean of the distribution.
* @param sigma The standard deviation of the distribution.
*/
public NormalBoxMuller(double mean, double sigma) {