// 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.Object2IntMap.Entry; import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair; import it.univr.di.cstnu.algorithms.AbstractCSTN.CSTNCheckStatus; import it.univr.di.cstnu.algorithms.AbstractCSTN.DCSemantics; import it.univr.di.cstnu.algorithms.*; import it.univr.di.cstnu.algorithms.CSTNU.CSTNUCheckStatus; import it.univr.di.cstnu.algorithms.STN.STNCheckStatus; import it.univr.di.cstnu.algorithms.STNU.CheckAlgorithm; import it.univr.di.cstnu.algorithms.STNU.STNUCheckStatus; import it.univr.di.cstnu.graph.*; import it.univr.di.labeledvalue.Constants; import it.univr.di.labeledvalue.Label; 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 org.xml.sax.SAXException; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.xml.parsers.ParserConfigurationException; 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.*; import java.util.concurrent.CancellationException; import java.util.logging.Level; import java.util.logging.Logger; /** * Simple class to determine the average execution time (and standard dev) of the (C)STN(U) DC checking algorithm on a given set of (C)STN(U)s. * * @author posenato * @version $Rev: 732 $ */ @SuppressWarnings({"NonAsciiCharacters", "AutoBoxing", "FieldMayBeFinal"}) @SuppressFBWarnings(value = "STCAL", justification = "It is not relevant here!") public class Checker { /** * Utility internal class to store #contingent, #nodes, and #propositions and allows the ordering among objects of this class. * * @author posenato */ public static class GlobalStatisticKey implements Comparable { /** * # contingents */ protected final int contingents; /** * # of nodes */ protected final int nodes; /** * # of propositions */ protected final int propositions; /** * default constructor * * @param inputNodes #nodes * @param inputPropositions #prop * @param inputContingents # cont */ public GlobalStatisticKey(final int inputNodes, final int inputPropositions, final int inputContingents) { nodes = inputNodes; propositions = inputPropositions; 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'. */ @SuppressWarnings("SubtractionInCompareTo") @Override public final int compareTo(@Nonnull GlobalStatisticKey o) { final long d = (long) nodes - o.nodes; if (d == 0) { final int d1 = contingents - o.contingents; if (d1 == 0) { return propositions - o.propositions; } return d1; } return (int) d; } @Override public final boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof GlobalStatisticKey o1)) { return false; } return o1.compareTo(this) == 0; } /** * @return #cont */ public final int getContingent() { return contingents; } /** * @return #nodes */ public final int getNodes() { return nodes; } /** * @return # prop */ public final int getPropositions() { return propositions; } @Override public final int hashCode() { return (contingents + propositions * 31) * 31 + nodes; } } /** * CSV separator */ static final String CSVSep = ";\t"; /** * */ static final String GLOBAL_HEADER_ROW = "%d" + CSVSep // + "%d" + CSVSep // + "%d" + CSVSep // + "%d" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%E" + CSVSep // + "%n"; /** * class logger */ static final Logger LOG = Logger.getLogger(Checker.class.getName()); /** * Output file header */ static final String OUTPUT_HEADER = "fileName" + CSVSep // + "#nodes" + CSVSep // + "#edges" + CSVSep // + "#propositions" + CSVSep // + "#ctg" + CSVSep // + "#minEdgeValue" + CSVSep // + "#maxEdgeValue" + CSVSep// + "avgTime[s]" + CSVSep // + "std.dev.[s]" + CSVSep // + "#ruleExecuted/cycles" // + CSVSep + "DC";// /** * Header */ static final String OUTPUT_HEADER_STNU = OUTPUT_HEADER + CSVSep // + "rateAddedEdges" + CSVSep // + "rateWaitAndOrdinaryEdge" + CSVSep // + "negativeEdgeFromContingent" + CSVSep;// /** * CSTN header */ static final String OUTPUT_HEADER_CSTN = OUTPUT_HEADER + CSVSep // + "#R0" + CSVSep // + "#R3" + CSVSep // + "#LP" + CSVSep // + "#PotentialUpdate" + CSVSep;// /** * CSTNU header */ static final String OUTPUT_HEADER_CSTNU = OUTPUT_HEADER_CSTN + "#LUC+FLUC+LCUC"// upperCaseRuleCalls + CSVSep + "#LLC"// lowerCaseRuleCalls + CSVSep + "#LCC"// crossCaseRuleCalls + CSVSep + "#LLR"// letterRemovalRuleCalls + CSVSep; /** * CSTNPSU header */ static final String OUTPUT_HEADER_CSTNPSU = OUTPUT_HEADER_CSTN // + "PrototypalTime[s]" + CSVSep // + "PrototypalTimeStdDev[s]" + CSVSep // + "Prototypal" + CSVSep;// /** * */ static final String OUTPUT_ROW_GRAPH = "%s" + CSVSep // + "%d" + CSVSep // + "%d" + CSVSep // + "%d" + CSVSep // + "%d" + CSVSep // + "%d" + CSVSep // + "%d" + CSVSep;// /** * */ static final String OUTPUT_ROW_TIME = "%E"// average time + CSVSep + "%E"// std dev + CSVSep + "%d"// #ruleExecuted or cycles + CSVSep + "%s"// true of false for DC + CSVSep; /** * */ static final String OUTPUT_ROW_TIME_CSTN = "%d"// r0 + CSVSep + "%d"// r3 + CSVSep + "%d"// LNC + CSVSep + "%d"// PotentialUpdate + CSVSep; /** * For CSTNPSU statistics */ static final String OUTPUT_ROW_TIME_STATS_CSTNPSU = "%E"// PrototypalTime[s] + CSVSep + "%s" //std dev of prototypal time + CSVSep + "%s"// Prototypal + CSVSep; /** * */ static final String OUTPUT_ROW_TIME_STATS_CSTNU = "%d"// upperCaseRuleCalls + CSVSep + "%d"// lowerCaseRuleCalls + CSVSep + "%d"// crossCaseRuleCalls + CSVSep + "%d"// letterRemovalRuleCalls + CSVSep; /** * */ static final String OUTPUT_ROW_TIME_STNU = "%E"// added edges + CSVSep + "%E" // waitAndOrdEdgeRate + CSVSep + "%E" // negativeOutgoingFromC + CSVSep; /** * Version */ // static final String VERSIONandDATE = "1.0, March, 22 2015"; // static final String VERSIONandDATE = "1.1, November, 18 2015"; // static final String VERSIONandDATE = "1.2, October, 10 2017"; // static final String VERSIONandDATE = "1.3, October, 16 2017";// executor code cleaned // static final String VERSIONandDATE = "1.4, November, 09 2017";// code cleaned // static final String VERSIONandDATE = "2.0, November, 13 2017";// Multi-thread version. // static final String VERSIONandDATE = "2.1, November, 14 2017";// Multi-thread version. Fixed a slip! // static final String VERSIONandDATE = "2.2, November, 15 2017";// Added the possibility to test // CSTNEpsilonWONodeLabels and CSTN2CSTN0 // static final String VERSIONandDATE = "2.23, November 30, 2017";// Improved the print of statistics file: std dev is printed only when # checks > 1 // static final String VERSIONandDATE = "2.24, December, 04 2017";// Added CSTNEpsilon3R // static final String VERSIONandDATE = "2.25, January, 17 2018";// Improved print of statistics. // static final String VERSIONandDATE = "2.26, December 18, 2018";// Improved print of statistics, adding the total number of applied rules // static final String VERSIONandDATE = "2.27, June, 9 2019";// Refactoring Edge // static final String VERSIONandDATE = "2.5, November, 09 2019";// Removed all potential counters // static final String VERSIONandDATE = "3, June, 29 2020";// Add check for STN and STNU // static final String VERSIONandDATE = "3.1, July, 28 2020";// Refined stats for STNU // static final String VERSIONandDATE = "3.2, January, 13 2021";// Fixed file encoding // static final String VERSIONandDATE = "3.5, January, 13 2022"; static final String VERSIONandDATE = "3.6, January 13, 2022"; // Cleaned after refactoring CSTNUEdge /** * Global header */ static final String GLOBAL_HEADER = "%n%nGlobal statistics%n" // + "#Networks" + CSVSep // + "#nodes" + CSVSep // + "#contingent" + CSVSep // + "#propositions" + CSVSep // + "avgExeTime[s]" + CSVSep // + "std.dev.[s]" + CSVSep // + "avgRules/Cycles" + CSVSep // + "std.dev." + CSVSep // + "avgAddedEdgesRate" + CSVSep // + "std.dev." + CSVSep // + "avgWaitAndOrdinaryEdgesRate" + CSVSep // + "std.dev." + CSVSep // + "avgNegativeFromCtgEdgesRate" + CSVSep // + "std.dev." + CSVSep // + "avgPartialExeTime[s]" + CSVSep // + "stdDevPartialExeTime[s]" + CSVSep // + "%n"; /** * Date formatter */ private final static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); // added STNU statistics about waitAndOrdinaryEdge and negativeEdgeFromContingent /** * Allows checking the execution time of a DC checking algorithm, given a set of instances. *

