From f5550bccd35019c6211736be25efec2528ba1719 Mon Sep 17 00:00:00 2001 From: Tamar DeWilde Date: Fri, 26 Jun 2026 03:05:08 -0700 Subject: [PATCH 1/2] Add external ReSolve interface --- CMakeLists.txt | 13 +- cmake/HiOpConfig.cmake.in | 6 + src/Drivers/Sparse/CMakeLists.txt | 14 +- src/Drivers/Sparse/NlpSparseEx2Driver.cpp | 43 +- src/Drivers/Sparse/NlpSparseRajaEx2Driver.cpp | 89 +- src/Interface/hiop_defs.hpp.in | 1 + src/LinAlg/CMakeLists.txt | 13 +- src/LinAlg/hiopLinSolverSparseEVLOSER.cpp | 40 + src/LinAlg/hiopLinSolverSparseEVLOSER.hpp | 116 ++ .../hiopLinSolverSparseEVLOSERExternal.cpp | 1078 +++++++++++++++++ .../hiopLinSolverSparseEVLOSERProvider.hpp | 48 + src/Optimization/hiopDualsUpdater.cpp | 38 +- src/Optimization/hiopKKTLinSysSparse.cpp | 93 ++ src/Utils/hiopOptions.cpp | 36 +- 14 files changed, 1596 insertions(+), 32 deletions(-) create mode 100644 src/LinAlg/hiopLinSolverSparseEVLOSER.cpp create mode 100644 src/LinAlg/hiopLinSolverSparseEVLOSER.hpp create mode 100644 src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp create mode 100644 src/LinAlg/hiopLinSolverSparseEVLOSERProvider.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 84e4f64..2f81438 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,12 @@ cmake_dependent_option( HIOP_USE_RESOLVE "Build with cuSolver LU support" ON "HIOP_USE_CUDA" OFF ) +option( + HIOP_USE_EVLOSER + "Build with external ReSolve sparse solver support" + OFF +) + add_library(hiop_tpl INTERFACE) add_library(hiop_options INTERFACE) add_library(hiop_warnings INTERFACE) @@ -394,14 +400,15 @@ if(HIOP_SPARSE) endif() endif(HIOP_USE_GINKGO) - if(NOT HIOP_USE_COINHSL AND NOT HIOP_USE_STRUMPACK AND NOT HIOP_USE_PARDISO AND NOT HIOP_USE_GINKGO) + if(NOT HIOP_USE_COINHSL AND NOT HIOP_USE_STRUMPACK AND NOT HIOP_USE_PARDISO AND NOT HIOP_USE_GINKGO AND NOT HIOP_USE_EVLOSER) set(HIOP_SPARSE OFF CACHE BOOL "Build without sparse linear algebra" FORCE) - message(STATUS "Cannot find COINHSL, STRUMPACK, PARDISO nor GINKGO for sparse linear algebra, and the option HIOP_SPARSE will be disabled") -endif(NOT HIOP_USE_COINHSL AND NOT HIOP_USE_STRUMPACK AND NOT HIOP_USE_PARDISO AND NOT HIOP_USE_GINKGO) + message(STATUS "Cannot find COINHSL, STRUMPACK, PARDISO, GINKGO, or external ReSolve support for sparse linear algebra, and the option HIOP_SPARSE will be disabled") +endif() else(HIOP_SPARSE) set(HIOP_USE_COINHSL OFF CACHE BOOL "Build without COINHSL" FORCE) set(HIOP_USE_STRUMPACK OFF CACHE BOOL "Build without STRUMPACK" FORCE) set(HIOP_USE_RESOLVE OFF CACHE BOOL "Build without cuSOLVER LU module" FORCE) + set(HIOP_USE_EVLOSER OFF CACHE BOOL "Build without EVLOSER" FORCE) set(HIOP_USE_PARDISO OFF CACHE BOOL "Build without PARDISO" FORCE) set(HIOP_USE_GINKGO OFF CACHE BOOL "Build without GINKGO" FORCE) endif(HIOP_SPARSE) diff --git a/cmake/HiOpConfig.cmake.in b/cmake/HiOpConfig.cmake.in index 9c146fc..5e68870 100644 --- a/cmake/HiOpConfig.cmake.in +++ b/cmake/HiOpConfig.cmake.in @@ -1,6 +1,12 @@ # Author(s): # - Cameron Rutherford +include(CMakeFindDependencyMacro) + +if(@HIOP_USE_EVLOSER@ AND NOT TARGET ReSolve::ReSolve) + find_dependency(ReSolve CONFIG) +endif() + include( "${CMAKE_CURRENT_LIST_DIR}/HiOpTargets.cmake" ) # Configure TPLs if not already in an export set diff --git a/src/Drivers/Sparse/CMakeLists.txt b/src/Drivers/Sparse/CMakeLists.txt index a1506b2..2144163 100644 --- a/src/Drivers/Sparse/CMakeLists.txt +++ b/src/Drivers/Sparse/CMakeLists.txt @@ -18,7 +18,7 @@ add_executable(NlpSparseEx4.exe NlpSparseEx4.cpp NlpSparseEx4Driver.cpp) target_link_libraries(NlpSparseEx4.exe HiOp::HiOp) if(HIOP_USE_RAJA) - if(HIOP_USE_GPU AND HIOP_USE_CUDA AND HIOP_USE_RESOLVE) + if(HIOP_USE_GPU AND HIOP_USE_CUDA AND (HIOP_USE_RESOLVE OR HIOP_USE_EVLOSER)) set_source_files_properties( NlpSparseRajaEx2.cpp NlpSparseRajaEx2Driver.cpp @@ -83,9 +83,15 @@ if(HIOP_USE_GINKGO) endif(HIOP_USE_HIP) endif(HIOP_USE_GINKGO) -if(HIOP_USE_RAJA AND HIOP_USE_GPU AND HIOP_USE_CUDA AND HIOP_USE_RESOLVE) - add_test(NAME NlpSparseRaja2_1 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_glu") - add_test(NAME NlpSparseRaja2_2 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_rf") +if(HIOP_USE_RAJA AND HIOP_USE_GPU AND HIOP_USE_CUDA) + if(HIOP_USE_RESOLVE) + add_test(NAME NlpSparseRaja2_1 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_glu") + add_test(NAME NlpSparseRaja2_2 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_rf") + endif() + + if(HIOP_USE_EVLOSER) + add_test(NAME NlpSparseRaja2_EVLOSER_RF COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-evloser_cuda_rf") + endif() endif() add_test(NAME NlpSparse3_1 COMMAND ${RUNCMD} "$" "500" "-selfcheck") diff --git a/src/Drivers/Sparse/NlpSparseEx2Driver.cpp b/src/Drivers/Sparse/NlpSparseEx2Driver.cpp index e664635..6b65117 100644 --- a/src/Drivers/Sparse/NlpSparseEx2Driver.cpp +++ b/src/Drivers/Sparse/NlpSparseEx2Driver.cpp @@ -2,6 +2,7 @@ #include "hiopNlpFormulation.hpp" #include "hiopAlgFilterIPM.hpp" +#include #include #include @@ -16,6 +17,7 @@ static bool parse_arguments(int argc, bool& inertia_free, bool& use_cusolver, bool& use_resolve, + bool& use_evloser, bool& use_ginkgo, bool& use_ginkgo_cuda, bool& use_ginkgo_hip) @@ -25,6 +27,7 @@ static bool parse_arguments(int argc, inertia_free = false; use_cusolver = false; use_resolve = false; + use_evloser = false; use_ginkgo = false; use_ginkgo_cuda = false; use_ginkgo_cuda = false; @@ -41,6 +44,8 @@ static bool parse_arguments(int argc, inertia_free = true; } else if(std::string(argv[4]) == "-cusolver") { use_cusolver = true; + } else if(std::string(argv[4]) == "-evloser") { + use_evloser = true; } else if(std::string(argv[4]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[4]) == "-ginkgo_cuda") { @@ -64,6 +69,8 @@ static bool parse_arguments(int argc, inertia_free = true; } else if(std::string(argv[3]) == "-cusolver") { use_cusolver = true; + } else if(std::string(argv[3]) == "-evloser") { + use_evloser = true; } else if(std::string(argv[3]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[3]) == "-ginkgo_cuda") { @@ -87,6 +94,8 @@ static bool parse_arguments(int argc, inertia_free = true; } else if(std::string(argv[2]) == "-cusolver") { use_cusolver = true; + } else if(std::string(argv[2]) == "-evloser") { + use_evloser = true; } else if(std::string(argv[2]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[2]) == "-ginkgo_cuda") { @@ -110,6 +119,8 @@ static bool parse_arguments(int argc, inertia_free = true; } else if(std::string(argv[1]) == "-cusolver") { use_cusolver = true; + } else if(std::string(argv[1]) == "-evloser") { + use_evloser = true; } else if(std::string(argv[1]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[1]) == "-ginkgo_cuda") { @@ -138,6 +149,14 @@ static bool parse_arguments(int argc, } #endif +#ifndef HIOP_USE_EVLOSER + if(use_evloser) { + printf("HiOp built without EVLOSER support. "); + printf("Using default linear solver ...\n"); + use_evloser = false; + } +#endif + // Use cuSOLVER's LU factorization, if it was configured #ifdef HIOP_USE_RESOLVE if(use_cusolver) { @@ -146,9 +165,9 @@ static bool parse_arguments(int argc, #endif // If cuSOLVER was selected, but inertia free approach was not, add inertia-free - if(use_cusolver && !(inertia_free)) { + if((use_cusolver || use_evloser) && !(inertia_free)) { inertia_free = true; - printf("LU solver from cuSOLVER library requires inertia free approach. "); + printf("Selected LU sparse solver requires inertia free approach. "); printf("Enabling now ...\n"); } @@ -182,6 +201,7 @@ static void usage(const char* exeName) " '-selfcheck': compares the optimal objective with a previously saved value for the " "problem specified by 'problem_size'. [optional]\n"); printf(" '-cusolver': use cuSOLVER linear solver [optional]\n"); + printf(" '-evloser': use EVLOSER linear solver [optional]\n"); printf(" '-ginkgo': use GINKGO linear solver [optional]\n"); } @@ -206,6 +226,7 @@ int main(int argc, char** argv) bool inertia_free = false; bool use_cusolver = false; bool use_resolve = false; + bool use_evloser = false; bool use_ginkgo = false; bool use_ginkgo_cuda = false; bool use_ginkgo_hip = false; @@ -216,6 +237,7 @@ int main(int argc, char** argv) inertia_free, use_cusolver, use_resolve, + use_evloser, use_ginkgo, use_ginkgo_cuda, use_ginkgo_hip)) { @@ -243,16 +265,20 @@ int main(int argc, char** argv) if(inertia_free) { nlp.options->SetStringValue("fact_acceptor", "inertia_free"); } - if(use_resolve) { + if(use_resolve || use_evloser) { nlp.options->SetStringValue("duals_init", "zero"); nlp.options->SetStringValue("linsol_mode", "speculative"); - nlp.options->SetStringValue("linear_solver_sparse", "resolve"); + nlp.options->SetStringValue( + "linear_solver_sparse", + use_evloser ? "evloser" : "resolve"); + nlp.options->SetIntegerValue("ir_outer_maxit", 0); +#if defined(HIOP_USE_CUDA) || defined(HIOP_USE_HIP) nlp.options->SetStringValue("resolve_refactorization", "rf"); nlp.options->SetStringValue("compute_mode", "hybrid"); - nlp.options->SetIntegerValue("ir_outer_maxit", 0); nlp.options->SetIntegerValue("ir_inner_conv_cond", 2); nlp.options->SetStringValue("ir_inner_gs_scheme", "cgs2"); nlp.options->SetNumericValue("ir_inner_tol", 1e-8); +#endif } if(use_ginkgo) { nlp.options->SetStringValue("linsol_mode", "speculative"); @@ -361,7 +387,12 @@ static bool self_check(size_type n, double objval, const bool inertia_free) for(int it = 0; it < num_n_saved; it++) { if(n_saved[it] == n) { found = true; - if(fabs((objval_saved[it] - objval) / (1 + objval_saved[it])) > relerr) { + const double error = + std::fabs((objval_saved[it] - objval) / + (1 + objval_saved[it])); + if(!std::isfinite(objval) || + !std::isfinite(error) || + error > relerr) { printf( "selfcheck failure. Objective (%18.12e) does not agree (%d digits) with the saved value (%18.12e) for n=%d.\n", objval, diff --git a/src/Drivers/Sparse/NlpSparseRajaEx2Driver.cpp b/src/Drivers/Sparse/NlpSparseRajaEx2Driver.cpp index d82aa66..c6952f3 100644 --- a/src/Drivers/Sparse/NlpSparseRajaEx2Driver.cpp +++ b/src/Drivers/Sparse/NlpSparseRajaEx2Driver.cpp @@ -20,6 +20,8 @@ static bool parse_arguments(int argc, bool& inertia_free, bool& use_resolve_cuda_glu, bool& use_resolve_cuda_rf, + bool& use_evloser_cuda_rf, + bool& use_evloser_hip_rf, bool& use_ginkgo, bool& use_ginkgo_cuda, bool& use_ginkgo_hip) @@ -29,6 +31,8 @@ static bool parse_arguments(int argc, inertia_free = false; use_resolve_cuda_glu = false; use_resolve_cuda_rf = false; + use_evloser_cuda_rf = false; + use_evloser_hip_rf = false; use_ginkgo = false; use_ginkgo_cuda = false; use_ginkgo_hip = false; @@ -47,6 +51,10 @@ static bool parse_arguments(int argc, use_resolve_cuda_glu = true; } else if(std::string(argv[4]) == "-resolve_cuda_rf") { use_resolve_cuda_rf = true; + } else if(std::string(argv[4]) == "-evloser_cuda_rf") { + use_evloser_cuda_rf = true; + } else if(std::string(argv[4]) == "-evloser_hip_rf") { + use_evloser_hip_rf = true; } else if(std::string(argv[4]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[4]) == "-ginkgo_cuda") { @@ -72,6 +80,10 @@ static bool parse_arguments(int argc, use_resolve_cuda_glu = true; } else if(std::string(argv[3]) == "-resolve_cuda_rf") { use_resolve_cuda_rf = true; + } else if(std::string(argv[3]) == "-evloser_cuda_rf") { + use_evloser_cuda_rf = true; + } else if(std::string(argv[3]) == "-evloser_hip_rf") { + use_evloser_hip_rf = true; } else if(std::string(argv[3]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[3]) == "-ginkgo_cuda") { @@ -97,6 +109,10 @@ static bool parse_arguments(int argc, use_resolve_cuda_glu = true; } else if(std::string(argv[2]) == "-resolve_cuda_rf") { use_resolve_cuda_rf = true; + } else if(std::string(argv[2]) == "-evloser_cuda_rf") { + use_evloser_cuda_rf = true; + } else if(std::string(argv[2]) == "-evloser_hip_rf") { + use_evloser_hip_rf = true; } else if(std::string(argv[2]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[2]) == "-ginkgo_cuda") { @@ -122,6 +138,10 @@ static bool parse_arguments(int argc, use_resolve_cuda_glu = true; } else if(std::string(argv[1]) == "-resolve_cuda_rf") { use_resolve_cuda_rf = true; + } else if(std::string(argv[1]) == "-evloser_cuda_rf") { + use_evloser_cuda_rf = true; + } else if(std::string(argv[1]) == "-evloser_hip_rf") { + use_evloser_hip_rf = true; } else if(std::string(argv[1]) == "-ginkgo") { use_ginkgo = true; } else if(std::string(argv[1]) == "-ginkgo_cuda") { @@ -153,21 +173,54 @@ static bool parse_arguments(int argc, printf("Using default instead of ReSolve ...\n"); use_resolve_cuda_rf = false; } + if(use_evloser_cuda_rf) { + printf("HiOp built without CUDA support. "); + printf("Using default instead of EVLOSER ...\n"); + use_evloser_cuda_rf = false; + } +#endif + +// EVLOSER HIP RF requires HIP support. +#ifndef HIOP_USE_HIP + if(use_evloser_hip_rf) { + printf("HiOp built without HIP support. "); + printf("Using default instead of EVLOSER ...\n"); + use_evloser_hip_rf = false; + } #endif // If ReSolve was selected, but inertia free approach was not, add inertia-free - if((use_resolve_cuda_glu || use_resolve_cuda_rf) && !(inertia_free)) { + // EVLOSER RF has the same inertia-free requirement as the ReSolve sparse-LU path. + if((use_resolve_cuda_glu || use_resolve_cuda_rf || use_evloser_cuda_rf || use_evloser_hip_rf) && !(inertia_free)) { inertia_free = true; printf("LU solver from ReSolve library requires inertia free approach. "); printf("Enabling now ...\n"); } - if(use_resolve_cuda_glu && use_resolve_cuda_rf) { + // GLU and RF still share one refactorization option, so keep this conflict check explicit. + if(use_resolve_cuda_glu && (use_resolve_cuda_rf || use_evloser_cuda_rf || use_evloser_hip_rf)) { use_resolve_cuda_rf = false; - printf("You can select either GLU or Rf refactorization with ReSolve, not both. "); + use_evloser_cuda_rf = false; + use_evloser_hip_rf = false; + printf("You can select either GLU or Rf refactorization, not both. "); printf("Using default GLU refactorization ...\n"); } + // ReSolve RF and EVLOSER RF select different backend classes, so only one should be active. + if(use_resolve_cuda_rf && (use_evloser_cuda_rf || use_evloser_hip_rf)) { + use_evloser_cuda_rf = false; + use_evloser_hip_rf = false; + printf("You can select either ReSolve or EVLOSER, not both. "); + printf("Using ReSolve ...\n"); + } + + // EVLOSER has separate CUDA and HIP RF flags because the HIP path disables IR below. + if(use_evloser_cuda_rf && use_evloser_hip_rf) { + use_evloser_hip_rf = false; + printf("You can select either CUDA RF or HIP RF with EVLOSER, not both. "); + printf("Using CUDA RF ...\n"); + } + // If Ginkgo is not available, de-select it. #ifndef HIOP_USE_GINKGO if(use_ginkgo) { @@ -198,10 +251,16 @@ static void usage(const char* exeName) " '-selfcheck': compares the optimal objective with a previously saved value for the " "problem specified by 'problem_size'. [optional]\n"); printf( - " '-use_resolve_cuda_glu': use ReSolve linear solver with KLU factorization and cusolverGLU refactorization " + " '-resolve_cuda_glu': use ReSolve linear solver with KLU factorization and cusolverGLU refactorization " "[optional]\n"); printf( - " '-use_resolve_cuda_rf' : use ReSolve linear solver with KLU factorization and cusolverRf refactorization " + " '-resolve_cuda_rf' : use ReSolve linear solver with KLU factorization and cusolverRf refactorization " + "[optional]\n"); + printf( + " '-evloser_cuda_rf' : use EVLOSER linear solver with KLU factorization and cusolverRf refactorization " + "[optional]\n"); + printf( + " '-evloser_hip_rf' : use EVLOSER linear solver with KLU factorization and hipsolverRf refactorization " "[optional]\n"); printf(" '-ginkgo': use GINKGO linear solver [optional]\n"); } @@ -235,6 +294,8 @@ int main(int argc, char** argv) bool inertia_free = false; bool use_resolve_cuda_glu = false; bool use_resolve_cuda_rf = false; + bool use_evloser_cuda_rf = false; + bool use_evloser_hip_rf = false; bool use_ginkgo = false; bool use_ginkgo_cuda = false; bool use_ginkgo_hip = false; @@ -245,6 +306,8 @@ int main(int argc, char** argv) inertia_free, use_resolve_cuda_glu, use_resolve_cuda_rf, + use_evloser_cuda_rf, + use_evloser_hip_rf, use_ginkgo, use_ginkgo_cuda, use_ginkgo_hip)) { @@ -270,12 +333,24 @@ int main(int argc, char** argv) // only support cusolverLU right now, 2023.02.28 // lsq initialization of the duals fails for this example since the Jacobian is rank deficient // use zero initialization - nlp.options->SetStringValue("linear_solver_sparse", "resolve"); - if(use_resolve_cuda_rf) { + // EVLOSER uses the same refactorization option string; the solver name selects the backend. + if(use_evloser_cuda_rf || use_evloser_hip_rf) { + nlp.options->SetStringValue("linear_solver_sparse", "evloser"); + } else { + nlp.options->SetStringValue("linear_solver_sparse", "resolve"); + } + if(use_resolve_cuda_rf || use_evloser_cuda_rf) { nlp.options->SetStringValue("resolve_refactorization", "rf"); nlp.options->SetIntegerValue("ir_inner_maxit", 20); nlp.options->SetIntegerValue("ir_outer_maxit", 0); } + + // HIP EVLOSER RF currently runs without iterative refinement. + if(use_evloser_hip_rf) { + nlp.options->SetStringValue("resolve_refactorization", "rf"); + nlp.options->SetIntegerValue("ir_inner_maxit", 0); + nlp.options->SetIntegerValue("ir_outer_maxit", 0); + } nlp.options->SetStringValue("duals_init", "zero"); nlp.options->SetStringValue("mem_space", "device"); nlp.options->SetStringValue("fact_acceptor", "inertia_free"); diff --git a/src/Interface/hiop_defs.hpp.in b/src/Interface/hiop_defs.hpp.in index 68570c8..ec47a2c 100644 --- a/src/Interface/hiop_defs.hpp.in +++ b/src/Interface/hiop_defs.hpp.in @@ -11,6 +11,7 @@ #cmakedefine HIOP_USE_STRUMPACK #cmakedefine HIOP_USE_PARDISO #cmakedefine HIOP_USE_RESOLVE +#cmakedefine HIOP_USE_EVLOSER #cmakedefine HIOP_USE_GINKGO #cmakedefine HIOP_USE_AXOM #define HIOP_VERSION "@PROJECT_VERSION@" diff --git a/src/LinAlg/CMakeLists.txt b/src/LinAlg/CMakeLists.txt index 8710490..cb86a27 100644 --- a/src/LinAlg/CMakeLists.txt +++ b/src/LinAlg/CMakeLists.txt @@ -12,6 +12,7 @@ set(hiopLinAlg_INTERFACE_HEADERS hiopLinSolverSparseSTRUMPACK.hpp hiopLinSolverSparsePARDISO.hpp hiopLinSolverSparseReSolve.hpp + hiopLinSolverSparseEVLOSER.hpp hiopLinSolverUMFPACKZ.hpp hiopLinSolverCholCuSparse.hpp hiopMatrix.hpp @@ -101,6 +102,11 @@ set(hiopLinAlg_CUSOLVER_LU_SRC hiopLinSolverSparseReSolve.cpp ) +set(hiopLinAlg_EVLOSER_SRC + hiopLinSolverSparseEVLOSER.cpp + hiopLinSolverSparseEVLOSERExternal.cpp +) + set(hiopLinAlg_CUSOLVER_CHOL_SRC hiopLinSolverCholCuSparse.cpp ) @@ -153,11 +159,16 @@ if(HIOP_SPARSE) list(APPEND hiopLinAlg_SRC ${hiopLinAlg_CUSOLVER_LU_SRC}) set_source_files_properties(${hiopLinAlg_CUSOLVER_LU_SRC} PROPERTIES LANGUAGE CUDA) endif(HIOP_USE_RESOLVE) + if(HIOP_USE_EVLOSER) + find_package(ReSolve CONFIG REQUIRED) + list(APPEND hiopLinAlg_SRC ${hiopLinAlg_EVLOSER_SRC}) + target_link_libraries(hiop_tpl INTERFACE ReSolve::ReSolve) + endif() if(HIOP_USE_CUDA) list(APPEND hiopLinAlg_SRC ${hiopLinAlg_CUSOLVER_CHOL_SRC}) set_source_files_properties(${hiopLinAlg_CUSOLVER_CHOL_SRC} PROPERTIES LANGUAGE CUDA) endif(HIOP_USE_CUDA) - + if(HIOP_USE_GINKGO) list(APPEND hiopLinAlg_SRC ${hiopLinAlg_Ginkgo_SRC}) endif(HIOP_USE_GINKGO) diff --git a/src/LinAlg/hiopLinSolverSparseEVLOSER.cpp b/src/LinAlg/hiopLinSolverSparseEVLOSER.cpp new file mode 100644 index 0000000..c3eb602 --- /dev/null +++ b/src/LinAlg/hiopLinSolverSparseEVLOSER.cpp @@ -0,0 +1,40 @@ +#include "hiopLinSolverSparseEVLOSER.hpp" +#include "hiopLinSolverSparseEVLOSERProvider.hpp" + +#include + +namespace hiop +{ + +hiopLinSolverSymSparseEVLOSER::hiopLinSolverSymSparseEVLOSER( + const int& n, + const int& nnz, + hiopNlpFormulation* nlp) + : hiopLinSolverSymSparse(n, nnz, nlp), + provider_{create_hiop_evloser_provider(n, nnz, nlp)} +{ + assert(provider_ != nullptr); +} + +hiopLinSolverSymSparseEVLOSER::~hiopLinSolverSymSparseEVLOSER() +{ + delete provider_; + provider_ = nullptr; +} + +int hiopLinSolverSymSparseEVLOSER::matrixChanged() +{ + assert(provider_ != nullptr); + assert(M_ != nullptr); + + return provider_->matrixChanged(*M_); +} + +bool hiopLinSolverSymSparseEVLOSER::solve(hiopVector& x) +{ + assert(provider_ != nullptr); + + return provider_->solve(x); +} + +} // namespace hiop diff --git a/src/LinAlg/hiopLinSolverSparseEVLOSER.hpp b/src/LinAlg/hiopLinSolverSparseEVLOSER.hpp new file mode 100644 index 0000000..350bb69 --- /dev/null +++ b/src/LinAlg/hiopLinSolverSparseEVLOSER.hpp @@ -0,0 +1,116 @@ +// +// This file is part of HiOp. For details, see https://github.com/LLNL/hiop. +// HiOp is released under the BSD 3-clause license +// (https://opensource.org/licenses/BSD-3-Clause). Please also read “Additional +// BSD Notice” below. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// i. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the disclaimer below. ii. Redistributions in +// binary form must reproduce the above copyright notice, this list of +// conditions and the disclaimer (as noted below) in the documentation and/or +// other materials provided with the distribution. +// iii. Neither the name of the LLNS/LLNL nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, +// THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Additional BSD Notice +// 1. This notice is required to be provided under our contract with the U.S. +// Department of Energy (DOE). This work was produced at Lawrence Livermore +// National Laboratory under Contract No. DE-AC52-07NA27344 with the DOE. +// 2. Neither the United States Government nor Lawrence Livermore National +// Security, LLC nor any of their employees, makes any warranty, express or +// implied, or assumes any liability or responsibility for the accuracy, +// completeness, or usefulness of any information, apparatus, product, or +// process disclosed, or represents that its use would not infringe +// privately-owned rights. +// 3. Also, reference herein to any specific commercial products, process, or +// services by trade name, trademark, manufacturer or otherwise does not +// necessarily constitute or imply its endorsement, recommendation, or favoring +// by the United States Government or Lawrence Livermore National Security, +// LLC. The views and opinions of authors expressed herein do not necessarily +// state or reflect those of the United States Government or Lawrence Livermore +// National Security, LLC, and shall not be used for advertising or product +// endorsement purposes. + +/** + * @file hiopLinSolverSparseEVLOSER.hpp + * + * @author Kasia Swirydowicz , PNNL + * @author Slaven Peles , ORNL + * + */ + +#ifndef HIOP_LINSOLVER_EVLOSER +#define HIOP_LINSOLVER_EVLOSER + +#include "hiopLinSolver.hpp" + +#include + +/** + * @brief Implements the HiOp sparse linear solver interface using the + * configured EVLOSER backend provider. + * + * The concrete provider is selected when HiOp is configured. Backend-specific + * matrix storage, factorization state, and execution resources are owned by + * the provider rather than by this HiOp-facing wrapper. + * + * @ingroup LinearSolvers + */ + +namespace hiop +{ + +class hiopLinSolverSparseEVLOSERProvider; + +class hiopLinSolverSymSparseEVLOSER : public hiopLinSolverSymSparse +{ +public: + hiopLinSolverSymSparseEVLOSER(const int& n, + const int& nnz, + hiopNlpFormulation* nlp); + + virtual ~hiopLinSolverSymSparseEVLOSER(); + + /** + * @brief Notifies the configured provider that the matrix changed. + */ + virtual int matrixChanged(); + + /** + * @brief Solves a linear system. + * + * @param x On entry, the right-hand side of the system. + * + * @post On exit, `x` is overwritten with the solution. + */ + virtual bool solve(hiopVector& x); + + /** Multiple right-hand sides are not supported yet. */ + virtual bool solve(hiopMatrix& /* x */) + { + assert(false && "not yet supported"); + return false; + } + +protected: + hiopLinSolverSparseEVLOSERProvider* provider_; +}; + +} // namespace hiop + +#endif \ No newline at end of file diff --git a/src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp b/src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp new file mode 100644 index 0000000..1bf3f4f --- /dev/null +++ b/src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp @@ -0,0 +1,1078 @@ +#include "hiopLinSolverSparseEVLOSERProvider.hpp" + +#include "LinAlgFactory.hpp" +#include "hiopLinSolver.hpp" +#include "hiopMatrixSparse.hpp" +#include "hiopVector.hpp" + +#include "hiop_blasdefs.hpp" + +#include +#include +#include +#include + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) +#include +#include +#include +#elif defined(HIOP_USE_HIP) || defined(HAVE_HIP) +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace hiop +{ +namespace +{ + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) + +bool copy_device_to_host(hiopNlpFormulation* nlp, + void* destination, + const void* source, + size_t bytes, + const char* operation) +{ + const cudaError_t status = + cudaMemcpy( + destination, + source, + bytes, + cudaMemcpyDeviceToHost); + + if(status == cudaSuccess) { + return true; + } + + nlp->log->printf( + hovError, + "CUDA failure during %s: %s\n", + operation, + cudaGetErrorString(status)); + + return false; +} + +bool copy_device_to_device(hiopNlpFormulation* nlp, + void* destination, + const void* source, + size_t bytes, + const char* operation) +{ + const cudaError_t status = + cudaMemcpy( + destination, + source, + bytes, + cudaMemcpyDeviceToDevice); + + if(status == cudaSuccess) { + return true; + } + + nlp->log->printf( + hovError, + "CUDA failure during %s: %s\n", + operation, + cudaGetErrorString(status)); + + return false; +} + +#elif defined(HIOP_USE_HIP) || defined(HAVE_HIP) + +bool copy_device_to_host(hiopNlpFormulation* nlp, + void* destination, + const void* source, + size_t bytes, + const char* operation) +{ + const hipError_t status = + hipMemcpy( + destination, + source, + bytes, + hipMemcpyDeviceToHost); + + if(status == hipSuccess) { + return true; + } + + nlp->log->printf( + hovError, + "HIP failure during %s: %s\n", + operation, + hipGetErrorString(status)); + + return false; +} + +bool copy_device_to_device(hiopNlpFormulation* nlp, + void* destination, + const void* source, + size_t bytes, + const char* operation) +{ + const hipError_t status = + hipMemcpy( + destination, + source, + bytes, + hipMemcpyDeviceToDevice); + + if(status == hipSuccess) { + return true; + } + + nlp->log->printf( + hovError, + "HIP failure during %s: %s\n", + operation, + hipGetErrorString(status)); + + return false; +} + +#endif + +class hiopLinSolverSparseEVLOSERExternal final + : public hiopLinSolverSparseEVLOSERProvider +{ +public: + hiopLinSolverSparseEVLOSERExternal(const int& n, + const int& nnz, + hiopNlpFormulation* nlp); + + ~hiopLinSolverSparseEVLOSERExternal() override; + + int matrixChanged(hiopMatrixSparse& matrix) override; + bool solve(hiopVector& x) override; + +private: + int firstCall(); + int update_matrix_values(); + void compute_nnz(); + void set_csr_indices_values(); + + int reset_solver(); + hiopMatrixSparse* host_matrix() const; + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + int setup_gpu_refactorization(); +#endif + +private: + hiopNlpFormulation* nlp_; + + ReSolve::LinSolverDirectKLU* solver_; + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) + ReSolve::LinSolverDirectCuSolverRf* rf_solver_; +#elif defined(HIOP_USE_HIP) || defined(HAVE_HIP) + ReSolve::LinSolverDirectRocSolverRf* rf_solver_; + ReSolve::LinAlgWorkspaceHIP* hip_workspace_; +#endif + + ReSolve::matrix::Csr* matrix_; + ReSolve::vector::Vector* rhs_; + ReSolve::vector::Vector* solution_; + + hiopMatrixSparse* M_; + hiopMatrixSparse* M_host_; + + bool use_device_; + + int n_; + int nnz_; + int ordering_; + + int* index_convert_CSR2Triplet_host_; + int* index_convert_extra_Diag2CSR_host_; + + bool is_first_call_; + bool factorization_valid_; +}; + +hiopLinSolverSparseEVLOSERExternal:: + hiopLinSolverSparseEVLOSERExternal(const int& n, + const int& nnz, + hiopNlpFormulation* nlp) + : nlp_{nlp}, + solver_{nullptr}, +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) + rf_solver_{nullptr}, +#elif defined(HIOP_USE_HIP) || defined(HAVE_HIP) + rf_solver_{nullptr}, + hip_workspace_{nullptr}, +#endif + matrix_{nullptr}, + rhs_{nullptr}, + solution_{nullptr}, + M_{nullptr}, + M_host_{nullptr}, + use_device_{false}, + n_{n}, + nnz_{0}, + ordering_{1}, + index_convert_CSR2Triplet_host_{nullptr}, + index_convert_extra_Diag2CSR_host_{nullptr}, + is_first_call_{true}, + factorization_valid_{false} +{ + const std::string mem_space = + nlp_->options->GetString("mem_space"); + + if(mem_space == "host" || mem_space == "default") { + use_device_ = false; + } else if(mem_space == "device") { +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + use_device_ = true; + + M_host_ = + LinearAlgebraFactory::create_matrix_sparse( + "default", + n, + n, + nnz); +#else + nlp_->log->printf( + hovError, + "External EVLOSER device execution requires a CUDA or HIP " + "build.\n"); + std::abort(); +#endif + } else { + nlp_->log->printf( + hovError, + "Memory space %s is incompatible with the external EVLOSER " + "provider.\n", + mem_space.c_str()); + std::abort(); + } + + const std::string ordering = + nlp_->options->GetString( + "linear_solver_sparse_ordering"); + + if(ordering == "amd_ssparse") { + ordering_ = 0; + } else if(ordering == "colamd_ssparse") { + ordering_ = 1; + } else { + nlp_->log->printf( + hovWarning, + "Ordering %s not compatible with the external EVLOSER " + "provider, using default ...\n", + ordering.c_str()); + ordering_ = 1; + } + + const std::string factorization = + nlp_->options->GetString("resolve_factorization"); + + if(factorization != "klu") { + nlp_->log->printf( + hovWarning, + "Factorization %s not compatible with the external EVLOSER " + "provider, using KLU ...\n", + factorization.c_str()); + } + + const std::string refactorization = + nlp_->options->GetString("resolve_refactorization"); + + if(use_device_ && refactorization != "rf") { + nlp_->log->printf( + hovWarning, + "External EVLOSER device execution supports RF " + "refactorization. Using rf instead of %s.\n", + refactorization.c_str()); + } + + solver_ = new ReSolve::LinSolverDirectKLU(); + solver_->setOrdering(ordering_); + solver_->setHaltIfSingular(true); + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) + if(use_device_) { + rf_solver_ = + new ReSolve::LinSolverDirectCuSolverRf(); + } +#elif defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_) { + hip_workspace_ = + new ReSolve::LinAlgWorkspaceHIP(); + + hip_workspace_->initializeHandles(); + + rf_solver_ = + new ReSolve::LinSolverDirectRocSolverRf( + hip_workspace_); + } +#endif + + rhs_ = new ReSolve::vector::Vector(n_); + solution_ = new ReSolve::vector::Vector(n_); + + const auto vector_memory = + use_device_ + ? ReSolve::memory::DEVICE + : ReSolve::memory::HOST; + + rhs_->allocate(vector_memory); + solution_->allocate(vector_memory); + + if(rhs_->getData(vector_memory) == nullptr || + solution_->getData(vector_memory) == nullptr) { + nlp_->log->printf( + hovError, + "Failed to allocate external ReSolve vectors.\n"); + std::abort(); + } + + nlp_->log->printf( + hovSummary, + "EVLOSER provider: external ReSolve\n"); + + nlp_->log->printf( + hovSummary, + "Ordering: %d\n", + ordering_); + + nlp_->log->printf( + hovSummary, + "Factorization: klu\n"); + + if(use_device_) { + nlp_->log->printf( + hovSummary, + "Refactorization: rf\n"); + } + + nlp_->log->printf( + hovSummary, + "Use IR: no\n"); +} + +hiopLinSolverSparseEVLOSERExternal:: + ~hiopLinSolverSparseEVLOSERExternal() +{ +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) + delete rf_solver_; + rf_solver_ = nullptr; +#elif defined(HIOP_USE_HIP) || defined(HAVE_HIP) + delete rf_solver_; + rf_solver_ = nullptr; + + delete hip_workspace_; + hip_workspace_ = nullptr; +#endif + + delete solver_; + solver_ = nullptr; + + delete matrix_; + matrix_ = nullptr; + + delete rhs_; + rhs_ = nullptr; + + delete solution_; + solution_ = nullptr; + + delete M_host_; + M_host_ = nullptr; + + delete[] index_convert_CSR2Triplet_host_; + index_convert_CSR2Triplet_host_ = nullptr; + + delete[] index_convert_extra_Diag2CSR_host_; + index_convert_extra_Diag2CSR_host_ = nullptr; +} + +int hiopLinSolverSparseEVLOSERExternal::matrixChanged( + hiopMatrixSparse& matrix) +{ + M_ = &matrix; + + assert(n_ == M_->n()); + assert(M_->n() == M_->m()); + assert(n_ > 0); + + nlp_->runStats.linsolv.tmFactTime.start(); + + if(is_first_call_) { + if(firstCall() != 0) { + nlp_->runStats.linsolv.tmFactTime.stop(); + return -1; + } + } else { + if(update_matrix_values() != 0) { + nlp_->runStats.linsolv.tmFactTime.stop(); + return -1; + } + } + + int status = 0; + + if(!factorization_valid_) { + status = solver_->factorize(); + + if(status != 0) { + nlp_->log->printf( + hovWarning, + "External ReSolve KLU factorization failed. " + "Regularizing ...\n"); + + factorization_valid_ = false; + reset_solver(); + + nlp_->runStats.linsolv.tmFactTime.stop(); + return -1; + } + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_) { + status = setup_gpu_refactorization(); + + if(status == 0) { + status = rf_solver_->refactorize(); + } + + if(status != 0) { + nlp_->log->printf( + hovWarning, + "External ReSolve RF setup or initial " + "refactorization failed. Regularizing ...\n"); + + factorization_valid_ = false; + reset_solver(); + + nlp_->runStats.linsolv.tmFactTime.stop(); + return -1; + } + } +#endif + + factorization_valid_ = true; + + nlp_->log->printf( + hovScalars, + "External ReSolve KLU factorization successful.\n"); + } else { +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_) { + status = rf_solver_->refactorize(); + } else +#endif + { + status = solver_->refactorize(); + } + + if(status != 0) { + nlp_->log->printf( + hovWarning, + "External ReSolve refactorization failed. " + "Regularizing ...\n"); + + factorization_valid_ = false; + reset_solver(); + + nlp_->runStats.linsolv.tmFactTime.stop(); + return -1; + } + } + + nlp_->runStats.linsolv.tmFactTime.stop(); + return 0; +} + +bool hiopLinSolverSparseEVLOSERExternal::solve( + hiopVector& x) +{ + assert(M_ != nullptr); + assert(n_ == M_->n()); + assert(M_->n() == M_->m()); + assert(n_ > 0); + assert(x.get_size() == M_->n()); + + if(!factorization_valid_) { + nlp_->log->printf( + hovError, + "External ReSolve solve requested without a valid " + "factorization.\n"); + return false; + } + + nlp_->runStats.linsolv.tmTriuSolves.start(); + + double* x_data = x.local_data(); + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_) { + if(rhs_->copyFromExternal( + x_data, + ReSolve::memory::DEVICE, + ReSolve::memory::DEVICE) != 0) { + nlp_->log->printf( + hovError, + "Failed to copy the device right-hand side into ReSolve.\n"); + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return false; + } + + if(rf_solver_->solve(rhs_, solution_) != 0) { + nlp_->log->printf( + hovError, + "External ReSolve GPU RF solve failed.\n"); + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return false; + } + + double* solution_data = + solution_->getData(ReSolve::memory::DEVICE); + + if(solution_data == nullptr) { + nlp_->log->printf( + hovError, + "Failed to access the external ReSolve device solution.\n"); + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return false; + } + + if(!copy_device_to_device( + nlp_, + x_data, + solution_data, + sizeof(double) * n_, + "copying the ReSolve solution into HiOp")) { + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return false; + } + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return true; + } +#endif + + double* rhs_data = + rhs_->getData(ReSolve::memory::HOST); + + if(rhs_data == nullptr) { + nlp_->log->printf( + hovError, + "Failed to access the external ReSolve right-hand side.\n"); + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return false; + } + + std::copy( + x_data, + x_data + n_, + rhs_data); + + rhs_->setDataUpdated(ReSolve::memory::HOST); + + if(solver_->solve(rhs_, solution_) != 0) { + nlp_->log->printf( + hovError, + "External ReSolve KLU solve failed.\n"); + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return false; + } + + const double* solution_data = + solution_->getData(ReSolve::memory::HOST); + + if(solution_data == nullptr) { + nlp_->log->printf( + hovError, + "Failed to access the external ReSolve solution.\n"); + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return false; + } + + std::copy( + solution_data, + solution_data + n_, + x_data); + + nlp_->runStats.linsolv.tmTriuSolves.stop(); + return true; +} + +int hiopLinSolverSparseEVLOSERExternal::firstCall() +{ + assert(M_ != nullptr); + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_) { + if(!copy_device_to_host( + nlp_, + M_host_->M(), + M_->M(), + sizeof(double) * M_->numberOfNonzeros(), + "copying EVLOSER values to the host")) { + return -1; + } + + if(!copy_device_to_host( + nlp_, + M_host_->i_row(), + M_->i_row(), + sizeof(index_type) * + M_->numberOfNonzeros(), + "copying EVLOSER row indices to the host")) { + return -1; + } + + if(!copy_device_to_host( + nlp_, + M_host_->j_col(), + M_->j_col(), + sizeof(index_type) * + M_->numberOfNonzeros(), + "copying EVLOSER column indices to the host")) { + return -1; + } + } +#endif + + compute_nnz(); + + matrix_ = new ReSolve::matrix::Csr( + n_, + n_, + nnz_, + true, + true); + + if(matrix_->allocateMatrixData( + ReSolve::memory::HOST) != 0) { + nlp_->log->printf( + hovError, + "Failed to allocate the external ReSolve CSR matrix.\n"); + return -1; + } + + set_csr_indices_values(); + + if(matrix_->setUpdated( + ReSolve::memory::HOST) != 0) { + nlp_->log->printf( + hovError, + "Failed to mark the external ReSolve matrix as updated.\n"); + return -1; + } + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_ && + (((matrix_->getRowData(ReSolve::memory::DEVICE) == nullptr || + matrix_->getColData(ReSolve::memory::DEVICE) == nullptr || + matrix_->getValues(ReSolve::memory::DEVICE) == nullptr) && + matrix_->allocateMatrixData(ReSolve::memory::DEVICE) != 0) || + matrix_->syncData(ReSolve::memory::DEVICE) != 0)) { + nlp_->log->printf( + hovError, + "Failed to copy the external ReSolve matrix to device memory.\n"); + return -1; + } +#endif + + if(solver_->setup(matrix_) != 0) { + nlp_->log->printf( + hovError, + "External ReSolve KLU setup failed.\n"); + return -1; + } + + if(solver_->analyze() != 0) { + nlp_->log->printf( + hovError, + "External ReSolve KLU symbolic analysis failed.\n"); + return -1; + } + + is_first_call_ = false; + return 0; +} + +int hiopLinSolverSparseEVLOSERExternal:: + update_matrix_values() +{ + assert(M_ != nullptr); + assert(matrix_ != nullptr); + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_) { + if(!copy_device_to_host( + nlp_, + M_host_->M(), + M_->M(), + sizeof(double) * M_->numberOfNonzeros(), + "updating EVLOSER host matrix values")) { + return -1; + } + } +#endif + + hiopMatrixSparse* source = host_matrix(); + assert(source != nullptr); + + double* values = + matrix_->getValues(ReSolve::memory::HOST); + + for(int k = 0; k < nnz_; ++k) { + values[k] = + source->M()[index_convert_CSR2Triplet_host_[k]]; + } + + for(int i = 0; i < n_; ++i) { + if(index_convert_extra_Diag2CSR_host_[i] != -1) { + values[index_convert_extra_Diag2CSR_host_[i]] += + source->M()[ + source->numberOfNonzeros() - n_ + i]; + } + } + + if(matrix_->setUpdated( + ReSolve::memory::HOST) != 0) { + return -1; + } + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_ && + (((matrix_->getRowData(ReSolve::memory::DEVICE) == nullptr || + matrix_->getColData(ReSolve::memory::DEVICE) == nullptr || + matrix_->getValues(ReSolve::memory::DEVICE) == nullptr) && + matrix_->allocateMatrixData(ReSolve::memory::DEVICE) != 0) || + matrix_->syncData(ReSolve::memory::DEVICE) != 0)) { + return -1; + } +#endif + + return 0; +} + +hiopMatrixSparse* +hiopLinSolverSparseEVLOSERExternal::host_matrix() const +{ + return use_device_ ? M_host_ : M_; +} + +void hiopLinSolverSparseEVLOSERExternal::compute_nnz() +{ + hiopMatrixSparse* source = host_matrix(); + assert(source != nullptr); + + nnz_ = n_; + + for(int k = 0; + k < source->numberOfNonzeros() - n_; + ++k) { + if(source->i_row()[k] != source->j_col()[k]) { + nnz_ += 2; + } + } +} + +void hiopLinSolverSparseEVLOSERExternal:: + set_csr_indices_values() +{ + assert(M_ != nullptr); + assert(matrix_ != nullptr); + + hiopMatrixSparse* source = host_matrix(); + assert(source != nullptr); + + ReSolve::index_type* row_ptr = + matrix_->getRowData(ReSolve::memory::HOST); + + ReSolve::index_type* col_idx = + matrix_->getColData(ReSolve::memory::HOST); + + double* values = + matrix_->getValues(ReSolve::memory::HOST); + + std::fill(row_ptr, row_ptr + n_ + 1, 0); + + for(int k = 0; + k < source->numberOfNonzeros() - n_; + ++k) { + if(source->i_row()[k] != source->j_col()[k]) { + row_ptr[source->i_row()[k] + 1]++; + row_ptr[source->j_col()[k] + 1]++; + } + } + + for(int i = 0; i < n_; ++i) { + row_ptr[i + 1]++; + } + + for(int i = 1; i < n_ + 1; ++i) { + row_ptr[i] += row_ptr[i - 1]; + } + + assert(nnz_ == row_ptr[n_]); + + index_convert_CSR2Triplet_host_ = + new int[nnz_]; + + index_convert_extra_Diag2CSR_host_ = + new int[n_]; + + int* nnz_each_row_tmp = new int[n_]{0}; + + int total_nnz_tmp{0}; + int nnz_tmp{0}; + int rowID_tmp{0}; + int colID_tmp{0}; + + for(int i = 0; i < n_; ++i) { + index_convert_extra_Diag2CSR_host_[i] = -1; + } + + for(int k = 0; + k < source->numberOfNonzeros() - n_; + ++k) { + rowID_tmp = source->i_row()[k]; + colID_tmp = source->j_col()[k]; + + if(rowID_tmp == colID_tmp) { + nnz_tmp = + nnz_each_row_tmp[rowID_tmp] + + row_ptr[rowID_tmp]; + + col_idx[nnz_tmp] = colID_tmp; + values[nnz_tmp] = source->M()[k]; + + index_convert_CSR2Triplet_host_[nnz_tmp] = k; + + values[nnz_tmp] += + source->M()[ + source->numberOfNonzeros() - + n_ + + rowID_tmp]; + + index_convert_extra_Diag2CSR_host_[rowID_tmp] = + nnz_tmp; + + nnz_each_row_tmp[rowID_tmp]++; + total_nnz_tmp++; + } else { + nnz_tmp = + nnz_each_row_tmp[rowID_tmp] + + row_ptr[rowID_tmp]; + + col_idx[nnz_tmp] = colID_tmp; + values[nnz_tmp] = source->M()[k]; + + index_convert_CSR2Triplet_host_[nnz_tmp] = k; + + nnz_tmp = + nnz_each_row_tmp[colID_tmp] + + row_ptr[colID_tmp]; + + col_idx[nnz_tmp] = rowID_tmp; + values[nnz_tmp] = source->M()[k]; + + index_convert_CSR2Triplet_host_[nnz_tmp] = k; + + nnz_each_row_tmp[rowID_tmp]++; + nnz_each_row_tmp[colID_tmp]++; + total_nnz_tmp += 2; + } + } + + for(int i = 0; i < n_; ++i) { + if(nnz_each_row_tmp[i] != + row_ptr[i + 1] - row_ptr[i]) { + assert( + nnz_each_row_tmp[i] == + row_ptr[i + 1] - row_ptr[i] - 1); + + nnz_tmp = + nnz_each_row_tmp[i] + + row_ptr[i]; + + col_idx[nnz_tmp] = i; + + values[nnz_tmp] = + source->M()[ + source->numberOfNonzeros() - n_ + i]; + + index_convert_CSR2Triplet_host_[nnz_tmp] = + source->numberOfNonzeros() - n_ + i; + + total_nnz_tmp++; + + std::vector permutation( + row_ptr[i + 1] - row_ptr[i]); + + std::iota( + permutation.begin(), + permutation.end(), + 0); + + std::sort( + permutation.begin(), + permutation.end(), + [&](int a, int b) { + return col_idx[a + row_ptr[i]] < + col_idx[b + row_ptr[i]]; + }); + + reorder( + values + row_ptr[i], + permutation, + row_ptr[i + 1] - row_ptr[i]); + + reorder( + index_convert_CSR2Triplet_host_ + + row_ptr[i], + permutation, + row_ptr[i + 1] - row_ptr[i]); + + std::sort( + col_idx + row_ptr[i], + col_idx + row_ptr[i + 1]); + } + } + + assert(total_nnz_tmp == nnz_); + + delete[] nnz_each_row_tmp; +} + +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) || \ + defined(HIOP_USE_HIP) || defined(HAVE_HIP) + +int hiopLinSolverSparseEVLOSERExternal:: + setup_gpu_refactorization() +{ + assert(rf_solver_ != nullptr); + + auto* L = + dynamic_cast( + solver_->getLFactor()); + + auto* U = + dynamic_cast( + solver_->getUFactor()); + + ReSolve::index_type* P = + solver_->getPOrdering(); + + ReSolve::index_type* Q = + solver_->getQOrdering(); + + if(L == nullptr || + U == nullptr || + P == nullptr || + Q == nullptr) { + nlp_->log->printf( + hovError, + "Failed to extract KLU factors for external " + "ReSolve RF.\n"); + return -1; + } + + if(L->allocateMatrixData(ReSolve::memory::DEVICE) != 0 || + U->allocateMatrixData(ReSolve::memory::DEVICE) != 0) { + nlp_->log->printf( + hovError, + "Failed to allocate device storage for external " + "ReSolve KLU factors.\n"); + return -1; + } + + return rf_solver_->setup( + matrix_, + L, + U, + P, + Q, + rhs_); +} + +#endif + +int hiopLinSolverSparseEVLOSERExternal::reset_solver() +{ +#if defined(HIOP_USE_CUDA) || defined(HAVE_CUDA) + if(use_device_) { + delete rf_solver_; + rf_solver_ = + new ReSolve::LinSolverDirectCuSolverRf(); + } +#elif defined(HIOP_USE_HIP) || defined(HAVE_HIP) + if(use_device_) { + delete rf_solver_; + rf_solver_ = + new ReSolve::LinSolverDirectRocSolverRf( + hip_workspace_); + } +#endif + + delete solver_; + + solver_ = new ReSolve::LinSolverDirectKLU(); + solver_->setOrdering(ordering_); + solver_->setHaltIfSingular(true); + + if(solver_->setup(matrix_) != 0) { + nlp_->log->printf( + hovError, + "External ReSolve KLU recovery setup failed.\n"); + return -1; + } + + if(solver_->analyze() != 0) { + nlp_->log->printf( + hovError, + "External ReSolve KLU recovery analysis failed.\n"); + return -1; + } + + return 0; +} + +} // namespace + +hiopLinSolverSparseEVLOSERProvider* +create_hiop_evloser_provider(const int& n, + const int& nnz, + hiopNlpFormulation* nlp) +{ + return new hiopLinSolverSparseEVLOSERExternal( + n, + nnz, + nlp); +} + +} // namespace hiop \ No newline at end of file diff --git a/src/LinAlg/hiopLinSolverSparseEVLOSERProvider.hpp b/src/LinAlg/hiopLinSolverSparseEVLOSERProvider.hpp new file mode 100644 index 0000000..0d6a95c --- /dev/null +++ b/src/LinAlg/hiopLinSolverSparseEVLOSERProvider.hpp @@ -0,0 +1,48 @@ +#ifndef HIOP_LINSOLVER_SPARSE_EVLOSER_PROVIDER +#define HIOP_LINSOLVER_SPARSE_EVLOSER_PROVIDER + +namespace hiop +{ + +class hiopMatrixSparse; +class hiopNlpFormulation; +class hiopVector; + +/** + * @brief External ReSolve backend boundary for the HiOp EVLOSER sparse solver. + * + * The implementation owns ReSolve matrix storage, solver objects, execution + * resources, and matrix-conversion state. + */ +class hiopLinSolverSparseEVLOSERProvider +{ +public: + virtual ~hiopLinSolverSparseEVLOSERProvider() = default; + + /** + * @brief Updates and factorizes or refactorizes the supplied HiOp matrix. + * + * @return Zero on success or a negative value when HiOp should regularize + * the matrix. + */ + virtual int matrixChanged(hiopMatrixSparse& matrix) = 0; + + /** + * @brief Solves the current linear system in place. + * + * @param x On entry, the right-hand side. On exit, the solution. + */ + virtual bool solve(hiopVector& x) = 0; +}; + +/** + * @brief Creates the external ReSolve-backed EVLOSER implementation. + */ +hiopLinSolverSparseEVLOSERProvider* +create_hiop_evloser_provider(const int& n, + const int& nnz, + hiopNlpFormulation* nlp); + +} // namespace hiop + +#endif diff --git a/src/Optimization/hiopDualsUpdater.cpp b/src/Optimization/hiopDualsUpdater.cpp index 0ec4bbf..c08381d 100644 --- a/src/Optimization/hiopDualsUpdater.cpp +++ b/src/Optimization/hiopDualsUpdater.cpp @@ -74,6 +74,10 @@ #ifdef HIOP_USE_RESOLVE #include "hiopLinSolverSparseReSolve.hpp" #endif + +#ifdef HIOP_USE_EVLOSER +#include "hiopLinSolverSparseEVLOSER.hpp" +#endif #ifdef HIOP_USE_GINKGO #include "hiopLinSolverSparseGinkgo.hpp" #endif @@ -375,6 +379,20 @@ bool hiopDualsLsqUpdateLinsysAugSparse::instantiate_linear_solver(const char* li // compute mode CPU ///////////////////////////////////////////////////////////////////////////////////////// assert(nullptr == lin_sys_); +#ifdef HIOP_USE_EVLOSER + if(linear_solver == "evloser") { + if(fact_acceptor == "inertia_correction") { + nlp_->log->printf(hovError, + "LSQ linear solver with EVLOSER does not support inertia correction. " + "Please set option 'fact_acceptor' to 'inertia_free'.\n"); + assert(false); + return false; + } + + ss_log << "LSQ linear solver --- KKT_SPARSE_XDYcYd linsys: EVLOSER on CPU "; + lin_sys_ = new hiopLinSolverSymSparseEVLOSER(n, nnz, nlp_); + } +#endif // HIOP_USE_EVLOSER if(linear_solver == "ma57" || linear_solver == "auto") { #ifdef HIOP_USE_COINHSL ss_log << "LSQ linear solver --- KKT_SPARSE_XDYcYd linsys: MA57 "; @@ -426,25 +444,35 @@ bool hiopDualsLsqUpdateLinsysAugSparse::instantiate_linear_solver(const char* li // Under gpu compute_mode, which is work in progress, the initialization should be done only using // GPU sparse linear solvers. -#ifdef HIOP_USE_RESOLVE +#if defined(HIOP_USE_RESOLVE) || defined(HIOP_USE_EVLOSER) if(compute_mode == "gpu") { - assert((linear_solver == "resolve" || linear_solver == "auto") && + assert((linear_solver == "resolve" || linear_solver == "evloser" || linear_solver == "auto") && "the value for duals_init_linear_solver_sparse is invalid and should have been corrected during " "options processing"); } if(fact_acceptor == "inertia_correction") { nlp_->log->printf(hovError, - "LSQ linear solver with ReSolve does not support inertia correction. " + "LSQ linear solver with ReSolve or EVLOSER does not support inertia correction. " "Please set option 'fact_acceptor' to 'inertia_free'.\n"); assert(false); return false; } - // This is our first choice on the device. + +#ifdef HIOP_USE_RESOLVE + // ReSolve remains the first choice when it is available. if(linear_solver == "resolve" || linear_solver == "auto") { ss_log << "LSQ linear solver --- KKT_SPARSE_XDYcYd linsys: ReSolve "; lin_sys_ = new hiopLinSolverSymSparseReSolve(n, nnz, nlp_); } -#else // of #ifdef HIOP_USE_RESOLVE +#endif + +#ifdef HIOP_USE_EVLOSER + if(nullptr == lin_sys_ && (linear_solver == "evloser" || linear_solver == "auto")) { + ss_log << "LSQ linear solver --- KKT_SPARSE_XDYcYd linsys: EVLOSER "; + lin_sys_ = new hiopLinSolverSymSparseEVLOSER(n, nnz, nlp_); + } +#endif +#else // no ReSolve or EVLOSER support // under compute mode gpu, at this point we don't have a sparse linear solver if(compute_mode == "gpu") { if(linear_solver == "auto") { diff --git a/src/Optimization/hiopKKTLinSysSparse.cpp b/src/Optimization/hiopKKTLinSysSparse.cpp index e9af09d..d629670 100644 --- a/src/Optimization/hiopKKTLinSysSparse.cpp +++ b/src/Optimization/hiopKKTLinSysSparse.cpp @@ -60,6 +60,10 @@ #ifdef HIOP_USE_RESOLVE #include "hiopLinSolverSparseReSolve.hpp" #endif + +#ifdef HIOP_USE_EVLOSER +#include "hiopLinSolverSparseEVLOSER.hpp" +#endif #ifdef HIOP_USE_GINKGO #include "hiopLinSolverSparseGinkgo.hpp" #endif @@ -310,6 +314,22 @@ hiopLinSolverSymSparse* hiopKKTLinSysCompressedSparseXYcYd::determineAndCreateLi //////////////////////////////////////////////////////////////////////////////////////////////// assert(nullptr == linSys_); +#ifdef HIOP_USE_EVLOSER + if(linear_solver == "evloser") { + linsol_actual = "EVLOSER"; + linSys_ = new hiopLinSolverSymSparseEVLOSER(n, nnz, nlp_); + + auto* fact_acceptor_ic = dynamic_cast(fact_acceptor_); + if(fact_acceptor_ic) { + nlp_->log->printf(hovError, + "KKT_SPARSE_XYcYd linsys with EVLOSER does not support inertia correction. " + "Please set option 'fact_acceptor' to 'inertia_free'.\n"); + assert(false); + return nullptr; + } + } +#endif // HIOP_USE_EVLOSER + if(linear_solver == "ma57" || linear_solver == "auto") { #ifdef HIOP_USE_COINHSL linsol_actual = "MA57"; @@ -376,6 +396,26 @@ hiopLinSolverSymSparse* hiopKKTLinSysCompressedSparseXYcYd::determineAndCreateLi #endif } +#ifdef HIOP_USE_EVLOSER + if(nullptr == linSys_ && linear_solver == "evloser") { + linSys_ = + new hiopLinSolverSymSparseEVLOSER(n, nnz, nlp_); + linsol_actual = "EVLOSER"; + + auto* fact_acceptor_ic = + dynamic_cast(fact_acceptor_); + if(fact_acceptor_ic) { + nlp_->log->printf( + hovError, + "KKT_SPARSE_XYcYd linsys with EVLOSER does not support " + "inertia correction. Please set option 'fact_acceptor' " + "to 'inertia_free'.\n"); + assert(false); + return nullptr; + } + } +#endif // HIOP_USE_EVLOSER + if((nullptr == linSys_ && linear_solver == "auto") || linear_solver == "strumpack") { #if defined(HIOP_USE_STRUMPACK) linSys_ = new hiopLinSolverSymSparseSTRUMPACK(n, nnz, nlp_); @@ -686,6 +726,23 @@ hiopLinSolverSymSparse* hiopKKTLinSysCompressedSparseXDYcYd::determineAndCreateL ///////////////////////////////////////////////////////////////////////////////////////////// // CPU compute mode ///////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef HIOP_USE_EVLOSER + if(linear_solver == "evloser") { + actual_lin_solver = "EVLOSER"; + linSys_ = new hiopLinSolverSymSparseEVLOSER(n, nnz, nlp_); + + auto* fact_acceptor_ic = dynamic_cast(fact_acceptor_); + if(fact_acceptor_ic) { + nlp_->log->printf(hovError, + "KKT_SPARSE_XDYcYd linsys with EVLOSER does not support inertia correction. " + "Please set option 'fact_acceptor' to 'inertia_free'.\n"); + assert(false); + return nullptr; + } + } +#endif // HIOP_USE_EVLOSER + if(linear_solver == "ma57" || linear_solver == "auto") { #ifdef HIOP_USE_COINHSL linSys_ = new hiopLinSolverSymSparseMA57(n, nnz, nlp_); @@ -756,6 +813,25 @@ hiopLinSolverSymSparse* hiopKKTLinSysCompressedSparseXDYcYd::determineAndCreateL } #endif } // end resolve +#ifdef HIOP_USE_EVLOSER + if(nullptr == linSys_ && linear_solver == "evloser") { + actual_lin_solver = "EVLOSER"; + linSys_ = + new hiopLinSolverSymSparseEVLOSER(n, nnz, nlp_); + + auto* fact_acceptor_ic = + dynamic_cast(fact_acceptor_); + if(fact_acceptor_ic) { + nlp_->log->printf( + hovError, + "KKT_SPARSE_XDYcYd linsys with EVLOSER does not support " + "inertia correction. Please set option 'fact_acceptor' " + "to 'inertia_free'.\n"); + assert(false); + return nullptr; + } + } +#endif // HIOP_USE_EVLOSER if(nullptr == linSys_ && (linear_solver == "strumpack" || linear_solver == "auto")) { #if defined(HIOP_USE_STRUMPACK) @@ -828,6 +904,23 @@ hiopLinSolverSymSparse* hiopKKTLinSysCompressedSparseXDYcYd::determineAndCreateL } #endif } // end resolve + + // EVLOSER has its own solver object but uses this same sparse KKT selection point. + if(nullptr == linSys_ && linear_solver == "evloser") { +#if defined(HIOP_USE_EVLOSER) + linSys_ = new hiopLinSolverSymSparseEVLOSER(n, nnz, nlp_); + nlp_->log->printf(hovScalars, "KKT_SPARSE_XDYcYd linsys: alloc EVLOSER size %d (%d cons) (gpu)\n", n, neq + nineq); + auto* fact_acceptor_ic = dynamic_cast(fact_acceptor_); + if(fact_acceptor_ic) { + nlp_->log->printf(hovError, + "KKT_SPARSE_XDYcYd linsys with EVLOSER does not support inertia correction. " + "Please set option 'fact_acceptor' to 'inertia_free'.\n"); + assert(false); + return nullptr; + } +#endif + } + } // end of compute mode gpu } assert(linSys_ && "KKT_SPARSE_XDYcYd linsys: cannot instantiate backend linear solver"); diff --git a/src/Utils/hiopOptions.cpp b/src/Utils/hiopOptions.cpp index ec63f68..81c41ab 100644 --- a/src/Utils/hiopOptions.cpp +++ b/src/Utils/hiopOptions.cpp @@ -922,12 +922,12 @@ void hiopOptionsNLP::register_options() // - 'gpu' compute mode: work in progress { - vector range{"auto", "ma57", "pardiso", "strumpack", "resolve", "ginkgo", "cusolver-chol"}; + vector range{"auto", "ma57", "pardiso", "strumpack", "resolve", "evloser", "ginkgo", "cusolver-chol"}; register_str_option("linear_solver_sparse", "auto", range, - "Selects among MA57, PARDISO, STRUMPACK, cuSOLVER's Cholesky or LU, and GINKGO for the " + "Selects among MA57, PARDISO, STRUMPACK, ReSolve, EVLOSER, cuSOLVER's Cholesky or LU, and GINKGO for the " "sparse linear solves."); } @@ -936,12 +936,12 @@ void hiopOptionsNLP::register_options() // - when GPU mode is on, STRUMPACK is chosen by 'auto' if available // - choosing option ma57 or pardiso with GPU being on, it results in no device being used in the linear solve! { - vector range{"auto", "ma57", "pardiso", "resolve", "strumpack", "ginkgo"}; + vector range{"auto", "ma57", "pardiso", "resolve", "evloser", "strumpack", "ginkgo"}; register_str_option("duals_init_linear_solver_sparse", "auto", range, - "Selects among MA57, PARDISO, cuSOLVER, STRUMPACK, and GINKGO for the sparse linear solves."); + "Selects among MA57, PARDISO, ReSolve, EVLOSER, cuSOLVER, STRUMPACK, and GINKGO for the sparse linear solves."); } // choose hardware backend for the Ginkgo solver to run on. @@ -1402,7 +1402,7 @@ void hiopOptionsNLP::ensure_consistence() auto kkt_linsys = GetString("KKTLinsys"); auto sol_sp = GetString("linear_solver_sparse"); if(kkt_linsys == "full") { - if(sol_sp != "resolve" && sol_sp != "pardiso" && sol_sp != "strumpack" && sol_sp != "auto") { + if(sol_sp != "resolve" && sol_sp != "evloser" && sol_sp != "pardiso" && sol_sp != "strumpack" && sol_sp != "auto") { if(is_user_defined("linear_solver_sparse")) { log_printf(hovWarning, "The option 'linear_solver_sparse=%s' is not valid with option 'KKTLinsys=full'. " @@ -1425,6 +1425,29 @@ void hiopOptionsNLP::ensure_consistence() } } +#ifndef HIOP_USE_EVLOSER + if(sol_sp == "evloser") { + if(is_user_defined("linear_solver_sparse")) { + log_printf(hovWarning, + "The option 'linear_solver_sparse=%s' is not valid because HiOp was built without EVLOSER support." + " Will use 'linear_solver_sparse=auto'.\n", + GetString("linear_solver_sparse").c_str()); + } + set_val("linear_solver_sparse", "auto"); + } + + if(GetString("duals_init_linear_solver_sparse") == "evloser") { + if(is_user_defined("duals_init_linear_solver_sparse")) { + log_printf( + hovWarning, + "The option 'duals_init_linear_solver_sparse=%s' is not valid because HiOp was built without EVLOSER support." + " Will use 'duals_init_linear_solver_sparse=auto'.\n", + GetString("duals_init_linear_solver_sparse").c_str()); + } + set_val("duals_init_linear_solver_sparse", "auto"); + } +#endif // HIOP_USE_EVLOSER + #ifndef HIOP_USE_CUDA if(sol_sp == "resolve" || sol_sp == "cusolver-chol") { if(is_user_defined("linear_solver_sparse")) { @@ -1559,7 +1582,8 @@ void hiopOptionsNLP::ensure_consistence() } set_val("fact_acceptor", "inertia_free"); } - } else if(GetString("linear_solver_sparse") == "strumpack" || GetString("linear_solver_sparse") == "resolve") { + } else if(GetString("linear_solver_sparse") == "strumpack" || GetString("linear_solver_sparse") == "resolve" || + GetString("linear_solver_sparse") == "evloser") { if(GetString("fact_acceptor") == "inertia_correction") { if(is_user_defined("fact_acceptor") && is_user_defined("linear_solver_sparse")) { log_printf(hovWarning, From d348a16f2689f1ef8f12f588272dddbdc1d05cae Mon Sep 17 00:00:00 2001 From: Tamar DeWilde Date: Fri, 26 Jun 2026 15:35:12 -0400 Subject: [PATCH 2/2] Add HIP EVLOSER regression test --- src/Drivers/Sparse/CMakeLists.txt | 32 +++++++++++-------- src/LinAlg/hiopLinSolverSparseEVLOSER.hpp | 2 +- .../hiopLinSolverSparseEVLOSERExternal.cpp | 2 +- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Drivers/Sparse/CMakeLists.txt b/src/Drivers/Sparse/CMakeLists.txt index 2144163..fbce30a 100644 --- a/src/Drivers/Sparse/CMakeLists.txt +++ b/src/Drivers/Sparse/CMakeLists.txt @@ -18,12 +18,14 @@ add_executable(NlpSparseEx4.exe NlpSparseEx4.cpp NlpSparseEx4Driver.cpp) target_link_libraries(NlpSparseEx4.exe HiOp::HiOp) if(HIOP_USE_RAJA) - if(HIOP_USE_GPU AND HIOP_USE_CUDA AND (HIOP_USE_RESOLVE OR HIOP_USE_EVLOSER)) - set_source_files_properties( - NlpSparseRajaEx2.cpp - NlpSparseRajaEx2Driver.cpp - PROPERTIES LANGUAGE CUDA - ) + if(HIOP_USE_GPU AND (HIOP_USE_CUDA OR HIOP_USE_HIP) AND (HIOP_USE_RESOLVE OR HIOP_USE_EVLOSER)) + if(HIOP_USE_CUDA) + set_source_files_properties( + NlpSparseRajaEx2.cpp + NlpSparseRajaEx2Driver.cpp + PROPERTIES LANGUAGE CUDA + ) + endif() add_executable(NlpSparseRajaEx2.exe NlpSparseRajaEx2Driver.cpp NlpSparseRajaEx2.cpp) target_link_libraries(NlpSparseRajaEx2.exe HiOp::HiOp) @@ -83,14 +85,18 @@ if(HIOP_USE_GINKGO) endif(HIOP_USE_HIP) endif(HIOP_USE_GINKGO) -if(HIOP_USE_RAJA AND HIOP_USE_GPU AND HIOP_USE_CUDA) - if(HIOP_USE_RESOLVE) - add_test(NAME NlpSparseRaja2_1 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_glu") - add_test(NAME NlpSparseRaja2_2 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_rf") +if(HIOP_USE_RAJA AND HIOP_USE_GPU) + if(HIOP_USE_CUDA) + if(HIOP_USE_RESOLVE) + add_test(NAME NlpSparseRaja2_1 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_glu") + add_test(NAME NlpSparseRaja2_2 COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-resolve_cuda_rf") + endif() + if(HIOP_USE_EVLOSER) + add_test(NAME NlpSparseRaja2_EVLOSER_RF COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-evloser_cuda_rf") + endif() endif() - - if(HIOP_USE_EVLOSER) - add_test(NAME NlpSparseRaja2_EVLOSER_RF COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-evloser_cuda_rf") + if(HIOP_USE_HIP AND HIOP_USE_EVLOSER) + add_test(NAME NlpSparseRaja2_EVLOSER_HIP_RF COMMAND ${RUNCMD} "$" "500" "-inertiafree" "-selfcheck" "-evloser_hip_rf") endif() endif() diff --git a/src/LinAlg/hiopLinSolverSparseEVLOSER.hpp b/src/LinAlg/hiopLinSolverSparseEVLOSER.hpp index 350bb69..7c0838d 100644 --- a/src/LinAlg/hiopLinSolverSparseEVLOSER.hpp +++ b/src/LinAlg/hiopLinSolverSparseEVLOSER.hpp @@ -113,4 +113,4 @@ class hiopLinSolverSymSparseEVLOSER : public hiopLinSolverSymSparse } // namespace hiop -#endif \ No newline at end of file +#endif diff --git a/src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp b/src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp index 1bf3f4f..197180d 100644 --- a/src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp +++ b/src/LinAlg/hiopLinSolverSparseEVLOSERExternal.cpp @@ -1075,4 +1075,4 @@ create_hiop_evloser_provider(const int& n, nlp); } -} // namespace hiop \ No newline at end of file +} // namespace hiop