jp.kobe_u.sugar.csp.LinearSum 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.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import jp.kobe_u.sugar.SugarConstants;
import jp.kobe_u.sugar.SugarException;
import jp.kobe_u.sugar.expression.Expression;
/**
* A class for linear expressions.
* A linear expression represents the following formula:
* a0*x0+a1*x1+...+an*xn+b
* where ai's and b are integer constants and xi's are integer variables
* of CSP.
* @see CSP
* @see LinearLeLiteral
* @author Naoyuki Tamura
*/
public class LinearSum {
private int b;
private SortedMap coef;
private IntegerDomain domain = null;
public LinearSum(int b) {
coef = new TreeMap();
this.b = b;
}
public LinearSum(int a0, IntegerVariable v0, int b) {
this(b);
coef.put(v0, a0);
}
public LinearSum(IntegerVariable v0) {
this(1, v0, 0);
}
public LinearSum(LinearSum e) {
b = e.b;
coef = new TreeMap(e.coef);
domain = null;
}
/**
* Returns the size of the linear expression.
* @return the size
*/
public int size() {
return coef.size();
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public SortedMap getCoef() {
return coef;
}
public Set getVariables() {
return coef.keySet();
}
public boolean isIntegerVariable() {
return b == 0 && size() == 1 && getA(coef.firstKey()) == 1;
}
/**
* Returns true when the linear expression is simple.
* A linear expression is simple if it has at most one integer variable.
* @return true when the linear expression is simple
*/
public boolean isSimple() {
return coef.size() <= 1;
}
public Integer getA(IntegerVariable v) {
Integer a = coef.get(v);
if (a == null) {
a = 0;
}
return a;
}
public void setA(int a, IntegerVariable v) {
if (a == 0) {
coef.remove(v);
} else {
coef.put(v, a);
}
domain = null;
}
public boolean isDomainLargerThan(long limit) {
long size = 1;
for (IntegerVariable v : coef.keySet()) {
size *= v.getDomain().size();
if (size > limit)
return true;
}
return false;
}
public boolean isDomainLargerThanExcept(long limit, IntegerVariable v) {
long size = 1;
for (IntegerVariable v0 : coef.keySet()) {
if (v0.equals(v))
continue;
size *= v0.getDomain().size();
if (size > limit)
return true;
}
return false;
}
public boolean isDomainLargerThanExcept(long limit) {
IntegerVariable v = getLargestDomainVariable();
return isDomainLargerThanExcept(limit, v);
}
/**
* Adds the given linear expression.
* @param linearLe the linear expression to be added.
*/
public void add(LinearSum linearLe) {
b += linearLe.b;
for (IntegerVariable v : linearLe.coef.keySet()) {
int a = getA(v) + linearLe.getA(v);
setA(a, v);
}
domain = null;
}
/**
* Subtracts the given linear expression.
* @param linearLe the linear expression to be subtracted.
*/
public void subtract(LinearSum linearLe) {
b -= linearLe.b;
for (IntegerVariable v : linearLe.coef.keySet()) {
int a = getA(v) - linearLe.getA(v);
setA(a, v);
}
domain = null;
}
/**
* Multiplies the given constant.
* @param c the constant to be multiplied by
*/
public void multiply(int c) {
b *= c;
for (IntegerVariable v : coef.keySet()) {
int a = c * getA(v);
setA(a, v);
}
domain = null;
}
public void divide(int c) {
b /= c;
for (IntegerVariable v : coef.keySet()) {
int a = getA(v) / c;
setA(a, v);
}
domain = null;
}
private int gcd(int p, int q) {
while (true) {
int r = p % q;
if (r == 0)
break;
p = q;
q = r;
}
return q;
}
public int factor() {
if (size() == 0) {
return b == 0 ? 1 : Math.abs(b);
}
int gcd = Math.abs(getA(coef.firstKey()));
for (IntegerVariable v : coef.keySet()) {
gcd = gcd(gcd, Math.abs(getA(v)));
if (gcd == 1)
break;
}
if (b != 0) {
gcd = gcd(gcd, Math.abs(b));
}
return gcd;
}
public void factorize() {
int factor = factor();
if (factor > 1) {
divide(factor);
}
}
public IntegerDomain getDomain() throws SugarException {
if (domain == null) {
domain = IntegerDomain.create(b, b);
for (IntegerVariable v : coef.keySet()) {
int a = getA(v);
domain = domain.add(v.getDomain().mul(a));
}
}
return domain;
}
public IntegerDomain getDomainExcept(IntegerVariable v) throws SugarException {
// Re-calculation is needed since variable domains might be modified.
IntegerDomain d = IntegerDomain.create(b, b);
for (IntegerVariable v1 : coef.keySet()) {
if (! v1.equals(v)) {
int a = getA(v1);
d = d.add(v1.getDomain().mul(a));
}
}
return d;
}
public LinearSum[] split(int m) {
if (true) {
LinearSum[] es = new LinearSum[m];
for (int i = 0; i < m; i++) {
es[i] = new LinearSum(0);
}
IntegerVariable[] vs = getVariablesSorted();
for (int i = 0; i < vs.length; i++) {
IntegerVariable v = vs[i];
es[i % m].setA(getA(v), v);
}
return es;
} else {
LinearSum e1 = new LinearSum(0);
LinearSum e2 = new LinearSum(0);
if (false) {
IntegerVariable[] vs = getVariablesSorted();
int n2 = vs.length - 2;
int i = 0;
for (IntegerVariable v : vs) {
if (i < n2) {
e1.setA(getA(v), v);
} else {
e2.setA(getA(v), v);
}
i++;
}
} else if (true) {
int i = 0;
for (IntegerVariable v : coef.keySet()) {
if (i % 2 == 0) {
e1.setA(getA(v), v);
} else {
e2.setA(getA(v), v);
}
i++;
}
} else {
int i = 0;
int n2 = coef.size() / 2;
for (IntegerVariable v : coef.keySet()) {
if (i < n2) {
e1.setA(getA(v), v);
} else {
e2.setA(getA(v), v);
}
i++;
}
}
return new LinearSum[] { e1, e2 };
}
}
public IntegerVariable getLargestDomainVariable() {
IntegerVariable var = null;
for (IntegerVariable v : coef.keySet()) {
if (var == null || var.getDomain().size() < v.getDomain().size()) {
var = v;
}
}
return var;
}
public IntegerVariable[] getVariablesSorted() {
int n = coef.size();
IntegerVariable[] vs = new IntegerVariable[n];
vs = coef.keySet().toArray(vs);
Arrays.sort(vs, new Comparator() {
public int compare(IntegerVariable v1, IntegerVariable v2) {
if (true) {
int s1 = v1.getDomain().size();
int s2 = v2.getDomain().size();
if (s1 != s2)
return s1 < s2 ? -1 : 1;
}
int a1 = Math.abs(getA(v1));
int a2 = Math.abs(getA(v2));
if (a1 != a2)
return a1 > a2 ? -1 : 1;
return v1.compareTo(v2);
}
});
return vs;
}
private long calcSatSize(long limit, IntegerVariable[] vs, int i, int s) throws SugarException {
long size = 0;
if (i >= vs.length - 1) {
size = 1;
} else {
int lb0 = s;
int ub0 = s;
for (int j = i + 1; j < vs.length; j++) {
int a = getA(vs[j]);
if (a > 0) {
lb0 += a * vs[j].getDomain().getLowerBound();
ub0 += a * vs[j].getDomain().getUpperBound();
} else {
lb0 += a * vs[j].getDomain().getUpperBound();
ub0 += a * vs[j].getDomain().getLowerBound();
}
}
int a = getA(vs[i]);
IntegerDomain domain = vs[i].getDomain();
int lb = domain.getLowerBound();
int ub = domain.getUpperBound();
if (a >= 0) {
// ub = Math.min(ub, (int)Math.floor(-(double)lb0 / a));
if (-lb0 >= 0) {
ub = Math.min(ub, -lb0/a);
} else {
ub = Math.min(ub, (-lb0-a+1)/a);
}
Iterator iter = domain.values(lb, ub);
while (iter.hasNext()) {
int c = iter.next();
// vs[i]>=c -> ...
// encoder.writeComment(vs[i].getName() + " <= " + (c-1));
size += calcSatSize(limit, vs, i+1, s+a*c);
if (size > limit) {
return size;
}
}
size += calcSatSize(limit, vs, i+1, s+a*(ub+1));
if (size > limit) {
return size;
}
} else {
// lb = Math.max(lb, (int)Math.ceil(-(double)lb0/a));
if (-lb0 >= 0) {
lb = Math.max(lb, -lb0/a);
} else {
lb = Math.max(lb, (-lb0+a+1)/a);
}
size += calcSatSize(limit, vs, i+1, s+a*(lb-1));
if (size > limit) {
return size;
}
Iterator iter = domain.values(lb, ub);
while (iter.hasNext()) {
int c = iter.next();
// vs[i]<=c -> ...
size += calcSatSize(limit, vs, i+1, s+a*c);
if (size > limit) {
return size;
}
}
}
}
return size;
}
public boolean satSizeLE(long limit) throws SugarException {
if (isSimple()) {
return 1 <= limit;
} else {
IntegerVariable[] vs = getVariablesSorted();
long size = calcSatSize(limit, vs, 0, getB());
return size <= limit;
}
}
public Expression toExpression() {
Expression x = Expression.create(b);
for (IntegerVariable v : coef.keySet()) {
Expression ax = Expression.create(getA(v));
Expression vx = Expression.create(v.getName());
x = x.add(ax.mul(vx));
}
return x;
}
/**
* Returns the value of the linear expression.
* @return the value of the linear expression
*/
public int getValue() {
int value = b;
for (IntegerVariable v : coef.keySet()) {
value += getA(v) * v.getValue();
}
return value;
}
/**
* Returns true when the linear expression is equal to the given linear expression.
* @param linearLe the linear expression to be compared
* @return true when the linear expression is equal to the given linear expression
*/
public boolean equals(LinearSum linearLe) {
if (linearLe == null)
return false;
if (this == linearLe)
return true;
return b == linearLe.b && coef.equals(linearLe.coef);
}
/**
* Returns true when the linear expression is equal to the given object.
* @param obj the object to be compared
* @return true when the linear expression is equal to the given object.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null || getClass() != obj.getClass()) {
return false;
}
return equals((LinearSum)obj);
}
/**
* Returns the hash code of the linear expression.
* @return the hash code
*/
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((coef == null) ? 0 : coef.hashCode());
result = PRIME * result + b;
return result;
}
/**
* Returns the string representation of the linear expression.
* @return the string representation
*/
public String toString0() {
StringBuilder sb = new StringBuilder();
sb.append("(");
sb.append(SugarConstants.ADD);
for (IntegerVariable v : coef.keySet()) {
sb.append(" (");
sb.append(SugarConstants.MUL);
sb.append(" ");
sb.append(coef.get(v));
sb.append(" ");
sb.append(v.getName());
sb.append(")");
}
sb.append(" ");
sb.append(b);
sb.append(")");
return sb.toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (IntegerVariable v : coef.keySet()) {
int c = getA(v);
if (c == 0) {
} else if (c > 0) {
if (sb.length() > 0) {
sb.append("+");
}
if (c != 1) {
sb.append(c);
sb.append("*");
}
sb.append(v.getName());
} else {
if (c == -1) {
sb.append("-");
} else {
sb.append(c);
sb.append("*");
}
sb.append(v.getName());
}
}
if (sb.length() == 0) {
sb.append(b);
} else {
if (b >= 0) {
sb.append("+");
}
sb.append(b);
}
return sb.toString();
}
}