pyoframe 0.0.4__py3-none-any.whl → 0.0.6__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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyoframe
3
- Version: 0.0.4
3
+ Version: 0.0.6
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,10 +15,11 @@ 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 ==0.20.13
18
+ Requires-Dist: polars
19
19
  Requires-Dist: numpy
20
20
  Requires-Dist: pyarrow
21
21
  Requires-Dist: pandas
22
+ Requires-Dist: tqdm
22
23
  Provides-Extra: dev
23
24
  Requires-Dist: black ; extra == 'dev'
24
25
  Requires-Dist: bumpver ; extra == 'dev'
@@ -56,3 +57,9 @@ Contributions are welcome! See [`CONTRIBUTE.md`](./CONTRIBUTE.md).
56
57
  ## Acknowledgments
57
58
 
58
59
  Martin Staadecker first created this library while working for [Bravos Power](https://www.bravospower.com/) The library takes inspiration from Linopy and Pyomo, two prior libraries for optimization for which we are thankful.
60
+
61
+ ## Troubleshooting Common Errors
62
+
63
+ ### `datatypes of join keys don't match`
64
+
65
+ Often, this error indicates that two dataframes in your inputs representing the same dimension have different datatypes (e.g. 16bit integer and 64bit integer). This is not allowed and you should ensure for the same dimensions, datatypes are identical.
@@ -0,0 +1,18 @@
1
+ pyoframe/__init__.py,sha256=D7HHQPy2Me-LLyfPCcSE74dn83PeMK3aOby7i7oiLTs,507
2
+ pyoframe/_arithmetic.py,sha256=riyN2JC-BnOgTIxGfXKIS-X_p7zm8JaKrLk_KDwKAAw,9046
3
+ pyoframe/constants.py,sha256=olao24mkzQfwTlc5o4rQ-AOOTytmtVqNCFwJIc8zbds,7169
4
+ pyoframe/core.py,sha256=bTaPOfkgIs-9wrXZ3REthNI7y8oj4S9VLe7xKIye7wc,51139
5
+ pyoframe/io.py,sha256=7bd_oKD8EZsuM0ONUAA9BDFSFvUoXLWGXDhnUqMde8o,5235
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=9K4TBSFDf4-O3gP-5CsWlVpZgem4P11tZdvNIspWbjs,11834
12
+ pyoframe/user_defined.py,sha256=UWZSTpFj0a8n1_RHwC8Ubwqr4FO-gRPBqqfNUut1IZg,1717
13
+ pyoframe/util.py,sha256=KJubFV66E7WPI5UhcuUNsVwCm7WOcQBiLN1af1MAAgA,9647
14
+ pyoframe-0.0.6.dist-info/LICENSE,sha256=L1pXz6p_1OW5XGWb2UCR6PNu6k3JAT0XWhi8jV0cuRg,1137
15
+ pyoframe-0.0.6.dist-info/METADATA,sha256=uv31DtVZ0j5QncOmsElAwkhm82DxTvHzOAA4iVsNBi4,3445
16
+ pyoframe-0.0.6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
17
+ pyoframe-0.0.6.dist-info/top_level.txt,sha256=10z3OOJSVLriQ0IrFLMH8CH9zByugPWolqhlHlkNjV4,9
18
+ pyoframe-0.0.6.dist-info/RECORD,,
pyoframe/variables.py DELETED
@@ -1,193 +0,0 @@
1
- """
2
- File containing Variable class representing decision variables in optimization models.
3
- """
4
-
5
- from __future__ import annotations
6
- from typing import Iterable
7
-
8
- import polars as pl
9
-
10
- from pyoframe.constraints import SupportsMath, Set
11
-
12
- from pyoframe.constants import COEF_KEY, SOLUTION_KEY, VAR_KEY, VType, VTypeValue
13
- from pyoframe.constraints import Expression
14
- from pyoframe.model_element import ModelElement
15
- from pyoframe.constraints import SetTypes
16
- from pyoframe.util import IdCounterMixin, get_obj_repr
17
-
18
-
19
- class Variable(ModelElement, SupportsMath, IdCounterMixin):
20
- """
21
- Represents one or many decision variable in an optimization model.
22
-
23
- Parameters:
24
- *indexing_sets: SetTypes (typically a DataFrame or Set)
25
- If no indexing_sets are provided, a single variable with no dimensions is created.
26
- Otherwise, a variable is created for each element in the Cartesian product of the indexing_sets (see Set for details on behaviour).
27
- lb: float
28
- The lower bound for all variables.
29
- ub: float
30
- The upper bound for all variables.
31
- vtype: VType | VTypeValue
32
- The type of the variable. Can be either a VType enum or a string. Default is VType.CONTINUOUS.
33
-
34
- Examples:
35
- >>> import pandas as pd
36
- >>> from pyoframe import Variable
37
- >>> df = pd.DataFrame({"dim1": [1, 1, 2, 2, 3, 3], "dim2": ["a", "b", "a", "b", "a", "b"]})
38
- >>> Variable(df)
39
- <Variable lb=-inf ub=inf size=6 dimensions={'dim1': 3, 'dim2': 2}>
40
- [1,a]: x1
41
- [1,b]: x2
42
- [2,a]: x3
43
- [2,b]: x4
44
- [3,a]: x5
45
- [3,b]: x6
46
- >>> Variable(df[["dim1"]])
47
- Traceback (most recent call last):
48
- ...
49
- ValueError: Duplicate rows found in input data.
50
- >>> Variable(df[["dim1"]].drop_duplicates())
51
- <Variable lb=-inf ub=inf size=3 dimensions={'dim1': 3}>
52
- [1]: x7
53
- [2]: x8
54
- [3]: x9
55
- """
56
-
57
- # TODO: Breaking change, remove support for Iterable[AcceptableSets]
58
- def __init__(
59
- self,
60
- *indexing_sets: SetTypes | Iterable[SetTypes],
61
- lb: float = float("-inf"),
62
- ub: float = float("inf"),
63
- vtype: VType | VTypeValue = VType.CONTINUOUS,
64
- ):
65
- data = Set(*indexing_sets).data if len(indexing_sets) > 0 else pl.DataFrame()
66
- data = self._assign_ids(data)
67
-
68
- super().__init__(data)
69
-
70
- self.vtype: VType = VType(vtype)
71
-
72
- # Tightening the bounds is not strictly necessary, but it adds clarity
73
- if self.vtype == VType.BINARY:
74
- lb, ub = 0, 1
75
-
76
- self.lb = lb
77
- self.ub = ub
78
-
79
- @classmethod
80
- def get_id_column_name(cls):
81
- return VAR_KEY
82
-
83
- @property
84
- def solution(self):
85
- if SOLUTION_KEY not in self.data.columns:
86
- raise ValueError(f"No solution solution found for Variable '{self.name}'.")
87
- df = self.data.select(self.dimensions_unsafe + [SOLUTION_KEY])
88
- if df.shape == (1, 1):
89
- return df.item()
90
- return df
91
-
92
- @solution.setter
93
- def solution(self, value):
94
- assert sorted(value.columns) == sorted([SOLUTION_KEY, VAR_KEY])
95
- df = self.data
96
- if SOLUTION_KEY in self.data.columns:
97
- df = df.drop(SOLUTION_KEY)
98
- self._data = df.join(value, on=VAR_KEY, how="left", validate="1:1")
99
-
100
- @property
101
- def ids(self):
102
- return self.data.select(self.dimensions_unsafe + [VAR_KEY])
103
-
104
- def __repr__(self):
105
- return (
106
- get_obj_repr(
107
- self, ("name", "lb", "ub"), size=self.data.height, dimensions=self.shape
108
- )
109
- + "\n"
110
- + self.to_expr().to_str(max_line_len=80, max_rows=10)
111
- )
112
-
113
- def to_expr(self) -> Expression:
114
- return self._new(self.data.drop(SOLUTION_KEY))
115
-
116
- def _new(self, data: pl.DataFrame):
117
- e = Expression(data.with_columns(pl.lit(1.0).alias(COEF_KEY)))
118
- e._model = self._model
119
- # We propogate the unmatched strategy intentionally. Without this a .keep_unmatched() on a variable would always be lost.
120
- e.unmatched_strategy = self.unmatched_strategy
121
- e.allowed_new_dims = self.allowed_new_dims
122
- return e
123
-
124
- def next(self, dim: str, wrap_around: bool = False) -> Expression:
125
- """
126
- Creates an expression where the variable at each index is the next variable in the specified dimension.
127
-
128
- Parameters:
129
- dim:
130
- The dimension over which to shift the variable.
131
- wrap_around:
132
- If True, the last index in the dimension is connected to the first index.
133
-
134
- Examples:
135
- >>> import pandas as pd
136
- >>> from pyoframe import Variable, Model
137
- >>> time_dim = pd.DataFrame({"time": ["00:00", "06:00", "12:00", "18:00"]})
138
- >>> space_dim = pd.DataFrame({"city": ["Toronto", "Berlin"]})
139
- >>> m = Model()
140
- >>> m.bat_charge = Variable(time_dim, space_dim)
141
- >>> m.bat_flow = Variable(time_dim, space_dim)
142
- >>> # Fails because the dimensions are not the same
143
- >>> m.bat_charge + m.bat_flow == m.bat_charge.next("time")
144
- Traceback (most recent call last):
145
- ...
146
- pyoframe._arithmetic.PyoframeError: Failed to add expressions:
147
- <Expression size=8 dimensions={'time': 4, 'city': 2} terms=16> + <Expression size=6 dimensions={'city': 2, 'time': 3} terms=6>
148
- Due to error:
149
- Dataframe has unmatched values. If this is intentional, use .drop_unmatched() or .keep_unmatched()
150
- shape: (2, 4)
151
- ┌───────┬─────────┬────────────┬────────────┐
152
- │ time ┆ city ┆ time_right ┆ city_right │
153
- │ --- ┆ --- ┆ --- ┆ --- │
154
- │ str ┆ str ┆ str ┆ str │
155
- ╞═══════╪═════════╪════════════╪════════════╡
156
- │ 18:00 ┆ Toronto ┆ null ┆ null │
157
- │ 18:00 ┆ Berlin ┆ null ┆ null │
158
- └───────┴─────────┴────────────┴────────────┘
159
-
160
- >>> (m.bat_charge + m.bat_flow).drop_unmatched() == m.bat_charge.next("time")
161
- <Constraint sense='=' size=6 dimensions={'time': 3, 'city': 2} terms=18>
162
- [00:00,Berlin]: bat_charge[00:00,Berlin] + bat_flow[00:00,Berlin] - bat_charge[06:00,Berlin] = 0
163
- [00:00,Toronto]: bat_charge[00:00,Toronto] + bat_flow[00:00,Toronto] - bat_charge[06:00,Toronto] = 0
164
- [06:00,Berlin]: bat_charge[06:00,Berlin] + bat_flow[06:00,Berlin] - bat_charge[12:00,Berlin] = 0
165
- [06:00,Toronto]: bat_charge[06:00,Toronto] + bat_flow[06:00,Toronto] - bat_charge[12:00,Toronto] = 0
166
- [12:00,Berlin]: bat_charge[12:00,Berlin] + bat_flow[12:00,Berlin] - bat_charge[18:00,Berlin] = 0
167
- [12:00,Toronto]: bat_charge[12:00,Toronto] + bat_flow[12:00,Toronto] - bat_charge[18:00,Toronto] = 0
168
-
169
- >>> (m.bat_charge + m.bat_flow) == m.bat_charge.next("time", wrap_around=True)
170
- <Constraint sense='=' size=8 dimensions={'time': 4, 'city': 2} terms=24>
171
- [00:00,Berlin]: bat_charge[00:00,Berlin] + bat_flow[00:00,Berlin] - bat_charge[06:00,Berlin] = 0
172
- [00:00,Toronto]: bat_charge[00:00,Toronto] + bat_flow[00:00,Toronto] - bat_charge[06:00,Toronto] = 0
173
- [06:00,Berlin]: bat_charge[06:00,Berlin] + bat_flow[06:00,Berlin] - bat_charge[12:00,Berlin] = 0
174
- [06:00,Toronto]: bat_charge[06:00,Toronto] + bat_flow[06:00,Toronto] - bat_charge[12:00,Toronto] = 0
175
- [12:00,Berlin]: bat_charge[12:00,Berlin] + bat_flow[12:00,Berlin] - bat_charge[18:00,Berlin] = 0
176
- [12:00,Toronto]: bat_charge[12:00,Toronto] + bat_flow[12:00,Toronto] - bat_charge[18:00,Toronto] = 0
177
- [18:00,Berlin]: bat_charge[18:00,Berlin] + bat_flow[18:00,Berlin] - bat_charge[00:00,Berlin] = 0
178
- [18:00,Toronto]: bat_charge[18:00,Toronto] + bat_flow[18:00,Toronto] - bat_charge[00:00,Toronto] = 0
179
- """
180
-
181
- wrapped = self.data.select(dim).unique(maintain_order=True).sort(by=dim)
182
- wrapped = wrapped.with_columns(pl.col(dim).shift(-1).alias("__next"))
183
- if wrap_around:
184
- wrapped = wrapped.with_columns(pl.col("__next").fill_null(pl.first(dim)))
185
- else:
186
- wrapped = wrapped.drop_nulls(dim)
187
-
188
- expr = self.to_expr()
189
- data = expr.data.rename({dim: "__prev"})
190
- data = data.join(
191
- wrapped, left_on="__prev", right_on="__next", how="inner"
192
- ).drop(["__prev", "__next"])
193
- return expr._new(data)
@@ -1,18 +0,0 @@
1
- pyoframe/__init__.py,sha256=GCcDnYaiuPJgFbVznihTvYku7Hs39ayAqo5P0UvSKmM,479
2
- pyoframe/_arithmetic.py,sha256=9qJRuelwU2ycVjDiUSBf3F-jmS0D4Gq_MKb79tiaU_0,9061
3
- pyoframe/constants.py,sha256=dPvt90WP2oYTRf32y7hj_W6McnjyJR8fs7NUZtE6dXs,7076
4
- pyoframe/constraints.py,sha256=jfX4N1XMzOlVYoPRFLP4T0vYxTqM9owsWDlgCK6vVMQ,32229
5
- pyoframe/io.py,sha256=syecN1aT_d02Hu4D42IAgA13LCrQhWkrSwfONN6y9E8,4067
6
- pyoframe/io_mappers.py,sha256=Il0lRGv-EvVDa3_M7vCwp0hajQfAbOVZEBDqNIwzNpg,6208
7
- pyoframe/model.py,sha256=ZkVDWVorAFTXb6Be_04G707jaFqml4eVhkM3Sj1GnNU,3236
8
- pyoframe/model_element.py,sha256=lj5kd5XmhPDepP4lGhsHG1m1qN_8TS-Dl1cTcfveVHo,3753
9
- pyoframe/monkey_patch.py,sha256=lSsJir2fXkEl_uScYbfVle99r_WvlWvD298alP2uWlE,2082
10
- pyoframe/objective.py,sha256=9vDZeDu7fT2A7-DY5WatSJrARQVRHcEWkF7GixpVMa4,1247
11
- pyoframe/solvers.py,sha256=onudQUoGilQ4oOrMA7vz64a08So82dF28hjxHPQooTM,5532
12
- pyoframe/util.py,sha256=iMRvnAFEFzpd23ZjgLy30ZiZGsWXpVzsCNtT_BXZYi4,9962
13
- pyoframe/variables.py,sha256=tPgK3S_Csu-JqY7L5ybs0GdRB7Ha1mG0mAsHdlW0hQI,8878
14
- pyoframe-0.0.4.dist-info/LICENSE,sha256=L1pXz6p_1OW5XGWb2UCR6PNu6k3JAT0XWhi8jV0cuRg,1137
15
- pyoframe-0.0.4.dist-info/METADATA,sha256=Fmo2mZ0lji7wMP3lPZ4JsxuH_bCK2bCHUaMGiRMCaeE,3106
16
- pyoframe-0.0.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
17
- pyoframe-0.0.4.dist-info/top_level.txt,sha256=10z3OOJSVLriQ0IrFLMH8CH9zByugPWolqhlHlkNjV4,9
18
- pyoframe-0.0.4.dist-info/RECORD,,