change solution observer on sat to take a CpSolverResponse as parameter

This commit is contained in:
Laurent Perron
2017-10-16 15:02:51 +02:00
parent e8540c1363
commit a54b49ebb8
7 changed files with 32 additions and 35 deletions

View File

@@ -173,7 +173,7 @@ void Solve(const std::vector<int>& durations, const std::vector<int>& due_dates,
// Optional preprocessing: add precedences that don't change the optimal
// solution value.
//
// Proof: in any schedule, if such precendece between task A and B is not
// Proof: in any schedule, if such precedence between task A and B is not
// satisfied, then it is always better (or the same) to swap A and B. This is
// because the tasks between A and B will be completed earlier (because the
// duration of A is smaller), and the cost of the swap itself is also smaller.
@@ -211,15 +211,15 @@ void Solve(const std::vector<int>& durations, const std::vector<int>& due_dates,
// lower bound for the objective and the tardiness variables.
Model model;
model.Add(NewSatParameters(FLAGS_params));
model.Add(NewFeasibleSolutionObserver([&](const std::vector<int64>& values) {
model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) {
// Note that we conpute the "real" cost here and do not use the tardiness
// variables. This is because in the core based appraoch, the tardiness
// variables. This is because in the core based approach, the tardiness
// variable might be fixed before the end date, and we just have a >=
// relation.
int64 objective = 0;
for (int i = 0; i < num_tasks; ++i) {
objective +=
weights[i] * std::max(0ll, values[tasks_end[i]] - due_dates[i]);
weights[i] * std::max<int64>(0ll, r.solution(tasks_end[i]) - due_dates[i]);
}
LOG(INFO) << "Cost " << objective;
@@ -227,21 +227,21 @@ void Solve(const std::vector<int>& durations, const std::vector<int>& due_dates,
std::vector<int> sorted_tasks(num_tasks);
std::iota(sorted_tasks.begin(), sorted_tasks.end(), 0);
std::sort(sorted_tasks.begin(), sorted_tasks.end(), [&](int v1, int v2) {
return values[tasks_start[v1]] < values[tasks_start[v2]];
return r.solution(tasks_start[v1]) < r.solution(tasks_start[v2]);
});
std::string solution = "0";
int end = 0;
for (const int i : sorted_tasks) {
const int64 cost = weights[i] * values[tardiness_vars[i]];
const int64 cost = weights[i] * r.solution(tardiness_vars[i]);
StrAppend(&solution, "| #", i, " ");
if (cost > 0) {
// Display the cost in red.
StrAppend(&solution, "\033[1;31m(+", cost, ") \033[0m");
}
StrAppend(&solution, "|", values[tasks_end[i]]);
CHECK_EQ(end, values[tasks_start[i]]);
StrAppend(&solution, "|", r.solution(tasks_end[i]));
CHECK_EQ(end, r.solution(tasks_start[i]));
end += durations[i];
CHECK_EQ(end, values[tasks_end[i]]);
CHECK_EQ(end, r.solution(tasks_end[i]));
}
LOG(INFO) << "solution: " << solution;
}));

View File

@@ -863,10 +863,10 @@ void SolveFzWithCpModelProto(const fz::Model& fz_model,
if (FLAGS_use_flatzinc_format && p.all_solutions) {
int solution_count = 1; // Start at 1 as in the sat solver output.
auto printer = [&fz_model, &solution_count,
&m](const std::vector<int64>& values) {
&m](const sat::CpSolverResponse& response) {
const std::string solution_string =
SolutionString(fz_model, [&values, &m](fz::IntegerVariable* v) {
return values[m.fz_var_to_index[v]];
SolutionString(fz_model, [&response, &m](fz::IntegerVariable* v) {
return response.solution(m.fz_var_to_index[v]);
});
std::cout << "%% solution #" << solution_count++ << std::endl;
std::cout << solution_string << std::endl;

View File

@@ -65,7 +65,7 @@ message IntegerVariableProto {
// value in its domain. As of 2017/09/18, only linear constraints with two
// terms support this (i.e. precedence constraints).
//
// This can be a powerfull concept because it allows the solver to propagate
// This can be a powerful concept because it allows the solver to propagate
// the variable regardless of the enforcement literal, and if its domain
// become empty, it can propagate this literal to false.
repeated int32 enforcement_literal = 3;
@@ -220,7 +220,7 @@ message ConstraintProto {
// constraint must be true when this literal is true, if it is false, then it
// doesn't matter. This is also called half-reification. To have an
// equivalence between a literal and a constraint (full reification), one must
// add both a constraint (controled by a literal l) and its negation
// add both a constraint (controlled by a literal l) and its negation
// (controlled by the negation of l).
repeated int32 enforcement_literal = 2;

View File

@@ -1977,11 +1977,11 @@ void ExtractLinearObjective(const CpModelProto& model_proto,
// Used by NewFeasibleSolutionObserver to register observers.
struct SolutionObservers {
explicit SolutionObservers(Model* model) {}
std::vector<std::function<void(const std::vector<int64>& values)>> observers;
std::vector<std::function<void(const CpSolverResponse& response)>> observers;
};
std::function<void(Model*)> NewFeasibleSolutionObserver(
const std::function<void(const std::vector<int64>& values)>& observer) {
const std::function<void(const CpSolverResponse& response)>& observer) {
return [=](Model* model) {
model->GetOrCreate<SolutionObservers>()->observers.push_back(observer);
};
@@ -2378,14 +2378,7 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) {
model_proto, true,
[&](const CpSolverResponse& response) {
for (const auto& observer : observers) {
if (response.solution().empty()) {
observer(
std::vector<int64>(response.solution_lower_bounds().begin(),
response.solution_lower_bounds().end()));
} else {
observer(std::vector<int64>(response.solution().begin(),
response.solution().end()));
}
observer(response);
}
},
model);
@@ -2405,13 +2398,7 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model) {
CpSolverResponse copy = response;
PostsolveResponse(model_proto, mapping_proto, postsolve_mapping, &copy);
for (const auto& observer : observers) {
if (copy.solution().empty()) {
observer(std::vector<int64>(copy.solution_lower_bounds().begin(),
copy.solution_lower_bounds().end()));
} else {
observer(std::vector<int64>(copy.solution().begin(),
copy.solution().end()));
}
observer(copy);
}
},
model);

View File

@@ -49,11 +49,8 @@ CpSolverResponse SolveCpModel(const CpModelProto& model_proto, Model* model);
//
// Hack: For the non-fully instantiated variables, the value will be the
// propagated lower bound. Note that this will be fixed with the TODO below.
//
// TODO(user): Change the API to take the full CpSolverResponse() so we have
// solve statistics and the current objective value.
std::function<void(Model*)> NewFeasibleSolutionObserver(
const std::function<void(const std::vector<int64>& values)>& observer);
const std::function<void(const CpSolverResponse& response)>& observer);
// Allows to change the default parameters with
// model->Add(NewSatParameters(parameters_as_string_or_proto))

View File

@@ -43,6 +43,7 @@ PY_PROTO_TYPEMAP(ortools.sat.sat_parameters_pb2,
%unignore operations_research::sat::SatHelper;
%unignore operations_research::sat::SatHelper::Solve;
%unignore operations_research::sat::SatHelper::SolveWithParameters;
%unignore operations_research::sat::SatHelper::SolveWithParametersAndSolutionObserver;
%include "ortools/sat/swig_helper.h"

View File

@@ -43,6 +43,18 @@ class SatHelper {
model.Add(NewSatParameters(parameters));
return SolveCpModel(model_proto, &model);
}
static operations_research::sat::CpSolverResponse
SolveWithParametersAndSolutionObserver(
const operations_research::sat::CpModelProto& model_proto,
const operations_research::sat::SatParameters& parameters,
const std::function<void(const operations_research::sat::CpSolverResponse&
response)>& observer) {
Model model;
model.Add(NewSatParameters(parameters));
model.Add(NewFeasibleSolutionObserver(observer));
return SolveCpModel(model_proto, &model);
}
};
} // namespace sat