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