Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68687fa595 | ||
|
|
9b51562469 | ||
| e11bb3f419 | |||
| 2ed8149110 | |||
| f4d3262cb7 | |||
| 60ef40c0ab | |||
| 6cb87be89b | |||
| cd3ff3b699 | |||
| 1687154c62 | |||
| 76adc91a43 | |||
| 744df75d41 | |||
| 8fe2f9d781 | |||
| 611e14e6fa | |||
| 121d4cd44a | |||
| c2b9d350aa | |||
| 1cab669fd9 | |||
| 64e135810a | |||
| fbd1fab9fb | |||
| e97703e23a | |||
| 3a769d6ae3 | |||
| 6b3b5fea2f | |||
| f0d9c59350 | |||
| 65e8f378f5 | |||
| cb8b2d2913 | |||
| 2331e42e55 | |||
| d7bf804313 | |||
| 762a1d1d0a | |||
| f5d70140d7 |
23
.vscode/launch.json
vendored
23
.vscode/launch.json
vendored
@@ -9,56 +9,49 @@
|
||||
"name": "Run1k Simple",
|
||||
"request": "launch",
|
||||
"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",
|
||||
"name": "Run1k Complex",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "simulation -net 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"
|
||||
"args": "simulation -net src/test/resources/example2.net -runs 1000 -p"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Run Incremental",
|
||||
"request": "launch",
|
||||
"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",
|
||||
"name": "Run10",
|
||||
"request": "launch",
|
||||
"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",
|
||||
"name": "Plot Simple",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "plot -csv example1.csv"
|
||||
"args": "plot -csv src/test/resources/example1.csv"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Plot Complex",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "plot -csv example2.csv"
|
||||
"args": "plot -csv src/test/resources/example2.csv"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Build Net",
|
||||
"name": "Interactive Net Builder",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "net"
|
||||
"args": "interactive"
|
||||
},
|
||||
]
|
||||
}
|
||||
54
README.md
54
README.md
@@ -5,15 +5,18 @@ Il simulatore è iniziato con il lavoro trovato sul libro di testo [Discrete-Eve
|
||||
|
||||
Il risultato è la creazione in una libreria per la simulazione di eventi discreti nella quale si può scegliera la topologia e la quantità di nodi nella rete da simulare.
|
||||
|
||||
Questa libreria è stata confrontata con il tool [JMT](https://jmt.sourceforge.net/Download.html) e le reti usate per fare il confronto si possono trovare sotto [le risorse del main](https://github.com/Berack96/upo-valpre/tree/main/src/main/resources).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Il JAR risultante che si trova nelle [Releases](https://github.com/Berack96/upo-valpre/releases).\
|
||||
> La versione di Java usata è la 23 (precisamente la [23.0.1](https://www.oracle.com/java/technologies/javase/jdk23-archive-downloads.html)).
|
||||
|
||||
---
|
||||
### Comandi Jar
|
||||
|
||||
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.\
|
||||
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]`\
|
||||
@@ -21,33 +24,64 @@ Usato per avviare una simulazione della rete. Nel caso la rete non abbia eventua
|
||||
Esistono vari tipi di argomenti per scegliere come fare la simulazione:
|
||||
* `-runs <N>` per fare la simulazione N volte
|
||||
* `-seed <value>` per dare un seed iniziale scelto
|
||||
* `-i <confidences>` per scegliere gli indici di terminazione delle run di simulazione quando l'intervallo di confidenza associato è raggiunto. Viene ignorato il comando -p se questa opzione è attiva. Il formato da usare è\
|
||||
**\[nodo:statistica=confidenza:errore%\];\[..\]**
|
||||
* `-csv <file>` per salvare i risultati delle run in un file csv
|
||||
* `-p` per fare le simulazioni in parallelo (ovvero su più thread)
|
||||
* `-end <criteria>` per scegliere quando la simulazione finisce nel caso non ci siano dei source limitati nella creazione di arrivi. La tipologia di fine simulazione la si può trovare dentro `EndCriteria` (ovvero MaxArrivals, MaxDepartures, MaxTime) e la formattazione da usare per passare il parametro è la seguente:\
|
||||
**\[Tipo1:param1,..,paramN\];\[..\];\[TipoN:param1,..,paramN\]**
|
||||
**\[tipo:param1,..,paramN\];\[..\]**
|
||||
* `java -jar upo-valpre.jar plot -csv <file>`\
|
||||
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
|
||||
|
||||
Esistono molteplici classi interne che vengono usate per supportare la simulazione e/o mostrare i risultati. In generale le classi dentro il percorso [net.berack.upo.valpre](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre) sono usate per l'utilizzo del jar e quindi non sono essenziali per la simulazione.
|
||||
Esistono molteplici classi interne che vengono usate per supportare la simulazione e/o mostrare i risultati. In generale le classi dentro il percorso [net.berack.upo.valpre](src/main/java/net/berack/upo/valpre) sono usate per l'utilizzo del jar e quindi non sono essenziali per la simulazione.
|
||||
I percorsi che invece sono direttamente responsabili per la simulazione sono:
|
||||
- [net.berack.upo.valpre.rand](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre/rand) All'interno del quale si possono trovare:
|
||||
- [net.berack.upo.valpre.rand](src/main/java/net/berack/upo/valpre/rand) All'interno del quale si possono trovare:
|
||||
- **Rng** che viene usato per il calcolo di numeri pseudo-casuali tramite un seed iniziale e la generazione di molteplici stream di generazione di numeri casuali
|
||||
- **Distribution** interfaccia usata per la generazione di un numero casuale di una distribuzione. In questo file esistono molteplici classi interne che implementano l'interfaccia; per esempio: Exponential, Normal, Uniform
|
||||
- [net.berack.upo.valpre.sim](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre/sim) Package che contiene tutte le parti utili alla simulazione; per esempio la creazione della rete o la simulazione si più thread:
|
||||
- [net.berack.upo.valpre.sim](src/main/java/net/berack/upo/valpre/sim) Package che contiene tutte le parti utili alla simulazione; per esempio la creazione della rete o la simulazione si più thread:
|
||||
- **Net** che viene usato per rappresentare una rete da simulare.
|
||||
- **ServerNode** che viene usato per rappresentare un singolo nodo della rete.
|
||||
- **Event** che viene usato per rappresentare un evento della simulazione.
|
||||
- **EndCriteria** interfaccia che viene implementata dalle classi interne usata per controllare se e quando la simulazione debba finire.
|
||||
- **Simulation** e **SimulationMultiple** che vengono usate per far partire la simulazione; la versione multiple serve ad organizzare molteplici simulazioni su più thread o su un singolo core.
|
||||
- [net.berack.upo.valpre.sim.stats](https://github.com/Berack96/upo-valpre/tree/main/src/main/java/net/berack/upo/valpre/sim/stats) Package che contiene tutte le classi utili per la raccolta e l'analisi statistica dei vari valori generati dalla simulazione:
|
||||
- [net.berack.upo.valpre.sim.stats](src/main/java/net/berack/upo/valpre/sim/stats) Package che contiene tutte le classi utili per la raccolta e l'analisi statistica dei vari valori generati dalla simulazione:
|
||||
- **Result** il risultato di una run e la sua classe interna **Result.Summary** che contiene molteplici risultati di run già analizzati.
|
||||
- **NodeStats** contiene indici statistici di un nodo e la sua classe interna **NodeStats.Summary** che contiene molteplici indici statistici già analizzati.
|
||||
- **ConsoleTable** utile per mostrare i risultati in console sottoforma di tabella
|
||||
- **CsvResult** utile per la lettura/scrittura dei risultati in formato csv
|
||||
|
||||
---
|
||||
### Esempi
|
||||
|
||||
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 interactive`
|
||||
|
||||
##### 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).\
|
||||
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:\
|
||||
Queue Response Time = 7.3022 con un range [7.1456, 7.4589]\
|
||||
Queue Throughput = 0.2226 con un range [0.2182, 0.2271]\
|
||||
Queue Utilization = 0.7111 con un range [0.6959, 0.7262]
|
||||
|
||||
##### Secondo esempio
|
||||
\
|
||||
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 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:\
|
||||
Service1 Response Time ~ 1.9866\
|
||||
Busy2 Response Time ~ 0.2825\
|
||||
Queue2 Response Time ~ 0.2279\
|
||||
Service1 Utilization ~ 0.7488\
|
||||
Calibration Number of Customers ~ 0.0150\
|
||||
Busy2 Number of Customers ~ 0.4279\
|
||||
Throughput ~ 1.5000
|
||||
|
||||
BIN
image/README/1741860064265.png
Normal file
BIN
image/README/1741860064265.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
image/README/1741862185715.png
Normal file
BIN
image/README/1741862185715.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
image/README/1741862486547.png
Normal file
BIN
image/README/1741862486547.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
image/README/1741862746304.png
Normal file
BIN
image/README/1741862746304.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 902 B |
BIN
image/README/1741863043733.png
Normal file
BIN
image/README/1741863043733.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
24
pom.xml
24
pom.xml
@@ -13,30 +13,6 @@
|
||||
<maven.compiler.target>23</maven.compiler.target>
|
||||
</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>
|
||||
<dependency>
|
||||
<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);
|
||||
plot.show();
|
||||
}
|
||||
case "net" -> {
|
||||
var net = new NetBuilderInteractive();
|
||||
net.run();
|
||||
}
|
||||
case "interactive" -> new InteractiveConsole().run();
|
||||
default -> exit("Invalid program!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -86,11 +83,11 @@ public class Main {
|
||||
var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
|
||||
var name = new File(uri).getName();
|
||||
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>]"
|
||||
+ "[-p] [-end <end>] [-i <indices>]");
|
||||
System.out.println("plot args: -csv <csv>");
|
||||
System.out.println("net args: none");
|
||||
System.out.println("interactive: no args needed");
|
||||
System.exit(1);
|
||||
} catch (URISyntaxException e) {
|
||||
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;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -194,23 +191,4 @@ public class Parameters {
|
||||
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
|
||||
*/
|
||||
public Plot(String csv) throws IOException {
|
||||
var stream = Parameters.getFileOrExample(csv);
|
||||
if (stream == null)
|
||||
throw new IllegalArgumentException("CSV file needed!");
|
||||
var results = CsvResult.loadResults(stream);
|
||||
stream.close();
|
||||
|
||||
var results = new CsvResult(csv).loadResults();
|
||||
this.summary = new Result.Summary(results);
|
||||
|
||||
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.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
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.SimulationMultiple;
|
||||
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
|
||||
@@ -35,10 +37,8 @@ public class SimulationBuilder {
|
||||
*/
|
||||
public SimulationBuilder(String netFile) throws IOException {
|
||||
try {
|
||||
var file = Parameters.getFileOrExample(netFile);
|
||||
this.net = Net.load(file);
|
||||
this.net = Net.load(netFile);
|
||||
this.confidences = new ConfidenceIndices(this.net);
|
||||
file.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IllegalArgumentException("Net file needed!");
|
||||
} catch (KryoException e) {
|
||||
@@ -230,23 +230,38 @@ public class SimulationBuilder {
|
||||
* @throws ExecutionException If the simulation has an error.
|
||||
* @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 sim = new SimulationMultiple(this.net);
|
||||
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 Normal -> sim.run(this.seed, this.runs, this.endCriteria);
|
||||
};
|
||||
nano = System.nanoTime() - nano;
|
||||
|
||||
System.out.print(summary);
|
||||
System.out.println("Final time " + nano / 1e6 + "ms");
|
||||
out.print(summary);
|
||||
out.println("Final time " + nano / 1e6 + "ms");
|
||||
|
||||
if (csv != null) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -23,11 +23,7 @@ public class Event implements Comparable<Event> {
|
||||
|
||||
@Override
|
||||
public int compareTo(Event other) {
|
||||
if (this.time < other.time)
|
||||
return -1;
|
||||
if (this.time == other.time)
|
||||
return 0;
|
||||
return 1;
|
||||
return Double.compare(this.time, other.time);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,7 +75,6 @@ public final class Net implements Iterable<ServerNode> {
|
||||
* @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
|
||||
* @throws IllegalArgumentException if the child is a source node
|
||||
*/
|
||||
public void addConnection(int parent, int child, double weight) {
|
||||
if (weight <= 0)
|
||||
@@ -85,9 +84,6 @@ public final class Net implements Iterable<ServerNode> {
|
||||
if (parent < 0 || child < 0 || parent > max || child > max)
|
||||
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);
|
||||
list.removeIf(conn -> conn.index == child);
|
||||
list.add(new Connection(child, weight));
|
||||
@@ -170,14 +166,13 @@ public final class Net implements Iterable<ServerNode> {
|
||||
for (var conn : list)
|
||||
sum += conn.weight;
|
||||
|
||||
var newOne = new Connection[list.size()];
|
||||
for (var i = 0; i < list.size(); i++) {
|
||||
var conn = list.get(i);
|
||||
var newOne = new ArrayList<Connection>();
|
||||
for (var conn : list) {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -18,7 +18,7 @@ public class ServerNode {
|
||||
/**
|
||||
* 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 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
|
||||
* will be put to the servers number.
|
||||
* The service distribution can't be null, otherwise an exception is thrown.
|
||||
@@ -37,7 +37,7 @@ public class ServerNode {
|
||||
if (servers <= 0)
|
||||
servers = 1;
|
||||
if (spawn < 0)
|
||||
spawn = 0;
|
||||
spawn = -1;
|
||||
if (queue < servers)
|
||||
queue = servers;
|
||||
|
||||
@@ -71,6 +71,28 @@ public class ServerNode {
|
||||
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
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ServerNode))
|
||||
@@ -183,26 +205,27 @@ public class ServerNode {
|
||||
|
||||
/**
|
||||
* 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 distribution The distribution of the inter-arrival times.
|
||||
* @return The created source node.
|
||||
*/
|
||||
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.
|
||||
* Once it has finished spawning the arrivals, it will not spawn anymore.
|
||||
*
|
||||
* @param name The name of the node.
|
||||
* @param service The distribution of the inter-arrival times.
|
||||
* @param spawnArrivals The number of arrivals to spawn.
|
||||
* @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();
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ public class ServerNodeState {
|
||||
* @return True if the node should spawn an arrival, false otherwise.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* as input
|
||||
@@ -168,12 +185,7 @@ public class ServerNodeState {
|
||||
* otherwise
|
||||
*/
|
||||
public Event spawnArrivalToChild(double time, Rng rng) {
|
||||
var random = rng.random();
|
||||
for (var child : this.children) {
|
||||
random -= child.weight;
|
||||
if (random <= 0)
|
||||
return Event.newArrival(child.index, time);
|
||||
}
|
||||
return null;
|
||||
var child = this.getRandomChild(rng);
|
||||
return child > -1 ? Event.newArrival(child, time) : null;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.berack.upo.valpre.sim;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
@@ -98,8 +97,13 @@ public final class Simulation {
|
||||
case DEPARTURE -> {
|
||||
state.updateDeparture(time);
|
||||
|
||||
// Spawn unavailability if has unavailable time
|
||||
this.addToFel(state.spawnUnavailableIfPossible(time, this.rng));
|
||||
|
||||
// Spawn departure if has requests and server is available
|
||||
this.addToFel(state.spawnDepartureIfPossible(time, this.rng));
|
||||
|
||||
// Spawn arrival to self if is source node
|
||||
this.addToFel(state.spawnArrivalIfPossilbe(time));
|
||||
|
||||
// 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.
|
||||
*/
|
||||
public List<Event> getFutureEventList() {
|
||||
return new ArrayList<>(this.fel);
|
||||
return List.copyOf(this.fel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.berack.upo.valpre.sim;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
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
|
||||
* simulation runs will stop when the relative error of the confidence index is
|
||||
* 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 criterias The criteria to determine when to end the simulation. If
|
||||
* null then the simulation will run until there are no more
|
||||
* events.
|
||||
* @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
|
||||
* null then the simulation will run until there are no more
|
||||
* events.
|
||||
* @return The statistics the network.
|
||||
* @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)
|
||||
throw new IllegalArgumentException("Confidence must be not null");
|
||||
|
||||
@@ -116,7 +122,8 @@ public class SimulationMultiple {
|
||||
var results = new Result.Summary(rng.getSeed(), nodes);
|
||||
var output = new StringBuilder();
|
||||
var stop = false;
|
||||
for (int i = 0; !stop; i++) {
|
||||
|
||||
for (int i = 0; !stop && runs > i; i++) {
|
||||
var sim = new Simulation(this.net, rng, criterias);
|
||||
var result = sim.run();
|
||||
results.add(result);
|
||||
@@ -132,11 +139,11 @@ public class SimulationMultiple {
|
||||
var oneSting = String.join("], [", errString);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
146
src/main/resources/example1.jsimg
Normal file
146
src/main/resources/example1.jsimg
Normal file
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
|
||||
<archive xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="example1.jsimg" timestamp="Thu Mar 13 11:14:34 CET 2025" xsi:noNamespaceSchemaLocation="Archive.xsd">
|
||||
<sim disableStatisticStop="false" logDecimalSeparator="," logDelimiter=";" logPath="C:\Users\giaco\JMT\" logReplaceMode="0" maxEvents="-1" maxSamples="1000000" name="example1.jsimg" polling="1.0" xsi:noNamespaceSchemaLocation="SIMmodeldefinition.xsd">
|
||||
<userClass name="Class1" priority="0" referenceSource="Source " softDeadline="0.0" type="open"/>
|
||||
<node name="Source ">
|
||||
<section className="RandomSource">
|
||||
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="ServiceStrategy">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
|
||||
<subParameter classPath="jmt.engine.random.Exponential" name="Exponential"/>
|
||||
<subParameter classPath="jmt.engine.random.ExponentialPar" name="distrPar">
|
||||
<subParameter classPath="java.lang.Double" name="lambda">
|
||||
<value>0.2222222222222222</value>
|
||||
</subParameter>
|
||||
</subParameter>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
</section>
|
||||
<section className="ServiceTunnel"/>
|
||||
<section className="Router">
|
||||
<parameter array="true" classPath="jmt.engine.NetStrategies.RoutingStrategy" name="RoutingStrategy">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
|
||||
</parameter>
|
||||
</section>
|
||||
</node>
|
||||
<node name="Sink">
|
||||
<section className="JobSink"/>
|
||||
</node>
|
||||
<node name="Queue">
|
||||
<classSoftDeadlines>
|
||||
<softDeadline>0.0</softDeadline>
|
||||
</classSoftDeadlines>
|
||||
<quantumSize>
|
||||
<quantaSize>0.0</quantaSize>
|
||||
</quantumSize>
|
||||
<quantumSwitchoverTime>
|
||||
<quantumSwitchoverTime>0.0</quantumSwitchoverTime>
|
||||
</quantumSwitchoverTime>
|
||||
<section className="Queue">
|
||||
<parameter classPath="java.lang.Integer" name="size">
|
||||
<value>-1</value>
|
||||
</parameter>
|
||||
<parameter array="true" classPath="java.lang.String" name="dropStrategies">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="java.lang.String" name="dropStrategy">
|
||||
<value>drop</value>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
<parameter classPath="jmt.engine.NetStrategies.QueueGetStrategies.FCFSstrategy" name="FCFSstrategy"/>
|
||||
<parameter array="true" classPath="jmt.engine.NetStrategies.QueuePutStrategy" name="QueuePutStrategy">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="jmt.engine.NetStrategies.QueuePutStrategies.TailStrategy" name="TailStrategy"/>
|
||||
</parameter>
|
||||
</section>
|
||||
<section className="Server">
|
||||
<parameter classPath="java.lang.Integer" name="maxJobs">
|
||||
<value>1</value>
|
||||
</parameter>
|
||||
<parameter array="true" classPath="java.lang.Integer" name="numberOfVisits">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="java.lang.Integer" name="numberOfVisits">
|
||||
<value>1</value>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
<parameter array="true" classPath="jmt.engine.NetStrategies.ServiceStrategy" name="ServiceStrategy">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy" name="ServiceTimeStrategy">
|
||||
<subParameter classPath="jmt.engine.random.Normal" name="Normal"/>
|
||||
<subParameter classPath="jmt.engine.random.NormalPar" name="distrPar">
|
||||
<subParameter classPath="java.lang.Double" name="mean">
|
||||
<value>3.2</value>
|
||||
</subParameter>
|
||||
<subParameter classPath="java.lang.Double" name="standardDeviation">
|
||||
<value>0.6000000000000001</value>
|
||||
</subParameter>
|
||||
</subParameter>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
<parameter array="true" classPath="java.lang.Integer" name="classParallelism">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="java.lang.Integer" name="serverParallelism">
|
||||
<value>1</value>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
<parameter array="true" classPath="java.lang.String" name="serverNames">
|
||||
<subParameter classPath="java.lang.String" name="serverTypesNames">
|
||||
<value>Queue - Server Type 1</value>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
<parameter array="true" classPath="java.lang.Integer" name="serversPerServerType">
|
||||
<subParameter classPath="java.lang.Integer" name="serverTypesNumOfServers">
|
||||
<value>1</value>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
<parameter array="true" classPath="java.lang.Object" name="serverCompatibilities">
|
||||
<subParameter array="true" classPath="java.lang.Boolean" name="serverTypesCompatibilities">
|
||||
<subParameter classPath="java.lang.Boolean" name="compatibilities">
|
||||
<value>true</value>
|
||||
</subParameter>
|
||||
</subParameter>
|
||||
</parameter>
|
||||
<parameter classPath="java.lang.String" name="schedulingPolicy">
|
||||
<value>ALIS (Assign Longest Idle Server)</value>
|
||||
</parameter>
|
||||
</section>
|
||||
<section className="Router">
|
||||
<parameter array="true" classPath="jmt.engine.NetStrategies.RoutingStrategy" name="RoutingStrategy">
|
||||
<refClass>Class1</refClass>
|
||||
<subParameter classPath="jmt.engine.NetStrategies.RoutingStrategies.RandomStrategy" name="Random"/>
|
||||
</parameter>
|
||||
</section>
|
||||
</node>
|
||||
<measure alpha="0.01" name="Queue_Class1_Response Time" nodeType="station" precision="0.03" referenceNode="Queue" referenceUserClass="Class1" type="Response Time" verbose="false"/>
|
||||
<measure alpha="0.01" name="Queue_Class1_Utilization" nodeType="station" precision="0.03" referenceNode="Queue" referenceUserClass="Class1" type="Utilization" verbose="false"/>
|
||||
<measure alpha="0.01" name="Queue_Class1_Throughput" nodeType="station" precision="0.03" referenceNode="Queue" referenceUserClass="Class1" type="Throughput" verbose="false"/>
|
||||
<connection source="Source " target="Queue"/>
|
||||
<connection source="Queue" target="Sink"/>
|
||||
</sim>
|
||||
<jmodel xsi:noNamespaceSchemaLocation="JModelGUI.xsd">
|
||||
<userClass color="#FF0000FF" name="Class1"/>
|
||||
<station name="Source ">
|
||||
<position angle="0.0" rotate="false" x="73.0" y="139.0"/>
|
||||
</station>
|
||||
<station name="Sink">
|
||||
<position angle="0.0" rotate="false" x="354.0" y="139.0"/>
|
||||
</station>
|
||||
<station name="Queue">
|
||||
<position angle="0.0" rotate="false" x="191.0" y="136.0"/>
|
||||
</station>
|
||||
</jmodel>
|
||||
<results elapsedTime="932" logDecimalSeparator="," logDelimiter=";" pollingInterval="1.0" xsi:noNamespaceSchemaLocation="Results.xsd">
|
||||
<measure alpha="0.99" analyzedSamples="245760" discardedSamples="390" finalValue="7.30224421202774" name="Queue_Class1_Response Time" nodeType="station" precision="0.03" referenceClass="Class1" referenceStation="Queue" state="1" type="2">
|
||||
<sample lastIntervalAvgValue="7.171346385053164" lowerBound="6.925342616994286" meanValue="7.2654952908722095" simulationTime="349360.1710046531" upperBound="7.6056479647501325"/>
|
||||
<sample lastIntervalAvgValue="7.340886944129393" lowerBound="7.145587638668616" meanValue="7.30224421202774" simulationTime="1105542.162869484" upperBound="7.458900785386865"/>
|
||||
</measure>
|
||||
<measure alpha="0.99" analyzedSamples="56320" discardedSamples="1755" finalValue="0.7110892727677632" name="Queue_Class1_Utilization" nodeType="station" precision="0.03" referenceClass="Class1" referenceStation="Queue" state="1" type="6">
|
||||
<sample lastIntervalAvgValue="0.7121434625959978" lowerBound="0.6959357730746889" meanValue="0.7110892727677632" simulationTime="202809.54195132584" upperBound="0.7262427724608376"/>
|
||||
<sample lastIntervalAvgValue="0.7121434625959978" lowerBound="0.6959357730746889" meanValue="0.7110892727677632" simulationTime="202809.54195132584" upperBound="0.7262427724608376"/>
|
||||
</measure>
|
||||
<measure alpha="0.99" analyzedSamples="51200" discardedSamples="180" finalValue="0.22256617058473832" name="Queue_Class1_Throughput" nodeType="station" precision="0.03" referenceClass="Class1" referenceStation="Queue" state="1" type="5">
|
||||
<sample lastIntervalAvgValue="0.22277397312289457" lowerBound="0.21822871906729738" meanValue="0.22256617058473832" simulationTime="230637.3553415772" upperBound="0.22707953845333897"/>
|
||||
<sample lastIntervalAvgValue="0.22277397312289457" lowerBound="0.21822871906729738" meanValue="0.22256617058473832" simulationTime="230637.3553415772" upperBound="0.22707953845333897"/>
|
||||
</measure>
|
||||
</results>
|
||||
</archive>
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
1000
src/main/resources/example3.jsimg
Normal file
1000
src/main/resources/example3.jsimg
Normal file
File diff suppressed because it is too large
Load Diff
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 java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.HashSet;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
@@ -11,61 +13,26 @@ import org.junit.jupiter.api.BeforeAll;
|
||||
|
||||
import com.esotericsoftware.kryo.KryoException;
|
||||
|
||||
import net.berack.upo.valpre.NetExamples;
|
||||
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.sim.stats.CsvResult;
|
||||
import net.berack.upo.valpre.sim.stats.NodeStats;
|
||||
|
||||
public class TestSaveExamplesNet {
|
||||
|
||||
private static final Distribution exp0_22 = new Distribution.Exponential(1.0 / 4.5);
|
||||
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 path = "src/test/resources/example%d.%s";
|
||||
private static final String netFile1 = path.formatted(1, "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 csv2 = path.formatted(2, "csv");
|
||||
private static final String csv3 = path.formatted(3, "csv");
|
||||
|
||||
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);
|
||||
}
|
||||
private static final Net net1 = NetExamples.getNet1();
|
||||
private static final Net net2 = NetExamples.getNet2();
|
||||
|
||||
@BeforeAll
|
||||
public void saveAll() throws IOException {
|
||||
net1.save(netFile1);
|
||||
net2.save(netFile2);
|
||||
net3.save(netFile3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -85,20 +52,6 @@ public class TestSaveExamplesNet {
|
||||
public void loadExample2() throws KryoException, IOException {
|
||||
var sim = new Simulation(Net.load(netFile2), new Rng());
|
||||
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 maxErr = time / 1000.0;
|
||||
|
||||
@@ -153,33 +106,26 @@ public class TestSaveExamplesNet {
|
||||
@Test
|
||||
@AfterAll
|
||||
public void multiSimulation1() throws Exception {
|
||||
new SimulationBuilder(net1)
|
||||
.setCsv(csv1)
|
||||
.setMaxRuns(1000)
|
||||
.setSeed(2007539552L)
|
||||
.setParallel(true)
|
||||
.run();
|
||||
try (var newOut = new PrintStream(OutputStream.nullOutputStream())) {
|
||||
new SimulationBuilder(net1)
|
||||
.setCsv(csv1)
|
||||
.setMaxRuns(1000)
|
||||
.setSeed(2007539552L)
|
||||
.setParallel(true)
|
||||
.run(newOut);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@AfterAll
|
||||
public void multiSimulation2() throws Exception {
|
||||
new SimulationBuilder(net2)
|
||||
.setCsv(csv2)
|
||||
.setMaxRuns(1000)
|
||||
.setSeed(2007539552L)
|
||||
.setParallel(true)
|
||||
.run();
|
||||
}
|
||||
|
||||
@Test
|
||||
@AfterAll
|
||||
public void multiSimulation3() throws Exception {
|
||||
new SimulationBuilder(net3)
|
||||
.setCsv(csv3)
|
||||
.setMaxRuns(1000)
|
||||
.setSeed(2007539552L)
|
||||
.setParallel(true)
|
||||
.run();
|
||||
try (var newOut = new PrintStream(OutputStream.nullOutputStream())) {
|
||||
new SimulationBuilder(net2)
|
||||
.setCsv(csv2)
|
||||
.setMaxRuns(1000)
|
||||
.setSeed(2007539552L)
|
||||
.setParallel(true)
|
||||
.run(newOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
@@ -23,7 +24,7 @@ public class TestSimulation {
|
||||
private static final ServerNode node0;
|
||||
private static final ServerNode node1;
|
||||
static {
|
||||
node0 = ServerNode.Builder.sourceLimited("First", 0, const1);
|
||||
node0 = ServerNode.Builder.terminal("First", 0, const1);
|
||||
node1 = ServerNode.Builder.queue("Second", 1, const1);
|
||||
|
||||
simpleNet = new Net();
|
||||
@@ -56,10 +57,10 @@ public class TestSimulation {
|
||||
node = ServerNode.Builder.source("Source", const1);
|
||||
assertEquals("Source", node.name);
|
||||
assertEquals(1, node.maxServers);
|
||||
assertEquals(Integer.MAX_VALUE, node.spawnArrivals);
|
||||
assertEquals(-1, node.spawnArrivals);
|
||||
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(1, node.maxServers);
|
||||
assertEquals(50, node.spawnArrivals);
|
||||
@@ -196,7 +197,7 @@ public class TestSimulation {
|
||||
@Test
|
||||
public void nodeStatsUpdates() {
|
||||
var net = new Net();
|
||||
net.addNode(ServerNode.Builder.sourceLimited("Source", 50, const1));
|
||||
net.addNode(ServerNode.Builder.terminal("Source", 50, const1));
|
||||
net.addNode(node1);
|
||||
net.addConnection(0, 1, 1.0);
|
||||
|
||||
@@ -393,13 +394,23 @@ public class TestSimulation {
|
||||
|
||||
@Test
|
||||
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);
|
||||
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
|
||||
// we can use the average time
|
||||
var endAllocation = System.nanoTime();
|
||||
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());
|
||||
assertEquals(0, sim.getEventsProcessed());
|
||||
@@ -410,7 +421,7 @@ public class TestSimulation {
|
||||
assertEquals(0, sim.getNodeState(node0.name).numServerUnavailable);
|
||||
assertEquals(0, sim.getNodeState(node1.name).numServerBusy);
|
||||
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
|
||||
var fel = sim.getFutureEventList();
|
||||
fel = sim.getFutureEventList();
|
||||
assertEquals(0, fel.size());
|
||||
|
||||
sim.addToFel(Event.newArrival(0, sim.getTime()));
|
||||
@@ -489,6 +500,8 @@ public class TestSimulation {
|
||||
assertEquals(0, sim.getNodeState(node1.name).numServerUnavailable);
|
||||
fel = sim.getFutureEventList();
|
||||
assertEquals(0, fel.size());
|
||||
final var s = sim;
|
||||
assertThrows(NullPointerException.class, () -> s.processNextEvent());
|
||||
|
||||
var elapsed = (double) (System.nanoTime() - time);
|
||||
var result = sim.endSimulation();
|
||||
@@ -529,7 +542,7 @@ public class TestSimulation {
|
||||
@Test
|
||||
public void simulationStats() {
|
||||
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 result = sim.run();
|
||||
@@ -581,7 +594,7 @@ public class TestSimulation {
|
||||
@Test
|
||||
public void simulationDrop() {
|
||||
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.addConnection(0, 1, 1.0);
|
||||
|
||||
|
||||
|
Can't render this file because it is too large.
|
Reference in New Issue
Block a user