public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/coin-or-HiGHS] rawhide: Version 1.15.1
@ 2026-07-14 16:55 Jerry James
  0 siblings, 0 replies; only message in thread
From: Jerry James @ 2026-07-14 16:55 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/coin-or-HiGHS
            Branch : rawhide
            Commit : b9404b79ed484a64cfc07725dd431322453ef5d3
            Author : Jerry James <loganjerry@gmail.com>
            Date   : 2026-07-14T10:55:31-06:00
            Stats  : +153/-1075 in 4 file(s)
            URL    : https://src.fedoraproject.org/rpms/coin-or-HiGHS/c/b9404b79ed484a64cfc07725dd431322453ef5d3?branch=rawhide

            Log:
            Version 1.15.1

- Bundle AMD and metis until they are updated in Rawhide
- Remove unused BuildRequires

---
diff --git a/coin-or-HiGHS-issue-2957.patch b/coin-or-HiGHS-issue-2957.patch
deleted file mode 100644
index c9616d3..0000000
--- a/coin-or-HiGHS-issue-2957.patch
+++ /dev/null
@@ -1,763 +0,0 @@
-From 0199f95a453eadba6aab5161bb4bb078b33cdfb9 Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Wed, 8 Apr 2026 11:24:45 +0200
-Subject: [PATCH 1/9] WIP
-
----
- highs/presolve/HPresolve.cpp | 46 ++++++++++++++++++------------------
- 1 file changed, 23 insertions(+), 23 deletions(-)
-
-diff --git a/highs/presolve/HPresolve.cpp b/highs/presolve/HPresolve.cpp
-index 8f55b22be3..dad787aeb5 100644
---- a/highs/presolve/HPresolve.cpp
-+++ b/highs/presolve/HPresolve.cpp
-@@ -4982,7 +4982,11 @@ HPresolve::Result HPresolve::singletonColStuffing(
-   // count number of fixed columns
-   HighsInt numFixedCols = 0;
- 
--  typedef std::tuple<HighsInt, double, HighsInt> candidate;
-+  struct candidate {
-+    HighsInt col;
-+    double val;
-+    HighsInt multiplier;
-+  };
- 
-   auto isSingleton = [&](HighsInt col) {
-     return (!colDeleted[col] && colsize[col] == 1 &&
-@@ -4991,9 +4995,9 @@ HPresolve::Result HPresolve::singletonColStuffing(
- 
-   auto sortCols = [&](std::vector<candidate>& vec) {
-     boost::sort::pdqsort(vec.begin(), vec.end(),
--            [&](const candidate& col1, const candidate& col2) {
--              return model->col_cost_[std::get<0>(col1)] / std::get<1>(col1) <
--                     model->col_cost_[std::get<0>(col2)] / std::get<1>(col2);
-+            [&](const candidate& c1, const candidate& c2) {
-+              return model->col_cost_[c1.col] / c1.val <
-+                     model->col_cost_[c2.col] / c2.val;
-             });
-   };
- 
-@@ -5018,7 +5022,7 @@ HPresolve::Result HPresolve::singletonColStuffing(
-         allInteger && model->integrality_[col] == HighsVarType::kInteger;
-     minWeight = std::min(minWeight, direction * val);
-     maxWeight = std::max(maxWeight, direction * val);
--    candidates.push_back(std::make_tuple(col, val, direction));
-+    candidates.push_back(candidate{col, val, direction});
-   };
- 
-   // lambda for fixing a variable
-@@ -5083,40 +5087,36 @@ HPresolve::Result HPresolve::singletonColStuffing(
- 
-     // remove integer columns if there are also continuous ones
-     if (!allInteger)
--      candidates.erase(
--          std::remove_if(candidates.begin(), candidates.end(),
--                         [&](const candidate& p) {
--                           return model->integrality_[std::get<0>(p)] ==
--                                  HighsVarType::kInteger;
--                         }),
--          candidates.end());
-+      candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
-+                                      [&](const candidate& p) {
-+                                        return model->integrality_[p.col] ==
-+                                               HighsVarType::kInteger;
-+                                      }),
-+                       candidates.end());
- 
-     // sort candidates
-     sortCols(candidates);
- 
-     // check candidates
-     for (const auto& t : candidates) {
--      // get variable index, coefficient and multiplier (-1 if sign was flipped)
--      HighsInt j = std::get<0>(t);
--      double aj = std::get<1>(t);
--      HighsInt multiplier = std::get<2>(t);
-       // both bounds have to be finite
--      if (model->col_lower_[j] == -kHighsInf ||
--          model->col_upper_[j] == kHighsInf)
-+      if (model->col_lower_[t.col] == -kHighsInf ||
-+          model->col_upper_[t.col] == kHighsInf)
-         break;
-       // compute delta (bound difference)
--      HighsCDouble delta = multiplier * aj *
--                           (static_cast<HighsCDouble>(model->col_upper_[j]) -
--                            static_cast<HighsCDouble>(model->col_lower_[j]));
-+      HighsCDouble delta =
-+          t.multiplier * t.val *
-+          (static_cast<HighsCDouble>(model->col_upper_[t.col]) -
-+           static_cast<HighsCDouble>(model->col_lower_[t.col]));
-       // check if variable can be fixed
-       if (sumUpperFinite &&
-           delta <= direction * rhs - sumUpper + primal_feastol) {
-         numFixedCols++;
--        HPRESOLVE_CHECKED_CALL(fixCol(j, multiplier));
-+        HPRESOLVE_CHECKED_CALL(fixCol(t.col, t.multiplier));
-       } else if (sumLowerFinite &&
-                  direction * rhs <= sumLower + primal_feastol) {
-         numFixedCols++;
--        HPRESOLVE_CHECKED_CALL(fixCol(j, -multiplier));
-+        HPRESOLVE_CHECKED_CALL(fixCol(t.col, -t.multiplier));
-       }
-       // update row activities
-       if (sumLowerFinite) sumLower += delta;
-
-From 198a8c8dba4389ebca73dbaed04a70b33457a243 Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 08:40:47 +0200
-Subject: [PATCH 2/9] Fix candidate computation
-
----
- highs/presolve/HPresolve.cpp | 146 ++++++++++++++++++++++-------------
- 1 file changed, 93 insertions(+), 53 deletions(-)
-
-diff --git a/highs/presolve/HPresolve.cpp b/highs/presolve/HPresolve.cpp
-index dad787aeb5..faa5295574 100644
---- a/highs/presolve/HPresolve.cpp
-+++ b/highs/presolve/HPresolve.cpp
-@@ -5017,7 +5017,10 @@ HPresolve::Result HPresolve::singletonColStuffing(
-   // lambda for storing a candidate
-   auto addCandidate = [&](std::vector<candidate>& candidates, HighsInt col,
-                           double val, HighsInt direction, double& minWeight,
--                          double& maxWeight, bool& allInteger) {
-+                          double& maxWeight, bool& hasInteger,
-+                          bool& allInteger) {
-+    hasInteger =
-+        hasInteger || model->integrality_[col] == HighsVarType::kInteger;
-     allInteger =
-         allInteger && model->integrality_[col] == HighsVarType::kInteger;
-     minWeight = std::min(minWeight, direction * val);
-@@ -5034,6 +5037,68 @@ HPresolve::Result HPresolve::singletonColStuffing(
-     return Result::kOk;
-   };
- 
-+  // lambda for computing candidates for stuffing
-+  auto computeCandidates =
-+      [&](HighsInt row, HighsInt direction, std::vector<candidate>& candidates,
-+          HighsCDouble& sumLower, HighsCDouble& sumUpper, bool& sumLowerFinite,
-+          bool& sumUpperFinite, bool& hasInteger, bool& allInteger,
-+          double& minWeight, double& maxWeight, bool allowIntegerCandidates) {
-+        // vectors for candidates and activity bounds
-+        candidates.clear();
-+        candidates.reserve(rowsize[row]);
-+        sumLower = 0.0;
-+        sumUpper = 0.0;
-+        sumLowerFinite = true;
-+        sumUpperFinite = true;
-+        hasInteger = false;
-+        allInteger = true;
-+        minWeight = kHighsInf;
-+        maxWeight = -kHighsInf;
-+
-+        for (auto& nz : getRowVector(row)) {
-+          // get column index, coefficient, cost and bounds
-+          HighsInt j = nz.index();
-+          double aj = direction * nz.value();
-+          double cj = model->col_cost_[j];
-+          double sumLowerBound = model->col_lower_[j];
-+          double sumUpperBound = model->col_upper_[j];
-+          bool isCandidate = allowIntegerCandidates ||
-+                             model->integrality_[j] != HighsVarType::kInteger;
-+
-+          if (isSingleton(j)) {
-+            // check singleton
-+            if (aj > 0) {
-+              if (cj >= 0)
-+                // dual fixing: fix to lower bound
-+                sumUpperBound = sumLowerBound;
-+              else if (isCandidate) {
-+                // candidate for stuffing
-+                sumUpperBound = sumLowerBound;
-+                addCandidate(candidates, j, aj, HighsInt{1}, minWeight,
-+                             maxWeight, hasInteger, allInteger);
-+              }
-+            } else {
-+              if (cj <= 0)
-+                // dual fixing: fix to upper bound
-+                sumLowerBound = sumUpperBound;
-+              else if (isCandidate) {
-+                // candidate for stuffing; multiply column with -1
-+                sumLowerBound = sumUpperBound;
-+                addCandidate(candidates, j, aj, HighsInt{-1}, minWeight,
-+                             maxWeight, hasInteger, allInteger);
-+              }
-+            }
-+          }
-+          // update activities
-+          if (aj < 0) std::swap(sumLowerBound, sumUpperBound);
-+          updateActivityBounds(sumLower, sumUpper, sumLowerFinite,
-+                               sumUpperFinite, aj, sumLowerBound,
-+                               sumUpperBound);
-+          if (!sumLowerFinite && !sumUpperFinite) return false;
-+        }
-+        return true;
-+      };
-+
-   // lambda for actual stuffing
-   auto checkRow = [&](HighsInt row, double rhs, HighsInt direction) {
-     // skip row if rhs is not finite
-@@ -5041,58 +5106,33 @@ HPresolve::Result HPresolve::singletonColStuffing(
- 
-     // vectors for candidates and activity bounds
-     std::vector<candidate> candidates;
--    candidates.reserve(rowsize[row]);
--    HighsCDouble sumLower = 0.0;
--    HighsCDouble sumUpper = 0.0;
--    bool sumLowerFinite = true;
--    bool sumUpperFinite = true;
--    bool allInteger = true;
--    double minWeight = kHighsInf;
--    double maxWeight = -kHighsInf;
--
--    for (auto& nz : getRowVector(row)) {
--      // get column index, coefficient, cost and bounds
--      HighsInt j = nz.index();
--      double aj = direction * nz.value();
--      double cj = model->col_cost_[j];
--      double sumLowerBound = model->col_lower_[j];
--      double sumUpperBound = model->col_upper_[j];
--      if (isSingleton(j)) {
--        // check singleton
--        if (aj > 0) {
--          // use lower bound
--          sumUpperBound = sumLowerBound;
--          // candidate for stuffing?
--          if (cj < 0)
--            addCandidate(candidates, j, aj, HighsInt{1}, minWeight, maxWeight,
--                         allInteger);
--        } else {
--          // use upper bound
--          sumLowerBound = sumUpperBound;
--          // candidate for stuffing? multiply column with -1
--          if (cj > 0)
--            addCandidate(candidates, j, aj, HighsInt{-1}, minWeight, maxWeight,
--                         allInteger);
--        }
--      } else if (aj < 0)
--        std::swap(sumLowerBound, sumUpperBound);
--      // update activities
--      updateActivityBounds(sumLower, sumUpper, sumLowerFinite, sumUpperFinite,
--                           aj, sumLowerBound, sumUpperBound);
--      if (!sumLowerFinite && !sumUpperFinite) return Result::kOk;
--    }
--
--    // all columns need to have same weights if we only have integer columns
--    if (allInteger && minWeight != maxWeight) return Result::kOk;
--
--    // remove integer columns if there are also continuous ones
--    if (!allInteger)
--      candidates.erase(std::remove_if(candidates.begin(), candidates.end(),
--                                      [&](const candidate& p) {
--                                        return model->integrality_[p.col] ==
--                                               HighsVarType::kInteger;
--                                      }),
--                       candidates.end());
-+    HighsCDouble sumLower;
-+    HighsCDouble sumUpper;
-+    bool sumLowerFinite;
-+    bool sumUpperFinite;
-+    bool hasInteger;
-+    bool allInteger;
-+    double minWeight;
-+    double maxWeight;
-+
-+    // compute candidates
-+    while (true) {
-+      if (computeCandidates(row, direction, candidates, sumLower, sumUpper,
-+                            sumLowerFinite, sumUpperFinite, hasInteger,
-+                            allInteger, minWeight, maxWeight, true)) {
-+        // all columns need to have same weights if we only have integer columns
-+        if (!hasInteger || (allInteger && minWeight == maxWeight)) break;
-+      }
-+
-+      // recompute candidates without integer columns
-+      if (hasInteger &&
-+          computeCandidates(row, direction, candidates, sumLower, sumUpper,
-+                            sumLowerFinite, sumUpperFinite, hasInteger,
-+                            allInteger, minWeight, maxWeight, false))
-+        break;
-+
-+      return Result::kOk;
-+    }
- 
-     // sort candidates
-     sortCols(candidates);
-
-From 491392b318e419157dbcff3693bba2dba16bd929 Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 08:51:57 +0200
-Subject: [PATCH 3/9] Add test
-
----
- check/TestMipSolver.cpp | 24 ++++++++++++++++++++++++
- 1 file changed, 24 insertions(+)
-
-diff --git a/check/TestMipSolver.cpp b/check/TestMipSolver.cpp
-index cf0b248c68..9d7df7fad6 100644
---- a/check/TestMipSolver.cpp
-+++ b/check/TestMipSolver.cpp
-@@ -1404,3 +1404,27 @@ TEST_CASE("issue-2173", "[highs_test_mip_solver]") {
-   const double optimal_objective = -26770.8075489;
-   solve(highs, kHighsOnString, require_model_status, optimal_objective);
- }
-+
-+TEST_CASE("issue-2957", "[highs_test_mip_solver]") {
-+  HighsLp lp;
-+  lp.num_col_ = 2;
-+  lp.num_row_ = 1;
-+  lp.col_cost_ = {1, 2};
-+  lp.col_lower_ = {0, 8};
-+  lp.col_upper_ = {20, 20};
-+  lp.integrality_ = {HighsVarType::kInteger, HighsVarType::kContinuous};
-+  lp.row_lower_ = {20.1};
-+  lp.row_upper_ = {kHighsInf};
-+  lp.a_matrix_.start_ = {0, 1, 2};
-+  lp.a_matrix_.index_ = {0, 1};
-+  lp.a_matrix_.value_ = {1, 1};
-+  Highs highs;
-+  highs.passModel(lp);
-+  highs.setOptionValue("output_flag", dev_run);
-+  highs.setOptionValue("mip_rel_gap", 0);
-+  highs.setOptionValue("mip_abs_gap", 0);
-+  highs.readModel(filename);
-+  const HighsModelStatus require_model_status = HighsModelStatus::kOptimal;
-+  const double optimal_objective = 28.2;
-+  solve(highs, kHighsOnString, require_model_status, optimal_objective);
-+}
-
-From c08e4e1087276161c031b1cdbfa40881521d649e Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 08:53:07 +0200
-Subject: [PATCH 4/9] Fix test
-
----
- check/TestMipSolver.cpp | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
-diff --git a/check/TestMipSolver.cpp b/check/TestMipSolver.cpp
-index 9d7df7fad6..fbd52bdb5d 100644
---- a/check/TestMipSolver.cpp
-+++ b/check/TestMipSolver.cpp
-@@ -1419,11 +1419,10 @@ TEST_CASE("issue-2957", "[highs_test_mip_solver]") {
-   lp.a_matrix_.index_ = {0, 1};
-   lp.a_matrix_.value_ = {1, 1};
-   Highs highs;
--  highs.passModel(lp);
-   highs.setOptionValue("output_flag", dev_run);
-   highs.setOptionValue("mip_rel_gap", 0);
-   highs.setOptionValue("mip_abs_gap", 0);
--  highs.readModel(filename);
-+  highs.passModel(lp);
-   const HighsModelStatus require_model_status = HighsModelStatus::kOptimal;
-   const double optimal_objective = 28.2;
-   solve(highs, kHighsOnString, require_model_status, optimal_objective);
-
-From 5891232dbf35609784162199f5b44718e605b218 Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 09:20:07 +0200
-Subject: [PATCH 5/9] Fix indexing
-
----
- check/TestMipSolver.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/check/TestMipSolver.cpp b/check/TestMipSolver.cpp
-index fbd52bdb5d..ea971f8a94 100644
---- a/check/TestMipSolver.cpp
-+++ b/check/TestMipSolver.cpp
-@@ -1416,7 +1416,7 @@ TEST_CASE("issue-2957", "[highs_test_mip_solver]") {
-   lp.row_lower_ = {20.1};
-   lp.row_upper_ = {kHighsInf};
-   lp.a_matrix_.start_ = {0, 1, 2};
--  lp.a_matrix_.index_ = {0, 1};
-+  lp.a_matrix_.index_ = {0, 0};
-   lp.a_matrix_.value_ = {1, 1};
-   Highs highs;
-   highs.setOptionValue("output_flag", dev_run);
-
-From eabdcc9f9f1a8a2b8e8b153242d1203e15ef4f11 Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 11:17:43 +0200
-Subject: [PATCH 6/9] Clean up
-
----
- highs/presolve/HPresolve.cpp | 46 +++++++++++++++++++++++-------------
- 1 file changed, 30 insertions(+), 16 deletions(-)
-
-diff --git a/highs/presolve/HPresolve.cpp b/highs/presolve/HPresolve.cpp
-index faa5295574..7a217e57d0 100644
---- a/highs/presolve/HPresolve.cpp
-+++ b/highs/presolve/HPresolve.cpp
-@@ -5099,6 +5099,33 @@ HPresolve::Result HPresolve::singletonColStuffing(
-         return true;
-       };
- 
-+  // lambda for computing candidates for stuffing
-+  auto checkCandidates =
-+      [&](HighsInt row, HighsInt direction, std::vector<candidate>& candidates,
-+          HighsCDouble& sumLower, HighsCDouble& sumUpper, bool& sumLowerFinite,
-+          bool& sumUpperFinite, bool& hasInteger, bool& allInteger,
-+          double& minWeight, double& maxWeight) {
-+        // compute candidates
-+        if (computeCandidates(row, direction, candidates, sumLower, sumUpper,
-+                              sumLowerFinite, sumUpperFinite, hasInteger,
-+                              allInteger, minWeight, maxWeight, true)) {
-+          // all columns need to have same weights if we only have integer
-+          // columns
-+          if (!hasInteger || (allInteger && minWeight == maxWeight))
-+            return true;
-+        } else
-+          allInteger = false;
-+
-+        // recompute candidates without integer columns
-+        if (hasInteger && !allInteger &&
-+            computeCandidates(row, direction, candidates, sumLower, sumUpper,
-+                              sumLowerFinite, sumUpperFinite, hasInteger,
-+                              allInteger, minWeight, maxWeight, false))
-+          return true;
-+
-+        return false;
-+      };
-+
-   // lambda for actual stuffing
-   auto checkRow = [&](HighsInt row, double rhs, HighsInt direction) {
-     // skip row if rhs is not finite
-@@ -5116,23 +5143,10 @@ HPresolve::Result HPresolve::singletonColStuffing(
-     double maxWeight;
- 
-     // compute candidates
--    while (true) {
--      if (computeCandidates(row, direction, candidates, sumLower, sumUpper,
--                            sumLowerFinite, sumUpperFinite, hasInteger,
--                            allInteger, minWeight, maxWeight, true)) {
--        // all columns need to have same weights if we only have integer columns
--        if (!hasInteger || (allInteger && minWeight == maxWeight)) break;
--      }
--
--      // recompute candidates without integer columns
--      if (hasInteger &&
--          computeCandidates(row, direction, candidates, sumLower, sumUpper,
--                            sumLowerFinite, sumUpperFinite, hasInteger,
--                            allInteger, minWeight, maxWeight, false))
--        break;
--
-+    if (!checkCandidates(row, direction, candidates, sumLower, sumUpper,
-+                         sumLowerFinite, sumUpperFinite, hasInteger, allInteger,
-+                         minWeight, maxWeight))
-       return Result::kOk;
--    }
- 
-     // sort candidates
-     sortCols(candidates);
-
-From 65c83af54537bbda07f459110f47a9e5804a9002 Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 14:20:00 +0200
-Subject: [PATCH 7/9] Simplify
-
----
- highs/presolve/HPresolve.cpp | 63 +++++++++++++++++++-----------------
- 1 file changed, 33 insertions(+), 30 deletions(-)
-
-diff --git a/highs/presolve/HPresolve.cpp b/highs/presolve/HPresolve.cpp
-index 7a217e57d0..e7105f2823 100644
---- a/highs/presolve/HPresolve.cpp
-+++ b/highs/presolve/HPresolve.cpp
-@@ -5099,32 +5099,40 @@ HPresolve::Result HPresolve::singletonColStuffing(
-         return true;
-       };
- 
--  // lambda for computing candidates for stuffing
--  auto checkCandidates =
--      [&](HighsInt row, HighsInt direction, std::vector<candidate>& candidates,
--          HighsCDouble& sumLower, HighsCDouble& sumUpper, bool& sumLowerFinite,
--          bool& sumUpperFinite, bool& hasInteger, bool& allInteger,
--          double& minWeight, double& maxWeight) {
--        // compute candidates
--        if (computeCandidates(row, direction, candidates, sumLower, sumUpper,
--                              sumLowerFinite, sumUpperFinite, hasInteger,
--                              allInteger, minWeight, maxWeight, true)) {
--          // all columns need to have same weights if we only have integer
--          // columns
--          if (!hasInteger || (allInteger && minWeight == maxWeight))
--            return true;
--        } else
--          allInteger = false;
-+  // lambda for computing and checking candidates for stuffing
-+  auto checkCandidates = [&](HighsInt row, HighsInt direction,
-+                             std::vector<candidate>& candidates,
-+                             HighsCDouble& sumLower, HighsCDouble& sumUpper,
-+                             bool& sumLowerFinite, bool& sumUpperFinite) {
-+    // indicators for integer candidates and weights
-+    bool hasInteger;
-+    bool allInteger;
-+    double minWeight;
-+    double maxWeight;
- 
--        // recompute candidates without integer columns
--        if (hasInteger && !allInteger &&
--            computeCandidates(row, direction, candidates, sumLower, sumUpper,
--                              sumLowerFinite, sumUpperFinite, hasInteger,
--                              allInteger, minWeight, maxWeight, false))
--          return true;
-+    // compute candidates
-+    if (computeCandidates(row, direction, candidates, sumLower, sumUpper,
-+                          sumLowerFinite, sumUpperFinite, hasInteger,
-+                          allInteger, minWeight, maxWeight, true)) {
-+      // return if there are no integer columns
-+      if (!hasInteger) return true;
-+      // all columns need to have same weights if we only have integer
-+      // columns
-+      if (allInteger) {
-+        if (minWeight != maxWeight) return false;
-+        return true;
-+      }
-+    }
- 
--        return false;
--      };
-+    // recompute candidates without integer columns
-+    if (hasInteger &&
-+        computeCandidates(row, direction, candidates, sumLower, sumUpper,
-+                          sumLowerFinite, sumUpperFinite, hasInteger,
-+                          allInteger, minWeight, maxWeight, false))
-+      return true;
-+
-+    return false;
-+  };
- 
-   // lambda for actual stuffing
-   auto checkRow = [&](HighsInt row, double rhs, HighsInt direction) {
-@@ -5137,15 +5145,10 @@ HPresolve::Result HPresolve::singletonColStuffing(
-     HighsCDouble sumUpper;
-     bool sumLowerFinite;
-     bool sumUpperFinite;
--    bool hasInteger;
--    bool allInteger;
--    double minWeight;
--    double maxWeight;
- 
-     // compute candidates
-     if (!checkCandidates(row, direction, candidates, sumLower, sumUpper,
--                         sumLowerFinite, sumUpperFinite, hasInteger, allInteger,
--                         minWeight, maxWeight))
-+                         sumLowerFinite, sumUpperFinite))
-       return Result::kOk;
- 
-     // sort candidates
-
-From 373bfc770707ccee76bd6e969644e9bde40d7b9f Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 14:34:13 +0200
-Subject: [PATCH 8/9] Use counter instead of boolean indicators
-
----
- highs/presolve/HPresolve.cpp | 141 +++++++++++++++++------------------
- 1 file changed, 68 insertions(+), 73 deletions(-)
-
-diff --git a/highs/presolve/HPresolve.cpp b/highs/presolve/HPresolve.cpp
-index e7105f2823..3e3ed588e9 100644
---- a/highs/presolve/HPresolve.cpp
-+++ b/highs/presolve/HPresolve.cpp
-@@ -5017,12 +5017,9 @@ HPresolve::Result HPresolve::singletonColStuffing(
-   // lambda for storing a candidate
-   auto addCandidate = [&](std::vector<candidate>& candidates, HighsInt col,
-                           double val, HighsInt direction, double& minWeight,
--                          double& maxWeight, bool& hasInteger,
--                          bool& allInteger) {
--    hasInteger =
--        hasInteger || model->integrality_[col] == HighsVarType::kInteger;
--    allInteger =
--        allInteger && model->integrality_[col] == HighsVarType::kInteger;
-+                          double& maxWeight, size_t& numIntegerCandidates) {
-+    if (model->integrality_[col] == HighsVarType::kInteger)
-+      numIntegerCandidates++;
-     minWeight = std::min(minWeight, direction * val);
-     maxWeight = std::max(maxWeight, direction * val);
-     candidates.push_back(candidate{col, val, direction});
-@@ -5038,66 +5035,65 @@ HPresolve::Result HPresolve::singletonColStuffing(
-   };
- 
-   // lambda for computing candidates for stuffing
--  auto computeCandidates =
--      [&](HighsInt row, HighsInt direction, std::vector<candidate>& candidates,
--          HighsCDouble& sumLower, HighsCDouble& sumUpper, bool& sumLowerFinite,
--          bool& sumUpperFinite, bool& hasInteger, bool& allInteger,
--          double& minWeight, double& maxWeight, bool allowIntegerCandidates) {
--        // vectors for candidates and activity bounds
--        candidates.clear();
--        candidates.reserve(rowsize[row]);
--        sumLower = 0.0;
--        sumUpper = 0.0;
--        sumLowerFinite = true;
--        sumUpperFinite = true;
--        hasInteger = false;
--        allInteger = true;
--        minWeight = kHighsInf;
--        maxWeight = -kHighsInf;
--
--        for (auto& nz : getRowVector(row)) {
--          // get column index, coefficient, cost and bounds
--          HighsInt j = nz.index();
--          double aj = direction * nz.value();
--          double cj = model->col_cost_[j];
--          double sumLowerBound = model->col_lower_[j];
--          double sumUpperBound = model->col_upper_[j];
--          bool isCandidate = allowIntegerCandidates ||
--                             model->integrality_[j] != HighsVarType::kInteger;
--
--          if (isSingleton(j)) {
--            // check singleton
--            if (aj > 0) {
--              if (cj >= 0)
--                // dual fixing: fix to lower bound
--                sumUpperBound = sumLowerBound;
--              else if (isCandidate) {
--                // candidate for stuffing
--                sumUpperBound = sumLowerBound;
--                addCandidate(candidates, j, aj, HighsInt{1}, minWeight,
--                             maxWeight, hasInteger, allInteger);
--              }
--            } else {
--              if (cj <= 0)
--                // dual fixing: fix to upper bound
--                sumLowerBound = sumUpperBound;
--              else if (isCandidate) {
--                // candidate for stuffing; multiply column with -1
--                sumLowerBound = sumUpperBound;
--                addCandidate(candidates, j, aj, HighsInt{-1}, minWeight,
--                             maxWeight, hasInteger, allInteger);
--              }
--            }
-+  auto computeCandidates = [&](HighsInt row, HighsInt direction,
-+                               std::vector<candidate>& candidates,
-+                               HighsCDouble& sumLower, HighsCDouble& sumUpper,
-+                               bool& sumLowerFinite, bool& sumUpperFinite,
-+                               size_t& numIntegerCandidates, double& minWeight,
-+                               double& maxWeight, bool allowIntegerCandidates) {
-+    // vectors for candidates and activity bounds
-+    candidates.clear();
-+    candidates.reserve(rowsize[row]);
-+    sumLower = 0.0;
-+    sumUpper = 0.0;
-+    sumLowerFinite = true;
-+    sumUpperFinite = true;
-+    numIntegerCandidates = 0;
-+    minWeight = kHighsInf;
-+    maxWeight = -kHighsInf;
-+
-+    for (auto& nz : getRowVector(row)) {
-+      // get column index, coefficient, cost and bounds
-+      HighsInt j = nz.index();
-+      double aj = direction * nz.value();
-+      double cj = model->col_cost_[j];
-+      double sumLowerBound = model->col_lower_[j];
-+      double sumUpperBound = model->col_upper_[j];
-+      bool isCandidate = allowIntegerCandidates ||
-+                         model->integrality_[j] != HighsVarType::kInteger;
-+
-+      if (isSingleton(j)) {
-+        // check singleton
-+        if (aj > 0) {
-+          if (cj >= 0)
-+            // dual fixing: fix to lower bound
-+            sumUpperBound = sumLowerBound;
-+          else if (isCandidate) {
-+            // candidate for stuffing
-+            sumUpperBound = sumLowerBound;
-+            addCandidate(candidates, j, aj, HighsInt{1}, minWeight, maxWeight,
-+                         numIntegerCandidates);
-+          }
-+        } else {
-+          if (cj <= 0)
-+            // dual fixing: fix to upper bound
-+            sumLowerBound = sumUpperBound;
-+          else if (isCandidate) {
-+            // candidate for stuffing; multiply column with -1
-+            sumLowerBound = sumUpperBound;
-+            addCandidate(candidates, j, aj, HighsInt{-1}, minWeight, maxWeight,
-+                         numIntegerCandidates);
-           }
--          // update activities
--          if (aj < 0) std::swap(sumLowerBound, sumUpperBound);
--          updateActivityBounds(sumLower, sumUpper, sumLowerFinite,
--                               sumUpperFinite, aj, sumLowerBound,
--                               sumUpperBound);
--          if (!sumLowerFinite && !sumUpperFinite) return false;
-         }
--        return true;
--      };
-+      }
-+      // update activities
-+      if (aj < 0) std::swap(sumLowerBound, sumUpperBound);
-+      updateActivityBounds(sumLower, sumUpper, sumLowerFinite, sumUpperFinite,
-+                           aj, sumLowerBound, sumUpperBound);
-+      if (!sumLowerFinite && !sumUpperFinite) return false;
-+    }
-+    return true;
-+  };
- 
-   // lambda for computing and checking candidates for stuffing
-   auto checkCandidates = [&](HighsInt row, HighsInt direction,
-@@ -5105,30 +5101,29 @@ HPresolve::Result HPresolve::singletonColStuffing(
-                              HighsCDouble& sumLower, HighsCDouble& sumUpper,
-                              bool& sumLowerFinite, bool& sumUpperFinite) {
-     // indicators for integer candidates and weights
--    bool hasInteger;
--    bool allInteger;
-+    size_t numIntegerCandidates;
-     double minWeight;
-     double maxWeight;
- 
-     // compute candidates
-     if (computeCandidates(row, direction, candidates, sumLower, sumUpper,
--                          sumLowerFinite, sumUpperFinite, hasInteger,
--                          allInteger, minWeight, maxWeight, true)) {
-+                          sumLowerFinite, sumUpperFinite, numIntegerCandidates,
-+                          minWeight, maxWeight, true)) {
-       // return if there are no integer columns
--      if (!hasInteger) return true;
-+      if (numIntegerCandidates == 0) return true;
-       // all columns need to have same weights if we only have integer
-       // columns
--      if (allInteger) {
-+      if (numIntegerCandidates == candidates.size()) {
-         if (minWeight != maxWeight) return false;
-         return true;
-       }
-     }
- 
-     // recompute candidates without integer columns
--    if (hasInteger &&
-+    if (numIntegerCandidates > 0 &&
-         computeCandidates(row, direction, candidates, sumLower, sumUpper,
--                          sumLowerFinite, sumUpperFinite, hasInteger,
--                          allInteger, minWeight, maxWeight, false))
-+                          sumLowerFinite, sumUpperFinite, numIntegerCandidates,
-+                          minWeight, maxWeight, false))
-       return true;
- 
-     return false;
-
-From cc1df27728bb6f5a9c516868e46b7f43dcc156af Mon Sep 17 00:00:00 2001
-From: fwesselm <fwesselm@mathworks.com>
-Date: Thu, 9 Apr 2026 14:34:50 +0200
-Subject: [PATCH 9/9] Fix comment
-
----
- highs/presolve/HPresolve.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/highs/presolve/HPresolve.cpp b/highs/presolve/HPresolve.cpp
-index 3e3ed588e9..1ebf6d9ca5 100644
---- a/highs/presolve/HPresolve.cpp
-+++ b/highs/presolve/HPresolve.cpp
-@@ -5100,7 +5100,7 @@ HPresolve::Result HPresolve::singletonColStuffing(
-                              std::vector<candidate>& candidates,
-                              HighsCDouble& sumLower, HighsCDouble& sumUpper,
-                              bool& sumLowerFinite, bool& sumUpperFinite) {
--    // indicators for integer candidates and weights
-+    // number of integer candidates and weights
-     size_t numIntegerCandidates;
-     double minWeight;
-     double maxWeight;

diff --git a/coin-or-HiGHS-unbundle.patch b/coin-or-HiGHS-unbundle.patch
index 5c21190..10420d8 100644
--- a/coin-or-HiGHS-unbundle.patch
+++ b/coin-or-HiGHS-unbundle.patch
@@ -1,17 +1,28 @@
---- HiGHS-1.14.0/app/HighsRuntimeOptions.h.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/app/HighsRuntimeOptions.h	2026-04-06 13:50:14.751516416 -0600
-@@ -14,7 +14,7 @@
+--- HiGHS-1.15.1/app/HighsAppExternalDeps.h.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/app/HighsAppExternalDeps.h	2026-07-04 12:00:19.480938223 -0600
+@@ -13,7 +13,7 @@
+ #define HIGHS_APP_EXTERNAL_DEPS_H_
+ 
+ #include "HighsExternalApi.h"
+-#include "cli11/CLI11.hpp"
++#include <CLI/CLI.hpp>
  
+ namespace HighsExtras {
+ struct app_family {};
+--- HiGHS-1.15.1/app/HighsRuntimeOptions.h.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/app/HighsRuntimeOptions.h	2026-07-04 11:59:29.528548797 -0600
+@@ -15,7 +15,7 @@
  #include <cassert>
+ #include <tuple>
  
--#include "../extern/CLI11.hpp"
+-#include "../extern/cli11/CLI11.hpp"
 +#include <CLI/CLI.hpp>
  #include "HConfig.h"
+ #include "HighsAppExternalDeps.h"
  #include "io/HighsIO.h"
- #include "io/LoadOptions.h"
---- HiGHS-1.14.0/CMakeLists.txt.orig	2026-05-27 16:51:14.869693032 -0600
-+++ HiGHS-1.14.0/CMakeLists.txt	2026-05-27 16:51:32.269938087 -0600
-@@ -547,8 +547,9 @@ if(NOT FAST_BUILD OR CSHARP)
+--- HiGHS-1.15.1/CMakeLists.txt.orig	2026-07-13 09:54:10.447453642 -0600
++++ HiGHS-1.15.1/CMakeLists.txt	2026-07-13 09:54:26.080125164 -0600
+@@ -558,8 +558,9 @@ if(NOT FAST_BUILD OR CSHARP)
  endif()
  
  # if zlib is found, then we can enable reading zlib-compressed input
@@ -22,90 +33,10 @@
 +  set(ZLIB_FOUND ON)
  endif()
  
- if (PYTHON_BUILD_SETUP OR CMAKE_INSTALL_DOCDIR STREQUAL "" OR NOT BUILD_CXX)
---- HiGHS-1.14.0/cmake/sources.cmake.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/cmake/sources.cmake	2026-04-06 13:50:14.751990479 -0600
-@@ -1,7 +1,5 @@
- set(include_dirs
-   $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/extern>
--  $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/extern/pdqsort>
--  $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/extern/zstr>
-   $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/highs>
-   $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/highs/interfaces>
-   $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/highs/io>
-@@ -259,71 +257,9 @@ set(hipo_util_headers
-     ipm/hipo/auxiliary/VectorOperations.h)
- 
- set(hipo_orderings_sources
--    ../extern/amd/amd_1.c
--    ../extern/amd/amd_2.c
--    ../extern/amd/amd_aat.c
--    ../extern/amd/amd_control.c
--    ../extern/amd/amd_defaults.c
--    ../extern/amd/amd_info.c
--    ../extern/amd/amd_order.c
--    ../extern/amd/amd_post_tree.c
--    ../extern/amd/amd_postorder.c
--    ../extern/amd/amd_preprocess.c
--    ../extern/amd/amd_valid.c
--    ../extern/amd/SuiteSparse_config.c
--    ../extern/metis/GKlib/error.c
--    ../extern/metis/GKlib/mcore.c
--    ../extern/metis/GKlib/memory.c
--    ../extern/metis/GKlib/random.c
--    ../extern/metis/libmetis/auxapi.c
--    ../extern/metis/libmetis/balance.c
--    ../extern/metis/libmetis/bucketsort.c
--    ../extern/metis/libmetis/coarsen.c
--    ../extern/metis/libmetis/compress.c
--    ../extern/metis/libmetis/contig.c
--    ../extern/metis/libmetis/fm.c
--    ../extern/metis/libmetis/gklib.c
--    ../extern/metis/libmetis/graph.c
--    ../extern/metis/libmetis/initpart.c
--    ../extern/metis/libmetis/mcutil.c
--    ../extern/metis/libmetis/mmd.c
--    ../extern/metis/libmetis/ometis.c
--    ../extern/metis/libmetis/options.c
--    ../extern/metis/libmetis/refine.c
--    ../extern/metis/libmetis/separator.c
--    ../extern/metis/libmetis/sfm.c
--    ../extern/metis/libmetis/srefine.c
--    ../extern/metis/libmetis/util.c
--    ../extern/metis/libmetis/wspace.c
-     ../extern/rcm/rcm.cpp)
- 
- set(hipo_orderings_headers
--    ../extern/amd/amd_internal.h
--    ../extern/amd/amd.h
--    ../extern/amd/SuiteSparse_config.h
--    ../extern/metis/GKlib/gk_arch.h
--    ../extern/metis/GKlib/gk_defs.h
--    ../extern/metis/GKlib/gk_macros.h
--    ../extern/metis/GKlib/gk_mkblas.h
--    ../extern/metis/GKlib/gk_mkmemory.h
--    ../extern/metis/GKlib/gk_mkpqueue.h
--    ../extern/metis/GKlib/gk_mkrandom.h
--    ../extern/metis/GKlib/gk_mksort.h
--    ../extern/metis/GKlib/gk_ms_inttypes.h
--    ../extern/metis/GKlib/gk_ms_stat.h
--    ../extern/metis/GKlib/gk_ms_stdint.h
--    ../extern/metis/GKlib/gk_proto.h
--    ../extern/metis/GKlib/gk_struct.h
--    ../extern/metis/GKlib/gk_types.h
--    ../extern/metis/GKlib/GKlib.h
--    ../extern/metis/libmetis/defs.h
--    ../extern/metis/libmetis/gklib_defs.h
--    ../extern/metis/libmetis/macros.h
--    ../extern/metis/libmetis/metislib.h
--    ../extern/metis/libmetis/proto.h
--    ../extern/metis/libmetis/stdheaders.h
--    ../extern/metis/libmetis/struct.h
--    ../extern/metis/metis.h
-     ../extern/rcm/rcm.h)
- 
- # redefinition of 'kHighsInf'
-@@ -447,9 +383,6 @@ set(highs_sources
+ if (NOT CMAKE_INSTALL_DOCDIR STREQUAL "" AND BUILD_CXX)
+--- HiGHS-1.15.1/cmake/sources.cmake.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/cmake/sources.cmake	2026-07-13 09:55:16.533539008 -0600
+@@ -463,9 +393,6 @@ set(highs_sources
  
  # add catch header?
  set(highs_headers
@@ -115,27 +46,9 @@
      interfaces/highs_c_api.h
      io/Filereader.h
      io/FilereaderLp.h
---- HiGHS-1.14.0/cmake/sources-python.cmake.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/cmake/sources-python.cmake	2026-04-06 13:50:14.752167118 -0600
-@@ -1,7 +1,5 @@
- set(include_dirs_python
-     $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/extern>
--    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/extern/pdqsort>
--    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/extern/zstr>
-     $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/highs>
-     $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/highs/interfaces>
-     $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/highs/io>
-@@ -292,7 +290,6 @@ set(highs_sources_python
-     highs/util/stringutil.cpp)
- 
- set(highs_headers_python
--    extern/pdqsort/pdqsort.h
-     highs/interfaces/highs_c_api.h
-     highs/io/Filereader.h
-     highs/io/FilereaderLp.h
---- HiGHS-1.14.0/highs/CMakeLists.txt.orig	2026-05-27 16:51:09.999028876 -0600
-+++ HiGHS-1.14.0/highs/CMakeLists.txt	2026-05-27 16:51:32.270775528 -0600
-@@ -51,8 +51,8 @@ if(NOT FAST_BUILD)
+--- HiGHS-1.15.1/highs/CMakeLists.txt.orig	2026-07-13 09:54:07.872817310 -0600
++++ HiGHS-1.15.1/highs/CMakeLists.txt	2026-07-13 10:33:45.276614239 -0600
+@@ -57,8 +57,8 @@ if(NOT FAST_BUILD)
    )
  
    if(ZLIB AND ZLIB_FOUND)
@@ -146,24 +59,7 @@
    endif()
  
    # set the install rpath to the installed destination
-@@ -156,6 +156,7 @@ else()
- 
- 
-   target_sources(highs PRIVATE ${sources} ${headers} ${win_version_file})
-+  target_link_libraries(highs PRIVATE metis amd)
- 
-   # Optional Cuda
-   if (CUPDLP_GPU)
-@@ -257,7 +258,7 @@ else()
-       else()
-           # LINUX
-           if(BLAS_LIB)
--              target_link_libraries(highs PRIVATE "${BLAS_LIB}" cblas)
-+              target_link_libraries(highs PRIVATE "${BLAS_LIB}")
-           elseif(OPENBLAS_LIB)
-               target_link_libraries(highs PRIVATE "${OPENBLAS_LIB}")
-               target_compile_definitions(highs PRIVATE HIPO_USES_OPENBLAS)
-@@ -342,10 +343,7 @@ else()
+@@ -314,10 +315,7 @@ else()
  
  
    if(ZLIB AND ZLIB_FOUND)
@@ -174,9 +70,29 @@
 +    target_link_libraries(highs PRIVATE zlib-ng::zlib)
    endif()
  
-   # install the header files of highs
---- HiGHS-1.14.0/highs/io/filereaderlp/reader.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/io/filereaderlp/reader.cpp	2026-05-27 16:54:25.423097121 -0600
+   # allow consumers of highs to be aware of potential external dependencies
+--- HiGHS-1.15.1/highs/HighsExternalDeps.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/HighsExternalDeps.cpp	2026-07-04 11:56:41.546978695 -0600
+@@ -15,7 +15,7 @@ namespace HighsExtras {
+ 
+ #ifdef ZLIB_FOUND
+ constexpr bool __zlib_enabled = true;
+-#include "zlib.h"  // defines ZLIB_VERSION
++#include <zlib-ng.h>  // defines ZLIB_VERSION
+ #else
+ constexpr bool __zlib_enabled = false;
+ #define ZLIB_VERSION "unknown"
+@@ -30,7 +30,7 @@ constexpr bool __cuda_enabled = false;
+ const HighsExtrasFeatureInfo highs_family_info_[] = {
+     {"pdqsort", "git:b1ef26a", "Zlib", true},
+     {"zstr", "1.0.6", "MIT", __zlib_enabled},
+-    {"ZLIB", ZLIB_VERSION, "Zlib", __zlib_enabled},
++    {"ZLIB-NG", ZLIBNG_VERSION, "Zlib", __zlib_enabled},
+     {"NVIDIA Driver API", "runtime", "N/A (not redistributed)",
+      __cuda_enabled}};
+ 
+--- HiGHS-1.15.1/highs/io/filereaderlp/reader.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/io/filereaderlp/reader.cpp	2026-07-04 10:06:20.514832612 -0600
 @@ -17,7 +17,7 @@
  #include "builder.hpp"
  #include "def.hpp"
@@ -186,8 +102,8 @@
  #endif
  
  // Cygwin doesn't come with an implementation for strdup if compiled with
---- HiGHS-1.14.0/highs/io/HMpsFF.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/io/HMpsFF.cpp	2026-05-27 16:54:25.425604826 -0600
+--- HiGHS-1.15.1/highs/io/HMpsFF.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/io/HMpsFF.cpp	2026-07-04 10:06:20.515144022 -0600
 @@ -11,7 +11,7 @@
  #include "lp_data/HighsModelUtils.h"
  
@@ -197,8 +113,8 @@
  #endif
  
  namespace free_format_parser {
---- HiGHS-1.14.0/highs/io/HMPSIO.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/io/HMPSIO.cpp	2026-05-27 16:54:25.426027211 -0600
+--- HiGHS-1.15.1/highs/io/HMPSIO.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/io/HMPSIO.cpp	2026-07-04 10:06:20.515451596 -0600
 @@ -21,7 +21,7 @@
  #include "util/stringutil.h"
  
@@ -208,70 +124,8 @@
  #endif
  
  using std::map;
---- HiGHS-1.14.0/highs/ipm/hipo/factorhighs/Analyse.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/ipm/hipo/factorhighs/Analyse.cpp	2026-05-27 16:54:25.432757511 -0600
-@@ -10,10 +10,10 @@
- #include "DataCollector.h"
- #include "FactorHiGHSSettings.h"
- #include "ReturnValues.h"
--#include "amd/amd.h"
-+#include <suitesparse/amd.h>
- #include "ipm/hipo/auxiliary/Auxiliary.h"
- #include "ipm/hipo/auxiliary/Log.h"
--#include "metis/metis.h"
-+#include <metis.h>
- #include "rcm/rcm.h"
- 
- namespace hipo {
---- HiGHS-1.14.0/highs/ipm/hipo/ipm/FactorHiGHSSolver.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/ipm/hipo/ipm/FactorHiGHSSolver.cpp	2026-05-27 16:54:25.436186183 -0600
-@@ -3,10 +3,10 @@
- #include <limits>
- 
- #include "Status.h"
--#include "amd/amd.h"
-+#include <suitesparse/amd.h>
- #include "ipm/hipo/auxiliary/Auxiliary.h"
- #include "ipm/hipo/auxiliary/Log.h"
--#include "metis/metis.h"
-+#include <metis.h>
- #include "parallel/HighsParallel.h"
- #include "rcm/rcm.h"
- 
-@@ -591,7 +591,7 @@ Int FactorHiGHSSolver::chooseOrdering(co
-     // compute ordering
-     if (orderings_to_try[i] == kHipoMetisString) {
-       idx_t options[METIS_NOPTIONS];
--      Highs_METIS_SetDefaultOptions(options);
-+      METIS_SetDefaultOptions(options);
-       options[METIS_OPTION_SEED] = kMetisSeed;
- 
-       // set logging of Metis depending on debug level
-@@ -606,7 +606,7 @@ Int FactorHiGHSSolver::chooseOrdering(co
-       std::vector<Int> iperm(n);
- 
-       Int status =
--          Highs_METIS_NodeND(&n, full_ptr.data(), full_rows.data(), NULL,
-+          METIS_NodeND(&n, full_ptr.data(), full_rows.data(), NULL,
-                              options, permutations[i].data(), iperm.data());
- 
-       log_.printDevInfo("Metis done\n");
-@@ -616,11 +616,11 @@ Int FactorHiGHSSolver::chooseOrdering(co
-       }
-     } else if (orderings_to_try[i] == kHipoAmdString) {
-       double control[AMD_CONTROL];
--      Highs_amd_defaults(control);
-+      amd_defaults(control);
-       double info[AMD_INFO];
- 
-       log_.printDevInfo("Running AMD\n");
--      Int status = Highs_amd_order(n, full_ptr.data(), full_rows.data(),
-+      Int status = amd_order(n, full_ptr.data(), full_rows.data(),
-                                    permutations[i].data(), control, info);
-       log_.printDevInfo("AMD done\n");
- 
---- HiGHS-1.14.0/highs/ipm/ipx/model.cc.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/ipm/ipx/model.cc	2026-05-27 16:54:25.442792098 -0600
+--- HiGHS-1.15.1/highs/ipm/ipx/model.cc.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/ipm/ipx/model.cc	2026-07-04 10:06:20.516406035 -0600
 @@ -3,7 +3,7 @@
  #include <cassert>
  #include <cmath>
@@ -290,8 +144,8 @@
  
      for (Int j = 1; j < num_cols_; j++) {
          if (colcount[j] > 
---- HiGHS-1.14.0/highs/ipm/ipx/sparse_matrix.cc.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/ipm/ipx/sparse_matrix.cc	2026-05-27 16:54:25.443027015 -0600
+--- HiGHS-1.15.1/highs/ipm/ipx/sparse_matrix.cc.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/ipm/ipx/sparse_matrix.cc	2026-07-04 10:06:20.516665388 -0600
 @@ -4,7 +4,7 @@
  #include <cmath>
  #include <utility>
@@ -310,8 +164,8 @@
          for (Int k = 0, p = begin(j); p < end(j); k++, p++) {
              index(p) = work[k].first;
              value(p) = work[k].second;
---- HiGHS-1.14.0/highs/ipm/ipx/utils.cc.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/ipm/ipx/utils.cc	2026-05-27 16:54:25.443537017 -0600
+--- HiGHS-1.15.1/highs/ipm/ipx/utils.cc.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/ipm/ipx/utils.cc	2026-07-04 10:06:20.516815548 -0600
 @@ -3,7 +3,7 @@
  #include <cassert>
  #include <cmath>
@@ -335,8 +189,8 @@
              return std::make_pair(values[i], i) < std::make_pair(values[j], j);
          });
  
---- HiGHS-1.14.0/highs/mip/HighsCliqueTable.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsCliqueTable.cpp	2026-05-27 16:54:25.461049916 -0600
+--- HiGHS-1.15.1/highs/mip/HighsCliqueTable.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsCliqueTable.cpp	2026-07-04 10:06:20.517038293 -0600
 @@ -12,7 +12,7 @@
  #include <cstdio>
  #include <numeric>
@@ -355,7 +209,7 @@
      return std::make_pair(a.weight(data.sol), a.index()) >
             std::make_pair(b.weight(data.sol), b.index());
    });
-@@ -513,7 +513,7 @@ void HighsCliqueTable::queryNeighbourhoo
+@@ -514,7 +514,7 @@ void HighsCliqueTable::queryNeighbourhoo
                                 d.neighbourhoodInds.end());
        numQueries += d.numQueries;
      });
@@ -364,7 +218,7 @@
    }
  }
  
-@@ -901,7 +901,7 @@ void HighsCliqueTable::extractCliques(
+@@ -902,7 +902,7 @@ void HighsCliqueTable::extractCliques(
    // only one binary means we do have no cliques
    if (nbin <= 1) return;
  
@@ -373,7 +227,7 @@
      return std::make_pair(vals[p1], p1) > std::make_pair(vals[p2], p2);
    });
    // check if any cliques exists
-@@ -1009,7 +1009,7 @@ void HighsCliqueTable::cliquePartition(c
+@@ -1010,7 +1010,7 @@ void HighsCliqueTable::cliquePartition(c
                                         std::vector<HighsInt>& partitionStart) {
    randgen.shuffle(clqVars.data(), clqVars.size());
  
@@ -382,7 +236,7 @@
                       [&](CliqueVar v1, CliqueVar v2) {
                         return (2 * v1.val - 1) * objective[v1.col] >
                                (2 * v2.val - 1) * objective[v2.col];
-@@ -1029,7 +1029,7 @@ void HighsCliqueTable::cliquePartition(c
+@@ -1030,7 +1030,7 @@ void HighsCliqueTable::cliquePartition(c
        partitionStart.push_back(i);
        extensionEnd = numClqVars;
        if (lastSwappedIndex >= i)
@@ -391,7 +245,7 @@
                             clqVars.begin() + lastSwappedIndex + 1,
                             [&](CliqueVar v1, CliqueVar v2) {
                               return (2 * v1.val - 1) * objective[v1.col] >
-@@ -1210,7 +1210,7 @@ void HighsCliqueTable::extractCliquesFro
+@@ -1211,7 +1211,7 @@ void HighsCliqueTable::extractCliquesFro
    std::vector<CliqueVar> clique;
    clique.reserve(nbin);
  
@@ -400,7 +254,7 @@
      return std::make_pair(std::abs(vals[p1]), p1) >
             std::make_pair(std::abs(vals[p2]), p2);
    });
-@@ -1405,7 +1405,7 @@ void HighsCliqueTable::extractObjCliques
+@@ -1406,7 +1406,7 @@ void HighsCliqueTable::extractObjCliques
    std::vector<CliqueVar> clique;
    clique.reserve(nbin);
  
@@ -409,18 +263,18 @@
      return std::make_pair(std::fabs(vals[p1]), p1) >
             std::make_pair(std::fabs(vals[p2]), p2);
    });
---- HiGHS-1.14.0/highs/mip/HighsCutGeneration.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsCutGeneration.cpp	2026-05-27 16:54:25.461831754 -0600
+--- HiGHS-1.15.1/highs/mip/HighsCutGeneration.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsCutGeneration.cpp	2026-07-04 10:19:20.617855675 -0600
 @@ -7,7 +7,7 @@
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  #include "mip/HighsCutGeneration.h"
  
 -#include "../extern/pdqsort/pdqsort.h"
 +#include <boost/sort/pdqsort/pdqsort.hpp>
+ #include "mip/HighsDomain.h"
  #include "mip/HighsMipSolverData.h"
  #include "mip/HighsTransformedLp.h"
- #include "util/HighsIntegers.h"
-@@ -57,7 +57,7 @@ bool HighsCutGeneration::determineCover(
+@@ -58,7 +58,7 @@ bool HighsCutGeneration::determineCover(
  
      // sort the remaining variables by the contribution to the rows activity in
      // the current solution
@@ -429,7 +283,7 @@
              [&](HighsInt i, HighsInt j) {
                if (upper[i] < 1.5 && upper[j] > 1.5) return true;
                if (upper[i] > 1.5 && upper[j] < 1.5) return false;
-@@ -82,7 +82,7 @@ bool HighsCutGeneration::determineCover(
+@@ -83,7 +83,7 @@ bool HighsCutGeneration::determineCover(
      // the current solution
      const auto& nodequeue = lpRelaxation.getMipSolver().mipdata_->nodequeue;
  
@@ -438,7 +292,7 @@
              [&](HighsInt i, HighsInt j) {
                if (solval[i] > feastol && solval[j] <= feastol) return true;
                if (solval[i] <= feastol && solval[j] > feastol) return false;
-@@ -136,7 +136,7 @@ void HighsCutGeneration::separateLiftedK
+@@ -135,7 +135,7 @@ void HighsCutGeneration::separateLiftedK
    S.resize(coversize);
    std::vector<int8_t> coverflag;
    coverflag.resize(rowlen);
@@ -447,7 +301,7 @@
                       [&](HighsInt a, HighsInt b) { return vals[a] > vals[b]; });
  
    HighsCDouble abartmp = vals[cover[0]];
-@@ -229,7 +229,7 @@ bool HighsCutGeneration::separateLiftedM
+@@ -228,7 +228,7 @@ bool HighsCutGeneration::separateLiftedM
  
    for (HighsInt i = 0; i != coversize; ++i) coverflag[cover[i]] = 1;
  
@@ -456,7 +310,7 @@
                       [&](HighsInt a, HighsInt b) { return vals[a] > vals[b]; });
    HighsCDouble sum = 0;
  
-@@ -292,7 +292,7 @@ bool HighsCutGeneration::separateLiftedM
+@@ -291,7 +291,7 @@ bool HighsCutGeneration::separateLiftedM
    for (HighsInt i : cover) coverflag[i] = 1;
  
    auto comp = [&](HighsInt a, HighsInt b) { return vals[a] > vals[b]; };
@@ -465,7 +319,7 @@
  
    std::vector<HighsCDouble> a;
    std::vector<HighsCDouble> u;
-@@ -566,7 +566,7 @@ bool HighsCutGeneration::cmirCutGenerati
+@@ -565,7 +565,7 @@ bool HighsCutGeneration::cmirCutGenerati
  
    if (deltas.empty()) return false;
  
@@ -474,8 +328,8 @@
    double curdelta = deltas[0];
    for (size_t i = 1; i < deltas.size(); ++i) {
      if (deltas[i] - curdelta <= 10 * feastol)
---- HiGHS-1.14.0/highs/mip/HighsCutPool.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsCutPool.cpp	2026-05-27 16:54:25.462342526 -0600
+--- HiGHS-1.15.1/highs/mip/HighsCutPool.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsCutPool.cpp	2026-07-04 10:06:20.517726165 -0600
 @@ -11,7 +11,7 @@
  #include <cassert>
  #include <numeric>
@@ -485,7 +339,7 @@
  #include "mip/HighsDomain.h"
  #include "mip/HighsLpRelaxation.h"
  #include "mip/HighsMipSolverData.h"
-@@ -272,7 +272,7 @@ void HighsCutPool::separate(const std::v
+@@ -346,7 +346,7 @@ void HighsCutPool::separate(const std::v
    assert((HighsInt)propRows.size() == numPropRows);
    if (efficacious_cuts.empty()) return;
  
@@ -494,7 +348,7 @@
            [&efficacious_cuts](const std::pair<double, HighsInt>& a,
                                const std::pair<double, HighsInt>& b) {
              if (a.first > b.first) return true;
-@@ -420,7 +420,7 @@ HighsInt HighsCutPool::addCut(const High
+@@ -518,7 +518,7 @@ HighsInt HighsCutPool::addCut(const High
      sortBuffer[i].first = Rindex[i];
      sortBuffer[i].second = Rvalue[i];
    }
@@ -503,8 +357,8 @@
        sortBuffer.begin(), sortBuffer.end(),
        [](const std::pair<HighsInt, double>& a,
           const std::pair<HighsInt, double>& b) { return a.first < b.first; });
---- HiGHS-1.14.0/highs/mip/HighsDomain.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsDomain.cpp	2026-05-27 16:54:25.462641974 -0600
+--- HiGHS-1.15.1/highs/mip/HighsDomain.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsDomain.cpp	2026-07-04 10:06:20.518071969 -0600
 @@ -12,7 +12,7 @@
  #include <numeric>
  #include <queue>
@@ -514,7 +368,7 @@
  #include "mip/HighsConflictPool.h"
  #include "mip/HighsCutPool.h"
  #include "mip/HighsMipSolverData.h"
-@@ -2731,7 +2731,7 @@ bool HighsDomain::ConflictSet::explainBo
+@@ -2832,7 +2832,7 @@ bool HighsDomain::ConflictSet::explainBo
  
    if (domchgVal == 0) return false;
  
@@ -523,7 +377,7 @@
  
    // to explain the bound change we start from the bound constraint,
    // multiply it by the columns coefficient in the constraint. Then the
-@@ -2838,7 +2838,7 @@ bool HighsDomain::ConflictSet::explainBo
+@@ -2939,7 +2939,7 @@ bool HighsDomain::ConflictSet::explainBo
  
    if (domchgVal == 0) return false;
  
@@ -532,7 +386,7 @@
  
    assert(domchgVal != 0);
  
-@@ -3309,7 +3309,7 @@ bool HighsDomain::ConflictSet::explainIn
+@@ -3410,7 +3410,7 @@ bool HighsDomain::ConflictSet::explainIn
      resolveBuffer.push_back(cand);
    }
  
@@ -541,7 +395,7 @@
  
    // compute the lower bound of M that is necessary
    double Mupper = rhs - std::max(10.0, std::fabs(rhs)) *
-@@ -3356,7 +3356,7 @@ bool HighsDomain::ConflictSet::explainIn
+@@ -3457,7 +3457,7 @@ bool HighsDomain::ConflictSet::explainIn
      resolveBuffer.push_back(cand);
    }
  
@@ -550,8 +404,8 @@
  
    // compute the lower bound of M that is necessary
    double Mlower = rhs + std::max(10.0, std::fabs(rhs)) *
---- HiGHS-1.14.0/highs/mip/HighsImplications.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsImplications.cpp	2026-05-27 16:54:25.463698383 -0600
+--- HiGHS-1.15.1/highs/mip/HighsImplications.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsImplications.cpp	2026-07-04 10:06:20.518461686 -0600
 @@ -7,7 +7,7 @@
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  #include "mip/HighsImplications.h"
@@ -570,8 +424,8 @@
  
    std::array<HighsCliqueTable::CliqueVar, 2> clique;
    clique[0] = HighsCliqueTable::CliqueVar(col, val);
---- HiGHS-1.14.0/highs/mip/HighsMipSolverData.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsMipSolverData.cpp	2026-05-27 16:54:25.464066069 -0600
+--- HiGHS-1.15.1/highs/mip/HighsMipSolverData.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsMipSolverData.cpp	2026-07-04 10:06:20.518709448 -0600
 @@ -10,7 +10,7 @@
  #include <random>
  #include <sstream>
@@ -581,8 +435,8 @@
  #include "lp_data/HighsModelUtils.h"
  #include "mip/HighsPseudocost.h"
  #include "mip/HighsRedcostFixing.h"
---- HiGHS-1.14.0/highs/mip/HighsModkSeparator.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsModkSeparator.cpp	2026-05-27 16:54:25.465010960 -0600
+--- HiGHS-1.15.1/highs/mip/HighsModkSeparator.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsModkSeparator.cpp	2026-07-04 10:06:20.518981605 -0600
 @@ -12,7 +12,7 @@
  
  #include <unordered_set>
@@ -601,8 +455,8 @@
      if (!usedWeights.insert(weights)) return;
  
      assert(lpAggregator.isEmpty());
---- HiGHS-1.14.0/highs/mip/HighsObjectiveFunction.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsObjectiveFunction.cpp	2026-05-27 16:54:25.465224809 -0600
+--- HiGHS-1.15.1/highs/mip/HighsObjectiveFunction.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsObjectiveFunction.cpp	2026-07-04 10:06:20.519194452 -0600
 @@ -10,7 +10,7 @@
  
  #include <numeric>
@@ -621,8 +475,8 @@
              [&](HighsInt i, HighsInt j) {
                return std::make_pair((HighsUInt)colToPartition[i],
                                      HighsHashHelpers::hash(i)) <
---- HiGHS-1.14.0/highs/mip/HighsPrimalHeuristics.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsPrimalHeuristics.cpp	2026-05-27 16:54:25.465473883 -0600
+--- HiGHS-1.15.1/highs/mip/HighsPrimalHeuristics.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsPrimalHeuristics.cpp	2026-07-04 10:06:20.519429409 -0600
 @@ -10,7 +10,7 @@
  #include <numeric>
  #include <unordered_set>
@@ -632,7 +486,7 @@
  #include "io/HighsIO.h"
  #include "lp_data/HConst.h"
  #include "lp_data/HighsLpUtils.h"
-@@ -48,7 +48,7 @@ HighsPrimalHeuristics::HighsPrimalHeuris
+@@ -43,7 +43,7 @@ HighsPrimalHeuristics::HighsPrimalHeuris
  void HighsPrimalHeuristics::setupIntCols() {
    intcols = mipsolver.mipdata_->integer_cols;
  
@@ -641,8 +495,8 @@
      const FP_32BIT_VOLATILE double lockScore1 =
          (mipsolver.mipdata_->feastol + mipsolver.mipdata_->uplocks[c1]) *
          (mipsolver.mipdata_->feastol + mipsolver.mipdata_->downlocks[c1]);
-@@ -286,7 +286,7 @@ void HighsPrimalHeuristics::rootReducedC
-       mipsolver.mipdata_->redcostfixing.getLurkingBounds(mipsolver);
+@@ -317,7 +317,7 @@ void HighsPrimalHeuristics::rootReducedC
+           mipsolver, worker.getGlobalDomain());
    if (10 * lurkingBounds.size() < mipsolver.mipdata_->integral_cols.size())
      return;
 -  pdqsort(lurkingBounds.begin(), lurkingBounds.end(),
@@ -650,7 +504,7 @@
            [](const std::pair<double, HighsDomainChange>& a,
               const std::pair<double, HighsDomainChange>& b) {
              return a.first > b.first;
-@@ -485,7 +485,7 @@ retry:
+@@ -533,7 +533,7 @@ retry:
          return fixval;
        };
  
@@ -659,7 +513,7 @@
                heurlp.getFractionalIntegers().end(),
                [&](const std::pair<HighsInt, double>& a,
                    const std::pair<HighsInt, double>& b) {
-@@ -769,7 +769,7 @@ retry:
+@@ -844,7 +844,7 @@ retry:
  
      // now sort the variables by their distance towards the value they will be
      // fixed to
@@ -668,8 +522,8 @@
              [&](const std::pair<HighsInt, double>& a,
                  const std::pair<HighsInt, double>& b) {
                return std::make_pair(
---- HiGHS-1.14.0/highs/mip/HighsTableauSeparator.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/mip/HighsTableauSeparator.cpp	2026-05-27 16:54:25.466148812 -0600
+--- HiGHS-1.15.1/highs/mip/HighsTableauSeparator.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/mip/HighsTableauSeparator.cpp	2026-07-04 10:06:20.519669838 -0600
 @@ -12,7 +12,7 @@
  
  #include <algorithm>
@@ -706,8 +560,8 @@
    double bestScore = -1.0;
  
    HighsInt numCuts = cutpool.getNumCuts();
---- HiGHS-1.14.0/highs/presolve/HighsSymmetry.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/presolve/HighsSymmetry.cpp	2026-05-27 16:54:25.477032141 -0600
+--- HiGHS-1.15.1/highs/presolve/HighsSymmetry.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/presolve/HighsSymmetry.cpp	2026-07-04 10:06:20.519881823 -0600
 @@ -15,7 +15,7 @@
  #include <algorithm>
  #include <numeric>
@@ -717,7 +571,7 @@
  #include "mip/HighsCliqueTable.h"
  #include "mip/HighsDomain.h"
  #include "parallel/HighsParallel.h"
-@@ -231,10 +231,10 @@ HighsSymmetries::computeStabilizerOrbits
+@@ -240,10 +240,10 @@ HighsSymmetries::computeStabilizerOrbits
        stabilizerOrbits.orbitCols.push_back(permutationColumns[i]);
    }
    stabilizerOrbits.symmetries = this;
@@ -729,8 +583,8 @@
 +    boost::sort::pdqsort(stabilizerOrbits.orbitCols.begin(),
              stabilizerOrbits.orbitCols.end(),
              [&](HighsInt col1, HighsInt col2) {
-               return getOrbit(col1) < getOrbit(col2);
-@@ -740,7 +740,7 @@ HighsInt HighsOrbitopeMatrix::orbitalFix
+               return getOrbit(col1, workspace.orbitPartition,
+@@ -755,7 +755,7 @@ HighsInt HighsOrbitopeMatrix::orbitalFix
  
  void HighsSymmetryDetection::initializeGroundSet() {
    vertexGroundSet = currentPartition;
@@ -739,7 +593,7 @@
    vertexPosition.resize(vertexToCell.size(), -1);
    for (HighsInt i = 0; i < numVertices; ++i)
      vertexPosition[vertexGroundSet[i]] = i;
-@@ -911,7 +911,7 @@ bool HighsSymmetryDetection::partitionRe
+@@ -926,7 +926,7 @@ bool HighsSymmetryDetection::partitionRe
      if (refineStart == cellEnd) continue;
  
      // sort the vertices that have updated hash values by their hash values
@@ -748,7 +602,7 @@
              currentPartition.begin() + cellEnd, [&](HighsInt v1, HighsInt v2) {
                return vertexHash[v1] < vertexHash[v2];
              });
-@@ -1267,7 +1267,7 @@ void HighsSymmetryDetection::loadModelAs
+@@ -1282,7 +1282,7 @@ void HighsSymmetryDetection::loadModelAs
    // assigned above
    currentPartition.resize(numVertices);
    std::iota(currentPartition.begin(), currentPartition.end(), 0);
@@ -757,7 +611,7 @@
            [&](HighsInt v1, HighsInt v2) {
              return vertexToCell[v1] < vertexToCell[v2];
            });
-@@ -1428,7 +1428,7 @@ HighsSymmetryDetection::computeComponent
+@@ -1443,7 +1443,7 @@ HighsSymmetryDetection::computeComponent
  
    componentData.componentSets.assign(vertexGroundSet.begin(),
                                       vertexGroundSet.begin() + numActiveCols);
@@ -766,7 +620,7 @@
            componentData.componentSets.end(), [&](HighsInt u, HighsInt v) {
              HighsInt uComp = componentData.components.getSet(vertexPosition[u]);
              HighsInt vComp = componentData.components.getSet(vertexPosition[v]);
-@@ -1466,7 +1466,7 @@ HighsSymmetryDetection::computeComponent
+@@ -1481,7 +1481,7 @@ HighsSymmetryDetection::computeComponent
      componentData.permComponents.push_back(i);
    }
  
@@ -775,8 +629,8 @@
            componentData.permComponents.end(), [&](HighsInt i, HighsInt j) {
              HighsInt seti =
                  componentData.components.getSet(componentData.firstUnfixed[i]);
---- HiGHS-1.14.0/highs/presolve/HPresolve.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/presolve/HPresolve.cpp	2026-05-27 16:54:25.478026611 -0600
+--- HiGHS-1.15.1/highs/presolve/HPresolve.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/presolve/HPresolve.cpp	2026-07-04 10:20:50.713026544 -0600
 @@ -12,7 +12,7 @@
  #include <cmath>
  #include <limits>
@@ -786,7 +640,7 @@
  #include "Highs.h"
  #include "io/HighsIO.h"
  #include "lp_data/HConst.h"
-@@ -1623,7 +1623,7 @@ HPresolve::Result HPresolve::runProbing(
+@@ -1633,7 +1633,7 @@ HPresolve::Result HPresolve::runProbing(
    }
    if (!binaries.empty()) {
      // sort variables with many implications on other binaries first
@@ -795,7 +649,7 @@
  
      size_t numChangedCols = 0;
      while (domain.getChangedCols().size() != numChangedCols) {
-@@ -1996,7 +1996,7 @@ HPresolve::Result HPresolve::liftingForP
+@@ -2006,7 +2006,7 @@ HPresolve::Result HPresolve::liftingForP
    };
  
    // sort according to score
@@ -804,16 +658,16 @@
        liftingtable.begin(), liftingtable.end(),
        [&](const liftingdata& opp1, const liftingdata& opp2) {
          double score1 = computeOverallScore(
-@@ -4990,7 +4990,7 @@ HPresolve::Result HPresolve::singletonCo
+@@ -5005,7 +5005,7 @@ HPresolve::Result HPresolve::singletonCo
    };
  
    auto sortCols = [&](std::vector<candidate>& vec) {
 -    pdqsort(vec.begin(), vec.end(),
 +    boost::sort::pdqsort(vec.begin(), vec.end(),
-             [&](const candidate& col1, const candidate& col2) {
-               return model->col_cost_[std::get<0>(col1)] / std::get<1>(col1) <
-                      model->col_cost_[std::get<0>(col2)] / std::get<1>(col2);
-@@ -5192,7 +5192,7 @@ HPresolve::Result HPresolve::enumerateSo
+             [&](const candidate& c1, const candidate& c2) {
+               return model->col_cost_[c1.col] / c1.val <
+                      model->col_cost_[c2.col] / c2.val;
+@@ -5265,7 +5265,7 @@ HPresolve::Result HPresolve::enumerateSo
        binvars[numnzs++] = col;
      }
      if (numnzs == 0) return false;
@@ -822,7 +676,7 @@
      return true;
    };
  
-@@ -5321,7 +5321,7 @@ HPresolve::Result HPresolve::enumerateSo
+@@ -5394,7 +5394,7 @@ HPresolve::Result HPresolve::enumerateSo
  
    // compile rows and sort them
    compileRows(rows);
@@ -831,7 +685,7 @@
  
    // remove similar rows
    removeSimilarRows(rows);
-@@ -6618,7 +6618,7 @@ HPresolve::Result HPresolve::aggregator(
+@@ -6700,7 +6700,7 @@ HPresolve::Result HPresolve::aggregator(
                       }),
        substitutionOpportunities.end());
  
@@ -840,7 +694,7 @@
        substitutionOpportunities.begin(), substitutionOpportunities.end(),
        [&](const std::pair<HighsInt, HighsInt>& nz1,
            const std::pair<HighsInt, HighsInt>& nz2) {
-@@ -7068,7 +7068,7 @@ HPresolve::Result HPresolve::strengthenI
+@@ -7150,7 +7150,7 @@ HPresolve::Result HPresolve::strengthenI
        if (maxviolation - continuouscontribution <= smallVal || indices.empty())
          break;
  
@@ -849,8 +703,8 @@
          return std::make_pair(reducedcost[i1], i1) >
                 std::make_pair(reducedcost[i2], i2);
        });
---- HiGHS-1.14.0/highs/simplex/HEkkDualRHS.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/simplex/HEkkDualRHS.cpp	2026-05-27 16:54:25.489819431 -0600
+--- HiGHS-1.15.1/highs/simplex/HEkkDualRHS.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/simplex/HEkkDualRHS.cpp	2026-07-04 10:06:20.521268326 -0600
 @@ -15,7 +15,7 @@
  #include <iostream>
  #include <set>
@@ -887,8 +741,8 @@
    if ((HighsInt)(setP.size()) > chLimit) setP.resize(chLimit);
    *chCount = static_cast<HighsInt>(setP.size());
    for (size_t i = 0; i < setP.size(); i++) chIndex[i] = setP[i].second;
---- HiGHS-1.14.0/highs/simplex/HEkkDualRow.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/simplex/HEkkDualRow.cpp	2026-05-27 16:54:25.490114932 -0600
+--- HiGHS-1.15.1/highs/simplex/HEkkDualRow.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/simplex/HEkkDualRow.cpp	2026-07-04 10:06:20.521452269 -0600
 @@ -13,7 +13,7 @@
  #include <cassert>
  #include <iostream>
@@ -907,8 +761,8 @@
    analysis->simplexTimerStop(Chuzc4eClock);
    analysis->simplexTimerStop(Chuzc4Clock);
  
---- HiGHS-1.14.0/highs/simplex/HEkkPrimal.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/simplex/HEkkPrimal.cpp	2026-05-27 16:54:25.490437944 -0600
+--- HiGHS-1.15.1/highs/simplex/HEkkPrimal.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/simplex/HEkkPrimal.cpp	2026-07-04 10:06:20.521699580 -0600
 @@ -10,7 +10,7 @@
   */
  #include "simplex/HEkkPrimal.h"
@@ -936,8 +790,8 @@
    double dMaxAlpha = 0.0;
    size_t iLast = ph1SorterT.size();
    for (size_t i = 0; i < ph1SorterT.size(); i++) {
---- HiGHS-1.14.0/highs/simplex/HSimplexNla.cpp.orig	2026-04-06 08:36:31.000000000 -0600
-+++ HiGHS-1.14.0/highs/simplex/HSimplexNla.cpp	2026-05-27 16:54:25.491394868 -0600
+--- HiGHS-1.15.1/highs/simplex/HSimplexNla.cpp.orig	2026-07-02 00:42:37.000000000 -0600
++++ HiGHS-1.15.1/highs/simplex/HSimplexNla.cpp	2026-07-04 10:06:20.521969643 -0600
 @@ -14,7 +14,7 @@
  
  #include <stdio.h>
@@ -965,8 +819,8 @@
    for (HighsInt en = 0; en < vector->packCount; en++) {
      HighsInt iRow = sorted_index[en];
      if (en % 5 == 0) printf("\n");
---- HiGHS-1.14.0/highs/util/HFactor.cpp.orig	2026-05-27 16:51:17.165196338 -0600
-+++ HiGHS-1.14.0/highs/util/HFactor.cpp	2026-05-27 16:54:25.498108254 -0600
+--- HiGHS-1.15.1/highs/util/HFactor.cpp.orig	2026-07-13 09:54:15.646690768 -0600
++++ HiGHS-1.15.1/highs/util/HFactor.cpp	2026-07-13 09:54:26.088203673 -0600
 @@ -13,7 +13,7 @@
  #include <cassert>
  #include <iostream>

diff --git a/coin-or-HiGHS.spec b/coin-or-HiGHS.spec
index 1727628..ea9d4b1 100644
--- a/coin-or-HiGHS.spec
+++ b/coin-or-HiGHS.spec
@@ -5,12 +5,12 @@
 %bcond ctest 1
 
 # The build runs git to get a commit, but we don't have a git checkout
-%global commit  7df0786de
+%global commit  04024d701
 
 %global giturl  https://github.com/ERGO-Code/HiGHS
 
 Name:           coin-or-HiGHS
-Version:        1.14.0
+Version:        1.15.1
 Release:        %autorelease
 Summary:        Linear optimization software
 
@@ -24,23 +24,8 @@ Patch:          %{name}-rpath.patch
 Patch:          %{name}-popcount.patch
 # Fix out-of-bounds vector accesses
 Patch:          %{name}-vector.patch
-# Unbundle amd, cli11, metis, pdqsort, and zstr
+# Unbundle cli11, pdqsort, and zstr
 Patch:          %{name}-unbundle.patch
-# Fix 2957
-# https://github.com/ERGO-Code/HiGHS/pull/2961
-#
-# Fixes:
-#
-# - MIP incorrect solution in HiGHS 1.14:
-#   https://github.com/ERGO-Code/HiGHS/issues/2957
-# - highspy v1.14 regression: presolve in toy example is reaching non-optimal
-#   solution: https://github.com/ERGO-Code/HiGHS/issues/3002
-# - Test regressions in 3.3.0 with HiGHS 1.14:
-#   https://github.com/coin-or/pulp/issues/904
-# - python-pulp: FTBFS in Fedora Rawhide: pulp.constants.PulpError: Tests
-#   failed for solver <pulp.apis.highs_api.HiGHS_CMD object at 0x7f39f283cb00>:
-#   var x == 2.0 != 3: https://bugzilla.redhat.com/show_bug.cgi?id=2466661
-Patch:          %{name}-issue-2957.patch
 # Patch courtesy of Gentoo to fix the tests on some arches
 Patch:          %{name}-ignore-test-iterations.patch
 
@@ -60,15 +45,10 @@ BuildRequires:  boost-devel
 BuildRequires:  catch2-devel
 BuildRequires:  cli11-static
 BuildRequires:  cmake
-BuildRequires:  cmake(AMD)
-BuildRequires:  doctest-static
 BuildRequires:  gcc-c++
 BuildRequires:  help2man
 BuildRequires:  libatomic
-BuildRequires:  metis-devel
 BuildRequires:  ninja-build
-BuildRequires:  pkgconfig(coindatanetlib)
-BuildRequires:  pkgconfig(coindatasample)
 BuildRequires:  pkgconfig(flexiblas)
 BuildRequires:  python3-devel
 BuildRequires:  zstr-static
@@ -83,6 +63,13 @@ Provides:       bundled(FilereaderLP)
 # https://people.sc.fsu.edu/~jburkardt/f77_src/sparsepak/sparsepak.html
 Provides:       bundled(rcm)
 
+# In the past, we unbundled both AMD (part of suitesparse) and metis.  However,
+# with version 1.15.1, we see multiple test failures when unbundling either.
+# The bundled versions are newer than those available in Rawhide.  If either
+# package is updated in Rawhide, see if we can unbundle again.
+Provides:       bundled(metis) = 5.2.1
+Provides:       bundled(suitesparse) = 7.12.1
+
 %description
 HiGHS is a high performance serial and parallel solver for large scale sparse
 linear optimization problems of the form
@@ -127,11 +114,11 @@ This package contains a Python 3 interface to coin-or-HiGHS.
 sed -i 's,n/a,%{commit},' CMakeLists.txt
 
 # Unbundle catch
-rm extern/catch.hpp
-ln -s %{_includedir}/catch2/catch.hpp extern/catch.hpp
+rm extern/catch/catch.hpp
+ln -s %{_includedir}/catch2/catch.hpp extern/catch/catch.hpp
 
-# Ensure the bundled amd, cli11, metis, pdqsort, and zstr are not used
-rm -fr extern/{CLI11.hpp,amd,metis,pdqsort,zstr}
+# Ensure the bundled cli11, pdqsort, and zstr are not used
+rm -fr extern/{cli11,pdqsort,zstr}
 
 %generate_buildrequires
 %pyproject_buildrequires -x test
@@ -157,9 +144,9 @@ rm -fr %{buildroot}%{_docdir}
 
 # Instead of linking with and installing a private copy of the library,
 # fix up the installed python tree to use the installed library
-cd highs
-g++ %{build_cxxflags} -fPIC -shared -I . -I ../%{_vpath_builddir} \
-  -I %{_includedir}/python%{python3_version} highs_bindings.cpp \
+cd highspy
+g++ %{build_cxxflags} -fPIC -shared -I ../highs -I ../%{_vpath_builddir} \
+  -I ../extern -I %{_includedir}/python%{python3_version} highs_bindings.cpp \
   -o %{buildroot}%{python3_sitearch}/highspy/_core%{python3_ext_suffix} \
   %{build_ldflags} -L %{buildroot}%{_libdir} -lhighs
 cd -

diff --git a/sources b/sources
index febb87b..e2895aa 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (HiGHS-1.14.0.tar.gz) = d4c2d09bf41384a58a04528ffaac223eee40dea4a1cadaea369791f6e8d1aa61ff3fce57e7c97cb5f4bef0f1305681066e8ef7483f4471770295f95b9ec337f7
+SHA512 (HiGHS-1.15.1.tar.gz) = 0ea407784bc6c8e31c72fbcdcab201e8a7cc7debbca57504e74177658208151e90637e5fb1e0cdf1b1ae6b677e7865e77771ec025b17f8223f74d00fc6420546

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-07-14 16:55 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-14 16:55 [rpms/coin-or-HiGHS] rawhide: Version 1.15.1 Jerry James

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox