deba@458: /* -*- mode: C++; indent-tabs-mode: nil; -*-
deba@458:  *
deba@458:  * This file is a part of LEMON, a generic C++ optimization library.
deba@458:  *
deba@551:  * Copyright (C) 2003-2009
deba@458:  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
deba@458:  * (Egervary Research Group on Combinatorial Optimization, EGRES).
deba@458:  *
deba@458:  * Permission to use, modify and distribute this software is granted
deba@458:  * provided that this copyright notice appears in all copies. For
deba@458:  * precise terms see the accompanying LICENSE file.
deba@458:  *
deba@458:  * This software is provided "AS IS" with no warranty of any kind,
deba@458:  * express or implied, and with no claim as to its suitability for any
deba@458:  * purpose.
deba@458:  *
deba@458:  */
deba@458: 
deba@458: #include <sstream>
deba@458: #include <lemon/lp_skeleton.h>
deba@458: #include "test_tools.h"
deba@458: #include <lemon/tolerance.h>
deba@458: 
deba@458: #include <lemon/config.h>
deba@458: 
ladanyi@627: #ifdef LEMON_HAVE_GLPK
alpar@461: #include <lemon/glpk.h>
deba@458: #endif
deba@458: 
ladanyi@627: #ifdef LEMON_HAVE_CPLEX
alpar@461: #include <lemon/cplex.h>
deba@458: #endif
deba@458: 
ladanyi@627: #ifdef LEMON_HAVE_SOPLEX
alpar@461: #include <lemon/soplex.h>
deba@458: #endif
deba@458: 
ladanyi@627: #ifdef LEMON_HAVE_CLP
alpar@461: #include <lemon/clp.h>
deba@459: #endif
deba@459: 
deba@458: using namespace lemon;
deba@458: 
deba@459: void lpTest(LpSolver& lp)
deba@458: {
deba@458: 
deba@459:   typedef LpSolver LP;
deba@458: 
deba@458:   std::vector<LP::Col> x(10);
deba@458:   //  for(int i=0;i<10;i++) x.push_back(lp.addCol());
deba@458:   lp.addColSet(x);
deba@458:   lp.colLowerBound(x,1);
deba@458:   lp.colUpperBound(x,1);
deba@458:   lp.colBounds(x,1,2);
deba@458: 
deba@458:   std::vector<LP::Col> y(10);
deba@458:   lp.addColSet(y);
deba@458: 
deba@458:   lp.colLowerBound(y,1);
deba@458:   lp.colUpperBound(y,1);
deba@458:   lp.colBounds(y,1,2);
deba@458: 
deba@458:   std::map<int,LP::Col> z;
deba@458: 
deba@458:   z.insert(std::make_pair(12,INVALID));
deba@458:   z.insert(std::make_pair(2,INVALID));
deba@458:   z.insert(std::make_pair(7,INVALID));
deba@458:   z.insert(std::make_pair(5,INVALID));
deba@458: 
deba@458:   lp.addColSet(z);
deba@458: 
deba@458:   lp.colLowerBound(z,1);
deba@458:   lp.colUpperBound(z,1);
deba@458:   lp.colBounds(z,1,2);
deba@458: 
deba@458:   {
deba@458:     LP::Expr e,f,g;
deba@458:     LP::Col p1,p2,p3,p4,p5;
deba@458:     LP::Constr c;
deba@458: 
deba@458:     p1=lp.addCol();
deba@458:     p2=lp.addCol();
deba@458:     p3=lp.addCol();
deba@458:     p4=lp.addCol();
deba@458:     p5=lp.addCol();
deba@458: 
deba@458:     e[p1]=2;
deba@459:     *e=12;
deba@458:     e[p1]+=2;
deba@459:     *e+=12;
deba@458:     e[p1]-=2;
deba@459:     *e-=12;
deba@458: 
deba@458:     e=2;
deba@458:     e=2.2;
deba@458:     e=p1;
deba@458:     e=f;
deba@458: 
deba@458:     e+=2;
deba@458:     e+=2.2;
deba@458:     e+=p1;
deba@458:     e+=f;
deba@458: 
deba@458:     e-=2;
deba@458:     e-=2.2;
deba@458:     e-=p1;
deba@458:     e-=f;
deba@458: 
deba@458:     e*=2;
deba@458:     e*=2.2;
deba@458:     e/=2;
deba@458:     e/=2.2;
deba@458: 
deba@458:     e=((p1+p2)+(p1-p2)+(p1+12)+(12+p1)+(p1-12)+(12-p1)+
deba@458:        (f+12)+(12+f)+(p1+f)+(f+p1)+(f+g)+
deba@458:        (f-12)+(12-f)+(p1-f)+(f-p1)+(f-g)+
deba@458:        2.2*f+f*2.2+f/2.2+
deba@458:        2*f+f*2+f/2+
deba@458:        2.2*p1+p1*2.2+p1/2.2+
deba@458:        2*p1+p1*2+p1/2
deba@458:        );
deba@458: 
deba@458: 
deba@458:     c = (e  <= f  );
deba@458:     c = (e  <= 2.2);
deba@458:     c = (e  <= 2  );
deba@458:     c = (e  <= p1 );
deba@458:     c = (2.2<= f  );
deba@458:     c = (2  <= f  );
deba@458:     c = (p1 <= f  );
deba@458:     c = (p1 <= p2 );
deba@458:     c = (p1 <= 2.2);
deba@458:     c = (p1 <= 2  );
deba@458:     c = (2.2<= p2 );
deba@458:     c = (2  <= p2 );
deba@458: 
deba@458:     c = (e  >= f  );
deba@458:     c = (e  >= 2.2);
deba@458:     c = (e  >= 2  );
deba@458:     c = (e  >= p1 );
deba@458:     c = (2.2>= f  );
deba@458:     c = (2  >= f  );
deba@458:     c = (p1 >= f  );
deba@458:     c = (p1 >= p2 );
deba@458:     c = (p1 >= 2.2);
deba@458:     c = (p1 >= 2  );
deba@458:     c = (2.2>= p2 );
deba@458:     c = (2  >= p2 );
deba@458: 
deba@458:     c = (e  == f  );
deba@458:     c = (e  == 2.2);
deba@458:     c = (e  == 2  );
deba@458:     c = (e  == p1 );
deba@458:     c = (2.2== f  );
deba@458:     c = (2  == f  );
deba@458:     c = (p1 == f  );
deba@458:     //c = (p1 == p2 );
deba@458:     c = (p1 == 2.2);
deba@458:     c = (p1 == 2  );
deba@458:     c = (2.2== p2 );
deba@458:     c = (2  == p2 );
deba@458: 
alpar@460:     c = ((2 <= e) <= 3);
alpar@460:     c = ((2 <= p1) <= 3);
deba@458: 
alpar@460:     c = ((2 >= e) >= 3);
alpar@460:     c = ((2 >= p1) >= 3);
deba@458: 
deba@458:     e[x[3]]=2;
deba@458:     e[x[3]]=4;
deba@458:     e[x[3]]=1;
deba@459:     *e=12;
deba@458: 
deba@459:     lp.addRow(-LP::INF,e,23);
deba@459:     lp.addRow(-LP::INF,3.0*(x[1]+x[2]/2)-x[3],23);
deba@459:     lp.addRow(-LP::INF,3.0*(x[1]+x[2]*2-5*x[3]+12-x[4]/3)+2*x[4]-4,23);
deba@458: 
deba@458:     lp.addRow(x[1]+x[3]<=x[5]-3);
alpar@460:     lp.addRow((-7<=x[1]+x[3]-12)<=3);
deba@458:     lp.addRow(x[1]<=x[5]);
deba@458: 
deba@458:     std::ostringstream buf;
deba@458: 
deba@458: 
deba@458:     e=((p1+p2)+(p1-0.99*p2));
deba@458:     //e.prettyPrint(std::cout);
deba@458:     //(e<=2).prettyPrint(std::cout);
deba@458:     double tolerance=0.001;
deba@458:     e.simplify(tolerance);
deba@458:     buf << "Coeff. of p2 should be 0.01";
deba@458:     check(e[p2]>0, buf.str());
deba@458: 
deba@458:     tolerance=0.02;
deba@458:     e.simplify(tolerance);
deba@458:     buf << "Coeff. of p2 should be 0";
deba@459:     check(const_cast<const LpSolver::Expr&>(e)[p2]==0, buf.str());
deba@458: 
alpar@540:     //Test for clone/new
alpar@540:     LP* lpnew = lp.newSolver();
alpar@540:     LP* lpclone = lp.cloneSolver();
alpar@540:     delete lpnew;
alpar@540:     delete lpclone;
deba@458: 
deba@458:   }
deba@458: 
deba@458:   {
deba@458:     LP::DualExpr e,f,g;
deba@458:     LP::Row p1 = INVALID, p2 = INVALID, p3 = INVALID,
deba@458:       p4 = INVALID, p5 = INVALID;
deba@458: 
deba@458:     e[p1]=2;
deba@458:     e[p1]+=2;
deba@458:     e[p1]-=2;
deba@458: 
deba@458:     e=p1;
deba@458:     e=f;
deba@458: 
deba@458:     e+=p1;
deba@458:     e+=f;
deba@458: 
deba@458:     e-=p1;
deba@458:     e-=f;
deba@458: 
deba@458:     e*=2;
deba@458:     e*=2.2;
deba@458:     e/=2;
deba@458:     e/=2.2;
deba@458: 
deba@458:     e=((p1+p2)+(p1-p2)+
deba@458:        (p1+f)+(f+p1)+(f+g)+
deba@458:        (p1-f)+(f-p1)+(f-g)+
deba@458:        2.2*f+f*2.2+f/2.2+
deba@458:        2*f+f*2+f/2+
deba@458:        2.2*p1+p1*2.2+p1/2.2+
deba@458:        2*p1+p1*2+p1/2
deba@458:        );
deba@458:   }
deba@458: 
deba@458: }
deba@458: 
deba@459: void solveAndCheck(LpSolver& lp, LpSolver::ProblemType stat,
deba@458:                    double exp_opt) {
deba@458:   using std::string;
deba@458:   lp.solve();
deba@459: 
deba@458:   std::ostringstream buf;
deba@459:   buf << "PrimalType should be: " << int(stat) << int(lp.primalType());
deba@458: 
deba@459:   check(lp.primalType()==stat, buf.str());
deba@458: 
deba@459:   if (stat ==  LpSolver::OPTIMAL) {
deba@458:     std::ostringstream sbuf;
alpar@540:     sbuf << "Wrong optimal value (" << lp.primal() <<") with "
alpar@540:          << lp.solverName() <<"\n     the right optimum is " << exp_opt;
deba@459:     check(std::abs(lp.primal()-exp_opt) < 1e-3, sbuf.str());
deba@458:   }
deba@458: }
deba@458: 
deba@459: void aTest(LpSolver & lp)
deba@458: {
deba@459:   typedef LpSolver LP;
deba@458: 
deba@458:  //The following example is very simple
deba@458: 
deba@459:   typedef LpSolver::Row Row;
deba@459:   typedef LpSolver::Col Col;
deba@458: 
deba@458: 
deba@458:   Col x1 = lp.addCol();
deba@458:   Col x2 = lp.addCol();
deba@458: 
deba@458: 
deba@458:   //Constraints
deba@459:   Row upright=lp.addRow(x1+2*x2 <=1);
deba@458:   lp.addRow(x1+x2 >=-1);
deba@458:   lp.addRow(x1-x2 <=1);
deba@458:   lp.addRow(x1-x2 >=-1);
deba@458:   //Nonnegativity of the variables
deba@458:   lp.colLowerBound(x1, 0);
deba@458:   lp.colLowerBound(x2, 0);
deba@458:   //Objective function
deba@458:   lp.obj(x1+x2);
deba@458: 
deba@459:   lp.sense(lp.MAX);
deba@458: 
deba@458:   //Testing the problem retrieving routines
deba@458:   check(lp.objCoeff(x1)==1,"First term should be 1 in the obj function!");
deba@459:   check(lp.sense() == lp.MAX,"This is a maximization!");
deba@458:   check(lp.coeff(upright,x1)==1,"The coefficient in question is 1!");
deba@459:   check(lp.colLowerBound(x1)==0,
deba@459:         "The lower bound for variable x1 should be 0.");
deba@459:   check(lp.colUpperBound(x1)==LpSolver::INF,
deba@459:         "The upper bound for variable x1 should be infty.");
deba@459:   check(lp.rowLowerBound(upright) == -LpSolver::INF,
deba@459:         "The lower bound for the first row should be -infty.");
deba@459:   check(lp.rowUpperBound(upright)==1,
deba@459:         "The upper bound for the first row should be 1.");
deba@459:   LpSolver::Expr e = lp.row(upright);
deba@459:   check(e[x1] == 1, "The first coefficient should 1.");
deba@459:   check(e[x2] == 2, "The second coefficient should 1.");
deba@458: 
deba@459:   lp.row(upright, x1+x2 <=1);
deba@459:   e = lp.row(upright);
deba@459:   check(e[x1] == 1, "The first coefficient should 1.");
deba@459:   check(e[x2] == 1, "The second coefficient should 1.");
deba@459: 
deba@459:   LpSolver::DualExpr de = lp.col(x1);
deba@458:   check(  de[upright] == 1, "The first coefficient should 1.");
deba@458: 
deba@459:   LpSolver* clp = lp.cloneSolver();
deba@458: 
deba@458:   //Testing the problem retrieving routines
deba@458:   check(clp->objCoeff(x1)==1,"First term should be 1 in the obj function!");
deba@459:   check(clp->sense() == clp->MAX,"This is a maximization!");
deba@458:   check(clp->coeff(upright,x1)==1,"The coefficient in question is 1!");
deba@458:   //  std::cout<<lp.colLowerBound(x1)<<std::endl;
deba@459:   check(clp->colLowerBound(x1)==0,
deba@459:         "The lower bound for variable x1 should be 0.");
deba@459:   check(clp->colUpperBound(x1)==LpSolver::INF,
deba@459:         "The upper bound for variable x1 should be infty.");
deba@458: 
deba@459:   check(lp.rowLowerBound(upright)==-LpSolver::INF,
deba@459:         "The lower bound for the first row should be -infty.");
deba@459:   check(lp.rowUpperBound(upright)==1,
deba@459:         "The upper bound for the first row should be 1.");
deba@458:   e = clp->row(upright);
deba@459:   check(e[x1] == 1, "The first coefficient should 1.");
deba@459:   check(e[x2] == 1, "The second coefficient should 1.");
deba@458: 
deba@458:   de = clp->col(x1);
deba@459:   check(de[upright] == 1, "The first coefficient should 1.");
deba@458: 
deba@458:   delete clp;
deba@458: 
deba@458:   //Maximization of x1+x2
deba@458:   //over the triangle with vertices (0,0) (0,1) (1,0)
deba@458:   double expected_opt=1;
deba@459:   solveAndCheck(lp, LpSolver::OPTIMAL, expected_opt);
deba@458: 
deba@458:   //Minimization
deba@459:   lp.sense(lp.MIN);
deba@458:   expected_opt=0;
deba@459:   solveAndCheck(lp, LpSolver::OPTIMAL, expected_opt);
deba@458: 
deba@458:   //Vertex (-1,0) instead of (0,0)
deba@459:   lp.colLowerBound(x1, -LpSolver::INF);
deba@458:   expected_opt=-1;
deba@459:   solveAndCheck(lp, LpSolver::OPTIMAL, expected_opt);
deba@458: 
deba@458:   //Erase one constraint and return to maximization
deba@459:   lp.erase(upright);
deba@459:   lp.sense(lp.MAX);
deba@459:   expected_opt=LpSolver::INF;
deba@459:   solveAndCheck(lp, LpSolver::UNBOUNDED, expected_opt);
deba@458: 
deba@458:   //Infeasibilty
deba@458:   lp.addRow(x1+x2 <=-2);
deba@459:   solveAndCheck(lp, LpSolver::INFEASIBLE, expected_opt);
deba@458: 
deba@458: }
deba@458: 
alpar@540: template<class LP>
alpar@540: void cloneTest()
alpar@540: {
alpar@540:   //Test for clone/new
deba@551: 
alpar@540:   LP* lp = new LP();
alpar@540:   LP* lpnew = lp->newSolver();
alpar@540:   LP* lpclone = lp->cloneSolver();
alpar@540:   delete lp;
alpar@540:   delete lpnew;
alpar@540:   delete lpclone;
alpar@540: }
alpar@540: 
deba@458: int main()
deba@458: {
deba@458:   LpSkeleton lp_skel;
deba@458:   lpTest(lp_skel);
deba@458: 
ladanyi@627: #ifdef LEMON_HAVE_GLPK
deba@459:   {
alpar@462:     GlpkLp lp_glpk1,lp_glpk2;
deba@459:     lpTest(lp_glpk1);
deba@459:     aTest(lp_glpk2);
alpar@540:     cloneTest<GlpkLp>();
deba@459:   }
deba@458: #endif
deba@458: 
ladanyi@627: #ifdef LEMON_HAVE_CPLEX
deba@459:   try {
alpar@462:     CplexLp lp_cplex1,lp_cplex2;
deba@459:     lpTest(lp_cplex1);
deba@459:     aTest(lp_cplex2);
deba@551:     cloneTest<CplexLp>();
deba@459:   } catch (CplexEnv::LicenseError& error) {
deba@459:     check(false, error.what());
deba@459:   }
deba@458: #endif
deba@458: 
ladanyi@627: #ifdef LEMON_HAVE_SOPLEX
deba@459:   {
alpar@462:     SoplexLp lp_soplex1,lp_soplex2;
deba@459:     lpTest(lp_soplex1);
deba@459:     aTest(lp_soplex2);
alpar@540:     cloneTest<SoplexLp>();
deba@459:   }
deba@459: #endif
deba@459: 
ladanyi@627: #ifdef LEMON_HAVE_CLP
deba@459:   {
alpar@462:     ClpLp lp_clp1,lp_clp2;
deba@459:     lpTest(lp_clp1);
deba@459:     aTest(lp_clp2);
alpar@540:     cloneTest<ClpLp>();
deba@459:   }
deba@458: #endif
deba@458: 
deba@458:   return 0;
deba@458: }