1 // Copyright (c) 2018 ERGO-Code. See license.txt for license.
2
3 #include "sparse_matrix.h"
4 #include <algorithm>
5 #include <cassert>
6 #include <cmath>
7 #include <utility>
8 #include "utils.h"
9
10 namespace ipx {
11
SparseMatrix()12 SparseMatrix::SparseMatrix() {
13 resize(0,0);
14 }
15
SparseMatrix(Int nrow,Int ncol)16 SparseMatrix::SparseMatrix(Int nrow, Int ncol) {
17 resize(nrow, ncol);
18 }
19
SparseMatrix(Int nrow,Int ncol,Int min_capacity)20 SparseMatrix::SparseMatrix(Int nrow, Int ncol, Int min_capacity) {
21 resize(nrow, ncol, min_capacity);
22 }
23
reserve(Int min_capacity)24 void SparseMatrix::reserve(Int min_capacity) {
25 if (min_capacity > capacity()) {
26 rowidx_.resize(min_capacity);
27 values_.resize(min_capacity);
28 }
29 }
30
resize(Int nrow,Int ncol,Int min_capacity)31 void SparseMatrix::resize(Int nrow, Int ncol, Int min_capacity) {
32 assert(nrow >= 0);
33 assert(ncol >= 0);
34 assert(min_capacity >= 0);
35 nrow_ = nrow;
36 colptr_.resize(ncol+1);
37 colptr_.shrink_to_fit();
38 std::fill(colptr_.begin(), colptr_.end(), 0);
39 rowidx_.resize(min_capacity);
40 rowidx_.shrink_to_fit();
41 values_.resize(min_capacity);
42 values_.shrink_to_fit();
43 }
44
clear()45 void SparseMatrix::clear() {
46 resize(0,0);
47 }
48
LoadFromArrays(Int nrow,Int ncol,const Int * Abegin,const Int * Aend,const Int * Ai,const double * Ax)49 void SparseMatrix::LoadFromArrays(Int nrow, Int ncol, const Int* Abegin,
50 const Int* Aend, const Int* Ai,
51 const double* Ax) {
52 Int nz = 0;
53 for (Int j = 0; j < ncol; j++)
54 nz += Aend[j]-Abegin[j];
55 resize(nrow, ncol, nz);
56 Int put = 0;
57 for (Int j = 0; j < ncol; j++) {
58 colptr_[j] = put;
59 for (Int p = Abegin[j]; p < Aend[j]; p++) {
60 if (Ax[p] != 0.0) {
61 rowidx_[put] = Ai[p];
62 values_[put] = Ax[p];
63 put++;
64 }
65 }
66 }
67 colptr_[ncol] = put;
68 SortIndices();
69 }
70
SortIndices()71 void SparseMatrix::SortIndices() {
72 if (IsSorted())
73 return;
74 std::vector<std::pair<Int,double>> work(rows());
75 for (Int j = 0; j < cols(); j++) {
76 Int nz = 0; // # entries in column j
77 for (Int p = begin(j); p < end(j); p++) {
78 work[nz].first = index(p);
79 work[nz].second = value(p);
80 nz++;
81 }
82 std::sort(work.begin(), work.begin() + nz);
83 for (Int k = 0, p = begin(j); p < end(j); k++, p++) {
84 index(p) = work[k].first;
85 value(p) = work[k].second;
86 }
87 }
88 }
89
add_column()90 void SparseMatrix::add_column() {
91 Int nz = entries();
92 Int nznew = nz + queue_size();
93 reserve(nznew);
94 std::copy(rowidx_queue_.begin(), rowidx_queue_.end(), rowidx_.begin() + nz);
95 std::copy(values_queue_.begin(), values_queue_.end(), values_.begin() + nz);
96 colptr_.push_back(nznew);
97 clear_queue();
98 }
99
clear_queue()100 void SparseMatrix::clear_queue() {
101 rowidx_queue_.clear();
102 values_queue_.clear();
103 }
104
IsSorted() const105 bool SparseMatrix::IsSorted() const {
106 for (Int j = 0; j < cols(); j++) {
107 for (Int p = begin(j); p < end(j)-1; p++)
108 if (index(p) > index(p+1))
109 return false;
110 }
111 return true;
112 }
113
Transpose(const SparseMatrix & A)114 SparseMatrix Transpose(const SparseMatrix& A) {
115 SparseMatrix AT;
116 Transpose(A, AT);
117 return AT;
118 }
119
Transpose(const SparseMatrix & A,SparseMatrix & AT)120 void Transpose(const SparseMatrix& A, SparseMatrix& AT) {
121 const Int m = A.rows();
122 const Int n = A.cols();
123 const Int nz = A.entries();
124 AT.resize(n, m, nz);
125
126 // Compute row counts of A in workspace.
127 std::vector<Int> work(m);
128 for (Int p = 0; p < nz; p++)
129 work[A.index(p)]++;
130
131 // Set column pointers for AT.
132 Int* ATp = AT.colptr();
133 Int sum = 0;
134 for (Int i = 0; i < m; i++) {
135 ATp[i] = sum;
136 sum += work[i];
137 work[i] = ATp[i];
138 }
139 assert(sum == nz);
140 ATp[m] = sum;
141
142 // Fill AT with one column of A at a time.
143 // work[i] is the next free slot in column i of AT.
144 for (Int j = 0; j < n; j++) {
145 for (Int p = A.begin(j); p < A.end(j); p++) {
146 Int put = work[A.index(p)]++;
147 AT.index(put) = j;
148 AT.value(put) = A.value(p);
149 }
150 }
151 }
152
CopyColumns(const SparseMatrix & A,const std::vector<Int> & cols)153 SparseMatrix CopyColumns(const SparseMatrix& A, const std::vector<Int>& cols) {
154 SparseMatrix A2(A.rows(), 0);
155 for (Int j : cols) {
156 for (Int p = A.begin(j); p < A.end(j); p++)
157 A2.push_back(A.index(p), A.value(p));
158 A2.add_column();
159 }
160 return A2;
161 }
162
PermuteRows(SparseMatrix & A,const std::vector<Int> & perm)163 void PermuteRows(SparseMatrix& A, const std::vector<Int>& perm) {
164 for (Int p = 0; p < A.entries(); p++)
165 A.index(p) = perm[A.index(p)];
166 }
167
RemoveDiagonal(SparseMatrix & A,double * diag)168 Int RemoveDiagonal(SparseMatrix& A, double* diag) {
169 Int ncol = A.cols();
170 Int* Ap = A.colptr();
171 Int* Ai = A.rowidx();
172 double* Ax = A.values();
173 Int get = 0;
174 Int put = 0;
175 for (Int j = 0; j < ncol; j++) {
176 if (diag)
177 diag[j] = 0.0; // if no diagonal entry in column j
178 Ap[j] = put;
179 for (; get < Ap[j+1]; get++) {
180 if (Ai[get] == j) {
181 if (diag)
182 diag[j] = Ax[get];
183 } else {
184 Ai[put] = Ai[get];
185 Ax[put] = Ax[get];
186 put++;
187 }
188 }
189 }
190 Ap[ncol] = put;
191 return get-put;
192 }
193
MultiplyAdd(const SparseMatrix & A,const Vector & rhs,double alpha,Vector & lhs,char trans)194 void MultiplyAdd(const SparseMatrix& A, const Vector& rhs, double alpha,
195 Vector& lhs, char trans) {
196 const Int m = A.rows();
197 const Int n = A.cols();
198 if (trans == 't' || trans == 'T') {
199 assert((int)rhs.size() == m);
200 assert((int)lhs.size() == n);
201 for (Int j = 0; j < n; j++)
202 lhs[j] += alpha * DotColumn(A, j, rhs);
203 } else {
204 assert((int)rhs.size() == n);
205 assert((int)lhs.size() == m);
206 for (Int j = 0; j < n; j++)
207 ScatterColumn(A, j, alpha*rhs[j], lhs);
208 }
209 (void)(m);
210 }
211
AddNormalProduct(const SparseMatrix & A,const double * D,const Vector & rhs,Vector & lhs)212 void AddNormalProduct(const SparseMatrix& A, const double* D, const Vector& rhs,
213 Vector& lhs) {
214 const Int m = A.rows();
215 const Int n = A.cols();
216 assert((int)rhs.size() == m);
217 assert((int)lhs.size() == m);
218 for (Int j = 0; j < n; j++) {
219 double temp = DotColumn(A, j, rhs);
220 if (D) temp *= D[j]*D[j];
221 ScatterColumn(A, j, temp, lhs);
222 }
223 (void)(m);
224 }
225
TriangularSolve(const SparseMatrix & A,Vector & x,char trans,const char * uplo,int unitdiag)226 Int TriangularSolve(const SparseMatrix& A, Vector& x, char trans,
227 const char* uplo, int unitdiag) {
228 const Int ncol = A.cols();
229 const Int* Ap = A.colptr();
230 const Int* Ai = A.rowidx();
231 const double* Ax = A.values();
232 Int nz = 0;
233 if (trans == 't' || trans == 'T') {
234 if (*uplo == 'u' || *uplo == 'U') {
235 // transposed solve with upper triangular matrix
236 for (Int i = 0; i < ncol; i++) {
237 Int begin = Ap[i];
238 Int end = Ap[i+1] - (unitdiag ? 0 : 1);
239 double d = 0.0;
240 for (Int p = begin; p < end; p++)
241 d += x[Ai[p]] * Ax[p];
242 x[i] -= d;
243 if (!unitdiag) {
244 assert(Ai[end] == i);
245 x[i] /= Ax[end];
246 }
247 if (x[i] != 0.0)
248 nz++;
249 }
250 } else {
251 // transposed solve with lower triangular matrix
252 for (Int i = ncol-1; i >= 0; i--) {
253 Int begin = Ap[i] + (unitdiag ? 0 : 1);
254 Int end = Ap[i+1];
255 double d = 0.0;
256 for (Int p = begin; p < end; p++)
257 d += x[Ai[p]] * Ax[p];
258 x[i] -= d;
259 if (!unitdiag) {
260 assert(Ai[begin-1] == i);
261 x[i] /= Ax[begin-1];
262 }
263 if (x[i] != 0.0)
264 nz++;
265 }
266 }
267 } else {
268 if (*uplo == 'u' || *uplo == 'U') {
269 // forward solve with upper triangular matrix
270 for (Int j = ncol-1; j >= 0; j--) {
271 Int begin = Ap[j];
272 Int end = Ap[j+1] - (unitdiag ? 0 : 1);
273 if (!unitdiag) {
274 assert(Ai[end] == j);
275 x[j] /= Ax[end];
276 }
277 double temp = x[j];
278 if (temp != 0.0) {
279 for (Int p = begin; p < end; p++)
280 x[Ai[p]] -= Ax[p] * temp;
281 nz++;
282 }
283 }
284 } else {
285 // forward solve with lower triangular matrix
286 for (Int j = 0; j < ncol; j++) {
287 Int begin = Ap[j] + (unitdiag ? 0 : 1);
288 Int end = Ap[j+1];
289 if (!unitdiag) {
290 assert(Ai[begin-1] == j);
291 x[j] /= Ax[begin-1];
292 }
293 double temp = x[j];
294 if (temp != 0.0) {
295 for (Int p = begin; p < end; p++)
296 x[Ai[p]] -= Ax[p] * temp;
297 nz++;
298 }
299 }
300 }
301 }
302 return nz;
303 }
304
ForwardSolve(const SparseMatrix & L,const SparseMatrix & U,Vector & x)305 void ForwardSolve(const SparseMatrix& L, const SparseMatrix& U, Vector& x) {
306 TriangularSolve(L, x, 'n', "lower", 1);
307 TriangularSolve(U, x, 'n', "upper", 0);
308 }
309
BackwardSolve(const SparseMatrix & L,const SparseMatrix & U,Vector & x)310 void BackwardSolve(const SparseMatrix& L, const SparseMatrix& U, Vector& x) {
311 TriangularSolve(U, x, 't', "upper", 0);
312 TriangularSolve(L, x, 't', "lower", 1);
313 }
314
Onenorm(const SparseMatrix & A)315 double Onenorm(const SparseMatrix& A) {
316 double norm = 0.0;
317 for (Int j = 0; j < A.cols(); j++) {
318 double colsum = 0.0;
319 for (Int p = A.begin(j); p < A.end(j); p++)
320 colsum += std::abs(A.value(p));
321 norm = std::max(norm, colsum);
322 }
323 return norm;
324 }
325
Infnorm(const SparseMatrix & A)326 double Infnorm(const SparseMatrix& A) {
327 Vector rowsum(A.rows());
328 for (Int j = 0; j < A.cols(); j++) {
329 for (Int p = A.begin(j); p < A.end(j); p++)
330 rowsum[A.index(p)] += std::abs(A.value(p));
331 }
332 return Infnorm(rowsum);
333 }
334
NormestInverse(const SparseMatrix & A,const char * uplo,int unitdiag)335 double NormestInverse(const SparseMatrix& A, const char* uplo, int unitdiag) {
336 const Int m = A.rows();
337 Vector x(m);
338 assert(A.rows() == A.cols());
339
340 // Solve A'x=b, where the entries of b are +/-1 chosen dynamically to make x
341 // large.
342 if (*uplo == 'u' || *uplo == 'U') {
343 for (Int j = 0; j < m; j++) {
344 Int begin = A.begin(j);
345 Int end = A.end(j);
346 if (!unitdiag)
347 end--;
348 double temp = 0.0;
349 for (Int p = begin; p < end; p++)
350 temp -= x[A.index(p)] * A.value(p);
351 temp += temp >= 0.0 ? 1.0 : -1.0; // choose b[j] = 1 or b[j] = -1
352 if (!unitdiag) {
353 assert(A.index(end) == j);
354 temp /= A.value(end);
355 }
356 x[j] = temp;
357 }
358 } else {
359 for (Int j = m-1; j >= 0; j--) {
360 Int begin = A.begin(j);
361 Int end = A.end(j);
362 if (!unitdiag)
363 begin++;
364 double temp = 0.0;
365 for (Int p = begin; p < end; p++)
366 temp -= x[A.index(p)] * A.value(p);
367 temp += temp >= 0.0 ? 1.0 : -1.0; // choose b[j] = 1 or b[j] = -1
368 if (!unitdiag) {
369 assert(A.index(begin-1) == j);
370 temp /= A.value(begin-1);
371 }
372 x[j] = temp;
373 }
374 }
375 double x1norm = Onenorm(x);
376 double xinfnorm = Infnorm(x);
377
378 // Solve Ay=x, solution overwrites x.
379 TriangularSolve(A, x, 'n', uplo, unitdiag);
380 double y1norm = Onenorm(x);
381
382 return std::max(y1norm/x1norm, xinfnorm);
383 }
384
385 } // namespace ipx
386