Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
lp_decomposer.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 <vector>
18
19#include "absl/log/check.h"
20#include "absl/synchronization/mutex.h"
21#include "absl/types/span.h"
27#include "ortools/util/bitset.h"
28
29namespace operations_research {
30namespace glop {
31
32//------------------------------------------------------------------------------
33// LPDecomposer
34//------------------------------------------------------------------------------
36 : original_problem_(nullptr), clusters_(), mutex_() {}
38void LPDecomposer::Decompose(const LinearProgram* linear_problem) {
39 absl::MutexLock mutex_lock(&mutex_);
40 original_problem_ = linear_problem;
41 clusters_.clear();
42
43 const SparseMatrix& transposed_matrix =
44 original_problem_->GetTransposeSparseMatrix();
45 MergingPartition partition(original_problem_->num_variables().value());
46
47 // Iterate on all constraints, and merge all variables of each constraint.
48 const ColIndex num_ct = RowToColIndex(original_problem_->num_constraints());
49 for (ColIndex ct(0); ct < num_ct; ++ct) {
50 const SparseColumn& sparse_constraint = transposed_matrix.column(ct);
51 if (sparse_constraint.num_entries() > 1) {
52 const RowIndex first_row = sparse_constraint.GetFirstRow();
53 for (EntryIndex e(1); e < sparse_constraint.num_entries(); ++e) {
54 partition.MergePartsOf(first_row.value(),
55 sparse_constraint.EntryRow(e).value());
56 }
57 }
58 }
59
60 std::vector<int> classes;
61 const int num_classes = partition.FillEquivalenceClasses(&classes);
62 clusters_.resize(num_classes);
63 for (int i = 0; i < classes.size(); ++i) {
64 clusters_[classes[i]].push_back(ColIndex(i));
65 }
66 for (int i = 0; i < num_classes; ++i) {
67 std::sort(clusters_[i].begin(), clusters_[i].end());
68 }
69}
70
71int LPDecomposer::GetNumberOfProblems() const {
72 absl::MutexLock mutex_lock(&mutex_);
73 return clusters_.size();
74}
75
76const LinearProgram& LPDecomposer::original_problem() const {
77 absl::MutexLock mutex_lock(&mutex_);
78 return *original_problem_;
79}
80
81void LPDecomposer::ExtractLocalProblem(int problem_index, LinearProgram* lp) {
82 CHECK(lp != nullptr);
83 CHECK_GE(problem_index, 0);
84 CHECK_LT(problem_index, clusters_.size());
85
86 lp->Clear();
87
88 absl::MutexLock mutex_lock(&mutex_);
89 const std::vector<ColIndex>& cluster = clusters_[problem_index];
91 original_problem_->num_variables(), kInvalidCol);
92 SparseBitset<RowIndex> constraints_to_use(
93 original_problem_->num_constraints());
94 lp->SetMaximizationProblem(original_problem_->IsMaximizationProblem());
95
96 // Create variables and get all constraints of the cluster.
97 const SparseMatrix& original_matrix = original_problem_->GetSparseMatrix();
98 const SparseMatrix& transposed_matrix =
99 original_problem_->GetTransposeSparseMatrix();
100 for (int i = 0; i < cluster.size(); ++i) {
101 const ColIndex global_col = cluster[i];
102 const ColIndex local_col = lp->CreateNewVariable();
103 CHECK_EQ(local_col, ColIndex(i));
104 CHECK(global_to_local[global_col] == kInvalidCol ||
105 global_to_local[global_col] == local_col)
106 << "If the mapping is already assigned it has to be the same.";
107 global_to_local[global_col] = local_col;
108
109 lp->SetVariableName(local_col,
110 original_problem_->GetVariableName(global_col));
111 lp->SetVariableType(local_col,
112 original_problem_->GetVariableType(global_col));
114 local_col, original_problem_->variable_lower_bounds()[global_col],
115 original_problem_->variable_upper_bounds()[global_col]);
117 local_col, original_problem_->objective_coefficients()[global_col]);
118
119 for (const SparseColumn::Entry e : original_matrix.column(global_col)) {
120 constraints_to_use.Set(e.row());
121 }
122 }
123 // Create the constraints.
124 for (const RowIndex global_row :
125 constraints_to_use.PositionsSetAtLeastOnce()) {
126 const RowIndex local_row = lp->CreateNewConstraint();
127 lp->SetConstraintName(local_row,
128 original_problem_->GetConstraintName(global_row));
130 local_row, original_problem_->constraint_lower_bounds()[global_row],
131 original_problem_->constraint_upper_bounds()[global_row]);
132
133 for (const SparseColumn::Entry e :
134 transposed_matrix.column(RowToColIndex(global_row))) {
135 const ColIndex global_col = RowToColIndex(e.row());
136 const ColIndex local_col = global_to_local[global_col];
137 lp->SetCoefficient(local_row, local_col, e.coefficient());
138 }
139 }
140}
141
142DenseRow LPDecomposer::AggregateAssignments(
143 absl::Span<const DenseRow> assignments) const {
144 CHECK_EQ(assignments.size(), clusters_.size());
145
146 absl::MutexLock mutex_lock(&mutex_);
147 DenseRow global_assignment(original_problem_->num_variables(),
148 Fractional(0.0));
149 for (int problem = 0; problem < assignments.size(); ++problem) {
150 const DenseRow& local_assignment = assignments[problem];
151 const std::vector<ColIndex>& cluster = clusters_[problem];
152 for (int i = 0; i < local_assignment.size(); ++i) {
153 const ColIndex global_col = cluster[i];
154 global_assignment[global_col] = local_assignment[ColIndex(i)];
155 }
156 }
157 return global_assignment;
158}
159
160DenseRow LPDecomposer::ExtractLocalAssignment(int problem_index,
161 const DenseRow& assignment) {
162 CHECK_GE(problem_index, 0);
163 CHECK_LT(problem_index, clusters_.size());
164 CHECK_EQ(assignment.size(), original_problem_->num_variables());
165
166 absl::MutexLock mutex_lock(&mutex_);
167 const std::vector<ColIndex>& cluster = clusters_[problem_index];
168 DenseRow local_assignment(ColIndex(cluster.size()), Fractional(0.0));
169 for (int i = 0; i < cluster.size(); ++i) {
170 const ColIndex global_col = cluster[i];
171 local_assignment[ColIndex(i)] = assignment[global_col];
172 }
173 return local_assignment;
174}
175
176} // namespace glop
177} // namespace operations_research
void Clear()
Clears, i.e. reset the object to its initial value.
Definition lp_data.cc:143
void SetConstraintName(RowIndex row, absl::string_view name)
Definition lp_data.cc:254
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition lp_data.cc:335
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition lp_data.cc:258
void SetVariableType(ColIndex col, VariableType type)
Set the type of the variable.
Definition lp_data.cc:245
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition lp_data.cc:318
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Defines the coefficient for col / row.
Definition lp_data.cc:326
void SetVariableName(ColIndex col, absl::string_view name)
Definition lp_data.cc:241
void SetMaximizationProblem(bool maximize)
Definition lp_data.cc:352
RowIndex EntryRow(EntryIndex i) const
Use a separate API to get the row and coefficient of entry #i.
const SparseColumn & column(ColIndex col) const
Access the underlying sparse columns.
Definition sparse.h:194
EntryIndex num_entries() const
Note this method can only be used when the vector has no duplicates.
const Constraint * ct
ColIndex RowToColIndex(RowIndex row)
Get the ColIndex corresponding to the column # row.
Definition lp_types.h:54
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
Definition lp_types.h:353
In SWIG mode, we don't want anything besides these top-level includes.
std::optional< int64_t > end