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