blvpy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- blvpy/__init__.py +58 -0
- blvpy/backends.py +81 -0
- blvpy/canonicalization.py +1211 -0
- blvpy/cones.py +500 -0
- blvpy/continuation.py +1230 -0
- blvpy/diagnostics.py +288 -0
- blvpy/errors.py +92 -0
- blvpy/lower_problem.py +162 -0
- blvpy/problem.py +577 -0
- blvpy/progress.py +496 -0
- blvpy/result.py +620 -0
- blvpy-0.1.0.dist-info/METADATA +170 -0
- blvpy-0.1.0.dist-info/RECORD +15 -0
- blvpy-0.1.0.dist-info/WHEEL +4 -0
- blvpy-0.1.0.dist-info/licenses/LICENSE +201 -0
blvpy/__init__.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""BLVPY: disciplined optimistic bilevel optimization with CVXPY."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import version as _distribution_version
|
|
4
|
+
|
|
5
|
+
from .canonicalization import (
|
|
6
|
+
AffineRecoveryMap,
|
|
7
|
+
CanonicalData,
|
|
8
|
+
CanonicalExpressions,
|
|
9
|
+
CanonicalLowerProblem,
|
|
10
|
+
ParameterSpec,
|
|
11
|
+
RecoverySpec,
|
|
12
|
+
)
|
|
13
|
+
from .cones import ConeBlock, ConeLayout
|
|
14
|
+
from .errors import (
|
|
15
|
+
ApproximateCanonicalizationError,
|
|
16
|
+
BilevelError,
|
|
17
|
+
CanonicalizationError,
|
|
18
|
+
InitializationError,
|
|
19
|
+
ParameterMappingError,
|
|
20
|
+
SolveError,
|
|
21
|
+
SolverUnavailableError,
|
|
22
|
+
UnsupportedConeError,
|
|
23
|
+
UnsupportedModelError,
|
|
24
|
+
ValidationError,
|
|
25
|
+
)
|
|
26
|
+
from .lower_problem import LowerProblem
|
|
27
|
+
from .problem import BilevelProblem
|
|
28
|
+
from .result import BilevelResult, GapDiagnostics, IterationRecord, Residuals, RunRecord
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"ApproximateCanonicalizationError",
|
|
32
|
+
"AffineRecoveryMap",
|
|
33
|
+
"BilevelError",
|
|
34
|
+
"BilevelProblem",
|
|
35
|
+
"BilevelResult",
|
|
36
|
+
"CanonicalData",
|
|
37
|
+
"CanonicalExpressions",
|
|
38
|
+
"CanonicalLowerProblem",
|
|
39
|
+
"CanonicalizationError",
|
|
40
|
+
"ConeBlock",
|
|
41
|
+
"ConeLayout",
|
|
42
|
+
"GapDiagnostics",
|
|
43
|
+
"InitializationError",
|
|
44
|
+
"IterationRecord",
|
|
45
|
+
"LowerProblem",
|
|
46
|
+
"ParameterMappingError",
|
|
47
|
+
"ParameterSpec",
|
|
48
|
+
"RecoverySpec",
|
|
49
|
+
"Residuals",
|
|
50
|
+
"RunRecord",
|
|
51
|
+
"SolveError",
|
|
52
|
+
"SolverUnavailableError",
|
|
53
|
+
"UnsupportedConeError",
|
|
54
|
+
"UnsupportedModelError",
|
|
55
|
+
"ValidationError",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
__version__ = _distribution_version("blvpy")
|
blvpy/backends.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Internal CVXPY solver invocation adapters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import cvxpy as cp
|
|
9
|
+
|
|
10
|
+
from .errors import SolverUnavailableError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def solve_conic(
|
|
14
|
+
problem: cp.Problem,
|
|
15
|
+
solver: str,
|
|
16
|
+
options: Mapping[str, Any],
|
|
17
|
+
solver_verbose: bool,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Solve a conic problem through CVXPY's regular solve path."""
|
|
20
|
+
|
|
21
|
+
_solve(problem, solver, options, solver_verbose, nlp=False)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def solve_dnlp(
|
|
25
|
+
problem: cp.Problem,
|
|
26
|
+
solver: str,
|
|
27
|
+
options: Mapping[str, Any],
|
|
28
|
+
solver_verbose: bool,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Solve a DNLP problem through CVXPY's nonlinear solve path."""
|
|
31
|
+
|
|
32
|
+
_solve(problem, solver, options, solver_verbose, nlp=True)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _solve(
|
|
36
|
+
problem: cp.Problem,
|
|
37
|
+
solver: str,
|
|
38
|
+
options: Mapping[str, Any],
|
|
39
|
+
solver_verbose: bool,
|
|
40
|
+
*,
|
|
41
|
+
nlp: bool,
|
|
42
|
+
) -> None:
|
|
43
|
+
solve_options = dict(options)
|
|
44
|
+
if nlp and str(solver).upper() == "IPOPT" and not solver_verbose:
|
|
45
|
+
solve_options.setdefault("print_level", 0)
|
|
46
|
+
solve_options.setdefault("sb", "yes")
|
|
47
|
+
try:
|
|
48
|
+
if nlp:
|
|
49
|
+
problem.solve(
|
|
50
|
+
solver=solver,
|
|
51
|
+
nlp=True,
|
|
52
|
+
warm_start=True,
|
|
53
|
+
verbose=solver_verbose,
|
|
54
|
+
**solve_options,
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
problem.solve(
|
|
58
|
+
solver=solver,
|
|
59
|
+
warm_start=True,
|
|
60
|
+
verbose=solver_verbose,
|
|
61
|
+
**solve_options,
|
|
62
|
+
)
|
|
63
|
+
except cp.SolverError as error:
|
|
64
|
+
if "not installed" in str(error).lower():
|
|
65
|
+
raise _solver_unavailable_error(solver) from error
|
|
66
|
+
raise
|
|
67
|
+
except (ImportError, OSError) as error:
|
|
68
|
+
raise _solver_unavailable_error(solver, detail=str(error)) from error
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _solver_unavailable_error(solver: str, *, detail: str | None = None) -> SolverUnavailableError:
|
|
72
|
+
if str(solver).upper() == "IPOPT":
|
|
73
|
+
message = (
|
|
74
|
+
"IPOPT is not available. Install its native library, then reinstall "
|
|
75
|
+
"BLVPY so its required cyipopt binding can be built."
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
message = f"Requested solver {solver!r} is not installed or could not be loaded by CVXPY."
|
|
79
|
+
if detail:
|
|
80
|
+
message = f"{message} Native loading error: {detail}"
|
|
81
|
+
return SolverUnavailableError(message)
|