Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_utils.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <functional>
18#include <queue>
19#include <tuple>
20#include <utility>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "absl/types/span.h"
26
27namespace operations_research {
28
30 std::function<int64_t(int, int)> load_demand_of_item_for_bin,
31 std::vector<LoadLimit> load_limit_per_bin) {
32 DCHECK_EQ(num_bins_, load_limit_per_bin.size());
33 for (const LoadLimit& limit : load_limit_per_bin) {
34 const int64_t violation = std::max<int64_t>(0, CapOpp(limit.soft_max_load));
35 total_cost_ =
36 CapAdd(total_cost_, CapProd(violation, limit.cost_above_soft_max_load));
37 }
38 load_demands_per_dimension_.push_back(std::move(load_demand_of_item_for_bin));
39 for (int b = 0; b < num_bins_; ++b) {
40 load_per_bin_[b].push_back(0);
41 load_limits_per_bin_[b].push_back(load_limit_per_bin[b]);
42 }
43}
44
45bool BinCapacities::CheckAdditionFeasibility(int item, int bin) const {
46 return CheckAdditionsFeasibility({item}, bin);
47}
48
49bool BinCapacities::CheckAdditionsFeasibility(absl::Span<const int> items,
50 int bin) const {
51 for (size_t dim = 0; dim < load_demands_per_dimension_.size(); ++dim) {
52 const LoadLimit& limit = load_limits_per_bin_[bin][dim];
53 int64_t new_load = load_per_bin_[bin][dim];
54 for (const int item : items) {
55 new_load = CapAdd(new_load, load_demands_per_dimension_[dim](item, bin));
56 }
57 // TODO(user): try to reorder on failure, so that tight dimensions are
58 // checked first.
59 if (new_load > limit.max_load) return false;
60 }
61 return true;
62}
63
64bool BinCapacities::AddItemToBin(int item, int bin) {
65 bool feasible = true;
66 for (size_t dim = 0; dim < load_demands_per_dimension_.size(); ++dim) {
67 int64_t& load = load_per_bin_[bin][dim];
68 const LoadLimit& limit = load_limits_per_bin_[bin][dim];
69 const int64_t prev_violation =
70 std::max<int64_t>(0, CapSub(load, limit.soft_max_load));
71 load = CapAdd(load, load_demands_per_dimension_[dim](item, bin));
72 const int64_t curr_violation =
73 std::max<int64_t>(0, CapSub(load, limit.soft_max_load));
74 total_cost_ =
75 CapAdd(total_cost_, CapProd(CapSub(curr_violation, prev_violation),
77 feasible &= load <= limit.max_load;
78 }
79 return feasible;
80}
81
82bool BinCapacities::RemoveItemFromBin(int item, int bin) {
83 bool feasible = true;
84 for (size_t dim = 0; dim < load_demands_per_dimension_.size(); ++dim) {
85 int64_t& load = load_per_bin_[bin][dim];
86 const LoadLimit& limit = load_limits_per_bin_[bin][dim];
87 const int64_t prev_violation =
88 std::max<int64_t>(0, CapSub(load, limit.soft_max_load));
89 load = CapSub(load, load_demands_per_dimension_[dim](item, bin));
90 const int64_t curr_violation =
91 std::max<int64_t>(0, CapSub(load, limit.soft_max_load));
92 total_cost_ =
93 CapAdd(total_cost_, CapProd(CapSub(curr_violation, prev_violation),
95 feasible &= load <= limit.max_load;
96 }
97 return feasible;
98}
99
101 for (size_t dim = 0; dim < load_demands_per_dimension_.size(); ++dim) {
102 int64_t& load = load_per_bin_[bin][dim];
103 const LoadLimit& limit = load_limits_per_bin_[bin][dim];
104 const int64_t prev_violation =
105 std::max<int64_t>(0, CapSub(load, limit.soft_max_load));
106 load = 0;
107 const int64_t curr_violation =
108 std::max<int64_t>(0, CapOpp(limit.soft_max_load));
109 total_cost_ =
110 CapAdd(total_cost_, CapProd(CapSub(curr_violation, prev_violation),
112 }
113}
114
116 for (int bin = 0; bin < num_bins_; ++bin) {
117 ClearItemsOfBin(bin);
118 }
119}
120
122 int num_arcs, int64_t start,
123 const std::function<int64_t(int64_t)>& next_accessor,
124 const std::function<bool(int64_t)>& is_end,
125 const std::function<int64_t(int64_t, int64_t, int64_t)>&
126 arc_cost_for_route_start,
127 std::vector<std::pair<int64_t, int>>* most_expensive_arc_starts_and_ranks,
128 std::pair<int, int>* first_expensive_arc_indices) {
129 if (is_end(next_accessor(start))) {
130 // Empty route.
131 *first_expensive_arc_indices = {-1, -1};
132 return false;
133 }
134
135 // NOTE: The negative ranks are so that for a given cost, lower ranks are
136 // given higher priority.
137 using ArcCostNegativeRankStart = std::tuple<int64_t, int, int64_t>;
138 std::priority_queue<ArcCostNegativeRankStart,
139 std::vector<ArcCostNegativeRankStart>,
140 std::greater<ArcCostNegativeRankStart>>
141 arc_info_pq;
142
143 int64_t before_node = start;
144 int rank = 0;
145 while (!is_end(before_node)) {
146 const int64_t after_node = next_accessor(before_node);
147 const int64_t arc_cost =
148 arc_cost_for_route_start(before_node, after_node, start);
149 arc_info_pq.emplace(arc_cost, -rank, before_node);
150
151 before_node = after_node;
152 rank++;
153
154 if (rank > num_arcs) {
155 arc_info_pq.pop();
156 }
157 }
158
159 DCHECK_GE(rank, 2);
160 DCHECK_EQ(arc_info_pq.size(), std::min(rank, num_arcs));
161
162 most_expensive_arc_starts_and_ranks->resize(arc_info_pq.size());
163 int arc_index = arc_info_pq.size() - 1;
164 while (!arc_info_pq.empty()) {
165 const ArcCostNegativeRankStart& arc_info = arc_info_pq.top();
166 (*most_expensive_arc_starts_and_ranks)[arc_index] = {
167 std::get<2>(arc_info), -std::get<1>(arc_info)};
168 arc_index--;
169 arc_info_pq.pop();
170 }
171
172 *first_expensive_arc_indices = {0, 1};
173 return true;
174}
175
176} // namespace operations_research
void ClearItems()
Removes all items from all bins.
bool RemoveItemFromBin(int item, int bin)
Removes item from bin, return whether the bin is feasible.
void AddDimension(std::function< int64_t(int, int)> load_demand_of_item_for_bin, std::vector< LoadLimit > load_limit_per_bin)
void ClearItemsOfBin(int bin)
Removes all items from given bin, resets the total cost.
bool CheckAdditionFeasibility(int item, int bin) const
Checks whether adding item(s) is feasible w.r.t. dimensions.
bool AddItemToBin(int item, int bin)
bool CheckAdditionsFeasibility(absl::Span< const int > items, int bin) const
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
bool FindMostExpensiveArcsOnRoute(int num_arcs, int64_t start, const std::function< int64_t(int64_t)> &next_accessor, const std::function< bool(int64_t)> &is_end, const std::function< int64_t(int64_t, int64_t, int64_t)> &arc_cost_for_route_start, std::vector< std::pair< int64_t, int > > *most_expensive_arc_starts_and_ranks, std::pair< int, int > *first_expensive_arc_indices)
int64_t CapOpp(int64_t v)
Note(user): -kint64min != kint64max, but kint64max == ~kint64min.