Net creation #2
23
.vscode/launch.json
vendored
23
.vscode/launch.json
vendored
@@ -9,56 +9,49 @@
|
|||||||
"name": "Run1k Simple",
|
"name": "Run1k Simple",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
"mainClass": "net.berack.upo.valpre.Main",
|
||||||
"args": "simulation -net example1.net -runs 1000 -p"
|
"args": "simulation -net src/test/resources/example1.net -runs 1000 -p"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "java",
|
"type": "java",
|
||||||
"name": "Run1k Complex",
|
"name": "Run1k Complex",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
"mainClass": "net.berack.upo.valpre.Main",
|
||||||
"args": "simulation -net example2.net -runs 1000 -p"
|
"args": "simulation -net src/test/resources/example2.net -runs 1000 -p"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "java",
|
|
||||||
"name": "Run1k Complex EXP",
|
|
||||||
"request": "launch",
|
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
|
||||||
"args": "simulation -net example3.net -runs 1000 -p"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "java",
|
"type": "java",
|
||||||
"name": "Run Incremental",
|
"name": "Run Incremental",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
"mainClass": "net.berack.upo.valpre.Main",
|
||||||
"args": "simulation -net example3.net -runs 1000 -i \"[Service1:throughput=0.98:0.01],[Service2:utilization=0.98:0.01],[Service2:unavailable=0.98:0.01]\""
|
"args": "simulation -net src/test/resources/example2.net -runs 1000 -i \"[Service1:throughput=0.98:0.01],[Service2:utilization=0.98:0.01],[Service2:unavailable=0.98:0.01]\""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "java",
|
"type": "java",
|
||||||
"name": "Run10",
|
"name": "Run10",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
"mainClass": "net.berack.upo.valpre.Main",
|
||||||
"args": "simulation -net example1.net -runs 10 -seed 2007539552"
|
"args": "simulation -net src/test/resources/example1.net -runs 10 -seed 2007539552"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "java",
|
"type": "java",
|
||||||
"name": "Plot Simple",
|
"name": "Plot Simple",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
"mainClass": "net.berack.upo.valpre.Main",
|
||||||
"args": "plot -csv example1.csv"
|
"args": "plot -csv src/test/resources/example1.csv"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "java",
|
"type": "java",
|
||||||
"name": "Plot Complex",
|
"name": "Plot Complex",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
"mainClass": "net.berack.upo.valpre.Main",
|
||||||
"args": "plot -csv example2.csv"
|
"args": "plot -csv src/test/resources/example2.csv"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "java",
|
"type": "java",
|
||||||
"name": "Build Net",
|
"name": "Interactive Net Builder",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "net.berack.upo.valpre.Main",
|
"mainClass": "net.berack.upo.valpre.Main",
|
||||||
"args": "net"
|
"args": "interactive"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
25
README.md
25
README.md
@@ -16,7 +16,7 @@ Questa libreria è stata confrontata con il tool [JMT](https://jmt.sourceforge.n
|
|||||||
|
|
||||||
Il JAR viene invocato tramite il classico comando java: `java -jar upo-valpre.jar` al quale si aggiungono vari argomenti successivi in base a cosa si vuole fare:
|
Il JAR viene invocato tramite il classico comando java: `java -jar upo-valpre.jar` al quale si aggiungono vari argomenti successivi in base a cosa si vuole fare:
|
||||||
|
|
||||||
* `java -jar upo-valpre.jar net`\
|
* `java -jar upo-valpre.jar interactive`\
|
||||||
Usato per avviare una sessione interattiva per la creazione di una rete. Da usare se la rete è relativamente breve da descrivere, altrimenti è più comodo usare il codice della libreria per la generazione della rete.\
|
Usato per avviare una sessione interattiva per la creazione di una rete. Da usare se la rete è relativamente breve da descrivere, altrimenti è più comodo usare il codice della libreria per la generazione della rete.\
|
||||||
Una volta scelta la rete è necessario salvarla in un file per la successiva simulazione e analisi.
|
Una volta scelta la rete è necessario salvarla in un file per la successiva simulazione e analisi.
|
||||||
* `java -jar upo-valpre.jar simulation -net <file> [other]`\
|
* `java -jar upo-valpre.jar simulation -net <file> [other]`\
|
||||||
@@ -34,10 +34,6 @@ Esistono vari tipi di argomenti per scegliere come fare la simulazione:
|
|||||||
Mostra (con un ambiente grafico) una finestra nella quale si può scegliere quale nodo vedere e ogni statistica associata ad esso. Di seguito un'immagine di esempio:\
|
Mostra (con un ambiente grafico) una finestra nella quale si può scegliere quale nodo vedere e ogni statistica associata ad esso. Di seguito un'immagine di esempio:\
|
||||||

|

|
||||||
|
|
||||||
Esistono dei file prefatti per vedere eventuali simulazioni che, nel caso vengano passati come parametri, automaticamente vengono usati:
|
|
||||||
* `example1.net`, `example2.net` e `example3.net` per `simulation -net`
|
|
||||||
* `example1.csv`, `example2.csv` e `example3.csv` per `plot -csv`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
### Classi Interne
|
### Classi Interne
|
||||||
|
|
||||||
@@ -61,14 +57,13 @@ I percorsi che invece sono direttamente responsabili per la simulazione sono:
|
|||||||
---
|
---
|
||||||
### Esempi
|
### Esempi
|
||||||
|
|
||||||
Nel jar sono presenti già 3 reti per fare degli esperimenti e/o testare se il tool funziona correttamente. Per tutti e tre gli esempi i comandi usati sono i seguenti, dove viene sostituito un numero al posto dell'asterisco:
|
Nel jar sono presenti già 2 reti per fare degli esperimenti e/o testare se il tool funziona correttamente. Per poter vedere una run usando questi esempi basta far partire il tool in modalità interattiva e scegliere di caricare gli esempi.\
|
||||||
- `java -jar .\upo-valpre.jar simulation -net example*.net -runs 100` per fare una simulazione di 100 run e vedere i risultati aggregati.
|
`java -jar upo-valpre.jar interactive`
|
||||||
- `java -jar .\upo-valpre.jar plot -csv example*.csv` per mostrare un'aggregazione con più dettagli di una simulazione di 1000 run.
|
|
||||||
|
|
||||||
##### Primo esempio
|
##### Primo esempio
|
||||||
\
|
\
|
||||||
Il primo è `example1`; è una rete composta da una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=0.222 e quindi media 4.5) e un centro di servizio (Queue) con tasso di servizio distribuito come una normale (μ=3.2, σ=0.6).\
|
Il primo è `example1`; è una rete composta da una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=0.222 e quindi media 4.5) e un centro di servizio (Queue) con tasso di servizio distribuito come una normale (μ=3.2, σ=0.6).\
|
||||||
Se si effettua una simulazione con il comando precedente si vedranno i risultati sulla console in questo modo:
|
Se si effettua una simulazione si vedranno i risultati sulla console in questo modo:
|
||||||

|

|
||||||
|
|
||||||
Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:\
|
Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:\
|
||||||
@@ -78,15 +73,10 @@ Queue Utilization = 0.7111 con un range [0.6959, 0.7262]
|
|||||||
|
|
||||||
##### Secondo esempio
|
##### Secondo esempio
|
||||||
\
|
\
|
||||||
Il secondo è `example2`; è una rete composta da una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=0.222 e quindi media 4.5), un centro di servizio (Queue) con tasso di servizio distribuito come una normale (μ=3.2, σ=0.6) e un altro centro di servizio (Queue Wait) con tasso di servizio distribuito come una normale (μ=3.2, σ=0.6) e con un tempo di indisponibilità che viene attivato con probabilità 20% e distribuito con una normale (μ=4.2, σ=0.6)\
|
Il secondo esempio è `example2`; è una rete composta da una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=1.5 e quindi media 0.666), un centro di servizio (Service1) con tasso di servizio distribuito come una esponenziale (λ=2.0 e quindi media 0.5) e un altro centro di servizio (Service2) con tasso di servizio distribuito come una esponenziale (λ=3.5 e quindi media 0.2857) e con un tempo di indisponibilità che viene attivato con probabilità 10% e distribuito con una eseponenziale (λ=10.0 e quindi media 0.1)\
|
||||||
Se si effettua una simulazione con il comando precedente si vedranno i risultati sulla console in questo modo:
|
Se si effettua una simulazione si vedranno i risultati sulla console in questo modo:
|
||||||

|
|
||||||
|
|
||||||
##### Terzo esempio
|
|
||||||
\
|
|
||||||
Il terzo è `example3`; è uguale al secondo esempio ma nel quale cambiano i nomi dei nodi e le loro distribuzioni: è una rete composta da una fonte di clienti (Source) che arrivano con tasso esponenziale (λ=1.5 e quindi media 0.666), un centro di servizio (Service1) con tasso di servizio distribuito come una esponenziale (λ=2.0 e quindi media 0.5) e un altro centro di servizio (Service2) con tasso di servizio distribuito come una esponenziale (λ=3.5 e quindi media 0.2857) e con un tempo di indisponibilità che viene attivato con probabilità 10% e distribuito con una eseponenziale (λ=10.0 e quindi media 0.1)\
|
|
||||||
Se si effettua una simulazione con il comando precedente si vedranno i risultati sulla console in questo modo:
|
|
||||||

|

