blob: 8e8d04abcd3f316a6222de43aeb6b040e67205db [file] [log] [blame]
Austin Schuh9049e202022-02-20 17:34:16 -08001#include "util.h"
2
3/***************
4* Versioning *
5***************/
6const char* osqp_version(void) {
7 return OSQP_VERSION;
8}
9
10/************************************
11* Printing Constants to set Layout *
12************************************/
13#ifdef PRINTING
14# define HEADER_LINE_LEN 65
15#endif /* ifdef PRINTING */
16
17/**********************
18* Utility Functions *
19**********************/
20void c_strcpy(char dest[], const char source[]) {
21 int i = 0;
22
23 while (1) {
24 dest[i] = source[i];
25
26 if (dest[i] == '\0') break;
27 i++;
28 }
29}
30
31#ifdef PRINTING
32
33static void print_line(void) {
34 char the_line[HEADER_LINE_LEN + 1];
35 c_int i;
36
37 for (i = 0; i < HEADER_LINE_LEN; ++i) the_line[i] = '-';
38 the_line[HEADER_LINE_LEN] = '\0';
39 c_print("%s\n", the_line);
40}
41
42void print_header(void) {
43 // Different indentation required for windows
44#if defined(IS_WINDOWS) && !defined(PYTHON)
45 c_print("iter ");
46#else
47 c_print("iter ");
48#endif
49
50 // Main information
51 c_print("objective pri res dua res rho");
52# ifdef PROFILING
53 c_print(" time");
54# endif /* ifdef PROFILING */
55 c_print("\n");
56}
57
58void print_setup_header(const OSQPWorkspace *work) {
59 OSQPData *data;
60 OSQPSettings *settings;
61 c_int nnz; // Number of nonzeros in the problem
62
63 data = work->data;
64 settings = work->settings;
65
66 // Number of nonzeros
67 nnz = data->P->p[data->P->n] + data->A->p[data->A->n];
68
69 print_line();
70 c_print(" OSQP v%s - Operator Splitting QP Solver\n"
71 " (c) Bartolomeo Stellato, Goran Banjac\n"
72 " University of Oxford - Stanford University 2021\n",
73 OSQP_VERSION);
74 print_line();
75
76 // Print variables and constraints
77 c_print("problem: ");
78 c_print("variables n = %i, constraints m = %i\n ",
79 (int)data->n,
80 (int)data->m);
81 c_print("nnz(P) + nnz(A) = %i\n", (int)nnz);
82
83 // Print Settings
84 c_print("settings: ");
85 c_print("linear system solver = %s",
86 LINSYS_SOLVER_NAME[settings->linsys_solver]);
87
88 if (work->linsys_solver->nthreads != 1) {
89 c_print(" (%d threads)", (int)work->linsys_solver->nthreads);
90 }
91 c_print(",\n ");
92
93 c_print("eps_abs = %.1e, eps_rel = %.1e,\n ",
94 settings->eps_abs, settings->eps_rel);
95 c_print("eps_prim_inf = %.1e, eps_dual_inf = %.1e,\n ",
96 settings->eps_prim_inf, settings->eps_dual_inf);
97 c_print("rho = %.2e ", settings->rho);
98
99 if (settings->adaptive_rho) {
100 c_print("(adaptive)");
101 }
102 c_print(",\n ");
103 c_print("sigma = %.2e, alpha = %.2f, ",
104 settings->sigma, settings->alpha);
105 c_print("max_iter = %i\n", (int)settings->max_iter);
106
107 if (settings->check_termination) {
108 c_print(" check_termination: on (interval %i),\n",
109 (int)settings->check_termination);
110 } else {c_print(" check_termination: off,\n");}
111# ifdef PROFILING
112 if (settings->time_limit) {
113 c_print(" time_limit: %.2e sec,\n", settings->time_limit);
114 }
115# endif /* ifdef PROFILING */
116
117 if (settings->scaling) {
118 c_print(" scaling: on, ");
119 } else {
120 c_print(" scaling: off, ");
121 }
122
123 if (settings->scaled_termination) {
124 c_print("scaled_termination: on\n");
125 } else {
126 c_print("scaled_termination: off\n");
127 }
128
129 if (settings->warm_start) {
130 c_print(" warm start: on, ");
131 } else {
132 c_print(" warm start: off, ");
133 }
134
135 if (settings->polish) {
136 c_print("polish: on, ");
137 } else {
138 c_print("polish: off, ");
139 }
140
141# ifdef PROFILING
142 if (settings->time_limit) {
143 c_print("time_limit: %.2e sec\n", settings->time_limit);
144 } else {
145 c_print("time_limit: off\n");
146 }
147# endif
148
149 c_print("\n");
150}
151
152void print_summary(OSQPWorkspace *work) {
153 OSQPInfo *info;
154
155 info = work->info;
156
157 c_print("%4i", (int)info->iter);
158 c_print(" %12.4e", info->obj_val);
159 c_print(" %9.2e", info->pri_res);
160 c_print(" %9.2e", info->dua_res);
161 c_print(" %9.2e", work->settings->rho);
162# ifdef PROFILING
163
164 if (work->first_run) {
165 // total time: setup + solve
166 c_print(" %9.2es", info->setup_time + info->solve_time);
167 } else {
168 // total time: update + solve
169 c_print(" %9.2es", info->update_time + info->solve_time);
170 }
171# endif /* ifdef PROFILING */
172 c_print("\n");
173
174 work->summary_printed = 1; // Summary has been printed
175}
176
177void print_polish(OSQPWorkspace *work) {
178 OSQPInfo *info;
179
180 info = work->info;
181
182 c_print("%4s", "plsh");
183 c_print(" %12.4e", info->obj_val);
184 c_print(" %9.2e", info->pri_res);
185 c_print(" %9.2e", info->dua_res);
186
187 // Different characters for windows/unix
188#if defined(IS_WINDOWS) && !defined(PYTHON)
189 c_print(" ---------");
190#else
191 c_print(" --------");
192#endif
193
194# ifdef PROFILING
195 if (work->first_run) {
196 // total time: setup + solve
197 c_print(" %9.2es", info->setup_time + info->solve_time +
198 info->polish_time);
199 } else {
200 // total time: update + solve
201 c_print(" %9.2es", info->update_time + info->solve_time +
202 info->polish_time);
203 }
204# endif /* ifdef PROFILING */
205 c_print("\n");
206}
207
208void print_footer(OSQPInfo *info, c_int polish) {
209 c_print("\n"); // Add space after iterations
210
211 c_print("status: %s\n", info->status);
212
213 if (polish && (info->status_val == OSQP_SOLVED)) {
214 if (info->status_polish == 1) {
215 c_print("solution polish: successful\n");
216 } else if (info->status_polish < 0) {
217 c_print("solution polish: unsuccessful\n");
218 }
219 }
220
221 c_print("number of iterations: %i\n", (int)info->iter);
222
223 if ((info->status_val == OSQP_SOLVED) ||
224 (info->status_val == OSQP_SOLVED_INACCURATE)) {
225 c_print("optimal objective: %.4f\n", info->obj_val);
226 }
227
228# ifdef PROFILING
229 c_print("run time: %.2es\n", info->run_time);
230# endif /* ifdef PROFILING */
231
232# if EMBEDDED != 1
233 c_print("optimal rho estimate: %.2e\n", info->rho_estimate);
234# endif /* if EMBEDDED != 1 */
235 c_print("\n");
236}
237
238#endif /* End #ifdef PRINTING */
239
240
241#ifndef EMBEDDED
242
243OSQPSettings* copy_settings(const OSQPSettings *settings) {
244 OSQPSettings *new = c_malloc(sizeof(OSQPSettings));
245
246 if (!new) return OSQP_NULL;
247
248 // Copy settings
249 // NB. Copying them explicitly because memcpy is not
250 // defined when PRINTING is disabled (appears in string.h)
251 new->rho = settings->rho;
252 new->sigma = settings->sigma;
253 new->scaling = settings->scaling;
254
255# if EMBEDDED != 1
256 new->adaptive_rho = settings->adaptive_rho;
257 new->adaptive_rho_interval = settings->adaptive_rho_interval;
258 new->adaptive_rho_tolerance = settings->adaptive_rho_tolerance;
259# ifdef PROFILING
260 new->adaptive_rho_fraction = settings->adaptive_rho_fraction;
261# endif
262# endif // EMBEDDED != 1
263 new->max_iter = settings->max_iter;
264 new->eps_abs = settings->eps_abs;
265 new->eps_rel = settings->eps_rel;
266 new->eps_prim_inf = settings->eps_prim_inf;
267 new->eps_dual_inf = settings->eps_dual_inf;
268 new->alpha = settings->alpha;
269 new->linsys_solver = settings->linsys_solver;
270 new->delta = settings->delta;
271 new->polish = settings->polish;
272 new->polish_refine_iter = settings->polish_refine_iter;
273 new->verbose = settings->verbose;
274 new->scaled_termination = settings->scaled_termination;
275 new->check_termination = settings->check_termination;
276 new->warm_start = settings->warm_start;
277# ifdef PROFILING
278 new->time_limit = settings->time_limit;
279# endif
280
281 return new;
282}
283
284#endif // #ifndef EMBEDDED
285
286
287/*******************
288* Timer Functions *
289*******************/
290
291#ifdef PROFILING
292
293// Windows
294# ifdef IS_WINDOWS
295
296void osqp_tic(OSQPTimer *t)
297{
298 QueryPerformanceFrequency(&t->freq);
299 QueryPerformanceCounter(&t->tic);
300}
301
302c_float osqp_toc(OSQPTimer *t)
303{
304 QueryPerformanceCounter(&t->toc);
305 return (t->toc.QuadPart - t->tic.QuadPart) / (c_float)t->freq.QuadPart;
306}
307
308// Mac
309# elif defined IS_MAC
310
311void osqp_tic(OSQPTimer *t)
312{
313 /* read current clock cycles */
314 t->tic = mach_absolute_time();
315}
316
317c_float osqp_toc(OSQPTimer *t)
318{
319 uint64_t duration; /* elapsed time in clock cycles*/
320
321 t->toc = mach_absolute_time();
322 duration = t->toc - t->tic;
323
324 /*conversion from clock cycles to nanoseconds*/
325 mach_timebase_info(&(t->tinfo));
326 duration *= t->tinfo.numer;
327 duration /= t->tinfo.denom;
328
329 return (c_float)duration / 1e9;
330}
331
332// Linux
333# else /* ifdef IS_WINDOWS */
334
335/* read current time */
336void osqp_tic(OSQPTimer *t)
337{
338 clock_gettime(CLOCK_MONOTONIC, &t->tic);
339}
340
341/* return time passed since last call to tic on this timer */
342c_float osqp_toc(OSQPTimer *t)
343{
344 struct timespec temp;
345
346 clock_gettime(CLOCK_MONOTONIC, &t->toc);
347
348 if ((t->toc.tv_nsec - t->tic.tv_nsec) < 0) {
349 temp.tv_sec = t->toc.tv_sec - t->tic.tv_sec - 1;
350 temp.tv_nsec = 1e9 + t->toc.tv_nsec - t->tic.tv_nsec;
351 } else {
352 temp.tv_sec = t->toc.tv_sec - t->tic.tv_sec;
353 temp.tv_nsec = t->toc.tv_nsec - t->tic.tv_nsec;
354 }
355 return (c_float)temp.tv_sec + (c_float)temp.tv_nsec / 1e9;
356}
357
358# endif /* ifdef IS_WINDOWS */
359
360#endif // If Profiling end
361
362
363/* ==================== DEBUG FUNCTIONS ======================= */
364
365
366
367// If debug mode enabled
368#ifdef DDEBUG
369
370#ifdef PRINTING
371
372void print_csc_matrix(csc *M, const char *name)
373{
374 c_int j, i, row_start, row_stop;
375 c_int k = 0;
376
377 // Print name
378 c_print("%s :\n", name);
379
380 for (j = 0; j < M->n; j++) {
381 row_start = M->p[j];
382 row_stop = M->p[j + 1];
383
384 if (row_start == row_stop) continue;
385 else {
386 for (i = row_start; i < row_stop; i++) {
387 c_print("\t[%3u,%3u] = %.3g\n", (int)M->i[i], (int)j, M->x[k++]);
388 }
389 }
390 }
391}
392
393void dump_csc_matrix(csc *M, const char *file_name) {
394 c_int j, i, row_strt, row_stop;
395 c_int k = 0;
396 FILE *f = fopen(file_name, "w");
397
398 if (f != NULL) {
399 for (j = 0; j < M->n; j++) {
400 row_strt = M->p[j];
401 row_stop = M->p[j + 1];
402
403 if (row_strt == row_stop) continue;
404 else {
405 for (i = row_strt; i < row_stop; i++) {
406 fprintf(f, "%d\t%d\t%20.18e\n",
407 (int)M->i[i] + 1, (int)j + 1, M->x[k++]);
408 }
409 }
410 }
411 fprintf(f, "%d\t%d\t%20.18e\n", (int)M->m, (int)M->n, 0.0);
412 fclose(f);
413 c_print("File %s successfully written.\n", file_name);
414 } else {
415 c_eprint("Error during writing file %s.\n", file_name);
416 }
417}
418
419void print_trip_matrix(csc *M, const char *name)
420{
421 c_int k = 0;
422
423 // Print name
424 c_print("%s :\n", name);
425
426 for (k = 0; k < M->nz; k++) {
427 c_print("\t[%3u, %3u] = %.3g\n", (int)M->i[k], (int)M->p[k], M->x[k]);
428 }
429}
430
431void print_dns_matrix(c_float *M, c_int m, c_int n, const char *name)
432{
433 c_int i, j;
434
435 c_print("%s : \n\t", name);
436
437 for (i = 0; i < m; i++) { // Cycle over rows
438 for (j = 0; j < n; j++) { // Cycle over columns
439 if (j < n - 1)
440 // c_print("% 14.12e, ", M[j*m+i]);
441 c_print("% .3g, ", M[j * m + i]);
442
443 else
444 // c_print("% 14.12e; ", M[j*m+i]);
445 c_print("% .3g; ", M[j * m + i]);
446 }
447
448 if (i < m - 1) {
449 c_print("\n\t");
450 }
451 }
452 c_print("\n");
453}
454
455void print_vec(c_float *v, c_int n, const char *name) {
456 print_dns_matrix(v, 1, n, name);
457}
458
459void dump_vec(c_float *v, c_int len, const char *file_name) {
460 c_int i;
461 FILE *f = fopen(file_name, "w");
462
463 if (f != NULL) {
464 for (i = 0; i < len; i++) {
465 fprintf(f, "%20.18e\n", v[i]);
466 }
467 fclose(f);
468 c_print("File %s successfully written.\n", file_name);
469 } else {
470 c_print("Error during writing file %s.\n", file_name);
471 }
472}
473
474void print_vec_int(c_int *x, c_int n, const char *name) {
475 c_int i;
476
477 c_print("%s = [", name);
478
479 for (i = 0; i < n; i++) {
480 c_print(" %i ", (int)x[i]);
481 }
482 c_print("]\n");
483}
484
485#endif // PRINTING
486
487#endif // DEBUG MODE