diff --git a/cmake/FindHipoDeps.cmake b/cmake/FindHipoDeps.cmake index 1660c4b7577..cf7f8218e7b 100644 --- a/cmake/FindHipoDeps.cmake +++ b/cmake/FindHipoDeps.cmake @@ -31,6 +31,11 @@ endfunction() function(highs_configure_blas_metadata) set(HIGHS_BLAS_COMPILE_DEFINITION "" PARENT_SCOPE) + string(TOLOWER "${BLAS_LIBRARIES}" BLAS_LIBRARIES_LOWER) + if(BLAS_LIBRARIES_LOWER MATCHES "openblas") + set(BLA_VENDOR OpenBLAS) + endif() + if(OpenBLAS_FOUND OR OPENBLAS_LIB OR BLA_VENDOR MATCHES "OpenBLAS") set(HIGHS_BLAS_VENDOR OpenBLAS PARENT_SCOPE) set(HIGHS_BLAS_VERSION "${OpenBLAS_VERSION}" PARENT_SCOPE) diff --git a/extern/HighsExtrasApiBinding.h b/extern/HighsExtrasApiBinding.h index 54a83cab013..d8be6f3df09 100644 --- a/extern/HighsExtrasApiBinding.h +++ b/extern/HighsExtrasApiBinding.h @@ -15,7 +15,6 @@ #include - // provide metadata info for each feature struct HighsExtrasFeatureInfo { HighsExtrasFeatureInfo(const char* provider_ = nullptr, diff --git a/extern/HighsExtrasExternalDeps.h b/extern/HighsExtrasExternalDeps.h index 80a9d8cc147..035b08eabb8 100644 --- a/extern/HighsExtrasExternalDeps.h +++ b/extern/HighsExtrasExternalDeps.h @@ -184,8 +184,8 @@ struct blas : extras_feature<1> { lda, B, ldb); } - static void openblas_set_num_threads(int num_threads) { - impl::template fn<11>()(num_threads); + static int openblas_set_num_threads(int num_threads) { + return impl::template fn<11>()(num_threads); } }; diff --git a/extern/blas/myblas.cpp b/extern/blas/myblas.cpp index 0b4b1e76716..556ccf58b01 100644 --- a/extern/blas/myblas.cpp +++ b/extern/blas/myblas.cpp @@ -1,7 +1,10 @@ #include "mycblas.h" -void highs_openblas_set_num_threads(int num_threads) { +int highs_openblas_set_num_threads(int num_threads) { #if defined(HIPO_USES_OPENBLAS) openblas_set_num_threads(num_threads); + return openblas_get_num_threads(); +#else + return -1; #endif } diff --git a/extern/blas/mycblas.h b/extern/blas/mycblas.h index fe1c449367d..d61971d1307 100644 --- a/extern/blas/mycblas.h +++ b/extern/blas/mycblas.h @@ -107,9 +107,10 @@ extern "C" { #if defined(HIPO_USES_OPENBLAS) void openblas_set_num_threads(int num_threads); +int openblas_get_num_threads(void); #endif -void highs_openblas_set_num_threads(int num_threads); +int highs_openblas_set_num_threads(int num_threads); #ifdef __cplusplus } #endif diff --git a/highs/ipm/IpxWrapper.cpp b/highs/ipm/IpxWrapper.cpp index 4007285e3e4..461107c6018 100644 --- a/highs/ipm/IpxWrapper.cpp +++ b/highs/ipm/IpxWrapper.cpp @@ -420,6 +420,19 @@ HighsStatus solveQpHipo(HighsQpSolverObject& solver_object) { solver_object.callback_); } +static HighsInt prepareOpenBLAS(const HighsOptions& options) { + // force openblas to run in serial, for determinism and better performance + // no-op if openblas is not used + const int threads_used = HighsExtras::blas::openblas_set_num_threads(1); + if (is_substring_case(HighsExtras::blas::getInfo()->provider, "openblas") && + threads_used != 1) { + highsLogUser(options.log_options, HighsLogType::kError, + "OpenBLAS failed to set the number of threads to 1\n"); + return 1; + } + return 0; +} + HighsStatus solveHipo(const HighsOptions& options, HighsTimer& timer, const HighsLp& lp, const HighsHessian& Q, HighsBasis& highs_basis, HighsSolution& highs_solution, @@ -457,9 +470,10 @@ HighsStatus solveHipo(const HighsOptions& options, HighsTimer& timer, // Indicate that no imprecise solution has (yet) been found resetModelStatusAndHighsInfo(model_status, highs_info); - // force openblas to run in serial, for determinism and better performance - // no-op if openblas is not used - HighsExtras::blas::openblas_set_num_threads(1); + if (prepareOpenBLAS(options)) { + model_status = HighsModelStatus::kSolveError; + return HighsStatus::kError; + } // Create solver instance hipo::Solver hipo{}; diff --git a/highs/ipm/hipo/ipm/FactorHighsSolver.cpp b/highs/ipm/hipo/ipm/FactorHighsSolver.cpp index 9eff5d80c33..86a9825c1fa 100644 --- a/highs/ipm/hipo/ipm/FactorHighsSolver.cpp +++ b/highs/ipm/hipo/ipm/FactorHighsSolver.cpp @@ -1,6 +1,5 @@ #include "FactorHighsSolver.h" -#include #include #include "HighsExternalApi.h" @@ -592,15 +591,11 @@ void FactorHighsSolver::setParallelBeforeSymbolic() { parallel_NE_values_default); } -static bool usingAppleBlas() { - return strstr(HighsExtras::blas::getInfo()->provider, "Apple") != nullptr; -} - void FactorHighsSolver::setParallelAfterSymbolic() { bool parallel_tree = false; bool parallel_node = false; - if (usingAppleBlas()) { + if (is_substring_case(HighsExtras::blas::getInfo()->provider, "apple")) { // Blas on Apple do not work well with parallel_node, but parallel_tree // seems to always be beneficial. parallel_node = false; diff --git a/highs/util/stringutil.h b/highs/util/stringutil.h index 228a2be9b37..5aeaf90b658 100644 --- a/highs/util/stringutil.h +++ b/highs/util/stringutil.h @@ -146,4 +146,22 @@ bool is_end(std::string& str, size_t end, std::string first_word(std::string& str, size_t start); size_t first_word_end(std::string& str, size_t start); +#if defined(_WIN32) || defined(_WIN64) +#include // _strnicmp +#else +#include // strcasestr +#endif + +// check if rhs is a substring of lhs, case insensitive +inline bool is_substring_case(const char* lhs, const char* rhs) { +#if defined(_WIN32) || defined(_WIN64) + size_t len = strlen(rhs); + for (; *lhs != '\0'; ++lhs) + if (_strnicmp(lhs, rhs, len) == 0) return true; + return false; +#else + return strcasestr(lhs, rhs) != nullptr; +#endif +} + #endif