From 2a4b4948434c18ed83d06376f827944daaadf485 Mon Sep 17 00:00:00 2001 From: Berack96 Date: Wed, 29 Jan 2025 10:54:42 +0100 Subject: [PATCH] Test Net - added net test - added more docs --- src/main/java/net/berack/upo/valpre/Main.java | 58 +++++--- .../java/net/berack/upo/valpre/sim/Net.java | 133 ++++++++++++++++-- .../net/berack/upo/valpre/sim/Simulation.java | 7 +- .../upo/valpre/sim/SimulationMultiple.java | 5 +- .../berack/upo/valpre/sim/stats/Result.java | 17 ++- .../berack/upo/valpre/sim/TestSimulation.java | 71 +++++++++- 6 files changed, 246 insertions(+), 45 deletions(-) diff --git a/src/main/java/net/berack/upo/valpre/Main.java b/src/main/java/net/berack/upo/valpre/Main.java index 00df773..a72ae64 100644 --- a/src/main/java/net/berack/upo/valpre/Main.java +++ b/src/main/java/net/berack/upo/valpre/Main.java @@ -14,10 +14,6 @@ public class Main { String csv = null; var runs = 100; var seed = 2007539552L; - var total = 10000; - var lambda = 1.0 / 4.5; - var mu = 3.2; - var sigma = 0.6; // Evantually change the parameters var arguments = parseParameters(args); @@ -29,26 +25,11 @@ public class Main { csv = arguments.get("csv"); var parallel = arguments.containsKey("p"); - // varius distributions - var distrExp = new Distribution.Exponential(lambda); - var distrNorm = new Distribution.NormalBoxMuller(mu, sigma); - var distrUnav = new Distribution.UnavailableTime(0.2, distrNorm); - - // Build the network - var net = new Net(); - var node1 = ServerNode.createLimitedSource("Source", distrExp, total); - var node2 = ServerNode.createQueue("Queue", 1, distrNorm); - var node3 = ServerNode.createQueue("Queue Wait", 1, distrNorm, distrUnav); - net.addNode(node1); - net.addNode(node2); - net.addNode(node3); - net.addConnection(node1, node2, 1.0); - net.addConnection(node2, node3, 1.0); - net.normalizeWeights(); - - /// Run multiple simulations + var net = moreComplexNet(); // var maxDepartures = new EndSimulationCriteria.MaxDepartures("Queue", total); // var maxTime = new EndSimulationCriteria.MaxTime(1000.0); + + /// Run multiple simulations var nano = System.nanoTime(); var sim = new SimulationMultiple(net); var results = parallel ? sim.runParallel(seed, runs) : sim.run(seed, runs); @@ -64,6 +45,39 @@ public class Main { } } + public static Net simpleNet() { + var lambda = 1.0 / 4.5; + var mu = 3.2; + var sigma = 0.6; + var total = 10000; + + var distrExp = new Distribution.Exponential(lambda); + var distrNorm = new Distribution.NormalBoxMuller(mu, sigma); + + // Build the network + var net = new Net(); + net.addNode(ServerNode.createLimitedSource("Source", distrExp, total)); + net.addNode(ServerNode.createQueue("Queue", 1, distrNorm)); + net.addConnection(0, 1, 1.0); + net.normalizeWeights(); + + return net; + } + + public static Net moreComplexNet() { + var net = simpleNet(); + var distrNorm = new Distribution.NormalBoxMuller(3.2, 0.6); + var distrNorm2 = new Distribution.NormalBoxMuller(4.2, 0.6); + var distrUnav = new Distribution.UnavailableTime(0.2, distrNorm2); + + // Build the network + net.addNode(ServerNode.createQueue("Queue Wait", 1, distrNorm, distrUnav)); + net.addConnection(1, 2, 1.0); + net.normalizeWeights(); + + return net; + } + public static Map parseParameters(String[] args) { var arguments = new HashMap(); arguments.put("p", false); diff --git a/src/main/java/net/berack/upo/valpre/sim/Net.java b/src/main/java/net/berack/upo/valpre/sim/Net.java index c72412b..0802f0a 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Net.java +++ b/src/main/java/net/berack/upo/valpre/sim/Net.java @@ -25,14 +25,17 @@ public final class Net { * * @param node The server node to add. * @throws IllegalArgumentException if the node already exist + * @return the index of the created node */ - public void addNode(ServerNode node) { + public int addNode(ServerNode node) { if (this.indices.containsKey(node)) throw new IllegalArgumentException("Node already exist"); + var index = this.servers.size(); this.servers.add(node); - this.indices.put(node, this.servers.size() - 1); + this.indices.put(node, index); this.connections.add(new ArrayList<>()); + return index; } /** @@ -49,20 +52,36 @@ public final class Net { public void addConnection(ServerNode parent, ServerNode child, double weight) { var nodeP = this.indices.get(parent); var nodeC = this.indices.get(child); + this.addConnection(nodeP, nodeC, weight); + } + /** + * Adds a connection between the nodes with the given weight to select it. + * The weight must be > 0 and the nodes must be already added to the net. + * If the connection is already present then the new weight is used. + * + * @param parent The parent node index. + * @param child The child node index to add. + * @param weight The probability of the child node. + * @throws IndexOutOfBoundsException if one of the two nodes are not in the net + * @throws IllegalArgumentException if the weight is negative or zero + */ + public void addConnection(int parent, int child, double weight) { if (weight <= 0) throw new IllegalArgumentException("Weight must be > 0"); - if (nodeP == nodeC && nodeP == null) - throw new NullPointerException("One of the nodes does not exist"); - var list = this.connections.get(nodeP); + var max = this.servers.size() - 1; + if (parent < 0 || child < 0 || parent > max || child > max) + throw new IndexOutOfBoundsException("One of the nodes does not exist"); + + var list = this.connections.get(parent); for (var conn : list) { - if (conn.index == nodeC) { + if (conn.index == child) { conn.weight = weight; return; } } - list.add(new Connection(nodeC, weight)); + list.add(new Connection(child, weight)); } /** @@ -75,9 +94,43 @@ public final class Net { } /** - * Get one of the child nodes from the parent specified. If the index is out of - * bounds then an - * exception is thrown. If the node has no child then null is returned; + * Return the index of the node based on the name passed as input. + * Note that this will iterate over all the nodes. + * + * @param name the name of the node + * @return the node + */ + public int getNodeIndex(String name) { + for (var entry : this.indices.entrySet()) { + if (entry.getKey().name.equals(name)) + return entry.getValue(); + } + return -1; + } + + /** + * Return a node based on the hash of the string name passed as input + * + * @param name the name of the node + * @return the node + */ + public ServerNode getNode(String name) { + return this.servers.get(this.getNodeIndex(name)); + } + + /** + * Return a node based on the index, faster than recovering it by the name + * + * @param index the index of the node + * @return the node + */ + public ServerNode getNode(int index) { + return this.servers.get(index); + } + + /** + * Get one of the child nodes from the parent specified. + * If the node has no child then null is returned. * * @param parent the parent node * @param rng the random number generator used for getting one of the child @@ -85,8 +138,22 @@ public final class Net { */ public ServerNode getChildOf(ServerNode parent, Rng rng) { var index = this.indices.get(parent); + return this.getChildOf(index, rng); + } + + /** + * Get one of the child nodes from the parent specified. If the index is out of + * bounds then an exception is thrown. If the node has no child then null is + * returned; + * + * @param parent the parent node + * @param rng the random number generator used for getting one of the child + * @throws IndexOutOfBoundsException If the index is not in the range + * @return the resultig node + */ + public ServerNode getChildOf(int parent, Rng rng) { var random = rng.random(); - for (var conn : this.connections.get(index)) { + for (var conn : this.connections.get(parent)) { random -= conn.weight; if (random <= 0) { return this.servers.get(conn.index); @@ -95,6 +162,37 @@ public final class Net { return null; } + /** + * Get a list of all the children of the parent. + * In the list there is the node and the weight associated with. + * + * @param parent the parent node + * @return the list of children + */ + public List getChildren(ServerNode parent) { + var index = this.indices.get(parent); + return this.getChildren(index); + } + + /** + * Get a list of all the children of the parent. + * In the list there is the node and the weight associated with. + * + * @param parent the parent node + * @throws IndexOutOfBoundsException If the index is not in the range + * @return the resultig node + */ + public List getChildren(int parent) { + var children = new ArrayList(); + for (var conn : this.connections.get(parent)) { + var child = this.servers.get(conn.index); + var listEntry = new NetChild(child, conn.weight); + children.add(listEntry); + } + + return children; + } + /** * Normalizes the weights in each connections so that their sum equals 1. * This method should be called by the user if they have inserted weights that @@ -132,4 +230,17 @@ public final class Net { this.weight = weight; } } + + /** + * A Static inner class used to represent the connection of a node + */ + public static class NetChild { + public final ServerNode child; + public final double weight; + + private NetChild(ServerNode child, double weight) { + this.child = child; + this.weight = weight; + } + } } 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 b82bbc5..4a32605 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Simulation.java +++ b/src/main/java/net/berack/upo/valpre/sim/Simulation.java @@ -153,6 +153,7 @@ public final class Simulation { * distribution. * * @param node The node to create the event for. + * @param state The current state of the node */ public void addDepartureIfPossible(ServerNode node, NodeState state) { var canServe = node.maxServers > state.numServerBusy + state.numServerUnavailable; @@ -167,10 +168,10 @@ public final class Simulation { } /** - * TODO + * Add an AVAILABLE event in the case that the node has an unavailability time. * - * @param node - * @param state + * @param node The node to create the event for + * @param state The current state of the node */ public void addUnavailableIfPossible(ServerNode node, NodeState state) { var delay = node.getUnavailableTime(rng); 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 12cbacb..73b0f08 100644 --- a/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java +++ b/src/main/java/net/berack/upo/valpre/sim/SimulationMultiple.java @@ -16,8 +16,9 @@ public class SimulationMultiple { private final Net net; /** - * TODO - * @param net + * Create a new object that can simulate the net in input multiple times + * + * @param net the net that sould be simulated */ public SimulationMultiple(Net net) { this.net = net; 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 index fddb4c6..14d2de1 100644 --- a/src/main/java/net/berack/upo/valpre/sim/stats/Result.java +++ b/src/main/java/net/berack/upo/valpre/sim/stats/Result.java @@ -40,6 +40,8 @@ public class Result { * Get the global information of the simulation. In particular this method build * a string that contains the seed and the time elapsed in the simulation and in * real time + * + * @return a string with the info */ public String getHeader() { var builder = new StringBuilder(); @@ -52,8 +54,11 @@ public class Result { /** * 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. + * The summary includes all the statistics of nodes and for each it displays the + * departures, queue, wait, response, throughput, utilization, unavailability + * and the last event time. + * + * @return a string with all the stats */ public String getSummary() { String[] h = { "Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput", "Utilization %", @@ -77,10 +82,14 @@ public class Result { } /** - * TODO + * Return a summary formatted for CSV. + * This meaning that all the stats will be separated by a comma (,) and each row + * is a single statistic of the node. + * Each row it will have all the statistic from the class {@link Statistics}, + * the seed used for obtaining them. * * @param tableHeader - * @return + * @return a string with all the stats formatted */ public String getSummaryCSV(boolean tableHeader) { var builder = new StringBuilder(); diff --git a/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java b/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java index 82a4517..0b7007e 100644 --- a/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java +++ b/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java @@ -4,6 +4,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.util.HashSet; + import org.junit.jupiter.api.Test; import net.berack.upo.valpre.rand.Distribution; @@ -19,7 +21,7 @@ public class TestSimulation { private final static class RiggedRng extends Rng { @Override public double random() { - return 0.5; + return 0.1; } } @@ -106,9 +108,72 @@ public class TestSimulation { @Test public void net() { - // TODO var net = new Net(); - net.addNode(null); + assertEquals(0, net.size()); + + var node = ServerNode.createSource("First", const0); + var index = net.addNode(node); + assertEquals(1, net.size()); + assertEquals(0, index); + assertEquals(node, net.getNode(0)); + assertEquals(node, net.getNode("First")); + assertEquals(index, net.getNodeIndex("First")); + + var node1 = ServerNode.createQueue("Second", 1, const0); + var index1 = net.addNode(node1); + assertEquals(2, net.size()); + assertEquals(0, index); + assertEquals(node, net.getNode(0)); + assertEquals(node, net.getNode("First")); + assertEquals(index, net.getNodeIndex("First")); + assertEquals(1, index1); + assertEquals(node1, net.getNode(1)); + assertEquals(node1, net.getNode("Second")); + assertEquals(index1, net.getNodeIndex("Second")); + + var nodes = new HashSet(); + nodes.add(node); + nodes.add(node1); + net.forEachNode(n -> assertTrue(nodes.contains(n))); + + net.addConnection(0, 1, 1.0); + var conn = net.getChildren(0); + assertEquals(1, conn.size()); + assertEquals(node1, conn.get(0).child); + assertEquals(1.0, conn.get(0).weight, DELTA); + conn = net.getChildren(1); + assertEquals(0, conn.size()); + + var node2 = ServerNode.createQueue("Third", 1, const0); + net.addNode(node2); + net.addConnection(0, 2, 1.0); + conn = net.getChildren(0); + assertEquals(2, conn.size()); + assertEquals(node1, conn.get(0).child); + assertEquals(node2, conn.get(1).child); + assertEquals(1.0, conn.get(0).weight, DELTA); + assertEquals(1.0, conn.get(1).weight, DELTA); + conn = net.getChildren(1); + assertEquals(0, conn.size()); + conn = net.getChildren(2); + assertEquals(0, conn.size()); + + net.normalizeWeights(); + conn = net.getChildren(0); + assertEquals(2, conn.size()); + assertEquals(node1, conn.get(0).child); + assertEquals(node2, conn.get(1).child); + assertEquals(0.5, conn.get(0).weight, DELTA); + assertEquals(0.5, conn.get(1).weight, DELTA); + conn = net.getChildren(1); + assertEquals(0, conn.size()); + conn = net.getChildren(2); + assertEquals(0, conn.size()); + + var sample = net.getChildOf(0, rigged); + assertEquals(node1, sample); + sample = net.getChildOf(node, rigged); + assertEquals(node1, sample); } @Test