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.
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/actions/setup_optimizers_linux/action.yml +29 -27
- pyoframe-1.0.1/.github/dependabot.yml +16 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/ci.yml +8 -5
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.gitignore +1 -1
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.pre-commit-config.yaml +1 -1
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/PKG-INFO +11 -12
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/README.md +3 -3
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/contribute/index.md +4 -4
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/diet.md +3 -3
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/index.md +1 -1
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/performance.md +6 -6
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/concepts/.nav.yml +1 -0
- pyoframe-1.0.1/docs/learn/concepts/addition.md +238 -0
- pyoframe-1.0.1/docs/learn/concepts/solver-access.md +104 -0
- pyoframe-1.0.1/docs/learn/concepts/special-functions.md +112 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/example-with-dimensions.md +2 -2
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/installation.md +21 -13
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/power_grid_example.ipynb +2 -4
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/migrate/v1.0.md +28 -9
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/main.html +5 -4
- pyoframe-1.0.1/docs/reference/.nav.yml +6 -0
- pyoframe-1.0.1/docs/reference/bases/.nav.yml +3 -0
- pyoframe-1.0.1/docs/reference/bases/BaseBlock.md +3 -0
- pyoframe-1.0.1/docs/reference/bases/BaseOperableBlock.md +3 -0
- pyoframe-1.0.1/docs/reference/external/.nav.yml +4 -0
- pyoframe-1.0.1/docs/reference/external/pandas.Series.to_expr.md +3 -0
- pyoframe-1.0.1/docs/reference/index.md +32 -0
- pyoframe-1.0.0a0/docs/reference/pyoframe.Config.md → pyoframe-1.0.1/docs/reference/public/Config.md +3 -3
- pyoframe-1.0.1/docs/reference/types/.nav.yml +2 -0
- pyoframe-1.0.1/docs/reference/types/Operable.md +3 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/index.md +1 -1
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/pyoframe-performance.ipynb +5 -5
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/mkdocs.yml +2 -2
- {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/PKG-INFO +11 -12
- {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/SOURCES.txt +51 -8
- {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/requires.txt +5 -6
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/pyproject.toml +18 -13
- pyoframe-1.0.1/scripts/generate_api_reference.py +25 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/griffe_extensions.py +1 -1
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_arithmetic.py +176 -174
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_constants.py +94 -47
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_core.py +225 -148
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_model.py +46 -26
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_model_element.py +32 -18
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_monkey_patch.py +15 -13
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_objective.py +2 -4
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_utils.py +8 -9
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/_version.py +3 -3
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/README.md +1 -1
- pyoframe-1.0.1/tests/examples/cutting_stock_problem/results/problem-copt-machine.lp +89 -0
- pyoframe-1.0.1/tests/examples/cutting_stock_problem/results/problem-copt-pretty.lp +190 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/model.py +2 -6
- pyoframe-1.0.1/tests/examples/diet_problem/results/problem-copt-machine.lp +29 -0
- pyoframe-1.0.1/tests/examples/diet_problem/results/problem-copt-pretty.lp +36 -0
- pyoframe-1.0.1/tests/examples/diet_problem/results/solution-copt-machine.sol +11 -0
- pyoframe-1.0.1/tests/examples/diet_problem/results/solution-copt-pretty.sol +11 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/model.py +4 -13
- pyoframe-1.0.1/tests/examples/facility_problem/results/problem-copt-machine.lp +29 -0
- pyoframe-1.0.1/tests/examples/facility_problem/results/problem-copt-pretty.lp +41 -0
- pyoframe-1.0.1/tests/examples/facility_problem/results/solution-copt-machine.sol +27 -0
- pyoframe-1.0.1/tests/examples/facility_problem/results/solution-copt-pretty.sol +27 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/model.py +32 -13
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-copt-machine.lp +20 -0
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-copt-pretty.lp +24 -0
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-highs-machine.lp +14 -0
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/problem-highs-pretty.lp +14 -0
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-copt-machine.sol +7 -0
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-copt-pretty.sol +7 -0
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-highs-machine.sol +37 -0
- pyoframe-1.0.1/tests/examples/portfolio_optim/results/solution-highs-pretty.sol +37 -0
- pyoframe-1.0.1/tests/examples/production_planning/results/problem-copt-machine.lp +11 -0
- pyoframe-1.0.1/tests/examples/production_planning/results/problem-copt-pretty.lp +13 -0
- pyoframe-1.0.1/tests/examples/production_planning/results/solution-copt-machine.sol +5 -0
- pyoframe-1.0.1/tests/examples/production_planning/results/solution-copt-pretty.sol +5 -0
- pyoframe-1.0.1/tests/examples/pumped_storage/results/problem-copt-machine.lp +8146 -0
- pyoframe-1.0.1/tests/examples/pumped_storage/results/problem-copt-pretty.lp +20447 -0
- pyoframe-1.0.1/tests/examples/pumped_storage/results/solution-copt-machine.sol +4382 -0
- pyoframe-1.0.1/tests/examples/pumped_storage/results/solution-copt-pretty.sol +4382 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/model.py +1 -1
- pyoframe-1.0.1/tests/examples/sudoku/results/problem-copt-machine.lp +1469 -0
- pyoframe-1.0.1/tests/examples/sudoku/results/problem-copt-pretty.lp +1513 -0
- pyoframe-1.0.1/tests/examples/sudoku/results/solution-copt-machine.sol +731 -0
- pyoframe-1.0.1/tests/examples/sudoku/results/solution-copt-pretty.sol +731 -0
- pyoframe-1.0.0a0/tests/test_arithmetic.py → pyoframe-1.0.1/tests/test_addition.py +124 -239
- pyoframe-1.0.1/tests/test_arithmetic.py +164 -0
- pyoframe-1.0.1/tests/test_constraint.py +38 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_examples.py +17 -5
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_io.py +7 -4
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_model.py +7 -9
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_names.py +1 -1
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/test_objective.py +0 -4
- pyoframe-1.0.1/tests/test_variable.py +68 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/util.py +1 -0
- pyoframe-1.0.0a0/docs/learn/concepts/solver-access.md +0 -43
- pyoframe-1.0.0a0/docs/learn/concepts/special-functions.md +0 -227
- pyoframe-1.0.0a0/docs/reference/index.md +0 -7
- pyoframe-1.0.0a0/scripts/generate_api_reference.py +0 -34
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.gitattributes +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/CODEOWNERS +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/actions/setup_optimizers_macos/action.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/actions/setup_optimizers_windows/action.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/format.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/lines_changed_counter.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/lint.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/publish_doc.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/publish_doc_dev.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/publish_to_pypi.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.github/workflows/test_doc.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.vscode/launch.json +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/.vscode/settings.json +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/CHANGELOG.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/LICENSE +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/conftest.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/.nav.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/.nav.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/facility_location.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/index.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/portfolio_optimization.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/examples/production.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/javascripts/feedback.js +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/.nav.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/datastructure.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/internals.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/quadratics.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/advanced-concepts/troubleshooting.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/concepts/building-blocks.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/.nav.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/example.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/food_data.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/foods.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/foods_to_nutrients.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/nutrients.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/basic-example/results.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/learn/get-started/three-bus.png +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/partials/actions.html +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/partials/comments.html +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
- {pyoframe-1.0.0a0/docs/reference → pyoframe-1.0.1/docs/reference/external}/pandas.DataFrame.to_expr.md +0 -0
- {pyoframe-1.0.0a0/docs/reference → pyoframe-1.0.1/docs/reference/external}/polars.DataFrame.to_expr.md +0 -0
- {pyoframe-1.0.0a0/docs/reference → pyoframe-1.0.1/docs/reference/public}/.nav.yml +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/stylesheets/extra.css +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/data_py.parquet +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/gen_py.parquet +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/docs/why-pyoframe/three-bus-four-gen.png +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/giscus.json +0 -0
- {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/dependency_links.txt +0 -0
- {pyoframe-1.0.0a0/src → pyoframe-1.0.1}/pyoframe.egg-info/top_level.txt +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_assign_ids_constraints.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_assign_ids_constraints_2.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_assign_ids_variables.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/scripts/archive/benchmark_attr_performance.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/setup.cfg +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/src/pyoframe/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/conftest.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/input_data/orders.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/input_data/parameters.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/model.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/objective.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-highs-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/cutting_stock_problem/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/README.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/input_data/foods.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/input_data/foods_to_nutrients.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/input_data/nutrients.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/model_gurobipy.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/Buy.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/Buy_ub.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/max_nutrients.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/min_nutrients.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/objective.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-highs-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-highs-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/diet_problem/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/results/objective.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_location/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/input_data/plants.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/input_data/transport_costs.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/input_data/wharehouses.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/model.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/model_gurobipy.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/objective.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/open.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-highs-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-highs-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/facility_problem/results/transport.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/input_data/assets.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/input_data/covariance.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/input_data/portfolio_params.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/con_min_return.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/con_weights_sum.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/objective.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/portfolio_optim/results/weight.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/input_data/machines_availability.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/input_data/processing_times.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/input_data/products_profit.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/model.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/objective.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-highs-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-highs-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/production_planning/results/solution.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/README.md +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/input_data/elspot-prices_2021_hourly_eur.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/model.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/Pump.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/Storage_level.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/Turb.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/objective.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-highs-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-highs-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/pumped_storage/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/__init__.py +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/input_data/initial_numbers.csv +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-gurobi-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-gurobi-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-highs-machine.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/problem-highs-pretty.lp +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-gurobi-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-gurobi-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-highs-machine.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution-highs-pretty.sol +0 -0
- {pyoframe-1.0.0a0 → pyoframe-1.0.1}/tests/examples/sudoku/results/solution.csv +0 -0
- {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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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:
|
|
84
|
-
|
|
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
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyoframe
|
|
3
|
-
Version: 1.0.
|
|
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
|
|
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:
|
|
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
|
[](https://codecov.io/gh/Bravos-Power/pyoframe)
|
|
59
58
|
[](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml)
|
|
60
|
-
[](https://Bravos-Power.github.io/pyoframe/reference/)
|
|
59
|
+
[](https://Bravos-Power.github.io/pyoframe/latest/reference/)
|
|
61
60
|
[](https://opensource.org/licenses/MIT)
|
|
62
61
|
[](https://github.com/Bravos-Power/pyoframe/issues?q=is%3Aopen+is%3Aissue+no%3Alabel)
|
|
63
62
|
[](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
|
[](https://codecov.io/gh/Bravos-Power/pyoframe)
|
|
4
4
|
[](https://github.com/Bravos-Power/pyoframe/actions/workflows/ci.yml)
|
|
5
|
-
[](https://Bravos-Power.github.io/pyoframe/reference/)
|
|
5
|
+
[](https://Bravos-Power.github.io/pyoframe/latest/reference/)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://github.com/Bravos-Power/pyoframe/issues?q=is%3Aopen+is%3Aissue+no%3Alabel)
|
|
8
8
|
[](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
|
-
|
|
45
|
+
For core developers:
|
|
46
46
|
|
|
47
|
-
|
|
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
|
-
|
|
49
|
+
For repository maintainers:
|
|
50
50
|
|
|
51
|
-
|
|
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.
|
|
31
|
+
nutrients[["category", "min"]] <= nutrient_intake.drop_extras() # (1)!
|
|
32
32
|
)
|
|
33
|
-
m.max_nutrients = nutrient_intake.
|
|
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. `.
|
|
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
|
|
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
|
|
9
|
+
## Use integer labels
|
|
10
10
|
|
|
11
|
-
Pyoframe
|
|
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
|
|
21
|
+
## Disable checks for extra values
|
|
22
22
|
|
|
23
|
-
Disabling
|
|
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
|
|
25
|
+
The following code disables checks for extra values:
|
|
26
26
|
|
|
27
27
|
```python
|
|
28
|
-
pf.Config.
|
|
28
|
+
pf.Config.disable_extras_checks = True
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
<!-- TODO REVISIT
|
|
@@ -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].)
|