/* glpapi4.c (glp_call_bbm1) */

/*----------------------------------------------------------------------
-- Copyright (C) 2000, 2001, 2002 Andrew Makhorin <mao@mai2.rcnet.ru>,
--               Department for Applied Informatics, Moscow Aviation
--               Institute, Moscow, Russia. All rights reserved.
--
-- This file is a part of GLPK (GNU Linear Programming Kit).
--
-- 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 2, 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; see the file COPYING. If not, write to the Free
-- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-- 02111-1307, USA.
----------------------------------------------------------------------*/

#include <stddef.h>
#include "glpk.h"
#include "glpbbm.h"
#include "glprsm.h"

#define error print

/*----------------------------------------------------------------------
-- glp_init_bbm1 - initialize parameter block by default values.
--
-- *Synopsis*
--
-- #include "glpk.h"
-- void glp_init_bbm1(struct bbm1 *parm);
--
-- *Description*
--
-- The routine glp_init_bbm1 initializes parameter block passed to the
-- branch-and-bound method by default values. */

void glp_init_bbm1(struct bbm1 *parm)
{     parm->what = 2;
      parm->branch = BB_DRTOM;
      parm->btrack = BB_BESTP;
      parm->tol_int = 1e-6;
      parm->tol_obj = 1e-7;
      parm->meth = 1;
      parm->scale = 1;
      parm->dual = 0;
      parm->steep = 1;
      parm->relax = 1;
      parm->tol_bnd = 1e-8;
      parm->tol_dj = 1e-7;
      parm->tol_piv = 1e-10;
      parm->iter_max = 0;
      parm->round = 1;
      return;
}

/*----------------------------------------------------------------------
-- glp_call_bbm1 - solve MIP problem using branch-and-bound method.
--
-- *Synopsis*
--
-- #include "glpk.h"
-- int glp_call_bbm1(LPI *lpi, struct bbm1 *parm);
--
-- *Description*
--
-- The routine glp_call_bbm1 is an interface to a MIP problem solver
-- based on the branch-and-bound method.
--
-- This routine obtains problem data from the problem object, which the
-- parameter lpi points to, solves the problem, and stores the computed
-- solution back to the problem object.
--
-- The parameter parm is a pointer to the parameter block used by the
-- solver. This block may be initialized by the routine glp_init_bbm1
-- by standard default values. It is allowed to specify NULL, in which
-- case standard default values are used. If the problem is not very
-- hard, standard default values fit for most cases.
--
-- Since many MIP problems may take a long time, the solver reports
-- some visual information about current status of the search. This
-- information is sent to stdout approximately once per second and has
-- the following format:
--
--    +nnn: mip = xxx; lp = yyy (aaa; sss)
--
-- where nnn is total number of simplex iteration, xxx is a value of
-- the objective function that corresponds to the best MIP solution
-- (if this solution has not been found yet, xxx is the text "not found
-- yet"), yyy is a value of the objective function that corresponds to
-- the initial relaxed optimal solution (it is not changed during all
-- the optimization process), aaa is number of subroblems in the active
-- list, sss is number of subproblems which have been solved yet.
--
-- Note that currently this solver uses easy heuristics for branching
-- and backtracking and therefore it is not perfect. Most probably this
-- solver fits for MIP problems that have not so many integer variables
-- (several tens, not more). Of course, hard or very large MIP problems
-- can't be resolved by this solver.
--
-- *Returns*
--
-- The routine glp_call_bbm1 returns one of the following codes:
--
-- 0 - no errors. This case means that the solver has successfully
--     finished solving the problem. (Note, for example, if the problem
--     has no integer feasible solution, the solver returns zero code);
-- 1 - iteration limit exceeded. In this case the solver reports the
--     recently obtained basis solution;
-- 2 - numerical problems with basis matrix. This case means that the
--     solver is not able to solve the problem. */

int glp_call_bbm1(LPI *lpi, struct bbm1 *parm)
{     LP *lp;
      LPSOL *sol;
      struct bbm1 _parm;
      struct rsm1_cp rsm_cp;
      struct spx2 spx_cp;
      struct bbm1_cp bbm_cp;
      int ret, bstat, status, i, j;
      /* if parameter block is not specified, create the dummy one */
      if (parm == NULL)
      {  parm = &_parm;
         glp_init_bbm1(parm);
      }
      /* check control parameters for correctness */
      if (!(0 <= parm->what && parm->what <= 2))
         fault("glp_call_bbm1: what = %d; invalid parameter",
            parm->what);
      if (!(parm->branch == BB_FIRST || parm->branch == BB_LAST ||
            parm->branch == BB_DRTOM))
         fault("glp_call_bbm1: branch = %d; invalid parameter",
            parm->branch);
      if (!(parm->btrack == BB_FIFO || parm->btrack == BB_LIFO ||
            parm->btrack == BB_BESTP))
         fault("glp_call_bbm1: btrack = %d; invalid parameter",
            parm->btrack);
      if (!(0.0 < parm->tol_int && parm->tol_int < 1.0))
         fault("glp_call_bbm1: tol_int = %g; invalid parameter",
            parm->tol_int);
      if (!(0.0 < parm->tol_obj && parm->tol_obj < 1.0))
         fault("glp_call_bbm1: tol_obj = %g; invalid parameter",
            parm->tol_obj);
      if (!(parm->meth == 0 || parm->meth == 1))
         fault("glp_call_bbm1: meth = %d; invalid parameter",
            parm->meth);
      if (!(0.0 < parm->tol_bnd && parm->tol_bnd < 1.0))
         fault("glp_call_bbm1: tol_bnd = %g; invalid parameter",
            parm->tol_bnd);
      if (!(0.0 < parm->tol_dj && parm->tol_dj < 1.0))
         fault("glp_call_bbm1: tol_dj = %g; invalid parameter",
            parm->tol_dj);
      if (!(0.0 < parm->tol_piv && parm->tol_piv < 1.0))
         fault("glp_call_bbm1: tol_piv = %g; invalid parameter",
            parm->tol_piv);
      /* the problem should be of MIP class */
      if (glp_get_num_int(lpi) == 0)
         fault("glp_call_bbm1: problem has no integer variables");
      /* STAGE 1: FIND AN OPTIMAL SOLUTION OF THE LP RELAXATION */
      if (parm->meth == 0)
      {  /* extract LP problem data from the problem object */
         lp = extract_prob(lpi);
         /* create LP solution block */
         sol = create_lpsol(lp->m, lp->n);
         /* prepare control parameters */
         rsm_cp.what = 2; /* optimal solution is required */
         rsm_cp.scale = parm->scale;
         rsm_cp.dual = parm->dual;
         rsm_cp.steep = parm->steep;
         rsm_cp.relax = parm->relax;
         rsm_cp.tol_bnd = parm->tol_bnd;
         rsm_cp.tol_dj = 0.30 * parm->tol_dj;
         rsm_cp.tol_piv = parm->tol_piv;
         rsm_cp.iter_max = 0;
         rsm_cp.round = parm->round;
         /* solve LP problem using the revised simplex method */
         ret = rsm1_driver(lp, sol, &rsm_cp);
         /* store obtained LP solution back to the problem object */
         switch (sol->status)
         {  case '?':
               bstat = '?', status = GLP_UNDEF; break;
            case 'O':
               bstat = 'O', status = GLP_OPT; break;
            case 'F':
               bstat = 'P', status = GLP_FEAS; break;
            case 'I':
               bstat = 'N', status = GLP_INFEAS; break;
            case 'N':
               bstat = 'N', status = GLP_NOFEAS; break;
            case 'U':
               bstat = 'P', status = GLP_UNBND; break;
            default:
               insist(sol->status != sol->status);
         }
         glp_put_soln_info(lpi, bstat, status, sol->objval);
         for (i = 1; i <= sol->m; i++)
            glp_put_row_soln(lpi, i, sol->tagx[i], sol->valx[i],
               sol->dx[i]);
         for (j = 1; j <= sol->n; j++)
            glp_put_col_soln(lpi, j, sol->tagx[sol->m+j],
               sol->valx[sol->m+j], sol->dx[sol->m+j]);
      }
      else
      {  /* prepare control parameters */
         glp_init_spx2(&spx_cp);
         spx_cp.scale = parm->scale;
         spx_cp.tol_bnd = parm->tol_bnd;
         spx_cp.tol_dj = 0.30 * parm->tol_dj;
         spx_cp.tol_piv = parm->tol_piv;
         spx_cp.round = parm->round;
         /* solve LP problem using the primal simplex method */
         ret = glp_simplex2(lpi, &spx_cp);
         /* extract LP problem data from the problem object */
         lp = extract_prob(lpi);
         /* create LP solution block */
         sol = create_lpsol(lp->m, lp->n);
         sol->mipsol = 0;
         switch (glp_get_status(lpi))
         {  case GLP_UNDEF:
               sol->status = '?'; break;
            case GLP_OPT:
               sol->status = 'O'; break;
            case GLP_FEAS:
               sol->status = 'F'; break;
            case GLP_INFEAS:
               sol->status = 'I'; break;
            case GLP_NOFEAS:
               sol->status = 'N'; break;
            case GLP_UNBND:
               sol->status = 'U'; break;
            default: insist(lpi != lpi);
         }
         sol->objval = glp_get_obj_val(lpi);
         for (i = 1; i <= sol->m; i++)
            glp_get_row_soln(lpi, i, &sol->tagx[i], &sol->valx[i],
               &sol->dx[i]);
         for (j = 1; j <= sol->n; j++)
            glp_get_col_soln(lpi, j, &sol->tagx[sol->m+j],
               &sol->valx[sol->m+j], &sol->dx[sol->m+j]);
      }
      /* check if the LP relaxation has been solved successfully */
      if (!(ret == 0 && sol->status == 'O'))
      {  error("glp_call_bbm1: integer optimization not possible");
         goto done;
      }
      /* STAGE 2: SOLVE MIP PROBLEM USING BRANCH-AND-BOUND METHOD */
      /* prepare control parameters */
      bbm_cp.what = parm->what;
      bbm_cp.branch = parm->branch;
      bbm_cp.btrack = parm->btrack;
      bbm_cp.tol_int = parm->tol_int;
      bbm_cp.tol_obj = parm->tol_obj;
      bbm_cp.steep = parm->steep;
      bbm_cp.relax = parm->relax;
      bbm_cp.tol_bnd = parm->tol_bnd;
      bbm_cp.tol_dj = parm->tol_dj;
      bbm_cp.tol_piv = parm->tol_piv;
      bbm_cp.iter_max = parm->iter_max;
      bbm_cp.round = parm->round;
      /* solve MIP problem using branch-and-bound method */
      ret = bbm1_driver(lp, sol, &bbm_cp);
      /* store obtained MIP solution back to the problem object */
      if (!sol->mipsol) goto done;
      switch (sol->status)
      {  case 'O':
            bstat = '?', status = GLP_INTOPT; break;
         case 'F':
            bstat = '?', status = GLP_INTSOL; break;
         case 'I':
            bstat = '?', status = GLP_DISINT; break;
         case 'N':
            bstat = '?', status = GLP_NOINT; break;
         default:
            insist(sol->status != sol->status);
      }
      glp_put_soln_info(lpi, bstat, status, sol->objval);
      for (i = 1; i <= sol->m; i++)
         glp_put_row_soln(lpi, i, sol->tagx[i], sol->valx[i],
            sol->dx[i]);
      for (j = 1; j <= sol->n; j++)
         glp_put_col_soln(lpi, j, sol->tagx[sol->m+j],
            sol->valx[sol->m+j], sol->dx[sol->m+j]);
done: /* free working data structures */
      delete_lp(lp);
      delete_lpsol(sol);
      /* return to the application program */
      return ret;
}

/* eof */
