[CP-SAT] Next iteration in java modelling layer

This commit is contained in:
Laurent Perron
2022-01-04 16:30:04 +01:00
parent de6ea0da5f
commit 1166e1663a
35 changed files with 319 additions and 596 deletions

View File

@@ -15,27 +15,34 @@ package com.google.ortools.sat;
/** A specialized linear expression: a * x + b */
public final class AffineExpression implements LinearExpr {
private final IntVar var;
private final int varIndex;
private final long coefficient;
private final long offset;
public AffineExpression(IntVar var, long coefficient, long offset) {
this.var = var;
public AffineExpression(int varIndex, long coefficient, long offset) {
this.varIndex = varIndex;
this.coefficient = coefficient;
this.offset = offset;
}
// LinearArgument interface.
@Override
public LinearExpr build() {
return this;
}
// LinearExpr interface.
@Override
public int numElements() {
return 1;
}
@Override
public IntVar getVariable(int index) {
public int getVariableIndex(int index) {
if (index != 0) {
throw new IllegalArgumentException("wrong index in LinearExpr.getVariable(): " + index);
throw new IllegalArgumentException("wrong index in LinearExpr.getIndex(): " + index);
}
return var;
return varIndex;
}
@Override

View File

@@ -20,32 +20,47 @@ import com.google.ortools.util.Domain;
public final class BoolVar extends IntVar implements Literal {
BoolVar(CpModelProto.Builder builder, Domain domain, String name) {
super(builder, domain, name);
this.negation_ = null;
this.negation = null;
}
BoolVar(CpModelProto.Builder builder, int index) {
super(builder, index);
this.negation_ = null;
this.negation = null;
}
/** Returns the negation of a boolean variable. */
@Override
public Literal not() {
if (negation_ == null) {
negation_ = new NotBooleanVariable(this);
if (negation == null) {
negation = new NotBoolVar(this);
}
return negation_;
return negation;
}
@Override
public BoolVar getBoolVar() {
return this;
public String toString() {
if (varBuilder.getName().isEmpty()) {
if (varBuilder.getDomainCount() == 2 && varBuilder.getDomain(0) == varBuilder.getDomain(1)) {
if (varBuilder.getDomain(0) == 0) {
return "false";
} else {
return "true";
}
} else {
return String.format("boolvar_%d(%s)", getIndex(), displayBounds());
}
} else {
if (varBuilder.getDomainCount() == 2 && varBuilder.getDomain(0) == varBuilder.getDomain(1)) {
if (varBuilder.getDomain(0) == 0) {
return String.format("%s(false)", varBuilder.getName());
} else {
return String.format("%s(true)", varBuilder.getName());
}
} else {
return String.format("%s(%s)", getName(), displayBounds());
}
}
}
@Override
public boolean negated() {
return false;
}
private NotBooleanVariable negation_ = null;
private NotBoolVar negation = null;
}

View File

@@ -21,13 +21,20 @@ public final class ConstantExpression implements LinearExpr {
this.offset = offset;
}
// LinearArgument interface.
@Override
public LinearExpr build() {
return this;
}
// LinearExpr interface.
@Override
public int numElements() {
return 0;
}
@Override
public IntVar getVariable(int index) {
public int getVariableIndex(int index) {
throw new IllegalArgumentException("wrong index in LinearExpr.getVariable(): " + index);
}
@@ -40,4 +47,9 @@ public final class ConstantExpression implements LinearExpr {
public long getOffset() {
return offset;
}
@Override
public String toString() {
return String.format("%d", offset);
}
}

View File

@@ -30,6 +30,7 @@ import com.google.ortools.sat.NoOverlapConstraintProto;
import com.google.ortools.sat.ReservoirConstraintProto;
import com.google.ortools.sat.TableConstraintProto;
import com.google.ortools.util.Domain;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -123,12 +124,7 @@ public final class CpModel {
/** Adds {@code Or(literals) == true}. */
public Constraint addBoolOr(Literal[] literals) {
Constraint ct = new Constraint(modelBuilder);
BoolArgumentProto.Builder boolOr = ct.getBuilder().getBoolOrBuilder();
for (Literal lit : literals) {
boolOr.addLiterals(lit.getIndex());
}
return ct;
return addBoolOr(Arrays.asList(literals));
}
/** Adds {@code Or(literals) == true}. */
@@ -143,32 +139,17 @@ public final class CpModel {
/** Same as addBoolOr. {@code Sum(literals) >= 1}. */
public Constraint addAtLeastOne(Literal[] literals) {
Constraint ct = new Constraint(modelBuilder);
BoolArgumentProto.Builder boolOr = ct.getBuilder().getBoolOrBuilder();
for (Literal lit : literals) {
boolOr.addLiterals(lit.getIndex());
}
return ct;
return addBoolOr(Arrays.asList(literals));
}
/** Same as addBoolOr. {@code Sum(literals) >= 1}. */
public Constraint addAtLeastOne(Iterable<Literal> literals) {
Constraint ct = new Constraint(modelBuilder);
BoolArgumentProto.Builder boolOr = ct.getBuilder().getBoolOrBuilder();
for (Literal lit : literals) {
boolOr.addLiterals(lit.getIndex());
}
return ct;
return addBoolOr(literals);
}
/** Adds {@code AtMostOne(literals): Sum(literals) <= 1}. */
public Constraint addAtMostOne(Literal[] literals) {
Constraint ct = new Constraint(modelBuilder);
BoolArgumentProto.Builder atMostOne = ct.getBuilder().getAtMostOneBuilder();
for (Literal lit : literals) {
atMostOne.addLiterals(lit.getIndex());
}
return ct;
return addAtMostOne(Arrays.asList(literals));
}
/** Adds {@code AtMostOne(literals): Sum(literals) <= 1}. */
@@ -183,12 +164,7 @@ public final class CpModel {
/** Adds {@code ExactlyOne(literals): Sum(literals) == 1}. */
public Constraint addExactlyOne(Literal[] literals) {
Constraint ct = new Constraint(modelBuilder);
BoolArgumentProto.Builder exactlyOne = ct.getBuilder().getExactlyOneBuilder();
for (Literal lit : literals) {
exactlyOne.addLiterals(lit.getIndex());
}
return ct;
return addExactlyOne(Arrays.asList(literals));
}
/** Adds {@code ExactlyOne(literals): Sum(literals) == 1}. */
@@ -203,12 +179,7 @@ public final class CpModel {
/** Adds {@code And(literals) == true}. */
public Constraint addBoolAnd(Literal[] literals) {
Constraint ct = new Constraint(modelBuilder);
BoolArgumentProto.Builder boolAnd = ct.getBuilder().getBoolAndBuilder();
for (Literal lit : literals) {
boolAnd.addLiterals(lit.getIndex());
}
return ct;
return addBoolAnd(Arrays.asList(literals));
}
/** Adds {@code And(literals) == true}. */
@@ -223,12 +194,7 @@ public final class CpModel {
/** Adds {@code XOr(literals) == true}. */
public Constraint addBoolXor(Literal[] literals) {
Constraint ct = new Constraint(modelBuilder);
BoolArgumentProto.Builder boolXOr = ct.getBuilder().getBoolXorBuilder();
for (Literal lit : literals) {
boolXOr.addLiterals(lit.getIndex());
}
return ct;
return addBoolXor(Arrays.asList(literals));
}
/** Adds {@code XOr(literals) == true}. */
@@ -249,127 +215,112 @@ public final class CpModel {
// Linear constraints.
/** Adds {@code expr in domain}. */
public Constraint addLinearExpressionInDomain(LinearExpr expr, Domain domain) {
public Constraint addLinearExpressionInDomain(LinearArgument expr, Domain domain) {
Constraint ct = new Constraint(modelBuilder);
LinearConstraintProto.Builder lin = ct.getBuilder().getLinearBuilder();
for (int i = 0; i < expr.numElements(); ++i) {
lin.addVars(expr.getVariable(i).getIndex()).addCoeffs(expr.getCoefficient(i));
}
long offset = expr.getOffset();
if (offset != 0) {
lin.addVars(newConstant(1).getIndex()).addCoeffs(offset);
final LinearExpr e = expr.build();
for (int i = 0; i < e.numElements(); ++i) {
lin.addVars(e.getVariableIndex(i)).addCoeffs(e.getCoefficient(i));
}
long offset = e.getOffset();
for (long b : domain.flattenedIntervals()) {
lin.addDomain(b);
if (b == Long.MIN_VALUE || b == Long.MAX_VALUE) {
lin.addDomain(b);
} else {
lin.addDomain(b - offset);
}
}
return ct;
}
/** Adds {@code lb <= expr <= ub}. */
public Constraint addLinearConstraint(LinearExpr expr, long lb, long ub) {
public Constraint addLinearConstraint(LinearArgument expr, long lb, long ub) {
return addLinearExpressionInDomain(expr, new Domain(lb, ub));
}
/** Adds {@code expr == value}. */
public Constraint addEquality(LinearExpr expr, long value) {
public Constraint addEquality(LinearArgument expr, long value) {
return addLinearExpressionInDomain(expr, new Domain(value));
}
/** Adds {@code left == right}. */
public Constraint addEquality(LinearExpr left, LinearExpr right) {
public Constraint addEquality(LinearArgument left, LinearArgument right) {
LinearExprBuilder difference = LinearExpr.newBuilder();
difference.addTerm(left, 1);
difference.addTerm(right, -1);
return addLinearExpressionInDomain(difference.build(), new Domain(0));
return addLinearExpressionInDomain(difference, new Domain(0));
}
/** Adds {@code expr <= value}. */
public Constraint addLessOrEqual(LinearExpr expr, long value) {
public Constraint addLessOrEqual(LinearArgument expr, long value) {
return addLinearExpressionInDomain(expr, new Domain(Long.MIN_VALUE, value));
}
/** Adds {@code left <= right}. */
public Constraint addLessOrEqual(LinearExpr left, LinearExpr right) {
public Constraint addLessOrEqual(LinearArgument left, LinearArgument right) {
LinearExprBuilder difference = LinearExpr.newBuilder();
difference.addTerm(left, 1);
difference.addTerm(right, -1);
return addLinearExpressionInDomain(difference.build(), new Domain(Long.MIN_VALUE, 0));
return addLinearExpressionInDomain(difference, new Domain(Long.MIN_VALUE, 0));
}
/** Adds {@code expr < value}. */
public Constraint addLessThan(LinearExpr expr, long value) {
public Constraint addLessThan(LinearArgument expr, long value) {
return addLinearExpressionInDomain(expr, new Domain(Long.MIN_VALUE, value - 1));
}
/** Adds {@code left < right}. */
public Constraint addLessThan(LinearExpr left, LinearExpr right) {
public Constraint addLessThan(LinearArgument left, LinearArgument right) {
LinearExprBuilder difference = LinearExpr.newBuilder();
difference.addTerm(left, 1);
difference.addTerm(right, -1);
return addLinearExpressionInDomain(difference.build(), new Domain(Long.MIN_VALUE, -1));
return addLinearExpressionInDomain(difference, new Domain(Long.MIN_VALUE, -1));
}
/** Adds {@code expr >= value}. */
public Constraint addGreaterOrEqual(LinearExpr expr, long value) {
public Constraint addGreaterOrEqual(LinearArgument expr, long value) {
return addLinearExpressionInDomain(expr, new Domain(value, Long.MAX_VALUE));
}
/** Adds {@code left >= right}. */
public Constraint addGreaterOrEqual(LinearExpr left, LinearExpr right) {
public Constraint addGreaterOrEqual(LinearArgument left, LinearArgument right) {
LinearExprBuilder difference = LinearExpr.newBuilder();
difference.addTerm(left, 1);
difference.addTerm(right, -1);
return addLinearExpressionInDomain(difference.build(), new Domain(0, Long.MAX_VALUE));
return addLinearExpressionInDomain(difference, new Domain(0, Long.MAX_VALUE));
}
/** Adds {@code expr > value}. */
public Constraint addGreaterThan(LinearExpr expr, long value) {
public Constraint addGreaterThan(LinearArgument expr, long value) {
return addLinearExpressionInDomain(expr, new Domain(value + 1, Long.MAX_VALUE));
}
/** Adds {@code left > right}. */
public Constraint addGreaterThan(LinearExpr left, LinearExpr right) {
public Constraint addGreaterThan(LinearArgument left, LinearArgument right) {
LinearExprBuilder difference = LinearExpr.newBuilder();
difference.addTerm(left, 1);
difference.addTerm(right, -1);
return addLinearExpressionInDomain(difference.build(), new Domain(1, Long.MAX_VALUE));
return addLinearExpressionInDomain(difference, new Domain(1, Long.MAX_VALUE));
}
/** Adds {@code expr != value}. */
public Constraint addDifferent(LinearExpr expr, long value) {
public Constraint addDifferent(LinearArgument expr, long value) {
return addLinearExpressionInDomain(expr,
Domain.fromFlatIntervals(
new long[] {Long.MIN_VALUE, value - 1, value + 1, Long.MAX_VALUE}));
}
/** Adds {@code left != right}. */
public Constraint addDifferent(LinearExpr left, LinearExpr right) {
public Constraint addDifferent(LinearArgument left, LinearArgument right) {
LinearExprBuilder difference = LinearExpr.newBuilder();
difference.addTerm(left, 1);
difference.addTerm(right, -1);
return addLinearExpressionInDomain(difference.build(),
Domain.fromFlatIntervals(new long[] {Long.MIN_VALUE, -1, 1, Long.MAX_VALUE}));
return addLinearExpressionInDomain(
difference, Domain.fromFlatIntervals(new long[] {Long.MIN_VALUE, -1, 1, Long.MAX_VALUE}));
}
// Integer constraints.
/**
* Adds {@code AllDifferent(variables)}.
*
* <p>This constraint forces all variables to have different values.
*
* @param variables a list of integer variables
* @return an instance of the Constraint class
*/
public Constraint addAllDifferent(IntVar[] variables) {
Constraint ct = new Constraint(modelBuilder);
AllDifferentConstraintProto.Builder allDiff = ct.getBuilder().getAllDiffBuilder();
for (IntVar var : variables) {
allDiff.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(var, /*negate=*/false));
}
return ct;
}
/**
* Adds {@code AllDifferent(expressions)}.
*
@@ -378,25 +329,20 @@ public final class CpModel {
* @param expressions a list of affine integer expressions
* @return an instance of the Constraint class
*/
public Constraint addAllDifferent(LinearExpr[] expressions) {
Constraint ct = new Constraint(modelBuilder);
AllDifferentConstraintProto.Builder allDiff = ct.getBuilder().getAllDiffBuilder();
for (LinearExpr expr : expressions) {
allDiff.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/false));
}
return ct;
public Constraint addAllDifferent(LinearArgument[] expressions) {
return addAllDifferent(Arrays.asList(expressions));
}
/**
* Adds {@code AllDifferent(expressions)}.
*
* @see addAllDifferent(LinearExpr[]).
* @see addAllDifferent(LinearArgument[]).
*/
public Constraint addAllDifferent(Iterable<LinearExpr> expressions) {
public Constraint addAllDifferent(Iterable<? extends LinearArgument> expressions) {
Constraint ct = new Constraint(modelBuilder);
AllDifferentConstraintProto.Builder allDiff = ct.getBuilder().getAllDiffBuilder();
for (LinearExpr expr : expressions) {
allDiff.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/false));
for (LinearArgument expr : expressions) {
allDiff.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/false));
}
return ct;
}
@@ -419,7 +365,7 @@ public final class CpModel {
ElementConstraintProto.Builder element =
ct.getBuilder().getElementBuilder().setIndex(index.getIndex());
for (long v : values) {
element.addVars(indexFromConstant(v));
element.addVars(newConstant(v).getIndex());
}
element.setTarget(target.getIndex());
return ct;
@@ -431,7 +377,7 @@ public final class CpModel {
ElementConstraintProto.Builder element =
ct.getBuilder().getElementBuilder().setIndex(index.getIndex());
for (long v : values) {
element.addVars(indexFromConstant(v));
element.addVars(newConstant(v).getIndex());
}
element.setTarget(target.getIndex());
return ct;
@@ -476,13 +422,7 @@ public final class CpModel {
* directly to the table constraint.
*/
public TableConstraint addAllowedAssignments(IntVar[] variables) {
TableConstraint ct = new TableConstraint(modelBuilder);
TableConstraintProto.Builder table = ct.getBuilder().getTableBuilder();
for (IntVar var : variables) {
table.addVars(var.getIndex());
}
table.setNegated(false);
return ct;
return addAllowedAssignments(Arrays.asList(variables));
}
/**
@@ -511,13 +451,7 @@ public final class CpModel {
* directly to the table constraint.
*/
public TableConstraint addForbiddenAssignments(IntVar[] variables) {
TableConstraint ct = new TableConstraint(modelBuilder);
TableConstraintProto.Builder table = ct.getBuilder().getTableBuilder();
for (IntVar var : variables) {
table.addVars(var.getIndex());
}
table.setNegated(true);
return ct;
return addForbiddenAssignments(Arrays.asList(variables));
}
/**
@@ -649,144 +583,115 @@ public final class CpModel {
}
/** Adds {@code target == Min(vars)}. */
public Constraint addMinEquality(LinearExpr target, IntVar[] vars) {
public Constraint addMinEquality(LinearArgument target, LinearArgument[] exprs) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder linMax = ct.getBuilder().getLinMaxBuilder();
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/true));
for (IntVar var : vars) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(var, /*negate=*/true));
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/true));
for (LinearArgument expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/true));
}
return ct;
}
/** Adds {@code target == Min(exprs)}. */
public Constraint addMinEquality(LinearExpr target, LinearExpr[] exprs) {
public Constraint addMinEquality(
LinearArgument target, Iterable<? extends LinearArgument> exprs) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder linMax = ct.getBuilder().getLinMaxBuilder();
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/true));
for (LinearExpr expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/true));
}
return ct;
}
/** Adds {@code target == Min(exprs)}. */
public Constraint addMinEquality(LinearExpr target, Iterable<LinearExpr> exprs) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder linMax = ct.getBuilder().getLinMaxBuilder();
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/true));
for (LinearExpr expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/true));
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/true));
for (LinearArgument expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/true));
}
return ct;
}
/** Adds {@code target == Max(vars)}. */
public Constraint addMaxEquality(LinearExpr target, IntVar[] vars) {
public Constraint addMaxEquality(LinearArgument target, LinearArgument[] exprs) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder linMax = ct.getBuilder().getLinMaxBuilder();
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false));
for (IntVar var : vars) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(var, /*negate=*/false));
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false));
for (LinearArgument expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/false));
}
return ct;
}
/** Adds {@code target == Max(exprs)}. */
public Constraint addMaxEquality(LinearExpr target, LinearExpr[] exprs) {
public Constraint addMaxEquality(
LinearArgument target, Iterable<? extends LinearArgument> exprs) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder linMax = ct.getBuilder().getLinMaxBuilder();
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false));
for (LinearExpr expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/false));
}
return ct;
}
/** Adds {@code target == Max(exprs)}. */
public Constraint addMaxEquality(LinearExpr target, Iterable<LinearExpr> exprs) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder linMax = ct.getBuilder().getLinMaxBuilder();
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false));
for (LinearExpr expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/false));
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false));
for (LinearArgument expr : exprs) {
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/false));
}
return ct;
}
/** Adds {@code target == num / denom}, rounded towards 0. */
public Constraint addDivisionEquality(LinearExpr target, LinearExpr num, LinearExpr denom) {
public Constraint addDivisionEquality(
LinearArgument target, LinearArgument num, LinearArgument denom) {
Constraint ct = new Constraint(modelBuilder);
ct.getBuilder()
.getIntDivBuilder()
.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(num, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(denom, /*negate=*/false));
.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(num, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(denom, /*negate=*/false));
return ct;
}
/** Adds {@code target == Abs(expr)}. */
public Constraint addAbsEquality(LinearExpr target, LinearExpr expr) {
public Constraint addAbsEquality(LinearArgument target, LinearArgument expr) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder linMax = ct.getBuilder().getLinMaxBuilder();
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false));
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/false));
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/true));
linMax.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false));
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/false));
linMax.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/true));
return ct;
}
/** Adds {@code target == var % mod}. */
public Constraint addModuloEquality(LinearExpr target, LinearExpr var, LinearExpr mod) {
public Constraint addModuloEquality(
LinearArgument target, LinearArgument var, LinearArgument mod) {
Constraint ct = new Constraint(modelBuilder);
ct.getBuilder()
.getIntModBuilder()
.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(var, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(mod, /*negate=*/false));
.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(var, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(mod, /*negate=*/false));
return ct;
}
/** Adds {@code target == var % mod}. */
public Constraint addModuloEquality(LinearExpr target, LinearExpr var, long mod) {
public Constraint addModuloEquality(LinearArgument target, LinearArgument var, long mod) {
Constraint ct = new Constraint(modelBuilder);
ct.getBuilder()
.getIntModBuilder()
.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(var, /*negate=*/false))
.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(var, /*negate=*/false))
.addExprs(getLinearExpressionProtoBuilderFromLong(mod));
return ct;
}
/** Adds {@code target == Product(vars)}. */
public Constraint addMultiplicationEquality(LinearExpr target, IntVar[] vars) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder intProd = ct.getBuilder().getIntProdBuilder();
intProd.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false));
for (IntVar var : vars) {
intProd.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(var, /*negate=*/false));
}
return ct;
}
/** Adds {@code target == Product(exprs)}. */
public Constraint addMultiplicationEquality(LinearExpr target, LinearExpr[] exprs) {
public Constraint addMultiplicationEquality(LinearArgument target, LinearArgument[] exprs) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder intProd = ct.getBuilder().getIntProdBuilder();
intProd.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false));
for (LinearExpr expr : exprs) {
intProd.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(expr, /*negate=*/false));
intProd.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false));
for (LinearArgument expr : exprs) {
intProd.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(expr, /*negate=*/false));
}
return ct;
}
/** Adds {@code target == left * right}. */
public Constraint addMultiplicationEquality(
LinearExpr target, LinearExpr left, LinearExpr right) {
LinearArgument target, LinearArgument left, LinearArgument right) {
Constraint ct = new Constraint(modelBuilder);
LinearArgumentProto.Builder intProd = ct.getBuilder().getIntProdBuilder();
intProd.setTarget(getLinearExpressionProtoBuilderFromLinearExpr(target, /*negate=*/false));
intProd.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(left, /*negate=*/false));
intProd.addExprs(getLinearExpressionProtoBuilderFromLinearExpr(right, /*negate=*/false));
intProd.setTarget(getLinearExpressionProtoBuilderFromLinearArgument(target, /*negate=*/false));
intProd.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(left, /*negate=*/false));
intProd.addExprs(getLinearExpressionProtoBuilderFromLinearArgument(right, /*negate=*/false));
return ct;
}
@@ -807,12 +712,12 @@ public final class CpModel {
* @return An IntervalVar object
*/
public IntervalVar newIntervalVar(
LinearExpr start, LinearExpr size, LinearExpr end, String name) {
addEquality(LinearExpr.newBuilder().add(start).add(size).build(), end);
LinearArgument start, LinearArgument size, LinearArgument end, String name) {
addEquality(LinearExpr.newBuilder().add(start).add(size), end);
return new IntervalVar(modelBuilder,
getLinearExpressionProtoBuilderFromLinearExpr(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearExpr(size, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearExpr(end, /*negate=*/false), name);
getLinearExpressionProtoBuilderFromLinearArgument(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(size, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(end, /*negate=*/false), name);
}
/**
@@ -826,12 +731,12 @@ public final class CpModel {
* @param name the name of the interval variable.
* @return An IntervalVar object
*/
public IntervalVar newFixedSizeIntervalVar(LinearExpr start, long size, String name) {
public IntervalVar newFixedSizeIntervalVar(LinearArgument start, long size, String name) {
return new IntervalVar(modelBuilder,
getLinearExpressionProtoBuilderFromLinearExpr(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLong(size),
getLinearExpressionProtoBuilderFromLinearExpr(
LinearExpr.newBuilder().add(start).add(size).build(), /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(
LinearExpr.newBuilder().add(start).add(size), /*negate=*/false),
name);
}
@@ -860,14 +765,14 @@ public final class CpModel {
* @param name The name of the interval variable
* @return an IntervalVar object
*/
public IntervalVar newOptionalIntervalVar(
LinearExpr start, LinearExpr size, LinearExpr end, Literal isPresent, String name) {
addEquality(LinearExpr.newBuilder().add(start).add(size).build(), end).onlyEnforceIf(isPresent);
public IntervalVar newOptionalIntervalVar(LinearArgument start, LinearArgument size,
LinearArgument end, Literal isPresent, String name) {
addEquality(LinearExpr.newBuilder().add(start).add(size), end).onlyEnforceIf(isPresent);
return new IntervalVar(modelBuilder,
getLinearExpressionProtoBuilderFromLinearExpr(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearExpr(size, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearExpr(end, /*negate=*/false), isPresent.getIndex(),
name);
getLinearExpressionProtoBuilderFromLinearArgument(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(size, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(end, /*negate=*/false),
isPresent.getIndex(), name);
}
/**
@@ -884,12 +789,12 @@ public final class CpModel {
* @return An IntervalVar object
*/
public IntervalVar newOptionalFixedSizeIntervalVar(
LinearExpr start, long size, Literal isPresent, String name) {
LinearArgument start, long size, Literal isPresent, String name) {
return new IntervalVar(modelBuilder,
getLinearExpressionProtoBuilderFromLinearExpr(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(start, /*negate=*/false),
getLinearExpressionProtoBuilderFromLong(size),
getLinearExpressionProtoBuilderFromLinearExpr(
LinearExpr.newBuilder().add(start).add(size).build(), /*negate=*/false),
getLinearExpressionProtoBuilderFromLinearArgument(
LinearExpr.newBuilder().add(start).add(size), /*negate=*/false),
isPresent.getIndex(), name);
}
@@ -910,12 +815,7 @@ public final class CpModel {
* @return an instance of the Constraint class
*/
public Constraint addNoOverlap(IntervalVar[] intervalVars) {
Constraint ct = new Constraint(modelBuilder);
NoOverlapConstraintProto.Builder noOverlap = ct.getBuilder().getNoOverlapBuilder();
for (IntervalVar var : intervalVars) {
noOverlap.addIntervals(var.getIndex());
}
return ct;
return addNoOverlap(Arrays.asList(intervalVars));
}
/**
@@ -961,17 +861,17 @@ public final class CpModel {
* @return an instance of the CumulativeConstraint class. this class allows adding (interval,
* demand) pairs incrementally.
*/
public CumulativeConstraint addCumulative(LinearExpr capacity) {
public CumulativeConstraint addCumulative(LinearArgument capacity) {
CumulativeConstraint ct = new CumulativeConstraint(this);
CumulativeConstraintProto.Builder cumul = ct.getBuilder().getCumulativeBuilder();
cumul.setCapacity(getLinearExpressionProtoBuilderFromLinearExpr(capacity, false));
cumul.setCapacity(getLinearExpressionProtoBuilderFromLinearArgument(capacity, false));
return ct;
}
/**
* Adds {@code Cumulative(capacity)}.
*
* @see #addCumulative(LinearExpr capacity)
* @see #addCumulative(LinearArgument capacity)
*/
public CumulativeConstraint addCumulative(long capacity) {
CumulativeConstraint ct = new CumulativeConstraint(this);
@@ -1011,12 +911,13 @@ public final class CpModel {
// Objective.
/** Adds a minimization objective of a linear expression. */
public void minimize(LinearExpr expr) {
public void minimize(LinearArgument expr) {
CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder();
for (int i = 0; i < expr.numElements(); ++i) {
obj.addVars(expr.getVariable(i).getIndex()).addCoeffs(expr.getCoefficient(i));
final LinearExpr e = expr.build();
for (int i = 0; i < e.numElements(); ++i) {
obj.addVars(e.getVariableIndex(i)).addCoeffs(e.getCoefficient(i));
}
obj.setOffset(expr.getOffset());
obj.setOffset(e.getOffset());
}
public void minimize(DoubleLinearExpr expr) {
@@ -1028,12 +929,13 @@ public final class CpModel {
}
/** Adds a maximization objective of a linear expression. */
public void maximize(LinearExpr expr) {
public void maximize(LinearArgument expr) {
CpObjectiveProto.Builder obj = modelBuilder.getObjectiveBuilder();
for (int i = 0; i < expr.numElements(); ++i) {
obj.addVars(expr.getVariable(i).getIndex()).addCoeffs(-expr.getCoefficient(i));
final LinearExpr e = expr.build();
for (int i = 0; i < e.numElements(); ++i) {
obj.addVars(e.getVariableIndex(i)).addCoeffs(-e.getCoefficient(i));
}
obj.setOffset(-expr.getOffset());
obj.setOffset(-e.getOffset());
obj.setScalingFactor(-1.0);
}
@@ -1081,27 +983,14 @@ public final class CpModel {
}
// Helpers
long[] toLongArray(int[] values) {
long[] result = new long[values.length];
for (int i = 0; i < values.length; ++i) {
result[i] = values[i];
}
return result;
}
int indexFromConstant(long constant) {
IntVar constVar = newConstant(constant);
return constVar.getIndex();
}
LinearExpressionProto.Builder getLinearExpressionProtoBuilderFromLinearExpr(
LinearExpr expr, boolean negate) {
LinearExpressionProto.Builder getLinearExpressionProtoBuilderFromLinearArgument(
LinearArgument arg, boolean negate) {
LinearExpressionProto.Builder builder = LinearExpressionProto.newBuilder();
final LinearExpr expr = arg.build();
final int numVariables = expr.numElements();
final long mult = negate ? -1 : 1;
for (int i = 0; i < numVariables; ++i) {
builder.addVars(expr.getVariable(i).getIndex());
builder.addVars(expr.getVariableIndex(i));
builder.addCoeffs(expr.getCoefficient(i) * mult);
}
builder.setOffset(expr.getOffset() * mult);

View File

@@ -127,10 +127,11 @@ public final class CpSolver {
}
/** Returns the value of a linear expression in the last solution found. */
public long value(LinearExpr expr) {
long result = expr.getOffset();
for (int i = 0; i < expr.numElements(); ++i) {
result += solveResponse.getSolution(expr.getVariable(i).getIndex()) * expr.getCoefficient(i);
public long value(LinearArgument expr) {
final LinearExpr e = expr.build();
long result = e.getOffset();
for (int i = 0; i < e.numElements(); ++i) {
result += solveResponse.getSolution(e.getVariableIndex(i)) * e.getCoefficient(i);
}
return result;
}

View File

@@ -38,10 +38,11 @@ package com.google.ortools.sat;
*/
public class CpSolverSolutionCallback extends SolutionCallback {
/** Returns the value of the linear expression in the current solution. */
public long value(LinearExpr expr) {
long result = expr.getOffset();
for (int i = 0; i < expr.numElements(); ++i) {
result += solutionIntegerValue(expr.getVariable(i).getIndex()) * expr.getCoefficient(i);
public long value(LinearArgument expr) {
final LinearExpr e = expr.build();
long result = e.getOffset();
for (int i = 0; i < e.numElements(); ++i) {
result += solutionIntegerValue(e.getVariableIndex(i)) * e.getCoefficient(i);
}
return result;
}

View File

@@ -28,10 +28,10 @@ public class CumulativeConstraint extends Constraint {
}
/// Adds a pair (interval, demand) to the constraint.
void addDemand(IntervalVar interval, LinearExpr demand) {
void addDemand(IntervalVar interval, LinearArgument demand) {
CumulativeConstraintProto.Builder cumul = getBuilder().getCumulativeBuilder();
cumul.addIntervals(interval.getIndex());
cumul.addDemands(model.getLinearExpressionProtoBuilderFromLinearExpr(demand, false));
cumul.addDemands(model.getLinearExpressionProtoBuilderFromLinearArgument(demand, false));
}
/// Adds a pair (interval, demand) to the constraint.

View File

@@ -18,7 +18,7 @@ import com.google.ortools.sat.IntegerVariableProto;
import com.google.ortools.util.Domain;
/** An integer variable. */
public class IntVar implements LinearExpr {
public class IntVar implements LinearArgument {
IntVar(CpModelProto.Builder builder, Domain domain, String name) {
this.modelBuilder = builder;
this.variableIndex = modelBuilder.getVariablesCount();
@@ -35,17 +35,12 @@ public class IntVar implements LinearExpr {
this.varBuilder = modelBuilder.getVariablesBuilder(index);
}
@Override
public String toString() {
return varBuilder.toString();
}
/** Returns the name of the variable given upon creation. */
public String getName() {
return varBuilder.getName();
}
/** Internal, returns the index of the variable in the underlying CpModelProto. */
/** Returns the index of the variable in the underlying CpModelProto. */
public int getIndex() {
return variableIndex;
}
@@ -55,31 +50,10 @@ public class IntVar implements LinearExpr {
return varBuilder;
}
// LinearExpr interface.
// LinearArgument interface
@Override
public int numElements() {
return 1;
}
@Override
public IntVar getVariable(int index) {
if (index != 0) {
throw new IllegalArgumentException("wrong index in LinearExpr.getVariable(): " + index);
}
return this;
}
@Override
public long getCoefficient(int index) {
if (index != 0) {
throw new IllegalArgumentException("wrong index in LinearExpr.getCoefficient(): " + index);
}
return 1;
}
@Override
public long getOffset() {
return 0;
public LinearExpr build() {
return new AffineExpression(variableIndex, 1, 0);
}
/** Returns the domain as a string without the enclosing []. */
@@ -103,8 +77,8 @@ public class IntVar implements LinearExpr {
return CpSatHelper.variableDomain(varBuilder.build());
}
/** Returns a short string describing the variable. */
public String getShortString() {
@Override
public String toString() {
if (varBuilder.getName().isEmpty()) {
if (varBuilder.getDomainCount() == 2 && varBuilder.getDomain(0) == varBuilder.getDomain(1)) {
return String.format("%d", varBuilder.getDomain(0));

View File

@@ -69,17 +69,17 @@ public final class IntervalVar {
/** Returns the start expression. */
public LinearExpr getStartExpr() {
return LinearExpr.rebuildFromLinearExpressionProto(intervalBuilder.getStart(), modelBuilder);
return LinearExpr.rebuildFromLinearExpressionProto(intervalBuilder.getStart());
}
/** Returns the size expression. */
public LinearExpr getSizeExpr() {
return LinearExpr.rebuildFromLinearExpressionProto(intervalBuilder.getSize(), modelBuilder);
return LinearExpr.rebuildFromLinearExpressionProto(intervalBuilder.getSize());
}
/** Returns the end expression. */
public LinearExpr getEndExpr() {
return LinearExpr.rebuildFromLinearExpressionProto(intervalBuilder.getEnd(), modelBuilder);
return LinearExpr.rebuildFromLinearExpressionProto(intervalBuilder.getEnd());
}
private final CpModelProto.Builder modelBuilder;

View File

@@ -0,0 +1,20 @@
// Copyright 2010-2021 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.ortools.sat;
/** A object that can build a LinearExpr object. */
public interface LinearArgument {
/** Builds a linear expression. */
LinearExpr build();
}

View File

@@ -13,16 +13,15 @@
package com.google.ortools.sat;
import com.google.ortools.sat.CpModelProto;
import com.google.ortools.sat.LinearExpressionProto;
/** A linear expression interface that can be parsed. */
public interface LinearExpr {
/** Returns the number of elements in the interface. */
/** A linear expression (sum (ai * xi) + b). It specifies methods to help parsing the expression. */
public interface LinearExpr extends LinearArgument {
/** Returns the number of terms (excluding the constant one) in this expression. */
int numElements();
/** Returns the ith variable. */
IntVar getVariable(int index);
/** Returns the index of the ith variable. */
int getVariableIndex(int index);
/** Returns the ith coefficient. */
long getCoefficient(int index);
@@ -40,76 +39,36 @@ public interface LinearExpr {
return newBuilder().add(value).build();
}
/** Shortcut for newBuilder().addTerm(var, coeff).build() */
static LinearExpr term(IntVar var, long coeff) {
return newBuilder().addTerm(var, coeff).build();
}
/** Shortcut for newBuilder().addTerm(expr, coeff).build() */
static LinearExpr term(LinearExpr expr, long coeff) {
static LinearExpr term(LinearArgument expr, long coeff) {
return newBuilder().addTerm(expr, coeff).build();
}
/** Shortcut for newBuilder().addTerm(literal, coeff).build() */
static LinearExpr term(Literal literal, long coeff) {
return newBuilder().addTerm(literal, coeff).build();
}
/** Shortcut for newBuilder().addSum(vars).build() */
static LinearExpr sum(IntVar[] vars) {
return newBuilder().addSum(vars).build();
}
/** Shortcut for newBuilder().addSum(exprs).build() */
static LinearExpr sum(LinearExpr[] exprs) {
static LinearExpr sum(LinearArgument[] exprs) {
return newBuilder().addSum(exprs).build();
}
/** Shortcut for newBuilder().addSum(literals).build() */
static LinearExpr sum(Literal[] literals) {
return newBuilder().addSum(literals).build();
}
/** Shortcut for newBuilder().addWeightedSum(vars, coeffs).build() */
static LinearExpr weightedSum(IntVar[] vars, long[] coeffs) {
return newBuilder().addWeightedSum(vars, coeffs).build();
}
/** Shortcut for newBuilder().addWeightedSum(literals, coeffs).build() */
static LinearExpr weightedSum(Literal[] literals, long[] coeffs) {
return newBuilder().addWeightedSum(literals, coeffs).build();
}
/** Shortcut for newBuilder().addWeightedSum(exprs, coeffs).build() */
static LinearExpr weightedSum(LinearExpr[] exprs, long[] coeffs) {
static LinearExpr weightedSum(LinearArgument[] exprs, long[] coeffs) {
return newBuilder().addWeightedSum(exprs, coeffs).build();
}
static LinearExpr rebuildFromLinearExpressionProto(
LinearExpressionProto proto, CpModelProto.Builder builder) {
static LinearExpr rebuildFromLinearExpressionProto(LinearExpressionProto proto) {
int numElements = proto.getVarsCount();
if (numElements == 0) {
return new ConstantExpression(proto.getOffset());
} else if (numElements == 1) {
return new AffineExpression(
new IntVar(builder, proto.getVars(0)), proto.getCoeffs(0), proto.getOffset());
return new AffineExpression(proto.getVars(0), proto.getCoeffs(0), proto.getOffset());
} else {
IntVar[] vars = new IntVar[numElements];
int[] varsIndices = new int[numElements];
long[] coeffs = new long[numElements];
long offset = proto.getOffset();
boolean allOnes = true;
for (int i = 0; i < numElements; ++i) {
vars[i] = new IntVar(builder, proto.getVars(i));
varsIndices[i] = proto.getVars(i);
coeffs[i] = proto.getCoeffs(i);
if (coeffs[i] != 1) {
allOnes = false;
}
}
if (allOnes) {
return new SumExpression(vars, offset);
} else {
return new WeightedSumExpression(vars, coeffs, offset);
}
return new WeightedSumExpression(varsIndices, coeffs, offset);
}
}
}

View File

@@ -13,160 +13,88 @@
package com.google.ortools.sat;
import static java.util.Comparator.comparingInt;
import java.util.Map;
import java.util.TreeMap;
/** Builder class for the LinearExpr container. */
public final class LinearExprBuilder {
private final TreeMap<IntVar, Long> coefficients;
public final class LinearExprBuilder implements LinearArgument {
private final TreeMap<Integer, Long> coefficients;
private long offset;
LinearExprBuilder() {
this.coefficients = new TreeMap<>(comparingInt(IntVar::getIndex));
this.coefficients = new TreeMap<>();
this.offset = 0;
}
public LinearExprBuilder add(LinearExpr expr) {
public LinearExprBuilder add(LinearArgument expr) {
addTerm(expr, 1);
return this;
}
public LinearExprBuilder add(Literal literal) {
addTerm(literal, 1);
return this;
}
public LinearExprBuilder add(long constant) {
offset = offset + constant;
return this;
}
public LinearExprBuilder addTerm(LinearExpr expr, long coeff) {
final int numElements = expr.numElements();
public LinearExprBuilder addTerm(LinearArgument expr, long coeff) {
final LinearExpr e = expr.build();
final int numElements = e.numElements();
for (int i = 0; i < numElements; ++i) {
coefficients.merge(expr.getVariable(i), expr.getCoefficient(i) * coeff, Long::sum);
coefficients.merge(e.getVariableIndex(i), e.getCoefficient(i) * coeff, Long::sum);
}
offset = offset + expr.getOffset() * coeff;
offset = offset + e.getOffset() * coeff;
return this;
}
public LinearExprBuilder addTerm(Literal literal, long coeff) {
final BoolVar boolVar = literal.getBoolVar();
if (literal.negated()) {
coefficients.merge(boolVar, -coeff, Long::sum);
offset = offset + coeff;
} else {
coefficients.merge(boolVar, coeff, Long::sum);
}
return this;
}
public LinearExprBuilder addSum(IntVar[] vars) {
for (final IntVar var : vars) {
addTerm(var, 1);
}
return this;
}
public LinearExprBuilder addSum(Literal[] literals) {
for (final Literal literal : literals) {
addTerm(literal, 1);
}
return this;
}
public LinearExprBuilder addSum(LinearExpr[] exprs) {
for (final LinearExpr expr : exprs) {
public LinearExprBuilder addSum(LinearArgument[] exprs) {
for (final LinearArgument expr : exprs) {
addTerm(expr, 1);
}
return this;
}
public LinearExprBuilder addWeightedSum(IntVar[] vars, long[] coeffs) {
for (int i = 0; i < vars.length; ++i) {
addTerm(vars[i], coeffs[i]);
}
return this;
}
public LinearExprBuilder addWeightedSum(IntVar[] vars, int[] coeffs) {
for (int i = 0; i < vars.length; ++i) {
addTerm(vars[i], coeffs[i]);
}
return this;
}
public LinearExprBuilder addWeightedSum(Literal[] literals, long[] coeffs) {
for (int i = 0; i < literals.length; ++i) {
addTerm(literals[i], coeffs[i]);
}
return this;
}
public LinearExprBuilder addWeightedSum(Literal[] literals, int[] coeffs) {
for (int i = 0; i < literals.length; ++i) {
addTerm(literals[i], coeffs[i]);
}
return this;
}
public LinearExprBuilder addWeightedSum(LinearExpr[] exprs, long[] coeffs) {
public LinearExprBuilder addWeightedSum(LinearArgument[] exprs, long[] coeffs) {
for (int i = 0; i < exprs.length; ++i) {
addTerm(exprs[i], coeffs[i]);
}
return this;
}
public LinearExprBuilder addWeightedSum(LinearExpr[] exprs, int[] coeffs) {
public LinearExprBuilder addWeightedSum(LinearArgument[] exprs, int[] coeffs) {
for (int i = 0; i < exprs.length; ++i) {
addTerm(exprs[i], coeffs[i]);
}
return this;
}
@Override
public LinearExpr build() {
boolean allOnes = true;
int numElements = 0;
IntVar lastVar = null;
int lastVarIndex = -1;
long lastCoeff = 0;
for (Map.Entry<IntVar, Long> entry : coefficients.entrySet()) {
for (Map.Entry<Integer, Long> entry : coefficients.entrySet()) {
if (entry.getValue() != 0) {
numElements++;
lastVar = entry.getKey();
lastVarIndex = entry.getKey();
lastCoeff = entry.getValue();
if (lastCoeff != 1) {
allOnes = false;
}
}
}
if (numElements == 0) {
return new ConstantExpression(offset);
} else if (numElements == 1) {
return new AffineExpression(lastVar, lastCoeff, offset);
} else if (allOnes) {
IntVar[] vars = new IntVar[numElements];
int index = 0;
for (Map.Entry<IntVar, Long> entry : coefficients.entrySet()) {
if (entry.getValue() != 0) {
vars[index] = entry.getKey();
index++;
}
}
return new SumExpression(vars, offset);
return new AffineExpression(lastVarIndex, lastCoeff, offset);
} else {
IntVar[] vars = new IntVar[numElements];
int[] varIndices = new int[numElements];
long[] coeffs = new long[numElements];
int index = 0;
for (Map.Entry<IntVar, Long> entry : coefficients.entrySet()) {
for (Map.Entry<Integer, Long> entry : coefficients.entrySet()) {
if (entry.getValue() != 0) {
vars[index] = entry.getKey();
varIndices[index] = entry.getKey();
coeffs[index] = entry.getValue();
index++;
}
}
return new WeightedSumExpression(vars, coeffs, offset);
return new WeightedSumExpression(varIndices, coeffs, offset);
}
}
}

View File

@@ -14,18 +14,9 @@
package com.google.ortools.sat;
/** Interface to describe a boolean variable or its negation. */
public interface Literal extends LinearExpr {
public interface Literal extends LinearArgument {
public int getIndex();
/** Returns the Boolean negation of the current literal. */
public Literal not();
/** Returns a short string to describe the literal. */
public String getShortString();
/** Returns the underlying BoolVar */
public BoolVar getBoolVar();
/** Returns true if the literal is the negation of a Boolean variable */
public boolean negated();
}

View File

@@ -17,12 +17,12 @@ package com.google.ortools.sat;
* The negation of a boolean variable. This class should not be used directly, Literal must be used
* instead.
*/
public final class NotBooleanVariable implements Literal {
public NotBooleanVariable(BoolVar boolVar) {
public final class NotBoolVar implements Literal {
public NotBoolVar(BoolVar boolVar) {
this.boolVar = boolVar;
}
/** Internal: returns the index in the literal in the underlying CpModelProto. */
/** returns the index in the literal in the underlying CpModelProto. */
@Override
public int getIndex() {
return -boolVar.getIndex() - 1;
@@ -34,47 +34,16 @@ public final class NotBooleanVariable implements Literal {
return boolVar;
}
// LinearArgument interface.
@Override
public BoolVar getBoolVar() {
return boolVar;
}
@Override
public boolean negated() {
return true;
}
// LinearExpr interface.
@Override
public int numElements() {
return 1;
}
@Override
public IntVar getVariable(int index) {
if (index != 0) {
throw new IllegalArgumentException("wrong index in LinearExpr.getVariable(): " + index);
}
return boolVar;
}
@Override
public long getCoefficient(int index) {
if (index != 0) {
throw new IllegalArgumentException("wrong index in LinearExpr.getCoefficient(): " + index);
}
return -1;
}
@Override
public long getOffset() {
return 1;
public LinearExpr build() {
return new AffineExpression(boolVar.getIndex(), -1, 1);
}
/** Returns a short string describing this literal. */
@Override
public String getShortString() {
return "not(" + boolVar.getShortString() + ")";
public String toString() {
return "not(" + boolVar + ")";
}
private final BoolVar boolVar;

View File

@@ -33,10 +33,10 @@ public class ReservoirConstraint extends Constraint {
* <p>It will increase the used capacity by `levelChange` at time `time`. `time` must be an affine
* expression.
*/
void addEvent(LinearExpr time, long levelChange) {
void addEvent(LinearArgument time, long levelChange) {
ReservoirConstraintProto.Builder reservoir = getBuilder().getReservoirBuilder();
reservoir.addTimeExprs(
model.getLinearExpressionProtoBuilderFromLinearExpr(time, /*negate=*/false));
model.getLinearExpressionProtoBuilderFromLinearArgument(time, /*negate=*/false));
reservoir.addLevelChanges(levelChange);
reservoir.addActiveLiterals(model.trueLiteral().getIndex());
}
@@ -62,7 +62,7 @@ public class ReservoirConstraint extends Constraint {
void addOptionalEvent(LinearExpr time, long levelChange, Literal isActive) {
ReservoirConstraintProto.Builder reservoir = getBuilder().getReservoirBuilder();
reservoir.addTimeExprs(
model.getLinearExpressionProtoBuilderFromLinearExpr(time, /*negate=*/false));
model.getLinearExpressionProtoBuilderFromLinearArgument(time, /*negate=*/false));
reservoir.addLevelChanges(levelChange);
reservoir.addActiveLiterals(isActive.getIndex());
}

View File

@@ -1,48 +0,0 @@
// Copyright 2010-2021 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.ortools.sat;
/** A specialized linear expression: sum(xi) + b. */
public final class SumExpression implements LinearExpr {
private final IntVar[] variables;
private final long offset;
public SumExpression(IntVar[] variables, long offset) {
this.variables = variables;
this.offset = offset;
}
@Override
public int numElements() {
return variables.length;
}
@Override
public IntVar getVariable(int index) {
if (index < 0 || index >= variables.length) {
throw new IllegalArgumentException("wrong index in LinearExpr.getVariable(): " + index);
}
return variables[index];
}
@Override
public long getCoefficient(int index) {
return 1;
}
@Override
public long getOffset() {
return offset;
}
}

View File

@@ -15,32 +15,37 @@ package com.google.ortools.sat;
/** A specialized linear expression: sum(ai * xi) + b. */
public final class WeightedSumExpression implements LinearExpr {
private final IntVar[] variables;
private final int[] variablesIndices;
private final long[] coefficients;
private final long offset;
public WeightedSumExpression(IntVar[] variables, long[] coefficients, long offset) {
this.variables = variables;
public WeightedSumExpression(int[] variablesIndices, long[] coefficients, long offset) {
this.variablesIndices = variablesIndices;
this.coefficients = coefficients;
this.offset = offset;
}
@Override
public int numElements() {
return variables.length;
public LinearExpr build() {
return this;
}
@Override
public IntVar getVariable(int index) {
if (index < 0 || index >= variables.length) {
public int numElements() {
return variablesIndices.length;
}
@Override
public int getVariableIndex(int index) {
if (index < 0 || index >= variablesIndices.length) {
throw new IllegalArgumentException("wrong index in LinearExpr.getVariable(): " + index);
}
return variables[index];
return variablesIndices[index];
}
@Override
public long getCoefficient(int index) {
if (index < 0 || index >= variables.length) {
if (index < 0 || index >= variablesIndices.length) {
throw new IllegalArgumentException("wrong index in LinearExpr.getCoefficient(): " + index);
}
return coefficients[index];

View File

@@ -107,7 +107,7 @@ public class LiteralSampleSat {
CpModel model = new CpModel();
BoolVar x = model.newBoolVar("x");
Literal notX = x.not();
System.out.println(notX.getShortString());
System.out.println(notX);
}
}
```

View File

@@ -561,7 +561,7 @@ public class BinPackingProblemSat {
for (int i = 0; i < numItems; ++i) {
expr.addTerm(x[i][b], items[i][0]);
}
model.addEquality(expr.build(), load[b]);
model.addEquality(expr, load[b]);
}
// Place all items.
@@ -570,7 +570,7 @@ public class BinPackingProblemSat {
for (int b = 0; b < numBins; ++b) {
expr.add(x[i][b]);
}
model.addEquality(expr.build(), items[i][1]);
model.addEquality(expr, items[i][1]);
}
// Links load and slack.

View File

@@ -238,9 +238,9 @@ public class RabbitsAndPheasantsSat {
IntVar r = model.newIntVar(0, 100, "r");
IntVar p = model.newIntVar(0, 100, "p");
// 20 heads.
model.addEquality(LinearExpr.newBuilder().add(r).add(p).build(), 20);
model.addEquality(LinearExpr.newBuilder().add(r).add(p), 20);
// 56 legs.
model.addEquality(LinearExpr.newBuilder().addTerm(r, 4).addTerm(p, 2).build(), 56);
model.addEquality(LinearExpr.newBuilder().addTerm(r, 4).addTerm(p, 2), 56);
// Creates a solver and solves the model.
CpSolver solver = new CpSolver();

View File

@@ -177,7 +177,7 @@ public class IntervalSampleSat {
model.newIntervalVar(
startVar,
LinearExpr.constant(10),
LinearExpr.newBuilder().add(endVar).add(2).build(),
LinearExpr.newBuilder().add(endVar).add(2),
"interval");
System.out.println(intervalVar);
@@ -347,7 +347,7 @@ public class OptionalIntervalSampleSat {
model.newOptionalIntervalVar(
startVar,
LinearExpr.constant(10),
LinearExpr.newBuilder().add(endVar).add(2).build(),
LinearExpr.newBuilder().add(endVar).add(2),
presence,
"interval");
System.out.println(intervalVar);
@@ -1067,7 +1067,7 @@ public class RankingSampleSat {
expr.add(precedences[j][i]);
}
expr.add(-1);
model.addEquality(ranks[i], expr.build());
model.addEquality(ranks[i], expr);
}
}
@@ -1132,7 +1132,7 @@ public class RankingSampleSat {
obj.addTerm(presences[t], -7);
}
obj.addTerm(makespan, 2);
model.minimize(obj.build());
model.minimize(obj);
// Creates a solver and solves the model.
CpSolver solver = new CpSolver();

View File

@@ -131,7 +131,7 @@ public class AssignmentGroupsSat {
for (int task : allTasks) {
expr.add(x[worker][task]);
}
model.addEquality(work[worker], expr.build());
model.addEquality(work[worker], expr);
}
// Define the allowed groups of worders
@@ -160,7 +160,7 @@ public class AssignmentGroupsSat {
obj.addTerm(x[worker][task], costs[worker][task]);
}
}
model.minimize(obj.build());
model.minimize(obj);
// [END objective]
// Solve

View File

@@ -91,7 +91,7 @@ public class AssignmentSat {
obj.addTerm(x[worker][task], costs[worker][task]);
}
}
model.minimize(obj.build());
model.minimize(obj);
// [END objective]
// Solve

View File

@@ -79,7 +79,7 @@ public class AssignmentTaskSizesSat {
for (int task : allTasks) {
expr.addTerm(x[worker][task], taskSizes[task]);
}
model.addLessOrEqual(expr.build(), totalSizeMax);
model.addLessOrEqual(expr, totalSizeMax);
}
// Each task is assigned to exactly one worker.
@@ -100,7 +100,7 @@ public class AssignmentTaskSizesSat {
obj.addTerm(x[worker][task], costs[worker][task]);
}
}
model.minimize(obj.build());
model.minimize(obj);
// [END objective]
// Solve

View File

@@ -96,7 +96,7 @@ public class AssignmentTeamsSat {
team1Tasks.add(x[worker][task]);
}
}
model.addLessOrEqual(team1Tasks.build(), teamMax);
model.addLessOrEqual(team1Tasks, teamMax);
LinearExprBuilder team2Tasks = LinearExpr.newBuilder();
for (int worker : team2) {
@@ -104,7 +104,7 @@ public class AssignmentTeamsSat {
team2Tasks.add(x[worker][task]);
}
}
model.addLessOrEqual(team2Tasks.build(), teamMax);
model.addLessOrEqual(team2Tasks, teamMax);
// [END constraints]
// Objective
@@ -115,7 +115,7 @@ public class AssignmentTeamsSat {
obj.addTerm(x[worker][task], costs[worker][task]);
}
}
model.minimize(obj.build());
model.minimize(obj);
// [END objective]
// Solve

View File

@@ -64,7 +64,7 @@ public class BinPackingProblemSat {
for (int i = 0; i < numItems; ++i) {
expr.addTerm(x[i][b], items[i][0]);
}
model.addEquality(expr.build(), load[b]);
model.addEquality(expr, load[b]);
}
// Place all items.
@@ -73,7 +73,7 @@ public class BinPackingProblemSat {
for (int b = 0; b < numBins; ++b) {
expr.add(x[i][b]);
}
model.addEquality(expr.build(), items[i][1]);
model.addEquality(expr, items[i][1]);
}
// Links load and slack.

View File

@@ -29,8 +29,8 @@ public class IntervalSampleSat {
// An interval can be created from three affine expressions.
IntVar startVar = model.newIntVar(0, horizon, "start");
IntVar endVar = model.newIntVar(0, horizon, "end");
IntervalVar intervalVar = model.newIntervalVar(startVar, LinearExpr.constant(10),
LinearExpr.newBuilder().add(endVar).add(2).build(), "interval");
IntervalVar intervalVar = model.newIntervalVar(
startVar, LinearExpr.constant(10), LinearExpr.newBuilder().add(endVar).add(2), "interval");
System.out.println(intervalVar);
// If the size is fixed, a simpler version uses the start expression and the size.

View File

@@ -25,6 +25,6 @@ public class LiteralSampleSat {
CpModel model = new CpModel();
BoolVar x = model.newBoolVar("x");
Literal notX = x.not();
System.out.println(notX.getShortString());
System.out.println(notX);
}
}

View File

@@ -125,7 +125,7 @@ public class MinimalJobshopSat {
// [START objective]
// Makespan objective.
IntVar objVar = model.newIntVar(0, horizon, "makespan");
List<LinearExpr> ends = new ArrayList<>();
List<IntVar> ends = new ArrayList<>();
for (int jobID = 0; jobID < allJobs.size(); ++jobID) {
List<Task> job = allJobs.get(jobID);
List<Integer> key = Arrays.asList(jobID, job.size() - 1);

View File

@@ -74,7 +74,7 @@ public class MultipleKnapsackSat {
for (int i : allItems) {
load.addTerm(x[i][b], weights[i]);
}
model.addLessOrEqual(load.build(), binCapacities[b]);
model.addLessOrEqual(load, binCapacities[b]);
}
// [END constraints]
@@ -87,7 +87,7 @@ public class MultipleKnapsackSat {
obj.addTerm(x[i][b], values[i]);
}
}
model.maximize(obj.build());
model.maximize(obj);
// [END objective]
// [START solve]

View File

@@ -104,7 +104,7 @@ public class NursesSat {
numShiftsWorked.add(shifts[n][d][s]);
}
}
model.addLinearConstraint(numShiftsWorked.build(), minShiftsPerNurse, maxShiftsPerNurse);
model.addLinearConstraint(numShiftsWorked, minShiftsPerNurse, maxShiftsPerNurse);
}
// [END assign_nurses_evenly]

View File

@@ -32,7 +32,7 @@ public class OptionalIntervalSampleSat {
IntVar endVar = model.newIntVar(0, horizon, "end");
Literal presence = model.newBoolVar("presence");
IntervalVar intervalVar = model.newOptionalIntervalVar(startVar, LinearExpr.constant(10),
LinearExpr.newBuilder().add(endVar).add(2).build(), presence, "interval");
LinearExpr.newBuilder().add(endVar).add(2), presence, "interval");
System.out.println(intervalVar);
// If the size is fixed, a simpler version uses the start expression, the size and the literal.

View File

@@ -33,9 +33,9 @@ public class RabbitsAndPheasantsSat {
IntVar r = model.newIntVar(0, 100, "r");
IntVar p = model.newIntVar(0, 100, "p");
// 20 heads.
model.addEquality(LinearExpr.newBuilder().add(r).add(p).build(), 20);
model.addEquality(LinearExpr.newBuilder().add(r).add(p), 20);
// 56 legs.
model.addEquality(LinearExpr.newBuilder().addTerm(r, 4).addTerm(p, 2).build(), 56);
model.addEquality(LinearExpr.newBuilder().addTerm(r, 4).addTerm(p, 2), 56);
// Creates a solver and solves the model.
CpSolver solver = new CpSolver();

View File

@@ -89,7 +89,7 @@ public class RankingSampleSat {
expr.add(precedences[j][i]);
}
expr.add(-1);
model.addEquality(ranks[i], expr.build());
model.addEquality(ranks[i], expr);
}
}
@@ -152,7 +152,7 @@ public class RankingSampleSat {
obj.addTerm(presences[t], -7);
}
obj.addTerm(makespan, 2);
model.minimize(obj.build());
model.minimize(obj);
// Creates a solver and solves the model.
CpSolver solver = new CpSolver();

View File

@@ -151,7 +151,7 @@ public class ScheduleRequestsSat {
numShiftsWorked.add(shifts[n][d][s]);
}
}
model.addLinearConstraint(numShiftsWorked.build(), minShiftsPerNurse, maxShiftsPerNurse);
model.addLinearConstraint(numShiftsWorked, minShiftsPerNurse, maxShiftsPerNurse);
}
// [END assign_nurses_evenly]
@@ -164,7 +164,7 @@ public class ScheduleRequestsSat {
}
}
}
model.maximize(obj.build());
model.maximize(obj);
// [END objective]
// Creates a solver and solves the model.