GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
sparse-dmsolve.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2006-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <algorithm>
31 
32 #include "CMatrix.h"
33 #include "CSparse.h"
34 #include "MArray.h"
35 #include "MSparse.h"
36 #include "MatrixType.h"
37 #include "dSparse.h"
38 #include "lo-error.h"
39 #include "oct-inttypes.h"
40 #include "oct-locbuf.h"
41 #include "oct-sort.h"
42 #include "oct-sparse.h"
43 #include "quit.h"
44 #include "sparse-dmsolve.h"
45 #include "sparse-qr.h"
46 
47 template <typename T>
48 static MSparse<T>
50  const octave_idx_type *Q, octave_idx_type rst,
52  octave_idx_type cend, octave_idx_type maxnz = -1,
53  bool lazy = false)
54 {
55  octave_idx_type nr = rend - rst;
56  octave_idx_type nc = cend - cst;
57 
58  maxnz = (maxnz < 0 ? A.nnz () : maxnz);
59 
60  octave_idx_type nz;
61 
62  // Cast to uint64 to handle overflow in this multiplication
63  if (octave_uint64 (nr)*octave_uint64 (nc) < octave_uint64 (maxnz))
64  nz = nr*nc;
65  else
66  nz = maxnz;
67 
68  MSparse<T> B (nr, nc, (nz < maxnz ? nz : maxnz));
69 
70  // Some sparse functions can support lazy indexing (where elements
71  // in the row are in no particular order), even though octave in
72  // general can't. For those functions that can using it is a big
73  // win here in terms of speed.
74 
75  if (lazy)
76  {
77  nz = 0;
78 
79  for (octave_idx_type j = cst ; j < cend ; j++)
80  {
81  octave_idx_type qq = (Q ? Q[j] : j);
82 
83  B.xcidx (j - cst) = nz;
84 
85  for (octave_idx_type p = A.cidx (qq) ; p < A.cidx (qq+1) ; p++)
86  {
87  octave_quit ();
88 
89  octave_idx_type r = (Pinv ? Pinv[A.ridx (p)] : A.ridx (p));
90 
91  if (r >= rst && r < rend)
92  {
93  B.xdata (nz) = A.data (p);
94  B.xridx (nz++) = r - rst;
95  }
96  }
97  }
98 
99  B.xcidx (cend - cst) = nz;
100  }
101  else
102  {
103  OCTAVE_LOCAL_BUFFER (T, X, rend - rst);
104 
106  octave_idx_type *ri = B.xridx ();
107 
108  nz = 0;
109 
110  for (octave_idx_type j = cst ; j < cend ; j++)
111  {
112  octave_idx_type qq = (Q ? Q[j] : j);
113 
114  B.xcidx (j - cst) = nz;
115 
116  for (octave_idx_type p = A.cidx (qq) ; p < A.cidx (qq+1) ; p++)
117  {
118  octave_quit ();
119 
120  octave_idx_type r = (Pinv ? Pinv[A.ridx (p)] : A.ridx (p));
121 
122  if (r >= rst && r < rend)
123  {
124  X[r-rst] = A.data (p);
125  B.xridx (nz++) = r - rst;
126  }
127  }
128 
129  sort.sort (ri + B.xcidx (j - cst), nz - B.xcidx (j - cst));
130 
131  for (octave_idx_type p = B.cidx (j - cst); p < nz; p++)
132  B.xdata (p) = X[B.xridx (p)];
133  }
134 
135  B.xcidx (cend - cst) = nz;
136  }
137 
138  return B;
139 }
140 
141 template <typename T>
142 static MArray<T>
144  const octave_idx_type *, octave_idx_type r1,
146  octave_idx_type c2)
147 {
148  r2 -= 1;
149  c2 -= 1;
150 
151  if (r1 > r2)
152  std::swap (r1, r2);
153 
154  if (c1 > c2)
155  std::swap (c1, c2);
156 
157  octave_idx_type new_r = r2 - r1 + 1;
158  octave_idx_type new_c = c2 - c1 + 1;
159 
160  MArray<T> result (dim_vector (new_r, new_c));
161 
162  for (octave_idx_type j = 0; j < new_c; j++)
163  {
164  for (octave_idx_type i = 0; i < new_r; i++)
165  result.xelem (i, j) = m.elem (r1+i, c1+j);
166  }
167 
168  return result;
169 }
170 
171 template <typename T>
172 static void
175 {
176  T *ax = a.fortran_vec ();
177 
178  const T *bx = b.fortran_vec ();
179 
180  octave_idx_type anr = a.rows ();
181 
182  octave_idx_type nr = b.rows ();
183  octave_idx_type nc = b.cols ();
184 
185  for (octave_idx_type j = 0; j < nc; j++)
186  {
187  octave_idx_type aoff = (c + j) * anr;
188  octave_idx_type boff = j * nr;
189 
190  for (octave_idx_type i = 0; i < nr; i++)
191  {
192  octave_quit ();
193  ax[Q[r + i] + aoff] = bx[i + boff];
194  }
195  }
196 }
197 
198 template <typename T>
199 static void
202 {
203  octave_idx_type b_rows = b.rows ();
204  octave_idx_type b_cols = b.cols ();
205 
206  octave_idx_type nr = a.rows ();
207  octave_idx_type nc = a.cols ();
208 
210 
211  for (octave_idx_type i = 0; i < nr; i++)
212  Qinv[Q[i]] = i;
213 
214  // First count the number of elements in the final array
215  octave_idx_type nel = a.xcidx (c) + b.nnz ();
216 
217  if (c + b_cols < nc)
218  nel += a.xcidx (nc) - a.xcidx (c + b_cols);
219 
220  for (octave_idx_type i = c; i < c + b_cols; i++)
221  {
222  for (octave_idx_type j = a.xcidx (i); j < a.xcidx (i+1); j++)
223  {
224  if (Qinv[a.xridx (j)] < r || Qinv[a.xridx (j)] >= r + b_rows)
225  nel++;
226  }
227  }
228 
229  OCTAVE_LOCAL_BUFFER (T, X, nr);
230 
232 
233  MSparse<T> tmp (a);
234 
235  a = MSparse<T> (nr, nc, nel);
236 
237  octave_idx_type *ri = a.xridx ();
238 
239  for (octave_idx_type i = 0; i < tmp.cidx (c); i++)
240  {
241  a.xdata (i) = tmp.xdata (i);
242  a.xridx (i) = tmp.xridx (i);
243  }
244 
245  for (octave_idx_type i = 0; i < c + 1; i++)
246  a.xcidx (i) = tmp.xcidx (i);
247 
248  octave_idx_type ii = a.xcidx (c);
249 
250  for (octave_idx_type i = c; i < c + b_cols; i++)
251  {
252  octave_quit ();
253 
254  for (octave_idx_type j = tmp.xcidx (i); j < tmp.xcidx (i+1); j++)
255  {
256  if (Qinv[tmp.xridx (j)] < r || Qinv[tmp.xridx (j)] >= r + b_rows)
257  {
258  X[tmp.xridx (j)] = tmp.xdata (j);
259  a.xridx (ii++) = tmp.xridx (j);
260  }
261  }
262 
263  octave_quit ();
264 
265  for (octave_idx_type j = b.cidx (i-c); j < b.cidx (i-c+1); j++)
266  {
267  X[Q[r + b.ridx (j)]] = b.data (j);
268  a.xridx (ii++) = Q[r + b.ridx (j)];
269  }
270 
271  sort.sort (ri + a.xcidx (i), ii - a.xcidx (i));
272 
273  for (octave_idx_type p = a.xcidx (i); p < ii; p++)
274  a.xdata (p) = X[a.xridx (p)];
275 
276  a.xcidx (i+1) = ii;
277  }
278 
279  for (octave_idx_type i = c + b_cols; i < nc; i++)
280  {
281  for (octave_idx_type j = tmp.xcidx (i); j < tmp.cidx (i+1); j++)
282  {
283  a.xdata (ii) = tmp.xdata (j);
284  a.xridx (ii++) = tmp.xridx (j);
285  }
286 
287  a.xcidx (i+1) = ii;
288  }
289 }
290 
291 template <typename T, typename RT>
292 static void
294 {
295  octave_idx_type b_nr = b.rows ();
296  octave_idx_type b_nc = b.cols ();
297 
298  const T *Bx = b.fortran_vec ();
299 
300  a.resize (dim_vector (b_nr, b_nc));
301 
302  RT *Btx = a.fortran_vec ();
303 
304  for (octave_idx_type j = 0; j < b_nc; j++)
305  {
306  octave_idx_type off = j * b_nr;
307  for (octave_idx_type i = 0; i < b_nr; i++)
308  {
309  octave_quit ();
310  Btx[p[i] + off] = Bx[ i + off];
311  }
312  }
313 }
314 
315 template <typename T, typename RT>
316 static void
318 {
319  octave_idx_type b_nr = b.rows ();
320  octave_idx_type b_nc = b.cols ();
321  octave_idx_type b_nz = b.nnz ();
322 
323  octave_idx_type nz = 0;
324 
325  a = MSparse<RT> (b_nr, b_nc, b_nz);
327  octave_idx_type *ri = a.xridx ();
328 
329  OCTAVE_LOCAL_BUFFER (RT, X, b_nr);
330 
331  a.xcidx (0) = 0;
332 
333  for (octave_idx_type j = 0; j < b_nc; j++)
334  {
335  for (octave_idx_type i = b.cidx (j); i < b.cidx (j+1); i++)
336  {
337  octave_quit ();
338  octave_idx_type r = p[b.ridx (i)];
339  X[r] = b.data (i);
340  a.xridx (nz++) = p[b.ridx (i)];
341  }
342 
343  sort.sort (ri + a.xcidx (j), nz - a.xcidx (j));
344 
345  for (octave_idx_type i = a.cidx (j); i < nz; i++)
346  {
347  octave_quit ();
348  a.xdata (i) = X[a.xridx (i)];
349  }
350 
351  a.xcidx (j+1) = nz;
352  }
353 }
354 
355 #if defined (HAVE_CXSPARSE)
356 
357 static void
359 {
360  // Dummy singularity handler so that LU solver doesn't flag
361  // an error for numerically rank defficient matrices
362 }
363 
364 #endif
365 
366 template <typename RT, typename ST, typename T>
367 RT
368 dmsolve (const ST& a, const T& b, octave_idx_type& info)
369 {
370  RT retval;
371 
372 #if defined (HAVE_CXSPARSE)
373 
374  octave_idx_type nr = a.rows ();
375  octave_idx_type nc = a.cols ();
376 
377  octave_idx_type b_nr = b.rows ();
378  octave_idx_type b_nc = b.cols ();
379 
380  if (nr < 0 || nc < 0 || nr != b_nr)
381  (*current_liboctave_error_handler)
382  ("matrix dimension mismatch in solution of minimum norm problem");
383 
384  if (nr == 0 || nc == 0 || b_nc == 0)
385  retval = RT (nc, b_nc, 0.0);
386  else
387  {
388  octave_idx_type nnz_remaining = a.nnz ();
389 
390  CXSPARSE_DNAME () csm;
391 
392  csm.m = nr;
393  csm.n = nc;
394  csm.x = nullptr;
395  csm.nz = -1;
396  csm.nzmax = a.nnz ();
397 
398  // Cast away const on A, with full knowledge that CSparse won't touch it.
399  // Prevents the methods below from making a copy of the data.
400  csm.p = const_cast<octave::suitesparse_integer *>
401  (octave::to_suitesparse_intptr (a.cidx ()));
402  csm.i = const_cast<octave::suitesparse_integer *>
403  (octave::to_suitesparse_intptr (a.ridx ()));
404 
405  CXSPARSE_DNAME (d) *dm = CXSPARSE_DNAME(_dmperm) (&csm, 0);
408 
410 
411  for (octave_idx_type i = 0; i < nr; i++)
412  pinv[p[i]] = i;
413 
414  RT btmp;
415  dmsolve_permute (btmp, b, pinv);
416  info = 0;
417 
418  retval.resize (nc, b_nc);
419 
420  // Leading over-determined block
421  if (dm->rr[2] < nr && dm->cc[3] < nc)
422  {
423  ST m = dmsolve_extract (a, pinv, q, dm->rr[2], nr, dm->cc[3], nc,
424  nnz_remaining, true);
425  nnz_remaining -= m.nnz ();
426  RT mtmp = octave::math::qrsolve (m, dmsolve_extract (btmp,
427  nullptr, nullptr,
428  dm->rr[2], b_nr,
429  0, b_nc),
430  info);
431  dmsolve_insert (retval, mtmp, q, dm->cc[3], 0);
432 
433  if (dm->rr[2] > 0 && ! info)
434  {
435  m = dmsolve_extract (a, pinv, q, 0, dm->rr[2],
436  dm->cc[3], nc, nnz_remaining, true);
437  nnz_remaining -= m.nnz ();
438  RT ctmp = dmsolve_extract (btmp, nullptr, nullptr,
439  0, dm->rr[2], 0, b_nc);
440  btmp.insert (ctmp - m * mtmp, 0, 0);
441  }
442  }
443 
444  // Structurally non-singular blocks
445  // FIXME: Should use fine Dulmange-Mendelsohn decomposition here.
446  if (dm->rr[1] < dm->rr[2] && dm->cc[2] < dm->cc[3] && ! info)
447  {
448  ST m = dmsolve_extract (a, pinv, q, dm->rr[1], dm->rr[2],
449  dm->cc[2], dm->cc[3], nnz_remaining, false);
450  nnz_remaining -= m.nnz ();
451  RT btmp2 = dmsolve_extract (btmp, nullptr, nullptr,
452  dm->rr[1], dm->rr[2],
453  0, b_nc);
454  double rcond = 0.0;
456  RT mtmp = m.solve (mtyp, btmp2, info, rcond,
458  if (info != 0)
459  {
460  info = 0;
461  mtmp = octave::math::qrsolve (m, btmp2, info);
462  }
463 
464  dmsolve_insert (retval, mtmp, q, dm->cc[2], 0);
465  if (dm->rr[1] > 0 && ! info)
466  {
467  m = dmsolve_extract (a, pinv, q, 0, dm->rr[1], dm->cc[2],
468  dm->cc[3], nnz_remaining, true);
469  nnz_remaining -= m.nnz ();
470  RT ctmp = dmsolve_extract (btmp, nullptr, nullptr,
471  0, dm->rr[1], 0, b_nc);
472  btmp.insert (ctmp - m * mtmp, 0, 0);
473  }
474  }
475 
476  // Trailing under-determined block
477  if (dm->rr[1] > 0 && dm->cc[2] > 0 && ! info)
478  {
479  ST m = dmsolve_extract (a, pinv, q, 0, dm->rr[1], 0,
480  dm->cc[2], nnz_remaining, true);
481  RT mtmp = octave::math::qrsolve (m, dmsolve_extract (btmp, nullptr,
482  nullptr, 0,
483  dm->rr[1], 0,
484  b_nc),
485  info);
486  dmsolve_insert (retval, mtmp, q, 0, 0);
487  }
488 
489  CXSPARSE_DNAME (_dfree) (dm);
490  }
491 
492 #else
493 
494  octave_unused_parameter (a);
495  octave_unused_parameter (b);
496  octave_unused_parameter (info);
497 
498  (*current_liboctave_error_handler)
499  ("support for CXSparse was unavailable or disabled when liboctave was built");
500 
501 #endif
502 
503  return retval;
504 }
505 
506 // Instantiations we need.
507 
510  (const SparseComplexMatrix&, const Matrix&, octave_idx_type&);
511 
515 
519 
523 
524 template Matrix
526  (const SparseMatrix&, const Matrix&, octave_idx_type&);
527 
528 template SparseMatrix
530  (const SparseMatrix&, const SparseMatrix&, octave_idx_type&);
531 
534  (const SparseMatrix&, const ComplexMatrix&, octave_idx_type&);
535 
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:469
octave_idx_type cols(void) const
Definition: Array.h:423
octave_idx_type rows(void) const
Definition: Array.h:415
const T * fortran_vec(void) const
Size of the specified dimension.
Definition: Array.h:583
Template for N-dimensional array classes with like-type math operators.
Definition: MArray.h:63
Definition: dMatrix.h:42
octave_idx_type cols(void) const
Definition: Sparse.h:251
octave_idx_type * xridx(void)
Definition: Sparse.h:485
T * data(void)
Definition: Sparse.h:470
octave_idx_type * cidx(void)
Definition: Sparse.h:492
octave_idx_type nnz(void) const
Actual number of nonzero terms.
Definition: Sparse.h:238
octave_idx_type rows(void) const
Definition: Sparse.h:250
octave_idx_type * xcidx(void)
Definition: Sparse.h:498
T * xdata(void)
Definition: Sparse.h:472
octave_idx_type * ridx(void)
Definition: Sparse.h:479
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
void sort(T *data, octave_idx_type nel)
Definition: oct-sort.cc:1517
F77_RET_T const F77_INT F77_CMPLX const F77_INT F77_CMPLX * B
F77_RET_T const F77_INT const F77_INT const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE * Q
F77_RET_T const F77_INT F77_CMPLX * A
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
T octave_idx_type m
Definition: mx-inlines.cc:773
T * r
Definition: mx-inlines.cc:773
Matrix qrsolve(const SparseMatrix &a, const MArray< double > &b, octave_idx_type &info)
Definition: sparse-qr.cc:2299
int suitesparse_integer
Definition: oct-sparse.h:171
octave_idx_type * to_octave_idx_type_ptr(suitesparse_integer *i)
Definition: oct-sparse.cc:67
suitesparse_integer * to_suitesparse_intptr(octave_idx_type *i)
Definition: oct-sparse.cc:51
octave_int< uint64_t > octave_uint64
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
#define CXSPARSE_DNAME(name)
Definition: oct-sparse.h:147
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
template SparseMatrix dmsolve< SparseMatrix, SparseMatrix, SparseMatrix >(const SparseMatrix &, const SparseMatrix &, octave_idx_type &)
template Matrix dmsolve< Matrix, SparseMatrix, Matrix >(const SparseMatrix &, const Matrix &, octave_idx_type &)
static void dmsolve_permute(MArray< RT > &a, const MArray< T > &b, const octave_idx_type *p)
static MSparse< T > dmsolve_extract(const MSparse< T > &A, const octave_idx_type *Pinv, const octave_idx_type *Q, octave_idx_type rst, octave_idx_type rend, octave_idx_type cst, octave_idx_type cend, octave_idx_type maxnz=-1, bool lazy=false)
template ComplexMatrix dmsolve< ComplexMatrix, SparseComplexMatrix, ComplexMatrix >(const SparseComplexMatrix &, const ComplexMatrix &, octave_idx_type &)
template ComplexMatrix dmsolve< ComplexMatrix, SparseComplexMatrix, Matrix >(const SparseComplexMatrix &, const Matrix &, octave_idx_type &)
template SparseComplexMatrix dmsolve< SparseComplexMatrix, SparseComplexMatrix, SparseComplexMatrix >(const SparseComplexMatrix &, const SparseComplexMatrix &, octave_idx_type &)
template ComplexMatrix dmsolve< ComplexMatrix, SparseMatrix, ComplexMatrix >(const SparseMatrix &, const ComplexMatrix &, octave_idx_type &)
static void solve_singularity_warning(double)
template SparseComplexMatrix dmsolve< SparseComplexMatrix, SparseMatrix, SparseComplexMatrix >(const SparseMatrix &, const SparseComplexMatrix &, octave_idx_type &)
RT dmsolve(const ST &a, const T &b, octave_idx_type &info)
template SparseComplexMatrix dmsolve< SparseComplexMatrix, SparseComplexMatrix, SparseMatrix >(const SparseComplexMatrix &, const SparseMatrix &, octave_idx_type &)
static void dmsolve_insert(MArray< T > &a, const MArray< T > &b, const octave_idx_type *Q, octave_idx_type r, octave_idx_type c)