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