GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
sparse-dmsolve.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2006-2025 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-fwd.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
47template <typename T>
48static MSparse<T>
49dmsolve_extract (const MSparse<T>& A, const octave_idx_type *Pinv,
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
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
141template <typename T>
142static MArray<T>
143dmsolve_extract (const MArray<T>& m, const octave_idx_type *,
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
171template <typename T>
172static void
173dmsolve_insert (MArray<T>& a, const MArray<T>& b, const octave_idx_type *Q,
175{
176 T *ax = a.rwdata ();
177
178 const T *bx = b.data ();
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
198template <typename T>
199static void
200dmsolve_insert (MSparse<T>& a, const MSparse<T>& b, const octave_idx_type *Q,
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
291template <typename T, typename RT>
292static void
293dmsolve_permute (MArray<RT>& a, const MArray<T>& b, const octave_idx_type *p)
294{
295 octave_idx_type b_nr = b.rows ();
296 octave_idx_type b_nc = b.cols ();
297
298 const T *Bx = b.data ();
299
300 a.resize (dim_vector (b_nr, b_nc));
301
302 RT *Btx = a.rwdata ();
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
315template <typename T, typename RT>
316static void
317dmsolve_permute (MSparse<RT>& a, const MSparse<T>& b, const octave_idx_type *p)
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
357static void
358solve_singularity_warning (double)
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
366template <typename RT, typename ST, typename T>
367RT
368dmsolve (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);
406 octave_idx_type *p = octave::to_octave_idx_type_ptr (dm->p);
407 octave_idx_type *q = octave::to_octave_idx_type_ptr (dm->q);
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,
457 solve_singularity_warning, false);
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
526 (const SparseMatrix&, const Matrix&, octave_idx_type&);
527
530 (const SparseMatrix&, const SparseMatrix&, octave_idx_type&);
531
534 (const SparseMatrix&, const ComplexMatrix&, octave_idx_type&);
535
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition Array.h:563
octave_idx_type rows() const
Definition Array.h:463
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
octave_idx_type cols() const
Definition Array.h:473
const T * data() const
Size of the specified dimension.
Definition Array.h:665
T * rwdata()
Size of the specified dimension.
Template for N-dimensional array classes with like-type math operators.
Definition MArray.h:61
octave_idx_type cols() const
Definition Sparse.h:349
octave_idx_type * cidx()
Definition Sparse.h:593
T * data()
Definition Sparse.h:571
T * xdata()
Definition Sparse.h:573
octave_idx_type * ridx()
Definition Sparse.h:580
octave_idx_type nnz() const
Actual number of nonzero terms.
Definition Sparse.h:336
octave_idx_type rows() const
Definition Sparse.h:348
octave_idx_type * xcidx()
Definition Sparse.h:599
octave_idx_type * xridx()
Definition Sparse.h:586
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
void sort(T *data, octave_idx_type nel)
Definition oct-sort.cc:1521
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
#define OCTAVE_API
Definition main.in.cc:55
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:159
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 &)
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 &)
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 &)