@@ -646,7 +646,24 @@ class route_t {
646646 orig_route, intra_ejection_indices[n_ejections - 1 ] + 1 , route_length + 1 , curr_size);
647647 }
648648
649- // extend for other things later
649+ /* *
650+ * @brief Recomputes the objective and infeasibility cost of the current route.
651+ *
652+ * Resets the route cost accumulators, invokes cost computation for each active
653+ * dimension, and returns the resulting objective and infeasibility costs.
654+ *
655+ * When both BREAK and TIME dimensions are present, this method also adds an
656+ * explicit infeasibility penalty for missing required breaks. A break is treated
657+ * as required if its latest time is no later than the vehicle arrival time at
658+ * the depot. Any deficit between required breaks and breaks present in the
659+ * route is added to the BREAK infeasibility component.
660+ *
661+ * @param check_single_threaded If true, assert that exactly one warp thread is active.
662+ *
663+ * @return Tuple of `{objective_cost, infeasibility_cost}` for the route.
664+ *
665+ * @note Mutates and returns `objective_cost[0]` and `infeasibility_cost[0]`.
666+ */
650667 DI thrust::tuple<objective_cost_t , infeasible_cost_t > compute_cost (
651668 bool check_single_threaded = true )
652669 {
@@ -663,6 +680,23 @@ class route_t {
663680 .compute_cost (this ->vehicle_info (), *n_nodes, objective_cost[0 ], infeasibility_cost[0 ]);
664681 });
665682
683+ // Penalize missing required breaks; the per-dim formula only catches excess.
684+ if (dimensions_info ().has_dimension (dim_t ::BREAK ) &&
685+ dimensions_info ().has_dimension (dim_t ::TIME )) {
686+ auto vinfo = this ->vehicle_info ();
687+ const i_t n_breaks = vinfo.num_breaks ();
688+ if (n_breaks > 0 ) {
689+ const double arrival_at_depot = dimensions.time_dim .departure_forward [*n_nodes] +
690+ dimensions.time_dim .excess_forward [*n_nodes];
691+ i_t required = 0 ;
692+ for (i_t i = 0 ; i < n_breaks; ++i) {
693+ required += (vinfo.break_latest [i] <= arrival_at_depot);
694+ }
695+ const i_t breaks_present = dimensions.break_dim .breaks_forward [*n_nodes];
696+ infeasibility_cost[0 ][dim_t ::BREAK ] += max (0.0 , required - breaks_present);
697+ }
698+ }
699+
666700 return thrust::make_tuple (objective_cost[0 ], infeasibility_cost[0 ]);
667701 }
668702
0 commit comments