Skip to content

Commit 3c47d89

Browse files
committed
optimize: have linprog return a specialized OptimizeResult
1 parent a308cab commit 3c47d89

File tree

3 files changed

+41
-34
lines changed

3 files changed

+41
-34
lines changed

scipy-stubs/optimize/_linprog.pyi

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from collections.abc import Callable, Sequence
2-
from typing import Final, Literal, LiteralString, TypeAlias, TypedDict, overload, type_check_only
2+
from typing import Final, Literal, TypeAlias, TypedDict, overload, type_check_only
33
from typing_extensions import deprecated
44

55
import numpy as np
@@ -15,11 +15,9 @@ __all__ = ["linprog", "linprog_terse_callback", "linprog_verbose_callback"]
1515

1616
_Ignored: TypeAlias = object
1717
_Max3: TypeAlias = Literal[0, 1, 2, 3]
18-
_Max4: TypeAlias = Literal[_Max3, 4]
1918

2019
_Int: TypeAlias = int | np.int32 | np.int64
2120
_Float: TypeAlias = float | np.float64
22-
_Float1D: TypeAlias = onp.Array1D[np.float64]
2321

2422
@type_check_only
2523
class _OptionsCommon(TypedDict, total=False):
@@ -83,17 +81,17 @@ __docformat__: Final = "restructuredtext en" # undocumented
8381
LINPROG_METHODS: Final[Sequence[MethodLinprog | MethodLinprogLegacy]] = ... # undocumented
8482

8583
class OptimizeResult(_OptimizeResult):
86-
x: _Float1D # minimizing decision variables w.r.t. the constraints
87-
fun: _Float # optimal objective function value
88-
slack: _Float1D # slack values; nominally positive
89-
con: _Float1D # residuals of equality constraints; nominally zero
90-
status: _Max4
91-
message: LiteralString
92-
nit: int # >=0
93-
success: bool # `success = status == 0`
94-
95-
def linprog_verbose_callback(res: _OptimizeResult) -> None: ...
96-
def linprog_terse_callback(res: _OptimizeResult) -> None: ...
84+
x: onp.Array1D[np.float64] | None
85+
fun: float | None
86+
slack: onp.Array1D[np.float64] | None
87+
con: onp.Array1D[np.float64] | None
88+
success: bool
89+
status: Literal[0, 1, 2, 3, 4]
90+
message: str
91+
nit: int
92+
93+
def linprog_verbose_callback(res: OptimizeResult) -> None: ...
94+
def linprog_terse_callback(res: OptimizeResult) -> None: ...
9795

