Macaulay2 Engine
Loading...
Searching...
No Matches
dmat-lu-inplace.hpp
Go to the documentation of this file.
1// Copyright 2014 Michael E. Stillman
2
3#ifndef _dmat_lu_inplace_hpp_
4#define _dmat_lu_inplace_hpp_
5
40
41#include "dmat.hpp"
42#include "mat-elem-ops.hpp"
43#include "mat-util.hpp"
44
45// The following needs to be included before any flint files are included.
46#include <M2/gc-include.h>
47
48#pragma GCC diagnostic push
49#pragma GCC diagnostic ignored "-Wconversion"
50#include <flint/fq_nmod_mat.h> // for fq_nmod_mat_lu, fq_zech_mat_lu
51#include <flint/perm.h> // for _perm_parity
52#pragma GCC diagnostic pop
53
54std::vector<double> make_lapack_array(const DMatRR& mat);
55std::vector<double> make_lapack_array(const DMatCC& mat);
56void fill_from_lapack_array(const std::vector <double> & doubles, DMatRR& mat);
57void fill_from_lapack_array(const std::vector <double> & doubles, DMatCC& mat);
58
59template <typename RT>
60class LUUtil
61{
62 public:
63 typedef RT RingType;
65
66 static void setUpperLower(const Mat& LU, Mat& lower, Mat& upper);
67 static void computePivotColumns(const Mat& LU,
68 std::vector<size_t>& result_columns);
69 // not written yet:
70 // static bool computeSign(const std::vector<size_t>& perm); // true: 1,
71 // false: -1
72
73 void debug_out(const Mat& M)
74 {
75 buffer o;
76 displayMat(o, M);
77 std::cout << o.str() << std::endl;
78 }
79};
80
81template <typename RT>
82class DMatLUinPlace;
83
84template <typename RT>
86{
87 public:
88 typedef RT RingType;
90
91 public:
92 DMatLUinPlace(const Mat& A);
93
94 const RingType& ring() const { return mLU.ring(); }
95 long numRows() const { return mLU.numRows(); }
96 long numColumns() const { return mLU.numColumns(); }
97 const Mat& LUinPlace()
98 {
99 computeLU();
100 return mLU;
101 } // raises an exception if there is an error
102 // Can be called repeatedly: the result is remembered once done.
103 // Returns a constant ref to the internal "in place" LU.
104
105 bool signOfPermutation() { return mSign; }
106 const std::vector<size_t>& permutation() { return mPerm; }
107 const std::vector<size_t>& pivotColumns() { return mPivotColumns; }
108 private:
109 typedef typename RingType::ElementType ElementType;
110
111 void computeLU();
112 size_t findPivot(size_t row, size_t col);
113
114 private:
116 std::vector<size_t> mPerm;
117 bool mSign;
119 std::vector<size_t> mPivotColumns;
120};
121
122template <>
124{
125 if (mIsDone) return;
126 //std::cout << "computing LU decomposition GFFlintBig" << std::endl;
127
128 mp_limb_signed_t* perm = newarray_atomic(mp_limb_signed_t, mLU.numRows());
129 fq_nmod_mat_lu(perm, mLU.fq_nmod_mat(), false, ring().flintContext());
130 // Now we set mPerm:
131 mPerm.clear();
132 for (long i = 0; i < mLU.numRows(); i++) mPerm.push_back(perm[i]);
133 mSign = (_perm_parity(perm, mLU.numRows()) == 0);
134 freemem(perm);
135
136 // Now we set mPivotColumns
138
139 mIsDone = true;
140}
141
142template <>
144{
145 if (mIsDone) return;
146 // std::cout << "computing LU decomposition GFFlint" << std::endl;
147 mp_limb_signed_t* perm = newarray_atomic(mp_limb_signed_t, mLU.numRows());
148 fq_zech_mat_lu(perm, mLU.fq_zech_mat(), false, ring().flintContext());
149 // Now we set mPerm:
150 mPerm.clear();
151 for (long i = 0; i < mLU.numRows(); i++) mPerm.push_back(perm[i]);
152 mSign = (_perm_parity(perm, mLU.numRows()) == 0);
153 freemem(perm);
154
155 // Now we set mPivotColumns
157
158 mIsDone = true;
159}
160
161template <class RingType>
163 : mLU(A), // copies A
164 mSign(true), // sign = 1
165 mIsDone(false)
166{
167 for (size_t i = 0; i < A.numRows(); i++) mPerm.push_back(i);
168}
169
170template <class RingType>
171size_t DMatLUinPlace<RingType>::findPivot(size_t row, size_t col)
172{
173 // Look at elements A[row,col], A[row+1,col], ..., A[nrows-1, col]
174 // Return the index r s.y. abs(A[r,col]) is maximum over all of these
175
176 for (size_t i = row; i < mLU.numRows(); i++)
177 {
178 if (!ring().is_zero(mLU.entry(i, col))) return i;
179 }
180 return static_cast<size_t>(-1);
181}
182
183template <>
184inline size_t DMatLUinPlace<M2::ARingRRR>::findPivot(size_t row, size_t col)
185{
186 // Look at elements A[row,col], A[row+1,col], ..., A[nrows-1, col]
187 // Return the index r s.y. abs(A[r,col]) is maximum over all of these
188
189 M2::ARingRRR::Element largest(ring()), abs(ring());
190 size_t best_row_so_far = static_cast<size_t>(-1);
191
192 ring().set_zero(largest);
193 for (size_t i = row; i < mLU.numRows(); i++)
194 {
195 ring().abs(abs, mLU.entry(i, col));
196 if (ring().compare_elems(abs, largest) > 0)
197 {
198 best_row_so_far = i;
199 ring().set(largest, abs);
200 }
201 }
202 return best_row_so_far;
203}
204
205template <>
206inline size_t DMatLUinPlace<M2::ARingCCC>::findPivot(size_t row, size_t col)
207{
208 // Look at elements A[row,col], A[row+1,col], ..., A[nrows-1, col]
209 // Return the index r s.y. abs(A[r,col]) is maximum over all of these
210
211 const M2::ARingRRR& RR = ring().real_ring();
212 M2::ARingRRR::Element largest(RR), abs(RR);
213 size_t best_row_so_far = static_cast<size_t>(-1);
214
215 RR.set_zero(largest);
216
217 for (size_t i = row; i < mLU.numRows(); i++)
218 {
219 ring().abs(abs, mLU.entry(i, col));
220 if (RR.compare_elems(abs, largest) > 0)
221 {
222 best_row_so_far = i;
223 RR.set(largest, abs);
224 }
225 }
226 return best_row_so_far;
227}
228
229template <class RingType>
231{
232 if (mIsDone) return;
233
234 // std::cout << "computing LU decomposition generic version" << std::endl;
235 typename RingType::Element tmp(mLU.ring());
236
237 size_t col = 0; // current column we are working on
238 size_t row = 0; // current row we are working on
239 size_t nrows = mLU.numRows();
240 size_t ncols = mLU.numColumns();
241
242 while (col < ncols && row < nrows)
243 {
244 // printf("*** in naive row,col = (%ld, %ld) ***\n", row, col);
245 // debug_out();
246
247 // Step 1: Set the 'upper' values: (row,col)..(nrows-1,col)
248 for (size_t r = row; r < nrows; r++)
249 {
250 for (size_t i = 0; i < row; i++)
251 {
252 mLU.ring().mult(tmp, mLU.entry(r, i), mLU.entry(i, col));
253 mLU.ring().subtract(mLU.entry(r, col), mLU.entry(r, col), tmp);
254 }
255 }
256
257 // printf("after step 1\n");
258 // debug_out();
259
260 // Step 2: Find a pivot among the elements in step 1.
261 // If one: swap rows if needed
262 // If none, increment 'col', and continue at start of loop
263 size_t k = findPivot(row, col);
264 if (k == static_cast<size_t>(-1))
265 {
266 col = col + 1;
267 continue;
268 }
269 // printf("pivot is in row %ld\n", k);
270 std::swap(mPerm[row], mPerm[k]);
271 if (k != row)
272 {
274 mSign = !mSign;
275 }
276 mPivotColumns.push_back(col);
277 const ElementType& pivot = mLU.entry(row, col);
278
279 // printf("after step 2:\n");
280 // debug_out();
281
282 // Step 3A: Set the 'upper' elements in (row,col+1), ..., (row,ncols-1).
283 for (size_t c = col + 1; c < ncols; c++)
284 {
285 for (size_t i = 0; i < row; i++)
286 {
287 mLU.ring().mult(tmp, mLU.entry(row, i), mLU.entry(i, c));
288 mLU.ring().subtract(mLU.entry(row, c), mLU.entry(row, c), tmp);
289 }
290 }
291 // printf("after step 3A:\n");
292 // debug_out();
293
294 // Step 3B: Set the 'lower' elements in (row+1,row), ..., (nrows-1,row)
295 // from (row+1,col), ..., (nrows-1,col)
296 // This just means dividing then by the pivot
297 // except, if we have skipped columns for pivots, we must set these
298 // elements
299 // in column 'row', not 'col'...
300 // Step 3C: if row != col, then set these elements to 0:
301 // (row+1,col), ..., (nrows-1,col)
302 for (size_t r = row + 1; r < nrows; r++)
303 {
304 mLU.ring().divide(mLU.entry(r, row), mLU.entry(r, col), pivot);
305 if (row < col) ring().set_zero(mLU.entry(r, col));
306 }
307
308 // printf("after step 3B:\n");
309 // debug_out();
310
311 row++;
312 col++;
313 }
314
315 mIsDone = true;
316}
317
318template <>
320{
321 if (mIsDone) return;
322
323 // std::cout << "computing LU decomposition ARingRR" << std::endl;
324 int rows = static_cast<int>(mLU.numRows());
325 int cols = static_cast<int>(mLU.numColumns());
326 int info;
327 int min = (rows <= cols) ? rows : cols;
328
329 if (min == 0)
330 return;
331
332 int* perm = new int[min];
333 std::vector<double> copyA = make_lapack_array(mLU);
334
335 dgetrf_(&rows, &cols, copyA.data(), &rows, perm, &info);
336
337 if (info < 0)
338 {
339 // First, clean up, then throw an exception
340 delete [] perm;
341 throw exc::engine_error("argument passed to dgetrf had an illegal value");
342 }
343
344 // Now copy back to row major order
346
347 // Now place the correct permutation into mPerm
348 for (int i = 0; i < min; i++)
349 {
350 int thisloc = perm[i] - 1;
351 if (i != thisloc)
352 {
353 mSign = not mSign;
354 size_t tmp = mPerm[thisloc];
355 mPerm[thisloc] = mPerm[i];
356 mPerm[i] = tmp;
357 }
358 }
359
361 mIsDone = true;
362
363 delete [] perm;
364}
365
366template <>
368{
369 if (mIsDone) return;
370
371 // std::cout << "computing LU decomposition ARingCC" << std::endl;
372 int rows = static_cast<int>(mLU.numRows());
373 int cols = static_cast<int>(mLU.numColumns());
374 int info;
375 int min = (rows <= cols) ? rows : cols;
376
377 if (min == 0)
378 return;
379
380 int* perm = new int[min];
381 auto copyA = make_lapack_array(mLU);
382
383 zgetrf_(&rows, &cols, copyA.data(), &rows, perm, &info);
384
385 if (info < 0)
386 {
387 delete[] perm;
388 throw exc::engine_error("argument passed to zgetrf had an illegal value");
389 }
390
391 // Now copy back to row major order
393
394 // Now place the correct permutation into mPerm
395 for (int i = 0; i < min; i++)
396 {
397 int thisloc = perm[i] - 1;
398 if (i != thisloc)
399 {
400 mSign = not mSign;
401 size_t tmp = mPerm[thisloc];
402 mPerm[thisloc] = mPerm[i];
403 mPerm[i] = tmp;
404 }
405 }
406
408 mIsDone = true;
409
410 delete[] perm;
411}
412
413template <class RingType>
414void LUUtil<RingType>::setUpperLower(const Mat& LU, Mat& lower, Mat& upper)
415{
416 size_t min = std::min(LU.numRows(), LU.numColumns());
417 lower.resize(LU.numRows(), min);
418 upper.resize(min, LU.numColumns());
419
420 // At this point, lower and upper should be zero matrices.
421 assert(MatrixOps::isZero(lower));
422 assert(MatrixOps::isZero(upper));
423
424 for (size_t c = 0; c < LU.numColumns(); c++)
425 {
426 if (c < min) LU.ring().set_from_long(lower.entry(c, c), 1);
427 for (size_t r = 0; r < LU.numRows(); r++)
428 {
429 if (r <= c)
430 LU.ring().set(upper.entry(r, c), LU.entry(r, c));
431 else if (c < lower.numRows())
432 {
433 LU.ring().set(lower.entry(r, c), LU.entry(r, c));
434 }
435 }
436 }
437}
438
439template <class RingType>
441 std::vector<size_t>& result_columns)
442{
443 result_columns.clear();
444 size_t thiscol = 0;
445 size_t thisrow = 0;
446 while (thisrow < LU.numRows() and thiscol < LU.numColumns())
447 {
448 if (not LU.ring().is_zero(LU.entry(thisrow, thiscol)))
449 {
450 result_columns.push_back(thiscol);
451 thisrow++;
452 }
453 thiscol++;
454 }
455}
456
457#endif
458
459// Local Variables:
460// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
461// indent-tabs-mode: nil
462// End:
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
Definition dmat.hpp:62
const Mat & LUinPlace()
long numRows() const
const RingType & ring() const
DMatLUinPlace(const Mat &A)
RingType::ElementType ElementType
std::vector< size_t > mPivotColumns
std::vector< size_t > mPerm
const std::vector< size_t > & permutation()
size_t findPivot(size_t row, size_t col)
DMat< RingType > Mat
const std::vector< size_t > & pivotColumns()
long numColumns() const
static void setUpperLower(const Mat &LU, Mat &lower, Mat &upper)
void debug_out(const Mat &M)
static void computePivotColumns(const Mat &LU, std::vector< size_t > &result_columns)
DMat< RingType > Mat
void set(ElementType &result, const ElementType &a) const
int compare_elems(const ElementType &f, const ElementType &g) const
void set_zero(ElementType &result) const
aring-style adapter for arbitrary-precision real numbers, backed by MPFR.
Definition aring-RRR.hpp:70
char * str()
Definition buffer.hpp:72
std::vector< double > make_lapack_array(const DMatRR &mat)
Definition lapack.cpp:20
void fill_from_lapack_array(const std::vector< double > &doubles, DMatRR &mat)
Definition lapack.cpp:45
DMat<ACoeffRing> — dense-matrix template plus the umbrella that wires in every per-ring specialisatio...
int zgetrf_(int *rows, int *cols, double *M, int *ld, int *ipiv, int *info)
DMat< M2::ARingCC > DMatCC
Definition lapack.hpp:53
void dgetrf_(const int *rows, const int *cols, double *A, const int *ld, int *ipiv, int *info)
DMat< M2::ARingRR > DMatRR
Definition lapack.hpp:52
void freemem(void *s)
Definition m2-mem.cpp:103
MatElementaryOps<MT> — row / column primitives templated over dense or sparse matrix storage.
void displayMat(buffer &o, const Mat &A)
Definition mat-util.hpp:43
Generic helpers (displayMat, concatenateMatrices) for DMat / SMat matrices.
doubling_stash * doubles
Definition mem.cpp:14
bool isZero(const DMat< RT > &A)
void swap(mpfr::mpreal &x, mpfr::mpreal &y)
Definition mpreal.h:3244
#define newarray_atomic(T, len)
Definition newdelete.hpp:91
#define abs(x)
Definition polyroots.cpp:51