// SPDX-FileCopyrightText: 2020 Roberto Posenato // // SPDX-License-Identifier: LGPL-3.0-or-later package it.univr.di.cstnu.util; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import it.unimi.dsi.fastutil.objects.*; import it.univr.di.Debug; import it.univr.di.cstnu.algorithms.PSTN; import it.univr.di.cstnu.algorithms.STNURTE; import it.univr.di.cstnu.graph.*; import it.univr.di.labeledvalue.Constants; import org.apache.commons.math3.stat.Frequency; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Option; import org.kohsuke.args4j.spi.StringArrayOptionHandler; import javax.annotation.Nonnull; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * A simple class to determine some statistics about the execution of PSTNs *

* The main idea is, given some DC PSTNs, to execute each of them for #times and register how much time the instance was executed without constraint violation. * * @author posenato * @version $Rev: 732 $ */ public class PSTNRTEBenchmarkRunner { /** * The possible exit of an RTE */ public enum ExitExecution { /** * successful with all contingent durations inside the contingent bounds */ okInBound, /** * successful with some contingent durations outside the contingent bounds */ okOutBounds, /** * not successful with some contingent durations outside the contingent bounds */ notOkOutBounds } /** * Maintains the map (contingent node, chosen duration of its contingent link), the number of chosen durations outside the bounds of the relative contingent link, and the conjunct probability mass of the contingent ranges with respect to the probability distribution functions of the contingent durations. */ @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "Ok..., but it is not so important!") public record PSTNSituation(Object2IntMap situation, double maxOutlierSpan, int durationsOutOfBounds, double conjunctProbabilityMass) { /** * @return true if there are no durations out of the admissible range. */ public boolean isWithinBounds() { return this.durationsOutOfBounds == 0; } } /** * Represents a key composed by {@code (nodes, contingents)}. *

