1 // NOLINT(legal/copyright)
2 // SYMBOL "house"
3 // Householder reflection
4 // Ref: Chapter 5, Direct Methods for Sparse Linear Systems by Tim Davis
5 template<typename T1>
casadi_house(T1 * v,T1 * beta,casadi_int nv)6 T1 casadi_house(T1* v, T1* beta, casadi_int nv) {
7 // Local variable
8 casadi_int i;
9 T1 v0, sigma, s, sigma_is_zero, v0_nonpos;
10 // Calculate norm
11 v0 = v[0]; // Save v0 (overwritten below)
12 sigma=0;
13 for (i=1; i<nv; ++i) sigma += v[i]*v[i];
14 s = sqrt(v0*v0 + sigma); // s = norm(v)
15 // Calculate consistently with symbolic datatypes (SXElem)
16 sigma_is_zero = sigma==0;
17 v0_nonpos = v0<=0;
18 // C-REPLACE "if_else" "casadi_if_else"
19 v[0] = if_else(sigma_is_zero, 1,
20 if_else(v0_nonpos, v0-s, -sigma/(v0+s))); // NOLINT
21 *beta = if_else(sigma_is_zero, 2*v0_nonpos, -1/(s*v[0])); // NOLINT
22 return s;
23 }
24
25 // SYMBOL "qr"
26 // Numeric QR factorization
27 // Ref: Chapter 5, Direct Methods for Sparse Linear Systems by Tim Davis
28 // len[x] = nrow
29 // sp_v = [nrow, ncol, 0, 0, ...] len[3 + ncol + nnz_v]
30 // len[v] nnz_v
31 // sp_r = [nrow, ncol, 0, 0, ...] len[3 + ncol + nnz_r]
32 // len[r] nnz_r
33 // len[beta] ncol
34 template<typename T1>
casadi_qr(const casadi_int * sp_a,const T1 * nz_a,T1 * x,const casadi_int * sp_v,T1 * nz_v,const casadi_int * sp_r,T1 * nz_r,T1 * beta,const casadi_int * prinv,const casadi_int * pc)35 void casadi_qr(const casadi_int* sp_a, const T1* nz_a, T1* x,
36 const casadi_int* sp_v, T1* nz_v, const casadi_int* sp_r, T1* nz_r, T1* beta,
37 const casadi_int* prinv, const casadi_int* pc) {
38 // Local variables
39 casadi_int ncol, nrow, r, c, k, k1;
40 T1 alpha;
41 const casadi_int *a_colind, *a_row, *v_colind, *v_row, *r_colind, *r_row;
42 // Extract sparsities
43 ncol = sp_a[1];
44 a_colind=sp_a+2; a_row=sp_a+2+ncol+1;
45 nrow = sp_v[0];
46 v_colind=sp_v+2; v_row=sp_v+2+ncol+1;
47 r_colind=sp_r+2; r_row=sp_r+2+ncol+1;
48 // Clear work vector
49 for (r=0; r<nrow; ++r) x[r] = 0;
50 // Loop over columns of R, A and V
51 for (c=0; c<ncol; ++c) {
52 // Copy (permuted) column of A to x
53 for (k=a_colind[pc[c]]; k<a_colind[pc[c]+1]; ++k) x[prinv[a_row[k]]] = nz_a[k];
54 // Use the equality R = (I-betan*vn*vn')*...*(I-beta1*v1*v1')*A to get
55 // strictly upper triangular entries of R
56 for (k=r_colind[c]; k<r_colind[c+1] && (r=r_row[k])<c; ++k) {
57 // Calculate scalar factor alpha = beta(r)*dot(v(:,r), x)
58 alpha = 0;
59 for (k1=v_colind[r]; k1<v_colind[r+1]; ++k1) alpha += nz_v[k1]*x[v_row[k1]];
60 alpha *= beta[r];
61 // x -= alpha*v(:,r)
62 for (k1=v_colind[r]; k1<v_colind[r+1]; ++k1) x[v_row[k1]] -= alpha*nz_v[k1];
63 // Get r entry
64 *nz_r++ = x[r];
65 // Strictly upper triangular entries in x no longer needed
66 x[r] = 0;
67 }
68 // Get V column
69 for (k=v_colind[c]; k<v_colind[c+1]; ++k) {
70 nz_v[k] = x[v_row[k]];
71 // Lower triangular entries of x no longer needed
72 x[v_row[k]] = 0;
73 }
74 // Get diagonal entry of R, normalize V column
75 *nz_r++ = casadi_house(nz_v + v_colind[c], beta + c, v_colind[c+1] - v_colind[c]);
76 }
77 }
78
79 // SYMBOL "qr_mv"
80 // Multiply QR Q matrix from the right with a vector, with Q represented
81 // by the Householder vectors V and beta
82 // x = Q*x or x = Q'*x
83 // with Q = (I-beta(1)*v(:,1)*v(:,1)')*...*(I-beta(n)*v(:,n)*v(:,n)')
84 // len[x] >= nrow_ext
85 template<typename T1>
casadi_qr_mv(const casadi_int * sp_v,const T1 * v,const T1 * beta,T1 * x,casadi_int tr)86 void casadi_qr_mv(const casadi_int* sp_v, const T1* v, const T1* beta, T1* x,
87 casadi_int tr) {
88 // Local variables
89 casadi_int ncol, c, c1, k;
90 T1 alpha;
91 const casadi_int *colind, *row;
92 // Extract sparsity
93 ncol=sp_v[1];
94 colind=sp_v+2; row=sp_v+2+ncol+1;
95 // Loop over vectors
96 for (c1=0; c1<ncol; ++c1) {
97 // Forward order for transpose, otherwise backwards
98 c = tr ? c1 : ncol-1-c1;
99 // Calculate scalar factor alpha = beta(c)*dot(v(:,c), x)
100 alpha=0;
101 for (k=colind[c]; k<colind[c+1]; ++k) alpha += v[k]*x[row[k]];
102 alpha *= beta[c];
103 // x -= alpha*v(:,c)
104 for (k=colind[c]; k<colind[c+1]; ++k) x[row[k]] -= alpha*v[k];
105 }
106 }
107
108 // SYMBOL "qr_trs"
109 // Solve for an (optionally transposed) upper triangular matrix R
110 template<typename T1>
casadi_qr_trs(const casadi_int * sp_r,const T1 * nz_r,T1 * x,casadi_int tr)111 void casadi_qr_trs(const casadi_int* sp_r, const T1* nz_r, T1* x, casadi_int tr) {
112 // Local variables
113 casadi_int ncol, r, c, k;
114 const casadi_int *colind, *row;
115 // Extract sparsity
116 ncol=sp_r[1];
117 colind=sp_r+2; row=sp_r+2+ncol+1;
118 if (tr) {
119 // Forward substitution
120 for (c=0; c<ncol; ++c) {
121 for (k=colind[c]; k<colind[c+1]; ++k) {
122 r = row[k];
123 if (r==c) {
124 x[c] /= nz_r[k];
125 } else {
126 x[c] -= nz_r[k]*x[r];
127 }
128 }
129 }
130 } else {
131 // Backward substitution
132 for (c=ncol-1; c>=0; --c) {
133 for (k=colind[c+1]-1; k>=colind[c]; --k) {
134 r=row[k];
135 if (r==c) {
136 x[r] /= nz_r[k];
137 } else {
138 x[r] -= nz_r[k]*x[c];
139 }
140 }
141 }
142 }
143 }
144
145 // SYMBOL "qr_solve"
146 // Solve a factorized linear system
147 // len[w] >= max(ncol, nrow_ext)
148 template<typename T1>
casadi_qr_solve(T1 * x,casadi_int nrhs,casadi_int tr,const casadi_int * sp_v,const T1 * v,const casadi_int * sp_r,const T1 * r,const T1 * beta,const casadi_int * prinv,const casadi_int * pc,T1 * w)149 void casadi_qr_solve(T1* x, casadi_int nrhs, casadi_int tr,
150 const casadi_int* sp_v, const T1* v, const casadi_int* sp_r, const T1* r,
151 const T1* beta, const casadi_int* prinv, const casadi_int* pc, T1* w) {
152 casadi_int k, c, nrow_ext, ncol;
153 nrow_ext = sp_v[0]; ncol = sp_v[1];
154 for (k=0; k<nrhs; ++k) {
155 if (tr) {
156 // (PR' Q R PC)' x = PC' R' Q' PR x = b <-> x = PR' Q R' \ PC b
157 // Multiply by PC
158 for (c=0; c<ncol; ++c) w[c] = x[pc[c]];
159 // Solve for R'
160 casadi_qr_trs(sp_r, r, w, 1);
161 // Multiply by Q
162 casadi_qr_mv(sp_v, v, beta, w, 0);
163 // Multiply by PR'
164 for (c=0; c<ncol; ++c) x[c] = w[prinv[c]];
165 } else {
166 //PR' Q R PC x = b <-> x = PC' R \ Q' PR b
167 // Multiply with PR
168 for (c=0; c<nrow_ext; ++c) w[c] = 0;
169 for (c=0; c<ncol; ++c) w[prinv[c]] = x[c];
170 // Multiply with Q'
171 casadi_qr_mv(sp_v, v, beta, w, 1);
172 // Solve for R
173 casadi_qr_trs(sp_r, r, w, 0);
174 // Multiply with PC'
175 for (c=0; c<ncol; ++c) x[pc[c]] = w[c];
176 }
177 x += ncol;
178 }
179 }
180
181 // SYMBOL "qr_singular"
182 // Check if QR factorization corresponds to a singular matrix
183 template<typename T1>
casadi_qr_singular(T1 * rmin,casadi_int * irmin,const T1 * nz_r,const casadi_int * sp_r,const casadi_int * pc,T1 eps)184 casadi_int casadi_qr_singular(T1* rmin, casadi_int* irmin, const T1* nz_r,
185 const casadi_int* sp_r, const casadi_int* pc, T1 eps) {
186 // Local variables
187 T1 rd, rd_min;
188 casadi_int ncol, c, nullity;
189 const casadi_int* r_colind;
190 // Nullity
191 nullity = 0;
192 // Extract sparsity
193 ncol = sp_r[1];
194 r_colind = sp_r + 2;
195 // Find the smallest diagonal entry
196 for (c=0; c<ncol; ++c) {
197 rd = fabs(nz_r[r_colind[c+1]-1]);
198 // Increase nullity if smaller than eps
199 if (rd<eps) nullity++;
200 // Check if smallest so far
201 if (c==0 || rd < rd_min) {
202 rd_min = rd;
203 if (rmin) *rmin = rd;
204 if (irmin) *irmin = pc[c];
205 }
206 }
207 // Return number of zero-ish eigenvalues
208 return nullity;
209 }
210
211 // SYMBOL "qr_colcomb"
212 // Get a vector v such that A*v = 0 and |v| == 1
213 template<typename T1>
casadi_qr_colcomb(T1 * v,const T1 * nz_r,const casadi_int * sp_r,const casadi_int * pc,T1 eps,casadi_int ind)214 void casadi_qr_colcomb(T1* v, const T1* nz_r, const casadi_int* sp_r,
215 const casadi_int* pc, T1 eps, casadi_int ind) {
216 // Local variables
217 casadi_int ncol, r, c, k;
218 const casadi_int *r_colind, *r_row;
219 // Extract sparsity
220 ncol = sp_r[1];
221 r_colind = sp_r + 2;
222 r_row = r_colind + ncol + 1;
223 // Find the ind-th diagonal which is smaller than eps, overwrite ind with c
224 for (c=0; c<ncol; ++c) {
225 if (fabs(nz_r[r_colind[c+1]-1])<eps && 0==ind--) {
226 ind = c;
227 break;
228 }
229 }
230 // Reset w
231 casadi_clear(v, ncol);
232 v[pc[ind]] = 1.;
233 // Copy ind-th column to v
234 for (k=r_colind[ind]; k<r_colind[ind+1]-1; ++k) {
235 v[pc[r_row[k]]] = -nz_r[k];
236 }
237 // Backward substitution
238 for (c=ind-1; c>=0; --c) {
239 for (k=r_colind[c+1]-1; k>=r_colind[c]; --k) {
240 r=r_row[k];
241 if (r==c) {
242 if (fabs(nz_r[k])<eps) {
243 v[pc[r]] = 0;
244 } else {
245 v[pc[r]] /= nz_r[k];
246 }
247 } else {
248 v[pc[r]] -= nz_r[k]*v[pc[c]];
249 }
250 }
251 }
252 // Normalize v
253 casadi_scal(ncol, 1./sqrt(casadi_dot(ncol, v, v)), v);
254 }
255