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.
- pyoframe-1.2.0/.github/dependabot.yml +11 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/ci.yml +2 -1
- {pyoframe-1.0.1 → pyoframe-1.2.0}/PKG-INFO +5 -5
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/.nav.yml +2 -2
- pyoframe-1.2.0/docs/contribute/index.md +65 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/facility_location.md +4 -4
- {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/.nav.yml +1 -1
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/concepts/addition.md +41 -26
- {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/internals.md +8 -3
- pyoframe-1.2.0/docs/learn/develop/.nav.yml +8 -0
- pyoframe-1.2.0/docs/learn/develop/create-a-model.md +41 -0
- pyoframe-1.2.0/docs/learn/develop/create-constraints.md +27 -0
- pyoframe-1.2.0/docs/learn/develop/create-expressions.md +95 -0
- pyoframe-1.2.0/docs/learn/develop/create-variables.md +215 -0
- pyoframe-1.2.0/docs/learn/develop/define-objective.md +42 -0
- pyoframe-1.2.0/docs/learn/develop/read-results.md +11 -0
- 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
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/example-with-dimensions.md +19 -22
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/example.md +4 -4
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/installation.md +2 -2
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/migrate/v1.0.md +2 -2
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/.nav.yml +0 -1
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/index.md +1 -5
- {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/PKG-INFO +5 -5
- {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/SOURCES.txt +14 -13
- {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/requires.txt +4 -4
- {pyoframe-1.0.1 → pyoframe-1.2.0}/pyproject.toml +4 -4
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/__init__.py +2 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_arithmetic.py +6 -5
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_constants.py +9 -10
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_core.py +104 -57
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_model.py +180 -18
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_model_element.py +2 -0
- pyoframe-1.2.0/src/pyoframe/_monkey_patch.py +38 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_objective.py +2 -2
- pyoframe-1.2.0/src/pyoframe/_param.py +99 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_utils.py +2 -2
- {pyoframe-1.0.1 → pyoframe-1.2.0}/src/pyoframe/_version.py +3 -3
- pyoframe-1.2.0/tests/examples/cutting_stock_problem/results/problem-highs-machine.lp +183 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/model.py +3 -3
- pyoframe-1.2.0/tests/examples/diet_problem/results/problem-highs-machine.lp +21 -0
- pyoframe-1.2.0/tests/examples/diet_problem/results/solution-highs-machine.sol +69 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/model.py +4 -6
- pyoframe-1.2.0/tests/examples/facility_problem/results/problem-highs-machine.lp +29 -0
- pyoframe-1.2.0/tests/examples/facility_problem/results/solution-highs-machine.sol +50 -0
- pyoframe-1.2.0/tests/examples/portfolio_optim/results/problem-highs-machine.lp +14 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-highs-machine.sol +14 -14
- pyoframe-1.2.0/tests/examples/production_planning/results/problem-highs-machine.lp +9 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-highs-machine.sol +10 -10
- pyoframe-1.2.0/tests/examples/pumped_storage/results/problem-highs-machine.lp +8860 -0
- pyoframe-1.2.0/tests/examples/pumped_storage/results/solution-highs-machine.sol +7317 -0
- pyoframe-1.2.0/tests/examples/sudoku/results/problem-highs-machine.lp +1813 -0
- pyoframe-1.2.0/tests/examples/sudoku/results/solution-highs-machine.sol +1090 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_addition.py +62 -13
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_arithmetic.py +102 -7
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_examples.py +10 -4
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_io.py +1 -1
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_model.py +10 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_names.py +18 -2
- pyoframe-1.2.0/tests/test_objective.py +90 -0
- pyoframe-1.2.0/tests/test_param.py +29 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_variable.py +11 -1
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/util.py +4 -2
- pyoframe-1.0.1/.github/dependabot.yml +0 -16
- pyoframe-1.0.1/docs/contribute/index.md +0 -51
- pyoframe-1.0.1/docs/learn/advanced-concepts/datastructure.md +0 -7
- pyoframe-1.0.1/docs/learn/concepts/.nav.yml +0 -5
- pyoframe-1.0.1/docs/learn/concepts/building-blocks.md +0 -17
- pyoframe-1.0.1/docs/learn/concepts/special-functions.md +0 -112
- pyoframe-1.0.1/docs/reference/external/.nav.yml +0 -4
- pyoframe-1.0.1/docs/reference/external/pandas.DataFrame.to_expr.md +0 -3
- pyoframe-1.0.1/docs/reference/external/pandas.Series.to_expr.md +0 -3
- pyoframe-1.0.1/docs/reference/external/polars.DataFrame.to_expr.md +0 -3
- pyoframe-1.0.1/src/pyoframe/_monkey_patch.py +0 -82
- pyoframe-1.0.1/tests/examples/cutting_stock_problem/results/problem-highs-machine.lp +0 -183
- pyoframe-1.0.1/tests/examples/diet_problem/results/problem-highs-machine.lp +0 -21
- pyoframe-1.0.1/tests/examples/diet_problem/results/solution-highs-machine.sol +0 -69
- pyoframe-1.0.1/tests/examples/facility_problem/results/problem-highs-machine.lp +0 -29
- pyoframe-1.0.1/tests/examples/facility_problem/results/solution-highs-machine.sol +0 -50
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-highs-machine.lp +0 -14
- pyoframe-1.0.1/tests/examples/production_planning/results/problem-highs-machine.lp +0 -9
- pyoframe-1.0.1/tests/examples/pumped_storage/results/problem-highs-machine.lp +0 -8876
- pyoframe-1.0.1/tests/examples/pumped_storage/results/solution-highs-machine.sol +0 -7317
- pyoframe-1.0.1/tests/examples/sudoku/results/problem-highs-machine.lp +0 -1813
- pyoframe-1.0.1/tests/examples/sudoku/results/solution-highs-machine.sol +0 -1090
- pyoframe-1.0.1/tests/test_objective.py +0 -22
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.gitattributes +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/CODEOWNERS +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/actions/setup_optimizers_linux/action.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/actions/setup_optimizers_macos/action.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/actions/setup_optimizers_windows/action.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/format.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/lines_changed_counter.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/lint.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/publish_doc.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/publish_doc_dev.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/publish_to_pypi.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.github/workflows/test_doc.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.gitignore +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.pre-commit-config.yaml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.vscode/launch.json +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/.vscode/settings.json +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/CHANGELOG.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/LICENSE +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/README.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/conftest.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/.nav.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/diet.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/index.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/portfolio_optimization.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/examples/production.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/index.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/javascripts/feedback.js +0 -0
- {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/performance.md +0 -0
- {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/quadratics.md +0 -0
- {pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/troubleshooting.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/.nav.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/food_data.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/foods.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/foods_to_nutrients.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/nutrients.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/basic-example/results.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/power_grid_example.ipynb +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/learn/get-started/three-bus.png +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/main.html +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/partials/actions.html +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/partials/comments.html +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/bases/.nav.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/bases/BaseBlock.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/bases/BaseOperableBlock.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/public/.nav.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/public/Config.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/types/.nav.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/reference/types/Operable.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/stylesheets/extra.css +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/data_py.parquet +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/gen_py.parquet +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/index.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/pyoframe-performance.ipynb +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/docs/why-pyoframe/three-bus-four-gen.png +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/giscus.json +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/mkdocs.yml +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/dependency_links.txt +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/pyoframe.egg-info/top_level.txt +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_assign_ids_constraints.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_assign_ids_constraints_2.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_assign_ids_variables.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/archive/benchmark_attr_performance.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/generate_api_reference.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/scripts/griffe_extensions.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/setup.cfg +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/conftest.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/README.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/input_data/orders.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/input_data/parameters.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/model.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/objective.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-copt-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-copt-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/cutting_stock_problem/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/README.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/input_data/foods.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/input_data/foods_to_nutrients.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/input_data/nutrients.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/model_gurobipy.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/Buy.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/Buy_ub.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/max_nutrients.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/min_nutrients.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/objective.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-copt-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-copt-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-copt-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-copt-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/diet_problem/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/results/objective.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_location/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/input_data/plants.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/input_data/transport_costs.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/input_data/wharehouses.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/model.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/model_gurobipy.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/objective.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/open.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-copt-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-copt-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-copt-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-copt-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/facility_problem/results/transport.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/input_data/assets.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/input_data/covariance.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/input_data/portfolio_params.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/model.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/con_min_return.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/con_weights_sum.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/objective.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-copt-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-copt-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-copt-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-copt-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/portfolio_optim/results/weight.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/input_data/machines_availability.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/input_data/processing_times.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/input_data/products_profit.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/model.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/objective.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-copt-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-copt-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-copt-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-copt-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/production_planning/results/solution.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/README.md +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/input_data/elspot-prices_2021_hourly_eur.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/model.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/Pump.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/Storage_level.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/Turb.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/objective.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-copt-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-copt-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-copt-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-copt-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/pumped_storage/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/__init__.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/input_data/initial_numbers.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/model.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-copt-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-copt-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-copt-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-copt-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/examples/sudoku/results/solution.csv +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_constraint.py +0 -0
- {pyoframe-1.0.1 → pyoframe-1.2.0}/tests/test_solver.py +0 -0
|
@@ -55,7 +55,8 @@ jobs:
|
|
|
55
55
|
exit 1
|
|
56
56
|
run:
|
|
57
57
|
if: |
|
|
58
|
-
(github.event_name == '
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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 =
|
|
29
|
+
model.customer_position_x = pf.Param(
|
|
30
30
|
{"x": range(G), "x_pos": [step / (G - 1) for step in range(G)]}
|
|
31
|
-
)
|
|
32
|
-
model.customer_position_y =
|
|
31
|
+
)
|
|
32
|
+
model.customer_position_y = pf.Param(
|
|
33
33
|
{"y": range(G), "y_pos": [step / (G - 1) for step in range(G)]}
|
|
34
|
-
)
|
|
34
|
+
)
|
|
35
35
|
|
|
36
36
|
model.max_distance = pf.Variable(lb=0)
|
|
37
37
|
|
|
@@ -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
|
-
|
|
9
|
+
!!! warning "Addition modifiers also apply to subtraction and constraint creation"
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
##
|
|
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
|
|
{pyoframe-1.0.1/docs/learn/advanced-concepts → pyoframe-1.2.0/docs/learn/concepts}/internals.md
RENAMED
|
@@ -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).
|
|
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,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
|
+
```
|