* Implements a natural order based on increasing pair {@code (nodes, contingents)}. * * @author posenato */ static private class GlobalStatisticsKey implements Comparable { /** * # contingents */ final int contingents; /** * # of nodes */ final int nodes; /** * default constructor * * @param inputNodes #nodes * @param inputContingents # cont */ GlobalStatisticsKey(final int inputNodes, final int inputContingents) { nodes = inputNodes; contingents = inputContingents; } /** * The order is respect to #nodes,#contingents, and #propositions. * * @param o the object to be compared. * * @return negative if this has, in order, fewer nodes or fewer contingents or fewer propositions than the parameter 'o'; a positive value in the opposite case, 0 when all three values are equal to the corresponding values in 'o'. */ @Override public int compareTo(@Nonnull GlobalStatisticsKey o) { final long d = (long) nodes - o.nodes; if (d == 0) { return contingents - o.contingents; } return (int) d; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof GlobalStatisticsKey o1)) { return false; } return o1.compareTo(this) == 0; } /** * @return #cont */ public int getContingent() { return contingents; } /** * @return #nodes */ public int getNodes() { return nodes; } @Override public int hashCode() { return (contingents) * 100000 + nodes; } } /** * Each instance of this class represents a set of global statistics. Each global statistics element represents a map {@code (GlobalStatisticsKey, SummaryStatistics)}. Each SummaryStatistics element allows the determination of different statistics of all items added to the element. */ static private class GlobalStatistics { Object2ObjectMap networkEdges = new Object2ObjectAVLTreeMap<>(); Object2ObjectMap execTimeInSec = new Object2ObjectAVLTreeMap<>(); // Object2ObjectMap minimizationExecTimeInSec = new Object2ObjectAVLTreeMap<>(); Object2ObjectMap probConjunctedProbMass = new Object2ObjectAVLTreeMap<>(); // Object2ObjectMap numberMinimizationProblem = new Object2ObjectAVLTreeMap<>(); /** * Three possible values: okInBound, okOutBounds, notOkOutBounds for early_execution strategy (_E suffix) and middle_point_execution_strategy (_M * suffix) */ Object2ObjectMap executionExit_E = new Object2ObjectAVLTreeMap<>(); Object2ObjectMap durationOutsideBoundsInOK_E = new Object2ObjectAVLTreeMap<>(); Object2ObjectMap durationOutsideBoundsInNOTOK_E = new Object2ObjectAVLTreeMap<>(); Object2ObjectMap executionExit_M = new Object2ObjectAVLTreeMap<>(); Object2ObjectMap durationOutsideBoundsInOK_M = new Object2ObjectAVLTreeMap<>(); Object2ObjectMap durationOutsideBoundsInNOTOK_M = new Object2ObjectAVLTreeMap<>(); } /** * CSV separator */ static final String CSVSep = ";\t"; /** * Global header */ static final String GLOBAL_HEADER = "%n%nGlobal statistics%n" + "#networks" + CSVSep + "#nodes" + CSVSep + "#contingents" + CSVSep + "#avgEdges" + CSVSep + "stdDevEdges" + CSVSep + "avgExeTime[s]" + CSVSep + "stdDevExeTime[s]" + CSVSep + "avgConjunctedProbMass" + CSVSep + "stdDevConjunctedProbMass" + CSVSep + "%%InBoundOK_E" + CSVSep + "%%OutBoundOK_E" + CSVSep + "%%OutBoundNOTOK_E" + CSVSep + "#OutBoundDDuraOK_E" + CSVSep + "#OutBoundDDuraOK_E" + CSVSep + "%%InBoundOK_M" + CSVSep + "%%OutBoundOK_M" + CSVSep + "%%OutBoundNOTOK_M" + CSVSep + "#OutBoundDDuraOK_M" + CSVSep + "#OutBoundDDuraOK_M" + CSVSep + "%n"; /** * */ static final String GLOBAL_HEADER_ROW = "%d" + CSVSep + "%d" + CSVSep + "%d" + CSVSep + "%E" + CSVSep // avgEdges + "%E" + CSVSep // stdDevEdges + "%E" + CSVSep // avgExeTime + "%E" + CSVSep // stdDevExeTime + "%E" + CSVSep // avgConjunctedProbMass + "%E" + CSVSep // scale ConjunctedProbMass + "%6.4f" + CSVSep // "%InBoundOK_E" + "%6.4f" + CSVSep // "%OutBoundOK_E" + "%6.4f" + CSVSep // "%OutBoundNOTOK_E" + "%6.4f" + CSVSep // #OutBoundDDuraInOK_E" + "%6.4f" + CSVSep // #OutBoundDDuraInOK_E" + "%6.4f" + CSVSep // "%InBoundOK_M" + "%6.4f" + CSVSep // "%OutBoundOK_M" + "%6.4f" + CSVSep // "%OutBoundNOTOK_M" + "%6.4f" + CSVSep // #OutBoundDDuraInOK_M" + "%6.4f" + CSVSep // #OutBoundDDuraInOK_M" + "%n"; /** * class logger */ static final Logger LOG = Logger.getLogger(PSTNRTEBenchmarkRunner.class.getName()); /** * Output file header */ static final String OUTPUT_HEADER = String.format("%31s%s", "fileName", CSVSep) + String.format("%6s%s", "#nodes", CSVSep) + String.format("%4s%s", "#ctg", CSVSep) + String.format("%5s%s", "#edges", CSVSep) + String.format("%14s%s", "avgExeTime[s]", CSVSep) + String.format("%14s%s", "std.dev.[s]", CSVSep) + String.format("%14s%s", "ConjProbMass", CSVSep) + String.format("%8s%s", "%InBOK_E", CSVSep) + String.format("%8s%s", "%OutBOK_E", CSVSep) + String.format("%8s%s", "%OutBNOTOK_E", CSVSep)// + String.format("%8s%s", "AvgOutBDOK_E", CSVSep) + String.format("%8s%s", "AvgOutBDNOTOK_E", CSVSep) + String.format("%8s%s", "%InBOK_M", CSVSep) + String.format("%8s%s", "%OutBOK_M", CSVSep) + String.format("%8s%s", "%OutBNOTOK_M", CSVSep)// + String.format("%8s%s", "AvgOutBDOK_M", CSVSep) + String.format("%8s%s", "AvgOutBDNOTOK_M", CSVSep); /** * OUTPUT_ROW is split in OUTPUT_ROW_GRAPH + OUTPUT_ROW_ALG_STATS */ static final String OUTPUT_ROW_GRAPH = "%31s" + CSVSep //name + "%6d" + CSVSep //#nodes + "%4d" + CSVSep //#contingents + "%5d" + CSVSep//#edges ; /** * OUTPUT_ROW is split in OUTPUT_ROW_GRAPH + OUTPUT_ROW_ALG_STATS */ static final String OUTPUT_ROW_ALG_STATS = "%14E" + CSVSep // alg avgExe + "%14E" + CSVSep // alg avgExe dev std + "%14E" + CSVSep // ConjProbMass + "%8.4f" + CSVSep // %InBoundOK_E + "%8.4f" + CSVSep // %OutBoundOK_E + "%8.4f" + CSVSep // %OutBoundNOTOK_E + "%8.4f" + CSVSep // #OutBoundDDuraInOK_E + "%8.4f" + CSVSep // #OutBoundDDuraInOK_E + "%8.4f" + CSVSep // %InBoundOK_M + "%8.4f" + CSVSep // %OutBoundOK_M + "%8.4f" + CSVSep // %OutBoundNOTOK_M + "%8.4f" + CSVSep // #OutBoundDDuraInOK_M + "%8.4f" + CSVSep // #OutBoundDDuraInOK_M ; /** * Version */ static final String VERSIONandDATE = "1.0, July, 1 2024"; /** * Date formatter */ private final static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); /** * Allows checking the execution time of algorithms on a set of instances. * * @param args an array of {@link String} objects. */ public static void main(String[] args) { LOG.finest("Checker " + VERSIONandDATE + "\nStart..."); System.out.println("Checker " + VERSIONandDATE + "\n" + getNow() + ": Start of execution."); final PSTNRTEBenchmarkRunner tester = new PSTNRTEBenchmarkRunner(); if (!tester.manageParameters(args)) { return; } LOG.finest("Parameters ok!"); if (tester.versionReq) { return; } /* * To collect statistics w.r.t. the dimension of networks */ final GlobalStatistics globalStatistics = new GlobalStatistics(); final RunMeter runMeter = new RunMeter(System.currentTimeMillis(), tester.instances.size(), 0); runMeter.printProgress(0); tester.output.println("*".repeat(79)); tester.output.println("* Trial date: " + getNow()); tester.output.println("*".repeat(79)); tester.output.println(OUTPUT_HEADER); tester.output.flush(); int nTaskSuccessfullyFinished = 0; for (final File file : tester.instances) { final boolean isOk = tester.worker(file, runMeter, globalStatistics); if (isOk) { nTaskSuccessfullyFinished++; } } final String msg = "Number of instances processed successfully over total: " + nTaskSuccessfullyFinished + "/" + tester.instances.size() + "."; LOG.info(msg); System.out.println("\n" + getNow() + ": " + msg); tester.output.printf(GLOBAL_HEADER); //Use one of the elements in globalStatistics to extract all the possible globalStatisticsKeys for (final Object2ObjectMap.Entry entryNetworkEdges : globalStatistics.networkEdges.object2ObjectEntrySet()) { final GlobalStatisticsKey globalStatisticsKey = entryNetworkEdges.getKey(); tester.output.printf(GLOBAL_HEADER_ROW, // Long.valueOf(entryNetworkEdges.getValue().getN()), // Integer.valueOf(globalStatisticsKey.getNodes()),// Integer.valueOf(globalStatisticsKey.getContingent()), // Double.valueOf(entryNetworkEdges.getValue().getMean()),// Double.valueOf(entryNetworkEdges.getValue().getStandardDeviation()),// Double.valueOf(globalStatistics.execTimeInSec.get(globalStatisticsKey).getMean()),// Double.valueOf(globalStatistics.execTimeInSec.get(globalStatisticsKey).getStandardDeviation()),// Double.valueOf(globalStatistics.probConjunctedProbMass.get(globalStatisticsKey).getMean()),// Double.valueOf(globalStatistics.probConjunctedProbMass.get(globalStatisticsKey).getStandardDeviation()),// Double.valueOf(globalStatistics.executionExit_E.get(globalStatisticsKey).getPct(ExitExecution.okInBound)),// Double.valueOf(globalStatistics.executionExit_E.get(globalStatisticsKey).getPct(ExitExecution.okOutBounds)),// Double.valueOf(globalStatistics.executionExit_E.get(globalStatisticsKey).getPct(ExitExecution.notOkOutBounds)),// Double.valueOf(globalStatistics.durationOutsideBoundsInOK_E.get(globalStatisticsKey).getMean()),// Double.valueOf(globalStatistics.durationOutsideBoundsInNOTOK_E.get(globalStatisticsKey).getMean()),// Double.valueOf(globalStatistics.executionExit_M.get(globalStatisticsKey).getPct(ExitExecution.okInBound)),// Double.valueOf(globalStatistics.executionExit_M.get(globalStatisticsKey).getPct(ExitExecution.okOutBounds)),// Double.valueOf(globalStatistics.executionExit_M.get(globalStatisticsKey).getPct(ExitExecution.notOkOutBounds)),// Double.valueOf(globalStatistics.durationOutsideBoundsInOK_M.get(globalStatisticsKey).getMean()),// Double.valueOf(globalStatistics.durationOutsideBoundsInNOTOK_M.get(globalStatisticsKey).getMean())// );// tester.output.println(); } tester.output.printf("%n%n%n"); tester.output.close(); } /** * @param offSet the offset to add to the lower bound for finding a new upper bound when the upper bound is not finite. * * @return a strategy for ordinary node execution time. */ private static STNURTE.Strategy buildMiddlePointStrategy(int offSet) { /* * It uses averageContingentRangeWidth to determine a middle exec value for nodes * having ∞ as an upper bound in their time window. */ final STNURTE.NodeAndExecutionTimeChoice strategy = candidates -> { LabeledNode first = null; TimeInterval firstTW = null; int minUpperBound = Constants.INT_POS_INFINITE; for (final STNURTE.NodeWithTimeInterval entry : candidates.nodes()) { final LabeledNode node = entry.node(); final TimeInterval tw = entry.timeInterval(); if (tw.getUpper() <= candidates.timeInterval().getUpper()) { first = node; firstTW = tw; break; } else { if (minUpperBound > tw.getUpper()) { minUpperBound = tw.getUpper(); first = node; firstTW = tw; } } } if (minUpperBound == Constants.INT_POS_INFINITE) { //all candidates have an upper bound equal to +∞ //select the first one and limit its upper bound using averageContingentRangeWidth @SuppressWarnings("OptionalGetWithoutIsPresent") final STNURTE.NodeWithTimeInterval entry = candidates.nodes().stream().findFirst().get(); first = entry.node(); firstTW = entry.timeInterval(); final int lb = entry.timeInterval().getLower(); firstTW.set(lb, lb + offSet); if (Debug.ON) { LOG.info("All candidates have upper bound = ∞. Selected the first node " + first + " with time window " + firstTW); } } final ObjectSet singleSet = new ObjectArraySet<>(1); singleSet.add(first); final int lowerBound = Math.max(firstTW.getLower(), candidates.timeInterval().getLower()); final int upperBound = Math.min(firstTW.getUpper(), candidates.timeInterval().getUpper()); final int exeTime = Constants.sumWithOverflowCheck(lowerBound, upperBound) / 2; return new STNURTE.NodeOccurrence(exeTime, singleSet); }; return () -> strategy; } /** * Builds a situation for the PSTN. Moreover, it collects the number of durations that are outside the bounds of the relative contingent link and the conjunct probability mass determined by the contingent bounds. * * @param pstn a network already initialized. * * @return a PSTNSituation if the network has some contingent links; otherwise, it is null. */ private static PSTNSituation buildSituation(PSTN pstn) { if (pstn.getLowerCaseEdgesMap() == null) { //no contingent link LOG.info("There is no contingent link. Giving up!"); return null; } final Object2IntMap situation = new Object2IntOpenHashMap<>(pstn.getLowerCaseEdgesMap().size()); int durationsOutOfBounds = 0; double conjunctProbabilityMass = 1.0; double maxOutlierSpan = 0.0; for (final LabeledNode ctg : pstn.getLowerCaseEdgesMap().keySet()) { final LogNormalDistributionParameter logNormalParam = ctg.getLogNormalDistribution(); if (logNormalParam == null) { continue; } final int rndValue = (int) logNormalParam.sample(); situation.put(ctg, rndValue); //possible shift of activation timepoint // final int shift = logNormalParam.getShift(); //check if the value is outside the contingent approximated bounds. final int lowerBound = pstn.getLowerCaseEdgesMap().get(ctg).getLabeledValue(); assert pstn.getUpperCaseEdgesMap() != null && pstn.getUpperCaseEdgesMap().get(ctg) != null; final int upperBound = (-pstn.getUpperCaseEdgesMap().get(ctg).getLabeledValue()); if (lowerBound >= upperBound) { throw new IllegalStateException("Contingent ranges for " + ctg + " are wrong: [" + lowerBound + ", " + upperBound + "]"); } if (rndValue < lowerBound || rndValue > upperBound) { LOG.info("The chosen duration for " + ctg + " is " + rndValue + ". It is outside the range [" + lowerBound + ", " + upperBound + "]"); durationsOutOfBounds++; double diff; if (rndValue < lowerBound && (diff = lowerBound - rndValue) > maxOutlierSpan) { maxOutlierSpan = diff; } if (rndValue > lowerBound && (diff = rndValue - upperBound) > maxOutlierSpan) { maxOutlierSpan = diff; } } final double cdfValue = (logNormalParam.cumulativeProbability(upperBound) - logNormalParam.cumulativeProbability(lowerBound)); if (-1E-4 < cdfValue && cdfValue < 1E-4) { throw new IllegalStateException("Contingent ranges for %s: [%d, %d] determine a not significative cumulative distribution value %s\n Lognormale parameter: %s".formatted(ctg, lowerBound, upperBound, cdfValue, ctg.getLogNormalDistribution())); } conjunctProbabilityMass *= cdfValue; } return new PSTNSituation(situation, maxOutlierSpan, durationsOutOfBounds, conjunctProbabilityMass); } /** * @param situation a situation * * @return a strategy for contingent durations. */ private static STNURTE.Strategy buildSituationStrategy(@Nonnull Object2IntMap situation) { final Object2IntMap localSituation = new Object2IntOpenHashMap<>(situation); /* * It is assumed that for each contingent node, the proper time window has value '[executionTimeActivationTP, ∞]' * The candidates.timeInterval() is used only to guarantee that the chosen value is greater than the minimum. */ final STNURTE.NodeAndExecutionTimeChoice strategy = candidates -> { final ObjectSet nodes = new ObjectLinkedOpenHashSet<>(); if (candidates.nodes() == null) { return new STNURTE.NodeOccurrence(0, nodes); } final ExtendedPriorityQueue queue = new ExtendedPriorityQueue<>(); for (final STNURTE.NodeWithTimeInterval entry : candidates.nodes()) { final int ctgTime = Constants.sumWithOverflowCheck(entry.timeInterval().getLower(), localSituation.getInt(entry.node())); if (ctgTime < candidates.timeInterval().getLower()) { throw new IllegalArgumentException("The chosen time %d for ctg %s is before the lower bound of the time window %s".formatted(ctgTime, entry.node(), candidates.timeInterval())); } queue.insertOrUpdate(entry.node(), ctgTime); } assert queue.getFirstPriority() != null; AbstractObject2IntMap.BasicEntry entry = queue.extractFirstEntry(); final int minTime = entry.getIntValue(); nodes.add(entry.getKey()); while (!queue.isEmpty()) { entry = queue.extractFirstEntry(); if (entry.getIntValue() == minTime) { nodes.add(entry.getKey()); } else { break; } } if (Debug.ON) { if (LOG.isLoggable(Level.FINER)) {LOG.finer("Environment chooses that " + Arrays.toString(nodes.toArray()) + " occur at time " + minTime);} } return new STNURTE.NodeOccurrence(minTime, nodes); }; return () -> strategy; } /** * @return current time in {@link #dateFormatter} format */ private static String getNow() { return dateFormatter.format(new Date()); } /** * @param value value in nanoseconds * * @return the value in seconds */ private static double nanoSeconds2Seconds(double value) { return value / 1E9; } /** * Class for representing an edge. */ private final Class currentEdgeImplClass = STNUEdgeInt.class; /** * The input file names. Each file has to contain a CSTN graph in GraphML format. */ @Argument(required = true, usage = "Input files. Each input file has to be an STNU graph in GraphML format.", metaVar = "STNU_file_names", handler = StringArrayOptionHandler.class) private String[] inputFiles; /** * */ private List instances; /** * Parameter for asking how many times to execute a PSTN. */ @Option(name = "--numExecution", usage = "Number of times to re-execute a PSTN") private int nPSTNExecution = 1; // /** // * Parameter for asking the execution strategy // */ // @Option(name = "--executionStrategy", usage = "Execution strategy for ordinary nodes.") // private STNURTE.StrategyEnum executionStrategy; // /** // * // */ // @Option(name = "--save", usage = "Save all checked instances in dispatchable form.") // private boolean save; // /** // * Parameter for asking for a timeout in seconds. // */ // @Option(name = "--timeOut", usage = "Time in seconds.") // private int timeOut = 1800; // 20 min /** * Output stream to outputFile */ private PrintStream output; /** * Output file where to write the determined experimental execution times in CSV format. */ @Option(name = "-o", aliases = "--output", usage = "Output to this file in CSV format. If the file is already present, data will be added.", metaVar = "outputFile") private File outputFile; /** * Software Version. */ @Option(name = "-v", aliases = "--version", usage = "Version") private boolean versionReq; /** * Execute the STNU graph as PSTN, determining some statistics. It returns the string representing the ({@link #OUTPUT_ROW_ALG_STATS}) part to add to the row in the statistics file. * * @param pstn a network already initialized. */ private String executePSTNTest(@Nonnull PSTN pstn, @Nonnull String rowToWrite, // @Nonnull SummaryStatistics gExecTimeInSec,// @Nonnull SummaryStatistics gProbConjunctedProbMass,// @Nonnull Frequency gExecutionExit_E,// @Nonnull SummaryStatistics gDurationOutsideBoundsInOK_E,// @Nonnull SummaryStatistics gDurationOutsideBoundsInNOTOK_E,// @Nonnull Frequency gExecutionExit_M,// @Nonnull SummaryStatistics gDurationOutsideBoundsInOK_M,// @Nonnull SummaryStatistics gDurationOutsideBoundsInNOTOK_M// ) { String msg; boolean checkInterrupted = false; final String fileName = pstn.getG().getFileName().getName(); PSTNSituation situation = null; final SummaryStatistics localExecTimeStat_E = new SummaryStatistics(); final SummaryStatistics localDurationOutsideBoundsInOK_E = new SummaryStatistics(); final SummaryStatistics localDurationOutsideBoundsInNOTOK_E = new SummaryStatistics(); final Frequency localExitExe_E = new Frequency(); final SummaryStatistics localDurationOutsideBoundsInOK_M = new SummaryStatistics(); final SummaryStatistics localDurationOutsideBoundsInNOTOK_M = new SummaryStatistics(); final Frequency localExitExe_M = new Frequency(); // final STNURTE.Strategy middlePointStrategy = buildMiddlePointStrategy(pstn.getAvgContingentRangeWidth()); STNURTE.RTEState status = null; for (int j = 0; j < nPSTNExecution && !checkInterrupted; j++) { LOG.info("Execution " + (j + 1) + "/" + nPSTNExecution + " for PSTN " + fileName); final STNURTE stnuRTE = new STNURTE(pstn.getG(), false); boolean rteExists = true; try { situation = buildSituation(pstn); } catch (IllegalArgumentException e) { msg = "%s: %s does not admit a situation. Details:%s\nIgnored.".formatted(getNow(), fileName, e.getMessage()); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; continue; } catch (IllegalStateException e1) { msg = "%s: %s cannot be executed for the following unmanaged reason. Details:%s\nIgnored.".formatted(getNow(), fileName, e1.getMessage()); LOG.severe(msg); checkInterrupted = true; continue; } if (situation == null) { msg = "%s: %s does not admit a situation".formatted(getNow(), fileName); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; continue; } LOG.info("Situation: " + situation); final STNURTE.Strategy middlePointStrategy = buildMiddlePointStrategy((int) (situation.maxOutlierSpan() * 2.5)); if (situation.isWithinBounds()) { LOG.info("Execution OKinBounds: All contingent durations are within their bounds. Execution is not necessary."); localExitExe_E.addValue(ExitExecution.okInBound); localExitExe_M.addValue(ExitExecution.okInBound); gExecutionExit_E.addValue(ExitExecution.okInBound); gExecutionExit_M.addValue(ExitExecution.okInBound); // localExecTimeStat_E.addValue(1); continue; } final STNURTE.Strategy situationStrategy = buildSituationStrategy(situation.situation); //EARLY EXECUTION STRATEGY LOG.info("***\nTest with FIRST_NODE_EARLY_EXECUTION_STRATEGY\n***"); try { status = stnuRTE.rte(middlePointStrategy, situationStrategy); } catch (IllegalStateException e) { msg = getNow() + ": " + fileName + " cannot be executed with the current situation. Details: " + e.getMessage() + "\nContinue with another try!"; // System.out.println(msg); LOG.info("EARLY Execution notOKOutBounds: " + msg); localExitExe_E.addValue(ExitExecution.notOkOutBounds); gExecutionExit_E.addValue(ExitExecution.notOkOutBounds); localDurationOutsideBoundsInNOTOK_E.addValue(situation.durationsOutOfBounds()); rteExists = false; } if (rteExists) { LOG.info("EARLY Execution oKOutBounds."); localExitExe_E.addValue(ExitExecution.okOutBounds); gExecutionExit_E.addValue(ExitExecution.okOutBounds); localExecTimeStat_E.addValue(status.executionTimeRTEns.getMean()); localDurationOutsideBoundsInOK_E.addValue(situation.durationsOutOfBounds()); } //MIDDLE POINT EXECUTION STRATEGY LOG.info("***\nTest with FIRST_NODE_MIDDLE_EXECUTION_STRATEGY\n***"); try { status = stnuRTE.rte(STNURTE.StrategyEnum.FIRST_NODE_MIDDLE_EXECUTION_STRATEGY, situationStrategy); } catch (IllegalStateException e) { msg = getNow() + ": " + fileName + " cannot be executed with the current situation. Details: " + e.getMessage() + "\nContinue with another try!"; // System.out.println(msg); LOG.info("MIDDLE Execution notOKOutBounds: " + msg); localExitExe_M.addValue(ExitExecution.notOkOutBounds); gExecutionExit_M.addValue(ExitExecution.notOkOutBounds); localDurationOutsideBoundsInNOTOK_M.addValue(situation.durationsOutOfBounds()); continue; } LOG.info("MIDDLE Execution oKOutBounds."); localExitExe_M.addValue(ExitExecution.okOutBounds); gExecutionExit_M.addValue(ExitExecution.okOutBounds); // localExecTimeStat_E.addValue(status.executionTimeRTEns.getSum()); localDurationOutsideBoundsInOK_M.addValue(situation.durationsOutOfBounds()); } // end for checking repetition for a single file if (checkInterrupted) { return "NOT EXECUTABLE"; } final double localAvgExeInSec = nanoSeconds2Seconds(localExecTimeStat_E.getMean()); final double localStdDevExeInSec = nanoSeconds2Seconds(localExecTimeStat_E.getStandardDeviation()); assert situation != null; if (!Double.isNaN(localAvgExeInSec)) { gExecTimeInSec.addValue(localAvgExeInSec); gProbConjunctedProbMass.addValue(situation.conjunctProbabilityMass); } if (!Double.isNaN(localDurationOutsideBoundsInOK_E.getMean())) { gDurationOutsideBoundsInOK_E.addValue(localDurationOutsideBoundsInOK_E.getMean()); } if (!Double.isNaN(localDurationOutsideBoundsInNOTOK_E.getMean())) { gDurationOutsideBoundsInNOTOK_E.addValue(localDurationOutsideBoundsInNOTOK_E.getMean()); } if (!Double.isNaN(localDurationOutsideBoundsInOK_M.getMean())) { gDurationOutsideBoundsInOK_M.addValue(localDurationOutsideBoundsInOK_M.getMean()); } if (!Double.isNaN(localDurationOutsideBoundsInNOTOK_M.getMean())) { gDurationOutsideBoundsInNOTOK_M.addValue(localDurationOutsideBoundsInNOTOK_M.getMean()); } LOG.info(fileName + " has been executed: average execution time [s]: " + localAvgExeInSec); rowToWrite += String.format(OUTPUT_ROW_ALG_STATS,// localAvgExeInSec, // localStdDevExeInSec,// situation.conjunctProbabilityMass,// //EARLY localExitExe_E.getPct(ExitExecution.okInBound),// localExitExe_E.getPct(ExitExecution.okOutBounds),// localExitExe_E.getPct(ExitExecution.notOkOutBounds),// localDurationOutsideBoundsInOK_E.getMean(),// localDurationOutsideBoundsInNOTOK_E.getMean(),// //MIDDLE localExitExe_M.getPct(ExitExecution.okInBound),// localExitExe_M.getPct(ExitExecution.okOutBounds),// localExitExe_M.getPct(ExitExecution.notOkOutBounds),// localDurationOutsideBoundsInOK_M.getMean(),// localDurationOutsideBoundsInNOTOK_M.getMean()// ); return rowToWrite; } /** * Simple method to manage command line parameters using {@code args4j} library. * * @param args input arguments * * @return false if a parameter is missing or wrong. True if every parameter is given in the right format. */ private boolean manageParameters(String[] args) { final CmdLineParser parser = new CmdLineParser(this); try { parser.parseArgument(args); } catch (CmdLineException e) { //If there's a problem in the command line, you'll get this exception. This will report an error message. System.err.println(e.getMessage()); System.err.println("java -cp CSTNU-.jar -cp it.univr.di.cstnu.DispatchabilityBenchmarkRunner [options...] arguments..."); // print the list of available options parser.printUsage(System.err); System.err.println(); // print option sample. This is useful sometimes // System.err.println("Example: java -jar Checker.jar" + parser.printExample(OptionHandlerFilter.REQUIRED) + // " ..."); return false; } if (versionReq) { System.out.printf("%s %s. Academic and non-commercial use only.\nCopyright © 2017-2020, Roberto Posenato", getClass().getName(), VERSIONandDATE); return true; } if (outputFile != null) { if (outputFile.isDirectory()) { System.err.println("Output file is a directory."); parser.printUsage(System.err); System.err.println(); return false; } // the filename has to end with .csv if (!outputFile.getName().endsWith(".csv")) { if (!outputFile.renameTo(new File(outputFile.getAbsolutePath() + ".csv"))) { final String m = "File %s cannot be renamed!".formatted(outputFile.getAbsolutePath()); LOG.severe(m); System.err.println(m); return false; } } try { output = new PrintStream(new FileOutputStream(outputFile, true), true, StandardCharsets.UTF_8); } catch (IOException e) { System.err.printf("Output file cannot be created: %s%n", e.getMessage()); parser.printUsage(System.err); System.err.println(); return false; } } else { output = System.out; } final String suffix = "pstn"; instances = new ArrayList<>(inputFiles.length); for (final String fileName : inputFiles) { final File file = new File(fileName); if (!file.exists()) { System.err.printf("File %s does not exit.%n", fileName); parser.printUsage(System.err); System.err.println(); return false; } if (!file.getName().endsWith(suffix)) { System.err.printf("File %s has not the right suffix associated with the suffix of the given network type (right suffix: %s). Game over :-/%n", fileName, suffix); parser.printUsage(System.err); System.err.println(); return false; } instances.add(file); } return true; } /** * Loads the file and executes all the actions (specified as instance parameters) on the network represented by the file. * * @param file input file * @param runState current state * @param globalStatistics global statistics * * @return true if the required task ends successfully, false otherwise. */ private boolean worker(@Nonnull File file,// @Nonnull RunMeter runState,// @Nonnull GlobalStatistics globalStatistics) { if (LOG.isLoggable(Level.FINER)) { LOG.finer("Loading %s...".formatted(file.getName())); } final TNGraphMLReader graphMLReader = new TNGraphMLReader<>(); final TNGraph graphToExecute; try { graphToExecute = graphMLReader.readGraph(file, currentEdgeImplClass); } catch (Exception e2) { final String msg = "File %s cannot be parsed. Details: %s.\nIgnored.".formatted(file.getName(), e2.getMessage()); LOG.warning(msg); System.out.println(msg); return false; } LOG.finer("...done!"); //NODES and EDGES in the original graph. DO NOT MOVE such a variable. final int nNodes = graphToExecute.getVertexCount(); final int nEdges = graphToExecute.getEdgeCount(); final PSTN pstn = new PSTN(graphToExecute); try { pstn.initAndCheck(); } catch (Exception e) { final String msg = "%s: %s is not a not well-defined instance. Details:%s\nIgnored.".formatted(getNow(), file.getName(), e.getMessage()); System.out.println(msg); LOG.severe(msg); return false; } //Only now the contingent node number is significant final int nContingents = graphToExecute.getContingentNodeCount(); final GlobalStatisticsKey globalStatisticsKey = new GlobalStatisticsKey(nNodes, nContingents); SummaryStatistics gNetworkEdges = globalStatistics.networkEdges.get(globalStatisticsKey); if (gNetworkEdges == null) { gNetworkEdges = new SummaryStatistics(); globalStatistics.networkEdges.put(globalStatisticsKey, gNetworkEdges); } gNetworkEdges.addValue(nEdges); SummaryStatistics gExecTimeInSec = globalStatistics.execTimeInSec.get(globalStatisticsKey); if (gExecTimeInSec == null) { gExecTimeInSec = new SummaryStatistics(); globalStatistics.execTimeInSec.put(globalStatisticsKey, gExecTimeInSec); } SummaryStatistics gProbConjunctedProbMass = globalStatistics.probConjunctedProbMass.get(globalStatisticsKey); if (gProbConjunctedProbMass == null) { gProbConjunctedProbMass = new SummaryStatistics(); globalStatistics.probConjunctedProbMass.put(globalStatisticsKey, gProbConjunctedProbMass); } Frequency gExecutionExit_E = globalStatistics.executionExit_E.get(globalStatisticsKey); if (gExecutionExit_E == null) { gExecutionExit_E = new Frequency(); globalStatistics.executionExit_E.put(globalStatisticsKey, gExecutionExit_E); } SummaryStatistics gDurationOutsideBoundsInOK_E = globalStatistics.durationOutsideBoundsInOK_E.get(globalStatisticsKey); if (gDurationOutsideBoundsInOK_E == null) { gDurationOutsideBoundsInOK_E = new SummaryStatistics(); globalStatistics.durationOutsideBoundsInOK_E.put(globalStatisticsKey, gDurationOutsideBoundsInOK_E); } SummaryStatistics gDurationOutsideBoundsInNOTOK_E = globalStatistics.durationOutsideBoundsInNOTOK_E.get(globalStatisticsKey); if (gDurationOutsideBoundsInNOTOK_E == null) { gDurationOutsideBoundsInNOTOK_E = new SummaryStatistics(); globalStatistics.durationOutsideBoundsInNOTOK_E.put(globalStatisticsKey, gDurationOutsideBoundsInNOTOK_E); } Frequency gExecutionExit_M = globalStatistics.executionExit_M.get(globalStatisticsKey); if (gExecutionExit_M == null) { gExecutionExit_M = new Frequency(); globalStatistics.executionExit_M.put(globalStatisticsKey, gExecutionExit_M); } SummaryStatistics gDurationOutsideBoundsInOK_M = globalStatistics.durationOutsideBoundsInOK_M.get(globalStatisticsKey); if (gDurationOutsideBoundsInOK_M == null) { gDurationOutsideBoundsInOK_M = new SummaryStatistics(); globalStatistics.durationOutsideBoundsInOK_M.put(globalStatisticsKey, gDurationOutsideBoundsInOK_M); } SummaryStatistics gDurationOutsideBoundsInNOTOK_M = globalStatistics.durationOutsideBoundsInNOTOK_M.get(globalStatisticsKey); if (gDurationOutsideBoundsInNOTOK_M == null) { gDurationOutsideBoundsInNOTOK_M = new SummaryStatistics(); globalStatistics.durationOutsideBoundsInNOTOK_M.put(globalStatisticsKey, gDurationOutsideBoundsInNOTOK_M); } String rowToWrite = String.format(OUTPUT_ROW_GRAPH, file.getName(), Integer.valueOf(nNodes), Integer.valueOf(nContingents), Integer.valueOf(nEdges)); // final PSTN.PSTNCheckStatus status; LOG.info(getNow() + ": Executing PSTN: start."); rowToWrite = executePSTNTest(pstn, rowToWrite,// gExecTimeInSec,// gProbConjunctedProbMass,// gExecutionExit_E,// gDurationOutsideBoundsInOK_E,// gDurationOutsideBoundsInNOTOK_E,// gExecutionExit_M,// gDurationOutsideBoundsInOK_M,// gDurationOutsideBoundsInNOTOK_M// ); LOG.info(getNow() + ": Executing PSTN: finished."); output.println(rowToWrite); output.flush(); runState.printProgress(); return !rowToWrite.contains("NOT EXECUTABLE"); } }