https://gitlab.inria.fr/cado-nfs/cado-nfs
Raw File
Tip revision: b0795d85dd65d5365c3308ae1a2966403bb7878a authored by Pierrick Gaudry on 14 February 2013, 14:48:36 UTC
at work here
Tip revision: b0795d8
merge_mono.c
// TODO: use unified compact lists...!
// TODO: reintroduce mat->weight

/* If one wants to change the R data structure, please check the diff of
   revision 1568, which clearly identifies the places where R is used. */

#include "cado.h"
#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>
#include <string.h>
#include <math.h>
#include <unistd.h>

#include "utils.h"

#include "merge_opts.h"
#include "sparse.h"
#include "filter_matrix.h"
#include "mst.h"
#include "report.h"

#include "markowitz.h"
#include "merge_mono.h"

#define DEBUG 0

#pragma GCC diagnostic ignored "-Wunused-parameter"

#if DEBUG >= 1
/* total number of row additions in merge */
static unsigned long row_additions = 0;
#endif

// not mallocing to speed up(?).
static int
findBestIndex(filter_matrix_t *mat, int m, int32_t *ind, int32_t ideal)
{
    int A[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX], i, j, imin, wmin, w;

    ASSERT(m <= MERGE_LEVEL_MAX);
    if(m == 2)
	return 0;
    fillRowAddMatrix(A, mat, m, ind, ideal);
    // iterate over all vertices
    imin = -1;
    wmin = 0;
    for(i = 0; i < m; i++){
	// compute the new total weight if i is used as pivot
	w = 0;
	for(j = 0; j < m; j++)
	    // note A[i][i] = 0
	    w += A[i][j];
#if DEBUG >= 1
	printf ("W[%d]=%d\n", i, w);
#endif
	if((imin == -1) || (w < wmin)){
	    imin = i;
	    wmin = w;
	}
    }
    return imin;
}

#if DEBUG >= 2
// We should have mat->wt[j] == m == nchk
static void
checkCoherence(filter_matrix_t *mat, int m, int j)
{
    int nchk = 0, k;

    for(k = 1; k <= mat->R[GETJ(mat, j)][0]; k++)
	if(mat->R[GETJ(mat, j)][k] != -1)
	    nchk++;
    ASSERT(nchk == (mat->wt[GETJ(mat, j)] >= 0 ? mat->wt[GETJ(mat, j)] : -mat->wt[GETJ(mat, j)]));
    if(m != -1){
	if(nchk != m){
	    printf ("HYPERCHECK: mat->R[%d][0]=%ld, m=%d\n", j,
                     (long int) mat->R[GETJ(mat, j)][0], m);
	    printf ("Gasp: nchk=%d\n", nchk);
	}
	ASSERT(nchk == m);
    }
}
#endif

//////////////////////////////////////////////////////////////////////
// making things independent of the real data structure used
//////////////////////////////////////////////////////////////////////

static void
removeColDefinitely(report_t *rep, filter_matrix_t *mat, int32_t j)
{
    int32_t k;

    for(k = 1; k <= mat->R[GETJ(mat, j)][0]; k++)
	if(mat->R[GETJ(mat, j)][k] != -1){
# if TRACE_COL >= 0
	    if(j == TRACE_COL)
		printf ("deleteAllCols: row is %d\n",mat->R[GETJ(mat, j)][k]);
# endif
	    remove_j_from_row(mat, mat->R[GETJ(mat, j)][k], j);
	    removeRowDefinitely(rep, mat, mat->R[GETJ(mat, j)][k]);
	    mat->rem_ncols--;
	}
    mat->wt[GETJ(mat, j)] = 0;
}

/* remove column j and update matrix */
static void
removeColumnAndUpdate(filter_matrix_t *mat, int j)
{
    MkzRemoveJ (mat, j);
    freeRj(mat, j);
}

