Skip to content

Commit

Permalink
[CP-SAT] check the time limit more frequently; experimental support f…
Browse files Browse the repository at this point in the history
…or lp folding; more pedantic tests in presolve
  • Loading branch information
lperron committed Oct 16, 2024
1 parent 2ce45ed commit b8626a3
Show file tree
Hide file tree
Showing 37 changed files with 1,010 additions and 184 deletions.
4 changes: 2 additions & 2 deletions ortools/sat/2d_orthogonal_packing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ std::optional<std::pair<int, int>> FindPairwiseConflict(
absl::Span<const IntegerValue> sizes_x,
absl::Span<const IntegerValue> sizes_y,
std::pair<IntegerValue, IntegerValue> bounding_box_size,
const std::vector<int>& index_by_decreasing_x_size,
const std::vector<int>& index_by_decreasing_y_size) {
absl::Span<const int> index_by_decreasing_x_size,
absl::Span<const int> index_by_decreasing_y_size) {
// Look for pairwise incompatible pairs by using the logic such conflict can
// only happen between a "tall" item a "wide" item.
int x_idx = 0;
Expand Down
2 changes: 1 addition & 1 deletion ortools/sat/2d_orthogonal_packing_testing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ std::vector<RectangleInRange> MakeItemsFromRectangles(

std::vector<ItemForPairwiseRestriction>
GenerateItemsRectanglesWithNoPairwiseConflict(
const std::vector<Rectangle>& rectangles, double slack_factor,
absl::Span<const Rectangle> rectangles, double slack_factor,
absl::BitGenRef random) {
const std::vector<RectangleInRange> range_items =
MakeItemsFromRectangles(rectangles, slack_factor, random);
Expand Down
2 changes: 1 addition & 1 deletion ortools/sat/2d_orthogonal_packing_testing.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ std::vector<RectangleInRange> MakeItemsFromRectangles(

std::vector<ItemForPairwiseRestriction>
GenerateItemsRectanglesWithNoPairwiseConflict(
const std::vector<Rectangle>& rectangles, double slack_factor,
absl::Span<const Rectangle> rectangles, double slack_factor,
absl::BitGenRef random);

std::vector<ItemForPairwiseRestriction>
Expand Down
2 changes: 1 addition & 1 deletion ortools/sat/2d_rectangle_presolve_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ TEST(RectanglePresolve, RandomTest) {
}
}

Neighbours NaiveBuildNeighboursGraph(const std::vector<Rectangle>& rectangles) {
Neighbours NaiveBuildNeighboursGraph(absl::Span<const Rectangle> rectangles) {
auto interval_intersect = [](IntegerValue begin1, IntegerValue end1,
IntegerValue begin2, IntegerValue end2) {
return std::max(begin1, begin2) < std::min(end1, end2);
Expand Down
16 changes: 12 additions & 4 deletions ortools/sat/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_proto_library")
load("@rules_java//java:defs.bzl", "java_proto_library")
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_python//python:proto.bzl", "py_proto_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")

package(default_visibility = ["//visibility:public"])

Expand Down Expand Up @@ -410,6 +411,7 @@ cc_library(
":intervals",
":lb_tree_search",
":linear_constraint",
":linear_constraint_manager",
":linear_model",
":linear_programming_constraint",
":linear_relaxation",
Expand All @@ -429,9 +431,11 @@ cc_library(
":simplification",
":stat_tables",
":subsolver",
":symmetry_util",
":synchronization",
":util",
":work_assignment",
"//ortools/algorithms:sparse_permutation",
"//ortools/base",
"//ortools/base:status_macros",
"//ortools/base:strong_vector",
Expand Down Expand Up @@ -1284,6 +1288,7 @@ cc_library(
srcs = ["symmetry_util.cc"],
hdrs = ["symmetry_util.h"],
deps = [
":cp_model_cc_proto",
"//ortools/algorithms:dynamic_partition",
"//ortools/algorithms:sparse_permutation",
"//ortools/base",
Expand All @@ -1301,6 +1306,7 @@ cc_test(
":symmetry_util",
"//ortools/algorithms:sparse_permutation",
"//ortools/base:gmock_main",
"//ortools/base:parse_test_proto",
"@com_google_absl//absl/types:span",
],
)
Expand Down Expand Up @@ -2153,6 +2159,7 @@ cc_library(
"//ortools/util:saturated_arithmetic",
"//ortools/util:strong_integers",
"//ortools/util:time_limit",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/numeric:int128",
Expand All @@ -2176,7 +2183,6 @@ cc_library(
"//ortools/base",
"//ortools/base:hash",
"//ortools/base:strong_vector",
"//ortools/glop:revised_simplex",
"//ortools/glop:variables_info",
"//ortools/lp_data:base",
"//ortools/util:logging",
Expand All @@ -2185,10 +2191,11 @@ cc_library(
"//ortools/util:time_limit",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
],
)

Expand Down Expand Up @@ -3011,7 +3018,6 @@ cc_library(
":diffn_util",
":integer",
":linear_constraint_manager",
":linear_programming_constraint",
":model",
":presolve_context",
":rins",
Expand All @@ -3032,6 +3038,7 @@ cc_library(
"@com_google_absl//absl/base:log_severity",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/meta:type_traits",
Expand Down Expand Up @@ -3357,8 +3364,8 @@ cc_library(
hdrs = ["inclusion.h"],
deps = [
"//ortools/base",
"//ortools/util:time_limit",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/types:span",
],
)

Expand Down Expand Up @@ -3434,6 +3441,7 @@ cc_test(
":inclusion",
":util",
"//ortools/base:gmock_main",
"//ortools/util:time_limit",
"@com_google_absl//absl/random",
"@com_google_absl//absl/types:span",
],
Expand Down
8 changes: 6 additions & 2 deletions ortools/sat/clause.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1475,17 +1475,20 @@ bool BinaryImplicationGraph::DetectEquivalences(bool log_info) {
// using the binary implication graph only.
//
// TODO(user): Track which literal have new implications, and only process
// the antecedants of these.
// the antecedents of these.
bool BinaryImplicationGraph::ComputeTransitiveReduction(bool log_info) {
DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
if (time_limit_->LimitReached()) return true;
if (!DetectEquivalences()) return false;

// TODO(user): the situation with fixed variable is not really "clean".
// Simplify the code so we are sure we don't run into issue or have to deal
// with any of that here.
if (time_limit_->LimitReached()) return true;
if (!Propagate(trail_)) return false;
RemoveFixedVariables();
DCHECK(InvariantsAreOk());
if (time_limit_->LimitReached()) return true;

log_info |= VLOG_IS_ON(1);
WallTimer wall_timer;
Expand Down Expand Up @@ -1708,7 +1711,7 @@ bool BinaryImplicationGraph::TransformIntoMaxCliques(
// Data to detect inclusion of base amo into extend amo.
std::vector<int> detector_clique_index;
CompactVectorVector<int> storage;
InclusionDetector detector(storage);
InclusionDetector detector(storage, time_limit_);
detector.SetWorkLimit(1e9);

std::vector<int> dense_index_to_index;
Expand Down Expand Up @@ -2503,6 +2506,7 @@ void BinaryImplicationGraph::CleanupAllRemovedAndFixedVariables() {
}

bool BinaryImplicationGraph::InvariantsAreOk() {
if (time_limit_->LimitReached()) return true;
// We check that if a => b then not(b) => not(a).
absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>> seen;
int num_redundant = 0;
Expand Down
7 changes: 7 additions & 0 deletions ortools/sat/clause.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ class ClauseManager : public SatPropagator {
add_clause_callback_ = std::move(add_clause_callback);
}

// Removes the add clause callback and returns it. This can be used to
// temporarily disable the callback.
absl::AnyInvocable<void(int lbd, absl::Span<const Literal>)>
TakeAddClauseCallback() {
return std::move(add_clause_callback_);
}

private:
// Attaches the given clause. This eventually propagates a literal which is
// enqueued on the trail. Returns false if a contradiction was encountered.
Expand Down
60 changes: 47 additions & 13 deletions ortools/sat/cp_model_lns.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "absl/base/log_severity.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/flags/flag.h"
#include "absl/log/check.h"
#include "absl/meta/type_traits.h"
#include "absl/random/bit_gen_ref.h"
Expand All @@ -49,7 +50,6 @@
#include "ortools/sat/diffn_util.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/linear_constraint_manager.h"
#include "ortools/sat/linear_programming_constraint.h"
#include "ortools/sat/model.h"
#include "ortools/sat/presolve_context.h"
#include "ortools/sat/rins.h"
Expand Down Expand Up @@ -933,7 +933,8 @@ NeighborhoodGeneratorHelper::GetSchedulingPrecedences(
return result;
}

std::vector<std::vector<int>> NeighborhoodGeneratorHelper::GetRoutingPaths(
std::vector<std::vector<int>>
NeighborhoodGeneratorHelper::GetRoutingPathVariables(
const CpSolverResponse& initial_solution) const {
struct HeadAndArcLiteral {
int head;
Expand Down Expand Up @@ -1901,6 +1902,15 @@ Neighborhood LocalBranchingLpBasedNeighborhoodGenerator::Generate(
local_cp_model.mutable_objective()->set_integer_scaling_factor(0);
}

// Dump?
if (absl::GetFlag(FLAGS_cp_model_dump_submodels)) {
const std::string dump_name =
absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
"lb_relax_lns_lp_", data.task_id, ".pb.txt");
LOG(INFO) << "Dumping linear relaxed model to '" << dump_name << "'.";
CHECK(WriteModelProtoToFile(local_cp_model, dump_name));
}

// Solve.
//
// TODO(user): Shall we pass the objective upper bound so we have more
Expand Down Expand Up @@ -2445,26 +2455,24 @@ Neighborhood RoutingRandomNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
const std::vector<std::vector<int>> all_paths =
helper_.GetRoutingPaths(initial_solution);
helper_.GetRoutingPathVariables(initial_solution);

// Collect all unique variables.
absl::flat_hash_set<int> all_path_variables;
for (auto& path : all_paths) {
all_path_variables.insert(path.begin(), path.end());
std::vector<int> variables_to_fix;
for (const auto& path : all_paths) {
variables_to_fix.insert(variables_to_fix.end(), path.begin(), path.end());
}
std::vector<int> fixed_variables(all_path_variables.begin(),
all_path_variables.end());
std::sort(fixed_variables.begin(), fixed_variables.end());
GetRandomSubset(1.0 - data.difficulty, &fixed_variables, random);
gtl::STLSortAndRemoveDuplicates(&variables_to_fix);
GetRandomSubset(1.0 - data.difficulty, &variables_to_fix, random);
return helper_.FixGivenVariables(
initial_solution, {fixed_variables.begin(), fixed_variables.end()});
initial_solution, {variables_to_fix.begin(), variables_to_fix.end()});
}

Neighborhood RoutingPathNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
std::vector<std::vector<int>> all_paths =
helper_.GetRoutingPaths(initial_solution);
helper_.GetRoutingPathVariables(initial_solution);

// Collect all unique variables.
absl::flat_hash_set<int> all_path_variables;
Expand Down Expand Up @@ -2510,7 +2518,7 @@ Neighborhood RoutingFullPathNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
std::vector<std::vector<int>> all_paths =
helper_.GetRoutingPaths(initial_solution);
helper_.GetRoutingPathVariables(initial_solution);
// Remove a corner case where all paths are empty.
if (all_paths.empty()) {
return helper_.NoNeighborhood();
Expand Down Expand Up @@ -2572,6 +2580,32 @@ Neighborhood RoutingFullPathNeighborhoodGenerator::Generate(
return helper_.FixGivenVariables(initial_solution, fixed_variables);
}

Neighborhood RoutingStartsNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
std::vector<std::vector<int>> all_paths =
helper_.GetRoutingPathVariables(initial_solution);
// TODO(user): Maybe enable some routes to be non empty?
if (all_paths.empty()) return helper_.NoNeighborhood();

// Collect all unique path variables, except the start and end of the paths.
// For circuit constraints, we approximate this with the arc going in and out
// of the smallest node. This works well for model where the zero node is an
// artificial node.
std::vector<int> variables_to_fix;
for (const auto& path : all_paths) {
for (const int ref : path) {
if (ref == path.front() || ref == path.back()) continue;
variables_to_fix.push_back(PositiveRef(ref));
}
}
gtl::STLSortAndRemoveDuplicates(&variables_to_fix);
GetRandomSubset(1.0 - data.difficulty, &variables_to_fix, random);

return helper_.FixGivenVariables(
initial_solution, {variables_to_fix.begin(), variables_to_fix.end()});
}

bool RelaxationInducedNeighborhoodGenerator::ReadyToGenerate() const {
return (incomplete_solutions_->HasSolution() ||
lp_solutions_->NumSolutions() > 0);
Expand Down
20 changes: 18 additions & 2 deletions ortools/sat/cp_model_lns.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ class NeighborhoodGeneratorHelper : public SubSolver {
// self-looping arcs. Path are sorted, starting from the arc with the lowest
// tail index, and going in sequence up to the last arc before the circuit is
// closed. Each entry correspond to the arc literal on the circuit.
std::vector<std::vector<int>> GetRoutingPaths(
std::vector<std::vector<int>> GetRoutingPathVariables(
const CpSolverResponse& initial_solution) const;

// Returns all precedences extracted from the scheduling constraint and the
Expand Down Expand Up @@ -392,6 +392,9 @@ class NeighborhoodGenerator {
IntegerValue base_objective = IntegerValue(0);
IntegerValue new_objective = IntegerValue(0);

// For debugging.
int task_id = 0;

// This is just used to construct a deterministic order for the updates.
bool operator<(const SolveData& o) const {
return std::tie(status, difficulty, deterministic_limit,
Expand Down Expand Up @@ -782,7 +785,7 @@ class RoutingPathNeighborhoodGenerator : public NeighborhoodGenerator {
SolveData& data, absl::BitGenRef random) final;
};

// This routing based LNS generator aims are relaxing one full path, and make
// This routing based LNS generator aims at relaxing one full path, and make
// some room on the other paths to absorb the nodes of the relaxed path.
//
// In order to do so, it will relax the first and the last arc of each path in
Expand All @@ -799,6 +802,19 @@ class RoutingFullPathNeighborhoodGenerator : public NeighborhoodGenerator {
SolveData& data, absl::BitGenRef random) final;
};

// This routing based LNS generator performs like the
// RoutingRandomNeighborhoodGenerator, but always relax the arcs going in and
// out of the depot for routes constraints, and of the node with the minimal
// index for circuit constraints.
class RoutingStartsNeighborhoodGenerator : public NeighborhoodGenerator {
public:
RoutingStartsNeighborhoodGenerator(NeighborhoodGeneratorHelper const* helper,
absl::string_view name)
: NeighborhoodGenerator(name, helper) {}
Neighborhood Generate(const CpSolverResponse& initial_solution,
SolveData& data, absl::BitGenRef random) final;
};

// Generates a neighborhood by fixing the variables to solutions reported in
// various repositories. This is inspired from RINS published in "Exploring
// relaxation induced neighborhoods to improve MIP solutions" 2004 by E. Danna
Expand Down
Loading

0 comments on commit b8626a3

Please sign in to comment.