package splar.apps.generator; import java.io.File; import java.io.PrintStream; import java.text.DecimalFormat; import java.util.LinkedList; import java.util.List; import org.sat4j.specs.ContradictionException; import splar.core.constraints.CNFGenerator; import splar.core.fm.FeatureModel; import splar.core.fm.FeatureModelStatistics; import splar.core.fm.randomization.Random3CNFFeatureModel; import splar.plugins.reasoners.sat.sat4j.FMReasoningWithSAT; public class FMGeneratorEngine{ private List<FMGeneratorEngineListener> listeners; CNFGenerator cnfGenerator; String collectionName = "undefinedCollectionName"; int modelSize = 100; int ECR = 0; int ECRRange = 2; // accept ECRs of ECR +/- ECRRange, e.g., [20-2, 20+2] int ECR_comp = 0; // when ECR of models is consistently lower/higher than ECR, use ECR_comp to adjust int startIndex = 1; int endIndex = 10; int genSAT = 1; // -1: UNSAT, 1: SAT, 0: doesn't matter // percentage of variables to be considered for generating 3-CNF clauses int percVar3cnf = 100; // percentage of the 2-CNF clauses to be extended to 3-CNF int percForm3cnf = 100; // clause density for 3-CNF clauses float clauseDensity = 3f; // 0 if you don't mind, hard SAT instances between 3.42 and 4.506 String modelPath = ""; int percentageOptional = 25; int percentageMandatory = 25; int percentageExclusiveOR = 25; int percentageInclusiveOR = 25; int minBranchingFactor = 1; int maxBranchingFactor = 6; int maxGroupSize = 5; boolean canceled = false; DecimalFormat format2 = new DecimalFormat("0.0"); DecimalFormat format3 = new DecimalFormat("0.00"); public FMGeneratorEngine() { listeners = new LinkedList<FMGeneratorEngineListener>(); } public void addListener(FMGeneratorEngineListener listener) { listeners.add(listener); } public void fireEvent(String event, String modelName, String message) { for( FMGeneratorEngineListener listener : listeners ) { if ( event.equalsIgnoreCase("modelGenerationStarted") ) { listener.modelGenerationStarted(); } else if ( event.equalsIgnoreCase("modelGenerationEnded") ) { listener.modelGenerationEnded(); } else if ( event.equalsIgnoreCase("generatingModel") ) { listener.generatingModel(modelName); } else if ( event.equalsIgnoreCase("doneGeneratingModel") ) { listener.doneGeneratingModel(modelName); } else if ( event.equalsIgnoreCase("modelAccepted") ) { listener.modelAccepted(modelName); } else if ( event.equalsIgnoreCase("modelRejected") ) { listener.modelRejected(modelName, message); } else if ( event.equalsIgnoreCase("modelIsUnsat") ) { listener.modelIsUnsat(modelName); } else if ( event.equalsIgnoreCase("modelIsSat") ) { listener.modelIsSat(modelName); } else if ( event.equalsIgnoreCase("errorDuringGeneration") ) { listener.errorDuringGeneration(modelName, message); } else if ( event.equalsIgnoreCase("generationCanceled") ) { listener.generationCanceled(); } } } private void prepare() { if ( collectionName.trim().isEmpty() ) { collectionName = "undefinedCollectionName"; } // modelName = collectionName + "-FM-3CNF-"+modelSize+"-" + ECR; canceled = false; cnfGenerator = new CNFGenerator(); } public void setCollectionName(String collectionName) { this.collectionName = collectionName; } public void setCollectionPath(String path) { this.modelPath = path; if ( !path.endsWith("\\")) { this.modelPath += "\\"; } } public void setCollectionSize(int size) { this.startIndex = 1; this.endIndex = size; } public void setFeatureModelSize(int size) { this.modelSize = size; } public void setMandatoryPercentage(int percentage) { percentageMandatory = percentage; } public void setOptionalPercentage(int percentage) { percentageOptional = percentage; } public void setExclusiveORPercentage(int percentage) { percentageExclusiveOR = percentage; } public void setInclusiveORPercentage(int percentage) { percentageInclusiveOR = percentage; } public void setMinimumBranchingFactor(int minBranchingFactor) { this.minBranchingFactor = minBranchingFactor; } public void setMaximumBranchingFactor(int maxBranchingFactor) { this.maxBranchingFactor = maxBranchingFactor; } public void setMaximumGroupSize(int maxGroupSize) { this.maxGroupSize = maxGroupSize; } public void setCTCR(int CTCR) { this.ECR = CTCR; } public void setCTCRTolerance(int CTCRTolerance) { this.ECRRange = CTCRTolerance; } public void setClauseDensity(float clauseDensity) { this.clauseDensity = clauseDensity; } public void setModelConsistency(int consistency) { this.genSAT = consistency; } public void cancel() { canceled = true; } public void run() { fireEvent("modelGenerationStarted", "", ""); prepare(); FeatureModel fm = null; FeatureModelStatistics stats = null; for( int index = startIndex ; index <= endIndex && !canceled; ) { // System.out.println("------------------------------------------------"); // System.out.println("Generating model: " + modelName + "-" + index + " ..."); String modelIndex = "#"+index; try { fireEvent("generatingModel", modelIndex, ""); fm = generateFeatureModel(index, modelSize, ECR/100.0, clauseDensity); fireEvent("doneGeneratingModel", modelIndex, ""); } catch( Exception e ) { // System.out.println("Oops, exception, trying again :-)"); // e.printStackTrace(); fireEvent("errorDuringGeneration", modelIndex, e.getMessage()); continue; } stats = new FeatureModelStatistics(fm); stats.update(); try { boolean modelIsSAT = false; try { modelIsSAT = isSAT(fm); } catch(ContradictionException ce) { modelIsSAT = false; } String modelName = getModelName(modelIsSAT, index); // must be UNSAT if ( genSAT == -1 ) { if (modelIsSAT) { fireEvent("modelRejected", modelIndex, "Model is CONSISTENT"); fireEvent("modelIsSat", modelIndex, ""); continue; } fireEvent("modelIsUnsat", modelIndex, ""); } // must be SAT else if ( genSAT == 1 ) { if (!modelIsSAT) { fireEvent("modelRejected", modelIndex, "Model is INCONSISTENT"); fireEvent("modelIsUnsat", modelIndex, ""); continue; } fireEvent("modelIsSat", modelIndex, ""); } fm.setName(modelName); saveFeatureModel(fm, stats, modelPath + modelName + ".xml"); // System.out.println("done! [ECR=" + stats.getECRepresentativeness() + "]"); fireEvent("modelAccepted", modelName, ""); fireEvent("doneGeneratingModel", modelName, ""); index++; } catch (Exception e) { fireEvent("errorDuringGeneration", modelIndex, e.getMessage()); } } if ( canceled ) { fireEvent("generationCanceled", "", ""); } else { fireEvent("modelGenerationEnded", "", ""); } } private String getModelName(boolean isSAT, int index) { return collectionName + "-3CNF-FM-" + modelSize + "-" + (int)((ECR/100.0)*modelSize) + "-" + format3.format(clauseDensity) + "-" + (isSAT ? "SAT-" : "UNSAT-") + index; } public boolean isSAT(FeatureModel fm) throws Exception { // System.out.println("Checking satisfiability..."); FMReasoningWithSAT r = new FMReasoningWithSAT("MiniSAT",fm, 60000); long start = System.nanoTime(); r.init(); // System.out.println("Time: " + ((System.nanoTime()-start)/1E6)); return r.isConsistent(); } private void saveFeatureModel(FeatureModel fm, FeatureModelStatistics stats, String location) { File file = new File(location); PrintStream stream = null; PrintStream standartOut = System.out; try { stream = new PrintStream(location); System.setOut(stream); fm.dumpXML(); System.out.println("<!--"); stats.dump(); System.out.println("*************************************************************"); System.out.println("CROSS-TREE CONSTRAINTS (Random 3-CNF Formula)"); System.out.println(" CTC Representativeness (CTCR): " + format2.format(stats.getECRepresentativeness()*100) + "%"); System.out.println(" Number of 3-CNF clauses......: " + stats.countConstraints()); System.out.println(" CTC clause density specified.: " + format3.format(clauseDensity)); System.out.println("*************************************************************"); System.out.println("-->"); System.setOut(standartOut); stream.flush(); stream.close(); } catch(Exception e) { e.printStackTrace(); } } public FeatureModel generateFeatureModel(int index, int fmSize, double ECR, double density) throws Exception { Random3CNFFeatureModel fm = new Random3CNFFeatureModel( "", modelSize, percentageMandatory, percentageOptional, percentageInclusiveOR, percentageExclusiveOR, minBranchingFactor, maxBranchingFactor, maxGroupSize, 0 ); fm.loadModel(); fm.createCrossTreeConstraintsAsRandom3CNFFormula((int)(ECR * fmSize), clauseDensity); return fm; } } //fm = new RandomFeatureModel2( modelName+"-"+index, modelSize, //percentageMandatory, percentageOptional, percentageInclusiveOR, percentageExclusiveOR, //minBranchingFactor, maxBranchingFactor, maxGroupSize, 0); //fm.loadModel(); //if ( ECR > 0 ) { //int ecVar = (int)(modelSize * ((ECR+ECR_comp)/100.0)); //// RandomFeatureModel2.createExtraConstraints(fm, ecVar, ecVar/2, 2, new int[][] {{1,20,100,80},{20,20,100,100},{40,30,90,100},{60,30,90,100}}); //RandomFeatureModel2.createExtraConstraints(fm, ecVar, ecVar, 2, new int[][] {{1,30,100,90},{10,40,90,90},{30,30,90,90}}, percVar3cnf, percForm3cnf); //// RandomFeatureModel2.createExtraConstraints(fm, ecVar, ecVar, 2, new int[][] {{1,50,100,100},{10,50,100,100}}, percVar3cnf, percForm3cnf); //if ( percForm3cnf > 0 ) { //RandomFeatureModel2.expand3CNFClauses(fm, clauseDensity, "C-3CNF-"); //} //} //RandomFeatureModel2.createExtraConstraints(fm, ecVar, ecVar/2, 2, new int[][] {{1,50,100,90},{30,50,100,100}});