9896
#
9997
@overload # highs (default)
@@ -105,11 +103,11 @@ def linprog(
105103
b_eq: onp.ToFloat1D | None = None,
106104
bounds: Bound | Sequence[Bound] = (0, None),
107105
method: Literal["highs"] = "highs",
108-
callback: Callable[[_OptimizeResult], _Ignored] | None = None,
106+
callback: Callable[[OptimizeResult], _Ignored] | None = None,
109107
options: _OptionsHighs | None = None,
110108
x0: onp.ToFloat1D | None = None,
111109
integrality: _Max3 | Sequence[_Max3] | onp.CanArrayND[npc.integer] | None = None,
112-
) -> _OptimizeResult: ...
110+
) -> OptimizeResult: ...
113111
@overload # highs-ds
114112
def linprog(
115113
c: onp.ToFloat1D,
@@ -120,11 +118,11 @@ def linprog(
120118
bounds: Bound | Sequence[Bound] = (0, None),
121119
*,
122120
method: Literal["highs-ds"],
123-
callback: Callable[[_OptimizeResult], _Ignored] | None = None,
121+
callback: Callable[[OptimizeResult], _Ignored] | None = None,
124122
options: _OptionsHighsDS | None = None,
125123
x0: onp.ToFloat1D | None = None,
126124
integrality: _Max3 | Sequence[_Max3] | onp.CanArrayND[npc.integer] | None = None,
127-
) -> _OptimizeResult: ...
125+
) -> OptimizeResult: ...
128126
@overload # highs-ipm
129127
def linprog(
130128
c: onp.ToFloat1D,
@@ -135,11 +133,11 @@ def linprog(
135133
bounds: Bound | Sequence[Bound] = (0, None),
136134
*,
137135
method: Literal["highs-ipm"],
138-
callback: Callable[[_OptimizeResult], _Ignored] | None = None,
136+
callback: Callable[[OptimizeResult], _Ignored] | None = None,
139137
options: _OptionsHighsIPM | None = None,
140138
x0: onp.ToFloat1D | None = None,
141139
integrality: _Max3 | Sequence[_Max3] | onp.CanArrayND[npc.integer] | None = None,
142-
) -> _OptimizeResult: ...
140+
) -> OptimizeResult: ...
143141
@overload # interior-point (legacy, see https://github.com/scipy/scipy/issues/15707)
144142
@deprecated("`method='interior-point'` is deprecated and will be removed in SciPy 1.17. Please use one of the HIGHS solvers.")
145143
def linprog(
@@ -151,11 +149,11 @@ def linprog(
151149
bounds: Bound | Sequence[Bound] = (0, None),
152150
*,
153151
method: Literal["interior-point"],
154-
callback: Callable[[_OptimizeResult], _Ignored] | None = None,
152+
callback: Callable[[OptimizeResult], _Ignored] | None = None,
155153
options: _OptionsInteriorPoint | None = None,
156154
x0: onp.ToFloat1D | None = None,
157155
integrality: _Max3 | Sequence[_Max3] | onp.CanArrayND[npc.integer] | None = None,
158-
) -> _OptimizeResult: ...
156+
) -> OptimizeResult: ...
159157
@overload # revised simplex (legacy, see https://github.com/scipy/scipy/issues/15707)
160158
@deprecated("`method='revised simplex'` is deprecated and will be removed in SciPy 1.17. Please use one of the HIGHS solvers.")
161159
def linprog(
@@ -167,11 +165,11 @@ def linprog(
167165
bounds: Bound | Sequence[Bound] = (0, None),
168166
*,
169167
method: Literal["revised simplex"],
170-
callback: Callable[[_OptimizeResult], _Ignored] | None = None,
168+
callback: Callable[[OptimizeResult], _Ignored] | None = None,
171169
options: _OptionsRevisedSimplex | None = None,
172170
x0: onp.ToFloat1D | None = None,
173171
integrality: _Max3 | Sequence[_Max3] | onp.CanArrayND[npc.integer] | None = None,
174-
) -> _OptimizeResult: ...
172+
) -> OptimizeResult: ...
175173
@overload # simplex (legacy, see https://github.com/scipy/scipy/issues/15707)
176174
@deprecated("`method='simplex'` is deprecated and will be removed in SciPy 1.17. Please use one of the HIGHS solvers.")
177175
def linprog(
@@ -183,11 +181,11 @@ def linprog(
183181
bounds: Bound | Sequence[Bound] = (0, None),
184182
*,
185183
method: Literal["simplex"],
186-
callback: Callable[[_OptimizeResult], _Ignored] | None = None,
184+
callback: Callable[[OptimizeResult], _Ignored] | None = None,
187185
options: _OptionsSimplex | None = None,
188186
x0: onp.ToFloat1D | None = None,
189187
integrality: _Max3 | Sequence[_Max3] | onp.CanArrayND[npc.integer] | None = None,
190-
) -> _OptimizeResult: ...
188+
) -> OptimizeResult: ...
191189
@overload # any "highs"
192190
def linprog(
193191
c: onp.ToFloat1D,
@@ -197,8 +195,8 @@ def linprog(
197195
b_eq: onp.ToFloat1D | None = None,
198196
bounds: Bound | Sequence[Bound] = (0, None),
199197
method: MethodLinprog = "highs",
200-
callback: Callable[[_OptimizeResult], _Ignored] | None = None,
198+
callback: Callable[[OptimizeResult], _Ignored] | None = None,
201199
options: _OptionsHighs | None = None,
202200
x0: onp.ToFloat1D | None = None,
203201
integrality: _Max3 | Sequence[_Max3] | onp.CanArrayND[npc.integer] | None = None,
204-
) -> _OptimizeResult: ...
202+
) -> OptimizeResult: ...

tests/.ruff.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
extend = "../pyproject.toml"
22

33
[lint]
4-
extend-ignore = ["B018", "PYI015", "PYI017"]
4+
extend-ignore = ["B018", "PYI002", "PYI015", "PYI017"]

tests/optimize/test_linprog.pyi

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
from collections.abc import Sequence
22
from typing import assert_type
33

4+
import numpy as np
5+
46
from scipy.optimize import OptimizeResult, linprog
57
from scipy.optimize._typing import Bound
68

79
c: list[float]
810

911
bound: Bound
10-
uniformly_bounded = linprog(c, bounds=bound)
11-
assert_type(uniformly_bounded, OptimizeResult)
12-
1312
bounds: Sequence[Bound]
14-
variably_bounded = linprog(c, bounds=bounds)
15-
assert_type(variably_bounded, OptimizeResult)
13+
14+
###
15+
16+
res = linprog(c, bounds=bounds)
17+
assert_type(res.fun, float | None)
18+
assert_type(res.x, np.ndarray[tuple[int], np.dtype[np.float64]] | None)
19+
assert_type(res.fun, float | None)
20+
assert_type(res.success, bool)
21+
assert_type(res.message, str)
22+
23+
_1: OptimizeResult = linprog(c, bounds=bound)
24+
_2: OptimizeResult = linprog(c, bounds=bounds)

0 commit comments

Comments
 (0)