diff --git a/ci/build_wheel_cuopt_mps_parser.sh b/ci/build_wheel_cuopt_mps_parser.sh index 826b229e5..3ca2220df 100755 --- a/ci/build_wheel_cuopt_mps_parser.sh +++ b/ci/build_wheel_cuopt_mps_parser.sh @@ -33,7 +33,13 @@ fi ci/build_wheel.sh cuopt_mps_parser ${package_dir} + +EXCLUDE_ARGS=( + --exclude "libzlib.so" + --exclude "libbz2.so" +) + # repair wheels and write to the location that artifact-uploading code expects to find them -python -m auditwheel repair -w "${RAPIDS_WHEEL_BLD_OUTPUT_DIR}" ${package_dir}/dist/* +python -m auditwheel repair "${EXCLUDE_ARGS[@]}" -w "${RAPIDS_WHEEL_BLD_OUTPUT_DIR}" ${package_dir}/dist/* ci/validate_wheel.sh "${package_dir}" "${RAPIDS_WHEEL_BLD_OUTPUT_DIR}" diff --git a/conda/environments/all_cuda-129_arch-aarch64.yaml b/conda/environments/all_cuda-129_arch-aarch64.yaml index 9f9a642c2..0db519d77 100644 --- a/conda/environments/all_cuda-129_arch-aarch64.yaml +++ b/conda/environments/all_cuda-129_arch-aarch64.yaml @@ -7,6 +7,7 @@ channels: dependencies: - boost - breathe +- bzip2 - c-compiler - ccache - clang-tools=20.1.4 @@ -79,6 +80,7 @@ dependencies: - sphinxcontrib-websupport - sysroot_linux-aarch64==2.28 - uvicorn==0.34.* +- zlib - pip: - nvidia_sphinx_theme - swagger-plugin-for-sphinx diff --git a/conda/environments/all_cuda-129_arch-x86_64.yaml b/conda/environments/all_cuda-129_arch-x86_64.yaml index f2a73c08a..ac47f6808 100644 --- a/conda/environments/all_cuda-129_arch-x86_64.yaml +++ b/conda/environments/all_cuda-129_arch-x86_64.yaml @@ -7,6 +7,7 @@ channels: dependencies: - boost - breathe +- bzip2 - c-compiler - ccache - clang-tools=20.1.4 @@ -79,6 +80,7 @@ dependencies: - sphinxcontrib-websupport - sysroot_linux-64==2.28 - uvicorn==0.34.* +- zlib - pip: - nvidia_sphinx_theme - swagger-plugin-for-sphinx diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 9924107d2..176e296dd 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -57,6 +57,8 @@ cache: - cuda-version =${{ cuda_version }} - cmake ${{ cmake_version }} - ninja + - zlib + - bzip2 host: - cpp-argparse - cuda-version =${{ cuda_version }} @@ -70,6 +72,8 @@ cache: - libcusparse-dev - cuda-cudart-dev - boost + - zlib + - bzip2 outputs: - package: @@ -90,6 +94,14 @@ outputs: build: - cmake ${{ cmake_version }} - ${{ stdlib("c") }} + - zlib + - bzip2 + host: + - zlib + - bzip2 + run: + - zlib + - bzip2 ignore_run_exports: by_name: - cuda-cudart @@ -99,6 +111,8 @@ outputs: - libcurand - libcusparse - librmm + - libzlib + - libbz2 tests: - package_contents: files: diff --git a/conda/recipes/mps-parser/recipe.yaml b/conda/recipes/mps-parser/recipe.yaml index c9e10c8a8..bf3666f08 100644 --- a/conda/recipes/mps-parser/recipe.yaml +++ b/conda/recipes/mps-parser/recipe.yaml @@ -32,6 +32,7 @@ requirements: build: - cmake ${{ cmake_version }} - ninja + - libmps-parser =${{ version }} - ${{ compiler("c") }} - ${{ compiler("cxx") }} - ${{ stdlib("c") }} diff --git a/cpp/libmps_parser/CMakeLists.txt b/cpp/libmps_parser/CMakeLists.txt index 33f0a7b58..4a3cbb769 100644 --- a/cpp/libmps_parser/CMakeLists.txt +++ b/cpp/libmps_parser/CMakeLists.txt @@ -38,6 +38,8 @@ rapids_cmake_build_type(Release) # ############################################################################# # - User Options ------------------------------------------------------------ option(BUILD_TESTS "Configure CMake to build tests" ON) +option(MPS_PARSER_WITH_BZIP2 "Build with bzip2 decompression" ON) +option(MPS_PARSER_WITH_ZLIB "Build with zlib decompression" ON) message(VERBOSE "cuOpt: Build mps-parser unit-tests: ${BUILD_TESTS}") @@ -50,6 +52,16 @@ if(CMAKE_COMPILER_IS_GNUCXX) list(APPEND MPS_PARSER_CXX_FLAGS -Werror -Wno-error=deprecated-declarations) endif(CMAKE_COMPILER_IS_GNUCXX) +if(MPS_PARSER_WITH_BZIP2) + find_package(BZip2 REQUIRED) + add_compile_definitions(-DMPS_PARSER_WITH_BZIP2) +endif(MPS_PARSER_WITH_BZIP2) + +if(MPS_PARSER_WITH_ZLIB) + find_package(ZLIB REQUIRED) + add_compile_definitions(-DMPS_PARSER_WITH_ZLIB) +endif(MPS_PARSER_WITH_ZLIB) + if(DEFINE_ASSERT) add_definitions(-DASSERT_MODE) endif(DEFINE_ASSERT) @@ -107,6 +119,14 @@ target_include_directories(mps_parser "$" ) +if(MPS_PARSER_WITH_BZIP2) + target_include_directories(mps_parser PRIVATE BZip2::BZip2) +endif(MPS_PARSER_WITH_BZIP2) + +if(MPS_PARSER_WITH_ZLIB) + target_include_directories(mps_parser PRIVATE ZLIB::ZLIB) +endif(MPS_PARSER_WITH_ZLIB) + # ################################################################################################## # - generate tests -------------------------------------------------------------------------------- if(BUILD_TESTS) diff --git a/cpp/libmps_parser/include/mps_parser/parser.hpp b/cpp/libmps_parser/include/mps_parser/parser.hpp index fedfca514..0660491c7 100644 --- a/cpp/libmps_parser/include/mps_parser/parser.hpp +++ b/cpp/libmps_parser/include/mps_parser/parser.hpp @@ -22,19 +22,24 @@ namespace cuopt::mps_parser { /** - * @brief Reads the equation from the input text file which is MPS or QPS formatted + * @brief Reads the equation from an MPS or QPS file. + * + * The input file can be a plain text file in MPS-/QPS-format or a compressed MPS/QPS + * file (.mps.gz or .mps.bz2). * * Read this link http://lpsolve.sourceforge.net/5.5/mps-format.htm for more * details on both free and fixed MPS format. - * * This function supports both standard MPS files (for linear programming) and * QPS files (for quadratic programming). QPS files are MPS files with additional * sections: * - QUADOBJ: Defines quadratic terms in the objective function * - * @param[in] mps_file_path Path to MPS or QPS formatted file. - * @param[in] fixed_mps_format If MPS/QPS file should be parsed as fixed format, false by default - * @return mps_data_model_t A fully formed LP/QP problem which represents the given MPS/QPS file + * Note: Compressed MPS files .mps.gz, .mps.bz2 can only be read if the compression + * libraries zlib or libbzip2 are installed, respectively. + * + * @param[in] mps_file_path Path to MPS/QPSfile. + * @param[in] fixed_mps_format If MPS/QPS file should be parsed as fixed, false by default + * @return mps_data_model_t A fully formed LP/QP problem which represents the given file */ template mps_data_model_t parse_mps(const std::string& mps_file_path, diff --git a/cpp/libmps_parser/src/mps_parser.cpp b/cpp/libmps_parser/src/mps_parser.cpp index 2a2193768..09c8bec64 100644 --- a/cpp/libmps_parser/src/mps_parser.cpp +++ b/cpp/libmps_parser/src/mps_parser.cpp @@ -26,9 +26,193 @@ #include #include #include +#include #include #include +#ifdef MPS_PARSER_WITH_BZIP2 +#include +#endif // MPS_PARSER_WITH_BZIP2 + +#ifdef MPS_PARSER_WITH_ZLIB +#include +#endif // MPS_PARSER_WITH_ZLIB + +#if defined(MPS_PARSER_WITH_BZIP2) || defined(MPS_PARSER_WITH_ZLIB) +#include +#endif // MPS_PARSER_WITH_BZIP2 || MPS_PARSER_WITH_ZLIB + +namespace { +using cuopt::mps_parser::error_type_t; +using cuopt::mps_parser::mps_parser_expects; +using cuopt::mps_parser::mps_parser_expects_fatal; + +struct FcloseDeleter { + void operator()(FILE* fp) + { + mps_parser_expects_fatal( + fclose(fp) == 0, error_type_t::ValidationError, "Error closing MPS file!"); + } +}; +} // end namespace + +#ifdef MPS_PARSER_WITH_BZIP2 +namespace { +using BZ2_bzReadOpen_t = decltype(&BZ2_bzReadOpen); +using BZ2_bzReadClose_t = decltype(&BZ2_bzReadClose); +using BZ2_bzRead_t = decltype(&BZ2_bzRead); + +std::vector bz2_file_to_string(const std::string& file) +{ + struct DlCloseDeleter { + void operator()(void* fp) + { + mps_parser_expects_fatal( + dlclose(fp) == 0, error_type_t::ValidationError, "Error closing libbz2.so!"); + } + }; + struct BzReadCloseDeleter { + void operator()(void* f) + { + int bzerror; + if (f != nullptr) fptr(&bzerror, f); + mps_parser_expects_fatal( + bzerror == BZ_OK, error_type_t::ValidationError, "Error closing bzip2 file!"); + } + BZ2_bzReadClose_t fptr = nullptr; + }; + + std::unique_ptr lbz2handle{dlopen("libbz2.so", RTLD_LAZY)}; + mps_parser_expects( + lbz2handle != nullptr, + error_type_t::ValidationError, + "Could not open .mps.bz2 file since libbz2.so was not found. In order to open .mps.bz2 files " + "directly, please ensure libbzip2 is installed. Alternatively, decompress the .mps.bz2 file " + "manually and open the uncompressed .mps file. Given path: %s", + file.c_str()); + + BZ2_bzReadOpen_t BZ2_bzReadOpen = + reinterpret_cast(dlsym(lbz2handle.get(), "BZ2_bzReadOpen")); + BZ2_bzReadClose_t BZ2_bzReadClose = + reinterpret_cast(dlsym(lbz2handle.get(), "BZ2_bzReadClose")); + BZ2_bzRead_t BZ2_bzRead = reinterpret_cast(dlsym(lbz2handle.get(), "BZ2_bzRead")); + mps_parser_expects( + BZ2_bzReadOpen != nullptr && BZ2_bzReadClose != nullptr && BZ2_bzRead != nullptr, + error_type_t::ValidationError, + "Error loading libbzip2! Library version might be incompatible. Please decompress the .mps.bz2 " + "file manually and open the uncompressed .mps file. Given path: %s", + file.c_str()); + + std::unique_ptr fp{fopen(file.c_str(), "rb")}; + mps_parser_expects(fp != nullptr, + error_type_t::ValidationError, + "Error opening MPS file! Given path: %s", + file.c_str()); + int bzerror = BZ_OK; + std::unique_ptr bzfile{ + BZ2_bzReadOpen(&bzerror, fp.get(), 0, 0, nullptr, 0), {BZ2_bzReadClose}}; + mps_parser_expects(bzerror == BZ_OK, + error_type_t::ValidationError, + "Could not open bzip2 compressed file! Given path: %s", + file.c_str()); + + std::vector buf; + const size_t readbufsize = 1ull << 24; // 16MiB - just a guess. + std::vector readbuf(readbufsize); + while (bzerror == BZ_OK) { + const size_t bytes_read = BZ2_bzRead(&bzerror, bzfile.get(), readbuf.data(), readbuf.size()); + if (bzerror == BZ_OK || bzerror == BZ_STREAM_END) { + buf.insert(buf.end(), begin(readbuf), begin(readbuf) + bytes_read); + } + } + buf.push_back('\0'); + mps_parser_expects(bzerror == BZ_STREAM_END, + error_type_t::ValidationError, + "Error in bzip2 decompression of MPS file! Given path: %s", + file.c_str()); + return buf; +} +} // end namespace +#endif // MPS_PARSER_WITH_BZIP2 + +#ifdef MPS_PARSER_WITH_ZLIB +namespace { +using gzopen_t = decltype(&gzopen); +using gzclose_r_t = decltype(&gzclose_r); +using gzbuffer_t = decltype(&gzbuffer); +using gzread_t = decltype(&gzread); +using gzerror_t = decltype(&gzerror); +std::vector zlib_file_to_string(const std::string& file) +{ + struct DlCloseDeleter { + void operator()(void* fp) + { + mps_parser_expects_fatal( + dlclose(fp) == 0, error_type_t::ValidationError, "Error closing libbz2.so!"); + } + }; + struct GzCloseDeleter { + void operator()(gzFile_s* f) + { + int err = fptr(f); + mps_parser_expects_fatal( + err == Z_OK, error_type_t::ValidationError, "Error closing gz file!"); + } + gzclose_r_t fptr = nullptr; + }; + + std::unique_ptr lzhandle{dlopen("libz.so.1", RTLD_LAZY)}; + mps_parser_expects( + lzhandle != nullptr, + error_type_t::ValidationError, + "Could not open .mps.gz file since libz.so was not found. In order to open .mps.gz files " + "directly, please ensure zlib is installed. Alternatively, decompress the .mps.gz file " + "manually and open the uncompressed .mps file. Given path: %s", + file.c_str()); + gzopen_t gzopen = reinterpret_cast(dlsym(lzhandle.get(), "gzopen")); + gzclose_r_t gzclose_r = reinterpret_cast(dlsym(lzhandle.get(), "gzclose_r")); + gzbuffer_t gzbuffer = reinterpret_cast(dlsym(lzhandle.get(), "gzbuffer")); + gzread_t gzread = reinterpret_cast(dlsym(lzhandle.get(), "gzread")); + gzerror_t gzerror = reinterpret_cast(dlsym(lzhandle.get(), "gzerror")); + mps_parser_expects( + gzopen != nullptr && gzclose_r != nullptr && gzbuffer != nullptr && gzread != nullptr && + gzerror != nullptr, + error_type_t::ValidationError, + "Error loading zlib! Library version might be incompatible. Please decompress the .mps.gz file " + "manually and open the uncompressed .mps file. Given path: %s", + file.c_str()); + std::unique_ptr gzfp{gzopen(file.c_str(), "rb"), {gzclose_r}}; + mps_parser_expects(gzfp != nullptr, + error_type_t::ValidationError, + "Error opening compressed MPS file! Given path: %s", + file.c_str()); + int zlib_status = gzbuffer(gzfp.get(), 1 << 20); // 1 MiB + mps_parser_expects(zlib_status == Z_OK, + error_type_t::ValidationError, + "Could not set zlib internal buffer size for decompression! Given path: %s", + file.c_str()); + std::vector buf; + const size_t readbufsize = 1ull << 24; // 16MiB + std::vector readbuf(readbufsize); + int bytes_read = -1; + while (bytes_read != 0) { + bytes_read = gzread(gzfp.get(), readbuf.data(), readbuf.size()); + if (bytes_read > 0) { buf.insert(buf.end(), begin(readbuf), begin(readbuf) + bytes_read); } + if (bytes_read < 0) { + gzerror(gzfp.get(), &zlib_status); + break; + } + } + buf.push_back('\0'); + mps_parser_expects(zlib_status == Z_OK, + error_type_t::ValidationError, + "Error in zlib decompression of MPS file! Given path: %s", + file.c_str()); + return buf; +} +} // end namespace +#endif // MPS_PARSER_WITH_ZLIB + namespace cuopt::mps_parser { template @@ -342,6 +526,18 @@ std::vector mps_parser_t::file_to_string(const std::string& file { // raft::common::nvtx::range fun_scope("file to string"); +#ifdef MPS_PARSER_WITH_BZIP2 + if (file.size() > 4 && file.substr(file.size() - 4, 4) == ".bz2") { + return bz2_file_to_string(file); + } +#endif // MPS_PARSER_WITH_BZIP2 + +#ifdef MPS_PARSER_WITH_ZLIB + if (file.size() > 3 && file.substr(file.size() - 3, 3) == ".gz") { + return zlib_file_to_string(file); + } +#endif // MPS_PARSER_WITH_ZLIB + // Faster than using C++ I/O FILE* fp = fopen(file.c_str(), "r"); mps_parser_expects(fp != nullptr, diff --git a/cpp/libmps_parser/src/mps_parser.hpp b/cpp/libmps_parser/src/mps_parser.hpp index e06b2bbe6..58b4a6128 100644 --- a/cpp/libmps_parser/src/mps_parser.hpp +++ b/cpp/libmps_parser/src/mps_parser.hpp @@ -145,10 +145,10 @@ class mps_parser_t { std::unordered_set bounds_defined_for_var_id{}; static constexpr f_t unset_range_value = std::numeric_limits::infinity(); - /* Reads the equation from the input text file which is MPS formatted + /* Reads an MPS input file into a buffer. * - * Read this link http://lpsolve.sourceforge.net/5.5/mps-format.htm for more - * details on this format. + * If the file has a .gz or .bz2 suffix and zlib or libbzip2 are installed, respectively, + * the function directly reads and decompresses the compressed MPS file. */ std::vector file_to_string(const std::string& file); void fill_problem(mps_data_model_t& problem); diff --git a/cpp/libmps_parser/src/utilities/error.hpp b/cpp/libmps_parser/src/utilities/error.hpp index 3aafa6340..17518d8da 100644 --- a/cpp/libmps_parser/src/utilities/error.hpp +++ b/cpp/libmps_parser/src/utilities/error.hpp @@ -45,7 +45,7 @@ inline std::string error_to_string(error_type_t error) } /** - * @brief Macro for checking (pre-)conditions that throws an exception when a + * @brief Function for checking (pre-)conditions that throws an exception when a * condition is false * * @param[bool] cond From expression that evaluates to true or false @@ -70,6 +70,36 @@ inline void mps_parser_expects(bool cond, error_type_t error_type, const char* f } } +/** + * @brief Function for checking (pre-)conditions that aborts the program when a + * condition is false + * + * @param[bool] cond From expression that evaluates to true or false + * @param[error_type_t] error enum error type + * @param[const char *] fmt String format for error message + * @param variable set of arguments used for fmt + * @throw std::logic_error if the condition evaluates to false. + */ +inline void mps_parser_expects_fatal(bool cond, error_type_t error_type, const char* fmt, ...) +{ + if (not cond) { + va_list args; + va_start(args, fmt); + + char msg[2048]; + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + std::string error_string = error_to_string(error_type); + std::fprintf(stderr, + "{\"MPS_PARSER_ERROR_TYPE\": \"%s\", \"msg\": \"%s\"}\n", + error_to_string(error_type).c_str(), + msg); + std::fflush(stderr); + std::abort(); + } +} + #define MPS_PARSER_SET_ERROR_MSG(msg, location_prefix, fmt, ...) \ do { \ char err_msg[2048]; /* NOLINT */ \ diff --git a/cpp/libmps_parser/tests/mps_parser_test.cpp b/cpp/libmps_parser/tests/mps_parser_test.cpp index 1e7f218c8..508cc3abf 100644 --- a/cpp/libmps_parser/tests/mps_parser_test.cpp +++ b/cpp/libmps_parser/tests/mps_parser_test.cpp @@ -757,6 +757,78 @@ TEST(mps_parser, good_mps_file_partial_bounds) EXPECT_EQ(10.0, mps.variable_upper_bounds[1]); } +TEST(mps_parser, good_mps_file_bzip2_compressed) +{ + auto mps = read_from_mps("linear_programming/good-mps-1.mps.bz2"); + EXPECT_EQ("good-1", mps.problem_name); + ASSERT_EQ(int(2), mps.row_names.size()); + EXPECT_EQ("ROW1", mps.row_names[0]); + EXPECT_EQ("ROW2", mps.row_names[1]); + ASSERT_EQ(int(2), mps.row_types.size()); + EXPECT_EQ(LesserThanOrEqual, mps.row_types[0]); + EXPECT_EQ(LesserThanOrEqual, mps.row_types[1]); + EXPECT_EQ("COST", mps.objective_name); + ASSERT_EQ(int(2), mps.var_names.size()); + EXPECT_EQ("VAR1", mps.var_names[0]); + EXPECT_EQ("VAR2", mps.var_names[1]); + ASSERT_EQ(int(2), mps.A_indices.size()); + ASSERT_EQ(int(2), mps.A_indices[0].size()); + EXPECT_EQ(int(0), mps.A_indices[0][0]); + EXPECT_EQ(int(1), mps.A_indices[0][1]); + ASSERT_EQ(int(2), mps.A_indices[1].size()); + EXPECT_EQ(int(0), mps.A_indices[1][0]); + EXPECT_EQ(int(1), mps.A_indices[1][1]); + ASSERT_EQ(int(2), mps.A_values.size()); + ASSERT_EQ(int(2), mps.A_values[0].size()); + EXPECT_EQ(3., mps.A_values[0][0]); + EXPECT_EQ(4., mps.A_values[0][1]); + ASSERT_EQ(int(2), mps.A_values[1].size()); + EXPECT_EQ(2.7, mps.A_values[1][0]); + EXPECT_EQ(10.1, mps.A_values[1][1]); + ASSERT_EQ(int(2), mps.b_values.size()); + EXPECT_EQ(5.4, mps.b_values[0]); + EXPECT_EQ(4.9, mps.b_values[1]); + ASSERT_EQ(int(2), mps.c_values.size()); + EXPECT_EQ(0.2, mps.c_values[0]); + EXPECT_EQ(0.1, mps.c_values[1]); +} + +TEST(mps_parser, good_mps_file_zlib_compressed) +{ + auto mps = read_from_mps("linear_programming/good-mps-1.mps.gz"); + EXPECT_EQ("good-1", mps.problem_name); + ASSERT_EQ(int(2), mps.row_names.size()); + EXPECT_EQ("ROW1", mps.row_names[0]); + EXPECT_EQ("ROW2", mps.row_names[1]); + ASSERT_EQ(int(2), mps.row_types.size()); + EXPECT_EQ(LesserThanOrEqual, mps.row_types[0]); + EXPECT_EQ(LesserThanOrEqual, mps.row_types[1]); + EXPECT_EQ("COST", mps.objective_name); + ASSERT_EQ(int(2), mps.var_names.size()); + EXPECT_EQ("VAR1", mps.var_names[0]); + EXPECT_EQ("VAR2", mps.var_names[1]); + ASSERT_EQ(int(2), mps.A_indices.size()); + ASSERT_EQ(int(2), mps.A_indices[0].size()); + EXPECT_EQ(int(0), mps.A_indices[0][0]); + EXPECT_EQ(int(1), mps.A_indices[0][1]); + ASSERT_EQ(int(2), mps.A_indices[1].size()); + EXPECT_EQ(int(0), mps.A_indices[1][0]); + EXPECT_EQ(int(1), mps.A_indices[1][1]); + ASSERT_EQ(int(2), mps.A_values.size()); + ASSERT_EQ(int(2), mps.A_values[0].size()); + EXPECT_EQ(3., mps.A_values[0][0]); + EXPECT_EQ(4., mps.A_values[0][1]); + ASSERT_EQ(int(2), mps.A_values[1].size()); + EXPECT_EQ(2.7, mps.A_values[1][0]); + EXPECT_EQ(10.1, mps.A_values[1][1]); + ASSERT_EQ(int(2), mps.b_values.size()); + EXPECT_EQ(5.4, mps.b_values[0]); + EXPECT_EQ(4.9, mps.b_values[1]); + ASSERT_EQ(int(2), mps.c_values.size()); + EXPECT_EQ(0.2, mps.c_values[0]); + EXPECT_EQ(0.1, mps.c_values[1]); +} + // ================================================================================================ // QPS (Quadratic Programming) Support Tests // ================================================================================================ diff --git a/datasets/linear_programming/good-mps-1.mps.bz2 b/datasets/linear_programming/good-mps-1.mps.bz2 new file mode 100644 index 000000000..ee96fb055 Binary files /dev/null and b/datasets/linear_programming/good-mps-1.mps.bz2 differ diff --git a/datasets/linear_programming/good-mps-1.mps.gz b/datasets/linear_programming/good-mps-1.mps.gz new file mode 100644 index 000000000..b9a10d173 Binary files /dev/null and b/datasets/linear_programming/good-mps-1.mps.gz differ diff --git a/dependencies.yaml b/dependencies.yaml index 1a8e1f8d7..1ea0f5952 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -306,6 +306,8 @@ dependencies: - cpp-argparse - librmm==25.10.* - libraft-headers==25.10.* + - zlib + - bzip2 test_cpp: common: - output_types: [conda] diff --git a/thirdparty/THIRD_PARTY_LICENSES b/thirdparty/THIRD_PARTY_LICENSES index e6cb70a11..6bce42d31 100644 --- a/thirdparty/THIRD_PARTY_LICENSES +++ b/thirdparty/THIRD_PARTY_LICENSES @@ -878,3 +878,76 @@ may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . + + +----------------------------------------------------------------------------------------- +== bzip2 + +Usage: libmps_parser uses libbzip2 + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2019 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + +Julian Seward, jseward@acm.org +bzip2/libbzip2 version 1.0.8 of 13 July 2019 + + +----------------------------------------------------------------------------------------- +== zlib + +Usage: libmps_parser uses zlib + +Copyright notice: + + (C) 1995-2024 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu