[CP-SAT] better dtime for scheduling constraints; connect the time limit in the repair_hint mode; fix #4743
This commit is contained in:
@@ -1800,6 +1800,7 @@ cc_test(
|
||||
deps = [
|
||||
":integer_base",
|
||||
"//ortools/base:gmock_main",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -2098,9 +2099,7 @@ cc_library(
|
||||
"//ortools/base",
|
||||
"//ortools/base:mathutil",
|
||||
"//ortools/base:strong_vector",
|
||||
"//ortools/graph",
|
||||
"//ortools/graph:topologicalsorter",
|
||||
"//ortools/util:bitset",
|
||||
"//ortools/util:logging",
|
||||
"//ortools/util:rev",
|
||||
"//ortools/util:strong_integers",
|
||||
@@ -2335,6 +2334,7 @@ cc_library(
|
||||
"//ortools/util:sort",
|
||||
"//ortools/util:strong_integers",
|
||||
"@abseil-cpp//absl/algorithm:container",
|
||||
"@abseil-cpp//absl/cleanup",
|
||||
"@abseil-cpp//absl/log",
|
||||
"@abseil-cpp//absl/log:check",
|
||||
"@abseil-cpp//absl/log:vlog_is_on",
|
||||
@@ -3614,6 +3614,7 @@ cc_library(
|
||||
":synchronization",
|
||||
":timetable",
|
||||
":util",
|
||||
"//ortools/base:stl_util",
|
||||
"//ortools/util:bitset",
|
||||
"//ortools/util:saturated_arithmetic",
|
||||
"//ortools/util:strong_integers",
|
||||
|
||||
@@ -1076,7 +1076,9 @@ class FullProblemSolver : public SubSolver {
|
||||
previous_task_is_completed_ = false;
|
||||
}
|
||||
return [this]() {
|
||||
auto* time_limit = local_model_.GetOrCreate<TimeLimit>();
|
||||
if (solving_first_chunk_) {
|
||||
const double init_dtime = time_limit->GetElapsedDeterministicTime();
|
||||
LoadCpModel(shared_->model_proto, &local_model_);
|
||||
|
||||
// Level zero variable bounds sharing. It is important to register
|
||||
@@ -1127,15 +1129,18 @@ class FullProblemSolver : public SubSolver {
|
||||
// No need for mutex since we only run one task at the time.
|
||||
solving_first_chunk_ = false;
|
||||
|
||||
// Make sure we count the loading/hint dtime.
|
||||
absl::MutexLock mutex_lock(&mutex_);
|
||||
dtime_since_last_sync_ +=
|
||||
time_limit->GetElapsedDeterministicTime() - init_dtime;
|
||||
|
||||
// Abort first chunk and allow to schedule the next.
|
||||
if (split_in_chunks_) {
|
||||
// Abort first chunk and allow to schedule the next.
|
||||
absl::MutexLock mutex_lock(&mutex_);
|
||||
previous_task_is_completed_ = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto* time_limit = local_model_.GetOrCreate<TimeLimit>();
|
||||
if (split_in_chunks_) {
|
||||
// Configure time limit for chunk solving. Note that we do not want
|
||||
// to do that for the hint search for now.
|
||||
|
||||
@@ -1866,10 +1866,9 @@ void QuickSolveWithHint(const CpModelProto& model_proto, Model* model) {
|
||||
void MinimizeL1DistanceWithHint(const CpModelProto& model_proto, Model* model) {
|
||||
Model local_model;
|
||||
|
||||
// Forward some shared class.
|
||||
local_model.Register<ModelSharedTimeLimit>(
|
||||
model->GetOrCreate<ModelSharedTimeLimit>());
|
||||
local_model.Register<WallTimer>(model->GetOrCreate<WallTimer>());
|
||||
// Pass the time limit and stop boolean to local limit.
|
||||
model->GetOrCreate<ModelSharedTimeLimit>()->UpdateLocalLimit(
|
||||
local_model.GetOrCreate<TimeLimit>());
|
||||
|
||||
if (!model_proto.has_solution_hint()) return;
|
||||
|
||||
@@ -1967,6 +1966,10 @@ void MinimizeL1DistanceWithHint(const CpModelProto& model_proto, Model* model) {
|
||||
shared_response_manager->NewSolution(
|
||||
solution, absl::StrCat(solution_info, " [repaired]"), &local_model);
|
||||
}
|
||||
|
||||
// Make sure we update the higher model with the timing info.
|
||||
model->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
|
||||
local_model.GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
|
||||
}
|
||||
|
||||
// TODO(user): If this ever shows up in the profile, we could avoid copying
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/cleanup/cleanup.h"
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "absl/types/span.h"
|
||||
@@ -1195,6 +1196,13 @@ bool DisjunctivePrecedences::Propagate() {
|
||||
}
|
||||
|
||||
bool DisjunctivePrecedences::PropagateSubwindow() {
|
||||
// This function can be slow, so count it in the dtime.
|
||||
int64_t num_hash_lookup = 0;
|
||||
auto cleanup = ::absl::MakeCleanup([&num_hash_lookup, this]() {
|
||||
time_limit_->AdvanceDeterministicTime(static_cast<double>(num_hash_lookup) *
|
||||
5e-8);
|
||||
});
|
||||
|
||||
// TODO(user): We shouldn't consider ends for fixed intervals here. But
|
||||
// then we should do a better job of computing the min-end of a subset of
|
||||
// intervals from this disjunctive (like using fixed intervals even if there
|
||||
@@ -1278,6 +1286,7 @@ bool DisjunctivePrecedences::PropagateSubwindow() {
|
||||
|
||||
// TODO(user): The lookup here is a bit slow, so we avoid fetching
|
||||
// the offset as much as possible.
|
||||
++num_hash_lookup;
|
||||
const IntegerValue inner_offset =
|
||||
-linear2_bounds_->NonTrivialUpperBound(lin2_index);
|
||||
DCHECK_NE(inner_offset, kMinIntegerValue);
|
||||
|
||||
@@ -355,6 +355,7 @@ class DisjunctivePrecedences : public PropagatorInterface {
|
||||
integer_trail_(model->GetOrCreate<IntegerTrail>()),
|
||||
precedence_relations_(model->GetOrCreate<EnforcedLinear2Bounds>()),
|
||||
linear2_bounds_(model->GetOrCreate<Linear2Bounds>()),
|
||||
time_limit_(model->GetOrCreate<TimeLimit>()),
|
||||
stats_("DisjunctivePrecedences", model) {
|
||||
window_.ClearAndReserve(helper->NumTasks());
|
||||
index_to_end_vars_.ClearAndReserve(helper->NumTasks());
|
||||
@@ -372,6 +373,7 @@ class DisjunctivePrecedences : public PropagatorInterface {
|
||||
IntegerTrail* integer_trail_;
|
||||
EnforcedLinear2Bounds* precedence_relations_;
|
||||
Linear2Bounds* linear2_bounds_;
|
||||
TimeLimit* time_limit_;
|
||||
|
||||
FixedCapacityVector<TaskTime> window_;
|
||||
FixedCapacityVector<IntegerVariable> index_to_end_vars_;
|
||||
|
||||
@@ -20,7 +20,21 @@ negation of `x`.
|
||||
### Python code
|
||||
|
||||
```python
|
||||
# Snippet from ortools/sat/samples/literal_sample_sat.py
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2010-2025 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.
|
||||
|
||||
"""Code sample to demonstrate Boolean variable and literals."""
|
||||
|
||||
|
||||
@@ -28,25 +42,27 @@ from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def literal_sample_sat():
|
||||
model = cp_model.CpModel()
|
||||
x = model.new_bool_var("x")
|
||||
not_x = ~x
|
||||
print(x)
|
||||
print(not_x)
|
||||
model = cp_model.CpModel()
|
||||
x = model.new_bool_var('x')
|
||||
not_x = ~x
|
||||
print(x)
|
||||
print(not_x)
|
||||
|
||||
|
||||
literal_sample_sat()
|
||||
|
||||
```
|
||||
|
||||
### C++ code
|
||||
|
||||
```cpp
|
||||
// Snippet from ortools/sat/samples/literal_sample_sat.cc
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -69,17 +85,19 @@ int main(int argc, char* argv[]) {
|
||||
operations_research::sat::LiteralSampleSat();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Java code
|
||||
|
||||
```java
|
||||
// Snippet from ortools/sat/samples/LiteralSampleSat.java
|
||||
package com.google.ortools.sat.samples;
|
||||
|
||||
import com.google.ortools.Loader;
|
||||
import com.google.ortools.sat.BoolVar;
|
||||
import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.Literal;
|
||||
import com.google.ortools.Loader;
|
||||
|
||||
/** Code sample to demonstrate Boolean variable and literals. */
|
||||
public class LiteralSampleSat {
|
||||
@@ -91,11 +109,15 @@ public class LiteralSampleSat {
|
||||
System.out.println(notX);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### C\# code
|
||||
|
||||
```cs
|
||||
```csharp
|
||||
// Snippet from ortools/sat/samples/LiteralSampleSat.cs
|
||||
|
||||
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
@@ -108,11 +130,14 @@ public class LiteralSampleSat
|
||||
ILiteral not_x = x.Not();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Go code
|
||||
|
||||
```cs
|
||||
```go
|
||||
// Snippet from ortools/sat/samples/literal_sample_sat.go
|
||||
|
||||
// The literal_sample_sat command is a simple example of literals.
|
||||
package main
|
||||
|
||||
@@ -133,6 +158,7 @@ func literalSampleSat() {
|
||||
func main() {
|
||||
literalSampleSat()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Boolean constraints
|
||||
@@ -150,6 +176,7 @@ constraints. For instance, we can add a constraint Or(x, not(y)).
|
||||
### Python code
|
||||
|
||||
```python
|
||||
# Snippet from ortools/sat/samples/bool_or_sample_sat.py
|
||||
#!/usr/bin/env python3
|
||||
"""Code sample to demonstrates a simple Boolean constraint."""
|
||||
|
||||
@@ -158,29 +185,33 @@ from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def bool_or_sample_sat():
|
||||
model = cp_model.CpModel()
|
||||
model = cp_model.CpModel()
|
||||
|
||||
x = model.new_bool_var("x")
|
||||
y = model.new_bool_var("y")
|
||||
x = model.new_bool_var('x')
|
||||
y = model.new_bool_var('y')
|
||||
|
||||
model.add_bool_or([x, y.negated()])
|
||||
# The [] is not mandatory.
|
||||
# ~y is equivalent to y.negated()
|
||||
model.add_bool_or(x, ~y)
|
||||
model.add_bool_or([x, y.negated()])
|
||||
# The [] is not mandatory.
|
||||
# ~y is equivalent to y.negated()
|
||||
model.add_bool_or(x, ~y)
|
||||
|
||||
|
||||
bool_or_sample_sat()
|
||||
|
||||
```
|
||||
|
||||
### C++ code
|
||||
|
||||
```cpp
|
||||
// Snippet from ortools/sat/samples/bool_or_sample_sat.cc
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -205,11 +236,15 @@ int main(int argc, char* argv[]) {
|
||||
operations_research::sat::BoolOrSampleSat();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Java code
|
||||
|
||||
```java
|
||||
// Snippet from ortools/sat/samples/BoolOrSampleSat.java
|
||||
|
||||
|
||||
package com.google.ortools.sat.samples;
|
||||
|
||||
import com.google.ortools.Loader;
|
||||
@@ -227,11 +262,15 @@ public class BoolOrSampleSat {
|
||||
model.addBoolOr(new Literal[] {x, y.not()});
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### C\# code
|
||||
|
||||
```cs
|
||||
```csharp
|
||||
// Snippet from ortools/sat/samples/BoolOrSampleSat.cs
|
||||
|
||||
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
@@ -247,11 +286,15 @@ public class BoolOrSampleSat
|
||||
model.AddBoolOr(new ILiteral[] { x, y.Not() });
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Go code
|
||||
|
||||
```cs
|
||||
```go
|
||||
// Snippet from ortools/sat/samples/bool_or_sample_sat.go
|
||||
|
||||
|
||||
// The bool_or_sample_sat command is simple example of the BoolOr constraint.
|
||||
package main
|
||||
|
||||
@@ -271,6 +314,7 @@ func boolOrSampleSat() {
|
||||
func main() {
|
||||
boolOrSampleSat()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Reified constraints
|
||||
@@ -278,12 +322,14 @@ func main() {
|
||||
The CP-SAT solver supports *half-reified* constraints, also called
|
||||
*implications*, which are of the form:
|
||||
|
||||
x implies constraint
|
||||
```
|
||||
x implies constraint
|
||||
```
|
||||
|
||||
where the constraint must hold if `x` is true.
|
||||
|
||||
Please note that this is not an equivalence relation. The constraint can still
|
||||
be true if `x` is false.
|
||||
Note that this is not an equivalence relation. The constraint can still be true
|
||||
if `x` is false.
|
||||
|
||||
So we can write b => And(x, not y). That is, if b is true, then x is true and y
|
||||
is false. Note that in this particular example, there are multiple ways to
|
||||
@@ -293,6 +339,7 @@ then is written as Or(not b, x) and Or(not b, not y).
|
||||
### Python code
|
||||
|
||||
```python
|
||||
# Snippet from ortools/sat/samples/reified_sample_sat.py
|
||||
#!/usr/bin/env python3
|
||||
"""Simple model with a reified constraint."""
|
||||
|
||||
@@ -300,37 +347,42 @@ from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def reified_sample_sat():
|
||||
"""Showcase creating a reified constraint."""
|
||||
model = cp_model.CpModel()
|
||||
"""Showcase creating a reified constraint."""
|
||||
model = cp_model.CpModel()
|
||||
|
||||
x = model.new_bool_var("x")
|
||||
y = model.new_bool_var("y")
|
||||
b = model.new_bool_var("b")
|
||||
x = model.new_bool_var('x')
|
||||
y = model.new_bool_var('y')
|
||||
b = model.new_bool_var('b')
|
||||
|
||||
# First version using a half-reified bool and.
|
||||
model.add_bool_and(x, ~y).only_enforce_if(b)
|
||||
# First version using a half-reified bool and.
|
||||
model.add_bool_and(x, ~y).only_enforce_if(b)
|
||||
|
||||
# Second version using implications.
|
||||
model.add_implication(b, x)
|
||||
model.add_implication(b, ~y)
|
||||
# Second version using implications.
|
||||
model.add_implication(b, x)
|
||||
model.add_implication(b, ~y)
|
||||
|
||||
# Third version using bool or.
|
||||
model.add_bool_or(~b, x)
|
||||
model.add_bool_or(~b, ~y)
|
||||
# Third version using bool or.
|
||||
model.add_bool_or(~b, x)
|
||||
model.add_bool_or(~b, ~y)
|
||||
|
||||
|
||||
reified_sample_sat()
|
||||
|
||||
```
|
||||
|
||||
### C++ code
|
||||
|
||||
```cpp
|
||||
// Snippet from ortools/sat/samples/reified_sample_sat.cc
|
||||
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -364,11 +416,15 @@ int main(int argc, char* argv[]) {
|
||||
operations_research::sat::ReifiedSampleSat();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Java code
|
||||
|
||||
```java
|
||||
// Snippet from ortools/sat/samples/ReifiedSampleSat.java
|
||||
|
||||
|
||||
package com.google.ortools.sat.samples;
|
||||
|
||||
import com.google.ortools.Loader;
|
||||
@@ -407,11 +463,15 @@ public class ReifiedSampleSat {
|
||||
model.addBoolOr(new Literal[] {b.not(), y.not()});
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### C\# code
|
||||
|
||||
```cs
|
||||
```csharp
|
||||
// Snippet from ortools/sat/samples/ReifiedSampleSat.cs
|
||||
|
||||
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
@@ -437,11 +497,15 @@ public class ReifiedSampleSat
|
||||
model.AddBoolOr(new ILiteral[] { b.Not(), y.Not() });
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Go code
|
||||
|
||||
```cs
|
||||
```go
|
||||
// Snippet from ortools/sat/samples/reified_sample_sat.go
|
||||
|
||||
|
||||
// The reified_sample_sat command is a simple example of implication constraints.
|
||||
package main
|
||||
|
||||
@@ -471,29 +535,37 @@ func reifiedSampleSat() {
|
||||
func main() {
|
||||
reifiedSampleSat()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Product of two Boolean Variables
|
||||
|
||||
A useful construct is the product `p` of two Boolean variables `x` and `y`.
|
||||
|
||||
p == x * y
|
||||
```
|
||||
p == x * y
|
||||
```
|
||||
|
||||
This is equivalent to the logical relation
|
||||
|
||||
p <=> x and y
|
||||
```
|
||||
p <=> x and y
|
||||
```
|
||||
|
||||
This is encoded using one bool_or constraint and two implications. The following
|
||||
code samples output this truth table:
|
||||
|
||||
x = 0 y = 0 p = 0
|
||||
x = 1 y = 0 p = 0
|
||||
x = 0 y = 1 p = 0
|
||||
x = 1 y = 1 p = 1
|
||||
```
|
||||
x = 0 y = 0 p = 0
|
||||
x = 1 y = 0 p = 0
|
||||
x = 0 y = 1 p = 0
|
||||
x = 1 y = 1 p = 1
|
||||
```
|
||||
|
||||
### Python code
|
||||
|
||||
```python
|
||||
# Snippet from ortools/sat/samples/boolean_product_sample_sat.py
|
||||
#!/usr/bin/env python3
|
||||
"""Code sample that encodes the product of two Boolean variables."""
|
||||
|
||||
@@ -502,35 +574,39 @@ from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def boolean_product_sample_sat():
|
||||
"""Encoding of the product of two Boolean variables.
|
||||
"""Encoding of the product of two Boolean variables.
|
||||
|
||||
p == x * y, which is the same as p <=> x and y
|
||||
"""
|
||||
model = cp_model.CpModel()
|
||||
x = model.new_bool_var("x")
|
||||
y = model.new_bool_var("y")
|
||||
p = model.new_bool_var("p")
|
||||
p == x * y, which is the same as p <=> x and y
|
||||
"""
|
||||
model = cp_model.CpModel()
|
||||
x = model.new_bool_var('x')
|
||||
y = model.new_bool_var('y')
|
||||
p = model.new_bool_var('p')
|
||||
|
||||
# x and y implies p, rewrite as not(x and y) or p.
|
||||
model.add_bool_or(~x, ~y, p)
|
||||
# x and y implies p, rewrite as not(x and y) or p.
|
||||
model.add_bool_or(~x, ~y, p)
|
||||
|
||||
# p implies x and y, expanded into two implications.
|
||||
model.add_implication(p, x)
|
||||
model.add_implication(p, y)
|
||||
# p implies x and y, expanded into two implications.
|
||||
model.add_implication(p, x)
|
||||
model.add_implication(p, y)
|
||||
|
||||
# Create a solver and solve.
|
||||
solver = cp_model.CpSolver()
|
||||
solution_printer = cp_model.VarArraySolutionPrinter([x, y, p])
|
||||
solver.parameters.enumerate_all_solutions = True
|
||||
solver.solve(model, solution_printer)
|
||||
# Create a solver and solve.
|
||||
solver = cp_model.CpSolver()
|
||||
solution_printer = cp_model.VarArraySolutionPrinter([x, y, p])
|
||||
solver.parameters.enumerate_all_solutions = True
|
||||
solver.solve(model, solution_printer)
|
||||
|
||||
|
||||
boolean_product_sample_sat()
|
||||
|
||||
```
|
||||
|
||||
### Go code
|
||||
|
||||
```cs
|
||||
```go
|
||||
// Snippet from ortools/sat/samples/boolean_product_sample_sat.go
|
||||
|
||||
|
||||
// The boolean_product_sample_sat command is a simple example of the product of two literals.
|
||||
package main
|
||||
|
||||
@@ -538,9 +614,10 @@ import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
"github.com/google/or-tools/ortools/sat/go/cpmodel"
|
||||
sppb "github.com/google/or-tools/ortools/sat/proto/satparameters"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"github.com/google/or-tools/ortools/sat/go/cpmodel"
|
||||
|
||||
sppb "github.com/google/or-tools/ortools/sat/proto/satparameters"
|
||||
)
|
||||
|
||||
func booleanProductSample() error {
|
||||
@@ -564,7 +641,7 @@ func booleanProductSample() error {
|
||||
}
|
||||
// Set `fill_additional_solutions_in_response` and `enumerate_all_solutions` to true so
|
||||
// the solver returns all solutions found.
|
||||
params := &sppb.SatParameters{
|
||||
params := &sppb.SatParameters{
|
||||
FillAdditionalSolutionsInResponse: proto.Bool(true),
|
||||
EnumerateAllSolutions: proto.Bool(true),
|
||||
SolutionPoolSize: proto.Int32(4),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -77,39 +77,37 @@ Some remarks:
|
||||
### Python code
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Code sample that solves a model using solution hinting."""
|
||||
|
||||
# Snippet from ortools/sat/samples/solution_hinting_sample_sat.py
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def solution_hinting_sample_sat():
|
||||
"""Showcases solution hinting."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
"""Showcases solution hinting."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.new_int_var(0, num_vals - 1, "x")
|
||||
y = model.new_int_var(0, num_vals - 1, "y")
|
||||
z = model.new_int_var(0, num_vals - 1, "z")
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.new_int_var(0, num_vals - 1, 'x')
|
||||
y = model.new_int_var(0, num_vals - 1, 'y')
|
||||
z = model.new_int_var(0, num_vals - 1, 'z')
|
||||
|
||||
# Creates the constraints.
|
||||
model.add(x != y)
|
||||
# Creates the constraints.
|
||||
model.add(x != y)
|
||||
|
||||
model.maximize(x + 2 * y + 3 * z)
|
||||
model.maximize(x + 2 * y + 3 * z)
|
||||
|
||||
# Solution hinting: x <- 1, y <- 2
|
||||
model.add_hint(x, 1)
|
||||
model.add_hint(y, 2)
|
||||
# Solution hinting: x <- 1, y <- 2
|
||||
model.add_hint(x, 1)
|
||||
model.add_hint(y, 2)
|
||||
|
||||
# Creates a solver and solves.
|
||||
solver = cp_model.CpSolver()
|
||||
solution_printer = cp_model.VarArrayAndObjectiveSolutionPrinter([x, y, z])
|
||||
status = solver.solve(model, solution_printer)
|
||||
# Creates a solver and solves.
|
||||
solver = cp_model.CpSolver()
|
||||
solution_printer = cp_model.VarArrayAndObjectiveSolutionPrinter([x, y, z])
|
||||
status = solver.solve(model, solution_printer)
|
||||
|
||||
print(f"Status = {solver.status_name(status)}")
|
||||
print(f"Number of solutions found: {solution_printer.solution_count}")
|
||||
print(f'Status = {solver.status_name(status)}')
|
||||
print(f'Number of solutions found: {solution_printer.solution_count}')
|
||||
|
||||
|
||||
solution_hinting_sample_sat()
|
||||
@@ -118,12 +116,13 @@ solution_hinting_sample_sat()
|
||||
### C++ code
|
||||
|
||||
```cpp
|
||||
// Snippet from ortools/sat/samples/solution_hinting_sample_sat.cc
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
#include "ortools/sat/cp_model.pb.h"
|
||||
#include "ortools/sat/cp_model_solver.h"
|
||||
@@ -179,13 +178,14 @@ int main(int argc, char* argv[]) {
|
||||
### Java code
|
||||
|
||||
```java
|
||||
// Snippet from ortools/sat/samples/SolutionHintingSampleSat.java
|
||||
package com.google.ortools.sat.samples;
|
||||
|
||||
import com.google.ortools.Loader;
|
||||
import com.google.ortools.sat.CpSolverStatus;
|
||||
import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.CpSolverSolutionCallback;
|
||||
import com.google.ortools.sat.CpSolverStatus;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
import com.google.ortools.sat.LinearExpr;
|
||||
|
||||
@@ -242,7 +242,8 @@ public class SolutionHintingSampleSat {
|
||||
|
||||
### C\# code
|
||||
|
||||
```cs
|
||||
```csharp
|
||||
// Snippet from ortools/sat/samples/SolutionHintingSampleSat.cs
|
||||
using System;
|
||||
using Google.OrTools.Sat;
|
||||
|
||||
@@ -307,7 +308,7 @@ public class SolutionHintingSampleSat
|
||||
|
||||
### Go code
|
||||
|
||||
```cs
|
||||
```go
|
||||
// The solution_hinting_sample_sat command is an example of setting solution hints on the model.
|
||||
package main
|
||||
|
||||
@@ -316,6 +317,7 @@ import (
|
||||
|
||||
log "github.com/golang/glog"
|
||||
"github.com/google/or-tools/ortools/sat/go/cpmodel"
|
||||
|
||||
cmpb "github.com/google/or-tools/ortools/sat/proto/cpmodel"
|
||||
)
|
||||
|
||||
@@ -360,6 +362,7 @@ func main() {
|
||||
log.Exitf("solutionHintingSampleSat returned with error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Model copy
|
||||
@@ -375,58 +378,60 @@ The deep copy python mechanism relies on the
|
||||
[`copy` Python Standard Library](https://docs.python.org/3/library/copy.html).
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Showcases deep copying of a model."""
|
||||
|
||||
# Snippet from ortools/sat/samples/clone_model_sample_sat.py
|
||||
import copy
|
||||
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def clone_model_sample_sat():
|
||||
"""Showcases cloning a model."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
"""Showcases cloning a model."""
|
||||
# Creates the model.
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.new_int_var(0, num_vals - 1, "x")
|
||||
y = model.new_int_var(0, num_vals - 1, "y")
|
||||
z = model.new_int_var(0, num_vals - 1, "z")
|
||||
# Creates the variables.
|
||||
num_vals = 3
|
||||
x = model.new_int_var(0, num_vals - 1, 'x')
|
||||
y = model.new_int_var(0, num_vals - 1, 'y')
|
||||
z = model.new_int_var(0, num_vals - 1, 'z')
|
||||
|
||||
# Creates the constraints.
|
||||
model.add(x != y)
|
||||
# Creates the constraints.
|
||||
model.add(x != y)
|
||||
|
||||
model.maximize(x + 2 * y + 3 * z)
|
||||
model.maximize(x + 2 * y + 3 * z)
|
||||
|
||||
# Creates a solver and solves.
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.solve(model)
|
||||
# Creates a solver and solves.
|
||||
solver = cp_model.CpSolver()
|
||||
status = solver.solve(model)
|
||||
|
||||
if status == cp_model.OPTIMAL:
|
||||
print("Optimal value of the original model: {}".format(solver.objective_value))
|
||||
if status == cp_model.OPTIMAL:
|
||||
print(
|
||||
'Optimal value of the original model: {}'.format(solver.objective_value)
|
||||
)
|
||||
|
||||
# Creates a dictionary holding the model and the variables you want to use.
|
||||
to_clone = {
|
||||
"model": model,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"z": z,
|
||||
}
|
||||
# Creates a dictionary holding the model and the variables you want to use.
|
||||
to_clone = {
|
||||
'model': model,
|
||||
'x': x,
|
||||
'y': y,
|
||||
'z': z,
|
||||
}
|
||||
|
||||
# Deep copy the dictionary.
|
||||
clone = copy.deepcopy(to_clone)
|
||||
# Deep copy the dictionary.
|
||||
clone = copy.deepcopy(to_clone)
|
||||
|
||||
# Retrieve the cloned model and variables.
|
||||
cloned_model: cp_model.CpModel = clone["model"]
|
||||
cloned_x = clone["x"]
|
||||
cloned_y = clone["y"]
|
||||
cloned_model.add(cloned_x + cloned_y <= 1)
|
||||
# Retrieve the cloned model and variables.
|
||||
cloned_model: cp_model.CpModel = clone['model']
|
||||
cloned_x = clone['x']
|
||||
cloned_y = clone['y']
|
||||
cloned_model.add(cloned_x + cloned_y <= 1)
|
||||
|
||||
status = solver.solve(cloned_model)
|
||||
status = solver.solve(cloned_model)
|
||||
|
||||
if status == cp_model.OPTIMAL:
|
||||
print("Optimal value of the modified model: {}".format(solver.objective_value))
|
||||
if status == cp_model.OPTIMAL:
|
||||
print(
|
||||
'Optimal value of the modified model: {}'.format(solver.objective_value)
|
||||
)
|
||||
|
||||
|
||||
clone_model_sample_sat()
|
||||
@@ -435,12 +440,13 @@ clone_model_sample_sat()
|
||||
### C++ code
|
||||
|
||||
```cpp
|
||||
// Snippet from ortools/sat/samples/clone_model_sample_sat.cc
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "absl/base/log_severity.h"
|
||||
#include "absl/log/globals.h"
|
||||
#include "ortools/sat/cp_model.h"
|
||||
#include "ortools/sat/cp_model.pb.h"
|
||||
#include "ortools/sat/cp_model_solver.h"
|
||||
@@ -492,12 +498,13 @@ int main(int argc, char* argv[]) {
|
||||
### Java code
|
||||
|
||||
```java
|
||||
// Snippet from ortools/sat/samples/CloneModelSampleSat.java
|
||||
package com.google.ortools.sat.samples;
|
||||
|
||||
import com.google.ortools.Loader;
|
||||
import com.google.ortools.sat.CpSolverStatus;
|
||||
import com.google.ortools.sat.CpModel;
|
||||
import com.google.ortools.sat.CpSolver;
|
||||
import com.google.ortools.sat.CpSolverStatus;
|
||||
import com.google.ortools.sat.IntVar;
|
||||
import com.google.ortools.sat.LinearExpr;
|
||||
|
||||
|
||||
@@ -119,7 +119,12 @@ bool LinearExpression2::IsCanonicalized() const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (vars[0] >= vars[1]) return false;
|
||||
if (vars[0] >= vars[1]) {
|
||||
if (vars[0] == kNoIntegerVariable && vars[1] == kNoIntegerVariable) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vars[0] == kNoIntegerVariable) return true;
|
||||
|
||||
|
||||
@@ -358,7 +358,9 @@ H AbslHashValue(H h, const AffineExpression& e) {
|
||||
// A linear expression with at most two variables (coeffs can be zero).
|
||||
// And some utility to canonicalize them.
|
||||
struct LinearExpression2 {
|
||||
// Construct a zero expression.
|
||||
LinearExpression2() = default;
|
||||
|
||||
LinearExpression2(IntegerVariable v1, IntegerVariable v2, IntegerValue c1,
|
||||
IntegerValue c2) {
|
||||
vars[0] = v1;
|
||||
@@ -428,7 +430,7 @@ struct LinearExpression2 {
|
||||
}
|
||||
|
||||
IntegerValue coeffs[2];
|
||||
IntegerVariable vars[2];
|
||||
IntegerVariable vars[2] = {kNoIntegerVariable, kNoIntegerVariable};
|
||||
|
||||
template <typename Sink>
|
||||
friend void AbslStringify(Sink& sink, const LinearExpression2& expr) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace operations_research::sat {
|
||||
@@ -22,6 +23,7 @@ namespace {
|
||||
|
||||
TEST(CanonicalizeAffinePrecedenceTest, Basic) {
|
||||
LinearExpression2 expr;
|
||||
CHECK(expr.IsCanonicalized()) << expr;
|
||||
expr.vars[0] = IntegerVariable(0);
|
||||
expr.vars[1] = IntegerVariable(2);
|
||||
expr.coeffs[0] = IntegerValue(4);
|
||||
@@ -30,6 +32,7 @@ TEST(CanonicalizeAffinePrecedenceTest, Basic) {
|
||||
IntegerValue lb(0);
|
||||
IntegerValue ub(11);
|
||||
expr.CanonicalizeAndUpdateBounds(lb, ub);
|
||||
CHECK(expr.IsCanonicalized());
|
||||
|
||||
EXPECT_EQ(expr.vars[0], IntegerVariable(0));
|
||||
EXPECT_EQ(expr.vars[1], IntegerVariable(2));
|
||||
@@ -47,6 +50,7 @@ TEST(CanonicalizeAffinePrecedenceTest, OneSingleVariable) {
|
||||
expr.coeffs[1] = IntegerValue(2);
|
||||
|
||||
expr.SimpleCanonicalization();
|
||||
CHECK(expr.IsCanonicalized());
|
||||
|
||||
EXPECT_EQ(expr.vars[0], kNoIntegerVariable);
|
||||
EXPECT_EQ(expr.vars[1], IntegerVariable(0));
|
||||
|
||||
@@ -183,12 +183,12 @@ IntervalsRepository::GetOrCreateDisjunctivePrecedenceLiteralIfNonTrivial(
|
||||
if (enforcement_literals.empty()) {
|
||||
const auto [expr_a_before_b, ub_a_before_b] =
|
||||
EncodeDifferenceLowerThan(a.end, b.start, 0);
|
||||
reified_precedences_->AddReifiedBoundIfNonTrivial(
|
||||
reified_precedences_->AddBoundEncodingIfNonTrivial(
|
||||
a_before_b, expr_a_before_b, ub_a_before_b);
|
||||
|
||||
const auto [expr_b_before_a, ub_b_before_a] =
|
||||
EncodeDifferenceLowerThan(b.end, a.start, 0);
|
||||
reified_precedences_->AddReifiedBoundIfNonTrivial(
|
||||
reified_precedences_->AddBoundEncodingIfNonTrivial(
|
||||
a_before_b.Negated(), expr_b_before_a, ub_b_before_a);
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ IntervalsRepository::GetOrCreateDisjunctivePrecedenceLiteralIfNonTrivial(
|
||||
bool IntervalsRepository::CreatePrecedenceLiteralIfNonTrivial(
|
||||
AffineExpression x, AffineExpression y) {
|
||||
const auto [expr, ub] = EncodeDifferenceLowerThan(x, y, 0);
|
||||
const LiteralIndex index = reified_precedences_->GetReifiedBound(expr, ub);
|
||||
const LiteralIndex index = reified_precedences_->GetEncodedBound(expr, ub);
|
||||
if (index != kNoLiteralIndex) return false;
|
||||
|
||||
// We want l => x <= y and not(l) => x > y <=> y + 1 <= x
|
||||
@@ -231,7 +231,7 @@ bool IntervalsRepository::CreatePrecedenceLiteralIfNonTrivial(
|
||||
// Create a new literal.
|
||||
const BooleanVariable boolean_var = sat_solver_->NewBooleanVariable();
|
||||
const Literal x_before_y = Literal(boolean_var, true);
|
||||
reified_precedences_->AddReifiedBoundIfNonTrivial(x_before_y, expr, ub);
|
||||
reified_precedences_->AddBoundEncodingIfNonTrivial(x_before_y, expr, ub);
|
||||
|
||||
AffineExpression y_plus_one = y;
|
||||
y_plus_one.constant += 1;
|
||||
@@ -243,7 +243,7 @@ bool IntervalsRepository::CreatePrecedenceLiteralIfNonTrivial(
|
||||
LiteralIndex IntervalsRepository::GetPrecedenceLiteral(
|
||||
AffineExpression x, AffineExpression y) const {
|
||||
const auto [expr, ub] = EncodeDifferenceLowerThan(x, y, 0);
|
||||
return reified_precedences_->GetReifiedBound(expr, ub);
|
||||
return reified_precedences_->GetEncodedBound(expr, ub);
|
||||
}
|
||||
|
||||
Literal IntervalsRepository::GetOrCreatePrecedenceLiteral(AffineExpression x,
|
||||
|
||||
@@ -1093,7 +1093,8 @@ int GreaterThanAtLeastOneOfDetector::AddGreaterThanAtLeastOneOfConstraints(
|
||||
|
||||
ReifiedLinear2Bounds::ReifiedLinear2Bounds(Model* model)
|
||||
: integer_encoder_(model->GetOrCreate<IntegerEncoder>()),
|
||||
best_root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()) {
|
||||
best_root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()),
|
||||
lin2_indices_(model->GetOrCreate<Linear2Indices>()) {
|
||||
int index = 0;
|
||||
model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
|
||||
[index = index, trail = model->GetOrCreate<Trail>(), this]() mutable {
|
||||
@@ -1108,14 +1109,16 @@ ReifiedLinear2Bounds::ReifiedLinear2Bounds(Model* model)
|
||||
if (relevant_true_literals.empty()) return true;
|
||||
|
||||
// Linear scan.
|
||||
for (const auto [l, expr, ub] : all_reified_relations_) {
|
||||
for (const auto [l, expr_index, ub] : all_reified_relations_) {
|
||||
if (relevant_true_literals.contains(l)) {
|
||||
best_root_level_bounds_->AddUpperBound(expr, ub);
|
||||
VLOG(2) << "New fixed precedence: " << expr << " <= " << ub
|
||||
best_root_level_bounds_->AddUpperBound(expr_index, ub);
|
||||
VLOG(2) << "New fixed precedence: "
|
||||
<< lin2_indices_->GetExpression(expr_index) << " <= " << ub
|
||||
<< " (was reified by " << l << ")";
|
||||
} else if (relevant_true_literals.contains(l.Negated())) {
|
||||
best_root_level_bounds_->AddLowerBound(expr, ub + 1);
|
||||
VLOG(2) << "New fixed precedence: " << expr << " > " << ub
|
||||
best_root_level_bounds_->AddLowerBound(expr_index, ub + 1);
|
||||
VLOG(2) << "New fixed precedence: "
|
||||
<< lin2_indices_->GetExpression(expr_index) << " > " << ub
|
||||
<< " (was reified by not(" << l << "))";
|
||||
}
|
||||
}
|
||||
@@ -1138,20 +1141,35 @@ RelationStatus ReifiedLinear2Bounds::GetLevelZeroPrecedenceStatus(
|
||||
ub);
|
||||
}
|
||||
|
||||
void ReifiedLinear2Bounds::AddReifiedBoundIfNonTrivial(Literal l,
|
||||
LinearExpression2 expr,
|
||||
IntegerValue ub) {
|
||||
void ReifiedLinear2Bounds::AddBoundEncodingIfNonTrivial(Literal l,
|
||||
LinearExpression2 expr,
|
||||
IntegerValue ub) {
|
||||
DCHECK(expr.IsCanonicalized());
|
||||
DCHECK_EQ(expr.DivideByGcd(), 1);
|
||||
const RelationStatus status =
|
||||
best_root_level_bounds_->GetLevelZeroStatus(expr, kMinIntegerValue, ub);
|
||||
if (status != RelationStatus::IS_UNKNOWN) return;
|
||||
|
||||
relation_to_lit_.insert({{expr, ub}, l});
|
||||
if (expr.vars[0] == kNoIntegerVariable) {
|
||||
// We checked for IS_UNKNOWN above, which is not possible for constant
|
||||
// linear2.
|
||||
DCHECK_NE(expr.vars[1], kNoIntegerVariable);
|
||||
|
||||
DCHECK_EQ(expr.coeffs[1], 1);
|
||||
integer_encoder_->AssociateToIntegerLiteral(
|
||||
l, IntegerLiteral::LowerOrEqual(expr.vars[1], ub));
|
||||
return;
|
||||
}
|
||||
const LinearExpression2Index expr_index = lin2_indices_->AddOrGet(expr);
|
||||
relation_to_lit_.insert({{expr_index, ub}, l});
|
||||
variable_appearing_in_reified_relations_.insert(l.Variable());
|
||||
all_reified_relations_.push_back({l, expr, ub});
|
||||
all_reified_relations_.push_back({l, expr_index, ub});
|
||||
}
|
||||
|
||||
LiteralIndex ReifiedLinear2Bounds::GetReifiedBound(LinearExpression2 expr,
|
||||
LiteralIndex ReifiedLinear2Bounds::GetEncodedBound(LinearExpression2 expr,
|
||||
IntegerValue ub) {
|
||||
DCHECK(expr.IsCanonicalized());
|
||||
DCHECK_EQ(expr.DivideByGcd(), 1);
|
||||
const RelationStatus status =
|
||||
best_root_level_bounds_->GetLevelZeroStatus(expr, kMinIntegerValue, ub);
|
||||
if (status == RelationStatus::IS_TRUE) {
|
||||
@@ -1160,8 +1178,16 @@ LiteralIndex ReifiedLinear2Bounds::GetReifiedBound(LinearExpression2 expr,
|
||||
if (status == RelationStatus::IS_FALSE) {
|
||||
return integer_encoder_->GetFalseLiteral().Index();
|
||||
}
|
||||
if (expr.vars[0] == kNoIntegerVariable) {
|
||||
DCHECK_NE(expr.vars[1], kNoIntegerVariable);
|
||||
DCHECK_EQ(expr.coeffs[1], 1);
|
||||
return integer_encoder_->GetAssociatedLiteral(
|
||||
IntegerLiteral::LowerOrEqual(expr.vars[1], ub));
|
||||
}
|
||||
|
||||
const auto it = relation_to_lit_.find({expr, ub});
|
||||
const LinearExpression2Index expr_index = lin2_indices_->GetIndex(expr);
|
||||
if (expr_index == kNoLinearExpression2Index) return kNoLiteralIndex;
|
||||
const auto it = relation_to_lit_.find({expr_index, ub});
|
||||
if (it == relation_to_lit_.end()) return kNoLiteralIndex;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -188,6 +188,10 @@ class RootLevelLinear2Bounds {
|
||||
return AddUpperBound(expr, -lb);
|
||||
}
|
||||
|
||||
bool AddLowerBound(LinearExpression2Index index, IntegerValue lb) {
|
||||
return AddUpperBound(NegationOf(index), -lb);
|
||||
}
|
||||
|
||||
// All modifications go through this function.
|
||||
bool AddUpperBound(LinearExpression2Index index, IntegerValue ub);
|
||||
|
||||
@@ -615,21 +619,24 @@ class ReifiedLinear2Bounds {
|
||||
AffineExpression b) const;
|
||||
|
||||
// Register the fact that l <=> ( expr <= ub ).
|
||||
// `expr` must already be canonicalized and gcd-reduced.
|
||||
// These are considered equivalence relation.
|
||||
void AddReifiedBoundIfNonTrivial(Literal l, LinearExpression2 expr,
|
||||
IntegerValue ub);
|
||||
void AddBoundEncodingIfNonTrivial(Literal l, LinearExpression2 expr,
|
||||
IntegerValue ub);
|
||||
|
||||
// Returns kNoLiteralIndex if we don't have a literal <=> ( expr <= ub ), or
|
||||
// returns that literal if we have one. Note that we will return the
|
||||
// true/false literal if the status is known at level zero.
|
||||
LiteralIndex GetReifiedBound(LinearExpression2 expr, IntegerValue ub);
|
||||
// `expr` must be canonicalized and gcd-reduced.
|
||||
LiteralIndex GetEncodedBound(LinearExpression2 expr, IntegerValue ub);
|
||||
|
||||
private:
|
||||
IntegerEncoder* integer_encoder_;
|
||||
RootLevelLinear2Bounds* best_root_level_bounds_;
|
||||
Linear2Indices* lin2_indices_;
|
||||
|
||||
// This stores relations l <=> (linear2 <= rhs).
|
||||
absl::flat_hash_map<std::pair<LinearExpression2, IntegerValue>, Literal>
|
||||
absl::flat_hash_map<std::pair<LinearExpression2Index, IntegerValue>, Literal>
|
||||
relation_to_lit_;
|
||||
|
||||
// This is used to detect relations that become fixed at level zero and
|
||||
@@ -637,7 +644,7 @@ class ReifiedLinear2Bounds {
|
||||
// we fix variable, a linear scan shouldn't be too bad and is relatively
|
||||
// compact memory wise.
|
||||
absl::flat_hash_set<BooleanVariable> variable_appearing_in_reified_relations_;
|
||||
std::vector<std::tuple<Literal, LinearExpression2, IntegerValue>>
|
||||
std::vector<std::tuple<Literal, LinearExpression2Index, IntegerValue>>
|
||||
all_reified_relations_;
|
||||
};
|
||||
|
||||
|
||||
@@ -356,6 +356,27 @@ class CpModelHelperTest(absltest.TestCase):
|
||||
self.assertFalse(e12.is_integer())
|
||||
self.assertEqual(str(e12), "(3.1 * (x + 2))")
|
||||
|
||||
def test_large_lin_expr(self):
|
||||
model = cmh.CpModelProto()
|
||||
all_records = range(4)
|
||||
decision_var = []
|
||||
for r in all_records:
|
||||
decision_var.append(
|
||||
[
|
||||
cmh.IntVar(model).with_name(f"x{r}0"),
|
||||
cmh.IntVar(model).with_name(f"x{r}1"),
|
||||
cmh.IntVar(model).with_name(f"x{r}2"),
|
||||
]
|
||||
)
|
||||
|
||||
expr = sum(
|
||||
(decision_var[r][1] + decision_var[r][2]) * 2
|
||||
+ (decision_var[r][0] + decision_var[r][2]) * 3
|
||||
+ (decision_var[r][0] + decision_var[r][1]) * 4
|
||||
for r in all_records
|
||||
)
|
||||
print(expr, expr == 2)
|
||||
|
||||
|
||||
class CpModelBuilderTest(absltest.TestCase):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user