// The cell [i, j] may be incorporated to the data structure, at least
// if j is not too heavy, etc.
static void
addCellAndUpdate(filter_matrix_t *mat, int i, int32_t j)
{
    int w = MkzIncrCol(mat, j);

    if (w >= 0)
      {
	if(w > mat->cwmax)
          {
	    // the weight of column w exceeds cwmax, thus we remove it
	    removeColumnAndUpdate(mat, j);
	    mat->wt[j] = -w;
          }
	else
          {
	    // update R[j] by adding i
	    add_i_to_Rj(mat, i, j);
	    MkzUpdate(mat, i, j);
          }
      }
}

// remove the cell (i,j), and updates matrix correspondingly.
// if final, also update the Markowitz counts
static void
removeCellAndUpdate(filter_matrix_t *mat, int i, int32_t j, int final)
{
#if TRACE_ROW >= 0
    if(i == TRACE_ROW){
	printf ("TRACE_ROW: removeCellAndUpdate i=%d j=%d\n", i, j);
    }
#endif
    if(mat->wt[GETJ(mat, j)] < 0){
	// if mat->wt[j] is already < 0, we don't care about
	// decreasing, updating, etc. except when > 2
	return;
    }
    MkzDecreaseColWeight(mat, j);
    // update R[j] by removing i
    remove_i_from_Rj(mat, i, j);
    if (final)
      MkzUpdateDown (mat, i, j);
}

//////////////////////////////////////////////////////////////////////
// now, these guys are generic...!

// for all j in row[i], removes j and update data
// if final, also update the Markowitz counts
static void
removeRowAndUpdate(filter_matrix_t *mat, int i, int final)
{
    int k;

#if TRACE_ROW >= 0
    if(i == TRACE_ROW)
	printf ("TRACE_ROW: removeRowAndUpdate i=%d\n", i);
#endif
    mat->weight -= matLengthRow(mat, i);
    for(k = 1; k <= matLengthRow(mat, i); k++){
#if TRACE_COL >= 0
	if(matCell(mat, i, k) == TRACE_COL){
	    printf ("removeRowAndUpdate removes %d from R_%d\n", TRACE_COL, i);
	}
#endif
	removeCellAndUpdate(mat, i, matCell(mat, i, k), final);
    }
}

// All entries M[i, j] are potentially added to the structure.
static void
addOneRowAndUpdate(filter_matrix_t *mat, int i)
{
    int k;

    mat->weight += matLengthRow(mat, i);
    for(k = 1; k <= matLengthRow(mat, i); k++)
	addCellAndUpdate(mat, i, matCell(mat, i, k));
}

// realize mat[i1] += mat[i2] and update the data structure.
// len could be the real length of row[i1]+row[i2] or -1.
void
addRowsAndUpdate(filter_matrix_t *mat, int i1, int i2, int32_t j)
{
    // cleaner one, that shares addRowsData() to prepare the next move...!
    // i1 is to disappear, replaced by a new one
    removeRowAndUpdate(mat, i1, 0);
    // we know the length of row[i1]+row[i2]
    addRows(mat->rows, i1, i2, j);
    addOneRowAndUpdate(mat, i1);
}

static int
removeSingletons(report_t *rep, filter_matrix_t *mat)
{
    int32_t j;
    int njrem = 0;

    for(j = mat->jmin; j < mat->jmax; j++)
	if(mat->wt[GETJ(mat, j)] == 1){
	    removeColDefinitely(rep, mat, j);
	    njrem++;
	}
    return njrem;
}

int
deleteHeavyColumns(report_t *rep, filter_matrix_t *mat)
{
    return MkzDeleteHeavyColumns(rep, mat);
}

void
removeRowDefinitely(report_t *rep, filter_matrix_t *mat, int32_t i)
{
    removeRowAndUpdate(mat, i, 1);
    destroyRow(mat, i);
    report1(rep, i, -1);
    mat->rem_nrows--;
}

