Sleipnir C++ API
Loading...
Searching...
No Matches
problem.hpp
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6#include <array>
7#include <cmath>
8#include <concepts>
9#include <functional>
10#include <iterator>
11#include <memory>
12#include <optional>
13#include <ranges>
14#include <utility>
15
16#include <Eigen/Core>
17#include <Eigen/SparseCore>
18#include <gch/small_vector.hpp>
19
20#include "sleipnir/autodiff/expression_type.hpp"
21#include "sleipnir/autodiff/gradient.hpp"
22#include "sleipnir/autodiff/hessian.hpp"
23#include "sleipnir/autodiff/jacobian.hpp"
24#include "sleipnir/autodiff/variable.hpp"
25#include "sleipnir/autodiff/variable_matrix.hpp"
26#include "sleipnir/optimization/solver/exit_status.hpp"
27#include "sleipnir/optimization/solver/interior_point.hpp"
28#include "sleipnir/optimization/solver/iteration_info.hpp"
29#include "sleipnir/optimization/solver/newton.hpp"
30#include "sleipnir/optimization/solver/options.hpp"
31#include "sleipnir/optimization/solver/sqp.hpp"
32#include "sleipnir/optimization/solver/util/bounds.hpp"
33#include "sleipnir/optimization/solver/util/problem_scaling.hpp"
34#include "sleipnir/util/empty.hpp"
35#include "sleipnir/util/print.hpp"
36#include "sleipnir/util/print_diagnostics.hpp"
37#include "sleipnir/util/profiler.hpp"
38#include "sleipnir/util/spy.hpp"
39#include "sleipnir/util/symbol_exports.hpp"
40
41namespace slp {
42
66template <typename Scalar>
67class Problem {
68 public:
71
77 [[nodiscard]]
79 m_decision_variables.emplace_back();
80 return m_decision_variables.back();
81 }
82
90 [[nodiscard]]
91 VariableMatrix<Scalar> decision_variable(int rows, int cols = 1) {
92 m_decision_variables.reserve(m_decision_variables.size() + rows * cols);
93
94 VariableMatrix<Scalar> vars{detail::empty, rows, cols};
95
96 for (int row = 0; row < rows; ++row) {
97 for (int col = 0; col < cols; ++col) {
98 m_decision_variables.emplace_back();
99 vars[row, col] = m_decision_variables.back();
100 }
101 }
102
103 return vars;
104 }
105
117 [[nodiscard]]
119 // We only need to store the lower triangle of an n x n symmetric matrix;
120 // the other elements are duplicates. The lower triangle has (n² + n)/2
121 // elements.
122 //
123 // n
124 // Σ k = (n² + n)/2
125 // k=1
126 m_decision_variables.reserve(m_decision_variables.size() +
127 (rows * rows + rows) / 2);
128
129 VariableMatrix<Scalar> vars{detail::empty, rows, rows};
130
131 for (int row = 0; row < rows; ++row) {
132 for (int col = 0; col <= row; ++col) {
133 m_decision_variables.emplace_back();
134 vars[row, col] = m_decision_variables.back();
135 vars[col, row] = m_decision_variables.back();
136 }
137 }
138
139 return vars;
140 }
141
151 void minimize(const Variable<Scalar>& cost) { m_f = cost; }
152
162 void minimize(Variable<Scalar>&& cost) { m_f = std::move(cost); }
163
174 // Maximizing a cost function is the same as minimizing its negative
175 m_f = -objective;
176 }
177
188 // Maximizing a cost function is the same as minimizing its negative
189 m_f = -std::move(objective);
190 }
191
197 m_equality_constraints.reserve(m_equality_constraints.size() +
198 constraint.constraints.size());
199 std::ranges::copy(constraint.constraints,
200 std::back_inserter(m_equality_constraints));
201 }
202
208 m_equality_constraints.reserve(m_equality_constraints.size() +
209 constraint.constraints.size());
210 std::ranges::copy(constraint.constraints,
211 std::back_inserter(m_equality_constraints));
212 }
213
219 m_inequality_constraints.reserve(m_inequality_constraints.size() +
220 constraint.constraints.size());
221 std::ranges::copy(constraint.constraints,
222 std::back_inserter(m_inequality_constraints));
223 }
224
230 m_inequality_constraints.reserve(m_inequality_constraints.size() +
231 constraint.constraints.size());
232 std::ranges::copy(constraint.constraints,
233 std::back_inserter(m_inequality_constraints));
234 }
235
239 ExpressionType cost_function_type() const {
240 if (m_f) {
241 return m_f.value().type();
242 } else {
243 return ExpressionType::NONE;
244 }
245 }
246
250 ExpressionType equality_constraint_type() const {
251 if (!m_equality_constraints.empty()) {
252 return std::ranges::max(m_equality_constraints, {},
254 .type();
255 } else {
256 return ExpressionType::NONE;
257 }
258 }
259
263 ExpressionType inequality_constraint_type() const {
264 if (!m_inequality_constraints.empty()) {
265 return std::ranges::max(m_inequality_constraints, {},
267 .type();
268 } else {
269 return ExpressionType::NONE;
270 }
271 }
272
281 ExitStatus solve(const Options& options = Options{},
282 [[maybe_unused]] bool spy = false) {
283 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
284 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
285 using SparseVector = Eigen::SparseVector<Scalar>;
286
287 // Create the initial value column vector
288 DenseVector x{m_decision_variables.size()};
289 for (size_t i = 0; i < m_decision_variables.size(); ++i) {
290 x[i] = m_decision_variables[i].value();
291 }
292
293 if (options.diagnostics) {
294 print_exit_conditions(options);
295 print_problem_analysis();
296 }
297
298 // Get the highest order constraint expression types
299 auto f_type = cost_function_type();
300 auto c_e_type = equality_constraint_type();
301 auto c_i_type = inequality_constraint_type();
302
303 // If the problem is empty or constant, there's nothing to do
304 if (f_type <= ExpressionType::CONSTANT &&
305 c_e_type <= ExpressionType::CONSTANT &&
306 c_i_type <= ExpressionType::CONSTANT) {
307#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
308 if (options.diagnostics) {
309 slp::println("\nInvoking no-op solver\n");
310 }
311#endif
312 return ExitStatus::SUCCESS;
313 }
314
315 VariableMatrix<Scalar> x_ad{m_decision_variables};
316
317 // Set up cost function
318 Variable f = m_f.value_or(Scalar(0));
319
320 int num_decision_variables = m_decision_variables.size();
321 int num_equality_constraints = m_equality_constraints.size();
322 int num_inequality_constraints = m_inequality_constraints.size();
323
324 gch::small_vector<std::function<bool(const IterationInfo<Scalar>& info)>>
325 iteration_callbacks;
326 for (const auto& callback : m_iteration_callbacks) {
327 iteration_callbacks.emplace_back(callback);
328 }
329 for (const auto& callback : m_persistent_iteration_callbacks) {
330 iteration_callbacks.emplace_back(callback);
331 }
332
333 // Solve the optimization problem
334 ExitStatus status;
335 if (m_equality_constraints.empty() && m_inequality_constraints.empty()) {
336 if (options.diagnostics) {
337 slp::println("\nInvoking Newton solver\n");
338 }
339
340 gch::small_vector<SetupProfiler> ad_setup_profilers;
341 ad_setup_profilers.emplace_back("setup");
342 ad_setup_profilers.emplace_back("↳ ∇f(x)");
343 ad_setup_profilers.emplace_back("↳ ∇²ₓₓL");
344
345 ad_setup_profilers[0].start();
346
347 // Set up gradient autodiff
348 ad_setup_profilers[1].start();
349 Gradient g{f, x_ad};
350 ad_setup_profilers[1].stop();
351
352 // Set up Lagrangian Hessian autodiff
353 ad_setup_profilers[2].start();
354 Hessian<Scalar, Eigen::Lower> H{f, x_ad};
355 ad_setup_profilers[2].stop();
356
357 ad_setup_profilers[0].stop();
358
359 if (options.diagnostics) {
360 print_setup_diagnostics(ad_setup_profilers);
361 }
362
363#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
364 // Sparsity pattern files written when spy flag is set
365 std::unique_ptr<Spy<Scalar>> H_spy;
366
367 if (spy) {
368 H_spy = std::make_unique<Spy<Scalar>>(
369 "H.spy", "Hessian", "Decision variables", "Decision variables",
370 num_decision_variables, num_decision_variables);
371 iteration_callbacks.push_back(
372 [&](const IterationInfo<Scalar>& info) -> bool {
373 H_spy->add(info.H);
374 return false;
375 });
376 }
377#endif
378
379 // Automatically scale the cost. The problem scaling procedure is
380 // described in more detail in docs/algorithms.md#problem-scaling.
381 x_ad.set_value(x);
382 const ProblemScaling<Scalar> scaling{g.value()};
383
384 NewtonMatrixCallbacks<Scalar> matrix_callbacks{
385 num_decision_variables,
386 [&](const DenseVector& x) -> Scalar {
387 x_ad.set_value(x);
388 return scaling.f * f.value();
389 },
390 [&](const DenseVector& x) -> SparseVector {
391 x_ad.set_value(x);
392 return scaling.f * g.value();
393 },
394 [&](const DenseVector& x) -> SparseMatrix {
395 x_ad.set_value(x);
396 return scaling.f * H.value();
397 },
398 scaling};
399
400 // Invoke Newton solver
401 status =
402 newton<Scalar>(matrix_callbacks, iteration_callbacks, options, x);
403 } else if (m_inequality_constraints.empty()) {
404 if (options.diagnostics) {
405 slp::println("\nInvoking SQP solver\n");
406 }
407
408 VariableMatrix<Scalar> c_e_ad{m_equality_constraints};
409 VariableMatrix<Scalar> y_ad(num_equality_constraints);
410
411 gch::small_vector<SetupProfiler> ad_setup_profilers;
412 ad_setup_profilers.emplace_back("setup");
413 ad_setup_profilers.emplace_back("↳ ∇f(x)");
414 ad_setup_profilers.emplace_back("↳ ∇²ₓₓL");
415 ad_setup_profilers.emplace_back(" ↳ ∇²ₓₓL_f");
416 ad_setup_profilers.emplace_back(" ↳ ∇²ₓₓL_c");
417 ad_setup_profilers.emplace_back("↳ ∂cₑ/∂x");
418
419 ad_setup_profilers[0].start();
420
421 // Set up gradient autodiff
422 ad_setup_profilers[1].start();
423 Gradient g{f, x_ad};
424 ad_setup_profilers[1].stop();
425
426 ad_setup_profilers[2].start();
427
428 // Set up cost part of Lagrangian Hessian autodiff
429 ad_setup_profilers[3].start();
430 Hessian<Scalar, Eigen::Lower> H_f{f, x_ad};
431 ad_setup_profilers[3].stop();
432
433 // Set up constraint part of Lagrangian Hessian autodiff
434 ad_setup_profilers[4].start();
435 Hessian<Scalar, Eigen::Lower> H_c{-y_ad.T() * c_e_ad, x_ad};
436 ad_setup_profilers[4].stop();
437
438 ad_setup_profilers[2].stop();
439
440 // Set up equality constraint Jacobian autodiff
441 ad_setup_profilers[5].start();
442 Jacobian A_e{c_e_ad, x_ad};
443 ad_setup_profilers[5].stop();
444
445 ad_setup_profilers[0].stop();
446
447 if (options.diagnostics) {
448 print_setup_diagnostics(ad_setup_profilers);
449 }
450
451#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
452 // Sparsity pattern files written when spy flag is set
453 std::unique_ptr<Spy<Scalar>> H_spy;
454 std::unique_ptr<Spy<Scalar>> A_e_spy;
455
456 if (spy) {
457 H_spy = std::make_unique<Spy<Scalar>>(
458 "H.spy", "Hessian", "Decision variables", "Decision variables",
459 num_decision_variables, num_decision_variables);
460 A_e_spy = std::make_unique<Spy<Scalar>>(
461 "A_e.spy", "Equality constraint Jacobian", "Constraints",
462 "Decision variables", num_equality_constraints,
463 num_decision_variables);
464 iteration_callbacks.push_back(
465 [&](const IterationInfo<Scalar>& info) -> bool {
466 H_spy->add(info.H);
467 A_e_spy->add(info.A_e);
468 return false;
469 });
470 }
471#endif
472
473 // Automatically scale the cost and constraints. The problem scaling
474 // procedure is described in more detail in
475 // docs/algorithms.md#problem-scaling.
476 x_ad.set_value(x);
477 const ProblemScaling<Scalar> scaling{g.value(), A_e.value()};
478
479 SQPMatrixCallbacks<Scalar> matrix_callbacks{
480 num_decision_variables,
481 num_equality_constraints,
482 [&](const DenseVector& x) -> Scalar {
483 x_ad.set_value(x);
484 return scaling.f * f.value();
485 },
486 [&](const DenseVector& x) -> SparseVector {
487 x_ad.set_value(x);
488 return scaling.f * g.value();
489 },
490 [&](const DenseVector& x, const DenseVector& y) -> SparseMatrix {
491 x_ad.set_value(x);
492 y_ad.set_value(scaling.c_e.cwiseProduct(y));
493 return scaling.f * H_f.value() + H_c.value();
494 },
495 [&](const DenseVector& x, const DenseVector& y) -> SparseMatrix {
496 x_ad.set_value(x);
497 y_ad.set_value(scaling.c_e.cwiseProduct(y));
498 return H_c.value();
499 },
500 [&](const DenseVector& x) -> DenseVector {
501 x_ad.set_value(x);
502 return scaling.c_e.cwiseProduct(c_e_ad.value());
503 },
504 [&](const DenseVector& x) -> SparseMatrix {
505 x_ad.set_value(x);
506 return scaling.c_e.asDiagonal() * A_e.value();
507 },
508 scaling};
509
510 // Invoke SQP solver
511 status = sqp<Scalar>(matrix_callbacks, iteration_callbacks, options, x);
512 } else {
513 if (options.diagnostics) {
514 slp::println("\nInvoking IPM solver\n");
515 }
516
517 VariableMatrix<Scalar> c_e_ad{m_equality_constraints};
518 VariableMatrix<Scalar> c_i_ad{m_inequality_constraints};
519 VariableMatrix<Scalar> y_ad(num_equality_constraints);
520 VariableMatrix<Scalar> z_ad(num_inequality_constraints);
521
522 gch::small_vector<SetupProfiler> ad_setup_profilers;
523 ad_setup_profilers.emplace_back("setup");
524 ad_setup_profilers.emplace_back("↳ ∇f(x)");
525 ad_setup_profilers.emplace_back("↳ ∇²ₓₓL");
526 ad_setup_profilers.emplace_back(" ↳ ∇²ₓₓL_f");
527 ad_setup_profilers.emplace_back(" ↳ ∇²ₓₓL_c");
528 ad_setup_profilers.emplace_back("↳ ∂cₑ/∂x");
529 ad_setup_profilers.emplace_back("↳ ∂cᵢ/∂x");
530
531 ad_setup_profilers[0].start();
532
533 // Set up gradient autodiff
534 ad_setup_profilers[1].start();
535 Gradient g{f, x_ad};
536 ad_setup_profilers[1].stop();
537
538 ad_setup_profilers[2].start();
539
540 // Set up cost part of Lagrangian Hessian autodiff
541 ad_setup_profilers[3].start();
542 Hessian<Scalar, Eigen::Lower> H_f{f, x_ad};
543 ad_setup_profilers[3].stop();
544
545 // Set up constraint part of Lagrangian Hessian autodiff
546 ad_setup_profilers[4].start();
547 Hessian<Scalar, Eigen::Lower> H_c{-y_ad.T() * c_e_ad - z_ad.T() * c_i_ad,
548 x_ad};
549 ad_setup_profilers[4].stop();
550
551 ad_setup_profilers[2].stop();
552
553 // Set up equality constraint Jacobian autodiff
554 ad_setup_profilers[5].start();
555 Jacobian A_e{c_e_ad, x_ad};
556 ad_setup_profilers[5].stop();
557
558 // Set up inequality constraint Jacobian autodiff
559 ad_setup_profilers[6].start();
560 Jacobian A_i{c_i_ad, x_ad};
561 ad_setup_profilers[6].stop();
562
563 ad_setup_profilers[0].stop();
564
565 if (options.diagnostics) {
566 print_setup_diagnostics(ad_setup_profilers);
567 }
568
569#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
570 // Sparsity pattern files written when spy flag is set
571 std::unique_ptr<Spy<Scalar>> H_spy;
572 std::unique_ptr<Spy<Scalar>> A_e_spy;
573 std::unique_ptr<Spy<Scalar>> A_i_spy;
574
575 if (spy) {
576 H_spy = std::make_unique<Spy<Scalar>>(
577 "H.spy", "Hessian", "Decision variables", "Decision variables",
578 num_decision_variables, num_decision_variables);
579 A_e_spy = std::make_unique<Spy<Scalar>>(
580 "A_e.spy", "Equality constraint Jacobian", "Constraints",
581 "Decision variables", num_equality_constraints,
582 num_decision_variables);
583 A_i_spy = std::make_unique<Spy<Scalar>>(
584 "A_i.spy", "Inequality constraint Jacobian", "Constraints",
585 "Decision variables", num_inequality_constraints,
586 num_decision_variables);
587 iteration_callbacks.push_back(
588 [&](const IterationInfo<Scalar>& info) -> bool {
589 H_spy->add(info.H);
590 A_e_spy->add(info.A_e);
591 A_i_spy->add(info.A_i);
592 return false;
593 });
594 }
595#endif
596
597 const auto [bound_constraint_mask, bounds, conflicting_bound_indices] =
598 get_bounds<Scalar>(m_decision_variables, m_inequality_constraints,
599 A_i.value());
600 if (!conflicting_bound_indices.empty()) {
601 if (options.diagnostics) {
602 print_bound_constraint_global_infeasibility_error(
603 conflicting_bound_indices);
604 }
605 return ExitStatus::GLOBALLY_INFEASIBLE;
606 }
607
608#ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
609 project_onto_bounds(x, bounds);
610#endif
611
612 // Automatically scale the cost and constraints. The problem scaling
613 // procedure is described in more detail in
614 // docs/algorithms.md#problem-scaling.
615 x_ad.set_value(x);
616 const ProblemScaling<Scalar> scaling{g.value(), A_e.value(), A_i.value()};
617
618 InteriorPointMatrixCallbacks<Scalar> matrix_callbacks{
619 num_decision_variables,
620 num_equality_constraints,
621 num_inequality_constraints,
622 [&](const DenseVector& x) -> Scalar {
623 x_ad.set_value(x);
624 return scaling.f * f.value();
625 },
626 [&](const DenseVector& x) -> SparseVector {
627 x_ad.set_value(x);
628 return scaling.f * g.value();
629 },
630 [&](const DenseVector& x, const DenseVector& y,
631 const DenseVector& z) -> SparseMatrix {
632 x_ad.set_value(x);
633 y_ad.set_value(scaling.c_e.cwiseProduct(y));
634 z_ad.set_value(scaling.c_i.cwiseProduct(z));
635 return scaling.f * H_f.value() + H_c.value();
636 },
637 [&](const DenseVector& x, const DenseVector& y,
638 const DenseVector& z) -> SparseMatrix {
639 x_ad.set_value(x);
640 y_ad.set_value(scaling.c_e.cwiseProduct(y));
641 z_ad.set_value(scaling.c_i.cwiseProduct(z));
642 return H_c.value();
643 },
644 [&](const DenseVector& x) -> DenseVector {
645 x_ad.set_value(x);
646 return scaling.c_e.cwiseProduct(c_e_ad.value());
647 },
648 [&](const DenseVector& x) -> SparseMatrix {
649 x_ad.set_value(x);
650 return scaling.c_e.asDiagonal() * A_e.value();
651 },
652 [&](const DenseVector& x) -> DenseVector {
653 x_ad.set_value(x);
654 return scaling.c_i.cwiseProduct(c_i_ad.value());
655 },
656 [&](const DenseVector& x) -> SparseMatrix {
657 x_ad.set_value(x);
658 return scaling.c_i.asDiagonal() * A_i.value();
659 },
660 scaling};
661
662 // Invoke interior-point method solver
663 status =
664 interior_point<Scalar>(matrix_callbacks, iteration_callbacks, options,
665#ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
666 bound_constraint_mask,
667#endif
668 x);
669 }
670
671 if (options.diagnostics) {
672 slp::println("\nExit: {}", status);
673 }
674
675 // Assign the solution to the original Variable instances
676 VariableMatrix<Scalar>{m_decision_variables}.set_value(x);
677
678 return status;
679 }
680
686 template <typename F>
687 requires requires(F callback, const IterationInfo<Scalar>& info) {
688 { callback(info) } -> std::same_as<void>;
689 }
691 m_iteration_callbacks.emplace_back(
692 [=, callback =
693 std::forward<F>(callback)](const IterationInfo<Scalar>& info) {
694 callback(info);
695 return false;
696 });
697 }
698
705 template <typename F>
706 requires requires(F callback, const IterationInfo<Scalar>& info) {
707 { callback(info) } -> std::same_as<bool>;
708 }
710 m_iteration_callbacks.emplace_back(std::forward<F>(callback));
711 }
712
714 void clear_callbacks() { m_iteration_callbacks.clear(); }
715
724 template <typename F>
725 requires requires(F callback, const IterationInfo<Scalar>& info) {
726 { callback(info) } -> std::same_as<bool>;
727 }
729 m_persistent_iteration_callbacks.emplace_back(std::forward<F>(callback));
730 }
731
732 private:
733 // The list of decision variables, which are the root of the problem's
734 // expression tree
735 gch::small_vector<Variable<Scalar>> m_decision_variables;
736
737 // The cost function: f(x)
738 std::optional<Variable<Scalar>> m_f;
739
740 // The list of equality constraints: cₑ(x) = 0
741 gch::small_vector<Variable<Scalar>> m_equality_constraints;
742
743 // The list of inequality constraints: cᵢ(x) ≥ 0
744 gch::small_vector<Variable<Scalar>> m_inequality_constraints;
745
746 // The iteration callbacks
747 gch::small_vector<std::function<bool(const IterationInfo<Scalar>& info)>>
748 m_iteration_callbacks;
749 gch::small_vector<std::function<bool(const IterationInfo<Scalar>& info)>>
750 m_persistent_iteration_callbacks;
751
752 void print_exit_conditions([[maybe_unused]] const Options& options) {
753 // Print possible exit conditions
754 slp::println("User-configured exit conditions:");
755 slp::println(" ↳ error below {}", options.tolerance);
756 if (!m_iteration_callbacks.empty() ||
757 !m_persistent_iteration_callbacks.empty()) {
758 slp::println(" ↳ iteration callback requested stop");
759 }
760 if (std::isfinite(options.max_iterations)) {
761 slp::println(" ↳ executed {} iterations", options.max_iterations);
762 }
763 if (std::isfinite(options.timeout.count())) {
764 slp::println(" ↳ {} elapsed", options.timeout);
765 }
766 }
767
768 void print_problem_analysis() {
769 constexpr std::array types{"no", "constant", "linear", "quadratic",
770 "nonlinear"};
771
772 // Print problem structure
773 slp::println("\nProblem structure:");
774 slp::println(" ↳ {} cost function",
775 types[std::to_underlying(cost_function_type())]);
776 slp::println(" ↳ {} equality constraints",
777 types[std::to_underlying(equality_constraint_type())]);
778 slp::println(" ↳ {} inequality constraints",
779 types[std::to_underlying(inequality_constraint_type())]);
780
781 if (m_decision_variables.size() == 1) {
782 slp::print("\n1 decision variable\n");
783 } else {
784 slp::print("\n{} decision variables\n", m_decision_variables.size());
785 }
786
787 auto print_constraint_types =
788 [](const gch::small_vector<Variable<Scalar>>& constraints) {
789 std::array<size_t, 5> counts{};
790 for (const auto& constraint : constraints) {
791 ++counts[std::to_underlying(constraint.type())];
792 }
793 for (const auto& [count, name] :
794 std::views::zip(counts, std::array{"empty", "constant", "linear",
795 "quadratic", "nonlinear"})) {
796 if (count > 0) {
797 slp::println(" ↳ {} {}", count, name);
798 }
799 }
800 };
801
802 // Print constraint types
803 if (m_equality_constraints.size() == 1) {
804 slp::println("1 equality constraint");
805 } else {
806 slp::println("{} equality constraints", m_equality_constraints.size());
807 }
808 print_constraint_types(m_equality_constraints);
809 if (m_inequality_constraints.size() == 1) {
810 slp::println("1 inequality constraint");
811 } else {
812 slp::println("{} inequality constraints",
813 m_inequality_constraints.size());
814 }
815 print_constraint_types(m_inequality_constraints);
816 }
817};
818
819extern template class EXPORT_TEMPLATE_DECLARE(SLEIPNIR_DLLEXPORT)
820Problem<double>;
821
822} // namespace slp
Definition intrusive_shared_ptr.hpp:27
Definition problem.hpp:67
VariableMatrix< Scalar > symmetric_decision_variable(int rows)
Definition problem.hpp:118
void subject_to(InequalityConstraints< Scalar > &&constraint)
Definition problem.hpp:229
void add_callback(F &&callback)
Definition problem.hpp:709
void subject_to(EqualityConstraints< Scalar > &&constraint)
Definition problem.hpp:207
ExpressionType equality_constraint_type() const
Definition problem.hpp:250
void maximize(Variable< Scalar > &&objective)
Definition problem.hpp:187
void add_persistent_callback(F &&callback)
Definition problem.hpp:728
void subject_to(const InequalityConstraints< Scalar > &constraint)
Definition problem.hpp:218
ExpressionType inequality_constraint_type() const
Definition problem.hpp:263
void minimize(const Variable< Scalar > &cost)
Definition problem.hpp:151
Problem() noexcept=default
Constructs the optimization problem.
VariableMatrix< Scalar > decision_variable(int rows, int cols=1)
Definition problem.hpp:91
void minimize(Variable< Scalar > &&cost)
Definition problem.hpp:162
void clear_callbacks()
Clears the registered callbacks.
Definition problem.hpp:714
void subject_to(const EqualityConstraints< Scalar > &constraint)
Definition problem.hpp:196
void maximize(const Variable< Scalar > &objective)
Definition problem.hpp:173
void add_callback(F &&callback)
Definition problem.hpp:690
ExpressionType cost_function_type() const
Definition problem.hpp:239
ExitStatus solve(const Options &options=Options{}, bool spy=false)
Definition problem.hpp:281
Variable< Scalar > decision_variable()
Definition problem.hpp:78
Definition variable.hpp:47
Solver options.
Definition options.hpp:13