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.
 
 
 
 

508 lines
14 KiB

/* bfd.c (LP basis factorization driver) */
/***********************************************************************
* This code is part of GLPK (GNU Linear Programming Kit).
*
* Copyright (C) 2007, 2014 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 "glpk.h"
#include "env.h"
#include "bfd.h"
#include "fhvint.h"
#include "scfint.h"
#ifdef GLP_DEBUG
#include "glpspm.h"
#endif
struct BFD
{ /* LP basis factorization driver */
int valid;
/* factorization is valid only if this flag is set */
int type;
/* type of factorization used:
0 - interface not established yet
1 - FHV-factorization
2 - Schur-complement-based factorization */
union
{ void *none; /* type = 0 */
FHVINT *fhvi; /* type = 1 */
SCFINT *scfi; /* type = 2 */
} u;
/* interface to factorization of LP basis */
glp_bfcp parm;
/* factorization control parameters */
#ifdef GLP_DEBUG
SPM *B;
/* current basis (for testing/debugging only) */
#endif
int upd_cnt;
/* factorization update count */
#if 1 /* 21/IV-2014 */
double b_norm;
/* 1-norm of matrix B */
double i_norm;
/* estimated 1-norm of matrix inv(B) */
#endif
};
BFD *bfd_create_it(void)
{ /* create LP basis factorization */
BFD *bfd;
#ifdef GLP_DEBUG
xprintf("bfd_create_it: warning: debugging version used\n");
#endif
bfd = talloc(1, BFD);
bfd->valid = 0;
bfd->type = 0;
bfd->u.none = NULL;
bfd_set_bfcp(bfd, NULL);
#ifdef GLP_DEBUG
bfd->B = NULL;
#endif
bfd->upd_cnt = 0;
return bfd;
}
#if 0 /* 08/III-2014 */
void bfd_set_parm(BFD *bfd, const void *parm)
{ /* change LP basis factorization control parameters */
memcpy(&bfd->parm, parm, sizeof(glp_bfcp));
return;
}
#endif
void bfd_get_bfcp(BFD *bfd, void /* glp_bfcp */ *parm)
{ /* retrieve LP basis factorization control parameters */
memcpy(parm, &bfd->parm, sizeof(glp_bfcp));
return;
}
void bfd_set_bfcp(BFD *bfd, const void /* glp_bfcp */ *parm)
{ /* change LP basis factorization control parameters */
if (parm == NULL)
{ /* reset to default */
memset(&bfd->parm, 0, sizeof(glp_bfcp));
bfd->parm.type = GLP_BF_LUF + GLP_BF_FT;
bfd->parm.piv_tol = 0.10;
bfd->parm.piv_lim = 4;
bfd->parm.suhl = 1;
bfd->parm.eps_tol = DBL_EPSILON;
bfd->parm.nfs_max = 100;
bfd->parm.nrs_max = 70;
}
else
memcpy(&bfd->parm, parm, sizeof(glp_bfcp));
return;
}
#if 1 /* 21/IV-2014 */
struct bfd_info
{ BFD *bfd;
int (*col)(void *info, int j, int ind[], double val[]);
void *info;
};
static int bfd_col(void *info_, int j, int ind[], double val[])
{ struct bfd_info *info = info_;
int t, len;
double sum;
len = info->col(info->info, j, ind, val);
sum = 0.0;
for (t = 1; t <= len; t++)
{ if (val[t] >= 0.0)
sum += val[t];
else
sum -= val[t];
}
if (info->bfd->b_norm < sum)
info->bfd->b_norm = sum;
return len;
}
#endif
int bfd_factorize(BFD *bfd, int m, /*const int bh[],*/ int (*col1)
(void *info, int j, int ind[], double val[]), void *info1)
{ /* compute LP basis factorization */
#if 1 /* 21/IV-2014 */
struct bfd_info info;
#endif
int type, ret;
/*xassert(bh == bh);*/
/* invalidate current factorization */
bfd->valid = 0;
/* determine required factorization type */
switch (bfd->parm.type)
{ case GLP_BF_LUF + GLP_BF_FT:
type = 1;
break;
case GLP_BF_LUF + GLP_BF_BG:
case GLP_BF_LUF + GLP_BF_GR:
case GLP_BF_BTF + GLP_BF_BG:
case GLP_BF_BTF + GLP_BF_GR:
type = 2;
break;
default:
xassert(bfd != bfd);
}
/* delete factorization interface, if necessary */
switch (bfd->type)
{ case 0:
break;
case 1:
if (type != 1)
{ bfd->type = 0;
fhvint_delete(bfd->u.fhvi);
bfd->u.fhvi = NULL;
}
break;
case 2:
if (type != 2)
{ bfd->type = 0;
scfint_delete(bfd->u.scfi);
bfd->u.scfi = NULL;
}
break;
default:
xassert(bfd != bfd);
}
/* establish factorization interface, if necessary */
if (bfd->type == 0)
{ switch (type)
{ case 1:
bfd->type = 1;
xassert(bfd->u.fhvi == NULL);
bfd->u.fhvi = fhvint_create();
break;
case 2:
bfd->type = 2;
xassert(bfd->u.scfi == NULL);
if (!(bfd->parm.type & GLP_BF_BTF))
bfd->u.scfi = scfint_create(1);
else
bfd->u.scfi = scfint_create(2);
break;
default:
xassert(type != type);
}
}
/* try to compute factorization */
#if 1 /* 21/IV-2014 */
bfd->b_norm = bfd->i_norm = 0.0;
info.bfd = bfd;
info.col = col1;
info.info = info1;
#endif
switch (bfd->type)
{ case 1:
bfd->u.fhvi->lufi->sgf_piv_tol = bfd->parm.piv_tol;
bfd->u.fhvi->lufi->sgf_piv_lim = bfd->parm.piv_lim;
bfd->u.fhvi->lufi->sgf_suhl = bfd->parm.suhl;
bfd->u.fhvi->lufi->sgf_eps_tol = bfd->parm.eps_tol;
bfd->u.fhvi->nfs_max = bfd->parm.nfs_max;
ret = fhvint_factorize(bfd->u.fhvi, m, bfd_col, &info);
#if 1 /* FIXME */
if (ret == 0)
bfd->i_norm = fhvint_estimate(bfd->u.fhvi);
else
ret = BFD_ESING;
#endif
break;
case 2:
if (bfd->u.scfi->scf.type == 1)
{ bfd->u.scfi->u.lufi->sgf_piv_tol = bfd->parm.piv_tol;
bfd->u.scfi->u.lufi->sgf_piv_lim = bfd->parm.piv_lim;
bfd->u.scfi->u.lufi->sgf_suhl = bfd->parm.suhl;
bfd->u.scfi->u.lufi->sgf_eps_tol = bfd->parm.eps_tol;
}
else if (bfd->u.scfi->scf.type == 2)
{ bfd->u.scfi->u.btfi->sgf_piv_tol = bfd->parm.piv_tol;
bfd->u.scfi->u.btfi->sgf_piv_lim = bfd->parm.piv_lim;
bfd->u.scfi->u.btfi->sgf_suhl = bfd->parm.suhl;
bfd->u.scfi->u.btfi->sgf_eps_tol = bfd->parm.eps_tol;
}
else
xassert(bfd != bfd);
bfd->u.scfi->nn_max = bfd->parm.nrs_max;
ret = scfint_factorize(bfd->u.scfi, m, bfd_col, &info);
#if 1 /* FIXME */
if (ret == 0)
bfd->i_norm = scfint_estimate(bfd->u.scfi);
else
ret = BFD_ESING;
#endif
break;
default:
xassert(bfd != bfd);
}
#ifdef GLP_DEBUG
/* save specified LP basis */
if (bfd->B != NULL)
spm_delete_mat(bfd->B);
bfd->B = spm_create_mat(m, m);
{ int *ind = talloc(1+m, int);
double *val = talloc(1+m, double);
int j, k, len;
for (j = 1; j <= m; j++)
{ len = col(info, j, ind, val);
for (k = 1; k <= len; k++)
spm_new_elem(bfd->B, ind[k], j, val[k]);
}
tfree(ind);
tfree(val);
}
#endif
if (ret == 0)
{ /* factorization has been successfully computed */
double cond;
bfd->valid = 1;
#ifdef GLP_DEBUG
cond = bfd_condest(bfd);
if (cond > 1e9)
xprintf("bfd_factorize: warning: cond(B) = %g\n", cond);
#endif
}
#ifdef GLP_DEBUG
xprintf("bfd_factorize: m = %d; ret = %d\n", m, ret);
#endif
bfd->upd_cnt = 0;
return ret;
}
#if 0 /* 21/IV-2014 */
double bfd_estimate(BFD *bfd)
{ /* estimate 1-norm of inv(B) */
double norm;
xassert(bfd->valid);
xassert(bfd->upd_cnt == 0);
switch (bfd->type)
{ case 1:
norm = fhvint_estimate(bfd->u.fhvi);
break;
case 2:
norm = scfint_estimate(bfd->u.scfi);
break;
default:
xassert(bfd != bfd);
}
return norm;
}
#endif
#if 1 /* 21/IV-2014 */
double bfd_condest(BFD *bfd)
{ /* estimate condition of B */
double cond;
xassert(bfd->valid);
/*xassert(bfd->upd_cnt == 0);*/
cond = bfd->b_norm * bfd->i_norm;
if (cond < 1.0)
cond = 1.0;
return cond;
}
#endif
void bfd_ftran(BFD *bfd, double x[])
{ /* perform forward transformation (solve system B * x = b) */
#ifdef GLP_DEBUG
SPM *B = bfd->B;
int m = B->m;
double *b = talloc(1+m, double);
SPME *e;
int k;
double s, relerr, maxerr;
for (k = 1; k <= m; k++)
b[k] = x[k];
#endif
xassert(bfd->valid);
switch (bfd->type)
{ case 1:
fhvint_ftran(bfd->u.fhvi, x);
break;
case 2:
scfint_ftran(bfd->u.scfi, x);
break;
default:
xassert(bfd != bfd);
}
#ifdef GLP_DEBUG
maxerr = 0.0;
for (k = 1; k <= m; k++)
{ s = 0.0;
for (e = B->row[k]; e != NULL; e = e->r_next)
s += e->val * x[e->j];
relerr = (b[k] - s) / (1.0 + fabs(b[k]));
if (maxerr < relerr)
maxerr = relerr;
}
if (maxerr > 1e-8)
xprintf("bfd_ftran: maxerr = %g; relative error too large\n",
maxerr);
tfree(b);
#endif
return;
}
void bfd_btran(BFD *bfd, double x[])
{ /* perform backward transformation (solve system B'* x = b) */
#ifdef GLP_DEBUG
SPM *B = bfd->B;
int m = B->m;
double *b = talloc(1+m, double);
SPME *e;
int k;
double s, relerr, maxerr;
for (k = 1; k <= m; k++)
b[k] = x[k];
#endif
xassert(bfd->valid);
switch (bfd->type)
{ case 1:
fhvint_btran(bfd->u.fhvi, x);
break;
case 2:
scfint_btran(bfd->u.scfi, x);
break;
default:
xassert(bfd != bfd);
}
#ifdef GLP_DEBUG
maxerr = 0.0;
for (k = 1; k <= m; k++)
{ s = 0.0;
for (e = B->col[k]; e != NULL; e = e->c_next)
s += e->val * x[e->i];
relerr = (b[k] - s) / (1.0 + fabs(b[k]));
if (maxerr < relerr)
maxerr = relerr;
}
if (maxerr > 1e-8)
xprintf("bfd_btran: maxerr = %g; relative error too large\n",
maxerr);
tfree(b);
#endif
return;
}
int bfd_update(BFD *bfd, int j, int len, const int ind[], const double
val[])
{ /* update LP basis factorization */
int ret;
xassert(bfd->valid);
switch (bfd->type)
{ case 1:
ret = fhvint_update(bfd->u.fhvi, j, len, ind, val);
#if 1 /* FIXME */
switch (ret)
{ case 0:
break;
case 1:
ret = BFD_ESING;
break;
case 2:
case 3:
ret = BFD_ECOND;
break;
case 4:
ret = BFD_ELIMIT;
break;
case 5:
ret = BFD_ECHECK;
break;
default:
xassert(ret != ret);
}
#endif
break;
case 2:
switch (bfd->parm.type & 0x0F)
{ case GLP_BF_BG:
ret = scfint_update(bfd->u.scfi, 1, j, len, ind, val);
break;
case GLP_BF_GR:
ret = scfint_update(bfd->u.scfi, 2, j, len, ind, val);
break;
default:
xassert(bfd != bfd);
}
#if 1 /* FIXME */
switch (ret)
{ case 0:
break;
case 1:
ret = BFD_ELIMIT;
break;
case 2:
ret = BFD_ECOND;
break;
default:
xassert(ret != ret);
}
#endif
break;
default:
xassert(bfd != bfd);
}
if (ret != 0)
{ /* updating factorization failed */
bfd->valid = 0;
}
#ifdef GLP_DEBUG
/* save updated LP basis */
{ SPME *e;
int k;
for (e = bfd->B->col[j]; e != NULL; e = e->c_next)
e->val = 0.0;
spm_drop_zeros(bfd->B, 0.0);
for (k = 1; k <= len; k++)
spm_new_elem(bfd->B, ind[k], j, val[k]);
}
#endif
if (ret == 0)
bfd->upd_cnt++;
return ret;
}
int bfd_get_count(BFD *bfd)
{ /* determine factorization update count */
return bfd->upd_cnt;
}
void bfd_delete_it(BFD *bfd)
{ /* delete LP basis factorization */
switch (bfd->type)
{ case 0:
break;
case 1:
fhvint_delete(bfd->u.fhvi);
break;
case 2:
scfint_delete(bfd->u.scfi);
break;
default:
xassert(bfd != bfd);
}
#ifdef GLP_DEBUG
if (bfd->B != NULL)
spm_delete_mat(bfd->B);
#endif
tfree(bfd);
return;
}
/* eof */