pyoframe 0.0.8__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.8/src/pyoframe.egg-info → pyoframe-0.0.9}/PKG-INFO +1 -2
- {pyoframe-0.0.8 → pyoframe-0.0.9}/pyproject.toml +2 -2
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/core.py +25 -26
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/io.py +76 -9
- {pyoframe-0.0.8 → pyoframe-0.0.9/src/pyoframe.egg-info}/PKG-INFO +1 -2
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe.egg-info/requires.txt +0 -1
- {pyoframe-0.0.8 → pyoframe-0.0.9}/LICENSE +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/README.md +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/setup.cfg +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/__init__.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/_arithmetic.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/constants.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/io_mappers.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/model.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/model_element.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/monkey_patch.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/objective.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/solvers.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/user_defined.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe/util.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe.egg-info/SOURCES.txt +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe.egg-info/dependency_links.txt +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/src/pyoframe.egg-info/top_level.txt +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/tests/test_arithmetic.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/tests/test_examples.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/tests/test_io.py +0 -0
- {pyoframe-0.0.8 → pyoframe-0.0.9}/tests/test_operations.py +0 -0
- {pyoframe-0.0.8 → 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/
|
|
@@ -19,7 +19,6 @@ 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==0.20.27", "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
|
|
@@ -692,7 +692,9 @@ class Expression(ModelElement, SupportsMath, SupportPolarsMethodMixin):
|
|
|
692
692
|
>>> m.expr_1 = 2 * m.X + 1
|
|
693
693
|
>>> m.expr_2 = pf.sum(m.expr_1)
|
|
694
694
|
>>> m.objective = m.expr_2 - 3
|
|
695
|
-
>>> result = m.solve(log_to_console=False)
|
|
695
|
+
>>> result = m.solve(log_to_console=False) # doctest: +ELLIPSIS
|
|
696
|
+
<BLANKLINE>
|
|
697
|
+
...
|
|
696
698
|
>>> m.expr_1.value
|
|
697
699
|
shape: (3, 2)
|
|
698
700
|
┌──────┬──────────┐
|
|
@@ -1003,13 +1005,9 @@ class Constraint(ModelElementWithId):
|
|
|
1003
1005
|
>>> m.hours_spent = pf.Variable(homework_due_tomorrow[["project"]], lb=0)
|
|
1004
1006
|
>>> m.must_finish_project = m.hours_spent >= homework_due_tomorrow[["project", "hours_to_finish"]]
|
|
1005
1007
|
>>> m.only_one_day = sum("project", m.hours_spent) <= 24
|
|
1006
|
-
>>> m.solve(log_to_console=False)
|
|
1007
|
-
Status: warning
|
|
1008
|
-
Termination condition: infeasible
|
|
1009
|
-
<BLANKLINE>
|
|
1010
|
-
|
|
1011
1008
|
>>> _ = m.must_finish_project.relax(homework_due_tomorrow[["project", "cost_per_hour_underdelivered"]], max=homework_due_tomorrow[["project", "max_underdelivered"]])
|
|
1012
|
-
>>>
|
|
1009
|
+
>>> _ = m.solve(log_to_console=False) # doctest: +ELLIPSIS
|
|
1010
|
+
\rWriting ...
|
|
1013
1011
|
>>> m.hours_spent.solution
|
|
1014
1012
|
shape: (3, 2)
|
|
1015
1013
|
┌─────────┬──────────┐
|
|
@@ -1029,7 +1027,8 @@ class Constraint(ModelElementWithId):
|
|
|
1029
1027
|
>>> m.hours_spent = pf.Variable(homework_due_tomorrow[["project"]], lb=0)
|
|
1030
1028
|
>>> m.must_finish_project = (m.hours_spent >= homework_due_tomorrow[["project", "hours_to_finish"]]).relax(5)
|
|
1031
1029
|
>>> m.only_one_day = (sum("project", m.hours_spent) <= 24).relax(1)
|
|
1032
|
-
>>> _ = m.solve(log_to_console=False)
|
|
1030
|
+
>>> _ = m.solve(log_to_console=False) # doctest: +ELLIPSIS
|
|
1031
|
+
\rWriting ...
|
|
1033
1032
|
>>> m.objective.value
|
|
1034
1033
|
-3.0
|
|
1035
1034
|
>>> m.hours_spent.solution
|
|
@@ -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/
|
|
@@ -19,7 +19,6 @@ 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
|