@@ -72,80 +72,92 @@ Int FactorHiGHSSolver::setup() {
7272 return kStatusOk ;
7373}
7474
75- Int FactorHiGHSSolver::buildNEstructureDense (const HighsSparseMatrix& A,
76- int64_t nz_limit) {
77- // Build lower triangular structure of AAt.
78- // This approach should be advantageous if AAt is expected to be relatively
79- // dense.
80-
81- // create row-wise copy of the matrix
82- AT_ = A;
83- AT_.ensureRowwise ();
84-
85- ptrNE_.clear ();
86- rowsNE_.clear ();
75+ Int FactorHiGHSSolver::buildNEvalues (const HighsSparseMatrix& A,
76+ const std::vector<double >& scaling) {
77+ // given the NE structure already computed, fill in the NE values
8778
88- // ptr is allocated its exact size
89- ptrNE_.resize (A.num_row_ + 1 , 0 );
79+ assert (!ptrNE_.empty () && !rowsNE_.empty ());
9080
91- // temporary dense vector
92- std::vector<Int> work (A.num_row_ );
81+ valNE_.assign (rowsNE_.size (), 0.0 );
9382
94- int64_t AAt_nz = 0 ;
83+ std::vector< double > work (A. num_row_ , 0.0 ) ;
9584
9685 for (Int row = 0 ; row < A.num_row_ ; ++row) {
97- // work contains information about column "row".
98- // if there is a 1 in position pos, then AAt has nonzero in entry (pos,row).
99- // only entries below entry "row" (the diagonal) are used.
100-
10186 // go along the entries of the row, and then down each column.
10287 // this builds the lower triangular part of the row-th column of AAt.
10388 for (Int rowEl = AT_.start_ [row]; rowEl < AT_.start_ [row + 1 ]; ++rowEl) {
10489 Int col = AT_.index_ [rowEl];
10590
91+ const double theta =
92+ scaling.empty () ? 1.0 : 1.0 / (scaling[col] + regul_.primal );
93+
94+ const double row_value = theta * AT_.value_ [rowEl];
95+
10696 // for each nonzero in the row, go down corresponding column
10797 for (Int colEl = A.start_ [col]; colEl < A.start_ [col + 1 ]; ++colEl) {
10898 Int row2 = A.index_ [colEl];
10999
110100 // skip when row2 is above row
111101 if (row2 < row) continue ;
112102
113- // save information that there is nonzero in position (row2,row).
114- work[row2] = 1 ;
103+ // compute and accumulate value
104+ double value = row_value * A.value_ [colEl];
105+ work[row2] += value;
115106 }
116107 }
117108 // intersection of row with rows below finished.
118- // now work contains the sparsity pattern of AAt(row:end,row).
119109
120- // now assign indices
121- Int col_nz = 0 ;
122- for (Int i = row; i < work.size (); ++i) {
123- if (work[i]) {
124- if (AAt_nz + 1 >= nz_limit) return kStatusOoM ;
125-
126- rowsNE_.push_back (i);
127- work[i] = 0 ;
128- ++AAt_nz;
129- ++col_nz;
130- }
110+ // read from work, using indices of column "row" of AAt
111+ for (Int el = ptrNE_[row]; el < ptrNE_[row + 1 ]; ++el) {
112+ Int index = rowsNE_[el];
113+ valNE_[el] = work[index];
114+ work[index] = 0.0 ;
131115 }
132-
133- // update pointers
134- ptrNE_[row + 1 ] = ptrNE_[row] + col_nz;
135116 }
136117
137118 return kStatusOk ;
138119}
139120
140- Int FactorHiGHSSolver::buildNEstructureSparse (const HighsSparseMatrix& A,
141- int64_t nz_limit) {
121+ Int FactorHiGHSSolver::buildNEstructure (const HighsSparseMatrix& A,
122+ int64_t nz_limit) {
142123 // Build lower triangular structure of AAt.
143- // This approach should be advantageous if AAt is expected to be very sparse.
124+ // This approach uses a column-wise copy of A and a collection of linked lists
125+ // to access the rows
144126
145- // create row-wise copy of the matrix
127+ // create row-wise copy of the matrix (to be removed later!!!!!!!!)
146128 AT_ = A;
147129 AT_.ensureRowwise ();
148130
131+ // NB: A must have sorted columns for this to work
132+
133+ // colNE stores the index of the column of each entry.
134+ // It's useless when accessing the matrix by columns, but useful when doing it
135+ // by rows.
136+ colNE_.resize (A.numNz ());
137+
138+ // head and next are a collection of linked lists to access the matrix by
139+ // rows.
140+ nextNE_.resize (A.numNz (), -1 );
141+ headNE_.resize (A.num_row_ , -1 );
142+
143+ // temp stores the address of the latest entry in each row
144+ std::vector<Int> temp (A.num_row_ , -1 );
145+
146+ // build linked lists for row access
147+ for (Int col = 0 ; col < A.num_col_ ; ++col) {
148+ for (Int el = A.start_ [col]; el < A.start_ [col + 1 ]; ++el) {
149+ Int row = A.index_ [el];
150+ colNE_[el] = col;
151+
152+ if (temp[row] == -1 )
153+ headNE_[row] = el;
154+ else
155+ nextNE_[temp[row]] = el;
156+
157+ temp[row] = el;
158+ }
159+ }
160+
149161 ptrNE_.clear ();
150162 rowsNE_.clear ();
151163
@@ -164,34 +176,27 @@ Int FactorHiGHSSolver::buildNEstructureSparse(const HighsSparseMatrix& A,
164176
165177 Int nz_in_col = 0 ;
166178
167- for (Int rowEl = AT_.start_ [row]; rowEl < AT_.start_ [row + 1 ]; ++rowEl) {
168- Int col = AT_.index_ [rowEl];
179+ Int current = headNE_[row];
180+ while (current != -1 ) {
181+ Int col = colNE_[current];
169182
170- // for each nonzero in the row, go down corresponding column
171- for (Int colEl = A.start_ [col]; colEl < A.start_ [col + 1 ]; ++colEl) {
183+ // for each nonzero in the row, go down corresponding column, starting
184+ // from current position
185+ for (Int colEl = current; colEl < A.start_ [col + 1 ]; ++colEl) {
172186 Int row2 = A.index_ [colEl];
173187
174- // skip when row2 is above row
175- if (row2 < row) continue ;
188+ // row2 is guaranteed to be larger or equal than row
189+ // (provided that the columns of A are sorted)
176190
177191 // save information that there is nonzero in position (row2,row).
178192 if (!is_nz[row2]) {
179193 is_nz[row2] = true ;
180194 temp_index[nz_in_col] = row2;
181195 ++nz_in_col;
182196 }
183-
184- // the same if statement could be executed without branching:
185- // (there does not seem to be a performance advantage)
186- /*
187- Int old_v = is_nz[row2];
188- Int new_v = 1;
189- Int diff_v = new_v - old_v;
190- is_nz[row2] = true;
191- temp_index[nz_in_col] = temp_index[nz_in_col] * old_v + diff_v * row2;
192- nz_in_col += diff_v;
193- */
194197 }
198+
199+ current = nextNE_[current];
195200 }
196201 // intersection of row with rows below finished.
197202
@@ -213,89 +218,6 @@ Int FactorHiGHSSolver::buildNEstructureSparse(const HighsSparseMatrix& A,
213218 return kStatusOk ;
214219}
215220
216- Int FactorHiGHSSolver::buildNEvalues (const HighsSparseMatrix& A,
217- const std::vector<double >& scaling) {
218- // given the NE structure already computed, fill in the NE values
219-
220- assert (!ptrNE_.empty () && !rowsNE_.empty ());
221-
222- valNE_.assign (rowsNE_.size (), 0.0 );
223-
224- std::vector<double > work (A.num_row_ , 0.0 );
225-
226- for (Int row = 0 ; row < A.num_row_ ; ++row) {
227- // go along the entries of the row, and then down each column.
228- // this builds the lower triangular part of the row-th column of AAt.
229- for (Int rowEl = AT_.start_ [row]; rowEl < AT_.start_ [row + 1 ]; ++rowEl) {
230- Int col = AT_.index_ [rowEl];
231-
232- const double theta =
233- scaling.empty () ? 1.0 : 1.0 / (scaling[col] + regul_.primal );
234-
235- const double row_value = theta * AT_.value_ [rowEl];
236-
237- // for each nonzero in the row, go down corresponding column
238- for (Int colEl = A.start_ [col]; colEl < A.start_ [col + 1 ]; ++colEl) {
239- Int row2 = A.index_ [colEl];
240-
241- // skip when row2 is above row
242- if (row2 < row) continue ;
243-
244- // compute and accumulate value
245- double value = row_value * A.value_ [colEl];
246- work[row2] += value;
247- }
248- }
249- // intersection of row with rows below finished.
250-
251- // read from work, using indices of column "row" of AAt
252- for (Int el = ptrNE_[row]; el < ptrNE_[row + 1 ]; ++el) {
253- Int index = rowsNE_[el];
254- valNE_[el] = work[index];
255- work[index] = 0.0 ;
256- }
257- }
258-
259- return kStatusOk ;
260- }
261-
262- Int FactorHiGHSSolver::buildNEstructureJacek (const HighsSparseMatrix& A,
263- int64_t nz_limit) {
264- // Build lower triangular structure of AAt.
265- // This approach uses a column-wise copy of A and a collection of linked lists
266- // to access the rows
267-
268- // NB: A must have sorted columns for this to work
269-
270- // colNE stores the index of the column of each entry.
271- // It's useless when accessing the matrix by columns, but useful when doing it
272- // by rows.
273- colNE_.resize (A.numNz ());
274-
275- // head and next are a collection of linked lists to access the matrix by
276- // rows.
277- nextNE_.resize (A.numNz (), -1 );
278- headNE_.resize (A.num_row_ , -1 );
279-
280- // temp stores the address of the latest entry in each row
281- std::vector<Int> temp (A.num_row_ , -1 );
282-
283- // build linked lists for row access
284- for (Int col = 0 ; col < A.num_col_ ; ++col) {
285- for (Int el = A.start_ [col]; el < A.start_ [col + 1 ]; ++el) {
286- Int row = A.index_ [el];
287- colNE_[el] = col;
288-
289- if (temp[row] == -1 )
290- headNE_[row] = el;
291- else
292- nextNE_[temp[row]] = el;
293-
294- temp[row] = el;
295- }
296- }
297- }
298-
299221Int FactorHiGHSSolver::factorAS (const HighsSparseMatrix& A,
300222 const std::vector<double >& scaling) {
301223 // only execute factorisation if it has not been done yet
@@ -491,10 +413,8 @@ Int FactorHiGHSSolver::analyseNE(Symbolic& S, int64_t nz_limit) {
491413
492414 log_.printDevInfo (" Building NE structure\n " );
493415
494- buildNEstructureJacek (model_.A (), nz_limit);
495-
496416 Clock clock;
497- if (Int status = buildNEstructureSparse (model_.A (), nz_limit)) return status;
417+ if (Int status = buildNEstructure (model_.A (), nz_limit)) return status;
498418 if (info_) info_->matrix_structure_time = clock.stop ();
499419
500420 // create vector of signs of pivots
0 commit comments