oILAB
Loading...
Searching...
No Matches
SmithDecomposition.h
Go to the documentation of this file.
1/* This file is part of gbLAB.
2 *
3 * gbLAB is distributed without any warranty under the MIT License.
4 */
5
6
7#ifndef gbLAB_SmithDecomposition_h_
8#define gbLAB_SmithDecomposition_h_
9
10#include <utility> // std::pair, std::make_pair
11#include <Eigen/Core>
12
13namespace oILAB {
26template <int N> class SmithDecomposition {
27 typedef long long int IntValueType;
28 typedef Eigen::Matrix<IntValueType, N, N> MatrixNi;
29
30 /**********************************************************************/
31 static IntValueType gcd(const IntValueType &a, const IntValueType &b) {
32 return b > 0 ? gcd(b, a % b) : a;
33 }
34
35 /**********************************************************************/
36 template <typename Derived>
37 static IntValueType gcd(const Eigen::MatrixBase<Derived> &v) {
38 IntValueType g = v(0);
39 for (int n = 1; n < v.size(); ++n) {
40 g = gcd(g, v(n));
41 }
42 return g;
43 }
44
45 /**********************************************************************/
46 static std::pair<int, int> findNonZero(const MatrixNi &A) {
47 for (int i = 0; i < N; ++i) {
48 for (int j = 0; j < N; ++j) {
49 if (A(i, j) != 0) {
50 return std::make_pair(i, j);
51 }
52 }
53 }
54 return std::make_pair(-1, -1);
55 }
56
57 /**********************************************************************/
58 static std::pair<int, int> findNonDivisible(const MatrixNi &A) {
59 for (int i = 1; i < N; ++i) {
60 for (int j = 1; j < N; ++j) {
61 if (A(i, j) % A(0, 0)) // D(i,j) is no divisible by D(0,0)
62 {
63 return std::make_pair(i, j);
64 }
65 }
66 }
67 return std::make_pair(-1, -1);
68 }
69
70 /**********************************************************************/
72 const int &ID) { /* unimodular matrix changing the sign of row/col ID
73 */
74 MatrixNi T(MatrixNi::Identity());
75 T(ID, ID) = -1;
76 U = T * U;
77 X = X * T;
78 D = T * D;
79 }
80
81 /**********************************************************************/
83 const int &ID) { /* unimodular matrix changing the sign of row/col ID
84 */
85 MatrixNi T(MatrixNi::Identity());
86 T(ID, ID) = -1;
87 V = V * T;
88 Y = T * Y;
89 D = D * T;
90 }
91
92 /**********************************************************************/
93 void row_swap(const int &i,
94 const int &j) { /* unimodular matrix swapping rows/cols i and j
95 */
96 MatrixNi T(MatrixNi::Identity());
97 T(i, i) = 0;
98 T(j, j) = 0;
99 T(i, j) = 1;
100 T(j, i) = 1;
101 U = T * U;
102 X = X * T;
103 D = T * D;
104 }
105
106 /**********************************************************************/
107 void col_swap(const int &i,
108 const int &j) { /* unimodular matrix swapping rows/cols i and j
109 */
110 MatrixNi T(MatrixNi::Identity());
111 T(i, i) = 0;
112 T(j, j) = 0;
113 T(i, j) = 1;
114 T(j, i) = 1;
115 V = V * T;
116 Y = T * Y;
117 D = D * T;
118 }
119
120 /**********************************************************************/
122 const int &i, const int &j,
123 const IntValueType
124 &n) { /* unimodular matrix adding n times row (col) j to row (col) i
125 */
126 // note that add applied to colums needs transpose!
127 MatrixNi T(MatrixNi::Identity());
128 T(i, j) = n;
129 U = T * U;
130 D = T * D;
131 T(i, j) = -n;
132 X = X * T;
133 }
134
135 /**********************************************************************/
137 const int &i, const int &j,
138 const IntValueType
139 &n) { /* unimodular matrix adding n times row (col) j to row (col) i
140 */
141 // note that add applied to colums needs transpose!
142 MatrixNi T(MatrixNi::Identity());
143 T(j, i) = n;
144 V = V * T;
145 D = D * T;
146 T(j, i) = -n;
147 Y = T * Y;
148 }
149
150 /**********************************************************************/
152 // Operate on first row so that D(0,j) is divisible by D(0,0)
153 bool didOperate = false;
154 int j = 1;
155 while (j < N) {
156 const IntValueType r = D(0, j) % D(0, 0);
157 const IntValueType q = D(0, j) / D(0, 0);
158 if (r) {
159 col_add_multiply(j, 0, -q);
160 col_swap(0, j);
161 j = 1;
162 didOperate = true;
163 } else {
164 j++;
165 }
166 }
167 return didOperate;
168 }
169
170 /**********************************************************************/
172 // Operate on first row so that D(0,j) is divisible by D(0,0)
173 bool didOperate = false;
174 int j = 1;
175 while (j < N) {
176 const IntValueType r = D(j, 0) % D(0, 0);
177 const IntValueType q = D(j, 0) / D(0, 0);
178 if (r) {
179 row_add_multiply(j, 0, -q);
180 row_swap(0, j);
181 j = 1;
182 didOperate = true;
183 } else {
184 j++;
185 }
186 }
187 return didOperate;
188 }
189
195
196public:
197 /**********************************************************************/
199 : // D=U*A*V, A=X*D*Y
200 /* init */ D(A),
201 /* init */ U(MatrixNi::Identity()),
202 /* init */ V(MatrixNi::Identity()),
203 /* init */ X(MatrixNi::Identity()),
204 /* init */ Y(MatrixNi::Identity()) {
211 std::pair<int, int> rc = std::make_pair(0, 0);
212 while (rc.first >= 0) {
213 std::pair<int, int> ij = findNonZero(D);
214 if (ij.first >= 0) {
215 // Swap row(i) and col (j) to amke D(0,0) non-zero.
216 row_swap(0, ij.first);
217 col_swap(0, ij.second);
218
219 // Operate on first row and col so that all entried are divisible by
220 // D(0,0)
221 bool didOperate = true;
222 while (didOperate) {
223 didOperate = reduceFirstRow() + reduceFirstCol();
224 }
225
226 // If D(0,0) is negative make it positive
227 if (D(0, 0) < 0) {
229 }
230
231 // Subtract suitable multiples of the first row from the other
232 // rows to replace each entry in the first column, except a(0,0), by
233 // zero
234 for (int i = 1; i < N; ++i) {
235 const IntValueType q = D(i, 0) / D(0, 0);
236 row_add_multiply(i, 0, -q);
237 }
238
239 // Subtract suitable multiples of the first column from the other
240 // columns to replace each entry in the first row, except a(0,0), by
241 // zero
242 for (int j = 1; j < N; ++j) {
243 const IntValueType q = D(0, j) / D(0, 0);
244 col_add_multiply(j, 0, -q);
245 }
246
247 // Now D is in the form [D(0,0) 0s;0s D'] where D' is a (N-1)x(N-1)
248 // matrix
249 rc = findNonDivisible(D);
250 if (rc.first > 0) {
251 row_add_multiply(0, rc.first, 1);
252 }
253
254 } else {
255 rc = std::make_pair(-1, -1); // end main while loop immediately
256 }
257 }
258
259 // Recursive iteration
260 const SmithDecomposition<N - 1> sd(D.template block<N - 1, N - 1>(1, 1));
261 D.template block<N - 1, N - 1>(1, 1) = sd.matrixD();
262 U.template block<N - 1, N>(1, 0) =
263 sd.matrixU() * U.template block<N - 1, N>(1, 0);
264 V.template block<1, N - 1>(0, 1) =
265 V.template block<1, N - 1>(0, 1) * sd.matrixV();
266 V.template block<N - 1, N - 1>(1, 1) =
267 V.template block<N - 1, N - 1>(1, 1) * sd.matrixV();
268
269 Y.template block<N - 1, N>(1, 0) =
270 sd.matrixY() * Y.template block<N - 1, N>(1, 0);
271 X.template block<1, N - 1>(0, 1) =
272 X.template block<1, N - 1>(0, 1) * sd.matrixX();
273 X.template block<N - 1, N - 1>(1, 1) =
274 X.template block<N - 1, N - 1>(1, 1) * sd.matrixX();
275
276 // Find a non-zero value of A and make it D(0,0)
277
278 const IntValueType UAVD((U * A * V - D).squaredNorm());
279 const IntValueType XDYA((X * D * Y - A).squaredNorm());
280
281 if (UAVD != 0 || XDYA != 0) {
282 throw std::runtime_error("Smith decomposition failed\n");
283 }
284 }
285
286 /**********************************************************************/
287 const MatrixNi &matrixU() const { return U; }
288
289 /**********************************************************************/
290 const MatrixNi &matrixD() const { return D; }
291
292 /**********************************************************************/
293 const MatrixNi &matrixV() const { return V; }
294
295 /**********************************************************************/
296 const MatrixNi &matrixX() const { return X; }
297
298 /**********************************************************************/
299 const MatrixNi &matrixY() const { return Y; }
300 };
301
302 /**************************************************************************/
303 /**************************************************************************/
304 template <>
306 {
307 typedef long long int IntValueType;
308 typedef Eigen::Matrix<IntValueType,1,1> MatrixNi;
309
310 const MatrixNi D;
311 const MatrixNi U;
312 const MatrixNi V;
313 const MatrixNi X;
314 const MatrixNi Y;
315
316 public:
317
318 /**********************************************************************/
320 /* init */ D(A),
321 /* init */ U(MatrixNi::Identity()),
322 /* init */ V(MatrixNi::Identity()),
323 /* init */ X(MatrixNi::Identity()),
324 /* init */ Y(MatrixNi::Identity())
325
326 {
327 }
328 /**********************************************************************/
329 const MatrixNi& matrixU() const
330 {
331 return U;
332 }
333
334 /**********************************************************************/
335 const MatrixNi& matrixD() const
336 {
337 return D;
338 }
339
340 /**********************************************************************/
341 const MatrixNi& matrixV() const
342 {
343 return V;
344 }
345
346 /**********************************************************************/
347 const MatrixNi& matrixX() const
348 {
349 return X;
350 }
351
352 /**********************************************************************/
353 const MatrixNi& matrixY() const
354 {
355 return Y;
356 }
357 };
372 } // namespace oILAB
373#endif
const MatrixNi & matrixV() const
const MatrixNi & matrixU() const
const MatrixNi & matrixY() const
const MatrixNi & matrixX() const
const MatrixNi & matrixD() const
Eigen::Matrix< IntValueType, 1, 1 > MatrixNi
static IntValueType gcd(const Eigen::MatrixBase< Derived > &v)
void col_swap(const int &i, const int &j)
Eigen::Matrix< IntValueType, N, N > MatrixNi
void col_add_multiply(const int &i, const int &j, const IntValueType &n)
const MatrixNi & matrixD() const
void row_signChange(const int &ID)
void row_add_multiply(const int &i, const int &j, const IntValueType &n)
void row_swap(const int &i, const int &j)
SmithDecomposition(const MatrixNi &A)
const MatrixNi & matrixX() const
const MatrixNi & matrixU() const
static IntValueType gcd(const IntValueType &a, const IntValueType &b)
void col_signChange(const int &ID)
const MatrixNi & matrixV() const
const MatrixNi & matrixY() const
static std::pair< int, int > findNonDivisible(const MatrixNi &A)
static std::pair< int, int > findNonZero(const MatrixNi &A)