Skip to content

Commit 4fb4298

Browse files
committed
Merge branch 'hipo-dev' into hipo
2 parents a684d57 + afb55d8 commit 4fb4298

File tree

13 files changed

+106
-144
lines changed

13 files changed

+106
-144
lines changed

highs/ipm/hipo/factorhighs/FactorHiGHS.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,15 @@ Int FHsolver::analyse(Symbolic& S, const std::vector<Int>& rows,
4141
return an_obj.run(S);
4242
}
4343

44-
Int FHsolver::factorise(Numeric& N, const Symbolic& S,
45-
const std::vector<Int>& rows,
44+
Int FHsolver::factorise(const Symbolic& S, const std::vector<Int>& rows,
4645
const std::vector<Int>& ptr,
4746
const std::vector<double>& vals) {
48-
Factorise fact_obj(S, rows, ptr, vals, regul_, log_, data_);
49-
return fact_obj.run(N);
47+
Factorise fact_obj(S, rows, ptr, vals, regul_, log_, data_, sn_columns_);
48+
return fact_obj.run(N_);
49+
}
50+
51+
Int FHsolver::solve(std::vector<double>& x, Int* solve_count, double* omega) {
52+
return N_.solve(x, solve_count, omega);
5053
}
5154

5255
} // namespace hipo

