DGtal 2.1.0
Loading...
Searching...
No Matches
IntegerMatrixFunctions.ih
1/**
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 **/
16
17/**
18 * @file IntegerMatrixFunctions.ih
19 * @author Jacques-Olivier Lachaud (\c jacques-olivier.lachaud@univ-savoie.fr )
20 * Laboratory of Mathematics (CNRS, UMR 5127), University of Savoie, France
21 *
22 * @date 2025/09/01
23 *
24 * Implementation of inline methods defined in IntegerMatrixFunctions.h
25 *
26 * This file is part of the DGtal library.
27 */
28
29
30//////////////////////////////////////////////////////////////////////////////
31//////////////////////////////////////////////////////////////////////////////
32
33namespace DGtal {
34 namespace functions {
35
36 template <typename TComponent>
37 bool
38 equals( const std::vector<TComponent> &a, const std::vector<TComponent> &b )
39 {
40 ASSERT( a.size() == b.size() );
41 for ( std::size_t i = 0; i < a.size(); i++ )
42 if ( a[ i ] != b[ i ] ) return false;
43 return true;
44 }
45
46 template <typename T, typename U >
47 typename DGtal::ArithmeticConversionTraits<T,U>::type
48 dotProduct( const std::vector<T>& a, const std::vector<U>& b )
49 {
50 typename DGtal::ArithmeticConversionTraits<T,U>::type s = 0;
51 for ( size_t i = 0; i < a.size(); i++ ) s += a[i] * b[i];
52 return s;
53 }
54
55 double
56 dotProduct( const std::vector<BigInteger>& a, const std::vector<double>& b )
57 {
58 double s = 0;
59 for ( size_t i = 0; i < a.size(); i++ )
60 s += NumberTraits<BigInteger>::castToDouble( a[i] ) * b[i];
61 return s;
62 }
63
64 double
65 dotProduct( const std::vector<double>& a, const std::vector<BigInteger>& b )
66 {
67 double s = 0;
68 for ( size_t i = 0; i < a.size(); i++ )
69 s += a[ i ] * NumberTraits<BigInteger>::castToDouble( b[i] );
70 return s;
71 }
72
73 template <typename T, typename U >
74 std::vector< typename DGtal::ArithmeticConversionTraits<T,U>::type >
75 crossProduct( const std::vector<T>& a, const std::vector<U>& b )
76 {
77 ASSERT( a.size() == 3 && b.size() == 3 );
78 typedef typename DGtal::ArithmeticConversionTraits<T,U>::type W;
79 std::vector< W > R( 3 );
80 R[ 0 ] = a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ];
81 R[ 1 ] = a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ];
82 R[ 2 ] = a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ];
83 return R;
84 }
85
86 template <typename T, typename U, typename Op2 >
87 std::vector< typename DGtal::ArithmeticConversionTraits<T,U>::type >
88 apply( const std::vector<T>& a, const std::vector<U>& b,
89 Op2 op2 )
90 {
91 ASSERT( a.size() == b.size() );
92 std::vector< typename DGtal::ArithmeticConversionTraits<T,U>::type > R( a.size() );
93 for ( std::size_t i = 0; i < R.size(); i++ )
94 R[ i ] = op2( a[ i ], b[ i ] );
95 return R;
96 }
97
98
99 template <typename T>
100 T
101 squaredNormL2( const std::vector<T>& a )
102 {
103 T n = 0;
104 for ( size_t i = 0; i < a.size(); i++ )
105 n += a[ i ] * a[ i ];
106 return n;
107 }
108
109 template <typename T>
110 T
111 normL1( const std::vector<T>& a )
112 {
113 T n = 0;
114 for ( size_t i = 0; i < a.size(); i++ )
115 {
116 T c = a[ i ];
117 n += ( c >= 0 ) ? c : -c;
118 }
119 return n;
120 }
121
122 template <typename T>
123 T
124 normLoo( const std::vector<T>& a )
125 {
126 T n = 0;
127 for ( size_t i = 0; i < a.size(); i++ )
128 {
129 T c = ( a[ i ] >= 0 ) ? a[ i ] : -a[ i ];
130 if ( c > n ) n = c;
131 }
132 return n;
133 }
134
135 template <typename TOutput, typename T>
136 void
137 getSquaredNormL2( TOutput& n, const std::vector<T>& a )
138 {
139 n = 0;
140 for ( size_t i = 0; i < a.size(); i++ )
141 {
142 TOutput c = TOutput( a[ i ] );
143 n += c * c;
144 }
145 }
146
147 template <typename TOutput,
148 DGtal::Dimension dim,
149 typename TEuclideanRing,
150 typename TContainer>
151 void
152 getSquaredNormL2( TOutput& n,
153 const DGtal::PointVector<dim,TEuclideanRing,TContainer>& a )
154 {
155 n = 0;
156 for ( size_t i = 0; i < a.size(); i++ )
157 {
158 TOutput c = TOutput( a[ i ] );
159 n += c * c;
160 }
161 }
162
163 } // namespace detail {
164} // namespace DGtal {
165
166///////////////////////////////////////////////////////////////////////////////
167// IMPLEMENTATION of inline methods.
168///////////////////////////////////////////////////////////////////////////////
169
170///////////////////////////////////////////////////////////////////////////////
171// Implementation of inline functions //
172
173
174//-----------------------------------------------------------------------------
175template <typename TComponent, DGtal::Dimension TN,
176 typename TInternalNumber>
177void
178DGtal::functions::getDeterminantBareiss
179( TInternalNumber& result,
180 const SimpleMatrix<TComponent, TN, TN>& matrix )
181{
182 result = TInternalNumber( 1 );
183 if constexpr (TN == 0) return;
184 const TInternalNumber zero = TInternalNumber( 0 );
185 TInternalNumber prev = TInternalNumber( 1 );
186 typedef std::array< TInternalNumber, TN > TRow;
187 typedef std::array< TRow, TN > TMatrix;
188 TMatrix M;
189 for ( Dimension i = 0; i < TN; ++i )
190 for ( Dimension j = 0; j < TN; ++j )
191 M[ i ][ j ] = TInternalNumber( matrix( i, j ) );
192 bool change_sign = false;
193 for ( Dimension k = 0; k < TN; ++k )
194 {
195 // pivot
196 Dimension piv = k;
197 while ( ( piv < TN ) && ( M[ piv ][ k ] == zero ) ) ++piv;
198 if ( piv == TN ) { result = zero; return; }
199 if ( piv != k )
200 { // swaping 2 rows => change determinant sign
201 M[ piv ].swap( M[ k ] );
202 change_sign = ! change_sign;
203 }
204 TRow& M_k = M[ k ];
205 TInternalNumber pivot = M_k[ k ];
206 for ( Dimension i = k+1; i < TN; ++i )
207 {
208 TRow& M_i = M[ i ];
209 for ( Dimension j = k+1; j < TN; ++j )
210 {
211 // Bareiss: (a_ij*a_kk - a_ik*a_kj)/prev
212 M_i[ j ] = M_i[ j ] * pivot - M_i[ k ] * M_k[ j ];
213 if ( k != 0 ) M_i[ j ] /= prev;
214 }
215 }
216 prev = pivot;
217 // Optionnally one could zero the column k below the diagonal
218 // (not necessary for the determinant).
219 // for (int i = k+1; i < TN; ++i) M[i][k] = 0;
220 }
221 result = change_sign ? -M[ TN-1 ][ TN-1 ] : M[ TN-1 ][ TN-1 ];
222}
223
224//-----------------------------------------------------------------------------
225template <typename TComponent,
226 typename TInternalNumber>
227void
228DGtal::functions::getDeterminantBareiss
229( TInternalNumber& result,
230 const std::vector< std::vector< TComponent > >& matrix )
231{
232 result = TInternalNumber( 1 );
233 const Dimension n = matrix.size();
234 if (n == 0) return;
235 const TInternalNumber zero = TInternalNumber( 0 );
236 TInternalNumber prev = TInternalNumber( 1 );
237 typedef std::vector< TInternalNumber > TRow;
238 typedef std::vector< TRow > TMatrix;
239 TMatrix M( n );
240 for ( Dimension i = 0; i < n; ++i )
241 {
242 if ( matrix[ i ].size() != n )
243 {
244 trace.error() << "[functions::getDeterminantBareiss] Input matrix is not square: "
245 << n << " rows, while row " << i << " has " << matrix[ i ].size()
246 << "columns. Returning det=1." << "\n";
247 return;
248 }
249 M[ i ] = TRow( n );
250 for ( Dimension j = 0; j < n; ++j )
251 M[ i ][ j ] = TInternalNumber( matrix[ i ][ j ] );
252 }
253 bool change_sign = false;
254 for ( Dimension k = 0; k < n; ++k )
255 {
256 // pivot
257 Dimension piv = k;
258 while ( ( piv < n ) && ( M[ piv ][ k ] == zero ) ) ++piv;
259 if ( piv == n ) { result = zero; return; }
260 if ( piv != k )
261 { // swaping 2 rows => change determinant sign
262 M[ piv ].swap( M[ k ] );
263 change_sign = ! change_sign;
264 }
265 TRow& M_k = M[ k ];
266 TInternalNumber pivot = M_k[ k ];
267 for ( Dimension i = k+1; i < n; ++i )
268 {
269 TRow& M_i = M[ i ];
270 for ( Dimension j = k+1; j < n; ++j )
271 {
272 // Bareiss: (a_ij*a_kk - a_ik*a_kj)/prev
273 M_i[ j ] = M_i[ j ] * pivot - M_i[ k ] * M_k[ j ];
274 if ( k != 0 ) M_i[ j ] /= prev;
275 }
276 }
277 prev = pivot;
278 // Optionnally one could zero the column k below the diagonal
279 // (not necessary for the determinant).
280 // for (int i = k+1; i < n; ++i) M[i][k] = 0;
281 }
282 result = change_sign ? -M[ n-1 ][ n-1 ] : M[ n-1 ][ n-1 ];
283}
284
285
286//-----------------------------------------------------------------------------
287template <typename TComponent>
288std::vector< std::vector< TComponent > >
289DGtal::functions::matrixAsVectorVector
290( std::size_t m, std::size_t n,
291 const std::vector< TComponent >& c )
292{
293 std::vector< std::vector< TComponent > > R( m );
294 std::size_t k = 0;
295 for ( std::size_t i = 0; i < m; i++ )
296 {
297 R[ i ] = std::vector< TComponent >( n );
298 for ( std::size_t j = 0; j < n; j++ )
299 R[ i ][ j ] = ( k < c.size() ) ? c[ k++ ] : TComponent( 0 );
300 }
301 return R;
302}
303
304//-----------------------------------------------------------------------------
305template <typename TComponent, DGtal::Dimension TM, DGtal::Dimension TN>
306std::vector< std::vector< TComponent > >
307DGtal::functions::matrixAsVectorVector
308( const SimpleMatrix<TComponent, TM, TN>& M )
309{
310 std::vector< std::vector< TComponent > > R( TM );
311 for ( std::size_t i = 0; i < TM; i++ )
312 {
313 R[ i ] = std::vector< TComponent >( TN );
314 for ( std::size_t j = 0; j < TN; j++ )
315 R[ i ][ j ] = M( i, j );
316 }
317 return R;
318}
319
320//-----------------------------------------------------------------------------
321template <typename TComponent, typename TDouble >
322std::vector< std::vector< TComponent > >
323DGtal::functions::computeLLLBasis
324( const std::vector< std::vector< TComponent > >& B,
325 TDouble delta )
326{
327 std::vector< std::vector< TComponent > > R( B );
328 functions::reduceBasisWithLLL( R, delta );
329 return R;
330}
331
332//-----------------------------------------------------------------------------
333template <typename TComponent, typename TDouble >
334void
335DGtal::functions::reduceBasisWithLLL
336( std::vector< std::vector< TComponent > >& B,
337 TDouble delta )
338{
339 typedef TComponent Integer;
340 typedef TDouble Double;
341 typedef std::vector< Double > Doubles;
342 const int64_t n = B.size(); // number of input row vectors
343 const std::size_t d = B[0].size(); // dimension of row vectors (and embedding space)
344 std::vector<Doubles> Bo(n, Doubles(d)); // orthogonalized basis
345 Doubles Bo_norm(n); // squared 2-norm of each row vector
346 std::vector<Doubles> mu(n, Doubles(n)); // projection coefficients on orth. basis
347 bool sign = true;
348 int64_t k = 1; // current vector
349 while ( k < n )
350 {
351 // Apply Gram–Schmidt until row k included.
352 for ( int64_t i = 0; i <= k; i++ )
353 {
354 Doubles v( B[i].cbegin(), B[i].cend() ); // cast row i to doubles.
355 for ( int64_t j = 0; j < i; j++ )
356 {
357 mu[i][j] = functions::dotProduct( B[i], Bo[j] ) / Bo_norm[j];
358 for ( std::size_t l = 0; l < d; l++ )
359 v[l] -= mu[i][j] * Bo[j][l];
360 }
361 Bo[i].swap( v ); // faster than `Bo[i] = v;`
362 functions::getSquaredNormL2( Bo_norm[i], Bo[i] );
363 }
364 // réduction de taille
365 for ( int64_t j = k - 1; j >= 0; j-- ) {
366 Double mu_kj = functions::dotProduct( B[k], Bo[j] ) / Bo_norm[j];
367 Double q = round( mu_kj ); //round( mu[k][j] );
368 if ( q != 0.0 )
369 {
370 const Integer iq = Integer( q );
371 for ( std::size_t l = 0; l < d; l++ )
372 B[k][l] -= iq * B[j][l];
373 }
374 }
375 // Reperform Gram–Schmidt for k after reduction
376 {
377 Doubles v( B[k].cbegin(), B[k].cend());
378 for ( int64_t j = 0; j < k; j++ )
379 {
380 mu[k][j] = functions::dotProduct( B[k], Bo[j] ) / Bo_norm[j];
381 for ( std::size_t l = 0; l < d; l++ )
382 v[l] -= mu[k][j] * Bo[j][l];
383 }
384 Bo[k].swap( v ); // faster than `Bo[k] = v;`
385 functions::getSquaredNormL2( Bo_norm[k], Bo[k] );
386 }
387
388 // Lovász test
389 if ( Bo_norm[k] >= ( (delta - mu[k][k-1]*mu[k][k-1]) * Bo_norm[k-1] ) )
390 k++;
391 else
392 {
393 swap( B[k], B[k-1] );
394 k = std::max( k - 1, int64_t(1) );
395 sign = ! sign;
396 }
397 }
398 if ( ! sign )
399 swap( B[n-2], B[n-1] );
400}
401
402//-----------------------------------------------------------------------------
403template <typename TComponent>
404TComponent
405DGtal::functions::makePrimitive( std::vector< TComponent >& N )
406{
407 typedef IntegerComputer<TComponent> Comp;
408 TComponent g = 0;
409 for ( Dimension k = 0; k < N.size(); k++ )
410 g = Comp::staticGcd( g, N[ k ] );
411 if ( g != 0 )
412 for ( Dimension k = 0; k < N.size(); k++ )
413 N[ k ] /= g;
414 return g;
415}
416
417//-----------------------------------------------------------------------------
418template <typename TComponent>
419void
420DGtal::functions::negate( std::vector<TComponent> &V )
421{
422 for ( auto& v :V ) v = -v;
423}
424
425//-----------------------------------------------------------------------------
426template <typename TComponent>
427TComponent
428DGtal::functions::extendedGcd
429( TComponent& x, TComponent& y, TComponent a, TComponent b )
430{
431 if ( b == 0 ){ x = 1; y = 0; return a; }
432 TComponent x1,y1;
433 TComponent g = functions::extendedGcd( x1, y1, b, a%b );
434 x = y1;
435 y = x1 - (a/b)*y1;
436 return g;
437}
438
439//-----------------------------------------------------------------------------
440template <typename TComponent>
441TComponent
442DGtal::functions::extendedGcd
443( std::vector<TComponent> &coeffs, const std::vector<TComponent> &A )
444{
445 auto n = A.size();
446 coeffs.assign(n, 0);
447 TComponent g = A[0];
448 coeffs[0] = 1;
449 for ( Dimension i = 1; i < n; i++)
450 {
451 TComponent x, y;
452 TComponent g2 = functions::extendedGcd( x, y, g, A[i] );
453 for ( Dimension j = 0; j < i; j++) coeffs[j] *= x;
454 coeffs[i] = y;
455 g = g2;
456 }
457 return g;
458}
459
460//-----------------------------------------------------------------------------
461template <typename TComponent>
462std::vector< std::vector< TComponent > >
463DGtal::functions::computeOrthogonalLattice( std::vector< TComponent > N )
464{
465 std::vector< std::vector< TComponent > > R;
466 const std::size_t n = N.size();
467 if ( n <= 1 ) return R;
468 std::vector< TComponent > V = N;
469 std::size_t m = 0;
470 while ( m+1 < n )
471 {
472 makePrimitive( V );
473 if ( n-m == 2 )
474 { // last orthogonal vector
475 std::vector< TComponent > B( N.size(), 0 );
476 B[ m ] = V[ m+1 ];
477 B[ m+1 ] = -V[ m ];
478 R.push_back( B );
479 m = n;
480 break;
481 }
482 else
483 { // general case
484 TComponent Vm = V[ m ];
485 V[ m ] = 0;
486 std::vector< TComponent > B( n );
487 TComponent gp = extendedGcd( B, V );
488 if ( gp == 0 ) break;
489 // Build orthogonal vector
490 for ( Dimension k = m+1; k < n; k++ ) B[ k ] *= -Vm;
491 B[ m ] = gp;
492 R.push_back( B );
493 }
494 m += 1;
495 } // while ( m+1 < n )
496 // complete basis
497 std::vector< TComponent > B( N.size(), 0 );
498 for( ; m+1 < n; m++ )
499 {
500 B[ m ] = 0;
501 B[ m+1 ] = 1;
502 R.push_back( B );
503 }
504 return R;
505}
506
507//-----------------------------------------------------------------------------
508template <typename TComponent>
509bool
510DGtal::functions::shortenVectors( std::vector< TComponent >& u,
511 std::vector< TComponent >& v )
512{
513 auto is_shorter = []
514 ( const std::vector< TComponent >& a, const std::vector< TComponent >& b ) -> bool
515 {
516 return dotProduct( a, a ) < dotProduct( b, b );
517 };
518 bool u_shorter_v = is_shorter( u, v );
519 auto min_uv = u_shorter_v ? u : v;
520 auto max_uv = u_shorter_v ? v : u;
521 auto u_plus_v = functions::apply( u, v, std::plus() );
522 auto u_minus_v= functions::apply( u, v, std::minus() );
523 if ( is_shorter( u_plus_v, max_uv ) )
524 {
525 u = min_uv;
526 v = u_plus_v;
527 return true;
528 }
529 else if ( is_shorter( u_minus_v, max_uv ) )
530 {
531 u = min_uv;
532 v = u_minus_v;
533 return true;
534 }
535 return false;
536}
537
538//-----------------------------------------------------------------------------
539template <typename TComponent>
540std::size_t
541DGtal::functions::shortenBasis( std::vector< std::vector< TComponent > >& B )
542{
543 std::size_t total = 0;
544 if ( B.size() <= 1 ) return total;
545 while ( true )
546 {
547 std::size_t nb_shortened = 0;
548 for ( std::size_t i = 0; i < B.size(); i++ )
549 for ( std::size_t j = i+1; j < B.size(); j++ )
550 nb_shortened += functions::shortenVectors( B[ i ], B[ j ] ) ? 1 : 0;
551 TComponent m = 0;
552 for ( std::size_t i = 0; i < B.size(); i++ )
553 m = std::max( m, dotProduct( B[ i ], B[ i ] ) );
554 total += nb_shortened;
555 if ( nb_shortened == 0 ) break;
556 }
557 return total;
558}
559