// try all combinations to find the smaller one; resists to m==1
static void
tryAllCombinations(report_t *rep, filter_matrix_t *mat, int m, int32_t *ind,
                   int32_t j)
{
    int i, k;

    if(m == 1)
      printf ("Warning: m=1 in tryAllCombinations\n");
    i = findBestIndex(mat, m, ind, j);
#if DEBUG >= 1
    printf ("Minimal is i=%d (%d %d)\n", i, ind[0], ind[1]);
#endif
    for(k = 0; k < m; k++)
	if(k != i){
	    addRowsAndUpdate(mat, ind[k], ind[i], j);
#if DEBUG >= 1
	    printf ("new row[%d]=", ind[k]);
	    print_row (mat, ind[k]);
	    printf ("\n");
#endif
	}
    if(i > 0){
	// put ind[i] in front
        // FIXME: can we simply swap ind[0] and ind[i]?
	int itmp = ind[i];
	for(k = i; k > 0; k--)
	    ind[k] = ind[k-1];
	ind[0] = itmp;
    }
#if DEBUG >= 1
    printf ("=> new_ind: %d %d\n", ind[0], ind[1]);
#endif
    reportn(rep, ind, m, j);
    removeRowAndUpdate(mat, ind[0], 1);
    destroyRow(mat, ind[0]);
}

// add u to its sons; we save the history in the history array, so that
// we can report all at once and prepare for MPI.
// A[i][j] contains the estimated weight/length of R[ind[i]]+R[ind[j]].
static int
addFatherToSonsRec(int history[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX+1],
		   filter_matrix_t *mat, int m, int *ind, int32_t j,
		   int A[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX],
		   int *father,
		   int sons[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX+1],
		   int u, int level0)
{
    int k, itab = 1, i1, i2, level = level0;

    if(sons[u][0] == 0)
	// nothing to do for a leaf...!
	return -1;
    i2 = ind[u];
    if(u == father[u])
	history[level0][itab++] = i2; // trick for the root!!!
    else
	// the usual trick not to destroy row
	history[level0][itab++] = -(i2+1);
    for(k = 1; k <= sons[u][0]; k++){
	i1 = addFatherToSonsRec(history, mat, m, ind, j, A,
				father, sons, sons[u][k], level+1);
	if(i1 != -1)
	    level = i1;
	i1 = ind[sons[u][k]];
	// add u to its son
	addRowsAndUpdate(mat, i1, i2, j);
	history[level0][itab++] = i1;
    }
    history[level0][0] = itab-1;
    return level;
}

int
addFatherToSons(int history[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX+1],
		filter_matrix_t *mat, int m, int *ind, int32_t j,
		int A[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX],
		int *father,
                int *height MAYBE_UNUSED, int hmax MAYBE_UNUSED,
		int sons[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX+1])
{
    return addFatherToSonsRec(history, mat, m, ind, j, A, father, sons, 0, 0);
}

void
MSTWithA(report_t *rep, filter_matrix_t *mat, int m, int32_t *ind, int32_t j, 
         double *tMST, int A[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX])
{
    int history[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX+1];
    int sons[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX+1];
    int father[MERGE_LEVEL_MAX], height[MERGE_LEVEL_MAX], hmax, i, w;

    *tMST = seconds();
    hmax = minimalSpanningTree(&w, father, height, sons, m, A);
    *tMST = seconds()-*tMST;
#if DEBUG >= 1
    printMST(father, sons, m);
#endif
    hmax = addFatherToSons(history, mat, m, ind, j,A, father, height, hmax,sons);
    for(i = hmax; i >= 0; i--)
#if 0
	reporthis(rep, history, i);
#else
        reportn(rep, history[i]+1, history[i][0], j);
#endif
    removeRowAndUpdate(mat, ind[0], 1);
    destroyRow(mat, ind[0]);
}

static void
useMinimalSpanningTree(report_t *rep, filter_matrix_t *mat, int m, int32_t *ind,
                       int32_t j, double *tfill, double *tMST)
{
    int A[MERGE_LEVEL_MAX][MERGE_LEVEL_MAX];

    *tfill = seconds();
    fillRowAddMatrix(A, mat, m, ind, j);
    *tfill = seconds()-*tfill;
    MSTWithA(rep, mat, m, ind, j, tMST, A);
}

