Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
92ffa8f
Add LP format reader; accept .lp wherever .mps is accepted
mlubin May 15, 2026
7e944b1
ci: update expected error substring for MPS/LP local-file rejection
mlubin May 17, 2026
16c1c3d
Reject ambiguous '<number> [' and stray '<number> *' in LP parser
mlubin May 17, 2026
7e3899c
Inline finalize_problem into lp_parser.cpp
mlubin May 17, 2026
f32d12e
Rewrite refactor-history comments to describe current state
mlubin May 17, 2026
2b2ff75
Accept bare 'Semi' and 'Semis' as Semi-Continuous section keywords
mlubin May 17, 2026
8c6f25a
Simplify LP parser's quadratic objective to upper-triangular pass
mlubin May 17, 2026
4268886
Reject bare linear terms inside quadratic '[ ... ]' brackets
mlubin May 17, 2026
9cfd53a
Remove references to named commercial solvers from comments and docs
mlubin May 17, 2026
a7cdfb8
cuopt_cli: expand filename --help to match runtime dispatch
mlubin May 17, 2026
94e730e
c_api_tests: guarantee cleanup in read_lp_file_by_extension on failure
mlubin May 17, 2026
079ed41
parser_test: exception-safe temp-file cleanup in dispatch_parse
mlubin May 17, 2026
bee7dfb
Self-hosted client: extend extension dispatch to QPS and compressed v…
mlubin May 17, 2026
9a91532
Rename Python mps_parser package to io
mlubin May 17, 2026
1683bea
ParseLp: add type annotations and Raises docstring section
mlubin May 18, 2026
c82cfcc
parser_test: replace shared /tmp paths with RAII temp_file_t
mlubin May 18, 2026
7047c39
docs: fix mps_datamodel_example docstring to name ParseMps
mlubin May 18, 2026
9567265
cuOptReadProblem: validate inputs and return CUOPT_INVALID_ARGUMENT
mlubin May 18, 2026
49a23ca
Merge LP format reader (PR #1120)
Iroy30 May 19, 2026
7e843ac
Add ParseProblem API and unify file-format parsing in Python.
Iroy30 May 20, 2026
19e4957
fix conflicts
Iroy30 May 21, 2026
9f994d6
cleanup unsed references
Iroy30 May 21, 2026
ce60a90
doc fixes
Iroy30 May 21, 2026
7d802b1
doc fixes
Iroy30 May 21, 2026
4a82497
Update parser_example.rst
Iroy30 May 21, 2026
1384e7b
Update cli-examples.rst
Iroy30 May 21, 2026
bc70303
renaming
Iroy30 May 22, 2026
951c582
add back call_parse_mps
Iroy30 May 22, 2026
96c72df
Merge remote-tracking branch 'iroy30/python-read-mps-lp' into python-…
Iroy30 May 22, 2026
62c84e0
add back call_parse_mps
Iroy30 May 22, 2026
103bb44
rever routing hanges
Iroy30 May 22, 2026
ca97a74
Merge branch 'release/26.06' into python-read-mps-lp
Iroy30 May 22, 2026
cd340df
Update docs/cuopt/source/cuopt-server/examples/lp/examples/lp_datamod…
Iroy30 May 22, 2026
af02ce4
address review
Iroy30 May 22, 2026
89c6280
Merge branch 'release/26.06' into python-read-mps-lp
Iroy30 May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmarks/linear_programming/cuopt/benchmark_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, double> op_problem =
cuopt::linear_programming::io::parse_mps<int, double>(p);
cuopt::linear_programming::io::read_mps<int, double>(p);

auto filename_string = filename.filename().string();

Expand Down
2 changes: 1 addition & 1 deletion benchmarks/linear_programming/cuopt/run_mip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, double>(file_path, input_mps_strict);
cuopt::linear_programming::io::read_mps<int, double>(file_path, input_mps_strict);
} catch (const std::logic_error& e) {
CUOPT_LOG_ERROR("MPS parser execption: %s", e.what());
parsing_failed = true;
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/linear_programming/cuopt/run_pdlp.cu
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, double> op_problem =
cuopt::linear_programming::io::parse_mps<int, double>(program.get<std::string>("--path"));
cuopt::linear_programming::io::read_mps<int, double>(program.get<std::string>("--path"));

// Solve LP problem
bool problem_checking = true;
Expand Down
21 changes: 18 additions & 3 deletions ci/test_self_hosted_service.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion cpp/cuopt_cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, double>(file_path);
mps_data_model = cuopt::linear_programming::io::read<int, double>(file_path);
} catch (const std::logic_error& e) {
CUOPT_LOG_ERROR("Parser exception: %s", e.what());
parsing_failed = true;
Expand Down
38 changes: 20 additions & 18 deletions cpp/include/cuopt/linear_programming/io/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_mps(const std::string& mps_file_path,
bool fixed_mps_format = false);
mps_data_model_t<i_t, f_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 <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_mps_from_string(std::string_view mps_contents,
bool fixed_mps_format = false);
mps_data_model_t<i_t, f_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
Expand All @@ -82,62 +82,64 @@ mps_data_model_t<i_t, f_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 <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_lp(const std::string& lp_file_path);
mps_data_model_t<i_t, f_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.
* @return mps_data_model_t A fully formed LP/MIP/QP problem representing the
* given content.
*/
template <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_lp_from_string(std::string_view lp_contents);
mps_data_model_t<i_t, f_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 <typename i_t, typename f_t>
inline mps_data_model_t<i_t, f_t> parse_problem(const std::string& path)
inline mps_data_model_t<i_t, f_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) {
return static_cast<char>(std::tolower(c));
});
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<i_t, f_t>(path);
return read_mps<i_t, f_t>(path, fixed_mps_format);
}
if (lower.ends_with(".lp") || lower.ends_with(".lp.gz") || lower.ends_with(".lp.bz2")) {
return parse_lp<i_t, f_t>(path);
return read_lp<i_t, f_t>(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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
namespace cuopt {
namespace cython {

std::unique_ptr<cuopt::linear_programming::io::mps_data_model_t<int, double>> call_read(
const std::string& file_path, bool fixed_mps_format);

std::unique_ptr<cuopt::linear_programming::io::mps_data_model_t<int, double>> call_parse_mps(
const std::string& mps_file_path, bool fixed_mps_format);

std::unique_ptr<cuopt::linear_programming::io::mps_data_model_t<int, double>> call_parse_lp(
const std::string& lp_file_path);

} // namespace cython
} // namespace cuopt
16 changes: 8 additions & 8 deletions cpp/src/io/lp_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ template <typename i_t, typename f_t>
class LpParseEngine {
public:
LpParseEngine(lp_parser_t<i_t, f_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<i_t, f_t>& out, std::string_view text);

private:
Expand Down Expand Up @@ -1546,28 +1546,28 @@ template class lp_parser_t<int, float>;
template class lp_parser_t<int, double>;

// ===========================================================================
// Public parse_lp() / parse_lp_from_string()
// Public read_lp() / read_lp_from_string()
// ===========================================================================

template <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_lp(const std::string& lp_file_path)
mps_data_model_t<i_t, f_t> read_lp(const std::string& lp_file_path)
{
mps_data_model_t<i_t, f_t> problem;
lp_parser_t<i_t, f_t> parser(problem, lp_file_path);
return problem;
}

template <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_lp_from_string(std::string_view lp_contents)
mps_data_model_t<i_t, f_t> read_lp_from_string(std::string_view lp_contents)
{
mps_data_model_t<i_t, f_t> problem;
lp_parser_t<i_t, f_t> parser(problem, lp_contents);
return problem;
}

template mps_data_model_t<int, float> parse_lp<int, float>(const std::string&);
template mps_data_model_t<int, double> parse_lp<int, double>(const std::string&);
template mps_data_model_t<int, float> parse_lp_from_string<int, float>(std::string_view);
template mps_data_model_t<int, double> parse_lp_from_string<int, double>(std::string_view);
template mps_data_model_t<int, float> read_lp<int, float>(const std::string&);
template mps_data_model_t<int, double> read_lp<int, double>(const std::string&);
template mps_data_model_t<int, float> read_lp_from_string<int, float>(std::string_view);
template mps_data_model_t<int, double> read_lp_from_string<int, double>(std::string_view);

} // namespace cuopt::linear_programming::io
2 changes: 1 addition & 1 deletion cpp/src/io/lp_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class lp_parser_t {
lp_parser_t(mps_data_model_t<i_t, f_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<i_t, f_t>& problem, std::string_view input);
Expand Down
17 changes: 8 additions & 9 deletions cpp/src/io/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,27 @@
namespace cuopt::linear_programming::io {

template <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_mps(const std::string& mps_file, bool fixed_mps_format)
mps_data_model_t<i_t, f_t> read_mps(const std::string& mps_file, bool fixed_mps_format)
{
mps_data_model_t<i_t, f_t> problem;
mps_parser_t<i_t, f_t> parser(problem, mps_file, fixed_mps_format);
return problem;
}

template <typename i_t, typename f_t>
mps_data_model_t<i_t, f_t> parse_mps_from_string(std::string_view mps_contents,
bool fixed_mps_format)
mps_data_model_t<i_t, f_t> read_mps_from_string(std::string_view mps_contents,
bool fixed_mps_format)
{
mps_data_model_t<i_t, f_t> problem;
mps_parser_t<i_t, f_t> parser(problem, mps_contents, fixed_mps_format);
return problem;
}

template mps_data_model_t<int, float> parse_mps(const std::string& mps_file, bool fixed_mps_format);
template mps_data_model_t<int, double> parse_mps(const std::string& mps_file,
bool fixed_mps_format);
template mps_data_model_t<int, float> parse_mps_from_string(std::string_view mps_contents,
template mps_data_model_t<int, float> read_mps(const std::string& mps_file, bool fixed_mps_format);
template mps_data_model_t<int, double> read_mps(const std::string& mps_file, bool fixed_mps_format);
template mps_data_model_t<int, float> read_mps_from_string(std::string_view mps_contents,
bool fixed_mps_format);
template mps_data_model_t<int, double> read_mps_from_string(std::string_view mps_contents,
bool fixed_mps_format);
template mps_data_model_t<int, double> parse_mps_from_string(std::string_view mps_contents,
bool fixed_mps_format);

} // namespace cuopt::linear_programming::io
16 changes: 8 additions & 8 deletions cpp/src/io/utilities/cython_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@
namespace cuopt {
namespace cython {

std::unique_ptr<cuopt::linear_programming::io::mps_data_model_t<int, double>> call_parse_mps(
const std::string& mps_file_path, bool fixed_mps_format)
std::unique_ptr<cuopt::linear_programming::io::mps_data_model_t<int, double>> call_read(
const std::string& file_path, bool fixed_mps_format)
{
return std::make_unique<cuopt::linear_programming::io::mps_data_model_t<int, double>>(std::move(
cuopt::linear_programming::io::parse_mps<int, double>(mps_file_path, fixed_mps_format)));
return std::make_unique<cuopt::linear_programming::io::mps_data_model_t<int, double>>(
std::move(cuopt::linear_programming::io::read<int, double>(file_path, fixed_mps_format)));
}

std::unique_ptr<cuopt::linear_programming::io::mps_data_model_t<int, double>> call_parse_lp(
const std::string& lp_file_path)
std::unique_ptr<cuopt::linear_programming::io::mps_data_model_t<int, double>> call_parse_mps(
const std::string& mps_file_path, bool fixed_mps_format)
{
return std::make_unique<cuopt::linear_programming::io::mps_data_model_t<int, double>>(
std::move(cuopt::linear_programming::io::parse_lp<int, double>(lp_file_path)));
return std::make_unique<cuopt::linear_programming::io::mps_data_model_t<int, double>>(std::move(
cuopt::linear_programming::io::read_mps<int, double>(mps_file_path, fixed_mps_format)));
}

} // namespace cython
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/pdlp/cuopt_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ cuopt_int_t cuOptReadProblem(const char* filename, cuOptOptimizationProblem* pro
std::string filename_str(filename);
std::unique_ptr<mps_data_model_t<cuopt_int_t, cuopt_float_t>> 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<mps_data_model_t<cuopt_int_t, cuopt_float_t>>(
parse_problem<cuopt_int_t, cuopt_float_t>(filename_str));
read<cuopt_int_t, cuopt_float_t>(filename_str));
} catch (const std::exception& e) {
CUOPT_LOG_INFO("Error parsing input file: %s", e.what());
delete problem_and_stream;
Expand Down
2 changes: 1 addition & 1 deletion cpp/tests/dual_simplex/unit_tests/solve_barrier.cu
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, double>(path);
auto mps_data = cuopt::linear_programming::io::read_mps<int, double>(path);

auto settings = cuopt::linear_programming::pdlp_solver_settings_t<int, double>{};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ class GrpcIntegrationTestBase : public ::testing::Test {

cpu_optimization_problem_t<int32_t, double> load_problem_from_mps(const std::string& mps_path)
{
auto mps_data = cuopt::linear_programming::io::parse_mps<int32_t, double>(mps_path);
auto mps_data = cuopt::linear_programming::io::read_mps<int32_t, double>(mps_path);
cpu_optimization_problem_t<int32_t, double> problem;
populate_from_mps_data_model(&problem, mps_data);
return problem;
Expand Down
Loading
Loading