Skip to content

Commit

Permalink
working on spasm_echelonize()
Browse files Browse the repository at this point in the history
but I broke spasm_GPLU()
  • Loading branch information
cbouilla committed Aug 29, 2023
1 parent bb42432 commit 6334dd7
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 126 deletions.
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ add_library(libspasm spasm_util.c mmio.c spasm_io.c spasm_triplet.c
spasm_scatter.c spasm_gaxpy.c spasm_permutation.c spasm_reach.c
spasm_triangular.c spasm_lu.c spasm_solutions.c spasm_pivots.c
spasm_matching.c spasm_dm.c spasm_cc.c spasm_scc.c spasm_kernel.c
spasm_concatenate.c spasm_dense_lu.c spasm_schur.c spasm_ffpack.cpp)
spasm_concatenate.c spasm_dense_lu.c spasm_schur.c spasm_ffpack.cpp
spasm_echelonize.c)

target_link_libraries(libspasm PUBLIC OpenMP::OpenMP_C)
target_link_libraries(libspasm PUBLIC m)
target_link_libraries(libspasm PUBLIC PkgConfig::GIVARO)
Expand Down
4 changes: 4 additions & 0 deletions src/spasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ spasm *spasm_kernel(const spasm * A, const int *column_permutation);
void spasm_dense_setzero(int prime, int n, int m, double *A, int ldA);
int spasm_dense_echelonize(int prime, int n, int m, double *A, int ldA, size_t *Q);

/* spasm_echelonize */
spasm* spasm_echelonize(spasm *A, int *Uqinv);


