diff --git a/examples/cpp/fap_parser.cc b/examples/cpp/fap_parser.cc index 054013c1b4..e0b20db307 100644 --- a/examples/cpp/fap_parser.cc +++ b/examples/cpp/fap_parser.cc @@ -24,7 +24,7 @@ namespace operations_research { void ParseFileByLines(const std::string& filename, std::vector* lines) { - CHECK_NOTNULL(lines); + CHECK(lines != nullptr); std::string result; CHECK_OK(file::GetContents(filename, &result, file::Defaults())); *lines = absl::StrSplit(result, '\n', absl::SkipEmpty()); @@ -295,10 +295,10 @@ void ParseInstance(const std::string& data_directory, bool find_components, std::vector* constraints, std::string* objective, std::vector* frequencies, std::unordered_map* components) { - CHECK_NOTNULL(variables); - CHECK_NOTNULL(constraints); - CHECK_NOTNULL(objective); - CHECK_NOTNULL(frequencies); + CHECK(variables != nullptr); + CHECK(constraints != nullptr); + CHECK(objective != nullptr); + CHECK(frequencies != nullptr); // Parse the data files. VariableParser var(data_directory); @@ -352,7 +352,7 @@ void ParseInstance(const std::string& data_directory, bool find_components, *objective = cst.objective(); if (find_components) { - CHECK_NOTNULL(components); + CHECK(components != nullptr); FindComponents(*constraints, *variables, maximum_variable_id, components); // Evaluate each components's constraints impacts. for (auto& component : *components) { diff --git a/examples/cpp/flexible_jobshop.cc b/examples/cpp/flexible_jobshop.cc index de9abb0504..0aa46112ba 100644 --- a/examples/cpp/flexible_jobshop.cc +++ b/examples/cpp/flexible_jobshop.cc @@ -44,21 +44,13 @@ #include "examples/cpp/flexible_jobshop.h" #include "ortools/util/string_array.h" -DEFINE_string( - data_file, - "", - "Required: input file description the scheduling problem to solve, " - "in our jssp format:\n" - " - the first line is \"instance \"\n" - " - the second line is \" \"\n" - " - then one line per job, with a single space-separated " - "list of \" \"\n" - "note: jobs with one task are not supported"); +DEFINE_string(data_file, "", "A flexible shobshop problem (.fjs)."); DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); namespace operations_research { + struct TaskAlternative { - TaskAlternative(int j) : job_id(j), alternative_variable(nullptr) {} + explicit TaskAlternative(int j) : job_id(j), alternative_variable(nullptr) {} int job_id; std::vector intervals; IntVar* alternative_variable; @@ -92,12 +84,8 @@ void FlexibleJobshop(const FlexibleJobShopData& data) { for (int alt = 0; alt < task.machines.size(); ++alt) { const int machine_id = task.machines[alt]; const int duration = task.durations[alt]; - const string name = StringPrintf("J%dI%dA%dM%dD%d", - task.job_id, - task_index, - alt, - machine_id, - duration); + const std::string name = StringPrintf("J%dI%dA%dM%dD%d", task.job_id, + task_index, alt, machine_id, duration); IntervalVar* const interval = solver.MakeFixedDurationIntervalVar( 0, horizon, duration, optional, name); jobs_to_tasks[job_id].back().intervals.push_back(interval); @@ -106,7 +94,7 @@ void FlexibleJobshop(const FlexibleJobShopData& data) { active_variables.push_back(interval->PerformedExpr()->Var()); } } - string alternative_name = StringPrintf("J%dI%d", job_id, task_index); + std::string alternative_name = StringPrintf("J%dI%d", job_id, task_index); IntVar* alt_var = solver.MakeIntVar(0, task.machines.size() - 1, alternative_name); jobs_to_tasks[job_id].back().alternative_variable = alt_var; @@ -152,7 +140,7 @@ void FlexibleJobshop(const FlexibleJobShopData& data) { // whose job is to sequence interval variables. std::vector all_sequences; for (int machine_id = 0; machine_id < machine_count; ++machine_id) { - const string name = StringPrintf("Machine_%d", machine_id); + const std::string name = StringPrintf("Machine_%d", machine_id); DisjunctiveConstraint* const ct = solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id], name); solver.AddConstraint(ct); @@ -191,10 +179,8 @@ void FlexibleJobshop(const FlexibleJobShopData& data) { // we can schedule each task at its earliest start time. This is // conveniently done by fixing the objective variable to its // minimum value. - DecisionBuilder* const obj_phase = - solver.MakePhase(objective_var, - Solver::CHOOSE_FIRST_UNBOUND, - Solver::ASSIGN_MIN_VALUE); + DecisionBuilder* const obj_phase = solver.MakePhase( + objective_var, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE); // The main decision builder (ranks all tasks, then fixes the // objective_variable). @@ -206,7 +192,7 @@ void FlexibleJobshop(const FlexibleJobShopData& data) { SearchMonitor* const search_log = solver.MakeSearchLog(kLogFrequency, objective_monitor); - SearchLimit* limit = NULL; + SearchLimit* limit = nullptr; if (FLAGS_time_limit_in_ms > 0) { limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); } @@ -217,15 +203,12 @@ void FlexibleJobshop(const FlexibleJobShopData& data) { collector->Add(all_sequences); // Search. - if (solver.Solve(main_phase, - search_log, - objective_monitor, - limit, + if (solver.Solve(main_phase, search_log, objective_monitor, limit, collector)) { for (int m = 0; m < machine_count; ++m) { SequenceVar* const seq = all_sequences[m]; LOG(INFO) << seq->name() << ": " - << strings::Join(collector->ForwardSequence(0, seq), ", "); + << absl::StrJoin(collector->ForwardSequence(0, seq), ", "); } } } @@ -244,5 +227,5 @@ int main(int argc, char **argv) { operations_research::FlexibleJobShopData data; data.Load(FLAGS_data_file); operations_research::FlexibleJobshop(data); - return 0; + return EXIT_SUCCESS; } diff --git a/examples/cpp/flexible_jobshop.h b/examples/cpp/flexible_jobshop.h index e28b925719..0bda8e1727 100644 --- a/examples/cpp/flexible_jobshop.h +++ b/examples/cpp/flexible_jobshop.h @@ -50,17 +50,17 @@ namespace operations_research { class FlexibleJobShopData { public: // A task is the basic block of a jobshop. + // The diference in a flexible jobshop is that a task has a list of machine + // on which it can be scheduled (with possibly not the same duration). struct Task { Task(int j, const std::vector& m, const std::vector& d) : job_id(j), machines(m), durations(d) {} - string DebugString() const { - string out = StringPrintf("Task(", job_id); + std::string DebugString() const { + std::string out = StringPrintf("Job %d Task(", job_id); for (int k = 0; k < machines.size(); ++k) { + if (k > 0) out.append(" | "); out.append(StringPrintf("", machines[k], durations[k])); - if (k < machines.size() - 1) { - out.append(" | "); - } } out.append(")"); return out; @@ -80,11 +80,15 @@ class FlexibleJobShopData { ~FlexibleJobShopData() {} - // Parses a file in jssp or taillard format and loads the model. See the flag - // --data_file for a description of the format. Note that the format - // is only partially checked: bad inputs might cause undefined - // behavior. - void Load(const string& filename) { + // Parses a file in .fjp format and loads the model. Note that the format is + // only partially checked: bad inputs might cause undefined behavior. + void Load(const std::string& filename) { + size_t found = filename.find_last_of("/\\"); + if (found != std::string::npos) { + name_ = filename.substr(found + 1); + } else { + name_ = filename; + } for (const std::string& line : FileLines(filename)) { if (line.empty()) { continue; @@ -100,7 +104,7 @@ class FlexibleJobShopData { int job_count() const { return job_count_; } // The name of the jobshop instance. - const string& name() const { return name_; } + const std::string& name() const { return name_; } // The horizon of the workshop (the sum of all durations), which is // a trivial upper bound of the optimal make_span. @@ -111,8 +115,8 @@ class FlexibleJobShopData { return all_tasks_[job_id]; } - string DebugString() const { - string out = + std::string DebugString() const { + std::string out = StringPrintf("FlexibleJobshop(name = %s, %d machines, %d jobs)\n", name_.c_str(), machine_count_, job_count_); for (int j = 0; j < all_tasks_.size(); ++j) { @@ -131,8 +135,8 @@ class FlexibleJobShopData { private: void ProcessNewLine(const std::string& line) { - static const char kWordDelimiters[] = " "; - std::vector words = absl::StrSplit(line, " ", absl::SkipEmpty()); + const std::vector words = + absl::StrSplit(line, ' ', absl::SkipEmpty()); if (machine_count_ == -1 && words.size() > 1) { job_count_ = atoi32(words[0]); machine_count_ = atoi32(words[1]); @@ -175,12 +179,13 @@ class FlexibleJobShopData { horizon_ += SumOfDurations(durations); } - string name_; + std::string name_; int machine_count_; int job_count_; int horizon_; std::vector > all_tasks_; int current_job_index_; }; -} // namespace operations_research + +} // namespace operations_research #endif // OR_TOOLS_EXAMPLES_FLEXIBLE_JOBSHOP_H_ diff --git a/examples/cpp/frequency_assignment_problem.cc b/examples/cpp/frequency_assignment_problem.cc index a953a6af35..6c6bb04035 100644 --- a/examples/cpp/frequency_assignment_problem.cc +++ b/examples/cpp/frequency_assignment_problem.cc @@ -333,7 +333,7 @@ bool ConstraintImpactComparator(FapConstraint constraint1, int64 ValueEvaluator( std::unordered_map>* value_evaluator_map, int64 variable_index, int64 value) { - CHECK_NOTNULL(value_evaluator_map); + CHECK(value_evaluator_map != nullptr); // Evaluate the choice. Smaller ranking denotes a better choice. int64 ranking = -1; for (const auto& it : *value_evaluator_map) { @@ -382,10 +382,10 @@ void CreateModelVariables(const std::map& data_variables, Solver* solver, std::vector* model_variables, std::map* index_from_key, std::vector* key_from_index) { - CHECK_NOTNULL(solver); - CHECK_NOTNULL(model_variables); - CHECK_NOTNULL(index_from_key); - CHECK_NOTNULL(key_from_index); + CHECK(solver != nullptr); + CHECK(model_variables != nullptr); + CHECK(index_from_key != nullptr); + CHECK(key_from_index != nullptr); const int number_of_variables = static_cast(data_variables.size()); model_variables->resize(number_of_variables); @@ -412,7 +412,7 @@ void CreateModelConstraints(const std::vector& data_constraints, const std::vector& variables, const std::map& index_from_key, Solver* solver) { - CHECK_NOTNULL(solver); + CHECK(solver != nullptr); for (const FapConstraint& ct : data_constraints) { const int index1 = gtl::FindOrDie(index_from_key, ct.variable1); @@ -438,7 +438,7 @@ void CreateModelConstraints(const std::vector& data_constraints, // According to the value of a command line flag, chooses the strategy which // determines the selection of the variable to be assigned next. void ChooseVariableStrategy(Solver::IntVarStrategy* variable_strategy) { - CHECK_NOTNULL(variable_strategy); + CHECK(variable_strategy != nullptr); switch (FLAGS_choose_next_variable_strategy) { case 1: { @@ -476,8 +476,8 @@ void ChooseVariableStrategy(Solver::IntVarStrategy* variable_strategy) { // for the search of the Solver. void CreateAdditionalMonitors(OptimizeVar* const objective, Solver* solver, std::vector* monitors) { - CHECK_NOTNULL(solver); - CHECK_NOTNULL(monitors); + CHECK(solver != nullptr); + CHECK(monitors != nullptr); // Search Log if (FLAGS_log_search) { diff --git a/examples/cpp/jobshop_ls.h b/examples/cpp/jobshop_ls.h index 632ab938c7..c917579a0d 100644 --- a/examples/cpp/jobshop_ls.h +++ b/examples/cpp/jobshop_ls.h @@ -61,7 +61,7 @@ class SwapIntervals : public SequenceVarLocalSearchOperator { ~SwapIntervals() override {} bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override { - CHECK_NOTNULL(delta); + CHECK(delta != nullptr); while (true) { RevertChanges(true); if (!Increment()) { @@ -123,7 +123,7 @@ class ShuffleIntervals : public SequenceVarLocalSearchOperator { ~ShuffleIntervals() override {} bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override { - CHECK_NOTNULL(delta); + CHECK(delta != nullptr); while (true) { RevertChanges(true); if (!Increment()) { @@ -198,7 +198,7 @@ class SequenceLns : public SequenceVarLocalSearchOperator { ~SequenceLns() override {} bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override { - CHECK_NOTNULL(delta); + CHECK(delta != nullptr); while (true) { RevertChanges(true); if (random_.Uniform(2) == 0) { diff --git a/examples/cpp/jobshop_sat.cc b/examples/cpp/jobshop_sat.cc index 9cdc27e1f4..d2df9159de 100644 --- a/examples/cpp/jobshop_sat.cc +++ b/examples/cpp/jobshop_sat.cc @@ -37,6 +37,9 @@ DEFINE_string(params, "", "Sat parameters in text proto format."); DEFINE_bool(use_boolean_precedences, false, "Whether we create Boolean variables for all the possible " "precedences between tasks on the same machine, or not."); +DEFINE_bool(use_optional_variables, true, + "Whether we use optional variables for bounds of an optional " + "interval or not."); namespace { struct Task { @@ -108,15 +111,31 @@ void Solve(const std::vector>& tasks_per_job, int horizon) { if (num_alternatives == 1) { machine_to_intervals[task.machines[0]].push_back(interval); } else { - std::vector alternatives; - for (int i = 0; i < num_alternatives; ++i) { - const Literal is_present(model.Add(NewBooleanVariable()), true); - const IntervalVariable alternative = model.Add( - NewOptionalInterval(0, horizon, task.durations[i], is_present)); - alternatives.push_back(alternative); - machine_to_intervals[task.machines[i]].push_back(alternative); + if (FLAGS_use_optional_variables) { + std::vector alternatives; + for (int i = 0; i < num_alternatives; ++i) { + const Literal is_present(model.Add(NewBooleanVariable()), true); + const IntervalVariable alternative = + model.Add(NewOptionalIntervalWithOptionalVariables( + 0, horizon, task.durations[i], is_present)); + alternatives.push_back(alternative); + machine_to_intervals[task.machines[i]].push_back(alternative); + } + model.Add(IntervalWithAlternatives(interval, alternatives)); + } else { + std::vector exactly_one; + for (int i = 0; i < num_alternatives; ++i) { + const Literal is_present(model.Add(NewBooleanVariable()), true); + exactly_one.push_back(is_present); + const IntervalVariable alternative = + model.GetOrCreate()->CreateInterval( + model.Get(StartVar(interval)), model.Get(EndVar(interval)), + kNoIntegerVariable, IntegerValue(task.durations[i]), + is_present.Index()); + machine_to_intervals[task.machines[i]].push_back(alternative); + } + model.Add(ExactlyOneConstraint(exactly_one)); } - model.Add(IntervalWithAlternatives(interval, alternatives)); } } @@ -133,6 +152,13 @@ void Solve(const std::vector>& tasks_per_job, int horizon) { } } + // Auto detect "at least one of" constraints in the PrecedencesPropagator. + if (model.Mutable() != nullptr) { + model.Mutable() + ->AddGreaterThanAtLeastOneOfConstraints(&model); + model.GetOrCreate()->Propagate(); + } + LOG(INFO) << "#machines:" << num_machines; LOG(INFO) << "#jobs:" << tasks_per_job.size(); LOG(INFO) << "#tasks:" << model.Get()->NumIntervals(); @@ -143,7 +169,7 @@ void Solve(const std::vector>& tasks_per_job, int horizon) { } MinimizeIntegerVariableWithLinearScanAndLazyEncoding( /*log_info=*/true, makespan, - FirstUnassignedVarAtItsMinHeuristic(decision_variables, &model), + UnassignedVarWithLowestMinAtItsMinHeuristic(decision_variables, &model), /*feasible_solution_observer=*/ [makespan](const Model& model) { LOG(INFO) << "Makespan " << model.Get(LowerBound(makespan)); diff --git a/examples/cpp/model_util.cc b/examples/cpp/model_util.cc index ffa51dc2d5..238abba7cc 100644 --- a/examples/cpp/model_util.cc +++ b/examples/cpp/model_util.cc @@ -109,7 +109,7 @@ void ExportLinks(const CpModel& model, const std::string& source, const T& proto // integer variable with min_value == max_value. bool GetValueIfConstant(const CpModel& model, const CpIntegerExpression& proto, int64* const value) { - CHECK_NOTNULL(value); + CHECK(value != nullptr); const int expr_type = proto.type_index(); if (model.tags(expr_type) != ModelVisitor::kIntegerVariable) { return false; @@ -144,7 +144,7 @@ void DeclareExpression(int index, const CpModel& proto, if (!expr.name().empty()) { exporter->WriteNode(label, expr.name(), "oval", kGreen1); } else if (GetValueIfConstant(proto, expr, &value)) { - exporter->WriteNode(label, StrCat(value), "oval", kYellow); + exporter->WriteNode(label, absl::StrCat(value), "oval", kYellow); } else { const std::string& type = proto.tags(expr.type_index()); exporter->WriteNode(label, type, "oval", kWhite); diff --git a/examples/cpp/parse_dimacs_assignment.h b/examples/cpp/parse_dimacs_assignment.h index b57f903f41..0a8608071f 100644 --- a/examples/cpp/parse_dimacs_assignment.h +++ b/examples/cpp/parse_dimacs_assignment.h @@ -220,8 +220,8 @@ void DimacsAssignmentParser::ParseOneLine(const std::string& line) { template LinearSumAssignment* DimacsAssignmentParser::Parse( std::string* error_message, GraphType** graph_handle) { - CHECK_NOTNULL(error_message); - CHECK_NOTNULL(graph_handle); + CHECK(error_message != nullptr); + CHECK(graph_handle != nullptr); for (const std::string& line : FileLines(filename_)) { if (line.empty()) { diff --git a/examples/cpp/sat_runner.cc b/examples/cpp/sat_runner.cc index 6b2f5eecd3..df5fd40f5a 100644 --- a/examples/cpp/sat_runner.cc +++ b/examples/cpp/sat_runner.cc @@ -183,7 +183,8 @@ std::string SolutionString(const LinearBooleanProblem& problem, BooleanVariable limit(problem.original_num_variables()); for (BooleanVariable index(0); index < limit; ++index) { if (index > 0) output += " "; - StrAppend(&output, Literal(index, assignment[index.value()]).SignedValue()); + absl::StrAppend(&output, + Literal(index, assignment[index.value()]).SignedValue()); } return output; } diff --git a/examples/cpp/weighted_tardiness_sat.cc b/examples/cpp/weighted_tardiness_sat.cc index 4d4cb9f1d4..2c121aa316 100644 --- a/examples/cpp/weighted_tardiness_sat.cc +++ b/examples/cpp/weighted_tardiness_sat.cc @@ -233,12 +233,12 @@ void Solve(const std::vector& durations, const std::vector& due_dates, int end = 0; for (const int i : sorted_tasks) { const int64 cost = weights[i] * r.solution(tardiness_vars[i]); - StrAppend(&solution, "| #", i, " "); + absl::StrAppend(&solution, "| #", i, " "); if (cost > 0) { // Display the cost in red. - StrAppend(&solution, "\033[1;31m(+", cost, ") \033[0m"); + absl::StrAppend(&solution, "\033[1;31m(+", cost, ") \033[0m"); } - StrAppend(&solution, "|", r.solution(tasks_end[i])); + absl::StrAppend(&solution, "|", r.solution(tasks_end[i])); CHECK_EQ(end, r.solution(tasks_start[i])); end += durations[i]; CHECK_EQ(end, r.solution(tasks_end[i]));