pyoframe 0.2.0__py3-none-any.whl → 1.0.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.
@@ -1,37 +1,48 @@
1
- """
2
- File containing utility functions and classes.
3
- """
1
+ """Contains utility functions and classes."""
4
2
 
3
+ from __future__ import annotations
4
+
5
+ import itertools
6
+ import sys
7
+ from collections.abc import Iterable
5
8
  from dataclasses import dataclass, field
6
9
  from functools import wraps
7
- from typing import (
8
- TYPE_CHECKING,
9
- Any,
10
- Dict,
11
- Iterable,
12
- List,
13
- Optional,
14
- Sequence,
15
- Type,
16
- Union,
17
- )
10
+ from typing import TYPE_CHECKING, Any, Callable
18
11
 
19
12
  import pandas as pd
20
13
  import polars as pl
21
14
 
22
- from pyoframe.constants import COEF_KEY, CONST_TERM, RESERVED_COL_KEYS, VAR_KEY, Config
15
+ from pyoframe._constants import (
16
+ COEF_KEY,
17
+ CONST_TERM,
18
+ RESERVED_COL_KEYS,
19
+ VAR_KEY,
20
+ Config,
21
+ )
23
22
 
24
23
  if TYPE_CHECKING: # pragma: no cover
25
- from pyoframe.model import Variable
26
- from pyoframe.model_element import ModelElementWithId
24
+ from pyoframe._core import BaseOperableBlock
25
+ from pyoframe._model import Variable
27
26
 
27
+ if sys.version_info >= (3, 10):
28
+ pairwise = itertools.pairwise
29
+ else:
28
30
 
