src/glpfhv.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
/* glpfhv.c (LP basis factorization, FHV eta file version) */
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
*  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
alpar@1
     7
*  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
alpar@1
     8
*  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
alpar@1
     9
*  E-mail: <mao@gnu.org>.
alpar@1
    10
*
alpar@1
    11
*  GLPK is free software: you can redistribute it and/or modify it
alpar@1
    12
*  under the terms of the GNU General Public License as published by
alpar@1
    13
*  the Free Software Foundation, either version 3 of the License, or
alpar@1
    14
*  (at your option) any later version.
alpar@1
    15
*
alpar@1
    16
*  GLPK is distributed in the hope that it will be useful, but WITHOUT
alpar@1
    17
*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
alpar@1
    18
*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
alpar@1
    19
*  License for more details.
alpar@1
    20
*
alpar@1
    21
*  You should have received a copy of the GNU General Public License
alpar@1
    22
*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
alpar@1
    23
***********************************************************************/
alpar@1
    24
alpar@1
    25
#include "glpfhv.h"
alpar@1
    26
#include "glpenv.h"
alpar@1
    27
#define xfault xerror
alpar@1
    28
alpar@1
    29
/* CAUTION: DO NOT CHANGE THE LIMIT BELOW */
alpar@1
    30
alpar@1
    31
#define M_MAX 100000000 /* = 100*10^6 */
alpar@1
    32
/* maximal order of the basis matrix */
alpar@1
    33
alpar@1
    34
/***********************************************************************
alpar@1
    35
*  NAME
alpar@1
    36
*
alpar@1
    37
*  fhv_create_it - create LP basis factorization
alpar@1
    38
*
alpar@1
    39
*  SYNOPSIS
alpar@1
    40
*
alpar@1
    41
*  #include "glpfhv.h"
alpar@1
    42
*  FHV *fhv_create_it(void);
alpar@1
    43
*
alpar@1
    44
*  DESCRIPTION
alpar@1
    45
*
alpar@1
    46
*  The routine fhv_create_it creates a program object, which represents
alpar@1
    47
*  a factorization of LP basis.
alpar@1
    48
*
alpar@1
    49
*  RETURNS
alpar@1
    50
*
alpar@1
    51
*  The routine fhv_create_it returns a pointer to the object created. */
alpar@1
    52
alpar@1
    53
FHV *fhv_create_it(void)
alpar@1
    54
{     FHV *fhv;
alpar@1
    55
      fhv = xmalloc(sizeof(FHV));
alpar@1
    56
      fhv->m_max = fhv->m = 0;
alpar@1
    57
      fhv->valid = 0;
alpar@1
    58
      fhv->luf = luf_create_it();
alpar@1
    59
      fhv->hh_max = 50;
alpar@1
    60
      fhv->hh_nfs = 0;
alpar@1
    61
      fhv->hh_ind = fhv->hh_ptr = fhv->hh_len = NULL;
alpar@1
    62
      fhv->p0_row = fhv->p0_col = NULL;
alpar@1
    63
      fhv->cc_ind = NULL;
alpar@1
    64
      fhv->cc_val = NULL;
alpar@1
    65
      fhv->upd_tol = 1e-6;
alpar@1
    66
      fhv->nnz_h = 0;
alpar@1
    67
      return fhv;
alpar@1
    68
}
alpar@1
    69
alpar@1
    70
/***********************************************************************
alpar@1
    71
*  NAME
alpar@1
    72
*
alpar@1
    73
*  fhv_factorize - compute LP basis factorization
alpar@1
    74
*
alpar@1
    75
*  SYNOPSIS
alpar@1
    76
*
alpar@1
    77
*  #include "glpfhv.h"
alpar@1
    78
*  int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j,
alpar@1
    79
*     int ind[], double val[]), void *info);
alpar@1
    80
*
alpar@1
    81
*  DESCRIPTION
alpar@1
    82
*
alpar@1
    83
*  The routine fhv_factorize computes the factorization of the basis
alpar@1
    84
*  matrix B specified by the routine col.
alpar@1
    85
*
alpar@1
    86
*  The parameter fhv specified the basis factorization data structure
alpar@1
    87
*  created by the routine fhv_create_it.
alpar@1
    88
*
alpar@1
    89
*  The parameter m specifies the order of B, m > 0.
alpar@1
    90
*
alpar@1
    91
*  The formal routine col specifies the matrix B to be factorized. To
alpar@1
    92
*  obtain j-th column of A the routine fhv_factorize calls the routine
alpar@1
    93
*  col with the parameter j (1 <= j <= n). In response the routine col
alpar@1
    94
*  should store row indices and numerical values of non-zero elements
alpar@1
    95
*  of j-th column of B to locations ind[1,...,len] and val[1,...,len],
alpar@1
    96
*  respectively, where len is the number of non-zeros in j-th column
alpar@1
    97
*  returned on exit. Neither zero nor duplicate elements are allowed.
alpar@1
    98
*
alpar@1
    99
*  The parameter info is a transit pointer passed to the routine col.
alpar@1
   100
*
alpar@1
   101
*  RETURNS
alpar@1
   102
*
alpar@1
   103
*  0  The factorization has been successfully computed.
alpar@1
   104
*
alpar@1
   105
*  FHV_ESING
alpar@1
   106
*     The specified matrix is singular within the working precision.
alpar@1
   107
*
alpar@1
   108
*  FHV_ECOND
alpar@1
   109
*     The specified matrix is ill-conditioned.
alpar@1
   110
*
alpar@1
   111
*  For more details see comments to the routine luf_factorize.
alpar@1
   112
*
alpar@1
   113
*  ALGORITHM
alpar@1
   114
*
alpar@1
   115
*  The routine fhv_factorize calls the routine luf_factorize (see the
alpar@1
   116
*  module GLPLUF), which actually computes LU-factorization of the basis
alpar@1
   117
*  matrix B in the form
alpar@1
   118
*
alpar@1
   119
*     [B] = (F, V, P, Q),
alpar@1
   120
*
alpar@1
   121
*  where F and V are such matrices that
alpar@1
   122
*
alpar@1
   123
*     B = F * V,
alpar@1
   124
*
alpar@1
   125
*  and P and Q are such permutation matrices that the matrix
alpar@1
   126
*
alpar@1
   127
*     L = P * F * inv(P)
alpar@1
   128
*
alpar@1
   129
*  is lower triangular with unity diagonal, and the matrix
alpar@1
   130
*
alpar@1
   131
*     U = P * V * Q
alpar@1
   132
*
alpar@1
   133
*  is upper triangular.
alpar@1
   134
*
alpar@1
   135
*  In order to build the complete representation of the factorization
alpar@1
   136
*  (see formula (1) in the file glpfhv.h) the routine fhv_factorize just
alpar@1
   137
*  additionally sets H = I and P0 = P. */
alpar@1
   138
alpar@1
   139
int fhv_factorize(FHV *fhv, int m, int (*col)(void *info, int j,
alpar@1
   140
      int ind[], double val[]), void *info)
alpar@1
   141
{     int ret;
alpar@1
   142
      if (m < 1)
alpar@1
   143
         xfault("fhv_factorize: m = %d; invalid parameter\n", m);
alpar@1
   144
      if (m > M_MAX)
alpar@1
   145
         xfault("fhv_factorize: m = %d; matrix too big\n", m);
alpar@1
   146
      fhv->m = m;
alpar@1
   147
      /* invalidate the factorization */
alpar@1
   148
      fhv->valid = 0;
alpar@1
   149
      /* allocate/reallocate arrays, if necessary */
alpar@1
   150
      if (fhv->hh_ind == NULL)
alpar@1
   151
         fhv->hh_ind = xcalloc(1+fhv->hh_max, sizeof(int));
alpar@1
   152
      if (fhv->hh_ptr == NULL)
alpar@1
   153
         fhv->hh_ptr = xcalloc(1+fhv->hh_max, sizeof(int));
alpar@1
   154
      if (fhv->hh_len == NULL)
alpar@1
   155
         fhv->hh_len = xcalloc(1+fhv->hh_max, sizeof(int));
alpar@1
   156
      if (fhv->m_max < m)
alpar@1
   157
      {  if (fhv->p0_row != NULL) xfree(fhv->p0_row);
alpar@1
   158
         if (fhv->p0_col != NULL) xfree(fhv->p0_col);
alpar@1
   159
         if (fhv->cc_ind != NULL) xfree(fhv->cc_ind);
alpar@1
   160
         if (fhv->cc_val != NULL) xfree(fhv->cc_val);
alpar@1
   161
         fhv->m_max = m + 100;
alpar@1
   162
         fhv->p0_row = xcalloc(1+fhv->m_max, sizeof(int));
alpar@1
   163
         fhv->p0_col = xcalloc(1+fhv->m_max, sizeof(int));
alpar@1
   164
         fhv->cc_ind = xcalloc(1+fhv->m_max, sizeof(int));
alpar@1
   165
         fhv->cc_val = xcalloc(1+fhv->m_max, sizeof(double));
alpar@1
   166
      }
alpar@1
   167
      /* try to factorize the basis matrix */
alpar@1
   168
      switch (luf_factorize(fhv->luf, m, col, info))
alpar@1
   169
      {  case 0:
alpar@1
   170
            break;
alpar@1
   171
         case LUF_ESING:
alpar@1
   172
            ret = FHV_ESING;
alpar@1
   173
            goto done;
alpar@1
   174
         case LUF_ECOND:
alpar@1
   175
            ret = FHV_ECOND;
alpar@1
   176
            goto done;
alpar@1
   177
         default:
alpar@1
   178
            xassert(fhv != fhv);
alpar@1
   179
      }
alpar@1
   180
      /* the basis matrix has been successfully factorized */
alpar@1
   181
      fhv->valid = 1;
alpar@1
   182
      /* H := I */
alpar@1
   183
      fhv->hh_nfs = 0;
alpar@1
   184
      /* P0 := P */
alpar@1
   185
      memcpy(&fhv->p0_row[1], &fhv->luf->pp_row[1], sizeof(int) * m);
alpar@1
   186
      memcpy(&fhv->p0_col[1], &fhv->luf->pp_col[1], sizeof(int) * m);
alpar@1
   187
      /* currently H has no factors */
alpar@1
   188
      fhv->nnz_h = 0;
alpar@1
   189
      ret = 0;
alpar@1
   190
done: /* return to the calling program */
alpar@1
   191
      return ret;
alpar@1
   192
}
alpar@1
   193
alpar@1
   194
/***********************************************************************
alpar@1
   195
*  NAME
alpar@1
   196
*
alpar@1
   197
*  fhv_h_solve - solve system H*x = b or H'*x = b
alpar@1
   198
*
alpar@1
   199
*  SYNOPSIS
alpar@1
   200
*
alpar@1
   201
*  #include "glpfhv.h"
alpar@1
   202
*  void fhv_h_solve(FHV *fhv, int tr, double x[]);
alpar@1
   203
*
alpar@1
   204
*  DESCRIPTION
alpar@1
   205
*
alpar@1
   206
*  The routine fhv_h_solve solves either the system H*x = b (if the
alpar@1
   207
*  flag tr is zero) or the system H'*x = b (if the flag tr is non-zero),
alpar@1
   208
*  where the matrix H is a component of the factorization specified by
alpar@1
   209
*  the parameter fhv, H' is a matrix transposed to H.
alpar@1
   210
*
alpar@1
   211
*  On entry the array x should contain elements of the right-hand side
alpar@1
   212
*  vector b in locations x[1], ..., x[m], where m is the order of the
alpar@1
   213
*  matrix H. On exit this array will contain elements of the solution
alpar@1
   214
*  vector x in the same locations. */
alpar@1
   215
alpar@1
   216
void fhv_h_solve(FHV *fhv, int tr, double x[])
alpar@1
   217
{     int nfs = fhv->hh_nfs;
alpar@1
   218
      int *hh_ind = fhv->hh_ind;
alpar@1
   219
      int *hh_ptr = fhv->hh_ptr;
alpar@1
   220
      int *hh_len = fhv->hh_len;
alpar@1
   221
      int *sv_ind = fhv->luf->sv_ind;
alpar@1
   222
      double *sv_val = fhv->luf->sv_val;
alpar@1
   223
      int i, k, beg, end, ptr;
alpar@1
   224
      double temp;
alpar@1
   225
      if (!fhv->valid)
alpar@1
   226
         xfault("fhv_h_solve: the factorization is not valid\n");
alpar@1
   227
      if (!tr)
alpar@1
   228
      {  /* solve the system H*x = b */
alpar@1
   229
         for (k = 1; k <= nfs; k++)
alpar@1
   230
         {  i = hh_ind[k];
alpar@1
   231
            temp = x[i];
alpar@1
   232
            beg = hh_ptr[k];
alpar@1
   233
            end = beg + hh_len[k] - 1;
alpar@1
   234
            for (ptr = beg; ptr <= end; ptr++)
alpar@1
   235
               temp -= sv_val[ptr] * x[sv_ind[ptr]];
alpar@1
   236
            x[i] = temp;
alpar@1
   237
         }
alpar@1
   238
      }
alpar@1
   239
      else
alpar@1
   240
      {  /* solve the system H'*x = b */
alpar@1
   241
         for (k = nfs; k >= 1; k--)
alpar@1
   242
         {  i = hh_ind[k];
alpar@1
   243
            temp = x[i];
alpar@1
   244
            if (temp == 0.0) continue;
alpar@1
   245
            beg = hh_ptr[k];
alpar@1
   246
            end = beg + hh_len[k] - 1;
alpar@1
   247
            for (ptr = beg; ptr <= end; ptr++)
alpar@1
   248
               x[sv_ind[ptr]] -= sv_val[ptr] * temp;
alpar@1
   249
         }
alpar@1
   250
      }
alpar@1
   251
      return;
alpar@1
   252
}
alpar@1
   253
alpar@1
   254
/***********************************************************************
alpar@1
   255
*  NAME
alpar@1
   256
*
alpar@1
   257
*  fhv_ftran - perform forward transformation (solve system B*x = b)
alpar@1
   258
*
alpar@1
   259
*  SYNOPSIS
alpar@1
   260
*
alpar@1
   261
*  #include "glpfhv.h"
alpar@1
   262
*  void fhv_ftran(FHV *fhv, double x[]);
alpar@1
   263
*
alpar@1
   264
*  DESCRIPTION
alpar@1
   265
*
alpar@1
   266
*  The routine fhv_ftran performs forward transformation, i.e. solves
alpar@1
   267
*  the system B*x = b, where B is the basis matrix, x is the vector of
alpar@1
   268
*  unknowns to be computed, b is the vector of right-hand sides.
alpar@1
   269
*
alpar@1
   270
*  On entry elements of the vector b should be stored in dense format
alpar@1
   271
*  in locations x[1], ..., x[m], where m is the number of rows. On exit
alpar@1
   272
*  the routine stores elements of the vector x in the same locations. */
alpar@1
   273
alpar@1
   274
void fhv_ftran(FHV *fhv, double x[])
alpar@1
   275
{     int *pp_row = fhv->luf->pp_row;
alpar@1
   276
      int *pp_col = fhv->luf->pp_col;
alpar@1
   277
      int *p0_row = fhv->p0_row;
alpar@1
   278
      int *p0_col = fhv->p0_col;
alpar@1
   279
      if (!fhv->valid)
alpar@1
   280
         xfault("fhv_ftran: the factorization is not valid\n");
alpar@1
   281
      /* B = F*H*V, therefore inv(B) = inv(V)*inv(H)*inv(F) */
alpar@1
   282
      fhv->luf->pp_row = p0_row;
alpar@1
   283
      fhv->luf->pp_col = p0_col;
alpar@1
   284
      luf_f_solve(fhv->luf, 0, x);
alpar@1
   285
      fhv->luf->pp_row = pp_row;
alpar@1
   286
      fhv->luf->pp_col = pp_col;
alpar@1
   287
      fhv_h_solve(fhv, 0, x);
alpar@1
   288
      luf_v_solve(fhv->luf, 0, x);
alpar@1
   289
      return;
alpar@1
   290
}
alpar@1
   291
alpar@1
   292
/***********************************************************************
alpar@1
   293
*  NAME
alpar@1
   294
*
alpar@1
   295
*  fhv_btran - perform backward transformation (solve system B'*x = b)
alpar@1
   296
*
alpar@1
   297
*  SYNOPSIS
alpar@1
   298
*
alpar@1
   299
*  #include "glpfhv.h"
alpar@1
   300
*  void fhv_btran(FHV *fhv, double x[]);
alpar@1
   301
*
alpar@1
   302
*  DESCRIPTION
alpar@1
   303
*
alpar@1
   304
*  The routine fhv_btran performs backward transformation, i.e. solves
alpar@1
   305
*  the system B'*x = b, where B' is a matrix transposed to the basis
alpar@1
   306
*  matrix B, x is the vector of unknowns to be computed, b is the vector
alpar@1
   307
*  of right-hand sides.
alpar@1
   308
*
alpar@1
   309
*  On entry elements of the vector b should be stored in dense format
alpar@1
   310
*  in locations x[1], ..., x[m], where m is the number of rows. On exit
alpar@1
   311
*  the routine stores elements of the vector x in the same locations. */
alpar@1
   312
alpar@1
   313
void fhv_btran(FHV *fhv, double x[])
alpar@1
   314
{     int *pp_row = fhv->luf->pp_row;
alpar@1
   315
      int *pp_col = fhv->luf->pp_col;
alpar@1
   316
      int *p0_row = fhv->p0_row;
alpar@1
   317
      int *p0_col = fhv->p0_col;
alpar@1
   318
      if (!fhv->valid)
alpar@1
   319
         xfault("fhv_btran: the factorization is not valid\n");
alpar@1
   320
      /* B = F*H*V, therefore inv(B') = inv(F')*inv(H')*inv(V') */
alpar@1
   321
      luf_v_solve(fhv->luf, 1, x);
alpar@1
   322
      fhv_h_solve(fhv, 1, x);
alpar@1
   323
      fhv->luf->pp_row = p0_row;
alpar@1
   324
      fhv->luf->pp_col = p0_col;
alpar@1
   325
      luf_f_solve(fhv->luf, 1, x);
alpar@1
   326
      fhv->luf->pp_row = pp_row;
alpar@1
   327
      fhv->luf->pp_col = pp_col;
alpar@1
   328
      return;
alpar@1
   329
}
alpar@1
   330
alpar@1
   331
/***********************************************************************
alpar@1
   332
*  NAME
alpar@1
   333
*
alpar@1
   334
*  fhv_update_it - update LP basis factorization
alpar@1
   335
*
alpar@1
   336
*  SYNOPSIS
alpar@1
   337
*
alpar@1
   338
*  #include "glpfhv.h"
alpar@1
   339
*  int fhv_update_it(FHV *fhv, int j, int len, const int ind[],
alpar@1
   340
*     const double val[]);
alpar@1
   341
*
alpar@1
   342
*  DESCRIPTION
alpar@1
   343
*
alpar@1
   344
*  The routine fhv_update_it updates the factorization of the basis
alpar@1
   345
*  matrix B after replacing its j-th column by a new vector.
alpar@1
   346
*
alpar@1
   347
*  The parameter j specifies the number of column of B, which has been
alpar@1
   348
*  replaced, 1 <= j <= m, where m is the order of B.
alpar@1
   349
*
alpar@1
   350
*  Row indices and numerical values of non-zero elements of the new
alpar@1
   351
*  column of B should be placed in locations ind[1], ..., ind[len] and
alpar@1
   352
*  val[1], ..., val[len], resp., where len is the number of non-zeros
alpar@1
   353
*  in the column. Neither zero nor duplicate elements are allowed.
alpar@1
   354
*
alpar@1
   355
*  RETURNS
alpar@1
   356
*
alpar@1
   357
*  0  The factorization has been successfully updated.
alpar@1
   358
*
alpar@1
   359
*  FHV_ESING
alpar@1
   360
*     The adjacent basis matrix is structurally singular, since after
alpar@1
   361
*     changing j-th column of matrix V by the new column (see algorithm
alpar@1
   362
*     below) the case k1 > k2 occured.
alpar@1
   363
*
alpar@1
   364
*  FHV_ECHECK
alpar@1
   365
*     The factorization is inaccurate, since after transforming k2-th
alpar@1
   366
*     row of matrix U = P*V*Q, its diagonal element u[k2,k2] is zero or
alpar@1
   367
*     close to zero,
alpar@1
   368
*
alpar@1
   369
*  FHV_ELIMIT
alpar@1
   370
*     Maximal number of H factors has been reached.
alpar@1
   371
*
alpar@1
   372
*  FHV_EROOM
alpar@1
   373
*     Overflow of the sparse vector area.
alpar@1
   374
*
alpar@1
   375
*  In case of non-zero return code the factorization becomes invalid.
alpar@1
   376
*  It should not be used until it has been recomputed with the routine
alpar@1
   377
*  fhv_factorize.
alpar@1
   378
*
alpar@1
   379
*  ALGORITHM
alpar@1
   380
*
alpar@1
   381
*  The routine fhv_update_it is based on the transformation proposed by
alpar@1
   382
*  Forrest and Tomlin.
alpar@1
   383
*
alpar@1
   384
*  Let j-th column of the basis matrix B have been replaced by new
alpar@1
   385
*  column B[j]. In order to keep the equality B = F*H*V j-th column of
alpar@1
   386
*  matrix V should be replaced by the column inv(F*H)*B[j].
alpar@1
   387
*
alpar@1
   388
*  From the standpoint of matrix U = P*V*Q, replacement of j-th column
alpar@1
   389
*  of matrix V is equivalent to replacement of k1-th column of matrix U,
alpar@1
   390
*  where k1 is determined by permutation matrix Q. Thus, matrix U loses
alpar@1
   391
*  its upper triangular form and becomes the following:
alpar@1
   392
*
alpar@1
   393
*         1   k1       k2   m
alpar@1
   394
*     1   x x * x x x x x x x
alpar@1
   395
*         . x * x x x x x x x
alpar@1
   396
*     k1  . . * x x x x x x x
alpar@1
   397
*         . . * x x x x x x x
alpar@1
   398
*         . . * . x x x x x x
alpar@1
   399
*         . . * . . x x x x x
alpar@1
   400
*         . . * . . . x x x x
alpar@1
   401
*     k2  . . * . . . . x x x
alpar@1
   402
*         . . . . . . . . x x
alpar@1
   403
*     m   . . . . . . . . . x
alpar@1
   404
*
alpar@1
   405
*  where row index k2 corresponds to the lowest non-zero element of
alpar@1
   406
*  k1-th column.
alpar@1
   407
*
alpar@1
   408
*  The routine moves rows and columns k1+1, k1+2, ..., k2 of matrix U
alpar@1
   409
*  by one position to the left and upwards and moves k1-th row and k1-th
alpar@1
   410
*  column to position k2. As the result of such symmetric permutations
alpar@1
   411
*  matrix U becomes the following:
alpar@1
   412
*
alpar@1
   413
*         1   k1       k2   m
alpar@1
   414
*     1   x x x x x x x * x x
alpar@1
   415
*         . x x x x x x * x x
alpar@1
   416
*     k1  . . x x x x x * x x
alpar@1
   417
*         . . . x x x x * x x
alpar@1
   418
*         . . . . x x x * x x
alpar@1
   419
*         . . . . . x x * x x
alpar@1
   420
*         . . . . . . x * x x
alpar@1
   421
*     k2  . . x x x x x * x x
alpar@1
   422
*         . . . . . . . . x x
alpar@1
   423
*     m   . . . . . . . . . x
alpar@1
   424
*
alpar@1
   425
*  Then the routine performs gaussian elimination to eliminate elements
alpar@1
   426
*  u[k2,k1], u[k2,k1+1], ..., u[k2,k2-1] using diagonal elements
alpar@1
   427
*  u[k1,k1], u[k1+1,k1+1], ..., u[k2-1,k2-1] as pivots in the same way
alpar@1
   428
*  as described in comments to the routine luf_factorize (see the module
alpar@1
   429
*  GLPLUF). Note that actually all operations are performed on matrix V,
alpar@1
   430
*  not on matrix U. During the elimination process the routine permutes
alpar@1
   431
*  neither rows nor columns, so only k2-th row of matrix U is changed.
alpar@1
   432
*
alpar@1
   433
*  To keep the main equality B = F*H*V, each time when the routine
alpar@1
   434
*  applies elementary gaussian transformation to the transformed row of
alpar@1
   435
*  matrix V (which corresponds to k2-th row of matrix U), it also adds
alpar@1
   436
*  a new element (gaussian multiplier) to the current row-like factor
alpar@1
   437
*  of matrix H, which corresponds to the transformed row of matrix V. */
alpar@1
   438
alpar@1
   439
int fhv_update_it(FHV *fhv, int j, int len, const int ind[],
alpar@1
   440
      const double val[])
alpar@1
   441
{     int m = fhv->m;
alpar@1
   442
      LUF *luf = fhv->luf;
alpar@1
   443
      int *vr_ptr = luf->vr_ptr;
alpar@1
   444
      int *vr_len = luf->vr_len;
alpar@1
   445
      int *vr_cap = luf->vr_cap;
alpar@1
   446
      double *vr_piv = luf->vr_piv;
alpar@1
   447
      int *vc_ptr = luf->vc_ptr;
alpar@1
   448
      int *vc_len = luf->vc_len;
alpar@1
   449
      int *vc_cap = luf->vc_cap;
alpar@1
   450
      int *pp_row = luf->pp_row;
alpar@1
   451
      int *pp_col = luf->pp_col;
alpar@1
   452
      int *qq_row = luf->qq_row;
alpar@1
   453
      int *qq_col = luf->qq_col;
alpar@1
   454
      int *sv_ind = luf->sv_ind;
alpar@1
   455
      double *sv_val = luf->sv_val;
alpar@1
   456
      double *work = luf->work;
alpar@1
   457
      double eps_tol = luf->eps_tol;
alpar@1
   458
      int *hh_ind = fhv->hh_ind;
alpar@1
   459
      int *hh_ptr = fhv->hh_ptr;
alpar@1
   460
      int *hh_len = fhv->hh_len;
alpar@1
   461
      int *p0_row = fhv->p0_row;
alpar@1
   462
      int *p0_col = fhv->p0_col;
alpar@1
   463
      int *cc_ind = fhv->cc_ind;
alpar@1
   464
      double *cc_val = fhv->cc_val;
alpar@1
   465
      double upd_tol = fhv->upd_tol;
alpar@1
   466
      int i, i_beg, i_end, i_ptr, j_beg, j_end, j_ptr, k, k1, k2, p, q,
alpar@1
   467
         p_beg, p_end, p_ptr, ptr, ret;
alpar@1
   468
      double f, temp;
alpar@1
   469
      if (!fhv->valid)
alpar@1
   470
         xfault("fhv_update_it: the factorization is not valid\n");
alpar@1
   471
      if (!(1 <= j && j <= m))
alpar@1
   472
         xfault("fhv_update_it: j = %d; column number out of range\n",
alpar@1
   473
            j);
alpar@1
   474
      /* check if the new factor of matrix H can be created */
alpar@1
   475
      if (fhv->hh_nfs == fhv->hh_max)
alpar@1
   476
      {  /* maximal number of updates has been reached */
alpar@1
   477
         fhv->valid = 0;
alpar@1
   478
         ret = FHV_ELIMIT;
alpar@1
   479
         goto done;
alpar@1
   480
      }
alpar@1
   481
      /* convert new j-th column of B to dense format */
alpar@1
   482
      for (i = 1; i <= m; i++)
alpar@1
   483
         cc_val[i] = 0.0;
alpar@1
   484
      for (k = 1; k <= len; k++)
alpar@1
   485
      {  i = ind[k];
alpar@1
   486
         if (!(1 <= i && i <= m))
alpar@1
   487
            xfault("fhv_update_it: ind[%d] = %d; row number out of rang"
alpar@1
   488
               "e\n", k, i);
alpar@1
   489
         if (cc_val[i] != 0.0)
alpar@1
   490
            xfault("fhv_update_it: ind[%d] = %d; duplicate row index no"
alpar@1
   491
               "t allowed\n", k, i);
alpar@1
   492
         if (val[k] == 0.0)
alpar@1
   493
            xfault("fhv_update_it: val[%d] = %g; zero element not allow"
alpar@1
   494
               "ed\n", k, val[k]);
alpar@1
   495
         cc_val[i] = val[k];
alpar@1
   496
      }
alpar@1
   497
      /* new j-th column of V := inv(F * H) * (new B[j]) */
alpar@1
   498
      fhv->luf->pp_row = p0_row;
alpar@1
   499
      fhv->luf->pp_col = p0_col;
alpar@1
   500
      luf_f_solve(fhv->luf, 0, cc_val);
alpar@1
   501
      fhv->luf->pp_row = pp_row;
alpar@1
   502
      fhv->luf->pp_col = pp_col;
alpar@1
   503
      fhv_h_solve(fhv, 0, cc_val);
alpar@1
   504
      /* convert new j-th column of V to sparse format */
alpar@1
   505
      len = 0;
alpar@1
   506
      for (i = 1; i <= m; i++)
alpar@1
   507
      {  temp = cc_val[i];
alpar@1
   508
         if (temp == 0.0 || fabs(temp) < eps_tol) continue;
alpar@1
   509
         len++, cc_ind[len] = i, cc_val[len] = temp;
alpar@1
   510
      }
alpar@1
   511
      /* clear old content of j-th column of matrix V */
alpar@1
   512
      j_beg = vc_ptr[j];
alpar@1
   513
      j_end = j_beg + vc_len[j] - 1;
alpar@1
   514
      for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++)
alpar@1
   515
      {  /* get row index of v[i,j] */
alpar@1
   516
         i = sv_ind[j_ptr];
alpar@1
   517
         /* find v[i,j] in the i-th row */
alpar@1
   518
         i_beg = vr_ptr[i];
alpar@1
   519
         i_end = i_beg + vr_len[i] - 1;
alpar@1
   520
         for (i_ptr = i_beg; sv_ind[i_ptr] != j; i_ptr++) /* nop */;
alpar@1
   521
         xassert(i_ptr <= i_end);
alpar@1
   522
         /* remove v[i,j] from the i-th row */
alpar@1
   523
         sv_ind[i_ptr] = sv_ind[i_end];
alpar@1
   524
         sv_val[i_ptr] = sv_val[i_end];
alpar@1
   525
         vr_len[i]--;
alpar@1
   526
      }
alpar@1
   527
      /* now j-th column of matrix V is empty */
alpar@1
   528
      luf->nnz_v -= vc_len[j];
alpar@1
   529
      vc_len[j] = 0;
alpar@1
   530
      /* add new elements of j-th column of matrix V to corresponding
alpar@1
   531
         row lists; determine indices k1 and k2 */
alpar@1
   532
      k1 = qq_row[j], k2 = 0;
alpar@1
   533
      for (ptr = 1; ptr <= len; ptr++)
alpar@1
   534
      {  /* get row index of v[i,j] */
alpar@1
   535
         i = cc_ind[ptr];
alpar@1
   536
         /* at least one unused location is needed in i-th row */
alpar@1
   537
         if (vr_len[i] + 1 > vr_cap[i])
alpar@1
   538
         {  if (luf_enlarge_row(luf, i, vr_len[i] + 10))
alpar@1
   539
            {  /* overflow of the sparse vector area */
alpar@1
   540
               fhv->valid = 0;
alpar@1
   541
               luf->new_sva = luf->sv_size + luf->sv_size;
alpar@1
   542
               xassert(luf->new_sva > luf->sv_size);
alpar@1
   543
               ret = FHV_EROOM;
alpar@1
   544
               goto done;
alpar@1
   545
            }
alpar@1
   546
         }
alpar@1
   547
         /* add v[i,j] to i-th row */
alpar@1
   548
         i_ptr = vr_ptr[i] + vr_len[i];
alpar@1
   549
         sv_ind[i_ptr] = j;
alpar@1
   550
         sv_val[i_ptr] = cc_val[ptr];
alpar@1
   551
         vr_len[i]++;
alpar@1
   552
         /* adjust index k2 */
alpar@1
   553
         if (k2 < pp_col[i]) k2 = pp_col[i];
alpar@1
   554
      }
alpar@1
   555
      /* capacity of j-th column (which is currently empty) should be
alpar@1
   556
         not less than len locations */
alpar@1
   557
      if (vc_cap[j] < len)
alpar@1
   558
      {  if (luf_enlarge_col(luf, j, len))
alpar@1
   559
         {  /* overflow of the sparse vector area */
alpar@1
   560
            fhv->valid = 0;
alpar@1
   561
            luf->new_sva = luf->sv_size + luf->sv_size;
alpar@1
   562
            xassert(luf->new_sva > luf->sv_size);
alpar@1
   563
            ret = FHV_EROOM;
alpar@1
   564
            goto done;
alpar@1
   565
         }
alpar@1
   566
      }
alpar@1
   567
      /* add new elements of matrix V to j-th column list */
alpar@1
   568
      j_ptr = vc_ptr[j];
alpar@1
   569
      memmove(&sv_ind[j_ptr], &cc_ind[1], len * sizeof(int));
alpar@1
   570
      memmove(&sv_val[j_ptr], &cc_val[1], len * sizeof(double));
alpar@1
   571
      vc_len[j] = len;
alpar@1
   572
      luf->nnz_v += len;
alpar@1
   573
      /* if k1 > k2, diagonal element u[k2,k2] of matrix U is zero and
alpar@1
   574
         therefore the adjacent basis matrix is structurally singular */
alpar@1
   575
      if (k1 > k2)
alpar@1
   576
      {  fhv->valid = 0;
alpar@1
   577
         ret = FHV_ESING;
alpar@1
   578
         goto done;
alpar@1
   579
      }
alpar@1
   580
      /* perform implicit symmetric permutations of rows and columns of
alpar@1
   581
         matrix U */
alpar@1
   582
      i = pp_row[k1], j = qq_col[k1];
alpar@1
   583
      for (k = k1; k < k2; k++)
alpar@1
   584
      {  pp_row[k] = pp_row[k+1], pp_col[pp_row[k]] = k;
alpar@1
   585
         qq_col[k] = qq_col[k+1], qq_row[qq_col[k]] = k;
alpar@1
   586
      }
alpar@1
   587
      pp_row[k2] = i, pp_col[i] = k2;
alpar@1
   588
      qq_col[k2] = j, qq_row[j] = k2;
alpar@1
   589
      /* now i-th row of the matrix V is k2-th row of matrix U; since
alpar@1
   590
         no pivoting is used, only this row will be transformed */
alpar@1
   591
      /* copy elements of i-th row of matrix V to the working array and
alpar@1
   592
         remove these elements from matrix V */
alpar@1
   593
      for (j = 1; j <= m; j++) work[j] = 0.0;
alpar@1
   594
      i_beg = vr_ptr[i];
alpar@1
   595
      i_end = i_beg + vr_len[i] - 1;
alpar@1
   596
      for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++)
alpar@1
   597
      {  /* get column index of v[i,j] */
alpar@1
   598
         j = sv_ind[i_ptr];
alpar@1
   599
         /* store v[i,j] to the working array */
alpar@1
   600
         work[j] = sv_val[i_ptr];
alpar@1
   601
         /* find v[i,j] in the j-th column */
alpar@1
   602
         j_beg = vc_ptr[j];
alpar@1
   603
         j_end = j_beg + vc_len[j] - 1;
alpar@1
   604
         for (j_ptr = j_beg; sv_ind[j_ptr] != i; j_ptr++) /* nop */;
alpar@1
   605
         xassert(j_ptr <= j_end);
alpar@1
   606
         /* remove v[i,j] from the j-th column */
alpar@1
   607
         sv_ind[j_ptr] = sv_ind[j_end];
alpar@1
   608
         sv_val[j_ptr] = sv_val[j_end];
alpar@1
   609
         vc_len[j]--;
alpar@1
   610
      }
alpar@1
   611
      /* now i-th row of matrix V is empty */
alpar@1
   612
      luf->nnz_v -= vr_len[i];
alpar@1
   613
      vr_len[i] = 0;
alpar@1
   614
      /* create the next row-like factor of the matrix H; this factor
alpar@1
   615
         corresponds to i-th (transformed) row */
alpar@1
   616
      fhv->hh_nfs++;
alpar@1
   617
      hh_ind[fhv->hh_nfs] = i;
alpar@1
   618
      /* hh_ptr[] will be set later */
alpar@1
   619
      hh_len[fhv->hh_nfs] = 0;
alpar@1
   620
      /* up to (k2 - k1) free locations are needed to add new elements
alpar@1
   621
         to the non-trivial row of the row-like factor */
alpar@1
   622
      if (luf->sv_end - luf->sv_beg < k2 - k1)
alpar@1
   623
      {  luf_defrag_sva(luf);
alpar@1
   624
         if (luf->sv_end - luf->sv_beg < k2 - k1)
alpar@1
   625
         {  /* overflow of the sparse vector area */
alpar@1
   626
            fhv->valid = luf->valid = 0;
alpar@1
   627
            luf->new_sva = luf->sv_size + luf->sv_size;
alpar@1
   628
            xassert(luf->new_sva > luf->sv_size);
alpar@1
   629
            ret = FHV_EROOM;
alpar@1
   630
            goto done;
alpar@1
   631
         }
alpar@1
   632
      }
alpar@1
   633
      /* eliminate subdiagonal elements of matrix U */
alpar@1
   634
      for (k = k1; k < k2; k++)
alpar@1
   635
      {  /* v[p,q] = u[k,k] */
alpar@1
   636
         p = pp_row[k], q = qq_col[k];
alpar@1
   637
         /* this is the crucial point, where even tiny non-zeros should
alpar@1
   638
            not be dropped */
alpar@1
   639
         if (work[q] == 0.0) continue;
alpar@1
   640
         /* compute gaussian multiplier f = v[i,q] / v[p,q] */
alpar@1
   641
         f = work[q] / vr_piv[p];
alpar@1
   642
         /* perform gaussian transformation:
alpar@1
   643
            (i-th row) := (i-th row) - f * (p-th row)
alpar@1
   644
            in order to eliminate v[i,q] = u[k2,k] */
alpar@1
   645
         p_beg = vr_ptr[p];
alpar@1
   646
         p_end = p_beg + vr_len[p] - 1;
alpar@1
   647
         for (p_ptr = p_beg; p_ptr <= p_end; p_ptr++)
alpar@1
   648
            work[sv_ind[p_ptr]] -= f * sv_val[p_ptr];
alpar@1
   649
         /* store new element (gaussian multiplier that corresponds to
alpar@1
   650
            p-th row) in the current row-like factor */
alpar@1
   651
         luf->sv_end--;
alpar@1
   652
         sv_ind[luf->sv_end] = p;
alpar@1
   653
         sv_val[luf->sv_end] = f;
alpar@1
   654
         hh_len[fhv->hh_nfs]++;
alpar@1
   655
      }
alpar@1
   656
      /* set pointer to the current row-like factor of the matrix H
alpar@1
   657
         (if no elements were added to this factor, it is unity matrix
alpar@1
   658
         and therefore can be discarded) */
alpar@1
   659
      if (hh_len[fhv->hh_nfs] == 0)
alpar@1
   660
         fhv->hh_nfs--;
alpar@1
   661
      else
alpar@1
   662
      {  hh_ptr[fhv->hh_nfs] = luf->sv_end;
alpar@1
   663
         fhv->nnz_h += hh_len[fhv->hh_nfs];
alpar@1
   664
      }
alpar@1
   665
      /* store new pivot which corresponds to u[k2,k2] */
alpar@1
   666
      vr_piv[i] = work[qq_col[k2]];
alpar@1
   667
      /* new elements of i-th row of matrix V (which are non-diagonal
alpar@1
   668
         elements u[k2,k2+1], ..., u[k2,m] of matrix U = P*V*Q) now are
alpar@1
   669
         contained in the working array; add them to matrix V */
alpar@1
   670
      len = 0;
alpar@1
   671
      for (k = k2+1; k <= m; k++)
alpar@1
   672
      {  /* get column index and value of v[i,j] = u[k2,k] */
alpar@1
   673
         j = qq_col[k];
alpar@1
   674
         temp = work[j];
alpar@1
   675
         /* if v[i,j] is close to zero, skip it */
alpar@1
   676
         if (fabs(temp) < eps_tol) continue;
alpar@1
   677
         /* at least one unused location is needed in j-th column */
alpar@1
   678
         if (vc_len[j] + 1 > vc_cap[j])
alpar@1
   679
         {  if (luf_enlarge_col(luf, j, vc_len[j] + 10))
alpar@1
   680
            {  /* overflow of the sparse vector area */
alpar@1
   681
               fhv->valid = 0;
alpar@1
   682
               luf->new_sva = luf->sv_size + luf->sv_size;
alpar@1
   683
               xassert(luf->new_sva > luf->sv_size);
alpar@1
   684
               ret = FHV_EROOM;
alpar@1
   685
               goto done;
alpar@1
   686
            }
alpar@1
   687
         }
alpar@1
   688
         /* add v[i,j] to j-th column */
alpar@1
   689
         j_ptr = vc_ptr[j] + vc_len[j];
alpar@1
   690
         sv_ind[j_ptr] = i;
alpar@1
   691
         sv_val[j_ptr] = temp;
alpar@1
   692
         vc_len[j]++;
alpar@1
   693
         /* also store v[i,j] to the auxiliary array */
alpar@1
   694
         len++, cc_ind[len] = j, cc_val[len] = temp;
alpar@1
   695
      }
alpar@1
   696
      /* capacity of i-th row (which is currently empty) should be not
alpar@1
   697
         less than len locations */
alpar@1
   698
      if (vr_cap[i] < len)
alpar@1
   699
      {  if (luf_enlarge_row(luf, i, len))
alpar@1
   700
         {  /* overflow of the sparse vector area */
alpar@1
   701
            fhv->valid = 0;
alpar@1
   702
            luf->new_sva = luf->sv_size + luf->sv_size;
alpar@1
   703
            xassert(luf->new_sva > luf->sv_size);
alpar@1
   704
            ret = FHV_EROOM;
alpar@1
   705
            goto done;
alpar@1
   706
         }
alpar@1
   707
      }
alpar@1
   708
      /* add new elements to i-th row list */
alpar@1
   709
      i_ptr = vr_ptr[i];
alpar@1
   710
      memmove(&sv_ind[i_ptr], &cc_ind[1], len * sizeof(int));
alpar@1
   711
      memmove(&sv_val[i_ptr], &cc_val[1], len * sizeof(double));
alpar@1
   712
      vr_len[i] = len;
alpar@1
   713
      luf->nnz_v += len;
alpar@1
   714
      /* updating is finished; check that diagonal element u[k2,k2] is
alpar@1
   715
         not very small in absolute value among other elements in k2-th
alpar@1
   716
         row and k2-th column of matrix U = P*V*Q */
alpar@1
   717
      /* temp = max(|u[k2,*]|, |u[*,k2]|) */
alpar@1
   718
      temp = 0.0;
alpar@1
   719
      /* walk through k2-th row of U which is i-th row of V */
alpar@1
   720
      i = pp_row[k2];
alpar@1
   721
      i_beg = vr_ptr[i];
alpar@1
   722
      i_end = i_beg + vr_len[i] - 1;
alpar@1
   723
      for (i_ptr = i_beg; i_ptr <= i_end; i_ptr++)
alpar@1
   724
         if (temp < fabs(sv_val[i_ptr])) temp = fabs(sv_val[i_ptr]);
alpar@1
   725
      /* walk through k2-th column of U which is j-th column of V */
alpar@1
   726
      j = qq_col[k2];
alpar@1
   727
      j_beg = vc_ptr[j];
alpar@1
   728
      j_end = j_beg + vc_len[j] - 1;
alpar@1
   729
      for (j_ptr = j_beg; j_ptr <= j_end; j_ptr++)
alpar@1
   730
         if (temp < fabs(sv_val[j_ptr])) temp = fabs(sv_val[j_ptr]);
alpar@1
   731
      /* check that u[k2,k2] is not very small */
alpar@1
   732
      if (fabs(vr_piv[i]) < upd_tol * temp)
alpar@1
   733
      {  /* the factorization seems to be inaccurate and therefore must
alpar@1
   734
            be recomputed */
alpar@1
   735
         fhv->valid = 0;
alpar@1
   736
         ret = FHV_ECHECK;
alpar@1
   737
         goto done;
alpar@1
   738
      }
alpar@1
   739
      /* the factorization has been successfully updated */
alpar@1
   740
      ret = 0;
alpar@1
   741
done: /* return to the calling program */
alpar@1
   742
      return ret;
alpar@1
   743
}
alpar@1
   744
alpar@1
   745
/***********************************************************************
alpar@1
   746
*  NAME
alpar@1
   747
*
alpar@1
   748
*  fhv_delete_it - delete LP basis factorization
alpar@1
   749
*
alpar@1
   750
*  SYNOPSIS
alpar@1
   751
*
alpar@1
   752
*  #include "glpfhv.h"
alpar@1
   753
*  void fhv_delete_it(FHV *fhv);
alpar@1
   754
*
alpar@1
   755
*  DESCRIPTION
alpar@1
   756
*
alpar@1
   757
*  The routine fhv_delete_it deletes LP basis factorization specified
alpar@1
   758
*  by the parameter fhv and frees all memory allocated to this program
alpar@1
   759
*  object. */
alpar@1
   760
alpar@1
   761
void fhv_delete_it(FHV *fhv)
alpar@1
   762
{     luf_delete_it(fhv->luf);
alpar@1
   763
      if (fhv->hh_ind != NULL) xfree(fhv->hh_ind);
alpar@1
   764
      if (fhv->hh_ptr != NULL) xfree(fhv->hh_ptr);
alpar@1
   765
      if (fhv->hh_len != NULL) xfree(fhv->hh_len);
alpar@1
   766
      if (fhv->p0_row != NULL) xfree(fhv->p0_row);
alpar@1
   767
      if (fhv->p0_col != NULL) xfree(fhv->p0_col);
alpar@1
   768
      if (fhv->cc_ind != NULL) xfree(fhv->cc_ind);
alpar@1
   769
      if (fhv->cc_val != NULL) xfree(fhv->cc_val);
alpar@1
   770
      xfree(fhv);
alpar@1
   771
      return;
alpar@1
   772
}
alpar@1
   773
alpar@1
   774
/* eof */