Skip to content

Commit c7cdea9

Browse files
h-g-sCopilot
andcommitted
fix: use highsbox as fallback for HiGHS C API on Windows
highspy's _core.pyd on Windows does not export C symbols (only PyInit_* is exported from a Windows DLL by default). On Linux/macOS the .so exports all symbols so ffi.dlopen + Highs_create works fine; on Windows it raises AttributeError. Changes: - After dlopen succeeds, probe Highs_create accessibility. If it fails (Windows + highspy), automatically retry with highsbox's highs.dll. - highsbox is added as a Windows-only optional dependency in [highs]: highsbox>=1.9.0; sys_platform == 'win32' so 'pip install mip[highs]' on Windows installs both highspy (Python bindings) and highsbox (C API DLL), giving full HiGHS functionality. - Hoist the highsbox path resolver to a module-level helper function _get_highsbox_libfile() so it is reachable both at initial load and at the fallback probe point. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 57f1881 commit c7cdea9

2 files changed

Lines changed: 41 additions & 25 deletions

File tree

mip/highs.py

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@
1414

1515
# try loading the solver library
1616
ffi = cffi.FFI()
17+
18+
19+
def _get_highsbox_libfile():
20+
"""Return the path to the highsbox HiGHS shared library."""
21+
import highsbox
22+
23+
root = highsbox.highs_dist_dir()
24+
platform = sys.platform.lower()
25+
if "linux" in platform:
26+
return os.path.join(root, "lib", "libhighs.so")
27+
elif platform.startswith("win"):
28+
return os.path.join(root, "bin", "highs.dll")
29+
elif any(platform.startswith(p) for p in ("darwin", "macos")):
30+
return os.path.join(root, "lib", "libhighs.dylib")
31+
else:
32+
raise NotImplementedError(f"{sys.platform} not supported!")
33+
34+
1735
try:
1836
ENV_KEY = "PMIP_HIGHS_LIBRARY"
1937
if ENV_KEY in os.environ:
@@ -25,26 +43,14 @@
2543
# contains the full HiGHS C API. On Linux/macOS all symbols are
2644
# visible in the shared library; on Windows only the Python init
2745
# symbol is exported from the .pyd, so the C API is not accessible
28-
# via dlopen there. We detect that below after loading.
46+
# via dlopen there. We detect that below and fall back to highsbox.
2947
try:
3048
import highspy._core as _highs_core
3149

3250
libfile = _highs_core.__file__
3351
logger.debug(f"Choosing HiGHS library {libfile} via highspy package.")
3452
except ImportError:
35-
# Fall back to highsbox (third-party binary distribution).
36-
import highsbox
37-
38-
root = highsbox.highs_dist_dir()
39-
platform = sys.platform.lower()
40-
if "linux" in platform:
41-
libfile = os.path.join(root, "lib", "libhighs.so")
42-
elif platform.startswith("win"):
43-
libfile = os.path.join(root, "bin", "highs.dll")
44-
elif any(platform.startswith(p) for p in ("darwin", "macos")):
45-
libfile = os.path.join(root, "lib", "libhighs.dylib")
46-
else:
47-
raise NotImplementedError(f"{sys.platform} not supported!")
53+
libfile = _get_highsbox_libfile()
4854
logger.debug(f"Choosing HiGHS library {libfile} via highsbox package.")
4955

5056
highslib = ffi.dlopen(libfile)
@@ -682,20 +688,30 @@
682688
)
683689

684690
# On Windows, highspy's _core.pyd does not export C symbols (only the
685-
# Python init function is exported from a .pyd). Verify the C API is
686-
# actually accessible; if not, disable HiGHS gracefully.
691+
# Python init function is exported from a .pyd). Detect this and
692+
# automatically fall back to highsbox, which ships a proper highs.dll.
687693
try:
688694
_ = highslib.Highs_create
689695
except AttributeError:
690-
logger.error(
691-
"HiGHS C API symbols not accessible in the loaded library "
692-
f"({libfile!r}). "
693-
"This typically happens on Windows with the highspy package "
694-
"because its .pyd does not export C symbols. "
695-
"Install highsbox for Windows support, or set PMIP_HIGHS_LIBRARY "
696-
"to point to a highs.dll that exports the C API."
696+
logger.warning(
697+
f"HiGHS C API not accessible via {libfile!r} "
698+
"(typical on Windows with highspy). "
699+
"Falling back to highsbox."
697700
)
698-
has_highs = False
701+
try:
702+
libfile = _get_highsbox_libfile()
703+
highslib = ffi.dlopen(libfile)
704+
_ = highslib.Highs_create # verify symbols are accessible
705+
logger.debug(
706+
f"Choosing HiGHS library {libfile} via highsbox package (fallback)."
707+
)
708+
except Exception as e:
709+
logger.error(
710+
f"highsbox fallback also failed: {e}. "
711+
"HiGHS will not be available. "
712+
"Install highsbox (pip install highsbox) for HiGHS support on Windows."
713+
)
714+
has_highs = False
699715

700716
if has_highs:
701717
STATUS_ERROR = highslib.kHighsStatusError

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ numpy = [
4141
"numpy>=1.25,<3",
4242
]
4343
gurobi = ["gurobipy>=10"]
44-
highs = ["highspy>=1.9.0"]
44+
highs = ["highspy>=1.9.0", "highsbox>=1.9.0; sys_platform == 'win32'"]
4545
test = [
4646
"pytest>=7.4,<9",
4747
"networkx>=2.8.8,<4",

0 commit comments

Comments
 (0)