GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
symrcm.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2007-2018 Michael Weitzel
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 /*
24 An implementation of the Reverse Cuthill-McKee algorithm (symrcm)
25 
26 The implementation of this algorithm is based in the descriptions found in
27 
28 @INPROCEEDINGS{,
29  author = {E. Cuthill and J. McKee},
30  title = {Reducing the Bandwidth of Sparse Symmetric Matrices},
31  booktitle = {Proceedings of the 24th ACM National Conference},
32  publisher = {Brandon Press},
33  pages = {157 -- 172},
34  location = {New Jersey},
35  year = {1969}
36 }
37 
38 @BOOK{,
39  author = {Alan George and Joseph W. H. Liu},
40  title = {Computer Solution of Large Sparse Positive Definite Systems},
41  publisher = {Prentice Hall Series in Computational Mathematics},
42  ISBN = {0-13-165274-5},
43  year = {1981}
44 }
45 
46 The algorithm represents a heuristic approach to the NP-complete minimum
47 bandwidth problem.
48 
49 Written by Michael Weitzel <michael.weitzel@@uni-siegen.de>
50  <weitzel@@ldknet.org>
51 */
52 
53 #if defined (HAVE_CONFIG_H)
54 # include "config.h"
55 #endif
56 
57 #include <algorithm>
58 
59 #include "CSparse.h"
60 #include "boolNDArray.h"
61 #include "dNDArray.h"
62 #include "dSparse.h"
63 #include "oct-locbuf.h"
64 #include "oct-sparse.h"
65 #include "quit.h"
66 
67 #include "defun-dld.h"
68 #include "errwarn.h"
69 #include "ov.h"
70 #include "ovl.h"
71 
72 // A node struct for the Cuthill-McKee algorithm
73 struct CMK_Node
74 {
75  // the node's id (matrix row index)
77  // the node's degree
79  // minimal distance to the root of the spanning tree
81 };
82 
83 // A simple queue.
84 // Queues Q have a fixed maximum size N (rows,cols of the matrix) and are
85 // stored in an array. qh and qt point to queue head and tail.
86 
87 // Enqueue operation (adds a node "o" at the tail)
88 
89 inline static void
91 {
92  Q[qt] = o;
93  qt = (qt + 1) % (N + 1);
94 }
95 
96 // Dequeue operation (removes a node from the head)
97 
98 inline static CMK_Node
100 {
101  CMK_Node r = Q[qh];
102  qh = (qh + 1) % (N + 1);
103  return r;
104 }
105 
106 // Predicate (queue empty)
107 #define Q_empty(Q, N, qh, qt) ((qh) == (qt))
108 
109 // A simple, array-based binary heap (used as a priority queue for nodes)
110 
111 // the left descendant of entry i
112 #define LEFT(i) (((i) << 1) + 1) // = (2*(i)+1)
113 // the right descendant of entry i
114 #define RIGHT(i) (((i) << 1) + 2) // = (2*(i)+2)
115 // the parent of entry i
116 #define PARENT(i) (((i) - 1) >> 1) // = floor(((i)-1)/2)
117 
118 // Builds a min-heap (the root contains the smallest element). A is an array
119 // with the graph's nodes, i is a starting position, size is the length of A.
120 
121 static void
123 {
124  octave_idx_type j = i;
125  for (;;)
126  {
127  octave_idx_type l = LEFT(j);
128  octave_idx_type r = RIGHT(j);
129 
130  octave_idx_type smallest;
131  if (l < size && A[l].deg < A[j].deg)
132  smallest = l;
133  else
134  smallest = j;
135 
136  if (r < size && A[r].deg < A[smallest].deg)
137  smallest = r;
138 
139  if (smallest != j)
140  {
141  std::swap (A[j], A[smallest]);
142  j = smallest;
143  }
144  else
145  break;
146  }
147 }
148 
149 // Heap operation insert. Running time is O(log(n))
150 
151 static void
153 {
154  octave_idx_type i = h++;
155 
156  H[i] = o;
157 
158  if (i == 0)
159  return;
160  do
161  {
163  if (H[i].deg < H[p].deg)
164  {
165  std::swap (H[i], H[p]);
166 
167  i = p;
168  }
169  else
170  break;
171  }
172  while (i > 0);
173 }
174 
175 // Heap operation remove-min. Removes the smalles element in O(1) and
176 // reorganizes the heap optionally in O(log(n))
177 
178 inline static CMK_Node
179 H_remove_min (CMK_Node *H, octave_idx_type& h, int reorg/*=1*/)
180 {
181  CMK_Node r = H[0];
182  H[0] = H[--h];
183  if (reorg)
184  H_heapify_min (H, 0, h);
185  return r;
186 }
187 
188 // Predicate (heap empty)
189 #define H_empty(H, h) ((h) == 0)
190 
191 // Helper function for the Cuthill-McKee algorithm. Tries to determine a
192 // pseudo-peripheral node of the graph as starting node.
193 
194 static octave_idx_type
196  const octave_idx_type *cidx, const octave_idx_type *ridx2,
197  const octave_idx_type *cidx2, octave_idx_type *D,
199 {
200  CMK_Node w;
201 
203  boolNDArray btmp (dim_vector (1, N), false);
204  bool *visit = btmp.fortran_vec ();
205 
206  octave_idx_type qh = 0;
207  octave_idx_type qt = 0;
208  CMK_Node x;
209  x.id = start;
210  x.deg = D[start];
211  x.dist = 0;
212  Q_enq (Q, N, qt, x);
213  visit[start] = true;
214 
215  // distance level
216  octave_idx_type level = 0;
217  // current largest "eccentricity"
218  octave_idx_type max_dist = 0;
219 
220  for (;;)
221  {
222  while (! Q_empty (Q, N, qh, qt))
223  {
224  CMK_Node v = Q_deq (Q, N, qh);
225 
226  if (v.dist > x.dist || (v.id != x.id && v.deg > x.deg))
227  x = v;
228 
229  octave_idx_type i = v.id;
230 
231  // add all unvisited neighbors to the queue
232  octave_idx_type j1 = cidx[i];
233  octave_idx_type j2 = cidx2[i];
234  while (j1 < cidx[i+1] || j2 < cidx2[i+1])
235  {
236  octave_quit ();
237 
238  if (j1 == cidx[i+1])
239  {
240  octave_idx_type r2 = ridx2[j2++];
241  if (! visit[r2])
242  {
243  // the distance of node j is dist(i)+1
244  w.id = r2;
245  w.deg = D[r2];
246  w.dist = v.dist+1;
247  Q_enq (Q, N, qt, w);
248  visit[r2] = true;
249 
250  if (w.dist > level)
251  level = w.dist;
252  }
253  }
254  else if (j2 == cidx2[i+1])
255  {
256  octave_idx_type r1 = ridx[j1++];
257  if (! visit[r1])
258  {
259  // the distance of node j is dist(i)+1
260  w.id = r1;
261  w.deg = D[r1];
262  w.dist = v.dist+1;
263  Q_enq (Q, N, qt, w);
264  visit[r1] = true;
265 
266  if (w.dist > level)
267  level = w.dist;
268  }
269  }
270  else
271  {
272  octave_idx_type r1 = ridx[j1];
273  octave_idx_type r2 = ridx2[j2];
274  if (r1 <= r2)
275  {
276  if (! visit[r1])
277  {
278  w.id = r1;
279  w.deg = D[r1];
280  w.dist = v.dist+1;
281  Q_enq (Q, N, qt, w);
282  visit[r1] = true;
283 
284  if (w.dist > level)
285  level = w.dist;
286  }
287  j1++;
288  if (r1 == r2)
289  j2++;
290  }
291  else
292  {
293  if (! visit[r2])
294  {
295  w.id = r2;
296  w.deg = D[r2];
297  w.dist = v.dist+1;
298  Q_enq (Q, N, qt, w);
299  visit[r2] = true;
300 
301  if (w.dist > level)
302  level = w.dist;
303  }
304  j2++;
305  }
306  }
307  }
308  } // finish of BFS
309 
310  if (max_dist < x.dist)
311  {
312  max_dist = x.dist;
313 
314  for (octave_idx_type i = 0; i < N; i++)
315  visit[i] = false;
316 
317  visit[x.id] = true;
318  x.dist = 0;
319  qt = qh = 0;
320  Q_enq (Q, N, qt, x);
321  }
322  else
323  break;
324  }
325  return x.id;
326 }
327 
328 // Calculates the node's degrees. This means counting the nonzero elements
329 // in the symmetric matrix' rows. This works for non-symmetric matrices
330 // as well.
331 
332 static octave_idx_type
334  const octave_idx_type *cidx, octave_idx_type *D)
335 {
336  octave_idx_type max_deg = 0;
337 
338  for (octave_idx_type i = 0; i < N; i++)
339  D[i] = 0;
340 
341  for (octave_idx_type j = 0; j < N; j++)
342  {
343  for (octave_idx_type i = cidx[j]; i < cidx[j+1]; i++)
344  {
345  octave_quit ();
346 
347  octave_idx_type k = ridx[i];
348  // there is a nonzero element (k,j)
349  D[k]++;
350  if (D[k] > max_deg)
351  max_deg = D[k];
352  // if there is no element (j,k) there is one in
353  // the symmetric matrix:
354  if (k != j)
355  {
356  bool found = false;
357  for (octave_idx_type l = cidx[k]; l < cidx[k + 1]; l++)
358  {
359  octave_quit ();
360 
361  if (ridx[l] == j)
362  {
363  found = true;
364  break;
365  }
366  else if (ridx[l] > j)
367  break;
368  }
369 
370  if (! found)
371  {
372  // A(j,k) == 0
373  D[j]++;
374  if (D[j] > max_deg)
375  max_deg = D[j];
376  }
377  }
378  }
379  }
380  return max_deg;
381 }
382 
383 // Transpose of the structure of a square sparse matrix
384 
385 static void
387  const octave_idx_type *cidx, octave_idx_type *ridx2,
388  octave_idx_type *cidx2)
389 {
390  octave_idx_type nz = cidx[N];
391 
393  for (octave_idx_type i = 0; i < N; i++)
394  w[i] = 0;
395  for (octave_idx_type i = 0; i < nz; i++)
396  w[ridx[i]]++;
397  nz = 0;
398  for (octave_idx_type i = 0; i < N; i++)
399  {
400  octave_quit ();
401 
402  cidx2[i] = nz;
403  nz += w[i];
404  w[i] = cidx2[i];
405  }
406  cidx2[N] = nz;
407  w[N] = nz;
408 
409  for (octave_idx_type j = 0; j < N; j++)
410  for (octave_idx_type k = cidx[j]; k < cidx[j + 1]; k++)
411  {
412  octave_quit ();
413 
414  octave_idx_type q = w[ridx[k]]++;
415  ridx2[q] = j;
416  }
417 }
418 
419 // An implementation of the Cuthill-McKee algorithm.
420 DEFUN_DLD (symrcm, args, ,
421  doc: /* -*- texinfo -*-
422 @deftypefn {} {@var{p} =} symrcm (@var{S})
423 Return the symmetric reverse @nospell{Cuthill-McKee} permutation of @var{S}.
424 
425 @var{p} is a permutation vector such that
426 @code{@var{S}(@var{p}, @var{p})} tends to have its diagonal elements closer
427 to the diagonal than @var{S}. This is a good preordering for LU or
428 Cholesky@tie{}factorization of matrices that come from ``long, skinny''
429 problems. It works for both symmetric and asymmetric @var{S}.
430 
431 The algorithm represents a heuristic approach to the NP-complete bandwidth
432 minimization problem. The implementation is based in the descriptions found
433 in
434 
435 @nospell{E. Cuthill, J. McKee}.
436 @cite{Reducing the Bandwidth of Sparse Symmetric Matrices}.
437 Proceedings of the 24th @nospell{ACM} National Conference,
438 157--172 1969, Brandon Press, New Jersey.
439 
440 @nospell{A. George, J.W.H. Liu}. @cite{Computer Solution of Large Sparse
441 Positive Definite Systems}, Prentice Hall Series in Computational
442 Mathematics, ISBN 0-13-165274-5, 1981.
443 
444 @seealso{colperm, colamd, symamd}
445 @end deftypefn */)
446 {
447  if (args.length () != 1)
448  print_usage ();
449 
450  octave_value arg = args(0);
451 
452  // the parameter of the matrix is converted into a sparse matrix
453  //(if necessary)
454  octave_idx_type *cidx;
455  octave_idx_type *ridx;
456  SparseMatrix Ar;
458 
459  if (arg.isreal ())
460  {
461  Ar = arg.sparse_matrix_value ();
462  // Note cidx/ridx are const, so use xridx and xcidx...
463  cidx = Ar.xcidx ();
464  ridx = Ar.xridx ();
465  }
466  else
467  {
469  cidx = Ac.xcidx ();
470  ridx = Ac.xridx ();
471  }
472 
473  octave_idx_type nr = arg.rows ();
474  octave_idx_type nc = arg.columns ();
475 
476  if (nr != nc)
477  err_square_matrix_required ("symrcm", "S");
478 
479  if (nr == 0 && nc == 0)
480  return ovl (NDArray (dim_vector (1, 0)));
481 
482  // sizes of the heaps
483  octave_idx_type s = 0;
484 
485  // head- and tail-indices for the queue
486  octave_idx_type qt = 0;
487  octave_idx_type qh = 0;
488  CMK_Node v, w;
489  // dimension of the matrix
490  octave_idx_type N = nr;
491 
492  OCTAVE_LOCAL_BUFFER (octave_idx_type, cidx2, N + 1);
493  OCTAVE_LOCAL_BUFFER (octave_idx_type, ridx2, cidx[N]);
494  transpose (N, ridx, cidx, ridx2, cidx2);
495 
496  // the permutation vector
497  NDArray P (dim_vector (1, N));
498 
499  // compute the node degrees
501  octave_idx_type max_deg = calc_degrees (N, ridx, cidx, D);
502 
503  // if none of the nodes has a degree > 0 (a matrix of zeros)
504  // the return value corresponds to the identity permutation
505  if (max_deg == 0)
506  {
507  for (octave_idx_type i = 0; i < N; i++)
508  P(i) = i;
509 
510  return ovl (P);
511  }
512 
513  // a heap for the a node's neighbors. The number of neighbors is
514  // limited by the maximum degree max_deg:
515  OCTAVE_LOCAL_BUFFER (CMK_Node, S, max_deg);
516 
517  // a queue for the BFS. The array is always one element larger than
518  // the number of entries that are stored.
520 
521  // a counter (for building the permutation)
522  octave_idx_type c = -1;
523 
524  // upper bound for the bandwidth (=quality of solution)
525  // initialize the bandwidth of the graph with 0. B contains the
526  // the maximum of the theoretical lower limits of the subgraphs
527  // bandwidths.
528  octave_idx_type B = 0;
529 
530  // mark all nodes as unvisited; with the exception of the nodes
531  // that have degree==0 and build a CC of the graph.
532 
533  boolNDArray btmp (dim_vector (1, N), false);
534  bool *visit = btmp.fortran_vec ();
535 
536  do
537  {
538  // locate an unvisited starting node of the graph
540  for (i = 0; i < N; i++)
541  if (! visit[i])
542  break;
543 
544  // locate a probably better starting node
545  v.id = find_starting_node (N, ridx, cidx, ridx2, cidx2, D, i);
546 
547  // mark the node as visited and enqueue it (a starting node
548  // for the BFS). Since the node will be a root of a spanning
549  // tree, its dist is 0.
550  v.deg = D[v.id];
551  v.dist = 0;
552  visit[v.id] = true;
553  Q_enq (Q, N, qt, v);
554 
555  // lower bound for the bandwidth of a subgraph
556  // keep a "level" in the spanning tree (= min. distance to the
557  // root) for determining the bandwidth of the computed
558  // permutation P
559  octave_idx_type Bsub = 0;
560  // min. dist. to the root is 0
561  octave_idx_type level = 0;
562  // the root is the first/only node on level 0
563  octave_idx_type level_N = 1;
564 
565  while (! Q_empty (Q, N, qh, qt))
566  {
567  v = Q_deq (Q, N, qh);
568  i = v.id;
569 
570  c++;
571 
572  // for computing the inverse permutation P where
573  // A(inv(P),inv(P)) or P'*A*P is banded
574  // P(i) = c;
575 
576  // for computing permutation P where
577  // A(P(i),P(j)) or P*A*P' is banded
578  P(c) = i;
579 
580  // put all unvisited neighbors j of node i on the heap
581  s = 0;
582  octave_idx_type j1 = cidx[i];
583  octave_idx_type j2 = cidx2[i];
584 
585  octave_quit ();
586 
587  while (j1 < cidx[i+1] || j2 < cidx2[i+1])
588  {
589  octave_quit ();
590 
591  if (j1 == cidx[i+1])
592  {
593  octave_idx_type r2 = ridx2[j2++];
594  if (! visit[r2])
595  {
596  // the distance of node j is dist(i)+1
597  w.id = r2;
598  w.deg = D[r2];
599  w.dist = v.dist+1;
600  H_insert (S, s, w);
601  visit[r2] = true;
602  }
603  }
604  else if (j2 == cidx2[i+1])
605  {
606  octave_idx_type r1 = ridx[j1++];
607  if (! visit[r1])
608  {
609  w.id = r1;
610  w.deg = D[r1];
611  w.dist = v.dist+1;
612  H_insert (S, s, w);
613  visit[r1] = true;
614  }
615  }
616  else
617  {
618  octave_idx_type r1 = ridx[j1];
619  octave_idx_type r2 = ridx2[j2];
620  if (r1 <= r2)
621  {
622  if (! visit[r1])
623  {
624  w.id = r1;
625  w.deg = D[r1];
626  w.dist = v.dist+1;
627  H_insert (S, s, w);
628  visit[r1] = true;
629  }
630  j1++;
631  if (r1 == r2)
632  j2++;
633  }
634  else
635  {
636  if (! visit[r2])
637  {
638  w.id = r2;
639  w.deg = D[r2];
640  w.dist = v.dist+1;
641  H_insert (S, s, w);
642  visit[r2] = true;
643  }
644  j2++;
645  }
646  }
647  }
648 
649  // add the neighbors to the queue (sorted by node degree)
650  while (! H_empty (S, s))
651  {
652  octave_quit ();
653 
654  // locate a neighbor of i with minimal degree in O(log(N))
655  v = H_remove_min (S, s, 1);
656 
657  // entered the BFS a new level?
658  if (v.dist > level)
659  {
660  // adjustment of bandwith:
661  // "[...] the minimum bandwidth that
662  // can be obtained [...] is the
663  // maximum number of nodes per level"
664  if (Bsub < level_N)
665  Bsub = level_N;
666 
667  level = v.dist;
668  // v is the first node on the new level
669  level_N = 1;
670  }
671  else
672  {
673  // there is no new level but another node on
674  // this level:
675  level_N++;
676  }
677 
678  // enqueue v in O(1)
679  Q_enq (Q, N, qt, v);
680  }
681 
682  // synchronize the bandwidth with level_N once again:
683  if (Bsub < level_N)
684  Bsub = level_N;
685  }
686  // finish of BFS. If there are still unvisited nodes in the graph
687  // then it is split into CCs. The computed bandwidth is the maximum
688  // of all subgraphs. Update:
689  if (Bsub > B)
690  B = Bsub;
691  }
692  // are there any nodes left?
693  while (c+1 < N);
694 
695  // compute the reverse-ordering
696  s = N / 2 - 1;
697  for (octave_idx_type i = 0, j = N - 1; i <= s; i++, j--)
698  std::swap (P.elem (i), P.elem (j));
699 
700  // increment all indices, since Octave is not C
701  return ovl (P+1);
702 }
is already an absolute the name is checked against the file system instead of Octave s loadpath In this if otherwise an empty string is returned If the first argument is a cell array of search each directory of the loadpath for element of the cell array and return the first that matches If the second optional argument return a cell array containing the list of all files that have the same name in the path If no files are found
Definition: utils.cc:305
F77_RET_T const F77_INT F77_CMPLX const F77_INT F77_CMPLX * B
static void Q_enq(CMK_Node *Q, octave_idx_type N, octave_idx_type &qt, const CMK_Node &o)
Definition: symrcm.cc:90
static octave_idx_type calc_degrees(octave_idx_type N, const octave_idx_type *ridx, const octave_idx_type *cidx, octave_idx_type *D)
Definition: symrcm.cc:333
static void H_insert(CMK_Node *H, octave_idx_type &h, const CMK_Node &o)
Definition: symrcm.cc:152
F77_RET_T const F77_INT & N
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
for large enough k
Definition: lu.cc:617
const T * fortran_vec(void) const
Definition: Array.h:584
static void transpose(octave_idx_type N, const octave_idx_type *ridx, const octave_idx_type *cidx, octave_idx_type *ridx2, octave_idx_type *cidx2)
Definition: symrcm.cc:386
octave_idx_type deg
Definition: symrcm.cc:78
#define RIGHT(i)
Definition: symrcm.cc:114
T & elem(octave_idx_type n)
Definition: Array.h:488
void err_square_matrix_required(const char *fcn, const char *name)
Definition: errwarn.cc:118
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
s
Definition: file-io.cc:2729
octave_value arg
Definition: pr-output.cc:3244
static octave_idx_type find_starting_node(octave_idx_type N, const octave_idx_type *ridx, const octave_idx_type *cidx, const octave_idx_type *ridx2, const octave_idx_type *cidx2, octave_idx_type *D, octave_idx_type start)
Definition: symrcm.cc:195
bool swap
Definition: load-save.cc:738
double h
Definition: graphics.cc:11808
F77_RET_T const F77_INT F77_CMPLX * A
octave_idx_type columns(void) const
Definition: ov.h:474
std::complex< double > w(std::complex< double > z, double relerr=0)
#define PARENT(i)
Definition: symrcm.cc:116
octave_idx_type rows(void) const
Definition: ov.h:472
octave_idx_type * xridx(void)
Definition: Sparse.h:501
#define LEFT(i)
Definition: symrcm.cc:112
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:885
bool isreal(void) const
Definition: ov.h:703
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
octave::sys::time start
Definition: graphics.cc:12337
OCTAVE_EXPORT octave_value_list isa nd deftypefn *return ovl(args(0).isinteger())
octave_idx_type dist
Definition: symrcm.cc:80
p
Definition: lu.cc:138
#define Q_empty(Q, N, qh, qt)
Definition: symrcm.cc:107
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:881
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:41
for i
Definition: data.cc:5264
static CMK_Node Q_deq(CMK_Node *Q, octave_idx_type N, octave_idx_type &qh)
Definition: symrcm.cc:99
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin function.
Definition: defun-dld.h:58
octave_idx_type * xcidx(void)
Definition: Sparse.h:514
octave_idx_type id
Definition: symrcm.cc:76
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:87
static CMK_Node H_remove_min(CMK_Node *H, octave_idx_type &h, int reorg)
Definition: symrcm.cc:179
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE * x
#define H_empty(H, h)
Definition: symrcm.cc:189
static void H_heapify_min(CMK_Node *A, octave_idx_type i, octave_idx_type size)
Definition: symrcm.cc:122