Sleipnir C++ API
Loading...
Searching...
No Matches
variable_matrix.hpp
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6#include <array>
7#include <concepts>
8#include <cstddef>
9#include <initializer_list>
10#include <iterator>
11#include <span>
12#include <utility>
13#include <vector>
14
15#include <Eigen/Core>
16#include <Eigen/LU>
17#include <Eigen/QR>
18#include <gch/small_vector.hpp>
19
20#include "sleipnir/autodiff/expression_graph.hpp"
21#include "sleipnir/autodiff/sleipnir_base.hpp"
22#include "sleipnir/autodiff/slice.hpp"
23#include "sleipnir/autodiff/variable.hpp"
24#include "sleipnir/autodiff/variable_block.hpp"
25#include "sleipnir/util/assert.hpp"
26#include "sleipnir/util/concepts.hpp"
27#include "sleipnir/util/empty.hpp"
28#include "sleipnir/util/function_ref.hpp"
29#include "sleipnir/util/symbol_exports.hpp"
30
31namespace slp {
32
36template <typename Scalar_>
38 public:
40 using Scalar = Scalar_;
41
43 VariableMatrix() = default;
44
49 explicit VariableMatrix(int rows) : VariableMatrix{rows, 1} {}
50
55 VariableMatrix(int rows, int cols) : m_rows{rows}, m_cols{cols} {
56 m_storage.reserve(rows * cols);
57 for (int index = 0; index < rows * cols; ++index) {
58 m_storage.emplace_back();
59 }
60 }
61
67 : m_rows{rows}, m_cols{cols} {
68 m_storage.reserve(rows * cols);
69 for (int index = 0; index < rows * cols; ++index) {
70 m_storage.emplace_back(nullptr);
71 }
72 }
73
78 std::initializer_list<std::initializer_list<Variable<Scalar>>> list) {
79 // Get row and column counts for destination matrix
80 m_rows = list.size();
81 m_cols = 0;
82 if (list.size() > 0) {
83 m_cols = list.begin()->size();
84 }
85
86 // Assert all column counts are the same
87 for ([[maybe_unused]]
88 const auto& row : list) {
89 slp_assert(static_cast<int>(row.size()) == m_cols);
90 }
91
92 m_storage.reserve(rows() * cols());
93 for (const auto& row : list) {
94 std::ranges::copy(row, std::back_inserter(m_storage));
95 }
96 }
97
103 // NOLINTNEXTLINE (google-explicit-constructor)
104 VariableMatrix(const std::vector<std::vector<Scalar>>& list) {
105 // Get row and column counts for destination matrix
106 m_rows = list.size();
107 m_cols = 0;
108 if (list.size() > 0) {
109 m_cols = list.begin()->size();
110 }
111
112 // Assert all column counts are the same
113 for ([[maybe_unused]]
114 const auto& row : list) {
115 slp_assert(static_cast<int>(row.size()) == m_cols);
116 }
117
118 m_storage.reserve(rows() * cols());
119 for (const auto& row : list) {
120 std::ranges::copy(row, std::back_inserter(m_storage));
121 }
122 }
123
129 // NOLINTNEXTLINE (google-explicit-constructor)
130 VariableMatrix(const std::vector<std::vector<Variable<Scalar>>>& list) {
131 // Get row and column counts for destination matrix
132 m_rows = list.size();
133 m_cols = 0;
134 if (list.size() > 0) {
135 m_cols = list.begin()->size();
136 }
137
138 // Assert all column counts are the same
139 for ([[maybe_unused]]
140 const auto& row : list) {
141 slp_assert(static_cast<int>(row.size()) == m_cols);
142 }
143
144 m_storage.reserve(rows() * cols());
145 for (const auto& row : list) {
146 std::ranges::copy(row, std::back_inserter(m_storage));
147 }
148 }
149
153 template <typename Derived>
154 // NOLINTNEXTLINE (google-explicit-constructor)
155 VariableMatrix(const Eigen::MatrixBase<Derived>& values)
156 : m_rows{static_cast<int>(values.rows())},
157 m_cols{static_cast<int>(values.cols())} {
158 m_storage.reserve(values.rows() * values.cols());
159 for (int row = 0; row < values.rows(); ++row) {
160 for (int col = 0; col < values.cols(); ++col) {
161 m_storage.emplace_back(values[row, col]);
162 }
163 }
164 }
165
169 template <typename Derived>
170 // NOLINTNEXTLINE (google-explicit-constructor)
171 VariableMatrix(const Eigen::DiagonalBase<Derived>& values)
172 : m_rows{static_cast<int>(values.rows())},
173 m_cols{static_cast<int>(values.cols())} {
174 m_storage.reserve(values.rows() * values.cols());
175 for (int row = 0; row < values.rows(); ++row) {
176 for (int col = 0; col < values.cols(); ++col) {
177 if (row == col) {
178 m_storage.emplace_back(values.diagonal()[row]);
179 } else {
180 m_storage.emplace_back(Scalar(0));
181 }
182 }
183 }
184 }
185
189 // NOLINTNEXTLINE (google-explicit-constructor)
190 VariableMatrix(const Variable<Scalar>& variable) : m_rows{1}, m_cols{1} {
191 m_storage.emplace_back(variable);
192 }
193
197 // NOLINTNEXTLINE (google-explicit-constructor)
198 VariableMatrix(Variable<Scalar>&& variable) : m_rows{1}, m_cols{1} {
199 m_storage.emplace_back(std::move(variable));
200 }
201
205 // NOLINTNEXTLINE (google-explicit-constructor)
207 : m_rows{values.rows()}, m_cols{values.cols()} {
208 m_storage.reserve(rows() * cols());
209 for (int row = 0; row < rows(); ++row) {
210 for (int col = 0; col < cols(); ++col) {
211 m_storage.emplace_back(values[row, col]);
212 }
213 }
214 }
215
219 // NOLINTNEXTLINE (google-explicit-constructor)
221 : m_rows{values.rows()}, m_cols{values.cols()} {
222 m_storage.reserve(rows() * cols());
223 for (int row = 0; row < rows(); ++row) {
224 for (int col = 0; col < cols(); ++col) {
225 m_storage.emplace_back(values[row, col]);
226 }
227 }
228 }
229
233 explicit VariableMatrix(std::span<const Variable<Scalar>> values)
234 : m_rows{static_cast<int>(values.size())}, m_cols{1} {
235 m_storage.reserve(rows() * cols());
236 for (int row = 0; row < rows(); ++row) {
237 for (int col = 0; col < cols(); ++col) {
238 m_storage.emplace_back(values[row * cols() + col]);
239 }
240 }
241 }
242
248 VariableMatrix(std::span<const Variable<Scalar>> values, int rows, int cols)
249 : m_rows{rows}, m_cols{cols} {
250 slp_assert(static_cast<int>(values.size()) == rows * cols);
251 m_storage.reserve(rows * cols);
252 for (int row = 0; row < rows; ++row) {
253 for (int col = 0; col < cols; ++col) {
254 m_storage.emplace_back(values[row * cols + col]);
255 }
256 }
257 }
258
263 template <typename Derived>
264 VariableMatrix& operator=(const Eigen::MatrixBase<Derived>& values) {
265 slp_assert(rows() == values.rows() && cols() == values.cols());
266
267 for (int row = 0; row < values.rows(); ++row) {
268 for (int col = 0; col < values.cols(); ++col) {
269 (*this)[row, col] = values[row, col];
270 }
271 }
272
273 return *this;
274 }
275
283 slp_assert(rows() == 1 && cols() == 1);
284
285 (*this)[0, 0] = value;
286
287 return *this;
288 }
289
293 template <typename Derived>
294 requires std::same_as<typename Derived::Scalar, Scalar>
295 void set_value(const Eigen::MatrixBase<Derived>& values) {
296 slp_assert(rows() == values.rows() && cols() == values.cols());
297
298 for (int row = 0; row < values.rows(); ++row) {
299 for (int col = 0; col < values.cols(); ++col) {
300 (*this)[row, col].set_value(values[row, col]);
301 }
302 }
303 }
304
311 slp_assert(row >= 0 && row < rows());
312 slp_assert(col >= 0 && col < cols());
313 return m_storage[row * cols() + col];
314 }
315
321 const Variable<Scalar>& operator[](int row, int col) const {
322 slp_assert(row >= 0 && row < rows());
323 slp_assert(col >= 0 && col < cols());
324 return m_storage[row * cols() + col];
325 }
326
332 slp_assert(index >= 0 && index < rows() * cols());
333 return m_storage[index];
334 }
335
340 const Variable<Scalar>& operator[](int index) const {
341 slp_assert(index >= 0 && index < rows() * cols());
342 return m_storage[index];
343 }
344
352 VariableBlock<VariableMatrix> block(int row_offset, int col_offset,
353 int block_rows, int block_cols) {
354 slp_assert(row_offset >= 0 && row_offset <= rows());
355 slp_assert(col_offset >= 0 && col_offset <= cols());
356 slp_assert(block_rows >= 0 && block_rows <= rows() - row_offset);
357 slp_assert(block_cols >= 0 && block_cols <= cols() - col_offset);
358 return VariableBlock{*this, row_offset, col_offset, block_rows, block_cols};
359 }
360
369 int col_offset,
370 int block_rows,
371 int block_cols) const {
372 slp_assert(row_offset >= 0 && row_offset <= rows());
373 slp_assert(col_offset >= 0 && col_offset <= cols());
374 slp_assert(block_rows >= 0 && block_rows <= rows() - row_offset);
375 slp_assert(block_cols >= 0 && block_cols <= cols() - col_offset);
376 return VariableBlock{*this, row_offset, col_offset, block_rows, block_cols};
377 }
378
385 int row_slice_length = row_slice.adjust(rows());
386 int col_slice_length = col_slice.adjust(cols());
387 return VariableBlock{*this, std::move(row_slice), row_slice_length,
388 std::move(col_slice), col_slice_length};
389 }
390
397 Slice col_slice) const {
398 int row_slice_length = row_slice.adjust(rows());
399 int col_slice_length = col_slice.adjust(cols());
400 return VariableBlock{*this, std::move(row_slice), row_slice_length,
401 std::move(col_slice), col_slice_length};
402 }
403
415 int row_slice_length,
416 Slice col_slice,
417 int col_slice_length) {
418 return VariableBlock{*this, std::move(row_slice), row_slice_length,
419 std::move(col_slice), col_slice_length};
420 }
421
433 Slice row_slice, int row_slice_length, Slice col_slice,
434 int col_slice_length) const {
435 return VariableBlock{*this, std::move(row_slice), row_slice_length,
436 std::move(col_slice), col_slice_length};
437 }
438
444 VariableBlock<VariableMatrix> segment(int offset, int length) {
445 slp_assert(cols() == 1);
446 slp_assert(offset >= 0 && offset < rows());
447 slp_assert(length >= 0 && length <= rows() - offset);
448 return block(offset, 0, length, 1);
449 }
450
457 int length) const {
458 slp_assert(cols() == 1);
459 slp_assert(offset >= 0 && offset < rows());
460 slp_assert(length >= 0 && length <= rows() - offset);
461 return block(offset, 0, length, 1);
462 }
463
469 slp_assert(row >= 0 && row < rows());
470 return block(row, 0, 1, cols());
471 }
472
478 slp_assert(row >= 0 && row < rows());
479 return block(row, 0, 1, cols());
480 }
481
487 slp_assert(col >= 0 && col < cols());
488 return block(0, col, rows(), 1);
489 }
490
496 slp_assert(col >= 0 && col < cols());
497 return block(0, col, rows(), 1);
498 }
499
504 template <EigenMatrixLike LHS, SleipnirMatrixLike<Scalar> RHS>
505 friend VariableMatrix<Scalar> operator*(const LHS& lhs, const RHS& rhs) {
506 slp_assert(lhs.cols() == rhs.rows());
507
508 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), rhs.cols());
509
510 for (int i = 0; i < lhs.rows(); ++i) {
511 for (int j = 0; j < rhs.cols(); ++j) {
512 Variable sum{Scalar(0)};
513 for (int k = 0; k < lhs.cols(); ++k) {
514 sum += lhs[i, k] * rhs[k, j];
515 }
516 result[i, j] = sum;
517 }
518 }
519
520 return result;
521 }
522
527 template <SleipnirMatrixLike<Scalar> LHS, EigenMatrixLike RHS>
528 friend VariableMatrix<Scalar> operator*(const LHS& lhs, const RHS& rhs) {
529 slp_assert(lhs.cols() == rhs.rows());
530
531 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), rhs.cols());
532
533 for (int i = 0; i < lhs.rows(); ++i) {
534 for (int j = 0; j < rhs.cols(); ++j) {
535 Variable sum{Scalar(0)};
536 for (int k = 0; k < lhs.cols(); ++k) {
537 sum += lhs[i, k] * rhs[k, j];
538 }
539 result[i, j] = sum;
540 }
541 }
542
543 return result;
544 }
545
550 template <SleipnirMatrixLike<Scalar> LHS, SleipnirMatrixLike<Scalar> RHS>
551 friend VariableMatrix<Scalar> operator*(const LHS& lhs, const RHS& rhs) {
552 slp_assert(lhs.cols() == rhs.rows());
553
554 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), rhs.cols());
555
556 for (int i = 0; i < lhs.rows(); ++i) {
557 for (int j = 0; j < rhs.cols(); ++j) {
558 Variable sum{Scalar(0)};
559 for (int k = 0; k < lhs.cols(); ++k) {
560 sum += lhs[i, k] * rhs[k, j];
561 }
562 result[i, j] = sum;
563 }
564 }
565
566 return result;
567 }
568
573 template <EigenMatrixLike LHS>
574 friend VariableMatrix<Scalar> operator*(const LHS& lhs,
575 const Variable<Scalar>& rhs) {
576 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
577
578 for (int row = 0; row < result.rows(); ++row) {
579 for (int col = 0; col < result.cols(); ++col) {
580 result[row, col] = lhs[row, col] * rhs;
581 }
582 }
583
584 return result;
585 }
586
591 template <SleipnirMatrixLike<Scalar> LHS, ScalarLike RHS>
592 friend VariableMatrix<Scalar> operator*(const LHS& lhs, const RHS& rhs) {
593 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
594
595 for (int row = 0; row < result.rows(); ++row) {
596 for (int col = 0; col < result.cols(); ++col) {
597 result[row, col] = lhs[row, col] * rhs;
598 }
599 }
600
601 return result;
602 }
603
608 template <EigenMatrixLike RHS>
610 const RHS& rhs) {
611 VariableMatrix<Scalar> result(detail::empty, rhs.rows(), rhs.cols());
612
613 for (int row = 0; row < result.rows(); ++row) {
614 for (int col = 0; col < result.cols(); ++col) {
615 result[row, col] = rhs[row, col] * lhs;
616 }
617 }
618
619 return result;
620 }
621
626 template <ScalarLike LHS, SleipnirMatrixLike<Scalar> RHS>
627 friend VariableMatrix<Scalar> operator*(const LHS& lhs, const RHS& rhs) {
628 VariableMatrix<Scalar> result(detail::empty, rhs.rows(), rhs.cols());
629
630 for (int row = 0; row < result.rows(); ++row) {
631 for (int col = 0; col < result.cols(); ++col) {
632 result[row, col] = rhs[row, col] * lhs;
633 }
634 }
635
636 return result;
637 }
638
644 slp_assert(cols() == rhs.rows() && cols() == rhs.cols());
645
646 for (int i = 0; i < rows(); ++i) {
647 VariableMatrix lhs_old_row = row(i);
648 for (int j = 0; j < rhs.cols(); ++j) {
649 Variable sum{Scalar(0)};
650 for (int k = 0; k < cols(); ++k) {
651 sum += lhs_old_row[k] * rhs[k, j];
652 }
653 (*this)[i, j] = sum;
654 }
655 }
656
657 return *this;
658 }
659
665 for (int row = 0; row < rows(); ++row) {
666 for (int col = 0; col < cols(); ++col) {
667 (*this)[row, col] *= rhs;
668 }
669 }
670
671 return *this;
672 }
673
679 template <EigenMatrixLike LHS>
680 friend VariableMatrix<Scalar> operator/(const LHS& lhs,
681 const Variable<Scalar>& rhs) {
682 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
683
684 for (int row = 0; row < result.rows(); ++row) {
685 for (int col = 0; col < result.cols(); ++col) {
686 result[row, col] = lhs[row, col] / rhs;
687 }
688 }
689
690 return result;
691 }
692
698 template <SleipnirMatrixLike<Scalar> LHS, ScalarLike RHS>
700 friend VariableMatrix<Scalar> operator/(const LHS& lhs, const RHS& rhs) {
701 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
702
703 for (int row = 0; row < result.rows(); ++row) {
704 for (int col = 0; col < result.cols(); ++col) {
705 result[row, col] = lhs[row, col] / rhs;
706 }
707 }
708
709 return result;
710 }
711
717 template <SleipnirMatrixLike<Scalar> LHS>
718 friend VariableMatrix<Scalar> operator/(const LHS& lhs,
719 const Variable<Scalar>& rhs) {
720 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
721
722 for (int row = 0; row < result.rows(); ++row) {
723 for (int col = 0; col < result.cols(); ++col) {
724 result[row, col] = lhs[row, col] / rhs;
725 }
726 }
727
728 return result;
729 }
730
736 for (int row = 0; row < rows(); ++row) {
737 for (int col = 0; col < cols(); ++col) {
738 (*this)[row, col] /= rhs;
739 }
740 }
741
742 return *this;
743 }
744
750 template <EigenMatrixLike LHS, SleipnirMatrixLike<Scalar> RHS>
751 friend VariableMatrix<Scalar> operator+(const LHS& lhs, const RHS& rhs) {
752 slp_assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols());
753
754 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
755
756 for (int row = 0; row < result.rows(); ++row) {
757 for (int col = 0; col < result.cols(); ++col) {
758 result[row, col] = lhs[row, col] + rhs[row, col];
759 }
760 }
761
762 return result;
763 }
764
770 template <SleipnirMatrixLike<Scalar> LHS, EigenMatrixLike RHS>
771 friend VariableMatrix<Scalar> operator+(const LHS& lhs, const RHS& rhs) {
772 slp_assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols());
773
774 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
775
776 for (int row = 0; row < result.rows(); ++row) {
777 for (int col = 0; col < result.cols(); ++col) {
778 result[row, col] = lhs[row, col] + rhs[row, col];
779 }
780 }
781
782 return result;
783 }
784
790 template <SleipnirMatrixLike<Scalar> LHS, SleipnirMatrixLike<Scalar> RHS>
791 friend VariableMatrix<Scalar> operator+(const LHS& lhs, const RHS& rhs) {
792 slp_assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols());
793
794 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
795
796 for (int row = 0; row < result.rows(); ++row) {
797 for (int col = 0; col < result.cols(); ++col) {
798 result[row, col] = lhs[row, col] + rhs[row, col];
799 }
800 }
801
802 return result;
803 }
804
810 slp_assert(rows() == rhs.rows() && cols() == rhs.cols());
811
812 for (int row = 0; row < rows(); ++row) {
813 for (int col = 0; col < cols(); ++col) {
814 (*this)[row, col] += rhs[row, col];
815 }
816 }
817
818 return *this;
819 }
820
826 slp_assert(rows() == 1 && cols() == 1);
827
828 for (int row = 0; row < rows(); ++row) {
829 for (int col = 0; col < cols(); ++col) {
830 (*this)[row, col] += rhs;
831 }
832 }
833
834 return *this;
835 }
836
842 template <EigenMatrixLike LHS, SleipnirMatrixLike<Scalar> RHS>
843 friend VariableMatrix<Scalar> operator-(const LHS& lhs, const RHS& rhs) {
844 slp_assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols());
845
846 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
847
848 for (int row = 0; row < result.rows(); ++row) {
849 for (int col = 0; col < result.cols(); ++col) {
850 result[row, col] = lhs[row, col] - rhs[row, col];
851 }
852 }
853
854 return result;
855 }
856
862 template <SleipnirMatrixLike<Scalar> LHS, EigenMatrixLike RHS>
863 friend VariableMatrix<Scalar> operator-(const LHS& lhs, const RHS& rhs) {
864 slp_assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols());
865
866 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
867
868 for (int row = 0; row < result.rows(); ++row) {
869 for (int col = 0; col < result.cols(); ++col) {
870 result[row, col] = lhs[row, col] - rhs[row, col];
871 }
872 }
873
874 return result;
875 }
876
882 template <SleipnirMatrixLike<Scalar> LHS, SleipnirMatrixLike<Scalar> RHS>
883 friend VariableMatrix<Scalar> operator-(const LHS& lhs, const RHS& rhs) {
884 slp_assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols());
885
886 VariableMatrix<Scalar> result(detail::empty, lhs.rows(), lhs.cols());
887
888 for (int row = 0; row < result.rows(); ++row) {
889 for (int col = 0; col < result.cols(); ++col) {
890 result[row, col] = lhs[row, col] - rhs[row, col];
891 }
892 }
893
894 return result;
895 }
896
902 slp_assert(rows() == rhs.rows() && cols() == rhs.cols());
903
904 for (int row = 0; row < rows(); ++row) {
905 for (int col = 0; col < cols(); ++col) {
906 (*this)[row, col] -= rhs[row, col];
907 }
908 }
909
910 return *this;
911 }
912
918 slp_assert(rows() == 1 && cols() == 1);
919
920 for (int row = 0; row < rows(); ++row) {
921 for (int col = 0; col < cols(); ++col) {
922 (*this)[row, col] -= rhs;
923 }
924 }
925
926 return *this;
927 }
928
933 const SleipnirMatrixLike<Scalar> auto& lhs) {
934 VariableMatrix<Scalar> result{detail::empty, lhs.rows(), lhs.cols()};
935
936 for (int row = 0; row < result.rows(); ++row) {
937 for (int col = 0; col < result.cols(); ++col) {
938 result[row, col] = -lhs[row, col];
939 }
940 }
941
942 return result;
943 }
944
946 // NOLINTNEXTLINE (google-explicit-constructor)
947 operator Variable<Scalar>() const {
948 slp_assert(rows() == 1 && cols() == 1);
949 return (*this)[0, 0];
950 }
951
956 VariableMatrix<Scalar> result{detail::empty, cols(), rows()};
957
958 for (int row = 0; row < rows(); ++row) {
959 for (int col = 0; col < cols(); ++col) {
960 result[col, row] = (*this)[row, col];
961 }
962 }
963
964 return result;
965 }
966
970 int rows() const { return m_rows; }
971
975 int cols() const { return m_cols; }
976
982 Scalar value(int row, int col) { return (*this)[row, col].value(); }
983
988 Scalar value(int index) { return (*this)[index].value(); }
989
993 Eigen::MatrixX<Scalar> value() {
994 Eigen::MatrixX<Scalar> result{rows(), cols()};
995
996 for (int row = 0; row < rows(); ++row) {
997 for (int col = 0; col < cols(); ++col) {
998 result[row, col] = value(row, col);
999 }
1000 }
1001
1002 return result;
1003 }
1004
1008 Eigen::MatrixX<Variable<Scalar>> to_eigen() const {
1009 Eigen::MatrixX<Variable<Scalar>> result{rows(), cols()};
1010
1011 for (int row = 0; row < rows(); ++row) {
1012 for (int col = 0; col < cols(); ++col) {
1013 result[row, col] = (*this)[row, col];
1014 }
1015 }
1016
1017 return result;
1018 }
1019
1026 const {
1027 VariableMatrix<Scalar> result{detail::empty, rows(), cols()};
1028
1029 for (int row = 0; row < rows(); ++row) {
1030 for (int col = 0; col < cols(); ++col) {
1031 result[row, col] = unary_op((*this)[row, col]);
1032 }
1033 }
1034
1035 return result;
1036 }
1037
1042 slp_assert(rows() == cols());
1043
1044 // Coefficients for (13, 13) Padé approximant of exp(A) are from the
1045 // following program:
1046 //
1047 // ```python
1048 // #!/usr/bin/env python
1049 //
1050 // import mpmath as mp
1051 //
1052 // # https://en.wikipedia.org/wiki/IEEE_754#Basic_and_interchange_formats
1053 // mp.mp.prec = 113 # quad precision
1054 //
1055 // L = 13
1056 // M = 13
1057 // p, q = mp.pade(mp.taylor(mp.exp, 0, L + M), L, M)
1058 //
1059 // print("constexpr std::array p{")
1060 // for k, p_k in enumerate(p):
1061 // print(f"Scalar({p_k}L){',' if k < len(p) - 1 else '};'}")
1062 // print("constexpr std::array q{")
1063 // for k, q_k in enumerate(q):
1064 // print(f"Scalar({q_k}L){',' if k < len(q) - 1 else '};'}")
1065 // ```
1066 constexpr size_t NUM_COEFFS = 14;
1067 constexpr std::array p{Scalar(1.0L),
1068 Scalar(0.499999999999999999999987615564159L),
1069 Scalar(0.119999999999999999999993719796468L),
1070 Scalar(0.0183333333333333333333318078905823L),
1071 Scalar(0.00199275362318840579710121408775205L),
1072 Scalar(0.00016304347826086956521736560868541L),
1073 Scalar(1.03519668737060041407846479512981e-05L),
1074 Scalar(5.17598343685300207039205144279119e-07L),
1075 Scalar(2.04315135665250081725989398668247e-08L),
1076 Scalar(6.30602270571759511499920806570486e-10L),
1077 Scalar(1.48377004840414002705850442238441e-11L),
1078 Scalar(2.52915349159796595521307855480881e-13L),
1079 Scalar(2.81017054621996217245857568431558e-15L),
1080 Scalar(1.54404975067030888596595580141692e-17L)};
1081 constexpr std::array q{Scalar(1.0L),
1082 Scalar(-0.500000000000000000000012384435841L),
1083 Scalar(0.120000000000000000000006104232309L),
1084 Scalar(-0.0183333333333333333333347707904729L),
1085 Scalar(0.00199275362318840579710166350137732L),
1086 Scalar(-0.000163043478260869565217413851346892L),
1087 Scalar(1.03519668737060041407885187519331e-05L),
1088 Scalar(-5.17598343685300207039443859549262e-07L),
1089 Scalar(2.04315135665250081726103781506842e-08L),
1090 Scalar(-6.30602270571759511500345035969254e-10L),
1091 Scalar(1.48377004840414002705969744674248e-11L),
1092 Scalar(-2.52915349159796595521550648156349e-13L),
1093 Scalar(2.81017054621996217246180850207342e-15L),
1094 Scalar(-1.54404975067030888596810607146175e-17L)};
1095 static_assert(p.size() == NUM_COEFFS);
1096 static_assert(q.size() == NUM_COEFFS);
1097
1098 // 13
1099 // P = Σ pₖAᵏ
1100 // k=0
1101 //
1102 // 13
1103 // Q = Σ qₖAᵏ
1104 // k=0
1106 Eigen::VectorX<Scalar>::Constant(rows(), p[0]).asDiagonal()};
1108 Eigen::VectorX<Scalar>::Constant(rows(), q[0]).asDiagonal()};
1109 auto A_pow = *this;
1110 size_t k = 1;
1111 while (true) {
1112 P += p[k] * A_pow;
1113 Q += q[k] * A_pow;
1114
1115 ++k;
1116 if (k < NUM_COEFFS) {
1117 A_pow *= *this;
1118 } else {
1119 break;
1120 }
1121 }
1122
1123 // https://mpmath.org/doc/current/calculus/approximation.html#mpmath.pade
1124 // defines the Padé approximant as exp(A)Q ≈ P, so:
1125 //
1126 // exp(A) ≈ P / Q
1127 // exp(A) ≈ (Qᵀ \ Pᵀ)ᵀ
1129 Q.T().to_eigen().lu().solve(P.T().to_eigen()).transpose()};
1130 }
1131
1132#ifndef DOXYGEN_SHOULD_SKIP_THIS
1133
1134 class iterator {
1135 public:
1136 using iterator_category = std::bidirectional_iterator_tag;
1137 using value_type = Variable<Scalar>;
1138 using difference_type = std::ptrdiff_t;
1139 using pointer = Variable<Scalar>*;
1140 using reference = Variable<Scalar>&;
1141
1142 constexpr iterator() noexcept = default;
1143
1144 explicit constexpr iterator(
1145 gch::small_vector<Variable<Scalar>>::iterator it) noexcept
1146 : m_it{it} {}
1147
1148 constexpr iterator& operator++() noexcept {
1149 ++m_it;
1150 return *this;
1151 }
1152
1153 constexpr iterator operator++(int) noexcept {
1154 iterator retval = *this;
1155 ++(*this);
1156 return retval;
1157 }
1158
1159 constexpr iterator& operator--() noexcept {
1160 --m_it;
1161 return *this;
1162 }
1163
1164 constexpr iterator operator--(int) noexcept {
1165 iterator retval = *this;
1166 --(*this);
1167 return retval;
1168 }
1169
1170 constexpr bool operator==(const iterator&) const noexcept = default;
1171
1172 constexpr reference operator*() const noexcept { return *m_it; }
1173
1174 private:
1175 gch::small_vector<Variable<Scalar>>::iterator m_it;
1176 };
1177
1178 class const_iterator {
1179 public:
1180 using iterator_category = std::bidirectional_iterator_tag;
1181 using value_type = Variable<Scalar>;
1182 using difference_type = std::ptrdiff_t;
1183 using pointer = Variable<Scalar>*;
1184 using const_reference = const Variable<Scalar>&;
1185
1186 constexpr const_iterator() noexcept = default;
1187
1188 explicit constexpr const_iterator(
1189 gch::small_vector<Variable<Scalar>>::const_iterator it) noexcept
1190 : m_it{it} {}
1191
1192 constexpr const_iterator& operator++() noexcept {
1193 ++m_it;
1194 return *this;
1195 }
1196
1197 constexpr const_iterator operator++(int) noexcept {
1198 const_iterator retval = *this;
1199 ++(*this);
1200 return retval;
1201 }
1202
1203 constexpr const_iterator& operator--() noexcept {
1204 --m_it;
1205 return *this;
1206 }
1207
1208 constexpr const_iterator operator--(int) noexcept {
1209 const_iterator retval = *this;
1210 --(*this);
1211 return retval;
1212 }
1213
1214 constexpr bool operator==(const const_iterator&) const noexcept = default;
1215
1216 constexpr const_reference operator*() const noexcept { return *m_it; }
1217
1218 private:
1219 gch::small_vector<Variable<Scalar>>::const_iterator m_it;
1220 };
1221
1222 using reverse_iterator = std::reverse_iterator<iterator>;
1223 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
1224
1225#endif // DOXYGEN_SHOULD_SKIP_THIS
1226
1230 iterator begin() { return iterator{m_storage.begin()}; }
1231
1235 iterator end() { return iterator{m_storage.end()}; }
1236
1240 const_iterator begin() const { return const_iterator{m_storage.begin()}; }
1241
1245 const_iterator end() const { return const_iterator{m_storage.end()}; }
1246
1250 const_iterator cbegin() const { return const_iterator{m_storage.cbegin()}; }
1251
1255 const_iterator cend() const { return const_iterator{m_storage.cend()}; }
1256
1260 reverse_iterator rbegin() { return reverse_iterator{end()}; }
1261
1265 reverse_iterator rend() { return reverse_iterator{begin()}; }
1266
1270 const_reverse_iterator rbegin() const {
1271 return const_reverse_iterator{end()};
1272 }
1273
1277 const_reverse_iterator rend() const {
1278 return const_reverse_iterator{begin()};
1279 }
1280
1284 const_reverse_iterator crbegin() const {
1285 return const_reverse_iterator{cend()};
1286 }
1287
1291 const_reverse_iterator crend() const {
1292 return const_reverse_iterator{cbegin()};
1293 }
1294
1298 size_t size() const { return m_storage.size(); }
1299
1305 VariableMatrix<Scalar> result{detail::empty, rows, rows};
1306
1307 for (int row = 0; row < rows; ++row) {
1308 for (int col = 0; col < rows; ++col) {
1309 result[row, col] = row == col ? Scalar(1) : Scalar(0);
1310 }
1311 }
1312
1313 return result;
1314 }
1315
1322 VariableMatrix<Scalar> result{detail::empty, rows, cols};
1323
1324 for (auto& elem : result) {
1325 elem = Scalar(0);
1326 }
1327
1328 return result;
1329 }
1330
1337 VariableMatrix<Scalar> result{detail::empty, rows, cols};
1338
1339 for (auto& elem : result) {
1340 elem = Scalar(1);
1341 }
1342
1343 return result;
1344 }
1345
1353 VariableMatrix<Scalar> result{detail::empty, rows, cols};
1354
1355 for (auto& elem : result) {
1356 elem = constant;
1357 }
1358
1359 return result;
1360 }
1361
1362 private:
1363 gch::small_vector<Variable<Scalar>> m_storage;
1364 int m_rows = 0;
1365 int m_cols = 0;
1366};
1367
1368template <typename Derived>
1369VariableMatrix(const Eigen::MatrixBase<Derived>&)
1370 -> VariableMatrix<typename Derived::Scalar>;
1371
1372template <typename Derived>
1373VariableMatrix(const Eigen::DiagonalBase<Derived>&)
1374 -> VariableMatrix<typename Derived::Scalar>;
1375
1382template <typename Scalar>
1383VariableMatrix<Scalar> cwise_reduce(
1384 const VariableMatrix<Scalar>& lhs, const VariableMatrix<Scalar>& rhs,
1385 function_ref<Variable<Scalar>(const Variable<Scalar>& x,
1386 const Variable<Scalar>& y)>
1387 binary_op) {
1388 slp_assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols());
1389
1390 VariableMatrix<Scalar> result{detail::empty, lhs.rows(), lhs.cols()};
1391
1392 for (int row = 0; row < lhs.rows(); ++row) {
1393 for (int col = 0; col < lhs.cols(); ++col) {
1394 result[row, col] = binary_op(lhs[row, col], rhs[row, col]);
1395 }
1396 }
1397
1398 return result;
1399}
1400
1410template <typename Scalar>
1411VariableMatrix<Scalar> block(
1412 std::initializer_list<std::initializer_list<VariableMatrix<Scalar>>> list) {
1413 // Get row and column counts for destination matrix
1414 int rows = 0;
1415 int cols = -1;
1416 for (const auto& row : list) {
1417 if (row.size() > 0) {
1418 rows += row.begin()->rows();
1419 }
1420
1421 // Get number of columns in this row
1422 int latest_cols = 0;
1423 for (const auto& elem : row) {
1424 // Assert the first and latest row have the same height
1425 slp_assert(row.begin()->rows() == elem.rows());
1426
1427 latest_cols += elem.cols();
1428 }
1429
1430 // If this is the first row, record the column count. Otherwise, assert the
1431 // first and latest column counts are the same.
1432 if (cols == -1) {
1433 cols = latest_cols;
1434 } else {
1435 slp_assert(cols == latest_cols);
1436 }
1437 }
1438
1439 VariableMatrix<Scalar> result{detail::empty, rows, cols};
1440
1441 int row_offset = 0;
1442 for (const auto& row : list) {
1443 int col_offset = 0;
1444 for (const auto& elem : row) {
1445 result.block(row_offset, col_offset, elem.rows(), elem.cols()) = elem;
1446 col_offset += elem.cols();
1447 }
1448 if (row.size() > 0) {
1449 row_offset += row.begin()->rows();
1450 }
1451 }
1452
1453 return result;
1454}
1455
1467template <typename Scalar>
1468VariableMatrix<Scalar> block(
1469 const std::vector<std::vector<VariableMatrix<Scalar>>>& list) {
1470 // Get row and column counts for destination matrix
1471 int rows = 0;
1472 int cols = -1;
1473 for (const auto& row : list) {
1474 if (row.size() > 0) {
1475 rows += row.begin()->rows();
1476 }
1477
1478 // Get number of columns in this row
1479 int latest_cols = 0;
1480 for (const auto& elem : row) {
1481 // Assert the first and latest row have the same height
1482 slp_assert(row.begin()->rows() == elem.rows());
1483
1484 latest_cols += elem.cols();
1485 }
1486
1487 // If this is the first row, record the column count. Otherwise, assert the
1488 // first and latest column counts are the same.
1489 if (cols == -1) {
1490 cols = latest_cols;
1491 } else {
1492 slp_assert(cols == latest_cols);
1493 }
1494 }
1495
1496 VariableMatrix<Scalar> result{detail::empty, rows, cols};
1497
1498 int row_offset = 0;
1499 for (const auto& row : list) {
1500 int col_offset = 0;
1501 for (const auto& elem : row) {
1502 result.block(row_offset, col_offset, elem.rows(), elem.cols()) = elem;
1503 col_offset += elem.cols();
1504 }
1505 if (row.size() > 0) {
1506 row_offset += row.begin()->rows();
1507 }
1508 }
1509
1510 return result;
1511}
1512
1519template <typename Scalar>
1520VariableMatrix<Scalar> solve(const VariableMatrix<Scalar>& A,
1521 const VariableMatrix<Scalar>& B) {
1522 // m x n * n x p = m x p
1523 slp_assert(A.rows() == B.rows());
1524
1525 if (A.rows() == 1 && A.cols() == 1) {
1526 // Compute optimal inverse instead of using Eigen's general solver
1527 return B[0, 0] / A[0, 0];
1528 } else if (A.rows() == 2 && A.cols() == 2) {
1529 // Compute optimal inverse instead of using Eigen's general solver
1530 //
1531 // [a b]⁻¹ ___1___ [ d −b]
1532 // [c d] = ad − bc [−c a]
1533
1534 const auto& a = A[0, 0];
1535 const auto& b = A[0, 1];
1536 const auto& c = A[1, 0];
1537 const auto& d = A[1, 1];
1538
1539 VariableMatrix adj_A{{d, -b}, {-c, a}};
1540 auto det_A = a * d - b * c;
1541 return adj_A / det_A * B;
1542 } else if (A.rows() == 3 && A.cols() == 3) {
1543 // Compute optimal inverse instead of using Eigen's general solver
1544 //
1545 // [a b c]⁻¹
1546 // [d e f]
1547 // [g h i]
1548 // 1 [ei − fh ch − bi bf − ce]
1549 // = ------------------------------------ [fg − di ai − cg cd − af]
1550 // a(ei − fh) + b(fg − di) + c(dh − eg) [dh − eg bg − ah ae − bd]
1551 //
1552 // https://www.wolframalpha.com/input?i=inverse+%7B%7Ba%2C+b%2C+c%7D%2C+%7Bd%2C+e%2C+f%7D%2C+%7Bg%2C+h%2C+i%7D%7D
1553
1554 const auto& a = A[0, 0];
1555 const auto& b = A[0, 1];
1556 const auto& c = A[0, 2];
1557 const auto& d = A[1, 0];
1558 const auto& e = A[1, 1];
1559 const auto& f = A[1, 2];
1560 const auto& g = A[2, 0];
1561 const auto& h = A[2, 1];
1562 const auto& i = A[2, 2];
1563
1564 auto ae = a * e;
1565 auto af = a * f;
1566 auto ah = a * h;
1567 auto ai = a * i;
1568 auto bd = b * d;
1569 auto bf = b * f;
1570 auto bg = b * g;
1571 auto bi = b * i;
1572 auto cd = c * d;
1573 auto ce = c * e;
1574 auto cg = c * g;
1575 auto ch = c * h;
1576 auto dh = d * h;
1577 auto di = d * i;
1578 auto eg = e * g;
1579 auto ei = e * i;
1580 auto fg = f * g;
1581 auto fh = f * h;
1582
1583 auto adj_A00 = ei - fh;
1584 auto adj_A10 = fg - di;
1585 auto adj_A20 = dh - eg;
1586
1587 VariableMatrix adj_A{{adj_A00, ch - bi, bf - ce},
1588 {adj_A10, ai - cg, cd - af},
1589 {adj_A20, bg - ah, ae - bd}};
1590 auto det_A = a * adj_A00 + b * adj_A10 + c * adj_A20;
1591 return adj_A / det_A * B;
1592 } else if (A.rows() == 4 && A.cols() == 4) {
1593 // Compute optimal inverse instead of using Eigen's general solver
1594 //
1595 // [a b c d]⁻¹
1596 // [e f g h]
1597 // [i j k l]
1598 // [m n o p]
1599 //
1600 // https://www.wolframalpha.com/input?i=inverse+%7B%7Ba%2C+b%2C+c%2C+d%7D%2C+%7Be%2C+f%2C+g%2C+h%7D%2C+%7Bi%2C+j%2C+k%2C+l%7D%2C+%7Bm%2C+n%2C+o%2C+p%7D%7D
1601
1602 const auto& a = A[0, 0];
1603 const auto& b = A[0, 1];
1604 const auto& c = A[0, 2];
1605 const auto& d = A[0, 3];
1606 const auto& e = A[1, 0];
1607 const auto& f = A[1, 1];
1608 const auto& g = A[1, 2];
1609 const auto& h = A[1, 3];
1610 const auto& i = A[2, 0];
1611 const auto& j = A[2, 1];
1612 const auto& k = A[2, 2];
1613 const auto& l = A[2, 3];
1614 const auto& m = A[3, 0];
1615 const auto& n = A[3, 1];
1616 const auto& o = A[3, 2];
1617 const auto& p = A[3, 3];
1618
1619 auto afk = a * f * k;
1620 auto afl = a * f * l;
1621 auto afo = a * f * o;
1622 auto afp = a * f * p;
1623 auto agj = a * g * j;
1624 auto agl = a * g * l;
1625 auto agn = a * g * n;
1626 auto agp = a * g * p;
1627 auto ahj = a * h * j;
1628 auto ahk = a * h * k;
1629 auto ahn = a * h * n;
1630 auto aho = a * h * o;
1631 auto ajo = a * j * o;
1632 auto ajp = a * j * p;
1633 auto akn = a * k * n;
1634 auto akp = a * k * p;
1635 auto aln = a * l * n;
1636 auto alo = a * l * o;
1637 auto bek = b * e * k;
1638 auto bel = b * e * l;
1639 auto beo = b * e * o;
1640 auto bep = b * e * p;
1641 auto bgi = b * g * i;
1642 auto bgl = b * g * l;
1643 auto bgm = b * g * m;
1644 auto bgp = b * g * p;
1645 auto bhi = b * h * i;
1646 auto bhk = b * h * k;
1647 auto bhm = b * h * m;
1648 auto bho = b * h * o;
1649 auto bio = b * i * o;
1650 auto bip = b * i * p;
1651 auto bjp = b * j * p;
1652 auto bkm = b * k * m;
1653 auto bkp = b * k * p;
1654 auto blm = b * l * m;
1655 auto blo = b * l * o;
1656 auto cej = c * e * j;
1657 auto cel = c * e * l;
1658 auto cen = c * e * n;
1659 auto cep = c * e * p;
1660 auto cfi = c * f * i;
1661 auto cfl = c * f * l;
1662 auto cfm = c * f * m;
1663 auto cfp = c * f * p;
1664 auto chi = c * h * i;
1665 auto chj = c * h * j;
1666 auto chm = c * h * m;
1667 auto chn = c * h * n;
1668 auto cin = c * i * n;
1669 auto cip = c * i * p;
1670 auto cjm = c * j * m;
1671 auto cjp = c * j * p;
1672 auto clm = c * l * m;
1673 auto cln = c * l * n;
1674 auto dej = d * e * j;
1675 auto dek = d * e * k;
1676 auto den = d * e * n;
1677 auto deo = d * e * o;
1678 auto dfi = d * f * i;
1679 auto dfk = d * f * k;
1680 auto dfm = d * f * m;
1681 auto dfo = d * f * o;
1682 auto dgi = d * g * i;
1683 auto dgj = d * g * j;
1684 auto dgm = d * g * m;
1685 auto dgn = d * g * n;
1686 auto din = d * i * n;
1687 auto dio = d * i * o;
1688 auto djm = d * j * m;
1689 auto djo = d * j * o;
1690 auto dkm = d * k * m;
1691 auto dkn = d * k * n;
1692 auto ejo = e * j * o;
1693 auto ejp = e * j * p;
1694 auto ekn = e * k * n;
1695 auto ekp = e * k * p;
1696 auto eln = e * l * n;
1697 auto elo = e * l * o;
1698 auto fio = f * i * o;
1699 auto fip = f * i * p;
1700 auto fkm = f * k * m;
1701 auto fkp = f * k * p;
1702 auto flm = f * l * m;
1703 auto flo = f * l * o;
1704 auto gin = g * i * n;
1705 auto gip = g * i * p;
1706 auto gjm = g * j * m;
1707 auto gjp = g * j * p;
1708 auto glm = g * l * m;
1709 auto gln = g * l * n;
1710 auto hin = h * i * n;
1711 auto hio = h * i * o;
1712 auto hjm = h * j * m;
1713 auto hjo = h * j * o;
1714 auto hkm = h * k * m;
1715 auto hkn = h * k * n;
1716
1717 auto adj_A00 = fkp - flo - gjp + gln + hjo - hkn;
1718 auto adj_A01 = -bkp + blo + cjp - cln - djo + dkn;
1719 auto adj_A02 = bgp - bho - cfp + chn + dfo - dgn;
1720 auto adj_A03 = -bgl + bhk + cfl - chj - dfk + dgj;
1721 auto adj_A10 = -ekp + elo + gip - glm - hio + hkm;
1722 auto adj_A11 = akp - alo - cip + clm + dio - dkm;
1723 auto adj_A12 = -agp + aho + cep - chm - deo + dgm;
1724 auto adj_A13 = agl - ahk - cel + chi + dek - dgi;
1725 auto adj_A20 = ejp - eln - fip + flm + hin - hjm;
1726 auto adj_A21 = -ajp + aln + bip - blm - din + djm;
1727 auto adj_A22 = afp - ahn - bep + bhm + den - dfm;
1728 auto adj_A23 = -afl + ahj + bel - bhi - dej + dfi;
1729 auto adj_A30 = -ejo + ekn + fio - fkm - gin + gjm;
1730 // NOLINTNEXTLINE(build/include_what_you_use)
1731 auto adj_A31 = ajo - akn - bio + bkm + cin - cjm;
1732 auto adj_A32 = -afo + agn + beo - bgm - cen + cfm;
1733 auto adj_A33 = afk - agj - bek + bgi + cej - cfi;
1734
1735 VariableMatrix adj_A{{adj_A00, adj_A01, adj_A02, adj_A03},
1736 {adj_A10, adj_A11, adj_A12, adj_A13},
1737 {adj_A20, adj_A21, adj_A22, adj_A23},
1738 {adj_A30, adj_A31, adj_A32, adj_A33}};
1739 auto det_A = a * adj_A00 + b * adj_A10 + c * adj_A20 + d * adj_A30;
1740 return adj_A / det_A * B;
1741 } else {
1742 return VariableMatrix<Scalar>{
1743 A.to_eigen().householderQr().solve(B.to_eigen())};
1744 }
1745}
1746
1747namespace detail {
1748
1760template <typename Scalar>
1761VariableMatrix<Scalar> gradient_tree(const ExpressionGraph<Scalar>& top_list,
1762 const VariableMatrix<Scalar>& wrt) {
1763 slp_assert(wrt.cols() == 1);
1764
1765 // Read docs/algorithms.md#Reverse_accumulation_automatic_differentiation
1766 // for background on reverse accumulation automatic differentiation.
1767
1768 if (top_list.empty()) {
1769 return VariableMatrix<Scalar>{detail::empty, wrt.rows(), 1};
1770 }
1771
1772 // Set root node's adjoint to 1 since df/df is 1
1773 top_list[0]->adj_expr = constant_ptr(Scalar(1));
1774
1775 // df/dx = (df/dy)(dy/dx). The adjoint of x is equal to the adjoint of y
1776 // multiplied by dy/dx. If there are multiple "paths" from the root node to
1777 // variable; the variable's adjoint is the sum of each path's adjoint
1778 // contribution.
1779 for (auto& node : top_list) {
1780 // Leaf nodes have no children to propagate adjoints to, and a null (zero)
1781 // adjoint has nothing to contribute
1782 if (!node->is_leaf && node->adj_expr != nullptr) {
1783 node->accumulate_adjoints_expr();
1784 }
1785 }
1786
1787 // Move gradient tree to return value
1788 VariableMatrix<Scalar> grad{detail::empty, wrt.rows(), 1};
1789 for (int row = 0; row < grad.rows(); ++row) {
1790 grad[row] = Variable{std::move(wrt[row].expr->adj_expr)};
1791 }
1792
1793 // Unlink adjoints to avoid circular references between them and their
1794 // parent expressions. This ensures all expressions are returned to the free
1795 // list.
1796 for (auto& node : top_list) {
1797 node->adj_expr = nullptr;
1798 }
1799
1800 return grad;
1801}
1802
1803} // namespace detail
1804
1805extern template class EXPORT_TEMPLATE_DECLARE(SLEIPNIR_DLLEXPORT)
1806VariableMatrix<double>;
1807
1808extern template SLEIPNIR_DLLEXPORT VariableMatrix<double> solve(
1809 const VariableMatrix<double>& A, const VariableMatrix<double>& B);
1810
1811} // namespace slp
Definition sleipnir_base.hpp:9
Represents a sequence of elements in an iterable object.
Definition slice.hpp:25
constexpr int adjust(int length)
Definition slice.hpp:118
Definition variable_block.hpp:27
Definition variable_matrix.hpp:37
const_reverse_iterator crbegin() const
Definition variable_matrix.hpp:1284
VariableMatrix(std::initializer_list< std::initializer_list< Variable< Scalar > > > list)
Definition variable_matrix.hpp:77
const_reverse_iterator crend() const
Definition variable_matrix.hpp:1291
Scalar_ Scalar
Scalar type alias.
Definition variable_matrix.hpp:40
iterator end()
Definition variable_matrix.hpp:1235
const Variable< Scalar > & operator[](int row, int col) const
Definition variable_matrix.hpp:321
VariableMatrix(Variable< Scalar > &&variable)
Definition variable_matrix.hpp:198
const VariableBlock< const VariableMatrix > block(int row_offset, int col_offset, int block_rows, int block_cols) const
Definition variable_matrix.hpp:368
size_t size() const
Definition variable_matrix.hpp:1298
static VariableMatrix< Scalar > zero(int rows, int cols)
Definition variable_matrix.hpp:1321
VariableBlock< VariableMatrix > block(int row_offset, int col_offset, int block_rows, int block_cols)
Definition variable_matrix.hpp:352
VariableMatrix(const std::vector< std::vector< Scalar > > &list)
Definition variable_matrix.hpp:104
VariableMatrix & operator=(ScalarLike auto value)
Definition variable_matrix.hpp:282
Eigen::MatrixX< Scalar > value()
Definition variable_matrix.hpp:993
VariableMatrix & operator-=(const MatrixLike auto &rhs)
Definition variable_matrix.hpp:901
Variable< Scalar > & operator[](int index)
Definition variable_matrix.hpp:331
VariableMatrix & operator=(const Eigen::MatrixBase< Derived > &values)
Definition variable_matrix.hpp:264
friend VariableMatrix< Scalar > operator-(const LHS &lhs, const RHS &rhs)
Definition variable_matrix.hpp:843
VariableMatrix & operator-=(const ScalarLike auto &rhs)
Definition variable_matrix.hpp:917
friend VariableMatrix< Scalar > operator*(const Variable< Scalar > &lhs, const RHS &rhs)
Definition variable_matrix.hpp:609
VariableMatrix & operator*=(const ScalarLike auto &rhs)
Definition variable_matrix.hpp:664
Scalar value(int index)
Definition variable_matrix.hpp:988
VariableMatrix(const VariableBlock< VariableMatrix > &values)
Definition variable_matrix.hpp:206
VariableMatrix(int rows, int cols)
Definition variable_matrix.hpp:55
const_iterator cbegin() const
Definition variable_matrix.hpp:1250
const_iterator begin() const
Definition variable_matrix.hpp:1240
const_iterator cend() const
Definition variable_matrix.hpp:1255
const VariableBlock< const VariableMatrix > operator[](Slice row_slice, Slice col_slice) const
Definition variable_matrix.hpp:396
friend VariableMatrix< Scalar > operator/(const LHS &lhs, const Variable< Scalar > &rhs)
Definition variable_matrix.hpp:680
VariableMatrix(int rows)
Definition variable_matrix.hpp:49
VariableMatrix(std::span< const Variable< Scalar > > values, int rows, int cols)
Definition variable_matrix.hpp:248
VariableBlock< VariableMatrix > col(int col)
Definition variable_matrix.hpp:486
static VariableMatrix< Scalar > identity(int rows)
Definition variable_matrix.hpp:1304
VariableMatrix(const Eigen::MatrixBase< Derived > &values)
Definition variable_matrix.hpp:155
VariableMatrix< Scalar > exp() const
Definition variable_matrix.hpp:1041
const VariableBlock< const VariableMatrix > operator[](Slice row_slice, int row_slice_length, Slice col_slice, int col_slice_length) const
Definition variable_matrix.hpp:432
const VariableBlock< const VariableMatrix > col(int col) const
Definition variable_matrix.hpp:495
VariableMatrix & operator+=(const ScalarLike auto &rhs)
Definition variable_matrix.hpp:825
friend VariableMatrix< Scalar > operator*(const LHS &lhs, const Variable< Scalar > &rhs)
Definition variable_matrix.hpp:574
VariableBlock< VariableMatrix > segment(int offset, int length)
Definition variable_matrix.hpp:444
Scalar value(int row, int col)
Definition variable_matrix.hpp:982
const VariableBlock< const VariableMatrix > row(int row) const
Definition variable_matrix.hpp:477
VariableMatrix()=default
Constructs an empty VariableMatrix.
Eigen::MatrixX< Variable< Scalar > > to_eigen() const
Definition variable_matrix.hpp:1008
VariableMatrix & operator+=(const MatrixLike auto &rhs)
Definition variable_matrix.hpp:809
VariableMatrix(std::span< const Variable< Scalar > > values)
Definition variable_matrix.hpp:233
friend VariableMatrix< Scalar > operator-(const SleipnirMatrixLike< Scalar > auto &lhs)
Definition variable_matrix.hpp:932
static VariableMatrix< Scalar > constant(int rows, int cols, Scalar constant)
Definition variable_matrix.hpp:1352
VariableMatrix< Scalar > T() const
Definition variable_matrix.hpp:955
VariableMatrix< Scalar > cwise_transform(function_ref< Variable< Scalar >(const Variable< Scalar > &x)> unary_op) const
Definition variable_matrix.hpp:1024
friend VariableMatrix< Scalar > operator*(const LHS &lhs, const RHS &rhs)
Definition variable_matrix.hpp:505
const_reverse_iterator rbegin() const
Definition variable_matrix.hpp:1270
VariableMatrix(const Variable< Scalar > &variable)
Definition variable_matrix.hpp:190
reverse_iterator rend()
Definition variable_matrix.hpp:1265
VariableMatrix(const VariableBlock< const VariableMatrix > &values)
Definition variable_matrix.hpp:220
int rows() const
Definition variable_matrix.hpp:970
static VariableMatrix< Scalar > one(int rows, int cols)
Definition variable_matrix.hpp:1336
VariableMatrix(const Eigen::DiagonalBase< Derived > &values)
Definition variable_matrix.hpp:171
VariableBlock< VariableMatrix > operator[](Slice row_slice, Slice col_slice)
Definition variable_matrix.hpp:384
const_reverse_iterator rend() const
Definition variable_matrix.hpp:1277
VariableMatrix & operator*=(const MatrixLike auto &rhs)
Definition variable_matrix.hpp:643
VariableMatrix(detail::empty_t, int rows, int cols)
Definition variable_matrix.hpp:66
const_iterator end() const
Definition variable_matrix.hpp:1245
void set_value(const Eigen::MatrixBase< Derived > &values)
Definition variable_matrix.hpp:295
Variable< Scalar > & operator[](int row, int col)
Definition variable_matrix.hpp:310
iterator begin()
Definition variable_matrix.hpp:1230
const VariableBlock< const VariableMatrix > segment(int offset, int length) const
Definition variable_matrix.hpp:456
reverse_iterator rbegin()
Definition variable_matrix.hpp:1260
VariableBlock< VariableMatrix > row(int row)
Definition variable_matrix.hpp:468
friend VariableMatrix< Scalar > operator+(const LHS &lhs, const RHS &rhs)
Definition variable_matrix.hpp:751
VariableMatrix(const std::vector< std::vector< Variable< Scalar > > > &list)
Definition variable_matrix.hpp:130
VariableBlock< VariableMatrix > operator[](Slice row_slice, int row_slice_length, Slice col_slice, int col_slice_length)
Definition variable_matrix.hpp:414
VariableMatrix & operator/=(const ScalarLike auto &rhs)
Definition variable_matrix.hpp:735
const Variable< Scalar > & operator[](int index) const
Definition variable_matrix.hpp:340
int cols() const
Definition variable_matrix.hpp:975
Definition variable.hpp:56
Definition function_ref.hpp:13
Definition concepts.hpp:18
Definition concepts.hpp:24
Definition concepts.hpp:33
Definition concepts.hpp:38
Type tag used to designate an uninitialized VariableMatrix.
Definition empty.hpp:8