highs/ipm/hipo/factorhighs/FactorHiGHS.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ It is stored using three arrays:
2222
2323
The direct solver uses the following objects:
2424
- Symbolic, to store the symbolic factorization;
25-
- Numeric, to store the numeric factorization;
2625
- FHsolver, to perform analyse and factorise phases.
2726
2827
Define a vector signs that contains the expected sign of each pivot (1 or -1).
@@ -32,13 +31,10 @@ M^{-1} * rhs.
3231
Then, the factorization is performed as follows.
3332
3433
Symbolic S;
35-
Numeric N(S);
36-
3734
FHsolver FH;
3835
FH.analyse(S, rows, ptr, signs);
39-
FH.factorise(N, S, rows, ptr, val);
40-
41-
N.solve(rhs);
36+
FH.factorise(S, rows, ptr, val);
37+
FH.solve(x);
4238
4339
Printing to screen is achieved using the interface in auxiliary/Log.h. Pass an
4440
object of type Log for normal printing:
@@ -64,10 +60,16 @@ class FHsolver {
6460
const Log* log_;
6561
DataCollector data_;
6662
Regul regul_;
63+
Numeric N_;
6764

6865
const Int nb_; // block size
6966
static const Int default_nb_ = 128;
7067

68+
// Columns of factorisation, stored by supernode.
69+
// This memory is allocated the first time that it is used. Subsequent
70+
// factorisations reuse the same memory.
71+
std::vector<std::vector<double>> sn_columns_;
72+
7173
public:
7274
// Create object and initialise DataCollector
7375
FHsolver(const Log* log = nullptr, Int block_size = default_nb_);
@@ -85,9 +87,18 @@ class FHsolver {
8587
// numerical factorisation in object N. Matrix is moved into the object, so
8688
// rows, ptr, vals are invalid afterwards.
8789
// See ReturnValues.h for errors.
88-
Int factorise(Numeric& N, const Symbolic& S, const std::vector<Int>& rows,
90+
Int factorise(const Symbolic& S, const std::vector<Int>& rows,
8991
const std::vector<Int>& ptr, const std::vector<double>& vals);
9092

93+
// Perform solve phase with rhs given by x, which is overwritten with the
94+
// solution. solve_count returns the number of solves performed during the
95+
// phase (including refinement). omega returns the final residual after
96+
// refinement.
97+
// For now refinement is performed automatically as part of solve,
98+
// this will change in the future.
99+
Int solve(std::vector<double>& x, Int* solve_count = nullptr,
100+
double* omega = nullptr);
101+
91102
// If multiple factorisation are performed, call newIter() before each
92103
// factorisation. This is used only to collect data for debugging, if
93104
// expensive data collection is turned on at compile time.

highs/ipm/hipo/factorhighs/Factorise.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ namespace hipo {
1818
Factorise::Factorise(const Symbolic& S, const std::vector<Int>& rowsA,
1919
const std::vector<Int>& ptrA,
2020
const std::vector<double>& valA, const Regul& regul,
21-
const Log* log, DataCollector& data)
22-
: S_{S}, regul_{regul}, log_{log}, data_{data} {
21+
const Log* log, DataCollector& data,
22+
std::vector<std::vector<double>>& sn_columns)
23+
: S_{S}, regul_{regul}, log_{log}, data_{data}, sn_columns_{sn_columns} {
2324
// Input the symmetric matrix to be factorised in CSC format and the symbolic
2425
// factorisation coming from Analyse.
2526
// Only the lower triangular part of the matrix is used.
@@ -200,7 +201,7 @@ void Factorise::processSupernode(Int sn) {
200201
// initialise the format handler
201202
// this also allocates space for the frontal matrix and schur complement
202203
std::unique_ptr<FormatHandler> FH(
203-
new HybridHybridFormatHandler(S_, sn, regul_, data_));
204+
new HybridHybridFormatHandler(S_, sn, regul_, data_, sn_columns_[sn]));
204205

205206
#if HIPO_TIMING_LEVEL >= 2
206207
data_.sumTime(kTimeFactorisePrepare, clock.stop());
@@ -343,8 +344,8 @@ void Factorise::processSupernode(Int sn) {
343344
FH->extremeEntries();
344345

345346
// terminate the format handler
346-
FH->terminate(sn_columns_[sn], schur_contribution_[sn], total_reg_,
347-
swaps_[sn], pivot_2x2_[sn]);
347+
FH->terminate(schur_contribution_[sn], total_reg_, swaps_[sn],
348+
pivot_2x2_[sn]);
348349
#if HIPO_TIMING_LEVEL >= 2
349350
data_.sumTime(kTimeFactoriseTerminate, clock.stop());
350351
#endif
@@ -357,12 +358,15 @@ bool Factorise::run(Numeric& num) {
357358

358359
total_reg_.assign(n_, 0.0);
359360

360-
// allocate space for list of generated elements and columns of L
361+
// allocate space
361362
schur_contribution_.resize(S_.sn());
362-
sn_columns_.resize(S_.sn());
363363
swaps_.resize(S_.sn());
364364
pivot_2x2_.resize(S_.sn());
365365

366+
// This should actually allocate only the first time, then sn_columns_ reuses
367+
// the memory of previous factorisations.
368+
sn_columns_.resize(S_.sn());
369+
366370
if (S_.parTree()) {
367371
Int spawned_roots{};
368372
// spawn tasks for root supernodes
@@ -387,7 +391,8 @@ bool Factorise::run(Numeric& num) {
387391
if (flag_stop_) return true;
388392

389393
// move factorisation to numerical object
390-
num.sn_columns_ = std::move(sn_columns_);
394+
num.S_ = &S_;
395+
num.sn_columns_ = &sn_columns_;
391396
num.total_reg_ = std::move(total_reg_);
392397
num.swaps_ = std::move(swaps_);
393398
num.pivot_2x2_ = std::move(pivot_2x2_);

highs/ipm/hipo/factorhighs/Factorise.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class Factorise {
3434
std::vector<std::vector<double>> schur_contribution_{};
3535

3636
// columns of L, stored as dense supernodes
37-
std::vector<std::vector<double>> sn_columns_{};
37+
// This memory is managed outside of Factorise, so that it can be reused for
38+
// all ipm iterations.
39+
std::vector<std::vector<double>>& sn_columns_;
3840

3941
// swaps of columns for each supernode, ordered locally within a block
4042
std::vector<std::vector<Int>> swaps_{};
@@ -71,7 +73,8 @@ class Factorise {
7173
public:
7274
Factorise(const Symbolic& S, const std::vector<Int>& rowsA,
7375
const std::vector<Int>& ptrA, const std::vector<double>& valA,
74-
const Regul& regul, const Log* log, DataCollector& data);
76+
const Regul& regul, const Log* log, DataCollector& data,
77+
std::vector<std::vector<double>>& sn_columns);
7578

7679
bool run(Numeric& num);
7780
};

highs/ipm/hipo/factorhighs/FormatHandler.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@
66

77
namespace hipo {
88

9-
FormatHandler::FormatHandler(const Symbolic& S, Int sn, const Regul& regul)
9+
FormatHandler::FormatHandler(const Symbolic& S, Int sn, const Regul& regul,
10+
std::vector<double>& frontal)
1011
: S_{&S},
1112
regul_{regul},
1213
sn_{sn},
1314
nb_{S_->blockSize()},
1415
sn_size_{S_->snStart(sn_ + 1) - S_->snStart(sn_)},
1516
ldf_{S_->ptr(sn_ + 1) - S_->ptr(sn_)},
16-
ldc_{ldf_ - sn_size_} {
17+
ldc_{ldf_ - sn_size_},
18+
frontal_{frontal} {
1719
local_reg_.resize(sn_size_);
1820
swaps_.resize(sn_size_);
1921
pivot_2x2_.resize(sn_size_);
2022
}
2123

22-
void FormatHandler::terminate(std::vector<double>& frontal,
23-
std::vector<double>& clique,
24+
void FormatHandler::terminate(std::vector<double>& clique,
2425
std::vector<double>& total_reg,
2526
std::vector<Int>& swaps,
2627
std::vector<double>& pivot_2x2) {
@@ -29,7 +30,6 @@ void FormatHandler::terminate(std::vector<double>& frontal,
2930
// accessed only here, while a local copy is used for the assembly and dense
3031
// factorisation. This should avoid the problem of false sharing.
3132

32-
frontal = std::move(frontal_);
3333
clique = std::move(clique_);
3434
swaps = std::move(swaps_);
3535
pivot_2x2 = std::move(pivot_2x2_);

highs/ipm/hipo/factorhighs/FormatHandler.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,17 @@ class FormatHandler {
4848
const Int ldc_{};
4949

5050
// local copies to be moved at the end
51-
std::vector<double> frontal_{};
51+
std::vector<double>& frontal_;
5252
std::vector<double> clique_{};
5353
std::vector<double> local_reg_{};
5454
std::vector<Int> swaps_{};
5555
std::vector<double> pivot_2x2_{};
5656

5757
public:
58-
FormatHandler(const Symbolic& S, Int sn, const Regul& regul);
59-
void terminate(std::vector<double>& frontal, std::vector<double>& clique,
60-
std::vector<double>& total_reg, std::vector<Int>& swaps,
61-
std::vector<double>& pivot_2x2);
58+
FormatHandler(const Symbolic& S, Int sn, const Regul& regul,
59+
std::vector<double>& frontal);
60+
void terminate(std::vector<double>& clique, std::vector<double>& total_reg,
61+
std::vector<Int>& swaps, std::vector<double>& pivot_2x2);
6262

6363
// avoid copies
6464
FormatHandler(const FormatHandler&) = delete;

highs/ipm/hipo/factorhighs/HybridHybridFormatHandler.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
namespace hipo {
99

10-
HybridHybridFormatHandler::HybridHybridFormatHandler(const Symbolic& S, Int sn,
11-
const Regul& regul,
12-
DataCollector& data)
13-
: FormatHandler(S, sn, regul), data_{data} {
10+
HybridHybridFormatHandler::HybridHybridFormatHandler(
11+
const Symbolic& S, Int sn, const Regul& regul, DataCollector& data,
12+
std::vector<double>& frontal)
13+
: FormatHandler(S, sn, regul, frontal), data_{data} {
1414
// initialise frontal and clique
1515
initFrontal();
1616
initClique();
@@ -20,8 +20,11 @@ void HybridHybridFormatHandler::initFrontal() {
2020
const Int n_blocks = (sn_size_ - 1) / nb_ + 1;
2121
diag_start_.resize(n_blocks);
2222
Int frontal_size = getDiagStart(ldf_, sn_size_, nb_, n_blocks, diag_start_);
23-
frontal_.resize(frontal_size + extra_space);
23+
frontal_.assign(frontal_size + extra_space, 0.0);
2424
// NB: the plus 10 is not needed, but it avoids weird problems later on.
25+
26+
// frontal_ is actually allocated just the first time, then the memory is
27+
// reused from the previous factorisations and just initialised.
2528
}
2629

2730
void HybridHybridFormatHandler::initClique() {

highs/ipm/hipo/factorhighs/HybridHybridFormatHandler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class HybridHybridFormatHandler : public FormatHandler {
2323

2424
public:
2525
HybridHybridFormatHandler(const Symbolic& S, Int sn, const Regul& regul,
26-
DataCollector& data);
26+
DataCollector& data, std::vector<double>& frontal);
2727
};
2828

2929
} // namespace hipo

highs/ipm/hipo/factorhighs/Numeric.cpp

Lines changed: 14 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "DataCollector.h"
44
#include "FactorHiGHSSettings.h"
55
#include "HybridSolveHandler.h"
6+
#include "ReturnValues.h"
67
#include "Timing.h"
78
#include "ipm/hipo/auxiliary/Auxiliary.h"
89
#include "ipm/hipo/auxiliary/Log.h"
@@ -12,14 +13,15 @@
1213

1314
namespace hipo {
1415

15-
Numeric::Numeric(const Symbolic& S) : S_{S} {
16-
// initialise solve handler
17-
SH_.reset(new HybridSolveHandler(S_, sn_columns_, swaps_, pivot_2x2_));
18-
}
19-
20-
std::pair<Int, double> Numeric::solve(std::vector<double>& x) const {
16+
Int Numeric::solve(std::vector<double>& x, Int* solve_count,
17+
double* omega) const {
2118
// Return the number of solves performed
2219

20+
if (!sn_columns_ || !S_) return kRetInvalidPointer;
21+
22+
// initialise solve handler
23+
SH_.reset(new HybridSolveHandler(*S_, *sn_columns_, swaps_, pivot_2x2_));
24+
2325
SH_->setData(data_);
2426

2527
#if HIPO_TIMING_LEVEL >= 1
@@ -31,7 +33,7 @@ std::pair<Int, double> Numeric::solve(std::vector<double>& x) const {
3133
#endif
3234

3335
// permute rhs
34-
permuteVectorInverse(x, S_.iperm());
36+
permuteVectorInverse(x, S_->iperm());
3537

3638
// make a copy of permuted rhs, for refinement
3739
const std::vector<double> rhs(x);
@@ -58,7 +60,7 @@ std::pair<Int, double> Numeric::solve(std::vector<double>& x) const {
5860
#endif
5961

6062
// unpermute solution
61-
permuteVector(x, S_.iperm());
63+
permuteVector(x, S_->iperm());
6264

6365
#if HIPO_TIMING_LEVEL >= 2
6466
if (data_) data_->sumTime(kTimeSolvePrepare, clock_fine.stop());
@@ -68,7 +70,10 @@ std::pair<Int, double> Numeric::solve(std::vector<double>& x) const {
6870
if (data_) data_->sumTime(kTimeSolve, clock.stop());
6971
#endif
7072

71-
return {refine_data.first + 1, refine_data.second};
73+
if (solve_count) *solve_count = refine_data.first + 1;
74+
if (omega) *omega = refine_data.second;
75+
76+
return kRetOk;
7277
}
7378

7479
std::vector<double> Numeric::residual(const std::vector<double>& rhs,
@@ -229,73 +234,4 @@ double Numeric::computeOmega(const std::vector<double>& b,
229234
return omega_1 + omega_2;
230235
}
231236

232-
void Numeric::conditionNumber() const {
233-
HighsRandom random;
234-
235-
const Int n = S_.size();
236-
237-
// estimate largest eigenvalue with power iteration:
238-
// x <- x / ||x||
239-
// y <- M * x
240-
// lambda = ||y||
241-
242-
double lambda_large{};
243-
std::vector<double> x(n);
244-
for (Int i = 0; i < n; ++i) x[i] = random.fraction();
245-
246-
for (Int iter = 0; iter < 10; ++iter) {
247-
// normalize x
248-
double x_norm = norm2(x);
249-
vectorScale(x, 1.0 / x_norm);
250-
251-
// multiply by matrix
252-
std::vector<double> y(n);
253-
symProduct(ptrA_, rowsA_, valA_, x, y);
254-
255-
double norm_y = norm2(y);
256-
257-
if (std::abs(lambda_large - norm_y) / std::max(1.0, lambda_large) < 1e-2) {
258-
// converged
259-
break;
260-
}
261-
262-
lambda_large = norm_y;
263-
x = std::move(y);
264-
}
265-
266-
// estimate inverse of smallest eigenvalue with inverse power iteration:
267-
// x <- x / ||x||
268-
// y <- M^-1 * x
269-
// lambda_inv = ||y||
270-
271-
double lambda_small_inv{};
272-
for (Int i = 0; i < n; ++i) x[i] = random.fraction();
273-
274-
for (Int iter = 0; iter < 10; ++iter) {
275-
// normalize x
276-
double x_norm = norm2(x);
277-
vectorScale(x, 1.0 / x_norm);
278-
279-
// solve with matrix
280-
std::vector<double> y(x);
281-
SH_->forwardSolve(y);
282-
SH_->diagSolve(y);
283-
SH_->backwardSolve(y);
284-
285-
double norm_y = norm2(y);
286-
287-
if (std::abs(lambda_small_inv - norm_y) / std::max(1.0, lambda_small_inv) <
288-
1e-2) {
289-
// converged
290-
break;
291-
}
292-
293-
lambda_small_inv = norm_y;
294-
x = std::move(y);
295-
}
296-
297-
// condition number: lambda_large / lambda_small
298-
double cond = lambda_large * lambda_small_inv;
299-
}
300-
301237
} // namespace hipo

0 commit comments

Comments
 (0)