scs 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +1 -1
  4. data/lib/scs/ffi.rb +18 -0
  5. data/lib/scs/version.rb +1 -1
  6. data/vendor/scs/CITATION.cff +2 -3
  7. data/vendor/scs/CLAUDE.md +115 -0
  8. data/vendor/scs/CMakeLists.txt +330 -201
  9. data/vendor/scs/CONTRIBUTING.md +49 -0
  10. data/vendor/scs/Makefile +90 -43
  11. data/vendor/scs/README.md +96 -2
  12. data/vendor/scs/include/aa.h +59 -11
  13. data/vendor/scs/include/aa_stats.h +47 -0
  14. data/vendor/scs/include/cones.h +16 -7
  15. data/vendor/scs/include/glbopts.h +113 -26
  16. data/vendor/scs/include/linalg.h +5 -1
  17. data/vendor/scs/include/linsys.h +1 -2
  18. data/vendor/scs/include/normalize.h +6 -2
  19. data/vendor/scs/include/rw.h +9 -3
  20. data/vendor/scs/include/scs.h +17 -1
  21. data/vendor/scs/include/scs_blas.h +8 -0
  22. data/vendor/scs/include/scs_types.h +2 -2
  23. data/vendor/scs/include/scs_work.h +22 -1
  24. data/vendor/scs/include/util.h +3 -6
  25. data/vendor/scs/include/util_spectral_cones.h +3 -3
  26. data/vendor/scs/linsys/accelerate/direct/private.c +126 -0
  27. data/vendor/scs/linsys/accelerate/direct/private.h +34 -0
  28. data/vendor/scs/linsys/cpu/dense/private.c +250 -0
  29. data/vendor/scs/linsys/cpu/dense/private.h +31 -0
  30. data/vendor/scs/linsys/cpu/direct/private.c +86 -44
  31. data/vendor/scs/linsys/cpu/indirect/private.c +647 -113
  32. data/vendor/scs/linsys/cpu/indirect/private.h +28 -0
  33. data/vendor/scs/linsys/csparse.c +9 -3
  34. data/vendor/scs/linsys/csparse.h +4 -2
  35. data/vendor/scs/linsys/cudss/direct/private.c +77 -43
  36. data/vendor/scs/linsys/cudss/direct/private.h +33 -7
  37. data/vendor/scs/linsys/gpu/indirect/private.c +6 -2
  38. data/vendor/scs/linsys/mkl/direct/private.c +63 -31
  39. data/vendor/scs/linsys/mkl/direct/private.h +0 -1
  40. data/vendor/scs/linsys/scs_matrix.c +291 -165
  41. data/vendor/scs/linsys/scs_matrix.h +7 -9
  42. data/vendor/scs/scs.mk +35 -13
  43. data/vendor/scs/src/aa.c +673 -166
  44. data/vendor/scs/src/cones.c +545 -223
  45. data/vendor/scs/src/ctrlc.c +59 -16
  46. data/vendor/scs/src/exp_cone.c +70 -50
  47. data/vendor/scs/src/linalg.c +21 -2
  48. data/vendor/scs/src/normalize.c +24 -26
  49. data/vendor/scs/src/rw.c +596 -124
  50. data/vendor/scs/src/scs.c +990 -513
  51. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_IPM.c +240 -187
  52. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_Newton.c +108 -85
  53. data/vendor/scs/src/spectral_cones/logdeterminant/log_cone_wrapper.c +62 -63
  54. data/vendor/scs/src/spectral_cones/logdeterminant/logdet_cone.c +85 -78
  55. data/vendor/scs/src/spectral_cones/nuclear/ell1_cone.c +92 -97
  56. data/vendor/scs/src/spectral_cones/nuclear/nuclear_cone.c +39 -28
  57. data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_cone.c +59 -40
  58. data/vendor/scs/src/spectral_cones/sum-largest/sum_largest_eval_cone.c +37 -29
  59. data/vendor/scs/src/spectral_cones/util_spectral_cones.c +12 -6
  60. data/vendor/scs/src/util.c +37 -7
  61. data/vendor/scs/test/mkl_interface_mismatch.c +97 -0
  62. data/vendor/scs/test/packaging/CMakeLists.txt +21 -0
  63. data/vendor/scs/test/packaging/consume.c +50 -0
  64. data/vendor/scs/test/problem_utils.h +9 -2
  65. data/vendor/scs/test/problems/dense_qp.h +64 -0
  66. data/vendor/scs/test/problems/hs21_tiny_qp.h +6 -2
  67. data/vendor/scs/test/problems/hs21_tiny_qp_rw.h +10 -2
  68. data/vendor/scs/test/problems/infeasible_lp.h +66 -0
  69. data/vendor/scs/test/problems/infeasible_socp.h +75 -0
  70. data/vendor/scs/test/problems/lp_update.h +110 -0
  71. data/vendor/scs/test/problems/qafiro_tiny_qp.h +3 -1
  72. data/vendor/scs/test/problems/rob_gauss_cov_est.h +6 -3
  73. data/vendor/scs/test/problems/small_qp.h +5 -0
  74. data/vendor/scs/test/problems/test_box_cone.h +92 -0
  75. data/vendor/scs/test/problems/test_dual_exp_cone.h +96 -0
  76. data/vendor/scs/test/problems/test_inaccurate.h +212 -0
  77. data/vendor/scs/test/problems/test_mixed_cones.h +113 -0
  78. data/vendor/scs/test/problems/test_normalize_roundtrip.h +279 -0
  79. data/vendor/scs/test/problems/test_power_cone.h +414 -0
  80. data/vendor/scs/test/problems/test_psd_metric.h +109 -0
  81. data/vendor/scs/test/problems/test_psd_n1.h +84 -0
  82. data/vendor/scs/test/problems/test_root_plus.h +191 -0
  83. data/vendor/scs/test/problems/test_rw_settings.h +112 -0
  84. data/vendor/scs/test/problems/test_soc_sizes.h +393 -0
  85. data/vendor/scs/test/problems/test_solver_options.h +558 -0
  86. data/vendor/scs/test/problems/test_validation.h +263 -24
  87. data/vendor/scs/test/problems/test_zero_cone.h +81 -0
  88. data/vendor/scs/test/problems/unbounded_lp.h +66 -0
  89. data/vendor/scs/test/problems/unbounded_socp.h +71 -0
  90. data/vendor/scs/test/run_from_file.c +6 -0
  91. data/vendor/scs/test/run_tests.c +71 -0
  92. data/vendor/scs/test/rw_settings.c +7 -0
  93. data/vendor/scs/test/spectral_cones_problems/test_ell1_and_nuc.h +128 -0
  94. data/vendor/scs/test/spectral_cones_problems/test_ell1_cone.h +115 -0
  95. metadata +35 -3
