gpkit-core 0.3.2__tar.gz → 0.3.4__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.
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/PKG-INFO +1 -1
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/__init__.py +1 -1
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/model.py +40 -2
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/core.py +6 -1
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/report.py +214 -40
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_report.py +30 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_report_descriptions.py +18 -8
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.flake8 +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.github/workflows/lint.yml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.github/workflows/publish.yml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.github/workflows/pylint.yml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.github/workflows/tests.yml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.gitignore +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.importlinter +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.pre-commit-config.yaml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.pylintrc +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/.pylintrc.examples +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/CONTRIBUTING.md +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/LICENSE +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/Makefile +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/README.md +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/catalog.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/Makefile +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/apidoc.sh +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/make.bat +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/refresh_build.sh +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/_static/css/custom.css +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/acknowledgements.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/advancedcommands.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/autodoc/gpkit.constraints.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/autodoc/gpkit.interactive.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/autodoc/gpkit.nomials.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/autodoc/gpkit.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/autodoc/gpkit.solvers.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/autodoc/gpkit.tools.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/autodoc/modules.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/citinggpkit.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/conf.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/debugging.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/autosweep_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/beam.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/beam_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/bemt_hover_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/boundschecking_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/breakdowns/solartest.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/breakdowns_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/checking_result_changes_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/debug_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/evaluated_fixed_variables_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/evaluated_free_variables_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/external_constraint_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/external_function_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/external_sp_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/freeing_fixed_variables_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/fuel_burn_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/gettingstarted_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/gp_textbook_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/issue_1513_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/issue_1522_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/last_verified.pkl +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/last_verified.sol +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/loose_constraintsets_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/migp_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/model_var_access_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/performance_modeling_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/plot_autosweep1d.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/plot_sweep1d.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/plot_sweep1d_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/primal_infeasible_ex1_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/primal_infeasible_ex2_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/relaxation_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/simple_box_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/simple_sp_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/simpleflight_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/sin_approx_example_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/solar.p +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/solar_10.p +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/solar_12.p +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/solar_13.p +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/sp_to_gp_sweep_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/sub_multi_values_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/substitutions_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/tight_constraintsets_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/toml/beam.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/toml/modular_aircraft.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/toml/performance_modeling.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/toml/simple_box.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/toml/simpleflight.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/toml/water_tank.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/toml/wing_aircraft.toml +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/treemap_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/unbounded_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/vectorization_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/vectorize_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/water_tank_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples/x_greaterthan_1_output.txt +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/examples.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/Mission.gif +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/performance_modeling.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/referencesplot.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/Mission.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/Mission.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/Model.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/Model.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_Aircraft.Wing.Planform.b.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_Aircraft.Wing.Planform.b.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_CFRPFabric.tmin.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_CFRPFabric.tmin.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_Nprop.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_Nprop.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_Wtotal.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sankey_autosaves/SolarMission_Wtotal.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/sizedconstrainttreemap.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/solartest.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/figures/treemap.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/gettingstarted.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/gp101.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/gplogo.png +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/gplogo.svg +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/index.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/installation.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/modelbuilding.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/modeling_conventions.md +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/releasenotes.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/signomialprogramming.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/docs/source/visint.rst +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/ast_nodes.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/breakdowns.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/budgets.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/bounded.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/costed.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/loose.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/relax.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/set.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/sigeq.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/constraints/tight.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/autosweep.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/beam.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/bemt_hover.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/boundschecking.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/breakdowns.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/checking_result_changes.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/debug.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/evaluated_fixed_variables.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/evaluated_free_variables.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/external_constraint.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/external_function.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/external_sp.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/freeing_fixed_variables.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/fuel_burn.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/gettingstarted.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/gp_textbook.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/issue_1513.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/issue_1522.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/loose_constraintsets.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/migp.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/model_var_access.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/performance_modeling.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/plot_sweep1d.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/primal_infeasible_ex1.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/primal_infeasible_ex2.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/relaxation.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/simple_box.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/simple_sp.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/simpleflight.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/sin_approx_example.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/sp_to_gp_sweep.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/sub_multi_values.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/substitutions.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/tight_constraintsets.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/treemap.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/unbounded.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/vectorization.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/vectorize.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/water_tank.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/examples/x_greaterthan_1.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/exceptions.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/interactive/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/interactive/plot_sweep.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/interactive/plotting.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/interactive/references.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/interactive/referencesplot.html +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/interactive/sankey.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/interactive/widgets.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/array.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/constraints.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/data.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/map.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/math.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/substitution.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/nomials/variables.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/printing.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/programs/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/programs/gp.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/programs/prog_factories.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/programs/sgp.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/solutions.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/solvers/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/solvers/cvxopt.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/solvers/mosek_cli.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/solvers/mosek_conif.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/conftest.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_budgets.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_catalog.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_constraints.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_examples.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_ir.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_model.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_model_graph.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_nomial_array.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_nomials.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_solution.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_sub.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_toml_expr.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_toml_parser.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_toml_printer.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_toml_subs.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_tools.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_util.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_varmap.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_vars.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tests/test_varset.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/toml/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/toml/_expr.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/toml/_parser.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/toml/_printer.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/toml/_subs.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tools/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tools/autosweep.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/tools/tools.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/units.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/util/__init__.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/util/build.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/util/globals.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/util/repr_conventions.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/util/small_classes.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/util/small_scripts.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/var.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/varkey.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/gpkit/varmap.py +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/linecount.sh +0 -0
- {gpkit_core-0.3.2 → gpkit_core-0.3.4}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gpkit-core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Package for defining and manipulating geometric programming models.
|
|
5
5
|
Project-URL: Homepage, https://www.github.com/beautifulmachines/gpkit-core
|
|
6
6
|
Author-email: Warren Hoburg <whoburg@alum.mit.edu>
|
|
@@ -343,7 +343,30 @@ class Model(CostedConstraintSet): # pylint: disable=too-many-instance-attribute
|
|
|
343
343
|
ir_doc = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
344
344
|
return cls.from_ir(ir_doc)
|
|
345
345
|
|
|
346
|
-
|
|
346
|
+
@classmethod
|
|
347
|
+
def report_preamble(cls) -> str:
|
|
348
|
+
"""Return optional markdown prose prepended before this model's report section.
|
|
349
|
+
|
|
350
|
+
Override in Model subclasses to attach section-specific context to a
|
|
351
|
+
specific part of a hierarchical report. The returned string is placed
|
|
352
|
+
before the section heading (and before the model's description/variables
|
|
353
|
+
tables).
|
|
354
|
+
|
|
355
|
+
Example::
|
|
356
|
+
|
|
357
|
+
class Wing(Model):
|
|
358
|
+
@classmethod
|
|
359
|
+
def report_preamble(cls):
|
|
360
|
+
return "This section covers aerodynamic and structural wing sizing."
|
|
361
|
+
|
|
362
|
+
For preambles that depend on model-specific counts (free variables,
|
|
363
|
+
constraints, objective), use :func:`gpkit.report.feasibility_block`
|
|
364
|
+
and :func:`gpkit.report.sensitivities_block` instead, and pass the
|
|
365
|
+
result via the *front_matter* argument of :meth:`report`.
|
|
366
|
+
"""
|
|
367
|
+
return ""
|
|
368
|
+
|
|
369
|
+
def report(self, solution=None, fmt="text", front_matter="", toc=False):
|
|
347
370
|
"""Build a hierarchical report for this model.
|
|
348
371
|
|
|
349
372
|
Parameters
|
|
@@ -352,11 +375,26 @@ class Model(CostedConstraintSet): # pylint: disable=too-many-instance-attribute
|
|
|
352
375
|
If provided, variable tables include solved values and sensitivities.
|
|
353
376
|
fmt : str
|
|
354
377
|
Output format: "dict", "text", "md", or "latex".
|
|
378
|
+
front_matter : str, optional
|
|
379
|
+
Raw text/markdown prepended before the entire report (before the
|
|
380
|
+
root model's heading). Combine with
|
|
381
|
+
:func:`gpkit.report.feasibility_block` and
|
|
382
|
+
:func:`gpkit.report.sensitivities_block` to add standard GP
|
|
383
|
+
explanatory text::
|
|
384
|
+
|
|
385
|
+
from gpkit.report import feasibility_block, sensitivities_block
|
|
386
|
+
m.report(sol, fmt="md",
|
|
387
|
+
front_matter=feasibility_block(m) + "\\n\\n"
|
|
388
|
+
+ sensitivities_block())
|
|
389
|
+
toc : bool, optional
|
|
390
|
+
If True, a table-of-contents marker is inserted (Markdown only).
|
|
355
391
|
"""
|
|
356
392
|
# pylint: disable=import-outside-toplevel
|
|
357
393
|
from .report import build_report_ir, render_report
|
|
358
394
|
|
|
359
|
-
ir = build_report_ir(
|
|
395
|
+
ir = build_report_ir(
|
|
396
|
+
self, solution=solution, front_matter=front_matter, toc=toc
|
|
397
|
+
)
|
|
360
398
|
return render_report(ir, fmt=fmt)
|
|
361
399
|
|
|
362
400
|
gp = progify(GeometricProgram)
|
|
@@ -26,7 +26,12 @@ def nomial_latex_helper(c, pos_vars, neg_vars):
|
|
|
26
26
|
cstr = f"{cstr[:idx]} \\times 10^{{int(cstr[idx + 1:])}}"
|
|
27
27
|
|
|
28
28
|
if pos_vars and neg_vars:
|
|
29
|
-
|
|
29
|
+
if cstr in (
|
|
30
|
+
"",
|
|
31
|
+
"-",
|
|
32
|
+
): # coefficient is ±1; sign (or nothing) before the fraction
|
|
33
|
+
return f"{cstr}\\frac{{{pvarstr}}}{{{nvarstr}}}"
|
|
34
|
+
return f"\\frac{{{cstr} {pvarstr}}}{{{nvarstr}}}" # numeric coeff in numerator
|
|
30
35
|
if neg_vars and not pos_vars:
|
|
31
36
|
return f"\\frac{{{cstr}}}{{{nvarstr}}}"
|
|
32
37
|
if pos_vars:
|
|
@@ -10,6 +10,7 @@ from typing import Any, List, Optional, Tuple
|
|
|
10
10
|
|
|
11
11
|
from .constraints.tight import Tight
|
|
12
12
|
from .model import Model as _Model
|
|
13
|
+
from .nomials import Variable
|
|
13
14
|
from .printing import _format_aligned_columns
|
|
14
15
|
from .util.repr_conventions import unitstr
|
|
15
16
|
from .util.small_classes import Quantity
|
|
@@ -79,10 +80,12 @@ class ReportSection: # pylint: disable=too-many-instance-attributes
|
|
|
79
80
|
)
|
|
80
81
|
objective_str: str = "" # text representation of cost expression; "" if constant
|
|
81
82
|
objective_latex: str = "" # LaTeX representation of cost expression
|
|
83
|
+
objective_label: str = "" # variable label when expr is a single variable; else ""
|
|
82
84
|
objective_value: Optional[float] = (
|
|
83
85
|
None # magnitude of attained cost; None if unsolved
|
|
84
86
|
)
|
|
85
87
|
objective_units: str = "" # unit string for the cost expression
|
|
88
|
+
objective_direction: str = "minimize" # "minimize" or "maximize"
|
|
86
89
|
|
|
87
90
|
def to_dict(self) -> dict:
|
|
88
91
|
"""JSON-serializable dict (for format='dict' and future API)."""
|
|
@@ -90,6 +93,8 @@ class ReportSection: # pylint: disable=too-many-instance-attributes
|
|
|
90
93
|
"title": self.title,
|
|
91
94
|
"lineage_path": self.lineage_path,
|
|
92
95
|
"magic_prefix": self.magic_prefix,
|
|
96
|
+
"objective_direction": self.objective_direction,
|
|
97
|
+
"objective_label": self.objective_label,
|
|
93
98
|
"is_anonymous": self.is_anonymous,
|
|
94
99
|
"description": self.description,
|
|
95
100
|
"assumptions": list(self.assumptions),
|
|
@@ -324,27 +329,201 @@ def _build_constraint_groups(model) -> List[CGroup]:
|
|
|
324
329
|
return [CGroup(label="", constraints=own)] if own else []
|
|
325
330
|
|
|
326
331
|
|
|
332
|
+
def _reciprocal_if_1_over_x(cost):
|
|
333
|
+
"""Return (True, 1/cost) if cost is a single monomial 1/expr, else (False, cost).
|
|
334
|
+
|
|
335
|
+
Detects the pattern coeff=1, all-negative exponents — the 1/x form that
|
|
336
|
+
GPs use to express maximization. Mirrors the TOML printer's _is_reciprocal
|
|
337
|
+
check but operates on the live cost object rather than the IR AST dict.
|
|
338
|
+
"""
|
|
339
|
+
hmap = cost.hmap
|
|
340
|
+
if len(hmap) != 1:
|
|
341
|
+
return False, cost
|
|
342
|
+
(exp,) = hmap.keys()
|
|
343
|
+
coeff = hmap[exp]
|
|
344
|
+
exp_dict = dict(exp)
|
|
345
|
+
if abs(coeff - 1.0) < 1e-10 and exp_dict and all(v < 0 for v in exp_dict.values()):
|
|
346
|
+
# Build the inner expression from VarKeys rather than computing 1/cost.
|
|
347
|
+
# 1/cost encodes div(1, cost.ast) in its AST, causing str/latex to
|
|
348
|
+
# render as 1/(1/x) instead of x.
|
|
349
|
+
inner = None
|
|
350
|
+
for vk, e in exp_dict.items():
|
|
351
|
+
term = Variable(vk) if e == -1 else Variable(vk) ** (-e)
|
|
352
|
+
inner = term if inner is None else inner * term
|
|
353
|
+
return True, inner
|
|
354
|
+
return False, cost
|
|
355
|
+
|
|
356
|
+
|
|
327
357
|
def _build_objective(model, solution) -> dict:
|
|
328
358
|
"""Return objective keyword args for ReportSection for the model's cost.
|
|
329
359
|
|
|
330
360
|
All fields are empty/None when the cost has no variables (i.e. it is a
|
|
331
361
|
trivial constant placeholder rather than a real optimization objective).
|
|
362
|
+
Detects the 1/expr pattern and flips direction to "maximize".
|
|
332
363
|
"""
|
|
333
364
|
if not model.cost.vks:
|
|
334
365
|
return {
|
|
335
366
|
"objective_str": "",
|
|
336
367
|
"objective_latex": "",
|
|
368
|
+
"objective_label": "",
|
|
337
369
|
"objective_value": None,
|
|
338
370
|
"objective_units": "",
|
|
371
|
+
"objective_direction": "minimize",
|
|
339
372
|
}
|
|
373
|
+
is_recip, expr = _reciprocal_if_1_over_x(model.cost)
|
|
374
|
+
cost_value = (
|
|
375
|
+
(1.0 / float(solution.cost) if is_recip else float(solution.cost))
|
|
376
|
+
if solution is not None
|
|
377
|
+
else None
|
|
378
|
+
)
|
|
379
|
+
excluded = {"units", "lineage"}
|
|
380
|
+
vks = list(expr.vks)
|
|
381
|
+
label = vks[0].label if len(vks) == 1 else ""
|
|
382
|
+
return {
|
|
383
|
+
"objective_str": expr.str_without(excluded),
|
|
384
|
+
"objective_latex": expr.latex(excluded),
|
|
385
|
+
"objective_label": label or "",
|
|
386
|
+
"objective_value": cost_value,
|
|
387
|
+
"objective_units": unitstr(expr),
|
|
388
|
+
"objective_direction": "maximize" if is_recip else "minimize",
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# ── Standard text blocks ──────────────────────────────────────────────────────
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def _model_stats(model) -> dict:
|
|
396
|
+
"""Compute model-wide counts for use in report text blocks.
|
|
397
|
+
|
|
398
|
+
Returns a dict with keys: n_free, n_constraints, objective_str,
|
|
399
|
+
objective_latex. All values are derived from the model without requiring
|
|
400
|
+
a solved solution.
|
|
401
|
+
"""
|
|
402
|
+
n_free = len(model.vks) - len(model.substitutions)
|
|
403
|
+
# flat() returns a generator (flatiter), so use sum() rather than len().
|
|
404
|
+
n_constraints = sum(1 for _ in model.flat())
|
|
405
|
+
if model.cost.vks:
|
|
406
|
+
is_recip, expr = _reciprocal_if_1_over_x(model.cost)
|
|
407
|
+
excluded = {"units", "lineage"}
|
|
408
|
+
vks = list(expr.vks)
|
|
409
|
+
obj_latex = expr.latex(excluded)
|
|
410
|
+
obj_label = vks[0].label if len(vks) == 1 else ""
|
|
411
|
+
obj_direction = "maximize" if is_recip else "minimize"
|
|
412
|
+
else:
|
|
413
|
+
obj_latex = None
|
|
414
|
+
obj_label = ""
|
|
415
|
+
obj_direction = "minimize"
|
|
340
416
|
return {
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
"
|
|
344
|
-
"
|
|
417
|
+
"n_free": n_free,
|
|
418
|
+
"n_constraints": n_constraints,
|
|
419
|
+
"objective_latex": obj_latex,
|
|
420
|
+
"objective_label": obj_label or "",
|
|
421
|
+
"objective_direction": obj_direction,
|
|
345
422
|
}
|
|
346
423
|
|
|
347
424
|
|
|
425
|
+
def feasibility_block(model) -> str:
|
|
426
|
+
"""Return a markdown explanation of feasibility and optimality for *model*.
|
|
427
|
+
|
|
428
|
+
Fills in the number of free variables, constraints, and current objective
|
|
429
|
+
expression. Suitable for use as ``front_matter`` or a ``report_preamble``
|
|
430
|
+
in a custom report.
|
|
431
|
+
|
|
432
|
+
Example usage::
|
|
433
|
+
|
|
434
|
+
from gpkit.report import feasibility_block, sensitivities_block
|
|
435
|
+
|
|
436
|
+
class Aircraft(Model):
|
|
437
|
+
...
|
|
438
|
+
|
|
439
|
+
m = Aircraft()
|
|
440
|
+
sol = m.solve()
|
|
441
|
+
print(m.report(sol, fmt="md",
|
|
442
|
+
front_matter=feasibility_block(m) + "\\n\\n"
|
|
443
|
+
+ sensitivities_block()))
|
|
444
|
+
"""
|
|
445
|
+
ctx = _model_stats(model)
|
|
446
|
+
if ctx["objective_latex"]:
|
|
447
|
+
direction = ctx["objective_direction"]
|
|
448
|
+
label_clause = f", {ctx['objective_label']}" if ctx["objective_label"] else ""
|
|
449
|
+
obj_clause = (
|
|
450
|
+
f" The objective is currently set to {direction}"
|
|
451
|
+
f" ${ctx['objective_latex']}${label_clause}."
|
|
452
|
+
)
|
|
453
|
+
else:
|
|
454
|
+
obj_clause = ""
|
|
455
|
+
return (
|
|
456
|
+
f"## Feasibility and Optimality\n\n"
|
|
457
|
+
f"The model currently has {ctx['n_free']} free variables and "
|
|
458
|
+
f"{ctx['n_constraints']} constraints. A design satisfying all "
|
|
459
|
+
f"constraints is *feasible*; the set of all feasible designs is the "
|
|
460
|
+
f"*feasible set*."
|
|
461
|
+
f"{obj_clause} "
|
|
462
|
+
f"The solver finds a globally optimal solution — the unique feasible "
|
|
463
|
+
f"design that cannot be improved further — with a reliable, efficient "
|
|
464
|
+
f"algorithm. This guarantee comes from the convex structure of the "
|
|
465
|
+
f"problem, not from luck or tuning."
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
SENSITIVITIES_BLOCK = (
|
|
470
|
+
"## Sensitivities\n\n"
|
|
471
|
+
"Each fixed constant in the model has a *sensitivity* — a number that "
|
|
472
|
+
"tells you how much the objective would change if that parameter changed. "
|
|
473
|
+
"Specifically, if a constant $c$ has sensitivity $s$, then increasing $c$ "
|
|
474
|
+
"by 1% would worsen the objective by approximately $s$% (holding all other "
|
|
475
|
+
"constants fixed and re-solving). A sensitivity of 1.5 means a 1% increase "
|
|
476
|
+
"in that constant would worsen the objective by 1.5%; a sensitivity of −0.8 "
|
|
477
|
+
"means a 1% increase would improve the objective by 0.8%. "
|
|
478
|
+
"Sensitivities with large magnitude flag the parameters that matter most; "
|
|
479
|
+
"near-zero sensitivities indicate parameters the design is insensitive to. "
|
|
480
|
+
"These numbers come for free alongside every solve — no extra computation "
|
|
481
|
+
"required."
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def sensitivities_block() -> str:
|
|
486
|
+
"""Return a markdown explanation of GP dual solution / sensitivity information.
|
|
487
|
+
|
|
488
|
+
This text is model-independent and can be included in any GP report.
|
|
489
|
+
"""
|
|
490
|
+
return SENSITIVITIES_BLOCK
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def objective_block(model, solution=None) -> str:
|
|
494
|
+
"""Return a markdown summary of the model's objective for use in a report.
|
|
495
|
+
|
|
496
|
+
Shows the objective direction (minimize/maximize), the expression without
|
|
497
|
+
lineage, the variable label when the expression is a single variable, and
|
|
498
|
+
the attained value when *solution* is provided.
|
|
499
|
+
|
|
500
|
+
Like :func:`feasibility_block` and :func:`sensitivities_block`, this
|
|
501
|
+
returns a plain markdown string so it can be placed wherever the author
|
|
502
|
+
wants — front matter, a subsection preamble, or anywhere else.
|
|
503
|
+
|
|
504
|
+
Example::
|
|
505
|
+
|
|
506
|
+
from gpkit.report import objective_block
|
|
507
|
+
m.report(sol, fmt="md", front_matter=objective_block(m, sol))
|
|
508
|
+
"""
|
|
509
|
+
if not model.cost.vks:
|
|
510
|
+
return ""
|
|
511
|
+
is_recip, expr = _reciprocal_if_1_over_x(model.cost)
|
|
512
|
+
excluded = {"units", "lineage"}
|
|
513
|
+
direction = "maximize" if is_recip else "minimize"
|
|
514
|
+
latex = expr.latex(excluded)
|
|
515
|
+
vks = list(expr.vks)
|
|
516
|
+
label_clause = f", {vks[0].label}" if len(vks) == 1 and vks[0].label else ""
|
|
517
|
+
lines = [f"**Objective:** {direction} ${latex}${label_clause}"]
|
|
518
|
+
if solution is not None:
|
|
519
|
+
cost_value = 1.0 / float(solution.cost) if is_recip else float(solution.cost)
|
|
520
|
+
val_str = _fmt_value(cost_value)
|
|
521
|
+
attained = f"{val_str} {unitstr(expr)}".rstrip()
|
|
522
|
+
lines.append("")
|
|
523
|
+
lines.append(f"**Attained:** {attained}")
|
|
524
|
+
return "\n".join(lines)
|
|
525
|
+
|
|
526
|
+
|
|
348
527
|
# ── Core builder ─────────────────────────────────────────────────────────────
|
|
349
528
|
|
|
350
529
|
|
|
@@ -364,8 +543,10 @@ def build_report_ir(
|
|
|
364
543
|
solution : Solution, optional
|
|
365
544
|
If provided, variable entries include solved values and sensitivities.
|
|
366
545
|
front_matter : str, optional
|
|
367
|
-
Raw text/markdown prepended before the
|
|
368
|
-
|
|
546
|
+
Raw text/markdown prepended before the root section. For the root
|
|
547
|
+
model, caller-supplied *front_matter* is combined with the model's
|
|
548
|
+
``report_preamble()`` (if any). For child models, only
|
|
549
|
+
``report_preamble()`` is used.
|
|
369
550
|
toc : bool, optional
|
|
370
551
|
If True, a table-of-contents marker is inserted by renderers that have
|
|
371
552
|
a native TOC facility (e.g. ``[TOC]`` in Markdown). Set only on the
|
|
@@ -386,8 +567,16 @@ def build_report_ir(
|
|
|
386
567
|
)
|
|
387
568
|
if is_anon:
|
|
388
569
|
desc = {"summary": "", "assumptions": [], "references": []}
|
|
570
|
+
section_fm = front_matter
|
|
389
571
|
else:
|
|
390
572
|
desc = type(model).description()
|
|
573
|
+
preamble = type(model).report_preamble()
|
|
574
|
+
if preamble and front_matter:
|
|
575
|
+
section_fm = front_matter + "\n\n" + preamble
|
|
576
|
+
elif preamble:
|
|
577
|
+
section_fm = preamble
|
|
578
|
+
else:
|
|
579
|
+
section_fm = front_matter
|
|
391
580
|
return ReportSection(
|
|
392
581
|
title=own_name or "Model",
|
|
393
582
|
description=desc["summary"],
|
|
@@ -400,7 +589,7 @@ def build_report_ir(
|
|
|
400
589
|
fixed_variables=fixed_vars,
|
|
401
590
|
constraint_groups=cgroups,
|
|
402
591
|
lineage_map=lineage_map,
|
|
403
|
-
front_matter=
|
|
592
|
+
front_matter=section_fm,
|
|
404
593
|
toc=toc,
|
|
405
594
|
**_build_objective(model, solution),
|
|
406
595
|
children=[
|
|
@@ -570,13 +759,6 @@ def _text_prose_lines(ir: "ReportSection", pad: str) -> list:
|
|
|
570
759
|
for item in ir.references:
|
|
571
760
|
lines.append(f"{pad} - {item}")
|
|
572
761
|
lines.append("")
|
|
573
|
-
if ir.objective_str:
|
|
574
|
-
lines.append(f"{pad} Objective")
|
|
575
|
-
lines.append(f"{pad} minimize: {ir.objective_str}")
|
|
576
|
-
if ir.objective_value is not None:
|
|
577
|
-
val_str = _fmt_value(ir.objective_value)
|
|
578
|
-
lines.append(f"{pad} attained: {val_str} {ir.objective_units}".rstrip())
|
|
579
|
-
lines.append("")
|
|
580
762
|
return lines
|
|
581
763
|
|
|
582
764
|
|
|
@@ -597,7 +779,6 @@ def render_text(ir: "ReportSection", indent: int = 0) -> str:
|
|
|
597
779
|
pad = _INDENT * indent
|
|
598
780
|
lines: list = []
|
|
599
781
|
|
|
600
|
-
# Front matter (root only)
|
|
601
782
|
if ir.front_matter:
|
|
602
783
|
lines.append(ir.front_matter)
|
|
603
784
|
lines.append("")
|
|
@@ -612,6 +793,9 @@ def render_text(ir: "ReportSection", indent: int = 0) -> str:
|
|
|
612
793
|
|
|
613
794
|
lines.extend(_text_prose_lines(ir, pad))
|
|
614
795
|
|
|
796
|
+
# Constraint groups
|
|
797
|
+
lines.extend(_text_cgroup_lines(ir.constraint_groups, pad, ir.lineage_map))
|
|
798
|
+
|
|
615
799
|
# Optimized Variables table (primal — no sensitivity column)
|
|
616
800
|
if ir.free_variables:
|
|
617
801
|
lines.append(f"{pad} Optimized Variables")
|
|
@@ -626,9 +810,6 @@ def render_text(ir: "ReportSection", indent: int = 0) -> str:
|
|
|
626
810
|
lines.append(f"{pad} {row_line}")
|
|
627
811
|
lines.append("")
|
|
628
812
|
|
|
629
|
-
# Constraint groups
|
|
630
|
-
lines.extend(_text_cgroup_lines(ir.constraint_groups, pad, ir.lineage_map))
|
|
631
|
-
|
|
632
813
|
# Children (recursive)
|
|
633
814
|
for child in ir.children:
|
|
634
815
|
lines.append(render_text(child, indent=child_indent))
|
|
@@ -696,13 +877,6 @@ def _md_prose_lines(ir: "ReportSection") -> list:
|
|
|
696
877
|
if ir.references:
|
|
697
878
|
lines.append(f"**References:** {'; '.join(ir.references)}")
|
|
698
879
|
lines.append("")
|
|
699
|
-
if ir.objective_str:
|
|
700
|
-
lines.append(f"**Objective:** minimize $${ir.objective_latex}$$")
|
|
701
|
-
if ir.objective_value is not None:
|
|
702
|
-
val_str = _fmt_value(ir.objective_value)
|
|
703
|
-
attained = f"{val_str} {ir.objective_units}".rstrip()
|
|
704
|
-
lines.append(f"**Attained:** {attained}")
|
|
705
|
-
lines.append("")
|
|
706
880
|
return lines
|
|
707
881
|
|
|
708
882
|
|
|
@@ -723,7 +897,7 @@ def render_markdown(ir: "ReportSection", level: int = 1) -> str:
|
|
|
723
897
|
hdr = "#" * min(level, 6)
|
|
724
898
|
lines: list = []
|
|
725
899
|
|
|
726
|
-
# Front matter and TOC marker (
|
|
900
|
+
# Front matter and TOC marker (before first heading)
|
|
727
901
|
if ir.front_matter:
|
|
728
902
|
lines.append(ir.front_matter)
|
|
729
903
|
lines.append("")
|
|
@@ -742,20 +916,6 @@ def render_markdown(ir: "ReportSection", level: int = 1) -> str:
|
|
|
742
916
|
|
|
743
917
|
lines.extend(_md_prose_lines(ir))
|
|
744
918
|
|
|
745
|
-
# Optimized Variables pipe table (no sensitivity column)
|
|
746
|
-
if ir.free_variables:
|
|
747
|
-
lines.append("**Optimized Variables**")
|
|
748
|
-
lines.append("")
|
|
749
|
-
lines.extend(_md_var_table(ir.free_variables))
|
|
750
|
-
lines.append("")
|
|
751
|
-
|
|
752
|
-
# Fixed Variables pipe table (value | units | sensitivity | label)
|
|
753
|
-
if ir.fixed_variables:
|
|
754
|
-
lines.append("**Fixed Variables**")
|
|
755
|
-
lines.append("")
|
|
756
|
-
lines.extend(_md_var_table(ir.fixed_variables, include_sensitivity=True))
|
|
757
|
-
lines.append("")
|
|
758
|
-
|
|
759
919
|
# Constraint groups
|
|
760
920
|
excluded = ("units", ":MAGIC:" + ir.magic_prefix) if ir.magic_prefix else ("units",)
|
|
761
921
|
for cg in ir.constraint_groups:
|
|
@@ -773,6 +933,20 @@ def render_markdown(ir: "ReportSection", level: int = 1) -> str:
|
|
|
773
933
|
lines.append("\\end{aligned}$$")
|
|
774
934
|
lines.append("")
|
|
775
935
|
|
|
936
|
+
# Optimized Variables pipe table (no sensitivity column)
|
|
937
|
+
if ir.free_variables:
|
|
938
|
+
lines.append("**Optimized Variables**")
|
|
939
|
+
lines.append("")
|
|
940
|
+
lines.extend(_md_var_table(ir.free_variables))
|
|
941
|
+
lines.append("")
|
|
942
|
+
|
|
943
|
+
# Fixed Variables pipe table (value | units | sensitivity | label)
|
|
944
|
+
if ir.fixed_variables:
|
|
945
|
+
lines.append("**Fixed Variables**")
|
|
946
|
+
lines.append("")
|
|
947
|
+
lines.extend(_md_var_table(ir.fixed_variables, include_sensitivity=True))
|
|
948
|
+
lines.append("")
|
|
949
|
+
|
|
776
950
|
# Children (recursive)
|
|
777
951
|
for child in ir.children:
|
|
778
952
|
child_md = render_markdown(child, level=child_level)
|
|
@@ -347,6 +347,36 @@ class TestRenderText:
|
|
|
347
347
|
# Should contain variable names from constraints
|
|
348
348
|
assert "x_ca" in result
|
|
349
349
|
|
|
350
|
+
def test_report_section_order_text(self):
|
|
351
|
+
"""Constraints appear before Optimized/Fixed Variables in text output."""
|
|
352
|
+
|
|
353
|
+
class _OrderM(Model):
|
|
354
|
+
def setup(self):
|
|
355
|
+
x = Variable("x_ord_t")
|
|
356
|
+
x_max = Variable("x_ord_max", 10)
|
|
357
|
+
return [x >= 2, x <= x_max]
|
|
358
|
+
|
|
359
|
+
m = _OrderM()
|
|
360
|
+
sol = m.solve(verbosity=0)
|
|
361
|
+
result = m.report(solution=sol, fmt="text")
|
|
362
|
+
assert result.index("Constraints") < result.index("Optimized Variables")
|
|
363
|
+
assert result.index("Optimized Variables") < result.index("Fixed Variables")
|
|
364
|
+
|
|
365
|
+
def test_report_section_order_markdown(self):
|
|
366
|
+
"""Constraints appear before Optimized/Fixed Variables in markdown output."""
|
|
367
|
+
|
|
368
|
+
class _OrderMd(Model):
|
|
369
|
+
def setup(self):
|
|
370
|
+
x = Variable("x_ord_md")
|
|
371
|
+
x_max = Variable("x_ord_md_max", 10)
|
|
372
|
+
return [x >= 2, x <= x_max]
|
|
373
|
+
|
|
374
|
+
m = _OrderMd()
|
|
375
|
+
sol = m.solve(verbosity=0)
|
|
376
|
+
result = m.report(solution=sol, fmt="md")
|
|
377
|
+
assert result.index("Constraints") < result.index("Optimized Variables")
|
|
378
|
+
assert result.index("Optimized Variables") < result.index("Fixed Variables")
|
|
379
|
+
|
|
350
380
|
def test_report_text_with_solution(self):
|
|
351
381
|
"""model.report(solution=sol, fmt='text') includes variable values."""
|
|
352
382
|
|
|
@@ -12,6 +12,7 @@ from gpkit import Model, Variable
|
|
|
12
12
|
from gpkit.report import (
|
|
13
13
|
ReportSection,
|
|
14
14
|
build_report_ir,
|
|
15
|
+
objective_block,
|
|
15
16
|
render_markdown,
|
|
16
17
|
render_text,
|
|
17
18
|
)
|
|
@@ -388,11 +389,16 @@ class TestObjective:
|
|
|
388
389
|
assert ir.objective_units != ""
|
|
389
390
|
|
|
390
391
|
def test_build_ir_populates_objective_solved(self):
|
|
391
|
-
"""build_report_ir populates objective_value from solution.
|
|
392
|
+
"""build_report_ir populates objective_value from solution.
|
|
393
|
+
|
|
394
|
+
Box uses cost=1/(h*w*d), so the direction flips to "maximize" and
|
|
395
|
+
objective_value is the maximized volume (1/sol.cost), not sol.cost.
|
|
396
|
+
"""
|
|
392
397
|
m, sol = self._solved_box()
|
|
393
398
|
ir = build_report_ir(m, solution=sol)
|
|
394
399
|
assert ir.objective_value is not None
|
|
395
|
-
assert ir.
|
|
400
|
+
assert ir.objective_direction == "maximize"
|
|
401
|
+
assert ir.objective_value == pytest.approx(1.0 / sol.cost)
|
|
396
402
|
|
|
397
403
|
def test_build_ir_objective_empty_for_constant_cost(self):
|
|
398
404
|
"""build_report_ir leaves objective fields empty when cost has no variables."""
|
|
@@ -431,18 +437,22 @@ class TestObjective:
|
|
|
431
437
|
assert ir.children[0].objective_str == "" # child does not
|
|
432
438
|
|
|
433
439
|
def test_render_text_objective(self):
|
|
434
|
-
"""
|
|
440
|
+
"""Objective is no longer auto-rendered; use objective_block() instead."""
|
|
435
441
|
m, sol = self._solved_box()
|
|
436
442
|
out = m.report(solution=sol, fmt="text")
|
|
437
|
-
assert "Objective" in out
|
|
438
|
-
|
|
443
|
+
assert "Objective" not in out # removed from auto-render
|
|
444
|
+
block = objective_block(m, sol)
|
|
445
|
+
assert "maximize" in block.lower()
|
|
446
|
+
assert "Attained" in block
|
|
439
447
|
|
|
440
448
|
def test_render_markdown_objective(self):
|
|
441
|
-
"""
|
|
449
|
+
"""Objective is no longer auto-rendered; use objective_block() instead."""
|
|
442
450
|
m, sol = self._solved_box()
|
|
443
451
|
out = m.report(solution=sol, fmt="md")
|
|
444
|
-
assert "Objective" in out
|
|
445
|
-
|
|
452
|
+
assert "Objective" not in out # removed from auto-render
|
|
453
|
+
block = objective_block(m, sol)
|
|
454
|
+
assert "maximize" in block.lower()
|
|
455
|
+
assert "Attained" in block
|
|
446
456
|
|
|
447
457
|
def test_render_text_no_objective_when_empty(self):
|
|
448
458
|
"""render_text omits objective section when cost has no variables."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|