Skip to content

Commit 948085c

Browse files
committed
Merge branch 'latest' of https://github.com/ERGO-Code/HiGHS into moveMirToImplications
2 parents 151d123 + 39665ea commit 948085c

File tree

4 files changed

+82
-117
lines changed

4 files changed

+82
-117
lines changed

check/TestQpSolver.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,20 @@ TEST_CASE("test-qjh", "[qpsolver]") {
462462
return_status = highs.clearModel();
463463

464464
std::string filename;
465-
for (HighsInt test_k = 0; test_k < 2; test_k++) {
465+
for (HighsInt test_k = 0; test_k < 4; test_k++) {
466466
if (test_k == 0) {
467467
filename = std::string(HIGHS_DIR) + "/check/instances/qjh.mps";
468468
} else if (test_k == 1) {
469469
filename = std::string(HIGHS_DIR) + "/check/instances/qjh_quadobj.mps";
470-
} else {
470+
} else if (test_k == 2) {
471471
filename = std::string(HIGHS_DIR) + "/check/instances/qjh_qmatrix.mps";
472+
} else {
473+
// This instance has both quadobj and qmatrix sections, and
474+
// both correspond to identical Hessians. They are added so
475+
// objective changes
476+
filename =
477+
std::string(HIGHS_DIR) + "/check/instances/qjh_quadobj_qmatrix.mps";
478+
required_objective_function_value = -2.75;
472479
}
473480

474481
return_status = highs.readModel(filename);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
NAME qjh_qmatrix
2+
ROWS
3+
N obj
4+
L c1
5+
COLUMNS
6+
x1 obj 0.0
7+
x1 c1 1.0
8+
x2 obj -1.0
9+
x3 obj -3.0
10+
x3 c1 1.0
11+
RHS
12+
rhs c1 2.0
13+
QUADOBJ
14+
x1 x1 2.0
15+
x1 x3 -1.0
16+
x2 x2 0.2
17+
x3 x3 2.0
18+
QMATRIX
19+
x1 x1 2.0
20+
x1 x3 -1.0
21+
x3 x1 -1.0
22+
x2 x2 0.2
23+
x3 x3 2.0
24+
ENDATA
25+

highs/io/HMpsFF.cpp

Lines changed: 43 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ HighsInt HMpsFF::fillMatrix(const HighsLogOptions& log_options) {
175175
}
176176

177177
HighsInt HMpsFF::fillHessian(const HighsLogOptions& log_options) {
178-
const std::vector<Triplet>& qentries = q_entries;
179178
size_t num_entries = q_entries.size();
180179
if (!num_entries) {
181180
q_dim = 0;
@@ -212,6 +211,7 @@ HighsInt HMpsFF::fillHessian(const HighsLogOptions& log_options) {
212211
q_value[q_length[iCol]] = value;
213212
q_length[iCol]++;
214213
}
214+
q_format = HessianFormat::kSquare;
215215
return 0;
216216
}
217217

@@ -1735,120 +1735,23 @@ HMpsFF::Parsekey HMpsFF::parseRanges(const HighsLogOptions& log_options,
17351735
typename HMpsFF::Parsekey HMpsFF::parseHessian(
17361736
const HighsLogOptions& log_options, std::istream& file,
17371737
const HMpsFF::Parsekey keyword) {
1738-
// Parse Hessian information from QUADOBJ or QMATRIX
1739-
// section according to keyword
1740-
const bool qmatrix = keyword == HMpsFF::Parsekey::kQmatrix;
1741-
std::string section_name;
1742-
if (qmatrix) {
1743-
section_name = "QMATRIX";
1744-
} else if (keyword == HMpsFF::Parsekey::kQuadobj) {
1745-
section_name = "QUADOBJ";
1746-
}
1747-
// Store all nonzeros in the section, but QMATRIX should have all
1748-
// entries - so its format is HessianFormat::kSquare format -
1749-
// whereas every off-diagonal QUADOBJ entry also defines its entry
1750-
// in the opposite triangle, so its format is
1751-
// HessianFormat::kTriangular.
1752-
q_format = qmatrix ? HessianFormat::kSquare : HessianFormat::kTriangular;
1753-
std::string strline;
1754-
std::string col_name;
1755-
std::string row_name;
1756-
std::string coeff_name;
1757-
size_t end_row_name;
1758-
size_t end_coeff_name;
1759-
HighsInt colidx, rowidx;
1760-
1761-
bool skip;
1762-
while (getMpsLine(file, strline, skip)) {
1763-
if (skip) continue;
1764-
if (timeout()) return HMpsFF::Parsekey::kTimeout;
1765-
1766-
size_t begin = 0;
1767-
size_t end = 0;
1768-
HMpsFF::Parsekey key = checkFirstWord(strline, begin, end, col_name);
1769-
1770-
// start of new section?
1771-
if (key != Parsekey::kNone) {
1772-
highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read %s OK\n",
1773-
section_name.c_str());
1774-
return key;
1775-
}
1776-
1777-
// Get the column index from the name
1778-
colidx = getColIdx(col_name);
1779-
assert(colidx >= 0 && colidx < num_col);
1780-
1781-
// Loop over the maximum of two entries per row of the file
1782-
for (int entry = 0; entry < 2; entry++) {
1783-
// Get the row name
1784-
row_name = "";
1785-
row_name = first_word(strline, end);
1786-
end_row_name = first_word_end(strline, end);
1787-
1788-
if (row_name == "") break;
1789-
1790-
coeff_name = "";
1791-
coeff_name = first_word(strline, end_row_name);
1792-
end_coeff_name = first_word_end(strline, end_row_name);
1793-
1794-
if (coeff_name == "") {
1795-
trim(row_name);
1796-
trim(col_name);
1797-
highsLogUser(
1798-
log_options, HighsLogType::kError,
1799-
"%s has no coefficient for entry \"%s\" in column \"%s\"\n",
1800-
section_name.c_str(), row_name.c_str(), col_name.c_str());
1801-
return HMpsFF::Parsekey::kFail;
1802-
}
1803-
1804-
rowidx = getColIdx(row_name);
1805-
assert(rowidx >= 0 && rowidx < num_col);
1806-
1807-
bool is_nan = false;
1808-
double coeff = getValue(coeff_name, is_nan); // atof(word.c_str());
1809-
if (is_nan) {
1810-
highsLogUser(
1811-
log_options, HighsLogType::kError,
1812-
"Hessian coefficient for entry \"%s\" in column \"%s\" is NaN\n",
1813-
row_name.c_str(), col_name.c_str());
1814-
return HMpsFF::Parsekey::kFail;
1815-
}
1816-
if (coeff) q_entries.push_back(std::make_tuple(rowidx, colidx, coeff));
1817-
end = end_coeff_name;
1818-
// Don't read more if end of line reached
1819-
if (end == strline.length()) break;
1820-
}
1821-
}
1822-
1823-
return HMpsFF::Parsekey::kFail;
1738+
assert(keyword == HMpsFF::Parsekey::kQuadobj ||
1739+
keyword == HMpsFF::Parsekey::kQmatrix);
1740+
const std::string section_name =
1741+
keyword == HMpsFF::Parsekey::kQuadobj ? "QUADOBJ" : "QMATRIX";
1742+
return parseQuadMatrix(log_options, file, section_name, q_entries);
18241743
}
18251744

18261745
typename HMpsFF::Parsekey HMpsFF::parseQuadRows(
18271746
const HighsLogOptions& log_options, std::istream& file,
18281747
const HMpsFF::Parsekey keyword) {
1829-
// Parse Hessian information from QSECTION or QCMATRIX
1830-
// section according to keyword
1831-
const bool qcmatrix = keyword == HMpsFF::Parsekey::kQcmatrix;
1832-
std::string section_name;
1833-
if (qcmatrix) {
1834-
section_name = "QCMATRIX";
1835-
} else {
1836-
section_name = "QSECTION";
1837-
}
1838-
// Store all nonzeros in the section, but QCMATRIX should have all
1839-
// entries - so its format is HessianFormat::kSquare format -
1840-
// whereas every off-diagonal QSECTION entry also defines its entry
1841-
// in the opposite triangle, so its format is
1842-
// HessianFormat::kTriangular.
1843-
q_format = qcmatrix ? HessianFormat::kSquare : HessianFormat::kTriangular;
1748+
assert(keyword == HMpsFF::Parsekey::kQsection ||
1749+
keyword == HMpsFF::Parsekey::kQcmatrix);
1750+
const std::string section_name =
1751+
keyword == HMpsFF::Parsekey::kQsection ? "QSECTION" : "QCMATRIX";
18441752
std::string strline;
18451753
std::string col_name;
1846-
std::string row_name;
1847-
std::string coeff_name;
1848-
size_t end_row_name;
1849-
size_t end_coeff_name;
1850-
HighsInt rowidx; // index of quadratic row
1851-
HighsInt qcolidx, qrowidx; // indices in quadratic coefs matrix
1754+
HighsInt rowidx; // index of quadratic row
18521755

18531756
// Get row name from section argument
18541757
std::string rowname = first_word(section_args, 0);
@@ -1894,6 +1797,25 @@ typename HMpsFF::Parsekey HMpsFF::parseQuadRows(
18941797
assert(rowidx == -1 || qrows_entries.size() == static_cast<size_t>(num_row));
18951798

18961799
auto& qentries = (rowidx == -1 ? q_entries : qrows_entries[rowidx]);
1800+
return parseQuadMatrix(log_options, file, section_name, qentries);
1801+
}
1802+
1803+
typename HMpsFF::Parsekey HMpsFF::parseQuadMatrix(
1804+
const HighsLogOptions& log_options, std::istream& file,
1805+
const std::string& section_name, std::vector<Triplet>& q_entries) {
1806+
// Store all nonzeros in the section. QMATRIX/QCMATRIX should have
1807+
// all entries, whereas every off-diagonal QUADOBJ/QSECTION entry
1808+
// also defines its entry in the opposite triangle, so add this
1809+
// explicitly to unify the record regardless of section
1810+
const bool triangular =
1811+
section_name == "QUADOBJ" || section_name == "QSECTION";
1812+
std::string strline;
1813+
std::string col_name;
1814+
std::string row_name;
1815+
std::string coeff_name;
1816+
size_t end_row_name;
1817+
size_t end_coeff_name;
1818+
HighsInt colidx, rowidx;
18971819

18981820
bool skip;
18991821
while (getMpsLine(file, strline, skip)) {
@@ -1906,14 +1828,14 @@ typename HMpsFF::Parsekey HMpsFF::parseQuadRows(
19061828

19071829
// start of new section?
19081830
if (key != Parsekey::kNone) {
1909-
highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read %s OK\n",
1831+
highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read %s OK\n",
19101832
section_name.c_str());
19111833
return key;
19121834
}
19131835

1914-
// Get the column index
1915-
qcolidx = getColIdx(col_name);
1916-
assert(qcolidx >= 0 && qcolidx < num_col);
1836+
// Get the column index from the name
1837+
colidx = getColIdx(col_name);
1838+
assert(colidx >= 0 && colidx < num_col);
19171839

19181840
// Loop over the maximum of two entries per row of the file
19191841
for (int entry = 0; entry < 2; entry++) {
@@ -1938,8 +1860,8 @@ typename HMpsFF::Parsekey HMpsFF::parseQuadRows(
19381860
return HMpsFF::Parsekey::kFail;
19391861
}
19401862

1941-
qrowidx = getColIdx(row_name);
1942-
assert(qrowidx >= 0 && qrowidx < num_col);
1863+
rowidx = getColIdx(row_name);
1864+
assert(rowidx >= 0 && rowidx < num_col);
19431865

19441866
bool is_nan = false;
19451867
double coeff = getValue(coeff_name, is_nan); // atof(word.c_str());
@@ -1950,7 +1872,13 @@ typename HMpsFF::Parsekey HMpsFF::parseQuadRows(
19501872
row_name.c_str(), col_name.c_str());
19511873
return HMpsFF::Parsekey::kFail;
19521874
}
1953-
if (coeff) q_entries.push_back(std::make_tuple(qrowidx, qcolidx, coeff));
1875+
if (coeff) {
1876+
q_entries.push_back(std::make_tuple(rowidx, colidx, coeff));
1877+
// For a triangular section (QUADOBJ/QSECTION), make a
1878+
// separate record of the entry in the opposite triangle
1879+
if (triangular && rowidx != colidx)
1880+
q_entries.push_back(std::make_tuple(colidx, rowidx, coeff));
1881+
}
19541882
end = end_coeff_name;
19551883
// Don't read more if end of line reached
19561884
if (end == strline.length()) break;

highs/io/HMpsFF.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ class HMpsFF {
224224
HMpsFF::Parsekey parseQuadRows(const HighsLogOptions& log_options,
225225
std::istream& file,
226226
const HMpsFF::Parsekey keyword);
227+
HMpsFF::Parsekey parseQuadMatrix(const HighsLogOptions& log_options,
228+
std::istream& file,
229+
const std::string& section_name,
230+
std::vector<Triplet>& q_entries);
231+
227232
HMpsFF::Parsekey parseCones(const HighsLogOptions& log_options,
228233
std::istream& file);
229234
HMpsFF::Parsekey parseSos(const HighsLogOptions& log_options,

0 commit comments

Comments
 (0)