EndCriteria&NetBuilder
- Add end criteria parsing and update simulation parameters - added net builder from console
This commit is contained in:
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -46,5 +46,12 @@
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "plot -csv example2.csv"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Build Net",
|
||||
"request": "launch",
|
||||
"mainClass": "net.berack.upo.valpre.Main",
|
||||
"args": "net"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.berack.upo.valpre.sim.EndCriteria;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0)
|
||||
@@ -16,20 +18,23 @@ public class Main {
|
||||
var param = Main.getParameters(program, subArgs);
|
||||
switch (program) {
|
||||
case "simulation" -> {
|
||||
var net = param.get("net");
|
||||
var csv = param.get("csv");
|
||||
var runs = param.getOrDefault("runs", Integer::parseInt, 100);
|
||||
var seed = param.getOrDefault("seed", Long::parseLong, 2007539552L);
|
||||
var p = param.getOrDefault("p", Boolean::parseBoolean, false);
|
||||
|
||||
var sim = new Simulation(net, seed, runs, p, csv);
|
||||
sim.run();
|
||||
new Simulation(param.get("net"))
|
||||
.setCsv(param.get("csv"))
|
||||
.setRuns(param.getOrDefault("runs", Integer::parseInt, 100))
|
||||
.setSeed(param.getOrDefault("seed", Long::parseLong, 2007539552L))
|
||||
.setParallel(param.get("p") != null)
|
||||
.setEndCriteria(EndCriteria.parse(param.get("end")))
|
||||
.run();
|
||||
}
|
||||
case "plot" -> {
|
||||
var csv = param.get("csv");
|
||||
var plot = new Plot(csv);
|
||||
plot.show();
|
||||
}
|
||||
case "net" -> {
|
||||
var net = new NetBuilderInteractive();
|
||||
net.run();
|
||||
}
|
||||
default -> exit("Invalid program!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -50,12 +55,14 @@ public class Main {
|
||||
arguments.put("seed", true);
|
||||
arguments.put("runs", true);
|
||||
arguments.put("net", true);
|
||||
arguments.put("end", true);
|
||||
arguments.put("csv", true);
|
||||
|
||||
var descriptions = new HashMap<String, String>();
|
||||
descriptions.put("p", "Add this if you want the simulation to use threads (one each run).");
|
||||
descriptions.put("seed", "The seed of the simulation.");
|
||||
descriptions.put("runs", "How many runs the simulator should run.");
|
||||
descriptions.put("end", "When the simulation should end. Format is [ClassName:param1,..,paramN];[..]");
|
||||
descriptions.put("net", "The file net to use. Use example1.net or example2.net for the provided ones.");
|
||||
|
||||
var csvDesc = switch (program) {
|
||||
@@ -65,6 +72,8 @@ public class Main {
|
||||
};
|
||||
descriptions.put("csv", csvDesc);
|
||||
|
||||
if (program.equals("net"))
|
||||
return null;
|
||||
return Parameters.getArgsOrHelper(args, "-", arguments, descriptions);
|
||||
}
|
||||
|
||||
@@ -76,7 +85,10 @@ public class Main {
|
||||
var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI();
|
||||
var name = new File(uri).getName();
|
||||
System.out.println(message);
|
||||
System.out.println("Usage: java -jar " + name + ".jar [simulation|plot] [args]");
|
||||
System.out.println("Usage: java -jar " + name + ".jar [simulation|plot|net] [args]");
|
||||
System.out.println("simulation args: -net <net> -csv <csv> [-runs <runs>] [-seed <seed>] [-p]");
|
||||
System.out.println("plot args: -csv <csv>");
|
||||
System.out.println("net args: none");
|
||||
System.exit(1);
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
182
src/main/java/net/berack/upo/valpre/NetBuilderInteractive.java
Normal file
182
src/main/java/net/berack/upo/valpre/NetBuilderInteractive.java
Normal file
@@ -0,0 +1,182 @@
|
||||
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))
|
||||
builder.append(connection.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.createLimitedSource(name, distribution, limit);
|
||||
}
|
||||
case 2 -> {
|
||||
var servers = ask("Number of servers: ", Integer::parseInt, 1);
|
||||
var unavailable = askDistribution("Unavailable distribution");
|
||||
yield ServerNode.createQueue(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;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import com.esotericsoftware.kryo.KryoException;
|
||||
|
||||
import net.berack.upo.valpre.sim.EndCriteria;
|
||||
import net.berack.upo.valpre.sim.Net;
|
||||
import net.berack.upo.valpre.sim.SimulationMultiple;
|
||||
import net.berack.upo.valpre.sim.stats.CsvResult;
|
||||
@@ -14,27 +15,20 @@ import net.berack.upo.valpre.sim.stats.CsvResult;
|
||||
* and runs the simulation with the given parameters.
|
||||
*/
|
||||
public class Simulation {
|
||||
public final String csv;
|
||||
public final int runs;
|
||||
public final long seed;
|
||||
public final boolean parallel;
|
||||
public final Net net;
|
||||
private String csv;
|
||||
private int runs;
|
||||
private long seed;
|
||||
private boolean parallel;
|
||||
private Net net;
|
||||
private EndCriteria[] endCriteria;
|
||||
|
||||
/**
|
||||
* Create a new simulation with the given arguments.
|
||||
* Create a new simulation for the given net.
|
||||
*
|
||||
* @param args The arguments for the simulation.
|
||||
* @throws IOException if the file is has a problem
|
||||
* @param netFile the net file to load
|
||||
* @throws IOException if the file has a problem
|
||||
*/
|
||||
public Simulation(String netFile, long seed, int runs, boolean parallel, String csv) throws IOException {
|
||||
if (runs <= 0)
|
||||
throw new IllegalArgumentException("Runs must be greater than 0!");
|
||||
|
||||
this.runs = runs;
|
||||
this.seed = seed;
|
||||
this.csv = csv;
|
||||
this.parallel = parallel;
|
||||
|
||||
public Simulation(String netFile) throws IOException {
|
||||
try {
|
||||
var file = Parameters.getFileOrExample(netFile);
|
||||
this.net = Net.load(file);
|
||||
@@ -46,6 +40,83 @@ public class Simulation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new simulation for the given net.
|
||||
*
|
||||
* @param net the net
|
||||
* @throws IllegalArgumentException if the net is null
|
||||
*/
|
||||
public Simulation(Net net) {
|
||||
if (net == null)
|
||||
throw new IllegalArgumentException("Net needed!");
|
||||
this.net = net;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of runs for the simulation.
|
||||
*
|
||||
* @param runs the number of runs
|
||||
* @throws IllegalArgumentException if the runs are less than 1
|
||||
* @return this simulation
|
||||
*/
|
||||
public Simulation setRuns(int runs) {
|
||||
if (runs <= 0)
|
||||
throw new IllegalArgumentException("Runs must be greater than 0!");
|
||||
|
||||
this.runs = runs;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the seed for the simulation.
|
||||
*
|
||||
* @param seed the seed
|
||||
* @return this simulation
|
||||
*/
|
||||
public Simulation setSeed(long seed) {
|
||||
this.seed = seed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the simulation should run in parallel.
|
||||
* The parallelization is done by running each simulation in a separate thread.
|
||||
*
|
||||
* @param parallel if the simulation should run in parallel
|
||||
* @return this simulation
|
||||
*/
|
||||
public Simulation setParallel(boolean parallel) {
|
||||
this.parallel = parallel;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CSV file to save the results.
|
||||
*
|
||||
* @param csv the CSV file
|
||||
* @return this simulation
|
||||
*/
|
||||
public Simulation setCsv(String csv) {
|
||||
this.csv = csv;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the end criteria for the simulation.
|
||||
* Can be an empty array if no criteria are needed.
|
||||
* Cannot be null.
|
||||
*
|
||||
* @param criterias the end criteria
|
||||
* @return this simulation
|
||||
* @throws IllegalArgumentException if the criteria are null
|
||||
*/
|
||||
public Simulation setEndCriteria(EndCriteria... criterias) {
|
||||
if (criterias == null)
|
||||
throw new IllegalArgumentException("End criteria cannot be null!");
|
||||
this.endCriteria = criterias;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the simulation with the given parameters.
|
||||
* At the end it prints the results and saves them to a CSV file if requested.
|
||||
@@ -58,7 +129,9 @@ public class Simulation {
|
||||
public void run() throws InterruptedException, ExecutionException, KryoException, IOException {
|
||||
var nano = System.nanoTime();
|
||||
var sim = new SimulationMultiple(this.net);
|
||||
var summary = this.parallel ? sim.runParallel(this.seed, this.runs) : sim.run(this.seed, this.runs);
|
||||
var summary = this.parallel
|
||||
? sim.runParallel(this.seed, this.runs, this.endCriteria)
|
||||
: sim.run(this.seed, this.runs, this.endCriteria);
|
||||
nano = System.nanoTime() - nano;
|
||||
|
||||
System.out.print(summary);
|
||||
|
||||
@@ -12,6 +12,45 @@ public interface EndCriteria {
|
||||
*/
|
||||
public boolean shouldEnd(Simulation run);
|
||||
|
||||
/**
|
||||
* Parses the given string to create an array of end criteria.
|
||||
* The string passed must be in the following format:
|
||||
* [criteria1];[criteria2];...;[criteriaN]
|
||||
*
|
||||
* and each criteria must be in the following format:
|
||||
* ClassName:param1,param2,...,paramN
|
||||
*
|
||||
* If the string is empty or null, an empty array is returned.
|
||||
* If one of the criteria is not valid, an exception is thrown.
|
||||
*
|
||||
* @param criterias The string to parse.
|
||||
* @return An array of end criteria.
|
||||
* @throws IllegalArgumentException If one of the criteria is not valid.
|
||||
*/
|
||||
public static EndCriteria[] parse(String criterias) {
|
||||
if (criterias == null || criterias.isEmpty())
|
||||
return new EndCriteria[0];
|
||||
|
||||
var criteria = criterias.split(";");
|
||||
var endCriteria = new EndCriteria[criteria.length];
|
||||
for (int i = 0; i < criteria.length; i++) {
|
||||
var current = criteria[i].substring(1, criteria[i].length() - 1); // Remove the brackets
|
||||
var parts = current.split(":");
|
||||
if (parts.length != 2)
|
||||
throw new IllegalArgumentException("Invalid criteria: " + current);
|
||||
|
||||
var className = parts[0];
|
||||
var params = parts[1].split(",");
|
||||
endCriteria[i] = switch (className) {
|
||||
case "MaxArrivals" -> new MaxArrivals(params[0], Integer.parseInt(params[1]));
|
||||
case "MaxDepartures" -> new MaxDepartures(params[0], Integer.parseInt(params[1]));
|
||||
case "MaxTime" -> new MaxTime(Double.parseDouble(params[0]));
|
||||
default -> throw new IllegalArgumentException("Invalid criteria: " + current);
|
||||
};
|
||||
}
|
||||
return endCriteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the simulation when the given node has reached the specified number of
|
||||
* arrivals.
|
||||
|
||||
Reference in New Issue
Block a user