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