@@ -25,6 +25,34 @@ struct SCS_LIN_SYS_WORK {
25
25
  /* preconditioning */
26
26
  scs_float *z;
27
27
  scs_float *M;
28
+ /* eigCG deflation (Stathopoulos & Orginos 2010). During the deep
29
+ * solve for g = K^{-1}h (cold, run to CG_BEST_TOL, once per metric
30
+ * change) the CG recurrence is observed -- never perturbed -- and
31
+ * approximate eigenvectors of the smallest eigenvalues of the
32
+ * preconditioned operator are extracted by thick-restarted Lanczos on
33
+ * CG's own scalars. Later warm solves Galerkin-project their initial
34
+ * residual against this near-invariant subspace, which removes the
35
+ * slowly-converging components; because the subspace is
36
+ * near-invariant, CG does not reintroduce them. Discarded whenever
37
+ * the metric changes; the deep re-solve re-harvests. */
38
+ scs_float *dfl_p; /* W: harvested eigenvectors, n x dfl_max */
39
+ scs_float *dfl_Ap; /* A W images, n x dfl_max */
40
+ scs_float *dfl_G; /* Cholesky factor of W'AW, dfl_max x dfl_max */
41
+ scs_float *dfl_c; /* projection coefficients, dfl_max */
42
+ scs_int dfl_max, dfl_count, dfl_harvest;
43
+ /* eigCG window state (live only while harvesting) */
44
+ scs_float *eig_V; /* n x eig_win basis (Lanczos / locked Ritz) */
45
+ scs_float *eig_T; /* eig_win x eig_win projected operator */
46
+ scs_float *eig_E; /* eig_win x eig_win syev scratch */
47
+ scs_float *eig_w; /* eigenvalue scratch */
48
+ scs_float *eig_S; /* eig_win x 2 dfl_max restart stack */
49
+ scs_float *eig_TS; /* eig_win x 2 dfl_max scratch */
50
+ scs_float *eig_H; /* 2 dfl_max x 2 dfl_max small projection */
51
+ scs_float *eig_G; /* eig_win x 2 dfl_max restart transform */
52
+ scs_float *eig_VS; /* n x 2 dfl_max basis-update scratch */
53
+ scs_float *eig_work; /* syev workspace */
54
+ scs_int eig_win, eig_j, eig_jd, eig_step, eig_have_prev, eig_dead, eig_lwork;
55
+ scs_float eig_pa, eig_pb; /* previous iteration's alpha / beta */
28
56
  /* reporting */
29
57
  scs_int tot_cg_its;
30
58
  const scs_float *diag_r;
@@ -205,11 +205,17 @@ ScsMatrix *SCS(form_kkt)(const ScsMatrix *A, const ScsMatrix *P,
205
205
  }
206
206
 
207
207
  idx_mapping = (scs_int *)scs_calloc(count, sizeof(scs_int));
208
- Kcsc = SCS(cs_compress)(K, count, idx_mapping);
209
- for (i = 0; i < m + n; i++) {
210
- diag_r_idxs[i] = idx_mapping[diag_r_idxs[i]];
208
+ if (!idx_mapping) {
209
+ SCS(cs_spfree)(K);
210
+ return SCS_NULL;
211
211
  }
212
+ Kcsc = SCS(cs_compress)(K, count, idx_mapping);
212
213
  SCS(cs_spfree)(K);
214
+ if (Kcsc) {
215
+ for (i = 0; i < m + n; i++) {
216
+ diag_r_idxs[i] = idx_mapping[diag_r_idxs[i]];
217
+ }
218
+ }
213
219
  scs_free(idx_mapping);
214
220
  return Kcsc;
215
221
  }
@@ -1,4 +1,7 @@
1
- /* Routines modified from CSparse, T. Davis et al */
1
+ /*
2
+ * Sparse matrix utilities adapted from CSparse (T. Davis).
3
+ * Provides allocation, triplet-to-CSC compression, and KKT matrix formation.
4
+ */
2
5
 
3
6
  #ifndef CS_H_GUARD
4
7
  #define CS_H_GUARD
@@ -8,7 +11,6 @@ extern "C" {
8
11
  #endif
9
12
 
10
13
  #include "glbopts.h"
11
- #include "scs.h"
12
14
 
13
15
  ScsMatrix *SCS(cs_spalloc)(scs_int m, scs_int n, scs_int nzmax, scs_int values,
14
16
  scs_int triplet);
@@ -1,5 +1,6 @@
1
1
  #include "private.h"
2
2
  #include "linsys.h"
3
+ #include <string.h>
3
4
 
4
5
  /* In case of error abort freeing p */
5
6
  #define CUDSS_CHECK_ABORT(call, p, fname) \
@@ -31,7 +32,23 @@ const char *scs_get_lin_sys_method() {
31
32
  /* Free allocated resources for the linear system solver */
32
33
  void scs_free_lin_sys_work(ScsLinSysWork *p) {
33
34
  if (p) {
34
- /* Free GPU resources */
35
+ /* Free cuDSS resources first, before freeing the GPU memory they reference */
36
+ if (p->solver_data && p->handle)
37
+ cudssDataDestroy(p->handle, p->solver_data);
38
+ if (p->solver_config)
39
+ cudssConfigDestroy(p->solver_config);
40
+
41
+ if (p->d_kkt_mat)
42
+ cudssMatrixDestroy(p->d_kkt_mat);
43
+ if (p->d_b_mat)
44
+ cudssMatrixDestroy(p->d_b_mat);
45
+ if (p->d_sol_mat)
46
+ cudssMatrixDestroy(p->d_sol_mat);
47
+
48
+ if (p->handle)
49
+ cudssDestroy(p->handle);
50
+
51
+ /* Free GPU memory */
35
52
  if (p->d_kkt_val)
36
53
  cudaFree(p->d_kkt_val);
37
54
  if (p->d_kkt_row_ptr)
@@ -43,26 +60,15 @@ void scs_free_lin_sys_work(ScsLinSysWork *p) {
43
60
  if (p->d_sol)
44
61
  cudaFree(p->d_sol);
45
62
 
46
- /* Free cuDSS resources */
47
- if (p->d_kkt_mat)
48
- cudssMatrixDestroy(p->d_kkt_mat);
49
- if (p->d_b_mat)
50
- cudssMatrixDestroy(p->d_b_mat);
51
- if (p->d_sol_mat)
52
- cudssMatrixDestroy(p->d_sol_mat);
53
-
54
- if (p->solver_config)
55
- cudssConfigDestroy(p->solver_config);
56
- if (p->solver_data && p->handle)
57
- cudssDataDestroy(p->handle, p->solver_data);
58
- if (p->handle)
59
- cudssDestroy(p->handle);
63
+ /* Free pinned host memory */
64
+ if (p->h_b_pinned)
65
+ cudaFreeHost(p->h_b_pinned);
66
+ if (p->h_sol_pinned)
67
+ cudaFreeHost(p->h_sol_pinned);
60
68
 
61
69
  /* Free CPU resources */
62
70
  if (p->kkt)
63
71
  SCS(cs_spfree)(p->kkt);
64
- if (p->sol)
65
- scs_free(p->sol);
66
72
  if (p->diag_r_idxs)
67
73
  scs_free(p->diag_r_idxs);
68
74
  if (p->diag_p)
@@ -85,12 +91,6 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
85
91
  p->n_plus_m = p->n + p->m;
86
92
 
87
93
  /* Allocate CPU memory */
88
- p->sol = (scs_float *)scs_malloc(sizeof(scs_float) * p->n_plus_m);
89
- if (!p->sol) {
90
- scs_free_lin_sys_work(p);
91
- return SCS_NULL;
92
- }
93
-
94
94
  p->diag_r_idxs = (scs_int *)scs_calloc(p->n_plus_m, sizeof(scs_int));
95
95
  if (!p->diag_r_idxs) {
96
96
  scs_free_lin_sys_work(p);
@@ -112,9 +112,6 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
112
112
  return SCS_NULL;
113
113
  }
114
114
 
115
- cudssStatus_t status;
116
- cudaError_t cuda_error;
117
-
118
115
  /* Create cuDSS handle */
119
116
  CUDSS_CHECK_ABORT(cudssCreate(&p->handle), p, "cudssCreate");
120
117
  /* Creating cuDSS solver configuration and data objects */
@@ -154,11 +151,23 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
154
151
  cudssMatrixType_t mtype = CUDSS_MTYPE_SYMMETRIC;
155
152
  cudssMatrixViewType_t mview = CUDSS_MVIEW_LOWER;
156
153
  cudssIndexBase_t base = CUDSS_BASE_ZERO;
154
+ /* cuDSS 0.8.0+ added an offsetType parameter before indexType. The two
155
+ * arrays use the same scs_int type for us, so we pass SCS_CUDA_INDEX
156
+ * twice. */
157
+ #if SCS_CUDSS_NEW_API
158
+ CUDSS_CHECK_ABORT(cudssMatrixCreateCsr(
159
+ &p->d_kkt_mat, p->kkt->m, p->kkt->n, nnz,
160
+ p->d_kkt_row_ptr, NULL, p->d_kkt_col_ind, p->d_kkt_val,
161
+ SCS_CUDA_INDEX, SCS_CUDA_INDEX, SCS_CUDA_FLOAT,
162
+ mtype, mview, base),
163
+ p, "cudssMatrixCreateCsr");
164
+ #else
157
165
  CUDSS_CHECK_ABORT(cudssMatrixCreateCsr(
158
166
  &p->d_kkt_mat, p->kkt->m, p->kkt->n, nnz,
159
167
  p->d_kkt_row_ptr, NULL, p->d_kkt_col_ind, p->d_kkt_val,
160
168
  SCS_CUDA_INDEX, SCS_CUDA_FLOAT, mtype, mview, base),
161
169
  p, "cudssMatrixCreateCsr");
170
+ #endif
162
171
 
163
172
  /* Allocate device memory for vectors */
164
173
  CUDA_CHECK_ABORT(
@@ -168,6 +177,14 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
168
177
  cudaMalloc((void **)&p->d_sol, p->n_plus_m * sizeof(scs_float)), p,
169
178
  "cudaMalloc: sol");
170
179
 
180
+ /* Allocate pinned host memory for faster H<->D transfers in solve loop */
181
+ CUDA_CHECK_ABORT(
182
+ cudaMallocHost((void **)&p->h_b_pinned, p->n_plus_m * sizeof(scs_float)),
183
+ p, "cudaMallocHost: b");
184
+ CUDA_CHECK_ABORT(cudaMallocHost((void **)&p->h_sol_pinned,
185
+ p->n_plus_m * sizeof(scs_float)),
186
+ p, "cudaMallocHost: sol");
187
+
171
188
  /* Create RHS and solution matrix descriptors */
172
189
  scs_int nrhs = 1;
173
190
  CUDSS_CHECK_ABORT(cudssMatrixCreateDn(&p->d_b_mat, p->n_plus_m, nrhs,
@@ -191,24 +208,39 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
191
208
  p->d_sol_mat, p->d_b_mat),
192
209
  p, "cudssExecute: factorization");
193
210
 
211
+ /* Inertia check: verify P is PSD by checking positive eigenvalue count */
212
+ {
213
+ scs_int inertia[2]; /* [0] = positive, [1] = negative */
214
+ size_t inertia_written;
215
+ CUDSS_CHECK_ABORT(
216
+ cudssDataGet(p->handle, p->solver_data, CUDSS_DATA_INERTIA, inertia,
217
+ sizeof(inertia), &inertia_written),
218
+ p, "cudssDataGet: inertia");
219
+ if (inertia[0] < p->n) {
220
+ scs_printf("KKT matrix has < n positive eigenvalues. P not PSD.");
221
+ scs_free_lin_sys_work(p);
222
+ return SCS_NULL;
223
+ }
224
+ }
225
+
194
226
  return p;
195
227
  }
196
228
 
197
229
  /* Solve the linear system for a given RHS b */
198
230
  scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *ws,
199
231
  scs_float tol) {
200
- /* Copy right-hand side to device */
201
- cudaError_t custatus = cudaMemcpy(p->d_b, b, p->n_plus_m * sizeof(scs_float),
202
- cudaMemcpyHostToDevice);
232
+ size_t vec_bytes = p->n_plus_m * sizeof(scs_float);
233
+
234
+ /* Copy RHS to pinned staging buffer, then to device */
235
+ memcpy(p->h_b_pinned, b, vec_bytes);
236
+ cudaError_t custatus =
237
+ cudaMemcpy(p->d_b, p->h_b_pinned, vec_bytes, cudaMemcpyHostToDevice);
203
238
  if (custatus != cudaSuccess) {
204
- scs_printf("scs_solve_lin_sys: Error copying `b` side to device: %d\n",
239
+ scs_printf("scs_solve_lin_sys: Error copying b to device: %d\n",
205
240
  (int)custatus);
206
241
  return custatus;
207
242
  }
208
243
 
209
- // is this really needed?
210
- cudssMatrixSetValues(p->d_b_mat, p->d_b);
211
-
212
244
  /* Solve the system */
213
245
  cudssStatus_t status =
214
246
  cudssExecute(p->handle, CUDSS_PHASE_SOLVE, p->solver_config,
@@ -219,20 +251,21 @@ scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *ws,
219
251
  return status;
220
252
  }
221
253
 
222
- /* Copy solution back to host */
223
- custatus = cudaMemcpy(b, p->d_sol, p->n_plus_m * sizeof(scs_float),
224
- cudaMemcpyDeviceToHost);
225
- if (status != cudaSuccess) {
254
+ /* Copy solution from device to pinned staging buffer, then to output */
255
+ custatus =
256
+ cudaMemcpy(p->h_sol_pinned, p->d_sol, vec_bytes, cudaMemcpyDeviceToHost);
257
+ if (custatus != cudaSuccess) {
226
258
  scs_printf("scs_solve_lin_sys: Error copying d_sol to host: %d\n",
227
- (int)status);
228
- return status;
259
+ (int)custatus);
260
+ return custatus;
229
261
  }
262
+ memcpy(b, p->h_sol_pinned, vec_bytes);
230
263
 
231
264
  return 0; /* Success */
232
265
  }
233
266
 
234
267
  /* Update the KKT matrix when R changes */
235
- void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
268
+ scs_int scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
236
269
  scs_int i;
237
270
 
238
271
  /* Update KKT matrix on CPU */
@@ -253,7 +286,7 @@ void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
253
286
  scs_printf(
254
287
  "scs_update_lin_sys_diag_r: Error copying kkt->x to device: %d\n",
255
288
  (int)custatus);
256
- return;
289
+ return (scs_int)custatus;
257
290
  }
258
291
 
259
292
  /* Update the matrix values in cuDSS */
@@ -264,7 +297,7 @@ void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
264
297
  scs_printf(
265
298
  "scs_update_lin_sys_diag_r: Error updating kkt matrix on device: %d\n",
266
299
  (int)status);
267
- return;
300
+ return (scs_int)status;
268
301
  }
269
302
 
270
303
  /* Perform Refactorization with the updated matrix */
@@ -274,6 +307,7 @@ void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
274
307
  if (status != CUDSS_STATUS_SUCCESS) {
275
308
  scs_printf("scs_update_lin_sys_diag_r: Error during re-factorization: %d\n",
276
309
  (int)status);
277
- return;
310
+ return (scs_int)status;
278
311
  }
312
+ return 0;
279
313
  }
@@ -5,22 +5,45 @@
5
5
  extern "C" {
6
6
  #endif
7
7
 
8
+ #include "csparse.h"
9
+ #include "linsys.h"
10
+ #include <cuda_runtime.h>
11
+ #include <cudss.h>
12
+
13
+ /* cuDSS 0.8.0 renamed cudaDataType_t to cudssDataType_t (so CUDA_R_* enum
14
+ * values become CUDSS_R_*) and added an offsetType parameter to
15
+ * cudssMatrixCreateCsr. cudss.h exposes CUDSS_VERSION = MAJOR*10000 +
16
+ * MINOR*100 + PATCH; gate on that so the same source still builds against
17
+ * older cuDSS (<=0.7.x). */
18
+ #if defined(CUDSS_VERSION) && CUDSS_VERSION >= 800
19
+ #define SCS_CUDSS_NEW_API 1
20
+ #else
21
+ #define SCS_CUDSS_NEW_API 0
22
+ #endif
23
+
24
+ #if SCS_CUDSS_NEW_API
25
+ #ifndef SFLOAT
26
+ #define SCS_CUDA_FLOAT CUDSS_R_64F
27
+ #else
28
+ #define SCS_CUDA_FLOAT CUDSS_R_32F
29
+ #endif
30
+ #ifndef DLONG
31
+ #define SCS_CUDA_INDEX CUDSS_R_32I
32
+ #else
33
+ #define SCS_CUDA_INDEX CUDSS_R_64I
34
+ #endif
35
+ #else
8
36
  #ifndef SFLOAT
9
37
  #define SCS_CUDA_FLOAT CUDA_R_64F
10
38
  #else
11
39
  #define SCS_CUDA_FLOAT CUDA_R_32F
12
40
  #endif
13
-
14
41
  #ifndef DLONG
15
42
  #define SCS_CUDA_INDEX CUDA_R_32I
16
43
  #else
17
44
  #define SCS_CUDA_INDEX CUDA_R_64I
18
45
  #endif
19
-
20
- #include "csparse.h"
21
- #include "linsys.h"
22
- #include <cuda_runtime.h>
23
- #include <cudss.h>
46
+ #endif
24
47
 
25
48
  struct SCS_LIN_SYS_WORK {
26
49
  /* General problem dimensions */
@@ -30,7 +53,6 @@ struct SCS_LIN_SYS_WORK {
30
53
 
31
54
  /* CPU matrices and vectors */
32
55
  ScsMatrix *kkt; /* KKT matrix in CSR format */
33
- scs_float *sol; /* solution to the KKT system */
34
56
 
35
57
  /* cuDSS handle and descriptors */
36
58
  cudssHandle_t handle; /* cuDSS library handle */
@@ -47,6 +69,10 @@ struct SCS_LIN_SYS_WORK {
47
69
  scs_float *d_b; /* device copy of right-hand side */
48
70
  scs_float *d_sol; /* device copy of solution */
49
71
 
72
+ /* Pinned host memory for faster H<->D transfers */
73
+ scs_float *h_b_pinned; /* pinned host staging buffer for RHS */
74
+ scs_float *h_sol_pinned; /* pinned host staging buffer for solution */
75
+
50
76
  /* These are required for matrix updates */
51
77
  scs_int *diag_r_idxs; /* indices where R appears in the KKT matrix */
52
78
  scs_float *diag_p; /* Diagonal values of P */
@@ -66,7 +66,7 @@ static void set_preconditioner(ScsLinSysWork *p, const scs_float *diag_r) {
66
66
  }
67
67
 
68
68
  /* no need to update anything in this case */
69
- void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
69
+ scs_int scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
70
70
  scs_int i;
71
71
 
72
72
  /* R_x to gpu */
@@ -81,6 +81,7 @@ void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
81
81
 
82
82
  /* set preconditioner M on gpu */
83
83
  set_preconditioner(p, diag_r);
84
+ return 0;
84
85
  }
85
86
 
86
87
  void scs_free_lin_sys_work(ScsLinSysWork *p) {
@@ -141,7 +142,7 @@ static void mat_vec(ScsLinSysWork *p, const scs_float *x, scs_float *y) {
141
142
  cusparseDnVecSetValues(p->dn_vec_n_p, (void *)y);
142
143
 
143
144
  /* y = x */
144
- cudaMemcpy(y, x, p->n * sizeof(scs_float), cudaMemcpyHostToDevice);
145
+ cudaMemcpy(y, x, p->n * sizeof(scs_float), cudaMemcpyDeviceToDevice);
145
146
  /* y = R_x * x */
146
147
  scale_by_diag(p->cublas_handle, p->r_x_gpu, y, p->n);
147
148
 
@@ -430,6 +431,9 @@ static scs_int pcg(ScsLinSysWork *pr, const scs_float *s, scs_float *bg,
430
431
  ztr_prev = ztr;
431
432
  /* ztr = z'r */
432
433
  CUBLAS(dot)(cublas_handle, n, r, 1, z, 1, &ztr);
434
+ if (ztr_prev == 0.) {
435
+ break;
436
+ }
433
437
  beta = ztr / ztr_prev;
434
438
  /* p = beta * p, where beta = ztr / ztr_prev */
435
439
  CUBLAS(scal)(cublas_handle, n, &beta, p, 1);
@@ -5,25 +5,56 @@
5
5
  #define PARDISO_SOLVE (33)
6
6
  #define PARDISO_CLEANUP (-1)
7
7
 
8
- /* TODO: is it necessary to use pardiso_64 and MKL_Set_Interface_Layer ? */
9
8
  /*
9
+ * MKL interface layer constants. MKL has two integer interfaces:
10
+ *
11
+ * LP64 (MKL_INTERFACE_LP64 = 0): BLAS/LAPACK use 32-bit integers (int).
12
+ * ILP64 (MKL_INTERFACE_ILP64 = 1): BLAS/LAPACK use 64-bit integers (long long).
13
+ *
14
+ * These affect the standard BLAS/LAPACK symbols (dgemm, dpotrf, etc.).
15
+ * PARDISO has separate entry points for each integer width:
16
+ *
17
+ * pardiso — 32-bit integer indices (used when !DLONG)
18
+ * pardiso_64 — 64-bit integer indices (used when DLONG)
19
+ *
20
+ * The pardiso/pardiso_64 choice is independent of the interface layer; each is
21
+ * a distinct symbol that always uses its own integer width regardless of what
22
+ * MKL_Set_Interface_Layer says.
23
+ *
24
+ * The BLAS integer width is controlled by the use_blas64 meson option:
25
+ *
26
+ * use_blas64=false (default) -> links mkl-dynamic-lp64-*, expects LP64
27
+ * use_blas64=true -> links mkl-dynamic-ilp64-*, expects ILP64
28
+ *
29
+ * See meson.build for the linkage logic. The MKL-specific initialization in
30
+ * src/scs.c uses BLAS64 to pick the right expected interface layer.
31
+ *
32
+ * PARDISO is independent: pardiso_64 always uses 64-bit ints regardless of
33
+ * the interface layer or BLAS64 setting.
34
+ */
10
35
  #define MKL_INTERFACE_LP64 0
11
36
  #define MKL_INTERFACE_ILP64 1
12
- */
37
+
13
38
  #ifdef DLONG
14
39
  #define _PARDISO pardiso_64
15
40
  #else
16
41
  #define _PARDISO pardiso
17
42
  #endif
18
43
 
19
- /* Prototypes for Pardiso functions */
44
+ /* Prototypes for Pardiso and MKL service functions. */
20
45
  void _PARDISO(void **pt, const scs_int *maxfct, const scs_int *mnum,
21
46
  const scs_int *mtype, const scs_int *phase, const scs_int *n,
22
47
  const scs_float *a, const scs_int *ia, const scs_int *ja,
23
48
  scs_int *perm, const scs_int *nrhs, scs_int *iparm,
24
49
  const scs_int *msglvl, scs_float *b, scs_float *x,
25
50
  scs_int *error);
26
- /* scs_int MKL_Set_Interface_Layer(scs_int); */
51
+ /* BLAS64 requires DLONG for MKL builds: the interface layer (ILP64) set by
52
+ * MKL_Set_Interface_Layer must match the PARDISO entry point (pardiso_64).
53
+ * Without DLONG, pardiso (32-bit) is used but ILP64 makes its internal BLAS
54
+ * calls expect 64-bit integers, causing hangs or memory corruption. */
55
+ #if defined(BLAS64) && !defined(DLONG)
56
+ #error "MKL PARDISO requires DLONG when BLAS64 is set (pardiso_64 needs 64-bit ints)"
57
+ #endif
27
58
 
28
59
  const char *scs_get_lin_sys_method() {
29
60
  return "sparse-direct-mkl-pardiso";
@@ -31,18 +62,21 @@ const char *scs_get_lin_sys_method() {
31
62
 
32
63
  void scs_free_lin_sys_work(ScsLinSysWork *p) {
33
64
  if (p) {
34
- p->phase = PARDISO_CLEANUP;
35
- _PARDISO(p->pt, &(p->maxfct), &(p->mnum), &(p->mtype), &(p->phase),
36
- &(p->n_plus_m), SCS_NULL, p->kkt->p, p->kkt->i, SCS_NULL,
37
- &(p->nrhs), p->iparm, &(p->msglvl), SCS_NULL, SCS_NULL,
38
- &(p->error));
39
- if (p->error != 0) {
40
- scs_printf("Error during MKL Pardiso cleanup: %d", (int)p->error);
41
- }
42
- if (p->kkt)
65
+ /* Pardiso cleanup needs the KKT index arrays; on early allocation
66
+ * failures the workspace may be freed before the KKT matrix (or
67
+ * the factorization) ever existed, so gate the solver teardown on
68
+ * what was actually initialized. */
69
+ if (p->kkt) {
70
+ p->phase = PARDISO_CLEANUP;
71
+ _PARDISO(p->pt, &(p->maxfct), &(p->mnum), &(p->mtype), &(p->phase),
72
+ &(p->n_plus_m), SCS_NULL, p->kkt->p, p->kkt->i, SCS_NULL,
73
+ &(p->nrhs), p->iparm, &(p->msglvl), SCS_NULL, SCS_NULL,
74
+ &(p->error));
75
+ if (p->error != 0) {
76
+ scs_printf("Error during MKL Pardiso cleanup: %d", (int)p->error);
77
+ }
43
78
  SCS(cs_spfree)(p->kkt);
44
- if (p->sol)
45
- scs_free(p->sol);
79
+ }
46
80
  if (p->diag_r_idxs)
47
81
  scs_free(p->diag_r_idxs);
48
82
  if (p->diag_p)
@@ -55,24 +89,18 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
55
89
  const scs_float *diag_r) {
56
90
  scs_int i;
57
91
  ScsLinSysWork *p = scs_calloc(1, sizeof(ScsLinSysWork));
58
-
59
- /* TODO: is this necessary with pardiso_64? */
60
- /* Set MKL interface layer */
61
- /*
62
- #ifdef DLONG
63
- MKL_Set_Interface_Layer(MKL_INTERFACE_ILP64);
64
- #else
65
- MKL_Set_Interface_Layer(MKL_INTERFACE_LP64);
66
- #endif
67
- */
92
+ if (!p)
93
+ return SCS_NULL;
68
94
  p->n = A->n;
69
95
  p->m = A->m;
70
96
  p->n_plus_m = p->n + p->m;
71
97
 
72
- /* Even though we overwrite rhs with sol pardiso requires the memory */
73
- p->sol = (scs_float *)scs_malloc(sizeof(scs_float) * p->n_plus_m);
74
98
  p->diag_r_idxs = (scs_int *)scs_calloc(p->n_plus_m, sizeof(scs_int));
75
99
  p->diag_p = (scs_float *)scs_calloc(p->n, sizeof(scs_float));
100
+ if (!p->diag_r_idxs || !p->diag_p) {
101
+ scs_free_lin_sys_work(p);
102
+ return SCS_NULL;
103
+ }
76
104
 
77
105
  /* MKL pardiso requires upper triangular CSR matrices. The KKT matrix stuffed
78
106
  * as CSC lower triangular is equivalent. Pass upper=0. */
@@ -101,7 +129,9 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
101
129
  p->iparm[1] = 3; /* Fill-in reordering from OpenMP */
102
130
  p->iparm[5] = 1; /* Write solution into b */
103
131
  p->iparm[7] = 0; /* Automatic iterative refinement calculation */
104
- p->iparm[9] = 8; /* Perturb the pivot elements with 1E-8 */
132
+ p->iparm[9] = 13; /* Perturb the pivot elements with 1E-13 (default) */
133
+ p->iparm[23] = 1; /* Two-level scheduling for parallel factorization */
134
+ p->iparm[24] = 1; /* Parallel forward/backward solve */
105
135
  p->iparm[34] = 1; /* Use C-style indexing for indices */
106
136
  /* p->iparm[36] = -80; */ /* Form block sparse matrices */
107
137
 
@@ -135,6 +165,7 @@ ScsLinSysWork *scs_init_lin_sys_work(const ScsMatrix *A, const ScsMatrix *P,
135
165
 
136
166
  if (p->iparm[21] < p->n) {
137
167
  scs_printf("KKT matrix has < n positive eigenvalues. P not PSD.");
168
+ scs_free_lin_sys_work(p);
138
169
  return SCS_NULL;
139
170
  }
140
171
 
@@ -148,7 +179,7 @@ scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *ws,
148
179
  p->phase = PARDISO_SOLVE;
149
180
  _PARDISO(p->pt, &(p->maxfct), &(p->mnum), &(p->mtype), &(p->phase),
150
181
  &(p->n_plus_m), p->kkt->x, p->kkt->p, p->kkt->i, SCS_NULL,
151
- &(p->nrhs), p->iparm, &(p->msglvl), b, p->sol, &(p->error));
182
+ &(p->nrhs), p->iparm, &(p->msglvl), b, SCS_NULL, &(p->error));
152
183
  if (p->error != 0) {
153
184
  scs_printf("Error during linear system solution: %d", (int)p->error);
154
185
  }
@@ -156,7 +187,7 @@ scs_int scs_solve_lin_sys(ScsLinSysWork *p, scs_float *b, const scs_float *ws,
156
187
  }
157
188
 
158
189
  /* Update factorization when R changes */
159
- void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
190
+ scs_int scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
160
191
  scs_int i;
161
192
 
162
193
  for (i = 0; i < p->n; ++i) {
@@ -177,6 +208,7 @@ void scs_update_lin_sys_diag_r(ScsLinSysWork *p, const scs_float *diag_r) {
177
208
  if (p->error != 0) {
178
209
  scs_printf("Error in PARDISO factorization when updating: %d.\n",
179
210
  (int)p->error);
180
- scs_free_lin_sys_work(p);
211
+ return (scs_int)p->error;
181
212
  }
213
+ return 0;
182
214
  }
@@ -10,7 +10,6 @@ extern "C" {
10
10
 
11
11
  struct SCS_LIN_SYS_WORK {
12
12
  ScsMatrix *kkt; /* Upper triangular KKT matrix (in CSR format) */
13
- scs_float *sol; /* solution to the KKT system */
14
13
  scs_int n; /* number of QP variables */
15
14
  scs_int m; /* number of QP constraints */
16
15