Add graph references
This commit is contained in:
+407
@@ -0,0 +1,407 @@
|
||||
/* cfg.c (conflict graph) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2012-2013 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "cfg.h"
|
||||
#include "env.h"
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_create_graph - create conflict graph
|
||||
*
|
||||
* This routine creates the conflict graph, which initially is empty,
|
||||
* and returns a pointer to the graph descriptor.
|
||||
*
|
||||
* The parameter n specifies the number of *all* variables in MIP, for
|
||||
* which the conflict graph will be built.
|
||||
*
|
||||
* The parameter nv_max specifies maximal number of vertices in the
|
||||
* conflict graph. It should be the double number of binary variables
|
||||
* in corresponding MIP. */
|
||||
|
||||
CFG *cfg_create_graph(int n, int nv_max)
|
||||
{ CFG *G;
|
||||
xassert(n >= 0);
|
||||
xassert(0 <= nv_max && nv_max <= n + n);
|
||||
G = talloc(1, CFG);
|
||||
G->n = n;
|
||||
G->pos = talloc(1+n, int);
|
||||
memset(&G->pos[1], 0, n * sizeof(int));
|
||||
G->neg = talloc(1+n, int);
|
||||
memset(&G->neg[1], 0, n * sizeof(int));
|
||||
G->pool = dmp_create_pool();
|
||||
G->nv_max = nv_max;
|
||||
G->nv = 0;
|
||||
G->ref = talloc(1+nv_max, int);
|
||||
G->vptr = talloc(1+nv_max, CFGVLE *);
|
||||
G->cptr = talloc(1+nv_max, CFGCLE *);
|
||||
return G;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_add_clique - add clique to conflict graph
|
||||
*
|
||||
* This routine adds a clique to the conflict graph.
|
||||
*
|
||||
* The parameter size specifies the clique size, size >= 2. Note that
|
||||
* any edge can be considered as a clique of size 2.
|
||||
*
|
||||
* The array ind specifies vertices constituting the clique in elements
|
||||
* ind[k], 1 <= k <= size:
|
||||
*
|
||||
* ind[k] = +j means a vertex of the conflict graph that corresponds to
|
||||
* original binary variable x[j], 1 <= j <= n.
|
||||
*
|
||||
* ind[k] = -j means a vertex of the conflict graph that corresponds to
|
||||
* complement of original binary variable x[j], 1 <= j <= n.
|
||||
*
|
||||
* Note that if both vertices for x[j] and (1 - x[j]) have appeared in
|
||||
* the conflict graph, the routine automatically adds an edge incident
|
||||
* to these vertices. */
|
||||
|
||||
static void add_edge(CFG *G, int v, int w)
|
||||
{ /* add clique of size 2 */
|
||||
DMP *pool = G->pool;
|
||||
int nv = G->nv;
|
||||
CFGVLE **vptr = G->vptr;
|
||||
CFGVLE *vle;
|
||||
xassert(1 <= v && v <= nv);
|
||||
xassert(1 <= w && w <= nv);
|
||||
xassert(v != w);
|
||||
vle = dmp_talloc(pool, CFGVLE);
|
||||
vle->v = w;
|
||||
vle->next = vptr[v];
|
||||
vptr[v] = vle;
|
||||
vle = dmp_talloc(pool, CFGVLE);
|
||||
vle->v = v;
|
||||
vle->next = vptr[w];
|
||||
vptr[w] = vle;
|
||||
return;
|
||||
}
|
||||
|
||||
void cfg_add_clique(CFG *G, int size, const int ind[])
|
||||
{ int n = G->n;
|
||||
int *pos = G->pos;
|
||||
int *neg = G->neg;
|
||||
DMP *pool = G->pool;
|
||||
int nv_max = G->nv_max;
|
||||
int *ref = G->ref;
|
||||
CFGVLE **vptr = G->vptr;
|
||||
CFGCLE **cptr = G->cptr;
|
||||
int j, k, v;
|
||||
xassert(2 <= size && size <= nv_max);
|
||||
/* add new vertices to the conflict graph */
|
||||
for (k = 1; k <= size; k++)
|
||||
{ j = ind[k];
|
||||
if (j > 0)
|
||||
{ /* vertex corresponds to x[j] */
|
||||
xassert(1 <= j && j <= n);
|
||||
if (pos[j] == 0)
|
||||
{ /* no such vertex exists; add it */
|
||||
v = pos[j] = ++(G->nv);
|
||||
xassert(v <= nv_max);
|
||||
ref[v] = j;
|
||||
vptr[v] = NULL;
|
||||
cptr[v] = NULL;
|
||||
if (neg[j] != 0)
|
||||
{ /* now both vertices for x[j] and (1 - x[j]) exist */
|
||||
add_edge(G, v, neg[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* vertex corresponds to (1 - x[j]) */
|
||||
j = -j;
|
||||
xassert(1 <= j && j <= n);
|
||||
if (neg[j] == 0)
|
||||
{ /* no such vertex exists; add it */
|
||||
v = neg[j] = ++(G->nv);
|
||||
xassert(v <= nv_max);
|
||||
ref[v] = j;
|
||||
vptr[v] = NULL;
|
||||
cptr[v] = NULL;
|
||||
if (pos[j] != 0)
|
||||
{ /* now both vertices for x[j] and (1 - x[j]) exist */
|
||||
add_edge(G, v, pos[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* add specified clique to the conflict graph */
|
||||
if (size == 2)
|
||||
add_edge(G,
|
||||
ind[1] > 0 ? pos[+ind[1]] : neg[-ind[1]],
|
||||
ind[2] > 0 ? pos[+ind[2]] : neg[-ind[2]]);
|
||||
else
|
||||
{ CFGVLE *vp, *vle;
|
||||
CFGCLE *cle;
|
||||
/* build list of clique vertices */
|
||||
vp = NULL;
|
||||
for (k = 1; k <= size; k++)
|
||||
{ vle = dmp_talloc(pool, CFGVLE);
|
||||
vle->v = ind[k] > 0 ? pos[+ind[k]] : neg[-ind[k]];
|
||||
vle->next = vp;
|
||||
vp = vle;
|
||||
}
|
||||
/* attach the clique to all its vertices */
|
||||
for (k = 1; k <= size; k++)
|
||||
{ cle = dmp_talloc(pool, CFGCLE);
|
||||
cle->vptr = vp;
|
||||
v = ind[k] > 0 ? pos[+ind[k]] : neg[-ind[k]];
|
||||
cle->next = cptr[v];
|
||||
cptr[v] = cle;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_get_adjacent - get vertices adjacent to specified vertex
|
||||
*
|
||||
* This routine stores numbers of all vertices adjacent to specified
|
||||
* vertex v of the conflict graph in locations ind[1], ..., ind[len],
|
||||
* and returns len, 1 <= len <= nv-1, where nv is the total number of
|
||||
* vertices in the conflict graph.
|
||||
*
|
||||
* Note that the conflict graph defined by this routine has neither
|
||||
* self-loops nor multiple edges. */
|
||||
|
||||
int cfg_get_adjacent(CFG *G, int v, int ind[])
|
||||
{ int nv = G->nv;
|
||||
int *ref = G->ref;
|
||||
CFGVLE **vptr = G->vptr;
|
||||
CFGCLE **cptr = G->cptr;
|
||||
CFGVLE *vle;
|
||||
CFGCLE *cle;
|
||||
int k, w, len;
|
||||
xassert(1 <= v && v <= nv);
|
||||
len = 0;
|
||||
/* walk thru the list of adjacent vertices */
|
||||
for (vle = vptr[v]; vle != NULL; vle = vle->next)
|
||||
{ w = vle->v;
|
||||
xassert(1 <= w && w <= nv);
|
||||
xassert(w != v);
|
||||
if (ref[w] > 0)
|
||||
{ ind[++len] = w;
|
||||
ref[w] = -ref[w];
|
||||
}
|
||||
}
|
||||
/* walk thru the list of incident cliques */
|
||||
for (cle = cptr[v]; cle != NULL; cle = cle->next)
|
||||
{ /* walk thru the list of clique vertices */
|
||||
for (vle = cle->vptr; vle != NULL; vle = vle->next)
|
||||
{ w = vle->v;
|
||||
xassert(1 <= w && w <= nv);
|
||||
if (w != v && ref[w] > 0)
|
||||
{ ind[++len] = w;
|
||||
ref[w] = -ref[w];
|
||||
}
|
||||
}
|
||||
}
|
||||
xassert(1 <= len && len < nv);
|
||||
/* unmark vertices included in the resultant adjacency list */
|
||||
for (k = 1; k <= len; k++)
|
||||
{ w = ind[k];
|
||||
ref[w] = -ref[w];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_expand_clique - expand specified clique to maximal clique
|
||||
*
|
||||
* Given some clique in the conflict graph this routine expands it to
|
||||
* a maximal clique by including in it new vertices.
|
||||
*
|
||||
* On entry vertex indices constituting the initial clique should be
|
||||
* stored in locations c_ind[1], ..., c_ind[c_len], where c_len is the
|
||||
* initial clique size. On exit the routine stores new vertex indices
|
||||
* to locations c_ind[c_len+1], ..., c_ind[c_len'], where c_len' is the
|
||||
* size of the maximal clique found, and returns c_len'.
|
||||
*
|
||||
* ALGORITHM
|
||||
*
|
||||
* Let G = (V, E) be a graph, C within V be a current clique to be
|
||||
* expanded, and D within V \ C be a subset of vertices adjacent to all
|
||||
* vertices from C. On every iteration the routine chooses some vertex
|
||||
* v in D, includes it into C, and removes from D the vertex v as well
|
||||
* as all vertices not adjacent to v. Initially C is empty and D = V.
|
||||
* Iterations repeat until D becomes an empty set. Obviously, the final
|
||||
* set C is a maximal clique in G.
|
||||
*
|
||||
* Now let C0 be an initial clique, and we want C0 to be a subset of
|
||||
* the final maximal clique C. To provide this condition the routine
|
||||
* starts constructing C by choosing only such vertices v in D, which
|
||||
* are in C0, until all vertices from C0 have been included in C. May
|
||||
* note that if on some iteration C0 \ C is non-empty (i.e. if not all
|
||||
* vertices from C0 have been included in C), C0 \ C is a subset of D,
|
||||
* because C0 is a clique. */
|
||||
|
||||
static int intersection(int d_len, int d_ind[], int d_pos[], int len,
|
||||
const int ind[])
|
||||
{ /* compute intersection D := D inter W, where W is some specified
|
||||
* set of vertices */
|
||||
int k, t, v, new_len;
|
||||
/* walk thru vertices in W and mark vertices in D */
|
||||
for (t = 1; t <= len; t++)
|
||||
{ /* v in W */
|
||||
v = ind[t];
|
||||
/* determine position of v in D */
|
||||
k = d_pos[v];
|
||||
if (k != 0)
|
||||
{ /* v in D */
|
||||
xassert(d_ind[k] == v);
|
||||
/* mark v to keep it in D */
|
||||
d_ind[k] = -v;
|
||||
}
|
||||
}
|
||||
/* remove all unmarked vertices from D */
|
||||
new_len = 0;
|
||||
for (k = 1; k <= d_len; k++)
|
||||
{ /* v in D */
|
||||
v = d_ind[k];
|
||||
if (v < 0)
|
||||
{ /* v is marked; keep it */
|
||||
v = -v;
|
||||
new_len++;
|
||||
d_ind[new_len] = v;
|
||||
d_pos[v] = new_len;
|
||||
}
|
||||
else
|
||||
{ /* v is not marked; remove it */
|
||||
d_pos[v] = 0;
|
||||
}
|
||||
}
|
||||
return new_len;
|
||||
}
|
||||
|
||||
int cfg_expand_clique(CFG *G, int c_len, int c_ind[])
|
||||
{ int nv = G->nv;
|
||||
int d_len, *d_ind, *d_pos, len, *ind;
|
||||
int k, v;
|
||||
xassert(0 <= c_len && c_len <= nv);
|
||||
/* allocate working arrays */
|
||||
d_ind = talloc(1+nv, int);
|
||||
d_pos = talloc(1+nv, int);
|
||||
ind = talloc(1+nv, int);
|
||||
/* initialize C := 0, D := V */
|
||||
d_len = nv;
|
||||
for (k = 1; k <= nv; k++)
|
||||
d_ind[k] = d_pos[k] = k;
|
||||
/* expand C by vertices of specified initial clique C0 */
|
||||
for (k = 1; k <= c_len; k++)
|
||||
{ /* v in C0 */
|
||||
v = c_ind[k];
|
||||
xassert(1 <= v && v <= nv);
|
||||
/* since C0 is clique, v should be in D */
|
||||
xassert(d_pos[v] != 0);
|
||||
/* W := set of vertices adjacent to v */
|
||||
len = cfg_get_adjacent(G, v, ind);
|
||||
/* D := D inter W */
|
||||
d_len = intersection(d_len, d_ind, d_pos, len, ind);
|
||||
/* since v not in W, now v should be not in D */
|
||||
xassert(d_pos[v] == 0);
|
||||
}
|
||||
/* expand C by some other vertices until D is empty */
|
||||
while (d_len > 0)
|
||||
{ /* v in D */
|
||||
v = d_ind[1];
|
||||
xassert(1 <= v && v <= nv);
|
||||
/* note that v is adjacent to all vertices in C (by design),
|
||||
* so add v to C */
|
||||
c_ind[++c_len] = v;
|
||||
/* W := set of vertices adjacent to v */
|
||||
len = cfg_get_adjacent(G, v, ind);
|
||||
/* D := D inter W */
|
||||
d_len = intersection(d_len, d_ind, d_pos, len, ind);
|
||||
/* since v not in W, now v should be not in D */
|
||||
xassert(d_pos[v] == 0);
|
||||
}
|
||||
/* free working arrays */
|
||||
tfree(d_ind);
|
||||
tfree(d_pos);
|
||||
tfree(ind);
|
||||
/* bring maximal clique to calling routine */
|
||||
return c_len;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_check_clique - check clique in conflict graph
|
||||
*
|
||||
* This routine checks that vertices of the conflict graph specified
|
||||
* in locations c_ind[1], ..., c_ind[c_len] constitute a clique.
|
||||
*
|
||||
* NOTE: for testing/debugging only. */
|
||||
|
||||
void cfg_check_clique(CFG *G, int c_len, const int c_ind[])
|
||||
{ int nv = G->nv;
|
||||
int k, kk, v, w, len, *ind;
|
||||
char *flag;
|
||||
ind = talloc(1+nv, int);
|
||||
flag = talloc(1+nv, char);
|
||||
memset(&flag[1], 0, nv);
|
||||
/* walk thru clique vertices */
|
||||
xassert(c_len >= 0);
|
||||
for (k = 1; k <= c_len; k++)
|
||||
{ /* get clique vertex v */
|
||||
v = c_ind[k];
|
||||
xassert(1 <= v && v <= nv);
|
||||
/* get vertices adjacent to vertex v */
|
||||
len = cfg_get_adjacent(G, v, ind);
|
||||
for (kk = 1; kk <= len; kk++)
|
||||
{ w = ind[kk];
|
||||
xassert(1 <= w && w <= nv);
|
||||
xassert(w != v);
|
||||
flag[w] = 1;
|
||||
}
|
||||
/* check that all clique vertices other than v are adjacent
|
||||
to v */
|
||||
for (kk = 1; kk <= c_len; kk++)
|
||||
{ w = c_ind[kk];
|
||||
xassert(1 <= w && w <= nv);
|
||||
if (w != v)
|
||||
xassert(flag[w]);
|
||||
}
|
||||
/* reset vertex flags */
|
||||
for (kk = 1; kk <= len; kk++)
|
||||
flag[ind[kk]] = 0;
|
||||
}
|
||||
tfree(ind);
|
||||
tfree(flag);
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_delete_graph - delete conflict graph
|
||||
*
|
||||
* This routine deletes the conflict graph by freeing all the memory
|
||||
* allocated to this program object. */
|
||||
|
||||
void cfg_delete_graph(CFG *G)
|
||||
{ tfree(G->pos);
|
||||
tfree(G->neg);
|
||||
dmp_delete_pool(G->pool);
|
||||
tfree(G->ref);
|
||||
tfree(G->vptr);
|
||||
tfree(G->cptr);
|
||||
tfree(G);
|
||||
return;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/* cfg.h (conflict graph) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2012-2013 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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/>.
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef CFG_H
|
||||
#define CFG_H
|
||||
|
||||
#include "dmp.h"
|
||||
|
||||
/***********************************************************************
|
||||
* The structure CFG describes the conflict graph.
|
||||
*
|
||||
* Conflict graph is an undirected graph G = (V, E), where V is a set
|
||||
* of vertices, E <= V x V is a set of edges. Each vertex v in V of the
|
||||
* conflict graph corresponds to a binary variable z[v], which is
|
||||
* either an original binary variable x[j] or its complement 1 - x[j].
|
||||
* Edge (v,w) in E means that z[v] and z[w] cannot take the value 1 at
|
||||
* the same time, i.e. it defines an inequality z[v] + z[w] <= 1, which
|
||||
* is assumed to be valid for original MIP.
|
||||
*
|
||||
* Since the conflict graph may be dense, it is stored as an union of
|
||||
* its cliques rather than explicitly. */
|
||||
|
||||
#if 0 /* 08/III-2016 */
|
||||
typedef struct CFG CFG;
|
||||
#else
|
||||
typedef struct glp_cfg CFG;
|
||||
#endif
|
||||
typedef struct CFGVLE CFGVLE;
|
||||
typedef struct CFGCLE CFGCLE;
|
||||
|
||||
#if 0 /* 08/III-2016 */
|
||||
struct CFG
|
||||
#else
|
||||
struct glp_cfg
|
||||
#endif
|
||||
{ /* conflict graph descriptor */
|
||||
int n;
|
||||
/* number of *all* variables (columns) in corresponding MIP */
|
||||
int *pos; /* int pos[1+n]; */
|
||||
/* pos[0] is not used;
|
||||
* pos[j] = v, 1 <= j <= n, means that vertex v corresponds to
|
||||
* original binary variable x[j], and pos[j] = 0 means that the
|
||||
* conflict graph has no such vertex */
|
||||
int *neg; /* int neg[1+n]; */
|
||||
/* neg[0] is not used;
|
||||
* neg[j] = v, 1 <= j <= n, means that vertex v corresponds to
|
||||
* complement of original binary variable x[j], and neg[j] = 0
|
||||
* means that the conflict graph has no such vertex */
|
||||
DMP *pool;
|
||||
/* memory pool to allocate elements of the conflict graph */
|
||||
int nv_max;
|
||||
/* maximal number of vertices in the conflict graph */
|
||||
int nv;
|
||||
/* current number of vertices in the conflict graph */
|
||||
int *ref; /* int ref[1+nv_max]; */
|
||||
/* ref[v] = j, 1 <= v <= nv, means that vertex v corresponds
|
||||
* either to original binary variable x[j] or to its complement,
|
||||
* i.e. either pos[j] = v or neg[j] = v */
|
||||
CFGVLE **vptr; /* CFGVLE *vptr[1+nv_max]; */
|
||||
/* vptr[v], 1 <= v <= nv, is an initial pointer to the list of
|
||||
* vertices adjacent to vertex v */
|
||||
CFGCLE **cptr; /* CFGCLE *cptr[1+nv_max]; */
|
||||
/* cptr[v], 1 <= v <= nv, is an initial pointer to the list of
|
||||
* cliques that contain vertex v */
|
||||
};
|
||||
|
||||
struct CFGVLE
|
||||
{ /* vertex list element */
|
||||
int v;
|
||||
/* vertex number, 1 <= v <= nv */
|
||||
CFGVLE *next;
|
||||
/* pointer to next vertex list element */
|
||||
};
|
||||
|
||||
struct CFGCLE
|
||||
{ /* clique list element */
|
||||
CFGVLE *vptr;
|
||||
/* initial pointer to the list of clique vertices */
|
||||
CFGCLE *next;
|
||||
/* pointer to next clique list element */
|
||||
};
|
||||
|
||||
#define cfg_create_graph _glp_cfg_create_graph
|
||||
CFG *cfg_create_graph(int n, int nv_max);
|
||||
/* create conflict graph */
|
||||
|
||||
#define cfg_add_clique _glp_cfg_add_clique
|
||||
void cfg_add_clique(CFG *G, int size, const int ind[]);
|
||||
/* add clique to conflict graph */
|
||||
|
||||
#define cfg_get_adjacent _glp_cfg_get_adjacent
|
||||
int cfg_get_adjacent(CFG *G, int v, int ind[]);
|
||||
/* get vertices adjacent to specified vertex */
|
||||
|
||||
#define cfg_expand_clique _glp_cfg_expand_clique
|
||||
int cfg_expand_clique(CFG *G, int c_len, int c_ind[]);
|
||||
/* expand specified clique to maximal clique */
|
||||
|
||||
#define cfg_check_clique _glp_cfg_check_clique
|
||||
void cfg_check_clique(CFG *G, int c_len, const int c_ind[]);
|
||||
/* check clique in conflict graph */
|
||||
|
||||
#define cfg_delete_graph _glp_cfg_delete_graph
|
||||
void cfg_delete_graph(CFG *G);
|
||||
/* delete conflict graph */
|
||||
|
||||
#define cfg_build_graph _glp_cfg_build_graph
|
||||
CFG *cfg_build_graph(void /* glp_prob */ *P);
|
||||
/* build conflict graph */
|
||||
|
||||
#define cfg_find_clique _glp_cfg_find_clique
|
||||
int cfg_find_clique(void /* glp_prob */ *P, CFG *G, int ind[],
|
||||
double *sum);
|
||||
/* find maximum weight clique in conflict graph */
|
||||
|
||||
#endif
|
||||
|
||||
/* eof */
|
||||
+701
@@ -0,0 +1,701 @@
|
||||
/* cfg1.c (conflict graph) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2012-2018 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "cfg.h"
|
||||
#include "env.h"
|
||||
#include "prob.h"
|
||||
#include "wclique.h"
|
||||
#include "wclique1.h"
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_build_graph - build conflict graph
|
||||
*
|
||||
* This routine builds the conflict graph. It analyzes the specified
|
||||
* problem object to discover original and implied packing inequalities
|
||||
* and adds corresponding cliques to the conflict graph.
|
||||
*
|
||||
* Packing inequality has the form:
|
||||
*
|
||||
* sum z[j] <= 1, (1)
|
||||
* j in J
|
||||
*
|
||||
* where z[j] = x[j] or z[j] = 1 - x[j], x[j] is an original binary
|
||||
* variable. Every packing inequality (1) is equivalent to a set of
|
||||
* edge inequalities:
|
||||
*
|
||||
* z[i] + z[j] <= 1 for all i, j in J, i != j, (2)
|
||||
*
|
||||
* and since every edge inequality (2) defines an edge in the conflict
|
||||
* graph, corresponding packing inequality (1) defines a clique.
|
||||
*
|
||||
* To discover packing inequalities the routine analyzes constraints
|
||||
* of the specified MIP. To simplify the analysis each constraint is
|
||||
* analyzed separately. The analysis is performed as follows.
|
||||
*
|
||||
* Let some original constraint be the following:
|
||||
*
|
||||
* L <= sum a[j] x[j] <= U. (3)
|
||||
*
|
||||
* To analyze it the routine analyzes two constraints of "not greater
|
||||
* than" type:
|
||||
*
|
||||
* sum (-a[j]) x[j] <= -L, (4)
|
||||
*
|
||||
* sum (+a[j]) x[j] <= +U, (5)
|
||||
*
|
||||
* which are relaxations of the original constraint (3). (If, however,
|
||||
* L = -oo, or U = +oo, corresponding constraint being redundant is not
|
||||
* analyzed.)
|
||||
*
|
||||
* Let a constraint of "not greater than" type be the following:
|
||||
*
|
||||
* sum a[j] x[j] + sum a[j] x[j] <= b, (6)
|
||||
* j in J j in J'
|
||||
*
|
||||
* where J is a subset of binary variables, J' is a subset of other
|
||||
* (continues and non-binary integer) variables. The constraint (6) is
|
||||
* is relaxed as follows, to eliminate non-binary variables:
|
||||
*
|
||||
* sum a[j] x[j] <= b - sum a[j] x[j] <= b', (7)
|
||||
* j in J j in J'
|
||||
*
|
||||
* b' = sup(b - sum a[j] x[j]) =
|
||||
* j in J'
|
||||
*
|
||||
* = b - inf(sum a[j] x[j]) =
|
||||
*
|
||||
* = b - sum inf(a[j] x[j]) = (8)
|
||||
*
|
||||
* = b - sum a[j] inf(x[j]) - sum a[j] sup(x[j]) =
|
||||
* a[j]>0 a[j]<0
|
||||
*
|
||||
* = b - sum a[j] l[j] - sum a[j] u[j],
|
||||
* a[j]>0 a[j]<0
|
||||
*
|
||||
* where l[j] and u[j] are, resp., lower and upper bounds of x[j].
|
||||
*
|
||||
* Then the routine transforms the relaxed constraint containing only
|
||||
* binary variables:
|
||||
*
|
||||
* sum a[j] x[j] <= b (9)
|
||||
*
|
||||
* to an equivalent 0-1 knapsack constraint as follows:
|
||||
*
|
||||
* sum a[j] x[j] + sum a[j] x[j] <= b ==>
|
||||
* a[j]>0 a[j]<0
|
||||
*
|
||||
* sum a[j] x[j] + sum a[j] (1 - x[j]) <= b ==>
|
||||
* a[j]>0 a[j]<0 (10)
|
||||
*
|
||||
* sum (+a[j]) x[j] + sum (-a[j]) x[j] <= b + sum (-a[j]) ==>
|
||||
* a[j]>0 a[j]<0 a[j]<0
|
||||
*
|
||||
* sum a'[j] z[j] <= b',
|
||||
*
|
||||
* where a'[j] = |a[j]| > 0, and
|
||||
*
|
||||
* ( x[j] if a[j] > 0
|
||||
* z[j] = <
|
||||
* ( 1 - x[j] if a[j] < 0
|
||||
*
|
||||
* is a binary variable, which is either original binary variable x[j]
|
||||
* or its complement.
|
||||
*
|
||||
* Finally, the routine analyzes the resultant 0-1 knapsack inequality:
|
||||
*
|
||||
* sum a[j] z[j] <= b, (11)
|
||||
* j in J
|
||||
*
|
||||
* where all a[j] are positive, to discover clique inequalities (1),
|
||||
* which are valid for (11) and therefore valid for (3). (It is assumed
|
||||
* that the original MIP has been preprocessed, so it is not checked,
|
||||
* for example, that b > 0 or that a[j] <= b.)
|
||||
*
|
||||
* In principle, to discover any edge inequalities valid for (11) it
|
||||
* is sufficient to check whether a[i] + a[j] > b for all i, j in J,
|
||||
* i < j. However, this way requires O(|J|^2) checks, so the routine
|
||||
* analyses (11) in the following way, which is much more efficient in
|
||||
* many practical cases.
|
||||
*
|
||||
* 1. Let a[p] and a[q] be two minimal coefficients:
|
||||
*
|
||||
* a[p] = min a[j], (12)
|
||||
*
|
||||
* a[q] = min a[j], j != p, (13)
|
||||
*
|
||||
* such that
|
||||
*
|
||||
* a[p] + a[q] > b. (14)
|
||||
*
|
||||
* This means that a[i] + a[j] > b for any i, j in J, i != j, so
|
||||
*
|
||||
* z[i] + z[j] <= 1 (15)
|
||||
*
|
||||
* are valid for (11) for any i, j in J, i != j. This case means that
|
||||
* J define a clique in the conflict graph.
|
||||
*
|
||||
* 2. Otherwise, let a[p] and [q] be two maximal coefficients:
|
||||
*
|
||||
* a[p] = max a[j], (16)
|
||||
*
|
||||
* a[q] = max a[j], j != p, (17)
|
||||
*
|
||||
* such that
|
||||
*
|
||||
* a[p] + a[q] <= b. (18)
|
||||
*
|
||||
* This means that a[i] + a[j] <= b for any i, j in J, i != j, so in
|
||||
* this case no valid edge inequalities for (11) exist.
|
||||
*
|
||||
* 3. Otherwise, let all a[j] be ordered by descending their values:
|
||||
*
|
||||
* a[1] >= a[2] >= ... >= a[p-1] >= a[p] >= a[p+1] >= ... (19)
|
||||
*
|
||||
* where p is such that
|
||||
*
|
||||
* a[p-1] + a[p] > b, (20)
|
||||
*
|
||||
* a[p] + a[p+1] <= b. (21)
|
||||
*
|
||||
* (May note that due to the former two cases in this case we always
|
||||
* have 2 <= p <= |J|-1.)
|
||||
*
|
||||
* Since a[p] and a[p-1] are two minimal coefficients in the set
|
||||
* J' = {1, ..., p}, J' define a clique in the conflict graph for the
|
||||
* same reason as in the first case. Similarly, since a[p] and a[p+1]
|
||||
* are two maximal coefficients in the set J" = {p, ..., |J|}, no edge
|
||||
* inequalities exist for all i, j in J" for the same reason as in the
|
||||
* second case. Thus, to discover other edge inequalities (15) valid
|
||||
* for (11), the routine checks if a[i] + a[j] > b for all i in J',
|
||||
* j in J", i != j. */
|
||||
|
||||
#define is_binary(j) \
|
||||
(P->col[j]->kind == GLP_IV && P->col[j]->type == GLP_DB && \
|
||||
P->col[j]->lb == 0.0 && P->col[j]->ub == 1.0)
|
||||
/* check if x[j] is binary variable */
|
||||
|
||||
struct term { int ind; double val; };
|
||||
/* term a[j] * z[j] used to sort a[j]'s */
|
||||
|
||||
static int CDECL fcmp(const void *e1, const void *e2)
|
||||
{ /* auxiliary routine called from qsort */
|
||||
const struct term *t1 = e1, *t2 = e2;
|
||||
if (t1->val > t2->val)
|
||||
return -1;
|
||||
else if (t1->val < t2->val)
|
||||
return +1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void analyze_ineq(glp_prob *P, CFG *G, int len, int ind[],
|
||||
double val[], double rhs, struct term t[])
|
||||
{ /* analyze inequality constraint (6) */
|
||||
/* P is the original MIP
|
||||
* G is the conflict graph to be built
|
||||
* len is the number of terms in the constraint
|
||||
* ind[1], ..., ind[len] are indices of variables x[j]
|
||||
* val[1], ..., val[len] are constraint coefficients a[j]
|
||||
* rhs is the right-hand side b
|
||||
* t[1+len] is a working array */
|
||||
int j, k, kk, p, q, type, new_len;
|
||||
/* eliminate non-binary variables; see (7) and (8) */
|
||||
new_len = 0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ /* get index of variable x[j] */
|
||||
j = ind[k];
|
||||
if (is_binary(j))
|
||||
{ /* x[j] remains in relaxed constraint */
|
||||
new_len++;
|
||||
ind[new_len] = j;
|
||||
val[new_len] = val[k];
|
||||
}
|
||||
else if (val[k] > 0.0)
|
||||
{ /* eliminate non-binary x[j] in case a[j] > 0 */
|
||||
/* b := b - a[j] * l[j]; see (8) */
|
||||
type = P->col[j]->type;
|
||||
if (type == GLP_FR || type == GLP_UP)
|
||||
{ /* x[j] has no lower bound */
|
||||
goto done;
|
||||
}
|
||||
rhs -= val[k] * P->col[j]->lb;
|
||||
}
|
||||
else /* val[j] < 0.0 */
|
||||
{ /* eliminate non-binary x[j] in case a[j] < 0 */
|
||||
/* b := b - a[j] * u[j]; see (8) */
|
||||
type = P->col[j]->type;
|
||||
if (type == GLP_FR || type == GLP_LO)
|
||||
{ /* x[j] has no upper bound */
|
||||
goto done;
|
||||
}
|
||||
rhs -= val[k] * P->col[j]->ub;
|
||||
}
|
||||
}
|
||||
len = new_len;
|
||||
/* now we have the constraint (9) */
|
||||
if (len <= 1)
|
||||
{ /* at least two terms are needed */
|
||||
goto done;
|
||||
}
|
||||
/* make all constraint coefficients positive; see (10) */
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (val[k] < 0.0)
|
||||
{ /* a[j] < 0; substitute x[j] = 1 - x'[j], where x'[j] is
|
||||
* a complement binary variable */
|
||||
ind[k] = -ind[k];
|
||||
val[k] = -val[k];
|
||||
rhs += val[k];
|
||||
}
|
||||
}
|
||||
/* now we have 0-1 knapsack inequality (11) */
|
||||
/* increase the right-hand side a bit to avoid false checks due
|
||||
* to rounding errors */
|
||||
rhs += 0.001 * (1.0 + fabs(rhs));
|
||||
/*** first case ***/
|
||||
/* find two minimal coefficients a[p] and a[q] */
|
||||
p = 0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (p == 0 || val[p] > val[k])
|
||||
p = k;
|
||||
}
|
||||
q = 0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (k != p && (q == 0 || val[q] > val[k]))
|
||||
q = k;
|
||||
}
|
||||
xassert(p != 0 && q != 0 && p != q);
|
||||
/* check condition (14) */
|
||||
if (val[p] + val[q] > rhs)
|
||||
{ /* all z[j] define a clique in the conflict graph */
|
||||
cfg_add_clique(G, len, ind);
|
||||
goto done;
|
||||
}
|
||||
/*** second case ***/
|
||||
/* find two maximal coefficients a[p] and a[q] */
|
||||
p = 0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (p == 0 || val[p] < val[k])
|
||||
p = k;
|
||||
}
|
||||
q = 0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (k != p && (q == 0 || val[q] < val[k]))
|
||||
q = k;
|
||||
}
|
||||
xassert(p != 0 && q != 0 && p != q);
|
||||
/* check condition (18) */
|
||||
if (val[p] + val[q] <= rhs)
|
||||
{ /* no valid edge inequalities exist */
|
||||
goto done;
|
||||
}
|
||||
/*** third case ***/
|
||||
xassert(len >= 3);
|
||||
/* sort terms in descending order of coefficient values */
|
||||
for (k = 1; k <= len; k++)
|
||||
{ t[k].ind = ind[k];
|
||||
t[k].val = val[k];
|
||||
}
|
||||
qsort(&t[1], len, sizeof(struct term), fcmp);
|
||||
for (k = 1; k <= len; k++)
|
||||
{ ind[k] = t[k].ind;
|
||||
val[k] = t[k].val;
|
||||
}
|
||||
/* now a[1] >= a[2] >= ... >= a[len-1] >= a[len] */
|
||||
/* note that a[1] + a[2] > b and a[len-1] + a[len] <= b due two
|
||||
* the former two cases */
|
||||
xassert(val[1] + val[2] > rhs);
|
||||
xassert(val[len-1] + val[len] <= rhs);
|
||||
/* find p according to conditions (20) and (21) */
|
||||
for (p = 2; p < len; p++)
|
||||
{ if (val[p] + val[p+1] <= rhs)
|
||||
break;
|
||||
}
|
||||
xassert(p < len);
|
||||
/* z[1], ..., z[p] define a clique in the conflict graph */
|
||||
cfg_add_clique(G, p, ind);
|
||||
/* discover other edge inequalities */
|
||||
for (k = 1; k <= p; k++)
|
||||
{ for (kk = p; kk <= len; kk++)
|
||||
{ if (k != kk && val[k] + val[kk] > rhs)
|
||||
{ int iii[1+2];
|
||||
iii[1] = ind[k];
|
||||
iii[2] = ind[kk];
|
||||
cfg_add_clique(G, 2, iii);
|
||||
}
|
||||
}
|
||||
}
|
||||
done: return;
|
||||
}
|
||||
|
||||
CFG *cfg_build_graph(void *P_)
|
||||
{ glp_prob *P = P_;
|
||||
int m = P->m;
|
||||
int n = P->n;
|
||||
CFG *G;
|
||||
int i, k, type, len, *ind;
|
||||
double *val;
|
||||
struct term *t;
|
||||
/* create the conflict graph (number of its vertices cannot be
|
||||
* greater than double number of binary variables) */
|
||||
G = cfg_create_graph(n, 2 * glp_get_num_bin(P));
|
||||
/* allocate working arrays */
|
||||
ind = talloc(1+n, int);
|
||||
val = talloc(1+n, double);
|
||||
t = talloc(1+n, struct term);
|
||||
/* analyze constraints to discover edge inequalities */
|
||||
for (i = 1; i <= m; i++)
|
||||
{ type = P->row[i]->type;
|
||||
if (type == GLP_LO || type == GLP_DB || type == GLP_FX)
|
||||
{ /* i-th row has lower bound */
|
||||
/* analyze inequality sum (-a[j]) * x[j] <= -lb */
|
||||
len = glp_get_mat_row(P, i, ind, val);
|
||||
for (k = 1; k <= len; k++)
|
||||
val[k] = -val[k];
|
||||
analyze_ineq(P, G, len, ind, val, -P->row[i]->lb, t);
|
||||
}
|
||||
if (type == GLP_UP || type == GLP_DB || type == GLP_FX)
|
||||
{ /* i-th row has upper bound */
|
||||
/* analyze inequality sum (+a[j]) * x[j] <= +ub */
|
||||
len = glp_get_mat_row(P, i, ind, val);
|
||||
analyze_ineq(P, G, len, ind, val, +P->row[i]->ub, t);
|
||||
}
|
||||
}
|
||||
/* free working arrays */
|
||||
tfree(ind);
|
||||
tfree(val);
|
||||
tfree(t);
|
||||
return G;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* cfg_find_clique - find maximum weight clique in conflict graph
|
||||
*
|
||||
* This routine finds a maximum weight clique in the conflict graph
|
||||
* G = (V, E), where the weight of vertex v in V is the value of
|
||||
* corresponding binary variable z (which is either an original binary
|
||||
* variable or its complement) in the optimal solution to LP relaxation
|
||||
* provided in the problem object. The goal is to find a clique in G,
|
||||
* whose weight is greater than 1, in which case corresponding packing
|
||||
* inequality is violated at the optimal point.
|
||||
*
|
||||
* On exit the routine stores vertex indices of the conflict graph
|
||||
* included in the clique found to locations ind[1], ..., ind[len], and
|
||||
* returns len, which is the clique size. The clique weight is stored
|
||||
* in location pointed to by the parameter sum. If no clique has been
|
||||
* found, the routine returns 0.
|
||||
*
|
||||
* Since the conflict graph may have a big number of vertices and be
|
||||
* quite dense, the routine uses an induced subgraph G' = (V', E'),
|
||||
* which is constructed as follows:
|
||||
*
|
||||
* 1. If the weight of some vertex v in V is zero (close to zero), it
|
||||
* is not included in V'. Obviously, including in a clique
|
||||
* zero-weight vertices does not change its weight, so if in G there
|
||||
* exist a clique of a non-zero weight, in G' exists a clique of the
|
||||
* same weight. This point is extremely important, because dropping
|
||||
* out zero-weight vertices can be done without retrieving lists of
|
||||
* adjacent vertices whose size may be very large.
|
||||
*
|
||||
* 2. Cumulative weight of vertex v in V is the sum of the weight of v
|
||||
* and weights of all vertices in V adjacent to v. Obviously, if
|
||||
* a clique includes a vertex v, the clique weight cannot be greater
|
||||
* than the cumulative weight of v. Since we are interested only in
|
||||
* cliques whose weight is greater than 1, vertices of V, whose
|
||||
* cumulative weight is not greater than 1, are not included in V'.
|
||||
*
|
||||
* May note that in many practical cases the size of the induced
|
||||
* subgraph G' is much less than the size of the original conflict
|
||||
* graph G due to many binary variables, whose optimal values are zero
|
||||
* or close to zero. For example, it may happen that |V| = 100,000 and
|
||||
* |E| = 1e9 while |V'| = 50 and |E'| = 1000. */
|
||||
|
||||
struct csa
|
||||
{ /* common storage area */
|
||||
glp_prob *P;
|
||||
/* original MIP */
|
||||
CFG *G;
|
||||
/* original conflict graph G = (V, E), |V| = nv */
|
||||
int *ind; /* int ind[1+nv]; */
|
||||
/* working array */
|
||||
/*--------------------------------------------------------------*/
|
||||
/* induced subgraph G' = (V', E') of original conflict graph */
|
||||
int nn;
|
||||
/* number of vertices in V' */
|
||||
int *vtoi; /* int vtoi[1+nv]; */
|
||||
/* vtoi[v] = i, 1 <= v <= nv, means that vertex v in V is vertex
|
||||
* i in V'; vtoi[v] = 0 means that vertex v is not included in
|
||||
* the subgraph */
|
||||
int *itov; /* int itov[1+nv]; */
|
||||
/* itov[i] = v, 1 <= i <= nn, means that vertex i in V' is vertex
|
||||
* v in V */
|
||||
double *wgt; /* double wgt[1+nv]; */
|
||||
/* wgt[i], 1 <= i <= nn, is a weight of vertex i in V', which is
|
||||
* the value of corresponding binary variable in optimal solution
|
||||
* to LP relaxation */
|
||||
};
|
||||
|
||||
static void build_subgraph(struct csa *csa)
|
||||
{ /* build induced subgraph */
|
||||
glp_prob *P = csa->P;
|
||||
int n = P->n;
|
||||
CFG *G = csa->G;
|
||||
int *ind = csa->ind;
|
||||
int *pos = G->pos;
|
||||
int *neg = G->neg;
|
||||
int nv = G->nv;
|
||||
int *ref = G->ref;
|
||||
int *vtoi = csa->vtoi;
|
||||
int *itov = csa->itov;
|
||||
double *wgt = csa->wgt;
|
||||
int j, k, v, w, nn, len;
|
||||
double z, sum;
|
||||
/* initially induced subgraph is empty */
|
||||
nn = 0;
|
||||
/* walk thru vertices of original conflict graph */
|
||||
for (v = 1; v <= nv; v++)
|
||||
{ /* determine value of binary variable z[j] that corresponds to
|
||||
* vertex v */
|
||||
j = ref[v];
|
||||
xassert(1 <= j && j <= n);
|
||||
if (pos[j] == v)
|
||||
{ /* z[j] = x[j], where x[j] is original variable */
|
||||
z = P->col[j]->prim;
|
||||
}
|
||||
else if (neg[j] == v)
|
||||
{ /* z[j] = 1 - x[j], where x[j] is original variable */
|
||||
z = 1.0 - P->col[j]->prim;
|
||||
}
|
||||
else
|
||||
xassert(v != v);
|
||||
/* if z[j] is close to zero, do not include v in the induced
|
||||
* subgraph */
|
||||
if (z < 0.001)
|
||||
{ vtoi[v] = 0;
|
||||
continue;
|
||||
}
|
||||
/* calculate cumulative weight of vertex v */
|
||||
sum = z;
|
||||
/* walk thru all vertices adjacent to v */
|
||||
len = cfg_get_adjacent(G, v, ind);
|
||||
for (k = 1; k <= len; k++)
|
||||
{ /* there is an edge (v,w) in the conflict graph */
|
||||
w = ind[k];
|
||||
xassert(w != v);
|
||||
/* add value of z[j] that corresponds to vertex w */
|
||||
j = ref[w];
|
||||
xassert(1 <= j && j <= n);
|
||||
if (pos[j] == w)
|
||||
sum += P->col[j]->prim;
|
||||
else if (neg[j] == w)
|
||||
sum += 1.0 - P->col[j]->prim;
|
||||
else
|
||||
xassert(w != w);
|
||||
}
|
||||
/* cumulative weight of vertex v is an upper bound of weight
|
||||
* of any clique containing v; so if it not greater than 1, do
|
||||
* not include v in the induced subgraph */
|
||||
if (sum < 1.010)
|
||||
{ vtoi[v] = 0;
|
||||
continue;
|
||||
}
|
||||
/* include vertex v in the induced subgraph */
|
||||
nn++;
|
||||
vtoi[v] = nn;
|
||||
itov[nn] = v;
|
||||
wgt[nn] = z;
|
||||
}
|
||||
/* induced subgraph has been built */
|
||||
csa->nn = nn;
|
||||
return;
|
||||
}
|
||||
|
||||
static int sub_adjacent(struct csa *csa, int i, int adj[])
|
||||
{ /* retrieve vertices of induced subgraph adjacent to specified
|
||||
* vertex */
|
||||
CFG *G = csa->G;
|
||||
int nv = G->nv;
|
||||
int *ind = csa->ind;
|
||||
int nn = csa->nn;
|
||||
int *vtoi = csa->vtoi;
|
||||
int *itov = csa->itov;
|
||||
int j, k, v, w, len, len1;
|
||||
/* determine original vertex v corresponding to vertex i */
|
||||
xassert(1 <= i && i <= nn);
|
||||
v = itov[i];
|
||||
/* retrieve vertices adjacent to vertex v in original graph */
|
||||
len1 = cfg_get_adjacent(G, v, ind);
|
||||
/* keep only adjacent vertices which are in induced subgraph and
|
||||
* change their numbers appropriately */
|
||||
len = 0;
|
||||
for (k = 1; k <= len1; k++)
|
||||
{ /* there exists edge (v, w) in original graph */
|
||||
w = ind[k];
|
||||
xassert(1 <= w && w <= nv && w != v);
|
||||
j = vtoi[w];
|
||||
if (j != 0)
|
||||
{ /* vertex w is vertex j in induced subgraph */
|
||||
xassert(1 <= j && j <= nn && j != i);
|
||||
adj[++len] = j;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int find_clique(struct csa *csa, int c_ind[])
|
||||
{ /* find maximum weight clique in induced subgraph with exact
|
||||
* Ostergard's algorithm */
|
||||
int nn = csa->nn;
|
||||
double *wgt = csa->wgt;
|
||||
int i, j, k, p, q, t, ne, nb, len, *iwt, *ind;
|
||||
unsigned char *a;
|
||||
xassert(nn >= 2);
|
||||
/* allocate working array */
|
||||
ind = talloc(1+nn, int);
|
||||
/* calculate the number of elements in lower triangle (without
|
||||
* diagonal) of adjacency matrix of induced subgraph */
|
||||
ne = (nn * (nn - 1)) / 2;
|
||||
/* calculate the number of bytes needed to store lower triangle
|
||||
* of adjacency matrix */
|
||||
nb = (ne + (CHAR_BIT - 1)) / CHAR_BIT;
|
||||
/* allocate lower triangle of adjacency matrix */
|
||||
a = talloc(nb, unsigned char);
|
||||
/* fill lower triangle of adjacency matrix */
|
||||
memset(a, 0, nb);
|
||||
for (p = 1; p <= nn; p++)
|
||||
{ /* retrieve vertices adjacent to vertex p */
|
||||
len = sub_adjacent(csa, p, ind);
|
||||
for (k = 1; k <= len; k++)
|
||||
{ /* there exists edge (p, q) in induced subgraph */
|
||||
q = ind[k];
|
||||
xassert(1 <= q && q <= nn && q != p);
|
||||
/* determine row and column indices of this edge in lower
|
||||
* triangle of adjacency matrix */
|
||||
if (p > q)
|
||||
i = p, j = q;
|
||||
else /* p < q */
|
||||
i = q, j = p;
|
||||
/* set bit a[i,j] to 1, i > j */
|
||||
t = ((i - 1) * (i - 2)) / 2 + (j - 1);
|
||||
a[t / CHAR_BIT] |=
|
||||
(unsigned char)(1 << ((CHAR_BIT - 1) - t % CHAR_BIT));
|
||||
}
|
||||
}
|
||||
/* scale vertex weights by 1000 and convert them to integers as
|
||||
* required by Ostergard's algorithm */
|
||||
iwt = ind;
|
||||
for (i = 1; i <= nn; i++)
|
||||
{ /* it is assumed that 0 <= wgt[i] <= 1 */
|
||||
t = (int)(1000.0 * wgt[i] + 0.5);
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
else if (t > 1000)
|
||||
t = 1000;
|
||||
iwt[i] = t;
|
||||
}
|
||||
/* find maximum weight clique */
|
||||
len = wclique(nn, iwt, a, c_ind);
|
||||
/* free working arrays */
|
||||
tfree(ind);
|
||||
tfree(a);
|
||||
/* return clique size to calling routine */
|
||||
return len;
|
||||
}
|
||||
|
||||
static int func(void *info, int i, int ind[])
|
||||
{ /* auxiliary routine used by routine find_clique1 */
|
||||
struct csa *csa = info;
|
||||
xassert(1 <= i && i <= csa->nn);
|
||||
return sub_adjacent(csa, i, ind);
|
||||
}
|
||||
|
||||
static int find_clique1(struct csa *csa, int c_ind[])
|
||||
{ /* find maximum weight clique in induced subgraph with greedy
|
||||
* heuristic */
|
||||
int nn = csa->nn;
|
||||
double *wgt = csa->wgt;
|
||||
int len;
|
||||
xassert(nn >= 2);
|
||||
len = wclique1(nn, wgt, func, csa, c_ind);
|
||||
/* return clique size to calling routine */
|
||||
return len;
|
||||
}
|
||||
|
||||
int cfg_find_clique(void *P, CFG *G, int ind[], double *sum_)
|
||||
{ int nv = G->nv;
|
||||
struct csa csa;
|
||||
int i, k, len;
|
||||
double sum;
|
||||
/* initialize common storage area */
|
||||
csa.P = P;
|
||||
csa.G = G;
|
||||
csa.ind = talloc(1+nv, int);
|
||||
csa.nn = -1;
|
||||
csa.vtoi = talloc(1+nv, int);
|
||||
csa.itov = talloc(1+nv, int);
|
||||
csa.wgt = talloc(1+nv, double);
|
||||
/* build induced subgraph */
|
||||
build_subgraph(&csa);
|
||||
#ifdef GLP_DEBUG
|
||||
xprintf("nn = %d\n", csa.nn);
|
||||
#endif
|
||||
/* if subgraph has less than two vertices, do nothing */
|
||||
if (csa.nn < 2)
|
||||
{ len = 0;
|
||||
sum = 0.0;
|
||||
goto skip;
|
||||
}
|
||||
/* find maximum weight clique in induced subgraph */
|
||||
#if 1 /* FIXME */
|
||||
if (csa.nn <= 50)
|
||||
#endif
|
||||
{ /* induced subgraph is small; use exact algorithm */
|
||||
len = find_clique(&csa, ind);
|
||||
}
|
||||
else
|
||||
{ /* induced subgraph is large; use greedy heuristic */
|
||||
len = find_clique1(&csa, ind);
|
||||
}
|
||||
/* do not report clique, if it has less than two vertices */
|
||||
if (len < 2)
|
||||
{ len = 0;
|
||||
sum = 0.0;
|
||||
goto skip;
|
||||
}
|
||||
/* convert indices of clique vertices from induced subgraph to
|
||||
* original conflict graph and compute clique weight */
|
||||
sum = 0.0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ i = ind[k];
|
||||
xassert(1 <= i && i <= csa.nn);
|
||||
sum += csa.wgt[i];
|
||||
ind[k] = csa.itov[i];
|
||||
}
|
||||
skip: /* free working arrays */
|
||||
tfree(csa.ind);
|
||||
tfree(csa.vtoi);
|
||||
tfree(csa.itov);
|
||||
tfree(csa.wgt);
|
||||
/* return to calling routine */
|
||||
*sum_ = sum;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
@@ -0,0 +1,89 @@
|
||||
/* cfg2.c (conflict graph) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2015-2016 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "cfg.h"
|
||||
#include "env.h"
|
||||
#include "prob.h"
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* glp_cfg_init - create and initialize conflict graph
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* glp_cfg *glp_cfg_init(glp_prob *P);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* This routine creates and initializes the conflict graph for the
|
||||
* specified problem object.
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* The routine returns a pointer to the conflict graph descriptor.
|
||||
* However, if the conflict graph is empty (no conflicts have been
|
||||
* found), the routine returns NULL. */
|
||||
|
||||
glp_cfg *glp_cfg_init(glp_prob *P)
|
||||
{ glp_cfg *G;
|
||||
int j, n1, n2;
|
||||
xprintf("Constructing conflict graph...\n");
|
||||
G = cfg_build_graph(P);
|
||||
n1 = n2 = 0;
|
||||
for (j = 1; j <= P->n; j++)
|
||||
{ if (G->pos[j])
|
||||
n1 ++;
|
||||
if (G->neg[j])
|
||||
n2++;
|
||||
}
|
||||
if (n1 == 0 && n2 == 0)
|
||||
{ xprintf("No conflicts found\n");
|
||||
cfg_delete_graph(G);
|
||||
G = NULL;
|
||||
}
|
||||
else
|
||||
xprintf("Conflict graph has %d + %d = %d vertices\n",
|
||||
n1, n2, G->nv);
|
||||
return G;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* glp_cfg_free - delete conflict graph descriptor
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* void glp_cfg_free(glp_cfg *G);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* This routine deletes the conflict graph descriptor and frees all the
|
||||
* memory allocated to it. */
|
||||
|
||||
void glp_cfg_free(glp_cfg *G)
|
||||
{ xassert(G != NULL);
|
||||
cfg_delete_graph(G);
|
||||
return;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/* clqcut.c (clique cut generator) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2008-2016 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "cfg.h"
|
||||
#include "env.h"
|
||||
#include "prob.h"
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* glp_clq_cut - generate clique cut from conflict graph
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* int glp_clq_cut(glp_prob *P, glp_cfg *G, int ind[], double val[]);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* This routine attempts to generate a clique cut.
|
||||
*
|
||||
* The cut generated by the routine is the following inequality:
|
||||
*
|
||||
* sum a[j] * x[j] <= b,
|
||||
*
|
||||
* which is expected to be violated at the current basic solution.
|
||||
*
|
||||
* If the cut has been successfully generated, the routine stores its
|
||||
* non-zero coefficients a[j] and corresponding column indices j in the
|
||||
* array locations val[1], ..., val[len] and ind[1], ..., ind[len],
|
||||
* where 1 <= len <= n is the number of non-zero coefficients. The
|
||||
* right-hand side value b is stored in val[0], and ind[0] is set to 0.
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* If the cut has been successfully generated, the routine returns
|
||||
* len, the number of non-zero coefficients in the cut, 1 <= len <= n.
|
||||
* Otherwise, the routine returns a non-positive value. */
|
||||
|
||||
int glp_clq_cut(glp_prob *P, glp_cfg *G, int ind[], double val[])
|
||||
{ int n = P->n;
|
||||
int *pos = G->pos;
|
||||
int *neg = G->neg;
|
||||
int nv = G->nv;
|
||||
int *ref = G->ref;
|
||||
int j, k, v, len;
|
||||
double rhs, sum;
|
||||
xassert(G->n == n);
|
||||
/* find maximum weight clique in conflict graph */
|
||||
len = cfg_find_clique(P, G, ind, &sum);
|
||||
#ifdef GLP_DEBUG
|
||||
xprintf("len = %d; sum = %g\n", len, sum);
|
||||
cfg_check_clique(G, len, ind);
|
||||
#endif
|
||||
/* check if clique inequality is violated */
|
||||
if (sum < 1.07)
|
||||
return 0;
|
||||
/* expand clique to maximal one */
|
||||
len = cfg_expand_clique(G, len, ind);
|
||||
#ifdef GLP_DEBUG
|
||||
xprintf("maximal clique size = %d\n", len);
|
||||
cfg_check_clique(G, len, ind);
|
||||
#endif
|
||||
/* construct clique cut (fixed binary variables are removed, so
|
||||
this cut is only locally valid) */
|
||||
rhs = 1.0;
|
||||
for (j = 1; j <= n; j++)
|
||||
val[j] = 0.0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ /* v is clique vertex */
|
||||
v = ind[k];
|
||||
xassert(1 <= v && v <= nv);
|
||||
/* j is number of corresponding binary variable */
|
||||
j = ref[v];
|
||||
xassert(1 <= j && j <= n);
|
||||
if (pos[j] == v)
|
||||
{ /* v corresponds to x[j] */
|
||||
if (P->col[j]->type == GLP_FX)
|
||||
{ /* x[j] is fixed */
|
||||
rhs -= P->col[j]->prim;
|
||||
}
|
||||
else
|
||||
{ /* x[j] is not fixed */
|
||||
val[j] += 1.0;
|
||||
}
|
||||
}
|
||||
else if (neg[j] == v)
|
||||
{ /* v corresponds to (1 - x[j]) */
|
||||
if (P->col[j]->type == GLP_FX)
|
||||
{ /* x[j] is fixed */
|
||||
rhs -= (1.0 - P->col[j]->prim);
|
||||
}
|
||||
else
|
||||
{ /* x[j] is not fixed */
|
||||
val[j] -= 1.0;
|
||||
rhs -= 1.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
xassert(v != v);
|
||||
}
|
||||
/* convert cut inequality to sparse format */
|
||||
len = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ if (val[j] != 0.0)
|
||||
{ len++;
|
||||
ind[len] = j;
|
||||
val[len] = val[j];
|
||||
}
|
||||
}
|
||||
ind[0] = 0, val[0] = rhs;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
+883
@@ -0,0 +1,883 @@
|
||||
/* covgen.c */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2017-2018 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "fvs.h"
|
||||
#include "ks.h"
|
||||
#include "prob.h"
|
||||
|
||||
struct glp_cov
|
||||
{ /* cover cut generator working area */
|
||||
int n;
|
||||
/* number of columns (variables) */
|
||||
glp_prob *set;
|
||||
/* set of globally valid 0-1 knapsack inequalities chosen from
|
||||
* the root problem; each inequality is either original row or
|
||||
* its relaxation (surrogate 0-1 knapsack) which is constructed
|
||||
* by substitution of lower/upper single/variable bounds for
|
||||
* continuous and general integer (non-binary) variables */
|
||||
};
|
||||
|
||||
struct bnd
|
||||
{ /* simple or variable bound */
|
||||
/* if z = 0, it is a simple bound x >= or <= b; if b = -DBL_MAX
|
||||
* (b = +DBL_MAX), x has no lower (upper) bound; otherwise, if
|
||||
* z != 0, it is a variable bound x >= or <= a * z + b */
|
||||
int z;
|
||||
/* number of binary variable or 0 */
|
||||
double a, b;
|
||||
/* bound parameters */
|
||||
};
|
||||
|
||||
struct csa
|
||||
{ /* common storage area */
|
||||
glp_prob *P;
|
||||
/* original (root) MIP */
|
||||
struct bnd *l; /* struct bnd l[1+P->n]; */
|
||||
/* lower simple/variable bounds of variables */
|
||||
struct bnd *u; /* struct bnd u[1+P->n]; */
|
||||
/* upper simple/variable bounds of variables */
|
||||
glp_prob *set;
|
||||
/* see struct glp_cov above */
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* init_bounds - initialize bounds of variables with simple bounds
|
||||
*
|
||||
* This routine initializes lower and upper bounds of all variables
|
||||
* with simple bounds specified in the original mip. */
|
||||
|
||||
static void init_bounds(struct csa *csa)
|
||||
{ glp_prob *P = csa->P;
|
||||
struct bnd *l = csa->l, *u = csa->u;
|
||||
int j;
|
||||
for (j = 1; j <= P->n; j++)
|
||||
{ l[j].z = u[j].z = 0;
|
||||
l[j].a = u[j].a = 0;
|
||||
l[j].b = glp_get_col_lb(P, j);
|
||||
u[j].b = glp_get_col_ub(P, j);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* check_vb - check variable bound
|
||||
*
|
||||
* This routine checks if the specified i-th row has the form
|
||||
*
|
||||
* a1 * x + a2 * z >= or <= rhs, (1)
|
||||
*
|
||||
* where x is a non-fixed continuous or general integer variable, and
|
||||
* z is a binary variable. If it is, the routine converts the row to
|
||||
* the following variable lower/upper bound (VLB/VUB) of x:
|
||||
*
|
||||
* x >= or <= a * z + b, (2)
|
||||
*
|
||||
* where a = - a2 / a1, b = rhs / a1. Note that the inequality type is
|
||||
* changed to opposite one when a1 < 0.
|
||||
*
|
||||
* If the row is identified as a variable bound, the routine returns
|
||||
* GLP_LO for VLB or GLP_UP for VUB and provides the reference numbers
|
||||
* of variables x and z and values of a and b. Otherwise, the routine
|
||||
* returns zero. */
|
||||
|
||||
static int check_vb(struct csa *csa, int i, int *x, int *z, double *a,
|
||||
double *b)
|
||||
{ glp_prob *P = csa->P;
|
||||
GLPROW *row;
|
||||
GLPAIJ *a1, *a2;
|
||||
int type;
|
||||
double rhs;
|
||||
xassert(1 <= i && i <= P->m);
|
||||
row = P->row[i];
|
||||
/* check row type */
|
||||
switch (row->type)
|
||||
{ case GLP_LO:
|
||||
case GLP_UP:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
/* take first term of the row */
|
||||
a1 = row->ptr;
|
||||
if (a1 == NULL)
|
||||
return 0;
|
||||
/* take second term of the row */
|
||||
a2 = a1->r_next;
|
||||
if (a2 == NULL)
|
||||
return 0;
|
||||
/* there should be exactly two terms in the row */
|
||||
if (a2->r_next != NULL)
|
||||
return 0;
|
||||
/* if first term is a binary variable, swap the terms */
|
||||
if (glp_get_col_kind(P, a1->col->j) == GLP_BV)
|
||||
{ GLPAIJ *a;
|
||||
a = a1, a1 = a2, a2 = a;
|
||||
}
|
||||
/* now first term should be a non-fixed continuous or general
|
||||
* integer variable */
|
||||
if (a1->col->type == GLP_FX)
|
||||
return 0;
|
||||
if (glp_get_col_kind(P, a1->col->j) == GLP_BV)
|
||||
return 0;
|
||||
/* and second term should be a binary variable */
|
||||
if (glp_get_col_kind(P, a2->col->j) != GLP_BV)
|
||||
return 0;
|
||||
/* VLB/VUB row has been identified */
|
||||
switch (row->type)
|
||||
{ case GLP_LO:
|
||||
type = a1->val > 0 ? GLP_LO : GLP_UP;
|
||||
rhs = row->lb;
|
||||
break;
|
||||
case GLP_UP:
|
||||
type = a1->val > 0 ? GLP_UP : GLP_LO;
|
||||
rhs = row->ub;
|
||||
break;
|
||||
default:
|
||||
xassert(type != type);
|
||||
}
|
||||
*x = a1->col->j;
|
||||
*z = a2->col->j;
|
||||
*a = - a2->val / a1->val;
|
||||
*b = rhs / a1->val;
|
||||
return type;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* set_vb - set variable bound
|
||||
*
|
||||
* This routine sets lower or upper variable bound specified as
|
||||
*
|
||||
* x >= a * z + b (type = GLP_LO)
|
||||
*
|
||||
* x <= a * z + b (type = GLP_UP) */
|
||||
|
||||
static void set_vb(struct csa *csa, int type, int x, int z, double a,
|
||||
double b)
|
||||
{ glp_prob *P = csa->P;
|
||||
struct bnd *l = csa->l, *u = csa->u;
|
||||
xassert(glp_get_col_type(P, x) != GLP_FX);
|
||||
xassert(glp_get_col_kind(P, x) != GLP_BV);
|
||||
xassert(glp_get_col_kind(P, z) == GLP_BV);
|
||||
xassert(a != 0);
|
||||
switch (type)
|
||||
{ case GLP_LO:
|
||||
/* FIXME: check existing simple lower bound? */
|
||||
l[x].z = z, l[x].a = a, l[x].b = b;
|
||||
break;
|
||||
case GLP_UP:
|
||||
/* FIXME: check existing simple upper bound? */
|
||||
u[x].z = z, u[x].a = a, u[x].b = b;
|
||||
break;
|
||||
default:
|
||||
xassert(type != type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* obtain_vbs - obtain and set variable bounds
|
||||
*
|
||||
* This routine walks thru all rows of the original mip, identifies
|
||||
* rows specifying variable lower/upper bounds, and sets these bounds
|
||||
* for corresponding (non-binary) variables. */
|
||||
|
||||
static void obtain_vbs(struct csa *csa)
|
||||
{ glp_prob *P = csa->P;
|
||||
int i, x, z, type, save;
|
||||
double a, b;
|
||||
for (i = 1; i <= P->m; i++)
|
||||
{ switch (P->row[i]->type)
|
||||
{ case GLP_FR:
|
||||
break;
|
||||
case GLP_LO:
|
||||
case GLP_UP:
|
||||
type = check_vb(csa, i, &x, &z, &a, &b);
|
||||
if (type)
|
||||
set_vb(csa, type, x, z, a, b);
|
||||
break;
|
||||
case GLP_DB:
|
||||
case GLP_FX:
|
||||
/* double-side inequality l <= ... <= u and equality
|
||||
* ... = l = u are considered as two single inequalities
|
||||
* ... >= l and ... <= u */
|
||||
save = P->row[i]->type;
|
||||
P->row[i]->type = GLP_LO;
|
||||
type = check_vb(csa, i, &x, &z, &a, &b);
|
||||
if (type)
|
||||
set_vb(csa, type, x, z, a, b);
|
||||
P->row[i]->type = GLP_UP;
|
||||
type = check_vb(csa, i, &x, &z, &a, &b);
|
||||
if (type)
|
||||
set_vb(csa, type, x, z, a, b);
|
||||
P->row[i]->type = save;
|
||||
break;
|
||||
default:
|
||||
xassert(P != P);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* add_term - add term to sparse vector
|
||||
*
|
||||
* This routine computes the following linear combination:
|
||||
*
|
||||
* v := v + a * e[j],
|
||||
*
|
||||
* where v is a sparse vector in full storage format, a is a non-zero
|
||||
* scalar, e[j] is j-th column of unity matrix. */
|
||||
|
||||
static void add_term(FVS *v, int j, double a)
|
||||
{ xassert(1 <= j && j <= v->n);
|
||||
xassert(a != 0);
|
||||
if (v->vec[j] == 0)
|
||||
{ /* create j-th component */
|
||||
v->nnz++;
|
||||
xassert(v->nnz <= v->n);
|
||||
v->ind[v->nnz] = j;
|
||||
}
|
||||
/* perform addition */
|
||||
v->vec[j] += a;
|
||||
if (fabs(v->vec[j]) < 1e-9 * (1 + fabs(a)))
|
||||
{ /* remove j-th component */
|
||||
v->vec[j] = DBL_MIN;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* build_ks - build "0-1 knapsack" inequality
|
||||
*
|
||||
* Given an inequality of "not greater" type:
|
||||
*
|
||||
* sum{j in 1..n} a[j]*x[j] <= b, (1)
|
||||
*
|
||||
* this routine attempts to transform it to equivalent or relaxed "0-1
|
||||
* knapsack" inequality that contains only binary variables.
|
||||
*
|
||||
* If x[j] is a binary variable, the term a[j]*x[j] is not changed.
|
||||
* Otherwise, if x[j] is a continuous or integer non-binary variable,
|
||||
* it is replaced by its lower (if a[j] > 0) or upper (if a[j] < 0)
|
||||
* single or variable bound. In the latter case, if x[j] is a non-fixed
|
||||
* variable, this results in a relaxation of original inequality known
|
||||
* as "surrogate knapsack". Thus, if the specified inequality is valid
|
||||
* for the original mip, the resulting inequality is also valid.
|
||||
*
|
||||
* Note that in both source and resulting inequalities coefficients
|
||||
* a[j] can have any sign.
|
||||
*
|
||||
* On entry to the routine the source inequality is specified by the
|
||||
* parameters n, ind (contains original numbers of x[j]), a, and b. The
|
||||
* parameter v is a working sparse vector whose components are assumed
|
||||
* to be zero.
|
||||
*
|
||||
* On exit the routine stores the resulting "0-1 knapsack" inequality
|
||||
* in the parameters ind, a, and b, and returns n which is the number
|
||||
* of terms in the resulting inequality. Zero content of the vector v
|
||||
* is restored before exit.
|
||||
*
|
||||
* If the resulting inequality cannot be constructed due to missing
|
||||
* lower/upper bounds of some variable, the routine returns a negative
|
||||
* value. */
|
||||
|
||||
static int build_ks(struct csa *csa, int n, int ind[], double a[],
|
||||
double *b, FVS *v)
|
||||
{ glp_prob *P = csa->P;
|
||||
struct bnd *l = csa->l, *u = csa->u;
|
||||
int j, k;
|
||||
/* check that v = 0 */
|
||||
#ifdef GLP_DEBUG
|
||||
fvs_check_vec(v);
|
||||
#endif
|
||||
xassert(v->nnz == 0);
|
||||
/* walk thru terms of original inequality */
|
||||
for (j = 1; j <= n; j++)
|
||||
{ /* process term a[j]*x[j] */
|
||||
k = ind[j]; /* original number of x[j] in mip */
|
||||
if (glp_get_col_kind(P, k) == GLP_BV)
|
||||
{ /* x[j] is a binary variable */
|
||||
/* include its term into resulting inequality */
|
||||
add_term(v, k, a[j]);
|
||||
}
|
||||
else if (a[j] > 0)
|
||||
{ /* substitute x[j] by its lower bound */
|
||||
if (l[k].b == -DBL_MAX)
|
||||
{ /* x[j] has no lower bound */
|
||||
n = -1;
|
||||
goto skip;
|
||||
}
|
||||
else if (l[k].z == 0)
|
||||
{ /* x[j] has simple lower bound */
|
||||
*b -= a[j] * l[k].b;
|
||||
}
|
||||
else
|
||||
{ /* x[j] has variable lower bound (a * z + b) */
|
||||
add_term(v, l[k].z, a[j] * l[k].a);
|
||||
*b -= a[j] * l[k].b;
|
||||
}
|
||||
}
|
||||
else /* a[j] < 0 */
|
||||
{ /* substitute x[j] by its upper bound */
|
||||
if (u[k].b == +DBL_MAX)
|
||||
{ /* x[j] has no upper bound */
|
||||
n = -1;
|
||||
goto skip;
|
||||
}
|
||||
else if (u[k].z == 0)
|
||||
{ /* x[j] has simple upper bound */
|
||||
*b -= a[j] * u[k].b;
|
||||
}
|
||||
else
|
||||
{ /* x[j] has variable upper bound (a * z + b) */
|
||||
add_term(v, u[k].z, a[j] * u[k].a);
|
||||
*b -= a[j] * u[k].b;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* replace tiny coefficients by exact zeros (see add_term) */
|
||||
fvs_adjust_vec(v, 2 * DBL_MIN);
|
||||
/* copy terms of resulting inequality */
|
||||
xassert(v->nnz <= n);
|
||||
n = v->nnz;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ ind[j] = v->ind[j];
|
||||
a[j] = v->vec[ind[j]];
|
||||
}
|
||||
skip: /* restore zero content of v */
|
||||
fvs_clear_vec(v);
|
||||
return n;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* can_be_active - check if inequality can be active
|
||||
*
|
||||
* This routine checks if the specified "0-1 knapsack" inequality
|
||||
*
|
||||
* sum{j in 1..n} a[j]*x[j] <= b
|
||||
*
|
||||
* can be active. If so, the routine returns true, otherwise false. */
|
||||
|
||||
static int can_be_active(int n, const double a[], double b)
|
||||
{ int j;
|
||||
double s;
|
||||
s = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ if (a[j] > 0)
|
||||
s += a[j];
|
||||
}
|
||||
return s > b + .001 * (1 + fabs(b));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* is_sos_ineq - check if inequality is packing (SOS) constraint
|
||||
*
|
||||
* This routine checks if the specified "0-1 knapsack" inequality
|
||||
*
|
||||
* sum{j in 1..n} a[j]*x[j] <= b (1)
|
||||
*
|
||||
* is equivalent to packing inequality (Padberg calls such inequalities
|
||||
* special ordered set or SOS constraints)
|
||||
*
|
||||
* sum{j in J'} x[j] - sum{j in J"} x[j] <= 1 - |J"|. (2)
|
||||
*
|
||||
* If so, the routine returns true, otherwise false.
|
||||
*
|
||||
* Note that if X is a set of feasible binary points satisfying to (2),
|
||||
* its convex hull conv(X) equals to the set of feasible points of LP
|
||||
* relaxation of (2), which is a n-dimensional simplex, so inequalities
|
||||
* (2) are useless for generating cover cuts (due to unimodularity).
|
||||
*
|
||||
* ALGORITHM
|
||||
*
|
||||
* First, we make all a[j] positive by complementing x[j] = 1 - x'[j]
|
||||
* in (1). This is performed implicitly (i.e. actually the array a is
|
||||
* not changed), but b is replaced by b - sum{j : a[j] < 0}.
|
||||
*
|
||||
* Then we find two smallest coefficients a[p] = min{j in 1..n} a[j]
|
||||
* and a[q] = min{j in 1..n : j != p} a[j]. It is obvious that if
|
||||
* a[p] + a[q] > b, then a[i] + a[j] > b for all i != j, from which it
|
||||
* follows that x[i] + x[j] <= 1 for all i != j. But the latter means
|
||||
* that the original inequality (with all a[j] > 0) is equivalent to
|
||||
* packing inequality
|
||||
*
|
||||
* sum{j in 1..n} x[j] <= 1. (3)
|
||||
*
|
||||
* Returning to original (uncomplemented) variables x'[j] = 1 - x[j]
|
||||
* we have that the original inequality is equivalent to (2), where
|
||||
* J' = {j : a[j] > 0} and J" = {j : a[j] < 0}. */
|
||||
|
||||
static int is_sos_ineq(int n, const double a[], double b)
|
||||
{ int j, p, q;
|
||||
xassert(n >= 2);
|
||||
/* compute b := b - sum{j : a[j] < 0} */
|
||||
for (j = 1; j <= n; j++)
|
||||
{ if (a[j] < 0)
|
||||
b -= a[j];
|
||||
}
|
||||
/* find a[p] = min{j in 1..n} a[j] */
|
||||
p = 1;
|
||||
for (j = 2; j <= n; j++)
|
||||
{ if (fabs(a[p]) > fabs(a[j]))
|
||||
p = j;
|
||||
}
|
||||
/* find a[q] = min{j in 1..n : j != p} a[j] */
|
||||
q = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ if (j != p)
|
||||
{ if (q == 0 || fabs(a[q]) > fabs(a[j]))
|
||||
q = j;
|
||||
}
|
||||
}
|
||||
xassert(q != 0);
|
||||
/* check condition a[p] + a[q] > b */
|
||||
return fabs(a[p]) + fabs(a[q]) > b + .001 * (1 + fabs(b));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* process_ineq - basic inequality processing
|
||||
*
|
||||
* This routine performs basic processing of an inequality of "not
|
||||
* greater" type
|
||||
*
|
||||
* sum{j in 1..n} a[j]*x[j] <= b
|
||||
*
|
||||
* specified by the parameters, n, ind, a, and b.
|
||||
*
|
||||
* If the inequality can be transformed to "0-1 knapsack" ineqiality
|
||||
* suitable for generating cover cuts, the routine adds it to the set
|
||||
* of "0-1 knapsack" inequalities.
|
||||
*
|
||||
* Note that the arrays ind and a are not saved on exit. */
|
||||
|
||||
static void process_ineq(struct csa *csa, int n, int ind[], double a[],
|
||||
double b, FVS *v)
|
||||
{ int i;
|
||||
/* attempt to transform the specified inequality to equivalent or
|
||||
* relaxed "0-1 knapsack" inequality */
|
||||
n = build_ks(csa, n, ind, a, &b, v);
|
||||
if (n <= 1)
|
||||
{ /* uninteresting inequality (in principle, such inequalities
|
||||
* should be removed by the preprocessor) */
|
||||
goto done;
|
||||
}
|
||||
if (!can_be_active(n, a, b))
|
||||
{ /* inequality is redundant (i.e. cannot be active) */
|
||||
goto done;
|
||||
}
|
||||
if (is_sos_ineq(n, a, b))
|
||||
{ /* packing (SOS) inequality is useless for generating cover
|
||||
* cuts; currently such inequalities are just ignored */
|
||||
goto done;
|
||||
}
|
||||
/* add resulting "0-1 knapsack" inequality to the set */
|
||||
i = glp_add_rows(csa->set, 1);
|
||||
glp_set_mat_row(csa->set, i, n, ind, a);
|
||||
glp_set_row_bnds(csa->set, i, GLP_UP, b, b);
|
||||
done: return;
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
glp_cov *glp_cov_init(glp_prob *P)
|
||||
{ /* create and initialize cover cut generator */
|
||||
glp_cov *cov;
|
||||
struct csa csa;
|
||||
int i, k, len, *ind;
|
||||
double rhs, *val;
|
||||
FVS fvs;
|
||||
csa.P = P;
|
||||
csa.l = talloc(1+P->n, struct bnd);
|
||||
csa.u = talloc(1+P->n, struct bnd);
|
||||
csa.set = glp_create_prob();
|
||||
glp_add_cols(csa.set, P->n);
|
||||
/* initialize bounds of variables with simple bounds */
|
||||
init_bounds(&csa);
|
||||
/* obtain and set variable bounds */
|
||||
obtain_vbs(&csa);
|
||||
/* allocate working arrays */
|
||||
ind = talloc(1+P->n, int);
|
||||
val = talloc(1+P->n, double);
|
||||
fvs_alloc_vec(&fvs, P->n);
|
||||
/* process all rows of the root mip */
|
||||
for (i = 1; i <= P->m; i++)
|
||||
{ switch (P->row[i]->type)
|
||||
{ case GLP_FR:
|
||||
break;
|
||||
case GLP_LO:
|
||||
/* obtain row of ">=" type */
|
||||
len = glp_get_mat_row(P, i, ind, val);
|
||||
rhs = P->row[i]->lb;
|
||||
/* transforms it to row of "<=" type */
|
||||
for (k = 1; k <= len; k++)
|
||||
val[k] = - val[k];
|
||||
rhs = - rhs;
|
||||
/* process the row */
|
||||
process_ineq(&csa, len, ind, val, rhs, &fvs);
|
||||
break;
|
||||
case GLP_UP:
|
||||
/* obtain row of "<=" type */
|
||||
len = glp_get_mat_row(P, i, ind, val);
|
||||
rhs = P->row[i]->ub;
|
||||
/* and process it */
|
||||
process_ineq(&csa, len, ind, val, rhs, &fvs);
|
||||
break;
|
||||
case GLP_DB:
|
||||
case GLP_FX:
|
||||
/* double-sided inequalitiy and equality constraints are
|
||||
* processed as two separate inequalities */
|
||||
/* obtain row as if it were of ">=" type */
|
||||
len = glp_get_mat_row(P, i, ind, val);
|
||||
rhs = P->row[i]->lb;
|
||||
/* transforms it to row of "<=" type */
|
||||
for (k = 1; k <= len; k++)
|
||||
val[k] = - val[k];
|
||||
rhs = - rhs;
|
||||
/* and process it */
|
||||
process_ineq(&csa, len, ind, val, rhs, &fvs);
|
||||
/* obtain the same row as if it were of "<=" type */
|
||||
len = glp_get_mat_row(P, i, ind, val);
|
||||
rhs = P->row[i]->ub;
|
||||
/* and process it */
|
||||
process_ineq(&csa, len, ind, val, rhs, &fvs);
|
||||
break;
|
||||
default:
|
||||
xassert(P != P);
|
||||
}
|
||||
}
|
||||
/* free working arrays */
|
||||
tfree(ind);
|
||||
tfree(val);
|
||||
fvs_check_vec(&fvs);
|
||||
fvs_free_vec(&fvs);
|
||||
/* the set of "0-1 knapsack" inequalities has been built */
|
||||
if (csa.set->m == 0)
|
||||
{ /* the set is empty */
|
||||
xprintf("No 0-1 knapsack inequalities detected\n");
|
||||
cov = NULL;
|
||||
glp_delete_prob(csa.set);
|
||||
}
|
||||
else
|
||||
{ /* create the cover cut generator working area */
|
||||
xprintf("Number of 0-1 knapsack inequalities = %d\n",
|
||||
csa.set->m);
|
||||
cov = talloc(1, glp_cov);
|
||||
cov->n = P->n;
|
||||
cov->set = csa.set;
|
||||
#if 0
|
||||
glp_write_lp(cov->set, 0, "set.lp");
|
||||
#endif
|
||||
}
|
||||
tfree(csa.l);
|
||||
tfree(csa.u);
|
||||
return cov;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* solve_ks - solve 0-1 knapsack problem
|
||||
*
|
||||
* This routine finds (sub)optimal solution to 0-1 knapsack problem:
|
||||
*
|
||||
* maximize z = sum{j in 1..n} c[j]x[j] (1)
|
||||
*
|
||||
* s.t. sum{j in 1..n} a[j]x[j] <= b (2)
|
||||
*
|
||||
* x[j] in {0, 1} for all j in 1..n (3)
|
||||
*
|
||||
* It is assumed that the instance is non-normalized, i.e. parameters
|
||||
* a, b, and c may have any sign.
|
||||
*
|
||||
* On exit the routine stores the (sub)optimal point found in locations
|
||||
* x[1], ..., x[n] and returns the optimal objective value. However, if
|
||||
* the instance is infeasible, the routine returns INT_MIN. */
|
||||
|
||||
static int solve_ks(int n, const int a[], int b, const int c[],
|
||||
char x[])
|
||||
{ int z;
|
||||
/* surprisingly, even for some small instances (n = 50-100)
|
||||
* MT1 routine takes too much time, so it is used only for tiny
|
||||
* instances */
|
||||
if (n <= 16)
|
||||
#if 0
|
||||
z = ks_enum(n, a, b, c, x);
|
||||
#else
|
||||
z = ks_mt1(n, a, b, c, x);
|
||||
#endif
|
||||
else
|
||||
z = ks_greedy(n, a, b, c, x);
|
||||
return z;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* simple_cover - find simple cover cut
|
||||
*
|
||||
* Given a 0-1 knapsack inequality (which may be globally as well as
|
||||
* locally valid)
|
||||
*
|
||||
* sum{j in 1..n} a[j]x[j] <= b, (1)
|
||||
*
|
||||
* where all x[j] are binary variables and all a[j] are positive, and
|
||||
* a fractional point x~{j in 1..n}, which is feasible to LP relaxation
|
||||
* of (1), this routine attempts to find a simple cover inequality
|
||||
*
|
||||
* sum{j in C} (1 - x[j]) >= 1, (2)
|
||||
*
|
||||
* which is valid for (1) and violated at x~.
|
||||
*
|
||||
* Actually, the routine finds a cover C, i.e. a subset of {1, ..., n}
|
||||
* such that
|
||||
*
|
||||
* sum{j in C} a[j] > b, (3)
|
||||
*
|
||||
* and which minimizes the left-hand side of (2) at x~
|
||||
*
|
||||
* zeta = sum{j in C} (1 - x~[j]). (4)
|
||||
*
|
||||
* On exit the routine stores the characteritic vector z{j in 1..n}
|
||||
* of the cover found (i.e. z[j] = 1 means j in C, and z[j] = 0 means
|
||||
* j not in C), and returns corresponding minimal value of zeta (4).
|
||||
* However, if no cover is found, the routine returns DBL_MAX.
|
||||
*
|
||||
* ALGORITHM
|
||||
*
|
||||
* The separation problem (3)-(4) is converted to 0-1 knapsack problem
|
||||
* as follows.
|
||||
*
|
||||
* First, note that the constraint (3) is equivalent to
|
||||
*
|
||||
* sum{j in 1..n} a[j]z[j] >= b + eps, (5)
|
||||
*
|
||||
* where eps > 0 is a sufficiently small number (in case of integral
|
||||
* a and b we may take eps = 1). Multiplying both sides of (5) by (-1)
|
||||
* gives
|
||||
*
|
||||
* sum{j in 1..n} (-a[j])z[j] <= - b - eps. (6)
|
||||
*
|
||||
* To make all coefficients in (6) positive, z[j] is complemented by
|
||||
* substitution z[j] = 1 - z'[j] that finally gives
|
||||
*
|
||||
* sum{j in 1..n} a[j]z'[j] <= sum{j in 1..n} a[j] - b - eps. (7)
|
||||
*
|
||||
* Minimization of zeta (4) is equivalent to maximization of
|
||||
*
|
||||
* -zeta = sum{j in 1..n} (x~[j] - 1)z[j]. (8)
|
||||
*
|
||||
* Substitution z[j] = 1 - z'[j] gives
|
||||
*
|
||||
* -zeta = sum{j in 1..n} (1 - x~[j])z'[j] - zeta0, (9)
|
||||
*
|
||||
* where zeta0 = sum{j in 1..n} (1 - x~[j]) is a constant term.
|
||||
*
|
||||
* Thus, the 0-1 knapsack problem to be solved is the following:
|
||||
*
|
||||
* maximize
|
||||
*
|
||||
* -zeta = sum{j in 1..n} (1 - x~[j])z'[j] - zeta0 (10)
|
||||
*
|
||||
* subject to
|
||||
*
|
||||
* sum{j in 1..n} a[j]z'[j] <= sum{j in 1..n} a[j] - b - eps (11)
|
||||
*
|
||||
* z'[j] in {0,1} for all j = 1,...,n (12)
|
||||
*
|
||||
* (The constant term zeta0 doesn't affect the solution, so it can be
|
||||
* dropped.) */
|
||||
|
||||
static double simple_cover(int n, const double a[], double b, const
|
||||
double x[], char z[])
|
||||
{ int j, *aa, bb, *cc;
|
||||
double max_aj, min_aj, s, eps;
|
||||
xassert(n >= 3);
|
||||
/* allocate working arrays */
|
||||
aa = talloc(1+n, int);
|
||||
cc = talloc(1+n, int);
|
||||
/* compute max{j in 1..n} a[j] and min{j in 1..n} a[j] */
|
||||
max_aj = 0, min_aj = DBL_MAX;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ xassert(a[j] > 0);
|
||||
if (max_aj < a[j])
|
||||
max_aj = a[j];
|
||||
if (min_aj > a[j])
|
||||
min_aj = a[j];
|
||||
}
|
||||
/* scale and round constraint parameters to make them integral;
|
||||
* note that we make the resulting inequality stronger than (11),
|
||||
* so a[j]'s are rounded up while rhs is rounded down */
|
||||
s = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ s += a[j];
|
||||
aa[j] = ceil(a[j] / max_aj * 1000);
|
||||
}
|
||||
bb = floor((s - b) / max_aj * 1000) - 1;
|
||||
/* scale and round obj. coefficients to make them integral;
|
||||
* again we make the objective function stronger than (10), so
|
||||
* the coefficients are rounded down */
|
||||
for (j = 1; j <= n; j++)
|
||||
{ xassert(0 <= x[j] && x[j] <= 1);
|
||||
cc[j] = floor((1 - x[j]) * 1000);
|
||||
}
|
||||
/* solve separation problem */
|
||||
if (solve_ks(n, aa, bb, cc, z) == INT_MIN)
|
||||
{ /* no cover exists */
|
||||
s = DBL_MAX;
|
||||
goto skip;
|
||||
}
|
||||
/* determine z[j] = 1 - z'[j] */
|
||||
for (j = 1; j <= n; j++)
|
||||
{ xassert(z[j] == 0 || z[j] == 1);
|
||||
z[j] ^= 1;
|
||||
}
|
||||
/* check condition (11) for original (non-scaled) parameters */
|
||||
s = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ if (z[j])
|
||||
s += a[j];
|
||||
}
|
||||
eps = 0.01 * (min_aj >= 1 ? min_aj : 1);
|
||||
if (!(s >= b + eps))
|
||||
{ /* no cover found within a precision req'd */
|
||||
s = DBL_MAX;
|
||||
goto skip;
|
||||
}
|
||||
/* compute corresponding zeta (4) for cover found */
|
||||
s = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ if (z[j])
|
||||
s += 1 - x[j];
|
||||
}
|
||||
skip: /* free working arrays */
|
||||
tfree(aa);
|
||||
tfree(cc);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
void glp_cov_gen1(glp_prob *P, glp_cov *cov, glp_prob *pool)
|
||||
{ /* generate locally valid simple cover cuts */
|
||||
int i, k, len, new_len, *ind;
|
||||
double *val, rhs, *x, zeta;
|
||||
char *z;
|
||||
xassert(P->n == cov->n && P->n == cov->set->n);
|
||||
xassert(glp_get_status(P) == GLP_OPT);
|
||||
/* allocate working arrays */
|
||||
ind = talloc(1+P->n, int);
|
||||
val = talloc(1+P->n, double);
|
||||
x = talloc(1+P->n, double);
|
||||
z = talloc(1+P->n, char);
|
||||
/* walk thru 0-1 knapsack inequalities */
|
||||
for (i = 1; i <= cov->set->m; i++)
|
||||
{ /* retrieve 0-1 knapsack inequality */
|
||||
len = glp_get_mat_row(cov->set, i, ind, val);
|
||||
rhs = glp_get_row_ub(cov->set, i);
|
||||
xassert(rhs != +DBL_MAX);
|
||||
/* FIXME: skip, if slack is too large? */
|
||||
/* substitute and eliminate binary variables which have been
|
||||
* fixed in the current subproblem (this makes the inequality
|
||||
* only locally valid) */
|
||||
new_len = 0;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (glp_get_col_type(P, ind[k]) == GLP_FX)
|
||||
rhs -= val[k] * glp_get_col_prim(P, ind[k]);
|
||||
else
|
||||
{ new_len++;
|
||||
ind[new_len] = ind[k];
|
||||
val[new_len] = val[k];
|
||||
}
|
||||
}
|
||||
len = new_len;
|
||||
/* we need at least 3 binary variables in the inequality */
|
||||
if (len <= 2)
|
||||
continue;
|
||||
/* obtain values of binary variables from optimal solution to
|
||||
* LP relaxation of current subproblem */
|
||||
for (k = 1; k <= len; k++)
|
||||
{ xassert(glp_get_col_kind(P, ind[k]) == GLP_BV);
|
||||
x[k] = glp_get_col_prim(P, ind[k]);
|
||||
if (x[k] < 0.00001)
|
||||
x[k] = 0;
|
||||
else if (x[k] > 0.99999)
|
||||
x[k] = 1;
|
||||
/* if val[k] < 0, perform substitution x[k] = 1 - x'[k] to
|
||||
* make all coefficients positive */
|
||||
if (val[k] < 0)
|
||||
{ ind[k] = - ind[k]; /* x[k] is complemented */
|
||||
val[k] = - val[k];
|
||||
rhs += val[k];
|
||||
x[k] = 1 - x[k];
|
||||
}
|
||||
}
|
||||
/* find locally valid simple cover cut */
|
||||
zeta = simple_cover(len, val, rhs, x, z);
|
||||
if (zeta > 0.95)
|
||||
{ /* no violation or insufficient violation; see (2) */
|
||||
continue;
|
||||
}
|
||||
/* construct cover inequality (2) for the cover found, which
|
||||
* for original binary variables x[k] is equivalent to:
|
||||
* sum{k in C'} x[k] + sum{k in C"} x'[k] <= |C| - 1
|
||||
* or
|
||||
* sum{k in C'} x[k] + sum{k in C"} (1 - x[k]) <= |C| - 1
|
||||
* or
|
||||
* sum{k in C'} x[k] - sum{k in C"} x[k] <= |C'| - 1
|
||||
* since |C| - |C"| = |C'| */
|
||||
new_len = 0;
|
||||
rhs = -1;
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (z[k])
|
||||
{ new_len++;
|
||||
if (ind[k] > 0)
|
||||
{ ind[new_len] = +ind[k];
|
||||
val[new_len] = +1;
|
||||
rhs++;
|
||||
}
|
||||
else /* ind[k] < 0 */
|
||||
{ ind[new_len] = -ind[k];
|
||||
val[new_len] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
len = new_len;
|
||||
/* add the cover inequality to the local cut pool */
|
||||
k = glp_add_rows(pool, 1);
|
||||
glp_set_mat_row(pool, k, len, ind, val);
|
||||
glp_set_row_bnds(pool, k, GLP_UP, rhs, rhs);
|
||||
}
|
||||
/* free working arrays */
|
||||
tfree(ind);
|
||||
tfree(val);
|
||||
tfree(x);
|
||||
tfree(z);
|
||||
return;
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
void glp_cov_free(glp_cov *cov)
|
||||
{ /* delete cover cut generator workspace */
|
||||
xassert(cov != NULL);
|
||||
glp_delete_prob(cov->set);
|
||||
tfree(cov);
|
||||
return;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
/* fpump.c (feasibility pump heuristic) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2009-2018 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "ios.h"
|
||||
#include "rng.h"
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* ios_feas_pump - feasibility pump heuristic
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void ios_feas_pump(glp_tree *T);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine ios_feas_pump is a simple implementation of the Feasi-
|
||||
* bility Pump heuristic.
|
||||
*
|
||||
* REFERENCES
|
||||
*
|
||||
* M.Fischetti, F.Glover, and A.Lodi. "The feasibility pump." Math.
|
||||
* Program., Ser. A 104, pp. 91-104 (2005). */
|
||||
|
||||
struct VAR
|
||||
{ /* binary variable */
|
||||
int j;
|
||||
/* ordinal number */
|
||||
int x;
|
||||
/* value in the rounded solution (0 or 1) */
|
||||
double d;
|
||||
/* sorting key */
|
||||
};
|
||||
|
||||
static int CDECL fcmp(const void *x, const void *y)
|
||||
{ /* comparison routine */
|
||||
const struct VAR *vx = x, *vy = y;
|
||||
if (vx->d > vy->d)
|
||||
return -1;
|
||||
else if (vx->d < vy->d)
|
||||
return +1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ios_feas_pump(glp_tree *T)
|
||||
{ glp_prob *P = T->mip;
|
||||
int n = P->n;
|
||||
glp_prob *lp = NULL;
|
||||
struct VAR *var = NULL;
|
||||
RNG *rand = NULL;
|
||||
GLPCOL *col;
|
||||
glp_smcp parm;
|
||||
int j, k, new_x, nfail, npass, nv, ret, stalling;
|
||||
double dist, tol;
|
||||
xassert(glp_get_status(P) == GLP_OPT);
|
||||
/* this heuristic is applied only once on the root level */
|
||||
if (!(T->curr->level == 0 && T->curr->solved == 1)) goto done;
|
||||
/* determine number of binary variables */
|
||||
nv = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ col = P->col[j];
|
||||
/* if x[j] is continuous, skip it */
|
||||
if (col->kind == GLP_CV) continue;
|
||||
/* if x[j] is fixed, skip it */
|
||||
if (col->type == GLP_FX) continue;
|
||||
/* x[j] is non-fixed integer */
|
||||
xassert(col->kind == GLP_IV);
|
||||
if (col->type == GLP_DB && col->lb == 0.0 && col->ub == 1.0)
|
||||
{ /* x[j] is binary */
|
||||
nv++;
|
||||
}
|
||||
else
|
||||
{ /* x[j] is general integer */
|
||||
if (T->parm->msg_lev >= GLP_MSG_ALL)
|
||||
xprintf("FPUMP heuristic cannot be applied due to genera"
|
||||
"l integer variables\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* there must be at least one binary variable */
|
||||
if (nv == 0) goto done;
|
||||
if (T->parm->msg_lev >= GLP_MSG_ALL)
|
||||
xprintf("Applying FPUMP heuristic...\n");
|
||||
/* build the list of binary variables */
|
||||
var = xcalloc(1+nv, sizeof(struct VAR));
|
||||
k = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ col = P->col[j];
|
||||
if (col->kind == GLP_IV && col->type == GLP_DB)
|
||||
var[++k].j = j;
|
||||
}
|
||||
xassert(k == nv);
|
||||
/* create working problem object */
|
||||
lp = glp_create_prob();
|
||||
more: /* copy the original problem object to keep it intact */
|
||||
glp_copy_prob(lp, P, GLP_OFF);
|
||||
/* we are interested to find an integer feasible solution, which
|
||||
is better than the best known one */
|
||||
if (P->mip_stat == GLP_FEAS)
|
||||
{ int *ind;
|
||||
double *val, bnd;
|
||||
/* add a row and make it identical to the objective row */
|
||||
glp_add_rows(lp, 1);
|
||||
ind = xcalloc(1+n, sizeof(int));
|
||||
val = xcalloc(1+n, sizeof(double));
|
||||
for (j = 1; j <= n; j++)
|
||||
{ ind[j] = j;
|
||||
val[j] = P->col[j]->coef;
|
||||
}
|
||||
glp_set_mat_row(lp, lp->m, n, ind, val);
|
||||
xfree(ind);
|
||||
xfree(val);
|
||||
/* introduce upper (minimization) or lower (maximization)
|
||||
bound to the original objective function; note that this
|
||||
additional constraint is not violated at the optimal point
|
||||
to LP relaxation */
|
||||
#if 0 /* modified by xypron <xypron.glpk@gmx.de> */
|
||||
if (P->dir == GLP_MIN)
|
||||
{ bnd = P->mip_obj - 0.10 * (1.0 + fabs(P->mip_obj));
|
||||
if (bnd < P->obj_val) bnd = P->obj_val;
|
||||
glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0);
|
||||
}
|
||||
else if (P->dir == GLP_MAX)
|
||||
{ bnd = P->mip_obj + 0.10 * (1.0 + fabs(P->mip_obj));
|
||||
if (bnd > P->obj_val) bnd = P->obj_val;
|
||||
glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0);
|
||||
}
|
||||
else
|
||||
xassert(P != P);
|
||||
#else
|
||||
bnd = 0.1 * P->obj_val + 0.9 * P->mip_obj;
|
||||
/* xprintf("bnd = %f\n", bnd); */
|
||||
if (P->dir == GLP_MIN)
|
||||
glp_set_row_bnds(lp, lp->m, GLP_UP, 0.0, bnd - P->c0);
|
||||
else if (P->dir == GLP_MAX)
|
||||
glp_set_row_bnds(lp, lp->m, GLP_LO, bnd - P->c0, 0.0);
|
||||
else
|
||||
xassert(P != P);
|
||||
#endif
|
||||
}
|
||||
/* reset pass count */
|
||||
npass = 0;
|
||||
/* invalidate the rounded point */
|
||||
for (k = 1; k <= nv; k++)
|
||||
var[k].x = -1;
|
||||
pass: /* next pass starts here */
|
||||
npass++;
|
||||
if (T->parm->msg_lev >= GLP_MSG_ALL)
|
||||
xprintf("Pass %d\n", npass);
|
||||
/* initialize minimal distance between the basic point and the
|
||||
rounded one obtained during this pass */
|
||||
dist = DBL_MAX;
|
||||
/* reset failure count (the number of succeeded iterations failed
|
||||
to improve the distance) */
|
||||
nfail = 0;
|
||||
/* if it is not the first pass, perturb the last rounded point
|
||||
rather than construct it from the basic solution */
|
||||
if (npass > 1)
|
||||
{ double rho, temp;
|
||||
if (rand == NULL)
|
||||
rand = rng_create_rand();
|
||||
for (k = 1; k <= nv; k++)
|
||||
{ j = var[k].j;
|
||||
col = lp->col[j];
|
||||
rho = rng_uniform(rand, -0.3, 0.7);
|
||||
if (rho < 0.0) rho = 0.0;
|
||||
temp = fabs((double)var[k].x - col->prim);
|
||||
if (temp + rho > 0.5) var[k].x = 1 - var[k].x;
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
loop: /* innermost loop begins here */
|
||||
/* round basic solution (which is assumed primal feasible) */
|
||||
stalling = 1;
|
||||
for (k = 1; k <= nv; k++)
|
||||
{ col = lp->col[var[k].j];
|
||||
if (col->prim < 0.5)
|
||||
{ /* rounded value is 0 */
|
||||
new_x = 0;
|
||||
}
|
||||
else
|
||||
{ /* rounded value is 1 */
|
||||
new_x = 1;
|
||||
}
|
||||
if (var[k].x != new_x)
|
||||
{ stalling = 0;
|
||||
var[k].x = new_x;
|
||||
}
|
||||
}
|
||||
/* if the rounded point has not changed (stalling), choose and
|
||||
flip some its entries heuristically */
|
||||
if (stalling)
|
||||
{ /* compute d[j] = |x[j] - round(x[j])| */
|
||||
for (k = 1; k <= nv; k++)
|
||||
{ col = lp->col[var[k].j];
|
||||
var[k].d = fabs(col->prim - (double)var[k].x);
|
||||
}
|
||||
/* sort the list of binary variables by descending d[j] */
|
||||
qsort(&var[1], nv, sizeof(struct VAR), fcmp);
|
||||
/* choose and flip some rounded components */
|
||||
for (k = 1; k <= nv; k++)
|
||||
{ if (k >= 5 && var[k].d < 0.35 || k >= 10) break;
|
||||
var[k].x = 1 - var[k].x;
|
||||
}
|
||||
}
|
||||
skip: /* check if the time limit has been exhausted */
|
||||
if (T->parm->tm_lim < INT_MAX &&
|
||||
(double)(T->parm->tm_lim - 1) <=
|
||||
1000.0 * xdifftime(xtime(), T->tm_beg)) goto done;
|
||||
/* build the objective, which is the distance between the current
|
||||
(basic) point and the rounded one */
|
||||
lp->dir = GLP_MIN;
|
||||
lp->c0 = 0.0;
|
||||
for (j = 1; j <= n; j++)
|
||||
lp->col[j]->coef = 0.0;
|
||||
for (k = 1; k <= nv; k++)
|
||||
{ j = var[k].j;
|
||||
if (var[k].x == 0)
|
||||
lp->col[j]->coef = +1.0;
|
||||
else
|
||||
{ lp->col[j]->coef = -1.0;
|
||||
lp->c0 += 1.0;
|
||||
}
|
||||
}
|
||||
/* minimize the distance with the simplex method */
|
||||
glp_init_smcp(&parm);
|
||||
if (T->parm->msg_lev <= GLP_MSG_ERR)
|
||||
parm.msg_lev = T->parm->msg_lev;
|
||||
else if (T->parm->msg_lev <= GLP_MSG_ALL)
|
||||
{ parm.msg_lev = GLP_MSG_ON;
|
||||
parm.out_dly = 10000;
|
||||
}
|
||||
ret = glp_simplex(lp, &parm);
|
||||
if (ret != 0)
|
||||
{ if (T->parm->msg_lev >= GLP_MSG_ERR)
|
||||
xprintf("Warning: glp_simplex returned %d\n", ret);
|
||||
goto done;
|
||||
}
|
||||
ret = glp_get_status(lp);
|
||||
if (ret != GLP_OPT)
|
||||
{ if (T->parm->msg_lev >= GLP_MSG_ERR)
|
||||
xprintf("Warning: glp_get_status returned %d\n", ret);
|
||||
goto done;
|
||||
}
|
||||
if (T->parm->msg_lev >= GLP_MSG_DBG)
|
||||
xprintf("delta = %g\n", lp->obj_val);
|
||||
/* check if the basic solution is integer feasible; note that it
|
||||
may be so even if the minimial distance is positive */
|
||||
tol = 0.3 * T->parm->tol_int;
|
||||
for (k = 1; k <= nv; k++)
|
||||
{ col = lp->col[var[k].j];
|
||||
if (tol < col->prim && col->prim < 1.0 - tol) break;
|
||||
}
|
||||
if (k > nv)
|
||||
{ /* okay; the basic solution seems to be integer feasible */
|
||||
double *x = xcalloc(1+n, sizeof(double));
|
||||
for (j = 1; j <= n; j++)
|
||||
{ x[j] = lp->col[j]->prim;
|
||||
if (P->col[j]->kind == GLP_IV) x[j] = floor(x[j] + 0.5);
|
||||
}
|
||||
#if 1 /* modified by xypron <xypron.glpk@gmx.de> */
|
||||
/* reset direction and right-hand side of objective */
|
||||
lp->c0 = P->c0;
|
||||
lp->dir = P->dir;
|
||||
/* fix integer variables */
|
||||
for (k = 1; k <= nv; k++)
|
||||
#if 0 /* 18/VI-2013; fixed by mao
|
||||
* this bug causes numerical instability, because column statuses
|
||||
* are not changed appropriately */
|
||||
{ lp->col[var[k].j]->lb = x[var[k].j];
|
||||
lp->col[var[k].j]->ub = x[var[k].j];
|
||||
lp->col[var[k].j]->type = GLP_FX;
|
||||
}
|
||||
#else
|
||||
glp_set_col_bnds(lp, var[k].j, GLP_FX, x[var[k].j], 0.);
|
||||
#endif
|
||||
/* copy original objective function */
|
||||
for (j = 1; j <= n; j++)
|
||||
lp->col[j]->coef = P->col[j]->coef;
|
||||
/* solve original LP and copy result */
|
||||
ret = glp_simplex(lp, &parm);
|
||||
if (ret != 0)
|
||||
{ if (T->parm->msg_lev >= GLP_MSG_ERR)
|
||||
xprintf("Warning: glp_simplex returned %d\n", ret);
|
||||
#if 1 /* 17/III-2016: fix memory leak */
|
||||
xfree(x);
|
||||
#endif
|
||||
goto done;
|
||||
}
|
||||
ret = glp_get_status(lp);
|
||||
if (ret != GLP_OPT)
|
||||
{ if (T->parm->msg_lev >= GLP_MSG_ERR)
|
||||
xprintf("Warning: glp_get_status returned %d\n", ret);
|
||||
#if 1 /* 17/III-2016: fix memory leak */
|
||||
xfree(x);
|
||||
#endif
|
||||
goto done;
|
||||
}
|
||||
for (j = 1; j <= n; j++)
|
||||
if (P->col[j]->kind != GLP_IV) x[j] = lp->col[j]->prim;
|
||||
#endif
|
||||
ret = glp_ios_heur_sol(T, x);
|
||||
xfree(x);
|
||||
if (ret == 0)
|
||||
{ /* the integer solution is accepted */
|
||||
if (ios_is_hopeful(T, T->curr->bound))
|
||||
{ /* it is reasonable to apply the heuristic once again */
|
||||
goto more;
|
||||
}
|
||||
else
|
||||
{ /* the best known integer feasible solution just found
|
||||
is close to optimal solution to LP relaxation */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* the basic solution is fractional */
|
||||
if (dist == DBL_MAX ||
|
||||
lp->obj_val <= dist - 1e-6 * (1.0 + dist))
|
||||
{ /* the distance is reducing */
|
||||
nfail = 0, dist = lp->obj_val;
|
||||
}
|
||||
else
|
||||
{ /* improving the distance failed */
|
||||
nfail++;
|
||||
}
|
||||
if (nfail < 3) goto loop;
|
||||
if (npass < 5) goto pass;
|
||||
done: /* delete working objects */
|
||||
if (lp != NULL) glp_delete_prob(lp);
|
||||
if (var != NULL) xfree(var);
|
||||
if (rand != NULL) rng_delete_rand(rand);
|
||||
return;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
/* gmicut.c (Gomory's mixed integer cut generator) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2002-2016 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "prob.h"
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* glp_gmi_cut - generate Gomory's mixed integer cut (core routine)
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* int glp_gmi_cut(glp_prob *P, int j, int ind[], double val[], double
|
||||
* phi[]);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* This routine attempts to generate a Gomory's mixed integer cut for
|
||||
* specified integer column (structural variable), whose primal value
|
||||
* in current basic solution is integer infeasible (fractional).
|
||||
*
|
||||
* On entry to the routine the basic solution contained in the problem
|
||||
* object P should be optimal, and the basis factorization should be
|
||||
* valid. The parameter j should specify the ordinal number of column
|
||||
* (structural variable x[j]), for which the cut should be generated,
|
||||
* 1 <= j <= n, where n is the number of columns in the problem object.
|
||||
* This column should be integer, non-fixed, and basic, and its primal
|
||||
* value should be fractional.
|
||||
*
|
||||
* The cut generated by the routine is the following inequality:
|
||||
*
|
||||
* sum a[j] * x[j] >= b,
|
||||
*
|
||||
* which is expected to be violated at the current basic solution.
|
||||
*
|
||||
* If the cut has been successfully generated, the routine stores its
|
||||
* non-zero coefficients a[j] and corresponding column indices j in the
|
||||
* array locations val[1], ..., val[len] and ind[1], ..., ind[len],
|
||||
* where 1 <= len <= n is the number of non-zero coefficients. The
|
||||
* right-hand side value b is stored in val[0], and ind[0] is set to 0.
|
||||
*
|
||||
* The working array phi should have 1+m+n locations (location phi[0]
|
||||
* is not used), where m and n is the number of rows and columns in the
|
||||
* problem object, resp.
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* If the cut has been successfully generated, the routine returns
|
||||
* len, the number of non-zero coefficients in the cut, 1 <= len <= n.
|
||||
*
|
||||
* Otherwise, the routine returns one of the following codes:
|
||||
*
|
||||
* -1 current basis factorization is not valid;
|
||||
*
|
||||
* -2 current basic solution is not optimal;
|
||||
*
|
||||
* -3 column ordinal number j is out of range;
|
||||
*
|
||||
* -4 variable x[j] is not of integral kind;
|
||||
*
|
||||
* -5 variable x[j] is either fixed or non-basic;
|
||||
*
|
||||
* -6 primal value of variable x[j] in basic solution is too close
|
||||
* to nearest integer;
|
||||
*
|
||||
* -7 some coefficients in the simplex table row corresponding to
|
||||
* variable x[j] are too large in magnitude;
|
||||
*
|
||||
* -8 some free (unbounded) variables have non-zero coefficients in
|
||||
* the simplex table row corresponding to variable x[j].
|
||||
*
|
||||
* ALGORITHM
|
||||
*
|
||||
* See glpk/doc/notes/gomory (in Russian). */
|
||||
|
||||
#define f(x) ((x) - floor(x))
|
||||
/* compute fractional part of x */
|
||||
|
||||
int glp_gmi_cut(glp_prob *P, int j,
|
||||
int ind[/*1+n*/], double val[/*1+n*/], double phi[/*1+m+n*/])
|
||||
{ int m = P->m;
|
||||
int n = P->n;
|
||||
GLPROW *row;
|
||||
GLPCOL *col;
|
||||
GLPAIJ *aij;
|
||||
int i, k, len, kind, stat;
|
||||
double lb, ub, alfa, beta, ksi, phi1, rhs;
|
||||
/* sanity checks */
|
||||
if (!(P->m == 0 || P->valid))
|
||||
{ /* current basis factorization is not valid */
|
||||
return -1;
|
||||
}
|
||||
if (!(P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS))
|
||||
{ /* current basic solution is not optimal */
|
||||
return -2;
|
||||
}
|
||||
if (!(1 <= j && j <= n))
|
||||
{ /* column ordinal number is out of range */
|
||||
return -3;
|
||||
}
|
||||
col = P->col[j];
|
||||
if (col->kind != GLP_IV)
|
||||
{ /* x[j] is not of integral kind */
|
||||
return -4;
|
||||
}
|
||||
if (col->type == GLP_FX || col->stat != GLP_BS)
|
||||
{ /* x[j] is either fixed or non-basic */
|
||||
return -5;
|
||||
}
|
||||
if (fabs(col->prim - floor(col->prim + 0.5)) < 0.001)
|
||||
{ /* primal value of x[j] is too close to nearest integer */
|
||||
return -6;
|
||||
}
|
||||
/* compute row of the simplex tableau, which (row) corresponds
|
||||
* to specified basic variable xB[i] = x[j]; see (23) */
|
||||
len = glp_eval_tab_row(P, m+j, ind, val);
|
||||
/* determine beta[i], which a value of xB[i] in optimal solution
|
||||
* to current LP relaxation; note that this value is the same as
|
||||
* if it would be computed with formula (27); it is assumed that
|
||||
* beta[i] is fractional enough */
|
||||
beta = P->col[j]->prim;
|
||||
/* compute cut coefficients phi and right-hand side rho, which
|
||||
* correspond to formula (30); dense format is used, because rows
|
||||
* of the simplex tableau are usually dense */
|
||||
for (k = 1; k <= m+n; k++)
|
||||
phi[k] = 0.0;
|
||||
rhs = f(beta); /* initial value of rho; see (28), (32) */
|
||||
for (j = 1; j <= len; j++)
|
||||
{ /* determine original number of non-basic variable xN[j] */
|
||||
k = ind[j];
|
||||
xassert(1 <= k && k <= m+n);
|
||||
/* determine the kind, bounds and current status of xN[j] in
|
||||
* optimal solution to LP relaxation */
|
||||
if (k <= m)
|
||||
{ /* auxiliary variable */
|
||||
row = P->row[k];
|
||||
kind = GLP_CV;
|
||||
lb = row->lb;
|
||||
ub = row->ub;
|
||||
stat = row->stat;
|
||||
}
|
||||
else
|
||||
{ /* structural variable */
|
||||
col = P->col[k-m];
|
||||
kind = col->kind;
|
||||
lb = col->lb;
|
||||
ub = col->ub;
|
||||
stat = col->stat;
|
||||
}
|
||||
/* xN[j] cannot be basic */
|
||||
xassert(stat != GLP_BS);
|
||||
/* determine row coefficient ksi[i,j] at xN[j]; see (23) */
|
||||
ksi = val[j];
|
||||
/* if ksi[i,j] is too large in magnitude, report failure */
|
||||
if (fabs(ksi) > 1e+05)
|
||||
return -7;
|
||||
/* if ksi[i,j] is too small in magnitude, skip it */
|
||||
if (fabs(ksi) < 1e-10)
|
||||
goto skip;
|
||||
/* compute row coefficient alfa[i,j] at y[j]; see (26) */
|
||||
switch (stat)
|
||||
{ case GLP_NF:
|
||||
/* xN[j] is free (unbounded) having non-zero ksi[i,j];
|
||||
* report failure */
|
||||
return -8;
|
||||
case GLP_NL:
|
||||
/* xN[j] has active lower bound */
|
||||
alfa = - ksi;
|
||||
break;
|
||||
case GLP_NU:
|
||||
/* xN[j] has active upper bound */
|
||||
alfa = + ksi;
|
||||
break;
|
||||
case GLP_NS:
|
||||
/* xN[j] is fixed; skip it */
|
||||
goto skip;
|
||||
default:
|
||||
xassert(stat != stat);
|
||||
}
|
||||
/* compute cut coefficient phi'[j] at y[j]; see (21), (28) */
|
||||
switch (kind)
|
||||
{ case GLP_IV:
|
||||
/* y[j] is integer */
|
||||
if (fabs(alfa - floor(alfa + 0.5)) < 1e-10)
|
||||
{ /* alfa[i,j] is close to nearest integer; skip it */
|
||||
goto skip;
|
||||
}
|
||||
else if (f(alfa) <= f(beta))
|
||||
phi1 = f(alfa);
|
||||
else
|
||||
phi1 = (f(beta) / (1.0 - f(beta))) * (1.0 - f(alfa));
|
||||
break;
|
||||
case GLP_CV:
|
||||
/* y[j] is continuous */
|
||||
if (alfa >= 0.0)
|
||||
phi1 = + alfa;
|
||||
else
|
||||
phi1 = (f(beta) / (1.0 - f(beta))) * (- alfa);
|
||||
break;
|
||||
default:
|
||||
xassert(kind != kind);
|
||||
}
|
||||
/* compute cut coefficient phi[j] at xN[j] and update right-
|
||||
* hand side rho; see (31), (32) */
|
||||
switch (stat)
|
||||
{ case GLP_NL:
|
||||
/* xN[j] has active lower bound */
|
||||
phi[k] = + phi1;
|
||||
rhs += phi1 * lb;
|
||||
break;
|
||||
case GLP_NU:
|
||||
/* xN[j] has active upper bound */
|
||||
phi[k] = - phi1;
|
||||
rhs -= phi1 * ub;
|
||||
break;
|
||||
default:
|
||||
xassert(stat != stat);
|
||||
}
|
||||
skip: ;
|
||||
}
|
||||
/* now the cut has the form sum_k phi[k] * x[k] >= rho, where cut
|
||||
* coefficients are stored in the array phi in dense format;
|
||||
* x[1,...,m] are auxiliary variables, x[m+1,...,m+n] are struc-
|
||||
* tural variables; see (30) */
|
||||
/* eliminate auxiliary variables in order to express the cut only
|
||||
* through structural variables; see (33) */
|
||||
for (i = 1; i <= m; i++)
|
||||
{ if (fabs(phi[i]) < 1e-10)
|
||||
continue;
|
||||
/* auxiliary variable x[i] has non-zero cut coefficient */
|
||||
row = P->row[i];
|
||||
/* x[i] cannot be fixed variable */
|
||||
xassert(row->type != GLP_FX);
|
||||
/* substitute x[i] = sum_j a[i,j] * x[m+j] */
|
||||
for (aij = row->ptr; aij != NULL; aij = aij->r_next)
|
||||
phi[m+aij->col->j] += phi[i] * aij->val;
|
||||
}
|
||||
/* convert the final cut to sparse format and substitute fixed
|
||||
* (structural) variables */
|
||||
len = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ if (fabs(phi[m+j]) < 1e-10)
|
||||
continue;
|
||||
/* structural variable x[m+j] has non-zero cut coefficient */
|
||||
col = P->col[j];
|
||||
if (col->type == GLP_FX)
|
||||
{ /* eliminate x[m+j] */
|
||||
rhs -= phi[m+j] * col->lb;
|
||||
}
|
||||
else
|
||||
{ len++;
|
||||
ind[len] = j;
|
||||
val[len] = phi[m+j];
|
||||
}
|
||||
}
|
||||
if (fabs(rhs) < 1e-12)
|
||||
rhs = 0.0;
|
||||
ind[0] = 0, val[0] = rhs;
|
||||
/* the cut has been successfully generated */
|
||||
return len;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/* gmigen.c (Gomory's mixed integer cuts generator) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2002-2018 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "prob.h"
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* glp_gmi_gen - generate Gomory's mixed integer cuts
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* int glp_gmi_gen(glp_prob *P, glp_prob *pool, int max_cuts);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* This routine attempts to generate Gomory's mixed integer cuts for
|
||||
* integer variables, whose primal values in current basic solution are
|
||||
* integer infeasible (fractional).
|
||||
*
|
||||
* On entry to the routine the basic solution contained in the problem
|
||||
* object P should be optimal, and the basis factorization should be
|
||||
* valid.
|
||||
*
|
||||
* The cutting plane inequalities generated by the routine are added to
|
||||
* the specified cut pool.
|
||||
*
|
||||
* The parameter max_cuts specifies the maximal number of cuts to be
|
||||
* generated. Note that the number of cuts cannot exceed the number of
|
||||
* basic variables, which is the number of rows in the problem object.
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* The routine returns the number of cuts that have been generated and
|
||||
* added to the cut pool. */
|
||||
|
||||
#define f(x) ((x) - floor(x))
|
||||
/* compute fractional part of x */
|
||||
|
||||
struct var { int j; double f; };
|
||||
|
||||
static int CDECL fcmp(const void *p1, const void *p2)
|
||||
{ const struct var *v1 = p1, *v2 = p2;
|
||||
if (v1->f > v2->f) return -1;
|
||||
if (v1->f < v2->f) return +1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int glp_gmi_gen(glp_prob *P, glp_prob *pool, int max_cuts)
|
||||
{ int m = P->m;
|
||||
int n = P->n;
|
||||
GLPCOL *col;
|
||||
struct var *var;
|
||||
int i, j, k, t, len, nv, nnn, *ind;
|
||||
double frac, *val, *phi;
|
||||
/* sanity checks */
|
||||
if (!(P->m == 0 || P->valid))
|
||||
xerror("glp_gmi_gen: basis factorization does not exist\n");
|
||||
if (!(P->pbs_stat == GLP_FEAS && P->dbs_stat == GLP_FEAS))
|
||||
xerror("glp_gmi_gen: optimal basic solution required\n");
|
||||
if (pool->n != n)
|
||||
xerror("glp_gmi_gen: cut pool has wrong number of columns\n");
|
||||
/* allocate working arrays */
|
||||
var = xcalloc(1+n, sizeof(struct var));
|
||||
ind = xcalloc(1+n, sizeof(int));
|
||||
val = xcalloc(1+n, sizeof(double));
|
||||
phi = xcalloc(1+m+n, sizeof(double));
|
||||
/* build the list of integer structural variables, which are
|
||||
* basic and have integer infeasible (fractional) primal values
|
||||
* in optimal solution to specified LP */
|
||||
nv = 0;
|
||||
for (j = 1; j <= n; j++)
|
||||
{ col = P->col[j];
|
||||
if (col->kind != GLP_IV)
|
||||
continue;
|
||||
if (col->type == GLP_FX)
|
||||
continue;
|
||||
if (col->stat != GLP_BS)
|
||||
continue;
|
||||
frac = f(col->prim);
|
||||
if (!(0.05 <= frac && frac <= 0.95))
|
||||
continue;
|
||||
/* add variable to the list */
|
||||
nv++, var[nv].j = j, var[nv].f = frac;
|
||||
}
|
||||
/* sort the list by descending fractionality */
|
||||
qsort(&var[1], nv, sizeof(struct var), fcmp);
|
||||
/* try to generate cuts by one for each variable in the list, but
|
||||
* not more than max_cuts cuts */
|
||||
nnn = 0;
|
||||
for (t = 1; t <= nv; t++)
|
||||
{ len = glp_gmi_cut(P, var[t].j, ind, val, phi);
|
||||
if (len < 1)
|
||||
goto skip;
|
||||
/* if the cut inequality seems to be badly scaled, reject it
|
||||
* to avoid numerical difficulties */
|
||||
for (k = 1; k <= len; k++)
|
||||
{ if (fabs(val[k]) < 1e-03)
|
||||
goto skip;
|
||||
if (fabs(val[k]) > 1e+03)
|
||||
goto skip;
|
||||
}
|
||||
/* add the cut to the cut pool for further consideration */
|
||||
i = glp_add_rows(pool, 1);
|
||||
glp_set_row_bnds(pool, i, GLP_LO, val[0], 0);
|
||||
glp_set_mat_row(pool, i, len, ind, val);
|
||||
/* one cut has been generated */
|
||||
nnn++;
|
||||
if (nnn == max_cuts)
|
||||
break;
|
||||
skip: ;
|
||||
}
|
||||
/* free working arrays */
|
||||
xfree(var);
|
||||
xfree(ind);
|
||||
xfree(val);
|
||||
xfree(phi);
|
||||
return nnn;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
+1527
File diff suppressed because it is too large
Load Diff
+301
@@ -0,0 +1,301 @@
|
||||
/* spv.c (operations on sparse vectors) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2007-2017 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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 "spv.h"
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_create_vec - create sparse vector
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* SPV *spv_create_vec(int n);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_create_vec creates a sparse vector of dimension n,
|
||||
* which initially is a null vector.
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* The routine returns a pointer to the vector created. */
|
||||
|
||||
SPV *spv_create_vec(int n)
|
||||
{ SPV *v;
|
||||
xassert(n >= 0);
|
||||
v = xmalloc(sizeof(SPV));
|
||||
v->n = n;
|
||||
v->nnz = 0;
|
||||
v->pos = xcalloc(1+n, sizeof(int));
|
||||
memset(&v->pos[1], 0, n * sizeof(int));
|
||||
v->ind = xcalloc(1+n, sizeof(int));
|
||||
v->val = xcalloc(1+n, sizeof(double));
|
||||
return v;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_check_vec - check that sparse vector has correct representation
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void spv_check_vec(SPV *v);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_check_vec checks that a sparse vector specified by
|
||||
* the parameter v has correct representation.
|
||||
*
|
||||
* NOTE
|
||||
*
|
||||
* Complexity of this operation is O(n). */
|
||||
|
||||
void spv_check_vec(SPV *v)
|
||||
{ int j, k, nnz;
|
||||
xassert(v->n >= 0);
|
||||
nnz = 0;
|
||||
for (j = v->n; j >= 1; j--)
|
||||
{ k = v->pos[j];
|
||||
xassert(0 <= k && k <= v->nnz);
|
||||
if (k != 0)
|
||||
{ xassert(v->ind[k] == j);
|
||||
nnz++;
|
||||
}
|
||||
}
|
||||
xassert(v->nnz == nnz);
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_get_vj - retrieve component of sparse vector
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* double spv_get_vj(SPV *v, int j);
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* The routine spv_get_vj returns j-th component of a sparse vector
|
||||
* specified by the parameter v. */
|
||||
|
||||
double spv_get_vj(SPV *v, int j)
|
||||
{ int k;
|
||||
xassert(1 <= j && j <= v->n);
|
||||
k = v->pos[j];
|
||||
xassert(0 <= k && k <= v->nnz);
|
||||
return (k == 0 ? 0.0 : v->val[k]);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_set_vj - set/change component of sparse vector
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void spv_set_vj(SPV *v, int j, double val);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_set_vj assigns val to j-th component of a sparse
|
||||
* vector specified by the parameter v. */
|
||||
|
||||
void spv_set_vj(SPV *v, int j, double val)
|
||||
{ int k;
|
||||
xassert(1 <= j && j <= v->n);
|
||||
k = v->pos[j];
|
||||
if (val == 0.0)
|
||||
{ if (k != 0)
|
||||
{ /* remove j-th component */
|
||||
v->pos[j] = 0;
|
||||
if (k < v->nnz)
|
||||
{ v->pos[v->ind[v->nnz]] = k;
|
||||
v->ind[k] = v->ind[v->nnz];
|
||||
v->val[k] = v->val[v->nnz];
|
||||
}
|
||||
v->nnz--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ if (k == 0)
|
||||
{ /* create j-th component */
|
||||
k = ++(v->nnz);
|
||||
v->pos[j] = k;
|
||||
v->ind[k] = j;
|
||||
}
|
||||
v->val[k] = val;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_clear_vec - set all components of sparse vector to zero
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void spv_clear_vec(SPV *v);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_clear_vec sets all components of a sparse vector
|
||||
* specified by the parameter v to zero. */
|
||||
|
||||
void spv_clear_vec(SPV *v)
|
||||
{ int k;
|
||||
for (k = 1; k <= v->nnz; k++)
|
||||
v->pos[v->ind[k]] = 0;
|
||||
v->nnz = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_clean_vec - remove zero or small components from sparse vector
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void spv_clean_vec(SPV *v, double eps);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_clean_vec removes zero components and components
|
||||
* whose magnitude is less than eps from a sparse vector specified by
|
||||
* the parameter v. If eps is 0.0, only zero components are removed. */
|
||||
|
||||
void spv_clean_vec(SPV *v, double eps)
|
||||
{ int k, nnz;
|
||||
nnz = 0;
|
||||
for (k = 1; k <= v->nnz; k++)
|
||||
{ if (fabs(v->val[k]) == 0.0 || fabs(v->val[k]) < eps)
|
||||
{ /* remove component */
|
||||
v->pos[v->ind[k]] = 0;
|
||||
}
|
||||
else
|
||||
{ /* keep component */
|
||||
nnz++;
|
||||
v->pos[v->ind[k]] = nnz;
|
||||
v->ind[nnz] = v->ind[k];
|
||||
v->val[nnz] = v->val[k];
|
||||
}
|
||||
}
|
||||
v->nnz = nnz;
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_copy_vec - copy sparse vector (x := y)
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void spv_copy_vec(SPV *x, SPV *y);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_copy_vec copies a sparse vector specified by the
|
||||
* parameter y to a sparse vector specified by the parameter x. */
|
||||
|
||||
void spv_copy_vec(SPV *x, SPV *y)
|
||||
{ int j;
|
||||
xassert(x != y);
|
||||
xassert(x->n == y->n);
|
||||
spv_clear_vec(x);
|
||||
x->nnz = y->nnz;
|
||||
memcpy(&x->ind[1], &y->ind[1], x->nnz * sizeof(int));
|
||||
memcpy(&x->val[1], &y->val[1], x->nnz * sizeof(double));
|
||||
for (j = 1; j <= x->nnz; j++)
|
||||
x->pos[x->ind[j]] = j;
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_linear_comb - compute linear combination (x := x + a * y)
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void spv_linear_comb(SPV *x, double a, SPV *y);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_linear_comb computes the linear combination
|
||||
*
|
||||
* x := x + a * y,
|
||||
*
|
||||
* where x and y are sparse vectors, a is a scalar. */
|
||||
|
||||
void spv_linear_comb(SPV *x, double a, SPV *y)
|
||||
{ int j, k;
|
||||
double xj, yj;
|
||||
xassert(x != y);
|
||||
xassert(x->n == y->n);
|
||||
for (k = 1; k <= y->nnz; k++)
|
||||
{ j = y->ind[k];
|
||||
xj = spv_get_vj(x, j);
|
||||
yj = y->val[k];
|
||||
spv_set_vj(x, j, xj + a * yj);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NAME
|
||||
*
|
||||
* spv_delete_vec - delete sparse vector
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* #include "glpios.h"
|
||||
* void spv_delete_vec(SPV *v);
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* The routine spv_delete_vec deletes a sparse vector specified by the
|
||||
* parameter v freeing all the memory allocated to this object. */
|
||||
|
||||
void spv_delete_vec(SPV *v)
|
||||
{ /* delete sparse vector */
|
||||
xfree(v->pos);
|
||||
xfree(v->ind);
|
||||
xfree(v->val);
|
||||
xfree(v);
|
||||
return;
|
||||
}
|
||||
|
||||
/* eof */
|
||||
@@ -0,0 +1,81 @@
|
||||
/* spv.h (operations on sparse vectors) */
|
||||
|
||||
/***********************************************************************
|
||||
* This code is part of GLPK (GNU Linear Programming Kit).
|
||||
* Copyright (C) 2007-2017 Free Software Foundation, Inc.
|
||||
* Written by Andrew Makhorin <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/>.
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SPV_H
|
||||
#define SPV_H
|
||||
|
||||
typedef struct SPV SPV;
|
||||
|
||||
struct SPV
|
||||
{ /* sparse vector v = (v[j]) */
|
||||
int n;
|
||||
/* dimension, n >= 0 */
|
||||
int nnz;
|
||||
/* number of non-zero components, 0 <= nnz <= n */
|
||||
int *pos; /* int pos[1+n]; */
|
||||
/* pos[j] = k, 1 <= j <= n, is position of (non-zero) v[j] in the
|
||||
* arrays ind and val, where 1 <= k <= nnz; pos[j] = 0 means that
|
||||
* v[j] is structural zero */
|
||||
int *ind; /* int ind[1+n]; */
|
||||
/* ind[k] = j, 1 <= k <= nnz, is index of v[j] */
|
||||
double *val; /* double val[1+n]; */
|
||||
/* val[k], 1 <= k <= nnz, is a numeric value of v[j] */
|
||||
};
|
||||
|
||||
#define spv_create_vec _glp_spv_create_vec
|
||||
SPV *spv_create_vec(int n);
|
||||
/* create sparse vector */
|
||||
|
||||
#define spv_check_vec _glp_spv_check_vec
|
||||
void spv_check_vec(SPV *v);
|
||||
/* check that sparse vector has correct representation */
|
||||
|
||||
#define spv_get_vj _glp_spv_get_vj
|
||||
double spv_get_vj(SPV *v, int j);
|
||||
/* retrieve component of sparse vector */
|
||||
|
||||
#define spv_set_vj _glp_spv_set_vj
|
||||
void spv_set_vj(SPV *v, int j, double val);
|
||||
/* set/change component of sparse vector */
|
||||
|
||||
#define spv_clear_vec _glp_spv_clear_vec
|
||||
void spv_clear_vec(SPV *v);
|
||||
/* set all components of sparse vector to zero */
|
||||
|
||||
#define spv_clean_vec _glp_spv_clean_vec
|
||||
void spv_clean_vec(SPV *v, double eps);
|
||||
/* remove zero or small components from sparse vector */
|
||||
|
||||
#define spv_copy_vec _glp_spv_copy_vec
|
||||
void spv_copy_vec(SPV *x, SPV *y);
|
||||
/* copy sparse vector (x := y) */
|
||||
|
||||
#define spv_linear_comb _glp_spv_linear_comb
|
||||
void spv_linear_comb(SPV *x, double a, SPV *y);
|
||||
/* compute linear combination (x := x + a * y) */
|
||||
|
||||
#define spv_delete_vec _glp_spv_delete_vec
|
||||
void spv_delete_vec(SPV *v);
|
||||
/* delete sparse vector */
|
||||
|
||||
#endif
|
||||
|
||||
/* eof */
|
||||
Reference in New Issue
Block a user