Fix COPT interface silently dropping nonzero quadratic objective when Q has an all-zero column - #1529
Merged
johanlofberg merged 1 commit intoSep 15, 2026
Conversation
… all-zero column
The COPT interface used `if any(interfacedata.Q)` to decide whether to attach
the quadratic objective matrix. `interfacedata.Q` is a sparse square matrix,
and `any(Q)` on a matrix returns a logical row vector (per-column reduction),
not a scalar. MATLAB's `if` collapses a non-scalar with an implicit
`all(...(:))`, so the guard actually tests "every column has a nonzero
entry" rather than "Q has any nonzero entry."
When Q is nonzero but has at least one all-zero column — i.e., some variable
appears only linearly in the objective — the guard evaluates to false and
`model.Q` is never set, so the entire quadratic objective is silently
dropped. The model is solved as a linear program while YALMIP still reports
success, and the original YALMIP objective disagrees with the COPT-reported
objective at the returned point.
This is not an edge case in conic models: `interfacedata.Q` spans all
variables including cone-auxiliary columns, which never appear in the
quadratic objective and are therefore always all-zero. Hence any SOCP model
with a nonzero quadratic objective trips the same guard at both call sites,
even when the Q block on the original variables is full rank.
The same guard appears twice and both are changed:
- Line 133: the initial `model.Q = interfacedata.Q` assignment.
- Line 195: the dimension-extension `model.Q(n,n) = 0` performed after
appending cone/expcone variables. Skipping this would also leave
`model.Q` smaller than `ncol`, which COPT rejects (coptinit.c requires
problem.Q to be ncol-by-ncol).
The fix replaces the non-scalar `any(interfacedata.Q)` with the scalar
`nnz(interfacedata.Q)`, which is consistent with how the rest of YALMIP's
solver interfaces test for a nonzero quadratic objective (e.g.
yalmip2quadprog uses `nnz(Q)==0`, yalmip2cplex uses `nnz(H)==0`,
yalmip2gurobi assigns unconditionally, yalmip2mosek uses
`find(tril(sparse(2*Q)))`). `nnz` always returns a scalar, so the `if`
behaves correctly under all sparsity patterns.
Minimal example: min (x-3)^2 + y, 0<=x<=10, 0<=y<=1. Correct solution is
x=3, y=0, objective 0. Before the fix the quadratic term is dropped, COPT
returns x=10, y=0 with solver objective -51, while the original objective
evaluates to 49.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi, there is a suspected issue in COPT interface.
Problem
The COPT interface (
solvers/yalmip2copt.m) usesif anyto decide whether to attach the quadratic objective matrix:Minimal reproducer
Variable order [x; y] gives Q = diag([1, 0]) — the y column is all-zero.