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