Squashed 'third_party/osqp/' content from commit 33454b3e23

Change-Id: I056df0582ca06664e86554c341a94c47ab932001
git-subtree-dir: third_party/osqp
git-subtree-split: 33454b3e236f1f44193bfbbb6b8c8e71f8f04e9a
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/src/osqp.c b/src/osqp.c
new file mode 100644
index 0000000..20cb1af
--- /dev/null
+++ b/src/osqp.c
@@ -0,0 +1,1617 @@
+#include "osqp.h"
+#include "auxil.h"
+#include "util.h"
+#include "scaling.h"
+#include "glob_opts.h"
+#include "error.h"
+
+
+#ifndef EMBEDDED
+# include "polish.h"
+#endif /* ifndef EMBEDDED */
+
+#ifdef CTRLC
+# include "ctrlc.h"
+#endif /* ifdef CTRLC */
+
+#ifndef EMBEDDED
+# include "lin_sys.h"
+#endif /* ifndef EMBEDDED */
+
+/**********************
+* Main API Functions *
+**********************/
+void osqp_set_default_settings(OSQPSettings *settings) {
+
+  settings->rho           = (c_float)RHO;            /* ADMM step */
+  settings->sigma         = (c_float)SIGMA;          /* ADMM step */
+  settings->scaling = SCALING;                       /* heuristic problem scaling */
+#if EMBEDDED != 1
+  settings->adaptive_rho           = ADAPTIVE_RHO;
+  settings->adaptive_rho_interval  = ADAPTIVE_RHO_INTERVAL;
+  settings->adaptive_rho_tolerance = (c_float)ADAPTIVE_RHO_TOLERANCE;
+
+# ifdef PROFILING
+  settings->adaptive_rho_fraction = (c_float)ADAPTIVE_RHO_FRACTION;
+# endif /* ifdef PROFILING */
+#endif  /* if EMBEDDED != 1 */
+
+  settings->max_iter      = MAX_ITER;                /* maximum iterations to
+                                                        take */
+  settings->eps_abs       = (c_float)EPS_ABS;        /* absolute convergence
+                                                        tolerance */
+  settings->eps_rel       = (c_float)EPS_REL;        /* relative convergence
+                                                        tolerance */
+  settings->eps_prim_inf  = (c_float)EPS_PRIM_INF;   /* primal infeasibility
+                                                        tolerance */
+  settings->eps_dual_inf  = (c_float)EPS_DUAL_INF;   /* dual infeasibility
+                                                        tolerance */
+  settings->alpha         = (c_float)ALPHA;          /* relaxation parameter */
+  settings->linsys_solver = LINSYS_SOLVER;           /* relaxation parameter */
+
+#ifndef EMBEDDED
+  settings->delta              = DELTA;              /* regularization parameter
+                                                        for polish */
+  settings->polish             = POLISH;             /* ADMM solution polish: 1
+                                                      */
+  settings->polish_refine_iter = POLISH_REFINE_ITER; /* iterative refinement
+                                                        steps in polish */
+  settings->verbose            = VERBOSE;            /* print output */
+#endif /* ifndef EMBEDDED */
+
+  settings->scaled_termination = SCALED_TERMINATION; /* Evaluate scaled
+                                                        termination criteria*/
+  settings->check_termination  = CHECK_TERMINATION;  /* Interval for evaluating
+                                                        termination criteria */
+  settings->warm_start         = WARM_START;         /* warm starting */
+
+#ifdef PROFILING
+  settings->time_limit = TIME_LIMIT;
+#endif /* ifdef PROFILING */
+}
+
+#ifndef EMBEDDED
+
+
+c_int osqp_setup(OSQPWorkspace** workp, const OSQPData *data, const OSQPSettings *settings) {
+  c_int exitflag;
+
+  OSQPWorkspace * work;
+
+  // Validate data
+  if (validate_data(data)) return osqp_error(OSQP_DATA_VALIDATION_ERROR);
+
+  // Validate settings
+  if (validate_settings(settings)) return osqp_error(OSQP_SETTINGS_VALIDATION_ERROR);
+
+  // Allocate empty workspace
+  work = c_calloc(1, sizeof(OSQPWorkspace));
+  if (!(work)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  *workp = work;
+
+  // Start and allocate directly timer
+# ifdef PROFILING
+  work->timer = c_malloc(sizeof(OSQPTimer));
+  if (!(work->timer)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  osqp_tic(work->timer);
+# endif /* ifdef PROFILING */
+
+  // Copy problem data into workspace
+  work->data = c_malloc(sizeof(OSQPData));
+  if (!(work->data)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->data->n = data->n;
+  work->data->m = data->m;
+
+  // Cost function
+  work->data->P = copy_csc_mat(data->P);
+  work->data->q = vec_copy(data->q, data->n);
+  if (!(work->data->P) || !(work->data->q)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Constraints
+  work->data->A = copy_csc_mat(data->A);
+  if (!(work->data->A)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->data->l = vec_copy(data->l, data->m);
+  work->data->u = vec_copy(data->u, data->m);
+  if ( data->m && (!(work->data->l) || !(work->data->u)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Vectorized rho parameter
+  work->rho_vec     = c_malloc(data->m * sizeof(c_float));
+  work->rho_inv_vec = c_malloc(data->m * sizeof(c_float));
+  if ( data->m && (!(work->rho_vec) || !(work->rho_inv_vec)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Type of constraints
+  work->constr_type = c_calloc(data->m, sizeof(c_int));
+  if (data->m && !(work->constr_type)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Allocate internal solver variables (ADMM steps)
+  work->x        = c_calloc(data->n, sizeof(c_float));
+  work->z        = c_calloc(data->m, sizeof(c_float));
+  work->xz_tilde = c_calloc(data->n + data->m, sizeof(c_float));
+  work->x_prev   = c_calloc(data->n, sizeof(c_float));
+  work->z_prev   = c_calloc(data->m, sizeof(c_float));
+  work->y        = c_calloc(data->m, sizeof(c_float));
+  if (!(work->x) || !(work->xz_tilde) || !(work->x_prev))
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if ( data->m && (!(work->z) || !(work->z_prev) || !(work->y)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Initialize variables x, y, z to 0
+  cold_start(work);
+
+  // Primal and dual residuals variables
+  work->Ax  = c_calloc(data->m, sizeof(c_float));
+  work->Px  = c_calloc(data->n, sizeof(c_float));
+  work->Aty = c_calloc(data->n, sizeof(c_float));
+
+  // Primal infeasibility variables
+  work->delta_y   = c_calloc(data->m, sizeof(c_float));
+  work->Atdelta_y = c_calloc(data->n, sizeof(c_float));
+
+  // Dual infeasibility variables
+  work->delta_x  = c_calloc(data->n, sizeof(c_float));
+  work->Pdelta_x = c_calloc(data->n, sizeof(c_float));
+  work->Adelta_x = c_calloc(data->m, sizeof(c_float));
+
+  if (!(work->Px) || !(work->Aty) || !(work->Atdelta_y) ||
+      !(work->delta_x) || !(work->Pdelta_x))
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if ( data->m && (!(work->Ax) || !(work->delta_y) || !(work->Adelta_x)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Copy settings
+  work->settings = copy_settings(settings);
+  if (!(work->settings)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Perform scaling
+  if (settings->scaling) {
+    // Allocate scaling structure
+    work->scaling = c_malloc(sizeof(OSQPScaling));
+    if (!(work->scaling)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    work->scaling->D    = c_malloc(data->n * sizeof(c_float));
+    work->scaling->Dinv = c_malloc(data->n * sizeof(c_float));
+    work->scaling->E    = c_malloc(data->m * sizeof(c_float));
+    work->scaling->Einv = c_malloc(data->m * sizeof(c_float));
+    if (!(work->scaling->D) || !(work->scaling->Dinv))
+      return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    if ( data->m && (!(work->scaling->E) || !(work->scaling->Einv)) )
+      return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+
+    // Allocate workspace variables used in scaling
+    work->D_temp   = c_malloc(data->n * sizeof(c_float));
+    work->D_temp_A = c_malloc(data->n * sizeof(c_float));
+    work->E_temp   = c_malloc(data->m * sizeof(c_float));
+    // if (!(work->D_temp) || !(work->D_temp_A) || !(work->E_temp))
+    //   return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    if (!(work->D_temp) || !(work->D_temp_A)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+    if (data->m && !(work->E_temp))           return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+    // Scale data
+    scale_data(work);
+  } else {
+    work->scaling  = OSQP_NULL;
+    work->D_temp   = OSQP_NULL;
+    work->D_temp_A = OSQP_NULL;
+    work->E_temp   = OSQP_NULL;
+  }
+
+  // Set type of constraints
+  set_rho_vec(work);
+
+  // Load linear system solver
+  if (load_linsys_solver(work->settings->linsys_solver)) return osqp_error(OSQP_LINSYS_SOLVER_LOAD_ERROR);
+
+  // Initialize linear system solver structure
+  exitflag = init_linsys_solver(&(work->linsys_solver), work->data->P, work->data->A,
+                                work->settings->sigma, work->rho_vec,
+                                work->settings->linsys_solver, 0);
+
+  if (exitflag) {
+    return osqp_error(exitflag);
+  }
+
+  // Initialize active constraints structure
+  work->pol = c_malloc(sizeof(OSQPPolish));
+  if (!(work->pol)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->pol->Alow_to_A = c_malloc(data->m * sizeof(c_int));
+  work->pol->Aupp_to_A = c_malloc(data->m * sizeof(c_int));
+  work->pol->A_to_Alow = c_malloc(data->m * sizeof(c_int));
+  work->pol->A_to_Aupp = c_malloc(data->m * sizeof(c_int));
+  work->pol->x         = c_malloc(data->n * sizeof(c_float));
+  work->pol->z         = c_malloc(data->m * sizeof(c_float));
+  work->pol->y         = c_malloc(data->m * sizeof(c_float));
+  if (!(work->pol->x)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if ( data->m && (!(work->pol->Alow_to_A) || !(work->pol->Aupp_to_A) ||
+      !(work->pol->A_to_Alow) || !(work->pol->A_to_Aupp) ||
+      !(work->pol->z) || !(work->pol->y)) )
+    return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Allocate solution
+  work->solution = c_calloc(1, sizeof(OSQPSolution));
+  if (!(work->solution)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->solution->x = c_calloc(1, data->n * sizeof(c_float));
+  work->solution->y = c_calloc(1, data->m * sizeof(c_float));
+  if (!(work->solution->x))            return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  if (data->m && !(work->solution->y)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+
+  // Allocate and initialize information
+  work->info = c_calloc(1, sizeof(OSQPInfo));
+  if (!(work->info)) return osqp_error(OSQP_MEM_ALLOC_ERROR);
+  work->info->status_polish = 0;              // Polishing not performed
+  update_status(work->info, OSQP_UNSOLVED);
+# ifdef PROFILING
+  work->info->solve_time  = 0.0;                   // Solve time to zero
+  work->info->update_time = 0.0;                   // Update time to zero
+  work->info->polish_time = 0.0;                   // Polish time to zero
+  work->info->run_time    = 0.0;                   // Total run time to zero
+  work->info->setup_time  = osqp_toc(work->timer); // Update timer information
+
+  work->first_run         = 1;
+  work->clear_update_time = 0;
+  work->rho_update_from_solve = 0;
+# endif /* ifdef PROFILING */
+  work->info->rho_updates  = 0;                    // Rho updates set to 0
+  work->info->rho_estimate = work->settings->rho;  // Best rho estimate
+
+  // Print header
+# ifdef PRINTING
+  if (work->settings->verbose) print_setup_header(work);
+  work->summary_printed = 0; // Initialize last summary  to not printed
+# endif /* ifdef PRINTING */
+
+
+  // If adaptive rho and automatic interval, but profiling disabled, we need to
+  // set the interval to a default value
+# ifndef PROFILING
+  if (work->settings->adaptive_rho && !work->settings->adaptive_rho_interval) {
+    if (work->settings->check_termination) {
+      // If check_termination is enabled, we set it to a multiple of the check
+      // termination interval
+      work->settings->adaptive_rho_interval = ADAPTIVE_RHO_MULTIPLE_TERMINATION *
+                                              work->settings->check_termination;
+    } else {
+      // If check_termination is disabled we set it to a predefined fix number
+      work->settings->adaptive_rho_interval = ADAPTIVE_RHO_FIXED;
+    }
+  }
+# endif /* ifndef PROFILING */
+
+  // Return exit flag
+  return 0;
+}
+
+#endif // #ifndef EMBEDDED
+
+
+c_int osqp_solve(OSQPWorkspace *work) {
+
+  c_int exitflag;
+  c_int iter;
+  c_int compute_cost_function; // Boolean: compute the cost function in the loop or not
+  c_int can_check_termination; // Boolean: check termination or not
+
+#ifdef PROFILING
+  c_float temp_run_time;       // Temporary variable to store current run time
+#endif /* ifdef PROFILING */
+
+#ifdef PRINTING
+  c_int can_print;             // Boolean whether you can print
+#endif /* ifdef PRINTING */
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1)
+    work->info->update_time = 0.0;
+  work->rho_update_from_solve = 1;
+#endif /* ifdef PROFILING */
+
+  // Initialize variables
+  exitflag              = 0;
+  can_check_termination = 0;
+#ifdef PRINTING
+  can_print = work->settings->verbose;
+#endif /* ifdef PRINTING */
+#ifdef PRINTING
+  compute_cost_function = work->settings->verbose; // Compute cost function only
+                                                   // if verbose is on
+#else /* ifdef PRINTING */
+  compute_cost_function = 0;                       // Never compute cost
+                                                   // function during the
+                                                   // iterations if no printing
+                                                   // enabled
+#endif /* ifdef PRINTING */
+
+
+
+#ifdef PROFILING
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+
+#ifdef PRINTING
+
+  if (work->settings->verbose) {
+    // Print Header for every column
+    print_header();
+  }
+#endif /* ifdef PRINTING */
+
+#ifdef CTRLC
+
+  // initialize Ctrl-C support
+  osqp_start_interrupt_listener();
+#endif /* ifdef CTRLC */
+
+  // Initialize variables (cold start or warm start depending on settings)
+  if (!work->settings->warm_start) cold_start(work);  // If not warm start ->
+                                                      // set x, z, y to zero
+
+  // Main ADMM algorithm
+  for (iter = 1; iter <= work->settings->max_iter; iter++) {
+    // Update x_prev, z_prev (preallocated, no malloc)
+    swap_vectors(&(work->x), &(work->x_prev));
+    swap_vectors(&(work->z), &(work->z_prev));
+
+    /* ADMM STEPS */
+    /* Compute \tilde{x}^{k+1}, \tilde{z}^{k+1} */
+    update_xz_tilde(work);
+
+    /* Compute x^{k+1} */
+    update_x(work);
+
+    /* Compute z^{k+1} */
+    update_z(work);
+
+    /* Compute y^{k+1} */
+    update_y(work);
+
+    /* End of ADMM Steps */
+
+#ifdef CTRLC
+
+    // Check the interrupt signal
+    if (osqp_is_interrupted()) {
+      update_status(work->info, OSQP_SIGINT);
+# ifdef PRINTING
+      c_print("Solver interrupted\n");
+# endif /* ifdef PRINTING */
+      exitflag = 1;
+      goto exit;
+    }
+#endif /* ifdef CTRLC */
+
+#ifdef PROFILING
+
+    // Check if solver time_limit is enabled. In case, check if the current
+    // run time is more than the time_limit option.
+    if (work->first_run) {
+      temp_run_time = work->info->setup_time + osqp_toc(work->timer);
+    }
+    else {
+      temp_run_time = work->info->update_time + osqp_toc(work->timer);
+    }
+
+    if (work->settings->time_limit &&
+        (temp_run_time >= work->settings->time_limit)) {
+      update_status(work->info, OSQP_TIME_LIMIT_REACHED);
+# ifdef PRINTING
+      if (work->settings->verbose) c_print("run time limit reached\n");
+      can_print = 0;  // Not printing at this iteration
+# endif /* ifdef PRINTING */
+      break;
+    }
+#endif /* ifdef PROFILING */
+
+
+    // Can we check for termination ?
+    can_check_termination = work->settings->check_termination &&
+                            (iter % work->settings->check_termination == 0);
+
+#ifdef PRINTING
+
+    // Can we print ?
+    can_print = work->settings->verbose &&
+                ((iter % PRINT_INTERVAL == 0) || (iter == 1));
+
+    if (can_check_termination || can_print) { // Update status in either of
+                                              // these cases
+      // Update information
+      update_info(work, iter, compute_cost_function, 0);
+
+      if (can_print) {
+        // Print summary
+        print_summary(work);
+      }
+
+      if (can_check_termination) {
+        // Check algorithm termination
+        if (check_termination(work, 0)) {
+          // Terminate algorithm
+          break;
+        }
+      }
+    }
+#else /* ifdef PRINTING */
+
+    if (can_check_termination) {
+      // Update information and compute also objective value
+      update_info(work, iter, compute_cost_function, 0);
+
+      // Check algorithm termination
+      if (check_termination(work, 0)) {
+        // Terminate algorithm
+        break;
+      }
+    }
+#endif /* ifdef PRINTING */
+
+
+#if EMBEDDED != 1
+# ifdef PROFILING
+
+    // If adaptive rho with automatic interval, check if the solve time is a
+    // certain fraction
+    // of the setup time.
+    if (work->settings->adaptive_rho && !work->settings->adaptive_rho_interval) {
+      // Check time
+      if (osqp_toc(work->timer) >
+          work->settings->adaptive_rho_fraction * work->info->setup_time) {
+        // Enough time has passed. We now get the number of iterations between
+        // the updates.
+        if (work->settings->check_termination) {
+          // If check_termination is enabled, we round the number of iterations
+          // between
+          // rho updates to the closest multiple of check_termination
+          work->settings->adaptive_rho_interval = (c_int)c_roundmultiple(iter,
+                                                                         work->settings->check_termination);
+        } else {
+          // If check_termination is disabled, we round the number of iterations
+          // between
+          // updates to the closest multiple of the default check_termination
+          // interval.
+          work->settings->adaptive_rho_interval = (c_int)c_roundmultiple(iter,
+                                                                         CHECK_TERMINATION);
+        }
+
+        // Make sure the interval is not 0 and at least check_termination times
+        work->settings->adaptive_rho_interval = c_max(
+          work->settings->adaptive_rho_interval,
+          work->settings->check_termination);
+      } // If time condition is met
+    }   // If adaptive rho enabled and interval set to auto
+# else // PROFILING
+    if (work->settings->adaptive_rho && !work->settings->adaptive_rho_interval) {
+      // Set adaptive_rho_interval to constant value
+      if (work->settings->check_termination) {
+        // If check_termination is enabled, we set it to a multiple of the check
+        // termination interval
+        work->settings->adaptive_rho_interval = ADAPTIVE_RHO_MULTIPLE_TERMINATION *
+                                                work->settings->check_termination;
+      } else {
+        // If check_termination is disabled we set it to a predefined fix number
+        work->settings->adaptive_rho_interval = ADAPTIVE_RHO_FIXED;
+      }
+    }
+# endif /* ifdef PROFILING */
+
+    // Adapt rho
+    if (work->settings->adaptive_rho &&
+        work->settings->adaptive_rho_interval &&
+        (iter % work->settings->adaptive_rho_interval == 0)) {
+      // Update info with the residuals if it hasn't been done before
+# ifdef PRINTING
+
+      if (!can_check_termination && !can_print) {
+        // Information has not been computed neither for termination or printing
+        // reasons
+        update_info(work, iter, compute_cost_function, 0);
+      }
+# else /* ifdef PRINTING */
+
+      if (!can_check_termination) {
+        // Information has not been computed before for termination check
+        update_info(work, iter, compute_cost_function, 0);
+      }
+# endif /* ifdef PRINTING */
+
+      // Actually update rho
+      if (adapt_rho(work)) {
+# ifdef PRINTING
+        c_eprint("Failed rho update");
+# endif // PRINTING
+        exitflag = 1;
+        goto exit;
+      }
+    }
+#endif // EMBEDDED != 1
+
+  }        // End of ADMM for loop
+
+
+  // Update information and check termination condition if it hasn't been done
+  // during last iteration (max_iter reached or check_termination disabled)
+  if (!can_check_termination) {
+    /* Update information */
+#ifdef PRINTING
+
+    if (!can_print) {
+      // Update info only if it hasn't been updated before for printing
+      // reasons
+      update_info(work, iter - 1, compute_cost_function, 0);
+    }
+#else /* ifdef PRINTING */
+
+    // If no printing is enabled, update info directly
+    update_info(work, iter - 1, compute_cost_function, 0);
+#endif /* ifdef PRINTING */
+
+#ifdef PRINTING
+
+    /* Print summary */
+    if (work->settings->verbose && !work->summary_printed) print_summary(work);
+#endif /* ifdef PRINTING */
+
+    /* Check whether a termination criterion is triggered */
+    check_termination(work, 0);
+  }
+
+  // Compute objective value in case it was not
+  // computed during the iterations
+  if (!compute_cost_function && has_solution(work->info)){
+    work->info->obj_val = compute_obj_val(work, work->x);
+  }
+
+
+#ifdef PRINTING
+  /* Print summary for last iteration */
+  if (work->settings->verbose && !work->summary_printed) {
+    print_summary(work);
+  }
+#endif /* ifdef PRINTING */
+
+  /* if max iterations reached, change status accordingly */
+  if (work->info->status_val == OSQP_UNSOLVED) {
+    if (!check_termination(work, 1)) { // Try to check for approximate
+      update_status(work->info, OSQP_MAX_ITER_REACHED);
+    }
+  }
+
+#ifdef PROFILING
+  /* if time-limit reached check termination and update status accordingly */
+ if (work->info->status_val == OSQP_TIME_LIMIT_REACHED) {
+    if (!check_termination(work, 1)) { // Try for approximate solutions
+      update_status(work->info, OSQP_TIME_LIMIT_REACHED); /* Change update status back to OSQP_TIME_LIMIT_REACHED */
+    }
+  }
+#endif /* ifdef PROFILING */
+
+
+#if EMBEDDED != 1
+  /* Update rho estimate */
+  work->info->rho_estimate = compute_rho_estimate(work);
+#endif /* if EMBEDDED != 1 */
+
+  /* Update solve time */
+#ifdef PROFILING
+  work->info->solve_time = osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+
+#ifndef EMBEDDED
+  // Polish the obtained solution
+  if (work->settings->polish && (work->info->status_val == OSQP_SOLVED))
+    polish(work);
+#endif /* ifndef EMBEDDED */
+
+#ifdef PROFILING
+  /* Update total time */
+  if (work->first_run) {
+    // total time: setup + solve + polish
+    work->info->run_time = work->info->setup_time +
+                           work->info->solve_time +
+                           work->info->polish_time;
+  } else {
+    // total time: update + solve + polish
+    work->info->run_time = work->info->update_time +
+                           work->info->solve_time +
+                           work->info->polish_time;
+  }
+
+  // Indicate that the solve function has already been executed
+  if (work->first_run) work->first_run = 0;
+
+  // Indicate that the update_time should be set to zero
+  work->clear_update_time = 1;
+
+  // Indicate that osqp_update_rho is not called from osqp_solve
+  work->rho_update_from_solve = 0;
+#endif /* ifdef PROFILING */
+
+#ifdef PRINTING
+  /* Print final footer */
+  if (work->settings->verbose) print_footer(work->info, work->settings->polish);
+#endif /* ifdef PRINTING */
+
+  // Store solution
+  store_solution(work);
+
+
+// Define exit flag for quitting function
+#if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1
+exit:
+#endif /* if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1 */
+
+#ifdef CTRLC
+  // Restore previous signal handler
+  osqp_end_interrupt_listener();
+#endif /* ifdef CTRLC */
+
+  return exitflag;
+}
+
+
+#ifndef EMBEDDED
+
+c_int osqp_cleanup(OSQPWorkspace *work) {
+  c_int exitflag = 0;
+
+  if (work) { // If workspace has been allocated
+    // Free Data
+    if (work->data) {
+      if (work->data->P) csc_spfree(work->data->P);
+      if (work->data->A) csc_spfree(work->data->A);
+      if (work->data->q) c_free(work->data->q);
+      if (work->data->l) c_free(work->data->l);
+      if (work->data->u) c_free(work->data->u);
+      c_free(work->data);
+    }
+
+    // Free scaling variables
+    if (work->scaling){
+      if (work->scaling->D)    c_free(work->scaling->D);
+      if (work->scaling->Dinv) c_free(work->scaling->Dinv);
+      if (work->scaling->E)    c_free(work->scaling->E);
+      if (work->scaling->Einv) c_free(work->scaling->Einv);
+      c_free(work->scaling);
+    }
+
+    // Free temp workspace variables for scaling
+    if (work->D_temp)   c_free(work->D_temp);
+    if (work->D_temp_A) c_free(work->D_temp_A);
+    if (work->E_temp)   c_free(work->E_temp);
+
+    // Free linear system solver structure
+    if (work->linsys_solver) {
+      if (work->linsys_solver->free) {
+        work->linsys_solver->free(work->linsys_solver);
+      }
+    }
+
+    // Unload linear system solver after free
+    if (work->settings) {
+      exitflag = unload_linsys_solver(work->settings->linsys_solver);
+    }
+
+#ifndef EMBEDDED
+    // Free active constraints structure
+    if (work->pol) {
+      if (work->pol->Alow_to_A) c_free(work->pol->Alow_to_A);
+      if (work->pol->Aupp_to_A) c_free(work->pol->Aupp_to_A);
+      if (work->pol->A_to_Alow) c_free(work->pol->A_to_Alow);
+      if (work->pol->A_to_Aupp) c_free(work->pol->A_to_Aupp);
+      if (work->pol->x)         c_free(work->pol->x);
+      if (work->pol->z)         c_free(work->pol->z);
+      if (work->pol->y)         c_free(work->pol->y);
+      c_free(work->pol);
+    }
+#endif /* ifndef EMBEDDED */
+
+    // Free other Variables
+    if (work->rho_vec)     c_free(work->rho_vec);
+    if (work->rho_inv_vec) c_free(work->rho_inv_vec);
+#if EMBEDDED != 1
+    if (work->constr_type) c_free(work->constr_type);
+#endif
+    if (work->x)           c_free(work->x);
+    if (work->z)           c_free(work->z);
+    if (work->xz_tilde)    c_free(work->xz_tilde);
+    if (work->x_prev)      c_free(work->x_prev);
+    if (work->z_prev)      c_free(work->z_prev);
+    if (work->y)           c_free(work->y);
+    if (work->Ax)          c_free(work->Ax);
+    if (work->Px)          c_free(work->Px);
+    if (work->Aty)         c_free(work->Aty);
+    if (work->delta_y)     c_free(work->delta_y);
+    if (work->Atdelta_y)   c_free(work->Atdelta_y);
+    if (work->delta_x)     c_free(work->delta_x);
+    if (work->Pdelta_x)    c_free(work->Pdelta_x);
+    if (work->Adelta_x)    c_free(work->Adelta_x);
+
+    // Free Settings
+    if (work->settings) c_free(work->settings);
+
+    // Free solution
+    if (work->solution) {
+      if (work->solution->x) c_free(work->solution->x);
+      if (work->solution->y) c_free(work->solution->y);
+      c_free(work->solution);
+    }
+
+    // Free information
+    if (work->info) c_free(work->info);
+
+# ifdef PROFILING
+    // Free timer
+    if (work->timer) c_free(work->timer);
+# endif /* ifdef PROFILING */
+
+    // Free work
+    c_free(work);
+  }
+
+  return exitflag;
+}
+
+#endif // #ifndef EMBEDDED
+
+
+/************************
+* Update problem data  *
+************************/
+c_int osqp_update_lin_cost(OSQPWorkspace *work, const c_float *q_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Replace q by the new vector
+  prea_vec_copy(q_new, work->data->q, work->data->n);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->D, work->data->q, work->data->q, work->data->n);
+    vec_mult_scalar(work->data->q, work->scaling->c, work->data->n);
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return 0;
+}
+
+c_int osqp_update_bounds(OSQPWorkspace *work,
+                         const c_float *l_new,
+                         const c_float *u_new) {
+  c_int i, exitflag = 0;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Check if lower bound is smaller than upper bound
+  for (i = 0; i < work->data->m; i++) {
+    if (l_new[i] > u_new[i]) {
+#ifdef PRINTING
+      c_eprint("lower bound must be lower than or equal to upper bound");
+#endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  // Replace l and u by the new vectors
+  prea_vec_copy(l_new, work->data->l, work->data->m);
+  prea_vec_copy(u_new, work->data->u, work->data->m);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->E, work->data->l, work->data->l, work->data->m);
+    vec_ew_prod(work->scaling->E, work->data->u, work->data->u, work->data->m);
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#if EMBEDDED != 1
+  // Update rho_vec and refactor if constraints type changes
+  exitflag = update_rho_vec(work);
+#endif // EMBEDDED != 1
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_update_lower_bound(OSQPWorkspace *work, const c_float *l_new) {
+  c_int i, exitflag = 0;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Replace l by the new vector
+  prea_vec_copy(l_new, work->data->l, work->data->m);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->E, work->data->l, work->data->l, work->data->m);
+  }
+
+  // Check if lower bound is smaller than upper bound
+  for (i = 0; i < work->data->m; i++) {
+    if (work->data->l[i] > work->data->u[i]) {
+#ifdef PRINTING
+      c_eprint("upper bound must be greater than or equal to lower bound");
+#endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#if EMBEDDED != 1
+  // Update rho_vec and refactor if constraints type changes
+  exitflag = update_rho_vec(work);
+#endif // EMBEDDED ! =1
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_update_upper_bound(OSQPWorkspace *work, const c_float *u_new) {
+  c_int i, exitflag = 0;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  // Replace u by the new vector
+  prea_vec_copy(u_new, work->data->u, work->data->m);
+
+  // Scaling
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->E, work->data->u, work->data->u, work->data->m);
+  }
+
+  // Check if upper bound is greater than lower bound
+  for (i = 0; i < work->data->m; i++) {
+    if (work->data->u[i] < work->data->l[i]) {
+#ifdef PRINTING
+      c_eprint("lower bound must be lower than or equal to upper bound");
+#endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  // Reset solver information
+  reset_info(work->info);
+
+#if EMBEDDED != 1
+  // Update rho_vec and refactor if constraints type changes
+  exitflag = update_rho_vec(work);
+#endif // EMBEDDED != 1
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_warm_start(OSQPWorkspace *work, const c_float *x, const c_float *y) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Update warm_start setting to true
+  if (!work->settings->warm_start) work->settings->warm_start = 1;
+
+  // Copy primal and dual variables into the iterates
+  prea_vec_copy(x, work->x, work->data->n);
+  prea_vec_copy(y, work->y, work->data->m);
+
+  // Scale iterates
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->Dinv, work->x, work->x, work->data->n);
+    vec_ew_prod(work->scaling->Einv, work->y, work->y, work->data->m);
+    vec_mult_scalar(work->y, work->scaling->c, work->data->m);
+  }
+
+  // Compute Ax = z and store it in z
+  mat_vec(work->data->A, work->x, work->z, 0);
+
+  return 0;
+}
+
+c_int osqp_warm_start_x(OSQPWorkspace *work, const c_float *x) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Update warm_start setting to true
+  if (!work->settings->warm_start) work->settings->warm_start = 1;
+
+  // Copy primal variable into the iterate x
+  prea_vec_copy(x, work->x, work->data->n);
+
+  // Scale iterate
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->Dinv, work->x, work->x, work->data->n);
+  }
+
+  // Compute Ax = z and store it in z
+  mat_vec(work->data->A, work->x, work->z, 0);
+
+  return 0;
+}
+
+c_int osqp_warm_start_y(OSQPWorkspace *work, const c_float *y) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Update warm_start setting to true
+  if (!work->settings->warm_start) work->settings->warm_start = 1;
+
+  // Copy dual variable into the iterate y
+  prea_vec_copy(y, work->y, work->data->m);
+
+  // Scale iterate
+  if (work->settings->scaling) {
+    vec_ew_prod(work->scaling->Einv, work->y, work->y, work->data->m);
+    vec_mult_scalar(work->y, work->scaling->c, work->data->m);
+  }
+
+  return 0;
+}
+
+
+#if EMBEDDED != 1
+
+c_int osqp_update_P(OSQPWorkspace *work,
+                    const c_float *Px_new,
+                    const c_int   *Px_new_idx,
+                    c_int          P_new_n) {
+  c_int i;        // For indexing
+  c_int exitflag; // Exit flag
+  c_int nnzP;     // Number of nonzeros in P
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  nnzP = work->data->P->p[work->data->P->n];
+
+  if (Px_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in P
+    if (P_new_n > nnzP) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in P (%i)",
+               (int)P_new_n,
+               (int)nnzP);
+# endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Unscale data
+    unscale_data(work);
+  }
+
+  // Update P elements
+  if (Px_new_idx) { // Change only Px_new_idx
+    for (i = 0; i < P_new_n; i++) {
+      work->data->P->x[Px_new_idx[i]] = Px_new[i];
+    }
+  }
+  else // Change whole P
+  {
+    for (i = 0; i < nnzP; i++) {
+      work->data->P->x[i] = Px_new[i];
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Scale data
+    scale_data(work);
+  }
+
+  // Update linear system structure with new data
+  exitflag = work->linsys_solver->update_matrices(work->linsys_solver,
+                                                  work->data->P,
+                                                  work->data->A);
+
+  // Reset solver information
+  reset_info(work->info);
+
+# ifdef PRINTING
+
+  if (exitflag < 0) {
+    c_eprint("new KKT matrix is not quasidefinite");
+  }
+# endif /* ifdef PRINTING */
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+
+c_int osqp_update_A(OSQPWorkspace *work,
+                    const c_float *Ax_new,
+                    const c_int   *Ax_new_idx,
+                    c_int          A_new_n) {
+  c_int i;        // For indexing
+  c_int exitflag; // Exit flag
+  c_int nnzA;     // Number of nonzeros in A
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  nnzA = work->data->A->p[work->data->A->n];
+
+  if (Ax_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in A
+    if (A_new_n > nnzA) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in A (%i)",
+               (int)A_new_n,
+               (int)nnzA);
+# endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Unscale data
+    unscale_data(work);
+  }
+
+  // Update A elements
+  if (Ax_new_idx) { // Change only Ax_new_idx
+    for (i = 0; i < A_new_n; i++) {
+      work->data->A->x[Ax_new_idx[i]] = Ax_new[i];
+    }
+  }
+  else { // Change whole A
+    for (i = 0; i < nnzA; i++) {
+      work->data->A->x[i] = Ax_new[i];
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Scale data
+    scale_data(work);
+  }
+
+  // Update linear system structure with new data
+  exitflag = work->linsys_solver->update_matrices(work->linsys_solver,
+                                                  work->data->P,
+                                                  work->data->A);
+
+  // Reset solver information
+  reset_info(work->info);
+
+# ifdef PRINTING
+
+  if (exitflag < 0) {
+    c_eprint("new KKT matrix is not quasidefinite");
+  }
+# endif /* ifdef PRINTING */
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+
+c_int osqp_update_P_A(OSQPWorkspace *work,
+                      const c_float *Px_new,
+                      const c_int   *Px_new_idx,
+                      c_int          P_new_n,
+                      const c_float *Ax_new,
+                      const c_int   *Ax_new_idx,
+                      c_int          A_new_n) {
+  c_int i;          // For indexing
+  c_int exitflag;   // Exit flag
+  c_int nnzP, nnzA; // Number of nonzeros in P and A
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+#ifdef PROFILING
+  if (work->clear_update_time == 1) {
+    work->clear_update_time = 0;
+    work->info->update_time = 0.0;
+  }
+  osqp_tic(work->timer); // Start timer
+#endif /* ifdef PROFILING */
+
+  nnzP = work->data->P->p[work->data->P->n];
+  nnzA = work->data->A->p[work->data->A->n];
+
+
+  if (Px_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in P
+    if (P_new_n > nnzP) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in P (%i)",
+               (int)P_new_n,
+               (int)nnzP);
+# endif /* ifdef PRINTING */
+      return 1;
+    }
+  }
+
+
+  if (Ax_new_idx) { // Passing the index of elements changed
+    // Check if number of elements is less or equal than the total number of
+    // nonzeros in A
+    if (A_new_n > nnzA) {
+# ifdef PRINTING
+      c_eprint("new number of elements (%i) greater than elements in A (%i)",
+               (int)A_new_n,
+               (int)nnzA);
+# endif /* ifdef PRINTING */
+      return 2;
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Unscale data
+    unscale_data(work);
+  }
+
+  // Update P elements
+  if (Px_new_idx) { // Change only Px_new_idx
+    for (i = 0; i < P_new_n; i++) {
+      work->data->P->x[Px_new_idx[i]] = Px_new[i];
+    }
+  }
+  else // Change whole P
+  {
+    for (i = 0; i < nnzP; i++) {
+      work->data->P->x[i] = Px_new[i];
+    }
+  }
+
+  // Update A elements
+  if (Ax_new_idx) { // Change only Ax_new_idx
+    for (i = 0; i < A_new_n; i++) {
+      work->data->A->x[Ax_new_idx[i]] = Ax_new[i];
+    }
+  }
+  else { // Change whole A
+    for (i = 0; i < nnzA; i++) {
+      work->data->A->x[i] = Ax_new[i];
+    }
+  }
+
+  if (work->settings->scaling) {
+    // Scale data
+    scale_data(work);
+  }
+
+  // Update linear system structure with new data
+  exitflag = work->linsys_solver->update_matrices(work->linsys_solver,
+                                                  work->data->P,
+                                                  work->data->A);
+
+  // Reset solver information
+  reset_info(work->info);
+
+# ifdef PRINTING
+
+  if (exitflag < 0) {
+    c_eprint("new KKT matrix is not quasidefinite");
+  }
+# endif /* ifdef PRINTING */
+
+#ifdef PROFILING
+  work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+c_int osqp_update_rho(OSQPWorkspace *work, c_float rho_new) {
+  c_int exitflag, i;
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check value of rho
+  if (rho_new <= 0) {
+# ifdef PRINTING
+    c_eprint("rho must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+#ifdef PROFILING
+  if (work->rho_update_from_solve == 0) {
+    if (work->clear_update_time == 1) {
+      work->clear_update_time = 0;
+      work->info->update_time = 0.0;
+    }
+    osqp_tic(work->timer); // Start timer
+  }
+#endif /* ifdef PROFILING */
+
+  // Update rho in settings
+  work->settings->rho = c_min(c_max(rho_new, RHO_MIN), RHO_MAX);
+
+  // Update rho_vec and rho_inv_vec
+  for (i = 0; i < work->data->m; i++) {
+    if (work->constr_type[i] == 0) {
+      // Inequalities
+      work->rho_vec[i]     = work->settings->rho;
+      work->rho_inv_vec[i] = 1. / work->settings->rho;
+    }
+    else if (work->constr_type[i] == 1) {
+      // Equalities
+      work->rho_vec[i]     = RHO_EQ_OVER_RHO_INEQ * work->settings->rho;
+      work->rho_inv_vec[i] = 1. / work->rho_vec[i];
+    }
+  }
+
+  // Update rho_vec in KKT matrix
+  exitflag = work->linsys_solver->update_rho_vec(work->linsys_solver,
+                                                 work->rho_vec);
+
+#ifdef PROFILING
+  if (work->rho_update_from_solve == 0)
+    work->info->update_time += osqp_toc(work->timer);
+#endif /* ifdef PROFILING */
+
+  return exitflag;
+}
+
+#endif // EMBEDDED != 1
+
+/****************************
+* Update problem settings  *
+****************************/
+c_int osqp_update_max_iter(OSQPWorkspace *work, c_int max_iter_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that max_iter is positive
+  if (max_iter_new <= 0) {
+#ifdef PRINTING
+    c_eprint("max_iter must be positive");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update max_iter
+  work->settings->max_iter = max_iter_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_abs(OSQPWorkspace *work, c_float eps_abs_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_abs is positive
+  if (eps_abs_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_abs must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_abs
+  work->settings->eps_abs = eps_abs_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_rel(OSQPWorkspace *work, c_float eps_rel_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_rel is positive
+  if (eps_rel_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_rel must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_rel
+  work->settings->eps_rel = eps_rel_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_prim_inf(OSQPWorkspace *work, c_float eps_prim_inf_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_prim_inf is positive
+  if (eps_prim_inf_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_prim_inf must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_prim_inf
+  work->settings->eps_prim_inf = eps_prim_inf_new;
+
+  return 0;
+}
+
+c_int osqp_update_eps_dual_inf(OSQPWorkspace *work, c_float eps_dual_inf_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that eps_dual_inf is positive
+  if (eps_dual_inf_new < 0.) {
+#ifdef PRINTING
+    c_eprint("eps_dual_inf must be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update eps_dual_inf
+  work->settings->eps_dual_inf = eps_dual_inf_new;
+
+
+  return 0;
+}
+
+c_int osqp_update_alpha(OSQPWorkspace *work, c_float alpha_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that alpha is between 0 and 2
+  if ((alpha_new <= 0.) || (alpha_new >= 2.)) {
+#ifdef PRINTING
+    c_eprint("alpha must be between 0 and 2");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update alpha
+  work->settings->alpha = alpha_new;
+
+  return 0;
+}
+
+c_int osqp_update_warm_start(OSQPWorkspace *work, c_int warm_start_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that warm_start is either 0 or 1
+  if ((warm_start_new != 0) && (warm_start_new != 1)) {
+#ifdef PRINTING
+    c_eprint("warm_start should be either 0 or 1");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update warm_start
+  work->settings->warm_start = warm_start_new;
+
+  return 0;
+}
+
+c_int osqp_update_scaled_termination(OSQPWorkspace *work, c_int scaled_termination_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that scaled_termination is either 0 or 1
+  if ((scaled_termination_new != 0) && (scaled_termination_new != 1)) {
+#ifdef PRINTING
+    c_eprint("scaled_termination should be either 0 or 1");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update scaled_termination
+  work->settings->scaled_termination = scaled_termination_new;
+
+  return 0;
+}
+
+c_int osqp_update_check_termination(OSQPWorkspace *work, c_int check_termination_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that check_termination is nonnegative
+  if (check_termination_new < 0) {
+#ifdef PRINTING
+    c_eprint("check_termination should be nonnegative");
+#endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update check_termination
+  work->settings->check_termination = check_termination_new;
+
+  return 0;
+}
+
+#ifndef EMBEDDED
+
+c_int osqp_update_delta(OSQPWorkspace *work, c_float delta_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that delta is positive
+  if (delta_new <= 0.) {
+# ifdef PRINTING
+    c_eprint("delta must be positive");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update delta
+  work->settings->delta = delta_new;
+
+  return 0;
+}
+
+c_int osqp_update_polish(OSQPWorkspace *work, c_int polish_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that polish is either 0 or 1
+  if ((polish_new != 0) && (polish_new != 1)) {
+# ifdef PRINTING
+    c_eprint("polish should be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update polish
+  work->settings->polish = polish_new;
+
+# ifdef PROFILING
+
+  // Reset polish time to zero
+  work->info->polish_time = 0.0;
+# endif /* ifdef PROFILING */
+
+  return 0;
+}
+
+c_int osqp_update_polish_refine_iter(OSQPWorkspace *work, c_int polish_refine_iter_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that polish_refine_iter is nonnegative
+  if (polish_refine_iter_new < 0) {
+# ifdef PRINTING
+    c_eprint("polish_refine_iter must be nonnegative");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update polish_refine_iter
+  work->settings->polish_refine_iter = polish_refine_iter_new;
+
+  return 0;
+}
+
+c_int osqp_update_verbose(OSQPWorkspace *work, c_int verbose_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that verbose is either 0 or 1
+  if ((verbose_new != 0) && (verbose_new != 1)) {
+# ifdef PRINTING
+    c_eprint("verbose should be either 0 or 1");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update verbose
+  work->settings->verbose = verbose_new;
+
+  return 0;
+}
+
+#endif // EMBEDDED
+
+#ifdef PROFILING
+
+c_int osqp_update_time_limit(OSQPWorkspace *work, c_float time_limit_new) {
+
+  // Check if workspace has been initialized
+  if (!work) return osqp_error(OSQP_WORKSPACE_NOT_INIT_ERROR);
+
+  // Check that time_limit is nonnegative
+  if (time_limit_new < 0.) {
+# ifdef PRINTING
+    c_print("time_limit must be nonnegative\n");
+# endif /* ifdef PRINTING */
+    return 1;
+  }
+
+  // Update time_limit
+  work->settings->time_limit = time_limit_new;
+
+  return 0;
+}
+#endif /* ifdef PROFILING */