Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
set_cover_submodel.cc
Go to the documentation of this file.
1// Copyright 2025 Francesco Cavaliere
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
19
21
23 : base_view(model, &cols_sizes_, &rows_sizes_, &cols_focus_, &rows_focus_),
24 full_model_(model) {
25 ResetToIdentitySubModel();
26 DCHECK(ValidateSubModel(*this));
27}
28
30 const std::vector<FullSubsetIndex>& columns_focus)
31 : base_view(model, &cols_sizes_, &rows_sizes_, &cols_focus_, &rows_focus_),
32 full_model_(model) {
33 rows_sizes_.resize(full_model_->num_elements(), 0);
34 for (ElementIndex i : full_model_->ElementRange()) {
35 rows_sizes_[i] = full_model_->rows()[i].size();
36 }
37 SetFocus(columns_focus);
38}
39
40void SubModelView::ResetToIdentitySubModel() {
41 cols_sizes_.resize(full_model_->num_subsets());
42 rows_sizes_.resize(full_model_->num_elements());
43 cols_focus_.clear();
44 rows_focus_.clear();
45 for (SubsetIndex j : full_model_->SubsetRange()) {
46 cols_sizes_[j] = full_model_->columns()[j].size();
47 cols_focus_.push_back(j);
48 }
49 for (ElementIndex i : full_model_->ElementRange()) {
50 rows_sizes_[i] = full_model_->rows()[i].size();
51 rows_focus_.push_back(i);
52 }
53 fixed_columns_.clear();
54 fixed_cost_ = .0;
55}
56
58 const std::vector<SubsetIndex>& columns_to_fix) {
59 DCHECK(full_model_ != nullptr);
60 Cost old_fixed_cost = fixed_cost_;
61 if (columns_to_fix.empty()) {
62 return fixed_cost_ - old_fixed_cost;
63 }
64
65 for (SubsetIndex j : columns_to_fix) {
66 DCHECK(cols_sizes_[j] > 0);
67 fixed_cost_ += full_model_->subset_costs()[j];
68 fixed_columns_.push_back(static_cast<FullSubsetIndex>(j));
69 cols_sizes_[j] = 0;
70 for (ElementIndex i : full_model_->columns()[j]) {
71 rows_sizes_[i] = 0;
72 }
73 }
74
75 gtl::STLEraseAllFromSequenceIf(&cols_focus_, [&](SubsetIndex j) {
76 DCHECK(full_model_ != nullptr);
77 if (cols_sizes_[j] > 0) {
78 cols_sizes_[j] = absl::c_count_if(full_model_->columns()[j], [&](auto i) {
79 return rows_sizes_[i] > 0;
80 });
81 }
82 return cols_sizes_[j] == 0;
83 });
85 &rows_focus_, [&](ElementIndex i) { return !rows_sizes_[i]; });
86
87 DCHECK(ValidateSubModel(*this));
88 return fixed_cost_ - old_fixed_cost;
89}
90
92 const std::vector<FullSubsetIndex>& columns_to_fix, const DualState&) {
93 ResetToIdentitySubModel();
94 std::vector<SubsetIndex> core_column_to_fix;
95 for (FullSubsetIndex full_j : columns_to_fix) {
96 core_column_to_fix.push_back(static_cast<SubsetIndex>(full_j));
97 }
98 FixMoreColumns(core_column_to_fix);
99}
100
101void SubModelView::SetFocus(const std::vector<FullSubsetIndex>& columns_focus) {
102 DCHECK(full_model_ != nullptr);
103 DCHECK(!rows_sizes_.empty());
104 if (columns_focus.empty()) {
105 return;
106 }
107 cols_focus_.clear();
108 rows_focus_.clear();
109
110 ElementBoolVector enabled_rows(full_model_->num_elements(), false);
111 for (ElementIndex i : full_model_->ElementRange()) {
112 enabled_rows[i] = rows_sizes_[i] > 0;
113 }
114 cols_sizes_.assign(full_model_->num_subsets(), 0);
115 rows_sizes_.assign(full_model_->num_elements(), 0);
116 for (FullSubsetIndex full_j : columns_focus) {
117 SubsetIndex j = static_cast<SubsetIndex>(full_j);
118 for (ElementIndex i : full_model_->columns()[j]) {
119 if (enabled_rows[i] > 0) {
120 ++cols_sizes_[j];
121 ++rows_sizes_[i];
122 }
123 }
124 if (cols_sizes_[j] > 0) {
125 cols_focus_.push_back(j);
126 }
127 }
128 for (ElementIndex i : full_model_->ElementRange()) {
129 if (rows_sizes_[i] > 0) {
130 rows_focus_.push_back(i);
131 }
132 }
133 DCHECK(ValidateSubModel(*this));
134}
135
136CoreModel::CoreModel(const Model* model) : Model(), full_model_(model) {
137 CHECK(ElementIndex(full_model_.num_elements()) < null_element_index)
138 << "Max element index is reserved.";
139 CHECK(SubsetIndex(full_model_.num_subsets()) < null_subset_index)
140 << "Max subset index is reserved.";
141 ResetToIdentitySubModel();
142}
143
145 const std::vector<FullSubsetIndex>& columns_focus)
146 : Model(),
147 full_model_(model),
148 core2full_row_map_(model->num_elements()),
149 full2core_row_map_(model->num_elements()) {
150 CHECK(ElementIndex(full_model_.num_elements()) < null_element_index)
151 << "Max element index is reserved.";
152 CHECK(SubsetIndex(full_model_.num_subsets()) < null_subset_index)
153 << "Max subset index is reserved.";
154
155 absl::c_iota(core2full_row_map_, FullElementIndex());
156 absl::c_iota(full2core_row_map_, ElementIndex());
157 SetFocus(columns_focus);
158}
159
160void CoreModel::ResetToIdentitySubModel() {
161 core2full_row_map_.resize(full_model_.num_elements());
162 full2core_row_map_.resize(full_model_.num_elements());
163 core2full_col_map_.resize(full_model_.num_subsets());
164 absl::c_iota(core2full_row_map_, FullElementIndex());
165 absl::c_iota(full2core_row_map_, ElementIndex());
166 absl::c_iota(core2full_col_map_, FullSubsetIndex());
167 fixed_cost_ = .0;
168 fixed_columns_.clear();
169 static_cast<Model&>(*this) = Model(full_model_.base());
170}
171
172// Note: Assumes that columns_focus covers all rows for which rows_flags is true
173// (i.e.: non-covered rows should be set to false to rows_flags). This property
174// get exploited to keep the rows in the same ordering of the original model
175// using "cleanish" code.
176void CoreModel::SetFocus(const std::vector<FullSubsetIndex>& columns_focus) {
177 if (columns_focus.empty()) {
178 return;
179 }
180
181 // TODO(c4v4): change model in-place to avoid reallocations.
182 Model& submodel = static_cast<Model&>(*this);
183 submodel = Model();
184 core2full_col_map_.clear();
185
186 // Now we can fill the new core model
187 for (FullSubsetIndex full_j : columns_focus) {
188 bool first_row = true;
189 for (FullElementIndex full_i : full_model_.columns()[full_j]) {
190 ElementIndex core_i = full2core_row_map_[full_i];
191 if (core_i != null_element_index) {
192 if (first_row) {
193 // SetCoverModel lacks a way to remove columns
194 first_row = false;
195 submodel.AddEmptySubset(full_model_.subset_costs()[full_j]);
196 }
197 submodel.AddElementToLastSubset(core_i);
198 }
199 }
200 // Handle empty columns
201 if (!first_row) {
202 core2full_col_map_.push_back(full_j);
203 }
204 }
205
206 submodel.CreateSparseRowView();
207 DCHECK(ValidateSubModel(*this));
208}
209
210// Mark columns and row that will be removed from the core model
211// The "to-be-removed" indices are marked by setting the relative core->full
212// mappings to null_*_index.
213void CoreModel::MarkNewFixingInMaps(
214 const std::vector<SubsetIndex>& columns_to_fix) {
215 for (SubsetIndex old_core_j : columns_to_fix) {
216 fixed_cost_ += subset_costs()[old_core_j];
217 fixed_columns_.push_back(core2full_col_map_[old_core_j]);
218
219 core2full_col_map_[old_core_j] = null_full_subset_index;
220 for (ElementIndex old_core_i : columns()[old_core_j]) {
221 core2full_row_map_[old_core_i] = null_full_element_index;
222 }
223 }
224}
225
226// Once fixed columns and covered rows are marked, we need to create a new row
227// mapping, both core->full(returned) and full->core(modifed in-place)
228CoreToFullElementMapVector CoreModel::MakeOrFillBothRowMaps() {
229 full2core_row_map_.assign(full_model_.num_elements(), null_element_index);
230 CoreToFullElementMapVector new_c2f_row_map; // Future core->full mapping.
231 for (ElementIndex old_core_i : ElementRange()) {
232 FullElementIndex full_i = core2full_row_map_[old_core_i];
233 if (full_i != null_full_element_index) {
234 full2core_row_map_[full_i] = ElementIndex(new_c2f_row_map.size());
235 new_c2f_row_map.push_back(full_i);
236 }
237 }
238 return new_c2f_row_map;
239}
240
241// Create a new core model by applying the remapping from the old core model to
242// the new one considering the given column fixing.
243// Both the old and new core->full row mappings are required too keep track of
244// what changed, the old mapping gets overwritten with the new one at the end.
245// Empty columns are detected and removed - or better - not added.
246Model CoreModel::MakeNewCoreModel(
247 const CoreToFullElementMapVector& new_c2f_row_map) {
248 Model new_submodel;
249 BaseInt new_core_j = 0;
250 // Loop over old core column indices.
251 for (SubsetIndex old_core_j : SubsetRange()) {
252 // If the column is not marked, then it should be mapped.
253 FullSubsetIndex full_j = core2full_col_map_[old_core_j];
254 if (full_j != null_full_subset_index) {
255 bool first_row = true;
256 // Loop over the old core column (with old core row indices).
257 for (ElementIndex old_core_i : columns()[old_core_j]) {
258 // If the row is not marked, then it should be mapped.
259 FullElementIndex full_i = core2full_row_map_[old_core_i];
260 if (full_i != null_full_element_index) {
261 if (first_row) {
262 // SetCoverModel lacks a way to remove columns
263 first_row = false;
264 new_submodel.AddEmptySubset(full_model_.subset_costs()[full_j]);
265
266 // Put the full index in the proper (new) position.
267 // Note that old_core_j >= new_core_j is always true.
268 SubsetIndex new_j(new_core_j++);
269 core2full_col_map_[new_j] = full_j;
270 }
271 ElementIndex new_core_i = full2core_row_map_[full_i];
272 DCHECK(new_core_i != null_element_index);
273 new_submodel.AddElementToLastSubset(new_core_i);
274 }
275 }
276 }
277 }
278
279 core2full_col_map_.resize(new_core_j);
280 core2full_row_map_ = std::move(new_c2f_row_map);
281 new_submodel.CreateSparseRowView();
282
283 return new_submodel;
284}
285
286Cost CoreModel::FixMoreColumns(const std::vector<SubsetIndex>& columns_to_fix) {
287 if (columns_to_fix.empty()) {
288 return .0;
289 }
290 Cost old_fixed_cost = fixed_cost_;
291
292 // Mark columns to be fixed and rows that will be covered by them
293 MarkNewFixingInMaps(columns_to_fix);
294
295 // Compute new core->full(returned) and full->core(modified original) row maps
296 CoreToFullElementMapVector new_c2f_row_map = MakeOrFillBothRowMaps();
297
298 // Create new model object applying the computed mappings
299 static_cast<Model&>(*this) = MakeNewCoreModel(new_c2f_row_map);
300
301 DCHECK(ValidateSubModel(*this));
302 DCHECK(absl::c_is_sorted(core2full_col_map_));
303 DCHECK(absl::c_is_sorted(core2full_row_map_));
304
305 return fixed_cost_ - old_fixed_cost;
306}
307
309 const std::vector<FullSubsetIndex>& columns_to_fix, const DualState&) {
310 ResetToIdentitySubModel();
311 std::vector<SubsetIndex> core_column_to_fix;
312 for (FullSubsetIndex full_j : columns_to_fix) {
313 core_column_to_fix.push_back(static_cast<SubsetIndex>(full_j));
314 }
315 FixMoreColumns(core_column_to_fix);
316}
317
318} // namespace operations_research::scp
util_intops::StrongIntRange< ElementIndex > ElementRange() const
void AddElementToLastSubset(BaseInt element)
const SparseRowView & rows() const
Row view of the set covering problem.
util_intops::StrongIntRange< SubsetIndex > SubsetRange() const
Access to the ranges of subsets and elements.
const SparseColumnView & columns() const
Column view of the set covering problem.
const SetCoverModel & base() const
virtual void ResetColumnFixing(const std::vector< FullSubsetIndex > &columns_to_fix, const DualState &state)
util_intops::StrongIntRange< ElementIndex > ElementRange() const
void SetFocus(const std::vector< FullSubsetIndex > &columns_focus)
CoreModel()=default
Empty initialization to facilitate delayed construction.
util_intops::StrongIntRange< SubsetIndex > SubsetRange() const
Access to the ranges of subsets and elements.
const SubsetCostVector & subset_costs() const
Vector of costs for each subset.
const SparseColumnView & columns() const
Member function relevant for the CFT inherited from Model.
virtual Cost FixMoreColumns(const std::vector< SubsetIndex > &columns_to_fix)
void SetFocus(const std::vector< FullSubsetIndex > &columns_focus)
SubModelView()=default
Empty initialization to facilitate delayed construction.
virtual void ResetColumnFixing(const std::vector< FullSubsetIndex > &columns_to_fix, const DualState &state)
virtual Cost FixMoreColumns(const std::vector< SubsetIndex > &columns_to_fix)
void assign(size_type n, const value_type &val)
void resize(size_type new_size)
void STLEraseAllFromSequenceIf(T *v, P pred)
Remove each element e in v satisfying pred(e).
Definition stl_util.h:104
bool ValidateSubModel(const SubModelT &model)
util_intops::StrongVector< ElementIndex, FullElementIndex > CoreToFullElementMapVector
util_intops::StrongVector< ElementIndex, bool > ElementBoolVector
Definition base_types.h:72
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
Definition base_types.h:32