static void
findOptimalCombination(report_t *rep, filter_matrix_t *mat, int m, int32_t *ind,
                       int32_t j, double *tfill, double *tMST, int useMST)
{
  if ((m <= 2) || (useMST == 0))
    {
      *tfill = *tMST = 0;
      tryAllCombinations (rep, mat, m, ind, j);
    }
  else
    useMinimalSpanningTree (rep, mat, m, ind, j, tfill, tMST);
}

#if DEBUG >= 1
static void
checkWeights (filter_matrix_t *mat)
{
  int *W, j, i, k, minw = INT_MAX;

  W = (int*) malloc (mat->jmax * sizeof(int));
  for (j = mat->jmin; j < mat->jmax; j++)
    W[j] = 0;
  for(i = 0; i < mat->nrows; i++)
    if(!isRowNull(mat, i))
      {
        for(k = 1; k <= lengthRow(mat, i); k++)
          {
            j = cell(mat, i, k);
            ASSERT_ALWAYS(mat->jmin <= j && j < mat->jmax);
            W[j] ++;
          }
      }
  for (j = mat->jmin; j < mat->jmax; j++)
    {
      static int count = 0;
      if (W[j] != mat->wt[j])
        {
          if (mat->wt[j] >= 0)
            {
              fprintf (stderr, "Wrong weight for column %d: expected %d, got %d\n",
                       j, mat->wt[j], W[j]);
              exit (1);
            }
          else if (W[j] <= mat->mergelevelmax)
            {
              if (count++ <= 10)
                fprintf (stderr, "Warning, column %d: wt=%d W=%d\n",
                         j, mat->wt[j], W[j]);
            }
        }
      if (W[j] == 1 && mat->wt[j] == 1)
        {
          fprintf (stderr, "Error, weight=1 for column %d\n", j);
          exit (1);
        }
      if (W[j] != 0 && mat->wt[j] >= 0 && W[j] < minw)
        minw = W[j];
      if (W[j] == 2 && mat->wt[j] == 2)
        {
          static int count = 0;
          if (count ++ <= 10)
            fprintf (stderr, "Warning, column %d: wt=%d W=%d\n",
                     j, mat->wt[j], W[j]);
        }
    }
  printf ("Minimum non-zero column weight: %d\n", minw);
  free (W);
}
#endif

#if 0
static void
checkWeight(filter_matrix_t *mat, int32_t j)
{
    int i, w = 0;

    printf ("Rows containing %ld:", (long int) j);
    for(i = 0; i < mat->nrows; i++)
	if(!isRowNull(mat, i))
	    if(hasCol(mat->rows, i, j)){
		printf (" %d", i);
		w++;
	    }
    printf ("\n");
    ASSERT(w == (mat->wt[GETJ(mat, j)] >= 0 ? mat->wt[GETJ(mat, j)] : -mat->wt[GETJ(mat, j)]));
}
#endif

