[CP-SAT] fix more fuzzer bugs; polish python code
This commit is contained in:
@@ -608,7 +608,7 @@ bool CpModelPresolver::PresolveAtMostOrExactlyOne(ConstraintProto* ct) {
|
||||
|
||||
// By domination argument, we can fix to false everything but the minimum.
|
||||
if (singleton_literal_with_cost.size() > 1) {
|
||||
std::sort(
|
||||
std::stable_sort(
|
||||
singleton_literal_with_cost.begin(),
|
||||
singleton_literal_with_cost.end(),
|
||||
[](const std::pair<int, int64_t>& a,
|
||||
@@ -1629,8 +1629,8 @@ bool CpModelPresolver::PresolveIntProd(ConstraintProto* ct) {
|
||||
PossibleIntegerOverflow(*context_->working_model, lin->vars(),
|
||||
lin->coeffs(), lin->domain(0))) {
|
||||
context_->working_model->mutable_constraints()->RemoveLast();
|
||||
// Re-add a new term with the constant factor.
|
||||
ct->mutable_int_prod()->add_exprs()->set_offset(constant_factor);
|
||||
// The constant factor will be handled by the creation of an affine
|
||||
// relation below.
|
||||
} else { // Replace with a linear equation.
|
||||
context_->UpdateNewConstraintsVariableUsage();
|
||||
context_->UpdateRuleStats("int_prod: linearize product by constant.");
|
||||
|
||||
@@ -191,7 +191,7 @@ void CompressTuples(absl::Span<const int64_t> domain_sizes,
|
||||
std::vector<int> to_remove;
|
||||
std::vector<int64_t> tuple_minus_var_i(num_vars - 1);
|
||||
for (int i = 0; i < num_vars; ++i) {
|
||||
const int domain_size = domain_sizes[i];
|
||||
const int64_t domain_size = domain_sizes[i];
|
||||
if (domain_size == 1) continue;
|
||||
absl::flat_hash_map<std::vector<int64_t>, std::vector<int>>
|
||||
masked_tuples_to_indices;
|
||||
|
||||
@@ -196,6 +196,18 @@ TEST(CompressTuplesTest, NotPerfect) {
|
||||
EXPECT_EQ(tuples, expected_tuples);
|
||||
}
|
||||
|
||||
TEST(CompressTuplesTest, BigInteger) {
|
||||
const std::vector<int64_t> domain_sizes = {576460752303423490};
|
||||
const std::vector<std::vector<int64_t>> original_tuples = {
|
||||
{1},
|
||||
{2},
|
||||
};
|
||||
std::vector<std::vector<int64_t>> tuples = original_tuples;
|
||||
CompressTuples(domain_sizes, &tuples);
|
||||
|
||||
EXPECT_EQ(tuples, original_tuples);
|
||||
}
|
||||
|
||||
TEST(FullyCompressTuplesTest, BasicTest) {
|
||||
const std::vector<int64_t> domain_sizes = {4, 4};
|
||||
std::vector<std::vector<int64_t>> tuples = {
|
||||
|
||||
@@ -106,8 +106,14 @@ void AddDiffnCumulativeRelationOnX(SchedulingConstraintHelper* x,
|
||||
const IntegerVariable max_end_var =
|
||||
CreateVariableAtOrBelowMaxOf(y->Ends(), model);
|
||||
|
||||
// (max_end - min_start) >= capacity.
|
||||
auto* integer_trail = model->GetOrCreate<IntegerTrail>();
|
||||
if (integer_trail->UpperBound(max_end_var) <
|
||||
integer_trail->LowerBound(min_start_var)) {
|
||||
// Trivial infeasible case, will be handled by the linear constraint
|
||||
// from the interval.
|
||||
return;
|
||||
}
|
||||
// (max_end - min_start) >= capacity.
|
||||
const AffineExpression capacity(model->Add(NewIntegerVariable(
|
||||
0, CapSub(integer_trail->UpperBound(max_end_var).value(),
|
||||
integer_trail->LowerBound(min_start_var).value()))));
|
||||
|
||||
@@ -61,6 +61,7 @@ void LinearConstraintBuilder::AddTerm(AffineExpression expr,
|
||||
terms_.push_back({NegationOf(expr.var), -coeff * expr.coeff});
|
||||
}
|
||||
}
|
||||
DCHECK(!ProdOverflow(coeff, expr.constant));
|
||||
offset_ += coeff * expr.constant;
|
||||
}
|
||||
|
||||
|
||||
@@ -359,12 +359,13 @@ LinearProgrammingConstraint::LinearProgrammingConstraint(
|
||||
lp_reduced_cost_.assign(vars.size(), 0.0);
|
||||
|
||||
if (!vars.empty()) {
|
||||
const int max_index = NegationOf(vars.back()).value();
|
||||
const IntegerVariable max_index = std::max(
|
||||
NegationOf(vars.back()), integer_trail_->NumIntegerVariables() - 1);
|
||||
if (max_index >= expanded_lp_solution_.size()) {
|
||||
expanded_lp_solution_.assign(max_index + 1, 0.0);
|
||||
expanded_lp_solution_.assign(max_index.value() + 1, 0.0);
|
||||
}
|
||||
if (max_index >= expanded_reduced_costs_.size()) {
|
||||
expanded_reduced_costs_.assign(max_index + 1, 0.0);
|
||||
expanded_reduced_costs_.assign(max_index.value() + 1, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -805,6 +806,17 @@ void LinearProgrammingConstraint::SetLevel(int level) {
|
||||
lp_solution_is_set_ = true;
|
||||
lp_solution_ = level_zero_lp_solution_;
|
||||
lp_solution_level_ = 0;
|
||||
// Add the fixed variables. They might have been skipped when we did the
|
||||
// linear relaxation of the model, but cut generators expect all variables
|
||||
// to have an LP value.
|
||||
if (expanded_lp_solution_.size() < integer_trail_->NumIntegerVariables()) {
|
||||
expanded_lp_solution_.resize(integer_trail_->NumIntegerVariables());
|
||||
}
|
||||
for (IntegerVariable i(0); i < integer_trail_->NumIntegerVariables(); ++i) {
|
||||
if (integer_trail_->IsFixed(i)) {
|
||||
expanded_lp_solution_[i] = ToDouble(integer_trail_->LowerBound(i));
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < lp_solution_.size(); i++) {
|
||||
const IntegerVariable var = extended_integer_variables_[i];
|
||||
expanded_lp_solution_[var] = lp_solution_[i];
|
||||
@@ -1175,7 +1187,7 @@ bool LinearProgrammingConstraint::AnalyzeLp() {
|
||||
// linear expression == rhs, we can use this to propagate more!
|
||||
//
|
||||
// TODO(user): Also propagate on -cut ? in practice we already do that in many
|
||||
// places were we try to generate the cut on -cut... But we coould do it sooner
|
||||
// places were we try to generate the cut on -cut... But we could do it sooner
|
||||
// and more cleanly here.
|
||||
bool LinearProgrammingConstraint::PreprocessCut(IntegerVariable first_slack,
|
||||
CutData* cut) {
|
||||
|
||||
@@ -1302,7 +1302,10 @@ void AppendLinearConstraintRelaxation(const ConstraintProto& ct,
|
||||
const IntegerVariable int_var = mapping->Integer(ref);
|
||||
lc.AddTerm(int_var, coeff);
|
||||
}
|
||||
relaxation->linear_constraints.push_back(lc.Build());
|
||||
LinearConstraint built_ct = lc.Build();
|
||||
if (!PossibleOverflow(*integer_trail, built_ct)) {
|
||||
relaxation->linear_constraints.push_back(std::move(built_ct));
|
||||
}
|
||||
}
|
||||
if (rhs_domain_max < max_activity) {
|
||||
// And(ei) => terms <= rhs_domain_max
|
||||
@@ -1319,7 +1322,10 @@ void AppendLinearConstraintRelaxation(const ConstraintProto& ct,
|
||||
const IntegerVariable int_var = mapping->Integer(ref);
|
||||
lc.AddTerm(int_var, coeff);
|
||||
}
|
||||
relaxation->linear_constraints.push_back(lc.Build());
|
||||
LinearConstraint built_ct = lc.Build();
|
||||
if (!PossibleOverflow(*integer_trail, built_ct)) {
|
||||
relaxation->linear_constraints.push_back(std::move(built_ct));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ cc_library(
|
||||
hdrs = ["linear_expr.h"],
|
||||
deps = [
|
||||
"//ortools/sat:cp_model_cc_proto",
|
||||
"//ortools/util:fp_roundtrip_conv",
|
||||
"//ortools/util:sorted_interval_list",
|
||||
"@com_google_absl//absl/container:btree",
|
||||
"@com_google_absl//absl/container:fixed_array",
|
||||
|
||||
@@ -721,7 +721,7 @@ class CpModel:
|
||||
if not isinstance(index, pd.Index):
|
||||
raise TypeError("Non-index object is used as index")
|
||||
if not name.isidentifier():
|
||||
raise ValueError(f"name={name} is not a valid identifier")
|
||||
raise ValueError(f"name={name!r} is not a valid identifier")
|
||||
if (
|
||||
isinstance(lower_bounds, IntegralTypes)
|
||||
and isinstance(upper_bounds, IntegralTypes)
|
||||
@@ -775,7 +775,7 @@ class CpModel:
|
||||
if not isinstance(index, pd.Index):
|
||||
raise TypeError("Non-index object is used as index")
|
||||
if not name.isidentifier():
|
||||
raise ValueError(f"name={name} is not a valid identifier")
|
||||
raise ValueError(f"name={name!r} is not a valid identifier")
|
||||
return pd.Series(
|
||||
index=index,
|
||||
data=[
|
||||
@@ -824,17 +824,9 @@ class CpModel:
|
||||
else:
|
||||
return self.add_bool_and([]) # Evaluate to true.
|
||||
raise TypeError(
|
||||
"not supported: CpModel.add_linear_expression_in_domain("
|
||||
+ str(linear_expr)
|
||||
+ " "
|
||||
+ str(type(linear_expr))
|
||||
+ " "
|
||||
+ str(linear_expr.is_integer())
|
||||
+ " "
|
||||
+ str(domain)
|
||||
+ " "
|
||||
+ str(type(domain))
|
||||
+ ")"
|
||||
f"not supported: CpModel.add_linear_expression_in_domain({linear_expr} "
|
||||
f" {type(linear_expr)} {linear_expr.is_integer()} {domain} "
|
||||
f"{type(domain)}"
|
||||
)
|
||||
|
||||
def add(self, ct: Union[BoundedLinearExpression, bool, np.bool_]) -> Constraint:
|
||||
@@ -1642,7 +1634,7 @@ class CpModel:
|
||||
if not isinstance(index, pd.Index):
|
||||
raise TypeError("Non-index object is used as index")
|
||||
if not name.isidentifier():
|
||||
raise ValueError(f"name={name} is not a valid identifier")
|
||||
raise ValueError(f"name={name!r} is not a valid identifier")
|
||||
|
||||
starts = _convert_to_linear_expr_series_and_validate_index(starts, index)
|
||||
sizes = _convert_to_linear_expr_series_and_validate_index(sizes, index)
|
||||
@@ -1715,7 +1707,7 @@ class CpModel:
|
||||
if not isinstance(index, pd.Index):
|
||||
raise TypeError("Non-index object is used as index")
|
||||
if not name.isidentifier():
|
||||
raise ValueError(f"name={name} is not a valid identifier")
|
||||
raise ValueError(f"name={name!r} is not a valid identifier")
|
||||
|
||||
starts = _convert_to_linear_expr_series_and_validate_index(starts, index)
|
||||
sizes = _convert_to_integral_series_and_validate_index(sizes, index)
|
||||
@@ -1819,7 +1811,7 @@ class CpModel:
|
||||
if not isinstance(index, pd.Index):
|
||||
raise TypeError("Non-index object is used as index")
|
||||
if not name.isidentifier():
|
||||
raise ValueError(f"name={name} is not a valid identifier")
|
||||
raise ValueError(f"name={name!r} is not a valid identifier")
|
||||
|
||||
starts = _convert_to_linear_expr_series_and_validate_index(starts, index)
|
||||
sizes = _convert_to_linear_expr_series_and_validate_index(sizes, index)
|
||||
@@ -1913,7 +1905,7 @@ class CpModel:
|
||||
if not isinstance(index, pd.Index):
|
||||
raise TypeError("Non-index object is used as index")
|
||||
if not name.isidentifier():
|
||||
raise ValueError(f"name={name} is not a valid identifier")
|
||||
raise ValueError(f"name={name!r} is not a valid identifier")
|
||||
|
||||
starts = _convert_to_linear_expr_series_and_validate_index(starts, index)
|
||||
sizes = _convert_to_integral_series_and_validate_index(sizes, index)
|
||||
@@ -2857,7 +2849,8 @@ class ObjectiveSolutionPrinter(CpSolverSolutionCallback):
|
||||
obj = self.objective_value
|
||||
print(
|
||||
f"Solution {self.__solution_count}, time ="
|
||||
f" {current_time - self.__start_time:0.2f} s, objective = {obj}"
|
||||
f" {current_time - self.__start_time:0.2f} s, objective = {obj}",
|
||||
flush=True,
|
||||
)
|
||||
self.__solution_count += 1
|
||||
|
||||
@@ -2885,7 +2878,7 @@ class VarArrayAndObjectiveSolutionPrinter(CpSolverSolutionCallback):
|
||||
)
|
||||
for v in self.__variables:
|
||||
print(f" {v} = {self.value(v)}", end=" ")
|
||||
print()
|
||||
print(flush=True)
|
||||
self.__solution_count += 1
|
||||
|
||||
@property
|
||||
@@ -2912,7 +2905,7 @@ class VarArraySolutionPrinter(CpSolverSolutionCallback):
|
||||
)
|
||||
for v in self.__variables:
|
||||
print(f" {v} = {self.value(v)}", end=" ")
|
||||
print()
|
||||
print(flush=True)
|
||||
self.__solution_count += 1
|
||||
|
||||
@property
|
||||
|
||||
@@ -570,42 +570,19 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertEqual(1.0, solver.objective_value)
|
||||
for i in range(100):
|
||||
self.assertEqual(solver.value(x[i]), 1 if i == 99 else 0)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
cp_model.LinearExpr.weighted_sum,
|
||||
[x[0]],
|
||||
[1, 2],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
cp_model.LinearExpr.weighted_sum,
|
||||
[x[0]],
|
||||
[1.1, 2.2],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
cp_model.LinearExpr.weighted_sum,
|
||||
[x[0], 3, 5],
|
||||
[1, 2],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
cp_model.LinearExpr.weighted_sum,
|
||||
[x[0], 2.2, 3],
|
||||
[1.1, 2.2],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
cp_model.LinearExpr.WeightedSum,
|
||||
[x[0]],
|
||||
[1, 2],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
cp_model.LinearExpr.WeightedSum,
|
||||
[x[0]],
|
||||
[1.1, 2.2],
|
||||
)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
cp_model.LinearExpr.weighted_sum([x[0]], [1, 2])
|
||||
with self.assertRaises(ValueError):
|
||||
cp_model.LinearExpr.weighted_sum([x[0]], [1.1, 2.2])
|
||||
with self.assertRaises(ValueError):
|
||||
cp_model.LinearExpr.weighted_sum([x[0], 3, 5], [1, 2])
|
||||
with self.assertRaises(ValueError):
|
||||
cp_model.LinearExpr.weighted_sum([x[0], 2.2, 3], [1.1, 2.2])
|
||||
with self.assertRaises(ValueError):
|
||||
cp_model.LinearExpr.WeightedSum([x[0]], [1, 2])
|
||||
with self.assertRaises(ValueError):
|
||||
cp_model.LinearExpr.WeightedSum([x[0]], [1.1, 2.2])
|
||||
|
||||
def testAllDifferent(self) -> None:
|
||||
print("testAllDifferent")
|
||||
@@ -643,7 +620,8 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertLen(model.proto.constraints[0].element.exprs, 4)
|
||||
self.assertEqual(0, model.proto.constraints[0].element.linear_index.vars[0])
|
||||
self.assertEqual(4, model.proto.constraints[0].element.linear_target.vars[0])
|
||||
self.assertRaises(ValueError, model.add_element, x[0], [], x[4])
|
||||
with self.assertRaises(ValueError):
|
||||
model.add_element(x[0], [], x[4])
|
||||
|
||||
def testFixedElement(self) -> None:
|
||||
print("testFixedElement")
|
||||
@@ -691,7 +669,8 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertLen(model.proto.constraints[0].circuit.heads, 5)
|
||||
self.assertLen(model.proto.constraints[0].circuit.tails, 5)
|
||||
self.assertLen(model.proto.constraints[0].circuit.literals, 5)
|
||||
self.assertRaises(ValueError, model.add_circuit, [])
|
||||
with self.assertRaises(ValueError):
|
||||
model.add_circuit([])
|
||||
|
||||
def testMultipleCircuit(self) -> None:
|
||||
print("testMultipleCircuit")
|
||||
@@ -706,7 +685,8 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertLen(model.proto.constraints[0].routes.heads, 5)
|
||||
self.assertLen(model.proto.constraints[0].routes.tails, 5)
|
||||
self.assertLen(model.proto.constraints[0].routes.literals, 5)
|
||||
self.assertRaises(ValueError, model.add_multiple_circuit, [])
|
||||
with self.assertRaises(ValueError):
|
||||
model.add_multiple_circuit([])
|
||||
|
||||
def testAllowedAssignments(self) -> None:
|
||||
print("testAllowedAssignments")
|
||||
@@ -719,18 +699,16 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertLen(model.proto.constraints, 1)
|
||||
self.assertLen(model.proto.constraints[0].table.exprs, 5)
|
||||
self.assertLen(model.proto.constraints[0].table.values, 15)
|
||||
self.assertRaises(
|
||||
TypeError,
|
||||
model.add_allowed_assignments,
|
||||
x,
|
||||
[(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
model.add_allowed_assignments,
|
||||
[],
|
||||
[(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)],
|
||||
)
|
||||
with self.assertRaises(TypeError):
|
||||
model.add_allowed_assignments(
|
||||
x,
|
||||
[(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)],
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
model.add_allowed_assignments(
|
||||
[],
|
||||
[(0, 1, 2, 3, 4), (4, 3, 2, 1, 1), (0, 0, 0, 0)],
|
||||
)
|
||||
|
||||
def testForbiddenAssignments(self) -> None:
|
||||
print("testForbiddenAssignments")
|
||||
@@ -770,31 +748,29 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertLen(model.proto.constraints[0].automaton.transition_label, 4)
|
||||
self.assertLen(model.proto.constraints[0].automaton.final_states, 2)
|
||||
self.assertEqual(0, model.proto.constraints[0].automaton.starting_state)
|
||||
self.assertRaises(
|
||||
TypeError,
|
||||
model.add_automaton,
|
||||
x,
|
||||
0,
|
||||
[2, 3],
|
||||
[(0, 0, 0), (0, 1, 1), (2, 2), (2, 3, 3)],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
model.add_automaton,
|
||||
[],
|
||||
0,
|
||||
[2, 3],
|
||||
[(0, 0, 0), (0, 1, 1), (2, 3, 3)],
|
||||
)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
model.add_automaton,
|
||||
x,
|
||||
0,
|
||||
[],
|
||||
[(0, 0, 0), (0, 1, 1), (2, 3, 3)],
|
||||
)
|
||||
self.assertRaises(ValueError, model.add_automaton, x, 0, [2, 3], [])
|
||||
with self.assertRaises(TypeError):
|
||||
model.add_automaton(
|
||||
x,
|
||||
0,
|
||||
[2, 3],
|
||||
[(0, 0, 0), (0, 1, 1), (2, 2), (2, 3, 3)],
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
model.add_automaton(
|
||||
[],
|
||||
0,
|
||||
[2, 3],
|
||||
[(0, 0, 0), (0, 1, 1), (2, 3, 3)],
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
model.add_automaton(
|
||||
x,
|
||||
0,
|
||||
[],
|
||||
[(0, 0, 0), (0, 1, 1), (2, 3, 3)],
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
model.add_automaton(x, 0, [2, 3], [])
|
||||
|
||||
def testInverse(self) -> None:
|
||||
print("testInverse")
|
||||
@@ -995,9 +971,11 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertLen(model.proto.constraints[0].bool_or.literals, 5)
|
||||
model.add_bool_or([x[0], x[1], False])
|
||||
self.assertLen(model.proto.variables, 6)
|
||||
self.assertRaises(TypeError, model.add_bool_or, [x[2], 2])
|
||||
with self.assertRaises(TypeError):
|
||||
model.add_bool_or([x[2], 2])
|
||||
y = model.new_int_var(0, 4, "y")
|
||||
self.assertRaises(TypeError, model.add_bool_or, [y, False])
|
||||
with self.assertRaises(TypeError):
|
||||
model.add_bool_or([y, False])
|
||||
|
||||
def testBoolOrListOrGet(self) -> None:
|
||||
print("testBoolOrListOrGet")
|
||||
@@ -1153,13 +1131,12 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertEqual(1, j.index)
|
||||
self.assertEqual(2, k.index)
|
||||
self.assertEqual(3, l.index)
|
||||
self.assertRaises(TypeError, model.new_optional_interval_var, 1, 2, 3, x, "x")
|
||||
self.assertRaises(
|
||||
TypeError, model.new_optional_interval_var, b + x, 2, 3, b, "x"
|
||||
)
|
||||
self.assertRaises(
|
||||
TypeError, model.new_optional_interval_var, 1, 2, 3, b + 1, "x"
|
||||
)
|
||||
with self.assertRaises(TypeError):
|
||||
model.new_optional_interval_var(1, 2, 3, x, "x")
|
||||
with self.assertRaises(TypeError):
|
||||
model.new_optional_interval_var(b + x, 2, 3, b, "x")
|
||||
with self.assertRaises(TypeError):
|
||||
model.new_optional_interval_var(1, 2, 3, b + 1, "x")
|
||||
|
||||
def testNoOverlap(self) -> None:
|
||||
print("testNoOverlap")
|
||||
@@ -1209,7 +1186,8 @@ class CpModelTest(absltest.TestCase):
|
||||
ct = model.add_cumulative(intervals, demands, capacity)
|
||||
self.assertEqual(10, ct.index)
|
||||
self.assertLen(ct.proto.cumulative.intervals, 10)
|
||||
self.assertRaises(TypeError, model.add_cumulative, [intervals[0], 3], [2, 3], 3)
|
||||
with self.assertRaises(TypeError):
|
||||
model.add_cumulative([intervals[0], 3], [2, 3], 3)
|
||||
|
||||
def testGetOrMakeIndexFromConstant(self) -> None:
|
||||
print("testGetOrMakeIndexFromConstant")
|
||||
|
||||
@@ -22,13 +22,14 @@
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "ortools/util/fp_roundtrip_conv.h"
|
||||
#include "ortools/util/sorted_interval_list.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
namespace python {
|
||||
|
||||
bool LinearExpr::IsInteger() {
|
||||
bool LinearExpr::IsInteger() const {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
return lin.ProcessAll();
|
||||
@@ -240,21 +241,21 @@ LinearExpr* LinearExpr::MulFloat(double cst) {
|
||||
|
||||
LinearExpr* LinearExpr::Neg() { return new IntAffine(this, -1, 0); }
|
||||
|
||||
void FloatExprVisitor::AddToProcess(LinearExpr* expr, double coeff) {
|
||||
void FloatExprVisitor::AddToProcess(const LinearExpr* expr, double coeff) {
|
||||
to_process_.push_back(std::make_pair(expr, coeff));
|
||||
}
|
||||
void FloatExprVisitor::AddConstant(double constant) { offset_ += constant; }
|
||||
void FloatExprVisitor::AddVarCoeff(BaseIntVar* var, double coeff) {
|
||||
void FloatExprVisitor::AddVarCoeff(const BaseIntVar* var, double coeff) {
|
||||
canonical_terms_[var] += coeff;
|
||||
}
|
||||
double FloatExprVisitor::Process(LinearExpr* expr,
|
||||
std::vector<BaseIntVar*>* vars,
|
||||
double FloatExprVisitor::Process(const LinearExpr* expr,
|
||||
std::vector<const BaseIntVar*>* vars,
|
||||
std::vector<double>* coeffs) {
|
||||
AddToProcess(expr, 1.0);
|
||||
while (!to_process_.empty()) {
|
||||
const auto [expr, coeff] = to_process_.back();
|
||||
to_process_.pop_back();
|
||||
expr->VisitAsFloat(this, coeff);
|
||||
expr->VisitAsFloat(*this, coeff);
|
||||
}
|
||||
|
||||
vars->clear();
|
||||
@@ -273,20 +274,20 @@ CanonicalFloatExpression::CanonicalFloatExpression(LinearExpr* expr) {
|
||||
offset_ = lin.Process(expr, &vars_, &coeffs_);
|
||||
}
|
||||
|
||||
CanonicalIntExpression::CanonicalIntExpression(LinearExpr* expr) : ok_(true) {
|
||||
CanonicalIntExpression::CanonicalIntExpression(LinearExpr* expr) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(expr, 1);
|
||||
ok_ = lin.Process(&vars_, &coeffs_, &offset_);
|
||||
}
|
||||
|
||||
void FloatConstant::VisitAsFloat(FloatExprVisitor* lin, double c) {
|
||||
lin->AddConstant(value_ * c);
|
||||
void FloatConstant::VisitAsFloat(FloatExprVisitor& lin, double c) const {
|
||||
lin.AddConstant(value_ * c);
|
||||
}
|
||||
|
||||
std::string FloatConstant::ToString() const { return absl::StrCat(value_); }
|
||||
|
||||
std::string FloatConstant::DebugString() const {
|
||||
return absl::StrCat("FloatConstant(", value_, ")");
|
||||
return absl::StrCat("FloatConstant(", RoundTripDoubleFormat(value_), ")");
|
||||
}
|
||||
|
||||
FloatWeightedSum::FloatWeightedSum(const std::vector<LinearExpr*>& exprs,
|
||||
@@ -302,11 +303,11 @@ FloatWeightedSum::FloatWeightedSum(const std::vector<LinearExpr*>& exprs,
|
||||
coeffs_(coeffs.begin(), coeffs.end()),
|
||||
offset_(offset) {}
|
||||
|
||||
void FloatWeightedSum::VisitAsFloat(FloatExprVisitor* lin, double c) {
|
||||
void FloatWeightedSum::VisitAsFloat(FloatExprVisitor& lin, double c) const {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
lin.AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
lin.AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string FloatWeightedSum::ToString() const {
|
||||
@@ -423,9 +424,9 @@ std::string IntWeightedSum::DebugString() const {
|
||||
FloatAffine::FloatAffine(LinearExpr* expr, double coeff, double offset)
|
||||
: expr_(expr), coeff_(coeff), offset_(offset) {}
|
||||
|
||||
void FloatAffine::VisitAsFloat(FloatExprVisitor* lin, double c) {
|
||||
lin->AddToProcess(expr_, c * coeff_);
|
||||
lin->AddConstant(offset_ * c);
|
||||
void FloatAffine::VisitAsFloat(FloatExprVisitor& lin, double c) const {
|
||||
lin.AddToProcess(expr_, c * coeff_);
|
||||
lin.AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string FloatAffine::ToString() const {
|
||||
@@ -454,7 +455,7 @@ BoundedLinearExpression* LinearExpr::Eq(LinearExpr* rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
lin.AddToProcess(rhs, -1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -464,7 +465,7 @@ BoundedLinearExpression* LinearExpr::Eq(LinearExpr* rhs) {
|
||||
BoundedLinearExpression* LinearExpr::EqCst(int64_t rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -475,7 +476,7 @@ BoundedLinearExpression* LinearExpr::Ne(LinearExpr* rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
lin.AddToProcess(rhs, -1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -486,7 +487,7 @@ BoundedLinearExpression* LinearExpr::Ne(LinearExpr* rhs) {
|
||||
BoundedLinearExpression* LinearExpr::NeCst(int64_t rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -498,7 +499,7 @@ BoundedLinearExpression* LinearExpr::Le(LinearExpr* rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
lin.AddToProcess(rhs, -1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -509,7 +510,7 @@ BoundedLinearExpression* LinearExpr::Le(LinearExpr* rhs) {
|
||||
BoundedLinearExpression* LinearExpr::LeCst(int64_t rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -521,7 +522,7 @@ BoundedLinearExpression* LinearExpr::Lt(LinearExpr* rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
lin.AddToProcess(rhs, -1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -532,7 +533,7 @@ BoundedLinearExpression* LinearExpr::Lt(LinearExpr* rhs) {
|
||||
BoundedLinearExpression* LinearExpr::LtCst(int64_t rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -545,7 +546,7 @@ BoundedLinearExpression* LinearExpr::Ge(LinearExpr* rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
lin.AddToProcess(rhs, -1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -556,7 +557,7 @@ BoundedLinearExpression* LinearExpr::Ge(LinearExpr* rhs) {
|
||||
BoundedLinearExpression* LinearExpr::GeCst(int64_t rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -568,7 +569,7 @@ BoundedLinearExpression* LinearExpr::Gt(LinearExpr* rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
lin.AddToProcess(rhs, -1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -579,7 +580,7 @@ BoundedLinearExpression* LinearExpr::Gt(LinearExpr* rhs) {
|
||||
BoundedLinearExpression* LinearExpr::GtCst(int64_t rhs) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(this, 1);
|
||||
std::vector<BaseIntVar*> vars;
|
||||
std::vector<const BaseIntVar*> vars;
|
||||
std::vector<int64_t> coeffs;
|
||||
int64_t offset;
|
||||
if (!lin.Process(&vars, &coeffs, &offset)) return nullptr;
|
||||
@@ -588,13 +589,13 @@ BoundedLinearExpression* LinearExpr::GtCst(int64_t rhs) {
|
||||
Domain(rhs + 1, std::numeric_limits<int64_t>::max()));
|
||||
}
|
||||
|
||||
void IntExprVisitor::AddToProcess(LinearExpr* expr, int64_t coeff) {
|
||||
void IntExprVisitor::AddToProcess(const LinearExpr* expr, int64_t coeff) {
|
||||
to_process_.push_back(std::make_pair(expr, coeff));
|
||||
}
|
||||
|
||||
void IntExprVisitor::AddConstant(int64_t constant) { offset_ += constant; }
|
||||
|
||||
void IntExprVisitor::AddVarCoeff(BaseIntVar* var, int64_t coeff) {
|
||||
void IntExprVisitor::AddVarCoeff(const BaseIntVar* var, int64_t coeff) {
|
||||
canonical_terms_[var] += coeff;
|
||||
}
|
||||
|
||||
@@ -602,12 +603,12 @@ bool IntExprVisitor::ProcessAll() {
|
||||
while (!to_process_.empty()) {
|
||||
const auto [expr, coeff] = to_process_.back();
|
||||
to_process_.pop_back();
|
||||
if (!expr->VisitAsInt(this, coeff)) return false;
|
||||
if (!expr->VisitAsInt(*this, coeff)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntExprVisitor::Process(std::vector<BaseIntVar*>* vars,
|
||||
bool IntExprVisitor::Process(std::vector<const BaseIntVar*>* vars,
|
||||
std::vector<int64_t>* coeffs, int64_t* offset) {
|
||||
if (!ProcessAll()) return false;
|
||||
vars->clear();
|
||||
@@ -621,7 +622,7 @@ bool IntExprVisitor::Process(std::vector<BaseIntVar*>* vars,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntExprVisitor::Evaluate(LinearExpr* expr,
|
||||
bool IntExprVisitor::Evaluate(const LinearExpr* expr,
|
||||
const CpSolverResponse& solution,
|
||||
int64_t* value) {
|
||||
AddToProcess(expr, 1);
|
||||
@@ -644,14 +645,13 @@ BaseIntVar::BaseIntVar(int index, bool is_boolean)
|
||||
: index_(index),
|
||||
negated_(is_boolean ? new NotBooleanVariable(this) : nullptr) {}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(std::vector<BaseIntVar*> vars,
|
||||
std::vector<int64_t> coeffs,
|
||||
int64_t offset,
|
||||
const Domain& bounds)
|
||||
BoundedLinearExpression::BoundedLinearExpression(
|
||||
const std::vector<const BaseIntVar*>& vars,
|
||||
const std::vector<int64_t>& coeffs, int64_t offset, const Domain& bounds)
|
||||
: vars_(vars), coeffs_(coeffs), offset_(offset), bounds_(bounds) {}
|
||||
|
||||
const Domain& BoundedLinearExpression::bounds() const { return bounds_; }
|
||||
const std::vector<BaseIntVar*>& BoundedLinearExpression::vars() const {
|
||||
const std::vector<const BaseIntVar*>& BoundedLinearExpression::vars() const {
|
||||
return vars_;
|
||||
}
|
||||
const std::vector<int64_t>& BoundedLinearExpression::coeffs() const {
|
||||
@@ -731,14 +731,14 @@ std::string BoundedLinearExpression::ToString() const {
|
||||
}
|
||||
|
||||
std::string BoundedLinearExpression::DebugString() const {
|
||||
return absl::StrCat("BoundedLinearExpression(vars=[",
|
||||
absl::StrJoin(vars_, ", ",
|
||||
[](std::string* out, BaseIntVar* var) {
|
||||
absl::StrAppend(out, var->DebugString());
|
||||
}),
|
||||
"], coeffs=[", absl::StrJoin(coeffs_, ", "),
|
||||
"], offset=", offset_, ", bounds=", bounds_.ToString(),
|
||||
")");
|
||||
return absl::StrCat(
|
||||
"BoundedLinearExpression(vars=[",
|
||||
absl::StrJoin(vars_, ", ",
|
||||
[](std::string* out, const BaseIntVar* var) {
|
||||
absl::StrAppend(out, var->DebugString());
|
||||
}),
|
||||
"], coeffs=[", absl::StrJoin(coeffs_, ", "), "], offset=", offset_,
|
||||
", bounds=", bounds_.ToString(), ")");
|
||||
}
|
||||
|
||||
bool BoundedLinearExpression::CastToBool(bool* result) const {
|
||||
|
||||
@@ -25,11 +25,10 @@
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "ortools/sat/cp_model.pb.h"
|
||||
#include "ortools/util/fp_roundtrip_conv.h"
|
||||
#include "ortools/util/sorted_interval_list.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
namespace python {
|
||||
namespace operations_research::sat::python {
|
||||
|
||||
class BoundedLinearExpression;
|
||||
class CanonicalFloatExpression;
|
||||
@@ -40,7 +39,7 @@ class LinearExpr;
|
||||
class BaseIntVar;
|
||||
class NotBooleanVariable;
|
||||
|
||||
// A class to hold an linear expression or a constant.
|
||||
// A class to hold a pointer to a linear expression or a constant.
|
||||
struct ExprOrValue {
|
||||
explicit ExprOrValue(LinearExpr* e) : expr(e) {}
|
||||
explicit ExprOrValue(double v) : double_value(v) {}
|
||||
@@ -51,56 +50,93 @@ struct ExprOrValue {
|
||||
int64_t int_value = 0;
|
||||
};
|
||||
|
||||
// A linear expression that can be either integer or floating point.
|
||||
// Interface for a linear expression that can be either integer or floating
|
||||
// point.
|
||||
class LinearExpr {
|
||||
public:
|
||||
virtual ~LinearExpr() = default;
|
||||
virtual void VisitAsFloat(FloatExprVisitor* /*lin*/, double /*c*/) = 0;
|
||||
virtual bool VisitAsInt(IntExprVisitor* /*lin*/, int64_t /*c*/) = 0;
|
||||
bool IsInteger();
|
||||
virtual std::string ToString() const { return "LinearExpr"; }
|
||||
virtual std::string DebugString() const { return "LinearExpr()"; }
|
||||
virtual void VisitAsFloat(FloatExprVisitor& /*lin*/, double /*c*/) const = 0;
|
||||
virtual bool VisitAsInt(IntExprVisitor& /*lin*/, int64_t /*c*/) const = 0;
|
||||
bool IsInteger() const;
|
||||
virtual std::string ToString() const = 0;
|
||||
virtual std::string DebugString() const = 0;
|
||||
|
||||
// Returns a new LinearExpr that is the sum of the given expressions.
|
||||
static LinearExpr* Sum(const std::vector<LinearExpr*>& exprs);
|
||||
// Returns a new LinearExpr that is the sum of the given expressions or
|
||||
// constants.
|
||||
static LinearExpr* MixedSum(const std::vector<ExprOrValue>& exprs);
|
||||
// Returns the sum(exprs[i] * coeffs[i]).
|
||||
static LinearExpr* WeightedSumInt(const std::vector<LinearExpr*>& exprs,
|
||||
const std::vector<int64_t>& coeffs);
|
||||
// Returns the sum(exprs[i] * coeffs[i]).
|
||||
static LinearExpr* WeightedSumFloat(const std::vector<LinearExpr*>& exprs,
|
||||
const std::vector<double>& coeffs);
|
||||
// Returns the sum(exprs[i] * coeffs[i]).
|
||||
static LinearExpr* MixedWeightedSumInt(const std::vector<ExprOrValue>& exprs,
|
||||
const std::vector<int64_t>& coeffs);
|
||||
// Returns the sum(exprs[i] * coeffs[i]).
|
||||
static LinearExpr* MixedWeightedSumFloat(
|
||||
const std::vector<ExprOrValue>& exprs, const std::vector<double>& coeffs);
|
||||
// returns expr * coeff.
|
||||
static LinearExpr* TermInt(LinearExpr* expr, int64_t coeff);
|
||||
// returns expr * coeff.
|
||||
static LinearExpr* TermFloat(LinearExpr* expr, double coeff);
|
||||
// returns expr * coeff + offset.
|
||||
static LinearExpr* AffineInt(LinearExpr* expr, int64_t coeff, int64_t offset);
|
||||
// returns expr * coeff + offset.
|
||||
static LinearExpr* AffineFloat(LinearExpr* expr, double coeff, double offset);
|
||||
// Returns a new LinearExpr that is the given constant.
|
||||
static LinearExpr* ConstantInt(int64_t value);
|
||||
// Returns a new LinearExpr that is the given constant.
|
||||
static LinearExpr* ConstantFloat(double value);
|
||||
|
||||
// return (this) + (expr).
|
||||
LinearExpr* Add(LinearExpr* expr);
|
||||
// return (this) + (cst).
|
||||
LinearExpr* AddInt(int64_t cst);
|
||||
// return (this) + (cst).
|
||||
LinearExpr* AddFloat(double cst);
|
||||
// return (this) - (expr).
|
||||
LinearExpr* Sub(LinearExpr* expr);
|
||||
// return (this) - (cst).
|
||||
LinearExpr* SubInt(int64_t cst);
|
||||
// return (this) - (cst).
|
||||
LinearExpr* SubFloat(double cst);
|
||||
// return (cst) - (this).
|
||||
LinearExpr* RSubInt(int64_t cst);
|
||||
// return (cst) - (this).
|
||||
LinearExpr* RSubFloat(double cst);
|
||||
// return (this) * (cst).
|
||||
LinearExpr* MulInt(int64_t cst);
|
||||
// return (this) * (cst).
|
||||
LinearExpr* MulFloat(double cst);
|
||||
// return -(this).
|
||||
LinearExpr* Neg();
|
||||
|
||||
// returns (this) == (rhs).
|
||||
BoundedLinearExpression* Eq(LinearExpr* rhs);
|
||||
// returns (this) == (rhs).
|
||||
BoundedLinearExpression* EqCst(int64_t rhs);
|
||||
// returns (this) != (rhs).
|
||||
BoundedLinearExpression* Ne(LinearExpr* rhs);
|
||||
// returns (this) != (rhs).
|
||||
BoundedLinearExpression* NeCst(int64_t rhs);
|
||||
// returns (this) >= (rhs).
|
||||
BoundedLinearExpression* Ge(LinearExpr* rhs);
|
||||
// returns (this) >= (rhs).
|
||||
BoundedLinearExpression* GeCst(int64_t rhs);
|
||||
// returns (this) <= (rhs).
|
||||
BoundedLinearExpression* Le(LinearExpr* rhs);
|
||||
// returns (this) <= (rhs).
|
||||
BoundedLinearExpression* LeCst(int64_t rhs);
|
||||
// returns (this) < (rhs).
|
||||
BoundedLinearExpression* Lt(LinearExpr* rhs);
|
||||
// returns (this) < (rhs).
|
||||
BoundedLinearExpression* LtCst(int64_t rhs);
|
||||
// returns (this) > (rhs).
|
||||
BoundedLinearExpression* Gt(LinearExpr* rhs);
|
||||
// returns (this) > (rhs).
|
||||
BoundedLinearExpression* GtCst(int64_t rhs);
|
||||
};
|
||||
|
||||
@@ -112,15 +148,16 @@ struct BaseIntVarComparator {
|
||||
// A visitor class to process a floating point linear expression.
|
||||
class FloatExprVisitor {
|
||||
public:
|
||||
void AddToProcess(LinearExpr* expr, double coeff);
|
||||
void AddToProcess(const LinearExpr* expr, double coeff);
|
||||
void AddConstant(double constant);
|
||||
void AddVarCoeff(BaseIntVar* var, double coeff);
|
||||
double Process(LinearExpr* expr, std::vector<BaseIntVar*>* vars,
|
||||
void AddVarCoeff(const BaseIntVar* var, double coeff);
|
||||
double Process(const LinearExpr* expr, std::vector<const BaseIntVar*>* vars,
|
||||
std::vector<double>* coeffs);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<LinearExpr*, double>> to_process_;
|
||||
absl::btree_map<BaseIntVar*, double, BaseIntVarComparator> canonical_terms_;
|
||||
std::vector<std::pair<const LinearExpr*, double>> to_process_;
|
||||
absl::btree_map<const BaseIntVar*, double, BaseIntVarComparator>
|
||||
canonical_terms_;
|
||||
double offset_ = 0;
|
||||
};
|
||||
|
||||
@@ -128,31 +165,32 @@ class FloatExprVisitor {
|
||||
class CanonicalFloatExpression {
|
||||
public:
|
||||
explicit CanonicalFloatExpression(LinearExpr* expr);
|
||||
const std::vector<BaseIntVar*>& vars() const { return vars_; }
|
||||
const std::vector<const BaseIntVar*>& vars() const { return vars_; }
|
||||
const std::vector<double>& coeffs() const { return coeffs_; }
|
||||
double offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
std::vector<BaseIntVar*> vars_;
|
||||
std::vector<const BaseIntVar*> vars_;
|
||||
std::vector<double> coeffs_;
|
||||
double offset_;
|
||||
double offset_ = 0;
|
||||
};
|
||||
|
||||
// A visitor class to process an integer linear expression.
|
||||
class IntExprVisitor {
|
||||
public:
|
||||
void AddToProcess(LinearExpr* expr, int64_t coeff);
|
||||
void AddToProcess(const LinearExpr* expr, int64_t coeff);
|
||||
void AddConstant(int64_t constant);
|
||||
void AddVarCoeff(BaseIntVar* var, int64_t coeff);
|
||||
void AddVarCoeff(const BaseIntVar* var, int64_t coeff);
|
||||
bool ProcessAll();
|
||||
bool Process(std::vector<BaseIntVar*>* vars, std::vector<int64_t>* coeffs,
|
||||
int64_t* offset);
|
||||
bool Evaluate(LinearExpr* expr, const CpSolverResponse& solution,
|
||||
bool Process(std::vector<const BaseIntVar*>* vars,
|
||||
std::vector<int64_t>* coeffs, int64_t* offset);
|
||||
bool Evaluate(const LinearExpr* expr, const CpSolverResponse& solution,
|
||||
int64_t* value);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<LinearExpr*, int64_t>> to_process_;
|
||||
absl::btree_map<BaseIntVar*, int64_t, BaseIntVarComparator> canonical_terms_;
|
||||
std::vector<std::pair<const LinearExpr*, int64_t>> to_process_;
|
||||
absl::btree_map<const BaseIntVar*, int64_t, BaseIntVarComparator>
|
||||
canonical_terms_;
|
||||
int64_t offset_ = 0;
|
||||
};
|
||||
|
||||
@@ -160,53 +198,55 @@ class IntExprVisitor {
|
||||
class CanonicalIntExpression {
|
||||
public:
|
||||
explicit CanonicalIntExpression(LinearExpr* expr);
|
||||
const std::vector<BaseIntVar*>& vars() const { return vars_; }
|
||||
const std::vector<const BaseIntVar*>& vars() const { return vars_; }
|
||||
const std::vector<int64_t>& coeffs() const { return coeffs_; }
|
||||
int64_t offset() const { return offset_; }
|
||||
bool ok() const { return ok_; }
|
||||
|
||||
private:
|
||||
std::vector<BaseIntVar*> vars_;
|
||||
std::vector<const BaseIntVar*> vars_;
|
||||
std::vector<int64_t> coeffs_;
|
||||
int64_t offset_;
|
||||
bool ok_;
|
||||
int64_t offset_ = 0;
|
||||
bool ok_ = true;
|
||||
};
|
||||
|
||||
// A class to hold a sum of linear expressions, and optional integer and
|
||||
// double offsets.
|
||||
// double offsets (at most one of them can be non-zero, this is DCHECKed).
|
||||
class SumArray : public LinearExpr {
|
||||
public:
|
||||
explicit SumArray(const std::vector<LinearExpr*>& exprs,
|
||||
int64_t int_offset = 0, double double_offset = 0.0)
|
||||
: exprs_(exprs.begin(), exprs.end()),
|
||||
int_offset_(int_offset),
|
||||
double_offset_(double_offset) {}
|
||||
double_offset_(double_offset) {
|
||||
DCHECK(int_offset_ == 0 || double_offset_ == 0.0);
|
||||
}
|
||||
~SumArray() override = default;
|
||||
|
||||
bool VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
bool VisitAsInt(IntExprVisitor& lin, int64_t c) const override {
|
||||
if (double_offset_ != 0.0) return false;
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], c);
|
||||
lin.AddToProcess(exprs_[i], c);
|
||||
}
|
||||
lin->AddConstant(int_offset_ * c);
|
||||
lin.AddConstant(int_offset_ * c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], c);
|
||||
lin.AddToProcess(exprs_[i], c);
|
||||
}
|
||||
if (int_offset_ != 0) {
|
||||
lin->AddConstant(int_offset_ * c);
|
||||
lin.AddConstant(int_offset_ * c);
|
||||
} else if (double_offset_ != 0.0) {
|
||||
lin->AddConstant(double_offset_ * c);
|
||||
lin.AddConstant(double_offset_ * c);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
if (exprs_.empty()) {
|
||||
if (double_offset_ != 0.0) {
|
||||
return absl::StrCat(double_offset_);
|
||||
return absl::StrCat(RoundTripDoubleFormat(double_offset_));
|
||||
} else {
|
||||
return absl::StrCat(int_offset_);
|
||||
}
|
||||
@@ -246,7 +286,8 @@ class SumArray : public LinearExpr {
|
||||
absl::StrAppend(&s, ", int_offset=", int_offset_);
|
||||
}
|
||||
if (double_offset_ != 0.0) {
|
||||
absl::StrAppend(&s, ", double_offset=", double_offset_);
|
||||
absl::StrAppend(
|
||||
&s, ", double_offset=", RoundTripDoubleFormat(double_offset_));
|
||||
}
|
||||
absl::StrAppend(&s, ")");
|
||||
return s;
|
||||
@@ -266,11 +307,11 @@ class FloatWeightedSum : public LinearExpr {
|
||||
const std::vector<double>& coeffs, double offset);
|
||||
~FloatWeightedSum() override = default;
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override;
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override;
|
||||
std::string ToString() const override;
|
||||
std::string DebugString() const override;
|
||||
|
||||
bool VisitAsInt(IntExprVisitor* /*lin*/, int64_t /*c*/) override {
|
||||
bool VisitAsInt(IntExprVisitor& /*lin*/, int64_t /*c*/) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -290,18 +331,18 @@ class IntWeightedSum : public LinearExpr {
|
||||
offset_(offset) {}
|
||||
~IntWeightedSum() override = default;
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
lin.AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
lin.AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
bool VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
bool VisitAsInt(IntExprVisitor& lin, int64_t c) const override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
lin.AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
lin.AddConstant(offset_ * c);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -320,8 +361,8 @@ class FloatAffine : public LinearExpr {
|
||||
FloatAffine(LinearExpr* expr, double coeff, double offset);
|
||||
~FloatAffine() override = default;
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override;
|
||||
bool VisitAsInt(IntExprVisitor* /*lin*/, int64_t /*c*/) override {
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override;
|
||||
bool VisitAsInt(IntExprVisitor& /*lin*/, int64_t /*c*/) const override {
|
||||
return false;
|
||||
}
|
||||
std::string ToString() const override;
|
||||
@@ -344,15 +385,15 @@ class IntAffine : public LinearExpr {
|
||||
: expr_(expr), coeff_(coeff), offset_(offset) {}
|
||||
~IntAffine() override = default;
|
||||
|
||||
bool VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddToProcess(expr_, c * coeff_);
|
||||
lin->AddConstant(offset_ * c);
|
||||
bool VisitAsInt(IntExprVisitor& lin, int64_t c) const override {
|
||||
lin.AddToProcess(expr_, c * coeff_);
|
||||
lin.AddConstant(offset_ * c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddToProcess(expr_, c * coeff_);
|
||||
lin->AddConstant(offset_ * c);
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override {
|
||||
lin.AddToProcess(expr_, c * coeff_);
|
||||
lin.AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
@@ -394,8 +435,8 @@ class FloatConstant : public LinearExpr {
|
||||
explicit FloatConstant(double value) : value_(value) {}
|
||||
~FloatConstant() override = default;
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override;
|
||||
bool VisitAsInt(IntExprVisitor* /*lin*/, int64_t /*c*/) override {
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override;
|
||||
bool VisitAsInt(IntExprVisitor& /*lin*/, int64_t /*c*/) const override {
|
||||
return false;
|
||||
}
|
||||
std::string ToString() const override;
|
||||
@@ -410,13 +451,13 @@ class IntConstant : public LinearExpr {
|
||||
public:
|
||||
explicit IntConstant(int64_t value) : value_(value) {}
|
||||
~IntConstant() override = default;
|
||||
bool VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddConstant(value_ * c);
|
||||
bool VisitAsInt(IntExprVisitor& lin, int64_t c) const override {
|
||||
lin.AddConstant(value_ * c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddConstant(value_ * c);
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override {
|
||||
lin.AddConstant(value_ * c);
|
||||
}
|
||||
|
||||
std::string ToString() const override { return absl::StrCat(value_); }
|
||||
@@ -451,13 +492,13 @@ class BaseIntVar : public Literal {
|
||||
|
||||
int index() const override { return index_; }
|
||||
|
||||
bool VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddVarCoeff(this, c);
|
||||
bool VisitAsInt(IntExprVisitor& lin, int64_t c) const override {
|
||||
lin.AddVarCoeff(this, c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddVarCoeff(this, c);
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override {
|
||||
lin.AddVarCoeff(this, c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
@@ -499,15 +540,15 @@ class NotBooleanVariable : public Literal {
|
||||
|
||||
int index() const override { return -var_->index() - 1; }
|
||||
|
||||
bool VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddVarCoeff(var_, -c);
|
||||
lin->AddConstant(c);
|
||||
bool VisitAsInt(IntExprVisitor& lin, int64_t c) const override {
|
||||
lin.AddVarCoeff(var_, -c);
|
||||
lin.AddConstant(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddVarCoeff(var_, -c);
|
||||
lin->AddConstant(c);
|
||||
void VisitAsFloat(FloatExprVisitor& lin, double c) const override {
|
||||
lin.AddVarCoeff(var_, -c);
|
||||
lin.AddConstant(c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
@@ -521,20 +562,20 @@ class NotBooleanVariable : public Literal {
|
||||
}
|
||||
|
||||
private:
|
||||
BaseIntVar* var_;
|
||||
BaseIntVar* const var_;
|
||||
};
|
||||
|
||||
// A class to hold a linear expression with bounds.
|
||||
class BoundedLinearExpression {
|
||||
public:
|
||||
BoundedLinearExpression(std::vector<BaseIntVar*> vars,
|
||||
std::vector<int64_t> coeffs, int64_t offset,
|
||||
BoundedLinearExpression(const std::vector<const BaseIntVar*>& vars,
|
||||
const std::vector<int64_t>& coeffs, int64_t offset,
|
||||
const Domain& bounds);
|
||||
|
||||
~BoundedLinearExpression() = default;
|
||||
|
||||
const Domain& bounds() const;
|
||||
const std::vector<BaseIntVar*>& vars() const;
|
||||
const std::vector<const BaseIntVar*>& vars() const;
|
||||
const std::vector<int64_t>& coeffs() const;
|
||||
int64_t offset() const;
|
||||
std::string ToString() const;
|
||||
@@ -542,14 +583,12 @@ class BoundedLinearExpression {
|
||||
bool CastToBool(bool* result) const;
|
||||
|
||||
private:
|
||||
std::vector<BaseIntVar*> vars_;
|
||||
std::vector<int64_t> coeffs_;
|
||||
const std::vector<const BaseIntVar*> vars_;
|
||||
const std::vector<int64_t> coeffs_;
|
||||
int64_t offset_;
|
||||
const Domain bounds_;
|
||||
};
|
||||
|
||||
} // namespace python
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research::sat::python
|
||||
|
||||
#endif // OR_TOOLS_SAT_PYTHON_LINEAR_EXPR_H_
|
||||
|
||||
@@ -35,9 +35,7 @@
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
namespace python {
|
||||
namespace operations_research::sat::python {
|
||||
|
||||
using ::py::arg;
|
||||
|
||||
@@ -70,18 +68,18 @@ class PyBaseIntVar : public BaseIntVar {
|
||||
using BaseIntVar::BaseIntVar; /* Inherit constructors */
|
||||
|
||||
std::string ToString() const override {
|
||||
PYBIND11_OVERRIDE_NAME(std::string, // Return type (ret_type)
|
||||
BaseIntVar, // Parent class (cname)
|
||||
"__str__", // Name of method in Python (name)
|
||||
ToString, // Name of function in C++ (fn)
|
||||
PYBIND11_OVERRIDE_PURE_NAME(std::string, // Return type (ret_type)
|
||||
BaseIntVar, // Parent class (cname)
|
||||
"__str__", // Name of method in Python (name)
|
||||
ToString, // Name of function in C++ (fn)
|
||||
);
|
||||
}
|
||||
|
||||
std::string DebugString() const override {
|
||||
PYBIND11_OVERRIDE_NAME(std::string, // Return type (ret_type)
|
||||
BaseIntVar, // Parent class (cname)
|
||||
"__repr__", // Name of method in Python (name)
|
||||
DebugString, // Name of function in C++ (fn)
|
||||
PYBIND11_OVERRIDE_PURE_NAME(std::string, // Return type (ret_type)
|
||||
BaseIntVar, // Parent class (cname)
|
||||
"__repr__", // Name of method in Python (name)
|
||||
DebugString, // Name of function in C++ (fn)
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -163,8 +161,7 @@ class ResponseWrapper {
|
||||
const CpSolverResponse response_;
|
||||
};
|
||||
|
||||
const char* kLinearExprClassDoc = R"doc(
|
||||
Holds an integer linear expression.
|
||||
const char* kLinearExprClassDoc = R"doc(Holds an integer linear expression.
|
||||
|
||||
A linear expression is built from integer constants and variables.
|
||||
For example, `x + 2 * (y - z + 1)`.
|
||||
@@ -810,8 +807,8 @@ PYBIND11_MODULE(swig_helper, m) {
|
||||
py::return_value_policy::reference_internal);
|
||||
|
||||
py::class_<BoundedLinearExpression>(m, "BoundedLinearExpression")
|
||||
.def(py::init<std::vector<BaseIntVar*>, std::vector<int64_t>, int64_t,
|
||||
Domain>())
|
||||
.def(py::init<std::vector<const BaseIntVar*>, std::vector<int64_t>,
|
||||
int64_t, Domain>())
|
||||
.def_property_readonly("bounds", &BoundedLinearExpression::bounds)
|
||||
.def_property_readonly("vars", &BoundedLinearExpression::vars)
|
||||
.def_property_readonly("coeffs", &BoundedLinearExpression::coeffs)
|
||||
@@ -831,6 +828,4 @@ PYBIND11_MODULE(swig_helper, m) {
|
||||
});
|
||||
} // NOLINT(readability/fn_size)
|
||||
|
||||
} // namespace python
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
} // namespace operations_research::sat::python
|
||||
|
||||
Reference in New Issue
Block a user