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 class Main {
public static void main(String[] args) { 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 // Build the network
var node1 = ServerNode.createSource("Source", new Distribution.Exponential(1.0 / 4.5)); var node1 = ServerNode.createLimitedSource("Source", new Distribution.Exponential(lambda), total);
var node2 = ServerNode.createQueue("Queue", 1, new Distribution.NormalBoxMuller(3.2, 0.6)); var node2 = ServerNode.createQueue("Queue", 1, new Distribution.NormalBoxMuller(mu, sigma));
node1.addChild(node2, 1.0); node1.addChild(node2, 1.0);
/// Run the simulation /// Run the simulation
var sim = new NetSimulation(System.nanoTime()); var sim = new NetSimulation(seed);
sim.addNode(node1); sim.addNode(node1);
sim.addNode(node2); 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 // Display the results
for (var entry : results.entrySet()) { for (var entry : results.entrySet()) {

View File

@@ -12,7 +12,6 @@ import net.berack.upo.valpre.rand.Rng;
*/ */
public class NetSimulation { public class NetSimulation {
public final long seed; public final long seed;
private final Rng rng;
private final Map<String, ServerNode> servers = new HashMap<>(); private final Map<String, ServerNode> servers = new HashMap<>();
/** /**
@@ -22,7 +21,6 @@ public class NetSimulation {
*/ */
public NetSimulation(long seed) { public NetSimulation(long seed) {
this.seed = 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 * Runs the simulation for the given number of total arrivals, stopping when the
* given node has reached the * given node has reached the specified number of departures.
* 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 criteria The criteria to determine when to end the simulation. If null
* @param untilDepartureNode The name of the node to stop at. * then the simulation will run until there are no more events.
* @return A map of statistics for each server node in the network. * @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 // Initialization
var timeNow = 0.0d; 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 fel = new PriorityQueue<Event>();
var stats = new HashMap<String, Statistics>();
for (var node : this.servers.values()) { for (var node : this.servers.values()) {
var s = new Statistics(this.rng); var s = new Statistics(rng);
s.addArrivalIf(node.isSource, node, timeNow, fel); s.addArrivalIf(node.shouldSpawnArrival(s.numArrivals), node, timeNow, fel);
stats.put(node.name, s); stats.put(node.name, s);
} }
// Main Simulation Loop // Main Simulation Loop
var nodeStop = stats.get(untilDepartureNode); while (!fel.isEmpty() && !this.shouldEnd(criteria, stats)) {
while (nodeStop.numDepartures < total) {
var event = fel.poll(); var event = fel.poll();
if (event == null) {
break;
}
timeNow = event.time;
var statsNode = stats.get(event.node.name); var statsNode = stats.get(event.node.name);
timeNow = event.time;
switch (event.type) { switch (event.type) {
case ARRIVAL -> statsNode.processArrival(event, timeNow, fel); case ARRIVAL -> statsNode.processArrival(event, timeNow, fel);
case DEPARTURE -> statsNode.processDeparture(event, timeNow, fel); case DEPARTURE -> statsNode.processDeparture(event, timeNow, fel);
@@ -72,6 +67,15 @@ public class NetSimulation {
return stats; 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 * Represents a statistical summary of the behavior of a server node in the
* network. * network.
@@ -137,7 +141,7 @@ public class NetSimulation {
} }
this.lastEventTime = timeNow; 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; this.lastEventTime = timeNow;
var next = event.node.getChild(rng); 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,51 +12,71 @@ import net.berack.upo.valpre.rand.Rng;
public class ServerNode { public class ServerNode {
public final String name; public final String name;
public final int maxServers; public final int maxServers;
public final int sinkDepartures;
public final int spawnArrivals;
public final Distribution distribution; public final Distribution distribution;
public final boolean isSink;
public final boolean isSource;
private final List<NodeChild> children = new ArrayList<>(); private final List<NodeChild> children = new ArrayList<>();
private double sumProbabilities = 0.0; private double sumProbabilities = 0.0;
/** /**
* Creates a source node with the given name and distribution. * Creates a source node with the given name and distribution.
* It swpawns infinite arrivals (Integer.MAX_VALUE) that are served by infinite
* servers (Integer.MAX_VALUE).
*
* @param name The name of the node. * @param name The name of the node.
* @param distribution The distribution of the inter-arrival times. * @param distribution The distribution of the inter-arrival times.
* @return The created source node. * @return The created source node.
*/ */
public static ServerNode createSource(String name, Distribution distribution) { 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. * 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 name The name of the node.
* @param maxServers The maximum number of servers in the queue. * @param maxServers The maximum number of servers in the queue.
* @param distribution The distribution of the service times. * @param distribution The distribution of the service times.
* @return The created queue node. * @return The created queue node.
*/ */
public static ServerNode createQueue(String name, int maxServers, Distribution distribution) { 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. * Creates a generic node with the given name and distribution.
*
* @param name The name of the node. * @param name The name of the node.
* @param maxServers The maximum number of servers in the queue. * @param maxServers The maximum number of servers in the queue.
* @param distribution The distribution of the service times. * @param distribution The distribution of the service times.
* @param isSink Whether the node is a sink. * @param spawnArrivals The number of arrivals to spawn.
* @param isSource Whether the node is a source. * @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.name = name;
this.maxServers = maxServers; this.maxServers = maxServers;
this.distribution = distribution; this.distribution = distribution;
this.isSink = isSink; this.spawnArrivals = spawnArrivals;
this.isSource = isSource; this.sinkDepartures = sinkDepartures;
} }
/** /**
* Adds a child node with the given probability to select it. * 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. * @param probability The probability of the child node.
*/ */
@@ -67,6 +87,7 @@ public class ServerNode {
/** /**
* Gets a child node based on the given random number generator. * Gets a child node based on the given random number generator.
*
* @param rng The random number generator to use. * @param rng The random number generator to use.
* @return The child node selected based on the probabilities. * @return The child node selected based on the probabilities.
*/ */
@@ -81,6 +102,28 @@ public class ServerNode {
return null; 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. * 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. * Creates a new exponential distribution with the given rate.
*
* @param lambda The rate of the distribution. * @param lambda The rate of the distribution.
*/ */
public Exponential(double lambda) { public Exponential(double lambda) {
@@ -35,6 +36,7 @@ public interface Distribution {
/** /**
* Creates a new normal distribution with the given mean and standard deviation. * 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. * @param sigma The standard deviation of the distribution.
*/ */
@@ -60,6 +62,7 @@ public interface Distribution {
/** /**
* Creates a new normal distribution with the given mean and standard deviation. * 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. * @param sigma The standard deviation of the distribution.
*/ */