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