Skip to content

Instantly share code, notes, and snippets.

@molnarg
Created May 9, 2026 08:16
Show Gist options
  • Select an option

  • Save molnarg/c5cc72c2f951791737cd5834eac959e4 to your computer and use it in GitHub Desktop.

Select an option

Save molnarg/c5cc72c2f951791737cd5834eac959e4 to your computer and use it in GitHub Desktop.
HiGHS 1.12.0 SC variable bug repro: MIP claims optimality with infeasibility = SC lower bound
"""
Reproducer for HiGHS 1.12.0 semi-continuous variable bug.
Symptom
-------
When a MIP contains semi-continuous variables (integrality=2) and the LP
relaxation places one or more of them at their lower bound `lo` exactly,
HiGHS may return a "feasible / Optimal" solution that its OWN internal
feasibility check passes (`row viol. 0`), while a separate post-validation
rejects it with `infeasibility = X` where X equals the SC variable's `lo`
exactly. scipy/linprog surfaces this as
`status=4, message="(HiGHS Status 4: Solve error)"`.
In the verbose HiGHS output, the smoking gun is two adjacent reports:
Solution status feasible
...
0 (row viol.)
...
ERROR: MIP solver claims optimality, but with num/max/sum
primal(1/63.125/63.125) infeasibilities
ERROR: Setting model status to Solve error
i.e. the solver tells itself the solution is feasible (row viol. 0), then
a separate check on the same solution finds row viol. = 63.125 (which is
the `lo` bound of one specific SC variable in the LP).
Workaround
----------
Replace each SC variable q (bounds [lo, hi], integrality=2) with:
- plain continuous q (bounds [0, hi], integrality=0)
- binary indicator b (bounds [0, 1], integrality=1)
- two coupling rows: q - hi*b <= 0 and lo*b - q <= 0
The feasible region {0} ∪ [lo, hi] is preserved exactly. This routes the
disjunction through HiGHS's well-tested integer code path instead of the
SC-handling code that has the bug.
Setup
-----
- scipy 1.17.1
- HiGHS 1.12.0 (git hash: 4f96ee8) bundled with scipy
- Python 3.12
The LP data is embedded below as base64-encoded numpy NPZ. 866 rows x
696 cols, 78 SC variables. All row/column names removed -- only numeric
arrays remain. Extracted from a real failing optimization.
Run
---
python highs_sc_bug_repro.py
Expected output: original formulation fails with Status 4, the
binary-indicator workaround returns an optimal solution.
"""
import base64
import io
import numpy as np
from scipy.optimize import linprog
# Base64-encoded NPZ archive of the failing LP. Decoded at runtime.
LP_DATA_B64 = """\
UEsDBC0AAAAIAAAAIQA0moHe//////////8FABQAYy5ucHkBABAAQBYAAAAAAADcAAAAAAAAAJvs
F+obEMnIUMZQrZ6SWpxcpG6loG6TZqGuo6Cell9UUpSYF59flJIKEndLzClOBYoXZyQWpAL5GmaW
ZjqaOgq1CmQDLmMQ2N10YNZMIPgZcSANBG61HmAAAYt+uDilNCHzIPL1BMzJOHD2DBBI9ED5HXB5
iD+KqOZe4ugKOtuXgSFOmb8xzaOVv1Djt4Ni82DplLrhgZtGzyeDK11QKx0PFXcPlDuHSvjQ279D
P1yIKzeI9Sep5Srtwo8BAhpG6VF6lB6lR+lRepQepUfpUXqUHqVH6aFIRx6A0QBQSwMELQAAAAgA
AAAhAE/Cp5b//////////wgAFABBX3ViLm5weQEAEAAAlEkAAAAAAGoyAAAAAAAA7N1frGxXXcDx
W/6lEBNqgoohMVdCWiQNWgulELBDQUBAaEvLvZgYIdLCQ2O1GKNB402Mb8ZqTEzAxFSMPhtNTHzi
8CC+YCLCC4kxxAdIDAgRYyw8YO+Z2ff2rnP3mf1n7b1/a63P56HTOefMzD7nzOw1d76/s+dP3vfI
zz/woVsu/MaFT97x0Uc/8ctP3vGmi3e8+bF777jz4h2PPfHkrz/5kV/5pSee/OijVz/+jo88/olH
n/34Jz7+kV999Nnzr773nnvuvHjPG+/5iTsv/s7FSV5yAQAAaNvJpZNbT//n25/dn//gyf4Th9Mr
D558/6rf3R0+/v7D6aXD6eWTFbbyWQ8kt5ee9vnA/vNXku9rd/30u6ff4I8cuZ6Fddu3O/xcryS/
l/TrTi6df7pLznc/p5Pr13/zDXnZbn/6od025287+nW3nJ6+fH/+5PLh4688nHbnX70/3V06nP+Z
w9c/dDh94PDxtx2+rrvcxWQ7ohj6c+v7/P37092Dydenp/m37+a/r8PPeZf8/k6639ddh9MPHk7f
fvj67vd2+D6u3H+4XPd1rzmcpt9Xtz237s7/PjZy5ZHDz+ne3f4D3c/xVfvTK8n9+sLth49f6jmf
3v9vT86/Kvn6uw+ff+Rw/g2H04f3pyeH0wv3HM4nP+9nf683/J7P3C9zS34+z95/9rd/5/58931d
u7/dvjvdzX//FWfuj93H95c7e/6Gr7/2e7gtuf5XHk6v397+tNuO5Od1k+tNf343ns/kyrv313vl
vYfbffhwO28+nO/uDz91bTv2p6/qOZ/b9evvfg83/D6e83O58eN9j/fx61D6+z89vfZ77r6uf397
4+9t7v6mu34AAID5nv7UVV85vM54+eT07DPd64gP708vPLz/+Kfed3L3VX93eD326QdOHjt1+PoP
d687Xt5//PPHXx/uvm7/76a3nHz89ANvOXq5m7ry0H777r588s9feNad11/3PN38f+i2L3kd9cr1
101fePo/Lzi59/R6nn/yxavX84ULh+376cPpXYfTO49u5+On38/Lpn0/53jR6X//77P/erp9L8h+
/aU7/bF/7fLu06f358u70/vDNw7/jv/q4fwX/ue+0/vFG5PXda50r3fdvTu9nr9+3+5jp//ztsP1
PLC/3k/t9ucfv3Q4/6OH6z3c7le6613e6d31O5d3N5x+qbv97vXvBw/fz+H0a29Zbfty+cFH3vn4
f+0u7/72b+7+8q1/9K5dd/61X3z9T371qfdcO//DT/7iL/zLS9+x+8vnf/Yzb7rl8u7HX//+V7zg
tnftHtrdfevFFz68e/Q/f/O/3/3OB3av+fI9n3zgHz+w+7Vn/vSJZ1704O559z/62z90/6Xdd3b/
du9/PPWOa9f3ub96+clnXvqz187//ddffum3XnHfbuufxypOPrDb/8+bdt3jZX9/f/H+47vk9bEP
d59/4eHjh9eNrz2u2nFYdw77hcPP5+nLu9edPkC/d9/p4/D27vXfH0j2G4fXWa+9fpzPe7/+vfff
/rHLu7te/P0r/3Txtbsfe89d//7dp969u/DHz3v7S277ud0Tn37lN275/EO7Lz35zdd866Xv2f35
W//iDb//uUu7bz72xO/92R++9drj5H+/8gffeuplr8u+faE83d2fn7mvW1dOP/7V6+vKfn140f7x
8aF0v+913GV8+76ttwAArkvmtgDgXNYNAAAAAICYzKMAALXTqwEAAAAAYtKrAYjAegTAHOZSAAAA
AABi0gEBgFrp1AAAAAAAsenVAESQrkc6IwBjWDcAAAAAAGIylwIA1EqnBgAAAACISacGYEtpR7Qu
ATCH+RQAAAAAgJh0QACgNvo0AAAAAEBsOjUAkaTrkt4IwBjWDQAAAACAmMynAAC10acBAAAAAGLS
pwGIIO2J1icA5jCnAgAAAAAQkw4IANRClwYAAAAAiE2fBiCidH3SHQEYw7oBAAAAABCTORUAoBa6
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
AM2rmo+WbSdfBQAAAOiHrnOW4vHkPAAA7Up/bxWXAOxOvwMAAAAAQExyLgAi6TpXLB5PrgkAAAAA
0I1550eQawIAABCRfiMAAACgbfJygHp8fwIAAABdUYcAAAAAAIhNHRcAAMZr3uuTmU8IAAAAAADa
1/b16vQLAQAAUIV+MQAAAACAmPT/AMBObc+XIT8FAAAAAKin6XwzjSc3BQAAoA59QAAAAAAAMekH
AoAtTeeaaTx5KQAAAABAs9qaT6Gt8QEAABgmfUEAAAAAADHp/wGAndqaT6Gt8QEAAAAA+mqv8sqy
48pPAQAAmIc+IAAAAACAmPQBATBke5VTlh1XbgoAAAAAAADQjtuyyfKpbG/WD83cbv/G8vBkfX01
v/2OfJnWPz1ZZiv5+pfy7c/ny7P57Q/m26X9jm4+jmdueGM1e+HKdddWsz+/ct1b+XZX8/VX/n3y
xt1XPr+6ud/GYu1Cvr48GedXX82e3fjHg/k4ZyfjXskm68+t5OufyMfNj/t6GrfqeSu7/1T+PM8V
tt9aLt/w3ur08tV0/HT+zuXPJ1++8cWZj2/31ys/z1nh9VtPr9eJfPlkvnwo3z69bvnzWMuf13ra
7njJ80uP50D2sYuPPPfO9eP+5tfLrx340aOb63f+9e7PXr18ZnP9489//Wt/Ofhw9osP/f6l+/ev
Zp+6+/EjHz70aHY+Wz5w9OYL2dP//Na/Tj9yNjv+2j3fOfuHJ7JvXvvJpWu3nMtuOvX0d28/tZK9
l/3tvn9cfnhzvJd/eXj9pYNf3lz/7ZuHV7595GT+PC7m5+m+wnk8lt9feF/vW8pvXylZL77/lwrr
xwrbL+f3X8zX782X+ft4Pb2f79n9fF9/Xade5+L7cv2JfP3+LH1eJu/3W/PjFt7n30j33zx93M3P
VeH8XH//TI7/menntfl+W8o+2HBkx/sx3T7Zb+f61PZrhc956fdPfr73pcdROF+7jFs8f2l94zT8
biX/XsjPz4ur2V0bH9D3T258DpfS/h8tfG8cmz7+2unJdmuP5esX8v2+MH3e9n1u83FMjbNt/bE3
33986dnV7MStH6z96eid2SfPnPj7fy+fzvb9+KaHPnLoK9mlF+54a/8fz2evPv/28XcPnsl+9sDP
7/3+yyvZ289c+t5Pf/jA5ufkP6//4N3Lt921Y/z0Oky9HtvOy/TtZZ/3+X+Hiq//5PwVv4fLv2+n
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
"""
def load_lp():
raw = base64.b64decode(LP_DATA_B64)
data = np.load(io.BytesIO(raw))
bounds = list(zip(
[None if v == -np.inf else float(v) for v in data["bounds_lo"]],
[None if v == np.inf else float(v) for v in data["bounds_hi"]],
))
return (
data["c"], data["A_ub"], data["b_ub"],
bounds, data["integrality"].tolist(),
)
def expand_sc_to_binary(c, A_ub, b_ub, bounds, integrality):
"""
Rewrite each semi-continuous variable as continuous + binary indicator.
For column i with integrality[i] >= 2 and lo > 0:
- relax bounds[i] = (0, hi); integrality[i] -= 2 (SC->cont, semi-int->int)
- append a binary column b with bounds (0, 1)
- append two rows: A_new @ x <= 0
row 1: q - hi * b (i.e. q <= hi * b)
row 2: -q + lo * b (i.e. q >= lo * b)
"""
n_rows, n_cols = A_ub.shape
sc_cols = [
(i, lo, hi)
for i, ((lo, hi), integ) in enumerate(zip(bounds, integrality))
if integ >= 2 and lo is not None and lo > 0 and hi is not None
]
n_sc = len(sc_cols)
A2 = np.zeros((n_rows + 2 * n_sc, n_cols + n_sc))
A2[:n_rows, :n_cols] = A_ub
b2 = np.concatenate([b_ub, np.zeros(2 * n_sc)])
c2 = np.concatenate([c, np.zeros(n_sc)])
bounds2 = list(bounds) + [(0, 1)] * n_sc
integ2 = list(integrality) + [1] * n_sc # binaries
for k, (col, lo, hi) in enumerate(sc_cols):
bounds2[col] = (0, hi)
integ2[col] = integrality[col] - 2
b_idx = n_cols + k
A2[n_rows + 2 * k , col] = 1
A2[n_rows + 2 * k , b_idx] = -hi # q <= hi * b
A2[n_rows + 2 * k + 1, col] = -1
A2[n_rows + 2 * k + 1, b_idx] = lo # q >= lo * b
return c2, A2, b2, bounds2, integ2, n_sc
def main():
c, A_ub, b_ub, bounds, integrality = load_lp()
n_sc = sum(1 for v in integrality if v >= 2)
print(f"Loaded LP: {A_ub.shape[0]} rows x {A_ub.shape[1]} cols, "
f"{n_sc} semi-continuous variables.")
# === Original formulation ===
print("\n=== 1) Original formulation with semi-continuous vars ===")
print(" (set disp=True to see HiGHS internal 'row viol = 0' vs "
"post-check 'claims optimality with infeasibilities')")
res = linprog(
c, A_ub, b_ub, bounds=bounds, integrality=integrality,
options={"mip_rel_gap": 1e-7, "disp": False},
)
print(f" -> status = {res.status}")
print(f" -> message = {res.message!r}")
if res.success:
print(f" -> objective = {res.fun:.6f}")
# === Binary-indicator workaround ===
print("\n=== 2) Workaround: SC -> binary indicator + coupling rows ===")
c2, A2, b2, bounds2, integ2, n_added = expand_sc_to_binary(
c, A_ub, b_ub, bounds, integrality,
)
print(f" Added {n_added} binary columns and {2 * n_added} coupling rows.")
print(f" New shape: {A2.shape[0]} rows x {A2.shape[1]} cols.")
res2 = linprog(
c2, A2, b2, bounds=bounds2, integrality=integ2,
options={"mip_rel_gap": 1e-7, "disp": False},
)
print(f" -> status = {res2.status}")
print(f" -> message = {res2.message!r}")
if res2.success:
print(f" -> objective = {res2.fun:.6f}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment