Skip to content

Add algebraic __str__ and detailed __repr__ to Python LP API classes - #1400

Open
jackthepunished wants to merge 3 commits into
NVIDIA:mainfrom
jackthepunished:feature/str-repr-for-lp-api
Open

Add algebraic __str__ and detailed __repr__ to Python LP API classes#1400
jackthepunished wants to merge 3 commits into
NVIDIA:mainfrom
jackthepunished:feature/str-repr-for-lp-api

Conversation

@jackthepunished

@jackthepunished jackthepunished commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

The Python LP modeling classes (Variable, LinearExpression, QuadraticExpression, Constraint, Problem) currently fall back to <cuopt.linear_programming.problem.X object at 0x...> when printed, which makes model construction hard to verify in notebooks and REPLs. This adds str (algebraic form, e.g. 2.0 * x + 3.0 * y <= 10.0) and repr (detailed summary with bounds, type, and variable/constraint counts and solve status) to all five classes. Purely additive; covered by est_str_and_repr in est_python_API.py.

Adds __str__ and __repr__ to Variable, LinearExpression,

QuadraticExpression, Constraint, and Problem. Printing these objects now

shows their algebraic form (e.g. '2.0 * x + 3.0 * y <= 10.0') and the REPL

shows a detailed summary, improving debuggability in notebooks and

interactive sessions. The change is purely additive.

Signed-off-by: jackthepunished <kosapinarbahadir@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 6, 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.

@jackthepunished
jackthepunished marked this pull request as ready for review June 6, 2026 00:36
@jackthepunished
jackthepunished requested a review from a team as a code owner June 6, 2026 00:36
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds readable __str__ and __repr__ output for LP variables, expressions, constraints, and problems. It adds shared formatting rules, quadratic variable tracking, model summaries, and tests for exact output and truncation.

Changes

Human-readable display support for LP API objects

Layer / File(s) Summary
Expression formatting infrastructure
python/cuopt/cuopt/linear_programming/problem.py
Adds constraint sense symbols, variable display names, coefficient simplification, quadratic term ordering, zero omission, and bounded expression formatting.
Expression and constraint display
python/cuopt/cuopt/linear_programming/problem.py
Adds string and representation output for variables, linear expressions, quadratic expressions, and constraints. Quadratic constraints retain a deduplicated list of participating variables.
Problem summary display
python/cuopt/cuopt/linear_programming/problem.py
Adds compact and multiline problem summaries with model structure, constraint counts, nonzero counts, solved status, and objective value.
Display formatting tests
python/cuopt/cuopt/tests/linear_programming/test_python_API.py
Tests exact output for LP objects, normalized and updated constraints, unnamed and solved problems, empty expressions, and truncation at the display-term limit.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: ramakrishnap-nv, copilot, iroy30

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.48% 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 and concisely describes the added string and representation methods for Python LP API classes.
Description check ✅ Passed The description accurately explains the new formatting behavior, affected classes, and test coverage.
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: 1

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/tests/linear_programming/test_python_API.py (1)

14-25: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add missing LinearExpression import to prevent NameError in the new test.

LinearExpression is referenced at Line 873, Line 875, and Line 876 but is not imported, so this test will fail at runtime.

Proposed fix
 from cuopt.linear_programming.problem import (
     CONTINUOUS,
     INTEGER,
     MAXIMIZE,
     MINIMIZE,
     SEMI_CONTINUOUS,
     CType,
+    LinearExpression,
     Problem,
     VType,
     sense,
     QuadraticExpression,
 )
As per coding guidelines, “Run pre-commit hooks before committing code to enforce code linters and formatters”; Ruff’s F821 here indicates a correctness break that should be fixed before merge.
🤖 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_python_API.py` around lines
14 - 25, The test imports from cuopt.linear_programming.problem but omits
LinearExpression, causing NameError in tests referencing LinearExpression;
update the import list in test_python_API.py to include LinearExpression (i.e.,
add LinearExpression to the grouped import alongside CONTINUOUS, INTEGER,
MAXIMIZE, MINIMIZE, SEMI_CONTINUOUS, CType, Problem, VType, sense,
QuadraticExpression) so the symbol is available where referenced.

Sources: Coding guidelines, Linters/SAST tools

🤖 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 `@python/cuopt/cuopt/linear_programming/problem.py`:
- Line 21: The _SENSE_SYMBOLS dictionary is created before the constants LE, GE,
and EQ are defined, causing an import-time NameError; fix by deferring its
construction until after those constants are declared (or by keying it with the
raw numeric/char codes used for LE/GE/EQ instead of the names), i.e., move or
rebuild _SENSE_SYMBOLS after the definitions of LE, GE, EQ (or replace keys with
the literal codes) so references in _SENSE_SYMBOLS resolve correctly.

---

Outside diff comments:
In `@python/cuopt/cuopt/tests/linear_programming/test_python_API.py`:
- Around line 14-25: The test imports from cuopt.linear_programming.problem but
omits LinearExpression, causing NameError in tests referencing LinearExpression;
update the import list in test_python_API.py to include LinearExpression (i.e.,
add LinearExpression to the grouped import alongside CONTINUOUS, INTEGER,
MAXIMIZE, MINIMIZE, SEMI_CONTINUOUS, CType, Problem, VType, sense,
QuadraticExpression) so the symbol is available where referenced.
🪄 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: 84099242-4510-46e2-b7dc-41322ef3afa6

📥 Commits

Reviewing files that changed from the base of the PR and between 2384454 and 3a01e57.

📒 Files selected for processing (2)
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_python_API.py

Comment thread python/cuopt/cuopt/linear_programming/problem.py Outdated
@chris-maes

Copy link
Copy Markdown
Contributor

This is interesting. Could you provide some example output, showing what strings this would compute, on a few different problems?

@jackthepunished

jackthepunished commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Here are some examples of what you'd see when printing or repr'ing objects after this change. On a small MILP with constraints like 2x + 3y <= 10, x - y >= 0, and x + 1 == 5, you get readable algebra instead of memory addresses: str(c1) gives 2.0 * x + 3.0 * y <= 10.0, str(c2) gives x - y >= 0.0, and str(c3) gives x + 1.0 == 5.0 (the last one keeps the constant on the LHS as the user wrote it, not the normalized internal form). Variables print as their name (str(x) → x) or as C{index} when unnamed (str(z) → C2), and repr(x) gives something like <cuopt.Variable 'x' (index=0), type=CONTINUOUS, bounds=[0.0, 10.0], value=nan>. For a QP, quadratic terms format naturally: str(xx) → x^2, str(xx + 2xy + 3x) → x^2 + 2.0 * x * y + 3.0 * x, and mixed-sign expressions like -xx + 0.5yy + x*y → -x^2 + 0.5 * y^2 + x * y. On the problem itself, str(prob) is a short summary with the problem name, objective sense, variable/constraint counts, non-zero count, and after solve the status and objective value, while repr(prob) stays compact, e.g. <cuopt.Problem 'str_repr_test' (3 vars, 3 constrs, IsMIP=True)>.

@jackthepunished

Copy link
Copy Markdown
Contributor Author

@chris-maes can you /review

@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator

@chris-maes May I get your review on this PR

@mlubin
mlubin requested review from mlubin and removed request for chris-maes June 25, 2026 19:46
@mlubin

mlubin commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

I think this would be a nice addition. My main concern on the behavior side is there's no truncation in the case of large expressions. I doubt users want to see a linear or quadratic expression with 10,000 terms in it. If we can work out the right behavior in this case and validate it in the both REPL and notebooks then we'll be on track to merging this.

@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.

Requesting the changes I mentioned above.

Linear and quadratic expressions now render only the first
_MAX_DISPLAY_TERMS (10) terms followed by a "... (N more terms)" marker,
so printing a model with thousands of terms stays readable in a REPL or
notebook instead of flooding the output. Applies to both __str__ and
__repr__ (and therefore Constraint, whose LHS is the expression); the
cap is a module constant and can be set to None to disable.

Also fix an import-time NameError: the module-level _SENSE_SYMBOLS table
referenced LE/GE/EQ before they were defined. Re-key it by the
underlying CType char codes ("L"/"G"/"E"), which CType members compare
and hash equal to, so the module imports regardless of definition order.

Add test_str_truncation_large_expression covering the linear/quadratic
head + marker, the exactly-at-cap (no marker) and one-over (singular)
boundaries, and the truncation-disabled case.

Signed-off-by: jackthepunished <kosapinarbahadir@gmail.com>
@jackthepunished
jackthepunished force-pushed the feature/str-repr-for-lp-api branch from aca0220 to cd6c5ca Compare June 26, 2026 23:52

@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: 1

🤖 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 `@python/cuopt/cuopt/tests/linear_programming/test_python_API.py`:
- Around line 991-998: The quadratic truncation test is too loose because it
only asserts an upper bound on the rendered term count, so regressions that
display fewer than the intended display limit could still pass. Tighten the
assertions in the QuadraticExpression string/representation test to match the
same exact truncation boundary used by the linear expression branch, using
qexpr/qs and the existing _MAX_DISPLAY_TERMS behavior to verify the precise
number of displayed quadratic terms and the expected ellipsis suffix.
🪄 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: 99a0419f-4f28-4154-833f-85c8de41d7dc

📥 Commits

Reviewing files that changed from the base of the PR and between 3a01e57 and aca0220.

📒 Files selected for processing (2)
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_python_API.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • python/cuopt/cuopt/linear_programming/problem.py

Comment thread python/cuopt/cuopt/tests/linear_programming/test_python_API.py
@mlubin mlubin added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Jun 26, 2026
@jackthepunished
jackthepunished requested a review from mlubin June 27, 2026 20:30
name = var.VariableName
if name:
return name
if getattr(var, "index", -1) >= 0:

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.

Why getattr instead of var.getVariableIndex()? When would the attribute not exist? And when would it be -1?

@@ -16,6 +16,133 @@
import warnings


# ---- Display helpers for __str__/__repr__ ----

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.

The location of this code isn't very natural. Why are we defining the printing helpers before VType, CType, Variable, etc?

# string. Using the codes (rather than the LE/GE/EQ aliases) also keeps this
# module-level table independent of definition order.
_SENSE_SYMBOLS = {"L": "<=", "G": ">=", "E": "=="}
_TYPE_NAMES = {"C": "CONTINUOUS", "I": "INTEGER", "S": "SEMI_CONTINUOUS"}

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.

Use VType().name rather than hardcoding this mapping.

# Keyed by the underlying CType char codes ("L"/"G"/"E"). CType is a
# ``(str, Enum)`` whose members compare and hash equal to these codes, so the
# lookup works whether ``Constraint.Sense`` holds a CType member or a raw
# string. Using the codes (rather than the LE/GE/EQ aliases) also keeps this

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.

Could this mapping be a member of the CType enum?


def __repr__(self):
name = _var_display_name(self)
idx = getattr(self, "index", -1)

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.

Again why getattr?

@@ -1322,6 +1492,7 @@ def __init__(self, expr, sense, rhs, name=""):
self.ConstraintName = name
self.DualValue = float("nan")
self.Slack = float("nan")
self._expr = expr

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.

Seems like we're potentially doubling the model's memory usage by storing an extra copy of the constraint data here. That's not ideal.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🔔 Hi @anandhkb, this pull request has had no activity for 7 days. Please update or let us know if it can be closed. Thank you!

If this is an "epic" issue, then please add the "epic" label to this issue.
If it is a PR and not ready for review, then please convert this to draft.
If you just want to switch off this notification, then use the "skip inactivity reminder" label.

1 similar comment
@github-actions

Copy link
Copy Markdown

🔔 Hi @anandhkb, this pull request has had no activity for 7 days. Please update or let us know if it can be closed. Thank you!

If this is an "epic" issue, then please add the "epic" label to this issue.
If it is a PR and not ready for review, then please convert this to draft.
If you just want to switch off this notification, then use the "skip inactivity reminder" label.

@github-actions

Copy link
Copy Markdown

🔔 Hi @anandhkb @mlubin, this pull request has had no activity for 7 days. Please update or let us know if it can be closed. Thank you!

If this is an "epic" issue, then please add the "epic" label to this issue.
If it is a PR and not ready for review, then please convert this to draft.
If you just want to switch off this notification, then use the "skip inactivity reminder" label.

@mlubin

mlubin commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@jackthepunished do you plan to follow up on this PR?

@jackthepunished

Copy link
Copy Markdown
Contributor Author

Firstly I'm sorry to leave this PR without notice, but I was a companion for a family member at hospital so I couldn't give any attention to here, i'll get onto it asap.

- Replace _SENSE_SYMBOLS with a CType.symbol property and _TYPE_NAMES
  with VType(...).name lookups.
- Fold _var_display_name into Variable.__str__ and move the remaining
  display helpers (_MAX_DISPLAY_TERMS, _ExprBuilder, _format_linear)
  next to the expression classes that use them.
- Use direct .index access instead of getattr; the attribute is always
  set in Variable.__init__ (-1 until addVariable assigns it).
- Stop storing the expression on Constraint; __str__ now renders lazily
  from the solver data the constraint already holds, so constraints
  print in normalized form (duplicates merged, constants folded into
  the RHS) and stay in sync after updateConstraint. Quadratic rows now
  record all participating variables so QCMATRIX indices map to names.
- Tests: add missing LinearExpression import, tighten the quadratic
  truncation assertion, and cover quadratic/duplicate/updateConstraint
  constraint display.
Copilot AI review requested due to automatic review settings August 1, 2026 13:41

@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: 1

🤖 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 `@python/cuopt/cuopt/linear_programming/problem.py`:
- Around line 51-55: Update the CType.symbol property in
python/cuopt/cuopt/linear_programming/problem.py at lines 51-55 to add a str
return annotation and document that it returns the algebraic constraint symbol;
apply the same annotation/documentation change to the sibling display methods at
lines 343-359 and 1559-1579 as requested.
🪄 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: 07d9fb5e-241f-42f7-bfc6-66c3ff95c680

📥 Commits

Reviewing files that changed from the base of the PR and between cd6c5ca and 26fc820.

📒 Files selected for processing (2)
  • python/cuopt/cuopt/linear_programming/problem.py
  • python/cuopt/cuopt/tests/linear_programming/test_python_API.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • python/cuopt/cuopt/tests/linear_programming/test_python_API.py

Comment on lines +51 to +55
@property
def symbol(self):
"""Algebraic symbol used when printing constraints."""
return {CType.LE: "<=", CType.GE: ">=", CType.EQ: "=="}[self]

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -A4 -B2 \
  'def symbol|def __str__|def __repr__' \
  python/cuopt/cuopt/linear_programming/problem.py

Repository: NVIDIA/cuopt

Length of output: 2928


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=python/cuopt/cuopt/linear_programming/problem.py

printf '%s\n' '--- targeted diff ---'
git diff --unified=12 -- "$file" | sed -n \
  '/CType/,/^[^+ -].*$/p' | head -n 220

printf '%s\n' '--- class and method definitions ---'
rg -n '^(class (CType|Variable|Constraint)|    def (symbol|__str__|__repr__))' "$file"

printf '%s\n' '--- surrounding source ---'
sed -n '35,65p;315,375p;1535,1590p' "$file"

Repository: NVIDIA/cuopt

Length of output: 5668


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import ast
from pathlib import Path

path = Path("python/cuopt/cuopt/linear_programming/problem.py")
tree = ast.parse(path.read_text())

wanted = {"CType", "Variable", "Constraint"}
for node in tree.body:
    if isinstance(node, ast.ClassDef) and node.name in wanted:
        print(f"class {node.name}:")
        for item in node.body:
            if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
                if item.name in {"symbol", "__str__", "__repr__"}:
                    returns = ast.unparse(item.returns) if item.returns else "<missing>"
                    doc = ast.get_docstring(item, clean=False)
                    print(
                        f"  {item.name}: return={returns}; "
                        f"docstring={'yes' if doc else 'no'}"
                    )
                    if doc:
                        print(f"    {doc!r}")
PY

Repository: NVIDIA/cuopt

Length of output: 415


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=python/cuopt/cuopt/linear_programming/problem.py

printf '%s\n' '--- repository state ---'
git status --short
git log -1 --oneline
git diff --stat
git show --stat --oneline HEAD

printf '%s\n' '--- commit-level changes in target file ---'
git show --format= --unified=3 HEAD -- "$file" | sed -n '1,260p'

printf '%s\n' '--- method blocks and immediate docstrings ---'
python3 - "$file" <<'PY'
from pathlib import Path
import re
import sys

text = Path(sys.argv[1]).read_text().splitlines()
targets = {"symbol", "__str__", "__repr__"}
class_name = None
for i, line in enumerate(text):
    class_match = re.match(r"class\s+([A-Za-z_]\w*)\b", line)
    if class_match:
        class_name = class_match.group(1)
    method_match = re.match(r"    def\s+([A-Za-z_]\w*)\s*\((.*?)\)\s*(?:->\s*([^:]+))?:", line)
    if method_match and method_match.group(1) in targets:
        name, args, ret = method_match.groups()
        print(f"{class_name}.{name}: return={ret or '<missing>'}, line={i + 1}")
        j = i + 1
        while j < len(text) and text[j].strip() == "":
            j += 1
        if j < len(text) and text[j].lstrip().startswith(('"""', "'''")):
            print(f"  docstring starts line {j + 1}: {text[j].strip()}")
        else:
            print("  docstring: <missing>")
PY

Repository: NVIDIA/cuopt

Length of output: 11182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=python/cuopt/cuopt/linear_programming/problem.py

printf '%s\n' '--- parent definitions ---'
git show HEAD^:"$file" | sed -n '35,65p;435,485p;1510,1570p'

printf '%s\n' '--- exact changed definitions ---'
git diff --unified=0 HEAD^ HEAD -- "$file" | rg -n -A18 -B4 \
  'symbol|def __str__|def __repr__|^@@'

printf '%s\n' '--- annotations and Returns sections in public APIs ---'
rg -n -U \
  '^[ ]{4}(def|async def) [A-Za-z_][A-Za-z0-9_]*\([^\\n]*\)( -> [^:]+)?:\\n|^[ ]{4}.*Returns?:' \
  "$file" | head -n 160

Repository: NVIDIA/cuopt

Length of output: 10805


Annotate the new CType.symbol property.

Add -> str and document the returned algebraic constraint symbol. The other display methods already existed.

📍 Affects 1 file
  • python/cuopt/cuopt/linear_programming/problem.py#L51-L55 (this comment)
  • python/cuopt/cuopt/linear_programming/problem.py#L343-L359
  • python/cuopt/cuopt/linear_programming/problem.py#L1559-L1579
🤖 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 51 - 55,
Update the CType.symbol property in
python/cuopt/cuopt/linear_programming/problem.py at lines 51-55 to add a str
return annotation and document that it returns the algebraic constraint symbol;
apply the same annotation/documentation change to the sibling display methods at
lines 343-359 and 1559-1579 as requested.

Sources: Coding guidelines, Path instructions

Copilot AI 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.

Pull request overview

This PR improves the usability of the Python Linear Programming (LP) modeling API in interactive contexts (notebooks/REPL) by adding algebraic __str__ and more descriptive __repr__ implementations for core modeling objects, and adds tests to lock in the expected formatting and truncation behavior.

Changes:

  • Add algebraic string formatting (__str__) and detailed object summaries (__repr__) for Variable, LinearExpression, QuadraticExpression, Constraint, and Problem.
  • Introduce shared expression formatting utilities (_ExprBuilder, _format_linear) and output truncation via _MAX_DISPLAY_TERMS.
  • Add test coverage validating formatting details and truncation behavior for large expressions.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
python/cuopt/cuopt/linear_programming/problem.py Implements __str__/__repr__ across LP modeling classes and adds shared formatting utilities and truncation logic.
python/cuopt/cuopt/tests/linear_programming/test_python_API.py Adds unit tests for the new string/representation behavior, including truncation for large expressions.

Comment on lines +409 to +416
v1_str = str(var1)
v2_str = str(var2)
if v1_str == v2_str:
term_str = f"{v1_str}^2"
elif v1_str <= v2_str:
term_str = f"{v1_str} * {v2_str}"
else:
term_str = f"{v2_str} * {v1_str}"
Comment on lines +1564 to +1574
index_to_var = {v.index: v for v in self.vars}
if self.is_quadratic:
for row, col, val in zip(self.rows, self.cols, self.vals):
builder.add_quadratic(
val, index_to_var[row], index_to_var[col]
)
for idx, val in zip(self.linear_indices, self.linear_values):
builder.add_linear(val, index_to_var[idx])
else:
for idx, coeff in self.vindex_coeff_dict.items():
builder.add_linear(coeff, index_to_var[idx])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants