pyoframe 1.0.1__tar.gz → 1.2.0__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 (279) hide show
  1. pyoframe-1.2.0/.github/dependabot.yml +11 -0
  2. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/ci.yml +2 -1
  3. {pyoframe-1.0.1 → pyoframe-1.2.0}/PKG-INFO +5 -5
  4. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/.nav.yml +2 -2
  5. pyoframe-1.2.0/docs/contribute/index.md +65 -0
  6. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/facility_location.md +4 -4
  7. {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/.nav.yml +1 -1
  8. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/concepts/addition.md +41 -26
  9. {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/internals.md +8 -3
  10. pyoframe-1.2.0/docs/learn/develop/.nav.yml +8 -0
  11. pyoframe-1.2.0/docs/learn/develop/create-a-model.md +41 -0
  12. pyoframe-1.2.0/docs/learn/develop/create-constraints.md +27 -0
  13. pyoframe-1.2.0/docs/learn/develop/create-expressions.md +95 -0
  14. pyoframe-1.2.0/docs/learn/develop/create-variables.md +215 -0
  15. pyoframe-1.2.0/docs/learn/develop/define-objective.md +42 -0
  16. pyoframe-1.2.0/docs/learn/develop/read-results.md +11 -0
  17. pyoframe-1.0.1/docs/learn/concepts/solver-access.md → pyoframe-1.2.0/docs/learn/develop/run-and-configure-the-solver.md +37 -19
  18. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/example-with-dimensions.md +19 -22
  19. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/example.md +4 -4
  20. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/installation.md +2 -2
  21. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/migrate/v1.0.md +2 -2
  22. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/.nav.yml +0 -1
  23. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/index.md +1 -5
  24. {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/PKG-INFO +5 -5
  25. {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/SOURCES.txt +14 -13
  26. {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/requires.txt +4 -4
  27. {pyoframe-1.0.1 → pyoframe-1.2.0}/pyproject.toml +4 -4
  28. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/__init__.py +2 -0
  29. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_arithmetic.py +6 -5
  30. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_constants.py +9 -10
  31. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_core.py +104 -57
  32. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_model.py +180 -18
  33. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_model_element.py +2 -0
  34. pyoframe-1.2.0/src/pyoframe/_monkey_patch.py +38 -0
  35. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_objective.py +2 -2
  36. pyoframe-1.2.0/src/pyoframe/_param.py +99 -0
  37. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_utils.py +2 -2
  38. {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_version.py +3 -3
  39. pyoframe-1.2.0/tests/examples/cutting_stock_problem/results/problem-highs-machine.lp +183 -0
  40. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/model.py +3 -3
  41. pyoframe-1.2.0/tests/examples/diet_problem/results/problem-highs-machine.lp +21 -0
  42. pyoframe-1.2.0/tests/examples/diet_problem/results/solution-highs-machine.sol +69 -0
  43. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/model.py +4 -6
  44. pyoframe-1.2.0/tests/examples/facility_problem/results/problem-highs-machine.lp +29 -0
  45. pyoframe-1.2.0/tests/examples/facility_problem/results/solution-highs-machine.sol +50 -0
  46. pyoframe-1.2.0/tests/examples/portfolio_optim/results/problem-highs-machine.lp +14 -0
  47. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-highs-machine.sol +14 -14
  48. pyoframe-1.2.0/tests/examples/production_planning/results/problem-highs-machine.lp +9 -0
  49. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-highs-machine.sol +10 -10
  50. pyoframe-1.2.0/tests/examples/pumped_storage/results/problem-highs-machine.lp +8860 -0
  51. pyoframe-1.2.0/tests/examples/pumped_storage/results/solution-highs-machine.sol +7317 -0
  52. pyoframe-1.2.0/tests/examples/sudoku/results/problem-highs-machine.lp +1813 -0
  53. pyoframe-1.2.0/tests/examples/sudoku/results/solution-highs-machine.sol +1090 -0
  54. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_addition.py +62 -13
  55. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_arithmetic.py +102 -7
  56. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_examples.py +10 -4
  57. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_io.py +1 -1
  58. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_model.py +10 -0
  59. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_names.py +18 -2
  60. pyoframe-1.2.0/tests/test_objective.py +90 -0
  61. pyoframe-1.2.0/tests/test_param.py +29 -0
  62. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_variable.py +11 -1
  63. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/util.py +4 -2
  64. pyoframe-1.0.1/.github/dependabot.yml +0 -16
  65. pyoframe-1.0.1/docs/contribute/index.md +0 -51
  66. pyoframe-1.0.1/docs/learn/advanced-concepts/datastructure.md +0 -7
  67. pyoframe-1.0.1/docs/learn/concepts/.nav.yml +0 -5
  68. pyoframe-1.0.1/docs/learn/concepts/building-blocks.md +0 -17
  69. pyoframe-1.0.1/docs/learn/concepts/special-functions.md +0 -112
  70. pyoframe-1.0.1/docs/reference/external/.nav.yml +0 -4
  71. pyoframe-1.0.1/docs/reference/external/pandas.DataFrame.to_expr.md +0 -3
  72. pyoframe-1.0.1/docs/reference/external/pandas.Series.to_expr.md +0 -3
  73. pyoframe-1.0.1/docs/reference/external/polars.DataFrame.to_expr.md +0 -3
  74. pyoframe-1.0.1/src/pyoframe/_monkey_patch.py +0 -82
  75. pyoframe-1.0.1/tests/examples/cutting_stock_problem/results/problem-highs-machine.lp +0 -183
  76. pyoframe-1.0.1/tests/examples/diet_problem/results/problem-highs-machine.lp +0 -21
  77. pyoframe-1.0.1/tests/examples/diet_problem/results/solution-highs-machine.sol +0 -69
  78. pyoframe-1.0.1/tests/examples/facility_problem/results/problem-highs-machine.lp +0 -29
  79. pyoframe-1.0.1/tests/examples/facility_problem/results/solution-highs-machine.sol +0 -50
  80. pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-highs-machine.lp +0 -14
  81. pyoframe-1.0.1/tests/examples/production_planning/results/problem-highs-machine.lp +0 -9
  82. pyoframe-1.0.1/tests/examples/pumped_storage/results/problem-highs-machine.lp +0 -8876
  83. pyoframe-1.0.1/tests/examples/pumped_storage/results/solution-highs-machine.sol +0 -7317
  84. pyoframe-1.0.1/tests/examples/sudoku/results/problem-highs-machine.lp +0 -1813
  85. pyoframe-1.0.1/tests/examples/sudoku/results/solution-highs-machine.sol +0 -1090
  86. pyoframe-1.0.1/tests/test_objective.py +0 -22
  87. {pyoframe-1.0.1 → pyoframe-1.2.0}/.gitattributes +0 -0
  88. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/CODEOWNERS +0 -0
  89. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/actions/setup_optimizers_linux/action.yml +0 -0
  90. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/actions/setup_optimizers_macos/action.yml +0 -0
  91. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/actions/setup_optimizers_windows/action.yml +0 -0
  92. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/format.yml +0 -0
  93. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/lines_changed_counter.yml +0 -0
  94. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/lint.yml +0 -0
  95. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/publish_doc.yml +0 -0
  96. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/publish_doc_dev.yml +0 -0
  97. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/publish_to_pypi.yml +0 -0
  98. {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/test_doc.yml +0 -0
  99. {pyoframe-1.0.1 → pyoframe-1.2.0}/.gitignore +0 -0
  100. {pyoframe-1.0.1 → pyoframe-1.2.0}/.pre-commit-config.yaml +0 -0
  101. {pyoframe-1.0.1 → pyoframe-1.2.0}/.vscode/launch.json +0 -0
  102. {pyoframe-1.0.1 → pyoframe-1.2.0}/.vscode/settings.json +0 -0
  103. {pyoframe-1.0.1 → pyoframe-1.2.0}/CHANGELOG.md +0 -0
  104. {pyoframe-1.0.1 → pyoframe-1.2.0}/LICENSE +0 -0
  105. {pyoframe-1.0.1 → pyoframe-1.2.0}/README.md +0 -0
  106. {pyoframe-1.0.1 → pyoframe-1.2.0}/conftest.py +0 -0
  107. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/.nav.yml +0 -0
  108. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/diet.md +0 -0
  109. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/index.md +0 -0
  110. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/portfolio_optimization.md +0 -0
  111. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/production.md +0 -0
  112. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/index.md +0 -0
  113. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/javascripts/feedback.js +0 -0
  114. {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/performance.md +0 -0
  115. {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/quadratics.md +0 -0
  116. {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/troubleshooting.md +0 -0
  117. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/.nav.yml +0 -0
  118. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/food_data.csv +0 -0
  119. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/foods.csv +0 -0
  120. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/foods_to_nutrients.csv +0 -0
  121. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/nutrients.csv +0 -0
  122. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/results.csv +0 -0
  123. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/power_grid_example.ipynb +0 -0
  124. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/three-bus.png +0 -0
  125. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/main.html +0 -0
  126. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/partials/actions.html +0 -0
  127. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/partials/comments.html +0 -0
  128. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
  129. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/bases/.nav.yml +0 -0
  130. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/bases/BaseBlock.md +0 -0
  131. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/bases/BaseOperableBlock.md +0 -0
  132. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/public/.nav.yml +0 -0
  133. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/public/Config.md +0 -0
  134. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/types/.nav.yml +0 -0
  135. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/types/Operable.md +0 -0
  136. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/stylesheets/extra.css +0 -0
  137. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/data_py.parquet +0 -0
  138. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/gen_py.parquet +0 -0
  139. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/index.md +0 -0
  140. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/pyoframe-performance.ipynb +0 -0
  141. {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/three-bus-four-gen.png +0 -0
  142. {pyoframe-1.0.1 → pyoframe-1.2.0}/giscus.json +0 -0
  143. {pyoframe-1.0.1 → pyoframe-1.2.0}/mkdocs.yml +0 -0
  144. {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/dependency_links.txt +0 -0
  145. {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/top_level.txt +0 -0
  146. {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_assign_ids_constraints.py +0 -0
  147. {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_assign_ids_constraints_2.py +0 -0
  148. {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_assign_ids_variables.py +0 -0
  149. {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_attr_performance.py +0 -0
  150. {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/generate_api_reference.py +0 -0
  151. {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/griffe_extensions.py +0 -0
  152. {pyoframe-1.0.1 → pyoframe-1.2.0}/setup.cfg +0 -0
  153. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/__init__.py +0 -0
  154. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/conftest.py +0 -0
  155. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/README.md +0 -0
  156. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/__init__.py +0 -0
  157. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/__init__.py +0 -0
  158. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/input_data/orders.csv +0 -0
  159. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/input_data/parameters.csv +0 -0
  160. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/model.py +0 -0
  161. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/objective.csv +0 -0
  162. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-copt-machine.lp +0 -0
  163. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-copt-pretty.lp +0 -0
  164. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-gurobi-machine.lp +0 -0
  165. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-gurobi-pretty.lp +0 -0
  166. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-highs-pretty.lp +0 -0
  167. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/README.md +0 -0
  168. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/__init__.py +0 -0
  169. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/input_data/foods.csv +0 -0
  170. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/input_data/foods_to_nutrients.csv +0 -0
  171. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/input_data/nutrients.csv +0 -0
  172. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/model_gurobipy.py +0 -0
  173. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/Buy.csv +0 -0
  174. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/Buy_ub.csv +0 -0
  175. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/max_nutrients.csv +0 -0
  176. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/min_nutrients.csv +0 -0
  177. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/objective.csv +0 -0
  178. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-copt-machine.lp +0 -0
  179. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-copt-pretty.lp +0 -0
  180. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-gurobi-machine.lp +0 -0
  181. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-gurobi-pretty.lp +0 -0
  182. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-highs-pretty.lp +0 -0
  183. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-copt-machine.sol +0 -0
  184. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-copt-pretty.sol +0 -0
  185. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-gurobi-machine.sol +0 -0
  186. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-gurobi-pretty.sol +0 -0
  187. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-highs-pretty.sol +0 -0
  188. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/__init__.py +0 -0
  189. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/results/objective.csv +0 -0
  190. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/results/problem-gurobi-machine.lp +0 -0
  191. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/results/problem-gurobi-pretty.lp +0 -0
  192. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/__init__.py +0 -0
  193. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/input_data/plants.csv +0 -0
  194. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/input_data/transport_costs.csv +0 -0
  195. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/input_data/wharehouses.csv +0 -0
  196. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/model.py +0 -0
  197. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/model_gurobipy.py +0 -0
  198. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/objective.csv +0 -0
  199. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/open.csv +0 -0
  200. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-copt-machine.lp +0 -0
  201. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-copt-pretty.lp +0 -0
  202. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-gurobi-machine.lp +0 -0
  203. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-gurobi-pretty.lp +0 -0
  204. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-highs-pretty.lp +0 -0
  205. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-copt-machine.sol +0 -0
  206. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-copt-pretty.sol +0 -0
  207. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-gurobi-machine.sol +0 -0
  208. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-gurobi-pretty.sol +0 -0
  209. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-highs-pretty.sol +0 -0
  210. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/transport.csv +0 -0
  211. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/input_data/assets.csv +0 -0
  212. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/input_data/covariance.csv +0 -0
  213. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/input_data/portfolio_params.csv +0 -0
  214. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/model.py +0 -0
  215. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/con_min_return.csv +0 -0
  216. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/con_weights_sum.csv +0 -0
  217. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/objective.csv +0 -0
  218. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-copt-machine.lp +0 -0
  219. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-copt-pretty.lp +0 -0
  220. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-gurobi-machine.lp +0 -0
  221. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-gurobi-pretty.lp +0 -0
  222. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-highs-pretty.lp +0 -0
  223. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-copt-machine.sol +0 -0
  224. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-copt-pretty.sol +0 -0
  225. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-gurobi-machine.sol +0 -0
  226. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-gurobi-pretty.sol +0 -0
  227. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-highs-pretty.sol +0 -0
  228. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/weight.csv +0 -0
  229. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/__init__.py +0 -0
  230. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/input_data/machines_availability.csv +0 -0
  231. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/input_data/processing_times.csv +0 -0
  232. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/input_data/products_profit.csv +0 -0
  233. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/model.py +0 -0
  234. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/objective.csv +0 -0
  235. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-copt-machine.lp +0 -0
  236. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-copt-pretty.lp +0 -0
  237. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-gurobi-machine.lp +0 -0
  238. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-gurobi-pretty.lp +0 -0
  239. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-highs-pretty.lp +0 -0
  240. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-copt-machine.sol +0 -0
  241. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-copt-pretty.sol +0 -0
  242. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-gurobi-machine.sol +0 -0
  243. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-gurobi-pretty.sol +0 -0
  244. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-highs-pretty.sol +0 -0
  245. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution.csv +0 -0
  246. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/README.md +0 -0
  247. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/__init__.py +0 -0
  248. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/input_data/elspot-prices_2021_hourly_eur.csv +0 -0
  249. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/model.py +0 -0
  250. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/Pump.csv +0 -0
  251. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/Storage_level.csv +0 -0
  252. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/Turb.csv +0 -0
  253. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/objective.csv +0 -0
  254. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-copt-machine.lp +0 -0
  255. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-copt-pretty.lp +0 -0
  256. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-gurobi-machine.lp +0 -0
  257. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-gurobi-pretty.lp +0 -0
  258. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-highs-pretty.lp +0 -0
  259. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-copt-machine.sol +0 -0
  260. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-copt-pretty.sol +0 -0
  261. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-gurobi-machine.sol +0 -0
  262. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-gurobi-pretty.sol +0 -0
  263. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-highs-pretty.sol +0 -0
  264. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/__init__.py +0 -0
  265. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/input_data/initial_numbers.csv +0 -0
  266. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/model.py +0 -0
  267. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-copt-machine.lp +0 -0
  268. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-copt-pretty.lp +0 -0
  269. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-gurobi-machine.lp +0 -0
  270. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-gurobi-pretty.lp +0 -0
  271. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-highs-pretty.lp +0 -0
  272. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-copt-machine.sol +0 -0
  273. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-copt-pretty.sol +0 -0
  274. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-gurobi-machine.sol +0 -0
  275. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-gurobi-pretty.sol +0 -0
  276. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-highs-pretty.sol +0 -0
  277. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution.csv +0 -0
  278. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_constraint.py +0 -0
  279. {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_solver.py +0 -0
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "17:00"
8
+ allow:
9
+ - dependency-type: production
10
+ - dependency-name: highsbox
11
+ - dependency-name: llvmlite
@@ -55,7 +55,8 @@ jobs:
55
55
  exit 1
56
56
  run:
57
57
  if: |
58
- (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
58
+ (github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]') ||
59
+ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') ||
59
60
  (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) ||
60
61
  (github.event_name != 'pull_request_target' && github.event_name != 'pull_request')
61
62
  runs-on: ubuntu-latest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyoframe
3
- Version: 1.0.1
3
+ Version: 1.2.0
4
4
  Summary: Blazing fast linear program interface
5
5
  Author-email: Bravos Power <dev@bravospower.com>
6
6
  License-Expression: MIT
@@ -20,21 +20,21 @@ Requires-Dist: pyarrow
20
20
  Requires-Dist: pandas<3
21
21
  Requires-Dist: pyoptinterface==0.5.1
22
22
  Provides-Extra: highs
23
- Requires-Dist: highsbox<=1.11.0; extra == "highs"
23
+ Requires-Dist: highsbox<=1.12.0; extra == "highs"
24
24
  Provides-Extra: ipopt
25
25
  Requires-Dist: pyoptinterface[nlp]; extra == "ipopt"
26
- Requires-Dist: llvmlite<=0.44.0; extra == "ipopt"
26
+ Requires-Dist: llvmlite<=0.46.0; extra == "ipopt"
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: ruff==0.12.11; extra == "dev"
29
29
  Requires-Dist: polars>=1.32.3; extra == "dev"
30
30
  Requires-Dist: pytest==8.4.1; extra == "dev"
31
31
  Requires-Dist: pytest-cov==6.2.1; extra == "dev"
32
- Requires-Dist: sybil[pytest]==9.2.0; extra == "dev"
32
+ Requires-Dist: sybil[pytest]==9.3.0; extra == "dev"
33
33
  Requires-Dist: pre-commit==4.3.0; extra == "dev"
34
34
  Requires-Dist: gurobipy==12.0.3; extra == "dev"
35
35
  Requires-Dist: coverage==7.10.6; extra == "dev"
36
36
  Requires-Dist: ipykernel==6.30.1; extra == "dev"
37
- Requires-Dist: highsbox<=1.11.0; extra == "dev"
37
+ Requires-Dist: highsbox<=1.12.0; extra == "dev"
38
38
  Requires-Dist: pyoptinterface[nlp]; extra == "dev"
39
39
  Requires-Dist: numpy; extra == "dev"
40
40
  Provides-Extra: docs
@@ -4,10 +4,10 @@ nav:
4
4
  - Home: index.md
5
5
  - Learn:
6
6
  - learn/get-started
7
+ - learn/develop
7
8
  - learn/concepts
8
- - learn/advanced-concepts
9
9
  - learn/migrate
10
10
  - examples
11
- - reference
11
+ - Reference API: reference
12
12
  - why-pyoframe/index.md
13
13
  - contribute
@@ -0,0 +1,65 @@
1
+ ---
2
+ hide:
3
+ - navigation
4
+ ---
5
+ # Contribute
6
+
7
+ Contributions are more than welcome! Submit a pull request, or [open an issue](https://github.com/Bravos-Power/pyoframe/issues/new) and I (Martin) will gladly answer your questions on how to contribute.
8
+
9
+ ## Setup a development environment
10
+
11
+ 1. Clone this repository.
12
+ ```console
13
+ git clone https://github.com/Bravos-Power/pyoframe
14
+ ```
15
+
16
+ 2. Install the dependencies.
17
+ ```console
18
+ pip install --editable .[dev,docs]
19
+ ```
20
+
21
+ 3. Install the pre-commit hooks.
22
+ ```console
23
+ pre-commit install
24
+ ```
25
+
26
+ ## Running the test suite
27
+
28
+ Run `pytest` to execute the test suite. (If you'd like to view coverage information add the flag `--cov` and then run `coverage html`.)
29
+
30
+ The only errors you should see when running the test suite are those related to a solver not being installed.
31
+
32
+ Pyoframe has several types of tests.
33
+
34
+ 1. Your typical unit tests under the `tests/` folder.
35
+
36
+ 2. Integration tests in `tests/test_examples.py`. These tests will run all the models in `tests/examples` and make sure that your changes haven't altered the model results (stored under `tests/examples/<model>/results`). In the rare cases where you _want_ the model results to change (e.g. if you've changed the model), you can regenerate the results using `python -m tests.test_examples`.
37
+
38
+ 3. Doctests in the docstrings of the source code (`src/`).
39
+
40
+ 4. Documentation tests (in `docs/`). All Python code blocks in the documentation are run to ensure the documentation doesn't become outdated. This is done using Sybil. Refer to the [Sybil documentation](https://sybil.readthedocs.io/en/latest/markdown.html#code-blocks) to learn how to create setup code or skip code blocks you don't wish to test.
41
+
42
+ !!! warning "Non-breaking spaces"
43
+ Be aware that Pyoframe uses non-breaking spaces to improve the formatting of expressions. If your Sybil tests are unexpectedly failing, make sure that the expected output contains all the needed non-breaking spaces.
44
+
45
+ ## Writing documentation
46
+
47
+ You can preview the documentation website by running `mkdocs serve` and navigating to [`http://127.0.0.1:8000/pyoframe/`](http://127.0.0.1:8000/pyoframe/).
48
+
49
+ We use [Material Docs](https://squidfunk.github.io/mkdocs-material/) for documentation with several plugins to enable features like automatically compiling the docstrings into the reference API. Please follow the [Google docstring style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) and the [Google style guide](https://developers.google.com/style). We use Mike to allow readers to view the documentation for previous releases (preview available via `mike serve`).
50
+
51
+ ## Linting and formatting
52
+
53
+ We use Ruff for linting and formatting. The pre-commit hooks will run `ruff format` on commit. You should also make sure `ruff check` returns no errors before submitting a PR. To format code blocks in the documentation run: `doccmd --language=python --no-pad-file --command="ruff format" docs/`.
54
+
55
+ ## Additional tips
56
+
57
+ I recommend skimming or reading the [Internal Details](../learn/concepts/internals.md) page for some background on how Pyoframe works.
58
+
59
+ For core developers:
60
+
61
+ - If you use `.unique`, `.join`, `.sort`, or `.group_by` on a Polars dataframe, make sure to set the `maintain_order` parameter appropriately (typically, `maintain_order=Config.maintain_order`).
62
+
63
+ For repository maintainers:
64
+
65
+ - Our CI pipeline on Github Actions requires a Gurobi and COPT license to run. If the Gurobi license expires, generate a new one and copy the contents of the `guorbi.lic` file into the `GUROBI_WLS` Github secret (Settings -> Secrets and variables -> actions). Similarly, if the COPT license expires, request a new academic license (or email COPT sales for a free one) and copy the contents of both license files to the matching Github secrets.
@@ -26,12 +26,12 @@ model.customers = model.x_axis * model.y_axis # (1)!
26
26
 
27
27
 
28
28
  model.facility_position = pf.Variable(model.facilities, model.axis, lb=0, ub=1)
29
- model.customer_position_x = pd.DataFrame(
29
+ model.customer_position_x = pf.Param(
30
30
  {"x": range(G), "x_pos": [step / (G - 1) for step in range(G)]}
31
- ).to_expr()
32
- model.customer_position_y = pd.DataFrame(
31
+ )
32
+ model.customer_position_y = pf.Param(
33
33
  {"y": range(G), "y_pos": [step / (G - 1) for step in range(G)]}
34
- ).to_expr()
34
+ )
35
35
 
36
36
  model.max_distance = pf.Variable(lb=0)
37
37
 
@@ -1,5 +1,5 @@
1
1
  nav:
2
- - datastructure.md
2
+ - addition.md
3
3
  - performance.md
4
4
  - quadratics.md
5
5
  - troubleshooting.md
@@ -4,19 +4,13 @@ In Pyoframe, [`Expression`][pyoframe.Expression] objects can be added using the
4
4
 
5
5
  However, sometimes an addition is ambiguous or indicative of a potential mistake in your model. In these situations, Pyoframe forces you to use _addition modifiers_ to specify exactly how you'd like the addition to be performed. This safety feature helps prevent and quickly fix mistakes in your model.
6
6
 
7
- There are three common addition modifiers in Pyoframe: [`.over(…)`][pyoframe.Expression.over], [`.keep_extras()`][pyoframe.Expression.keep_extras], and [`.drop_extras()`][pyoframe.Expression.drop_extras].
7
+ There are three common addition modifiers in Pyoframe: [`.over(…)`][pyoframe.Expression.over], [`.keep_extras()`][pyoframe.Expression.keep_extras], and [`.drop_extras()`][pyoframe.Expression.drop_extras]. We'll discuss each of these as well as how the bitwise OR operator (`|`) can be used as a shortcut.
8
8
 
9
- Before delving into these addition modifiers, please note that **these addition rules also apply to subtraction as well as the `<=` and `>=` operators used to create constraints**. This is because subtraction is actually computed as an addition (`a - b` is computed as `a + (-b)`). Similarly, creating a constraint with the `<=` or `>=` operators involves combining the left and right hand sides using addition (`a <= b` becomes `a + (-b) <= 0`). So, although I may only mention addition from now on, please remember that this page also applies to subtraction and to constraint creation.
9
+ !!! warning "Addition modifiers also apply to subtraction and constraint creation"
10
10
 
11
- The rest of the page is organized as follows:
11
+ Please note that **the addition rules described here also apply to subtraction as well as the `<=` and `>=` operators used to create constraints**. This is because subtraction is actually computed as an addition (`a - b` is computed as `a + (-b)`). Similarly, creating a constraint with the `<=` or `>=` operators involves combining the left and right hand sides using addition (`a <= b` becomes `a + (-b) <= 0`). So, although I may only mention addition from now on, please remember that this page also applies to subtraction and to constraint creation.
12
12
 
13
- 1. [The `.over(…)` addition modifier](#adding-expressions-with-differing-dimensions-using-over)
14
-
15
- 2. [The `.keep_extras()` and `.drop_extras()` addition modifiers](#handling-extra-labels-with-keep_extras-and-drop_extras)
16
-
17
- 3. [Important note on the order of operations of addition modifiers](#order-of-operations-for-addition-modifiers)
18
-
19
- ## Adding expressions with differing dimensions using `.over(…)`
13
+ ## `.over(…)`
20
14
 
21
15
  To help catch mistakes, adding expressions with differing dimensions is disallowed by default. [`.over(…)`][pyoframe.Expression.over] overrides this default and **indicates that an addition should be performed by "broadcasting" the differing dimensions.**
22
16
 
@@ -31,14 +25,13 @@ import pyoframe as pf
31
25
  import polars as pl
32
26
 
33
27
  air_data = pl.DataFrame({"flight_no": ["A4543", "K937"], "emissions": [1.4, 2.4]})
34
- ground_data = pl.DataFrame(
35
- {"flight_number": ["A4543", "K937"], "emissions": [0.02, 0.05]}
36
- )
37
28
 
38
29
  model = pf.Model()
39
30
  model.Fly = pf.Variable(air_data["flight_no"], vtype="binary")
40
31
  model.air_emissions = model.Fly * air_data
41
- model.ground_emissions = ground_data.to_expr()
32
+ model.ground_emissions = pf.Param(
33
+ {"flight_number": ["A4543", "K937"], "emissions": [0.02, 0.05]}
34
+ )
42
35
  -->
43
36
 
44
37
  ```pycon
@@ -63,7 +56,7 @@ Do you understand what happened? The error informs us that `model.air_emissions`
63
56
 
64
57
  Benign mistakes like these are relatively common and Pyoframe's error messages help you detect them early. Now, let's examine a case where `.over(…)` is needed.
65
58
 
66
- ### Example 2: Broadcasting with `.over(…)`
59
+ ### Example 2: Broadcasting
67
60
 
68
61
  Say, you'd like to see what happens if, instead of minimizing total emissions, you were to minimize the emissions of the _most emitting flight_. Mathematically, this is equivalent to minimizing variable `E_max` where `E_max` is constrained to be greater or equal to the emissions of every flight.
69
62
 
@@ -90,7 +83,7 @@ What we'd like to do is effectively 'copy' (aka. 'broadcast') `E_max` _over_ eve
90
83
 
91
84
  ```pycon
92
85
  >>> model.E_max.over("flight_no")
93
- <Expression terms=1 type=linear>
86
+ <Expression (linear) terms=1>
94
87
  ┌───────────┬────────────┐
95
88
  │ flight_no ┆ expression │
96
89
  ╞═══════════╪════════════╡
@@ -104,7 +97,7 @@ Notice how applying `.over("flight_no")` added a dimension `flight_no` with valu
104
97
  ```pycon
105
98
  >>> model.emission_constraint = model.E_max.over("flight_no") >= model.flight_emissions
106
99
  >>> model.emission_constraint
107
- <Constraint 'emission_constraint' height=2 terms=6 type=linear>
100
+ <Constraint 'emission_constraint' (linear) height=2 terms=6>
108
101
  ┌───────────┬───────────────────────────────┐
109
102
  │ flight_no ┆ constraint │
110
103
  │ (2) ┆ │
@@ -115,7 +108,7 @@ Notice how applying `.over("flight_no")` added a dimension `flight_no` with valu
115
108
 
116
109
  ```
117
110
 
118
- ## Handling 'extra' labels with `.keep_extras()` and `.drop_extras()`
111
+ ## `.keep_extras()` / `.drop_extras()`
119
112
 
120
113
  Addition is performed by pairing the labels in the left `Expression` with those in the right `Expression`. But, what happens when the left and right labels differ?
121
114
 
@@ -127,19 +120,16 @@ If one of the two expressions in an addition has extras labels not present in th
127
120
  import pyoframe as pf
128
121
  import polars as pl
129
122
 
130
- air_data = pl.DataFrame(
123
+ model = pf.Model()
124
+ model.air_emissions = pf.Param(
131
125
  {
132
126
  "flight_no": ["A4543", "K937", "D2082", "D8432", "D1206"],
133
127
  "emissions": [1.4, 2.4, 4, 7.6, 4],
134
128
  }
135
129
  )
136
- ground_data = pl.DataFrame(
130
+ model.ground_emissions = pf.Param(
137
131
  {"flight_no": ["A4543", "K937", "B3420"], "emissions": [0.02, 0.05, 0.001]}
138
132
  )
139
-
140
- model = pf.Model()
141
- model.air_emissions = air_data.to_expr()
142
- model.ground_emissions = ground_data.to_expr()
143
133
  -->
144
134
 
145
135
  Consider again [example 1](#example-1-catching-a-mistake) where we added air emissions and ground emissions.
@@ -205,7 +195,7 @@ Option 2 hardly seems reasonable this time considering that air emissions make u
205
195
 
206
196
  ```pycon
207
197
  >>> model.air_emissions.keep_extras() + model.ground_emissions.drop_extras()
208
- <Expression height=5 terms=5 type=constant>
198
+ <Expression (parameter) height=5 terms=5>
209
199
  ┌───────────┬────────────┐
210
200
  │ flight_no ┆ expression │
211
201
  │ (5) ┆ │
@@ -219,7 +209,32 @@ Option 2 hardly seems reasonable this time considering that air emissions make u
219
209
 
220
210
  ```
221
211
 
222
- ## Order of operations for addition modifiers
212
+ ## The bitwise OR operator
213
+
214
+ In practice, you'll find that it is common to want to keep extra labels on both sides of an addition or subtraction. As such, Pyoframe offers the bitwise OR operator (`|`) as a convenient shortcut. Instead of
215
+
216
+ <!-- invisible-code-block: python
217
+ a = model.air_emissions
218
+ b = model.ground_emissions
219
+ -->
220
+
221
+ ```python
222
+ a.keep_extras() + b.keep_extras()
223
+ ```
224
+ you can simply write
225
+
226
+ ```python
227
+ a | b
228
+ ```
229
+
230
+ For subtraction, the following lines are equivalent:
231
+
232
+ ```python
233
+ a.keep_extras() - b.keep_extras()
234
+ a | -b
235
+ ```
236
+
237
+ ## Note on order of operations
223
238
 
224
239
  When an operation creates a new [Expression][pyoframe.Expression], any previously applied addition modifiers are discarded to prevent unexpected behaviors. As such, **addition modifiers only work if they're applied _right before_ an addition**. For example, `a.drop_extras().sum("time") + b` won't work but `a.sum("time").drop_extras() + b` will.
225
240
 
@@ -1,7 +1,6 @@
1
1
  # Internal details
2
2
 
3
- Pyoframe's inner workings involve a few tricks that you should be aware of
4
- if you wish to modify Pyoframe's internal code.
3
+ Pyoframe's inner workings involve a few tricks that you should be aware of if you wish to contribute to Pyoframe's code base.
5
4
 
6
5
  ## The zero variable
7
6
 
@@ -16,4 +15,10 @@ constant terms and also simplifies the [handling of quadratics](#quadratics).
16
15
 
17
16
  Internally, [Expression][pyoframe.Expression] is used to represent both linear and quadratic mathematical expressions. When a quadratic expression is formed, column `__quadratic_variable_id` is added to [Expression.data][pyoframe.Expression.data]. If an expression's quadratic terms happen to cancel out (e.g. `(ab + c) - ab`), this column is automatically removed.
18
17
 
19
- Column `__quadratic_variable_id` records the ID of the _second_ variable in a quadratic term (the `b` in `3ab`). For linear terms, which have no second variable, this column contains the [Zero Variable](#the-zero-variable). Quadratic terms are always stored such that the first term's variable ID (in column `__variable_id`) is greater or equal to the second term's variable id (in column `__quadratic_variable_id`). For example, `var_7 * var_8` would be rearranged and stored as `var_8 * var_7`. This helps simplify expressions and provides a useful guarantee: If the variable in the first column (`__variable_id`) is the Zero Variable (`var_0`) we know the variable in the second column must also be the Zero Variable and, thus, the term must be a constant.
18
+ Column `__quadratic_variable_id` records the ID of the _second_ variable in a quadratic term (the `b` in `3ab`). For linear terms, which have no second variable, this column contains the [Zero Variable](#the-zero-variable).
19
+
20
+ Quadratic terms are always stored such that the first term's variable ID (in column `__variable_id`) is greater or equal to the second term's variable id (in column `__quadratic_variable_id`). For example, `var_7 * var_8` would be rearranged and stored as `var_8 * var_7`. This helps simplify expressions and provides a useful guarantee: If the variable in the first column (`__variable_id`) is the Zero Variable (`var_0`) we know the variable in the second column must also be the Zero Variable and, thus, the term must be a constant.
21
+
22
+ ## Division
23
+
24
+ Divisions are rearranged into multiplications when possible. Specifically, `a / b` is computed as `a * (1 / b)` (see `BaseOperableBlock.__truediv__`) except for the special case where `a` is a `float` or `int`. In that case, a Polars operation is used to compute the division (see `Expression.__rtruediv__`).
@@ -0,0 +1,8 @@
1
+ nav:
2
+ - create-a-model.md
3
+ - create-variables.md
4
+ - create-expressions.md
5
+ - create-constraints.md
6
+ - define-objective.md
7
+ - run-and-configure-the-solver.md
8
+ - read-results.md
@@ -0,0 +1,41 @@
1
+ # Create a model
2
+
3
+ To create a model write:
4
+
5
+ ```python
6
+ import pyoframe as pf
7
+
8
+ m = pf.Model()
9
+ ```
10
+
11
+ ## Specify a solver
12
+
13
+ By default, Pyoframe will try to use whichever solver is installed on your computer. To specify a particular solver, use the `solver` argument.
14
+
15
+ === "Gurobi"
16
+
17
+ ```python
18
+ m = pf.Model(solver="gurobi")
19
+ ```
20
+
21
+ === "HiGHS"
22
+
23
+ ```python
24
+ m = pf.Model(solver="highs")
25
+ ```
26
+
27
+ === "COPT"
28
+
29
+ ```python
30
+ m = pf.Model(solver="copt")
31
+ ```
32
+
33
+ === "Ipopt"
34
+
35
+ ```python
36
+ m = pf.Model(solver="ipopt")
37
+ ```
38
+
39
+ ## Advanced options
40
+
41
+ Additional options are detailed in the [`Model`][pyoframe.Model] API documentation.
@@ -0,0 +1,27 @@
1
+ # Create constraints
2
+
3
+ Create constraints by using the `<=`, `>=`, and `==` operators between two expressions. For example,
4
+
5
+ <!-- invisible-code-block: python
6
+ import pyoframe as pf
7
+
8
+ m = pf.Model()
9
+ m.Hours_Worked = pf.Variable({"day": ["Mon", "Tue", "Wed", "Thu", "Fri"]})
10
+
11
+ -->
12
+
13
+ ```python
14
+ m.Con_Max_Weekly_Hours = m.Hours_Worked.sum() <= 40
15
+ ```
16
+
17
+ !!! tip "Naming constraints"
18
+ I like prefixing constraint names with `Con_` to easily distinguish them from other module attributes.
19
+
20
+
21
+ ## Handle extra labels
22
+
23
+ When creating constraints, Pyoframe always merges the left- and right-hand side expressions into a single expression (e.g. `a <= b` becomes `(a - b) <= 0`). Thus, if the left- and/or right-hand sides have labels not present in the other side, you will need to handle these extra labels using `drop_extras()` or `keep_extras()`. Read [Addition and its quirks](../concepts/addition.md) to learn more or see the [diet problem](../../examples/diet.md) for an example.
24
+
25
+ ## Relax a constraint
26
+
27
+ Refer to the API documentation for [`.relax()`][pyoframe.Constraint.relax].
@@ -0,0 +1,95 @@
1
+ # Create expressions
2
+
3
+ Mathematical expressions in Pyoframe are represented by the [`Expression`][pyoframe.Expression] class and can be created in a few ways.
4
+
5
+ ## Using arithmetic operators
6
+
7
+ Expressions are automatically created whenever standard arithmetic operators (`+`, `-`, `*`, `/`, `**`) are used between Pyoframe objects. For example, the following code creates the expression `m.hours_remaining`:
8
+
9
+ ```python
10
+ import pyoframe as pf
11
+
12
+ m = pf.Model()
13
+ m.Hours_Worked = pf.Variable({"day": ["Mon", "Tue", "Wed", "Thu", "Fri"]}, lb=0)
14
+ m.Hours_Sleep = pf.Variable({"day": ["Fri", "Thu", "Wed", "Tue", "Mon"]}, lb=0)
15
+ m.hours_remaining = 24 - m.Hours_Worked - m.Hours_Sleep
16
+ ```
17
+
18
+ ```pycon
19
+ >>> m.hours_remaining
20
+ <Expression (linear) height=5 terms=15>
21
+ ┌─────┬───────────────────────────────────────────┐
22
+ │ day ┆ expression │
23
+ │ (5) ┆ │
24
+ ╞═════╪═══════════════════════════════════════════╡
25
+ │ Mon ┆ 24 - Hours_Worked[Mon] - Hours_Sleep[Mon] │
26
+ │ Tue ┆ 24 - Hours_Worked[Tue] - Hours_Sleep[Tue] │
27
+ │ Wed ┆ 24 - Hours_Worked[Wed] - Hours_Sleep[Wed] │
28
+ │ Thu ┆ 24 - Hours_Worked[Thu] - Hours_Sleep[Thu] │
29
+ │ Fri ┆ 24 - Hours_Worked[Fri] - Hours_Sleep[Fri] │
30
+ └─────┴───────────────────────────────────────────┘
31
+
32
+ ```
33
+
34
+ !!! warning "Pyoframe always aligns labels and dimensions"
35
+
36
+ Pyoframe always performs operations label-by-label. For example, Pyoframe subtracted `m.Hours_Sleep` from `m.Hours_Worked` using the labels; the fact that the days were listed in reverse order in `m.Hours_Sleep` (see above) does not matter.
37
+
38
+ When the left- and/or right-hand side expressions have labels not present in the other, it may be necessary to use `.keep_extras()` or `.drop_extras()` to specify how these extra labels should be handled. Similarly, if one of the two operands is missing a dimension, it may be necessary to use `.over` to force broadcasting. Read [Addition and its quirks](../concepts/addition.md) to learn more.
39
+
40
+
41
+
42
+ ## Using parameters
43
+
44
+ External data can be incorporated into an optimization problem by using [`pf.Param(data)`][pyoframe.Param] which converts a DataFrame into a Pyoframe expression. The last column of the DataFrame will be treated as the expression value, and all other columns will be treated as labels. For example, the following code creates a Pyoframe expression equal to `1` on Friday and `0` otherwise.
45
+
46
+ ```python
47
+ import pandas as pd
48
+
49
+ is_holiday = pd.DataFrame(
50
+ {"day": ["Mon", "Tue", "Wed", "Thu", "Fri"], "is_holiday": [0, 0, 0, 0, 1]}
51
+ )
52
+ ```
53
+
54
+ ```pycon
55
+ >>> pf.Param(is_holiday)
56
+ <Expression (parameter) height=5 terms=5>
57
+ ┌─────┬────────────┐
58
+ │ day ┆ expression │
59
+ │ (5) ┆ │
60
+ ╞═════╪════════════╡
61
+ │ Mon ┆ 0 │
62
+ │ Tue ┆ 0 │
63
+ │ Wed ┆ 0 │
64
+ │ Thu ┆ 0 │
65
+ │ Fri ┆ 1 │
66
+ └─────┴────────────┘
67
+
68
+ ```
69
+
70
+ The expression can then be used like any other Pyoframe object:
71
+
72
+ ```python
73
+ m.holiday_hours_worked = m.Hours_Worked * pf.Param(is_holiday)
74
+ ```
75
+
76
+ Note that `pf.Param` is automatically applied when a Pyoframe object is operated with a DataFrame so the previous line can be simplified to
77
+
78
+ ```python
79
+ m.holiday_hours_worked = m.Hours_Worked * is_holiday
80
+ ```
81
+
82
+ !!! tip "`pf.Param` also accepts file paths"
83
+
84
+ `pf.Param` also accepts a file path to a `.csv` or `.parquet` file, see the [`Param`][pyoframe.Param] API documentation to learn more.
85
+
86
+ ## Using transforms
87
+
88
+ The functions [`sum`][pyoframe.Expression.sum], [`sum_by`][pyoframe.Expression.sum_by], [`map`][pyoframe.Expression.map], [`next`][pyoframe.Variable.next], [`rolling_sum`][pyoframe.Expression.rolling_sum], and [`within`][pyoframe.Expression.within] are _transforms_ that make it easy to convert an expression or variable from one shape to another. For example, `sum` can be used to collapse a dimensioned expression into a dimensionless one:
89
+
90
+ ```pycon
91
+ >>> m.Hours_Worked.sum()
92
+ <Expression (linear) terms=5>
93
+ Hours_Worked[Mon] + Hours_Worked[Tue] + Hours_Worked[Wed] + Hours_Worked[Thu] + Hours_Worked[Fri]
94
+
95
+ ```