model builder: add clear_terms API; add enforced linear constraint in java + minimal test
This commit is contained in:
@@ -19,6 +19,7 @@ java_library(
|
||||
srcs = [
|
||||
"AffineExpression.java",
|
||||
"ConstantExpression.java",
|
||||
"EnforcedLinearConstraint.java",
|
||||
"LinearArgument.java",
|
||||
"LinearConstraint.java",
|
||||
"LinearExpr.java",
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright 2010-2022 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.modelbuilder;
|
||||
|
||||
/** Wrapper around a linear constraint stored in the ModelBuilderHelper instance. */
|
||||
public class EnforcedLinearConstraint {
|
||||
public EnforcedLinearConstraint(ModelBuilderHelper helper) {
|
||||
this.helper = helper;
|
||||
this.index = helper.addEnforcedLinearConstraint();
|
||||
}
|
||||
|
||||
EnforcedLinearConstraint(ModelBuilderHelper helper, int index) {
|
||||
this.helper = helper;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/** Returns the index of the constraint in the model. */
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/** Returns the constraint builder. */
|
||||
public ModelBuilderHelper getHelper() {
|
||||
return helper;
|
||||
}
|
||||
|
||||
/** Returns the lower bound of the constraint. */
|
||||
public double getLowerBound() {
|
||||
return helper.getEnforcedConstraintLowerBound(index);
|
||||
}
|
||||
|
||||
/** Sets the lower bound of the constraint. */
|
||||
public void setLowerBound(double lb) {
|
||||
helper.setEnforcedConstraintLowerBound(index, lb);
|
||||
}
|
||||
|
||||
/** Returns the upper bound of the constraint. */
|
||||
public double getUpperBound() {
|
||||
return helper.getEnforcedConstraintUpperBound(index);
|
||||
}
|
||||
|
||||
/** Sets the upper bound of the constraint. */
|
||||
public void setUpperBound(double ub) {
|
||||
helper.setEnforcedConstraintUpperBound(index, ub);
|
||||
}
|
||||
|
||||
/** Returns the name of the constraint given upon creation. */
|
||||
public String getName() {
|
||||
return helper.getEnforcedConstraintName(index);
|
||||
}
|
||||
|
||||
// Sets the name of the constraint. */
|
||||
public void setName(String name) {
|
||||
helper.setEnforcedConstraintName(index, name);
|
||||
}
|
||||
|
||||
// Adds var * coeff to the constraint.
|
||||
public void addEnforcedTerm(Variable v, double coeff) {
|
||||
helper.safeAddEnforcedConstraintTerm(index, v.getIndex(), coeff);
|
||||
}
|
||||
|
||||
// Returns the indicator variable of the constraint.
|
||||
public Variable getIndicatorVariable() {
|
||||
return new Variable(helper, helper.getEnforcedIndicatorVariableIndex(index));
|
||||
}
|
||||
|
||||
// Sets the indicator variable of the constraint.
|
||||
public void setIndicatorVariable(Variable v) {
|
||||
helper.setEnforcedIndicatorVariable(index, v.index);
|
||||
}
|
||||
|
||||
// Returns the indicator value of the constraint.
|
||||
public boolean getIndicatorValue() {
|
||||
return helper.getEnforcedIndicatorValue(index);
|
||||
}
|
||||
|
||||
// Sets the indicator value of the constraint.
|
||||
public void setIndicatorValue(boolean b) {
|
||||
helper.setEnforcedIndicatorValue(index, b);
|
||||
}
|
||||
|
||||
/** Inline setter */
|
||||
public EnforcedLinearConstraint withName(String name) {
|
||||
setName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
private final ModelBuilderHelper helper;
|
||||
private final int index;
|
||||
}
|
||||
@@ -117,11 +117,6 @@ public final class ModelBuilder {
|
||||
return lin;
|
||||
}
|
||||
|
||||
/** Returns the number of variables in the model. */
|
||||
public int numVariables() {
|
||||
return helper.numVariables();
|
||||
}
|
||||
|
||||
/** Adds {@code expr == value}. */
|
||||
public LinearConstraint addEquality(LinearArgument expr, double value) {
|
||||
return addLinearConstraint(expr, value, value);
|
||||
@@ -161,16 +156,80 @@ public final class ModelBuilder {
|
||||
return addLinearConstraint(difference, 0.0, Double.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
/** Returns the number of constraints in the model. */
|
||||
public int numConstraints() {
|
||||
return helper.numConstraints();
|
||||
}
|
||||
|
||||
/** Rebuilds a linear constraint from its index. */
|
||||
public LinearConstraint constraintFromIndex(int index) {
|
||||
return new LinearConstraint(helper, index);
|
||||
}
|
||||
|
||||
// Enforced Linear constraints.
|
||||
|
||||
/** Adds {@code lb <= expr <= ub}. */
|
||||
public EnforcedLinearConstraint addEnforcedLinearConstraint(LinearArgument expr, double lb, double ub, Variable iVar, boolean iValue) {
|
||||
EnforcedLinearConstraint lin = new EnforcedLinearConstraint(helper);
|
||||
lin.setIndicatorVariable(iVar);
|
||||
lin.setIndicatorValue(iValue);
|
||||
final LinearExpr e = expr.build();
|
||||
for (int i = 0; i < e.numElements(); ++i) {
|
||||
helper.addEnforcedConstraintTerm(lin.getIndex(), e.getVariableIndex(i), e.getCoefficient(i));
|
||||
}
|
||||
double offset = e.getOffset();
|
||||
if (lb == Double.NEGATIVE_INFINITY || lb == Double.POSITIVE_INFINITY) {
|
||||
lin.setLowerBound(lb);
|
||||
} else {
|
||||
lin.setLowerBound(lb - offset);
|
||||
}
|
||||
if (ub == Double.NEGATIVE_INFINITY || ub == Double.POSITIVE_INFINITY) {
|
||||
lin.setUpperBound(ub);
|
||||
} else {
|
||||
lin.setUpperBound(ub - offset);
|
||||
}
|
||||
return lin;
|
||||
}
|
||||
|
||||
/** Adds {@code expr == value}. */
|
||||
public EnforcedLinearConstraint addEnforcedEquality(LinearArgument expr, double value, Variable iVar, boolean iValue) {
|
||||
return addEnforcedLinearConstraint(expr, value, value, iVar, iValue);
|
||||
}
|
||||
|
||||
/** Adds {@code left == right}. */
|
||||
public EnforcedLinearConstraint addEnforcedEquality(LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) {
|
||||
LinearExprBuilder difference = LinearExpr.newBuilder();
|
||||
difference.addTerm(left, 1);
|
||||
difference.addTerm(right, -1);
|
||||
return addEnforcedLinearConstraint(difference, 0.0, 0.0, iVar, iValue);
|
||||
}
|
||||
|
||||
/** Adds {@code expr <= value}. */
|
||||
public EnforcedLinearConstraint addEnforcedLessOrEqual(LinearArgument expr, double value, Variable iVar, boolean iValue) {
|
||||
return addEnforcedLinearConstraint(expr, Double.NEGATIVE_INFINITY, value, iVar, iValue);
|
||||
}
|
||||
|
||||
/** Adds {@code left <= right}. */
|
||||
public EnforcedLinearConstraint addEnforcedLessOrEqual(LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) {
|
||||
LinearExprBuilder difference = LinearExpr.newBuilder();
|
||||
difference.addTerm(left, 1);
|
||||
difference.addTerm(right, -1);
|
||||
return addEnforcedLinearConstraint(difference, Double.NEGATIVE_INFINITY, 0.0, iVar, iValue);
|
||||
}
|
||||
|
||||
/** Adds {@code expr >= value}. */
|
||||
public EnforcedLinearConstraint addEnforcedGreaterOrEqual(LinearArgument expr, double value, Variable iVar, boolean iValue) {
|
||||
return addEnforcedLinearConstraint(expr, value, Double.POSITIVE_INFINITY, iVar, iValue);
|
||||
}
|
||||
|
||||
/** Adds {@code left >= right}. */
|
||||
public EnforcedLinearConstraint addEnforcedGreaterOrEqual(LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) {
|
||||
LinearExprBuilder difference = LinearExpr.newBuilder();
|
||||
difference.addTerm(left, 1);
|
||||
difference.addTerm(right, -1);
|
||||
return addEnforcedLinearConstraint(difference, 0.0, Double.POSITIVE_INFINITY, iVar, iValue);
|
||||
}
|
||||
|
||||
/** Rebuilds a linear constraint from its index. */
|
||||
public EnforcedLinearConstraint enforcedConstraintFromIndex(int index) {
|
||||
return new EnforcedLinearConstraint(helper, index);
|
||||
}
|
||||
|
||||
/** Minimize expression */
|
||||
public void minimize(LinearArgument obj) {
|
||||
optimize(obj, false);
|
||||
@@ -214,12 +273,22 @@ public final class ModelBuilder {
|
||||
}
|
||||
|
||||
/** Adds var == value as a hint to the model. Note that variables must not appear more than once in the list of hints. */
|
||||
void addHint(Variable var, double value) {
|
||||
helper.addHint(var.getIndex(), value);
|
||||
void addHint(Variable v, double value) {
|
||||
helper.addHint(v.getIndex(), value);
|
||||
}
|
||||
|
||||
// Model getters, import, export.
|
||||
|
||||
/** Returns the number of variables in the model. */
|
||||
public int numVariables() {
|
||||
return helper.numVariables();
|
||||
}
|
||||
|
||||
/** Returns the number of constraints in the model. */
|
||||
public int numConstraints() {
|
||||
return helper.numConstraints();
|
||||
}
|
||||
|
||||
/** Returns the name of the model. */
|
||||
public String getName() {
|
||||
return helper.getName();
|
||||
|
||||
@@ -27,9 +27,28 @@ public final class ModelBuilderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runMinimalLinearExample_ok() {
|
||||
public void testEnforcedLinearApi() {
|
||||
ModelBuilder model = new ModelBuilder();
|
||||
model.setName("minimal_linear_example");
|
||||
model.setName("minimal enforced linear test");
|
||||
double infinity = Double.POSITIVE_INFINITY;
|
||||
Variable x = model.newNumVar(0.0, infinity, "x");
|
||||
Variable y = model.newNumVar(0.0, infinity, "y");
|
||||
Variable z = model.newBoolVar("z");
|
||||
|
||||
assertThat(model.numVariables()).isEqualTo(3);
|
||||
|
||||
EnforcedLinearConstraint c0 = model.addEnforcedGreaterOrEqual(
|
||||
LinearExpr.newBuilder().add(x).addTerm(y, 2.0), 10.0, z, false);
|
||||
assertThat(c0.getLowerBound()).isEqualTo(10.0);
|
||||
assertThat(c0.getIndicatorVariable().getIndex()).isEqualTo(z.getIndex());
|
||||
assertThat(c0.getIndicatorValue()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runMinimalLinearExample_ok() {
|
||||
final String name = "minimal_linear_example";
|
||||
ModelBuilder model = new ModelBuilder();
|
||||
model.setName(name);
|
||||
double infinity = Double.POSITIVE_INFINITY;
|
||||
Variable x1 = model.newNumVar(0.0, infinity, "x1");
|
||||
Variable x2 = model.newNumVar(0.0, infinity, "x2");
|
||||
@@ -88,8 +107,8 @@ public final class ModelBuilderTest {
|
||||
assertThat(solver.getActivity(c1)).isWithin(1e-5).of(600.0);
|
||||
assertThat(solver.getActivity(c2)).isWithin(1e-5).of(200.0);
|
||||
|
||||
assertThat(model.exportToLpString(false)).contains("minimal_linear_example");
|
||||
assertThat(model.exportToMpsString(false)).contains("minimal_linear_example");
|
||||
assertThat(model.exportToLpString(false)).contains(name);
|
||||
assertThat(model.exportToMpsString(false)).contains(name);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -117,14 +117,13 @@ class GlobalRefGuard {
|
||||
%rename (getVarName) operations_research::ModelBuilderHelper::VarName;
|
||||
%rename (getVarObjectiveCoefficient) operations_research::ModelBuilderHelper::VarObjectiveCoefficient;
|
||||
%rename (getVarUpperBound) operations_research::ModelBuilderHelper::VarUpperBound;
|
||||
%rename (numVariables) operations_research::ModelBuilderHelper::num_variables;
|
||||
%rename (setVarIntegrality) operations_research::ModelBuilderHelper::SetVarIntegrality;
|
||||
%rename (setVarLowerBound) operations_research::ModelBuilderHelper::SetVarLowerBound;
|
||||
%rename (setVarName) operations_research::ModelBuilderHelper::SetVarName;
|
||||
%rename (setVarObjectiveCoefficient) operations_research::ModelBuilderHelper::SetVarObjectiveCoefficient;
|
||||
%rename (setVarUpperBound) operations_research::ModelBuilderHelper::SetVarUpperBound;
|
||||
|
||||
// Constraint API.
|
||||
// Linear Constraint API.
|
||||
%rename (addConstraintTerm) operations_research::ModelBuilderHelper::AddConstraintTerm;
|
||||
%rename (addLinearConstraint) operations_research::ModelBuilderHelper::AddLinearConstraint;
|
||||
%rename (getConstraintCoefficients) operations_research::ModelBuilderHelper::ConstraintCoefficients;
|
||||
@@ -132,11 +131,29 @@ class GlobalRefGuard {
|
||||
%rename (getConstraintName) operations_research::ModelBuilderHelper::ConstraintName;
|
||||
%rename (getConstraintUpperBound) operations_research::ModelBuilderHelper::ConstraintUpperBound;
|
||||
%rename (getConstraintVarIndices) operations_research::ModelBuilderHelper::ConstraintVarIndices;
|
||||
%rename (numConstraints) operations_research::ModelBuilderHelper::num_constraints;
|
||||
%rename (safeAddConstraintTerm) operations_research::ModelBuilderHelper::SafeAddConstraintTerm;
|
||||
%rename (setConstraintCoefficients) operations_research::ModelBuilderHelper::SetConstraintCoefficients;
|
||||
%rename (setConstraintLowerBound) operations_research::ModelBuilderHelper::SetConstraintLowerBound;
|
||||
%rename (setConstraintName) operations_research::ModelBuilderHelper::SetConstraintName;
|
||||
%rename (setConstraintUpperBound) operations_research::ModelBuilderHelper::SetConstraintUpperBound;
|
||||
|
||||
// Enforced Linear Constraint API.
|
||||
%rename (addEnforcedConstraintTerm) operations_research::ModelBuilderHelper::AddEnforcedConstraintTerm;
|
||||
%rename (addEnforcedLinearConstraint) operations_research::ModelBuilderHelper::AddEnforcedLinearConstraint;
|
||||
%rename (getEnforcedConstraintCoefficients) operations_research::ModelBuilderHelper::EnforcedConstraintCoefficients;
|
||||
%rename (getEnforcedConstraintLowerBound) operations_research::ModelBuilderHelper::EnforcedConstraintLowerBound;
|
||||
%rename (getEnforcedConstraintName) operations_research::ModelBuilderHelper::EnforcedConstraintName;
|
||||
%rename (getEnforcedConstraintUpperBound) operations_research::ModelBuilderHelper::EnforcedConstraintUpperBound;
|
||||
%rename (getEnforcedConstraintVarIndices) operations_research::ModelBuilderHelper::EnforcedConstraintVarIndices;
|
||||
%rename (getEnforcedIndicatorValue) operations_research::ModelBuilderHelper::EnforcedIndicatorValue;
|
||||
%rename (getEnforcedIndicatorVariableIndex) operations_research::ModelBuilderHelper::EnforcedIndicatorVariableIndex;
|
||||
%rename (safeAddEnforcedConstraintTerm) operations_research::ModelBuilderHelper::SafeAddEnforcedConstraintTerm;
|
||||
%rename (setEnforcedConstraintLowerBound) operations_research::ModelBuilderHelper::SetEnforcedConstraintLowerBound;
|
||||
%rename (setEnforcedConstraintName) operations_research::ModelBuilderHelper::SetEnforcedConstraintName;
|
||||
%rename (setEnforcedConstraintUpperBound) operations_research::ModelBuilderHelper::SetEnforcedConstraintUpperBound;
|
||||
%rename (setEnforcedIndicatorValue) operations_research::ModelBuilderHelper::SetEnforcedIndicatorValue;
|
||||
%rename (setEnforcedIndicatorVariable) operations_research::ModelBuilderHelper::SetEnforcedIndicatorVariable;
|
||||
|
||||
// Objective API.
|
||||
%rename (clearObjective) operations_research::ModelBuilderHelper::ClearObjective;
|
||||
%rename (getMaximize) operations_research::ModelBuilderHelper::maximize;
|
||||
@@ -149,6 +166,8 @@ class GlobalRefGuard {
|
||||
%rename (addHint) operations_research::ModelBuilderHelper::AddHint;
|
||||
|
||||
// Model API.
|
||||
%rename (numVariables) operations_research::ModelBuilderHelper::num_variables;
|
||||
%rename (numConstraints) operations_research::ModelBuilderHelper::num_constraints;
|
||||
%rename (getName) operations_research::ModelBuilderHelper::name;
|
||||
%rename (setName) operations_research::ModelBuilderHelper::SetName;
|
||||
%rename (writeModelToFile) operations_research::ModelBuilderHelper::WriteModelToFile;
|
||||
|
||||
@@ -694,6 +694,10 @@ class LinearConstraint:
|
||||
self.__helper.safe_add_term_to_constraint(self.__index, var.index,
|
||||
coeff)
|
||||
|
||||
def clear_terms(self) -> None:
|
||||
"""Clear all terms of the constraint."""
|
||||
self.__helper.clear_constraint_terms(self.__index)
|
||||
|
||||
|
||||
class EnforcedLinearConstraint:
|
||||
"""Stores an enforced linear equation, also name indicator constraint.
|
||||
@@ -809,6 +813,10 @@ class EnforcedLinearConstraint:
|
||||
self.__helper.safe_add_term_to_enforced_constraint(
|
||||
self.__index, var.index, coeff)
|
||||
|
||||
def clear_terms(self) -> None:
|
||||
"""Clear all terms of the constraint."""
|
||||
self.__helper.clear_enforced_constraint_terms(self.__index)
|
||||
|
||||
|
||||
class ModelBuilder:
|
||||
"""Methods for building a linear model.
|
||||
|
||||
@@ -168,6 +168,12 @@ void ModelBuilderHelper::SetConstraintUpperBound(int ct_index, double ub) {
|
||||
model_.mutable_constraint(ct_index)->set_upper_bound(ub);
|
||||
}
|
||||
|
||||
void ModelBuilderHelper::ClearConstraintTerms(int ct_index) {
|
||||
MPConstraintProto* ct_proto = model_.mutable_constraint(ct_index);
|
||||
ct_proto->clear_var_index();
|
||||
ct_proto->clear_coefficient();
|
||||
}
|
||||
|
||||
void ModelBuilderHelper::AddConstraintTerm(int ct_index, int var_index,
|
||||
double coeff) {
|
||||
if (coeff == 0.0) return;
|
||||
@@ -264,6 +270,14 @@ void ModelBuilderHelper::SetEnforcedConstraintUpperBound(int ct_index, double ub
|
||||
ct_proto->set_upper_bound(ub);
|
||||
}
|
||||
|
||||
void ModelBuilderHelper::ClearEnforcedConstraintTerms(int ct_index) {
|
||||
MPConstraintProto* ct_proto = model_.mutable_general_constraint(ct_index)
|
||||
->mutable_indicator_constraint()
|
||||
->mutable_constraint();
|
||||
ct_proto->clear_var_index();
|
||||
ct_proto->clear_coefficient();
|
||||
}
|
||||
|
||||
void ModelBuilderHelper::AddEnforcedConstraintTerm(int ct_index, int var_index,
|
||||
double coeff) {
|
||||
DCHECK(IsEnforcedConstraint(ct_index));
|
||||
|
||||
@@ -84,6 +84,7 @@ class ModelBuilderHelper {
|
||||
void SafeAddConstraintTerm(int ct_index, int var_index, double coeff);
|
||||
void SetConstraintName(int ct_index, const std::string& name);
|
||||
void SetConstraintCoefficient(int ct_index, int var_index, double coeff);
|
||||
void ClearConstraintTerms(int ct_index);
|
||||
|
||||
double ConstraintLowerBound(int ct_index) const;
|
||||
double ConstraintUpperBound(int ct_index) const;
|
||||
@@ -102,6 +103,7 @@ class ModelBuilderHelper {
|
||||
void SetEnforcedConstraintCoefficient(int ct_index, int var_index, double coeff);
|
||||
void SetEnforcedIndicatorVariable(int ct_index, int var_index);
|
||||
void SetEnforcedIndicatorValue(int ct_index, bool positive);
|
||||
void ClearEnforcedConstraintTerms(int ct_index);
|
||||
|
||||
double EnforcedConstraintLowerBound(int ct_index) const;
|
||||
double EnforcedConstraintUpperBound(int ct_index) const;
|
||||
|
||||
Reference in New Issue
Block a user