Skip to content

Commit 0676998

Browse files
authored
Persistent basis_update_mpf_t in the Dual Simplex Phase 2 (#383)
This PR extract the LU factorisation from the `dual_phase2` to a separated method. It also added a new methods in the dual simplex to allow the `basis_update_mpf_t` to persist between calls. This allows the branch-and-bound to pass the `basis_update` from parent to child in the future. Authors: - Nicolas L. Guidotti (https://github.com/nguidotti) - Chris Maes (https://github.com/chris-maes) Approvers: - Chris Maes (https://github.com/chris-maes) - Akif ÇÖRDÜK (https://github.com/akifcorduk) URL: #383
1 parent c6bda57 commit 0676998

File tree

6 files changed

+313
-86
lines changed

6 files changed

+313
-86
lines changed

cpp/src/dual_simplex/basis_updates.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18+
#include <dual_simplex/basis_solves.hpp>
1819
#include <dual_simplex/basis_updates.hpp>
20+
#include <dual_simplex/initial_basis.hpp>
1921
#include <dual_simplex/triangle_solve.hpp>
2022

2123
#include <cmath>
@@ -2046,6 +2048,73 @@ void basis_update_mpf_t<i_t, f_t>::multiply_lu(csc_matrix_t<i_t, f_t>& out) cons
20462048
out.nz_max = B_nz;
20472049
}
20482050

2051+
template <typename i_t, typename f_t>
2052+
int basis_update_mpf_t<i_t, f_t>::refactor_basis(
2053+
const csc_matrix_t<i_t, f_t>& A,
2054+
const simplex_solver_settings_t<i_t, f_t>& settings,
2055+
std::vector<i_t>& basic_list,
2056+
std::vector<i_t>& nonbasic_list,
2057+
std::vector<variable_status_t>& vstatus)
2058+
{
2059+
std::vector<i_t> deficient;
2060+
std::vector<i_t> slacks_needed;
2061+
2062+
if (L0_.m != A.m) { resize(A.m); }
2063+
std::vector<i_t> q;
2064+
if (factorize_basis(A,
2065+
settings,
2066+
basic_list,
2067+
L0_,
2068+
U0_,
2069+
row_permutation_,
2070+
inverse_row_permutation_,
2071+
q,
2072+
deficient,
2073+
slacks_needed) == -1) {
2074+
settings.log.debug("Initial factorization failed\n");
2075+
basis_repair(A, settings, deficient, slacks_needed, basic_list, nonbasic_list, vstatus);
2076+
2077+
#ifdef CHECK_BASIS_REPAIR
2078+
const i_t m = A.m;
2079+
csc_matrix_t<i_t, f_t> B(m, m, 0);
2080+
form_b(A, basic_list, B);
2081+
for (i_t k = 0; k < deficient.size(); ++k) {
2082+
const i_t j = deficient[k];
2083+
const i_t col_start = B.col_start[j];
2084+
const i_t col_end = B.col_start[j + 1];
2085+
const i_t col_nz = col_end - col_start;
2086+
if (col_nz != 1) { settings.log.printf("Deficient column %d has %d nonzeros\n", j, col_nz); }
2087+
const i_t i = B.i[col_start];
2088+
if (i != slacks_needed[k]) {
2089+
settings.log.printf("Slack %d needed but found %d instead\n", slacks_needed[k], i);
2090+
}
2091+
}
2092+
#endif
2093+
2094+
if (factorize_basis(A,
2095+
settings,
2096+
basic_list,
2097+
L0_,
2098+
U0_,
2099+
row_permutation_,
2100+
inverse_row_permutation_,
2101+
q,
2102+
deficient,
2103+
slacks_needed) == -1) {
2104+
#ifdef CHECK_L_FACTOR
2105+
if (L0_.check_matrix() == -1) { settings.log.printf("Bad L after basis repair\n"); }
2106+
#endif
2107+
return deficient.size();
2108+
}
2109+
settings.log.debug("Basis repaired\n");
2110+
}
2111+
2112+
assert(q.size() == A.m);
2113+
reorder_basic_list(q, basic_list); // We no longer need q after reordering the basic list
2114+
reset();
2115+
return 0;
2116+
}
2117+
20492118
#ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE
20502119
template class basis_update_t<int, double>;
20512120
template class basis_update_mpf_t<int, double>;

cpp/src/dual_simplex/basis_updates.hpp

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#pragma once
1919

20+
#include <dual_simplex/initial_basis.hpp>
21+
#include <dual_simplex/simplex_solver_settings.hpp>
2022
#include <dual_simplex/sparse_matrix.hpp>
2123
#include <dual_simplex/sparse_vector.hpp>
2224
#include <dual_simplex/types.hpp>
@@ -176,6 +178,31 @@ class basis_update_t {
176178
template <typename i_t, typename f_t>
177179
class basis_update_mpf_t {
178180
public:
181+
basis_update_mpf_t(i_t n, const i_t refactor_frequency)
182+
: L0_(n, n, 1),
183+
U0_(n, n, 1),
184+
row_permutation_(n),
185+
inverse_row_permutation_(n),
186+
S_(n, 0, 0),
187+
xi_workspace_(2 * n, 0),
188+
x_workspace_(n, 0.0),
189+
U0_transpose_(1, 1, 1),
190+
L0_transpose_(1, 1, 1),
191+
refactor_frequency_(refactor_frequency),
192+
total_sparse_L_transpose_(0),
193+
total_dense_L_transpose_(0),
194+
total_sparse_L_(0),
195+
total_dense_L_(0),
196+
total_sparse_U_transpose_(0),
197+
total_dense_U_transpose_(0),
198+
total_sparse_U_(0),
199+
total_dense_U_(0),
200+
hypersparse_threshold_(0.05)
201+
{
202+
clear();
203+
reset_stats();
204+
}
205+
179206
basis_update_mpf_t(const csc_matrix_t<i_t, f_t>& Linit,
180207
const csc_matrix_t<i_t, f_t>& Uinit,
181208
const std::vector<i_t>& p,
@@ -185,8 +212,6 @@ class basis_update_mpf_t {
185212
row_permutation_(p),
186213
inverse_row_permutation_(p.size()),
187214
S_(Linit.m, 0, 0),
188-
col_permutation_(Linit.m),
189-
inverse_col_permutation_(Linit.m),
190215
xi_workspace_(2 * Linit.m, 0),
191216
x_workspace_(Linit.m, 0.0),
192217
U0_transpose_(1, 1, 1),
@@ -205,7 +230,7 @@ class basis_update_mpf_t {
205230
inverse_permutation(row_permutation_, inverse_row_permutation_);
206231
clear();
207232
compute_transposes();
208-
reset_stas();
233+
reset_stats();
209234
}
210235

211236
void print_stats() const
@@ -226,7 +251,7 @@ class basis_update_mpf_t {
226251
// clang-format on
227252
}
228253

229-
void reset_stas()
254+
void reset_stats()
230255
{
231256
num_calls_L_ = 0;
232257
num_calls_U_ = 0;
@@ -249,10 +274,33 @@ class basis_update_mpf_t {
249274
inverse_permutation(row_permutation_, inverse_row_permutation_);
250275
clear();
251276
compute_transposes();
252-
reset_stas();
277+
reset_stats();
278+
return 0;
279+
}
280+
281+
i_t reset()
282+
{
283+
clear();
284+
compute_transposes();
285+
reset_stats();
253286
return 0;
254287
}
255288

289+
void resize(i_t n)
290+
{
291+
L0_.resize(n, n, 1);
292+
U0_.resize(n, n, 1);
293+
row_permutation_.resize(n);
294+
inverse_row_permutation_.resize(n);
295+
S_.resize(n, 0, 0);
296+
xi_workspace_.resize(2 * n, 0);
297+
x_workspace_.resize(n, 0.0);
298+
U0_transpose_.resize(1, 1, 1);
299+
L0_transpose_.resize(1, 1, 1);
300+
clear();
301+
reset_stats();
302+
}
303+
256304
f_t estimate_solution_density(f_t rhs_nz, f_t sum, i_t& num_calls, bool& use_hypersparse) const
257305
{
258306
num_calls++;
@@ -332,13 +380,18 @@ class basis_update_mpf_t {
332380

333381
void multiply_lu(csc_matrix_t<i_t, f_t>& out) const;
334382

383+
// Compute L*U = A(p, basic_list)
384+
int refactor_basis(const csc_matrix_t<i_t, f_t>& A,
385+
const simplex_solver_settings_t<i_t, f_t>& settings,
386+
std::vector<i_t>& basic_list,
387+
std::vector<i_t>& nonbasic_list,
388+
std::vector<variable_status_t>& vstatus);
389+
335390
private:
336391
void clear()
337392
{
338393
pivot_indices_.clear();
339394
pivot_indices_.reserve(L0_.m);
340-
std::iota(col_permutation_.begin(), col_permutation_.end(), 0);
341-
std::iota(inverse_col_permutation_.begin(), inverse_col_permutation_.end(), 0);
342395
S_.col_start.resize(refactor_frequency_ + 1);
343396
S_.col_start[0] = 0;
344397
S_.col_start[1] = 0;
@@ -391,8 +444,6 @@ class basis_update_mpf_t {
391444
std::vector<i_t> pivot_indices_; // indicies for rank-1 updates to L
392445
csc_matrix_t<i_t, f_t> S_; // stores information about the rank-1 updates to L
393446
std::vector<f_t> mu_values_; // stores information about the rank-1 updates to L
394-
std::vector<i_t> col_permutation_; // symmetric permuation q used in U(q, q) represents Q
395-
std::vector<i_t> inverse_col_permutation_; // inverse permutation represents Q'
396447
mutable std::vector<i_t> xi_workspace_;
397448
mutable std::vector<f_t> x_workspace_;
398449
mutable csc_matrix_t<i_t, f_t> U0_transpose_; // Needed for sparse solves

0 commit comments

Comments
 (0)