package splar.plugins.reasoners.sat.sat4j; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.naming.OperationNotSupportedException; import org.sat4j.core.LiteralsUtils; import org.sat4j.minisat.SolverFactory; import org.sat4j.minisat.core.IOrder; import org.sat4j.minisat.core.Solver; import org.sat4j.specs.IConstr; import org.sat4j.specs.ISolver; import org.sat4j.specs.TimeoutException; import org.sat4j.tools.ModelIterator; import splar.core.fm.reasoning.FMReasoningException; import splar.core.fm.reasoning.FMReasoningInterface; public abstract class ReasoningWithSAT extends FMReasoningInterface { protected ISolver satSolver; protected String solverName; protected int timeout; public ReasoningWithSAT(String solverName, int timeout) { super(); this.solverName = solverName; this.timeout = timeout; } public ISolver getSolver() { return satSolver; } @Override public String getVariableName(int index) { return super.getVariableName(index-1); } public void setVariableOrder(String order[]) { ((Solver)satSolver).setOrder(new StaticVariableOrderSAT(order, false, varName2IndexMap, varIndex2NameMap)); } public void setVariableOrderObject(IOrder order) { ((Solver)satSolver).setOrder(order); } protected abstract void addSolverClauses(ISolver solver) throws Exception; /**************************************************************************************************************** * INIT/END ****************************************************************************************************************/ /* * initializes the reasoner */ protected List<IConstr> constrList = null; public void init() throws Exception { satSolver = SolverFactory.instance().createSolverByName(solverName); satSolver.setTimeout(timeout); // System.out.println("Solver created: " + solverName); addSolverClauses(satSolver); } /* * finalizes the reasoner */ public void end() { } /**************************************************************************************************************** * REASONING OPERATIONS ****************************************************************************************************************/ /**************************************************************************************************************** * Checks if feature model is consistent ****************************************************************************************************************/ @Override public boolean isConsistent() throws FMReasoningException { try { return satSolver.isSatisfiable(); } catch (TimeoutException e) { throw new FMReasoningException(e); } } /**************************************************************************************************************** * Count feature model valid configurations ****************************************************************************************************************/ // Important: Performance of this operation is very poor as expected for a SAT solver, use a BDD instead @Override public double countValidConfigurations() throws FMReasoningException { if ( isConsistent() ) { ISolver solver = new ModelIterator(satSolver); double countSat = 0; long start = System.nanoTime(); try { while (solver.isSatisfiable()) { solver.model(); countSat++; if ( (System.nanoTime()-start)/1E6 > timeout ) return -1; } } catch (Exception e) { throw new FMReasoningException(e); } return countSat; } throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } /**************************************************************************************************************** * Checks if feature can only assume a false value (deselected) ****************************************************************************************************************/ @Override public boolean isDeadFeature(String featureId) throws FMReasoningException { try { if ( isConsistent() ) { if ( ((Solver)satSolver).assume(LiteralsUtils.posLit(getVariableIndex(featureId))) ) { return !isConsistent(); } return true; } else { throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } } catch (Exception e) { throw new FMReasoningException(e); } } /**************************************************************************************************************** * Checks if feature can only assume a true value (selected) ****************************************************************************************************************/ @Override public boolean isCoreFeature(String featureId) throws FMReasoningException { try { if ( isConsistent() ) { if ( ((Solver)satSolver).assume(LiteralsUtils.negLit(getVariableIndex(featureId))) ) { return !isConsistent(); } return true; } else { throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } } catch (Exception e) { throw new FMReasoningException(e); } } /**************************************************************************************************************** * Checks if feature can assume either boolean values (deselected/selected) ****************************************************************************************************************/ @Override public boolean isFreeFeature(String featureId) throws FMReasoningException { return !isDeadFeature(featureId) && !isCoreFeature(featureId); } /**************************************************************************************************************** * Compute valid domain for a given feature ****************************************************************************************************************/ @Override public Boolean[] validDomain(String featureId) throws FMReasoningException { if ( isConsistent() ) { if ( isDeadFeature(featureId) ) { return new Boolean[] {false}; } if ( isCoreFeature(featureId) ) { return new Boolean[] {true}; } return new Boolean[] {true, false}; } else { throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } } /**************************************************************************************************************** * Returns all dead features ****************************************************************************************************************/ @Override public List<String> allDeadFeatures(Map<String,String> stats) throws FMReasoningException { if ( isConsistent() ) { List<String> features = new LinkedList<String>(); boolean optimizations[] = {true,true,true,true,true}; int domainSearch[] = {1}; byte[][] domainTable = computeValidDomains(domainSearch, optimizations, stats); for( int i = 0 ; i < domainTable.length ; i++ ) { if ( domainTable[i][1] == NO ) { features.add(getVariableName(i+1)); } } return features; } else { throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } } /**************************************************************************************************************** * Returns all core features ****************************************************************************************************************/ public List<String> allCoreFeatures(Map<String,String> stats) throws OperationNotSupportedException, FMReasoningException { if ( isConsistent() ) { List<String> features = new LinkedList<String>(); boolean optimizations[] = {true,true,true,true,true}; int domainSearch[] = {0}; byte[][] domainTable = computeValidDomains(domainSearch, optimizations, stats); for( int i = 0 ; i < domainTable.length ; i++ ) { if ( domainTable[i][0] == NO ) { features.add(getVariableName(i+1)); } } return features; } else { throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } } /**************************************************************************************************************** * Returns all free features ****************************************************************************************************************/ public List<String> allFreeFeatures(Map<String,String> stats) throws OperationNotSupportedException, FMReasoningException { if ( isConsistent() ) { List<String> features = new LinkedList<String>(); boolean optimizations[] = {true,true,true,true,true}; int domainSearch[] = {0,1}; byte[][] domainTable = computeValidDomains(domainSearch, optimizations, stats); for( int i = 0 ; i < domainTable.length ; i++ ) { if ( domainTable[i][0] == YES && domainTable[i][1] == YES) { features.add(getVariableName(i+1)); } } return features; } else { throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } } /**************************************************************************************************************** * Compute valid domains for all features ****************************************************************************************************************/ @Override public Map<String, Boolean[]> allValidDomains(Map<String,String> stats) throws OperationNotSupportedException, FMReasoningException { if ( isConsistent() ) { Map<String,Boolean[]> allDomains = new HashMap<String, Boolean[]>(); boolean optimizations[] = {true,true,true,true,true}; int domainSearch[] = {1,0}; byte[][] domainTable = computeValidDomains(domainSearch, optimizations, stats); for( int i = 0 ; i < domainTable.length ; i++ ) { List<Boolean> domain = new LinkedList<Boolean>(); if ( domainTable[i][0] == YES ) { domain.add(false); } if ( domainTable[i][1] == YES ) { domain.add(true); } allDomains.put(getVariableName(i+1),domain.toArray(new Boolean[0])); } return allDomains; } else { throw new FMReasoningException("Operation does not apply to inconsistent feature models"); } } public abstract byte[][] computeValidDomains(int testValues[], boolean optimizations[], Map<String,String> stats); }