You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

311 lines
12 KiB

/* triang.c (find maximal triangular part of rectangular matrix) */
/***********************************************************************
* This code is part of GLPK (GNU Linear Programming Kit).
*
* Copyright (C) 2012-2013 Andrew Makhorin, Department for Applied
* Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
* reserved. E-mail: <mao@gnu.org>.
*
* GLPK is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GLPK is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/
#include "env.h"
#include "triang.h"
/***********************************************************************
* triang - find maximal triangular part of rectangular matrix
*
* Given a mxn sparse matrix A this routine finds permutation matrices
* P and Q such that matrix A' = P * A * Q has the following structure:
*
* 1 s n
* 1 * . . . . . x x x x x
* * * . . . . x x x x x
* * * * . . . x x x x x
* * * * * . . x x x x x
* * * * * * . x x x x x
* s * * * * * * x x x x x
* x x x x x x x x x x x
* x x x x x x x x x x x
* m x x x x x x x x x x x
*
* where '*' are elements of the triangular part, '.' are structural
* zeros, 'x' are other elements.
*
* The formal routine mat specifies the original matrix A in both row-
* and column-wise format. If the routine mat is called with k = +i,
* 1 <= i <= m, it should store column indices and values of non-zero
* elements of i-th row of A in locations ind[1], ..., ind[len] and
* val[1], ..., val[len], resp., where len is the returned number of
* non-zeros in the row, 0 <= len <= n. Similarly, if the routine mat
* is called with k = -j, 1 <= j <= n, it should store row indices and
* values of non-zero elements of j-th column of A and return len, the
* number of non-zeros in the column, 0 <= len <= m. Should note that
* duplicate indices are not allowed.
*
* The parameter info is a transit pointer passed to the routine mat.
*
* The parameter tol is a tolerance. The routine triang guarantees that
* each diagonal element in the triangular part of matrix A' is not
* less in magnitude than tol * max, where max is the maximal magnitude
* of elements in corresponding column.
*
* On exit the routine triang stores information on the triangular part
* found in the arrays rn and cn. Elements rn[1], ..., rn[s] specify
* row numbers and elements cn[1], ..., cn[s] specify column numbers
* of the original matrix A, which correspond to rows/columns 1, ..., s
* of matrix A', where s is the size of the triangular part returned by
* the routine, 0 <= s <= min(m, n). The order of rows and columns that
* are not included in the triangular part remains unspecified.
*
* ALGORITHM
*
* The routine triang uses a simple greedy heuristic.
*
* At some step the matrix A' = P * A * Q has the following structure:
*
* 1 n
* 1 * . . . . . . . x x x
* * * . . . . . . x x x
* * * * . . . . . x x x
* * * * * . . . . x x x
* x x x x # # # # x x x
* x x x x # # # # x x x
* x x x x # # # # x x x
* x x x x # # # # x x x
* m x x x x # # # # x x x
*
* where '#' are elements of active submatrix. Initially P = Q = I, so
* the active submatrix is the original matrix A = A'.
*
* If some row has exactly one non-zero in the active submatrix (row
* singleton), the routine includes this row and corresponding column
* in the triangular part, and removes the column from the active
* submatrix. Otherwise, the routine simply removes a column having
* maximal number of non-zeros from the active submatrix in the hope
* that new row singleton(s) will appear.
*
* COMPLEXITY
*
* The time complexity of the routine triang is O(nnz), where nnz is
* number of non-zeros in the original matrix A. */
int triang(int m, int n, int (*mat)(void *info, int k, int ind[],
double val[]), void *info, double tol, int rn[], int cn[])
{ int head, i, j, jj, k, kk, ks, len, len2, next_j, ns, size;
int *cind, *rind, *cnt, *ptr, *list, *prev, *next;
double *cval, *rval, *big;
char *flag;
/* allocate working arrays */
cind = talloc(1+m, int);
cval = talloc(1+m, double);
rind = talloc(1+n, int);
rval = talloc(1+n, double);
cnt = ptr = talloc(1+m, int);
list = talloc(1+n, int);
prev = talloc(1+n, int);
next = talloc(1+n, int);
big = talloc(1+n, double);
flag = talloc(1+n, char);
/*--------------------------------------------------------------*/
/* build linked lists of columns having equal lengths */
/*--------------------------------------------------------------*/
/* ptr[len], 0 <= len <= m, is number of first column of length
* len;
* next[j], 1 <= j <= n, is number of next column having the same
* length as column j;
* big[j], 1 <= j <= n, is maximal magnitude of elements in j-th
* column */
for (len = 0; len <= m; len++)
ptr[len] = 0;
for (j = 1; j <= n; j++)
{ /* get j-th column */
len = mat(info, -j, cind, cval);
xassert(0 <= len && len <= m);
/* add this column to beginning of list ptr[len] */
next[j] = ptr[len];
ptr[len] = j;
/* determine maximal magnitude of elements in this column */
big[j] = 0.0;
for (k = 1; k <= len; k++)
{ if (big[j] < fabs(cval[k]))
big[j] = fabs(cval[k]);
}
}
/*--------------------------------------------------------------*/
/* build doubly linked list of columns ordered by decreasing */
/* column lengths */
/*--------------------------------------------------------------*/
/* head is number of first column in the list;
* prev[j], 1 <= j <= n, is number of column that precedes j-th
* column in the list;
* next[j], 1 <= j <= n, is number of column that follows j-th
* column in the list */
head = 0;
for (len = 0; len <= m; len++)
{ /* walk thru list of columns of length len */
for (j = ptr[len]; j != 0; j = next_j)
{ next_j = next[j];
/* add j-th column to beginning of the column list */
prev[j] = 0;
next[j] = head;
if (head != 0)
prev[head] = j;
head = j;
}
}
/*--------------------------------------------------------------*/
/* build initial singleton list */
/*--------------------------------------------------------------*/
/* there are used two list of columns:
* 1) doubly linked list of active columns, in which all columns
* are ordered by decreasing column lengths;
* 2) singleton list; an active column is included in this list
* if it has at least one row singleton in active submatrix */
/* flag[j], 1 <= j <= n, is a flag of j-th column:
* 0 j-th column is inactive;
* 1 j-th column is active;
* 2 j-th column is active and has row singleton(s) */
/* initially all columns are active */
for (j = 1; j <= n; j++)
flag[j] = 1;
/* initialize row counts and build initial singleton list */
/* cnt[i], 1 <= i <= m, is number of non-zeros, which i-th row
* has in active submatrix;
* ns is size of singleton list;
* list[1], ..., list[ns] are numbers of active columns included
* in the singleton list */
ns = 0;
for (i = 1; i <= m; i++)
{ /* get i-th row */
len = cnt[i] = mat(info, +i, rind, rval);
xassert(0 <= len && len <= n);
if (len == 1)
{ /* a[i,j] is row singleton */
j = rind[1];
xassert(1 <= j && j <= n);
if (flag[j] != 2)
{ /* include j-th column in singleton list */
flag[j] = 2;
list[++ns] = j;
}
}
}
/*--------------------------------------------------------------*/
/* main loop */
/*--------------------------------------------------------------*/
size = 0; /* size of triangular part */
/* loop until active column list is non-empty, i.e. until the
* active submatrix has at least one column */
while (head != 0)
{ if (ns == 0)
{ /* singleton list is empty */
/* remove from the active submatrix a column of maximal
* length in the hope that some row singletons appear */
j = head;
len = mat(info, -j, cind, cval);
xassert(0 <= len && len <= m);
goto drop;
}
/* take column j from the singleton list */
j = list[ns--];
xassert(flag[j] == 2);
/* j-th column has at least one row singleton in the active
* submatrix; choose one having maximal magnitude */
len = mat(info, -j, cind, cval);
xassert(0 <= len && len <= m);
kk = 0;
for (k = 1; k <= len; k++)
{ i = cind[k];
xassert(1 <= i && i <= m);
if (cnt[i] == 1)
{ /* a[i,j] is row singleton */
if (kk == 0 || fabs(cval[kk]) < fabs(cval[k]))
kk = k;
}
}
xassert(kk > 0);
/* check magnitude of the row singleton chosen */
if (fabs(cval[kk]) < tol * big[j])
{ /* all row singletons are too small in magnitude; drop j-th
* column */
goto drop;
}
/* row singleton a[i,j] is ok; add i-th row and j-th column to
* the triangular part */
size++;
rn[size] = cind[kk];
cn[size] = j;
drop: /* remove j-th column from the active submatrix */
xassert(flag[j]);
flag[j] = 0;
if (prev[j] == 0)
head = next[j];
else
next[prev[j]] = next[j];
if (next[j] == 0)
;
else
prev[next[j]] = prev[j];
/* decrease row counts */
for (k = 1; k <= len; k++)
{ i = cind[k];
xassert(1 <= i && i <= m);
xassert(cnt[i] > 0);
cnt[i]--;
if (cnt[i] == 1)
{ /* new singleton appeared in i-th row; determine number
* of corresponding column (it is the only active column
* in this row) */
len2 = mat(info, +i, rind, rval);
xassert(0 <= len2 && len2 <= n);
ks = 0;
for (kk = 1; kk <= len2; kk++)
{ jj = rind[kk];
xassert(1 <= jj && jj <= n);
if (flag[jj])
{ xassert(ks == 0);
ks = kk;
}
}
xassert(ks > 0);
/* a[i,jj] is new row singleton */
jj = rind[ks];
if (flag[jj] != 2)
{ /* include jj-th column in the singleton list */
flag[jj] = 2;
list[++ns] = jj;
}
}
}
}
/* now all row counts should be zero */
for (i = 1; i <= m; i++)
xassert(cnt[i] == 0);
/* deallocate working arrays */
tfree(cind);
tfree(cval);
tfree(rind);
tfree(rval);
tfree(ptr);
tfree(list);
tfree(prev);
tfree(next);
tfree(big);
tfree(flag);
return size;
}
/* eof */