GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
dmperm.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1998-2024 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 "CSparse.h"
31 #include "dRowVector.h"
32 #include "dSparse.h"
33 #include "oct-sparse.h"
34 
35 #include "defun.h"
36 #include "errwarn.h"
37 #include "ov.h"
38 #include "ovl.h"
39 #include "utils.h"
40 
42 
43 #if defined (OCTAVE_ENABLE_64)
44 # define CXSPARSE_NAME(name) cs_dl ## name
45 #else
46 # define CXSPARSE_NAME(name) cs_di ## name
47 #endif
48 
49 #if defined (HAVE_CXSPARSE)
50 
51 static RowVector
53 {
54  RowVector ret (n);
55  for (octave_idx_type i = 0; i < n; i++)
56  ret.xelem (i) = p[i] + 1;
57  return ret;
58 }
59 
60 static octave_value_list
61 dmperm_internal (bool rank, const octave_value arg, int nargout)
62 {
63  octave_value_list retval;
64  octave_idx_type nr = arg.rows ();
65  octave_idx_type nc = arg.columns ();
68  CXSPARSE_NAME () csm;
69  csm.m = nr;
70  csm.n = nc;
71  csm.x = nullptr;
72  csm.nz = -1;
73 
74  if (arg.isreal ())
75  {
76  m = arg.sparse_matrix_value ();
77  csm.nzmax = m.nnz ();
78  csm.p = to_suitesparse_intptr (m.xcidx ());
79  csm.i = to_suitesparse_intptr (m.xridx ());
80  }
81  else
82  {
83  cm = arg.sparse_complex_matrix_value ();
84  csm.nzmax = cm.nnz ();
85  csm.p = to_suitesparse_intptr (cm.xcidx ());
86  csm.i = to_suitesparse_intptr (cm.xridx ());
87  }
88 
89  if (nargout <= 1 || rank)
90  {
91  suitesparse_integer *jmatch = CXSPARSE_NAME (_maxtrans) (&csm, 0);
92  if (rank)
93  {
94  octave_idx_type r = 0;
95  for (octave_idx_type i = 0; i < nc; i++)
96  if (jmatch[nr+i] >= 0)
97  r++;
98  retval(0) = static_cast<double> (r);
99  }
100  else
101  retval(0) = put_int (jmatch + nr, nc);
102  CXSPARSE_NAME (_free) (jmatch);
103  }
104  else
105  {
106  CXSPARSE_NAME (d) *dm = CXSPARSE_NAME(_dmperm) (&csm, 0);
107 
108  retval = ovl (put_int (dm->p, nr), put_int (dm->q, nc),
109  put_int (dm->r, dm->nb+1), put_int (dm->s, dm->nb+1),
110  put_int (dm->cc, 5), put_int (dm->rr, 5));
111 
112  CXSPARSE_NAME (_dfree) (dm);
113  }
114 
115  return retval;
116 }
117 
118 #endif
119 
120 // NOTE: the docstring for dmperm is adapted from the text found in the
121 // file cs_dmperm.m that is distributed with the CSparse portion of the
122 // SuiteSparse library, version 5.6.0. CSparse is distributed under the
123 // terms of the LGPL v2.1 or any later version.
124 
125 DEFUN (dmperm, args, nargout,
126  doc: /* -*- texinfo -*-
127 @deftypefn {} {@var{p} =} dmperm (@var{A})
128 @deftypefnx {} {[@var{p}, @var{q}, @var{r}, @var{s}, @var{cc}, @var{rr}] =} dmperm (@var{A})
129 
130 @cindex @nospell{Dulmage-Mendelsohn} decomposition
131 Perform a @nospell{Dulmage-Mendelsohn} permutation of the sparse matrix
132 @var{A}.
133 
134 With a single output argument @code{dmperm}, return a maximum matching @var{p}
135 such that @code{p(j) = i} if column @var{j} is matched to row @var{i}, or 0 if
136 column @var{j} is unmatched. If @var{A} is square and full structural rank,
137 @var{p} is a row permutation and @code{A(p,:)} has a zero-free diagonal. The
138 structural rank of @var{A} is @code{sprank(A) = sum(p>0)}.
139 
140 Called with two or more output arguments, return the
141 @nospell{Dulmage-Mendelsohn} decomposition of @var{A}. @var{p} and @var{q} are
142 permutation vectors. @var{cc} and @var{rr} are vectors of length 5.
143 @code{c = A(p,q)} is split into a 4-by-4 set of coarse blocks:
144 
145 @example
146 @group
147  A11 A12 A13 A14
148  0 0 A23 A24
149  0 0 0 A34
150  0 0 0 A44
151 @end group
152 @end example
153 
154 @noindent
155 where @code{A12}, @code{A23}, and @code{A34} are square with zero-free
156 diagonals. The columns of @code{A11} are the unmatched columns, and the rows
157 of @code{A44} are the unmatched rows. Any of these blocks can be empty. In
158 the "coarse" decomposition, the (i,j)-th block is
159 @code{C(rr(i):rr(i+1)-1,cc(j):cc(j+1)-1)}. In terms of a linear system,
160 @code{[A11 A12]} is the underdetermined part of the system (it is always
161 rectangular and with more columns and rows, or 0-by-0), @code{A23} is the
162 well-determined part of the system (it is always square), and
163 @code{[A34 ; A44]} is the over-determined part of the system (it is always
164 rectangular with more rows than columns, or 0-by-0).
165 
166 The structural rank of @var{A} is @code{sprank (A) = rr(4)-1}, which is an
167 upper bound on the numerical rank of @var{A}.
168 @code{sprank(A) = rank(full(sprand(A)))} with probability 1 in exact
169 arithmetic.
170 
171 The @code{A23} submatrix is further subdivided into block upper triangular form
172 via the "fine" decomposition (the strongly-connected components of @code{A23}).
173 If @var{A} is square and structurally non-singular, @code{A23} is the entire
174 matrix.
175 
176 @code{C(r(i):r(i+1)-1,s(j):s(j+1)-1)} is the (i,j)-th block of the fine
177 decomposition. The (1,1) block is the rectangular block @code{[A11 A12]},
178 unless this block is 0-by-0. The (b,b) block is the rectangular block
179 @code{[A34 ; A44]}, unless this block is 0-by-0, where @code{b = length(r)-1}.
180 All other blocks of the form @code{C(r(i):r(i+1)-1,s(i):s(i+1)-1)} are diagonal
181 blocks of @code{A23}, and are square with a zero-free diagonal.
182 
183 The method used is described in: @nospell{A. Pothen & C.-J. Fan.}
184 @cite{Computing the Block Triangular Form of a Sparse Matrix}.
185 @nospell{ACM} Trans.@: Math.@: Software, 16(4):303--324, 1990.
186 @seealso{colamd, ccolamd}
187 @end deftypefn */)
188 {
189 #if defined (HAVE_CXSPARSE)
190 
191  if (args.length () != 1)
192  print_usage ();
193 
194  return dmperm_internal (false, args(0), nargout);
195 
196 #else
197 
198  octave_unused_parameter (args);
199  octave_unused_parameter (nargout);
200 
201  err_disabled_feature ("dmperm", "CXSparse");
202 
203 #endif
204 }
205 
206 /*
207 %!testif HAVE_CXSPARSE
208 %! n = 20;
209 %! a = speye (n,n);
210 %! a = a(randperm (n),:);
211 %! assert (a(dmperm (a),:), speye (n));
212 
213 %!testif HAVE_CXSPARSE
214 %! n = 20;
215 %! d = 0.2;
216 %! a = tril (sprandn (n,n,d), -1) + speye (n,n);
217 %! a = a(randperm (n), randperm (n));
218 %! [p,q,r,s] = dmperm (a);
219 %! assert (tril (a(p,q), -1), sparse (n, n));
220 */
221 
222 DEFUN (sprank, args, nargout,
223  doc: /* -*- texinfo -*-
224 @deftypefn {} {@var{p} =} sprank (@var{S})
225 @cindex structural rank
226 
227 Calculate the structural rank of the sparse matrix @var{S}.
228 
229 Note that only the structure of the matrix is used in this calculation based
230 on a @nospell{Dulmage-Mendelsohn} permutation to block triangular form. As
231 such the numerical rank of the matrix @var{S} is bounded by
232 @code{sprank (@var{S}) >= rank (@var{S})}. Ignoring floating point errors
233 @code{sprank (@var{S}) == rank (@var{S})}.
234 @seealso{dmperm}
235 @end deftypefn */)
236 {
237 #if defined (HAVE_CXSPARSE)
238 
239  if (args.length () != 1)
240  print_usage ();
241 
242  return dmperm_internal (true, args(0), nargout);
243 
244 #else
245 
246  octave_unused_parameter (args);
247  octave_unused_parameter (nargout);
248 
249  err_disabled_feature ("sprank", "CXSparse");
250 
251 #endif
252 }
253 
254 /*
255 %!testif HAVE_CXSPARSE
256 %! assert (sprank (speye (20)), 20);
257 %!testif HAVE_CXSPARSE
258 %! assert (sprank ([1,0,2,0;2,0,4,0]), 2);
259 
260 %!error sprank (1,2)
261 */
262 
263 OCTAVE_END_NAMESPACE(octave)
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:524
octave_idx_type * xcidx()
Definition: Sparse.h:602
octave_idx_type nzmax() const
Amount of storage for nonzero elements.
Definition: Sparse.h:336
octave_idx_type nnz() const
Actual number of nonzero terms.
Definition: Sparse.h:339
octave_idx_type * xridx()
Definition: Sparse.h:589
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:900
octave_idx_type rows() const
Definition: ov.h:545
octave_idx_type columns() const
Definition: ov.h:547
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:904
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
#define CXSPARSE_NAME(name)
Definition: dmperm.cc:46
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
T * r
Definition: mx-inlines.cc:781
suitesparse_integer * to_suitesparse_intptr(octave_idx_type *i)
Definition: oct-sparse.cc:51
int suitesparse_integer
Definition: oct-sparse.h:184
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219