jp.kobe_u.sugar.csp.CSP Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsr331-sugar Show documentation
Show all versions of jsr331-sugar Show documentation
This is a JSR331 interface for the open source Java constraint programming library "Sugar" v. 2.1.3
package jp.kobe_u.sugar.csp;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import jp.kobe_u.sugar.Logger;
import jp.kobe_u.sugar.SugarException;
import jp.kobe_u.sugar.converter.Converter;
import jp.kobe_u.sugar.expression.Expression;
/**
* A class for CSP (Constraint Satisfaction Problems).
* A CSP consists of IntegerVariables, BooleanVariables, Clauses, and
* an optional objective IntegerVariable with specific objective (MINIMIZE or MAXIMIZE).
* @author Naoyuki Tamura ([email protected])
*/
public class CSP {
public static boolean USE_SIMPLIFYCACHE = true;
public static int MAX_SIMPLIFYCACHE_SIZE = 1000;
public static boolean simplifyAll = true;
private class SimplifyMap extends LinkedHashMap {
SimplifyMap() {
super(100, 0.75f, true);
}
/* (non-Javadoc)
* @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
*/
@Override
protected boolean removeEldestEntry(Entry eldest) {
return size() > CSP.MAX_SIMPLIFYCACHE_SIZE;
}
}
private List integerVariables;
private List booleanVariables;
private List relations;
private List clauses;
private int currentGroupID;
private HashMap integerVariableMap;
private HashMap booleanVariableMap;
private HashMap relationMap;
private List objectiveVariables = null;
private Objective objective = Objective.NONE;
private int groups = 0;
private int topWeight = 0;
private int integerVariablesSizeSave = 0;
private int booleanVariablesSizeSave = 0;
private int clausesSizeSave = 0;
private Map simplifyCache;
/**
* Objective types.
*/
public static enum Objective {
/**
* No objectives
*/
NONE,
/**
* Maximize
*/
MAXIMIZE,
/**
* Minimize
*/
MINIMIZE
}
/**
* Constructs a new CSP.
*/
public CSP() {
integerVariables = new ArrayList();
booleanVariables = new ArrayList();
relations = new ArrayList();
clauses = new ArrayList();
objectiveVariables = null;
objective = Objective.NONE;
integerVariableMap = new HashMap();
booleanVariableMap = new HashMap();
relationMap = new HashMap();
simplifyCache = new SimplifyMap();
}
public void commit() {
integerVariablesSizeSave = integerVariables.size();
booleanVariablesSizeSave = booleanVariables.size();
clausesSizeSave = clauses.size();
}
public void cancel() {
int i = integerVariablesSizeSave;
while (integerVariablesSizeSave < integerVariables.size()) {
integerVariableMap.remove(integerVariables.get(i).getName());
integerVariables.remove(i);
}
i = booleanVariablesSizeSave;
while (booleanVariablesSizeSave < booleanVariables.size()) {
booleanVariableMap.remove(booleanVariables.get(i).getName());
booleanVariables.remove(i);
}
i = clausesSizeSave;
while (clausesSizeSave < clauses.size()) {
clauses.remove(i);
}
}
/**
* Returns the list of objective variables or null.
* @return the list of objective variables or null
*/
public List getObjectiveVariables() {
return objectiveVariables;
}
public void setObjectiveVariables(List objectiveVariables) {
this.objectiveVariables = objectiveVariables;
}
/**
* Returns the objective.
* @return the objective
*/
public Objective getObjective() {
return objective;
}
public void setObjective(Objective objective) {
this.objective = objective;
}
public boolean isMaximize() {
return objective == Objective.MAXIMIZE;
}
public boolean isMinimize() {
return objective == Objective.MINIMIZE;
}
public int getGroups() {
return groups;
}
public void setGroups(int groups) {
this.groups = groups;
}
public int getTopWeight() {
return topWeight;
}
public void setTopWeight(int topWeight) {
this.topWeight = topWeight;
}
/**
* Returns the integer variables.
* @return the integer variables
*/
public List getIntegerVariables() {
return integerVariables;
}
public List getIntegerVariablesDelta() {
return integerVariables.subList(integerVariablesSizeSave, integerVariables.size());
}
/**
* Returns the integer variable of the given name.
* @param name the name of the integer variable
* @return the integer variable or null
*/
public IntegerVariable getIntegerVariable(String name) {
return integerVariableMap.get(name);
}
public void add(IntegerVariable v) throws SugarException {
String name = v.getName();
if (integerVariableMap.containsKey(name)) {
throw new SugarException("Duplicated integer variable " + name);
}
integerVariableMap.put(v.getName(), v);
integerVariables.add(v);
}
/**
* Returns the boolean variables.
* @return the boolean variables
*/
public List getBooleanVariables() {
return booleanVariables;
}
public List getBooleanVariablesDelta() {
return booleanVariables.subList(booleanVariablesSizeSave, booleanVariables.size());
}
public void add(BooleanVariable v) throws SugarException {
String name = v.getName();
if (booleanVariableMap.containsKey(name)) {
throw new SugarException("Duplicated boolean variable " + name);
}
booleanVariableMap.put(v.getName(), v);
booleanVariables.add(v);
}
/**
* Returns the boolean variable of the given name.
* @param name the name of the boolean variable
* @return the boolean variable or null
*/
public BooleanVariable getBooleanVariable(String name) {
return booleanVariableMap.get(name);
}
/**
* Returns the relations.
* @return the relations
*/
public List getRelations() {
return relations;
}
public Relation getRelation(String name) {
return relationMap.get(name);
}
/**
* Returns the clauses.
* @return the clauses
*/
public List getClauses() {
return clauses;
}
public List getClausesDelta() {
return clauses.subList(clausesSizeSave, clauses.size());
}
/**
* Adds a relation.
* @param relation the relation to be added
*/
public void addRelation(Relation relation) {
relations.add(relation);
relationMap.put(relation.name, relation);
}
/**
* Adds a clause.
* @param clause the clause to be added
*/
public void add(Clause clause) {
clauses.add(clause);
}
public boolean isUnsatisfiable() throws SugarException {
for (IntegerVariable v : integerVariables) {
if (v.isUnsatisfiable()) {
Logger.fine("Unsatisfiable integer variable " + v);
return true;
}
}
for (Clause c : clauses) {
if (c.isUnsatisfiable()) {
Logger.fine("Unsatisfiable constraint " + c.toString());
return true;
}
}
return false;
}
public int propagate() throws SugarException {
int removedValues = 0;
int removedLiterals = 0;
while (true) {
List modifiedClauses = new ArrayList();
for (Clause clause : clauses) {
if (clause.isModified()) {
modifiedClauses.add(clause);
}
}
for (IntegerVariable v : integerVariables) {
v.setModified(false);
}
int values = 0;
int literals = 0;
for (Clause clause : modifiedClauses) {
values += clause.propagate();
literals += clause.removeFalsefood();
}
if (values == 0 && literals == 0)
break;
removedValues += values;
removedLiterals += literals;
}
int removedClauses = 0;
int i = 0;
while (i < clauses.size()) {
if (clauses.get(i).isValid()) {
clauses.remove(i);
removedClauses++;
} else {
i++;
}
}
Logger.fine(removedValues + " values, "
+ removedLiterals + " unsatisfiable literals, and "
+ removedClauses + " valid clauses are removed");
return removedValues + removedLiterals + removedClauses;
}
private List simplify(Clause clause) throws SugarException {
List literals = clause.getLiterals();
List newClauses = new ArrayList();
clause = new Clause();
int complex = 0;
for (Literal literal : literals) {
if (literal.isSimple()) {
clause.add(literal);
} else {
complex++;
if (! CSP.simplifyAll && complex == 1) {
clause.add(literal);
} else if (USE_SIMPLIFYCACHE && simplifyCache.containsKey(literal)) {
Literal lit = simplifyCache.get(literal);
clause.add(lit);
} else {
BooleanVariable p = new BooleanVariable();
add(p);
BooleanLiteral posLiteral = new BooleanLiteral(p, false);
BooleanLiteral negLiteral = new BooleanLiteral(p, true);
Clause newClause = new Clause();
newClause.add(negLiteral);
newClause.add(literal);
newClauses.add(newClause);
clause.add(posLiteral);
if (USE_SIMPLIFYCACHE)
simplifyCache.put(literal, posLiteral);
}
}
}
newClauses.add(clause);
return newClauses;
}
public void simplify() throws SugarException {
List newClauses = new ArrayList();
for (Clause clause : clauses) {
if (clause.isSimple()) {
newClauses.add(clause);
} else {
newClauses.addAll(simplify(clause));
}
}
clauses = newClauses;
}
public void compact() throws SugarException {
throw new SugarException("Unimplemented method compact()");
/*
int n = integerVariables.size();
for (int i = 0; i < n; i++) {
IntegerVariable v = integerVariables.get(i);
v.compact(this);
}
List newClauses = new ArrayList();
for (Clause clause : clauses) {
if (clause.isCompact()) {
newClauses.add(clause);
} else {
newClauses.addAll(compact(clause));
}
}
clauses = newClauses;
*/
}
/**
* Returns true when the CSP is satisfied.
* @return true when the CSP is satisfied
*/
public boolean isSatisfied() {
for (IntegerVariable v : integerVariables) {
if (! v.isSatisfied()) {
// Logger.fine(v.toString() + " is not satisfied");
return false;
}
}
for (Clause clause : clauses) {
if (! clause.isSatisfied()) {
// Logger.fine(clause.toString() + " is not satisfied");
return false;
}
}
return true;
}
public void outputValues(PrintStream out) {
for (IntegerVariable v : integerVariables) {
if (v.isAux()) {
out.println(v.getName() + " = " + v.getValue());
} else {
out.println(v.getName() + " = " + v.getValue());
}
}
for (BooleanVariable v : booleanVariables) {
if (! v.isAux()) {
out.println(v.getName() + " = " + v.getValue());
} else {
out.println(v.getName() + " = " + v.getValue());
}
}
}
public void output(PrintStream out, String pre) {
for (IntegerVariable v : integerVariables) {
if (v.getComment() != null) {
out.print(pre + "; ");
out.println(v.getComment());
}
out.println(pre + v.toString());
}
for (BooleanVariable v : booleanVariables) {
if (v.getComment() != null) {
out.print(pre + "; ");
out.println(v.getComment());
}
out.println(pre + v.toString());
}
for (Clause clause : clauses) {
if (clause.getComment() != null) {
out.print(pre + "; ");
out.println(clause.getComment());
}
out.println(pre + clause.toString());
}
List vs = getObjectiveVariables();
if (vs != null) {
String obj = "none";
if (objective == Objective.MINIMIZE) {
obj = "minimize";
} else if (objective == Objective.MAXIMIZE) {
obj = "maximize";
}
out.print(pre + "(objective " + obj);
for (IntegerVariable v : vs) {
out.print(" " + v.getName() + ")");
}
out.println();
}
}
public String summary() {
int size = 0;
for (IntegerVariable v : integerVariables) {
size = Math.max(size, v.getDomain().size());
}
return
getIntegerVariables().size() + " integers, " +
getBooleanVariables().size() + " booleans, " +
getClauses().size() + " clauses, " +
"largest domain size " + size;
}
/**
* Returns the string representation of the CSP.
* @return the string representation
*/
@Override
public String toString() {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bs);
output(out, "");
return bs.toString();
}
}