├── .gitignore ├── .pylintrc ├── CODE_OF_CONDUCT.md ├── LICENSE ├── MANIFEST ├── README.md ├── docs ├── Makefile ├── apidoc.sh ├── make.bat ├── refresh_build.sh └── source │ ├── _static │ └── css │ │ └── custom.css │ ├── acknowledgements.rst │ ├── advancedcommands.rst │ ├── autodoc │ ├── gpkit.constraints.rst │ ├── gpkit.interactive.rst │ ├── gpkit.nomials.rst │ ├── gpkit.rst │ ├── gpkit.solvers.rst │ ├── gpkit.tools.rst │ └── modules.rst │ ├── citinggpkit.rst │ ├── conf.py │ ├── debugging.rst │ ├── examples.rst │ ├── examples │ ├── autosweep.py │ ├── autosweep_output.txt │ ├── beam.py │ ├── beam.svg │ ├── beam_output.txt │ ├── boundschecking.py │ ├── boundschecking_output.txt │ ├── breakdowns.py │ ├── breakdowns │ │ └── solartest.py │ ├── breakdowns_output.txt │ ├── checking_result_changes.py │ ├── checking_result_changes_output.txt │ ├── debug.py │ ├── debug_output.txt │ ├── docstringparsing.py │ ├── docstringparsing_output.txt │ ├── evaluated_fixed_variables.py │ ├── evaluated_fixed_variables_output.txt │ ├── evaluated_free_variables.py │ ├── evaluated_free_variables_output.txt │ ├── external_constraint.py │ ├── external_constraint_output.txt │ ├── external_function.py │ ├── external_function_output.txt │ ├── external_sp.py │ ├── external_sp_output.txt │ ├── freeing_fixed_variables.py │ ├── freeing_fixed_variables_output.txt │ ├── gettingstarted.py │ ├── gettingstarted_output.txt │ ├── issue_1513.py │ ├── issue_1513_output.txt │ ├── issue_1522.py │ ├── issue_1522_output.txt │ ├── last_verified.pkl │ ├── last_verified.sol │ ├── loose_constraintsets.py │ ├── loose_constraintsets_output.txt │ ├── migp.py │ ├── migp_output.txt │ ├── model_var_access.py │ ├── model_var_access_output.txt │ ├── performance_modeling.py │ ├── performance_modeling_output.txt │ ├── plot_autosweep1d.png │ ├── plot_sweep1d.png │ ├── plot_sweep1d.py │ ├── plot_sweep1d_output.txt │ ├── primal_infeasible_ex1.py │ ├── primal_infeasible_ex1_output.txt │ ├── primal_infeasible_ex2.py │ ├── primal_infeasible_ex2_output.txt │ ├── relaxation.py │ ├── relaxation_output.txt │ ├── simple_box.py │ ├── simple_box_output.txt │ ├── simple_sp.py │ ├── simple_sp_output.txt │ ├── simpleflight.py │ ├── simpleflight_output.txt │ ├── sin_approx_example.py │ ├── sin_approx_example_output.txt │ ├── solar.p │ ├── solar_10.p │ ├── solar_12.p │ ├── solar_13.p │ ├── sp_to_gp_sweep.py │ ├── sp_to_gp_sweep_output.txt │ ├── sub_multi_values.py │ ├── sub_multi_values_output.txt │ ├── substitutions.py │ ├── substitutions_output.txt │ ├── tight_constraintsets.py │ ├── tight_constraintsets_output.txt │ ├── treemap.py │ ├── treemap_output.txt │ ├── unbounded.py │ ├── unbounded_output.txt │ ├── vectorization.py │ ├── vectorization_output.txt │ ├── vectorize.py │ ├── vectorize_output.txt │ ├── water_tank.py │ ├── water_tank_output.txt │ ├── x_greaterthan_1.py │ └── x_greaterthan_1_output.txt │ ├── figures │ ├── Mission.gif │ ├── documentation sankeys.ipynb │ ├── performance_modeling.svg │ ├── referencesplot.png │ ├── sankey_autosaves │ │ ├── Mission.png │ │ ├── Mission.svg │ │ ├── Model.png │ │ ├── Model.svg │ │ ├── SolarMission.png │ │ ├── SolarMission.svg │ │ ├── SolarMission_Aircraft.Wing.Planform.b.png │ │ ├── SolarMission_Aircraft.Wing.Planform.b.svg │ │ ├── SolarMission_CFRPFabric.tmin.png │ │ ├── SolarMission_CFRPFabric.tmin.svg │ │ ├── SolarMission_Nprop.png │ │ ├── SolarMission_Nprop.svg │ │ ├── SolarMission_Wtotal.png │ │ └── SolarMission_Wtotal.svg │ ├── sizedconstrainttreemap.png │ ├── solartest.py │ └── treemap.png │ ├── gettingstarted.rst │ ├── gp101.rst │ ├── gplogo.png │ ├── gplogo.svg │ ├── index.rst │ ├── installation.rst │ ├── ipynb │ ├── BEMT.ipynb │ ├── Box │ │ ├── Box.ipynb │ │ ├── Box.rst │ │ ├── Box_files │ │ │ └── Box_34_0.png │ │ ├── box-ractive.svg │ │ ├── box.constraints │ │ ├── box.gpkit │ │ ├── box.html │ │ ├── box.svg │ │ └── boxlogo.svg │ ├── Examples from Geometric Programming.ipynb │ ├── Fuel │ │ ├── Fuel.ipynb │ │ ├── Fuel.rst │ │ ├── Fuel_files │ │ │ ├── Fuel_17_0.png │ │ │ ├── Fuel_17_1.png │ │ │ └── Fuel_17_2.png │ │ ├── fuel-ractive.svg │ │ ├── fuel.gpkit │ │ ├── fuel.html │ │ ├── fuel.svg │ │ └── fuellogo.svg │ ├── Wind.ipynb │ └── maintenance optimization.ipynb │ ├── modelbuilding.rst │ ├── releasenotes.rst │ ├── signomialprogramming.rst │ └── visint.rst ├── fulltests.sh ├── gpkit ├── __init__.py ├── breakdowns.py ├── build.py ├── constraints │ ├── __init__.py │ ├── array.py │ ├── bounded.py │ ├── costed.py │ ├── gp.py │ ├── loose.py │ ├── model.py │ ├── prog_factories.py │ ├── relax.py │ ├── set.py │ ├── sgp.py │ ├── sigeq.py │ ├── single_equation.py │ └── tight.py ├── exceptions.py ├── globals.py ├── interactive │ ├── __init__.py │ ├── plot_sweep.py │ ├── plotting.py │ ├── references.py │ ├── referencesplot.html │ ├── sankey.py │ └── widgets.py ├── keydict.py ├── nomials │ ├── __init__.py │ ├── array.py │ ├── core.py │ ├── data.py │ ├── map.py │ ├── math.py │ ├── substitution.py │ └── variables.py ├── repr_conventions.py ├── small_classes.py ├── small_scripts.py ├── solution_array.py ├── solution_ensemble.py ├── solvers │ ├── __init__.py │ ├── cvxopt.py │ ├── mosek_cli.py │ └── mosek_conif.py ├── tests │ ├── __init__.py │ ├── from_paths.py │ ├── helpers.py │ ├── run_tests.py │ ├── t_constraints.py │ ├── t_examples.py │ ├── t_keydict.py │ ├── t_model.py │ ├── t_nomial_array.py │ ├── t_nomials.py │ ├── t_small.py │ ├── t_solution_array.py │ ├── t_sub.py │ ├── t_tools.py │ ├── t_vars.py │ └── test_repo.py ├── tools │ ├── __init__.py │ ├── autosweep.py │ ├── docstring.py │ └── tools.py ├── units.py └── varkey.py ├── linecount.sh ├── pylint.sh ├── rtd_requirements.txt ├── runtests.sh └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # iPython checkpoint folders 2 | .ipynb_checkpoints/ 3 | 4 | # Pickled solutions 5 | *.p 6 | 7 | # MOSEK CLI folder 8 | gpkit_tmp/ 9 | 10 | # Byte-compiled / optimized / DLL files 11 | __pycache__/ 12 | *.py[cod] 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | env/ 20 | bin/ 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | 46 | # Translations 47 | *.mo 48 | 49 | # Mr Developer 50 | .mr.developer.cfg 51 | .project 52 | .pydevproject 53 | 54 | # Rope 55 | .ropeproject 56 | 57 | # Django stuff: 58 | *.log 59 | *.pot 60 | 61 | # Sphinx documentation 62 | docs/_build/ 63 | 64 | # MATLAB autosave 65 | *.asv 66 | 67 | # CI test xmloutput 68 | test_reports/ 69 | test_reports_nounits/ 70 | 71 | # MacOSX 72 | *.DS_Store 73 | 74 | # vim 75 | *.swp 76 | 77 | # OSX 78 | .DS_Store 79 | 80 | # Development environment 81 | .idea/ 82 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at gpkit@mit.edu. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Edward Burnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | gpkit/constraints/__init__.py 2 | gpkit/constraints/array.py 3 | gpkit/constraints/bounded.py 4 | gpkit/constraints/costed.py 5 | gpkit/constraints/gp.py 6 | gpkit/constraints/loose.py 7 | gpkit/constraints/model.py 8 | gpkit/constraints/prog_factories.py 9 | gpkit/constraints/relax.py 10 | gpkit/constraints/set.py 11 | gpkit/constraints/sgp.py 12 | gpkit/constraints/sigeq.py 13 | gpkit/constraints/single_equation.py 14 | gpkit/constraints/tight.py 15 | gpkit/interactive/__init__.py 16 | gpkit/interactive/plot_sweep.py 17 | gpkit/interactive/plotting.py 18 | gpkit/interactive/references.py 19 | gpkit/interactive/referencesplot.html 20 | gpkit/interactive/sankey.py 21 | gpkit/interactive/widgets.py 22 | gpkit/nomials/__init__.py 23 | gpkit/nomials/array.py 24 | gpkit/nomials/data.py 25 | gpkit/nomials/core.py 26 | gpkit/nomials/math.py 27 | gpkit/nomials/map.py 28 | gpkit/nomials/substitution.py 29 | gpkit/nomials/variables.py 30 | gpkit/solvers/__init__.py 31 | gpkit/solvers/mosek_cli.py 32 | gpkit/solvers/cvxopt.py 33 | gpkit/solvers/mosek_conif.py 34 | gpkit/tests/__init__.py 35 | gpkit/tests/from_paths.py 36 | gpkit/tests/helpers.py 37 | gpkit/tests/run_tests.py 38 | gpkit/tests/t_constraints.py 39 | gpkit/tests/t_examples.py 40 | gpkit/tests/t_keydict.py 41 | gpkit/tests/t_model.py 42 | gpkit/tests/t_nomial_array.py 43 | gpkit/tests/t_nomials.py 44 | gpkit/tests/t_small.py 45 | gpkit/tests/t_solution_array.py 46 | gpkit/tests/t_sub.py 47 | gpkit/tests/t_tools.py 48 | gpkit/tests/t_vars.py 49 | gpkit/tests/test_repo.py 50 | gpkit/tools/__init__.py 51 | gpkit/tools/autosweep.py 52 | gpkit/tools/docstring.py 53 | gpkit/tools/tools.py 54 | gpkit/units.py 55 | gpkit/__init__.py 56 | gpkit/build.py 57 | gpkit/breakdowns.py 58 | gpkit/exceptions.py 59 | gpkit/globals.py 60 | gpkit/keydict.py 61 | gpkit/repr_conventions.py 62 | gpkit/small_classes.py 63 | gpkit/small_scripts.py 64 | gpkit/solution_array.py 65 | gpkit/varkey.py 66 | setup.py 67 | README.md 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [GPkit](http://gpkit.readthedocs.org/) 2 | 3 | **[Documentation](http://gpkit.readthedocs.org/)** | [Install instructions](http://gpkit.readthedocs.org/en/latest/installation.html) | [Examples](http://gpkit.readthedocs.org/en/latest/examples.html) | [Glossary](https://gpkit.readthedocs.io/en/latest/autodoc/gpkit.html) | [Citing GPkit](http://gpkit.readthedocs.org/en/latest/citinggpkit.html) 4 | 5 | GPkit is a Python package for defining and manipulating 6 | geometric programming models, 7 | abstracting away the backend solver. 8 | Supported solvers are 9 | [mosek](http://mosek.com) 10 | and [cvxopt](http://cvxopt.org/). 11 | 12 | [![Build Status](https://acdl.mit.edu/csi/buildStatus/icon?job=CE_gpkit_Push_unit_tests)](https://acdl.mit.edu/csi/view/convex%20engineering/job/CE_gpkit_Push_unit_tests/) Unit tests 13 | 14 | [![Build Status](https://acdl.mit.edu/csi/buildStatus/icon?job=CE_gpkit_Install)](https://acdl.mit.edu/csi/view/convex%20engineering/job/CE_gpkit_Install/) pip install 15 | 16 | [![Build Status](https://acdl.mit.edu/csi/buildStatus/icon?job=CE_gpkit_Push_dependency_tests)](https://acdl.mit.edu/csi/view/convex%20engineering/job/CE_gpkit_Push_dependency_tests/) Dependencies 17 | -------------------------------------------------------------------------------- /docs/apidoc.sh: -------------------------------------------------------------------------------- 1 | rm source/autodoc/* 2 | sphinx-apidoc ../gpkit -o source/autodoc 3 | 4 | # Delete first 3 lines 5 | tail -n+3 source/autodoc/gpkit.rst 6 | 7 | # Add header 8 | header=$'Glossary\n********\n\n*For an alphabetical listing of all commands, check out the* :ref:\`genindex\`\n' 9 | echo $header | cat - source/autodoc/gpkit.rst > gpkit.rst && mv gpkit.rst source/autodoc/gpkit.rst 10 | -------------------------------------------------------------------------------- /docs/refresh_build.sh: -------------------------------------------------------------------------------- 1 | while true 2 | do 3 | sleep 10s 4 | git pull 5 | make html 6 | done 7 | -------------------------------------------------------------------------------- /docs/source/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | @import 'theme.css'; 2 | 3 | div[class^='highlight-breakdowns'] pre { 4 | line-height: 1.15 !important; 5 | } 6 | -------------------------------------------------------------------------------- /docs/source/acknowledgements.rst: -------------------------------------------------------------------------------- 1 | Acknowledgements 2 | **************** 3 | 4 | We thank the following contributors for helping to improve GPkit: 5 | 6 | * Marshall Galbraith for setting up continuous integration. 7 | * `Stephen Boyd`_ for inspiration and suggestions. 8 | * `Kirsten Bray`_ for designing the GPkit logo. 9 | 10 | .. _`Stephen Boyd`: http://stanford.edu/~boyd/ 11 | .. _`Kirsten Bray`: mailto:kgbray@umich.edu 12 | -------------------------------------------------------------------------------- /docs/source/autodoc/gpkit.constraints.rst: -------------------------------------------------------------------------------- 1 | gpkit.constraints package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | gpkit.constraints.array module 8 | ------------------------------ 9 | 10 | .. automodule:: gpkit.constraints.array 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | gpkit.constraints.bounded module 16 | -------------------------------- 17 | 18 | .. automodule:: gpkit.constraints.bounded 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | gpkit.constraints.costed module 24 | ------------------------------- 25 | 26 | .. automodule:: gpkit.constraints.costed 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | gpkit.constraints.gp module 32 | --------------------------- 33 | 34 | .. automodule:: gpkit.constraints.gp 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | gpkit.constraints.loose module 40 | ------------------------------ 41 | 42 | .. automodule:: gpkit.constraints.loose 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | gpkit.constraints.model module 48 | ------------------------------ 49 | 50 | .. automodule:: gpkit.constraints.model 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | gpkit.constraints.prog\_factories module 56 | ---------------------------------------- 57 | 58 | .. automodule:: gpkit.constraints.prog_factories 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | gpkit.constraints.relax module 64 | ------------------------------ 65 | 66 | .. automodule:: gpkit.constraints.relax 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | gpkit.constraints.set module 72 | ---------------------------- 73 | 74 | .. automodule:: gpkit.constraints.set 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | gpkit.constraints.sgp module 80 | ---------------------------- 81 | 82 | .. automodule:: gpkit.constraints.sgp 83 | :members: 84 | :undoc-members: 85 | :show-inheritance: 86 | 87 | gpkit.constraints.sigeq module 88 | ------------------------------ 89 | 90 | .. automodule:: gpkit.constraints.sigeq 91 | :members: 92 | :undoc-members: 93 | :show-inheritance: 94 | 95 | gpkit.constraints.single\_equation module 96 | ----------------------------------------- 97 | 98 | .. automodule:: gpkit.constraints.single_equation 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | 103 | gpkit.constraints.tight module 104 | ------------------------------ 105 | 106 | .. automodule:: gpkit.constraints.tight 107 | :members: 108 | :undoc-members: 109 | :show-inheritance: 110 | 111 | 112 | Module contents 113 | --------------- 114 | 115 | .. automodule:: gpkit.constraints 116 | :members: 117 | :undoc-members: 118 | :show-inheritance: 119 | -------------------------------------------------------------------------------- /docs/source/autodoc/gpkit.interactive.rst: -------------------------------------------------------------------------------- 1 | gpkit.interactive package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | gpkit.interactive.plot\_sweep module 8 | ------------------------------------ 9 | 10 | .. automodule:: gpkit.interactive.plot_sweep 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | gpkit.interactive.plotting module 16 | --------------------------------- 17 | 18 | .. automodule:: gpkit.interactive.plotting 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | gpkit.interactive.references module 24 | ----------------------------------- 25 | 26 | .. automodule:: gpkit.interactive.references 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | gpkit.interactive.sankey module 32 | ------------------------------- 33 | 34 | .. automodule:: gpkit.interactive.sankey 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | gpkit.interactive.widgets module 40 | -------------------------------- 41 | 42 | .. automodule:: gpkit.interactive.widgets 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | 48 | Module contents 49 | --------------- 50 | 51 | .. automodule:: gpkit.interactive 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | -------------------------------------------------------------------------------- /docs/source/autodoc/gpkit.nomials.rst: -------------------------------------------------------------------------------- 1 | gpkit.nomials package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | gpkit.nomials.array module 8 | -------------------------- 9 | 10 | .. automodule:: gpkit.nomials.array 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | gpkit.nomials.core module 16 | ------------------------- 17 | 18 | .. automodule:: gpkit.nomials.core 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | gpkit.nomials.data module 24 | ------------------------- 25 | 26 | .. automodule:: gpkit.nomials.data 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | gpkit.nomials.map module 32 | ------------------------ 33 | 34 | .. automodule:: gpkit.nomials.map 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | gpkit.nomials.math module 40 | ------------------------- 41 | 42 | .. automodule:: gpkit.nomials.math 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | gpkit.nomials.substitution module 48 | --------------------------------- 49 | 50 | .. automodule:: gpkit.nomials.substitution 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | gpkit.nomials.variables module 56 | ------------------------------ 57 | 58 | .. automodule:: gpkit.nomials.variables 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | 64 | Module contents 65 | --------------- 66 | 67 | .. automodule:: gpkit.nomials 68 | :members: 69 | :undoc-members: 70 | :show-inheritance: 71 | -------------------------------------------------------------------------------- /docs/source/autodoc/gpkit.rst: -------------------------------------------------------------------------------- 1 | Glossary 2 | ******** 3 | 4 | *For an alphabetical listing of all commands, check out the* :ref:`genindex` 5 | 6 | gpkit package 7 | ============= 8 | 9 | Subpackages 10 | ----------- 11 | 12 | .. toctree:: 13 | :maxdepth: 4 14 | 15 | gpkit.constraints 16 | gpkit.interactive 17 | gpkit.nomials 18 | gpkit.solvers 19 | gpkit.tools 20 | 21 | Submodules 22 | ---------- 23 | 24 | gpkit.build module 25 | ------------------ 26 | 27 | .. automodule:: gpkit.build 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | 32 | gpkit.exceptions module 33 | ----------------------- 34 | 35 | .. automodule:: gpkit.exceptions 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | 40 | gpkit.globals module 41 | -------------------- 42 | 43 | .. automodule:: gpkit.globals 44 | :members: 45 | :undoc-members: 46 | :show-inheritance: 47 | 48 | gpkit.keydict module 49 | -------------------- 50 | 51 | .. automodule:: gpkit.keydict 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | 56 | gpkit.repr\_conventions module 57 | ------------------------------ 58 | 59 | .. automodule:: gpkit.repr_conventions 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | 64 | gpkit.small\_classes module 65 | --------------------------- 66 | 67 | .. automodule:: gpkit.small_classes 68 | :members: 69 | :undoc-members: 70 | :show-inheritance: 71 | 72 | gpkit.small\_scripts module 73 | --------------------------- 74 | 75 | .. automodule:: gpkit.small_scripts 76 | :members: 77 | :undoc-members: 78 | :show-inheritance: 79 | 80 | gpkit.solution\_array module 81 | ---------------------------- 82 | 83 | .. automodule:: gpkit.solution_array 84 | :members: 85 | :undoc-members: 86 | :show-inheritance: 87 | 88 | gpkit.units module 89 | ------------------ 90 | 91 | .. automodule:: gpkit.units 92 | :members: 93 | :undoc-members: 94 | :show-inheritance: 95 | 96 | gpkit.varkey module 97 | ------------------- 98 | 99 | .. automodule:: gpkit.varkey 100 | :members: 101 | :undoc-members: 102 | :show-inheritance: 103 | 104 | 105 | Module contents 106 | --------------- 107 | 108 | .. automodule:: gpkit 109 | :members: 110 | :undoc-members: 111 | :show-inheritance: 112 | -------------------------------------------------------------------------------- /docs/source/autodoc/gpkit.solvers.rst: -------------------------------------------------------------------------------- 1 | gpkit.solvers package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | gpkit.solvers.cvxopt module 8 | --------------------------- 9 | 10 | .. automodule:: gpkit.solvers.cvxopt 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | gpkit.solvers.mosek\_cli module 16 | ------------------------------- 17 | 18 | .. automodule:: gpkit.solvers.mosek_cli 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | gpkit.solvers.mosek\_conif module 24 | --------------------------------- 25 | 26 | .. automodule:: gpkit.solvers.mosek_conif 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: gpkit.solvers 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /docs/source/autodoc/gpkit.tools.rst: -------------------------------------------------------------------------------- 1 | gpkit.tools package 2 | =================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | gpkit.tools.autosweep module 8 | ---------------------------- 9 | 10 | .. automodule:: gpkit.tools.autosweep 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | gpkit.tools.docstring module 16 | ---------------------------- 17 | 18 | .. automodule:: gpkit.tools.docstring 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | gpkit.tools.tools module 24 | ------------------------ 25 | 26 | .. automodule:: gpkit.tools.tools 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: gpkit.tools 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /docs/source/autodoc/modules.rst: -------------------------------------------------------------------------------- 1 | gpkit 2 | ===== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | gpkit 8 | -------------------------------------------------------------------------------- /docs/source/citinggpkit.rst: -------------------------------------------------------------------------------- 1 | Citing GPkit 2 | ************ 3 | 4 | If you use GPkit please cite it with the following bibtex:: 5 | 6 | @inproceedings{burnell2020gpkit, 7 | author={Burnell, Edward and Damen, Nicole B and Hoburg, Warren}, 8 | title={\hbox{GPkit}: A Human-Centered Approach to Convex Optimization in Engineering Design}, 9 | booktitle={Proceedings of the 2020 {CHI} Conference on Human Factors in Computing Systems}, 10 | year={2020}, 11 | doi={10.1145/3313831.3376412} 12 | } 13 | 14 | (and you can read that paper, which describes some of GPkit's design philosophy, `here. `_) 15 | -------------------------------------------------------------------------------- /docs/source/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ******** 3 | 4 | iPython Notebook Examples 5 | ========================= 6 | 7 | More examples, including some with in-depth explanations and interactive visualizations, can be seen `on nbviewer `_. 8 | 9 | 10 | A Trivial GP 11 | ============ 12 | The most trivial GP we can think of: 13 | minimize :math:`x` subject to the constraint :math:`x \ge 1`. 14 | 15 | .. literalinclude:: examples/x_greaterthan_1.py 16 | 17 | Of course, the optimal value is 1. Output: 18 | 19 | .. literalinclude:: examples/x_greaterthan_1_output.txt 20 | :language: breakdowns 21 | 22 | Maximizing the Volume of a Box 23 | ============================== 24 | This example comes from Section 2.4 of the `GP tutorial `_, by S. Boyd et. al. 25 | 26 | .. literalinclude:: examples/simple_box.py 27 | 28 | The output is 29 | 30 | .. literalinclude:: examples/simple_box_output.txt 31 | :language: breakdowns 32 | 33 | Water Tank 34 | ========== 35 | Say we had a fixed mass of water we wanted to contain within a tank, but also wanted to minimize the cost of the material we had to purchase (i.e. the surface area of the tank): 36 | 37 | .. literalinclude:: examples/water_tank.py 38 | 39 | The output is: 40 | 41 | .. literalinclude:: examples/water_tank_output.txt 42 | :language: breakdowns 43 | 44 | Simple Wing 45 | =========== 46 | This example comes from Section 3 of `Geometric Programming for Aircraft Design Optimization `_, by W. Hoburg and P. Abbeel. 47 | 48 | .. literalinclude:: examples/simpleflight.py 49 | 50 | The output is: 51 | 52 | .. literalinclude:: examples/simpleflight_output.txt 53 | :language: breakdowns 54 | 55 | Simple Beam 56 | =========== 57 | In this example we consider a beam subjected to a uniformly distributed transverse force along its length. The beam has fixed geometry so we are not optimizing its shape, rather we are simply solving a discretization of the Euler-Bernoulli beam bending equations using GP. 58 | 59 | .. literalinclude:: examples/beam.py 60 | 61 | The output is: 62 | 63 | .. literalinclude:: examples/beam_output.txt 64 | :language: breakdowns 65 | 66 | By plotting the deflection, we can see that the agreement between the analytical solution and the GP solution is good. 67 | 68 | .. figure:: examples/beam.svg 69 | :width: 500 px 70 | :align: center 71 | 72 | .. Comments: 73 | 74 | .. 75 | .. literalinclude:: code/simple_box.py 76 | :language: python 77 | :emphasize-lines: 2-4, 6 78 | :lines: 1-7 79 | -------------------------------------------------------------------------------- /docs/source/examples/autosweep.py: -------------------------------------------------------------------------------- 1 | "Show autosweep_1d functionality" 2 | import pickle 3 | import numpy as np 4 | import gpkit 5 | from gpkit import units, Variable, Model 6 | from gpkit.tools.autosweep import autosweep_1d 7 | from gpkit.small_scripts import mag 8 | 9 | A = Variable("A", "m**2") 10 | l = Variable("l", "m") 11 | 12 | m1 = Model(A**2, [A >= l**2 + units.m**2]) 13 | tol1 = 1e-3 14 | bst1 = autosweep_1d(m1, tol1, l, [1, 10], verbosity=0) 15 | print("Solved after %2i passes, cost logtol +/-%.3g" % (bst1.nsols, bst1.tol)) 16 | # autosweep solution accessing 17 | l_vals = np.linspace(1, 10, 10) 18 | sol1 = bst1.sample_at(l_vals) 19 | print("values of l: %s" % l_vals) 20 | print("values of A: [%s] %s" % 21 | (" ".join("% .1f" % n for n in sol1("A").magnitude), sol1("A").units)) 22 | cost_estimate = sol1["cost"] 23 | cost_lb, cost_ub = sol1.cost_lb(), sol1.cost_ub() 24 | print("cost lower bound:\n%s\n" % cost_lb) 25 | print("cost estimate:\n%s\n" % cost_estimate) 26 | print("cost upper bound:\n%s\n" % cost_ub) 27 | # you can evaluate arbitrary posynomials 28 | np.testing.assert_allclose(mag(2*sol1(A)), mag(sol1(2*A))) 29 | assert (sol1["cost"] == sol1(A**2)).all() 30 | # the cost estimate is the logspace mean of its upper and lower bounds 31 | np.testing.assert_allclose((np.log(mag(cost_lb)) + np.log(mag(cost_ub)))/2, 32 | np.log(mag(cost_estimate))) 33 | # save autosweep to a file and retrieve it 34 | bst1.save("autosweep.pkl") 35 | bst1_loaded = pickle.load(open("autosweep.pkl", "rb")) 36 | 37 | # this problem is two intersecting lines in logspace 38 | m2 = Model(A**2, [A >= (l/3)**2, A >= (l/3)**0.5 * units.m**1.5]) 39 | tol2 = {"mosek_cli": 1e-6, "mosek_conif": 1e-6, 40 | "cvxopt": 1e-7}[gpkit.settings["default_solver"]] 41 | # test Model method 42 | sol2 = m2.autosweep({l: [1, 10]}, tol2, verbosity=0) 43 | bst2 = sol2.bst 44 | print("Solved after %2i passes, cost logtol +/-%.3g" % (bst2.nsols, bst2.tol)) 45 | print("Table of solutions used in the autosweep:") 46 | print(bst2.solarray.table()) 47 | -------------------------------------------------------------------------------- /docs/source/examples/autosweep_output.txt: -------------------------------------------------------------------------------- 1 | Solved after 33 passes, cost logtol +/-0.000992 2 | values of l: [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] 3 | values of A: [ 2.0 5.0 10.0 17.0 26.0 37.0 50.0 65.0 82.0 101.0] meter ** 2 4 | cost lower bound: 5 | [3.99999897e+00 2.49990635e+01 9.99519417e+01 2.88964405e+02 6 | 6.75761038e+02 1.36887689e+03 2.49888336e+03 4.22418997e+03 7 | 6.72085595e+03 1.02009910e+04] 8 | 9 | cost estimate: 10 | [3.99999897e+00 2.50021684e+01 1.00001162e+02 2.89043164e+02 11 | 6.76096986e+02 1.36923920e+03 2.50043987e+03 4.22599006e+03 12 | 6.72550897e+03 1.02009910e+04] 13 | 14 | cost upper bound: 15 | [3.99999897e+00 2.50052737e+01 1.00050406e+02 2.89121944e+02 16 | 6.76433102e+02 1.36960161e+03 2.50199736e+03 4.22779092e+03 17 | 6.73016521e+03 1.02009910e+04] 18 | 19 | Solved after 3 passes, cost logtol +/-0 20 | Table of solutions used in the autosweep: 21 | 22 | Optimal Cost 23 | ------------ 24 | [ 0.333 1 123 ] 25 | 26 | Free Variables 27 | -------------- 28 | A : [ 0.577 1 11.1 ] [m²] 29 | 30 | Fixed Variables 31 | --------------- 32 | l : [ 1 3 10 ] [m] 33 | 34 | Variable Sensitivities 35 | ---------------------- 36 | l : [ +1 +2.5 +4 ] 37 | 38 | Most Sensitive Constraints (in last sweep) 39 | ------------------------------------------ 40 | +2 : A ≥ (l/3)² 41 | 42 | -------------------------------------------------------------------------------- /docs/source/examples/beam.py: -------------------------------------------------------------------------------- 1 | """ 2 | A simple beam example with fixed geometry. Solves the discretized 3 | Euler-Bernoulli beam equations for a constant distributed load 4 | """ 5 | import numpy as np 6 | from gpkit import parse_variables, Model, ureg 7 | from gpkit.small_scripts import mag 8 | 9 | eps = 2e-4 # has to be quite large for consistent cvxopt printouts; 10 | # normally you'd set this to something more like 1e-20 11 | 12 | 13 | class Beam(Model): 14 | """Discretization of the Euler beam equations for a distributed load. 15 | 16 | Variables 17 | --------- 18 | EI [N*m^2] Bending stiffness 19 | dx [m] Length of an element 20 | L 5 [m] Overall beam length 21 | 22 | Boundary Condition Variables 23 | ---------------------------- 24 | V_tip eps [N] Tip loading 25 | M_tip eps [N*m] Tip moment 26 | th_base eps [-] Base angle 27 | w_base eps [m] Base deflection 28 | 29 | Node Variables of length N 30 | -------------------------- 31 | q 100*np.ones(N) [N/m] Distributed load 32 | V [N] Internal shear 33 | M [N*m] Internal moment 34 | th [-] Slope 35 | w [m] Displacement 36 | 37 | Upper Unbounded 38 | --------------- 39 | w_tip 40 | 41 | """ 42 | @parse_variables(__doc__, globals()) 43 | def setup(self, N=4): 44 | # minimize tip displacement (the last w) 45 | self.cost = self.w_tip = w[-1] 46 | return { 47 | "definition of dx": L == (N-1)*dx, 48 | "boundary_conditions": [ 49 | V[-1] >= V_tip, 50 | M[-1] >= M_tip, 51 | th[0] >= th_base, 52 | w[0] >= w_base 53 | ], 54 | # below: trapezoidal integration to form a piecewise-linear 55 | # approximation of loading, shear, and so on 56 | # shear and moment increase from tip to base (left > right) 57 | "shear integration": 58 | V[:-1] >= V[1:] + 0.5*dx*(q[:-1] + q[1:]), 59 | "moment integration": 60 | M[:-1] >= M[1:] + 0.5*dx*(V[:-1] + V[1:]), 61 | # slope and displacement increase from base to tip (right > left) 62 | "theta integration": 63 | th[1:] >= th[:-1] + 0.5*dx*(M[1:] + M[:-1])/EI, 64 | "displacement integration": 65 | w[1:] >= w[:-1] + 0.5*dx*(th[1:] + th[:-1]) 66 | } 67 | 68 | 69 | b = Beam(N=6, substitutions={"L": 6, "EI": 1.1e4, "q": 110*np.ones(6)}) 70 | sol = b.solve(verbosity=0) 71 | print(sol.summary(maxcolumns=6)) 72 | w_gp = sol("w") # deflection along beam 73 | 74 | L, EI, q = sol("L"), sol("EI"), sol("q") 75 | x = np.linspace(0, mag(L), len(q))*ureg.m # position along beam 76 | q = q[0] # assume uniform loading for the check below 77 | w_exact = q/(24*EI) * x**2 * (x**2 - 4*L*x + 6*L**2) # analytic soln 78 | assert max(abs(w_gp - w_exact)) <= 1.1*ureg.cm 79 | 80 | PLOT = False 81 | if PLOT: # pragma: no cover 82 | import matplotlib.pyplot as plt 83 | x_exact = np.linspace(0, L, 1000) 84 | w_exact = q/(24*EI) * x_exact**2 * (x_exact**2 - 4*L*x_exact + 6*L**2) 85 | plt.plot(x, w_gp, color='red', linestyle='solid', marker='^', 86 | markersize=8) 87 | plt.plot(x_exact, w_exact, color='blue', linestyle='dashed') 88 | plt.xlabel('x [m]') 89 | plt.ylabel('Deflection [m]') 90 | plt.axis('equal') 91 | plt.legend(['GP solution', 'Analytical solution']) 92 | plt.show() 93 | -------------------------------------------------------------------------------- /docs/source/examples/beam_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃┓ ┓ ┓ ┓ ┓ 3 | ┃┃ ┃ ┃ ┃ ┣╸th[2]╶⎨ 4 | ┃┃ ┃ ┃ ┣╸w[2] ┛ (0.285) 5 | ┃┃ ┃ ┃ ┃ (0.384m) ┣╸th[1]╶⎨ 6 | ┃┃ ┃ ┣╸w[3] ┛ ┣╸w[1]╶⎨ 7 | ┃┃ ┃ ┃ (0.76m) ┣╸th[3] ┣╸th[2]╶⎨ 8 | ┃┃ ┃ ┃ ┛ (0.341) ┛ 9 | ┃┃ ┣╸w[4] ┃ ┣╸th[2]╶⎨ 10 | ┃┃ ┃ (1.18m) ┛ ┛ 11 | Cost╺┫┃ ┃ ┣╸th[3] ┣╸th[2]╶⎨ 12 | (1.62m) ┃┣╸w[5] ┃ ┛ ┛ 13 | ┃┃ (1.62m) ┃ ┓ ┓ 14 | ┃┃ ┃ ┣╸th[4] ┣╸th[2]╶⎨ 15 | ┃┃ ┛ ┛ (0.363) ┛ 16 | ┃┃ ┓ ┓ 17 | ┃┃ ┣╸th[5] ┣╸th[2]╶⎨ 18 | ┃┃ ┛ (0.367) ┛ 19 | ┃┃ ┓ ┓ 20 | ┃┃ ┣╸th[4] ┣╸th[2]╶⎨ 21 | ┃┛ ┛ ┛ 22 | 23 | 24 | 25 | ┃┓ ┓ 26 | ┃┃ ┃ 27 | ┃┃ ┣╸L = 5·dx 28 | ┃┃ ┛ 29 | ┃┃ ┓ 30 | ┃┃ ┃ 31 | ┃┃ ┣╸L = 6m 32 | ┃┃ ┛ 33 | ┃┃ ┣╸EI = 11,000N·m² 34 | Model╺┫┃ ┣╸w[5] ≥ w[4] + 0.5·dx·(th[5] + th[4]) 35 | ┃┣╸Beam ┣╸th[2] ≥ th[1] + 0.5·dx·(M[2] + M[1])/EI 36 | ┃┃ ┣╸w[4] ≥ w[3] + 0.5·dx·(th[4] + th[3]) 37 | ┃┃ ┣╸M[1] ≥ M[2] + 0.5·dx·(V[1] + V[2]) 38 | ┃┃ ┣╸th[3] ≥ th[2] + 0.5·dx·(M[3] + M[2])/EI 39 | ┃┃ ┣╸V[3] ≥ V[4] + 0.5·dx·(q[3] + q[4]) 40 | ┃┃ ┣╸th[1] ≥ th[0] + 0.5·dx·(M[1] + M[0])/EI 41 | ┃┃ ┓ 42 | ┃┃ ┃ 43 | ┃┃ ┣╸[17 terms] 44 | ┃┛ ┛ 45 | 46 | 47 | Free Variables 48 | -------------- 49 | dx : 1.2 [m] Length of an element 50 | M : [ 1.98e+03 1.27e+03 713 317 79.2 0.0002 ] [N·m] Internal moment 51 | V : [ 660 528 396 264 132 0.0002 ] [N] Internal shear 52 | th : [ 0.0002 0.177 0.285 0.341 0.363 0.367 ] Slope 53 | w : [ 0.0002 0.107 0.384 0.76 1.18 1.62 ] [m] Displacement 54 | 55 | -------------------------------------------------------------------------------- /docs/source/examples/boundschecking.py: -------------------------------------------------------------------------------- 1 | "Verifies that bounds are caught through monomials" 2 | from gpkit import Model, parse_variables 3 | from gpkit.exceptions import UnboundedGP, UnknownInfeasible 4 | 5 | 6 | class BoundsChecking(Model): 7 | """Implements a crazy set of unbounded variables. 8 | 9 | Variables 10 | --------- 11 | Ap [-] d 12 | D [-] e 13 | F [-] s 14 | mi [-] c 15 | mf [-] r 16 | T [-] i 17 | nu [-] p 18 | Fs 0.9 [-] t 19 | mb 0.4 [-] i 20 | rf 0.01 [-] o 21 | V 300 [-] n 22 | 23 | Upper Unbounded 24 | --------------- 25 | F 26 | 27 | Lower Unbounded 28 | --------------- 29 | D 30 | 31 | """ 32 | @parse_variables(__doc__, globals()) 33 | def setup(self): 34 | self.cost = F 35 | return [ 36 | F >= D + T, 37 | D == rf*V**2*Ap, 38 | Ap == nu, 39 | T == mf*V, 40 | mf >= mi + mb, 41 | mf == rf*V, 42 | Fs <= mi 43 | ] 44 | 45 | 46 | m = BoundsChecking() 47 | print(m.str_without(["lineage"])) 48 | try: 49 | m.solve() 50 | except UnboundedGP: 51 | gp = m.gp(checkbounds=False) 52 | missingbounds = gp.check_bounds() 53 | 54 | try: 55 | sol = gp.solve(verbosity=0) # Errors on mosek_cli 56 | except UnknownInfeasible: # pragma: no cover 57 | pass 58 | 59 | bpl = ", but would gain it from any of these sets: " 60 | assert missingbounds[(m.D.key, 'lower')] == bpl + "[(%s, 'lower')]" % m.Ap 61 | assert missingbounds[(m.nu.key, 'lower')] == bpl + "[(%s, 'lower')]" % m.Ap 62 | # ordering is arbitrary: 63 | assert missingbounds[(m.Ap.key, 'lower')] in ( 64 | bpl + ("[(%s, 'lower')] or [(%s, 'lower')]" % (m.D, m.nu)), 65 | bpl + ("[(%s, 'lower')] or [(%s, 'lower')]" % (m.nu, m.D))) 66 | -------------------------------------------------------------------------------- /docs/source/examples/boundschecking_output.txt: -------------------------------------------------------------------------------- 1 | BoundsChecking 2 | ============== 3 | 4 | Cost Function 5 | ------------- 6 | F 7 | 8 | Constraints 9 | ----------- 10 | F ≥ D + T 11 | D = rf·V²·Ap 12 | Ap = nu 13 | T = mf·V 14 | mf ≥ mi + mb 15 | mf = rf·V 16 | Fs ≤ mi 17 | -------------------------------------------------------------------------------- /docs/source/examples/breakdowns.py: -------------------------------------------------------------------------------- 1 | "An example to show off Breakdowns" 2 | import os 3 | import sys 4 | import pickle 5 | import pint 6 | from packaging import version 7 | from gpkit.breakdowns import Breakdowns 8 | 9 | dirpath = os.path.dirname(os.path.realpath(__file__)) + os.sep 10 | if version.parse(pint.__version__) >= version.parse("0.13"): 11 | sol = pickle.load(open(dirpath+"solar_13.p", "rb")) 12 | elif version.parse(pint.__version__) >= version.parse("0.12"): 13 | sol = pickle.load(open(dirpath+"solar_12.p", "rb")) 14 | elif version.parse(pint.__version__) >= version.parse("0.10"): 15 | sol = pickle.load(open(dirpath+"solar_10.p", "rb")) 16 | elif version.parse(pint.__version__) == version.parse("0.9"): 17 | sol = pickle.load(open(dirpath+"solar.p", "rb")) 18 | else: 19 | sol = None 20 | 21 | # our Miniconda windows test platform can't print unicode 22 | if sys.platform[:3] != "win" and sol is not None: 23 | # the code to create solar.p is in ./breakdowns/solartest.py 24 | bds = Breakdowns(sol) 25 | 26 | print("Cost breakdown (as seen in solution tables)") 27 | print("==============") 28 | bds.plot("cost") 29 | 30 | print("Variable breakdowns (note the two methods of access)") 31 | print("===================") 32 | varkey, = sol["variables"].keymap[("Mission.FlightSegment.AircraftPerf" 33 | ".AircraftDrag.Poper")] 34 | bds.plot(varkey) 35 | bds.plot("AircraftPerf.AircraftDrag.MotorPerf.Q") 36 | 37 | print("Combining the two above by increasing maxwidth") 38 | print("----------------------------------------------") 39 | bds.plot("AircraftPerf.AircraftDrag.Poper", maxwidth=105) 40 | 41 | print("Model sensitivity breakdowns (note the two methods of access)") 42 | print("============================") 43 | bds.plot("model sensitivities") 44 | bds.plot("Aircraft") 45 | 46 | print("Exhaustive variable breakdown traces (and configuration arguments)") 47 | print("====================================") 48 | # often useful as a reference point when reading traces 49 | bds.plot("AircraftPerf.AircraftDrag.Poper", height=12) 50 | # includes factors, can be useful for reading traces as well 51 | bds.plot("AircraftPerf.AircraftDrag.Poper", showlegend=True) 52 | print("\nPermissivity = 2 (the default)") 53 | print("----------------") 54 | bds.trace("AircraftPerf.AircraftDrag.Poper") 55 | print("\nPermissivity = 1 (stops at Pelec = v·i)") 56 | print("----------------") 57 | bds.trace("AircraftPerf.AircraftDrag.Poper", permissivity=1) 58 | 59 | # you can also produce Plotly treemaps/icicle plots of your breakdowns 60 | fig = bds.treemap("model sensitivities", returnfig=True) 61 | fig = bds.icicle("cost", returnfig=True) 62 | # uncommenting any of the below makes and shows the plot directly 63 | # bds.icicle("model sensitivities") 64 | # bds.treemap("cost") 65 | -------------------------------------------------------------------------------- /docs/source/examples/breakdowns/solartest.py: -------------------------------------------------------------------------------- 1 | from solar.solar import * 2 | Vehicle = Aircraft(Npod=3, sp=True) 3 | M = Mission(Vehicle, latitude=[20]) 4 | M.cost = M[M.aircraft.Wtotal] 5 | 6 | M.localsolve().save("solar_13.p") # suffix is min pint version worked for 7 | -------------------------------------------------------------------------------- /docs/source/examples/checking_result_changes.py: -------------------------------------------------------------------------------- 1 | "Example code for solution saving and differencing." 2 | import pickle 3 | from gpkit import Model, Variable 4 | 5 | # build model (dummy) 6 | # decision variable 7 | x = Variable("x") 8 | y = Variable("y") 9 | 10 | # objective and constraints 11 | objective = 0.23 + x/y # minimize x and y 12 | constraints = [x + y <= 5, x >= 1, y >= 2] 13 | 14 | # create model 15 | m = Model(objective, constraints) 16 | 17 | # solve the model 18 | # verbosity is 0 for testing's sake, no need to do that in your code! 19 | sol = m.solve(verbosity=0) 20 | 21 | # save the current state of the model 22 | sol.save("last_verified.sol") 23 | 24 | # uncomment the line below to verify a new model 25 | last_verified_sol = pickle.load(open("last_verified.sol", mode="rb")) 26 | if not sol.almost_equal(last_verified_sol, reltol=1e-3): 27 | print(last_verified_sol.diff(sol)) 28 | 29 | # Note you can replace the last three lines above with 30 | # print(sol.diff("last_verified.sol")) 31 | # if you don't mind doing the diff in that direction. 32 | -------------------------------------------------------------------------------- /docs/source/examples/checking_result_changes_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/checking_result_changes_output.txt -------------------------------------------------------------------------------- /docs/source/examples/debug.py: -------------------------------------------------------------------------------- 1 | "Debug examples" 2 | from gpkit import Variable, Model, units 3 | 4 | x = Variable("x", "ft") 5 | x_min = Variable("x_min", 2, "ft") 6 | x_max = Variable("x_max", 1, "ft") 7 | y = Variable("y", "volts") 8 | 9 | m = Model(x/y, [x <= x_max, x >= x_min]) 10 | m.debug() 11 | 12 | print("# Now let's try a model unsolvable with relaxed constants\n") 13 | 14 | m2 = Model(x, [x <= units("inch"), x >= units("yard")]) 15 | m2.debug() 16 | 17 | print("# And one that's only unbounded\n") 18 | 19 | # the value of x_min was used up in the previous model! 20 | x_min = Variable("x_min", 2, "ft") 21 | m3 = Model(x/y, [x >= x_min]) 22 | m3.debug() 23 | 24 | x_min = Variable("x_min", 2, "ft") 25 | m4 = Model(x, [x >= x_min]) 26 | m4.debug() 27 | -------------------------------------------------------------------------------- /docs/source/examples/debug_output.txt: -------------------------------------------------------------------------------- 1 | Model is feasible with these modifications: 2 | 3 | Arbitrarily Bounded Variables 4 | ----------------------------- 5 | value near upper bound of 1e+30: y 6 | sensitive to upper bound of 1e+30: y 7 | 8 | Relaxed Constants 9 | ----------------- 10 | x_min [ft]: relaxed from 2 to 1 11 | 12 | # Now let's try a model unsolvable with relaxed constants 13 | 14 | Model is not feasible with relaxed constants and bounded variables. 15 | Model is feasible with these modifications: 16 | 17 | Relaxed Constraints 18 | ------------------- 19 | 1: 3500% relaxed, from x [ft] >= 1 [yd] 20 | to 36·x [ft] >= 1 [yd] 21 | 22 | # And one that's only unbounded 23 | 24 | Model is feasible with these modifications: 25 | 26 | Arbitrarily Bounded Variables 27 | ----------------------------- 28 | value near upper bound of 1e+30: y 29 | sensitive to upper bound of 1e+30: y 30 | 31 | Model seems feasible without modification, or only needs relaxations of less than 1%. Check the returned solution for details. 32 | -------------------------------------------------------------------------------- /docs/source/examples/docstringparsing.py: -------------------------------------------------------------------------------- 1 | "Docstring parsing example" 2 | from gpkit import Model, parse_variables 3 | from gpkit.tools.docstring import parse_varstring 4 | 5 | 6 | class Cube(Model): 7 | """Demonstration of nomenclature syntax 8 | 9 | Lines that end in "Variables" will be parsed as a scalar variable table 10 | until the next blank line. 11 | 12 | Variables 13 | --------- 14 | A [m^2] surface area 15 | V 100 [L] minimum volume 16 | 17 | Lines that end in "Variables of length $N" will be parsed as vector 18 | variables of length $N until the next blank line. 19 | 20 | Variables of length 3 21 | --------------------- 22 | s [m] side length 23 | 24 | Let's introduce more variables: (any line ending in "Variables" is parsed) 25 | 26 | Zoning Variables 27 | ---------------- 28 | h 1 [m] minimum height 29 | 30 | Upper Unbounded 31 | --------------- 32 | A 33 | 34 | The ordering of these blocks doesn't affect anything; order them in the 35 | way that makes the most sense to someone else reading your model. 36 | """ 37 | @parse_variables(__doc__, globals()) 38 | def setup(self): 39 | 40 | return [A >= 2*(s[0]*s[1] + s[1]*s[2] + s[2]*s[0]), 41 | s.prod() >= V, 42 | s[2] >= h] 43 | 44 | 45 | print(parse_varstring(Cube.__doc__)) 46 | c = Cube() 47 | c.cost = c.A 48 | print(c.solve(verbosity=0).table()) 49 | -------------------------------------------------------------------------------- /docs/source/examples/docstringparsing_output.txt: -------------------------------------------------------------------------------- 1 | from gpkit import Variable, VectorVariable # Demonstration of nomenclature syntax 2 | # 3 | # Lines that end in "Variables" will be parsed as a scalar variable table 4 | # until the next blank line. 5 | # 6 | # Variables 7 | # --------- 8 | A = self.A = Variable('A', 'm^2', 'surface area') # from 'A [m^2] surface area' 9 | V = self.V = Variable('V', 100, 'L', 'minimum volume') # from 'V 100 [L] minimum volume' 10 | # 11 | # Lines that end in "Variables of length $N" will be parsed as vector 12 | # variables of length $N until the next blank line. 13 | # 14 | # Variables of length 3 15 | # --------------------- 16 | s = self.s = VectorVariable(3, 's', 'm', 'side length') # from 's [m] side length' 17 | # 18 | # Let's introduce more variables: (any line ending in "Variables" is parsed) 19 | # 20 | # Zoning Variables 21 | # ---------------- 22 | h = self.h = Variable('h', 1, 'm', 'minimum height') # from 'h 1 [m] minimum height' 23 | # 24 | # Upper Unbounded 25 | # --------------- 26 | # A 27 | # 28 | # The ordering of these blocks doesn't affect anything; order them in the 29 | # way that makes the most sense to someone else reading your model. 30 | # 31 | 32 | ┃┓ ┓ /┓ 33 | ┃┃ ┃ ┃ 34 | ┃┃ ┣╸s[0] /┣╸h 35 | Cost╺┫┃ ┃ (0.316m) ┃ (1m, fixed) 36 | (1.46m²) ┃┣╸A ┛ /┛ 37 | ┃┃ (1.46m²) ┓ ┓ 38 | ┃┃ ┣╸s[2] ┣╸h 39 | ┃┛ ┛ (1m) ┛ 40 | 41 | 42 | 43 | ┃┓ ┓ 44 | ┃┃ ┃ 45 | ┃┃ ┃ 46 | ┃┃ ┃ 47 | ┃┃ ┣╸A ≥ 2·(s[0]·s[1] + s[1]·s[2] + s[2]·s[0]) 48 | ┃┃ ┃ 49 | ┃┃ ┃ 50 | ┃┃ ┛ 51 | ┃┃ ┓ 52 | Model╺┫┃ ┃ 53 | ┃┣╸Cube ┣╸V = 100l 54 | ┃┃ ┛ 55 | ┃┃ ┓ 56 | ┃┃ ┃ 57 | ┃┃ ┣╸V ≤ s[:].prod() 58 | ┃┃ ┛ 59 | ┃┃ ┣╸h = 1m 60 | ┃┃ ┛ 61 | ┃┃ ┣╸s[2] ≥ h 62 | ┃┛ ┛ 63 | 64 | 65 | Free Variables 66 | -------------- 67 | A : 1.465 [m²] surface area 68 | s : [ 0.316 0.316 1 ] [m] side length 69 | 70 | Fixed Variables 71 | --------------- 72 | V : 100 [l] minimum volume 73 | h : 1 [m] minimum height 74 | 75 | Variable Sensitivities 76 | ---------------------- 77 | V : +0.57 minimum volume 78 | h : +0.3 minimum height 79 | 80 | Most Sensitive Constraints 81 | -------------------------- 82 | +1 : A ≥ 2·(s[0]·s[1] + s[1]·s[2] + s[2]·s[0]) 83 | +0.57 : V ≤ s[:].prod() 84 | +0.3 : s[2] ≥ h 85 | 86 | -------------------------------------------------------------------------------- /docs/source/examples/evaluated_fixed_variables.py: -------------------------------------------------------------------------------- 1 | "Example pre-solve evaluated fixed variable" 2 | from gpkit import Variable, Model, units 3 | 4 | # code from t_GPSubs.test_calcconst in tests/t_sub.py 5 | x = Variable("x", "hours") 6 | t_day = Variable("t_{day}", 12, "hours") 7 | t_night = Variable("t_{night}", 8 | lambda c: 1*units.day - c(t_day), "hours") 9 | 10 | # note that t_night has a function as its value 11 | m = Model(x, [x >= t_day, x >= t_night]) 12 | sol = m.solve(verbosity=0) 13 | assert sol["variables"][t_night] == 12 14 | 15 | # call substitutions 16 | m.substitutions.update({t_day: ("sweep", [8, 12, 16])}) 17 | sol = m.solve(verbosity=0) 18 | assert (sol["variables"][t_night] == [16, 12, 8]).all() 19 | -------------------------------------------------------------------------------- /docs/source/examples/evaluated_fixed_variables_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/evaluated_fixed_variables_output.txt -------------------------------------------------------------------------------- /docs/source/examples/evaluated_free_variables.py: -------------------------------------------------------------------------------- 1 | "Example post-solve evaluated variable" 2 | from gpkit import Variable, Model 3 | 4 | # code from t_constraints.test_evalfn in tests/t_sub.py 5 | x = Variable("x") 6 | x2 = Variable("x^2", evalfn=lambda v: v(x)**2) 7 | m = Model(x, [x >= 2]) 8 | m.unique_varkeys = set([x2.key]) 9 | sol = m.solve(verbosity=0) 10 | assert abs(sol(x2) - 4) <= 1e-4 11 | -------------------------------------------------------------------------------- /docs/source/examples/evaluated_free_variables_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/evaluated_free_variables_output.txt -------------------------------------------------------------------------------- /docs/source/examples/external_constraint.py: -------------------------------------------------------------------------------- 1 | "Can be found in gpkit/docs/source/examples/external_constraint.py" 2 | from external_function import external_code 3 | 4 | 5 | class ExternalConstraint: 6 | "Class for external calling" 7 | 8 | def __init__(self, x, y): 9 | # We need a GPkit variable defined to return in our constraint. The 10 | # easiest way to do this is to read in the parameters of interest in 11 | # the initiation of the class and store them here. 12 | self.x = x 13 | self.y = y 14 | 15 | def as_gpconstr(self, x0): 16 | "Returns locally-approximating GP constraint" 17 | # Creating a default constraint for the first solve 18 | if self.x not in x0: 19 | return (self.y >= self.x) 20 | # Otherwise calls external code at the current position... 21 | x_star = x0[self.x] 22 | res = external_code(x_star) 23 | # ...and returns a posynomial approximation around that position 24 | return (self.y >= res * self.x/x_star) 25 | -------------------------------------------------------------------------------- /docs/source/examples/external_constraint_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/external_constraint_output.txt -------------------------------------------------------------------------------- /docs/source/examples/external_function.py: -------------------------------------------------------------------------------- 1 | """External function for GPkit to call. Can be found 2 | in gpkit/docs/source/examples/external_function.py""" 3 | import numpy as np 4 | 5 | def external_code(x): 6 | "Returns sin(x)" 7 | return np.sin(x) 8 | -------------------------------------------------------------------------------- /docs/source/examples/external_function_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/external_function_output.txt -------------------------------------------------------------------------------- /docs/source/examples/external_sp.py: -------------------------------------------------------------------------------- 1 | "Can be found in gpkit/docs/source/examples/external_sp.py" 2 | import numpy as np 3 | from gpkit import Variable, Model 4 | from external_constraint import ExternalConstraint 5 | 6 | x = Variable("x") 7 | y = Variable("y") 8 | 9 | objective = y 10 | 11 | constraints = [ExternalConstraint(x, y), 12 | x <= np.pi/2, 13 | x >= np.pi/4, 14 | ] 15 | 16 | m = Model(objective, constraints) 17 | print(m.localsolve(verbosity=0).summary()) 18 | -------------------------------------------------------------------------------- /docs/source/examples/external_sp_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃┓ 3 | Cost╺┫┃ 4 | (0.707) ┃┣╸0.785 5 | ┃┛ 6 | 7 | 8 | 9 | ┃┓ 10 | ┃┃ 11 | ┃┣╸ 12 | Model╺┫┛ 13 | ┃┓ 14 | ┃┃ 15 | ┃┣╸x ≥ 0.785 16 | ┃┛ 17 | 18 | 19 | Free Variables 20 | -------------- 21 | x : 0.7854 22 | y : 0.7071 23 | 24 | -------------------------------------------------------------------------------- /docs/source/examples/freeing_fixed_variables.py: -------------------------------------------------------------------------------- 1 | "Example of freeing fixed variables" 2 | from gpkit import Variable, Model 3 | x = Variable("x") 4 | y = Variable("y", 3) # fix value to 3 5 | m = Model(x, [x >= 1 + y, y >= 1]) 6 | # verbosity is 0 for testing's sake, no need to do that in your code! 7 | sol = m.solve(verbosity=0) # optimal cost is 4; y appears in sol["constants"] 8 | 9 | assert abs(sol["cost"] - 4) <= 1e-4 10 | assert y in sol["constants"] 11 | 12 | del m.substitutions["y"] 13 | sol = m.solve(verbosity=0) # optimal cost is 2; y appears in Free Variables 14 | assert abs(sol["cost"] - 2) <= 1e-4 15 | assert y in sol["freevariables"] 16 | -------------------------------------------------------------------------------- /docs/source/examples/freeing_fixed_variables_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/freeing_fixed_variables_output.txt -------------------------------------------------------------------------------- /docs/source/examples/gettingstarted.py: -------------------------------------------------------------------------------- 1 | "The getting started page" 2 | from gpkit import Variable, VectorVariable, Model 3 | from gpkit.nomials import Monomial, Posynomial, PosynomialInequality 4 | 5 | ### Example Free Variables 6 | # Declare a variable, x 7 | x = Variable("x") 8 | 9 | # Declare a variable, y, with units of meters 10 | y = Variable("y", "m") 11 | 12 | # Declare a variable, z, with units of meters, and a description 13 | z = Variable("z", "m", "A variable called z with units of meters") 14 | 15 | ### Example Fixed Variables 1 16 | rho = Variable("rho", 1.225, "kg/m^3", "Density of air at sea level") 17 | 18 | ### Example Fixed Variables 2 19 | #Declare pi equal to 3.14 20 | pi = Variable("pi", 3.14159, "-", constant=True) 21 | 22 | ### Example Vector Variables 23 | # Declare a 3-element vector variable "x" with units of "m" 24 | x = VectorVariable(3, "x", "m", "Cube corner coordinates") 25 | x_min = VectorVariable(3, "x", [1, 2, 3], "m", "Cube corner minimum") 26 | 27 | ### Example Creating Monomials and Posynomials 1 28 | # create a Monomial term xy^2/z 29 | x = Variable("x") 30 | y = Variable("y") 31 | z = Variable("z") 32 | m = x * y**2 / z 33 | assert isinstance(m, Monomial) 34 | 35 | ### Example Creating Monomials and Posynomials 2 36 | # create a Posynomial expression x + xy^2 37 | x = Variable("x") 38 | y = Variable("y") 39 | p = x + x * y**2 40 | assert isinstance(p, Posynomial) 41 | 42 | ### Example Declaring Constraints 43 | # consider a block with dimensions x, y, z less than 1 44 | # constrain surface area less than 1.0 m^2 45 | x = Variable("x", "m") 46 | y = Variable("y", "m") 47 | z = Variable("z", "m") 48 | S = Variable("S", 1.0, "m^2") 49 | c = (2*x*y + 2*x*z + 2*y*z <= S) 50 | assert isinstance(c, PosynomialInequality) 51 | 52 | ### Example Formulating a Model 53 | x = Variable("x") 54 | y = Variable("y") 55 | z = Variable("z") 56 | S = 200 57 | objective = 1/(x*y*z) 58 | constraints = [2*x*y + 2*x*z + 2*y*z <= S, 59 | x >= 2*y] 60 | m = Model(objective, constraints) 61 | 62 | ### Example Solving the Model 63 | sol = m.solve(verbosity=0) 64 | 65 | ### Printing Results 1 66 | print(sol.table()) 67 | 68 | ### Printing Results 2 69 | print("The optimal value is %.4g." % sol["cost"]) 70 | 71 | ### Example variable sensitivity usage 72 | x = Variable("x") 73 | x_min = Variable("x_{min}", 2) 74 | sol = Model(x, [x_min <= x]).solve(verbosity=0) 75 | sens_x_min = sol["sensitivities"]["variables"][x_min] 76 | 77 | x = Variable("x") 78 | x_squared_min = Variable("x^2_{min}", 2) 79 | sol = Model(x, [x_squared_min <= x**2]).solve(verbosity=0) 80 | sens_x_min = sol["sensitivities"]["variables"][x_squared_min] 81 | -------------------------------------------------------------------------------- /docs/source/examples/gettingstarted_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃/┓ 3 | Cost╺┫ ┃ 4 | (0.00551) ┃/┣╸y 5 | ┃/┛ (4.08) 6 | 7 | 8 | 9 | ┃┓ 10 | ┃┃ 11 | ┃┃ 12 | Model╺┫┣╸2·x·y + 2·x·z + 2·y·z ≤ 200 13 | ┃┃ 14 | ┃┃ 15 | ┃┛ 16 | ┃┣╸x ≥ 2·y 17 | 18 | 19 | Free Variables 20 | -------------- 21 | x : 8.165 22 | y : 4.082 23 | z : 5.443 24 | 25 | Most Sensitive Constraints 26 | -------------------------- 27 | +1.5 : 2·x·y + 2·x·z + 2·y·z ≤ 200 28 | +0.17 : x ≥ 2·y 29 | 30 | The optimal value is 0.005511. 31 | -------------------------------------------------------------------------------- /docs/source/examples/issue_1513.py: -------------------------------------------------------------------------------- 1 | "Tests non-array linked functions & subs in a vectorization environment" 2 | import numpy as np 3 | from gpkit import Variable, Model, ConstraintSet, Vectorize 4 | 5 | class Vehicle(Model): 6 | "Vehicle model" 7 | def setup(self): 8 | self.a = a = Variable("a") 9 | constraints = [a >= 1] 10 | return constraints 11 | 12 | class System(Model): 13 | "System model" 14 | def setup(self): 15 | with Vectorize(1): 16 | self.Fleet2 = Fleet2() 17 | constraints = [self.Fleet2] 18 | self.cost = sum(self.Fleet2.z) 19 | return constraints 20 | 21 | class Fleet2(Model): 22 | "Fleet model (composed of multiple Vehicles)" 23 | def setup(self): 24 | x = Variable("x") 25 | lambdafun = lambda c: [c[x]-1, np.ones(x.shape)] 26 | with Vectorize(2): 27 | y = Variable("y", lambdafun) 28 | self.Vehicle = Vehicle() 29 | 30 | self.z = z = Variable("z") 31 | substitutions = {"x": 4} 32 | constraints = [ 33 | z >= sum(y/x*self.Vehicle.a), 34 | self.Vehicle, 35 | ] 36 | return constraints, substitutions 37 | 38 | m = System() 39 | sol = m.solve(verbosity=0) 40 | print(sol.table()) 41 | 42 | # now with more fleets per system 43 | class System2(Model): 44 | "System model" 45 | def setup(self): 46 | with Vectorize(3): 47 | self.Fleet2 = Fleet2() 48 | constraints = [self.Fleet2] 49 | self.cost = sum(self.Fleet2.z) 50 | return constraints 51 | 52 | m = System2() 53 | sol = m.solve(verbosity=0) 54 | print(sol.table()) 55 | 56 | 57 | # now testing substitutions 58 | 59 | class Simple(Model): 60 | "Simple model" 61 | def setup(self): 62 | self.x = x = Variable("x") 63 | y = Variable("y", 1) 64 | z = Variable("z", 2) 65 | constraints = [ 66 | x >= y + z, 67 | ] 68 | return constraints 69 | 70 | class Cake(Model): 71 | "Cake model" 72 | def setup(self): 73 | with Vectorize(3): 74 | s = Simple() 75 | c = ConstraintSet([s]) 76 | self.cost = sum(s.x) 77 | return c 78 | 79 | m = Cake() 80 | m.substitutions.update({ 81 | "y": ("sweep", [1, 2, 3]), 82 | "z": lambda v: v("y")**2, 83 | }) 84 | sol = m.solve(verbosity=0) 85 | print(sol.table()) 86 | -------------------------------------------------------------------------------- /docs/source/examples/issue_1522.py: -------------------------------------------------------------------------------- 1 | "Tests broadcast_sub function for returned-dictionary substitutions" 2 | from gpkit import Variable, Model, ConstraintSet, Vectorize 3 | from gpkit.small_scripts import broadcast_substitution 4 | 5 | class Pie(Model): 6 | "Pie model" 7 | def setup(self): 8 | self.x = x = Variable("x") 9 | z = Variable("z") 10 | constraints = [ 11 | x >= z, 12 | ] 13 | substitutions = {'z': 1} 14 | return constraints, substitutions 15 | 16 | class Cake(Model): 17 | "Cake model, containing a vector of Pies" 18 | def setup(self): 19 | self.y = y = Variable("y") 20 | with Vectorize(2): 21 | s = Pie() 22 | constraints = [y >= s.x] 23 | constraints += [s] 24 | subs = {'x': broadcast_substitution(s.x, [2, 3])} 25 | return constraints, subs 26 | 27 | class Yum1(Model): 28 | "Total dessert system model containing 5 Cakes" 29 | def setup(self): 30 | with Vectorize(5): 31 | cake = Cake() 32 | y = cake.y 33 | self.cost = sum(y) 34 | constraints = ConstraintSet([cake]) 35 | return constraints 36 | 37 | m = Yum1() 38 | sol = m.solve(verbosity=0) 39 | print(sol.table()) 40 | 41 | class Yum2(Model): 42 | "Total dessert system model containing 1 Cake" 43 | def setup(self): 44 | with Vectorize(1): 45 | cake = Cake() 46 | y = cake.y 47 | self.cost = sum(y) 48 | constraints = ConstraintSet([cake]) 49 | return constraints 50 | 51 | m = Yum2() 52 | sol = m.solve(verbosity=0) 53 | print(sol.table()) 54 | -------------------------------------------------------------------------------- /docs/source/examples/issue_1522_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃┓ ┓ 3 | ┃┃ ┃ 4 | ┃┣╸y[0] ┣╸x[1,0] 5 | ┃┛ (3) ┛ (3, fixed) 6 | ┃┓ ┓ 7 | ┃┃ ┃ 8 | ┃┣╸y[1] ┣╸x[1,1] 9 | ┃┛ (3) ┛ (3, fixed) 10 | ┃┓ ┓ 11 | Cost╺┫┃ ┃ 12 | (15) ┃┣╸y[2] ┣╸x[1,2] 13 | ┃┛ (3) ┛ (3, fixed) 14 | ┃┓ ┓ 15 | ┃┃ ┃ 16 | ┃┣╸y[3] ┣╸x[1,3] 17 | ┃┛ (3) ┛ (3, fixed) 18 | ┃┓ ┓ 19 | ┃┃ ┃ 20 | ┃┣╸y[4] ┣╸x[1,4] 21 | ┃┛ (3) ┛ (3, fixed) 22 | 23 | 24 | 25 | ┃┓ ┓ 26 | ┃┃ ┃ 27 | ┃┃ ┣╸y[0] ≥ x[1,0] 28 | ┃┃ ┛ 29 | ┃┃ ┓ 30 | ┃┃ ┃ 31 | ┃┃ ┣╸y[1] ≥ x[1,1] 32 | ┃┃ ┛ 33 | ┃┃ ┓ 34 | Model╺┫┃ ┃ 35 | ┃┣╸Cake ┣╸y[2] ≥ x[1,2] 36 | ┃┃ ┛ 37 | ┃┃ ┓ 38 | ┃┃ ┃ 39 | ┃┃ ┣╸y[3] ≥ x[1,3] 40 | ┃┃ ┛ 41 | ┃┃ ┓ 42 | ┃┃ ┃ 43 | ┃┃ ┣╸y[4] ≥ x[1,4] 44 | ┃┛ ┛ 45 | 46 | 47 | Free Variables 48 | -------------- 49 | | Yum1.Cake 50 | y : [ 3 3 3 3 3 ] 51 | 52 | Fixed Variables 53 | --------------- 54 | | Yum1.Cake.Pie 55 | x : [ 2 2 2 2 2 56 | 3 3 3 3 3 ] 57 | z : [ 1 1 1 1 1 58 | 1 1 1 1 1 ] 59 | 60 | Variable Sensitivities 61 | ---------------------- 62 | | Yum1.Cake.Pie 63 | x : [ +7.1e-07 +7.1e-07 +7.1e-07 +7.1e-07 +7.1e-07 64 | +0.2 +0.2 +0.2 +0.2 +0.2 ] 65 | 66 | Most Sensitive Constraints 67 | -------------------------- 68 | | Yum1.Cake 69 | +0.2 : y[0] ≥ x[1,0] 70 | +0.2 : y[1] ≥ x[1,1] 71 | +0.2 : y[2] ≥ x[1,2] 72 | +0.2 : y[3] ≥ x[1,3] 73 | +0.2 : y[4] ≥ x[1,4] 74 | 75 | 76 | ┃┓ 77 | Cost╺┫┃ 78 | (3) ┃┣╸x[1,0] 79 | ┃┛ (3, fixed) 80 | 81 | 82 | 83 | ┃┓ 84 | Model╺┫┃ 85 | ┃┣╸y[0] ≥ x[1,0] 86 | ┃┛ 87 | 88 | 89 | Free Variables 90 | -------------- 91 | | Yum2.Cake 92 | y : [ 3 ] 93 | 94 | Fixed Variables 95 | --------------- 96 | | Yum2.Cake.Pie 97 | x : [ 2 3 ] 98 | z : [ 1 1 ] 99 | 100 | Variable Sensitivities 101 | ---------------------- 102 | | Yum2.Cake.Pie 103 | x : [ +8.4e-08 +1 ] 104 | 105 | Most Sensitive Constraints 106 | -------------------------- 107 | | Yum2.Cake 108 | +1 : y[0] ≥ x[1,0] 109 | 110 | -------------------------------------------------------------------------------- /docs/source/examples/last_verified.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/last_verified.pkl -------------------------------------------------------------------------------- /docs/source/examples/last_verified.sol: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/last_verified.sol -------------------------------------------------------------------------------- /docs/source/examples/loose_constraintsets.py: -------------------------------------------------------------------------------- 1 | "Example Loose ConstraintSet usage" 2 | from gpkit import Variable, Model 3 | from gpkit.constraints.loose import Loose 4 | 5 | Loose.reltol = 1e-4 # set the global tolerance of Loose 6 | x = Variable('x') 7 | x_min = Variable('x_{min}', 1) 8 | m = Model(x, [Loose([x >= 2], senstol=1e-4), # set the specific tolerance 9 | x >= x_min]) 10 | m.solve(verbosity=0) # prints warning 11 | -------------------------------------------------------------------------------- /docs/source/examples/loose_constraintsets_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/loose_constraintsets_output.txt -------------------------------------------------------------------------------- /docs/source/examples/migp.py: -------------------------------------------------------------------------------- 1 | "Example choice variable usage" 2 | import numpy as np 3 | from gpkit import Variable, Model 4 | 5 | x = Variable("x", choices=range(1, 4)) 6 | num = Variable("numerator", np.linspace(0.5, 7, 11)) 7 | 8 | m = Model(x + num/x) 9 | sol = m.solve(verbosity=0) 10 | 11 | print(sol.table()) 12 | -------------------------------------------------------------------------------- /docs/source/examples/migp_output.txt: -------------------------------------------------------------------------------- 1 | 2 | Optimal Cost 3 | ------------ 4 | [ 1.41 2.14 2.68 3.13 ... ] 5 | 6 | ~~~~~~~~ 7 | WARNINGS 8 | ~~~~~~~~ 9 | Freed Choice Variables 10 | ---------------------- 11 | This model has the discretized choice variables [x], but since the 'cvxopt' solver doesn't support discretization they were treated as continuous variables. 12 | ~~~~~~~~ 13 | 14 | Swept Variables 15 | --------------- 16 | numerator : [ 0.5 17 | 1.15 18 | 1.8 19 | 2.45 20 | 3.1 21 | 3.75 22 | 4.4 23 | 5.05 24 | 5.7 25 | 6.35 26 | 7 ] 27 | 28 | Free Variables 29 | -------------- 30 | x : [ 0.707 31 | 1.07 32 | 1.34 33 | 1.57 34 | 1.76 35 | 1.94 36 | 2.1 37 | 2.25 38 | 2.39 39 | 2.52 40 | 2.65 ] 41 | 42 | Variable Sensitivities 43 | ---------------------- 44 | numerator : [ +0.5 45 | +0.5 46 | +0.5 47 | +0.5 48 | +0.5 49 | +0.5 50 | +0.5 51 | +0.5 52 | +0.5 53 | +0.5 54 | +0.5 ] 55 | 56 | Most Sensitive Constraints (in last sweep) 57 | ------------------------------------------ 58 | (none) 59 | 60 | -------------------------------------------------------------------------------- /docs/source/examples/model_var_access.py: -------------------------------------------------------------------------------- 1 | "Demo of accessing variables in models" 2 | from gpkit import Model, Variable 3 | 4 | 5 | class Battery(Model): 6 | """A simple battery 7 | 8 | Upper Unbounded 9 | --------------- 10 | m 11 | 12 | Lower Unbounded 13 | --------------- 14 | E 15 | 16 | """ 17 | def setup(self): 18 | h = Variable("h", 200, "Wh/kg", "specific energy") 19 | E = self.E = Variable("E", "MJ", "stored energy") 20 | m = self.m = Variable("m", "lb", "battery mass") 21 | return [E <= m*h] 22 | 23 | 24 | class Motor(Model): 25 | """Electric motor 26 | 27 | Upper Unbounded 28 | --------------- 29 | m 30 | 31 | Lower Unbounded 32 | --------------- 33 | Pmax 34 | 35 | """ 36 | def setup(self): 37 | m = self.m = Variable("m", "lb", "motor mass") 38 | f = Variable("f", 20, "lb/hp", "mass per unit power") 39 | Pmax = self.Pmax = Variable("P_{max}", "hp", "max output power") 40 | return [m >= f*Pmax] 41 | 42 | 43 | class PowerSystem(Model): 44 | """A battery powering a motor 45 | 46 | Upper Unbounded 47 | --------------- 48 | m 49 | 50 | Lower Unbounded 51 | --------------- 52 | E, Pmax 53 | 54 | """ 55 | def setup(self): 56 | battery, motor = Battery(), Motor() 57 | components = [battery, motor] 58 | m = self.m = Variable("m", "lb", "mass") 59 | self.E = battery.E 60 | self.Pmax = motor.Pmax 61 | 62 | return [components, 63 | m >= sum(comp.m for comp in components)] 64 | 65 | PS = PowerSystem() 66 | print("Getting the only var 'E': %s" % PS["E"]) 67 | print("The top-level var 'm': %s" % PS.m) 68 | print("All the variables 'm': %s" % PS.variables_byname("m")) 69 | -------------------------------------------------------------------------------- /docs/source/examples/model_var_access_output.txt: -------------------------------------------------------------------------------- 1 | Getting the only var 'E': PowerSystem.Battery.E [MJ] 2 | The top-level var 'm': PowerSystem.m [lb] 3 | All the variables 'm': [gpkit.Variable(PowerSystem.Battery.m [lb]), gpkit.Variable(PowerSystem.Motor.m [lb]), gpkit.Variable(PowerSystem.m [lb])] 4 | -------------------------------------------------------------------------------- /docs/source/examples/performance_modeling_output.txt: -------------------------------------------------------------------------------- 1 | 2 | Cost Function 3 | ------------- 4 | Wfuel[0] 5 | 6 | Constraints 7 | ----------- 8 | Mission 9 | "fuel constraints": 10 | Wfuel[:-1] ≥ Wfuel[1:] + Wburn[:-1] 11 | Wfuel[3] ≥ Wburn[3] 12 | 13 | FlightSegment 14 | AircraftP 15 | Wburn[:] ≥ 0.1·D[:] 16 | Aircraft.W + Wfuel[:] ≤ 0.5·Mission.FlightSegment.FlightState.rho[:]·CL[:]·S·V[:]² 17 | "performance": 18 | WingAero 19 | D[:] ≥ 0.5·Mission.FlightSegment.FlightState.rho[:]·V[:]²·CD[:]·S 20 | Re[:] = Mission.FlightSegment.FlightState.rho[:]·V[:]·c/mu[:] 21 | CD[:] ≥ 0.074/Re[:]^0.2 + CL[:]²/π/A/e[:] 22 | 23 | FlightState 24 | (no constraints) 25 | 26 | Aircraft 27 | Aircraft.W ≥ Fuselage.W + Wing.W 28 | Fuselage 29 | (no constraints) 30 | 31 | Wing 32 | c = (S/A)^0.5 33 | Wing.W ≥ S·Wing.rho 34 | 35 | ┃┓ ┓ ┓ ┓ ┓ 36 | ┃┃ ┃ ┃ ┃ ┃ 37 | ┃┃ ┃ ┃ ┣╸Wburn[2] ┣╸CD[2]╶⎨ 38 | ┃┃ ┃ ┃ ┃ (0.272lbf) ┃ (0.0189) 39 | ┃┃ ┃ ┃ ┛ ┛ 40 | ┃┃ ┃ ┣╸Wfuel[2] ┓ ┓ 41 | ┃┃ ┃ ┃ (0.544lbf) ┃ ┃ 42 | ┃┃ ┣╸Wfuel[1] ┃ ┣╸Wfuel[3] ┣╸CD[3]╶⎨ 43 | ┃┃ ┃ (0.817lbf) ┃ ┃ (0.272lbf) ┃ (0.0188) 44 | Cost╺┫┃ ┃ ┛ ┛ ┛ 45 | (1.09lbf) ┃┣╸Wfuel[0] ┃ ┓ ┓ ┓ 46 | ┃┃ (1.09lbf) ┃ ┃ ┃ ┣╸CL[1]² 47 | ┃┃ ┃ ┣╸Wburn[1] ┣╸CD[1] ┛ (1.01) 48 | ┃┃ ┃ ┃ (0.273lbf) ┃ (0.0189) ┣╸1/Re[1]^0.2 49 | ┃┃ ┛ ┛ ┛ ┛ (0.0772) 50 | ┃┃ ┓ ┓ ┓ 51 | ┃┃ ┃ ┃ ┣╸CL[0]² 52 | ┃┃ ┣╸Wburn[0] ┣╸CD[0] ┛ (1.01) 53 | ┃┃ ┃ (0.274lbf) ┃ (0.019) ┣╸1/Re[0]^0.2 54 | ┃┛ ┛ ┛ ┛ (0.0772) 55 | 56 | 57 | 58 | ┃┓ ┓ ┓ 59 | ┃┃ ┃ ┃ 60 | ┃┃ ┃ ┃ 61 | ┃┃ ┃ ┃ 62 | ┃┃ ┣╸FlightSegment ┣╸AircraftP╶⎨ 63 | ┃┃ ┃ ┃ 64 | ┃┣╸Mission ┃ ┃ 65 | ┃┃ ┃ ┃ 66 | ┃┃ ┛ ┛ 67 | Model╺┫┃ ┣╸Wfuel[0] ≥ Wfuel[1] + Wburn[0] 68 | ┃┃ ┛ 69 | ┃┃ ┣╸Wfuel[1] ≥ Wfuel[2] + Wburn[1] 70 | ┃┛ ┣╸Wfuel[2] ≥ Wfuel[3] + Wburn[2] 71 | ┃┓ ┓ 72 | ┃┃ ┣╸Wing╶⎨ 73 | ┃┃ ┛ 74 | ┃┣╸Aircraft ┣╸W ≥ Fuselage.W + Wing.W 75 | ┃┃ ┛ 76 | ┃┃ ┣╸Fuselage ┣╸W = 100lbf 77 | ┃┛ ┛ ┛ 78 | 79 | 80 | Free Variables 81 | -------------- 82 | | Aircraft 83 | W : 144.1 [lbf] weight 84 | 85 | | Aircraft.Wing 86 | S : 44.14 [ft²] surface area 87 | W : 44.14 [lbf] weight 88 | c : 1.279 [ft] mean chord 89 | 90 | | Mission.FlightSegment.AircraftP 91 | Wburn : [ 0.274 0.273 0.272 0.272 ] [lbf] segment fuel burn 92 | Wfuel : [ 1.09 0.817 0.544 0.272 ] [lbf] fuel weight 93 | 94 | | Mission.FlightSegment.AircraftP.WingAero 95 | D : [ 2.74 2.73 2.72 2.72 ] [lbf] drag force 96 | 97 | Insensitive Constraints |below +1e-05| 98 | -------------------------------------- 99 | (none) 100 | 101 | Solution Diff (for selected variables) 102 | ====================================== 103 | (argument is the baseline solution) 104 | 105 | Constraint Differences 106 | ********************** 107 | @@ -31,3 +31,4 @@ 108 | Wing 109 | c = (S/A)^0.5 110 | Wing.W ≥ S·Wing.rho 111 | + Wburn[:] ≥ 0.2·D[:] 112 | 113 | ********************** 114 | 115 | Relative Differences |above 1%| 116 | ------------------------------- 117 | Wburn : [ +102.1% +101.6% +101.1% +100.5% ] segment fuel burn 118 | Wfuel : [ +101.3% +101.1% +100.8% +100.5% ] fuel weight 119 | D : [ +1.1% - - - ] drag force 120 | 121 | -------------------------------------------------------------------------------- /docs/source/examples/plot_autosweep1d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/plot_autosweep1d.png -------------------------------------------------------------------------------- /docs/source/examples/plot_sweep1d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/plot_sweep1d.png -------------------------------------------------------------------------------- /docs/source/examples/plot_sweep1d.py: -------------------------------------------------------------------------------- 1 | "Demonstrates manual and auto sweeping and plotting" 2 | import matplotlib as mpl 3 | mpl.use('Agg') 4 | # comment out the lines above to show figures in a window 5 | import numpy as np 6 | from gpkit import Model, Variable, units 7 | from gpkit.constraints.tight import Tight 8 | 9 | x = Variable("x", "m", "Swept Variable") 10 | y = Variable("y", "m^2", "Cost") 11 | m = Model(y, [ 12 | y >= (x/2)**-0.5 * units.m**2.5 + 1*units.m**2, 13 | Tight([y >= (x/2)**2]) 14 | ]) 15 | 16 | # arguments are: model, swept: values, posnomial for y-axis 17 | sol = m.sweep({x: np.linspace(1, 3, 20)}, verbosity=0) 18 | f, ax = sol.plot(y) 19 | ax.set_title("Manually swept (20 points)") 20 | f.show() 21 | f.savefig("plot_sweep1d.png") 22 | sol.save() 23 | 24 | # arguments are: model, swept: (min, max, optional logtol), posnomial for y-axis 25 | sol = m.autosweep({x: (1, 3)}, tol=0.001, verbosity=0) 26 | f, ax = sol.plot(y) 27 | ax.set_title("Autoswept (7 points)\nGuaranteed to be in blue region") 28 | f.show() 29 | f.savefig("plot_autosweep1d.png") 30 | -------------------------------------------------------------------------------- /docs/source/examples/plot_sweep1d_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/plot_sweep1d_output.txt -------------------------------------------------------------------------------- /docs/source/examples/primal_infeasible_ex1.py: -------------------------------------------------------------------------------- 1 | "A simple primal infeasible example" 2 | from gpkit import Variable, Model 3 | 4 | x = Variable("x") 5 | y = Variable("y") 6 | 7 | m = Model(x*y, [ 8 | x >= 1, 9 | y >= 2, 10 | x*y >= 0.5, 11 | x*y <= 1.5 12 | ]) 13 | 14 | # raises UnknownInfeasible on cvxopt, PrimalInfeasible on mosek 15 | # m.solve() 16 | -------------------------------------------------------------------------------- /docs/source/examples/primal_infeasible_ex1_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/primal_infeasible_ex1_output.txt -------------------------------------------------------------------------------- /docs/source/examples/primal_infeasible_ex2.py: -------------------------------------------------------------------------------- 1 | "Another simple primal infeasible example" 2 | from gpkit import Variable, Model 3 | 4 | x = Variable("x") 5 | y = Variable("y", 2) 6 | 7 | constraints = [ 8 | x >= 1, 9 | 0.5 <= x*y, 10 | x*y <= 1.5 11 | ] 12 | 13 | objective = x*y 14 | m = Model(objective, constraints) 15 | 16 | # raises UnknownInfeasible on cvxopt and PrimalInfeasible on mosek 17 | # m.solve() 18 | -------------------------------------------------------------------------------- /docs/source/examples/primal_infeasible_ex2_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/primal_infeasible_ex2_output.txt -------------------------------------------------------------------------------- /docs/source/examples/relaxation.py: -------------------------------------------------------------------------------- 1 | "Relaxation examples" 2 | 3 | from gpkit import Variable, Model 4 | 5 | x = Variable("x") 6 | x_min = Variable("x_min", 2) 7 | x_max = Variable("x_max", 1) 8 | m = Model(x, [x <= x_max, x >= x_min]) 9 | print("Original model") 10 | print("==============") 11 | print(m) 12 | print("") 13 | # m.solve() # raises a RuntimeWarning! 14 | 15 | print("With constraints relaxed equally") 16 | print("================================") 17 | 18 | from gpkit.constraints.relax import ConstraintsRelaxedEqually 19 | 20 | allrelaxed = ConstraintsRelaxedEqually(m) 21 | mr1 = Model(allrelaxed.relaxvar, allrelaxed) 22 | print(mr1) 23 | print(mr1.solve(verbosity=0).table()) # solves with an x of 1.414 24 | from gpkit.breakdowns import Breakdowns 25 | Breakdowns(mr1.solution).trace("cost") 26 | print("") 27 | 28 | print("With constraints relaxed individually") 29 | print("=====================================") 30 | 31 | from gpkit.constraints.relax import ConstraintsRelaxed 32 | 33 | constraintsrelaxed = ConstraintsRelaxed(m) 34 | mr2 = Model(constraintsrelaxed.relaxvars.prod() * m.cost**0.01, 35 | # add a bit of the original cost in 36 | constraintsrelaxed) 37 | print(mr2) 38 | print(mr2.solve(verbosity=0).table()) # solves with an x of 1.0 39 | print("") 40 | 41 | print("With constants relaxed individually") 42 | print("===================================") 43 | 44 | from gpkit.constraints.relax import ConstantsRelaxed 45 | 46 | constantsrelaxed = ConstantsRelaxed(m) 47 | mr3 = Model(constantsrelaxed.relaxvars.prod() * m.cost**0.01, 48 | # add a bit of the original cost in 49 | constantsrelaxed) 50 | print(mr3) 51 | print(mr3.solve(verbosity=0).table()) # brings x_min down to 1.0 52 | print("") 53 | -------------------------------------------------------------------------------- /docs/source/examples/simple_box.py: -------------------------------------------------------------------------------- 1 | "Maximizes box volume given area and aspect ratio constraints." 2 | from gpkit import Variable, Model 3 | 4 | # Parameters 5 | alpha = Variable("alpha", 2, "-", "lower limit, wall aspect ratio") 6 | beta = Variable("beta", 10, "-", "upper limit, wall aspect ratio") 7 | gamma = Variable("gamma", 2, "-", "lower limit, floor aspect ratio") 8 | delta = Variable("delta", 10, "-", "upper limit, floor aspect ratio") 9 | A_wall = Variable("A_{wall}", 200, "m^2", "upper limit, wall area") 10 | A_floor = Variable("A_{floor}", 50, "m^2", "upper limit, floor area") 11 | 12 | # Decision variables 13 | h = Variable("h", "m", "height") 14 | w = Variable("w", "m", "width") 15 | d = Variable("d", "m", "depth") 16 | 17 | # Constraints 18 | constraints = [A_wall >= 2*h*w + 2*h*d, 19 | A_floor >= w*d, 20 | h/w >= alpha, 21 | h/w <= beta, 22 | d/w >= gamma, 23 | d/w <= delta] 24 | 25 | # Objective function 26 | V = h*w*d 27 | objective = 1/V # To maximize V, we minimize its reciprocal 28 | 29 | # Formulate the Model 30 | m = Model(objective, constraints) 31 | 32 | # Solve the Model and print the results table 33 | print(m.solve(verbosity=0).table()) 34 | -------------------------------------------------------------------------------- /docs/source/examples/simple_box_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃/┓ 3 | Cost╺┫ ┃ 4 | (0.00367/m³) ┃/┣╸alpha 5 | ┃/┛ (2, fixed) 6 | 7 | 8 | 9 | ┃┓ 10 | ┃┃ 11 | ┃┃ 12 | ┃┣╸A_{wall} = 200m² 13 | ┃┃ 14 | ┃┛ 15 | ┃┓ 16 | Model╺┫┃ 17 | ┃┃ 18 | ┃┣╸A_{wall} ≥ 2·h·w + 2·h·d 19 | ┃┃ 20 | ┃┛ 21 | ┃┣╸alpha = 2 22 | ┃┛ 23 | ┃┣╸alpha ≤ h/w 24 | ┃┛ 25 | 26 | 27 | Free Variables 28 | -------------- 29 | d : 8.17 [m] depth 30 | h : 8.163 [m] height 31 | w : 4.081 [m] width 32 | 33 | Fixed Variables 34 | --------------- 35 | A_{floor} : 50 [m²] upper limit, floor area 36 | A_{wall} : 200 [m²] upper limit, wall area 37 | alpha : 2 lower limit, wall aspect ratio 38 | beta : 10 upper limit, wall aspect ratio 39 | delta : 10 upper limit, floor aspect ratio 40 | gamma : 2 lower limit, floor aspect ratio 41 | 42 | Variable Sensitivities 43 | ---------------------- 44 | A_{wall} : -1.5 upper limit, wall area 45 | alpha : +0.5 lower limit, wall aspect ratio 46 | 47 | Most Sensitive Constraints 48 | -------------------------- 49 | +1.5 : A_{wall} ≥ 2·h·w + 2·h·d 50 | +0.5 : alpha ≤ h/w 51 | 52 | -------------------------------------------------------------------------------- /docs/source/examples/simple_sp.py: -------------------------------------------------------------------------------- 1 | """Adapted from t_SP in tests/t_geometric_program.py""" 2 | from gpkit import Model, Variable, SignomialsEnabled 3 | 4 | # Decision variables 5 | x = Variable('x') 6 | y = Variable('y') 7 | 8 | # must enable signomials for subtraction 9 | with SignomialsEnabled(): 10 | constraints = [x >= 1-y, y <= 0.1] 11 | 12 | # create and solve the SP 13 | m = Model(x, constraints) 14 | print(m.localsolve(verbosity=0).summary()) 15 | assert abs(m.solution(x) - 0.9) < 1e-6 16 | 17 | # full interim solutions are available 18 | print("x values of each GP solve (note convergence)") 19 | print(", ".join("%.5f" % sol["freevariables"][x] for sol in m.program.results)) 20 | 21 | # use x0 to give the solution, reducing number of GPs needed 22 | m.localsolve(verbosity=0, x0={x: 0.9, y:0.1}) 23 | assert len(m.program.results) == 2 24 | -------------------------------------------------------------------------------- /docs/source/examples/simple_sp_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃┓ 3 | Cost╺┫┃ 4 | (0.9) ┃┣╸1/y^0.1 5 | ┃┛ (1.26) 6 | 7 | 8 | 9 | ┃┓ 10 | ┃┃ 11 | ┃┃ 12 | Model╺┫┣╸1 - y ≤ x 13 | ┃┃ 14 | ┃┃ 15 | ┃┛ 16 | ┃┣╸y ≤ 0.1 17 | 18 | 19 | Free Variables 20 | -------------- 21 | x : 0.9 22 | y : 0.1 23 | 24 | x values of each GP solve (note convergence) 25 | 2.50000, 0.92548, 0.90003, 0.90000 26 | -------------------------------------------------------------------------------- /docs/source/examples/simpleflight.py: -------------------------------------------------------------------------------- 1 | "Minimizes airplane drag for a simple drag and structure model." 2 | import pickle 3 | import numpy as np 4 | from gpkit import Variable, Model, SolutionArray 5 | pi = np.pi 6 | 7 | 8 | # Constants 9 | k = Variable("k", 1.2, "-", "form factor") 10 | e = Variable("e", 0.95, "-", "Oswald efficiency factor") 11 | mu = Variable("\\mu", 1.78e-5, "kg/m/s", "viscosity of air") 12 | rho = Variable("\\rho", 1.23, "kg/m^3", "density of air") 13 | tau = Variable("\\tau", 0.12, "-", "airfoil thickness to chord ratio") 14 | N_ult = Variable("N_{ult}", 3.8, "-", "ultimate load factor") 15 | V_min = Variable("V_{min}", 22, "m/s", "takeoff speed") 16 | C_Lmax = Variable("C_{L,max}", 1.5, "-", "max CL with flaps down") 17 | S_wetratio = Variable("(\\frac{S}{S_{wet}})", 2.05, "-", "wetted area ratio") 18 | W_W_coeff1 = Variable("W_{W_{coeff1}}", 8.71e-5, "1/m", 19 | "Wing Weight Coefficent 1") 20 | W_W_coeff2 = Variable("W_{W_{coeff2}}", 45.24, "Pa", 21 | "Wing Weight Coefficent 2") 22 | CDA0 = Variable("(CDA0)", 0.031, "m^2", "fuselage drag area") 23 | W_0 = Variable("W_0", 4940.0, "N", "aircraft weight excluding wing") 24 | 25 | # Free Variables 26 | D = Variable("D", "N", "total drag force") 27 | A = Variable("A", "-", "aspect ratio") 28 | S = Variable("S", "m^2", "total wing area") 29 | V = Variable("V", "m/s", "cruising speed") 30 | W = Variable("W", "N", "total aircraft weight") 31 | Re = Variable("Re", "-", "Reynold's number") 32 | C_D = Variable("C_D", "-", "Drag coefficient of wing") 33 | C_L = Variable("C_L", "-", "Lift coefficent of wing") 34 | C_f = Variable("C_f", "-", "skin friction coefficient") 35 | W_w = Variable("W_w", "N", "wing weight") 36 | 37 | constraints = [] 38 | 39 | # Drag model 40 | C_D_fuse = CDA0/S 41 | C_D_wpar = k*C_f*S_wetratio 42 | C_D_ind = C_L**2/(pi*A*e) 43 | constraints += [C_D >= C_D_fuse + C_D_wpar + C_D_ind] 44 | 45 | # Wing weight model 46 | W_w_strc = W_W_coeff1*(N_ult*A**1.5*(W_0*W*S)**0.5)/tau 47 | W_w_surf = W_W_coeff2 * S 48 | constraints += [W_w >= W_w_surf + W_w_strc] 49 | 50 | # and the rest of the models 51 | constraints += [D >= 0.5*rho*S*C_D*V**2, 52 | Re <= (rho/mu)*V*(S/A)**0.5, 53 | C_f >= 0.074/Re**0.2, 54 | W <= 0.5*rho*S*C_L*V**2, 55 | W <= 0.5*rho*S*C_Lmax*V_min**2, 56 | W >= W_0 + W_w] 57 | 58 | print("SINGLE\n======") 59 | m = Model(D, constraints) 60 | sol = m.solve(verbosity=0) 61 | print(sol.summary()) 62 | # save solution to a file and retrieve it 63 | sol.save("solution.pkl") 64 | sol.save_compressed("solution.pgz") 65 | print(sol.diff("solution.pkl")) 66 | 67 | print("SWEEP\n=====") 68 | N = 2 69 | sweeps = {V_min: ("sweep", np.linspace(20, 25, N)), 70 | V: ("sweep", np.linspace(45, 55, N)), } 71 | m.substitutions.update(sweeps) 72 | sweepsol = m.solve(verbosity=0) 73 | print(sweepsol.summary()) 74 | sol_loaded = pickle.load(open("solution.pkl", "rb")) 75 | assert sol_loaded.almost_equal(SolutionArray.decompress_file("solution.pgz")) 76 | print(sweepsol.diff(sol_loaded, absdiff=True, senssdiff=True)) 77 | -------------------------------------------------------------------------------- /docs/source/examples/sin_approx_example.py: -------------------------------------------------------------------------------- 1 | "Can be found in gpkit/docs/source/examples/sin_approx_example.py" 2 | import numpy as np 3 | from gpkit import Variable, Model 4 | 5 | 6 | x = Variable("x") 7 | y = Variable("y") 8 | 9 | objective = y 10 | 11 | constraints = [y >= x, 12 | x <= np.pi/2, 13 | x >= np.pi/4, 14 | ] 15 | 16 | m = Model(objective, constraints) 17 | print(m.solve(verbosity=0).summary()) 18 | -------------------------------------------------------------------------------- /docs/source/examples/sin_approx_example_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃┓ 3 | Cost╺┫┃ 4 | (0.785) ┃┣╸0.785 5 | ┃┛ 6 | 7 | 8 | 9 | ┃┓ 10 | ┃┃ 11 | ┃┣╸x ≥ 0.785 12 | Model╺┫┛ 13 | ┃┓ 14 | ┃┃ 15 | ┃┣╸y ≥ x 16 | ┃┛ 17 | 18 | 19 | Free Variables 20 | -------------- 21 | x : 0.7854 22 | y : 0.7854 23 | 24 | -------------------------------------------------------------------------------- /docs/source/examples/solar.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/solar.p -------------------------------------------------------------------------------- /docs/source/examples/solar_10.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/solar_10.p -------------------------------------------------------------------------------- /docs/source/examples/solar_12.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/solar_12.p -------------------------------------------------------------------------------- /docs/source/examples/solar_13.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/solar_13.p -------------------------------------------------------------------------------- /docs/source/examples/sp_to_gp_sweep.py: -------------------------------------------------------------------------------- 1 | "example of an SP that turns into a GP" 2 | import numpy as np 3 | from gpkit import Model, Variable, SignomialsEnabled, units 4 | from gpkit.constraints.tight import Tight 5 | 6 | 7 | def SimPleAC(): 8 | "Creates SimpleAC model" 9 | # Env. constants 10 | g = Variable("g", 9.81, "m/s^2", "gravitational acceleration") 11 | mu = Variable("\\mu", 1.775e-5, "kg/m/s", "viscosity of air") 12 | rho = Variable("\\rho", 1.23, "kg/m^3", "density of air") 13 | rho_f = Variable("\\rho_f", 817, "kg/m^3", "density of fuel") 14 | 15 | # Non-dimensional constants 16 | C_Lmax = Variable("C_{L,max}", 1.6, "-", "max CL with flaps down") 17 | e = Variable("e", 0.92, "-", "Oswald efficiency factor") 18 | k = Variable("k", 1.17, "-", "form factor") 19 | N_ult = Variable("N_{ult}", 3.3, "-", "ultimate load factor") 20 | S_wetratio = Variable("(\\frac{S}{S_{wet}})", 2.075, "-", 21 | "wetted area ratio") 22 | tau = Variable("\\tau", 0.12, "-", "airfoil thickness to chord ratio") 23 | W_W_coeff1 = Variable("W_{W_{coeff1}}", 2e-5, "1/m", 24 | "wing weight coefficent 1") # 12e-5 originally 25 | W_W_coeff2 = Variable("W_{W_{coeff2}}", 60, "Pa", 26 | "wing weight coefficent 2") 27 | 28 | # Dimensional constants 29 | Range = Variable("Range", 3000, "km", "aircraft range") 30 | TSFC = Variable("TSFC", 0.6, "1/hr", "thrust specific fuel consumption") 31 | V_min = Variable("V_{min}", 25, "m/s", "takeoff speed") 32 | W_0 = Variable("W_0", 6250, "N", "aircraft weight excluding wing") 33 | 34 | # Free Variables 35 | LoD = Variable("L/D", "-", "lift-to-drag ratio") 36 | D = Variable("D", "N", "total drag force") 37 | V = Variable("V", "m/s", "cruising speed") 38 | W = Variable("W", "N", "total aircraft weight") 39 | Re = Variable("Re", "-", "Reynold's number") 40 | CDA0 = Variable("(CDA0)", "m^2", "fuselage drag area") # 0.035 originally 41 | C_D = Variable("C_D", "-", "drag coefficient") 42 | C_L = Variable("C_L", "-", "lift coefficient of wing") 43 | C_f = Variable("C_f", "-", "skin friction coefficient") 44 | W_f = Variable("W_f", "N", "fuel weight") 45 | V_f = Variable("V_f", "m^3", "fuel volume") 46 | V_f_avail = Variable("V_{f_{avail}}", "m^3", "fuel volume available") 47 | T_flight = Variable("T_{flight}", "hr", "flight time") 48 | 49 | # Free variables (fixed for performance eval.) 50 | A = Variable("A", "-", "aspect ratio") 51 | S = Variable("S", "m^2", "total wing area") 52 | W_w = Variable("W_w", "N", "wing weight") 53 | W_w_strc = Variable("W_w_strc", "N", "wing structural weight") 54 | W_w_surf = Variable("W_w_surf", "N", "wing skin weight") 55 | V_f_wing = Variable("V_f_wing", "m^3", "fuel volume in the wing") 56 | V_f_fuse = Variable("V_f_fuse", "m^3", "fuel volume in the fuselage") 57 | 58 | objective = W_f 59 | 60 | constraints = [] 61 | 62 | # Weight and lift model 63 | constraints += [ 64 | W >= W_0 + W_w + W_f, 65 | W_0 + W_w + 0.5 * W_f <= 0.5 * rho * S * C_L * V ** 2, 66 | W <= 0.5 * rho * S * C_Lmax * V_min ** 2, 67 | T_flight >= Range / V, 68 | LoD == C_L/C_D] 69 | 70 | # Thrust and drag model 71 | C_D_fuse = CDA0 / S 72 | C_D_wpar = k * C_f * S_wetratio 73 | C_D_ind = C_L ** 2 / (np.pi * A * e) 74 | constraints += [ 75 | W_f >= TSFC * T_flight * D, 76 | D >= 0.5 * rho * S * C_D * V ** 2, 77 | C_D >= C_D_fuse + C_D_wpar + C_D_ind, 78 | V_f_fuse <= 10*units("m")*CDA0, 79 | Re <= (rho / mu) * V * (S / A) ** 0.5, 80 | C_f >= 0.074 / Re ** 0.2] 81 | 82 | # Fuel volume model 83 | with SignomialsEnabled(): 84 | constraints += [ 85 | V_f == W_f / g / rho_f, 86 | # linear with b and tau, quadratic with chord 87 | V_f_wing**2 <= 0.0009*S**3/A*tau**2, 88 | V_f_avail <= V_f_wing + V_f_fuse, # [SP] 89 | Tight([V_f_avail >= V_f])] 90 | 91 | # Wing weight model 92 | constraints += [ 93 | W_w_surf >= W_W_coeff2 * S, 94 | W_w_strc**2 >= W_W_coeff1**2/tau**2 * N_ult**2*A**3*(V_f_fuse*g*rho_f 95 | + W_0)*W*S, 96 | W_w >= W_w_surf + W_w_strc] 97 | 98 | m = Model(objective, constraints) 99 | 100 | return m 101 | 102 | 103 | sa = SimPleAC() 104 | sa.substitutions.update({"V_f_wing": ("sweep", np.linspace(0.1, 0.5, 3)), 105 | "V_f_fuse": 0.5}) 106 | sol = sa.solve(verbosity=0) 107 | print(sol.summary()) 108 | -------------------------------------------------------------------------------- /docs/source/examples/sp_to_gp_sweep_output.txt: -------------------------------------------------------------------------------- 1 | 2 | Optimal Cost 3 | ------------ 4 | [ 4.63e+03 6.23e+03 7.36e+03 ] 5 | 6 | ~~~~~~~~ 7 | WARNINGS 8 | ~~~~~~~~ 9 | Unexpectedly Loose Constraints in sweep 0 10 | ----------------------------------------- 11 | 0.5886 >= 0.5775 : V_{f_{avail}} ≥ V_f 12 | 13 | Unexpectedly Loose Constraints in sweep 1 14 | ----------------------------------------- 15 | 0.7884 >= 0.7769 : V_{f_{avail}} ≥ V_f 16 | 17 | Unexpectedly Loose Constraints in sweep 2 18 | ----------------------------------------- 19 | 0.9585 >= 0.9187 : V_{f_{avail}} ≥ V_f 20 | ~~~~~~~~ 21 | 22 | Swept Variables 23 | --------------- 24 | V_f_wing : [ 0.1 0.3 0.5 ] [m³] fuel volume in the wing 25 | 26 | Free Variables 27 | -------------- 28 | (CDA0) : [ 0.05 0.05 0.05 ] [m²] fuselage drag area 29 | A : [ 12.4 3.78 2.35 ] aspect ratio 30 | C_D : [ 0.0136 0.011 0.0099 ] drag coefficient 31 | C_L : [ 0.327 0.162 0.121 ] lift coefficient of wing 32 | C_f : [ 0.00343 0.00284 0.00261 ] skin friction coefficient 33 | D : [ 466 774 1e+03 ] [N] total drag force 34 | L/D : [ 24.1 14.8 12.2 ] lift-to-drag ratio 35 | Re : [ 4.64e+06 1.21e+07 1.83e+07 ] Reynold's number 36 | S : [ 22 29.7 35.6 ] [m²] total wing area 37 | T_{flight} : [ 16.6 13.4 12.3 ] [hr] flight time 38 | V : [ 50.3 62.1 67.9 ] [m/s] cruising speed 39 | V_f : [ 0.577 0.777 0.919 ] [m³] fuel volume 40 | V_{f_{avail}} : [ 0.589 0.788 0.958 ] [m³] fuel volume available 41 | W : [ 1.35e+04 1.45e+04 1.59e+04 ] [N] total aircraft weight 42 | W_f : [ 4.63e+03 6.23e+03 7.36e+03 ] [N] fuel weight 43 | W_w : [ 2.65e+03 2.05e+03 2.29e+03 ] [N] wing weight 44 | W_w_strc : [ 1.33e+03 269 151 ] [N] wing structural weight 45 | W_w_surf : [ 1.32e+03 1.78e+03 2.14e+03 ] [N] wing skin weight 46 | 47 | -------------------------------------------------------------------------------- /docs/source/examples/sub_multi_values.py: -------------------------------------------------------------------------------- 1 | "Example substitution; adapted from t_sub.py/t_NomialSubs/test_Vector" 2 | from gpkit import Variable, VectorVariable 3 | x = Variable("x") 4 | y = Variable("y") 5 | z = VectorVariable(2, "z") 6 | p = x*y*z 7 | assert all(p.sub({x: 1, "y": 2}) == 2*z) 8 | assert all(p.sub({x: 1, y: 2, "z": [1, 2]}) == z.sub({z: [2, 4]})) 9 | -------------------------------------------------------------------------------- /docs/source/examples/sub_multi_values_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/sub_multi_values_output.txt -------------------------------------------------------------------------------- /docs/source/examples/substitutions.py: -------------------------------------------------------------------------------- 1 | "Example substitution; adapted from t_sub.py/t_NomialSubs /test_Basic" 2 | from gpkit import Variable 3 | x = Variable("x") 4 | p = x**2 5 | assert p.sub({x: 3}) == 9 6 | assert p.sub({x.key: 3}) == 9 7 | assert p.sub({"x": 3}) == 9 8 | -------------------------------------------------------------------------------- /docs/source/examples/substitutions_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/substitutions_output.txt -------------------------------------------------------------------------------- /docs/source/examples/tight_constraintsets.py: -------------------------------------------------------------------------------- 1 | "Example Tight ConstraintSet usage" 2 | from gpkit import Variable, Model 3 | from gpkit.constraints.tight import Tight 4 | 5 | Tight.reltol = 1e-2 # set the global tolerance of Tight 6 | x = Variable('x') 7 | x_min = Variable('x_{min}', 2) 8 | m = Model(x, [Tight([x >= 1], reltol=1e-3), # set the specific tolerance 9 | x >= x_min]) 10 | m.solve(verbosity=0) # prints warning 11 | -------------------------------------------------------------------------------- /docs/source/examples/tight_constraintsets_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/tight_constraintsets_output.txt -------------------------------------------------------------------------------- /docs/source/examples/treemap.py: -------------------------------------------------------------------------------- 1 | "Treemap example" 2 | import plotly # pylint: disable=unused-import 3 | from gpkit.interactive.plotting import treemap 4 | from performance_modeling import M 5 | 6 | fig = treemap(M) 7 | # plotly.offline.plot(fig, filename="treemap.html") # uncomment to show 8 | 9 | fig = treemap(M, itemize="constraints", sizebycount=True) 10 | # plotly.offline.plot(fig, filename="sizedtreemap.html") # uncomment to show 11 | -------------------------------------------------------------------------------- /docs/source/examples/treemap_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/treemap_output.txt -------------------------------------------------------------------------------- /docs/source/examples/unbounded.py: -------------------------------------------------------------------------------- 1 | "Demonstrate a trivial unbounded variable" 2 | from gpkit import Variable, Model 3 | from gpkit.constraints.bounded import Bounded 4 | 5 | x = Variable("x") 6 | 7 | constraints = [x >= 1] 8 | 9 | m = Model(1/x, constraints) # MOSEK returns DUAL_INFEAS_CER on .solve() 10 | m = Model(1/x, Bounded(constraints)) 11 | # by default, prints bounds warning during solve 12 | sol = m.solve(verbosity=0) 13 | print(sol.summary()) 14 | # but they can also be accessed from the solution: 15 | assert (sol["boundedness"]["value near upper bound of 1e+30"] 16 | == sol["boundedness"]["sensitive to upper bound of 1e+30"]) 17 | -------------------------------------------------------------------------------- /docs/source/examples/unbounded_output.txt: -------------------------------------------------------------------------------- 1 | 2 | ┃┓ 3 | Cost╺┫┃ 4 | (1e-30) ┃┣╸1/x 5 | ┃┛ (1e-30) 6 | 7 | 8 | 9 | ┃┓ 10 | Model╺┫┃ 11 | ┃┣╸x ≤ 1e+30 12 | ┃┛ 13 | 14 | 15 | ~~~~~~~~ 16 | WARNINGS 17 | ~~~~~~~~ 18 | Arbitrarily Bounded Variables 19 | ----------------------------- 20 | value near upper bound of 1e+30: x 21 | sensitive to upper bound of 1e+30: x 22 | ~~~~~~~~ 23 | 24 | Free Variables 25 | -------------- 26 | x : 1e+30 27 | 28 | -------------------------------------------------------------------------------- /docs/source/examples/vectorization.py: -------------------------------------------------------------------------------- 1 | "Example Vectorize usage, from gpkit/tests/t_vars.py" 2 | from gpkit import Variable, Vectorize, VectorVariable 3 | 4 | with Vectorize(3): 5 | with Vectorize(5): 6 | y = Variable("y") 7 | x = VectorVariable(2, "x") 8 | z = VectorVariable(7, "z") 9 | 10 | assert(y.shape == (5, 3)) 11 | assert(x.shape == (2, 5, 3)) 12 | assert(z.shape == (7, 3)) 13 | -------------------------------------------------------------------------------- /docs/source/examples/vectorization_output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/examples/vectorization_output.txt -------------------------------------------------------------------------------- /docs/source/examples/vectorize.py: -------------------------------------------------------------------------------- 1 | "Vectorization demonstration" 2 | from gpkit import Model, Variable, Vectorize 3 | 4 | class Test(Model): 5 | """A simple scalar model 6 | 7 | Upper Unbounded 8 | --------------- 9 | x 10 | """ 11 | def setup(self): 12 | x = self.x = Variable("x") 13 | return [x >= 1] 14 | 15 | print("SCALAR") 16 | m = Test() 17 | m.cost = m["x"] 18 | print(m.solve(verbosity=0).summary()) 19 | 20 | print("__________\n") 21 | print("VECTORIZED") 22 | with Vectorize(3): 23 | m = Test() 24 | m.cost = m["x"].prod() 25 | m.append(m["x"][1] >= 2) 26 | print(m.solve(verbosity=0).summary()) 27 | -------------------------------------------------------------------------------- /docs/source/examples/vectorize_output.txt: -------------------------------------------------------------------------------- 1 | SCALAR 2 | 3 | ┃┓ 4 | Cost╺┫┃ 5 | (1) ┃┣╸1 6 | ┃┛ 7 | 8 | 9 | 10 | ┃┓ 11 | Model╺┫┃ 12 | ┃┣╸x ≥ 1 13 | ┃┛ 14 | 15 | 16 | Free Variables 17 | -------------- 18 | x : 1 19 | 20 | __________ 21 | 22 | VECTORIZED 23 | 24 | ┃┓ 25 | Cost╺┫┃ 26 | (2) ┃┣╸2 27 | ┃┛ 28 | 29 | 30 | 31 | ┃┓ ┓ 32 | ┃┃ ┃ 33 | ┃┃ ┣╸x[0] ≥ 1 34 | ┃┃ ┛ 35 | ┃┣╸Test1 ┓ 36 | Model╺┫┃ ┃ 37 | ┃┃ ┣╸x[2] ≥ 1 38 | ┃┛ ┛ 39 | ┃┓ 40 | ┃┃ 41 | ┃┣╸x[1] ≥ 2 42 | ┃┛ 43 | 44 | 45 | Free Variables 46 | -------------- 47 | x : [ 1 2 1 ] 48 | 49 | -------------------------------------------------------------------------------- /docs/source/examples/water_tank.py: -------------------------------------------------------------------------------- 1 | "Minimizes cylindrical tank surface area for a particular volume." 2 | from gpkit import Variable, VectorVariable, Model 3 | 4 | M = Variable("M", 100, "kg", "Mass of Water in the Tank") 5 | rho = Variable("\\rho", 1000, "kg/m^3", "Density of Water in the Tank") 6 | A = Variable("A", "m^2", "Surface Area of the Tank") 7 | V = Variable("V", "m^3", "Volume of the Tank") 8 | d = VectorVariable(3, "d", "m", "Dimension Vector") 9 | 10 | # because its units are incorrect the line below will print a warning 11 | bad_monomial_equality = (M == V) 12 | 13 | constraints = (A >= 2*(d[0]*d[1] + d[0]*d[2] + d[1]*d[2]), 14 | V == d[0]*d[1]*d[2], 15 | M == V*rho) 16 | 17 | m = Model(A, constraints) 18 | sol = m.solve(verbosity=0) 19 | print(sol.summary()) 20 | -------------------------------------------------------------------------------- /docs/source/examples/water_tank_output.txt: -------------------------------------------------------------------------------- 1 | Infeasible monomial equality: Cannot convert from 'V [m³]' to 'M [kg]' 2 | 3 | ┃┓ ┓ ┓ 4 | ┃┃ ┃ ┃ 5 | ┃┃ ┣╸d[0] ┣╸M 6 | Cost╺┫┃ ┃ (0.464m) ┃ (100kg, fixed) 7 | (1.29m²) ┃┣╸A ┛ ┛ 8 | ┃┃ (1.29m²) ┓ 9 | ┃┃ ┣╸d[1]·d[2] 10 | ┃┛ ┛ (0.215m²) 11 | 12 | 13 | 14 | ┃┓ 15 | ┃┃ 16 | ┃┣╸A ≥ 2·(d[0]·d[1] + d[0]·d[2] + d[1]·d[2]) 17 | ┃┛ 18 | ┃┓ 19 | ┃┃ 20 | ┃┣╸M = 100kg 21 | ┃┛ 22 | ┃┓ 23 | Model╺┫┃ 24 | ┃┣╸M = V·\rho 25 | ┃┛ 26 | ┃┓ 27 | ┃┃ 28 | ┃┣╸V = d[0]·d[1]·d[2] 29 | ┃┛ 30 | ┃┓ 31 | ┃┃ 32 | ┃┣╸\rho = 1,000kg/m³ 33 | ┃┛ 34 | 35 | 36 | Free Variables 37 | -------------- 38 | A : 1.293 [m²] Surface Area of the Tank 39 | V : 0.1 [m³] Volume of the Tank 40 | d : [ 0.464 0.464 0.464 ] [m] Dimension Vector 41 | 42 | -------------------------------------------------------------------------------- /docs/source/examples/x_greaterthan_1.py: -------------------------------------------------------------------------------- 1 | "Very simple problem: minimize x while keeping x greater than 1." 2 | from gpkit import Variable, Model 3 | 4 | # Decision variable 5 | x = Variable("x") 6 | 7 | # Constraint 8 | constraints = [x >= 1] 9 | 10 | # Objective (to minimize) 11 | objective = x 12 | 13 | # Formulate the Model 14 | m = Model(objective, constraints) 15 | 16 | # Solve the Model 17 | sol = m.solve(verbosity=0) 18 | 19 | # print selected results 20 | print("Optimal cost: %.4g" % sol["cost"]) 21 | print("Optimal x val: %.4g" % sol["variables"][x]) 22 | -------------------------------------------------------------------------------- /docs/source/examples/x_greaterthan_1_output.txt: -------------------------------------------------------------------------------- 1 | Optimal cost: 1 2 | Optimal x val: 1 3 | -------------------------------------------------------------------------------- /docs/source/figures/Mission.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/Mission.gif -------------------------------------------------------------------------------- /docs/source/figures/referencesplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/referencesplot.png -------------------------------------------------------------------------------- /docs/source/figures/sankey_autosaves/Mission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sankey_autosaves/Mission.png -------------------------------------------------------------------------------- /docs/source/figures/sankey_autosaves/Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sankey_autosaves/Model.png -------------------------------------------------------------------------------- /docs/source/figures/sankey_autosaves/SolarMission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sankey_autosaves/SolarMission.png -------------------------------------------------------------------------------- /docs/source/figures/sankey_autosaves/SolarMission_Aircraft.Wing.Planform.b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sankey_autosaves/SolarMission_Aircraft.Wing.Planform.b.png -------------------------------------------------------------------------------- /docs/source/figures/sankey_autosaves/SolarMission_CFRPFabric.tmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sankey_autosaves/SolarMission_CFRPFabric.tmin.png -------------------------------------------------------------------------------- /docs/source/figures/sankey_autosaves/SolarMission_Nprop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sankey_autosaves/SolarMission_Nprop.png -------------------------------------------------------------------------------- /docs/source/figures/sankey_autosaves/SolarMission_Wtotal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sankey_autosaves/SolarMission_Wtotal.png -------------------------------------------------------------------------------- /docs/source/figures/sizedconstrainttreemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/sizedconstrainttreemap.png -------------------------------------------------------------------------------- /docs/source/figures/solartest.py: -------------------------------------------------------------------------------- 1 | from solar.solar import * 2 | Vehicle = Aircraft(Npod=3, sp=True) 3 | M = Mission(Vehicle, latitude=[20]) 4 | M.cost = M[M.aircraft.Wtotal] 5 | 6 | M.localsolve().save("solar.p") 7 | -------------------------------------------------------------------------------- /docs/source/figures/treemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/figures/treemap.png -------------------------------------------------------------------------------- /docs/source/gp101.rst: -------------------------------------------------------------------------------- 1 | .. _geometricprogramming: 2 | 3 | Geometric Programming 101 4 | ************************* 5 | 6 | What is a GP? 7 | ============= 8 | 9 | A Geometric Program (GP) is a type of non-linear optimization problem whose objective and constraints have a particular form. 10 | 11 | The decision variables must be strictly positive (non-zero, non-negative) quantities. This is a good fit for engineering design equations (which are often constructed to have only positive quantities), but any model with variables of unknown sign (such as forces and velocities without a predefined direction) may be difficult to express in a GP. Such models might be better expressed as :ref:`Signomials `. 12 | 13 | More precisely, GP objectives and inequalities are formed out of *monomials* and *posynomials*. In the context of GP, a monomial is defined as: 14 | 15 | .. math:: 16 | 17 | f(x) = c x_1^{a_1} x_2^{a_2} ... x_n^{a_n} 18 | 19 | where :math:`c` is a positive constant, :math:`x_{1..n}` are decision variables, and :math:`a_{1..n}` are real exponents. For example, taking :math:`x`, :math:`y` and :math:`z` to be positive variables, the expressions 20 | 21 | .. math:: 22 | 23 | 7x \qquad 4xy^2z \qquad \frac{2x}{y^2z^{0.3}} \qquad \sqrt{2xy} 24 | 25 | are all monomials. Building on this, a posynomial is defined as a sum of monomials: 26 | 27 | .. math:: 28 | 29 | g(x) = \sum_{k=1}^K c_k x_1^{a_1k} x_2^{a_2k} ... x_n^{a_nk} 30 | 31 | For example, the expressions 32 | 33 | .. math:: 34 | 35 | x^2 + 2xy + 1 \qquad 7xy + 0.4(yz)^{-1/3} \qquad 0.56 + \frac{x^{0.7}}{yz} 36 | 37 | are all posynomials. 38 | Alternatively, monomials can be defined as the subset of posynomials having only one term. 39 | Using :math:`f_i` to represent a monomial and :math:`g_i` to represent a posynomial, 40 | a GP in standard form is written as: 41 | 42 | .. math:: \begin{array}{lll}\text{} 43 | \text{minimize} & g_0(x) & \\ 44 | \text{subject to} & f_i(x) = 1, & i = 1,....,m \\ 45 | & g_i(x) \leq 1, & i = 1,....,n 46 | \end{array} 47 | 48 | Boyd et. al. give the following example of a GP in standard form: 49 | 50 | .. math:: \begin{array}{llll}\text{} 51 | \text{minimize} & x^{-1}y^{-1/2}z^{-1} + 2.3xz + 4xyz \\ 52 | \text{subject to} & (1/3)x^{-2}y^{-2} + (4/3)y^{1/2}z^{-1} \leq 1 \\ 53 | & x + 2y + 3z \leq 1 \\ 54 | & (1/2)xy = 1 55 | \end{array} 56 | 57 | Why are GPs special? 58 | ==================== 59 | 60 | Geometric programs have several powerful properties: 61 | 62 | #. Unlike most non-linear optimization problems, large GPs can be **solved extremely quickly**. 63 | #. If there exists an optimal solution to a GP, it is guaranteed to be **globally optimal**. 64 | #. Modern GP solvers require **no initial guesses** or tuning of solver parameters. 65 | 66 | These properties arise because GPs become *convex optimization problems* via a logarithmic transformation. In addition to their mathematical benefits, recent research has shown that many practical problems can be formulated as GPs or closely approximated as GPs. 67 | 68 | 69 | .. _signomials: 70 | 71 | What are Signomials / Signomial Programs? 72 | ========================================= 73 | 74 | When the coefficients in a posynomial are allowed to be negative (but the variables stay strictly positive), that is called a Signomial. 75 | 76 | A Signomial Program has signomial constraints. While they cannot be solved as quickly or to global optima, because they build on the structure of a GP they can often be solved more quickly than a generic nonlinear program. More information can be found under :ref:`signomialprogramming`. 77 | 78 | 79 | Where can I learn more? 80 | ======================= 81 | 82 | To learn more about GPs, refer to the following resources: 83 | 84 | * `A tutorial on geometric programming `_, by S. Boyd, S.J. Kim, L. Vandenberghe, and A. Hassibi. 85 | * `Convex optimization `_, by S. Boyd and L. Vandenberghe. 86 | * `Geometric Programming for Aircraft Design Optimization `_, Hoburg, Abbeel 2014 87 | -------------------------------------------------------------------------------- /docs/source/gplogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/gplogo.png -------------------------------------------------------------------------------- /docs/source/gplogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. figure:: gplogo.png 2 | :width: 250 px 3 | 4 | GPkit is a Python package for defining and manipulating 5 | geometric programming (GP) models. 6 | 7 | Our hopes are to bring geometric programming 8 | into engineering design processes 9 | in a disciplined and collaborative way, and to 10 | encourage research with GPs by providing an 11 | extensible object-oriented framework. 12 | 13 | GPkit abstracts away solvers so users 14 | can work directly with engineering equations and optimization concepts. 15 | Supported solvers are 16 | `MOSEK `_ 17 | and `CVXOPT `_. 18 | 19 | Join our `mailing list `_ and/or `chatroom `_ for support and examples. 20 | 21 | Table of contents 22 | ==================== 23 | .. toctree:: 24 | :maxdepth: 2 25 | 26 | gp101 27 | installation 28 | gettingstarted 29 | debugging 30 | visint 31 | modelbuilding 32 | advancedcommands 33 | signomialprogramming 34 | examples 35 | autodoc/gpkit 36 | citinggpkit 37 | acknowledgements 38 | releasenotes 39 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ************ 5 | 6 | 1. If you are on Mac or Windows, we recommend installing `Anaconda `_. Alternatively, `install pip and create a virtual environment `_. 7 | 2. (optional) Install the MOSEK 9 solver with ``pip install Mosek``, then a license as described below 8 | 3. (optional) Install the MOSEK 8 solver as described below 9 | 4. Run ``pip install gpkit`` in the appropriate terminal or command prompt. 10 | 5. Open a Python prompt and run ``import gpkit`` to finish installation and run unit tests. 11 | 12 | If you encounter any bugs please email ``gpkit@mit.edu`` 13 | or `raise a GitHub issue `_. 14 | 15 | 16 | Installing MOSEK 8 17 | ================== 18 | GPkit interfaces with two off the shelf solvers: cvxopt, and MOSEK (versions 8 and 9). 19 | Cvxopt is open source and installed by default; MOSEK requires a commercial licence or (free) 20 | academic license. In MOSEK version 8 GPkit uses the command-line interface ``mskexpopt`` solver, while 21 | in MOSEK 9 it uses the more active exponential-cone interface (and hence supports :ref:`migp`). 22 | 23 | Mac OS X 24 | - If ``which gcc`` does not return anything, install the `Apple Command Line Tools `_. 25 | - Download `MOSEK 8 `_, then: 26 | - Move the ``mosek`` folder to your home directory 27 | - Follow `these steps for Mac `_. 28 | - If applicable, request an `academic license file `_ and put it in ``~/mosek/`` 29 | 30 | Linux 31 | - Download `MOSEK 8 `_, then: 32 | - Move the ``mosek`` folder to your home directory 33 | - Follow `these steps for Linux `_. 34 | - If applicable, request an `academic license file `_ and put it in ``~/mosek/`` 35 | 36 | Windows 37 | - Make sure ``gcc`` is on your system path. 38 | - To do this, type ``gcc`` into a command prompt. 39 | - If you get ``executable not found``, then install the 64-bit version (x86_64 installer architecture dropdown option) with GCC version 6.4.0 or older of `mingw `_. 40 | - In an Anaconda command prompt (or equivalent), run ``cd C:\Program Files\mingw-w64\x86_64-6.4.0-posix-seh-rt_v5-rev0\`` (or whatever corresponds to the correct installation directory; note that if mingw is in ``Program Files (x86)`` instead of ``Program Files`` you've installed the 32-bit version by mistake) 41 | - Run ``mingw-w64`` to add it to your executable path. For step 3 of the install process you'll need to run ``pip install gpkit`` from this prompt. 42 | - Download `MOSEK 8 `_, then: 43 | - Follow `these steps for Windows `_. 44 | - If applicable, request an `academic license file `_ and put it in ``C:\Users\(your_username)\mosek\`` 45 | 46 | Debugging your installation 47 | =========================== 48 | 49 | You may need to rebuild GPkit if any of the following occur: 50 | - You install MOSEK after installing GPkit 51 | - You see ``Could not load settings file.`` when importing GPkit, or 52 | - ``Could not load MOSEK library: ImportError('expopt.so not found.')`` 53 | 54 | To rebuild GPkit run ``python -c "from gpkit.build import rebuild; rebuild()"``. 55 | 56 | If that doesn't solve your issue then try the following: 57 | - ``pip uninstall gpkit`` 58 | - ``pip install --no-cache-dir --no-deps gpkit`` 59 | - ``python -c "import gpkit.tests; gpkit.tests.run()"`` 60 | - If any tests fail, please email ``gpkit@mit.edu`` or `raise a GitHub issue `_. 61 | 62 | 63 | Bleeding-edge installations 64 | =========================== 65 | 66 | Active developers may wish to install the `latest GPkit `_ directly from Github. To do so, 67 | 68 | 1. ``pip uninstall gpkit`` to uninstall your existing GPkit. 69 | 2. ``git clone https://github.com/convexengineering/gpkit.git`` 70 | 3. ``pip install -e gpkit`` to install that directory as your environment-wide GPkit. 71 | 4. ``cd ..; python -c "import gpkit.tests; gpkit.tests.run()"`` to test your installation from a non-local directory. 72 | -------------------------------------------------------------------------------- /docs/source/ipynb/Box/Box_files/Box_34_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/ipynb/Box/Box_files/Box_34_0.png -------------------------------------------------------------------------------- /docs/source/ipynb/Box/box-ractive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/source/ipynb/Box/box.constraints: -------------------------------------------------------------------------------- 1 | var w = r.plan.scalex, 2 | h = r.left.scalex, 3 | d = r.plan.scaley, 4 | dw = 50*(w-1), 5 | dh = 50*(h-1), 6 | dd = 50*(d-1) 7 | 8 | r.top.scalex = w 9 | r.top.scaley = h 10 | r.top.y = -dh -dd 11 | r.bottom.scalex = w 12 | r.bottom.scaley = h 13 | r.bottom.y = dh + dd 14 | 15 | r.left.scalex = h 16 | r.left.scaley = d 17 | r.left.x = -dh - dw 18 | r.right.scalex = h 19 | r.right.scaley = d 20 | r.right.x = dh + dw 21 | -------------------------------------------------------------------------------- /docs/source/ipynb/Box/box.gpkit: -------------------------------------------------------------------------------- 1 |
2 | 16 | 17 | -------------------------------------------------------------------------------- /docs/source/ipynb/Box/box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 45 | 47 | 48 | 50 | image/svg+xml 51 | 53 | 54 | 55 | 56 | 61 | 67 | 73 | 79 | 85 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /docs/source/ipynb/Box/boxlogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 63 | 70 | 77 | 84 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /docs/source/ipynb/Fuel/Fuel_files/Fuel_17_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/ipynb/Fuel/Fuel_files/Fuel_17_0.png -------------------------------------------------------------------------------- /docs/source/ipynb/Fuel/Fuel_files/Fuel_17_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/ipynb/Fuel/Fuel_files/Fuel_17_1.png -------------------------------------------------------------------------------- /docs/source/ipynb/Fuel/Fuel_files/Fuel_17_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/docs/source/ipynb/Fuel/Fuel_files/Fuel_17_2.png -------------------------------------------------------------------------------- /docs/source/modelbuilding.rst: -------------------------------------------------------------------------------- 1 | Building Complex Models 2 | *********************** 3 | 4 | Checking for result changes 5 | =========================== 6 | Tracking the effects of changes to complex models can get out of hand; 7 | we recommend saving solutions with ``sol.save()``, then checking that new solutions are almost equivalent 8 | with ``sol1.almost_equal(sol2)`` and/or ``print(sol1.diff(sol2))``, as shown below. 9 | 10 | .. literalinclude:: examples/checking_result_changes.py 11 | 12 | You can also check differences between swept solutions, or between 13 | a point solution and a sweep. 14 | 15 | 16 | Inheriting from ``Model`` 17 | ========================= 18 | 19 | GPkit encourages an object-oriented modeling approach, where the modeler creates objects that inherit from Model to break large systems down into subsystems and analysis domains. The benefits of this approach include modularity, reusability, and the ability to more closely follow mental models of system hierarchy. For example: two different models for a simple beam, designed by different modelers, should be able to be used interchangeably inside another subsystem (such as an aircraft wing) without either modeler having to write specifically with that use in mind. 20 | 21 | When you create a class that inherits from Model, write a ``.setup()`` method to create the model's variables and return its constraints. ``GPkit.Model.__init__`` will call that method and automatically add your model's name and unique ID to any created variables. 22 | 23 | Variables created in a ``setup`` method are added to the model even if they are not present in any constraints. This allows for simplistic 'template' models, which assume constant values for parameters and can grow incrementally in complexity as those variables are freed. 24 | 25 | At the end of this page a detailed example shows this technique in practice. 26 | 27 | Accessing Variables in Models 28 | ============================= 29 | GPkit provides several ways to access a Variable in a ``Model`` (or ``ConstraintSet``): 30 | 31 | - using ``Model.variables_byname(key)``. This returns all Variables in the Model, as well as in any submodels, that match the key. 32 | - using ``Model.__getitem__``. ``Model[key]`` returns the only variable matching the key, even if the match occurs in a submodel. If multiple variables match the key, an error is raised. 33 | 34 | These methods are illustrated in the following example. 35 | 36 | .. literalinclude:: examples/model_var_access.py 37 | 38 | .. literalinclude:: examples/model_var_access_output.txt 39 | :language: breakdowns 40 | 41 | Vectorization 42 | ============= 43 | 44 | ``gpkit.Vectorize`` creates an environment in which Variables are created with an additional dimension: 45 | 46 | .. literalinclude:: examples/vectorization.py 47 | 48 | This allows models written with scalar constraints to be created with vector constraints: 49 | 50 | .. literalinclude:: examples/vectorize.py 51 | 52 | .. literalinclude:: examples/vectorize_output.txt 53 | :language: breakdowns 54 | 55 | 56 | 57 | Multipoint analysis modeling 58 | ============================ 59 | 60 | In many engineering models, there is a physical object that is operated in multiple conditions. Some variables correspond to the design of the object (size, weight, construction) while others are vectorized over the different conditions (speed, temperature, altitude). By combining named models and vectorization we can create intuitive representations of these systems while maintaining modularity and interoperability. 61 | 62 | In the example below, the models ``Aircraft`` and ``Wing`` have a ``.dynamic()`` method which creates instances of ``AircraftPerformance`` and ``WingAero``, respectively. The ``Aircraft`` and ``Wing`` models create variables, such as size and weight without fuel, that represent a physical object. The ``dynamic`` models create properties that change based on the flight conditions, such as drag and fuel weight. 63 | 64 | This means that when an aircraft is being optimized for a mission, you can create the aircraft (``AC`` in this example) and then pass it to a ``Mission`` model which can create vectorized aircraft performance models for each flight segment and/or flight condition. 65 | 66 | The :ref:`sensitivity diagram ` which this code outputs shows how it is organized (right-click and open in a new tab to see it more clearly): 67 | 68 | .. figure:: figures/sankey_autosaves/Model.png 69 | 70 | .. literalinclude:: examples/performance_modeling.py 71 | 72 | Note that the output table has been filtered above to show only variables of interest. 73 | 74 | .. literalinclude:: examples/performance_modeling_output.txt 75 | :language: breakdowns 76 | -------------------------------------------------------------------------------- /docs/source/releasenotes.rst: -------------------------------------------------------------------------------- 1 | Release Notes 2 | ************* 3 | 4 | `Release notes are available on Github `_ 5 | -------------------------------------------------------------------------------- /docs/source/signomialprogramming.rst: -------------------------------------------------------------------------------- 1 | .. _signomialprogramming: 2 | 3 | Signomial Programming 4 | ********************* 5 | 6 | Signomial programming finds a local solution to a problem of the form: 7 | 8 | 9 | .. math:: \begin{array}{lll}\text{} 10 | \text{minimize} & g_0(x) & \\ 11 | \text{subject to} & f_i(x) = 1, & i = 1,....,m \\ 12 | & g_i(x) - h_i(x) \leq 1, & i = 1,....,n 13 | \end{array} 14 | 15 | where each :math:`f` is monomial while each :math:`g` and :math:`h` is a posynomial. 16 | 17 | This requires multiple solutions of geometric programs, and so will take longer to solve than an equivalent geometric programming formulation. 18 | 19 | In general, when given the choice of which variables to include in the positive-posynomial / :math:`g` side of the constraint, the modeler should: 20 | 21 | #. maximize the number of variables in :math:`g`, 22 | #. prioritize variables that are in the objective, 23 | #. then prioritize variables that are present in other constraints. 24 | 25 | The ``.localsolve`` syntax was chosen to emphasize that signomial programming returns a local optimum. For the same reason, calling ``.solve`` on an SP will raise an error. 26 | 27 | By default, signomial programs are first solved conservatively (by assuming each :math:`h` is equal only to its constant portion) and then become less conservative on each iteration. 28 | 29 | Example Usage 30 | ============= 31 | 32 | .. literalinclude:: examples/simple_sp.py 33 | 34 | When using the ``localsolve`` method, the ``reltol`` argument specifies the relative tolerance of the solver: that is, by what percent does the solution have to improve between iterations? If any iteration improves less than that amount, the solver stops and returns its value. 35 | 36 | If you wish to start the local optimization at a particular point :math:`x_0`, however, you may do so by putting that position (a dictionary formatted as you would a substitution) as the ``x0`` argument. 37 | 38 | .. _sgp: 39 | 40 | Sequential Geometric Programs 41 | ============================= 42 | 43 | The method of solving local GP approximations of a non-GP compatible model can be generalized, at the cost of the general smoothness and lack of a need for trust regions that SPs guarantee. 44 | 45 | For some applications, it is useful to call external codes which may not be GP compatible. Imagine we wished to solve the following optimization problem: 46 | 47 | .. math:: \begin{array}{lll}\text{} 48 | \text{minimize} & y & \\ 49 | \text{subject to} & y \geq \sin(x) \\ 50 | & \frac{\pi}{4} \leq x \leq \frac{\pi}{2} 51 | \end{array} 52 | 53 | This problem is not GP compatible due to the :math:`sin(x)` constraint. One approach might be to take the first term of the Taylor expansion of :math:`sin(x)` and attempt to solve: 54 | 55 | .. literalinclude:: examples/sin_approx_example.py 56 | 57 | .. literalinclude:: examples/sin_approx_example_output.txt 58 | :language: breakdowns 59 | 60 | Assume we have some external code which is capable of evaluating our incompatible function: 61 | 62 | .. literalinclude:: examples/external_function.py 63 | 64 | Now, we can create a ConstraintSet that allows GPkit to treat the incompatible constraint as though it were a signomial programming constraint: 65 | 66 | .. literalinclude:: examples/external_constraint.py 67 | 68 | and replace the incompatible constraint in our GP: 69 | 70 | .. literalinclude:: examples/external_sp.py 71 | 72 | .. literalinclude:: examples/external_sp_output.txt 73 | :language: breakdowns 74 | 75 | which is the expected result. This method has been generalized to larger problems, such as calling XFOIL and AVL. 76 | 77 | If you wish to start the local optimization at a particular point :math:`x_0`, however, you may do so by putting that position (a dictionary formatted as you would a substitution) as the ``x0`` argument 78 | -------------------------------------------------------------------------------- /fulltests.sh: -------------------------------------------------------------------------------- 1 | python -c "from gpkit.tests import run; run()" 2 | rm *.pkl 3 | rm solution.* 4 | rm referencesplot.* 5 | -------------------------------------------------------------------------------- /gpkit/__init__.py: -------------------------------------------------------------------------------- 1 | "GP and SP modeling package" 2 | #pylint:disable=wrong-import-position 3 | __version__ = "1.1" 4 | GPCOLORS = ["#59ade4", "#FA3333"] 5 | GPBLU, GPRED = GPCOLORS 6 | 7 | from .build import build 8 | from .units import units, ureg, DimensionalityError 9 | from .globals import settings, SignomialsEnabled, Vectorize, NamedVariables 10 | from .varkey import VarKey 11 | from .nomials import Monomial, Posynomial, Signomial, NomialArray 12 | from .nomials import VectorizableVariable as Variable 13 | # NOTE above: the Variable the user sees is not the Variable used internally 14 | from .nomials import VectorVariable, ArrayVariable 15 | from .constraints.gp import GeometricProgram 16 | from .constraints.sgp import SequentialGeometricProgram 17 | from .constraints.sigeq import SignomialEquality 18 | from .constraints.set import ConstraintSet 19 | from .constraints.model import Model 20 | from .solution_array import SolutionArray 21 | from .tools.docstring import parse_variables 22 | from .tests.run_tests import run as run_unit_tests 23 | 24 | if "just built!" in settings: # pragma: no cover 25 | run_unit_tests(verbosity=1) 26 | print(""" 27 | GPkit is now installed with solver(s) %s 28 | To incorporate new solvers at a later date, run `gpkit.build()`. 29 | 30 | If any tests didn't pass, please post the output above 31 | (starting from "Found no installed solvers, beginning a build.") 32 | to gpkit@mit.edu or https://github.com/convexengineering/gpkit/issues/new 33 | so we can prevent others from having these errors. 34 | 35 | The same goes for any other bugs you encounter with GPkit: 36 | send 'em our way, along with any interesting models, speculative features, 37 | comments, discussions, or clarifications you feel like sharing. 38 | 39 | Finally, we hope you find our documentation (https://gpkit.readthedocs.io/) 40 | and engineering-design models (https://github.com/convexengineering/gplibrary/) 41 | to be useful resources for your own applications. 42 | 43 | Enjoy! 44 | """ % settings["installed_solvers"]) 45 | -------------------------------------------------------------------------------- /gpkit/constraints/__init__.py: -------------------------------------------------------------------------------- 1 | "Contains ConstraintSet and related classes and objects" 2 | from .single_equation import SingleEquationConstraint 3 | from .array import ArrayConstraint 4 | -------------------------------------------------------------------------------- /gpkit/constraints/array.py: -------------------------------------------------------------------------------- 1 | "Implements ArrayConstraint" 2 | from .single_equation import SingleEquationConstraint 3 | 4 | 5 | class ArrayConstraint(SingleEquationConstraint, list): 6 | """A ConstraintSet for prettier array-constraint printing. 7 | 8 | When created by NomialArray `left` and `right` are likely to be 9 | be either NomialArrays or Varkeys of VectorVariables. 10 | """ 11 | def __init__(self, constraints, left, oper, right): 12 | self.constraints = constraints 13 | list.__init__(self, constraints) 14 | SingleEquationConstraint.__init__(self, left, oper, right) 15 | 16 | def __iter__(self): 17 | yield from self.constraints.flat 18 | 19 | def lines_without(self, excluded): 20 | "Returns lines for indentation in hierarchical printing." 21 | return self.str_without(excluded).split("\n") 22 | 23 | def __bool__(self): 24 | "Allows the use of '=' NomialArrays as truth elements." 25 | return False if self.oper != "=" else bool(self.constraints.all()) 26 | -------------------------------------------------------------------------------- /gpkit/constraints/bounded.py: -------------------------------------------------------------------------------- 1 | "Implements Bounded" 2 | from collections import defaultdict 3 | import numpy as np 4 | from .. import Variable 5 | from .set import ConstraintSet 6 | from ..small_scripts import appendsolwarning, initsolwarning 7 | 8 | 9 | def varkey_bounds(varkeys, lower, upper): 10 | """Returns constraints list bounding all varkeys. 11 | 12 | Arguments 13 | --------- 14 | varkeys : iterable 15 | list of varkeys to create bounds for 16 | 17 | lower : float 18 | lower bound for all varkeys 19 | 20 | upper : float 21 | upper bound for all varkeys 22 | """ 23 | constraints = [] 24 | for varkey in varkeys: 25 | variable = Variable(**varkey.descr) 26 | if variable.units: # non-dimensionalize the variable monomial 27 | variable.units = variable.hmap.units = None 28 | constraint = [] 29 | if lower: 30 | constraint.append(lower <= variable) 31 | if upper: 32 | constraint.append(variable <= upper) 33 | constraints.append(constraint) 34 | return constraints 35 | 36 | 37 | class Bounded(ConstraintSet): 38 | """Bounds contained variables, generally ensuring dual feasibility. 39 | 40 | Arguments 41 | --------- 42 | constraints : iterable 43 | constraints whose varkeys will be bounded 44 | 45 | eps : float (default 1e-30) 46 | default lower bound is eps, upper bound is 1/eps 47 | 48 | lower : float (default None) 49 | lower bound for all varkeys, replaces eps 50 | 51 | upper : float (default None) 52 | upper bound for all varkeys, replaces 1/eps 53 | """ 54 | sens_threshold = 1e-7 55 | logtol_threshold = 3 56 | 57 | def __init__(self, constraints, *, eps=1e-30, lower=None, upper=None): 58 | if not isinstance(constraints, ConstraintSet): 59 | constraints = ConstraintSet(constraints) 60 | self.lowerbound = lower or eps 61 | self.upperbound = upper or 1/eps 62 | constrained_varkeys = constraints.constrained_varkeys() 63 | self.bound_varkeys = frozenset(vk for vk in constrained_varkeys 64 | if vk not in constraints.substitutions) 65 | bounding_constraints = varkey_bounds(self.bound_varkeys, 66 | self.lowerbound, self.upperbound) 67 | super().__init__({"original constraints": constraints, 68 | "variable bounds": bounding_constraints}) 69 | 70 | def process_result(self, result): 71 | "Add boundedness to the model's solution" 72 | super().process_result(result) 73 | if "boundedness" not in result: 74 | result["boundedness"] = {} 75 | result["boundedness"].update(self.check_boundaries(result)) 76 | 77 | def check_boundaries(self, result): 78 | "Creates (and potentially prints) a dictionary of unbounded variables." 79 | out = defaultdict(set) 80 | initsolwarning(result, "Arbitrarily Bounded Variables") 81 | for i, varkey in enumerate(self.bound_varkeys): 82 | value = result["variables"][varkey] 83 | c_senss = [result["sensitivities"]["constraints"].get(c, 0) 84 | for c in self["variable bounds"][i]] 85 | if self.lowerbound: 86 | bound = "lower bound of %.2g" % self.lowerbound 87 | if c_senss[0] >= self.sens_threshold: 88 | out["sensitive to " + bound].add(varkey) 89 | if np.log(value/self.lowerbound) <= self.logtol_threshold: 90 | out["value near " + bound].add(varkey) 91 | if self.upperbound: 92 | bound = "upper bound of %.2g" % self.upperbound 93 | if c_senss[-1] >= self.sens_threshold: 94 | out["sensitive to " + bound].add(varkey) 95 | if np.log(self.upperbound/value) <= self.logtol_threshold: 96 | out["value near " + bound].add(varkey) 97 | for bound, vks in out.items(): 98 | msg = "% 34s: %s" % (bound, ", ".join([str(v) for v in vks])) 99 | appendsolwarning(msg, out, result, 100 | "Arbitrarily Bounded Variables") 101 | return out 102 | -------------------------------------------------------------------------------- /gpkit/constraints/costed.py: -------------------------------------------------------------------------------- 1 | "Implement CostedConstraintSet" 2 | import numpy as np 3 | from .set import ConstraintSet 4 | from ..small_scripts import maybe_flatten 5 | from ..repr_conventions import lineagestr 6 | 7 | 8 | class CostedConstraintSet(ConstraintSet): 9 | """A ConstraintSet with a cost 10 | 11 | Arguments 12 | --------- 13 | cost : gpkit.Posynomial 14 | constraints : Iterable 15 | substitutions : dict 16 | """ 17 | lineage = None 18 | 19 | def __init__(self, cost, constraints, substitutions=None): 20 | self.cost = maybe_flatten(cost) 21 | if isinstance(self.cost, np.ndarray): # if it's still a vector 22 | raise ValueError("Cost must be scalar, not the vector %s." % cost) 23 | subs = {k: k.value for k in self.cost.vks if "value" in k.descr} 24 | if substitutions: 25 | subs.update(substitutions) 26 | ConstraintSet.__init__(self, constraints, subs, bonusvks=self.cost.vks) 27 | 28 | def constrained_varkeys(self): 29 | "Return all varkeys in the cost and non-ConstraintSet constraints" 30 | constrained_varkeys = ConstraintSet.constrained_varkeys(self) 31 | constrained_varkeys.update(self.cost.vks) 32 | return constrained_varkeys 33 | 34 | def _rootlines(self, excluded=()): 35 | "String showing cost, to be used when this is the top constraint" 36 | if self.cost.vks: 37 | description = ["", "Cost Function", "-------------", 38 | " %s" % self.cost.str_without(excluded), 39 | "", "Constraints", "-----------"] 40 | else: # don't print the cost if it's a constant 41 | description = ["", "Constraints", "-----------"] 42 | if self.lineage: 43 | fullname = lineagestr(self) 44 | description = [fullname, "="*len(fullname)] + description 45 | return description 46 | 47 | def _rootlatex(self, excluded=()): 48 | "Latex showing cost, to be used when this is the top constraint" 49 | return "\n".join(["\\text{minimize}", 50 | " & %s \\\\" % self.cost.latex(excluded), 51 | "\\text{subject to}"]) 52 | -------------------------------------------------------------------------------- /gpkit/constraints/loose.py: -------------------------------------------------------------------------------- 1 | "Implements Loose" 2 | from .set import ConstraintSet 3 | from ..small_scripts import appendsolwarning, initsolwarning 4 | 5 | 6 | class Loose(ConstraintSet): 7 | "ConstraintSet whose inequalities must result in an equality." 8 | senstol = 1e-5 9 | raiseerror = False 10 | 11 | def __init__(self, constraints, *, senstol=None): 12 | super().__init__(constraints) 13 | self.senstol = senstol or self.senstol 14 | 15 | def process_result(self, result): 16 | "Checks that all constraints are satisfied with equality" 17 | super().process_result(result) 18 | initsolwarning(result, "Unexpectedly Tight Constraints") 19 | if "sensitivities" not in result: 20 | appendsolwarning("Could not evaluate due to choice variables.", 21 | (), result, "Unexpectedly Tight Constraints") 22 | return 23 | for constraint in self.flat(): 24 | c_senss = result["sensitivities"]["constraints"].get(constraint, 0) 25 | if c_senss >= self.senstol: 26 | cstr = ("Constraint [ %.100s... %s %.100s... )" 27 | % (constraint.left, constraint.oper, constraint.right)) 28 | msg = ("%s is not loose: it has a sensitivity of %+.4g." 29 | " (Allowable sensitivity: %.4g)" % 30 | (cstr, c_senss, self.senstol)) 31 | appendsolwarning(msg, (c_senss, constraint), result, 32 | "Unexpectedly Tight Constraints") 33 | if self.raiseerror: 34 | raise RuntimeWarning(msg) 35 | -------------------------------------------------------------------------------- /gpkit/constraints/sigeq.py: -------------------------------------------------------------------------------- 1 | "Implements SignomialEquality" 2 | from .set import ConstraintSet 3 | from ..nomials import SingleSignomialEquality as SSE 4 | from ..nomials.array import array_constraint as arrify 5 | 6 | 7 | class SignomialEquality(ConstraintSet): 8 | "A constraint of the general form posynomial == posynomial" 9 | 10 | def __init__(self, left, right): 11 | if hasattr(left, "shape") or hasattr(right, "shape"): 12 | ConstraintSet.__init__(self, arrify("=", SSE)(left, right)) 13 | else: 14 | ConstraintSet.__init__(self, [SSE(left, right)]) 15 | -------------------------------------------------------------------------------- /gpkit/constraints/single_equation.py: -------------------------------------------------------------------------------- 1 | "Implements SingleEquationConstraint" 2 | from operator import le, ge, eq 3 | from ..small_scripts import try_str_without 4 | from ..repr_conventions import ReprMixin, UNICODE_EXPONENTS 5 | 6 | 7 | class SingleEquationConstraint(ReprMixin): 8 | "Constraint expressible in a single equation." 9 | latex_opers = {"<=": "\\leq", ">=": "\\geq", "=": "="} 10 | unicode_opers = {"<=": "≤", ">=": "≥", "=": "="} 11 | func_opers = {"<=": le, ">=": ge, "=": eq} 12 | 13 | def __init__(self, left, oper, right): 14 | self.left, self.oper, self.right = left, oper, right 15 | 16 | def str_without(self, excluded=("units")): 17 | "String representation without attributes in excluded list" 18 | leftstr = try_str_without(self.left, excluded) 19 | rightstr = try_str_without(self.right, excluded) 20 | rlines = rightstr.split("\n") 21 | if len(rlines) > 1: 22 | indent = len("%s %s " % (leftstr.split("\n")[-1], self.oper)) 23 | rightstr = ("\n" + " "*indent).join(rlines) 24 | if UNICODE_EXPONENTS: 25 | oper = self.unicode_opers[self.oper] 26 | else: 27 | oper = self.oper 28 | return "%s %s %s" % (leftstr, oper, rightstr) 29 | 30 | def latex(self, excluded=("units")): 31 | "Latex representation without attributes in excluded list" 32 | return ("%s %s %s" % ( 33 | try_str_without(self.left, excluded, latex=True), 34 | self.latex_opers[self.oper], 35 | try_str_without(self.right, excluded, latex=True))) 36 | -------------------------------------------------------------------------------- /gpkit/constraints/tight.py: -------------------------------------------------------------------------------- 1 | "Implements Tight" 2 | from .set import ConstraintSet 3 | from ..small_scripts import mag 4 | from ..small_scripts import appendsolwarning, initsolwarning 5 | from .. import SignomialsEnabled 6 | 7 | 8 | class Tight(ConstraintSet): 9 | "ConstraintSet whose inequalities must result in an equality." 10 | reltol = 1e-3 11 | 12 | def __init__(self, constraints, *, reltol=None, **kwargs): 13 | super().__init__(constraints) 14 | self.reltol = reltol or self.reltol 15 | self.__dict__.update(kwargs) # NOTE: for Berk's use in labelling 16 | 17 | def process_result(self, result): 18 | "Checks that all constraints are satisfied with equality" 19 | super().process_result(result) 20 | variables = result["variables"] 21 | initsolwarning(result, "Unexpectedly Loose Constraints") 22 | for constraint in self.flat(): 23 | with SignomialsEnabled(): 24 | leftval = constraint.left.sub(variables).value 25 | rightval = constraint.right.sub(variables).value 26 | rel_diff = mag(abs(1 - leftval/rightval)) 27 | if rel_diff >= self.reltol: 28 | msg = ("Constraint [%.100s... %s %.100s...] is not tight:" 29 | " the left hand side evaluated to %s but" 30 | " the right hand side evaluated to %s" 31 | " (Allowable error: %s%%, Actual error: %.2g%%)" % 32 | (constraint.left, constraint.oper, constraint.right, 33 | leftval, rightval, 34 | self.reltol*100, mag(rel_diff)*100)) 35 | if hasattr(leftval, "magnitude"): 36 | rightval = rightval.to(leftval.units).magnitude 37 | leftval = leftval.magnitude 38 | tightvalues = (leftval, constraint.oper, rightval) 39 | appendsolwarning(msg, (rel_diff, tightvalues, constraint), 40 | result, "Unexpectedly Loose Constraints") 41 | -------------------------------------------------------------------------------- /gpkit/exceptions.py: -------------------------------------------------------------------------------- 1 | "GPkit-specific Exception classes" 2 | from . import DimensionalityError # pylint: disable=unused-import 3 | 4 | class MathematicallyInvalid(TypeError): 5 | "Raised whenever something violates a mathematical definition." 6 | 7 | class InvalidPosynomial(MathematicallyInvalid): 8 | "Raised if a Posynomial would be created with a negative coefficient" 9 | 10 | class InvalidGPConstraint(MathematicallyInvalid): 11 | "Raised if a non-GP-compatible constraint is used in a GP" 12 | 13 | class InvalidSGPConstraint(MathematicallyInvalid): 14 | "Raised if a non-SGP-compatible constraint is used in an SGP" 15 | 16 | 17 | class UnnecessarySGP(ValueError): 18 | "Raised if an SGP is fully GP-compatible" 19 | 20 | class UnboundedGP(ValueError): 21 | "Raise if a GP is not fully bounded" 22 | 23 | 24 | class InvalidLicense(RuntimeWarning): 25 | "Raised if a solver's license is missing, invalid, or expired." 26 | 27 | 28 | class Infeasible(RuntimeWarning): 29 | "Raised if a model does not solve" 30 | 31 | class UnknownInfeasible(Infeasible): 32 | "Raised if a model does not solve for unknown reasons" 33 | 34 | class PrimalInfeasible(Infeasible): 35 | "Raised if a model returns a certificate of primal infeasibility" 36 | 37 | class DualInfeasible(Infeasible): 38 | "Raised if a model returns a certificate of dual infeasibility" 39 | -------------------------------------------------------------------------------- /gpkit/globals.py: -------------------------------------------------------------------------------- 1 | "global mutable variables" 2 | import os 3 | from collections import defaultdict 4 | from . import build 5 | 6 | 7 | def load_settings(path=None, trybuild=True): 8 | "Load the settings file at SETTINGS_PATH; return settings dict" 9 | if path is None: 10 | path = os.sep.join([os.path.dirname(__file__), "env", "settings"]) 11 | try: # if the settings file already exists, read it 12 | with open(path) as settingsfile: 13 | lines = [line[:-1].split(" : ") for line in settingsfile 14 | if len(line.split(" : ")) == 2] 15 | settings_ = {name: value.split(", ") for name, value in lines} 16 | for name, value in settings_.items(): 17 | # flatten 1-element lists unless they're the solver list 18 | if len(value) == 1 and name != "installed_solvers": 19 | settings_[name], = value 20 | except IOError: # pragma: no cover 21 | settings_ = {"installed_solvers": [""]} 22 | if settings_["installed_solvers"] == [""] and trybuild: # pragma: no cover 23 | print("Found no installed solvers, beginning a build.") 24 | build() 25 | settings_ = load_settings(path, trybuild=False) 26 | if settings_["installed_solvers"] != [""]: 27 | settings_["just built!"] = True 28 | else: 29 | print(""" 30 | ============= 31 | Build failed! :( 32 | ============= 33 | You may need to install a solver and then `import gpkit` again; 34 | see https://gpkit.readthedocs.io/en/latest/installation.html 35 | for troubleshooting details. 36 | 37 | But before you go, please post the output above 38 | (starting from "Found no installed solvers, beginning a build.") 39 | to gpkit@mit.edu or https://github.com/convexengineering/gpkit/issues/new 40 | so we can prevent others from having to see this message. 41 | 42 | Thanks! :) 43 | """) 44 | settings_["default_solver"] = settings_["installed_solvers"][0] 45 | return settings_ 46 | 47 | 48 | settings = load_settings() 49 | 50 | 51 | class SignomialsEnabledMeta(type): 52 | "Metaclass to implement falsiness for SignomialsEnabled" 53 | def __bool__(cls): return cls._true # pylint: disable=multiple-statements 54 | 55 | class SignomialsEnabled(metaclass=SignomialsEnabledMeta): # pylint: disable=no-init 56 | """Class to put up and tear down signomial support in an instance of GPkit. 57 | 58 | Example 59 | ------- 60 | >>> import gpkit 61 | >>> x = gpkit.Variable("x") 62 | >>> y = gpkit.Variable("y", 0.1) 63 | >>> with SignomialsEnabled(): 64 | >>> constraints = [x >= 1-y] 65 | >>> gpkit.Model(x, constraints).localsolve() 66 | """ 67 | _true = False # default signomial permissions 68 | # pylint: disable=multiple-statements 69 | def __enter__(self): SignomialsEnabled._true = True 70 | def __exit__(self, type_, val, traceback): SignomialsEnabled._true = False 71 | 72 | 73 | class Vectorize: 74 | """Creates an environment in which all variables are 75 | exended in an additional dimension. 76 | """ 77 | vectorization = () # the current vectorization shape 78 | 79 | def __init__(self, dimension_length): 80 | self.dimension_length = dimension_length 81 | 82 | def __enter__(self): 83 | "Enters a vectorized environment." 84 | Vectorize.vectorization = (self.dimension_length,) + self.vectorization 85 | 86 | def __exit__(self, type_, val, traceback): 87 | "Leaves a vectorized environment." 88 | Vectorize.vectorization = self.vectorization[1:] 89 | 90 | 91 | class NamedVariables: 92 | """Creates an environment in which all variables have 93 | a model name and num appended to their varkeys. 94 | """ 95 | lineage = () # the current model nesting 96 | modelnums = defaultdict(int) # the number of models of each lineage 97 | namedvars = defaultdict(list) # variables created in the current nesting 98 | 99 | @classmethod 100 | def reset_modelnumbers(cls): 101 | "Clear all model number counters" 102 | for key in list(cls.modelnums): 103 | del cls.modelnums[key] 104 | 105 | def __init__(self, name): 106 | self.name = name 107 | 108 | def __enter__(self): 109 | "Enters a named environment." 110 | num = self.modelnums[(self.lineage, self.name)] 111 | self.modelnums[(self.lineage, self.name)] += 1 112 | NamedVariables.lineage += ((self.name, num),) # NOTE: Side effects 113 | return self.lineage, self.namedvars[self.lineage] 114 | 115 | def __exit__(self, type_, val, traceback): 116 | "Leaves a named environment." 117 | del self.namedvars[self.lineage] 118 | NamedVariables.lineage = self.lineage[:-1] # NOTE: Side effects 119 | -------------------------------------------------------------------------------- /gpkit/interactive/__init__.py: -------------------------------------------------------------------------------- 1 | "Module for the interactive and plotting functions of GPkit" 2 | -------------------------------------------------------------------------------- /gpkit/interactive/plot_sweep.py: -------------------------------------------------------------------------------- 1 | "Implements plot_sweep1d function" 2 | import matplotlib.pyplot as plt 3 | from ..exceptions import InvalidGPConstraint 4 | 5 | 6 | def assign_axes(var, posys, axes): 7 | "Assigns axes to posys, creating and formatting if necessary" 8 | if not hasattr(posys, "__iter__"): 9 | posys = [posys] 10 | N = len(posys) 11 | if axes is None: 12 | _, axes = plt.subplots(N, 1, sharex="col", figsize=(4.5, 3+1.5*N)) 13 | if N == 1: 14 | axes = [axes] 15 | format_and_label_axes(var, posys, axes) 16 | elif N == 1 and not hasattr(axes, "__len__"): 17 | axes = [axes] 18 | return posys, axes 19 | 20 | 21 | def format_and_label_axes(var, posys, axes, ylabel=True): 22 | "Formats and labels axes" 23 | for posy, ax in zip(posys, axes): 24 | if ylabel: 25 | if hasattr(posy, "key"): 26 | ylabel = (posy.key.descr.get("label", posy.key.name) 27 | + " [%s]" % posy.key.unitstr(dimless="-")) 28 | else: 29 | ylabel = str(posy) 30 | ax.set_ylabel(ylabel) 31 | ax.grid(color="0.6") 32 | # ax.set_frame_on(False) 33 | for item in [ax.xaxis.label, ax.yaxis.label]: 34 | item.set_fontsize(12) 35 | for item in ax.get_xticklabels() + ax.get_yticklabels(): 36 | item.set_fontsize(9) 37 | ax.tick_params(length=0) 38 | ax.spines['left'].set_visible(False) 39 | ax.spines['top'].set_visible(False) 40 | for i in ax.spines.values(): 41 | i.set_linewidth(0.6) 42 | i.set_color("0.6") 43 | i.set_linestyle("dotted") 44 | xlabel = (var.key.descr.get("label", var.key.name) 45 | + " [%s]" % var.key.unitstr(dimless="-")) 46 | ax.set_xlabel(xlabel) # pylint: disable=undefined-loop-variable 47 | plt.locator_params(nbins=4) 48 | plt.subplots_adjust(wspace=0.15) 49 | 50 | 51 | # pylint: disable=too-many-locals,too-many-branches,too-many-statements 52 | def plot_1dsweepgrid(model, sweeps, posys, origsol=None, tol=0.01, **solveargs): 53 | """Creates and plots a sweep from an existing model 54 | 55 | Example usage: 56 | f, _ = plot_sweep_1d(m, {'x': np.linspace(1, 2, 5)}, 'y') 57 | f.savefig('mysweep.png') 58 | """ 59 | origsubs = {swept: model.substitutions[swept] for swept in sweeps 60 | if swept in model.substitutions} 61 | if origsubs and not origsol: 62 | try: 63 | origsol = model.solve(**solveargs) 64 | except InvalidGPConstraint: 65 | origsol = model.localsolve(**solveargs) 66 | if not hasattr(posys, "__iter__"): 67 | posys = [posys] 68 | 69 | N, S = len(posys), len(sweeps) 70 | f, axes = plt.subplots(N, S, sharex='col', sharey='row', 71 | figsize=(4+2*S, 4+2*N)) 72 | plt.subplots_adjust(hspace=0.15) 73 | 74 | for i, (swept, swept_over) in enumerate(sweeps.items()): 75 | if isinstance(swept_over, tuple) and len(swept_over) == 2: 76 | sol = model.autosweep({swept: swept_over}, tol=tol, **solveargs) 77 | else: 78 | sol = model.sweep({swept: swept_over}, **solveargs) 79 | 80 | if len(sweeps) == 1: 81 | if len(posys) == 1: 82 | subaxes = [axes] 83 | else: 84 | subaxes = axes 85 | elif len(posys) == 1: 86 | subaxes = [axes[i]] 87 | else: 88 | subaxes = axes[:, i] 89 | 90 | sol.plot(posys, subaxes) 91 | if origsubs: 92 | for posy, ax in zip(posys, subaxes): 93 | ax.plot(origsubs[swept], origsol(posy), "ko", markersize=4) 94 | format_and_label_axes(swept, posys, subaxes, ylabel=(i == 0)) 95 | model.substitutions.update(origsubs) 96 | 97 | return f, axes 98 | -------------------------------------------------------------------------------- /gpkit/interactive/plotting.py: -------------------------------------------------------------------------------- 1 | """Plotting methods""" 2 | from collections import Counter 3 | import plotly.graph_objects as go 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | from .plot_sweep import assign_axes 7 | from .. import GPCOLORS 8 | 9 | 10 | def compare(models, sweeps, posys, tol=0.001): 11 | """Compares the values of posys over a sweep of several models. 12 | 13 | If posys is of the same length as models, this will plot different 14 | variables from different models. 15 | 16 | Currently only supports a single sweepvar. 17 | 18 | Example Usage: 19 | compare([aec, fbc], {"R": (160, 300)}, 20 | ["cost", ("W_{\\rm batt}", "W_{\\rm fuel}")], tol=0.001) 21 | """ 22 | sols = [m.autosweep(sweeps, tol, verbosity=0) for m in models] 23 | posys, axes = assign_axes(sols[0].bst.sweptvar, posys, None) 24 | for posy, ax in zip(posys, axes): 25 | for i, sol in enumerate(sols): 26 | if hasattr(posy, "__len__") and len(posy) == len(sols): 27 | p = posy[i] 28 | else: 29 | p = posy 30 | color = GPCOLORS[i % len(GPCOLORS)] 31 | if sol._is_cost(p): # pylint: disable=protected-access 32 | ax.fill_between(sol.sampled_at, 33 | sol.cost_lb(), sol.cost_ub(), 34 | facecolor=color, edgecolor=color, 35 | linewidth=0.75) 36 | else: 37 | ax.plot(sol.sampled_at, sol(p), color=color) 38 | 39 | 40 | def plot_convergence(model): 41 | """Plots the convergence of a signomial programming model 42 | 43 | Arguments 44 | --------- 45 | model: Model 46 | Signomial programming model that has already been solved 47 | 48 | Returns 49 | ------- 50 | matplotlib.pyplot Figure 51 | Plot of cost as functions of SP iteration # 52 | """ 53 | fig, ax = plt.subplots() 54 | 55 | it = np.array([]) 56 | cost = np.array([]) 57 | for n in range(len(model.program.gps)): 58 | try: 59 | cost = np.append(cost, model.program.gps[n].result['cost']) 60 | it = np.append(it, n+1) 61 | except TypeError: 62 | pass 63 | ax.plot(it, cost, '-o') 64 | ax.set_xlabel('Iteration') 65 | ax.set_ylabel('Cost') 66 | ax.set_xticks(range(1, len(model.program.gps)+1)) 67 | return fig, ax 68 | 69 | 70 | def treemap(model, itemize="variables", sizebycount=False): 71 | """Plots model structure as Plotly TreeMap 72 | 73 | Arguments 74 | --------- 75 | model: Model 76 | GPkit model object 77 | 78 | itemize (optional): string, either "variables" or "constraints" 79 | Specify whether to iterate over the model varkeys or constraints 80 | 81 | sizebycount (optional): bool 82 | Whether to size blocks by number of variables/constraints or use 83 | default sizing 84 | 85 | Returns 86 | ------- 87 | plotly.graph_objects.Figure 88 | Plot of model hierarchy 89 | 90 | """ 91 | modelnames = [] 92 | parents = [] 93 | sizes = [] 94 | 95 | if itemize == "variables": 96 | lineagestrs = [l.lineagestr() or "Model" for l in model.varkeys] 97 | elif itemize == "constraints": 98 | lineagestrs = [l.lineagestr() or "Model" for l in model.flat()] 99 | 100 | modelcount = Counter(lineagestrs) 101 | for modelname, count in modelcount.items(): 102 | modelnames.append(modelname) 103 | if "." in modelname: 104 | parent = modelname.rsplit(".", 1)[0] 105 | elif modelname != "Model": 106 | parent = "Model" 107 | else: 108 | parent = "" 109 | parents.append(parent) 110 | sizes.append(count) 111 | 112 | for parent in parents: 113 | if parent not in modelnames: 114 | modelnames.append(parent) 115 | if "." in parent: 116 | grandparent = parent.rsplit(".", 1)[0] 117 | elif parent != "Model": 118 | grandparent = "Model" 119 | else: 120 | grandparent = "" 121 | parents.append(grandparent) 122 | sizes.append(0) 123 | 124 | fig = go.Figure(go.Treemap( 125 | ids=modelnames, 126 | labels=[modelname.split(".")[-1] for modelname in modelnames], 127 | parents=parents, 128 | values=sizes if sizebycount else None, 129 | )) 130 | return fig 131 | -------------------------------------------------------------------------------- /gpkit/interactive/references.py: -------------------------------------------------------------------------------- 1 | "Code to make variable references plots" 2 | 3 | import os 4 | import shutil 5 | import webbrowser 6 | from collections import defaultdict 7 | 8 | 9 | # pylint:disable=too-many-locals 10 | def referencesplot(model, *, openimmediately=True): 11 | """Makes a references plot. 12 | 13 | 1) Creates the JSON file for a d3 references plot 14 | 2) Places it and the corresponding HTML file in the working directory 15 | 3) (optionally) opens that HTML file up immediately in a web browser 16 | 17 | """ 18 | imports = {} 19 | totalv_ss = defaultdict(dict) 20 | for constraint in model.flat(): 21 | for varkey in constraint.vks: 22 | vlineage = varkey.lineagestr() 23 | clineage = constraint.lineagestr() 24 | if not vlineage: 25 | vlineage = "%s [%s]" % (varkey, varkey.unitstr()) 26 | for lin in (clineage, vlineage): 27 | if lin not in imports: 28 | imports[lin] = set() 29 | if vlineage != clineage: 30 | imports[clineage].add(vlineage) 31 | if constraint.v_ss: 32 | totalv_ss[clineage] += constraint.v_ss 33 | 34 | def clean_lineage(lineage, clusterdepth=2): 35 | prelineage = ".".join(lineage.split(".")[:clusterdepth]) 36 | last = "0".join(lineage.split(".")[clusterdepth:]) 37 | return "model."+prelineage + "." + last 38 | 39 | lines = ['jsondata = ['] 40 | for lineage, limports in imports.items(): 41 | name, short = clean_lineage(lineage), lineage.split(".")[-1] 42 | limports = map(clean_lineage, limports) 43 | lines.append( 44 | ' {"name":"%s","fullname":"%s","shortname":"%s","imports":%s},' 45 | % (name, lineage, short, repr(list(limports)).replace("'", '"'))) 46 | lines[-1] = lines[-1][:-1] 47 | lines.append("]") 48 | 49 | if totalv_ss: 50 | def get_total_senss(clineage, vlineage, normalize=False): 51 | v_ss = totalv_ss[clineage] 52 | num = sum(abs(ss) for vk, ss in v_ss.items() 53 | if vk.lineagestr() == vlineage) 54 | if not normalize: 55 | return num 56 | return num/sum(abs(ss) for ss in v_ss.values()) 57 | lines.append("globalsenss = {") 58 | for clineage, limports in imports.items(): 59 | if not limports: 60 | continue 61 | limports = {vl: get_total_senss(clineage, vl) for vl in limports} 62 | lines.append(' "%s": %s,' % 63 | (clineage, repr(limports).replace("'", '"'))) 64 | lines[-1] = lines[-1][:-1] 65 | lines.append("}") 66 | lines.append("normalizedsenss = {") 67 | for clineage, limports in imports.items(): 68 | if not limports: 69 | continue 70 | limports = {vl: get_total_senss(clineage, vl, normalize=True) 71 | for vl in limports} 72 | lines.append(' "%s": %s,' % 73 | (clineage, repr(limports).replace("'", '"'))) 74 | lines[-1] = lines[-1][:-1] 75 | lines.append("}") 76 | 77 | with open("referencesplot.json", "w") as f: 78 | f.write("\n".join(lines)) 79 | 80 | htmlfile = "referencesplot.html" 81 | if not os.path.isfile(htmlfile): 82 | shutil.copy(os.path.join(os.path.dirname(__file__), htmlfile), htmlfile) 83 | 84 | if openimmediately: 85 | webbrowser.open("file://" + os.path.join(os.getcwd(), htmlfile), 86 | autoraise=True) 87 | -------------------------------------------------------------------------------- /gpkit/nomials/__init__.py: -------------------------------------------------------------------------------- 1 | "Contains nomials, inequalities, and arrays" 2 | from .array import NomialArray 3 | from .core import Nomial 4 | from .data import NomialData 5 | from .map import NomialMap 6 | from .math import Monomial, Posynomial, Signomial 7 | from .math import MonomialEquality, PosynomialInequality 8 | from .math import SignomialInequality, SingleSignomialEquality 9 | from .substitution import parse_subs 10 | from .variables import Variable, ArrayVariable, VectorizableVariable 11 | 12 | VectorVariable = ArrayVariable 13 | -------------------------------------------------------------------------------- /gpkit/nomials/data.py: -------------------------------------------------------------------------------- 1 | """Machinery for exps, cs, varlocs data -- common to nomials and programs""" 2 | import numpy as np 3 | from ..keydict import KeySet 4 | from ..repr_conventions import ReprMixin 5 | from ..varkey import VarKey 6 | 7 | 8 | class NomialData(ReprMixin): 9 | """Object for holding cs, exps, and other basic 'nomial' properties. 10 | 11 | cs: array (coefficient of each monomial term) 12 | exps: tuple of {VarKey: float} (exponents of each monomial term) 13 | varlocs: {VarKey: list} (terms each variable appears in) 14 | units: pint.UnitsContainer 15 | """ 16 | # pylint: disable=too-many-instance-attributes 17 | _hashvalue = _varlocs = _exps = _cs = _varkeys = None 18 | 19 | def __init__(self, hmap): 20 | self.hmap = hmap 21 | self.units = self.hmap.units 22 | self.any_nonpositive_cs = any(c <= 0 for c in self.hmap.values()) 23 | 24 | def to(self, units): 25 | "Create new Signomial converted to new units" 26 | return self.__class__(self.hmap.to(units)) 27 | 28 | @property 29 | def exps(self): 30 | "Create exps or return cached exps" 31 | if self._exps is None: 32 | self._exps = tuple(self.hmap.keys()) 33 | return self._exps 34 | 35 | @property 36 | def cs(self): 37 | "Create cs or return cached cs" 38 | if self._cs is None: 39 | self._cs = np.array(list(self.hmap.values())) 40 | if self.hmap.units: 41 | # TODO: treat vars as dimensionless, it's a hack 42 | self._cs = self._cs*self.hmap.units 43 | return self._cs 44 | 45 | def __hash__(self): 46 | return hash(self.hmap) 47 | 48 | @property 49 | def vks(self): 50 | "Set of a NomialData's varkeys, created as necessary." 51 | vks = set() 52 | for exp in self.hmap: 53 | vks.update(exp) 54 | return vks 55 | 56 | @property # TODO: remove this 57 | def varkeys(self): 58 | "KeySet of a NomialData's varkeys, created as necessary." 59 | return KeySet(self.vks) 60 | 61 | def __eq__(self, other): 62 | "Equality test" 63 | if not hasattr(other, "hmap"): 64 | return NotImplemented 65 | if isinstance(other, VarKey): 66 | return False 67 | if self.hmap != other.hmap: 68 | return False 69 | if self.units != other.units: 70 | return False 71 | return True 72 | -------------------------------------------------------------------------------- /gpkit/nomials/substitution.py: -------------------------------------------------------------------------------- 1 | "Scripts to parse and collate substitutions" 2 | import warnings as pywarnings 3 | import numpy as np 4 | from ..small_scripts import splitsweep 5 | from ..keydict import KeySet 6 | 7 | 8 | def parse_subs(varkeys, substitutions, clean=False): 9 | "Seperates subs into the constants, sweeps, linkedsweeps actually present." 10 | constants, sweep, linkedsweep = {}, {}, {} 11 | if clean: 12 | for var in varkeys: 13 | if dict.__contains__(substitutions, var): 14 | sub = dict.__getitem__(substitutions, var) 15 | append_sub(sub, [var], constants, sweep, linkedsweep) 16 | else: 17 | if not hasattr(varkeys, "keymap"): 18 | varkeys = KeySet(varkeys) 19 | varkeys.update_keymap() 20 | if hasattr(substitutions, "keymap"): 21 | for var in varkeys.keymap: 22 | if dict.__contains__(substitutions, var): 23 | sub = dict.__getitem__(substitutions, var) 24 | keys = varkeys.keymap[var] 25 | append_sub(sub, keys, constants, sweep, linkedsweep) 26 | else: 27 | for var in substitutions: 28 | key = getattr(var, "key", var) 29 | if key in varkeys.keymap: 30 | sub, keys = substitutions[var], varkeys.keymap[key] 31 | append_sub(sub, keys, constants, sweep, linkedsweep) 32 | return constants, sweep, linkedsweep 33 | 34 | 35 | def append_sub(sub, keys, constants, sweep, linkedsweep): 36 | "Appends sub to constants, sweep, or linkedsweep." 37 | sweepsub, sweepval = splitsweep(sub) 38 | if sweepsub: # if the whole key is swept 39 | sub = sweepval 40 | for key in keys: 41 | if not key.shape or not getattr(sub, "shape", hasattr(sub, "__len__")): 42 | value = sub 43 | else: 44 | with pywarnings.catch_warnings(): 45 | pywarnings.filterwarnings("error") 46 | try: 47 | sub = np.array(sub) if not hasattr(sub, "shape") else sub 48 | except Warning: # pragma: no cover #TODO: coverage this 49 | # ragged nested sequences, eg [[2]], [3, 4]], in py3.7+ 50 | sub = np.array(sub, dtype=object) 51 | if key.shape == sub.shape: 52 | value = sub[key.idx] 53 | sweepel, sweepval = splitsweep(value) 54 | if sweepel: # if only an element is swept 55 | value = sweepval 56 | sweepsub = True 57 | elif sweepsub: 58 | try: 59 | np.broadcast(sub, np.empty(key.shape)) 60 | except ValueError: 61 | raise ValueError("cannot sweep variable %s of shape %s" 62 | " with array of shape %s; array shape" 63 | " must either be %s or %s" % 64 | (key.veckey, key.shape, sub.shape, 65 | key.shape, ("N",)+key.shape)) 66 | idx = (slice(None),)+key.descr["idx"] 67 | value = sub[idx] 68 | else: 69 | raise ValueError("cannot substitute array of shape %s for" 70 | " variable %s of shape %s." % 71 | (sub.shape, key.veckey, key.shape)) 72 | if hasattr(value, "__call__") and not hasattr(value, "key"): 73 | linkedsweep[key] = value 74 | elif sweepsub: 75 | sweep[key] = value 76 | else: 77 | try: 78 | assert np.isnan(value) 79 | except (AssertionError, TypeError, ValueError): 80 | constants[key] = value 81 | -------------------------------------------------------------------------------- /gpkit/small_scripts.py: -------------------------------------------------------------------------------- 1 | """Assorted helper methods""" 2 | from collections.abc import Iterable 3 | import numpy as np 4 | 5 | 6 | def broadcast_substitution(key, array): 7 | "Broadcasts input into the shape of a given key" 8 | return np.broadcast_to(array, reversed(key.key.shape)).T 9 | 10 | 11 | def veclinkedfn(linkedfn, i): 12 | "Generate an indexed linking function." 13 | def newlinkedfn(c): 14 | "Linked function that pulls out a particular index" 15 | return np.array(linkedfn(c))[i] 16 | return newlinkedfn 17 | 18 | 19 | def initsolwarning(result, category="uncategorized"): 20 | "Creates a results dictionary for a particular category of warning." 21 | if "warnings" not in result: 22 | result["warnings"] = {} 23 | if category not in result["warnings"]: 24 | result["warnings"][category] = [] 25 | 26 | 27 | def appendsolwarning(msg, data, result, category="uncategorized"): 28 | "Append a particular category of warnings to a solution." 29 | result["warnings"][category].append((msg, data)) 30 | 31 | 32 | @np.vectorize 33 | def isnan(element): 34 | "Determine if something of arbitrary type is a numpy nan." 35 | try: 36 | return np.isnan(element) 37 | except TypeError: 38 | return False 39 | 40 | 41 | def maybe_flatten(value): 42 | "Extract values from 0-d numpy arrays, if necessary" 43 | if hasattr(value, "size") and value.size == 1: 44 | return value.item() 45 | return value 46 | 47 | 48 | def try_str_without(item, excluded, *, latex=False): 49 | "Try to call item.str_without(excluded); fall back to str(item)" 50 | if latex and hasattr(item, "latex"): 51 | return item.latex(excluded) 52 | if hasattr(item, "str_without"): 53 | return item.str_without(excluded) 54 | return str(item) 55 | 56 | 57 | def mag(c): 58 | "Return magnitude of a Number or Quantity" 59 | return getattr(c, "magnitude", c) 60 | 61 | 62 | def is_sweepvar(sub): 63 | "Determines if a given substitution indicates a sweep." 64 | return splitsweep(sub)[0] 65 | 66 | 67 | def splitsweep(sub): 68 | "Splits a substitution into (is_sweepvar, sweepval)" 69 | try: 70 | sweep, value = sub 71 | if sweep is "sweep" and (isinstance(value, Iterable) or # pylint: disable=literal-comparison 72 | hasattr(value, "__call__")): 73 | return True, value 74 | except (TypeError, ValueError): 75 | pass 76 | return False, None 77 | -------------------------------------------------------------------------------- /gpkit/solvers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convexengineering/gpkit/bea1234606649dd11a2e59b610b2ba8b8c8adfae/gpkit/solvers/__init__.py -------------------------------------------------------------------------------- /gpkit/solvers/cvxopt.py: -------------------------------------------------------------------------------- 1 | "Implements the GPkit interface to CVXOPT" 2 | import numpy as np 3 | from cvxopt import spmatrix, matrix 4 | from cvxopt.solvers import gp 5 | from gpkit.exceptions import UnknownInfeasible, DualInfeasible 6 | 7 | 8 | # pylint: disable=too-many-locals,too-many-statements 9 | def optimize(*, c, A, k, meq_idxs, use_leqs=True, **kwargs): 10 | """Interface to the CVXOPT solver 11 | 12 | Definitions 13 | ----------- 14 | "[a,b] array of floats" indicates array-like data with shape [a,b] 15 | n is the number of monomials in the gp 16 | m is the number of variables in the gp 17 | p is the number of posynomial constraints in the gp 18 | 19 | Arguments 20 | --------- 21 | c : floats array of shape n 22 | Coefficients of each monomial 23 | A : floats array of shape (n, m) 24 | Exponents of the various free variables for each monomial. 25 | k : ints array of shape p+1 26 | k[0] is the number of monomials (rows of A) present in the objective 27 | k[1:] is the number of monomials present in each constraint 28 | 29 | Returns 30 | ------- 31 | dict 32 | Contains the following keys 33 | "success": bool 34 | "objective_sol" float 35 | Optimal value of the objective 36 | "primal_sol": floats array of size m 37 | Optimal value of free variables. Note: not in logspace. 38 | "dual_sol": floats array of size p 39 | Optimal value of the dual variables, in logspace. 40 | """ 41 | log_c = np.log(np.array(c)) 42 | A = A.tocsr() 43 | maxcol = A.shape[1]-1 44 | lse_mons, lin_mons, leq_mons = [], [], [] 45 | lse_posys, lin_posys, leq_posys = [], [], [] 46 | constraint_hashes = set() 47 | for i, n_monomials in enumerate(k): 48 | start = sum(k[:i]) 49 | mons = range(start, start+k[i]) 50 | A_m = A[mons, :].tocoo() 51 | chash = hash((c[i], tuple(A_m.data), tuple(A_m.row), tuple(A_m.col))) 52 | if chash in constraint_hashes: 53 | continue # already got it 54 | if i: # skip cost posy 55 | constraint_hashes.add(chash) 56 | if use_leqs and start in meq_idxs.all: 57 | if start in meq_idxs.first_half: 58 | leq_posys.append(i) 59 | leq_mons.extend(mons) 60 | elif i != 0 and n_monomials == 1: 61 | lin_mons.extend(mons) 62 | lin_posys.append(i) 63 | else: 64 | lse_mons.extend(mons) 65 | lse_posys.append(i) 66 | if leq_mons: 67 | A_leq = A[leq_mons, :].tocoo() 68 | log_c_leq = log_c[leq_mons] 69 | kwargs["A"] = spmatrix([float(r) for r in A_leq.data]+[0], 70 | [int(r) for r in A_leq.row]+[0], 71 | [int(r) for r in A_leq.col]+[maxcol], tc="d") 72 | kwargs["b"] = matrix(-log_c_leq) 73 | if lin_mons: 74 | A_lin = A[lin_mons, :].tocoo() 75 | log_c_lin = log_c[lin_mons] 76 | kwargs["G"] = spmatrix([float(r) for r in A_lin.data]+[0], 77 | [int(r) for r in A_lin.row]+[0], 78 | [int(r) for r in A_lin.col]+[maxcol], tc="d") 79 | kwargs["h"] = matrix(-log_c_lin) 80 | k_lse = [k[i] for i in lse_posys] 81 | A_lse = A[lse_mons, :].tocoo() 82 | log_c_lse = log_c[lse_mons] 83 | F = spmatrix([float(r) for r in A_lse.data]+[0], 84 | [int(r) for r in A_lse.row]+[0], 85 | [int(r) for r in A_lse.col]+[maxcol], tc="d") 86 | g = matrix(log_c_lse) 87 | try: 88 | solution = gp(k_lse, F, g, **kwargs) 89 | except ValueError as e: 90 | raise DualInfeasible() from e 91 | if solution["status"] != "optimal": 92 | raise UnknownInfeasible("solution status " + repr(solution["status"])) 93 | la = np.zeros(len(k)) 94 | la[lin_posys] = list(solution["zl"]) 95 | la[lse_posys] = [1.] + list(solution["znl"]) 96 | for leq_posy, yi in zip(leq_posys, solution["y"]): 97 | if yi >= 0: 98 | la[leq_posy] = yi 99 | else: # flip it around to the other "inequality" 100 | la[leq_posy+1] = -yi 101 | return dict(status=solution["status"], 102 | objective=np.exp(solution["primal objective"]), 103 | primal=np.ravel(solution["x"]), 104 | la=la) 105 | -------------------------------------------------------------------------------- /gpkit/tests/__init__.py: -------------------------------------------------------------------------------- 1 | "GPkit testing module" 2 | 3 | from .run_tests import run 4 | -------------------------------------------------------------------------------- /gpkit/tests/from_paths.py: -------------------------------------------------------------------------------- 1 | "Runs each file listed in pwd/TESTS as a test" 2 | import unittest 3 | import os 4 | import re 5 | from gpkit import settings 6 | from gpkit.tests.helpers import generate_example_tests, new_test 7 | 8 | 9 | class TestFiles(unittest.TestCase): 10 | "Stub to be filled with files in $pwd/TESTS" 11 | 12 | 13 | def clean(string): 14 | """Parses string into valid python variable name 15 | 16 | https://stackoverflow.com/questions/3303312/ 17 | how-do-i-convert-a-string-to-a-valid-variable-name-in-python 18 | """ 19 | string = re.sub('[^0-9a-zA-Z_]', '_', string) # Replace invalid with _ 20 | return re.sub('^[^a-zA-Z_]+', '', string) # Remove leading until valid 21 | 22 | 23 | def add_filetest(testclass, path): 24 | "Add test that imports the given path and runs its test() function" 25 | path = path.strip() 26 | print("adding test for", repr(path)) 27 | 28 | def test_fn(self): 29 | top_level = os.getcwd() 30 | try: 31 | dirname = os.path.dirname(path) 32 | if dirname: 33 | os.chdir(os.path.dirname(path)) 34 | mod = __import__(os.path.basename(path)[:-3]) 35 | if not hasattr(mod, "test"): 36 | self.fail("file '%s' had no `test` function." % path) 37 | mod.test() 38 | finally: 39 | os.chdir(top_level) 40 | 41 | setattr(testclass, "test_"+clean(path), test_fn) 42 | 43 | 44 | def newtest_fn(name, solver, import_dict, path): 45 | "Doubly nested callbacks to run the test with `getattr(self, name)()`" 46 | return new_test(name, solver, import_dict, path, 47 | testfn=(lambda name, import_dict, path: 48 | lambda self: getattr(self, name)())) # pylint:disable=undefined-variable 49 | 50 | 51 | def run(filename="TESTS", xmloutput=False, skipsolvers="look around"): 52 | "Parse and run paths from a given file for each solver" 53 | with open(filename, "r") as f: 54 | for path in f: 55 | add_filetest(TestFiles, path) 56 | if skipsolvers == "look around": 57 | from .test_repo import get_settings 58 | skipsolvers = get_settings()["skipsolvers"] 59 | solvers = [s for s in settings["installed_solvers"] 60 | if not skipsolvers or s not in skipsolvers] 61 | tests = generate_example_tests("", [TestFiles], solvers, 62 | newtest_fn=newtest_fn) 63 | if not solvers: 64 | # Dummy test in case all installed solvers are skipped. 65 | tests[0].test_dummy = lambda self: None 66 | from gpkit.tests.run_tests import run as run_ 67 | run_(tests=tests, xmloutput=xmloutput) 68 | -------------------------------------------------------------------------------- /gpkit/tests/run_tests.py: -------------------------------------------------------------------------------- 1 | """Script for running all gpkit unit tests""" 2 | from gpkit.tests.helpers import run_tests 3 | 4 | 5 | def import_tests(): 6 | """Get a list of all GPkit unit test TestCases""" 7 | tests = [] 8 | 9 | from gpkit.tests import t_tools 10 | tests += t_tools.TESTS 11 | 12 | from gpkit.tests import t_sub 13 | tests += t_sub.TESTS 14 | 15 | from gpkit.tests import t_vars 16 | tests += t_vars.TESTS 17 | 18 | from gpkit.tests import t_nomials 19 | tests += t_nomials.TESTS 20 | 21 | from gpkit.tests import t_constraints 22 | tests += t_constraints.TESTS 23 | 24 | from gpkit.tests import t_nomial_array 25 | tests += t_nomial_array.TESTS 26 | 27 | from gpkit.tests import t_model 28 | tests += t_model.TESTS 29 | 30 | from gpkit.tests import t_solution_array 31 | tests += t_solution_array.TESTS 32 | 33 | from gpkit.tests import t_small 34 | tests += t_small.TESTS 35 | 36 | from gpkit.tests import t_examples 37 | tests += t_examples.TESTS 38 | 39 | from gpkit.tests import t_keydict 40 | tests += t_keydict.TESTS 41 | 42 | return tests 43 | 44 | 45 | def run(xmloutput=False, tests=None, verbosity=1): 46 | """Run all gpkit unit tests. 47 | 48 | Arguments 49 | --------- 50 | xmloutput: bool 51 | If true, generate xml output files for continuous integration 52 | """ 53 | if tests is None: 54 | tests = import_tests() 55 | if xmloutput: 56 | run_tests(tests, xmloutput='test_reports') 57 | else: # pragma: no cover 58 | run_tests(tests, verbosity=verbosity) 59 | 60 | if __name__ == "__main__": # pragma: no cover 61 | run() 62 | -------------------------------------------------------------------------------- /gpkit/tests/t_keydict.py: -------------------------------------------------------------------------------- 1 | """Test KeyDict class""" 2 | import unittest 3 | import numpy as np 4 | from gpkit import Variable, VectorVariable 5 | import gpkit 6 | from gpkit.keydict import KeyDict 7 | from gpkit.tests.helpers import run_tests 8 | 9 | 10 | class TestKeyDict(unittest.TestCase): 11 | """TestCase for the KeyDict class""" 12 | 13 | def test_nonnumeric(self): 14 | x = VectorVariable(2, "x") 15 | kd = KeyDict() 16 | kd[x[1]] = "2" 17 | self.assertTrue(np.isnan(kd[x[0]])) 18 | self.assertEqual(kd[x[1]], "2") 19 | self.assertNotIn(x[0], kd) 20 | self.assertIn(x[1], kd) 21 | 22 | def test_setattr(self): 23 | kd = KeyDict() 24 | x = Variable("x", lineage=(("test", 0),)) 25 | kd[x] = 1 26 | self.assertIn(x, kd) 27 | self.assertEqual(set(kd), set([x.key])) 28 | 29 | def test_getattr(self): 30 | kd = KeyDict() 31 | x = Variable("x", lineage=[("Motor", 0)]) 32 | kd[x] = 52 33 | self.assertEqual(kd[x], 52) 34 | self.assertEqual(kd[x.key], 52) 35 | self.assertEqual(kd["x"], 52) 36 | self.assertEqual(kd["Motor.x"], 52) 37 | self.assertNotIn("x.Someothermodelname", kd) 38 | 39 | def test_failed_getattr(self): 40 | kd = KeyDict() 41 | with self.assertRaises(KeyError): 42 | _ = kd["waldo"] 43 | # issue 893: failed __getitem__ caused state change 44 | self.assertNotIn("waldo", kd) 45 | waldo = Variable("waldo") 46 | kd.update({waldo: 5}) 47 | res = kd["waldo"] 48 | self.assertEqual(res, 5) 49 | self.assertIn("waldo", kd) 50 | 51 | def test_vector(self): 52 | v = VectorVariable(3, "v") 53 | kd = KeyDict() 54 | kd[v] = np.array([2, 3, 4]) 55 | self.assertTrue(all(kd[v] == kd[v.key])) # pylint:disable=no-member 56 | self.assertTrue(all(kd["v"] == np.array([2, 3, 4]))) 57 | self.assertEqual(v[0].key.idx, (0,)) 58 | self.assertEqual(kd[v][0], kd[v[0]]) 59 | self.assertEqual(kd[v][0], 2) 60 | kd[v[0]] = 6 61 | self.assertEqual(kd[v][0], kd[v[0]]) 62 | self.assertEqual(kd[v][0], 6) 63 | self.assertTrue(all(kd[v] == np.array([6, 3, 4]))) 64 | v = VectorVariable(3, "v", "m") 65 | kd[v] = np.array([2, 3, 4]) 66 | if gpkit.units: 67 | kd[v[0]] = gpkit.units("inch") 68 | 69 | 70 | TESTS = [TestKeyDict] 71 | 72 | 73 | if __name__ == "__main__": # pragma: no cover 74 | run_tests(TESTS) 75 | -------------------------------------------------------------------------------- /gpkit/tests/t_small.py: -------------------------------------------------------------------------------- 1 | """Tests for small_classes.py and small_scripts.py""" 2 | import unittest 3 | from gpkit.small_classes import HashVector 4 | from gpkit.repr_conventions import unitstr 5 | import gpkit 6 | 7 | 8 | class TestHashVector(unittest.TestCase): 9 | """TestCase for the HashVector class""" 10 | 11 | def test_init(self): 12 | """Make sure HashVector acts like a dict""" 13 | # args and kwargs 14 | hv = HashVector([(2, 3), (1, 10)], dog='woof') 15 | self.assertTrue(isinstance(hv, dict)) 16 | self.assertEqual(hv, {2: 3, 1: 10, 'dog': 'woof'}) 17 | # no args 18 | self.assertEqual(HashVector(), {}) 19 | # creation from dict 20 | self.assertEqual(HashVector({'x': 7}), {'x': 7}) 21 | 22 | def test_neg(self): 23 | """Test negation""" 24 | hv = HashVector(x=7, y=0, z=-1) 25 | self.assertEqual(-hv, {'x': -7, 'y': 0, 'z': 1}) 26 | 27 | def test_pow(self): 28 | """Test exponentiation""" 29 | hv = HashVector(x=4, y=0, z=1) 30 | self.assertEqual(hv**0.5, {'x': 2, 'y': 0, 'z': 1}) 31 | with self.assertRaises(TypeError): 32 | _ = hv**hv 33 | with self.assertRaises(TypeError): 34 | _ = hv**"a" 35 | 36 | def test_mul_add(self): 37 | """Test multiplication and addition""" 38 | a = HashVector(x=1, y=7) 39 | b = HashVector() 40 | c = HashVector(x=3, z=4) 41 | # nonsense multiplication 42 | with self.assertRaises(TypeError): 43 | _ = a * set() 44 | # multiplication and addition by scalars 45 | r = a*0 46 | self.assertEqual(r, HashVector(x=0, y=0)) 47 | self.assertTrue(isinstance(r, HashVector)) 48 | r = a - 2 49 | self.assertEqual(r, HashVector(x=-1, y=5)) 50 | self.assertTrue(isinstance(r, HashVector)) 51 | with self.assertRaises(TypeError): 52 | _ = r + "a" 53 | # multiplication and addition by dicts 54 | self.assertEqual(a + b, a) 55 | self.assertEqual(a + b + c, HashVector(x=4, y=7, z=4)) 56 | 57 | 58 | class TestSmallScripts(unittest.TestCase): 59 | """TestCase for gpkit.small_scripts""" 60 | def test_unitstr(self): 61 | x = gpkit.Variable("x", "ft") 62 | # pint issue 356 63 | footstrings = ("ft", "foot") # backwards compatibility with pint 0.6 64 | if gpkit.units: 65 | self.assertEqual(unitstr(gpkit.Variable("n", "count")), "count") 66 | self.assertIn(unitstr(x), footstrings) 67 | self.assertIn(unitstr(x.key), footstrings) 68 | self.assertEqual(unitstr(gpkit.Variable("y"), dimless="---"), "---") 69 | self.assertEqual(unitstr(None, dimless="--"), "--") 70 | 71 | def test_pint_366(self): 72 | # test for https://github.com/hgrecco/pint/issues/366 73 | if gpkit.units: 74 | self.assertIn(unitstr(gpkit.units("nautical_mile")), 75 | ("nmi", "nautical_mile")) 76 | self.assertEqual(gpkit.units("nautical_mile"), gpkit.units("nmi")) 77 | 78 | 79 | TESTS = [TestHashVector, TestSmallScripts] 80 | 81 | 82 | if __name__ == "__main__": # pragma: no cover 83 | # pylint: disable=wrong-import-position 84 | from gpkit.tests.helpers import run_tests 85 | run_tests(TESTS) 86 | -------------------------------------------------------------------------------- /gpkit/tests/t_solution_array.py: -------------------------------------------------------------------------------- 1 | """Tests for SolutionArray class""" 2 | import unittest 3 | import numpy as np 4 | from gpkit import Variable, VectorVariable, Model, SignomialsEnabled 5 | import gpkit 6 | from gpkit.solution_array import var_table 7 | from gpkit.varkey import VarKey 8 | from gpkit.small_classes import Strings, Quantity 9 | 10 | 11 | class TestSolutionArray(unittest.TestCase): 12 | """Unit tests for the SolutionArray class""" 13 | 14 | def test_call(self): 15 | A = Variable('A', '-', 'Test Variable') 16 | prob = Model(A, [A >= 1]) 17 | sol = prob.solve(verbosity=0) 18 | self.assertAlmostEqual(sol(A), 1.0, 8) 19 | 20 | def test_call_units(self): 21 | # test from issue541 22 | x = Variable("x", 10, "ft") 23 | y = Variable("y", "m") 24 | m = Model(y, [y >= x]) 25 | sol = m.solve(verbosity=0) 26 | self.assertAlmostEqual(sol("y")/sol("x"), 1.0, 6) 27 | self.assertAlmostEqual(sol(x)/sol(y), 1.0, 6) 28 | 29 | def test_call_vector(self): 30 | n = 5 31 | x = VectorVariable(n, 'x') 32 | prob = Model(sum(x), [x >= 2.5]) 33 | sol = prob.solve(verbosity=0) 34 | solx = sol(x) 35 | self.assertEqual(type(solx), Quantity) 36 | self.assertEqual(type(sol["variables"][x]), np.ndarray) 37 | self.assertEqual(solx.shape, (n,)) 38 | for i in range(n): 39 | self.assertAlmostEqual(solx[i], 2.5, places=4) 40 | 41 | def test_subinto(self): 42 | Nsweep = 20 43 | Pvals = np.linspace(13, 24, Nsweep) 44 | H_max = Variable("H_max", 10, "m", "Length") 45 | A_min = Variable("A_min", 10, "m^2", "Area") 46 | P_max = Variable("P", Pvals, "m", "Perimeter") 47 | H = Variable("H", "m", "Length") 48 | W = Variable("W", "m", "Width") 49 | m = Model(12/(W*H**3), 50 | [H <= H_max, 51 | H*W >= A_min, 52 | P_max >= 2*H + 2*W]) 53 | sol = m.solve(verbosity=0) 54 | Psol = sol.subinto(P_max) 55 | self.assertEqual(len(Psol), Nsweep) 56 | self.assertAlmostEqual(0*gpkit.ureg.m, 57 | np.max(np.abs(Pvals*gpkit.ureg.m - Psol))) 58 | self.assertAlmostEqual(0*gpkit.ureg.m, 59 | np.max(np.abs(Psol - sol(P_max)))) 60 | 61 | def test_table(self): 62 | x = Variable('x') 63 | gp = Model(x, [x >= 12]) 64 | sol = gp.solve(verbosity=0) 65 | tab = sol.table() 66 | self.assertTrue(isinstance(tab, Strings)) 67 | 68 | def test_units_sub(self): 69 | # issue 809 70 | T = Variable("T", "N", "thrust") 71 | Tmin = Variable("T_{min}", "N", "minimum thrust") 72 | m = Model(T, [T >= Tmin]) 73 | tminsub = 1000 * gpkit.ureg.lbf 74 | m.substitutions.update({Tmin: tminsub}) 75 | sol = m.solve(verbosity=0) 76 | self.assertAlmostEqual(sol(Tmin), tminsub) 77 | self.assertFalse( 78 | "1000N" in 79 | sol.table().replace(" ", "").replace("[", "").replace("]", "")) 80 | 81 | def test_key_options(self): 82 | # issue 993 83 | x = Variable("x") 84 | y = Variable("y") 85 | with SignomialsEnabled(): 86 | m = Model(y, [y + 6*x >= 13 + x**2]) 87 | msol = m.localsolve(verbosity=0) 88 | spsol = m.sp().localsolve(verbosity=0) # pylint: disable=no-member 89 | gpsol = m.program.gps[-1].solve(verbosity=0) 90 | self.assertEqual(msol(x), msol("x")) 91 | self.assertEqual(spsol(x), spsol("x")) 92 | self.assertEqual(gpsol(x), gpsol("x")) 93 | self.assertEqual(msol(x), spsol(x)) 94 | self.assertEqual(msol(x), gpsol(x)) 95 | 96 | 97 | class TestResultsTable(unittest.TestCase): 98 | """TestCase for var_table()""" 99 | 100 | def test_nan_printing(self): 101 | """Test that solution prints when it contains nans""" 102 | x = VarKey(name='x') 103 | data = {x: np.array([np.nan, 1, 1, 1, 1])} 104 | title = "Free variables" 105 | printstr = "\n".join(var_table(data, title)) 106 | self.assertTrue(" - " in printstr) # nan is printed as " - " 107 | self.assertTrue(title in printstr) 108 | 109 | def test_result_access(self): 110 | x = Variable("x") 111 | y = Variable("y") 112 | with SignomialsEnabled(): 113 | sig = (y + 6*x >= 13 + x**2) 114 | m = Model(y, [sig]) 115 | sol = m.localsolve(verbosity=0) 116 | self.assertTrue(all([isinstance(gp.result.table(), Strings) 117 | for gp in m.program.gps])) 118 | self.assertAlmostEqual(sol["cost"]/4.0, 1.0, 5) 119 | self.assertAlmostEqual(sol("x")/3.0, 1.0, 3) 120 | 121 | TESTS = [TestSolutionArray, TestResultsTable] 122 | 123 | if __name__ == "__main__": # pragma: no cover 124 | # pylint: disable=wrong-import-position 125 | from gpkit.tests.helpers import run_tests 126 | run_tests(TESTS) 127 | -------------------------------------------------------------------------------- /gpkit/tests/test_repo.py: -------------------------------------------------------------------------------- 1 | "Implements tests for all external repositories." 2 | import os 3 | import sys 4 | import subprocess 5 | from time import sleep 6 | from collections import defaultdict 7 | 8 | 9 | def test_repo(repo=".", xmloutput=False): 10 | """Test repository. 11 | 12 | If no repo name given, runs in current directory. 13 | Otherwise, assumes is in directory above the repo 14 | with a shared gplibrary repository. 15 | """ 16 | os.chdir(repo) 17 | settings = get_settings() 18 | print("") 19 | print("SETTINGS") 20 | print(settings) 21 | print("") 22 | 23 | if repo == "." and not os.path.isdir("gpkitmodels"): 24 | git_clone("gplibrary") 25 | pip_install("gplibrary", local=True) 26 | 27 | # install dependencies other than gplibrary 28 | if settings["pip install"]: 29 | for package in settings["pip install"].split(","): 30 | package = package.strip() 31 | pip_install(package) 32 | if os.path.isfile("setup.py"): 33 | pip_install(".") 34 | 35 | skipsolvers = None 36 | if "skipsolvers" in settings: 37 | skipsolvers = [s.strip() for s in settings["skipsolvers"].split(",")] 38 | 39 | testpy = ("from gpkit.tests.from_paths import run;" 40 | "run(xmloutput=%s, skipsolvers=%s)" 41 | % (xmloutput, skipsolvers)) 42 | subprocess.call(["python", "-c", testpy]) 43 | if repo != ".": 44 | os.chdir("..") 45 | 46 | 47 | def test_repos(repos=None, xmloutput=False, ingpkitmodels=False): 48 | """Get the list of external repos to test, and test. 49 | 50 | Arguments 51 | --------- 52 | xmloutput : bool 53 | True if the tests should produce xml reports 54 | 55 | ingpkitmodels : bool 56 | False if you're in the gpkitmodels directory that should be considered 57 | as the default. (overriden by repo-specific branch specifications) 58 | """ 59 | if not ingpkitmodels: 60 | git_clone("gplibrary") 61 | repos_list_filename = "gplibrary"+os.sep+"EXTERNALTESTS" 62 | pip_install("gplibrary", local=True) 63 | else: 64 | print("USING LOCAL DIRECTORY AS GPKITMODELS DIRECTORY") 65 | repos_list_filename = "EXTERNALTESTS" 66 | pip_install(".", local=True) 67 | repos = [line.strip() for line in open(repos_list_filename, "r")] 68 | for repo in repos: 69 | git_clone(repo) 70 | test_repo(repo, xmloutput) 71 | 72 | 73 | def get_settings(): 74 | "Gets settings from a TESTCONFIG file" 75 | settings = defaultdict(str) 76 | if os.path.isfile("TESTCONFIG"): 77 | with open("TESTCONFIG", "r") as f: 78 | for line in f: 79 | if len(line.strip().split(" : ")) > 1: 80 | key, value = line.strip().split(" : ") 81 | settings[key] = value 82 | return settings 83 | 84 | 85 | def git_clone(repo, branch="master"): 86 | "Tries several times to clone a given repository" 87 | cmd = ["git", "clone", "--depth", "1"] 88 | cmd += ["-b", branch] 89 | cmd += ["https://github.com/convexengineering/%s.git" % repo] 90 | call_and_retry(cmd) 91 | 92 | 93 | def pip_install(package, local=False): 94 | "Tries several times to install a pip package" 95 | if sys.platform == "win32": 96 | cmd = ["pip"] 97 | else: 98 | cmd = ["python", os.environ["PIP"]] 99 | cmd += ["install"] 100 | if local: 101 | cmd += ["--no-cache-dir", "--no-deps", "-e"] 102 | cmd += [package] 103 | call_and_retry(cmd) 104 | 105 | 106 | def call_and_retry(cmd, max_iterations=5, delay=5): 107 | "Tries max_iterations times (waiting d each time) to run a command" 108 | iterations = 0 109 | return_code = None 110 | print("calling", cmd) 111 | while return_code != 0 and iterations < max_iterations: 112 | iterations += 1 113 | print(" attempt", iterations) 114 | return_code = subprocess.call(cmd) 115 | sleep(delay) 116 | return return_code 117 | -------------------------------------------------------------------------------- /gpkit/tools/__init__.py: -------------------------------------------------------------------------------- 1 | "Contains miscellaneous tools including fmincon comparison tool" 2 | from .autosweep import autosweep_1d 3 | from .tools import te_exp_minus1 4 | -------------------------------------------------------------------------------- /gpkit/tools/tools.py: -------------------------------------------------------------------------------- 1 | """Non-application-specific convenience methods for GPkit""" 2 | import numpy as np 3 | 4 | 5 | def te_exp_minus1(posy, nterm): 6 | """Taylor expansion of e^{posy} - 1 7 | 8 | Arguments 9 | --------- 10 | posy : gpkit.Posynomial 11 | Variable or expression to exponentiate 12 | nterm : int 13 | Number of non-constant terms in resulting Taylor expansion 14 | 15 | Returns 16 | ------- 17 | gpkit.Posynomial 18 | Taylor expansion of e^{posy} - 1, carried to nterm terms 19 | """ 20 | res = 0 21 | factorial_denom = 1 22 | for i in range(1, nterm + 1): 23 | factorial_denom *= i 24 | res += posy**i / factorial_denom 25 | return res 26 | 27 | 28 | def te_secant(var, nterm): 29 | """Taylor expansion of secant(var). 30 | 31 | Arguments 32 | --------- 33 | var : gpkit.monomial 34 | Variable or expression argument 35 | nterm : int 36 | Number of non-constant terms in resulting Taylor expansion 37 | 38 | Returns 39 | ------- 40 | gpkit.Posynomial 41 | Taylor expansion of secant(x), carried to nterm terms 42 | """ 43 | # The first 12 Euler Numbers 44 | E2n = np.asarray([1.0, 45 | 5, 46 | 61, 47 | 1385, 48 | 50521, 49 | 2702765, 50 | 199360981, 51 | 19391512145, 52 | 2404879675441, 53 | 370371188237525, 54 | 69348874393137901, 55 | 15514534163557086905]) 56 | if nterm > 12: 57 | n_extend = np.asarray(range(13, nterm+1)) 58 | E2n_add = (8 * np.sqrt(n_extend/np.pi) 59 | * (4*n_extend/(np.pi * np.exp(1)))**(2*n_extend)) 60 | E2n = np.append(E2n, E2n_add) 61 | 62 | res = 1 63 | factorial_denom = 1 64 | for i in range(1, nterm + 1): 65 | factorial_denom *= ((2*i)*(2*i-1)) 66 | res = res + var**(2*i) * E2n[i-1] / factorial_denom 67 | return res 68 | 69 | 70 | def te_tangent(var, nterm): 71 | """Taylor expansion of tangent(var). 72 | 73 | Arguments 74 | --------- 75 | var : gpkit.monomial 76 | Variable or expression argument 77 | nterm : int 78 | Number of non-constant terms in resulting Taylor expansion 79 | 80 | Returns 81 | ------- 82 | gpkit.Posynomial 83 | Taylor expansion of tangent(x), carried to nterm terms 84 | """ 85 | if nterm > 15: 86 | raise NotImplementedError("Tangent expansion not implemented above" 87 | " 15 terms") 88 | 89 | # The first 15 Bernoulli Numbers 90 | B2n = np.asarray([1/6, 91 | -1/30, 92 | 1/42, 93 | -1/30, 94 | 5/66, 95 | -691/2730, 96 | 7/6, 97 | -3617/510, 98 | 43867/798, 99 | -174611/330, 100 | 854513/138, 101 | -236364091/2730, 102 | 8553103/6, 103 | -23749461029/870, 104 | 8615841276005/14322]) 105 | 106 | res = 0 107 | factorial_denom = 1 108 | for i in range(1, nterm + 1): 109 | factorial_denom *= ((2*i)*(2*i-1)) 110 | res += ((-1)**(i-1) * 2**(2*i) * (2**(2*i) - 1) * 111 | B2n[i-1] / factorial_denom * var**(2*i-1)) 112 | return res 113 | -------------------------------------------------------------------------------- /gpkit/units.py: -------------------------------------------------------------------------------- 1 | "wraps pint in gpkit monomials" 2 | import pint 3 | ureg = pint.UnitRegistry() # pylint: disable=invalid-name 4 | ureg.define("USD = [money] = $") 5 | pint.set_application_registry(ureg) 6 | Quantity = ureg.Quantity 7 | DimensionalityError = pint.DimensionalityError 8 | QTY_CACHE = {} 9 | 10 | 11 | def qty(unit): 12 | "Returns a Quantity, caching the result for future retrievals" 13 | if unit not in QTY_CACHE: 14 | QTY_CACHE[unit] = Quantity(1, unit) 15 | return QTY_CACHE[unit] 16 | 17 | 18 | class GPkitUnits: 19 | "Return Monomials instead of Quantitites" 20 | division_cache = {} 21 | multiplication_cache = {} 22 | monomial_cache = {} 23 | 24 | def __call__(self, unity): 25 | "Returns a unit Monomial, caching the result for future retrievals" 26 | from . import Monomial 27 | if unity not in self.monomial_cache: 28 | self.monomial_cache[unity] = Monomial(qty(unity)) 29 | return self.monomial_cache[unity] 30 | 31 | __getattr__ = __call__ 32 | 33 | def of_division(self, numerator, denominator): 34 | "Cached unit division. Requires Quantity inputs." 35 | if numerator.units is denominator.units: 36 | return 1 37 | key = (id(numerator.units), id(denominator.units)) 38 | try: 39 | return self.division_cache[key] 40 | except KeyError: 41 | if numerator.units and denominator.units: 42 | conversion = numerator.units/denominator.units 43 | else: 44 | conversion = numerator.units or 1/denominator.units 45 | try: 46 | self.division_cache[key] = float(conversion) 47 | except DimensionalityError: 48 | raise DimensionalityError(numerator, denominator) 49 | return self.division_cache[key] 50 | 51 | def of_product(self, thing1, thing2): 52 | "Cached unit division. Requires united inputs." 53 | key = (thing1.units, thing2.units) 54 | try: 55 | return self.multiplication_cache[key] 56 | except KeyError: 57 | mul_units = qty((thing1*thing2).units) 58 | try: 59 | self.multiplication_cache[key] = (None, float(mul_units)) 60 | except DimensionalityError: 61 | self.multiplication_cache[key] = (mul_units, None) 62 | return self.multiplication_cache[key] 63 | 64 | 65 | units = GPkitUnits() 66 | -------------------------------------------------------------------------------- /linecount.sh: -------------------------------------------------------------------------------- 1 | pylint --rcfile .pylintrc gpkit | grep "Raw metrics" -A 14 | tail -n 13 2 | echo "Just tests" 3 | pylint --rcfile .pylintrc gpkit/tests | grep "Raw metrics" -A 14 | tail -n 13 4 | -------------------------------------------------------------------------------- /pylint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This means you can run the script anywhere in the repo, but not outside 4 | WORKSPACE=$(git rev-parse --show-toplevel) 5 | 6 | # Calling pylint directly will not work correcly in a virtualenv if pylint is not installed in the venv 7 | # Using python with the pylint script will always work properly in a virtualenv 8 | PYLINT=`which pylint` 9 | echo $PYLINT 10 | $PYLINT --version 11 | 12 | # Add gpkit to the python path so that pylint can import gpkit when analyzing the examples directory 13 | export PYTHONPATH=$PYTHONPATH:$WORKSPACE/gpkit/ 14 | 15 | python3 $PYLINT --rcfile=$WORKSPACE/.pylintrc $@ $WORKSPACE/gpkit/ 16 | 17 | python3 $PYLINT --rcfile=$WORKSPACE/.pylintrc --disable=superfluous-parens,undefined-variable,no-member,not-callable,attribute-defined-outside-init,invalid-name,too-many-locals,redefined-outer-name,wrong-import-position $@ $WORKSPACE/docs/source/examples/*.py 18 | -------------------------------------------------------------------------------- /rtd_requirements.txt: -------------------------------------------------------------------------------- 1 | pint 2 | adce 3 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | cp gpkit/env/settings . 2 | sed -i '1s/.*/installed_solvers : cvxopt/' gpkit/env/settings 3 | cat gpkit/env/settings 4 | python -c "import gpkit.tests; gpkit.tests.run()" && mv settings gpkit/env 5 | rm *.pkl 6 | rm *.pgz 7 | rm solution.* 8 | rm referencesplot.* 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Standard Python setup script for gpkit""" 2 | import os 3 | from distutils.core import setup 4 | 5 | 6 | LICENSE = """The MIT License (MIT) 7 | 8 | Copyright (c) 2022 Edward Burnell 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE.""" 27 | 28 | # create blank settings file to replace anything cached 29 | THIS_DIR = os.path.dirname(__file__) 30 | try: 31 | with open(os.sep.join([THIS_DIR, "gpkit", "env", "settings"]), "w") as f: 32 | f.write("installed_solvers : ") 33 | except IOError: 34 | pass 35 | 36 | # read the README file 37 | with open(os.path.join(THIS_DIR, "README.md"), encoding="utf-8") as f: 38 | LONG_DESCRIPTION = f.read() 39 | 40 | setup( 41 | name="gpkit", 42 | description="Package for defining and manipulating geometric " 43 | "programming models.", 44 | author="Edward Burnell", 45 | author_email="gpkit@mit.edu", 46 | url="https://www.github.com/convexengineering/gpkit", 47 | python_requires=">=3.5.2", 48 | install_requires=["numpy >= 1.16.4", "pint >= 0.8.1", "plotly", 49 | "scipy", "adce", "cvxopt >= 1.1.8", 50 | "matplotlib"], 51 | version="1.1.1", 52 | packages=["gpkit", "gpkit.tools", "gpkit.interactive", "gpkit.constraints", 53 | "gpkit.nomials", "gpkit.tests", "gpkit.solvers"], 54 | package_data={"gpkit": ["env/settings"]}, 55 | license=LICENSE, 56 | long_description=LONG_DESCRIPTION, 57 | long_description_content_type="text/markdown", 58 | ) 59 | --------------------------------------------------------------------------------