/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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 . */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ namespace papilo { #include "papilo/core/postsolve/Postsolve.hpp" #include "papilo/core/postsolve/PostsolveStorage.hpp" template struct Validation { static void validateProblem( const Problem& problem, const PostsolveStorage& postsolve, const std::string& optimal_solution_file, const PresolveStatus status ) { Solution optimal_solution; bool success = parse_solution( postsolve, optimal_solution_file, optimal_solution ); if( success && ( status == PresolveStatus::kUnchanged || status == PresolveStatus::kReduced ) && check_if_solution_is_contained_in_problem( problem, postsolve, optimal_solution ) && can_reduced_solution_be_recalculated( problem, postsolve, optimal_solution ) ) fmt::print( "validation: SUCCESS\n" ); else fmt::print( "validation: FAILURE\n" ); } private: static bool can_reduced_solution_be_recalculated( const Problem& problem, const PostsolveStorage& postsolveStorage, const Solution& optimal_solution ) { bool validation_succcess = true; Vec vec{}; for( int i = 0; i < (int) postsolveStorage.origcol_mapping.size(); i++ ) vec.push_back( optimal_solution.primal[postsolveStorage.origcol_mapping[i]] ); Solution calculated_orig_solution{}; Solution reducedSolution = Solution( vec ); const Message msg{}; Postsolve postsolve{msg, postsolveStorage.getNum()}; postsolve.undo( reducedSolution, calculated_orig_solution, postsolveStorage ); for( int i = 0; i < (int) postsolveStorage.nColsOriginal; i++ ) { if( ! postsolveStorage.getNum().isFeasEq( optimal_solution.primal[i], calculated_orig_solution.primal[i] ) ) { fmt::print( "postsolve for variable {} not equal {} !={}\n", problem.getVariableNames()[i], (double) optimal_solution.primal[i], (double) calculated_orig_solution.primal[i] ); validation_succcess = false; } } return validation_succcess; } static bool parse_solution( const PostsolveStorage& postsolveStorage, const std::string& optimal_solution_file, Solution& optimal_solution) { SolParser parser; std::vector one_to_one_mapping; for( int i = 0; i < (int) postsolveStorage.nColsOriginal; i++ ) one_to_one_mapping.push_back( i ); return parser.read( optimal_solution_file, one_to_one_mapping, postsolveStorage.getOriginalProblem().getVariableNames(), optimal_solution ); } static bool check_if_solution_is_contained_in_problem( const Problem& problem, const PostsolveStorage& postsolve, const Solution optimal_solution ) { bool validation_success = true; for( int i = 0; i < problem.getNCols(); i++ ) { REAL solution_coeff = optimal_solution.primal[postsolve.origcol_mapping[i]]; if( !problem.getColFlags()[i].test( ColFlag::kUbInf ) && !postsolve.getNum().isFeasLE( solution_coeff, problem.getUpperBounds()[i] ) ) { fmt::print( "lb {} of var {} violates bounds for value {} ", (double) problem.getLowerBounds()[i], problem.getVariableNames()[postsolve.origcol_mapping[i]], (double) solution_coeff ); validation_success = false; } if( !problem.getColFlags()[i].test( ColFlag::kLbInf ) && !postsolve.getNum().isFeasGE( solution_coeff, problem.getLowerBounds()[i] ) ) { fmt::print( "ub {} of var {} violates bounds for value {} ", (double) problem.getUpperBounds()[i], problem.getVariableNames()[postsolve.origcol_mapping[i]], (double) solution_coeff ); validation_success = false; } } for( int i = 0; i < problem.getConstraintMatrix().getNRows(); i++ ) { REAL row_value = calculateRowValueForSolution( problem, postsolve, optimal_solution, i ); if( problem.getConstraintMatrix().getRowFlags()[i].test( RowFlag::kEquation ) && !postsolve.getNum().isFeasEq( row_value, problem.getConstraintMatrix().getRightHandSides()[i] ) ) { fmt::print( "equality in row {} is violated: {} != {}\n", problem.getConstraintNames()[postsolve.origrow_mapping[i]], (double) row_value, (double) problem.getConstraintMatrix().getRightHandSides()[i] ); validation_success = false; } else { if( !problem.getConstraintMatrix().getRowFlags()[i].test( RowFlag::kRhsInf ) && !postsolve.getNum().isFeasLE( row_value, problem.getConstraintMatrix().getRightHandSides()[i] ) ) { fmt::print( "LE inequality in row {} is violated: {} !<= {}\n", problem.getConstraintNames()[postsolve.origrow_mapping[i]], (double) row_value, (double) problem.getConstraintMatrix().getRightHandSides()[i] ); validation_success = false; } if( !problem.getConstraintMatrix().getRowFlags()[i].test( RowFlag::kLhsInf ) && !postsolve.getNum().isFeasGE( (double) row_value, (double) problem.getConstraintMatrix().getLeftHandSides()[i] ) ) { fmt::print( "GE inequality in row {} is violated: {} !>= {}\n", problem.getConstraintNames()[postsolve.origrow_mapping[i]], (double) row_value, (double) problem.getConstraintMatrix().getLeftHandSides()[i] ); validation_success = false; } } } return validation_success; } static REAL calculateRowValueForSolution( const Problem& problem, const PostsolveStorage& postsolve, const Solution& optimal_solution, int i ) { const SparseVectorView& row = problem.getConstraintMatrix().getRowCoefficients( i ); REAL row_value = 0; for( int j = 0; j < row.getLength(); ++j ) { int col_index = row.getIndices()[j]; REAL solution_coeff = optimal_solution.primal[postsolve.origcol_mapping[col_index]]; REAL col_value = row.getValues()[j]; row_value += solution_coeff * col_value; } return row_value; } }; } // namespace papilo