Austin Schuh | a20e8c9 | 2022-02-20 17:44:06 -0800 | [diff] [blame^] | 1 | // Copyright 2020 Google LLC |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #ifndef OSQP_CPP_H_ |
| 16 | #define OSQP_CPP_H_ |
| 17 | |
| 18 | // A C++ wrapper for OSQP (https://osqp.org/). See README.md for an overview. |
| 19 | |
| 20 | #include <memory> |
| 21 | #include <string> |
| 22 | |
| 23 | #include "absl/base/attributes.h" |
| 24 | #include "absl/status/status.h" |
| 25 | #include "absl/status/statusor.h" |
| 26 | #include "Eigen/Core" |
| 27 | #include "Eigen/SparseCore" |
| 28 | |
| 29 | namespace osqp { |
| 30 | |
| 31 | // Must match the typedef in osqp/include/glob_opts.h (if not, it will trigger |
| 32 | // a static_assert failure in osqp++.cc). |
| 33 | using c_int = long long; // NOLINT |
| 34 | |
| 35 | // A memory-safe mirror of the OSQPData struct defined in osqp/include/types.h. |
| 36 | // The number of variables and constraints is implied by the shape of |
| 37 | // constraint_matrix. The format of the struct is further discussed in |
| 38 | // README.md. See also osqp++_test.cc for example usage. |
| 39 | struct OsqpInstance { |
| 40 | c_int num_variables() const { return constraint_matrix.cols(); } |
| 41 | c_int num_constraints() const { return constraint_matrix.rows(); } |
| 42 | |
| 43 | // Only the upper triangle of the objective matrix is read. The lower triangle |
| 44 | // is ignored. |
| 45 | Eigen::SparseMatrix<double, Eigen::ColMajor, c_int> objective_matrix; |
| 46 | Eigen::VectorXd objective_vector; |
| 47 | Eigen::SparseMatrix<double, Eigen::ColMajor, c_int> constraint_matrix; |
| 48 | Eigen::VectorXd lower_bounds; |
| 49 | Eigen::VectorXd upper_bounds; |
| 50 | }; |
| 51 | |
| 52 | // This is a mirror of the OSQPSettings struct defined in |
| 53 | // osqp/include/types.h and documented at |
| 54 | // http://osqp.readthedocs.io/en/latest/interfaces/solver_settings.html. The |
| 55 | // names are unchanged and (hence) violate Google naming conventions. The |
| 56 | // default values are defined in osqp/include/constants.h. Note, OSQP's default |
| 57 | // settings are looser than other QP solvers. Do choose appropriate values of |
| 58 | // eps_abs and eps_rel for your application. |
| 59 | struct OsqpSettings { |
| 60 | OsqpSettings(); // Sets default values. |
| 61 | |
| 62 | double rho; |
| 63 | double sigma; |
| 64 | c_int scaling; |
| 65 | bool adaptive_rho; |
| 66 | c_int adaptive_rho_interval; |
| 67 | double adaptive_rho_tolerance; |
| 68 | double adaptive_rho_fraction; |
| 69 | c_int max_iter; |
| 70 | double eps_abs; |
| 71 | double eps_rel; |
| 72 | double eps_prim_inf; |
| 73 | double eps_dual_inf; |
| 74 | double alpha; |
| 75 | // linsys_solver is omitted. We don't change this. |
| 76 | double delta; |
| 77 | bool polish; |
| 78 | c_int polish_refine_iter; |
| 79 | bool verbose; |
| 80 | bool scaled_termination; |
| 81 | c_int check_termination; |
| 82 | bool warm_start; |
| 83 | double time_limit; |
| 84 | }; |
| 85 | |
| 86 | // Type-safe wrapper for OSQP's status codes that are defined at |
| 87 | // osqp/include/constants.h. |
| 88 | enum class OsqpExitCode { |
| 89 | kOptimal, // Optimal solution found. |
| 90 | kPrimalInfeasible, // Certificate of primal infeasibility found. |
| 91 | kDualInfeasible, // Certificate of dual infeasibility found. |
| 92 | kOptimalInaccurate, // Optimal solution found subject to reduced tolerances |
| 93 | kPrimalInfeasibleInaccurate, // Certificate of primal infeasibility found |
| 94 | // subject to reduced tolerances. |
| 95 | kDualInfeasibleInaccurate, // Certificate of dual infeasibility found |
| 96 | // subject to reduced tolerances. |
| 97 | kMaxIterations, // Maximum number of iterations reached. |
| 98 | kInterrupted, // Interrupted by signal or CTRL-C. |
| 99 | kTimeLimitReached, // Ran out of time. |
| 100 | kNonConvex, // The problem was found to be non-convex. |
| 101 | kUnknown, // Unknown problem in solver. |
| 102 | }; |
| 103 | |
| 104 | std::string ToString(OsqpExitCode exitcode); |
| 105 | |
| 106 | // This is a workaround to avoid including OSQP's header file. We can't directly |
| 107 | // forward-declare OSQPWorkspace because it is defined as a typedef of an |
| 108 | // anonymous struct. |
| 109 | struct OSQPWorkspaceHelper; |
| 110 | |
| 111 | // This class is the main interface for calling OSQP. See example usage in |
| 112 | // README.md. |
| 113 | class OsqpSolver { |
| 114 | public: |
| 115 | OsqpSolver() = default; |
| 116 | // Move-only. |
| 117 | OsqpSolver(OsqpSolver&& rhs) = default; |
| 118 | OsqpSolver& operator=(OsqpSolver&& rhs) = default; |
| 119 | OsqpSolver(const OsqpSolver&) = delete; |
| 120 | OsqpSolver& operator=(const OsqpSolver&) = delete; |
| 121 | |
| 122 | // Creates the internal OSQP workspace given the instance data and settings. |
| 123 | // It is valid to call Init() multiple times. |
| 124 | absl::Status Init(const OsqpInstance& instance, const OsqpSettings& settings); |
| 125 | |
| 126 | // Updates the elements of matrix the objective matrix P (upper triangular). |
| 127 | // The new matrix should have the same sparsity structure. |
| 128 | // |
| 129 | // The solve will start from the previous optimal solution, which might not be |
| 130 | // a good starting point given the new objective matrix. If that's the |
| 131 | // case, one can call SetWarmStart with zero vectors to reset the state of the |
| 132 | // solver. |
| 133 | absl::Status UpdateObjectiveMatrix( |
| 134 | const Eigen::SparseMatrix<double, Eigen::ColMajor, c_int>& |
| 135 | objective_matrix); |
| 136 | |
| 137 | // Updates the elements of matrix the constraint matrix A. |
| 138 | // The new matrix should have the same sparsity structure. |
| 139 | absl::Status UpdateConstraintMatrix( |
| 140 | const Eigen::SparseMatrix<double, Eigen::ColMajor, c_int>& |
| 141 | constraint_matrix); |
| 142 | |
| 143 | // Combines call of UpdateObjectiveMatrix and UpdateConstraintMatrix. |
| 144 | absl::Status UpdateObjectiveAndConstraintMatrices( |
| 145 | const Eigen::SparseMatrix<double, Eigen::ColMajor, c_int>& |
| 146 | objective_matrix, |
| 147 | const Eigen::SparseMatrix<double, Eigen::ColMajor, c_int>& |
| 148 | constraint_matrix); |
| 149 | |
| 150 | // Returns true if Init() has been called successfully. |
| 151 | bool IsInitialized() const { return workspace_ != nullptr; } |
| 152 | |
| 153 | // Solves the instance by calling osqp_solve(). CHECK-fails if IsInitialized() |
| 154 | // is false. |
| 155 | ABSL_MUST_USE_RESULT OsqpExitCode Solve(); |
| 156 | |
| 157 | // The number of iterations taken. CHECK-fails if IsInitialized() is false. |
| 158 | c_int iterations() const; |
| 159 | |
| 160 | // The objective value of the primal solution. CHECK-fails if IsInitialized() |
| 161 | // is false. |
| 162 | double objective_value() const; |
| 163 | |
| 164 | // The primal solution, i.e., x. The Map is valid only for the lifetime of |
| 165 | // the OSQP workspace. It will be invalidated by a call to Init() or if the |
| 166 | // OsqpSolver is deleted. CHECK-fails if IsInitialized() is false. |
| 167 | // Implementation details (do not depend on these): The underlying memory is |
| 168 | // overwritten by SetPrimalWarmStart(). Modification of the problem data does |
| 169 | // not destroy the solution. |
| 170 | Eigen::Map<const Eigen::VectorXd> primal_solution() const; |
| 171 | |
| 172 | // The vector of lagrange multipliers on the linear constraints. The Map is |
| 173 | // valid only for the lifetime of the OSQP workspace. It will be invalidated |
| 174 | // by a call to Init() or if the OsqpSolver is deleted. CHECK-fails if |
| 175 | // IsInitialized() is false. Implementation details (do not depend on these): |
| 176 | // The underlying memory is overwritten by SetDualWarmStart(). Modification of |
| 177 | // the problem data does not destroy the solution. |
| 178 | Eigen::Map<const Eigen::VectorXd> dual_solution() const; |
| 179 | |
| 180 | // The primal infeasibility certificate. It is valid to query this only if |
| 181 | // Solve() returns kPrimalInfeasible or kPrimalInfeasibleInaccurate. The |
| 182 | // Map is valid only for the lifetime of the OSQP workspace. It will be |
| 183 | // invalidated by a call to Init() or of the OsqpSolver is deleted. |
| 184 | Eigen::Map<const Eigen::VectorXd> primal_infeasibility_certificate() const; |
| 185 | |
| 186 | // TODO(ml): Implement dual_infeasibility_certificate. |
| 187 | |
| 188 | // Sets a primal and dual warm-start for the next solve. Equivalent to |
| 189 | // SetPrimalWarmStart(primal_vector) and SetDualWarmStart(dual_vector). |
| 190 | // Returns: |
| 191 | // - FailedPreconditionError if IsInitialized() is false |
| 192 | // - InvalidArgumentError if the vectors do not have expected dimensions |
| 193 | // - UnknownError if the internal OSQP call fails |
| 194 | // - OkStatus on success |
| 195 | absl::Status SetWarmStart( |
| 196 | const Eigen::Ref<const Eigen::VectorXd>& primal_vector, |
| 197 | const Eigen::Ref<const Eigen::VectorXd>& dual_vector); |
| 198 | |
| 199 | // Sets a warm-start for the primal iterate for the next solve. Use a vector |
| 200 | // of zeros to reset to the default initialization. |
| 201 | // - FailedPreconditionError if IsInitialized() is false |
| 202 | // - InvalidArgumentError if the vector does not have expected dimensions |
| 203 | // - UnknownError if the internal OSQP call fails |
| 204 | // - OkStatus on success |
| 205 | absl::Status SetPrimalWarmStart( |
| 206 | const Eigen::Ref<const Eigen::VectorXd>& primal_vector); |
| 207 | |
| 208 | // Sets a warm-start for the dual iterate for the next solve. Use a vector |
| 209 | // of zeros to reset to the default initialization. |
| 210 | // - FailedPreconditionError if IsInitialized() is false |
| 211 | // - InvalidArgumentError if the vector does not have expected dimensions |
| 212 | // - UnknownError if the internal OSQP call fails |
| 213 | // - OkStatus on success |
| 214 | absl::Status SetDualWarmStart( |
| 215 | const Eigen::Ref<const Eigen::VectorXd>& dual_vector); |
| 216 | |
| 217 | // Sets the objective vector for the next solve. Returns: |
| 218 | // - FailedPreconditionError if IsInitialized() is false |
| 219 | // - InvalidArgumentError if the vectors do not have expected dimensions |
| 220 | // - UnknownError if the internal OSQP call fails |
| 221 | // - OkStatus on success |
| 222 | absl::Status SetObjectiveVector( |
| 223 | const Eigen::Ref<const Eigen::VectorXd>& objective_vector); |
| 224 | |
| 225 | // Sets the lower_bounds and upper_bounds vectors for the next solve. Returns: |
| 226 | // - FailedPreconditionError if IsInitialized() is false |
| 227 | // - InvalidArgumentError if the vectors do not have expected dimensions |
| 228 | // - InvalidArgumentError if lower_bounds[i] > upper_bounds[i] for some i |
| 229 | // - UnknownError if the internal OSQP call fails |
| 230 | // - OkStatus on success |
| 231 | absl::Status SetBounds(const Eigen::Ref<const Eigen::VectorXd>& lower_bounds, |
| 232 | const Eigen::Ref<const Eigen::VectorXd>& upper_bounds); |
| 233 | |
| 234 | // Gets the current value of the rho setting, i.e., the ADMM rho step. Returns |
| 235 | // a FailedPreconditionError if IsInitialized() is false. |
| 236 | absl::StatusOr<double> GetRho() const; |
| 237 | |
| 238 | // Gets the current value of the sigma setting, i.e., the ADMM sigma step. |
| 239 | // Returns a FailedPreconditionError if IsInitialized() is false. |
| 240 | absl::StatusOr<double> GetSigma() const; |
| 241 | |
| 242 | // Gets the current value of the scaling setting, i.e., the number of |
| 243 | // heuristic scaling iterations. Returns a FailedPreconditionError if |
| 244 | // IsInitialized() is false. |
| 245 | absl::StatusOr<c_int> GetScaling() const; |
| 246 | |
| 247 | // Gets the current value of the adaptive_rho setting, i.e., whether the rho |
| 248 | // step size is adaptively set. Returns a FailedPreconditionError if |
| 249 | // IsInitialized() is false. |
| 250 | absl::StatusOr<bool> GetAdaptiveRho() const; |
| 251 | |
| 252 | // Gets the current value of the adaptive_rho_interval setting, i.e., the |
| 253 | // number of iterations between rho adaptations. Returns a |
| 254 | // FailedPreconditionError if IsInitialized() is false. |
| 255 | absl::StatusOr<c_int> GetAdaptiveRhoInterval() const; |
| 256 | |
| 257 | // Gets the current value of the adaptive_rho_tolerance setting, i.e., the |
| 258 | // tolerance X for adapting rho (the new value must be X times larger, or 1/X |
| 259 | // times smaller, than the current value). Returns a FailedPreconditionError |
| 260 | // if IsInitialized() is false. |
| 261 | absl::StatusOr<double> GetAdaptiveRhoTolerance() const; |
| 262 | |
| 263 | // Gets the current value of the adaptive_rho_fraction setting, i.e., in |
| 264 | // automatic mode (adaptive_rho_interval = 0), what fraction of setup time is |
| 265 | // spent on selecting rho. Returns a FailedPreconditionError if |
| 266 | // IsInitialized() is false. |
| 267 | absl::StatusOr<double> GetAdaptiveRhoFraction() const; |
| 268 | |
| 269 | // Gets the current value of the max_iter setting, i.e., the maximum number of |
| 270 | // iterations. Returns a FailedPreconditionError if IsInitialized() is false. |
| 271 | absl::StatusOr<c_int> GetMaxIter() const; |
| 272 | |
| 273 | // Gets the current value of the eps_abs setting, i.e., the absolute error |
| 274 | // tolerance for convergence. Returns a FailedPreconditionError if |
| 275 | // IsInitialized() is false. |
| 276 | absl::StatusOr<double> GetEpsAbs() const; |
| 277 | |
| 278 | // Gets the current value of the eps_rel setting, i.e., the relative error |
| 279 | // tolerance for convergence. Returns a FailedPreconditionError if |
| 280 | // IsInitialized() is false. |
| 281 | absl::StatusOr<double> GetEpsRel() const; |
| 282 | |
| 283 | // Gets the current value of the eps_prim_inf setting, i.e., the absolute |
| 284 | // error tolerance for primal infeasibility. Returns a FailedPreconditionError |
| 285 | // if IsInitialized() is false. |
| 286 | absl::StatusOr<double> GetEpsPrimInf() const; |
| 287 | |
| 288 | // Gets the current value of the eps_dual_inf setting, i.e., the absolute |
| 289 | // error tolerance for dual infeasibility. Returns a FailedPreconditionError |
| 290 | // if IsInitialized() is false. |
| 291 | absl::StatusOr<double> GetEpsDualInf() const; |
| 292 | |
| 293 | // Gets the current value of the alpha setting, i.e., the ADMM overrelaxation |
| 294 | // parameter. Returns a FailedPreconditionError if IsInitialized() is false. |
| 295 | absl::StatusOr<double> GetAlpha() const; |
| 296 | |
| 297 | // Gets the current value of the delta setting, i.e., the polishing |
| 298 | // regularization parameter. Returns a FailedPreconditionError if |
| 299 | // IsInitialized() is false. |
| 300 | absl::StatusOr<double> GetDelta() const; |
| 301 | |
| 302 | // Gets the current value of the polish setting, i.e., whether polishing is |
| 303 | // performed. Returns a FailedPreconditionError if IsInitialized() is false. |
| 304 | absl::StatusOr<bool> GetPolish() const; |
| 305 | |
| 306 | // Gets the current value of the polish_refine_iter setting, i.e., the number |
| 307 | // of refinement iterations in polishing. Returns a FailedPreconditionError if |
| 308 | // IsInitialized() is false. |
| 309 | absl::StatusOr<c_int> GetPolishRefineIter() const; |
| 310 | |
| 311 | // Gets the current value of the verbose setting, i.e., whether solver output |
| 312 | // is printed. Returns a FailedPreconditionError if IsInitialized() is false. |
| 313 | absl::StatusOr<bool> GetVerbose() const; |
| 314 | |
| 315 | // Gets the current value of the scaled_termination setting, i.e., whether |
| 316 | // scaled termination criteria is used. Returns a FailedPreconditionError if |
| 317 | // IsInitialized() is false. |
| 318 | absl::StatusOr<bool> GetScaledTermination() const; |
| 319 | |
| 320 | // Gets the current value of the check_termination setting, i.e., the interval |
| 321 | // for checking termination. Returns a FailedPreconditionError if |
| 322 | // IsInitialized() is false. |
| 323 | absl::StatusOr<c_int> GetCheckTermination() const; |
| 324 | |
| 325 | // Gets the current value of the warm_start setting, i.e., if warm starting is |
| 326 | // performed. Returns a FailedPreconditionError if IsInitialized() is false. |
| 327 | absl::StatusOr<bool> GetWarmStart() const; |
| 328 | |
| 329 | // Gets the current value of the time_limit setting, i.e., the time limit as |
| 330 | // expressed in seconds. Returns a FailedPreconditionError if IsInitialized() |
| 331 | // is false. |
| 332 | absl::StatusOr<double> GetTimeLimit() const; |
| 333 | |
| 334 | // Updates the rho setting, i.e., the ADMM rho step. Returns: |
| 335 | // - FailedPreconditionError if IsInitialized() is false |
| 336 | // - InvalidArgumentError if rho_new <= 0.0 |
| 337 | // - OkStatus on success |
| 338 | absl::Status UpdateRho(double rho_new); |
| 339 | |
| 340 | // Updates the max_iter setting, i.e., the maximum number of iterations. |
| 341 | // Returns: |
| 342 | // - FailedPreconditionError if IsInitialized() is false |
| 343 | // - InvalidArgumentError if max_iter_new <= 0 |
| 344 | // - OkStatus on success |
| 345 | absl::Status UpdateMaxIter(int max_iter_new); |
| 346 | |
| 347 | // Updates the eps_abs setting, i.e., the absolute error tolerance for |
| 348 | // convergence. Returns: |
| 349 | // - FailedPreconditionError if IsInitialized() is false |
| 350 | // - InvalidArgumentError if eps_abs_new < 0.0 |
| 351 | // - OkStatus on success |
| 352 | absl::Status UpdateEpsAbs(double eps_abs_new); |
| 353 | |
| 354 | // Updates the eps_rel setting, i.e., the relative error tolerance for |
| 355 | // convergence. Returns: |
| 356 | // - FailedPreconditionError if IsInitialized() is false |
| 357 | // - InvalidArgumentError if eps_rel_new < 0.0 |
| 358 | // - OkStatus on success |
| 359 | absl::Status UpdateEpsRel(double eps_rel_new); |
| 360 | |
| 361 | // Updates the eps_prim_inf setting, i.e., the absolute error tolerance for |
| 362 | // primal infeasibility. Returns: |
| 363 | // - FailedPreconditionError if IsInitialized() is false |
| 364 | // - InvalidArgumentError if eps_prim_inf_new < 0.0 |
| 365 | // - OkStatus on success |
| 366 | absl::Status UpdateEpsPrimInf(double eps_prim_inf_new); |
| 367 | |
| 368 | // Updates the eps_dual_inf setting, i.e., the absolute error tolerance for |
| 369 | // dual infeasibility. Returns: |
| 370 | // - FailedPreconditionError if IsInitialized() is false |
| 371 | // - InvalidArgumentError if eps_dual_inf_new < 0.0 |
| 372 | // - OkStatus on success |
| 373 | absl::Status UpdateEpsDualInf(double eps_dual_inf_new); |
| 374 | |
| 375 | // Updates the alpha setting, i.e., the ADMM overrelaxation parameter. |
| 376 | // Returns: |
| 377 | // - FailedPreconditionError if IsInitialized() is false |
| 378 | // - InvalidArgumentError if !(0 < alpha_new < 2) |
| 379 | // - OkStatus on success |
| 380 | absl::Status UpdateAlpha(double alpha_new); |
| 381 | |
| 382 | // Updates the delta setting, i.e., the polishing regularization parameter. |
| 383 | // Returns: |
| 384 | // - FailedPreconditionError if IsInitialized() is false |
| 385 | // - InvalidArgumentError if delta_new <= 0.0 |
| 386 | // - OkStatus on success |
| 387 | absl::Status UpdateDelta(double delta_new); |
| 388 | |
| 389 | // Updates the polish setting, i.e., whether polishing is performed. Returns: |
| 390 | // - FailedPreconditionError if IsInitialized() is false |
| 391 | // - OkStatus on success |
| 392 | absl::Status UpdatePolish(bool polish_new); |
| 393 | |
| 394 | // Updates the polish_refine_iter setting, i.e., the number of refinement |
| 395 | // iterations in polishing. Returns: |
| 396 | // - FailedPreconditionError if IsInitialized() is false |
| 397 | // - InvalidArgumentError if polish_refine_iter_new <= 0.0 |
| 398 | // - OkStatus on success |
| 399 | absl::Status UpdatePolishRefineIter(int polish_refine_iter_new); |
| 400 | |
| 401 | // Updates the verbose setting, i.e., whether solver output is printed. |
| 402 | // Returns: |
| 403 | // - FailedPreconditionError if IsInitialized() is false |
| 404 | // - OkStatus on success |
| 405 | absl::Status UpdateVerbose(bool verbose_new); |
| 406 | |
| 407 | // Updates the scaled_termination setting, i.e., whether scaled termination |
| 408 | // criteria is used. Returns: |
| 409 | // - FailedPreconditionError if IsInitialized() is false |
| 410 | // - OkStatus on success |
| 411 | absl::Status UpdateScaledTermination(bool scaled_termination_new); |
| 412 | |
| 413 | // Updates the check_termination setting, i.e., the interval for checking |
| 414 | // termination. Setting to zero disables termination checking. Returns: |
| 415 | // - FailedPreconditionError if IsInitialized() is false |
| 416 | // - InvalidArgumentError if check_termination_new < 0.0 |
| 417 | // - OkStatus on success |
| 418 | absl::Status UpdateCheckTermination(c_int check_termination_new); |
| 419 | |
| 420 | // Updates the warm_start setting, i.e., whether warm starting is performed. |
| 421 | // Returns: |
| 422 | // - FailedPreconditionError if IsInitialized() is false |
| 423 | // - OkStatus on success |
| 424 | absl::Status UpdateWarmStart(bool warm_start_new); |
| 425 | |
| 426 | // Updates the time_limit setting, i.e., the time limit as expressed in |
| 427 | // seconds. Setting the time limit to zero disables time-limiting. Returns: |
| 428 | // - FailedPreconditionError if IsInitialized() is false |
| 429 | // - InvalidArgumentError if time_limit_new < 0.0 |
| 430 | // - OkStatus on success |
| 431 | absl::Status UpdateTimeLimit(double time_limit_new); |
| 432 | |
| 433 | private: |
| 434 | struct OsqpDeleter { |
| 435 | void operator()(OSQPWorkspaceHelper* workspace) const; |
| 436 | }; |
| 437 | |
| 438 | std::unique_ptr<OSQPWorkspaceHelper, OsqpDeleter> workspace_; |
| 439 | }; |
| 440 | |
| 441 | } // namespace osqp |
| 442 | |
| 443 | #endif // OSQP_CPP_H_ |