- Refactor simulation handling and improve net iteration methods
This commit is contained in:
2025-02-03 18:55:31 +01:00
parent 2acb27a6b9
commit 2d5f43bbc9
9 changed files with 80 additions and 34 deletions

7
.vscode/launch.json vendored
View File

@@ -25,13 +25,6 @@
"mainClass": "net.berack.upo.valpre.Main",
"args": "simulation -net example1.net -runs 10"
},
{
"type": "java",
"name": "Run10",
"request": "launch",
"mainClass": "net.berack.upo.valpre.Main",
"args": "simulation -net example1.net -runs 10"
},
{
"type": "java",
"name": "Plot Simple",

View File

@@ -1,2 +1,49 @@
# upo-valpre
valutazione delle prestazioni (principalemnte Reti di code e di Petri)
# Valutazione delle Prestazioni
Il progetto riguarda un simulatore ad eventi discreti.
Il simulatore è iniziato con il lavoro trovato sul libro di testo [Discrete-Event System Simulation](https://www.pearson.com/en-us/subject-catalog/p/discrete-event-system-simulation/P200000003161/9780136062127) al Capitolo 4, per poi esser personalizzato e modificato radicalmente.
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.
Per fare ciò si può usare il JAR risultante che si trova nelle [Releases](https://github.com/Berack96/upo-valpre/releases).
### 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`\
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]`\
Usato per avviare una simulazione della rete. Nel caso la rete non abbia eventuali limiti nella generazione di arrivi, viene restituito un errore.
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
* `-csv <file>` per salvare i risultati delle run in un file csv
* `-p` per fare le simulazioni in parallelo (ovvero su più thread)
* `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:
![1738603552417](image/README/1738603552417.png)
Esistono dei file prefatti per vedere eventuali simulazioni:
* `example1.net` e `example2.net` per `simulation -net`
* `example1.csv` e `example2.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.
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:
- **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** 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 la simulazione debba finire.
- **Simulation** e **SimulationMultiple** che vengono usate per far partire la simulazione; la versione multiple serve ad organizzare molteplici simulazioni si 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:
- **Result** un singolo risultato e la sua controparte **ResultSummary** che contiene molteplici risultati già analizzati.
- **Statistics** un singolo valore di indici statistici di un nodo e la sua controparte **StatisticsSummary** che contiene molteplici risultati già analizzati.
- **ConsoleTable** utile per mostrare i risultati in console sottoforma di tabella
- **CsvResult** utile per la lettura/scrittura di risultati in formato csv

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -18,7 +18,7 @@ public class Main {
var param = Main.getParameters(program, subArgs);
switch (program) {
case "simulation" -> {
new Simulation(param.get("net"))
new SimulationBuilder(param.get("net"))
.setCsv(param.get("csv"))
.setRuns(param.getOrDefault("runs", Integer::parseInt, 100))
.setSeed(param.getOrDefault("seed", Long::parseLong, 2007539552L))

View File

@@ -142,7 +142,9 @@ public class Plot {
var columnKey = String.format("%.3f", columnVal);
dataset.addValue(frequency[i], "Frequency", columnKey);
}
this.panelBarChart.getChart().getCategoryPlot().setDataset(dataset);
var chart = this.panelBarChart.getChart();
chart.getCategoryPlot().setDataset(dataset);
chart.setTitle(stat + " distribution");
var model = this.statList.getModel();
for (int i = 0; i < model.getSize(); i++) {

View File

@@ -14,7 +14,7 @@ import net.berack.upo.valpre.sim.stats.CsvResult;
* This class is responsible for running the simulation. It parses the arguments
* and runs the simulation with the given parameters.
*/
public class Simulation {
public class SimulationBuilder {
private String csv;
private int runs;
private long seed;
@@ -28,7 +28,7 @@ public class Simulation {
* @param netFile the net file to load
* @throws IOException if the file has a problem
*/
public Simulation(String netFile) throws IOException {
public SimulationBuilder(String netFile) throws IOException {
try {
var file = Parameters.getFileOrExample(netFile);
this.net = Net.load(file);
@@ -46,7 +46,7 @@ public class Simulation {
* @param net the net
* @throws IllegalArgumentException if the net is null
*/
public Simulation(Net net) {
public SimulationBuilder(Net net) {
if (net == null)
throw new IllegalArgumentException("Net needed!");
this.net = net;
@@ -59,7 +59,7 @@ public class Simulation {
* @throws IllegalArgumentException if the runs are less than 1
* @return this simulation
*/
public Simulation setRuns(int runs) {
public SimulationBuilder setRuns(int runs) {
if (runs <= 0)
throw new IllegalArgumentException("Runs must be greater than 0!");
@@ -73,7 +73,7 @@ public class Simulation {
* @param seed the seed
* @return this simulation
*/
public Simulation setSeed(long seed) {
public SimulationBuilder setSeed(long seed) {
this.seed = seed;
return this;
}
@@ -85,7 +85,7 @@ public class Simulation {
* @param parallel if the simulation should run in parallel
* @return this simulation
*/
public Simulation setParallel(boolean parallel) {
public SimulationBuilder setParallel(boolean parallel) {
this.parallel = parallel;
return this;
}
@@ -96,7 +96,7 @@ public class Simulation {
* @param csv the CSV file
* @return this simulation
*/
public Simulation setCsv(String csv) {
public SimulationBuilder setCsv(String csv) {
this.csv = csv;
return this;
}
@@ -110,7 +110,7 @@ public class Simulation {
* @return this simulation
* @throws IllegalArgumentException if the criteria are null
*/
public Simulation setEndCriteria(EndCriteria... criterias) {
public SimulationBuilder setEndCriteria(EndCriteria... criterias) {
if (criterias == null)
throw new IllegalArgumentException("End criteria cannot be null!");
this.endCriteria = criterias;

View File

@@ -7,8 +7,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import org.objenesis.strategy.StdInstantiatorStrategy;
@@ -25,7 +25,7 @@ import net.berack.upo.valpre.rand.Rng;
* connections between nodes. In order to start a simulation, at least one node
* must be a Source or must generate at least one event to be processed.
*/
public final class Net {
public final class Net implements Iterable<ServerNode> {
private final List<ServerNode> servers = new ArrayList<>();
private final HashMap<ServerNode, Integer> indices = new HashMap<>();
private final List<List<Connection>> connections = new ArrayList<>();
@@ -220,16 +220,6 @@ public final class Net {
}
}
/**
* Apply a consumer to all the nodes. The implementation uses a stream and for
* this reason you should consider to make thread safe the consumer.
*
* @param consumer a function that takes in input a ServerNode
*/
public void forEachNode(Consumer<ServerNode> consumer) {
this.servers.stream().forEach(consumer);
}
/**
* Save the current net to a file.
* The resulting file is saved with Kryo.
@@ -304,4 +294,9 @@ public final class Net {
this.weight = weight;
}
}
@Override
public Iterator<ServerNode> iterator() {
return this.servers.iterator();
}
}

View File

@@ -40,8 +40,17 @@ public final class Simulation {
this.rng = rng;
this.time = 0.0d;
// check for ending criteria in simulation
boolean hasLimit = false;
for (var node : net)
if (node.spawnArrivals != Integer.MAX_VALUE)
hasLimit = true;
if (!hasLimit && (criterias == null || criterias.length == 0))
throw new IllegalArgumentException("At least one end criteria is needed!");
// Initial arrivals (if spawned)
net.forEachNode(node -> {
net.forEach(node -> {
this.states.put(node.name, new NodeState());
if (node.shouldSpawnArrival(0))
this.addArrival(node);
@@ -152,7 +161,7 @@ public final class Simulation {
* on the given node, and the delay is determined by the node's service
* distribution.
*
* @param node The node to create the event for.
* @param node The node to create the event for.
* @param state The current state of the node
*/
public void addDepartureIfPossible(ServerNode node, NodeState state) {

View File

@@ -134,7 +134,7 @@ public class TestSimulation {
var nodes = new HashSet<ServerNode>();
nodes.add(node);
nodes.add(node1);
net.forEachNode(n -> assertTrue(nodes.contains(n)));
net.forEach(n -> assertTrue(nodes.contains(n)));
net.addConnection(0, 1, 1.0);
var conn = net.getChildren(0);