// j has weight m, which should coherent with mat->wt[j] == m
static void
mergeForColumn (report_t *rep, double *tt, double *tfill, double *tMST,
                filter_matrix_t *mat, int m, int32_t j, int useMST)
{
    int32_t ind[MERGE_LEVEL_MAX];
    int ni, k;

# if 0
    // let's be cautious...
    if(mat->wt[GETJ(mat, j)] != m){
	printf ("GASP: wt[%d]=%d != %d\n", j, mat->wt[j], m);
    }
# endif

    /* each m-merge leads to m-1 additions of rows */
#if DEBUG >= 1
    row_additions += m - 1;
    if (m > MERGE_LEVEL_MAX)
      {
	fprintf (stderr, "Error: m=%d > MERGE_LEVEL_MAX=%d\n",
                 m, MERGE_LEVEL_MAX);
	exit(1);
      }
    printf ("Treating column %d of weight %d\n",j,mat->wt[GETJ(mat, j)]);
#if DEBUG >= 2
    printf ("Status before next j=%d to start\n", j);
    // the corresponding rows are in R[j], skipping 1st cell and -1's
    checkCoherence(mat, m, j);
#endif
#endif

    for(ni = 0, k = 1; k <= mat->R[GETJ(mat, j)][0]; k++){
	if(mat->R[GETJ(mat, j)][k] != -1){
	    ind[ni++] = mat->R[GETJ(mat, j)][k];
      if (ni == m)
              break; /* early abort, since we know there are m rows */
	}
    }
    /* now ind[0], ..., ind[m-1] are the m rows containing j */

#if DEBUG >= 1
    printf (" %d", j);
    printf (" => the %d rows are:\n", m);
    for(k = 0; k < m; k++){
	printf ("row[%d]=", ind[k]);
	print_row (mat, ind[k]);
	printf ("\n");
    }
    printf ("\n");
#endif

    *tt = seconds();
    findOptimalCombination (rep, mat, m, ind, j, tfill, tMST, useMST);
    *tt = seconds()-(*tt);
    mat->rem_nrows--;
    mat->rem_ncols--;
    removeColumnAndUpdate (mat, j);
}

// It could be fun to find rows s.t. at least one j is of small weight.
// Or components of rows with some sharing?
// We could define sf(row) = sum_{j in row} w(j) and remove the
// rows of smallest value of sf?
// For the time being, remove heaviest rows.
static int
deleteScore(filter_matrix_t *mat, int32_t i)
{
#if 1
    // plain weight to remove heaviest rows
    return matLengthRow(mat, i);
#endif
#if 0
    // -plain weight to remove lightest rows
    return -lengthRow(mat, i);
#endif
#if 0
    // not using rows with too many heavy column part
    int k, s = 0;

    for(k = 1; k <= lengthRow(mat, i); k++)
	s += abs(mat->wt[GETJ(mat, mat->rows[i][k])]);
    return s;
#endif
#if 0
    // not using rows with too many light columns
    int k, s = 0;

    for(k = 1; k <= lengthRow(mat, i); k++)
	s += abs(mat->wt[GETJ(mat, mat->rows[i][k])]);
    return -s;
#endif
#if 0
    // return the weight of the lightest column
    int k, s = abs(mat->wt[GETJ(mat, mat->rows[i][1])]);

    for(k = 2; k <= lengthRow(mat, i); k++)
	if(abs(mat->wt[GETJ(mat, mat->rows[i][k])]) > s)
	    s = abs(mat->wt[GETJ(mat, mat->rows[i][k])]);
    return s;
#endif
}

// this guy is linear => total is quadratic, be careful!
static int
findSuperfluousRows(int *tmp, int ntmp, filter_matrix_t *mat)
{
    int i, itmp;

    for(i = 0, itmp = 0; i < mat->nrows; i++)
        if(!isRowNull(mat, i)){
	    tmp[itmp++] = deleteScore(mat, i);
	    tmp[itmp++] = i;
	}
    // rows with largest score will be at the end
    qsort(tmp, itmp>>1, 2 * sizeof(int), cmp);
    return itmp;
}

// Delete at most niremmax superfluous rows such that nrows-ncols >= keep.
// Let's say we are at merge-level m.
// Return the number of removed rows.
static int
deleteSuperfluousRows (report_t *rep, filter_matrix_t *mat,
                       int niremmax, int m)
{
  int keep = mat->keep;
  int nirem = 0, *tmp, ntmp, i;

  if (m <= 2)
    return 0; /* it is not worth removing rows during level-2 merges */

  if ((mat->rem_nrows - mat->rem_ncols) <= keep)
    return 0;

  if (niremmax > (mat->rem_nrows - mat->rem_ncols) - keep)
    niremmax = (mat->rem_nrows - mat->rem_ncols) - keep;

  ntmp = mat->rem_nrows << 1;
  tmp = (int *) malloc (ntmp * sizeof(int));
  ntmp = findSuperfluousRows (tmp, ntmp, mat);
  // remove rows with largest score
  for (i = ntmp - 1; i >= 0 && nirem < niremmax; i -= 2)
    if (mat->rows[tmp[i]] != NULL)
      {
        removeRowDefinitely(rep, mat, tmp[i]);
        nirem++;
      }
  free (tmp);
  return nirem;
}

