Skip to content

Add lp format writer and rename mps references#1567

Open
Iroy30 wants to merge 4 commits into
NVIDIA:mainfrom
Iroy30:add_lp_writer
Open

Add lp format writer and rename mps references#1567
Iroy30 wants to merge 4 commits into
NVIDIA:mainfrom
Iroy30:add_lp_writer

Conversation

@Iroy30

@Iroy30 Iroy30 commented Jul 14, 2026

Copy link
Copy Markdown
Member

Description

Summary

Adds an LP-format writer to cuOpt (complementing the existing MPS writer),
and — since the writer surfaced a long-standing naming issue — renames the
shared, format-neutral problem model from mps_data_model_t to data_model_t
(the type is used by both the LP and MPS readers/writers and is not
MPS-specific). The LP writer is exposed through the C, C++, and Python APIs.

Changes

LP writer (C++)

  • New lp_writer_t (cpp/src/io/lp_writer.cpp, cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp), mirroring the MPS writer: objective (incl. quadratic [ ... ] / 2), linear + quadratic constraints, range-constraint splitting, variable bounds, and Generals/Binaries/Semi-Continuous sections.
  • Public write_lp(...) added to io/writer.hpp + src/io/writer.cpp.
  • Build wiring in cpp/src/io/CMakeLists.txt.

Rename: mps_data_model_tdata_model_t

  • git mv io/mps_data_model.hppio/data_model.hpp and src/io/mps_data_model.cppsrc/io/data_model.cpp; class renamed.
  • Migrated all in-repo C++ usages (io module, solve/pdlp/mip, C API, CLI, tests, benchmarks).

Rename: mps_data_model_to_optimization_problemdata_model_to_optimization_problem

  • Full rename across definition, explicit instantiations, solve.hpp/solve.cuh declarations, callers, and all test call sites. No deprecation shim (the name is now consistent with data_model_t).

Python bindings for the LP writer

  • Added the write_lp Cython extern and DataModel.writeLP(...) / Problem.writeLP(...), mirroring writeMPS.

Tests

  • New C++ LP_WRITER_TEST — 6 round-trip cases (simple LP, maximize+offset, MIP generals/binaries, free/fixed bounds, quadratic objective, quadratic constraint).
  • New Python test_write_lp_round_trip in test_parser.py (parse LP → writeLP → read back → assert model equivalence).

Issue

Checklist

  • I am familiar with the Contributing Guidelines.
  • Testing
    • New or existing tests cover these changes
    • Added tests
    • Created an issue to follow-up
    • NA
  • Documentation
    • The documentation is up to date with these changes
    • Added new documentation
    • NA

@Iroy30
Iroy30 requested review from a team as code owners July 14, 2026 06:04
@copy-pr-bot

copy-pr-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ea31907a-bb6d-485d-a260-579a4ac7e013

📥 Commits

Reviewing files that changed from the base of the PR and between eacde95 and 34b57c4.

📒 Files selected for processing (17)
  • .lp_harness
  • .lp_harness.cpp
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp
  • cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem_interface.hpp
  • cpp/src/io/lp_writer.cpp
  • cpp/tests/linear_programming/lp_writer_test.cpp
  • cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu
  • docs/cuopt/source/convex-settings.rst
  • docs/cuopt/source/mip-settings.rst
  • python/cuopt/cuopt/linear_programming/data_model/data_model.py
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • python/cuopt/cuopt/linear_programming/io/__init__.py
  • python/cuopt/cuopt/linear_programming/io/format.py
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_parser.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • docs/cuopt/source/mip-settings.rst
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp

📝 Walkthrough

Walkthrough

Changes

LP output support

Layer / File(s) Summary
Format contracts and dispatch
cpp/include/cuopt/mathematical_optimization/{constants.h,optimization_problem_interface.hpp,optimization_problem.hpp,cpu_optimization_problem.hpp,cuopt_c.h}, docs/cuopt/source/*
Adds LP format constants, extension detection, and format-aware output interfaces.
LP writer implementation
cpp/include/cuopt/mathematical_optimization/io/*, cpp/src/io/*
Adds LP serialization for linear, mixed-integer, quadratic, bounded, and semi-continuous models.
Solver and language integration
cpp/src/pdlp/*, cpp/src/mip_heuristics/solve.cu, python/cuopt/cuopt/linear_programming/*
Routes exports through LP or MPS writers and exposes file-format selection through Python APIs.
Round-trip validation
cpp/tests/linear_programming/*, cpp/tests/qp/unit_tests/mps_writer_test.cpp, .lp_harness.cpp, python/cuopt/cuopt/tests/linear_programming/*
Adds LP writer, parser, solver-output, C API round-trip, and format-detection tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: improvement

Suggested reviewers: kaatish, aliceb-nv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding an LP writer and updating MPS-related names.
Description check ✅ Passed The description is directly about the LP writer and related API updates, so it matches the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx (1)

519-527: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reject variable-type vectors with inconsistent cardinality.

The helper only handles the empty case; a non-empty variable_types array whose length differs from n_vars is passed directly to the C++ view. Validate that the vector contains exactly one entry per variable before writing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx`
around lines 519 - 527, Update _prepare_data_model_view_for_write to validate
that a non-empty variable_types array has exactly n_vars entries before calling
set_data_model_view. Preserve the existing default creation for an empty vector
with variables, and reject mismatched cardinality rather than passing it to the
C++ view.
🧹 Nitpick comments (3)
cpp/include/cuopt/mathematical_optimization/io/writer.hpp (1)

26-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the write_lp execution and failure contract.

The new public API documents format support and parameters, but not I/O failure behavior, thread-safety, or GPU/stream requirements. Add the actual contract from the implementation so callers know what errors and execution constraints to expect.

As per path instructions, public C++ headers should document thread-safety, GPU requirements, and numerical behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/io/writer.hpp` around lines 26 -
36, Expand the write_lp documentation to state its I/O failure behavior,
thread-safety guarantees or restrictions, GPU/stream requirements, and relevant
numerical behavior, using the implementation’s actual contract. Keep the
existing format-support and parameter documentation, and anchor the additions to
the public write_lp declaration.

Source: Path instructions

cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp (1)

42-54: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Document the lifetime requirement on both constructors.

create_view() builds data_model_view_t from raw pointers into the passed-in problem's buffers (non-owning view), and the ctor at line 54 stores this in owned_view_. If callers construct lp_writer_t from a temporary data_model_t/data_model_view_t and then call write() in a later statement, the referenced data is already destroyed — same hazard as the existing mps_writer_t. Worth a @note on both ctors that the passed-in problem must outlive the lp_writer_t instance (at least through the write() call).

📝 Suggested doc addition
   /**
    * `@brief` Ctor. Takes a data model as input and writes it out as an LP formatted file
    *
    * `@param`[in] problem Data model to write
+   *
+   * `@note` `problem` must outlive this lp_writer_t instance (or at least remain
+   * valid until write() is called), since the constructed view stores
+   * non-owning references into `problem`'s underlying buffers.
    */
   lp_writer_t(const data_model_t<i_t, f_t>& problem);

