pyoframe 0.0.7__tar.gz → 0.0.9__tar.gz
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.
- {pyoframe-0.0.7/src/pyoframe.egg-info → pyoframe-0.0.9}/PKG-INFO +2 -3
- {pyoframe-0.0.7 → pyoframe-0.0.9}/pyproject.toml +2 -2
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/core.py +28 -33
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/io.py +76 -9
- {pyoframe-0.0.7 → pyoframe-0.0.9/src/pyoframe.egg-info}/PKG-INFO +2 -3
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/requires.txt +1 -2
- {pyoframe-0.0.7 → pyoframe-0.0.9}/LICENSE +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/README.md +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/setup.cfg +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/__init__.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/_arithmetic.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/constants.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/io_mappers.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/model.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/model_element.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/monkey_patch.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/objective.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/solvers.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/user_defined.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/util.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/SOURCES.txt +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/dependency_links.txt +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/top_level.txt +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_arithmetic.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_examples.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_io.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_operations.py +0 -0
- {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_solver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyoframe
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: Blazing fast linear program interface
|
|
5
5
|
Author-email: Bravos Power <dev@bravospower.com>
|
|
6
6
|
Project-URL: Homepage, https://bravos-power.github.io/pyoframe/
|
|
@@ -15,11 +15,10 @@ Classifier: Natural Language :: English
|
|
|
15
15
|
Requires-Python: >=3.8
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: polars
|
|
18
|
+
Requires-Dist: polars==0.20.27
|
|
19
19
|
Requires-Dist: numpy
|
|
20
20
|
Requires-Dist: pyarrow
|
|
21
21
|
Requires-Dist: pandas
|
|
22
|
-
Requires-Dist: tqdm
|
|
23
22
|
Provides-Extra: dev
|
|
24
23
|
Requires-Dist: black; extra == "dev"
|
|
25
24
|
Requires-Dist: bumpver; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyoframe"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.9"
|
|
8
8
|
authors = [{ name = "Bravos Power", email = "dev@bravospower.com" }]
|
|
9
9
|
description = "Blazing fast linear program interface"
|
|
10
10
|
readme = "README.md"
|
|
@@ -16,7 +16,7 @@ classifiers = [
|
|
|
16
16
|
"License :: OSI Approved :: MIT License",
|
|
17
17
|
"Natural Language :: English",
|
|
18
18
|
]
|
|
19
|
-
dependencies = ["polars", "numpy", "pyarrow", "pandas"
|
|
19
|
+
dependencies = ["polars==0.20.27", "numpy", "pyarrow", "pandas"]
|
|
20
20
|
|
|
21
21
|
[project.optional-dependencies]
|
|
22
22
|
dev = [
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
2
4
|
from typing import (
|
|
5
|
+
TYPE_CHECKING,
|
|
3
6
|
Iterable,
|
|
4
7
|
List,
|
|
5
8
|
Mapping,
|
|
9
|
+
Optional,
|
|
6
10
|
Protocol,
|
|
7
11
|
Sequence,
|
|
8
|
-
overload,
|
|
9
12
|
Union,
|
|
10
|
-
|
|
11
|
-
TYPE_CHECKING,
|
|
13
|
+
overload,
|
|
12
14
|
)
|
|
13
|
-
from abc import ABC, abstractmethod
|
|
14
15
|
|
|
15
16
|
import pandas as pd
|
|
16
17
|
import polars as pl
|
|
@@ -21,33 +22,32 @@ from pyoframe.constants import (
|
|
|
21
22
|
CONST_TERM,
|
|
22
23
|
CONSTRAINT_KEY,
|
|
23
24
|
DUAL_KEY,
|
|
25
|
+
RC_COL,
|
|
24
26
|
RESERVED_COL_KEYS,
|
|
25
27
|
SLACK_COL,
|
|
26
|
-
VAR_KEY,
|
|
27
28
|
SOLUTION_KEY,
|
|
28
|
-
|
|
29
|
-
VType,
|
|
30
|
-
VTypeValue,
|
|
29
|
+
VAR_KEY,
|
|
31
30
|
Config,
|
|
32
31
|
ConstraintSense,
|
|
33
|
-
UnmatchedStrategy,
|
|
34
|
-
PyoframeError,
|
|
35
32
|
ObjSense,
|
|
33
|
+
PyoframeError,
|
|
34
|
+
UnmatchedStrategy,
|
|
35
|
+
VType,
|
|
36
|
+
VTypeValue,
|
|
37
|
+
)
|
|
38
|
+
from pyoframe.model_element import (
|
|
39
|
+
ModelElement,
|
|
40
|
+
ModelElementWithId,
|
|
41
|
+
SupportPolarsMethodMixin,
|
|
36
42
|
)
|
|
37
43
|
from pyoframe.util import (
|
|
44
|
+
FuncArgs,
|
|
38
45
|
cast_coef_to_string,
|
|
39
46
|
concat_dimensions,
|
|
47
|
+
dataframe_to_tupled_list,
|
|
40
48
|
get_obj_repr,
|
|
41
49
|
parse_inputs_as_iterable,
|
|
42
50
|
unwrap_single_values,
|
|
43
|
-
dataframe_to_tupled_list,
|
|
44
|
-
FuncArgs,
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
from pyoframe.model_element import (
|
|
48
|
-
ModelElement,
|
|
49
|
-
ModelElementWithId,
|
|
50
|
-
SupportPolarsMethodMixin,
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
if TYPE_CHECKING: # pragma: no cover
|
|
@@ -271,11 +271,7 @@ class Set(ModelElement, SupportsMath, SupportPolarsMethodMixin):
|
|
|
271
271
|
elif isinstance(set, Constraint):
|
|
272
272
|
df = set.data.select(set.dimensions_unsafe)
|
|
273
273
|
elif isinstance(set, SupportsMath):
|
|
274
|
-
df = (
|
|
275
|
-
set.to_expr()
|
|
276
|
-
.data.drop(RESERVED_COL_KEYS, strict=False)
|
|
277
|
-
.unique(maintain_order=True)
|
|
278
|
-
)
|
|
274
|
+
df = set.to_expr().data.drop(RESERVED_COL_KEYS).unique(maintain_order=True)
|
|
279
275
|
elif isinstance(set, pd.Index):
|
|
280
276
|
df = pl.from_pandas(pd.DataFrame(index=set).reset_index())
|
|
281
277
|
elif isinstance(set, pd.DataFrame):
|
|
@@ -696,7 +692,9 @@ class Expression(ModelElement, SupportsMath, SupportPolarsMethodMixin):
|
|
|
696
692
|
>>> m.expr_1 = 2 * m.X + 1
|
|
697
693
|
>>> m.expr_2 = pf.sum(m.expr_1)
|
|
698
694
|
>>> m.objective = m.expr_2 - 3
|
|
699
|
-
>>> result = m.solve(log_to_console=False)
|
|
695
|
+
>>> result = m.solve(log_to_console=False) # doctest: +ELLIPSIS
|
|
696
|
+
<BLANKLINE>
|
|
697
|
+
...
|
|
700
698
|
>>> m.expr_1.value
|
|
701
699
|
shape: (3, 2)
|
|
702
700
|
┌──────┬──────────┐
|
|
@@ -1007,13 +1005,9 @@ class Constraint(ModelElementWithId):
|
|
|
1007
1005
|
>>> m.hours_spent = pf.Variable(homework_due_tomorrow[["project"]], lb=0)
|
|
1008
1006
|
>>> m.must_finish_project = m.hours_spent >= homework_due_tomorrow[["project", "hours_to_finish"]]
|
|
1009
1007
|
>>> m.only_one_day = sum("project", m.hours_spent) <= 24
|
|
1010
|
-
>>> m.solve(log_to_console=False)
|
|
1011
|
-
Status: warning
|
|
1012
|
-
Termination condition: infeasible
|
|
1013
|
-
<BLANKLINE>
|
|
1014
|
-
|
|
1015
1008
|
>>> _ = m.must_finish_project.relax(homework_due_tomorrow[["project", "cost_per_hour_underdelivered"]], max=homework_due_tomorrow[["project", "max_underdelivered"]])
|
|
1016
|
-
>>>
|
|
1009
|
+
>>> _ = m.solve(log_to_console=False) # doctest: +ELLIPSIS
|
|
1010
|
+
\rWriting ...
|
|
1017
1011
|
>>> m.hours_spent.solution
|
|
1018
1012
|
shape: (3, 2)
|
|
1019
1013
|
┌─────────┬──────────┐
|
|
@@ -1033,7 +1027,8 @@ class Constraint(ModelElementWithId):
|
|
|
1033
1027
|
>>> m.hours_spent = pf.Variable(homework_due_tomorrow[["project"]], lb=0)
|
|
1034
1028
|
>>> m.must_finish_project = (m.hours_spent >= homework_due_tomorrow[["project", "hours_to_finish"]]).relax(5)
|
|
1035
1029
|
>>> m.only_one_day = (sum("project", m.hours_spent) <= 24).relax(1)
|
|
1036
|
-
>>> _ = m.solve(log_to_console=False)
|
|
1030
|
+
>>> _ = m.solve(log_to_console=False) # doctest: +ELLIPSIS
|
|
1031
|
+
\rWriting ...
|
|
1037
1032
|
>>> m.objective.value
|
|
1038
1033
|
-3.0
|
|
1039
1034
|
>>> m.hours_spent.solution
|
|
@@ -1263,7 +1258,7 @@ class Variable(ModelElementWithId, SupportsMath, SupportPolarsMethodMixin):
|
|
|
1263
1258
|
)
|
|
1264
1259
|
|
|
1265
1260
|
def to_expr(self) -> Expression:
|
|
1266
|
-
return self._new(self.data.drop(SOLUTION_KEY
|
|
1261
|
+
return self._new(self.data.drop(SOLUTION_KEY))
|
|
1267
1262
|
|
|
1268
1263
|
def _new(self, data: pl.DataFrame):
|
|
1269
1264
|
e = Expression(data.with_columns(pl.lit(1.0).alias(COEF_KEY)))
|
|
@@ -1341,5 +1336,5 @@ class Variable(ModelElementWithId, SupportsMath, SupportPolarsMethodMixin):
|
|
|
1341
1336
|
data = expr.data.rename({dim: "__prev"})
|
|
1342
1337
|
data = data.join(
|
|
1343
1338
|
wrapped, left_on="__prev", right_on="__next", how="inner"
|
|
1344
|
-
).drop(["__prev", "__next"]
|
|
1339
|
+
).drop(["__prev", "__next"])
|
|
1345
1340
|
return expr._new(data)
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
Module containing all import/export functionalities.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
5
7
|
from io import TextIOWrapper
|
|
6
|
-
from tempfile import NamedTemporaryFile
|
|
7
8
|
from pathlib import Path
|
|
9
|
+
from tempfile import NamedTemporaryFile
|
|
8
10
|
from typing import TYPE_CHECKING, Iterable, Optional, TypeVar, Union
|
|
9
|
-
from tqdm import tqdm
|
|
10
11
|
|
|
11
12
|
from pyoframe.constants import CONST_TERM, VAR_KEY, ObjSense
|
|
12
13
|
from pyoframe.core import Constraint, Variable
|
|
@@ -24,6 +25,57 @@ if TYPE_CHECKING: # pragma: no cover
|
|
|
24
25
|
|
|
25
26
|
import polars as pl
|
|
26
27
|
|
|
28
|
+
T = TypeVar("T")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def io_progress_bar(
|
|
32
|
+
iterable: Iterable[T],
|
|
33
|
+
prefix: str = "",
|
|
34
|
+
suffix: str = "",
|
|
35
|
+
length: int = 50,
|
|
36
|
+
fill: str = "█",
|
|
37
|
+
update_every: int = 1,
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
Display progress bar for I/O operations.
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
total = len(iterable)
|
|
44
|
+
except TypeError:
|
|
45
|
+
total = None
|
|
46
|
+
|
|
47
|
+
start_time = time.time()
|
|
48
|
+
|
|
49
|
+
def print_progress(iteration: int):
|
|
50
|
+
if total is not None:
|
|
51
|
+
percent = f"{100 * (iteration / float(total)):.1f}"
|
|
52
|
+
filled_length = int(length * iteration // total)
|
|
53
|
+
bar = fill * filled_length + "-" * (length - filled_length)
|
|
54
|
+
else:
|
|
55
|
+
percent = "N/A"
|
|
56
|
+
bar = fill * (iteration % length) + "-" * (length - (iteration % length))
|
|
57
|
+
elapsed_time = time.time() - start_time
|
|
58
|
+
if iteration > 0:
|
|
59
|
+
estimated_total_time = (
|
|
60
|
+
elapsed_time * (total / iteration) if total else elapsed_time
|
|
61
|
+
)
|
|
62
|
+
estimated_remaining_time = estimated_total_time - elapsed_time
|
|
63
|
+
eta = time.strftime("%H:%M:%S", time.gmtime(estimated_remaining_time))
|
|
64
|
+
else:
|
|
65
|
+
eta = "Estimating..." # pragma: no cover
|
|
66
|
+
sys.stdout.write(
|
|
67
|
+
f'\r{prefix} |{bar}| {percent}% Complete ({iteration}/{total if total else "?"}) ETA: {eta} {suffix}'
|
|
68
|
+
)
|
|
69
|
+
sys.stdout.flush()
|
|
70
|
+
|
|
71
|
+
for i, item in enumerate(iterable):
|
|
72
|
+
yield item
|
|
73
|
+
if (i + 1) % update_every == 0 or total is None or i == total - 1:
|
|
74
|
+
print_progress(i + 1)
|
|
75
|
+
|
|
76
|
+
sys.stdout.write("\n")
|
|
77
|
+
sys.stdout.flush()
|
|
78
|
+
|
|
27
79
|
|
|
28
80
|
def objective_to_file(m: "Model", f: TextIOWrapper, var_map):
|
|
29
81
|
"""
|
|
@@ -41,7 +93,11 @@ def objective_to_file(m: "Model", f: TextIOWrapper, var_map):
|
|
|
41
93
|
|
|
42
94
|
def constraints_to_file(m: "Model", f: TextIOWrapper, var_map, const_map):
|
|
43
95
|
for constraint in create_section(
|
|
44
|
-
|
|
96
|
+
io_progress_bar(
|
|
97
|
+
m.constraints, prefix="Writing constraints to file", update_every=5
|
|
98
|
+
),
|
|
99
|
+
f,
|
|
100
|
+
"s.t.",
|
|
45
101
|
):
|
|
46
102
|
f.write(constraint.to_str(var_map=var_map, const_map=const_map) + "\n")
|
|
47
103
|
|
|
@@ -58,7 +114,9 @@ def bounds_to_file(m: "Model", f, var_map):
|
|
|
58
114
|
)
|
|
59
115
|
f.write(f"{var_map.apply(const_term_df).item()} = 1\n")
|
|
60
116
|
|
|
61
|
-
for variable in
|
|
117
|
+
for variable in io_progress_bar(
|
|
118
|
+
m.variables, prefix="Writing bounds to file", update_every=1
|
|
119
|
+
):
|
|
62
120
|
terms = []
|
|
63
121
|
|
|
64
122
|
if variable.lb != 0:
|
|
@@ -88,7 +146,13 @@ def binaries_to_file(m: "Model", f, var_map: Mapper):
|
|
|
88
146
|
Write out binaries of a model to a lp file.
|
|
89
147
|
"""
|
|
90
148
|
for variable in create_section(
|
|
91
|
-
|
|
149
|
+
io_progress_bar(
|
|
150
|
+
m.binary_variables,
|
|
151
|
+
prefix="Writing binary variables to file",
|
|
152
|
+
update_every=1,
|
|
153
|
+
),
|
|
154
|
+
f,
|
|
155
|
+
"binary",
|
|
92
156
|
):
|
|
93
157
|
lines = (
|
|
94
158
|
var_map.apply(variable.data, to_col=None)
|
|
@@ -103,7 +167,13 @@ def integers_to_file(m: "Model", f, var_map: Mapper):
|
|
|
103
167
|
Write out integers of a model to a lp file.
|
|
104
168
|
"""
|
|
105
169
|
for variable in create_section(
|
|
106
|
-
|
|
170
|
+
io_progress_bar(
|
|
171
|
+
m.integer_variables,
|
|
172
|
+
prefix="Writing integer variables to file",
|
|
173
|
+
update_every=5,
|
|
174
|
+
),
|
|
175
|
+
f,
|
|
176
|
+
"general",
|
|
107
177
|
):
|
|
108
178
|
lines = (
|
|
109
179
|
var_map.apply(variable.data, to_col=None)
|
|
@@ -113,9 +183,6 @@ def integers_to_file(m: "Model", f, var_map: Mapper):
|
|
|
113
183
|
f.write(lines + "\n")
|
|
114
184
|
|
|
115
185
|
|
|
116
|
-
T = TypeVar("T")
|
|
117
|
-
|
|
118
|
-
|
|
119
186
|
def create_section(iterable: Iterable[T], f, section_header) -> Iterable[T]:
|
|
120
187
|
wrote = False
|
|
121
188
|
for item in iterable:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyoframe
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: Blazing fast linear program interface
|
|
5
5
|
Author-email: Bravos Power <dev@bravospower.com>
|
|
6
6
|
Project-URL: Homepage, https://bravos-power.github.io/pyoframe/
|
|
@@ -15,11 +15,10 @@ Classifier: Natural Language :: English
|
|
|
15
15
|
Requires-Python: >=3.8
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: polars
|
|
18
|
+
Requires-Dist: polars==0.20.27
|
|
19
19
|
Requires-Dist: numpy
|
|
20
20
|
Requires-Dist: pyarrow
|
|
21
21
|
Requires-Dist: pandas
|
|
22
|
-
Requires-Dist: tqdm
|
|
23
22
|
Provides-Extra: dev
|
|
24
23
|
Requires-Dist: black; extra == "dev"
|
|
25
24
|
Requires-Dist: bumpver; extra == "dev"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|