feat: Add CVXOPT solver infrastructure and VSCode settings
- Add CVXOPT dependency to pyproject.toml and uv.lock - Create solver module with GLPK-based integer linear programming solver - Add VSCode Python analysis settings - Implement matrix and sparse matrix wrappers for CVXOPT - Add GLPK solver wrapper with type-safe interfaces
This commit is contained in:
249
app/solver/_wrap/glpk.py
Normal file
249
app/solver/_wrap/glpk.py
Normal file
@ -0,0 +1,249 @@
|
||||
"""
|
||||
See also:
|
||||
https://github.com/cvxopt/cvxopt/blob/master/src/C/glpk.c
|
||||
"""
|
||||
|
||||
from typing import Tuple, Union, Literal, Optional, Dict, Any, Set, overload, TypedDict
|
||||
from cvxopt import glpk # type: ignore
|
||||
from . import Matrix, SparseMatrix
|
||||
|
||||
|
||||
CvxMatLike = Union[Matrix, SparseMatrix]
|
||||
CvxBool = Literal["GLP_ON", "GLP_OFF"]
|
||||
|
||||
|
||||
class GLPKOptions(TypedDict, total=False):
|
||||
# Common parameters
|
||||
msg_lev: Literal["GLP_MSG_OFF", "GLP_MSG_ERR", "GLP_MSG_ON", "GLP_MSG_ALL"]
|
||||
presolve: CvxBool
|
||||
tm_lim: int
|
||||
out_frq: int
|
||||
out_dly: int
|
||||
# LP-specific parameters
|
||||
meth: Literal["GLP_PRIMAL", "GLP_DUAL", "GLP_DUALP"]
|
||||
pricing: Literal["GLP_PT_STD", "GLP_PT_PSE"]
|
||||
r_test: Literal["GLP_RT_STD", "GLP_RT_HAR"]
|
||||
tol_bnd: float
|
||||
tol_dj: float
|
||||
tol_piv: float
|
||||
obj_ll: float
|
||||
obj_ul: float
|
||||
it_lim: int
|
||||
# MILP-specific parameters
|
||||
br_tech: Literal[
|
||||
"GLP_BR_FFV", "GLP_BR_LFV", "GLP_BR_MFV", "GLP_BR_DTH", "GLP_BR_PCH"
|
||||
]
|
||||
bt_tech: Literal["GLP_BT_DFS", "GLP_BT_BFS", "GLP_BT_BLB", "GLP_BT_BPH"]
|
||||
pp_tech: Literal["GLP_PP_NONE", "GLP_PP_ROOT", "GLP_PP_ALL"]
|
||||
fp_heur: CvxBool
|
||||
gmi_cuts: CvxBool
|
||||
mir_cuts: CvxBool
|
||||
cov_cuts: CvxBool
|
||||
clq_cuts: CvxBool
|
||||
tol_int: float
|
||||
tol_obj: float
|
||||
mip_gap: float
|
||||
cb_size: int
|
||||
binarize: CvxBool
|
||||
|
||||
|
||||
StatusLP = Literal["optimal", "primal infeasible", "dual infeasible", "unknown"]
|
||||
StatusILP = Literal[
|
||||
"optimal",
|
||||
"feasible",
|
||||
"undefined",
|
||||
"invalid formulation",
|
||||
"infeasible problem",
|
||||
"LP relaxation is primal infeasible",
|
||||
"LP relaxation is dual infeasible",
|
||||
"unknown",
|
||||
]
|
||||
|
||||
|
||||
@overload
|
||||
def lp(
|
||||
c: Matrix,
|
||||
G: CvxMatLike,
|
||||
h: Matrix,
|
||||
) -> Tuple[StatusLP, Optional[Matrix], Optional[Matrix]]:
|
||||
"""
|
||||
(status, x, z) = lp(c, G, h)
|
||||
|
||||
PURPOSE
|
||||
(status, x, z) = lp(c, G, h) solves the pair of primal and dual LPs
|
||||
|
||||
minimize c'*x maximize -h'*z
|
||||
subject to G*x <= h subject to G'*z + c = 0
|
||||
z >= 0.
|
||||
|
||||
ARGUMENTS
|
||||
c nx1 dense 'd' matrix with n>=1
|
||||
|
||||
G mxn dense or sparse 'd' matrix with m>=1
|
||||
|
||||
h mx1 dense 'd' matrix
|
||||
|
||||
status 'optimal', 'primal infeasible', 'dual infeasible'
|
||||
or 'unknown'
|
||||
|
||||
x if status is 'optimal', a primal optimal solution;
|
||||
None otherwise
|
||||
|
||||
z if status is 'optimal', the dual optimal solution;
|
||||
None otherwise
|
||||
"""
|
||||
|
||||
|
||||
@overload
|
||||
def lp(
|
||||
c: Matrix,
|
||||
G: CvxMatLike,
|
||||
h: Matrix,
|
||||
A: CvxMatLike,
|
||||
b: Matrix,
|
||||
) -> Tuple[StatusLP, Optional[Matrix], Optional[Matrix], Optional[Matrix]]:
|
||||
"""
|
||||
(status, x, z, y) = lp(c, G, h, A, b)
|
||||
|
||||
PURPOSE
|
||||
(status, x, z, y) = lp(c, G, h, A, b) solves the pair of primal and
|
||||
dual LPs
|
||||
|
||||
minimize c'*x maximize -h'*z + b'*y
|
||||
subject to G*x <= h subject to G'*z + A'*y + c = 0
|
||||
A*x = b z >= 0.
|
||||
|
||||
|
||||
ARGUMENTS
|
||||
c nx1 dense 'd' matrix with n>=1
|
||||
|
||||
G mxn dense or sparse 'd' matrix with m>=1
|
||||
|
||||
h mx1 dense 'd' matrix
|
||||
|
||||
A pxn dense or sparse 'd' matrix with p>=0
|
||||
|
||||
b px1 dense 'd' matrix
|
||||
|
||||
status 'optimal', 'primal infeasible', 'dual infeasible'
|
||||
or 'unknown'
|
||||
|
||||
x if status is 'optimal', a primal optimal solution;
|
||||
None otherwise
|
||||
|
||||
z,y if status is 'optimal', the dual optimal solution;
|
||||
None otherwise
|
||||
"""
|
||||
|
||||
|
||||
# https://cvxopt.org/userguide/coneprog.html#linear-programming
|
||||
|
||||
|
||||
def lp(
|
||||
c: Matrix,
|
||||
G: CvxMatLike,
|
||||
h: Matrix,
|
||||
A: Optional[CvxMatLike] = None,
|
||||
b: Optional[Matrix] = None,
|
||||
):
|
||||
"""
|
||||
(status, x, z, y) = lp(c, G, h, A, b)
|
||||
(status, x, z) = lp(c, G, h)
|
||||
|
||||
PURPOSE
|
||||
(status, x, z, y) = lp(c, G, h, A, b) solves the pair of primal and
|
||||
dual LPs
|
||||
|
||||
minimize c'*x maximize -h'*z + b'*y
|
||||
subject to G*x <= h subject to G'*z + A'*y + c = 0
|
||||
A*x = b z >= 0.
|
||||
|
||||
(status, x, z) = lp(c, G, h) solves the pair of primal and dual LPs
|
||||
|
||||
minimize c'*x maximize -h'*z
|
||||
subject to G*x <= h subject to G'*z + c = 0
|
||||
z >= 0.
|
||||
|
||||
ARGUMENTS
|
||||
c nx1 dense 'd' matrix with n>=1
|
||||
|
||||
G mxn dense or sparse 'd' matrix with m>=1
|
||||
|
||||
h mx1 dense 'd' matrix
|
||||
|
||||
A pxn dense or sparse 'd' matrix with p>=0
|
||||
|
||||
b px1 dense 'd' matrix
|
||||
|
||||
status 'optimal', 'primal infeasible', 'dual infeasible'
|
||||
or 'unknown'
|
||||
|
||||
x if status is 'optimal', a primal optimal solution;
|
||||
None otherwise
|
||||
|
||||
z,y if status is 'optimal', the dual optimal solution;
|
||||
None otherwise
|
||||
"""
|
||||
if A is None and b is None:
|
||||
return glpk.lp(c, G, h)
|
||||
return glpk.lp(c, G, h, A, b)
|
||||
|
||||
|
||||
def ilp(
|
||||
c: Matrix,
|
||||
G: CvxMatLike,
|
||||
h: Matrix,
|
||||
A: Optional[CvxMatLike] = None,
|
||||
b: Optional[Matrix] = None,
|
||||
I: Optional[Set[int]] = None,
|
||||
B: Optional[Set[int]] = None,
|
||||
) -> Tuple[StatusILP, Optional[Matrix]]:
|
||||
"""
|
||||
Solves a mixed integer linear program using GLPK.
|
||||
|
||||
(status, x) = ilp(c, G, h, A, b, I, B)
|
||||
|
||||
PURPOSE
|
||||
Solves the mixed integer linear programming problem
|
||||
|
||||
minimize c'*x
|
||||
subject to G*x <= h
|
||||
A*x = b
|
||||
x[k] is integer for k in I
|
||||
x[k] is binary for k in B
|
||||
|
||||
ARGUMENTS
|
||||
c nx1 dense 'd' matrix with n>=1
|
||||
|
||||
G mxn dense or sparse 'd' matrix with m>=1
|
||||
|
||||
h mx1 dense 'd' matrix
|
||||
|
||||
A pxn dense or sparse 'd' matrix with p>=0
|
||||
|
||||
b px1 dense 'd' matrix
|
||||
|
||||
I set of indices of integer variables
|
||||
|
||||
B set of indices of binary variables
|
||||
|
||||
status if status is 'optimal', 'feasible', or 'undefined',
|
||||
a value of x is returned and the status string
|
||||
gives the status of x. Other possible values of
|
||||
status are: 'invalid formulation',
|
||||
'infeasible problem', 'LP relaxation is primal
|
||||
infeasible', 'LP relaxation is dual infeasible',
|
||||
'unknown'.
|
||||
|
||||
x a (sub-)optimal solution if status is 'optimal',
|
||||
'feasible', or 'undefined'. None otherwise
|
||||
"""
|
||||
return glpk.ilp(c, G, h, A, b, I, B)
|
||||
|
||||
|
||||
def set_global_options(options: GLPKOptions) -> None:
|
||||
glpk.options = options
|
||||
|
||||
|
||||
def get_global_options() -> GLPKOptions:
|
||||
return glpk.options
|
||||
Reference in New Issue
Block a user