50 for (
const auto &v : fleet) {
51 if (v.is_phony()) unassigned_orders += v.orders_in_vehicle();
53 return unassigned_orders;
64 return unassigned.
empty();
69 namespace optimizers {
98 bool stop_on_all_served,
100 problem::Solution(old_solution),
101 best_solution(old_solution),
102 m_max_cycles(max_cycles),
103 m_stop_on_all_served(stop_on_all_served),
104 m_optimize(optimize) {
143 bool moves_were_done(
false);
148 for (
auto &phony_vehicle :
m_fleet) {
154 if (phony_vehicle.is_real() || phony_vehicle.empty())
continue;
155 pgassert(phony_vehicle.is_phony() && !phony_vehicle.empty());
158 auto orders_in_phony_vehicle = phony_vehicle.orders_in_vehicle();
159 for (
const auto o_id : orders_in_phony_vehicle) {
163 double best_score = 0;
164 auto order =
orders()[o_id];
166 auto best_vehicle_ref = &phony_vehicle;
168 for (
auto &real_vehicle :
m_fleet) {
175 if (&phony_vehicle == &real_vehicle)
continue;
176 if (real_vehicle.is_phony())
continue;
177 if (!real_vehicle.feasible_orders().has(o_id))
continue;
178 if (real_vehicle.has_order(order))
continue;
180 pgassert(&phony_vehicle != &real_vehicle);
186 auto real_vehicle_copy = real_vehicle;
189 auto curr_travel_time = phony_vehicle.total_travel_time() + real_vehicle_copy.total_travel_time();
194 pgassert(!real_vehicle_copy.has_order(order));
195 real_vehicle_copy.hillClimb(order);
198 pgassert((real_vehicle_copy.has_order(order) && real_vehicle_copy.is_feasible())
199 || !real_vehicle_copy.has_order(order));
204 if (!real_vehicle_copy.has_order(order))
continue;
205 pgassert(real_vehicle_copy.has_order(order));
207 auto new_travel_time = phony_vehicle.total_travel_time() + real_vehicle_copy.total_travel_time();
208 auto delta_travel_time = new_travel_time - curr_travel_time;
210 auto delta_objective = delta_travel_time;
211 auto estimated_objective =
objective() +
static_cast<double>(delta_objective);
214 if (best_score == 0 || estimated_objective < best_score) {
215 best_score = estimated_objective;
216 best_vehicle_ref = &real_vehicle;
220 if (best_score != 0) {
221 pgassert(best_vehicle_ref != &phony_vehicle);
222 phony_vehicle.erase(order);
223 best_vehicle_ref->hillClimb(order);
226 moves_were_done =
true;
232 return moves_were_done;
304 int max_no_moves = 2;
306 int max_no_swaps = 2;
308 bool diversification =
true;
310 bool intensification =
false;
314 int could_not_spi = 0;
316 int stuck_counter = 0;
318 int wander_counter = 0;
320 int max_no_improvement = 1000;
322 std::string neighborhood;
324 int wander_length = 100;
329 if (stuck_counter == max_no_improvement) {
335 if (no_moves < max_no_moves && do_spi) {
336 neighborhood =
"spi";
338 neighborhood =
"sbr";
343 if (neighborhood ==
"spi") {
360 if (neighborhood ==
"spi") {
363 if (neighborhood ==
"sbr") {
370 if (no_moves >= max_no_swaps + max_no_moves)
break;
380 if (stuck_counter % wander_length == 0) {
381 intensification = !intensification;
382 diversification = !diversification;
384 if (intensification) {
387 if (diversification) {
416 while (do_spi && i < max_iter) {
422 while (do_sbr && i < max_iter) {
441 size_t max_swaps = std::min(
m_fleet.size(),
orders().size() / 10);
443 bool swaps_were_done =
true;
444 while (swaps_were_done && s < max_swaps) {
446 if (!swaps_were_done) {
449 if (!swaps_were_done) {
455 return swaps_were_done;
471 auto best_from_v = &
m_fleet[0];
473 double best_to_score;
474 double best_from_score;
476 auto best_order =
orders()[0];
477 bool has_phony =
false;
479 std::string best_candidate;
481 for (
auto &from_vehicle :
m_fleet) {
482 if (from_vehicle.is_phony() || from_vehicle.empty())
continue;
484 auto first_order_set = from_vehicle.orders_in_vehicle();
485 for (
const auto o_id : first_order_set) {
486 auto order =
orders()[o_id];
488 if (&from_vehicle == &to_v)
continue;
489 if (to_v.is_phony()) {
492 if (to_v.is_phony())
continue;
493 if (!has_phony && to_v.empty())
continue;
495 if (!to_v.feasible_orders().has(o_id))
continue;
497 std::ostringstream ss;
499 ss << to_v.path_str() <<
":" << o_id;
501 std::string candidate = ss.str();
510 auto to_v_copy = to_v;
511 auto from_v_copy = from_vehicle;
514 auto curr_travel_time = from_v_copy.total_travel_time() + to_v_copy.total_travel_time();
519 pgassert(!to_v_copy.has_order(order));
520 to_v_copy.hillClimb(order);
523 pgassert((to_v_copy.has_order(order) && to_v_copy.is_feasible()) || !to_v_copy.has_order(order));
528 if (!to_v_copy.has_order(order)) {
533 from_v_copy.erase(order);
534 if (from_v_copy.has_order(order) || !from_v_copy.is_feasible())
continue;
536 auto new_travel_time = from_v_copy.total_travel_time() + to_v_copy.total_travel_time();
537 auto delta_travel_time = new_travel_time - curr_travel_time;
539 auto delta_objective = delta_travel_time;
540 auto estimated_objective =
objective() +
static_cast<double>(delta_objective);
545 if (estimated_objective >= best_score && !from_v_copy.empty() && best_score != 0)
continue;
548 from_v_copy, to_v_copy, order, to_v_copy.objective(),
549 from_v_copy.objective())
551 && !from_v_copy.empty())
continue;
553 if (estimated_objective < best_score || from_v_copy.empty() || best_score == 0) {
555 best_score = estimated_objective;
556 best_to_score = to_v.objective();
557 best_from_score = from_vehicle.objective();
559 best_from_v = &from_vehicle;
561 best_candidate = candidate;
568 tabu_list.
add(
Move((*best_from_v), (*best_to_v), best_order, best_to_score, best_from_score));
569 best_to_v->hillClimb(best_order);
570 best_from_v->erase(best_order);
590 auto best_from_v = &
m_fleet[0];
592 double best_to_score;
593 double best_from_score;
594 bool swapped =
false;
595 auto best_from_order =
orders()[0];
596 auto best_to_order =
orders()[0];
598 std::string best_candidate1;
599 std::string best_candidate2;
601 for (
size_t i = 0; i <
m_fleet.size(); ++i) {
603 if (from_vehicle.
is_phony() || from_vehicle.
empty())
continue;
606 for (
const auto o_id1 : first_order_set) {
607 auto order1 =
orders()[o_id1];
608 for (
size_t j = i + 1; j <
m_fleet.size(); ++j) {
610 if (&from_vehicle == &to_v)
continue;
619 for (
const auto o_id2 : second_order_set) {
620 auto order2 =
orders()[o_id2];
623 auto curr_from_objective = from_vehicle.
objective();
624 auto curr_to_objective = to_v.
objective();
629 auto from_v_copy = from_vehicle;
630 auto to_v_copy = to_v;
633 pgassert(from_v_copy.has_order(order1));
634 pgassert(to_v_copy.has_order(order2));
640 to_v_copy.erase(order2);
641 if (to_v_copy.orders_in_vehicle().has(o_id2) || !to_v_copy.is_feasible())
continue;
643 std::ostringstream ss1;
644 ss1 << to_v_copy.path_str() <<
":" << o_id1;
646 std::string candidate1 = ss1.str();
650 from_v_copy.erase(order1);
651 if (from_v_copy.orders_in_vehicle().has(o_id1) || !from_v_copy.is_feasible())
continue;
653 std::ostringstream ss2;
654 ss2 << from_v_copy.path_str() <<
":" << o_id2;
656 std::string candidate2 = ss2.str();
662 to_v_copy.hillClimb(order1);
663 if (!to_v_copy.has_order(order1) || !to_v_copy.is_feasible()) {
669 from_v_copy.hillClimb(order2);
670 if (!from_v_copy.has_order(order2) || !from_v_copy.is_feasible()) {
679 auto new_from_objective = from_v_copy.objective();
680 auto new_to_objective = to_v_copy.objective();
682 auto estimated_delta =
683 +(new_to_objective + new_from_objective)
684 - (curr_from_objective + curr_to_objective);
686 auto estimated_objective =
objective() + estimated_delta;
690 if (estimated_objective >= best_score && best_score != 0)
continue;
693 from_v_copy, to_v_copy, order1, order2, from_v_copy.objective(),
694 to_v_copy.objective())
698 if (estimated_objective < best_score || best_score == 0) {
700 best_from_score = from_vehicle.
objective();
702 best_score = estimated_objective;
704 best_from_v = &from_vehicle;
705 best_from_order = order1;
706 best_to_order = order2;
707 best_candidate1 = candidate1;
708 best_candidate2 = candidate2;
716 Move((*best_from_v), (*best_to_v), best_from_order, best_to_order, best_to_score, best_from_score));
717 best_from_v->erase(best_from_order);
718 best_to_v->erase(best_to_order);
719 best_from_v->hillClimb(best_to_order);
720 best_to_v->hillClimb(best_from_order);
723 if (best_from_v->is_phony()) {
727 if (best_to_v->is_phony()) {
750 if (lhs.orders_in_vehicle().size() == rhs.orders_in_vehicle().size()) {
751 return lhs.id() < rhs.id();
753 return lhs.orders_in_vehicle().size() < rhs.orders_in_vehicle().size();
757 std::sort(m_fleet.begin(), m_fleet.end(), []
760 if (lhs.orders_in_vehicle().size() == rhs.orders_in_vehicle().size()) {
761 return lhs.id() > rhs.id();
763 return lhs.orders_in_vehicle().size() > rhs.orders_in_vehicle().size();
772 std::stable_partition(m_fleet.begin(), m_fleet.end(), []
788 Optimize::delete_empty_truck() {
792 m_fleet.erase(std::remove_if(
796 return v.orders_in_vehicle().empty() && v.is_phony();}),
805 Optimize::save_if_best() {
806 if (objective() < best_solution.objective()) {
807 best_solution = (*this);
808 msg().log <<
"\t*** best objective " << best_solution.cost_str();