As per path instructions: "Suggest documenting thread-safety, GPU requirements, numerical behavior" for new public headers under cpp/include/cuopt/**/*.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp` around lines 42
- 54, Add a `@note` to both lp_writer_t constructors documenting that the
referenced problem must outlive the lp_writer_t instance, at least until write()
completes, because the stored view is non-owning. Apply the same lifetime
wording to the data_model_view_t and data_model_t overloads without changing
constructor behavior.

Source: Path instructions

cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp (1)

38-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename the helper to match its generic input.

The changed signature accepts data_model_t, while the function name and documentation still describe an MPS-only model. Rename it to populate_from_data_model and update its call sites so the API reflects the actual contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp`
around lines 38 - 51, Rename populate_from_mps_data_model to
populate_from_data_model, update its documentation to remove MPS-specific
wording, and change every call site to use the new name while preserving the
existing data_model_t behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/include/cuopt/mathematical_optimization/io/data_model.hpp`:
- Around line 331-387: Add a private: access specifier before the model
data-member list containing maximize_, CSR buffers, bounds, metadata, solution
vectors, and quadratic-programming fields. Keep the existing validated accessor
methods public and do not alter field names, initialization, or validation
behavior.
- Line 51: Update the default constructor of data_model_t to initialize
maximize_ explicitly to false, ensuring default-constructed models use
minimization during conversion and writing while preserving existing
initialization behavior for other members.

In `@cpp/include/cuopt/mathematical_optimization/io/utilities/cython_parser.hpp`:
- Around line 17-21: Add a direct `#include` for the standard string header in
cython_parser.hpp before the declarations of call_read and call_parse_mps, so
their std::string parameters do not rely on transitive includes. Preserve the
existing function declarations unchanged.

In `@cpp/include/cuopt/mathematical_optimization/solve.hpp`:
- Around line 72-77: Update all affected solve_lp overload declarations and
Doxygen, including the batch overload, to consistently use data_model_t as the
data-model parameter name instead of mps_data_model or user_problem. Revise each
parameter description to accurately describe the io::data_model_t<i_t, f_t> type
and its actual behavior.

In `@cpp/tests/linear_programming/lp_writer_test.cpp`:
- Around line 149-150: Strengthen the quadratic comparison helper around
has_quadratic_objective() so it validates content rather than only presence and
count. Compare quadratic objective values, indices, and offsets, then inspect
every quadratic constraint’s row metadata, linear terms, RHS, and quadratic
triplets using tolerance for numeric values while preserving exact structural
comparisons.

In `@python/cuopt/cuopt/linear_programming/data_model/data_model.py`:
- Around line 761-764: Add type hints to both new public writeLP methods:
annotate DataModel.writeLP in
python/cuopt/cuopt/linear_programming/data_model/data_model.py at lines 761-764
with user_problem_file: str and a None return type, and annotate the
corresponding writeLP method in python/cuopt/cuopt/linear_programming/problem.py
at lines 2049-2059 with lp_file: str and a None return type.

In `@python/cuopt/cuopt/tests/linear_programming/test_parser.py`:
- Around line 259-304: Expand _assert_models_equivalent to compare
constraint-matrix structure, including column indices/offsets, and assert
get_variable_types() and get_row_names() match between models. Add a separate
test_write_lp_round_trip case using multiple constraints plus Integer and Binary
variables and their bounds sections so the variable-type writer/parser path is
exercised.

---

Outside diff comments:
In `@python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx`:
- Around line 519-527: Update _prepare_data_model_view_for_write to validate
that a non-empty variable_types array has exactly n_vars entries before calling
set_data_model_view. Preserve the existing default creation for an empty vector
with variables, and reject mismatched cardinality rather than passing it to the
C++ view.

---

Nitpick comments:
In `@cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp`:
- Around line 42-54: Add a `@note` to both lp_writer_t constructors documenting
that the referenced problem must outlive the lp_writer_t instance, at least
until write() completes, because the stored view is non-owning. Apply the same
lifetime wording to the data_model_view_t and data_model_t overloads without
changing constructor behavior.

In `@cpp/include/cuopt/mathematical_optimization/io/writer.hpp`:
- Around line 26-36: Expand the write_lp documentation to state its I/O failure
behavior, thread-safety guarantees or restrictions, GPU/stream requirements, and
relevant numerical behavior, using the implementation’s actual contract. Keep
the existing format-support and parameter documentation, and anchor the
additions to the public write_lp declaration.

In `@cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp`:
- Around line 38-51: Rename populate_from_mps_data_model to
populate_from_data_model, update its documentation to remove MPS-specific
wording, and change every call site to use the new name while preserving the
existing data_model_t behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6d5ec415-b588-4d89-8468-00825bcc1b05

📥 Commits

Reviewing files that changed from the base of the PR and between 5e6e5ba and 10d1d5c.

📒 Files selected for processing (63)
  • benchmarks/linear_programming/cuopt/benchmark_helper.hpp
  • benchmarks/linear_programming/cuopt/initial_problem_check.hpp
  • benchmarks/linear_programming/cuopt/run_mip.cpp
  • benchmarks/linear_programming/cuopt/run_pdlp.cu
  • cpp/cuopt_cli.cpp
  • cpp/include/cuopt/mathematical_optimization/io/data_model.hpp
  • cpp/include/cuopt/mathematical_optimization/io/data_model_view.hpp
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp
  • cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
  • cpp/include/cuopt/mathematical_optimization/io/mps_writer.hpp
  • cpp/include/cuopt/mathematical_optimization/io/parser.hpp
  • cpp/include/cuopt/mathematical_optimization/io/utilities/cython_parser.hpp
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem_utils.hpp
  • cpp/include/cuopt/mathematical_optimization/solve.hpp
  • cpp/src/branch_and_bound/pseudo_costs.cpp
  • cpp/src/io/CMakeLists.txt
  • cpp/src/io/data_model.cpp
  • cpp/src/io/data_model_view.cpp
  • cpp/src/io/lp_parser.cpp
  • cpp/src/io/lp_parser.hpp
  • cpp/src/io/lp_writer.cpp
  • cpp/src/io/mps_parser.cpp
  • cpp/src/io/mps_parser_internal.hpp
  • cpp/src/io/mps_writer.cpp
  • cpp/src/io/parser.cpp
  • cpp/src/io/utilities/cython_parser.cpp
  • cpp/src/io/writer.cpp
  • cpp/src/mip_heuristics/solve.cu
  • cpp/src/pdlp/cuopt_c.cpp
  • cpp/src/pdlp/solve.cu
  • cpp/src/pdlp/solve.cuh
  • cpp/src/pdlp/utilities/cython_solve.cu
  • cpp/tests/linear_programming/CMakeLists.txt
  • cpp/tests/linear_programming/lp_writer_test.cpp
  • cpp/tests/linear_programming/parser_test.cpp
  • cpp/tests/linear_programming/pdlp_test.cu
  • cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu
  • cpp/tests/linear_programming/unit_tests/presolve_test.cu
  • cpp/tests/linear_programming/unit_tests/solution_interface_test.cu
  • cpp/tests/linear_programming/utilities/pdlp_test_utilities.cuh
  • cpp/tests/mip/bounds_standardization_test.cu
  • cpp/tests/mip/cuts_test.cu
  • cpp/tests/mip/doc_example_test.cu
  • cpp/tests/mip/elim_var_remap_test.cu
  • cpp/tests/mip/feasibility_jump_tests.cu
  • cpp/tests/mip/incumbent_callback_test.cu
  • cpp/tests/mip/load_balancing_test.cu
  • cpp/tests/mip/mip_utils.cuh
  • cpp/tests/mip/miplib_test.cu
  • cpp/tests/mip/multi_probe_test.cu
  • cpp/tests/mip/presolve_test.cu
  • cpp/tests/mip/problem_test.cu
  • cpp/tests/mip/semi_continuous_test.cu
  • cpp/tests/mip/server_test.cu
  • cpp/tests/mip/unit_test.cu
  • cpp/tests/utilities/inline_lp_test_utils.hpp
  • cpp/tests/utilities/inline_mps_test_utils.hpp
  • python/cuopt/cuopt/linear_programming/data_model/data_model.pxd
  • python/cuopt/cuopt/linear_programming/data_model/data_model.py
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_parser.py

static_assert(std::is_floating_point<f_t>::value,
"'data_model_t' accepts only floating point types for weights");

data_model_t() = default;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Initialize the default optimization sense.

A default-constructed model reads an indeterminate maximize_ value, despite the documented default being minimization. This can select the wrong objective sense during conversion or writing.

Proposed fix
-  bool maximize_;
+  bool maximize_{false};

