/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* This file is part of the program and library */ /* PaPILO --- Parallel Presolve for Integer and Linear Optimization */ /* */ /* Copyright (C) 2020-2022 Konrad-Zuse-Zentrum */ /* fuer Informationstechnik Berlin */ /* */ /* This program is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU Lesser General Public License as published */ /* by the Free Software Foundation, either version 3 of the License, or */ /* (at your option) any later version. */ /* */ /* This program 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 Lesser General Public License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with this program. If not, see . */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef _PAPILO_INTERFACES_GLOP_INTERFACE_HPP_ #define _PAPILO_INTERFACES_GLOP_INTERFACE_HPP_ #include "papilo/misc/Vec.hpp" #include #include #include "ortools/linear_solver/linear_solver.h" #include "papilo/core/Problem.hpp" #include "papilo/core/Solution.hpp" #include "papilo/interfaces/SolverInterface.hpp" namespace papilo { using operations_research::MPObjective; using operations_research::MPSolver; using operations_research::MPConstraint; using operations_research::MPVariable; template class GlopInterface : public SolverInterface { private: int n_cols{}; Vec variables; std::unique_ptr solver; std::string solver_id = "PDLP"; int doSetUp( const Problem& problem, const Vec& origRowMap, const Vec& origColMap ) { if( !std::is_same::value ) { fmt::print( "Please use double precision when solving with " "OR-TOOLS." ); return -1; } auto domains = problem.getVariableDomains(); n_cols = problem.getNCols(); //TODO: enable log output for PDLP solver = std::unique_ptr( MPSolver::CreateSolver( solver_id ) ); solver->EnableOutput(); const double infinity = MPSolver::infinity(); auto& row_flags = problem.getRowFlags(); auto lhs = problem.getConstraintMatrix().getLeftHandSides(); auto rhs = problem.getConstraintMatrix().getRightHandSides(); const Vec& varNames = problem.getVariableNames(); auto coefficients = problem.getObjective().coefficients; variables = Vec{}; variables.reserve(problem.getNRows()); MPObjective* const objective = solver->MutableObjective(); for( int i = 0; i < problem.getNCols(); i++ ) { double lb = domains.flags[i].test( ColFlag::kLbInf ) ? -infinity : domains.lower_bounds[i]; double ub = domains.flags[i].test( ColFlag::kUbInf ) ? infinity : domains.upper_bounds[i]; variables.push_back(solver->MakeNumVar( lb, ub, varNames[origColMap[i]])); objective->SetCoefficient( variables[i], (double)coefficients[i] ); } assert( solver->NumVariables() == problem.getNCols() ); for( int i = 0; i < problem.getNRows(); i++ ) { auto data = problem.getConstraintMatrix().getRowCoefficients( i ); double l = row_flags[i].test( RowFlag::kLhsInf ) ? -infinity : lhs[i]; double r = row_flags[i].test( RowFlag::kRhsInf ) ? infinity : rhs[i]; MPConstraint* const con = solver->MakeRowConstraint( l, r ); for( int j = 0; j < data.getLength(); j++ ) { double coeff = data.getValues()[j]; int index = data.getIndices()[j]; con->SetCoefficient( variables[index], coeff ); } } assert( solver->NumConstraints() == problem.getNRows() ); objective->SetMinimization(); return 0; } int doSetUp( const Problem& problem, const Vec& origRowMap, const Vec& origColMap, const Components& components, const ComponentInfo& component ) { if( !std::is_same::value ) { fmt::print( "Please use double precision when solving with " "OR-TOOLS." ); return -1; } int ncols = components.getComponentsNumCols( component.componentid ); int nrows = components.getComponentsNumRows( component.componentid ); const int* colset = components.getComponentsCols( component.componentid ); const int* rowset = components.getComponentsRows( component.componentid ); const Vec& varNames = problem.getVariableNames(); const Vec& rowNames = problem.getConstraintNames(); const VariableDomains& domains = problem.getVariableDomains(); const Vec& obj = problem.getObjective().coefficients; const Vec& rhs = problem.getConstraintMatrix().getRightHandSides(); const Vec& lhs = problem.getConstraintMatrix().getLeftHandSides(); const auto consMatrix = problem.getConstraintMatrix(); auto coefficients = problem.getObjective().coefficients; auto& row_flags = problem.getRowFlags(); n_cols = problem.getNCols(); solver = std::unique_ptr(MPSolver::CreateSolver( solver_id )); solver->EnableOutput(); const double infinity = MPSolver::infinity(); variables = Vec{}; variables.reserve(problem.getNRows()); MPObjective* const objective = solver->MutableObjective(); for( int j = 0; j < ncols; j++ ) { int col = colset[j]; double lb = domains.flags[col].test( ColFlag::kLbInf ) ? -infinity : domains.lower_bounds[col]; double ub = domains.flags[col].test( ColFlag::kUbInf ) ? infinity : domains.upper_bounds[col]; variables[col] = solver->MakeNumVar( lb, ub, varNames[origColMap[col]] ); objective->SetCoefficient( variables[col], (double)coefficients[col] ); } assert( solver->NumVariables() == ncols ); for( int i = 0; i < nrows; i++ ) { int row = rowset[i]; auto data = problem.getConstraintMatrix().getRowCoefficients( row ); double l = row_flags[row].test( RowFlag::kLhsInf ) ? -infinity : lhs[row]; double r = row_flags[row].test( RowFlag::kRhsInf ) ? infinity : rhs[row]; MPConstraint* const con = solver->MakeRowConstraint( l, r ); for( int j = 0; j < data.getLength(); j++ ) { double coeff = data.getValues()[j]; int index = components.getColComponentIdx( data.getIndices()[j]); con->SetCoefficient( variables[index], coeff ); } } assert( solver->NumConstraints() == nrows ); objective->SetMinimization(); return 0; } public: GlopInterface() =default; void setUp( const Problem& prob, const Vec& row_maps, const Vec& col_maps, const Components& components, const ComponentInfo& component ) override { if( doSetUp( prob, row_maps, col_maps, components, component ) != 0 ) this->status = SolverStatus::kError; } void setNodeLimit( int num ) override { } void setGapLimit( const REAL& gaplim ) override { } void setSoftTimeLimit( double tlim ) override { } void setTimeLimit( double tlim ) override { solver->SetTimeLimit(absl::Seconds(tlim)); } void setVerbosity( VerbosityLevel verbosity ) override { } void setUp( const Problem& prob, const Vec& row_maps, const Vec& col_maps ) override { if( doSetUp( prob, row_maps, col_maps ) != 0 ) this->status = SolverStatus::kError; } void solve() override { MPSolver::ResultStatus result_status = solver->Solve(); assert( this->status != SolverStatus::kError ); switch( result_status ) { case MPSolver::INFEASIBLE: this->status = SolverStatus::kInfeasible; break; case MPSolver::UNBOUNDED: this->status = SolverStatus::kUnbounded; break; case MPSolver::OPTIMAL: this->status = SolverStatus::kOptimal; break; case MPSolver::FEASIBLE: case MPSolver::NOT_SOLVED: this->status = SolverStatus::kInterrupted; break; case MPSolver::ABNORMAL: case MPSolver::MODEL_INVALID: this->status = SolverStatus::kError; break; } } REAL getDualBound() override { return 0; } bool is_dual_solution_available() override { return false; } bool getSolution( Solution& solbuffer ) override { Vec primal{}; for( int i = 0; i < n_cols; i++ ) primal.push_back( variables[i]->solution_value() ); solbuffer = Solution( primal ); return true; } bool getSolution( const Components& components, int component, Solution& solbuffer ) override { const int* colset = components.getComponentsCols( component ); for( std::size_t i = 0; i < n_cols; ++i ) solbuffer.primal[colset[i]] = variables[i]->solution_value(); return true; } SolverType getType() override { return SolverType::MIP; } String getName() override { return "or-tools"; } void printDetails() override { } void addParameters( ParameterSet& paramSet ) override { paramSet.addParameter( "ortools.solver_id", "solver id", solver_id ); } ~GlopInterface() = default; }; template class GlopFactory : public SolverFactory { GlopFactory() { solver = new GlopInterface(); } GlopInterface* solver; public: virtual std::unique_ptr> newSolver( VerbosityLevel verbosity ) const { auto glop = std::unique_ptr>( solver ); return std::move( glop ); } virtual void add_parameters( ParameterSet& parameter ) const { //TODO: configure parameters of GLOP solver->addParameters( parameter ); } static std::unique_ptr> create() { return std::unique_ptr>( new GlopFactory() ); } }; } // namespace papilo #endif