Macaulay2 Engine
Loading...
Searching...
No Matches
dmat-lu.hpp
Go to the documentation of this file.
1// Copyright 2013 Michael E. Stillman
2
3#ifndef _dmat_lu_hpp_
4#define _dmat_lu_hpp_
5
40
41#include "dmat.hpp"
42#include "mat-elem-ops.hpp"
43#include "mat-util.hpp"
44
45#include "dmat-lu-inplace.hpp"
46
47template <class RingType>
48class DMatLinAlg;
49
51#include "dmat-lu-zzp-flint.hpp"
52#include "dmat-lu-qq.hpp"
53
55
56template <class RingType>
58{
59 public:
60 typedef typename RingType::ElementType ElementType;
62
63 private:
65
66 public:
68 DMatLinAlg(const Mat& A);
69
73 bool solve(const Mat& B, Mat& X);
74
79 bool solveInvertible(const Mat& B, Mat& X);
80
83 bool inverse(Mat& X);
84
87
91 bool matrixPLU(std::vector<size_t>& P, Mat& L, Mat& U);
92
95 size_t kernel(Mat& X);
96
98 size_t rank();
99
103 void columnRankProfile(std::vector<size_t>& profile);
104
105 private:
106 // Private functions below this line
107 const RingType& ring() const { return mLUObject.ring(); }
108 void setUpperLower(const Mat& LU, Mat& lower, Mat& upper);
109
111 {
112 buffer o;
113 displayMat(o, mLUObject.LU());
114 std::cout << o.str() << std::endl;
115 }
116
117 void debug_out_list(ElementType* x, size_t len)
118 {
119 buffer o;
120 o << "[ ";
121 for (size_t i = 0; i < len; i++)
122 {
123 ring().elem_text_out(o, x[i], true, false);
124 o << " ";
125 }
126 o << "]" << newline;
127 std::cout << o.str();
128 }
129};
130
131template <class RingType>
135
136#if 0
137template <class RingType>
138size_t DMatLinAlg<RingType>::findPivot(size_t row, size_t col)
139{
140 // Look at elements A[row,col], A[row+1,col], ..., A[nrows-1, col]
141 // Return the index r s.y. abs(A[r,col]) is maximum over all of these
142
143 auto A = mLU.rowMajorArray();
144 A += row * mLU.numColumns() + col;
145 for (size_t i=row; i<mLU.numRows(); i++)
146 {
147 if (mLU.ring().is_zero(*A))
148 A += mLU.numColumns();
149 else
150 return i;
151 }
152 return static_cast<size_t>(-1);
153}
154#endif
155#if 0
156template <class RingType>
158{
159 //TODO: this is likely all wrong, and needs testing
160
161 if (mIsDone) return;
162
163 ElementType tmp;
164 mLU.ring().init(tmp);
165 size_t col = 0;
166 size_t nrows = mLU.numRows();
167 size_t ncols = mLU.numColumns();
168
169 auto array = mLU.rowMajorArray();
170
171 // The following 3 arrays are used to point to the places we need: A is the location we are working on
172 // B is the part from 'L', C is the part from 'U' (and the original matrix)
173 auto A = array; // mLU[row, col+1]
174 auto B = array; // mLU[row,0]
175 auto C = array; // mLU[0,col]
176
177 for (size_t row = 0; row < nrows; row++)
178 {
179 // First, find a pivot, if any, in this column (from row row..nrows-1)
180 // Find the element with largest absolute value:
181
182 size_t k = findPivot(row,col);
183 if (k != row) MatElementaryOps<Mat>::interchange_rows(mLU, k, row);
184
185 const ElementType& pivot = *A;
186 // Now set the elements in the pivot row at columns col+1..ncols-1
187
188 auto B1 = B;
189 auto C1 = C;
190 for (size_t c = col+1; c < ncols; c++)
191 {
192 for (size_t i = 0; i<row; i++)
193 {
194 mLU.ring().mult(tmp, *B1++, *C1);
195 mLU.ring().subtract(*A, *A, tmp);
196 C1 += ncols;
197 }
198 }
199
200 B1 = B;
201 C1 = C;
202 // Now set the elements in L in column 'row'
203 for (size_t r = row+1; r < nrows; r++)
204 {
205 for (size_t i = 0; i<row; i++)
206 {
207 mLU.ring().mult(tmp, *B1++, *C1);
208 mLU.ring().subtract(*A, *A, tmp);
209 C += ncols;
210 }
211 mLU.ring().divide(*A, *A, pivot);
212 *A /= pivot;
213 A += ncols;
214 }
215
216 // Now place A,B,C into the right location
217 B += ncols;
218 C++;
219 A += ncols;
220 }
221
222 mLU.ring().clear(tmp);
223}
224#endif
225#if 0
226template <class RingType>
227void DMatLinAlg<RingType>::setUpperLower(const Mat& LU, Mat& lower, Mat& upper)
228{
229 // NOT TESTED YET!!
230 // MAJOR ASSUMPTION: the matrices lower, upper, and mLU are stored in row major order!
231
232 size_t min = std::min(LU.numRows(), LU.numColumns());
233 lower.resize(LU.numRows(), min);
234 upper.resize(min, LU.numColumns());
235
236 // At this point, lower and upper should be zero matrices.
237 assert(MatrixOps::isZero(lower));
238 assert(MatrixOps::isZero(upper));
239
240 auto LUraw = LU.rowMajorArray();
241 auto L = lower.rowMajorArray();
242 auto U = upper.rowMajorArray();
243
244 for (size_t c=0; c<upper.numColumns(); c++)
245 {
246 auto U1 = U;
247 for (size_t r=0; r<=c; r++)
248 {
249 if (r >= upper.numRows()) break;
250 upper.ring().set(*U1, *LUraw++);
251 U1 += upper.numColumns();
252 }
253 U++; // change to next column
254
255 if (c < lower.numColumns())
256 {
257 lower.ring().set_from_long(*L, 1); // diagonal entry of L should be 1
258 L += lower.numColumns(); // pointing to entry right below diagonal
259 auto L1 = L; // will increment by lower.numRows() each loop here
260 for (size_t r=c+1; r<lower.numRows(); r++)
261 {
262 lower.ring().set(*L1, *LUraw++);
263 L1 += lower.numColumns(); // to place next entry.
264 }
265 L++; // change to next column
266 }
267 }
268}
269#endif
270template <class RingType>
271void DMatLinAlg<RingType>::setUpperLower(const Mat& LU, Mat& lower, Mat& upper)
272{
273 size_t min = std::min(LU.numRows(), LU.numColumns());
274 lower.resize(LU.numRows(), min);
275 upper.resize(min, LU.numColumns());
276
277 // At this point, lower and upper should be zero matrices.
278 assert(MatrixOps::isZero(lower));
279 assert(MatrixOps::isZero(upper));
280
281 for (size_t c = 0; c < LU.numColumns(); c++)
282 {
283 if (c < min) ring().set_from_long(lower.entry(c, c), 1);
284 for (size_t r = 0; r < LU.numRows(); r++)
285 {
286 if (r <= c)
287 ring().set(upper.entry(r, c), LU.entry(r, c));
288 else if (c < lower.numRows())
289 {
290 ring().set(lower.entry(r, c), LU.entry(r, c));
291 }
292 }
293 }
294}
295
296template <class RingType>
297bool DMatLinAlg<RingType>::matrixPLU(std::vector<size_t>& P, Mat& L, Mat& U)
298{
299 const Mat& LU = mLUObject.LUinPlace();
300
301 // std::cout << "calling matrixPLU generic\n";
302 setUpperLower(LU, L, U);
303 P = mLUObject.permutation();
304 return mLUObject.signOfPermutation();
305}
306
307template <class RingType>
309{
310 const Mat& LU = mLUObject.LUinPlace();
311
312 // This is just the product of the diagonal entries of mLU.
313 assert(LU.numRows() == LU.numColumns());
314
315 if (mLUObject.signOfPermutation())
316 ring().set_from_long(result, 1);
317 else
318 ring().set_from_long(result, -1);
319 for (size_t i = 0; i < LU.numRows(); i++)
320 ring().mult(result, result, LU.entry(i, i));
321}
322
323template <class RingType>
324void DMatLinAlg<RingType>::columnRankProfile(std::vector<size_t>& profile)
325{
326 mLUObject.LUinPlace();
327
328 profile = mLUObject.pivotColumns();
329}
330
331template <class RingType>
333{
334 mLUObject.LUinPlace();
335
336 return mLUObject.pivotColumns().size();
337}
338
339template <class RingType>
341{
342 // printf("in dmat-lu: solve\n");
343 const Mat& LU = mLUObject.LUinPlace();
344 // printf("in dmat-lu: after LU solve\n");
345
346 // For each column of B, we solve it separately.
347
348 // Step 1: Take a column, and permute it via mPerm, into b.
349 // Step 2: Solve the lower triangular system Ly=b
350 // If there is no solution, then return false.
351 // Step 3: Solve the upper triangular system Ux = y.
352
353 // Sizes of objects here:
354 // A is m x n
355 // L is m x r
356 // U is r x n. Here, r <= m, and A has rank r.
357 // b is a vector 0..m-1
358 // y is a vector 0..r-1
359 // x is a vector 0..n-1
360
361 // printf("entering DMatLinAlg::solve\n");
362 const std::vector<size_t>& pivotColumns = mLUObject.pivotColumns();
363 const std::vector<size_t>& perm = mLUObject.permutation();
364 size_t rk = pivotColumns.size();
365
366 typename RingType::Element tmp(ring()), tmp2(ring());
367
368 typedef typename RingType::ElementArray ElementArray;
369 ElementArray b(ring(), LU.numRows());
370 ElementArray y(ring(), rk);
371 ElementArray x(ring(), LU.numColumns());
372
373 X.resize(LU.numColumns(), B.numColumns());
374
375 for (size_t col = 0; col < B.numColumns(); col++)
376 {
377 // Step 0: erase x (b will be set below, y is also set when needed).
378
379 for (size_t i = 0; i < LU.numColumns(); i++) ring().set_zero(x[i]);
380
381 // Step 1: set b to be the permuted i-th column of B.
382 for (size_t r = 0; r < B.numRows(); r++)
383 ring().set(b[r], B.entry(perm[r], col));
384
387
388 // Step 2: Solve Ly=b
389 for (size_t i = 0; i < rk; i++)
390 {
391 ring().set(y[i], b[i]);
392 for (size_t j = 0; j < i; j++)
393 {
394 ring().mult(tmp, LU.entry(i, j), y[j]);
395 ring().subtract(y[i], y[i], tmp);
396 }
397 }
398
401
402 // Step 2B: see if the solution is consistent
403 for (size_t i = rk; i < LU.numRows(); i++)
404 {
405 ring().set(tmp, b[i]);
406 for (size_t j = 0; j < rk; j++)
407 {
408 ring().mult(tmp2, LU.entry(i, j), y[j]);
409 ring().subtract(tmp, tmp, tmp2);
410 }
411 if (!ring().is_zero(tmp))
412 {
413 // printf("returning false\n");
414 return false;
415 }
416 }
417
419
420 // Step 3: Solve Ux=y
421 // and place x back into X as col-th column
422 for (long i = rk - 1; i >= 0; --i)
423 {
424 ring().set(x[i], y[i]);
425 for (size_t j = i + 1; j <= rk - 1; j++)
426 {
427 ring().mult(tmp, LU.entry(i, pivotColumns[j]), x[j]);
428 ring().subtract(x[i], x[i], tmp);
429 }
430 ring().divide(x[i], x[i], LU.entry(i, pivotColumns[i]));
431 ring().set(X.entry(pivotColumns[i], col), x[i]);
432
437 }
440
445 }
446
447 return true; // The system seems to have been consistent
448}
449
450#if 0
451template <class RingType>
452bool DMatLinAlg<RingType>::solveInvertible(const Mat& B, Mat& X)
453{
454 // possible TODO: incorporate a faster method if we know matrix is invertible...
455 assert(mLUObject.numRows() == mLUObject.numColumns());
456 assert(mLUObject.numRows() == B.numRows());
457
458 if (rank() < mLUObject.numRows()) return false;
459 solve(B,X);
460 return true;
461}
462#endif
463
464template <class Mat>
465void permuteRows(const Mat& B,
466 const std::vector<size_t> permutation,
467 Mat& result)
468{
469 // Better would be if the Mat type allows easy swapping of rows...
470 result.resize(B.numRows(),
471 B.numColumns()); // leaves B alone if correct size already...
472 for (long r = 0; r < B.numRows(); r++)
473 for (long c = 0; c < B.numColumns(); c++)
474 B.ring().set(result.entry(r, c), B.entry(permutation[r], c));
475}
476
477template <class Mat>
478void solveLowerTriangular(const Mat& LU, const Mat& B, Mat& X)
479{
480}
481
482template <>
484 const DMatGFFlintBig& B,
486{
487 fq_nmod_mat_solve_tril(X.fq_nmod_mat(),
488 LU.fq_nmod_mat(),
489 B.fq_nmod_mat(),
490 1,
491 LU.ring().flintContext());
492}
493
494template <>
496 const DMatGFFlint& B,
497 DMatGFFlint& X)
498{
499 fq_zech_mat_solve_tril(X.fq_zech_mat(),
500 LU.fq_zech_mat(),
501 B.fq_zech_mat(),
502 1,
503 LU.ring().flintContext());
504}
505
506template <class Mat>
507void solveUpperTriangular(const Mat& LU, const Mat& B, Mat& X)
508{
509}
510
511template <>
513 const DMatGFFlint& B,
514 DMatGFFlint& X)
515{
516 fq_zech_mat_solve_triu(X.fq_zech_mat(),
517 LU.fq_zech_mat(),
518 B.fq_zech_mat(),
519 0,
520 LU.ring().flintContext());
521}
522template <>
524 const DMatGFFlintBig& B,
526{
527 fq_nmod_mat_solve_triu(X.fq_nmod_mat(),
528 LU.fq_nmod_mat(),
529 B.fq_nmod_mat(),
530 0,
531 LU.ring().flintContext());
532}
533
534template <class RingType>
536{
537 // printf("in dmat-lu solveInvertible\n");
538 // possible TODO: incorporate a faster method if we know matrix is
539 // invertible...
540 assert(mLUObject.numRows() == mLUObject.numColumns());
541 assert(mLUObject.numRows() == B.numRows());
542
543 X.resize(mLUObject.numColumns(), B.numColumns());
544 mLUObject.LUinPlace();
545 if (rank() < mLUObject.numRows()) return false;
546 solve(B, X);
547 return true;
548#if 0
549 // The following code doesn't really seem to be any faster than the naive code we have
550 // at least for sizes less than a few thousand rows/columns.
551 Mat B1(ring(), LU.numRows(), LU.numRows()); // square matrix
552 printf("in dmat-lu solveInvertible step 3\n");
553 permuteRows<Mat>(B, mLUObject.permutation(), B1);
554 printf("in dmat-lu solveInvertible step 4\n");
555 solveLowerTriangular(LU, B, X);
556 printf("in dmat-lu solveInvertible step 5\n");
557 solveUpperTriangular(LU, X, X);
558 printf("in dmat-lu solveInvertible step 6\n");
559 return true;
560#endif
561}
562
563template <class RingType>
565{
566 const Mat& LU = mLUObject.LUinPlace();
567
568 // Make the identity matrix
569 // printf("in DMatLinAlg<RingType>::inverse\n");
570
571 Mat id(ring(), LU.numRows(), LU.numRows());
572 for (size_t i = 0; i < LU.numRows(); i++)
573 ring().set_from_long(id.entry(i, i), 1);
574
575 solve(id, X);
576 return true;
577}
578
579template <class RingType>
581{
582 const Mat& LU = mLUObject.LUinPlace();
583 const std::vector<size_t>& pivotColumns = mLUObject.pivotColumns();
584
585 typename RingType::Element tmp(ring()), tmp2(ring());
586
587 // First, let's set X to be the correct size:
588 X.resize(LU.numColumns(), LU.numColumns() - pivotColumns.size());
589
590 size_t col = 0;
591 size_t nextpivotidx = 0;
592 size_t nextpivotcol =
593 (pivotColumns.size() > 0 ? pivotColumns[0] : LU.numColumns());
594 size_t colX = 0;
595 while (colX < X.numColumns())
596 {
597 if (col == nextpivotcol)
598 {
599 col++;
600 nextpivotidx++;
601 nextpivotcol =
602 (nextpivotidx < pivotColumns.size() ? pivotColumns[nextpivotidx]
603 : LU.numColumns());
604 continue;
605 }
606 // At this point, we are ready to create a column of X.
607 ring().set_from_long(X.entry(col, colX), -1);
608 // Now we loop through and set the elements in the rows of X = pivot
609 // columns.
610 for (long p = nextpivotidx - 1; p >= 0; p--)
611 {
612 // set X.entry(pivotColumns[p], colX)
613 ring().set(tmp, LU.entry(p, col));
614 for (size_t i = nextpivotidx - 1; i >= p + 1; i--)
615 {
616 ring().mult(tmp2,
617 LU.entry(p, pivotColumns[i]),
618 X.entry(pivotColumns[i], colX));
619 ring().subtract(tmp, tmp, tmp2);
620 }
621 ring().divide(tmp, tmp, LU.entry(p, pivotColumns[p]));
622 ring().set(X.entry(pivotColumns[p], colX), tmp);
623 }
624 colX++;
625 col++;
626 }
627 return X.numColumns();
628}
629
630#endif
631
632// Local Variables:
633// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
634// indent-tabs-mode: nil
635// End:
const fq_zech_mat_t & fq_zech_mat() const
const fq_nmod_mat_t & fq_nmod_mat() const
size_t numRows() const
Definition dmat.hpp:144
ElementType & entry(size_t row, size_t column)
Definition dmat.hpp:148
void resize(size_t new_nrows, size_t new_ncols)
Definition dmat.hpp:157
size_t numColumns() const
Definition dmat.hpp:145
Definition dmat.hpp:62
DMatLUinPlace< RingType > mLUObject
Definition dmat-lu.hpp:64
DMatLinAlg(const Mat &A)
Copies A into mLU and initializes all fields.
Definition dmat-lu.hpp:132
void debug_out()
Definition dmat-lu.hpp:110
size_t kernel(Mat &X)
Definition dmat-lu.hpp:580
size_t rank()
Output: returns the approximate rank of A (-1 if fails).
Definition dmat-lu.hpp:332
const RingType & ring() const
Definition dmat-lu.hpp:107
RingType::ElementType ElementType
Definition dmat-lu.hpp:60
void determinant(ElementType &result)
Output: result, the determinant of A.
Definition dmat-lu.hpp:308
DMat< RingType > Mat
Definition dmat-lu.hpp:61
bool solveInvertible(const Mat &B, Mat &X)
Definition dmat-lu.hpp:535
bool matrixPLU(std::vector< size_t > &P, Mat &L, Mat &U)
Definition dmat-lu.hpp:297
bool inverse(Mat &X)
Definition dmat-lu.hpp:564
void debug_out_list(ElementType *x, size_t len)
Definition dmat-lu.hpp:117
void setUpperLower(const Mat &LU, Mat &lower, Mat &upper)
Definition dmat-lu.hpp:271
bool solve(const Mat &B, Mat &X)
Definition dmat-lu.hpp:340
void columnRankProfile(std::vector< size_t > &profile)
Definition dmat-lu.hpp:324
Type-erased owning handle to a dense coefficient vector held by a ConcreteVectorArithmetic<Ring>.
char * str()
Definition buffer.hpp:72
DMatLUinPlace<RT> — the LU worker that DMatLinAlg<RT> delegates to, with RR / CC / GF specialisations...
DMatLinAlg<M2::ARingQQ> — rational dense LU routed through FLINT fmpq_mat / fmpz_mat.
DMatLinAlg<ARingZZpFFPACK> and the ffpackInterface namespace — FFLAS-FFPACK-backed Z/p linear algebra...
DMatLinAlg<M2::ARingZZpFlint> — dense Z/p linear algebra routed through FLINT nmod_mat_*.
void solveLowerTriangular(const Mat &LU, const Mat &B, Mat &X)
Definition dmat-lu.hpp:478
void solveUpperTriangular< DMatGFFlintBig >(const DMatGFFlintBig &LU, const DMatGFFlintBig &B, DMatGFFlintBig &X)
Definition dmat-lu.hpp:523
void solveUpperTriangular(const Mat &LU, const Mat &B, Mat &X)
Definition dmat-lu.hpp:507
void solveUpperTriangular< DMatGFFlint >(const DMatGFFlint &LU, const DMatGFFlint &B, DMatGFFlint &X)
Definition dmat-lu.hpp:512
void solveLowerTriangular< DMatGFFlintBig >(const DMatGFFlintBig &LU, const DMatGFFlintBig &B, DMatGFFlintBig &X)
Definition dmat-lu.hpp:483
DMat< M2::ARingGFFlintBig > DMatGFFlintBig
Definition dmat-lu.hpp:54
void solveLowerTriangular< DMatGFFlint >(const DMatGFFlint &LU, const DMatGFFlint &B, DMatGFFlint &X)
Definition dmat-lu.hpp:495
void permuteRows(const Mat &B, const std::vector< size_t > permutation, Mat &result)
Definition dmat-lu.hpp:465
DMat<ACoeffRing> — dense-matrix template plus the umbrella that wires in every per-ring specialisatio...
int p
VALGRIND_MAKE_MEM_DEFINED & result(result)
char newline[]
Definition m2-types.cpp:49
MatElementaryOps<MT> — row / column primitives templated over dense or sparse matrix storage.
DMat< M2::ARingGFFlint > DMatGFFlint
void displayMat(buffer &o, const Mat &A)
Definition mat-util.hpp:43
Generic helpers (displayMat, concatenateMatrices) for DMat / SMat matrices.
bool isZero(const DMat< RT > &A)
M2_arrayintOrNull LU(const Mat &A, Mat &L, Mat &U)
const mpreal min(const mpreal &x, const mpreal &y)
Definition mpreal.h:2792
volatile int x