Also applies to: 331-332

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/io/data_model.hpp` at line 51,
Update the default constructor of data_model_t to initialize maximize_
explicitly to false, ensuring default-constructed models use minimization during
conversion and writing while preserving existing initialization behavior for
other members.

Comment on lines +331 to +387
/** whether to maximize or minimize the objective function */
bool maximize_;
/**
* the constraint matrix itself in the CSR format
* @{
*/
std::vector<f_t> A_;
std::vector<i_t> A_indices_;
std::vector<i_t> A_offsets_;
/** @} */
/** RHS of the constraints */
std::vector<f_t> b_;
/** weights in the objective function */
std::vector<f_t> c_;
/** scale factor of the objective function */
f_t objective_scaling_factor_{1};
/** offset of the objective function */
f_t objective_offset_{0};
/** lower bounds of the variables (primal part) */
std::vector<f_t> variable_lower_bounds_;
/** upper bounds of the variables (primal part) */
std::vector<f_t> variable_upper_bounds_;
/** types of variables can be 'C' or 'I' */
std::vector<char> var_types_;
/** lower bounds of the constraint (dual part) */
std::vector<f_t> constraint_lower_bounds_;
/** upper bounds of the constraint (dual part) */
std::vector<f_t> constraint_upper_bounds_;
/** Type of each constraint */
std::vector<char> row_types_;
/** name of the objective (only a single objective is currently allowed) */
std::string objective_name_;
/** name of the problem */
std::string problem_name_;
/** names of each of the variables in the OP */
std::vector<std::string> var_names_{};
/** names of linear constraint rows in exported MPS order. */
std::vector<std::string> row_names_{};
/** number of variables */
i_t n_vars_{0};
/** number of constraints in the LP representation */
i_t n_constraints_{0};
/** number of non-zero elements in the constraint matrix */
i_t nnz_{0};
/** Initial primal solution */
std::vector<f_t> initial_primal_solution_;
/** Initial dual solution */
std::vector<f_t> initial_dual_solution_;

// QPS-specific data members for quadratic programming support
/** Quadratic objective matrix in CSR format (for (1/2) * x^T * Q * x term) */
std::vector<f_t> Q_objective_values_;
std::vector<i_t> Q_objective_indices_;
std::vector<i_t> Q_objective_offsets_;

/** One full quadratic constraint per QCMATRIX block, in order of appearance in the file */
std::vector<quadratic_constraint_t> quadratic_constraints_;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Make the model state private.

These fields are publicly mutable, allowing callers to bypass model invariants and leave dimensions, CSR buffers, and metadata inconsistent. Add a private: section before the field list and retain validated accessors. As per coding guidelines, “keep data members private.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/io/data_model.hpp` around lines
331 - 387, Add a private: access specifier before the model data-member list
containing maximize_, CSR buffers, bounds, metadata, solution vectors, and
quadratic-programming fields. Keep the existing validated accessor methods
public and do not alter field names, initialization, or validation behavior.

Source: Coding guidelines

Comment on lines 17 to 21
std::unique_ptr<cuopt::mathematical_optimization::io::data_model_t<int, double>> call_read(
const std::string& file_path, bool fixed_mps_format);

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Include <string> directly.

This header exposes std::string but relies on a transitive include from data_model.hpp; add #include <string> to keep it self-contained. As per coding guidelines, “C++ headers should be self-contained” and follow Include What You Use.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/io/utilities/cython_parser.hpp`
around lines 17 - 21, Add a direct `#include` for the standard string header in
cython_parser.hpp before the declarations of call_read and call_parse_mps, so
their std::string parameters do not rely on transitive includes. Preserve the
existing function declarations unchanged.

Source: Coding guidelines

