From 87bedd9d385228d6384dc8f21e9f9aa1c9751f93 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Mon, 15 Jan 2024 13:59:50 +1100 Subject: [PATCH] Mosek, COPT: enable convex QCP with rotated SOC #229 #192 --- include/mp/flat/redef/conic/qcones2qc.h | 29 +++++++++++++++++-- .../categorized/fast/conic/modellist.json | 8 +++++ .../fast/conic/socp_11_qpobj__RSOC.mod | 8 +++++ test/end2end/scripts/python/Solver.py | 2 +- 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 test/end2end/cases/categorized/fast/conic/socp_11_qpobj__RSOC.mod diff --git a/include/mp/flat/redef/conic/qcones2qc.h b/include/mp/flat/redef/conic/qcones2qc.h index 5a0375a5a..029ea443d 100644 --- a/include/mp/flat/redef/conic/qcones2qc.h +++ b/include/mp/flat/redef/conic/qcones2qc.h @@ -39,7 +39,8 @@ class QConeConverter : GetMC().NarrowVarBounds(x[0], 0.0, GetMC().Infty()); auto qc {QuadConLE{ {{}, {c, x, x}}, {0.0} }}; GetMC().AddConstraint(std::move(qc)); - } else { // produce fixed RHS, better for Mosek + } else { + // Reproduce fixed RHS, better for Mosek & COPT. conic/socp_10 auto rhs = -c[0] * GetMC().fixed_value(x[0]); c.erase(c.begin()); auto x0 = x; @@ -85,8 +86,30 @@ class RQConeConverter : c12[0] *= -2.0*c[0]; for (auto i=c12.size(); --i; ) c12[i] *= c12[i]; - auto qc = QuadConLE{ {{}, {c12, x1, x2}}, {0.0} }; - GetMC().AddConstraint(std::move(qc)); + // Reproduce linear term, for Mosek & COPT. conic/socp_11. + if (GetMC().is_fixed(x1[0])) { + std::vector clin + = { c12[0] * GetMC().fixed_value(x1[0]) }; + std::vector xlin = { x2[0] }; + x1.erase(x1.begin()); + x2.erase(x2.begin()); + c12.erase(c12.begin()); + GetMC().AddConstraint( + QuadConLE{ {{clin, xlin}, {c12, x1, x2}}, {0.0} }); + } else + if (GetMC().is_fixed(x2[0])) { + std::vector clin + = { c12[0] * GetMC().fixed_value(x2[0]) }; + std::vector xlin = { x1[0] }; + x1.erase(x1.begin()); + x2.erase(x2.begin()); + c12.erase(c12.begin()); + GetMC().AddConstraint( + QuadConLE{ {{clin, xlin}, {c12, x1, x2}}, {0.0} }); + } else { + auto qc = QuadConLE{ {{}, {c12, x1, x2}}, {0.0} }; + GetMC().AddConstraint(std::move(qc)); + } } /// Reuse the stored ModelConverter diff --git a/test/end2end/cases/categorized/fast/conic/modellist.json b/test/end2end/cases/categorized/fast/conic/modellist.json index d7d32288f..9d32d38db 100644 --- a/test/end2end/cases/categorized/fast/conic/modellist.json +++ b/test/end2end/cases/categorized/fast/conic/modellist.json @@ -69,6 +69,14 @@ "solve_result_num": 0 } }, + { + "name" : "socp_11_qpobj__RSOC", + "tags" : ["quadratic"], + "objective" : 27777.7949, + "values": { + "solve_result_num": 0 + } + }, { "name" : "expcones_01__plain", "objective" : 0.7821882953, diff --git a/test/end2end/cases/categorized/fast/conic/socp_11_qpobj__RSOC.mod b/test/end2end/cases/categorized/fast/conic/socp_11_qpobj__RSOC.mod new file mode 100644 index 000000000..5515d6c59 --- /dev/null +++ b/test/end2end/cases/categorized/fast/conic/socp_11_qpobj__RSOC.mod @@ -0,0 +1,8 @@ +# This is actually convex quadratic, not conic, +# but we'd first conify, then QC-fy. +# Test that this works for Mosek 10: +# the linear term must be restored. + +var x {1..7} >= 0; +minimize qobj: sum {j in 1..7} x[j]^2; +subj to qconstr: sum {j in 1..6} x[j]^2 <= 6*x[7] - 1000; diff --git a/test/end2end/scripts/python/Solver.py b/test/end2end/scripts/python/Solver.py index 71053fa98..8d8f2ebbf 100644 --- a/test/end2end/scripts/python/Solver.py +++ b/test/end2end/scripts/python/Solver.py @@ -855,7 +855,7 @@ def __init__(self, exeName, timeout=None, nthreads=None, otherOptions=None): ModelTags.socp, ## MP transforms cones to quadratics ModelTags.socp_hard_to_recognize, - ModelTags.writelp, ModelTags.writesol, + ModelTags.writelp, } super().__init__(exeName, timeout, nthreads, otherOptions, stags)