├── .coveragerc ├── .github └── workflows │ ├── build-and-test.yaml │ └── close-stale-issues.yaml ├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── amplpy ├── __init__.py ├── ampl.pyx ├── amplpython │ ├── __init__.py │ └── cppinterface │ │ ├── include │ │ └── .gitignore │ │ └── lib │ │ └── .gitignore ├── bundle │ └── __main__.py ├── campl.pxd ├── constraint.pxi ├── dataframe.pxi ├── entity.pxi ├── environment.pxi ├── errorhandler.pxi ├── exceptions.pxi ├── iterators.pxi ├── modules │ ├── __init__.py │ └── __main__.py ├── objective.pxi ├── outputhandler.pxi ├── parameter.pxi ├── set.pxi ├── tests │ ├── TestBase.py │ ├── __init__.py │ ├── __main__.py │ ├── context.py │ ├── data.csv │ ├── test_ampl.py │ ├── test_dataframe.py │ ├── test_entities.py │ ├── test_environment.py │ ├── test_exceptions.py │ ├── test_iterators.py │ ├── test_outputhandler.py │ └── test_properties.py ├── tests_camel │ ├── TestBase.py │ ├── __init__.py │ ├── __main__.py │ ├── context.py │ ├── data.csv │ ├── test_ampl.py │ ├── test_dataframe.py │ ├── test_entities.py │ ├── test_environment.py │ ├── test_iterators.py │ └── test_properties.py ├── tools │ └── __init__.py ├── util.pxi ├── utils.py ├── variable.pxi └── vendor │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.md │ ├── ampltools │ ├── __init__.py │ ├── bundle │ │ ├── __init__.py │ │ ├── __main__.py │ │ └── bundle.py │ ├── modules │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── amplpypi.py │ │ ├── commands.py │ │ └── utils.py │ ├── notebooks.py │ ├── tests │ │ ├── TestBase.py │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── test_install.py │ │ ├── test_load.py │ │ ├── test_preload.py │ │ └── test_run.py │ └── utils.py │ ├── bumpversion.sh │ ├── clear.sh │ ├── register.sh │ ├── requirements.txt │ └── setup.py ├── azure-pipelines.yml ├── dev ├── bumpversion.sh ├── clear.sh ├── coverage.sh ├── docker.sh ├── download-ampl.sh ├── register.sh ├── requirements.txt └── updatelib.py ├── docs ├── .gitignore ├── Makefile ├── common │ └── images │ │ ├── ArchitectureSchema.png │ │ ├── ClassDiagramModelEntitiesNew.PNG │ │ ├── EntityMapItem.png │ │ └── sources │ │ ├── Architecture Schema.docx │ │ └── Instance.vsd ├── requirements-dev.txt ├── requirements.txt └── source │ ├── _static │ ├── cropped-favicon-raw-192x192.png │ └── logo-inline-web-v4.png │ ├── class-structure.rst │ ├── classes │ ├── ampl.rst │ ├── amplexception.rst │ ├── constraint.rst │ ├── dataframe.rst │ ├── entity.rst │ ├── environment.rst │ ├── errorhandler.rst │ ├── objective.rst │ ├── outputhandler.rst │ ├── parameter.rst │ ├── set.rst │ └── variable.rst │ ├── conf.py │ ├── examples.rst │ ├── getting-started.rst │ ├── images │ └── ClassDiagramModelEntitiesNew.PNG │ ├── index.rst │ ├── intro.rst │ ├── quick-start.rst │ └── reference.rst ├── examples ├── README.md ├── dataframe_example.py ├── diet_model.py ├── docker │ ├── Dockerfile │ ├── build.sh │ └── run.sh ├── efficient_frontier.py ├── first_example.py ├── location_transportation.py ├── models │ ├── alm │ │ ├── alm.random2.dat │ │ ├── almtwo.dat │ │ ├── almtwo.deteq.mod │ │ ├── almtwo.mod │ │ ├── almtwodata.dat │ │ └── prices360.bit │ ├── asyncboost │ │ ├── test.run │ │ ├── trnloc2.dat │ │ ├── trnloc2a.mod │ │ └── trnloc2a.run │ ├── benders │ │ ├── trnloc.dat │ │ └── trnloc2.mod │ ├── commands │ │ ├── model.mod │ │ └── model2.mod │ ├── diet │ │ ├── diet.dat │ │ ├── diet.mod │ │ └── diet.sol │ ├── errors │ │ ├── errorDefined.mod │ │ ├── errorODBC.mod │ │ └── errorSyntax.mod │ ├── locationtransportation │ │ ├── trnloc.dat │ │ └── trnloc2.mod │ ├── nonlinearforabs │ │ ├── solution.sol │ │ ├── sub_simple.dat │ │ └── sub_simple.mod │ ├── nqueens │ │ └── nqueens.mod │ ├── qpmv │ │ ├── assetsReturns.bit │ │ ├── covar.bit │ │ ├── qpmv.mod │ │ └── qpmvbit.run │ └── tracking │ │ ├── assets.bit │ │ ├── indret.bit │ │ ├── returns.bit │ │ ├── tracking.dat │ │ ├── tracking.mod │ │ ├── tracking.xls │ │ └── trackingbit.run ├── multidimensional_example.py ├── notebooks │ └── README.md ├── options_example.py ├── test_examples.py └── tracking_model.py ├── pyproject.toml └── setup.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | plugins = Cython.Coverage 3 | 4 | [report] 5 | include = 6 | amplpy/*.pyx 7 | amplpy/*.pxi 8 | 9 | exclude_also = 10 | class Kind\(.*\): 11 | ^ {4,}[A-Z_]+ = campl\..*$ 12 | ^ {4,}""".*"""$ 13 | ^ {4,}""".* 14 | PyOutput 15 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yaml: -------------------------------------------------------------------------------- 1 | name: build-and-test 2 | run-name: ${{ github.actor }} is building "${{ github.ref_name }}" 3 | on: [push] 4 | 5 | env: 6 | CIBW_ARCHS_LINUX: auto 7 | CIBW_ARCHS_MACOS: x86_64 universal2 8 | CIBW_BEFORE_BUILD: pip install cython==3.1.0 9 | CIBW_SKIP: pp* cp36-* cp37-* *_i686 *-win32 *musllinux* 10 | CIBW_ENABLE: cpython-freethreading # Include free-threaded support 11 | CIBW_TEST_COMMAND: python -m amplpy.tests 12 | CIBW_TEST_REQUIRES: --index-url https://pypi.ampl.com --extra-index-url https://pypi.org/simple ampl_module_base ampl_module_highs pandas numpy 13 | 14 | jobs: 15 | build_native: 16 | name: Build wheels on ${{ matrix.os }} 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | matrix: 20 | os: [ubuntu-24.04, windows-2022, macos-latest] 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Download libampl 26 | run: python3 dev/updatelib.py 27 | 28 | - name: Build wheels 29 | uses: pypa/cibuildwheel@v2.21.3 30 | env: 31 | CIBW_ARCHS_LINUX: ${{ env.CIBW_ARCHS_LINUX }} 32 | CIBW_ARCHS_MACOS: ${{ env.CIBW_ARCHS_MACOS }} 33 | CIBW_BEFORE_BUILD: ${{ env.CIBW_BEFORE_BUILD }} 34 | CIBW_SKIP: ${{ env.CIBW_SKIP }} 35 | CIBW_ENABLE: ${{ env.CIBW_ENABLE }} 36 | CIBW_TEST_COMMAND: ${{ env.CIBW_TEST_COMMAND }} 37 | CIBW_TEST_REQUIRES: ${{ env.CIBW_TEST_REQUIRES }} 38 | 39 | - uses: actions/upload-artifact@v4 40 | with: 41 | name: artifact-${{ matrix.os }} 42 | path: ./wheelhouse/*.whl 43 | 44 | build_with_qemu: 45 | #if: github.ref == 'refs/heads/release' 46 | name: Build ${{ matrix.build }} wheels for ${{ matrix.arch }} 47 | #needs: build_native 48 | strategy: 49 | matrix: 50 | arch: [aarch64] 51 | build: [cp38-*, cp39-*, cp310-*, cp311-*, cp312-*, cp313-*] 52 | runs-on: ubuntu-24.04 53 | steps: 54 | - uses: actions/checkout@v4 55 | 56 | - name: Set up QEMU 57 | uses: docker/setup-qemu-action@v3 58 | with: 59 | # This should be temporary 60 | # xref https://github.com/docker/setup-qemu-action/issues/188 61 | # xref https://github.com/tonistiigi/binfmt/issues/215 62 | image: tonistiigi/binfmt:qemu-v8.1.5 63 | 64 | - name: Download libampl 65 | run: python3 dev/updatelib.py 66 | 67 | - name: Build wheels 68 | uses: pypa/cibuildwheel@v2.21.3 69 | env: 70 | CIBW_ARCHS_LINUX: ${{ matrix.arch }} 71 | CIBW_ARCHS_MACOS: ${{ env.CIBW_ARCHS_MACOS }} 72 | CIBW_BUILD: ${{ matrix.build }} 73 | CIBW_SKIP: ${{ env.CIBW_SKIP }} 74 | CIBW_TEST_COMMAND: ${{ env.CIBW_TEST_COMMAND }} 75 | CIBW_TEST_REQUIRES: --index-url https://pypi.ampl.com --extra-index-url https://pypi.org/simple ampl_module_base 76 | 77 | - name: Remove '-*' from build variable 78 | run: | 79 | # Remove '-*' from the build variable 80 | ESCAPED_BUILD=$(echo "${{ matrix.build }}" | sed 's/-\*//g') 81 | echo "ESCAPED_BUILD=$ESCAPED_BUILD" >> $GITHUB_ENV 82 | 83 | - uses: actions/upload-artifact@v4 84 | with: 85 | name: artifact-${{ matrix.arch }}-${{ env.ESCAPED_BUILD }} 86 | path: ./wheelhouse/*.whl 87 | -------------------------------------------------------------------------------- /.github/workflows/close-stale-issues.yaml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v5 14 | with: 15 | days-before-issue-stale: 30 16 | days-before-issue-close: 30 17 | stale-issue-label: "stale" 18 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." 19 | close-issue-message: "This issue was closed because it has been inactive for 30 days since being marked as stale." 20 | days-before-pr-stale: -1 21 | days-before-pr-close: -1 22 | repo-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | /build/ 13 | /docs/build/doctrees 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | /cover 45 | .hypothesis/ 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | local_settings.py 54 | 55 | # Flask stuff: 56 | instance/ 57 | .webassets-cache 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | # IPython Notebook 69 | .ipynb_checkpoints 70 | 71 | # pyenv 72 | .python-version 73 | 74 | # celery beat schedule file 75 | celerybeat-schedule 76 | 77 | # dotenv 78 | .env 79 | 80 | # virtualenv 81 | .venv* 82 | venv* 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | # VSCode 92 | .vscode 93 | 94 | # Temprary files 95 | *~ 96 | #* 97 | 98 | /tmp 99 | /debug 100 | /notes.txt 101 | 102 | 103 | # Cython 104 | amplpy/*.c 105 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | version: 2 5 | 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3.11" 10 | 11 | # Build documentation in the "docs/" directory with Sphinx 12 | sphinx: 13 | configuration: docs/source/conf.py 14 | 15 | python: 16 | install: 17 | - requirements: docs/requirements.txt 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017-2023, AMPL Optimization inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AMPLPY: Python API for AMPL 2 | 3 | [![PyPI - Downloads](https://img.shields.io/pypi/dm/amplpy?label=PyPI%20downloads)](https://pypistats.org/packages/amplpy) 4 | [![Conda](https://img.shields.io/conda/dn/conda-forge/amplpy?label=Conda%20downloads)](https://anaconda.org/conda-forge/amplpy) 5 | [![Build Status](https://dev.azure.com/ampldev/amplpy/_apis/build/status/ampl.amplpy?branchName=master)](https://dev.azure.com/ampldev/amplpy/_build/latest?definitionId=9&branchName=test) 6 | [![build-and-test](https://github.com/ampl/amplpy/actions/workflows/build-and-test.yaml/badge.svg)](https://github.com/ampl/amplpy/actions/workflows/build-and-test.yaml) 7 | 8 | ```python 9 | # Install Python API for AMPL 10 | $ python -m pip install amplpy --upgrade 11 | 12 | # Install solver modules (e.g., HiGHS, CBC, Gurobi) 13 | $ python -m amplpy.modules install highs cbc gurobi 14 | 15 | # Activate your license (e.g., free https://ampl.com/ce license) 16 | $ python -m amplpy.modules activate 17 | 18 | # Import in Python 19 | $ python 20 | >>> from amplpy import AMPL 21 | >>> ampl = AMPL() # instantiate AMPL object 22 | ``` 23 | ```python 24 | # Minimal example: 25 | from amplpy import AMPL 26 | import pandas as pd 27 | ampl = AMPL() 28 | ampl.eval(r""" 29 | set A ordered; 30 | param S{A, A}; 31 | param lb default 0; 32 | param ub default 1; 33 | var w{A} >= lb <= ub; 34 | minimize portfolio_variance: 35 | sum {i in A, j in A} w[i] * S[i, j] * w[j]; 36 | s.t. portfolio_weights: 37 | sum {i in A} w[i] = 1; 38 | """) 39 | tickers, cov_matrix = # ... pre-process data in Python 40 | ampl.set["A"] = tickers 41 | ampl.param["S"] = pd.DataFrame(cov_matrix, index=tickers, columns=tickers) 42 | ampl.solve(solver="gurobi", gurobi_options="outlev=1") 43 | assert ampl.solve_result == "solved" 44 | sigma = ampl.get_value("sqrt(sum {i in A, j in A} w[i] * S[i, j] * w[j])") 45 | print(f"Volatility: {sigma*100:.1f}%") 46 | # ... post-process solution in Python 47 | ``` 48 | 49 | [![Hands-On Mathematical Optimization with AMPL in Python](https://portal.ampl.com/dl/ads/mo_book_big.png)](https://ampl.com/mo-book/) 50 | 51 | [[Documentation](https://amplpy.readthedocs.io/)] [[AMPL Modules for Python](https://dev.ampl.com/ampl/python/modules.html)] [[AMPL on Streamlit](https://ampl.com/streamlit)] [[AMPL on Google Colab](https://colab.ampl.com/)] [[Community Edition](https://ampl.com/ce)] 52 | 53 | `amplpy` is an interface that allows developers to access the features of [AMPL](https://ampl.com) from within Python. For a quick introduction to AMPL see [Quick Introduction to AMPL](https://dev.ampl.com/ampl/introduction.html). 54 | 55 | In the same way that AMPL’s syntax matches naturally the mathematical description of the model, the input and output data matches naturally Python lists, sets, dictionaries, `pandas` and `numpy` objects. 56 | 57 | All model generation and solver interaction is handled directly by AMPL, which leads to great stability and speed; the library just acts as an intermediary, and the added overhead (in terms of memory and CPU usage) depends mostly on how much data is sent and read back from AMPL, the size of the expanded model as such is irrelevant. 58 | 59 | With `amplpy` you can model and solve large scale optimization problems in Python with the performance of heavily optimized C code without losing model readability. The same model can be deployed on applications built on different languages by just switching the API used. 60 | 61 | ## Documentation 62 | 63 | - http://amplpy.ampl.com 64 | 65 | ## Examples 66 | 67 | Data can be loaded in various forms: 68 | - One of which is ``pandas.DataFrame`` objects: 69 | 70 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ampl/amplcolab/blob/master/authors/fdabrandao/quick-start/pandasdiet.ipynb) 71 | - Python lists and dictionaries: 72 | 73 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ampl/amplcolab/blob/master/authors/fdabrandao/quick-start/nativediet.ipynb) 74 | 75 | More notebooks with examples available on the [AMPL Model Colaboratory](https://colab.ampl.com/). 76 | 77 | ## Repositories 78 | 79 | - GitHub Repository: https://github.com/ampl/amplpy 80 | - PyPI Repository: https://pypi.python.org/pypi/amplpy 81 | - Conda-Forge: https://anaconda.org/conda-forge/amplpy 82 | 83 | ## Setup 84 | 85 | ### PyPI 86 | 87 | Install from the [PyPI repository](https://pypi.python.org/pypi/amplpy): 88 | 89 | ``` 90 | python -m pip install amplpy 91 | ``` 92 | 93 | ### AMPL Modules for Python 94 | 95 | [AMPL and all Solvers are now available as Python Packages](https://dev.ampl.com/ampl/python/modules.html): 96 | 97 | - Install Python API for AMPL: 98 | ``` 99 | python -m pip install amplpy --upgrade 100 | ``` 101 | 102 | - Install solver modules (e.g., HiGHS and Gurobi): 103 | ``` 104 | python -m amplpy.modules install highs gurobi 105 | ``` 106 | 107 | - Activate your license (e.g., free [AMPL Community Edition](https://ampl.com/ce) license): 108 | ``` 109 | python -m amplpy.modules activate 110 | ``` 111 | 112 | - Import and instantiate in Python: 113 | ``` 114 | $ python 115 | >>> from amplpy import AMPL 116 | >>> ampl = AMPL() # instantiate AMPL object 117 | ``` 118 | 119 | ### Conda 120 | 121 | Install from the [Conda repository](https://anaconda.org/conda-forge/amplpy): 122 | 123 | ``` 124 | conda install -c conda-forge amplpy 125 | ``` 126 | 127 | ### Air-gapped installation 128 | 129 | For air-gapped installations we recomend the following: 130 | - Download on another machine the `.whl` file for the corresponding platform and python version from [pypi](https://pypi.org/project/amplpy/#files). 131 | - Install on the target machine with: `python -m pip install amplpy-version-python_version-*-platform.whl --no-deps` 132 | 133 | ### Build locally 134 | 135 | You can build and install the package locally as follows: 136 | ``` 137 | $ git clone https://github.com/ampl/amplpy.git 138 | $ cd amplpy 139 | $ python dev/updatelib.py 140 | $ python setup.py build 141 | $ pip install . --upgrade 142 | ``` 143 | 144 | ## License 145 | 146 | BSD-3 147 | 148 | *** 149 | Copyright © 2024 AMPL Optimization inc. All rights reserved. 150 | -------------------------------------------------------------------------------- /amplpy/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | import platform 5 | import ctypes 6 | 7 | 8 | if platform.system().startswith(("Windows", "MSYS", "CYGWIN", "MINGW")): 9 | libbase = os.path.join(os.path.dirname(__file__), "amplpython", "cppinterface", "lib") 10 | lib64 = os.path.join(libbase, "amd64") 11 | from glob import glob 12 | 13 | try: 14 | dllfile = glob(lib64 + "/*.dll")[0] 15 | ctypes.CDLL(dllfile) 16 | except Exception as exp: 17 | raise exp 18 | 19 | from amplpy.ampl import OutputHandler, Kind 20 | from amplpy.ampl import ErrorHandler 21 | from amplpy.ampl import AMPLException, PresolveException, InfeasibilityException 22 | from amplpy.ampl import EntityMap 23 | from amplpy.ampl import Objective 24 | from amplpy.ampl import Variable 25 | from amplpy.ampl import Constraint 26 | from amplpy.ampl import Set 27 | from amplpy.ampl import Parameter 28 | from amplpy.ampl import Entity 29 | from amplpy.ampl import DataFrame, Row, Column 30 | from .utils import add_to_path, multidict, register_magics 31 | from amplpy.ampl import Environment 32 | from amplpy.ampl import AMPL 33 | from amplpy.ampl import logger 34 | 35 | _parent_dir = os.path.abspath(os.path.dirname(__file__)) 36 | _vendor_dir = os.path.join(_parent_dir, "vendor") 37 | if _vendor_dir not in sys.path: 38 | sys.path.append(_vendor_dir) 39 | 40 | try: 41 | from ampltools import register_magics 42 | except Exception: 43 | pass 44 | try: 45 | from ampltools import ampl_notebook 46 | except Exception: 47 | pass 48 | try: 49 | from ampltools import add_to_path 50 | except Exception: 51 | pass 52 | try: 53 | from . import modules 54 | 55 | modules.preload() 56 | except Exception: 57 | pass 58 | 59 | __version__ = "0.15.0b8" 60 | 61 | 62 | def _list_aliases(): 63 | from inflection import camelize 64 | 65 | classes = [ 66 | OutputHandler, 67 | ErrorHandler, 68 | AMPLException, 69 | EntityMap, 70 | Entity, 71 | Objective, 72 | Variable, 73 | Constraint, 74 | Set, 75 | Parameter, 76 | Row, 77 | Column, 78 | DataFrame, 79 | Environment, 80 | AMPL, 81 | ] 82 | for cls in classes: 83 | print(cls) 84 | for method in list(dir(cls)): 85 | if method.startswith("__"): 86 | continue 87 | cammel_method = camelize(method, False) 88 | if cammel_method != method: 89 | print(f"\t{cammel_method} = {method}") 90 | # setattr(cls, cammel_method, getattr(cls, method)) 91 | -------------------------------------------------------------------------------- /amplpy/amplpython/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import ctypes 5 | import platform 6 | 7 | if platform.system().startswith(("Windows", "MSYS", "CYGWIN", "MINGW")): 8 | libbase = os.path.join(os.path.dirname(__file__), "cppinterface", "lib") 9 | lib32 = os.path.join(libbase, "intel32") 10 | lib64 = os.path.join(libbase, "amd64") 11 | from glob import glob 12 | 13 | try: 14 | if ctypes.sizeof(ctypes.c_voidp) == 4: 15 | dllfile = glob(lib32 + "/*.dll")[0] 16 | else: 17 | dllfile = glob(lib64 + "/*.dll")[0] 18 | ctypes.CDLL(dllfile) 19 | except Exception as exp: 20 | raise exp 21 | 22 | sys.path.append(os.path.join(os.path.dirname(__file__), "cppinterface")) 23 | try: 24 | # from amplpython import _READTABLE, _WRITETABLE 25 | # from amplpython import * 26 | pass 27 | except Exception as exp: 28 | raise exp 29 | -------------------------------------------------------------------------------- /amplpy/amplpython/cppinterface/include/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | 4 | -------------------------------------------------------------------------------- /amplpy/amplpython/cppinterface/lib/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | 4 | -------------------------------------------------------------------------------- /amplpy/bundle/__main__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | _parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 6 | _vendor_dir = os.path.join(_parent_dir, "vendor") 7 | if _vendor_dir not in sys.path: 8 | sys.path.append(_vendor_dir) 9 | 10 | if __name__ == "__main__": 11 | try: 12 | from ampltools.bundle import _main 13 | except ImportError: 14 | print( 15 | "Failed to import 'ampltools.bundle'. Install or upgrade with:\n" 16 | " $ python -m pip install ampltools --upgrade", 17 | file=sys.stderr, 18 | ) 19 | sys.exit(1) 20 | _main() 21 | -------------------------------------------------------------------------------- /amplpy/environment.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | 5 | cdef class Environment(object): 6 | """ 7 | This class provides access to the environment variables and provides 8 | facilities to specify where to load the underlying AMPL interpreter. 9 | """ 10 | cdef campl.AMPL_ENVIRONMENT* _c_env 11 | 12 | def __cinit__(self, binary_directory=None, binary_name=None): 13 | """ 14 | Constructor with ability to select the location of the AMPL binary. 15 | Note that if binaryDirectory is set, the automatic lookup for an AMPL 16 | executable will not be executed. 17 | 18 | Args: 19 | binary_directory: The directory in which look for the AMPL executable. 20 | binary_name: The name of the AMPL executable. 21 | """ 22 | if binary_directory is None: 23 | binary_directory = "" 24 | if binary_name is None: 25 | binary_name = "" 26 | campl.AMPL_EnvironmentCreate(&self._c_env, binary_directory.encode('utf-8'), binary_name.encode('utf-8')) 27 | if os.name == "nt": 28 | # Workaround for Windows issue with environment variables 29 | for option_name in ["ampl_libpath", "solver"]: 30 | option_value = os.environ.get(option_name, "") 31 | if option_value: 32 | campl.AMPL_EnvironmentAddEnvironmentVariable(self._c_env, option_name.encode('utf-8'), option_value.encode('utf-8')) 33 | 34 | cdef campl.AMPL_ENVIRONMENT* get_c_pointer(self): 35 | return self._c_env 36 | 37 | def __dealloc__(self): 38 | campl.AMPL_EnvironmentFree(&self._c_env) 39 | 40 | def __str__(self): 41 | return self.to_string() 42 | 43 | def __iter__(self): 44 | return EnvIterator.create(self._c_env) 45 | 46 | def __setitem__(self, name, value): 47 | """ 48 | Add an environment variable to the environment, or change its value if 49 | already defined. 50 | 51 | Args: 52 | name: Name of the environment variable. 53 | 54 | value: Value to be assigned. 55 | """ 56 | campl.AMPL_EnvironmentAddEnvironmentVariable(self._c_env, name.encode('utf-8'), value.encode('utf-8')) 57 | 58 | def __getitem__(self, name): 59 | """ 60 | Searches the current object for an environment variable called name and 61 | returns an iterator to it if found, otherwise it returns `None`. 62 | """ 63 | cdef campl.AMPL_ENVIRONMENTVAR* iterator 64 | cdef char* value_c 65 | if not campl.AMPL_EnvironmentFindEnvironmentVar(self._c_env, name.encode('utf-8'), &iterator): 66 | campl.AMPL_EnvironmentVarGetValue(iterator, &value_c) 67 | value = str(value_c.decode('utf-8')) 68 | return value 69 | else: 70 | return None 71 | 72 | def to_string(self): 73 | cdef const char* to_string_c 74 | campl.AMPL_EnvironmentToString(self._c_env, &to_string_c) 75 | to_string = str(to_string_c.decode('utf-8')) 76 | return to_string 77 | 78 | def set_bin_dir(self, binary_directory): 79 | """ 80 | Set the location where AMPL API will search for the AMPL executable. 81 | 82 | Args: 83 | binary_directory: The directory in which look for the AMPL executable. 84 | """ 85 | campl.AMPL_EnvironmentSetBinaryDirectory(self._c_env, binary_directory.encode('utf-8')) 86 | 87 | def get_bin_dir(self): 88 | """ 89 | Get the location where AMPL API will search for the AMPL executable. 90 | """ 91 | cdef const char* bin_dir_c 92 | campl.AMPL_EnvironmentGetBinaryDirectory(self._c_env, &bin_dir_c) 93 | bin_dir = str(bin_dir_c.decode('utf-8')) 94 | return bin_dir 95 | 96 | def set_bin_name(self, binary_name): 97 | """ 98 | Set the name of the AMPL executable. 99 | 100 | Args: 101 | binary_name: The name of the AMPL executable. 102 | """ 103 | campl.AMPL_EnvironmentSetBinaryName(self._c_env, binary_name.encode('utf-8')) 104 | 105 | def get_bin_name(self): 106 | """ 107 | Get the name of the AMPL executable. 108 | """ 109 | cdef const char* bin_name_c 110 | campl.AMPL_EnvironmentGetBinaryName(self._c_env, &bin_name_c) 111 | bin_name = str(bin_name_c.decode('utf-8')) 112 | return bin_name 113 | 114 | # Aliases 115 | toString = to_string 116 | getBinDir = get_bin_dir 117 | getBinName = get_bin_name 118 | setBinDir = set_bin_dir 119 | setBinName = set_bin_name 120 | -------------------------------------------------------------------------------- /amplpy/errorhandler.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import logging 3 | 4 | try: 5 | from .tools import _SUPPORT_MESSAGE 6 | except Exception: 7 | _SUPPORT_MESSAGE = "" 8 | 9 | logger = logging.getLogger("amplpy") 10 | logger.setLevel(logging.WARNING) 11 | 12 | if not logger.hasHandlers(): 13 | handler = logging.StreamHandler() 14 | formatter = logging.Formatter("[%(levelname)s] %(message)s") 15 | handler.setFormatter(formatter) 16 | logger.addHandler(handler) 17 | logger.propagate = False 18 | 19 | def display_error_message(exception, error=True): 20 | logger.setLevel(logging.WARNING) 21 | msg = "\t" + str(exception).replace(_SUPPORT_MESSAGE, "").replace("\n", "\n\t") 22 | if error: 23 | logger.error(f"\n{msg}") 24 | else: 25 | logger.warning(f"\n{msg}") 26 | 27 | 28 | cdef class ErrorHandler: 29 | """ 30 | A basic interface for AMPL error handlers. If an application needs to 31 | implement customized error handling, it must implement this interface and 32 | then register an instance with the AMPL API using the 33 | :func:`~amplpy.AMPL.set_error_handler` method. 34 | The underlying AMPL interpreter will then report all errors and warnings 35 | through this interface as :class:`~amplpy.AMPLException` objects. 36 | """ 37 | 38 | def error(self, exception): 39 | """ 40 | Receives notification of an error. 41 | """ 42 | display_error_message(exception) 43 | raise exception 44 | 45 | def warning(self, exception): 46 | """ 47 | Receives notification of a warning. 48 | """ 49 | display_error_message(exception, error=False) 50 | raise exception 51 | 52 | cdef void PyError(bool_c isWarning, const char* filename, int row, int offset, const char* message, void* errorHandler) except * with gil: 53 | handler = errorHandler 54 | if isWarning: 55 | exception = AMPLException(filename.decode('utf-8'), row, offset, message.decode('utf-8')) 56 | handler.warning(exception) 57 | else: 58 | exception = AMPLException(filename.decode('utf-8'), row, offset, f"{message.decode('utf-8')}{_SUPPORT_MESSAGE}") 59 | handler.error(exception) 60 | -------------------------------------------------------------------------------- /amplpy/exceptions.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | cdef class AMPLException(Exception): 5 | """ 6 | Represent an exception generated by AMPL. 7 | """ 8 | 9 | def __init__(self, source_name, line_number, offset, message): 10 | super(AMPLException, self).__init__() 11 | self.source_name = source_name 12 | self.line_number = line_number 13 | self.offset = offset 14 | self.message = message 15 | 16 | def get_source_name(self): 17 | """ 18 | Get the name of the file where the error was detected. 19 | """ 20 | return self.source_name 21 | 22 | def get_line_number(self): 23 | """ 24 | Get the row where the error is located. 25 | """ 26 | return self.line_number 27 | 28 | def get_offset(self): 29 | """ 30 | Get the offset where the error is located. 31 | """ 32 | return self.offset 33 | 34 | def get_message(self): 35 | """ 36 | Get the error message. 37 | """ 38 | return self.message 39 | 40 | def __str__(self): 41 | if self.source_name == "-": 42 | return "line " + str(self.line_number) + " offset " + str(self.offset) + "\n" + self.message 43 | return self.source_name + "\nline " + str(self.line_number) + " offset " + str(self.offset) + "\n" + self.message 44 | 45 | # Aliases 46 | getLineNumber = get_line_number 47 | getMessage = get_message 48 | getOffset = get_offset 49 | getSourceName = get_source_name 50 | 51 | 52 | class PresolveException(RuntimeError): 53 | pass 54 | 55 | 56 | class InfeasibilityException(RuntimeError): 57 | pass 58 | -------------------------------------------------------------------------------- /amplpy/modules/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | _parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 6 | _vendor_dir = os.path.join(_parent_dir, "vendor") 7 | if _vendor_dir not in sys.path: 8 | sys.path.append(_vendor_dir) 9 | 10 | try: 11 | from ampltools.modules import * 12 | from ampltools.modules import __version__ 13 | except ImportError: 14 | print( 15 | "Failed to import 'ampltools.modules'. Install or upgrade with:\n" 16 | " $ python -m pip install ampltools --upgrade\n" 17 | ) 18 | raise 19 | -------------------------------------------------------------------------------- /amplpy/modules/__main__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | _parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 6 | _vendor_dir = os.path.join(_parent_dir, "vendor") 7 | if _vendor_dir not in sys.path: 8 | sys.path.append(_vendor_dir) 9 | 10 | if __name__ == "__main__": 11 | try: 12 | from ampltools.modules import _main 13 | except ImportError: 14 | print( 15 | "Failed to import 'ampltools.modules'. Install or upgrade with:\n" 16 | " $ python -m pip install ampltools --upgrade", 17 | file=sys.stderr, 18 | ) 19 | sys.exit(1) 20 | _main() 21 | -------------------------------------------------------------------------------- /amplpy/objective.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | cdef class Objective(Entity): 5 | """ 6 | Bases: :class:`~amplpy.Entity`. 7 | 8 | Represents an AMPL objective. Note that, in case of a scalar objective, all 9 | the properties (corresponding to AMPL suffixes) of the objective instance 10 | can be accessed through methods like :func:`~amplpy.Objective.value`. 11 | The methods have the same name of the corresponding AMPL suffixes. 12 | See http://www.ampl.com/NEW/suffbuiltin.html for a list of the available 13 | suffixes. 14 | 15 | All these methods throw a TypeError if called for a non scalar 16 | objective and an RuntimeError if called on an entity which has been deleted 17 | in the underlying intepreter. 18 | 19 | To gain access to all the values in an entity (for all instances and all 20 | suffixes for that entities), see 21 | :func:`~amplpy.Entity.get_values` 22 | and the :class:`~amplpy.DataFrame` class. 23 | """ 24 | @staticmethod 25 | cdef create(AMPL ampl, char* name, campl.AMPL_TUPLE* index, parent): 26 | entity = Objective() 27 | entity._ampl = ampl 28 | Py_INCREF(entity._ampl) 29 | entity._name = name 30 | entity._index = index 31 | entity.wrap_function = campl.AMPL_OBJECTIVE 32 | entity._entity = parent 33 | if entity._entity is not None: 34 | Py_INCREF(entity._entity) 35 | return entity 36 | 37 | def value(self): 38 | """ 39 | Get the value of the objective. 40 | """ 41 | cdef double value 42 | PY_AMPL_CALL(campl.AMPL_InstanceGetDoubleSuffix(self._ampl._c_ampl, self._name, self._index, campl.AMPL_NUMERICSUFFIX.AMPL_VALUE, &value)) 43 | return value 44 | 45 | def astatus(self): 46 | """ 47 | Return the AMPL status. 48 | """ 49 | cdef campl.AMPL_ERRORINFO* errorinfo 50 | cdef char* value_c 51 | errorinfo = campl.AMPL_InstanceGetStringSuffix(self._ampl._c_ampl, self._name, self._index, campl.AMPL_STRINGSUFFIX.AMPL_ASTATUS, &value_c) 52 | if errorinfo: 53 | PY_AMPL_CALL(errorinfo) 54 | value = str(value_c.decode('utf-8')) 55 | campl.AMPL_StringFree(&value_c) 56 | 57 | return value 58 | 59 | def sstatus(self): 60 | """ 61 | Return the solver status. 62 | """ 63 | cdef campl.AMPL_ERRORINFO* errorinfo 64 | cdef char* value_c 65 | errorinfo = campl.AMPL_InstanceGetStringSuffix(self._ampl._c_ampl, self._name, self._index, campl.AMPL_STRINGSUFFIX.AMPL_SSTATUS, &value_c) 66 | if errorinfo: 67 | PY_AMPL_CALL(errorinfo) 68 | value = str(value_c.decode('utf-8')) 69 | campl.AMPL_StringFree(&value_c) 70 | 71 | return value 72 | 73 | def exitcode(self): 74 | """ 75 | Exit code returned by solver after most recent solve with this 76 | objective. 77 | """ 78 | cdef int value 79 | PY_AMPL_CALL(campl.AMPL_InstanceGetIntSuffix(self._ampl._c_ampl, self._name, self._index, campl.AMPL_NUMERICSUFFIX.AMPL_EXITCODE, &value)) 80 | return value 81 | 82 | def message(self): 83 | """ 84 | Result message returned by solver after most recent solve with this 85 | objective. 86 | """ 87 | cdef campl.AMPL_ERRORINFO* errorinfo 88 | cdef char* value_c 89 | errorinfo = campl.AMPL_InstanceGetStringSuffix(self._ampl._c_ampl, self._name, self._index, campl.AMPL_STRINGSUFFIX.AMPL_MESSAGE, &value_c) 90 | if errorinfo: 91 | PY_AMPL_CALL(errorinfo) 92 | value = str(value_c.decode('utf-8')) 93 | campl.AMPL_StringFree(&value_c) 94 | 95 | return value 96 | 97 | def result(self): 98 | """ 99 | Result string returned by solver after most recent solve with this 100 | objective. 101 | """ 102 | cdef campl.AMPL_ERRORINFO* errorinfo 103 | cdef char* value_c 104 | errorinfo = campl.AMPL_InstanceGetStringSuffix(self._ampl._c_ampl, self._name, self._index, campl.AMPL_STRINGSUFFIX.AMPL_RESULT, &value_c) 105 | if errorinfo: 106 | PY_AMPL_CALL(errorinfo) 107 | value = str(value_c.decode('utf-8')) 108 | campl.AMPL_StringFree(&value_c) 109 | 110 | return value 111 | 112 | def drop(self): 113 | """ 114 | Drop this objective instance. 115 | """ 116 | PY_AMPL_CALL(campl.AMPL_EntityDrop(self._ampl._c_ampl, self._name)) 117 | 118 | def restore(self): 119 | """ 120 | Restore this objective (if it had been dropped, no effect otherwise). 121 | """ 122 | PY_AMPL_CALL(campl.AMPL_EntityRestore(self._ampl._c_ampl, self._name)) 123 | 124 | def minimization(self): 125 | """ 126 | Get the sense of this objective. Returns True if minimize, 127 | False if maximize. 128 | """ 129 | cdef campl.AMPL_ERRORINFO* errorinfo 130 | cdef char* value_c 131 | errorinfo = campl.AMPL_InstanceGetStringSuffix(self._ampl._c_ampl, self._name, self._index, campl.AMPL_STRINGSUFFIX.AMPL_SENSE, &value_c) 132 | if errorinfo: 133 | PY_AMPL_CALL(errorinfo) 134 | value = str(value_c.decode('utf-8')) 135 | campl.AMPL_StringFree(&value_c) 136 | 137 | if value == 'minimize': 138 | return True 139 | else: 140 | return False 141 | -------------------------------------------------------------------------------- /amplpy/parameter.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from numbers import Real 3 | from collections.abc import Iterable 4 | 5 | 6 | import itertools 7 | 8 | try: 9 | import pandas as pd 10 | except ImportError: 11 | pd = None 12 | try: 13 | import numpy as np 14 | except ImportError: 15 | np = None 16 | 17 | 18 | 19 | cdef class Parameter(Entity): 20 | """ 21 | Represents an AMPL parameter. The values can be float or string (in case of 22 | symbolic parameters). 23 | 24 | Data can be assigned to the set using the methods 25 | :func:`~amplpy.Parameter.set` and 26 | :func:`~amplpy.Parameter.set_values` 27 | or using 28 | :func:`~amplpy.AMPL.set_data` 29 | and an object of class :class:`~amplpy.DataFrame`. 30 | """ 31 | @staticmethod 32 | cdef create(AMPL ampl, char* name, campl.AMPL_TUPLE* index, parent): 33 | entity = Parameter() 34 | entity._ampl = ampl 35 | Py_INCREF(entity._ampl) 36 | entity._name = name 37 | entity._index = index 38 | entity.wrap_function = campl.AMPL_PARAMETER 39 | entity._entity = parent 40 | if entity._entity is not None: 41 | Py_INCREF(entity._entity) 42 | return entity 43 | 44 | def __setitem__(self, index, value): 45 | self.set(index, value) 46 | 47 | def is_symbolic(self): 48 | """ 49 | Returns True if the parameter is declared as symbolic (can store both 50 | numerical and string values). 51 | """ 52 | cdef bool_c value 53 | PY_AMPL_CALL(campl.AMPL_ParameterIsSymbolic(self._ampl._c_ampl, self._name, &value)) 54 | return value 55 | 56 | def has_default(self): 57 | """ 58 | Check if the parameter has a default initial value. In case of the 59 | following AMPL code: 60 | 61 | .. code-block:: ampl 62 | 63 | param a; 64 | param b default a; 65 | 66 | the function will return true for parameter ``b``. 67 | 68 | Returns: 69 | True if the parameter has a default initial value. Please note 70 | that if the parameter has a default expression which refers to 71 | another parameter which value is not defined, this will return 72 | True. 73 | """ 74 | cdef bool_c value 75 | PY_AMPL_CALL(campl.AMPL_ParameterHasDefault(self._ampl._c_ampl, self._name, &value)) 76 | return value 77 | 78 | def __getitem__(self, index): 79 | if not isinstance(index, (tuple, list)): 80 | index = [index] 81 | cdef campl.AMPL_ERRORINFO* errorinfo 82 | cdef campl.AMPL_TUPLE* tuple_c = to_c_tuple(index) 83 | cdef char* expression 84 | cdef campl.AMPL_VARIANT* v 85 | errorinfo = campl.AMPL_InstanceGetName(self._ampl._c_ampl, self._name, tuple_c, &expression) 86 | campl.AMPL_TupleFree(&tuple_c) 87 | if errorinfo: 88 | PY_AMPL_CALL(errorinfo) 89 | errorinfo = campl.AMPL_GetValue(self._ampl._c_ampl, expression, &v) 90 | campl.AMPL_StringFree(&expression) 91 | if errorinfo: 92 | PY_AMPL_CALL(errorinfo) 93 | py_variant = to_py_variant(v) 94 | campl.AMPL_VariantFree(&v) 95 | return py_variant 96 | 97 | def value(self): 98 | """ 99 | Get the value of this parameter. Valid only for non-indexed parameters. 100 | """ 101 | cdef campl.AMPL_ERRORINFO* errorinfo 102 | cdef campl.AMPL_VARIANT* v 103 | errorinfo = campl.AMPL_GetValue(self._ampl._c_ampl, self._name, &v) 104 | if errorinfo: 105 | PY_AMPL_CALL(errorinfo) 106 | py_variant = to_py_variant(v) 107 | campl.AMPL_VariantFree(&v) 108 | return py_variant 109 | 110 | def set(self, *args): 111 | """ 112 | Set the value of a single instance of this parameter. 113 | 114 | Args: 115 | args: value if the parameter is scalar, index and value 116 | otherwise. 117 | 118 | Raises: 119 | RuntimeError: If the entity has been deleted in the underlying 120 | AMPL. 121 | 122 | TypeError: If the parameter is not scalar and the index is not 123 | provided. 124 | """ 125 | assert len(args) in (1, 2) 126 | cdef campl.AMPL_ERRORINFO* errorinfo 127 | cdef campl.AMPL_TUPLE* index_c 128 | if len(args) == 1: 129 | value = args[0] 130 | 131 | if isinstance(value, Real): 132 | errorinfo = campl.AMPL_ParameterSetNumeric(self._ampl._c_ampl, self._name, float(value)) 133 | elif isinstance(value, str): 134 | errorinfo = campl.AMPL_ParameterSetString(self._ampl._c_ampl, self._name, value.encode('utf-8')) 135 | else: 136 | raise TypeError 137 | else: 138 | index, value = args 139 | if not isinstance(index, (tuple, list)): 140 | index = [index] 141 | index_c = to_c_tuple(index) 142 | if isinstance(value, Real): 143 | errorinfo = campl.AMPL_ParameterInstanceSetNumericValue(self._ampl._c_ampl, self._name, index_c, float(value)) 144 | campl.AMPL_TupleFree(&index_c) 145 | elif isinstance(value, str): 146 | errorinfo = campl.AMPL_ParameterInstanceSetStringValue(self._ampl._c_ampl, self._name, index_c, value.encode('utf-8')) 147 | campl.AMPL_TupleFree(&index_c) 148 | else: 149 | campl.AMPL_TupleFree(&index_c) 150 | raise TypeError 151 | if errorinfo: 152 | PY_AMPL_CALL(errorinfo) 153 | 154 | def set_values(self, values): 155 | """ 156 | Assign the values (string or float) to the parameter instances with the 157 | specified indices, equivalent to the AMPL code: 158 | 159 | .. code-block:: ampl 160 | 161 | let {i in indices} par[i] := values[i]; 162 | 163 | Args: 164 | values: list, dictionary or :class:`~amplpy.DataFrame` with the 165 | indices and the values to be set. 166 | 167 | Raises: 168 | TypeError: If called on a scalar parameter. 169 | """ 170 | cdef campl.AMPL_ERRORINFO* errorinfo 171 | if isinstance(values, dict): 172 | if not values: 173 | return 174 | errorinfo = setValuesPyDict(self._ampl._c_ampl, self._name, values) 175 | if errorinfo: 176 | PY_AMPL_CALL(errorinfo) 177 | elif isinstance(values, DataFrame): 178 | Entity.set_values(self, values) 179 | elif pd is not None and isinstance(values, (pd.DataFrame, pd.Series)): 180 | Entity.set_values(self, values) 181 | elif np is not None and isinstance(values, np.ndarray): 182 | if len(values.shape) <= 1: 183 | self.set_values(values.tolist()) 184 | else: 185 | self.set_values(tuple(itertools.chain(*values.tolist()))) 186 | elif isinstance(values, Iterable): 187 | if all(isinstance(value, str) for value in values): 188 | if not isinstance(values, (list, tuple)): 189 | values = list(values) 190 | errorinfo = setValuesParamStr(self._ampl._c_ampl, self._name, values) 191 | if errorinfo: 192 | PY_AMPL_CALL(errorinfo) 193 | elif all(isinstance(value, Real) for value in values): 194 | values = list(map(float, values)) 195 | errorinfo = setValuesParamNum(self._ampl._c_ampl, self._name, values) 196 | if errorinfo: 197 | PY_AMPL_CALL(errorinfo) 198 | else: 199 | Entity.set_values(self, values) 200 | else: 201 | Entity.set_values(self, values) 202 | 203 | # Aliases 204 | hasDefault = has_default 205 | isSymbolic = is_symbolic 206 | setValues = set_values 207 | -------------------------------------------------------------------------------- /amplpy/tests/TestBase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | import tempfile 5 | import shutil 6 | import os 7 | from .context import amplpy 8 | 9 | 10 | # For MSYS2, MINGW, etc., run with: 11 | # $ REAL_ROOT=`cygpath -w /` python -m amplpy.tests 12 | REAL_ROOT = os.environ.get("REAL_ROOT", None) 13 | 14 | 15 | class TestBase(unittest.TestCase): 16 | def setUp(self): 17 | print("setUp:", self._testMethodName) 18 | self.ampl = amplpy.AMPL() 19 | self.ampl.option["solver"] = "gurobi" 20 | self.ampl.option["gurobi_options"] = "outlev=1" 21 | self.dirpath = tempfile.mkdtemp() 22 | 23 | def _tmpfile(self, filename): 24 | return os.path.join(self.dirpath, filename) 25 | 26 | def _real_filename(self, filename): 27 | # Workaround for MSYS2, MINGW paths 28 | if REAL_ROOT is not None and filename.startswith("/"): 29 | filename = filename.replace("/", REAL_ROOT, 1) 30 | return filename 31 | 32 | def str2file(self, filename, content): 33 | fullpath = self._tmpfile(filename) 34 | with open(fullpath, "w") as file: 35 | print(content, file=file) 36 | return self._real_filename(fullpath) 37 | 38 | def tmpfile(self, filename): 39 | return self._real_filename(self._tmpfile(filename)) 40 | 41 | def tearDown(self): 42 | print("tearDown:", self._testMethodName) 43 | self.ampl.close() 44 | shutil.rmtree(self.dirpath) 45 | print("done!") 46 | 47 | 48 | if __name__ == "__main__": 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /amplpy/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /amplpy/tests/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | from .test_ampl import TestAMPL 5 | from .test_entities import TestEntities 6 | from .test_exceptions import TestExceptions 7 | from .test_iterators import TestIterators 8 | from .test_dataframe import TestDataFrame 9 | from .test_environment import TestEnvironment 10 | from .test_outputhandler import TestOutputHandler 11 | from .test_properties import TestProperties 12 | 13 | 14 | if __name__ == "__main__": 15 | unittest.main() 16 | -------------------------------------------------------------------------------- /amplpy/tests/context.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | import amplpy 6 | -------------------------------------------------------------------------------- /amplpy/tests/data.csv: -------------------------------------------------------------------------------- 1 | x;y 2 | 1;0.01 3 | 2;0.02 4 | 3;0.03 5 | -------------------------------------------------------------------------------- /amplpy/tests/test_environment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | import os 5 | 6 | 7 | class TestEnvironment(unittest.TestCase): 8 | """Test Environment.""" 9 | 10 | def test_environment_initialization(self): 11 | from amplpy import Environment 12 | 13 | env = Environment("binary_directory") 14 | self.assertEqual("binary_directory", env.get_bin_dir()) 15 | env = Environment("binary_directory", "binary_name") 16 | self.assertEqual("binary_directory", env.get_bin_dir()) 17 | self.assertEqual("binary_name", env.get_bin_name()) 18 | env.set_bin_dir("binary_directory_2") 19 | self.assertEqual("binary_directory_2", env.get_bin_dir()) 20 | env.set_bin_name("binary_name_2") 21 | self.assertEqual("binary_name_2", env.get_bin_name()) 22 | 23 | def test_environment(self): 24 | from amplpy import Environment, AMPL 25 | 26 | env1 = Environment() 27 | env2 = Environment(os.curdir) 28 | self.assertEqual(env2.get_bin_dir(), os.curdir) 29 | env1.set_bin_dir(env2.get_bin_dir()) 30 | self.assertEqual(env1.get_bin_dir(), env1.get_bin_dir()) 31 | self.assertEqual(len(dict(env1)), len(list(env1))) 32 | self.assertEqual(list(sorted(dict(env1).items())), list(sorted(env1))) 33 | env1["MyEnvVar"] = "TEST" 34 | self.assertEqual(env1["MyEnvVar"], "TEST") 35 | self.assertEqual(env2["MyEnvVar"], None) 36 | d = dict(env1) 37 | self.assertEqual(d["MyEnvVar"], "TEST") 38 | ampl = AMPL(Environment()) 39 | ampl.close() 40 | 41 | def test_env_options(self): 42 | from amplpy import AMPL, Environment 43 | 44 | os.environ["ampl_libpath"] = "abc" 45 | 46 | ampl = AMPL() 47 | self.assertEqual(ampl.option["ampl_libpath"], "abc") 48 | 49 | ampl = AMPL(Environment()) 50 | self.assertEqual(ampl.option["ampl_libpath"], "abc") 51 | 52 | os.environ["solver"] = "gurobi" 53 | 54 | ampl = AMPL() 55 | self.assertEqual(ampl.option["solver"], "gurobi") 56 | 57 | ampl = AMPL(Environment()) 58 | self.assertEqual(ampl.option["solver"], "gurobi") 59 | 60 | 61 | if __name__ == "__main__": 62 | unittest.main() 63 | -------------------------------------------------------------------------------- /amplpy/tests/test_exceptions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | from . import TestBase 5 | import amplpy 6 | 7 | class TestExceptions(TestBase.TestBase): 8 | """Test Exceptions.""" 9 | 10 | def test_get_option_fail(self): 11 | ampl = self.ampl 12 | with self.assertRaises(ValueError): 13 | ampl.get_option("option 42 42") 14 | 15 | def test_get_value_fail(self): 16 | ampl = self.ampl 17 | with self.assertRaises(TypeError): 18 | ampl.get_value("x.val") 19 | 20 | def test_ampl_couldnotstarted(self): 21 | env = amplpy.Environment("nodir", "noname") 22 | with self.assertRaises(RuntimeError): 23 | ampl = amplpy.AMPL(env) 24 | 25 | def test_export_model_exception(self): 26 | ampl = self.ampl 27 | with self.assertRaises(IOError): 28 | ampl.export_model("/") 29 | 30 | def test_export_data_exception(self): 31 | ampl = self.ampl 32 | with self.assertRaises(IOError): 33 | ampl.export_data("/") 34 | 35 | def test_snapshot_exception(self): 36 | ampl = self.ampl 37 | with self.assertRaises(IOError): 38 | ampl.snapshot("/") 39 | 40 | def test_ampl_cd_exception(self): 41 | ampl = self.ampl 42 | with self.assertRaises(RuntimeError): 43 | ampl.cd("nodir") 44 | 45 | def test_get_variable_exception(self): 46 | ampl = self.ampl 47 | ampl.eval("param x;") 48 | with self.assertRaises(KeyError): 49 | ampl.get_variable("x") 50 | 51 | def test_get_constraint_exception(self): 52 | ampl = self.ampl 53 | ampl.eval("var x;") 54 | with self.assertRaises(KeyError): 55 | ampl.get_constraint("x") 56 | 57 | def test_get_objective_exception(self): 58 | ampl = self.ampl 59 | ampl.eval("var x;") 60 | with self.assertRaises(KeyError): 61 | ampl.get_objective("x") 62 | 63 | def test_get_set_exception(self): 64 | ampl = self.ampl 65 | ampl.eval("var x;") 66 | with self.assertRaises(KeyError): 67 | ampl.get_set("x") 68 | 69 | def test_get_param_exception(self): 70 | ampl = self.ampl 71 | ampl.eval("set x;") 72 | with self.assertRaises(KeyError): 73 | ampl.get_parameter("x") 74 | 75 | def test_entitymap_iterator_exception(self): 76 | ampl = self.ampl 77 | ampl.eval("param x; param xx; var y;") 78 | with self.assertRaises(KeyError): 79 | y = ampl.get_parameters()["y"] 80 | 81 | def test_display_exception(self): 82 | ampl = self.ampl 83 | with self.assertRaises(RuntimeError): 84 | ampl.display("x;") 85 | 86 | def test_ampl_exception_methods(self): 87 | exc = amplpy.AMPLException("test.mod", 42, 5, "Invalid expression") 88 | 89 | self.assertEqual(exc.get_source_name(), "test.mod") 90 | self.assertEqual(exc.get_line_number(), 42) 91 | self.assertEqual(exc.get_offset(), 5) 92 | self.assertEqual(exc.get_message(), "Invalid expression") 93 | 94 | def test_default_errorhandler_error(self): 95 | ampl = self.ampl 96 | with self.assertRaises(amplpy.AMPLException) as context: 97 | ampl.eval("Y Y;") 98 | 99 | def test_default_errorhandler_warning(self): 100 | ampl = self.ampl 101 | with self.assertRaises(amplpy.AMPLException) as context: 102 | ampl.eval("var x := y; var xx := y; var xy := y;") 103 | 104 | self.assertEqual(context.exception.get_message(), 105 | "y is not defined\ncontext: var x := >>> y; <<< var xx := y; var xy := y;") 106 | 107 | 108 | if __name__ == "__main__": 109 | unittest.main() 110 | -------------------------------------------------------------------------------- /amplpy/tests/test_outputhandler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | from . import TestBase 5 | import amplpy 6 | 7 | 8 | class TestOutputHandler(TestBase.TestBase): 9 | """Test OutputHandlers.""" 10 | 11 | def test_output_kind(self): 12 | ampl = self.ampl 13 | class LastOutput(amplpy.OutputHandler): 14 | def __init__(self): 15 | self.lastkind = None 16 | super(LastOutput, self).__init__() 17 | 18 | def output(self, kind, msg): 19 | self.lastkind = kind 20 | 21 | def getLastKind(self): 22 | return self.lastkind 23 | 24 | output_handler = LastOutput() 25 | ampl.set_output_handler(output_handler) 26 | ampl.eval("print '1';") 27 | self.assertEqual(output_handler.getLastKind(), amplpy.Kind.PRINT) 28 | ampl.eval("printf '1';") 29 | self.assertEqual(output_handler.getLastKind(), amplpy.Kind.PRINTF) 30 | ampl.eval("var x; display x;") 31 | self.assertEqual(output_handler.getLastKind(), amplpy.Kind.DISPLAY) 32 | ampl.eval("show x;") 33 | self.assertEqual(output_handler.getLastKind(), amplpy.Kind.SHOW) 34 | ampl.eval("solve;") 35 | self.assertEqual(output_handler.getLastKind(), amplpy.Kind.SOLVE) 36 | 37 | 38 | if __name__ == "__main__": 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /amplpy/tests/test_properties.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | from . import TestBase 5 | 6 | 7 | class TestProperties(TestBase.TestBase): 8 | """Test Properties.""" 9 | 10 | def test_var_prop(self): 11 | ampl = self.ampl 12 | ampl.eval("var x;") 13 | ampl.var["x"] = 10 14 | 15 | self.assertEqual(ampl.var["x"].value(), 10) 16 | self.assertEqual(ampl.get_variable("x").value(), 10) 17 | self.assertEqual(len(list(ampl.var)), 1) 18 | self.assertEqual(len(list(ampl.get_variables())), 1) 19 | 20 | def test_con_prop(self): 21 | ampl = self.ampl 22 | ampl.eval("var x; s.t. c: x = 10;") 23 | self.assertEqual(ampl.con["c"].body(), 10) 24 | self.assertEqual(ampl.get_constraint("c").body(), 10) 25 | self.assertEqual(len(list(ampl.con)), 1) 26 | self.assertEqual(len(list(ampl.get_constraints())), 1) 27 | 28 | def test_con_dual(self): 29 | ampl = self.ampl 30 | ampl.eval("var x; var y; s.t. c: x + y >= 42;") 31 | ampl.con["c"].set_dual(1) 32 | self.assertEqual(ampl.con["c"].dual(), 1) 33 | ampl.con["c"] = 42 34 | self.assertEqual(ampl.con["c"].dual(), 42) 35 | 36 | def test_obj_prop(self): 37 | ampl = self.ampl 38 | ampl.eval("var x; maximize obj: x;") 39 | self.assertEqual(ampl.obj["obj"].name(), "obj") 40 | self.assertEqual(ampl.get_objective("obj").name(), "obj") 41 | self.assertEqual(len(list(ampl.obj)), 1) 42 | self.assertEqual(len(list(ampl.get_objectives())), 1) 43 | 44 | def test_set_prop(self): 45 | ampl = self.ampl 46 | ampl.eval("set s;") 47 | ampl.set["s"] = [1, 2, 3] 48 | self.assertEqual(ampl.set["s"].name(), "s") 49 | self.assertEqual(ampl.get_set("s").name(), "s") 50 | self.assertEqual(ampl.set["s"].size(), 3) 51 | self.assertEqual(len(list(ampl.set)), 1) 52 | self.assertEqual(len(list(ampl.get_sets())), 1) 53 | 54 | def test_param_prop(self): 55 | ampl = self.ampl 56 | ampl.eval("param p1; param p2{1..2};") 57 | self.assertEqual(ampl.param["p1"].name(), "p1") 58 | self.assertEqual(ampl.get_parameter("p1").name(), "p1") 59 | ampl.param["p1"] = 3 60 | self.assertEqual(ampl.param["p1"].value(), 3) 61 | ampl.param["p2"] = {1: 10, 2: 20} 62 | self.assertEqual(ampl.param["p2"][1], 10) 63 | self.assertEqual(ampl.param["p2"][2], 20) 64 | self.assertEqual(len(list(ampl.param)), 2) 65 | self.assertEqual(len(list(ampl.get_parameters())), 2) 66 | 67 | def test_option_prop(self): 68 | ampl = self.ampl 69 | ampl.option["solver"] = "gurobi" 70 | self.assertEqual(ampl.option["solver"], "gurobi") 71 | self.assertEqual(ampl.get_option("solver"), "gurobi") 72 | 73 | def test_option_prop_setitem_dict(self): 74 | ampl = self.ampl 75 | ampl.option["gurobi_options"] = { 76 | "outlev": True, 77 | "alg:method": 2, 78 | "cvt:mip:eps": 1e-3, 79 | } 80 | self.assertEqual(ampl.get_option("gurobi_options"), "outlev=1 alg:method=2 cvt:mip:eps=0.001") 81 | 82 | def test_option_prop_dict(self): 83 | ampl = self.ampl 84 | ampl.option = { 85 | "solver": "gurobi", 86 | "presolve": False, 87 | "pl_linearize": False, 88 | "gurobi_options": {"outlev": True, "timelim": 1}, 89 | } 90 | self.assertEqual(ampl.get_option("solver"), "gurobi") 91 | self.assertEqual(ampl.get_option("presolve"), False) 92 | self.assertEqual(ampl.get_option("pl_linearize"), False) 93 | self.assertEqual(ampl.get_option("gurobi_options"), "outlev=1 timelim=1") 94 | 95 | def test_solve_result(self): 96 | ampl = self.ampl 97 | self.assertEqual(ampl.solve_result, "?") 98 | self.assertEqual(ampl.solve_result_num, -1) 99 | ampl.solve() 100 | self.assertEqual(ampl.solve_result, "solved") 101 | self.assertEqual(ampl.solve_result_num, 0) 102 | 103 | 104 | if __name__ == "__main__": 105 | unittest.main() 106 | -------------------------------------------------------------------------------- /amplpy/tests_camel/TestBase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from .context import amplpy 4 | import unittest 5 | import tempfile 6 | import shutil 7 | import os 8 | 9 | 10 | # For MSYS2, MINGW, etc., run with: 11 | # $ REAL_ROOT=`cygpath -w /` python -m amplpy.tests 12 | REAL_ROOT = os.environ.get("REAL_ROOT", None) 13 | 14 | 15 | class TestBase(unittest.TestCase): 16 | def setUp(self): 17 | print("Method:", self._testMethodName) 18 | os.environ["solver"] = "gurobi" 19 | os.environ["gurobi_options"] = "outlev=1" 20 | self.ampl = amplpy.AMPL() 21 | self.dirpath = tempfile.mkdtemp() 22 | 23 | def _tmpfile(self, filename): 24 | return os.path.join(self.dirpath, filename) 25 | 26 | def _real_filename(self, filename): 27 | # Workaround for MSYS2, MINGW paths 28 | if REAL_ROOT is not None and filename.startswith("/"): 29 | filename = filename.replace("/", REAL_ROOT, 1) 30 | return filename 31 | 32 | def str2file(self, filename, content): 33 | fullpath = self._tmpfile(filename) 34 | with open(fullpath, "w") as f: 35 | print(content, file=f) 36 | return self._real_filename(fullpath) 37 | 38 | def tmpfile(self, filename): 39 | return self._real_filename(self._tmpfile(filename)) 40 | 41 | def tearDown(self): 42 | self.ampl.close() 43 | shutil.rmtree(self.dirpath) 44 | 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | -------------------------------------------------------------------------------- /amplpy/tests_camel/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /amplpy/tests_camel/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | from .test_ampl import TestAMPL 5 | from .test_entities import TestEntities 6 | from .test_iterators import TestIterators 7 | from .test_dataframe import TestDataFrame 8 | from .test_environment import TestEnvironment 9 | from .test_properties import TestProperties 10 | 11 | 12 | if __name__ == "__main__": 13 | unittest.main() 14 | -------------------------------------------------------------------------------- /amplpy/tests_camel/context.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | import amplpy 6 | -------------------------------------------------------------------------------- /amplpy/tests_camel/data.csv: -------------------------------------------------------------------------------- 1 | x;y 2 | 1;0.01 3 | 2;0.02 4 | 3;0.03 5 | -------------------------------------------------------------------------------- /amplpy/tests_camel/test_environment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | import amplpy 5 | import os 6 | 7 | 8 | class TestEnvironment(unittest.TestCase): 9 | """Test Environment.""" 10 | 11 | def testEnvironmentInitialization(self): 12 | from amplpy import Environment 13 | 14 | env = Environment("binary_directory") 15 | self.assertEqual("binary_directory", env.getBinDir()) 16 | env = Environment("binary_directory", "binary_name") 17 | self.assertEqual("binary_directory", env.getBinDir()) 18 | self.assertEqual("binary_name", env.getBinName()) 19 | env.setBinDir("binary_directory_2") 20 | self.assertEqual("binary_directory_2", env.getBinDir()) 21 | env.setBinName("binary_name_2") 22 | self.assertEqual("binary_name_2", env.getBinName()) 23 | 24 | def testEnvironment(self): 25 | from amplpy import Environment, AMPL 26 | 27 | env1 = Environment() 28 | env2 = Environment(os.curdir) 29 | self.assertEqual(env2.getBinDir(), os.curdir) 30 | env1.setBinDir(env2.getBinDir()) 31 | self.assertEqual(env1.getBinDir(), env1.getBinDir()) 32 | self.assertEqual(len(dict(env1)), len(list(env1))) 33 | self.assertEqual(list(sorted(dict(env1).items())), list(sorted(env1))) 34 | env1["MyEnvVar"] = "TEST" 35 | self.assertEqual(env1["MyEnvVar"], "TEST") 36 | self.assertEqual(env2["MyEnvVar"], None) 37 | d = dict(env1) 38 | self.assertEqual(d["MyEnvVar"], "TEST") 39 | ampl = AMPL(Environment()) 40 | ampl.close() 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /amplpy/tests_camel/test_properties.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from . import TestBase 4 | import unittest 5 | import amplpy 6 | 7 | 8 | class TestProperties(TestBase.TestBase): 9 | """Test Properties.""" 10 | 11 | def testVarProp(self): 12 | ampl = self.ampl 13 | ampl.eval("var x;") 14 | ampl.var["x"] = 10 15 | self.assertEqual(ampl.var["x"].value(), ampl.getVariable("x").value()) 16 | self.assertEqual(len(list(ampl.var)), len(list(ampl.getVariables()))) 17 | 18 | def testConProp(self): 19 | ampl = self.ampl 20 | ampl.eval("var x; s.t. c: x = 10;") 21 | ampl.con["c"] = 10 22 | self.assertEqual(ampl.con["c"].dual(), ampl.getConstraint("c").dual()) 23 | self.assertEqual(len(list(ampl.con)), len(list(ampl.getConstraints()))) 24 | 25 | def testObjProp(self): 26 | ampl = self.ampl 27 | ampl.eval("var x; maximize obj: x;") 28 | self.assertEqual(ampl.obj["obj"].name(), ampl.getObjective("obj").name()) 29 | self.assertEqual(len(list(ampl.obj)), len(list(ampl.getObjectives()))) 30 | 31 | def testSetProp(self): 32 | ampl = self.ampl 33 | ampl.eval("set s;") 34 | ampl.set["s"] = [1, 2, 3] 35 | self.assertEqual(ampl.set["s"].name(), ampl.getSet("s").name()) 36 | self.assertEqual(ampl.set["s"].size(), 3) 37 | self.assertEqual(len(list(ampl.set)), len(list(ampl.getSets()))) 38 | 39 | def testParamProp(self): 40 | ampl = self.ampl 41 | ampl.eval("param p1; param p2{1..2};") 42 | self.assertEqual(ampl.param["p1"].name(), ampl.getParameter("p1").name()) 43 | ampl.param["p1"] = 3 44 | self.assertEqual(ampl.param["p1"].value(), 3) 45 | ampl.param["p2"] = {1: 10, 2: 20} 46 | self.assertEqual(ampl.param["p2"][1], 10) 47 | self.assertEqual(ampl.param["p2"][2], 20) 48 | self.assertEqual(len(list(ampl.param)), len(list(ampl.getParameters()))) 49 | 50 | def testOptionProp(self): 51 | ampl = self.ampl 52 | ampl.option["solver"] = "gurobi" 53 | self.assertEqual(ampl.option["solver"], ampl.getOption("solver")) 54 | 55 | 56 | if __name__ == "__main__": 57 | unittest.main() 58 | -------------------------------------------------------------------------------- /amplpy/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | _parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 6 | _vendor_dir = os.path.join(_parent_dir, "vendor") 7 | if _vendor_dir not in sys.path: 8 | sys.path.append(_vendor_dir) 9 | 10 | try: 11 | from ampltools import * 12 | from ampltools import __version__ 13 | except ImportError: 14 | print( 15 | "Failed to import 'ampltools'. Install or upgrade with:\n" 16 | " $ python -m pip install ampltools --upgrade\n" 17 | ) 18 | raise 19 | 20 | try: 21 | from ampltools import _SUPPORT_MESSAGE 22 | except Exception: 23 | pass -------------------------------------------------------------------------------- /amplpy/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | 5 | def add_to_path(path, head=True): 6 | if head: 7 | os.environ["PATH"] = path + os.pathsep + os.environ["PATH"] 8 | else: 9 | os.environ["PATH"] = os.environ["PATH"] + os.pathsep + path 10 | 11 | 12 | def register_magics(store_name="_ampl_cells", ampl_object=None): 13 | """ 14 | Register jupyter notebook magics ``%%ampl`` and ``%%ampl_eval``. 15 | 16 | Args: 17 | store_name: Name of the store where ``%%ampl cells`` will be stored. 18 | ampl_object: Object used to evaluate ``%%ampl_eval`` cells. 19 | """ 20 | from IPython.core.magic import Magics, magics_class, cell_magic, line_magic 21 | 22 | @magics_class 23 | class StoreAMPL(Magics): 24 | def __init__(self, shell=None, **kwargs): 25 | Magics.__init__(self, shell=shell, **kwargs) 26 | self._store = [] 27 | shell.user_ns[store_name] = self._store 28 | 29 | @cell_magic 30 | def ampl(self, line, cell): 31 | """Store the cell in the store""" 32 | self._store.append(cell) 33 | 34 | @cell_magic 35 | def ampl_eval(self, line, cell): 36 | """Evaluate the cell""" 37 | ampl_object.eval(cell) 38 | 39 | @line_magic 40 | def get_ampl(self, line): 41 | """Retrieve the store""" 42 | return self._store 43 | 44 | get_ipython().register_magics(StoreAMPL) 45 | 46 | 47 | def multidict(d): 48 | ncols = min(len(d[k]) for k in d) 49 | return [list(d.keys())] + [{k: d[k][i] for k in d} for i in range(ncols)] 50 | -------------------------------------------------------------------------------- /amplpy/vendor/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /amplpy/vendor/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## 0.7.5-2024-02-27 6 | - Fix bug when the environment variable PATH is not set. 7 | 8 | ## 0.7.4-2024-01-05 9 | - Update support message. 10 | 11 | ## 0.7.3-2023-12-27 12 | - amplpy.bundle: fix issue "--keep-license" argument. 13 | 14 | ## 0.7.2-2023-12-26 15 | - amplpy.bundle: only include ampl.lic if "--keep-license" is set. 16 | 17 | ## 0.7.1-2023-12-26 18 | - Adjust ampltools.bundle to be used as amplpy.bundle. 19 | 20 | ## 0.7.0-2023-12-22 21 | - Preload modules to the beginning of the PATH. 22 | - Fix module load and unload issues with Pyinstaller. 23 | - Add ampltools.bundle to create executable bundles for distribution using PyInstaller. 24 | 25 | ## 0.6.4 - 2023-11-17 26 | - Fix error after installing modules. 27 | 28 | ## 0.6.3 - 2023-11-15 29 | - Ignore default license outside Google Colab. 30 | 31 | ## 0.6.2 - 2023-09-21 32 | - Only try to activate default license if uuid is "default" or contains "license-uuid". 33 | 34 | ## 0.6.1 - 2023-09-04 35 | - Fix issue with license activation when using ampl_notebook locally. 36 | 37 | ## 0.6.0 - 2023-09-04 38 | - Register magics globally by default. 39 | - Allow ampl_notebook to be used outside notebooks. 40 | - Load modules automatically after installing. 41 | - Allow modules.activate to work with default licenses. 42 | 43 | ## 0.5.6 - 2023-06-06 44 | - Adjust modules.find to find files, not modules. 45 | 46 | ## 0.5.5 - 2023-06-06 47 | - Add modules.find. 48 | 49 | ## 0.5.4 - 2023-05-30 50 | - ampl_notebook: Drop g/globals_ argument. 51 | - ampl_notebook: Remove button to use existing license. 52 | 53 | ## 0.5.3 - 2023-05-03 54 | - Set ampl_libpath when loading modules. 55 | 56 | ## 0.5.2 - 2023-04-05 57 | - Add message to append to AMPLException messages. 58 | 59 | ## 0.5.1 - 2023-03-14 60 | - When a module is not available, try to indicate a bundle with it. 61 | - Add unit tests for modules. 62 | - Make it compatible with Python 3.5. 63 | - Handle read-only filesystems. 64 | 65 | ## 0.5.0 - 2023-03-02 66 | - Add modules.activate and activate command. 67 | - Add modules.preload to load modules silently. 68 | - Add modules.unload to unload modules. 69 | - Add modules.run. 70 | 71 | ## 0.4.6 - 2023-01-24 72 | - Ignore ampl module since it is installed by default. 73 | - Drop tools.load_modules. 74 | 75 | ## 0.4.5 - 2023-01-23 76 | - Show usage message with any errors in main. 77 | 78 | ## 0.4.4 - 2023-01-20 79 | - Remove install_modules from ampltools. 80 | - Move add_to_path to ampltools. 81 | - Add PATH and run commands for modules. 82 | 83 | ## 0.4.3 - 2023-01-19 84 | - Fix bug in tools.modules.uninstall. 85 | 86 | ## 0.4.2 - 2023-01-19 87 | - Do not specify the version of the dependency 'requests'. 88 | - Reduce number of symbols exported. 89 | 90 | ## 0.4.1 - 2023-01-19 91 | - Move modules code to its own submodule. 92 | 93 | ## 0.4.0 - 2023-01-18 94 | - Add command line actions to manage modules. 95 | 96 | ## 0.3.10 - 2023-01-13 97 | - Only show UUID prompt on Colab if any module is not open-source. 98 | 99 | ## 0.3.9 - 2022-12-29 100 | - Allow simplified ampl_notebook invocation with modules and license_uuid only. 101 | 102 | ## 0.3.8 - 2022-12-28 103 | - Handle license_uuid="default". 104 | 105 | ## 0.3.7 - 2022-12-28 106 | - Fix license activation in ampl_notebook when using license_uuid. 107 | 108 | ## 0.3.6 - 2022-12-26 109 | - Use x-ampl by default on Google Colab. 110 | 111 | ## 0.3.5 - 2022-12-23 112 | - Fix line number offset in ampl_eval cells. 113 | 114 | ## 0.3.4 - 2022-12-22 115 | - Make ampl_notebook output even more compact. 116 | 117 | ## 0.3.3 - 2022-12-21 118 | - Make ampl_notebook output more compact. 119 | 120 | ## 0.3.2 - 2022-12-21 121 | - Fixed typo. 122 | 123 | ## 0.3.1 - 2022-12-21 124 | - Simplify output of ampl_notebook. 125 | 126 | ## 0.3.0 - 2022-12-20 127 | - Add support for AMPL modules as python packages. 128 | 129 | ## 0.2.8 - 2022-11-14 130 | - Add default license. 131 | 132 | ## 0.2.7 - 2022-10-24 133 | - Fix Google Colab detection. 134 | 135 | ## 0.2.6 - 2022-10-14 136 | - Add simplified option for ampl_notebook cell. 137 | 138 | ## 0.2.5 - 2022-04-07 139 | - Remove one last f-string present in the code. 140 | 141 | ## 0.2.4 - 2022-04-07 142 | - Avoid the use of f-strings to increase portability. 143 | 144 | ## 0.2.3 - 2022-02-28 145 | - Add ampl_notebook to simplify jupyter notebooks. 146 | 147 | ## 0.2.2 - 2022-02-25 148 | - Add ampl_license_cell and ampl_installer_cell for jupyter notebooks. 149 | 150 | ## 0.2.1 - 2022-02-21 151 | - Fix SageMaker Studio Lab detection in cloud_platform_name(). 152 | 153 | ## 0.2.0 - 2022-02-10 154 | - Add cloud_platform_name to guess the name of the cloud platform currently running on. 155 | 156 | ## 0.1.0 - 2022-02-08 157 | - Initial release with module_installer and ampl_installer. 158 | -------------------------------------------------------------------------------- /amplpy/vendor/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, AMPL Optimization inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /amplpy/vendor/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md CHANGELOG.md LICENSE requirements.txt 2 | 3 | -------------------------------------------------------------------------------- /amplpy/vendor/README.md: -------------------------------------------------------------------------------- 1 | ### AMPL Python Tools 2 | 3 | This package includes tools to use with AMPL and amplpy. 4 | 5 | #### Repositories 6 | 7 | - GitHub Repository: https://github.com/ampl/amplpy/tree/master/ampltools 8 | - PyPI Repository: https://pypi.python.org/pypi/ampltools 9 | 10 | #### Setup 11 | 12 | Install from the [repository](https://pypi.python.org/pypi/ampltools): 13 | ``` 14 | $ python -m pip install ampltools 15 | ``` 16 | 17 | #### License 18 | 19 | BSD-3 20 | 21 | *** 22 | Copyright © 2024 AMPL Optimization inc. All rights reserved. 23 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = "0.7.5" 3 | from .notebooks import ( 4 | ampl_notebook, 5 | ) 6 | 7 | from .modules import ( 8 | activate as activate_license, 9 | cloud_platform_name, 10 | ) 11 | 12 | from .utils import ( 13 | register_magics, 14 | add_to_path, 15 | ) 16 | 17 | _SUPPORT_MESSAGE = """ 18 | 19 | For support/feedback go to https://discuss.ampl.com or e-mail 20 | """ 21 | 22 | try: 23 | from IPython import get_ipython 24 | 25 | register_magics(ampl_object="ampl", globals_=get_ipython().user_global_ns) 26 | except: 27 | pass 28 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/bundle/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .bundle import bundle, _main 3 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/bundle/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from .bundle import _main 4 | 5 | if __name__ == "__main__": 6 | _main() 7 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/bundle/bundle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import shutil 5 | import subprocess 6 | 7 | USAGE = """Usage: 8 | $ python -m $PACKAGE.bundle script.py 9 | """ 10 | 11 | 12 | def bundle(args): 13 | from amplpy import modules, __file__ as amplpy_dir 14 | 15 | main_script = args[0] 16 | extra_arguments = args[1:] 17 | basename = os.path.basename(main_script).replace(".py", "") 18 | dist_dir = os.path.join(os.path.abspath(os.curdir), "dist", basename) 19 | keep_license = "--keep-license" in extra_arguments 20 | if keep_license: 21 | extra_arguments.remove("--keep-license") 22 | 23 | if os.path.isdir(dist_dir): 24 | print(f"Deleting: {dist_dir}") 25 | shutil.rmtree(dist_dir) 26 | 27 | # List of modules and paths to include 28 | pymods = ["amplpy", "ampltools"] + [ 29 | "ampl_module_" + mod for mod in modules.installed() 30 | ] 31 | paths = os.path.join(os.path.dirname(amplpy_dir), "amplpython", "cppinterface") 32 | 33 | # Build PyInstaller command 34 | cmd = [sys.executable, "-m", "PyInstaller", main_script] 35 | for pymod in pymods: 36 | for collect in [ 37 | "--collect-all", 38 | # "--copy-metadata", 39 | # "--collect-submodules", 40 | # "--collect-datas", 41 | # "--collect-binaries", 42 | ]: 43 | cmd += [collect, pymod] 44 | cmd += ["--paths", paths] 45 | cmd += extra_arguments 46 | 47 | # Run the command 48 | print("$ " + " ".join(cmd)) 49 | process = subprocess.Popen( 50 | cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True 51 | ) 52 | for line in process.stdout: 53 | print(line, end="") 54 | exit_code = process.wait() 55 | 56 | if not keep_license: 57 | for fname in ["ampl.lic", "amplkey.log"]: 58 | to_delete = os.path.join( 59 | dist_dir, "_internal", "ampl_module_base", "bin", fname 60 | ) 61 | if os.path.isfile(to_delete): 62 | print(f"Deleting: {to_delete}") 63 | os.remove(to_delete) 64 | 65 | if exit_code == 0: 66 | # Locate the executable 67 | executable = os.path.join(dist_dir, basename) 68 | if not os.path.exists(executable): 69 | executable += ".exe" 70 | if os.path.exists(executable): 71 | print(f'\n\nYour executable is at "{executable}".') 72 | 73 | sys.exit(exit_code) 74 | 75 | 76 | def _main(): 77 | args = sys.argv 78 | if len(args) < 2 or not args[1].endswith(".py"): 79 | print( 80 | USAGE.replace("$PACKAGE", "amplpy" if "amplpy" in args[0] else "ampltools") 81 | ) 82 | sys.exit(1) 83 | bundle(args[1:]) 84 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/modules/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = "0.7.5" 3 | 4 | from .amplpypi import ( 5 | path, 6 | find, 7 | run_command as run, 8 | activate_license as activate, 9 | install_modules as install, 10 | uninstall_modules as uninstall, 11 | load_modules as load, 12 | unload_modules as unload, 13 | preload_modules as preload, 14 | installed_modules as installed, 15 | available_modules as available, 16 | generate_requirements as requirements, 17 | ) 18 | 19 | from .utils import cloud_platform_name 20 | 21 | from .commands import _main 22 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/modules/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from .commands import _main 4 | 5 | if __name__ == "__main__": 6 | _main() 7 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/modules/commands.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .amplpypi import ( 3 | install_modules, 4 | uninstall_modules, 5 | installed_modules, 6 | available_modules, 7 | generate_requirements, 8 | load_modules, 9 | path, 10 | find, 11 | activate_license, 12 | ) 13 | import subprocess 14 | import sys 15 | 16 | ERROR = """ 17 | Invalid command. 18 | Valid commands: 19 | install, uninstall, list/installed, available, requirements, path, 20 | run, and activate. 21 | """ 22 | USAGE = """Usage: 23 | - Install modules: 24 | $ python -m $PACKAGE.modules install ... 25 | Example: 26 | $ python -m $PACKAGE.modules install highs gurobi 27 | 28 | - Uninstall modules: 29 | $ python -m $PACKAGE.modules uninstall ... 30 | Example: 31 | $ python -m $PACKAGE.modules uninstall highs gurobi 32 | $ python -m $PACKAGE.modules uninstall # uninstall all modules 33 | 34 | - List installed modules: 35 | $ python -m $PACKAGE.modules installed 36 | 37 | - List modules available to be installed: 38 | $ python -m $PACKAGE.modules available 39 | 40 | - Value to append to the environment variable PATH to access modules 41 | $ python -m $PACKAGE.modules path 42 | Example: 43 | $ export PATH=$PATH:`python -m amplpy.modules path` 44 | 45 | - Find the path to a file in any module 46 | $ python -m $PACKAGE.modules find 47 | Example: 48 | $ python -m amplpy.modules find gurobi 49 | 50 | - Generate requirements.txt for the modules currently installed 51 | $ python -m $PACKAGE.modules requirements 52 | 53 | - Run command in the same environment as the modules: 54 | $ python -m $PACKAGE.modules run 55 | Example: 56 | $ python -m $PACKAGE.modules run ampl -v 57 | 58 | - Activate a license using amplkey: 59 | $ python -m $PACKAGE.modules activate 60 | """ 61 | 62 | 63 | def _main(): 64 | try: 65 | _commands(sys.argv) 66 | except Exception as e: 67 | print(f"Error: {e}", file=sys.stderr) 68 | sys.exit(1) 69 | 70 | 71 | def verbose_option(options): 72 | for opt in options: 73 | if opt.startswith("--"): 74 | if opt == "--quiet": 75 | return False 76 | elif opt.startswith("-"): 77 | if "q" in opt: 78 | return False 79 | return True 80 | 81 | 82 | def _commands(args): 83 | usage = USAGE.replace("$PACKAGE", "amplpy" if "amplpy" in args[0] else "ampltools") 84 | if len(args) < 2: 85 | print(usage) 86 | return 87 | command, args = args[1].lower(), args[2:] 88 | if command == "usage": 89 | print(usage) 90 | elif command == "install": 91 | modules = [m for m in args if not m.startswith("-")] 92 | options = [o for o in args if o.startswith("-")] 93 | install_modules( 94 | modules=modules, options=options, verbose=verbose_option(options) 95 | ) 96 | elif command == "uninstall": 97 | modules = [m for m in args if not m.startswith("-")] 98 | options = [o for o in args if o.startswith("-")] 99 | uninstall_modules( 100 | modules=modules, options=options, verbose=verbose_option(options) 101 | ) 102 | elif command in ("list", "installed"): 103 | names = installed_modules() 104 | if names == []: 105 | raise Exception("Could not find any modules installed.") 106 | print("You have the following modules installed:") 107 | for name in sorted(set(names)): 108 | print("\t" + name) 109 | elif command == "available": 110 | names = available_modules() 111 | if names == []: 112 | raise Exception("Could not find any modules for download.") 113 | print("You can install any of the following modules:") 114 | for name in sorted(set(names)): 115 | print("\t" + name) 116 | elif command == "path": 117 | modules = [m for m in args if not m.startswith("-")] 118 | print(path(modules)) 119 | elif command == "find": 120 | if len(args) != 1: 121 | raise Exception(ERROR + usage) 122 | filename = args[0] 123 | print(find(filename)) 124 | elif command == "run": 125 | if len(args) == 0: 126 | raise Exception(ERROR + usage) 127 | load_modules() 128 | p = subprocess.run(" ".join(args), shell=True) 129 | if p.returncode != 0: 130 | raise Exception(f"Exit code {p.returncode}") 131 | elif command == "activate": 132 | if len(args) != 1: 133 | raise Exception(ERROR + usage) 134 | uuid = args[0] 135 | activate_license(uuid, verbose=True) 136 | elif command == "requirements": 137 | modules = [m for m in args if not m.startswith("-")] 138 | print(generate_requirements(modules)) 139 | else: 140 | raise Exception(ERROR + usage) 141 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/modules/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import platform 3 | import os 4 | 5 | 6 | def cloud_platform_name(): 7 | """Guesses the name of cloud platform currently running on.""" 8 | if platform.system() != "Linux": 9 | return None 10 | envkeys = dict(os.environ).keys() 11 | if any(key.startswith("COLAB_") for key in envkeys): 12 | return "colab" 13 | if os.path.isdir("/content/"): 14 | return "colab" 15 | if any(key.startswith("KAGGLE_") for key in envkeys): 16 | return "kaggle" 17 | if any(key.startswith("PAPERSPACE_") for key in envkeys): 18 | return "paperspace" 19 | if os.path.isdir("/home/studio-lab-user"): 20 | return "sagemaker-studio-lab" 21 | return None 22 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/notebooks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from .modules import activate as activate_license 4 | from .modules import install as install_modules 5 | from .modules import load as load_modules 6 | from .modules import cloud_platform_name 7 | from .utils import ( 8 | register_magics, 9 | _is_valid_uuid, 10 | ) 11 | 12 | 13 | def _deactivate_license(): 14 | if "AMPL_LICFILE_DEFAULT" in os.environ: 15 | os.environ["AMPL_LICFILE"] = os.environ["AMPL_LICFILE_DEFAULT"] 16 | elif "AMPL_LICFILE" in os.environ: 17 | del os.environ["AMPL_LICFILE"] 18 | 19 | 20 | def _ampl_license_cell(check_callback): 21 | try: 22 | import ipywidgets as widgets 23 | except ImportError: 24 | check_callback() 25 | return 26 | from IPython.display import display 27 | 28 | header = widgets.Output() 29 | message = widgets.Output() 30 | version = widgets.Output() 31 | 32 | platform = cloud_platform_name() 33 | with header: 34 | print( 35 | "AMPL License UUID (you can use free https://ampl.com/ce or https://ampl.com/courses licenses):" 36 | ) 37 | with message: 38 | ampl_lic = os.environ.get("AMPL_LICFILE", None) 39 | if ampl_lic is not None and platform is None: 40 | print(f"License license at {ampl_lic}.") 41 | with version: 42 | if check_callback: 43 | check_callback() 44 | 45 | uuid_input = widgets.Text( 46 | description="License UUID:", style={"description_width": "initial"} 47 | ) 48 | 49 | def activate(where): 50 | uuid = uuid_input.value.strip() 51 | if where == "uuid" and len(uuid) == 0: 52 | return 53 | message.clear_output(wait=False) 54 | with message: 55 | if where == "uuid": 56 | if len(uuid) == 36: 57 | uuid_input.value = "" # clear the input 58 | try: 59 | activate_license(uuid) 60 | print("License activated.") 61 | except: 62 | print("Failed to activate license.") 63 | _deactivate_license() 64 | else: 65 | print("Please provide a license UUID or use an existing license.") 66 | return 67 | 68 | version.clear_output(wait=False) 69 | with version: 70 | if check_callback: 71 | check_callback() 72 | 73 | uuid_input.observe(lambda d: activate("uuid"), "value") 74 | display(widgets.VBox([header, widgets.HBox([uuid_input]), message, version])) 75 | 76 | 77 | def ampl_notebook( 78 | modules=[], 79 | license_uuid=None, 80 | reinstall=False, 81 | verbose=False, 82 | show_license=None, 83 | **kwargs, 84 | ): 85 | try: 86 | from IPython import get_ipython 87 | except: 88 | get_ipython = lambda: None 89 | globals_ = kwargs.get("globals_", kwargs.get("g", None)) 90 | if globals_ is None: 91 | if get_ipython() is not None: 92 | globals_ = get_ipython().user_global_ns 93 | else: 94 | globals_ = {} 95 | 96 | show_prompt = get_ipython() is not None and globals_ is not None 97 | if show_license is None: 98 | show_license = True 99 | 100 | def instantiate_ampl(print_license=True): 101 | from amplpy import AMPL 102 | 103 | ampl = AMPL() 104 | if print_license: 105 | version = ampl.option["version"] 106 | for row in version.split("\n"): 107 | if row.startswith("Licensed to "): 108 | print(row) 109 | break 110 | else: 111 | print(version) 112 | globals_["ampl"] = ampl 113 | 114 | if modules not in (None, []): 115 | install_modules(modules, reinstall=reinstall, verbose=verbose) 116 | else: 117 | modules = [] 118 | 119 | open_modules = [ 120 | "highs", 121 | "cbc", 122 | "couenne", 123 | "bonmin", 124 | "ipopt", 125 | "coin", 126 | "gcg", 127 | "scip", 128 | "gecode", 129 | "open", 130 | "plugins", 131 | "ampl", 132 | ] 133 | open_source_only = len(set(modules) - set(open_modules)) == 0 134 | 135 | using_default_license = license_uuid is not None and ( 136 | license_uuid == "default" or "license-uuid" in license_uuid 137 | ) 138 | 139 | # Ignore default license outside colab 140 | if using_default_license and cloud_platform_name() != "colab": 141 | using_default_license = False 142 | license_uuid = None 143 | 144 | if using_default_license: 145 | using_default_license = activate_license("default") 146 | if using_default_license: 147 | if show_prompt and open_source_only: 148 | show_prompt = False 149 | if not show_prompt: 150 | print( 151 | "Using default Community Edition License for Colab. " 152 | "Get yours at: https://ampl.com/ce" 153 | ) 154 | 155 | if using_default_license: 156 | if show_prompt: 157 | _ampl_license_cell(check_callback=instantiate_ampl) 158 | else: 159 | instantiate_ampl(print_license=show_license) 160 | elif not _is_valid_uuid(license_uuid): 161 | if license_uuid not in (None, ""): 162 | print( 163 | "Please provide a valid license UUID. " 164 | "You can use a free https://ampl.com/ce license." 165 | ) 166 | _deactivate_license() 167 | if show_prompt: 168 | _ampl_license_cell(check_callback=instantiate_ampl) 169 | else: 170 | instantiate_ampl(print_license=True) 171 | else: 172 | try: 173 | activate_license(license_uuid) 174 | instantiate_ampl(print_license=show_license) 175 | except: 176 | print("Failed to activate license.") 177 | _deactivate_license() 178 | if show_prompt: 179 | _ampl_license_cell(check_callback=instantiate_ampl) 180 | else: 181 | instantiate_ampl(print_license=True) 182 | 183 | if get_ipython() is not None: 184 | register_magics(ampl_object="ampl", globals_=globals_) 185 | return globals_.get("ampl", None) 186 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/tests/TestBase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | import os 5 | from .. import modules 6 | 7 | 8 | class TestBase(unittest.TestCase): 9 | def setUp(self): 10 | modules.uninstall() 11 | 12 | def get_env_path(self): 13 | return os.environ.get("PATH", "") 14 | 15 | def get_env_path_list(self): 16 | return self.get_env_path().split(os.pathsep) 17 | 18 | def tearDown(self): 19 | modules.uninstall() 20 | 21 | 22 | if __name__ == "__main__": 23 | unittest.main() 24 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/tests/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import unittest 5 | 6 | from .test_preload import TestPreload as Test1 7 | from .test_install import TestInstall as Test2 8 | from .test_load import TestLoad as Test3 9 | from .test_run import TestRun as Test4 10 | 11 | if __name__ == "__main__": 12 | unittest.main() 13 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/tests/test_install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | 5 | from .. import modules 6 | from . import TestBase 7 | 8 | 9 | class TestInstall(TestBase.TestBase): 10 | """Test ampltools.modules: install, uninstall, available, installed, and requirements.""" 11 | 12 | def test_install_verbose(self): 13 | self._test_install(verbose=True) 14 | 15 | def test_install_silent(self): 16 | self._test_install(verbose=False) 17 | 18 | def _test_install(self, verbose): 19 | self.assertEqual(modules.installed(), []) 20 | 21 | modules.install("highs", verbose=verbose) 22 | self.assertEqual(modules.installed(), ["base", "highs"]) 23 | 24 | with self.assertRaises(Exception) as context: 25 | modules.install("high", verbose=verbose) 26 | self.assertTrue( 27 | "AMPL module 'high' is not available." in str(context.exception) 28 | ) 29 | 30 | with self.assertRaises(Exception) as context: 31 | modules.install("ipopt", verbose=verbose) 32 | self.assertTrue( 33 | "AMPL module 'ipopt' is not available. It is included in module 'coin'." 34 | in str(context.exception) 35 | ) 36 | 37 | modules.install(["coin"], verbose=verbose) 38 | self.assertEqual(set(modules.installed()), {"base", "highs", "coin"}) 39 | 40 | modules.uninstall("highs", verbose=verbose) 41 | self.assertEqual(set(modules.installed()), {"base", "coin"}) 42 | 43 | with self.assertRaises(Exception) as context: 44 | modules.uninstall("base") 45 | 46 | self.assertTrue( 47 | "Base module cannot be uninstalled alone. You need to uninstall all modules." 48 | in str(context.exception) 49 | ) 50 | 51 | modules.uninstall("coin", verbose=verbose) 52 | self.assertEqual(set(modules.installed()), {"base"}) 53 | 54 | modules.uninstall("base", verbose=verbose) 55 | self.assertEqual(modules.installed(), []) 56 | 57 | modules.install(["cbc", "highs"], verbose=verbose) 58 | self.assertEqual(set(modules.installed()), {"base", "highs", "cbc"}) 59 | 60 | modules.uninstall(verbose=verbose) 61 | self.assertEqual(modules.installed(), []) 62 | 63 | def test_available(self): 64 | self.assertTrue(isinstance(modules.available(), list)) 65 | self.assertGreaterEqual(len(modules.available()), 5) 66 | self.assertTrue("base" in modules.available()) 67 | self.assertTrue("highs" in modules.available()) 68 | self.assertTrue("coin" in modules.available()) 69 | self.assertTrue("cbc" in modules.available()) 70 | self.assertFalse("ipopt" in modules.available()) 71 | 72 | def test_requirements(self): 73 | self.assertEqual(modules.installed(), []) 74 | ampl_pypi = "--index-url https://pypi.ampl.com" 75 | extra_pypi = "--extra-index-url https://pypi.org/simple" 76 | 77 | requirements = modules.requirements().strip().split("\n") 78 | print("requirements:", requirements) 79 | self.assertEqual(requirements[0], ampl_pypi) 80 | self.assertEqual(requirements[1], extra_pypi) 81 | self.assertEqual(len(requirements), 2) 82 | 83 | modules.install(["cbc", "highs"]) 84 | 85 | requirements = modules.requirements().strip().split("\n") 86 | self.assertEqual(requirements[0], ampl_pypi) 87 | self.assertEqual(requirements[1], extra_pypi) 88 | self.assertTrue( 89 | any(req.startswith("ampl_module_base==") for req in requirements[2:]) 90 | ) 91 | self.assertTrue( 92 | any(req.startswith("ampl_module_cbc==") for req in requirements[2:]) 93 | ) 94 | self.assertTrue( 95 | any(req.startswith("ampl_module_highs==") for req in requirements[2:]) 96 | ) 97 | 98 | 99 | if __name__ == "__main__": 100 | unittest.main() 101 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/tests/test_load.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | import os 5 | 6 | from .. import modules 7 | from . import TestBase 8 | 9 | 10 | class TestLoad(TestBase.TestBase): 11 | """Test ampltools.modules: load, unload, and path.""" 12 | 13 | def test_load_verbose(self): 14 | self._test_load(verbose=True) 15 | 16 | def test_load_silent(self): 17 | self._test_load(verbose=False) 18 | 19 | def _test_load(self, verbose): 20 | self.assertEqual(modules.installed(), []) 21 | 22 | initial_path = self.get_env_path_list() 23 | 24 | self.assertEqual(modules.installed(), []) 25 | modules.preload(verbose=verbose) 26 | self.assertEqual(initial_path, self.get_env_path_list()) 27 | 28 | modules.install("highs", verbose=verbose) 29 | self.assertNotEqual(initial_path, self.get_env_path_list()) 30 | self.assertTrue(os.path.isfile(modules.find("highs"))) 31 | 32 | modules.load("highs", verbose=verbose) 33 | self.assertNotEqual(initial_path, self.get_env_path_list()) 34 | expected = modules.path().split(os.pathsep) + initial_path 35 | self.assertEqual(expected, self.get_env_path_list()) 36 | self.assertEqual(len(initial_path) + 2, len(self.get_env_path_list())) 37 | self.assertIn(modules.path("highs", add_base=False), self.get_env_path_list()) 38 | 39 | modules.install(["cbc"], verbose=verbose) 40 | modules.unload(["base", "cbc", "highs"]) 41 | modules.load(["cbc", "highs"], verbose=verbose) 42 | expected = modules.path().split(os.pathsep) + initial_path 43 | self.assertEqual(expected, self.get_env_path_list()) 44 | self.assertEqual(len(initial_path) + 3, len(self.get_env_path_list())) 45 | 46 | modules.unload("highs") 47 | self.assertNotEqual(initial_path, self.get_env_path_list()) 48 | self.assertEqual(len(initial_path) + 2, len(self.get_env_path_list())) 49 | 50 | modules.unload(["cbc"]) 51 | self.assertNotEqual(initial_path, self.get_env_path_list()) 52 | self.assertEqual(len(initial_path) + 1, len(self.get_env_path_list())) 53 | 54 | modules.unload("base") 55 | self.assertEqual(initial_path, self.get_env_path_list()) 56 | 57 | 58 | if __name__ == "__main__": 59 | unittest.main() 60 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/tests/test_preload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | 5 | from .. import modules 6 | from . import TestBase 7 | 8 | 9 | class TestPreload(TestBase.TestBase): 10 | """Test ampltools.modules: preload.""" 11 | 12 | def test_preload_verbose(self): 13 | self._test_preload(verbose=True) 14 | 15 | def test_preload_silent(self): 16 | self._test_preload(verbose=False) 17 | 18 | def _test_preload(self, verbose): 19 | """When preloading without ampl_module_base it is not loaded 20 | afterwards unless we reload modules. 21 | """ 22 | modules.unload() 23 | initial_path = self.get_env_path_list() 24 | modules.preload(verbose=verbose) 25 | self.assertEqual(modules.installed(), []) 26 | requirements = modules.requirements().strip().split("\n") 27 | self.assertEqual(len(requirements), 2) 28 | 29 | with self.assertRaises(Exception) as context: 30 | modules.load("cbc", verbose=verbose) 31 | self.assertTrue( 32 | "Module ampl_module_base needs to be reinstalled." in str(context.exception) 33 | ) 34 | 35 | modules.install(["cbc"], verbose=verbose) 36 | self.assertEqual(set(modules.installed()), {"base", "cbc"}) 37 | modules.load() 38 | self.assertTrue( 39 | any("ampl_module_cbc" in path for path in self.get_env_path_list()) 40 | ) 41 | self.assertTrue( 42 | any("ampl_module_base" in path for path in self.get_env_path_list()) 43 | ) 44 | 45 | self.assertEqual(len(initial_path) + 2, len(self.get_env_path_list())) 46 | 47 | requirements = modules.requirements().strip().split("\n") 48 | self.assertEqual(len(requirements), 4) 49 | 50 | self.assertTrue( 51 | any(req.startswith("ampl_module_base==") for req in requirements[2:]) 52 | ) 53 | self.assertTrue( 54 | any(req.startswith("ampl_module_cbc==") for req in requirements[2:]) 55 | ) 56 | 57 | modules.uninstall(["cbc"], verbose=verbose) 58 | self.assertEqual(set(modules.installed()), {"base"}) 59 | 60 | self.assertEqual(len(initial_path) + 1, len(self.get_env_path_list())) 61 | 62 | requirements = modules.requirements().strip().split("\n") 63 | self.assertEqual(len(requirements), 3) 64 | 65 | self.assertTrue( 66 | any(req.startswith("ampl_module_base==") for req in requirements[2:]) 67 | ) 68 | self.assertFalse( 69 | any(req.startswith("ampl_module_cbc==") for req in requirements[2:]) 70 | ) 71 | 72 | 73 | if __name__ == "__main__": 74 | unittest.main() 75 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/tests/test_run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | import os 5 | 6 | from .. import modules 7 | from . import TestBase 8 | 9 | 10 | class TestRun(TestBase.TestBase): 11 | """Test ampltools.modules: run, and activate.""" 12 | 13 | def test_run_verbose(self): 14 | self._test_run(verbose=True) 15 | 16 | def test_run_silent(self): 17 | self._test_run(verbose=False) 18 | 19 | def _test_run(self, verbose): 20 | uuid = os.environ.get("AMPLKEY_UUID", None) 21 | if uuid is None: 22 | raise unittest.SkipTest("Needs AMPLKEY_UUID to run.") 23 | 24 | modules.install("base", verbose=verbose) 25 | modules.load("base", verbose=verbose) 26 | 27 | modules.activate(uuid, verbose=verbose) 28 | self.assertEqual( 29 | modules.run(f"amplkey activate --uuid {uuid}", verbose=verbose), 0 30 | ) 31 | self.assertEqual( 32 | modules.run(["amplkey", "activate", "--uuid", uuid], verbose=verbose), 33 | 0, 34 | ) 35 | 36 | def test_error_verbose(self): 37 | self._test_error(verbose=True) 38 | 39 | def test_error_silent(self): 40 | self._test_error(verbose=False) 41 | 42 | def _test_error(self, verbose): 43 | modules.install("base", verbose=verbose) 44 | modules.load("base", verbose=verbose) 45 | 46 | with self.assertRaises(Exception) as context: 47 | modules.activate("invalid-uuid", verbose=verbose) 48 | self.assertTrue("The license activation failed." in str(context.exception)) 49 | 50 | self.assertNotEqual( 51 | modules.run("amplkey activate --uuid invalid-uuid", verbose=verbose), 0 52 | ) 53 | self.assertNotEqual( 54 | modules.run( 55 | ["amplkey", "activate", "--uuid", "invalid-uuid"], verbose=verbose 56 | ), 57 | 0, 58 | ) 59 | 60 | 61 | if __name__ == "__main__": 62 | unittest.main() 63 | -------------------------------------------------------------------------------- /amplpy/vendor/ampltools/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from uuid import UUID 4 | 5 | 6 | def _is_valid_uuid(uuid): 7 | if uuid in (None, ""): 8 | return False 9 | try: 10 | UUID(str(uuid)) 11 | except ValueError: 12 | return False 13 | 14 | return True 15 | 16 | 17 | def add_to_path(path, head=True): 18 | if head: 19 | os.environ["PATH"] = path + os.pathsep + os.environ.get("PATH", "") 20 | else: 21 | os.environ["PATH"] = os.environ.get("PATH", "") + os.pathsep + path 22 | 23 | 24 | def register_magics(store_name="_ampl_cells", ampl_object=None, globals_=None): 25 | """ 26 | Register jupyter notebook magics ``%%ampl`` and ``%%ampl_eval``. 27 | Args: 28 | store_name: Name of the store where ``%%ampl cells`` will be stored. 29 | ampl_object: Object used to evaluate ``%%ampl_eval`` cells. 30 | """ 31 | from IPython.core.magic import Magics, magics_class, cell_magic, line_magic 32 | from IPython import get_ipython 33 | 34 | @magics_class 35 | class StoreAMPL(Magics): 36 | def __init__(self, shell=None, **kwargs): 37 | Magics.__init__(self, shell=shell, **kwargs) 38 | self._store = [] 39 | shell.user_ns[store_name] = self._store 40 | 41 | @cell_magic 42 | def ampl(self, line, cell): 43 | """Store the cell in the store""" 44 | self._store.append(cell) 45 | 46 | @cell_magic 47 | def ampl_eval(self, line, cell): 48 | """Evaluate the cell""" 49 | if globals_ is not None and isinstance(ampl_object, str): 50 | ampl = globals_[ampl_object] 51 | else: 52 | ampl = ampl_object 53 | ampl.eval("\n" + cell) 54 | 55 | @line_magic 56 | def get_ampl(self, line): 57 | """Retrieve the store""" 58 | return self._store 59 | 60 | get_ipython().register_magics(StoreAMPL) 61 | -------------------------------------------------------------------------------- /amplpy/vendor/bumpversion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | set -ex 4 | 5 | if [ "$#" -eq 0 ]; then 6 | echo "Usage: $0 " 7 | else 8 | version=$1 9 | sed -i~ "s/version=\"[^']*\"/version=\"$version\"/" setup.py 10 | sed -i~ "s/__version__ = \"[^']*\"/__version__ = \"$version\"/" ampltools/__init__.py 11 | sed -i~ "s/__version__ = \"[^']*\"/__version__ = \"$version\"/" ampltools/modules/__init__.py 12 | sed -i~ "s/ampltools[ ]*>=[ ]*[^\"]*\"/ampltools >= $version\"/" ../../setup.py 13 | fi 14 | -------------------------------------------------------------------------------- /amplpy/vendor/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | set -ex 4 | 5 | find ./ -name "*~" -exec rm -v "{}" \; 2>/dev/null 6 | find ./ -name "*.pyc" -exec rm -v "{}" \; 2>/dev/null 7 | find ./ -name "*.pyo" -exec rm -v "{}" \; 2>/dev/null 8 | find ./ -name "*.o" -exec rm -v "{}" \; 2>/dev/null 9 | find ./ -name "__pycache__" -exec rm -rf "{}" \; 2>/dev/null 10 | 11 | rm -rf src/.deps/ 12 | rm -f src/stamp-h1 13 | rm -f src/.dirstamp 14 | rm -f config.status 15 | rm -rf autom4te.cache/ 16 | rm -f Makefile 17 | rm -f config.log 18 | rm -rf *.egg-info 19 | 20 | rm -rf build 21 | rm -rf dist 22 | -------------------------------------------------------------------------------- /amplpy/vendor/register.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | set -ex 4 | 5 | bash clear.sh 6 | rm -rf dist/* 7 | rm -rf build *.egg-info 8 | 9 | #python setup.py register 10 | #python setup.py build 11 | python setup.py sdist bdist_wheel 12 | twine upload dist/*.tar.gz dist/*.whl --skip-existing 13 | #python setup.py sdist bdist_wheel upload 14 | -------------------------------------------------------------------------------- /amplpy/vendor/requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /amplpy/vendor/setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # AMPL Python Tools 4 | 5 | This package includes tools to use with [AMPL](https://ampl.com) and [amplpy](https://amplpy.readthedocs.io). 6 | 7 | ## Links 8 | 9 | * GitHub Repository: https://github.com/ampl/amplpy/ 10 | * PyPI Repository: https://pypi.python.org/pypi/ampltools 11 | """ 12 | from setuptools import setup 13 | import os 14 | 15 | 16 | def ls_dir(base_dir): 17 | """List files recursively.""" 18 | return [ 19 | os.path.join(dirpath.replace(base_dir, "", 1), f) 20 | for (dirpath, dirnames, files) in os.walk(base_dir) 21 | for f in files 22 | ] 23 | 24 | 25 | setup( 26 | name="ampltools", 27 | version="0.7.5", 28 | description="AMPL Python Tools", 29 | long_description=__doc__, 30 | long_description_content_type="text/markdown", 31 | license="BSD-3", 32 | platforms="any", 33 | author="Filipe Brandão", 34 | author_email="fdabrandao@ampl.com", 35 | url="http://ampl.com/", 36 | download_url="https://github.com/ampl/amplpy/tree/master/ampltools", 37 | classifiers=[ 38 | "Development Status :: 4 - Beta", 39 | "Environment :: Console", 40 | "Topic :: Software Development", 41 | "Topic :: Scientific/Engineering", 42 | "Intended Audience :: Developers", 43 | "Intended Audience :: Science/Research", 44 | "License :: OSI Approved :: BSD License", 45 | "Operating System :: POSIX", 46 | "Operating System :: Unix", 47 | "Operating System :: MacOS", 48 | "Operating System :: Microsoft :: Windows", 49 | "Programming Language :: Python", 50 | "Programming Language :: Python :: 3", 51 | "Programming Language :: Python :: Implementation :: CPython", 52 | ], 53 | install_requires=open("requirements.txt").read().split("\n"), 54 | packages=["ampltools"], 55 | package_data={"": ls_dir("ampltools/")}, 56 | ) 57 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | CIBW_SKIP: pp* cp27-* cp36-* cp37-* *_i686 *-win32 *musllinux* 3 | CIBW_ARCHS_MACOS: x86_64 universal2 4 | CIBW_TEST_REQUIRES: --index-url https://pypi.ampl.com --extra-index-url https://pypi.org/simple ampl_module_base ampl_module_highs ampl_module_gurobi pandas numpy 5 | CIBW_TEST_REQUIRES_AARCH64: --index-url https://pypi.ampl.com --extra-index-url https://pypi.org/simple ampl_module_base ampl_module_highs 6 | 7 | stages: 8 | - stage: native 9 | displayName: 'Build native' 10 | jobs: 11 | - job: docs 12 | pool: {vmImage: 'Ubuntu-22.04'} 13 | steps: 14 | - task: UsePythonVersion@0 15 | - bash: | 16 | set -ex 17 | python dev/updatelib.py 18 | python -m pip install setuptools cython==3.1.0 19 | python -m pip install . 20 | python setup.py sdist -d upload 21 | cd docs 22 | pip install -r requirements-dev.txt --upgrade 23 | make html 24 | cd .. 25 | mkdir -p upload/doc 26 | cp -r docs/build/html upload/doc/python 27 | displayName: Build documentation 28 | - task: PublishBuildArtifacts@1 29 | inputs: {pathtoPublish: 'upload'} 30 | - job: manylinux 31 | pool: {vmImage: 'Ubuntu-22.04'} 32 | steps: 33 | - task: UsePythonVersion@0 34 | - bash: | 35 | set -ex 36 | python dev/updatelib.py 37 | export CIBW_TEST_COMMAND="python -m amplpy.modules activate ${AMPLKEY_UUID} && python -m amplpy.tests" 38 | python -m pip install --upgrade cibuildwheel==2.21.3 39 | cibuildwheel --platform linux --output-dir wheelhouse . 40 | displayName: Build wheels 41 | env: 42 | AMPLKEY_UUID: $(AMPLKEY_UUID) 43 | - task: PublishBuildArtifacts@1 44 | inputs: {pathtoPublish: 'wheelhouse'} 45 | - job: macos 46 | pool: {vmImage: 'macos-latest'} 47 | steps: 48 | - task: UsePythonVersion@0 49 | - bash: | 50 | set -ex 51 | python dev/updatelib.py 52 | export CIBW_TEST_COMMAND="python -m amplpy.modules activate ${AMPLKEY_UUID} && python -m amplpy.tests" 53 | python -m pip install --upgrade cibuildwheel==2.21.3 54 | cibuildwheel --platform macos --output-dir wheelhouse . 55 | displayName: Build wheels 56 | env: 57 | AMPLKEY_UUID: $(AMPLKEY_UUID) 58 | - task: PublishBuildArtifacts@1 59 | inputs: {pathtoPublish: 'wheelhouse'} 60 | - job: windows 61 | pool: {vmImage: 'windows-2022'} 62 | steps: 63 | - task: UsePythonVersion@0 64 | - bash: | 65 | set -ex 66 | python dev/updatelib.py 67 | export CIBW_TEST_COMMAND="python -m amplpy.modules activate ${AMPLKEY_UUID} && python -m amplpy.tests" 68 | python -m pip install --upgrade cibuildwheel==2.21.3 69 | cibuildwheel --platform windows --output-dir wheelhouse . 70 | displayName: Build wheels 71 | env: 72 | AMPLKEY_UUID: $(AMPLKEY_UUID) 73 | - task: PublishBuildArtifacts@1 74 | inputs: {pathtoPublish: 'wheelhouse'} 75 | 76 | - stage: qemu 77 | displayName: 'Build with QEMU' 78 | jobs: 79 | - job: qemu 80 | pool: {vmImage: 'Ubuntu-22.04'} 81 | strategy: 82 | matrix: 83 | aarch64 cp38: 84 | CIBW_BUILD: cp38-* 85 | CIBW_ARCHS_LINUX: aarch64 86 | CIBW_TEST_REQUIRES: $(CIBW_TEST_REQUIRES_AARCH64) 87 | aarch64 cp39: 88 | CIBW_BUILD: cp39-* 89 | CIBW_ARCHS_LINUX: aarch64 90 | CIBW_TEST_REQUIRES: $(CIBW_TEST_REQUIRES_AARCH64) 91 | aarch64 cp310: 92 | CIBW_BUILD: cp310-* 93 | CIBW_ARCHS_LINUX: aarch64 94 | CIBW_TEST_REQUIRES: $(CIBW_TEST_REQUIRES_AARCH64) 95 | aarch64 cp311: 96 | CIBW_BUILD: cp311-* 97 | CIBW_ARCHS_LINUX: aarch64 98 | CIBW_TEST_REQUIRES: $(CIBW_TEST_REQUIRES_AARCH64) 99 | aarch64 cp312: 100 | CIBW_BUILD: cp312-* 101 | CIBW_ARCHS_LINUX: aarch64 102 | CIBW_TEST_REQUIRES: $(CIBW_TEST_REQUIRES_AARCH64) 103 | aarch64 cp313: 104 | CIBW_BUILD: cp313-* 105 | CIBW_ARCHS_LINUX: aarch64 106 | CIBW_TEST_REQUIRES: $(CIBW_TEST_REQUIRES_AARCH64) 107 | steps: 108 | - task: UsePythonVersion@0 109 | - bash: docker run --rm --privileged docker.io/tonistiigi/binfmt:qemu-v8.1.5 --install all 110 | displayName: Configure qemu (temp solution until qemu fix) 111 | - bash: | 112 | set -ex 113 | python dev/updatelib.py 114 | python -m pip install --upgrade cibuildwheel==2.21.3 115 | cibuildwheel --platform linux --output-dir wheelhouse . 116 | displayName: Build wheels 117 | - task: PublishBuildArtifacts@1 118 | inputs: {pathtoPublish: 'wheelhouse'} 119 | 120 | - stage: publish 121 | jobs: 122 | - job: upload 123 | pool: {vmImage: 'ubuntu-latest'} 124 | steps: 125 | - task: DownloadBuildArtifacts@0 126 | inputs: 127 | buildType: 'current' 128 | downloadPath: 'artifacts/' 129 | artifactName: 'drop' 130 | displayName: 'Download current pipeline artifacts' 131 | - bash: mv artifacts/drop artifacts/amplpy 132 | displayName: Rename directory 133 | - task: PublishPipelineArtifact@1 134 | inputs: 135 | targetPath: 'artifacts/' 136 | artifact: 'release' 137 | artifactType: 'pipeline' 138 | -------------------------------------------------------------------------------- /dev/bumpversion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | cd .. 4 | set -ex 5 | 6 | if [ "$#" -eq 0 ]; then 7 | echo "Usage: $0 " 8 | else 9 | version=$1 10 | sed -i~ "s/amplpy==.*/amplpy==$version/" docs/requirements.txt 11 | sed -i~ "s/version=\"[^\"]*\"/version=\"$version\"/" setup.py 12 | sed -i~ "s/__version__ = \"[^\"]*\"/__version__ = \"$version\"/" amplpy/__init__.py 13 | fi 14 | 15 | -------------------------------------------------------------------------------- /dev/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | cd .. 4 | set -ex 5 | 6 | find ./ -name "*~" -exec rm -v "{}" \; 2>/dev/null 7 | find ./ -name "*.pyc" -exec rm -v "{}" \; 2>/dev/null 8 | find ./ -name "*.pyo" -exec rm -v "{}" \; 2>/dev/null 9 | find ./ -name "*.o" -exec rm -v "{}" \; 2>/dev/null 10 | find ./ -name "__pycache__" -exec rm -rf "{}" \; 2>/dev/null 11 | 12 | rm -rf src/.deps/ 13 | rm -f src/stamp-h1 14 | rm -f src/.dirstamp 15 | rm -f config.status 16 | rm -rf autom4te.cache/ 17 | rm -f Makefile 18 | rm -f config.log 19 | rm -rf *.egg-info 20 | 21 | rm -rf build 22 | rm -rf dist 23 | -------------------------------------------------------------------------------- /dev/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | cd .. 4 | set -ex 5 | 6 | # python -m pip install coverage 7 | coverage run examples/test_examples.py 8 | coverage run -a -m amplpy.tests_camel 9 | coverage run -a -m amplpy.tests 10 | coverage report 11 | coverage html 12 | -------------------------------------------------------------------------------- /dev/docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | cd .. 4 | set -ex 5 | 6 | PREFIX="fdabrandao/manylinux:" 7 | SUFFIX="" 8 | OPTIONS="-v `pwd`:/shared -it --rm" 9 | 10 | if [ "$#" -ne 1 ]; then 11 | echo "Usage: $0 " 12 | else 13 | ARCH=$1 14 | docker run $OPTIONS ${PREFIX}${ARCH}${SUFFIX} bash 15 | fi 16 | -------------------------------------------------------------------------------- /dev/download-ampl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | cd .. 4 | 5 | if [ "$#" -ne 1 ]; then 6 | echo "Usage: $0 " 7 | exit 1 8 | fi 9 | set -ex 10 | 11 | URL=$1 12 | PACKAGE=`basename $URL` 13 | curl -k -L -O $URL 14 | if [[ $PACKAGE == *.zip ]]; then 15 | unzip $PACKAGE 16 | else 17 | tar xzvf $PACKAGE 18 | fi 19 | rm $PACKAGE 20 | mv ampl.* ampl 21 | cd ampl 22 | pwd 23 | -------------------------------------------------------------------------------- /dev/register.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | cd .. 4 | set -ex 5 | 6 | bash clear.sh 7 | rm -rf dist/* 8 | rm -rf build *.egg-info 9 | 10 | #python setup.py register 11 | #python setup.py build 12 | python setup.py sdist #bdist_wheel 13 | twine upload dist/*.tar.gz 14 | #python setup.py sdist bdist_wheel upload 15 | -------------------------------------------------------------------------------- /dev/requirements.txt: -------------------------------------------------------------------------------- 1 | nose 2 | sphinx 3 | coverage 4 | bokeh 5 | pandas 6 | jupyter 7 | 8 | -------------------------------------------------------------------------------- /dev/updatelib.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import sys 5 | import shutil 6 | import tempfile 7 | 8 | VERSION = "nightly/v3" 9 | API_URL = f"https://portal.ampl.com/~jurgen/jl/libampl.zip" 10 | ARCHS = ["amd64", "aarch64"] 11 | 12 | 13 | def updatelib(package, archs): 14 | from zipfile import ZipFile 15 | 16 | try: 17 | from urllib import urlretrieve 18 | except Exception: 19 | from urllib.request import urlretrieve 20 | 21 | os.chdir(os.path.join(os.path.dirname(__file__), "..") or os.curdir) 22 | 23 | tmpfile = tempfile.mktemp(".zip") 24 | tmpdir = os.path.join(os.curdir, "tmp") 25 | libampldir = os.path.join(tmpdir, "libampl") 26 | try: 27 | shutil.rmtree(tmpdir) 28 | except Exception: 29 | pass 30 | 31 | if package.startswith("http"): 32 | # Disable SSL verification 33 | import ssl 34 | 35 | ssl._create_default_https_context = ssl._create_unverified_context 36 | print("Downloading:", API_URL) 37 | urlretrieve(API_URL, tmpfile) 38 | with ZipFile(tmpfile) as zp: 39 | zp.extractall(tmpdir) 40 | try: 41 | os.remove(tmpfile) 42 | except Exception: 43 | pass 44 | else: 45 | with ZipFile(package) as zp: 46 | zp.extractall(tmpdir) 47 | 48 | include_dir = os.path.join(libampldir, "include", "ampl") 49 | wrapper_dir = os.path.join(libampldir, "python") 50 | 51 | amplpy_include = os.path.join( 52 | "amplpy", "amplpython", "cppinterface", "include", "ampl" 53 | ) 54 | try: 55 | shutil.rmtree(amplpy_include) 56 | except Exception: 57 | pass 58 | shutil.copytree(include_dir, amplpy_include) 59 | print( 60 | "*\n!.gitignore\n", file=open(os.path.join(amplpy_include, ".gitignore"), "w") 61 | ) 62 | 63 | # print('wrapper:') 64 | # for filename in os.listdir(wrapper_dir): 65 | # print(f'\t{filename}') 66 | # shutil.copyfile( 67 | # os.path.join(wrapper_dir, filename), 68 | # os.path.join('amplpy', 'amplpython', 'cppinterface', filename) 69 | # ) 70 | 71 | dstbase = os.path.join("amplpy", "amplpython", "cppinterface", "lib") 72 | try: 73 | shutil.rmtree(dstbase) 74 | os.mkdir(dstbase) 75 | except Exception: 76 | pass 77 | print("*\n!.gitignore\n", file=open(os.path.join(dstbase, ".gitignore"), "w")) 78 | 79 | for libname in archs: 80 | srcdir = os.path.join(libampldir, libname) 81 | dstdir = os.path.join(dstbase, libname) 82 | os.mkdir(dstdir) 83 | print(f"{libname}:") 84 | for filename in os.listdir(srcdir): 85 | print(f"\t{filename}") 86 | shutil.copyfile( 87 | os.path.join(srcdir, filename), os.path.join(dstdir, filename) 88 | ) 89 | 90 | 91 | if __name__ == "__main__": 92 | # if len(sys.argv) > 1: 93 | # updatelib(API_URL, sys.argv[1:]) 94 | # else: 95 | # updatelib(API_URL, ['intel32', 'amd64', 'ppc64le', 'aarch64']) 96 | if len(sys.argv) == 2: 97 | updatelib(sys.argv[1], ARCHS) 98 | else: 99 | updatelib(API_URL, ARCHS) 100 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | venv 3 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = AMPLPY 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/common/images/ArchitectureSchema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/common/images/ArchitectureSchema.png -------------------------------------------------------------------------------- /docs/common/images/ClassDiagramModelEntitiesNew.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/common/images/ClassDiagramModelEntitiesNew.PNG -------------------------------------------------------------------------------- /docs/common/images/EntityMapItem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/common/images/EntityMapItem.png -------------------------------------------------------------------------------- /docs/common/images/sources/Architecture Schema.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/common/images/sources/Architecture Schema.docx -------------------------------------------------------------------------------- /docs/common/images/sources/Instance.vsd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/common/images/sources/Instance.vsd -------------------------------------------------------------------------------- /docs/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | ampl-sphinx-theme @ git+https://github.com/ampl/ampl.github.io.git#subdirectory=themes/ampl_sphinx_theme 2 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | amplpy==0.14.0 2 | ampl-sphinx-theme @ git+https://github.com/ampl/ampl.github.io.git#subdirectory=themes/ampl_sphinx_theme 3 | -------------------------------------------------------------------------------- /docs/source/_static/cropped-favicon-raw-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/source/_static/cropped-favicon-raw-192x192.png -------------------------------------------------------------------------------- /docs/source/_static/logo-inline-web-v4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/source/_static/logo-inline-web-v4.png -------------------------------------------------------------------------------- /docs/source/classes/ampl.rst: -------------------------------------------------------------------------------- 1 | .. _ref::AMPL: 2 | 3 | AMPL 4 | ---- 5 | 6 | .. autoclass:: amplpy.AMPL 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__new__,__reduce__,__setstate__, 12 | exportData,exportModel,getConstraint,getConstraints, 13 | getCurrentObjective,getData,getEntity,getErrorHandler, 14 | getObjective,getObjectives,getOption,getOutput,getOutputHandler, 15 | getParameter,getParameters,getSet,getSets,getValue,getVariable, 16 | getVariables,isRunning,readData,readTable,setData,setErrorHandler, 17 | setOption,setOutputHandler,writeTable 18 | 19 | 20 | 21 | .. autoclass:: amplpy.EntityMap 22 | :member-order: bysource 23 | :members: 24 | :undoc-members: 25 | :special-members: 26 | :exclude-members: __dict__,__weakref__,__module__,__init__, 27 | __new__,__reduce__,__setstate__,__pyx_vtable__, 28 | __next__ 29 | -------------------------------------------------------------------------------- /docs/source/classes/amplexception.rst: -------------------------------------------------------------------------------- 1 | .. _ref::AMPLException: 2 | 3 | AMPLException 4 | ------------- 5 | 6 | .. autoclass:: amplpy.AMPLException 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__, 12 | getLineNumber,getMessage,getOffset,getSourceName 13 | -------------------------------------------------------------------------------- /docs/source/classes/constraint.rst: -------------------------------------------------------------------------------- 1 | .. _ref::Constraint: 2 | 3 | Constraint 4 | ---------- 5 | 6 | .. autoclass:: amplpy.Constraint 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__,isLogical,setDual, 12 | __delitem__,__new__,__pyx_vtable__,__reduce__,__setstate__ -------------------------------------------------------------------------------- /docs/source/classes/dataframe.rst: -------------------------------------------------------------------------------- 1 | .. _ref::DataFrame: 2 | 3 | DataFrame 4 | --------- 5 | 6 | .. autoclass:: amplpy.DataFrame 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__,__iter__,addColumn,add_column,addRow,add_row,fromDict,fromNumpy,fromPandas,getColumn,get_column,getHeaders,get_headers,getNumCols,get_num_cols,getNumIndices,get_num_indices,getNumRows,get_num_rows,getRow,get_row,getRowByIndex,get_row_by_index,setColumn,set_column,setValues,set_values,toDict,toList,toPandas, 12 | __new__,__pyx_vtable__,__reduce__,__setstate__ 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/source/classes/entity.rst: -------------------------------------------------------------------------------- 1 | .. _ref::Entity: 2 | 3 | Entity 4 | ------ 5 | 6 | .. autoclass:: amplpy.Entity 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__,getIndexingSets,getValues,isScalar,numInstances,setValues, 12 | __new__,__pyx_vtable__,__reduce__,__setstate__ 13 | -------------------------------------------------------------------------------- /docs/source/classes/environment.rst: -------------------------------------------------------------------------------- 1 | .. _ref::Environment: 2 | 3 | Environment 4 | ----------- 5 | 6 | .. autoclass:: amplpy.Environment 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,getBinDir,getBinName,setBinDir,setBinName, 12 | __delitem__,__new__,__pyx_vtable__,__reduce__,__setstate__,__str__ 13 | -------------------------------------------------------------------------------- /docs/source/classes/errorhandler.rst: -------------------------------------------------------------------------------- 1 | .. _ref::ErrorHandler: 2 | 3 | ErrorHandler 4 | ------------ 5 | 6 | .. autoclass:: amplpy.ErrorHandler 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__, 12 | __new__,__reduce__,__reduce_cython__, __setstate__,__setstate_cython__ 13 | -------------------------------------------------------------------------------- /docs/source/classes/objective.rst: -------------------------------------------------------------------------------- 1 | .. _ref::Objective: 2 | 3 | Objective 4 | --------- 5 | 6 | .. autoclass:: amplpy.Objective 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__, 12 | __new__,__pyx_vtable__,__reduce__,__setstate__ 13 | -------------------------------------------------------------------------------- /docs/source/classes/outputhandler.rst: -------------------------------------------------------------------------------- 1 | .. _ref::OutputHandler: 2 | 3 | OutputHandler 4 | ------------- 5 | 6 | .. autoclass:: amplpy.OutputHandler 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__, 12 | __new__,__reduce__,__reduce_cython__, __setstate__,__setstate_cython__ 13 | 14 | 15 | .. autoclass:: amplpy.Kind 16 | :member-order: bysource 17 | :members: 18 | :undoc-members: 19 | :special-members: 20 | :exclude-members: __dict__,__weakref__,__module__ 21 | -------------------------------------------------------------------------------- /docs/source/classes/parameter.rst: -------------------------------------------------------------------------------- 1 | .. _ref::Parameter: 2 | 3 | Parameter 4 | --------- 5 | 6 | .. autoclass:: amplpy.Parameter 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__,hasDefault,isSymbolic,setValues, 12 | __delitem__,__new__,__pyx_vtable__,__reduce__,__setstate__ 13 | -------------------------------------------------------------------------------- /docs/source/classes/set.rst: -------------------------------------------------------------------------------- 1 | .. _ref::Set: 2 | 3 | Set 4 | --- 5 | 6 | .. autoclass:: amplpy.Set 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__,getValues,setValues, 12 | __delitem__,__new__,__pyx_vtable__,__reduce__,__setstate__ 13 | -------------------------------------------------------------------------------- /docs/source/classes/variable.rst: -------------------------------------------------------------------------------- 1 | .. _ref::Variable: 2 | 3 | Variable 4 | -------- 5 | 6 | .. autoclass:: amplpy.Variable 7 | :member-order: bysource 8 | :members: 9 | :undoc-members: 10 | :special-members: 11 | :exclude-members: __dict__,__weakref__,__module__,__init__,setValue, 12 | __delitem__,__new__,__pyx_vtable__,__reduce__,__setstate__ 13 | -------------------------------------------------------------------------------- /docs/source/examples.rst: -------------------------------------------------------------------------------- 1 | .. _secExamplesPython: 2 | 3 | Examples 4 | ======== 5 | 6 | 7 | Many Jupyter notebooks with examples are available on the `AMPL Model Colaboratory `_ 8 | and the new book `Hands-On Mathematical Optimization with AMPL in Python 🐍 `_. 9 | 10 | You should also check out our collection of interactive `Streamlit Apps `_ and 11 | learn how easy you can build your own apps. 12 | 13 | 14 | .. grid:: 1 1 2 2 15 | :gutter: 1 16 | 17 | .. grid-item:: 18 | 19 | .. grid:: 1 1 1 1 20 | :gutter: 1 21 | 22 | .. grid-item-card:: 23 | 24 | `AMPL Model Colaboratory `_ is a collection of AMPL models in Jupyter Notebooks that run on platforms such as Google Colab, Kaggle, Gradient, and AWS SageMaker. 25 | 26 | Available at: `ampl.com/colab `_. 27 | 28 | .. grid-item-card:: 29 | 30 | You can use the **Christmas notebook** written by `ChatGPT `_ to get started: 31 | 32 | .. image:: https://colab.research.google.com/assets/colab-badge.svg 33 | :target: https://colab.research.google.com/github/ampl/colab.ampl.com/blob/master/authors/fdabrandao/chatgpt/christmas.ipynb 34 | :alt: Open In Colab 35 | 36 | .. image:: https://kaggle.com/static/images/open-in-kaggle.svg 37 | :target: https://kaggle.com/kernels/welcome?src=https://github.com/ampl/colab.ampl.com/blob/master/authors/fdabrandao/chatgpt/christmas.ipynb 38 | :alt: Kaggle 39 | 40 | .. image:: https://assets.paperspace.io/img/gradient-badge.svg 41 | :target: https://console.paperspace.com/github/ampl/colab.ampl.com/blob/master/authors/fdabrandao/chatgpt/christmas.ipynb 42 | :alt: Gradient 43 | 44 | .. image:: https://studiolab.sagemaker.aws/studiolab.svg 45 | :target: https://studiolab.sagemaker.aws/import/github/ampl/colab.ampl.com/blob/master/authors/fdabrandao/chatgpt/christmas.ipynb 46 | :alt: Open In SageMaker Studio Lab 47 | 48 | | BTW: you can even ask `ChatGPT `_ to write models for you! If it makes mistakes you can ask for help in our new `Discourse Forum `_! 49 | 50 | .. grid-item:: 51 | 52 | .. grid:: 1 1 1 1 53 | :gutter: 1 54 | 55 | .. grid-item-card:: 56 | 57 | The repository of notebooks `MO-BOOK: Hands-On Mathematical Optimization with AMPL in Python 🐍 `_ 58 | introduces the concepts and tools of mathematical optimization with examples from a range of disciplines. 59 | 60 | Available at: `ampl.com/mo-book `_. 61 | 62 | .. grid-item-card:: 63 | 64 | Build and share data apps quickly with Streamlit - no front-end experience necessary. 65 | 66 | Available at: `ampl.com/streamlit `_. 67 | 68 | .. figure:: https://streamlit.io/images/brand/streamlit-logo-primary-colormark-darktext.png 69 | :alt: AMPL Optimization Apps on Streamlit Cloud 70 | :target: https://ampl.com/streamlit/ 71 | 72 | Example files 73 | ============= 74 | 75 | This section lists a few examples in Python. 76 | These are the same files that can be found in the `amplpy Github repository `_, and show the basic usage of the Python API. 77 | 78 | Example 1: First steps 79 | ---------------------- 80 | 81 | :download:`first_example.py <../../examples/first_example.py>` 82 | 83 | This example shows how to 84 | 85 | * read an AMPL model 86 | * reassign values to parameters 87 | * solve the model 88 | * display the objective function value 89 | 90 | 91 | Example 2: Get and set AMPL options 92 | ----------------------------------- 93 | 94 | :download:`options_example.py <../../examples/options_example.py>` 95 | 96 | This example shows how to: 97 | 98 | * get and set AMPL options 99 | 100 | 101 | Example 3: Assign all data to a model and solve it 102 | -------------------------------------------------- 103 | 104 | :download:`diet_model.py <../../examples/diet_model.py>` 105 | 106 | This example shows how to: 107 | 108 | * Assign all the data necessary to generate a model instance programmatically 109 | 110 | Example 4: Build an efficient frontier 111 | -------------------------------------- 112 | 113 | :download:`efficient_frontier.py <../../examples/efficient_frontier.py>` 114 | 115 | This example shows how to: 116 | 117 | * build an efficient frontier by repeteatly solve a portfolio problem in AMPL 118 | 119 | 120 | Example 5: Simple heuristic 121 | --------------------------- 122 | 123 | :download:`tracking_model.py <../../examples/tracking_model.py>` 124 | 125 | This example shows how to: 126 | 127 | * Do a simple heuristics for solving a QMIP problem, using the relaxed solution as a hint 128 | -------------------------------------------------------------------------------- /docs/source/images/ClassDiagramModelEntitiesNew.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/docs/source/images/ClassDiagramModelEntitiesNew.PNG -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. _amplapi: 2 | 3 | 4 | 5 | AMPL Python API 6 | =============== 7 | 8 | ``amplpy`` is an interface that allows developers to access the features of `AMPL `_ from within Python. 9 | For a quick introduction to AMPL see `Quick Introduction to AMPL `_. 10 | 11 | In the same way that AMPL's syntax matches naturally the mathematical description of the model, 12 | the input and output data matches naturally Python lists, sets, dictionaries, ``pandas`` and ``numpy`` objects. 13 | 14 | All model generation and solver interaction is handled directly by AMPL, which leads to 15 | great stability and speed; the library just acts as an intermediary, and the added overhead (in terms of memory and 16 | CPU usage) depends mostly on how much data is sent and read back from AMPL, the size of the expanded model as such is irrelevant. 17 | 18 | With ``amplpy`` you can model and solve large scale optimization problems in Python with the performance of heavily optimized C code 19 | without losing model readability. The same model can be deployed on applications 20 | built on different languages by just switching the API used. 21 | 22 | .. image:: https://portal.ampl.com/dl/ads/mo_book_big.png 23 | :alt: Hands-On Mathematical Optimization with AMPL in Python 24 | :target: https://ampl.com/mo-book/ 25 | 26 | .. note:: 27 | Many Jupyter notebooks with examples are available on the `AMPL Model Colaboratory `_ 28 | and the new book `Hands-On Mathematical Optimization with AMPL in Python 🐍 `_. 29 | 30 | You should also check out our collection of interactive `Streamlit Apps `_ and 31 | learn how easy you can build your own apps. 32 | 33 | Installation & minimal example 34 | ------------------------------ 35 | 36 | .. code-block:: bash 37 | 38 | # Install Python API for AMPL 39 | $ python -m pip install amplpy --upgrade 40 | 41 | # Install solver modules (e.g., HiGHS, CBC, Gurobi) 42 | $ python -m amplpy.modules install highs cbc gurobi 43 | 44 | # Activate your license (e.g., free https://ampl.com/ce license) 45 | $ python -m amplpy.modules activate 46 | 47 | # Import in Python 48 | $ python 49 | >>> from amplpy import AMPL 50 | >>> ampl = AMPL() # instantiate AMPL object 51 | 52 | .. note:: 53 | You can use a free `Community Edition license `_, which allows **free 54 | and perpetual use of AMPL with Open-Source solvers**. There are also free `AMPL for Courses `_ licenses that give unlimited 55 | access to all commercial solvers for teaching. 56 | 57 | .. code-block:: python 58 | 59 | # Minimal example: 60 | from amplpy import AMPL 61 | import pandas as pd 62 | ampl = AMPL() 63 | ampl.eval(r""" 64 | set A ordered; 65 | param S{A, A}; 66 | param lb default 0; 67 | param ub default 1; 68 | var w{A} >= lb <= ub; 69 | minimize portfolio_variance: 70 | sum {i in A, j in A} w[i] * S[i, j] * w[j]; 71 | s.t. portfolio_weights: 72 | sum {i in A} w[i] = 1; 73 | """) 74 | tickers, cov_matrix = # ... pre-process data in Python 75 | ampl.set["A"] = tickers 76 | ampl.param["S"] = pd.DataFrame(cov_matrix, index=tickers, columns=tickers) 77 | ampl.solve(solver="gurobi", gurobi_options="outlev=1") 78 | assert ampl.solve_result == "solved" 79 | sigma = ampl.get_value("sqrt(sum {i in A, j in A} w[i] * S[i, j] * w[j])") 80 | print(f"Volatility: {sigma*100:.1f}%") 81 | # ... post-process solution in Python 82 | 83 | 84 | Contents 85 | -------- 86 | 87 | .. toctree:: 88 | :maxdepth: 2 89 | 90 | intro 91 | getting-started 92 | quick-start 93 | class-structure 94 | reference 95 | examples 96 | -------------------------------------------------------------------------------- /docs/source/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | What are AMPL APIs? 5 | ------------------- 6 | 7 | `AMPL APIs `_ are interfaces that allows developers to access the features of the AMPL interpreter from within a 8 | programming language. All model generation and solver interaction is handled directly by AMPL, which leads to 9 | great stability and speed; the library just acts as an intermediary, and the added overhead (in terms of memory and 10 | CPU usage) depends mostly on how much data is read back from AMPL, the size of the model as such is irrelevant. 11 | Functions for directly assigning data to AMPL parameters and sets are provided, which can be used instead of the 12 | normal AMPL data reading procedures. AMPL API has been written with usability in mind, and it is easy to access 13 | its functionalities from 14 | `Python `_, 15 | `R `_, 16 | `C++ `_, 17 | `C#/.NET `_, 18 | `Java `_, and 19 | `MATLAB `_. 20 | 21 | Who can use AMPL APIs 22 | --------------------- 23 | 24 | The intended user of the library is the developer who needs to connect an application to optimization models and solvers, 25 | or the analyst with some experience in programming who wants to build a quick proof-of-concept application. 26 | 27 | System requirements 28 | ------------------- 29 | 30 | In general, :ref:`amplpy.modules ` or a valid AMPL setup, and Python 3 are necessary and sufficient conditions for the correct execution of AMPL API. 31 | 32 | The officially supported platforms are Windows, Linux, and macOS. 33 | Ports to any other platform supported by AMPL can be easily obtained. 34 | 35 | For Windows only, please make sure that the Microsoft Visual C++ Redistributable Package is installed. It often comes preinstalled or as part of other software; it can also be downloaded from Microsoft 36 | download center. Click `here `_ to download. 37 | 38 | 39 | About this manual 40 | ----------------- 41 | 42 | This document intends to guide a developer in the process of implementing an “AMPL API based” application in Python. 43 | The section :ref:`secClassStructure` presents the main logic of the API, which does not change depending on which programming environment is chosen. 44 | Further sections walk the reader through the implementation of the most common applications, finally the sections 45 | :ref:`secReferencePython` and :ref:`secExamplesPython` contain respectively the API reference documentation and a collection of examples. 46 | -------------------------------------------------------------------------------- /docs/source/reference.rst: -------------------------------------------------------------------------------- 1 | .. _secReferencePython: 2 | 3 | API Reference 4 | ============= 5 | 6 | AMPL classes 7 | ------------ 8 | All functions and classes provided by the AMPL API reside 9 | in namespace ``amplpy``. For brevity the namespace will be omitted. 10 | 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | classes/ampl 16 | classes/dataframe 17 | classes/environment 18 | classes/errorhandler 19 | classes/outputhandler 20 | 21 | .. _secPythonAlgebraicEntitiesReference: 22 | 23 | Algebraic entities classes 24 | -------------------------- 25 | 26 | .. toctree:: 27 | :maxdepth: 2 28 | 29 | classes/entity 30 | classes/variable 31 | classes/constraint 32 | classes/objective 33 | classes/set 34 | classes/parameter 35 | 36 | Exceptions 37 | ---------- 38 | 39 | .. toctree:: 40 | :maxdepth: 2 41 | 42 | classes/amplexception 43 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # AMPLPY Examples 2 | 3 | Data can be loaded in various forms: 4 | - One of which is ``pandas.DataFrame`` objects: 5 | 6 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ampl/amplcolab/blob/master/authors/fdabrandao/quick-start/pandasdiet.ipynb) 7 | - Python lists and dictionaries: 8 | 9 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ampl/amplcolab/blob/master/authors/fdabrandao/quick-start/nativediet.ipynb) 10 | 11 | More notebooks with examples available on the [AMPL Model Colaboratory](https://colab.ampl.com/). 12 | 13 | ## Documentation 14 | 15 | - http://amplpy.ampl.com -------------------------------------------------------------------------------- /examples/dataframe_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | import numpy as np 6 | import pandas as pd 7 | 8 | 9 | def main(argc, argv): 10 | # You can install amplpy with "python -m pip install amplpy" 11 | from amplpy import AMPL, DataFrame 12 | 13 | os.chdir(os.path.dirname(__file__) or os.curdir) 14 | model_directory = os.path.join(os.curdir, "models", "diet") 15 | 16 | # Create first pandas.DataFrame (for data indexed over NUTR) 17 | nutr_df = pd.DataFrame( 18 | [ 19 | ("A", 700, 20000), 20 | ("C", 700, 20000), 21 | ("B1", 700, 20000), 22 | ("B2", 700, 20000), 23 | ("NA", 0, 50000), 24 | ("CAL", 16000, 24000), 25 | ], 26 | columns=["NUTR", "n_min", "n_max"], 27 | ).set_index("NUTR") 28 | 29 | # Create second pandas.DataFrame (for data indexed over FOOD) 30 | food_df = pd.DataFrame( 31 | { 32 | "FOOD": ["BEEF", "CHK", "FISH", "HAM", "MCH", "MTL", "SPG", "TUR"], 33 | "f_min": [2] * 8, 34 | "f_max": [10] * 8, 35 | "cost": [3.19, 2.59, 2.29, 2.89, 1.89, 1.99, 1.99, 2.49], 36 | } 37 | ).set_index("FOOD") 38 | 39 | # Create third pandas.DataFrame to assign data to the AMPL entity 40 | # param amt{NUTR, FOOD}; 41 | amt_df = ( 42 | pd.DataFrame( 43 | np.array( 44 | [ 45 | [60, 8, 8, 40, 15, 70, 25, 60], 46 | [20, 0, 10, 40, 35, 30, 50, 20], 47 | [10, 20, 15, 35, 15, 15, 25, 15], 48 | [15, 20, 10, 10, 15, 15, 15, 10], 49 | [928, 2180, 945, 278, 1182, 896, 1329, 1397], 50 | [295, 770, 440, 430, 315, 400, 379, 450], 51 | ] 52 | ), 53 | columns=food_df.index.to_list(), 54 | index=nutr_df.index.to_list(), 55 | ) 56 | .stack() 57 | .rename("amt") 58 | ) 59 | 60 | # Create an AMPL instance 61 | ampl = AMPL() 62 | 63 | # Set the solver to use 64 | solver = argv[1] if argc > 1 else "highs" 65 | ampl.set_option("solver", solver) 66 | 67 | # Read the model file 68 | ampl.read(os.path.join(model_directory, "diet.mod")) 69 | 70 | # Assign data to NUTR, n_min and n_max 71 | ampl.set_data(nutr_df, "NUTR") 72 | # Assign data to FOOD, f_min, f_max and cost 73 | ampl.set_data(food_df, "FOOD") 74 | 75 | # Assign data to amt 76 | ampl.set_data(amt_df) 77 | # Solve the model 78 | 79 | ampl.solve() 80 | 81 | # Print out the result 82 | objective_value = ampl.get_objective("Total_Cost").value() 83 | print(f"Objective function value: {objective_value}") 84 | 85 | # Get the values of the variable Buy in a dataframe 86 | results = ampl.get_variable("Buy").get_values() 87 | # Print 88 | print(results) 89 | 90 | 91 | if __name__ == "__main__": 92 | try: 93 | main(len(sys.argv), sys.argv) 94 | except Exception as e: 95 | print(e) 96 | raise 97 | -------------------------------------------------------------------------------- /examples/diet_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | import pandas as pd # for pandas.DataFrame objects (https://pandas.pydata.org/) 6 | import numpy as np # for numpy.matrix objects (https://numpy.org/) 7 | 8 | 9 | def prepare_data(): 10 | food_df = pd.DataFrame( 11 | [ 12 | ("BEEF", 3.59, 2, 10), 13 | ("CHK", 2.59, 2, 10), 14 | ("FISH", 2.29, 2, 10), 15 | ("HAM", 2.89, 2, 10), 16 | ("MCH", 1.89, 2, 10), 17 | ("MTL", 1.99, 2, 10), 18 | ("SPG", 1.99, 2, 10), 19 | ("TUR", 2.49, 2, 10), 20 | ], 21 | columns=["FOOD", "cost", "f_min", "f_max"], 22 | ).set_index("FOOD") 23 | 24 | # Create a pandas.DataFrame with data for n_min, n_max 25 | nutr_df = pd.DataFrame( 26 | [ 27 | ("A", 700, 20000), 28 | ("C", 700, 20000), 29 | ("B1", 700, 20000), 30 | ("B2", 700, 20000), 31 | ("NA", 0, 50000), 32 | ("CAL", 16000, 24000), 33 | ], 34 | columns=["NUTR", "n_min", "n_max"], 35 | ).set_index("NUTR") 36 | 37 | amt_df = pd.DataFrame( 38 | np.array( 39 | [ 40 | [60, 8, 8, 40, 15, 70, 25, 60], 41 | [20, 0, 10, 40, 35, 30, 50, 20], 42 | [10, 20, 15, 35, 15, 15, 25, 15], 43 | [15, 20, 10, 10, 15, 15, 15, 10], 44 | [928, 2180, 945, 278, 1182, 896, 1329, 1397], 45 | [295, 770, 440, 430, 315, 400, 379, 450], 46 | ] 47 | ), 48 | columns=food_df.index.to_list(), 49 | index=nutr_df.index.to_list(), 50 | ) 51 | return food_df, nutr_df, amt_df 52 | 53 | 54 | def main(argc, argv): 55 | # You can install amplpy with "python -m pip install amplpy" 56 | from amplpy import AMPL 57 | 58 | os.chdir(os.path.dirname(__file__) or os.curdir) 59 | 60 | """ 61 | # If you are not using amplpy.modules, and the AMPL installation directory 62 | # is not in the system search path, add it as follows: 63 | from amplpy import add_to_path 64 | add_to_path(r"full path to the AMPL installation directory") 65 | """ 66 | 67 | # Create an AMPL instance 68 | ampl = AMPL() 69 | 70 | # Set the solver to use 71 | solver = argv[1] if argc > 1 else "highs" 72 | ampl.set_option("solver", solver) 73 | 74 | ampl.eval( 75 | r""" 76 | set NUTR; 77 | set FOOD; 78 | 79 | param cost {FOOD} > 0; 80 | param f_min {FOOD} >= 0; 81 | param f_max {j in FOOD} >= f_min[j]; 82 | 83 | param n_min {NUTR} >= 0; 84 | param n_max {i in NUTR} >= n_min[i]; 85 | 86 | param amt {NUTR,FOOD} >= 0; 87 | 88 | var Buy {j in FOOD} >= f_min[j], <= f_max[j]; 89 | 90 | minimize Total_Cost: 91 | sum {j in FOOD} cost[j] * Buy[j]; 92 | 93 | subject to Diet {i in NUTR}: 94 | n_min[i] <= sum {j in FOOD} amt[i,j] * Buy[j] <= n_max[i]; 95 | """ 96 | ) 97 | 98 | # Load the data from pandas.DataFrame objects: 99 | food_df, nutr_df, amt_df = prepare_data() 100 | # 1. Send the data from "amt_df" to AMPL and initialize the indexing set "FOOD" 101 | ampl.set_data(food_df, "FOOD") 102 | # 2. Send the data from "nutr_df" to AMPL and initialize the indexing set "NUTR" 103 | ampl.set_data(nutr_df, "NUTR") 104 | # 3. Set the values for the parameter "amt" using "amt_df" 105 | ampl.get_parameter("amt").set_values(amt_df) 106 | 107 | # Solve 108 | ampl.solve() 109 | 110 | # Get objective entity by AMPL name 111 | totalcost = ampl.get_objective("Total_Cost") 112 | # Print it 113 | print("Objective is:", totalcost.value()) 114 | 115 | # Reassign data - specific instances 116 | cost = ampl.get_parameter("cost") 117 | cost.set_values({"BEEF": 5.01, "HAM": 4.55}) 118 | print("Increased costs of beef and ham.") 119 | 120 | # Resolve and display objective 121 | ampl.solve() 122 | assert ampl.solve_result == "solved" 123 | print("New objective value:", totalcost.value()) 124 | 125 | # Reassign data - all instances 126 | cost.set_values( 127 | { 128 | "BEEF": 3, 129 | "CHK": 5, 130 | "FISH": 5, 131 | "HAM": 6, 132 | "MCH": 1, 133 | "MTL": 2, 134 | "SPG": 5.01, 135 | "TUR": 4.55, 136 | } 137 | ) 138 | 139 | print("Updated all costs.") 140 | 141 | # Resolve and display objective 142 | ampl.solve() 143 | assert ampl.solve_result == "solved" 144 | print("New objective value:", totalcost.value()) 145 | 146 | # Get the values of the variable Buy in a pandas.DataFrame object 147 | df = ampl.get_variable("Buy").get_values().to_pandas() 148 | # Print them 149 | print(df) 150 | 151 | # Get the values of an expression into a pandas.DataFrame object 152 | df2 = ampl.get_data("{j in FOOD} 100*Buy[j]/Buy[j].ub").to_pandas() 153 | # Print them 154 | print(df2) 155 | 156 | 157 | if __name__ == "__main__": 158 | try: 159 | main(len(sys.argv), sys.argv) 160 | except Exception as e: 161 | print(e) 162 | raise 163 | -------------------------------------------------------------------------------- /examples/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use any image as base image 2 | FROM python:3.9-slim-bullseye 3 | 4 | RUN python -m pip install amplpy --no-cache-dir 5 | RUN python -m amplpy.modules install highs --no-cache-dir 6 | 7 | # Add non-root user (optional) 8 | ARG USERNAME=guest 9 | ARG USER_UID=1000 10 | ARG USER_GID=$USER_UID 11 | RUN groupadd --gid $USER_GID $USERNAME \ 12 | && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME 13 | # Change to non-root privilege 14 | USER ${USERNAME} 15 | -------------------------------------------------------------------------------- /examples/docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | docker build . --tag ampl-container 4 | -------------------------------------------------------------------------------- /examples/docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "`dirname "$0"`" 3 | docker run --rm -it ampl-container bash 4 | -------------------------------------------------------------------------------- /examples/efficient_frontier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | 6 | 7 | def main(argc, argv): 8 | # You can install amplpy with "python -m pip install amplpy" 9 | from amplpy import AMPL 10 | 11 | os.chdir(os.path.dirname(__file__) or os.curdir) 12 | model_directory = os.path.join(os.curdir, "models", "qpmv") 13 | 14 | """ 15 | # If you are not using amplpy.modules, and the AMPL installation directory 16 | # is not in the system search path, add it as follows: 17 | from amplpy import add_to_path 18 | add_to_path(r"full path to the AMPL installation directory") 19 | """ 20 | 21 | # Create an AMPL instance 22 | ampl = AMPL() 23 | 24 | # Number of steps of the efficient frontier 25 | steps = 10 26 | 27 | ampl.set_option("reset_initial_guesses", True) 28 | ampl.set_option("send_statuses", False) 29 | ampl.set_option("solver", "cplex") 30 | 31 | # Load the AMPL model from file 32 | ampl.read(os.path.join(model_directory, "qpmv.mod")) 33 | ampl.read(os.path.join(model_directory, "qpmvbit.run")) 34 | 35 | # Set tables directory (parameter used in the script above) 36 | ampl.get_parameter("data_dir").set(model_directory) 37 | # Read tables 38 | ampl.read_table("assetstable") 39 | ampl.read_table("astrets") 40 | 41 | portfolio_return = ampl.get_variable("portret") 42 | target_return = ampl.get_parameter("targetret") 43 | variance = ampl.get_objective("cst") 44 | 45 | # Relax the integrality 46 | ampl.set_option("relax_integrality", True) 47 | # Solve the problem 48 | ampl.solve() 49 | # Calibrate the efficient frontier range 50 | minret = portfolio_return.value() 51 | maxret = ampl.get_value("max {s in stockall} averret[s]") 52 | stepsize = (maxret - minret) / steps 53 | returns = [None] * steps 54 | variances = [None] * steps 55 | for i in range(steps): 56 | print(f"Solving for return = {maxret - i * stepsize:g}") 57 | # Set target return to the desired point 58 | target_return.set(maxret - i * stepsize) 59 | ampl.eval("let stockopall := {}; let stockrun := stockall;") 60 | # Relax integrality 61 | ampl.set_option("relax_integrality", True) 62 | ampl.solve() 63 | print(f"QP result = {variance.value():g}") 64 | # Adjust included stocks 65 | ampl.eval("let stockrun := {i in stockrun:weights[i] > 0};") 66 | ampl.eval("let stockopall := {i in stockrun:weights[i] > 0.5};") 67 | # Set integrality back 68 | ampl.set_option("relax_integrality", False) 69 | # Solve the problem 70 | ampl.solve() 71 | # Check if the problem was solved successfully 72 | if ampl.solve_result != "solved": 73 | raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") 74 | print(f"QMIP result = {variance.value():g}") 75 | # Store data of corrent frontier point 76 | returns[i] = maxret - (i - 1) * stepsize 77 | variances[i] = variance.value() 78 | 79 | # Display efficient frontier points 80 | print("RETURN VARIANCE") 81 | for i in range(steps): 82 | print(f"{returns[i]:-6f} {variances[i]:-6f}") 83 | 84 | 85 | if __name__ == "__main__": 86 | try: 87 | main(len(sys.argv), sys.argv) 88 | except Exception as e: 89 | print(e) 90 | raise 91 | -------------------------------------------------------------------------------- /examples/first_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | 6 | 7 | def main(argc, argv): 8 | # You can install amplpy with "python -m pip install amplpy" 9 | from amplpy import AMPL 10 | 11 | os.chdir(os.path.dirname(__file__) or os.curdir) 12 | model_directory = os.path.join(os.curdir, "models", "diet") 13 | 14 | """ 15 | # If you are not using amplpy.modules, and the AMPL installation directory 16 | # is not in the system search path, add it as follows: 17 | from amplpy import add_to_path 18 | add_to_path(r"full path to the AMPL installation directory") 19 | """ 20 | 21 | # Create an AMPL instance 22 | ampl = AMPL() 23 | 24 | # Set the solver to use 25 | solver = argv[1] if argc > 1 else "highs" 26 | ampl.set_option("solver", solver) 27 | 28 | # Read the model and data files. 29 | ampl.read(os.path.join(model_directory, "diet.mod")) 30 | ampl.read_data(os.path.join(model_directory, "diet.dat")) 31 | 32 | # Solve 33 | ampl.solve() 34 | if ampl.solve_result != "solved": 35 | raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") 36 | 37 | # Get objective entity by AMPL name 38 | totalcost = ampl.get_objective("Total_Cost") 39 | # Print it 40 | print("Objective is:", totalcost.value()) 41 | 42 | # Reassign data - specific instances 43 | cost = ampl.get_parameter("cost") 44 | cost.set_values({"BEEF": 5.01, "HAM": 4.55}) 45 | print("Increased costs of beef and ham.") 46 | 47 | # Resolve and display objective 48 | ampl.solve() 49 | if ampl.solve_result != "solved": 50 | raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") 51 | print("New objective value:", totalcost.value()) 52 | 53 | # Reassign data - all instances 54 | elements = [3, 5, 5, 6, 1, 2, 5.01, 4.55] 55 | cost.set_values(elements) 56 | print("Updated all costs.") 57 | 58 | # Resolve and display objective 59 | ampl.solve() 60 | if ampl.solve_result != "solved": 61 | raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") 62 | print("New objective value:", totalcost.value()) 63 | 64 | # Get the values of the variable Buy in a dataframe object 65 | buy = ampl.get_variable("Buy") 66 | df = buy.get_values() 67 | # Print as pandas dataframe 68 | print(df.to_pandas()) 69 | 70 | # Get the values of an expression into a DataFrame object 71 | df2 = ampl.get_data("{j in FOOD} 100*Buy[j]/Buy[j].ub") 72 | # Print as pandas dataframe 73 | print(df2.to_pandas()) 74 | 75 | 76 | if __name__ == "__main__": 77 | try: 78 | main(len(sys.argv), sys.argv) 79 | except Exception as e: 80 | print(e) 81 | raise 82 | -------------------------------------------------------------------------------- /examples/location_transportation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | 6 | 7 | def main(argc, argv): 8 | # You can install amplpy with "python -m pip install amplpy" 9 | from amplpy import AMPL 10 | 11 | os.chdir(os.path.dirname(__file__) or os.curdir) 12 | model_directory = os.path.join(os.curdir, "models", "locationtransportation") 13 | 14 | """ 15 | # If you are not using amplpy.modules, and the AMPL installation directory 16 | # is not in the system search path, add it as follows: 17 | from amplpy import add_to_path 18 | add_to_path(r"full path to the AMPL installation directory") 19 | """ 20 | 21 | # Create an AMPL instance 22 | ampl = AMPL() 23 | 24 | # Must be solved with a solver supporting the suffix dunbdd 25 | ampl.set_option("solver", "cplex") 26 | ampl.set_option("presolve", False) 27 | ampl.set_option("omit_zero_rows", False) 28 | 29 | # Load the AMPL model from file 30 | ampl.read(os.path.join(model_directory, "trnloc2.mod")) 31 | # Read data 32 | ampl.read_data(os.path.join(model_directory, "trnloc.dat")) 33 | 34 | # Get references to AMPL's model entities for easy access. 35 | ship_cost = ampl.get_objective("Ship_Cost") 36 | max_ship_cost = ampl.get_variable("Max_Ship_Cost") 37 | build_var = ampl.get_variable("Build") 38 | supply = ampl.get_constraint("Supply") 39 | demand = ampl.get_constraint("Demand") 40 | num_cut_param = ampl.get_parameter("nCUT") 41 | cut_type = ampl.get_parameter("cut_type") 42 | build_param = ampl.get_parameter("build") 43 | supply_price = ampl.get_parameter("supply_price") 44 | demand_price = ampl.get_parameter("demand_price") 45 | 46 | num_cut_param.set(0) 47 | max_ship_cost.set_value(0) 48 | build_param.set_values([1] * ampl.get_set("ORIG").size()) 49 | 50 | num_cuts = 0 51 | while True: 52 | num_cuts += 1 53 | print(f"Iteration {num_cuts}") 54 | ampl.display("build") 55 | # Solve the subproblem. 56 | ampl.solve("Sub") 57 | result = ship_cost.result() 58 | if result == "infeasible": 59 | # Add a feasibility cut. 60 | num_cut_param.set(num_cuts) 61 | cut_type.set(num_cuts, "ray") 62 | for index, value in supply.get_values(["dunbdd"]): 63 | supply_price[index, num_cuts] = value 64 | for index, value in demand.get_values(["dunbdd"]): 65 | demand_price[index, num_cuts] = value 66 | elif ship_cost.value() > max_ship_cost.value() + 0.00001: 67 | # Add an optimality cut. 68 | num_cut_param.set(num_cuts) 69 | cut_type.set(num_cuts, "point") 70 | ampl.set_option("display_1col", 0) 71 | ampl.display("Ship") 72 | for index, value in supply.get_values(): 73 | supply_price[index, num_cuts] = value 74 | for index, value in demand.get_values(): 75 | demand_price[index, num_cuts] = value 76 | else: 77 | break 78 | # Re-solve the master problem. 79 | print("RE-SOLVING MASTER PROBLEM") 80 | ampl.solve("Master") 81 | if ampl.solve_result != "solved": 82 | raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") 83 | # Copy the data from the Build variable used in the master problem 84 | # to the build parameter used in the subproblem. 85 | build_param.set_values(build_var.get_values()) 86 | print(f"\nProcedure completed in {num_cuts} iterations\n") 87 | ampl.display("Ship") 88 | 89 | 90 | if __name__ == "__main__": 91 | try: 92 | main(len(sys.argv), sys.argv) 93 | except Exception as e: 94 | print(e) 95 | raise 96 | -------------------------------------------------------------------------------- /examples/models/alm/alm.random2.dat: -------------------------------------------------------------------------------- 1 | param price:= 2 | 1 1 1 0.21 3 | 1 1 2 0.21 4 | 1 2 1 0.278104 5 | 1 2 2 0.277314 6 | 2 1 1 9.84 7 | 2 1 2 9.84 8 | 2 2 1 10.5847 9 | 2 2 2 10.5854 10 | ; -------------------------------------------------------------------------------- /examples/models/alm/almtwo.dat: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | table tbl_prices360 IN 'ODBC', '.\alm.mdb': [tp,assets,scen], price; 5 | table tbl_liabs IN 'ODBC', '.\alm.mdb': [tp], liabilities ~ liability; 6 | table tbl_incomes IN 'ODBC', '.\alm.mdb': [tp], income; 7 | table tbl_targets IN 'ODBC', '.\alm.mdb': [tp], target; 8 | 9 | read table tbl_prices360; 10 | read table tbl_liabs ; 11 | read table tbl_incomes ; 12 | read table tbl_targets ; 13 | -------------------------------------------------------------------------------- /examples/models/alm/almtwo.deteq.mod: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #PARAMETERS: SCALARS 5 | param NT:=4; 6 | param NA:=23; 7 | param NS:=360; 8 | param tbuy := 1.025; 9 | param tsell := 0.975; 10 | param risklevel:=0.3; 11 | 12 | #SETS 13 | set assets := 1..NA; 14 | set tp :=1.. NT; 15 | 16 | #SCENARIO 17 | set scen:=1..NS; 18 | 19 | #PROBABILITIES 20 | param Prob {scen} :=1/360; 21 | 22 | 23 | #TREE 24 | #tree theTree:= multibranch {15,8,3}; 25 | #tree theTree:= twostage{card(scen)}; 26 | 27 | #RANDOM PARAMETERS 28 | param price{tp,assets,scen}; 29 | 30 | #PARAMETERS : VECTORS (read from database!) 31 | param liabilities{tp}; 32 | param initialholdings{assets} := 0; 33 | param income{tp}; 34 | param target{tp}; 35 | 36 | 37 | #STAGES 38 | suffix stage LOCAL; 39 | 40 | #VARIABLES 41 | var amounthold{t in tp,a in assets,s in scen} >=0; 42 | var amountbuy{t in tp,a in assets,s in scen} >=0; 43 | var amountsell{t in tp,a in assets,s in scen} >=0; 44 | var marketvalue{t in tp,s in scen} >=0 ; 45 | 46 | #STAGING INFORMATION 47 | #(ampl doesn't like "suffix stage t" in the var declarations above!) 48 | 49 | let {t in tp,a in assets,s in scen} amounthold[t,a,s].stage := if t=1 then 1 else 2; 50 | let {t in tp,a in assets,s in scen} amountbuy[t,a,s].stage :=if t=1 then 1 else 2; 51 | let {t in tp,a in assets,s in scen} amountsell[t,a,s].stage :=if t=1 then 1 else 2; 52 | let {t in tp,s in scen} marketvalue[t,s].stage :=if t=1 then 1 else 2; 53 | 54 | 55 | #OBJECTIVE 56 | maximize wealth : sum{s in scen} Prob[s]*marketvalue[4,s]; 57 | 58 | #CONSTRAINTS 59 | subject to 60 | 61 | assetmarketvalue1{s in scen}: 62 | marketvalue[1,s]=sum{a in assets} initialholdings[a]*price[1,a,s]; 63 | 64 | assetmarketvalue2{t in 2..NT,s in scen}: 65 | marketvalue[t,s] = sum{a in assets} amounthold[t,a,s]*price[t,a,s]; 66 | 67 | stockbalance1{a in assets,s in scen}: 68 | amounthold[1,a,s]=initialholdings[a]+amountbuy[1,a,s]-amountsell[1,a,s]; 69 | 70 | stockbalance2{a in assets,t in 2..NT, s in scen}: 71 | amounthold[t,a,s]=amounthold[t-1,a,s]+amountbuy[t,a,s]-amountsell[t,a,s]; 72 | 73 | fundbalance1{t in tp,s in scen}: 74 | sum{a in assets} amountbuy[t,a,s]*price[t,a,s]*tbuy 75 | -sum{a in assets} amountsell[t,a,s]*price[t,a,s]*tsell= 76 | income[t]-liabilities[t]; 77 | 78 | zeta{ t in 2..NT,s in scen}: target[t]-marketvalue[t,s]<=risklevel*target[t]; 79 | 80 | nant1{a in assets, t in 1..1, s in scen} : amounthold[t,a,1] = amounthold[t,a,s]; 81 | nant2{a in assets, t in 1..1, s in scen} : amountbuy[t,a,1] = amountbuy[t,a,s]; 82 | nant3{a in assets, t in 1..1, s in scen} : amountsell[t,a,1] = amountsell[t,a,s]; 83 | nant4{t in 1..1, s in scen} : marketvalue[t,1] = marketvalue[t,s]; -------------------------------------------------------------------------------- /examples/models/alm/almtwo.mod: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #PARAMETERS: SCALARS 5 | param NT:=4; 6 | param NA:=23; 7 | param NS:=360; 8 | param tbuy := 1.025; 9 | param tsell := 0.975; 10 | param risklevel:=0.3; 11 | 12 | #SETS 13 | set assets := 1..NA; 14 | set tp :=1.. NT; 15 | 16 | #SCENARIO 17 | scenarioset scen:=1..NS; 18 | 19 | #PROBABILITIES 20 | probability param Prob {scen} :=1/360; 21 | 22 | 23 | #TREE 24 | #tree theTree:= multibranch {15,8,3}; 25 | tree theTree:= twostage{card(scen)}; 26 | 27 | #RANDOM PARAMETERS 28 | random param price{tp,assets,scen}; 29 | 30 | #PARAMETERS : VECTORS (read from database!) 31 | param liabilities{tp}; 32 | param initialholdings{assets} := 0; 33 | param income{tp}; 34 | param target{tp}; 35 | 36 | 37 | #STAGES 38 | suffix stage LOCAL; 39 | 40 | #VARIABLES 41 | var amounthold{t in tp,a in assets,s in scen} >=0; 42 | var amountbuy{t in tp,a in assets,s in scen} >=0; 43 | var amountsell{t in tp,a in assets,s in scen} >=0; 44 | var marketvalue{t in tp,s in scen} >=0 ; 45 | 46 | #STAGING INFORMATION 47 | #(ampl doesn't like "suffix stage t" in the var declarations above!) 48 | 49 | let {t in tp,a in assets,s in scen} amounthold[t,a,s].stage := if t=1 then 1 else 2; 50 | let {t in tp,a in assets,s in scen} amountbuy[t,a,s].stage :=if t=1 then 1 else 2; 51 | let {t in tp,a in assets,s in scen} amountsell[t,a,s].stage :=if t=1 then 1 else 2; 52 | let {t in tp,s in scen} marketvalue[t,s].stage :=if t=1 then 1 else 2; 53 | 54 | 55 | #OBJECTIVE 56 | maximize wealth : sum{s in scen} Prob[s]*marketvalue[4,s]; 57 | 58 | #CONSTRAINTS 59 | subject to 60 | 61 | assetmarketvalue1{s in scen}: 62 | marketvalue[1,s]=sum{a in assets} initialholdings[a]*price[1,a,s]; 63 | 64 | assetmarketvalue2{t in 2..NT,s in scen}: 65 | marketvalue[t,s] = sum{a in assets} amounthold[t,a,s]*price[t,a,s]; 66 | 67 | stockbalance1{a in assets,s in scen}: 68 | amounthold[1,a,s]=initialholdings[a]+amountbuy[1,a,s]-amountsell[1,a,s]; 69 | 70 | stockbalance2{a in assets,t in 2..NT, s in scen}: 71 | amounthold[t,a,s]=amounthold[t-1,a,s]+amountbuy[t,a,s]-amountsell[t,a,s]; 72 | 73 | fundbalance1{t in tp,s in scen}: 74 | sum{a in assets} amountbuy[t,a,s]*price[t,a,s]*tbuy 75 | -sum{a in assets} amountsell[t,a,s]*price[t,a,s]*tsell= 76 | income[t]-liabilities[t]; 77 | 78 | zeta{ t in 2..NT,s in scen}: target[t]-marketvalue[t,s]<=risklevel*target[t]; -------------------------------------------------------------------------------- /examples/models/alm/almtwodata.dat: -------------------------------------------------------------------------------- 1 | 2 | param liabilities := 3 | 1 0 4 | 2 1000 5 | 3 1200 6 | 4 1250; 7 | 8 | param income := 9 | 1 100000 10 | 2 0 11 | 3 0 12 | 4 0; 13 | 14 | param target := 15 | 1 0 16 | 2 100000 17 | 3 110000 18 | 4 121000; 19 | 20 | 21 | 22 | table tbl_prices IN , 'prices360.bit': [tp,assets,scen], price; 23 | read table tbl_prices; 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/models/alm/prices360.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/alm/prices360.bit -------------------------------------------------------------------------------- /examples/models/asyncboost/test.run: -------------------------------------------------------------------------------- 1 | 2 | model trnloc2a.mod; 3 | data trnloc2.dat; 4 | 5 | 6 | 7 | param LB; param UB; 8 | 9 | param slack {CITY}; 10 | param scale default 1; 11 | param norm; 12 | param step; 13 | 14 | param same default 0; 15 | param same_limit := 3; 16 | 17 | param iter_limit := 20; 18 | param LBlog {0..iter_limit}; 19 | param UBlog {0..iter_limit}; 20 | param scalelog {1..iter_limit}; 21 | param steplog {1..iter_limit}; 22 | 23 | problem LowerBound: Build, Ship, Supply, Limit, Lagrangian; 24 | problem UpperBound: Ship, Supply, Demand, Limit, Shipping_Cost; 25 | 26 | 27 | if (1 == 1) then { 28 | include trnloc2a.run 29 | } 30 | 31 | if (1 == 1) then { 32 | include trnloc2a.run 33 | } -------------------------------------------------------------------------------- /examples/models/asyncboost/trnloc2.dat: -------------------------------------------------------------------------------- 1 | 2 | param build_limit := 8; 3 | 4 | param: CITY: supply demand := 5 | 1 19 4 6 | 2 13 1 7 | 3 20 2 8 | 4 18 7 9 | 5 23 9 10 | 6 25 9 11 | 7 22 3 12 | 8 28 9 13 | 9 18 5 14 | 10 16 2 15 | 11 17 4 16 | 12 14 1 17 | 13 22 9 18 | 14 13 3 19 | 15 18 1 20 | 16 20 7 21 | 17 15 5 22 | 18 23 9 23 | 19 25 2 24 | 20 19 4 25 | 21 18 9 26 | 22 19 6 27 | 23 22 5 28 | 24 22 4 29 | 25 21 5 30 | 26 19 6 31 | 27 17 8 32 | 28 21 9 33 | 29 15 5 34 | 30 25 6 ; 35 | 36 | param ship_cost 37 | 38 | : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 := 39 | 1 0 61 35 35 53 16 11 58 28 34 43 18 48 30 68 40 | 2 61 0 96 88 29 77 50 92 57 75 48 47 76 32 77 41 | 3 35 96 0 18 88 21 45 72 58 53 65 49 51 66 93 42 | 4 35 88 18 0 87 27 42 84 62 62 51 42 33 62 100 43 | 5 53 29 88 87 0 67 45 66 37 54 62 47 85 26 49 44 | 6 16 77 21 27 67 0 27 57 36 34 55 32 51 46 73 45 | 7 11 50 45 42 45 27 0 64 29 40 35 8 46 20 68 46 | 8 58 92 72 84 66 57 64 0 37 24 99 72 106 65 31 47 | 9 28 57 58 62 37 36 29 37 0 18 62 36 74 28 40 48 | 10 34 75 53 62 54 34 40 24 18 0 75 48 81 45 41 49 | 11 43 48 65 51 62 55 35 99 62 75 0 27 29 39 99 50 | 12 18 47 49 42 47 32 8 72 36 48 27 0 40 21 74 51 | 13 48 76 51 33 85 51 46 106 74 81 29 40 0 60 114 52 | 14 30 32 66 62 26 46 20 65 28 45 39 21 60 0 60 53 | 15 68 77 93 100 49 73 68 31 40 41 99 74 114 60 0 54 | 16 15 74 22 20 68 10 24 67 41 43 46 27 41 44 80 55 | 17 27 34 61 55 35 43 16 72 34 50 30 13 51 10 69 56 | 18 53 75 76 85 48 57 55 19 26 24 88 62 100 51 17 57 | 19 26 87 14 26 77 10 37 60 45 39 63 42 55 56 79 58 | 20 32 68 40 25 73 37 31 90 59 66 27 27 15 48 99 59 | 21 13 70 33 39 57 12 23 47 24 23 57 31 59 38 61 60 | 22 41 38 76 76 14 54 35 54 23 40 59 39 79 19 42 61 | 23 54 8 88 81 27 69 43 87 51 69 41 39 69 25 75 62 | 24 36 87 43 55 67 29 44 30 30 15 79 52 80 55 53 63 | 25 51 71 76 83 44 57 52 23 24 24 85 60 98 48 17 64 | 26 68 82 91 100 54 73 69 26 41 39 101 77 115 63 6 65 | 27 29 39 64 63 24 43 22 56 19 37 48 26 66 10 51 66 | 28 20 78 17 16 73 12 29 69 46 46 49 31 40 49 85 67 | 29 49 104 43 59 85 38 60 38 48 32 92 67 89 72 67 68 | 30 48 106 33 51 90 34 59 49 54 39 89 65 82 74 77 69 | 70 | : 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 := 71 | 1 15 27 53 26 32 13 41 54 36 51 68 29 20 49 48 72 | 2 74 34 75 87 68 70 38 8 87 71 82 39 78 104 106 73 | 3 22 61 76 14 40 33 76 88 43 76 91 64 17 43 33 74 | 4 20 55 85 26 25 39 76 81 55 83 100 63 16 59 51 75 | 5 68 35 48 77 73 57 14 27 67 44 54 24 73 85 90 76 | 6 10 43 57 10 37 12 54 69 29 57 73 43 12 38 34 77 | 7 24 16 55 37 31 23 35 43 44 52 69 22 29 60 59 78 | 8 67 72 19 60 90 47 54 87 30 23 26 56 69 38 49 79 | 9 41 34 26 45 59 24 23 51 30 24 41 19 46 48 54 80 | 10 43 50 24 39 66 23 40 69 15 24 39 37 46 32 39 81 | 11 46 30 88 63 27 57 59 41 79 85 101 48 49 92 89 82 | 12 27 13 62 42 27 31 39 39 52 60 77 26 31 67 65 83 | 13 41 51 100 55 15 59 79 69 80 98 115 66 40 89 82 84 | 14 44 10 51 56 48 38 19 25 55 48 63 10 49 72 74 85 | 15 80 69 17 79 99 61 42 75 53 17 6 51 85 67 77 86 | 16 0 39 65 17 26 20 56 66 39 63 80 44 5 48 43 87 | 17 39 0 59 53 39 38 29 27 58 56 72 18 44 74 74 88 | 18 65 59 0 63 85 45 37 70 37 4 15 42 69 51 60 89 | 19 17 53 63 0 42 21 64 80 30 63 78 53 15 34 27 90 | 20 26 39 85 42 0 44 65 61 65 83 100 52 27 75 69 91 | 21 20 38 45 21 44 0 44 63 22 44 61 34 24 36 36 92 | 22 56 29 37 64 65 44 0 34 53 33 46 13 61 71 76 93 | 23 66 27 70 80 61 63 34 0 80 67 79 32 71 97 99 94 | 24 39 58 37 30 65 22 53 80 0 38 51 48 41 18 25 95 | 25 63 56 4 63 83 44 33 67 38 0 17 38 68 53 62 96 | 26 80 72 15 78 100 61 46 79 51 17 0 54 84 63 73 97 | 27 44 18 42 53 52 34 13 32 48 38 54 0 49 66 69 98 | 28 5 44 69 15 27 24 61 71 41 68 84 49 0 48 42 99 | 29 48 74 51 34 75 36 71 97 18 53 63 66 48 0 12 100 | 30 43 74 60 27 69 36 76 99 25 62 73 69 42 12 0 ; 101 | -------------------------------------------------------------------------------- /examples/models/asyncboost/trnloc2a.mod: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------- 3 | # LOCATION-TRANSPORTATION PROBLEM 4 | # USING LAGRANGIAN RELAXATION 5 | # ---------------------------------------- 6 | 7 | set CITY; 8 | 9 | param build_limit integer; 10 | 11 | param demand {i in CITY} integer > 0; 12 | param supply {CITY} integer > 0; 13 | 14 | param ship_cost {i in CITY, j in CITY} >= 0; 15 | 16 | param mult {CITY} >= 0; # Lagrange multipliers for Demand constr 17 | 18 | var Build {CITY} integer >= 0 <= 1; # = 1 iff warehouse built at i 19 | var Ship {i in CITY, j in CITY} >= 0; # amounts shipped 20 | 21 | minimize Lagrangian: 22 | sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j] + 23 | sum {j in CITY} mult[j] * (demand[j] - sum {i in CITY} Ship[i,j]); 24 | 25 | minimize Shipping_Cost: 26 | sum {i in CITY, j in CITY} ship_cost[i,j] * Ship[i,j]; 27 | 28 | subj to Supply {i in CITY}: 29 | sum {j in CITY} Ship[i,j] <= supply[i] * Build[i]; 30 | 31 | subj to Demand {j in CITY}: 32 | sum {i in CITY} Ship[i,j] >= demand[j]; 33 | 34 | subj to Limit: sum {i in CITY} Build[i] <= build_limit; 35 | -------------------------------------------------------------------------------- /examples/models/asyncboost/trnloc2a.run: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------- 3 | # LAGRANGIAN RELAXATION FOR 4 | # THE LOCATION-TRANSPORTATION PROBLEM 5 | # ---------------------------------------- 6 | 7 | printf "\nLP RELAXATION\n\n"; 8 | 9 | option omit_zero_rows 1; 10 | option display_eps .000001; 11 | option solution_round 8; 12 | 13 | option solver_msg 0; 14 | 15 | option relax_integrality 1; 16 | objective Shipping_Cost; 17 | 18 | solve; 19 | 20 | 21 | let LB := Shipping_Cost.val; 22 | let UB := sum {j in CITY} max {i in CITY} ship_cost[i,j]; 23 | 24 | option relax_integrality 0; 25 | 26 | let {j in CITY} mult[j] := 0; 27 | 28 | let LBlog[0] := LB; 29 | let UBlog[0] := UB; 30 | 31 | for {k in 1..iter_limit} { printf "\nITERATION %d\n\n", k; 32 | solve LowerBound; 33 | 34 | let {j in CITY} slack[j] := sum {i in CITY} Ship[i,j] - demand[j]; 35 | 36 | if Lagrangian > LB + 0.000001 then { 37 | let LB := Lagrangian; 38 | let same := 0; } 39 | else let same := same + 1; 40 | 41 | if same = same_limit then { 42 | let scale := scale / 2; 43 | let same := 0; 44 | }; 45 | 46 | let norm := sum {j in CITY} slack[j]^2; 47 | let step := scale * (UB - Lagrangian) / norm; 48 | 49 | let {j in CITY} mult[j] := mult[j] less step * slack[j]; 50 | 51 | if sum {i in CITY} supply[i] * Build[i] 52 | >= sum {j in CITY} demand[j] - 1e-8 then { 53 | solve UpperBound; 54 | let UB := min (UB, Shipping_Cost); 55 | } 56 | 57 | let LBlog[k] := LB; 58 | let UBlog[k] := UB; 59 | let scalelog[k] := scale; 60 | let steplog[k] := step; 61 | } 62 | 63 | printf "\n\n"; 64 | display LBlog, UBlog, scalelog, steplog; 65 | -------------------------------------------------------------------------------- /examples/models/benders/trnloc.dat: -------------------------------------------------------------------------------- 1 | 2 | param: ORIG: supply := 3 | 1 23070 6 21090 11 22690 16 17110 21 16720 4 | 2 18290 7 16650 12 19360 17 15380 22 21540 5 | 3 20010 8 18420 13 22330 18 18690 23 16500 6 | 4 15080 9 19160 14 15440 19 20720 24 15310 7 | 5 17540 10 18860 15 19330 20 21220 25 18970 ; 8 | 9 | param: DEST: demand := 10 | A3 12000 11 | A6 12000 12 | A8 14000 13 | A9 13500 14 | B2 25000 15 | B4 29000 ; 16 | 17 | param fix_cost default 500000 ; 18 | 19 | param var_cost: 20 | 21 | A3 A6 A8 A9 B2 B4 := 22 | 1 73.78 14.76 86.82 91.19 51.03 76.49 23 | 2 60.28 20.92 76.43 83.99 58.84 68.86 24 | 3 58.18 21.64 69.84 72.39 61.64 58.39 25 | 4 50.37 21.74 61.49 65.72 60.48 56.68 26 | 5 42.73 35.19 44.11 58.08 65.76 55.51 27 | 6 44.62 39.21 44.44 48.32 76.12 51.17 28 | 7 49.31 51.72 36.27 42.96 84.52 49.61 29 | 8 50.79 59.25 22.53 33.22 94.30 49.66 30 | 9 51.93 72.13 21.66 29.39 93.52 49.63 31 | 10 65.90 13.07 79.59 86.07 46.83 69.55 32 | 11 50.79 9.99 67.83 78.81 49.34 60.79 33 | 12 47.51 12.95 59.57 67.71 51.13 54.65 34 | 13 39.36 19.01 56.39 62.37 57.25 47.91 35 | 14 33.55 30.16 40.66 48.50 60.83 42.51 36 | 15 34.17 40.46 40.23 47.10 66.22 38.94 37 | 16 41.68 53.03 22.56 30.89 77.22 35.88 38 | 17 42.75 62.94 18.58 27.02 80.36 40.11 39 | 18 46.46 71.17 17.17 21.16 91.65 41.56 40 | 19 56.83 8.84 83.99 91.88 41.38 67.79 41 | 20 46.21 2.92 68.94 76.86 38.89 60.38 42 | 21 41.67 11.69 61.05 70.06 43.24 48.48 43 | 22 25.57 17.59 54.93 57.07 44.93 43.97 44 | 23 28.16 29.39 38.64 46.48 50.16 34.20 45 | 24 26.97 41.62 29.72 40.61 59.56 31.21 46 | 25 34.24 54.09 22.13 28.43 69.68 24.09 ; 47 | -------------------------------------------------------------------------------- /examples/models/benders/trnloc2.mod: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------- 3 | # LOCATION-TRANSPORTATION PROBLEM 4 | # USING BENDERS DECOMPOSITION 5 | # (using primal formulation of subproblem) 6 | # ---------------------------------------- 7 | 8 | ### SUBPROBLEM ### 9 | 10 | set ORIG; # shipment origins (warehouses) 11 | set DEST; # shipment destinations (stores) 12 | 13 | param supply {ORIG} > 0; 14 | param demand {DEST} > 0; 15 | 16 | param fix_cost {ORIG} > 0; 17 | param var_cost {ORIG,DEST} > 0; 18 | 19 | param build {ORIG} binary; # = 1 iff warehouse built at i 20 | 21 | var Ship {ORIG,DEST} >= 0; # amounts shipped 22 | 23 | minimize Ship_Cost: 24 | sum {i in ORIG, j in DEST} var_cost[i,j] * Ship[i,j]; 25 | 26 | subj to Supply {i in ORIG}: 27 | sum {j in DEST} Ship[i,j] <= supply[i] * build[i]; 28 | 29 | subj to Demand {j in DEST}: 30 | sum {i in ORIG} Ship[i,j] = demand[j]; 31 | 32 | ### MASTER PROBLEM ### 33 | 34 | param nCUT >= 0 integer; 35 | param cut_type {1..nCUT} symbolic within {"point","ray"}; 36 | param supply_price {ORIG,1..nCUT} <= 0.000001; 37 | param demand_price {DEST,1..nCUT}; 38 | 39 | var Build {ORIG} binary; # = 1 iff warehouse built at i 40 | var Max_Ship_Cost >= 0; 41 | 42 | minimize Total_Cost: 43 | sum {i in ORIG} fix_cost[i] * Build[i] + Max_Ship_Cost; 44 | 45 | subj to Cut_Defn {k in 1..nCUT}: 46 | if cut_type[k] = "point" then Max_Ship_Cost >= 47 | sum {i in ORIG} supply_price[i,k] * supply[i] * Build[i] + 48 | sum {j in DEST} demand_price[j,k] * demand[j]; 49 | 50 | problem Master: Build, Max_Ship_Cost, Total_Cost, Cut_Defn; 51 | problem Sub: Ship, Ship_Cost, Supply, Demand; 52 | -------------------------------------------------------------------------------- /examples/models/commands/model.mod: -------------------------------------------------------------------------------- 1 | param G default 0; 2 | commands 'model2.mod'; -------------------------------------------------------------------------------- /examples/models/commands/model2.mod: -------------------------------------------------------------------------------- 1 | let G := 5; -------------------------------------------------------------------------------- /examples/models/diet/diet.dat: -------------------------------------------------------------------------------- 1 | set NUTR := A C B1 B2 NA CAL; 2 | set FOOD := BEEF CHK FISH HAM MCH MTL SPG TUR ; 3 | param : cost f_min f_max := 4 | BEEF 3.19 2 10 5 | CHK 2.59 2 10 6 | FISH 2.29 2 10 7 | HAM 2.89 2 10 8 | MCH 1.89 2 10 9 | MTL 1.99 2 10 10 | SPG 1.99 2 10 11 | TUR 2.49 2 10 ; 12 | param : n_min n_max := 13 | A 700 20000 14 | C 700 20000 15 | B1 700 20000 16 | B2 700 20000 17 | NA 0 50000 18 | CAL 16000 24000 ; 19 | param amt (tr): 20 | A C B1 B2 NA CAL := 21 | BEEF 60 20 10 15 938 295 22 | CHK 8 0 20 20 2180 770 23 | FISH 8 10 15 10 945 440 24 | HAM 40 40 35 10 278 430 25 | MCH 15 35 15 15 1182 315 26 | MTL 70 30 15 15 896 400 27 | SPG 25 50 25 15 1329 370 28 | TUR 60 20 15 10 1397 450 ; -------------------------------------------------------------------------------- /examples/models/diet/diet.mod: -------------------------------------------------------------------------------- 1 | set NUTR; 2 | set FOOD; 3 | 4 | param cost {FOOD} > 0; 5 | param f_min {FOOD} >= 0; 6 | param f_max {j in FOOD} >= f_min[j]; 7 | 8 | param n_min {NUTR} >= 0; 9 | param n_max {i in NUTR} >= n_min[i]; 10 | 11 | param amt {NUTR,FOOD} >= 0; 12 | 13 | var Buy {j in FOOD} >= f_min[j], <= f_max[j]; 14 | 15 | minimize Total_Cost: sum {j in FOOD} cost[j] * Buy[j]; 16 | 17 | subject to Diet {i in NUTR}: 18 | n_min[i] <= sum {j in FOOD} amt[i,j] * Buy[j] <= n_max[i]; 19 | -------------------------------------------------------------------------------- /examples/models/diet/diet.sol: -------------------------------------------------------------------------------- 1 | MINOS 5.51: optimal solution found. 2 | 13 iterations, objective 118.0594032 3 | 4 | Options 5 | 3 6 | 2 7 | 1 8 | 0 9 | 6 10 | 6 11 | 8 12 | 8 13 | 0 14 | 0 15 | 0 16 | 0.40458482523444117 17 | -0.0030690537084398927 18 | 0 19 | 5.360613810741664 20 | 2 21 | 2 22 | 10 23 | 10 24 | 10 25 | 9.306052855924994 26 | 2 27 | objno 0 0 28 | suffix 0 8 8 0 0 29 | sstatus 30 | 0 1 31 | 1 3 32 | 2 3 33 | 3 4 34 | 4 4 35 | 5 4 36 | 6 1 37 | 7 3 38 | suffix 1 6 8 0 0 39 | sstatus 40 | 0 1 41 | 1 1 42 | 2 1 43 | 3 3 44 | 4 4 45 | 5 1 46 | -------------------------------------------------------------------------------- /examples/models/errors/errorDefined.mod: -------------------------------------------------------------------------------- 1 | param f; 2 | param g; 3 | param f; -------------------------------------------------------------------------------- /examples/models/errors/errorODBC.mod: -------------------------------------------------------------------------------- 1 | set s; 2 | param f{s}; 3 | 4 | table t IN "ODBC", "banana.ocd" : [s], f; 5 | read table t; -------------------------------------------------------------------------------- /examples/models/errors/errorSyntax.mod: -------------------------------------------------------------------------------- 1 | param f; 2 | parram g; -------------------------------------------------------------------------------- /examples/models/locationtransportation/trnloc.dat: -------------------------------------------------------------------------------- 1 | 2 | param: ORIG: supply := 3 | 1 23070 6 21090 11 22690 16 17110 21 16720 4 | 2 18290 7 16650 12 19360 17 15380 22 21540 5 | 3 20010 8 18420 13 22330 18 18690 23 16500 6 | 4 15080 9 19160 14 15440 19 20720 24 15310 7 | 5 17540 10 18860 15 19330 20 21220 25 18970 ; 8 | 9 | param: DEST: demand := 10 | A3 12000 11 | A6 12000 12 | A8 14000 13 | A9 13500 14 | B2 25000 15 | B4 29000 ; 16 | 17 | param fix_cost default 500000 ; 18 | 19 | param var_cost: 20 | 21 | A3 A6 A8 A9 B2 B4 := 22 | 1 73.78 14.76 86.82 91.19 51.03 76.49 23 | 2 60.28 20.92 76.43 83.99 58.84 68.86 24 | 3 58.18 21.64 69.84 72.39 61.64 58.39 25 | 4 50.37 21.74 61.49 65.72 60.48 56.68 26 | 5 42.73 35.19 44.11 58.08 65.76 55.51 27 | 6 44.62 39.21 44.44 48.32 76.12 51.17 28 | 7 49.31 51.72 36.27 42.96 84.52 49.61 29 | 8 50.79 59.25 22.53 33.22 94.30 49.66 30 | 9 51.93 72.13 21.66 29.39 93.52 49.63 31 | 10 65.90 13.07 79.59 86.07 46.83 69.55 32 | 11 50.79 9.99 67.83 78.81 49.34 60.79 33 | 12 47.51 12.95 59.57 67.71 51.13 54.65 34 | 13 39.36 19.01 56.39 62.37 57.25 47.91 35 | 14 33.55 30.16 40.66 48.50 60.83 42.51 36 | 15 34.17 40.46 40.23 47.10 66.22 38.94 37 | 16 41.68 53.03 22.56 30.89 77.22 35.88 38 | 17 42.75 62.94 18.58 27.02 80.36 40.11 39 | 18 46.46 71.17 17.17 21.16 91.65 41.56 40 | 19 56.83 8.84 83.99 91.88 41.38 67.79 41 | 20 46.21 2.92 68.94 76.86 38.89 60.38 42 | 21 41.67 11.69 61.05 70.06 43.24 48.48 43 | 22 25.57 17.59 54.93 57.07 44.93 43.97 44 | 23 28.16 29.39 38.64 46.48 50.16 34.20 45 | 24 26.97 41.62 29.72 40.61 59.56 31.21 46 | 25 34.24 54.09 22.13 28.43 69.68 24.09 ; 47 | -------------------------------------------------------------------------------- /examples/models/locationtransportation/trnloc2.mod: -------------------------------------------------------------------------------- 1 | 2 | # ---------------------------------------- 3 | # LOCATION-TRANSPORTATION PROBLEM 4 | # USING BENDERS DECOMPOSITION 5 | # (using primal formulation of subproblem) 6 | # ---------------------------------------- 7 | 8 | ### SUBPROBLEM ### 9 | 10 | set ORIG; # shipment origins (warehouses) 11 | set DEST; # shipment destinations (stores) 12 | 13 | param supply {ORIG} > 0; 14 | param demand {DEST} > 0; 15 | 16 | param fix_cost {ORIG} > 0; 17 | param var_cost {ORIG,DEST} > 0; 18 | 19 | param build {ORIG} binary; # = 1 iff warehouse built at i 20 | 21 | var Ship {ORIG,DEST} >= 0; # amounts shipped 22 | 23 | minimize Ship_Cost: 24 | sum {i in ORIG, j in DEST} var_cost[i,j] * Ship[i,j]; 25 | 26 | subj to Supply {i in ORIG}: 27 | sum {j in DEST} Ship[i,j] <= supply[i] * build[i]; 28 | 29 | subj to Demand {j in DEST}: 30 | sum {i in ORIG} Ship[i,j] = demand[j]; 31 | 32 | ### MASTER PROBLEM ### 33 | 34 | param nCUT >= 0 integer; 35 | param cut_type {1..nCUT} symbolic within {"point","ray"}; 36 | param supply_price {ORIG,1..nCUT} <= 0.000001; 37 | param demand_price {DEST,1..nCUT}; 38 | 39 | var Build {ORIG} binary; # = 1 iff warehouse built at i 40 | var Max_Ship_Cost >= 0; 41 | 42 | minimize Total_Cost: 43 | sum {i in ORIG} fix_cost[i] * Build[i] + Max_Ship_Cost; 44 | 45 | subj to Cut_Defn {k in 1..nCUT}: 46 | if cut_type[k] = "point" then Max_Ship_Cost >= 47 | sum {i in ORIG} supply_price[i,k] * supply[i] * Build[i] + 48 | sum {j in DEST} demand_price[j,k] * demand[j]; 49 | 50 | 51 | problem Master: Build, Max_Ship_Cost, Total_Cost, Cut_Defn; 52 | problem Sub: Ship, Ship_Cost, Supply, Demand; -------------------------------------------------------------------------------- /examples/models/nonlinearforabs/solution.sol: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/nonlinearforabs/solution.sol -------------------------------------------------------------------------------- /examples/models/nonlinearforabs/sub_simple.dat: -------------------------------------------------------------------------------- 1 | set HYDRO:= U1 U2 U3 U4 U5 U6 U7 U8 U9 U10 U11; 2 | set GENS := GT1 GT2 GT3 GT4 GT5 GT6 GT7 GT8 GT9 GT10; 3 | 4 | 5 | 6 | param: A1 A2 PMIN PMAX:= 7 | GT1 13.19 0.0004 80 350 8 | GT2 13.19 0.0004 80 350 9 | GT3 14.19 0.0005 80 300 10 | GT4 16.60 0.0020 40 200 11 | GT5 19.50 0.0022 40 150 12 | GT6 19.50 0.0022 40 150 13 | GT7 22.26 0.0071 20 80 14 | GT8 26.26 0.0071 20 50 15 | GT9 32.92 0.0041 20 55 16 | GT10 32.92 0.0041 20 55; 17 | 18 | 19 | 20 | 21 | param K0:= 17.28; 22 | param K1:= 0.002242; 23 | param K2:= 0.8e-6; 24 | param B0:= 12.47; 25 | param B1:= 0.0071; 26 | 27 | 28 | # Q QV QV^2 Q^2 Q^3 Q^4 CONSTANTE 29 | param: C0 C1 C2 C3 C4 C5 C00:= 30 | U1 4.490 1261e-7 -6.15e-9 0 0 0 0 31 | U2 1.200 0 0 0 0 0 0 32 | U3 1.630 0 0 0 0 0 0 33 | U4 2.618 2393e-7 0 0 0 0 0 34 | U5 1.495 0 0 -0.588e-2 0 0 -15.89 35 | U6 0.833 0 0 0.715e-3 0.951e-4 -0.891e-6 0 36 | U7 1.010 5676e-7 -1.263e-7 0 0 0 0 37 | U8 0.359 0 0 -0.235e-3 -0.370e-6 0 0 38 | U9 1.878 1889e-7 0 0 0 0 0 39 | U10 0.5711 4220e-7 -4.423e-7 0 0 0 0 40 | U11 1.022 0.01097 -3.969e-5 0 0 0 0; 41 | 42 | 43 | 44 | param TOPOLOGY: U1 U2 U3 U4 U5 U6 U7 U8 U9 U10 U11:= 45 | U1 1 0 0 0 0 0 0 0 0 0 0 46 | U2 0 1 0 0 0 0 0 0 0 0 0 47 | U3 -1 -1 1 0 0 0 0 0 0 0 0 48 | U4 0 0 0 1 0 0 0 0 0 0 0 49 | U5 0 0 0 -1 1 0 0 0 0 0 0 50 | U6 0 0 0 0 -1 1 0 0 0 0 0 51 | U7 0 0 0 0 0 0 1 0 0 0 0 52 | U8 0 0 0 0 0 0 -1 1 0 0 0 53 | U9 0 0 0 0 0 0 0 0 1 0 0 54 | U10 0 0 0 0 0 0 0 0 0 1 0 55 | U11 0 0 0 0 0 0 0 0 0 0 1; 56 | 57 | 58 | 59 | param: INFLOW V0 VMIN VMAX QMIN QMAX:= 60 | U1 18.4 1866 500 5572.4 5 92 61 | U2 36.0 0 0 0 0 90 62 | U3 23.0 0 0 0 0 192 63 | U4 27.3 41.7 7.5 174.64 0 40 64 | U5 58.0 0 0 0 15.89 84 65 | U6 0.0 0 0 0 0 84 66 | U7 88.0 946.2 384 1512 56.5 310 67 | U8 0.0 0 0 0 56.5 310 68 | U9 6.7 662.80 224.9 1065.4 0 83 69 | U10 151.0 198.05 142.65 433.32 0 578 70 | U11 55.0 106.60 103.00 133.64 15 315; 71 | 72 | # from MATLAB 73 | param LOAD := 74 | 1 1800 75 | 2 1840 76 | 3 1920 77 | 4 2000 78 | 5 2080 79 | 6 2160 80 | 7 2200 81 | 8 2240 82 | 9 2280 83 | 10 2300 84 | 11 2320 85 | 12 2320 86 | 13 2300 87 | 14 2280 88 | 15 2240 89 | 16 2240 90 | 17 22880 91 | 18 2396 92 | 19 2400 93 | 20 2440 94 | 21 2440 95 | 22 2320 96 | 23 2000 97 | 24 241800; 98 | 99 | param UH (tr): U1 U2 U3 U4 U5 U6 U7 U8 U9 U10 U11 := 100 | 4 1 1 1 1 1 1 1 1 1 1 1 101 | 8 1 1 1 1 1 1 1 1 1 1 1 102 | 12 1 1 1 1 1 1 1 1 1 1 1 103 | 16 1 1 1 1 1 1 1 1 1 1 1 104 | 20 1 1 1 1 1 1 1 1 1 1 1 105 | 24 1 1 1 1 1 1 1 1 1 1 1; 106 | 107 | 108 | param U (tr): GT1 GT2 GT3 GT4 GT5 GT6 GT7 GT8 GT9 GT10 := 109 | 1 1 1 1 0 0 0 0 0 0 0 110 | 2 1 1 1 0 0 0 0 0 0 0 111 | 3 1 1 1 0 0 0 0 0 0 0 112 | 4 1 1 1 0 0 0 0 0 0 0 113 | 5 1 1 0 0 0 0 0 0 0 0 114 | 6 1 1 0 0 0 0 0 0 0 0 115 | 7 1 1 0 0 0 0 0 0 0 0 116 | 8 1 1 0 0 0 0 0 0 0 0 117 | 9 1 1 0 0 0 0 0 0 0 0 118 | 10 1 1 0 0 0 0 0 0 0 0 119 | 11 1 1 0 0 0 0 0 0 0 0 120 | 12 1 1 0 0 0 0 0 0 0 0 121 | 13 1 1 0 0 0 0 0 0 0 0 122 | 14 1 1 0 0 0 0 0 0 0 0 123 | 15 1 1 0 0 0 0 0 0 0 0 124 | 16 1 1 0 0 0 0 0 0 0 0 125 | 17 1 1 0 0 0 0 0 0 0 0 126 | 18 1 1 0 0 0 0 0 0 0 0 127 | 19 1 0 0 0 0 0 0 0 0 0 128 | 20 1 0 0 0 0 0 0 0 0 0 129 | 21 1 0 0 0 0 0 0 0 0 0 130 | 22 1 0 0 0 0 0 0 0 0 0 131 | 23 1 0 0 0 0 0 0 0 0 0 132 | 24 1 0 0 0 0 0 0 0 0 0; -------------------------------------------------------------------------------- /examples/models/nonlinearforabs/sub_simple.mod: -------------------------------------------------------------------------------- 1 | # Sets: 2 | set PERIODS = 1..24 by 1 ; # Load demand periods 3 | set STAGES within PERIODS = 4..24 by 4 ; # Step periods for reservoirs 4 | set HYDRO ordered ; # Hydro Plants 5 | set GENS ordered ; # Thermal Plants 6 | 7 | 8 | 9 | 10 | 11 | # ============= 12 | # Parameters: 13 | # ============= 14 | # a) Bring from Matlab: 15 | param LOAD{PERIODS}; 16 | param U {GENS,PERIODS}; 17 | param UH {HYDRO,STAGES}; 18 | 19 | 20 | 21 | 22 | # b) Thermal System 23 | param A1 {GENS} ; # Linear coefficient of cost function 24 | param A2 {GENS} ; # Quadratic coefficient of cost function 25 | param PMAX{GENS} ; 26 | param PMIN{GENS} ; 27 | param VOLL:= 1500; 28 | 29 | 30 | # c) Hydro system 31 | # Filtration function coefficients for reservoir 1 (cuadratic) 32 | param K0; 33 | param K1; 34 | param K2; 35 | # Filtration function coefficients (linear) 36 | param B0; 37 | param B1; 38 | # Future cost function coefficients (Reservoir 1): 39 | param p3:= -1.352e-5; 40 | param p2:= 0.1765; 41 | param p1:= -811.1; 42 | param p0:= 1.775e6; 43 | # Hydropower Productivity Function coefficients 44 | param C0 {HYDRO}; 45 | param C1 {HYDRO}; 46 | param C2 {HYDRO}; 47 | param C3 {HYDRO}; 48 | param C4 {HYDRO}; 49 | param C5 {HYDRO}; 50 | param C00{HYDRO}; 51 | 52 | param TOPOLOGY{HYDRO,HYDRO} ; 53 | param FC:=1.44e-2 ; # Con DeltaT= 4 horas; 54 | param INFLOW{HYDRO} ; 55 | param VMIN{HYDRO} ; 56 | param VMAX{HYDRO} ; 57 | param V0 {HYDRO} ; 58 | param QMIN{HYDRO} ; 59 | param QMAX{HYDRO} ; 60 | 61 | 62 | # ====================== 63 | # Variables: 64 | # ====================== 65 | var P {GENS,PERIODS} ; 66 | var u {GENS,PERIODS}; 67 | var ENS{PERIODS}>=0 ; 68 | var H {HYDRO,PERIODS}>=0; 69 | var V {HYDRO,STAGES} ; 70 | var Q {HYDRO,STAGES} >=0; 71 | var S {HYDRO,STAGES} >=0; 72 | var uh {HYDRO,STAGES}; 73 | 74 | var ce = sum{t in PERIODS}(sum{i in GENS} (A1[i]*P[i,t]+P[i,t]*A2[i]*P[i,t]) + VOLL*ENS[t]); 75 | var cf = p1*V['U1',24]+p2*V['U1',24]*V['U1',24]+p3*V['U1',24]*V['U1',24]*V['U1',24]; 76 | 77 | 78 | # ========================================== 79 | # Optimization Problem 80 | # ========================================== 81 | minimize of : ce + cf; 82 | 83 | 84 | 85 | #Constraints: 86 | subject to R1{t in PERIODS}: 87 | sum{i in GENS} P[i,t]+ sum{j in HYDRO}H[j,t]+ENS[t]= LOAD[t]; 88 | 89 | subject to R2{i in GENS, t in PERIODS}: P[i,t] <= PMAX[i]*u[i,t]; 90 | subject to R3{i in GENS, t in PERIODS}: P[i,t] >= PMIN[i]*u[i,t]; 91 | subject to R4{t in PERIODS}: ENS[t] <= LOAD[t]; 92 | 93 | # Water Balance : 94 | subject to R10{j in HYDRO, h in STAGES}: 95 | V[j,h] = (if h=4 then V0[j] else V[j,h-4]) + 96 | FC*(INFLOW[j] - sum{jj in HYDRO} TOPOLOGY[j,jj]*(Q[jj,h]+S[jj,h])) - 97 | FC*(if j='U1' then (K0+K1*V[j,h]+V[j,h]*K2*V[j,h])); 98 | 99 | 100 | # Productivity function: 101 | #subject to R11{j in HYDRO, h in STAGES, t in PERIODS: (t>=h-3 and t<=h)}: 102 | #H[j,t]= C0[j]*Q[j,h]+C1[j]*Q[j,h]*V[j,h]+C2[j]*V[j,h]*Q[j,h]*V[j,h] + 103 | # Q[j,h]*C3[j]*Q[j,h] + C4[j]*Q[j,h]*Q[j,h]*Q[j,h] + C5[j]*Q[j,h]*Q[j,h]*Q[j,h]*Q[j,h]+ 104 | # (if j='U5' then C00[j]*uh[j,h]); 105 | 106 | # Productivity function: 107 | subject to R11{j in HYDRO, h in STAGES, t in PERIODS: (t>=h-3 and t<=h)}: 108 | H[j,t]= C0[j]*Q[j,h]; 109 | 110 | 111 | 112 | subject to R12{j in HYDRO, h in STAGES}: V[j,h] <= VMAX[j]; 113 | subject to R13{j in HYDRO, h in STAGES}: V[j,h] >= VMIN[j]; 114 | subject to R14{j in HYDRO, h in STAGES}: Q[j,h] >= QMIN[j]*uh[j,h]; 115 | subject to R15{j in HYDRO, h in STAGES}: Q[j,h] <= QMAX[j]*uh[j,h]; 116 | 117 | 118 | # Hydroelectric plants without reservoir; 119 | subject to R16{j in HYDRO, h in STAGES: j='U2' and j='U3' and j='U5' and j='U6' and j='U8'}: 120 | V[j,h]=0; 121 | 122 | 123 | subject to R20{i in GENS, t in PERIODS}: u[i,t] = U[i,t]; 124 | subject to R21{j in HYDRO, h in STAGES}: uh[j,h] = UH[j,h]; 125 | 126 | -------------------------------------------------------------------------------- /examples/models/nqueens/nqueens.mod: -------------------------------------------------------------------------------- 1 | param n default 1000; 2 | set ROWS := {1..n}; 3 | set COLUMNS := {1..n}; 4 | var X{ROWS, COLUMNS} binary; 5 | # X[i,j] is one if there is a queen at (i,j); else zero 6 | maximize max_queens: sum {i in ROWS, j in COLUMNS} X[i,j]; 7 | subject to column_attacks {j in COLUMNS}: sum {i in ROWS} X[i,j] = 1; 8 | subject to row_attacks {i in ROWS}: sum {j in COLUMNS} X[i,j] = 1; 9 | subject to diagonal1_attacks {k in 2..2*n}: sum {i in ROWS, j in COLUMNS: i+j=k} X[i,j] <= 1; 10 | subject to diagonal2_attacks {k in -(n-1)..(n-1)}: sum {i in ROWS, j in COLUMNS: i-j=k} X[i,j] <= 1; -------------------------------------------------------------------------------- /examples/models/qpmv/assetsReturns.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/qpmv/assetsReturns.bit -------------------------------------------------------------------------------- /examples/models/qpmv/covar.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/qpmv/covar.bit -------------------------------------------------------------------------------- /examples/models/qpmv/qpmv.mod: -------------------------------------------------------------------------------- 1 | # This is a mean-variance model, with additional sets and 2 | # parameters to support the heuristics implemented in the script. 3 | 4 | set stockall ordered; # All stocks in the universe of assets 5 | set stockrun ordered default stockall; # Stocks used for this run 6 | set stockopall ordered default {}; # Stocks which had more than 0.5 weight 7 | 8 | param ncard default 10; # Maximum cardinality of portfolio 9 | param averret{stockall}; # Average return of each stock 10 | param covar{stockall, stockall}; # Covariance matrix 11 | param cutoffl default 0.0001; # Lower cutoff (weight < cutoffl -> stock out) 12 | param cutoffh{stockall} default 1; # High cutoff (weight > cutoffh -> stock in) 13 | param targetret default 0; # Target return for the efficient frontier 14 | 15 | var weights{s in stockrun} >= 0; # Weight of each stock in the current run 16 | var ifstock{s in stockrun} binary; # Indicates if a stock had weight > cutoffl 17 | var portret >= targetret; # Portfolio return 18 | 19 | # Minimize risk (X * C * X) 20 | minimize cst: sum{s in stockrun, s1 in stockrun} weights[s] * covar[s, s1] * weights[s1]; 21 | 22 | subject to 23 | 24 | # Total weights = 1 25 | invest: sum{s in stockrun} weights[s] = 1; 26 | 27 | # Portfolio return definition 28 | defret: sum{s in stockrun} averret[s] * weights[s] = portret; 29 | 30 | # Force ifstock to be 0 if weight < cutoffl 31 | lowlnk{s in stockrun}: weights[s] >= cutoffl * ifstock[s]; 32 | 33 | # Force ifstock to be one if weight > cutoffh 34 | uplink{s in stockrun}: weights[s] <= cutoffh[s] * ifstock[s]; 35 | 36 | # Fix each stock in stockopall to be included 37 | fixing{s in stockopall}: ifstock[s] = 1; 38 | 39 | # Cardinality constraint 40 | carda: sum{s in stockrun} ifstock[s] <= ncard; 41 | -------------------------------------------------------------------------------- /examples/models/qpmv/qpmvbit.run: -------------------------------------------------------------------------------- 1 | # Data tables in native AMPL format 2 | 3 | param data_dir symbolic; # Data directory 4 | 5 | table assetstable IN (data_dir & "/assetsReturns.bit"): 6 | stockall <- [stockall], averret; 7 | 8 | table astrets IN (data_dir & "/covar.bit"): 9 | [Asset, stockall], covar; 10 | -------------------------------------------------------------------------------- /examples/models/tracking/assets.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/tracking/assets.bit -------------------------------------------------------------------------------- /examples/models/tracking/indret.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/tracking/indret.bit -------------------------------------------------------------------------------- /examples/models/tracking/returns.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/tracking/returns.bit -------------------------------------------------------------------------------- /examples/models/tracking/tracking.dat: -------------------------------------------------------------------------------- 1 | set asset := a01 a02 a03 a04 a05 a06 a07 a08 a09 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a20 a21 a22 a23 a24 a25 a26 a27 a28 a29 a30 a31 a32 a33; -------------------------------------------------------------------------------- /examples/models/tracking/tracking.mod: -------------------------------------------------------------------------------- 1 | # This model implements a simple index tracking model, 2 | # in which we choose out holdings to minimize deviations from an index 3 | # over a time horizon. 4 | # It also includes some facilities to implement the simple heuristic 5 | # described in the source file. 6 | param data_dir symbolic; # Directory of data files 7 | 8 | set asset; # Set of assets 9 | set time:=1..290; # Set of timeperiods considered 10 | set rtime; # Set of timeperiods for which we have data 11 | 12 | param indret{rtime}; # Index return 13 | param astret{rtime,asset}; # Assets returns 14 | param ifinuniverse{asset} default 1; # Indicator for heuristic 15 | # set to 1 to be able to choose that asset for the portfolio, 16 | # to 0 to force it out, to 2 to force it in 17 | 18 | var overindex{time} >=0; # Amount of overdeviation 19 | var underindex{time} >=0; # Amount of underdeviation 20 | var hold{i in asset : ifinuniverse[i]>=1} >=0; # Positions 21 | var ifhold{i in asset : ifinuniverse[i]>=1} binary; # Indicates whether we have 22 | # invested in an asset at all 23 | 24 | # Minimize total deviation over time 25 | minimize cst: sum{t in time} (overindex[t]+underindex[t]); 26 | 27 | subject to 28 | 29 | # Balance constraint Considers only the assets which are in the universe 30 | target{t in time}: sum{i in asset : ifinuniverse[i]>=1} hold[i]*astret[t,i]= 31 | indret[t]+overindex[t]-underindex[t]; 32 | # Total holdings = 100% 33 | stckbal: sum{i in asset : ifinuniverse[i]>=1} hold[i]=1; 34 | # Forces ifhold to 0 if we do not hold any of the asset 35 | lowlink{a in asset : ifinuniverse[a]>=1}: hold[a]>=0.005*ifhold[a]; 36 | # Forces ifhold to 1 if we hold any amount of the asset 37 | uplink{a in asset : ifinuniverse[a]>=1}: hold[a]<=ifhold[a]; 38 | # Cardinality constraint 39 | cardlim: sum{a in asset : ifinuniverse[a]>=1}ifhold[a]<=15; 40 | # For "ifinuniverse=2", force the asset in 41 | fixing{a in asset :ifinuniverse[a]=2}:ifhold[a]=1; 42 | -------------------------------------------------------------------------------- /examples/models/tracking/tracking.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ampl/amplpy/477915f768a7c1265167f64dc9058068acc259f8/examples/models/tracking/tracking.xls -------------------------------------------------------------------------------- /examples/models/tracking/trackingbit.run: -------------------------------------------------------------------------------- 1 | table assets IN (data_dir & "/assets.bit"): 2 | asset <-[asset]; 3 | 4 | table indret IN (data_dir & "/indret.bit"): 5 | rtime <-[rtime], indret; 6 | 7 | table returns IN (data_dir & "/returns.bit"): 8 | [TIME, asset], astret; 9 | -------------------------------------------------------------------------------- /examples/multidimensional_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | import pandas as pd 6 | 7 | 8 | def main(argc, argv): 9 | from amplpy import AMPL, DataFrame 10 | 11 | os.chdir(os.path.dirname(__file__) or os.curdir) 12 | 13 | """ 14 | # If you are not using amplpy.modules, and the AMPL installation directory 15 | # is not in the system search path, add it as follows: 16 | from amplpy import add_to_path 17 | add_to_path(r"full path to the AMPL installation directory") 18 | """ 19 | 20 | # Create an AMPL instance 21 | ampl = AMPL() 22 | 23 | ampl.eval("set CITIES; set LINKS within (CITIES cross CITIES);") 24 | ampl.eval("param cost {LINKS} >= 0; param capacity {LINKS} >= 0;") 25 | ampl.eval("data; set CITIES := PITT NE SE BOS EWR BWI ATL MCO;") 26 | 27 | # Using a pandas.DataFrame: 28 | df = pd.DataFrame( 29 | { 30 | "LINKSFrom": ["PITT", "PITT", "NE", "NE", "NE", "SE", "SE", "SE", "SE"], 31 | "LINKSTo": ["NE", "SE", "BOS", "EWR", "BWI", "EWR", "BWI", "ATL", "MCO"], 32 | "cost": [2.5, 3.5, 1.7, 0.7, 1.3, 1.3, 0.8, 0.2, 2.1], 33 | "capacity": [250, 250, 100, 100, 100, 100, 100, 100, 100], 34 | } 35 | ).set_index(["LINKSFrom", "LINKSTo"]) 36 | print(df) 37 | 38 | ampl.eval("reset data LINKS;") 39 | ampl.set_data(df, "LINKS") 40 | ampl.display("LINKS") 41 | 42 | 43 | if __name__ == "__main__": 44 | try: 45 | main(len(sys.argv), sys.argv) 46 | except Exception as e: 47 | print(e) 48 | raise 49 | -------------------------------------------------------------------------------- /examples/notebooks/README.md: -------------------------------------------------------------------------------- 1 | # AMPLPY Notebooks 2 | 3 | AMPL Notebooks have been moved to the [AMPL Model Colaboratory](https://colab.ampl.com/). 4 | 5 | ## Documentation 6 | 7 | - http://amplpy.readthedocs.io 8 | -------------------------------------------------------------------------------- /examples/options_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | 6 | 7 | def main(argc, argv): 8 | # You can install amplpy with "python -m pip install amplpy" 9 | from amplpy import AMPL 10 | 11 | os.chdir(os.path.dirname(__file__) or os.curdir) 12 | 13 | """ 14 | # If you are not using amplpy.modules, and the AMPL installation directory 15 | # is not in the system search path, add it as follows: 16 | from amplpy import add_to_path 17 | add_to_path(r"full path to the AMPL installation directory") 18 | """ 19 | 20 | # Create an AMPL instance 21 | ampl = AMPL() 22 | 23 | # Get the value of the option presolve and print 24 | presolve = ampl.get_option("presolve") 25 | print("AMPL presolve is", presolve) 26 | 27 | # Set the value to false (maps to 0) 28 | ampl.set_option("presolve", False) 29 | 30 | # Get the value of the option presolve and print 31 | presolve = ampl.get_option("presolve") 32 | print("AMPL presolve is now", presolve) 33 | 34 | # Check whether an option with a specified name 35 | # exists 36 | value = ampl.get_option("solver") 37 | if value is not None: 38 | print("Option solver exists and has value:", value) 39 | 40 | # Check again, this time failing 41 | value = ampl.get_option("s_o_l_v_e_r") 42 | if value is None: 43 | print("Option s_o_l_v_e_r does not exist!") 44 | 45 | 46 | if __name__ == "__main__": 47 | try: 48 | main(len(sys.argv), sys.argv) 49 | except Exception as e: 50 | print(e) 51 | raise 52 | -------------------------------------------------------------------------------- /examples/test_examples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | import amplpy 5 | 6 | 7 | class TestExamples(unittest.TestCase): 8 | """Test examples.""" 9 | 10 | def test_first_example(self): 11 | import first_example 12 | 13 | first_example.main(1, [None]) 14 | 15 | def test_options_example(self): 16 | import options_example 17 | 18 | options_example.main(1, [None]) 19 | 20 | def test_dataframe_example(self): 21 | import dataframe_example 22 | 23 | dataframe_example.main(1, [None]) 24 | 25 | def test_multidimensional_example(self): 26 | import multidimensional_example 27 | 28 | multidimensional_example.main(1, [None]) 29 | 30 | def test_dietmodel_example(self): 31 | import diet_model 32 | 33 | diet_model.main(1, [None]) 34 | 35 | def test_efficient_frontier_example(self): 36 | import efficient_frontier 37 | 38 | efficient_frontier.main(1, [None]) 39 | 40 | def test_trackingmodel_example(self): 41 | import tracking_model 42 | 43 | tracking_model.main(1, [None]) 44 | 45 | def test_location_transportation(self): 46 | import location_transportation 47 | 48 | location_transportation.main(1, [None]) 49 | 50 | 51 | if __name__ == "__main__": 52 | unittest.main() 53 | -------------------------------------------------------------------------------- /examples/tracking_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | import os 5 | 6 | 7 | def main(argc, argv): 8 | # You can install amplpy with "python -m pip install amplpy" 9 | from amplpy import AMPL 10 | 11 | os.chdir(os.path.dirname(__file__) or os.curdir) 12 | model_directory = os.path.join(os.curdir, "models", "tracking") 13 | 14 | """ 15 | # If you are not using amplpy.modules, and the AMPL installation directory 16 | # is not in the system search path, add it as follows: 17 | from amplpy import add_to_path 18 | add_to_path(r"full path to the AMPL installation directory") 19 | """ 20 | 21 | # Create an AMPL instance 22 | ampl = AMPL() 23 | 24 | # Set the solver to use 25 | solver = argv[1] if argc > 1 else "highs" 26 | ampl.set_option("solver", solver) 27 | 28 | # Load the AMPL model from file 29 | ampl.read(os.path.join(model_directory, "tracking.mod")) 30 | # Read data 31 | ampl.read_data(os.path.join(model_directory, "tracking.dat")) 32 | # Read table declarations 33 | ampl.read(os.path.join(model_directory, "trackingbit.run")) 34 | # Set tables directory (parameter used in the script above) 35 | ampl.get_parameter("data_dir").set(model_directory) 36 | # Read tables 37 | ampl.read_table("assets") 38 | ampl.read_table("indret") 39 | ampl.read_table("returns") 40 | 41 | hold = ampl.get_variable("hold") 42 | ifinuniverse = ampl.get_parameter("ifinuniverse") 43 | 44 | # Relax the integrality 45 | ampl.set_option("relax_integrality", True) 46 | 47 | # Solve the problem 48 | ampl.solve() 49 | if ampl.solve_result != "solved": 50 | raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") 51 | 52 | objectives = list(obj for name, obj in ampl.get_objectives()) 53 | assert objectives[0].value() == ampl.get_objective("cst").value() 54 | print("QP objective value", ampl.get_objective("cst").value()) 55 | 56 | lowcutoff = 0.04 57 | highcutoff = 0.1 58 | 59 | # Get the variable representing the (relaxed) solution vector 60 | holdvalues = hold.get_values().to_list() 61 | to_hold = [] 62 | # For each asset, if it was held by more than the highcutoff, 63 | # forces it in the model, if less than lowcutoff, forces it out 64 | for _, value in holdvalues: 65 | if value < lowcutoff: 66 | to_hold.append(0) 67 | elif value > highcutoff: 68 | to_hold.append(2) 69 | else: 70 | to_hold.append(1) 71 | # uses those values for the parameter ifinuniverse, which controls 72 | # which stock is included or not in the solution 73 | ifinuniverse.set_values(to_hold) 74 | 75 | # Get back to the integer problem 76 | ampl.set_option("relax_integrality", False) 77 | 78 | # Solve the (integer) problem 79 | ampl.solve() 80 | if ampl.solve_result != "solved": 81 | raise Exception(f"Failed to solve (solve_result: {ampl.solve_result})") 82 | 83 | print("QMIP objective value", ampl.get_objective("cst").value()) 84 | 85 | 86 | if __name__ == "__main__": 87 | try: 88 | main(len(sys.argv), sys.argv) 89 | except Exception as e: 90 | print(e) 91 | raise 92 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools", 4 | "cython==3.1.0", 5 | "wheel", 6 | ] --------------------------------------------------------------------------------