21 #include "absl/container/flat_hash_map.h"
22 #include "absl/container/flat_hash_set.h"
23 #include "absl/strings/string_view.h"
24 #include "absl/types/span.h"
28 #include "ortools/math_opt/model.pb.h"
29 #include "ortools/math_opt/model_update.pb.h"
30 #include "ortools/math_opt/result.pb.h"
31 #include "ortools/math_opt/solution.pb.h"
32 #include "ortools/math_opt/sparse_containers.pb.h"
40 template <
typename K,
typename V>
41 std::vector<K> MapKeys(
const absl::flat_hash_map<K, V>& in_map) {
43 keys.reserve(in_map.size());
44 for (
const auto& key_pair : in_map) {
45 keys.push_back(key_pair.first);
50 template <
typename K,
typename V>
51 std::vector<K> SortedMapKeys(
const absl::flat_hash_map<K, V>& in_map) {
52 std::vector<K> keys = MapKeys(in_map);
53 std::sort(keys.begin(), keys.end());
58 std::vector<T> SortedSetKeys(
const absl::flat_hash_set<T>& in_set) {
60 keys.reserve(in_set.size());
61 for (
const auto& key : in_set) {
64 std::sort(keys.begin(), keys.end());
69 template <
typename IdType>
70 void AppendFromMapOrDefault(
const absl::Span<const IdType> ids,
71 const absl::flat_hash_map<IdType, double>& values,
72 SparseDoubleVectorProto& sparse_vector) {
73 for (
const IdType
id : ids) {
74 sparse_vector.add_ids(
id.
value());
80 template <
typename IdType,
typename IdIterable>
81 void AppendFromMapIfPresent(
const IdIterable& ids,
82 const absl::flat_hash_map<IdType, double>& values,
83 SparseDoubleVectorProto& sparse_vector) {
84 for (
const IdType
id : ids) {
86 if (double_value !=
nullptr) {
87 sparse_vector.add_ids(
id.
value());
88 sparse_vector.add_values(*double_value);
93 template <
typename IdType,
typename DataType>
94 void AppendFromMap(
const absl::flat_hash_set<IdType>& dirty_keys,
95 const absl::flat_hash_map<IdType, DataType>& values,
96 double DataType::*field,
97 SparseDoubleVectorProto& sparse_vector) {
98 for (
const IdType
id : SortedSetKeys(dirty_keys)) {
99 sparse_vector.add_ids(
id.
value());
100 sparse_vector.add_values(values.at(
id).*field);
104 template <
typename T>
105 absl::flat_hash_map<T, BasisStatus> SparseBasisVectorToMap(
106 const SparseBasisStatusVector& sparse_vector) {
107 absl::flat_hash_map<T, BasisStatus> result;
108 CHECK_EQ(sparse_vector.ids_size(), sparse_vector.values_size());
109 result.reserve(sparse_vector.ids_size());
120 const bool is_integer,
121 const absl::string_view
name) {
122 const VariableId result = next_variable_id_++;
123 VariableData& var_data = variables_[result];
126 var_data.is_integer = is_integer;
127 var_data.name =
name;
128 if (!lazy_matrix_columns_.empty()) {
135 CHECK(variables_.contains(
id));
136 EnsureLazyMatrixColumns();
137 EnsureLazyMatrixRows();
138 linear_objective_.erase(
id);
139 variables_.erase(
id);
140 if (
id < variables_checkpoint_) {
141 dirty_variable_deletes_.insert(
id);
142 dirty_variable_lower_bounds_.erase(
id);
143 dirty_variable_upper_bounds_.erase(
id);
144 dirty_variable_is_integer_.erase(
id);
145 dirty_linear_objective_coefficients_.erase(
id);
147 for (
const LinearConstraintId related_constraint :
148 lazy_matrix_columns_.at(
id)) {
149 CHECK_GT(lazy_matrix_rows_.at(related_constraint).erase(
id), 0);
150 CHECK_GT(linear_constraint_matrix_.erase({related_constraint, id}), 0);
151 if (
id < variables_checkpoint_ &&
152 related_constraint < linear_constraints_checkpoint_) {
153 dirty_linear_constraint_matrix_keys_.erase({related_constraint,
id});
156 CHECK_GT(lazy_matrix_columns_.erase(
id), 0);
160 return MapKeys(variables_);
164 return SortedMapKeys(variables_);
169 const absl::string_view
name) {
170 const LinearConstraintId result = next_linear_constraint_id_++;
171 LinearConstraintData& lin_con_data = linear_constraints_[result];
174 lin_con_data.name =
name;
175 if (!lazy_matrix_rows_.empty()) {
182 CHECK(linear_constraints_.contains(
id));
183 EnsureLazyMatrixColumns();
184 EnsureLazyMatrixRows();
185 linear_constraints_.erase(
id);
186 if (
id < linear_constraints_checkpoint_) {
187 dirty_linear_constraint_deletes_.insert(
id);
188 dirty_linear_constraint_lower_bounds_.erase(
id);
189 dirty_linear_constraint_upper_bounds_.erase(
id);
191 for (
const VariableId related_variable : lazy_matrix_rows_.at(
id)) {
192 CHECK_GT(lazy_matrix_columns_.at(related_variable).erase(
id), 0);
193 CHECK_GT(linear_constraint_matrix_.erase({id, related_variable}), 0);
194 if (
id < linear_constraints_checkpoint_ &&
195 related_variable < variables_checkpoint_) {
196 dirty_linear_constraint_matrix_keys_.erase({id, related_variable});
199 CHECK_GT(lazy_matrix_rows_.erase(
id), 0);
203 return MapKeys(linear_constraints_);
207 return SortedMapKeys(linear_constraints_);
212 return SortedMapKeys(linear_objective_);
215 void IndexedModel::AppendVariable(
const VariableId
id,
216 VariablesProto& variables_proto)
const {
217 const VariableData& var_data = variables_.at(
id);
218 variables_proto.add_ids(
id.
value());
219 variables_proto.add_lower_bounds(var_data.lower_bound);
220 variables_proto.add_upper_bounds(var_data.upper_bound);
221 variables_proto.add_integers(var_data.is_integer);
222 variables_proto.add_names(var_data.name);
225 void IndexedModel::AppendLinearConstraint(
226 const LinearConstraintId
id,
227 LinearConstraintsProto& linear_constraints_proto)
const {
228 const LinearConstraintData& con_impl = linear_constraints_.at(
id);
229 linear_constraints_proto.add_ids(
id.
value());
230 linear_constraints_proto.add_lower_bounds(con_impl.lower_bound);
231 linear_constraints_proto.add_upper_bounds(con_impl.upper_bound);
232 linear_constraints_proto.add_names(con_impl.name);
235 void IndexedModel::ExportLinearConstraintMatrix(
236 const absl::Span<
const std::pair<LinearConstraintId, VariableId>> entries,
237 SparseDoubleMatrixProto& matrix)
const {
238 matrix.mutable_row_ids()->Reserve(entries.size());
239 matrix.mutable_column_ids()->Reserve(entries.size());
240 matrix.mutable_coefficients()->Reserve(entries.size());
241 for (
const auto [constraint_id, variable_id] : entries) {
242 matrix.add_row_ids(constraint_id.value());
243 matrix.add_column_ids(variable_id.value());
245 {constraint_id, variable_id}));
251 result.set_name(name_);
253 for (
const VariableId variable : SortedMapKeys(variables_)) {
254 AppendVariable(variable, *result.mutable_variables());
258 result.mutable_objective()->set_maximize(is_maximize_);
259 result.mutable_objective()->set_offset(objective_offset_);
260 AppendFromMapOrDefault<VariableId>(
261 SortedMapKeys(linear_objective_), linear_objective_,
262 *result.mutable_objective()->mutable_linear_coefficients());
265 for (
const LinearConstraintId con : SortedMapKeys(linear_constraints_)) {
266 AppendLinearConstraint(con, *result.mutable_linear_constraints());
270 ExportLinearConstraintMatrix(SortedMapKeys(linear_constraint_matrix_),
271 *result.mutable_linear_constraint_matrix());
276 ModelUpdateProto result;
280 EnsureLazyMatrixRows();
281 EnsureLazyMatrixColumns();
284 for (
const VariableId del_var : SortedSetKeys(dirty_variable_deletes_)) {
285 result.add_deleted_variable_ids(del_var.value());
287 for (
const LinearConstraintId del_lin_con :
288 SortedSetKeys(dirty_linear_constraint_deletes_)) {
289 result.add_deleted_linear_constraint_ids(del_lin_con.value());
293 auto var_updates = result.mutable_variable_updates();
294 AppendFromMap(dirty_variable_lower_bounds_, variables_,
296 *var_updates->mutable_lower_bounds());
297 AppendFromMap(dirty_variable_upper_bounds_, variables_,
299 *var_updates->mutable_upper_bounds());
301 for (
const VariableId integer_var :
302 SortedSetKeys(dirty_variable_is_integer_)) {
303 var_updates->mutable_integers()->add_ids(integer_var.value());
304 var_updates->mutable_integers()->add_values(
305 variables_.at(integer_var).is_integer);
307 for (VariableId new_id = variables_checkpoint_; new_id < next_variable_id_;
309 if (variables_.contains(new_id)) {
310 AppendVariable(new_id, *result.mutable_new_variables());
315 auto obj_updates = result.mutable_objective_updates();
316 if (dirty_objective_direction_) {
317 obj_updates->set_direction_update(is_maximize_);
319 if (dirty_objective_offset_) {
320 obj_updates->set_offset_update(objective_offset_);
322 AppendFromMapOrDefault<VariableId>(
323 SortedSetKeys(dirty_linear_objective_coefficients_), linear_objective_,
324 *obj_updates->mutable_linear_coefficients());
329 for (VariableId var_id = variables_checkpoint_; var_id < next_variable_id_;
331 const double*
const double_value =
333 if (double_value !=
nullptr) {
334 obj_updates->mutable_linear_coefficients()->add_ids(var_id.value());
335 obj_updates->mutable_linear_coefficients()->add_values(*double_value);
340 auto lin_con_updates = result.mutable_linear_constraint_updates();
341 AppendFromMap(dirty_linear_constraint_lower_bounds_, linear_constraints_,
343 *lin_con_updates->mutable_lower_bounds());
344 AppendFromMap(dirty_linear_constraint_upper_bounds_, linear_constraints_,
346 *lin_con_updates->mutable_upper_bounds());
348 for (LinearConstraintId new_id = linear_constraints_checkpoint_;
349 new_id < next_linear_constraint_id_; ++new_id) {
350 if (linear_constraints_.contains(new_id)) {
351 AppendLinearConstraint(new_id, *result.mutable_new_linear_constraints());
356 std::vector<std::pair<LinearConstraintId, VariableId>>
357 constraint_matrix_updates(dirty_linear_constraint_matrix_keys_.begin(),
358 dirty_linear_constraint_matrix_keys_.end());
359 for (VariableId new_var = variables_checkpoint_; new_var < next_variable_id_;
361 if (variables_.contains(new_var)) {
362 for (
const LinearConstraintId lin_con :
363 lazy_matrix_columns_.at(new_var)) {
364 constraint_matrix_updates.emplace_back(lin_con, new_var);
368 for (LinearConstraintId new_lin_con = linear_constraints_checkpoint_;
369 new_lin_con < next_linear_constraint_id_; ++new_lin_con) {
370 if (linear_constraints_.contains(new_lin_con)) {
371 for (
const VariableId
var : lazy_matrix_rows_.at(new_lin_con)) {
373 if (
var < variables_checkpoint_) {
374 constraint_matrix_updates.emplace_back(new_lin_con,
var);
379 std::sort(constraint_matrix_updates.begin(), constraint_matrix_updates.end());
380 ExportLinearConstraintMatrix(
381 constraint_matrix_updates,
382 *result.mutable_linear_constraint_matrix_updates());
386 void IndexedModel::EnsureLazyMatrixColumns() {
387 if (lazy_matrix_columns_.empty()) {
388 for (
const auto& var_pair : variables_) {
389 lazy_matrix_columns_.insert({var_pair.first, {}});
391 for (
const auto& mat_entry : linear_constraint_matrix_) {
392 lazy_matrix_columns_.at(mat_entry.first.second)
393 .insert(mat_entry.first.first);
398 void IndexedModel::EnsureLazyMatrixRows() {
399 if (lazy_matrix_rows_.empty()) {
400 for (
const auto& lin_con_pair : linear_constraints_) {
401 lazy_matrix_rows_.insert({lin_con_pair.first, {}});
403 for (
const auto& mat_entry : linear_constraint_matrix_) {
404 lazy_matrix_rows_.at(mat_entry.first.first)
405 .insert(mat_entry.first.second);
411 variables_checkpoint_ = next_variable_id_;
412 linear_constraints_checkpoint_ = next_linear_constraint_id_;
413 dirty_objective_direction_ =
false;
414 dirty_objective_offset_ =
false;
416 dirty_variable_deletes_.clear();
417 dirty_variable_lower_bounds_.clear();
418 dirty_variable_upper_bounds_.clear();
419 dirty_variable_is_integer_.clear();
421 dirty_linear_objective_coefficients_.clear();
423 dirty_linear_constraint_deletes_.clear();
424 dirty_linear_constraint_lower_bounds_.clear();
425 dirty_linear_constraint_upper_bounds_.clear();
426 dirty_linear_constraint_matrix_keys_.clear();
430 const SolveResultProto& solve_result) {
432 for (
const PrimalSolutionProto& primal_solution :
433 solve_result.primal_solutions()) {
436 MakeView(primal_solution.variable_values()).as_map<VariableId>();
440 for (
const PrimalRayProto& primal_ray : solve_result.primal_rays()) {
443 MakeView(primal_ray.variable_values()).as_map<VariableId>();
446 for (
const DualSolutionProto& dual_solution : solve_result.dual_solutions()) {
449 MakeView(dual_solution.reduced_costs()).as_map<VariableId>();
451 MakeView(dual_solution.dual_values()).as_map<LinearConstraintId>();
455 for (
const DualRayProto& dual_ray : solve_result.dual_rays()) {
459 MakeView(dual_ray.dual_values()).as_map<LinearConstraintId>();
460 solutions.
dual_rays.push_back(std::move(dr));
462 for (
const BasisProto& basis : solve_result.basis()) {
465 SparseBasisVectorToMap<LinearConstraintId>(basis.constraint_status());
467 SparseBasisVectorToMap<VariableId>(basis.variable_status());
468 solutions.
basis.push_back(std::move(indexed_basis));
#define CHECK_EQ(val1, val2)
#define CHECK_GT(val1, val2)
std::vector< LinearConstraintId > linear_constraints() const
std::vector< VariableId > SortedVariables() const
void DeleteVariable(VariableId id)
std::vector< VariableId > SortedLinearObjectiveNonzeroVariables() const
void DeleteLinearConstraint(LinearConstraintId id)
std::vector< VariableId > variables() const
VariableId AddVariable(absl::string_view name="")
ModelProto ExportModel() const
ModelUpdateProto ExportModelUpdate()
const std::string & name() const
LinearConstraintId AddLinearConstraint(absl::string_view name="")
std::vector< LinearConstraintId > SortedLinearConstraints() const
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
IndexedSolutions IndexedSolutionsFromProto(const SolveResultProto &solve_result)
Collection of objects used to extend the Constraint Solver library.
absl::flat_hash_map< VariableId, BasisStatus > variable_status
absl::flat_hash_map< LinearConstraintId, BasisStatus > constraint_status
absl::flat_hash_map< VariableId, double > reduced_costs
absl::flat_hash_map< LinearConstraintId, double > dual_values
absl::flat_hash_map< VariableId, double > reduced_costs
absl::flat_hash_map< LinearConstraintId, double > dual_values
absl::flat_hash_map< VariableId, double > variable_values
absl::flat_hash_map< VariableId, double > variable_values
std::vector< IndexedDualSolution > dual_solutions
std::vector< IndexedPrimalSolution > primal_solutions
std::vector< IndexedPrimalRay > primal_rays
std::vector< IndexedBasis > basis
std::vector< IndexedDualRay > dual_rays