1 2 module osqp; 3 4 nothrow @nogc extern(C): 5 6 /* Includes */ 7 import glob_opts; 8 version(EMBEDDED) { 9 import osqp_configure; // for c_free and others 10 } 11 import types; 12 import constants; 13 import util; // Needed for osqp_set_default_settings functions 14 15 // Library to deal with sparse matrices enabled only if embedded not defined 16 version(EMBEDDED) {} 17 else { 18 import cs; 19 } // ifndef EMBEDDED 20 21 import auxil; 22 import lin_alg; 23 import scaling; 24 import error; 25 26 version(EMBEDDED){} 27 else { 28 import polish; 29 }/* ifndef EMBEDDED */ 30 31 version(CTRLC){ 32 import ctrlc; 33 } /* ifdef CTRLC */ 34 35 //version(EMBEDDED){} 36 //else { 37 import lin_sys; 38 //} /* ifndef EMBEDDED */ 39 40 /********************** 41 * Main API Functions * 42 **********************/ 43 void osqp_set_default_settings(OSQPSettings *settings) { 44 45 settings.rho = cast(c_float)RHO; /* ADMM step */ 46 settings.sigma = cast(c_float)SIGMA; /* ADMM step */ 47 settings.scaling = SCALING; /* heuristic problem scaling */ 48 version(EMBEDDED_1){} 49 else { 50 settings.adaptive_rho = ADAPTIVE_RHO; 51 settings.adaptive_rho_interval = ADAPTIVE_RHO_INTERVAL; 52 settings.adaptive_rho_tolerance = cast(c_float)ADAPTIVE_RHO_TOLERANCE; 53 54 version(PROFILING){ 55 settings.adaptive_rho_fraction = cast(c_float)ADAPTIVE_RHO_FRACTION; 56 } /* ifdef PROFILING */ 57 } /* if EMBEDDED != 1 */ 58 59 settings.max_iter = MAX_ITER; /* maximum iterations to 60 take */ 61 settings.eps_abs = cast(c_float)EPS_ABS; /* absolute convergence 62 tolerance */ 63 settings.eps_rel = cast(c_float)EPS_REL; /* relative convergence 64 tolerance */ 65 settings.eps_prim_inf = cast(c_float)EPS_PRIM_INF; /* primal infeasibility 66 tolerance */ 67 settings.eps_dual_inf = cast(c_float)EPS_DUAL_INF; /* dual infeasibility 68 tolerance */ 69 settings.alpha = cast(c_float)ALPHA; /* relaxation parameter */ 70 settings.linsys_solver = cast(linsys_solver_type)LINSYS_SOLVER; /* relaxation parameter */ 71 72 version(EMBEDDED){} 73 else { 74 settings.delta = DELTA; /* regularization parameter 75 for polish */ 76 settings.polish = POLISH; /* ADMM solution polish: 1 77 */ 78 settings.polish_refine_iter = POLISH_REFINE_ITER; /* iterative refinement 79 steps in polish */ 80 settings.verbose = VERBOSE; /* print output */ 81 } /* ifndef EMBEDDED */ 82 83 settings.scaled_termination = SCALED_TERMINATION; /* Evaluate scaled 84 termination criteria*/ 85 settings.check_termination = CHECK_TERMINATION; /* Interval for evaluating 86 termination criteria */ 87 settings.warm_start = WARM_START; /* warm starting */ 88 89 version(PROFILING){ 90 settings.time_limit = TIME_LIMIT; 91 } /* ifdef PROFILING */ 92 } 93 94 version(EMBEDDED){} 95 else { 96 97 c_int osqp_setup(OSQPWorkspace** workp, const OSQPData *data, const OSQPSettings *settings) { 98 c_int exitflag; 99 100 OSQPWorkspace * work; 101 102 // Validate data 103 if (validate_data(data)) return osqp_error(cast(osqp_error_type)OSQP_DATA_VALIDATION_ERROR, __FUNCTION__); 104 105 // Validate settings 106 if (validate_settings(settings)) return osqp_error(cast(osqp_error_type)OSQP_SETTINGS_VALIDATION_ERROR, __FUNCTION__); 107 108 // Allocate empty workspace 109 work = cast(OSQPWorkspace*)c_calloc(1, (OSQPWorkspace.sizeof)); 110 if (!(work)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 111 *workp = work; 112 113 // Start and allocate directly timer 114 version(PROFILING){ 115 work.timer = cast(OSQPTimer*)c_malloc((OSQPTimer.sizeof)); 116 if (!(work.timer)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 117 osqp_tic(work.timer); 118 } /* ifdef PROFILING */ 119 120 // Copy problem data into workspace 121 work.data = cast(OSQPData*)c_malloc((OSQPData.sizeof)); 122 if (!(work.data)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 123 work.data.n = data.n; 124 work.data.m = data.m; 125 126 // Cost function 127 work.data.P = copy_csc_mat(data.P); 128 work.data.q = vec_copy(cast(c_float*)data.q, cast(c_int)data.n); 129 if (!(work.data.P) || !(work.data.q)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 130 131 // Constraints 132 work.data.A = copy_csc_mat(data.A); 133 if (!(work.data.A)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 134 work.data.l = vec_copy(cast(c_float*)data.l, cast(c_int)data.m); 135 work.data.u = vec_copy(cast(c_float*)data.u, cast(c_int)data.m); 136 if ( data.m && (!(work.data.l) || !(work.data.u)) ) 137 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 138 139 // Vectorized rho parameter 140 work.rho_vec = cast(c_float*)c_malloc(data.m * (c_float.sizeof)); 141 work.rho_inv_vec = cast(c_float*)c_malloc(data.m * (c_float.sizeof)); 142 if ( data.m && (!(work.rho_vec) || !(work.rho_inv_vec)) ) 143 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 144 145 // Type of constraints 146 work.constr_type = cast(c_int*)c_calloc(data.m, (c_int.sizeof)); 147 if (data.m && !(work.constr_type)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 148 149 // Allocate internal solver variables (ADMM steps) 150 work.x = cast(c_float*)c_calloc(data.n, (c_float.sizeof)); 151 work.z = cast(c_float*)c_calloc(data.m, (c_float.sizeof)); 152 work.xz_tilde = cast(c_float*)c_calloc(data.n + data.m, (c_float.sizeof)); 153 work.x_prev = cast(c_float*)c_calloc(data.n, (c_float.sizeof)); 154 work.z_prev = cast(c_float*)c_calloc(data.m, (c_float.sizeof)); 155 work.y = cast(c_float*)c_calloc(data.m, (c_float.sizeof)); 156 if (!(work.x) || !(work.xz_tilde) || !(work.x_prev)) 157 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 158 if ( data.m && (!(work.z) || !(work.z_prev) || !(work.y)) ) 159 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 160 161 // Initialize variables x, y, z to 0 162 cold_start(work); 163 164 // Primal and dual residuals variables 165 work.Ax = cast(c_float*)c_calloc(data.m, (c_float.sizeof)); 166 work.Px = cast(c_float*)c_calloc(data.n, (c_float.sizeof)); 167 work.Aty = cast(c_float*)c_calloc(data.n, (c_float.sizeof)); 168 169 // Primal infeasibility variables 170 work.delta_y = cast(c_float*)c_calloc(data.m, (c_float.sizeof)); 171 work.Atdelta_y = cast(c_float*)c_calloc(data.n, (c_float.sizeof)); 172 173 // Dual infeasibility variables 174 work.delta_x = cast(c_float*)c_calloc(data.n, (c_float.sizeof)); 175 work.Pdelta_x = cast(c_float*)c_calloc(data.n, (c_float.sizeof)); 176 work.Adelta_x = cast(c_float*)c_calloc(data.m, (c_float.sizeof)); 177 178 if (!(work.Px) || !(work.Aty) || !(work.Atdelta_y) || 179 !(work.delta_x) || !(work.Pdelta_x)) 180 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 181 if ( data.m && (!(work.Ax) || !(work.delta_y) || !(work.Adelta_x)) ) 182 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 183 184 // Copy settings 185 work.settings = copy_settings(settings); 186 if (!(work.settings)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 187 188 // Perform scaling 189 if (settings.scaling) { 190 // Allocate scaling structure 191 work.scaling = cast(OSQPScaling*)c_malloc((OSQPScaling.sizeof)); 192 if (!(work.scaling)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 193 work.scaling.D = cast(c_float*)c_malloc(data.n * (c_float.sizeof)); 194 work.scaling.Dinv = cast(c_float*)c_malloc(data.n * (c_float.sizeof)); 195 work.scaling.E = cast(c_float*)c_malloc(data.m * (c_float.sizeof)); 196 work.scaling.Einv = cast(c_float*)c_malloc(data.m * (c_float.sizeof)); 197 if (!(work.scaling.D) || !(work.scaling.Dinv)) 198 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 199 if ( data.m && (!(work.scaling.E) || !(work.scaling.Einv)) ) 200 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 201 202 203 // Allocate workspace variables used in scaling 204 work.D_temp = cast(c_float*)c_malloc(data.n * (c_float.sizeof)); 205 work.D_temp_A = cast(c_float*)c_malloc(data.n * (c_float.sizeof)); 206 work.E_temp = cast(c_float*)c_malloc(data.m * (c_float.sizeof)); 207 // if (!(work.D_temp) || !(work.D_temp_A) || !(work.E_temp)) 208 // return osqp_error(OSQP_MEM_ALLOC_ERROR); 209 if (!(work.D_temp) || !(work.D_temp_A)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 210 if (data.m && !(work.E_temp)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 211 212 // Scale data 213 scale_data(work); 214 } else { 215 work.scaling = OSQP_NULL; 216 work.D_temp = OSQP_NULL; 217 work.D_temp_A = OSQP_NULL; 218 work.E_temp = OSQP_NULL; 219 } 220 221 // Set type of constraints 222 set_rho_vec(work); 223 224 // Load linear system solver 225 if (load_linsys_solver(work.settings.linsys_solver)) return osqp_error(cast(osqp_error_type)OSQP_LINSYS_SOLVER_LOAD_ERROR, __FUNCTION__); 226 227 // Initialize linear system solver structure 228 exitflag = init_linsys_solver(&(work.linsys_solver), work.data.P, work.data.A, 229 work.settings.sigma, work.rho_vec, 230 work.settings.linsys_solver, 0); 231 232 if (exitflag) { 233 return osqp_error(cast(osqp_error_type)exitflag, __FUNCTION__); 234 } 235 236 // Initialize active constraints structure 237 work.pol = cast(OSQPPolish*)c_malloc(OSQPPolish.sizeof); 238 if (!(work.pol)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 239 work.pol.Alow_to_A = cast(c_int*)c_malloc(data.m * (c_int.sizeof)); 240 work.pol.Aupp_to_A = cast(c_int*)c_malloc(data.m * (c_int.sizeof)); 241 work.pol.A_to_Alow = cast(c_int*)c_malloc(data.m * (c_int.sizeof)); 242 work.pol.A_to_Aupp = cast(c_int*)c_malloc(data.m * (c_int.sizeof)); 243 work.pol.x = cast(c_float*)c_malloc(data.n * (c_float.sizeof)); 244 work.pol.z = cast(c_float*)c_malloc(data.m * (c_float.sizeof)); 245 work.pol.y = cast(c_float*)c_malloc(data.m * (c_float.sizeof)); 246 if (!(work.pol.x)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 247 if ( data.m && (!(work.pol.Alow_to_A) || !(work.pol.Aupp_to_A) || 248 !(work.pol.A_to_Alow) || !(work.pol.A_to_Aupp) || 249 !(work.pol.z) || !(work.pol.y)) ) 250 return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 251 252 // Allocate solution 253 work.solution = cast(OSQPSolution*)c_calloc(1, (OSQPSolution.sizeof)); 254 if (!(work.solution)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 255 work.solution.x = cast(c_float*)c_calloc(1, data.n * (c_float.sizeof)); 256 work.solution.y = cast(c_float*)c_calloc(1, data.m * (c_float.sizeof)); 257 if (!(work.solution.x)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 258 if (data.m && !(work.solution.y)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 259 260 // Allocate and initialize information 261 work.info = cast(OSQPInfo*)c_calloc(1, (OSQPInfo.sizeof)); 262 if (!(work.info)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__); 263 work.info.status_polish = 0; // Polishing not performed 264 update_status(work.info, OSQP_UNSOLVED); 265 version(PROFILING){ 266 work.info.solve_time = 0.0; // Solve time to zero 267 work.info.update_time = 0.0; // Update time to zero 268 work.info.polish_time = 0.0; // Polish time to zero 269 work.info.run_time = 0.0; // Total run time to zero 270 work.info.setup_time = osqp_toc(work.timer); // Update timer information 271 272 work.first_run = 1; 273 work.clear_update_time = 0; 274 work.rho_update_from_solve = 0; 275 } /* ifdef PROFILING */ 276 work.info.rho_updates = 0; // Rho updates set to 0 277 work.info.rho_estimate = work.settings.rho; // Best rho estimate 278 279 // Print header 280 version(PRINTING){ 281 if (work.settings.verbose) print_setup_header(work); 282 work.summary_printed = 0; // Initialize last summary to not printed 283 } /* ifdef PRINTING */ 284 285 286 // If adaptive rho and automatic interval, but profiling disabled, we need to 287 // set the interval to a default value 288 version(PROFILING){} 289 else { 290 if (work.settings.adaptive_rho && !work.settings.adaptive_rho_interval) { 291 if (work.settings.check_termination) { 292 // If check_termination is enabled, we set it to a multiple of the check 293 // termination interval 294 work.settings.adaptive_rho_interval = ADAPTIVE_RHO_MULTIPLE_TERMINATION * 295 work.settings.check_termination; 296 } else { 297 // If check_termination is disabled we set it to a predefined fix number 298 work.settings.adaptive_rho_interval = ADAPTIVE_RHO_FIXED; 299 } 300 } 301 } /* ifndef PROFILING */ 302 303 // Return exit flag 304 return 0; 305 } 306 307 } // #ifndef EMBEDDED 308 309 310 c_int osqp_solve(OSQPWorkspace *work) { 311 312 c_int exitflag; 313 c_int iter; 314 c_int compute_cost_function; // Boolean: compute the cost function in the loop or not 315 c_int can_check_termination; // Boolean: check termination or not 316 317 version(PROFILING){ 318 c_float temp_run_time; // Temporary variable to store current run time 319 } /* ifdef PROFILING */ 320 321 version(PRINTING){ 322 c_int can_print; // Boolean whether you can print 323 } /* ifdef PRINTING */ 324 325 // Check if workspace has been initialized 326 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 327 328 version(PROFILING){ 329 if (work.clear_update_time == 1) 330 work.info.update_time = 0.0; 331 work.rho_update_from_solve = 1; 332 } /* ifdef PROFILING */ 333 334 // Initialize variables 335 exitflag = 0; 336 can_check_termination = 0; 337 version(PRINTING){ 338 can_print = work.settings.verbose; 339 } /* ifdef PRINTING */ 340 version(PRINTING){ 341 compute_cost_function = work.settings.verbose; // Compute cost function only 342 // if verbose is on 343 } else {/* ifdef PRINTING */ 344 compute_cost_function = 0; // Never compute cost 345 // function during the 346 // iterations if no printing 347 // enabled 348 } /* ifdef PRINTING */ 349 350 351 352 version(PROFILING){ 353 osqp_tic(work.timer); // Start timer 354 } /* ifdef PROFILING */ 355 356 357 version(PRINTING){ 358 359 if (work.settings.verbose) { 360 // Print Header for every column 361 print_header(); 362 } 363 } /* ifdef PRINTING */ 364 365 version(CTRLC){ 366 367 // initialize Ctrl-C support 368 osqp_start_interrupt_listener(); 369 } /* ifdef CTRLC */ 370 371 // Initialize variables (cold start or warm start depending on settings) 372 if (!work.settings.warm_start) cold_start(work); // If not warm start . 373 // set x, z, y to zero 374 375 // Main ADMM algorithm 376 for (iter = 1; iter <= work.settings.max_iter; iter++) { 377 // Update x_prev, z_prev (preallocated, no malloc) 378 swap_vectors(&(work.x), &(work.x_prev)); 379 swap_vectors(&(work.z), &(work.z_prev)); 380 381 /* ADMM STEPS */ 382 /* Compute \tilde{x}^{k+1}, \tilde{z}^{k+1} */ 383 update_xz_tilde(work); 384 385 /* Compute x^{k+1} */ 386 update_x(work); 387 388 /* Compute z^{k+1} */ 389 update_z(work); 390 391 /* Compute y^{k+1} */ 392 update_y(work); 393 394 /* End of ADMM Steps */ 395 396 version(CTRLC){ 397 398 // Check the interrupt signal 399 if (osqp_is_interrupted()) { 400 update_status(work.info, OSQP_SIGINT); 401 version(PRINTING){ 402 c_print("Solver interrupted\n"); 403 } /* ifdef PRINTING */ 404 exitflag = 1; 405 goto exit; 406 } 407 } /* ifdef CTRLC */ 408 409 version(PROFILING){ 410 411 // Check if solver time_limit is enabled. In case, check if the current 412 // run time is more than the time_limit option. 413 if (work.first_run) { 414 temp_run_time = work.info.setup_time + osqp_toc(work.timer); 415 } 416 else { 417 temp_run_time = work.info.update_time + osqp_toc(work.timer); 418 } 419 420 if (work.settings.time_limit && 421 (temp_run_time >= work.settings.time_limit)) { 422 update_status(work.info, OSQP_TIME_LIMIT_REACHED); 423 version(PRINTING){ 424 if (work.settings.verbose) c_print("run time limit reached\n"); 425 can_print = 0; // Not printing at this iteration 426 } /* ifdef PRINTING */ 427 break; 428 } 429 } /* ifdef PROFILING */ 430 431 432 // Can we check for termination ? 433 can_check_termination = work.settings.check_termination && 434 (iter % work.settings.check_termination == 0); 435 436 version(PRINTING){ 437 438 // Can we print ? 439 can_print = work.settings.verbose && 440 ((iter % PRINT_INTERVAL == 0) || (iter == 1)); 441 442 if (can_check_termination || can_print) { // Update status in either of 443 // these cases 444 // Update information 445 update_info(work, iter, compute_cost_function, 0); 446 447 if (can_print) { 448 // Print summary 449 print_summary(work); 450 } 451 452 if (can_check_termination) { 453 // Check algorithm termination 454 if (check_termination(work, 0)) { 455 // Terminate algorithm 456 break; 457 } 458 } 459 } 460 }else{ /* ifdef PRINTING */ 461 462 if (can_check_termination) { 463 // Update information and compute also objective value 464 update_info(work, iter, compute_cost_function, 0); 465 466 // Check algorithm termination 467 if (check_termination(work, 0)) { 468 // Terminate algorithm 469 break; 470 } 471 } 472 } /* ifdef PRINTING */ 473 474 475 476 version(EMBEDDED_1){} 477 else{ 478 version(PROFILING){ 479 480 // If adaptive rho with automatic interval, check if the solve time is a 481 // certain fraction 482 // of the setup time. 483 if (work.settings.adaptive_rho && !work.settings.adaptive_rho_interval) { 484 // Check time 485 if (osqp_toc(work.timer) > 486 work.settings.adaptive_rho_fraction * work.info.setup_time) { 487 // Enough time has passed. We now get the number of iterations between 488 // the updates. 489 if (work.settings.check_termination) { 490 // If check_termination is enabled, we round the number of iterations 491 // between 492 // rho updates to the closest multiple of check_termination 493 work.settings.adaptive_rho_interval = cast(c_int)c_roundmultiple(iter, 494 work.settings.check_termination); 495 } else { 496 // If check_termination is disabled, we round the number of iterations 497 // between 498 // updates to the closest multiple of the default check_termination 499 // interval. 500 work.settings.adaptive_rho_interval = cast(c_int)c_roundmultiple(iter, 501 CHECK_TERMINATION); 502 } 503 504 // Make sure the interval is not 0 and at least check_termination times 505 work.settings.adaptive_rho_interval = c_max( 506 work.settings.adaptive_rho_interval, 507 work.settings.check_termination); 508 } // If time condition is met 509 } // If adaptive rho enabled and interval set to auto 510 } // PROFILING 511 512 // Adapt rho 513 if (work.settings.adaptive_rho && 514 work.settings.adaptive_rho_interval && 515 (iter % work.settings.adaptive_rho_interval == 0)) { 516 // Update info with the residuals if it hasn't been done before 517 version(PRINTING){ 518 519 if (!can_check_termination && !can_print) { 520 // Information has not been computed neither for termination or printing 521 // reasons 522 update_info(work, iter, compute_cost_function, 0); 523 } 524 } else {/* ifdef PRINTING */ 525 526 if (!can_check_termination) { 527 // Information has not been computed before for termination check 528 update_info(work, iter, compute_cost_function, 0); 529 } 530 } /* ifdef PRINTING */ 531 532 // Actually update rho 533 if (adapt_rho(work)) { 534 version(PRINTING){ 535 c_eprint("ERROR in %s: Failed rho update\n", __FUNCTION__.ptr); 536 } // PRINTING 537 exitflag = 1; 538 goto exit; 539 } 540 } 541 } // EMBEDDED_1 != 1 542 543 } // End of ADMM for loop 544 545 546 // Update information and check termination condition if it hasn't been done 547 // during last iteration (max_iter reached or check_termination disabled) 548 if (!can_check_termination) { 549 /* Update information */ 550 version(PRINTING){ 551 552 if (!can_print) { 553 // Update info only if it hasn't been updated before for printing 554 // reasons 555 update_info(work, iter - 1, compute_cost_function, 0); 556 } 557 }else {/* ifdef PRINTING */ 558 559 // If no printing is enabled, update info directly 560 update_info(work, iter - 1, compute_cost_function, 0); 561 } /* ifdef PRINTING */ 562 563 version(PRINTING){ 564 565 /* Print summary */ 566 if (work.settings.verbose && !work.summary_printed) print_summary(work); 567 } /* ifdef PRINTING */ 568 569 /* Check whether a termination criterion is triggered */ 570 check_termination(work, 0); 571 } 572 573 // Compute objective value in case it was not 574 // computed during the iterations 575 if (!compute_cost_function && has_solution(work.info)){ 576 work.info.obj_val = compute_obj_val(work, work.x); 577 } 578 579 580 version(PRINTING){ 581 /* Print summary for last iteration */ 582 if (work.settings.verbose && !work.summary_printed) { 583 print_summary(work); 584 } 585 } /* ifdef PRINTING */ 586 587 /* if max iterations reached, change status accordingly */ 588 if (work.info.status_val == OSQP_UNSOLVED) { 589 if (!check_termination(work, 1)) { // Try to check for approximate 590 update_status(work.info, OSQP_MAX_ITER_REACHED); 591 } 592 } 593 594 version(PROFILING){ 595 /* if time-limit reached check termination and update status accordingly */ 596 if (work.info.status_val == OSQP_TIME_LIMIT_REACHED) { 597 if (!check_termination(work, 1)) { // Try for approximate solutions 598 update_status(work.info, OSQP_TIME_LIMIT_REACHED); /* Change update status back to OSQP_TIME_LIMIT_REACHED */ 599 } 600 } 601 } /* ifdef PROFILING */ 602 603 604 version(EMBEDDED_1) {} 605 else { 606 /* Update rho estimate */ 607 work.info.rho_estimate = compute_rho_estimate(work); 608 } /* if EMBEDDED_1 != 1 */ 609 610 /* Update solve time */ 611 version(PROFILING){ 612 work.info.solve_time = osqp_toc(work.timer); 613 } /* ifdef PROFILING */ 614 615 616 version(EMBEDDED) {} 617 else { 618 // Polish the obtained solution 619 if (work.settings.polish && (work.info.status_val == OSQP_SOLVED)){ 620 polish.polish(work); 621 } 622 } /* ifndef EMBEDDED */ 623 624 version(PROFILING){ 625 /* Update total time */ 626 if (work.first_run) { 627 // total time: setup + solve + polish 628 work.info.run_time = work.info.setup_time + 629 work.info.solve_time + 630 work.info.polish_time; 631 } else { 632 // total time: update + solve + polish 633 work.info.run_time = work.info.update_time + 634 work.info.solve_time + 635 work.info.polish_time; 636 } 637 638 // Indicate that the solve function has already been executed 639 if (work.first_run) work.first_run = 0; 640 641 // Indicate that the update_time should be set to zero 642 work.clear_update_time = 1; 643 644 // Indicate that osqp_update_rho is not called from osqp_solve 645 work.rho_update_from_solve = 0; 646 } /* ifdef PROFILING */ 647 648 version(PRINTING){ 649 /* Print final footer */ 650 if (work.settings.verbose) print_footer(work.info, work.settings.polish); 651 } /* ifdef PRINTING */ 652 653 // Store solution 654 store_solution(work); 655 656 // Define exit flag for quitting function 657 version(PROFILING){ 658 exit: 659 } else { 660 version(CTRLC){ 661 exit: 662 }else { 663 version(EMBEDDED_1){} 664 else { 665 exit: 666 } 667 } 668 } 669 //#if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1 670 //exit: 671 //#endif /* if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1 */ 672 673 version(CTRLC){ 674 // Restore previous signal handler 675 osqp_end_interrupt_listener(); 676 } /* ifdef CTRLC */ 677 678 return exitflag; 679 } 680 681 682 c_int osqp_cleanup(OSQPWorkspace *work) { 683 c_int exitflag = 0; 684 685 if (work) { // If workspace has been allocated 686 // Free Data 687 if (work.data) { 688 version(EMBEDDED){} // todo : useless here 689 else { 690 if (work.data.P) csc_spfree(work.data.P); // todo : test it 691 if (work.data.A) csc_spfree(work.data.A); 692 } // !EMBEDDED 693 if (work.data.q) c_free(work.data.q); 694 if (work.data.l) c_free(work.data.l); 695 if (work.data.u) c_free(work.data.u); 696 c_free(work.data); 697 } 698 699 // Free scaling variables 700 if (work.scaling){ 701 if (work.scaling.D) c_free(work.scaling.D); 702 if (work.scaling.Dinv) c_free(work.scaling.Dinv); 703 if (work.scaling.E) c_free(work.scaling.E); 704 if (work.scaling.Einv) c_free(work.scaling.Einv); 705 c_free(work.scaling); 706 } 707 708 // Free temp workspace variables for scaling 709 if (work.D_temp) c_free(work.D_temp); 710 if (work.D_temp_A) c_free(work.D_temp_A); 711 if (work.E_temp) c_free(work.E_temp); 712 713 version(EMBEDDED){} 714 else { 715 // Free linear system solver structure 716 if (work.linsys_solver) { 717 if (work.linsys_solver.free) { 718 work.linsys_solver.free(work.linsys_solver); 719 } 720 } 721 } // !EMBEDDED 722 723 // Unload linear system solver after free 724 if (work.settings) { 725 exitflag = unload_linsys_solver(work.settings.linsys_solver); 726 } 727 728 version(EMBEDDED){} // todo : useless here 729 else { 730 // Free active constraints structure 731 if (work.pol) { 732 if (work.pol.Alow_to_A) c_free(work.pol.Alow_to_A); 733 if (work.pol.Aupp_to_A) c_free(work.pol.Aupp_to_A); 734 if (work.pol.A_to_Alow) c_free(work.pol.A_to_Alow); 735 if (work.pol.A_to_Aupp) c_free(work.pol.A_to_Aupp); 736 if (work.pol.x) c_free(work.pol.x); 737 if (work.pol.z) c_free(work.pol.z); 738 if (work.pol.y) c_free(work.pol.y); 739 c_free(work.pol); 740 } 741 } /* ifndef EMBEDDED */ 742 743 // Free other Variables 744 if (work.rho_vec) c_free(work.rho_vec); 745 if (work.rho_inv_vec) c_free(work.rho_inv_vec); 746 version(EMBEDDED_1){} 747 else { 748 if (work.constr_type) c_free(work.constr_type); 749 } 750 if (work.x) c_free(work.x); 751 if (work.z) c_free(work.z); 752 if (work.xz_tilde) c_free(work.xz_tilde); 753 if (work.x_prev) c_free(work.x_prev); 754 if (work.z_prev) c_free(work.z_prev); 755 if (work.y) c_free(work.y); 756 if (work.Ax) c_free(work.Ax); 757 if (work.Px) c_free(work.Px); 758 if (work.Aty) c_free(work.Aty); 759 if (work.delta_y) c_free(work.delta_y); 760 if (work.Atdelta_y) c_free(work.Atdelta_y); 761 if (work.delta_x) c_free(work.delta_x); 762 if (work.Pdelta_x) c_free(work.Pdelta_x); 763 if (work.Adelta_x) c_free(work.Adelta_x); 764 // Free Settings 765 if (work.settings) c_free(work.settings); 766 767 // Free solution 768 if (work.solution) { 769 if (work.solution.x) c_free(work.solution.x); 770 if (work.solution.y) c_free(work.solution.y); 771 c_free(work.solution); 772 } 773 774 // Free information 775 if (work.info) c_free(work.info); 776 777 version(PROFILING){ 778 // Free timer 779 if (work.timer) c_free(work.timer); 780 } /* ifdef PROFILING */ 781 782 // Free work 783 c_free(work); 784 } 785 return exitflag; 786 } 787 788 //} // #ifndef EMBEDDED 789 790 791 /************************ 792 * Update problem data * 793 ************************/ 794 c_int osqp_update_lin_cost(OSQPWorkspace *work, const c_float *q_new) { 795 796 // Check if workspace has been initialized 797 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 798 799 version(PROFILING){ 800 if (work.clear_update_time == 1) { 801 work.clear_update_time = 0; 802 work.info.update_time = 0.0; 803 } 804 osqp_tic(work.timer); // Start timer 805 } /* ifdef PROFILING */ 806 807 // Replace q by the new vector 808 prea_vec_copy(q_new, work.data.q, work.data.n); 809 810 // Scaling 811 if (work.settings.scaling) { 812 vec_ew_prod(work.scaling.D, work.data.q, work.data.q, work.data.n); 813 vec_mult_scalar(work.data.q, work.scaling.c, work.data.n); 814 } 815 816 // Reset solver information 817 reset_info(work.info); 818 819 version(PROFILING){ 820 work.info.update_time += osqp_toc(work.timer); 821 } /* ifdef PROFILING */ 822 823 return 0; 824 } 825 826 c_int osqp_update_bounds(OSQPWorkspace *work, 827 const c_float *l_new, 828 const c_float *u_new) { 829 c_int i, exitflag = 0; 830 831 // Check if workspace has been initialized 832 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 833 834 version(PROFILING){ 835 if (work.clear_update_time == 1) { 836 work.clear_update_time = 0; 837 work.info.update_time = 0.0; 838 } 839 osqp_tic(work.timer); // Start timer 840 } /* ifdef PROFILING */ 841 842 // Check if lower bound is smaller than upper bound 843 for (i = 0; i < work.data.m; i++) { 844 if (l_new[i] > u_new[i]) { 845 version(PRINTING){ 846 c_eprint("ERROR in %s: lower bound must be lower than or equal to upper bound\n", __FUNCTION__.ptr); 847 } /* ifdef PRINTING */ 848 return 1; 849 } 850 } 851 852 // Replace l and u by the new vectors 853 prea_vec_copy(l_new, work.data.l, work.data.m); 854 prea_vec_copy(u_new, work.data.u, work.data.m); 855 856 // Scaling 857 if (work.settings.scaling) { 858 vec_ew_prod(work.scaling.E, work.data.l, work.data.l, work.data.m); 859 vec_ew_prod(work.scaling.E, work.data.u, work.data.u, work.data.m); 860 } 861 862 // Reset solver information 863 reset_info(work.info); 864 865 version(EMBEDDED_1){} 866 else { 867 // Update rho_vec and refactor if constraints type changes 868 exitflag = update_rho_vec(work); 869 } // EMBEDDED != 1 870 871 version(PROFILING){ 872 work.info.update_time += osqp_toc(work.timer); 873 } /* ifdef PROFILING */ 874 875 return exitflag; 876 } 877 878 c_int osqp_update_lower_bound(OSQPWorkspace *work, const c_float *l_new) { 879 c_int i, exitflag = 0; 880 881 // Check if workspace has been initialized 882 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 883 884 version(PROFILING){ 885 if (work.clear_update_time == 1) { 886 work.clear_update_time = 0; 887 work.info.update_time = 0.0; 888 } 889 osqp_tic(work.timer); // Start timer 890 } /* ifdef PROFILING */ 891 892 // Replace l by the new vector 893 prea_vec_copy(l_new, work.data.l, work.data.m); 894 895 // Scaling 896 if (work.settings.scaling) { 897 vec_ew_prod(work.scaling.E, work.data.l, work.data.l, work.data.m); 898 } 899 900 // Check if lower bound is smaller than upper bound 901 for (i = 0; i < work.data.m; i++) { 902 if (work.data.l[i] > work.data.u[i]) { 903 version(PRINTING){ 904 c_eprint("ERROR in %s: upper bound must be greater than or equal to lower bound\n", __FUNCTION__.ptr); 905 } /* ifdef PRINTING */ 906 return 1; 907 } 908 } 909 910 // Reset solver information 911 reset_info(work.info); 912 913 version(EMBEDDED_1){} 914 else { 915 // Update rho_vec and refactor if constraints type changes 916 exitflag = update_rho_vec(work); 917 } // EMBEDDED ! =1 918 919 version(PROFILING){ 920 work.info.update_time += osqp_toc(work.timer); 921 } /* ifdef PROFILING */ 922 923 return exitflag; 924 } 925 926 c_int osqp_update_upper_bound(OSQPWorkspace *work, const c_float *u_new) { 927 c_int i, exitflag = 0; 928 929 // Check if workspace has been initialized 930 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 931 932 version(PROFILING){ 933 if (work.clear_update_time == 1) { 934 work.clear_update_time = 0; 935 work.info.update_time = 0.0; 936 } 937 osqp_tic(work.timer); // Start timer 938 } /* ifdef PROFILING */ 939 940 // Replace u by the new vector 941 prea_vec_copy(u_new, work.data.u, work.data.m); 942 943 // Scaling 944 if (work.settings.scaling) { 945 vec_ew_prod(work.scaling.E, work.data.u, work.data.u, work.data.m); 946 } 947 948 // Check if upper bound is greater than lower bound 949 for (i = 0; i < work.data.m; i++) { 950 if (work.data.u[i] < work.data.l[i]) { 951 version(PRINTING){ 952 c_eprint("ERROR in %s: lower bound must be lower than or equal to upper bound\n", __FUNCTION__.ptr); 953 } /* ifdef PRINTING */ 954 return 1; 955 } 956 } 957 958 // Reset solver information 959 reset_info(work.info); 960 961 version(EMBEDDED_1){} 962 else { 963 // Update rho_vec and refactor if constraints type changes 964 exitflag = update_rho_vec(work); 965 } // EMBEDDED != 1 966 967 version(PROFILING){ 968 work.info.update_time += osqp_toc(work.timer); 969 } /* ifdef PROFILING */ 970 971 return exitflag; 972 } 973 974 c_int osqp_warm_start(OSQPWorkspace *work, const c_float *x, const c_float *y) { 975 976 // Check if workspace has been initialized 977 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 978 979 // Update warm_start setting to true 980 if (!work.settings.warm_start) work.settings.warm_start = 1; 981 982 // Copy primal and dual variables into the iterates 983 prea_vec_copy(x, work.x, work.data.n); 984 prea_vec_copy(y, work.y, work.data.m); 985 986 // Scale iterates 987 if (work.settings.scaling) { 988 vec_ew_prod(work.scaling.Dinv, work.x, work.x, work.data.n); 989 vec_ew_prod(work.scaling.Einv, work.y, work.y, work.data.m); 990 vec_mult_scalar(work.y, work.scaling.c, work.data.m); 991 } 992 993 // Compute Ax = z and store it in z 994 mat_vec(work.data.A, work.x, work.z, 0); 995 996 return 0; 997 } 998 999 c_int osqp_warm_start_x(OSQPWorkspace *work, const c_float *x) { 1000 1001 // Check if workspace has been initialized 1002 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1003 1004 // Update warm_start setting to true 1005 if (!work.settings.warm_start) work.settings.warm_start = 1; 1006 1007 // Copy primal variable into the iterate x 1008 prea_vec_copy(x, work.x, work.data.n); 1009 1010 // Scale iterate 1011 if (work.settings.scaling) { 1012 vec_ew_prod(work.scaling.Dinv, work.x, work.x, work.data.n); 1013 } 1014 1015 // Compute Ax = z and store it in z 1016 mat_vec(work.data.A, work.x, work.z, 0); 1017 1018 return 0; 1019 } 1020 1021 c_int osqp_warm_start_y(OSQPWorkspace *work, const c_float *y) { 1022 1023 // Check if workspace has been initialized 1024 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1025 1026 // Update warm_start setting to true 1027 if (!work.settings.warm_start) work.settings.warm_start = 1; 1028 1029 // Copy primal variable into the iterate y 1030 prea_vec_copy(y, work.y, work.data.m); 1031 1032 // Scale iterate 1033 if (work.settings.scaling) { 1034 vec_ew_prod(work.scaling.Einv, work.y, work.y, work.data.m); 1035 vec_mult_scalar(work.y, work.scaling.c, work.data.m); 1036 } 1037 1038 return 0; 1039 } 1040 1041 1042 version(EMBEDDED_1){} 1043 else { 1044 1045 c_int osqp_update_P(OSQPWorkspace *work, 1046 const c_float *Px_new, 1047 const c_int *Px_new_idx, 1048 c_int P_new_n) { 1049 c_int i; // For indexing 1050 c_int exitflag; // Exit flag 1051 c_int nnzP; // Number of nonzeros in P 1052 1053 // Check if workspace has been initialized 1054 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1055 1056 version(PROFILING){ 1057 if (work.clear_update_time == 1) { 1058 work.clear_update_time = 0; 1059 work.info.update_time = 0.0; 1060 } 1061 osqp_tic(work.timer); // Start timer 1062 } /* ifdef PROFILING */ 1063 1064 nnzP = work.data.P.p[work.data.P.n]; 1065 1066 if (Px_new_idx) { // Passing the index of elements changed 1067 // Check if number of elements is less or equal than the total number of 1068 // nonzeros in P 1069 if (P_new_n > nnzP) { 1070 version(PRINTING){ 1071 c_eprint("ERROR in %s: new number of elements (%i) greater than elements in P (%i)\n", 1072 __FUNCTION__.ptr, 1073 cast(c_int)P_new_n, 1074 cast(c_int)nnzP); 1075 } /* ifdef PRINTING */ 1076 return 1; 1077 } 1078 } 1079 1080 if (work.settings.scaling) { 1081 // Unscale data 1082 unscale_data(work); 1083 } 1084 1085 // Update P elements 1086 if (Px_new_idx) { // Change only Px_new_idx 1087 for (i = 0; i < P_new_n; i++) { 1088 work.data.P.x[Px_new_idx[i]] = Px_new[i]; 1089 } 1090 } 1091 else // Change whole P 1092 { 1093 for (i = 0; i < nnzP; i++) { 1094 work.data.P.x[i] = Px_new[i]; 1095 } 1096 } 1097 1098 if (work.settings.scaling) { 1099 // Scale data 1100 scale_data(work); 1101 } 1102 1103 // Update linear system structure with new data 1104 exitflag = work.linsys_solver.update_matrices(work.linsys_solver, 1105 work.data.P, 1106 work.data.A); 1107 1108 // Reset solver information 1109 reset_info(work.info); 1110 1111 version(PRINTING){ 1112 1113 if (exitflag < 0) { 1114 c_eprint("ERROR in %s: new KKT matrix is not quasidefinite\n", __FUNCTION__.ptr); 1115 } 1116 } /* ifdef PRINTING */ 1117 1118 version(PROFILING){ 1119 work.info.update_time += osqp_toc(work.timer); 1120 } /* ifdef PROFILING */ 1121 1122 return exitflag; 1123 } 1124 1125 1126 c_int osqp_update_A(OSQPWorkspace *work, 1127 const c_float *Ax_new, 1128 const c_int *Ax_new_idx, 1129 c_int A_new_n) { 1130 c_int i; // For indexing 1131 c_int exitflag; // Exit flag 1132 c_int nnzA; // Number of nonzeros in A 1133 1134 // Check if workspace has been initialized 1135 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1136 1137 version(PROFILING){ 1138 if (work.clear_update_time == 1) { 1139 work.clear_update_time = 0; 1140 work.info.update_time = 0.0; 1141 } 1142 osqp_tic(work.timer); // Start timer 1143 } /* ifdef PROFILING */ 1144 1145 nnzA = work.data.A.p[work.data.A.n]; 1146 1147 if (Ax_new_idx) { // Passing the index of elements changed 1148 // Check if number of elements is less or equal than the total number of 1149 // nonzeros in A 1150 if (A_new_n > nnzA) { 1151 version(PRINTING){ 1152 c_eprint("ERROR in %s: new number of elements (%i) greater than elements in A (%i)\n", 1153 __FUNCTION__.ptr, 1154 cast(c_int)A_new_n, 1155 cast(c_int)nnzA); 1156 } /* ifdef PRINTING */ 1157 return 1; 1158 } 1159 } 1160 1161 if (work.settings.scaling) { 1162 // Unscale data 1163 unscale_data(work); 1164 } 1165 1166 // Update A elements 1167 if (Ax_new_idx) { // Change only Ax_new_idx 1168 for (i = 0; i < A_new_n; i++) { 1169 work.data.A.x[Ax_new_idx[i]] = Ax_new[i]; 1170 } 1171 } 1172 else { // Change whole A 1173 for (i = 0; i < nnzA; i++) { 1174 work.data.A.x[i] = Ax_new[i]; 1175 } 1176 } 1177 1178 if (work.settings.scaling) { 1179 // Scale data 1180 scale_data(work); 1181 } 1182 1183 // Update linear system structure with new data 1184 exitflag = work.linsys_solver.update_matrices(work.linsys_solver, 1185 work.data.P, 1186 work.data.A); 1187 1188 // Reset solver information 1189 reset_info(work.info); 1190 1191 version(PRINTING){ 1192 1193 if (exitflag < 0) { 1194 c_eprint("ERROR in %s: new KKT matrix is not quasidefinite\n", __FUNCTION__.ptr); 1195 } 1196 } /* ifdef PRINTING */ 1197 1198 version(PROFILING){ 1199 work.info.update_time += osqp_toc(work.timer); 1200 } /* ifdef PROFILING */ 1201 1202 return exitflag; 1203 } 1204 1205 1206 c_int osqp_update_P_A(OSQPWorkspace *work, 1207 const c_float *Px_new, 1208 const c_int *Px_new_idx, 1209 c_int P_new_n, 1210 const c_float *Ax_new, 1211 const c_int *Ax_new_idx, 1212 c_int A_new_n) { 1213 c_int i; // For indexing 1214 c_int exitflag; // Exit flag 1215 c_int nnzP, nnzA; // Number of nonzeros in P and A 1216 1217 // Check if workspace has been initialized 1218 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1219 1220 version(PROFILING){ 1221 if (work.clear_update_time == 1) { 1222 work.clear_update_time = 0; 1223 work.info.update_time = 0.0; 1224 } 1225 osqp_tic(work.timer); // Start timer 1226 } /* ifdef PROFILING */ 1227 1228 nnzP = work.data.P.p[work.data.P.n]; 1229 nnzA = work.data.A.p[work.data.A.n]; 1230 1231 1232 if (Px_new_idx) { // Passing the index of elements changed 1233 // Check if number of elements is less or equal than the total number of 1234 // nonzeros in P 1235 if (P_new_n > nnzP) { 1236 version(PRINTING){ 1237 c_eprint("ERROR in %s: new number of elements (%i) greater than elements in P (%i)\n", 1238 __FUNCTION__.ptr, 1239 cast(c_int)P_new_n, 1240 cast(c_int)nnzP); 1241 } /* ifdef PRINTING */ 1242 return 1; 1243 } 1244 } 1245 1246 1247 if (Ax_new_idx) { // Passing the index of elements changed 1248 // Check if number of elements is less or equal than the total number of 1249 // nonzeros in A 1250 if (A_new_n > nnzA) { 1251 version(PRINTING){ 1252 c_eprint("ERROR in %s: new number of elements (%i) greater than elements in A (%i)\n", 1253 __FUNCTION__.ptr, 1254 cast(c_int)A_new_n, 1255 cast(c_int)nnzA); 1256 } /* ifdef PRINTING */ 1257 return 2; 1258 } 1259 } 1260 1261 if (work.settings.scaling) { 1262 // Unscale data 1263 unscale_data(work); 1264 } 1265 1266 // Update P elements 1267 if (Px_new_idx) { // Change only Px_new_idx 1268 for (i = 0; i < P_new_n; i++) { 1269 work.data.P.x[Px_new_idx[i]] = Px_new[i]; 1270 } 1271 } 1272 else // Change whole P 1273 { 1274 for (i = 0; i < nnzP; i++) { 1275 work.data.P.x[i] = Px_new[i]; 1276 } 1277 } 1278 1279 // Update A elements 1280 if (Ax_new_idx) { // Change only Ax_new_idx 1281 for (i = 0; i < A_new_n; i++) { 1282 work.data.A.x[Ax_new_idx[i]] = Ax_new[i]; 1283 } 1284 } 1285 else { // Change whole A 1286 for (i = 0; i < nnzA; i++) { 1287 work.data.A.x[i] = Ax_new[i]; 1288 } 1289 } 1290 1291 if (work.settings.scaling) { 1292 // Scale data 1293 scale_data(work); 1294 } 1295 1296 // Update linear system structure with new data 1297 exitflag = work.linsys_solver.update_matrices(work.linsys_solver, 1298 work.data.P, 1299 work.data.A); 1300 1301 // Reset solver information 1302 reset_info(work.info); 1303 1304 version(PRINTING){ 1305 1306 if (exitflag < 0) { 1307 c_eprint("ERROR in %s: new KKT matrix is not quasidefinite\n", __FUNCTION__.ptr); 1308 } 1309 } /* ifdef PRINTING */ 1310 1311 version(PROFILING){ 1312 work.info.update_time += osqp_toc(work.timer); 1313 } /* ifdef PROFILING */ 1314 1315 return exitflag; 1316 } 1317 1318 c_int osqp_update_rho(OSQPWorkspace *work, c_float rho_new) { 1319 c_int exitflag, i; 1320 1321 // Check if workspace has been initialized 1322 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1323 1324 // Check value of rho 1325 if (rho_new <= 0) { 1326 version(PRINTING){ 1327 c_eprint("ERROR in %s: rho must be positive\n", __FUNCTION__.ptr); 1328 } /* ifdef PRINTING */ 1329 return 1; 1330 } 1331 1332 version(PROFILING){ 1333 if (work.rho_update_from_solve == 0) { 1334 if (work.clear_update_time == 1) { 1335 work.clear_update_time = 0; 1336 work.info.update_time = 0.0; 1337 } 1338 osqp_tic(work.timer); // Start timer 1339 } 1340 } /* ifdef PROFILING */ 1341 1342 // Update rho in settings 1343 work.settings.rho = c_min(c_max(rho_new, RHO_MIN), RHO_MAX); 1344 1345 // Update rho_vec and rho_inv_vec 1346 for (i = 0; i < work.data.m; i++) { 1347 if (work.constr_type[i] == 0) { 1348 // Inequalities 1349 work.rho_vec[i] = work.settings.rho; 1350 work.rho_inv_vec[i] = 1. / work.settings.rho; 1351 } 1352 else if (work.constr_type[i] == 1) { 1353 // Equalities 1354 work.rho_vec[i] = RHO_EQ_OVER_RHO_INEQ * work.settings.rho; 1355 work.rho_inv_vec[i] = 1. / work.rho_vec[i]; 1356 } 1357 } 1358 1359 // Update rho_vec in KKT matrix 1360 exitflag = work.linsys_solver.update_rho_vec(work.linsys_solver, 1361 work.rho_vec); 1362 1363 version(PROFILING){ 1364 if (work.rho_update_from_solve == 0) 1365 work.info.update_time += osqp_toc(work.timer); 1366 } /* ifdef PROFILING */ 1367 1368 return exitflag; 1369 } 1370 1371 } // EMBEDDED != 1 1372 1373 /**************************** 1374 * Update problem settings * 1375 ****************************/ 1376 c_int osqp_update_max_iter(OSQPWorkspace *work, c_int max_iter_new) { 1377 1378 // Check if workspace has been initialized 1379 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1380 1381 // Check that max_iter is positive 1382 if (max_iter_new <= 0) { 1383 version(PRINTING){ 1384 c_eprint("ERROR in %s: max_iter must be positive\n", __FUNCTION__.ptr); 1385 } /* ifdef PRINTING */ 1386 return 1; 1387 } 1388 1389 // Update max_iter 1390 work.settings.max_iter = max_iter_new; 1391 1392 return 0; 1393 } 1394 1395 c_int osqp_update_eps_abs(OSQPWorkspace *work, c_float eps_abs_new) { 1396 1397 // Check if workspace has been initialized 1398 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1399 1400 // Check that eps_abs is positive 1401 if (eps_abs_new < 0.) { 1402 version(PRINTING){ 1403 c_eprint("ERROR in %s: eps_abs must be nonnegative\n", __FUNCTION__.ptr); 1404 } /* ifdef PRINTING */ 1405 return 1; 1406 } 1407 1408 // Update eps_abs 1409 work.settings.eps_abs = eps_abs_new; 1410 1411 return 0; 1412 } 1413 1414 c_int osqp_update_eps_rel(OSQPWorkspace *work, c_float eps_rel_new) { 1415 1416 // Check if workspace has been initialized 1417 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1418 1419 // Check that eps_rel is positive 1420 if (eps_rel_new < 0.) { 1421 version(PRINTING){ 1422 c_eprint("ERROR in %s: eps_rel must be nonnegative\n", __FUNCTION__.ptr); 1423 } /* ifdef PRINTING */ 1424 return 1; 1425 } 1426 1427 // Update eps_rel 1428 work.settings.eps_rel = eps_rel_new; 1429 1430 return 0; 1431 } 1432 1433 c_int osqp_update_eps_prim_inf(OSQPWorkspace *work, c_float eps_prim_inf_new) { 1434 1435 // Check if workspace has been initialized 1436 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1437 1438 // Check that eps_prim_inf is positive 1439 if (eps_prim_inf_new < 0.) { 1440 version(PRINTING){ 1441 c_eprint("ERROR in %s: eps_prim_inf must be nonnegative\n", __FUNCTION__.ptr); 1442 } /* ifdef PRINTING */ 1443 return 1; 1444 } 1445 1446 // Update eps_prim_inf 1447 work.settings.eps_prim_inf = eps_prim_inf_new; 1448 1449 return 0; 1450 } 1451 1452 c_int osqp_update_eps_dual_inf(OSQPWorkspace *work, c_float eps_dual_inf_new) { 1453 1454 // Check if workspace has been initialized 1455 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1456 1457 // Check that eps_dual_inf is positive 1458 if (eps_dual_inf_new < 0.) { 1459 version(PRINTING){ 1460 c_eprint("ERROR in %s: eps_dual_inf must be nonnegative\n", __FUNCTION__.ptr); 1461 } /* ifdef PRINTING */ 1462 return 1; 1463 } 1464 1465 // Update eps_dual_inf 1466 work.settings.eps_dual_inf = eps_dual_inf_new; 1467 1468 1469 return 0; 1470 } 1471 1472 c_int osqp_update_alpha(OSQPWorkspace *work, c_float alpha_new) { 1473 1474 // Check if workspace has been initialized 1475 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1476 1477 // Check that alpha is between 0 and 2 1478 if ((alpha_new <= 0.) || (alpha_new >= 2.)) { 1479 version(PRINTING){ 1480 c_eprint("ERROR in %s: alpha must be between 0 and 2\n", __FUNCTION__.ptr); 1481 } /* ifdef PRINTING */ 1482 return 1; 1483 } 1484 1485 // Update alpha 1486 work.settings.alpha = alpha_new; 1487 1488 return 0; 1489 } 1490 1491 c_int osqp_update_warm_start(OSQPWorkspace *work, c_int warm_start_new) { 1492 1493 // Check if workspace has been initialized 1494 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1495 1496 // Check that warm_start is either 0 or 1 1497 if ((warm_start_new != 0) && (warm_start_new != 1)) { 1498 version(PRINTING){ 1499 c_eprint("ERROR in %s: warm_start should be either 0 or 1\n", __FUNCTION__.ptr); 1500 } /* ifdef PRINTING */ 1501 return 1; 1502 } 1503 1504 // Update warm_start 1505 work.settings.warm_start = warm_start_new; 1506 1507 return 0; 1508 } 1509 1510 c_int osqp_update_scaled_termination(OSQPWorkspace *work, c_int scaled_termination_new) { 1511 1512 // Check if workspace has been initialized 1513 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1514 1515 // Check that scaled_termination is either 0 or 1 1516 if ((scaled_termination_new != 0) && (scaled_termination_new != 1)) { 1517 version(PRINTING){ 1518 c_eprint("ERROR in %s: scaled_termination should be either 0 or 1\n", __FUNCTION__.ptr); 1519 } /* ifdef PRINTING */ 1520 return 1; 1521 } 1522 1523 // Update scaled_termination 1524 work.settings.scaled_termination = scaled_termination_new; 1525 1526 return 0; 1527 } 1528 1529 c_int osqp_update_check_termination(OSQPWorkspace *work, c_int check_termination_new) { 1530 1531 // Check if workspace has been initialized 1532 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1533 1534 // Check that check_termination is nonnegative 1535 if (check_termination_new < 0) { 1536 version(PRINTING){ 1537 c_eprint("ERROR in %s: check_termination should be nonnegative\n", __FUNCTION__.ptr); 1538 } /* ifdef PRINTING */ 1539 return 1; 1540 } 1541 1542 // Update check_termination 1543 work.settings.check_termination = check_termination_new; 1544 1545 return 0; 1546 } 1547 1548 version(EMBEDDED){} 1549 else{ 1550 1551 c_int osqp_update_delta(OSQPWorkspace *work, c_float delta_new) { 1552 1553 // Check if workspace has been initialized 1554 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1555 1556 // Check that delta is positive 1557 if (delta_new <= 0.) { 1558 version(PRINTING){ 1559 c_eprint("ERROR in %s: delta must be positive\n", __FUNCTION__.ptr); 1560 } /* ifdef PRINTING */ 1561 return 1; 1562 } 1563 1564 // Update delta 1565 work.settings.delta = delta_new; 1566 1567 return 0; 1568 } 1569 1570 c_int osqp_update_polish(OSQPWorkspace *work, c_int polish_new) { 1571 1572 // Check if workspace has been initialized 1573 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1574 1575 // Check that polish is either 0 or 1 1576 if ((polish_new != 0) && (polish_new != 1)) { 1577 version(PRINTING){ 1578 c_eprint("ERROR in %s: polish should be either 0 or 1\n", __FUNCTION__.ptr); 1579 } /* ifdef PRINTING */ 1580 return 1; 1581 } 1582 1583 // Update polish 1584 work.settings.polish = polish_new; 1585 1586 version(PROFILING){ 1587 1588 // Reset polish time to zero 1589 work.info.polish_time = 0.0; 1590 } /* ifdef PROFILING */ 1591 1592 return 0; 1593 } 1594 1595 c_int osqp_update_polish_refine_iter(OSQPWorkspace *work, c_int polish_refine_iter_new) { 1596 1597 // Check if workspace has been initialized 1598 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1599 1600 // Check that polish_refine_iter is nonnegative 1601 if (polish_refine_iter_new < 0) { 1602 version(PRINTING){ 1603 c_eprint("ERROR in %s: polish_refine_iter must be nonnegative\n", __FUNCTION__.ptr); 1604 } /* ifdef PRINTING */ 1605 return 1; 1606 } 1607 1608 // Update polish_refine_iter 1609 work.settings.polish_refine_iter = polish_refine_iter_new; 1610 1611 return 0; 1612 } 1613 1614 c_int osqp_update_verbose(OSQPWorkspace *work, c_int verbose_new) { 1615 1616 // Check if workspace has been initialized 1617 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1618 1619 // Check that verbose is either 0 or 1 1620 if ((verbose_new != 0) && (verbose_new != 1)) { 1621 1622 version(PRINTING){ 1623 c_eprint("ERROR in %s: verbose should be either 0 or 1\n", __FUNCTION__.ptr); 1624 } /* ifdef PRINTING */ 1625 return 1; 1626 } 1627 1628 // Update verbose 1629 work.settings.verbose = verbose_new; 1630 1631 return 0; 1632 } 1633 1634 } // EMBEDDED 1635 1636 version(PROFILING){ 1637 1638 c_int osqp_update_time_limit(OSQPWorkspace *work, c_float time_limit_new) { 1639 1640 // Check if workspace has been initialized 1641 if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__); 1642 1643 // Check that time_limit is nonnegative 1644 if (time_limit_new < 0.) { 1645 version(PRINTING){ 1646 c_print("time_limit must be nonnegative\n"); 1647 } /* ifdef PRINTING */ 1648 return 1; 1649 } 1650 1651 // Update time_limit 1652 work.settings.time_limit = time_limit_new; 1653 1654 return 0; 1655 } 1656 } /* ifdef PROFILING */