29
- def get_obj_repr(obj: object, _props: Iterable[str] = (), **kwargs):
30
- """
31
- Helper function to generate __repr__ strings for classes. See usage for examples.
31
+ def pairwise(iterable):
32
+ iterator = iter(iterable)
33
+ a = next(iterator)
34
+
35
+ for b in iterator:
36
+ yield a, b
37
+ a = b
38
+
39
+
40
+ def get_obj_repr(obj: object, *props: str | None, **kwargs):
41
+ """Generates __repr__() strings for classes.
42
+
43
+ See usage for examples.
32
44
  """
33
- props = {prop: getattr(obj, prop) for prop in _props}
34
- props_str = " ".join(f"{k}={v}" for k, v in props.items() if v is not None)
45
+ props_str = " ".join(f"'{v}'" for v in props if v is not None)
35
46
  if props_str:
36
47
  props_str += " "
37
48
  kwargs_str = " ".join(f"{k}={v}" for k, v in kwargs.items() if v is not None)
@@ -39,10 +50,10 @@ def get_obj_repr(obj: object, _props: Iterable[str] = (), **kwargs):
39
50
 
40
51
 
41
52
  def parse_inputs_as_iterable(
42
- *inputs: Union[Any, Iterable[Any]],
53
+ *inputs: Any | Iterable[Any],
43
54
  ) -> Iterable[Any]:
44
- """
45
- Converts a parameter *x: Any | Iteraable[Any] to a single Iterable[Any] object.
55
+ """Converts a parameter *x: Any | Iterable[Any] to a single Iterable[Any] object.
56
+
46
57
  This is helpful to support these two ways of passing arguments:
47
58
  - foo([1, 2, 3])
48
59
  - foo(1, 2, 3)
@@ -59,7 +70,7 @@ def parse_inputs_as_iterable(
59
70
  return inputs
60
71
 
61
72
 
62
- def _is_iterable(input: Union[Any, Iterable[Any]]) -> bool:
73
+ def _is_iterable(input: Any | Iterable[Any]) -> bool:
63
74
  # Inspired from the polars library, TODO: Consider using opposite check, i.e. equals list or tuple
64
75
  return isinstance(input, Iterable) and not isinstance(
65
76
  input,
@@ -78,15 +89,11 @@ def _is_iterable(input: Union[Any, Iterable[Any]]) -> bool:
78
89
 
79
90
 
80
91
  def concat_dimensions(
81
- df: pl.DataFrame,
82
- prefix: Optional[str] = None,
83
- keep_dims: bool = True,
84
- ignore_columns: Sequence[str] = RESERVED_COL_KEYS,
85
- replace_spaces: bool = True,
86
- to_col: str = "concated_dim",
92
+ df: pl.DataFrame, prefix: str, keep_dims: bool = True, to_col: str = "concated_dim"
87
93
  ) -> pl.DataFrame:
88
- """
89
- Returns a new DataFrame with the column 'concated_dim'. Reserved columns are ignored.
94
+ """Returns a new DataFrame with the column 'concated_dim'.
95
+
96
+ Reserved columns are ignored. Spaces are replaced with underscores.
90
97
 
91
98
  Parameters:
92
99
  df:
@@ -94,9 +101,7 @@ def concat_dimensions(
94
101
  prefix:
95
102
  The prefix to be added to the concated dimension.
96
103
  keep_dims:
97
- If True, the original dimensions are kept in the new DataFrame.
98
- replace_spaces : bool, optional
99
- If True, replaces spaces with underscores.
104
+ If `True`, the original dimensions are kept in the new DataFrame.
100
105
 
101
106
  Examples:
102
107
  >>> import polars as pl
@@ -106,20 +111,6 @@ def concat_dimensions(
106
111
  ... "dim2": ["Y", "Y", "Y", "N", "N", "N"],
107
112
  ... }
108
113
  ... )
109
- >>> concat_dimensions(df)
110
- shape: (6, 3)
111
- ┌──────┬──────┬──────────────┐
112
- │ dim1 ┆ dim2 ┆ concated_dim │
113
- │ --- ┆ --- ┆ --- │
114
- │ i64 ┆ str ┆ str │
115
- ╞══════╪══════╪══════════════╡
116
- │ 1 ┆ Y ┆ [1,Y] │
117
- │ 2 ┆ Y ┆ [2,Y] │
118
- │ 3 ┆ Y ┆ [3,Y] │
119
- │ 1 ┆ N ┆ [1,N] │
120
- │ 2 ┆ N ┆ [2,N] │
121
- │ 3 ┆ N ┆ [3,N] │
122
- └──────┴──────┴──────────────┘
123
114
  >>> concat_dimensions(df, prefix="x")
124
115
  shape: (6, 3)
125
116
  ┌──────┬──────┬──────────────┐
@@ -134,7 +125,7 @@ def concat_dimensions(
134
125
  │ 2 ┆ N ┆ x[2,N] │
135
126
  │ 3 ┆ N ┆ x[3,N] │
136
127
  └──────┴──────┴──────────────┘
137
- >>> concat_dimensions(df, keep_dims=False)
128
+ >>> concat_dimensions(df, prefix="", keep_dims=False)
138
129
  shape: (6, 1)
139
130
  ┌──────────────┐
140
131
  │ concated_dim │
@@ -148,7 +139,8 @@ def concat_dimensions(
148
139
  │ [2,N] │
149
140
  │ [3,N] │
150
141
  └──────────────┘
151
- >>> # Properly handles cases with no dimensions and ignores reserved columns
142
+
143
+ Properly handles cases with no dimensions and ignores reserved columns
152
144
  >>> df = pl.DataFrame({VAR_KEY: [1, 2]})
153
145
  >>> concat_dimensions(df, prefix="x")
154
146
  shape: (2, 2)
@@ -163,7 +155,7 @@ def concat_dimensions(
163
155
  """
164
156
  if prefix is None:
165
157
  prefix = ""
166
- dimensions = [col for col in df.columns if col not in ignore_columns]
158
+ dimensions = [col for col in df.columns if col not in RESERVED_COL_KEYS]
167
159
  if dimensions:
168
160
  query = pl.concat_str(
169
161
  pl.lit(prefix + "["),
@@ -173,10 +165,7 @@ def concat_dimensions(
173
165
  else:
174
166
  query = pl.lit(prefix)
175
167
 
176
- df = df.with_columns(query.alias(to_col))
177
-
178
- if replace_spaces:
179
- df = df.with_columns(pl.col(to_col).str.replace_all(" ", "_"))
168
+ df = df.with_columns(query.str.replace_all(" ", "_").alias(to_col))
180
169
 
181
170
  if not keep_dims:
182
171
  df = df.drop(*dimensions)
@@ -185,10 +174,12 @@ def concat_dimensions(
185
174
 
186
175
 
187
176
  def cast_coef_to_string(
188
- df: pl.DataFrame, column_name: str = COEF_KEY, drop_ones: bool = True
177
+ df: pl.DataFrame,
178
+ column_name: str = COEF_KEY,
179
+ drop_ones: bool = True,
180
+ always_show_sign: bool = True,
189
181
  ) -> pl.DataFrame:
190
- """
191
- Converts column `column_name` of the dataframe `df` to a string. Rounds to `Config.print_float_precision` decimal places if not None.
182
+ """Converts column `column_name` of the DataFrame `df` to a string. Round to `Config.print_float_precision` decimal places if not None.
192
183
 
193
184
  Parameters:
194
185
  df:
@@ -196,7 +187,9 @@ def cast_coef_to_string(
196
187
  column_name:
197
188
  The name of the column to be casted.
198
189
  drop_ones:
199
- If True, 1s are replaced with an empty string for non-constant terms.
190
+ If `True`, 1s are replaced with an empty string for non-constant terms.
191
+ always_show_sign:
192
+ If `True`, the sign of the coefficient is always shown, i.e. 1 becomes `+1` not just `1`.
200
193
 
201
194
  Examples:
202
195
  >>> import polars as pl
@@ -214,22 +207,26 @@ def cast_coef_to_string(
214
207
  │ +4 ┆ 4 │
215
208
  └─────┴───────────────┘
216
209
  """
217
- df = df.with_columns(
218
- pl.col(column_name).abs(),
219
- _sign=pl.when(pl.col(column_name) < 0).then(pl.lit("-")).otherwise(pl.lit("+")),
220
- )
221
-
222
210
  if Config.float_to_str_precision is not None:
223
211
  df = df.with_columns(pl.col(column_name).round(Config.float_to_str_precision))
224
212
 
213
+ if always_show_sign:
214
+ df = df.with_columns(
215
+ pl.col(column_name).abs(),
216
+ _sign=pl.when(pl.col(column_name) < 0)
217
+ .then(pl.lit("-"))
218
+ .otherwise(pl.lit("+")),
219
+ )
220
+
225
221
  df = df.with_columns(
226
- pl.when(pl.col(column_name) == pl.col(column_name).floor())
222
+ pl.when(pl.col(column_name) == pl.col(column_name).round())
227
223
  .then(pl.col(column_name).cast(pl.Int64).cast(pl.String))
228
224
  .otherwise(pl.col(column_name).cast(pl.String))
229
225
  .alias(column_name)
230
226
  )
231
227
 
232
228
  if drop_ones:
229
+ assert always_show_sign, "drop_ones requires always_show_sign=True"
233
230
  condition = pl.col(column_name) == str(1)
234
231
  if VAR_KEY in df.columns:
235
232
  condition = condition & (pl.col(VAR_KEY) != CONST_TERM)
@@ -239,15 +236,16 @@ def cast_coef_to_string(
239
236
  .otherwise(pl.col(column_name))
240
237
  .alias(column_name)
241
238
  )
242
- else:
243
- df = df.with_columns(pl.col(column_name).cast(pl.Utf8))
244
- return df.with_columns(pl.concat_str("_sign", column_name).alias(column_name)).drop(
245
- "_sign"
246
- )
239
+
240
+ if always_show_sign:
241
+ df = df.with_columns(
242
+ pl.concat_str("_sign", column_name).alias(column_name)
243
+ ).drop("_sign")
244
+ return df
247
245
 
248
246
 
249
- def unwrap_single_values(func):
250
- """Decorator for functions that return DataFrames. Returned dataframes with a single value will instead return the value."""
247
+ def unwrap_single_values(func) -> pl.DataFrame | Any:
248
+ """Returns the DataFrame unless it is a single value in which case return the value."""
251
249
 
252
250
  @wraps(func)
253
251
  def wrapper(*args, **kwargs):
@@ -259,52 +257,20 @@ def unwrap_single_values(func):
259
257
  return wrapper
260
258
 
261
259
 
262
- def dataframe_to_tupled_list(
263
- df: pl.DataFrame, num_max_elements: Optional[int] = None
264
- ) -> str:
265
- """
266
- Converts a dataframe into a list of tuples. Used to print a Set to the console. See examples for behaviour.
267
-
268
- Examples:
269
- >>> df = pl.DataFrame({"x": [1, 2, 3, 4, 5]})
270
- >>> dataframe_to_tupled_list(df)
271
- '[1, 2, 3, 4, 5]'
272
- >>> dataframe_to_tupled_list(df, 3)
273
- '[1, 2, 3, ...]'
274
-
275
- >>> df = pl.DataFrame({"x": [1, 2, 3, 4, 5], "y": [2, 3, 4, 5, 6]})
276
- >>> dataframe_to_tupled_list(df, 3)
277
- '[(1, 2), (2, 3), (3, 4), ...]'
278
- """
279
- elipse = False
280
- if num_max_elements is not None:
281
- if len(df) > num_max_elements:
282
- elipse = True
283
- df = df.head(num_max_elements)
284
-
285
- res = (row for row in df.iter_rows())
286
- if len(df.columns) == 1:
287
- res = (row[0] for row in res)
288
-
289
- res = str(list(res))
290
- if elipse:
291
- res = res[:-1] + ", ...]"
292
- return res
293
-
294
-
295
260
  @dataclass
296
261
  class FuncArgs:
297
- args: List
298
- kwargs: Dict = field(default_factory=dict)
262
+ args: list
263
+ kwargs: dict = field(default_factory=dict)
299
264
 
300
265
 
301
266
  class Container:
302
- """
303
- A placeholder object that makes it easy to set and get attributes. Used in Model.attr and Model.params, for example.
267
+ """A placeholder object that makes it easy to set and get attributes. Used in Model.attr and Model.params, for example.
304
268
 
305
269
  Examples:
306
270
  >>> x = {}
307
- >>> params = Container(setter=lambda n, v: x.__setitem__(n, v), getter=lambda n: x[n])
271
+ >>> params = Container(
272
+ ... setter=lambda n, v: x.__setitem__(n, v), getter=lambda n: x[n]
273
+ ... )
308
274
  >>> params.a = 1
309
275
  >>> params.b = 2
310
276
  >>> params.a
@@ -329,35 +295,34 @@ class Container:
329
295
 
330
296
 
331
297
  class NamedVariableMapper:
332
- """
333
- Maps variables to a string representation using the object's name and dimensions.
298
+ """Maps variables to a string representation using the object's name and dimensions.
334
299
 
335
300
  Examples:
336
301
  >>> import polars as pl
337
302
  >>> m = pf.Model()
338
303
  >>> m.foo = pf.Variable(pl.DataFrame({"t": range(4)}))
339
- >>> pf.sum(m.foo)
340
- <Expression size=1 dimensions={} terms=4>
341
- foo[0] + foo[1] + foo[2] + foo[3]
304
+ >>> m.foo.sum()
305
+ <Expression terms=4 type=linear>
306
+ foo[0] + foo[1] + foo[2] + foo[3]
342
307
  """
343
308
 
344
309
  CONST_TERM_NAME = "_ONE"
345
310
  NAME_COL = "__name"
346
311
 
347
- def __init__(self, cls: Type["ModelElementWithId"]) -> None:
312
+ def __init__(self) -> None:
348
313
  self._ID_COL = VAR_KEY
349
314
  self.mapping_registry = pl.DataFrame(
350
315
  {self._ID_COL: [], self.NAME_COL: []},
351
- schema={self._ID_COL: pl.UInt32, self.NAME_COL: pl.String},
316
+ schema={self._ID_COL: Config.id_dtype, self.NAME_COL: pl.String},
352
317
  )
353
318
  self._extend_registry(
354
319
  pl.DataFrame(
355
320
  {self._ID_COL: [CONST_TERM], self.NAME_COL: [self.CONST_TERM_NAME]},
356
- schema={self._ID_COL: pl.UInt32, self.NAME_COL: pl.String},
321
+ schema={self._ID_COL: Config.id_dtype, self.NAME_COL: pl.String},
357
322
  )
358
323
  )
359
324
 
360
- def add(self, element: "Variable") -> None:
325
+ def add(self, element: Variable) -> None:
361
326
  self._extend_registry(self._element_to_map(element))
362
327
 
363
328
  def _extend_registry(self, df: pl.DataFrame) -> None:
@@ -375,16 +340,17 @@ class NamedVariableMapper:
375
340
  validate="m:1",
376
341
  left_on=id_col,
377
342
  right_on=self._ID_COL,
343
+ maintain_order="left" if Config.maintain_order else None,
378
344
  ).rename({self.NAME_COL: to_col})
379
345
 
380
- def _element_to_map(self, element) -> pl.DataFrame:
346
+ def _element_to_map(self, element: Variable) -> pl.DataFrame:
381
347
  element_name = element.name # type: ignore
382
348
  assert element_name is not None, (
383
349
  "Element must have a name to be used in a named mapping."
384
350
  )
385
351
  element._assert_has_ids()
386
352
  return concat_dimensions(
387
- element.data.select(element.dimensions_unsafe + [VAR_KEY]),
353
+ element.data.select(element._dimensions_unsafe + [VAR_KEY]),
388
354
  keep_dims=False,
389
355
  prefix=element_name,
390
356
  to_col=self.NAME_COL,
@@ -392,19 +358,32 @@ class NamedVariableMapper:
392
358
 
393
359
 
394
360
  def for_solvers(*solvers: str):
395
- """
396
- Decorator that limits the function to only be called when the solver is in the `only` list.
397
- """
361
+ """Limits the decorated function to only be available when the solver is in the `solvers` list."""
398
362
 
399
363
  def decorator(func):
400
364
  @wraps(func)
401
365
  def wrapper(self, *args, **kwargs):
402
- if self.solver_name not in solvers:
366
+ if self.solver.name not in solvers:
403
367
  raise NotImplementedError(
404
- f"Method '{func.__name__}' is not implemented for solver '{self.solver_name}'."
368
+ f"Method '{func.__name__}' is not implemented for solver '{self.solver}'."
405
369
  )
406
370
  return func(self, *args, **kwargs)
407
371
 
408
372
  return wrapper
409
373
 
410
374
  return decorator
375
+
376
+
377
+ # TODO: rename and change to return_expr once Set is split away from BaseOperableBlock
378
+ def return_new(func: Callable[..., pl.DataFrame]) -> Callable[..., BaseOperableBlock]:
379
+ """Decorator that upcasts the returned DataFrame to an Expression.
380
+
381
+ Requires the first argument (self) to support self._new().
382
+ """
383
+
384
+ @wraps(func)
385
+ def wrapper(self: BaseOperableBlock, *args, **kwargs):
386
+ result = func(self, *args, **kwargs)
387
+ return self._new(result, name=f"{self.name}.{func.__name__}(…)")
388
+
389
+ return wrapper
pyoframe/_version.py CHANGED
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '0.2.0'
21
- __version_tuple__ = version_tuple = (0, 2, 0)
31
+ __version__ = version = '1.0.0'
32
+ __version_tuple__ = version_tuple = (1, 0, 0)
33
+
34
+ __commit_id__ = commit_id = None
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyoframe
3
- Version: 0.2.0
3
+ Version: 1.0.0
4
4
  Summary: Blazing fast linear program interface
5
5
  Author-email: Bravos Power <dev@bravospower.com>
6
6
  License-Expression: MIT
7
- Project-URL: Homepage, https://bravos-power.github.io/pyoframe/
8
- Project-URL: documentation, https://bravos-power.github.io/pyoframe/
7
+ Project-URL: Homepage, https://bravos-power.github.io/pyoframe/latest
8
+ Project-URL: documentation, https://bravos-power.github.io/pyoframe/latest
9
9
  Project-URL: repository, https://github.com/Bravos-Power/pyoframe/
10
10
  Project-URL: Issues, https://github.com/Bravos-Power/pyoframe/issues
11
11
  Classifier: Programming Language :: Python :: 3
@@ -16,51 +16,57 @@ Requires-Python: >=3.9
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: polars~=1.0
19
- Requires-Dist: numpy
20
19
  Requires-Dist: pyarrow
21
20
  Requires-Dist: pandas
22
- Requires-Dist: pyoptinterface<1,>=0.4.1
23
- Provides-Extra: dev
24
- Requires-Dist: ruff; extra == "dev"
25
- Requires-Dist: polars>=1.30.0; extra == "dev"
26
- Requires-Dist: bumpver; extra == "dev"
27
- Requires-Dist: pip-tools; extra == "dev"
28
- Requires-Dist: pytest; extra == "dev"
29
- Requires-Dist: pytest-cov; extra == "dev"
30
- Requires-Dist: pre-commit; extra == "dev"
31
- Requires-Dist: gurobipy; extra == "dev"
32
- Requires-Dist: highsbox; extra == "dev"
33
- Requires-Dist: coverage; extra == "dev"
34
- Requires-Dist: ipykernel; extra == "dev"
35
- Requires-Dist: pytest-markdown-docs; extra == "dev"
36
- Requires-Dist: mkdocs-material==9.*; extra == "dev"
37
- Requires-Dist: mkdocstrings[python]; extra == "dev"
38
- Requires-Dist: mkdocs-git-revision-date-localized-plugin; extra == "dev"
39
- Requires-Dist: mkdocs-git-committers-plugin-2; extra == "dev"
40
- Requires-Dist: mkdocs-gen-files; extra == "dev"
41
- Requires-Dist: mkdocs-section-index; extra == "dev"
42
- Requires-Dist: mkdocs-literate-nav; extra == "dev"
43
- Requires-Dist: mkdocs-table-reader-plugin; extra == "dev"
44
- Requires-Dist: markdown-hide-code>=0.1.1; extra == "dev"
21
+ Requires-Dist: pyoptinterface==0.5.1
45
22
  Provides-Extra: highs
46
23
  Requires-Dist: highsbox; extra == "highs"
24
+ Provides-Extra: ipopt
25
+ Requires-Dist: pyoptinterface[nlp]; extra == "ipopt"
26
+ Requires-Dist: llvmlite<=0.44.0; extra == "ipopt"
27
+ Provides-Extra: dev
28
+ Requires-Dist: ruff==0.12.11; extra == "dev"
29
+ Requires-Dist: polars>=1.32.3; extra == "dev"
30
+ Requires-Dist: pytest==8.4.1; extra == "dev"
31
+ Requires-Dist: pytest-cov==6.2.1; extra == "dev"
32
+ Requires-Dist: sybil[pytest]==9.2.0; extra == "dev"
33
+ Requires-Dist: pre-commit==4.3.0; extra == "dev"
34
+ Requires-Dist: gurobipy==12.0.3; extra == "dev"
35
+ Requires-Dist: coverage==7.10.6; extra == "dev"
36
+ Requires-Dist: ipykernel==6.30.1; extra == "dev"
37
+ Requires-Dist: highsbox; extra == "dev"
38
+ Requires-Dist: pyoptinterface[nlp]; extra == "dev"
39
+ Requires-Dist: numpy; extra == "dev"
40
+ Provides-Extra: docs
41
+ Requires-Dist: mkdocs-material~=9.6.18; extra == "docs"
42
+ Requires-Dist: mkdocstrings[python]~=0.30.0; extra == "docs"
43
+ Requires-Dist: mkdocs-git-revision-date-localized-plugin~=1.4.7; extra == "docs"
44
+ Requires-Dist: mkdocs-git-committers-plugin-2~=2.5.0; extra == "docs"
45
+ Requires-Dist: mkdocs-gen-files~=0.5.0; extra == "docs"
46
+ Requires-Dist: mkdocs-section-index~=0.3.10; extra == "docs"
47
+ Requires-Dist: mkdocs-awesome-nav~=3.1.2; extra == "docs"
48
+ Requires-Dist: doccmd==2025.4.8; extra == "docs"
49
+ Requires-Dist: mkdocs-table-reader-plugin~=3.1.0; extra == "docs"
50
+ Requires-Dist: markdown-katex==202406.1035; extra == "docs"
51
+ Requires-Dist: mike==2.1.3; extra == "docs"
52
+ Requires-Dist: ruff==0.12.11; extra == "docs"
47
53
  Dynamic: license-file
48
54
 
49
55
  # Pyoframe: Fast and low-memory linear programming models
50
56
 
51
57
  [![codecov](https://codecov.io/gh/Bravos-Power/pyoframe/graph/badge.svg?token=8258XESRYQ)](https://codecov.io/gh/Bravos-Power/pyoframe)
52
58
  [![Build](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml/badge.svg)](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml)
53
- [![Docs](https://github.com/Bravos-Power/pyoframe/actions/workflows/publish_doc.yml/badge.svg)](https://Bravos-Power.github.io/pyoframe/reference/)
59
+ [![Docs](https://github.com/Bravos-Power/pyoframe/actions/workflows/publish_doc.yml/badge.svg)](https://Bravos-Power.github.io/pyoframe/latest/reference/)
54
60
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
55
61
  [![Issues Needing Triage](https://img.shields.io/github/issues-search/Bravos-Power/pyoframe?query=no%3Alabel%20is%3Aopen&label=Needs%20Triage)](https://github.com/Bravos-Power/pyoframe/issues?q=is%3Aopen+is%3Aissue+no%3Alabel)
56
62
  [![Open Bugs](https://img.shields.io/github/issues-search/Bravos-Power/pyoframe?query=label%3Abug%20is%3Aopen&label=Open%20Bugs)](https://github.com/Bravos-Power/pyoframe/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
57
63
 
58
64
 
59
- A library to rapidly and memory-efficiently formulate large and sparse optimization models using Pandas or Polars dataframes.
65
+ A library to rapidly and memory-efficiently formulate large and sparse optimization models using Pandas or Polars DataFrames.
60
66
 
61
- ## **[Documentation](https://bravos-power.github.io/pyoframe/)**
67
+ ## **[Documentation](https://bravos-power.github.io/pyoframe/latest/)**
62
68
 
63
- [Read the documentation](https://bravos-power.github.io/pyoframe/) to get started or to learn how to [contribute](https://bravos-power.github.io/pyoframe/contribute/).
69
+ [Read the documentation](https://bravos-power.github.io/pyoframe/latest/) to get started or to learn how to [contribute](https://bravos-power.github.io/pyoframe/latest/contribute/index.md).
64
70
 
65
71
 
66
72
  ## Acknowledgments
@@ -0,0 +1,15 @@
1
+ pyoframe/__init__.py,sha256=Nlql3FYed7bXWumvUeMd3rjnoL4l8XC5orO4uxWrDAc,839
2
+ pyoframe/_arithmetic.py,sha256=9V3N2Yq7Ib13UfTQnINxa9oS7786f5DnRsrAPcFlYYE,20558
3
+ pyoframe/_constants.py,sha256=n80so80usutTJpeDlDXkHRE-_dpPD1LxF2plscXhbwQ,17925
4
+ pyoframe/_core.py,sha256=NXH_ze1iN61vk38Fj6Ire0uqKUJRSDUlD6PVc8mTHrQ,116975
5
+ pyoframe/_model.py,sha256=MtA9gleQoUAqO2dxhCFZ8GOZvyyJB_PIYBzvQ0m8enc,22466
6
+ pyoframe/_model_element.py,sha256=oQ7nykJ5XEzJ6Klq3lT6ZwQvDrxY_wgZYVaN7pgyZOs,6149
7
+ pyoframe/_monkey_patch.py,sha256=Y2zXN5MpqDeAWELddyaFQNam57fehSXHiza1PFaZ-QY,3128
8
+ pyoframe/_objective.py,sha256=yIHoaBLsjGCKzIB6RQErV3vzE2U5DGORlhifRStB_Mc,4335
9
+ pyoframe/_utils.py,sha256=5yy-5DOWCW7q3QzPv9tKquLUQJtNdpJJilEqxAq7TN8,12518
10
+ pyoframe/_version.py,sha256=vLA4ITz09S-S435nq6yTF6l3qiSz6w4euS1rOxXgd1M,704
11
+ pyoframe-1.0.0.dist-info/licenses/LICENSE,sha256=u_Spw4ynlwTMRZeCX-uacv_hBU547pBygiA6d2ONNV4,1074
12
+ pyoframe-1.0.0.dist-info/METADATA,sha256=pbmH3YsUkSvJtpl6NWKYw2P7zxLcwWsnt5LDCOF7jeM,4042
13
+ pyoframe-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ pyoframe-1.0.0.dist-info/top_level.txt,sha256=10z3OOJSVLriQ0IrFLMH8CH9zByugPWolqhlHlkNjV4,9
15
+ pyoframe-1.0.0.dist-info/RECORD,,