|
||||||
|
|
||||||
Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:\
|
Il tool JMT con la stessa rete produce i seguenti risultati che sono molto simili a quelli prodotti dalla libreria:\
|
||||||
Service1 Response Time ~ 1.9866\
|
Service1 Response Time ~ 1.9866\
|
||||||
Busy2 Response Time ~ 0.2825\
|
Busy2 Response Time ~ 0.2825\
|
||||||
@@ -95,4 +85,3 @@ Service1 Utilization ~ 0.7488\
|
|||||||
Calibration Number of Customers ~ 0.0150\
|
Calibration Number of Customers ~ 0.0150\
|
||||||
Busy2 Number of Customers ~ 0.4279\
|
Busy2 Number of Customers ~ 0.4279\
|
||||||
Throughput ~ 1.5000
|
Throughput ~ 1.5000
|
||||||
|
|
||||||
|
|||||||
24
pom.xml
24
pom.xml
@@ -13,30 +13,6 @@
|
|||||||
<maven.compiler.target>23</maven.compiler.target>
|
<maven.compiler.target>23</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<!-- Compilazione Java con Maven -->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>3.8.1</version>
|
|
||||||
<configuration>
|
|
||||||
<source>23</source>
|
|
||||||
<target>23</target>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<!-- Maven plugin per eseguire l'applicazione JavaFX -->
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.openjfx</groupId>
|
|
||||||
<artifactId>javafx-maven-plugin</artifactId>
|
|
||||||
<version>0.0.8</version>
|
|
||||||
<configuration>
|
|
||||||
<mainClass>net.berack.upo.valpre.Main</mainClass>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
|||||||
267
src/main/java/net/berack/upo/valpre/InteractiveConsole.java
Normal file
267
src/main/java/net/berack/upo/valpre/InteractiveConsole.java
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
package net.berack.upo.valpre;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.KryoException;
|
||||||
|
|
||||||
|
import net.berack.upo.valpre.rand.Distribution;
|
||||||
|
import net.berack.upo.valpre.sim.Net;
|
||||||
|
import net.berack.upo.valpre.sim.ServerNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interactive net builder. This class allows the user to build a net by adding
|
||||||
|
* nodes and connections. The user can also save the net to a file.
|
||||||
|
*/
|
||||||
|
public class InteractiveConsole {
|
||||||
|
|
||||||
|
private Net net = new Net();
|
||||||
|
private final PrintStream out;
|
||||||
|
private final Scanner scanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new interactive net builder. Uses System.in and System.out.
|
||||||
|
*/
|
||||||
|
public InteractiveConsole() {
|
||||||
|
this(System.out, System.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new interactive net builder.
|
||||||
|
*
|
||||||
|
* @param out the output stream
|
||||||
|
* @param in the input stream
|
||||||
|
*/
|
||||||
|
public InteractiveConsole(PrintStream out, InputStream in) {
|
||||||
|
this.out = out;
|
||||||
|
this.scanner = new Scanner(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the interactive net builder.
|
||||||
|
*/
|
||||||
|
public Net run() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
var choice = choose(this.net + "\nChoose the next step to do:",
|
||||||
|
"Add a node", "Add a connection", "Save the net", "Load net", "Clear", "Run", "Exit");
|
||||||
|
switch (choice) {
|
||||||
|
case 1 -> this.buildNode();
|
||||||
|
case 2 -> this.buildConnection();
|
||||||
|
case 3 -> this.net.save(ask("Enter the filename: "));
|
||||||
|
case 4 -> this.loadNet();
|
||||||
|
case 5 -> this.net = new Net();
|
||||||
|
case 6 -> this.simpleRuns();
|
||||||
|
default -> {
|
||||||
|
this.scanner.close();
|
||||||
|
return this.net;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a node as a source, terminal, queue, or queue with unavailable time.
|
||||||
|
*/
|
||||||
|
private void buildNode() {
|
||||||
|
var choice = choose("Choose the type of node to create:", "Source", "Terminal", "Queue",
|
||||||
|
"Queue with unavailable time");
|
||||||
|
var name = ask("Node name: ");
|
||||||
|
var distribution = askDistribution("Service distribution");
|
||||||
|
|
||||||
|
var node = switch (choice) {
|
||||||
|
case 1 -> ServerNode.Builder.source(name, distribution);
|
||||||
|
case 2 -> {
|
||||||
|
var limit = ask("Arrivals limit (0 for Int.Max): ", Integer::parseInt);
|
||||||
|
if (limit <= 0)
|
||||||
|
limit = Integer.MAX_VALUE;
|
||||||
|
yield ServerNode.Builder.terminal(name, limit, distribution);
|
||||||
|
}
|
||||||
|
case 3 -> {
|
||||||
|
var servers = ask("Number of servers: ", Integer::parseInt);
|
||||||
|
yield ServerNode.Builder.queue(name, servers, distribution, null);
|
||||||
|
}
|
||||||
|
case 4 -> {
|
||||||
|
var servers = ask("Number of servers: ", Integer::parseInt);
|
||||||
|
var unavailable = askDistribution("Unavailable distribution");
|
||||||
|
yield ServerNode.Builder.queue(name, servers, distribution, unavailable);
|
||||||
|
}
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (node != null)
|
||||||
|
this.net.addNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a connection.
|
||||||
|
*/
|
||||||
|
private void buildConnection() {
|
||||||
|
var source = ask("Enter the source node: ");
|
||||||
|
var target = ask("Enter the target node: ");
|
||||||
|
var weight = ask("Enter the weight: ", Double::parseDouble);
|
||||||
|
var sourceNode = this.net.getNode(source);
|
||||||
|
var targetNode = this.net.getNode(target);
|
||||||
|
this.net.addConnection(sourceNode, targetNode, weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a net from a file or from examples.
|
||||||
|
*/
|
||||||
|
private void loadNet() throws KryoException, IOException {
|
||||||
|
var choice = choose("Choose the type of net to load:", "From file", "From examples");
|
||||||
|
this.net = switch (choice) {
|
||||||
|
case 1 -> Net.load(ask("Enter the filename: "));
|
||||||
|
case 2 -> {
|
||||||
|
var choice2 = choose("Choose the example to load:",
|
||||||
|
"Example 1: Source -> Queue",
|
||||||
|
"Example 2: Source -> Queue -> Queue");
|
||||||
|
yield switch (choice2) {
|
||||||
|
case 1 -> NetExamples.getNet1();
|
||||||
|
case 2 -> NetExamples.getNet2();
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the simulation with the net.
|
||||||
|
*/
|
||||||
|
private void simpleRuns() throws InterruptedException, ExecutionException, IOException {
|
||||||
|
var choice = choose("Choose what to do:", "100 Run", "1K Runs", "1K Runs + Plot");
|
||||||
|
switch (choice) {
|
||||||
|
case 1 -> new SimulationBuilder(net).setMaxRuns(100).setParallel(true).run();
|
||||||
|
case 2 -> new SimulationBuilder(net).setMaxRuns(1000).setParallel(true).run();
|
||||||
|
case 3 -> {
|
||||||
|
var randName = "rand" + System.currentTimeMillis() + ".csv";
|
||||||
|
new SimulationBuilder(net).setMaxRuns(1000).setParallel(true).setCsv(randName).run();
|
||||||
|
new Plot(randName).show();
|
||||||
|
new File(randName).delete();
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the user for a distribution.
|
||||||
|
*
|
||||||
|
* @return the distribution
|
||||||
|
*/
|
||||||
|
private Distribution askDistribution(String ask) {
|
||||||
|
var choice = choose(ask + ":", "Exponential", "Uniform", "Erlang",
|
||||||
|
"UnavailableTime", "Normal", "NormalBoxMuller", "None");
|
||||||
|
|
||||||
|
return switch (choice) {
|
||||||
|
case 1 -> {
|
||||||
|
var lambda = ask("Lambda: ", Double::parseDouble);
|
||||||
|
yield new Distribution.Exponential(lambda);
|
||||||
|
}
|
||||||
|
case 2 -> {
|
||||||
|
var min = ask("Min: ", Double::parseDouble);
|
||||||
|
var max = ask("Max: ", Double::parseDouble);
|
||||||
|
yield new Distribution.Uniform(min, max);
|
||||||
|
}
|
||||||
|
case 3 -> {
|
||||||
|
var k = ask("K: ", Integer::parseInt);
|
||||||
|
var lambda = ask("Lambda: ", Double::parseDouble);
|
||||||
|
yield new Distribution.Erlang(k, lambda);
|
||||||
|
}
|
||||||
|
case 4 -> {
|
||||||
|
var probability = ask("Probability: ", Double::parseDouble);
|
||||||
|
var unavailable = askDistribution("Unavailable distribution");
|
||||||
|
yield new Distribution.UnavailableTime(probability, unavailable);
|
||||||
|
}
|
||||||
|
case 5 -> {
|
||||||
|
var mean = ask("Mean: ", Double::parseDouble);
|
||||||
|
var stdDev = ask("Standard deviation: ", Double::parseDouble);
|
||||||
|
yield new Distribution.Normal(mean, stdDev);
|
||||||
|
}
|
||||||
|
case 6 -> {
|
||||||
|
var mean = ask("Mean: ", Double::parseDouble);
|
||||||
|
var stdDev = ask("Standard deviation: ", Double::parseDouble);
|
||||||
|
yield new Distribution.NormalBoxMuller(mean, stdDev);
|
||||||
|
}
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the user a question.
|
||||||
|
*
|
||||||
|
* @param ask the question to ask
|
||||||
|
* @return the answer
|
||||||
|
*/
|
||||||
|
private String ask(String ask) {
|
||||||
|
return ask(ask, Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the user a question.
|
||||||
|
*
|
||||||
|
* @param ask the question to ask
|
||||||
|
* @param parser the parser to use
|
||||||
|
* @param defaultValue the default value
|
||||||
|
* @return the answer
|
||||||
|
*/
|
||||||
|
private <T> T ask(String ask, Function<String, T> parser) {
|
||||||
|
var totalRows = ask.chars().filter(ch -> ch == '\n').count();
|
||||||
|
|
||||||
|
var ask2 = "\033[" + totalRows + "A" + ask;
|
||||||
|
var first = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
this.out.print(first ? ask : ask2);
|
||||||
|
var line = this.scanner.nextLine();
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var value = parser.apply(line);
|
||||||
|
return value;
|
||||||
|
} catch (Exception e) {
|
||||||
|
this.out.print("\033[A\033[K"); // clear the line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null; // normally when the scanner is closed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask the user to choose an option.
|
||||||
|
*
|
||||||
|
* @param ask the question to ask
|
||||||
|
* @param options the options to choose from
|
||||||
|
* @return the choice
|
||||||
|
*/
|
||||||
|
private int choose(String ask, String... options) {
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.append(ask).append("\n");
|
||||||
|
for (int i = 0; i < options.length; i++) {
|
||||||
|
builder.append(i + 1).append(". ").append(options[i]).append("\n");
|
||||||
|
}
|
||||||
|
builder.append("> ");
|
||||||
|
|
||||||
|
var string = builder.toString();
|
||||||
|
var choice = 0;
|
||||||
|
choice = ask(string, val -> {
|
||||||
|
var x = Integer.parseInt(val);
|
||||||
|
if (x < 1 || x > options.length)
|
||||||
|
throw new NumberFormatException(); // retry the question
|
||||||
|
return x;
|
||||||
|
});
|
||||||
|
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,10 +31,7 @@ public class Main {
|
|||||||
var plot = new Plot(csv);
|
var plot = new Plot(csv);
|
||||||
plot.show();
|
plot.show();
|
||||||
}
|
}
|
||||||
case "net" -> {
|
case "interactive" -> new InteractiveConsole().run();
|
||||||
var net = new NetBuilderInteractive();
|
|
||||||
net.run();
|
|
||||||
}
|
|
||||||
default -> exit("Invalid program!");
|
default -> exit("Invalid program!");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -86,11 +83,11 @@ public class Main {
|
|||||||
var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
|
var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
|
||||||
var name = new File(uri).getName();
|
var name = new File(uri).getName();
|
||||||
System.err.println(message);
|
System.err.println(message);
|
||||||
System.out.println("Usage: java -jar " + name + ".jar [simulation|plot|net] [args]");
|
System.out.println("Usage: java -jar " + name + ".jar [simulation|plot|interactive] [args]");
|
||||||
System.out.println("simulation args: -net <net> [-csv <csv>] [-runs <runs>] [-seed <seed>]"
|
System.out.println("simulation args: -net <net> [-csv <csv>] [-runs <runs>] [-seed <seed>]"
|
||||||
+ "[-p] [-end <end>] [-i <indices>]");
|
+ "[-p] [-end <end>] [-i <indices>]");
|
||||||
System.out.println("plot args: -csv <csv>");
|
System.out.println("plot args: -csv <csv>");
|
||||||
System.out.println("net args: none");
|
System.out.println("interactive: no args needed");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -1,186 +0,0 @@
|
|||||||
package net.berack.upo.valpre;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
import net.berack.upo.valpre.rand.Distribution;
|
|
||||||
import net.berack.upo.valpre.sim.Net;
|
|
||||||
import net.berack.upo.valpre.sim.ServerNode;
|
|
||||||
|
|
||||||
public class NetBuilderInteractive {
|
|
||||||
|
|
||||||
private final Net net = new Net();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the interactive net builder.
|
|
||||||
*
|
|
||||||
* @param args the arguments
|
|
||||||
*/
|
|
||||||
public void run() {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
var choice = choose("Choose the next step to do:",
|
|
||||||
"Add a node", "Add a connection", "Print Nodes", "Save the net", "Exit");
|
|
||||||
switch (choice) {
|
|
||||||
case 1 -> {
|
|
||||||
var node = this.buildNode();
|
|
||||||
this.net.addNode(node);
|
|
||||||
}
|
|
||||||
case 2 -> {
|
|
||||||
var source = ask("Enter the source node: ");
|
|
||||||
var target = ask("Enter the target node: ");
|
|
||||||
var weight = ask("Enter the weight: ", Double::parseDouble, 0.0);
|
|
||||||
var sourceNode = this.net.getNode(source);
|
|
||||||
var targetNode = this.net.getNode(target);
|
|
||||||
this.net.addConnection(sourceNode, targetNode, weight);
|
|
||||||
}
|
|
||||||
case 3 -> this.printNodes();
|
|
||||||
case 4 -> this.net.save(ask("Enter the filename: "));
|
|
||||||
case 5 -> System.exit(0);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print the nodes in the net.
|
|
||||||
*/
|
|
||||||
private void printNodes() {
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
builder.append("Nodes:\n");
|
|
||||||
for (var i = 0; i < this.net.size(); i++) {
|
|
||||||
var name = this.net.getNode(i).name;
|
|
||||||
builder.append(name).append(" -> ");
|
|
||||||
|
|
||||||
for (var connection : this.net.getChildren(i)) {
|
|
||||||
var child = this.net.getNode(connection.index);
|
|
||||||
builder.append(child.name).append("(").append(connection.weight).append("), ");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.delete(builder.length() - 2, builder.length());
|
|
||||||
builder.append("\n");
|
|
||||||
}
|
|
||||||
System.out.print(builder.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a node.
|
|
||||||
*
|
|
||||||
* @return the node
|
|
||||||
*/
|
|
||||||
private ServerNode buildNode() {
|
|
||||||
var choice = choose("Choose the type of node to create:", "Source", "Queue");
|
|
||||||
var name = ask("Node name: ");
|
|
||||||
var distribution = askDistribution("Service distribution");
|
|
||||||
|
|
||||||
return switch (choice) {
|
|
||||||
case 1 -> {
|
|
||||||
var limit = ask("Arrivals limit (0 for Int.Max): ", Integer::parseInt, 1);
|
|
||||||
if (limit <= 0)
|
|
||||||
limit = Integer.MAX_VALUE;
|
|
||||||
yield ServerNode.Builder.sourceLimited(name, limit, distribution);
|
|
||||||
}
|
|
||||||
case 2 -> {
|
|
||||||
var servers = ask("Number of servers: ", Integer::parseInt, 1);
|
|
||||||
var unavailable = askDistribution("Unavailable distribution");
|
|
||||||
yield ServerNode.Builder.queue(name, servers, distribution, unavailable);
|
|
||||||
}
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask the user for a distribution.
|
|
||||||
*
|
|
||||||
* @return the distribution
|
|
||||||
*/
|
|
||||||
public static Distribution askDistribution(String ask) {
|
|
||||||
var choice = choose(ask + ":", "Exponential", "Uniform", "Erlang",
|
|
||||||
"UnavailableTime", "Normal", "NormalBoxMuller", "None");
|
|
||||||
|
|
||||||
return switch (choice) {
|
|
||||||
case 1 -> {
|
|
||||||
var lambda = ask("Lambda: ", Double::parseDouble, 1.0);
|
|
||||||
yield new Distribution.Exponential(lambda);
|
|
||||||
}
|
|
||||||
case 2 -> {
|
|
||||||
var min = ask("Min: ", Double::parseDouble, 0.0);
|
|
||||||
var max = ask("Max: ", Double::parseDouble, 1.0);
|
|
||||||
yield new Distribution.Uniform(min, max);
|
|
||||||
}
|
|
||||||
case 3 -> {
|
|
||||||
var k = ask("K: ", Integer::parseInt, 1);
|
|
||||||
var lambda = ask("Lambda: ", Double::parseDouble, 1.0);
|
|
||||||
yield new Distribution.Erlang(k, lambda);
|
|
||||||
}
|
|
||||||
case 4 -> {
|
|
||||||
var probability = ask("Probability: ", Double::parseDouble, 0.0);
|
|
||||||
var unavailable = askDistribution("Unavailable distribution");
|
|
||||||
yield new Distribution.UnavailableTime(probability, unavailable);
|
|
||||||
}
|
|
||||||
case 5 -> {
|
|
||||||
var mean = ask("Mean: ", Double::parseDouble, 0.0);
|
|
||||||
var stdDev = ask("Standard deviation: ", Double::parseDouble, 1.0);
|
|
||||||
yield new Distribution.Normal(mean, stdDev);
|
|
||||||
}
|
|
||||||
case 6 -> {
|
|
||||||
var mean = ask("Mean: ", Double::parseDouble, 0.0);
|
|
||||||
var stdDev = ask("Standard deviation: ", Double::parseDouble, 1.0);
|
|
||||||
yield new Distribution.NormalBoxMuller(mean, stdDev);
|
|
||||||
}
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask the user a question.
|
|
||||||
*
|
|
||||||
* @param ask the question to ask
|
|
||||||
* @return the answer
|
|
||||||
*/
|
|
||||||
private static String ask(String ask) {
|
|
||||||
return ask(ask, Function.identity(), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask the user a question.
|
|
||||||
*
|
|
||||||
* @param ask the question to ask
|
|
||||||
* @param parser the parser to use
|
|
||||||
* @param defaultValue the default value
|
|
||||||
* @return the answer
|
|
||||||
*/
|
|
||||||
private static <T> T ask(String ask, Function<String, T> parser, T defaultValue) {
|
|
||||||
System.out.print(ask);
|
|
||||||
try {
|
|
||||||
var line = System.console().readLine();
|
|
||||||
return parser.apply(line);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println("Invalid input: " + e.getMessage());
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask the user to choose an option.
|
|
||||||
*
|
|
||||||
* @param ask the question to ask
|
|
||||||
* @param options the options to choose from
|
|
||||||
* @return the choice
|
|
||||||
*/
|
|
||||||
private static int choose(String ask, String... options) {
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
builder.append(ask).append("\n");
|
|
||||||
for (int i = 0; i < options.length; i++) {
|
|
||||||
builder.append(i + 1).append(". ").append(options[i]).append("\n");
|
|
||||||
}
|
|
||||||
builder.append("> ");
|
|
||||||
|
|
||||||
var string = builder.toString();
|
|
||||||
var choice = 0;
|
|
||||||
while (choice < 1 || choice > options.length)
|
|
||||||
choice = ask(string, Integer::parseInt, 0);
|
|
||||||
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
94
src/main/java/net/berack/upo/valpre/NetExamples.java
Normal file
94
src/main/java/net/berack/upo/valpre/NetExamples.java
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package net.berack.upo.valpre;
|
||||||
|
|
||||||
|
import net.berack.upo.valpre.rand.Distribution;
|
||||||
|
import net.berack.upo.valpre.sim.Net;
|
||||||
|
import net.berack.upo.valpre.sim.ServerNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides two example networks.
|
||||||
|
* The first network is composed of a terminal node and a queue node.
|
||||||
|
* The second network is composed of a terminal node and two queue nodes.
|
||||||
|
*/
|
||||||
|
public final class NetExamples {
|
||||||
|
/**
|
||||||
|
* Return the first example network.
|
||||||
|
* The net is composed of a terminal node and a queue node.
|
||||||
|
* The terminal node generates 10000 jobs with an exponential distribution 4.5.
|
||||||
|
* The queue node has a capacity of 1 and a service time of 3.2 with a standard
|
||||||
|
* deviation of 0.6.
|
||||||
|
* The terminal node is connected to the queue node with a probability of 1.0.
|
||||||
|
*
|
||||||
|
* @return the first example network
|
||||||
|
*/
|
||||||
|
public static Net getNet1() {
|
||||||
|
var exp0_22 = new Distribution.Exponential(1.0 / 4.5);
|
||||||
|
var norm3_2 = new Distribution.NormalBoxMuller(3.2, 0.6);
|
||||||
|
var spawn = 10000;
|
||||||
|
return getNet1(spawn, exp0_22, norm3_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the first example network.
|
||||||
|
* The net is composed of a terminal node and a queue node.
|
||||||
|
* The terminal node is connected to the queue node.
|
||||||
|
*
|
||||||
|
* @param spawn the number of jobs to generate
|
||||||
|
* @param source the distribution of the source node
|
||||||
|
* @param queue the distribution of the queue node
|
||||||
|
* @return the first example network
|
||||||
|
*/
|
||||||
|
public static Net getNet1(int spawn, Distribution source, Distribution queue) {
|
||||||
|
var net1 = new Net();
|
||||||
|
net1.addNode(ServerNode.Builder.terminal("Source", spawn, source));
|
||||||
|
net1.addNode(ServerNode.Builder.queue("Queue", 1, queue));
|
||||||
|
net1.addConnection(0, 1, 1.0);
|
||||||
|
return net1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the second example network.
|
||||||
|
* The net is composed of a terminal node and two queue nodes.
|
||||||
|
* The terminal node generates 10000 jobs with an exponential distribution 1.5.
|
||||||
|
* The first queue node has a capacity of 1 and a service time of 2.0.
|
||||||
|
* The second queue node has a capacity of 1 and a service time of 3.5.
|
||||||
|
* The second queue node has an unavailable time of 0.1 with an exponential
|
||||||
|
* distribution of 10.0.
|
||||||
|
* The terminal node is connected to the first queue node.
|
||||||
|
* The first queue node is connected to the second queue node.
|
||||||
|
*
|
||||||
|
* @return the second example network
|
||||||
|
*/
|
||||||
|
public static Net getNet2() {
|
||||||
|
var exp1_5 = new Distribution.Exponential(1.5);
|
||||||
|
var exp2 = new Distribution.Exponential(2.0);
|
||||||
|
var exp3_5 = new Distribution.Exponential(3.5);
|
||||||
|
var exp10 = new Distribution.Exponential(10.0);
|
||||||
|
var unExp = new Distribution.UnavailableTime(0.1, exp10);
|
||||||
|
var spawn = 10000;
|
||||||
|
return getNet2(spawn, exp1_5, exp2, exp3_5, unExp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the second example network.
|
||||||
|
* The net is composed of a terminal node and two queue nodes.
|
||||||
|
* The terminal node is connected to the first queue node.
|
||||||
|
* The first queue node is connected to the second queue node.
|
||||||
|
*
|
||||||
|
* @param spawn the number of jobs to generate
|
||||||
|
* @param source the distribution of the source node
|
||||||
|
* @param service1 the distribution of the first queue node
|
||||||
|
* @param service2 the distribution of the second queue node
|
||||||
|
* @param unExp the distribution of the unavailable time
|
||||||
|
* @return the second example network
|
||||||
|
*/
|
||||||
|
public static Net getNet2(int spawn, Distribution source, Distribution service1, Distribution service2,
|
||||||
|
Distribution unExp) {
|
||||||
|
var net3 = new Net();
|
||||||
|
net3.addNode(ServerNode.Builder.terminal("Source", spawn, source));
|
||||||
|
net3.addNode(ServerNode.Builder.queue("Service1", 1, service1));
|
||||||
|
net3.addNode(ServerNode.Builder.queue("Service2", 1, service2, unExp));
|
||||||
|
net3.addConnection(0, 1, 1.0);
|
||||||
|
net3.addConnection(1, 2, 1.0);
|
||||||
|
return net3;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package net.berack.upo.valpre;
|
package net.berack.upo.valpre;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -194,23 +191,4 @@ public class Parameters {
|
|||||||
throw new IllegalArgumentException("Invalid arguments");
|
throw new IllegalArgumentException("Invalid arguments");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the file or the example file if it is present.
|
|
||||||
*
|
|
||||||
* @param file the file to get
|
|
||||||
* @return the file or the example file
|
|
||||||
* @throws FileNotFoundException if the file is not found
|
|
||||||
*/
|
|
||||||
public static InputStream getFileOrExample(String file) throws FileNotFoundException {
|
|
||||||
if (file == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (file.startsWith("example")) {
|
|
||||||
var resource = Parameters.class.getClassLoader().getResourceAsStream(file);
|
|
||||||
if (resource != null)
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
return new FileInputStream(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,7 @@ public class Plot {
|
|||||||
* @throws IOException if anything happens while reading the file
|
* @throws IOException if anything happens while reading the file
|
||||||
*/
|
*/
|
||||||
public Plot(String csv) throws IOException {
|
public Plot(String csv) throws IOException {
|
||||||
var stream = Parameters.getFileOrExample(csv);
|
var results = new CsvResult(csv).loadResults();
|
||||||
if (stream == null)
|
|
||||||
throw new IllegalArgumentException("CSV file needed!");
|
|
||||||
var results = CsvResult.loadResults(stream);
|
|
||||||
stream.close();
|
|
||||||
|
|
||||||
this.summary = new Result.Summary(results);
|
this.summary = new Result.Summary(results);
|
||||||
|
|
||||||
var nodes = this.summary.getNodes().toArray(new String[0]);
|
var nodes = this.summary.getNodes().toArray(new String[0]);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.berack.upo.valpre;
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import com.esotericsoftware.kryo.KryoException;
|
import com.esotericsoftware.kryo.KryoException;
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ import net.berack.upo.valpre.sim.EndCriteria.MaxTime;
|
|||||||
import net.berack.upo.valpre.sim.Net;
|
import net.berack.upo.valpre.sim.Net;
|
||||||
import net.berack.upo.valpre.sim.SimulationMultiple;
|
import net.berack.upo.valpre.sim.SimulationMultiple;
|
||||||
import net.berack.upo.valpre.sim.stats.CsvResult;
|
import net.berack.upo.valpre.sim.stats.CsvResult;
|
||||||
|
import net.berack.upo.valpre.sim.stats.Result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is responsible for running the simulation. It parses the arguments
|
* This class is responsible for running the simulation. It parses the arguments
|
||||||
@@ -35,10 +37,8 @@ public class SimulationBuilder {
|
|||||||
*/
|
*/
|
||||||
public SimulationBuilder(String netFile) throws IOException {
|
public SimulationBuilder(String netFile) throws IOException {
|
||||||
try {
|
try {
|
||||||
var file = Parameters.getFileOrExample(netFile);
|
this.net = Net.load(netFile);
|
||||||
this.net = Net.load(file);
|
|
||||||
this.confidences = new ConfidenceIndices(this.net);
|
this.confidences = new ConfidenceIndices(this.net);
|
||||||
file.close();
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
throw new IllegalArgumentException("Net file needed!");
|
throw new IllegalArgumentException("Net file needed!");
|
||||||
} catch (KryoException e) {
|
} catch (KryoException e) {
|
||||||
@@ -230,23 +230,38 @@ public class SimulationBuilder {
|
|||||||
* @throws ExecutionException If the simulation has an error.
|
* @throws ExecutionException If the simulation has an error.
|
||||||
* @throws IOException If the CSV file has a problem.
|
* @throws IOException If the CSV file has a problem.
|
||||||
*/
|
*/
|
||||||
public void run() throws InterruptedException, ExecutionException, IOException {
|
public Result.Summary run() throws InterruptedException, ExecutionException, IOException {
|
||||||
|
return this.run(System.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the simulation with the given parameters.
|
||||||
|
* At the end it prints the results and saves them to a CSV file if requested.
|
||||||
|
*
|
||||||
|
* @param out the output stream to print the results
|
||||||
|
* @throws InterruptedException If the simulation is interrupted.
|
||||||
|
* @throws ExecutionException If the simulation has an error.
|
||||||
|
* @throws IOException If the CSV file has a problem.
|
||||||
|
*/
|
||||||
|
public Result.Summary run(PrintStream out) throws InterruptedException, ExecutionException, IOException {
|
||||||
var nano = System.nanoTime();
|
var nano = System.nanoTime();
|
||||||
var sim = new SimulationMultiple(this.net);
|
var sim = new SimulationMultiple(this.net);
|
||||||
var summary = switch (this.type) {
|
var summary = switch (this.type) {
|
||||||
case Incremental -> sim.runIncremental(this.seed, this.runs, this.confidences, this.endCriteria);
|
case Incremental -> sim.runIncremental(this.seed, this.runs, out, this.confidences, this.endCriteria);
|
||||||
case Parallel -> sim.runParallel(this.seed, this.runs, this.endCriteria);
|
case Parallel -> sim.runParallel(this.seed, this.runs, this.endCriteria);
|
||||||
case Normal -> sim.run(this.seed, this.runs, this.endCriteria);
|
case Normal -> sim.run(this.seed, this.runs, this.endCriteria);
|
||||||
};
|
};
|
||||||
nano = System.nanoTime() - nano;
|
nano = System.nanoTime() - nano;
|
||||||
|
|
||||||
System.out.print(summary);
|
out.print(summary);
|
||||||
System.out.println("Final time " + nano / 1e6 + "ms");
|
out.println("Final time " + nano / 1e6 + "ms");
|
||||||
|
|
||||||
if (csv != null) {
|
if (csv != null) {
|
||||||
new CsvResult(this.csv).saveResults(summary.getRuns());
|
new CsvResult(this.csv).saveResults(summary.getRuns());
|
||||||
System.out.println("Data saved to " + this.csv);
|
out.println("Data saved to " + this.csv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,6 +32,36 @@ public interface Distribution {
|
|||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the distribution.
|
||||||
|
* In case the distribution is null, an empty string is returned.
|
||||||
|
*
|
||||||
|
* @param distribution The distribution to represent.
|
||||||
|
* @return A string representation of the distribution.
|
||||||
|
* @throws IllegalArgumentException if the field cannot be accessed
|
||||||
|
* @throws IllegalAccessException if the field cannot be accessed
|
||||||
|
*/
|
||||||
|
public static String toString(Distribution distribution) throws IllegalArgumentException, IllegalAccessException {
|
||||||
|
if (distribution == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
var dist = distribution.getClass();
|
||||||
|
|
||||||
|
builder.append(dist.getSimpleName()).append('(');
|
||||||
|
for (var param : dist.getFields()) {
|
||||||
|
var paramValue = param.get(distribution);
|
||||||
|
var str = paramValue instanceof Distribution
|
||||||
|
? toString((Distribution) paramValue)
|
||||||
|
: paramValue;
|
||||||
|
|
||||||
|
builder.append(str).append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.delete(builder.length() - 2, builder.length()).append(')');
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an exponential distribution.
|
* Represents an exponential distribution.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -23,11 +23,7 @@ public class Event implements Comparable<Event> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Event other) {
|
public int compareTo(Event other) {
|
||||||
if (this.time < other.time)
|
return Double.compare(this.time, other.time);
|
||||||
return -1;
|
|
||||||
if (this.time == other.time)
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ public final class Net implements Iterable<ServerNode> {
|
|||||||
* @param weight The probability of the child node.
|
* @param weight The probability of the child node.
|
||||||
* @throws IndexOutOfBoundsException if one of the two nodes are not in the net
|
* @throws IndexOutOfBoundsException if one of the two nodes are not in the net
|
||||||
* @throws IllegalArgumentException if the weight is negative or zero
|
* @throws IllegalArgumentException if the weight is negative or zero
|
||||||
* @throws IllegalArgumentException if the child is a source node
|
|
||||||
*/
|
*/
|
||||||
public void addConnection(int parent, int child, double weight) {
|
public void addConnection(int parent, int child, double weight) {
|
||||||
if (weight <= 0)
|
if (weight <= 0)
|
||||||
@@ -85,9 +84,6 @@ public final class Net implements Iterable<ServerNode> {
|
|||||||
if (parent < 0 || child < 0 || parent > max || child > max)
|
if (parent < 0 || child < 0 || parent > max || child > max)
|
||||||
throw new IndexOutOfBoundsException("One of the nodes does not exist");
|
throw new IndexOutOfBoundsException("One of the nodes does not exist");
|
||||||
|
|
||||||
if (this.servers.get(child).spawnArrivals > 0)
|
|
||||||
throw new IllegalArgumentException("Can't connect to a source node");
|
|
||||||
|
|
||||||
var list = this.connections.get(parent);
|
var list = this.connections.get(parent);
|
||||||
list.removeIf(conn -> conn.index == child);
|
list.removeIf(conn -> conn.index == child);
|
||||||
list.add(new Connection(child, weight));
|
list.add(new Connection(child, weight));
|
||||||
@@ -170,14 +166,13 @@ public final class Net implements Iterable<ServerNode> {
|
|||||||
for (var conn : list)
|
for (var conn : list)
|
||||||
sum += conn.weight;
|
sum += conn.weight;
|
||||||
|
|
||||||
var newOne = new Connection[list.size()];
|
var newOne = new ArrayList<Connection>();
|
||||||
for (var i = 0; i < list.size(); i++) {
|
for (var conn : list) {
|
||||||
var conn = list.get(i);
|
|
||||||
var newWeight = conn.weight / sum;
|
var newWeight = conn.weight / sum;
|
||||||
newOne[i] = new Connection(conn.index, newWeight);
|
newOne.add(new Connection(conn.index, newWeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connections.set(node, List.of(newOne));
|
this.connections.set(node, newOne);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +244,56 @@ public final class Net implements Iterable<ServerNode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a copy of the net passed as input.
|
||||||
|
* The new net will have the same nodes and connections.
|
||||||
|
*
|
||||||
|
* @param net the net to copy
|
||||||
|
* @return a new Net object
|
||||||
|
*/
|
||||||
|
public static Net copyOf(Net net) {
|
||||||
|
var newNet = new Net();
|
||||||
|
|
||||||
|
for (var index = 0; index < net.size(); index++) {
|
||||||
|
var node = net.servers.get(index);
|
||||||
|
var conn = net.connections.get(index);
|
||||||
|
conn = new ArrayList<>(conn);
|
||||||
|
|
||||||
|
newNet.indices.put(node, index);
|
||||||
|
newNet.servers.add(node);
|
||||||
|
newNet.connections.add(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
try {
|
||||||
|
for (var node : this.servers) {
|
||||||
|
builder.append(node)
|
||||||
|
.append(" -> ");
|
||||||
|
|
||||||
|
for (var child : this.getChildren(this.indices.get(node))) {
|
||||||
|
var childNode = this.servers.get(child.index);
|
||||||
|
builder.append(childNode.name)
|
||||||
|
.append("(")
|
||||||
|
.append(child.weight)
|
||||||
|
.append("), ");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.delete(builder.length() - 2, builder.length())
|
||||||
|
.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Static inner class used to represent the connection of a node
|
* A Static inner class used to represent the connection of a node
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class ServerNode {
|
|||||||
/**
|
/**
|
||||||
* Creates a generic node with the given name and distribution.
|
* Creates a generic node with the given name and distribution.
|
||||||
* The servers number must be 1 or higher; if lower will be put to 1.
|
* The servers number must be 1 or higher; if lower will be put to 1.
|
||||||
* The spawn number must be 0 or higher; if lower will be put to 0.
|
* The spawn number must be 0 or higher; if lower will be put to -1 (infinite).
|
||||||
* The queue number must be equal or higher than the servers number; if lower
|
* The queue number must be equal or higher than the servers number; if lower
|
||||||
* will be put to the servers number.
|
* will be put to the servers number.
|
||||||
* The service distribution can't be null, otherwise an exception is thrown.
|
* The service distribution can't be null, otherwise an exception is thrown.
|
||||||
@@ -37,7 +37,7 @@ public class ServerNode {
|
|||||||
if (servers <= 0)
|
if (servers <= 0)
|
||||||
servers = 1;
|
servers = 1;
|
||||||
if (spawn < 0)
|
if (spawn < 0)
|
||||||
spawn = 0;
|
spawn = -1;
|
||||||
if (queue < servers)
|
if (queue < servers)
|
||||||
queue = servers;
|
queue = servers;
|
||||||
|
|
||||||
@@ -71,6 +71,28 @@ public class ServerNode {
|
|||||||
return Distribution.getPositiveSample(this.unavailable, rng);
|
return Distribution.getPositiveSample(this.unavailable, rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
try {
|
||||||
|
var build = new StringBuilder()
|
||||||
|
.append(this.name)
|
||||||
|
.append("[servers:")
|
||||||
|
.append(this.maxServers)
|
||||||
|
.append(", queue:")
|
||||||
|
.append(this.maxQueue)
|
||||||
|
.append(", spawn:")
|
||||||
|
.append(this.spawnArrivals)
|
||||||
|
.append(", ")
|
||||||
|
.append(Distribution.toString(this.service));
|
||||||
|
if (this.unavailable != null)
|
||||||
|
build.append(", u:").append(Distribution.toString(this.unavailable));
|
||||||
|
return build.append(']').toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof ServerNode))
|
if (!(obj instanceof ServerNode))
|
||||||
@@ -183,26 +205,27 @@ public class ServerNode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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).
|
* It swpawns infinite arrivals (-1).
|
||||||
*
|
*
|
||||||
* @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 source(String name, Distribution distribution) {
|
public static ServerNode source(String name, Distribution distribution) {
|
||||||
return new Builder(name, distribution).spawn(Integer.MAX_VALUE).build();
|
return new Builder(name, distribution).spawn(-1).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a source node with the given name, distribution, and number of
|
* Creates a terminal node with the given name, distribution, and number of
|
||||||
* arrivals to spawn.
|
* arrivals to spawn.
|
||||||
|
* Once it has finished spawning the arrivals, it will not spawn anymore.
|
||||||
*
|
*
|
||||||
* @param name The name of the node.
|
* @param name The name of the node.
|
||||||
* @param service The distribution of the inter-arrival times.
|
* @param service The distribution of the inter-arrival times.
|
||||||
* @param spawnArrivals The number of arrivals to spawn.
|
* @param spawnArrivals The number of arrivals to spawn.
|
||||||
* @return The created source node.
|
* @return The created source node.
|
||||||
*/
|
*/
|
||||||
public static ServerNode sourceLimited(String name, int spawnArrivals, Distribution service) {
|
public static ServerNode terminal(String name, int spawnArrivals, Distribution service) {
|
||||||
return new Builder(name, service).spawn(spawnArrivals).build();
|
return new Builder(name, service).spawn(spawnArrivals).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class ServerNodeState {
|
|||||||
* @return True if the node should spawn an arrival, false otherwise.
|
* @return True if the node should spawn an arrival, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean shouldSpawnArrival() {
|
public boolean shouldSpawnArrival() {
|
||||||
return this.node.spawnArrivals > this.stats.numArrivals;
|
return this.node.spawnArrivals < 0 || this.node.spawnArrivals > this.stats.numArrivals;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,6 +158,23 @@ public class ServerNodeState {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a random child based on the weights of the children and the random number
|
||||||
|
* generator passed as input
|
||||||
|
*
|
||||||
|
* @param rng the random number generator
|
||||||
|
* @return the index of the child or -1 if no child is selected
|
||||||
|
*/
|
||||||
|
public int getRandomChild(Rng rng) {
|
||||||
|
var random = rng.random();
|
||||||
|
for (var child : this.children) {
|
||||||
|
random -= child.weight;
|
||||||
|
if (random <= 0)
|
||||||
|
return child.index;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an arrival event to a child node based on the node and the time passed
|
* Create an arrival event to a child node based on the node and the time passed
|
||||||
* as input
|
* as input
|
||||||
@@ -168,12 +185,7 @@ public class ServerNodeState {
|
|||||||
* otherwise
|
* otherwise
|
||||||
*/
|
*/
|
||||||
public Event spawnArrivalToChild(double time, Rng rng) {
|
public Event spawnArrivalToChild(double time, Rng rng) {
|
||||||
var random = rng.random();
|
var child = this.getRandomChild(rng);
|
||||||
for (var child : this.children) {
|
return child > -1 ? Event.newArrival(child, time) : null;
|
||||||
random -= child.weight;
|
|
||||||
if (random <= 0)
|
|
||||||
return Event.newArrival(child.index, time);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.berack.upo.valpre.sim;
|
package net.berack.upo.valpre.sim;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
@@ -98,8 +97,13 @@ public final class Simulation {
|
|||||||
case DEPARTURE -> {
|
case DEPARTURE -> {
|
||||||
state.updateDeparture(time);
|
state.updateDeparture(time);
|
||||||
|
|
||||||
|
// Spawn unavailability if has unavailable time
|
||||||
this.addToFel(state.spawnUnavailableIfPossible(time, this.rng));
|
this.addToFel(state.spawnUnavailableIfPossible(time, this.rng));
|
||||||
|
|
||||||
|
// Spawn departure if has requests and server is available
|
||||||
this.addToFel(state.spawnDepartureIfPossible(time, this.rng));
|
this.addToFel(state.spawnDepartureIfPossible(time, this.rng));
|
||||||
|
|
||||||
|
// Spawn arrival to self if is source node
|
||||||
this.addToFel(state.spawnArrivalIfPossilbe(time));
|
this.addToFel(state.spawnArrivalIfPossilbe(time));
|
||||||
|
|
||||||
// Spawn arrival to child node if queue is not full otherwise drop
|
// Spawn arrival to child node if queue is not full otherwise drop
|
||||||
@@ -151,7 +155,7 @@ public final class Simulation {
|
|||||||
* @return a list of future events.
|
* @return a list of future events.
|
||||||
*/
|
*/
|
||||||
public List<Event> getFutureEventList() {
|
public List<Event> getFutureEventList() {
|
||||||
return new ArrayList<>(this.fel);
|
return List.copyOf(this.fel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.berack.upo.valpre.sim;
|
package net.berack.upo.valpre.sim;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -99,16 +100,21 @@ public class SimulationMultiple {
|
|||||||
* Run the simulation multiple times with the given seed and end criteria. The
|
* Run the simulation multiple times with the given seed and end criteria. The
|
||||||
* simulation runs will stop when the relative error of the confidence index is
|
* simulation runs will stop when the relative error of the confidence index is
|
||||||
* less than the given value.
|
* less than the given value.
|
||||||
* The results are printed on the console.
|
* The results are printed on the PrintStream.
|
||||||
*
|
*
|
||||||
* @param seed The seed to use for the random number generator.
|
* @param seed The seed to use for the random number generator.
|
||||||
|
* @param runs The maximum number of runs to perform.
|
||||||
|
* @param stream The PrintStream to print the results.
|
||||||
|
* @param confidences The confidence indices to use to determine when to stop
|
||||||
|
* the simulation.
|
||||||
* @param criterias The criteria to determine when to end the simulation. If
|
* @param criterias The criteria to determine when to end the simulation. If
|
||||||
* null then the simulation will run until there are no more
|
* null then the simulation will run until there are no more
|
||||||
* events.
|
* events.
|
||||||
* @return The statistics the network.
|
* @return The statistics the network.
|
||||||
* @throws IllegalArgumentException If the confidence is not set.
|
* @throws IllegalArgumentException If the confidence is not set.
|
||||||
*/
|
*/
|
||||||
public Result.Summary runIncremental(long seed, int runs, ConfidenceIndices confidences, EndCriteria... criterias) {
|
public Result.Summary runIncremental(long seed, int runs, PrintStream stream, ConfidenceIndices confidences,
|
||||||
|
EndCriteria... criterias) {
|
||||||
if (confidences == null)
|
if (confidences == null)
|
||||||
throw new IllegalArgumentException("Confidence must be not null");
|
throw new IllegalArgumentException("Confidence must be not null");
|
||||||
|
|
||||||
@@ -133,11 +139,11 @@ public class SimulationMultiple {
|
|||||||
var oneSting = String.join("], [", errString);
|
var oneSting = String.join("], [", errString);
|
||||||
|
|
||||||
output.append('[').append(oneSting).append("]");
|
output.append('[').append(oneSting).append("]");
|
||||||
System.out.print(output);
|
stream.print(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(); // remove last printed line
|
stream.println(); // remove last printed line
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
187
src/test/java/net/berack/upo/valpre/sim/TestInteractions.java
Normal file
187
src/test/java/net/berack/upo/valpre/sim/TestInteractions.java
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package net.berack.upo.valpre.sim;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import net.berack.upo.valpre.InteractiveConsole;
|
||||||
|
import net.berack.upo.valpre.rand.Distribution;
|
||||||
|
|
||||||
|
public class TestInteractions {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void distributionToString() throws Exception {
|
||||||
|
var exp = new Distribution.Exponential(1.0);
|
||||||
|
assertEquals("Exponential(1.0)", Distribution.toString(exp));
|
||||||
|
|
||||||
|
var uniform = new Distribution.Uniform(0.0, 1.0);
|
||||||
|
assertEquals("Uniform(0.0, 1.0)", Distribution.toString(uniform));
|
||||||
|
|
||||||
|
var erlang = new Distribution.Erlang(2, 1.0);
|
||||||
|
assertEquals("Erlang(2, 1.0)", Distribution.toString(erlang));
|
||||||
|
|
||||||
|
var normal = new Distribution.Normal(3.2, 0.6);
|
||||||
|
assertEquals("Normal(3.2, 0.6)", Distribution.toString(normal));
|
||||||
|
|
||||||
|
var normalBoxMuller = new Distribution.NormalBoxMuller(3.2, 0.6);
|
||||||
|
assertEquals("NormalBoxMuller(3.2, 0.6)", Distribution.toString(normalBoxMuller));
|
||||||
|
|
||||||
|
var unavailable = new Distribution.UnavailableTime(0.1, exp);
|
||||||
|
assertEquals("UnavailableTime(0.1, Exponential(1.0))", Distribution.toString(unavailable));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nodeToString() {
|
||||||
|
var exp = new Distribution.Exponential(1.0);
|
||||||
|
var normal = new Distribution.Normal(3.2, 0.6);
|
||||||
|
var unavailable = new Distribution.UnavailableTime(0.1, exp);
|
||||||
|
|
||||||
|
var node = new ServerNode.Builder("Source", exp).build();
|
||||||
|
assertEquals("Source[servers:1, queue:100, spawn:0, Exponential(1.0)]", node.toString());
|
||||||
|
|
||||||
|
node = new ServerNode.Builder("Queue", normal).build();
|
||||||
|
assertEquals("Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)]", node.toString());
|
||||||
|
|
||||||
|
node = new ServerNode.Builder("Queue", normal).queue(10).servers(5).spawn(100).build();
|
||||||
|
assertEquals("Queue[servers:5, queue:10, spawn:100, Normal(3.2, 0.6)]", node.toString());
|
||||||
|
|
||||||
|
node = new ServerNode.Builder("Queue", normal).queue(10).servers(5).spawn(100).unavailable(unavailable).build();
|
||||||
|
assertEquals(
|
||||||
|
"Queue[servers:5, queue:10, spawn:100, Normal(3.2, 0.6), u:UnavailableTime(0.1, Exponential(1.0))]",
|
||||||
|
node.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void netToString() {
|
||||||
|
var net = new Net();
|
||||||
|
assertEquals("", net.toString());
|
||||||
|
|
||||||
|
var node1 = new ServerNode.Builder("Source", new Distribution.Exponential(1)).build();
|
||||||
|
net.addNode(node1);
|
||||||
|
assertEquals(node1 + " -\n", net.toString());
|
||||||
|
|
||||||
|
var node2 = new ServerNode.Builder("Server", new Distribution.Normal(3.2, 0.6)).build();
|
||||||
|
net.addNode(node2);
|
||||||
|
assertEquals(node1 + " -\n" + node2 + " -\n", net.toString());
|
||||||
|
|
||||||
|
net.addConnection(0, 1, 1.0);
|
||||||
|
assertEquals(node1 + " -> Server(1.0)\n" + node2 + " -\n", net.toString());
|
||||||
|
|
||||||
|
var node3 = new ServerNode.Builder("Server2", new Distribution.Normal(4.1, 0.1)).build();
|
||||||
|
net.addNode(node3);
|
||||||
|
assertEquals(node1 + " -> Server(1.0)\n" + node2 + " -\n" + node3 + " -\n", net.toString());
|
||||||
|
|
||||||
|
net.addConnection(0, 2, 1.0);
|
||||||
|
net.normalizeWeights();
|
||||||
|
assertEquals(node1 + " -> Server(0.5), Server2(0.5)\n" + node2 + " -\n" + node3 + " -\n", net.toString());
|
||||||
|
|
||||||
|
net.addConnection(1, 2, 1.0);
|
||||||
|
assertEquals(node1 + " -> Server(0.5), Server2(0.5)\n"
|
||||||
|
+ node2 + " -> Server2(1.0)\n"
|
||||||
|
+ node3 + " -\n",
|
||||||
|
net.toString());
|
||||||
|
|
||||||
|
var const0 = new Distribution.Uniform(0, 0);
|
||||||
|
var unavailable = new Distribution.UnavailableTime(0.1, new Distribution.Exponential(1));
|
||||||
|
var other = ServerNode.Builder.queue("Other", 1, const0, unavailable);
|
||||||
|
net.addNode(other);
|
||||||
|
assertEquals(node1 + " -> Server(0.5), Server2(0.5)\n"
|
||||||
|
+ node2 + " -> Server2(1.0)\n"
|
||||||
|
+ node3 + " -\n"
|
||||||
|
+ other + " -\n",
|
||||||
|
net.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 1000)
|
||||||
|
public void netBuilderInteractive() {
|
||||||
|
|
||||||
|
// Test the interactive console EXIT
|
||||||
|
var net = runInteraction("7");
|
||||||
|
assertEquals("", net.toString());
|
||||||
|
|
||||||
|
// Test the interactive console ADD NODE
|
||||||
|
net = runInteraction("1", "1", "Source", "1", "1.0", "7");
|
||||||
|
assertEquals("Source[servers:1, queue:100, spawn:-1, Exponential(1.0)] -\n", net.toString());
|
||||||
|
|
||||||
|
// Test the interactive console ADD SECOND NODE
|
||||||
|
net = runInteraction("1", "2", "Terminal", "1", "2.0", "500",
|
||||||
|
"1", "3", "Queue", "5", "3.2", "0.6", "1",
|
||||||
|
"7");
|
||||||
|
assertEquals("Terminal[servers:1, queue:100, spawn:500, Exponential(2.0)] -\n"
|
||||||
|
+ "Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)] -\n", net.toString());
|
||||||
|
|
||||||
|
// Test the interactive console ADD CONNECTION
|
||||||
|
net = runInteraction("1", "1", "Source", "1", "2.0",
|
||||||
|
"1", "3", "Queue", "5", "3.2", "0.6", "1",
|
||||||
|
"2", "Source", "Queue", "1.0",
|
||||||
|
"7");
|
||||||
|
assertEquals("Source[servers:1, queue:100, spawn:-1, Exponential(2.0)] -> Queue(1.0)\n"
|
||||||
|
+ "Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)] -\n", net.toString());
|
||||||
|
|
||||||
|
// Test the interactive console CLEAR
|
||||||
|
net = runInteraction("1", "1", "Source", "1", "2.0",
|
||||||
|
"1", "3", "Queue", "5", "3.2", "0.6", "1",
|
||||||
|
"2", "Source", "Queue", "1.0",
|
||||||
|
"2", "Queue", "Queue", "1.0",
|
||||||
|
"5", "7");
|
||||||
|
assertEquals("", net.toString());
|
||||||
|
|
||||||
|
// Test the interactive console LOAD
|
||||||
|
net = runInteraction("4", "1", "src/test/resources/example1.net", "7");
|
||||||
|
assertEquals("Source[servers:1, queue:100, spawn:10000, Exponential(0.2222222222222222)] -> Queue(1.0)\n"
|
||||||
|
+ "Queue[servers:1, queue:100, spawn:0, NormalBoxMuller(3.2, 0.6)] -\n",
|
||||||
|
net.toString());
|
||||||
|
|
||||||
|
// Test the interactive console LOAD EXAMPLE 1
|
||||||
|
net = runInteraction("4", "2", "1", "7");
|
||||||
|
assertEquals("Source[servers:1, queue:100, spawn:10000, Exponential(0.2222222222222222)] -> Queue(1.0)\n"
|
||||||
|
+ "Queue[servers:1, queue:100, spawn:0, NormalBoxMuller(3.2, 0.6)] -\n",
|
||||||
|
net.toString());
|
||||||
|
|
||||||
|
// Test the interactive console LOAD EXAMPLE 2
|
||||||
|
net = runInteraction("4", "2", "2", "7");
|
||||||
|
assertEquals("Source[servers:1, queue:100, spawn:10000, Exponential(1.5)] -> Service1(1.0)\n"
|
||||||
|
+ "Service1[servers:1, queue:100, spawn:0, Exponential(2.0)] -> Service2(1.0)\n"
|
||||||
|
+ "Service2[servers:1, queue:100, spawn:0, Exponential(3.5), u:UnavailableTime(0.1, Exponential(10.0))] -\n",
|
||||||
|
net.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Net runInteraction(String... commands) {
|
||||||
|
var out = new PrintStream(OutputStream.nullOutputStream());
|
||||||
|
var inputs = String.join("\n", commands);
|
||||||
|
var bytes = inputs.getBytes();
|
||||||
|
var in = new ByteArrayInputStream(bytes);
|
||||||
|
return new InteractiveConsole(out, in).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An interaction example is like this:
|
||||||
|
* 1. Add a node
|
||||||
|
* - Choose the type of node to create:
|
||||||
|
* - 1. Source
|
||||||
|
* - 2. Queue
|
||||||
|
* - 3. Queue with unavailable time
|
||||||
|
* - Node name: Name
|
||||||
|
* - Arrivals limit (0 for Int.Max) / Number of servers: 1
|
||||||
|
* - Choose the type of service distribution:
|
||||||
|
* - - 1. Exponential
|
||||||
|
* - - 2. Uniform
|
||||||
|
* - - 3. Erlang
|
||||||
|
* - - 4. UnavailableTime
|
||||||
|
* - - 5. Normal
|
||||||
|
* - - 6. NormalBoxMuller
|
||||||
|
* - - 7. None
|
||||||
|
* 2. Add a connection
|
||||||
|
* - Enter the source node: Source
|
||||||
|
* - Enter the target node: Queue
|
||||||
|
* - Enter the weight: 1.0
|
||||||
|
* 3. Save the net
|
||||||
|
* 4. Load net
|
||||||
|
* 5. Clear
|
||||||
|
* 6. Exit
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import static org.junit.Assert.assertArrayEquals;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
@@ -11,61 +13,26 @@ import org.junit.jupiter.api.BeforeAll;
|
|||||||
|
|
||||||
import com.esotericsoftware.kryo.KryoException;
|
import com.esotericsoftware.kryo.KryoException;
|
||||||
|
|
||||||
|
import net.berack.upo.valpre.NetExamples;
|
||||||
import net.berack.upo.valpre.SimulationBuilder;
|
import net.berack.upo.valpre.SimulationBuilder;
|
||||||
import net.berack.upo.valpre.rand.Distribution;
|
|
||||||
import net.berack.upo.valpre.rand.Rng;
|
import net.berack.upo.valpre.rand.Rng;
|
||||||
import net.berack.upo.valpre.sim.stats.CsvResult;
|
import net.berack.upo.valpre.sim.stats.CsvResult;
|
||||||
import net.berack.upo.valpre.sim.stats.NodeStats;
|
import net.berack.upo.valpre.sim.stats.NodeStats;
|
||||||
|
|
||||||
public class TestSaveExamplesNet {
|
public class TestSaveExamplesNet {
|
||||||
|
|
||||||
private static final Distribution exp0_22 = new Distribution.Exponential(1.0 / 4.5);
|
private static final String path = "src/test/resources/example%d.%s";
|
||||||
private static final Distribution exp2 = new Distribution.Exponential(2.0);
|
|
||||||
private static final Distribution exp1_5 = new Distribution.Exponential(1.5);
|
|
||||||
private static final Distribution exp3_5 = new Distribution.Exponential(3.5);
|
|
||||||
private static final Distribution exp10 = new Distribution.Exponential(10.0);
|
|
||||||
|
|
||||||
private static final Distribution norm3_2 = new Distribution.NormalBoxMuller(3.2, 0.6);
|
|
||||||
private static final Distribution norm4_2 = new Distribution.NormalBoxMuller(4.2, 0.6);
|
|
||||||
|
|
||||||
private static final Distribution unNorm = new Distribution.UnavailableTime(0.2, norm4_2);
|
|
||||||
private static final Distribution unExp = new Distribution.UnavailableTime(0.1, exp10);
|
|
||||||
|
|
||||||
private static final int spawn = 10000;
|
|
||||||
private static final String path = "src/main/resources/example%d.%s";
|
|
||||||
private static final String netFile1 = path.formatted(1, "net");
|
private static final String netFile1 = path.formatted(1, "net");
|
||||||
private static final String netFile2 = path.formatted(2, "net");
|
private static final String netFile2 = path.formatted(2, "net");
|
||||||
private static final String netFile3 = path.formatted(3, "net");
|
|
||||||
private static final String csv1 = path.formatted(1, "csv");
|
private static final String csv1 = path.formatted(1, "csv");
|
||||||
private static final String csv2 = path.formatted(2, "csv");
|
private static final String csv2 = path.formatted(2, "csv");
|
||||||
private static final String csv3 = path.formatted(3, "csv");
|
private static final Net net1 = NetExamples.getNet1();
|
||||||
|
private static final Net net2 = NetExamples.getNet2();
|
||||||
private static final Net net1 = new Net();
|
|
||||||
private static final Net net2 = new Net();
|
|
||||||
private static final Net net3 = new Net();
|
|
||||||
static {
|
|
||||||
net1.addNode(ServerNode.Builder.sourceLimited("Source", spawn, exp0_22));
|
|
||||||
net1.addNode(ServerNode.Builder.queue("Queue", 1, norm3_2));
|
|
||||||
net1.addConnection(0, 1, 1.0);
|
|
||||||
|
|
||||||
net2.addNode(ServerNode.Builder.sourceLimited("Source", spawn, exp0_22));
|
|
||||||
net2.addNode(ServerNode.Builder.queue("Queue", 1, norm3_2));
|
|
||||||
net2.addNode(ServerNode.Builder.queue("Queue Wait", 1, norm3_2, unNorm));
|
|
||||||
net2.addConnection(0, 1, 1.0);
|
|
||||||
net2.addConnection(1, 2, 1.0);
|
|
||||||
|
|
||||||
net3.addNode(ServerNode.Builder.sourceLimited("Source", spawn, exp1_5));
|
|
||||||
net3.addNode(ServerNode.Builder.queue("Service1", 1, exp2));
|
|
||||||
net3.addNode(ServerNode.Builder.queue("Service2", 1, exp3_5, unExp));
|
|
||||||
net3.addConnection(0, 1, 1.0);
|
|
||||||
net3.addConnection(1, 2, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public void saveAll() throws IOException {
|
public void saveAll() throws IOException {
|
||||||
net1.save(netFile1);
|
net1.save(netFile1);
|
||||||
net2.save(netFile2);
|
net2.save(netFile2);
|
||||||
net3.save(netFile3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -85,20 +52,6 @@ public class TestSaveExamplesNet {
|
|||||||
public void loadExample2() throws KryoException, IOException {
|
public void loadExample2() throws KryoException, IOException {
|
||||||
var sim = new Simulation(Net.load(netFile2), new Rng());
|
var sim = new Simulation(Net.load(netFile2), new Rng());
|
||||||
var res = sim.run();
|
var res = sim.run();
|
||||||
var time = 45417.0;
|
|
||||||
var maxErr = time / 1000.0;
|
|
||||||
|
|
||||||
assertEquals(Rng.DEFAULT, res.seed);
|
|
||||||
assertEquals(time, res.simulationTime, maxErr);
|
|
||||||
testNode(res.getStat("Source"), 10000, time, 1.0, 4.5, 0.0, 0.0);
|
|
||||||
testNode(res.getStat("Queue"), 10000, time, 2.6, 7.2, 4.0, 0.0);
|
|
||||||
testNode(res.getStat("Queue Wait"), 10000, time, 5.8, 22.3, 19.1, 8497.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadExample3() throws KryoException, IOException {
|
|
||||||
var sim = new Simulation(Net.load(netFile3), new Rng());
|
|
||||||
var res = sim.run();
|
|
||||||
var time = 6736.0;
|
var time = 6736.0;
|
||||||
var maxErr = time / 1000.0;
|
var maxErr = time / 1000.0;
|
||||||
|
|
||||||
@@ -153,33 +106,26 @@ public class TestSaveExamplesNet {
|
|||||||
@Test
|
@Test
|
||||||
@AfterAll
|
@AfterAll
|
||||||
public void multiSimulation1() throws Exception {
|
public void multiSimulation1() throws Exception {
|
||||||
|
try (var newOut = new PrintStream(OutputStream.nullOutputStream())) {
|
||||||
new SimulationBuilder(net1)
|
new SimulationBuilder(net1)
|
||||||
.setCsv(csv1)
|
.setCsv(csv1)
|
||||||
.setMaxRuns(1000)
|
.setMaxRuns(1000)
|
||||||
.setSeed(2007539552L)
|
.setSeed(2007539552L)
|
||||||
.setParallel(true)
|
.setParallel(true)
|
||||||
.run();
|
.run(newOut);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@AfterAll
|
@AfterAll
|
||||||
public void multiSimulation2() throws Exception {
|
public void multiSimulation2() throws Exception {
|
||||||
|
try (var newOut = new PrintStream(OutputStream.nullOutputStream())) {
|
||||||
new SimulationBuilder(net2)
|
new SimulationBuilder(net2)
|
||||||
.setCsv(csv2)
|
.setCsv(csv2)
|
||||||
.setMaxRuns(1000)
|
.setMaxRuns(1000)
|
||||||
.setSeed(2007539552L)
|
.setSeed(2007539552L)
|
||||||
.setParallel(true)
|
.setParallel(true)
|
||||||
.run();
|
.run(newOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@AfterAll
|
|
||||||
public void multiSimulation3() throws Exception {
|
|
||||||
new SimulationBuilder(net3)
|
|
||||||
.setCsv(csv3)
|
|
||||||
.setMaxRuns(1000)
|
|
||||||
.setSeed(2007539552L)
|
|
||||||
.setParallel(true)
|
|
||||||
.run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public class TestSimulation {
|
|||||||
private static final ServerNode node0;
|
private static final ServerNode node0;
|
||||||
private static final ServerNode node1;
|
private static final ServerNode node1;
|
||||||
static {
|
static {
|
||||||
node0 = ServerNode.Builder.sourceLimited("First", 0, const1);
|
node0 = ServerNode.Builder.terminal("First", 0, const1);
|
||||||
node1 = ServerNode.Builder.queue("Second", 1, const1);
|
node1 = ServerNode.Builder.queue("Second", 1, const1);
|
||||||
|
|
||||||
simpleNet = new Net();
|
simpleNet = new Net();
|
||||||
@@ -56,10 +57,10 @@ public class TestSimulation {
|
|||||||
node = ServerNode.Builder.source("Source", const1);
|
node = ServerNode.Builder.source("Source", const1);
|
||||||
assertEquals("Source", node.name);
|
assertEquals("Source", node.name);
|
||||||
assertEquals(1, node.maxServers);
|
assertEquals(1, node.maxServers);
|
||||||
assertEquals(Integer.MAX_VALUE, node.spawnArrivals);
|
assertEquals(-1, node.spawnArrivals);
|
||||||
assertEquals(1.0, node.getServiceTime(null), DELTA);
|
assertEquals(1.0, node.getServiceTime(null), DELTA);
|
||||||
|
|
||||||
node = ServerNode.Builder.sourceLimited("Source", 50, const1);
|
node = ServerNode.Builder.terminal("Source", 50, const1);
|
||||||
assertEquals("Source", node.name);
|
assertEquals("Source", node.name);
|
||||||
assertEquals(1, node.maxServers);
|
assertEquals(1, node.maxServers);
|
||||||
assertEquals(50, node.spawnArrivals);
|
assertEquals(50, node.spawnArrivals);
|
||||||
@@ -196,7 +197,7 @@ public class TestSimulation {
|
|||||||
@Test
|
@Test
|
||||||
public void nodeStatsUpdates() {
|
public void nodeStatsUpdates() {
|
||||||
var net = new Net();
|
var net = new Net();
|
||||||
net.addNode(ServerNode.Builder.sourceLimited("Source", 50, const1));
|
net.addNode(ServerNode.Builder.terminal("Source", 50, const1));
|
||||||
net.addNode(node1);
|
net.addNode(node1);
|
||||||
net.addConnection(0, 1, 1.0);
|
net.addConnection(0, 1, 1.0);
|
||||||
|
|
||||||
@@ -393,13 +394,23 @@ public class TestSimulation {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simulation() {
|
public void simulation() {
|
||||||
var start = System.nanoTime();
|
assertThrows(NullPointerException.class, () -> new Simulation(null, rigged));
|
||||||
|
assertThrows(NullPointerException.class, () -> new Simulation(simpleNet, null));
|
||||||
|
|
||||||
var sim = new Simulation(simpleNet, rigged);
|
var sim = new Simulation(simpleNet, rigged);
|
||||||
|
assertTrue(sim.hasEnded());
|
||||||
|
assertEquals(0, sim.getEventsProcessed());
|
||||||
|
assertEquals(0.0, sim.getTime(), DELTA);
|
||||||
|
var fel = sim.getFutureEventList();
|
||||||
|
assertEquals(0, fel.size());
|
||||||
|
|
||||||
|
var start = System.nanoTime();
|
||||||
|
sim = new Simulation(simpleNet, rigged);
|
||||||
// knowing that it takes time to allocate the object
|
// knowing that it takes time to allocate the object
|
||||||
// we can use the average time
|
// we can use the average time
|
||||||
var endAllocation = System.nanoTime();
|
var endAllocation = System.nanoTime();
|
||||||
var time = (endAllocation + start) / 2;
|
var time = (endAllocation + start) / 2;
|
||||||
var diff = 0.5e-6 * (endAllocation - start); // getting the error margin in ms
|
var diff = 1e-6 * (endAllocation - start); // getting the error margin in ms
|
||||||
|
|
||||||
assertTrue(sim.hasEnded());
|
assertTrue(sim.hasEnded());
|
||||||
assertEquals(0, sim.getEventsProcessed());
|
assertEquals(0, sim.getEventsProcessed());
|
||||||
@@ -410,7 +421,7 @@ public class TestSimulation {
|
|||||||
assertEquals(0, sim.getNodeState(node0.name).numServerUnavailable);
|
assertEquals(0, sim.getNodeState(node0.name).numServerUnavailable);
|
||||||
assertEquals(0, sim.getNodeState(node1.name).numServerBusy);
|
assertEquals(0, sim.getNodeState(node1.name).numServerBusy);
|
||||||
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
|
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
|
||||||
var fel = sim.getFutureEventList();
|
fel = sim.getFutureEventList();
|
||||||
assertEquals(0, fel.size());
|
assertEquals(0, fel.size());
|
||||||
|
|
||||||
sim.addToFel(Event.newArrival(0, sim.getTime()));
|
sim.addToFel(Event.newArrival(0, sim.getTime()));
|
||||||
@@ -489,6 +500,8 @@ public class TestSimulation {
|
|||||||
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
|
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
|
||||||
fel = sim.getFutureEventList();
|
fel = sim.getFutureEventList();
|
||||||
assertEquals(0, fel.size());
|
assertEquals(0, fel.size());
|
||||||
|
final var s = sim;
|
||||||
|
assertThrows(NullPointerException.class, () -> s.processNextEvent());
|
||||||
|
|
||||||
var elapsed = (double) (System.nanoTime() - time);
|
var elapsed = (double) (System.nanoTime() - time);
|
||||||
var result = sim.endSimulation();
|
var result = sim.endSimulation();
|
||||||
@@ -529,7 +542,7 @@ public class TestSimulation {
|
|||||||
@Test
|
@Test
|
||||||
public void simulationStats() {
|
public void simulationStats() {
|
||||||
var net = new Net();
|
var net = new Net();
|
||||||
net.addNode(ServerNode.Builder.sourceLimited("Source", 50, const1));
|
net.addNode(ServerNode.Builder.terminal("Source", 50, const1));
|
||||||
|
|
||||||
var sim = new Simulation(net, rigged);
|
var sim = new Simulation(net, rigged);
|
||||||
var result = sim.run();
|
var result = sim.run();
|
||||||
@@ -581,7 +594,7 @@ public class TestSimulation {
|
|||||||
@Test
|
@Test
|
||||||
public void simulationDrop() {
|
public void simulationDrop() {
|
||||||
var net = new Net();
|
var net = new Net();
|
||||||
net.addNode(ServerNode.Builder.sourceLimited("Source", 50, const1));
|
net.addNode(ServerNode.Builder.terminal("Source", 50, const1));
|
||||||
net.addNode(new ServerNode.Builder("Queue", _ -> 2.0).queue(20).build());
|
net.addNode(new ServerNode.Builder("Queue", _ -> 2.0).queue(20).build());
|
||||||
net.addConnection(0, 1, 1.0);
|
net.addConnection(0, 1, 1.0);
|
||||||
|
|
||||||
|
|||||||
|
Can't render this file because it is too large.
|
Reference in New Issue
Block a user