Comment on lines 72 to 77
optimization_problem_solution_t<i_t, f_t> solve_lp(
raft::handle_t const* handle_ptr,
const cuopt::mathematical_optimization::io::mps_data_model_t<i_t, f_t>& mps_data_model,
const cuopt::mathematical_optimization::io::data_model_t<i_t, f_t>& mps_data_model,
pdlp_solver_settings_t<i_t, f_t> const& settings = pdlp_solver_settings_t<i_t, f_t>{},
bool problem_checking = true,
bool use_pdlp_solver_mode = true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the renamed data-model parameter documentation.

These overloads now accept io::data_model_t, but the declarations and Doxygen still use the MPS-specific name mps_data_model; the batch overload also documents a different parameter name, user_problem. Update the parameter names and descriptions to consistently describe data_model_t<i_t, f_t>.

As per path instructions, C++ header parameter descriptions must match the actual types and behavior.

Also applies to: 107-113, 140-145

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/solve.hpp` around lines 72 - 77,
Update all affected solve_lp overload declarations and Doxygen, including the
batch overload, to consistently use data_model_t as the data-model parameter
name instead of mps_data_model or user_problem. Revise each parameter
description to accurately describe the io::data_model_t<i_t, f_t> type and its
actual behavior.

Source: Path instructions

Comment thread cpp/tests/linear_programming/lp_writer_test.cpp Outdated
Comment thread python/cuopt/cuopt/linear_programming/data_model/data_model.py Outdated
Comment thread python/cuopt/cuopt/tests/linear_programming/test_parser.py Outdated

@mlubin mlubin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this! I'd prefer if the mps_data_model_t -> data_model_t rename was done in a separate PR to keep the review of the LP writer more focused.

/**
* @brief Writes the problem to an LP formatted file
*
* Emits the (algebraic, CPLEX/Gurobi style) LP format understood by read_lp().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Emits the (algebraic, CPLEX/Gurobi style) LP format understood by read_lp().
* Emits the LP format understood by read_lp().

Comment thread cpp/src/io/lp_writer.cpp Outdated
const auto& quadratic_constraints = problem_.get_quadratic_constraints();

i_t n_variables = 0;
auto grow = [&](size_t s) { n_variables = std::max(n_variables, static_cast<i_t>(s)); };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please drop these static_casts, they're not needed. Also it would be good to know why the agents weren't influenced by #1505.

Comment thread cpp/src/io/lp_writer.cpp Outdated
auto grow = [&](size_t s) { n_variables = std::max(n_variables, static_cast<i_t>(s)); };
grow(c_span.size());
grow(lb_span.size());
grow(ub_span.size());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of maneuvering here to accept arrays of different lengths. What if we assert that the arrays all have the same lengths to simplify the code.

Comment thread cpp/src/io/lp_writer.cpp Outdated
const std::vector<std::pair<i_t, f_t>>& row,
const char* rel,
f_t rhs) {
lp_file << " " << name << ":";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Row names are not required, so if we don't have one we shouldn't print one.

/* clang-format on */

/**
* Round-trip tests for the LP writer. Each test parses LP text into a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's ok to have a couple round-trip tests but this shouldn't be the primary way we test the writer. We should have a range of input problems where we test the exact strings outputted by the writer, not equivalence. The input problems themselves could be specified in LP format just for convenience.

)


def test_write_lp_round_trip():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a round-trip test, construct a problem in the python interface and check the precise string that is written.

@chris-maes

Copy link
Copy Markdown
Contributor

Thanks for doing this! I'd prefer if the mps_data_model_t -> data_model_t rename was done in a separate PR to keep the review of the LP writer more focused.

It might also be useful to consider other names over data_model_t. We have a separate effort to try and consolidate the problem representation inside the solver. Perhaps data_model_t should become optimization_problem_t.

@mlubin

mlubin commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Yes, or we could leave mps_data_model_t as is until we make a decision on how to consolidate the problem representations.

root added 2 commits July 16, 2026 23:33
Implement lp_writer_t and write_lp(), expose writeLP from Python
DataModel/Problem with round-trip tests, and extend cuOptWriteProblem
to support CUOPT_FILE_FORMAT_LP via write_to_file().
Add file_format_from_path() and use it for user_problem_file and
presolve_file so .lp paths emit LP and other extensions emit MPS.
Document the behavior and add a Python e2e test.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py (1)

780-809: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert structural round-trip equivalence.

The test re-reads the LP but only checks section markers and one objective value. It can pass if constraints, bounds, names, or coefficients are lost without changing Afiro’s optimum. Compare the original and reread models, reusing the equivalence helper from python/cuopt/cuopt/tests/linear_programming/test_parser.py if appropriate.

As per path instructions, round-trip tests should validate numerical and structural equivalence, not just successful re-solving.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py` around lines
780 - 809, Strengthen test_write_files_lp_extension to compare the original
data_model_obj with the reread afiro model for structural and numerical
equivalence, reusing the equivalence helper from test_parser.py if available.
Retain the LP-format section-marker checks, but do not rely solely on the
objective value and successful re-solving.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@cpp/include/cuopt/mathematical_optimization/optimization_problem_interface.hpp`:
- Around line 35-48: The output path dispatch in file_format_from_path must not
advertise .lp.gz or .lp.bz2 unless compression is implemented: update
cpp/include/cuopt/mathematical_optimization/optimization_problem_interface.hpp
lines 35-48 accordingly. In cpp/src/io/lp_writer.cpp lines 134-141, use
compression-aware output for any retained compressed formats. Update
docs/cuopt/source/convex-settings.rst line 51 and
docs/cuopt/source/mip-settings.rst line 51 to list only formats the LP writer
actually supports.

In `@cpp/src/io/lp_writer.cpp`:
- Around line 197-201: Update the var_name fallback generation in the LP writer
to reserve all non-empty explicit variable names before creating placeholders,
then choose unused generated names while preserving variable indices across
transformations. Add a round-trip regression test covering partial names such as
{"C1", ""} and verify distinct variables remain distinct after serialization and
parsing.
- Around line 376-380: Update the ranged-constraint branch in the LP writer,
specifically the logic around write_linear_row, to serialize finite lower and
upper bounds as one logical row using parser-supported ranged syntax or another
reversible representation. Preserve the original row name and constraint count
during round trips, and add a regression test that validates structural
equivalence after serialization and parsing.
- Around line 271-338: Apply problem_.get_objective_scaling_factor() to every
objective component serialized in the objective-writing block: scale linear
coefficients passed to emit_term, the objective offset emitted directly, and
quadratic coefficients including the off-diagonal factor. Preserve objective
sense, and add round-trip coverage for non-unit and negative scaling factors
that verifies objective sense and offset equivalence.

In `@python/cuopt/cuopt/linear_programming/problem.py`:
- Around line 2056-2057: The quadratic-constraint conversion in _to_data_model
must pass the keyword expected by DataModel.add_quadratic_constraint(),
replacing the incompatible sense argument with constraint_row_type so writeLP()
succeeds. Add a regression test covering writeLP() for a model containing a
quadratic constraint.
- Around line 2049-2058: Update the public Problem.writeLP method signature to
type lp_file as str and the return type as None. Expand its docstring with
parameter documentation describing the output path, a None return description,
and raises documentation stating that underlying file-writing failures
propagate.

---

Nitpick comments:
In `@python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py`:
- Around line 780-809: Strengthen test_write_files_lp_extension to compare the
original data_model_obj with the reread afiro model for structural and numerical
equivalence, reusing the equivalence helper from test_parser.py if available.
Retain the LP-format section-marker checks, but do not rely solely on the
objective value and successful re-solving.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9ce3db6e-dd02-4417-a965-7a0e185e50b8

📥 Commits

Reviewing files that changed from the base of the PR and between 10d1d5c and eacde95.

📒 Files selected for processing (27)
  • cpp/include/cuopt/mathematical_optimization/constants.h
  • cpp/include/cuopt/mathematical_optimization/cpu_optimization_problem.hpp
  • cpp/include/cuopt/mathematical_optimization/cuopt_c.h
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem_interface.hpp
  • cpp/src/io/CMakeLists.txt
  • cpp/src/io/lp_writer.cpp
  • cpp/src/io/writer.cpp
  • cpp/src/mip_heuristics/solve.cu
  • cpp/src/pdlp/cpu_optimization_problem.cpp
  • cpp/src/pdlp/cuopt_c.cpp
  • cpp/src/pdlp/optimization_problem.cu
  • cpp/src/pdlp/solve.cu
  • cpp/tests/linear_programming/CMakeLists.txt
  • cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp
  • cpp/tests/linear_programming/lp_writer_test.cpp
  • cpp/tests/qp/unit_tests/mps_writer_test.cpp
  • docs/cuopt/source/convex-settings.rst
  • docs/cuopt/source/mip-settings.rst
  • python/cuopt/cuopt/linear_programming/data_model/data_model.pxd
  • python/cuopt/cuopt/linear_programming/data_model/data_model.py
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py
  • python/cuopt/cuopt/tests/linear_programming/test_parser.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • python/cuopt/cuopt/linear_programming/data_model/data_model.pxd
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp
  • cpp/src/io/writer.cpp
  • python/cuopt/cuopt/tests/linear_programming/test_parser.py
  • python/cuopt/cuopt/linear_programming/data_model/data_model.py

Comment thread cpp/include/cuopt/mathematical_optimization/optimization_problem_interface.hpp Outdated
Comment thread cpp/src/io/lp_writer.cpp
Comment on lines +197 to +201
auto var_name = [&](i_t j) -> std::string {
if (static_cast<size_t>(j) < var_names_ref.size() && !var_names_ref[j].empty())
return var_names_ref[j];
return "C" + std::to_string(j);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Generate collision-free fallback variable names.

A partial name list such as {"C1", ""} serializes both variables as C1, causing the parser to merge distinct indices. Reserve explicit names before generating placeholders and add a round-trip regression test.

As per path instructions, double-check variable index spaces across transformations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/io/lp_writer.cpp` around lines 197 - 201, Update the var_name
fallback generation in the LP writer to reserve all non-empty explicit variable
names before creating placeholders, then choose unused generated names while
preserving variable indices across transformations. Add a round-trip regression
test covering partial names such as {"C1", ""} and verify distinct variables
remain distinct after serialization and parsing.

Source: Path instructions

Comment thread cpp/src/io/lp_writer.cpp
Comment on lines +271 to +338
// --- Objective ----------------------------------------------------------
lp_file << (problem_.get_sense() ? "Maximize\n" : "Minimize\n");
{
std::string obj_name =
problem_.get_objective_name().empty() ? "obj" : problem_.get_objective_name();
lp_file << " " << obj_name << ":";

int terms_on_line = 0;
for (i_t j = 0; j < n_variables; ++j) {
if (c[j] != f_t(0)) { emit_term(c[j], var_name(j), terms_on_line); }
}
// A constant objective term is written directly; read_lp folds it into
// the objective offset.
const f_t offset = problem_.get_objective_offset();
if (std::isfinite(offset) && offset != f_t(0)) {
lp_file << (offset < f_t(0) ? " - " : " + ") << fmt(std::abs(offset));
}

// Quadratic objective: build the symmetric Hessian H = Q + Q^T (matching
// the MPS writer), then emit its upper triangle inside a '[ ... ] / 2'
// block. In the LP objective convention a bracket coefficient p on a term
// contributes 0.5*p to the objective, so for H = Q + Q^T the diagonal
// coefficient is H[i][i] and the off-diagonal coefficient is 2*H[i][j].
if (problem_.has_quadratic_objective()) {
auto Qv = problem_.get_quadratic_objective_values();
auto Qi = problem_.get_quadratic_objective_indices();
auto Qo = problem_.get_quadratic_objective_offsets();
std::vector<f_t> Q_values(Qv.begin(), Qv.end());
std::vector<i_t> Q_indices(Qi.begin(), Qi.end());
std::vector<i_t> Q_offsets(Qo.begin(), Qo.end());

std::vector<f_t> H_values;
std::vector<i_t> H_indices;
std::vector<i_t> H_offsets;
if (problem_.is_Q_symmetrized()) {
H_values = std::move(Q_values);
H_indices = std::move(Q_indices);
H_offsets = std::move(Q_offsets);
} else {
cuopt::symmetrize_csr<i_t, f_t>(
Q_values, Q_indices, Q_offsets, H_values, H_indices, H_offsets);
}

// Collect the upper-triangular entries first so we only open the bracket
// when there is at least one nonzero quadratic term.
const i_t n_rows = static_cast<i_t>(H_offsets.size()) > 0
? static_cast<i_t>(H_offsets.size()) - 1
: 0;
std::vector<std::tuple<i_t, i_t, f_t>> upper;
for (i_t i = 0; i < n_rows; ++i) {
for (i_t p = H_offsets[i]; p < H_offsets[i + 1]; ++p) {
const i_t j = H_indices[p];
const f_t v = H_values[p];
if (i <= j && v != f_t(0)) { upper.emplace_back(i, j, v); }
}
}
if (!upper.empty()) {
lp_file << " + [";
int quad_terms = 0;
for (const auto& [i, j, v] : upper) {
if (i == j) {
emit_term(v, var_name(i) + " ^ 2", quad_terms);
} else {
emit_term(f_t(2) * v, var_name(i) + " * " + var_name(j), quad_terms);
}
}
lp_file << " ] / 2";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Apply objective_scaling_factor to every serialized objective term.

The writer emits raw linear coefficients, offset, and quadratic coefficients, so any model with a scaling factor other than 1 changes objective after round-trip. Scale all three components and add coverage for non-unit and negative factors.

As per path instructions, round-trip tests must validate objective sense and offset equivalence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/io/lp_writer.cpp` around lines 271 - 338, Apply
problem_.get_objective_scaling_factor() to every objective component serialized
in the objective-writing block: scale linear coefficients passed to emit_term,
the objective offset emitted directly, and quadratic coefficients including the
off-diagonal factor. Preserve objective sense, and add round-trip coverage for
non-unit and negative scaling factors that verifies objective sense and offset
equivalence.

Source: Path instructions

Comment thread cpp/src/io/lp_writer.cpp Outdated
Comment on lines +376 to +380
} else if (!std::isinf(lo) && !std::isinf(hi)) {
// Range row: the LP format cannot express two finite bounds on a single
// line, so split it into a '>=' row and a '<=' row.
write_linear_row(row_name(k) + "_lo", row, ">=", lo);
write_linear_row(row_name(k) + "_up", row, "<=", hi);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve ranged constraints as one logical row.

Splitting one finite-bounded row into <name>_lo and <name>_up changes the constraint count and row names, so the serialized model fails structural round-trip equivalence. Use parser-supported ranged syntax or another reversible representation, with a regression test.

As per path instructions, round-trip tests must validate structural equivalence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/io/lp_writer.cpp` around lines 376 - 380, Update the
ranged-constraint branch in the LP writer, specifically the logic around
write_linear_row, to serialize finite lower and upper bounds as one logical row
using parser-supported ranged syntax or another reversible representation.
Preserve the original row name and constraint count during round trips, and add
a regression test that validates structural equivalence after serialization and
parsing.

Source: Path instructions

Comment thread python/cuopt/cuopt/linear_programming/problem.py Outdated
Comment on lines +2056 to +2057
if self.model is None:
self._to_data_model()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix quadratic-constraint conversion before delegating.

When self.model is None, Line 2056 calls _to_data_model(). For quadratic constraints, that method passes sense=constr.Sense, but DataModel.add_quadratic_constraint() in python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx accepts constraint_row_type, so writeLP() raises TypeError instead of writing the model. Use the matching keyword and add a quadratic-constraint regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cuopt/cuopt/linear_programming/problem.py` around lines 2056 - 2057,
The quadratic-constraint conversion in _to_data_model must pass the keyword
expected by DataModel.add_quadratic_constraint(), replacing the incompatible
sense argument with constraint_row_type so writeLP() succeeds. Add a regression
test covering writeLP() for a model containing a quadratic constraint.

@ramakrishnap-nv ramakrishnap-nv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the major comments are already raised by coderabbit, it would good to resolve those.

root added 2 commits July 18, 2026 02:39
Preserve unnamed constraints and validate model dimensions while making exact output the primary test contract. Add extension-based Python writes and deprecate the MPS-specific entry point.
Reject unsupported compressed output paths, clarify the Python write API, and initialize the default optimization sense.

Signed-off-by: root <root@ipp1-3302.aselab.nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.lp_harness:
- Around line 1-2: Remove the committed ELF executable .lp_harness from version
control and add .lp_harness to .gitignore so the build artifact generated from
.lp_harness.cpp is excluded from future commits.

In @.lp_harness.cpp:
- Around line 21-26: Update the round function’s output path construction to
avoid the hard-coded developer-local /home/nfs/iroy/lp_writer/ directory; use a
portable temporary path mechanism, or remove this ad-hoc harness and rely on the
existing gtest coverage.
- Line 14: Remove the file-scope using-directive for
cuopt::mathematical_optimization::io in .lp_harness.cpp, and qualify each
affected cuOpt I/O symbol explicitly or replace it with narrow, symbol-specific
aliases.

In `@python/cuopt/cuopt/linear_programming/data_model/data_model.py`:
- Around line 805-809: Update the deprecation messages in DataModel.writeMPS
(python/cuopt/cuopt/linear_programming/data_model/data_model.py, lines 805-809)
and Problem.writeMPS (python/cuopt/cuopt/linear_programming/problem.py, lines
2063-2067) to replace “future release” with the same concrete planned cuOpt
removal version in both warnings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ea31907a-bb6d-485d-a260-579a4ac7e013

📥 Commits

Reviewing files that changed from the base of the PR and between eacde95 and 34b57c4.

📒 Files selected for processing (17)
  • .lp_harness
  • .lp_harness.cpp
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp
  • cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem_interface.hpp
  • cpp/src/io/lp_writer.cpp
  • cpp/tests/linear_programming/lp_writer_test.cpp
  • cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu
  • docs/cuopt/source/convex-settings.rst
  • docs/cuopt/source/mip-settings.rst
  • python/cuopt/cuopt/linear_programming/data_model/data_model.py
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • python/cuopt/cuopt/linear_programming/io/__init__.py
  • python/cuopt/cuopt/linear_programming/io/format.py
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_parser.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • docs/cuopt/source/mip-settings.rst
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.lp_harness:
- Around line 1-2: Remove the committed ELF executable .lp_harness from version
control and add .lp_harness to .gitignore so the build artifact generated from
.lp_harness.cpp is excluded from future commits.

In @.lp_harness.cpp:
- Around line 21-26: Update the round function’s output path construction to
avoid the hard-coded developer-local /home/nfs/iroy/lp_writer/ directory; use a
portable temporary path mechanism, or remove this ad-hoc harness and rely on the
existing gtest coverage.
- Line 14: Remove the file-scope using-directive for
cuopt::mathematical_optimization::io in .lp_harness.cpp, and qualify each
affected cuOpt I/O symbol explicitly or replace it with narrow, symbol-specific
aliases.

In `@python/cuopt/cuopt/linear_programming/data_model/data_model.py`:
- Around line 805-809: Update the deprecation messages in DataModel.writeMPS
(python/cuopt/cuopt/linear_programming/data_model/data_model.py, lines 805-809)
and Problem.writeMPS (python/cuopt/cuopt/linear_programming/problem.py, lines
2063-2067) to replace “future release” with the same concrete planned cuOpt
removal version in both warnings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ea31907a-bb6d-485d-a260-579a4ac7e013

📥 Commits

Reviewing files that changed from the base of the PR and between eacde95 and 34b57c4.

📒 Files selected for processing (17)
  • .lp_harness
  • .lp_harness.cpp
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp
  • cpp/include/cuopt/mathematical_optimization/io/mps_data_model.hpp
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • cpp/include/cuopt/mathematical_optimization/optimization_problem_interface.hpp
  • cpp/src/io/lp_writer.cpp
  • cpp/tests/linear_programming/lp_writer_test.cpp
  • cpp/tests/linear_programming/unit_tests/optimization_problem_test.cu
  • docs/cuopt/source/convex-settings.rst
  • docs/cuopt/source/mip-settings.rst
  • python/cuopt/cuopt/linear_programming/data_model/data_model.py
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • python/cuopt/cuopt/linear_programming/io/__init__.py
  • python/cuopt/cuopt/linear_programming/io/format.py
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_parser.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • cpp/include/cuopt/mathematical_optimization/io/writer.hpp
  • docs/cuopt/source/mip-settings.rst
  • python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx
  • cpp/include/cuopt/mathematical_optimization/io/lp_writer.hpp
🛑 Comments failed to post (4)
.lp_harness (1)

1-2: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Compiled binary executable committed to the repository.

This file is not source code — it is a raw ELF binary (executable machine code, symbol tables, section headers) dumped as text. This looks like a build artifact from .lp_harness.cpp (referenced in the PR's test layer) that was accidentally committed instead of being excluded via .gitignore.

Committing binaries is problematic: it bloats the git history irreversibly, cannot be code-reviewed, and introduces an unreviewed executable into version control. Please remove this file from the commit and add the corresponding build output path (e.g. .lp_harness) to .gitignore.

🛠️ Suggested fix
- (remove binary file `.lp_harness` from the commit)
+ echo ".lp_harness" >> .gitignore
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.lp_harness around lines 1 - 2, Remove the committed ELF executable
.lp_harness from version control and add .lp_harness to .gitignore so the build
artifact generated from .lp_harness.cpp is excluded from future commits.
.lp_harness.cpp (2)

14-14: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the file-scope using namespace.

Qualify the cuOpt I/O symbols or use narrow aliases instead. As per coding guidelines: “Do not use using namespace at file scope.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.lp_harness.cpp at line 14, Remove the file-scope using-directive for
cuopt::mathematical_optimization::io in .lp_harness.cpp, and qualify each
affected cuOpt I/O symbol explicitly or replace it with narrow, symbol-specific
aliases.

Source: Coding guidelines


21-26: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not write to a developer-local absolute path.

/home/nfs/iroy/lp_writer/ will not exist in CI or other developer environments, so this harness cannot run portably. Use a temporary path or remove this ad-hoc harness in favor of the gtest coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.lp_harness.cpp around lines 21 - 26, Update the round function’s output
path construction to avoid the hard-coded developer-local
/home/nfs/iroy/lp_writer/ directory; use a portable temporary path mechanism, or
remove this ad-hoc harness and rely on the existing gtest coverage.
python/cuopt/cuopt/linear_programming/data_model/data_model.py (1)

805-809: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Declare a concrete removal version in both deprecation warnings.

“Future release” does not give callers a migration deadline. State the planned cuOpt removal version consistently.

  • python/cuopt/cuopt/linear_programming/data_model/data_model.py#L805-L809: add the removal version to DataModel.writeMPS.
  • python/cuopt/cuopt/linear_programming/problem.py#L2063-L2067: add the same removal version to Problem.writeMPS.

As per coding guidelines: “When changing a public Python API signature, emit a DeprecationWarning with a removal version before breaking the old signature.”

📍 Affects 2 files
  • python/cuopt/cuopt/linear_programming/data_model/data_model.py#L805-L809 (this comment)
  • python/cuopt/cuopt/linear_programming/problem.py#L2063-L2067
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cuopt/cuopt/linear_programming/data_model/data_model.py` around lines
805 - 809, Update the deprecation messages in DataModel.writeMPS
(python/cuopt/cuopt/linear_programming/data_model/data_model.py, lines 805-809)
and Problem.writeMPS (python/cuopt/cuopt/linear_programming/problem.py, lines
2063-2067) to replace “future release” with the same concrete planned cuOpt
removal version in both warnings.

Sources: Coding guidelines, Path instructions

@chris-maes chris-maes modified the milestone: 26.08 Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants