Skip to content

Commit

Permalink
Several significant updates:
Browse files Browse the repository at this point in the history
* Fixed all static analysis (strict) from pyright
* Updated typing across all highspy, including submodules
* Added numpy subclass HighspyArray. This allows for better typing, fast summation and vectorized linear expressions
* General cleanup, including fixes from ruff linting and formatting
* Added overload functions for improved intellisense
* Additional checks, tests, and code coverage
* Slight updates to cpp bindings to ensure types are available
  • Loading branch information
mathgeekcoder committed Sep 21, 2024
1 parent 370156f commit 536983f
Show file tree
Hide file tree
Showing 12 changed files with 3,342 additions and 1,906 deletions.
2 changes: 1 addition & 1 deletion examples/network_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# sum(out) - sum(in) = { -1 if n = dest
# { 0 otherwise
rhs = lambda n: 1 if n == orig else -1 if n == dest else 0
flow = lambda E: sum((x[e] for e in E))
flow = lambda E: h.qsum((x[e] for e in E))

h.addConstrs(flow(G.out_edges(n)) - flow(G.in_edges(n)) == rhs(n) for n in G.nodes)
h.minimize()
Expand Down
8 changes: 4 additions & 4 deletions examples/nqueens.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@

x = h.addBinaries(N, N)

h.addConstrs(h.qsum(x[i,:]) == 1 for i in range(N)) # each row has exactly one queen
h.addConstrs(h.qsum(x[:,j]) == 1 for j in range(N)) # each col has exactly one queen
h.addConstrs(x.sum(axis=0) == 1) # each row has exactly one queen
h.addConstrs(x.sum(axis=1) == 1) # each col has exactly one queen

y = np.fliplr(x)
h.addConstrs(h.qsum(x.diagonal(k)) <= 1 for k in range(-N + 1, N)) # each diagonal has at most one queen
h.addConstrs(h.qsum(y.diagonal(k)) <= 1 for k in range(-N + 1, N)) # each 'reverse' diagonal has at most one queen
h.addConstrs(x.diagonal(k).sum() <= 1 for k in range(-N + 1, N)) # each diagonal has at most one queen
h.addConstrs(y.diagonal(k).sum() <= 1 for k in range(-N + 1, N)) # each 'reverse' diagonal has at most one queen

h.solve()
sol = h.vals(x)
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Typing :: Typed",
]

[project.optional-dependencies]
Expand All @@ -44,7 +45,9 @@ wheel.packages = ["src/highspy"]
# gitignore syntax.
sdist.include = [
"src/highspy/highs.py",
"src/highspy/_core.pyi",
"src/highspy/__init__.py",
"src/highspy/__init__.pyi",
"src/highspy/_core/*.pyi",
"tests/test_highspy.py",
"Version.txt",
"LICENSE",
Expand Down Expand Up @@ -188,7 +191,6 @@ log_cli_level = "INFO"
filterwarnings = ["error"]
testpaths = ["tests"]


[tool.cibuildwheel]
build = "*"
skip = "cp3{6,7}-*"
Expand Down
16 changes: 11 additions & 5 deletions src/highs_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,13 +314,17 @@ HighsStatus highs_changeColsIntegrality(

// Same as deleteVars
HighsStatus highs_deleteCols(Highs* h, HighsInt num_set_entries,
std::vector<HighsInt>& indices) {
return h->deleteCols(num_set_entries, indices.data());
dense_array_t<HighsInt> indices) {
py::buffer_info index_info = indices.request();
HighsInt* index_ptr = reinterpret_cast<HighsInt*>(index_info.ptr);
return h->deleteCols(num_set_entries, index_ptr);
}

HighsStatus highs_deleteRows(Highs* h, HighsInt num_set_entries,
std::vector<HighsInt>& indices) {
return h->deleteRows(num_set_entries, indices.data());
dense_array_t<HighsInt> indices) {
py::buffer_info index_info = indices.request();
HighsInt* index_ptr = reinterpret_cast<HighsInt*>(index_info.ptr);
return h->deleteRows(num_set_entries, index_ptr);
}

HighsStatus highs_setSolution(Highs* h, HighsSolution& solution) {
Expand Down Expand Up @@ -748,6 +752,8 @@ PYBIND11_MODULE(_core, m) {
.def_readwrite("p_end_", &HighsSparseMatrix::p_end_)
.def_readwrite("index_", &HighsSparseMatrix::index_)
.def_readwrite("value_", &HighsSparseMatrix::value_);
py::class_<HighsLpMods>(m, "HighsLpMods");
py::class_<HighsScale>(m, "HighsScale");
py::class_<HighsLp>(m, "HighsLp")
.def(py::init<>())
.def_readwrite("num_col_", &HighsLp::num_col_)
Expand Down Expand Up @@ -928,7 +934,7 @@ PYBIND11_MODULE(_core, m) {
.def("postsolve", &highs_postsolve)
.def("postsolve", &highs_mipPostsolve)
.def("run", &Highs::run, py::call_guard<py::gil_scoped_release>())
.def("resetGlobalScheduler", &Highs::resetGlobalScheduler)
.def_static("resetGlobalScheduler", &Highs::resetGlobalScheduler)
.def(
"feasibilityRelaxation",
[](Highs& self, double global_lower_penalty,
Expand Down
282 changes: 86 additions & 196 deletions src/highspy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,199 +1,89 @@
# from __future__ import annotations

from ._core import \
ObjSense, \
MatrixFormat, \
HessianFormat, \
SolutionStatus, \
BasisValidity, \
HighsModelStatus, \
HighsPresolveStatus, \
HighsBasisStatus, \
HighsVarType, \
HighsOptionType, \
HighsInfoType, \
HighsStatus, \
HighsLogType, \
IisStrategy, \
IisBoundStatus, \
HighsSparseMatrix, \
HighsLp, \
HighsHessian, \
HighsModel, \
HighsInfo, \
HighsOptions, \
_Highs, \
HighsIis, \
HighsSolution, \
HighsObjectiveSolution, \
HighsBasis, \
HighsRangingRecord, \
HighsRanging, \
kHighsInf, \
kHighsIInf, \
HIGHS_VERSION_MAJOR, \
HIGHS_VERSION_MINOR, \
HIGHS_VERSION_PATCH, \
simplex_constants, \
cb, \
kSolutionStatusNone, \
kSolutionStatusInfeasible, \
kSolutionStatusFeasible, \
kBasisValidityInvalid, \
kBasisValidityValid

# kMaximize, \
# kColwise, \
# kRowwise, \
# kRowwisePartitioned, \
# kTriangular, \
# kSquare, \

# kNotset, \
# kLoadError, \
# kModelError, \
# kPresolveError, \
# kSolveError, \
# kPostsolveError, \
# kModelEmpty, \
# kOptimal, \
# kInfeasible, \
# kUnboundedOrInfeasible, \
# kUnbounded, \
# kObjectiveBound, \
# kObjectiveTarget, \
# kTimeLimit, \
# kUnknown, \
# kSolutionLimit, \
# kInterrupt, \
# kMemoryLimit, \
# kNotPresolved, \
# kNotReduced, \
# kInfeasible, \
# kUnboundedOrInfeasible, \
# kReduced, \
# kReducedToEmpty, \
# kTimeout, \
# kNullError, \
# kOptionsError, \
# kOutOfMemory, \
# kLower, \
# kBasic, \
# kUpper, \
# kZero, \
# kNonbasic, \
# kContinuous, \
# kInteger, \
# kSemiContinuous, \
# kSemiInteger, \
# kBool, \
# kInt, \
# kDouble, \
# , \
# , \
# , \
# , \
# , \
# , \
# , \
from ._core import (
ObjSense,
MatrixFormat,
HessianFormat,
SolutionStatus,
BasisValidity,
HighsModelStatus,
HighsPresolveStatus,
HighsBasisStatus,
HighsVarType,
HighsOptionType,
HighsInfoType,
HighsStatus,
HighsLogType,
IisStrategy,
IisBoundStatus,
HighsSparseMatrix,
HighsLp,
HighsHessian,
HighsModel,
HighsInfo,
HighsOptions,
_Highs, # type: ignore
HighsIis,
HighsSolution,
HighsObjectiveSolution,
HighsBasis,
HighsRangingRecord,
HighsRanging,
kHighsInf,
kHighsIInf,
HIGHS_VERSION_MAJOR,
HIGHS_VERSION_MINOR,
HIGHS_VERSION_PATCH,
simplex_constants, # type: ignore
cb, # type: ignore
kSolutionStatusNone,
kSolutionStatusInfeasible,
kSolutionStatusFeasible,
kBasisValidityInvalid,
kBasisValidityValid,
)

from .highs import Highs

__all__ = ["__doc__",
"__version__",
"ObjSense",
"MatrixFormat",
"HessianFormat",
"SolutionStatus",
"BasisValidity",
"HighsModelStatus",
"HighsPresolveStatus",
"HighsBasisStatus",
"HighsVarType",
"HighsOptionType",
"HighsInfoType",
"HighsStatus",
"HighsLogType",
"IisStrategy",
"IisBoundStatus",
"HighsSparseMatrix",
"HighsLp",
"HighsHessian",
"HighsModel",
"HighsInfo",
"HighsOptions",
"_Highs",
"Highs",
"HighsIis",
"HighsSolution",
"HighsObjectiveSolution",
"HighsBasis",
"HighsRangingRecord",
"HighsRanging",
"kHighsInf",
"kHighsIInf",
"HIGHS_VERSION_MAJOR",
"HIGHS_VERSION_MINOR",
"HIGHS_VERSION_PATCH",
"simplex_constants",
"cb",
# "kMinimize",
# "kMaximize",
# "kColwise",
# "kRowwise",
# "kRowwisePartitioned",
# "kTriangular",
# "kSquare",
"kSolutionStatusNone",
"kSolutionStatusInfeasible",
"kSolutionStatusFeasible",
"kBasisValidityInvalid",
"kBasisValidityValid",
# "kNotset",
# "kLoadError",
# "kModelError",
# "kPresolveError",
# "kSolveError",
# "kPostsolveError",
# "kModelEmpty",
# "kOptimal",
# "kInfeasible",
# "kUnboundedOrInfeasible",
# "kUnbounded",
# "kObjectiveBound",
# "kObjectiveTarget",
# "kTimeLimit",
# "kUnknown",
# "kSolutionLimit",
# "kInterrupt",
# "kMemoryLimit",
# "kNotPresolved",
# "kNotReduced",
# "kInfeasible",
# "kUnboundedOrInfeasible",
# "kReduced",
# "kReducedToEmpty",
# "kTimeout",
# "kNullError",
# "kOptionsError",
# "kOutOfMemory",
# "kLower",
# "kBasic",
# "kUpper",
# "kZero",
# "kNonbasic",
# "kContinuous",
# "kInteger",
# "kSemiContinuous",
# "kSemiInteger",
# "kBool",
# "kInt",
# "kDouble",
# "",
# "",
# "",
# "",
# "",
# "",
# "",
]
__all__ = [
"__doc__",
"ObjSense",
"MatrixFormat",
"HessianFormat",
"SolutionStatus",
"BasisValidity",
"HighsModelStatus",
"HighsPresolveStatus",
"HighsBasisStatus",
"HighsVarType",
"HighsOptionType",
"HighsInfoType",
"HighsStatus",
"HighsLogType",
"IisStrategy",
"IisBoundStatus",
"HighsSparseMatrix",
"HighsLp",
"HighsHessian",
"HighsModel",
"HighsInfo",
"HighsOptions",
"_Highs",
"Highs",
"HighsIis",
"HighsSolution",
"HighsObjectiveSolution",
"HighsBasis",
"HighsRangingRecord",
"HighsRanging",
"kHighsInf",
"kHighsIInf",
"HIGHS_VERSION_MAJOR",
"HIGHS_VERSION_MINOR",
"HIGHS_VERSION_PATCH",
"simplex_constants",
"cb",
"kSolutionStatusNone",
"kSolutionStatusInfeasible",
"kSolutionStatusFeasible",
"kBasisValidityInvalid",
"kBasisValidityValid",
]
Loading

0 comments on commit 536983f

Please sign in to comment.