[opengm] 74/386: Add cplex backend for quadratic solver. Compile learning test if cplex or gurobi is available.

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Wed Aug 31 08:35:07 UTC 2016


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch debian/master
in repository opengm.

commit f174a1494ba1ede6b5a8c83d03e9312483ef7b00
Author: Carsten Haubold <carstenhaubold at googlemail.com>
Date:   Tue Dec 16 17:28:16 2014 +0100

    Add cplex backend for quadratic solver. Compile learning test if cplex or gurobi is available.
---
 include/opengm/learning/solver/CplexBackend.h      | 429 +++++++++++++++++++++
 include/opengm/learning/solver/GurobiBackend.h     |  76 ++--
 .../learning/solver/QuadraticSolverFactory.h       |   4 +
 src/unittest/CMakeLists.txt                        |   9 +-
 4 files changed, 472 insertions(+), 46 deletions(-)

diff --git a/include/opengm/learning/solver/CplexBackend.h b/include/opengm/learning/solver/CplexBackend.h
new file mode 100644
index 0000000..30c7572
--- /dev/null
+++ b/include/opengm/learning/solver/CplexBackend.h
@@ -0,0 +1,429 @@
+#ifndef OPENGM_LEARNING_SOLVER_CPLEX_SOLVER_H__
+#define OPENGM_LEARNING_SOLVER_CPLEX_SOLVER_H__
+
+#ifdef WITH_CPLEX
+
+#include <string>
+#include <vector>
+
+#include <ilcplex/ilocplex.h>
+
+#include "LinearConstraints.h"
+#include "QuadraticObjective.h"
+#include "QuadraticSolverBackend.h"
+#include "Sense.h"
+#include "Solution.h"
+
+namespace opengm {
+namespace learning {
+namespace solver {
+
+/**
+ * Cplex interface to solve the following (integer) quadratic program:
+ *
+ * min  <a,x> + xQx
+ * s.t. Ax  == b
+ *      Cx  <= d
+ *      optionally: x_i \in {0,1} for all i
+ *
+ * Where (A,b) describes all linear equality constraints, (C,d) all linear
+ * inequality constraints and x is the solution vector. a is a real-valued
+ * vector denoting the coefficients of the objective and Q a PSD matrix giving
+ * the quadratic coefficients of the objective.
+ */
+class CplexBackend : public QuadraticSolverBackend {
+
+public:
+
+    struct Parameter {
+
+        Parameter() :
+            mipGap(0.0001),
+            mipFocus(0),
+            numThreads(0),
+            verbose(false) {}
+
+        // The Gurobi relative optimality gap.
+        double mipGap;
+
+        // The Gurobi MIP focus: 0 = balanced, 1 = feasible solutions, 2 =
+        // optimal solution, 3 = bound.
+        unsigned int mipFocus;
+
+        // The number of threads to be used by Gurobi. The default (0) uses all
+        // available CPUs.
+        unsigned int numThreads;
+
+        // Show the gurobi output.
+        bool verbose;
+    };
+
+    CplexBackend(const Parameter& parameter = Parameter());
+
+    virtual ~CplexBackend();
+
+    ///////////////////////////////////
+    // solver backend implementation //
+    ///////////////////////////////////
+
+    void initialize(
+            unsigned int numVariables,
+            VariableType variableType);
+
+    void initialize(
+            unsigned int                                numVariables,
+            VariableType                                defaultVariableType,
+            const std::map<unsigned int, VariableType>& specialVariableTypes);
+
+    void setObjective(const LinearObjective& objective);
+
+    void setObjective(const QuadraticObjective& objective);
+
+    void setConstraints(const LinearConstraints& constraints);
+
+    void addConstraint(const LinearConstraint& constraint);
+
+    bool solve(Solution& solution, double& value, std::string& message);
+
+private:
+
+    //////////////
+    // internal //
+    //////////////
+
+    // set the optimality gap
+    void setMIPGap(double gap);
+
+    // set the mpi focus
+    void setMIPFocus(unsigned int focus);
+
+    // set the number of threads to use
+    void setNumThreads(unsigned int numThreads);
+
+    // create a CPLEX constraint from a linear constraint
+    IloRange createConstraint(const LinearConstraint &constraint);
+
+    /**
+     * Enable solver output.
+     */
+    void setVerbose(bool verbose);
+
+    // size of a and x
+    unsigned int _numVariables;
+
+    // rows in A
+    unsigned int _numEqConstraints;
+
+    // rows in C
+    unsigned int _numIneqConstraints;
+
+    Parameter _parameter;
+
+    // the verbosity of the output
+    int _verbosity;
+
+    // a value by which to scale the objective
+    double _scale;
+
+    // Objective, constraints and cplex environment:
+    IloEnv env_;
+    IloModel model_;
+    IloNumVarArray x_;
+    IloRangeArray c_;
+    IloObjective obj_;
+    IloNumArray sol_;
+    IloCplex cplex_;
+    double constValue_;
+
+    typedef std::vector<IloExtractable> ConstraintVector;
+    ConstraintVector _constraints;
+};
+
+CplexBackend::CplexBackend(const Parameter& parameter) :
+    _parameter(parameter),
+    model_(env_),
+    x_(env_),
+    c_(env_),
+    obj_(env_),
+    sol_(env_)
+{
+    std::cout << "constructing cplex solver" << std::endl;
+}
+
+CplexBackend::~CplexBackend() {
+    std::cout << "destructing cplex solver..." << std::endl;
+}
+
+void
+CplexBackend::initialize(
+        unsigned int numVariables,
+        VariableType variableType) {
+
+    initialize(numVariables, variableType, std::map<unsigned int, VariableType>());
+}
+
+void
+CplexBackend::initialize(
+        unsigned int                                numVariables,
+        VariableType                                defaultVariableType,
+        const std::map<unsigned int, VariableType>& specialVariableTypes) {
+
+    _numVariables = numVariables;
+
+    // delete previous variables
+    x_.clear();
+
+    // add new variables to the model
+    if (defaultVariableType == Binary) {
+        std::cout << "creating " << _numVariables << " binary variables" << std::endl;
+        x_.add(IloNumVarArray(env_, _numVariables, 0, 1, ILOBOOL));
+    } else if (defaultVariableType == Continuous) {
+        std::cout << "creating " << _numVariables << " continuous variables" << std::endl;
+        x_.add(IloNumVarArray(env_, _numVariables, -IloInfinity, IloInfinity));
+    } else if (defaultVariableType == Integer) {
+        x_.add(IloNumVarArray(env_, _numVariables, -IloInfinity, IloInfinity, ILOINT));
+    }
+
+    // TODO: port me!
+//    // handle special variable types
+//    typedef std::map<unsigned int, VariableType>::const_iterator VarTypeIt;
+//    for (VarTypeIt i = specialVariableTypes.begin(); i != specialVariableTypes.end(); i++) {
+
+//        unsigned int v = i->first;
+//        VariableType type = i->second;
+
+//        char t = (type == Binary ? 'B' : (type == Integer ? 'I' : 'C'));
+//        _variables[v].set(GRB_CharAttr_VType, t);
+//    }
+
+    std::cout << "creating " << _numVariables << " ceofficients" << std::endl;
+}
+
+void
+CplexBackend::setObjective(const LinearObjective& objective) {
+
+    setObjective((QuadraticObjective)objective);
+}
+
+void
+CplexBackend::setObjective(const QuadraticObjective& objective) {
+
+    try {
+
+        // set sense of objective
+        if (objective.getSense() == Minimize)
+            obj_ = IloMinimize(env_);
+        else
+            obj_ = IloMaximize(env_);
+
+        // set the constant value of the objective
+        obj_.setConstant(objective.getConstant());
+
+        std::cout << "setting linear coefficients" << std::endl;
+
+        for(size_t i = 0; i < _numVariables; i++)
+        {
+            obj_.setLinearCoef(x_[i], objective.getCoefficients()[i]);
+        }
+
+        // set the quadratic coefficients for all pairs of variables
+        std::cout << "setting quadratic coefficients" << std::endl;
+
+        typedef std::map<std::pair<unsigned int, unsigned int>, double>::const_iterator QuadCoefIt;
+        for (QuadCoefIt i = objective.getQuadraticCoefficients().begin(); i != objective.getQuadraticCoefficients().end(); i++) {
+
+            const std::pair<unsigned int, unsigned int>& variables = i->first;
+            float value = i->second;
+
+            if (value != 0)
+                obj_.setQuadCoef(x_[variables.first], x_[variables.second], value);
+        }
+
+        model_.add(obj_);
+
+    } catch (IloCplex::Exception e) {
+
+        std::cerr << "CPLEX error: " << e.getMessage() << std::endl;
+    }
+}
+
+void
+CplexBackend::setConstraints(const LinearConstraints& constraints) {
+
+    // remove previous constraints
+    for (ConstraintVector::iterator constraint = _constraints.begin(); constraint != _constraints.end(); constraint++)
+        model_.remove(*constraint);
+    _constraints.clear();
+
+    // allocate memory for new constraints
+    _constraints.reserve(constraints.size());
+
+    try {
+        std::cout << "setting " << constraints.size() << " constraints" << std::endl;
+
+        IloExtractableArray cplex_constraints(env_);
+        for (LinearConstraints::const_iterator constraint = constraints.begin(); constraint != constraints.end(); constraint++) {
+            IloRange linearConstraint = createConstraint(*constraint);
+            _constraints.push_back(linearConstraint);
+            cplex_constraints.add(linearConstraint);
+        }
+
+        // add all constraints as batch to the model
+        model_.add(cplex_constraints);
+
+    } catch (IloCplex::Exception e) {
+
+        std::cerr << "error: " << e.getMessage() << std::endl;
+    }
+}
+
+void
+CplexBackend::addConstraint(const LinearConstraint& constraint) {
+
+    try {
+        std::cout << "adding a constraint" << std::endl;
+
+        // add to the model
+        _constraints.push_back(model_.add(createConstraint(constraint)));
+
+    } catch (IloCplex::Exception e) {
+
+        std::cerr << "error: " << e.getMessage() << std::endl;
+    }
+}
+
+IloRange
+CplexBackend::createConstraint(const LinearConstraint& constraint) {
+    // create the lhs expression
+    IloExpr linearExpr(env_);
+
+    // set the coefficients
+    typedef std::map<unsigned int, double>::const_iterator CoefIt;
+    for (CoefIt pair = constraint.getCoefficients().begin(); pair != constraint.getCoefficients().end(); pair++)
+    {
+        linearExpr.setLinearCoef(x_[pair->first], pair->second);
+    }
+
+    switch(constraint.getRelation())
+    {
+        case LessEqual:
+            return IloRange(env_, linearExpr, constraint.getValue());
+            break;
+        case GreaterEqual:
+            return IloRange(env_, constraint.getValue(), linearExpr);
+            break;
+    }
+}
+
+bool
+CplexBackend::solve(Solution& x, double& value, std::string& msg) {
+
+    try {
+        cplex_ = IloCplex(model_);
+        setVerbose(_parameter.verbose);
+
+        setMIPGap(_parameter.mipGap);
+
+        if (_parameter.mipFocus <= 3)
+            setMIPFocus(_parameter.mipFocus);
+        else
+            std::cerr << "Invalid value for MIP focus!" << std::endl;
+
+        setNumThreads(_parameter.numThreads);
+
+        if(!cplex_.solve()) {
+           std::cout << "failed to optimize. " << cplex_.getStatus() << std::endl;
+           msg = "Optimal solution *NOT* found";
+           return false;
+        }
+        else
+            msg = "Optimal solution found";
+
+        // extract solution
+        cplex_.getValues(sol_, x_);
+        x.resize(_numVariables);
+        for (unsigned int i = 0; i < _numVariables; i++)
+            x[i] = sol_[i];
+
+        // get current value of the objective
+        value = cplex_.getObjValue();
+
+        x.setValue(value);
+
+    } catch (IloCplex::Exception& e) {
+
+        std::cerr << "error: " << e.getMessage() << std::endl;
+
+        msg = e.getMessage();
+
+        return false;
+    }
+
+    return true;
+}
+
+void
+CplexBackend::setMIPGap(double gap) {
+     cplex_.setParam(IloCplex::EpGap, gap);
+}
+
+void
+CplexBackend::setMIPFocus(unsigned int focus) {
+    /*
+     * GUROBI and CPLEX have the same meaning for the values of the MIPFocus and MIPEmphasis parameter:
+     *
+     * GUROBI docs:
+     * If you are more interested in finding feasible solutions quickly, you can select MIPFocus=1.
+     * If you believe the solver is having no trouble finding good quality solutions,
+     * and wish to focus more attention on proving optimality, select MIPFocus=2.
+     * If the best objective bound is moving very slowly (or not at all), you may want to try MIPFocus=3
+     * to focus on the bound.
+     *
+     * CPLEX params:
+     * switch(focus) {
+        case MIP_EMPHASIS_BALANCED:
+            cplex_.setParam(IloCplex::MIPEmphasis, 0);
+            break;
+        case  MIP_EMPHASIS_FEASIBILITY:
+            cplex_.setParam(IloCplex::MIPEmphasis, 1);
+            break;
+        case MIP_EMPHASIS_OPTIMALITY:
+            cplex_.setParam(IloCplex::MIPEmphasis, 2);
+            break;
+        case MIP_EMPHASIS_BESTBOUND:
+            cplex_.setParam(IloCplex::MIPEmphasis, 3);
+            break;
+        }
+     */
+
+    cplex_.setParam(IloCplex::MIPEmphasis, focus);
+}
+
+void
+CplexBackend::setNumThreads(unsigned int numThreads) {
+    cplex_.setParam(IloCplex::Threads, numThreads);
+}
+
+void
+CplexBackend::setVerbose(bool verbose) {
+
+    // setup GRB environment
+    if (verbose)
+    {
+        cplex_.setParam(IloCplex::MIPDisplay, 1);
+        cplex_.setParam(IloCplex::SimDisplay, 1);
+        cplex_.setParam(IloCplex::SiftDisplay, 1);
+    }
+    else
+    {
+        cplex_.setParam(IloCplex::MIPDisplay, 0);
+        cplex_.setParam(IloCplex::SimDisplay, 0);
+        cplex_.setParam(IloCplex::SiftDisplay, 0);
+    }
+}
+
+}}} // namespace opengm::learning::solver
+
+#endif // WITH_CPLEX
+
+#endif // CPLEX_OPENGM_LEARNING_SOLVER_SOLVER_H__
diff --git a/include/opengm/learning/solver/GurobiBackend.h b/include/opengm/learning/solver/GurobiBackend.h
index 488f5f5..5c5fa71 100644
--- a/include/opengm/learning/solver/GurobiBackend.h
+++ b/include/opengm/learning/solver/GurobiBackend.h
@@ -4,6 +4,7 @@
 #ifdef WITH_GUROBI
 
 #include <string>
