Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 14 additions & 10 deletions cpp/cuopt_cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ static char cuda_module_loading_env[] = "CUDA_MODULE_LOADING=EAGER";
* @brief Command line interface for solving Linear Programming (LP) and Mixed Integer Programming
* (MIP) problems using cuOpt
*
* This CLI provides a simple interface to solve LP/MIP problems using cuOpt. It accepts MPS format
* input files and various solver parameters.
* This CLI provides a simple interface to solve LP/MIP problems using cuOpt. It accepts MPS or LP
* format input files (dispatched automatically by extension) and various solver parameters.
*
* Usage:
* ```
* cuopt_cli <mps_file_path> [OPTIONS]
* cuopt_cli [OPTIONS] <mps_file_path>
* cuopt_cli <input_file_path> [OPTIONS]
* cuopt_cli [OPTIONS] <input_file_path>
* ```
*
* Required arguments:
Expand Down Expand Up @@ -84,7 +84,9 @@ inline cuopt::init_logger_t dummy_logger(

/**
* @brief Run a single file
* @param file_path Path to the MPS format input file containing the optimization problem
* @param file_path Path to the input file in MPS or LP format. Dispatched by
* extension: a ".lp" suffix routes to the LP parser; anything
* else (.mps, .mps.gz, .mps.bz2, or extensionless) to MPS.
* @param initial_solution_file Path to initial solution file in SOL format
* @param settings Merged solver settings (config file loaded in main, then CLI overrides applied)
*/
Expand All @@ -98,22 +100,21 @@ int run_single_file(const std::string& file_path,

std::string base_filename = file_path.substr(file_path.find_last_of("/\\") + 1);

constexpr bool input_mps_strict = false;
cuopt::mps_parser::mps_data_model_t<int, double> mps_data_model;
bool parsing_failed = false;
auto timer = cuopt::timer_t(settings.get_parameter<double>(CUOPT_TIME_LIMIT));
{
CUOPT_LOG_INFO("Reading file %s", base_filename.c_str());
try {
mps_data_model = cuopt::mps_parser::parse_mps<int, double>(file_path, input_mps_strict);
mps_data_model = cuopt::mps_parser::parse_optimization_file<int, double>(file_path);
} catch (const std::logic_error& e) {
CUOPT_LOG_ERROR("MPS parser execption: %s", e.what());
CUOPT_LOG_ERROR("Parser exception: %s", e.what());
parsing_failed = true;
}
}
if (parsing_failed) {
auto log = dummy_logger(settings);
CUOPT_LOG_ERROR("Parsing MPS failed. Exiting!");
CUOPT_LOG_ERROR("Parsing input file failed. Exiting!");
return -1;
}
CUOPT_LOG_INFO("Read file %s in %.2f seconds", base_filename.c_str(), timer.elapsed_time());
Expand Down Expand Up @@ -279,7 +280,10 @@ int main(int argc, char* argv[])
argparse::ArgumentParser program("cuopt_cli", version_string);

// Define all arguments with appropriate defaults and help messages
program.add_argument("filename").help("input mps file").nargs(1).required();
program.add_argument("filename")
.help("input MPS or LP file (dispatched by .lp / .mps extension)")
.nargs(1)
.required();

// FIXME: use a standard format for initial solution file
program.add_argument("--initial-solution")
Expand Down
10 changes: 7 additions & 3 deletions cpp/include/cuopt/linear_programming/cuopt_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,16 @@ cuopt_int_t cuOptGetVersion(cuopt_int_t* version_major,
cuopt_int_t* version_patch);

/**
* @brief Read an optimization problem from an MPS file.
* @brief Read an optimization problem from an MPS or LP file.
*
* @param[in] filename - The path to the MPS file.
* The file format is dispatched on the extension: a case-insensitive ".lp"
* suffix is parsed as LP format; everything else (including ".mps",
* ".mps.gz", ".mps.bz2", or no extension) is parsed as MPS.
*
* @param[in] filename - The path to the MPS or LP file.
*
* @param[out] problem_ptr - A pointer to a cuOptOptimizationProblem. On output
* the problem will be created and initialized with the data from the MPS file
* the problem will be created and initialized with the data from the input file.
*
* @return A status code indicating success or failure.
*/
Expand Down
1 change: 1 addition & 0 deletions cpp/libmps_parser/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ add_library(mps_parser SHARED
src/data_model_view.cpp
src/mps_data_model.cpp
src/mps_parser.cpp
src/lp_parser.cpp
src/mps_writer.cpp
src/parser.cpp
src/writer.cpp
Expand Down
36 changes: 36 additions & 0 deletions cpp/libmps_parser/include/mps_parser/lp_parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */

#pragma once

#include <mps_parser/mps_data_model.hpp>

#include <string>

namespace cuopt::mps_parser {

/**
* @brief Reads a linear, mixed-integer, or quadratic optimization problem from
* a file in LP format.
*
* The LP format is a human-readable alternative to MPS format. This parser
* supports the conventional LP dialect implemented by most commercial
* optimization solvers (not the lpsolve variant, which has a different
* syntax).
*
* Scope: LP, MIP, and QP problems are supported. SOS constraints, PWL
* objectives, semi-continuous variables, general constraints, and user cuts
* cause a ValidationError when encountered.
*
* @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);

} // namespace cuopt::mps_parser
36 changes: 35 additions & 1 deletion cpp/libmps_parser/include/mps_parser/parser.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */

#pragma once

#include <mps_parser/lp_parser.hpp>
#include <mps_parser/mps_data_model.hpp>

#include <string>
#include <string_view>

namespace cuopt::mps_parser {

/**
Expand All @@ -35,4 +39,34 @@ 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);

/**
* @brief Reads an optimization problem from a file, dispatching on the file
* extension.
*
* Case-insensitive `.lp` suffix routes to parse_lp(). Everything else —
* including `.mps`, `.mps.gz`, `.mps.bz2`, and extensionless files — routes
* to parse_mps() (with free-format parsing). 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.
* @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_optimization_file(const std::string& path)
{
constexpr std::string_view lp_suffix = ".lp";
auto ends_with_ci = [](std::string_view s, std::string_view suffix) {
if (s.size() < suffix.size()) return false;
for (size_t i = 0; i < suffix.size(); ++i) {
char a = s[s.size() - suffix.size() + i];
if (a >= 'A' && a <= 'Z') a = static_cast<char>(a - 'A' + 'a');
if (a != suffix[i]) return false;
}
return true;
};
if (ends_with_ci(path, lp_suffix)) return parse_lp<i_t, f_t>(path);
return parse_mps<i_t, f_t>(path);
}

} // namespace cuopt::mps_parser
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */
Expand All @@ -17,5 +17,8 @@ namespace cython {
std::unique_ptr<cuopt::mps_parser::mps_data_model_t<int, double>> call_parse_mps(
const std::string& mps_file_path, bool fixed_mps_format);

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

} // namespace cython
} // namespace cuopt
Loading