JavaDoc added

This commit is contained in:
2025-01-17 12:40:18 +01:00
parent 0d541f7737
commit ccde6a9668
4 changed files with 158 additions and 0 deletions

View File

@@ -1,16 +1,26 @@
package net.berack.upo.valpre; package net.berack.upo.valpre;
/**
* Represents an event in the simulation.
*/
public class Event implements Comparable<Event> { public class Event implements Comparable<Event> {
public final double time; public final double time;
public final Type type; public final Type type;
public final ServerNode node; public final ServerNode node;
/**
* Create a new event.
* @param type The type of event.
* @param node The node that the event is associated with.
* @param time The time at which the event occurs.
*/
private Event(Type type, ServerNode node, double time) { private Event(Type type, ServerNode node, double time) {
this.type = type; this.type = type;
this.time = time; this.time = time;
this.node = node; this.node = node;
} }
@Override
public int compareTo(Event other) { public int compareTo(Event other) {
if (this.time < other.time) if (this.time < other.time)
return -1; return -1;
@@ -19,14 +29,29 @@ public class Event implements Comparable<Event> {
return 1; return 1;
} }
/**
* Create a new arrival event.
* @param node The node that the event is associated with.
* @param time The time at which the event occurs.
* @return The new event.
*/
public static Event newArrival(ServerNode node, double time) { public static Event newArrival(ServerNode node, double time) {
return new Event(Type.ARRIVAL, node, time); return new Event(Type.ARRIVAL, node, time);
} }
/**
* Create a new departure event.
* @param node The node that the event is associated with.
* @param time The time at which the event occurs.
* @return The new event.
*/
public static Event newDeparture(ServerNode node, double time) { public static Event newDeparture(ServerNode node, double time) {
return new Event(Type.DEPARTURE, node, time); return new Event(Type.DEPARTURE, node, time);
} }
/**
* The type of event.
*/
public static enum Type { public static enum Type {
ARRIVAL, ARRIVAL,
DEPARTURE, DEPARTURE,

View File

@@ -6,20 +6,43 @@ import java.util.Map;
import java.util.PriorityQueue; import java.util.PriorityQueue;
import net.berack.upo.valpre.rand.Rng; import net.berack.upo.valpre.rand.Rng;
/**
* A network simulation that uses a discrete event simulation to model the
* behavior of a network of servers.
*/
public class NetSimulation { public class NetSimulation {
public final long seed; public final long seed;
private final Rng rng; private final Rng rng;
private final Map<String, ServerNode> servers = new HashMap<>(); private final Map<String, ServerNode> servers = new HashMap<>();
/**
* Creates a new network simulation with the given seed.
*
* @param seed The seed to use for the random number generator.
*/
public NetSimulation(long seed) { public NetSimulation(long seed) {
this.seed = seed; this.seed = seed;
this.rng = new Rng(seed); this.rng = new Rng(seed);
} }
/**
* Adds a new server node to the network.
*
* @param node The server node to add.
*/
public void addNode(ServerNode node) { public void addNode(ServerNode node) {
this.servers.put(node.name, node); this.servers.put(node.name, node);
} }
/**
* Runs the simulation for the given number of total arrivals, stopping when the
* given node has reached the
* specified number of departures.
*
* @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.
*/
public Map<String, Statistics> run(long total, String untilDepartureNode) { public Map<String, Statistics> run(long total, String untilDepartureNode) {
// Initialization // Initialization
var timeNow = 0.0d; var timeNow = 0.0d;
@@ -49,6 +72,12 @@ public class NetSimulation {
return stats; return stats;
} }
/**
* Represents a statistical summary of the behavior of a server node in the
* network.
* It is used by the simulation to track the number of arrivals and departures,
* the maximum queue length, the busy time, and the response time.
*/
public static class Statistics { public static class Statistics {
public int numArrivals = 0; public int numArrivals = 0;
public int numDepartures = 0; public int numDepartures = 0;
@@ -61,10 +90,18 @@ public class NetSimulation {
private ArrayDeque<Double> queue = new ArrayDeque<>(); private ArrayDeque<Double> queue = new ArrayDeque<>();
private final Rng rng; private final Rng rng;
/**
* Creates a new statistics object with the given random number generator.
*
* @param rng The random number generator to use.
*/
public Statistics(Rng rng) { public Statistics(Rng rng) {
this.rng = rng; this.rng = rng;
} }
/**
* Resets the statistics to their initial values.
*/
public void reset() { public void reset() {
this.numArrivals = 0; this.numArrivals = 0;
this.numDepartures = 0; this.numDepartures = 0;
@@ -74,6 +111,17 @@ public class NetSimulation {
this.queue.clear(); this.queue.clear();
} }
/**
* Processes an arrival event for the given node at the given time.
* The event is processed by adding the arrival time to the queue, updating the
* maximum queue length, and checking if a server is available to process the
* arrival. If a server is available, a departure event is created and added to
* the future event list.
*
* @param event The arrival event to process.
* @param timeNow The current time of the simulation.
* @param fel The future event list to add new events to.
*/
private void processArrival(Event event, double timeNow, PriorityQueue<Event> fel) { private void processArrival(Event event, double timeNow, PriorityQueue<Event> fel) {
this.numArrivals++; this.numArrivals++;
this.queue.add(event.time); this.queue.add(event.time);
@@ -92,6 +140,18 @@ public class NetSimulation {
this.addArrivalIf(event.node.isSource, event.node, timeNow, fel); this.addArrivalIf(event.node.isSource, event.node, timeNow, fel);
} }
/**
* Processes a departure event for the given node at the given time.
* The event is processed by removing the departure time from the queue,
* updating the busy time, and checking if there are any arrivals in the queue.
* If there are, a new departure event is created and added to the fel.
* At the end it will add an arrival to the next node if the current node has a
* child.
*
* @param event The departure event to process.
* @param timeNow The current time of the simulation.
* @param fel The future event list to add new events to.
*/
private void processDeparture(Event event, double timeNow, PriorityQueue<Event> fel) { private void processDeparture(Event event, double timeNow, PriorityQueue<Event> fel) {
var startService = this.queue.poll(); var startService = this.queue.poll();
var response = timeNow - startService; var response = timeNow - startService;
@@ -113,6 +173,15 @@ public class NetSimulation {
this.addArrivalIf(!event.node.isSink, next, timeNow, fel); this.addArrivalIf(!event.node.isSink, next, timeNow, fel);
} }
/**
* Adds an arrival event to the future event list if the given condition is
* true.
*
* @param condition The condition to check.
* @param node The node to add the arrival event for.
* @param timeNow The current time of the simulation.
* @param fel The future event list to add the event to.
*/
private void addArrivalIf(boolean condition, ServerNode node, double timeNow, PriorityQueue<Event> fel) { private void addArrivalIf(boolean condition, ServerNode node, double timeNow, PriorityQueue<Event> fel) {
if (condition && node != null) { if (condition && node != null) {
var delay = node.distribution.sample(this.rng); var delay = node.distribution.sample(this.rng);

View File

@@ -5,6 +5,10 @@ import java.util.List;
import net.berack.upo.valpre.rand.Distribution; import net.berack.upo.valpre.rand.Distribution;
import net.berack.upo.valpre.rand.Rng; import net.berack.upo.valpre.rand.Rng;
/**
* Represents a node in the network. It can be a source, a queue, or a sink
* based on the configuration passed as parameters.
*/
public class ServerNode { public class ServerNode {
public final String name; public final String name;
public final int maxServers; public final int maxServers;
@@ -14,14 +18,35 @@ public class ServerNode {
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.
* @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) { 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, false, true);
} }
/**
* 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) { public static ServerNode createQueue(String name, int maxServers, Distribution distribution) {
return new ServerNode(name, maxServers, distribution, false, false); return new ServerNode(name, maxServers, distribution, false, false);
} }
/**
* 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.
*/
public ServerNode(String name, int maxServers, Distribution distribution, boolean isSink, boolean isSource) { public ServerNode(String name, int maxServers, Distribution distribution, boolean isSink, boolean isSource) {
this.name = name; this.name = name;
this.maxServers = maxServers; this.maxServers = maxServers;
@@ -30,11 +55,21 @@ public class ServerNode {
this.isSource = isSource; this.isSource = isSource;
} }
/**
* Adds a child node with the given probability to select it.
* @param node The child node to add.
* @param probability The probability of the child node.
*/
public void addChild(ServerNode node, double probability) { public void addChild(ServerNode node, double probability) {
this.children.add(new NodeChild(node, probability)); this.children.add(new NodeChild(node, probability));
this.sumProbabilities += probability; this.sumProbabilities += probability;
} }
/**
* 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.
*/
public ServerNode getChild(Rng rng) { public ServerNode getChild(Rng rng) {
var random = rng.random(); var random = rng.random();
for (var child : this.children) { for (var child : this.children) {
@@ -46,6 +81,9 @@ public class ServerNode {
return null; return null;
} }
/**
* Represents a child node with a probability to select it.
*/
public static class NodeChild { public static class NodeChild {
public final ServerNode node; public final ServerNode node;
public final double probability; public final double probability;

View File

@@ -1,11 +1,21 @@
package net.berack.upo.valpre.rand; package net.berack.upo.valpre.rand;
/**
* Represents a probability distribution.
*/
public interface Distribution { public interface Distribution {
public double sample(Rng rng); public double sample(Rng rng);
/**
* Represents an exponential distribution.
*/
public static class Exponential implements Distribution { public static class Exponential implements Distribution {
private final double lambda; private final double lambda;
/**
* Creates a new exponential distribution with the given rate.
* @param lambda The rate of the distribution.
*/
public Exponential(double lambda) { public Exponential(double lambda) {
this.lambda = lambda; this.lambda = lambda;
} }
@@ -16,10 +26,18 @@ public interface Distribution {
} }
} }
/**
* Represents a normal distribution.
*/
public static class Normal implements Distribution { public static class Normal implements Distribution {
private final double mean; private final double mean;
private final double sigma; private final double sigma;
/**
* Creates a new normal distribution with the given mean and standard deviation.
* @param mean The mean of the distribution.
* @param sigma The standard deviation of the distribution.
*/
public Normal(double mean, double sigma) { public Normal(double mean, double sigma) {
this.mean = mean; this.mean = mean;
this.sigma = sigma; this.sigma = sigma;
@@ -32,11 +50,19 @@ public interface Distribution {
} }
} }
/**
* Represents a normal distribution using the Box-Muller transform.
*/
public static class NormalBoxMuller implements Distribution { public static class NormalBoxMuller implements Distribution {
private final double mean; private final double mean;
private final double sigma; private final double sigma;
private double next = Double.NaN; private double next = Double.NaN;
/**
* Creates a new normal distribution with the given mean and standard deviation.
* @param mean The mean of the distribution.
* @param sigma The standard deviation of the distribution.
*/
public NormalBoxMuller(double mean, double sigma) { public NormalBoxMuller(double mean, double sigma) {
this.mean = mean; this.mean = mean;
this.sigma = sigma; this.sigma = sigma;