End Simulation Criteria
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user