Repository navigation
Description
Describe the bug
LinearExpression does not implement __neg__, __pos__, or __abs__, while Variable does support unary minus. Negation therefore works on a single variable but fails as soon as the operand is an expression:
-x # OK -> LinearExpression
-(2*x) # TypeError: bad operand type for unary -: 'LinearExpression'This asymmetry is the problem. -x working teaches the user that unary minus is supported, and the identical idiom then raises as soon as the expression has more than one term.
It also makes the documented QP maximization workaround inexpressible. cuOpt QP is minimize-only and the guidance is "negate and minimize", but the natural way to write that raises:
problem.setObjective(-(2*a), sense=MINIMIZE)
# TypeError: bad operand type for unary -: 'LinearExpression'Steps/Code to reproduce bug
from cuopt.linear_programming.problem import Problem, CONTINUOUS, LinearExpression
p = Problem("t")
x = p.addVariable(lb=0, ub=5, vtype=CONTINUOUS, name="x")
y = p.addVariable(lb=0, ub=5, vtype=CONTINUOUS, name="y")
e = 2*x + 3*y
le = LinearExpression([x, y], [2.0, 3.0], 0.0)
-x # OK -> LinearExpression
-e # FAIL TypeError: bad operand type for unary -: 'LinearExpression'
-le # FAIL TypeError: bad operand type for unary -: 'LinearExpression'
+e # FAIL TypeError: bad operand type for unary +: 'LinearExpression'
abs(e) # FAIL TypeError: bad operand type for abs(): 'LinearExpression'
0 - e # OK (workaround)
e * -1 # OK (workaround)
-1 * e # OK (workaround)
e - (x+y) # OK (binary __sub__ is implemented)Expected behavior
-expr returns a LinearExpression with negated coefficients and constant, consistent with -variable and with __sub__. PuLP, Gurobi (gurobipy), and Pyomo all support unary negation on expressions, so this is the idiom users arrive with. __pos__ and __abs__ are lower priority, but __neg__ and __pos__ are one-liners given __mul__ already exists.
Environment details
- Environment location: Bare-metal
- Method of cuOpt install: conda —
cuopt 26.08.00 cuda13_py312_260806_400863c1from thenvidiachannel, Python 3.12.14, CUDA 13.3, driver 590.48.01, Quadro RTX 8000
Additional context
Found while benchmarking LLM-generated cuOpt code (NL optimization problem → cuOpt model → execute → compare objective to gold) over 437 problems from the optimind benchmark set.
45 of 142 runtime errors (32%) from one model were this single missing operator — the largest single failure cause by a wide margin, ahead of every genuine modeling mistake. LLMs write -expr because it is idiomatic in every other Python modeling library, and because -x works in cuOpt.
This has an outsized effect on code generation specifically: a human hits the TypeError once, writes 0 - e, and moves on; a model generating single-shot code has no feedback loop and simply fails. Implementing __neg__ would remove roughly a third of the API-related failures we measured.
Activity
chris-maes commentedon Oct 6, 2026
We support maximization for QPs. We should remove any docs that suggest we dont.
We should support neg. We don't want to support abs (as this is not a linear expression---without additional variables and constraints). pos can be supported but seems lower priority.
fix(linear_programming): implement __neg__, __pos__, and __abs__ for …