You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
5.2 KiB
226 lines
5.2 KiB
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include "lp.h"
|
|
#include "branch_and_cut.h"
|
|
#include "util.h"
|
|
|
|
int BNC_NODE_COUNT = 0;
|
|
|
|
static int BNC_solve_node(struct BNC *bnc, int depth);
|
|
|
|
static int BNC_branch_node(struct BNC *bnc, double *x, int depth);
|
|
|
|
static int BNC_is_integral(double *x, int num_cols);
|
|
|
|
static int BNC_find_best_branching_var(double *x, int num_cols);
|
|
|
|
int BNC_init(struct BNC *bnc)
|
|
{
|
|
int rval = 0;
|
|
|
|
bnc->lp = 0;
|
|
bnc->problem_data = 0;
|
|
bnc->problem_init_lp = 0;
|
|
bnc->problem_add_cutting_planes = 0;
|
|
|
|
bnc->best_x = 0;
|
|
bnc->best_obj_val = 0;
|
|
|
|
bnc->lp = (struct LP *) malloc(sizeof(struct LP));
|
|
abort_if(!bnc->lp, "could not allocate bnc->lp");
|
|
|
|
CLEANUP:
|
|
return rval;
|
|
}
|
|
|
|
void BNC_free(struct BNC *bnc)
|
|
{
|
|
if (!bnc) return;
|
|
if (bnc->lp)
|
|
{
|
|
LP_free(bnc->lp);
|
|
free(bnc->lp);
|
|
}
|
|
if (bnc->best_x) free(bnc->best_x);
|
|
}
|
|
|
|
int BNC_init_lp(struct BNC *bnc)
|
|
{
|
|
int rval = 0;
|
|
|
|
rval = LP_open(bnc->lp);
|
|
abort_if(rval, "LP_open failed");
|
|
|
|
rval = LP_create(bnc->lp, "subtour");
|
|
abort_if(rval, "LP_create failed");
|
|
|
|
rval = bnc->problem_init_lp(bnc->lp, bnc->problem_data);
|
|
abort_if(rval, "problem_init_lp failed");
|
|
|
|
rval = LP_write(bnc->lp, "subtour.lp");
|
|
abort_if(rval, "LP_write failed");
|
|
|
|
CLEANUP:
|
|
return rval;
|
|
}
|
|
|
|
int BNC_solve(struct BNC *bnc)
|
|
{
|
|
return BNC_solve_node(bnc, 1);
|
|
}
|
|
|
|
static int BNC_solve_node(struct BNC *bnc, int depth)
|
|
{
|
|
struct LP *lp = bnc->lp;
|
|
double *best_val = &bnc->best_obj_val;
|
|
|
|
BNC_NODE_COUNT++;
|
|
|
|
int rval = 0;
|
|
double *x = (double *) NULL;
|
|
|
|
log_debug("Optimizing...\n");
|
|
|
|
int is_infeasible;
|
|
rval = LP_optimize(lp, &is_infeasible);
|
|
abort_if(rval, "LP_optimize failed\n");
|
|
|
|
if (is_infeasible)
|
|
{
|
|
log_debug("Branch pruned by infeasibility.\n");
|
|
goto CLEANUP;
|
|
}
|
|
|
|
double objval;
|
|
rval = LP_get_obj_val(lp, &objval);
|
|
abort_if(rval, "LP_get_obj_val failed\n");
|
|
|
|
log_debug(" obj value = %.2f\n", objval);
|
|
|
|
if (objval > *best_val + LP_EPSILON)
|
|
{
|
|
|
|
|
|
log_debug("Branch pruned by bound (%.2lf > %.2lf).\n", objval,
|
|
*best_val);
|
|
rval = 0;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
int num_cols = LP_get_num_cols(lp);
|
|
|
|
x = (double *) malloc(num_cols * sizeof(double));
|
|
abort_if(!x, "could not allocate x");
|
|
|
|
rval = LP_get_x(lp, x);
|
|
abort_if(rval, "LP_get_x failed");
|
|
|
|
if (bnc->problem_add_cutting_planes)
|
|
{
|
|
rval = bnc->problem_add_cutting_planes(lp, bnc->problem_data);
|
|
abort_if(rval, "problem_add_cutting_planes failed");
|
|
}
|
|
|
|
rval = LP_optimize(lp, &is_infeasible);
|
|
abort_if(rval, "LP_optimize failed\n");
|
|
|
|
rval = LP_get_obj_val(lp, &objval);
|
|
abort_if(rval, "LP_get_obj_val failed");
|
|
|
|
rval = LP_get_x(lp, x);
|
|
abort_if(rval, "LP_get_x failed");
|
|
|
|
if (BNC_is_integral(x, num_cols))
|
|
{
|
|
log_debug(" solution is integral\n");
|
|
|
|
if (objval + LP_EPSILON < *best_val)
|
|
{
|
|
if (bnc->best_x) free(bnc->best_x);
|
|
*best_val = objval;
|
|
bnc->best_x = x;
|
|
x = 0;
|
|
|
|
log_info("Found a better integral solution:\n");
|
|
log_info(" obj val = %.2lf **\n", objval);
|
|
|
|
if (bnc->problem_solution_found)
|
|
bnc->problem_solution_found(bnc->problem_data, bnc->best_x);
|
|
}
|
|
} else
|
|
{
|
|
log_debug(" solution is fractional\n");
|
|
rval = BNC_branch_node(bnc, x, depth);
|
|
abort_if(rval, "BNC_branch_node failed");
|
|
}
|
|
|
|
CLEANUP:
|
|
if (x) free(x);
|
|
return rval;
|
|
}
|
|
|
|
static int BNC_branch_node(struct BNC *bnc, double *x, int depth)
|
|
{
|
|
int rval = 0;
|
|
|
|
struct LP *lp = bnc->lp;
|
|
|
|
int num_cols = LP_get_num_cols(lp);
|
|
int best_branch_var = BNC_find_best_branching_var(x, num_cols);
|
|
|
|
log_debug("Branching on variable x%d = %.6lf (depth %d)...\n",
|
|
best_branch_var, x[best_branch_var], depth);
|
|
|
|
log_debug("Fixing variable x%d to one...\n", best_branch_var);
|
|
rval = LP_change_bound(lp, best_branch_var, 'L', 1.0);
|
|
abort_if(rval, "LP_change_bound failed");
|
|
|
|
rval = BNC_solve_node(bnc, depth + 1);
|
|
abort_if(rval, "BNC_solve_node failed");
|
|
|
|
rval = LP_change_bound(lp, best_branch_var, 'L', 0.0);
|
|
abort_if(rval, "LP_change_bound failed");
|
|
|
|
log_debug("Fixing variable x%d to zero...\n", best_branch_var);
|
|
rval = LP_change_bound(lp, best_branch_var, 'U', 0.0);
|
|
abort_if(rval, "LP_change_bound failed");
|
|
|
|
rval = BNC_solve_node(bnc, depth + 1);
|
|
abort_if(rval, "BNC_solve_node failed");
|
|
|
|
rval = LP_change_bound(lp, best_branch_var, 'U', 1.0);
|
|
abort_if(rval, "LP_change_bound failed");
|
|
|
|
log_debug("Finished branching on variable %d\n", best_branch_var);
|
|
|
|
CLEANUP:
|
|
return rval;
|
|
}
|
|
|
|
static int BNC_is_integral(double *x, int num_cols)
|
|
{
|
|
for (int i = 0; i < num_cols; i++)
|
|
if (x[i] > LP_EPSILON && x[i] < 1.0 - LP_EPSILON)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int BNC_find_best_branching_var(double *x, int num_cols)
|
|
{
|
|
int best_index = 0;
|
|
double best_index_frac = 1.0;
|
|
|
|
for (int i = 0; i < num_cols; i++)
|
|
{
|
|
if (fabs(x[i] - 0.5) < best_index_frac)
|
|
{
|
|
best_index = i;
|
|
best_index_frac = fabs(x[i] - 0.5);
|
|
}
|
|
}
|
|
|
|
return best_index;
|
|
}
|