diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ac55b3..7e41145 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12.0) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/lib/core/types.cpp b/lib/core/types.cpp new file mode 100644 index 0000000..e69de29 diff --git a/lib/utils/reader.cpp b/lib/utils/reader.cpp index 4f2bca7..ee739a9 100644 --- a/lib/utils/reader.cpp +++ b/lib/utils/reader.cpp @@ -7,79 +7,101 @@ namespace utils{ Reader::Reader(){} -//this function is probably now useless but still good practise! -void Reader::readFirstProblem(const std::string problems_filepath){ +void Reader::readNextProblem(std::fstream &problems_filestream){ + //clear anything still sored in class members from previous read + problem_matrix_.clear(); + lower_bounds_.clear(); + upper_bounds_.clear(); - //initialise filestream and open file - std::fstream newfile; - newfile.open(problems_filepath, std::ios::in); //check file is open - if(!newfile.is_open()){ + if(!problems_filestream.is_open()){ std::cout << "ERROR: Unable to open file" << std::endl; } + //initialise strings for storing elements read from file std::string temp_string; std::string num_variables_string; std::string num_inequality_rows_string; + std::string num_equality_rows_string; - // get number of variables and number of inequalities we are reading in - std::getline(newfile, num_variables_string); - const core::int_t num_variables = static_cast(atoi(num_variables_string.c_str())) + 1; + // get number of variables we are reading in, taking into + // account whether the line is a separation line or not + // (it is only not a separation line in the first problem.) + std::getline(problems_filestream, temp_string); + if (temp_string.find('~') != std::string::npos) { + std::getline(problems_filestream, num_variables_string); + } else { + num_variables_string = temp_string; + } + const int num_variables = static_cast(atoi(num_variables_string.c_str())); - std::getline(newfile, num_inequality_rows_string); - const core::int_t num_inequality_rows = static_cast(atoi(num_inequality_rows_string.c_str())); + // Get number of inequalities. + std::getline(problems_filestream, num_inequality_rows_string); + const int num_inequality_rows = static_cast(atoi(num_inequality_rows_string.c_str())); - //initialise vector for using as temporary holding for matrix row - std::vector matrix_row; + // Define vectors to read in constraints. + std::vector problem_row; // A line in the input file + int constant_term; + std::vector problem_matrix_row; // Formatted constraint without the + // constant term. //read inequality in rows, add vectors to problem matrix for(size_t i = 0; i < num_inequality_rows; ++i){ - std::getline(newfile, temp_string); - matrix_row = convertStringToVector(temp_string); + std::getline(problems_filestream, temp_string); + problem_row = getProblemRowAsIntVector(temp_string); + + constant_term = problem_row.at(0)*(-1); + problem_matrix_row = spliceVector(problem_row, 1, num_variables); + //check vector size - if(matrix_row.size() != num_variables){ - std::cout << "ERROR: length of matrix row is: " << matrix_row.size() << " but length of " << num_inequality_rows << " was expected" << std::endl; + if(problem_matrix_row.size() != num_variables){ + std::cout << "ERROR: length of matrix row is: " << problem_row.size() << " but length of " << num_variables << " was expected" << std::endl; } - problem_matrix_.push_back(matrix_row); - matrix_row.clear(); + + problem_matrix_.push_back(problem_matrix_row); + upper_bounds_.push_back(kMaxInt); + lower_bounds_.push_back(constant_term); + + problem_row.clear(); + problem_matrix_row.clear(); } //read in equality rows and add to problem matrix - std::string num_equality_rows_string; - std::getline(newfile, num_equality_rows_string); - - core::int_t num_equality_rows = static_cast(atoi(num_equality_rows_string.c_str())); + std::getline(problems_filestream, num_equality_rows_string); + int num_equality_rows = static_cast(atoi(num_equality_rows_string.c_str())); for(size_t i = 0; i < num_equality_rows; ++i){ - //get and typecast row vector - std::getline(newfile, temp_string); - matrix_row = convertStringToVector(temp_string); + std::getline(problems_filestream, temp_string); + problem_row = getProblemRowAsIntVector(temp_string); + + constant_term = problem_row.at(0)*(-1); + problem_matrix_row = spliceVector(problem_row, 1, num_variables); //check vector size - if(matrix_row.size() != num_variables){ - std::cout << "ERROR: Expexted row of length " << num_variables << " but row has length " << matrix_row.size() << std::endl; + if(problem_matrix_row.size() != num_variables){ + std::cout << "ERROR: length of matrix row is: " << problem_row.size() << " but length of " << num_variables << " was expected" << std::endl; } - problem_matrix_.push_back(matrix_row); - matrix_row.clear(); - } - newfile.close(); - //make bound vectors - for(size_t i = 0; i < num_inequality_rows; ++i){ - upper_bounds_.push_back(kMaxInt); - lower_bounds_.push_back(0); - } + problem_matrix_.push_back(problem_matrix_row); + upper_bounds_.push_back(constant_term); + lower_bounds_.push_back(constant_term); - for(size_t i = num_inequality_rows; i < num_equality_rows+num_inequality_rows; ++i){ - upper_bounds_.push_back(0); - lower_bounds_.push_back(0); + problem_row.clear(); + problem_matrix_row.clear(); } + // Read the tilde line so that next problem starts + // at the number of variables line. If the last problem + // ends with a tilde line would work and we would not need the + // tilde check at the start: + // std::getline(problems_filestream, temp_string); } -void Reader::readProblem(const std::string problems_filepath, const core::int_t problem_number){ +void Reader::readProblem(const std::string problems_filepath, const int problem_number){ //clear anything still sored in class members from previous read problem_matrix_.clear(); + lower_bounds_.clear(); + upper_bounds_.clear(); //initialise filestream and open file std::fstream newfile; @@ -89,7 +111,7 @@ void Reader::readProblem(const std::string problems_filepath, const core::int_t std::cout << "ERROR: Unable to open file" << std::endl; } //find location in file we want to look at (nth problem) - core::int_t current_problem_location = 0; + int current_problem_location = 0; std::string tempstring; std::string tilde = "~"; while(current_problem_location != problem_number-1){ @@ -106,62 +128,67 @@ void Reader::readProblem(const std::string problems_filepath, const core::int_t // get number of variables and number of inequalities we are reading in std::getline(newfile, num_variables_string); - const core::int_t num_variables = static_cast(atoi(num_variables_string.c_str())) + 1; + const int num_variables = static_cast(atoi(num_variables_string.c_str())); std::getline(newfile, num_inequality_rows_string); - const core::int_t num_inequality_rows = static_cast(atoi(num_inequality_rows_string.c_str())); + const int num_inequality_rows = static_cast(atoi(num_inequality_rows_string.c_str())); - //initialise vector for using as temporary holding for matrix row - std::vector matrix_row; + std::vector problem_row; + int constant_term; + std::vector problem_matrix_row; //read inequality in rows, add vectors to problem matrix for(size_t i = 0; i < num_inequality_rows; ++i){ std::getline(newfile, temp_string); - matrix_row = convertStringToVector(temp_string); + problem_row = getProblemRowAsIntVector(temp_string); + + constant_term = problem_row.at(0)*(-1); + problem_matrix_row = spliceVector(problem_row, 1, num_variables); + //check vector size - if(matrix_row.size() != num_variables){ - std::cout << "ERROR: length of matrix row is: " << matrix_row.size() << " but length of " << num_inequality_rows << " was expected" << std::endl; + if(problem_matrix_row.size() != num_variables){ + std::cout << "ERROR: length of matrix row is: " << problem_row.size() << " but length of " << num_variables << " was expected" << std::endl; } - problem_matrix_.push_back(matrix_row); - matrix_row.clear(); + + problem_matrix_.push_back(problem_matrix_row); + upper_bounds_.push_back(kMaxInt); + lower_bounds_.push_back(constant_term); + + problem_row.clear(); + problem_matrix_row.clear(); } //read in equality rows and add to problem matrix std::string num_equality_rows_string; std::getline(newfile, num_equality_rows_string); - core::int_t num_equality_rows = static_cast(atoi(num_equality_rows_string.c_str())); + int num_equality_rows = static_cast(atoi(num_equality_rows_string.c_str())); for(size_t i = 0; i < num_equality_rows; ++i){ - //get and typecast row vector std::getline(newfile, temp_string); - matrix_row = convertStringToVector(temp_string); + problem_row = getProblemRowAsIntVector(temp_string); + + constant_term = problem_row.at(0)*(-1); + problem_matrix_row = spliceVector(problem_row, 1, num_variables); //check vector size - if(matrix_row.size() != num_variables){ - std::cout << "ERROR: Expexted row of length " << num_variables << " but row has length " << matrix_row.size() << std::endl; + if(problem_matrix_row.size() != num_variables){ + std::cout << "ERROR: length of matrix row is: " << problem_row.size() << " but length of " << num_variables << " was expected" << std::endl; } - problem_matrix_.push_back(matrix_row); - matrix_row.clear(); - } + problem_matrix_.push_back(problem_matrix_row); + upper_bounds_.push_back(constant_term); + lower_bounds_.push_back(constant_term); - //make bound vectors - for(size_t i = 0; i < num_inequality_rows; ++i){ - lower_bounds_.push_back(0); - upper_bounds_.push_back(kMaxInt); + problem_row.clear(); + problem_matrix_row.clear(); } - for(size_t i = num_inequality_rows; i < num_inequality_rows+num_equality_rows; ++i){ - lower_bounds_.push_back(0); - upper_bounds_.push_back(0); - } - - newfile.close(); + newfile.close(); } -std::vector Reader::convertStringToVector(const std::string vector_string){ +std::vector Reader::convertStringToVector(const std::string vector_string){ std::string tempstring; std::vector stringvec; @@ -179,20 +206,54 @@ std::vector Reader::convertStringToVector(const std::string vector_ } //initialise variables - std::vector rowvector; - core::int_t temp_int; + std::vector rowvector; + int temp_int; std::string space = " "; //if string in vector is not empty or a space, convert to int and add to return vector for(size_t i = 0; i < stringvec.size(); ++i){ tempstring = stringvec.at(i); if(tempstring.compare(space) != 0 && !tempstring.empty()){ - temp_int = static_cast(atoi(tempstring.c_str())); + temp_int = static_cast(atoi(tempstring.c_str())); rowvector.push_back(temp_int); } } return rowvector; } +std::vector Reader::getProblemRowAsIntVector(const std::string problem_row_string){ + std::string tempstring; + std::vector row_vector; + + //convert single string to vector of strings with space + //character as delimiter + for(size_t i = 0; i < problem_row_string.size(); ++i){ + char position_char = problem_row_string.at(i); + + if(position_char != ' '){ + tempstring.push_back(position_char); + } else { + row_vector.push_back(static_cast(atoi(tempstring.c_str()))); + tempstring.clear(); + } + + if(i == problem_row_string.length()-1){ + row_vector.push_back(static_cast(atoi(tempstring.c_str()))); + } + } + + return row_vector; +} + +std::vector Reader::spliceVector(std::vector to_splice, const int range_start, const int range_end) { + std::vector spliced_vector; + + for (int i = range_start; i < range_end+1; i++) { + spliced_vector.push_back(to_splice.at(i)); + } + + return spliced_vector; +} + }//namespace utils diff --git a/lib/utils/reader.hpp b/lib/utils/reader.hpp index f9be300..39b35b5 100644 --- a/lib/utils/reader.hpp +++ b/lib/utils/reader.hpp @@ -1,7 +1,6 @@ #include #include #include -#include "../core/types.hpp" namespace utils{ @@ -9,13 +8,6 @@ namespace utils{ { public: Reader(); - /** - * @brief reads the first problem in the problem file and writes formatted contents - * to class members problem_matrix_ and cost_vector_. - * - * @param problems_filepath path to problem text file - */ - void readFirstProblem(const std::string problems_filepath); /** * @brief reads the nth problem in the problem file and writes formatted contents @@ -24,17 +16,26 @@ namespace utils{ * @param problems_filepath path to problem text file * @param problem_number number of problem to read in */ - void readProblem(const std::string problems_filepath, const core::int_t problem_number); + void readProblem(const std::string problems_filepath, const int problem_number); - std::vector> problem_matrix_; + /** + * @brief reads the next problem in the problem file and writes formatted contents + * to class members problem_matrix_ given an open filestream of test problems. + * + * @param problems_filestream the open filestream + */ + void readNextProblem(std::fstream &problems_filestream); - const core::int_t kMaxInt = 32765; + std::vector> problem_matrix_; - const core::int_t kMinInt = -32765; + const int kMaxInt = 32765; - std::vector upper_bounds_; + const int kMinInt = -32765; - std::vector lower_bounds_; + std::vector upper_bounds_; + + std::vector lower_bounds_; + private: @@ -46,7 +47,14 @@ namespace utils{ * @param vector_string input string to be formatted * @return std::vector formatted vector of ints produced from string input */ - std::vector convertStringToVector(const std::string vector_string); + std::vector convertStringToVector(const std::string vector_string); + + std::vector getProblemRowAsIntVector(const std::string problem_row_string); + + std::vector spliceVector(std::vector to_splice, const int range_start, const int range_end); + }; + + }//namespace utils \ No newline at end of file diff --git a/problems/primal_problems.txt b/problems/primal_problems.txt index ca25203..6949ed3 100644 --- a/problems/primal_problems.txt +++ b/problems/primal_problems.txt @@ -1,6 +1,6 @@ 3 5 -1 -1 -10 0 0 -1 20 1 0 100 -0 1 0 1 1 +1 -1 -10 0 0 0 +0 1 20 1 0 100 +0 0 1 0 1 1 ~~~~~ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d7115a1..46a2bac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ set(target run_main) +set(CMAKE_CXX_STANDARD 11) file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp") file(GLOB code "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") add_executable(${target} ${headers} ${code}) diff --git a/src/main.cpp b/src/main.cpp index cc000dc..ce4c33f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,28 +3,65 @@ #include "../lib/utils/primal_reader.hpp" #include #include +#include +#include -int main(){ - utils::Reader reader_; - //reader_.readFirstProblem("/home/maxguy/projects/hons/hons-project/problems/feasibility_testcases.txt"); +/// @brief Prints a test case problem. +/// @param problem_matrix - matrix for the problem constraints. +/// @param lower_bounds - lower bounds for problem constraints. +/// @param upper_bounds - upper bounds for problem constraints. +void printLP( + const std::vector> problem_matrix, + const std::vector lower_bounds, + const std::vector upper_bounds +) { + for (int i = 0; i < problem_matrix.size(); ++i) { + printf("Row %d \n", i+1); + // JAJH: [1] should be [0]. Caused segfault on a problem with only one row + // for (int j = 0; j < problem_matrix[1].size(); ++j) { + for (int j = 0; j < problem_matrix[0].size(); ++j) { + std::cout << problem_matrix[i][j] << std::endl; + } + std::cout<<" "<> mat = reader_.problem_matrix_; - std::vector max = reader_.upper_bounds_; - std::vector min = reader_.lower_bounds_; + for (int i = 0; i < problem_matrix.size(); ++i) { + std::string upper_bound = std::to_string(upper_bounds[i]); + if (upper_bound == "32765") { + upper_bound = "Inf"; + } + std::cout << lower_bounds.at(i) << ", " << upper_bound << std::endl; + } +} - for(int i =0; i < mat.size(); ++i){ - for(int j = 0; j < mat[1].size(); ++j){ - std::cout << mat[i][j] << std::endl; - } - } +int main(){ + std::string test_problems = "/Users/pepe/hons-project/problems/feasibility_testcases.txt"; + utils::Reader reader; + + // Start filestream to pass to reader. + std::fstream problems_filestream; + problems_filestream.open(test_problems, std::ios::in); - for(int i = 0; i < max.size(); ++i){ - std::cout << max.at(i) << ", " << min.at(i) << std::endl; + std::vector>& problem_matrix = reader.problem_matrix_; + std::vector& upper_bounds = reader.upper_bounds_; + std::vector& lower_bounds = reader.lower_bounds_; + + for (int i = 0; i < 20; i++){ + std::cout << i << std::endl; + reader.readNextProblem(problems_filestream); + printLP(problem_matrix, lower_bounds, upper_bounds); } + problems_filestream.close(); + + std::cout << "" << std::endl; + std::cout << "Printing problem 20 with readProblem" << std::endl; + reader.readProblem("/Users/pepe/hons-project/problems/feasibility_testcases.txt", 3); + printLP(problem_matrix, lower_bounds, upper_bounds); return 0; } \ No newline at end of file diff --git a/test-presolve/CMakeLists.txt b/test-presolve/CMakeLists.txt new file mode 100644 index 0000000..f15626e --- /dev/null +++ b/test-presolve/CMakeLists.txt @@ -0,0 +1,14 @@ +set(target basic_test) +file(GLOB code "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +add_executable(${target} ${code}) +target_include_directories(${target} + INTERFACE "${CMAKE_SOURCE_DIR}/lib" + "${CMAKE_CURRENT_SOURCE_DIR}" +) + +target_link_libraries(${target} + dual_simplex + primal_simplex + utils + logical_solver +) \ No newline at end of file diff --git a/test-presolve/CompLinkFILE b/test-presolve/CompLinkFILE new file mode 100755 index 0000000..a157291 --- /dev/null +++ b/test-presolve/CompLinkFILE @@ -0,0 +1,4 @@ +export HiGHS_ROOT="/Users/pepe/HiGHS" +echo $HiGHS_ROOT +clang++ -std=c++11 -g highs_presolve.cpp ../lib/utils/reader.cpp -I$HiGHS_ROOT/build -I$HiGHS_ROOT/src -I$HiGHS_ROOT/src -L$HiGHS_ROOT/build/lib -lhighs -o highs + diff --git a/test-presolve/Run b/test-presolve/Run new file mode 100755 index 0000000..ff5bbf4 --- /dev/null +++ b/test-presolve/Run @@ -0,0 +1,4 @@ +export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/Users/pepe/HiGHS/build/lib" +./highs $1 + + diff --git a/test-presolve/highs b/test-presolve/highs new file mode 100755 index 0000000..e4612ae Binary files /dev/null and b/test-presolve/highs differ diff --git a/test-presolve/highs.dSYM/Contents/Info.plist b/test-presolve/highs.dSYM/Contents/Info.plist new file mode 100644 index 0000000..7f186e6 --- /dev/null +++ b/test-presolve/highs.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.highs + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/test-presolve/highs.dSYM/Contents/Resources/DWARF/highs b/test-presolve/highs.dSYM/Contents/Resources/DWARF/highs new file mode 100644 index 0000000..21d5dd3 Binary files /dev/null and b/test-presolve/highs.dSYM/Contents/Resources/DWARF/highs differ diff --git a/test-presolve/highs_presolve.cpp b/test-presolve/highs_presolve.cpp new file mode 100644 index 0000000..5f70578 --- /dev/null +++ b/test-presolve/highs_presolve.cpp @@ -0,0 +1,369 @@ +#include "../lib/utils/reader.hpp" +#include "Highs.h" +#include +#include +#include +#include +#include +#include + + +// Constants: + +std::string test_problems = "/Users/pepe/hons-project/problems/feasibility_testcases.txt"; +const int all_test_cases_count = 150218; +const double inf = kHighsInf; + +// Parameters: + +// Set test_all to true to test all problems. +const bool test_all = true; +const int to_test_count = 3000; +bool print_problems = false;//true; + +// Report variables: +int reduced_to_empty_count = 0; +std::vector used_presolve_rules; + + +/// @brief Prints a test case problem. +/// @param problem_matrix - matrix for the problem constraints. +/// @param lower_bounds - lower bounds for problem constraints. +/// @param upper_bounds - upper bounds for problem constraints. +void printLP( + const std::vector> problem_matrix, + const std::vector lower_bounds, + const std::vector upper_bounds +); + + +/// @brief Returns whether a presolve rule has been used. +/// @param presolve_rule +/// @return Boolean indicating whether presolve_rule has been +/// found in used rules. +bool checkVectorContainsString(const std::vector search_vector, const std::string to_find); + + +/// @brief Defines a problem in a HiGHS instance. +/// @param highs - HiGHS object +/// @param problem_matrix - Matrix of problem constraints. +/// @param upprer_bounds - int vector of upper bounds for the constraints. +/// @param lower_bounds - int vector of lower bounds for the constraints. +/// @return HighsStatus to indicate whether the definition was successful. +HighsStatus defineLp( + Highs& highs, + const std::vector> problem_matrix, + const std::vector lower_bounds, + std::vector upper_bounds +); + + +std::vector reportAndLogPresolveLog(Highs& highs); + + +/// @brief Reads and presolves one problem from the test cases +/// using reader.cpp and HiGHS. +/// @param highs - HiGHS object. +/// @param problem_matrix - matrix for the problem constraints. +/// @param lower_bounds - lower bounds for problem constraints. +/// @param upper_bounds - upper bounds for problem constraints. +void presolveSingleProblem( + Highs& highs, + const std::vector> problem_matrix, + const std::vector lower_bounds, + const std::vector upper_bounds +); + + +/// @brief For test problems 1 to to problems_count reads the problems +/// using reader.cpp and applies and reports presolve using HiGHS. +/// @param n the number of problems to read and presolve. +void presolveProblems(const int problems_count); + + +void printLP( + const std::vector> problem_matrix, + const std::vector lower_bounds, + const std::vector upper_bounds +) { + for (int i = 0; i < problem_matrix.size(); ++i) { + printf("Row %d \n", i+1); + + for (int j = 0; j < problem_matrix[0].size(); ++j) { + std::cout << problem_matrix[i][j] << std::endl; + } + std::cout<<" "< search_vector, const std::string to_find) { + for (auto&vector_element : search_vector) { + if (vector_element == to_find) { + return true; + } + } + return false; +} + + +std::vector reportAndLogPresolveLog(Highs& highs) { + const HighsPresolveLog& presolve_log = highs.getPresolveLog(); + // The presolve_rule_off option will alow certain presolve rules to + // be switched off + + if (print_problems) { + printf("\nRule Bit| Call Row Col| Name\n"); + } + int bit = 1; + + // Problem presolve rules used + std::vector problem_presolve_rules; + + for (int rule_ix = 0; rule_ix < kPresolveRuleCount; rule_ix++) { + const HighsPresolveRuleLog& log = presolve_log.rule[rule_ix]; + const std::string presolve_rule = highs.presolveRuleTypeToString(rule_ix); + + if (log.call) { + if (print_problems) { + printf(" %2d %4d| %3d %3d %3d| %s\n", rule_ix, bit, + log.call, + log.row_removed, + log.col_removed, + presolve_rule.c_str()); + } + if (log.row_removed > 0 || log.col_removed > 0) { + if (!checkVectorContainsString(used_presolve_rules, presolve_rule)) { + used_presolve_rules.push_back(presolve_rule); + } + + if (!checkVectorContainsString(problem_presolve_rules, presolve_rule)) { + problem_presolve_rules.push_back(presolve_rule); + } + } + } + bit *= 2; + } + + return problem_presolve_rules; +}; + + +HighsStatus defineLp( + Highs& highs, + const std::vector> problem_matrix, + const std::vector lower_bounds, + const std::vector upper_bounds +) { + HighsStatus return_status = HighsStatus::kOk; + + // Problem dimensions. + // Since problems with no rows shouldn't reach here, add an assert + const int num_rows = problem_matrix.size(); + assert(num_rows > 0); + const int num_var = problem_matrix[0].size(); + + // Setting up problem bounds + std::vector lower; + std::vector upper; + lower.assign(num_var, -inf); + upper.assign(num_var, inf); + return_status = highs.addVars(num_var, &lower[0], &upper[0]); + + for (int row_num = 0; row_num < num_rows; ++row_num) { + // Getting row, constant term and bounds for the row. + std::vector row = problem_matrix[row_num]; + int constant_term = row[0]; + int lower_bound = lower_bounds[row_num]; + int upper_bound; + + if (upper_bounds[row_num] == 32765) { + upper_bound = kHighsIInf;// JAJH This was inf, but that's a double + } + + //non-zero indices and values. + std::vector indices; + std::vector values; + + for (int i = 0; i < num_var; ++i) { + int coeff = row[i]; + + // If coefficient is non-zero, update indices and values. + if (coeff != 0) { + indices.push_back(i); + values.push_back(coeff); + } + } + + // Add row to HiGHS. + int num_nz = indices.size(); + return_status = highs.addRow(lower_bound, upper_bound, num_nz, &indices[0], &values[0]); + } + + return return_status; +}; + + +void presolveSingleProblem( + Highs& highs, + const std::vector> problem_matrix, + const std::vector lower_bounds, + const std::vector upper_bounds + ) { + + // Useful references to HiGHS variables. + const HighsInfo& info = highs.getInfo(); + const HighsLp& lp = highs.getLp(); + const HighsLp& presolved_lp = highs.getPresolvedLp(); + const HighsSolution& solution = highs.getSolution(); + // Convenient short-hand for the number of rows + const int num_row = lp.num_row_; + + // Define problem into HiGHS. + HighsStatus return_status = defineLp(highs, problem_matrix, lower_bounds, upper_bounds); + assert(return_status == HighsStatus::kOk); + + return_status = highs.presolve(); + assert(return_status == HighsStatus::kOk); + + // If problem is reduced to empty + if (!(presolved_lp.num_row_+presolved_lp.num_col_)) { + reduced_to_empty_count += 1; + std::vector problem_presolve_rules = reportAndLogPresolveLog(highs); + + if (problem_presolve_rules.size() == 2) { + if (checkVectorContainsString(problem_presolve_rules, "Singleton row") && checkVectorContainsString(problem_presolve_rules, "Free col substitution")) { + printLP(problem_matrix, lower_bounds, upper_bounds); + } + } + } +}; + + +void presolveProblems(int problems_count) { + // Instantiate HiGHS and define variable for + // HiGHS status. + Highs highs; + HighsStatus return_status; + + // Set HiGHS running options. + return_status = highs.setOptionValue("output_flag", false); + assert(return_status == HighsStatus::kOk); + highs.setOptionValue("presolve_log_report", true); + + int no_rules_off = 0; + int free_col_substitution_rule_off = 256; + int doubleton_equation_rule_off = 512; // Doubleton equation rule off + int aggregator_rule_off = 4096; // Aggregator rule off + int parallel_rows_rule_off = 8192; // Parallel rows and columns rule off + + int presolve_rules_off = 0; + std::vector rules_off = {aggregator_rule_off, doubleton_equation_rule_off}; + + // Setting off rules in rules off array. + for (int rule_off : rules_off) { + presolve_rules_off += rule_off; + } + return_status = highs.setOptionValue("presolve_rule_off", presolve_rules_off); + assert(return_status == HighsStatus::kOk); + + // Instantiate reader. + utils::Reader reader; + + // Start filestream to pass to reader. + std::fstream problems_filestream; + problems_filestream.open(test_problems, std::ios::in); + + // problem_matrix, upper_bounds and + // lower_bounds references to the corresponding vector in reader, + // otherwise the data are copied every time + std::vector>& problem_matrix = reader.problem_matrix_; + std::vector& upper_bounds = reader.upper_bounds_; + std::vector& lower_bounds = reader.lower_bounds_; + + int num_empty_problems = 0; + + for (int n = 0; n < problems_count; n++) { + if (n % 1000 == 0) printf("Reading problem %6d\n", n); + + // Reading problem. + reader.readNextProblem(problems_filestream); + + // Ignore test problems with no rows + int num_row = problem_matrix.size(); + if (num_row <= 0) { + assert(num_row == 0); + num_empty_problems++; + continue; + } + + // If any problems have no variables, + // flag them up with assert and ignore them + int num_var = problem_matrix[0].size(); + assert(num_var > 0); + if (num_var <= 0) { + assert(num_var == 0); + printf("Ignore test problems with %d variables\n", num_var); + continue; + } + + if (print_problems) { + std::cout<<" "<(elapsed).count(); + + std::cout<<" "<