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