Java tutorial
/* * Copyright (c) 2010, 2011 AnjLab * * This file is part of * Reference Implementation of Romanov's Polynomial Algorithm for 3-SAT Problem. * * Reference Implementation of Romanov's Polynomial Algorithm for 3-SAT Problem * is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Reference Implementation of Romanov's Polynomial Algorithm for 3-SAT Problem * is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with * Reference Implementation of Romanov's Polynomial Algorithm for 3-SAT Problem. * If not, see <http://www.gnu.org/licenses/>. */ package com.anjlab.sat3; import static com.anjlab.sat3.Helper.printFormulas; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Properties; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.PosixParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cern.colt.list.ObjectArrayList; public class Program { private static final String GENERATE_3SAT_OPTION = "g"; private static final String FIND_HSS_ROUTE_OPTION = "r"; private static final String CREATE_SKT_OPTION = "c"; private static final String EVALUATE_OPTION = "e"; private static final String RESULTS_OUTPUT_FILE_OPTION = "o"; private static final String HELP_OPTION = "h"; private static final String HSS_IMAGE_OUTPUT_FILENAME_OPTION = "i"; private static final String USE_ABC_VAR_NAMES_OPTION = "u"; private static final String DISABLE_ASSERTIONS_OPTION = "a"; private static final String USE_PRETTY_PRINT_OPTION = "p"; private static final Logger LOGGER = LoggerFactory.getLogger(Program.class); public static void main(String[] args) throws Exception { System.out.println("Reference Implementation of Romanov's Polynomial Algorithm for 3-SAT Problem" + "\nCopyright (c) 2010 AnjLab" + "\nThis program comes with ABSOLUTELY NO WARRANTY." + "\nThis is free software, and you are welcome to redistribute it under certain conditions." + "\nSee LICENSE.txt file or visit <http://www.gnu.org/copyleft/lesser.html> for details."); LOGGER.debug("Reading version number from manifest"); String implementationVersion = Helper.getImplementationVersionFromManifest("3-SAT Core RI"); System.out.println("Version: " + implementationVersion + "\n"); Options options = getCommandLineOptions(); CommandLineParser parser = new PosixParser(); CommandLine commandLine = parser.parse(options, args); if (commandLine.getArgs().length != 1 || commandLine.hasOption(HELP_OPTION)) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp(Program.class.getName() + " [OPTIONS] <input-file-name>" + "\nWhere <input-file-name> is a path to file containing k-SAT formula instance in DIMACS CNF or Romanov SKT file format.", options); System.exit(0); } String formulaFile = commandLine.getArgs()[0]; Helper.UsePrettyPrint = commandLine.hasOption(USE_PRETTY_PRINT_OPTION); Helper.EnableAssertions = !commandLine.hasOption(DISABLE_ASSERTIONS_OPTION); Helper.UseUniversalVarNames = !commandLine.hasOption(USE_ABC_VAR_NAMES_OPTION); Properties statistics = new Properties(); StopWatch stopWatch = new StopWatch(); try { statistics.put(Helper.IMPLEMENTATION_VERSION, implementationVersion); stopWatch.start("Load formula"); ITabularFormula formula = Helper.loadFromFile(formulaFile); long timeElapsed = stopWatch.stop(); statistics.put(Helper.INITIAL_FORMULA_LOAD_TIME, String.valueOf(timeElapsed)); if (commandLine.hasOption(GENERATE_3SAT_OPTION)) { String generated3SatFilename = formulaFile + "-3sat.cnf"; LOGGER.info("Saving 3-SAT formula to {}...", generated3SatFilename); Helper.saveToDIMACSFileFormat(formula, generated3SatFilename); } if (formula.getVarCount() > 26) { LOGGER.info("Variables count > 26 => force using universal names for variables."); Helper.UseUniversalVarNames = true; } statistics.put(Helper.INITIAL_FORMULA_VAR_COUNT, String.valueOf(formula.getVarCount())); statistics.put(Helper.INITIAL_FORMULA_CLAUSES_COUNT, String.valueOf(formula.getClausesCount())); Helper.prettyPrint(formula); stopWatch.printElapsed(); if (commandLine.hasOption(FIND_HSS_ROUTE_OPTION)) { String hssPath = commandLine.getOptionValue(FIND_HSS_ROUTE_OPTION); stopWatch.start("Load HSS from " + hssPath); ObjectArrayList hss = Helper.loadHSS(hssPath); stopWatch.stop(); stopWatch.printElapsed(); findHSSRoute(commandLine, formulaFile, statistics, stopWatch, formula, null, null, hss, hssPath); return; } if (commandLine.hasOption(EVALUATE_OPTION)) { String resultsFilename = commandLine.getOptionValue(EVALUATE_OPTION); boolean satisfiable = evaluateFormula(stopWatch, formula, resultsFilename); if (satisfiable) { System.out.println("Formula evaluated as SAT"); } else { System.out.println("Formula evaluated as UNSAT"); } // Only evaluate formula value return; } // Find if formula is SAT // Clone initial formula to verify formula satisfiability later ITabularFormula formulaClone = null; if (Helper.EnableAssertions) { stopWatch.start("Clone initial formula"); formulaClone = formula.clone(); stopWatch.stop(); stopWatch.printElapsed(); } stopWatch.start("Create CTF"); ObjectArrayList ct = Helper.createCTF(formula); timeElapsed = stopWatch.stop(); printFormulas(ct); stopWatch.printElapsed(); statistics.put(Helper.CTF_CREATION_TIME, String.valueOf(timeElapsed)); statistics.put(Helper.CTF_COUNT, String.valueOf(ct.size())); LOGGER.info("CTF count: {}", ct.size()); if (Helper.EnableAssertions) { assertNoTripletsLost(formula, ct); } // Clone CTF to verify formula satisfiability against it later ObjectArrayList ctfClone = null; if (Helper.EnableAssertions) { ctfClone = Helper.cloneStructures(ct); } stopWatch.start("Create CTS"); Helper.completeToCTS(ct, formula.getPermutation()); timeElapsed = stopWatch.stop(); printFormulas(ct); stopWatch.printElapsed(); statistics.put(Helper.CTS_CREATION_TIME, String.valueOf(timeElapsed)); if (commandLine.hasOption(CREATE_SKT_OPTION)) { String sktFilename = formulaFile + ".skt"; stopWatch.start("Convert CTS to " + sktFilename); Helper.convertCTStructuresToRomanovSKTFileFormat(ct, sktFilename); stopWatch.stop(); stopWatch.printElapsed(); return; } ObjectArrayList hss = unifyAndCreateHSS(statistics, stopWatch, ct); String hssPath = formulaFile + "-hss"; stopWatch.start("Save HSS to " + hssPath + "..."); Helper.saveHSS(hssPath, hss); stopWatch.stop(); stopWatch.printElapsed(); findHSSRoute(commandLine, formulaFile, statistics, stopWatch, formula, formulaClone, ctfClone, hss, hssPath); } catch (EmptyStructureException e) { stopWatch.stop(); stopWatch.printElapsed(); LOGGER.info("One of the structures was built empty", e); String resultsFilename = getResultsFilename(commandLine, formulaFile); stopWatch.start("Saving current statictics of calculations to " + resultsFilename); writeUnsatToFile(resultsFilename, statistics); stopWatch.stop(); stopWatch.printElapsed(); System.out.println("Formula not satisfiable"); } finally { System.out.println("Program completed"); } } private static ObjectArrayList unifyAndCreateHSS(Properties statistics, StopWatch stopWatch, ObjectArrayList cts) { long timeElapsed; stopWatch.start("Unify all CTS"); Helper.unify(cts); timeElapsed = stopWatch.stop(); printFormulas(cts); stopWatch.printElapsed(); statistics.put(Helper.CTS_UNIFICATION_TIME, String.valueOf(timeElapsed)); LOGGER.info("CTF: {}", cts.size()); ObjectArrayList hss = null; try { stopWatch.start("Create HSS"); hss = Helper.createHyperStructuresSystem(cts, statistics); } finally { timeElapsed = stopWatch.stop(); stopWatch.printElapsed(); if (hss != null) { statistics.put(Helper.BASIC_CTS_FINAL_CLAUSES_COUNT, String.valueOf(((IHyperStructure) hss.get(0)).getBasicCTS().getClausesCount())); } statistics.put(Helper.HSS_CREATION_TIME, String.valueOf(timeElapsed)); } return hss; } private static void findHSSRoute(CommandLine commandLine, String formulaFile, Properties statistics, StopWatch stopWatch, ITabularFormula formula, ITabularFormula formulaClone, ObjectArrayList ctfClone, ObjectArrayList hss, String hssPath) throws IOException { long timeElapsed; // TODO Configure hssTempPath using CL options String hssTempPath = hssPath + "-temp"; stopWatch.start("Find HSS route"); ObjectArrayList route = Helper.findHSSRouteByReduce(hss, hssTempPath); timeElapsed = stopWatch.stop(); stopWatch.printElapsed(); statistics.put(Helper.SEARCH_HSS_ROUTE_TIME, String.valueOf(timeElapsed)); if (Helper.EnableAssertions) { if (formulaClone != null) { if (!formula.equals(formulaClone)) { LOGGER.warn("Initial formula differs from its cloned version"); } } } String hssImageFile = formulaFile + "-hss-0.png"; if (commandLine.hasOption(HSS_IMAGE_OUTPUT_FILENAME_OPTION)) { hssImageFile = commandLine.getOptionValue(HSS_IMAGE_OUTPUT_FILENAME_OPTION); } stopWatch.start("Write HSS as image to " + hssImageFile); Helper.writeToImage(((SimpleVertex) route.get(route.size() - 1)).getHyperStructure(), route, null, hssImageFile); stopWatch.stop(); stopWatch.printElapsed(); stopWatch.start("Verify formula is satisfiable using variable values from HSS route"); verifySatisfiable(formula, route); if (Helper.EnableAssertions) { if (ctfClone != null) { verifySatisfiable(ctfClone, route); } } stopWatch.stop(); stopWatch.printElapsed(); String resultsFilename = getResultsFilename(commandLine, formulaFile); stopWatch.start("Write HSS route to " + resultsFilename); writeSatToFile(formula, resultsFilename, statistics, route); stopWatch.stop(); stopWatch.printElapsed(); } private static boolean evaluateFormula(StopWatch stopWatch, ITabularFormula formula, String resultsFilename) throws FileNotFoundException, IOException { stopWatch.start("Evaluate formula"); boolean satisfiable; Properties properties = new Properties(); FileInputStream is = null; try { is = new FileInputStream(new File(resultsFilename)); properties.load(is); satisfiable = formula.evaluate(properties); stopWatch.stop(); stopWatch.printElapsed(); } finally { if (is != null) { is.close(); } } return satisfiable; } private static void writeUnsatToFile(String resultsFile, Properties statistics) throws IOException { OutputStream out = null; try { out = new FileOutputStream(new File(resultsFile)); statistics.store(out, "Unsatisfiable"); } finally { if (out != null) { out.close(); } } } private static void writeSatToFile(ITabularFormula formula, String resultsFile, Properties statistics, ObjectArrayList route) throws IOException { OutputStream out = null; try { out = new FileOutputStream(new File(resultsFile)); IVertex vertex = null; for (int i = 0; i < route.size(); i++) { vertex = (IVertex) route.get(i); writeToStatistics(formula, statistics, vertex.getPermutation().getAName(), vertex.getTripletValue().isNotA()); } if (vertex != null) { writeToStatistics(formula, statistics, vertex.getPermutation().getBName(), vertex.getTripletValue().isNotB()); writeToStatistics(formula, statistics, vertex.getPermutation().getCName(), vertex.getTripletValue().isNotC()); } statistics.store(out, "Satisfiable. Variable values from HSS route"); } finally { if (out != null) { out.close(); } } } private static void writeToStatistics(ITabularFormula formula, Properties statistics, int varName, boolean varValue) { String stringValue = String.valueOf(varValue); statistics.put("_" + varName, stringValue); int originalVarName = formula.getOriginalVarName(varName); if (originalVarName > 0) { statistics.put(String.valueOf(originalVarName), stringValue); } } private static String getResultsFilename(CommandLine commandLine, String formulaFile) { String resultsFile = formulaFile + "-results.txt"; if (commandLine.hasOption(RESULTS_OUTPUT_FILE_OPTION)) { resultsFile = commandLine.getOptionValue(RESULTS_OUTPUT_FILE_OPTION); } return resultsFile; } @SuppressWarnings("static-access") private static Options getCommandLineOptions() { Options options = new Options(); options.addOption( OptionBuilder.withLongOpt("help").withDescription("Prints this help message.").create(HELP_OPTION)); options.addOption(OptionBuilder.withLongOpt("use-pretty-print") .withDescription("If specified, program will print detailed information about " + "formulas including triplet values." + "\nUseful when studying how algorithm works (especially if variables count less than 20)." + "\nDisabled by default.") .create(USE_PRETTY_PRINT_OPTION)); options.addOption(OptionBuilder.withLongOpt("disable-assertions") .withDescription( "Disables internal program self-check during execution. This may improve performance.") .create(DISABLE_ASSERTIONS_OPTION)); options.addOption(OptionBuilder.withLongOpt("use-abc-var-names") .withDescription("If specified, program will use ABC names for variables " + "(like 'a', 'b', ..., 'z' instead of 'x1', 'x2', etc.) during formula output." + "\nDisabled by default. Forced disabled if variables count more than 26.") .create(USE_ABC_VAR_NAMES_OPTION)); options.addOption(OptionBuilder.withLongOpt("hss-image-output").hasArg().withArgName("filename") .withDescription( "File name where visual representation of resulting basic graph will be written (only for SAT instances). Defaults to <input-file-name>-hss-0.png") .create(HSS_IMAGE_OUTPUT_FILENAME_OPTION)); options.addOption(OptionBuilder.withLongOpt("output").hasArg().withArgName("filename").withDescription( "File name where results of calculation will be written (time measurements and satisfying set for SAT instances). Defaults to <input-file-name>-results.txt") .create(RESULTS_OUTPUT_FILE_OPTION)); options.addOption(OptionBuilder.withLongOpt("evaluate-formula").hasArg().withArgName("filename") .withDescription("Evaluate formula using variable values from this file.").create(EVALUATE_OPTION)); options.addOption(OptionBuilder.withLongOpt("create-skt") .withDescription("Convert input formula to Romanov SKT file format.").create(CREATE_SKT_OPTION)); options.addOption(OptionBuilder.withLongOpt("find-hss-route").hasArg().withArgName("dirname") .withDescription("Find route in HSS from folder <dirname>").create(FIND_HSS_ROUTE_OPTION)); options.addOption(OptionBuilder.withLongOpt("generate-3sat-formula") .withDescription( "Generate 3-SAT formula from <input-file-name> and save it to <input-file-name>-3sat.cnf.") .create(GENERATE_3SAT_OPTION)); return options; } private static void assertNoTripletsLost(ITabularFormula formula, ObjectArrayList ctf) { int tripletCount = 0; for (int i = 0; i < ctf.size(); i++) { ITabularFormula f = (ITabularFormula) ctf.get(i); for (int j = 0; j < f.getTiers().size(); j++) { ITier tier = f.getTier(j); if (!formula.containsAllValuesOf(tier)) { throw new AssertionError("CTF triplet not found in initial formula"); } else { tripletCount += tier.size(); } } } if (tripletCount != formula.getClausesCount()) { throw new AssertionError("Bad CTF: tripletCount != formula.getClausesCount()"); } } private static void verifySatisfiable(ITabularFormula formula, ObjectArrayList route) { boolean satisfiable = false; try { satisfiable = formula.evaluate(route); } catch (NullPointerException e) { // Bad route e.printStackTrace(); } if (!satisfiable) { throw new AssertionError( "HSS was built but initial formula is not satisfiable with values from HS route"); } else { LOGGER.info("Initial formula verified as satisfiable with variables from HSS route"); } } private static void verifySatisfiable(ObjectArrayList ctf, ObjectArrayList route) { boolean satisfiable = false; try { satisfiable = Helper.evaluate(ctf, route); } catch (NullPointerException e) { // Bad route e.printStackTrace(); } if (!satisfiable) { throw new AssertionError("HSS was built but CTF is not satisfiable with values from HS route"); } else { LOGGER.info("CTF verified as satisfiable with variables from HSS route"); } } }