+#include <vector>
 
 #include <gurobi_c++.h>
 
@@ -84,6 +85,7 @@ public:
 
 	bool solve(Solution& solution, double& value, std::string& message);
 
+
 private:
 
 	//////////////
@@ -102,6 +104,9 @@ private:
 	// set the number of threads to use
 	void setNumThreads(unsigned int numThreads);
 
+    // create a gurobi constraint from a linear constraint
+    GRBConstr createConstraint(const LinearConstraint &constraint);
+
 	/**
 	 * Enable solver output.
 	 */
@@ -299,27 +304,8 @@ GurobiBackend::setConstraints(const LinearConstraints& constraints) {
 
 		std::cout << "setting " << constraints.size() << " constraints" << std::endl;
 
-		unsigned int j = 0;
 		for (LinearConstraints::const_iterator constraint = constraints.begin(); constraint != constraints.end(); constraint++) {
-
-			// create the lhs expression
-			GRBLinExpr lhsExpr;
-
-			// set the coefficients
-			typedef std::map<unsigned int, double>::const_iterator CoefIt;
-			for (CoefIt pair = constraint->getCoefficients().begin(); pair != constraint->getCoefficients().end(); pair++)
-				lhsExpr += pair->second*_variables[pair->first];
-
-			// add to the model
-			_constraints.push_back(
-					_model.addConstr(
-						lhsExpr,
-						(constraint->getRelation() == LessEqual ? GRB_LESS_EQUAL :
-								(constraint->getRelation() == GreaterEqual ? GRB_GREATER_EQUAL :
-										GRB_EQUAL)),
-						constraint->getValue()));
-
-			j++;
+            _constraints.push_back(createConstraint(*constraint));
 		}
 
 		_model.update();
@@ -333,34 +319,36 @@ GurobiBackend::setConstraints(const LinearConstraints& constraints) {
 void
 GurobiBackend::addConstraint(const LinearConstraint& constraint) {
 
-	try {
-
-		std::cout << "adding a constraint" << std::endl;
+    try {
 
+        std::cout << "adding a constraint" << std::endl;
 
-		// create the lhs expression
-		GRBLinExpr lhsExpr;
+        _constraints.push_back(createConstraint(constraint));
+        _model.update();
 
-		// set the coefficients
-		typedef std::map<unsigned int, double>::const_iterator CoefIt;
-		for (CoefIt pair = constraint.getCoefficients().begin(); pair != constraint.getCoefficients().end(); pair++)
-			lhsExpr += pair->second*_variables[pair->first];
-
-		// add to the model
-		_constraints.push_back(
-				_model.addConstr(
-					lhsExpr,
-					(constraint.getRelation() == LessEqual ? GRB_LESS_EQUAL :
-							(constraint.getRelation() == GreaterEqual ? GRB_GREATER_EQUAL :
-									GRB_EQUAL)),
-					constraint.getValue()));
-
-		_model.update();
-
-	} catch (GRBException e) {
+    } catch (GRBException e) {
+        std::cerr << "error: " << e.getMessage() << std::endl;
+    }
+}
 
-		std::cerr << "error: " << e.getMessage() << std::endl;
-	}
+GRBConstr
+GurobiBackend::createConstraint(const LinearConstraint& constraint)
+{
+    // create the lhs expression
+    GRBLinExpr lhsExpr;
+
+    // set the coefficients
+    typedef std::map<unsigned int, double>::const_iterator CoefIt;
+    for (CoefIt pair = constraint.getCoefficients().begin(); pair != constraint.getCoefficients().end(); pair++)
+        lhsExpr += pair->second * _variables[pair->first];
+
+    // construct constraint
+    return _model.addConstr(
+                lhsExpr,
+                (constraint.getRelation() == LessEqual ? GRB_LESS_EQUAL :
+                                                          (constraint.getRelation() == GreaterEqual ? GRB_GREATER_EQUAL :
+                                                                                                       GRB_EQUAL)),
+                constraint.getValue());
 }
 
 bool
diff --git a/include/opengm/learning/solver/QuadraticSolverFactory.h b/include/opengm/learning/solver/QuadraticSolverFactory.h
index 1476907..db7813b 100644
--- a/include/opengm/learning/solver/QuadraticSolverFactory.h
+++ b/include/opengm/learning/solver/QuadraticSolverFactory.h
@@ -3,6 +3,8 @@
 
 #ifdef WITH_GUROBI
 #include "GurobiBackend.h"
+#elif WITH_CPLEX
+#include "CplexBackend.h"
 #endif
 
 namespace opengm {
@@ -17,6 +19,8 @@ public:
 
 #ifdef WITH_GUROBI
 		return new GurobiBackend();
+#elif WITH_CPLEX
+        return new CplexBackend();
 #endif
 
       throw opengm::RuntimeError("No quadratic solver available.");
diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt
index c25f77b..9fc0f1b 100644
--- a/src/unittest/CMakeLists.txt
+++ b/src/unittest/CMakeLists.txt
@@ -81,9 +81,14 @@ if(BUILD_TESTING)
 
   if(WITH_GUROBI)
     ADD_EXECUTABLE(test-learning test_learning.cxx ${headers})
-     target_link_libraries(test-learning ${GUROBI_CXX_LIBRARY} ${GUROBI_LIBRARY})
-     target_link_libraries(test-learning external-library-trws)
+    target_link_libraries(test-learning ${GUROBI_CXX_LIBRARY} ${GUROBI_LIBRARY})
     add_test(test-learning ${CMAKE_CURRENT_BINARY_DIR}/test-learning) 
+  else()
+    if(WITH_CPLEX)
+        ADD_EXECUTABLE(test-learning test_learning.cxx ${headers})
+        target_link_libraries(test-learning ${CPLEX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+        add_test(test-learning ${CMAKE_CURRENT_BINARY_DIR}/test-learning)
+    endif()
   endif()
 
    ADD_EXECUTABLE(test-memoryinfo test_memoryinfo.cxx ${headers})

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/opengm.git



More information about the debian-science-commits mailing list