* Each input file is checked sequentially. The former {@code --nCPUs} option only ran * independent files concurrently; it never parallelized an algorithm on one graph. Measurements * found no throughput benefit: these memory-bound computations contend for bandwidth, caches, * garbage collection, and possibly NUMA memory. CPU pinning and a standard Java thread pool * preserve that contention, so both were removed. * * @param args an array of {@link java.lang.String} objects. */ @SuppressWarnings("null") public static void main(String[] args) { LOG.finest("Checker %s\nStart...".formatted(VERSIONandDATE)); System.out.printf("Checker %s\n%s: Start of execution.%n", VERSIONandDATE, getNow()); final Checker tester = new Checker(); if (!tester.manageParameters(args)) { return; } LOG.finest("Parameters ok!"); if (tester.versionReq) { return; } // All parameters are set /* * To collect statistics w.r.t. the dimension of networks */ final Object2ObjectMap groupExecutionTimeStatisticsInSec = new Object2ObjectAVLTreeMap<>(); final Object2ObjectMap groupPrototypalExecutionTimeStatisticsInSec = new Object2ObjectAVLTreeMap<>(); final Object2ObjectMap groupRuleExecutionStatistics = new Object2ObjectAVLTreeMap<>(); final Object2ObjectMap groupAddedEdgeStatistics = new Object2ObjectAVLTreeMap<>(); final Object2ObjectMap groupWaitAndOrdinaryEdgeStatistics = new Object2ObjectAVLTreeMap<>(); final Object2ObjectMap groupNegativeFromContingentEdgeStatistics = new Object2ObjectAVLTreeMap<>(); System.out.printf("%s: Processing instances sequentially.%n", getNow()); System.out.printf("%s: Instances to check are %s instances.%n", getNow(), tester.networkType); 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(tester.getHeader()); tester.output.flush(); int nTaskSuccessfullyFinished = 0; for (final File file : tester.instances) { if (tester.worker(file, runMeter, groupExecutionTimeStatisticsInSec, groupRuleExecutionStatistics, groupAddedEdgeStatistics, groupWaitAndOrdinaryEdgeStatistics, groupNegativeFromContingentEdgeStatistics, groupPrototypalExecutionTimeStatisticsInSec)) { nTaskSuccessfullyFinished++; } } final String msg = "Number of instances processed successfully over total: %d/%d.".formatted(nTaskSuccessfullyFinished, tester.instances.size()); LOG.info(msg); System.out.println("\n" + getNow() + ": " + msg); tester.output.printf(GLOBAL_HEADER); for (final it.unimi.dsi.fastutil.objects.Object2ObjectMap.Entry entry : groupExecutionTimeStatisticsInSec.object2ObjectEntrySet()) { final GlobalStatisticKey globalStatisticsKey = entry.getKey(); tester.output.printf(GLOBAL_HEADER_ROW, entry.getValue().getN(), globalStatisticsKey.getNodes(), globalStatisticsKey.getContingent(), globalStatisticsKey.getPropositions(), entry.getValue().getMean(), entry.getValue().getStandardDeviation(), groupRuleExecutionStatistics.get(globalStatisticsKey).getMean(), groupRuleExecutionStatistics.get(globalStatisticsKey).getStandardDeviation(), groupAddedEdgeStatistics.get(globalStatisticsKey).getMean(), groupAddedEdgeStatistics.get(globalStatisticsKey).getStandardDeviation(), groupWaitAndOrdinaryEdgeStatistics.get(globalStatisticsKey).getMean(), groupWaitAndOrdinaryEdgeStatistics.get(globalStatisticsKey).getStandardDeviation(), groupNegativeFromContingentEdgeStatistics.get(globalStatisticsKey).getMean(), groupNegativeFromContingentEdgeStatistics.get(globalStatisticsKey).getStandardDeviation(), groupPrototypalExecutionTimeStatisticsInSec.get(globalStatisticsKey).getMean(), groupPrototypalExecutionTimeStatisticsInSec.get(globalStatisticsKey).getStandardDeviation()); } tester.output.printf("%n%n%n"); tester.output.close(); } /** * @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; } /** * */ EdgeSupplier currentEdgeFactory; /** * Class for representing an edge. */ Class currentEdgeImplClass; /** * Parameter for asking DC semantics. */ @Option(name = "--semantics", usage = "DC semantics. Possible values are: IR, ε, Std. Default is Std.") private DCSemantics dcSemantics = DCSemantics.Std; /** * */ private List instances; /** * Parameter for asking how many times to check the DC for each CSTN. */ @Option(name = "--numRepetitionDCCheck", usage = "Number of times to re-execute DC checking") private int nDCRepetition = 30; /** * Parameter for avoiding DC check */ @Option(name = "--noDCCheck", usage = "Do not execute DC check. Determine graph characteristics only.") private boolean noDCCheck; /** * Parameter for asking whether to consider only Lp, qR0, qR3* rules. */ @Option(name = "--onlyLPQR0QR3", aliases = "--limitToZ", depends = "--semantics", usage = "Check DC considering only rules LP, qR0 and QR3.") private boolean onlyLPQR0QR3OrToZ; /** * Output stream to outputFile */ private PrintStream output; /** * Check a CSTN instance using the Bellman-Ford algorithm */ @Option(name = "--potential", depends = "--type cstn", usage = "Check a CSTN instance using BellmanFord algorithm.") private boolean potential; /** * Parameter for asking reaction time. */ @Option(name = "--reactionTime", depends = "--semantics ε", usage = "Reaction time. It must be > 0.") private int reactionTime = 1; /** * */ @Option(name = "--save", usage = "Save all checked instances.") private boolean save; /** * Parameter for asking timeout in sec. */ @Option(name = "--timeOut", usage = "Time in seconds.") private int timeOut = 1200; // 20 min /** * Software Version. */ @Option(name = "-v", aliases = "--version", usage = "Version") private boolean versionReq; /** * Parameter for asking DC to check epsilon DC reducing to IR DC */ @Option(name = "--cstn2cstn0", usage = "Check the epsilon-DC of the input instance using IR-DC on a corresponding streamlined CSTN. It transforms the input CSTN and then checks its IR-DC. Epsilon value can be specified using the '-reactionTime' parameter.") private boolean cstn2cstn0; /** * Parameter for asking DC to check epsilon DC reducing to IR DC */ @Option(name = "--cstnu2cstn", usage = "Check the CSTNU DC of the input instance reducing the check to the equivalent streamlined CSTN instance and checking this last one using IR-DC. It is incompatible with the '-potential' option.") private boolean cstnu2cstn; /** * Parameter for asking how much to cut all edge values (for studying pseudo-polynomial characteristics) */ @Option(name = "--cuttingEdgeFactor", usage = "Cutting factor for reducing edge values. The default value is 1, i.e., no cut.") private int cuttingEdgeFactor = 1; /** * 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 a CSTN graph in GraphML format.", metaVar = "CSTN_file_names", handler = StringArrayOptionHandler.class) private String[] inputFiles; /** * What type of CSTN are the input files? */ @Option(required = true, name = "--type", usage = "Specify if input files contain STN, STNU, CSTN, CSTNU or CSTNPSU instances (use one of such keywords: STN STNU CSTN CSTNU CSTNPSU).") private TemporalConstraintNetworkType networkType = TemporalConstraintNetworkType.CSTN; /** * 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 a file is already present, data will be added.", metaVar = "outputFile") private File outputFile; /** * Parameter for asking to remove a value from all constraints. */ @Option(name = "--removeValue", usage = "Value to be removed from any edge. Default value is a value representing NULL.") private int removeValue = Constants.INT_NULL; /** * Parameter to ask which algorithm to use to check STNU. */ @Option(name = "--stnuCheck", usage = "Which algorithm to use for checking STNU network. (use one of such keywords: Morris2014 RUL2018 RUL2021 FastSTNUdispatch)") private STNU.CheckAlgorithm stnuCheckAlgorithm = CheckAlgorithm.RUL2021; /** * Parameter for asking whether to consider node labels during the DC check. */ @Option(name = "--woNodeLabels", usage = "Check DC transforming the network in an equivalent CSTN without node labels.") private boolean woNodeLabels; /** * For CSTNPSU (FTNU), it determines the prototypal link. In this way, executionTime is the time for DC checking, and when the instance is DC, partialExecutionTime is the time for PrototypalLink determination. The sum of these two values is the global time for the method. * * @param cstnpsu instance * @param ignoredRunState ignored * * @return the status of the check */ private CSTNCheckStatus CSTNPSUChecker(CSTNPSU cstnpsu, RunMeter ignoredRunState) { String msg; boolean checkInterrupted = false; CSTNCheckStatus status; final TemporalConstraintGraph graphToCheck; status = new CSTNUCheckStatus(); graphToCheck = cstnpsu.getGraph(); final SummaryStatistics localSummaryStat = new SummaryStatistics(); final SummaryStatistics localSummaryStat4PrototypalTime = new SummaryStatistics(); CSTNPSU.PrototypalLink prototypal = null; for (int j = 0; j < nDCRepetition && !checkInterrupted && !status.timeout; j++) { LOG.info("Test %d/%d for CSTNPSU %s".formatted(j + 1, nDCRepetition, Objects.requireNonNull(graphToCheck.getInputFile()).getName())); // It is necessary to reset the graph! cstnpsu.setG(graphToCheck.newInstance(graphToCheck)); try { prototypal = cstnpsu.getPrototypalLink(); status = cstnpsu.getCheckStatus(); } catch (CancellationException ex) { msg = "%s: Cancellation has occurred. %s CSTN(U) is ignored.".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } catch (Exception e) { msg = "%s: a different exception has occurred. %s CSTN(U) is ignored.\nError details: %s".formatted(getNow(), graphToCheck.getInputFile(), e); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } localSummaryStat.addValue(status.executionTimeNS); localSummaryStat4PrototypalTime.addValue(status.partialExecutionTimeNS);//partial time // for the determination of the prototypal link after the DC checking. } // end for checking repetition for a single file if (status.timeout || checkInterrupted) { if (status.timeout) { msg = "\n\n%s: Timeout or interrupt occurred. %s CSTN(U) is ignored.\n".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); } return status; } msg = "%s: done! It is %sDC.".formatted(getNow(), (!status.consistency) ? "NOT " : ""); // System.out.println(msg); LOG.info(msg); status.executionTimeNS = (long) localSummaryStat.getMean(); status.stdDevExecutionTimeNS = (long) localSummaryStat.getStandardDeviation(); status.partialExecutionTimeNS = (long) localSummaryStat4PrototypalTime.getMean(); status.stdDevPartialExecutionTimeNS = (long) localSummaryStat4PrototypalTime.getStandardDeviation(); status.note = (prototypal != null) ? prototypal.toString() : "not determined"; return status; } @Nullable private CSTNCheckStatus DCChecker(CSTN cstn, CSTNU cstnu, RunMeter ignoredRunState) { String msg; boolean checkInterrupted = false; CSTNCheckStatus status; if (isCSTNU()) { final TemporalConstraintGraph graphToCheck = cstnu.getGraph(); status = new CSTNUCheckStatus(); final SummaryStatistics localSummaryStat = new SummaryStatistics(); for (int j = 0; j < nDCRepetition && !checkInterrupted && !status.timeout; j++) { LOG.info("Test %d/%d for (C)STN(U) %s".formatted(j + 1, nDCRepetition, Objects.requireNonNull(graphToCheck.getInputFile()).getName())); // It is necessary to reset the graph! cstnu.setG(graphToCheck.newInstance(graphToCheck)); try { status = cstnu.dynamicControllabilityCheck(); } catch (CancellationException ex) { msg = "%s: Cancellation has occurred. %s CSTN(U) is ignored.".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } catch (Exception e) { msg = "%s: a different exception has occurred. %s CSTN(U) is ignored.\nError details: %s".formatted(getNow(), graphToCheck.getInputFile(), e); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } localSummaryStat.addValue(status.executionTimeNS); } // end for checking repetition for a single file if (status.timeout || checkInterrupted) { if (status.timeout) { msg = "\n\n%s: Timeout or interrupt occurred. %s CSTN(U) is ignored.\n".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); } return status; } msg = "%s: done! It is %sDC.".formatted(getNow(), (!status.consistency) ? "NOT " : ""); // System.out.println(msg); LOG.info(msg); status.executionTimeNS = (long) localSummaryStat.getMean(); status.stdDevExecutionTimeNS = (long) localSummaryStat.getStandardDeviation(); return status; } if (isCSTN()) { final TemporalConstraintGraph graphToCheck = cstn.getGraph(); status = new CSTNCheckStatus(); final SummaryStatistics localSummaryStat = new SummaryStatistics(); for (int j = 0; j < nDCRepetition && !checkInterrupted && !status.timeout; j++) { LOG.info("Test %d/%d for (C)STN(U) %s".formatted(j + 1, nDCRepetition, Objects.requireNonNull(graphToCheck.getInputFile()).getName())); // It is necessary to reset the graph! cstn.setG(graphToCheck.newInstance(graphToCheck)); try { status = cstn.dynamicConsistencyCheck(); } catch (CancellationException ex) { msg = "%s: Cancellation has occurred. %s CSTN(U) is ignored.".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } catch (Exception e) { msg = "%s: a different exception has occurred. %s CSTN(U) is ignored.\nError details: %s".formatted(getNow(), graphToCheck.getInputFile(), e); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } localSummaryStat.addValue(status.executionTimeNS); } // end for checking repetition for a single file if (status.timeout || checkInterrupted) { if (status.timeout) { msg = "\n\n%s: Timeout or interrupt occurred. %s CSTN(U) is ignored.\n".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); } return status; } msg = "%s: done! It is %sDC.".formatted(getNow(), (!status.consistency) ? "NOT " : ""); // System.out.println(msg); LOG.info(msg); status.executionTimeNS = (long) localSummaryStat.getMean(); status.stdDevExecutionTimeNS = (long) localSummaryStat.getStandardDeviation(); return status; } return null;// we check only cstn or cstnu } @Nullable private STNCheckStatus STNUDCChecker(STN stn, STNU stnu, RunMeter ignoredRunState) { String msg; boolean checkInterrupted = false; STNCheckStatus status; if (isSTN()) { final TemporalConstraintGraph graphToCheck = stn.getGraph(); status = new STNCheckStatus(); final SummaryStatistics localSummaryStat = new SummaryStatistics(); for (int j = 0; j < nDCRepetition && !checkInterrupted && !status.timeout; j++) { LOG.info("Test %d/%d for STN(U) %s".formatted(j + 1, nDCRepetition, Objects.requireNonNull(graphToCheck.getInputFile()).getName())); // It is necessary to reset the graph! stn.setG(graphToCheck.newInstance(graphToCheck)); try { status = stn.consistencyCheck(); } catch (CancellationException ex) { msg = "%s: Cancellation has occurred. %s STN(U) is ignored.".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } catch (Exception e) { msg = "%s: a different exception has occurred. %s STN(U) is ignored.\nError details: %s".formatted(getNow(), graphToCheck.getInputFile(), e); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } localSummaryStat.addValue(status.executionTimeNS); } // end for checking repetition for a single file if (status.timeout || checkInterrupted) { if (status.timeout) { msg = "\n\n%s: Timeout or interrupt occurred. %s STN(U) is ignored.\n".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); } return status; } msg = "%s: done! It is %sDC.".formatted(getNow(), (!status.consistency) ? "NOT " : ""); // System.out.println(msg); LOG.info(msg); status.executionTimeNS = (long) localSummaryStat.getMean(); status.stdDevExecutionTimeNS = (long) localSummaryStat.getStandardDeviation(); return status; } if (isSTNU()) { final TemporalConstraintGraph graphToCheck = stnu.getGraph(); status = new STNUCheckStatus(); final SummaryStatistics localSummaryStat = new SummaryStatistics(); for (int j = 0; j < nDCRepetition && !checkInterrupted && !status.timeout; j++) { LOG.info("Test %d/%d for STN(U) %s".formatted(j + 1, nDCRepetition, Objects.requireNonNull(graphToCheck.getInputFile()).getName())); // It is necessary to reset the graph! stnu.setG(graphToCheck.newInstance(graphToCheck)); try { status = stnu.dynamicControllabilityCheck(stnuCheckAlgorithm); } catch (CancellationException ex) { msg = "%s: Cancellation has occurred. %s STN(U) is ignored.".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } catch (Exception e) { msg = "%s: a different exception has occurred. %s STN(U) is ignored.\nError details: %s".formatted(getNow(), graphToCheck.getInputFile(), e); System.out.println(msg); LOG.severe(msg); checkInterrupted = true; status.consistency = false; continue; } localSummaryStat.addValue(status.executionTimeNS); } // end for checking repetition for a single file if (status.timeout || checkInterrupted) { if (status.timeout) { msg = "\n\n%s: Timeout or interrupt occurred. %s STN(U) is ignored.\n".formatted(getNow(), graphToCheck.getInputFile()); System.out.println(msg); } return status; } msg = "%s: done! It is %sDC.".formatted(getNow(), (!status.consistency) ? "NOT " : ""); // System.out.println(msg); LOG.info(msg); status.executionTimeNS = (long) localSummaryStat.getMean(); status.stdDevExecutionTimeNS = (long) localSummaryStat.getStandardDeviation(); return status; } return null;// check only STN or STNU } private String getHeader() { if (networkType == TemporalConstraintNetworkType.CSTN) { return (OUTPUT_HEADER_CSTN + CSVSep); } if (networkType == TemporalConstraintNetworkType.CSTNU) { return (OUTPUT_HEADER_CSTNU + CSVSep); } if (networkType == TemporalConstraintNetworkType.CSTNPSU) { return (OUTPUT_HEADER_CSTNPSU + CSVSep); } if (networkType == TemporalConstraintNetworkType.STNU) { return (OUTPUT_HEADER_STNU + CSVSep); } if (networkType == TemporalConstraintNetworkType.STN) { return (OUTPUT_HEADER + CSVSep); } return ""; } /** * @param status current status * * @return the number of executed rules. */ private int getNumberExecutedRules(STNCheckStatus status) { int nRules = status.propagationCalls; if (isCSTN()) { final CSTNCheckStatus s = ((CSTNCheckStatus) status); nRules = s.r0calls + s.r3calls + s.labeledValuePropagationCalls + s.potentialUpdate; } if (isSTNU()) { final STNUCheckStatus s = ((STNUCheckStatus) status); nRules = s.cycles; } if (isCSTNU()) { nRules += ((CSTNUCheckStatus) status).zExclamationRuleCalls + ((CSTNUCheckStatus) status).lowerCaseRuleCalls + ((CSTNUCheckStatus) status).crossCaseRuleCalls + ((CSTNUCheckStatus) status).letterRemovalRuleCalls; } return nRules; } /** * @return true if the input graph represents a CSTN instance */ private boolean isCSTN() { return networkType == TemporalConstraintNetworkType.CSTN; } /** * @return true if the input graph represents a CSTNPSU instance */ private boolean isCSTNPSU() { return networkType == TemporalConstraintNetworkType.CSTNPSU; } /** * @return true if the input graph represents a CSTNU instance */ private boolean isCSTNU() { return networkType == TemporalConstraintNetworkType.CSTNU; } /** * @return true if the input graph represents an STN instance */ private boolean isSTN() { return networkType == TemporalConstraintNetworkType.STN; } /** * @return true if the input graph represents an STNU instance */ private boolean isSTNU() { return networkType == TemporalConstraintNetworkType.STNU; } /** * @param g input graph * * @return a cstn instance */ private CSTN makeCSTNInstance(TemporalConstraintGraph g) { if (cstn2cstn0) { return new CSTN2CSTN0(reactionTime, g, timeOut); } if (potential) {// the implicit semantics is IR return new CSTNPotential(g, timeOut); } CSTN cstn = null; switch (dcSemantics) { case ε -> { if (onlyLPQR0QR3OrToZ) { cstn = (woNodeLabels) ? new CSTNEpsilon3RwoNodeLabels(reactionTime, g, timeOut) : new CSTNEpsilon3R(reactionTime, g, timeOut); } else { cstn = (woNodeLabels) ? new CSTNEpsilonwoNodeLabels(reactionTime, g, timeOut) : new CSTNEpsilon(reactionTime, g, timeOut); } } case IR -> { if (onlyLPQR0QR3OrToZ) { cstn = (woNodeLabels) ? new CSTNIR3RwoNodeLabels(g, timeOut) : new CSTNIR3R(g, timeOut); } else { cstn = (woNodeLabels) ? new CSTNIRwoNodeLabels(g, timeOut) : new CSTNIR(g, timeOut); } } case Std -> { if (onlyLPQR0QR3OrToZ) { throw new IllegalArgumentException("For standard semantics there is no DC checking algorithm that works only considering constraints ending to Z."); } cstn = (woNodeLabels) ? new CSTNwoNodeLabel(g, timeOut) : new CSTN(g, timeOut); } } return cstn; } /** * @param g input graph * * @return a cstnu instance */ private CSTNPSU makeCSTNPSUInstance(TemporalConstraintGraph g) { if (isCSTNPSU()) { return new CSTNPSU(g, timeOut); } throw new IllegalArgumentException("Required a CSTNPSU instance, but the graph is not a CSTNU graph."); } /** * @param g input graph * * @return a cstnu instance */ private CSTNU makeCSTNUInstance(TemporalConstraintGraph g) { if (isCSTNU()) { if (cstnu2cstn) { return new CSTNU2CSTN(g, timeOut); } return new CSTNU(g, timeOut, onlyLPQR0QR3OrToZ); } throw new IllegalArgumentException("Required a CSTNU instance, but the graph is not a CSTNU graph."); } /** * @param g input graph * * @return an STN instance */ private STN makeSTNInstance(DenseTCGraph g) { if (isSTN()) { return new STN(g, timeOut); } throw new IllegalArgumentException("Required an STN instance, but the input graph is not an STN graph."); } /** * @param g input graph * * @return a stnu instance */ private STNU makeSTNUInstance(TemporalConstraintGraph g) { if (isSTNU()) { final STNU stnu = new STNU(g, timeOut); stnu.setSave(save); return stnu; } throw new IllegalArgumentException("Required a STNU instance, but the input graph is not a STNU graph."); } /** * 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.Checker [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-2025, 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("%s.csv".formatted(outputFile.getAbsolutePath())))) { 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; } if (reactionTime <= 0) { System.err.println("Reaction time must be > 0"); return false; } final String suffix; //If you add a new type, update it, and getHeader(). if (networkType == TemporalConstraintNetworkType.CSTN) { currentEdgeImplClass = CSTNEdgePluggable.class; suffix = "cstn"; } else { if (networkType == TemporalConstraintNetworkType.CSTNU) { currentEdgeImplClass = CSTNUEdgePluggable.class; suffix = "cstnu"; } else { if (networkType == TemporalConstraintNetworkType.CSTNPSU) { currentEdgeImplClass = CSTNPSUEdgePluggable.class; suffix = "cstnpsu"; } else { if (networkType == TemporalConstraintNetworkType.STNU) { currentEdgeImplClass = STNUEdgeInt.class; suffix = "stnu"; } else { if (networkType == TemporalConstraintNetworkType.STN) { currentEdgeImplClass = STNEdgeInt.class; suffix = "stn"; } else { final String msg = "Type of network is not managed by the current version of this class. Game over :-/"; throw new IllegalArgumentException(msg); } } } } } currentEdgeFactory = new EdgeSupplier<>(currentEdgeImplClass); // LOG.finest("File number: "+ this.fileNameInput.length); // LOG.finest("File names: " + Arrays.deepToString(this.fileNameInput)); instances = new ArrayList<>(inputFiles.length); for (final String fileName : inputFiles) { final File file = new File(fileName); if (!file.exists()) { System.err.println("File " + fileName + " does not exit."); 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 parameter) on the network represented by the * file. * * @param type of edge * @param file input file * @param runState current state * @param globalExecutionTimeStatisticsInSecMap global statistics * @param globalRuleExecutionStatisticsMap another global statistics * @param globalAddedEdgeStatisticsMap only for STNU * @param globalPrototypalTimeStatisticsInSecMap only for CSTNPSU * * @return true if a required task ends successfully, false otherwise. */ @SuppressWarnings({"unchecked", "null"}) private boolean worker(File file, RunMeter runState, Object2ObjectMap globalExecutionTimeStatisticsInSecMap, Object2ObjectMap globalRuleExecutionStatisticsMap, Object2ObjectMap globalAddedEdgeStatisticsMap, Object2ObjectMap globalWaitAndOrdEdgeStatisticsMap, Object2ObjectMap globalNegativeFromContingentStatisticsMap, Object2ObjectMap globalPrototypalTimeStatisticsInSecMap) { // System.out.println("Analyzing file " + file.getName() + "..."); if (LOG.isLoggable(Level.FINER)) { LOG.finer("Loading " + file.getName() + "..."); } final TCGraphMLReader graphMLReader = new TCGraphMLReader<>(); final DenseTCGraph graphToCheck; try { graphToCheck = graphMLReader.readGraph(file, (Class) currentEdgeImplClass); } catch (IOException | ParserConfigurationException | SAXException 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!"); /* * ************************************************* * Possible required modifications of the structure. * ************************************************* */ if (cuttingEdgeFactor > 1 || removeValue != Constants.INT_NULL) { if (cuttingEdgeFactor > 1) { LOG.info("Cutting all edge values by a factor %d...".formatted(cuttingEdgeFactor)); for (final Edge e : graphToCheck.getEdges()) { if (e.isSTNEdge()) { ((STNEdge) e).setValue(((STNEdge) e).getValue() / cuttingEdgeFactor); } if (isCSTN() || isCSTNU()) { final CSTNEdge eNew = (CSTNEdge) currentEdgeFactory.get(e); for (final Entry