/* utilities */
static inline int spasm_max(int a, int b) {
return (a > b) ? a : b;
Expand Down
120 changes: 89 additions & 31 deletions src/spasm_echelonize.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,87 +44,145 @@ double estimate_schur_density(const spasm * A, int npiv, const int *p, const spa
* Returns the row echelon form of A. Initializes qinv (must be of size m, #columns(A)).
* Modifies A (permutes entries only)
*/
spasm* spasm_echelonize(spasm *A, int *Uqinv)
static const double sparsity_threshold = 0.05;

/* for debugging purposes */
void validate_pivots(const spasm *A, int npiv, const int *qinv, const int *p)
{
int n = A->n;
int m = A->m;
int *Ap = A->p;
int *Aj = A->j;
spasm_GFp *Ax = A->x;
for (int k = 0; k < npiv; k++) {
int i = (p != NULL) ? p[k] : k;
int j = Aj[Ap[i]];
assert(Ax[Ap[i]] == 1);
assert(qinv[j] == i);
}
}



spasm* spasm_echelonize(spasm *A, int *Uqinv)
{
int n = A->n;
int m = A->m;
int prime = A->prime;

int *qinv = spasm_malloc(n * sizeof(int)); /* for pivot search */
int *p = spasm_malloc(n * sizeof(int));

spasm *U = spasm_csr_alloc(n, m, spasm_nnz(A), prime, SPASM_WITH_NUMERICAL_VALUES);
U->n = 0;
int Unz = 0; /* #entries in U */
int Un = 0; /* #rows in U */
for (int j = 0; j < m; j++)
Uqinv[j] = -1;

double start = spasm_wtime();
double start = spasm_wtime();
int round = 0;
double density = -1;
for (;;) {
int *Ap = A->p;
int *Aj = A->j;
spasm_GFp *Ax = A->x;

/* invariant: A does not have coefficients on pivotal columns */
for (int i = 0; i < n; i++)
for (int px = Ap[i]; px < Ap[i + 1]; px++) {
int j = Aj[px];
assert(Uqinv[j] < 0);
}

/* find structural pivots in A */
int npiv = spasm_find_pivots(A, p, qinv);

if (npiv < 0.01 * spasm_min(n, m)) {
fprintf(stderr, "not enough pivots found\n");
/* TODO: echelonize A, take all the new pivots into U */
break; /* not enough pivots found */
}

/* compute total pivot nnz and reallocate U if necessary */
int pivot_nnz = 0;
for (int k = 0; k < npiv; k++) {
int i = p[k];
pivot_nnz += spasm_row_weigh(A[i]);
pivot_nnz += spasm_row_weight(A, i);
}
if (U->nz + pivot_nnz >= U->nzmax)
spasm_csr_realloc(U, U->nz + pivot_nnz);
if (spasm_nnz(U) + pivot_nnz >= U->nzmax)
spasm_csr_realloc(U, spasm_nnz(U) + pivot_nnz);

/* copy pivotal rows to U and make them unitary; update Uqinv */
int *Up = U->p;
int *Uj = U->j;
spasm_GFp *Ux = U->x;

for (int k = 0; k < npiv; k++) {
Up[Un] = Unz;
int i = p[k];
assert(qinv[Ap[i]] == i);
qinv[Ap[i]] = k;
int j = Aj[Ap[i]];
assert(Uqinv[j] < 0);
assert(qinv[j] == i);
Uqinv[j] = U->n;
for (int px = Ap[i]; px < Ap[i + 1]; px++) {
Uj[Unz] = Aj[px];
Ux[Unz] = Ax[px];
Unz += 1;
}
Un += 1;
U->n += 1;
Up[U->n] = Unz;
}
Up[Un] = nz; /* finalization */
spasm_make_pivots_unitary(U, SPASM_IDENTITY_PERMUTATION, Un); /* reprocess previous pivots; shame */

/* abort if the schur complement is not sparse enough */
double density = estimate_schur_density(A, npiv, p, U, Uqinv, 100);
if (density > sparsity_threshold)
break;
spasm_make_pivots_unitary(U, SPASM_IDENTITY_PERMUTATION, U->n); /* reprocess previous pivots; shame */
validate_pivots(U, U->n, Uqinv, NULL);

if (npiv < 0.01 * spasm_min(n, m))
break; /* abort if not enough pivots are found */
density = estimate_schur_density(A, npiv, p, U, Uqinv, 100);
if (density > sparsity_threshold) {
fprintf(stderr, "Schur complement is dense (esitmated %.2f%%)\n", 100 * density);
/* TODO: compute the dense schur complement and echelonize it */
break; /* schur complement is not sparse enough */
}

int64_t nnz = (density * (n - npiv)) * (m - npiv);
char tmp[6];
spasm_human_format(sizeof(int) * (n - npiv + nnz) + sizeof(spasm_GFp) * nnz, tmp);
fprintf(stderr, "round %d. Schur complement is %d x %d, estimated density : %.2f (%s byte)\n", round, n - npiv, m - npiv, density, tmp);

/* compute the schur complement */
spasm *S = spasm_schur(A, p, npiv, density, 0, NULL); ///////// FIXME must read U
spasm *S = spasm_schur(A, p, npiv, U, Uqinv, density, SPASM_DISCARD_L, NULL);
A = S;
n = A->n;
round += 1;
}

/* Final step: echelonize A, except rows p[0:npiv] */
/* Final step: echelonize A, rows p[npiv:n] */

/* sparse schur complement : GPLU */
if (gplu_final || (!dense_final && density < sparsity_threshold)) {
if (1 || density < sparsity_threshold) {
spasm_lu *LU = spasm_LU(A, p, SPASM_DISCARD_L);
rank += LU->U->n;
spasm *UU = LU->U;
int *UUp = UU->p;
int *UUj = UU->j;
spasm_GFp *UUx = UU->x;
int *qinv = LU->qinv;
// concatenate UU to U;
spasm_csr_realloc(U, spasm_nnz(U) + spasm_nnz(UU));
int *Up = U->p;
int *Uj = U->j;
spasm_GFp *Ux = U->x;
for (int i = 0; i < UU->n; i++) {
int j = UUj[UUp[i]];
assert(qinv[j] == i);
Uqinv[j] = U->n;
for (int px = UUp[i]; px < UUp[i + 1]; px++) {
Uj[Unz] = UUj[px];
Ux[Unz] = UUx[px];
Unz += 1;
}
U->n += 1;
Up[U->n] = Unz; /* finalization */
}
spasm_free_LU(LU);
} else {
/* dense schur complement */
int r = spasm_schur_rank(A, p, qinv, npiv);
fprintf(stderr, "rank = %d + %d\n", npiv, r);
rank += npiv + r;
}
}// else {
// /* dense schur complement */
// int r = spasm_schur_rank(A, p, qinv, npiv);
// fprintf(stderr, "rank = %d + %d\n", npiv, r);
//}
return U;
}
5 changes: 3 additions & 2 deletions src/spasm_schur.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ void spasm_stack_nonpivotal_columns(spasm *A, int *qinv)


/*
* Computes the Schur complement.
* Eliminating the pivots located on rows p[0:n_pivots] of A.
* Computes the Schur complement of A w.r.t. U
* Process rows p[npiv:n] of A.
* non-pivotal rows are p[npiv:n]
* The pivots must be the first entries on the rows.
* The pivots must be unitary.
Expand Down Expand Up @@ -95,6 +95,7 @@ spasm *spasm_schur(const spasm * A, const int *p, int npiv, const spasm *U, cons
int inew = p[i];
int top = spasm_sparse_forward_solve(U, A, inew, xj, x, qinv);

/* count surviving coefficients */
row_snz = 0;
for (int px = top; px < m; px++) {
const int j = xj[px];
Expand Down
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ set(KERNEL_TEST_MATRICES
BIOMD0000000525.int.mpl.sms
BIOMD0000000424.int.mpl.sms
)
spasm_run_tests(kernel "${KERNEL_TEST_MATRICES}")
# spasm_run_tests(kernel "${KERNEL_TEST_MATRICES}")


########### lu / pluq / solve / with and without permutation
Expand Down
111 changes: 20 additions & 91 deletions tools/rank.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,21 @@
/** Computes the rank of the input matrix using the hybrid strategy described in [PASCO'17] */
int main(int argc, char **argv)
{
int prime, n_times, rank, npiv, n, m, dense_final, gplu_final, allow_transpose, ch;
double start_time, end_time;
spasm_triplet *T;
spasm *A, *B;
int *p, *qinv;
double density, sparsity_threshold;
char nnz[6];
int allow_transpose = 1; /* transpose ON by default */
int prime = 42013;

allow_transpose = 1; /* transpose ON by default */
n_times = 3;
dense_final = 0;
gplu_final = 0;
sparsity_threshold = 0.1;
prime = 42013;

/* options descriptor */
/* parse command-line options */
struct option longopts[7] = {
{"sparse-threshold", required_argument, NULL, 's'},
{"max-recursion", required_argument, NULL, 'm'},
{"dense-last-step", no_argument, NULL, 'd'},
{"gplu-last-step", no_argument, NULL, 'g'},
{"no-transpose", no_argument, NULL, 'a'},
{"modulus", required_argument, NULL, 'p'},
{NULL, 0, NULL, 0}
};

char ch;
while ((ch = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
switch (ch) {
case 's':
sparsity_threshold = atof(optarg);
break;
case 'm':
n_times = atoi(optarg);
break;
case 'd':
dense_final = 1;
break;
case 'a':
allow_transpose = 0;
break;
case 'g':
gplu_final = 1;
break;
case 'p':
prime = atoi(optarg);
break;
Expand All @@ -63,72 +35,29 @@ int main(int argc, char **argv)
argv += optind;

/* load matrix from standard input */
T = spasm_load_sms(stdin, prime);
spasm_triplet *T = spasm_load_sms(stdin, prime);
if (allow_transpose && (T->n < T->m)) {
fprintf(stderr, "[rank] transposing matrix : ");
fflush(stderr);
start_time = spasm_wtime();
double start = spasm_wtime();
spasm_triplet_transpose(T);
fprintf(stderr, "%.1f s\n", spasm_wtime() - start_time);
fprintf(stderr, "%.1f s\n", spasm_wtime() - start);
}
A = spasm_compress(T);
spasm *A = spasm_compress(T);
spasm_triplet_free(T);
n = A->n;
m = A->m;
spasm_human_format(spasm_nnz(A), nnz);
fprintf(stderr, "start. A is %d x %d (%s nnz)\n", n, m, nnz);

p = spasm_malloc(n * sizeof(int));
qinv = spasm_malloc(m * sizeof(int));

start_time = spasm_wtime();
rank = 0;
npiv = spasm_find_pivots(A, p, qinv);
spasm_make_pivots_unitary(A, p, npiv);
density = spasm_schur_probe_density(A, p, npiv, A, qinv, 100);

for (int i = 0; i < n_times; i++) {
int64_t nnz = (density * (n - npiv)) * (m - npiv);
char tmp[6];
spasm_human_format(sizeof(int) * (n - npiv + nnz) + sizeof(spasm_GFp) * nnz, tmp);
fprintf(stderr, "round %d / %d. Schur complement is %d x %d, estimated density : %.2f (%s byte)\n", i, n_times, n - npiv, m - npiv, density, tmp);

if (density > sparsity_threshold)
break;

/* compute schur complement, update matrix */
B = spasm_schur(A, p, npiv, A, qinv, density, 0, NULL);
spasm_stack_nonpivotal_columns(B, qinv);

spasm_csr_free(A);
A = B;
rank += npiv;
n = A->n;
m = A->m;

npiv = spasm_find_pivots(A, p, qinv);
spasm_make_pivots_unitary(A, p, npiv);
density = spasm_schur_probe_density(A, p, npiv, A, qinv, 100);
}

/* ---- final step ---------- */

/* sparse schur complement : GPLU */
if (gplu_final || (!dense_final && density < sparsity_threshold)) {
spasm_lu *LU = spasm_LU(A, p, SPASM_DISCARD_L);
rank += LU->U->n;
spasm_free_LU(LU);
} else {
/* dense schur complement */
int r = spasm_schur_rank(A, p, qinv, npiv);
fprintf(stderr, "rank = %d + %d\n", npiv, r);
rank += npiv + r;
}

end_time = spasm_wtime();
fprintf(stderr, "done in %.3f s rank = %d\n", end_time - start_time, rank);
int n = A->n;
int m = A->m;
char hnnz[6];
spasm_human_format(spasm_nnz(A), hnnz);
fprintf(stderr, "start. A is %d x %d (%s nnz)\n", n, m, hnnz);

int *qinv = spasm_malloc(m * sizeof(int));
double start_time = spasm_wtime();
spasm *U = spasm_echelonize(A, qinv);
double end_time = spasm_wtime();
fprintf(stderr, "done in %.3f s rank = %d\n", end_time - start_time, U->n);
spasm_csr_free(A);
free(p);
spasm_csr_free(U);
free(qinv);
return 0;
}

0 comments on commit 6334dd7

Please sign in to comment.