diff --git a/benchmarks/linear_programming/cuopt/benchmark_helper.hpp b/benchmarks/linear_programming/cuopt/benchmark_helper.hpp index 6e2bbc29f1..2f6de22c6b 100644 --- a/benchmarks/linear_programming/cuopt/benchmark_helper.hpp +++ b/benchmarks/linear_programming/cuopt/benchmark_helper.hpp @@ -275,7 +275,7 @@ void mps_file_to_binary(const std::filesystem::path& filename) std::string p = std::string(filename); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(p); + cuopt::linear_programming::io::read_mps(p); auto filename_string = filename.filename().string(); diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 3201aa137a..f35543696f 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -172,7 +172,7 @@ int run_single_file(std::string file_path, CUOPT_LOG_INFO("running file %s on gpu : %d", base_filename.c_str(), device); try { mps_data_model = - cuopt::linear_programming::io::parse_mps(file_path, input_mps_strict); + cuopt::linear_programming::io::read_mps(file_path, input_mps_strict); } catch (const std::logic_error& e) { CUOPT_LOG_ERROR("MPS parser execption: %s", e.what()); parsing_failed = true; diff --git a/benchmarks/linear_programming/cuopt/run_pdlp.cu b/benchmarks/linear_programming/cuopt/run_pdlp.cu index b86f61ba1f..c88e5657df 100644 --- a/benchmarks/linear_programming/cuopt/run_pdlp.cu +++ b/benchmarks/linear_programming/cuopt/run_pdlp.cu @@ -149,7 +149,7 @@ static int run_solver(const argparse::ArgumentParser& program, const raft::handl // Parse MPS file cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(program.get("--path")); + cuopt::linear_programming::io::read_mps(program.get("--path")); // Solve LP problem bool problem_checking = true; diff --git a/ci/test_self_hosted_service.sh b/ci/test_self_hosted_service.sh index e9e74d0079..b9824a7422 100755 --- a/ci/test_self_hosted_service.sh +++ b/ci/test_self_hosted_service.sh @@ -113,14 +113,29 @@ if [ "$doservertest" -eq 1 ]; then # Success, small MILP problem with pure JSON which returns a solution with Optimal status run_cli_test "'status': 'Optimal'" cuopt_sh -s -c $CLIENT_CERT -p $CUOPT_SERVER_PORT -t LP ../../datasets/mixed_integer_programming/milp_data.json - # Succes, small LP problem with mps. Data will be transformed to JSON + # Success, small LP problem with MPS. Data will be transformed to JSON run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.mps - # Succes, small Batch LP problem with mps. Data will be transformed to JSON + # Success, small Batch LP problem with MPS. Data will be transformed to JSON run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.mps ../../datasets/linear_programming/good-mps-1.mps - # Error, local file mode is not allowed with mps + # Success, small LP problem with LP format. Data will be transformed to JSON + run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.lp + + # Success, small Batch LP problem with LP format. Data will be transformed to JSON + run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.lp ../../datasets/linear_programming/good-mps-1.lp + + # Success, compressed LP inputs (.lp.gz / .lp.bz2) via Read dispatch + run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.lp.gz + run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.lp.bz2 + + # Success, compressed MPS inputs (.mps.gz / .mps.bz2) for parity + run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.mps.gz + run_cli_test "'status': 'Optimal'" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP ../../datasets/linear_programming/good-mps-1.mps.bz2 + + # Error, local file mode is not allowed with MPS/LP file inputs run_cli_test "Cannot use local file mode with MPS/LP data" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP -f good-mps-1.mps + run_cli_test "Cannot use local file mode with MPS/LP data" cuopt_sh -s -c "$CLIENT_CERT" -p $CUOPT_SERVER_PORT -t LP -f good-mps-1.lp # Just run validator cp ../../datasets/cuopt_service_data/cuopt_problem_data.json "$CUOPT_DATA_DIR" diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index 39aab47170..1d26f26503 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -108,7 +108,7 @@ int run_single_file(const std::string& file_path, { CUOPT_LOG_INFO("Reading file %s", base_filename.c_str()); try { - mps_data_model = cuopt::linear_programming::io::parse_problem(file_path); + mps_data_model = cuopt::linear_programming::io::read(file_path); } catch (const std::logic_error& e) { CUOPT_LOG_ERROR("Parser exception: %s", e.what()); parsing_failed = true; diff --git a/cpp/include/cuopt/linear_programming/io/parser.hpp b/cpp/include/cuopt/linear_programming/io/parser.hpp index a175e821cd..a63e40f31f 100644 --- a/cpp/include/cuopt/linear_programming/io/parser.hpp +++ b/cpp/include/cuopt/linear_programming/io/parser.hpp @@ -40,23 +40,23 @@ namespace cuopt::linear_programming::io { * @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, - bool fixed_mps_format = false); +mps_data_model_t read_mps(const std::string& mps_file_path, + bool fixed_mps_format = false); /** * @brief Reads an MPS problem from in-memory file contents. * - * This parses the same plain-text MPS format as parse_mps(), but the input is + * This parses the same plain-text MPS format as read_mps(), but the input is * already loaded in memory. Compressed .mps.gz/.mps.bz2 inputs are only supported - * by parse_mps() because compression is detected from the file path. + * by read_mps() because compression is detected from the file path. * * @param[in] mps_contents MPS file contents. * @param[in] fixed_mps_format If MPS content should be parsed as fixed, false by default. * @return mps_data_model_t A fully formed problem which represents the given content. */ template -mps_data_model_t parse_mps_from_string(std::string_view mps_contents, - bool fixed_mps_format = false); +mps_data_model_t read_mps_from_string(std::string_view mps_contents, + bool fixed_mps_format = false); /** * @brief Reads a linear, mixed-integer, or quadratic optimization problem from @@ -82,22 +82,22 @@ mps_data_model_t parse_mps_from_string(std::string_view mps_contents, * a ValidationError when encountered. * * Compressed inputs (.lp.gz, .lp.bz2) are supported when zlib / libbzip2 - * are installed (same dispatching as parse_mps). + * are installed (same dispatching as read_mps). * * @param[in] lp_file_path Path to the LP file. * @return mps_data_model_t A fully formed LP/MIP/QP problem representing the * given file. */ template -mps_data_model_t parse_lp(const std::string& lp_file_path); +mps_data_model_t read_lp(const std::string& lp_file_path); /** * @brief Reads an LP, MIP, or QP problem from in-memory file contents. * - * This parses the same plain-text LP format as parse_lp(), but the input is + * This parses the same plain-text LP format as read_lp(), but the input is * already loaded in memory. Compressed .lp.gz/.lp.bz2 inputs are only - * supported by parse_lp() because compression is detected from the file - * path. Supports the same scope as parse_lp() (LP, MIP, QP, plus + * supported by read_lp() because compression is detected from the file + * path. Supports the same scope as read_lp() (LP, MIP, QP, plus * semi-continuous variables). * * @param[in] lp_contents LP file contents. @@ -105,25 +105,27 @@ mps_data_model_t parse_lp(const std::string& lp_file_path); * given content. */ template -mps_data_model_t parse_lp_from_string(std::string_view lp_contents); +mps_data_model_t read_lp_from_string(std::string_view lp_contents); /** * @brief Reads an optimization problem from a file, dispatching on the file * extension. Extension matching is case-insensitive. * * Routing: - * - .mps, .mps.gz, .mps.bz2, .qps, .qps.gz, .qps.bz2 → parse_mps() - * - .lp, .lp.gz, .lp.bz2 → parse_lp() + * - .mps, .mps.gz, .mps.bz2, .qps, .qps.gz, .qps.bz2 → read_mps() + * - .lp, .lp.gz, .lp.bz2 → read_lp() * - anything else → std::logic_error * * This is the entry point of choice for user-facing tools (CLI, C API) that * want both formats to "just work" without an explicit format flag. * * @param[in] path Path to the input file. + * @param[in] fixed_mps_format If the MPS/QPS reader should use fixed format; + * ignored for LP inputs. False by default. * @return mps_data_model_t The parsed problem. */ template -inline mps_data_model_t parse_problem(const std::string& path) +inline mps_data_model_t read(const std::string& path, bool fixed_mps_format = false) { std::string lower(path); std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { @@ -131,13 +133,13 @@ inline mps_data_model_t parse_problem(const std::string& path) }); if (lower.ends_with(".mps") || lower.ends_with(".mps.gz") || lower.ends_with(".mps.bz2") || lower.ends_with(".qps") || lower.ends_with(".qps.gz") || lower.ends_with(".qps.bz2")) { - return parse_mps(path); + return read_mps(path, fixed_mps_format); } if (lower.ends_with(".lp") || lower.ends_with(".lp.gz") || lower.ends_with(".lp.bz2")) { - return parse_lp(path); + return read_lp(path); } throw std::logic_error( - "parse_problem: unrecognized input file extension. Supported (case-insensitive): " + "read: unrecognized input file extension. Supported (case-insensitive): " ".mps, .mps.gz, .mps.bz2, .qps, .qps.gz, .qps.bz2, .lp, .lp.gz, .lp.bz2. " "Given path: " + path); diff --git a/cpp/include/cuopt/linear_programming/io/utilities/cython_parser.hpp b/cpp/include/cuopt/linear_programming/io/utilities/cython_parser.hpp index eb4044d1d0..711f1c73b2 100644 --- a/cpp/include/cuopt/linear_programming/io/utilities/cython_parser.hpp +++ b/cpp/include/cuopt/linear_programming/io/utilities/cython_parser.hpp @@ -14,11 +14,11 @@ namespace cuopt { namespace cython { +std::unique_ptr> call_read( + const std::string& file_path, bool fixed_mps_format); + std::unique_ptr> call_parse_mps( const std::string& mps_file_path, bool fixed_mps_format); -std::unique_ptr> call_parse_lp( - const std::string& lp_file_path); - } // namespace cython } // namespace cuopt diff --git a/cpp/src/io/lp_parser.cpp b/cpp/src/io/lp_parser.cpp index 84daa101b4..4f8c3f223f 100644 --- a/cpp/src/io/lp_parser.cpp +++ b/cpp/src/io/lp_parser.cpp @@ -199,7 +199,7 @@ template class LpParseEngine { public: LpParseEngine(lp_parser_t& out, const std::string& file); - // Parses `text` directly (used by parse_lp_from_string()). + // Parses `text` directly (used by read_lp_from_string()). LpParseEngine(lp_parser_t& out, std::string_view text); private: @@ -1546,11 +1546,11 @@ template class lp_parser_t; template class lp_parser_t; // =========================================================================== -// Public parse_lp() / parse_lp_from_string() +// Public read_lp() / read_lp_from_string() // =========================================================================== template -mps_data_model_t parse_lp(const std::string& lp_file_path) +mps_data_model_t read_lp(const std::string& lp_file_path) { mps_data_model_t problem; lp_parser_t parser(problem, lp_file_path); @@ -1558,16 +1558,16 @@ mps_data_model_t parse_lp(const std::string& lp_file_path) } template -mps_data_model_t parse_lp_from_string(std::string_view lp_contents) +mps_data_model_t read_lp_from_string(std::string_view lp_contents) { mps_data_model_t problem; lp_parser_t parser(problem, lp_contents); return problem; } -template mps_data_model_t parse_lp(const std::string&); -template mps_data_model_t parse_lp(const std::string&); -template mps_data_model_t parse_lp_from_string(std::string_view); -template mps_data_model_t parse_lp_from_string(std::string_view); +template mps_data_model_t read_lp(const std::string&); +template mps_data_model_t read_lp(const std::string&); +template mps_data_model_t read_lp_from_string(std::string_view); +template mps_data_model_t read_lp_from_string(std::string_view); } // namespace cuopt::linear_programming::io diff --git a/cpp/src/io/lp_parser.hpp b/cpp/src/io/lp_parser.hpp index 8314d6c97a..a607359657 100644 --- a/cpp/src/io/lp_parser.hpp +++ b/cpp/src/io/lp_parser.hpp @@ -36,7 +36,7 @@ class lp_parser_t { lp_parser_t(mps_data_model_t& problem, const std::string& file); // Parses `input` (LP format text already loaded in memory) and populates - // `problem`. Used by parse_lp_from_string() — compressed inputs are only + // `problem`. Used by read_lp_from_string() — compressed inputs are only // supported via the file-path constructor since compression is detected // from the path suffix. lp_parser_t(mps_data_model_t& problem, std::string_view input); diff --git a/cpp/src/io/parser.cpp b/cpp/src/io/parser.cpp index af76c41ff1..93d9d9c73c 100644 --- a/cpp/src/io/parser.cpp +++ b/cpp/src/io/parser.cpp @@ -12,7 +12,7 @@ namespace cuopt::linear_programming::io { template -mps_data_model_t parse_mps(const std::string& mps_file, bool fixed_mps_format) +mps_data_model_t read_mps(const std::string& mps_file, bool fixed_mps_format) { mps_data_model_t problem; mps_parser_t parser(problem, mps_file, fixed_mps_format); @@ -20,20 +20,19 @@ mps_data_model_t parse_mps(const std::string& mps_file, bool fixed_mps } template -mps_data_model_t parse_mps_from_string(std::string_view mps_contents, - bool fixed_mps_format) +mps_data_model_t read_mps_from_string(std::string_view mps_contents, + bool fixed_mps_format) { mps_data_model_t problem; mps_parser_t parser(problem, mps_contents, fixed_mps_format); return problem; } -template mps_data_model_t parse_mps(const std::string& mps_file, bool fixed_mps_format); -template mps_data_model_t parse_mps(const std::string& mps_file, - bool fixed_mps_format); -template mps_data_model_t parse_mps_from_string(std::string_view mps_contents, +template mps_data_model_t read_mps(const std::string& mps_file, bool fixed_mps_format); +template mps_data_model_t read_mps(const std::string& mps_file, bool fixed_mps_format); +template mps_data_model_t read_mps_from_string(std::string_view mps_contents, + bool fixed_mps_format); +template mps_data_model_t read_mps_from_string(std::string_view mps_contents, bool fixed_mps_format); -template mps_data_model_t parse_mps_from_string(std::string_view mps_contents, - bool fixed_mps_format); } // namespace cuopt::linear_programming::io diff --git a/cpp/src/io/utilities/cython_parser.cpp b/cpp/src/io/utilities/cython_parser.cpp index 86bd0bb77d..4de1de9b6f 100644 --- a/cpp/src/io/utilities/cython_parser.cpp +++ b/cpp/src/io/utilities/cython_parser.cpp @@ -11,18 +11,18 @@ namespace cuopt { namespace cython { -std::unique_ptr> call_parse_mps( - const std::string& mps_file_path, bool fixed_mps_format) +std::unique_ptr> call_read( + const std::string& file_path, bool fixed_mps_format) { - return std::make_unique>(std::move( - cuopt::linear_programming::io::parse_mps(mps_file_path, fixed_mps_format))); + return std::make_unique>( + std::move(cuopt::linear_programming::io::read(file_path, fixed_mps_format))); } -std::unique_ptr> call_parse_lp( - const std::string& lp_file_path) +std::unique_ptr> call_parse_mps( + const std::string& mps_file_path, bool fixed_mps_format) { - return std::make_unique>( - std::move(cuopt::linear_programming::io::parse_lp(lp_file_path))); + return std::make_unique>(std::move( + cuopt::linear_programming::io::read_mps(mps_file_path, fixed_mps_format))); } } // namespace cython diff --git a/cpp/src/pdlp/cuopt_c.cpp b/cpp/src/pdlp/cuopt_c.cpp index d29e849411..2e5c744f15 100644 --- a/cpp/src/pdlp/cuopt_c.cpp +++ b/cpp/src/pdlp/cuopt_c.cpp @@ -113,9 +113,9 @@ cuopt_int_t cuOptReadProblem(const char* filename, cuOptOptimizationProblem* pro std::string filename_str(filename); std::unique_ptr> mps_data_model_ptr; try { - // Dispatches on file extension; see parse_problem for the enumerated rules. + // Dispatches on file extension; see read for the enumerated rules. mps_data_model_ptr = std::make_unique>( - parse_problem(filename_str)); + read(filename_str)); } catch (const std::exception& e) { CUOPT_LOG_INFO("Error parsing input file: %s", e.what()); delete problem_and_stream; diff --git a/cpp/tests/dual_simplex/unit_tests/solve_barrier.cu b/cpp/tests/dual_simplex/unit_tests/solve_barrier.cu index b0cbe624dc..0c8c591e97 100644 --- a/cpp/tests/dual_simplex/unit_tests/solve_barrier.cu +++ b/cpp/tests/dual_simplex/unit_tests/solve_barrier.cu @@ -196,7 +196,7 @@ TEST(barrier, min_x_squared_free_variable_dual_correction) auto path = cuopt::test::get_rapids_dataset_root_dir() + "/quadratic_programming/min_x_squared.mps"; - auto mps_data = cuopt::linear_programming::io::parse_mps(path); + auto mps_data = cuopt::linear_programming::io::read_mps(path); auto settings = cuopt::linear_programming::pdlp_solver_settings_t{}; diff --git a/cpp/tests/linear_programming/grpc/grpc_integration_test.cpp b/cpp/tests/linear_programming/grpc/grpc_integration_test.cpp index 0523a3529c..beb2778400 100644 --- a/cpp/tests/linear_programming/grpc/grpc_integration_test.cpp +++ b/cpp/tests/linear_programming/grpc/grpc_integration_test.cpp @@ -379,7 +379,7 @@ class GrpcIntegrationTestBase : public ::testing::Test { cpu_optimization_problem_t load_problem_from_mps(const std::string& mps_path) { - auto mps_data = cuopt::linear_programming::io::parse_mps(mps_path); + auto mps_data = cuopt::linear_programming::io::read_mps(mps_path); cpu_optimization_problem_t problem; populate_from_mps_data_model(&problem, mps_data); return problem; diff --git a/cpp/tests/linear_programming/parser_test.cpp b/cpp/tests/linear_programming/parser_test.cpp index 4263a594ce..3b93f76f72 100644 --- a/cpp/tests/linear_programming/parser_test.cpp +++ b/cpp/tests/linear_programming/parser_test.cpp @@ -56,14 +56,14 @@ bool file_exists(const std::string& file) namespace { -// Non-template forwarding wrapper around parse_lp_from_string. -// Exists only so EXPECT_THROW(parse_lp_string(R"LP(...)LP"), exc) is parsed +// Non-template forwarding wrapper around read_lp_from_string. +// Exists only so EXPECT_THROW(read_lp_string(R"LP(...)LP"), exc) is parsed // correctly — gtest's macro splits its args on top-level commas, and the // comma inside would otherwise be treated as a macro-arg // separator. -mps_data_model_t parse_lp_string(std::string_view content) +mps_data_model_t read_lp_string(std::string_view content) { - return parse_lp_from_string(content); + return read_lp_from_string(content); } // Returns the index of `name` in the variable list, or -1 if absent. @@ -118,23 +118,23 @@ double q_entry(const mps_data_model_t& m, int row, int col) // MPS and LP TEST_F cases within a fixture share the same `check_model` // method, so the expected values live in exactly one place per fixture. // -// All fixtures inherit a common base that supplies parse_mps_file and -// parse_lp_file helpers. +// All fixtures inherit a common base that supplies read_mps_file and +// read_lp_file helpers. // =========================================================================== class parser_fixture_base : public ::testing::Test { protected: - static mps_data_model_t parse_mps_file(const std::string& file, - bool fixed_format = true) + static mps_data_model_t read_mps_file(const std::string& file, + bool fixed_format = true) { const std::string& root = cuopt::test::get_rapids_dataset_root_dir(); - return parse_mps(root + "/" + file, fixed_format); + return read_mps(root + "/" + file, fixed_format); } - static mps_data_model_t parse_lp_file(const std::string& file) + static mps_data_model_t read_lp_file(const std::string& file) { const std::string& root = cuopt::test::get_rapids_dataset_root_dir(); - return parse_lp(root + "/" + file); + return read_lp(root + "/" + file); } }; @@ -359,7 +359,7 @@ TEST(mps_parser, bad_mps_files) TEST_F(good_mps_1_test, mps) { - check_model(parse_mps_file("linear_programming/good-mps-1.mps")); + check_model(read_mps_file("linear_programming/good-mps-1.mps")); // Parser-struct fields that are MPS-only (not exposed via the data model). auto mps = read_from_mps("linear_programming/good-mps-1.mps"); EXPECT_EQ("good-1", mps.problem_name); @@ -369,19 +369,19 @@ TEST_F(good_mps_1_test, mps) EXPECT_EQ(LesserThanOrEqual, mps.row_types[1]); } -TEST_F(good_mps_1_test, lp) { check_model(parse_lp_file("linear_programming/good-mps-1.lp")); } +TEST_F(good_mps_1_test, lp) { check_model(read_lp_file("linear_programming/good-mps-1.lp")); } -// Compressed-LP coverage: parse_lp() shares file_to_string() with parse_mps(), +// Compressed-LP coverage: read_lp() shares file_to_string() with read_mps(), // so the same dlopen-based decompression path that handles .mps.gz / .mps.bz2 // must also work for .lp.gz / .lp.bz2. TEST_F(good_mps_1_test, lp_zlib_compressed) { - check_model(parse_lp_file("linear_programming/good-mps-1.lp.gz")); + check_model(read_lp_file("linear_programming/good-mps-1.lp.gz")); } TEST_F(good_mps_1_test, lp_bzip2_compressed) { - check_model(parse_lp_file("linear_programming/good-mps-1.lp.bz2")); + check_model(read_lp_file("linear_programming/good-mps-1.lp.bz2")); } TEST(mps_parser, good_mps_file_clrf) @@ -594,7 +594,7 @@ TEST(mps_parser_free_format, bad_mps_files_free_format) TEST_F(up_low_bounds_test, mps) { - check_model(parse_mps_file("linear_programming/lp_model_with_var_bounds.mps", false)); + check_model(read_mps_file("linear_programming/lp_model_with_var_bounds.mps", false)); auto mps = read_from_mps("linear_programming/lp_model_with_var_bounds.mps", false); EXPECT_EQ("lp_model_with_var_bounds", mps.problem_name); EXPECT_EQ("OBJ", mps.objective_name); @@ -604,54 +604,54 @@ TEST_F(up_low_bounds_test, mps) TEST_F(up_low_bounds_test, lp) { - check_model(parse_lp_file("linear_programming/lp_model_with_var_bounds.lp")); + check_model(read_lp_file("linear_programming/lp_model_with_var_bounds.lp")); } TEST_F(good_mps_1_test, mps_free_format) { // free-format-mps-1.mps encodes the same problem as good-mps-1 with default // [0, +inf) bounds (no BOUNDS section), so it satisfies the same checker. - check_model(parse_mps_file("linear_programming/free-format-mps-1.mps", false)); + check_model(read_mps_file("linear_programming/free-format-mps-1.mps", false)); } TEST_F(some_var_bounds_test, mps) { - check_model(parse_mps_file("linear_programming/good-mps-some-var-bounds.mps")); + check_model(read_mps_file("linear_programming/good-mps-some-var-bounds.mps")); } TEST_F(some_var_bounds_test, lp) { - check_model(parse_lp_file("linear_programming/good-mps-some-var-bounds.lp")); + check_model(read_lp_file("linear_programming/good-mps-some-var-bounds.lp")); } TEST_F(fixed_var_bound_test, mps) { - check_model(parse_mps_file("linear_programming/good-mps-fixed-var.mps")); + check_model(read_mps_file("linear_programming/good-mps-fixed-var.mps")); } TEST_F(fixed_var_bound_test, lp) { - check_model(parse_lp_file("linear_programming/good-mps-fixed-var.lp")); + check_model(read_lp_file("linear_programming/good-mps-fixed-var.lp")); } TEST_F(free_var_bound_test, mps) { - check_model(parse_mps_file("linear_programming/good-mps-free-var.mps")); + check_model(read_mps_file("linear_programming/good-mps-free-var.mps")); } TEST_F(free_var_bound_test, lp) { - check_model(parse_lp_file("linear_programming/good-mps-free-var.lp")); + check_model(read_lp_file("linear_programming/good-mps-free-var.lp")); } TEST_F(lower_inf_var_bound_test, mps) { - check_model(parse_mps_file("linear_programming/good-mps-lower-bound-inf-var.mps")); + check_model(read_mps_file("linear_programming/good-mps-lower-bound-inf-var.mps")); } TEST_F(lower_inf_var_bound_test, lp) { - check_model(parse_lp_file("linear_programming/good-mps-lower-bound-inf-var.lp")); + check_model(read_lp_file("linear_programming/good-mps-lower-bound-inf-var.lp")); } TEST(mps_bounds, rhs_cost) @@ -664,12 +664,12 @@ TEST(mps_bounds, rhs_cost) TEST_F(upper_inf_var_bound_test, mps) { - check_model(parse_mps_file("linear_programming/good-mps-upper-bound-inf-var.mps")); + check_model(read_mps_file("linear_programming/good-mps-upper-bound-inf-var.mps")); } TEST_F(upper_inf_var_bound_test, lp) { - check_model(parse_lp_file("linear_programming/good-mps-upper-bound-inf-var.lp")); + check_model(read_lp_file("linear_programming/good-mps-upper-bound-inf-var.lp")); } TEST(mps_ranges, fixed_ranges) @@ -685,7 +685,7 @@ TEST(mps_ranges, fixed_ranges) std::string rel_file{}; const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir(); rel_file = rapidsDatasetRootDir + "/" + file; - auto data_model = parse_mps(rel_file, true); + auto data_model = read_mps(rel_file, true); EXPECT_NEAR(1.2, data_model.get_constraint_lower_bounds()[0], tolerance); // ROW1 lower bound EXPECT_NEAR(5.4, data_model.get_constraint_upper_bounds()[0], tolerance); // ROW1 upper bound @@ -726,7 +726,7 @@ TEST(mps_ranges, free_ranges) std::string rel_file{}; const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir(); rel_file = rapidsDatasetRootDir + "/" + file; - auto data_model = parse_mps(rel_file, false); + auto data_model = read_mps(rel_file, false); EXPECT_NEAR(1.2, data_model.get_constraint_lower_bounds()[0], tolerance); // ROW1 lower bound EXPECT_NEAR(5.4, data_model.get_constraint_upper_bounds()[0], tolerance); // ROW1 upper bound @@ -819,7 +819,7 @@ TEST(mps_bounds, unsupported_or_invalid_mps_types) TEST_F(mip_with_bounds_test, mps) { - check_model(parse_mps_file("mixed_integer_programming/good-mip-mps-1.mps", false)); + check_model(read_mps_file("mixed_integer_programming/good-mip-mps-1.mps", false)); auto mps = read_from_mps("mixed_integer_programming/good-mip-mps-1.mps", false); EXPECT_EQ("COST", mps.objective_name); ASSERT_EQ(int(2), mps.row_types.size()); @@ -829,7 +829,7 @@ TEST_F(mip_with_bounds_test, mps) TEST_F(mip_with_bounds_test, lp) { - check_model(parse_lp_file("mixed_integer_programming/good-mip-mps-1.lp")); + check_model(read_lp_file("mixed_integer_programming/good-mip-mps-1.lp")); } TEST(mps_parser, good_mps_file_mip_no_marker) @@ -879,22 +879,22 @@ TEST(mps_parser, good_mps_file_mip_no_marker) TEST_F(mip_no_bounds_test, mps) { - check_model(parse_mps_file("mixed_integer_programming/good-mip-mps-no-bounds.mps", false)); + check_model(read_mps_file("mixed_integer_programming/good-mip-mps-no-bounds.mps", false)); } TEST_F(mip_no_bounds_test, lp) { - check_model(parse_lp_file("mixed_integer_programming/good-mip-mps-no-bounds.lp")); + check_model(read_lp_file("mixed_integer_programming/good-mip-mps-no-bounds.lp")); } TEST_F(mip_partial_bounds_test, mps) { - check_model(parse_mps_file("mixed_integer_programming/good-mip-mps-partial-bounds.mps", false)); + check_model(read_mps_file("mixed_integer_programming/good-mip-mps-partial-bounds.mps", false)); } TEST_F(mip_partial_bounds_test, lp) { - check_model(parse_lp_file("mixed_integer_programming/good-mip-mps-partial-bounds.lp")); + check_model(read_lp_file("mixed_integer_programming/good-mip-mps-partial-bounds.lp")); } #ifdef MPS_PARSER_WITH_BZIP2 @@ -1003,7 +1003,7 @@ TEST(qps_parser, test_qps_files) { // Test QP_Test_1.qps if it exists if (file_exists("quadratic_programming/QP_Test_1.qps")) { - auto parsed_data = parse_mps( + auto parsed_data = read_mps( cuopt::test::get_rapids_dataset_root_dir() + "/quadratic_programming/QP_Test_1.qps", false); EXPECT_EQ("QP_Test_1", parsed_data.get_problem_name()); @@ -1023,7 +1023,7 @@ TEST(qps_parser, test_qps_files) // Test QP_Test_2.qps if it exists if (file_exists("quadratic_programming/QP_Test_2.qps")) { - auto parsed_data = parse_mps( + auto parsed_data = read_mps( cuopt::test::get_rapids_dataset_root_dir() + "/quadratic_programming/QP_Test_2.qps", false); EXPECT_EQ("QP_Test_2", parsed_data.get_problem_name()); @@ -1194,14 +1194,14 @@ TEST(mps_roundtrip, linear_programming_basic) temp_file_t temp_file(".mps"); // Read original - auto original = parse_mps(input_file, true); + auto original = read_mps(input_file, true); // Write to temp file mps_writer_t writer(original); writer.write(temp_file.string()); // Read back - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); // Compare compare_data_models(original, reloaded); @@ -1218,14 +1218,14 @@ TEST(mps_roundtrip, linear_programming_with_bounds) temp_file_t temp_file(".mps"); // Read original - auto original = parse_mps(input_file, false); + auto original = read_mps(input_file, false); // Write to temp file mps_writer_t writer(original); writer.write(temp_file.string()); // Read back - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); // Compare compare_data_models(original, reloaded); @@ -1242,7 +1242,7 @@ TEST(mps_roundtrip, quadratic_programming_qp_test_1) temp_file_t temp_file(".mps"); // Read original - auto original = parse_mps(input_file, false); + auto original = read_mps(input_file, false); ASSERT_TRUE(original.has_quadratic_objective()) << "Original should have quadratic objective"; // Write to temp file @@ -1250,7 +1250,7 @@ TEST(mps_roundtrip, quadratic_programming_qp_test_1) writer.write(temp_file.string()); // Read back - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); ASSERT_TRUE(reloaded.has_quadratic_objective()) << "Reloaded should have quadratic objective"; // Compare @@ -1268,7 +1268,7 @@ TEST(mps_roundtrip, quadratic_programming_qp_test_2) temp_file_t temp_file(".mps"); // Read original - auto original = parse_mps(input_file, false); + auto original = read_mps(input_file, false); ASSERT_TRUE(original.has_quadratic_objective()) << "Original should have quadratic objective"; // Write to temp file @@ -1276,7 +1276,7 @@ TEST(mps_roundtrip, quadratic_programming_qp_test_2) writer.write(temp_file.string()); // Read back - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); ASSERT_TRUE(reloaded.has_quadratic_objective()) << "Reloaded should have quadratic objective"; // Compare @@ -1295,12 +1295,12 @@ TEST_F(good_mps_1_test, lp_roundtrip) { temp_file_t temp_file(".mps"); - auto original = parse_lp_file("linear_programming/good-mps-1.lp"); + auto original = read_lp_file("linear_programming/good-mps-1.lp"); mps_writer_t writer(original); writer.write(temp_file.string()); - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); compare_data_models(original, reloaded); } @@ -1309,12 +1309,12 @@ TEST_F(up_low_bounds_test, lp_roundtrip) { temp_file_t temp_file(".mps"); - auto original = parse_lp_file("linear_programming/lp_model_with_var_bounds.lp"); + auto original = read_lp_file("linear_programming/lp_model_with_var_bounds.lp"); mps_writer_t writer(original); writer.write(temp_file.string()); - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); compare_data_models(original, reloaded); } @@ -1323,23 +1323,23 @@ TEST_F(mip_with_bounds_test, lp_roundtrip) { temp_file_t temp_file(".mps"); - auto original = parse_lp_file("mixed_integer_programming/good-mip-mps-1.lp"); + auto original = read_lp_file("mixed_integer_programming/good-mip-mps-1.lp"); mps_writer_t writer(original); writer.write(temp_file.string()); - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); compare_data_models(original, reloaded); } // ================================================================================================ -// LP syntax / feature / error-path tests (parse_lp on inline LP content) +// LP syntax / feature / error-path tests (read_lp on inline LP content) // ================================================================================================ TEST(lp_parser, trivial) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -1369,7 +1369,7 @@ End TEST(lp_parser, basic_lp_with_float_coefficients) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x1 + x2 Subject To @@ -1401,7 +1401,7 @@ End TEST(lp_parser, maximize_flips_sense) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Maximize 3 x + 2 y Subject To @@ -1419,7 +1419,7 @@ End TEST(lp_parser, equality_constraints) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize c1 + 2 c2 + 3 c3 + 4 c4 Subject To @@ -1444,7 +1444,7 @@ End TEST(lp_parser, mixed_constraint_relations) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + 2 y + 3 z Subject To @@ -1470,7 +1470,7 @@ End TEST(lp_parser, free_and_negative_lower_bound_variables) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize xfree + xneg_lb + xstd Subject To @@ -1500,7 +1500,7 @@ End TEST(lp_parser, bounds_variety) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize xfixed + xub_only + xlb_pos Subject To @@ -1525,7 +1525,7 @@ End TEST(lp_parser, general_integers) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Maximize 3 x + 5 y Subject To @@ -1547,7 +1547,7 @@ End TEST(lp_parser, binaries_set_zero_one_bounds) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Maximize 3 x1 + 5 x2 + 4 x3 + 2 x4 Subject To @@ -1567,7 +1567,7 @@ End TEST(lp_parser, mixed_continuous_integer_binary) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Maximize 3 xc + 4 xi + 7 xb Subject To @@ -1591,7 +1591,7 @@ End TEST(lp_parser, quadratic_diagonal_only) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y + [ 2 x ^2 + 4 y ^2 ] / 2 Subject To @@ -1617,7 +1617,7 @@ End TEST(lp_parser, quadratic_with_cross_terms) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize - 3 x - 4 y - 2 z + [ 2 x ^2 + 2 x * y + 2 y ^2 + 2 y * z + 2 z ^2 ] / 2 Subject To @@ -1652,7 +1652,7 @@ End TEST(lp_parser, miqp_integer_with_quadratic_objective) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize - 4 xi - 2 xc + [ 2 xi ^2 + 2 xc ^2 ] / 2 Subject To @@ -1675,7 +1675,7 @@ End TEST(lp_parser, infeasible_model_parses_faithfully) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -1696,7 +1696,7 @@ End TEST(lp_parser, unbounded_model_parses) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Maximize x + y Subject To @@ -1712,7 +1712,7 @@ End TEST(lp_parser, missing_objective_throws) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Subject To c1: x + y <= 5 End @@ -1722,7 +1722,7 @@ End TEST(lp_parser, unsupported_sos_section_throws) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1736,7 +1736,7 @@ End TEST(lp_parser, semi_continuous_basic) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -1763,7 +1763,7 @@ TEST(lp_parser, semi_continuous_bare_semi_keyword) { // The LP-format convention accepts the bare "Semi" keyword as a synonym // for the "Semi-Continuous" section header. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -1785,7 +1785,7 @@ TEST(lp_parser, semi_continuous_bare_semis_keyword) { // The LP-format convention accepts the bare "Semis" keyword as a synonym // for the "Semi-Continuous" section header. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -1805,7 +1805,7 @@ End TEST(lp_parser, semi_continuous_default_lower_is_zero) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -1827,7 +1827,7 @@ End TEST(lp_parser, semi_continuous_missing_upper_throws) { // No upper bound specified ⇒ infinity ⇒ semantics degenerate, reject. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1843,7 +1843,7 @@ TEST(lp_parser, semi_continuous_and_generals_conflict_throws) { // Variable appearing in both Semi-Continuous and Generals is ambiguous // (integer vs. continuous-or-zero) ⇒ reject. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1861,7 +1861,7 @@ End TEST(lp_parser, semi_continuous_and_binaries_conflict_throws) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1880,7 +1880,7 @@ End TEST(lp_parser, semi_continuous_before_generals_conflict_throws) { // Conflict must also be detected when Semi-Continuous is declared first. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1898,7 +1898,7 @@ End TEST(lp_parser, unsupported_pwlobj_section_throws) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1913,7 +1913,7 @@ End TEST(lp_parser, unsupported_lazy_constraints_section_throws) { // Lazy constraints and user cuts are scope-limited out: LP/MIP/QP only. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1927,7 +1927,7 @@ End TEST(lp_parser, unsupported_user_cuts_section_throws) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -1941,13 +1941,13 @@ End TEST(lp_parser, unknown_file_throws) { - auto call = [] { return parse_lp("/definitely/does/not/exist.lp"); }; + auto call = [] { return read_lp("/definitely/does/not/exist.lp"); }; EXPECT_THROW(call(), std::logic_error); } TEST(lp_parser, case_insensitive_section_keywords) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( MINIMIZE x SUBJECT TO @@ -1962,7 +1962,7 @@ END TEST(lp_parser, backslash_comments_are_ignored) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( \ This is a comment Minimize x \ trailing comment @@ -1977,7 +1977,7 @@ End TEST(lp_parser, missing_end_warns_but_succeeds) { // No End — should still parse. (A warning is printed; see parse_all().) - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -1988,7 +1988,7 @@ Subject To TEST(lp_parser, auto_generates_names_for_unlabeled_constraints) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -2004,7 +2004,7 @@ End TEST(lp_parser, infinity_keyword_in_bounds) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -2023,7 +2023,7 @@ End TEST(lp_parser, coefficient_one_implicit_with_leading_minus) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize - x + y Subject To @@ -2043,7 +2043,7 @@ TEST(lp_parser, quadratic_without_slash_two_is_rejected) // The quadratic bracket in the objective must be followed by '/ 2'. // Without it there's no unambiguous way to tell whether the user meant // '/ 2' and forgot or intended the bare coefficients, so cuopt rejects. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize [ 1 x ^2 ] Subject To @@ -2058,7 +2058,7 @@ TEST(lp_parser, leading_coefficient_before_objective_bracket_rejected) // '2 [ x^2 ] / 2' is ambiguous between "constant 2 plus 0.5 x^2" and // "scalar 2 times 0.5 x^2"; the LP convention is to place coefficients // inside the brackets, so reject. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize 2 [ x ^ 2 ] / 2 Subject To @@ -2071,7 +2071,7 @@ End TEST(lp_parser, leading_coefficient_before_constraint_bracket_rejected) { // Same ambiguity as the objective case, in a quadratic constraint. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -2085,7 +2085,7 @@ TEST(lp_parser, constant_then_signed_bracket_in_objective_is_accepted) { // The positive form: a literal constant in the objective followed by a // signed quadratic bracket still parses (constant becomes objective offset). - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize 5 + [ x ^ 2 ] / 2 Subject To @@ -2100,7 +2100,7 @@ TEST(lp_parser, stray_star_after_number_without_variable_rejected) { // '3 *' followed by a relation, section header, or EOL must error rather // than silently drop the '*' and treat the '3' as a bare constant. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize 3 * Subject To @@ -2113,7 +2113,7 @@ End TEST(lp_parser, explicit_star_between_coefficient_and_variable_is_accepted) { // The positive form: '3 * x' is the same as '3 x'. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize 3 * x Subject To @@ -2139,7 +2139,7 @@ const auto& nth_qc(const mps_data_model_t& m, size_t k) TEST(lp_parser, qc_basic_diagonal_only) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -2166,7 +2166,7 @@ TEST(lp_parser, qc_cross_term_splits_symmetrically) { // `4 x*y` in the LP source means coefficient on x_i * x_j = 4 in the // symmetric x^T Q x. Split into Q[x,y] = Q[y,x] = 2 in the CSR. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -2186,7 +2186,7 @@ End TEST(lp_parser, qc_linear_and_quadratic_mixed) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -2212,7 +2212,7 @@ TEST(lp_parser, qc_multiple_constraints_indexing) { // 2 linear constraints, then 2 quadratic constraints. Per the data-model // convention, quadratic rows are indexed after all linear rows. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -2236,7 +2236,7 @@ TEST(lp_parser, qc_outer_minus_sign_flips_quadratic) // After moving the constant to the RHS: -x^2 - 2 x <= rhs - 5. // Here the RHS is 10, so the row becomes: -x^2 - 2 x <= 5 (in x^T Q x form // Q[x,x] = -1). - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -2259,7 +2259,7 @@ TEST(lp_parser, bare_linear_inside_objective_bracket_rejected) // The LP-format convention reserves `[ ... ]` for quadratic terms only // (squared and product). A bare linear term like `2 x` inside the // bracket is malformed; the user should write it outside. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize obj: [ x ^ 2 + 2 x ] / 2 Subject To @@ -2271,7 +2271,7 @@ End TEST(lp_parser, bare_linear_inside_constraint_bracket_rejected) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -2283,7 +2283,7 @@ End TEST(lp_parser, qc_named_constraint) { - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -2296,7 +2296,7 @@ End TEST(lp_parser, qc_ge_relation_throws) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -2308,7 +2308,7 @@ End TEST(lp_parser, qc_eq_relation_throws) { - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -2322,7 +2322,7 @@ TEST(lp_parser, qc_with_slash_two_is_rejected) { // '/ 2' is reserved for the objective bracket; using it in a constraint // bracket is rejected so the convention is unambiguous. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -2336,7 +2336,7 @@ TEST(lp_parser, qc_linear_only_bracket_is_rejected) { // A bracket with no quadratic terms inside is meaningless in a constraint // (the user could just write the linear terms directly). - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -2350,7 +2350,7 @@ TEST(lp_parser, qc_objective_quadratic_still_requires_slash_two) { // Regression: the existing '/ 2' requirement on the objective bracket // must not change after adding constraint-bracket support. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize [ x ^ 2 ] Subject To @@ -2363,7 +2363,7 @@ End TEST(lp_parser, duplicate_coefficient_accumulates) { // Repeated variable in the objective should sum coefficients. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize 2 x + 3 x + y Subject To @@ -2380,7 +2380,7 @@ TEST(lp_parser, subject_to_variant_st_dot) { // 'st.' with a trailing period is a Subject-To synonym in the LP-format // convention. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x st. @@ -2396,7 +2396,7 @@ TEST(lp_parser, swapped_relational_operators_eq_lt_and_eq_gt) // '=<' is an alias for '<=' and '=>' for '>=', in both constraints and // bounds. Tokenizer must produce LessEq / GreaterEq tokens regardless of // spelling. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x + y Subject To @@ -2424,7 +2424,7 @@ TEST(lp_parser, variable_names_with_special_characters) // Per the LP-format convention, variable names may contain assorted // punctuation beyond letters + underscore. The names are treated as // opaque identifiers; cuopt just has to keep them distinct. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x!a + x#b + x$c + x@d + x'e + x~f + x.g + x_h + x|i + x{j} + x(k) + a/b Subject To @@ -2442,7 +2442,7 @@ TEST(lp_parser, negative_upper_without_explicit_lower_throws) { // 'x <= -1' with no explicit lower makes the default lb=0 collide with the // upper. cuopt rejects rather than accept a silently infeasible problem. - EXPECT_THROW(parse_lp_string(R"LP( + EXPECT_THROW(read_lp_string(R"LP( Minimize x Subject To @@ -2457,7 +2457,7 @@ End TEST(lp_parser, negative_upper_with_explicit_lower_ok) { // Same test as above, but now the lower bound is explicit: no error. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -2475,7 +2475,7 @@ End TEST(lp_parser, negative_upper_with_range_bound_ok) { // -5 <= x <= -1 declares both bounds in a single line: no error. - auto m = parse_lp_string(R"LP( + auto m = read_lp_string(R"LP( Minimize x Subject To @@ -2490,7 +2490,7 @@ End } // ================================================================================================ -// parse_problem dispatch tests +// read dispatch tests // // Verifies the extension-based dispatch used by cuopt_cli and the C API. // ================================================================================================ @@ -2498,8 +2498,8 @@ End namespace { // Writes `content` to a temp file with the given suffix, parses it via -// parse_problem, and returns the resulting model. temp_file_t removes the -// file on every scope exit (including when parse_problem throws). +// read, and returns the resulting model. temp_file_t removes the +// file on every scope exit (including when read throws). mps_data_model_t dispatch_parse(const std::string& content, const std::string& suffix) { temp_file_t tmp(suffix); @@ -2507,7 +2507,7 @@ mps_data_model_t dispatch_parse(const std::string& content, const s std::ofstream out(tmp.string()); out << content; } - return parse_problem(tmp.string()); + return read(tmp.string()); } constexpr const char* kTrivialLp = R"LP( @@ -2536,7 +2536,7 @@ ENDATA } // namespace -TEST(parse_problem, lp_extension_dispatches_to_lp_parser) +TEST(read, lp_extension_dispatches_to_lp_parser) { auto m = dispatch_parse(kTrivialLp, ".lp"); ASSERT_EQ(m.get_variable_names().size(), 1u); @@ -2544,26 +2544,26 @@ TEST(parse_problem, lp_extension_dispatches_to_lp_parser) EXPECT_NEAR(m.get_variable_upper_bounds()[0], 10.0, tolerance); } -TEST(parse_problem, lp_gz_extension_dispatches_to_lp_parser) +TEST(read, lp_gz_extension_dispatches_to_lp_parser) { // Real compressed LP fixture; successful parse proves dispatch picked the - // LP path. (Routing a .lp.gz to parse_mps would either fail at + // LP path. (Routing a .lp.gz to read_mps would either fail at // decompression or fail to parse the LP content as MPS.) - auto m = parse_problem(cuopt::test::get_rapids_dataset_root_dir() + - "/linear_programming/good-mps-1.lp.gz"); + auto m = read(cuopt::test::get_rapids_dataset_root_dir() + + "/linear_programming/good-mps-1.lp.gz"); ASSERT_EQ(m.get_variable_names().size(), 2u); EXPECT_EQ(m.get_variable_names()[0], "VAR1"); } -TEST(parse_problem, lp_bz2_extension_dispatches_to_lp_parser) +TEST(read, lp_bz2_extension_dispatches_to_lp_parser) { - auto m = parse_problem(cuopt::test::get_rapids_dataset_root_dir() + - "/linear_programming/good-mps-1.lp.bz2"); + auto m = read(cuopt::test::get_rapids_dataset_root_dir() + + "/linear_programming/good-mps-1.lp.bz2"); ASSERT_EQ(m.get_variable_names().size(), 2u); EXPECT_EQ(m.get_variable_names()[0], "VAR1"); } -TEST(parse_problem, mps_extension_dispatches_to_mps_parser) +TEST(read, mps_extension_dispatches_to_mps_parser) { auto m = dispatch_parse(kTrivialMps, ".mps"); ASSERT_EQ(m.get_variable_names().size(), 1u); @@ -2571,45 +2571,45 @@ TEST(parse_problem, mps_extension_dispatches_to_mps_parser) EXPECT_NEAR(m.get_variable_upper_bounds()[0], 10.0, tolerance); } -TEST(parse_problem, qps_extension_dispatches_to_mps_parser) +TEST(read, qps_extension_dispatches_to_mps_parser) { // QPS is a superset of MPS; the MPS parser handles both. We just need - // parse_problem to route ".qps" to it. + // read to route ".qps" to it. auto m = dispatch_parse(kTrivialMps, ".qps"); ASSERT_EQ(m.get_variable_names().size(), 1u); EXPECT_EQ(m.get_variable_names()[0], "x"); } -TEST(parse_problem, mps_gz_extension_dispatches_to_mps_parser) +TEST(read, mps_gz_extension_dispatches_to_mps_parser) { - auto m = parse_problem(cuopt::test::get_rapids_dataset_root_dir() + - "/linear_programming/good-mps-1.mps.gz"); + auto m = read(cuopt::test::get_rapids_dataset_root_dir() + + "/linear_programming/good-mps-1.mps.gz"); EXPECT_EQ("good-1", m.get_problem_name()); } -TEST(parse_problem, mps_bz2_extension_dispatches_to_mps_parser) +TEST(read, mps_bz2_extension_dispatches_to_mps_parser) { - auto m = parse_problem(cuopt::test::get_rapids_dataset_root_dir() + - "/linear_programming/good-mps-1.mps.bz2"); + auto m = read(cuopt::test::get_rapids_dataset_root_dir() + + "/linear_programming/good-mps-1.mps.bz2"); EXPECT_EQ("good-1", m.get_problem_name()); } -TEST(parse_problem, uppercase_lp_extension_dispatches_to_lp_parser) +TEST(read, uppercase_lp_extension_dispatches_to_lp_parser) { - // Matching is case-insensitive: .LP must still route to parse_lp. + // Matching is case-insensitive: .LP must still route to read_lp. auto m = dispatch_parse(kTrivialLp, ".LP"); ASSERT_EQ(m.get_variable_names().size(), 1u); EXPECT_EQ(m.get_variable_names()[0], "x"); } -TEST(parse_problem, mixed_case_mps_extension_dispatches_to_mps_parser) +TEST(read, mixed_case_mps_extension_dispatches_to_mps_parser) { auto m = dispatch_parse(kTrivialMps, ".MpS"); ASSERT_EQ(m.get_variable_names().size(), 1u); EXPECT_EQ(m.get_variable_names()[0], "x"); } -TEST(parse_problem, unrecognized_extension_throws) +TEST(read, unrecognized_extension_throws) { // Extensionless and unrelated suffixes are rejected; case doesn't matter // (matching is case-insensitive, so ".lpgz" stays rejected too). @@ -2799,7 +2799,7 @@ TEST(qps_parser, qcmatrix_mps_linear_rhs_and_bounds) if (!file_exists("qcqp/QC_Test_1.mps")) { GTEST_SKIP() << "qcqp/QC_Test_1.mps not in dataset root"; } - const auto model = parse_mps( + const auto model = read_mps( cuopt::test::get_rapids_dataset_root_dir() + "/qcqp/QC_Test_1.mps", false); ASSERT_TRUE(model.has_quadratic_constraints()); @@ -2851,7 +2851,7 @@ TEST(qps_parser, qcqp_p0033_mps_sections) if (!file_exists("qcqp/p0033_qc1.mps")) { GTEST_SKIP() << "qcqp/p0033_qc1.mps not in dataset root"; } - const auto model = parse_mps( + const auto model = read_mps( cuopt::test::get_rapids_dataset_root_dir() + "/qcqp/p0033_qc1.mps", false); EXPECT_EQ(12, model.get_n_constraints()); @@ -2882,17 +2882,17 @@ TEST(mps_roundtrip, qcqp_p0033_qc1) temp_file_t temp_file(".mps"); temp_file_t temp_file_2(".mps"); - auto original = parse_mps(input_file, false); + auto original = read_mps(input_file, false); ASSERT_TRUE(original.has_quadratic_objective()); ASSERT_TRUE(original.has_quadratic_constraints()); mps_writer_t writer(original); writer.write(temp_file.string()); - auto reloaded = parse_mps(temp_file.string(), false); + auto reloaded = read_mps(temp_file.string(), false); mps_writer_t writer_r2(reloaded); writer_r2.write(temp_file_2.string()); - auto reloaded_2 = parse_mps(temp_file_2.string(), false); + auto reloaded_2 = read_mps(temp_file_2.string(), false); compare_data_models(reloaded, reloaded_2); } } // namespace cuopt::linear_programming::io diff --git a/cpp/tests/linear_programming/pdlp_test.cu b/cpp/tests/linear_programming/pdlp_test.cu index d29995efc5..92e09cd155 100644 --- a/cpp/tests/linear_programming/pdlp_test.cu +++ b/cpp/tests/linear_programming/pdlp_test.cu @@ -79,7 +79,7 @@ TEST(pdlp_class, run_double) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -98,7 +98,7 @@ TEST(pdlp_class, precision_mixed) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto settings = pdlp_solver_settings_t{}; settings.method = cuopt::linear_programming::method_t::PDLP; @@ -114,7 +114,7 @@ TEST(pdlp_class, precision_mixed) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto settings_mixed = pdlp_solver_settings_t{}; settings_mixed.method = cuopt::linear_programming::method_t::PDLP; @@ -149,7 +149,7 @@ TEST(pdlp_class, concurrent_pdlp_exception_joins_worker_threads) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto settings = pdlp_solver_settings_t{}; settings.method = cuopt::linear_programming::method_t::Concurrent; @@ -173,7 +173,7 @@ TEST(pdlp_class, run_double_very_low_accuracy) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); cuopt::linear_programming::pdlp_solver_settings_t settings = cuopt::linear_programming::pdlp_solver_settings_t{}; @@ -199,7 +199,7 @@ TEST(pdlp_class, run_double_initial_solution) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); std::vector inital_primal_sol(op_problem.get_n_variables()); std::fill(inital_primal_sol.begin(), inital_primal_sol.end(), 1.0); @@ -221,7 +221,7 @@ TEST(pdlp_class, run_iteration_limit) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); cuopt::linear_programming::pdlp_solver_settings_t settings = cuopt::linear_programming::pdlp_solver_settings_t{}; @@ -246,7 +246,7 @@ TEST(pdlp_class, batch_iteration_limit_updates_additional_termination_stats) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto settings = pdlp_solver_settings_t{}; settings.iteration_limit = 10; @@ -279,7 +279,7 @@ TEST(pdlp_class, batch_settings_overrides_preserve_user_limits_and_tolerances) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); constexpr int batch_size = 2; constexpr double tighter_tolerance = 1e-6; @@ -363,7 +363,7 @@ TEST(pdlp_class, run_time_limit) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/savsched1/savsched1.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); cuopt::linear_programming::pdlp_solver_settings_t settings = cuopt::linear_programming::pdlp_solver_settings_t{}; @@ -408,7 +408,7 @@ TEST(pdlp_class, run_sub_mittleman) auto path = make_path_absolute("linear_programming/" + name + "/" + name + ".mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); // Testing for each solver_mode is ok as it's parsing that is the bottleneck here, not // solving @@ -466,7 +466,7 @@ TEST(pdlp_class, initial_solution_test) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t mps_data_model = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto op_problem = cuopt::linear_programming::mps_data_model_to_optimization_problem( &handle_, mps_data_model); @@ -744,7 +744,7 @@ TEST(pdlp_class, initial_primal_weight_step_size_test) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t mps_data_model = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto op_problem = cuopt::linear_programming::mps_data_model_to_optimization_problem( &handle_, mps_data_model); @@ -932,9 +932,9 @@ TEST(pdlp_class, best_primal_so_far_iteration) solver_settings.method = cuopt::linear_programming::method_t::PDLP; solver_settings.pdlp_solver_mode = cuopt::linear_programming::pdlp_solver_mode_t::Stable2; cuopt::linear_programming::io::mps_data_model_t op_problem1 = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); cuopt::linear_programming::io::mps_data_model_t op_problem2 = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); optimization_problem_solution_t solution1 = solve_lp(&handle1, op_problem1, solver_settings); @@ -962,9 +962,9 @@ TEST(pdlp_class, best_primal_so_far_time) solver_settings.method = cuopt::linear_programming::method_t::PDLP; cuopt::linear_programming::io::mps_data_model_t op_problem1 = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); cuopt::linear_programming::io::mps_data_model_t op_problem2 = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); optimization_problem_solution_t solution1 = solve_lp(&handle1, op_problem1, solver_settings); @@ -992,9 +992,9 @@ TEST(pdlp_class, first_primal_feasible) solver_settings.method = cuopt::linear_programming::method_t::PDLP; cuopt::linear_programming::io::mps_data_model_t op_problem1 = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); cuopt::linear_programming::io::mps_data_model_t op_problem2 = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); optimization_problem_solution_t solution1 = solve_lp(&handle1, op_problem1, solver_settings); @@ -1022,7 +1022,7 @@ TEST(pdlp_class, per_constraint_residual_stable3) solver_settings.method = cuopt::linear_programming::method_t::PDLP; cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto sol = solve_lp(&handle, op_problem, solver_settings); RAFT_CUDA_TRY(cudaDeviceSynchronize()); @@ -1046,7 +1046,7 @@ TEST(pdlp_class, batch_per_constraint_residual_stable3) solver_settings.method = cuopt::linear_programming::method_t::PDLP; cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); constexpr int batch_size = 2; @@ -1093,7 +1093,7 @@ TEST(pdlp_class, batch_per_constraint_residual_different_rhs_stable3) solver_settings.method = cuopt::linear_programming::method_t::PDLP; cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); // Build two climbers that share A and variable bounds but differ on the constraint // lower/upper bounds (RHS): climber 0 keeps the original, climber 1 finite bounds get set to 100 @@ -1138,8 +1138,8 @@ TEST(pdlp_class, batch_per_constraint_residual_different_rhs_stable3) // Reload the original (single-climber) problem and build per-climber views so the // per-row sanity check evaluates each solution against its own constraint bounds. - auto climber0_problem = cuopt::linear_programming::io::parse_mps(path); - auto climber1_problem = cuopt::linear_programming::io::parse_mps(path); + auto climber0_problem = cuopt::linear_programming::io::read_mps(path); + auto climber1_problem = cuopt::linear_programming::io::read_mps(path); climber1_problem.set_constraint_lower_bounds({climber1_lb.data(), climber1_lb.size()}); climber1_problem.set_constraint_upper_bounds({climber1_ub.data(), climber1_ub.size()}); @@ -1176,7 +1176,7 @@ TEST(pdlp_class, first_primal_feasible_stable3) solver_settings.presolver = presolver_t::None; cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); // Wihout first primal feasible we hit iteration limit auto sol_base = solve_lp(&handle, op_problem, solver_settings); @@ -1205,7 +1205,7 @@ TEST(pdlp_class, first_primal_feasible_batch_stable3) auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1252,7 +1252,7 @@ TEST(pdlp_class, first_primal_feasible_batch_different_rhs_stable3) auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1316,7 +1316,7 @@ TEST(pdlp_class, all_primal_feasible_batch_different_rhs_stable3) auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1390,7 +1390,7 @@ TEST(pdlp_class, first_primal_feasible_and_per_constraint_residual_stable3) solver_settings.method = cuopt::linear_programming::method_t::PDLP; cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto sol = solve_lp(&handle, op_problem, solver_settings); RAFT_CUDA_TRY(cudaDeviceSynchronize()); @@ -1413,7 +1413,7 @@ TEST(pdlp_class, first_primal_feasible_and_per_constraint_residual_batch_stable3 auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1460,7 +1460,7 @@ TEST(pdlp_class, first_primal_feasible_and_per_constraint_residual_batch_differe auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1527,7 +1527,7 @@ TEST(pdlp_class, all_primal_feasible_and_per_constraint_residual_batch_different auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1594,7 +1594,7 @@ TEST(pdlp_class, all_primal_feasible_and_per_constraint_residual_batch_many_diff auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1705,7 +1705,7 @@ TEST(pdlp_class, all_primal_feasible_and_per_constraint_residual_batch_many_diff auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1815,7 +1815,7 @@ TEST(pdlp_class, batch_primal_feasible_non_batch_rejected) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1832,7 +1832,7 @@ TEST(pdlp_class, first_primal_feasible_and_batch_primal_feasible_rejected) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/ns1687037/ns1687037.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1870,7 +1870,7 @@ TEST(pdlp_class, warm_start) solver_settings.presolver = presolver_t::None; cuopt::linear_programming::io::mps_data_model_t mps_data_model = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto op_problem1 = cuopt::linear_programming::mps_data_model_to_optimization_problem( &handle, mps_data_model); @@ -1912,7 +1912,7 @@ TEST(pdlp_class, warm_start_stable3_not_supported) solver_settings.presolver = presolver_t::None; cuopt::linear_programming::io::mps_data_model_t mps_data_model = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto op_problem = cuopt::linear_programming::mps_data_model_to_optimization_problem( &handle, mps_data_model); optimization_problem_solution_t solution = solve_lp(op_problem, solver_settings); @@ -1928,7 +1928,7 @@ TEST(pdlp_class, dual_postsolve_size) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1962,7 +1962,7 @@ TEST(dual_simplex, afiro) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); optimization_problem_solution_t solution = solve_lp(&handle_, op_problem, settings); EXPECT_EQ(solution.get_termination_status(), pdlp_termination_status_t::Optimal); @@ -1977,7 +1977,7 @@ TEST(pdlp_class, run_empty_matrix_pdlp) auto path = make_path_absolute("linear_programming/empty_matrix.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -1995,7 +1995,7 @@ TEST(pdlp_class, run_empty_matrix_dual_simplex) auto path = make_path_absolute("linear_programming/empty_matrix.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::Concurrent; @@ -2013,7 +2013,7 @@ TEST(pdlp_class, test_max) auto path = make_path_absolute("linear_programming/good-max.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2033,7 +2033,7 @@ TEST(pdlp_class, test_max_with_offset) auto path = make_path_absolute("linear_programming/max_offset.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2052,7 +2052,7 @@ TEST(pdlp_class, test_lp_no_constraints) auto path = make_path_absolute("linear_programming/lp-model-no-constraints.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path); + cuopt::linear_programming::io::read_mps(path); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.presolver = presolver_t::None; @@ -2079,7 +2079,7 @@ TEST(pdlp_class, simple_batch_afiro) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2161,7 +2161,7 @@ TEST(pdlp_class, simple_batch_different_bounds) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2217,7 +2217,7 @@ TEST(pdlp_class, more_complex_batch_different_bounds) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2310,7 +2310,7 @@ TEST(pdlp_class, simple_batch_different_objectives) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2378,7 +2378,7 @@ TEST(pdlp_class, simple_batch_different_offsets) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2418,7 +2418,7 @@ TEST(pdlp_class, simple_batch_different_objectives_and_offsets) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2476,7 +2476,7 @@ TEST(pdlp_class, simple_batch_different_constraint_bounds) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2544,7 +2544,7 @@ TEST(pdlp_class, simple_batch_everything_different) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -2664,7 +2664,7 @@ TEST(pdlp_class, run_batch_pdlp_fixed_rejects_partial_per_climber_expansion) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); constexpr int batch_size = 3; const auto n_vars = static_cast(op_problem.get_n_variables()); @@ -2746,7 +2746,7 @@ TEST(pdlp_class, run_batch_pdlp_rejects_invalid_new_bounds) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto expect_validation_error = [&](pdlp_solver_settings_t settings) { auto gpu_op = cuopt::linear_programming::mps_data_model_to_optimization_problem( @@ -2854,7 +2854,7 @@ TEST(pdlp_class, run_batch_pdlp_rejects_save_best_primal_so_far) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); // Splitting path: trigger batch mode via a non-empty new_bounds list (size > 1). { @@ -2907,7 +2907,7 @@ TEST(pdlp_class, DISABLED_cupdlpx_infeasible_detection_afiro_new_bounds) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); for (size_t i = 1; i < 8; ++i) { op_problem.get_variable_lower_bounds()[i] = 7.0; @@ -2936,7 +2936,7 @@ TEST(pdlp_class, DISABLED_cupdlpx_batch_infeasible_detection) auto path = make_path_absolute("linear_programming/good-mps-fixed-ranges.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const std::vector& variable_lower_bounds = op_problem.get_variable_lower_bounds(); const std::vector& variable_upper_bounds = op_problem.get_variable_upper_bounds(); @@ -2976,7 +2976,7 @@ TEST(pdlp_class, DISABLED_cupdlpx_infeasible_detection_batch_afiro_new_bounds) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); // Use a ref problem that is infeasible auto op_problem_ref = op_problem; @@ -3020,7 +3020,7 @@ TEST(pdlp_class, new_bounds) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -3065,7 +3065,7 @@ TEST(pdlp_class, big_batch_afiro) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -3153,7 +3153,7 @@ TEST(pdlp_class, DISABLED_simple_batch_optimal_and_infeasible) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -3185,7 +3185,7 @@ TEST(pdlp_class, DISABLED_larger_batch_optimal_and_infeasible) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -3231,7 +3231,7 @@ TEST(pdlp_class, strong_branching_test) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const std::vector fractional = {1, 2, 4}; const std::vector root_soln_x = {0.891, 0.109, 0.636429}; @@ -3338,7 +3338,7 @@ TEST(pdlp_class, strong_branching_user_api) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const std::vector fractional = {1, 2, 4}; const std::vector root_soln_x = {0.891, 0.109, 0.636429}; @@ -3426,7 +3426,7 @@ TEST(pdlp_class, strong_branching_multi_bounds_per_climber) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -3505,7 +3505,7 @@ TEST(pdlp_class, run_batch_pdlp_many_different_bounds) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const auto& variable_lower_bounds = op_problem.get_variable_lower_bounds(); const auto& variable_upper_bounds = op_problem.get_variable_upper_bounds(); @@ -3619,7 +3619,7 @@ TEST(pdlp_class, run_batch_pdlp_many_different_bounds_good_mps_some_var_bounds) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/good-mps-some-var-bounds.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const auto& variable_lower_bounds = op_problem.get_variable_lower_bounds(); const auto& variable_upper_bounds = op_problem.get_variable_upper_bounds(); @@ -3717,7 +3717,7 @@ TEST(pdlp_class, run_batch_fixed_api_many_different_bounds_good_mps_some_var_bou const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/good-mps-some-var-bounds.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const auto& variable_lower_bounds = op_problem.get_variable_lower_bounds(); const auto& variable_upper_bounds = op_problem.get_variable_upper_bounds(); @@ -3809,7 +3809,7 @@ TEST(pdlp_class, many_different_bounds) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/good-mps-some-var-bounds.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const auto& variable_lower_bounds = op_problem.get_variable_lower_bounds(); const auto& variable_upper_bounds = op_problem.get_variable_upper_bounds(); @@ -3902,7 +3902,7 @@ TEST(pdlp_class, some_climber_hit_iteration_limit) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/good-mps-some-var-bounds.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const auto& variable_lower_bounds = op_problem.get_variable_lower_bounds(); const auto& variable_upper_bounds = op_problem.get_variable_upper_bounds(); @@ -3984,7 +3984,7 @@ TEST(pdlp_class, precision_single) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -4004,7 +4004,7 @@ TEST(pdlp_class, precision_single_crossover) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -4025,7 +4025,7 @@ TEST(pdlp_class, precision_single_concurrent) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::Concurrent; @@ -4045,7 +4045,7 @@ TEST(pdlp_class, precision_single_papilo_presolve) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -4065,7 +4065,7 @@ TEST(pdlp_class, precision_single_pslp_presolve) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -4137,7 +4137,7 @@ TEST(pdlp_class, shared_sb_view_batch_pre_solved) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const std::vector fractional = {1, 2, 4}; const std::vector root_soln_x = {0.891, 0.109, 0.636429}; @@ -4197,7 +4197,7 @@ TEST(pdlp_class, shared_sb_view_concurrent_mark) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const std::vector fractional = {1, 2, 4}; const std::vector root_soln_x = {0.891, 0.109, 0.636429}; @@ -4269,7 +4269,7 @@ TEST(pdlp_class, shared_sb_view_all_infeasible) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); const std::vector fractional = {1, 2, 4}; const std::vector root_soln_x = {0.891, 0.109, 0.636429}; @@ -4336,7 +4336,7 @@ TEST(pdlp_class, big_batch_fixed_path) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; @@ -4454,7 +4454,7 @@ TEST(pdlp_class, batch_bound_objective_rescaling_factors_match_input_expansion) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); constexpr int batch_size = 3; const int n_vars = op_problem.get_n_variables(); @@ -4598,7 +4598,7 @@ TEST(pdlp_class, batch_with_optimal_size_query) auto path = make_path_absolute("linear_programming/afiro_original.mps"); cuopt::linear_programming::io::mps_data_model_t op_problem = - cuopt::linear_programming::io::parse_mps(path, true); + cuopt::linear_programming::io::read_mps(path, true); auto solver_settings = pdlp_solver_settings_t{}; solver_settings.method = cuopt::linear_programming::method_t::PDLP; diff --git a/cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu b/cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu index cb6eb43367..005e2e7da7 100644 --- a/cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu +++ b/cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu @@ -31,7 +31,7 @@ cuopt::linear_programming::io::mps_data_model_t read_from_mps( // assume relative paths are relative to RAPIDS_DATASET_ROOT_DIR const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir(); rel_file = rapidsDatasetRootDir + "/" + file; - return cuopt::linear_programming::io::parse_mps(rel_file, fixed_mps_format); + return cuopt::linear_programming::io::read_mps(rel_file, fixed_mps_format); } TEST(optimization_problem_t, good_mps_file_1) diff --git a/cpp/tests/linear_programming/unit_tests/presolve_test.cu b/cpp/tests/linear_programming/unit_tests/presolve_test.cu index fd212c4b06..449f20edae 100644 --- a/cpp/tests/linear_programming/unit_tests/presolve_test.cu +++ b/cpp/tests/linear_programming/unit_tests/presolve_test.cu @@ -108,7 +108,7 @@ TEST(pslp_presolve, postsolve_accuracy_afiro) constexpr double expected_obj = -464.75314; // Known optimal objective for afiro auto path = make_path_absolute("linear_programming/afiro_original.mps"); - auto mps_data_model = cuopt::linear_programming::io::parse_mps(path, true); + auto mps_data_model = cuopt::linear_programming::io::read_mps(path, true); // Store original problem data for later verification const auto& orig_coefficients = mps_data_model.get_constraint_matrix_values(); @@ -168,7 +168,7 @@ TEST(pslp_presolve, postsolve_dual_accuracy_afiro) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); - auto mps_data_model = cuopt::linear_programming::io::parse_mps(path, true); + auto mps_data_model = cuopt::linear_programming::io::read_mps(path, true); const int orig_n_vars = mps_data_model.get_n_variables(); const int orig_n_constraints = mps_data_model.get_n_constraints(); @@ -204,7 +204,7 @@ TEST(pslp_presolve, postsolve_accuracy_larger_problem) constexpr double tolerance = 1e-4; auto path = make_path_absolute("linear_programming/ex10/ex10.mps"); - auto mps_data_model = cuopt::linear_programming::io::parse_mps(path, false); + auto mps_data_model = cuopt::linear_programming::io::read_mps(path, false); // Store original problem dimensions const auto& orig_coefficients = mps_data_model.get_constraint_matrix_values(); @@ -254,7 +254,7 @@ TEST(pslp_presolve, compare_with_no_presolve) constexpr double obj_tolerance = 1e-3; auto path = make_path_absolute("linear_programming/afiro_original.mps"); - auto mps_data_model = cuopt::linear_programming::io::parse_mps(path, true); + auto mps_data_model = cuopt::linear_programming::io::read_mps(path, true); // Solve without presolve auto settings_no_presolve = pdlp_solver_settings_t{}; @@ -324,7 +324,7 @@ TEST(pslp_presolve, postsolve_reduced_costs) const raft::handle_t handle_{}; auto path = make_path_absolute("linear_programming/afiro_original.mps"); - auto mps_data_model = cuopt::linear_programming::io::parse_mps(path, true); + auto mps_data_model = cuopt::linear_programming::io::read_mps(path, true); const int orig_n_vars = mps_data_model.get_n_variables(); @@ -359,7 +359,7 @@ TEST(pslp_presolve, postsolve_multiple_problems) for (const auto& [name, expected_obj] : instances) { auto path = make_path_absolute("linear_programming/" + name + ".mps"); auto mps_data_model = - cuopt::linear_programming::io::parse_mps(path, name == "afiro_original"); + cuopt::linear_programming::io::read_mps(path, name == "afiro_original"); const int orig_n_vars = mps_data_model.get_n_variables(); const int orig_n_constraints = mps_data_model.get_n_constraints(); diff --git a/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu b/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu index 7a29e5913a..f7f164900a 100644 --- a/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu +++ b/cpp/tests/linear_programming/unit_tests/solution_interface_test.cu @@ -368,7 +368,7 @@ TEST_F(SolutionInterfaceTest, cpu_problem_to_optimization_problem) // This test legitimately uses the MPS parser since it tests that pipeline TEST_F(SolutionInterfaceTest, mps_data_model_to_optimization_problem) { - auto mps_data = cuopt::linear_programming::io::parse_mps(lp_file_); + auto mps_data = cuopt::linear_programming::io::read_mps(lp_file_); raft::handle_t handle; auto problem = mps_data_model_to_optimization_problem(&handle, mps_data); diff --git a/cpp/tests/mip/bounds_standardization_test.cu b/cpp/tests/mip/bounds_standardization_test.cu index 0ea51af1a4..059b038f0c 100644 --- a/cpp/tests/mip/bounds_standardization_test.cu +++ b/cpp/tests/mip/bounds_standardization_test.cu @@ -46,7 +46,7 @@ void test_bounds_standardization_test(std::string test_instance) std::cout << "Running: " << test_instance << std::endl; auto path = make_path_absolute(test_instance); cuopt::linear_programming::io::mps_data_model_t problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, problem); problem_checking_t::check_problem_representation(op_problem); diff --git a/cpp/tests/mip/cuts_test.cu b/cpp/tests/mip/cuts_test.cu index 9bd2e5353c..ac411f5b89 100644 --- a/cpp/tests/mip/cuts_test.cu +++ b/cpp/tests/mip/cuts_test.cu @@ -209,8 +209,8 @@ io::mps_data_model_t& get_neos8_model_cached() static std::unique_ptr> model_ptr; std::call_once(init_flag, []() { const auto neos8_path = make_path_absolute("mip/neos8.mps"); - auto neos8_model = cuopt::linear_programming::io::parse_mps(neos8_path, false); - model_ptr = std::make_unique>(std::move(neos8_model)); + auto neos8_model = cuopt::linear_programming::io::read_mps(neos8_path, false); + model_ptr = std::make_unique>(std::move(neos8_model)); }); cuopt_assert(model_ptr != nullptr, "Failed to initialize cached neos8 model"); return *model_ptr; diff --git a/cpp/tests/mip/determinism_test.cu b/cpp/tests/mip/determinism_test.cu index 20ad338070..0accf2ba67 100644 --- a/cpp/tests/mip/determinism_test.cu +++ b/cpp/tests/mip/determinism_test.cu @@ -56,7 +56,7 @@ class DeterministicBBTest : public ::testing::Test { TEST_F(DeterministicBBTest, reproducible_objective) { auto path = make_path_absolute("/mip/gen-ip054.mps"); - auto problem = io::parse_mps(path, false); + auto problem = io::read_mps(path, false); handle_.sync_stream(); mip_solver_settings_t settings; @@ -88,7 +88,7 @@ TEST_F(DeterministicBBTest, reproducible_objective) TEST_F(DeterministicBBTest, reproducible_infeasibility) { auto path = make_path_absolute("/mip/stein9inf.mps"); - auto problem = io::parse_mps(path, false); + auto problem = io::read_mps(path, false); handle_.sync_stream(); mip_solver_settings_t settings; @@ -120,7 +120,7 @@ TEST_F(DeterministicBBTest, reproducible_infeasibility) TEST_F(DeterministicBBTest, reproducible_high_contention) { auto path = make_path_absolute("/mip/gen-ip054.mps"); - auto problem = io::parse_mps(path, false); + auto problem = io::read_mps(path, false); handle_.sync_stream(); mip_solver_settings_t settings; @@ -155,7 +155,7 @@ TEST_F(DeterministicBBTest, reproducible_high_contention) TEST_F(DeterministicBBTest, reproducible_solution_vector) { auto path = make_path_absolute("/mip/swath1.mps"); - auto problem = io::parse_mps(path, false); + auto problem = io::read_mps(path, false); handle_.sync_stream(); mip_solver_settings_t settings; @@ -188,7 +188,7 @@ TEST_P(DeterministicBBInstanceTest, deterministic_across_runs) { auto [instance_path, num_threads, time_limit, work_limit] = GetParam(); auto path = make_path_absolute(instance_path); - auto problem = io::parse_mps(path, false); + auto problem = io::read_mps(path, false); handle_.sync_stream(); // Get a random seed for each run diff --git a/cpp/tests/mip/doc_example_test.cu b/cpp/tests/mip/doc_example_test.cu index 74b8eaadbb..c198cca311 100644 --- a/cpp/tests/mip/doc_example_test.cu +++ b/cpp/tests/mip/doc_example_test.cu @@ -127,7 +127,7 @@ TEST(docs, user_problem_file) EXPECT_TRUE(std::filesystem::exists(user_problem_path)); cuopt::linear_programming::io::mps_data_model_t problem2 = - cuopt::linear_programming::io::parse_mps(user_problem_path, false); + cuopt::linear_programming::io::read_mps(user_problem_path, false); EXPECT_EQ(problem2.get_n_variables(), problem.get_n_variables()); EXPECT_EQ(problem2.get_n_constraints(), problem.get_n_constraints()); diff --git a/cpp/tests/mip/elim_var_remap_test.cu b/cpp/tests/mip/elim_var_remap_test.cu index dfab44c4f7..776ebbd310 100644 --- a/cpp/tests/mip/elim_var_remap_test.cu +++ b/cpp/tests/mip/elim_var_remap_test.cu @@ -61,7 +61,7 @@ void test_elim_var_remap(std::string test_instance) std::cout << "Running: " << test_instance << std::endl; auto path = make_path_absolute(test_instance); cuopt::linear_programming::io::mps_data_model_t mps_problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); problem_checking_t::check_problem_representation(op_problem); @@ -129,7 +129,7 @@ void test_elim_var_solution(std::string test_instance) std::cout << "Running: " << test_instance << std::endl; auto path = make_path_absolute(test_instance); cuopt::linear_programming::io::mps_data_model_t mps_problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); problem_checking_t::check_problem_representation(op_problem); diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index bf110c9232..7820f1f6a0 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -69,7 +69,7 @@ static fj_state_t run_fj(std::string test_instance, auto path = cuopt::test::get_rapids_dataset_root_dir() + ("/mip/" + test_instance); cuopt::linear_programming::io::mps_data_model_t mps_problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); problem_checking_t::check_problem_representation(op_problem); diff --git a/cpp/tests/mip/incumbent_callback_test.cu b/cpp/tests/mip/incumbent_callback_test.cu index 95a2b0a1b3..2dce940f73 100644 --- a/cpp/tests/mip/incumbent_callback_test.cu +++ b/cpp/tests/mip/incumbent_callback_test.cu @@ -113,7 +113,7 @@ void test_incumbent_callback(std::string test_instance, bool include_set_callbac std::cout << "Running: " << test_instance << std::endl; auto path = make_path_absolute(test_instance); cuopt::linear_programming::io::mps_data_model_t mps_problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); @@ -165,7 +165,7 @@ TEST(mip_solve, early_heuristic_incumbent_fallback) const raft::handle_t handle_{}; auto path = make_path_absolute("mip/pk1.mps"); cuopt::linear_programming::io::mps_data_model_t mps_problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); diff --git a/cpp/tests/mip/load_balancing_test.cu b/cpp/tests/mip/load_balancing_test.cu index affbbca7dc..afdc275e15 100644 --- a/cpp/tests/mip/load_balancing_test.cu +++ b/cpp/tests/mip/load_balancing_test.cu @@ -122,7 +122,7 @@ void test_multi_probe(std::string path) rmm::mr::set_current_device_resource(memory_resource); const raft::handle_t handle_{}; cuopt::linear_programming::io::mps_data_model_t mps_problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); problem_checking_t::check_problem_representation(op_problem); diff --git a/cpp/tests/mip/mip_utils.cuh b/cpp/tests/mip/mip_utils.cuh index d24d9a5be9..057047cefd 100644 --- a/cpp/tests/mip/mip_utils.cuh +++ b/cpp/tests/mip/mip_utils.cuh @@ -168,7 +168,7 @@ static std::tuple test_mps_file( auto path = make_path_absolute(test_instance); cuopt::linear_programming::io::mps_data_model_t problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); mip_solver_settings_t settings; settings.time_limit = time_limit; diff --git a/cpp/tests/mip/miplib_test.cu b/cpp/tests/mip/miplib_test.cu index 3a9a2391b0..0e255d35a9 100644 --- a/cpp/tests/mip/miplib_test.cu +++ b/cpp/tests/mip/miplib_test.cu @@ -39,7 +39,7 @@ void test_miplib_file(result_map_t test_instance, mip_solver_settings_t problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); // set the time limit depending on we are in assert mode or not #ifdef ASSERT_MODE @@ -81,7 +81,7 @@ TEST(mip_solve, low_thread_count_test) auto path = make_path_absolute("mip/dominating_set.mps"); cuopt::linear_programming::io::mps_data_model_t problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); mip_solution_t solution = solve_mip(&handle_, problem, settings); @@ -105,7 +105,7 @@ TEST(mip_solve, node_limit_test) auto path = make_path_absolute("mip/swath1.mps"); cuopt::linear_programming::io::mps_data_model_t problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); mip_solution_t solution = solve_mip(&handle_, problem, settings); diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 81897e9eac..b6ffb1e592 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -144,7 +144,7 @@ void test_multi_probe(std::string path) rmm::mr::set_current_device_resource(memory_resource); const raft::handle_t handle_{}; cuopt::linear_programming::io::mps_data_model_t mps_problem = - cuopt::linear_programming::io::parse_mps(path, false); + cuopt::linear_programming::io::read_mps(path, false); handle_.sync_stream(); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); problem_checking_t::check_problem_representation(op_problem); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index 4bd4265f34..a400d04854 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -34,7 +34,7 @@ TEST(problem, find_implied_integers) const raft::handle_t handle_{}; auto path = make_path_absolute("mip/fiball.mps"); - auto mps_data_model = cuopt::linear_programming::io::parse_mps(path, false); + auto mps_data_model = cuopt::linear_programming::io::read_mps(path, false); auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_data_model); auto presolver = std::make_unique>(); auto result = presolver->apply(op_problem, diff --git a/cpp/tests/qp/unit_tests/lp_parser_solve_test.cu b/cpp/tests/qp/unit_tests/lp_parser_solve_test.cu index 3dc96acbdb..31b65e3a9b 100644 --- a/cpp/tests/qp/unit_tests/lp_parser_solve_test.cu +++ b/cpp/tests/qp/unit_tests/lp_parser_solve_test.cu @@ -37,7 +37,7 @@ void expect_optimal_solution(const std::string& lp_text, const std::vector& expected_x) { raft::handle_t handle; - auto problem = io::parse_lp_from_string(lp_text); + auto problem = io::read_lp_from_string(lp_text); auto settings = pdlp_solver_settings_t(); auto solution = solve_lp(&handle, problem, settings); diff --git a/cpp/tests/utilities/inline_mps_test_utils.hpp b/cpp/tests/utilities/inline_mps_test_utils.hpp index 09a1bc158b..6f0370357b 100644 --- a/cpp/tests/utilities/inline_mps_test_utils.hpp +++ b/cpp/tests/utilities/inline_mps_test_utils.hpp @@ -104,7 +104,7 @@ ENDATA inline cuopt::linear_programming::io::mps_data_model_t parse_inline_mps( std::string_view mps_text) { - return cuopt::linear_programming::io::parse_mps_from_string(mps_text, false); + return cuopt::linear_programming::io::read_mps_from_string(mps_text, false); } } // namespace cuopt::test::inline_mps diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-example.rst b/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-example.rst index dc163009ed..d4b981719c 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-example.rst +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-example.rst @@ -153,7 +153,7 @@ Example With LP File used — it dispatches on the file extension (case-insensitive): ``.lp`` / ``.lp.gz`` / ``.lp.bz2`` → LP parser; ``.mps`` / ``.qps`` and their ``.gz`` / ``.bz2`` variants → MPS parser; unknown extensions are -rejected. See the ``parse_lp`` declaration in +rejected. See the ``read_lp`` declaration in ``cuopt/linear_programming/io/parser.hpp`` for the supported subset of the LP format. diff --git a/docs/cuopt/source/cuopt-cli/cli-examples.rst b/docs/cuopt/source/cuopt-cli/cli-examples.rst index 68ae0adba1..1a4f813b2e 100644 --- a/docs/cuopt/source/cuopt-cli/cli-examples.rst +++ b/docs/cuopt/source/cuopt-cli/cli-examples.rst @@ -13,9 +13,10 @@ format is dispatched automatically from the file extension variants) → parsed as MPS / QPS Any other extension (including no extension) is rejected with an error -listing the supported suffixes. See the ``parse_lp`` / ``parse_mps`` -declarations in ``cuopt/linear_programming/io/parser.hpp`` for the -supported subset of each format. +listing the supported suffixes. See ``read`` in +``cuopt/linear_programming/io/parser.hpp`` (and the Python +:func:`~cuopt.linear_programming.io.Read` wrapper). + Basic Usage ########### diff --git a/docs/cuopt/source/cuopt-python/lp-qp-milp/lp-qp-milp-api.rst b/docs/cuopt/source/cuopt-python/lp-qp-milp/lp-qp-milp-api.rst index e86c4a2920..609e11b34b 100644 --- a/docs/cuopt/source/cuopt-python/lp-qp-milp/lp-qp-milp-api.rst +++ b/docs/cuopt/source/cuopt-python/lp-qp-milp/lp-qp-milp-api.rst @@ -47,3 +47,5 @@ LP, QP and MILP API Reference :member-order: bysource :undoc-members: :exclude-members: __new__, __init__, _generate_next_value_, as_integer_ratio, bit_count, bit_length, conjugate, denominator, from_bytes, imag, is_integer, numerator, real, to_bytes + +.. autofunction:: cuopt.linear_programming.io.Read diff --git a/docs/cuopt/source/cuopt-server/examples/lp-examples.rst b/docs/cuopt/source/cuopt-server/examples/lp-examples.rst index 842daf9726..964d99680c 100644 --- a/docs/cuopt/source/cuopt-server/examples/lp-examples.rst +++ b/docs/cuopt/source/cuopt-server/examples/lp-examples.rst @@ -267,10 +267,17 @@ The response is: } -Generate Datamodel from MPS Parser ----------------------------------- +Generate Datamodel using Problem File Parser +-------------------------------------------- -Use a datamodel generated from mps file as input; this yields a solution object in response. For more details please refer to :doc:`LP/QP/MILP parameters <../../lp-qp-milp-settings>`. +Use a :class:`~cuopt.linear_programming.data_model.DataModel` built with +:func:`~cuopt.linear_programming.io.Read` as input to ``get_LP_solve``; +the client dispatches on the file extension (``.mps`` / ``.qps`` vs ``.lp``, +including ``.gz`` / ``.bz2`` compressed variants). For solver settings see +:doc:`LP/QP/MILP parameters <../../lp-qp-milp-settings>`. + +MPS format +~~~~~~~~~~ :download:`mps_datamodel_example.py ` @@ -278,8 +285,17 @@ Use a datamodel generated from mps file as input; this yields a solution object :language: python :linenos: +LP format +~~~~~~~~~ -The response would be as follows: +:download:`lp_datamodel_example.py ` + +.. literalinclude:: lp/examples/lp_datamodel_example.py + :language: python + :linenos: + +Expected output (either example, same problem instance) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: text :linenos: @@ -288,7 +304,7 @@ The response would be as follows: 1 Objective Value: -0.36000000000000004 - Mps Parse time: 0.000 sec + MPS Parse time: 0.000 sec Network time: 1.062 sec Engine Solve time: 0.004 sec Total end to end time: 1.066 sec diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/lp_datamodel_example.py b/docs/cuopt/source/cuopt-server/examples/lp/examples/lp_datamodel_example.py new file mode 100644 index 0000000000..cf6e8913ce --- /dev/null +++ b/docs/cuopt/source/cuopt-server/examples/lp/examples/lp_datamodel_example.py @@ -0,0 +1,97 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +LP DataModel from LP file parser example + +This example demonstrates how to: +- Read an LP-format file using cuopt.linear_programming.Read +- Create a DataModel from the parsed LP file +- Solve using the DataModel via the server +- Extract detailed solution information + +Requirements: + - cuOpt server running (default: localhost:5000) + - cuopt_sh_client package installed + - cuopt package installed + +Problem (in LP format; same instance as the MPS datamodel example): + Minimize: -0.2*VAR1 + 0.1*VAR2 + Subject to: + 3*VAR1 + 4*VAR2 <= 5.4 + 2.7*VAR1 + 10.1*VAR2 <= 4.9 + VAR1, VAR2 >= 0 + +Expected Output: + Termination Reason: 1 (Optimal) + Objective Value: -0.36 + Variables Values: {'VAR1': 1.8, 'VAR2': 0.0} +""" + +from cuopt_sh_client import ( + CuOptServiceSelfHostClient, + ThinClientSolverSettings, + PDLPSolverMode, +) +from cuopt.linear_programming import Read +import time + + +def main(): + """Run the LP file DataModel example.""" + data = "sample.lp" + + lp_data = r"""\ Same problem as mps_datamodel_example.py (good-1) +Minimize + -0.2 VAR1 + 0.1 VAR2 +Subject To + ROW1: 3 VAR1 + 4 VAR2 <= 5.4 + ROW2: 2.7 VAR1 + 10.1 VAR2 <= 4.9 +End +""" + + with open(data, "w") as file: + file.write(lp_data) + + print(f"Created LP file: {data}") + + print("\n=== Parsing LP File ===") + parse_start = time.time() + data_model = Read(data) + parse_time = time.time() - parse_start + print(f"Parse time: {parse_time:.3f} seconds") + + cuopt_service_client = CuOptServiceSelfHostClient( + ip="localhost", port=5000, timeout_exception=False + ) + + ss = ThinClientSolverSettings() + ss.set_parameter("pdlp_solver_mode", PDLPSolverMode.Fast1) + ss.set_optimality_tolerance(1e-4) + ss.set_parameter("time_limit", 5) + + print("\n=== Solving with Server ===") + network_time = time.time() + solution = cuopt_service_client.get_LP_solve(data_model, ss) + network_time = time.time() - network_time + + solution_status = solution["response"]["solver_response"]["status"] + solution_obj = solution["response"]["solver_response"]["solution"] + + print("\n=== Results ===") + print(f"Termination Reason: {solution_status}") + print(f"Objective Value: {solution_obj.get_primal_objective()}") + print(f"LP Parse time: {parse_time:.3f} sec") + + network_time = network_time - (solution_obj.get_solve_time()) + print(f"Network time: {network_time:.3f} sec") + + solve_time = solution_obj.get_solve_time() + print(f"Engine Solve time: {solve_time:.3f} sec") + + end_to_end_time = parse_time + network_time + solve_time + print(f"Total end to end time: {end_to_end_time:.3f} sec") + print(f"Variables Values: {solution_obj.get_vars()}") + + +if __name__ == "__main__": + main() diff --git a/docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py b/docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py index be372f4bd5..db940dd954 100644 --- a/docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py +++ b/docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py @@ -1,10 +1,10 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ -LP DataModel from MPS Parser Example +LP DataModel from MPS file parser example This example demonstrates how to: -- Parse an MPS file using cuopt.linear_programming.ParseMps +- Read an MPS file using cuopt.linear_programming.Read - Create a DataModel from the parsed MPS - Solve using the DataModel via the server - Extract detailed solution information @@ -32,7 +32,7 @@ ThinClientSolverSettings, PDLPSolverMode, ) -from cuopt.linear_programming import ParseMps +from cuopt.linear_programming import Read import time @@ -65,7 +65,7 @@ def main(): # Parse the MPS file and measure the time spent print("\n=== Parsing MPS File ===") parse_start = time.time() - data_model = ParseMps(data) + data_model = Read(data) parse_time = time.time() - parse_start print(f"Parse time: {parse_time:.3f} seconds") @@ -110,7 +110,6 @@ def main(): # Check found objective value print(f"Objective Value: {solution_obj.get_primal_objective()}") - # Check the MPS parse time print(f"MPS Parse time: {parse_time:.3f} sec") # Check network time (client call - solve time) diff --git a/docs/cuopt/source/hidden/mps-api.rst b/docs/cuopt/source/hidden/mps-api.rst index 637d8a03cc..ec2be8df66 100644 --- a/docs/cuopt/source/hidden/mps-api.rst +++ b/docs/cuopt/source/hidden/mps-api.rst @@ -2,12 +2,7 @@ cuOpt MPS/LP Parser API Reference =============================== -MPS Parser ----------- +MPS/QPS/LP parser +------------------- -.. autofunction:: cuopt.linear_programming.io.ParseMps - -LP Parser ---------- - -.. autofunction:: cuopt.linear_programming.io.ParseLp +.. autofunction:: cuopt.linear_programming.io.Read diff --git a/docs/cuopt/source/hidden/mps-example.rst b/docs/cuopt/source/hidden/mps-example.rst deleted file mode 100644 index cc6495d2b4..0000000000 --- a/docs/cuopt/source/hidden/mps-example.rst +++ /dev/null @@ -1,13 +0,0 @@ -~~~~~~~~~~~~~~~~~~~~~~~~ -cuOpt MPS Parser Example -~~~~~~~~~~~~~~~~~~~~~~~~ - - -Example -------- - -.. code-block:: python - :linenos: - - from cuopt.linear_programming import ParseMps - x = ParseMps('good-mps-1.mps') diff --git a/docs/cuopt/source/hidden/parser_example.rst b/docs/cuopt/source/hidden/parser_example.rst new file mode 100644 index 0000000000..9f79545e80 --- /dev/null +++ b/docs/cuopt/source/hidden/parser_example.rst @@ -0,0 +1,26 @@ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +cuOpt problem file parser example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +Example +------- + +Read MPS, QPS, or LP files (including ``.gz`` / ``.bz2`` compressed variants) +with :func:`~cuopt.linear_programming.Read`: + +.. code-block:: python + :linenos: + + from cuopt.linear_programming import Read + from cuopt.linear_programming.problem import Problem + + # MPS / QPS + mps_model = Read("good-mps-1.mps") + + # LP (plain or compressed) + lp_model = Read("good-mps-1.lp") + lp_gz = Read("good-mps-1.lp.gz") + + # High-level API + problem = Problem.read("good-mps-1.lp") diff --git a/python/cuopt/cuopt/linear_programming/__init__.py b/python/cuopt/cuopt/linear_programming/__init__.py index 6950d72bc8..835d09d76a 100644 --- a/python/cuopt/cuopt/linear_programming/__init__.py +++ b/python/cuopt/cuopt/linear_programming/__init__.py @@ -3,7 +3,7 @@ from cuopt.linear_programming import internals from cuopt.linear_programming.data_model import DataModel -from cuopt.linear_programming.io import ParseLp, ParseMps +from cuopt.linear_programming.io import ParseMps, Read from cuopt.linear_programming.problem import Problem from cuopt.linear_programming.solution import Solution from cuopt.linear_programming.solver import BatchSolve, Solve diff --git a/python/cuopt/cuopt/linear_programming/io/__init__.py b/python/cuopt/cuopt/linear_programming/io/__init__.py index f81e9369ec..c6843a9e61 100644 --- a/python/cuopt/cuopt/linear_programming/io/__init__.py +++ b/python/cuopt/cuopt/linear_programming/io/__init__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -from cuopt.linear_programming.io.parser import ParseLp, ParseMps, toDict +from cuopt.linear_programming.io.parser import ParseMps, Read, toDict diff --git a/python/cuopt/cuopt/linear_programming/io/parser.pxd b/python/cuopt/cuopt/linear_programming/io/parser.pxd index 402873f9ab..d1cc95e9d6 100644 --- a/python/cuopt/cuopt/linear_programming/io/parser.pxd +++ b/python/cuopt/cuopt/linear_programming/io/parser.pxd @@ -39,11 +39,12 @@ cdef extern from "cuopt/linear_programming/io/mps_data_model.hpp" namespace "cuo cdef extern from "cuopt/linear_programming/io/utilities/cython_parser.hpp" namespace "cuopt::cython": # noqa - cdef unique_ptr[mps_data_model_t[int, double]] call_parse_mps( - const string& mps_file_path, + cdef unique_ptr[mps_data_model_t[int, double]] call_read( + const string& file_path, bool fixed_mps_format ) except + - cdef unique_ptr[mps_data_model_t[int, double]] call_parse_lp( - const string& lp_file_path + cdef unique_ptr[mps_data_model_t[int, double]] call_parse_mps( + const string& mps_file_path, + bool fixed_mps_format ) except + diff --git a/python/cuopt/cuopt/linear_programming/io/parser.py b/python/cuopt/cuopt/linear_programming/io/parser.py index 385edfefe1..a9132eaf8f 100644 --- a/python/cuopt/cuopt/linear_programming/io/parser.py +++ b/python/cuopt/cuopt/linear_programming/io/parser.py @@ -10,114 +10,74 @@ @catch_io_exception -def ParseMps(mps_file_path, fixed_mps_format=False): - """ - Reads the equation from the input text file which is MPS formatted +def Read(file_path: str, fixed_mps_format: bool = False) -> DataModel: + """Read an optimization problem from a file, dispatching on extension. - See Also - -------- - ParseLp : parses LP format files (for users with .lp inputs). + Dispatches to the MPS/QPS or LP reader based on the filename suffix + (case-insensitive), matching the C++ ``read`` entry point: - Notes - ----- - Read this link http://lpsolve.sourceforge.net/5.5/mps-format.htm for more - details on both free and fixed MPS format. + - ``.mps``, ``.mps.gz``, ``.mps.bz2``, ``.qps``, ``.qps.gz``, ``.qps.bz2`` + → MPS/QPS reader + - ``.lp``, ``.lp.gz``, ``.lp.bz2`` → LP reader Parameters ---------- - mps_file_path : str - Path to MPS formatted file + file_path : str + Path to an MPS, QPS, or LP file (optionally ``.gz`` / ``.bz2`` + compressed). fixed_mps_format : bool - If MPS file should be parsed as fixed, false by default + If the MPS/QPS reader should parse as fixed MPS format. Ignored for + LP inputs. False by default. Returns ------- - data_model: DataModel - A fully formed LP problem which represents the given MPS file - - Examples - -------- - >>> from cuopt import linear_programming - >>> - >>> data_model = linear_programming.ParseMps(mps_file_path) - >>> - >>> # Build a solver setting object & lower the accuracy from 1e-4 to 1e-2 - >>> solver_settings = linear_programming.SolverSettings() - >>> solver_settings.set_optimality_tolerance(1e-2) - >>> - >>> # Call solve - >>> solution = linear_programming.Solve(data_model, solver_settings) - >>> - >>> # Print solution - >>> print(solution.get_primal_solution()) - """ + data_model : DataModel + A fully formed LP/MILP/QP problem. - return parser_wrapper.ParseMps(mps_file_path, fixed_mps_format) + Raises + ------ + InputValidationError, InputRuntimeError, OutOfMemoryError + Parser errors from the underlying C++ readers (via + ``catch_io_exception``). + RuntimeError + If the file extension is not one of the supported suffixes (raised by + the C++ ``read`` dispatch). + """ + return parser_wrapper.Read(file_path, fixed_mps_format) @catch_io_exception -def ParseLp(lp_file_path: str) -> DataModel: - """Read an optimization problem from a file in LP format. - - The LP format is a human-readable alternative to MPS and supports LP, - MIP, and QP, plus semi-continuous variables (declared via a - Semi-Continuous section; finite upper bound required) and - quadratic constraints (QCQP; ``<=`` only). - - Quadratic terms live in ``[ ... ]`` blocks. The objective bracket must - be followed by ``/ 2`` (the file states coefficients in the - ``0.5 x^T Q x`` convention); a constraint bracket must NOT be followed - by ``/ 2`` (coefficients are at face value, ``x^T Q x``). Only squared - (``x^2``) and product (``x * y``) terms are allowed inside the - bracket; bare linear terms must be written outside it. - - This function parses the dialect in which the objective and constraints - are written as algebraic expressions over named variables (it does not - implement the alternative tableau-style LP dialect used by some - open-source readers). +def ParseMps(mps_file_path: str, fixed_mps_format: bool = False) -> DataModel: + """Read an MPS or QPS file directly via the MPS/QPS reader. + + Unlike :func:`Read`, this function bypasses extension-based dispatch + and always invokes the MPS/QPS reader (``read_mps`` on the C++ side), + regardless of the filename suffix. Compressed inputs (``.mps.gz``, + ``.mps.bz2``, ``.qps.gz``, ``.qps.bz2``) are still supported when + zlib / libbz2 are available, because compression is detected from + the file path inside the reader. Parameters ---------- - lp_file_path : str - Path to LP-formatted file. May end in ``.lp``, ``.lp.gz``, or - ``.lp.bz2``; compressed inputs are decompressed at read time - via zlib / libbz2 when those libraries are available. + mps_file_path : str + Path to an MPS or QPS file (optionally ``.gz`` / ``.bz2`` + compressed). + fixed_mps_format : bool + If the MPS/QPS reader should parse the file as fixed MPS format. + False by default. Returns ------- data_model : DataModel - A fully formed LP/MIP/QP problem representing the contents of - ``lp_file_path``. + A fully formed LP/MILP/QP problem. Raises ------ - InputValidationError - Raised when ``lp_file_path`` is malformed or uses unsupported - syntax. Examples include unsupported sections (SOS, PWL - objective, user cuts, general constraints), bare linear terms - inside a quadratic ``[ ... ]`` bracket, an objective bracket - not followed by ``/ 2``, a constraint bracket followed by - ``/ 2``, a semi-continuous variable without a finite upper - bound, and similar input-level errors raised by the underlying - C++ parser. Exceptions propagated from - :func:`parser_wrapper.ParseLp` are translated to this type by - :func:`catch_io_exception`. - InputRuntimeError - Raised for non-validation runtime errors that the C++ parser - flags during file I/O or parsing. - OutOfMemoryError - Raised when the parser cannot allocate memory for the - resulting data model. - - Examples - -------- - >>> from cuopt import linear_programming - >>> - >>> data_model = linear_programming.ParseLp(lp_file_path) - >>> solver_settings = linear_programming.SolverSettings() - >>> solution = linear_programming.Solve(data_model, solver_settings) + InputValidationError, InputRuntimeError, OutOfMemoryError + Parser errors from the underlying C++ reader (via + ``catch_io_exception``). """ - return parser_wrapper.ParseLp(lp_file_path) + return parser_wrapper.ParseMps(mps_file_path, fixed_mps_format) def toDict(model, json=False): @@ -126,7 +86,6 @@ def toDict(model, json=False): "model must be a cuopt.linear_programming.io.parser_wrapper.DataModel" ) - # Replace numpy objects in generated data so that it is JSON serializable def transform(data): for key, value in data.items(): if isinstance(value, dict): diff --git a/python/cuopt/cuopt/linear_programming/io/parser_wrapper.pyx b/python/cuopt/cuopt/linear_programming/io/parser_wrapper.pyx index 61e10b1864..b2acff89fc 100644 --- a/python/cuopt/cuopt/linear_programming/io/parser_wrapper.pyx +++ b/python/cuopt/cuopt/linear_programming/io/parser_wrapper.pyx @@ -16,8 +16,7 @@ from libcpp.memory cimport unique_ptr from libcpp.string cimport string from libcpp.utility cimport move -from .parser cimport call_parse_lp, call_parse_mps, mps_data_model_t - +from .parser cimport call_read, call_parse_mps, mps_data_model_t import warnings import numpy as np @@ -32,8 +31,7 @@ def type_cast(np_obj, np_type, name): # Copies the C++ data model behind `dm` into the Python-side `data_model`. -# Shared by ParseMps and ParseLp — every field on mps_data_model_t is -# format-agnostic. +# Copies every field on mps_data_model_t into the Python DataModel. cdef _marshal_data_model(mps_data_model_t[int, double]* dm, data_model): A_values_data = dm.A_.data() A_values_size = dm.A_.size() @@ -139,21 +137,24 @@ cdef _marshal_data_model(mps_data_model_t[int, double]* dm, data_model): @catch_io_exception -def ParseMps(mps_file_path, fixed_mps_formats): +def Read(file_path, fixed_mps_format=False): data_model = DataModel() dm_ret_ptr = move( - call_parse_mps( - mps_file_path.encode('utf-8'), - fixed_mps_formats + call_read( + file_path.encode('utf-8'), + fixed_mps_format, ) ) return _marshal_data_model(dm_ret_ptr.get(), data_model) @catch_io_exception -def ParseLp(lp_file_path): +def ParseMps(mps_file_path, fixed_mps_format=False): data_model = DataModel() dm_ret_ptr = move( - call_parse_lp(lp_file_path.encode('utf-8')) + call_parse_mps( + mps_file_path.encode('utf-8'), + fixed_mps_format, + ) ) return _marshal_data_model(dm_ret_ptr.get(), data_model) diff --git a/python/cuopt/cuopt/linear_programming/problem.py b/python/cuopt/cuopt/linear_programming/problem.py index 25d9d634da..15b5403130 100644 --- a/python/cuopt/cuopt/linear_programming/problem.py +++ b/python/cuopt/cuopt/linear_programming/problem.py @@ -3,13 +3,14 @@ import copy +import os from enum import Enum import numpy as np from scipy.sparse import coo_matrix import cuopt.linear_programming.data_model as data_model -import cuopt.linear_programming.io as mps_parser +from cuopt.linear_programming import ParseMps, Read import cuopt.linear_programming.solver as solver import cuopt.linear_programming.solver_settings as solver_settings import warnings @@ -1792,16 +1793,76 @@ def getConstraint(self, identifier): if c.index == identifier or c.ConstraintName == identifier: return c + @classmethod + def read(cls, file_path, fixed_mps_format=False): + """ + Initialize a problem from an MPS, QPS, or LP file. + + Dispatches on the file extension via the C++ ``read`` entry + point (case-insensitive): ``.mps`` / ``.qps`` (and ``.gz`` / ``.bz2`` + variants) use the MPS/QPS reader; ``.lp`` (and compressed variants) + use the LP reader. + + Parameters + ---------- + file_path : str + Path to an MPS, QPS, or LP file. + fixed_mps_format : bool + If the MPS/QPS reader should parse as fixed MPS format. Ignored + for LP inputs. False by default. + + Returns + ------- + Problem + A problem populated from the file. + + Examples + -------- + >>> problem = problem.Problem.read("model.mps") + >>> lp_problem = problem.Problem.read("model.lp") + """ + if not isinstance(file_path, str) or not file_path: + raise ValueError("file_path must be a non-empty string") + if not os.path.isfile(file_path): + raise FileNotFoundError(f"No such file: {file_path}") + + problem = cls() + data_model = Read(file_path, fixed_mps_format) + problem._from_data_model(data_model) + problem.model = data_model + return problem + @classmethod def readMPS(cls, mps_file): """ - Initiliaze a problem from an `MPS `__ file. # noqa + Initialize a problem from an `MPS `__ file. # noqa + + Always invokes the MPS/QPS reader directly (via the + ``call_parse_mps`` Cython bridge), bypassing extension-based + dispatch. Compressed ``.mps.gz`` / ``.mps.bz2`` / ``.qps.gz`` / + ``.qps.bz2`` inputs are still supported via the reader's path- + based decompression. + + .. deprecated:: + Use :meth:`read` instead. + Examples -------- >>> problem = problem.Problem.readMPS("model.mps") """ + warnings.warn( + "Problem.readMPS is deprecated and will be removed in a future " + "release. Use Problem.read instead.", + DeprecationWarning, + stacklevel=2, + ) + if not isinstance(mps_file, str) or not mps_file: + raise ValueError("mps_file must be a non-empty string") + if not os.path.isfile(mps_file): + raise FileNotFoundError(f"No such file: {mps_file}") + problem = cls() - data_model = mps_parser.ParseMps(mps_file) + data_model = ParseMps(mps_file) problem._from_data_model(data_model) problem.model = data_model return problem diff --git a/python/cuopt/cuopt/linear_programming/solver/solver.py b/python/cuopt/cuopt/linear_programming/solver/solver.py index af54aeaa02..4963d0be8a 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver.py +++ b/python/cuopt/cuopt/linear_programming/solver/solver.py @@ -17,7 +17,7 @@ def Solve(data_model, solver_settings=None): Data Model object can be construed through setters (see linear_programming.DataModel class) or through a MPS file - (see cuopt.linear_programming.ParseMps function) + (see cuopt.linear_programming.Read function) Notes @@ -124,7 +124,7 @@ def BatchSolve(data_model_list, solver_settings=None): Data Model objects can be construed through setters (see linear_programming.DataModel class) or through a MPS file - (see cuopt.linear_programming.ParseMps function) + (see cuopt.linear_programming.Read function) Notes @@ -160,11 +160,11 @@ def BatchSolve(data_model_list, solver_settings=None): >>> from cuopt import linear_programming >>> from cuopt.linear_programming.solver_settings import PDLPSolverMode >>> from cuopt.linear_programming.solver.solver_parameters import * - >>> from cuopt.linear_programming import ParseMps + >>> from cuopt.linear_programming import Read >>> >>> data_models = [] >>> for i in range(...): - >>> data_models.append(ParseMps(...)) + >>> data_models.append(Read(...)) >>> >>> # Build a solver setting object >>> settings = linear_programming.SolverSettings() diff --git a/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py b/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py index 1c3a83b162..4453d38bcc 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py @@ -23,7 +23,7 @@ import sys import time -from cuopt.linear_programming import io as mps_parser +from cuopt.linear_programming import Read import pytest from cuopt import linear_programming from cuopt.linear_programming.solver.solver_parameters import CUOPT_TIME_LIMIT @@ -301,11 +301,10 @@ def _run_in_subprocess(func, env=None, timeout=120): def _impl_lp_solve_cpu_only(): """LP solve returns correctly-sized solution vectors.""" from cuopt import linear_programming - from cuopt.linear_programming import io as mps_parser dataset_root = os.environ.get("RAPIDS_DATASET_ROOT_DIR", "./") mps_file = f"{dataset_root}/linear_programming/afiro_original.mps" - dm = mps_parser.ParseMps(mps_file) + dm = Read(mps_file) n_vars = len(dm.get_objective_coefficients()) solution = linear_programming.Solve( @@ -331,11 +330,10 @@ def _impl_lp_solve_cpu_only(): def _impl_lp_dual_solution_cpu_only(): """Dual solution and reduced costs are correctly sized.""" from cuopt import linear_programming - from cuopt.linear_programming import io as mps_parser dataset_root = os.environ.get("RAPIDS_DATASET_ROOT_DIR", "./") mps_file = f"{dataset_root}/linear_programming/afiro_original.mps" - dm = mps_parser.ParseMps(mps_file) + dm = Read(mps_file) n_vars = len(dm.get_objective_coefficients()) n_cons = len(dm.get_constraint_bounds()) @@ -364,11 +362,10 @@ def _impl_mip_solve_cpu_only(): from cuopt.linear_programming.solver.solver_parameters import ( CUOPT_TIME_LIMIT, ) - from cuopt.linear_programming import io as mps_parser dataset_root = os.environ.get("RAPIDS_DATASET_ROOT_DIR", "./") mps_file = f"{dataset_root}/mip/bb_optimality.mps" - dm = mps_parser.ParseMps(mps_file) + dm = Read(mps_file) n_vars = len(dm.get_objective_coefficients()) settings = linear_programming.SolverSettings() @@ -400,11 +397,10 @@ def _impl_warmstart_cpu_only(): CUOPT_PRESOLVE, ) from cuopt.linear_programming.solver_settings import SolverMethod - from cuopt.linear_programming import io as mps_parser dataset_root = os.environ.get("RAPIDS_DATASET_ROOT_DIR", "./") mps_file = f"{dataset_root}/linear_programming/afiro_original.mps" - dm = mps_parser.ParseMps(mps_file) + dm = Read(mps_file) settings = linear_programming.SolverSettings() settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) @@ -658,7 +654,7 @@ def test_lp_solution_values(self): mps_file = ( f"{RAPIDS_DATASET_ROOT_DIR}/linear_programming/afiro_original.mps" ) - dm = mps_parser.ParseMps(mps_file) + dm = Read(mps_file) n_vars = len(dm.get_objective_coefficients()) n_cons = len(dm.get_constraint_bounds()) @@ -687,7 +683,7 @@ def test_lp_solution_values(self): def test_mip_solution_values(self): """MIP solve of bb_optimality.mps returns valid stats.""" mps_file = f"{RAPIDS_DATASET_ROOT_DIR}/mip/bb_optimality.mps" - dm = mps_parser.ParseMps(mps_file) + dm = Read(mps_file) n_vars = len(dm.get_objective_coefficients()) settings = linear_programming.SolverSettings() diff --git a/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py b/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py index 87e5496d17..03871b22af 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py @@ -3,7 +3,7 @@ import os -from cuopt.linear_programming import io as mps_parser +from cuopt.linear_programming import Read import pytest from cuopt.linear_programming import solver, solver_settings @@ -77,7 +77,7 @@ def set_solution( ) file_path = RAPIDS_DATASET_ROOT_DIR + file_name - data_model_obj = mps_parser.ParseMps(file_path) + data_model_obj = Read(file_path) settings = solver_settings.SolverSettings() settings.set_parameter(CUOPT_TIME_LIMIT, 10) diff --git a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py index 0b13b7705f..6fc4636b34 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -5,7 +5,7 @@ import os from enum import IntEnum -from cuopt.linear_programming import io as mps_parser +from cuopt.linear_programming import Read import numpy as np import pytest @@ -93,7 +93,7 @@ def test_solver(): def test_parser_and_solver(): file_path = RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-1.mps" - data_model_obj = mps_parser.ParseMps(file_path) + data_model_obj = Read(file_path) settings = solver_settings.SolverSettings() settings.set_optimality_tolerance(1e-2) @@ -365,7 +365,7 @@ def test_solver_settings_basic(): file_path = ( RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-1.mps" ) - solver.Solve(mps_parser.ParseMps(file_path), settings) + solver.Solve(Read(file_path), settings) settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Methodical1) assert settings.get_parameter(CUOPT_PDLP_SOLVER_MODE) == int( @@ -484,7 +484,7 @@ def test_parse_var_names(): file_path = ( RAPIDS_DATASET_ROOT_DIR + "/linear_programming/afiro_original.mps" ) - data_model_obj = mps_parser.ParseMps(file_path) + data_model_obj = Read(file_path) expected_names = [ "X01", @@ -583,7 +583,7 @@ def test_parser_and_batch_solver(): nb_solves = 5 for i in range(nb_solves): - data_model_list.append(mps_parser.ParseMps(file_path)) + data_model_list.append(Read(file_path)) settings = solver_settings.SolverSettings() settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) @@ -595,9 +595,7 @@ def test_parser_and_batch_solver(): # Call Solve on each individual data model object individual_solutions = [] for i in range(nb_solves): - individual_solution = solver.Solve( - mps_parser.ParseMps(file_path), settings - ) + individual_solution = solver.Solve(Read(file_path), settings) individual_solutions.append(individual_solution) # Verify that the results are the same @@ -610,7 +608,7 @@ def test_parser_and_batch_solver(): def test_warm_start(): file_path = RAPIDS_DATASET_ROOT_DIR + "/linear_programming/a2864/a2864.mps" - data_model_obj = mps_parser.ParseMps(file_path) + data_model_obj = Read(file_path) settings = solver_settings.SolverSettings() settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) @@ -644,7 +642,8 @@ def test_warm_start(): file_path = ( RAPIDS_DATASET_ROOT_DIR + "/linear_programming/afiro_original.mps" ) - data_model_obj_different = mps_parser.ParseMps(file_path) + + data_model_obj_different = Read(file_path) with pytest.raises(Exception, match="Invalid PDLPWarmStart data"): solver.Solve(data_model_obj_different, settings) @@ -691,7 +690,7 @@ def test_solved_by(): def test_heuristics_only(): file_path = RAPIDS_DATASET_ROOT_DIR + "/mip/swath1.mps" - data_model_obj = mps_parser.ParseMps(file_path) + data_model_obj = Read(file_path) settings = solver_settings.SolverSettings() settings.set_parameter(CUOPT_MIP_HEURISTICS_ONLY, True) @@ -758,7 +757,7 @@ def test_write_files(): file_path = ( RAPIDS_DATASET_ROOT_DIR + "/linear_programming/afiro_original.mps" ) - data_model_obj = mps_parser.ParseMps(file_path) + data_model_obj = Read(file_path) settings = solver_settings.SolverSettings() settings.set_parameter(CUOPT_METHOD, SolverMethod.DualSimplex) @@ -769,7 +768,7 @@ def test_write_files(): assert os.path.isfile("afiro_out.mps") - afiro = mps_parser.ParseMps("afiro_out.mps") + afiro = Read("afiro_out.mps") os.remove("afiro_out.mps") settings.set_parameter(CUOPT_USER_PROBLEM_FILE, "") diff --git a/python/cuopt/cuopt/tests/linear_programming/test_parser.py b/python/cuopt/cuopt/tests/linear_programming/test_parser.py index 795f1fe298..f40fd505ef 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_parser.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_parser.py @@ -4,7 +4,7 @@ import os import tempfile -from cuopt.linear_programming import io as mps_parser +from cuopt.linear_programming import Read import numpy as np import pytest from cuopt.linear_programming.io.utilities import InputValidationError @@ -14,6 +14,47 @@ RAPIDS_DATASET_ROOT_DIR = os.getcwd() RAPIDS_DATASET_ROOT_DIR = os.path.join(RAPIDS_DATASET_ROOT_DIR, "datasets") +GOOD_MPS_1_DIR = os.path.join(RAPIDS_DATASET_ROOT_DIR, "linear_programming") + +# Plain and compressed encodings of the same tiny LP (see good-mps-1-README.md). +GOOD_MPS_1_VARIANTS = ( + "good-mps-1.mps", + "good-mps-1.lp", + "good-mps-1.mps.gz", + "good-mps-1.mps.bz2", + "good-mps-1.lp.gz", + "good-mps-1.lp.bz2", +) + + +def _assert_good_mps_1_model(data_model): + """Same checks as C++ good_mps_1_test::check_model.""" + assert not data_model.get_sense() + assert data_model.get_variable_names().tolist() == ["VAR1", "VAR2"] + assert data_model.get_row_names().tolist() == ["ROW1", "ROW2"] + assert data_model.get_objective_coefficients().tolist() == pytest.approx( + [0.2, 0.1] + ) + assert data_model.get_variable_lower_bounds().tolist() == pytest.approx( + [0.0, 0.0] + ) + assert np.isinf(data_model.get_variable_upper_bounds()[0]) + assert np.isinf(data_model.get_variable_upper_bounds()[1]) + assert data_model.get_constraint_upper_bounds().tolist() == pytest.approx( + [5.4, 4.9] + ) + assert data_model.get_constraint_matrix_values().tolist() == pytest.approx( + [3.0, 4.0, 2.7, 10.1] + ) + + +@pytest.mark.parametrize("filename", GOOD_MPS_1_VARIANTS) +def test_read_good_mps_1_variants(filename): + path = os.path.join(GOOD_MPS_1_DIR, filename) + if not os.path.isfile(path): + pytest.skip(f"missing dataset {path}") + _assert_good_mps_1_model(Read(path)) + def test_bad_mps_files(): NumMpsFiles = 13 @@ -23,14 +64,14 @@ def test_bad_mps_files(): ) if os.path.exists(file_path): with pytest.raises(InputValidationError): - mps_parser.ParseMps(file_path, True) + Read(file_path, fixed_mps_format=True) def test_good_mps_file(): file_path = ( RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-free-var.mps" ) - data_model = mps_parser.ParseMps(file_path) + data_model = Read(file_path) assert not data_model.get_sense() @@ -71,7 +112,7 @@ def test_good_mps_file(): # Minimal LP content that should parse identically regardless of whether it's -# routed through ParseLp() or the server's extension-based dispatch path. +# routed through Read() or the server's extension-based dispatch path. _MINIMAL_LP = """ Minimize x @@ -90,7 +131,7 @@ def test_parse_lp_basic(): f.write(_MINIMAL_LP) path = f.name try: - data_model = mps_parser.ParseLp(path) + data_model = Read(path) finally: os.unlink(path) @@ -127,7 +168,7 @@ def test_parse_lp_rejects_unsupported_section(): path = f.name try: with pytest.raises(InputValidationError): - mps_parser.ParseLp(path) + Read(path) finally: os.unlink(path) @@ -161,8 +202,8 @@ def test_parse_lp_and_parse_mps_agree_on_trivial_problem(): f.write(_MINIMAL_LP) lp_path = f.name try: - lp_model = mps_parser.ParseLp(lp_path) - mps_model = mps_parser.ParseMps(mps_path) + lp_model = Read(lp_path) + mps_model = Read(mps_path) finally: os.unlink(mps_path) os.unlink(lp_path) @@ -184,3 +225,32 @@ def test_parse_lp_and_parse_mps_agree_on_trivial_problem(): lp_model.get_constraint_lower_bounds().tolist() == mps_model.get_constraint_lower_bounds().tolist() ) + + +def test_read_dispatches_mps_and_lp(): + mps_path = ( + RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-free-var.mps" + ) + lp_path = ( + RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-free-var.lp" + ) + mps_model = Read(mps_path) + lp_model = Read(lp_path) + assert mps_model.get_sense() == lp_model.get_sense() + assert ( + mps_model.get_variable_names().tolist() + == lp_model.get_variable_names().tolist() + ) + + +def test_read_unrecognized_extension(): + with tempfile.NamedTemporaryFile(suffix=".xyz", delete=False) as f: + f.write(b"x\n") + path = f.name + try: + with pytest.raises( + RuntimeError, match="unrecognized input file extension" + ): + Read(path) + finally: + os.unlink(path) diff --git a/python/cuopt/cuopt/tests/linear_programming/test_python_API.py b/python/cuopt/cuopt/tests/linear_programming/test_python_API.py index 66242550aa..afc1ec6992 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_python_API.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_python_API.py @@ -339,6 +339,30 @@ def test_read_write_mps_and_relaxation(): assert v.getValue() == pytest.approx(expected_values_lp[i]) +def test_problem_read_mps_and_lp(): + mps_path = ( + RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-free-var.mps" + ) + lp_path = ( + RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-free-var.lp" + ) + mps_problem = Problem.read(mps_path) + lp_problem = Problem.read(lp_path) + assert mps_problem.NumVariables == lp_problem.NumVariables == 2 + mps_names = {v.VariableName for v in mps_problem.getVariables()} + lp_names = {v.VariableName for v in lp_problem.getVariables()} + assert mps_names == lp_names == {"VAR1", "VAR2"} + + +def test_problem_read_mps_deprecated(): + mps_path = ( + RAPIDS_DATASET_ROOT_DIR + "/linear_programming/good-mps-free-var.mps" + ) + with pytest.warns(DeprecationWarning, match="readMPS is deprecated"): + problem = Problem.readMPS(mps_path) + assert problem.NumVariables == 2 + + def _run_incumbent_solutions(include_set_callback): # Callback for incumbent solution class CustomGetSolutionCallback(GetSolutionCallback): diff --git a/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py b/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py index a5b76d57f3..0e0db47366 100644 --- a/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py +++ b/python/cuopt_self_hosted/cuopt_sh_client/cuopt_self_host_client.py @@ -136,7 +136,7 @@ def is_uuid(cuopt_problem_data): # File extensions (case-insensitive, after stripping a compression suffix) that # the cuopt.linear_programming.io package can parse client-side. Matches the -# dispatch table in parse_problem() on the C++ side. +# dispatch table in read() on the C++ side. _PARSEABLE_LP_EXTS = (".lp",) _PARSEABLE_MPS_EXTS = (".mps", ".qps") _COMPRESSION_SUFFIXES = (".gz", ".bz2") @@ -164,7 +164,8 @@ def _client_parseable_extension(path): def _parse_file_to_data_model(problem_input, solver_config): try: - from cuopt.linear_programming import io as mps_parser + from cuopt.linear_programming import DataModel, Read + from cuopt.linear_programming.io import toDict except ImportError as e: raise ImportError( "MPS/LP parsing on the client requires the cuopt package. " @@ -174,27 +175,16 @@ def _parse_file_to_data_model(problem_input, solver_config): "DataModel." ) from e # problem_input is either a path (str) to an MPS/LP/QPS file (optionally - # .gz / .bz2 compressed), or an mps_parser DataModel already handed to us. - if isinstance(problem_input, mps_parser.parser_wrapper.DataModel): + # .gz / .bz2 compressed), or a DataModel already handed to us. + if isinstance(problem_input, DataModel): model = problem_input - log.debug("Received mps_parser DataModel object") + log.debug("Received DataModel object") else: t0 = time.time() - kind = ( - _client_parseable_extension(problem_input) - if isinstance(problem_input, str) - else None - ) - if kind == "lp": - model = mps_parser.ParseLp(problem_input) - else: - # MPS, QPS, and any unrecognized extension fall through to the - # MPS parser, which accepts both .mps and .qps (and their .gz / - # .bz2 variants) via the underlying C++ parse_mps(). - model = mps_parser.ParseMps(problem_input) + model = Read(problem_input) parse_time = time.time() - t0 log.debug(f"file parsing time was {parse_time}") - problem_data = mps_parser.toDict(model, json=use_zlib) + problem_data = toDict(model, json=use_zlib) if type(solver_config) is dict: problem_data["solver_config"] = solver_config diff --git a/python/cuopt_server/cuopt_server/tests/test_pdlp_warmstart.py b/python/cuopt_server/cuopt_server/tests/test_pdlp_warmstart.py index 76dd22c054..5d62b87935 100644 --- a/python/cuopt_server/cuopt_server/tests/test_pdlp_warmstart.py +++ b/python/cuopt_server/cuopt_server/tests/test_pdlp_warmstart.py @@ -3,7 +3,8 @@ import os -from cuopt.linear_programming import io as mps_parser +from cuopt.linear_programming import Read +from cuopt.linear_programming.io import toDict import msgpack from cuopt.linear_programming import solver_settings @@ -32,8 +33,8 @@ def test_warmstart(cuoptproc): # noqa RAPIDS_DATASET_ROOT_DIR, "linear_programming/square41/square41.mps", ) - data_model_obj = mps_parser.ParseMps(file_path) - data = mps_parser.toDict(data_model_obj, json=True) + data_model_obj = Read(file_path) + data = toDict(data_model_obj, json=True) settings = solver_settings.SolverSettings() settings.set_optimality_tolerance(1e-4) settings.set_parameter(CUOPT_INFEASIBILITY_DETECTION, False) diff --git a/python/libcuopt/libcuopt/tests/test_cli.sh b/python/libcuopt/libcuopt/tests/test_cli.sh index 90732a78ee..85f73fa58f 100644 --- a/python/libcuopt/libcuopt/tests/test_cli.sh +++ b/python/libcuopt/libcuopt/tests/test_cli.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 set -euo pipefail @@ -22,6 +22,12 @@ cuopt_cli --help | grep "Usage: cuopt_cli" > /dev/null || (echo "Expected usage cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/linear_programming/good-mps-1.mps | grep -q "Status: " || (echo "Expected solution not found" && exit 1) +cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/linear_programming/good-mps-1.lp | grep -q "Status: " || (echo "Expected solution not found for .lp" && exit 1) + +cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/linear_programming/good-mps-1.lp.gz | grep -q "Status: " || (echo "Expected solution not found for .lp.gz" && exit 1) + +cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/linear_programming/good-mps-1.lp.bz2 | grep -q "Status: " || (echo "Expected solution not found for .lp.bz2" && exit 1) + # Add a for mixed integer programming test with options cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/mip/sample.mps --mip-absolute-gap 0.01 --time-limit 10 | grep -q "Solution objective" || (echo "Expected solution objective not found" && exit 1) diff --git a/regression/benchmark_scripts/utils.py b/regression/benchmark_scripts/utils.py index f720dd81b1..d391ace3bf 100644 --- a/regression/benchmark_scripts/utils.py +++ b/regression/benchmark_scripts/utils.py @@ -3,7 +3,7 @@ from cuopt_server.utils.utils import build_routing_datamodel_from_json -from cuopt.linear_programming import mps_parser +from cuopt.linear_programming import Read from cuopt.linear_programming.solver_settings import SolverSettings import os import json @@ -16,7 +16,7 @@ def build_datamodel_from_mps(data): """ if os.path.isfile(data): - data_model = mps_parser.ParseMps(data) + data_model = Read(data) else: raise ValueError( f"Invalid type : {type(data)} has been provided as input, "