src/glpnet01.c
author Alpar Juttner <alpar@cs.elte.hu>
Sun, 05 Dec 2010 17:35:23 +0100
changeset 2 4c8956a7bdf4
permissions -rw-r--r--
Set up CMAKE build environment
alpar@1
     1
/* glpnet01.c (permutations for zero-free diagonal) */
alpar@1
     2
alpar@1
     3
/***********************************************************************
alpar@1
     4
*  This code is part of GLPK (GNU Linear Programming Kit).
alpar@1
     5
*
alpar@1
     6
*  This code is the result of translation of the Fortran subroutines
alpar@1
     7
*  MC21A and MC21B associated with the following paper:
alpar@1
     8
*
alpar@1
     9
*  I.S.Duff, Algorithm 575: Permutations for zero-free diagonal, ACM
alpar@1
    10
*  Trans. on Math. Softw. 7 (1981), 387-390.
alpar@1
    11
*
alpar@1
    12
*  Use of ACM Algorithms is subject to the ACM Software Copyright and
alpar@1
    13
*  License Agreement. See <http://www.acm.org/publications/policies>.
alpar@1
    14
*
alpar@1
    15
*  The translation was made by Andrew Makhorin <mao@gnu.org>.
alpar@1
    16
*
alpar@1
    17
*  GLPK is free software: you can redistribute it and/or modify it
alpar@1
    18
*  under the terms of the GNU General Public License as published by
alpar@1
    19
*  the Free Software Foundation, either version 3 of the License, or
alpar@1
    20
*  (at your option) any later version.
alpar@1
    21
*
alpar@1
    22
*  GLPK is distributed in the hope that it will be useful, but WITHOUT
alpar@1
    23
*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
alpar@1
    24
*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
alpar@1
    25
*  License for more details.
alpar@1
    26
*
alpar@1
    27
*  You should have received a copy of the GNU General Public License
alpar@1
    28
*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
alpar@1
    29
***********************************************************************/
alpar@1
    30
alpar@1
    31
#include "glpnet.h"
alpar@1
    32
alpar@1
    33
/***********************************************************************
alpar@1
    34
*  NAME
alpar@1
    35
*
alpar@1
    36
*  mc21a - permutations for zero-free diagonal
alpar@1
    37
*
alpar@1
    38
*  SYNOPSIS
alpar@1
    39
*
alpar@1
    40
*  #include "glpnet.h"
alpar@1
    41
*  int mc21a(int n, const int icn[], const int ip[], const int lenr[],
alpar@1
    42
*     int iperm[], int pr[], int arp[], int cv[], int out[]);
alpar@1
    43
*
alpar@1
    44
*  DESCRIPTION
alpar@1
    45
*
alpar@1
    46
*  Given the pattern of nonzeros of a sparse matrix, the routine mc21a
alpar@1
    47
*  attempts to find a permutation of its rows that makes the matrix have
alpar@1
    48
*  no zeros on its diagonal.
alpar@1
    49
*
alpar@1
    50
*  INPUT PARAMETERS
alpar@1
    51
*
alpar@1
    52
*  n     order of matrix.
alpar@1
    53
*
alpar@1
    54
*  icn   array containing the column indices of the non-zeros. Those
alpar@1
    55
*        belonging to a single row must be contiguous but the ordering
alpar@1
    56
*        of column indices within each row is unimportant and wasted
alpar@1
    57
*        space between rows is permitted.
alpar@1
    58
*
alpar@1
    59
*  ip    ip[i], i = 1,2,...,n, is the position in array icn of the
alpar@1
    60
*        first column index of a non-zero in row i.
alpar@1
    61
*
alpar@1
    62
*  lenr  lenr[i], i = 1,2,...,n, is the number of non-zeros in row i.
alpar@1
    63
*
alpar@1
    64
*  OUTPUT PARAMETER
alpar@1
    65
*
alpar@1
    66
*  iperm contains permutation to make diagonal have the smallest
alpar@1
    67
*        number of zeros on it. Elements (iperm[i], i), i = 1,2,...,n,
alpar@1
    68
*        are non-zero at the end of the algorithm unless the matrix is
alpar@1
    69
*        structurally singular. In this case, (iperm[i], i) will be
alpar@1
    70
*        zero for n - numnz entries.
alpar@1
    71
*
alpar@1
    72
*  WORKING ARRAYS
alpar@1
    73
*
alpar@1
    74
*  pr    working array of length [1+n], where pr[0] is not used.
alpar@1
    75
*        pr[i] is the previous row to i in the depth first search.
alpar@1
    76
*
alpar@1
    77
*  arp   working array of length [1+n], where arp[0] is not used.
alpar@1
    78
*        arp[i] is one less than the number of non-zeros in row i which
alpar@1
    79
*        have not been scanned when looking for a cheap assignment.
alpar@1
    80
*
alpar@1
    81
*  cv    working array of length [1+n], where cv[0] is not used.
alpar@1
    82
*        cv[i] is the most recent row extension at which column i was
alpar@1
    83
*        visited.
alpar@1
    84
*
alpar@1
    85
*  out   working array of length [1+n], where out[0] is not used.
alpar@1
    86
*        out[i] is one less than the number of non-zeros in row i
alpar@1
    87
*        which have not been scanned during one pass through the main
alpar@1
    88
*        loop.
alpar@1
    89
*
alpar@1
    90
*  RETURNS
alpar@1
    91
*
alpar@1
    92
*  The routine mc21a returns numnz, the number of non-zeros on diagonal
alpar@1
    93
*  of permuted matrix. */
alpar@1
    94
alpar@1
    95
int mc21a(int n, const int icn[], const int ip[], const int lenr[],
alpar@1
    96
      int iperm[], int pr[], int arp[], int cv[], int out[])
alpar@1
    97
{     int i, ii, in1, in2, j, j1, jord, k, kk, numnz;
alpar@1
    98
      /* Initialization of arrays. */
alpar@1
    99
      for (i = 1; i <= n; i++)
alpar@1
   100
      {  arp[i] = lenr[i] - 1;
alpar@1
   101
         cv[i] = iperm[i] = 0;
alpar@1
   102
      }
alpar@1
   103
      numnz = 0;
alpar@1
   104
      /* Main loop. */
alpar@1
   105
      /* Each pass round this loop either results in a new assignment
alpar@1
   106
         or gives a row with no assignment. */
alpar@1
   107
      for (jord = 1; jord <= n; jord++)
alpar@1
   108
      {  j = jord;
alpar@1
   109
         pr[j] = -1;
alpar@1
   110
         for (k = 1; k <= jord; k++)
alpar@1
   111
         {  /* Look for a cheap assignment. */
alpar@1
   112
            in1 = arp[j];
alpar@1
   113
            if (in1 >= 0)
alpar@1
   114
            {  in2 = ip[j] + lenr[j] - 1;
alpar@1
   115
               in1 = in2 - in1;
alpar@1
   116
               for (ii = in1; ii <= in2; ii++)
alpar@1
   117
               {  i = icn[ii];
alpar@1
   118
                  if (iperm[i] == 0) goto L110;
alpar@1
   119
               }
alpar@1
   120
               /* No cheap assignment in row. */
alpar@1
   121
               arp[j] = -1;
alpar@1
   122
            }
alpar@1
   123
            /* Begin looking for assignment chain starting with row j.*/
alpar@1
   124
            out[j] = lenr[j] - 1;
alpar@1
   125
            /* Inner loop. Extends chain by one or backtracks. */
alpar@1
   126
            for (kk = 1; kk <= jord; kk++)
alpar@1
   127
            {  in1 = out[j];
alpar@1
   128
               if (in1 >= 0)
alpar@1
   129
               {  in2 = ip[j] + lenr[j] - 1;
alpar@1
   130
                  in1 = in2 - in1;
alpar@1
   131
                  /* Forward scan. */
alpar@1
   132
                  for (ii = in1; ii <= in2; ii++)
alpar@1
   133
                  {  i = icn[ii];
alpar@1
   134
                     if (cv[i] != jord)
alpar@1
   135
                     {  /* Column i has not yet been accessed during
alpar@1
   136
                           this pass. */
alpar@1
   137
                        j1 = j;
alpar@1
   138
                        j = iperm[i];
alpar@1
   139
                        cv[i] = jord;
alpar@1
   140
                        pr[j] = j1;
alpar@1
   141
                        out[j1] = in2 - ii - 1;
alpar@1
   142
                        goto L100;
alpar@1
   143
                     }
alpar@1
   144
                  }
alpar@1
   145
               }
alpar@1
   146
               /* Backtracking step. */
alpar@1
   147
               j = pr[j];
alpar@1
   148
               if (j == -1) goto L130;
alpar@1
   149
            }
alpar@1
   150
L100:       ;
alpar@1
   151
         }
alpar@1
   152
L110:    /* New assignment is made. */
alpar@1
   153
         iperm[i] = j;
alpar@1
   154
         arp[j] = in2 - ii - 1;
alpar@1
   155
         numnz++;
alpar@1
   156
         for (k = 1; k <= jord; k++)
alpar@1
   157
         {  j = pr[j];
alpar@1
   158
            if (j == -1) break;
alpar@1
   159
            ii = ip[j] + lenr[j] - out[j] - 2;
alpar@1
   160
            i = icn[ii];
alpar@1
   161
            iperm[i] = j;
alpar@1
   162
         }
alpar@1
   163
L130:    ;
alpar@1
   164
      }
alpar@1
   165
      /* If matrix is structurally singular, we now complete the
alpar@1
   166
         permutation iperm. */
alpar@1
   167
      if (numnz < n)
alpar@1
   168
      {  for (i = 1; i <= n; i++)
alpar@1
   169
            arp[i] = 0;
alpar@1
   170
         k = 0;
alpar@1
   171
         for (i = 1; i <= n; i++)
alpar@1
   172
         {  if (iperm[i] == 0)
alpar@1
   173
               out[++k] = i;
alpar@1
   174
            else
alpar@1
   175
               arp[iperm[i]] = i;
alpar@1
   176
         }
alpar@1
   177
         k = 0;
alpar@1
   178
         for (i = 1; i <= n; i++)
alpar@1
   179
         {  if (arp[i] == 0)
alpar@1
   180
               iperm[out[++k]] = i;
alpar@1
   181
         }
alpar@1
   182
      }
alpar@1
   183
      return numnz;
alpar@1
   184
}
alpar@1
   185
alpar@1
   186
/**********************************************************************/
alpar@1
   187
alpar@1
   188
#if 0
alpar@1
   189
#include "glplib.h"
alpar@1
   190
alpar@1
   191
int sing;
alpar@1
   192
alpar@1
   193
void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum,
alpar@1
   194
      int iw[]);
alpar@1
   195
alpar@1
   196
void fa01bs(int max, int *nrand);
alpar@1
   197
alpar@1
   198
int main(void)
alpar@1
   199
{     /* test program for the routine mc21a */
alpar@1
   200
      /* these runs on random matrices cause all possible statements in
alpar@1
   201
         mc21a to be executed */
alpar@1
   202
      int i, iold, j, j1, j2, jj, knum, l, licn, n, nov4, num, numnz;
alpar@1
   203
      int ip[1+21], icn[1+1000], iperm[1+20], lenr[1+20], iw1[1+80];
alpar@1
   204
      licn = 1000;
alpar@1
   205
      /* run on random matrices of orders 1 through 20 */
alpar@1
   206
      for (n = 1; n <= 20; n++)
alpar@1
   207
      {  nov4 = n / 4;
alpar@1
   208
         if (nov4 < 1) nov4 = 1;
alpar@1
   209
L10:     fa01bs(nov4, &l);
alpar@1
   210
         knum = l * n;
alpar@1
   211
         /* knum is requested number of non-zeros in random matrix */
alpar@1
   212
         if (knum > licn) goto L10;
alpar@1
   213
         /* if sing is false, matrix is guaranteed structurally
alpar@1
   214
            non-singular */
alpar@1
   215
         sing = ((n / 2) * 2 == n);
alpar@1
   216
         /* call to subroutine to generate random matrix */
alpar@1
   217
         ranmat(n, n, icn, ip, n+1, &knum, iw1);
alpar@1
   218
         /* knum is now actual number of non-zeros in random matrix */
alpar@1
   219
         if (knum > licn) goto L10;
alpar@1
   220
         xprintf("n = %2d; nz = %4d; sing = %d\n", n, knum, sing);
alpar@1
   221
         /* set up array of row lengths */
alpar@1
   222
         for (i = 1; i <= n; i++)
alpar@1
   223
            lenr[i] = ip[i+1] - ip[i];
alpar@1
   224
         /* call to mc21a */
alpar@1
   225
         numnz = mc21a(n, icn, ip, lenr, iperm, &iw1[0], &iw1[n],
alpar@1
   226
            &iw1[n+n], &iw1[n+n+n]);
alpar@1
   227
         /* testing to see if there are numnz non-zeros on the diagonal
alpar@1
   228
            of the permuted matrix. */
alpar@1
   229
         num = 0;
alpar@1
   230
         for (i = 1; i <= n; i++)
alpar@1
   231
         {  iold = iperm[i];
alpar@1
   232
            j1 = ip[iold];
alpar@1
   233
            j2 = j1 + lenr[iold] - 1;
alpar@1
   234
            if (j2 < j1) continue;
alpar@1
   235
            for (jj = j1; jj <= j2; jj++)
alpar@1
   236
            {  j = icn[jj];
alpar@1
   237
               if (j == i)
alpar@1
   238
               {  num++;
alpar@1
   239
                  break;
alpar@1
   240
               }
alpar@1
   241
            }
alpar@1
   242
         }
alpar@1
   243
         if (num != numnz)
alpar@1
   244
            xprintf("Failure in mc21a, numnz = %d instead of %d\n",
alpar@1
   245
               numnz, num);
alpar@1
   246
      }
alpar@1
   247
      return 0;
alpar@1
   248
}
alpar@1
   249
alpar@1
   250
void ranmat(int m, int n, int icn[], int iptr[], int nnnp1, int *knum,
alpar@1
   251
      int iw[])
alpar@1
   252
{     /* subroutine to generate random matrix */
alpar@1
   253
      int i, ii, inum, j, lrow, matnum;
alpar@1
   254
      inum = (*knum / n) * 2;
alpar@1
   255
      if (inum > n-1) inum = n-1;
alpar@1
   256
      matnum = 1;
alpar@1
   257
      /* each pass through this loop generates a row of the matrix */
alpar@1
   258
      for (j = 1; j <= m; j++)
alpar@1
   259
      {  iptr[j] = matnum;
alpar@1
   260
         if (!(sing || j > n))
alpar@1
   261
            icn[matnum++] = j;
alpar@1
   262
         if (n == 1) continue;
alpar@1
   263
         for (i = 1; i <= n; i++) iw[i] = 0;
alpar@1
   264
         if (!sing) iw[j] = 1;
alpar@1
   265
         fa01bs(inum, &lrow);
alpar@1
   266
         lrow--;
alpar@1
   267
         if (lrow == 0) continue;
alpar@1
   268
         /* lrow off-diagonal non-zeros in row j of the matrix */
alpar@1
   269
         for (ii = 1; ii <= lrow; ii++)
alpar@1
   270
         {  for (;;)
alpar@1
   271
            {  fa01bs(n, &i);
alpar@1
   272
               if (iw[i] != 1) break;
alpar@1
   273
            }
alpar@1
   274
            iw[i] = 1;
alpar@1
   275
            icn[matnum++] = i;
alpar@1
   276
         }
alpar@1
   277
      }
alpar@1
   278
      for (i = m+1; i <= nnnp1; i++)
alpar@1
   279
         iptr[i] = matnum;
alpar@1
   280
      *knum = matnum - 1;
alpar@1
   281
      return;
alpar@1
   282
}
alpar@1
   283
alpar@1
   284
double g = 1431655765.0;
alpar@1
   285
alpar@1
   286
double fa01as(int i)
alpar@1
   287
{     /* random number generator */
alpar@1
   288
      g = fmod(g * 9228907.0, 4294967296.0);
alpar@1
   289
      if (i >= 0)
alpar@1
   290
         return g / 4294967296.0;
alpar@1
   291
      else
alpar@1
   292
         return 2.0 * g / 4294967296.0 - 1.0;
alpar@1
   293
}
alpar@1
   294
alpar@1
   295
void fa01bs(int max, int *nrand)
alpar@1
   296
{     *nrand = (int)(fa01as(1) * (double)max) + 1;
alpar@1
   297
      return;
alpar@1
   298
}
alpar@1
   299
#endif
alpar@1
   300
alpar@1
   301
/* eof */