ZonoOpt v2.0.1
Loading...
Searching...
No Matches
SparseMatrixUtilities.hpp
Go to the documentation of this file.
1#ifndef ZONOOPT_SPARSEMATRIXUTILITIES_HPP_
2#define ZONOOPT_SPARSEMATRIXUTILITIES_HPP_
3
15#include <Eigen/Sparse>
16#include <vector>
17
18namespace ZonoOpt::detail
19{
20
21 template <typename T>
22 Eigen::SparseMatrix<T> hcat(const Eigen::SparseMatrix<T> &A, const Eigen::SparseMatrix<T> &B)
23 {
24 if (A.rows() != B.rows())
25 {
26 throw std::invalid_argument("hcat: number of rows must match.");
27 }
28
29 Eigen::SparseMatrix<T> C(A.rows(), A.cols() + B.cols());
30 std::vector<Eigen::Triplet<T>> tripvec;
31 tripvec.reserve(A.nonZeros() + B.nonZeros());
32
33 for (int k=0; k<A.outerSize(); ++k)
34 {
35 for (typename Eigen::SparseMatrix<T>::InnerIterator it(A, k); it; ++it)
36 {
37 tripvec.emplace_back(static_cast<int>(it.row()), static_cast<int>(it.col()), it.value());
38 }
39 }
40
41 for (int k=0; k<B.outerSize(); ++k)
42 {
43 for (typename Eigen::SparseMatrix<T>::InnerIterator it(B, k); it; ++it)
44 {
45 tripvec.emplace_back(static_cast<int>(it.row()), static_cast<int>(it.col()+A.cols()), it.value());
46 }
47 }
48
49 C.setFromTriplets(tripvec.begin(), tripvec.end());
50 return C;
51 }
52
53
54 template<typename T>
55 Eigen::SparseMatrix<T> vcat(const Eigen::SparseMatrix<T> &A, const Eigen::SparseMatrix<T> &B)
56 {
57 if (A.cols() != B.cols())
58 {
59 throw std::invalid_argument("vcat: number of columns must match.");
60 }
61
62 Eigen::SparseMatrix<T> C(A.rows() + B.rows(), A.cols());
63 std::vector<Eigen::Triplet<T>> tripvec;
64 tripvec.reserve(A.nonZeros() + B.nonZeros());
65
66 for (int k=0; k<A.outerSize(); ++k)
67 {
68 for (typename Eigen::SparseMatrix<T>::InnerIterator it(A, k); it; ++it)
69 {
70 tripvec.emplace_back(static_cast<int>(it.row()), static_cast<int>(it.col()), it.value());
71 }
72 }
73
74 for (int k=0; k<B.outerSize(); ++k)
75 {
76 for (typename Eigen::SparseMatrix<T>::InnerIterator it(B, k); it; ++it)
77 {
78 tripvec.emplace_back(static_cast<int>(it.row()+A.rows()), static_cast<int>(it.col()), it.value());
79 }
80 }
81
82 C.setFromTriplets(tripvec.begin(), tripvec.end());
83 return C;
84 }
85
86 // get triplets for matrix
87 template <typename T>
88 void get_triplets_offset(const Eigen::SparseMatrix<T> &mat, std::vector<Eigen::Triplet<T>> &triplets,
89 const int i_offset, const int j_offset)
90 {
91 // check validity
92 if (i_offset < 0 || j_offset < 0)
93 {
94 throw std::invalid_argument("get_triplets_offset: offsets must be non-negative.");
95 }
96
97 // get triplets
98 for (int k=0; k<mat.outerSize(); ++k)
99 {
100 for (typename Eigen::SparseMatrix<T>::InnerIterator it(mat, k); it; ++it)
101 {
102 triplets.emplace_back(static_cast<int>(it.row() + i_offset), static_cast<int>(it.col() + j_offset), it.value());
103 }
104 }
105 }
106
107 // remove redundant constraints, A*x = b
108 template <typename T>
109 void remove_redundant_constraints(Eigen::SparseMatrix<T>& A, Eigen::Vector<T,-1>& b)
110 {
111 // check for empty input matrix
112 if (A.rows() == 0 || A.cols() == 0)
113 {
114 A.resize(0, 0);
115 b.resize(0);
116 return;
117 }
118
119 // transpose
120 Eigen::SparseMatrix<T> At = A.transpose();
121
122 // compute QR decomposition
123 Eigen::SparseQR<Eigen::SparseMatrix<T>, Eigen::COLAMDOrdering<int>> qr;
124 qr.analyzePattern(At);
125 qr.factorize(At);
126
127 // get the permutation matrix and its indices
128 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> P = qr.colsPermutation();
129 Eigen::VectorXi P_indices = P.indices();
130
131 // QR solver puts linearly dependent rows at end
132 std::vector<Eigen::Triplet<T>> tripvec;
133 for (int i=0; i<qr.rank(); i++)
134 {
135 tripvec.emplace_back(P_indices(i), i, static_cast<T>(1.0));
136 }
137
138 Eigen::SparseMatrix<T> P_full (At.cols(), qr.rank());
139 P_full.setFromTriplets(tripvec.begin(), tripvec.end());
140
141 // remove redundant constraints
142 A = (At * P_full).transpose();
143 b = (b.transpose() * P_full).transpose();
144 }
145
146 // get triplets for row of row-major sparse matrix
147 template <typename T>
148 std::vector<Eigen::Triplet<T>> get_triplets_row(const Eigen::SparseMatrix<T, Eigen::RowMajor> &mat, int row)
149 {
150 std::vector<Eigen::Triplet<T>> triplets;
151 if (row < 0 || row >= mat.rows())
152 {
153 throw std::out_of_range("get_triplets_row: row index out of range.");
154 }
155
156 for (typename Eigen::SparseMatrix<T, Eigen::RowMajor>::InnerIterator it(mat, row); it; ++it)
157 {
158 triplets.emplace_back(row, static_cast<int>(it.col()), it.value());
159 }
160
161 return triplets;
162 }
163
164 template <typename T>
165 struct QREqConsData
166 {
167 Eigen::SparseMatrix<T> R;
168 Eigen::Vector<T, -1> b_tilde;
169 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> P;
170 };
171
172 template <typename T>
173 void QREqCons(const Eigen::SparseMatrix<T>& A, const Eigen::Vector<T, -1>& b, QREqConsData<T>& data)
174 {
175 Eigen::SparseQR<Eigen::SparseMatrix<T>, Eigen::COLAMDOrdering<int>> qr;
176 qr.compute(A);
177 data.R = qr.matrixR();
178 data.b_tilde = qr.matrixQ().transpose()*b;
179 data.P = qr.colsPermutation();
180 }
181
182} // namespace ZonoOpt::detail
183// end namespace ZonoOpt
184
185#endif