pyoframe 0.0.11__py3-none-any.whl → 0.1.1__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.
pyoframe/io_mappers.py DELETED
@@ -1,238 +0,0 @@
1
- """
2
- Defines various methods for mapping a variable or constraint to its string representation.
3
- """
4
-
5
- from dataclasses import dataclass
6
- import math
7
- import string
8
- from abc import ABC, abstractmethod
9
-
10
- from typing import TYPE_CHECKING, Optional, Type, Union
11
- import polars as pl
12
- from pyoframe.util import concat_dimensions
13
- from pyoframe.constants import CONST_TERM
14
-
15
-
16
- if TYPE_CHECKING: # pragma: no cover
17
- from pyoframe.model import Variable
18
- from pyoframe.core import Constraint
19
- from pyoframe.model_element import ModelElementWithId
20
-
21
-
22
- @dataclass
23
- class IOMappers:
24
- var_map: "Mapper"
25
- const_map: "Mapper"
26
-
27
-
28
- class Mapper(ABC):
29
-
30
- NAME_COL = "__name"
31
-
32
- def __init__(self, cls: Type["ModelElementWithId"]) -> None:
33
- self._ID_COL = cls.get_id_column_name()
34
- self.mapping_registry = pl.DataFrame(
35
- {self._ID_COL: [], Mapper.NAME_COL: []},
36
- schema={self._ID_COL: pl.UInt32, Mapper.NAME_COL: pl.String},
37
- )
38
-
39
- def add(self, element: Union["Variable", "Constraint"]) -> None:
40
- self._extend_registry(self._element_to_map(element))
41
-
42
- def _extend_registry(self, df: pl.DataFrame) -> None:
43
- self.mapping_registry = pl.concat([self.mapping_registry, df])
44
-
45
- @abstractmethod
46
- def _element_to_map(self, element: "ModelElementWithId") -> pl.DataFrame: ...
47
-
48
- def apply(
49
- self,
50
- df: pl.DataFrame,
51
- to_col: Optional[str] = None,
52
- ) -> pl.DataFrame:
53
- if df.height == 0:
54
- return df
55
- result = df.join(
56
- self.mapping_registry, on=self._ID_COL, how="left", validate="m:1"
57
- )
58
- if to_col is None:
59
- result = result.drop(self._ID_COL)
60
- to_col = self._ID_COL
61
- return result.rename({Mapper.NAME_COL: to_col})
62
-
63
- def undo(self, df: pl.DataFrame) -> pl.DataFrame:
64
- if df.height == 0:
65
- return df
66
- df = df.rename({self._ID_COL: Mapper.NAME_COL})
67
- return df.join(
68
- self.mapping_registry, on=Mapper.NAME_COL, how="left", validate="m:1"
69
- ).drop(Mapper.NAME_COL)
70
-
71
-
72
- class NamedMapper(Mapper):
73
- """
74
- Maps constraints or variables to a string representation using the object's name and dimensions.
75
-
76
- Examples:
77
-
78
- >>> import polars as pl
79
- >>> import pyoframe as pf
80
- >>> m = pf.Model("min")
81
- >>> m.foo = pf.Variable(pl.DataFrame({"t": range(4)}))
82
- >>> pf.sum(m.foo)
83
- <Expression size=1 dimensions={} terms=4>
84
- foo[0] + foo[1] + foo[2] + foo[3]
85
- """
86
-
87
- def _element_to_map(self, element) -> pl.DataFrame:
88
- element_name = element.name # type: ignore
89
- assert (
90
- element_name is not None
91
- ), "Element must have a name to be used in a named mapping."
92
- return concat_dimensions(
93
- element.ids, keep_dims=False, prefix=element_name, to_col=Mapper.NAME_COL
94
- )
95
-
96
-
97
- class NamedVariableMapper(NamedMapper):
98
- CONST_TERM_NAME = "_ONE"
99
-
100
- def __init__(self, *args, **kwargs) -> None:
101
- super().__init__(*args, **kwargs)
102
- self._extend_registry(
103
- pl.DataFrame(
104
- {self._ID_COL: [CONST_TERM], self.NAME_COL: [self.CONST_TERM_NAME]},
105
- schema={self._ID_COL: pl.UInt32, self.NAME_COL: pl.String},
106
- )
107
- )
108
-
109
-
110
- class Base36Mapper(Mapper, ABC):
111
- # Mapping between a base 36 character and its integer value
112
- # Note: we must use only lowercase since Gurobi auto-converts variables that aren't in constraints to lowercase (kind of annoying)
113
- _CHAR_TABLE = pl.DataFrame(
114
- {"char": list(string.digits + string.ascii_lowercase)},
115
- ).with_columns(pl.int_range(pl.len()).cast(pl.UInt32).alias("code"))
116
-
117
- _BASE = _CHAR_TABLE.height # _BASE = 36
118
- _ZERO = _CHAR_TABLE.filter(pl.col("code") == 0).select("char").item() # _ZERO = "0"
119
-
120
- @property
121
- @abstractmethod
122
- def _prefix(self) -> "str": ...
123
-
124
- def apply(
125
- self,
126
- df: pl.DataFrame,
127
- to_col: Optional[str] = None,
128
- ) -> pl.DataFrame:
129
- if df.height == 0:
130
- return df
131
-
132
- query = pl.concat_str(
133
- pl.lit(self._prefix),
134
- pl.col(self._ID_COL).map_batches(
135
- Base36Mapper._to_base36,
136
- return_dtype=pl.String,
137
- is_elementwise=True,
138
- ),
139
- )
140
-
141
- if to_col is None:
142
- to_col = self._ID_COL
143
-
144
- return df.with_columns(query.alias(to_col))
145
-
146
- @classmethod
147
- def _to_base36(cls, int_col: pl.Series) -> pl.Series:
148
- """Returns a series of dtype str with a base 36 representation of the integers in int_col.
149
- The letters 0-9A-Z are used as symbols for the representation.
150
-
151
- Examples:
152
-
153
- >>> import polars as pl
154
- >>> s = pl.Series([0,10,20,60,53,66], dtype=pl.UInt32)
155
- >>> Base36Mapper._to_base36(s).to_list()
156
- ['0', 'a', 'k', '1o', '1h', '1u']
157
-
158
- >>> s = pl.Series([0], dtype=pl.UInt32)
159
- >>> Base36Mapper._to_base36(s).to_list()
160
- ['0']
161
- """
162
- assert isinstance(
163
- int_col.dtype, pl.UInt32
164
- ), "_to_base36() only works for UInt32 id columns"
165
-
166
- largest_id = int_col.max()
167
- if largest_id == 0:
168
- max_digits = 1
169
- else:
170
- max_digits = math.floor(math.log(largest_id, cls._BASE)) + 1 # type: ignore
171
-
172
- digits = []
173
-
174
- for i in range(max_digits):
175
- remainder = int_col % cls._BASE
176
-
177
- digits.append(
178
- remainder.to_frame(name="code")
179
- .join(cls._CHAR_TABLE, on="code", how="left")
180
- .select("char")
181
- .rename({"char": f"digit{i}"})
182
- )
183
- int_col //= cls._BASE
184
-
185
- return (
186
- pl.concat(reversed(digits), how="horizontal")
187
- .select(pl.concat_str(pl.all()))
188
- .to_series()
189
- .str.strip_chars_start(cls._ZERO)
190
- .replace("", cls._ZERO)
191
- )
192
-
193
- def _element_to_map(self, element) -> pl.DataFrame:
194
- return self.apply(element.ids.select(self._ID_COL), to_col=Mapper.NAME_COL)
195
-
196
-
197
- class Base36VarMapper(Base36Mapper):
198
- """
199
- Examples:
200
- >>> import polars as pl
201
- >>> from pyoframe import Model, Variable
202
- >>> from pyoframe.constants import VAR_KEY
203
- >>> m = Model("min")
204
- >>> m.x = Variable(pl.DataFrame({"t": range(1,63)}))
205
- >>> (m.x.filter(t=11)+1).to_str()
206
- '[11]: 1 + x[11]'
207
- >>> (m.x.filter(t=11)+1).to_str(var_map=Base36VarMapper(Variable))
208
- '[11]: 1 + xb'
209
-
210
- >>> Base36VarMapper(Variable).apply(pl.DataFrame({VAR_KEY: []}))
211
- shape: (0, 1)
212
- ┌───────────────┐
213
- │ __variable_id │
214
- │ --- │
215
- │ null │
216
- ╞═══════════════╡
217
- └───────────────┘
218
- """
219
-
220
- def __init__(self, *args, **kwargs) -> None:
221
- super().__init__(*args, **kwargs)
222
- df = pl.DataFrame(
223
- {self._ID_COL: [CONST_TERM]},
224
- schema={self._ID_COL: pl.UInt32},
225
- )
226
- df = self.apply(df, to_col=Mapper.NAME_COL)
227
- self._extend_registry(df)
228
-
229
- @property
230
- def _prefix(self) -> "str":
231
- return "x"
232
-
233
-
234
- class Base36ConstMapper(Base36Mapper):
235
-
236
- @property
237
- def _prefix(self) -> "str":
238
- return "c"
pyoframe/solvers.py DELETED
@@ -1,377 +0,0 @@
1
- """
2
- Code to interface with various solvers
3
- """
4
-
5
- from abc import abstractmethod, ABC
6
- from functools import lru_cache
7
- from pathlib import Path
8
- from typing import Any, Dict, Optional, Type, Union, TYPE_CHECKING
9
-
10
- import polars as pl
11
-
12
- from pyoframe.constants import (
13
- DUAL_KEY,
14
- SOLUTION_KEY,
15
- SLACK_COL,
16
- RC_COL,
17
- VAR_KEY,
18
- CONSTRAINT_KEY,
19
- Result,
20
- Solution,
21
- Status,
22
- )
23
- import contextlib
24
- import pyoframe as pf
25
-
26
- from pathlib import Path
27
-
28
- if TYPE_CHECKING: # pragma: no cover
29
- from pyoframe.model import Model
30
-
31
- available_solvers = []
32
- solver_registry: Dict[str, Type["Solver"]] = {}
33
-
34
- with contextlib.suppress(ImportError):
35
- import gurobipy
36
-
37
- available_solvers.append("gurobi")
38
-
39
-
40
- def _register_solver(solver_name):
41
- def decorator(cls):
42
- solver_registry[solver_name] = cls
43
- return cls
44
-
45
- return decorator
46
-
47
-
48
- def solve(
49
- m: "Model",
50
- solver=None,
51
- directory: Optional[Union[Path, str]] = None,
52
- use_var_names=False,
53
- log_fn=None,
54
- warmstart_fn=None,
55
- basis_fn=None,
56
- solution_file=None,
57
- log_to_console=True,
58
- ):
59
- if solver is None:
60
- if len(available_solvers) == 0:
61
- raise ValueError(
62
- "No solvers available. Please install a solving library like gurobipy."
63
- )
64
- solver = available_solvers[0]
65
-
66
- if solver not in solver_registry:
67
- raise ValueError(f"Solver {solver} not recognized or supported.")
68
-
69
- solver_cls = solver_registry[solver]
70
- m.solver = solver_cls(
71
- m,
72
- log_to_console,
73
- params={param: value for param, value in m.params},
74
- directory=directory,
75
- )
76
- m.solver_model = m.solver.create_solver_model(use_var_names)
77
- m.solver.solver_model = m.solver_model
78
-
79
- for attr_container in [m.variables, m.constraints, [m]]:
80
- for container in attr_container:
81
- for param_name, param_value in container.attr:
82
- m.solver.set_attr(container, param_name, param_value)
83
-
84
- result = m.solver.solve(log_fn, warmstart_fn, basis_fn, solution_file)
85
- result = m.solver.process_result(result)
86
- m.result = result
87
-
88
- if result.solution is not None:
89
- if m.objective is not None:
90
- m.objective.value = result.solution.objective
91
-
92
- for variable in m.variables:
93
- variable.solution = result.solution.primal
94
-
95
- if result.solution.dual is not None:
96
- for constraint in m.constraints:
97
- constraint.dual = result.solution.dual
98
-
99
- return result
100
-
101
-
102
- class Solver(ABC):
103
- def __init__(self, model: "Model", log_to_console, params, directory):
104
- self._model = model
105
- self.solver_model: Optional[Any] = None
106
- self.log_to_console: bool = log_to_console
107
- self.params = params
108
- self.directory = directory
109
-
110
- @abstractmethod
111
- def create_solver_model(self, use_var_names) -> Any: ...
112
-
113
- @abstractmethod
114
- def set_attr(self, element, param_name, param_value): ...
115
-
116
- @abstractmethod
117
- def solve(self, log_fn, warmstart_fn, basis_fn, solution_file) -> Result: ...
118
-
119
- @abstractmethod
120
- def process_result(self, results: Result) -> Result: ...
121
-
122
- def load_rc(self):
123
- rc = self._get_all_rc()
124
- for variable in self._model.variables:
125
- variable.RC = rc
126
-
127
- def load_slack(self):
128
- slack = self._get_all_slack()
129
- for constraint in self._model.constraints:
130
- constraint.slack = slack
131
-
132
- @abstractmethod
133
- def _get_all_rc(self): ...
134
-
135
- @abstractmethod
136
- def _get_all_slack(self): ...
137
-
138
- def dispose(self):
139
- """
140
- Clean up any resources that wouldn't be cleaned up by the garbage collector.
141
-
142
- For now, this is only used by the Gurobi solver to call .dispose() on the solver model and Gurobi environment
143
- which helps close a connection to the Gurobi Computer Server. Note that this effectively disables commands that
144
- need access to the solver model (like .slack and .RC)
145
- """
146
-
147
-
148
- class FileBasedSolver(Solver):
149
- def __init__(self, *args, **kwargs):
150
- super().__init__(*args, **kwargs)
151
- self.problem_file: Optional[Path] = None
152
- self.keep_files = self.directory is not None
153
-
154
- def create_solver_model(self, use_var_names) -> Any:
155
- problem_file = None
156
- directory = self.directory
157
- if directory is not None:
158
- if isinstance(directory, str):
159
- directory = Path(directory)
160
- if not directory.exists():
161
- directory.mkdir(parents=True)
162
- filename = (
163
- self._model.name if self._model.name is not None else "pyoframe-problem"
164
- )
165
- problem_file = directory / f"{filename}.lp"
166
- self.problem_file = self._model.to_file(
167
- problem_file, use_var_names=use_var_names
168
- )
169
- assert self._model.io_mappers is not None
170
- return self.create_solver_model_from_lp()
171
-
172
- @abstractmethod
173
- def create_solver_model_from_lp(self) -> Any: ...
174
-
175
- def set_attr(self, element, param_name, param_value):
176
- if isinstance(param_value, pl.DataFrame):
177
- if isinstance(element, pf.Variable):
178
- param_value = self._model.io_mappers.var_map.apply(param_value)
179
- elif isinstance(element, pf.Constraint):
180
- param_value = self._model.io_mappers.const_map.apply(param_value)
181
- return self.set_attr_unmapped(element, param_name, param_value)
182
-
183
- @abstractmethod
184
- def set_attr_unmapped(self, element, param_name, param_value): ...
185
-
186
- def process_result(self, results: Result) -> Result:
187
- if results.solution is not None:
188
- results.solution.primal = self._model.io_mappers.var_map.undo(
189
- results.solution.primal
190
- )
191
- if results.solution.dual is not None:
192
- results.solution.dual = self._model.io_mappers.const_map.undo(
193
- results.solution.dual
194
- )
195
-
196
- return results
197
-
198
- def _get_all_rc(self):
199
- return self._model.io_mappers.var_map.undo(self._get_all_rc_unmapped())
200
-
201
- def _get_all_slack(self):
202
- return self._model.io_mappers.const_map.undo(self._get_all_slack_unmapped())
203
-
204
- @abstractmethod
205
- def _get_all_rc_unmapped(self): ...
206
-
207
- @abstractmethod
208
- def _get_all_slack_unmapped(self): ...
209
-
210
-
211
- @_register_solver("gurobi")
212
- class GurobiSolver(FileBasedSolver):
213
- # see https://www.gurobi.com/documentation/10.0/refman/optimization_status_codes.html
214
- CONDITION_MAP = {
215
- 1: "unknown",
216
- 2: "optimal",
217
- 3: "infeasible",
218
- 4: "infeasible_or_unbounded",
219
- 5: "unbounded",
220
- 6: "other",
221
- 7: "iteration_limit",
222
- 8: "terminated_by_limit",
223
- 9: "time_limit",
224
- 10: "optimal",
225
- 11: "user_interrupt",
226
- 12: "other",
227
- 13: "suboptimal",
228
- 14: "unknown",
229
- 15: "terminated_by_limit",
230
- 16: "internal_solver_error",
231
- 17: "internal_solver_error",
232
- }
233
-
234
- def __init__(self, *args, **kwargs):
235
- super().__init__(*args, **kwargs)
236
- if not self.log_to_console:
237
- self.params["LogToConsole"] = 0
238
- self.env = None
239
-
240
- def create_solver_model_from_lp(self) -> Any:
241
- """
242
- Solve a linear problem using the gurobi solver.
243
-
244
- This function communicates with gurobi using the gurubipy package.
245
- """
246
- assert self.problem_file is not None
247
- self.env = gurobipy.Env(params=self.params)
248
-
249
- m = gurobipy.read(_path_to_str(self.problem_file), env=self.env)
250
- if not self.keep_files:
251
- self.problem_file.unlink()
252
-
253
- return m
254
-
255
- @lru_cache
256
- def _get_var_mapping(self):
257
- assert self.solver_model is not None
258
- vars = self.solver_model.getVars()
259
- return vars, pl.DataFrame(
260
- {VAR_KEY: self.solver_model.getAttr("VarName", vars)}
261
- ).with_columns(i=pl.int_range(pl.len()))
262
-
263
- @lru_cache
264
- def _get_constraint_mapping(self):
265
- assert self.solver_model is not None
266
- constraints = self.solver_model.getConstrs()
267
- return constraints, pl.DataFrame(
268
- {CONSTRAINT_KEY: self.solver_model.getAttr("ConstrName", constraints)}
269
- ).with_columns(i=pl.int_range(pl.len()))
270
-
271
- def set_attr_unmapped(self, element, param_name, param_value):
272
- assert self.solver_model is not None
273
- if isinstance(element, pf.Model):
274
- self.solver_model.setAttr(param_name, param_value)
275
- elif isinstance(element, pf.Variable):
276
- v, v_map = self._get_var_mapping()
277
- param_value = param_value.join(v_map, on=VAR_KEY, how="left").drop(VAR_KEY)
278
- self.solver_model.setAttr(
279
- param_name,
280
- [v[i] for i in param_value["i"]],
281
- param_value[param_name],
282
- )
283
- elif isinstance(element, pf.Constraint):
284
- c, c_map = self._get_constraint_mapping()
285
- param_value = param_value.join(c_map, on=CONSTRAINT_KEY, how="left").drop(
286
- CONSTRAINT_KEY
287
- )
288
- self.solver_model.setAttr(
289
- param_name,
290
- [c[i] for i in param_value["i"]],
291
- param_value[param_name],
292
- )
293
- else:
294
- raise ValueError(f"Element type {type(element)} not recognized.")
295
-
296
- def solve(self, log_fn, warmstart_fn, basis_fn, solution_file) -> Result:
297
- assert self.solver_model is not None
298
- m = self.solver_model
299
- if log_fn is not None:
300
- m.setParam("logfile", _path_to_str(log_fn))
301
- if warmstart_fn:
302
- m.read(_path_to_str(warmstart_fn))
303
-
304
- m.optimize()
305
-
306
- if basis_fn:
307
- try:
308
- m.write(_path_to_str(basis_fn))
309
- except gurobipy.GurobiError as err:
310
- print("No model basis stored. Raised error: %s", err)
311
-
312
- condition = m.status
313
- termination_condition = GurobiSolver.CONDITION_MAP.get(condition, condition)
314
- status = Status.from_termination_condition(termination_condition)
315
-
316
- if status.is_ok and (termination_condition == "optimal"):
317
- if solution_file:
318
- m.write(_path_to_str(solution_file))
319
-
320
- objective = m.ObjVal
321
- vars = m.getVars()
322
- sol = pl.DataFrame(
323
- {
324
- VAR_KEY: m.getAttr("VarName", vars),
325
- SOLUTION_KEY: m.getAttr("X", vars),
326
- }
327
- )
328
-
329
- constraints = m.getConstrs()
330
- try:
331
- dual = pl.DataFrame(
332
- {
333
- DUAL_KEY: m.getAttr("Pi", constraints),
334
- CONSTRAINT_KEY: m.getAttr("ConstrName", constraints),
335
- }
336
- )
337
- except gurobipy.GurobiError:
338
- dual = None
339
-
340
- solution = Solution(sol, dual, objective)
341
- else:
342
- solution = None
343
-
344
- return Result(status, solution)
345
-
346
- def _get_all_rc_unmapped(self):
347
- m = self._model.solver_model
348
- vars = m.getVars()
349
- return pl.DataFrame(
350
- {
351
- RC_COL: m.getAttr("RC", vars),
352
- VAR_KEY: m.getAttr("VarName", vars),
353
- }
354
- )
355
-
356
- def _get_all_slack_unmapped(self):
357
- m = self._model.solver_model
358
- constraints = m.getConstrs()
359
- return pl.DataFrame(
360
- {
361
- SLACK_COL: m.getAttr("Slack", constraints),
362
- CONSTRAINT_KEY: m.getAttr("ConstrName", constraints),
363
- }
364
- )
365
-
366
- def dispose(self):
367
- if self.solver_model is not None:
368
- self.solver_model.dispose()
369
- if self.env is not None:
370
- self.env.dispose()
371
-
372
-
373
- def _path_to_str(path: Union[Path, str]) -> str:
374
- """
375
- Convert a pathlib.Path to a string.
376
- """
377
- return str(path.resolve()) if isinstance(path, Path) else path
pyoframe/user_defined.py DELETED
@@ -1,60 +0,0 @@
1
- """
2
- Contains the base classes to support .params and .attr containers for user-defined parameters and attributes.
3
- """
4
-
5
- from typing import Any
6
-
7
-
8
- class Container:
9
- """
10
- A container for user-defined attributes or parameters.
11
-
12
- Parameters:
13
- preprocess : Callable[str, Any], optional
14
- A function to preprocess user-defined values before adding them to the container.
15
-
16
- Examples:
17
- >>> params = Container()
18
- >>> params.a = 1
19
- >>> params.b = 2
20
- >>> params.a
21
- 1
22
- >>> params.b
23
- 2
24
- >>> for k, v in params:
25
- ... print(k, v)
26
- a 1
27
- b 2
28
- """
29
-
30
- def __init__(self, preprocess=None):
31
- self._preprocess = preprocess
32
- self._attributes = {}
33
-
34
- def __setattr__(self, name: str, value: Any) -> None:
35
- if name.startswith("_"):
36
- return super().__setattr__(name, value)
37
- if self._preprocess is not None:
38
- value = self._preprocess(name, value)
39
- self._attributes[name] = value
40
-
41
- def __getattr__(self, name: str) -> Any:
42
- if name.startswith("_"):
43
- return super().__getattribute__(name)
44
- return self._attributes[name]
45
-
46
- def __iter__(self):
47
- return iter(self._attributes.items())
48
-
49
-
50
- class AttrContainerMixin:
51
- def __init__(self, *args, **kwargs) -> None:
52
- super().__init__(*args, **kwargs)
53
- self.attr = Container(preprocess=self._preprocess_attr)
54
-
55
- def _preprocess_attr(self, name: str, value: Any) -> Any:
56
- """
57
- Preprocesses user-defined values before adding them to the Params container.
58
- By default this function does nothing but subclasses can override it.
59
- """
60
- return value
@@ -1,18 +0,0 @@
1
- pyoframe/__init__.py,sha256=D7HHQPy2Me-LLyfPCcSE74dn83PeMK3aOby7i7oiLTs,507
2
- pyoframe/_arithmetic.py,sha256=riyN2JC-BnOgTIxGfXKIS-X_p7zm8JaKrLk_KDwKAAw,9046
3
- pyoframe/constants.py,sha256=WoWNVsTeAqOHdUPkaZqFR3X3swrsf7OQnZ9AOaUF3uE,7358
4
- pyoframe/core.py,sha256=co4Z-i2AZ4jpvqfTKG67AKcBSFputZ0QrTtrglfkOzQ,51915
5
- pyoframe/io.py,sha256=DJUQ-WlxYe-Ya49pn_T3z6HhJxSD2LSdxm43AGqLuXw,7106
6
- pyoframe/io_mappers.py,sha256=Op5451Yo4gNa-2BiPPCAjPYdFLo8jIeKHCYXUcGPRug,7385
7
- pyoframe/model.py,sha256=xod3hSf__WWDy0V9pao9wPlQTc7-7x56FJoKKidsMbw,3768
8
- pyoframe/model_element.py,sha256=H2gZxksb3UQ25vIdNlb07bCx3ZcWh7YD6-ViPVJV-JI,7691
9
- pyoframe/monkey_patch.py,sha256=S_DU7cieU5C3t3kAyKQrGyLTwno0WANpDBV3xn7AyG8,2068
10
- pyoframe/objective.py,sha256=JzuyMAQZ2OxEoAaK-splWwZei2hHPbCLdG-X2-yRkD0,1338
11
- pyoframe/solvers.py,sha256=yf-hzUHDvKgmIHk2FobmygzE9-LnOzTBL5ps-nqGo8I,11875
12
- pyoframe/user_defined.py,sha256=UWZSTpFj0a8n1_RHwC8Ubwqr4FO-gRPBqqfNUut1IZg,1717
13
- pyoframe/util.py,sha256=KJubFV66E7WPI5UhcuUNsVwCm7WOcQBiLN1af1MAAgA,9647
14
- pyoframe-0.0.11.dist-info/LICENSE,sha256=L1pXz6p_1OW5XGWb2UCR6PNu6k3JAT0XWhi8jV0cuRg,1137
15
- pyoframe-0.0.11.dist-info/METADATA,sha256=qtlWiEBTxnFXguG57ZfmJ0rG4AcjOad5HM3a2CFJym8,3461
16
- pyoframe-0.0.11.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
17
- pyoframe-0.0.11.dist-info/top_level.txt,sha256=10z3OOJSVLriQ0IrFLMH8CH9zByugPWolqhlHlkNjV4,9
18
- pyoframe-0.0.11.dist-info/RECORD,,