static double
my_cost (double N, double w, int forbw)
{
  if (forbw == 2)
    {
      double K1 = .19e-9, K2 = 3.4e-05, K3 = 1.4e-10; // kinda average
      return (K1+K3)*N*w+K2*N*log(N)*log(N);
    }
  else if (forbw == 3)
    return w / N;
  else if (forbw <= 1)
    return N * w;
  return 0.0;
}

static void
mergeForColumn2(report_t *rep, filter_matrix_t *mat, int *njrem,
		double *totopt, double *totfill, double *totMST,
		double *totdel, int useMST, int32_t j)
{
    double tt, tfill, tMST;
    
    mergeForColumn(rep, &tt, &tfill, &tMST, mat, mat->wt[j], j, useMST);
    *totopt += tt;
    *totfill += tfill;
    *totMST += tMST;
    tt = seconds();
    *njrem += deleteHeavyColumns(rep, mat);
    *totdel += (seconds()-tt);
}

int
number_of_superfluous_rows(filter_matrix_t *mat)
{
    int kappa = (mat->rem_nrows-mat->rem_ncols) / mat->keep, ni2rem;

    if(kappa <= (1<<4))
      ni2rem = mat->keep / 2;
    else if(kappa <= (1<<5))
	ni2rem = mat->keep * (kappa/4);
    else if(kappa <= (1<<10))
	ni2rem = mat->keep * (kappa/8);
    else if(kappa <= (1<<15))
	ni2rem = mat->keep * (kappa/16);
    else if(kappa <= (1<<20))
	ni2rem = mat->keep * (kappa/32);
    else
	ni2rem = mat->keep * (kappa/64);
    return ni2rem;
}

void
mergeOneByOne (report_t *rep, filter_matrix_t *mat, int maxlevel, int verbose,
               int forbw, double ratio, double coverNmax)
{
    double totopt = 0.0, totfill = 0.0, totMST = 0.0, totdel = 0.0;
    double bwcostmin = 0.0, oldbwcost = 0.0, bwcost = 0.0;
    int old_ncols, m = 2, njrem = 0, ncost = 0, ncostmax, njproc;
    int ni2rem;
    int *nb_merges;
    int32_t dj, j, mkz;
    int useMST = 1; /* non-zero if we use minimal spanning tree */
    int REPORT; /* controls frequency of reports */

    REPORT = mat->rem_nrows / 100;

    // clean things
    njrem = removeSingletons(rep, mat);

    nb_merges = (int*) malloc ((maxlevel + 1) * sizeof (int));
    for (m = 1; m <= maxlevel; m++)
      nb_merges[m] = 0;
    printf ("Using mergeOneByOne\n");
    ncostmax = 20; // was 5
    njproc = 0;
    while(1){
	if(mat->itermax && (njproc >= mat->itermax)){
	    printf ("itermax=%d reached, stopping!\n", mat->itermax);
	    break;
	}
	oldbwcost = bwcost;
	old_ncols = mat->rem_ncols;
        if (MkzPopQueue(&dj, &mkz, mat) == 0)
          {
            printf ("Warning: heap is empty, increase maxlevel\n");
            break;
          }
	j = dj + mat->jmin;
        m = mat->wt[dj];
	if (m == 1) /* singleton ideal */
          removeColDefinitely(rep, mat, j);
	else if (m > 0) /* m=0 can happen for already merged ideals */
          mergeForColumn2(rep, mat, &njrem,
                          &totopt, &totfill, &totMST, &totdel, useMST, j);
        if (nb_merges[m]++ == 0 && m > 1)
          printf ("First %d-merge, cost %d (#Q=%d)\n", m, mkz,
                  MkzQueueCardinality(mat->MKZQ));
	// number of columns removed
	njproc += old_ncols - mat->rem_ncols;
	bwcost = my_cost ((double) mat->rem_nrows, (double) mat->weight,
                          forbw);
	if (mat->rem_nrows % REPORT == 0){
	    njrem = removeSingletons (rep, mat);
	    ni2rem = number_of_superfluous_rows (mat);
	    deleteSuperfluousRows (rep, mat, ni2rem, m);
	    printf ("N=%d (%d) w=%lu", mat->rem_nrows,
                    mat->rem_nrows - mat->rem_ncols, mat->weight);
	    if (forbw == 2)
              printf (" bw=%e", bwcost);
	    else if (forbw == 3)
              printf (" w*N=%"PRIu64"", ((uint64_t)mat->rem_nrows)
                      * ((uint64_t)mat->weight));
	    else if (forbw <= 1)
		printf (" w*N=%e", bwcost);
	    printf (" w/N=%2.2lf\n",
		    ((double)mat->weight)/((double)mat->rem_nrows));
	    if((forbw != 0) && (forbw != 3))
		// what a trick!!!!
		fprintf (rep->outfile, "BWCOST: %1.0f\n", bwcost);
            fflush (stdout);
	}
	if((bwcostmin == 0.0) || (bwcost < bwcostmin)){
	    bwcostmin = bwcost;
	    if((forbw != 0) && (forbw != 3))
		// what a trick!!!!
		fprintf(rep->outfile, "BWCOST: %1.0f\n", bwcost);
	}
	// to be cleaned one day...
	if((forbw == 0) || (forbw == 2)){
          double r = bwcost / bwcostmin;
	    if(r > ratio){
		if(mat->rem_nrows-mat->rem_ncols > mat->keep){
		    // drop all remaining columns at once
		    ni2rem = mat->rem_nrows-mat->rem_ncols+mat->keep;
		    printf ("Dropping %d rows at once\n", ni2rem);
		    deleteSuperfluousRows(rep, mat, ni2rem, INT_MAX);
		}
		else{
		    if(forbw == 0)
			printf ("cN too high, stopping [%2.2lf]\n", r);
		    else
			printf ("bw too high, stopping [%2.2lf]\n", r);
		    break;
		}
	    }
	}
	else if (forbw == 3 && bwcost >= coverNmax)
          {
            int nrows = mat->rem_nrows;

            /* if the excess is still larger than what is wanted,
               remove the heaviest rows and loop again */
            printf ("remains %d rows\n", mat->rem_nrows);
            deleteSuperfluousRows (rep, mat,
                       (mat->rem_nrows - mat->rem_ncols) - mat->keep, INT_MAX);
            printf ("after deleteSuperfluousRows, remains %d rows\n",
                    mat->rem_nrows);
            removeSingletons (rep, mat);
            printf ("after removeSingletons, remains %d rows\n",
                    mat->rem_nrows);
            if (mat->rem_nrows == nrows)
              {
                printf ("w/N too high (%1.2f), stopping\n", bwcost);
                break;
              }
          }
	if((forbw == 1) && (oldbwcost != 0.0) && (bwcost > oldbwcost)){
	    ncost++;
#if 0
	    printf ("New cost > old cost (%.16e > %.16e) [%d/%d]\n",
		    bwcost, oldbwcost, ncost, ncostmax);
#endif
	    if(ncost >= ncostmax){
		int nirem;

		printf ("New cost > old cost %d times in a row:", ncost);
		nirem = deleteSuperfluousRows(rep, mat, 128, m);
		if(nirem == 0){
		    printf (" stopping\n");
		    break;
		}
		else{
		    printf (" try again after removing %d rows!\n", nirem);
		    njproc += nirem; // humf: odd name for njproc...!
		    continue;
		}
	    }
	}
	else
	    ncost = 0;
    }
    if (mat->itermax == 0)
      {
        printf ("Removing final excess, nrows=%d\n", mat->rem_nrows);
	deleteSuperfluousRows(rep, mat,
			      (mat->rem_nrows - mat->rem_ncols) - mat->keep,
                              INT_MAX);
        printf ("Removing singletons, nrows=%d\n", mat->rem_nrows);
        removeSingletons (rep, mat);
      }

#if DEBUG >= 1
    checkWeights (mat);
#endif

    if((forbw != 0) && (forbw != 3)){
	fprintf(rep->outfile, "BWCOSTMIN: %1.0f\n", bwcostmin);
	printf ("Minimal bwcost found: %1.0f\n", bwcostmin);
    }
#if DEBUG >= 1
    printf ("Total number of row additions: %lu\n", row_additions);
#endif
    for (m = 1; m <= maxlevel; m++)
      if (nb_merges[m] > 0)
        printf ("Number of %d-merges: %d\n", m, nb_merges[m]);
    free (nb_merges);
}

//////////////////////////////////////////////////////////////////////
//
// Resume section: very much inspired by replay.c, of course...!
//
//////////////////////////////////////////////////////////////////////

// A line is "i i1 ... ik".
// If i >= 0 then
//     row[i] is to be added to rows i1...ik and destroyed at the end of
//     the process.
//     Works also is i is alone (hence: destroyed row).
// If i < 0 then
//     row[-i-1] is to be added to rows i1...ik and NOT destroyed.
//
static void
doAllAdds(report_t *rep, filter_matrix_t *mat, char *str)
{
  int32_t j;
  int32_t ind[MERGE_LEVEL_MAX], i0;
  int ni, sg, k;

  ni = parse_hisfile_line (ind, str, &j);  
  
  if (ind[0] < 0)
    {
      sg = -1;
      i0 = -ind[0]-1;
    }
  else
    {
      sg = 1;
      i0 = ind[0];
    }

  for (k = 1; k < ni; k++)
      addRowsAndUpdate(mat, ind[k], i0, j);

  reportn(rep, ind, ni, j);
  
  if (sg > 0)
    {
      removeRowAndUpdate(mat, i0, 1);
      destroyRow(mat, i0);
      mat->rem_nrows--;
    }
}

// resumename is a file of the type mergehis.
// TODO: Compiles, but not really tested with Markowitz...!
void
resume(report_t *rep, filter_matrix_t *mat, char *resumename)
{
    FILE *resumefile = fopen(resumename, "r");
    char str[RELATION_MAX_BYTES];
    unsigned long addread = 0;
    int nactivej;
    int32_t j;
    char * rp;
    int REPORT = 10000;

    printf ("Resuming computations from %s\n", resumename);
    // skip first line containing nrows ncols
    rp = fgets(str, RELATION_MAX_BYTES, resumefile);
    ASSERT_ALWAYS(rp);

    printf ("Reading row additions\n");
    while(fgets(str, RELATION_MAX_BYTES, resumefile)){
	addread++;
	if((addread % (10 * REPORT)) == 0)
	    printf ("%lu lines read at %2.2lf\n", addread, seconds());
	if(str[strlen(str)-1] != '\n'){
	    printf ("Gasp: not a complete a line!");
	    printf (" I stop reading and go to the next phase\n");
	    break;
	}
	if(strncmp(str, "BWCOST", 6) != 0)
	    doAllAdds(rep, mat, str);
    }
    fclose(resumefile);
    for(j = mat->jmin; j < mat->jmax; j++)
	if((mat->wt[GETJ(mat,j)] == 0) && MkzIsAlive(mat->MKZA, GETJ(mat,j)))
	    // be sure j was removed...
	    removeColumnAndUpdate(mat, j);
    nactivej = 0;
    for(j = mat->jmin; j < mat->jmax; j++)
	if(mat->wt[GETJ(mat, j)] != 0)
	    nactivej++;
    mat->rem_ncols = nactivej;
    printf ("At the end of resume, we have");
    printf (" nrows=%d ncols=%d (%d)\n",
	    mat->rem_nrows, mat->rem_ncols, mat->rem_nrows-mat->rem_ncols);
}
back to top