Austin Schuh | 9049e20 | 2022-02-20 17:34:16 -0800 | [diff] [blame] | 1 | #include "cs.h" |
| 2 | |
| 3 | |
| 4 | static void* csc_malloc(c_int n, c_int size) { |
| 5 | return c_malloc(n * size); |
| 6 | } |
| 7 | |
| 8 | static void* csc_calloc(c_int n, c_int size) { |
| 9 | return c_calloc(n, size); |
| 10 | } |
| 11 | |
| 12 | csc* csc_matrix(c_int m, c_int n, c_int nzmax, c_float *x, c_int *i, c_int *p) |
| 13 | { |
| 14 | csc *M = (csc *)c_malloc(sizeof(csc)); |
| 15 | |
| 16 | if (!M) return OSQP_NULL; |
| 17 | |
| 18 | M->m = m; |
| 19 | M->n = n; |
| 20 | M->nz = -1; |
| 21 | M->nzmax = nzmax; |
| 22 | M->x = x; |
| 23 | M->i = i; |
| 24 | M->p = p; |
| 25 | return M; |
| 26 | } |
| 27 | |
| 28 | csc* csc_spalloc(c_int m, c_int n, c_int nzmax, c_int values, c_int triplet) { |
| 29 | csc *A = csc_calloc(1, sizeof(csc)); /* allocate the csc struct */ |
| 30 | |
| 31 | if (!A) return OSQP_NULL; /* out of memory */ |
| 32 | |
| 33 | A->m = m; /* define dimensions and nzmax */ |
| 34 | A->n = n; |
| 35 | A->nzmax = nzmax = c_max(nzmax, 1); |
| 36 | A->nz = triplet ? 0 : -1; /* allocate triplet or comp.col */ |
| 37 | A->p = csc_malloc(triplet ? nzmax : n + 1, sizeof(c_int)); |
| 38 | A->i = csc_malloc(nzmax, sizeof(c_int)); |
| 39 | A->x = values ? csc_malloc(nzmax, sizeof(c_float)) : OSQP_NULL; |
| 40 | if (!A->p || !A->i || (values && !A->x)){ |
| 41 | csc_spfree(A); |
| 42 | return OSQP_NULL; |
| 43 | } else return A; |
| 44 | } |
| 45 | |
| 46 | void csc_spfree(csc *A) { |
| 47 | if (A){ |
| 48 | if (A->p) c_free(A->p); |
| 49 | if (A->i) c_free(A->i); |
| 50 | if (A->x) c_free(A->x); |
| 51 | c_free(A); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | csc* triplet_to_csc(const csc *T, c_int *TtoC) { |
| 56 | c_int m, n, nz, p, k, *Cp, *Ci, *w, *Ti, *Tj; |
| 57 | c_float *Cx, *Tx; |
| 58 | csc *C; |
| 59 | |
| 60 | m = T->m; |
| 61 | n = T->n; |
| 62 | Ti = T->i; |
| 63 | Tj = T->p; |
| 64 | Tx = T->x; |
| 65 | nz = T->nz; |
| 66 | C = csc_spalloc(m, n, nz, Tx != OSQP_NULL, 0); /* allocate result */ |
| 67 | w = csc_calloc(n, sizeof(c_int)); /* get workspace */ |
| 68 | |
| 69 | if (!C || !w) return csc_done(C, w, OSQP_NULL, 0); /* out of memory */ |
| 70 | |
| 71 | Cp = C->p; |
| 72 | Ci = C->i; |
| 73 | Cx = C->x; |
| 74 | |
| 75 | for (k = 0; k < nz; k++) w[Tj[k]]++; /* column counts */ |
| 76 | csc_cumsum(Cp, w, n); /* column pointers */ |
| 77 | |
| 78 | for (k = 0; k < nz; k++) { |
| 79 | Ci[p = w[Tj[k]]++] = Ti[k]; /* A(i,j) is the pth entry in C */ |
| 80 | |
| 81 | if (Cx) { |
| 82 | Cx[p] = Tx[k]; |
| 83 | |
| 84 | if (TtoC != OSQP_NULL) TtoC[k] = p; // Assign vector of indices |
| 85 | } |
| 86 | } |
| 87 | return csc_done(C, w, OSQP_NULL, 1); /* success; free w and return C */ |
| 88 | } |
| 89 | |
| 90 | csc* triplet_to_csr(const csc *T, c_int *TtoC) { |
| 91 | c_int m, n, nz, p, k, *Cp, *Cj, *w, *Ti, *Tj; |
| 92 | c_float *Cx, *Tx; |
| 93 | csc *C; |
| 94 | |
| 95 | m = T->m; |
| 96 | n = T->n; |
| 97 | Ti = T->i; |
| 98 | Tj = T->p; |
| 99 | Tx = T->x; |
| 100 | nz = T->nz; |
| 101 | C = csc_spalloc(m, n, nz, Tx != OSQP_NULL, 0); /* allocate result */ |
| 102 | w = csc_calloc(m, sizeof(c_int)); /* get workspace */ |
| 103 | |
| 104 | if (!C || !w) return csc_done(C, w, OSQP_NULL, 0); /* out of memory */ |
| 105 | |
| 106 | Cp = C->p; |
| 107 | Cj = C->i; |
| 108 | Cx = C->x; |
| 109 | |
| 110 | for (k = 0; k < nz; k++) w[Ti[k]]++; /* row counts */ |
| 111 | csc_cumsum(Cp, w, m); /* row pointers */ |
| 112 | |
| 113 | for (k = 0; k < nz; k++) { |
| 114 | Cj[p = w[Ti[k]]++] = Tj[k]; /* A(i,j) is the pth entry in C */ |
| 115 | |
| 116 | if (Cx) { |
| 117 | Cx[p] = Tx[k]; |
| 118 | |
| 119 | if (TtoC != OSQP_NULL) TtoC[k] = p; // Assign vector of indices |
| 120 | } |
| 121 | } |
| 122 | return csc_done(C, w, OSQP_NULL, 1); /* success; free w and return C */ |
| 123 | } |
| 124 | |
| 125 | c_int csc_cumsum(c_int *p, c_int *c, c_int n) { |
| 126 | c_int i, nz = 0; |
| 127 | |
| 128 | if (!p || !c) return -1; /* check inputs */ |
| 129 | |
| 130 | for (i = 0; i < n; i++) |
| 131 | { |
| 132 | p[i] = nz; |
| 133 | nz += c[i]; |
| 134 | c[i] = p[i]; |
| 135 | } |
| 136 | p[n] = nz; |
| 137 | return nz; /* return sum (c [0..n-1]) */ |
| 138 | } |
| 139 | |
| 140 | c_int* csc_pinv(c_int const *p, c_int n) { |
| 141 | c_int k, *pinv; |
| 142 | |
| 143 | if (!p) return OSQP_NULL; /* p = OSQP_NULL denotes identity */ |
| 144 | |
| 145 | pinv = csc_malloc(n, sizeof(c_int)); /* allocate result */ |
| 146 | |
| 147 | if (!pinv) return OSQP_NULL; /* out of memory */ |
| 148 | |
| 149 | for (k = 0; k < n; k++) pinv[p[k]] = k; /* invert the permutation */ |
| 150 | return pinv; /* return result */ |
| 151 | } |
| 152 | |
| 153 | csc* csc_symperm(const csc *A, const c_int *pinv, c_int *AtoC, c_int values) { |
| 154 | c_int i, j, p, q, i2, j2, n, *Ap, *Ai, *Cp, *Ci, *w; |
| 155 | c_float *Cx, *Ax; |
| 156 | csc *C; |
| 157 | |
| 158 | n = A->n; |
| 159 | Ap = A->p; |
| 160 | Ai = A->i; |
| 161 | Ax = A->x; |
| 162 | C = csc_spalloc(n, n, Ap[n], values && (Ax != OSQP_NULL), |
| 163 | 0); /* alloc result*/ |
| 164 | w = csc_calloc(n, sizeof(c_int)); /* get workspace */ |
| 165 | |
| 166 | if (!C || !w) return csc_done(C, w, OSQP_NULL, 0); /* out of memory */ |
| 167 | |
| 168 | Cp = C->p; |
| 169 | Ci = C->i; |
| 170 | Cx = C->x; |
| 171 | |
| 172 | for (j = 0; j < n; j++) /* count entries in each column of C */ |
| 173 | { |
| 174 | j2 = pinv ? pinv[j] : j; /* column j of A is column j2 of C */ |
| 175 | |
| 176 | for (p = Ap[j]; p < Ap[j + 1]; p++) { |
| 177 | i = Ai[p]; |
| 178 | |
| 179 | if (i > j) continue; /* skip lower triangular part of A */ |
| 180 | i2 = pinv ? pinv[i] : i; /* row i of A is row i2 of C */ |
| 181 | w[c_max(i2, j2)]++; /* column count of C */ |
| 182 | } |
| 183 | } |
| 184 | csc_cumsum(Cp, w, n); /* compute column pointers of C */ |
| 185 | |
| 186 | for (j = 0; j < n; j++) { |
| 187 | j2 = pinv ? pinv[j] : j; /* column j of A is column j2 of C */ |
| 188 | |
| 189 | for (p = Ap[j]; p < Ap[j + 1]; p++) { |
| 190 | i = Ai[p]; |
| 191 | |
| 192 | if (i > j) continue; /* skip lower triangular |
| 193 | part of A*/ |
| 194 | i2 = pinv ? pinv[i] : i; /* row i of A is row i2 |
| 195 | of C */ |
| 196 | Ci[q = w[c_max(i2, j2)]++] = c_min(i2, j2); |
| 197 | |
| 198 | if (Cx) Cx[q] = Ax[p]; |
| 199 | |
| 200 | if (AtoC) { // If vector AtoC passed, store values of the mappings |
| 201 | AtoC[p] = q; |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | return csc_done(C, w, OSQP_NULL, 1); /* success; free workspace, return C */ |
| 206 | } |
| 207 | |
| 208 | csc* copy_csc_mat(const csc *A) { |
| 209 | csc *B = csc_spalloc(A->m, A->n, A->p[A->n], 1, 0); |
| 210 | |
| 211 | if (!B) return OSQP_NULL; |
| 212 | |
| 213 | prea_int_vec_copy(A->p, B->p, A->n + 1); |
| 214 | prea_int_vec_copy(A->i, B->i, A->p[A->n]); |
| 215 | prea_vec_copy(A->x, B->x, A->p[A->n]); |
| 216 | |
| 217 | return B; |
| 218 | } |
| 219 | |
| 220 | void prea_copy_csc_mat(const csc *A, csc *B) { |
| 221 | prea_int_vec_copy(A->p, B->p, A->n + 1); |
| 222 | prea_int_vec_copy(A->i, B->i, A->p[A->n]); |
| 223 | prea_vec_copy(A->x, B->x, A->p[A->n]); |
| 224 | |
| 225 | B->nzmax = A->nzmax; |
| 226 | } |
| 227 | |
| 228 | csc* csc_done(csc *C, void *w, void *x, c_int ok) { |
| 229 | c_free(w); /* free workspace */ |
| 230 | c_free(x); |
| 231 | if (ok) return C; |
| 232 | else { |
| 233 | csc_spfree(C); |
| 234 | return OSQP_NULL; |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | csc* csc_to_triu(csc *M) { |
| 239 | csc *M_trip; // Matrix in triplet format |
| 240 | csc *M_triu; // Resulting upper triangular matrix |
| 241 | c_int nnzorigM; // Number of nonzeros from original matrix M |
| 242 | c_int nnzmaxM; // Estimated maximum number of elements of upper triangular M |
| 243 | c_int n; // Dimension of M |
| 244 | c_int ptr, i, j; // Counters for (i,j) and index in M |
| 245 | c_int z_M = 0; // Counter for elements in M_trip |
| 246 | |
| 247 | |
| 248 | // Check if matrix is square |
| 249 | if (M->m != M->n) { |
| 250 | #ifdef PRINTING |
| 251 | c_eprint("Matrix M not square"); |
| 252 | #endif /* ifdef PRINTING */ |
| 253 | return OSQP_NULL; |
| 254 | } |
| 255 | n = M->n; |
| 256 | |
| 257 | // Get number of nonzeros full M |
| 258 | nnzorigM = M->p[n]; |
| 259 | |
| 260 | // Estimate nnzmaxM |
| 261 | // Number of nonzero elements in original M + diagonal part. |
| 262 | // -> Full matrix M as input: estimate is half the number of total elements + |
| 263 | // diagonal = .5 * (nnzorigM + n) |
| 264 | // -> Upper triangular matrix M as input: estimate is the number of total |
| 265 | // elements + diagonal = nnzorigM + n |
| 266 | // The maximum between the two is nnzorigM + n |
| 267 | nnzmaxM = nnzorigM + n; |
| 268 | |
| 269 | // OLD |
| 270 | // nnzmaxM = n*(n+1)/2; // Full upper triangular matrix (This version |
| 271 | // allocates too much memory!) |
| 272 | // nnzmaxM = .5 * (nnzorigM + n); // half of the total elements + diagonal |
| 273 | |
| 274 | // Allocate M_trip |
| 275 | M_trip = csc_spalloc(n, n, nnzmaxM, 1, 1); // Triplet format |
| 276 | |
| 277 | if (!M_trip) { |
| 278 | #ifdef PRINTING |
| 279 | c_eprint("Upper triangular matrix extraction failed (out of memory)"); |
| 280 | #endif /* ifdef PRINTING */ |
| 281 | return OSQP_NULL; |
| 282 | } |
| 283 | |
| 284 | // Fill M_trip with only elements in M which are in the upper triangular |
| 285 | for (j = 0; j < n; j++) { // Cycle over columns |
| 286 | for (ptr = M->p[j]; ptr < M->p[j + 1]; ptr++) { |
| 287 | // Get row index |
| 288 | i = M->i[ptr]; |
| 289 | |
| 290 | // Assign element only if in the upper triangular |
| 291 | if (i <= j) { |
| 292 | // c_print("\nM(%i, %i) = %.4f", M->i[ptr], j, M->x[ptr]); |
| 293 | |
| 294 | M_trip->i[z_M] = i; |
| 295 | M_trip->p[z_M] = j; |
| 296 | M_trip->x[z_M] = M->x[ptr]; |
| 297 | |
| 298 | // Increase counter for the number of elements |
| 299 | z_M++; |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | // Set number of nonzeros |
| 305 | M_trip->nz = z_M; |
| 306 | |
| 307 | // Convert triplet matrix to csc format |
| 308 | M_triu = triplet_to_csc(M_trip, OSQP_NULL); |
| 309 | |
| 310 | // Assign number of nonzeros of full matrix to triu M |
| 311 | M_triu->nzmax = nnzmaxM; |
| 312 | |
| 313 | // Cleanup and return result |
| 314 | csc_spfree(M_trip); |
| 315 | |
| 316 | // Return matrix in triplet form |
| 317 | return M_triu; |
| 318 | } |