pyoframe 1.0.0a0__tar.gz → 1.0.1__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 (259) hide show
  1. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/actions/setup_optimizers_linux/action.yml +29 -27
  2. pyoframe-1.0.1/.github/dependabot.yml +16 -0
  3. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/ci.yml +8 -5
  4. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.gitignore +1 -1
  5. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.pre-commit-config.yaml +1 -1
  6. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/PKG-INFO +11 -12
  7. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/README.md +3 -3
  8. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/contribute/index.md +4 -4
  9. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/diet.md +3 -3
  10. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/index.md +1 -1
  11. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/performance.md +6 -6
  12. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/concepts/.nav.yml +1 -0
  13. pyoframe-1.0.1/docs/learn/concepts/addition.md +238 -0
  14. pyoframe-1.0.1/docs/learn/concepts/solver-access.md +104 -0
  15. pyoframe-1.0.1/docs/learn/concepts/special-functions.md +112 -0
  16. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/example-with-dimensions.md +2 -2
  17. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/installation.md +21 -13
  18. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/power_grid_example.ipynb +2 -4
  19. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/migrate/v1.0.md +28 -9
  20. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/main.html +5 -4
  21. pyoframe-1.0.1/docs/reference/.nav.yml +6 -0
  22. pyoframe-1.0.1/docs/reference/bases/.nav.yml +3 -0
  23. pyoframe-1.0.1/docs/reference/bases/BaseBlock.md +3 -0
  24. pyoframe-1.0.1/docs/reference/bases/BaseOperableBlock.md +3 -0
  25. pyoframe-1.0.1/docs/reference/external/.nav.yml +4 -0
  26. pyoframe-1.0.1/docs/reference/external/pandas.Series.to_expr.md +3 -0
  27. pyoframe-1.0.1/docs/reference/index.md +32 -0
  28. pyoframe-1.0.0a0/docs/reference/pyoframe.Config.md → pyoframe-1.0.1/docs/reference/public/Config.md +3 -3
  29. pyoframe-1.0.1/docs/reference/types/.nav.yml +2 -0
  30. pyoframe-1.0.1/docs/reference/types/Operable.md +3 -0
  31. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/index.md +1 -1
  32. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/pyoframe-performance.ipynb +5 -5
  33. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/mkdocs.yml +2 -2
  34. {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/PKG-INFO +11 -12
  35. {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/SOURCES.txt +51 -8
  36. {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/requires.txt +5 -6
  37. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/pyproject.toml +18 -13
  38. pyoframe-1.0.1/scripts/generate_api_reference.py +25 -0
  39. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/griffe_extensions.py +1 -1
  40. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_arithmetic.py +176 -174
  41. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_constants.py +94 -47
  42. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_core.py +225 -148
  43. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_model.py +46 -26
  44. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_model_element.py +32 -18
  45. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_monkey_patch.py +15 -13
  46. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_objective.py +2 -4
  47. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_utils.py +8 -9
  48. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_version.py +3 -3
  49. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/README.md +1 -1
  50. pyoframe-1.0.1/tests/examples/cutting_stock_problem/results/problem-copt-machine.lp +89 -0
  51. pyoframe-1.0.1/tests/examples/cutting_stock_problem/results/problem-copt-pretty.lp +190 -0
  52. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/model.py +2 -6
  53. pyoframe-1.0.1/tests/examples/diet_problem/results/problem-copt-machine.lp +29 -0
  54. pyoframe-1.0.1/tests/examples/diet_problem/results/problem-copt-pretty.lp +36 -0
  55. pyoframe-1.0.1/tests/examples/diet_problem/results/solution-copt-machine.sol +11 -0
  56. pyoframe-1.0.1/tests/examples/diet_problem/results/solution-copt-pretty.sol +11 -0
  57. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/model.py +4 -13
  58. pyoframe-1.0.1/tests/examples/facility_problem/results/problem-copt-machine.lp +29 -0
  59. pyoframe-1.0.1/tests/examples/facility_problem/results/problem-copt-pretty.lp +41 -0
  60. pyoframe-1.0.1/tests/examples/facility_problem/results/solution-copt-machine.sol +27 -0
  61. pyoframe-1.0.1/tests/examples/facility_problem/results/solution-copt-pretty.sol +27 -0
  62. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/model.py +32 -13
  63. pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-copt-machine.lp +20 -0
  64. pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-copt-pretty.lp +24 -0
  65. pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-highs-machine.lp +14 -0
  66. pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-highs-pretty.lp +14 -0
  67. pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-copt-machine.sol +7 -0
  68. pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-copt-pretty.sol +7 -0
  69. pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-highs-machine.sol +37 -0
  70. pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-highs-pretty.sol +37 -0
  71. pyoframe-1.0.1/tests/examples/production_planning/results/problem-copt-machine.lp +11 -0
  72. pyoframe-1.0.1/tests/examples/production_planning/results/problem-copt-pretty.lp +13 -0
  73. pyoframe-1.0.1/tests/examples/production_planning/results/solution-copt-machine.sol +5 -0
  74. pyoframe-1.0.1/tests/examples/production_planning/results/solution-copt-pretty.sol +5 -0
  75. pyoframe-1.0.1/tests/examples/pumped_storage/results/problem-copt-machine.lp +8146 -0
  76. pyoframe-1.0.1/tests/examples/pumped_storage/results/problem-copt-pretty.lp +20447 -0
  77. pyoframe-1.0.1/tests/examples/pumped_storage/results/solution-copt-machine.sol +4382 -0
  78. pyoframe-1.0.1/tests/examples/pumped_storage/results/solution-copt-pretty.sol +4382 -0
  79. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/model.py +1 -1
  80. pyoframe-1.0.1/tests/examples/sudoku/results/problem-copt-machine.lp +1469 -0
  81. pyoframe-1.0.1/tests/examples/sudoku/results/problem-copt-pretty.lp +1513 -0
  82. pyoframe-1.0.1/tests/examples/sudoku/results/solution-copt-machine.sol +731 -0
  83. pyoframe-1.0.1/tests/examples/sudoku/results/solution-copt-pretty.sol +731 -0
  84. pyoframe-1.0.0a0/tests/test_arithmetic.py → pyoframe-1.0.1/tests/test_addition.py +124 -239
  85. pyoframe-1.0.1/tests/test_arithmetic.py +164 -0
  86. pyoframe-1.0.1/tests/test_constraint.py +38 -0
  87. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_examples.py +17 -5
  88. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_io.py +7 -4
  89. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_model.py +7 -9
  90. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_names.py +1 -1
  91. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_objective.py +0 -4
  92. pyoframe-1.0.1/tests/test_variable.py +68 -0
  93. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/util.py +1 -0
  94. pyoframe-1.0.0a0/docs/learn/concepts/solver-access.md +0 -43
  95. pyoframe-1.0.0a0/docs/learn/concepts/special-functions.md +0 -227
  96. pyoframe-1.0.0a0/docs/reference/index.md +0 -7
  97. pyoframe-1.0.0a0/scripts/generate_api_reference.py +0 -34
  98. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.gitattributes +0 -0
  99. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/CODEOWNERS +0 -0
  100. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/actions/setup_optimizers_macos/action.yml +0 -0
  101. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/actions/setup_optimizers_windows/action.yml +0 -0
  102. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/format.yml +0 -0
  103. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/lines_changed_counter.yml +0 -0
  104. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/lint.yml +0 -0
  105. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/publish_doc.yml +0 -0
  106. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/publish_doc_dev.yml +0 -0
  107. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/publish_to_pypi.yml +0 -0
  108. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/test_doc.yml +0 -0
  109. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.vscode/launch.json +0 -0
  110. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.vscode/settings.json +0 -0
  111. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/CHANGELOG.md +0 -0
  112. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/LICENSE +0 -0
  113. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/conftest.py +0 -0
  114. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/.nav.yml +0 -0
  115. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/.nav.yml +0 -0
  116. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/facility_location.md +0 -0
  117. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/index.md +0 -0
  118. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/portfolio_optimization.md +0 -0
  119. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/production.md +0 -0
  120. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/javascripts/feedback.js +0 -0
  121. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/.nav.yml +0 -0
  122. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/datastructure.md +0 -0
  123. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/internals.md +0 -0
  124. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/quadratics.md +0 -0
  125. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/troubleshooting.md +0 -0
  126. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/concepts/building-blocks.md +0 -0
  127. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/.nav.yml +0 -0
  128. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/example.md +0 -0
  129. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/food_data.csv +0 -0
  130. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/foods.csv +0 -0
  131. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/foods_to_nutrients.csv +0 -0
  132. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/nutrients.csv +0 -0
  133. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/results.csv +0 -0
  134. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/three-bus.png +0 -0
  135. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/partials/actions.html +0 -0
  136. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/partials/comments.html +0 -0
  137. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
  138. {pyoframe-1.0.0a0/docs/reference → pyoframe-1.0.1/docs/reference/external}/pandas.DataFrame.to_expr.md +0 -0
  139. {pyoframe-1.0.0a0/docs/reference → pyoframe-1.0.1/docs/reference/external}/polars.DataFrame.to_expr.md +0 -0
  140. {pyoframe-1.0.0a0/docs/reference → pyoframe-1.0.1/docs/reference/public}/.nav.yml +0 -0
  141. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/stylesheets/extra.css +0 -0
  142. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/data_py.parquet +0 -0
  143. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/gen_py.parquet +0 -0
  144. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/three-bus-four-gen.png +0 -0
  145. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/giscus.json +0 -0
  146. {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/dependency_links.txt +0 -0
  147. {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/top_level.txt +0 -0
  148. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_assign_ids_constraints.py +0 -0
  149. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_assign_ids_constraints_2.py +0 -0
  150. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_assign_ids_variables.py +0 -0
  151. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_attr_performance.py +0 -0
  152. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/setup.cfg +0 -0
  153. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/__init__.py +0 -0
  154. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/__init__.py +0 -0
  155. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/conftest.py +0 -0
  156. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/__init__.py +0 -0
  157. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/__init__.py +0 -0
  158. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/input_data/orders.csv +0 -0
  159. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/input_data/parameters.csv +0 -0
  160. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/model.py +0 -0
  161. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/objective.csv +0 -0
  162. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-gurobi-machine.lp +0 -0
  163. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-gurobi-pretty.lp +0 -0
  164. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-highs-machine.lp +0 -0
  165. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-highs-pretty.lp +0 -0
  166. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/README.md +0 -0
  167. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/__init__.py +0 -0
  168. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/input_data/foods.csv +0 -0
  169. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/input_data/foods_to_nutrients.csv +0 -0
  170. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/input_data/nutrients.csv +0 -0
  171. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/model_gurobipy.py +0 -0
  172. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/Buy.csv +0 -0
  173. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/Buy_ub.csv +0 -0
  174. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/max_nutrients.csv +0 -0
  175. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/min_nutrients.csv +0 -0
  176. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/objective.csv +0 -0
  177. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-gurobi-machine.lp +0 -0
  178. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-gurobi-pretty.lp +0 -0
  179. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-highs-machine.lp +0 -0
  180. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-highs-pretty.lp +0 -0
  181. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-gurobi-machine.sol +0 -0
  182. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-gurobi-pretty.sol +0 -0
  183. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-highs-machine.sol +0 -0
  184. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-highs-pretty.sol +0 -0
  185. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/__init__.py +0 -0
  186. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/results/objective.csv +0 -0
  187. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/results/problem-gurobi-machine.lp +0 -0
  188. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/results/problem-gurobi-pretty.lp +0 -0
  189. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/__init__.py +0 -0
  190. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/input_data/plants.csv +0 -0
  191. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/input_data/transport_costs.csv +0 -0
  192. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/input_data/wharehouses.csv +0 -0
  193. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/model.py +0 -0
  194. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/model_gurobipy.py +0 -0
  195. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/objective.csv +0 -0
  196. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/open.csv +0 -0
  197. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-gurobi-machine.lp +0 -0
  198. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-gurobi-pretty.lp +0 -0
  199. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-highs-machine.lp +0 -0
  200. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-highs-pretty.lp +0 -0
  201. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-gurobi-machine.sol +0 -0
  202. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-gurobi-pretty.sol +0 -0
  203. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-highs-machine.sol +0 -0
  204. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-highs-pretty.sol +0 -0
  205. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/transport.csv +0 -0
  206. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/input_data/assets.csv +0 -0
  207. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/input_data/covariance.csv +0 -0
  208. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/input_data/portfolio_params.csv +0 -0
  209. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/con_min_return.csv +0 -0
  210. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/con_weights_sum.csv +0 -0
  211. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/objective.csv +0 -0
  212. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/problem-gurobi-machine.lp +0 -0
  213. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/problem-gurobi-pretty.lp +0 -0
  214. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/solution-gurobi-machine.sol +0 -0
  215. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/solution-gurobi-pretty.sol +0 -0
  216. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/weight.csv +0 -0
  217. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/__init__.py +0 -0
  218. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/input_data/machines_availability.csv +0 -0
  219. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/input_data/processing_times.csv +0 -0
  220. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/input_data/products_profit.csv +0 -0
  221. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/model.py +0 -0
  222. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/objective.csv +0 -0
  223. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-gurobi-machine.lp +0 -0
  224. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-gurobi-pretty.lp +0 -0
  225. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-highs-machine.lp +0 -0
  226. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-highs-pretty.lp +0 -0
  227. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-gurobi-machine.sol +0 -0
  228. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-gurobi-pretty.sol +0 -0
  229. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-highs-machine.sol +0 -0
  230. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-highs-pretty.sol +0 -0
  231. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution.csv +0 -0
  232. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/README.md +0 -0
  233. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/__init__.py +0 -0
  234. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/input_data/elspot-prices_2021_hourly_eur.csv +0 -0
  235. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/model.py +0 -0
  236. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/Pump.csv +0 -0
  237. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/Storage_level.csv +0 -0
  238. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/Turb.csv +0 -0
  239. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/objective.csv +0 -0
  240. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-gurobi-machine.lp +0 -0
  241. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-gurobi-pretty.lp +0 -0
  242. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-highs-machine.lp +0 -0
  243. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-highs-pretty.lp +0 -0
  244. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-gurobi-machine.sol +0 -0
  245. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-gurobi-pretty.sol +0 -0
  246. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-highs-machine.sol +0 -0
  247. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-highs-pretty.sol +0 -0
  248. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/__init__.py +0 -0
  249. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/input_data/initial_numbers.csv +0 -0
  250. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-gurobi-machine.lp +0 -0
  251. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-gurobi-pretty.lp +0 -0
  252. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-highs-machine.lp +0 -0
  253. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-highs-pretty.lp +0 -0
  254. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-gurobi-machine.sol +0 -0
  255. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-gurobi-pretty.sol +0 -0
  256. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-highs-machine.sol +0 -0
  257. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-highs-pretty.sol +0 -0
  258. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution.csv +0 -0
  259. {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_solver.py +0 -0
@@ -8,9 +8,12 @@ inputs:
8
8
  GUROBI_WLS:
9
9
  description: "..."
10
10
  required: true
11
- # COPT_CLIENT_INI:
12
- # description: "..."
13
- # required: true
11
+ COPT_LICENSE_KEY:
12
+ description: "..."
13
+ required: true
14
+ COPT_LICENSE_DAT:
15
+ description: "..."
16
+ required: true
14
17
  # MOSEK_LICENSE:
15
18
  # description: "..."
16
19
  # required: true
@@ -46,8 +49,7 @@ runs:
46
49
  run: |
47
50
  curl -L -o ~/installers/gurobi.tar.gz https://packages.gurobi.com/12.0/gurobi12.0.2_linux64.tar.gz
48
51
  curl -L -o ~/installers/idaes-solvers.tar.gz https://github.com/IDAES/idaes-ext/releases/download/3.4.2/idaes-solvers-ubuntu2204-x86_64.tar.gz
49
-
50
- # curl -L -o ~/installers/copt.tar.gz https://pub.shanshu.ai/download/copt/7.2.8/linux64/CardinalOptimizer-7.2.8-lnx64.tar.gz
52
+ curl -L -o ~/installers/copt.tar.gz https://pub.shanshu.ai/download/copt/7.2.11/linux64/CardinalOptimizer-7.2.11-lnx64.tar.gz
51
53
  # curl -L -o ~/installers/mosek.tar.bz2 https://download.mosek.com/stable/10.2.0/mosektoolslinux64x86.tar.bz2
52
54
 
53
55
  - name: Setup Gurobi Installation
@@ -76,28 +78,28 @@ runs:
76
78
  timeout_minutes: 1
77
79
  command: gurobi_cl
78
80
 
79
- # - name: Setup COPT Installation
80
- # shell: bash
81
- # env:
82
- # COPT_CLIENT_INI: ${{ inputs.COPT_CLIENT_INI }}
83
- # run: |
84
- # tar xfz ~/installers/copt.tar.gz -C ~/
85
- # ls ~/copt72
86
- # # set environment variables
87
- # export COPT_HOME="${HOME}/copt72"
88
- # echo "COPT_HOME=${COPT_HOME}" >> $GITHUB_ENV
89
- # echo "PATH=${PATH}:${COPT_HOME}/bin" >> $GITHUB_ENV
90
- # echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${COPT_HOME}/lib" >> $GITHUB_ENV
91
- # echo $COPT_HOME
92
-
93
- # # Just use the size-limited license
94
- # # echo "$COPT_CLIENT_INI" > ~/client.ini
95
- # # echo "COPT_LICENSE_DIR=${HOME}" >> $GITHUB_ENV
96
- # - name: Test COPT
97
- # if: ${{ inputs.CHECK_LICENSE == 'true' }}
98
- # shell: bash
99
- # run: |
100
- # copt_cmd -c "quit"
81
+ - name: Setup COPT Installation
82
+ shell: bash
83
+ env:
84
+ COPT_LICENSE_KEY: ${{ inputs.COPT_LICENSE_KEY }}
85
+ COPT_LICENSE_DAT: ${{ inputs.COPT_LICENSE_DAT }}
86
+ run: |
87
+ tar xfz ~/installers/copt.tar.gz -C ~/
88
+ ls ~/copt72
89
+ # set environment variables
90
+ export COPT_HOME="${HOME}/copt72"
91
+ echo "COPT_HOME=${COPT_HOME}" >> $GITHUB_ENV
92
+ echo "PATH=${PATH}:${COPT_HOME}/bin" >> $GITHUB_ENV
93
+ echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${COPT_HOME}/lib" >> $GITHUB_ENV
94
+ echo $COPT_HOME
95
+ echo "$COPT_LICENSE_KEY" > ${COPT_HOME}/license.key
96
+ echo "$COPT_LICENSE_DAT" > ${COPT_HOME}/license.dat
97
+ echo "COPT_LICENSE_DIR=${COPT_HOME}" >> $GITHUB_ENV
98
+ - name: Test COPT
99
+ if: ${{ inputs.CHECK_LICENSE == 'true' }}
100
+ shell: bash
101
+ run: |
102
+ copt_cmd -c "quit"
101
103
 
102
104
  # - name: Setup MOSEK Installation
103
105
  # shell: bash
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "17:00"
8
+ groups:
9
+ python-production-packages:
10
+ dependency-type: production
11
+ patterns:
12
+ - "*"
13
+ python-dev-packages:
14
+ dependency-type: development
15
+ patterns:
16
+ - "*"
@@ -45,9 +45,9 @@ jobs:
45
45
  username: ${{ github.triggering_actor }}
46
46
  env:
47
47
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48
- # If neither has permissions and no 'safe to test' label throw error
48
+ # If neither has permissions and no 'safe to test' label and not dependabot throw error
49
49
  - name: Check User Permission
50
- if: steps.checkAccess.outputs.require-result == 'false' && steps.checkTriggeringAccess.outputs.require-result == 'false' && !contains(github.event.pull_request.labels.*.name, 'safe to test')
50
+ if: steps.checkAccess.outputs.require-result == 'false' && steps.checkTriggeringAccess.outputs.require-result == 'false' && !contains(github.event.pull_request.labels.*.name, 'safe to test') && (github.actor != 'dependabot[bot]')
51
51
  shell: bash
52
52
  run: |
53
53
  echo "${{ github.triggering_actor }} does not have permissions on this repo."
@@ -80,12 +80,15 @@ jobs:
80
80
  with:
81
81
  python-version: ${{ matrix.python-version }}
82
82
  cache: "pip"
83
- - name: Install dependencies
84
- run: pip install -e .[dev]
85
- - uses: ./.github/actions/setup_optimizers_linux
83
+ - name: Setup optimizers
84
+ uses: ./.github/actions/setup_optimizers_linux
86
85
  with:
87
86
  GUROBI_WLS: ${{ secrets.GUROBI_WLS }}
87
+ COPT_LICENSE_KEY: ${{ secrets.COPT_LICENSE_KEY }}
88
+ COPT_LICENSE_DAT: ${{ secrets.COPT_LICENSE_DAT }}
88
89
  CHECK_LICENSE: true
90
+ - name: Install dependencies
91
+ run: pip install -e .[dev]
89
92
  - name: Run tests and collect coverage
90
93
  run: pytest --cov
91
94
  - name: Upload coverage to Codecov
@@ -13,4 +13,4 @@ site
13
13
  .idea
14
14
  src/pyoframe/_version.py
15
15
  htmlcov/
16
- .snakemake
16
+ .snakemake/
@@ -12,4 +12,4 @@ repos:
12
12
  rev: v2025.4.8
13
13
  hooks:
14
14
  - id: doccmd
15
- args: ["--language", "python", "--no-pad-file", "--command", "ruff format", "docs/"]
15
+ args: ["--language", "python", "--no-pad-file", "--no-use-pty", "--command", "ruff format", "docs/"]
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyoframe
3
- Version: 1.0.0a0
3
+ Version: 1.0.1
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,12 +16,11 @@ 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
- Requires-Dist: pandas
22
- Requires-Dist: pyoptinterface<1,>=0.4.1
20
+ Requires-Dist: pandas<3
21
+ Requires-Dist: pyoptinterface==0.5.1
23
22
  Provides-Extra: highs
24
- Requires-Dist: highsbox; extra == "highs"
23
+ Requires-Dist: highsbox<=1.11.0; extra == "highs"
25
24
  Provides-Extra: ipopt
26
25
  Requires-Dist: pyoptinterface[nlp]; extra == "ipopt"
27
26
  Requires-Dist: llvmlite<=0.44.0; extra == "ipopt"
@@ -35,9 +34,9 @@ Requires-Dist: pre-commit==4.3.0; extra == "dev"
35
34
  Requires-Dist: gurobipy==12.0.3; extra == "dev"
36
35
  Requires-Dist: coverage==7.10.6; extra == "dev"
37
36
  Requires-Dist: ipykernel==6.30.1; extra == "dev"
38
- Requires-Dist: highsbox; extra == "dev"
37
+ Requires-Dist: highsbox<=1.11.0; extra == "dev"
39
38
  Requires-Dist: pyoptinterface[nlp]; extra == "dev"
40
- Requires-Dist: llvmlite<=0.44.0; extra == "dev"
39
+ Requires-Dist: numpy; extra == "dev"
41
40
  Provides-Extra: docs
42
41
  Requires-Dist: mkdocs-material~=9.6.18; extra == "docs"
43
42
  Requires-Dist: mkdocstrings[python]~=0.30.0; extra == "docs"
@@ -57,7 +56,7 @@ Dynamic: license-file
57
56
 
58
57
  [![codecov](https://codecov.io/gh/Bravos-Power/pyoframe/graph/badge.svg?token=8258XESRYQ)](https://codecov.io/gh/Bravos-Power/pyoframe)
59
58
  [![Build](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml/badge.svg)](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml)
60
- [![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/)
61
60
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
62
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)
63
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)
@@ -65,9 +64,9 @@ Dynamic: license-file
65
64
 
66
65
  A library to rapidly and memory-efficiently formulate large and sparse optimization models using Pandas or Polars DataFrames.
67
66
 
68
- ## **[Documentation](https://bravos-power.github.io/pyoframe/)**
67
+ ## **[Documentation](https://bravos-power.github.io/pyoframe/latest/)**
69
68
 
70
- [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/index.md).
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).
71
70
 
72
71
 
73
72
  ## Acknowledgments
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![codecov](https://codecov.io/gh/Bravos-Power/pyoframe/graph/badge.svg?token=8258XESRYQ)](https://codecov.io/gh/Bravos-Power/pyoframe)
4
4
  [![Build](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml/badge.svg)](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml)
5
- [![Docs](https://github.com/Bravos-Power/pyoframe/actions/workflows/publish_doc.yml/badge.svg)](https://Bravos-Power.github.io/pyoframe/reference/)
5
+ [![Docs](https://github.com/Bravos-Power/pyoframe/actions/workflows/publish_doc.yml/badge.svg)](https://Bravos-Power.github.io/pyoframe/latest/reference/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
  [![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)
8
8
  [![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)
@@ -10,9 +10,9 @@
10
10
 
11
11
  A library to rapidly and memory-efficiently formulate large and sparse optimization models using Pandas or Polars DataFrames.
12
12
 
13
- ## **[Documentation](https://bravos-power.github.io/pyoframe/)**
13
+ ## **[Documentation](https://bravos-power.github.io/pyoframe/latest/)**
14
14
 
15
- [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/index.md).
15
+ [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).
16
16
 
17
17
 
18
18
  ## Acknowledgments
@@ -42,10 +42,10 @@ We use [Material Docs](https://squidfunk.github.io/mkdocs-material/) for documen
42
42
 
43
43
  ## Additional tips
44
44
 
45
- - 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`).
45
+ For core developers:
46
46
 
47
- ## Details for repository maintainers
47
+ - 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`).
48
48
 
49
- ### Expired Gurobi License
49
+ For repository maintainers:
50
50
 
51
- We use a Gurobi license to run our tests. If the tests fail an give a license expired error, generate a new one and copy the contents of the `guorbi.lic` file into the `GUROBI_WLS` Github secret (Settings -> Secrets and variables -> actions).
51
+ - 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.
@@ -28,9 +28,9 @@ m.Buy = pf.Variable(food["food"], lb=0, ub=food[["food", "stock"]])
28
28
 
29
29
  nutrient_intake = (m.Buy * food_nutrients).sum_by("category")
30
30
  m.min_nutrients = (
31
- nutrients[["category", "min"]] <= nutrient_intake.drop_unmatched() # (1)!
31
+ nutrients[["category", "min"]] <= nutrient_intake.drop_extras() # (1)!
32
32
  )
33
- m.max_nutrients = nutrient_intake.drop_unmatched() <= nutrients[["category", "max"]]
33
+ m.max_nutrients = nutrient_intake.drop_extras() <= nutrients[["category", "max"]]
34
34
 
35
35
  total_cost = (m.Buy * food[["food", "cost"]]).sum()
36
36
  m.minimize = total_cost
@@ -39,7 +39,7 @@ m.minimize = total_cost
39
39
  m.optimize()
40
40
  ```
41
41
 
42
- 1. `.drop_unmatched()` ensures that if `min_nutrient` is `null` for certain foods, no constraint will be created for those foods. [Learn more](../learn/concepts/special-functions.md#drop_unmatched-and-keep_unmatched)
42
+ 1. `.drop_extras()` ensures that if `min_nutrient` is `null` for certain foods, no constraint will be created for those foods. [Learn more](../learn/concepts/addition.md)
43
43
 
44
44
  So the solution is...
45
45
 
@@ -12,7 +12,7 @@ hide:
12
12
  - Seamlessly integrates with pandas thanks to its dataframe-centric design.
13
13
  - Extremely **fast and memory efficient** (even for sparse problems) thanks to Polars, Rust, and Pyoptinterface.
14
14
  - Clear Pythonic syntax for writing mathematical models.
15
- - Supports Gurobi, HiGHS, and Ipopt solvers.
15
+ - Supports Gurobi, HiGHS, Ipopt and COPT solvers.
16
16
  - Continuously used in high-stakes production environments.
17
17
  - 100% open-source (MIT License).
18
18
 
@@ -6,9 +6,9 @@ Pyoframe is already one of the fastest and most memory-efficient libraries for f
6
6
 
7
7
  [Polars](https://pola.rs/) is much faster than Pandas. Moreover, if you use Pandas, there will be a (very small) overhead because Pyoframe converts all DataFrames to Polars prior to computations.
8
8
 
9
- ## Use integer indices
9
+ ## Use integer labels
10
10
 
11
- Pyoframe will work with all types of indices, however integer indices are faster and more memory efficient than alternatives (e.g. string indices).
11
+ Pyoframe works with any label data type (e.g. string labels, date labels, etc.), but integer labels are fastest and most memory efficient.
12
12
 
13
13
  ## Disable `maintain_order`
14
14
 
@@ -18,14 +18,14 @@ By default, Pyoframe ensures that the order of variables, constraints, and mathe
18
18
  pf.Config.maintain_order = False
19
19
  ```
20
20
 
21
- ## Disable unmatched checks
21
+ ## Disable checks for extra values
22
22
 
23
- Disabling unmatched checks means that, instead of raising [unmatched term exceptions](../concepts/special-functions.md#drop_unmatched-and-keep_unmatched), pyoframe will process sums with unmatched terms as if [`keep_unmatched`][pyoframe.Expression.keep_unmatched] had been applied. While this may improve performance, it will silence potentially important errors meant to help you build your model. If you'd like to disable unmatched checks, we recommend you do so only after thoroughly testing your model and ensuring that all potential unmatched term exceptions have been handled.
23
+ Disabling checks for extra values means that, instead of raising [extra value exceptions](../concepts/addition.md), pyoframe will process sums with extra values as if [`keep_extras`][pyoframe.Expression.keep_extras] had been applied. While this may improve performance, it will silence potentially important errors meant to help you build your model. If you'd like to disable checks for extra values, we recommend you do so only after thoroughly testing your model and ensuring that all potential extra value exceptions have been handled.
24
24
 
25
- The following code disables unmatched checks:
25
+ The following code disables checks for extra values:
26
26
 
27
27
  ```python
28
- pf.Config.disable_unmatched_checks = True
28
+ pf.Config.disable_extras_checks = True
29
29
  ```
30
30
 
31
31
  <!-- TODO REVISIT
@@ -1,4 +1,5 @@
1
1
  nav:
2
2
  - building-blocks.md
3
+ - addition.md
3
4
  - special-functions.md
4
5
  - solver-access.md
@@ -0,0 +1,238 @@
1
+ # Addition and its quirks
2
+
3
+ In Pyoframe, [`Expression`][pyoframe.Expression] objects can be added using the `+` operator, as you might expect.
4
+
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
+
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].
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.
10
+
11
+ The rest of the page is organized as follows:
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(…)`
20
+
21
+ 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
+
23
+ The following examples help illustrate when `.over(…)` should and shouldn't be used.
24
+
25
+ ### Example 1: Catching a mistake
26
+
27
+ Say you're developing an optimization model to study aviation emissions. You'd like to express the sum of in-flight emissions and on-the-ground ([taxiing](https://en.wikipedia.org/wiki/Taxiing)) emissions, _for each flight_, but when you try to add both `Expression` objects, you get an error:
28
+
29
+ <!-- invisible-code-block: python
30
+ import pyoframe as pf
31
+ import polars as pl
32
+
33
+ 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
+
38
+ model = pf.Model()
39
+ model.Fly = pf.Variable(air_data["flight_no"], vtype="binary")
40
+ model.air_emissions = model.Fly * air_data
41
+ model.ground_emissions = ground_data.to_expr()
42
+ -->
43
+
44
+ ```pycon
45
+ >>> model.flight_emissions = model.air_emissions + model.ground_emissions
46
+ Traceback (most recent call last):
47
+ ...
48
+ pyoframe._constants.PyoframeError: Cannot add the two expressions below because their
49
+ dimensions are different (['flight_no'] != ['flight_number']).
50
+ Expression 1: air_emissions
51
+ Expression 2: ground_emissions
52
+ If this is intentional, use .over(…) to broadcast. Learn more at
53
+ https://bravos-power.github.io/pyoframe/latest/learn/concepts/addition/#adding-expressions-with-differing-dimensions-using-over
54
+
55
+ ```
56
+
57
+ Do you understand what happened? The error informs us that `model.air_emissions` has dimension _`flight_no`_, but `model.ground_emissions` has dimension _`flight_number`_. Oops, the two datasets use slightly different spellings! You can use [`.rename(…)`][pyoframe.Expression.rename] to correct for the `Expression` objects having differing dimension names.
58
+
59
+ ```pycon
60
+ >>> model.flight_emissions = model.air_emissions + model.ground_emissions.rename({"flight_number": "flight_no"})
61
+
62
+ ```
63
+
64
+ 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
+
66
+ ### Example 2: Broadcasting with `.over(…)`
67
+
68
+ 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
+
70
+ You try to express this constraint in Pyoframe, but get an error:
71
+
72
+ ```pycon
73
+ >>> model.E_max = pf.Variable()
74
+ >>> model.minimize = model.E_max
75
+ >>> model.emission_constraint = model.E_max >= model.flight_emissions
76
+ Traceback (most recent call last):
77
+ ...
78
+ pyoframe._constants.PyoframeError: Cannot subtract the two expressions below because their
79
+ dimensions are different ([] != ['flight_no']).
80
+ Expression 1: E_max
81
+ Expression 2: flight_emissions
82
+ If this is intentional, use .over(…) to broadcast. Learn more at
83
+ https://bravos-power.github.io/pyoframe/latest/learn/concepts/addition/#adding-expressions-with-differing-dimensions-using-over
84
+
85
+ ```
86
+
87
+ The error indicates that `E_max` has no dimensions but `flight_emissions` has dimensions `flight_no`. The error is raised because, by default, combining terms with differing dimensions is not allowed (as explained in [example 1](#example-1-catching-a-mistake)).
88
+
89
+ What we'd like to do is effectively 'copy' (aka. 'broadcast') `E_max` _over_ every flight number. `E_max.over("flight_no")` does just this:
90
+
91
+ ```pycon
92
+ >>> model.E_max.over("flight_no")
93
+ <Expression terms=1 type=linear>
94
+ ┌───────────┬────────────┐
95
+ │ flight_no ┆ expression │
96
+ ╞═══════════╪════════════╡
97
+ │ * ┆ E_max │
98
+ └───────────┴────────────┘
99
+
100
+ ```
101
+
102
+ Notice how applying `.over("flight_no")` added a dimension `flight_no` with value `*`. The asterix (`*`) indicates that `flight_no` will take the shape of whichever expression `E_max` is later combined with. Since `E_max` is being combined with `flight_emissions`, `*` will be replaced with an entry for every flight number in `flight_emissions`. Now creating our constraint works properly:
103
+
104
+ ```pycon
105
+ >>> model.emission_constraint = model.E_max.over("flight_no") >= model.flight_emissions
106
+ >>> model.emission_constraint
107
+ <Constraint 'emission_constraint' height=2 terms=6 type=linear>
108
+ ┌───────────┬───────────────────────────────┐
109
+ │ flight_no ┆ constraint │
110
+ │ (2) ┆ │
111
+ ╞═══════════╪═══════════════════════════════╡
112
+ │ A4543 ┆ E_max -1.4 Fly[A4543] >= 0.02 │
113
+ │ K937 ┆ E_max -2.4 Fly[K937] >= 0.05 │
114
+ └───────────┴───────────────────────────────┘
115
+
116
+ ```
117
+
118
+ ## Handling 'extra' labels with `.keep_extras()` and `.drop_extras()`
119
+
120
+ 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
+
122
+ If one of the two expressions in an addition has extras labels not present in the other, [`.keep_extras()`][pyoframe.Expression.keep_extras] or [`.drop_extras()`][pyoframe.Expression.drop_extras] must be used to indicate how the extra labels should be handled.
123
+
124
+ ### Example 3: Deciding how to handle extra labels
125
+
126
+ <!-- invisible-code-block: python
127
+ import pyoframe as pf
128
+ import polars as pl
129
+
130
+ air_data = pl.DataFrame(
131
+ {
132
+ "flight_no": ["A4543", "K937", "D2082", "D8432", "D1206"],
133
+ "emissions": [1.4, 2.4, 4, 7.6, 4],
134
+ }
135
+ )
136
+ ground_data = pl.DataFrame(
137
+ {"flight_no": ["A4543", "K937", "B3420"], "emissions": [0.02, 0.05, 0.001]}
138
+ )
139
+
140
+ model = pf.Model()
141
+ model.air_emissions = air_data.to_expr()
142
+ model.ground_emissions = ground_data.to_expr()
143
+ -->
144
+
145
+ Consider again [example 1](#example-1-catching-a-mistake) where we added air emissions and ground emissions.
146
+ Let's say that you fixed the error in example 1, but when you try the addition again you get the following error:
147
+
148
+ ```pycon
149
+ >>> model.air_emissions + model.ground_emissions
150
+ Traceback (most recent call last):
151
+ ...
152
+ pyoframe._constants.PyoframeError: Cannot add the two expressions below because expression 1 has extra labels.
153
+ Expression 1: air_emissions
154
+ Expression 2: ground_emissions
155
+ Extra labels in expression 1:
156
+ ┌───────────┐
157
+ │ flight_no │
158
+ ╞═══════════╡
159
+ │ D2082 │
160
+ │ D8432 │
161
+ │ D1206 │
162
+ └───────────┘
163
+ Use .drop_extras() or .keep_extras() to indicate how the extra labels should be handled. Learn more at
164
+ https://bravos-power.github.io/pyoframe/latest/learn/concepts/addition
165
+
166
+ ```
167
+
168
+ Do you understand what happened? The error explains that `air_emissions` contains flight numbers that are not present in `ground_emissions` (specifically flight numbers `D2082`, `D8432`, and `D1206`). Your ground emissions dataset is missing some flights!
169
+
170
+ Pyoframe raised an error because it is unclear how you'd like the addition to be performed. In fact, you have three options:
171
+
172
+ 1. Decide to discard all flights with missing ground data (`model.air_emissions.drop_extras()`).
173
+ 2. Decide to keep all flights, assuming `0` ground emissions when the data is missing (`model.air_emissions.keep_extras()`).
174
+ 3. Go back to your data processing and fix the root cause of the missing data.
175
+
176
+ After investigating, you realize that the data is missing because the ground emissions for those flights were negligible. As such, you decide to use `.keep_extras()` (option 2), effectively setting ground emissions to `0` whenever the data is missing.
177
+
178
+ Let's try again!
179
+
180
+ ```pycon
181
+ >>> model.air_emissions.keep_extras() + model.ground_emissions
182
+ Traceback (most recent call last):
183
+ ...
184
+ pyoframe._constants.PyoframeError: Cannot add the two expressions below because expression 2 has extra labels.
185
+ Expression 1: air_emissions.keep_extras()
186
+ Expression 2: ground_emissions
187
+ Extra labels in expression 2:
188
+ ┌───────────┐
189
+ │ flight_no │
190
+ ╞═══════════╡
191
+ │ B3420 │
192
+ └───────────┘
193
+ Use .drop_extras() or .keep_extras() to indicate how the extra labels should be handled. Learn more at
194
+ https://bravos-power.github.io/pyoframe/latest/learn/concepts/addition
195
+
196
+ ```
197
+
198
+ Argh, another error! Do you understand what happened? This time `ground_emissions` has extra labels: flight `B3420` is present in `ground_emissions` but not `air_emissions`. Again, Pyoframe raised an error because it is unclear what should be done:
199
+
200
+ 1. Discard flight `B3420` because the air emissions data is missing.
201
+ 2. Keep flight `B3420`, assuming the air emissions data is `0`.
202
+ 3. Go back to your data processing and fix the root cause of the missing air emissions data.
203
+
204
+ Option 2 hardly seems reasonable this time considering that air emissions make up the majority of a flight's emissions. You end up deciding to discard the flight entirely (option 1) using `.drop_extras()`. Now, the addition works perfectly!
205
+
206
+ ```pycon
207
+ >>> model.air_emissions.keep_extras() + model.ground_emissions.drop_extras()
208
+ <Expression height=5 terms=5 type=constant>
209
+ ┌───────────┬────────────┐
210
+ │ flight_no ┆ expression │
211
+ │ (5) ┆ │
212
+ ╞═══════════╪════════════╡
213
+ │ A4543 ┆ 1.42 │
214
+ │ K937 ┆ 2.45 │
215
+ │ D2082 ┆ 4 │
216
+ │ D8432 ┆ 7.6 │
217
+ │ D1206 ┆ 4 │
218
+ └───────────┴────────────┘
219
+
220
+ ```
221
+
222
+ ## Order of operations for addition modifiers
223
+
224
+ 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
+
226
+ There are two exceptions to this rule:
227
+
228
+ 1. _Negation_. Negation preserves addition modifiers. If it weren't for this exception, `-my_obj.drop_extras()` wouldn't work as expected; you would have to write `(-my_obj).drop_extras()` which is unintuitive!
229
+
230
+ 2. _Addition/subtraction_. A `.keep_extras()` or `.drop_extras()` in the left and/or right side of an addition or subtraction is preserved in the result because this allows you to write
231
+ ```
232
+ a.keep_extras() + b.keep_extras() + c.keep_extras()
233
+ ```
234
+ instead of the annoyingly verbose,
235
+ ```
236
+ (a.keep_extras() + b.keep_extras()).keep_extras() + c.keep_extras()
237
+ ```
238
+ (If the left and right sides have conflicting addition modifiers, e.g., `a.keep_extras() + b.drop_extras()`, no addition modifiers are preserved. Also, if you'd like an addition or subtraction _not_ to preserve addition modifiers, you can force the result back to the default of raising errors whenever there are extra labels by using [`.raise_extras()`][pyoframe.Expression.raise_extras].)