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.
Files changed (28) hide show
  1. {pyoframe-0.0.7/src/pyoframe.egg-info → pyoframe-0.0.9}/PKG-INFO +2 -3
  2. {pyoframe-0.0.7 → pyoframe-0.0.9}/pyproject.toml +2 -2
  3. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/core.py +28 -33
  4. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/io.py +76 -9
  5. {pyoframe-0.0.7 → pyoframe-0.0.9/src/pyoframe.egg-info}/PKG-INFO +2 -3
  6. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/requires.txt +1 -2
  7. {pyoframe-0.0.7 → pyoframe-0.0.9}/LICENSE +0 -0
  8. {pyoframe-0.0.7 → pyoframe-0.0.9}/README.md +0 -0
  9. {pyoframe-0.0.7 → pyoframe-0.0.9}/setup.cfg +0 -0
  10. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/__init__.py +0 -0
  11. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/_arithmetic.py +0 -0
  12. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/constants.py +0 -0
  13. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/io_mappers.py +0 -0
  14. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/model.py +0 -0
  15. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/model_element.py +0 -0
  16. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/monkey_patch.py +0 -0
  17. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/objective.py +0 -0
  18. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/solvers.py +0 -0
  19. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/user_defined.py +0 -0
  20. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe/util.py +0 -0
  21. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/SOURCES.txt +0 -0
  22. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/dependency_links.txt +0 -0
  23. {pyoframe-0.0.7 → pyoframe-0.0.9}/src/pyoframe.egg-info/top_level.txt +0 -0
  24. {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_arithmetic.py +0 -0
  25. {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_examples.py +0 -0
  26. {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_io.py +0 -0
  27. {pyoframe-0.0.7 → pyoframe-0.0.9}/tests/test_operations.py +0 -0
  28. {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.7
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"
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", "tqdm"]
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
- Optional,
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
- RC_COL,
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
- >>> result = m.solve(log_to_console=False)
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, strict=False))
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"], strict=False)
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
- tqdm(m.constraints, desc="Writing constraints to file"), f, "s.t."
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 tqdm(m.variables, desc="Writing bounds to file"):
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
- tqdm(m.binary_variables, "Writing binary variables to file"), f, "binary"
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
- tqdm(m.integer_variables, "Writing integer variables to file"), f, "general"
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.7
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"
@@ -1,8 +1,7 @@
1
- polars
1
+ polars==0.20.27
2
2
  numpy
3
3
  pyarrow
4
4
  pandas
5
- tqdm
6
5
 
7
6
  [dev]
8
7
  black
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes