diff --git a/.codecov.yml b/.codecov.yml index 39efc7e8fd5..c61f2815a1b 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -14,6 +14,6 @@ coverage: # - !ci.appveyor.com codecov: notify: - # GHA: 18, Travis: 13, Jenkins: 6 - after_n_builds: 33 + # GHA: 5, Travis: 13, Jenkins: 6 + after_n_builds: 22 # all but 2 wait_for_ci: yes diff --git a/.coin-or/projDesc.xml b/.coin-or/projDesc.xml index 5ea18e58fff..7309508216c 100644 --- a/.coin-or/projDesc.xml +++ b/.coin-or/projDesc.xml @@ -227,8 +227,8 @@ Carl D. Laird, Chair, Pyomo Management Committee, cdlaird at sandia dot gov Use explicit overrides to disable use of automated version reporting. --> - 5.7.0 - 5.7.0 + 5.7.1 + 5.7.1 diff --git a/.github/workflows/pr_master_test.yml b/.github/workflows/pr_master_test.yml index c0877043f0c..6dafcf96f1b 100644 --- a/.github/workflows/pr_master_test.yml +++ b/.github/workflows/pr_master_test.yml @@ -19,23 +19,28 @@ defaults: env: PYTHONWARNINGS: ignore::UserWarning + PYTHON_CORE_PKGS: wheel + PYTHON_REQUIRED_PKGS: > + ply six nose coverage PYTHON_BASE_PKGS: > - coverage cython dill ipython networkx nose openpyxl pathos - pint pymysql pyro4 pyyaml sphinx_rtd_theme sympy xlrd wheel + dill ipython networkx openpyxl pathos + pint pymysql pyro4 pyyaml sphinx_rtd_theme sympy xlrd + python-louvain PYTHON_NUMPY_PKGS: > numpy scipy pyodbc pandas matplotlib seaborn CACHE_VER: v2 jobs: build: - name: ${{ matrix.TARGET }}/${{ matrix.python }}${{ matrix.NAME }} + name: ${{ matrix.TARGET }}/${{ matrix.python }}${{ matrix.other }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] python: [2.7, 3.5, 3.6, 3.7, 3.8, pypy2, pypy3] - mpi: [0] + other: [""] + include: - os: ubuntu-latest TARGET: linux @@ -52,12 +57,29 @@ jobs: - os: ubuntu-latest python: 3.7 + other: /mpi mpi: 3 skip_doctest: 1 TARGET: linux PYENV: conda - PACKAGES: mpi4py - NAME: /mpi + PACKAGES: mpi4py openmpi + + - os: ubuntu-latest + python: 3.9 + other: /slim + slim: 1 + skip_doctest: 1 + TARGET: linux + PYENV: pip + + - os: ubuntu-latest + python: 3.6 + other: /cython + setup_options: --with-cython + skip_doctest: 1 + TARGET: linux + PYENV: pip + PACKAGES: cython exclude: - {os: macos-latest, python: pypy2} @@ -65,14 +87,32 @@ jobs: - {os: windows-latest, python: pypy2} - {os: windows-latest, python: pypy3} - steps: - - uses: actions/checkout@v2 + - name: Checkout Pyomo source + uses: actions/checkout@v2 - name: Configure job parameters run: | - JOB="${{matrix.TARGET}}/${{matrix.python}}${{matrix.NAME}}" - echo "::set-env name=GHA_JOBNAME::"`echo "$JOB" | sed 's|/|_|g'` + JOB="${{matrix.TARGET}}/${{matrix.python}}${{matrix.other}}" + echo "GHA_JOBNAME=$JOB" | sed 's|/|_|g' >> $GITHUB_ENV + if test -z "${{matrix.other}}"; then + echo "GHA_JOBGROUP=${{matrix.TARGET}}" >> $GITHUB_ENV + else + echo "GHA_JOBGROUP=other" >> $GITHUB_ENV + fi + # Note: pandas 1.0.3 causes gams 29.1.0 import to fail in python 3.8 + PYTHON_PACKAGES="${PYTHON_REQUIRED_PKGS}" + if test -z "${{matrix.slim}}"; then + PYTHON_PACKAGES="$PYTHON_PACKAGES ${PYTHON_BASE_PKGS}" + fi + if [[ ${{matrix.python}} != pypy* && ! "${{matrix.slim}}" ]]; then + # NumPy and derivatives either don't build under pypy, or if + # they do, the builds take forever. + PYTHON_PACKAGES="$PYTHON_PACKAGES ${PYTHON_NUMPY_PKGS}" + fi + PYTHON_PACKAGES="$PYTHON_PACKAGES ${{matrix.PACKAGES}}" + echo "PYTHON_PACKAGES=$PYTHON_PACKAGES" \ + | tr '\n' ' ' | sed 's/ \+/ /g' >> $GITHUB_ENV # Ideally we would cache the conda downloads; however, each cache is # over 850MB, and with 5 python versions, that would consume 4.2 of @@ -96,6 +136,7 @@ jobs: - name: OS package cache uses: actions/cache@v1 + if: matrix.TARGET != 'osx' id: os-cache with: path: cache/os @@ -117,7 +158,7 @@ jobs: )" echo "$CURLRC" > ${GITHUB_WORKSPACE}/.curlrc echo "$CURLRC" > ${GITHUB_WORKSPACE}/_curlrc - echo "::set-env name=CURL_HOME::$GITHUB_WORKSPACE" + echo "CURL_HOME=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Update OSX if: matrix.TARGET == 'osx' @@ -155,7 +196,7 @@ jobs: - name: Set up Miniconda Python ${{ matrix.python }} if: matrix.PYENV == 'conda' - uses: goanpeca/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v1 with: auto-update-conda: true python-version: ${{ matrix.python }} @@ -182,21 +223,15 @@ jobs: run: | python -c 'import sys;print(sys.executable)' python -m pip install --cache-dir cache/pip --upgrade pip - # Note: pandas 1.0.3 causes gams 29.1.0 import to fail in python 3.8 - pip install --cache-dir cache/pip ${PYTHON_BASE_PKGS} \ - ${{matrix.PACKAGES}} - if [[ ${{matrix.python}} != pypy* ]]; then - # NumPy and derivatives either don't build under pypy, or if - # they do, the builds take forever. - pip install --cache-dir cache/pip ${PYTHON_NUMPY_PKGS} - fi + pip install --cache-dir cache/pip ${PYTHON_CORE_PKGS} + pip install --cache-dir cache/pip ${PYTHON_PACKAGES} pip install --cache-dir cache/pip cplex \ || echo "WARNING: CPLEX Community Edition is not available" pip install --cache-dir cache/pip xpress \ || echo "WARNING: Xpress Community Edition is not available" - python -c 'import sys; print("::set-env name=PYTHON_EXE::%s" \ - % (sys.executable,))' - echo "::set-env name=NOSETESTS::"`which nosetests` + python -c 'import sys; print("PYTHON_EXE=%s" \ + % (sys.executable,))' >> $GITHUB_ENV + echo "NOSETESTS="`which nosetests` >> $GITHUB_ENV - name: Install Python packages (conda) if: matrix.PYENV == 'conda' @@ -208,17 +243,17 @@ jobs: conda info conda config --show-sources conda list --show-channel-urls - conda install -q -y -c conda-forge ${PYTHON_BASE_PKGS} \ - ${PYTHON_NUMPY_PKGS} ${{matrix.PACKAGES}} + conda install -q -y -c conda-forge ${PYTHON_CORE_PKGS} + conda install -q -y -c conda-forge ${PYTHON_PACKAGES} # Note: CPLEX 12.9 (the last version in conda that supports # Python 2.7) causes a seg fault in the tests. conda install -q -y -c ibmdecisionoptimization cplex=12.10 \ || echo "WARNING: CPLEX Community Edition is not available" conda install -q -y -c fico-xpress xpress \ || echo "WARNING: Xpress Community Edition is not available" - python -c 'import sys; print("::set-env name=PYTHON_EXE::%s" \ - % (sys.executable,))' - echo "::set-env name=NOSETESTS::"`which nosetests` + python -c 'import sys; print("PYTHON_EXE=%s" \ + % (sys.executable,))' >> $GITHUB_ENV + echo "NOSETESTS="`which nosetests` >> $GITHUB_ENV - name: Setup TPL package directories run: | @@ -226,13 +261,13 @@ jobs: mkdir -p "$TPL_DIR" DOWNLOAD_DIR="${GITHUB_WORKSPACE}/cache/download" mkdir -p "$DOWNLOAD_DIR" - echo "::set-env name=TPL_DIR::$TPL_DIR" - echo "::set-env name=DOWNLOAD_DIR::$DOWNLOAD_DIR" + echo "TPL_DIR=$TPL_DIR" >> $GITHUB_ENV + echo "DOWNLOAD_DIR=$DOWNLOAD_DIR" >> $GITHUB_ENV - name: Install Ipopt run: | IPOPT_DIR=$TPL_DIR/ipopt - echo "::add-path::$IPOPT_DIR" + echo "$IPOPT_DIR" >> $GITHUB_PATH mkdir -p $IPOPT_DIR IPOPT_TAR=${DOWNLOAD_DIR}/ipopt.tar.gz if test ! -e $IPOPT_TAR; then @@ -260,9 +295,9 @@ jobs: shell: pwsh run: | $GAMS_DIR="${env:TPL_DIR}/gams" - echo "::add-path::$GAMS_DIR" - echo "::set-env name=LD_LIBRARY_PATH::${env:LD_LIBRARY_PATH}:$GAMS_DIR" - echo "::set-env name=DYLD_LIBRARY_PATH::${env:DYLD_LIBRARY_PATH}:$GAMS_DIR" + echo "$GAMS_DIR" >> $GITHUB_PATH + echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$GAMS_DIR" >> $GITHUB_ENV + echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:$GAMS_DIR" >> $GITHUB_ENV $INSTALLER="${env:DOWNLOAD_DIR}/gams_install.exe" $URL="https://d37drm4t2jghv5.cloudfront.net/distributions/29.1.0" if ( "${{matrix.TARGET}}" -eq "win" ) { @@ -290,7 +325,7 @@ jobs: - name: Install GAMS Python bindings run: | - GAMS_DIR="$TPL_DIR/gams" + GAMS_DIR="${env:TPL_DIR}/gams" py_ver=$($PYTHON_EXE -c 'import sys;v="_%s%s" % sys.version_info[:2] \ ;print(v if v != "_27" else "")') if test -e $GAMS_DIR/apifiles/Python/api$py_ver; then @@ -304,7 +339,7 @@ jobs: shell: pwsh run: | $BARON_DIR="${env:TPL_DIR}/baron" - echo "::add-path::$BARON_DIR" + echo "$BARON_DIR" >> $GITHUB_PATH $URL="https://www.minlp.com/downloads/xecs/baron/current/" if ( "${{matrix.TARGET}}" -eq "win" ) { $INSTALLER = "${env:DOWNLOAD_DIR}/baron_install.exe" @@ -334,7 +369,7 @@ jobs: if: matrix.TARGET != 'win' run: | GJH_DIR="$TPL_DIR/gjh" - echo "::add-path::${GJH_DIR}" + echo "${GJH_DIR}" >> $GITHUB_PATH INSTALL_DIR="${DOWNLOAD_DIR}/gjh" if test ! -e "$INSTALL_DIR/bin"; then mkdir -p "$INSTALL_DIR" @@ -363,11 +398,11 @@ jobs: echo "" echo "Install Pyomo..." echo "" - $PYTHON_EXE setup.py develop + $PYTHON_EXE setup.py develop ${{matrix.setup_options}} echo "" echo "Set custom PYOMO_CONFIG_DIR" echo "" - echo "::set-env name=PYOMO_CONFIG_DIR::${GITHUB_WORKSPACE}/config" + echo "PYOMO_CONFIG_DIR=${GITHUB_WORKSPACE}/config" >> $GITHUB_ENV - name: Set up coverage tracking run: | @@ -377,8 +412,8 @@ jobs: COVERAGE_BASE=${GITHUB_WORKSPACE}/.cover fi COVERAGE_RC=${COVERAGE_BASE}_rc - echo "::set-env name=COVERAGE_RCFILE::$COVERAGE_RC" - echo "::set-env name=COVERAGE_PROCESS_START::$COVERAGE_RC" + echo "COVERAGE_RCFILE=$COVERAGE_RC" >> $GITHUB_ENV + echo "COVERAGE_PROCESS_START=$COVERAGE_RC" >> $GITHUB_ENV cp ${GITHUB_WORKSPACE}/.coveragerc ${COVERAGE_RC} echo "data_file=${COVERAGE_BASE}age" >> ${COVERAGE_RC} SITE_PACKAGES=$($PYTHON_EXE -c "from distutils.sysconfig import \ @@ -427,16 +462,102 @@ jobs: make -C doc/OnlineDocs doctest -d - name: Process code coverage report - env: - CODECOV_NAME: ${{matrix.TARGET}}/${{matrix.python}}${{matrix.NAME}} run: | coverage combine coverage report -i coverage xml -i + + - name: Record build artifacts + uses: actions/upload-artifact@v2 + with: + name: ${{github.job}}_${{env.GHA_JOBGROUP}}-${{env.GHA_JOBNAME}} + path: | + .coverage + coverage.xml + # In general, do not record test results as artifacts to + # manage total artifact storage + # TEST-*.xml + + cover: + name: process-coverage-${{ matrix.TARGET }} + needs: build + if: always() # run even if a build job fails + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + include: + - os: ubuntu-latest + TARGET: linux + - os: macos-latest + TARGET: osx + - os: windows-latest + TARGET: win + + steps: + - name: Checkout Pyomo source + uses: actions/checkout@v2 + # We need the source for .codecov.yml and running "coverage xml" + + - name: Pip package cache + uses: actions/cache@v1 + id: pip-cache + with: + path: cache/pip + key: pip-${{env.CACHE_VER}}.0-${{runner.os}}-3.8 + + - name: Download build artifacts + uses: actions/download-artifact@v2 + with: + path: artifacts + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install Python Packages (pip) + shell: bash # DO NOT REMOVE: see note above + run: | + python -c 'import sys;print(sys.executable)' + python -m pip install --cache-dir cache/pip --upgrade pip + pip install --cache-dir cache/pip ${PYTHON_CORE_PKGS} \ + ${PYTHON_REQUIRED_PKGS} + python -c 'import sys; print("PYTHON_EXE=%s" \ + % (sys.executable,))' >> $GITHUB_ENV + + - name: Install Pyomo and PyUtilib + run: | + echo "" + echo "Clone Pyomo-model-libraries..." + git clone https://github.com/Pyomo/pyomo-model-libraries.git + echo "" + echo "Install PyUtilib..." + echo "" + $PYTHON_EXE -m pip install git+https://github.com/PyUtilib/pyutilib + echo "" + echo "Install Pyomo..." + echo "" + $PYTHON_EXE setup.py develop ${{matrix.setup_options}} + echo "" + echo "Set custom PYOMO_CONFIG_DIR" + echo "" + echo "PYOMO_CONFIG_DIR=${GITHUB_WORKSPACE}/config" >> $GITHUB_ENV + + - name: Generate parse_table_datacmds + run: | + # Manually invoke the DAT parser so that parse_table_datacmds.py + # is generated before running "coverage xml" + $PYTHON_EXE -c "from pyomo.dataportal.parse_datacmds import \ + parse_data_commands; parse_data_commands(data='')" + + - name: Update codecov uploader + run: | set +e - # Always attempt to update the codecov script, but fall back on - # the previously cached script if it fails - CODECOV="${GITHUB_WORKSPACE}/cache/download/codecov.sh" + CODECOV="${GITHUB_WORKSPACE}/codecov.sh" + echo "CODECOV=$CODECOV" >> $GITHUB_ENV for i in `seq 3`; do echo "Downloading current codecov script (attempt ${i})" curl -L https://codecov.io/bash -o $CODECOV @@ -447,54 +568,74 @@ jobs: echo "Pausing $DELAY seconds before re-attempting download" sleep $DELAY done - i=0 - while : ; do - ((i+=1)) - echo "Uploading coverage to codecov (attempt ${i})" - bash $CODECOV -Z -X gcov -X s3 -f coverage.xml - if test $? == 0; then - echo "PASS" > ${GITHUB_WORKSPACE}/codecov.result - break - elif test $i -ge 2; then - # Do not fail the build just because the codecov upload fails - echo "FAIL" > ${GITHUB_WORKSPACE}/codecov.result - break + if test ! -e $CODECOV; then + echo "Failed to download codecov.sh" + exit 1 + fi + + - name: Upload codecov reports + run: | + set +e + function upload { + echo "" + echo "Build group: $1" + export CODECOV_NAME=$1 + export TAG=$1 + export GHA_OS_NAME=${{matrix.TARGET}} + shift + rm -vf .coverage coverage.xml + echo " $@" | sed 's/ /\n /' + coverage combine --debug=dataio $@ + if test ! -e .coverage; then + echo "No coverage to upload." + return fi - DELAY=$(( RANDOM % 30 + 30)) - echo "Pausing $DELAY seconds before re-attempting upload" - sleep $DELAY + coverage xml || coverage xml -i + i=0 + while : ; do + ((i+=1)) + echo "Uploading coverage to codecov (attempt ${i})" + bash $CODECOV -Z -e TAG,GHA_OS_NAME -X gcov -X s3 \ + -f coverage.xml + if test $? == 0; then + echo "PASS $CODECOV_NAME" >> codecov.result + break + elif test $i -ge 2; then + # Do not fail the build (yet) just because the codecov + # upload fails + echo "FAIL $CODECOV_NAME" >> codecov.result + break + fi + DELAY=$(( RANDOM % 30 + 30)) + echo "Pausing $DELAY seconds before re-attempting upload" + sleep $DELAY + done + } + for ARTIFACT in artifacts/*_*${{matrix.TARGET}}_*; do + NAME=`echo $ARTIFACT | cut -d/ -f2` + cp -v $ARTIFACT/.coverage .coverage-$NAME done + upload ${{matrix.TARGET}} .coverage-*_${{matrix.TARGET}}-* + if compgen -G ".coverage-*" > /dev/null; then + upload ${{matrix.TARGET}}/other .coverage-* + fi + # Legacy builds that don't support coverage 5.x + if compgen -G ".coverage-*" > /dev/null; then + echo "" + echo "Installing coverage 4.x for legacy builds" + pip install 'coverage<5' + upload ${{matrix.TARGET}}/legacy .coverage-* + fi - - name: Record build artifacts - uses: actions/upload-artifact@v2 - with: - name: ${{github.job}}_${{env.GHA_JOBNAME}} - path: | - codecov.result - # In general, do not record test results as artifacts to - # manage total artifact storage - # TEST-*.xml - - post: - name: post-build - needs: build - runs-on: ubuntu-latest - steps: - - name: Download build artifacts - uses: actions/download-artifact@v2 - with: - path: artifacts - - - name: Check codecov upload success - run: | - FAIL=`grep FAIL artifacts/*/codecov.result | wc -l` - ALL=`ls -1 artifacts/*/codecov.result | wc -l` - # Fail is more than 1/9 codecov uploads fail - echo "$FAIL of $ALL codecov uploads failed" - if test $FAIL -gt 0; then - grep FAIL artifacts/*/codecov.result | sed 's/^/ /' - if test $(( $FAIL * 9 )) -gt $ALL; then - echo "More than 1/9 codecov uploads failed:" - exit 1 - fi - fi + - name: Check codecov upload success + run: | + FAIL=`grep FAIL codecov.result | wc -l` + ALL=`cat codecov.result | wc -l` + echo "$FAIL of $ALL codecov uploads failed" + if test $FAIL -gt 0; then + grep FAIL codecov.result | sed 's/^/ /' + if test $FAIL -gt 1; then + echo "More than 1 codecov upload failed." + exit 1 + fi + fi diff --git a/.github/workflows/push_branch_test.yml b/.github/workflows/push_branch_test.yml index 1ff9c682e41..768e000314b 100644 --- a/.github/workflows/push_branch_test.yml +++ b/.github/workflows/push_branch_test.yml @@ -16,23 +16,28 @@ defaults: env: PYTHONWARNINGS: ignore::UserWarning + PYTHON_CORE_PKGS: wheel + PYTHON_REQUIRED_PKGS: > + ply six nose coverage PYTHON_BASE_PKGS: > - coverage cython dill ipython networkx nose openpyxl pathos - pint pymysql pyro4 pyyaml sphinx_rtd_theme sympy xlrd wheel + dill ipython networkx openpyxl pathos + pint pymysql pyro4 pyyaml sphinx_rtd_theme sympy xlrd + python-louvain PYTHON_NUMPY_PKGS: > numpy scipy pyodbc pandas matplotlib seaborn CACHE_VER: v2 jobs: build: - name: ${{ matrix.TARGET }}/${{ matrix.python }}${{ matrix.NAME }} + name: ${{ matrix.TARGET }}/${{ matrix.python }}${{ matrix.other }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] python: [3.8] - mpi: [0] + other: [""] + include: - os: ubuntu-latest TARGET: linux @@ -51,12 +56,29 @@ jobs: - os: ubuntu-latest python: 3.7 + other: /mpi mpi: 3 skip_doctest: 1 TARGET: linux PYENV: conda - PACKAGES: mpi4py - NAME: /mpi + PACKAGES: mpi4py openmpi + + - os: ubuntu-latest + python: 3.9 + other: /slim + slim: 1 + skip_doctest: 1 + TARGET: linux + PYENV: pip + + - os: ubuntu-latest + python: 3.6 + other: /cython + setup_options: --with-cython + skip_doctest: 1 + TARGET: linux + PYENV: pip + PACKAGES: cython exclude: - {os: macos-latest, python: pypy2} @@ -64,14 +86,32 @@ jobs: - {os: windows-latest, python: pypy2} - {os: windows-latest, python: pypy3} - steps: - - uses: actions/checkout@v2 + - name: Checkout Pyomo source + uses: actions/checkout@v2 - name: Configure job parameters run: | - JOB="${{matrix.TARGET}}/${{matrix.python}}${{matrix.NAME}}" - echo "::set-env name=GHA_JOBNAME::"`echo "$JOB" | sed 's|/|_|g'` + JOB="${{matrix.TARGET}}/${{matrix.python}}${{matrix.other}}" + echo "GHA_JOBNAME=$JOB" | sed 's|/|_|g' >> $GITHUB_ENV + if test -z "${{matrix.other}}"; then + echo "GHA_JOBGROUP=${{matrix.TARGET}}" >> $GITHUB_ENV + else + echo "GHA_JOBGROUP=other" >> $GITHUB_ENV + fi + # Note: pandas 1.0.3 causes gams 29.1.0 import to fail in python 3.8 + PYTHON_PACKAGES="${PYTHON_REQUIRED_PKGS}" + if test -z "${{matrix.slim}}"; then + PYTHON_PACKAGES="$PYTHON_PACKAGES ${PYTHON_BASE_PKGS}" + fi + if [[ ${{matrix.python}} != pypy* && ! "${{matrix.slim}}" ]]; then + # NumPy and derivatives either don't build under pypy, or if + # they do, the builds take forever. + PYTHON_PACKAGES="$PYTHON_PACKAGES ${PYTHON_NUMPY_PKGS}" + fi + PYTHON_PACKAGES="$PYTHON_PACKAGES ${{matrix.PACKAGES}}" + echo "PYTHON_PACKAGES=$PYTHON_PACKAGES" \ + | tr '\n' ' ' | sed 's/ \+/ /g' >> $GITHUB_ENV # Ideally we would cache the conda downloads; however, each cache is # over 850MB, and with 5 python versions, that would consume 4.2 of @@ -95,6 +135,7 @@ jobs: - name: OS package cache uses: actions/cache@v1 + if: matrix.TARGET != 'osx' id: os-cache with: path: cache/os @@ -116,7 +157,7 @@ jobs: )" echo "$CURLRC" > ${GITHUB_WORKSPACE}/.curlrc echo "$CURLRC" > ${GITHUB_WORKSPACE}/_curlrc - echo "::set-env name=CURL_HOME::$GITHUB_WORKSPACE" + echo "CURL_HOME=$GITHUB_WORKSPACE" >> $GITHUB_ENV - name: Update OSX if: matrix.TARGET == 'osx' @@ -154,7 +195,7 @@ jobs: - name: Set up Miniconda Python ${{ matrix.python }} if: matrix.PYENV == 'conda' - uses: goanpeca/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v1 with: auto-update-conda: true python-version: ${{ matrix.python }} @@ -181,21 +222,15 @@ jobs: run: | python -c 'import sys;print(sys.executable)' python -m pip install --cache-dir cache/pip --upgrade pip - # Note: pandas 1.0.3 causes gams 29.1.0 import to fail in python 3.8 - pip install --cache-dir cache/pip ${PYTHON_BASE_PKGS} \ - ${{matrix.PACKAGES}} - if [[ ${{matrix.python}} != pypy* ]]; then - # NumPy and derivatives either don't build under pypy, or if - # they do, the builds take forever. - pip install --cache-dir cache/pip ${PYTHON_NUMPY_PKGS} - fi + pip install --cache-dir cache/pip ${PYTHON_CORE_PKGS} + pip install --cache-dir cache/pip ${PYTHON_PACKAGES} pip install --cache-dir cache/pip cplex \ || echo "WARNING: CPLEX Community Edition is not available" pip install --cache-dir cache/pip xpress \ || echo "WARNING: Xpress Community Edition is not available" - python -c 'import sys; print("::set-env name=PYTHON_EXE::%s" \ - % (sys.executable,))' - echo "::set-env name=NOSETESTS::"`which nosetests` + python -c 'import sys; print("PYTHON_EXE=%s" \ + % (sys.executable,))' >> $GITHUB_ENV + echo "NOSETESTS="`which nosetests` >> $GITHUB_ENV - name: Install Python packages (conda) if: matrix.PYENV == 'conda' @@ -207,17 +242,17 @@ jobs: conda info conda config --show-sources conda list --show-channel-urls - conda install -q -y -c conda-forge ${PYTHON_BASE_PKGS} \ - ${PYTHON_NUMPY_PKGS} ${{matrix.PACKAGES}} + conda install -q -y -c conda-forge ${PYTHON_CORE_PKGS} + conda install -q -y -c conda-forge ${PYTHON_PACKAGES} # Note: CPLEX 12.9 (the last version in conda that supports # Python 2.7) causes a seg fault in the tests. conda install -q -y -c ibmdecisionoptimization cplex=12.10 \ || echo "WARNING: CPLEX Community Edition is not available" conda install -q -y -c fico-xpress xpress \ || echo "WARNING: Xpress Community Edition is not available" - python -c 'import sys; print("::set-env name=PYTHON_EXE::%s" \ - % (sys.executable,))' - echo "::set-env name=NOSETESTS::"`which nosetests` + python -c 'import sys; print("PYTHON_EXE=%s" \ + % (sys.executable,))' >> $GITHUB_ENV + echo "NOSETESTS="`which nosetests` >> $GITHUB_ENV - name: Setup TPL package directories run: | @@ -225,13 +260,13 @@ jobs: mkdir -p "$TPL_DIR" DOWNLOAD_DIR="${GITHUB_WORKSPACE}/cache/download" mkdir -p "$DOWNLOAD_DIR" - echo "::set-env name=TPL_DIR::$TPL_DIR" - echo "::set-env name=DOWNLOAD_DIR::$DOWNLOAD_DIR" + echo "TPL_DIR=$TPL_DIR" >> $GITHUB_ENV + echo "DOWNLOAD_DIR=$DOWNLOAD_DIR" >> $GITHUB_ENV - name: Install Ipopt run: | IPOPT_DIR=$TPL_DIR/ipopt - echo "::add-path::$IPOPT_DIR" + echo "$IPOPT_DIR" >> $GITHUB_PATH mkdir -p $IPOPT_DIR IPOPT_TAR=${DOWNLOAD_DIR}/ipopt.tar.gz if test ! -e $IPOPT_TAR; then @@ -259,9 +294,9 @@ jobs: shell: pwsh run: | $GAMS_DIR="${env:TPL_DIR}/gams" - echo "::add-path::$GAMS_DIR" - echo "::set-env name=LD_LIBRARY_PATH::${env:LD_LIBRARY_PATH}:$GAMS_DIR" - echo "::set-env name=DYLD_LIBRARY_PATH::${env:DYLD_LIBRARY_PATH}:$GAMS_DIR" + echo "$GAMS_DIR" >> $GITHUB_PATH + echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$GAMS_DIR" >> $GITHUB_ENV + echo "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}:$GAMS_DIR" >> $GITHUB_ENV $INSTALLER="${env:DOWNLOAD_DIR}/gams_install.exe" $URL="https://d37drm4t2jghv5.cloudfront.net/distributions/29.1.0" if ( "${{matrix.TARGET}}" -eq "win" ) { @@ -289,7 +324,7 @@ jobs: - name: Install GAMS Python bindings run: | - GAMS_DIR="$TPL_DIR/gams" + GAMS_DIR="${env:TPL_DIR}/gams" py_ver=$($PYTHON_EXE -c 'import sys;v="_%s%s" % sys.version_info[:2] \ ;print(v if v != "_27" else "")') if test -e $GAMS_DIR/apifiles/Python/api$py_ver; then @@ -303,7 +338,7 @@ jobs: shell: pwsh run: | $BARON_DIR="${env:TPL_DIR}/baron" - echo "::add-path::$BARON_DIR" + echo "$BARON_DIR" >> $GITHUB_PATH $URL="https://www.minlp.com/downloads/xecs/baron/current/" if ( "${{matrix.TARGET}}" -eq "win" ) { $INSTALLER = "${env:DOWNLOAD_DIR}/baron_install.exe" @@ -333,7 +368,7 @@ jobs: if: matrix.TARGET != 'win' run: | GJH_DIR="$TPL_DIR/gjh" - echo "::add-path::${GJH_DIR}" + echo "${GJH_DIR}" >> $GITHUB_PATH INSTALL_DIR="${DOWNLOAD_DIR}/gjh" if test ! -e "$INSTALL_DIR/bin"; then mkdir -p "$INSTALL_DIR" @@ -362,11 +397,11 @@ jobs: echo "" echo "Install Pyomo..." echo "" - $PYTHON_EXE setup.py develop + $PYTHON_EXE setup.py develop ${{matrix.setup_options}} echo "" echo "Set custom PYOMO_CONFIG_DIR" echo "" - echo "::set-env name=PYOMO_CONFIG_DIR::${GITHUB_WORKSPACE}/config" + echo "PYOMO_CONFIG_DIR=${GITHUB_WORKSPACE}/config" >> $GITHUB_ENV - name: Set up coverage tracking run: | @@ -376,8 +411,8 @@ jobs: COVERAGE_BASE=${GITHUB_WORKSPACE}/.cover fi COVERAGE_RC=${COVERAGE_BASE}_rc - echo "::set-env name=COVERAGE_RCFILE::$COVERAGE_RC" - echo "::set-env name=COVERAGE_PROCESS_START::$COVERAGE_RC" + echo "COVERAGE_RCFILE=$COVERAGE_RC" >> $GITHUB_ENV + echo "COVERAGE_PROCESS_START=$COVERAGE_RC" >> $GITHUB_ENV cp ${GITHUB_WORKSPACE}/.coveragerc ${COVERAGE_RC} echo "data_file=${COVERAGE_BASE}age" >> ${COVERAGE_RC} SITE_PACKAGES=$($PYTHON_EXE -c "from distutils.sysconfig import \ @@ -426,16 +461,102 @@ jobs: make -C doc/OnlineDocs doctest -d - name: Process code coverage report - env: - CODECOV_NAME: ${{matrix.TARGET}}/${{matrix.python}}${{matrix.NAME}} run: | coverage combine coverage report -i coverage xml -i + + - name: Record build artifacts + uses: actions/upload-artifact@v2 + with: + name: ${{github.job}}_${{env.GHA_JOBGROUP}}-${{env.GHA_JOBNAME}} + path: | + .coverage + coverage.xml + # In general, do not record test results as artifacts to + # manage total artifact storage + # TEST-*.xml + + cover: + name: process-coverage-${{ matrix.TARGET }} + needs: build + if: always() # run even if a build job fails + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + include: + - os: ubuntu-latest + TARGET: linux + - os: macos-latest + TARGET: osx + - os: windows-latest + TARGET: win + + steps: + - name: Checkout Pyomo source + uses: actions/checkout@v2 + # We need the source for .codecov.yml and running "coverage xml" + + - name: Pip package cache + uses: actions/cache@v1 + id: pip-cache + with: + path: cache/pip + key: pip-${{env.CACHE_VER}}.0-${{runner.os}}-3.8 + + - name: Download build artifacts + uses: actions/download-artifact@v2 + with: + path: artifacts + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install Python Packages (pip) + shell: bash # DO NOT REMOVE: see note above + run: | + python -c 'import sys;print(sys.executable)' + python -m pip install --cache-dir cache/pip --upgrade pip + pip install --cache-dir cache/pip ${PYTHON_CORE_PKGS} \ + ${PYTHON_REQUIRED_PKGS} + python -c 'import sys; print("PYTHON_EXE=%s" \ + % (sys.executable,))' >> $GITHUB_ENV + + - name: Install Pyomo and PyUtilib + run: | + echo "" + echo "Clone Pyomo-model-libraries..." + git clone https://github.com/Pyomo/pyomo-model-libraries.git + echo "" + echo "Install PyUtilib..." + echo "" + $PYTHON_EXE -m pip install git+https://github.com/PyUtilib/pyutilib + echo "" + echo "Install Pyomo..." + echo "" + $PYTHON_EXE setup.py develop ${{matrix.setup_options}} + echo "" + echo "Set custom PYOMO_CONFIG_DIR" + echo "" + echo "PYOMO_CONFIG_DIR=${GITHUB_WORKSPACE}/config" >> $GITHUB_ENV + + - name: Generate parse_table_datacmds + run: | + # Manually invoke the DAT parser so that parse_table_datacmds.py + # is generated before running "coverage xml" + $PYTHON_EXE -c "from pyomo.dataportal.parse_datacmds import \ + parse_data_commands; parse_data_commands(data='')" + + - name: Update codecov uploader + run: | set +e - # Always attempt to update the codecov script, but fall back on - # the previously cached script if it fails - CODECOV="${GITHUB_WORKSPACE}/cache/download/codecov.sh" + CODECOV="${GITHUB_WORKSPACE}/codecov.sh" + echo "CODECOV=$CODECOV" >> $GITHUB_ENV for i in `seq 3`; do echo "Downloading current codecov script (attempt ${i})" curl -L https://codecov.io/bash -o $CODECOV @@ -446,54 +567,74 @@ jobs: echo "Pausing $DELAY seconds before re-attempting download" sleep $DELAY done - i=0 - while : ; do - ((i+=1)) - echo "Uploading coverage to codecov (attempt ${i})" - bash $CODECOV -Z -X gcov -X s3 -f coverage.xml - if test $? == 0; then - echo "PASS" > ${GITHUB_WORKSPACE}/codecov.result - break - elif test $i -ge 2; then - # Do not fail the build just because the codecov upload fails - echo "FAIL" > ${GITHUB_WORKSPACE}/codecov.result - break + if test ! -e $CODECOV; then + echo "Failed to download codecov.sh" + exit 1 + fi + + - name: Upload codecov reports + run: | + set +e + function upload { + echo "" + echo "Build group: $1" + export CODECOV_NAME=$1 + export TAG=$1 + export GHA_OS_NAME=${{matrix.TARGET}} + shift + rm -vf .coverage coverage.xml + echo " $@" | sed 's/ /\n /' + coverage combine --debug=dataio $@ + if test ! -e .coverage; then + echo "No coverage to upload." + return fi - DELAY=$(( RANDOM % 30 + 30)) - echo "Pausing $DELAY seconds before re-attempting upload" - sleep $DELAY + coverage xml || coverage xml -i + i=0 + while : ; do + ((i+=1)) + echo "Uploading coverage to codecov (attempt ${i})" + bash $CODECOV -Z -e TAG,GHA_OS_NAME -X gcov -X s3 \ + -f coverage.xml + if test $? == 0; then + echo "PASS $CODECOV_NAME" >> codecov.result + break + elif test $i -ge 2; then + # Do not fail the build (yet) just because the codecov + # upload fails + echo "FAIL $CODECOV_NAME" >> codecov.result + break + fi + DELAY=$(( RANDOM % 30 + 30)) + echo "Pausing $DELAY seconds before re-attempting upload" + sleep $DELAY + done + } + for ARTIFACT in artifacts/*_*${{matrix.TARGET}}_*; do + NAME=`echo $ARTIFACT | cut -d/ -f2` + cp -v $ARTIFACT/.coverage .coverage-$NAME done + upload ${{matrix.TARGET}} .coverage-*_${{matrix.TARGET}}-* + if compgen -G ".coverage-*" > /dev/null; then + upload ${{matrix.TARGET}}/other .coverage-* + fi + # Legacy builds that don't support coverage 5.x + if compgen -G ".coverage-*" > /dev/null; then + echo "" + echo "Installing coverage 4.x for legacy builds" + pip install 'coverage<5' + upload ${{matrix.TARGET}}/legacy .coverage-* + fi - - name: Record build artifacts - uses: actions/upload-artifact@v2 - with: - name: ${{github.job}}_${{env.GHA_JOBNAME}} - path: | - codecov.result - # In general, do not record test results as artifacts to - # manage total artifact storage - # TEST-*.xml - - post: - name: post-build - needs: build - runs-on: ubuntu-latest - steps: - - name: Download build artifacts - uses: actions/download-artifact@v2 - with: - path: artifacts - - - name: Check codecov upload success - run: | - FAIL=`grep FAIL artifacts/*/codecov.result | wc -l` - ALL=`ls -1 artifacts/*/codecov.result | wc -l` - # Fail is more than 1/9 codecov uploads fail - echo "$FAIL of $ALL codecov uploads failed" - if test $FAIL -gt 0; then - grep FAIL artifacts/*/codecov.result | sed 's/^/ /' - if test $(( $FAIL * 9 )) -gt $ALL; then - echo "More than 1/9 codecov uploads failed:" - exit 1 - fi - fi + - name: Check codecov upload success + run: | + FAIL=`grep FAIL codecov.result | wc -l` + ALL=`cat codecov.result | wc -l` + echo "$FAIL of $ALL codecov uploads failed" + if test $FAIL -gt 0; then + grep FAIL codecov.result | sed 's/^/ /' + if test $FAIL -gt 1; then + echo "More than 1 codecov upload failed." + exit 1 + fi + fi diff --git a/.gitignore b/.gitignore index 83a1ea0f817..f44c82598ae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ .idea .spyder* .ropeproject - +.vscode # Python generates numerous files when byte compiling / installing packages *.pyx *.pyc diff --git a/.jenkins.sh b/.jenkins.sh index 7f716779c6b..7fea07351ac 100644 --- a/.jenkins.sh +++ b/.jenkins.sh @@ -25,6 +25,9 @@ # # DISABLE_COVERAGE: if nonempty, then coverage analysis is disabled # +# PYOMO_SETUP_ARGS: passed to the 'python setup.py develop' command +# (e.g., to specify --with-cython) +# # PYOMO_DOWNLOAD_ARGS: passed to the 'pyomo download-extensions" command # (e.g., to set up local SSL certificate authorities) # @@ -52,16 +55,21 @@ MODE="$1" if test -z "$MODE" -o "$MODE" == setup; then # Clean old PYC files and remove any previous virtualenv echo "#" - echo "# Cleaning out old .pyc files" + echo "# Removing python virtual environment" echo "#" rm -rf ${WORKSPACE}/python - find ${WORKSPACE}/pyomo -name \*.pyc -delete - find ${WORKSPACE}/pyutilib -name \*.pyc -delete + echo "#" + echo "# Cleaning out old .pyc and cython files" + echo "#" + for EXT in pyc pyx pyd so dylib dll; do + find ${WORKSPACE}/pyomo -name \*.$EXT -delete + find ${WORKSPACE}/pyutilib -name \*.$EXT -delete + done # Set up the local lpython echo "" echo "#" - echo "# Setting up virutal environment" + echo "# Setting up virtual environment" echo "#" virtualenv python $VENV_SYSTEM_PACKAGES --clear # Put the venv at the beginning of the PATH @@ -81,7 +89,7 @@ if test -z "$MODE" -o "$MODE" == setup; then python setup.py develop || exit 1 popd pushd "$WORKSPACE/pyomo" || exit 1 - python setup.py develop || exit 1 + python setup.py develop $PYOMO_SETUP_ARGS || exit 1 popd # # DO NOT install pyomo-model-libraries @@ -187,7 +195,7 @@ if test -z "$MODE" -o "$MODE" == test; then while /bin/true; do i=$[$i+1] echo "Uploading coverage to codecov (attempt $i)" - codecov -X gcovcodecov -X gcov --no-color \ + codecov -X gcovcodecov -X gcov -X s3 --no-color \ -t $CODECOV_TOKEN --root `pwd` -e OS,python \ --name $CODECOV_JOB_NAME $CODECOV_ARGS \ | tee .cover.upload diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4500f279b28..2edf67bdeb0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,68 @@ Pyomo CHANGELOG =============== +------------------------------------------------------------------------------- +Pyomo 5.7.1 15 Sep 2020 +------------------------------------------------------------------------------- + +- General + - Add functions for checking the solver termination condition to environ (#1540) + - Remove appdirs dependency (#1558) + - Setting version number for new deprecation warnings (#1524) + - Add generic tarball creation to GitHub Actions Workflow for creating Wheels + (#1522) + - Deprecate pyomo install-extras (#1521) + - Add test to monitor 'import pyomo.environ' time (#1577) + - Move ComponentMap and ComponentSet to pyomo.common.collections (#1593) + - Fix issue when parsing /etc/os-release (#1603) + - Fix deprecation decorator tests (#1629) +- Core + - Add a logical expression system (#1507) + - Resolve table formatting with unicode character data (#1530) + - Fix Model.compute_statistics method (#1553) + - Deprecate CUID targets in add_slack_variables transformation (#1563) + - Defer import of sympy in the cnf_walker (#1576) + - Minor fixes to units (#1582, #1584) + - Add IndexedSet.data method that was mistakenly removed (#1609) + - Add support for class method initializers (#1608) + - Rework constraint initialization (#1592) + - Remove truthiness assumption for sequence in TuplizeValuesInitializer (#1620) + - Add deprecated support for Var(within=RealSet) (#1619) + - Fix standard repn of external functions with fixed arguments (#1623) +- Solver Interfaces + - Use Baron option to more consistently return duals (#1538) + - Add direct and persistent interfaces to Xpress (#1443) + - Augment persistent solver interfaces to support column generation (#1568) +- DAE Updates + - Add options to solve_consistent_initial_conditions (#1534, #1606) + - Add find_nearest_index method to ContinuousSet (#1559) + - Allow flattening of ctypes other than Var (#1583) +- GDP Updates + - Fix a couple bugs in GDP basic steps (#1532) + - Make key for calculated M values in bigM dictionary consistent (#1618) +- Testing + - Fix to retry codecov upload if codecov.sh script fails (#1539) + - Update to GitHub Actions tests to fix an OSX Python environment failure (#1555) + - Allow failing codecov uploads on GitHub Actions tests (#1561) + - Add doctests to the GitHub Actions builds (#1542) + - Add manual job trigger for GitHub Actions Workflows (#1578) + - Rebuild GitHub Actions download caches (#1598) + - Disable OS package cache for OSX (#1625) +- Documentation + - Clean up some old documentation on sparse sets (#1545) + - Remove OnlineDocs spy files from the repository (#1549) + - Resolve RTD documentation build warnings (#1552) + - Fix typo in code snippet (#1624) + - Update contribution guide (#1621) +- Contributed Packages + - Defer the Z3 availability check in the satsolver (#1529) + - MindtPy: add configuration arguments, bug fixes (#1500) + - Parmest: fix solver options typo, update graphics module (#1531, #1622) + - PyNumero: Add scaling to Cyipopt interface, set AMPLFUNC before loading NL + files, improve MPI matvec performance (#1554, #1546, #1610) + - Fix bugs in sensitivity toolbox (#1536, #1574) + - Add integer arithmetic option to FME transformation (#1594) + ------------------------------------------------------------------------------- Pyomo 5.7.0 19 Jun 2020 ------------------------------------------------------------------------------- diff --git a/README.md b/README.md index dea07ed9db3..62eae133fb2 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ [![a COIN-OR project](https://www.coin-or.org/GitHub/coin-or-badge.png)](https://www.coin-or.org) ## Pyomo Overview - Pyomo is a Python-based open-source software package that supports a diverse set of optimization capabilities for formulating and analyzing optimization models. Pyomo can be used to define symbolic problems, create concrete problem instances, and solve these instances with standard solvers. Pyomo supports a wide range of problem types, including: - Linear programming diff --git a/RELEASE.txt b/RELEASE.txt index 8225042e7ed..8cc5d3e7140 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,13 +1,15 @@ -We are pleased to announce the release of Pyomo 5.7.0. Pyomo is a collection +We are pleased to announce the release of Pyomo 5.7.1. Pyomo is a collection of Python software packages that supports a diverse set of optimization capabilities for formulating and analyzing optimization models. The following are highlights of the 5.7 release series: + - Adding a logical expression system - Standardizing component initialization - Distribution of Cythonized wheels - Reimplementation of the core Set component + - Deprecating pyomo-extras - Numerous bug fixes A full list of updates and changes is available in the CHANGELOG.txt diff --git a/doc/OnlineDocs/conf.py b/doc/OnlineDocs/conf.py index 4d21cb2c1af..8022cd22579 100644 --- a/doc/OnlineDocs/conf.py +++ b/doc/OnlineDocs/conf.py @@ -213,8 +213,13 @@ def setup(app): doctest_global_setup = ''' -import pyomo.opt +from pyomo.common.dependencies import ( + attempt_import, numpy_available, scipy_available, pandas_available, + yaml_available, networkx_available +) +pint_available = attempt_import('pint', defer_check=False)[1] +import pyomo.opt # Not using SolverFactory to check solver availability because # as of June 2020 there is no way to supress warnings when # solvers are not available diff --git a/doc/OnlineDocs/contributed_packages/mindtpy.rst b/doc/OnlineDocs/contributed_packages/mindtpy.rst index 3b05762b065..8c909ea5513 100644 --- a/doc/OnlineDocs/contributed_packages/mindtpy.rst +++ b/doc/OnlineDocs/contributed_packages/mindtpy.rst @@ -7,12 +7,13 @@ These decomposition algorithms usually rely on the solution of Mixed-Intger Line (MILP) and Nonlinear Programs (NLP). MindtPy currently implements the Outer Approximation (OA) algorithm originally described in -`Duran & Grossmann, 1986`_. Usage and implementation +`Duran & Grossmann, 1986`_ and the Extended Cutting Plane (ECP) algorithm originally described in `Westerlund & Petterson, 1995`_. Usage and implementation details for MindtPy can be found in the PSE 2018 paper Bernal et al., (`ref `_, `preprint `_). .. _Duran & Grossmann, 1986: https://dx.doi.org/10.1007/BF02592064 +.. _Westerlund & Petterson, 1995: http://dx.doi.org/10.1016/0098-1354(95)87027-X Usage of MindtPy to solve a Pyomo concrete model involves: @@ -58,6 +59,20 @@ The solution may then be displayed by using the commands >>> SolverFactory('mindtpy').solve(model, mip_solver='glpk', nlp_solver='ipopt', tee=True) +MindtPy also supports setting options for mip solver and nlp solver. + +.. code:: + + >>> SolverFactory('mindtpy').solve(model, + strategy='OA', + time_limit=3600, + mip_solver='gams', + mip_solver_args=dict(solver='cplex', warmstart=True), + nlp_solver='ipopt', + tee=True) + +There are three initialization strategies in MindtPy: rNLP, initial_binary, max_binary. In OA and GOA strategies, the default initialization strategy is rNLP. In ECP strategy, the default initialization strategy is max_binary. + Single tree implementation --------------------------------------------- diff --git a/doc/OnlineDocs/contributed_packages/parmest/datarec.rst b/doc/OnlineDocs/contributed_packages/parmest/datarec.rst index 92c61412993..1877cd49caf 100644 --- a/doc/OnlineDocs/contributed_packages/parmest/datarec.rst +++ b/doc/OnlineDocs/contributed_packages/parmest/datarec.rst @@ -1,7 +1,7 @@ .. _datarecsection: Data Reconciliation -================================= +==================== The method :class:`~pyomo.contrib.parmest.parmest.Estimator.theta_est` can optionally return model values. This feature can be used to return @@ -17,6 +17,8 @@ reconciliation. The functions :class:`~pyomo.contrib.parmest.graphics.grouped_violinplot` can be used to visually compare the original and reconciled data. +Here's a stylized code snippet showing how box plots might be created: + .. doctest:: :skipif: True @@ -24,3 +26,33 @@ to visually compare the original and reconciled data. >>> pest = parmest.Estimator(model_function, data, [], objective_function) >>> obj, theta, data_rec = pest.theta_est(return_values=['A', 'B']) >>> parmest.grouped_boxplot(data, data_rec) + +Returned Values +^^^^^^^^^^^^^^^ + +Here's a full program that can be run to see returned values (in this case it +is the response function that is defined in the model file): + +.. doctest:: + :skipif: not ipopt_available or not pandas_available + + >>> import pandas as pd + >>> import pyomo.contrib.parmest.parmest as parmest + >>> from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import rooney_biegler_model + + >>> theta_names = ['asymptote', 'rate_constant'] + + >>> data = pd.DataFrame(data=[[1,8.3],[2,10.3],[3,19.0], + ... [4,16.0],[5,15.6],[7,19.8]], + ... columns=['hour', 'y']) + + >>> def SSE(model, data): + ... expr = sum((data.y[i]\ + ... - model.response_function[data.hour[i]])**2 for i in data.index) + ... return expr + + >>> pest = parmest.Estimator(rooney_biegler_model, data, theta_names, SSE, + ... solver_options=None) + >>> obj, theta, var_values = pest.theta_est(return_values=['response_function']) + >>> #print(var_values) + diff --git a/doc/OnlineDocs/contribution_guide.rst b/doc/OnlineDocs/contribution_guide.rst index 3439a96ff88..0ac25173f43 100644 --- a/doc/OnlineDocs/contribution_guide.rst +++ b/doc/OnlineDocs/contribution_guide.rst @@ -14,6 +14,10 @@ separated from functional changes. This simplifies the review process and ensures that functional changes aren't obscured by large amounts of non-functional changes. +We do not squash and merge PRs so all commits in your branch will appear +in the master history. In addition to well-documented PR descriptions, +we encourage modular/targeted commits with descriptive commit messages. + Coding Standards ++++++++++++++++ @@ -36,20 +40,22 @@ and contributions to ``pyomo.contrib``. Testing +++++++ -Pyomo uses ``unittest``, TravisCI, and Appveyor for testing and -continuous integration. Submitted code should include tests to establish -the validity of its results and/or effects. Unit tests are preferred but -we also accept integration tests. When test are run on a PR, we require -at least 70% coverage of the lines modified in the PR and prefer -coverage closer to 90%. We also require that all tests pass before a PR -will be merged. - -The Pyomo master branch (as of `this commit `) provides a Github Action -workflow that will test any changes pushed to a branch using Ubuntu with -Python 3.7. For existing forks, fetch and merge your fork (and branches) with -Pyomo's master. For new forks, you will need to enable Github Actions -in the 'Actions' tab on your fork. Then the test will begin to run -automatically with each push to your fork. +Pyomo uses ``unittest``, TravisCI, and +`GitHub workflows `_ +for testing and continuous integration. Submitted code should include +tests to establish the validity of its results and/or effects. Unit +tests are preferred but we also accept integration tests. We require +at least 70% coverage of the lines modified in the PR and prefer coverage +closer to 90%. We also require that all tests pass before a PR will be +merged. + +The Pyomo master branch provides a Github Actions workflow (configured +in the ``.github/`` directory) that will test any changes pushed to +a branch on multiple virtual machines (ubuntu, mac-os, windows) +and with multiple Python versions. For existing forks, fetch and merge +your fork (and branches) with Pyomo's master. For new forks, you will +need to enable Github Actions in the 'Actions' tab on your fork. +Then the tests will begin to run automatically with each push to your fork. At any point in the development cycle, a "work in progress" pull request may be opened by including '[WIP]' at the beginning of the PR diff --git a/doc/OnlineDocs/working_models.rst b/doc/OnlineDocs/working_models.rst index 9bfcffb4ae4..df334aa0891 100644 --- a/doc/OnlineDocs/working_models.rst +++ b/doc/OnlineDocs/working_models.rst @@ -301,8 +301,8 @@ Note that is equivalent to - >>> instance.y.value = 2 - >>> instance.y.fixed = True + >>> instance.x.value = 2 + >>> instance.x.fixed = True and >>> instance.x.fix() @@ -614,7 +614,7 @@ snippet: If multiple options are needed, then multiple dictionary entries should be added. -Sometime it is desirable to pass options as part of the call to the +Sometimes it is desirable to pass options as part of the call to the solve function as in this snippet: .. literalinclude:: tests/scripting/spy4scripts_Add_multiple_options_to_solver.spy diff --git a/examples/pysp/farmer/models/ReferenceModel.py b/examples/pysp/farmer/models/ReferenceModel.py index 9620fe73916..973130af1b6 100644 --- a/examples/pysp/farmer/models/ReferenceModel.py +++ b/examples/pysp/farmer/models/ReferenceModel.py @@ -14,7 +14,9 @@ # Imports # -from pyomo.core import * +from pyomo.core import (AbstractModel, Set, Param, Var, sum_product, + PositiveReals, NonNegativeReals, Constraint, + Objective, Expression, minimize) # # Model diff --git a/pyomo/bilevel/__init__.py b/pyomo/bilevel/__init__.py index 9259b8a1e6f..12986cf5f43 100644 --- a/pyomo/bilevel/__init__.py +++ b/pyomo/bilevel/__init__.py @@ -10,4 +10,5 @@ # Package files -from pyomo.bilevel.components import * +from pyomo.bilevel.components import (SubModel, ModelComponentFactory, + Component, SimpleBlock) diff --git a/pyomo/bilevel/plugins/lcp.py b/pyomo/bilevel/plugins/lcp.py index 158ebbb3174..11ab41e9e1c 100644 --- a/pyomo/bilevel/plugins/lcp.py +++ b/pyomo/bilevel/plugins/lcp.py @@ -15,7 +15,6 @@ from pyomo.repn import generate_standard_repn from pyomo.mpec import ComplementarityList, complements from pyomo.bilevel.plugins.transform import Base_BilevelTransformation -from pyomo.bilevel.components import SubModel logger = logging.getLogger('pyomo.core') diff --git a/pyomo/bilevel/plugins/solver1.py b/pyomo/bilevel/plugins/solver1.py index 7e77fa6b849..b6bab05da8d 100644 --- a/pyomo/bilevel/plugins/solver1.py +++ b/pyomo/bilevel/plugins/solver1.py @@ -12,7 +12,6 @@ import pyutilib.misc from pyomo.core import TransformationFactory, Var, ComponentUID, Block, Objective, Set import pyomo.opt -from pyomo.bilevel.components import SubModel import pyomo.common diff --git a/pyomo/bilevel/plugins/solver2.py b/pyomo/bilevel/plugins/solver2.py index 6a80ad633cd..86a2592b4c2 100644 --- a/pyomo/bilevel/plugins/solver2.py +++ b/pyomo/bilevel/plugins/solver2.py @@ -116,12 +116,6 @@ def _postsolve(self): prob.number_of_continuous_variables = self._instance.statistics.number_of_continuous_variables prob.number_of_objectives = self._instance.statistics.number_of_objectives # - from pyomo.core import maximize - ##if self._instance.sense == maximize: - ##prob.sense = pyomo.opt.ProblemSense.maximize - ##else: - ##prob.sense = pyomo.opt.ProblemSense.minimize - # # SOLUTION(S) # self._instance.solutions.store_to(results) diff --git a/pyomo/bilevel/plugins/solver3.py b/pyomo/bilevel/plugins/solver3.py index 21e5912c198..8442540094e 100644 --- a/pyomo/bilevel/plugins/solver3.py +++ b/pyomo/bilevel/plugins/solver3.py @@ -118,12 +118,6 @@ def _postsolve(self): prob.number_of_continuous_variables = self._instance.statistics.number_of_continuous_variables prob.number_of_objectives = self._instance.statistics.number_of_objectives # - from pyomo.core import maximize - ##if self._instance.sense == maximize: - ##prob.sense = pyomo.opt.ProblemSense.maximize - ##else: - ##prob.sense = pyomo.opt.ProblemSense.minimize - # # SOLUTION(S) # self._instance.solutions.store_to(results) diff --git a/pyomo/bilevel/tests/test_blp.py b/pyomo/bilevel/tests/test_blp.py index 6dd2ace5a74..c0f68b3331a 100644 --- a/pyomo/bilevel/tests/test_blp.py +++ b/pyomo/bilevel/tests/test_blp.py @@ -12,7 +12,6 @@ # Test transformations for bilevel linear programs # -import sys import os from os.path import abspath, dirname, normpath, join currdir = dirname(abspath(__file__)) @@ -25,7 +24,7 @@ import pyomo.opt import pyomo.scripting.pyomo_main as pyomo_main from pyomo.scripting.util import cleanup -from pyomo.environ import * +from pyomo.environ import TransformationFactory from six import iteritems diff --git a/pyomo/bilevel/tests/test_linear_dual.py b/pyomo/bilevel/tests/test_linear_dual.py index 35b29a0ccfd..05c0dc425a3 100644 --- a/pyomo/bilevel/tests/test_linear_dual.py +++ b/pyomo/bilevel/tests/test_linear_dual.py @@ -15,7 +15,7 @@ import os from os.path import abspath, dirname, normpath, join currdir = dirname(abspath(__file__)) -exdir = normpath(join(currdir,'..','..','..','examples','bilevel')) +exdir = normpath(join(currdir, '..', '..', '..', 'examples', 'bilevel')) import pyutilib.th as unittest @@ -23,12 +23,13 @@ import pyomo.opt import pyomo.scripting.pyomo_main as pyomo_main from pyomo.scripting.util import cleanup -from pyomo.environ import * +import pyomo.environ from six import iteritems solvers = pyomo.opt.check_available_solvers('cplex', 'glpk') + class CommonTests: solve = True @@ -37,7 +38,7 @@ def run_bilevel(self, *_args, **kwds): if self.solve: args = ['solve'] if 'solver' in kwds: - _solver = kwds.get('solver','glpk') + _solver = kwds.get('solver', 'glpk') args.append('--solver=bilevel_ld') args.append('--solver-options="solver=%s"' % _solver) args.append('--save-results=result.yml') @@ -45,7 +46,7 @@ def run_bilevel(self, *_args, **kwds): else: args = ['convert'] if 'preprocess' in kwds: - #args.append('--solver=glpk') + # args.append('--solver=glpk') pp = kwds['preprocess'] if pp == 'linear_dual': args.append('--transform=bilevel.linear_dual') @@ -55,8 +56,8 @@ def run_bilevel(self, *_args, **kwds): # which now causes a helpful error message. # I've manually inserted them into those tests that need them to pass # (which is where they also get used) - #args.append('--symbolic-solver-labels') - #args.append('--file-determinism=2') + # args.append('--symbolic-solver-labels') + # args.append('--file-determinism=2') if False: args.append('--stream-solver') @@ -68,7 +69,7 @@ def run_bilevel(self, *_args, **kwds): os.chdir(currdir) print('***') - #print(' '.join(args)) + # print(' '.join(args)) try: output = pyomo_main.main(args) except SystemExit: @@ -99,17 +100,17 @@ def getObjective(self, fname): def updateDocStrings(self): for key in dir(self): if key.startswith('test'): - getattr(self,key).__doc__ = " (%s)" % getattr(self,key).__name__ + getattr(self, key).__doc__ = " (%s)" % getattr(self, key).__name__ def test_t5(self): - self.problem='test_t5' - self.run_bilevel( join(exdir,'t5.py')) - self.check( 't5', 'linear_dual' ) + self.problem = 'test_t5' + self.run_bilevel(join(exdir, 't5.py')) + self.check('t5', 'linear_dual') def test_t1(self): - self.problem='test_t1' - self.run_bilevel( join(exdir,'t1.py')) - self.check( 't1', 'linear_dual' ) + self.problem = 'test_t1' + self.run_bilevel(join(exdir, 't1.py')) + self.check('t1', 'linear_dual') def Xtest_t2(self): self.problem='test_t2' @@ -122,8 +123,8 @@ class Reformulate(unittest.TestCase, CommonTests): solve = False def tearDown(self): - if os.path.exists(os.path.join(currdir,'result.yml')): - os.remove(os.path.join(currdir,'result.yml')) + if os.path.exists(os.path.join(currdir, 'result.yml')): + os.remove(os.path.join(currdir, 'result.yml')) def run_bilevel(self, *args, **kwds): args = list(args) diff --git a/pyomo/checker/__init__.py b/pyomo/checker/__init__.py index 4ef9ae55172..70c13420d65 100644 --- a/pyomo/checker/__init__.py +++ b/pyomo/checker/__init__.py @@ -11,18 +11,16 @@ from pyomo.common.plugin import PluginGlobals PluginGlobals.add_env("pyomo") -from pyomo.checker.checker import * -from pyomo.checker.runner import * -from pyomo.checker.script import * -from pyomo.checker.hooks import * +from pyomo.checker.checker import Interface, IModelChecker +from pyomo.checker.runner import ExtensionPoint, CheckingNodeVisitor, ModelCheckRunner +from pyomo.checker.script import ModelScript +from pyomo.checker.hooks import IPreCheckHook, IPostCheckHook # Modules __all__ = [] # Checker classes __all__.extend(['IModelChecker']) -#__all__.extend(['ImmediateDataChecker', 'IterativeDataChecker']) -#__all__.extend(['ImmediateTreeChecker', 'IterativeTreeChecker']) # Other builtins __all__.extend(['ModelCheckRunner', 'ModelScript']) diff --git a/pyomo/checker/checker.py b/pyomo/checker/checker.py index 18e1b9aede7..9ac55250c75 100644 --- a/pyomo/checker/checker.py +++ b/pyomo/checker/checker.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.common.plugin import * +from pyomo.common.plugin import Interface class IModelChecker(Interface): diff --git a/pyomo/checker/hooks.py b/pyomo/checker/hooks.py index 03248d85f79..d80d31b7cab 100644 --- a/pyomo/checker/hooks.py +++ b/pyomo/checker/hooks.py @@ -10,7 +10,7 @@ __all__ = ['IPreCheckHook', 'IPostCheckHook'] -from pyomo.common.plugin import * +from pyomo.common.plugin import Interface class IPreCheckHook(Interface): diff --git a/pyomo/checker/plugins/checker.py b/pyomo/checker/plugins/checker.py index 70e69a00e5d..7694b40aefe 100644 --- a/pyomo/checker/plugins/checker.py +++ b/pyomo/checker/plugins/checker.py @@ -12,8 +12,8 @@ import re import textwrap -from pyomo.common.plugin import * -from pyomo.checker import * +from pyomo.common.plugin import SingletonPlugin, implements, ExtensionPoint +from pyomo.checker import IModelChecker, IPreCheckHook, IPostCheckHook class PyomoModelChecker(SingletonPlugin): diff --git a/pyomo/checker/plugins/function.py b/pyomo/checker/plugins/function.py index a6367a4f3f5..038844a45f4 100644 --- a/pyomo/checker/plugins/function.py +++ b/pyomo/checker/plugins/function.py @@ -10,8 +10,8 @@ import ast -from pyomo.common.plugin import * -from pyomo.checker.hooks import * +from pyomo.common.plugin import SingletonPlugin, implements +from pyomo.checker.hooks import IPreCheckHook, IPostCheckHook class FunctionTrackerHook(SingletonPlugin): diff --git a/pyomo/checker/plugins/model.py b/pyomo/checker/plugins/model.py index 8edfa00baa0..33feae5ec77 100644 --- a/pyomo/checker/plugins/model.py +++ b/pyomo/checker/plugins/model.py @@ -10,8 +10,8 @@ import ast -from pyomo.common.plugin import * -from pyomo.checker.hooks import * +from pyomo.common.plugin import SingletonPlugin, implements +from pyomo.checker.hooks import IPreCheckHook class ModelTrackerHook(SingletonPlugin): diff --git a/pyomo/checker/runner.py b/pyomo/checker/runner.py index 97ce4758465..08ad9288a4d 100644 --- a/pyomo/checker/runner.py +++ b/pyomo/checker/runner.py @@ -10,7 +10,8 @@ import ast -from pyomo.checker.checker import * +from pyomo.common.plugin import ExtensionPoint +from pyomo.checker.checker import IModelChecker from pyomo.checker.script import ModelScript diff --git a/pyomo/checker/tests/examples/model/ArrayValue_nomodel.py b/pyomo/checker/tests/examples/model/ArrayValue_nomodel.py index 3f223d78b7e..7add85414fe 100644 --- a/pyomo/checker/tests/examples/model/ArrayValue_nomodel.py +++ b/pyomo/checker/tests/examples/model/ArrayValue_nomodel.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import Var class Foo: pass diff --git a/pyomo/checker/tests/examples/model/ArrayValue_varonly.py b/pyomo/checker/tests/examples/model/ArrayValue_varonly.py index 31ff0ff5cdd..b8bff298df6 100644 --- a/pyomo/checker/tests/examples/model/ArrayValue_varonly.py +++ b/pyomo/checker/tests/examples/model/ArrayValue_varonly.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, NonNegativeReals model = AbstractModel() model.w = Var(within=NonNegativeReals) diff --git a/pyomo/checker/tests/examples/model/ArrayValue_wrong.py b/pyomo/checker/tests/examples/model/ArrayValue_wrong.py index 8b3099759d3..764cf553d67 100644 --- a/pyomo/checker/tests/examples/model/ArrayValue_wrong.py +++ b/pyomo/checker/tests/examples/model/ArrayValue_wrong.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, RangeSet model = AbstractModel() model.y = Var([10]) diff --git a/pyomo/checker/tests/examples/model/Imports_wrong.py b/pyomo/checker/tests/examples/model/Imports_wrong.py index 10366125b20..1baa986d60e 100644 --- a/pyomo/checker/tests/examples/model/Imports_wrong.py +++ b/pyomo/checker/tests/examples/model/Imports_wrong.py @@ -8,4 +8,3 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from os import * diff --git a/pyomo/checker/tests/examples/model/ModelAccess_global.py b/pyomo/checker/tests/examples/model/ModelAccess_global.py index 0f28c3a1992..7d45d172d3e 100644 --- a/pyomo/checker/tests/examples/model/ModelAccess_global.py +++ b/pyomo/checker/tests/examples/model/ModelAccess_global.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Constraint model = ConcreteModel() model.X = Var() diff --git a/pyomo/checker/tests/examples/model/ModelAccess_global2.py b/pyomo/checker/tests/examples/model/ModelAccess_global2.py index 24398f14d2c..6f25e2b730a 100644 --- a/pyomo/checker/tests/examples/model/ModelAccess_global2.py +++ b/pyomo/checker/tests/examples/model/ModelAccess_global2.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Constraint model = ConcreteModel() model.X = Var() diff --git a/pyomo/checker/tests/examples/model/ModelArgument_firstarg.py b/pyomo/checker/tests/examples/model/ModelArgument_firstarg.py index 80e9ba25cab..1a9bcc91b02 100644 --- a/pyomo/checker/tests/examples/model/ModelArgument_firstarg.py +++ b/pyomo/checker/tests/examples/model/ModelArgument_firstarg.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, RangeSet, Var, Constraint model = ConcreteModel() model.S = RangeSet(10) diff --git a/pyomo/checker/tests/examples/model/ModelArgument_lastarg.py b/pyomo/checker/tests/examples/model/ModelArgument_lastarg.py index 22b5bc93ab3..66394ea813f 100644 --- a/pyomo/checker/tests/examples/model/ModelArgument_lastarg.py +++ b/pyomo/checker/tests/examples/model/ModelArgument_lastarg.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, RangeSet, Var, Constraint model = ConcreteModel() model.S = RangeSet(10) diff --git a/pyomo/checker/tests/examples/model/ModelArgument_norule.py b/pyomo/checker/tests/examples/model/ModelArgument_norule.py index 5eb5f32677b..85c7c52df96 100644 --- a/pyomo/checker/tests/examples/model/ModelArgument_norule.py +++ b/pyomo/checker/tests/examples/model/ModelArgument_norule.py @@ -8,7 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * def myAdd(a, b): return a.val + b.val diff --git a/pyomo/checker/tests/examples/model/ModelCreate_nocall.py b/pyomo/checker/tests/examples/model/ModelCreate_nocall.py index 60c82d1a5db..eb1fccd0a6b 100644 --- a/pyomo/checker/tests/examples/model/ModelCreate_nocall.py +++ b/pyomo/checker/tests/examples/model/ModelCreate_nocall.py @@ -8,6 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel model = AbstractModel diff --git a/pyomo/checker/tests/examples/model/ModelValue_globalif.py b/pyomo/checker/tests/examples/model/ModelValue_globalif.py index 219452070b3..40636dc750d 100644 --- a/pyomo/checker/tests/examples/model/ModelValue_globalif.py +++ b/pyomo/checker/tests/examples/model/ModelValue_globalif.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, value model = AbstractModel() model.X = Var() diff --git a/pyomo/checker/tests/examples/model/ModelValue_globallistcomp.py b/pyomo/checker/tests/examples/model/ModelValue_globallistcomp.py index 0506e224a31..52ec14ddefd 100644 --- a/pyomo/checker/tests/examples/model/ModelValue_globallistcomp.py +++ b/pyomo/checker/tests/examples/model/ModelValue_globallistcomp.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, RangeSet, Var, value model = AbstractModel() model.S = RangeSet(10) diff --git a/pyomo/checker/tests/examples/model/ModelValue_multiif.py b/pyomo/checker/tests/examples/model/ModelValue_multiif.py index d3c798fa578..1c80615588a 100644 --- a/pyomo/checker/tests/examples/model/ModelValue_multiif.py +++ b/pyomo/checker/tests/examples/model/ModelValue_multiif.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, Constraint, value model = AbstractModel() model.X = Var() diff --git a/pyomo/checker/tests/examples/model/ModelValue_repeatif.py b/pyomo/checker/tests/examples/model/ModelValue_repeatif.py index 2e1340f129a..6fc8e16d1ee 100644 --- a/pyomo/checker/tests/examples/model/ModelValue_repeatif.py +++ b/pyomo/checker/tests/examples/model/ModelValue_repeatif.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, value model = AbstractModel() model.X = Var() diff --git a/pyomo/checker/tests/examples/model/ModelValue_ruleif.py b/pyomo/checker/tests/examples/model/ModelValue_ruleif.py index 3ec99c2171e..9e2a51eacf3 100644 --- a/pyomo/checker/tests/examples/model/ModelValue_ruleif.py +++ b/pyomo/checker/tests/examples/model/ModelValue_ruleif.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, Constraint, value model = AbstractModel() model.X = Var() diff --git a/pyomo/checker/tests/examples/model/ModelValue_rulelistcomp.py b/pyomo/checker/tests/examples/model/ModelValue_rulelistcomp.py index febe1debaae..3810afee97c 100644 --- a/pyomo/checker/tests/examples/model/ModelValue_rulelistcomp.py +++ b/pyomo/checker/tests/examples/model/ModelValue_rulelistcomp.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, RangeSet, Var, Constraint, value model = AbstractModel() model.S = RangeSet(10) diff --git a/pyomo/checker/tests/examples/model/NoneReturn_wrong.py b/pyomo/checker/tests/examples/model/NoneReturn_wrong.py index 7dfac33eb75..fcdb3de0b12 100644 --- a/pyomo/checker/tests/examples/model/NoneReturn_wrong.py +++ b/pyomo/checker/tests/examples/model/NoneReturn_wrong.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, Constraint model = AbstractModel() model.X = Var() diff --git a/pyomo/checker/tests/examples/py3k/XRange_1.py b/pyomo/checker/tests/examples/py3k/XRange_1.py index 490cd315eef..318857a752f 100644 --- a/pyomo/checker/tests/examples/py3k/XRange_1.py +++ b/pyomo/checker/tests/examples/py3k/XRange_1.py @@ -8,4 +8,5 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + xrange(10) diff --git a/pyomo/checker/tests/test_examples.py b/pyomo/checker/tests/test_examples.py index 4f697ed45b9..f56177f7007 100644 --- a/pyomo/checker/tests/test_examples.py +++ b/pyomo/checker/tests/test_examples.py @@ -13,7 +13,7 @@ import pyutilib.th as unittest -from pyomo.checker import * +from pyomo.checker import ModelCheckRunner from pyomo.checker.plugins.checker import PyomoModelChecker from pyomo.common.dependencies import yaml, yaml_available, yaml_load_args @@ -24,7 +24,6 @@ def createTestMethod(defs, package, checkerName, key): def testMethod(obj, name): import pyomo.environ - runner = ModelCheckRunner() path = os.path.join(exdir, package, "{0}_{1}.py".format(checkerName, key)) runner.run(script = path, checkers = {package:[checkerName]}) diff --git a/pyomo/checker/tests/test_runner.py b/pyomo/checker/tests/test_runner.py index b06ce3bc08d..33ecccbace0 100644 --- a/pyomo/checker/tests/test_runner.py +++ b/pyomo/checker/tests/test_runner.py @@ -12,8 +12,8 @@ import pyutilib.th as unittest -from pyomo.checker import * -from pyomo.checker.plugins.checker import * +from pyomo.checker import ModelCheckRunner, ModelScript +from pyomo.checker.plugins.checker import IModelChecker, ImmediateDataChecker, ImmediateTreeChecker, IterativeDataChecker, IterativeTreeChecker currdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/checker/tests/test_script.py b/pyomo/checker/tests/test_script.py index 601af2ff845..12681f07424 100644 --- a/pyomo/checker/tests/test_script.py +++ b/pyomo/checker/tests/test_script.py @@ -13,7 +13,7 @@ import pyutilib.th as unittest -from pyomo.checker import * +from pyomo.checker import ModelScript currdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/common/dependencies.py b/pyomo/common/dependencies.py index 37beece199f..1c32d324134 100644 --- a/pyomo/common/dependencies.py +++ b/pyomo/common/dependencies.py @@ -162,7 +162,14 @@ def __nonzero__(self): return bool(self._a) or bool(self._b) -def _check_version(module, min_version): +def check_min_version(module, min_version): + if isinstance(module, DeferredImportModule): + indicator = module._indicator_flag + indicator.resolve() + if indicator._available: + module = indicator._module + else: + return False try: from packaging import version as _version _parser = _version.parse @@ -275,7 +282,8 @@ def attempt_import(name, error_message=None, only_catch_importerror=True, module = importlib.import_module(name) else: module = importer() - if minimum_version is None or _check_version(module, minimum_version): + if ( minimum_version is None + or check_min_version(module, minimum_version) ): if callback is not None: callback(module, True) return module, True diff --git a/pyomo/common/deprecation.py b/pyomo/common/deprecation.py index 4ad44dc4a99..5a87756067d 100644 --- a/pyomo/common/deprecation.py +++ b/pyomo/common/deprecation.py @@ -76,7 +76,7 @@ def deprecated(msg=None, logger='pyomo.core', version=None, remove_in=None): removed from the code. """ - if version is None: # or version in ('','tbd','TBD'): + if version is None: # or version in ('','tbd','TBD'): raise DeveloperError("@deprecated missing initial version") def wrap(func): diff --git a/pyomo/common/download.py b/pyomo/common/download.py index 12459114a1b..391d83c3593 100644 --- a/pyomo/common/download.py +++ b/pyomo/common/download.py @@ -333,7 +333,6 @@ def get_zip_archive(self, url, dirOffset=0): % (self._fname,)) zip_file = zipfile.ZipFile(io.BytesIO(self.retrieve_url(url))) # Simple sanity checks - names = [] for info in zip_file.infolist(): f = info.filename if f[0] in '\\/' or '..' in f: diff --git a/pyomo/common/env.py b/pyomo/common/env.py index 894f5a04d5b..b0b64e04680 100644 --- a/pyomo/common/env.py +++ b/pyomo/common/env.py @@ -42,12 +42,12 @@ def _attempt_ctypes_cdll(name): return False -def _load_dll(name, timeout=1): +def _load_dll(name, timeout=10): """Load a DLL with a timeout On some platforms and some DLLs (notably Windows GitHub Actions with Python 3.5, 3.6, and 3.7 and the msvcr90.dll) we have observed - behavior where the ctypes.CDLL() call hangs indefinitely. This uses + behavior where the ctypes.CDLL() call hangs indefinitely. This uses multiprocessing to attempt the import in a subprocess (with a timeout) and then only calls the import in the main process if the subprocess succeeded. @@ -55,9 +55,14 @@ def _load_dll(name, timeout=1): Performance note: CtypesEnviron only ever attempts to load a DLL once (the DLL reference is then held in a class attribute), and this interface only spawns the subprocess if ctypes.util.find_library - actually locates the target library. This will have a measurable + actually locates the target library. This will have a measurable impact on Windows (where the DLLs exist), but not on other platforms. + The default timeout of 10 is arbitrary. For simple situations, 1 + seems adequate. However, more complex examples have been observed + that needed timeout==5. Using a default of 10 is simply doubling + that observed case. + """ if not ctypes.util.find_library(name): return False, None diff --git a/pyomo/common/tests/test_config.py b/pyomo/common/tests/test_config.py index e21b6856d29..0def4dda08f 100644 --- a/pyomo/common/tests/test_config.py +++ b/pyomo/common/tests/test_config.py @@ -12,7 +12,7 @@ import pyutilib.th as unittest from pyomo.common.config import ( - ConfigBlock, ConfigList, ConfigValue, + ConfigBlock, ConfigValue, PositiveInt, NegativeInt, NonPositiveInt, NonNegativeInt, PositiveFloat, NegativeFloat, NonPositiveFloat, NonNegativeFloat, In, Path, PathList, ConfigEnum diff --git a/pyomo/common/tests/test_dependencies.py b/pyomo/common/tests/test_dependencies.py index 55e1d524c08..5d64042990e 100644 --- a/pyomo/common/tests/test_dependencies.py +++ b/pyomo/common/tests/test_dependencies.py @@ -17,12 +17,11 @@ from pyomo.common.dependencies import ( attempt_import, ModuleUnavailable, DeferredImportModule, DeferredImportIndicator, DeferredImportError, - _DeferredAnd, _DeferredOr + _DeferredAnd, _DeferredOr, check_min_version ) import pyomo.common.tests.dep_mod as dep_mod from pyomo.common.tests.dep_mod import ( - numpy, numpy_available, bogus_nonexisting_module as bogus_nem, bogus_nonexisting_module_available as has_bogus_nem, ) @@ -79,6 +78,8 @@ def test_min_version(self): defer_check=False) self.assertTrue(avail) self.assertTrue(inspect.ismodule(mod)) + self.assertTrue(check_min_version(mod, '1.0')) + self.assertFalse(check_min_version(mod, '2.0')) mod, avail = attempt_import('pyomo.common.tests.dep_mod', minimum_version='2.0', @@ -101,6 +102,16 @@ def test_min_version(self): "\(version 1.5 does not satisfy the minimum version 2.0\)"): mod.hello + # Verify check_min_version works with deferred imports + + mod, avail = attempt_import('pyomo.common.tests.dep_mod', + defer_check=True) + self.assertTrue(check_min_version(mod, '1.0')) + + mod, avail = attempt_import('pyomo.common.tests.dep_mod', + defer_check=True) + self.assertFalse(check_min_version(mod, '2.0')) + def test_and_or(self): mod0, avail0 = attempt_import('pyutilib', defer_check=True) diff --git a/pyomo/common/tests/test_deprecated.py b/pyomo/common/tests/test_deprecated.py index 83a4f8e1332..7a46f5ae592 100644 --- a/pyomo/common/tests/test_deprecated.py +++ b/pyomo/common/tests/test_deprecated.py @@ -43,7 +43,7 @@ def test_no_doc_string(self): # Note: No docstring, else nose replaces the function name with # the docstring in output. #"""Test for deprecated function decorator.""" - @deprecated(version='') + @deprecated(version='test') def foo(bar='yeah'): logger.warning(bar) @@ -79,7 +79,7 @@ def foo(bar='yeah'): def test_with_doc_string(self): - @deprecated(version='') + @deprecated(version='test') def foo(bar='yeah'): """Show that I am a good person. @@ -121,7 +121,7 @@ def foo(bar='yeah'): def test_with_custom_message(self): - @deprecated('This is a custom message, too.', version='') + @deprecated('This is a custom message, too.', version='test') def foo(bar='yeah'): """Show that I am a good person. @@ -163,7 +163,7 @@ def foo(bar='yeah'): def test_with_custom_logger(self): @deprecated('This is a custom message', logger='pyomo.common', - version='') + version='test') def foo(bar='yeah'): """Show that I am a good person. @@ -205,7 +205,7 @@ def foo(bar='yeah'): self.assertNotIn('DEPRECATED:', DEP_OUT.getvalue()) def test_with_class(self): - @deprecated(version='') + @deprecated(version='test') class foo(object): def __init__(self): logger.warning('yeah') @@ -231,7 +231,7 @@ def test_with_method(self): class foo(object): def __init__(self): pass - @deprecated(version='') + @deprecated(version='test') def bar(self): logger.warning('yeah') diff --git a/pyomo/common/tests/test_download.py b/pyomo/common/tests/test_download.py index abdb275a93e..1d38ea103b1 100644 --- a/pyomo/common/tests/test_download.py +++ b/pyomo/common/tests/test_download.py @@ -8,7 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import io import os import platform import re diff --git a/pyomo/common/tests/test_task.py b/pyomo/common/tests/test_task.py index 3574746da64..a74e8b01435 100644 --- a/pyomo/common/tests/test_task.py +++ b/pyomo/common/tests/test_task.py @@ -9,9 +9,8 @@ # ___________________________________________________________________________ import pyutilib.th as unittest -import pyutilib.misc -from pyomo.common import * +from pyomo.common import PyomoAPIData, pyomo_api, PyomoAPIFactory from pyomo.common.log import LoggingIntercept from six import StringIO diff --git a/pyomo/contrib/benders/benders_cuts.py b/pyomo/contrib/benders/benders_cuts.py index 205d10f65b5..91ea37f9bcc 100644 --- a/pyomo/contrib/benders/benders_cuts.py +++ b/pyomo/contrib/benders/benders_cuts.py @@ -1,5 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.core.base.block import _BlockData, declare_custom_block -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver from pyomo.core.expr.visitor import identify_variables from pyomo.common.collections import ComponentSet @@ -103,35 +113,35 @@ def _setup_subproblem(b, master_vars, relax_subproblem_cons): # first get the objective and turn it into a constraint master_vars = ComponentSet(master_vars) - objs = list(b.component_data_objects(pe.Objective, descend_into=False, active=True)) + objs = list(b.component_data_objects(pyo.Objective, descend_into=False, active=True)) if len(objs) != 1: raise ValueError('Subproblem must have exactly one objective') orig_obj = objs[0] orig_obj_expr = orig_obj.expr b.del_component(orig_obj) - b._z = pe.Var(bounds=(0, None)) - b.objective = pe.Objective(expr=b._z) - b.dual = pe.Suffix(direction=pe.Suffix.IMPORT) - b._eta = pe.Var() + b._z = pyo.Var(bounds=(0, None)) + b.objective = pyo.Objective(expr=b._z) + b.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT) + b._eta = pyo.Var() - b.aux_cons = pe.ConstraintList() - for c in list(b.component_data_objects(pe.Constraint, descend_into=True, active=True, sort=True)): + b.aux_cons = pyo.ConstraintList() + for c in list(b.component_data_objects(pyo.Constraint, descend_into=True, active=True, sort=True)): if not relax_subproblem_cons: c_vars = ComponentSet(identify_variables(c.body, include_fixed=False)) if not _any_common_elements(master_vars, c_vars): continue if c.equality: body = c.body - rhs = pe.value(c.lower) + rhs = pyo.value(c.lower) body -= rhs b.aux_cons.add(body - b._z <= 0) b.aux_cons.add(-body - b._z <= 0) _del_con(c) else: body = c.body - lower = pe.value(c.lower) - upper = pe.value(c.upper) + lower = pyo.value(c.lower) + upper = pyo.value(c.upper) if upper is not None: body_upper = body - upper - b._z b.aux_cons.add(body_upper <= 0) @@ -142,7 +152,7 @@ def _setup_subproblem(b, master_vars, relax_subproblem_cons): b.aux_cons.add(body_lower <= 0) _del_con(c) - b.obj_con = pe.Constraint(expr=orig_obj_expr - b._eta - b._z <= 0) + b.obj_con = pyo.Constraint(expr=orig_obj_expr - b._eta - b._z <= 0) @declare_custom_block(name='BendersCutGenerator') @@ -158,7 +168,7 @@ def __init__(self, component): self.subproblems = list() self.complicating_vars_maps = list() self.master_vars = list() - self.master_vars_indices = pe.ComponentMap() + self.master_vars_indices = pyo.ComponentMap() self.master_etas = list() self.cuts = None self.subproblem_solvers = list() @@ -190,12 +200,12 @@ def set_input(self, master_vars, tol=1e-6, comm = None): self.comm = MPI.COMM_WORLD self.num_subproblems_by_rank = np.zeros(self.comm.Get_size()) del self.cuts - self.cuts = pe.ConstraintList() + self.cuts = pyo.ConstraintList() self.subproblems = list() self.master_etas = list() self.complicating_vars_maps = list() self.master_vars = list(master_vars) - self.master_vars_indices = pe.ComponentMap() + self.master_vars_indices = pyo.ComponentMap() for i, v in enumerate(self.master_vars): self.master_vars_indices[v] = i self.tol = tol @@ -216,7 +226,7 @@ def add_subproblem(self, subproblem_fn, subproblem_fn_kwargs, master_eta, subpro self._subproblem_ndx_map[len(self.subproblems) - 1] = self.global_num_subproblems() - 1 if isinstance(subproblem_solver, str): - subproblem_solver = pe.SolverFactory(subproblem_solver) + subproblem_solver = pyo.SolverFactory(subproblem_solver) self.subproblem_solvers.append(subproblem_solver) if isinstance(subproblem_solver, PersistentSolver): subproblem_solver.set_instance(subproblem) @@ -233,15 +243,15 @@ def generate_cut(self): master_eta = self.master_etas[local_subproblem_ndx] coeff_ndx = global_subproblem_ndx * len(self.master_vars) - subproblem.fix_complicating_vars = pe.ConstraintList() - var_to_con_map = pe.ComponentMap() + subproblem.fix_complicating_vars = pyo.ConstraintList() + var_to_con_map = pyo.ComponentMap() for master_var in self.master_vars: if master_var in complicating_vars_map: sub_var = complicating_vars_map[master_var] sub_var.value = master_var.value new_con = subproblem.fix_complicating_vars.add(sub_var - master_var.value == 0) var_to_con_map[master_var] = new_con - subproblem.fix_eta = pe.Constraint(expr=subproblem._eta - master_eta.value == 0) + subproblem.fix_eta = pyo.Constraint(expr=subproblem._eta - master_eta.value == 0) subproblem._eta.value = master_eta.value subproblem_solver = self.subproblem_solvers[local_subproblem_ndx] @@ -254,22 +264,22 @@ def generate_cut(self): subproblem_solver.add_constraint(c) subproblem_solver.add_constraint(subproblem.fix_eta) res = subproblem_solver.solve(tee=False, load_solutions=False, save_results=False) - if res.solver.termination_condition != pe.TerminationCondition.optimal: + if res.solver.termination_condition != pyo.TerminationCondition.optimal: raise RuntimeError('Unable to generate cut because subproblem failed to converge.') subproblem_solver.load_vars() subproblem_solver.load_duals() else: res = subproblem_solver.solve(subproblem, tee=False, load_solutions=False) - if res.solver.termination_condition != pe.TerminationCondition.optimal: + if res.solver.termination_condition != pyo.TerminationCondition.optimal: raise RuntimeError('Unable to generate cut because subproblem failed to converge.') subproblem.solutions.load_from(res) - constants[global_subproblem_ndx] = pe.value(subproblem._z) - eta_coeffs[global_subproblem_ndx] = sign_convention * pe.value(subproblem.dual[subproblem.obj_con]) + constants[global_subproblem_ndx] = pyo.value(subproblem._z) + eta_coeffs[global_subproblem_ndx] = sign_convention * pyo.value(subproblem.dual[subproblem.obj_con]) for master_var in self.master_vars: if master_var in complicating_vars_map: c = var_to_con_map[master_var] - coefficients[coeff_ndx] = sign_convention * pe.value(subproblem.dual[c]) + coefficients[coeff_ndx] = sign_convention * pyo.value(subproblem.dual[c]) coeff_ndx += 1 if isinstance(subproblem_solver, PersistentSolver): diff --git a/pyomo/contrib/benders/examples/farmer.py b/pyomo/contrib/benders/examples/farmer.py index 3fd402b2cae..925b66defd0 100644 --- a/pyomo/contrib/benders/examples/farmer.py +++ b/pyomo/contrib/benders/examples/farmer.py @@ -1,5 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.contrib.benders.benders_cuts import BendersCutGenerator -import pyomo.environ as pe +import pyomo.environ as pyo import time from mpi4py import MPI import sys @@ -35,51 +45,51 @@ def __init__(self): def create_master(farmer): - m = pe.ConcreteModel() + m = pyo.ConcreteModel() - m.crops = pe.Set(initialize=farmer.crops, ordered=True) - m.scenarios = pe.Set(initialize=farmer.scenarios, ordered=True) + m.crops = pyo.Set(initialize=farmer.crops, ordered=True) + m.scenarios = pyo.Set(initialize=farmer.scenarios, ordered=True) - m.devoted_acreage = pe.Var(m.crops, bounds=(0, farmer.total_acreage)) - m.eta = pe.Var(m.scenarios) + m.devoted_acreage = pyo.Var(m.crops, bounds=(0, farmer.total_acreage)) + m.eta = pyo.Var(m.scenarios) for s in m.scenarios: m.eta[s].setlb(-432000 * farmer.scenario_probabilities[s]) - m.total_acreage_con = pe.Constraint(expr=sum(m.devoted_acreage.values()) <= farmer.total_acreage) + m.total_acreage_con = pyo.Constraint(expr=sum(m.devoted_acreage.values()) <= farmer.total_acreage) - m.obj = pe.Objective(expr=sum(farmer.PlantingCostPerAcre[crop] * m.devoted_acreage[crop] for crop in m.crops) + sum(m.eta.values())) + m.obj = pyo.Objective(expr=sum(farmer.PlantingCostPerAcre[crop] * m.devoted_acreage[crop] for crop in m.crops) + sum(m.eta.values())) return m def create_subproblem(master, farmer, scenario): - m = pe.ConcreteModel() + m = pyo.ConcreteModel() - m.crops = pe.Set(initialize=farmer.crops, ordered=True) + m.crops = pyo.Set(initialize=farmer.crops, ordered=True) - m.devoted_acreage = pe.Var(m.crops) - m.QuantitySubQuotaSold = pe.Var(m.crops, bounds=(0.0, None)) - m.QuantitySuperQuotaSold = pe.Var(m.crops, bounds=(0.0, None)) - m.QuantityPurchased = pe.Var(m.crops, bounds=(0.0, None)) + m.devoted_acreage = pyo.Var(m.crops) + m.QuantitySubQuotaSold = pyo.Var(m.crops, bounds=(0.0, None)) + m.QuantitySuperQuotaSold = pyo.Var(m.crops, bounds=(0.0, None)) + m.QuantityPurchased = pyo.Var(m.crops, bounds=(0.0, None)) def EnforceCattleFeedRequirement_rule(m, i): return (farmer.CattleFeedRequirement[i] <= (farmer.crop_yield[scenario][i] * m.devoted_acreage[i]) + m.QuantityPurchased[i] - m.QuantitySubQuotaSold[i] - m.QuantitySuperQuotaSold[i]) - m.EnforceCattleFeedRequirement = pe.Constraint(m.crops, rule=EnforceCattleFeedRequirement_rule) + m.EnforceCattleFeedRequirement = pyo.Constraint(m.crops, rule=EnforceCattleFeedRequirement_rule) def LimitAmountSold_rule(m, i): return m.QuantitySubQuotaSold[i] + m.QuantitySuperQuotaSold[i] - (farmer.crop_yield[scenario][i] * m.devoted_acreage[i]) <= 0.0 - m.LimitAmountSold = pe.Constraint(m.crops, rule=LimitAmountSold_rule) + m.LimitAmountSold = pyo.Constraint(m.crops, rule=LimitAmountSold_rule) def EnforceQuotas_rule(m, i): return (0.0, m.QuantitySubQuotaSold[i], farmer.PriceQuota[i]) - m.EnforceQuotas = pe.Constraint(m.crops, rule=EnforceQuotas_rule) + m.EnforceQuotas = pyo.Constraint(m.crops, rule=EnforceQuotas_rule) obj_expr = sum(farmer.PurchasePrice[crop] * m.QuantityPurchased[crop] for crop in m.crops) obj_expr -= sum(farmer.SubQuotaSellingPrice[crop] * m.QuantitySubQuotaSold[crop] for crop in m.crops) obj_expr -= sum(farmer.SuperQuotaSellingPrice[crop] * m.QuantitySuperQuotaSold[crop] for crop in m.crops) - m.obj = pe.Objective(expr=farmer.scenario_probabilities[scenario] * obj_expr) + m.obj = pyo.Objective(expr=farmer.scenario_probabilities[scenario] * obj_expr) - complicating_vars_map = pe.ComponentMap() + complicating_vars_map = pyo.ComponentMap() for crop in m.crops: complicating_vars_map[master.devoted_acreage[crop]] = m.devoted_acreage[crop] @@ -106,10 +116,14 @@ def main(): subproblem_fn_kwargs=subproblem_fn_kwargs, master_eta=m.eta[s], subproblem_solver='gurobi_persistent') - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) - print('{0:<15}{1:<15}{2:<15}{3:<15}{4:<15}'.format('# Cuts', 'Corn', 'Sugar Beets', 'Wheat', 'Time')) + print('{0:<15}{1:<15}{2:<15}{3:<15}{4:<15}'.format('# Cuts', + 'Corn', + 'Sugar Beets', + 'Wheat', + 'Time')) for i in range(30): res = opt.solve(tee=False, save_results=False) cuts_added = m.benders.generate_cut() diff --git a/pyomo/contrib/benders/examples/grothey_ex.py b/pyomo/contrib/benders/examples/grothey_ex.py index 37aec2a6903..9628d2a02a0 100644 --- a/pyomo/contrib/benders/examples/grothey_ex.py +++ b/pyomo/contrib/benders/examples/grothey_ex.py @@ -1,25 +1,35 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.contrib.benders.benders_cuts import BendersCutGenerator -import pyomo.environ as pe +import pyomo.environ as pyo def create_master(): - m = pe.ConcreteModel() - m.y = pe.Var(bounds=(1, None)) - m.eta = pe.Var(bounds=(-10, None)) - m.obj = pe.Objective(expr=m.y**2 + m.eta) + m = pyo.ConcreteModel() + m.y = pyo.Var(bounds=(1, None)) + m.eta = pyo.Var(bounds=(-10, None)) + m.obj = pyo.Objective(expr=m.y**2 + m.eta) return m def create_subproblem(master): - m = pe.ConcreteModel() - m.x1 = pe.Var() - m.x2 = pe.Var() - m.y = pe.Var() - m.obj = pe.Objective(expr=-m.x2) - m.c1 = pe.Constraint(expr=(m.x1 - 1)**2 + m.x2**2 <= pe.log(m.y)) - m.c2 = pe.Constraint(expr=(m.x1 + 1)**2 + m.x2**2 <= pe.log(m.y)) - - complicating_vars_map = pe.ComponentMap() + m = pyo.ConcreteModel() + m.x1 = pyo.Var() + m.x2 = pyo.Var() + m.y = pyo.Var() + m.obj = pyo.Objective(expr=-m.x2) + m.c1 = pyo.Constraint(expr=(m.x1 - 1)**2 + m.x2**2 <= pyo.log(m.y)) + m.c2 = pyo.Constraint(expr=(m.x1 + 1)**2 + m.x2**2 <= pyo.log(m.y)) + + complicating_vars_map = pyo.ComponentMap() complicating_vars_map[master.y] = m.y return m, complicating_vars_map @@ -34,7 +44,7 @@ def main(): subproblem_fn_kwargs={'master': m}, master_eta=m.eta, subproblem_solver='ipopt', ) - opt = pe.SolverFactory('gurobi_direct') + opt = pyo.SolverFactory('gurobi_direct') for i in range(30): res = opt.solve(m, tee=False) diff --git a/pyomo/contrib/benders/tests/test_benders.py b/pyomo/contrib/benders/tests/test_benders.py index c91b4e4f04b..437f5818966 100644 --- a/pyomo/contrib/benders/tests/test_benders.py +++ b/pyomo/contrib/benders/tests/test_benders.py @@ -1,6 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest from pyomo.contrib.benders.benders_cuts import BendersCutGenerator -import pyomo.environ as pe +import pyomo.environ as pyo try: import mpi4py mpi4py_available = True @@ -13,10 +23,10 @@ numpy_available = False -ipopt_opt = pe.SolverFactory('ipopt') +ipopt_opt = pyo.SolverFactory('ipopt') ipopt_available = ipopt_opt.available(exception_flag=False) -cplex_opt = pe.SolverFactory('cplex_direct') +cplex_opt = pyo.SolverFactory('cplex_direct') cplex_available = cplex_opt.available(exception_flag=False) @@ -47,56 +57,56 @@ def __init__(self): self.scenario_probabilities['AboveAverageScenario'] = 0.3333 def create_master(farmer): - m = pe.ConcreteModel() + m = pyo.ConcreteModel() - m.crops = pe.Set(initialize=farmer.crops, ordered=True) - m.scenarios = pe.Set(initialize=farmer.scenarios, ordered=True) + m.crops = pyo.Set(initialize=farmer.crops, ordered=True) + m.scenarios = pyo.Set(initialize=farmer.scenarios, ordered=True) - m.devoted_acreage = pe.Var(m.crops, bounds=(0, farmer.total_acreage)) - m.eta = pe.Var(m.scenarios) + m.devoted_acreage = pyo.Var(m.crops, bounds=(0, farmer.total_acreage)) + m.eta = pyo.Var(m.scenarios) for s in m.scenarios: m.eta[s].setlb(-432000 * farmer.scenario_probabilities[s]) - m.total_acreage_con = pe.Constraint(expr=sum(m.devoted_acreage.values()) <= farmer.total_acreage) + m.total_acreage_con = pyo.Constraint(expr=sum(m.devoted_acreage.values()) <= farmer.total_acreage) - m.obj = pe.Objective( + m.obj = pyo.Objective( expr=sum(farmer.PlantingCostPerAcre[crop] * m.devoted_acreage[crop] for crop in m.crops) + sum( m.eta.values())) return m def create_subproblem(master, farmer, scenario): - m = pe.ConcreteModel() + m = pyo.ConcreteModel() - m.crops = pe.Set(initialize=farmer.crops, ordered=True) + m.crops = pyo.Set(initialize=farmer.crops, ordered=True) - m.devoted_acreage = pe.Var(m.crops) - m.QuantitySubQuotaSold = pe.Var(m.crops, bounds=(0.0, None)) - m.QuantitySuperQuotaSold = pe.Var(m.crops, bounds=(0.0, None)) - m.QuantityPurchased = pe.Var(m.crops, bounds=(0.0, None)) + m.devoted_acreage = pyo.Var(m.crops) + m.QuantitySubQuotaSold = pyo.Var(m.crops, bounds=(0.0, None)) + m.QuantitySuperQuotaSold = pyo.Var(m.crops, bounds=(0.0, None)) + m.QuantityPurchased = pyo.Var(m.crops, bounds=(0.0, None)) def EnforceCattleFeedRequirement_rule(m, i): return (farmer.CattleFeedRequirement[i] <= (farmer.crop_yield[scenario][i] * m.devoted_acreage[i]) + m.QuantityPurchased[i] - m.QuantitySubQuotaSold[i] - m.QuantitySuperQuotaSold[i]) - m.EnforceCattleFeedRequirement = pe.Constraint(m.crops, rule=EnforceCattleFeedRequirement_rule) + m.EnforceCattleFeedRequirement = pyo.Constraint(m.crops, rule=EnforceCattleFeedRequirement_rule) def LimitAmountSold_rule(m, i): return m.QuantitySubQuotaSold[i] + m.QuantitySuperQuotaSold[i] - ( farmer.crop_yield[scenario][i] * m.devoted_acreage[i]) <= 0.0 - m.LimitAmountSold = pe.Constraint(m.crops, rule=LimitAmountSold_rule) + m.LimitAmountSold = pyo.Constraint(m.crops, rule=LimitAmountSold_rule) def EnforceQuotas_rule(m, i): return (0.0, m.QuantitySubQuotaSold[i], farmer.PriceQuota[i]) - m.EnforceQuotas = pe.Constraint(m.crops, rule=EnforceQuotas_rule) + m.EnforceQuotas = pyo.Constraint(m.crops, rule=EnforceQuotas_rule) obj_expr = sum(farmer.PurchasePrice[crop] * m.QuantityPurchased[crop] for crop in m.crops) obj_expr -= sum(farmer.SubQuotaSellingPrice[crop] * m.QuantitySubQuotaSold[crop] for crop in m.crops) obj_expr -= sum(farmer.SuperQuotaSellingPrice[crop] * m.QuantitySuperQuotaSold[crop] for crop in m.crops) - m.obj = pe.Objective(expr=farmer.scenario_probabilities[scenario] * obj_expr) + m.obj = pyo.Objective(expr=farmer.scenario_probabilities[scenario] * obj_expr) - complicating_vars_map = pe.ComponentMap() + complicating_vars_map = pyo.ComponentMap() for crop in m.crops: complicating_vars_map[master.devoted_acreage[crop]] = m.devoted_acreage[crop] @@ -116,7 +126,7 @@ def EnforceQuotas_rule(m, i): subproblem_fn_kwargs=subproblem_fn_kwargs, master_eta=m.eta[s], subproblem_solver='cplex_direct') - opt = pe.SolverFactory('cplex_direct') + opt = pyo.SolverFactory('cplex_direct') for i in range(30): res = opt.solve(m, tee=False) @@ -133,22 +143,22 @@ def EnforceQuotas_rule(m, i): @unittest.skipIf(not ipopt_available, 'ipopt is not available.') def test_grothey(self): def create_master(): - m = pe.ConcreteModel() - m.y = pe.Var(bounds=(1, None)) - m.eta = pe.Var(bounds=(-10, None)) - m.obj = pe.Objective(expr=m.y ** 2 + m.eta) + m = pyo.ConcreteModel() + m.y = pyo.Var(bounds=(1, None)) + m.eta = pyo.Var(bounds=(-10, None)) + m.obj = pyo.Objective(expr=m.y ** 2 + m.eta) return m def create_subproblem(master): - m = pe.ConcreteModel() - m.x1 = pe.Var() - m.x2 = pe.Var() - m.y = pe.Var() - m.obj = pe.Objective(expr=-m.x2) - m.c1 = pe.Constraint(expr=(m.x1 - 1) ** 2 + m.x2 ** 2 <= pe.log(m.y)) - m.c2 = pe.Constraint(expr=(m.x1 + 1) ** 2 + m.x2 ** 2 <= pe.log(m.y)) - - complicating_vars_map = pe.ComponentMap() + m = pyo.ConcreteModel() + m.x1 = pyo.Var() + m.x2 = pyo.Var() + m.y = pyo.Var() + m.obj = pyo.Objective(expr=-m.x2) + m.c1 = pyo.Constraint(expr=(m.x1 - 1) ** 2 + m.x2 ** 2 <= pyo.log(m.y)) + m.c2 = pyo.Constraint(expr=(m.x1 + 1) ** 2 + m.x2 ** 2 <= pyo.log(m.y)) + + complicating_vars_map = pyo.ComponentMap() complicating_vars_map[master.y] = m.y return m, complicating_vars_map @@ -161,7 +171,7 @@ def create_subproblem(master): subproblem_fn_kwargs={'master': m}, master_eta=m.eta, subproblem_solver='ipopt', ) - opt = pe.SolverFactory('ipopt') + opt = pyo.SolverFactory('ipopt') for i in range(30): res = opt.solve(m, tee=False) @@ -198,56 +208,56 @@ def __init__(self): self.scenario_probabilities['Scenario4'] = 0.25 def create_master(farmer): - m = pe.ConcreteModel() + m = pyo.ConcreteModel() - m.crops = pe.Set(initialize=farmer.crops, ordered=True) - m.scenarios = pe.Set(initialize=farmer.scenarios, ordered=True) + m.crops = pyo.Set(initialize=farmer.crops, ordered=True) + m.scenarios = pyo.Set(initialize=farmer.scenarios, ordered=True) - m.devoted_acreage = pe.Var(m.crops, bounds=(0, farmer.total_acreage)) - m.eta = pe.Var(m.scenarios) + m.devoted_acreage = pyo.Var(m.crops, bounds=(0, farmer.total_acreage)) + m.eta = pyo.Var(m.scenarios) for s in m.scenarios: m.eta[s].setlb(-432000 * farmer.scenario_probabilities[s]) - m.total_acreage_con = pe.Constraint(expr=sum(m.devoted_acreage.values()) <= farmer.total_acreage) + m.total_acreage_con = pyo.Constraint(expr=sum(m.devoted_acreage.values()) <= farmer.total_acreage) - m.obj = pe.Objective( + m.obj = pyo.Objective( expr=sum(farmer.PlantingCostPerAcre[crop] * m.devoted_acreage[crop] for crop in m.crops) + sum( m.eta.values())) return m def create_subproblem(master, farmer, scenario): - m = pe.ConcreteModel() + m = pyo.ConcreteModel() - m.crops = pe.Set(initialize=farmer.crops, ordered=True) + m.crops = pyo.Set(initialize=farmer.crops, ordered=True) - m.devoted_acreage = pe.Var(m.crops) - m.QuantitySubQuotaSold = pe.Var(m.crops, bounds=(0.0, None)) - m.QuantitySuperQuotaSold = pe.Var(m.crops, bounds=(0.0, None)) - m.QuantityPurchased = pe.Var(m.crops, bounds=(0.0, None)) + m.devoted_acreage = pyo.Var(m.crops) + m.QuantitySubQuotaSold = pyo.Var(m.crops, bounds=(0.0, None)) + m.QuantitySuperQuotaSold = pyo.Var(m.crops, bounds=(0.0, None)) + m.QuantityPurchased = pyo.Var(m.crops, bounds=(0.0, None)) def EnforceCattleFeedRequirement_rule(m, i): return (farmer.CattleFeedRequirement[i] <= (farmer.crop_yield[scenario][i] * m.devoted_acreage[i]) + m.QuantityPurchased[i] - m.QuantitySubQuotaSold[i] - m.QuantitySuperQuotaSold[i]) - m.EnforceCattleFeedRequirement = pe.Constraint(m.crops, rule=EnforceCattleFeedRequirement_rule) + m.EnforceCattleFeedRequirement = pyo.Constraint(m.crops, rule=EnforceCattleFeedRequirement_rule) def LimitAmountSold_rule(m, i): return m.QuantitySubQuotaSold[i] + m.QuantitySuperQuotaSold[i] - ( farmer.crop_yield[scenario][i] * m.devoted_acreage[i]) <= 0.0 - m.LimitAmountSold = pe.Constraint(m.crops, rule=LimitAmountSold_rule) + m.LimitAmountSold = pyo.Constraint(m.crops, rule=LimitAmountSold_rule) def EnforceQuotas_rule(m, i): return (0.0, m.QuantitySubQuotaSold[i], farmer.PriceQuota[i]) - m.EnforceQuotas = pe.Constraint(m.crops, rule=EnforceQuotas_rule) + m.EnforceQuotas = pyo.Constraint(m.crops, rule=EnforceQuotas_rule) obj_expr = sum(farmer.PurchasePrice[crop] * m.QuantityPurchased[crop] for crop in m.crops) obj_expr -= sum(farmer.SubQuotaSellingPrice[crop] * m.QuantitySubQuotaSold[crop] for crop in m.crops) obj_expr -= sum(farmer.SuperQuotaSellingPrice[crop] * m.QuantitySuperQuotaSold[crop] for crop in m.crops) - m.obj = pe.Objective(expr=farmer.scenario_probabilities[scenario] * obj_expr) + m.obj = pyo.Objective(expr=farmer.scenario_probabilities[scenario] * obj_expr) - complicating_vars_map = pe.ComponentMap() + complicating_vars_map = pyo.ComponentMap() for crop in m.crops: complicating_vars_map[master.devoted_acreage[crop]] = m.devoted_acreage[crop] @@ -267,7 +277,7 @@ def EnforceQuotas_rule(m, i): subproblem_fn_kwargs=subproblem_fn_kwargs, master_eta=m.eta[s], subproblem_solver='cplex_direct') - opt = pe.SolverFactory('cplex_direct') + opt = pyo.SolverFactory('cplex_direct') for i in range(30): res = opt.solve(m, tee=False) diff --git a/pyomo/contrib/example/tests/test_example.py b/pyomo/contrib/example/tests/test_example.py index 05b0f6b4dab..72614167c35 100644 --- a/pyomo/contrib/example/tests/test_example.py +++ b/pyomo/contrib/example/tests/test_example.py @@ -1,9 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # # Only run the tests in this package if the pyomo.contrib.example package # has been successfully imported. # -import os -import sys + import pyomo.contrib.example import pyutilib.th as unittest diff --git a/pyomo/contrib/fbbt/fbbt.py b/pyomo/contrib/fbbt/fbbt.py index 30a60be11df..86f3c7622d7 100644 --- a/pyomo/contrib/fbbt/fbbt.py +++ b/pyomo/contrib/fbbt/fbbt.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.common.collections import ComponentMap, ComponentSet import pyomo.core.expr.numeric_expr as numeric_expr from pyomo.core.expr.visitor import ExpressionValueVisitor, identify_variables diff --git a/pyomo/contrib/fbbt/interval.py b/pyomo/contrib/fbbt/interval.py index df305cfde93..e9acd3b757b 100644 --- a/pyomo/contrib/fbbt/interval.py +++ b/pyomo/contrib/fbbt/interval.py @@ -1,5 +1,14 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import math -import warnings import logging from pyomo.common.errors import DeveloperError, InfeasibleConstraintException, PyomoException diff --git a/pyomo/contrib/fbbt/tests/test_fbbt.py b/pyomo/contrib/fbbt/tests/test_fbbt.py index 8c96d26f10b..0f7fe1b140b 100644 --- a/pyomo/contrib/fbbt/tests/test_fbbt.py +++ b/pyomo/contrib/fbbt/tests/test_fbbt.py @@ -1,14 +1,25 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.contrib.fbbt.fbbt import fbbt, compute_bounds_on_expr -from pyomo.contrib.fbbt import interval from pyomo.common.dependencies import numpy as np, numpy_available from pyomo.common.errors import InfeasibleConstraintException -from pyomo.core.expr.numeric_expr import ProductExpression, UnaryFunctionExpression +from pyomo.core.expr.numeric_expr import (ProductExpression, + UnaryFunctionExpression) import math import logging import io + class DummyExpr(ProductExpression): pass @@ -20,17 +31,17 @@ def test_add(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.p = pe.Param(mutable=True) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.p = pyo.Param(mutable=True) m.p.value = 1 - m.c = pe.Constraint(expr=pe.inequality(body=m.x+m.y+(m.p+1), lower=cl, upper=cu)) + m.c = pyo.Constraint(expr=pyo.inequality(body=m.x+m.y+(m.p+1), lower=cl, upper=cu)) new_bounds = fbbt(m) - self.assertEqual(new_bounds[m.x], (pe.value(m.x.lb), pe.value(m.x.ub))) - self.assertEqual(new_bounds[m.y], (pe.value(m.y.lb), pe.value(m.y.ub))) - x = np.linspace(pe.value(m.x.lb), pe.value(m.x.ub), 100) - z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100) + self.assertEqual(new_bounds[m.x], (pyo.value(m.x.lb), pyo.value(m.x.ub))) + self.assertEqual(new_bounds[m.y], (pyo.value(m.y.lb), pyo.value(m.y.ub))) + x = np.linspace(pyo.value(m.x.lb), pyo.value(m.x.ub), 100) + z = np.linspace(pyo.value(m.c.lower), pyo.value(m.c.upper), 100) if m.y.lb is None: yl = -np.inf else: @@ -50,13 +61,13 @@ def test_sub1(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=m.x-m.y, lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=m.x-m.y, lower=cl, upper=cu)) fbbt(m) - x = np.linspace(pe.value(m.x.lb), pe.value(m.x.ub), 100) - z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100) + x = np.linspace(pyo.value(m.x.lb), pyo.value(m.x.ub), 100) + z = np.linspace(pyo.value(m.c.lower), pyo.value(m.c.upper), 100) if m.y.lb is None: yl = -np.inf else: @@ -76,13 +87,13 @@ def test_sub2(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=m.y-m.x, lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=m.y-m.x, lower=cl, upper=cu)) fbbt(m) - x = np.linspace(pe.value(m.x.lb), pe.value(m.x.ub), 100) - z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100) + x = np.linspace(pyo.value(m.x.lb), pyo.value(m.x.ub), 100) + z = np.linspace(pyo.value(m.c.lower), pyo.value(m.c.upper), 100) if m.y.lb is None: yl = -np.inf else: @@ -102,13 +113,13 @@ def test_mul(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=m.x*m.y, lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=m.x*m.y, lower=cl, upper=cu)) fbbt(m) - x = np.linspace(pe.value(m.x.lb) + 1e-6, pe.value(m.x.ub), 100, endpoint=False) - z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100) + x = np.linspace(pyo.value(m.x.lb) + 1e-6, pyo.value(m.x.ub), 100, endpoint=False) + z = np.linspace(pyo.value(m.c.lower), pyo.value(m.c.upper), 100) if m.y.lb is None: yl = -np.inf else: @@ -128,13 +139,13 @@ def test_div1(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=m.x/m.y, lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=m.x/m.y, lower=cl, upper=cu)) fbbt(m) - x = np.linspace(pe.value(m.x.lb), pe.value(m.x.ub), 100) - z = np.linspace(pe.value(m.c.lower) + 1e-6, pe.value(m.c.upper), 100, endpoint=False) + x = np.linspace(pyo.value(m.x.lb), pyo.value(m.x.ub), 100) + z = np.linspace(pyo.value(m.c.lower) + 1e-6, pyo.value(m.c.upper), 100, endpoint=False) if m.y.lb is None: yl = -np.inf else: @@ -154,13 +165,13 @@ def test_div2(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=m.y/m.x, lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=m.y/m.x, lower=cl, upper=cu)) fbbt(m) - x = np.linspace(pe.value(m.x.lb), pe.value(m.x.ub), 100) - z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100) + x = np.linspace(pyo.value(m.x.lb), pyo.value(m.x.ub), 100) + z = np.linspace(pyo.value(m.c.lower), pyo.value(m.c.upper), 100) if m.y.lb is None: yl = -np.inf else: @@ -180,17 +191,17 @@ def test_pow1(self): c_bounds = [(-2.5, 2.8), (0.5, 2.8), (-2.5, 0), (0, 2.8), (1, 2.8), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=m.x**m.y, lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=m.x**m.y, lower=cl, upper=cu)) if xl > 0 and cu <= 0: with self.assertRaises(InfeasibleConstraintException): fbbt(m) else: fbbt(m) - x = np.linspace(pe.value(m.x.lb) + 1e-6, pe.value(m.x.ub), 100, endpoint=False) - z = np.linspace(pe.value(m.c.lower) + 1e-6, pe.value(m.c.upper), 100, endpoint=False) + x = np.linspace(pyo.value(m.x.lb) + 1e-6, pyo.value(m.x.ub), 100, endpoint=False) + z = np.linspace(pyo.value(m.c.lower) + 1e-6, pyo.value(m.c.upper), 100, endpoint=False) if m.y.lb is None: yl = -np.inf else: @@ -210,13 +221,13 @@ def test_pow2(self): c_bounds = [(-2.5, 2.8), (0.5, 2.8), (0, 2.8), (1, 2.8), (0.5, 1)] for xl, xu in x_bounds: for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=m.y**m.x, lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=m.y**m.x, lower=cl, upper=cu)) fbbt(m) - x = np.linspace(pe.value(m.x.lb) + 1e-6, pe.value(m.x.ub), 100, endpoint=False) - z = np.linspace(pe.value(m.c.lower) + 1e-6, pe.value(m.c.upper), 100, endpoint=False) + x = np.linspace(pyo.value(m.x.lb) + 1e-6, pyo.value(m.x.ub), 100, endpoint=False) + z = np.linspace(pyo.value(m.c.lower) + 1e-6, pyo.value(m.c.upper), 100, endpoint=False) if m.y.lb is None: yl = -np.inf else: @@ -231,10 +242,10 @@ def test_pow2(self): self.assertTrue(np.all(yu >= _y)) def test_x_sq(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.c = pe.Constraint(expr=m.x**2 == m.y) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.c = pyo.Constraint(expr=m.x**2 == m.y) fbbt(m) self.assertEqual(m.x.lb, None) @@ -284,20 +295,20 @@ def test_x_sq(self): self.assertEqual(m.x.ub, 0) def test_pow5(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var(bounds=(0.5, 1)) - m.c = pe.Constraint(expr=2**m.x == m.y) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var(bounds=(0.5, 1)) + m.c = pyo.Constraint(expr=2**m.x == m.y) fbbt(m) self.assertAlmostEqual(m.x.lb, -1) self.assertAlmostEqual(m.x.ub, 0) def test_x_pow_minus_2(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.c = pe.Constraint(expr=m.x**(-2) == m.y) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.c = pyo.Constraint(expr=m.x**(-2) == m.y) fbbt(m) self.assertEqual(m.x.lb, None) @@ -336,10 +347,10 @@ def test_x_pow_minus_2(self): self.assertAlmostEqual(m.x.ub, -1) def test_x_cubed(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.c = pe.Constraint(expr=m.x**3 == m.y) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.c = pyo.Constraint(expr=m.x**3 == m.y) fbbt(m) self.assertEqual(m.x.lb, None) @@ -380,10 +391,10 @@ def test_x_cubed(self): self.assertAlmostEqual(m.x.ub, -1) def test_x_pow_minus_3(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.c = pe.Constraint(expr=m.x**(-3) == m.y) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.c = pyo.Constraint(expr=m.x**(-3) == m.y) fbbt(m) self.assertEqual(m.x.lb, None) @@ -424,12 +435,12 @@ def test_pow4(self): exp_vals = [-3, -2.5, -2, -1.5, -1, -0.5, 0.5, 1, 1.5, 2, 2.5, 3] for yl, yu in y_bounds: for _exp_val in exp_vals: - m = pe.Block(concrete=True) - m.x = pe.Var() - m.y = pe.Var(bounds=(yl, yu)) - m.c = pe.Constraint(expr=m.x**_exp_val == m.y) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.y = pyo.Var(bounds=(yl, yu)) + m.c = pyo.Constraint(expr=m.x**_exp_val == m.y) fbbt(m) - y = np.linspace(pe.value(m.y.lb) + 1e-6, pe.value(m.y.ub), 100, endpoint=True) + y = np.linspace(pyo.value(m.y.lb) + 1e-6, pyo.value(m.y.ub), 100, endpoint=True) if m.x.lb is None: xl = -np.inf else: @@ -443,10 +454,10 @@ def test_pow4(self): self.assertTrue(np.all(xu >= _x)) def test_sqrt(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.c = pe.Constraint(expr=pe.sqrt(m.x) == m.y) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.c = pyo.Constraint(expr=pyo.sqrt(m.x) == m.y) fbbt(m) self.assertEqual(m.x.lb, 0) @@ -489,23 +500,23 @@ def test_sqrt(self): def test_exp(self): c_bounds = [(-2.5, 2.8), (0.5, 2.8), (0, 2.8), (1, 2.8), (0.5, 1)] for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.exp(m.x), lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.exp(m.x), lower=cl, upper=cu)) fbbt(m) - if pe.value(m.c.lower) <= 0: + if pyo.value(m.c.lower) <= 0: _cl = 1e-6 else: - _cl = pe.value(m.c.lower) - z = np.linspace(_cl, pe.value(m.c.upper), 100) + _cl = pyo.value(m.c.lower) + z = np.linspace(_cl, pyo.value(m.c.upper), 100) if m.x.lb is None: xl = -np.inf else: - xl = pe.value(m.x.lb) + xl = pyo.value(m.x.lb) if m.x.ub is None: xu = np.inf else: - xu = pe.value(m.x.ub) + xu = pyo.value(m.x.ub) x = np.log(z) self.assertTrue(np.all(xl <= x)) self.assertTrue(np.all(xu >= x)) @@ -514,19 +525,19 @@ def test_exp(self): def test_log(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.log(m.x), lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.log(m.x), lower=cl, upper=cu)) fbbt(m) - z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100) + z = np.linspace(pyo.value(m.c.lower), pyo.value(m.c.upper), 100) if m.x.lb is None: xl = -np.inf else: - xl = pe.value(m.x.lb) + xl = pyo.value(m.x.lb) if m.x.ub is None: xu = np.inf else: - xu = pe.value(m.x.ub) + xu = pyo.value(m.x.ub) x = np.exp(z) self.assertTrue(np.all(xl <= x)) self.assertTrue(np.all(xu >= x)) @@ -535,19 +546,19 @@ def test_log(self): def test_log10(self): c_bounds = [(-2.5, 2.8), (-2.5, -0.5), (0.5, 2.8), (-2.5, 0), (0, 2.8), (-2.5, -1), (1, 2.8), (-1, -0.5), (0.5, 1)] for cl, cu in c_bounds: - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.log10(m.x), lower=cl, upper=cu)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.log10(m.x), lower=cl, upper=cu)) fbbt(m) - z = np.linspace(pe.value(m.c.lower), pe.value(m.c.upper), 100) + z = np.linspace(pyo.value(m.c.lower), pyo.value(m.c.upper), 100) if m.x.lb is None: xl = -np.inf else: - xl = pe.value(m.x.lb) + xl = pyo.value(m.x.lb) if m.x.ub is None: xu = np.inf else: - xu = pe.value(m.x.ub) + xu = pyo.value(m.x.ub) x = 10**z print(xl, xu, cl, cu) print(x) @@ -555,149 +566,149 @@ def test_log10(self): self.assertTrue(np.all(xu >= x)) def test_sin(self): - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(-math.pi/2, math.pi/2)) - m.c = pe.Constraint(expr=pe.inequality(body=pe.sin(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(-math.pi/2, math.pi/2)) + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.sin(m.x), lower=-0.5, upper=0.5)) fbbt(m.c) - self.assertAlmostEqual(pe.value(m.x.lb), math.asin(-0.5)) - self.assertAlmostEqual(pe.value(m.x.ub), math.asin(0.5)) + self.assertAlmostEqual(pyo.value(m.x.lb), math.asin(-0.5)) + self.assertAlmostEqual(pyo.value(m.x.ub), math.asin(0.5)) - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.sin(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.sin(m.x), lower=-0.5, upper=0.5)) fbbt(m.c) self.assertEqual(m.x.lb, None) self.assertEqual(m.x.ub, None) def test_cos(self): - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(0, math.pi)) - m.c = pe.Constraint(expr=pe.inequality(body=pe.cos(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(0, math.pi)) + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.cos(m.x), lower=-0.5, upper=0.5)) fbbt(m) - self.assertAlmostEqual(pe.value(m.x.lb), math.acos(0.5)) - self.assertAlmostEqual(pe.value(m.x.ub), math.acos(-0.5)) + self.assertAlmostEqual(pyo.value(m.x.lb), math.acos(0.5)) + self.assertAlmostEqual(pyo.value(m.x.ub), math.acos(-0.5)) - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.cos(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.cos(m.x), lower=-0.5, upper=0.5)) fbbt(m) self.assertEqual(m.x.lb, None) self.assertEqual(m.x.ub, None) def test_tan(self): - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(-math.pi/2, math.pi/2)) - m.c = pe.Constraint(expr=pe.inequality(body=pe.tan(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(-math.pi/2, math.pi/2)) + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.tan(m.x), lower=-0.5, upper=0.5)) fbbt(m) - self.assertAlmostEqual(pe.value(m.x.lb), math.atan(-0.5)) - self.assertAlmostEqual(pe.value(m.x.ub), math.atan(0.5)) + self.assertAlmostEqual(pyo.value(m.x.lb), math.atan(-0.5)) + self.assertAlmostEqual(pyo.value(m.x.ub), math.atan(0.5)) - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.tan(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.tan(m.x), lower=-0.5, upper=0.5)) fbbt(m) self.assertEqual(m.x.lb, None) self.assertEqual(m.x.ub, None) def test_asin(self): - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.asin(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.asin(m.x), lower=-0.5, upper=0.5)) fbbt(m) - self.assertAlmostEqual(pe.value(m.x.lb), math.sin(-0.5)) - self.assertAlmostEqual(pe.value(m.x.ub), math.sin(0.5)) + self.assertAlmostEqual(pyo.value(m.x.lb), math.sin(-0.5)) + self.assertAlmostEqual(pyo.value(m.x.ub), math.sin(0.5)) def test_acos(self): - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.acos(m.x), lower=1, upper=2)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.acos(m.x), lower=1, upper=2)) fbbt(m) - self.assertAlmostEqual(pe.value(m.x.lb), math.cos(2)) - self.assertAlmostEqual(pe.value(m.x.ub), math.cos(1)) + self.assertAlmostEqual(pyo.value(m.x.lb), math.cos(2)) + self.assertAlmostEqual(pyo.value(m.x.ub), math.cos(1)) def test_atan(self): - m = pe.Block(concrete=True) - m.x = pe.Var() - m.c = pe.Constraint(expr=pe.inequality(body=pe.atan(m.x), lower=-0.5, upper=0.5)) + m = pyo.Block(concrete=True) + m.x = pyo.Var() + m.c = pyo.Constraint(expr=pyo.inequality(body=pyo.atan(m.x), lower=-0.5, upper=0.5)) fbbt(m) - self.assertAlmostEqual(pe.value(m.x.lb), math.tan(-0.5)) - self.assertAlmostEqual(pe.value(m.x.ub), math.tan(0.5)) + self.assertAlmostEqual(pyo.value(m.x.lb), math.tan(-0.5)) + self.assertAlmostEqual(pyo.value(m.x.ub), math.tan(0.5)) def test_multiple_constraints(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(-3, 3)) - m.y = pe.Var(bounds=(0, None)) - m.z = pe.Var() - m.c = pe.ConstraintList() + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(-3, 3)) + m.y = pyo.Var(bounds=(0, None)) + m.z = pyo.Var() + m.c = pyo.ConstraintList() m.c.add(m.x + m.y >= -1) m.c.add(m.x + m.y <= -1) m.c.add(m.y - m.x*m.z <= 2) m.c.add(m.y - m.x*m.z >= -2) m.c.add(m.x + m.z == 1) fbbt(m) - self.assertAlmostEqual(pe.value(m.x.lb), -1, 8) - self.assertAlmostEqual(pe.value(m.x.ub), -1, 8) - self.assertAlmostEqual(pe.value(m.y.lb), 0, 8) - self.assertAlmostEqual(pe.value(m.y.ub), 0, 8) - self.assertAlmostEqual(pe.value(m.z.lb), 2, 8) - self.assertAlmostEqual(pe.value(m.z.ub), 2, 8) + self.assertAlmostEqual(pyo.value(m.x.lb), -1, 8) + self.assertAlmostEqual(pyo.value(m.x.ub), -1, 8) + self.assertAlmostEqual(pyo.value(m.y.lb), 0, 8) + self.assertAlmostEqual(pyo.value(m.y.ub), 0, 8) + self.assertAlmostEqual(pyo.value(m.z.lb), 2, 8) + self.assertAlmostEqual(pyo.value(m.z.ub), 2, 8) def test_multiple_constraints2(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(-3, 3)) - m.y = pe.Var(bounds=(None, 0)) - m.z = pe.Var() - m.c = pe.ConstraintList() + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(-3, 3)) + m.y = pyo.Var(bounds=(None, 0)) + m.z = pyo.Var() + m.c = pyo.ConstraintList() m.c.add(-m.x - m.y >= -1) m.c.add(-m.x - m.y <= -1) m.c.add(-m.y - m.x*m.z >= -2) m.c.add(-m.y - m.x*m.z <= 2) m.c.add(-m.x - m.z == 1) fbbt(m) - self.assertAlmostEqual(pe.value(m.x.lb), 1, 8) - self.assertAlmostEqual(pe.value(m.x.ub), 1, 8) - self.assertAlmostEqual(pe.value(m.y.lb), 0, 8) - self.assertAlmostEqual(pe.value(m.y.ub), 0, 8) - self.assertAlmostEqual(pe.value(m.z.lb), -2, 8) - self.assertAlmostEqual(pe.value(m.z.ub), -2, 8) + self.assertAlmostEqual(pyo.value(m.x.lb), 1, 8) + self.assertAlmostEqual(pyo.value(m.x.ub), 1, 8) + self.assertAlmostEqual(pyo.value(m.y.lb), 0, 8) + self.assertAlmostEqual(pyo.value(m.y.ub), 0, 8) + self.assertAlmostEqual(pyo.value(m.z.lb), -2, 8) + self.assertAlmostEqual(pyo.value(m.z.ub), -2, 8) def test_binary(self): - m = pe.ConcreteModel() - m.x = pe.Var(domain=pe.Binary) - m.y = pe.Var(domain=pe.Binary) - m.c = pe.Constraint(expr=m.x + m.y >= 1.5) - fbbt(m) - self.assertEqual(pe.value(m.x.lb), 1) - self.assertEqual(pe.value(m.x.ub), 1) - self.assertEqual(pe.value(m.y.lb), 1) - self.assertEqual(pe.value(m.y.ub), 1) - - m = pe.ConcreteModel() - m.x = pe.Var(domain=pe.Binary) - m.y = pe.Var(domain=pe.Binary) - m.c = pe.Constraint(expr=m.x + m.y <= 0.5) - fbbt(m) - self.assertEqual(pe.value(m.x.lb), 0) - self.assertEqual(pe.value(m.x.ub), 0) - self.assertEqual(pe.value(m.y.lb), 0) - self.assertEqual(pe.value(m.y.ub), 0) + m = pyo.ConcreteModel() + m.x = pyo.Var(domain=pyo.Binary) + m.y = pyo.Var(domain=pyo.Binary) + m.c = pyo.Constraint(expr=m.x + m.y >= 1.5) + fbbt(m) + self.assertEqual(pyo.value(m.x.lb), 1) + self.assertEqual(pyo.value(m.x.ub), 1) + self.assertEqual(pyo.value(m.y.lb), 1) + self.assertEqual(pyo.value(m.y.ub), 1) + + m = pyo.ConcreteModel() + m.x = pyo.Var(domain=pyo.Binary) + m.y = pyo.Var(domain=pyo.Binary) + m.c = pyo.Constraint(expr=m.x + m.y <= 0.5) + fbbt(m) + self.assertEqual(pyo.value(m.x.lb), 0) + self.assertEqual(pyo.value(m.x.ub), 0) + self.assertEqual(pyo.value(m.y.lb), 0) + self.assertEqual(pyo.value(m.y.ub), 0) def test_always_feasible(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(1,2)) - m.y = pe.Var(bounds=(1,2)) - m.c = pe.Constraint(expr=m.x + m.y >= 0) + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(1,2)) + m.y = pyo.Var(bounds=(1,2)) + m.c = pyo.Constraint(expr=m.x + m.y >= 0) fbbt(m) self.assertTrue(m.c.active) fbbt(m, deactivate_satisfied_constraints=True) self.assertFalse(m.c.active) def test_iteration_limit(self): - m = pe.ConcreteModel() - m.x_set = pe.Set(initialize=[0, 1, 2], ordered=True) - m.c_set = pe.Set(initialize=[0, 1], ordered=True) - m.x = pe.Var(m.x_set) - m.c = pe.Constraint(m.c_set) + m = pyo.ConcreteModel() + m.x_set = pyo.Set(initialize=[0, 1, 2], ordered=True) + m.c_set = pyo.Set(initialize=[0, 1], ordered=True) + m.x = pyo.Var(m.x_set) + m.c = pyo.Constraint(m.c_set) m.c[0] = m.x[0] == m.x[1] m.c[1] = m.x[1] == m.x[2] m.x[2].setlb(-1) @@ -709,9 +720,9 @@ def test_iteration_limit(self): self.assertEqual(m.x[0].ub, None) def test_inf_bounds_on_expr(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(-1, 1)) - m.y = pe.Var() + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(-1, 1)) + m.y = pyo.Var() lb, ub = compute_bounds_on_expr(m.x + m.y) self.assertEqual(lb, None) self.assertEqual(ub, None) @@ -719,11 +730,11 @@ def test_inf_bounds_on_expr(self): @unittest.skip('This test passes locally, but not on travis or appveyor. I will add an issue.') def test_skip_unknown_expression1(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(1,1)) - m.y = pe.Var() + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(1,1)) + m.y = pyo.Var() expr = DummyExpr([m.x, m.y]) - m.c = pe.Constraint(expr=expr == 1) + m.c = pyo.Constraint(expr=expr == 1) logging_io = io.StringIO() handler = logging.StreamHandler(stream=logging_io) handler.setLevel(logging.WARNING) @@ -731,10 +742,10 @@ def test_skip_unknown_expression1(self): logger.addHandler(handler) new_bounds = fbbt(m) handler.flush() - self.assertEqual(pe.value(m.x.lb), 1) - self.assertEqual(pe.value(m.x.ub), 1) - self.assertEqual(pe.value(m.y.lb), None) - self.assertEqual(pe.value(m.y.ub), None) + self.assertEqual(pyo.value(m.x.lb), 1) + self.assertEqual(pyo.value(m.x.ub), 1) + self.assertEqual(pyo.value(m.y.lb), None) + self.assertEqual(pyo.value(m.y.ub), None) a = "Unsupported expression type for FBBT" b = logging_io.getvalue() a = a.strip() @@ -747,10 +758,10 @@ def test_skip_unknown_expression2(self): def dummy_unary_expr(x): return 0.5*x - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(0,4)) + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(0,4)) expr = UnaryFunctionExpression((m.x,), name='dummy_unary_expr', fcn=dummy_unary_expr) - m.c = pe.Constraint(expr=expr == 1) + m.c = pyo.Constraint(expr=expr == 1) logging_io = io.StringIO() handler = logging.StreamHandler(stream=logging_io) handler.setLevel(logging.WARNING) @@ -758,8 +769,8 @@ def dummy_unary_expr(x): logger.addHandler(handler) new_bounds = fbbt(m) handler.flush() - self.assertEqual(pe.value(m.x.lb), 0) - self.assertEqual(pe.value(m.x.ub), 4) + self.assertEqual(pyo.value(m.x.lb), 0) + self.assertEqual(pyo.value(m.x.ub), 4) a = "Unsupported expression type for FBBT" b = logging_io.getvalue() a = a.strip() @@ -768,19 +779,19 @@ def dummy_unary_expr(x): logger.removeHandler(handler) def test_compute_expr_bounds(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(-1,1)) - m.y = pe.Var(bounds=(-1,1)) + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(-1,1)) + m.y = pyo.Var(bounds=(-1,1)) e = m.x + m.y lb, ub = compute_bounds_on_expr(e) self.assertAlmostEqual(lb, -2, 14) self.assertAlmostEqual(ub, 2, 14) def test_encountered_bugs1(self): - m = pe.Block(concrete=True) - m.x = pe.Var(bounds=(-0.035, -0.035)) - m.y = pe.Var(bounds=(-0.023, -0.023)) - m.c = pe.Constraint(expr=m.x**2 + m.y**2 <= 0.0256) + m = pyo.Block(concrete=True) + m.x = pyo.Var(bounds=(-0.035, -0.035)) + m.y = pyo.Var(bounds=(-0.023, -0.023)) + m.c = pyo.Constraint(expr=m.x**2 + m.y**2 <= 0.0256) fbbt(m.c) self.assertEqual(m.x.lb, -0.035) self.assertEqual(m.x.ub, -0.035) @@ -788,10 +799,10 @@ def test_encountered_bugs1(self): self.assertEqual(m.y.ub, -0.023) def test_encountered_bugs2(self): - m = pe.Block(concrete=True) - m.x = pe.Var(within=pe.Integers) - m.y = pe.Var(within=pe.Integers) - m.c = pe.Constraint(expr=m.x + m.y == 1) + m = pyo.Block(concrete=True) + m.x = pyo.Var(within=pyo.Integers) + m.y = pyo.Var(within=pyo.Integers) + m.c = pyo.Constraint(expr=m.x + m.y == 1) fbbt(m.c) self.assertEqual(m.x.lb, None) self.assertEqual(m.x.ub, None) @@ -804,11 +815,11 @@ def test_encountered_bugs3(self): yl = 0.03369608678342047 yu = 0.04009243987444148 - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(xl, xu)) - m.y = pe.Var(bounds=(yl, yu)) + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(xl, xu)) + m.y = pyo.Var(bounds=(yl, yu)) - m.c = pe.Constraint(expr=m.x == pe.sin(m.y)) + m.c = pyo.Constraint(expr=m.x == pyo.sin(m.y)) fbbt(m.c) diff --git a/pyomo/contrib/fme/fourier_motzkin_elimination.py b/pyomo/contrib/fme/fourier_motzkin_elimination.py index 005e01be084..a8fa6dc7c2a 100644 --- a/pyomo/contrib/fme/fourier_motzkin_elimination.py +++ b/pyomo/contrib/fme/fourier_motzkin_elimination.py @@ -12,11 +12,8 @@ Expression, Objective, SortComponents, value, ConstraintList) from pyomo.core.base import TransformationFactory, _VarData -from pyomo.core.base.block import _BlockData -from pyomo.core.base.param import _ParamData -from pyomo.core.base.constraint import _ConstraintData from pyomo.core.plugins.transform.hierarchy import Transformation -from pyomo.common.config import ConfigBlock, ConfigValue +from pyomo.common.config import ConfigBlock, ConfigValue, NonNegativeFloat from pyomo.common.modeling import unique_component_name from pyomo.repn.standard_repn import generate_standard_repn from pyomo.common.collections import ComponentMap, ComponentSet @@ -25,9 +22,9 @@ import logging from six import iteritems -import inspect -logger = logging.getLogger('pyomo.contrib.fourier_motzkin_elimination') +logger = logging.getLogger('pyomo.contrib.fme') +NAME_BUFFER = {} def _check_var_bounds_filter(constraint): """Check if the constraint is already implied by the variable bounds""" @@ -67,6 +64,17 @@ def vars_to_eliminate_list(x): "Expected Var or list of Vars." "\n\tRecieved %s" % type(x)) +def gcd(a,b): + while b != 0: + a, b = b, a % b + return abs(a) + +def lcm(ints): + a = ints[0] + for b in ints[1:]: + a = abs(a*b) // gcd(a,b) + return a + @TransformationFactory.register('contrib.fourier_motzkin_elimination', doc="Project out specified (continuous) " "variables from a linear model.") @@ -74,13 +82,13 @@ class Fourier_Motzkin_Elimination_Transformation(Transformation): """Project out specified variables from a linear model. This transformation requires the following keyword argument: - vars_to_eliminate: A user-specified list of continuous variables to + vars_to_eliminate: A user-specified list of continuous variables to project out of the model The transformation will deactivate the original constraints of the model - and create a new block named "_pyomo_contrib_fme_transformation" with the - projected constraints. Note that this transformation will flatten the - structure of the original model since there is no obvious mapping between + and create a new block named "_pyomo_contrib_fme_transformation" with the + projected constraints. Note that this transformation will flatten the + structure of the original model since there is no obvious mapping between the original model and the transformed one. """ @@ -90,10 +98,10 @@ class Fourier_Motzkin_Elimination_Transformation(Transformation): default=None, domain=vars_to_eliminate_list, description="Continuous variable or list of continuous variables to " - "project out of the model", + "project out of the model", doc=""" This specifies the list of variables to project out of the model. - Note that these variables must all be continuous and the model must be + Note that these variables must all be continuous and the model must be linear.""" )) CONFIG.declare('constraint_filtering_callback', ConfigValue( @@ -105,26 +113,98 @@ class Fourier_Motzkin_Elimination_Transformation(Transformation): Specify None in order for no constraint filtering to occur during the transformation. - Specify a function that accepts a constraint (represented in the >= + Specify a function that accepts a constraint (represented in the >= dictionary form used in this transformation) and returns a Boolean indicating whether or not to add it to the model. """ )) + CONFIG.declare('do_integer_arithmetic', ConfigValue( + default=False, + domain=bool, + description="A Boolean flag to decide whether Fourier-Motzkin " + "elimination will be performed with only integer arithmetic.", + doc=""" + If True, only integer arithmetic will be performed during Fourier- + Motzkin elimination. This should result in no numerical error. + If True and there is non-integer data in the constraints being + projected, an error will be raised. + + If False, the algorithm will not check whether data is integer, and will + perform division operations. Use this setting when not all data is + integer, or when you are willing to sacrifice some numeric accuracy. + """ + )) + CONFIG.declare('verbose', ConfigValue( + default=False, + domain=bool, + description="A Boolean flag to enable verbose output.", + doc=""" + If True, logs the steps of the projection. + """ + )) + CONFIG.declare('zero_tolerance', ConfigValue( + default=0, + domain=NonNegativeFloat, + description="Absolute tolerance at which a float will be considered 0.", + doc=""" + Whenever fourier-motzkin elimination is used with non-integer data, + there is a chance of numeric trouble, the most obvious of which is + that 'eliminated' variables will remain in the constraints with very + small coefficients. Set this tolerance so that floating points smaller + than this will be treated as 0 (and reported that way in the final + constraints). + """ + )) + CONFIG.declare('integer_tolerance', ConfigValue( + default=0, + domain=NonNegativeFloat, + description="Absolute tolerance at which a float will be considered " + "(and cast to) an integer, when do_integer_arithmetic is True", + doc=""" + Tolerance at which a number x will be considered an integer, when we + are performing fourier-motzkin elimination with only integer_arithmetic. + That is, x will be cast to an integer if + abs(int(x) - x) <= integer_tolerance. + """ + )) def __init__(self): """Initialize transformation object""" super(Fourier_Motzkin_Elimination_Transformation, self).__init__() - + def _apply_to(self, instance, **kwds): - config = self.CONFIG(kwds.pop('options', {})) - config.set_value(kwds) + log_level = logger.getEffectiveLevel() + try: + assert not NAME_BUFFER + config = self.CONFIG(kwds.pop('options', {})) + config.set_value(kwds) + # lower logging values emit more + if config.verbose and log_level > logging.INFO: + logger.setLevel(logging.INFO) + self.verbose = True + # if the user used the logger to ask for info level messages + elif log_level <= logging.INFO: + self.verbose = True + else: + self.verbose = False + self._apply_to_impl(instance, config) + finally: + # clear the global name buffer + NAME_BUFFER.clear() + # restore logging level + logger.setLevel(log_level) + + def _apply_to_impl(self, instance, config): vars_to_eliminate = config.vars_to_eliminate self.constraint_filter = config.constraint_filtering_callback + self.do_integer_arithmetic = config.do_integer_arithmetic + self.integer_tolerance = config.integer_tolerance + self.zero_tolerance = config.zero_tolerance if vars_to_eliminate is None: raise RuntimeError("The Fourier-Motzkin Elimination transformation " "requires the argument vars_to_eliminate, a " "list of Vars to be projected out of the model.") - + # make transformation block transBlockName = unique_component_name( instance, @@ -138,7 +218,7 @@ def _apply_to(self, instance, **kwds): # NOTE that we are ignoring deactivated constraints constraints = [] ctypes_not_to_transform = set((Block, Param, Objective, Set, SetOf, - Expression, Suffix)) + Expression, Suffix, Var)) for obj in instance.component_data_objects( descend_into=Block, sort=SortComponents.deterministic, @@ -149,30 +229,27 @@ def _apply_to(self, instance, **kwds): cons_list = self._process_constraint(obj) constraints.extend(cons_list) obj.deactivate() # the truth will be on our transformation block - elif obj.ctype is Var: - # variable bounds are constraints, but we only need them if this - # is a variable we are projecting out - if obj not in vars_to_eliminate: - continue - if obj.lb is not None: - constraints.append({'body': generate_standard_repn(obj), - 'lower': value(obj.lb), - 'map': ComponentMap([(obj, 1)])}) - if obj.ub is not None: - constraints.append({'body': generate_standard_repn(-obj), - 'lower': -value(obj.ub), - 'map': ComponentMap([(obj, -1)])}) else: raise RuntimeError( "Found active component %s of type %s. The " "Fourier-Motzkin Elimination transformation can only " "handle purely algebraic models. That is, only " "Sets, Params, Vars, Constraints, Expressions, Blocks, " - "and Objectives may be active on the model." % (obj.name, + "and Objectives may be active on the model." % (obj.name, obj.ctype)) - new_constraints = self._fourier_motzkin_elimination(constraints, - vars_to_eliminate) + for obj in vars_to_eliminate: + if obj.lb is not None: + constraints.append({'body': generate_standard_repn(obj), + 'lower': value(obj.lb), + 'map': ComponentMap([(obj, 1)])}) + if obj.ub is not None: + constraints.append({'body': generate_standard_repn(-obj), + 'lower': -value(obj.ub), + 'map': ComponentMap([(obj, -1)])}) + + new_constraints = self._fourier_motzkin_elimination( constraints, + vars_to_eliminate) # put the new constraints on the transformation block for cons in new_constraints: @@ -182,18 +259,14 @@ def _apply_to(self, instance, **kwds): except: logger.error("Problem calling constraint filter callback " "on constraint with right-hand side %s and " - "body:\n%s" % (cons['lower'], cons['body'])) + "body:\n%s" % (cons['lower'], + cons['body'].to_expression())) raise if not keep: continue - body = cons['body'] - lhs = sum(coef*var for (coef, var) in zip(body.linear_coefs, - body.linear_vars)) + \ - sum(coef*v1*v2 for (coef, (v1, v2)) in zip(body.quadratic_coefs, - body.quadratic_vars)) - if body.nonlinear_expr is not None: - lhs += body.nonlinear_expr + lhs = cons['body'].to_expression(sort=True) lower = cons['lower'] + assert type(lower) is int or type(lower) is float if type(lhs >= lower) is bool: if lhs >= lower: continue @@ -210,8 +283,8 @@ def _process_constraint(self, constraint): representing only >= constraints. That is, if the constraint has both an ub and a lb, it is transformed into two constraints. Otherwise it is flipped if it is <=. Each dictionary contains the keys 'lower', - and 'body' where, after the process, 'lower' will be a constant, and - 'body' will be the standard repn of the body. (The constant will be + and 'body' where, after the process, 'lower' will be a constant, and + 'body' will be the standard repn of the body. (The constant will be moved to the RHS and we know that the upper bound is None after this). """ body = constraint.body @@ -242,7 +315,7 @@ def _process_constraint(self, constraint): return constraints_to_add def _move_constant_and_add_map(self, cons_dict): - """Takes constraint in dicionary form already in >= form, + """Takes constraint in dicionary form already in >= form, and moves the constant to the RHS """ body = cons_dict['body'] @@ -255,25 +328,26 @@ def _move_constant_and_add_map(self, cons_dict): # time searches later. Note also that we will take the value of the # coeficient here so that we never have to worry about it again during # the transformation. - cons_dict['map'] = ComponentMap(zip(body.linear_vars, + cons_dict['map'] = ComponentMap(zip(body.linear_vars, [value(coef) for coef in body.linear_coefs])) def _fourier_motzkin_elimination(self, constraints, vars_to_eliminate): - """Performs FME on the constraint list in the argument - (which is assumed to be all >= constraints and stored in the - dictionary representation), projecting out each of the variables in + """Performs FME on the constraint list in the argument + (which is assumed to be all >= constraints and stored in the + dictionary representation), projecting out each of the variables in vars_to_eliminate""" # We only need to eliminate variables that actually appear in # this set of constraints... Revise our list. vars_that_appear = [] + vars_that_appear_set = ComponentSet() for cons in constraints: std_repn = cons['body'] if not std_repn.is_linear(): # as long as none of vars_that_appear are in the nonlinear part, # we are actually okay. - nonlinear_vars = ComponentSet(v for two_tuple in + nonlinear_vars = ComponentSet(v for two_tuple in std_repn.quadratic_vars for v in two_tuple) nonlinear_vars.update(v for v in std_repn.nonlinear_vars) @@ -283,16 +357,23 @@ def _fourier_motzkin_elimination(self, constraints, vars_to_eliminate): "constraint. The Fourier-Motzkin " "Elimination transformation can only " "be used to eliminate variables " - "which only appear linearly." % + "which only appear linearly." % var.name) for var in std_repn.linear_vars: if var in vars_to_eliminate: - vars_that_appear.append(var) + if not var in vars_that_appear_set: + vars_that_appear.append(var) + vars_that_appear_set.add(var) # we actually begin the recursion here while vars_that_appear: # first var we will project out the_var = vars_that_appear.pop() + if self.verbose: + logger.info("Projecting out %s" % + the_var.getname(fully_qualified=True, + name_buffer=NAME_BUFFER)) + logger.info("New constraints are:") # we are 'reorganizing' the constraints, we sort based on the sign # of the coefficient of the_var: This tells us whether we have @@ -301,10 +382,15 @@ def _fourier_motzkin_elimination(self, constraints, vars_to_eliminate): geq_list = [] waiting_list = [] + coefs = [] for cons in constraints: leaving_var_coef = cons['map'].get(the_var) if leaving_var_coef is None or leaving_var_coef == 0: waiting_list.append(cons) + if self.verbose: + logger.info("\t%s <= %s" + % (cons['lower'], + cons['body'].to_expression())) continue # we know the constraint is a >= constraint, using that @@ -312,45 +398,145 @@ def _fourier_motzkin_elimination(self, constraints, vars_to_eliminate): # NOTE: neither of the scalar multiplications below flip the # constraint. So we are sure to have only geq constraints # forever, which is exactly what we want. - if leaving_var_coef < 0: - leq_list.append( - self._nonneg_scalar_multiply_linear_constraint( - cons, -1.0/leaving_var_coef)) + if not self.do_integer_arithmetic: + if leaving_var_coef < 0: + leq_list.append( + self._nonneg_scalar_multiply_linear_constraint( + cons, -1.0/leaving_var_coef)) + else: + geq_list.append( + self._nonneg_scalar_multiply_linear_constraint( + cons, 1.0/leaving_var_coef)) else: - geq_list.append( - self._nonneg_scalar_multiply_linear_constraint( - cons, 1.0/leaving_var_coef)) + coefs.append(self._as_integer( + leaving_var_coef, + self._get_noninteger_coef_error_message, + (the_var.name, leaving_var_coef) + )) + if self.do_integer_arithmetic and len(coefs) > 0: + least_common_mult = lcm(coefs) + for cons in constraints: + leaving_var_coef = cons['map'].get(the_var) + if leaving_var_coef is None or leaving_var_coef == 0: + continue + to_lcm = least_common_mult // abs(int(leaving_var_coef)) + if leaving_var_coef < 0: + leq_list.append( + self._nonneg_scalar_multiply_linear_constraint( + cons, to_lcm)) + else: + geq_list.append( + self._nonneg_scalar_multiply_linear_constraint( + cons, to_lcm)) constraints = waiting_list for leq in leq_list: for geq in geq_list: - constraints.append(self._add_linear_constraints(leq, geq)) + constraints.append( self._add_linear_constraints( leq, geq)) + if self.verbose: + cons = constraints[len(constraints)-1] + logger.info("\t%s <= %s" % + (cons['lower'], + cons['body'].to_expression())) return constraints + def _get_noninteger_coef_error_message(self, varname, coef): + return ("The do_integer_arithmetic flag was " + "set to True, but the coefficient of " + "%s is non-integer within the specified " + "tolerance, with value %s. \n" + "Please set do_integer_arithmetic=" + "False, increase integer_tolerance, " + "or make your data integer." % (varname, coef)) + + def _as_integer(self, x, error_message, error_args): + if abs(int(x) - x) <= self.integer_tolerance: + return int(round(x)) + raise ValueError(error_message if error_args is None + else error_message(*error_args)) + + def _multiply(self, scalar, coef, error_message, error_args): + if self.do_integer_arithmetic: + assert type(scalar) is int + return scalar * self._as_integer(coef, error_message, error_args) + elif abs(scalar*coef) > self.zero_tolerance: + return scalar*coef + else: + return 0 + + def _add(self, a, b, error_message, error_args): + if self.do_integer_arithmetic: + return self._as_integer(a, error_message, error_args) \ + + self._as_integer(b, error_message, error_args) + elif abs(a + b) > self.zero_tolerance: + return a + b + else: + return 0 + + def _nonneg_scalar_multiply_linear_constraint_error_msg(self, cons, coef): + return ( + "The do_integer_arithmetic flag was set to True, but the " + "lower bound of %s is non-integer within the specified " + "tolerance, with value %s. \n" + "Please set do_integer_arithmetic=False, increase " + "integer_tolerance, or make your data integer." % + (cons['body'].to_expression() >= cons['lower'], coef) + ) + def _nonneg_scalar_multiply_linear_constraint(self, cons, scalar): """Multiplies all coefficients and the RHS of a >= constraint by scalar. - There is no logic for flipping the equality, so this is just the + There is no logic for flipping the equality, so this is just the special case with a nonnegative scalar, which is all we need. + + If self.do_integer_arithmetic is True, this assumes that scalar is an + int. It also will throw an error if any data is non-integer (within + tolerance) """ body = cons['body'] - body.linear_coefs = [scalar*coef for coef in body.linear_coefs] + new_coefs = [] + for i, coef in enumerate(body.linear_coefs): + v = body.linear_vars[i] + new_coefs.append(self._multiply( + scalar, coef, self._get_noninteger_coef_error_message, + (v.name, coef) + )) + # update the map + cons['map'][v] = new_coefs[i] + body.linear_coefs = new_coefs + body.quadratic_coefs = [scalar*coef for coef in body.quadratic_coefs] body.nonlinear_expr = scalar*body.nonlinear_expr if \ body.nonlinear_expr is not None else None - # and update the map... (It isn't lovely that I am storing this in two - # places...) - for var, coef in cons['map'].items(): - cons['map'][var] = coef*scalar # assume scalar >= 0 and constraint only has lower bound - if cons['lower'] is not None: - cons['lower'] *= scalar - + lb = cons['lower'] + if lb is not None: + cons['lower'] = self._multiply( + scalar, lb, + self._nonneg_scalar_multiply_linear_constraint_error_msg, + (cons, coef) + ) return cons + def _add_linear_constraints_error_msg(self, cons1, cons2): + return ( + "The do_integer_arithmetic flag was set to True, but while " + "adding %s and %s, encountered a coefficient that is " + "non-integer within the specified tolerance\n" + "Please set do_integer_arithmetic=False, increase " + "integer_tolerance, or make your data integer." % + (cons1['body'].to_expression() >= cons1['lower'], + cons2['body'].to_expression() >= cons2['lower']) + ) + def _add_linear_constraints(self, cons1, cons2): - """Adds two >= constraints""" + """Adds two >= constraints + + Because this is always called after + _nonneg_scalar_multiply_linear_constraint, though it is implemented + more generally. + """ ans = {'lower': None, 'body': None, 'map': ComponentMap()} cons1_body = cons1['body'] cons2_body = cons2['body'] @@ -362,23 +548,28 @@ def _add_linear_constraints(self, cons1, cons2): for v in cons2_body.linear_vars: if v not in seen: all_vars.append(v) - + expr = 0 for var in all_vars: - coef = cons1['map'].get(var, 0) + cons2['map'].get(var, 0) + coef = self._add( + cons1['map'].get(var, 0), cons2['map'].get(var, 0), + self._add_linear_constraints_error_msg, (cons1, cons2)) ans['map'][var] = coef expr += coef*var + # deal with nonlinear stuff if there is any for cons in [cons1_body, cons2_body]: if cons.nonlinear_expr is not None: expr += cons.nonlinear_expr expr += sum(coef*v1*v2 for (coef, (v1, v2)) in - zip(cons.quadratic_coefs, cons.quadratic_vars)) - + zip(cons.quadratic_coefs, cons.quadratic_vars)) + ans['body'] = generate_standard_repn(expr) # upper is None and lower exists, so this gets the constant - ans['lower'] = cons1['lower'] + cons2['lower'] + ans['lower'] = self._add( + cons1['lower'], cons2['lower'], + self._add_linear_constraints_error_msg, (cons1, cons2)) return ans @@ -388,22 +579,22 @@ def post_process_fme_constraints(self, m, solver_factory, tolerance=0): Parameters ---------------- - m: A model, already transformed with FME. Note that if constraints - have been added, activated, or deactivated, we will check for - redundancy against the whole active part of the model. If you call - this straight after FME, you are only checking within the projected + m: A model, already transformed with FME. Note that if constraints + have been added, activated, or deactivated, we will check for + redundancy against the whole active part of the model. If you call + this straight after FME, you are only checking within the projected constraints, but otherwise it is up to the user. - solver_factory: A SolverFactory object (constructed with a solver - which can solve the continuous relaxation of the - active constraints on the model. That is, if you - had nonlinear constraints unrelated to the variables - being projected, you need to either deactivate them or + solver_factory: A SolverFactory object (constructed with a solver + which can solve the continuous relaxation of the + active constraints on the model. That is, if you + had nonlinear constraints unrelated to the variables + being projected, you need to either deactivate them or provide a solver which will do the right thing.) tolerance: Tolerance at which we decide a constraint is implied by the - others. Default is 0, meaning we remove the constraint if - the LP solve finds the constraint can be tight but not + others. Default is 0, meaning we remove the constraint if + the LP solve finds the constraint can be tight but not violated. Setting this to a small positive value would - remove constraints more conservatively. Setting it to a + remove constraints more conservatively. Setting it to a negative value would result in a relaxed problem. """ # make sure m looks like what we expect @@ -414,7 +605,7 @@ def post_process_fme_constraints(self, m, solver_factory, tolerance=0): % m.name) transBlock = m._pyomo_contrib_fme_transformation constraints = transBlock.projected_constraints - + # relax integrality so that we can do this with LP solves. TransformationFactory('core.relax_integer_vars').apply_to( m, transform_deactivated_blocks=True) @@ -449,7 +640,7 @@ def post_process_fme_constraints(self, m, solver_factory, tolerance=0): raise RuntimeError("Unsuccessful subproblem solve when checking" "constraint %s.\n\t" "Termination Condition: %s" % - (constraints[i].name, + (constraints[i].name, results.solver.termination_condition)) else: obj_val = value(obj) diff --git a/pyomo/contrib/fme/plugins.py b/pyomo/contrib/fme/plugins.py index 73e6acc24ce..a992684a81a 100644 --- a/pyomo/contrib/fme/plugins.py +++ b/pyomo/contrib/fme/plugins.py @@ -1,2 +1,12 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + def load(): import pyomo.contrib.fme.fourier_motzkin_elimination diff --git a/pyomo/contrib/fme/tests/test_fourier_motzkin_elimination.py b/pyomo/contrib/fme/tests/test_fourier_motzkin_elimination.py index 61f7df8f734..a28563221f1 100644 --- a/pyomo/contrib/fme/tests/test_fourier_motzkin_elimination.py +++ b/pyomo/contrib/fme/tests/test_fourier_motzkin_elimination.py @@ -78,6 +78,7 @@ def test_no_vars_specified(self): def check_projected_constraints(self, m, indices): constraints = m._pyomo_contrib_fme_transformation.projected_constraints + # x - 0.01y <= 1 cons = constraints[indices[0]] self.assertEqual(value(cons.lower), -1) @@ -100,10 +101,10 @@ def check_projected_constraints(self, m, indices): linear_vars = body.linear_vars coefs = body.linear_coefs self.assertEqual(len(linear_vars), 2) - self.assertIs(linear_vars[0], m.y) - self.assertEqual(coefs[0], -1) - self.assertIs(linear_vars[1], m.u[1]) - self.assertEqual(coefs[1], -1000) + self.assertIs(linear_vars[0], m.u[1]) + self.assertEqual(coefs[0], -1000) + self.assertIs(linear_vars[1], m.y) + self.assertEqual(coefs[1], -1) # -x + 0.01y + 1 <= 1000*(1 - u_2) cons = constraints[indices[2]] @@ -113,12 +114,12 @@ def check_projected_constraints(self, m, indices): linear_vars = body.linear_vars coefs = body.linear_coefs self.assertEqual(len(linear_vars), 3) - self.assertIs(linear_vars[0], m.x) - self.assertEqual(coefs[0], 1) - self.assertIs(linear_vars[1], m.y) - self.assertEqual(coefs[1], -0.01) - self.assertIs(linear_vars[2], m.u[2]) - self.assertEqual(coefs[2], -1000) + self.assertIs(linear_vars[0], m.u[2]) + self.assertEqual(coefs[0], -1000) + self.assertIs(linear_vars[1], m.x) + self.assertEqual(coefs[1], 1) + self.assertIs(linear_vars[2], m.y) + self.assertEqual(coefs[2], -0.01) # u_2 + 100u_1 >= 1 cons = constraints[indices[3]] @@ -258,8 +259,7 @@ def not_a_callback(cons): raise RuntimeError("I don't know how to do my job.") fme = TransformationFactory('contrib.fourier_motzkin_elimination') log = StringIO() - with LoggingIntercept(log, 'pyomo.contrib.fourier_motzkin_elimination', - logging.ERROR): + with LoggingIntercept(log, 'pyomo.contrib.fme', logging.ERROR): self.assertRaisesRegexp( RuntimeError, "I don't know how to do my job.", @@ -276,8 +276,7 @@ def test_constraint_filtering_callback_not_callable_error(self): m = self.makeModel() fme = TransformationFactory('contrib.fourier_motzkin_elimination') log = StringIO() - with LoggingIntercept(log, 'pyomo.contrib.fourier_motzkin_elimination', - logging.ERROR): + with LoggingIntercept(log, 'pyomo.contrib.fme', logging.ERROR): self.assertRaisesRegexp( TypeError, "'int' object is not callable", @@ -300,7 +299,7 @@ def test_combine_three_inequalities_and_flatten_blocks(self): m.b.b2 = Block() m.b.b2.c = Constraint(expr=m.y >= 4) TransformationFactory('contrib.fourier_motzkin_elimination').apply_to( - m, vars_to_eliminate=m.y) + m, vars_to_eliminate=m.y, do_integer_arithmetic=True) constraints = m._pyomo_contrib_fme_transformation.projected_constraints self.assertEqual(len(constraints), 2) @@ -323,10 +322,10 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 2) self.assertTrue(body.is_linear()) - self.assertIs(body.linear_vars[0], m.p[1]) - self.assertEqual(body.linear_coefs[0], 1) - self.assertIs(body.linear_vars[1], m.on.indicator_var) - self.assertEqual(body.linear_coefs[1], -1) + self.assertIs(body.linear_vars[0], m.on.indicator_var) + self.assertEqual(body.linear_coefs[0], -1) + self.assertIs(body.linear_vars[1], m.p[1]) + self.assertEqual(body.linear_coefs[1], 1) # p[1] <= 10*on.ind_var + 10*off.ind_var cons = constraints[indices[1]] @@ -336,9 +335,9 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) - self.assertIs(body.linear_vars[0], m.on.indicator_var) + self.assertIs(body.linear_vars[0], m.off.indicator_var) self.assertEqual(body.linear_coefs[0], 10) - self.assertIs(body.linear_vars[1], m.off.indicator_var) + self.assertIs(body.linear_vars[1], m.on.indicator_var) self.assertEqual(body.linear_coefs[1], 10) self.assertIs(body.linear_vars[2], m.p[1]) self.assertEqual(body.linear_coefs[2], -1) @@ -364,10 +363,10 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 2) self.assertTrue(body.is_linear()) - self.assertIs(body.linear_vars[0], m.time1_disjuncts[0].indicator_var) - self.assertEqual(body.linear_coefs[0], 10) - self.assertIs(body.linear_vars[1], m.p[1]) - self.assertEqual(body.linear_coefs[1], -1) + self.assertIs(body.linear_vars[0], m.p[1]) + self.assertEqual(body.linear_coefs[0], -1) + self.assertIs(body.linear_vars[1], m.time1_disjuncts[0].indicator_var) + self.assertEqual(body.linear_coefs[1], 10) # p[2] - p[1] <= 3*on.ind_var + 2*startup.ind_var cons = constraints[indices[4]] @@ -377,14 +376,14 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 4) self.assertTrue(body.is_linear()) - self.assertIs(body.linear_vars[3], m.p[2]) - self.assertEqual(body.linear_coefs[3], -1) - self.assertIs(body.linear_vars[0], m.p[1]) - self.assertEqual(body.linear_coefs[0], 1) - self.assertIs(body.linear_vars[1], m.on.indicator_var) - self.assertEqual(body.linear_coefs[1], 3) - self.assertIs(body.linear_vars[2], m.startup.indicator_var) - self.assertEqual(body.linear_coefs[2], 2) + self.assertIs(body.linear_vars[0], m.on.indicator_var) + self.assertEqual(body.linear_coefs[0], 3) + self.assertIs(body.linear_vars[1], m.p[1]) + self.assertEqual(body.linear_coefs[1], 1) + self.assertIs(body.linear_vars[2], m.p[2]) + self.assertEqual(body.linear_coefs[2], -1) + self.assertIs(body.linear_vars[3], m.startup.indicator_var) + self.assertEqual(body.linear_coefs[3], 2) # p[2] >= on.ind_var + startup.ind_var cons = constraints[indices[5]] @@ -394,11 +393,11 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) - self.assertIs(body.linear_vars[0], m.p[2]) - self.assertEqual(body.linear_coefs[0], 1) - self.assertIs(body.linear_vars[1], m.startup.indicator_var) - self.assertEqual(body.linear_coefs[1], -1) - self.assertIs(body.linear_vars[2], m.on.indicator_var) + self.assertIs(body.linear_vars[0], m.on.indicator_var) + self.assertEqual(body.linear_coefs[0], -1) + self.assertIs(body.linear_vars[1], m.p[2]) + self.assertEqual(body.linear_coefs[1], 1) + self.assertIs(body.linear_vars[2], m.startup.indicator_var) self.assertEqual(body.linear_coefs[2], -1) # p[2] <= 10*on.ind_var + 2*startup.ind_var @@ -411,10 +410,10 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.on.indicator_var) self.assertEqual(body.linear_coefs[0], 10) - self.assertIs(body.linear_vars[1], m.startup.indicator_var) - self.assertEqual(body.linear_coefs[1], 2) - self.assertIs(body.linear_vars[2], m.p[2]) - self.assertEqual(body.linear_coefs[2], -1) + self.assertIs(body.linear_vars[1], m.p[2]) + self.assertEqual(body.linear_coefs[1], -1) + self.assertIs(body.linear_vars[2], m.startup.indicator_var) + self.assertEqual(body.linear_coefs[2], 2) # 1 <= time1_disjuncts[0].ind_var + time_1.disjuncts[1].ind_var cons = constraints[indices[7]] @@ -450,13 +449,13 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) - self.assertIs(body.linear_vars[0], m.on.indicator_var) + self.assertIs(body.linear_vars[0], m.off.indicator_var) self.assertEqual(body.linear_coefs[0], 1) - self.assertIs(body.linear_vars[1], m.startup.indicator_var) + self.assertIs(body.linear_vars[1], m.on.indicator_var) self.assertEqual(body.linear_coefs[1], 1) - self.assertIs(body.linear_vars[2], m.off.indicator_var) + self.assertIs(body.linear_vars[2], m.startup.indicator_var) self.assertEqual(body.linear_coefs[2], 1) - + # 1 >= on.ind_var + startup.ind_var + off.ind_var cons = constraints[indices[10]] self.assertEqual(cons.lower, -1) @@ -465,11 +464,11 @@ def check_hull_projected_constraints(self, m, constraints, indices): self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) - self.assertIs(body.linear_vars[0], m.on.indicator_var) + self.assertIs(body.linear_vars[0], m.off.indicator_var) self.assertEqual(body.linear_coefs[0], -1) - self.assertIs(body.linear_vars[1], m.startup.indicator_var) + self.assertIs(body.linear_vars[1], m.on.indicator_var) self.assertEqual(body.linear_coefs[1], -1) - self.assertIs(body.linear_vars[2], m.off.indicator_var) + self.assertIs(body.linear_vars[2], m.startup.indicator_var) self.assertEqual(body.linear_coefs[2], -1) def create_hull_model(self): @@ -518,28 +517,28 @@ def test_project_disaggregated_vars(self): create_using(m, vars_to_eliminate=disaggregatedVars) TransformationFactory('contrib.fourier_motzkin_elimination').apply_to( m, vars_to_eliminate=disaggregatedVars, - constraint_filtering_callback=None) + constraint_filtering_callback=None, do_integer_arithmetic=True) constraints = m._pyomo_contrib_fme_transformation.projected_constraints # we of course get tremendous amounts of garbage, but we make sure that # what should be here is: - self.check_hull_projected_constraints(m, constraints, [21, 16, 57, 59, - 55, 33, 27, 1, 2, - 4, 5]) + self.check_hull_projected_constraints(m, constraints, [16, 11, 57, 59, + 46, 48, 27, 1, 2, + 4, 5]) # and when we filter, it's still there. constraints = filtered._pyomo_contrib_fme_transformation.\ projected_constraints - constraints.pprint() self.check_hull_projected_constraints(filtered, constraints, [6, 5, 16, - 17, 15, - 9, 8, 1, - 2, 3, 4]) + 17, 12, + 13, 8, 1, + 2, 3, 4]) @unittest.skipIf(not 'glpk' in solvers, 'glpk not available') def test_post_processing(self): m, disaggregatedVars = self.create_hull_model() fme = TransformationFactory('contrib.fourier_motzkin_elimination') - fme.apply_to(m, vars_to_eliminate=disaggregatedVars) + fme.apply_to(m, vars_to_eliminate=disaggregatedVars, + do_integer_arithmetic=True) # post-process fme.post_process_fme_constraints(m, SolverFactory('glpk')) @@ -548,9 +547,9 @@ def test_post_processing(self): # They should be the same as the above, but now these are *all* the # constraints - self.check_hull_projected_constraints(m, constraints, [6, 5, 16, 17, - 15, 9, 8, 1, 2, - 3, 4]) + self.check_hull_projected_constraints(m, constraints, [6, 5, 16, 17, 12, + 13, 8, 1, 2, 3, + 4]) # and check that we didn't change the model for disj in m.component_data_objects(Disjunct): @@ -582,11 +581,11 @@ def cons(m, i): constraints = m._pyomo_contrib_fme_transformation.projected_constraints # 0 <= y <= 3 - cons = constraints[6] - self.assertEqual(cons.lower, 0) - self.assertIs(cons.body, m.y) cons = constraints[5] - self.assertEqual(cons.lower, -3) + self.assertEqual(value(cons.lower), 0) + self.assertIs(cons.body, m.y) + cons = constraints[6] + self.assertEqual(value(cons.lower), -3) body = generate_standard_repn(cons.body) self.assertTrue(body.is_linear()) self.assertEqual(len(body.linear_vars), 1) @@ -594,8 +593,8 @@ def cons(m, i): self.assertEqual(body.linear_coefs[0], -1) # z <= y**2 + 3 - cons = constraints[4] - self.assertEqual(cons.lower, -3) + cons = constraints[2] + self.assertEqual(value(cons.lower), -3) body = generate_standard_repn(cons.body) self.assertTrue(body.is_quadratic()) self.assertEqual(len(body.linear_vars), 1) @@ -607,7 +606,7 @@ def cons(m, i): self.assertIs(body.quadratic_vars[0][1], m.y) # z <= 6 - cons = constraints[2] + cons = constraints[4] self.assertEqual(cons.lower, -6) body = generate_standard_repn(cons.body) self.assertTrue(body.is_linear()) @@ -617,7 +616,7 @@ def cons(m, i): # 0 <= ln(y+ 1) cons = constraints[1] - self.assertEqual(cons.lower, 0) + self.assertEqual(value(cons.lower), 0) body = generate_standard_repn(cons.body) self.assertTrue(body.is_nonlinear()) self.assertFalse(body.is_quadratic()) @@ -629,7 +628,7 @@ def cons(m, i): # 0 <= y**2 cons = constraints[3] - self.assertEqual(cons.lower, 0) + self.assertEqual(value(cons.lower), 0) body = generate_standard_repn(cons.body) self.assertTrue(body.is_quadratic()) self.assertEqual(len(body.quadratic_vars), 1) @@ -652,7 +651,7 @@ def cons(m, i): # check post process these are non-convex, so I don't want to deal with # it... (and this is a good test that I *don't* deal with it.) - constraints[4].deactivate() + constraints[2].deactivate() constraints[3].deactivate() constraints[1].deactivate() # NOTE also that some of the suproblems in this test are unbounded: We @@ -670,4 +669,239 @@ def cons(m, i): # now we should have lost one constraint self.assertEqual(len(constraints), 5) # and it should be the y <= 3 one... - self.assertIsNone(dict(constraints).get(5)) + self.assertIsNone(dict(constraints).get(6)) + + @unittest.skipIf(not 'glpk' in solvers, 'glpk not available') + def test_noninteger_coefficients_of_vars_being_projected_error(self): + m = ConcreteModel() + m.x = Var(bounds=(0,9)) + m.y = Var(bounds=(-5, 5)) + m.c1 = Constraint(expr=2*m.x + 0.5*m.y >= 2) + m.c2 = Constraint(expr=0.25*m.y >= 0.5*m.x) + + fme = TransformationFactory('contrib.fourier_motzkin_elimination') + self.assertRaisesRegexp( + ValueError, + "The do_integer_arithmetic flag was " + "set to True, but the coefficient of " + "x is non-integer within the specified tolerance, " + "with value -0.5. \n" + "Please set do_integer_arithmetic=" + "False, increase integer_tolerance, or make your data integer.", + fme.apply_to, + m, + vars_to_eliminate=m.x, + do_integer_arithmetic=True) + + @unittest.skipIf(not 'glpk' in solvers, 'glpk not available') + def test_noninteger_coefficients_of_vars_not_being_projected_error(self): + m = ConcreteModel() + m.x = Var(bounds=(0,9)) + m.y = Var(bounds=(-5, 5)) + m.c1 = Constraint(expr=2*m.x + 0.5*m.y >= 2) + m.c2 = Constraint(expr=0.25*m.y >= 5*m.x) + + fme = TransformationFactory('contrib.fourier_motzkin_elimination') + self.assertRaisesRegexp( + ValueError, + "The do_integer_arithmetic flag was " + "set to True, but the coefficient of " + "y is non-integer within the specified tolerance, " + "with value 0.5. \n" + "Please set do_integer_arithmetic=" + "False, increase integer_tolerance, or make your data integer.", + fme.apply_to, + m, + vars_to_eliminate=m.x, + do_integer_arithmetic=True) + + def test_integer_arithmetic_non1_coefficients(self): + m = ConcreteModel() + m.x = Var(bounds=(0,9)) + m.y = Var(bounds=(-5, 5)) + m.c1 = Constraint(expr=4*m.x + m.y >= 4) + m.c2 = Constraint(expr=m.y >= 2*m.x) + + fme = TransformationFactory('contrib.fourier_motzkin_elimination') + + fme.apply_to( m, vars_to_eliminate=m.x, + constraint_filtering_callback=None, + do_integer_arithmetic=True, verbose=True) + + constraints = m._pyomo_contrib_fme_transformation.projected_constraints + + self.assertEqual(len(constraints), 3) + + cons = constraints[3] + self.assertEqual(value(cons.lower), -32) + self.assertIs(cons.body, m.y) + self.assertIsNone(cons.upper) + + cons = constraints[2] + self.assertEqual(value(cons.lower), 0) + self.assertIsNone(cons.upper) + repn = generate_standard_repn(cons.body) + self.assertTrue(repn.is_linear()) + self.assertEqual(len(repn.linear_coefs), 1) + self.assertIs(repn.linear_vars[0], m.y) + self.assertEqual(repn.linear_coefs[0], 2) + + cons = constraints[1] + self.assertEqual(value(cons.lower), 4) + self.assertIsNone(cons.upper) + repn = generate_standard_repn(cons.body) + self.assertTrue(repn.is_linear()) + self.assertEqual(len(repn.linear_coefs), 1) + self.assertIs(repn.linear_vars[0], m.y) + self.assertEqual(repn.linear_coefs[0], 3) + + def test_numerical_instability_almost_canceling(self): + # It's possible that we get almost-but-not-quite zero on the variable + # being eliminated when we are doing this with floating point + # arithmetic. This can get ugly later becuase it might get muliplied by + # a large number later and start to "reappear" + m = ConcreteModel() + m.x = Var() + m.x0 = Var() + m.y = Var() + + m.cons1 = Constraint(expr=(1.342 + 2.371e-8)*m.x0 <= m.x + 17*m.y) + m.cons2 = Constraint(expr=(17.56 + 3.2e-7)*m.x0 >= m.y) + + fme = TransformationFactory('contrib.fourier_motzkin_elimination') + + fme.apply_to(m, vars_to_eliminate=[m.x0], verbose=True, + zero_tolerance=1e-9) + + constraints = m._pyomo_contrib_fme_transformation.projected_constraints + + # There's going to be numerical error here, and I can't really help + # it. What I care about is that x0 really is gone. + + useful = constraints[1] + repn = generate_standard_repn(useful.body) + self.assertTrue(repn.is_linear()) + self.assertEqual(len(repn.linear_coefs), 2) # this is the real test + self.assertEqual(useful.lower, 0) + self.assertIs(repn.linear_vars[0], m.x) + self.assertAlmostEqual(repn.linear_coefs[0], 0.7451564696962295) + self.assertIs(repn.linear_vars[1], m.y) + self.assertAlmostEqual(repn.linear_coefs[1], 12.610712377673217) + self.assertEqual(repn.constant, 0) + self.assertIsNone(useful.upper) + + def test_numerical_instability_early_elimination(self): + # A more subtle numerical problem is that, in infinite precision, a + # variable might be eliminated early. However, if this goes wrong, the + # result can be unexpected (including getting no constraints when some + # are expected.) + m = ConcreteModel() + m.x = Var() + m.x0 = Var() + m.y = Var() + + # we'll pretend that the 1.123e-9 is noise from previous calculations + m.cons1 = Constraint(expr=0 <= (4.27 + 1.123e-9)*m.x + 13*m.y - m.x0) + m.cons2 = Constraint(expr=m.x0 >= 12*m.y + 4.27*m.x) + + fme = TransformationFactory('contrib.fourier_motzkin_elimination') + + # doing my own clones because I want assertIs tests + first = m.clone() + second = m.clone() + third = m.clone() + + fme.apply_to(first, vars_to_eliminate=[first.x0], zero_tolerance=1e-10) + constraints = first._pyomo_contrib_fme_transformation.\ + projected_constraints + cons = constraints[1] + self.assertEqual(cons.lower, 0) + repn = generate_standard_repn(cons.body) + self.assertTrue(repn.is_linear()) + self.assertEqual(repn.constant, 0) + self.assertEqual(len(repn.linear_coefs), 2) # x is still around + self.assertIs(repn.linear_vars[0], first.x) + self.assertAlmostEqual(repn.linear_coefs[0], 1.123e-9) + self.assertIs(repn.linear_vars[1], first.y) + self.assertEqual(repn.linear_coefs[1], 1) + self.assertIsNone(cons.upper) + + # so just to drive home the point, this results in no constraints: + # (Though also note that that only happens if x0 is the first to be + # projected out) + fme.apply_to(second, vars_to_eliminate=[second.x0, second.x], + zero_tolerance=1e-10) + self.assertEqual(len(second._pyomo_contrib_fme_transformation.\ + projected_constraints), 0) + + # but in this version, we assume that x is already gone... + fme.apply_to(third, vars_to_eliminate=[third.x0], verbose=True, + zero_tolerance=1e-8) + constraints = third._pyomo_contrib_fme_transformation.\ + projected_constraints + cons = constraints[1] + self.assertEqual(cons.lower, 0) + self.assertIs(cons.body, third.y) + self.assertIsNone(cons.upper) + + # and this is exactly the same as the above: + fme.apply_to(m, vars_to_eliminate=[m.x0, m.x], verbose=True, + zero_tolerance=1e-8) + constraints = m._pyomo_contrib_fme_transformation.projected_constraints + cons = constraints[1] + self.assertEqual(cons.lower, 0) + self.assertIs(cons.body, m.y) + self.assertIsNone(cons.upper) + + def test_use_all_var_bounds(self): + m = ConcreteModel() + m.b = Block() + m.x = Var(bounds=(0, 15)) + m.y = Var(bounds=(3, 5)) + m.b.c = Constraint(expr=m.x + m.y <= 8) + + fme = TransformationFactory('contrib.fourier_motzkin_elimination') + fme.apply_to(m.b, vars_to_eliminate=[m.y]) + constraints = m.b.\ + _pyomo_contrib_fme_transformation.projected_constraints + + # if we hadn't included y's bounds, then we wouldn't get any constraints + # and y wouldn't be eliminated. If we do include y's bounds, we get new + # information that x <= 5: + self.assertEqual(len(constraints), 1) + cons = constraints[1] + self.assertEqual(value(cons.lower), -5) + self.assertIsNone(cons.upper) + repn = generate_standard_repn(cons.body) + self.assertEqual(repn.constant, 0) + self.assertEqual(len(repn.linear_vars), 1) + self.assertIs(repn.linear_vars[0], m.x) + self.assertEqual(repn.linear_coefs[0], -1) + + def test_simple_hull_example(self): + m = ConcreteModel() + m.x0 = Var(bounds=(0,3)) + m.x1 = Var(bounds=(0,3)) + m.x = Var(bounds=(0,3)) + m.disaggregation = Constraint(expr=m.x == m.x0 + m.x1) + m.y = Var(domain=Binary) + m.cons = Constraint(expr=2*m.y <= m.x1) + + fme = TransformationFactory('contrib.fourier_motzkin_elimination') + fme.apply_to(m, vars_to_eliminate=[m.x0, m.x1]) + + constraints = m._pyomo_contrib_fme_transformation.projected_constraints + constraints.pprint() + + self.assertEqual(len(constraints), 1) + cons = constraints[1] + self.assertIsNone(cons.upper) + self.assertEqual(value(cons.lower), 0) + repn = generate_standard_repn(cons.body) + self.assertEqual(repn.constant, 0) + self.assertEqual(len(repn.linear_vars), 2) + self.assertIs(repn.linear_vars[0], m.x) + self.assertEqual(repn.linear_coefs[0], 1) + self.assertIs(repn.linear_vars[1], m.y) + self.assertEqual(repn.linear_coefs[1], -2) + self.assertTrue(repn.is_linear()) diff --git a/pyomo/contrib/gdp_bounds/compute_bounds.py b/pyomo/contrib/gdp_bounds/compute_bounds.py index 7d0a030b501..58f5655117b 100644 --- a/pyomo/contrib/gdp_bounds/compute_bounds.py +++ b/pyomo/contrib/gdp_bounds/compute_bounds.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Provides functions to compute and enforce disjunctive variable bounds. These are tighter variable bounds that are valid within the scope of a certain @@ -15,7 +25,7 @@ from pyomo.core.base.block import Block, TraversalStrategy from pyomo.core.expr.current import identify_variables from pyomo.core import (Constraint, Objective, - TransformationFactory, maximize, minimize, value) + TransformationFactory, minimize, value) from pyomo.opt import SolverFactory from pyomo.gdp.disjunct import Disjunct from pyomo.core.plugins.transform.hierarchy import Transformation diff --git a/pyomo/contrib/gdp_bounds/plugins.py b/pyomo/contrib/gdp_bounds/plugins.py index 541dca352be..ca43eca54cb 100644 --- a/pyomo/contrib/gdp_bounds/plugins.py +++ b/pyomo/contrib/gdp_bounds/plugins.py @@ -1,2 +1,12 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + def load(): import pyomo.contrib.gdp_bounds.compute_bounds diff --git a/pyomo/contrib/gdpopt/branch_and_bound.py b/pyomo/contrib/gdpopt/branch_and_bound.py index ad93f69b8e7..52b3e6d4d39 100644 --- a/pyomo/contrib/gdpopt/branch_and_bound.py +++ b/pyomo/contrib/gdpopt/branch_and_bound.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import traceback from collections import namedtuple from heapq import heappush, heappop diff --git a/pyomo/contrib/gdpopt/cut_generation.py b/pyomo/contrib/gdpopt/cut_generation.py index f775fed2185..a9ef71daac4 100644 --- a/pyomo/contrib/gdpopt/cut_generation.py +++ b/pyomo/contrib/gdpopt/cut_generation.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """This module provides functions for cut generation.""" from __future__ import division @@ -12,7 +22,6 @@ minimize, value, TransformationFactory) from pyomo.core.expr import differentiate from pyomo.core.expr.visitor import identify_variables -from pyomo.gdp import Disjunct MAX_SYMBOLIC_DERIV_SIZE = 1000 JacInfo = namedtuple('JacInfo', ['mode','vars','jac']) diff --git a/pyomo/contrib/gdpopt/mip_solve.py b/pyomo/contrib/gdpopt/mip_solve.py index 786914b34bd..c8a62bd7db8 100644 --- a/pyomo/contrib/gdpopt/mip_solve.py +++ b/pyomo/contrib/gdpopt/mip_solve.py @@ -82,12 +82,14 @@ def solve_linear_GDP(linear_GDP_model, solve_data, config): mip_args['add_options'] = mip_args.get('add_options', []) mip_args['add_options'].append('option reslim=%s;' % remaining) elif config.mip_solver == 'multisolve': - mip_args['time_limit'] = min(mip_args.get('time_limit', float('inf')), remaining) + mip_args['time_limit'] = min(mip_args.get( + 'time_limit', float('inf')), remaining) results = SolverFactory(config.mip_solver).solve( m, **mip_args) except RuntimeError as e: if 'GAMS encountered an error during solve.' in str(e): - config.logger.warning("GAMS encountered an error in solve. Treating as infeasible.") + config.logger.warning( + "GAMS encountered an error in solve. Treating as infeasible.") mip_result = MasterProblemResult() mip_result.feasible = False mip_result.var_values = list(v.value for v in GDPopt.variable_list) @@ -115,7 +117,8 @@ def solve_linear_GDP(linear_GDP_model, solve_data, config): 'Resolving with arbitrary bound values of (-{0:.10g}, {0:.10g}) on the objective. ' 'Check your initialization routine.'.format(obj_bound)) main_objective = next(m.component_data_objects(Objective, active=True)) - GDPopt.objective_bound = Constraint(expr=(-obj_bound, main_objective.expr, obj_bound)) + GDPopt.objective_bound = Constraint( + expr=(-obj_bound, main_objective.expr, obj_bound)) with SuppressInfeasibleWarning(): results = SolverFactory(config.mip_solver).solve( m, **config.mip_solver_args) @@ -129,7 +132,7 @@ def solve_linear_GDP(linear_GDP_model, solve_data, config): mip_result.disjunct_values = list( disj.indicator_var.value for disj in GDPopt.disjunct_list) - if terminate_cond is tc.optimal or terminate_cond is tc.locallyOptimal: + if terminate_cond in {tc.optimal, tc.locallyOptimal, tc.feasible}: pass elif terminate_cond is tc.infeasible: config.logger.info( diff --git a/pyomo/contrib/gdpopt/nlp_solve.py b/pyomo/contrib/gdpopt/nlp_solve.py index 4516bf613ed..317e00f76a3 100644 --- a/pyomo/contrib/gdpopt/nlp_solve.py +++ b/pyomo/contrib/gdpopt/nlp_solve.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Functions for solving the nonlinear subproblem.""" from __future__ import division @@ -5,7 +15,6 @@ from pyomo.common.collections import ComponentSet from pyomo.common.errors import InfeasibleConstraintException -from pyomo.contrib.fbbt.fbbt import fbbt from pyomo.contrib.gdpopt.data_class import SubproblemResult from pyomo.contrib.gdpopt.util import (SuppressInfeasibleWarning, is_feasible, get_main_elapsed_time) diff --git a/pyomo/contrib/gdpopt/plugins.py b/pyomo/contrib/gdpopt/plugins.py index 853eb9a932f..f4dc4bfa917 100644 --- a/pyomo/contrib/gdpopt/plugins.py +++ b/pyomo/contrib/gdpopt/plugins.py @@ -1,2 +1,12 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + def load(): import pyomo.contrib.gdpopt.GDPopt diff --git a/pyomo/contrib/gdpopt/tests/test_gdpopt.py b/pyomo/contrib/gdpopt/tests/test_gdpopt.py index 50a2f370cc2..4e4f99872e7 100644 --- a/pyomo/contrib/gdpopt/tests/test_gdpopt.py +++ b/pyomo/contrib/gdpopt/tests/test_gdpopt.py @@ -1,7 +1,17 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Tests for the GDPopt solver plugin.""" import logging from math import fabs -from os.path import abspath, dirname, join, normpath +from os.path import join, normpath from six import StringIO diff --git a/pyomo/contrib/gdpopt/util.py b/pyomo/contrib/gdpopt/util.py index 4f8c618fcda..0234c27e06b 100644 --- a/pyomo/contrib/gdpopt/util.py +++ b/pyomo/contrib/gdpopt/util.py @@ -155,8 +155,10 @@ def process_objective(solve_data, config, move_linear_objective=False, use_mcpp= else: # Use Pyomo's contrib.fbbt package lb, ub = compute_bounds_on_expr(main_obj.expr) - util_blk.objective_value.setub(ub) - util_blk.objective_value.setlb(lb) + if solve_data.results.problem.sense == ProblemSense.minimize: + util_blk.objective_value.setlb(lb) + else: + util_blk.objective_value.setub(ub) if main_obj.sense == minimize: util_blk.objective_constr = Constraint( diff --git a/pyomo/contrib/interior_point/examples/ex1.py b/pyomo/contrib/interior_point/examples/ex1.py index f71c5f27890..1c503153492 100644 --- a/pyomo/contrib/interior_point/examples/ex1.py +++ b/pyomo/contrib/interior_point/examples/ex1.py @@ -1,4 +1,14 @@ -import pyomo.environ as pe +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import pyomo.environ as pyo from pyomo.contrib.interior_point.interior_point import InteriorPointSolver from pyomo.contrib.interior_point.interface import InteriorPointInterface from pyomo.contrib.interior_point.linalg.mumps_interface import MumpsInterface @@ -7,20 +17,20 @@ logging.basicConfig(level=logging.INFO) # Supposedly this sets the root logger's level to INFO. -# But when linear_solver.logger logs with debug, +# But when linear_solver.logger logs with debug, # it gets propagated to a mysterious root logger with # level NOTSET... -m = pe.ConcreteModel() -m.x = pe.Var() -m.y = pe.Var() -m.obj = pe.Objective(expr=m.x**2 + m.y**2) -m.c1 = pe.Constraint(expr=m.y == pe.exp(m.x)) -m.c2 = pe.Constraint(expr=m.y >= (m.x - 1)**2) +m = pyo.ConcreteModel() +m.x = pyo.Var() +m.y = pyo.Var() +m.obj = pyo.Objective(expr=m.x**2 + m.y**2) +m.c1 = pyo.Constraint(expr=m.y == pyo.exp(m.x)) +m.c2 = pyo.Constraint(expr=m.y >= (m.x - 1)**2) interface = InteriorPointInterface(m) linear_solver = MumpsInterface( -# log_filename='lin_sol.log', - icntl_options={11: 1}, # Set error level to 1 (most detailed) + # log_filename='lin_sol.log', + icntl_options={11: 1}, # Set error level to 1 (most detailed) ) ip_solver = InteriorPointSolver(linear_solver) diff --git a/pyomo/contrib/interior_point/inverse_reduced_hessian.py b/pyomo/contrib/interior_point/inverse_reduced_hessian.py index 91a9389e681..97390dee9c0 100644 --- a/pyomo/contrib/interior_point/inverse_reduced_hessian.py +++ b/pyomo/contrib/interior_point/inverse_reduced_hessian.py @@ -1,10 +1,22 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyomo.environ as pyo from pyomo.opt import check_optimal_termination from pyomo.common.dependencies import attempt_import from .interface import InteriorPointInterface from .linalg.scipy_interface import ScipyInterface -np, numpy_available = attempt_import('numpy', 'Interior point requires numpy', minimum_version='1.13.0') +np, numpy_available = attempt_import('numpy', + 'Interior point requires numpy', + minimum_version='1.13.0') # Todo: This function currently used IPOPT for the initial solve - should accept solver @@ -56,7 +68,7 @@ def inv_reduced_hessian_barrier(model, independent_variables, bound_tolerance=1e # create the ipopt solver solver = pyo.SolverFactory('ipopt') - + # copy additional solver options if solver_options is not None: for key in solver_options: @@ -101,7 +113,7 @@ def inv_reduced_hessian_barrier(model, independent_variables, bound_tolerance=1e # check that none of the independent variables are at their bounds for v in ind_vardatas: if (v.has_lb() and pyo.value(v) - v.lb <= bound_tolerance) or \ - (v.has_ub() and v.ub - pyo.value(b) <= bound_tolerance): + (v.has_ub() and v.ub - pyo.value(v) <= bound_tolerance): raise ValueError("Independent variable: {} has a solution value that is near" " its bound (according to tolerance). The reduced hessian" " computation does not support this at this time. All" diff --git a/pyomo/contrib/interior_point/linalg/mumps_interface.py b/pyomo/contrib/interior_point/linalg/mumps_interface.py index e435cbc0837..0a0d1bcca33 100644 --- a/pyomo/contrib/interior_point/linalg/mumps_interface.py +++ b/pyomo/contrib/interior_point/linalg/mumps_interface.py @@ -1,9 +1,19 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from .base_linear_solver_interface import LinearSolverInterface from .results import LinearSolverStatus, LinearSolverResults from pyomo.common.dependencies import attempt_import from scipy.sparse import isspmatrix_coo, tril from collections import OrderedDict -import logging + mumps, mumps_available = attempt_import(name='pyomo.contrib.pynumero.linalg.mumps_interface', error_message='pymumps is required to use the MumpsInterface') diff --git a/pyomo/contrib/interior_point/tests/test_interior_point.py b/pyomo/contrib/interior_point/tests/test_interior_point.py index 333ba92d158..ff896a43692 100644 --- a/pyomo/contrib/interior_point/tests/test_interior_point.py +++ b/pyomo/contrib/interior_point/tests/test_interior_point.py @@ -1,5 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.common.dependencies import attempt_import np, numpy_available = attempt_import('numpy', 'Interior point requires numpy', minimum_version='1.13.0') @@ -36,12 +46,12 @@ @unittest.skipIf(not asl_available, 'asl is not available') class TestSolveInteriorPoint(unittest.TestCase): def _test_solve_interior_point_1(self, linear_solver): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.obj = pe.Objective(expr=m.x**2 + m.y**2) - m.c1 = pe.Constraint(expr=m.y == pe.exp(m.x)) - m.c2 = pe.Constraint(expr=m.y >= (m.x - 1)**2) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.obj = pyo.Objective(expr=m.x**2 + m.y**2) + m.c1 = pyo.Constraint(expr=m.y == pyo.exp(m.x)) + m.c2 = pyo.Constraint(expr=m.y >= (m.x - 1)**2) interface = InteriorPointInterface(m) ip_solver = InteriorPointSolver(linear_solver) status = ip_solver.solve(interface) @@ -58,9 +68,9 @@ def _test_solve_interior_point_1(self, linear_solver): self.assertAlmostEqual(m.y.value, 1) def _test_solve_interior_point_2(self, linear_solver): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(1, 4)) - m.obj = pe.Objective(expr=m.x**2) + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(1, 4)) + m.obj = pyo.Objective(expr=m.x**2) interface = InteriorPointInterface(m) ip_solver = InteriorPointSolver(linear_solver) status = ip_solver.solve(interface) diff --git a/pyomo/contrib/interior_point/tests/test_inverse_reduced_hessian.py b/pyomo/contrib/interior_point/tests/test_inverse_reduced_hessian.py index dd6e3d17749..8122b134cf2 100644 --- a/pyomo/contrib/interior_point/tests/test_inverse_reduced_hessian.py +++ b/pyomo/contrib/interior_point/tests/test_inverse_reduced_hessian.py @@ -1,5 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.opt import check_optimal_termination from pyomo.common.dependencies import attempt_import @@ -15,9 +25,9 @@ if not (numpy_available and scipy_available and asl_available): raise unittest.SkipTest('inverse_reduced_hessian tests require numpy, scipy, and asl') -from pyomo.common.dependencies import(pandas as pd, pandas_available) -import pyomo.environ as pe -ipopt_solver = pe.SolverFactory('ipopt') +from pyomo.common.dependencies import (pandas as pd, pandas_available) + +ipopt_solver = pyo.SolverFactory('ipopt') if not ipopt_solver.available(exception_flag=False): raise unittest.SkipTest('ipopt is not available') @@ -32,10 +42,10 @@ class TestInverseReducedHessian(unittest.TestCase): # the original test def test_invrh_zavala_thesis(self): - m = pe.ConcreteModel() - m.x = pe.Var([1,2,3]) - m.obj = pe.Objective(expr=(m.x[1]-1)**2 + (m.x[2]-2)**2 + (m.x[3]-3)**2) - m.c1 = pe.Constraint(expr=m.x[1] + 2*m.x[2] + 3*m.x[3]==0) + m = pyo.ConcreteModel() + m.x = pyo.Var([1,2,3]) + m.obj = pyo.Objective(expr=(m.x[1]-1)**2 + (m.x[2]-2)**2 + (m.x[3]-3)**2) + m.c1 = pyo.Constraint(expr=m.x[1] + 2*m.x[2] + 3*m.x[3]==0) status, invrh = inv_reduced_hessian_barrier(m, [m.x[2], m.x[3]]) expected_invrh = np.asarray([[ 0.35714286, -0.21428571], @@ -66,26 +76,26 @@ def _simple_model(self, add_constraint=False): [18, 1.29, 15.60649164]], columns=['tofu','chard', 'y']) - model = pe.ConcreteModel() + model = pyo.ConcreteModel() - model.b0 = pe.Var(initialize = 0) - model.bindexes = pe.Set(initialize=['tofu', 'chard']) - model.b = pe.Var(model.bindexes, initialize = 1) + model.b0 = pyo.Var(initialize = 0) + model.bindexes = pyo.Set(initialize=['tofu', 'chard']) + model.b = pyo.Var(model.bindexes, initialize = 1) # try to make trouble if add_constraint: - model.binding_constraint = pe.Constraint(expr=model.b0>=10) + model.binding_constraint = pyo.Constraint(expr=model.b0>=10) # The columns need to have unique values (or you get warnings) def response_rule(m, t, c): expr = m.b0 + m.b['tofu']*t + m.b['chard']*c return expr - model.response_function = pe.Expression(data.tofu, data.chard, rule = response_rule) + model.response_function = pyo.Expression(data.tofu, data.chard, rule = response_rule) def SSE_rule(m): return sum((data.y[i] - m.response_function[data.tofu[i], data.chard[i]])**2\ for i in data.index) - model.SSE = pe.Objective(rule = SSE_rule, sense=pe.minimize) + model.SSE = pyo.Objective(rule = SSE_rule, sense=pyo.minimize) return model @@ -95,18 +105,18 @@ def test_3x3_using_linear_regression(self): """ simple linear regression with two x columns, so 3x3 Hessian""" model = self._simple_model() - solver = pe.SolverFactory("ipopt") + solver = pyo.SolverFactory("ipopt") status = solver.solve(model) self.assertTrue(check_optimal_termination(status)) - tstar = [pe.value(model.b0), - pe.value(model.b['tofu']), pe.value(model.b['chard'])] + tstar = [pyo.value(model.b0), + pyo.value(model.b['tofu']), pyo.value(model.b['chard'])] def _ndwrap(x): # wrapper for numdiff call model.b0.fix(x[0]) model.b["tofu"].fix(x[1]) model.b["chard"].fix(x[2]) - rval = pe.value(model.SSE) + rval = pyo.value(model.SSE) return rval H = nd.Hessian(_ndwrap)(tstar) @@ -136,6 +146,6 @@ def test_with_binding_constraint(self): model.b["chard"]]) print("test_with_binding_constraint should see an error raised.") - + if __name__ == '__main__': unittest.main() diff --git a/pyomo/contrib/interior_point/tests/test_reg.py b/pyomo/contrib/interior_point/tests/test_reg.py index 0cfc31c0525..848c94e6d81 100644 --- a/pyomo/contrib/interior_point/tests/test_reg.py +++ b/pyomo/contrib/interior_point/tests/test_reg.py @@ -1,5 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.core.base import ConcreteModel, Var, Constraint, Objective from pyomo.common.dependencies import attempt_import @@ -51,7 +61,7 @@ def make_model_2(): m.x = Var(initialize=0.1, bounds=(0, 1)) m.y = Var(initialize=0.1, bounds=(0, 1)) m.obj = Objective(expr=-m.x**2 - m.y**2) - m.c = Constraint(expr=m.y <= pe.exp(-m.x)) + m.c = Constraint(expr=m.y <= pyo.exp(-m.x)) return m @@ -103,7 +113,7 @@ def _test_regularization_2(self, linear_solver): self.assertEqual(status, InteriorPointStatus.optimal) interface.load_primals_into_pyomo_model() self.assertAlmostEqual(m.x.value, 1) - self.assertAlmostEqual(m.y.value, pe.exp(-1)) + self.assertAlmostEqual(m.y.value, pyo.exp(-1)) @unittest.skipIf(not mumps_available, 'Mumps is not available') def test_mumps_2(self): diff --git a/pyomo/contrib/mcpp/getMCPP.py b/pyomo/contrib/mcpp/getMCPP.py index 32f61571002..051ddb36f7c 100644 --- a/pyomo/contrib/mcpp/getMCPP.py +++ b/pyomo/contrib/mcpp/getMCPP.py @@ -10,7 +10,6 @@ import logging import os -import stat import sys from pyomo.common.download import FileDownloader diff --git a/pyomo/contrib/mindtpy/MindtPy.py b/pyomo/contrib/mindtpy/MindtPy.py index b0c67a84d40..4a07d395517 100644 --- a/pyomo/contrib/mindtpy/MindtPy.py +++ b/pyomo/contrib/mindtpy/MindtPy.py @@ -1,4 +1,15 @@ # -*- coding: utf-8 -*- + +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Implementation of the MindtPy solver. The MindtPy (MINLP Decomposition Toolkit) solver applies a variety of @@ -22,25 +33,23 @@ import logging -from pyomo.common.config import ( - ConfigBlock, ConfigValue, In, PositiveFloat, PositiveInt -) from pyomo.contrib.gdpopt.util import ( - _DoNothing, copy_var_list_values, + copy_var_list_values, create_utility_block, - restore_logger_level, time_code, - setup_results_object, process_objective, a_logger, lower_logger_level_to) + time_code, + setup_results_object, process_objective, lower_logger_level_to) from pyomo.contrib.mindtpy.initialization import MindtPy_initialize_master from pyomo.contrib.mindtpy.iterate import MindtPy_iteration_loop from pyomo.contrib.mindtpy.util import ( MindtPySolveData, model_is_valid ) from pyomo.core import ( - Block, ConstraintList, NonNegativeReals, RangeSet, Set, Suffix, Var, value, - VarList, TransformationFactory) + Block, ConstraintList, NonNegativeReals, Set, Suffix, Var, + VarList, TransformationFactory, Objective) from pyomo.opt import SolverFactory, SolverResults from pyutilib.misc import Container - +from pyomo.contrib.fbbt.fbbt import fbbt +from pyomo.contrib.mindtpy.config_options import _get_GDPopt_config logger = logging.getLogger('pyomo.contrib.mindtpy') @@ -53,214 +62,7 @@ class MindtPySolver(object): """A decomposition-based MINLP solver. """ - - CONFIG = ConfigBlock("MindtPy") - CONFIG.declare("bound_tolerance", ConfigValue( - default=1E-5, - domain=PositiveFloat, - description="Bound tolerance", - doc="Relative tolerance for bound feasibility checks." - )) - CONFIG.declare("iteration_limit", ConfigValue( - default=30, - domain=PositiveInt, - description="Iteration limit", - doc="Number of maximum iterations in the decomposition methods." - )) - CONFIG.declare("time_limit", ConfigValue( - default=600, - domain=PositiveInt, - description="Time limit (seconds, default=600)", - doc="Seconds allowed until terminated. Note that the time limit can" - "currently only be enforced between subsolver invocations. You may" - "need to set subsolver time limits as well." - )) - CONFIG.declare("strategy", ConfigValue( - default="OA", - domain=In(["OA", "GBD", "ECP", "PSC"]), - description="Decomposition strategy", - doc="MINLP Decomposition strategy to be applied to the method. " - "Currently available Outer Approximation (OA), Extended Cutting " - "Plane (ECP), Partial Surrogate Cuts (PSC), and Generalized " - "Benders Decomposition (GBD)." - )) - CONFIG.declare("init_strategy", ConfigValue( - default="rNLP", - domain=In(["rNLP", "initial_binary", "max_binary"]), - description="Initialization strategy", - doc="Initialization strategy used by any method. Currently the " - "continuous relaxation of the MINLP (rNLP), solve a maximal " - "covering problem (max_binary), and fix the initial value for " - "the integer variables (initial_binary)." - )) - CONFIG.declare("max_slack", ConfigValue( - default=1000.0, - domain=PositiveFloat, - description="Maximum slack variable", - doc="Maximum slack variable value allowed for the Outer Approximation " - "cuts." - )) - CONFIG.declare("OA_penalty_factor", ConfigValue( - default=1000.0, - domain=PositiveFloat, - description="Outer Approximation slack penalty factor", - doc="In the objective function of the Outer Approximation method, the " - "slack variables corresponding to all the constraints get " - "multiplied by this number and added to the objective." - )) - CONFIG.declare("ECP_tolerance", ConfigValue( - default=1E-4, - domain=PositiveFloat, - description="ECP tolerance", - doc="Feasibility tolerance used to determine the stopping criterion in" - "the ECP method. As long as nonlinear constraint are violated for " - "more than this tolerance, the method will keep iterating." - )) - CONFIG.declare("nlp_solver", ConfigValue( - default="ipopt", - domain=In(["ipopt", "gams"]), - description="NLP subsolver name", - doc="Which NLP subsolver is going to be used for solving the nonlinear" - "subproblems." - )) - CONFIG.declare("nlp_solver_args", ConfigBlock( - implicit=True, - description="NLP subsolver options", - doc="Which NLP subsolver options to be passed to the solver while " - "solving the nonlinear subproblems." - )) - CONFIG.declare("mip_solver", ConfigValue( - default="glpk", - domain=In(["gurobi", "cplex", "cbc", "glpk", "gams", - "gurobi_persistent", "cplex_persistent"]), - description="MIP subsolver name", - doc="Which MIP subsolver is going to be used for solving the mixed-" - "integer master problems." - )) - CONFIG.declare("mip_solver_args", ConfigBlock( - implicit=True, - description="MIP subsolver options", - doc="Which MIP subsolver options to be passed to the solver while " - "solving the mixed-integer master problems." - )) - CONFIG.declare("call_after_master_solve", ConfigValue( - default=_DoNothing(), - domain=None, - description="Function to be executed after every master problem", - doc="Callback hook after a solution of the master problem." - )) - CONFIG.declare("call_after_subproblem_solve", ConfigValue( - default=_DoNothing(), - domain=None, - description="Function to be executed after every subproblem", - doc="Callback hook after a solution of the nonlinear subproblem." - )) - CONFIG.declare("call_after_subproblem_feasible", ConfigValue( - default=_DoNothing(), - domain=None, - description="Function to be executed after every feasible subproblem", - doc="Callback hook after a feasible solution" - " of the nonlinear subproblem." - )) - CONFIG.declare("tee", ConfigValue( - default=False, - description="Stream output to terminal.", - domain=bool - )) - CONFIG.declare("logger", ConfigValue( - default='pyomo.contrib.mindtpy', - description="The logger object or name to use for reporting.", - domain=a_logger - )) - CONFIG.declare("small_dual_tolerance", ConfigValue( - default=1E-8, - description="When generating cuts, small duals multiplied " - "by expressions can cause problems. Exclude all duals " - "smaller in absolute value than the following." - )) - CONFIG.declare("integer_tolerance", ConfigValue( - default=1E-5, - description="Tolerance on integral values." - )) - CONFIG.declare("constraint_tolerance", ConfigValue( - default=1E-6, - description="Tolerance on constraint satisfaction." - )) - CONFIG.declare("variable_tolerance", ConfigValue( - default=1E-8, - description="Tolerance on variable bounds." - )) - CONFIG.declare("zero_tolerance", ConfigValue( - default=1E-8, - description="Tolerance on variable equal to zero." - )) - CONFIG.declare("initial_feas", ConfigValue( - default=True, - description="Apply an initial feasibility step.", - domain=bool - )) - CONFIG.declare("obj_bound", ConfigValue( - default=1E15, - domain=PositiveFloat, - description="Bound applied to the linearization of the objective function if master MILP is unbounded." - )) - CONFIG.declare("integer_to_binary", ConfigValue( - default=False, - description="Convert integer variables to binaries (for integer cuts).", - domain=bool - )) - CONFIG.declare("add_integer_cuts", ConfigValue( - default=False, - description="Add integer cuts (no-good cuts) to binary variables to disallow same integer solution again." - "Note that 'integer_to_binary' flag needs to be used to apply it to actual integers and not just binaries.", - domain=bool - )) - CONFIG.declare("single_tree", ConfigValue( - default=False, - description="Use single tree implementation in solving the MILP master problem.", - domain=bool - )) - CONFIG.declare("solution_pool", ConfigValue( - default=False, - description="Use solution pool in solving the MILP master problem.", - domain=bool - )) - CONFIG.declare("add_slack", ConfigValue( - default=False, - description="whether add slack variable here." - "slack variables here are used to deal with nonconvex MINLP.", - domain=bool - )) - CONFIG.declare("continuous_var_bound", ConfigValue( - default=1e10, - description="default bound added to unbounded continuous variables in nonlinear constraint if single tree is activated.", - domain=PositiveFloat - )) - CONFIG.declare("integer_var_bound", ConfigValue( - default=1e9, - description="default bound added to unbounded integral variables in nonlinear constraint if single tree is activated.", - domain=PositiveFloat - )) - CONFIG.declare("cycling_check", ConfigValue( - default=True, - description="check if OA algorithm is stalled in a cycle and terminate.", - domain=bool - )) - CONFIG.declare("feasibility_norm", ConfigValue( - default="L_infinity", - domain=In(["L1", "L2", "L_infinity"]), - description="different forms of objective function in feasibility subproblem." - )) - CONFIG.declare("differentiate_mode", ConfigValue( - default="reverse_symbolic", - domain=In(["reverse_symbolic", "sympy"]), - description="differentiate mode to calculate jacobian." - )) - CONFIG.declare("linearize_inactive", ConfigValue( - default=False, - description="Add OA cuts for inactive constraints.", - domain=bool - )) + CONFIG = _get_GDPopt_config() def available(self, exception_flag=True): """Check if solver is available. @@ -288,11 +90,11 @@ def solve(self, model, **kwds): config = self.CONFIG(kwds.pop('options', {})) config.set_value(kwds) - # configration confirmation + # configuration confirmation if config.single_tree: config.iteration_limit = 1 config.add_slack = False - config.add_integer_cuts = False + config.add_nogood_cuts = False config.mip_solver = 'cplex_persistent' config.logger.info( "Single tree implementation is activated. The defalt MIP solver is 'cplex_persistent'") @@ -300,12 +102,36 @@ def solve(self, model, **kwds): if config.max_slack == 0.0: config.add_slack = False + if config.strategy == "GOA": + config.add_nogood_cuts = True + config.add_slack = True + config.use_mcpp = True + config.integer_to_binary = True + config.use_dual = False + config.use_fbbt = True + + if config.nlp_solver == "baron": + config.use_dual = False + # if ecp tolerance is not provided use bound tolerance + if config.ecp_tolerance is None: + config.ecp_tolerance = config.bound_tolerance + + # if the objective function is a constant, dual bound constraint is not added. + obj = next(model.component_data_objects(ctype=Objective, active=True)) + if obj.expr.polynomial_degree() == 0: + config.use_dual_bound = False + solve_data = MindtPySolveData() solve_data.results = SolverResults() solve_data.timing = Container() solve_data.curr_int_sol = [] solve_data.prev_int_sol = [] + if config.use_fbbt: + fbbt(model) + config.logger.info( + "Use the fbbt to tighten the bounds of variables") + solve_data.original_model = model solve_data.working_model = model.clone() if config.integer_to_binary: @@ -320,7 +146,7 @@ def solve(self, model, **kwds): MindtPy = solve_data.working_model.MindtPy_utils setup_results_object(solve_data, config) - process_objective(solve_data, config, use_mcpp=False) + process_objective(solve_data, config, use_mcpp=config.use_mcpp) # Save model initial values. solve_data.initial_var_values = list( @@ -329,6 +155,7 @@ def solve(self, model, **kwds): # Store the initial model state as the best solution found. If we # find no better solution, then we will restore from this copy. solve_data.best_solution_found = None + solve_data.best_solution_found_time = None # Record solver name solve_data.results.solver.name = 'MindtPy' + str(config.strategy) @@ -373,6 +200,10 @@ def solve(self, model, **kwds): solve_data.UB = float('inf') solve_data.LB_progress = [solve_data.LB] solve_data.UB_progress = [solve_data.UB] + if config.single_tree and config.add_nogood_cuts: + solve_data.stored_bound = {} + if config.strategy == 'GOA' and config.add_nogood_cuts: + solve_data.num_no_good_cuts_added = {} # Set of NLP iterations for which cuts were generated lin.nlp_iters = Set(dimen=1) @@ -400,12 +231,13 @@ def solve(self, model, **kwds): # iteration or not solve_data.solution_improved = False - if not hasattr(solve_data.working_model, 'ipopt_zL_out'): - solve_data.working_model.ipopt_zL_out = Suffix( - direction=Suffix.IMPORT) - if not hasattr(solve_data.working_model, 'ipopt_zU_out'): - solve_data.working_model.ipopt_zU_out = Suffix( - direction=Suffix.IMPORT) + if config.nlp_solver == 'ipopt': + if not hasattr(solve_data.working_model, 'ipopt_zL_out'): + solve_data.working_model.ipopt_zL_out = Suffix( + direction=Suffix.IMPORT) + if not hasattr(solve_data.working_model, 'ipopt_zU_out'): + solve_data.working_model.ipopt_zU_out = Suffix( + direction=Suffix.IMPORT) # Initialize the master problem with time_code(solve_data.timing, 'initialization'): @@ -436,6 +268,7 @@ def solve(self, model, **kwds): solve_data.results.solver.wallclock_time = solve_data.timing.total solve_data.results.solver.iterations = solve_data.mip_iter + solve_data.results.solver.best_solution_found_time = solve_data.best_solution_found_time if config.single_tree: solve_data.results.solver.num_nodes = solve_data.nlp_iter - \ diff --git a/pyomo/contrib/mindtpy/config_options.py b/pyomo/contrib/mindtpy/config_options.py new file mode 100644 index 00000000000..c5440775898 --- /dev/null +++ b/pyomo/contrib/mindtpy/config_options.py @@ -0,0 +1,251 @@ +from pyomo.common.config import ( + ConfigBlock, ConfigValue, In, PositiveFloat, PositiveInt, NonNegativeInt) +from pyomo.contrib.gdpopt.util import _DoNothing, a_logger + + +def _get_GDPopt_config(): + CONFIG = ConfigBlock("MindtPy") + CONFIG.declare("bound_tolerance", ConfigValue( + default=1E-4, + domain=PositiveFloat, + description="Bound tolerance", + doc="Relative tolerance for bound feasibility checks." + )) + CONFIG.declare("iteration_limit", ConfigValue( + default=50, + domain=PositiveInt, + description="Iteration limit", + doc="Number of maximum iterations in the decomposition methods." + )) + CONFIG.declare("stalling_limit", ConfigValue( + default=15, + domain=PositiveInt, + description="Stalling limit", + doc="Stalling limit for progress in the decomposition methods." + )) + CONFIG.declare("time_limit", ConfigValue( + default=600, + domain=PositiveInt, + description="Time limit (seconds, default=600)", + doc="Seconds allowed until terminated. Note that the time limit can" + "currently only be enforced between subsolver invocations. You may" + "need to set subsolver time limits as well." + )) + CONFIG.declare("strategy", ConfigValue( + default="OA", + domain=In(["OA", "GBD", "ECP", "PSC", "GOA"]), + description="Decomposition strategy", + doc="MINLP Decomposition strategy to be applied to the method. " + "Currently available Outer Approximation (OA), Extended Cutting " + "Plane (ECP), Partial Surrogate Cuts (PSC), and Generalized " + "Benders Decomposition (GBD)." + )) + CONFIG.declare("init_strategy", ConfigValue( + default=None, + domain=In(["rNLP", "initial_binary", "max_binary"]), + description="Initialization strategy", + doc="Initialization strategy used by any method. Currently the " + "continuous relaxation of the MINLP (rNLP), solve a maximal " + "covering problem (max_binary), and fix the initial value for " + "the integer variables (initial_binary)." + )) + CONFIG.declare("max_slack", ConfigValue( + default=1000.0, + domain=PositiveFloat, + description="Maximum slack variable", + doc="Maximum slack variable value allowed for the Outer Approximation " + "cuts." + )) + CONFIG.declare("OA_penalty_factor", ConfigValue( + default=1000.0, + domain=PositiveFloat, + description="Outer Approximation slack penalty factor", + doc="In the objective function of the Outer Approximation method, the " + "slack variables corresponding to all the constraints get " + "multiplied by this number and added to the objective." + )) + CONFIG.declare("ecp_tolerance", ConfigValue( + default=None, + domain=PositiveFloat, + description="ECP tolerance", + doc="Feasibility tolerance used to determine the stopping criterion in" + "the ECP method. As long as nonlinear constraint are violated for " + "more than this tolerance, the method will keep iterating." + )) + CONFIG.declare("nlp_solver", ConfigValue( + default="ipopt", + domain=In(["ipopt", "gams", "baron"]), + description="NLP subsolver name", + doc="Which NLP subsolver is going to be used for solving the nonlinear" + "subproblems." + )) + CONFIG.declare("nlp_solver_args", ConfigBlock( + implicit=True, + description="NLP subsolver options", + doc="Which NLP subsolver options to be passed to the solver while " + "solving the nonlinear subproblems." + )) + CONFIG.declare("mip_solver", ConfigValue( + default="glpk", + domain=In(["gurobi", "cplex", "cbc", "glpk", "gams", + "gurobi_persistent", "cplex_persistent"]), + description="MIP subsolver name", + doc="Which MIP subsolver is going to be used for solving the mixed-" + "integer master problems." + )) + CONFIG.declare("mip_solver_args", ConfigBlock( + implicit=True, + description="MIP subsolver options", + doc="Which MIP subsolver options to be passed to the solver while " + "solving the mixed-integer master problems." + )) + CONFIG.declare("call_after_master_solve", ConfigValue( + default=_DoNothing(), + domain=None, + description="Function to be executed after every master problem", + doc="Callback hook after a solution of the master problem." + )) + CONFIG.declare("call_after_subproblem_solve", ConfigValue( + default=_DoNothing(), + domain=None, + description="Function to be executed after every subproblem", + doc="Callback hook after a solution of the nonlinear subproblem." + )) + CONFIG.declare("call_after_subproblem_feasible", ConfigValue( + default=_DoNothing(), + domain=None, + description="Function to be executed after every feasible subproblem", + doc="Callback hook after a feasible solution" + " of the nonlinear subproblem." + )) + CONFIG.declare("tee", ConfigValue( + default=False, + description="Stream output to terminal.", + domain=bool + )) + CONFIG.declare("solver_tee", ConfigValue( + default=False, + description="Stream the output of mip solver and nlp solver to terminal.", + domain=bool + )) + CONFIG.declare("logger", ConfigValue( + default='pyomo.contrib.mindtpy', + description="The logger object or name to use for reporting.", + domain=a_logger + )) + CONFIG.declare("small_dual_tolerance", ConfigValue( + default=1E-8, + description="When generating cuts, small duals multiplied " + "by expressions can cause problems. Exclude all duals " + "smaller in absolute value than the following." + )) + CONFIG.declare("integer_tolerance", ConfigValue( + default=1E-5, + description="Tolerance on integral values." + )) + CONFIG.declare("constraint_tolerance", ConfigValue( + default=1E-6, + description="Tolerance on constraint satisfaction." + )) + CONFIG.declare("variable_tolerance", ConfigValue( + default=1E-8, + description="Tolerance on variable bounds." + )) + CONFIG.declare("zero_tolerance", ConfigValue( + default=1E-7, + description="Tolerance on variable equal to zero." + )) + CONFIG.declare("initial_feas", ConfigValue( + default=True, + description="Apply an initial feasibility step.", + domain=bool + )) + CONFIG.declare("obj_bound", ConfigValue( + default=1E15, + domain=PositiveFloat, + description="Bound applied to the linearization of the objective function if master MILP is unbounded." + )) + CONFIG.declare("integer_to_binary", ConfigValue( + default=False, + description="Convert integer variables to binaries (for integer cuts).", + domain=bool + )) + CONFIG.declare("add_nogood_cuts", ConfigValue( + default=False, + description="Add integer cuts (no-good cuts) to binary variables to disallow same integer solution again." + "Note that 'integer_to_binary' flag needs to be used to apply it to actual integers and not just binaries.", + domain=bool + )) + CONFIG.declare("single_tree", ConfigValue( + default=False, + description="Use single tree implementation in solving the MILP master problem.", + domain=bool + )) + CONFIG.declare("solution_pool", ConfigValue( + default=False, + description="Use solution pool in solving the MILP master problem.", + domain=bool + )) + CONFIG.declare("add_slack", ConfigValue( + default=False, + description="whether add slack variable here." + "slack variables here are used to deal with nonconvex MINLP.", + domain=bool + )) + CONFIG.declare("continuous_var_bound", ConfigValue( + default=1e10, + description="default bound added to unbounded continuous variables in nonlinear constraint if single tree is activated.", + domain=PositiveFloat + )) + CONFIG.declare("integer_var_bound", ConfigValue( + default=1e9, + description="default bound added to unbounded integral variables in nonlinear constraint if single tree is activated.", + domain=PositiveFloat + )) + CONFIG.declare("cycling_check", ConfigValue( + default=True, + description="check if OA algorithm is stalled in a cycle and terminate.", + domain=bool + )) + CONFIG.declare("feasibility_norm", ConfigValue( + default="L_infinity", + domain=In(["L1", "L2", "L_infinity"]), + description="different forms of objective function in feasibility subproblem." + )) + CONFIG.declare("differentiate_mode", ConfigValue( + default="reverse_symbolic", + domain=In(["reverse_symbolic", "sympy"]), + description="differentiate mode to calculate jacobian." + )) + CONFIG.declare("linearize_inactive", ConfigValue( + default=False, + description="Add OA cuts for inactive constraints.", + domain=bool + )) + CONFIG.declare("use_mcpp", ConfigValue( + default=False, + description="use package MC++ to set a bound for variable 'objective_value', which is introduced when the original problem's objective function is nonlinear.", + domain=bool + )) + CONFIG.declare("use_dual", ConfigValue( + default=True, + description="use dual solution from the nlp solver to add OA cuts for equality constraints.", + domain=bool + )) + CONFIG.declare("use_fbbt", ConfigValue( + default=False, + description="use fbbt to tighten the feasible region of the problem", + domain=bool + )) + CONFIG.declare("threads", ConfigValue( + default=0, + domain=NonNegativeInt, + description="Threads", + doc="Threads used by milp solver and nlp solver." + )) + CONFIG.declare("use_dual_bound", ConfigValue( + default=True, + description="add dual bound constraint to enforce the objective function should improve on the best found dual bound", + domain=bool + )) + return CONFIG diff --git a/pyomo/contrib/mindtpy/cut_generation.py b/pyomo/contrib/mindtpy/cut_generation.py index 867365a9247..e814dd2da6a 100644 --- a/pyomo/contrib/mindtpy/cut_generation.py +++ b/pyomo/contrib/mindtpy/cut_generation.py @@ -1,20 +1,37 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Cut generation.""" from __future__ import division - +import logging from math import copysign -from pyomo.core import Constraint, minimize, value +from pyomo.core import minimize, value, Block, ConstraintList from pyomo.core.expr import current as EXPR -from pyomo.contrib.gdpopt.util import copy_var_list_values, identify_variables -from pyomo.core.expr.taylor_series import taylor_series_expansion -from pyomo.core.expr import differentiate +from pyomo.contrib.gdpopt.util import identify_variables +from pyomo.contrib.mcpp.pyomo_mcpp import McCormick as mc, MCPP_Error +logger = logging.getLogger('pyomo.contrib.mindtpy') -def add_objective_linearization(solve_data, config): - """Adds initial linearized objective in case it is nonlinear. - - This should be done for initializing the ECP method. +def add_objective_linearization(solve_data, config): + """ + If objective is nonlinear, then this function adds a linearized objective. This function should be used to + initialize the ECP method. + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm """ m = solve_data.working_model MindtPy = m.MindtPy_utils @@ -23,6 +40,7 @@ def add_objective_linearization(solve_data, config): if obj is MindtPy.MindtPy_objective_expr) MindtPy.MindtPy_linear_cuts.mip_iters.add(solve_data.mip_iter) sign_adjust = 1 if MindtPy.obj.sense == minimize else -1 + # generate new constraints # TODO some kind of special handling if the dual is phenomenally small? for obj in gen: @@ -34,13 +52,31 @@ def add_objective_linearization(solve_data, config): MindtPy.ECP_constr_map[obj, solve_data.mip_iter] = c +''' def add_oa_cuts(target_model, dual_values, solve_data, config, linearize_active=True, linearize_violated=True): - """Linearizes nonlinear constraints. + """ + Linearizes nonlinear constraints; modifies the model to include the OA cuts For nonconvex problems, turn on 'config.add_slack'. Slack variables will always be used for nonlinear equality constraints. + + Parameters + ---------- + target_model: + this is the MIP/MILP model for the OA algorithm; we want to add the OA cuts to 'target_model' + dual_values: + contains the value of the duals for each constraint + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + linearize_active: bool, optional + this parameter acts as a Boolean flag that signals whether the linearized constraint is active + linearize_violated: bool, optional + this parameter acts as a Boolean flag that signals whether the nonlinear constraint represented by the + linearized constraint has been violated """ for (constr, dual_value) in zip(target_model.MindtPy_utils.constraint_list, dual_values): @@ -63,6 +99,80 @@ def add_oa_cuts(target_model, dual_values, solve_data, config, + value(constr.body) - rhs) - (slack_var if config.add_slack else 0) <= 0) + else: # Inequality constraint (possibly two-sided) + if constr.has_ub() \ + and (linearize_active and abs(constr.uslack()) < config.bound_tolerance) \ + or (linearize_violated and constr.uslack() < 0) \ + or (config.linearize_inactive and constr.uslack() > 0): + if config.add_slack: + slack_var = target_model.MindtPy_utils.MindtPy_linear_cuts.slack_vars.add() + + target_model.MindtPy_utils.MindtPy_linear_cuts.oa_cuts.add( + expr=(sum(value(jacs[constr][var])*(var - var.value) + for var in constr_vars) + value(constr.body) + - (slack_var if config.add_slack else 0) + <= constr.upper) + ) + + if constr.has_lb() \ + and (linearize_active and abs(constr.lslack()) < config.bound_tolerance) \ + or (linearize_violated and constr.lslack() < 0) \ + or (config.linearize_inactive and constr.lslack() > 0): + if config.add_slack: + slack_var = target_model.MindtPy_utils.MindtPy_linear_cuts.slack_vars.add() + + target_model.MindtPy_utils.MindtPy_linear_cuts.oa_cuts.add( + expr=(sum(value(jacs[constr][var])*(var - var.value) + for var in constr_vars) + value(constr.body) + + (slack_var if config.add_slack else 0) + >= constr.lower) + ) +''' + + +def add_oa_cuts(target_model, dual_values, solve_data, config, + linearize_active=True, + linearize_violated=True): + """ + Linearizes nonlinear constraints; modifies the model to include the OA cuts + For nonconvex problems, turn on 'config.add_slack'. Slack variables will + always be used for nonlinear equality constraints. + Parameters + ---------- + target_model: + this is the MIP/MILP model for the OA algorithm; we want to add the OA cuts to 'target_model' + dual_values: + contains the value of the duals for each constraint + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + linearize_active: bool, optional + this parameter acts as a Boolean flag that signals whether the linearized constraint is active + linearize_violated: bool, optional + this parameter acts as a Boolean flag that signals whether the nonlinear constraint represented by the + linearized constraint has been violated + """ + for index, constr in enumerate(target_model.MindtPy_utils.constraint_list): + if constr.body.polynomial_degree() in (0, 1): + continue + + constr_vars = list(identify_variables(constr.body)) + jacs = solve_data.jacobians + + # Equality constraint (makes the problem nonconvex) + if constr.has_ub() and constr.has_lb() and constr.upper == constr.lower and config.use_dual: + sign_adjust = -1 if solve_data.objective_sense == minimize else 1 + rhs = constr.lower + if config.add_slack: + slack_var = target_model.MindtPy_utils.MindtPy_linear_cuts.slack_vars.add() + target_model.MindtPy_utils.MindtPy_linear_cuts.oa_cuts.add( + expr=copysign(1, sign_adjust * dual_values[index]) + * (sum(value(jacs[constr][var]) * (var - value(var)) + for var in list(EXPR.identify_variables(constr.body))) + + value(constr.body) - rhs) + - (slack_var if config.add_slack else 0) <= 0) + else: # Inequality constraint (possibly two-sided) if constr.has_ub() \ and (linearize_active and abs(constr.uslack()) < config.zero_tolerance) \ @@ -92,6 +202,91 @@ def add_oa_cuts(target_model, dual_values, solve_data, config, >= constr.lower) ) + +def add_ecp_cuts(target_model, solve_data, config, + linearize_active=True, + linearize_violated=True): + """ + Linearizes nonlinear constraints. Adds the cuts for the ECP method. + + For nonconvex problems, turn on 'config.add_slack'. Slack variables will + always be used for nonlinear equality constraints. + + Parameters + ---------- + target_model: + this is the MIP/MILP model for the OA algorithm; we want to add the OA cuts to 'target_model' + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + linearize_active: bool, optional + this parameter acts as a Boolean flag that signals whether the linearized constraint is active + linearize_violated: bool, optional + this parameter acts as a Boolean flag that signals whether the nonlinear constraint represented by the + linearized constraint has been violated + """ + for constr in target_model.MindtPy_utils.constraint_list: + + if constr.body.polynomial_degree() in (0, 1): + continue + + constr_vars = list(identify_variables(constr.body)) + jacs = solve_data.jacobians + + if constr.has_lb() and constr.has_ub(): + config.logger.warning( + 'constraint {} has both a lower ' + 'and upper bound.' + '\n'.format( + constr)) + continue + if constr.has_ub(): + try: + upper_slack = constr.uslack() + except (ValueError, OverflowError): + config.logger.warning( + 'constraint {} has caused either a ' + 'ValueError or OverflowError.' + '\n'.format( + constr)) + continue + if (linearize_active and abs(upper_slack) < config.ecp_tolerance) \ + or (linearize_violated and upper_slack < 0) \ + or (config.linearize_inactive and upper_slack > 0): + if config.add_slack: + slack_var = target_model.MindtPy_utils.MindtPy_linear_cuts.slack_vars.add() + + target_model.MindtPy_utils.MindtPy_linear_cuts.ecp_cuts.add( + expr=(sum(value(jacs[constr][var])*(var - var.value) + for var in constr_vars) + - (slack_var if config.add_slack else 0) + <= upper_slack) + ) + + if constr.has_lb(): + try: + lower_slack = constr.lslack() + except (ValueError, OverflowError): + config.logger.warning( + 'constraint {} has caused either a ' + 'ValueError or OverflowError.' + '\n'.format( + constr)) + continue + if (linearize_active and abs(lower_slack) < config.ecp_tolerance) \ + or (linearize_violated and lower_slack < 0) \ + or (config.linearize_inactive and lower_slack > 0): + if config.add_slack: + slack_var = target_model.MindtPy_utils.MindtPy_linear_cuts.slack_vars.add() + + target_model.MindtPy_utils.MindtPy_linear_cuts.ecp_cuts.add( + expr=(sum(value(jacs[constr][var])*(var - var.value) + for var in constr_vars) + + (slack_var if config.add_slack else 0) + >= -lower_slack) + ) + # def add_oa_equality_relaxation(var_values, duals, solve_data, config, ignore_integrality=False): # """More general case for outer approximation @@ -144,11 +339,25 @@ def add_oa_cuts(target_model, dual_values, solve_data, config, # - slack_var <= 0) -def add_int_cut(var_values, solve_data, config, feasible=False): - if not config.add_integer_cuts: +def add_nogood_cuts(var_values, solve_data, config, feasible=False): + """ + Adds integer cuts; modifies the model to include integer cuts + + Parameters + ---------- + var_values: list + values of the current variables, used to generate the cut + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + feasible: bool, optional + boolean indicating if integer combination yields a feasible or infeasible NLP + """ + if not config.add_nogood_cuts: return - config.logger.info("Adding integer cuts") + config.logger.info("Adding nogood cuts") m = solve_data.mip MindtPy = m.MindtPy_utils @@ -168,7 +377,7 @@ def add_int_cut(var_values, solve_data, config, feasible=False): raise ValueError('Binary {} = {} is not 0 or 1'.format( v.name, value(v))) - if not binary_vars: # if no binary variables, skip. + if not binary_vars: # if no binary variables, skip return int_cut = (sum(1 - v for v in binary_vars @@ -178,9 +387,91 @@ def add_int_cut(var_values, solve_data, config, feasible=False): MindtPy.MindtPy_linear_cuts.integer_cuts.add(expr=int_cut) - # TODO need to handle theoretical implications of backtracking - # if not feasible: - # # Add the integer cut - # MindtPy.MindtPy_linear_cuts.integer_cuts.add(expr=int_cut) - # else: - # MindtPy.MindtPy_linear_cuts.feasible_integer_cuts.add(expr=int_cut) + +def add_affine_cuts(solve_data, config): + """ + Adds affine cuts using MCPP; modifies the model to include affine cuts + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ + + m = solve_data.mip + config.logger.info("Adding affine cuts") + counter = 0 + + for constr in m.MindtPy_utils.constraint_list: + if constr.body.polynomial_degree() in (1, 0): + continue + + vars_in_constr = list( + identify_variables(constr.body)) + if any(var.value is None for var in vars_in_constr): + continue # a variable has no values + + # mcpp stuff + try: + mc_eqn = mc(constr.body) + except MCPP_Error as e: + config.logger.debug( + "Skipping constraint %s due to MCPP error %s" % (constr.name, str(e))) + continue # skip to the next constraint + + ccSlope = mc_eqn.subcc() + cvSlope = mc_eqn.subcv() + ccStart = mc_eqn.concave() + cvStart = mc_eqn.convex() + + # check if the value of ccSlope and cvSlope is not Nan or inf. If so, we skip this. + concave_cut_valid = True + convex_cut_valid = True + for var in vars_in_constr: + if not var.fixed: + if ccSlope[var] == float('nan') or ccSlope[var] == float('inf'): + concave_cut_valid = False + if cvSlope[var] == float('nan') or cvSlope[var] == float('inf'): + convex_cut_valid = False + # check if the value of ccSlope and cvSlope all equals zero. if so, we skip this. + if not any(list(ccSlope.values())): + concave_cut_valid = False + if not any(list(cvSlope.values())): + convex_cut_valid = False + if ccStart == float('nan') or ccStart == float('inf'): + concave_cut_valid = False + if cvStart == float('nan') or cvStart == float('inf'): + convex_cut_valid = False + if (concave_cut_valid or convex_cut_valid) is False: + continue + + ub_int = min(constr.upper, mc_eqn.upper() + ) if constr.has_ub() else mc_eqn.upper() + lb_int = max(constr.lower, mc_eqn.lower() + ) if constr.has_lb() else mc_eqn.lower() + + parent_block = constr.parent_block() + # Create a block on which to put outer approximation cuts. + # TODO: create it at the beginning. + aff_utils = parent_block.component('MindtPy_aff') + if aff_utils is None: + aff_utils = parent_block.MindtPy_aff = Block( + doc="Block holding affine constraints") + aff_utils.MindtPy_aff_cons = ConstraintList() + aff_cuts = aff_utils.MindtPy_aff_cons + if concave_cut_valid: + concave_cut = sum(ccSlope[var] * (var - var.value) + for var in vars_in_constr + if not var.fixed) + ccStart >= lb_int + aff_cuts.add(expr=concave_cut) + counter += 1 + if convex_cut_valid: + convex_cut = sum(cvSlope[var] * (var - var.value) + for var in vars_in_constr + if not var.fixed) + cvStart <= ub_int + aff_cuts.add(expr=convex_cut) + counter += 1 + + config.logger.info("Added %s affine cuts" % counter) diff --git a/pyomo/contrib/mindtpy/initialization.py b/pyomo/contrib/mindtpy/initialization.py index 3c02bf3b465..82187eee03c 100644 --- a/pyomo/contrib/mindtpy/initialization.py +++ b/pyomo/contrib/mindtpy/initialization.py @@ -1,27 +1,50 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Initialization functions.""" from __future__ import division - -from pyomo.contrib.gdpopt.util import SuppressInfeasibleWarning, _DoNothing, copy_var_list_values -from pyomo.contrib.mindtpy.cut_generation import ( - add_oa_cuts, add_objective_linearization, -) +import logging +from pyomo.contrib.gdpopt.util import (SuppressInfeasibleWarning, _DoNothing, + copy_var_list_values, + get_main_elapsed_time) +from pyomo.contrib.mindtpy.cut_generation import (add_oa_cuts, + add_affine_cuts) from pyomo.contrib.mindtpy.nlp_solve import solve_NLP_subproblem from pyomo.contrib.mindtpy.util import (calc_jacobians) from pyomo.core import (ConstraintList, Objective, - TransformationFactory, maximize, minimize, value, Var) -from pyomo.opt import TerminationCondition as tc -from pyomo.opt import SolverFactory + TransformationFactory, maximize, minimize, + value, Var,) +from pyomo.opt import SolverFactory, TerminationCondition as tc from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver -from pyomo.contrib.mindtpy.nlp_solve import (solve_NLP_subproblem, - handle_NLP_subproblem_optimal, handle_NLP_subproblem_infeasible, +from pyomo.contrib.mindtpy.nlp_solve import (handle_NLP_subproblem_optimal, + handle_NLP_subproblem_infeasible, handle_NLP_subproblem_other_termination) from pyomo.contrib.mindtpy.util import var_bound_add +import math + +logger = logging.getLogger('pyomo.contrib.mindtpy') def MindtPy_initialize_master(solve_data, config): - """Initialize the decomposition algorithm. - This includes generating the initial cuts require to build the master - problem. + """ + Initializes the decomposition algorithm and creates the master MIP/MILP problem. + + This function initializes the decomposition problem, which includes generating the initial cuts required to + build the master MIP/MILP + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm """ # if single tree is activated, we need to add bounds for unbounded variables in nonlinear constraints to avoid unbounded master problem. if config.single_tree: @@ -29,16 +52,17 @@ def MindtPy_initialize_master(solve_data, config): m = solve_data.mip = solve_data.working_model.clone() MindtPy = m.MindtPy_utils - m.dual.deactivate() + if config.use_dual: + m.dual.deactivate() if config.strategy == 'OA': calc_jacobians(solve_data, config) # preload jacobians MindtPy.MindtPy_linear_cuts.oa_cuts = ConstraintList( doc='Outer approximation cuts') - # elif config.strategy == 'ECP': - # calc_jacobians(solve_data, config) # preload jacobians - # MindtPy.MindtPy_linear_cuts.ecp_cuts = ConstraintList( - # doc='Extended Cutting Planes') + elif config.strategy == 'ECP': + calc_jacobians(solve_data, config) # preload jacobians + MindtPy.MindtPy_linear_cuts.ecp_cuts = ConstraintList( + doc='Extended Cutting Planes') # elif config.strategy == 'PSC': # detect_nonlinear_vars(solve_data, config) # MindtPy.MindtPy_linear_cuts.psc_cuts = ConstraintList( @@ -49,59 +73,86 @@ def MindtPy_initialize_master(solve_data, config): # Set default initialization_strategy if config.init_strategy is None: - if config.strategy == 'OA': + if config.strategy in {'OA', 'GOA'}: config.init_strategy = 'rNLP' else: config.init_strategy = 'max_binary' + + config.logger.info( + '{} is the initial strategy being used.' + '\n'.format( + config.init_strategy)) # Do the initialization - elif config.init_strategy == 'rNLP': + if config.init_strategy == 'rNLP': init_rNLP(solve_data, config) elif config.init_strategy == 'max_binary': init_max_binaries(solve_data, config) - # if config.strategy == 'ECP': - # add_ecp_cut(solve_data, config) - # else: - - fixed_nlp, fixed_nlp_result = solve_NLP_subproblem(solve_data, config) - if fixed_nlp_result.solver.termination_condition is tc.optimal or fixed_nlp_result.solver.termination_condition is tc.locallyOptimal: - handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) - elif fixed_nlp_result.solver.termination_condition is tc.infeasible: - handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) - else: - handle_NLP_subproblem_other_termination(fixed_nlp, fixed_nlp_result.solver.termination_condition, - solve_data, config) + elif config.init_strategy == 'initial_binary': + if config.strategy != 'ECP': + fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( + solve_data, config) + if fixed_nlp_result.solver.termination_condition in {tc.optimal, tc.locallyOptimal, tc.feasible}: + handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) + elif fixed_nlp_result.solver.termination_condition is tc.infeasible: + handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) + else: + handle_NLP_subproblem_other_termination(fixed_nlp, fixed_nlp_result.solver.termination_condition, + solve_data, config) def init_rNLP(solve_data, config): - """Initialize by solving the rNLP (relaxed binary variables).""" + """ + Initialize the problem by solving the relaxed NLP (fixed binary variables) and then store the optimal variable + values obtained from solving the rNLP + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ solve_data.nlp_iter += 1 m = solve_data.working_model.clone() config.logger.info( "NLP %s: Solve relaxed integrality" % (solve_data.nlp_iter,)) MindtPy = m.MindtPy_utils TransformationFactory('core.relax_integer_vars').apply_to(m) + nlp_args = dict(config.nlp_solver_args) + elapsed = get_main_elapsed_time(solve_data.timing) + remaining = int(max(config.time_limit - elapsed, 1)) + if config.nlp_solver == 'gams': + nlp_args['add_options'] = nlp_args.get('add_options', []) + nlp_args['add_options'].append('option reslim=%s;' % remaining) with SuppressInfeasibleWarning(): results = SolverFactory(config.nlp_solver).solve( - m, **config.nlp_solver_args) + m, tee=config.solver_tee, **nlp_args) subprob_terminate_cond = results.solver.termination_condition - if subprob_terminate_cond is tc.optimal or subprob_terminate_cond is tc.locallyOptimal: + if subprob_terminate_cond in {tc.optimal, tc.feasible, tc.locallyOptimal}: + if subprob_terminate_cond in {tc.feasible, tc.locallyOptimal}: + config.logger.info( + 'relaxed NLP is not solved to optimality.') main_objective = next(m.component_data_objects(Objective, active=True)) nlp_solution_values = list(v.value for v in MindtPy.variable_list) - dual_values = list(m.dual[c] for c in MindtPy.constraint_list) + dual_values = list( + m.dual[c] for c in MindtPy.constraint_list) if config.use_dual else None # Add OA cut - if main_objective.sense == minimize: - solve_data.LB = value(main_objective.expr) - else: - solve_data.UB = value(main_objective.expr) + if main_objective.sense == minimize and not math.isnan(results['Problem'][0]['Lower bound']): + solve_data.LB = results['Problem'][0]['Lower bound'] + elif not math.isnan(results['Problem'][0]['Upper bound']): + solve_data.UB = results['Problem'][0]['Upper bound'] config.logger.info( 'NLP %s: OBJ: %s LB: %s UB: %s' % (solve_data.nlp_iter, value(main_objective.expr), solve_data.LB, solve_data.UB)) - if config.strategy == 'OA': + if config.strategy in {'OA', 'GOA'}: copy_var_list_values(m.MindtPy_utils.variable_list, solve_data.mip.MindtPy_utils.variable_list, config, ignore_integrality=True) - add_oa_cuts(solve_data.mip, dual_values, solve_data, config) + if config.strategy == 'OA': + add_oa_cuts(solve_data.mip, dual_values, solve_data, config) + elif config.strategy == 'GOA': + add_affine_cuts(solve_data, config) # TODO check if value of the binary or integer varibles is 0/1 or integer value. for var in solve_data.mip.component_data_objects(ctype=Var): if var.is_integer(): @@ -111,6 +162,12 @@ def init_rNLP(solve_data, config): config.logger.info( 'Initial relaxed NLP problem is infeasible. ' 'Problem may be infeasible.') + elif subprob_terminate_cond is tc.maxTimeLimit: + config.logger.info( + 'NLP subproblem failed to converge within time limit.') + elif subprob_terminate_cond is tc.maxIterations: + config.logger.info( + 'NLP subproblem failed to converge within iteration limit.') else: raise ValueError( 'MindtPy unable to handle relaxed NLP termination condition ' @@ -119,14 +176,22 @@ def init_rNLP(solve_data, config): def init_max_binaries(solve_data, config): - """Initialize by turning on as many binary variables as possible. + """ + Modifies model by maximizing the number of activated binary variables - The user would usually want to call _solve_NLP_subproblem after an + Note - The user would usually want to call solve_NLP_subproblem after an invocation of this function. + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm """ m = solve_data.working_model.clone() - m.dual.deactivate() + if config.use_dual: + m.dual.deactivate() MindtPy = m.MindtPy_utils solve_data.mip_subiter += 1 config.logger.info( @@ -150,10 +215,12 @@ def init_max_binaries(solve_data, config): if isinstance(opt, PersistentSolver): opt.set_instance(m) mip_args = dict(config.mip_solver_args) + elapsed = get_main_elapsed_time(solve_data.timing) + remaining = int(max(config.time_limit - elapsed, 1)) if config.mip_solver == 'gams': mip_args['add_options'] = mip_args.get('add_options', []) - mip_args['add_options'].append('option optcr=0.0;') - results = opt.solve(m, **mip_args) + mip_args['add_options'].append('option optcr=0.001;') + results = opt.solve(m, tee=config.solver_tee, **mip_args) solve_terminate_cond = results.solver.termination_condition if solve_terminate_cond is tc.optimal: @@ -168,6 +235,12 @@ def init_max_binaries(solve_data, config): 'MILP master problem is infeasible. ' 'Problem may have no more feasible ' 'binary configurations.') + elif solve_terminate_cond is tc.maxTimeLimit: + config.logger.info( + 'NLP subproblem failed to converge within time limit.') + elif solve_terminate_cond is tc.maxIterations: + config.logger.info( + 'NLP subproblem failed to converge within iteration limit.') else: raise ValueError( 'MindtPy unable to handle MILP master termination condition ' diff --git a/pyomo/contrib/mindtpy/iterate.py b/pyomo/contrib/mindtpy/iterate.py index 415bc813690..d7df09b3b8f 100644 --- a/pyomo/contrib/mindtpy/iterate.py +++ b/pyomo/contrib/mindtpy/iterate.py @@ -1,17 +1,52 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Iteration loop for MindtPy.""" from __future__ import division +import logging +from pyomo.contrib.mindtpy.cut_generation import add_ecp_cuts from pyomo.contrib.mindtpy.mip_solve import (solve_OA_master, - handle_master_mip_optimal, handle_master_mip_other_conditions) + handle_master_mip_optimal, + handle_master_mip_other_conditions, + handle_master_mip_infeasible) + from pyomo.contrib.mindtpy.nlp_solve import (solve_NLP_subproblem, - handle_NLP_subproblem_optimal, handle_NLP_subproblem_infeasible, + handle_NLP_subproblem_optimal, + handle_NLP_subproblem_infeasible, handle_NLP_subproblem_other_termination) + from pyomo.core import minimize, Objective, Var +from pyomo.opt.results import ProblemSense from pyomo.opt import TerminationCondition as tc from pyomo.contrib.gdpopt.util import get_main_elapsed_time +from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver +from pyomo.opt import SolverFactory + +logger = logging.getLogger('pyomo.contrib.mindtpy') def MindtPy_iteration_loop(solve_data, config): + """ + Main loop for MindtPy Algorithms + + This is the outermost function for the algorithms in this package; this function controls the progression of + solving the model. + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ working_model = solve_data.working_model main_objective = next( working_model.component_data_objects(Objective, active=True)) @@ -21,33 +56,37 @@ def MindtPy_iteration_loop(solve_data, config): '---MindtPy Master Iteration %s---' % solve_data.mip_iter) - if algorithm_should_terminate(solve_data, config, check_cycling=False): - break - solve_data.mip_subiter = 0 # solve MILP master problem - if config.strategy == 'OA': + if config.strategy in {'OA', 'GOA', 'ECP'}: master_mip, master_mip_results = solve_OA_master( solve_data, config) - if master_mip_results.solver.termination_condition is tc.optimal: - handle_master_mip_optimal(master_mip, solve_data, config) - else: - handle_master_mip_other_conditions(master_mip, master_mip_results, - solve_data, config) - # Call the MILP post-solve callback - config.call_after_master_solve(master_mip, solve_data) + if config.single_tree is False: + if master_mip_results.solver.termination_condition is tc.optimal: + handle_master_mip_optimal(master_mip, solve_data, config) + elif master_mip_results.solver.termination_condition is tc.infeasible: + handle_master_mip_infeasible( + master_mip, solve_data, config) + last_iter_cuts = True + break + else: + handle_master_mip_other_conditions(master_mip, master_mip_results, + solve_data, config) + # Call the MILP post-solve callback + config.call_after_master_solve(master_mip, solve_data) else: raise NotImplementedError() if algorithm_should_terminate(solve_data, config, check_cycling=True): + last_iter_cuts = False break - if config.single_tree is False: # if we don't use lazy callback, i.e. LP_NLP + if config.single_tree is False and config.strategy != 'ECP': # if we don't use lazy callback, i.e. LP_NLP # Solve NLP subproblem # The constraint linearization happens in the handlers fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( solve_data, config) - if fixed_nlp_result.solver.termination_condition is tc.optimal or fixed_nlp_result.solver.termination_condition is tc.locallyOptimal: + if fixed_nlp_result.solver.termination_condition in {tc.optimal, tc.locallyOptimal, tc.feasible}: handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) elif fixed_nlp_result.solver.termination_condition is tc.infeasible: handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) @@ -57,6 +96,13 @@ def MindtPy_iteration_loop(solve_data, config): # Call the NLP post-solve callback config.call_after_subproblem_solve(fixed_nlp, solve_data) + if algorithm_should_terminate(solve_data, config, check_cycling=False): + last_iter_cuts = True + break + + if config.strategy == 'ECP': + add_ecp_cuts(solve_data.mip, solve_data, config) + # if config.strategy == 'PSC': # # If the hybrid algorithm is not making progress, switch to OA. # progress_required = 1E-6 @@ -92,15 +138,35 @@ def MindtPy_iteration_loop(solve_data, config): # 'Switching to OA.'.format(max_nonimprove_iter)) # config.strategy = 'OA' + # if add_nogood_cuts is True, the bound obtained in the last iteration is no reliable. + # we correct it after the iteration. + if config.add_nogood_cuts: + bound_fix(solve_data, config, last_iter_cuts) + def algorithm_should_terminate(solve_data, config, check_cycling): - """Check if the algorithm should terminate. + """ + Checks if the algorithm should terminate at the given point - Termination conditions based on solver options and progress. - Sets the solve_data.results.solver.termination_condition to the appropriate - condition, i.e. optimal, maxIterations, maxTimeLimit + This function determines whether the algorithm should terminate based on the solver options and progress. + (Sets the solve_data.results.solver.termination_condition to the appropriate condition, i.e. optimal, + maxIterations, maxTimeLimit) + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + check_cycling: bool + check for a special case that causes a binary variable to loop through the same values + + Returns + ------- + boolean + True if the algorithm should terminate else returns False """ + # Check bound convergence if solve_data.LB + config.bound_tolerance >= solve_data.UB: config.logger.info( @@ -118,7 +184,10 @@ def algorithm_should_terminate(solve_data, config, check_cycling): config.logger.info( 'Final bound values: LB: {} UB: {}'. format(solve_data.LB, solve_data.UB)) - solve_data.results.solver.termination_condition = tc.maxIterations + if config.single_tree: + solve_data.results.solver.termination_condition = tc.feasible + else: + solve_data.results.solver.termination_condition = tc.maxIterations return True # Check time limit @@ -134,8 +203,72 @@ def algorithm_should_terminate(solve_data, config, check_cycling): solve_data.results.solver.termination_condition = tc.maxTimeLimit return True + # Check if algorithm is stalling + if len(solve_data.LB_progress) >= config.stalling_limit: + if abs(solve_data.LB_progress[-1] - solve_data.LB_progress[-config.stalling_limit]) <= config.zero_tolerance: + config.logger.info( + 'Algorithm is not making enough progress. ' + 'Exiting iteration loop.') + config.logger.info( + 'Final bound values: LB: {} UB: {}'. + format(solve_data.LB, solve_data.UB)) + if solve_data.best_solution_found is not None: + solve_data.results.solver.termination_condition = tc.feasible + else: + solve_data.best_solution_found = solve_data.working_model.clone() + config.logger.warning( + 'Algorithm did not find a feasible solution. ' + 'Returning best bound solution. Consider increasing stalling_limit or bound_tolerance.') + solve_data.results.solver.termination_condition = tc.noSolution + + return True + + if config.strategy == 'ECP': + # check to see if the nonlinear constraints are satisfied + MindtPy = solve_data.working_model.MindtPy_utils + nonlinear_constraints = [c for c in MindtPy.constraint_list if + c.body.polynomial_degree() not in (1, 0)] + for nlc in nonlinear_constraints: + if nlc.has_lb(): + try: + lower_slack = nlc.lslack() + except (ValueError, OverflowError): + lower_slack = -10 + # Use not fixed numbers in this case. Try some factor of ecp_tolerance + if lower_slack < -config.ecp_tolerance: + config.logger.info( + 'MindtPy-ECP continuing as {} has not met the ' + 'nonlinear constraints satisfaction.' + '\n'.format( + nlc)) + return False + if nlc.has_ub(): + try: + upper_slack = nlc.uslack() + except (ValueError, OverflowError): + upper_slack = -10 + if upper_slack < -config.ecp_tolerance: + config.logger.info( + 'MindtPy-ECP continuing as {} has not met the ' + 'nonlinear constraints satisfaction.' + '\n'.format( + nlc)) + return False + # For ECP to know whether to know which bound to copy over (primal or dual) + if solve_data.objective_sense == 1: + solve_data.UB = solve_data.LB + else: + solve_data.LB = solve_data.UB + config.logger.info( + 'MindtPy-ECP exiting on nonlinear constraints satisfaction. ' + 'LB: {} UB: {}\n'.format( + solve_data.LB, solve_data.UB)) + + solve_data.best_solution_found = solve_data.working_model.clone() + solve_data.results.solver.termination_condition = tc.optimal + return True # Cycling check - if config.cycling_check == True and solve_data.mip_iter >= 1 and check_cycling: + if config.cycling_check and solve_data.mip_iter >= 1 and check_cycling: temp = [] for var in solve_data.mip.component_data_objects(ctype=Var): if var.is_integer(): @@ -163,3 +296,75 @@ def algorithm_should_terminate(solve_data, config, check_cycling): # 'Exiting iteration loop.') # return True return False + + +def bound_fix(solve_data, config, last_iter_cuts): + if config.single_tree: + config.logger.info( + 'Fix the bound to the value of one iteration before optimal solution is found.') + if solve_data.results.problem.sense == ProblemSense.minimize: + solve_data.LB = solve_data.stored_bound[solve_data.UB] + else: + solve_data.UB = solve_data.stored_bound[solve_data.LB] + else: + config.logger.info( + 'Solve the master problem without the last nogood cut to fix the bound.' + 'zero_tolerance is set to 1E-4') + config.zero_tolerance = 1E-4 + # Solve NLP subproblem + # The constraint linearization happens in the handlers + if last_iter_cuts is False: + fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( + solve_data, config) + if fixed_nlp_result.solver.termination_condition in {tc.optimal, tc.locallyOptimal, tc.feasible}: + handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config) + elif fixed_nlp_result.solver.termination_condition is tc.infeasible: + handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config) + else: + handle_NLP_subproblem_other_termination(fixed_nlp, fixed_nlp_result.solver.termination_condition, + solve_data, config) + + MindtPy = solve_data.mip.MindtPy_utils + # only deactivate the last integer cut. + if config.strategy == 'GOA': + if solve_data.results.problem.sense == ProblemSense.minimize: + valid_no_good_cuts_num = solve_data.num_no_good_cuts_added[solve_data.UB] + else: + valid_no_good_cuts_num = solve_data.num_no_good_cuts_added[solve_data.LB] + for i in range(valid_no_good_cuts_num+1, len( + MindtPy.MindtPy_linear_cuts.integer_cuts)+1): + MindtPy.MindtPy_linear_cuts.integer_cuts[i].deactivate() + elif config.strategy == 'OA': + MindtPy.MindtPy_linear_cuts.integer_cuts[len( + MindtPy.MindtPy_linear_cuts.integer_cuts)].deactivate() + # MindtPy.MindtPy_linear_cuts.oa_cuts.activate() + masteropt = SolverFactory(config.mip_solver) + # determine if persistent solver is called. + if isinstance(masteropt, PersistentSolver): + masteropt.set_instance(solve_data.mip, symbolic_solver_labels=True) + mip_args = dict(config.mip_solver_args) + elapsed = get_main_elapsed_time(solve_data.timing) + remaining = int(max(config.time_limit - elapsed, 1)) + if config.mip_solver == 'gams': + mip_args['add_options'] = mip_args.get('add_options', []) + mip_args['add_options'].append('option optcr=0.001;') + if config.threads > 0: + masteropt.options["threads"] = config.threads + master_mip_results = masteropt.solve( + solve_data.mip, tee=config.solver_tee, **mip_args) + main_objective = next( + solve_data.working_model.component_data_objects(Objective, active=True)) + if main_objective.sense == minimize: + solve_data.LB = max( + [master_mip_results.problem.lower_bound] + solve_data.LB_progress[:-1]) + solve_data.LB_progress.append(solve_data.LB) + else: + solve_data.UB = min( + [master_mip_results.problem.upper_bound] + solve_data.UB_progress[:-1]) + solve_data.UB_progress.append(solve_data.UB) + config.logger.info( + 'Fixed bound values: LB: {} UB: {}'. + format(solve_data.LB, solve_data.UB)) + # Check bound convergence + if solve_data.LB + config.bound_tolerance >= solve_data.UB: + solve_data.results.solver.termination_condition = tc.optimal diff --git a/pyomo/contrib/mindtpy/mip_solve.py b/pyomo/contrib/mindtpy/mip_solve.py index 7bd04930478..2b42ba2d29c 100644 --- a/pyomo/contrib/mindtpy/mip_solve.py +++ b/pyomo/contrib/mindtpy/mip_solve.py @@ -1,35 +1,49 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Master problem functions.""" from __future__ import division - +import logging from pyomo.contrib.gdpopt.util import copy_var_list_values -from pyomo.core import Constraint, Expression, Objective, minimize, value, Var +from pyomo.core import Constraint, Expression, Objective, minimize, value from pyomo.opt import TerminationCondition as tc from pyomo.opt import SolutionStatus, SolverFactory -from pyomo.contrib.gdpopt.util import SuppressInfeasibleWarning, _DoNothing +from pyomo.contrib.gdpopt.util import SuppressInfeasibleWarning, _DoNothing, get_main_elapsed_time from pyomo.contrib.gdpopt.mip_solve import distinguish_mip_infeasible_or_unbounded from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver - -from pyomo.contrib.mindtpy.nlp_solve import (solve_NLP_subproblem, - handle_NLP_subproblem_optimal, handle_NLP_subproblem_infeasible, - handle_NLP_subproblem_other_termination, solve_NLP_feas) -from pyomo.contrib.mindtpy.cut_generation import (add_oa_cuts, - add_int_cut) -from pyomo.contrib.gdpopt.util import copy_var_list_values, identify_variables -from math import copysign -from pyomo.environ import * -from pyomo.core import Constraint, minimize, value -from pyomo.core.expr import current as EXPR -from math import fabs - -from pyomo.repn import generate_standard_repn - from pyomo.common.dependencies import attempt_import +logger = logging.getLogger('pyomo.contrib.mindtpy') + single_tree, single_tree_available = attempt_import( 'pyomo.contrib.mindtpy.single_tree') def solve_OA_master(solve_data, config): + """ + This function solves the MIP master problem for the OA algorithm + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + + Returns + ------- + solve_data.mip: Pyomo model + the MIP stored in solve_data + master_mip_results: Pyomo results object + result from solving the master MIP + """ solve_data.mip_iter += 1 MindtPy = solve_data.mip.MindtPy_utils config.logger.info( @@ -55,16 +69,30 @@ def solve_OA_master(solve_data, config): expr=sign_adjust * config.OA_penalty_factor * sum( v for v in MindtPy.MindtPy_linear_cuts.slack_vars[...])) - MindtPy.MindtPy_oa_obj = Objective( - expr=main_objective.expr + MindtPy.MindtPy_penalty_expr, - sense=main_objective.sense) - else: - MindtPy.MindtPy_oa_obj = Objective( - expr=main_objective.expr, - sense=main_objective.sense) + MindtPy.MindtPy_oa_obj = Objective( + expr=main_objective.expr + + (MindtPy.MindtPy_penalty_expr if config.add_slack else 0), + sense=main_objective.sense) + + if config.use_dual_bound: + # Delete previously added dual bound constraint + if MindtPy.MindtPy_linear_cuts.find_component('dual_bound') is not None: + MindtPy.MindtPy_linear_cuts.del_component('dual_bound') + if main_objective.sense == minimize: + MindtPy.MindtPy_linear_cuts.dual_bound = Constraint( + expr=main_objective.expr + + (MindtPy.MindtPy_penalty_expr if config.add_slack else 0) >= solve_data.LB, + doc='Objective function expression should improve on the best found dual bound') + else: + MindtPy.MindtPy_linear_cuts.dual_bound = Constraint( + expr=main_objective.expr + + (MindtPy.MindtPy_penalty_expr if config.add_slack else 0) <= solve_data.UB, + doc='Objective function expression should improve on the best found dual bound') + # Deactivate extraneous IMPORT/EXPORT suffixes - getattr(solve_data.mip, 'ipopt_zL_out', _DoNothing()).deactivate() - getattr(solve_data.mip, 'ipopt_zU_out', _DoNothing()).deactivate() + if config.nlp_solver == 'ipopt': + getattr(solve_data.mip, 'ipopt_zL_out', _DoNothing()).deactivate() + getattr(solve_data.mip, 'ipopt_zU_out', _DoNothing()).deactivate() masteropt = SolverFactory(config.mip_solver) # determine if persistent solver is called. @@ -83,15 +111,24 @@ def solve_OA_master(solve_data, config): masteropt._solver_model.set_log_stream(None) masteropt._solver_model.set_error_stream(None) masteropt.options['timelimit'] = config.time_limit + if config.threads > 0: + masteropt.options["threads"] = config.threads mip_args = dict(config.mip_solver_args) + elapsed = get_main_elapsed_time(solve_data.timing) + remaining = int(max(config.time_limit - elapsed, 1)) if config.mip_solver == 'gams': mip_args['add_options'] = mip_args.get('add_options', []) - mip_args['add_options'].append('option optcr=0.0;') + mip_args['add_options'].append('option optcr=0.001;') + mip_args['add_options'].append('option reslim=%s;' % remaining) + # elif config.mip_solver == 'glpk': + # masteropt.options['timelimit'] = remaining master_mip_results = masteropt.solve( - solve_data.mip, **mip_args) # , tee=True) + solve_data.mip, tee=config.solver_tee, **mip_args) + + # if config.single_tree is False and config.add_nogood_cuts is False: if master_mip_results.solver.termination_condition is tc.optimal: - if config.single_tree: + if config.single_tree and config.add_nogood_cuts is False: if main_objective.sense == minimize: solve_data.LB = max( master_mip_results.problem.lower_bound, solve_data.LB) @@ -111,18 +148,34 @@ def solve_OA_master(solve_data, config): return solve_data.mip, master_mip_results -def handle_master_mip_optimal(master_mip, solve_data, config, copy=True): - """Copy the result to working model and update upper or lower bound""" +# The following functions deal with handling the solution we get from the above MIP solver function + + +def handle_master_mip_optimal(master_mip, solve_data, config): + """ + This function copies the result from 'solve_OA_master' to the working model and updates the upper/lower bound. This + function is called after an optimal solution is found for the master problem. + + Parameters + ---------- + master_mip: Pyomo model + the MIP master problem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ # proceed. Just need integer values MindtPy = master_mip.MindtPy_utils main_objective = next( master_mip.component_data_objects(Objective, active=True)) # check if the value of binary variable is valid for var in MindtPy.variable_list: - if var.value == None and var.is_integer(): + if var.value is None and var.is_integer(): config.logger.warning( - "Integer variable {} not initialized. It is set to it's lower bound when using the initial_binary initialization method".format(var.name)) + "Integer variable {} not initialized. It is set to it's lower bound".format(var.name)) var.value = var.lb # nlp_var.bounds[0] + # warm start for the nlp subproblem copy_var_list_values( master_mip.MindtPy_utils.variable_list, solve_data.working_model.MindtPy_utils.variable_list, @@ -143,6 +196,21 @@ def handle_master_mip_optimal(master_mip, solve_data, config, copy=True): def handle_master_mip_other_conditions(master_mip, master_mip_results, solve_data, config): + """ + This function handles the result of the latest iteration of solving the MIP problem (given any of a few + edge conditions, such as if the solution is neither infeasible nor optimal). + + Parameters + ---------- + master_mip: Pyomo model + the MIP master problem + master_mip_results: Pyomo results object + result from solving the MIP problem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ if master_mip_results.solver.termination_condition is tc.infeasible: handle_master_mip_infeasible(master_mip, solve_data, config) elif master_mip_results.solver.termination_condition is tc.unbounded: @@ -150,7 +218,7 @@ def handle_master_mip_other_conditions(master_mip, master_mip_results, solve_dat elif master_mip_results.solver.termination_condition is tc.maxTimeLimit: handle_master_mip_max_timelimit(master_mip, solve_data, config) elif (master_mip_results.solver.termination_condition is tc.other and - master_mip_results.solution.status is SolutionStatus.feasible): + master_mip_results.solution.status is SolutionStatus.feasible): # load the solution and suppress the warning message by setting # solver status to ok. MindtPy = master_mip.MindtPy_utils @@ -181,6 +249,18 @@ def handle_master_mip_other_conditions(master_mip, master_mip_results, solve_dat def handle_master_mip_infeasible(master_mip, solve_data, config): + """ + This function handles the result of the latest iteration of solving the MIP problem given an infeasible solution. + + Parameters + ---------- + master_mip: Pyomo model + the MIP master problem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ config.logger.info( 'MILP master problem is infeasible. ' 'Problem may have no more feasible ' @@ -189,6 +269,7 @@ def handle_master_mip_infeasible(master_mip, solve_data, config): config.logger.warning( 'MindtPy initialization may have generated poor ' 'quality cuts.') + # TODO nogood cuts for single tree case # set optimistic bound to infinity main_objective = next( master_mip.component_data_objects(Objective, active=True)) @@ -196,9 +277,29 @@ def handle_master_mip_infeasible(master_mip, solve_data, config): solve_data.LB_progress.append(solve_data.LB) else: solve_data.UB_progress.append(solve_data.UB) + config.logger.info( + 'MindtPy exiting due to MILP master problem infeasibility.') + if solve_data.results.solver.termination_condition is None: + if solve_data.mip_iter == 0: + solve_data.results.solver.termination_condition = tc.infeasible + else: + solve_data.results.solver.termination_condition = tc.feasible def handle_master_mip_max_timelimit(master_mip, solve_data, config): + """ + This function handles the result of the latest iteration of solving the MIP problem given that solving the + MIP takes too long. + + Parameters + ---------- + master_mip: Pyomo model + the MIP master problem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ # TODO check that status is actually ok and everything is feasible MindtPy = master_mip.MindtPy_utils config.logger.info( @@ -224,6 +325,19 @@ def handle_master_mip_max_timelimit(master_mip, solve_data, config): def handle_master_mip_unbounded(master_mip, solve_data, config): + """ + This function handles the result of the latest iteration of solving the MIP problem given an unbounded solution + due to the relaxation. + + Parameters + ---------- + master_mip: Pyomo model + the MIP master problem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ # Solution is unbounded. Add an arbitrary bound to the objective and resolve. # This occurs when the objective is nonlinear. The nonlinear objective is moved # to the constraints, and deactivated for the linear master problem. @@ -241,4 +355,4 @@ def handle_master_mip_unbounded(master_mip, solve_data, config): if isinstance(opt, PersistentSolver): opt.set_instance(master_mip) master_mip_results = opt.solve( - master_mip, **config.mip_solver_args) + master_mip, tee=config.solver_tee, **config.mip_solver_args) diff --git a/pyomo/contrib/mindtpy/nlp_solve.py b/pyomo/contrib/mindtpy/nlp_solve.py index f17378b7e19..d45290ff7bc 100644 --- a/pyomo/contrib/mindtpy/nlp_solve.py +++ b/pyomo/contrib/mindtpy/nlp_solve.py @@ -1,29 +1,51 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Solution of NLP subproblems.""" from __future__ import division - +import logging from pyomo.common.collections import ComponentMap from pyomo.contrib.mindtpy.cut_generation import (add_oa_cuts, - add_int_cut) + add_nogood_cuts, add_affine_cuts) from pyomo.contrib.mindtpy.util import add_feas_slacks -from pyomo.contrib.gdpopt.util import copy_var_list_values -from pyomo.core import (Constraint, Objective, TransformationFactory, Var, +from pyomo.contrib.gdpopt.util import copy_var_list_values, get_main_elapsed_time +from pyomo.core import (Constraint, Objective, TransformationFactory, minimize, value) from pyomo.opt import TerminationCondition as tc from pyomo.opt import SolverFactory from pyomo.contrib.gdpopt.util import SuppressInfeasibleWarning +from pyomo.opt.results import ProblemSense +logger = logging.getLogger('pyomo.contrib.mindtpy') -def solve_NLP_subproblem(solve_data, config): - """ Solves fixed NLP with fixed working model binaries - Sets up local working model `fixed_nlp` - Fixes binaries - Sets continuous variables to initial var values - Precomputes dual values - Deactivates trivial constraints - Solves NLP model - - Returns the fixed-NLP model and the solver results +def solve_NLP_subproblem(solve_data, config): + """ + Solves the fixed NLP (with fixed binaries) + + This function sets up the 'fixed_nlp' by fixing binaries, sets continuous variables to their intial var values, + precomputes dual values, deactivates trivial constraints, and then solves NLP model. + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + + Returns + ------- + fixed_nlp: Pyomo model + fixed NLP from the model + results: Pyomo results object + result from solving the fixed NLP """ fixed_nlp = solve_data.working_model.clone() @@ -75,24 +97,49 @@ def solve_NLP_subproblem(solve_data, config): TransformationFactory('contrib.deactivate_trivial_constraints')\ .apply_to(fixed_nlp, tmp=True, ignore_infeasible=True) # Solve the NLP + nlpopt = SolverFactory(config.nlp_solver) + nlp_args = dict(config.nlp_solver_args) + elapsed = get_main_elapsed_time(solve_data.timing) + remaining = int(max(config.time_limit - elapsed, 1)) + if config.nlp_solver == 'gams': + nlp_args['add_options'] = nlp_args.get('add_options', []) + nlp_args['add_options'].append('option reslim=%s;' % remaining) with SuppressInfeasibleWarning(): - results = SolverFactory(config.nlp_solver).solve( - fixed_nlp, **config.nlp_solver_args) + results = nlpopt.solve( + fixed_nlp, tee=config.solver_tee, **nlp_args) return fixed_nlp, results +# The next few functions deal with handling the solution we get from the above NLP solver function + + def handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config): - """Copies result to working model, updates bound, adds OA and integer cut, - stores best solution if new one is best""" + """ + This function copies the result of the NLP solver function ('solve_NLP_subproblem') to the working model, updates + the bounds, adds OA and integer cuts, and then stores the new solution if it is the new best solution. This + function handles the result of the latest iteration of solving the NLP subproblem given an optimal solution. + + Parameters + ---------- + fixed_nlp: Pyomo model + fixed NLP from the model + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ copy_var_list_values( fixed_nlp.MindtPy_utils.variable_list, solve_data.working_model.MindtPy_utils.variable_list, config) - for c in fixed_nlp.tmp_duals: - if fixed_nlp.dual.get(c, None) is None: - fixed_nlp.dual[c] = fixed_nlp.tmp_duals[c] - dual_values = list(fixed_nlp.dual[c] - for c in fixed_nlp.MindtPy_utils.constraint_list) + if config.use_dual: + for c in fixed_nlp.tmp_duals: + if fixed_nlp.dual.get(c, None) is None: + fixed_nlp.dual[c] = fixed_nlp.tmp_duals[c] + dual_values = list(fixed_nlp.dual[c] + for c in fixed_nlp.MindtPy_utils.constraint_list) + else: + dual_values = None main_objective = next( fixed_nlp.component_data_objects(Objective, active=True)) @@ -113,6 +160,15 @@ def handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config): if solve_data.solution_improved: solve_data.best_solution_found = fixed_nlp.clone() + solve_data.best_solution_found_time = get_main_elapsed_time( + solve_data.timing) + if config.strategy == 'GOA': + if solve_data.results.problem.sense == ProblemSense.minimize: + solve_data.num_no_good_cuts_added.update( + {solve_data.UB: len(solve_data.mip.MindtPy_utils.MindtPy_linear_cuts.integer_cuts)}) + else: + solve_data.num_no_good_cuts_added.update( + {solve_data.LB: len(solve_data.mip.MindtPy_utils.MindtPy_linear_cuts.integer_cuts)}) # Add the linear cut if config.strategy == 'OA': @@ -120,9 +176,16 @@ def handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config): solve_data.mip.MindtPy_utils.variable_list, config) add_oa_cuts(solve_data.mip, dual_values, solve_data, config) + elif config.strategy == "GOA": + copy_var_list_values(fixed_nlp.MindtPy_utils.variable_list, + solve_data.mip.MindtPy_utils.variable_list, + config) + add_affine_cuts(solve_data, config) elif config.strategy == 'PSC': + # !!THIS SEEMS LIKE A BUG!! - mrmundt # add_psc_cut(solve_data, config) elif config.strategy == 'GBD': + # !!THIS SEEMS LIKE A BUG!! - mrmundt # add_gbd_cut(solve_data, config) # This adds an integer cut to the feasible_integer_cuts @@ -130,27 +193,39 @@ def handle_NLP_subproblem_optimal(fixed_nlp, solve_data, config): # may be activated as needed in certain situations or for certain # values of option flags. var_values = list(v.value for v in fixed_nlp.MindtPy_utils.variable_list) - if config.add_integer_cuts: - add_int_cut(var_values, solve_data, config, feasible=True) + if config.add_nogood_cuts: + add_nogood_cuts(var_values, solve_data, config, feasible=True) config.call_after_subproblem_feasible(fixed_nlp, solve_data) def handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config): - """Solve feasibility problem, add cut according to strategy. + """ + Solves feasibility problem and adds cut according to the specified strategy - The solution of the feasibility problem is copied to the working model. + This function handles the result of the latest iteration of solving the NLP subproblem given an infeasible + solution and copies the solution of the feasibility problem to the working model. + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm """ # TODO try something else? Reinitialize with different initial # value? config.logger.info('NLP subproblem was locally infeasible.') - for c in fixed_nlp.component_data_objects(ctype=Constraint): - rhs = c.upper if c. has_ub() else c.lower - c_geq = -1 if c.has_ub() else 1 - fixed_nlp.dual[c] = (c_geq - * max(0, c_geq * (rhs - value(c.body)))) - dual_values = list(fixed_nlp.dual[c] - for c in fixed_nlp.MindtPy_utils.constraint_list) + if config.use_dual: + for c in fixed_nlp.component_data_objects(ctype=Constraint): + rhs = c.upper if c. has_ub() else c.lower + c_geq = -1 if c.has_ub() else 1 + fixed_nlp.dual[c] = (c_geq + * max(0, c_geq * (rhs - value(c.body)))) + dual_values = list(fixed_nlp.dual[c] + for c in fixed_nlp.MindtPy_utils.constraint_list) + else: + dual_values = None # if config.strategy == 'PSC' or config.strategy == 'GBD': # for var in fixed_nlp.component_data_objects(ctype=Var, descend_into=True): @@ -161,7 +236,7 @@ def handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config): # elif var.has_lb() and abs(value(var) - var.lb) < config.bound_tolerance: # fixed_nlp.ipopt_zU_out[var] = -1 - if config.strategy == 'OA': + if config.strategy in {'OA', 'GOA'}: config.logger.info('Solving feasibility problem') if config.initial_feas: # add_feas_slacks(fixed_nlp, solve_data) @@ -170,26 +245,41 @@ def handle_NLP_subproblem_infeasible(fixed_nlp, solve_data, config): copy_var_list_values(feas_NLP.MindtPy_utils.variable_list, solve_data.mip.MindtPy_utils.variable_list, config) - add_oa_cuts(solve_data.mip, dual_values, solve_data, config) + if config.strategy == "OA": + add_oa_cuts(solve_data.mip, dual_values, solve_data, config) + elif config.strategy == "GOA": + add_affine_cuts(solve_data, config) # Add an integer cut to exclude this discrete option var_values = list(v.value for v in fixed_nlp.MindtPy_utils.variable_list) - if config.add_integer_cuts: + if config.add_nogood_cuts: # excludes current discrete option - add_int_cut(var_values, solve_data, config) + add_nogood_cuts(var_values, solve_data, config) def handle_NLP_subproblem_other_termination(fixed_nlp, termination_condition, solve_data, config): - """Case that fix-NLP is neither optimal nor infeasible (i.e. max_iterations)""" + """ + Handles the result of the latest iteration of solving the NLP subproblem given a solution that is neither optimal + nor infeasible. + + Parameters + ---------- + termination_condition: Pyomo TerminationCondition + the termination condition of the NLP subproblem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ if termination_condition is tc.maxIterations: # TODO try something else? Reinitialize with different initial value? config.logger.info( 'NLP subproblem failed to converge within iteration limit.') var_values = list( v.value for v in fixed_nlp.MindtPy_utils.variable_list) - if config.add_integer_cuts: + if config.add_nogood_cuts: # excludes current discrete option - add_int_cut(var_values, solve_data, config) + add_nogood_cuts(var_values, solve_data, config) else: raise ValueError( 'MindtPy unable to handle NLP subproblem termination ' @@ -197,15 +287,32 @@ def handle_NLP_subproblem_other_termination(fixed_nlp, termination_condition, def solve_NLP_feas(solve_data, config): - """Solves feasibility NLP and copies result to working model - - Returns: Result values and dual values """ - fixed_nlp = solve_data.working_model.clone() - add_feas_slacks(fixed_nlp, config) - MindtPy = fixed_nlp.MindtPy_utils - next(fixed_nlp.component_data_objects(Objective, active=True)).deactivate() - for constr in fixed_nlp.component_data_objects( + Solves a feasibility NLP if the fixed_nlp problem is infeasible + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + + Returns + ------- + feas_nlp: Pyomo model + feasibility NLP from the model + feas_soln: Pyomo results object + result from solving the feasibility NLP + """ + feas_nlp = solve_data.working_model.clone() + add_feas_slacks(feas_nlp, config) + + MindtPy = feas_nlp.MindtPy_utils + if MindtPy.find_component('objective_value') is not None: + MindtPy.objective_value.value = 0 + + next(feas_nlp.component_data_objects(Objective, active=True)).deactivate() + for constr in feas_nlp.component_data_objects( ctype=Constraint, active=True, descend_into=True): if constr.body.polynomial_degree() not in [0, 1]: constr.deactivate() @@ -223,13 +330,28 @@ def solve_NLP_feas(solve_data, config): MindtPy.MindtPy_feas_obj = Objective( expr=MindtPy.MindtPy_feas.slack_var, sense=minimize) - TransformationFactory('core.fix_integer_vars').apply_to(fixed_nlp) - + TransformationFactory('core.fix_integer_vars').apply_to(feas_nlp) with SuppressInfeasibleWarning(): - feas_soln = SolverFactory(config.nlp_solver).solve( - fixed_nlp, **config.nlp_solver_args) + try: + nlpopt = SolverFactory(config.nlp_solver) + nlp_args = dict(config.nlp_solver_args) + elapsed = get_main_elapsed_time(solve_data.timing) + remaining = int(max(config.time_limit - elapsed, 1)) + if config.nlp_solver == 'gams': + nlp_args['add_options'] = nlp_args.get('add_options', []) + nlp_args['add_options'].append('option reslim=%s;' % remaining) + feas_soln = nlpopt.solve( + feas_nlp, tee=config.solver_tee, **nlp_args) + except (ValueError, OverflowError) as error: + for nlp_var, orig_val in zip( + MindtPy.variable_list, + solve_data.initial_var_values): + if not nlp_var.fixed and not nlp_var.is_binary(): + nlp_var.value = orig_val + feas_soln = nlpopt.solve( + feas_nlp, tee=config.solver_tee, **nlp_args) subprob_terminate_cond = feas_soln.solver.termination_condition - if subprob_terminate_cond is tc.optimal or subprob_terminate_cond is tc.locallyOptimal: + if subprob_terminate_cond in {tc.optimal, tc.locallyOptimal, tc.feasible}: copy_var_list_values( MindtPy.variable_list, solve_data.working_model.MindtPy_utils.variable_list, @@ -237,6 +359,9 @@ def solve_NLP_feas(solve_data, config): elif subprob_terminate_cond is tc.infeasible: raise ValueError('Feasibility NLP infeasible. ' 'This should never happen.') + elif subprob_terminate_cond is tc.maxIterations: + raise ValueError('Subsolver reached its maximum number of iterations without converging, ' + 'consider increasing the iterations limit of the subsolver or reviewing your formulation.') else: raise ValueError( 'MindtPy unable to handle feasibility NLP termination condition ' @@ -251,8 +376,9 @@ def solve_NLP_feas(solve_data, config): duals[i] = c_geq * max( 0, c_geq * (rhs - value(c.body))) - if value(MindtPy.MindtPy_feas_obj.expr) == 0: - raise ValueError( - 'Problem is not feasible, check NLP solver') + if value(MindtPy.MindtPy_feas_obj.expr) <= config.zero_tolerance: + config.logger.warning("The objective value %.4E of feasibility problem is less than zero_tolerance. " + "This indicates that the nlp subproblem is feasible, although it is found infeasible in the previous step. " + "Check the nlp solver output" % value(MindtPy.MindtPy_feas_obj.expr)) - return fixed_nlp, feas_soln + return feas_nlp, feas_soln diff --git a/pyomo/contrib/mindtpy/plugins.py b/pyomo/contrib/mindtpy/plugins.py index 9aecfd948f7..48ec1ad25f4 100644 --- a/pyomo/contrib/mindtpy/plugins.py +++ b/pyomo/contrib/mindtpy/plugins.py @@ -1,2 +1,12 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + def load(): import pyomo.contrib.mindtpy.MindtPy diff --git a/pyomo/contrib/mindtpy/single_tree.py b/pyomo/contrib/mindtpy/single_tree.py index 52eb5582529..01b42f23672 100644 --- a/pyomo/contrib/mindtpy/single_tree.py +++ b/pyomo/contrib/mindtpy/single_tree.py @@ -1,22 +1,31 @@ -from __future__ import division - +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ -from pyomo.core import Constraint, Expression, Objective, minimize, value, Var +from __future__ import division +from pyomo.core import Constraint, Objective, minimize, value from pyomo.opt import TerminationCondition as tc from pyomo.contrib.mindtpy.nlp_solve import (solve_NLP_subproblem, - handle_NLP_subproblem_optimal, handle_NLP_subproblem_infeasible, - handle_NLP_subproblem_other_termination, solve_NLP_feas) -from pyomo.contrib.gdpopt.util import copy_var_list_values, identify_variables + solve_NLP_feas) +from pyomo.contrib.gdpopt.util import copy_var_list_values, identify_variables, get_main_elapsed_time from math import copysign -from pyomo.environ import * +import pyomo.environ as pyo from pyomo.core.expr import current as EXPR from math import fabs from pyomo.repn import generate_standard_repn import logging -from pyomo.common.dependencies import attempt_import import cplex from cplex.callbacks import LazyConstraintCallback +from pyomo.contrib.mcpp.pyomo_mcpp import McCormick as mc, MCPP_Error +from pyomo.opt.results import ProblemSense +logger = logging.getLogger('pyomo.contrib.mindtpy') class LazyOACallback_cplex(LazyConstraintCallback): """Inherent class in Cplex to call Lazy callback.""" @@ -24,10 +33,20 @@ class LazyOACallback_cplex(LazyConstraintCallback): def copy_lazy_var_list_values(self, opt, from_list, to_list, config, skip_stale=False, skip_fixed=True, ignore_integrality=False): - """Copy variable values from one list to another. - + """This function copies variable values from one list to another. Rounds to Binary/Integer if neccessary Sets to zero for NonNegativeReals if neccessary + + Parameters + ---------- + opt: SolverFactory + the mip solver + from_list: variable list + contains variables and their values + to_list: variable list + contains the variables that need to set value + config: ConfigBlock + contains the specific configurations for the algorithm """ for v_from, v_to in zip(from_list, to_list): if skip_stale and v_from.stale: @@ -42,9 +61,9 @@ def copy_lazy_var_list_values(self, opt, from_list, to_list, config, v_to.stale = False except ValueError: # Snap the value to the bounds - if v_to.has_lb() and v_val < v_to.lb and v_to.lb - v_val <= config.zero_tolerance: + if v_to.has_lb() and v_val < v_to.lb and v_to.lb - v_val <= config.bound_tolerance: v_to.set_value(v_to.lb) - elif v_to.has_ub() and v_val > v_to.ub and v_val - v_to.ub <= config.zero_tolerance: + elif v_to.has_ub() and v_val > v_to.ub and v_val - v_to.ub <= config.bound_tolerance: v_to.set_value(v_to.ub) # ... or the nearest integer elif v_to.is_integer(): @@ -58,8 +77,30 @@ def copy_lazy_var_list_values(self, opt, from_list, to_list, config, def add_lazy_oa_cuts(self, target_model, dual_values, solve_data, config, opt, linearize_active=True, linearize_violated=True): - """Add oa_cuts through Cplex inherent function self.add()""" + """ + Linearizes nonlinear constraints; add the OA cuts through Cplex inherent function self.add() + For nonconvex problems, turn on 'config.add_slack'. Slack variables will + always be used for nonlinear equality constraints. + Parameters + ---------- + target_model: + this is the MIP/MILP model for the OA algorithm; we want to add the OA cuts to 'target_model' + dual_values: + contains the value of the duals for each constraint + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + opt: SolverFactory + the mip solver + linearize_active: bool, optional + this parameter acts as a Boolean flag that signals whether the linearized constraint is active + linearize_violated: bool, optional + this parameter acts as a Boolean flag that signals whether the nonlinear constraint represented by the + linearized constraint has been violated + """ + config.logger.info("Adding OA cuts") for (constr, dual_value) in zip(target_model.MindtPy_utils.constraint_list, dual_values): if constr.body.polynomial_degree() in (0, 1): @@ -71,7 +112,7 @@ def add_lazy_oa_cuts(self, target_model, dual_values, solve_data, config, opt, # Equality constraint (makes the problem nonconvex) if constr.has_ub() and constr.has_lb() and constr.upper == constr.lower: sign_adjust = -1 if solve_data.objective_sense == minimize else 1 - rhs = constr.lower if constr.has_lb() and constr.has_ub() else rhs + rhs = constr.lower # since the cplex requires the lazy cuts in cplex type, we need to transform the pyomo expression into cplex expression pyomo_expr = copysign(1, sign_adjust * dual_value) * (sum(value(jacs[constr][var]) * ( @@ -83,7 +124,7 @@ def add_lazy_oa_cuts(self, target_model, dual_values, solve_data, config, opt, rhs=cplex_rhs) else: # Inequality constraint (possibly two-sided) if constr.has_ub() \ - and (linearize_active and abs(constr.uslack()) < config.zero_tolerance) \ + and (linearize_active and abs(constr.uslack()) < config.bound_tolerance) \ or (linearize_violated and constr.uslack() < 0) \ or (config.linearize_inactive and constr.uslack() > 0): @@ -93,9 +134,9 @@ def add_lazy_oa_cuts(self, target_model, dual_values, solve_data, config, opt, cplex_expr, _ = opt._get_expr_from_pyomo_expr(pyomo_expr) self.add(constraint=cplex.SparsePair(ind=cplex_expr.variables, val=cplex_expr.coefficients), sense="L", - rhs=constr.upper.value+cplex_rhs) + rhs=constr.upper.value + cplex_rhs) if constr.has_lb() \ - and (linearize_active and abs(constr.lslack()) < config.zero_tolerance) \ + and (linearize_active and abs(constr.lslack()) < config.bound_tolerance) \ or (linearize_violated and constr.lslack() < 0) \ or (config.linearize_inactive and constr.lslack() > 0): pyomo_expr = sum(value(jacs[constr][var]) * (var - self.get_values( @@ -106,10 +147,181 @@ def add_lazy_oa_cuts(self, target_model, dual_values, solve_data, config, opt, sense="G", rhs=constr.lower.value + cplex_rhs) + def add_lazy_affine_cuts(self, solve_data, config, opt): + """ + Adds affine cuts using MCPP; add affine cuts through Cplex inherent function self.add() + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + opt: SolverFactory + the mip solver + """ + m = solve_data.mip + config.logger.info("Adding affine cuts") + counter = 0 + + for constr in m.MindtPy_utils.constraint_list: + if constr.body.polynomial_degree() in (1, 0): + continue + + vars_in_constr = list( + identify_variables(constr.body)) + if any(var.value is None for var in vars_in_constr): + continue # a variable has no values + + # mcpp stuff + try: + mc_eqn = mc(constr.body) + except MCPP_Error as e: + config.logger.debug( + "Skipping constraint %s due to MCPP error %s" % (constr.name, str(e))) + continue # skip to the next constraint + # TODO: check if the value of ccSlope and cvSlope is not Nan or inf. If so, we skip this. + ccSlope = mc_eqn.subcc() + cvSlope = mc_eqn.subcv() + ccStart = mc_eqn.concave() + cvStart = mc_eqn.convex() + + concave_cut_valid = True + convex_cut_valid = True + for var in vars_in_constr: + if not var.fixed: + if ccSlope[var] == float('nan') or ccSlope[var] == float('inf'): + concave_cut_valid = False + if cvSlope[var] == float('nan') or cvSlope[var] == float('inf'): + convex_cut_valid = False + if ccStart == float('nan') or ccStart == float('inf'): + concave_cut_valid = False + if cvStart == float('nan') or cvStart == float('inf'): + convex_cut_valid = False + # check if the value of ccSlope and cvSlope all equals zero. if so, we skip this. + if not any(list(ccSlope.values())): + concave_cut_valid = False + if not any(list(cvSlope.values())): + convex_cut_valid = False + if (concave_cut_valid or convex_cut_valid) is False: + continue + + ub_int = min(constr.upper, mc_eqn.upper() + ) if constr.has_ub() else mc_eqn.upper() + lb_int = max(constr.lower, mc_eqn.lower() + ) if constr.has_lb() else mc_eqn.lower() + + parent_block = constr.parent_block() + # Create a block on which to put outer approximation cuts. + # TODO: create it at the beginning. + aff_utils = parent_block.component('MindtPy_aff') + if aff_utils is None: + aff_utils = parent_block.MindtPy_aff = pyo.Block( + doc="Block holding affine constraints") + aff_utils.MindtPy_aff_cons = pyo.ConstraintList() + aff_cuts = aff_utils.MindtPy_aff_cons + if concave_cut_valid: + pyomo_concave_cut = sum(ccSlope[var] * (var - var.value) + for var in vars_in_constr + if not var.fixed) + ccStart + cplex_concave_rhs = generate_standard_repn( + pyomo_concave_cut).constant + cplex_concave_cut, _ = opt._get_expr_from_pyomo_expr( + pyomo_concave_cut) + self.add(constraint=cplex.SparsePair(ind=cplex_concave_cut.variables, val=cplex_concave_cut.coefficients), + sense="G", + rhs=lb_int - cplex_concave_rhs) + counter += 1 + if convex_cut_valid: + pyomo_convex_cut = sum(cvSlope[var] * (var - var.value) + for var in vars_in_constr + if not var.fixed) + cvStart + cplex_convex_rhs = generate_standard_repn( + pyomo_convex_cut).constant + cplex_convex_cut, _ = opt._get_expr_from_pyomo_expr( + pyomo_convex_cut) + self.add(constraint=cplex.SparsePair(ind=cplex_convex_cut.variables, val=cplex_convex_cut.coefficients), + sense="L", + rhs=ub_int - cplex_convex_rhs) + # aff_cuts.add(expr=convex_cut) + counter += 1 + + config.logger.info("Added %s affine cuts" % counter) + + def add_lazy_nogood_cuts(self, var_values, solve_data, config, opt, feasible=False): + """ + Adds integer cuts; add the nogood cuts through Cplex inherent function self.add() + + Parameters + ---------- + var_values: list + values of the current variables, used to generate the cut + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + feasible: bool, optional + boolean indicating if integer combination yields a feasible or infeasible NLP + opt: SolverFactory + the mip solver + """ + if not config.add_nogood_cuts: + return + + config.logger.info("Adding nogood cuts") + + m = solve_data.mip + MindtPy = m.MindtPy_utils + int_tol = config.integer_tolerance + + binary_vars = [v for v in MindtPy.variable_list if v.is_binary()] + + # copy variable values over + for var, val in zip(MindtPy.variable_list, var_values): + if not var.is_binary(): + continue + var.value = val + + # check to make sure that binary variables are all 0 or 1 + for v in binary_vars: + if value(abs(v - 1)) > int_tol and value(abs(v)) > int_tol: + raise ValueError('Binary {} = {} is not 0 or 1'.format( + v.name, value(v))) + + if not binary_vars: # if no binary variables, skip + return + + # int_cut = (sum(1 - v for v in binary_vars + # if value(abs(v - 1)) <= int_tol) + + # sum(v for v in binary_vars + # if value(abs(v)) <= int_tol) >= 1) + + # MindtPy.MindtPy_linear_cuts.integer_cuts.add(expr=int_cut) + + pyomo_nogood_cut = sum(1 - v for v in binary_vars if value(abs(v - 1)) + <= int_tol) + sum(v for v in binary_vars if value(abs(v)) <= int_tol) + cplex_nogood_rhs = generate_standard_repn(pyomo_nogood_cut).constant + cplex_nogood_cut, _ = opt._get_expr_from_pyomo_expr(pyomo_nogood_cut) + + self.add(constraint=cplex.SparsePair(ind=cplex_nogood_cut.variables, val=cplex_nogood_cut.coefficients), + sense="G", + rhs=1 - cplex_nogood_rhs) + def handle_lazy_master_mip_feasible_sol(self, master_mip, solve_data, config, opt): """ This function is called during the branch and bound of master mip, more exactly when a feasible solution is found and LazyCallback is activated. - Copy the result to working model and update upper or lower bound + Copy the result to working model and update upper or lower bound. In LP-NLP, upper or lower bound are updated during solving the master problem + + Parameters + ---------- + master_mip: Pyomo model + the MIP master problem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + opt: SolverFactory + the mip solver """ # proceed. Just need integer values MindtPy = master_mip.MindtPy_utils @@ -121,19 +333,45 @@ def handle_lazy_master_mip_feasible_sol(self, master_mip, solve_data, config, op master_mip.MindtPy_utils.variable_list, solve_data.working_model.MindtPy_utils.variable_list, config) + # if config.strategy == 'GOA': + # if not config.add_nogood_cuts: + if main_objective.sense == minimize: + solve_data.LB = max( + self.get_best_objective_value(), solve_data.LB) + solve_data.LB_progress.append(solve_data.LB) + else: + solve_data.UB = min( + self.get_best_objective_value(), solve_data.UB) + solve_data.UB_progress.append(solve_data.UB) config.logger.info( - 'MIP %s: OBJ: %s LB: %s UB: %s' - % (solve_data.mip_iter, value(MindtPy.MindtPy_oa_obj.expr), + 'MIP %s: OBJ: %s Bound: %s LB: %s UB: %s' + % (solve_data.mip_iter, value(MindtPy.MindtPy_oa_obj.expr), self.get_best_objective_value(), solve_data.LB, solve_data.UB)) def handle_lazy_NLP_subproblem_optimal(self, fixed_nlp, solve_data, config, opt): - """Copies result to mip(explaination see below), updates bound, adds OA and integer cut, - stores best solution if new one is best""" - for c in fixed_nlp.tmp_duals: - if fixed_nlp.dual.get(c, None) is None: - fixed_nlp.dual[c] = fixed_nlp.tmp_duals[c] - dual_values = list(fixed_nlp.dual[c] - for c in fixed_nlp.MindtPy_utils.constraint_list) + """ + This function copies result to mip(explaination see below), updates bound, adds OA and integer cut, + stores best solution if new one is best + + Parameters + ---------- + fixed_nlp: Pyomo model + fixed NLP from the model + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + opt: SolverFactory + the mip solver + """ + if config.use_dual: + for c in fixed_nlp.tmp_duals: + if fixed_nlp.dual.get(c, None) is None: + fixed_nlp.dual[c] = fixed_nlp.tmp_duals[c] + dual_values = list(fixed_nlp.dual[c] + for c in fixed_nlp.MindtPy_utils.constraint_list) + else: + dual_values = None main_objective = next( fixed_nlp.component_data_objects(Objective, active=True)) @@ -154,50 +392,95 @@ def handle_lazy_NLP_subproblem_optimal(self, fixed_nlp, solve_data, config, opt) if solve_data.solution_improved: solve_data.best_solution_found = fixed_nlp.clone() + solve_data.best_solution_found_time = get_main_elapsed_time( + solve_data.timing) + if config.add_nogood_cuts: + if solve_data.results.problem.sense == ProblemSense.minimize: + solve_data.stored_bound.update( + {solve_data.UB: solve_data.LB}) + else: + solve_data.stored_bound.update( + {solve_data.LB: solve_data.UB}) + # In OA algorithm, OA cuts are generated based on the solution of the subproblem + # We need to first copy the value of variables from the subproblem and then add cuts + # since value(constr.body), value(jacs[constr][var]), value(var) are used in self.add_lazy_oa_cuts() + copy_var_list_values(fixed_nlp.MindtPy_utils.variable_list, + solve_data.mip.MindtPy_utils.variable_list, + config) if config.strategy == 'OA': - # In OA algorithm, OA cuts are generated based on the solution of the subproblem - # We need to first copy the value of variables from the subproblem and then add cuts - # since value(constr.body), value(jacs[constr][var]), value(var) are used in self.add_lazy_oa_cuts() - copy_var_list_values(fixed_nlp.MindtPy_utils.variable_list, - solve_data.mip.MindtPy_utils.variable_list, - config) self.add_lazy_oa_cuts( solve_data.mip, dual_values, solve_data, config, opt) + elif config.strategy == 'GOA': + self.add_lazy_affine_cuts(solve_data, config, opt) + if config.add_nogood_cuts: + var_values = list( + v.value for v in fixed_nlp.MindtPy_utils.variable_list) + self.add_lazy_nogood_cuts(var_values, solve_data, config, opt) def handle_lazy_NLP_subproblem_infeasible(self, fixed_nlp, solve_data, config, opt): - """Solve feasibility problem, add cut according to strategy. + """ + Solves feasibility problem and adds cut according to the specified strategy - The solution of the feasibility problem is copied to the working model. + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + opt: SolverFactory + the mip solver """ # TODO try something else? Reinitialize with different initial # value? config.logger.info('NLP subproblem was locally infeasible.') - for c in fixed_nlp.component_data_objects(ctype=Constraint): - rhs = ((0 if c.upper is None else c.upper) - + (0 if c.lower is None else c.lower)) - sign_adjust = 1 if value(c.upper) is None else -1 - fixed_nlp.dual[c] = (sign_adjust - * max(0, sign_adjust * (rhs - value(c.body)))) - dual_values = list(fixed_nlp.dual[c] - for c in fixed_nlp.MindtPy_utils.constraint_list) + if config.use_dual: + for c in fixed_nlp.component_data_objects(ctype=Constraint): + rhs = ((0 if c.upper is None else c.upper) + + (0 if c.lower is None else c.lower)) + sign_adjust = 1 if value(c.upper) is None else -1 + fixed_nlp.dual[c] = (sign_adjust + * max(0, sign_adjust * (rhs - value(c.body)))) + dual_values = list(fixed_nlp.dual[c] + for c in fixed_nlp.MindtPy_utils.constraint_list) + else: + dual_values = None - if config.strategy == 'OA': - config.logger.info('Solving feasibility problem') - if config.initial_feas: - # config.initial_feas = False - feas_NLP, feas_NLP_results = solve_NLP_feas(solve_data, config) - # In OA algorithm, OA cuts are generated based on the solution of the subproblem - # We need to first copy the value of variables from the subproblem and then add cuts - copy_var_list_values(feas_NLP.MindtPy_utils.variable_list, - solve_data.mip.MindtPy_utils.variable_list, - config) + config.logger.info('Solving feasibility problem') + if config.initial_feas: + # config.initial_feas = False + feas_NLP, feas_NLP_results = solve_NLP_feas(solve_data, config) + # In OA algorithm, OA cuts are generated based on the solution of the subproblem + # We need to first copy the value of variables from the subproblem and then add cuts + copy_var_list_values(feas_NLP.MindtPy_utils.variable_list, + solve_data.mip.MindtPy_utils.variable_list, + config) + if config.strategy == 'OA': self.add_lazy_oa_cuts( solve_data.mip, dual_values, solve_data, config, opt) + elif config.strategy == 'GOA': + self.add_lazy_affine_cuts(solve_data, config, opt) + if config.add_nogood_cuts: + var_values = list( + v.value for v in fixed_nlp.MindtPy_utils.variable_list) + self.add_lazy_nogood_cuts( + var_values, solve_data, config, opt) def handle_lazy_NLP_subproblem_other_termination(self, fixed_nlp, termination_condition, solve_data, config): - """Case that fix-NLP is neither optimal nor infeasible (i.e. max_iterations)""" + """ + Handles the result of the latest iteration of solving the NLP subproblem given a solution that is neither optimal + nor infeasible. + + Parameters + ---------- + termination_condition: Pyomo TerminationCondition + the termination condition of the NLP subproblem + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ if termination_condition is tc.maxIterations: # TODO try something else? Reinitialize with different initial value? config.logger.info( @@ -210,6 +493,10 @@ def handle_lazy_NLP_subproblem_other_termination(self, fixed_nlp, termination_co 'condition of {}'.format(termination_condition)) def __call__(self): + """ + This is an inherent function in LazyConstraintCallback in cplex. + This funtion is call whenever the a integer solution is found during the branch and bound process + """ solve_data = self.solve_data config = self.config opt = self.opt @@ -219,15 +506,31 @@ def __call__(self): self.handle_lazy_master_mip_feasible_sol( master_mip, solve_data, config, opt) + if solve_data.LB + config.bound_tolerance >= solve_data.UB: + config.logger.info( + 'MindtPy exiting on bound convergence. ' + 'LB: {} + (tol {}) >= UB: {}\n'.format( + solve_data.LB, config.bound_tolerance, solve_data.UB)) + solve_data.results.solver.termination_condition = tc.optimal + return + # else: # solve subproblem # Solve NLP subproblem # The constraint linearization happens in the handlers - fixed_nlp, fixed_nlp_result = solve_NLP_subproblem(solve_data, config) + fixed_nlp, fixed_nlp_result = solve_NLP_subproblem( + solve_data, config) # add oa cuts - if fixed_nlp_result.solver.termination_condition is tc.optimal or fixed_nlp_result.solver.termination_condition is tc.locallyOptimal: + if fixed_nlp_result.solver.termination_condition in {tc.optimal, tc.locallyOptimal, tc.feasible}: self.handle_lazy_NLP_subproblem_optimal( fixed_nlp, solve_data, config, opt) + if solve_data.LB + config.bound_tolerance >= solve_data.UB: + config.logger.info( + 'MindtPy exiting on bound convergence. ' + 'LB: {} + (tol {}) >= UB: {}\n'.format( + solve_data.LB, config.bound_tolerance, solve_data.UB)) + solve_data.results.solver.termination_condition = tc.optimal + return elif fixed_nlp_result.solver.termination_condition is tc.infeasible: self.handle_lazy_NLP_subproblem_infeasible( fixed_nlp, solve_data, config, opt) diff --git a/pyomo/contrib/mindtpy/tests/MINLP2_simple.py b/pyomo/contrib/mindtpy/tests/MINLP2_simple.py index b91a1a264ce..36a3ff46396 100644 --- a/pyomo/contrib/mindtpy/tests/MINLP2_simple.py +++ b/pyomo/contrib/mindtpy/tests/MINLP2_simple.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Re-implementation of example 1 of Outer approximation and ECP. Re-implementation of Duran example 1 as written by Westerlund @@ -28,8 +38,7 @@ from six import iteritems from pyomo.environ import (Binary, ConcreteModel, Constraint, NonNegativeReals, - Objective, Param, RangeSet, Var, exp, minimize, - maximize, log) + Objective, RangeSet, Var, minimize, log) class SimpleMINLP(ConcreteModel): diff --git a/pyomo/contrib/mindtpy/tests/MINLP3_simple.py b/pyomo/contrib/mindtpy/tests/MINLP3_simple.py index 5d0151e2926..f0437d97b73 100644 --- a/pyomo/contrib/mindtpy/tests/MINLP3_simple.py +++ b/pyomo/contrib/mindtpy/tests/MINLP3_simple.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Re-implementation of example 1 of Quesada and Grossmann. Re-implementation of Quesada example 2 MINLP test problem in Pyomo @@ -20,10 +30,8 @@ """ from __future__ import division -from six import iteritems - from pyomo.environ import (Binary, ConcreteModel, Constraint, Reals, - Objective, Param, RangeSet, Var, exp, minimize, log) + Objective, RangeSet, Var, minimize, log) class SimpleMINLP(ConcreteModel): diff --git a/pyomo/contrib/mindtpy/tests/MINLP_simple.py b/pyomo/contrib/mindtpy/tests/MINLP_simple.py index 06d76cc67d1..75277cfafdd 100644 --- a/pyomo/contrib/mindtpy/tests/MINLP_simple.py +++ b/pyomo/contrib/mindtpy/tests/MINLP_simple.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Implementation of MINLP problem in Assignment 6 of the Advanced PSE lecture at CMU. Author: David Bernal @@ -19,8 +29,8 @@ from six import iteritems from pyomo.environ import (Binary, ConcreteModel, Constraint, - NonNegativeReals, Objective, Param, - RangeSet, Var, exp, minimize) + NonNegativeReals, Objective, + RangeSet, Var, minimize) class SimpleMINLP(ConcreteModel): diff --git a/pyomo/contrib/mindtpy/tests/eight_process_problem.py b/pyomo/contrib/mindtpy/tests/eight_process_problem.py index 70e5bddad72..4b6e35b316d 100644 --- a/pyomo/contrib/mindtpy/tests/eight_process_problem.py +++ b/pyomo/contrib/mindtpy/tests/eight_process_problem.py @@ -32,7 +32,7 @@ class EightProcessFlowsheet(ConcreteModel): """Flowsheet for the 8 process problem.""" - def __init__(self, *args, **kwargs): + def __init__(self, convex=True, *args, **kwargs): """Create the flowsheet.""" kwargs.setdefault('name', 'DuranEx3') super(EightProcessFlowsheet, self).__init__(*args, **kwargs) @@ -96,14 +96,21 @@ def __init__(self, *args, **kwargs): """Constraint definitions""" # INPUT-OUTPUT RELATIONS FOR process units 1 through 8 - m.inout1 = Constraint(expr=exp(m.X[3]) - 1 == m.X[2]) - m.inout2 = Constraint(expr=exp(m.X[5] / 1.2) - 1 == m.X[4]) m.inout3 = Constraint(expr=1.5 * m.X[9] + m.X[10] == m.X[8]) m.inout4 = Constraint(expr=1.25 * (m.X[12] + m.X[14]) == m.X[13]) m.inout5 = Constraint(expr=m.X[15] == 2 * m.X[16]) - m.inout6 = Constraint(expr=exp(m.X[20] / 1.5) - 1 == m.X[19]) - m.inout7 = Constraint(expr=exp(m.X[22]) - 1 == m.X[21]) - m.inout8 = Constraint(expr=exp(m.X[18]) - 1 == m.X[10] + m.X[17]) + if convex: + m.inout1 = Constraint(expr=exp(m.X[3]) - 1 <= m.X[2]) + m.inout2 = Constraint(expr=exp(m.X[5] / 1.2) - 1 <= m.X[4]) + m.inout7 = Constraint(expr=exp(m.X[22]) - 1 <= m.X[21]) + m.inout8 = Constraint(expr=exp(m.X[18]) - 1 <= m.X[10] + m.X[17]) + m.inout6 = Constraint(expr=exp(m.X[20] / 1.5) - 1 <= m.X[19]) + else: + m.inout1 = Constraint(expr=exp(m.X[3]) - 1 == m.X[2]) + m.inout2 = Constraint(expr=exp(m.X[5] / 1.2) - 1 == m.X[4]) + m.inout7 = Constraint(expr=exp(m.X[22]) - 1 == m.X[21]) + m.inout8 = Constraint(expr=exp(m.X[18]) - 1 == m.X[10] + m.X[17]) + m.inout6 = Constraint(expr=exp(m.X[20] / 1.5) - 1 == m.X[19]) # Mass balance equations m.massbal1 = Constraint(expr=m.X[13] == m.X[19] + m.X[21]) @@ -144,6 +151,8 @@ def __init__(self, *args, **kwargs): """Bound definitions""" # x (flow) upper bounds - x_ubs = {3: 2, 5: 2, 9: 2, 10: 1, 14: 1, 17: 2, 19: 2, 21: 2, 25: 3} + # x_ubs = {3: 2, 5: 2, 9: 2, 10: 1, 14: 1, 17: 2, 19: 2, 21: 2, 25: 3} + x_ubs = {2: 10, 3: 2, 4: 10, 5: 2, 9: 2, 10: 1, 14: 1, 17: 2, 18: 10, 19: 2, + 20: 10, 21: 2, 22: 10, 25: 3} # add bounds for variables in nonlinear constraints for i, x_ub in iteritems(x_ubs): X[i].setub(x_ub) diff --git a/pyomo/contrib/mindtpy/tests/nonconvex1.py b/pyomo/contrib/mindtpy/tests/nonconvex1.py new file mode 100644 index 00000000000..3295e52c926 --- /dev/null +++ b/pyomo/contrib/mindtpy/tests/nonconvex1.py @@ -0,0 +1,35 @@ +"""Problem A in paper "Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs" + +Ref: +Kesavan P, Allgor R J, Gatzke E P, et al. Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs[J]. Mathematical Programming, 2004, 100(3): 517-535. + +Problem type: nonconvex MINLP + size: 3 binary variable + 2 continuous variables + 6 constraints + +""" +from pyomo.environ import * + + +class Nonconvex1(ConcreteModel): + def __init__(self, *args, **kwargs): + """Create the problem.""" + kwargs.setdefault('name', 'Nonconvex1') + super(Nonconvex1, self).__init__(*args, **kwargs) + m = self + + m.x1 = Var(within=Reals, bounds=(0, 10)) + m.x2 = Var(within=Reals, bounds=(0, 10)) + m.y1 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y2 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y3 = Var(within=Binary, bounds=(0, 1), initialize=0) + + m.objective = Objective(expr=2 * m.x1 + 3 * m.x2 + 1.5 * m.y1 + + 2 * m.y2 - 0.5 * m.y3, sense=minimize) + + m.c1 = Constraint(expr=m.x1 * m.x1 + m.y1 == 1.25) + m.c2 = Constraint(expr=m.x2 ** 1.5 + 1.5 * m.y2 == 3) + m.c4 = Constraint(expr=m.x1 + m.y1 <= 1.6) + m.c5 = Constraint(expr=1.333 * m.x2 + m.y2 <= 3) + m.c6 = Constraint(expr=-m.y1 - m.y2 + m.y3 <= 0) diff --git a/pyomo/contrib/mindtpy/tests/nonconvex2.py b/pyomo/contrib/mindtpy/tests/nonconvex2.py new file mode 100644 index 00000000000..57368d1a1ee --- /dev/null +++ b/pyomo/contrib/mindtpy/tests/nonconvex2.py @@ -0,0 +1,47 @@ +"""Problem B in paper "Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs" + +Ref: +Kesavan P, Allgor R J, Gatzke E P, et al. Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs[J]. Mathematical Programming, 2004, 100(3): 517-535. + +Problem type: nonconvex MINLP + size: 8 binary variable + 3 continuous variables + 7 constraints + +""" +from pyomo.environ import * + + +class Nonconvex2(ConcreteModel): + def __init__(self, *args, **kwargs): + """Create the problem.""" + kwargs.setdefault('name', 'Nonconvex2') + super(Nonconvex2, self).__init__(*args, **kwargs) + m = self + + m.x1 = Var(within=Reals, bounds=(0, 0.9970)) + m.x2 = Var(within=Reals, bounds=(0, 0.9985)) + m.x3 = Var(within=Reals, bounds=(0, 0.9988)) + m.y1 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y2 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y3 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y4 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y5 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y6 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y7 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y8 = Var(within=Binary, bounds=(0, 1), initialize=0) + + m.objective = Objective(expr=- m.x1 * m.x2 * m.x3, sense=minimize) + + m.c1 = Constraint(expr=-log(1 - m.x1) + log(0.1) * m.y1 + + log(0.2) * m.y2 + log(0.15) * m.y3 == 0) + m.c2 = Constraint(expr=-log(1 - m.x2) + log(0.05) * m.y4 + + log(0.2) * m.y5 + log(0.15) * m.y6 == 0) + m.c3 = Constraint(expr=-log(1 - m.x3) + log(0.02) * m.y7 + + log(0.06) * m.y8 == 0) + + m.c4 = Constraint(expr=-m.y1 - m.y2 - m.y3 <= -1) + m.c5 = Constraint(expr=-m.y4 - m.y5 - m.y6 <= -1) + m.c6 = Constraint(expr=-m.y7 - m.y8 <= -1) + m.c7 = Constraint(expr=3 * m.y1 + m.y2 + 2 * m.y3 + 3 * + m.y4 + 2 * m.y5 + m.y6 + 3 * m.y7 + 2 * m.y8 <= 10) diff --git a/pyomo/contrib/mindtpy/tests/nonconvex3.py b/pyomo/contrib/mindtpy/tests/nonconvex3.py new file mode 100644 index 00000000000..ec878ea9fef --- /dev/null +++ b/pyomo/contrib/mindtpy/tests/nonconvex3.py @@ -0,0 +1,39 @@ +"""Problem C in paper "Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs" + +Ref: +Kesavan P, Allgor R J, Gatzke E P, et al. Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs[J]. Mathematical Programming, 2004, 100(3): 517-535. + +Problem type: nonconvex MINLP + size: 6 binary variable + 2 continuous variables + 6 constraints + +""" +from pyomo.environ import * + + +class Nonconvex3(ConcreteModel): + def __init__(self, *args, **kwargs): + """Create the problem.""" + kwargs.setdefault('name', 'Nonconvex3') + super(Nonconvex3, self).__init__(*args, **kwargs) + m = self + + m.x1 = Var(within=Reals, bounds=(1, 5)) + m.x2 = Var(within=Reals, bounds=(1, 5)) + m.y1 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y2 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y3 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y4 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y5 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y6 = Var(within=Binary, bounds=(0, 1), initialize=0) + + m.objective = Objective(expr=7 * m.x1 + 10 * m.x2, sense=minimize) + + m.c1 = Constraint(expr=(m.x1 ** 1.2) * (m.x2 ** 1.7) - + 7 * m.x1 - 9 * m.x2 <= -24) + m.c2 = Constraint(expr=-m.x1 - 2 * m.x2 <= 5) + m.c3 = Constraint(expr=-3 * m.x1 + m.x2 <= 1) + m.c4 = Constraint(expr=4 * m.x1 - 3 * m.x2 <= 11) + m.c5 = Constraint(expr=-m.x1 + m.y1 + 2 * m.y2 + 4 * m.y3 == 0) + m.c6 = Constraint(expr=-m.x2 + m.y4 + 2 * m.y5 + m.y6 == 0) diff --git a/pyomo/contrib/mindtpy/tests/nonconvex4.py b/pyomo/contrib/mindtpy/tests/nonconvex4.py new file mode 100644 index 00000000000..f41a97d5fcf --- /dev/null +++ b/pyomo/contrib/mindtpy/tests/nonconvex4.py @@ -0,0 +1,34 @@ +"""Problem D in paper "Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs" + +Ref: +Kesavan P, Allgor R J, Gatzke E P, et al. Outer approximation algorithms for separable nonconvex mixed-integer nonlinear programs[J]. Mathematical Programming, 2004, 100(3): 517-535. + +Problem type: nonconvex MINLP + size: 3 binary variable + 2 continuous variables + 4 constraints + +""" +from pyomo.environ import * + + +class Nonconvex4(ConcreteModel): + def __init__(self, *args, **kwargs): + """Create the problem.""" + kwargs.setdefault('name', 'Nonconvex4') + super(Nonconvex4, self).__init__(*args, **kwargs) + m = self + + m.x1 = Var(within=Reals, bounds=(1, 10)) + m.x2 = Var(within=Reals, bounds=(1, 6)) + m.y1 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y2 = Var(within=Binary, bounds=(0, 1), initialize=0) + m.y3 = Var(within=Binary, bounds=(0, 1), initialize=0) + + m.objective = Objective(expr=-5 * m.x1 + 3 * m.x2, sense=minimize) + + m.c1 = Constraint(expr=2 * (m.x2 ** 2) - 2 * (m.x2 ** 0.5) - + 2 * (m.x1 ** 0.5) * (m.x2 ** 2) + 11 * m.x2 + 8 * m.x1 <= 39) + m.c2 = Constraint(expr=m.x1 - m.x2 <= 3) + m.c3 = Constraint(expr=3 * m.x1 + 2 * m.x2 <= 24) + m.c4 = Constraint(expr=-m.x1 + m.y1 + 2 * m.y2 + 4 * m.y3 == 0) diff --git a/pyomo/contrib/mindtpy/tests/online_doc_example.py b/pyomo/contrib/mindtpy/tests/online_doc_example.py index a7199eadffa..bb0d2631e71 100644 --- a/pyomo/contrib/mindtpy/tests/online_doc_example.py +++ b/pyomo/contrib/mindtpy/tests/online_doc_example.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """ Example in the online doc. The expected optimal solution value is 2.438447187191098. @@ -10,10 +20,8 @@ """ from __future__ import division -from six import iteritems - -from pyomo.environ import (Binary, ConcreteModel, Constraint, Reals, - Objective, Param, RangeSet, Var, exp, minimize, log) +from pyomo.environ import (Binary, ConcreteModel, Constraint, + Objective, Var, minimize, log) class OnlineDocExample(ConcreteModel): diff --git a/pyomo/contrib/mindtpy/tests/test_mindtpy.py b/pyomo/contrib/mindtpy/tests/test_mindtpy.py index 735439650a1..98983d81a15 100644 --- a/pyomo/contrib/mindtpy/tests/test_mindtpy.py +++ b/pyomo/contrib/mindtpy/tests/test_mindtpy.py @@ -1,5 +1,14 @@ -"""Tests for the MINDT solver plugin.""" -from math import fabs +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +"""Tests for the MindtPy solver.""" import pyomo.core.base.symbolic import pyutilib.th as unittest from pyomo.contrib.mindtpy.tests.eight_process_problem import \ @@ -10,11 +19,9 @@ from pyomo.contrib.mindtpy.tests.from_proposal import ProposalModel from pyomo.contrib.mindtpy.tests.constraint_qualification_example import ConstraintQualificationExample from pyomo.contrib.mindtpy.tests.online_doc_example import OnlineDocExample -from pyomo.environ import SolverFactory, value -from pyomo.environ import * +from pyomo.environ import SolverFactory, value, maximize from pyomo.solvers.tests.models.LP_unbounded import LP_unbounded from pyomo.solvers.tests.models.QCP_simple import QCP_simple -from pyomo.solvers.tests.models.MIQCP_simple import MIQCP_simple from pyomo.opt import TerminationCondition required_solvers = ('ipopt', 'glpk') @@ -36,7 +43,7 @@ class TestMindtPy(unittest.TestCase): def test_OA_8PP(self): """Test the outer approximation decomposition algorithm.""" with SolverFactory('mindtpy') as opt: - model = EightProcessFlowsheet() + model = EightProcessFlowsheet(convex=False) print('\n Solving 8PP problem with Outer Approximation') results = opt.solve(model, strategy='OA', init_strategy='rNLP', @@ -51,7 +58,7 @@ def test_OA_8PP(self): def test_OA_8PP_init_max_binary(self): """Test the outer approximation decomposition algorithm.""" with SolverFactory('mindtpy') as opt: - model = EightProcessFlowsheet() + model = EightProcessFlowsheet(convex=False) print('\n Solving 8PP problem with Outer Approximation(max_binary)') results = opt.solve(model, strategy='OA', init_strategy='max_binary', @@ -65,7 +72,7 @@ def test_OA_8PP_init_max_binary(self): def test_OA_8PP_L2_norm(self): """Test the outer approximation decomposition algorithm.""" with SolverFactory('mindtpy') as opt: - model = EightProcessFlowsheet() + model = EightProcessFlowsheet(convex=False) print('\n Solving 8PP problem with Outer Approximation(max_binary)') results = opt.solve(model, strategy='OA', mip_solver=required_solvers[1], @@ -79,7 +86,7 @@ def test_OA_8PP_L2_norm(self): def test_OA_8PP_sympy(self): """Test the outer approximation decomposition algorithm.""" with SolverFactory('mindtpy') as opt: - model = EightProcessFlowsheet() + model = EightProcessFlowsheet(convex=False) print('\n Solving 8PP problem with Outer Approximation(max_binary)') results = opt.solve(model, strategy='OA', mip_solver=required_solvers[1], @@ -137,8 +144,7 @@ def test_OA_MINLP_simple(self): results = opt.solve(model, strategy='OA', init_strategy='initial_binary', mip_solver=required_solvers[1], - nlp_solver=required_solvers[0], - obj_bound=10) + nlp_solver=required_solvers[0]) self.assertIs(results.solver.termination_condition, TerminationCondition.optimal) @@ -152,8 +158,7 @@ def test_OA_MINLP2_simple(self): results = opt.solve(model, strategy='OA', init_strategy='initial_binary', mip_solver=required_solvers[1], - nlp_solver=required_solvers[0], - obj_bound=10) + nlp_solver=required_solvers[0]) self.assertIs(results.solver.termination_condition, TerminationCondition.optimal) @@ -166,8 +171,7 @@ def test_OA_MINLP3_simple(self): print('\n Solving MINLP3_simple problem with Outer Approximation') results = opt.solve(model, strategy='OA', init_strategy='initial_binary', mip_solver=required_solvers[1], - nlp_solver=required_solvers[0], - obj_bound=10) + nlp_solver=required_solvers[0]) self.assertIs(results.solver.termination_condition, TerminationCondition.optimal) @@ -194,7 +198,7 @@ def test_OA_Proposal_with_int_cuts(self): results = opt.solve(model, strategy='OA', mip_solver=required_solvers[1], nlp_solver=required_solvers[0], - add_integer_cuts=True, + add_nogood_cuts=True, integer_to_binary=True # if we use lazy callback, we cannot set integer_to_binary True ) @@ -222,10 +226,10 @@ def test_OA_ConstraintQualificationExample_integer_cut(self): results = opt.solve(model, strategy='OA', mip_solver=required_solvers[1], nlp_solver=required_solvers[0], - add_integer_cuts=True + add_nogood_cuts=True ) self.assertIs(results.solver.termination_condition, - TerminationCondition.feasible) + TerminationCondition.optimal) self.assertAlmostEqual(value(model.objective.expr), 3, places=2) def test_OA_OnlineDocExample(self): @@ -337,7 +341,6 @@ def test_initial_binary_add_slack(self): init_strategy='initial_binary', mip_solver=required_solvers[1], nlp_solver=required_solvers[0], - obj_bound=10, add_slack=True) self.assertIs(results.solver.termination_condition, diff --git a/pyomo/contrib/mindtpy/tests/test_mindtpy_ECP.py b/pyomo/contrib/mindtpy/tests/test_mindtpy_ECP.py new file mode 100644 index 00000000000..75a5631c755 --- /dev/null +++ b/pyomo/contrib/mindtpy/tests/test_mindtpy_ECP.py @@ -0,0 +1,289 @@ +"""Tests for the MindtPy solver.""" +from math import fabs +import pyomo.core.base.symbolic +import pyutilib.th as unittest +from pyomo.contrib.mindtpy.tests.eight_process_problem import \ + EightProcessFlowsheet +from pyomo.contrib.mindtpy.tests.MINLP_simple import SimpleMINLP as SimpleMINLP +from pyomo.contrib.mindtpy.tests.MINLP2_simple import SimpleMINLP as SimpleMINLP2 +from pyomo.contrib.mindtpy.tests.MINLP3_simple import SimpleMINLP as SimpleMINLP3 +from pyomo.contrib.mindtpy.tests.from_proposal import ProposalModel +from pyomo.contrib.mindtpy.tests.constraint_qualification_example import ConstraintQualificationExample +from pyomo.contrib.mindtpy.tests.online_doc_example import OnlineDocExample +from pyomo.environ import SolverFactory, value +from pyomo.environ import * +from pyomo.solvers.tests.models.LP_unbounded import LP_unbounded +from pyomo.solvers.tests.models.QCP_simple import QCP_simple +from pyomo.solvers.tests.models.MIQCP_simple import MIQCP_simple +from pyomo.opt import TerminationCondition + +required_solvers = ('ipopt', 'glpk') +# required_solvers = ('gams', 'gams') +if all(SolverFactory(s).available() for s in required_solvers): + subsolvers_available = True +else: + subsolvers_available = False + + +@unittest.skipIf(not subsolvers_available, + "Required subsolvers %s are not available" + % (required_solvers,)) +@unittest.skipIf(not pyomo.core.base.symbolic.differentiate_available, + "Symbolic differentiation is not available") +class TestMindtPy(unittest.TestCase): + """Tests for the MindtPy solver plugin.""" + + def test_ECP_8PP(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with extended cutting plane') + results = opt.solve(model, strategy='ECP', + init_strategy='rNLP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + bound_tolerance=1E-5) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_ECP_8PP_init_max_binary(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with extended cutting plane(max_binary)') + results = opt.solve(model, strategy='ECP', + init_strategy='max_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], obj_bound=5500) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_ECP_8PP_L2_norm(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with extended cutting plane(max_binary)') + results = opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], obj_bound=5500, + feasibility_norm='L2') + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_ECP_8PP_sympy(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with extended cutting plane(max_binary)') + results = opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], obj_bound=5500, + differentiate_mode='sympy') + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_ECP_MINLP_simple(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP() + print('\n Solving MINLP_simple problem with extended cutting plane') + results = opt.solve(model, strategy='ECP', + init_strategy='initial_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0]) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 3.5, places=2) + + def test_ECP_MINLP2_simple(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP2() + print('\n Solving MINLP2_simple problem with extended cutting plane') + results = opt.solve(model, strategy='ECP', + init_strategy='initial_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0]) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 6.00976, places=2) + + def test_ECP_MINLP3_simple(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP3() + print('\n Solving MINLP3_simple problem with extended cutting plane') + results = opt.solve(model, strategy='ECP', init_strategy='initial_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0]) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), -5.512, places=2) + + def test_ECP_Proposal(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = ProposalModel() + print('\n Solving Proposal problem with extended cutting plane') + results = opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0]) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.obj.expr), 0.66555, places=2) + + def test_ECP_ConstraintQualificationExample(self): + with SolverFactory('mindtpy') as opt: + model = ConstraintQualificationExample() + print( + '\n Solving Constraint Qualification Example with extended cutting plane') + results = opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], bound_tolerance=1e-5 + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.objective.expr), 3, places=2) + + def test_ECP_OnlineDocExample(self): + with SolverFactory('mindtpy') as opt: + model = OnlineDocExample() + print('\n Solving Online Doc Example with extended cutting plane') + results = opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], bound_tolerance=1e-3 + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 2.438447, places=2) + + def test_ECP_OnlineDocExample_L_infinity_norm(self): + with SolverFactory('mindtpy') as opt: + model = OnlineDocExample() + print('\n Solving Online Doc Example with extended cutting plane') + results = opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], bound_tolerance=1e-3, + feasibility_norm="L_infinity" + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 2.438447, places=2) + + # the following tests are used to improve code coverage + + def test_iteration_limit(self): + with SolverFactory('mindtpy') as opt: + model = ConstraintQualificationExample() + print('\n test iteration_limit to improve code coverage') + opt.solve(model, strategy='ECP', + iteration_limit=1, + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0] + ) + # self.assertAlmostEqual(value(model.objective.expr), 3, places=2) + + def test_time_limit(self): + with SolverFactory('mindtpy') as opt: + model = ConstraintQualificationExample() + print('\n test time_limit to improve code coverage') + opt.solve(model, strategy='ECP', + time_limit=1, + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0] + ) + + def test_LP_case(self): + with SolverFactory('mindtpy') as opt: + m_class = LP_unbounded() + m_class._generate_model() + model = m_class.model + print('\n Solving LP case with extended cutting plane') + opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + ) + + def test_QCP_case(self): + with SolverFactory('mindtpy') as opt: + m_class = QCP_simple() + m_class._generate_model() + model = m_class.model + print('\n Solving QCP case with extended cutting plane') + opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + ) + + def test_maximize_obj(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = ProposalModel() + model.obj.sense = maximize + print('\n test maximize case to improve code coverage') + opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + # mip_solver_args={'timelimit': 0.9} + ) + self.assertAlmostEqual(value(model.obj.expr), 14.83, places=1) + + def test_rNLP_add_slack(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print( + '\n Test rNLP initialize strategy and add_slack to improve code coverage') + opt.solve(model, strategy='ECP', + init_strategy='rNLP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + bound_tolerance=1E-5, + add_slack=True) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_initial_binary_add_slack(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP() + print( + '\n Test initial_binary initialize strategy and add_slack to improve code coverage') + results = opt.solve(model, strategy='ECP', + init_strategy='initial_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_slack=True) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 3.5, places=2) + + def test_maximize_obj(self): + """Test the extended cutting plane decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = ProposalModel() + model.obj.sense = maximize + print('\n test maximize case to improve code coverage') + opt.solve(model, strategy='ECP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0] + ) + self.assertAlmostEqual(value(model.obj.expr), 14.83, places=1) + + +if __name__ == "__main__": + unittest.main() diff --git a/pyomo/contrib/mindtpy/tests/test_mindtpy_global.py b/pyomo/contrib/mindtpy/tests/test_mindtpy_global.py new file mode 100644 index 00000000000..401fb794672 --- /dev/null +++ b/pyomo/contrib/mindtpy/tests/test_mindtpy_global.py @@ -0,0 +1,293 @@ +"""Tests for the MindtPy solver.""" +from math import fabs +import pyomo.core.base.symbolic +import pyutilib.th as unittest +from pyomo.contrib.mindtpy.tests.eight_process_problem import \ + EightProcessFlowsheet +from pyomo.contrib.mindtpy.tests.MINLP_simple import SimpleMINLP as SimpleMINLP +from pyomo.contrib.mindtpy.tests.MINLP2_simple import SimpleMINLP as SimpleMINLP2 +from pyomo.contrib.mindtpy.tests.MINLP3_simple import SimpleMINLP as SimpleMINLP3 +from pyomo.contrib.mindtpy.tests.from_proposal import ProposalModel +from pyomo.contrib.mindtpy.tests.constraint_qualification_example import ConstraintQualificationExample +from pyomo.contrib.mindtpy.tests.online_doc_example import OnlineDocExample +from pyomo.contrib.mindtpy.tests.nonconvex1 import Nonconvex1 +from pyomo.contrib.mindtpy.tests.nonconvex2 import Nonconvex2 +from pyomo.contrib.mindtpy.tests.nonconvex3 import Nonconvex3 +from pyomo.contrib.mindtpy.tests.nonconvex4 import Nonconvex4 +from pyomo.environ import SolverFactory, value +from pyomo.environ import * +from pyomo.solvers.tests.models.LP_unbounded import LP_unbounded +from pyomo.solvers.tests.models.QCP_simple import QCP_simple +from pyomo.solvers.tests.models.MIQCP_simple import MIQCP_simple +from pyomo.opt import TerminationCondition + +required_solvers = ('baron', 'cplex') +if not all(SolverFactory(s).available(False) for s in required_solvers): + subsolvers_available = False +elif not SolverFactory('baron').license_is_valid(): + subsolvers_available = False +else: + subsolvers_available = True + + +@unittest.skipIf(not subsolvers_available, + "Required subsolvers %s are not available" + % (required_solvers,)) +@unittest.skipIf(not pyomo.core.base.symbolic.differentiate_available, + "Symbolic differentiation is not available") +@unittest.skipIf(not pyomo.contrib.mcpp.pyomo_mcpp.mcpp_available(), + "MC++ is not available") +class TestMindtPy(unittest.TestCase): + """Tests for the MindtPy solver plugin.""" + + def test_GOA_8PP(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + init_strategy='rNLP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + bound_tolerance=1E-5) + + self.assertIn(results.solver.termination_condition, [ + TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_8PP_init_max_binary(self): + """Test the outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation(max_binary)') + results = opt.solve(model, strategy='GOA', + init_strategy='max_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True) + + self.assertIn(results.solver.termination_condition, [ + TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_8PP_L2_norm(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation(L2_norm)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + feasibility_norm='L2', + add_nogood_cuts=True) + + self.assertIn(results.solver.termination_condition, [ + TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_8PP_sympy(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation(sympy)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + differentiate_mode='sympy', + add_nogood_cuts=True) + + self.assertIn(results.solver.termination_condition, [ + TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_MINLP_simple(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP() + print('\n Solving MINLP_simple problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + obj_bound=10, + add_nogood_cuts=True) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 3.5, places=2) + + def test_GOA_MINLP2_simple(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP2() + print('\n Solving MINLP2_simple problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + init_strategy='initial_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + obj_bound=10, + add_nogood_cuts=True) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 6.00976, places=2) + + def test_GOA_MINLP3_simple(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP3() + print('\n Solving MINLP3_simple problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', init_strategy='initial_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + obj_bound=10, + add_nogood_cuts=True, + use_mcpp=True) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), -5.512, places=2) + + def test_GOA_Proposal(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = ProposalModel() + print('\n Solving Proposal problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + integer_to_binary=True) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.obj.expr), 0.66555, places=2) + + def test_GOA_Proposal_with_int_cuts(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = ProposalModel() + print('\n Solving Proposal problem with Outer Approximation(integer cuts)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + integer_to_binary=True # if we use lazy callback, we cannot set integer_to_binary True + ) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.obj.expr), 0.66555, places=2) + + def test_GOA_ConstraintQualificationExample(self): + with SolverFactory('mindtpy') as opt: + model = ConstraintQualificationExample() + print( + '\n Solving Constraint Qualification Example with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + ) + # self.assertIs(results.solver.termination_condition, + # TerminationCondition.optimal) + self.assertAlmostEqual(value(model.objective.expr), 3, places=2) + + def test_GOA_ConstraintQualificationExample_integer_cut(self): + with SolverFactory('mindtpy') as opt: + model = ConstraintQualificationExample() + print( + '\n Solving Constraint Qualification Example with global Outer Approximation(integer cut)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True + ) + self.assertIn(results.solver.termination_condition, [ + TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.objective.expr), 3, places=2) + + def test_GOA_OnlineDocExample(self): + with SolverFactory('mindtpy') as opt: + model = OnlineDocExample() + print('\n Solving Online Doc Example with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True + ) + # self.assertIs(results.solver.termination_condition, + # TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 2.438447, places=2) + + def test_GOA_OnlineDocExample_L_infinity_norm(self): + with SolverFactory('mindtpy') as opt: + model = OnlineDocExample() + print('\n Solving Online Doc Example with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + feasibility_norm="L_infinity", + add_nogood_cuts=True, + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 2.438447, places=2) + + def test_GOA_Nonconvex1(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex1() + print('\n Solving Nonconvex1 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 7.667, places=2) + + def test_GOA_Nonconvex2(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex2() + print('\n Solving Nonconvex2 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), -0.94347, places=2) + + def test_GOA_Nonconvex3(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex3() + print('\n Solving Nonconvex3 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 31, places=2) + + def test_GOA_Nonconvex4(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex4() + print('\n Solving Nonconvex4 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), -17, places=2) + + +if __name__ == "__main__": + unittest.main() diff --git a/pyomo/contrib/mindtpy/tests/test_mindtpy_global_lp_nlp.py b/pyomo/contrib/mindtpy/tests/test_mindtpy_global_lp_nlp.py new file mode 100644 index 00000000000..bafa8fe703e --- /dev/null +++ b/pyomo/contrib/mindtpy/tests/test_mindtpy_global_lp_nlp.py @@ -0,0 +1,311 @@ +"""Tests for the MindtPy solver.""" +from math import fabs +import pyomo.core.base.symbolic +import pyutilib.th as unittest +from pyomo.contrib.mindtpy.tests.eight_process_problem import \ + EightProcessFlowsheet +from pyomo.contrib.mindtpy.tests.MINLP_simple import SimpleMINLP as SimpleMINLP +from pyomo.contrib.mindtpy.tests.MINLP2_simple import SimpleMINLP as SimpleMINLP2 +from pyomo.contrib.mindtpy.tests.MINLP3_simple import SimpleMINLP as SimpleMINLP3 +from pyomo.contrib.mindtpy.tests.from_proposal import ProposalModel +from pyomo.contrib.mindtpy.tests.constraint_qualification_example import ConstraintQualificationExample +from pyomo.contrib.mindtpy.tests.online_doc_example import OnlineDocExample +from pyomo.contrib.mindtpy.tests.nonconvex1 import Nonconvex1 +from pyomo.contrib.mindtpy.tests.nonconvex2 import Nonconvex2 +from pyomo.contrib.mindtpy.tests.nonconvex3 import Nonconvex3 +from pyomo.contrib.mindtpy.tests.nonconvex4 import Nonconvex4 +from pyomo.environ import SolverFactory, value +from pyomo.environ import * +from pyomo.solvers.tests.models.LP_unbounded import LP_unbounded +from pyomo.solvers.tests.models.QCP_simple import QCP_simple +from pyomo.solvers.tests.models.MIQCP_simple import MIQCP_simple + +from pyomo.opt import TerminationCondition + +required_solvers = ('baron', 'cplex_persistent') +if not all(SolverFactory(s).available(False) for s in required_solvers): + subsolvers_available = False +elif not SolverFactory('baron').license_is_valid(): + subsolvers_available = False +else: + subsolvers_available = True + + +@unittest.skipIf(not subsolvers_available, + "Required subsolvers %s are not available" + % (required_solvers,)) +@unittest.skipIf(not pyomo.core.base.symbolic.differentiate_available, + "Symbolic differentiation is not available") +@unittest.skipIf(not pyomo.contrib.mcpp.pyomo_mcpp.mcpp_available(), + "MC++ is not available") +class TestMindtPy(unittest.TestCase): + """Tests for the MindtPy solver plugin.""" + + def test_GOA_8PP(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + init_strategy='rNLP', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + bound_tolerance=1E-5, + single_tree=True) + + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_8PP_init_max_binary(self): + """Test the outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation(max_binary)') + results = opt.solve(model, strategy='GOA', + init_strategy='max_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + single_tree=True) + + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_8PP_L2_norm(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation(L2_norm)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + feasibility_norm='L2', + add_nogood_cuts=True, + single_tree=True) + + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_8PP_sympy(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = EightProcessFlowsheet() + print('\n Solving 8PP problem with Outer Approximation(sympy)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + differentiate_mode='sympy', + add_nogood_cuts=True, + single_tree=True) + + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.cost.expr), 68, places=1) + + def test_GOA_MINLP_simple(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP() + print('\n Solving MINLP_simple problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + obj_bound=10, + add_nogood_cuts=True, + single_tree=True) + + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 3.5, places=2) + + # if no affine cuts is added in lp/nlp , stop + + def test_GOA_MINLP2_simple(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP2() + print('\n Solving MINLP2_simple problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + add_nogood_cuts=True, + single_tree=True, + bound_tolerance=1E-2 + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.cost.expr), 6.00976, places=2) + + def test_GOA_MINLP3_simple(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = SimpleMINLP3() + print('\n Solving MINLP3_simple problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', init_strategy='initial_binary', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + obj_bound=10, + add_nogood_cuts=True, + use_mcpp=True, + single_tree=True) + self.assertIs(results.solver.termination_condition, + TerminationCondition.feasible) + self.assertAlmostEqual(value(model.cost.expr), -5.512, places=2) + + def test_GOA_Proposal(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = ProposalModel() + print('\n Solving Proposal problem with Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + integer_to_binary=True, + single_tree=True) + + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.obj.expr), 0.66555, places=2) + + def test_GOA_Proposal_with_int_cuts(self): + """Test the global outer approximation decomposition algorithm.""" + with SolverFactory('mindtpy') as opt: + model = ProposalModel() + print('\n Solving Proposal problem with Outer Approximation(integer cuts)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + integer_to_binary=True, # if we use lazy callback, we cannot set integer_to_binary True + single_tree=True + ) + + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.obj.expr), 0.66555, places=2) + + def test_GOA_ConstraintQualificationExample(self): + with SolverFactory('mindtpy') as opt: + model = ConstraintQualificationExample() + print( + '\n Solving Constraint Qualification Example with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + single_tree=True + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual(value(model.objective.expr), 3, places=2) + + def test_GOA_ConstraintQualificationExample_integer_cut(self): + with SolverFactory('mindtpy') as opt: + model = ConstraintQualificationExample() + print( + '\n Solving Constraint Qualification Example with global Outer Approximation(integer cut)') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + single_tree=True + ) + self.assertIn(results.solver.termination_condition, [ + TerminationCondition.optimal, TerminationCondition.feasible]) + self.assertAlmostEqual(value(model.objective.expr), 3, places=2) + + def test_GOA_OnlineDocExample(self): + with SolverFactory('mindtpy') as opt: + model = OnlineDocExample() + print('\n Solving Online Doc Example with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + add_nogood_cuts=True, + single_tree=True + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 2.438447, places=2) + + def test_GOA_OnlineDocExample_L_infinity_norm(self): + with SolverFactory('mindtpy') as opt: + model = OnlineDocExample() + print('\n Solving Online Doc Example with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver=required_solvers[0], + feasibility_norm="L_infinity", + add_nogood_cuts=True, + single_tree=True + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 2.438447, places=2) + + def test_GOA_Nonconvex1(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex1() + print('\n Solving Nonconvex1 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + single_tree=True + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 7.667, places=2) + + def test_GOA_Nonconvex2(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex2() + print('\n Solving Nonconvex2 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + single_tree=True, + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), -0.94347, places=2) + + def test_GOA_Nonconvex3(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex3() + print('\n Solving Nonconvex3 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + single_tree=True + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), 31, places=2) + + def test_GOA_Nonconvex4(self): + with SolverFactory('mindtpy') as opt: + model = Nonconvex4() + print('\n Solving Nonconvex4 with global Outer Approximation') + results = opt.solve(model, strategy='GOA', + mip_solver=required_solvers[1], + nlp_solver='baron', + single_tree=True, + ) + self.assertIs(results.solver.termination_condition, + TerminationCondition.optimal) + self.assertAlmostEqual( + value(model.objective.expr), -17, places=2) + + +if __name__ == "__main__": + unittest.main() diff --git a/pyomo/contrib/mindtpy/tests/test_mindtpy_lp_nlp.py b/pyomo/contrib/mindtpy/tests/test_mindtpy_lp_nlp.py index 6cf764b0b37..53fef35ff41 100644 --- a/pyomo/contrib/mindtpy/tests/test_mindtpy_lp_nlp.py +++ b/pyomo/contrib/mindtpy/tests/test_mindtpy_lp_nlp.py @@ -1,5 +1,14 @@ -"""Tests for the MINDT solver plugin.""" -from math import fabs +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +"""Tests for the MindtPy solver.""" import pyomo.core.base.symbolic import pyutilib.th as unittest from pyomo.contrib.mindtpy.tests.eight_process_problem import \ @@ -42,8 +51,8 @@ def test_lazy_OA_8PP(self): bound_tolerance=1E-5, single_tree=True) - self.assertIs(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) self.assertAlmostEqual(value(model.cost.expr), 68, places=1) def test_lazy_OA_8PP_init_max_binary(self): @@ -57,8 +66,8 @@ def test_lazy_OA_8PP_init_max_binary(self): nlp_solver=required_solvers[0], single_tree=True) - self.assertIs(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) self.assertAlmostEqual(value(model.cost.expr), 68, places=1) def test_lazy_OA_MINLP_simple(self): @@ -117,8 +126,8 @@ def test_lazy_OA_Proposal(self): nlp_solver=required_solvers[0], single_tree=True) - self.assertIs(results.solver.termination_condition, - TerminationCondition.optimal) + self.assertIn(results.solver.termination_condition, + [TerminationCondition.optimal, TerminationCondition.feasible]) self.assertAlmostEqual(value(model.obj.expr), 0.66555, places=2) def test_lazy_OA_ConstraintQualificationExample(self): @@ -131,7 +140,7 @@ def test_lazy_OA_ConstraintQualificationExample(self): single_tree=True ) self.assertIs(results.solver.termination_condition, - TerminationCondition.maxIterations) + TerminationCondition.feasible) self.assertAlmostEqual(value(model.objective.expr), 3, places=2) def test_OA_OnlineDocExample(self): diff --git a/pyomo/contrib/mindtpy/util.py b/pyomo/contrib/mindtpy/util.py index c5ee9c4ddd0..c95b85d88c7 100644 --- a/pyomo/contrib/mindtpy/util.py +++ b/pyomo/contrib/mindtpy/util.py @@ -1,19 +1,26 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Utility functions and classes for the MindtPy solver.""" from __future__ import division import logging -from math import fabs, floor, log - -from pyomo.common.collections import ComponentMap, ComponentSet -from pyomo.core import (Any, Binary, Block, Constraint, NonNegativeReals, - Objective, Reals, Suffix, Var, minimize, value) +from pyomo.common.collections import ComponentMap +from pyomo.core import Objective, Suffix from pyomo.core.expr import differentiate from pyomo.core.expr import current as EXPR -from pyomo.core.expr.numvalue import native_numeric_types from pyomo.opt import SolverFactory -from pyomo.opt.results import ProblemSense from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver +logger = logging.getLogger('pyomo.contrib') + class MindtPySolveData(object): """Data container to hold solve-instance data. @@ -25,11 +32,22 @@ class MindtPySolveData(object): def model_is_valid(solve_data, config): - """Validate that the model is solveable by MindtPy. + """ + Determines whether the model is solveable by MindtPy. - Also preforms some preprocessing such as moving the objective to the - constraints. + This function returns True if the given model is solveable by MindtPy (and performs some preprocessing such + as moving the objective to the constraints). + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: MindtPy configurations + contains the specific configurations for the algorithm + + Returns + ------- + Boolean value (True if model is solveable in MindtPy else False) """ m = solve_data.working_model MindtPy = m.MindtPy_utils @@ -47,7 +65,7 @@ def model_is_valid(solve_data, config): "Your model is an NLP (nonlinear program). " "Using NLP solver %s to solve." % config.nlp_solver) SolverFactory(config.nlp_solver).solve( - solve_data.original_model, **config.nlp_solver_args) + solve_data.original_model, tee=config.solver_tee, **config.nlp_solver_args) return False else: config.logger.info( @@ -56,20 +74,34 @@ def model_is_valid(solve_data, config): mipopt = SolverFactory(config.mip_solver) if isinstance(mipopt, PersistentSolver): mipopt.set_instance(solve_data.original_model) - - mipopt.solve(solve_data.original_model, **config.mip_solver_args) + if config.threads > 0: + mipopt.options["threads"] = config.threads + mipopt.solve(solve_data.original_model, + tee=config.solver_tee, **config.mip_solver_args) return False - if not hasattr(m, 'dual'): # Set up dual value reporting + if not hasattr(m, 'dual') and config.use_dual: # Set up dual value reporting m.dual = Suffix(direction=Suffix.IMPORT) - # TODO if any continuous variables are multipled with binary ones, need - # to do some kind of transformation (Glover?) or throw an error message + # TODO if any continuous variables are multiplied with binary ones, + # need to do some kind of transformation (Glover?) or throw an error message return True def calc_jacobians(solve_data, config): - """Generate a map of jacobians.""" + """ + Generates a map of jacobians for the variables in the model + + This function generates a map of jacobians corresponding to the variables in the model and adds this + ComponentMap to solve_data + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: MindtPy configurations + contains the specific configurations for the algorithm + """ # Map nonlinear_constraint --> Map( # variable --> jacobian of constraint wrt. variable) solve_data.jacobians = ComponentMap() @@ -89,24 +121,53 @@ def calc_jacobians(solve_data, config): def add_feas_slacks(m, config): + """ + Adds feasibility slack variables according to config.feasibility_norm (given an infeasible problem) + + Parameters + ---------- + m: model + Pyomo model + config: ConfigBlock + contains the specific configurations for the algorithm + """ MindtPy = m.MindtPy_utils # generate new constraints for i, constr in enumerate(MindtPy.constraint_list, 1): if constr.body.polynomial_degree() not in [0, 1]: - rhs = constr.upper if constr.has_ub() else constr.lower - if config.feasibility_norm in {'L1', 'L2'}: - c = MindtPy.MindtPy_feas.feas_constraints.add( - constr.body - rhs - <= MindtPy.MindtPy_feas.slack_var[i]) - else: - c = MindtPy.MindtPy_feas.feas_constraints.add( - constr.body - rhs - <= MindtPy.MindtPy_feas.slack_var) + if constr.has_ub(): + if config.feasibility_norm in {'L1', 'L2'}: + c = MindtPy.MindtPy_feas.feas_constraints.add( + constr.body - constr.upper + <= MindtPy.MindtPy_feas.slack_var[i]) + else: + c = MindtPy.MindtPy_feas.feas_constraints.add( + constr.body - constr.upper + <= MindtPy.MindtPy_feas.slack_var) + if constr.has_lb(): + if config.feasibility_norm in {'L1', 'L2'}: + c = MindtPy.MindtPy_feas.feas_constraints.add( + constr.body - constr.lower + >= -MindtPy.MindtPy_feas.slack_var[i]) + else: + c = MindtPy.MindtPy_feas.feas_constraints.add( + constr.body - constr.lower + >= -MindtPy.MindtPy_feas.slack_var) def var_bound_add(solve_data, config): - """This function will add bound for variables in nonlinear constraints if they are not bounded. - This is to avoid an unbound master problem in the LP/NLP algorithm. + """ + This function will add bounds for variables in nonlinear constraints if they are not bounded. (This is to avoid + an unbounded master problem in the LP/NLP algorithm.) Thus, the model will be updated to include bounds for the + unbounded variables in nonlinear constraints. + + Parameters + ---------- + solve_data: MindtPy Data Container + data container that holds solve-instance data + config: ConfigBlock + contains the specific configurations for the algorithm + """ m = solve_data.working_model MindtPy = m.MindtPy_utils diff --git a/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py b/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py index c90da3ab820..4b243fc9c69 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py +++ b/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py @@ -12,8 +12,7 @@ pyomo\examples\doc\pyomobook\nonlinear-ch\react_design\ReactorDesign.py """ import pandas as pd -from pyomo.environ import * -from pyomo.core import * +from pyomo.environ import ConcreteModel, Var, PositiveReals, Objective, Constraint, maximize, SolverFactory def reactor_design_model(data): diff --git a/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py b/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py index 72a60799bf4..6d2a114ea5d 100644 --- a/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py +++ b/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py @@ -7,38 +7,42 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + """ Rooney Biegler model, based on Rooney, W. C. and Biegler, L. T. (2001). Design for model parameter uncertainty using nonlinear confidence regions. AIChE Journal, 47(8), 1794-1804. """ + import pandas as pd import pyomo.environ as pyo + def rooney_biegler_model(data): - + model = pyo.ConcreteModel() model.asymptote = pyo.Var(initialize = 15) model.rate_constant = pyo.Var(initialize = 0.5) - + def response_rule(m, h): expr = m.asymptote * (1 - pyo.exp(-m.rate_constant * h)) return expr model.response_function = pyo.Expression(data.hour, rule = response_rule) - + def SSE_rule(m): return sum((data.y[i] - m.response_function[data.hour[i]])**2 for i in data.index) model.SSE = pyo.Objective(rule = SSE_rule, sense=pyo.minimize) - + return model + if __name__ == '__main__': - + # These were taken from Table A1.4 in Bates and Watts (1988). data = pd.DataFrame(data=[[1,8.3],[2,10.3],[3,19.0],[4,16.0],[5,15.6],[7,19.8]], columns=['hour', 'y']) - + model = rooney_biegler_model(data) solver = pyo.SolverFactory('ipopt') solver.solve(model) diff --git a/pyomo/contrib/parmest/examples/semibatch/semibatch.py b/pyomo/contrib/parmest/examples/semibatch/semibatch.py index 7c267e65bff..552eace1d92 100644 --- a/pyomo/contrib/parmest/examples/semibatch/semibatch.py +++ b/pyomo/contrib/parmest/examples/semibatch/semibatch.py @@ -14,8 +14,8 @@ algebraic equations. Mathematical Programming Computation, 10(2), 187-223. """ import json -from pyomo.environ import * -from pyomo.dae import * +from pyomo.environ import ConcreteModel, Set, Param, Var, Constraint, ConstraintList, Expression, Objective, TransformationFactory, SolverFactory, exp, minimize +from pyomo.dae import ContinuousSet, DerivativeVar def generate_model(data): diff --git a/pyomo/contrib/parmest/graphics.py b/pyomo/contrib/parmest/graphics.py index be20338772e..ec76070c911 100644 --- a/pyomo/contrib/parmest/graphics.py +++ b/pyomo/contrib/parmest/graphics.py @@ -32,6 +32,7 @@ except (ImportError, RuntimeError, SyntaxError): imports_available = False +from pyomo.common.dependencies import check_min_version def _get_variables(ax,columns): sps = ax.get_subplotspec() @@ -93,15 +94,16 @@ def _get_data_slice(xvar,yvar,columns,data,theta_star): return X,Y,Z - -def _add_scatter(x,y,color,label,columns,theta_star): +# Note: seaborn 0.11 no longer expects color and label to be passed to the +# plotting functions. label is kept here for backward compatibility +def _add_scatter(x,y,color,columns,theta_star,label=None): ax = plt.gca() xvar, yvar, loc = _get_variables(ax, columns) ax.scatter(theta_star[xvar], theta_star[yvar], c=color, s=35) -def _add_rectangle_CI(x,y,color,label,columns,lower_bound,upper_bound): +def _add_rectangle_CI(x,y,color,columns,lower_bound,upper_bound,label=None): ax = plt.gca() xvar, yvar, loc = _get_variables(ax,columns) @@ -116,7 +118,7 @@ def _add_rectangle_CI(x,y,color,label,columns,lower_bound,upper_bound): ax.plot([xmin, xmin], [ymax, ymin], color=color) -def _add_scipy_dist_CI(x,y,color,label,columns,ncells,alpha,dist,theta_star): +def _add_scipy_dist_CI(x,y,color,columns,ncells,alpha,dist,theta_star,label=None): ax = plt.gca() xvar, yvar, loc = _get_variables(ax,columns) @@ -152,7 +154,7 @@ def _add_scipy_dist_CI(x,y,color,label,columns,ncells,alpha,dist,theta_star): ax.contour(X,Y,Z, levels=[alpha], colors=color) -def _add_obj_contour(x,y,color,label,columns,data,theta_star): +def _add_obj_contour(x,y,color,columns,data,theta_star,label=None): ax = plt.gca() xvar, yvar, loc = _get_variables(ax,columns) @@ -165,18 +167,6 @@ def _add_obj_contour(x,y,color,label,columns,data,theta_star): plt.tricontourf(triang,Z,cmap=cmap) except: print('Objective contour plot for', xvar, yvar,'slice failed') - - -def _add_LR_contour(x,y,color,label,columns,data,theta_star,threshold): - ax = plt.gca() - xvar, yvar, loc = _get_variables(ax,columns) - - X, Y, Z = _get_data_slice(xvar,yvar,columns,data,theta_star) - - triang = tri.Triangulation(X, Y) - - plt.tricontour(triang,Z,[threshold], colors='r') - def _set_axis_limits(g, axis_limits, theta_vals, theta_star): @@ -273,7 +263,13 @@ def pairwise_plot(theta_values, theta_star=None, alpha=None, distributions=[], g = sns.PairGrid(thetas) # Plot histogram on the diagonal - g.map_diag(sns.distplot, kde=False, hist=True, norm_hist=False) + # Note: distplot is deprecated and will be removed in a future + # version of seaborn, use histplot. distplot is kept for older + # versions of python. + if check_min_version(sns, "0.11"): + g.map_diag(sns.histplot) + else: + g.map_diag(sns.distplot, kde=False, hist=True, norm_hist=False) # Plot filled contours using all theta values based on obj if 'obj' in theta_values.columns and add_obj_contour: diff --git a/pyomo/contrib/parmest/mpi_utils.py b/pyomo/contrib/parmest/mpi_utils.py index 7183461f443..71344786229 100644 --- a/pyomo/contrib/parmest/mpi_utils.py +++ b/pyomo/contrib/parmest/mpi_utils.py @@ -10,6 +10,7 @@ from collections import OrderedDict import importlib +from six import iteritems """ This module is a collection of classes that provide a friendlier interface to MPI (through mpi4py). They help @@ -117,7 +118,7 @@ def global_to_local_data(self, global_data): local_data = OrderedDict() assert (len(global_data) == self._n_total_tasks) idx = 0 - for k,v in six.iteritems(global_data): + for k,v in iteritems(global_data): if idx in self._local_map: local_data[k] = v idx += idx diff --git a/pyomo/contrib/parmest/parmest.py b/pyomo/contrib/parmest/parmest.py index 3078ae4554a..d1848787e8e 100644 --- a/pyomo/contrib/parmest/parmest.py +++ b/pyomo/contrib/parmest/parmest.py @@ -19,7 +19,6 @@ pandas as pd, pandas_available, scipy, scipy_available, ) -parmest_available = numpy_available & pandas_available & scipy_available import pyomo.environ as pyo import pyomo.pysp.util.rapper as st @@ -29,20 +28,24 @@ import pyomo.contrib.parmest.mpi_utils as mpiu import pyomo.contrib.parmest.ipopt_solver_wrapper as ipopt_solver_wrapper -from pyomo.contrib.parmest.graphics import pairwise_plot, grouped_boxplot, grouped_violinplot, \ - fit_rect_dist, fit_mvn_dist, fit_kde_dist +from pyomo.contrib.parmest.graphics import (fit_rect_dist, + fit_mvn_dist, + fit_kde_dist) + +parmest_available = numpy_available & pandas_available & scipy_available if numpy_available and scipy_available: from pyomo.contrib.pynumero.asl import AmplInterface asl_available = AmplInterface.available() else: - asl_available=False + asl_available = False if asl_available: from pyomo.contrib.interior_point.inverse_reduced_hessian import inv_reduced_hessian_barrier __version__ = 0.1 + #============================================= def _object_from_string(instance, vstr): """ @@ -51,8 +54,8 @@ def _object_from_string(instance, vstr): instance: a concrete pyomo model vstr: a particular Var or Param (e.g. "pp.Keq_a[2]") output: - the object - NOTE: We need to deal with blocks + the object + NOTE: We need to deal with blocks and with indexes that might really be strings or ints """ # pull off the index @@ -554,11 +557,11 @@ def _Q_opt(self, ThetaVals=None, solver="ef_ipopt", if len(return_values) > 0: var_values = [] - for exp_i in stsolver.ef_instance.component_objects(Block, descend_into=False): + for exp_i in self.ef_instance.component_objects(Block, descend_into=False): vals = {} for var in return_values: exp_i_var = eval('exp_i.'+ str(var)) - temp = [_.value for _ in exp_i_var.itervalues()] + temp = [pyo.value(_) for _ in exp_i_var.itervalues()] if len(temp) == 1: vals[var] = temp[0] else: diff --git a/pyomo/contrib/parmest/scenariocreator.py b/pyomo/contrib/parmest/scenariocreator.py index 46e946c555f..720bc897242 100644 --- a/pyomo/contrib/parmest/scenariocreator.py +++ b/pyomo/contrib/parmest/scenariocreator.py @@ -1,8 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # ScenariosCreator.py - Class to create and deliver scenarios using parmest # DLW March 2020 -import json -import pyomo.contrib.parmest.parmest as parmest import pyomo.environ as pyo diff --git a/pyomo/contrib/parmest/tests/test_parmest.py b/pyomo/contrib/parmest/tests/test_parmest.py index 8fd0cf349c0..d67d6b9a150 100644 --- a/pyomo/contrib/parmest/tests/test_parmest.py +++ b/pyomo/contrib/parmest/tests/test_parmest.py @@ -24,13 +24,9 @@ is_osx = platform.mac_ver()[0] != '' import pyutilib.th as unittest -import tempfile import sys import os -import shutil -import glob import subprocess -import sys from itertools import product import pyomo.contrib.parmest.parmest as parmest @@ -121,9 +117,9 @@ def test_bootstrap(self): self.assertTrue(CR[0.75].sum() == 7) self.assertTrue(CR[1.0].sum() == 10) # all true - parmest.pairwise_plot(theta_est) - parmest.pairwise_plot(theta_est, thetavals) - parmest.pairwise_plot(theta_est, thetavals, 0.8, ['MVN', 'KDE', 'Rect']) + graphics.pairwise_plot(theta_est) + graphics.pairwise_plot(theta_est, thetavals) + graphics.pairwise_plot(theta_est, thetavals, 0.8, ['MVN', 'KDE', 'Rect']) @unittest.skipIf(not graphics.imports_available, "parmest.graphics imports are unavailable") @@ -143,7 +139,7 @@ def test_likelihood_ratio(self): self.assertTrue(LR[0.9].sum() == 11) self.assertTrue(LR[1.0].sum() == 60) # all true - parmest.pairwise_plot(LR, thetavals, 0.8) + graphics.pairwise_plot(LR, thetavals, 0.8) def test_leaveNout(self): lNo_theta = self.pest.theta_est_leaveNout(1) @@ -272,7 +268,7 @@ def test_theta_est_cov(self): @unittest.skipIf(not imports_present, "Cannot test parmest: required dependencies are missing") -@unittest.skipIf(not ipopt_available, "The 'ipopt' command is not available") +@unittest.skipIf(not ipopt_available, "The 'ipopt' solver is not available") class parmest_object_Tester_reactor_design(unittest.TestCase): def setUp(self): @@ -320,6 +316,13 @@ def test_theta_est(self): self.assertAlmostEqual(thetavals['k1'], 5.0/6.0, places=4) self.assertAlmostEqual(thetavals['k2'], 5.0/3.0, places=4) self.assertAlmostEqual(thetavals['k3'], 1.0/6000.0, places=7) + + def test_return_values(self): + objval, thetavals, data_rec =\ + self.pest.theta_est(return_values=['ca', 'cb', 'cc', 'cd', 'caf']) + self.assertAlmostEqual(data_rec["cc"].loc[18], 893.84924, places=3) + + @unittest.skipIf(not parmest.parmest_available, "Cannot test parmest: required dependencies are missing") @@ -333,14 +336,14 @@ def setUp(self): self.B = pd.DataFrame(np.random.randint(0,100,size=(100,4)), columns=list('ABCD')) def test_pairwise_plot(self): - parmest.pairwise_plot(self.A, alpha=0.8, distributions=['Rect', 'MVN', 'KDE']) + graphics.pairwise_plot(self.A, alpha=0.8, distributions=['Rect', 'MVN', 'KDE']) def test_grouped_boxplot(self): - parmest.grouped_boxplot(self.A, self.B, normalize=True, + graphics.grouped_boxplot(self.A, self.B, normalize=True, group_names=['A', 'B']) def test_grouped_violinplot(self): - parmest.grouped_violinplot(self.A, self.B) + graphics.grouped_violinplot(self.A, self.B) if __name__ == '__main__': unittest.main() diff --git a/pyomo/contrib/parmest/tests/test_scenariocreator.py b/pyomo/contrib/parmest/tests/test_scenariocreator.py index 5a0aa43ecab..920ebb171e8 100644 --- a/pyomo/contrib/parmest/tests/test_scenariocreator.py +++ b/pyomo/contrib/parmest/tests/test_scenariocreator.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # the matpolotlib stuff is to avoid $DISPLAY errors on Travis (DLW Oct 2018) try: import matplotlib @@ -19,16 +29,11 @@ import pyutilib.th as unittest import os -import sys - import pyomo.contrib.parmest.parmest as parmest import pyomo.contrib.parmest.scenariocreator as sc -import pyomo.contrib.parmest.graphics as graphics -import pyomo.contrib.parmest as parmestbase -import pyomo.environ as pyo import pyomo.contrib.parmest.examples.semibatch.scencreate as sbc - -from pyomo.opt import SolverFactory +import pyomo.environ as pyo +from pyomo.environ import SolverFactory ipopt_available = SolverFactory('ipopt').available() testdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/contrib/petsc/examples/dae_pyomo/car_example.py b/pyomo/contrib/petsc/examples/dae_pyomo/car_example.py index f2aecc44728..e54f02c8eca 100644 --- a/pyomo/contrib/petsc/examples/dae_pyomo/car_example.py +++ b/pyomo/contrib/petsc/examples/dae_pyomo/car_example.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # Ampl Car Example # # Modified pyomo.dae example to remove optimization, and set up PETSc solve @@ -9,8 +19,7 @@ # v(0)=0; v(tf)=0 # -3<=a<=1 -from pyomo.environ import * -from pyomo.dae import * +from pyomo.environ import ConcreteModel, Param, Var, Constraint, Suffix, SolverFactory m = ConcreteModel() diff --git a/pyomo/contrib/petsc/examples/dae_pyomo/chemakzo.py b/pyomo/contrib/petsc/examples/dae_pyomo/chemakzo.py index 8bd12abf688..ee259b30daa 100644 --- a/pyomo/contrib/petsc/examples/dae_pyomo/chemakzo.py +++ b/pyomo/contrib/petsc/examples/dae_pyomo/chemakzo.py @@ -1,12 +1,20 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """ A test problem from https://archimede.dm.uniba.it/~testset/report/chemakzo.pdf """ from __future__ import division # No integer division from __future__ import print_function # Python 3 style print -from pyomo.environ import * -from pyomo.opt import SolverFactory -import os +from pyomo.environ import ConcreteModel, Param, Var, Constraint, Suffix, SolverFactory if __name__ == "__main__": opt = SolverFactory('petsc') diff --git a/pyomo/contrib/petsc/examples/dae_pyomo/path_constraint.py b/pyomo/contrib/petsc/examples/dae_pyomo/path_constraint.py index 6c34e332677..f198bb484d9 100644 --- a/pyomo/contrib/petsc/examples/dae_pyomo/path_constraint.py +++ b/pyomo/contrib/petsc/examples/dae_pyomo/path_constraint.py @@ -21,8 +21,7 @@ # Modified for no optimization, and PETSc solver -from pyomo.environ import * -from pyomo.dae import * +from pyomo.environ import ConcreteModel, Param, Var, Constraint, Suffix, SolverFactory m = ConcreteModel() m.tf = Param(initialize=1) diff --git a/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py b/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py index b6ddfc224b2..fa3d38ffd1d 100644 --- a/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py +++ b/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py @@ -1,11 +1,20 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to convert explicit bounds to variable bounds.""" from __future__ import division -import math from math import fabs +import math -from pyomo.core import Integers from pyomo.core.base.plugin import TransformationFactory from pyomo.common.config import (ConfigBlock, ConfigValue, NonNegativeFloat, add_docstring_list) @@ -83,9 +92,11 @@ def _apply_to(self, model, **kwds): if var.is_integer() or var.is_binary(): # Make sure that the lb and ub are integral. Use safe construction if near to integer. if var.has_lb(): - var.setlb(int(min(math.ceil(var.lb - config.tolerance), math.ceil(var.lb)))) + var.setlb(int(min(math.ceil(var.lb - config.tolerance), + math.ceil(var.lb)))) if var.has_ub(): - var.setub(int(max(math.floor(var.ub + config.tolerance), math.floor(var.ub)))) + var.setub(int(max(math.floor(var.ub + config.tolerance), + math.floor(var.ub)))) if var is not None and var.value is not None: _adjust_var_value_if_not_feasible(var) diff --git a/pyomo/contrib/preprocessing/plugins/deactivate_trivial_constraints.py b/pyomo/contrib/preprocessing/plugins/deactivate_trivial_constraints.py index fd8a59249e9..f9998a2a822 100644 --- a/pyomo/contrib/preprocessing/plugins/deactivate_trivial_constraints.py +++ b/pyomo/contrib/preprocessing/plugins/deactivate_trivial_constraints.py @@ -1,7 +1,17 @@ # -*- coding: utf-8 -*- + +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to deactivate trivial constraints.""" import logging -import textwrap from pyomo.common.collections import ComponentSet from pyomo.common.config import (ConfigBlock, ConfigValue, NonNegativeFloat, @@ -11,6 +21,7 @@ from pyomo.core.expr.numvalue import value from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation +logger = logging.getLogger('pyomo.contrib.preprocessing') @TransformationFactory.register( 'contrib.deactivate_trivial_constraints', diff --git a/pyomo/contrib/preprocessing/plugins/detect_fixed_vars.py b/pyomo/contrib/preprocessing/plugins/detect_fixed_vars.py index dc62c6a734b..80e751eefec 100644 --- a/pyomo/contrib/preprocessing/plugins/detect_fixed_vars.py +++ b/pyomo/contrib/preprocessing/plugins/detect_fixed_vars.py @@ -1,5 +1,14 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to detect variables fixed by bounds and fix them.""" -import textwrap from math import fabs from six import iteritems diff --git a/pyomo/contrib/preprocessing/plugins/equality_propagate.py b/pyomo/contrib/preprocessing/plugins/equality_propagate.py index 2f3caf3e4fc..cce0658ba49 100644 --- a/pyomo/contrib/preprocessing/plugins/equality_propagate.py +++ b/pyomo/contrib/preprocessing/plugins/equality_propagate.py @@ -1,5 +1,14 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to propagate state through an equality set.""" -import textwrap from pyomo.common.collections import ComponentSet, ComponentMap from pyomo.core.base.constraint import Constraint diff --git a/pyomo/contrib/preprocessing/plugins/induced_linearity.py b/pyomo/contrib/preprocessing/plugins/induced_linearity.py index b3a6c81eb57..a9b570cd5f8 100644 --- a/pyomo/contrib/preprocessing/plugins/induced_linearity.py +++ b/pyomo/contrib/preprocessing/plugins/induced_linearity.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to reformulate nonlinear models with linearity induced from discrete variables. @@ -16,7 +26,6 @@ from pyomo.common.config import (ConfigBlock, ConfigValue, NonNegativeFloat, add_docstring_list) from pyomo.common.modeling import unique_component_name -from pyomo.common.config import ConfigBlock, ConfigValue, NonNegativeFloat from pyomo.contrib.preprocessing.util import SuppressConstantObjectiveWarning from pyomo.core import (Binary, Block, Constraint, Objective, Set, TransformationFactory, Var, summation, value) diff --git a/pyomo/contrib/preprocessing/plugins/init_vars.py b/pyomo/contrib/preprocessing/plugins/init_vars.py index b8bdc0d98f5..5810430940b 100644 --- a/pyomo/contrib/preprocessing/plugins/init_vars.py +++ b/pyomo/contrib/preprocessing/plugins/init_vars.py @@ -1,8 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Automatically initialize variables.""" from __future__ import division -import textwrap - from pyomo.core.base.var import Var from pyomo.core.base.plugin import TransformationFactory from pyomo.core.expr.numvalue import value diff --git a/pyomo/contrib/preprocessing/plugins/int_to_binary.py b/pyomo/contrib/preprocessing/plugins/int_to_binary.py index 199cf35dbb1..7bde2a38e12 100644 --- a/pyomo/contrib/preprocessing/plugins/int_to_binary.py +++ b/pyomo/contrib/preprocessing/plugins/int_to_binary.py @@ -53,9 +53,10 @@ def _apply_to(self, model, **kwds): integer_vars = list( v for v in model.component_data_objects( ctype=Var, descend_into=(Block, Disjunct)) - if v.is_integer() and not v.fixed) + if v.is_integer() and not v.is_binary() and not v.fixed) if len(integer_vars) == 0: - logger.info("Model has no free integer variables. No reformulation needed.") + logger.info( + "Model has no free integer variables. No reformulation needed.") return vars_on_constr = ComponentSet() @@ -111,7 +112,7 @@ def _apply_to(self, model, **kwds): idx, expr=int_var == sum( reform_block.new_binary_var[idx, pwr] * (2 ** pwr) for pwr in range(0, highest_power + 1)) - + int_var.lb) + + int_var.lb) # Relax the original integer variable if config.relax_integrality: diff --git a/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py b/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py index 08f3022fe9d..0e031d35bfd 100644 --- a/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py +++ b/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py @@ -1,16 +1,23 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # -*- coding: UTF-8 -*- """Transformation to remove zero terms from constraints.""" from __future__ import division -import textwrap - from pyomo.core import quicksum from pyomo.core.base.constraint import Constraint from pyomo.core.base.plugin import TransformationFactory from pyomo.core.expr import current as EXPR from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation from pyomo.repn import generate_standard_repn -from pyutilib.math.util import isclose @TransformationFactory.register( diff --git a/pyomo/contrib/preprocessing/plugins/strip_bounds.py b/pyomo/contrib/preprocessing/plugins/strip_bounds.py index b12ad4b96e5..076d758595a 100644 --- a/pyomo/contrib/preprocessing/plugins/strip_bounds.py +++ b/pyomo/contrib/preprocessing/plugins/strip_bounds.py @@ -1,5 +1,14 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to strip variable bounds from a model.""" -import textwrap from pyomo.common.collections import ComponentMap from pyomo.common.config import ConfigBlock, ConfigValue, add_docstring_list diff --git a/pyomo/contrib/preprocessing/plugins/var_aggregator.py b/pyomo/contrib/preprocessing/plugins/var_aggregator.py index 8b5ba8f3088..5160caf479e 100644 --- a/pyomo/contrib/preprocessing/plugins/var_aggregator.py +++ b/pyomo/contrib/preprocessing/plugins/var_aggregator.py @@ -1,11 +1,21 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to aggregate equal variables.""" from __future__ import division -import textwrap - from pyomo.common.collections import ComponentMap, ComponentSet -from pyomo.core.base import Block, Constraint, VarList, Objective, TransformationFactory +from pyomo.core.base import ( + Block, Constraint, VarList, Objective, TransformationFactory, +) from pyomo.core.expr.current import ExpressionReplacementVisitor from pyomo.core.expr.numvalue import value from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation diff --git a/pyomo/contrib/preprocessing/plugins/zero_sum_propagator.py b/pyomo/contrib/preprocessing/plugins/zero_sum_propagator.py index d31368ed9f3..0d8d760bc8f 100644 --- a/pyomo/contrib/preprocessing/plugins/zero_sum_propagator.py +++ b/pyomo/contrib/preprocessing/plugins/zero_sum_propagator.py @@ -1,5 +1,14 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Transformation to propagate a zero value to terms of a sum.""" -import textwrap from pyomo.core.base.plugin import TransformationFactory from pyomo.core.base.constraint import Constraint diff --git a/pyomo/contrib/preprocessing/tests/test_int_to_binary.py b/pyomo/contrib/preprocessing/tests/test_int_to_binary.py index 7c45d7f26b6..9b4bb2ad75f 100644 --- a/pyomo/contrib/preprocessing/tests/test_int_to_binary.py +++ b/pyomo/contrib/preprocessing/tests/test_int_to_binary.py @@ -1,6 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + """Tests the integer to binary variable reformulation.""" import pyutilib.th as unittest -from pyomo.environ import ConcreteModel, Var, Integers, NonNegativeReals, value +from pyomo.environ import ConcreteModel, Var, Integers, value from pyomo.environ import TransformationFactory as xfrm from pyomo.common.log import LoggingIntercept diff --git a/pyomo/contrib/pynumero/algorithms/solvers/cyipopt_solver.py b/pyomo/contrib/pynumero/algorithms/solvers/cyipopt_solver.py index a25ca96b4a0..4cd10ad896a 100644 --- a/pyomo/contrib/pynumero/algorithms/solvers/cyipopt_solver.py +++ b/pyomo/contrib/pynumero/algorithms/solvers/cyipopt_solver.py @@ -8,29 +8,96 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ """ -The cyipopt_solver module includes the python interface to the -Cythonized ipopt solver cyipopt (see more: -https://github.com/matthias-k/cyipopt.git). To use the solver, +The cyipopt_solver module includes the python interface to the +Cythonized ipopt solver cyipopt (see more: +https://github.com/matthias-k/cyipopt.git). To use the solver, you can create a derived implementation from the abstract base class CyIpoptProblemInterface that provides the necessary methods. Note: This module also includes a default implementation CyIpopt -that works with problems derived from AslNLP as long as those +that works with problems derived from AslNLP as long as those classes return numpy ndarray objects for the vectors and coo_matrix objects for the matrices (e.g., AmplNLP and PyomoNLP) """ -try: - import ipopt -except ImportError: - raise ImportError('ipopt solver relies on cyipopt. Install cyipopt' - ' https://github.com/matthias-k/cyipopt.git') -import numpy as np -from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP import six import sys import os import abc +from pyomo.common.dependencies import ( + attempt_import, + numpy as np, numpy_available, +) +ipopt, ipopt_available = attempt_import( + 'ipopt', + error_message='cyipopt solver relies on the ipopt module from cyipopt. ' + 'See https://github.com/matthias-k/cyipopt.git for cyipopt ' + 'installation instructions.' +) +# Because pynumero.interfaces requires numpy, we will leverage deferred +# imports here so that the solver can be registered even when numpy is +# not available. +pyomo_nlp = attempt_import('pyomo.contrib.pynumero.interfaces.pyomo_nlp')[0] +egb = attempt_import('pyomo.contrib.pynumero.interfaces.external_grey_box')[0] + +from pyomo.common.config import ConfigBlock, ConfigValue +from pyomo.common.timing import TicTocTimer +from pyomo.core.base import Block, Objective, minimize +from pyomo.opt import ( + SolverStatus, SolverResults, TerminationCondition, ProblemSense +) + +# This maps the cyipopt STATUS_MESSAGES back to string representations +# of the Ipopt ApplicationReturnStatus enum +_cyipopt_status_enum = [ + 'Solve_Succeeded', b'Algorithm terminated successfully at a locally optimal point, satisfying the convergence tolerances (can be specified by options).', + 'Solved_To_Acceptable_Level', b'Algorithm stopped at a point that was converged, not to "desired" tolerances, but to "acceptable" tolerances (see the acceptable-... options).', + 'Infeasible_Problem_Detected', b'Algorithm converged to a point of local infeasibility. Problem may be infeasible.', + 'Search_Direction_Becomes_Too_Small', b'Algorithm proceeds with very little progress.', + 'Diverging_Iterates', b'It seems that the iterates diverge.', + 'User_Requested_Stop', b'The user call-back function intermediate_callback (see Section 3.3.4 in the documentation) returned false, i.e., the user code requested a premature termination of the optimization.', + 'Feasible_Point_Found', b'Feasible point for square problem found.', + 'Maximum_Iterations_Exceeded', b'Maximum number of iterations exceeded (can be specified by an option).', + 'Restoration_Failed', b'Restoration phase failed, algorithm doesn\'t know how to proceed.', + 'Error_In_Step_Computation', b'An unrecoverable error occurred while Ipopt tried to compute the search direction.', + 'Maximum_CpuTime_Exceeded', b'Maximum CPU time exceeded.', + 'Not_Enough_Degrees_Of_Freedom', b'Problem has too few degrees of freedom.', + 'Invalid_Problem_Definition', b'Invalid problem definition.', + 'Invalid_Option', b'Invalid option encountered.', + 'Invalid_Number_Detected', b'Algorithm received an invalid number (such as NaN or Inf) from the NLP; see also option check_derivatives_for_naninf', + 'Unrecoverable_Exception', b'Some uncaught Ipopt exception encountered.', + 'NonIpopt_Exception_Thrown', b'Unknown Exception caught in Ipopt', + 'Insufficient_Memory', b'Not enough memory.', + 'Internal_Error', b'An unknown internal error occurred. Please contact the Ipopt authors through the mailing list.' +] +_cyipopt_status_enum = { + _cyipopt_status_enum[i+1]: _cyipopt_status_enum[i] + for i in range(0, len(_cyipopt_status_enum), 2) +} + +# This maps Ipopt ApplicationReturnStatus enum strings to an appropriate +# Pyomo TerminationCondition +_ipopt_term_cond = { + 'Solve_Succeeded': TerminationCondition.optimal, + 'Solved_To_Acceptable_Level': TerminationCondition.feasible, + 'Infeasible_Problem_Detected': TerminationCondition.infeasible, + 'Search_Direction_Becomes_Too_Small': TerminationCondition.minStepLength, + 'Diverging_Iterates': TerminationCondition.unbounded, + 'User_Requested_Stop': TerminationCondition.userInterrupt, + 'Feasible_Point_Found': TerminationCondition.feasible, + 'Maximum_Iterations_Exceeded': TerminationCondition.maxIterations, + 'Restoration_Failed': TerminationCondition.noSolution, + 'Error_In_Step_Computation': TerminationCondition.solverFailure, + 'Maximum_CpuTime_Exceeded': TerminationCondition.maxTimeLimit, + 'Not_Enough_Degrees_Of_Freedom': TerminationCondition.invalidProblem, + 'Invalid_Problem_Definition': TerminationCondition.invalidProblem, + 'Invalid_Option': TerminationCondition.error, + 'Invalid_Number_Detected': TerminationCondition.internalSolverError, + 'Unrecoverable_Exception': TerminationCondition.internalSolverError, + 'NonIpopt_Exception_Thrown': TerminationCondition.error, + 'Insufficient_Memory': TerminationCondition.resourceInterrupt, + 'Internal_Error': TerminationCondition.internalSolverError, +} @six.add_metaclass(abc.ABCMeta) class CyIpoptProblemInterface(object): @@ -39,7 +106,7 @@ def x_init(self): """Return the initial values for x as a numpy ndarray """ pass - + @abc.abstractmethod def x_lb(self): """Return the lower bounds on x as a numpy ndarray @@ -97,7 +164,7 @@ def constraints(self, x): def jacobianstructure(self): """Return the structure of the jacobian in coordinate format. That is, return (rows,cols) - where rows and cols are both numpy ndarray + where rows and cols are both numpy ndarray objects that contain the row and column indices for each of the nonzeros in the jacobian. """ @@ -115,7 +182,7 @@ def jacobian(self, x): def hessianstructure(self): """Return the structure of the hessian in coordinate format. That is, return (rows,cols) - where rows and cols are both numpy ndarray + where rows and cols are both numpy ndarray objects that contain the row and column indices for each of the nonzeros in the hessian. Note: return ONLY the lower diagonal of this symmetric matrix. @@ -131,7 +198,7 @@ def hessian(self, x, y, obj_factor): Note: return ONLY the lower diagonal of this symmetric matrix. """ pass - + def intermediate(self, alg_mod, iter_count, obj_value, inf_pr, inf_du, mu, d_norm, regularization_size, alpha_du, alpha_pr, ls_trials): @@ -145,8 +212,8 @@ def intermediate(self, alg_mod, iter_count, obj_value, class CyIpoptNLP(CyIpoptProblemInterface): def __init__(self, nlp): """This class provides a CyIpoptProblemInterface for use - with the CyIpoptSolver class that can take in an NLP - as long as it provides vectors as numpy ndarrays and + with the CyIpoptSolver class that can take in an NLP + as long as it provides vectors as numpy ndarrays and matrices as scipy.sparse.coo_matrix objects. This class provides the interface between AmplNLP or PyomoNLP objects and the CyIpoptSolver @@ -167,8 +234,14 @@ def __init__(self, nlp): # get jacobian and hessian structures self._jac_g = nlp.evaluate_jacobian() - self._hess_lag = nlp.evaluate_hessian_lag() - self._hess_lower_mask = self._hess_lag.row >= self._hess_lag.col + try: + self._hess_lag = nlp.evaluate_hessian_lag() + self._hess_lower_mask = self._hess_lag.row >= self._hess_lag.col + self._hessian_available = True + except NotImplementedError: + self._hessian_available = False + self._hess_lag = None + self._hess_lower_mask = None def _set_primals_if_necessary(self, x): if not np.array_equal(x, self._cached_x): @@ -190,7 +263,7 @@ def x_init(self): def x_lb(self): return self._nlp.primals_lb() - + def x_ub(self): return self._nlp.primals_ub() @@ -199,7 +272,7 @@ def g_lb(self): def g_ub(self): return self._nlp.constraints_ub() - + def scaling_factors(self): obj_scaling = self._nlp.get_obj_scaling() x_scaling = self._nlp.get_primals_scaling() @@ -220,18 +293,25 @@ def constraints(self, x): def jacobianstructure(self): return self._jac_g.row, self._jac_g.col - + def jacobian(self, x): self._set_primals_if_necessary(x) self._nlp.evaluate_jacobian(out=self._jac_g) return self._jac_g.data def hessianstructure(self): + if not self._hessian_available: + return np.zeros(0), np.zeros(0) + row = np.compress(self._hess_lower_mask, self._hess_lag.row) col = np.compress(self._hess_lower_mask, self._hess_lag.col) return row, col + def hessian(self, x, y, obj_factor): + if not self._hessian_available: + raise ValueError("Hessian requested, but not supported by the NLP") + self._set_primals_if_necessary(x) self._set_duals_if_necessary(y) self._set_obj_factor_if_necessary(obj_factor) @@ -256,7 +336,7 @@ def intermediate( pass -def redirect_stdout(): +def _redirect_stdout(): sys.stdout.flush() # <--- important when redirecting to files # Duplicate stdout (file descriptor 1) @@ -282,9 +362,8 @@ def redirect_stdout(): class CyIpoptSolver(object): def __init__(self, problem_interface, options=None): """Create an instance of the CyIpoptSolver. You must - provide a problem_interface that corresponds to + provide a problem_interface that corresponds to the abstract class CyIpoptProblemInterface - options can be provided as a dictionary of key value pairs """ @@ -305,22 +384,23 @@ def solve(self, x0=None, tee=False): if x0 is None: x0 = self._problem.x_init() xstart = x0 - + nx = len(xstart) ng = len(gl) - cyipopt_solver = ipopt.problem(n=nx, - m=ng, - problem_obj=self._problem, - lb=xl, - ub=xu, - cl=gl, - cu=gu + cyipopt_solver = ipopt.problem( + n=nx, + m=ng, + problem_obj=self._problem, + lb=xl, + ub=xu, + cl=gl, + cu=gu ) # check if we need scaling obj_scaling, x_scaling, g_scaling = self._problem.scaling_factors() - if obj_scaling is not None or x_scaling is not None or g_scaling is not None: + if any(_ is not None for _ in (obj_scaling, x_scaling, g_scaling)): # need to set scaling factors if obj_scaling is None: obj_scaling = 1.0 @@ -328,7 +408,6 @@ def solve(self, x0=None, tee=False): x_scaling = np.ones(nx) if g_scaling is None: g_scaling = np.ones(ng) - cyipopt_solver.setProblemScaling(obj_scaling, x_scaling, g_scaling) # add options @@ -338,8 +417,172 @@ def solve(self, x0=None, tee=False): if tee: x, info = cyipopt_solver.solve(xstart) else: - newstdout = redirect_stdout() + newstdout = _redirect_stdout() x, info = cyipopt_solver.solve(xstart) os.dup2(newstdout, 1) return x, info + + +def _numpy_vector(val): + ans = np.array(val, np.float64) + if len(ans.shape) != 1: + raise ValueError("expected a vector, but recieved a matrix " + "with shape %s" % (ans.shape,)) + return ans + + +class PyomoCyIpoptSolver(object): + + CONFIG = ConfigBlock("cyipopt") + CONFIG.declare("tee", ConfigValue( + default=False, + domain=bool, + description="Stream solver output to console", + )) + CONFIG.declare("load_solutions", ConfigValue( + default=True, + domain=bool, + description="Store the final solution into the original Pyomo model", + )) + CONFIG.declare("options", ConfigBlock(implicit=True)) + + + def __init__(self, **kwds): + """Create an instance of the CyIpoptSolver. You must + provide a problem_interface that corresponds to + the abstract class CyIpoptProblemInterface + + options can be provided as a dictionary of key value + pairs + """ + self.config = self.CONFIG(kwds) + + def _set_model(self, model): + self._model = model + + def available(self, exception_flag=False): + return numpy_available and ipopt_available + + def version(self): + return tuple(int(_) for _ in ipopt.__version__.split('.')) + + def solve(self, model, **kwds): + config = self.config(kwds, preserve_implicit=True) + + if not isinstance(model, Block): + raise ValueError("PyomoCyIpoptSolver.solve(model): model " + "must be a Pyomo Block") + + # If this is a Pyomo model / block, then we need to create + # the appropriate PyomoNLP, then wrap it in a CyIpoptNLP + grey_box_blocks = list(model.component_data_objects( + egb.ExternalGreyBoxBlock, active=True)) + if grey_box_blocks: + nlp = pyomo_nlp.PyomoGreyBoxNLP(model) + else: + nlp = pyomo_nlp.PyomoNLP(model) + problem = CyIpoptNLP(nlp) + + xl = problem.x_lb() + xu = problem.x_ub() + gl = problem.g_lb() + gu = problem.g_ub() + + nx = len(xl) + ng = len(gl) + + cyipopt_solver = ipopt.problem( + n=nx, + m=ng, + problem_obj=problem, + lb=xl, + ub=xu, + cl=gl, + cu=gu + ) + + # check if we need scaling + obj_scaling, x_scaling, g_scaling = problem.scaling_factors() + if any(_ is not None for _ in (obj_scaling, x_scaling, g_scaling)): + # need to set scaling factors + if obj_scaling is None: + obj_scaling = 1.0 + if x_scaling is None: + x_scaling = np.ones(nx) + if g_scaling is None: + g_scaling = np.ones(ng) + cyipopt_solver.setProblemScaling(obj_scaling, x_scaling, g_scaling) + + # add options + for k, v in config.options.items(): + cyipopt_solver.addOption(k, v) + + timer = TicTocTimer() + try: + if config.tee: + x, info = cyipopt_solver.solve(problem.x_init()) + else: + newstdout = _redirect_stdout() + x, info = cyipopt_solver.solve(problem.x_init()) + os.dup2(newstdout, 1) + solverStatus = SolverStatus.ok + except: + solverStatus = SolverStatus.unknown + wall_time = timer.toc("") + + results = SolverResults() + + if config.load_solutions: + nlp.set_primals(x) + nlp.set_duals(info['mult_g']) + nlp.load_state_into_pyomo( + bound_multipliers=(info['mult_x_L'], info['mult_x_U'])) + else: + soln = results.solution.add() + soln.variable.update( + (i, {'Value':j, 'ipopt_zL_out': zl, 'ipopt_zU_out': zu}) + for i,j,zl,zu in zip( nlp.variable_names(), + x, + info['mult_x_L'], + info['mult_x_U'] ) + ) + soln.constraint.update( + (i, {'Dual':j}) for i,j in zip( + nlp.constraint_names(), info['mult_g'])) + + + results.problem.name = model.name + obj = next(model.component_data_objects(Objective, active=True)) + if obj.sense == minimize: + results.problem.sense = ProblemSense.minimize + results.problem.upper_bound = info['obj_val'] + else: + results.problem.sense = ProblemSense.maximize + results.problem.lower_bound = info['obj_val'] + results.problem.number_of_objectives = 1 + results.problem.number_of_constraints = ng + results.problem.number_of_variables = nx + results.problem.number_of_binary_variables = 0 + results.problem.number_of_integer_variables = 0 + results.problem.number_of_continuous_variables = nx + # TODO: results.problem.number_of_nonzeros + + results.solver.name = 'cyipopt' + results.solver.return_code = info['status'] + results.solver.message = info['status_msg'] + results.solver.wallclock_time = wall_time + status_enum = _cyipopt_status_enum[info['status_msg']] + results.solver.termination_condition = _ipopt_term_cond[status_enum] + results.solver.status = TerminationCondition.to_solver_status( + results.solver.termination_condition) + return results + + # + # Support "with" statements. + # + def __enter__(self): + return self + + def __exit__(self, t, v, traceback): + pass diff --git a/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_interfaces.py b/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_interfaces.py index 32421fbc0b0..392d5b70c3b 100644 --- a/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_interfaces.py +++ b/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_interfaces.py @@ -83,7 +83,7 @@ def test_model1_CyIpoptNLP_scaling(self): cynlp = CyIpoptNLP(PyomoNLP(m)) obj_scaling, x_scaling, g_scaling = cynlp.scaling_factors() - self.assertTrue(obj_scaling == None) + self.assertTrue(obj_scaling == 1.0) self.assertTrue(len(x_scaling) == 3) # vars are in order x[2], x[3], x[1] self.assertTrue(x_scaling[0] == 1.0) diff --git a/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_solver.py b/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_solver.py index 4031b979ed8..b0a1d4d6e16 100644 --- a/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_solver.py +++ b/pyomo/contrib/pynumero/algorithms/solvers/tests/test_cyipopt_solver.py @@ -34,6 +34,7 @@ CyIpoptSolver, CyIpoptNLP ) + def create_model1(): m = pyo.ConcreteModel() m.x = pyo.Var([1, 2, 3], initialize=4.0) @@ -45,6 +46,7 @@ def create_model1(): return m + def create_model2(): m = pyo.ConcreteModel() m.x = pyo.Var([1, 2], initialize=4.0) @@ -223,7 +225,3 @@ def test_options(self): x, info = solver.solve(tee=False) nlp.set_primals(x) self.assertAlmostEqual(nlp.evaluate_objective(), -5.0879028e+02, places=5) - - - - diff --git a/pyomo/contrib/pynumero/algorithms/solvers/tests/test_pyomo_ext_cyipopt.py b/pyomo/contrib/pynumero/algorithms/solvers/tests/test_pyomo_ext_cyipopt.py index 2b87fd2e44d..5aece7d0988 100644 --- a/pyomo/contrib/pynumero/algorithms/solvers/tests/test_pyomo_ext_cyipopt.py +++ b/pyomo/contrib/pynumero/algorithms/solvers/tests/test_pyomo_ext_cyipopt.py @@ -31,6 +31,7 @@ from pyomo.contrib.pynumero.algorithms.solvers.pyomo_ext_cyipopt import ExternalInputOutputModel, PyomoExternalCyIpoptProblem from pyomo.contrib.pynumero.algorithms.solvers.cyipopt_solver import CyIpoptSolver + class PressureDropModel(ExternalInputOutputModel): def __init__(self): self._Pin = None @@ -56,6 +57,7 @@ def evaluate_derivatives(self): jac = np.asarray(jac, dtype=np.float64) return spa.coo_matrix(jac) + class TestExternalInputOutputModel(unittest.TestCase): def test_interface(self): diff --git a/pyomo/contrib/pynumero/asl.py b/pyomo/contrib/pynumero/asl.py index a14223f5b98..32474271371 100644 --- a/pyomo/contrib/pynumero/asl.py +++ b/pyomo/contrib/pynumero/asl.py @@ -7,12 +7,11 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from pyomo.common.fileutils import find_library import numpy.ctypeslib as npct import numpy as np -import platform import ctypes -import sys import os class _NotSet: diff --git a/pyomo/contrib/pynumero/doc/source/conf.py b/pyomo/contrib/pynumero/doc/source/conf.py index b75d2d105a7..90bf3e61543 100644 --- a/pyomo/contrib/pynumero/doc/source/conf.py +++ b/pyomo/contrib/pynumero/doc/source/conf.py @@ -26,7 +26,6 @@ # sys.path.insert(0, os.path.abspath('.')) import sys import os -import shlex sys.path.insert(0, os.path.abspath('../')) # -- Project information ----------------------------------------------------- diff --git a/pyomo/contrib/pynumero/examples/basic.py b/pyomo/contrib/pynumero/examples/basic.py index 583f918c212..84f4e4c3915 100644 --- a/pyomo/contrib/pynumero/examples/basic.py +++ b/pyomo/contrib/pynumero/examples/basic.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP import pyomo.environ as pyo @@ -13,6 +23,7 @@ def create_model(): return m + model = create_model() nlp = PyomoNLP(model) diff --git a/pyomo/contrib/pynumero/examples/derivatives.py b/pyomo/contrib/pynumero/examples/derivatives.py index f4f5d93ee61..7c8ecdaf13c 100644 --- a/pyomo/contrib/pynumero/examples/derivatives.py +++ b/pyomo/contrib/pynumero/examples/derivatives.py @@ -7,6 +7,7 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from pyomo.contrib.pynumero.sparse import BlockMatrix from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP import matplotlib.pylab as plt @@ -55,11 +56,15 @@ def _int_rule(M, i): m.init_condition_names = ['init_conditions'] return m + instance = create_problem(0.0, 10.0) # Discretize model using Orthogonal Collocation discretizer = pyo.TransformationFactory('dae.collocation') discretizer.apply_to(instance, nfe=100, ncp=3, scheme='LAGRANGE-RADAU') -discretizer.reduce_collocation_points(instance, var=instance.u, ncp=1, contset=instance.t) +discretizer.reduce_collocation_points(instance, + var=instance.u, + ncp=1, + contset=instance.t) # Interface pyomo model with nlp nlp = PyomoNLP(instance) @@ -84,11 +89,10 @@ def _int_rule(M, i): plt.show() # Build KKT matrix -kkt = BlockMatrix(2,2) +kkt = BlockMatrix(2, 2) kkt.set_block(0, 0, hess_lag) kkt.set_block(1, 0, jac) kkt.set_block(0, 1, jac.transpose()) plt.spy(kkt.tocoo()) plt.title('KKT system\n') plt.show() - diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/react-example/maximize_cb_outputs.py b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/maximize_cb_outputs.py new file mode 100644 index 00000000000..7ec2be479d4 --- /dev/null +++ b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/maximize_cb_outputs.py @@ -0,0 +1,49 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from __future__ import division +import pyomo.environ as pyo +from pyomo.contrib.pynumero.interfaces.external_grey_box import \ + ExternalGreyBoxBlock +from reactor_model_outputs import ReactorConcentrationsOutputModel + +def maximize_cb_outputs(show_solver_log=False): + # in this simple example, we will use an external grey box model representing + # a steady-state reactor, and solve for the space velocity that maximizes + # the concentration of component B coming out of the reactor + m = pyo.ConcreteModel() + + # create a block to store the external reactor model + m.reactor = ExternalGreyBoxBlock( + external_model=ReactorConcentrationsOutputModel() + ) + + # The reaction rate constants and the feed concentration will + # be fixed for this example + m.k1con = pyo.Constraint(expr=m.reactor.inputs['k1'] == 5/6) + m.k2con = pyo.Constraint(expr=m.reactor.inputs['k2'] == 5/3) + m.k3con = pyo.Constraint(expr=m.reactor.inputs['k3'] == 1/6000) + m.cafcon = pyo.Constraint(expr=m.reactor.inputs['caf'] == 10000) + + # add an objective function that maximizes the concentration + # of cb coming out of the reactor + m.obj = pyo.Objective(expr=m.reactor.outputs['cb'], sense=pyo.maximize) + + solver = pyo.SolverFactory('cyipopt') + solver.config.options['hessian_approximation'] = 'limited-memory' + results = solver.solve(m, tee=show_solver_log) + pyo.assert_optimal_termination(results) + return m + +if __name__ == '__main__': + m = maximize_cb_outputs(show_solver_log=True) + m.pprint() + + diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/react-example/maximize_cb_ratio_residuals.py b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/maximize_cb_ratio_residuals.py new file mode 100644 index 00000000000..e4a71912942 --- /dev/null +++ b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/maximize_cb_ratio_residuals.py @@ -0,0 +1,164 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ +import pyomo.environ as pyo +from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxBlock +from reactor_model_residuals import ReactorModel, ReactorModelNoOutputs, ReactorModelScaled + +def maximize_cb_ratio_residuals_with_output(show_solver_log=False, additional_options={}): + # in this simple example, we will use an external grey box model representing + # a steady-state reactor, and solve for the space velocity that maximizes + # the ratio of B to the other components coming out of the reactor + # This example illustrates the use of "equality constraints" or residuals + # in the external grey box example as well as outputs + m = pyo.ConcreteModel() + + # create a block to store the external reactor model + m.reactor = ExternalGreyBoxBlock(external_model=ReactorModel()) + + # The feed concentration will be fixed for this example + m.cafcon = pyo.Constraint(expr=m.reactor.inputs['caf'] == 10000) + + # add an objective function that maximizes the concentration + # of cb coming out of the reactor + m.obj = pyo.Objective(expr=m.reactor.outputs['cb_ratio'], sense=pyo.maximize) + + solver = pyo.SolverFactory('cyipopt') + solver.config.options['hessian_approximation'] = 'limited-memory' + for k,v in additional_options.items(): + solver.config.options[k] = v + results = solver.solve(m, tee=show_solver_log) + pyo.assert_optimal_termination(results) + return m + +def maximize_cb_ratio_residuals_with_output_scaling(show_solver_log=False, additional_options={}): + # in this simple example, we will use an external grey box model representing + # a steady-state reactor, and solve for the space velocity that maximizes + # the ratio of B to the other components coming out of the reactor + # This example illustrates the use of "equality constraints" or residuals + # in the external grey box example as well as outputs + + # This example also shows how to do scaling. + # There are two things to scale, the "Pyomo" variables/constraints, + # and the external model residuals / output equations. + # The scaling factors for the external residuals and output equations + # are set in the derived ExternalGreyBoxModel class (see ReactorModelScaled + # for this example). + # The scaling factors for the Pyomo part of the model are set using suffixes + # - this requires that we declare the scaling suffix on the main model as + # shown below. + # - Then the scaling factors can be set directly on the Pyomo variables + # and constraints as also shown below. + # - Note: In this example, the scaling factors for the input and output + # variables from the grey box model are set in the finalize_block_construction + # callback (again, see ReactorModelScaled) + m = pyo.ConcreteModel() + + # declare the scaling suffix on the model + m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) + + # create a block to store the external reactor model + m.reactor = ExternalGreyBoxBlock(external_model=ReactorModelScaled()) + + # The feed concentration will be fixed for this example + m.cafcon = pyo.Constraint(expr=m.reactor.inputs['caf'] == 10000) + # set a scaling factor for this constraint - if we had additional pyomo + # variables, we could set them the same way + m.scaling_factor[m.cafcon] = 42.0 + + # add an objective function that maximizes the concentration + # of cb coming out of the reactor + m.obj = pyo.Objective(expr=m.reactor.outputs['cb_ratio'], sense=pyo.maximize) + + solver = pyo.SolverFactory('cyipopt') + solver.config.options['hessian_approximation'] = 'limited-memory' + for k,v in additional_options.items(): + solver.config.options[k] = v + results = solver.solve(m, tee=show_solver_log) + pyo.assert_optimal_termination(results) + return m + +def maximize_cb_ratio_residuals_with_obj(show_solver_log=False, additional_options={}): + # in this simple example, we will use an external grey box model representing + # a steady-state reactor, and solve for the space velocity that maximizes + # the ratio of B to the other components coming out of the reactor + # This example illustrates the use of "equality constraints" or residuals + # in the external grey box example as well as additional pyomo variables + # and constraints + m = pyo.ConcreteModel() + + # create a block to store the external reactor model + m.reactor = ExternalGreyBoxBlock() + m.reactor.set_external_model(ReactorModelNoOutputs()) + + # The feed concentration will be fixed for this example + m.cafcon = pyo.Constraint(expr=m.reactor.inputs['caf'] == 10000) + + # add an objective function that maximizes the concentration + # of cb coming out of the reactor + u = m.reactor.inputs + m.obj = pyo.Objective(expr=u['cb']/(u['ca']+u['cc']+u['cd']), sense=pyo.maximize) + + solver = pyo.SolverFactory('cyipopt') + solver.config.options['hessian_approximation'] = 'limited-memory' + for k,v in additional_options.items(): + solver.config.options[k] = v + results = solver.solve(m, tee=show_solver_log) + pyo.assert_optimal_termination(results) + return m + +def maximize_cb_ratio_residuals_with_pyomo_variables(show_solver_log=False, additional_options={}): + # in this simple example, we will use an external grey box model representing + # a steady-state reactor, and solve for the space velocity that maximizes + # the ratio of B to the other components coming out of the reactor + # This example illustrates the use of "equality constraints" or residuals + # in the external grey box example as well as additional pyomo variables + # and constraints + m = pyo.ConcreteModel() + + # create a block to store the external reactor model + m.reactor = ExternalGreyBoxBlock() + m.reactor.set_external_model(ReactorModelNoOutputs()) + + # add a variable and constraint for the cb ratio + m.cb_ratio = pyo.Var(initialize=1) + u = m.reactor.inputs + m.cb_ratio_con = pyo.Constraint(expr = \ + u['cb']/(u['ca']+u['cc']+u['cd']) - m.cb_ratio == 0) + + # The feed concentration will be fixed for this example + m.cafcon = pyo.Constraint(expr=m.reactor.inputs['caf'] == 10000) + + # add an objective function that maximizes the concentration + # of cb coming out of the reactor + m.obj = pyo.Objective(expr=m.cb_ratio, sense=pyo.maximize) + + solver = pyo.SolverFactory('cyipopt') + solver.config.options['hessian_approximation'] = 'limited-memory' + for k,v in additional_options.items(): + solver.config.options[k] = v + results = solver.solve(m, tee=show_solver_log) + pyo.assert_optimal_termination(results) + return m + +if __name__ == '__main__': + #m = maximize_cb_ratio_residuals_with_output(show_solver_log=True) + #m.pprint() + aoptions={'hessian_approximation':'limited-memory', + #'limited_memory_update_type': 'sr1', + 'nlp_scaling_method': 'user-scaling', + 'print_level':10} + m = maximize_cb_ratio_residuals_with_output_scaling(show_solver_log=True, additional_options=aoptions) + #m.pprint() + #m = maximize_cb_ratio_residuals_with_obj(show_solver_log=True) + #m.pprint() + #m = maximize_cb_ratio_residuals_with_pyomo_variables(show_solver_log=True) + #m.pprint() + + diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/react-example/reactor_model_outputs.py b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/reactor_model_outputs.py new file mode 100644 index 00000000000..64377bc540d --- /dev/null +++ b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/reactor_model_outputs.py @@ -0,0 +1,129 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +""" +This file contains a black box model representing a simple +reactor design problem described in the Pyomo book. +It is part of the external_grey_box example with PyNumero. + +These functions solve a reactor model using scipy +Note: In this case, this model can be solved using +standard Pyomo constructs (see the Pyomo book), but +this is included as an example of the external grey +box model interface. +""" + +from __future__ import division + +import numpy as np +from scipy.optimize import fsolve +from scipy.sparse import coo_matrix +from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel + +def reactor_outlet_concentrations(sv, caf, k1, k2, k3): + def _model(x, sv, caf, k1, k2, k3): + ca, cb, cc, cd = x[0], x[1], x[2], x[3] + + # compute the residuals + r = np.zeros(4) + r[0] = sv*caf + (-sv-k1)*ca - 2*k3*ca**2 + r[1] = k1*ca + (-sv-k2)*cb + r[2] = k2*cb - sv*cc + r[3] = k3*ca**2 - sv*cd + + return r + + concentrations = \ + fsolve(lambda x: _model(x, sv, caf, k1, k2, k3), np.ones(4), xtol=1e-8) + + # Todo: check solve status + return concentrations + +class ReactorConcentrationsOutputModel(ExternalGreyBoxModel): + def input_names(self): + return ['sv', 'caf', 'k1', 'k2', 'k3'] + + def output_names(self): + return ['ca', 'cb', 'cc', 'cd'] + + def set_input_values(self, input_values): + self._input_values = list(input_values) + + def finalize_block_construction(self, pyomo_block): + # set lower bounds on the inputs and outputs + pyomo_block.inputs['sv'].setlb(0) + pyomo_block.outputs['ca'].setlb(0) + pyomo_block.outputs['cb'].setlb(0) + pyomo_block.outputs['cc'].setlb(0) + pyomo_block.outputs['cd'].setlb(0) + + # initialize the variables + pyomo_block.inputs['sv'].value = 5 + pyomo_block.inputs['caf'].value = 10000 + pyomo_block.inputs['k1'].value = 5/6 + pyomo_block.inputs['k2'].value = 5/3 + pyomo_block.inputs['k3'].value = 1/6000 + pyomo_block.outputs['ca'].value = 1 + pyomo_block.outputs['cb'].value = 1 + pyomo_block.outputs['cc'].value = 1 + pyomo_block.outputs['cd'].value = 1 + + def evaluate_outputs(self): + sv = self._input_values[0] + caf = self._input_values[1] + k1 = self._input_values[2] + k2 = self._input_values[3] + k3 = self._input_values[4] + ret = reactor_outlet_concentrations(sv, caf, k1, k2, k3) + return np.asarray(ret, dtype=np.float64) + + def evaluate_jacobian_outputs(self): + # here, we compute the derivatives using finite difference + # however, this would be better with analytical derivatives + delta = 1e-6 + u0 = np.copy(self._input_values) + y0 = self.evaluate_outputs() + jac = np.empty((4,5)) + u = np.copy(self._input_values) + for j in range(len(u)): + # perturb the variables + u[j] += delta + self.set_input_values(u) + yperturb = self.evaluate_outputs() + jac_col = (yperturb - y0)/delta + jac[:,j] = jac_col + u[j] = u0[j] + + # return us back to our starting state + self.set_input_values(u0) + + # this needs to be a sparse coo_matrix + # this approach is inefficient, but clear for an example + row = [] + col = [] + data = [] + for r in range(4): + for c in range(5): + row.append(r) + col.append(c) + data.append(jac[r,c]) + + return coo_matrix((data, (row, col)), shape=(4,5)) + +if __name__ == '__main__': + sv = 1.34 + caf = 10000 + k1 = 5/6 + k2 = 5/3 + k3 = 1/6000 + concentrations = reactor_outlet_concentrations(sv, caf, k1, k2, k3) + print(concentrations) + + diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/react-example/reactor_model_residuals.py b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/reactor_model_residuals.py new file mode 100644 index 00000000000..857b3d88221 --- /dev/null +++ b/pyomo/contrib/pynumero/examples/external_grey_box/react-example/reactor_model_residuals.py @@ -0,0 +1,357 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ +""" +This file contains an external grey box model representing a simple +reactor design problem described in the Pyomo book. +It is part of the external_grey_box examples with PyNumero. + +Note: In this case, this model can be solved using +standard Pyomo constructs (see the Pyomo book), but +this is included as an example of the external grey +box model interface. +""" + +from __future__ import division + +import numpy as np +from scipy.sparse import coo_matrix +from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxModel + +class ReactorModel(ExternalGreyBoxModel): + def __init__(self, use_exact_derivatives=True): + self._use_exact_derivatives = use_exact_derivatives + + def input_names(self): + return ['sv', 'caf', 'ca', 'cb', 'cc', 'cd'] + + def equality_constraint_names(self): + return ['ca_bal', 'cb_bal', 'cc_bal', 'cd_bal'] + + def output_names(self): + return ['cb_ratio'] + + def finalize_block_construction(self, pyomo_block): + # set lower bounds on the variables + pyomo_block.inputs['sv'].setlb(0) + pyomo_block.inputs['ca'].setlb(0) + pyomo_block.inputs['cb'].setlb(0) + pyomo_block.inputs['cc'].setlb(0) + pyomo_block.inputs['cd'].setlb(0) + + # initialize the variables + pyomo_block.inputs['sv'].value = 1 + pyomo_block.inputs['caf'].value = 1 + pyomo_block.inputs['ca'].value = 1 + pyomo_block.inputs['cb'].value = 1 + pyomo_block.inputs['cc'].value = 1 + pyomo_block.inputs['cd'].value = 1 + pyomo_block.outputs['cb_ratio'].value = 1 + + def set_input_values(self, input_values): + self._input_values = list(input_values) + + def evaluate_equality_constraints(self): + sv = self._input_values[0] + caf = self._input_values[1] + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + k1 = 5/6 + k2 = 5/3 + k3 = 1/6000 + r = np.zeros(4) + r[0] = sv*caf + (-sv-k1)*ca - 2*k3*ca**2 + r[1] = k1*ca + (-sv-k2)*cb + r[2] = k2*cb - sv*cc + r[3] = k3*ca**2 - sv*cd + return r + + def evaluate_outputs(self): + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + cb_ratio = cb/(ca+cc+cd) + return np.asarray([cb_ratio], dtype=np.float64) + + def evaluate_jacobian_equality_constraints(self): + sv = self._input_values[0] + caf = self._input_values[1] + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + k1 = 5/6 + k2 = 5/3 + k3 = 1/6000 + + if self._use_exact_derivatives: + row = np.zeros(12) + col = np.zeros(12) + data = np.zeros(12) + row[0], col[0], data[0] = (0, 0, caf-ca) + row[1], col[1], data[1] = (0, 1, sv) + row[2], col[2], data[2] = (0, 2, -sv-k1-4*k3*ca) + row[3], col[3], data[3] = (1, 0, -cb) + row[4], col[4], data[4] = (1, 2, k1) + row[5], col[5], data[5] = (1, 3, -sv-k2) + row[6], col[6], data[6] = (2, 0, -cc) + row[7], col[7], data[7] = (2, 3, k2) + row[8], col[8], data[8] = (2, 4, -sv) + row[9], col[9], data[9] = (3, 0, -cd) + row[10], col[10], data[10] = (3, 2, 2*k3*ca) + row[11], col[11], data[11] = (3, 5, -sv) + ret = coo_matrix((data, (row, col)), shape=(4,6)) + return ret + else: + delta = 1e-8 + u0 = np.copy(self._input_values) + y0 = self.evaluate_equality_constraints() + jac = np.empty((4,6)) + u = np.copy(self._input_values) + for j in range(len(u)): + # perturb the variables + u[j] += delta + self.set_input_values(u) + yperturb = self.evaluate_equality_constraints() + jac_col = (yperturb - y0)/delta + jac[:,j] = jac_col + u[j] = u0[j] + + # return us back to our starting state + self.set_input_values(u0) + + # this needs to be a sparse coo_matrix + # this approach is inefficient, but clear for an example + row = [] + col = [] + data = [] + for r in range(4): + for c in range(6): + row.append(r) + col.append(c) + data.append(jac[r,c]) + ret = coo_matrix((data, (row, col)), shape=(4,6)) + return ret + + def evaluate_jacobian_outputs(self): + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + cb_ratio = cb/(ca+cc+cd) + row = np.zeros(4) + col = np.zeros(4) + data = np.zeros(4) + row[0], col[0], data[0] = (0, 2, -cb/(ca+cc+cd)**2) + row[1], col[1], data[1] = (0, 3, 1/(ca+cc+cd)) + row[2], col[2], data[2] = (0, 4, -cb/(ca+cc+cd)**2) + row[3], col[3], data[3] = (0, 5, -cb/(ca+cc+cd)**2) + return coo_matrix((data, (row, col)), shape=(1,6)) + +class ReactorModelNoOutputs(ExternalGreyBoxModel): + def input_names(self): + return ['sv', 'caf', 'ca', 'cb', 'cc', 'cd'] + + def equality_constraint_names(self): + return ['ca_bal', 'cb_bal', 'cc_bal', 'cd_bal'] + + def output_names(self): + return [] + + def finalize_block_construction(self, pyomo_block): + # set lower bounds on the variables + pyomo_block.inputs['sv'].setlb(0) + pyomo_block.inputs['ca'].setlb(0) + pyomo_block.inputs['cb'].setlb(0) + pyomo_block.inputs['cc'].setlb(0) + pyomo_block.inputs['cd'].setlb(0) + + # initialize the variables + pyomo_block.inputs['sv'].value = 1 + pyomo_block.inputs['caf'].value = 1 + pyomo_block.inputs['ca'].value = 1 + pyomo_block.inputs['cb'].value = 1 + pyomo_block.inputs['cc'].value = 1 + pyomo_block.inputs['cd'].value = 1 + + def set_input_values(self, input_values): + self._input_values = list(input_values) + + def evaluate_equality_constraints(self): + sv = self._input_values[0] + caf = self._input_values[1] + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + k1 = 5/6 + k2 = 5/3 + k3 = 1/6000 + r = np.zeros(4) + r[0] = sv*caf + (-sv-k1)*ca - 2*k3*ca**2 + r[1] = k1*ca + (-sv-k2)*cb + r[2] = k2*cb - sv*cc + r[3] = k3*ca**2 - sv*cd + return r + + def evaluate_outputs(self): + raise NotImplementedError() + + def evaluate_jacobian_equality_constraints(self): + sv = self._input_values[0] + caf = self._input_values[1] + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + k1 = 5/6 + k2 = 5/3 + k3 = 1/6000 + + row = np.zeros(12) + col = np.zeros(12) + data = np.zeros(12) + row[0], col[0], data[0] = (0, 0, caf-ca) + row[1], col[1], data[1] = (0, 1, sv) + row[2], col[2], data[2] = (0, 2, -sv-k1-4*k3*ca) + row[3], col[3], data[3] = (1, 0, -cb) + row[4], col[4], data[4] = (1, 2, k1) + row[5], col[5], data[5] = (1, 3, -sv-k2) + row[6], col[6], data[6] = (2, 0, -cc) + row[7], col[7], data[7] = (2, 3, k2) + row[8], col[8], data[8] = (2, 4, -sv) + row[9], col[9], data[9] = (3, 0, -cd) + row[10], col[10], data[10] = (3, 2, 2*k3*ca) + row[11], col[11], data[11] = (3, 5, -sv) + ret = coo_matrix((data, (row, col)), shape=(4,6)) + return ret + + def evaluate_jacobian_outputs(self): + raise NotImplementedError() + +class ReactorModelScaled(ExternalGreyBoxModel): + def input_names(self): + return ['sv', 'caf', 'ca', 'cb', 'cc', 'cd'] + + def equality_constraint_names(self): + return ['ca_bal', 'cb_bal', 'cc_bal', 'cd_bal'] + + def output_names(self): + return ['cb_ratio'] + + def finalize_block_construction(self, pyomo_block): + # set lower bounds on the variables + pyomo_block.inputs['sv'].setlb(0) + pyomo_block.inputs['ca'].setlb(0) + pyomo_block.inputs['cb'].setlb(0) + pyomo_block.inputs['cc'].setlb(0) + pyomo_block.inputs['cd'].setlb(0) + + # initialize the variables + pyomo_block.inputs['sv'].value = 1 + pyomo_block.inputs['caf'].value = 1 + pyomo_block.inputs['ca'].value = 1 + pyomo_block.inputs['cb'].value = 1 + pyomo_block.inputs['cc'].value = 1 + pyomo_block.inputs['cd'].value = 1 + pyomo_block.outputs['cb_ratio'].value = 1 + + m = pyomo_block.model() + if not hasattr(m, 'scaling_factor'): + # add the scaling factor suffix to the model if it is not already declared + m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) + + m.scaling_factor[pyomo_block.inputs['sv']] = 1.1 + m.scaling_factor[pyomo_block.inputs['caf']] = 1.2 + m.scaling_factor[pyomo_block.inputs['ca']] = 1.3 + m.scaling_factor[pyomo_block.inputs['cb']] = 1.4 + m.scaling_factor[pyomo_block.inputs['cc']] = 1.5 + m.scaling_factor[pyomo_block.inputs['cd']] = 1.6 + m.scaling_factor[pyomo_block.outputs['cb_ratio']] = 1.7 + + def get_equality_constraint_scaling_factors(self): + return np.asarray([0.1, 0.2, 0.3, 0.4]) + + def get_output_constraint_scaling_factors(self): + return np.asarray([10]) + + def set_input_values(self, input_values): + self._input_values = list(input_values) + + def evaluate_equality_constraints(self): + sv = self._input_values[0] + caf = self._input_values[1] + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + k1 = 5/6 + k2 = 5/3 + k3 = 1/6000 + r = np.zeros(4) + r[0] = sv*caf + (-sv-k1)*ca - 2*k3*ca**2 + r[1] = k1*ca + (-sv-k2)*cb + r[2] = k2*cb - sv*cc + r[3] = k3*ca**2 - sv*cd + return r + + def evaluate_outputs(self): + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + cb_ratio = cb/(ca+cc+cd) + return np.asarray([cb_ratio], dtype=np.float64) + + def evaluate_jacobian_equality_constraints(self): + sv = self._input_values[0] + caf = self._input_values[1] + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + k1 = 5/6 + k2 = 5/3 + k3 = 1/6000 + + row = np.zeros(12) + col = np.zeros(12) + data = np.zeros(12) + row[0], col[0], data[0] = (0, 0, caf-ca) + row[1], col[1], data[1] = (0, 1, sv) + row[2], col[2], data[2] = (0, 2, -sv-k1-4*k3*ca) + row[3], col[3], data[3] = (1, 0, -cb) + row[4], col[4], data[4] = (1, 2, k1) + row[5], col[5], data[5] = (1, 3, -sv-k2) + row[6], col[6], data[6] = (2, 0, -cc) + row[7], col[7], data[7] = (2, 3, k2) + row[8], col[8], data[8] = (2, 4, -sv) + row[9], col[9], data[9] = (3, 0, -cd) + row[10], col[10], data[10] = (3, 2, 2*k3*ca) + row[11], col[11], data[11] = (3, 5, -sv) + ret = coo_matrix((data, (row, col)), shape=(4,6)) + return ret + + def evaluate_jacobian_outputs(self): + ca = self._input_values[2] + cb = self._input_values[3] + cc = self._input_values[4] + cd = self._input_values[5] + cb_ratio = cb/(ca+cc+cd) + row = np.zeros(4) + col = np.zeros(4) + data = np.zeros(4) + row[0], col[0], data[0] = (0, 2, -cb/(ca+cc+cd)**2) + row[1], col[1], data[1] = (0, 3, 1/(ca+cc+cd)) + row[2], col[2], data[2] = (0, 4, -cb/(ca+cc+cd)**2) + row[3], col[3], data[3] = (0, 5, -cb/(ca+cc+cd)**2) + return coo_matrix((data, (row, col)), shape=(1,6)) diff --git a/pyomo/contrib/pynumero/examples/feasibility.py b/pyomo/contrib/pynumero/examples/feasibility.py index 64c3906bb0a..d24d38ec26b 100644 --- a/pyomo/contrib/pynumero/examples/feasibility.py +++ b/pyomo/contrib/pynumero/examples/feasibility.py @@ -7,9 +7,11 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP -from pyomo.contrib.pynumero.interfaces.utils import build_bounds_mask, build_compression_matrix, full_to_compressed -import matplotlib.pylab as plt +from pyomo.contrib.pynumero.interfaces.utils import (build_bounds_mask, + build_compression_matrix, + full_to_compressed) import pyomo.environ as pyo import numpy as np @@ -101,4 +103,3 @@ def create_basic_model(): feasible = True print("Is x0 feasible:", feasible) - diff --git a/pyomo/contrib/pynumero/examples/gas_network_model.py b/pyomo/contrib/pynumero/examples/gas_network_model.py index a5e50a3c9da..55b6b510140 100644 --- a/pyomo/contrib/pynumero/examples/gas_network_model.py +++ b/pyomo/contrib/pynumero/examples/gas_network_model.py @@ -7,11 +7,12 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + import pyomo.environ as pyo import pyomo.dae as dae import numpy as np import networkx -import json + def create_model(demand_factor=1.0): diff --git a/pyomo/contrib/pynumero/examples/sensitivity.py b/pyomo/contrib/pynumero/examples/sensitivity.py index ac56b8e1a40..afbbc443fdb 100644 --- a/pyomo/contrib/pynumero/examples/sensitivity.py +++ b/pyomo/contrib/pynumero/examples/sensitivity.py @@ -7,6 +7,7 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + import pyomo.environ as pyo from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP from pyomo.contrib.pynumero.sparse import BlockMatrix, BlockVector @@ -110,7 +111,7 @@ def compute_init_lam(nlp, x=None, lam_max=1e3): new_x = x[x_indices] + dx print("dp:", dp) print("dx:", dx) -print("Variable names: \n",x_names[x_indices]) +print("Variable names: \n", x_names[x_indices]) print("Sensitivity based x:\n", new_x) ################################################################# diff --git a/pyomo/contrib/pynumero/examples/structured/nlp_transformations.py b/pyomo/contrib/pynumero/examples/structured/nlp_transformations.py index 2c405e7c62f..8af3a628323 100644 --- a/pyomo/contrib/pynumero/examples/structured/nlp_transformations.py +++ b/pyomo/contrib/pynumero/examples/structured/nlp_transformations.py @@ -7,11 +7,12 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from pyomo.contrib.pynumero.interfaces.nlp import NLP from pyomo.contrib.pynumero.sparse import empty_matrix -from scipy.sparse import coo_matrix, csc_matrix, csr_matrix -import pyomo.environ as aml +from scipy.sparse import coo_matrix, csr_matrix +import pyomo.environ as pyo import numpy as np __all__ = ['AdmmNLP'] @@ -711,15 +712,15 @@ def compose_two_stage_stochastic_model(models, complicating_vars): assert len(v) == nz, 'all models must have same number of complicating variables' counter += 1 - model = aml.ConcreteModel() - model.z = aml.Var(range(nz)) + model = pyo.ConcreteModel() + model.z = pyo.Var(range(nz)) model.scenario_names = sorted([name for name in models.keys()]) obj = 0.0 for i, j in enumerate(model.scenario_names): instance = models[j].clone() - model.add_component("{}_linking".format(j), aml.ConstraintList()) + model.add_component("{}_linking".format(j), pyo.ConstraintList()) model.add_component("{}".format(j), instance) linking = getattr(model, "{}_linking".format(j)) x = complicating_vars[j] @@ -727,16 +728,16 @@ def compose_two_stage_stochastic_model(models, complicating_vars): for k, var in enumerate(x): if var.is_indexed(): raise RuntimeError('indexed complicating variables not supported') - vid = aml.ComponentUID(var) + vid = pyo.ComponentUID(var) vv = vid.find_component_on(instance) linking.add(vv == model.z[k]) # gets objective - objectives = instance.component_map(aml.Objective, active=True) + objectives = instance.component_map(pyo.Objective, active=True) if len(objectives) > 1: raise RuntimeError('Multiple objectives not supported') instance_obj = list(objectives.values())[0] obj += instance_obj.expr instance_obj.deactivate() - model.obj = aml.Objective(expr=obj) + model.obj = pyo.Objective(expr=obj) return model \ No newline at end of file diff --git a/pyomo/contrib/pynumero/examples/structured/stochastic_nlp.py b/pyomo/contrib/pynumero/examples/structured/stochastic_nlp.py index c06af88d2ca..06e8af51ca4 100644 --- a/pyomo/contrib/pynumero/examples/structured/stochastic_nlp.py +++ b/pyomo/contrib/pynumero/examples/structured/stochastic_nlp.py @@ -7,27 +7,29 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from pyomo.contrib.pynumero.interfaces import PyomoNLP from pyomo.contrib.pynumero.interfaces.nlp_compositions import TwoStageStochasticNLP -from pyomo.contrib.pynumero.sparse import BlockSymMatrix, BlockVector +from pyomo.contrib.pynumero.sparse import BlockSymMatrix import matplotlib.pylab as plt -import pyomo.environ as aml +import pyomo.environ as pyo import numpy as np -import os + def create_basic_dense_qp(G, A, b, c): nx = G.shape[0] nl = A.shape[0] - model = aml.ConcreteModel() + model = pyo.ConcreteModel() model.var_ids = range(nx) model.con_ids = range(nl) - model.x = aml.Var(model.var_ids, initialize=0.0) - + model.x = pyo.Var(model.var_ids, initialize=0.0) + def equality_constraint_rule(m, i): return sum(A[i, j] * m.x[j] for j in m.var_ids) == b[i] - model.equalities = aml.Constraint(model.con_ids, rule=equality_constraint_rule) + model.equalities = pyo.Constraint(model.con_ids, + rule=equality_constraint_rule) def objective_rule(m): accum = 0.0 @@ -37,10 +39,11 @@ def objective_rule(m): accum += sum(m.x[j] * c[j] for j in m.var_ids) return accum - model.obj = aml.Objective(rule=objective_rule, sense=aml.minimize) + model.obj = pyo.Objective(rule=objective_rule, sense=pyo.minimize) return model + G = np.array([[6, 2, 1], [2, 5, 2], [1, 2, 4]]) A = np.array([[1, 0, 1], [0, 1, 1]]) b = np.array([3, 0]) diff --git a/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_compositions.py b/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_compositions.py index b802309d2ba..56bf718aa87 100644 --- a/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_compositions.py +++ b/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_compositions.py @@ -7,8 +7,9 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as aml +import pyomo.environ as pyo import os from pyomo.contrib.pynumero.dependencies import ( numpy as np, numpy_available, scipy_sparse, scipy_available @@ -31,30 +32,30 @@ def create_basic_dense_qp(G, A, b, c, complicated_var_ids): nx = G.shape[0] nl = A.shape[0] - model = aml.ConcreteModel() + model = pyo.ConcreteModel() model.var_ids = range(nx) model.complicated_var_ids = complicated_var_ids model.con_ids = range(nl) - model.x = aml.Var(model.var_ids, initialize=0.0) + model.x = pyo.Var(model.var_ids, initialize=0.0) model.x[0].value = 1.0 model.x[0].setlb(-100.0) model.x[0].setub(100.0) - model.z = aml.Var(model.complicated_var_ids, initialize=0.0) - model.hessian_f = aml.Param(model.var_ids, model.var_ids, mutable=True, rule=lambda m, i, j: G[i, j]) - model.jacobian_c = aml.Param(model.con_ids, model.var_ids, mutable=True, rule=lambda m, i, j: A[i, j]) - model.rhs = aml.Param(model.con_ids, mutable=True, rule=lambda m, i: b[i]) - model.grad_f = aml.Param(model.var_ids, mutable=True, rule=lambda m, i: c[i]) + model.z = pyo.Var(model.complicated_var_ids, initialize=0.0) + model.hessian_f = pyo.Param(model.var_ids, model.var_ids, mutable=True, rule=lambda m, i, j: G[i, j]) + model.jacobian_c = pyo.Param(model.con_ids, model.var_ids, mutable=True, rule=lambda m, i, j: A[i, j]) + model.rhs = pyo.Param(model.con_ids, mutable=True, rule=lambda m, i: b[i]) + model.grad_f = pyo.Param(model.var_ids, mutable=True, rule=lambda m, i: c[i]) def equality_constraint_rule(m, i): return sum(m.jacobian_c[i, j] * m.x[j] for j in m.var_ids) == m.rhs[i] - model.equalities = aml.Constraint(model.con_ids, rule=equality_constraint_rule) + model.equalities = pyo.Constraint(model.con_ids, rule=equality_constraint_rule) def fixing_constraints_rule(m, i): return m.z[i] == m.x[i] - model.fixing_constraints = aml.Constraint(model.complicated_var_ids, rule=fixing_constraints_rule) + model.fixing_constraints = pyo.Constraint(model.complicated_var_ids, rule=fixing_constraints_rule) def second_stage_cost_rule(m): accum = 0.0 @@ -64,30 +65,30 @@ def second_stage_cost_rule(m): accum += sum(m.x[j] * m.grad_f[j] for j in m.var_ids) return accum - model.FirstStageCost = aml.Expression(expr=0.0) - model.SecondStageCost = aml.Expression(rule=second_stage_cost_rule) + model.FirstStageCost = pyo.Expression(expr=0.0) + model.SecondStageCost = pyo.Expression(rule=second_stage_cost_rule) - model.obj = aml.Objective(expr=model.FirstStageCost + model.SecondStageCost, - sense=aml.minimize) + model.obj = pyo.Objective(expr=model.FirstStageCost + model.SecondStageCost, + sense=pyo.minimize) return model def create_basic_model(): - m = aml.ConcreteModel() - m.x = aml.Var([1, 2, 3], domain=aml.Reals) + m = pyo.ConcreteModel() + m.x = pyo.Var([1, 2, 3], domain=pyo.Reals) for i in range(1, 4): m.x[i].value = i - m.c1 = aml.Constraint(expr=m.x[1] ** 2 - m.x[2] - 1 == 0) - m.c2 = aml.Constraint(expr=m.x[1] - m.x[3] - 0.5 == 0) - m.d1 = aml.Constraint(expr=m.x[1] + m.x[2] <= 100.0) - m.d2 = aml.Constraint(expr=m.x[2] + m.x[3] >= -100.0) - m.d3 = aml.Constraint(expr=m.x[2] + m.x[3] + m.x[1] >= -500.0) + m.c1 = pyo.Constraint(expr=m.x[1] ** 2 - m.x[2] - 1 == 0) + m.c2 = pyo.Constraint(expr=m.x[1] - m.x[3] - 0.5 == 0) + m.d1 = pyo.Constraint(expr=m.x[1] + m.x[2] <= 100.0) + m.d2 = pyo.Constraint(expr=m.x[2] + m.x[3] >= -100.0) + m.d3 = pyo.Constraint(expr=m.x[2] + m.x[3] + m.x[1] >= -500.0) m.x[2].setlb(0.0) m.x[3].setlb(0.0) m.x[2].setub(100.0) - m.obj = aml.Objective(expr=m.x[2]**2) + m.obj = pyo.Objective(expr=m.x[2]**2) return m diff --git a/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_transformations.py b/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_transformations.py index 80b0442620b..38c0fc7626a 100644 --- a/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_transformations.py +++ b/pyomo/contrib/pynumero/examples/structured/tests/test_nlp_transformations.py @@ -7,10 +7,8 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + import pyutilib.th as unittest -from pyomo.common.plugin import alias -import pyomo.environ as aml -import os from pyomo.contrib.pynumero.dependencies import ( numpy as np, numpy_available, scipy_sparse as spa, scipy_available @@ -27,7 +25,7 @@ from pyomo.contrib.pynumero.interfaces.nlp import PyomoNLP from pyomo.contrib.pynumero.interfaces.nlp_transformations import AdmmNLP from pyomo.core.plugins.transform.hierarchy import Transformation -import pyomo.environ as aml +import pyomo.environ as pyo class AdmmModel(Transformation): @@ -59,7 +57,7 @@ def _apply_to(self, model, **kwds): cloned_vars = [] original_vars = [] for v in complicating_vars: - vid = aml.ComponentUID(v) + vid = pyo.ComponentUID(v) vv = vid.find_component_on(model) if v.is_indexed(): raise RuntimeError('Indexed variables not supported') @@ -78,16 +76,16 @@ def _apply_to(self, model, **kwds): assert len(w_estimates) == nz w_vals = w_estimates - model._z = aml.Param(range(nz), initialize=0.0, mutable=True) - model._w = aml.Param(range(nz), initialize=0.0, mutable=True) + model._z = pyo.Param(range(nz), initialize=0.0, mutable=True) + model._w = pyo.Param(range(nz), initialize=0.0, mutable=True) for i in range(nz): model._z[i].value = z_vals[i] model._w[i].value = w_vals[i] - model._rho = aml.Param(initialize=rho, mutable=True) + model._rho = pyo.Param(initialize=rho, mutable=True) # defines objective - objectives = model.component_map(aml.Objective, active=True) + objectives = model.component_map(pyo.Objective, active=True) if len(objectives) > 1: raise RuntimeError('Multiple objectives not supported') obj = list(objectives.values())[0] @@ -95,7 +93,8 @@ def _apply_to(self, model, **kwds): def rule_linkin_exprs(m, i): return cloned_vars[i] - m._z[i] # store non-anticipativity expression - model._linking_residuals = aml.Expression(range(nz), rule=rule_linkin_exprs) + model._linking_residuals = pyo.Expression(range(nz), + rule=rule_linkin_exprs) dual_term = 0.0 penalty_term = 0.0 @@ -104,11 +103,11 @@ def rule_linkin_exprs(m, i): penalty_term += (model._linking_residuals[zid])**2 # multiplier terms in objective - model._dual_obj_term = aml.Expression(expr=dual_term) + model._dual_obj_term = pyo.Expression(expr=dual_term) # penalty term - model._penalty_obj_term = aml.Expression(expr=0.5 * model._rho * penalty_term) + model._penalty_obj_term = pyo.Expression(expr=0.5 * model._rho * penalty_term) - model._aug_obj = aml.Objective(expr=obj.expr + + model._aug_obj = pyo.Objective(expr=obj.expr + model._dual_obj_term + model._penalty_obj_term) @@ -116,11 +115,12 @@ def rule_linkin_exprs(m, i): def propagate_solution(self, augmented_model, original_model): - for avar in augmented_model.component_objects(ctype=aml.Var, descend_into=True): - cuid = aml.ComponentUID(avar) + for avar in augmented_model.component_objects(ctype=pyo.Var, + descend_into=True): + cuid = pyo.ComponentUID(avar) original_v = cuid.find_component_on(original_model) for k in avar: - original_v[k].value = aml.value(avar[k]) + original_v[k].value = pyo.value(avar[k]) def create_basic_dense_qp(G, A, b, c): @@ -128,11 +128,11 @@ def create_basic_dense_qp(G, A, b, c): nx = G.shape[0] nl = A.shape[0] - model = aml.ConcreteModel() + model = pyo.ConcreteModel() model.var_ids = range(nx) model.con_ids = range(nl) - model.x = aml.Var(model.var_ids, initialize=0.0) + model.x = pyo.Var(model.var_ids, initialize=0.0) model.hessian_f = G model.jacobian_c = A model.rhs = b @@ -140,7 +140,8 @@ def create_basic_dense_qp(G, A, b, c): def equality_constraint_rule(m, i): return sum(m.jacobian_c[i, j] * m.x[j] for j in m.var_ids) == m.rhs[i] - model.equalities = aml.Constraint(model.con_ids, rule=equality_constraint_rule) + model.equalities = pyo.Constraint(model.con_ids, + rule=equality_constraint_rule) def objective_rule(m): accum = 0.0 @@ -150,44 +151,44 @@ def objective_rule(m): accum += sum(m.x[j] * m.grad_f[j] for j in m.var_ids) return accum - model.obj = aml.Objective(rule=objective_rule, sense=aml.minimize) + model.obj = pyo.Objective(rule=objective_rule, sense=pyo.minimize) return model def create_model2(): - model = aml.ConcreteModel() + model = pyo.ConcreteModel() model.indices = [i for i in range(1, 6)] - model.x = aml.Var(model.indices, initialize=2) + model.x = pyo.Var(model.indices, initialize=2) def rule_obj(m): expr = (m.x[1] - m.x[2]) ** 2 + (m.x[2] + m.x[3] - 2) ** 2 + (m.x[4] - 1) ** 2 + (m.x[5] - 1) **2 return expr - model.obj = aml.Objective(rule=rule_obj) + model.obj = pyo.Objective(rule=rule_obj) - model.c1 = aml.Constraint(expr=model.x[1] + 3 * model.x[2] == 0.0) - model.c2 = aml.Constraint(expr=model.x[3] + model.x[4] - 2 * model.x[5] == 0.0) - model.c3 = aml.Constraint(expr=model.x[2] - model.x[5] == 0.0) + model.c1 = pyo.Constraint(expr=model.x[1] + 3 * model.x[2] == 0.0) + model.c2 = pyo.Constraint(expr=model.x[3] + model.x[4] - 2 * model.x[5] == 0.0) + model.c3 = pyo.Constraint(expr=model.x[2] - model.x[5] == 0.0) return model def create_basic_model(): - m = aml.ConcreteModel() - m.x = aml.Var([1, 2, 3], domain=aml.Reals) + m = pyo.ConcreteModel() + m.x = pyo.Var([1, 2, 3], domain=pyo.Reals) for i in range(1, 4): m.x[i].value = i - m.c1 = aml.Constraint(expr=m.x[1] ** 2 - m.x[2] - 1 == 0) - m.c2 = aml.Constraint(expr=m.x[1] - m.x[3] - 0.5 == 0) - m.d1 = aml.Constraint(expr=m.x[1] + m.x[2] <= 100.0) - m.d2 = aml.Constraint(expr=m.x[2] + m.x[3] >= -100.0) - m.d3 = aml.Constraint(expr=m.x[2] + m.x[3] + m.x[1] >= -500.0) + m.c1 = pyo.Constraint(expr=m.x[1] ** 2 - m.x[2] - 1 == 0) + m.c2 = pyo.Constraint(expr=m.x[1] - m.x[3] - 0.5 == 0) + m.d1 = pyo.Constraint(expr=m.x[1] + m.x[2] <= 100.0) + m.d2 = pyo.Constraint(expr=m.x[2] + m.x[3] >= -100.0) + m.d3 = pyo.Constraint(expr=m.x[2] + m.x[3] + m.x[1] >= -500.0) m.x[2].setlb(0.0) m.x[3].setlb(0.0) m.x[2].setub(100.0) - m.obj = aml.Objective(expr=m.x[1]**2 + m.x[2]**2 + m.x[3]**2) + m.obj = pyo.Objective(expr=m.x[1]**2 + m.x[2]**2 + m.x[3]**2) return m diff --git a/pyomo/contrib/pynumero/interfaces/ampl_nlp.py b/pyomo/contrib/pynumero/interfaces/ampl_nlp.py index 12c612088ed..5aafaedccd7 100644 --- a/pyomo/contrib/pynumero/interfaces/ampl_nlp.py +++ b/pyomo/contrib/pynumero/interfaces/ampl_nlp.py @@ -362,13 +362,13 @@ def create_new_vector(self, vector_type): numpy.ndarray """ if vector_type == 'primals': - return np.zeros(self._n_primals, dtype=np.float64) + return np.zeros(self.n_primals(), dtype=np.float64) elif vector_type == 'constraints' or vector_type == 'duals': - return np.zeros(self._n_con_full, dtype=np.float64) + return np.zeros(self.n_constraints(), dtype=np.float64) elif vector_type == 'eq_constraints' or vector_type == 'duals_eq': - return np.zeros(self._n_con_eq, dtype=np.float64) + return np.zeros(self.n_eq_constraints(), dtype=np.float64) elif vector_type == 'ineq_constraints' or vector_type == 'duals_ineq': - return np.zeros(self._n_con_ineq, dtype=np.float64) + return np.zeros(self.n_ineq_constraints(), dtype=np.float64) else: raise RuntimeError('Called create_new_vector with an unknown vector_type') @@ -440,14 +440,16 @@ def get_constraints_scaling(self): def get_eq_constraints_scaling(self): constraints_scaling = self.get_constraints_scaling() if constraints_scaling is not None: - return np.compress(self._con_full_eq_mask, constraints_scaling) + return np.compress(self._con_full_eq_mask, + constraints_scaling) return None # overloaded from ExtendedNLP def get_ineq_constraints_scaling(self): constraints_scaling = self.get_constraints_scaling() if constraints_scaling is not None: - return np.compress(self._con_full_ineq_mask, constraints_scaling) + return np.compress(self._con_full_ineq_mask, + constraints_scaling) return None def _evaluate_objective_and_cache_if_necessary(self): diff --git a/pyomo/contrib/pynumero/interfaces/external_grey_box.py b/pyomo/contrib/pynumero/interfaces/external_grey_box.py new file mode 100644 index 00000000000..80c72394215 --- /dev/null +++ b/pyomo/contrib/pynumero/interfaces/external_grey_box.py @@ -0,0 +1,454 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ +import abc +import logging +import numpy as np +from scipy.sparse import coo_matrix + +from pyomo.common.timing import ConstructionTimer +from pyomo.core.base import Var, Constraint, value +from pyomo.core.base.block import _BlockData, Block, declare_custom_block +from pyomo.core.base.util import Initializer + +from ..sparse.block_matrix import BlockMatrix + +from six import add_metaclass, itervalues, iteritems +from six.moves import xrange + +logger = logging.getLogger('pyomo.contrib.pynumero') + +""" +This module is used for interfacing an external model as +a block in a Pyomo model. + +An ExternalGreyBoxModel is model is a model that does not +provide constraints explicitly as algebraic expressions, but +instead provides a set of methods that can compute the residuals +of the constraints (or outputs) and their derivatives. + +This allows one to interface external codes (e.g., compiled +external models) with a Pyomo model. + +Note: To solve a Pyomo model that contains these external models + we have a specialized interface built on PyNumero that provides + an interface to the CyIpopt solver. + +To use this interface: + * Create a class that is derived from ExternalGreyBoxModel and + implement the necessary methods. This derived class must provide + a list of names for: the inputs to your model, the equality constraints + (or residuals) that need to be converged, and any outputs that + are computed from your model. It will also need to provide methods to + compute the residuals, outputs, and the jacobian of these with respect to + the inputs. See the documentation on ExternalGreyBoxModel for more details. + + * Create a Pyomo model and make use of the ExternalGreyBoxBlock + to produce a Pyomo modeling component that represents your + external model. This block is a Pyomo component, and when you + call set_external_model() and provide an instance of your derived + ExternalGreyBoxModel, it will automatically create pyomo variables to + represent the inputs and the outputs from the external model. You + can implement a callback to modify the Pyomo block after it is + constructed. This also provides a mechanism to initalize variables, + etc. + + * Create a PyomoGreyBoxNLP and provide it with the Pyomo model + that contains the ExternalGreyBoxBlocks. This class presents + an NLP interface (i.e., the PyNumero NLP abstract class), and + can be used with any solver that makes use of this interface + (e.g., the CyIpopt solver interface provided in PyNumero) + +See pyomo/contrib/pynumero/examples/external_grey_box for examples +of the use of this interface. + +Note: + + * Currently, you cannot "fix" a pyomo variable that corresponds to an + input or output and you must use a constraint instead (this is + because Pyomo removes fixed variables before sending them to the + solver) + +""" + +@add_metaclass(abc.ABCMeta) +class ExternalGreyBoxModel(object): + """ + This is the base class for building external input output models + for use with Pyomo and CyIpopt. See the module documentation above, + and documentation of individual methods as well as examples. + """ + def n_inputs(self): + """ This method returns the number of inputs. You do not + need to overload this method in derived classes. + """ + return len(self.input_names()) + + def n_equality_constraints(self): + """ This method returns the number of equality constraints. + You do not need to overload this method in derived classes. + """ + return len(self.equality_constraint_names()) + + def n_outputs(self): + """ This method returns the number of outputs. You do not + need to overload this method in derived classes. + """ + return len(self.output_names()) + + @abc.abstractmethod + def input_names(self): + """ + Provide the list of string names to corresponding to the inputs + of this external model. These should be returned in the same order + that they are to be used in set_input_values. + """ + pass + + def equality_constraint_names(self): + """ + Provide the list of string names corresponding to any residuals + for this external model. These should be in the order corresponding + to values returned from evaluate_residuals. Return an empty list + if there are no equality constraints. + """ + return [] + + def output_names(self): + """ + Provide the list of string names corresponding to the outputs + of this external model. These should be in the order corresponding + to values returned from evaluate_outputs. Return an empty list if there + are no computed outputs. + """ + return [] + + def finalize_block_construction(self, pyomo_block): + """ + Implement this callback to provide any additional + specifications to the Pyomo block that is created + to represent this external grey box model. + + Note that pyomo_block.inputs and pyomo_block.outputs + have been created, and this callback provides an + opportunity to set initial values, bounds, etc. + """ + pass + + @abc.abstractmethod + def set_input_values(self, input_values): + """ + This method is called by the solver to set the current values + for the input variables. The derived class must cache these if + necessary for any subsequent calls to evalute_outputs or + evaluate_derivatives. + """ + pass + + def get_equality_constraint_scaling_factors(self): + """ + This method is called by the solver interface to get desired + values for scaling the equality constraints. None means no + scaling is desired. Note that, depending on the solver, + one may need to set solver options so these factors are used + """ + return None + + def get_output_constraint_scaling_factors(self): + """ + This method is called by the solver interface to get desired + values for scaling the constraints with output variables. Returning + None means that no scaling of the output constraints is desired. + Note that, depending on the solver, one may need to set solver options + so these factors are used + """ + return None + + def evaluate_equality_constraints(self): + """ + Compute the residuals from the model (using the values + set in input_values) and return as a numpy array + """ + raise NotImplementedError() + + def evaluate_outputs(self): + """ + Compute the outputs from the model (using the values + set in input_values) and return as a numpy array + """ + raise NotImplementedError() + + def evaluate_jacobian_equality_constraints(self): + """ + Compute the derivatives of the residuals with respect + to the inputs (using the values set in input_values). + This should be a scipy matrix with the rows in + the order of the residual names and the cols in + the order of the input variables. + """ + raise NotImplementedError() + + def evaluate_jacobian_outputs(self): + """ + Compute the derivatives of the outputs with respect + to the inputs (using the values set in input_values). + This should be a scipy matrix with the rows in + the order of the output variables and the cols in + the order of the input variables. + """ + raise NotImplementedError() + + # ToDo: Hessians not yet handled + + +class ExternalGreyBoxBlockData(_BlockData): + + def set_external_model(self, external_grey_box_model): + self._ex_model = ex_model = external_grey_box_model + if ex_model is None: + self._input_names = self._output_names = None + self.inputs = self.outputs = None + return + + self._input_names = ex_model.input_names() + if self._input_names is None or len(self._input_names) == 0: + raise ValueError( + 'No input_names specified for external_grey_box_model.' + ' Must specify at least one input.') + self.inputs = Var(self._input_names) + + self._equality_constraint_names = ex_model.equality_constraint_names() + self._output_names = ex_model.output_names() + + # Note, this works even if output_names is an empty list + self.outputs = Var(self._output_names) + + # call the callback so the model can set initialization, bounds, etc. + external_grey_box_model.finalize_block_construction(self) + + def get_external_model(self): + return self._ex_model + + +class ExternalGreyBoxBlock(Block): + def __new__(cls, *args, **kwds): + if cls != ExternalGreyBoxBlock: + target_cls = cls + elif not args or (args[0] is UnindexedComponent_set and len(args) == 1): + target_cls = SimpleExternalGreyBoxBlock + else: + target_cls = IndexedExternalGreyBoxBlock + return super(ExternalGreyBoxBlock, cls).__new__(target_cls) + + def __init__(self, *args, **kwds): + kwds.setdefault('ctype', ExternalGreyBoxBlock) + self._init_model = Initializer(kwds.pop('external_model', None)) + Block.__init__(self, *args, **kwds) + + def construct(self, data=None): + """ + Construct the ExternalGreyBoxBlockDatas + """ + if self._constructed: + return + # Do not set the constructed flag - Block.construct() will do that + + timer = ConstructionTimer(self) + if __debug__ and logger.isEnabledFor(logging.DEBUG): + logger.debug("Constructing external grey box model %s" + % (self.name)) + + super(ExternalGreyBoxBlock, self).construct(data) + + if self._init_model is not None: + block = self.parent_block() + for index, data in iteritems(self): + data.set_external_model(self._init_model(block, index)) + + +class SimpleExternalGreyBoxBlock(ExternalGreyBoxBlockData, ExternalGreyBoxBlock): + def __init__(self, *args, **kwds): + ExternalGreyBoxBlockData.__init__(self, component=self) + ExternalGreyBoxBlock.__init__(self, *args, **kwds) + + # Pick up the display() from Block and not BlockData + display = ExternalGreyBoxBlock.display + + +class IndexedExternalGreyBoxBlock(Block): + pass + + +class _ExternalGreyBoxModelHelper(object): + def __init__(self, ex_grey_box_block, vardata_to_idx, initial_primal_values): + """This helper takes an ExternalGreyBoxModel and provides the residual + and Jacobian computation. + + The ExternalGreyBoxModel provides an interface that supports + equality constraints (pure residuals) and output equations. Let + u be the inputs, o be the outputs, and x be the full set of + primal variables from the entire pyomo_nlp. + + With this, the ExternalGreyBoxModel provides the residual + computations w_eq(u), and w_o(u), as well as the Jacobians, + Jw_eq(u), and Jw_o(u). This helper provides h(x)=0, where h(x) = + [h_eq(x); h_o(x)-o] and h_eq(x)=w_eq(Pu*x), and + h_o(x)=w_o(Pu*x), and Pu is a mapping from the full primal + variables "x" to the inputs "u". + + It also provides the Jacobian of h w.r.t. x. + J_h(x) = [Jw_eq(Pu*x); Jw_o(Pu*x)-Po*x] + where Po is a mapping from the full primal variables "x" to the + outputs "o". + + """ + self._block = ex_grey_box_block + self._ex_model = ex_grey_box_block.get_external_model() + self._n_primals = len(initial_primal_values) + n_inputs = len(self._block.inputs) + n_outputs = len(self._block.outputs) + + # store the map of input indices (0 .. n_inputs) to + # the indices in the full primals vector + self._inputs_to_primals_map = np.fromiter( + (vardata_to_idx[v] for v in itervalues(self._block.inputs)), + dtype=np.int64, count=n_inputs) + + # store the map of output indices (0 .. n_outputs) to + # the indices in the full primals vector + self._outputs_to_primals_map = np.fromiter( + (vardata_to_idx[v] for v in itervalues(self._block.outputs)), + dtype=np.int64, count=n_outputs) + + # setup some structures for the jacobians + input_values = initial_primal_values[self._inputs_to_primals_map] + self._ex_model.set_input_values(input_values) + + if self._ex_model.n_outputs() == 0 and \ + self._ex_model.n_equality_constraints() == 0: + raise ValueError( + 'ExternalGreyBoxModel has no equality constraints ' + 'or outputs. It must have at least one or both.') + + # we need to change the column indices in the jacobian + # from the 0..n_inputs provided by the external model + # to the indices corresponding to the full Pyomo model + # so we create that here + self._eq_jac_primal_jcol = None + if self._ex_model.n_equality_constraints() > 0: + jac = self._ex_model.evaluate_jacobian_equality_constraints() + self._eq_jac_primal_jcol = self._inputs_to_primals_map[jac.col] + + self._outputs_jac_primal_jcol = None + if self._ex_model.n_outputs() > 0: + jac = self._ex_model.evaluate_jacobian_outputs() + self._outputs_jac_primal_jcol = self._inputs_to_primals_map[jac.col] + + # create the irow, jcol, nnz structure for the + # output variable portion of h(u)-o=0 + self._additional_output_entries_irow = np.asarray(xrange(n_outputs)) + self._additional_output_entries_jcol = self._outputs_to_primals_map + self._additional_output_entries_data = -1.0*np.ones(n_outputs) + + def set_primals(self, primals): + # map the full primals "x" to the inputs "u" and set + # the values on the external model + input_values = primals[self._inputs_to_primals_map] + self._ex_model.set_input_values(input_values) + + # map the full primals "x" to the outputs "o" and + # store a vector of the current output values to + # use when evaluating residuals + self._output_values = primals[self._outputs_to_primals_map] + + def n_residuals(self): + return self._ex_model.n_equality_constraints() \ + + self._ex_model.n_outputs() + + def get_residual_scaling(self): + eq_scaling = self._ex_model.get_equality_constraint_scaling_factors() + output_con_scaling = self._ex_model.get_output_constraint_scaling_factors() + if eq_scaling is None and output_con_scaling is None: + return None + if eq_scaling is None: + eq_scaling = np.ones(self._ex_model.n_equality_constraints()) + if output_con_scaling is None: + output_con_scaling = np.ones(self._ex_model.n_outputs()) + + return np.concatenate(( + eq_scaling, + output_con_scaling)) + + def evaluate_residuals(self): + # evalute the equality constraints and the output equations + # and return a single vector of residuals + # returns residual for h(x)=0, where h(x) = [h_eq(x); h_o(x)-o] + resid_list = [] + if self._ex_model.n_equality_constraints() > 0: + resid_list.append(self._ex_model.evaluate_equality_constraints()) + + if self._ex_model.n_outputs() > 0: + computed_output_values = self._ex_model.evaluate_outputs() + output_resid = computed_output_values - self._output_values + resid_list.append(output_resid) + + return np.concatenate(resid_list) + + def evaluate_jacobian(self): + # compute the jacobian of h(x) w.r.t. x + # J_h(x) = [Jw_eq(Pu*x); Jw_o(Pu*x)-Po*x] + + # Jw_eq(x) + eq_jac = None + if self._ex_model.n_equality_constraints() > 0: + eq_jac = self._ex_model.evaluate_jacobian_equality_constraints() + # map the columns from the inputs "u" back to the full primals "x" + eq_jac = coo_matrix( + (eq_jac.data, (eq_jac.row, self._eq_jac_primal_jcol)), + (eq_jac.shape[0], self._n_primals)) + + outputs_jac = None + if self._ex_model.n_outputs() > 0: + outputs_jac = self._ex_model.evaluate_jacobian_outputs() + + row = outputs_jac.row + # map the columns from the inputs "u" back to the full primals "x" + col = self._outputs_jac_primal_jcol + data = outputs_jac.data + + # add the additional entries for the -Po*x portion of the jacobian + row = np.concatenate((row, self._additional_output_entries_irow)) + col = np.concatenate((col, self._additional_output_entries_jcol)) + data = np.concatenate((data, self._additional_output_entries_data)) + outputs_jac = coo_matrix( + (data, (row, col)), + shape=(outputs_jac.shape[0], self._n_primals)) + + jac = None + if eq_jac is not None: + if outputs_jac is not None: + # create a jacobian with both Jw_eq and Jw_o + jac = BlockMatrix(2,1) + jac.name = 'external model jacobian' + jac.set_block(0,0,eq_jac) + jac.set_block(1,0,outputs_jac) + else: + assert self._ex_model.n_outputs() == 0 + assert self._ex_model.n_equality_constraints() > 0 + # only need the Jacobian with Jw_eq (there are not + # output equations) + jac = eq_jac + else: + assert outputs_jac is not None + assert self._ex_model.n_outputs() > 0 + assert self._ex_model.n_equality_constraints() == 0 + # only need the Jacobian with Jw_o (there are no equalities) + jac = outputs_jac + + return jac diff --git a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py index 5a4b0b4c8e9..09283e3f0a3 100644 --- a/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py +++ b/pyomo/contrib/pynumero/interfaces/pyomo_nlp.py @@ -18,12 +18,15 @@ from scipy.sparse import coo_matrix -import pyutilib -import pyomo -import pyomo.environ as aml +from pyutilib.services import TempfileManager +from pyomo.opt import WriterFactory +import pyomo.core.base as pyo from pyomo.common.collections import ComponentMap from pyomo.common.env import CtypesEnviron +from ..sparse.block_matrix import BlockMatrix from pyomo.contrib.pynumero.interfaces.ampl_nlp import AslNLP +from pyomo.contrib.pynumero.interfaces.nlp import NLP +from .external_grey_box import ExternalGreyBoxBlock, _ExternalGreyBoxModelHelper __all__ = ['PyomoNLP'] @@ -39,27 +42,36 @@ def __init__(self, pyomo_model): pyomo_model: pyomo.environ.ConcreteModel Pyomo concrete model """ - pyutilib.services.TempfileManager.push() + TempfileManager.push() try: # get the temp file names for the nl file - nl_file = pyutilib.services.TempfileManager.create_tempfile(suffix='pynumero.nl') - - # The current AmplInterface code only supports a single objective function - # Therefore, we throw an error if there is not one (and only one) active - # objective function. This is better than adding a dummy objective that the - # user does not know about (since we do nnot have a good place to remove - # this objective later - # TODO: extend the AmplInterface and the AslNLP to correctly handle this + nl_file = TempfileManager.create_tempfile( + suffix='pynumero.nl') + + # The current AmplInterface code only supports a single + # objective function Therefore, we throw an error if there + # is not one (and only one) active objective function. This + # is better than adding a dummy objective that the user does + # not know about (since we do not have a good place to + # remove this objective later) + # + # TODO: extend the AmplInterface and the AslNLP to correctly + # handle this + # # This currently addresses issue #1217 - objectives = list(pyomo_model.component_data_objects(ctype=aml.Objective, active=True, descend_into=True)) + objectives = list(pyomo_model.component_data_objects( + ctype=pyo.Objective, active=True, descend_into=True)) if len(objectives) != 1: - raise NotImplementedError('The ASL interface and PyomoNLP in PyNumero currently only support single objective' - ' problems. Deactivate any extra objectives you may have, or add a dummy objective' - ' (f(x)=0) if you have a square problem.') + raise NotImplementedError( + 'The ASL interface and PyomoNLP in PyNumero currently ' + 'only support single objective problems. Deactivate ' + 'any extra objectives you may have, or add a dummy ' + 'objective (f(x)=0) if you have a square problem.') self._objective = objectives[0] # write the nl file for the Pyomo model and get the symbolMap - fname, symbolMap = pyomo.opt.WriterFactory('nl')(pyomo_model, nl_file, lambda x:True, {}) + fname, symbolMap = WriterFactory('nl')( + pyomo_model, nl_file, lambda x:True, {}) # create component maps from vardata to idx and condata to idx self._vardata_to_idx = vdidx = ComponentMap() @@ -88,7 +100,8 @@ def __init__(self, pyomo_model): finally: # delete the nl file - pyutilib.services.TempfileManager.pop() + TempfileManager.pop() + def pyomo_model(self): """ @@ -127,7 +140,7 @@ def variable_names(self): names in the order corresponding to the primals """ pyomo_variables = self.get_pyomo_variables() - return [v.getname() for v in pyomo_variables] + return [v.getname(fully_qualified=True) for v in pyomo_variables] def constraint_names(self): """ @@ -135,7 +148,7 @@ def constraint_names(self): names in the order corresponding to internal constraint order """ pyomo_constraints = self.get_pyomo_constraints() - return [v.getname() for v in pyomo_constraints] + return [v.getname(fully_qualified=True) for v in pyomo_constraints] def get_primal_indices(self, pyomo_variables): """ @@ -183,15 +196,16 @@ def get_constraint_indices(self, pyomo_constraints): def get_obj_scaling(self): obj = self.get_pyomo_objective() scaling_suffix = self._pyomo_model.component('scaling_factor') - if scaling_suffix and scaling_suffix.ctype is aml.Suffix and \ - obj in scaling_suffix: - return scaling_suffix[obj] + if scaling_suffix and scaling_suffix.ctype is pyo.Suffix: + if obj in scaling_suffix: + return scaling_suffix[obj] + return 1.0 return None # overloaded from NLP def get_primals_scaling(self): scaling_suffix = self._pyomo_model.component('scaling_factor') - if scaling_suffix and scaling_suffix.ctype is aml.Suffix: + if scaling_suffix and scaling_suffix.ctype is pyo.Suffix: primals_scaling = np.ones(self.n_primals()) for i,v in enumerate(self.get_pyomo_variables()): if v in scaling_suffix: @@ -202,7 +216,7 @@ def get_primals_scaling(self): # overloaded from NLP def get_constraints_scaling(self): scaling_suffix = self._pyomo_model.component('scaling_factor') - if scaling_suffix and scaling_suffix.ctype is aml.Suffix: + if scaling_suffix and scaling_suffix.ctype is pyo.Suffix: constraints_scaling = np.ones(self.n_constraints()) for i,c in enumerate(self.get_pyomo_constraints()): if c in scaling_suffix: @@ -296,3 +310,620 @@ def extract_submatrix_hessian_lag(self, pyomo_variables_rows, pyomo_variables_co submatrix_jcols[i] = submatrix_map[v] return coo_matrix((submatrix_data, (submatrix_irows, submatrix_jcols)), shape=(len(primal_indices_rows), len(primal_indices_cols))) + + def load_state_into_pyomo(self, bound_multipliers=None): + primals = self.get_primals() + variables = self.get_pyomo_variables() + for var, val in zip(variables, primals): + var.set_value(val) + m = self.pyomo_model() + model_suffixes = dict( + pyo.suffix.active_import_suffix_generator(m)) + if 'dual' in model_suffixes: + duals = self.get_duals() + constraints = self.get_pyomo_constraints() + model_suffixes['dual'].clear() + model_suffixes['dual'].update( + zip(constraints, duals)) + if 'ipopt_zL_out' in model_suffixes: + model_suffixes['ipopt_zL_out'].clear() + if bound_multipliers is not None: + model_suffixes['ipopt_zL_out'].update( + zip(variables, bound_multipliers[0])) + if 'ipopt_zU_out' in model_suffixes: + model_suffixes['ipopt_zU_out'].clear() + if bound_multipliers is not None: + model_suffixes['ipopt_zU_out'].update( + zip(variables, bound_multipliers[1])) + + +class PyomoGreyBoxNLP(NLP): + def __init__(self, pyomo_model): + # store all the greybox custom block data objects + greybox_components = [] + try: + # We support Pynumero's ExternalGreyBoxBlock modeling + # objects. We need to find them and convert them to Blocks + # before calling the NL writer so that the attached Vars get + # picked up by the writer. + for greybox in pyomo_model.component_objects( + ExternalGreyBoxBlock, descend_into=True): + greybox.parent_block().reclassify_component_type( + greybox, pyo.Block) + greybox_components.append(greybox) + + self._pyomo_model = pyomo_model + self._pyomo_nlp = PyomoNLP(pyomo_model) + + finally: + # Restore the ctypes of the ExternalGreyBoxBlock components + for greybox in greybox_components: + greybox.parent_block().reclassify_component_type( + greybox, ExternalGreyBoxBlock) + + # get the greybox block data objects + greybox_data = [] + for greybox in greybox_components: + greybox_data.extend(data for data in greybox.values() + if data.active) + + if len(greybox_data) > 1: + raise NotImplementedError("The PyomoGreyBoxModel interface has not" + " been tested with Pyomo models that contain" + " more than one ExternalGreyBoxBlock. Currently," + " only a single block is supported.") + + if self._pyomo_nlp.n_primals() == 0: + raise ValueError( + "No variables were found in the Pyomo part of the model." + " PyomoGreyBoxModel requires at least one variable" + " to be active in a Pyomo objective or constraint") + + # number of additional variables required - they are in the + # greybox models but not included in the NL file + self._n_greybox_primals = 0 + + # number of residuals (equality constraints + output constraints + # coming from the grey box models + self._greybox_primals_names = [] + self._greybox_constraints_names = [] + + # Update the primal index map with any variables in the + # greybox models that do not otherwise appear in the NL + # and capture some other book keeping items + n_primals = self._pyomo_nlp.n_primals() + greybox_primals = [] + self._vardata_to_idx = ComponentMap(self._pyomo_nlp._vardata_to_idx) + for data in greybox_data: + # check that none of the inputs / outputs are fixed + for v in six.itervalues(data.inputs): + if v.fixed: + raise NotImplementedError('Found a grey box model input that is fixed: {}.' + ' This interface does not currently support fixed' + ' variables. Please add a constraint instead.' + ''.format(v.getname(fully_qualified=True))) + for v in six.itervalues(data.outputs): + if v.fixed: + raise NotImplementedError('Found a grey box model output that is fixed: {}.' + ' This interface does not currently support fixed' + ' variables. Please add a constraint instead.' + ''.format(v.getname(fully_qualified=True))) + + block_name = data.getname() + for nm in data._ex_model.equality_constraint_names(): + self._greybox_constraints_names.append('{}.{}'.format(block_name, nm)) + for nm in data._ex_model.output_names(): + self._greybox_constraints_names.append('{}.{}_con'.format(block_name, nm)) + + for var in data.component_data_objects(pyo.Var): + if var not in self._vardata_to_idx: + # there is a variable in the greybox block that + # is not in the NL - append this to the end + self._vardata_to_idx[var] = n_primals + n_primals += 1 + greybox_primals.append(var) + self._greybox_primals_names.append(var.getname(fully_qualified=True)) + self._n_greybox_primals = len(greybox_primals) + self._greybox_primal_variables = greybox_primals + + # Configure the primal and dual data caches + self._greybox_primals_lb = np.zeros(self._n_greybox_primals) + self._greybox_primals_ub = np.zeros(self._n_greybox_primals) + self._init_greybox_primals = np.zeros(self._n_greybox_primals) + for i, var in enumerate(greybox_primals): + if var.value is not None: + self._init_greybox_primals[i] = var.value + self._greybox_primals_lb[i] = -np.inf if var.lb is None else var.lb + self._greybox_primals_ub[i] = np.inf if var.ub is None else var.ub + self._greybox_primals_lb.flags.writeable = False + self._greybox_primals_ub.flags.writeable = False + + self._greybox_primals = self._init_greybox_primals.copy() + + # data member to store the cached greybox constraints and jacobian + self._cached_greybox_constraints = None + self._cached_greybox_jac = None + + # Now that we know the total number of columns, create the + # necessary greybox helper objects + self._external_greybox_helpers = \ + [_ExternalGreyBoxModelHelper(data, self._vardata_to_idx, self.init_primals()) for data in greybox_data] + + # make sure the primal values get to the greybox models + self.set_primals(self.get_primals()) + + self._n_greybox_constraints = 0 + for h in self._external_greybox_helpers: + self._n_greybox_constraints += h.n_residuals() + assert len(self._greybox_constraints_names) == self._n_greybox_constraints + + # If any part of the problem is scaled (i.e., obj, primals, + # or any of the constraints for any of the external grey boxes), + # then we want scaling factors for everything (defaulting to + # ones for any of the missing factors). + # This code builds all the scaling factors, with the defaults, + # but then sets them all to None if *no* scaling factors are provided + # for any of the pieces. (inefficient, but only done once) + need_scaling = False + self._obj_scaling = self._pyomo_nlp.get_obj_scaling() + if self._obj_scaling is None: + self._obj_scaling = 1.0 + else: + need_scaling = True + + self._primals_scaling = np.ones(self.n_primals()) + scaling_suffix = self._pyomo_nlp._pyomo_model.component('scaling_factor') + if scaling_suffix and scaling_suffix.ctype is pyo.Suffix: + need_scaling = True + for i,v in enumerate(self.get_pyomo_variables()): + if v in scaling_suffix: + self._primals_scaling[i] = scaling_suffix[v] + + self._constraints_scaling = [] + pyomo_nlp_scaling = self._pyomo_nlp.get_constraints_scaling() + if pyomo_nlp_scaling is None: + pyomo_nlp_scaling = np.ones(self._pyomo_nlp.n_constraints()) + else: + need_scaling = True + self._constraints_scaling.append(pyomo_nlp_scaling) + + for h in self._external_greybox_helpers: + tmp_scaling = h.get_residual_scaling() + if tmp_scaling is None: + tmp_scaling = np.ones(h.n_residuals()) + else: + need_scaling = True + self._constraints_scaling.append(tmp_scaling) + + if need_scaling: + self._constraints_scaling = np.concatenate(self._constraints_scaling) + else: + self._obj_scaling = None + self._primals_scaling = None + self._constraints_scaling = None + + # might want the user to be able to specify these at some point + self._init_greybox_duals = np.ones(self._n_greybox_constraints) + self._init_greybox_primals.flags.writeable = False + self._init_greybox_duals.flags.writeable = False + self._greybox_duals = self._init_greybox_duals.copy() + + # compute the jacobian for the external greybox models + # to get some of the statistics + self._evaluate_greybox_jacobians_and_cache_if_necessary() + self._nnz_greybox_jac = len(self._cached_greybox_jac.data) + + def _invalidate_greybox_primals_cache(self): + self._greybox_constraints_cached = False + self._greybox_jac_cached = False + + # overloaded from NLP + def n_primals(self): + return self._pyomo_nlp.n_primals() + self._n_greybox_primals + + # overloaded from NLP + def n_constraints(self): + return self._pyomo_nlp.n_constraints() + self._n_greybox_constraints + + # overloaded from ExtendedNLP + def n_eq_constraints(self): + return self._pyomo_nlp.n_eq_constraints() + self._n_greybox_constraints + + # overloaded from ExtendedNLP + def n_ineq_constraints(self): + return self._pyomo_nlp.n_ineq_constraints() + + # overloaded from NLP + def nnz_jacobian(self): + return self._pyomo_nlp.nnz_jacobian() + self._nnz_greybox_jac + + # overloaded from AslNLP + def nnz_jacobian_eq(self): + return self._pyomo_nlp.nnz_jacobian_eq() + self._nnz_greybox_jac + + # overloaded from NLP + def nnz_hessian_lag(self): + raise NotImplementedError( + "PyomoGreyBoxNLP does not currently support Hessians") + + # overloaded from NLP + def primals_lb(self): + return np.concatenate((self._pyomo_nlp.primals_lb(), + self._greybox_primals_lb, + )) + + # overloaded from NLP + def primals_ub(self): + return np.concatenate(( + self._pyomo_nlp.primals_ub(), + self._greybox_primals_ub, + )) + + # overloaded from NLP + def constraints_lb(self): + return np.concatenate(( + self._pyomo_nlp.constraints_lb(), + np.zeros(self._n_greybox_constraints, dtype=np.float64), + )) + + # overloaded from NLP + def constraints_ub(self): + return np.concatenate(( + self._pyomo_nlp.constraints_ub(), + np.zeros(self._n_greybox_constraints, dtype=np.float64), + )) + + # overloaded from NLP + def init_primals(self): + return np.concatenate(( + self._pyomo_nlp.init_primals(), + self._init_greybox_primals, + )) + + # overloaded from NLP + def init_duals(self): + return np.concatenate(( + self._pyomo_nlp.init_duals(), + self._init_greybox_duals, + )) + + # overloaded from ExtendedNLP + def init_duals_eq(self): + return np.concatenate(( + self._pyomo_nlp.init_duals_eq(), + self._init_greybox_duals, + )) + + # overloaded from NLP / Extended NLP + def create_new_vector(self, vector_type): + if vector_type == 'primals': + return np.zeros(self.n_primals(), dtype=np.float64) + elif vector_type == 'constraints' or vector_type == 'duals': + return np.zeros(self.n_constraints(), dtype=np.float64) + elif vector_type == 'eq_constraints' or vector_type == 'duals_eq': + return np.zeros(self.n_eq_constraints(), dtype=np.float64) + elif vector_type == 'ineq_constraints' or vector_type == 'duals_ineq': + return np.zeros(self.n_ineq_constraints(), dtype=np.float64) + else: + raise RuntimeError('Called create_new_vector with an unknown vector_type') + + # overloaded from NLP + def set_primals(self, primals): + self._invalidate_greybox_primals_cache() + + # set the primals on the "pyomo" part of the nlp + self._pyomo_nlp.set_primals( + primals[:self._pyomo_nlp.n_primals()]) + + # copy the values for the greybox primals + np.copyto(self._greybox_primals, primals[self._pyomo_nlp.n_primals():]) + + for external in self._external_greybox_helpers: + external.set_primals(primals) + + # overloaded from AslNLP + def get_primals(self): + # return the value of the primals that the pyomo + # part knows about as well as any extra values that + # are only in the greybox part + return np.concatenate(( + self._pyomo_nlp.get_primals(), + self._greybox_primals, + )) + + # overloaded from NLP + def set_duals(self, duals): + #self._invalidate_greybox_duals_cache() + + # set the duals for the pyomo part of the nlp + self._pyomo_nlp.set_duals( + duals[:-self._n_greybox_constraints]) + + # set the duals for the greybox part of the nlp + np.copyto(self._greybox_duals, duals[-self._n_greybox_constraints:]) + + # overloaded from NLP + def get_duals(self): + # return the duals for the pyomo part of the nlp + # concatenated with the greybox part + return np.concatenate(( + self._pyomo_nlp.get_duals(), + self._greybox_duals, + )) + + # overloaded from ExtendedNLP + def set_duals_eq(self, duals): + #self._invalidate_greybox_duals_cache() + + # set the duals for the pyomo part of the nlp + self._pyomo_nlp.set_duals_eq( + duals[:-self._n_greybox_constraints]) + + # set the duals for the greybox part of the nlp + np.copyto(self._greybox_duals, duals[-self._n_greybox_constraints:]) + + # overloaded from NLP + def get_duals_eq(self): + # return the duals for the pyomo part of the nlp + # concatenated with the greybox part + return np.concatenate(( + self._pyomo_nlp.get_duals_eq(), + self._greybox_duals, + )) + + # overloaded from NLP + def set_obj_factor(self, obj_factor): + # objective is owned by the pyomo model + self._pyomo_nlp.set_obj_factor(obj_factor) + + # overloaded from NLP + def get_obj_factor(self): + # objective is owned by the pyomo model + return self._pyomo_nlp.get_obj_factor() + + # overloaded from NLP + def get_obj_scaling(self): + return self._obj_scaling + + # overloaded from NLP + def get_primals_scaling(self): + return self._primals_scaling + + # overloaded from NLP + def get_constraints_scaling(self): + return self._constraints_scaling + + # overloaded from NLP + def evaluate_objective(self): + # objective is owned by the pyomo model + return self._pyomo_nlp.evaluate_objective() + + # overloaded from NLP + def evaluate_grad_objective(self, out=None): + # objective is owned by the pyomo model + return np.concatenate(( + self._pyomo_nlp.evaluate_grad_objective(out), + np.zeros(self._n_greybox_primals))) + + def _evaluate_greybox_constraints_and_cache_if_necessary(self): + if self._greybox_constraints_cached: + return + + self._cached_greybox_constraints = np.concatenate(tuple( + external.evaluate_residuals() + for external in self._external_greybox_helpers)) + self._greybox_constraints_cached = True + + # overloaded from NLP + def evaluate_constraints(self, out=None): + self._evaluate_greybox_constraints_and_cache_if_necessary() + + if out is not None: + if not isinstance(out, np.ndarray) \ + or out.size != self.n_constraints(): + raise RuntimeError( + 'Called evaluate_constraints with an invalid' + ' "out" argument - should take an ndarray of ' + 'size {}'.format(self.n_constraints())) + + # call on the pyomo part of the nlp + self._pyomo_nlp.evaluate_constraints( + out[:-self._n_greybox_constraints]) + + # call on the greybox part of the nlp + np.copyto(out[-self._n_greybox_constraints:], + self._cached_greybox_constraints) + return out + + else: + # concatenate the pyomo and external constraint residuals + return np.concatenate(( + self._pyomo_nlp.evaluate_constraints(), + self._cached_greybox_constraints, + )) + + # overloaded from ExtendedNLP + def evaluate_eq_constraints(self, out=None): + self._evaluate_greybox_constraints_and_cache_if_necessary() + + if out is not None: + if not isinstance(out, np.ndarray) \ + or out.size != self.n_eq_constraints(): + raise RuntimeError( + 'Called evaluate_eq_constraints with an invalid' + ' "out" argument - should take an ndarray of ' + 'size {}'.format(self.n_eq_constraints())) + self._pyomo_nlp.evaluate_eq_constraints( + out[:-self._n_greybox_constraints]) + np.copyto(out[-self._n_greybox_constraints:], self._cached_greybox_constraints) + return out + else: + return np.concatenate(( + self._pyomo_nlp.evaluate_eq_constraints(), + self._cached_greybox_constraints, + )) + + def _evaluate_greybox_jacobians_and_cache_if_necessary(self): + if self._greybox_jac_cached: + return + + jac = BlockMatrix(len(self._external_greybox_helpers), 1) + for i, external in enumerate(self._external_greybox_helpers): + jac.set_block(i, 0, external.evaluate_jacobian()) + self._cached_greybox_jac = jac.tocoo() + self._greybox_jac_cached = True + + # overloaded from NLP + def evaluate_jacobian(self, out=None): + self._evaluate_greybox_jacobians_and_cache_if_necessary() + + if out is not None: + if ( not isinstance(out, coo_matrix) + or out.shape[0] != self.n_constraints() + or out.shape[1] != self.n_primals() + or out.nnz != self.nnz_jacobian() ): + raise RuntimeError( + 'evaluate_jacobian called with an "out" argument' + ' that is invalid. This should be a coo_matrix with' + ' shape=({},{}) and nnz={}' + .format(self.n_constraints(), self.n_primals(), + self.nnz_jacobian())) + n_pyomo_constraints = self.n_constraints() - self._n_greybox_constraints + self._pyomo_nlp.evaluate_jacobian( + out=coo_matrix((out.data[:-self._nnz_greybox_jac], + (out.row[:-self._nnz_greybox_jac], + out.col[:-self._nnz_greybox_jac])), + shape=(n_pyomo_constraints, self._pyomo_nlp.n_primals()))) + np.copyto(out.data[-self._nnz_greybox_jac:], + self._cached_greybox_jac.data) + return out + else: + base = self._pyomo_nlp.evaluate_jacobian() + base = coo_matrix((base.data, (base.row, base.col)), + shape=(base.shape[0], self.n_primals())) + + jac = BlockMatrix(2,1) + jac.set_block(0, 0, base) + jac.set_block(1, 0, self._cached_greybox_jac) + return jac.tocoo() + + # TODO: Doesn't this need a "shape" specification? + #return coo_matrix(( + # np.concatenate((base.data, self._cached_greybox_jac.data)), + # ( np.concatenate((base.row, self._cached_greybox_jac.row)), + # np.concatenate((base.col, self._cached_greybox_jac.col)) ) + #)) + + # overloaded from ExtendedNLP + """ + def evaluate_jacobian_eq(self, out=None): + raise NotImplementedError() + self._evaluate_greybox_jacobians_and_cache_if_necessary() + + if out is not None: + if ( not isinstance(out, coo_matrix) + or out.shape[0] != self.n_eq_constraints() + or out.shape[1] != self.n_primals() + or out.nnz != self.nnz_jacobian_eq() ): + raise RuntimeError( + 'evaluate_jacobian called with an "out" argument' + ' that is invalid. This should be a coo_matrix with' + ' shape=({},{}) and nnz={}' + .format(self.n_eq_constraints(), self.n_primals(), + self.nnz_jacobian_eq())) + self._pyomo_nlp.evaluate_jacobian_eq( + coo_matrix((out.data[:-self._nnz_greybox_jac], + (out.row[:-self._nnz_greybox_jac], + out.col[:-self._nnz_greybox_jac]))) + ) + np.copyto(out.data[-self._nnz_greybox_jac], + self._cached_greybox_jac.data) + return out + else: + base = self._pyomo_nlp.evaluate_jacobian_eq() + # TODO: Doesn't this need a "shape" specification? + return coo_matrix(( + np.concatenate((base.data, self._cached_greybox_jac.data)), + ( np.concatenate((base.row, self._cached_greybox_jac.row)), + np.concatenate((base.col, self._cached_greybox_jac.col)) ) + )) + """ + # overloaded from NLP + def evaluate_hessian_lag(self, out=None): + # return coo_matrix(([], ([],[])), shape=(self.n_primals(), self.n_primals())) + raise NotImplementedError( + "PyomoGreyBoxNLP does not currently support Hessians") + + # overloaded from NLP + def report_solver_status(self, status_code, status_message): + raise NotImplementedError('Todo: implement this') + + def variable_names(self): + names = list(self._pyomo_nlp.variable_names()) + names.extend(self._greybox_primals_names) + return names + + def constraint_names(self): + names = list(self._pyomo_nlp.constraint_names()) + names.extend(self._greybox_constraints_names) + return names + + def pyomo_model(self): + """ + Return optimization model + """ + return self._pyomo_model + + def get_pyomo_objective(self): + """ + Return an instance of the active objective function on the Pyomo model. + (there can be only one) + """ + return self._pyomo_nlp.get_pyomo_objective() + + def get_pyomo_variables(self): + """ + Return an ordered list of the Pyomo VarData objects in + the order corresponding to the primals + """ + return self._pyomo_nlp.get_pyomo_variables() + \ + self._greybox_primal_variables + + def get_pyomo_constraints(self): + """ + Return an ordered list of the Pyomo ConData objects in + the order corresponding to the primals + """ + # FIXME: what do we return for the external block constraints? + # return self._pyomo_nlp.get_pyomo_constraints() + raise NotImplementedError( + "returning list of all constraints when using an external " + "model is TBD") + + def load_state_into_pyomo(self, bound_multipliers=None): + primals = self.get_primals() + variables = self.get_pyomo_variables() + for var, val in zip(variables, primals): + var.set_value(val) + m = self.pyomo_model() + model_suffixes = dict( + pyo.suffix.active_import_suffix_generator(m)) + if 'dual' in model_suffixes: + model_suffixes['dual'].clear() + # Until we sort out how to return the duals for the external + # block (implied) constraints, I am disabling *all* duals + # + # duals = self.get_duals() + # constraints = self.get_pyomo_constraints() + # model_suffixes['dual'].update( + # zip(constraints, duals)) + if 'ipopt_zL_out' in model_suffixes: + model_suffixes['ipopt_zL_out'].clear() + if bound_multipliers is not None: + model_suffixes['ipopt_zL_out'].update( + zip(variables, bound_multipliers[0])) + if 'ipopt_zU_out' in model_suffixes: + model_suffixes['ipopt_zU_out'].clear() + if bound_multipliers is not None: + model_suffixes['ipopt_zU_out'].update( + zip(variables, bound_multipliers[1])) diff --git a/pyomo/contrib/pynumero/interfaces/tests/test_external.py b/pyomo/contrib/pynumero/interfaces/tests/test_external_asl_function.py similarity index 93% rename from pyomo/contrib/pynumero/interfaces/tests/test_external.py rename to pyomo/contrib/pynumero/interfaces/tests/test_external_asl_function.py index 949a0458ca6..5d5d0bf8508 100644 --- a/pyomo/contrib/pynumero/interfaces/tests/test_external.py +++ b/pyomo/contrib/pynumero/interfaces/tests/test_external_asl_function.py @@ -20,9 +20,7 @@ "Pynumero needs the ASL extension to run NLP tests") from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP from pyomo.common.getGSL import find_GSL -from pyomo.environ import * -from pyomo.core.base.external import (PythonCallbackFunction, - AMPLExternalFunction) +from pyomo.environ import ConcreteModel, ExternalFunction, Var, Objective class TestAMPLExternalFunction(unittest.TestCase): def assertListsAlmostEqual(self, first, second, places=7, msg=None): diff --git a/pyomo/contrib/pynumero/interfaces/tests/test_external_grey_box_model.py b/pyomo/contrib/pynumero/interfaces/tests/test_external_grey_box_model.py new file mode 100644 index 00000000000..75b0bcef1aa --- /dev/null +++ b/pyomo/contrib/pynumero/interfaces/tests/test_external_grey_box_model.py @@ -0,0 +1,1494 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import os +import pyutilib.th as unittest +import pyomo.environ as pyo + +from pyomo.contrib.pynumero.dependencies import ( + numpy as np, numpy_available, scipy_sparse as spa, scipy_available +) +if not (numpy_available and scipy_available): + raise unittest.SkipTest("Pynumero needs scipy and numpy to run NLP tests") + +from pyomo.contrib.pynumero.asl import AmplInterface +if not AmplInterface.available(): + raise unittest.SkipTest( + "Pynumero needs the ASL extension to run CyIpoptSolver tests") + +from pyomo.contrib.pynumero.algorithms.solvers.cyipopt_solver import ( + CyIpoptSolver, CyIpoptNLP, ipopt, ipopt_available, +) + +from ..external_grey_box import ExternalGreyBoxModel, ExternalGreyBoxBlock, _ExternalGreyBoxModelHelper +from ..pyomo_nlp import PyomoGreyBoxNLP + +# set of external models for testing +# basic model is a simple pipe sequence with nonlinear pressure drop +# Pin -> P1 -> P2 -> P3 -> Pout +# +# We will assume that we have an external model to compute +# the pressure drop in this sequence of pipes, where the dP +# is given by c*F^2 +# +# There are several ways to format this. +# Model 1: Use the "external model" to compute the output pressure +# no equalities, 1 output +# u = [Pin, c, F] +# o = [Pout] +# h_eq(u) = {empty} +# h_o(u) = [Pin - 4*c*F^2] +# +# Model 2: Same as model 1, but treat Pout as an input to be converged by the optimizer +# 1 equality, no outputs +# u = [Pin, c, F, Pout] +# o = {empty} +# h_eq(u) = [Pout - (Pin - 4*c*F^2] +# h_o(u) = {empty} +# +# Model 3: Use the "external model" to compute the output pressure and the pressure +# at node 2 (e.g., maybe we have a measurement there we want to match) +# no equalities, 2 outputs +# u = [Pin, c, F] +# o = [P2, Pout] +# h_eq(u) = {empty} +# h_o(u) = [Pin - 2*c*F^2] +# [Pin - 4*c*F^2] +# +# Model 4: Same as model 2, but treat P2, and Pout as an input to be converged by the optimizer +# 2 equality, no outputs +# u = [Pin, c, F, P2, Pout] +# o = {empty} +# h_eq(u) = [P2 - (Pin - 2*c*F^2] +# [Pout - (P2 - 2*c*F^2] +# h_o(u) = {empty} + +# Model 4: Same as model 2, but treat P2 as an input to be converged by the solver +# u = [Pin, c, F, P2] +# o = [Pout] +# h_eq(u) = P2 - (Pin-2*c*F^2)] +# h_o(u) = [Pin - 4*c*F^2] (or could also be [P2 - 2*c*F^2]) +# +# Model 5: treat all "internal" variables as "inputs", equality and output equations +# u = [Pin, c, F, P1, P2, P3] +# o = [Pout] +# h_eq(u) = [ +# P1 - (Pin - c*F^2); +# P2 - (P1 - c*F^2); +# P3 - (P2 - c*F^2); +# ] +# h_o(u) = [P3 - c*F^2] (or could also be [Pin - 4*c*F^2] or [P1 - 3*c*F^2] or [P2 - 2*c*F^2]) +# +# Model 6: treat all variables as "inputs", equality only, and no output equations +# u = [Pin, c, F, P1, P2, P3, Pout] +# o = {empty} +# h_eq(u) = [ +# P1 - (Pin - c*F^2); +# P2 - (P1 - c*F^2); +# P3 - (P2 - c*F^2); +# Pout = (P3 - c*F^2); +# ] +# h_o(u) = {empty} +# +class PressureDropSingleOutput(ExternalGreyBoxModel): + def __init__(self): + self._input_names = ['Pin', 'c', 'F'] + self._input_values = np.zeros(3, dtype=np.float64) + self._output_names = ['Pout'] + + def input_names(self): + return self._input_names + + def equality_constraint_names(self): + return [] + + def output_names(self): + return self._output_names + + def set_input_values(self, input_values): + assert len(input_values) == 3 + np.copyto(self._input_values, input_values) + + def evaluate_equality_constraints(self): + raise NotImplementedError('This method should not be called for this model.') + + def evaluate_outputs(self): + Pin = self._input_values[0] + c = self._input_values[1] + F = self._input_values[2] + Pout = Pin - 4*c*F**2 + return np.asarray([Pout], dtype=np.float64) + + def evaluate_jacobian_equality_constraints(self): + raise NotImplementedError('This method should not be called for this model.') + + def evaluate_jacobian_outputs(self): + c = self._input_values[1] + F = self._input_values[2] + irow = np.asarray([0, 0, 0], dtype=np.int64) + jcol = np.asarray([0, 1, 2], dtype=np.int64) + nonzeros = np.asarray([1, -4*F**2, -4*c*2*F], dtype=np.float64) + jac = spa.coo_matrix((nonzeros, (irow, jcol)), shape=(1,3)) + return jac + +class PressureDropSingleEquality(ExternalGreyBoxModel): + # u = [Pin, c, F, Pout] + # o = {empty} + # h_eq(u) = [Pout - (Pin - 4*c*F^2] + # h_o(u) = {empty} + def __init__(self): + self._input_names = ['Pin', 'c', 'F', 'Pout'] + self._input_values = np.zeros(4, dtype=np.float64) + self._equality_constraint_names = ['pdrop'] + + def input_names(self): + return self._input_names + + def equality_constraint_names(self): + return self._equality_constraint_names + + def output_names(self): + return [] + + def set_input_values(self, input_values): + assert len(input_values) == 4 + np.copyto(self._input_values, input_values) + + def evaluate_equality_constraints(self): + Pin = self._input_values[0] + c = self._input_values[1] + F = self._input_values[2] + Pout = self._input_values[3] + return np.asarray([Pout - (Pin - 4*c*F**2)], dtype=np.float64) + + def evaluate_outputs(self): + raise NotImplementedError('This method should not be called for this model.') + + def evaluate_jacobian_equality_constraints(self): + c = self._input_values[1] + F = self._input_values[2] + irow = np.asarray([0, 0, 0, 0], dtype=np.int64) + jcol = np.asarray([0, 1, 2, 3], dtype=np.int64) + nonzeros = np.asarray([-1, 4*F**2, 4*2*c*F, 1], dtype=np.float64) + jac = spa.coo_matrix((nonzeros, (irow, jcol)), shape=(1,4)) + return jac + + def evaluate_jacobian_outputs(self): + raise NotImplementedError('This method should not be called for this model.') + +class PressureDropTwoOutputs(ExternalGreyBoxModel): + # u = [Pin, c, F] + # o = [P2, Pout] + # h_eq(u) = {empty} + # h_o(u) = [Pin - 2*c*F^2] + # [Pin - 4*c*F^2] + def __init__(self): + self._input_names = ['Pin', 'c', 'F'] + self._input_values = np.zeros(3, dtype=np.float64) + self._output_names = ['P2', 'Pout'] + + def input_names(self): + return self._input_names + + def equality_constraint_names(self): + return [] + + def output_names(self): + return self._output_names + + def set_input_values(self, input_values): + assert len(input_values) == 3 + np.copyto(self._input_values, input_values) + + def evaluate_equality_constraints(self): + raise NotImplementedError('This method should not be called for this model.') + + def evaluate_outputs(self): + Pin = self._input_values[0] + c = self._input_values[1] + F = self._input_values[2] + P2 = Pin - 2*c*F**2 + Pout = Pin - 4*c*F**2 + return np.asarray([P2, Pout], dtype=np.float64) + + def evaluate_jacobian_equality_constraints(self): + raise NotImplementedError('This method should not be called for this model.') + + def evaluate_jacobian_outputs(self): + c = self._input_values[1] + F = self._input_values[2] + irow = np.asarray([0, 0, 0, 1, 1, 1], dtype=np.int64) + jcol = np.asarray([0, 1, 2, 0, 1, 2], dtype=np.int64) + nonzeros = np.asarray([1, -2*F**2, -2*c*2*F, 1, -4*F**2, -4*c*2*F], dtype=np.float64) + jac = spa.coo_matrix((nonzeros, (irow, jcol)), shape=(2,3)) + return jac + +class PressureDropTwoEqualities(ExternalGreyBoxModel): + # u = [Pin, c, F, P2, Pout] + # o = {empty} + # h_eq(u) = [P2 - (Pin - 2*c*F^2] + # [Pout - (P2 - 2*c*F^2] + # h_o(u) = {empty} + def __init__(self): + self._input_names = ['Pin', 'c', 'F', 'P2', 'Pout'] + self._input_values = np.zeros(5, dtype=np.float64) + self._equality_constraint_names = ['pdrop2', 'pdropout'] + + def input_names(self): + return self._input_names + + def equality_constraint_names(self): + return self._equality_constraint_names + + def output_names(self): + return [] + + def set_input_values(self, input_values): + assert len(input_values) == 5 + np.copyto(self._input_values, input_values) + + def evaluate_equality_constraints(self): + Pin = self._input_values[0] + c = self._input_values[1] + F = self._input_values[2] + P2 = self._input_values[3] + Pout = self._input_values[4] + return np.asarray([P2 - (Pin - 2*c*F**2), Pout - (P2 - 2*c*F**2)], dtype=np.float64) + + def evaluate_outputs(self): + raise NotImplementedError('This method should not be called for this model.') + + def evaluate_jacobian_equality_constraints(self): + c = self._input_values[1] + F = self._input_values[2] + irow = np.asarray([0, 0, 0, 0, 1, 1, 1, 1], dtype=np.int64) + jcol = np.asarray([0, 1, 2, 3, 1, 2, 3, 4], dtype=np.int64) + nonzeros = np.asarray([-1, 2*F**2, 2*2*c*F, 1, 2*F**2, 2*2*c*F, -1, 1], dtype=np.float64) + jac = spa.coo_matrix((nonzeros, (irow, jcol)), shape=(2,5)) + return jac + + def evaluate_jacobian_outputs(self): + raise NotImplementedError('This method should not be called for this model.') + +class PressureDropTwoEqualitiesTwoOutputs(ExternalGreyBoxModel): + # u = [Pin, c, F, P1, P3] + # o = {P2, Pout} + # h_eq(u) = [P1 - (Pin - c*F^2] + # [P3 - (P1 - 2*c*F^2] + # h_o(u) = [P1 - c*F^2] + # [Pin - 4*c*F^2] + def __init__(self): + self._input_names = ['Pin', 'c', 'F', 'P1', 'P3'] + self._input_values = np.zeros(5, dtype=np.float64) + self._equality_constraint_names = ['pdrop1', 'pdrop3'] + self._output_names = ['P2', 'Pout'] + + def input_names(self): + return self._input_names + + def equality_constraint_names(self): + return self._equality_constraint_names + + def output_names(self): + return self._output_names + + def set_input_values(self, input_values): + assert len(input_values) == 5 + np.copyto(self._input_values, input_values) + + def evaluate_equality_constraints(self): + Pin = self._input_values[0] + c = self._input_values[1] + F = self._input_values[2] + P1 = self._input_values[3] + P3 = self._input_values[4] + return np.asarray([P1 - (Pin - c*F**2), P3 - (P1 - 2*c*F**2)], dtype=np.float64) + + def evaluate_outputs(self): + Pin = self._input_values[0] + c = self._input_values[1] + F = self._input_values[2] + P1 = self._input_values[3] + return np.asarray([P1 - c*F**2, Pin - 4*c*F**2], dtype=np.float64) + + def evaluate_jacobian_equality_constraints(self): + c = self._input_values[1] + F = self._input_values[2] + irow = np.asarray([0, 0, 0, 0, 1, 1, 1, 1], dtype=np.int64) + jcol = np.asarray([0, 1, 2, 3, 1, 2, 3, 4], dtype=np.int64) + nonzeros = np.asarray([-1, F**2, 2*c*F, 1, 2*F**2, 4*c*F, -1, 1], dtype=np.float64) + jac = spa.coo_matrix((nonzeros, (irow, jcol)), shape=(2,5)) + return jac + + def evaluate_jacobian_outputs(self): + c = self._input_values[1] + F = self._input_values[2] + irow = np.asarray([0, 0, 0, 1, 1, 1], dtype=np.int64) + jcol = np.asarray([1, 2, 3, 0, 1, 2], dtype=np.int64) + nonzeros = np.asarray([-F**2, -c*2*F, 1, 1, -4*F**2, -4*c*2*F], dtype=np.float64) + jac = spa.coo_matrix((nonzeros, (irow, jcol)), shape=(2,5)) + return jac + +class PressureDropTwoEqualitiesTwoOutputsScaleBoth(PressureDropTwoEqualitiesTwoOutputs): + def get_equality_constraint_scaling_factors(self): + return np.asarray([3.1, 3.2], dtype=np.float64) + + def get_output_constraint_scaling_factors(self): + return np.asarray([4.1, 4.2]) + +class PressureDropTwoEqualitiesTwoOutputsScaleEqualities(PressureDropTwoEqualitiesTwoOutputs): + def get_equality_constraint_scaling_factors(self): + return np.asarray([3.1, 3.2], dtype=np.float64) + +class PressureDropTwoEqualitiesTwoOutputsScaleOutputs(PressureDropTwoEqualitiesTwoOutputs): + def get_output_constraint_scaling_factors(self): + return np.asarray([4.1, 4.2]) + + +class TestExternalGreyBoxModel(unittest.TestCase): + + def test_pressure_drop_single_output(self): + egbm = PressureDropSingleOutput() + input_names = egbm.input_names() + self.assertEqual(input_names, ['Pin', 'c', 'F']) + eq_con_names = egbm.equality_constraint_names() + self.assertEqual(eq_con_names, []) + output_names = egbm.output_names() + self.assertEqual(output_names, ['Pout']) + + egbm.set_input_values(np.asarray([100, 2, 3], dtype=np.float64)) + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_equality_constraints() + + o = egbm.evaluate_outputs() + self.assertTrue(np.array_equal(o, np.asarray([28], dtype=np.float64))) + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_jacobian_equality_constraints() + + jac_o = egbm.evaluate_jacobian_outputs() + self.assertTrue(np.array_equal(jac_o.row, np.asarray([0,0,0], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_o.col, np.asarray([0,1,2], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_o.data, np.asarray([1,-36,-48], dtype=np.float64))) + + def test_pressure_drop_single_equality(self): + egbm = PressureDropSingleEquality() + input_names = egbm.input_names() + self.assertEqual(input_names, ['Pin', 'c', 'F', 'Pout']) + eq_con_names = egbm.equality_constraint_names() + self.assertEqual(eq_con_names, ['pdrop']) + output_names = egbm.output_names() + self.assertEqual(output_names, []) + + egbm.set_input_values(np.asarray([100, 2, 3, 50], dtype=np.float64)) + + eq = egbm.evaluate_equality_constraints() + self.assertTrue(np.array_equal(eq, np.asarray([22], dtype=np.float64))) + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_outputs() + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_jacobian_outputs() + + jac_eq = egbm.evaluate_jacobian_equality_constraints() + self.assertTrue(np.array_equal(jac_eq.row, np.asarray([0,0,0,0], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_eq.col, np.asarray([0,1,2,3], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_eq.data, np.asarray([-1, 36, 48, 1], dtype=np.float64))) + + def test_pressure_drop_two_outputs(self): + egbm = PressureDropTwoOutputs() + input_names = egbm.input_names() + self.assertEqual(input_names, ['Pin', 'c', 'F']) + eq_con_names = egbm.equality_constraint_names() + self.assertEqual([], eq_con_names) + output_names = egbm.output_names() + self.assertEqual(output_names, ['P2', 'Pout']) + + egbm.set_input_values(np.asarray([100, 2, 3], dtype=np.float64)) + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_equality_constraints() + + # u = [Pin, c, F] + # o = [P2, Pout] + # h_eq(u) = {empty} + # h_o(u) = [Pin - 2*c*F^2] + # [Pin - 4*c*F^2] + + o = egbm.evaluate_outputs() + self.assertTrue(np.array_equal(o, np.asarray([64, 28], dtype=np.float64))) + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_jacobian_equality_constraints() + + jac_o = egbm.evaluate_jacobian_outputs() + self.assertTrue(np.array_equal(jac_o.row, np.asarray([0,0,0,1,1,1], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_o.col, np.asarray([0,1,2,0,1,2], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_o.data, np.asarray([1, -18, -24, 1,-36,-48], dtype=np.float64))) + + def test_pressure_drop_two_equalities(self): + egbm = PressureDropTwoEqualities() + input_names = egbm.input_names() + self.assertEqual(input_names, ['Pin', 'c', 'F', 'P2', 'Pout']) + eq_con_names = egbm.equality_constraint_names() + self.assertEqual(eq_con_names, ['pdrop2', 'pdropout']) + output_names = egbm.output_names() + self.assertEqual([], output_names) + + egbm.set_input_values(np.asarray([100, 2, 3, 20, 50], dtype=np.float64)) + + # u = [Pin, c, F, P2, Pout] + # o = {empty} + # h_eq(u) = [P2 - (Pin - 2*c*F^2] + # [Pout - (P2 - 2*c*F^2] + # h_o(u) = {empty} + eq = egbm.evaluate_equality_constraints() + self.assertTrue(np.array_equal(eq, np.asarray([-44, 66], dtype=np.float64))) + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_outputs() + + with self.assertRaises(NotImplementedError): + tmp = egbm.evaluate_jacobian_outputs() + + jac_eq = egbm.evaluate_jacobian_equality_constraints() + self.assertTrue(np.array_equal(jac_eq.row, np.asarray([0,0,0,0,1,1,1,1], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_eq.col, np.asarray([0,1,2,3,1,2,3,4], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_eq.data, np.asarray([-1, 18, 24, 1, 18, 24, -1, 1], dtype=np.float64))) + + def test_pressure_drop_two_equalities_two_outputs(self): + # u = [Pin, c, F, P1, P3] + # o = {P2, Pout} + # h_eq(u) = [P1 - (Pin - c*F^2] + # [P3 - (Pin - 2*c*F^2] + # h_o(u) = [P1 - c*F^2] + # [Pin - 4*c*F^2] + egbm = PressureDropTwoEqualitiesTwoOutputs() + input_names = egbm.input_names() + self.assertEqual(input_names, ['Pin', 'c', 'F', 'P1', 'P3']) + eq_con_names = egbm.equality_constraint_names() + self.assertEqual(eq_con_names, ['pdrop1', 'pdrop3']) + output_names = egbm.output_names() + self.assertEqual(output_names, ['P2', 'Pout']) + + egbm.set_input_values(np.asarray([100, 2, 3, 80, 70], dtype=np.float64)) + eq = egbm.evaluate_equality_constraints() + self.assertTrue(np.array_equal(eq, np.asarray([-2, 26], dtype=np.float64))) + + o = egbm.evaluate_outputs() + self.assertTrue(np.array_equal(o, np.asarray([62, 28], dtype=np.float64))) + + jac_eq = egbm.evaluate_jacobian_equality_constraints() + self.assertTrue(np.array_equal(jac_eq.row, np.asarray([0,0,0,0,1,1,1,1], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_eq.col, np.asarray([0,1,2,3,1,2,3,4], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_eq.data, np.asarray([-1, 9, 12, 1, 18, 24, -1, 1], dtype=np.float64))) + + jac_o = egbm.evaluate_jacobian_outputs() + self.assertTrue(np.array_equal(jac_o.row, np.asarray([0,0,0,1,1,1], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_o.col, np.asarray([1,2,3,0,1,2], dtype=np.int64))) + self.assertTrue(np.array_equal(jac_o.data, np.asarray([-9, -12, 1, 1, -36, -48], dtype=np.float64))) + +# TODO: make this work even if there is only external and no variables anywhere in pyomo part +class TestPyomoGreyBoxNLP(unittest.TestCase): + @unittest.skip("It looks like ASL exits when there are no variables") + def test_error_no_variables(self): + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropSingleOutput()) + m.obj = pyo.Objective(expr=1) + with self.assertRaises(ValueError): + pyomo_nlp = PyomoGreyBoxNLP(m) + + def test_error_fixed_inputs_outputs(self): + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropSingleOutput()) + m.egb.inputs['Pin'].fix(100) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + with self.assertRaises(NotImplementedError): + pyomo_nlp = PyomoGreyBoxNLP(m) + + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropTwoOutputs()) + m.egb.outputs['P2'].fix(50) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + with self.assertRaises(NotImplementedError): + pyomo_nlp = PyomoGreyBoxNLP(m) + + def test_pressure_drop_single_output(self): + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropSingleOutput()) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.outputs['Pout'].value = 50 + m.egb.outputs['Pout'].setlb(0) + m.egb.outputs['Pout'].setub(100) + #m.dummy = pyo.Constraint(expr=sum(m.egb.inputs[i] for i in m.egb.inputs) + sum(m.egb.outputs[i] for i in m.egb.outputs) <= 1e6) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + self.assertEqual(4, pyomo_nlp.n_primals()) + self.assertEqual(1, pyomo_nlp.n_constraints()) + self.assertEqual(4, pyomo_nlp.nnz_jacobian()) + with self.assertRaises(NotImplementedError): + tmp = pyomo_nlp.nnz_hessian_lag() + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', 'egb.outputs[Pout]'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.Pout_con'] + c_order = pyomo_nlp.constraint_names() + + xlb = pyomo_nlp.primals_lb() + comparison_xlb = np.asarray([50, 1, 1, 0], dtype=np.float64) + check_vectors_specific_order(self, xlb, x_order, comparison_xlb, comparison_x_order) + xub = pyomo_nlp.primals_ub() + comparison_xub = np.asarray([150, 5, 5, 100], dtype=np.float64) + check_vectors_specific_order(self, xub, x_order, comparison_xub, comparison_x_order) + clb = pyomo_nlp.constraints_lb() + comparison_clb = np.asarray([0], dtype=np.float64) + check_vectors_specific_order(self, clb, c_order, comparison_clb, comparison_c_order) + cub = pyomo_nlp.constraints_ub() + comparison_cub = np.asarray([0], dtype=np.float64) + check_vectors_specific_order(self, cub, c_order, comparison_cub, comparison_c_order) + + xinit = pyomo_nlp.init_primals() + comparison_xinit = np.asarray([100, 2, 3, 50], dtype=np.float64) + check_vectors_specific_order(self, xinit, x_order, comparison_xinit, comparison_x_order) + duals_init = pyomo_nlp.init_duals() + comparison_duals_init = np.asarray([1], dtype=np.float64) + check_vectors_specific_order(self, duals_init, c_order, comparison_duals_init, comparison_c_order) + + self.assertEqual(4, len(pyomo_nlp.create_new_vector('primals'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('constraints'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('duals'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('eq_constraints'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('ineq_constraints'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('duals_eq'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('duals_ineq'))) + + pyomo_nlp.set_primals(np.asarray([1, 2, 3, 4], dtype=np.float64)) + x = pyomo_nlp.get_primals() + self.assertTrue(np.array_equal(x, np.asarray([1,2,3,4], dtype=np.float64))) + pyomo_nlp.set_primals(pyomo_nlp.init_primals()) + + pyomo_nlp.set_duals(np.asarray([42], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([42], dtype=np.float64))) + pyomo_nlp.set_duals(np.asarray([1], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([1], dtype=np.float64))) + + fac = pyomo_nlp.get_obj_factor() + self.assertEqual(fac, 1) + pyomo_nlp.set_obj_factor(42) + self.assertEqual(pyomo_nlp.get_obj_factor(), 42) + pyomo_nlp.set_obj_factor(1) + + f = pyomo_nlp.evaluate_objective() + self.assertEqual(f, 900) + + gradf = pyomo_nlp.evaluate_grad_objective() + comparison_gradf = np.asarray([0, 0, 0, 60], dtype=np.float64) + check_vectors_specific_order(self, gradf, x_order, comparison_gradf, comparison_x_order) + c = pyomo_nlp.evaluate_constraints() + comparison_c = np.asarray([-22], dtype=np.float64) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + c = np.zeros(1) + pyomo_nlp.evaluate_constraints(out=c) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + + + j = pyomo_nlp.evaluate_jacobian() + comparison_j = np.asarray([[1, -36, -48, -1]]) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + j = 2.0*j + pyomo_nlp.evaluate_jacobian(out=j) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + with self.assertRaises(NotImplementedError): + h = pyomo_nlp.evaluate_hessian_lag() + + def test_pressure_drop_single_equality(self): + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropSingleEquality()) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.inputs['Pout'].value = 50 + m.egb.inputs['Pout'].setlb(0) + m.egb.inputs['Pout'].setub(100) + m.obj = pyo.Objective(expr=(m.egb.inputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + self.assertEqual(4, pyomo_nlp.n_primals()) + self.assertEqual(1, pyomo_nlp.n_constraints()) + self.assertEqual(4, pyomo_nlp.nnz_jacobian()) + with self.assertRaises(NotImplementedError): + tmp = pyomo_nlp.nnz_hessian_lag() + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', 'egb.inputs[Pout]'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.pdrop'] + c_order = pyomo_nlp.constraint_names() + + xlb = pyomo_nlp.primals_lb() + comparison_xlb = np.asarray([50, 1, 1, 0], dtype=np.float64) + check_vectors_specific_order(self, xlb, x_order, comparison_xlb, comparison_x_order) + xub = pyomo_nlp.primals_ub() + comparison_xub = np.asarray([150, 5, 5, 100], dtype=np.float64) + check_vectors_specific_order(self, xub, x_order, comparison_xub, comparison_x_order) + clb = pyomo_nlp.constraints_lb() + comparison_clb = np.asarray([0], dtype=np.float64) + check_vectors_specific_order(self, clb, c_order, comparison_clb, comparison_c_order) + cub = pyomo_nlp.constraints_ub() + comparison_cub = np.asarray([0], dtype=np.float64) + check_vectors_specific_order(self, cub, c_order, comparison_cub, comparison_c_order) + + xinit = pyomo_nlp.init_primals() + comparison_xinit = np.asarray([100, 2, 3, 50], dtype=np.float64) + check_vectors_specific_order(self, xinit, x_order, comparison_xinit, comparison_x_order) + duals_init = pyomo_nlp.init_duals() + comparison_duals_init = np.asarray([1], dtype=np.float64) + check_vectors_specific_order(self, duals_init, c_order, comparison_duals_init, comparison_c_order) + + self.assertEqual(4, len(pyomo_nlp.create_new_vector('primals'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('constraints'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('duals'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('eq_constraints'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('ineq_constraints'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('duals_eq'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('duals_ineq'))) + + pyomo_nlp.set_primals(np.asarray([1, 2, 3, 4], dtype=np.float64)) + x = pyomo_nlp.get_primals() + self.assertTrue(np.array_equal(x, np.asarray([1,2,3,4], dtype=np.float64))) + pyomo_nlp.set_primals(pyomo_nlp.init_primals()) + + pyomo_nlp.set_duals(np.asarray([42], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([42], dtype=np.float64))) + pyomo_nlp.set_duals(np.asarray([1], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([1], dtype=np.float64))) + + fac = pyomo_nlp.get_obj_factor() + self.assertEqual(fac, 1) + pyomo_nlp.set_obj_factor(42) + self.assertEqual(pyomo_nlp.get_obj_factor(), 42) + pyomo_nlp.set_obj_factor(1) + + f = pyomo_nlp.evaluate_objective() + self.assertEqual(f, 900) + + gradf = pyomo_nlp.evaluate_grad_objective() + comparison_gradf = np.asarray([0, 0, 0, 60], dtype=np.float64) + check_vectors_specific_order(self, gradf, x_order, comparison_gradf, comparison_x_order) + c = pyomo_nlp.evaluate_constraints() + comparison_c = np.asarray([22], dtype=np.float64) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + c = np.zeros(1) + pyomo_nlp.evaluate_constraints(out=c) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + + + j = pyomo_nlp.evaluate_jacobian() + comparison_j = np.asarray([[-1, 36, 48, 1]]) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + j = 2.0*j + pyomo_nlp.evaluate_jacobian(out=j) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + with self.assertRaises(NotImplementedError): + h = pyomo_nlp.evaluate_hessian_lag() + + def test_pressure_drop_two_outputs(self): + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropTwoOutputs()) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.outputs['P2'].value = 80 + m.egb.outputs['P2'].setlb(10) + m.egb.outputs['P2'].setub(90) + m.egb.outputs['Pout'].value = 50 + m.egb.outputs['Pout'].setlb(0) + m.egb.outputs['Pout'].setub(100) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + self.assertEqual(5, pyomo_nlp.n_primals()) + self.assertEqual(2, pyomo_nlp.n_constraints()) + self.assertEqual(8, pyomo_nlp.nnz_jacobian()) + with self.assertRaises(NotImplementedError): + tmp = pyomo_nlp.nnz_hessian_lag() + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', 'egb.outputs[P2]', 'egb.outputs[Pout]'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.P2_con', 'egb.Pout_con'] + c_order = pyomo_nlp.constraint_names() + + xlb = pyomo_nlp.primals_lb() + comparison_xlb = np.asarray([50, 1, 1, 10, 0], dtype=np.float64) + check_vectors_specific_order(self, xlb, x_order, comparison_xlb, comparison_x_order) + xub = pyomo_nlp.primals_ub() + comparison_xub = np.asarray([150, 5, 5, 90, 100], dtype=np.float64) + check_vectors_specific_order(self, xub, x_order, comparison_xub, comparison_x_order) + clb = pyomo_nlp.constraints_lb() + comparison_clb = np.asarray([0, 0], dtype=np.float64) + check_vectors_specific_order(self, clb, c_order, comparison_clb, comparison_c_order) + cub = pyomo_nlp.constraints_ub() + comparison_cub = np.asarray([0, 0], dtype=np.float64) + check_vectors_specific_order(self, cub, c_order, comparison_cub, comparison_c_order) + + xinit = pyomo_nlp.init_primals() + comparison_xinit = np.asarray([100, 2, 3, 80, 50], dtype=np.float64) + check_vectors_specific_order(self, xinit, x_order, comparison_xinit, comparison_x_order) + duals_init = pyomo_nlp.init_duals() + comparison_duals_init = np.asarray([1, 1], dtype=np.float64) + check_vectors_specific_order(self, duals_init, c_order, comparison_duals_init, comparison_c_order) + + self.assertEqual(5, len(pyomo_nlp.create_new_vector('primals'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('constraints'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('duals'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('eq_constraints'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('ineq_constraints'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('duals_eq'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('duals_ineq'))) + + pyomo_nlp.set_primals(np.asarray([1, 2, 3, 4, 5], dtype=np.float64)) + x = pyomo_nlp.get_primals() + self.assertTrue(np.array_equal(x, np.asarray([1,2,3,4,5], dtype=np.float64))) + pyomo_nlp.set_primals(pyomo_nlp.init_primals()) + + pyomo_nlp.set_duals(np.asarray([42, 10], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([42, 10], dtype=np.float64))) + pyomo_nlp.set_duals(np.asarray([1, 1], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([1, 1], dtype=np.float64))) + + fac = pyomo_nlp.get_obj_factor() + self.assertEqual(fac, 1) + pyomo_nlp.set_obj_factor(42) + self.assertEqual(pyomo_nlp.get_obj_factor(), 42) + pyomo_nlp.set_obj_factor(1) + + f = pyomo_nlp.evaluate_objective() + self.assertEqual(f, 900) + + gradf = pyomo_nlp.evaluate_grad_objective() + comparison_gradf = np.asarray([0, 0, 0, 0, 60], dtype=np.float64) + check_vectors_specific_order(self, gradf, x_order, comparison_gradf, comparison_x_order) + c = pyomo_nlp.evaluate_constraints() + comparison_c = np.asarray([-16, -22], dtype=np.float64) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + c = np.zeros(2) + pyomo_nlp.evaluate_constraints(out=c) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + + + j = pyomo_nlp.evaluate_jacobian() + comparison_j = np.asarray([[1, -18, -24, -1, 0], [1, -36, -48, 0, -1]]) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + j = 2.0*j + pyomo_nlp.evaluate_jacobian(out=j) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + with self.assertRaises(NotImplementedError): + h = pyomo_nlp.evaluate_hessian_lag() + + def test_pressure_drop_two_equalities(self): + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropTwoEqualities()) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.inputs['P2'].value = 80 + m.egb.inputs['P2'].setlb(10) + m.egb.inputs['P2'].setub(90) + m.egb.inputs['Pout'].value = 50 + m.egb.inputs['Pout'].setlb(0) + m.egb.inputs['Pout'].setub(100) + m.obj = pyo.Objective(expr=(m.egb.inputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + self.assertEqual(5, pyomo_nlp.n_primals()) + self.assertEqual(2, pyomo_nlp.n_constraints()) + self.assertEqual(8, pyomo_nlp.nnz_jacobian()) + with self.assertRaises(NotImplementedError): + tmp = pyomo_nlp.nnz_hessian_lag() + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', 'egb.inputs[P2]', 'egb.inputs[Pout]'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.pdrop2', 'egb.pdropout'] + c_order = pyomo_nlp.constraint_names() + + xlb = pyomo_nlp.primals_lb() + comparison_xlb = np.asarray([50, 1, 1, 10, 0], dtype=np.float64) + check_vectors_specific_order(self, xlb, x_order, comparison_xlb, comparison_x_order) + xub = pyomo_nlp.primals_ub() + comparison_xub = np.asarray([150, 5, 5, 90, 100], dtype=np.float64) + check_vectors_specific_order(self, xub, x_order, comparison_xub, comparison_x_order) + clb = pyomo_nlp.constraints_lb() + comparison_clb = np.asarray([0, 0], dtype=np.float64) + check_vectors_specific_order(self, clb, c_order, comparison_clb, comparison_c_order) + cub = pyomo_nlp.constraints_ub() + comparison_cub = np.asarray([0, 0], dtype=np.float64) + check_vectors_specific_order(self, cub, c_order, comparison_cub, comparison_c_order) + + xinit = pyomo_nlp.init_primals() + comparison_xinit = np.asarray([100, 2, 3, 80, 50], dtype=np.float64) + check_vectors_specific_order(self, xinit, x_order, comparison_xinit, comparison_x_order) + duals_init = pyomo_nlp.init_duals() + comparison_duals_init = np.asarray([1, 1], dtype=np.float64) + check_vectors_specific_order(self, duals_init, c_order, comparison_duals_init, comparison_c_order) + + self.assertEqual(5, len(pyomo_nlp.create_new_vector('primals'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('constraints'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('duals'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('eq_constraints'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('ineq_constraints'))) + self.assertEqual(2, len(pyomo_nlp.create_new_vector('duals_eq'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('duals_ineq'))) + + pyomo_nlp.set_primals(np.asarray([1, 2, 3, 4, 5], dtype=np.float64)) + x = pyomo_nlp.get_primals() + self.assertTrue(np.array_equal(x, np.asarray([1,2,3,4,5], dtype=np.float64))) + pyomo_nlp.set_primals(pyomo_nlp.init_primals()) + + pyomo_nlp.set_duals(np.asarray([42, 10], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([42, 10], dtype=np.float64))) + pyomo_nlp.set_duals(np.asarray([1, 1], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([1, 1], dtype=np.float64))) + + fac = pyomo_nlp.get_obj_factor() + self.assertEqual(fac, 1) + pyomo_nlp.set_obj_factor(42) + self.assertEqual(pyomo_nlp.get_obj_factor(), 42) + pyomo_nlp.set_obj_factor(1) + + f = pyomo_nlp.evaluate_objective() + self.assertEqual(f, 900) + + gradf = pyomo_nlp.evaluate_grad_objective() + comparison_gradf = np.asarray([0, 0, 0, 0, 60], dtype=np.float64) + check_vectors_specific_order(self, gradf, x_order, comparison_gradf, comparison_x_order) + c = pyomo_nlp.evaluate_constraints() + comparison_c = np.asarray([16, 6], dtype=np.float64) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + c = np.zeros(2) + pyomo_nlp.evaluate_constraints(out=c) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + + + j = pyomo_nlp.evaluate_jacobian() + comparison_j = np.asarray([[-1, 18, 24, 1, 0], [0, 18, 24, -1, 1]]) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + j = 2.0*j + pyomo_nlp.evaluate_jacobian(out=j) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + with self.assertRaises(NotImplementedError): + h = pyomo_nlp.evaluate_hessian_lag() + + def test_pressure_drop_two_equalities_two_outputs(self): + m = pyo.ConcreteModel() + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropTwoEqualitiesTwoOutputs()) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.inputs['P1'].value = 80 + m.egb.inputs['P1'].setlb(10) + m.egb.inputs['P1'].setub(90) + m.egb.inputs['P3'].value = 70 + m.egb.inputs['P3'].setlb(20) + m.egb.inputs['P3'].setub(80) + m.egb.outputs['P2'].value = 75 + m.egb.outputs['P2'].setlb(15) + m.egb.outputs['P2'].setub(85) + m.egb.outputs['Pout'].value = 50 + m.egb.outputs['Pout'].setlb(30) + m.egb.outputs['Pout'].setub(70) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + self.assertEqual(7, pyomo_nlp.n_primals()) + self.assertEqual(4, pyomo_nlp.n_constraints()) + self.assertEqual(16, pyomo_nlp.nnz_jacobian()) + with self.assertRaises(NotImplementedError): + tmp = pyomo_nlp.nnz_hessian_lag() + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', + 'egb.inputs[P1]', 'egb.inputs[P3]', + 'egb.outputs[P2]', 'egb.outputs[Pout]'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.pdrop1', 'egb.pdrop3', 'egb.P2_con', 'egb.Pout_con'] + c_order = pyomo_nlp.constraint_names() + + xlb = pyomo_nlp.primals_lb() + comparison_xlb = np.asarray([50, 1, 1, 10, 20, 15, 30], dtype=np.float64) + check_vectors_specific_order(self, xlb, x_order, comparison_xlb, comparison_x_order) + xub = pyomo_nlp.primals_ub() + comparison_xub = np.asarray([150, 5, 5, 90, 80, 85, 70], dtype=np.float64) + check_vectors_specific_order(self, xub, x_order, comparison_xub, comparison_x_order) + clb = pyomo_nlp.constraints_lb() + comparison_clb = np.asarray([0, 0, 0, 0], dtype=np.float64) + check_vectors_specific_order(self, clb, c_order, comparison_clb, comparison_c_order) + cub = pyomo_nlp.constraints_ub() + comparison_cub = np.asarray([0, 0, 0, 0], dtype=np.float64) + check_vectors_specific_order(self, cub, c_order, comparison_cub, comparison_c_order) + + xinit = pyomo_nlp.init_primals() + comparison_xinit = np.asarray([100, 2, 3, 80, 70, 75, 50], dtype=np.float64) + check_vectors_specific_order(self, xinit, x_order, comparison_xinit, comparison_x_order) + duals_init = pyomo_nlp.init_duals() + comparison_duals_init = np.asarray([1, 1, 1, 1], dtype=np.float64) + check_vectors_specific_order(self, duals_init, c_order, comparison_duals_init, comparison_c_order) + + self.assertEqual(7, len(pyomo_nlp.create_new_vector('primals'))) + self.assertEqual(4, len(pyomo_nlp.create_new_vector('constraints'))) + self.assertEqual(4, len(pyomo_nlp.create_new_vector('duals'))) + self.assertEqual(4, len(pyomo_nlp.create_new_vector('eq_constraints'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('ineq_constraints'))) + self.assertEqual(4, len(pyomo_nlp.create_new_vector('duals_eq'))) + self.assertEqual(0, len(pyomo_nlp.create_new_vector('duals_ineq'))) + + pyomo_nlp.set_primals(np.asarray([1, 2, 3, 4, 5, 6, 7], dtype=np.float64)) + x = pyomo_nlp.get_primals() + self.assertTrue(np.array_equal(x, np.asarray([1,2,3,4,5,6,7], dtype=np.float64))) + pyomo_nlp.set_primals(pyomo_nlp.init_primals()) + + pyomo_nlp.set_duals(np.asarray([42, 10, 11, 12], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([42, 10, 11, 12], dtype=np.float64))) + pyomo_nlp.set_duals(np.asarray([1, 1, 1, 1], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([1, 1, 1, 1], dtype=np.float64))) + + fac = pyomo_nlp.get_obj_factor() + self.assertEqual(fac, 1) + pyomo_nlp.set_obj_factor(42) + self.assertEqual(pyomo_nlp.get_obj_factor(), 42) + pyomo_nlp.set_obj_factor(1) + + f = pyomo_nlp.evaluate_objective() + self.assertEqual(f, 900) + + gradf = pyomo_nlp.evaluate_grad_objective() + comparison_gradf = np.asarray([0, 0, 0, 0, 0, 0, 60], dtype=np.float64) + check_vectors_specific_order(self, gradf, x_order, comparison_gradf, comparison_x_order) + c = pyomo_nlp.evaluate_constraints() + comparison_c = np.asarray([-2, 26, -13, -22], dtype=np.float64) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + c = np.zeros(4) + pyomo_nlp.evaluate_constraints(out=c) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + + + j = pyomo_nlp.evaluate_jacobian() + comparison_j = np.asarray([[-1, 9, 12, 1, 0, 0, 0], + [ 0, 18, 24, -1, 1, 0, 0], + [ 0, -9, -12, 1, 0, -1, 0], + [ 1, -36, -48, 0, 0, 0, -1]]) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + j = 2.0*j + pyomo_nlp.evaluate_jacobian(out=j) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + with self.assertRaises(NotImplementedError): + h = pyomo_nlp.evaluate_hessian_lag() + + def test_external_additional_constraints_vars(self): + m = pyo.ConcreteModel() + m.hin = pyo.Var(bounds=(0,None), initialize=10) + m.hout = pyo.Var(bounds=(0,None)) + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropTwoEqualitiesTwoOutputs()) + m.incon = pyo.Constraint(expr= 0 <= m.egb.inputs['Pin'] - 10*m.hin) + m.outcon = pyo.Constraint(expr= 0 == m.egb.outputs['Pout'] - 10*m.hout) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.inputs['P1'].value = 80 + m.egb.inputs['P1'].setlb(10) + m.egb.inputs['P1'].setub(90) + m.egb.inputs['P3'].value = 70 + m.egb.inputs['P3'].setlb(20) + m.egb.inputs['P3'].setub(80) + m.egb.outputs['P2'].value = 75 + m.egb.outputs['P2'].setlb(15) + m.egb.outputs['P2'].setub(85) + m.egb.outputs['Pout'].value = 50 + m.egb.outputs['Pout'].setlb(30) + m.egb.outputs['Pout'].setub(70) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + self.assertEqual(9, pyomo_nlp.n_primals()) + self.assertEqual(6, pyomo_nlp.n_constraints()) + self.assertEqual(20, pyomo_nlp.nnz_jacobian()) + with self.assertRaises(NotImplementedError): + tmp = pyomo_nlp.nnz_hessian_lag() + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', + 'egb.inputs[P1]', 'egb.inputs[P3]', + 'egb.outputs[P2]', 'egb.outputs[Pout]', + 'hin', 'hout'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.pdrop1', 'egb.pdrop3', 'egb.P2_con', 'egb.Pout_con', 'incon', 'outcon'] + c_order = pyomo_nlp.constraint_names() + + xlb = pyomo_nlp.primals_lb() + comparison_xlb = np.asarray([50, 1, 1, 10, 20, 15, 30, 0, 0], dtype=np.float64) + check_vectors_specific_order(self, xlb, x_order, comparison_xlb, comparison_x_order) + xub = pyomo_nlp.primals_ub() + comparison_xub = np.asarray([150, 5, 5, 90, 80, 85, 70, np.inf, np.inf], dtype=np.float64) + check_vectors_specific_order(self, xub, x_order, comparison_xub, comparison_x_order) + clb = pyomo_nlp.constraints_lb() + comparison_clb = np.asarray([0, 0, 0, 0, 0, 0], dtype=np.float64) + check_vectors_specific_order(self, clb, c_order, comparison_clb, comparison_c_order) + cub = pyomo_nlp.constraints_ub() + comparison_cub = np.asarray([0, 0, 0, 0, np.inf, 0], dtype=np.float64) + check_vectors_specific_order(self, cub, c_order, comparison_cub, comparison_c_order) + + xinit = pyomo_nlp.init_primals() + comparison_xinit = np.asarray([100, 2, 3, 80, 70, 75, 50, 10, 0], dtype=np.float64) + check_vectors_specific_order(self, xinit, x_order, comparison_xinit, comparison_x_order) + duals_init = pyomo_nlp.init_duals() + comparison_duals_init = np.asarray([1, 1, 1, 1, 0, 0], dtype=np.float64) + check_vectors_specific_order(self, duals_init, c_order, comparison_duals_init, comparison_c_order) + + self.assertEqual(9, len(pyomo_nlp.create_new_vector('primals'))) + self.assertEqual(6, len(pyomo_nlp.create_new_vector('constraints'))) + self.assertEqual(6, len(pyomo_nlp.create_new_vector('duals'))) + self.assertEqual(5, len(pyomo_nlp.create_new_vector('eq_constraints'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('ineq_constraints'))) + self.assertEqual(5, len(pyomo_nlp.create_new_vector('duals_eq'))) + self.assertEqual(1, len(pyomo_nlp.create_new_vector('duals_ineq'))) + + pyomo_nlp.set_primals(np.asarray([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=np.float64)) + x = pyomo_nlp.get_primals() + self.assertTrue(np.array_equal(x, np.asarray([1,2,3,4,5,6,7,8,9], dtype=np.float64))) + pyomo_nlp.set_primals(pyomo_nlp.init_primals()) + + pyomo_nlp.set_duals(np.asarray([42, 10, 11, 12, 13, 14], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([42, 10, 11, 12, 13, 14], dtype=np.float64))) + pyomo_nlp.set_duals(np.asarray([1, 1, 1, 1, 0, 0], dtype=np.float64)) + y = pyomo_nlp.get_duals() + self.assertTrue(np.array_equal(y, np.asarray([1, 1, 1, 1, 0, 0], dtype=np.float64))) + + fac = pyomo_nlp.get_obj_factor() + self.assertEqual(fac, 1) + pyomo_nlp.set_obj_factor(42) + self.assertEqual(pyomo_nlp.get_obj_factor(), 42) + pyomo_nlp.set_obj_factor(1) + + f = pyomo_nlp.evaluate_objective() + self.assertEqual(f, 900) + + gradf = pyomo_nlp.evaluate_grad_objective() + comparison_gradf = np.asarray([0, 0, 0, 0, 0, 0, 60, 0, 0], dtype=np.float64) + check_vectors_specific_order(self, gradf, x_order, comparison_gradf, comparison_x_order) + c = pyomo_nlp.evaluate_constraints() + comparison_c = np.asarray([-2, 26, -13, -22, 0, 50], dtype=np.float64) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + c = np.zeros(6) + pyomo_nlp.evaluate_constraints(out=c) + check_vectors_specific_order(self, c, c_order, comparison_c, comparison_c_order) + + j = pyomo_nlp.evaluate_jacobian() + comparison_j = np.asarray([[-1, 9, 12, 1, 0, 0, 0, 0, 0], + [ 0, 18, 24, -1, 1, 0, 0, 0, 0], + [ 0, -9, -12, 1, 0, -1, 0, 0, 0], + [ 1, -36, -48, 0, 0, 0, -1, 0, 0], + [ 1, 0, 0, 0, 0, 0, 0, -10, 0], + [ 0, 0, 0, 0, 0, 0, 1, 0, -10]]) + + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + j = 2.0*j + pyomo_nlp.evaluate_jacobian(out=j) + check_sparse_matrix_specific_order(self, j, c_order, x_order, comparison_j, comparison_c_order, comparison_x_order) + + with self.assertRaises(NotImplementedError): + h = pyomo_nlp.evaluate_hessian_lag() + + @unittest.skipIf(not ipopt_available, "CyIpopt needed to run tests with solve") + def test_external_greybox_solve(self): + m = pyo.ConcreteModel() + m.mu = pyo.Var(bounds=(0,None), initialize=1) + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropTwoEqualitiesTwoOutputs()) + m.ccon = pyo.Constraint(expr = m.egb.inputs['c'] == 128/(3.14*1e-4)*m.mu*m.egb.inputs['F']) + m.pcon = pyo.Constraint(expr = m.egb.inputs['Pin'] - m.egb.outputs['Pout'] <= 72) + m.pincon = pyo.Constraint(expr = m.egb.inputs['Pin'] == 100.0) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.inputs['P1'].value = 80 + m.egb.inputs['P1'].setlb(10) + m.egb.inputs['P1'].setub(90) + m.egb.inputs['P3'].value = 70 + m.egb.inputs['P3'].setlb(20) + m.egb.inputs['P3'].setub(80) + m.egb.outputs['P2'].value = 75 + m.egb.outputs['P2'].setlb(15) + m.egb.outputs['P2'].setub(85) + m.egb.outputs['Pout'].value = 50 + m.egb.outputs['Pout'].setlb(10) + m.egb.outputs['Pout'].setub(70) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2 + (m.egb.inputs['F']-3)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + options = {'hessian_approximation':'limited-memory'} + cyipopt_problem = CyIpoptNLP(pyomo_nlp) + solver = CyIpoptSolver(cyipopt_problem, options) + x, info = solver.solve(tee=False) + pyomo_nlp.set_primals(x) + pyomo_nlp.load_state_into_pyomo() + + self.assertAlmostEqual(pyo.value(m.egb.inputs['F']), 3.0, places=3) + self.assertAlmostEqual(pyo.value(m.mu), 1.63542e-6, places=3) + self.assertAlmostEqual(pyo.value(m.egb.outputs['Pout']), 28.0, places=3) + self.assertAlmostEqual(pyo.value(m.egb.inputs['Pin']), 100.0, places=3) + self.assertAlmostEqual(pyo.value(m.egb.inputs['c']), 2.0, places=3) + self.assertAlmostEqual(pyo.value(m.egb.inputs['P1']), 82.0, places=3) + self.assertAlmostEqual(pyo.value(m.egb.inputs['P3']), 46.0, places=3) + self.assertAlmostEqual(pyo.value(m.egb.outputs['P2']), 64.0, places=3) + self.assertAlmostEqual(pyo.value(m.egb.inputs['F']), 3.0, places=3) + + def create_model_two_equalities_two_outputs(self, external_model): + m = pyo.ConcreteModel() + m.hin = pyo.Var(bounds=(0,None), initialize=10) + m.hout = pyo.Var(bounds=(0,None)) + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(external_model) + m.incon = pyo.Constraint(expr= 0 <= m.egb.inputs['Pin'] - 10*m.hin) + m.outcon = pyo.Constraint(expr= 0 == m.egb.outputs['Pout'] - 10*m.hout) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.inputs['P1'].value = 80 + m.egb.inputs['P1'].setlb(10) + m.egb.inputs['P1'].setub(90) + m.egb.inputs['P3'].value = 70 + m.egb.inputs['P3'].setlb(20) + m.egb.inputs['P3'].setub(80) + m.egb.outputs['P2'].value = 75 + m.egb.outputs['P2'].setlb(15) + m.egb.outputs['P2'].setub(85) + m.egb.outputs['Pout'].value = 50 + m.egb.outputs['Pout'].setlb(30) + m.egb.outputs['Pout'].setub(70) + return m + + def test_scaling_all_missing(self): + m = self.create_model_two_equalities_two_outputs(PressureDropTwoEqualitiesTwoOutputs()) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + fs = pyomo_nlp.get_obj_scaling() + xs = pyomo_nlp.get_primals_scaling() + cs = pyomo_nlp.get_constraints_scaling() + self.assertIsNone(fs) + self.assertIsNone(xs) + self.assertIsNone(cs) + + def test_scaling_pyomo_model_only(self): + m = self.create_model_two_equalities_two_outputs(PressureDropTwoEqualitiesTwoOutputs()) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) + #m.scaling_factor[m.obj] = 0.1 # scale the objective + m.scaling_factor[m.egb.inputs['Pin']] = 1.1 # scale the variable + m.scaling_factor[m.egb.inputs['c']] = 1.2 # scale the variable + m.scaling_factor[m.egb.inputs['F']] = 1.3 # scale the variable + #m.scaling_factor[m.egb.inputs['P1']] = 1.4 # scale the variable + m.scaling_factor[m.egb.inputs['P3']] = 1.5 # scale the variable + m.scaling_factor[m.egb.outputs['P2']] = 1.6 # scale the variable + m.scaling_factor[m.egb.outputs['Pout']] = 1.7 # scale the variable + #m.scaling_factor[m.hin] = 1.8 + m.scaling_factor[m.hout] = 1.9 + #m.scaling_factor[m.incon] = 2.1 + m.scaling_factor[m.outcon] = 2.2 + pyomo_nlp = PyomoGreyBoxNLP(m) + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', + 'egb.inputs[P1]', 'egb.inputs[P3]', + 'egb.outputs[P2]', 'egb.outputs[Pout]', + 'hin', 'hout'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.pdrop1', 'egb.pdrop3', 'egb.P2_con', 'egb.Pout_con', 'incon', 'outcon'] + c_order = pyomo_nlp.constraint_names() + + fs = pyomo_nlp.get_obj_scaling() + self.assertEqual(fs, 1.0) + + xs = pyomo_nlp.get_primals_scaling() + comparison_xs = np.asarray([1.1, 1.2, 1.3, 1.0, 1.5, 1.6, 1.7, 1.0, 1.9], dtype=np.float64) + check_vectors_specific_order(self, xs, x_order, comparison_xs, comparison_x_order) + + cs = pyomo_nlp.get_constraints_scaling() + comparison_cs = np.asarray([1, 1, 1, 1, 1, 2.2], dtype=np.float64) + check_vectors_specific_order(self, cs, c_order, comparison_cs, comparison_c_order) + + def test_scaling_greybox_only(self): + m = self.create_model_two_equalities_two_outputs(PressureDropTwoEqualitiesTwoOutputsScaleBoth()) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', + 'egb.inputs[P1]', 'egb.inputs[P3]', + 'egb.outputs[P2]', 'egb.outputs[Pout]', + 'hin', 'hout'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.pdrop1', 'egb.pdrop3', 'egb.P2_con', 'egb.Pout_con', 'incon', 'outcon'] + c_order = pyomo_nlp.constraint_names() + + fs = pyomo_nlp.get_obj_scaling() + self.assertEqual(fs, 1.0) + + xs = pyomo_nlp.get_primals_scaling() + comparison_xs = np.asarray([1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=np.float64) + check_vectors_specific_order(self, xs, x_order, comparison_xs, comparison_x_order) + + cs = pyomo_nlp.get_constraints_scaling() + comparison_cs = np.asarray([3.1, 3.2, 4.1, 4.2, 1, 1], dtype=np.float64) + check_vectors_specific_order(self, cs, c_order, comparison_cs, comparison_c_order) + + m = self.create_model_two_equalities_two_outputs(PressureDropTwoEqualitiesTwoOutputsScaleEqualities()) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + cs = pyomo_nlp.get_constraints_scaling() + comparison_cs = np.asarray([3.1, 3.2, 1, 1, 1, 1], dtype=np.float64) + check_vectors_specific_order(self, cs, c_order, comparison_cs, comparison_c_order) + + m = self.create_model_two_equalities_two_outputs(PressureDropTwoEqualitiesTwoOutputsScaleOutputs()) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + pyomo_nlp = PyomoGreyBoxNLP(m) + cs = pyomo_nlp.get_constraints_scaling() + comparison_cs = np.asarray([1, 1, 4.1, 4.2, 1, 1], dtype=np.float64) + check_vectors_specific_order(self, cs, c_order, comparison_cs, comparison_c_order) + + def test_scaling_pyomo_model_and_greybox(self): + m = self.create_model_two_equalities_two_outputs(PressureDropTwoEqualitiesTwoOutputsScaleBoth()) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2) + m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) + #m.scaling_factor[m.obj] = 0.1 # scale the objective + m.scaling_factor[m.egb.inputs['Pin']] = 1.1 # scale the variable + m.scaling_factor[m.egb.inputs['c']] = 1.2 # scale the variable + m.scaling_factor[m.egb.inputs['F']] = 1.3 # scale the variable + #m.scaling_factor[m.egb.inputs['P1']] = 1.4 # scale the variable + m.scaling_factor[m.egb.inputs['P3']] = 1.5 # scale the variable + m.scaling_factor[m.egb.outputs['P2']] = 1.6 # scale the variable + m.scaling_factor[m.egb.outputs['Pout']] = 1.7 # scale the variable + #m.scaling_factor[m.hin] = 1.8 + m.scaling_factor[m.hout] = 1.9 + #m.scaling_factor[m.incon] = 2.1 + m.scaling_factor[m.outcon] = 2.2 + pyomo_nlp = PyomoGreyBoxNLP(m) + + comparison_x_order = ['egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[F]', + 'egb.inputs[P1]', 'egb.inputs[P3]', + 'egb.outputs[P2]', 'egb.outputs[Pout]', + 'hin', 'hout'] + x_order = pyomo_nlp.variable_names() + comparison_c_order = ['egb.pdrop1', 'egb.pdrop3', 'egb.P2_con', 'egb.Pout_con', 'incon', 'outcon'] + c_order = pyomo_nlp.constraint_names() + + fs = pyomo_nlp.get_obj_scaling() + self.assertEqual(fs, 1.0) + + xs = pyomo_nlp.get_primals_scaling() + comparison_xs = np.asarray([1.1, 1.2, 1.3, 1.0, 1.5, 1.6, 1.7, 1.0, 1.9], dtype=np.float64) + check_vectors_specific_order(self, xs, x_order, comparison_xs, comparison_x_order) + + cs = pyomo_nlp.get_constraints_scaling() + comparison_cs = np.asarray([3.1, 3.2, 4.1, 4.2, 1, 2.2], dtype=np.float64) + check_vectors_specific_order(self, cs, c_order, comparison_cs, comparison_c_order) + + @unittest.skipIf(not ipopt_available, "CyIpopt needed to run tests with solve") + def test_external_greybox_solve_scaling(self): + m = pyo.ConcreteModel() + m.mu = pyo.Var(bounds=(0,None), initialize=1) + m.egb = ExternalGreyBoxBlock() + m.egb.set_external_model(PressureDropTwoEqualitiesTwoOutputsScaleBoth()) + m.ccon = pyo.Constraint(expr = m.egb.inputs['c'] == 128/(3.14*1e-4)*m.mu*m.egb.inputs['F']) + m.pcon = pyo.Constraint(expr = m.egb.inputs['Pin'] - m.egb.outputs['Pout'] <= 72) + m.pincon = pyo.Constraint(expr = m.egb.inputs['Pin'] == 100.0) + m.egb.inputs['Pin'].value = 100 + m.egb.inputs['Pin'].setlb(50) + m.egb.inputs['Pin'].setub(150) + m.egb.inputs['c'].value = 2 + m.egb.inputs['c'].setlb(1) + m.egb.inputs['c'].setub(5) + m.egb.inputs['F'].value = 3 + m.egb.inputs['F'].setlb(1) + m.egb.inputs['F'].setub(5) + m.egb.inputs['P1'].value = 80 + m.egb.inputs['P1'].setlb(10) + m.egb.inputs['P1'].setub(90) + m.egb.inputs['P3'].value = 70 + m.egb.inputs['P3'].setlb(20) + m.egb.inputs['P3'].setub(80) + m.egb.outputs['P2'].value = 75 + m.egb.outputs['P2'].setlb(15) + m.egb.outputs['P2'].setub(85) + m.egb.outputs['Pout'].value = 50 + m.egb.outputs['Pout'].setlb(10) + m.egb.outputs['Pout'].setub(70) + m.obj = pyo.Objective(expr=(m.egb.outputs['Pout']-20)**2 + (m.egb.inputs['F']-3)**2) + + m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) + m.scaling_factor[m.obj] = 0.1 # scale the objective + m.scaling_factor[m.egb.inputs['Pin']] = 1.1 # scale the variable + m.scaling_factor[m.egb.inputs['c']] = 1.2 # scale the variable + m.scaling_factor[m.egb.inputs['F']] = 1.3 # scale the variable + #m.scaling_factor[m.egb.inputs['P1']] = 1.4 # scale the variable + m.scaling_factor[m.egb.inputs['P3']] = 1.5 # scale the variable + m.scaling_factor[m.egb.outputs['P2']] = 1.6 # scale the variable + m.scaling_factor[m.egb.outputs['Pout']] = 1.7 # scale the variable + m.scaling_factor[m.mu] = 1.9 + m.scaling_factor[m.pincon] = 2.2 + pyomo_nlp = PyomoGreyBoxNLP(m) + + options={'hessian_approximation':'limited-memory', + 'nlp_scaling_method': 'user-scaling', + 'output_file': '_cyipopt-external-greybox-scaling.log', + 'file_print_level':10, + 'max_iter': 0} + cyipopt_problem = CyIpoptNLP(pyomo_nlp) + solver = CyIpoptSolver(cyipopt_problem, options) + x, info = solver.solve(tee=False) + + with open('_cyipopt-external-greybox-scaling.log', 'r') as fd: + solver_trace = fd.read() + #os.remove('_cyipopt-external-greybox-scaling.log') + + self.assertIn('nlp_scaling_method = user-scaling', solver_trace) + self.assertIn('output_file = _cyipopt-external-greybox-scaling.log', solver_trace) + self.assertIn('objective scaling factor = 0.1', solver_trace) + self.assertIn('x scaling provided', solver_trace) + self.assertIn('c scaling provided', solver_trace) + self.assertIn('d scaling provided', solver_trace) + # x order: ['egb.inputs[F]', 'mu', 'egb.outputs[Pout]', 'egb.inputs[Pin]', 'egb.inputs[c]', 'egb.inputs[P1]', 'egb.inputs[P3]', 'egb.outputs[P2]'] + # c order: ['ccon', 'pcon', 'pincon', 'egb.pdrop1', 'egb.pdrop3', 'egb.P2_con', 'egb.Pout_con'] + self.assertIn('DenseVector "x scaling vector" with 8 elements:', solver_trace) + self.assertIn('x scaling vector[ 1]= 1.3000000000000000e+00', solver_trace) # F + self.assertIn('x scaling vector[ 2]= 1.8999999999999999e+00', solver_trace) # mu + self.assertIn('x scaling vector[ 3]= 1.7000000000000000e+00', solver_trace) # Pout + self.assertIn('x scaling vector[ 4]= 1.1000000000000001e+00', solver_trace) # Pin + self.assertIn('x scaling vector[ 5]= 1.2000000000000000e+00', solver_trace) # c + self.assertIn('x scaling vector[ 6]= 1.0000000000000000e+00', solver_trace) # P1 + self.assertIn('x scaling vector[ 7]= 1.5000000000000000e+00', solver_trace) # P3 + self.assertIn('x scaling vector[ 8]= 1.6000000000000001e+00', solver_trace) # P2 + self.assertIn('DenseVector "c scaling vector" with 6 elements:', solver_trace) + self.assertIn('c scaling vector[ 1]= 1.0000000000000000e+00', solver_trace) # ccon + self.assertIn('c scaling vector[ 2]= 2.2000000000000002e+00', solver_trace) # pincon + self.assertIn('c scaling vector[ 3]= 3.1000000000000001e+00', solver_trace) # pdrop1 + self.assertIn('c scaling vector[ 4]= 3.2000000000000002e+00', solver_trace) # pdrop3 + self.assertIn('c scaling vector[ 5]= 4.0999999999999996e+00', solver_trace) # P2_con + self.assertIn('c scaling vector[ 6]= 4.2000000000000002e+00', solver_trace) # Pout_con + self.assertIn('DenseVector "d scaling vector" with 1 elements:', solver_trace) + self.assertIn('d scaling vector[ 1]= 1.0000000000000000e+00', solver_trace) # pcon + +def check_vectors_specific_order(tst, v1, v1order, v2, v2order): + tst.assertEqual(len(v1), len(v1order)) + tst.assertEqual(len(v2), len(v2order)) + tst.assertEqual(len(v1), len(v2)) + v2map = {s:i for i,s in enumerate(v2order)} + for i,s in enumerate(v1order): + tst.assertEqual(v1[i], v2[v2map[s]]) + +def check_sparse_matrix_specific_order(tst, m1, m1rows, m1cols, m2, m2rows, m2cols): + tst.assertEqual(m1.shape[0], len(m1rows)) + tst.assertEqual(m1.shape[1], len(m1cols)) + tst.assertEqual(m2.shape[0], len(m2rows)) + tst.assertEqual(m2.shape[1], len(m2cols)) + tst.assertEqual(len(m1rows), len(m2rows)) + tst.assertEqual(len(m1cols), len(m2cols)) + + m1c = m1.todense() + m2c = np.zeros((len(m2rows), len(m2cols))) + rowmap = [m2rows.index(x) for x in m1rows] + colmap = [m2cols.index(x) for x in m1cols] + for i in range(len(m1rows)): + for j in range(len(m1cols)): + m2c[i,j] = m2[rowmap[i], colmap[j]] + + #print(m1c) + #print(m2c) + tst.assertTrue(np.array_equal(m1c, m2c)) + +if __name__ == '__main__': + TestPyomoGreyBoxNLP().test_external_greybox_solve(self) diff --git a/pyomo/contrib/pynumero/interfaces/tests/test_nlp.py b/pyomo/contrib/pynumero/interfaces/tests/test_nlp.py index 70bc4d8d07d..9f409e4e081 100644 --- a/pyomo/contrib/pynumero/interfaces/tests/test_nlp.py +++ b/pyomo/contrib/pynumero/interfaces/tests/test_nlp.py @@ -7,6 +7,7 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + import pyutilib.th as unittest import os @@ -22,16 +23,14 @@ "Pynumero needs the ASL extension to run NLP tests") import pyomo.environ as pyo -from pyomo.opt.base import WriterFactory from pyomo.contrib.pynumero.interfaces.ampl_nlp import AslNLP, AmplNLP from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP import tempfile -from scipy.sparse import coo_matrix - from pyomo.contrib.pynumero.interfaces.utils import build_bounds_mask, build_compression_matrix, \ build_compression_mask_for_finite_values, full_to_compressed, compressed_to_full + def create_pyomo_model1(): m = pyo.ConcreteModel() m.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT_EXPORT) diff --git a/pyomo/contrib/pynumero/intrinsic.py b/pyomo/contrib/pynumero/intrinsic.py index 062d48f7c71..b1ecfb286fa 100644 --- a/pyomo/contrib/pynumero/intrinsic.py +++ b/pyomo/contrib/pynumero/intrinsic.py @@ -18,7 +18,7 @@ def norm(x, ord=None): f = np.linalg.norm if isinstance(x, np.ndarray): return f(x, ord=ord) - elif isinstance(x, BlockVector): + elif isinstance(x, block_vector.BlockVector): flat_x = x.flatten() return f(flat_x, ord=ord) else: diff --git a/pyomo/contrib/pynumero/linalg/ma57.py b/pyomo/contrib/pynumero/linalg/ma57.py index 26a13e092f6..9b633505cec 100644 --- a/pyomo/contrib/pynumero/linalg/ma57.py +++ b/pyomo/contrib/pynumero/linalg/ma57.py @@ -7,13 +7,13 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from pyomo.common.fileutils import find_library from pyomo.contrib.pynumero.linalg.utils import (validate_index, validate_value, _NotSet) import numpy.ctypeslib as npct import numpy as np import ctypes -import sys import os class MA57Interface(object): diff --git a/pyomo/contrib/pynumero/linalg/tests/test_ma27.py b/pyomo/contrib/pynumero/linalg/tests/test_ma27.py index 7f831b67dae..78844dc3415 100644 --- a/pyomo/contrib/pynumero/linalg/tests/test_ma27.py +++ b/pyomo/contrib/pynumero/linalg/tests/test_ma27.py @@ -11,7 +11,8 @@ from pyomo.contrib.pynumero.dependencies import numpy as np, numpy_available if not numpy_available: raise unittest.SkipTest('pynumero MA27 tests require numpy') -from pyomo.contrib.pynumero.linalg.ma27 import * +import ctypes +from pyomo.contrib.pynumero.linalg.ma27 import MA27Interface @unittest.skipIf(not MA27Interface.available(), reason='MA27 not available') diff --git a/pyomo/contrib/pynumero/linalg/tests/test_ma57.py b/pyomo/contrib/pynumero/linalg/tests/test_ma57.py index 61def1b91b4..4569f6c5133 100644 --- a/pyomo/contrib/pynumero/linalg/tests/test_ma57.py +++ b/pyomo/contrib/pynumero/linalg/tests/test_ma57.py @@ -12,7 +12,7 @@ from pyomo.contrib.pynumero.dependencies import numpy as np, numpy_available if not numpy_available: raise unittest.SkipTest('pynumero MA27 tests require numpy') -from pyomo.contrib.pynumero.linalg.ma57 import * +from pyomo.contrib.pynumero.linalg.ma57 import MA57Interface @unittest.skipIf(not MA57Interface.available(), reason='MA57 not available') diff --git a/pyomo/contrib/pynumero/plugins.py b/pyomo/contrib/pynumero/plugins.py index 9f7944b74e2..ae74c755900 100644 --- a/pyomo/contrib/pynumero/plugins.py +++ b/pyomo/contrib/pynumero/plugins.py @@ -9,8 +9,13 @@ # ___________________________________________________________________________ from pyomo.common.extensions import ExtensionBuilderFactory +from pyomo.opt import SolverFactory from .build import PyNumeroBuilder +from .algorithms.solvers.cyipopt_solver import PyomoCyIpoptSolver def load(): ExtensionBuilderFactory.register('pynumero')(PyNumeroBuilder) - + SolverFactory.register( + 'cyipopt', + doc='Cyipopt: direct python bindings to the Ipopt NLP solver' + )(PyomoCyIpoptSolver) diff --git a/pyomo/contrib/pynumero/sparse/base_block.py b/pyomo/contrib/pynumero/sparse/base_block.py index 36bd46cd01f..37dc7684671 100644 --- a/pyomo/contrib/pynumero/sparse/base_block.py +++ b/pyomo/contrib/pynumero/sparse/base_block.py @@ -8,9 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import abc -import six - # These classes are for checking types consistently and raising errors @@ -172,10 +169,10 @@ def setdiag(self, values, k=0): msg = "setdiag not implemented for {}".format(self.__class__.__name__) raise NotImplementedError(msg) - def transpose(*axes): + def transpose(self, *axes): msg = "transpose not implemented for {}".format(self.__class__.__name__) raise NotImplementedError(msg) - def tostring(order='C'): + def tostring(self, order='C'): msg = "tostring not implemented for {}".format(self.__class__.__name__) raise NotImplementedError(msg) diff --git a/pyomo/contrib/pynumero/sparse/block_matrix.py b/pyomo/contrib/pynumero/sparse/block_matrix.py index 6d76d19a3f3..e3b873db275 100644 --- a/pyomo/contrib/pynumero/sparse/block_matrix.py +++ b/pyomo/contrib/pynumero/sparse/block_matrix.py @@ -21,17 +21,13 @@ """ -from scipy.sparse.sputils import upcast, isscalarlike, get_index_dtype +from scipy.sparse.sputils import get_index_dtype from pyomo.contrib.pynumero.sparse.block_vector import BlockVector from scipy.sparse import coo_matrix, csr_matrix, csc_matrix from scipy.sparse import isspmatrix -from pyomo.contrib.pynumero.sparse.utils import is_symmetric_sparse from .base_block import BaseBlockMatrix -from scipy.sparse.base import spmatrix import operator import numpy as np -import six -import abc import logging import warnings @@ -143,7 +139,10 @@ def dtype(self): Returns data type of the matrix. """ all_dtypes = [blk.dtype for blk in self._blocks[self._block_mask]] - ref_dtype = all_dtypes[0] + if len(all_dtypes) == 0: + ref_dtype = np.double + else: + ref_dtype = all_dtypes[0] if all(ref_dtype is i for i in all_dtypes): return ref_dtype else: @@ -268,6 +267,12 @@ def block_shapes(self): sizes[i].append(shape) return sizes + def get_block_mask(self, copy=True): + if copy: + return self._block_mask.copy() + else: + return self._block_mask + def dot(self, other): """ Ordinary dot product @@ -824,10 +829,8 @@ def __setitem__(self, item, val): raise NotImplementedError('BlockMatrix does not support __setitem__. ' 'Use get_block or set_block to access sub-blocks.') - def __add__(self, other): - + def _binary_operation_helper(self, other, operation): assert_block_structure(self) - result = BlockMatrix(self.bshape[0], self.bshape[1]) if isinstance(other, BlockMatrix): @@ -837,71 +840,40 @@ def __add__(self, other): 'dimensions mismatch {} != {}'.format(self.shape, other.shape) assert_block_structure(other) - iterator = set(zip(*np.nonzero(self._block_mask))) - iterator.update(zip(*np.nonzero(other._block_mask))) - for i, j in iterator: - if not self.is_empty_block(i, j) and not other.is_empty_block(i, j): - result.set_block(i, j, self._blocks[i, j] + other.get_block(i, j)) - elif not self.is_empty_block(i, j): - result.set_block(i, j, self._blocks[i, j].copy()) - elif not other.is_empty_block(i, j): - result.set_block(i, j, other.get_block(i, j).copy()) + block_indices = np.bitwise_or(self.get_block_mask(copy=False), other.get_block_mask(copy=False)) + for i, j in zip(*np.nonzero(block_indices)): + mat1 = self.get_block(i, j) + mat2 = other.get_block(i, j) + if mat1 is not None and mat2 is not None: + result.set_block(i, j, operation(mat1, mat2)) + elif mat1 is not None: + result.set_block(i, j, operation(mat1, 0)) + elif mat2 is not None: + result.set_block(i, j, operation(0, mat2)) return result elif isspmatrix(other): # Note: this is not efficient but is just for flexibility. mat = self.copy_structure() mat.copyfrom(other) - return self.__add__(mat) + return operation(self, mat) + elif np.isscalar(other): + for i, j in zip(*np.nonzero(self.get_block_mask(copy=False))): + result.set_block(i, j, operation(self.get_block(i, j), other)) + return result else: - if other.__class__.__name__ == 'MPIBlockMatrix': - raise RuntimeError('Operation not supported by BlockMatrix') + return NotImplemented - raise NotImplementedError('Operation not supported by BlockMatrix') + def __add__(self, other): + return self._binary_operation_helper(other, operator.add) def __radd__(self, other): - return self.__add__(other) + return self._binary_operation_helper(other, operator.add) def __sub__(self, other): - - assert_block_structure(self) - result = BlockMatrix(self.bshape[0], self.bshape[1]) - - if isinstance(other, BlockMatrix): - assert other.bshape == self.bshape, \ - 'dimensions mismatch {} != {}'.format(self.bshape, other.bshape) - assert other.shape == self.shape, \ - 'dimensions mismatch {} != {}'.format(self.shape, other.shape) - assert_block_structure(other) - iterator = set(zip(*np.nonzero(self._block_mask))) - iterator.update(zip(*np.nonzero(other._block_mask))) - for i, j in iterator: - if not self.is_empty_block(i, j) and not other.is_empty_block(i, j): - result.set_block(i, j, self._blocks[i, j] - other.get_block(i, j)) - elif not self.is_empty_block(i, j): - result.set_block(i, j, self._blocks[i, j].copy()) - elif not other.is_empty_block(i, j): - result.set_block(i, j, -other.get_block(i, j)) - return result - elif isspmatrix(other): - # Note: this is not efficient but is just for flexibility. - mat = self.copy_structure() - mat.copyfrom(other) - return self.__sub__(mat) - else: - if other.__class__.__name__ == 'MPIBlockMatrix': - raise RuntimeError('Operation not supported by BlockMatrix') - raise NotImplementedError('Operation not supported by BlockMatrix') + return self._binary_operation_helper(other, operator.sub) def __rsub__(self, other): - assert_block_structure(self) - result = BlockMatrix(self.bshape[0], self.bshape[1]) - if isspmatrix(other): - # Note: this is not efficient but is just for flexibility. - mat = self.copy_structure() - mat.copyfrom(other) - return mat - self - else: - raise NotImplementedError('Operation not supported by BlockMatrix') + return (-self) + other def __mul__(self, other): """ @@ -912,11 +884,7 @@ def __mul__(self, other): bm, bn = self.bshape if np.isscalar(other): - result = BlockMatrix(bm, bn) - ii, jj = np.nonzero(self._block_mask) - for i, j in zip(ii, jj): - result.set_block(i, j, self._blocks[i, j] * other) - return result + return self._binary_operation_helper(other, operator.mul) elif isinstance(other, BlockVector): assert bn == other.bshape[0], 'Dimension mismatch' assert self.shape[1] == other.shape[0], 'Dimension mismatch' @@ -927,14 +895,13 @@ def __mul__(self, other): result = BlockVector(nblocks) for i in range(bm): result.set_block(i, np.zeros(self._brow_lengths[i])) - for j in range(bn): - if not self.is_empty_block(i, j): - x = other.get_block(j) - A = self._blocks[i, j] - blk = result.get_block(i) - _tmp = A*x - _tmp += blk - result.set_block(i, _tmp) + for i, j in zip(*np.nonzero(self._block_mask)): + x = other.get_block(j) + A = self._blocks[i, j] + blk = result.get_block(i) + _tmp = A*x + _tmp += blk + result.set_block(i, _tmp) return result elif isinstance(other, np.ndarray): @@ -957,23 +924,18 @@ def __mul__(self, other): x = other[counter: counter + A.shape[1]] blk = result.get_block(i) blk += A * x - counter += A.shape[0] + counter += self.get_col_size(j) return result elif isinstance(other, BlockMatrix) or isspmatrix(other): assert_block_structure(self) return self._mul_sparse_matrix(other) else: - raise NotImplementedError('input not recognized for multiplication') + return NotImplemented def __truediv__(self, other): - bm, bn = self.bshape if np.isscalar(other): - result = BlockMatrix(bm, bn) - ii, jj = np.nonzero(self._block_mask) - for i, j in zip(ii, jj): - result.set_block(i, j, self._blocks[i, j] / other) - return result - raise NotImplementedError('Operation not supported by BlockMatrix') + return self._binary_operation_helper(other, operator.truediv) + return NotImplemented def __rtruediv__(self, other): raise NotImplementedError('Operation not supported by BlockMatrix') diff --git a/pyomo/contrib/pynumero/sparse/block_vector.py b/pyomo/contrib/pynumero/sparse/block_vector.py index 410c51f97aa..d3b24a5a02c 100644 --- a/pyomo/contrib/pynumero/sparse/block_vector.py +++ b/pyomo/contrib/pynumero/sparse/block_vector.py @@ -1387,7 +1387,6 @@ def toMPIBlockVector(self, rank_ownership, mpi_comm): # populate blocks in the right spaces for bid in mpi_bv.owned_blocks: mpi_bv.set_block(bid, self.get_block(bid)) - mpi_bv.broadcast_block_sizes() return mpi_bv diff --git a/pyomo/contrib/pynumero/sparse/mpi_block_matrix.py b/pyomo/contrib/pynumero/sparse/mpi_block_matrix.py index 954c0ba0411..5b57af17952 100644 --- a/pyomo/contrib/pynumero/sparse/mpi_block_matrix.py +++ b/pyomo/contrib/pynumero/sparse/mpi_block_matrix.py @@ -22,13 +22,10 @@ """ from .mpi_block_vector import MPIBlockVector -from .mpi_block_vector import assert_block_structure as mpi_block_vector_assert_block_structure from .block_vector import BlockVector -from .block_vector import assert_block_structure as block_vector_assert_block_structure from .block_matrix import BlockMatrix, NotFullyDefinedBlockMatrixError from .block_matrix import assert_block_structure as block_matrix_assert_block_structure from .base_block import BaseBlockMatrix -from warnings import warn from mpi4py import MPI import numpy as np from scipy.sparse import coo_matrix @@ -36,16 +33,10 @@ __all__ = ['MPIBlockMatrix'] -# Array classifiers -SINGLE_OWNER = 1 -MULTIPLE_OWNER = 2 -ALL_OWN_IT = 0 - - -# ALL_OWNED = -1 - def assert_block_structure(mat): + if mat.has_undefined_row_sizes() or mat.has_undefined_col_sizes(): + mat.broadcast_block_sizes() if mat.has_undefined_row_sizes() or mat.has_undefined_col_sizes(): msg = 'Call MPIBlockMatrix.broadcast_block_sizes() first. ' raise NotFullyDefinedBlockMatrixError(msg) @@ -62,7 +53,7 @@ class MPIBlockMatrix(BaseBlockMatrix): single processor or by all processors. Blocks own by all processors have ownership -1. Blocks own by a single processor have ownership rank. where rank=MPI.COMM_WORLD.Get_rank() - _mpiw: MPI communicator + _mpiw: MPI.Comm A communicator from the MPI space. Typically MPI.COMM_WORLD _block_matrix: BlockMatrix Internal BlockMatrix. Blocks that belong to this processor are stored @@ -146,6 +137,7 @@ def shape(self): """ Returns tuple with total number of rows and columns """ + assert_block_structure(self) return self._block_matrix.shape @property @@ -230,6 +222,9 @@ def is_row_size_defined(self, row): def is_col_size_defined(self, col): return self._block_matrix.is_col_size_defined(col) + def get_block_mask(self, copy=True): + return self._block_matrix.get_block_mask(copy=copy) + @property def T(self): """ @@ -269,7 +264,6 @@ def transpose(self, axes=None, copy=True): m = self.bshape[0] n = self.bshape[1] - assert_block_structure(self) result = MPIBlockMatrix(n, m, self._rank_owner.T, self._mpiw) result._block_matrix = self._block_matrix.transpose() return result @@ -340,6 +334,7 @@ def to_local_array(self): ------- result: np.ndarray """ + assert_block_structure(self) local_result = self._block_matrix.copy_structure() rank = self._mpiw.Get_rank() block_indices = self._unique_owned_mask if rank != 0 else self._owned_mask @@ -349,7 +344,7 @@ def to_local_array(self): if not self._block_matrix.is_empty_block(i, j): local_result.set_block(i, j, self.get_block(i, j)) local_result = local_result.toarray() - global_result = np.zeros(shape=local_result.shape, dtype=local_result.dtype) + global_result = np.zeros(shape=self.shape, dtype=local_result.dtype) self._mpiw.Allreduce(local_result, global_result) return global_result @@ -695,107 +690,231 @@ def __getitem__(self, item): def __setitem__(self, item, val): raise NotImplementedError('MPIBlockMatrix does not support __setitem__.') - def __add__(self, other): - assert_block_structure(self) - m, n = self.bshape + def _binary_operation_helper(self, other, operation): result = self.copy_structure() - - rank = self._mpiw.Get_rank() - - if isinstance(other, MPIBlockMatrix): - assert_block_structure(other) - + if isinstance(other, (MPIBlockMatrix, BlockMatrix)): assert other.bshape == self.bshape, \ 'dimensions mismatch {} != {}'.format(self.bshape, other.bshape) - assert np.array_equal(self._rank_owner, other._rank_owner), \ - 'MPIBlockMatrices must be distributed in same processors' + if isinstance(other, MPIBlockMatrix): + assert np.array_equal(self._rank_owner, other._rank_owner), \ + 'MPIBlockMatrices must be distributed in same processors' - ii, jj = np.nonzero(self._owned_mask) + block_indices = np.bitwise_or(self.get_block_mask(copy=False), other.get_block_mask(copy=False)) + block_indices = np.bitwise_and(block_indices, self._owned_mask) + ii, jj = np.nonzero(block_indices) for i, j in zip(ii, jj): mat1 = self.get_block(i, j) mat2 = other.get_block(i, j) if mat1 is not None and mat2 is not None: - result.set_block(i, j, mat1 + mat2) + result.set_block(i, j, operation(mat1, mat2)) elif mat1 is not None and mat2 is None: - result.set_block(i, j, mat1.copy()) + result.set_block(i, j, operation(mat1, 0)) elif mat1 is None and mat2 is not None: - result.set_block(i, j, mat2.copy()) + result.set_block(i, j, operation(0, mat2)) else: - result.set_block(i, j, None) - return result - - raise NotImplementedError('Operation not supported by MPIBlockMatrix') - - def __radd__(self, other): # other + self - return self.__add__(other) - - def __sub__(self, other): - assert_block_structure(self) - m, n = self.bshape - result = self.copy_structure() - rank = self._mpiw.Get_rank() + raise ValueError('This is unexpected. Please report to the developers.') + elif np.isscalar(other): + block_indices = np.bitwise_and(self.get_block_mask(copy=False), self._owned_mask) + for i, j in zip(*np.nonzero(block_indices)): + result.set_block(i, j, operation(self.get_block(i, j), other)) + else: + raise NotImplementedError('Operation not supported by MPIBlockMatrix') + return result - if isinstance(other, MPIBlockMatrix): - assert_block_structure(other) + def _inplace_binary_operation_helper(self, other, operation): + if isinstance(other, (MPIBlockMatrix, BlockMatrix)): + assert operation in {operator.iadd, operator.isub} assert other.bshape == self.bshape, \ 'dimensions mismatch {} != {}'.format(self.bshape, other.bshape) - assert np.array_equal(self._rank_owner, other._rank_owner), \ - 'MPIBlockMatrices must be distributed in same processors' + if isinstance(other, MPIBlockMatrix): + assert np.array_equal(self._rank_owner, other._rank_owner), \ + 'MPIBlockMatrices must be distributed in same processors' - ii, jj = np.nonzero(self._owned_mask) + block_indices = other.get_block_mask(copy=False) + block_indices = np.bitwise_and(block_indices, self._owned_mask) + ii, jj = np.nonzero(block_indices) for i, j in zip(ii, jj): mat1 = self.get_block(i, j) mat2 = other.get_block(i, j) if mat1 is not None and mat2 is not None: - result.set_block(i, j, mat1 - mat2) - elif mat1 is not None and mat2 is None: - result.set_block(i, j, mat1.copy()) + mat1 = operation(mat1, mat2) + self.set_block(i, j, mat1) elif mat1 is None and mat2 is not None: - result.set_block(i, j, -mat2) + if operation is operator.iadd: + sub_res = mat2.copy() + else: + sub_res = -mat2 + self.set_block(i, j, sub_res) else: - result.set_block(i, j, None) - return result + raise RuntimeError('Please report this to the developers.') + elif np.isscalar(other): + block_indices = np.bitwise_and(self.get_block_mask(copy=False), self._owned_mask) + for i, j in zip(*np.nonzero(block_indices)): + blk = self.get_block(i, j) + blk = operation(blk, other) + self.set_block(i, j, blk) + else: + raise NotImplementedError('Operation not supported by MPIBlockMatrix') + return self - raise NotImplementedError('Operation not supported by MPIBlockMatrix') + def __add__(self, other): + return self._binary_operation_helper(other, operator.add) - def __rsub__(self, other): - raise NotImplementedError('Operation not supported by MPIBlockMatrix') + def __radd__(self, other): # other + self + return self._binary_operation_helper(other, operator.add) - def _block_vector_multiply(self, other): - """ - Parameters - ---------- - other: BlockVector + def __sub__(self, other): + return self._binary_operation_helper(other, operator.sub) - Returns - ------- - result: BlockVector - """ - block_vector_assert_block_structure(other) - assert self.bshape[1] == other.nblocks, 'Dimension mismatch' - local_result = BlockVector(self.bshape[0]) - for row_ndx in range(self.bshape[0]): - local_result.set_block(row_ndx, np.zeros(self.get_row_size(row_ndx))) - rank = self._mpiw.Get_rank() + def __rsub__(self, other): + return (-self) + other + + def _get_block_vector_for_dot_product(self, x): + if isinstance(x, MPIBlockVector): + """ + Consider a non-empty block m_{i, j} from the mpi block matrix with rank owner r_m and the + corresponding block v_{j} from the mpi block vector with rank owner r_v. There are 4 cases: + 1. r_m = r_v + In this case, all is good. + 2. r_v = -1 + In this case, all is good. + 3. r_m = -1 and r_v = 0 + All is good + 4. If none of the above cases hold, then v_{j} must be broadcast + """ + n_block_rows, n_block_cols = self.bshape + blocks_needing_broadcast = np.zeros(n_block_cols, dtype=np.int64) # a value > 0 means broadcast + x_rank_ownership = x.rank_ownership + comm = self._mpiw + rank = comm.Get_rank() + + if rank == 0: + block_indices = self._owned_mask + else: + block_indices = self._unique_owned_mask + block_indices = np.bitwise_and(block_indices, self.get_block_mask(copy=False)) + for i, j in zip(*np.nonzero(block_indices)): + r_m = self._rank_owner[i, j] + r_v = x_rank_ownership[j] + if r_m == r_v: + pass + elif r_v == -1: + pass + elif r_m == -1 and r_v == 0: + pass + else: + blocks_needing_broadcast[j] = 1 + + global_blocks_needing_broadcast = np.zeros(n_block_cols, dtype=np.int64) + comm.Allreduce(blocks_needing_broadcast, global_blocks_needing_broadcast) + indices_needing_broadcast = np.nonzero(global_blocks_needing_broadcast)[0] + if len(indices_needing_broadcast) == 0: + return x + else: + res = BlockVector(n_block_cols) + for ndx in np.nonzero(x.ownership_mask)[0]: + res.set_block(ndx, x.get_block(ndx)) + for j in indices_needing_broadcast: + j_owner = x_rank_ownership[j] + if rank == j_owner: + j_size = x.get_block_size(j) + else: + j_size = None + j_size = comm.bcast(j_size, j_owner) + if rank == j_owner: + data = x.get_block(j).flatten() + else: + data = np.empty(j_size) + comm.Bcast(data, j_owner) + res.set_block(j, data) + return res + elif isinstance(x, BlockVector): + return x + elif isinstance(x, np.ndarray): + y = BlockVector(self.bshape[1]) + for ndx, size in enumerate(self.col_block_sizes(copy=False)): + y.set_block(ndx, np.zeros(size)) + y.copyfrom(x) + return y + else: + raise NotImplementedError('Dot product is not yet supported for MPIBlockMatrix*'+str(type(x))) + + def _block_vector_multiply(self, x): + """ + In this method, we assume that we can access the correct blocks from x. This means that + _get_block_vector_for_dot_product should be called first. + + For a given block row, if there are multiple non-empty blocks with different rank owners, + then the result for that row is owned by all, and we need to do an Allreduce. Otherwise the + rank owner of the resulting block is the rank owner of the non-empty blocks in the block row. + """ + n_block_rows, n_block_cols = self.bshape + comm = self._mpiw + rank = comm.Get_rank() + + blocks_that_need_reduced = np.zeros(n_block_rows, dtype=np.int64) + res_rank_owner = np.zeros(n_block_rows, dtype=np.int64) + for i, j in zip(*np.nonzero(self._block_matrix._block_mask)): + blocks_that_need_reduced[i] = 1 + res_rank_owner[i] = self._rank_owner[i, j] + + # we need some special handling to determine the owner of empty rows + local_empty_rows = self._block_matrix._block_mask.any(axis=1) + local_empty_rows = np.array(local_empty_rows, dtype=np.int64) + global_empty_rows = np.empty(local_empty_rows.size, dtype=np.int64) + comm.Allreduce(local_empty_rows, global_empty_rows) + empty_rows = np.nonzero(global_empty_rows == 0)[0] + + global_blocks_that_need_reduced = np.zeros(n_block_rows, dtype=np.int64) + comm.Allreduce(blocks_that_need_reduced, global_blocks_that_need_reduced) + block_indices_that_need_reduced = np.nonzero(global_blocks_that_need_reduced > 1)[0] + global_res_rank_owner = np.zeros(n_block_rows, dtype=np.int64) + comm.Allreduce(res_rank_owner, global_res_rank_owner) + global_res_rank_owner[block_indices_that_need_reduced] = -1 + for ndx in empty_rows: + row_owners = set(self._rank_owner[ndx, :]) + if len(row_owners) == 1: + global_res_rank_owner[ndx] = row_owners.pop() + elif len(row_owners) == 2 and -1 in row_owners: + tmp = row_owners.pop() + if tmp == -1: + global_res_rank_owner[ndx] = row_owners.pop() + else: + global_res_rank_owner[ndx] = tmp + else: + global_res_rank_owner[ndx] = -1 + + res = MPIBlockVector(nblocks=n_block_rows, + rank_owner=global_res_rank_owner, + mpi_comm=comm, + assert_correct_owners=False) + for ndx in np.nonzero(res.ownership_mask)[0]: + res.set_block(ndx, np.zeros(self.get_row_size(ndx))) if rank == 0: block_indices = self._owned_mask else: block_indices = self._unique_owned_mask + block_indices = np.bitwise_and(block_indices, self._block_matrix._block_mask) for row_ndx, col_ndx in zip(*np.nonzero(block_indices)): - if self.get_block(row_ndx, col_ndx) is not None: - res_blk = local_result.get_block(row_ndx) - _tmp = self.get_block(row_ndx, col_ndx) * other.get_block(col_ndx) - res_blk = _tmp + res_blk - local_result.set_block(row_ndx, res_blk) - flat_local = local_result.flatten() - flat_global = np.zeros(flat_local.size) - self._mpiw.Allreduce(flat_local, flat_global) - global_result = local_result.copy_structure() - global_result.copyfrom(flat_global) - return global_result + res_blk = res.get_block(row_ndx) + tmp = self.get_block(row_ndx, col_ndx) * x.get_block(col_ndx) + tmp += res_blk + res.set_block(row_ndx, tmp) + + for ndx in block_indices_that_need_reduced: + local = res.get_block(ndx) + flat_local = local.flatten() + flat_global = np.zeros(flat_local.size) + comm.Allreduce(flat_local, flat_global) + if isinstance(local, BlockVector): + local.copyfrom(flat_global) + else: + res.set_block(ndx, flat_global) + + return res def __mul__(self, other): """ @@ -803,30 +922,11 @@ def __mul__(self, other): A*B with scipy sparse matrices, a matrix-matrix dot product is performed. We are following the scipy sparse matrix API. """ - - assert_block_structure(self) - - if isinstance(other, MPIBlockVector): - global_other = other.make_local_copy() - result = self._block_vector_multiply(global_other) - return result - elif isinstance(other, BlockVector): - return self._block_vector_multiply(other) - elif isinstance(other, np.ndarray): - block_other = BlockVector(nblocks=self.bshape[1]) - for ndx in range(self.bshape[1]): - block_other[ndx] = np.zeros(self.get_col_size(ndx), dtype=other.dtype) - block_other.copyfrom(other) - return self._block_vector_multiply(block_other).flatten() - elif np.isscalar(other): - result = self.copy_structure() - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - if not self._block_matrix.is_empty_block(i, j): - result.set_block(i, j, self.get_block(i, j) * other) - return result + if np.isscalar(other): + return self._binary_operation_helper(other, operator.mul) else: - raise NotImplementedError('Operation not supported by MPIBlockMatrix') + x = self._get_block_vector_for_dot_product(other) + return self._block_vector_multiply(x) def __rmul__(self, other): """ @@ -834,17 +934,8 @@ def __rmul__(self, other): A*B with scipy sparse matrices, a matrix-matrix dot product is performed. We are following the scipy sparse matrix API. """ - - assert_block_structure(self) - m, n = self.bshape - result = self.copy_structure() - if np.isscalar(other): - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - if not self._block_matrix.is_empty_block(i, j): - result.set_block(i, j, self.get_block(i, j) * other) - return result + return self._binary_operation_helper(other, operator.mul) if isinstance(other, MPIBlockVector): raise NotImplementedError('Vector-Matrix multiply not supported yet') @@ -862,100 +953,35 @@ def __pow__(self, other): raise NotImplementedError('Operation not supported by MPIBlockMatrix') def __truediv__(self, other): - assert_block_structure(self) - m, n = self.bshape - result = self.copy_structure() - if np.isscalar(other): - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - if not self._block_matrix.is_empty_block(i, j): - result.set_block(i, j, self.get_block(i, j) / other) - return result + return self._binary_operation_helper(other, operator.truediv) raise NotImplementedError('Operation not supported by MPIBlockMatrix') - def __floordiv__(self, other): + def __rtruediv__(self, other): raise NotImplementedError('Operation not supported by MPIBlockMatrix') - def __iadd__(self, other): - assert_block_structure(self) - m, n = self.bshape - - if isinstance(other, MPIBlockMatrix): - assert_block_structure(other) - - assert other.bshape == self.bshape, \ - 'dimensions mismatch {} != {}'.format(self.bshape, other.bshape) - - assert np.array_equal(self._rank_owner, other._rank_owner), \ - 'MPIBlockMatrices must be distributed in same processors' - - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - mat1 = self.get_block(i, j) - mat2 = other.get_block(i, j) - if mat1 is not None and mat2 is not None: - mat1 += mat2 - self.set_block(i, j, mat1) - elif mat1 is None and mat2 is not None: - self.set_block(i, j, mat2.copy()) - return self - + def __floordiv__(self, other): + if np.isscalar(other): + return self._binary_operation_helper(other, operator.floordiv) raise NotImplementedError('Operation not supported by MPIBlockMatrix') - def __isub__(self, other): - assert_block_structure(self) - m, n = self.bshape - - if isinstance(other, MPIBlockMatrix): - assert_block_structure(other) - - assert other.bshape == self.bshape, \ - 'dimensions mismatch {} != {}'.format(self.bshape, other.bshape) - - assert np.array_equal(self._rank_owner, other._rank_owner), \ - 'MPIBlockMatrices must be distributed in same processors' + def __rfloordiv__(self, other): + raise NotImplementedError('Operation not supported by MPIBlockMatrix') - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - mat1 = self.get_block(i, j) - mat2 = other.get_block(i, j) - if mat1 is not None and mat2 is not None: - blk = self.get_block(i, j) - blk -= mat2 - self.set_block(i, j, blk) - elif mat1 is None and mat2 is not None: - self.set_block(i, j, -mat2) - return self + def __iadd__(self, other): + return self._inplace_binary_operation_helper(other, operator.iadd) - raise NotImplementedError('Operation not supported by MPIBlockMatrix') + def __isub__(self, other): + return self._inplace_binary_operation_helper(other, operator.isub) def __imul__(self, other): - assert_block_structure(self) - m, n = self.bshape - if np.isscalar(other): - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - if not self._block_matrix.is_empty_block(i, j): - blk = self.get_block(i, j) - blk *= other - self.set_block(i, j, blk) - return self + return self._inplace_binary_operation_helper(other, operator.imul) raise NotImplementedError('Operation not supported by MPIBlockMatrix') def __itruediv__(self, other): - assert_block_structure(self) - m, n = self.bshape - if np.isscalar(other): - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - if not self._block_matrix.is_empty_block(i, j): - blk = self.get_block(i, j) - blk /= other - self.set_block(i, j, blk) - return self + return self._inplace_binary_operation_helper(other, operator.itruediv) raise NotImplementedError('Operation not supported by MPIBlockMatrix') def __div__(self, other): @@ -968,32 +994,24 @@ def __idiv__(self, other): return self.__itruediv__(other) def __neg__(self): - assert_block_structure(self) result = self.copy_structure() - - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - if not self._block_matrix.is_empty_block(i, j): - result.set_block(i, j, -self.get_block(i, j)) + block_indices = np.bitwise_and(self.get_block_mask(copy=False), self._owned_mask) + for i, j in zip(*np.nonzero(block_indices)): + result.set_block(i, j, -self.get_block(i, j)) return result def __abs__(self): - assert_block_structure(self) result = self.copy_structure() - - ii, jj = np.nonzero(self._owned_mask) - for i, j in zip(ii, jj): - if not self._block_matrix.is_empty_block(i, j): - result.set_block(i, j, abs(self.get_block(i, j))) + block_indices = np.bitwise_and(self.get_block_mask(copy=False), self._owned_mask) + for i, j in zip(*np.nonzero(block_indices)): + result.set_block(i, j, abs(self.get_block(i, j))) return result def _comparison_helper(self, operation, other): assert_block_structure(self) - m, n = self.bshape result = self.copy_structure() if isinstance(other, MPIBlockMatrix): - assert_block_structure(other) assert other.bshape == self.bshape, 'dimension mismatch {} != {}'.format(self.bshape, other.bshape) assert np.array_equal(self.rank_ownership, other.rank_ownership), 'MPIBlockMatrices must be distributed in ' \ 'the same processors' @@ -1239,8 +1257,13 @@ def fromBlockMatrix(block_matrix, rank_ownership, mpi_comm): mpi_comm) # populate matrix + for i in range(bm): + mat.set_row_size(i, block_matrix.get_row_size(i)) + + for j in range(bn): + mat.set_col_size(j, block_matrix.get_col_size(j)) + for i, j in mat.owned_blocks: mat.set_block(i, j, block_matrix.get_block(i, j)) - mat.broadcast_block_sizes() return mat diff --git a/pyomo/contrib/pynumero/sparse/mpi_block_vector.py b/pyomo/contrib/pynumero/sparse/mpi_block_vector.py index 532055263ae..a5f689c0709 100644 --- a/pyomo/contrib/pynumero/sparse/mpi_block_vector.py +++ b/pyomo/contrib/pynumero/sparse/mpi_block_vector.py @@ -15,7 +15,6 @@ from .block_vector import assert_block_structure as block_vector_assert_block_structure from mpi4py import MPI import numpy as np -import copy as cp import operator __all__ = ['MPIBlockVector'] @@ -85,7 +84,7 @@ class MPIBlockVector(np.ndarray, BaseBlockVector): An MPI communicator. Tyically MPI.COMM_WORLD """ - def __new__(cls, nblocks, rank_owner, mpi_comm): + def __new__(cls, nblocks, rank_owner, mpi_comm, assert_correct_owners=True): assert isinstance(nblocks, int) assert len(rank_owner) == nblocks @@ -114,11 +113,11 @@ def __new__(cls, nblocks, rank_owner, mpi_comm): obj._unique_owned_blocks.append(i) # containers that facilitate looping - obj._owned_blocks = np.array(obj._owned_blocks) - obj._unique_owned_blocks = np.array(obj._unique_owned_blocks) + obj._owned_blocks = np.array(obj._owned_blocks, dtype=np.int) + obj._unique_owned_blocks = np.array(obj._unique_owned_blocks, dtype=np.int) obj._brow_lengths = np.empty(nblocks, dtype=np.float64) obj._brow_lengths.fill(np.nan) - obj._undefined_brows = set(range(nblocks)) + obj._undefined_brows = set(obj._owned_blocks) # make some pointers unmutable. These arrays don't change after # MPIBlockVector has been created @@ -127,13 +126,16 @@ def __new__(cls, nblocks, rank_owner, mpi_comm): obj._owned_mask.flags.writeable = False obj._unique_owned_blocks.flags.writeable = False + obj._broadcasted = False + return obj - def __init__(self, nblocks, rank_owner, mpi_comm): + def __init__(self, nblocks, rank_owner, mpi_comm, assert_correct_owners=True): # Note: this requires communication but is disabled when assertions # are turned off - assert self._assert_correct_owners(), \ - 'rank_owner must be the same in all processors' + if assert_correct_owners: + assert self._assert_correct_owners(), \ + 'rank_owner must be the same in all processors' def __array_prepare__(self, out_arr, context=None): return super(MPIBlockVector, self).__array_prepare__(self, out_arr, context) @@ -208,7 +210,7 @@ def _binary_operation(self, ufunc, method, *args, **kwargs): if isinstance(x1, MPIBlockVector) and isinstance(x2, MPIBlockVector): msg = 'BlockVectors must be distributed in same processors' - assert np.array_equal(x1._rank_owner, x2._rank_owner), msg + assert np.array_equal(x1._rank_owner, x2._rank_owner) or self._mpiw.Get_size() == 1, msg assert x1._mpiw == x2._mpiw, 'Need to have same communicator' res = x1.copy_structure() @@ -266,8 +268,7 @@ def shape(self): """ Returns total number of elements in the MPIBlockVector """ - assert_block_structure(self) - return np.sum(self._brow_lengths), + return (self.size,) @property def size(self): @@ -275,7 +276,16 @@ def size(self): Returns total number of elements in this MPIBlockVector """ assert_block_structure(self) - return np.sum(self._brow_lengths) + comm = self._mpiw + rank = comm.Get_rank() + if rank == 0: + indices = self._owned_blocks + else: + indices = self._unique_owned_blocks + local_size = np.sum(self._brow_lengths[indices]) + size = comm.allreduce(local_size) + assert int(size) == size + return int(size) @property def ndim(self): @@ -326,26 +336,30 @@ def mpi_comm(self): """Returns MPI communicator""" return self._mpiw + def is_broadcasted(self): + return self._broadcasted + def block_sizes(self, copy=True): """ Returns 1D-Array with sizes of individual blocks in this MPIBlockVector """ - assert_block_structure(self) + if not self._broadcasted: + self.broadcast_block_sizes() if copy: return self._brow_lengths.copy() return self._brow_lengths def get_block_size(self, ndx): - if ndx in self._undefined_brows: + res = self._brow_lengths[ndx] + if np.isnan(res): raise NotFullyDefinedBlockVectorError('The dimensions of the requested block are not defined.') - return self._brow_lengths[ndx] + res = int(res) + return res def _set_block_size(self, ndx, size): if ndx in self._undefined_brows: self._undefined_brows.remove(ndx) self._brow_lengths[ndx] = size - if len(self._undefined_brows) == 0: - self._brow_lengths = np.asarray(self._brow_lengths, dtype=np.int64) else: if self._brow_lengths[ndx] != size: raise ValueError('Incompatible dimensions for block {ndx}; ' @@ -360,6 +374,7 @@ def broadcast_block_sizes(self): this MPIBlockVector knows it's dimensions across all blocks. This method must be called before running any operations with the MPIBlockVector. """ + assert_block_structure(self) rank = self._mpiw.Get_rank() num_processors = self._mpiw.Get_size() @@ -395,7 +410,26 @@ def broadcast_block_sizes(self): msg = 'The dimension of block {} was not specified in any process'.format(i) # here block_length must only have one element - self._set_block_size(i, block_length.pop()) + self._brow_lengths[i] = block_length.pop() + + self._brow_lengths = np.asarray(self._brow_lengths, dtype=np.int64) + self._broadcasted = True + + def finalize_block_sizes(self, broadcast=True, block_sizes=None): + """ + Only set broadcast=False if you know what you are doing! + + Parameters + ---------- + broadcast: bool + block_sizes: None or np.ndarray + """ + if broadcast: + self.broadcast_block_sizes() + else: + self._undefined_brows = set() + self._brow_lengths = block_sizes + self._broadcasted = True # Note: this requires communication but is only run in __new__ def _assert_correct_owners(self, root=0): @@ -533,7 +567,6 @@ def nonzero(self): assert_block_structure(self) for i in self._owned_blocks: result.set_block(i, self._block_vector.get_block(i).nonzero()[0]) - result.broadcast_block_sizes() return (result,) def round(self, decimals=0, out=None): @@ -595,7 +628,6 @@ def compress(self, condition, axis=None, out=None): assert self._mpiw == condition._mpiw, 'Need to have same communicator' for i in self._owned_blocks: result.set_block(i, self.get_block(i).compress(condition.get_block(i))) - result.broadcast_block_sizes() return result if isinstance(condition, BlockVector): raise RuntimeError('Operation not supported by MPIBlockVector') @@ -635,6 +667,8 @@ def copyfrom(self, other): self.set_block(i, other.get_block(i).copy()) elif isinstance(other, np.ndarray): assert_block_structure(self) + if not self.is_broadcasted(): + self.broadcast_block_sizes() assert self.shape == other.shape, 'Dimension mismatch {} != {}'.format(self.shape, other.shape) offset = 0 for idx in range(self.nblocks): @@ -718,10 +752,18 @@ def copy_structure(self): """ Returns a copy of the MPIBlockVector structure filled with zeros """ - result = MPIBlockVector(self.nblocks, self.rank_ownership, self.mpi_comm) - result._block_vector = self._block_vector.copy_structure() - result._brow_lengths = self._brow_lengths.copy() - result._undefined_brows = set(self._undefined_brows) + result = MPIBlockVector(self.nblocks, self.rank_ownership, self.mpi_comm, assert_correct_owners=False) + if self.is_broadcasted(): + result.finalize_block_sizes(broadcast=False, block_sizes=self.block_sizes(copy=False)) + for bid in self.owned_blocks: + block = self.get_block(bid) + if block is not None: + if isinstance(block, BlockVector): + result.set_block(bid, block.copy_structure()) + elif type(block) == np.ndarray: + result.set_block(bid, np.zeros(block.size)) + else: + raise NotImplementedError('Should never get here') return result def fill(self, value): @@ -775,7 +817,6 @@ def dot(self, other, out=None): 'Number of blocks mismatch: {} != {}'.format(self.nblocks, other.nblocks) return self.dot(other.toMPIBlockVector(self.rank_ownership, self.mpi_comm)) elif isinstance(other, np.ndarray): - assert self.shape == other.shape, 'Dimension mismatch: {} != {}'.format(self.shape, other.shape) other_bv = self.copy_structure() other_bv.copyfrom(other) return self.dot(other_bv) @@ -909,6 +950,8 @@ def make_local_copy(self): BlockVector """ assert_block_structure(self) + if not self.is_broadcasted(): + self.broadcast_block_sizes() result = self.make_local_structure_copy() local_data = np.zeros(self.size) @@ -943,7 +986,7 @@ def _binary_operation_helper(self, other, operation): assert self.nblocks == other.nblocks, \ 'Number of blocks mismatch: {} != {}'.format(self.nblocks, other.nblocks) if isinstance(other, MPIBlockVector): - assert np.array_equal(self._rank_owner, other._rank_owner), \ + assert np.array_equal(self._rank_owner, other._rank_owner) or self._mpiw.Get_size() == 1, \ 'MPIBlockVectors must be distributed in same processors' assert self._mpiw == other._mpiw, 'Need to have same communicator' for i in self._owned_blocks: @@ -980,7 +1023,7 @@ def _inplace_binary_operation_helper(self, other, operation): assert self.nblocks == other.nblocks, \ 'Number of blocks mismatch: {} != {}'.format(self.nblocks, other.nblocks) if isinstance(other, MPIBlockVector): - assert np.array_equal(self._rank_owner, other._rank_owner), \ + assert np.array_equal(self._rank_owner, other._rank_owner) or self._mpiw.Get_size() == 1, \ 'MPIBlockVectors must be distributed in same processors' assert self._mpiw == other._mpiw, 'Need to have same communicator' assert_block_structure(other) @@ -1070,7 +1113,7 @@ def _comparison_helper(self, other, operation): assert_block_structure(other) assert self.nblocks == other.nblocks, \ 'Number of blocks mismatch: {} != {}'.format(self.nblocks, other.nblocks) - assert np.array_equal(self._rank_owner, other._rank_owner), \ + assert np.array_equal(self._rank_owner, other._rank_owner) or self._mpiw.Get_size() == 1, \ 'MPIBlockVectors must be distributed in same processors' assert self._mpiw == other._mpiw, 'Need to have same communicator' @@ -1139,7 +1182,7 @@ def _has_equal_structure(self, other): if self.nblocks != other.nblocks: return False if isinstance(other, MPIBlockVector): - if (self.owned_blocks != other.owned_blocks).any(): + if (self.owned_blocks != other.owned_blocks).any() and self._mpiw.Get_size() != 1: return False for ndx in self.owned_blocks: block1 = self.get_block(ndx) @@ -1215,7 +1258,7 @@ def pprint(self, root=0): print(msg) def __len__(self): - return self.nblocks + raise NotImplementedError('Use size or nblocks') def __iter__(self): raise NotImplementedError('Not supported by MPIBlockVector') diff --git a/pyomo/contrib/pynumero/sparse/tests/test_block_matrix.py b/pyomo/contrib/pynumero/sparse/tests/test_block_matrix.py index 580e172475a..35193c079e2 100644 --- a/pyomo/contrib/pynumero/sparse/tests/test_block_matrix.py +++ b/pyomo/contrib/pynumero/sparse/tests/test_block_matrix.py @@ -220,6 +220,16 @@ def test_dot(self): self.assertTrue(np.allclose(A_dense.dot(x), block_res.flatten())) self.assertEqual(block_res.bshape[0], 2) + m = BlockMatrix(2, 2) + sub_m = np.array([[1, 0], + [0, 1]]) + sub_m = coo_matrix(sub_m) + m.set_block(0, 1, sub_m.copy()) + m.set_block(1, 0, sub_m.copy()) + x = np.arange(4) + res = m*x + self.assertTrue(np.allclose(res.flatten(), np.array([2, 3, 0, 1]))) + def test_reset_brow(self): self.basic_m.reset_brow(0) for j in range(self.basic_m.bshape[1]): @@ -318,7 +328,7 @@ def test_add(self): self.assertTrue(np.allclose(r.toarray(), dense_res)) with self.assertRaises(Exception) as context: - mm = A_block.__radd__(A_block.toarray()) + mm = A_block.toarray() + A_block with self.assertRaises(Exception) as context: mm = A_block + A_block.toarray() diff --git a/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py b/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py index d6aebd6a049..e0a72800717 100644 --- a/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py +++ b/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py @@ -7,8 +7,8 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from __future__ import division -import sys import pyutilib.th as unittest from pyomo.contrib.pynumero.dependencies import ( diff --git a/pyomo/contrib/pynumero/sparse/tests/test_intrinsics.py b/pyomo/contrib/pynumero/sparse/tests/test_intrinsics.py index b7c876b4a96..3f14955dff7 100644 --- a/pyomo/contrib/pynumero/sparse/tests/test_intrinsics.py +++ b/pyomo/contrib/pynumero/sparse/tests/test_intrinsics.py @@ -7,7 +7,7 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys + import pyutilib.th as unittest from pyomo.contrib.pynumero.dependencies import ( diff --git a/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_matrix.py b/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_matrix.py index 2beea888532..6269c3041f8 100644 --- a/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_matrix.py +++ b/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_matrix.py @@ -73,7 +73,6 @@ def setUpClass(cls): serial_bm.set_block(1, 1, m) cls.square_serial_mat = serial_bm - bm.broadcast_block_sizes() cls.square_mpi_mat = bm # create mpi matrix @@ -95,7 +94,6 @@ def setUpClass(cls): bm.set_block(1, 1, m) bm.set_block(0, 1, m) - bm.broadcast_block_sizes() cls.square_mpi_mat2 = bm # create serial matrix image @@ -117,7 +115,6 @@ def setUpClass(cls): bm.set_block(0, 2, m2) if rank == 1: bm.set_block(1, 1, m) - bm.broadcast_block_sizes() cls.rectangular_mpi_mat = bm bm = BlockMatrix(2, 3) @@ -133,8 +130,6 @@ def test_bshape(self): def test_shape(self): self.assertEqual(self.square_mpi_mat.shape, (8, 8)) self.assertEqual(self.rectangular_mpi_mat.shape, (8, 10)) - with self.assertRaises(NotFullyDefinedBlockMatrixError): - self.assertEqual(self.square_mpi_mat_no_broadcast.shape, (8, 8)) def test_tocoo(self): with self.assertRaises(Exception) as context: @@ -228,7 +223,6 @@ def test_reset_brow(self): bm.set_block(0, 0, m) if rank == 1: bm.set_block(1, 1, m) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m) @@ -261,7 +255,6 @@ def test_reset_bcol(self): bm.set_block(0, 0, m) if rank == 1: bm.set_block(1, 1, m) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m) @@ -358,43 +351,35 @@ def test_transpose(self): self.assertTrue(np.allclose(res.get_block(i, j).toarray().T, mat2.get_block(j, i).toarray())) - def test_add(self): + def _compare_mpi_and_serial_block_matrices(self, mpi_mat, serial_mat): + self.assertIsInstance(mpi_mat, MPIBlockMatrix) + rows, columns = np.nonzero(mpi_mat.ownership_mask) + for i, j in zip(rows, columns): + if mpi_mat.get_block(i, j) is not None: + self.assertTrue(np.allclose(mpi_mat.get_block(i, j).toarray(), + serial_mat.get_block(i, j).toarray())) + else: + self.assertIsNone(serial_mat.get_block(i, j)) + def test_add(self): mat1 = self.square_mpi_mat mat2 = self.square_mpi_mat2 serial_mat1 = self.square_serial_mat serial_mat2 = self.square_serial_mat2 - res = mat1 + mat1 - serial_res = serial_mat1 + serial_mat1 - self.assertIsInstance(res, MPIBlockMatrix) - self.assertTrue(np.allclose(mat1.rank_ownership, res.rank_ownership)) - rows, columns = np.nonzero(res.ownership_mask) - for i, j in zip(rows, columns): - if res.get_block(i, j) is not None: - self.assertTrue(np.allclose(res.get_block(i, j).toarray(), - serial_res.get_block(i, j).toarray())) - else: - self.assertIsNone(serial_res.get_block(i, j)) - res = mat1 + mat2 serial_res = serial_mat1 + serial_mat2 - self.assertIsInstance(res, MPIBlockMatrix) - rows, columns = np.nonzero(res.ownership_mask) self.assertTrue(np.allclose(mat1.rank_ownership, res.rank_ownership)) - for i, j in zip(rows, columns): - if res.get_block(i, j) is not None: - self.assertTrue(np.allclose(res.get_block(i, j).toarray(), - serial_res.get_block(i, j).toarray())) - else: - self.assertIsNone(serial_res.get_block(i, j)) + self._compare_mpi_and_serial_block_matrices(res, serial_res) - with self.assertRaises(Exception) as context: - res = mat1 + serial_mat2 + res = mat1 + serial_mat2 + self.assertTrue(np.allclose(mat1.rank_ownership, res.rank_ownership)) + self._compare_mpi_and_serial_block_matrices(res, serial_res) - with self.assertRaises(Exception) as context: - res = serial_mat2 + mat1 + res = serial_mat2 + mat1 + self.assertTrue(np.allclose(mat1.rank_ownership, res.rank_ownership)) + self._compare_mpi_and_serial_block_matrices(res, serial_res) with self.assertRaises(Exception) as context: res = mat1 + serial_mat2.tocoo() @@ -410,171 +395,26 @@ def test_sub(self): serial_mat1 = self.square_serial_mat serial_mat2 = self.square_serial_mat2 - res = mat1 - mat1 - serial_res = serial_mat1 - serial_mat1 - self.assertIsInstance(res, MPIBlockMatrix) - self.assertTrue(np.allclose(mat1.rank_ownership, res.rank_ownership)) - rows, columns = np.nonzero(res.ownership_mask) - for i, j in zip(rows, columns): - if res.get_block(i, j) is not None: - self.assertTrue(np.allclose(res.get_block(i, j).toarray(), - serial_res.get_block(i, j).toarray())) - else: - self.assertIsNone(serial_res.get_block(i, j)) - res = mat1 - mat2 serial_res = serial_mat1 - serial_mat2 - self.assertIsInstance(res, MPIBlockMatrix) - rows, columns = np.nonzero(res.ownership_mask) - for i, j in zip(rows, columns): - if res.get_block(i, j) is not None: - self.assertTrue(np.allclose(res.get_block(i, j).toarray(), - serial_res.get_block(i, j).toarray())) - else: - self.assertIsNone(serial_res.get_block(i, j)) + self.assertTrue(np.allclose(mat1.rank_ownership, res.rank_ownership)) + self._compare_mpi_and_serial_block_matrices(res, serial_res) + + res = mat1 - serial_mat2 + self._compare_mpi_and_serial_block_matrices(res, serial_res) + + res = mat2 - mat1 + serial_res = serial_mat2 - serial_mat1 + self._compare_mpi_and_serial_block_matrices(res, serial_res) + + res = serial_mat2 - mat1 + self._compare_mpi_and_serial_block_matrices(res, serial_res) - with self.assertRaises(Exception) as context: - res = mat1 - serial_mat2 - with self.assertRaises(Exception) as context: - res = serial_mat2 - mat1 with self.assertRaises(Exception) as context: res = mat1 - serial_mat2.tocoo() with self.assertRaises(Exception) as context: res = serial_mat2.tocoo() - mat1 - def test_mul(self): - - mat1 = self.square_mpi_mat - mat2 = self.square_mpi_mat2 - - serial_mat1 = self.square_serial_mat - serial_mat2 = self.square_serial_mat2 - - rank = comm.Get_rank() - - bv1 = MPIBlockVector(2, [0, 1], comm) - - if rank == 0: - bv1.set_block(0, np.arange(4, dtype=np.float64)) - if rank == 1: - bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - bv1.broadcast_block_sizes() - - serial_bv1 = BlockVector(2) - serial_bv1.set_block(0, np.arange(4, dtype=np.float64)) - serial_bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - - res = mat1 * bv1 - serial_res = serial_mat1 * serial_bv1 - self.assertIsInstance(res, BlockVector) - self.assertEqual(res.nblocks, serial_res.nblocks) - for bid in range(serial_res.nblocks): - self.assertTrue(np.allclose(res.get_block(bid), - serial_res.get_block(bid))) - - res = mat2 * bv1 - serial_res = serial_mat2 * serial_bv1 - self.assertIsInstance(res, BlockVector) - self.assertEqual(res.nblocks, serial_res.nblocks) - for bid in range(serial_res.nblocks): - self.assertTrue(np.allclose(res.get_block(bid), - serial_res.get_block(bid))) - - bv1 = MPIBlockVector(2, [0, -1], comm) - - if rank == 0: - bv1.set_block(0, np.arange(4, dtype=np.float64)) - bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - bv1.broadcast_block_sizes() - - res = mat1 * bv1 - serial_res = serial_mat1 * serial_bv1 - self.assertIsInstance(res, BlockVector) - self.assertEqual(res.nblocks, serial_res.nblocks) - for bid in range(serial_res.nblocks): - self.assertTrue(np.allclose(res.get_block(bid), - serial_res.get_block(bid))) - - res = mat2 * bv1 - serial_res = serial_mat2 * serial_bv1 - self.assertIsInstance(res, BlockVector) - self.assertEqual(res.nblocks, serial_res.nblocks) - for bid in range(serial_res.nblocks): - self.assertTrue(np.allclose(res.get_block(bid), - serial_res.get_block(bid))) - - # rectangular matrix - mat1 = self.rectangular_mpi_mat - serial_mat1 = self.rectangular_serial_mat - - bv1 = MPIBlockVector(3, [0, 1, 2], comm) - - if rank == 0: - bv1.set_block(0, np.arange(4, dtype=np.float64)) - if rank == 1: - bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - if rank == 2: - bv1.set_block(2, np.arange(2, dtype=np.float64) + 8) - - bv1.broadcast_block_sizes() - - serial_bv1 = BlockVector(3) - serial_bv1.set_block(0, np.arange(4, dtype=np.float64)) - serial_bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - serial_bv1.set_block(2, np.arange(2, dtype=np.float64) + 8) - - # with warnings.catch_warnings(): - # warnings.simplefilter("ignore") - res = mat1 * bv1 - serial_res = serial_mat1 * serial_bv1 - - self.assertIsInstance(res, BlockVector) - self.assertEqual(serial_res.nblocks, 2) - self.assertEqual(res.nblocks, 2) - for bid in range(serial_res.nblocks): - self.assertTrue(np.allclose(res.get_block(bid), - serial_res.get_block(bid))) - - bv1 = MPIBlockVector(3, [0, 1, 0], comm) - - if rank == 0: - bv1.set_block(0, np.arange(4, dtype=np.float64)) - bv1.set_block(2, np.arange(2, dtype=np.float64) + 8) - if rank == 1: - bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - bv1.broadcast_block_sizes() - - res = mat1 * bv1 - serial_res = serial_mat1 * serial_bv1 - self.assertIsInstance(res, BlockVector) - self.assertEqual(res.nblocks, serial_res.nblocks) - for bid in range(serial_res.nblocks): - self.assertTrue(np.allclose(res.get_block(bid), - serial_res.get_block(bid))) - - res = mat1 * 3.0 - serial_res = serial_mat1 * 3.0 - self.assertIsInstance(res, MPIBlockMatrix) - rows, columns = np.nonzero(res.ownership_mask) - for i, j in zip(rows, columns): - if res.get_block(i, j) is not None: - self.assertTrue(np.allclose(res.get_block(i, j).toarray(), - serial_res.get_block(i, j).toarray())) - else: - self.assertIsNone(serial_res.get_block(i, j)) - - res = 3.0 * mat1 - serial_res = serial_mat1 * 3.0 - - self.assertIsInstance(res, MPIBlockMatrix) - rows, columns = np.nonzero(res.ownership_mask) - for i, j in zip(rows, columns): - if res.get_block(i, j) is not None: - self.assertTrue(np.allclose(res.get_block(i, j).toarray(), - serial_res.get_block(i, j).toarray())) - else: - self.assertIsNone(serial_res.get_block(i, j)) - def test_div(self): mat1 = self.square_mpi_mat @@ -592,36 +432,6 @@ def test_div(self): else: self.assertIsNone(serial_res.get_block(i, j)) - def test_dot(self): - - mat1 = self.square_mpi_mat - mat2 = self.square_mpi_mat2 - - serial_mat1 = self.square_serial_mat - serial_mat2 = self.square_serial_mat2 - - rank = comm.Get_rank() - - bv1 = MPIBlockVector(2, [0, 1], comm) - - if rank == 0: - bv1.set_block(0, np.arange(4, dtype=np.float64)) - if rank == 1: - bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - bv1.broadcast_block_sizes() - - serial_bv1 = BlockVector(2) - serial_bv1.set_block(0, np.arange(4, dtype=np.float64)) - serial_bv1.set_block(1, np.arange(4, dtype=np.float64) + 4) - - res = mat1.dot(bv1) - serial_res = serial_mat1.dot(serial_bv1) - self.assertIsInstance(res, BlockVector) - self.assertEqual(res.nblocks, serial_res.nblocks) - for bid in range(serial_res.nblocks): - self.assertTrue(np.allclose(res.get_block(bid), - serial_res.get_block(bid))) - def test_iadd(self): row = np.array([0, 3, 1, 2, 3, 0]) @@ -637,7 +447,6 @@ def test_iadd(self): bm.set_block(0, 0, m.copy()) if rank == 1: bm.set_block(1, 1, m.copy()) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m.copy()) @@ -652,16 +461,9 @@ def test_iadd(self): self.assertTrue(np.allclose(bm.get_block(i, j).toarray(), serial_bm.get_block(i, j).toarray())) - with self.assertRaises(Exception) as context: - bm += serial_bm - - serial_bm2 = BlockMatrix(2, 2) - serial_bm2.set_block(0, 0, m.copy()) - serial_bm2.set_block(0, 1, m.copy()) - serial_bm2.set_block(1, 1, m.copy()) - - with self.assertRaises(Exception) as context: - bm += serial_bm2 + bm += serial_bm + serial_bm += serial_bm + self._compare_mpi_and_serial_block_matrices(bm, serial_bm) def test_isub(self): @@ -678,7 +480,6 @@ def test_isub(self): bm.set_block(0, 0, m.copy()) if rank == 1: bm.set_block(1, 1, m.copy()) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m.copy()) @@ -693,8 +494,9 @@ def test_isub(self): self.assertTrue(np.allclose(bm.get_block(i, j).toarray(), serial_bm.get_block(i, j).toarray())) - with self.assertRaises(Exception) as context: - bm -= serial_bm + bm -= serial_bm + serial_bm -= serial_bm + self._compare_mpi_and_serial_block_matrices(bm, serial_bm) def test_imul(self): @@ -711,7 +513,6 @@ def test_imul(self): bm.set_block(0, 0, m) if rank == 1: bm.set_block(1, 1, m) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m) @@ -741,7 +542,6 @@ def test_idiv(self): bm.set_block(0, 0, m) if rank == 1: bm.set_block(1, 1, m) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m) @@ -771,7 +571,6 @@ def test_neg(self): bm.set_block(0, 0, m) if rank == 1: bm.set_block(1, 1, m) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m) @@ -801,7 +600,6 @@ def test_abs(self): bm.set_block(0, 0, m) if rank == 1: bm.set_block(1, 1, m) - bm.broadcast_block_sizes() serial_bm = BlockMatrix(2, 2) serial_bm.set_block(0, 0, m) @@ -1153,3 +951,200 @@ def test_gt(self): with self.assertRaises(Exception) as context: res = serial_mat1 > mat1 + + +@unittest.category("mpi") +class TestMPIMatVec(unittest.TestCase): + + @classmethod + @unittest.skipIf(SKIPTESTS, SKIPTESTS) + def setUpClass(cls): + pass + + def test_get_block_vector_for_dot_product_1(self): + rank = comm.Get_rank() + + rank_ownership = np.array([[0, 1, 2], + [1, 1, 2], + [0, 1, 2], + [0, 1, 2]]) + m = MPIBlockMatrix(4, 3, rank_ownership, comm) + sub_m = np.array([[1, 0], + [0, 1]]) + sub_m = coo_matrix(sub_m) + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + + rank_ownership = np.array([0, 1, 2]) + v = MPIBlockVector(3, rank_ownership, comm) + sub_v = np.ones(2) + v.set_block(rank, sub_v) + + res = m._get_block_vector_for_dot_product(v) + + self.assertIs(res, v) + + def test_get_block_vector_for_dot_product_2(self): + rank = comm.Get_rank() + + rank_ownership = np.array([[1, 1, 2], + [0, 1, 2], + [0, 1, 2], + [0, 1, 2]]) + m = MPIBlockMatrix(4, 3, rank_ownership, comm) + sub_m = np.array([[1, 0], + [0, 1]]) + sub_m = coo_matrix(sub_m) + if rank == 0: + m.set_block(3, rank, sub_m.copy()) + elif rank == 1: + m.set_block(0, 0, sub_m.copy()) + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + else: + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + + rank_ownership = np.array([-1, 1, 2]) + v = MPIBlockVector(3, rank_ownership, comm) + sub_v = np.ones(2) + v.set_block(0, sub_v.copy()) + if rank != 0: + v.set_block(rank, sub_v.copy()) + + res = m._get_block_vector_for_dot_product(v) + + self.assertIs(res, v) + + def test_get_block_vector_for_dot_product_3(self): + rank = comm.Get_rank() + + rank_ownership = np.array([[1, 1, 2], + [0, 1, 2], + [0, 1, 2], + [0, 1, 2]]) + m = MPIBlockMatrix(4, 3, rank_ownership, comm) + sub_m = np.array([[1, 0], + [0, 1]]) + sub_m = coo_matrix(sub_m) + if rank == 0: + m.set_block(3, rank, sub_m.copy()) + elif rank == 1: + m.set_block(0, 0, sub_m.copy()) + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + else: + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + + rank_ownership = np.array([0, 1, 2]) + v = MPIBlockVector(3, rank_ownership, comm) + sub_v = np.ones(2) + v.set_block(rank, sub_v.copy()) + + res = m._get_block_vector_for_dot_product(v) + + self.assertIsNot(res, v) + self.assertTrue(np.array_equal(res.get_block(0), sub_v)) + if rank == 0: + self.assertIsNone(res.get_block(1)) + self.assertIsNone(res.get_block(2)) + elif rank == 1: + self.assertTrue(np.array_equal(res.get_block(1), sub_v)) + self.assertIsNone(res.get_block(2)) + elif rank == 2: + self.assertTrue(np.array_equal(res.get_block(2), sub_v)) + self.assertIsNone(res.get_block(1)) + + def test_get_block_vector_for_dot_product_4(self): + rank = comm.Get_rank() + + rank_ownership = np.array([[-1, 1, 2], + [0, 1, 2], + [0, 1, 2], + [0, 1, 2]]) + m = MPIBlockMatrix(4, 3, rank_ownership, comm) + sub_m = np.array([[1, 0], + [0, 1]]) + sub_m = coo_matrix(sub_m) + m.set_block(0, 0, sub_m.copy()) + if rank == 0: + m.set_block(3, rank, sub_m.copy()) + else: + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + + rank_ownership = np.array([0, 1, 2]) + v = MPIBlockVector(3, rank_ownership, comm) + sub_v = np.ones(2) + v.set_block(rank, sub_v.copy()) + + res = m._get_block_vector_for_dot_product(v) + + self.assertIs(res, v) + + def test_get_block_vector_for_dot_product_5(self): + rank = comm.Get_rank() + + rank_ownership = np.array([[1, 1, 2], + [0, 1, 2], + [0, 1, 2], + [0, 1, 2]]) + m = MPIBlockMatrix(4, 3, rank_ownership, comm) + sub_m = np.array([[1, 0], + [0, 1]]) + sub_m = coo_matrix(sub_m) + if rank == 0: + m.set_block(3, rank, sub_m.copy()) + elif rank == 1: + m.set_block(0, 0, sub_m.copy()) + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + else: + m.set_block(rank, rank, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + + v = BlockVector(3) + sub_v = np.ones(2) + for ndx in range(3): + v.set_block(ndx, sub_v.copy()) + + res = m._get_block_vector_for_dot_product(v) + + self.assertIs(res, v) + + v_flat = v.flatten() + res = m._get_block_vector_for_dot_product(v_flat) + self.assertIsInstance(res, BlockVector) + for ndx in range(3): + block = res.get_block(ndx) + self.assertTrue(np.array_equal(block, sub_v)) + + def test_matvec_1(self): + rank = comm.Get_rank() + + rank_ownership = np.array([[0, -1, -1, 0], + [-1, 1, -1, 1], + [-1, -1, 2, 2], + [0, 1, 2, -1]]) + m = MPIBlockMatrix(4, 4, rank_ownership, comm) + sub_m = np.array([[1, 0], + [0, 1]]) + sub_m = coo_matrix(sub_m) + m.set_block(rank, rank, sub_m.copy()) + m.set_block(rank, 3, sub_m.copy()) + m.set_block(3, rank, sub_m.copy()) + m.set_block(3, 3, sub_m.copy()) + + rank_ownership = np.array([0, 1, 2, -1]) + v = MPIBlockVector(4, rank_ownership, comm) + sub_v = np.ones(2) + v.set_block(rank, sub_v.copy()) + v.set_block(3, sub_v.copy()) + + res = m.dot(v) + self.assertIsInstance(res, MPIBlockVector) + self.assertTrue(np.array_equal(res.get_block(rank), sub_v*2)) + self.assertTrue(np.array_equal(res.get_block(3), sub_v*4)) + self.assertTrue(np.array_equal(res.rank_ownership, np.array([0, 1, 2, -1]))) + self.assertFalse(res.has_none) diff --git a/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_vector.py b/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_vector.py index db6fd4ce836..eab076a2409 100644 --- a/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_vector.py +++ b/pyomo/contrib/pynumero/sparse/tests/test_mpi_block_vector.py @@ -59,7 +59,6 @@ def setUpClass(cls): v1.set_block(3, np.ones(2)) cls.v1 = v1 - cls.v1.broadcast_block_sizes() v2 = MPIBlockVector(7, [0,0,1,1,2,2,-1], comm) rank = comm.Get_rank() @@ -75,7 +74,6 @@ def setUpClass(cls): v2.set_block(6, np.ones(2) * 3) cls.v2 = v2 - cls.v2.broadcast_block_sizes() def test_nblocks(self): v1 = self.v1 @@ -91,8 +89,10 @@ def test_bshape(self): def test_size(self): v1 = self.v1 + self.assertEqual(type(v1.size), int) self.assertEqual(v1.size, 10) v2 = self.v2 + self.assertEqual(type(v2.size), int) self.assertEqual(v2.size, 20) def test_shape(self): @@ -106,12 +106,18 @@ def test_ndim(self): self.assertEqual(v1.ndim, 1) def test_has_none(self): - v = MPIBlockVector(4, [0,1,0,1], comm) + v = MPIBlockVector(4, [0, 1, 0, 1], comm) rank = comm.Get_rank() if rank == 0: + self.assertTrue(v.has_none) v.set_block(0, np.ones(3)) + self.assertTrue(v.has_none) v.set_block(2, np.ones(3)) - self.assertTrue(v.has_none) + self.assertFalse(v.has_none) + elif rank == 1: + self.assertTrue(v.has_none) + else: + self.assertFalse(v.has_none) self.assertFalse(self.v1.has_none) def test_any(self): @@ -121,7 +127,6 @@ def test_any(self): v.set_block(0, np.ones(3)) if rank == 1: v.set_block(1, np.zeros(3)) - v.broadcast_block_sizes() self.assertTrue(v.any()) self.assertTrue(self.v1.any()) self.assertTrue(self.v2.any()) @@ -133,7 +138,6 @@ def test_all(self): v.set_block(0, np.ones(3)) if rank == 1: v.set_block(1, np.zeros(3)) - v.broadcast_block_sizes() self.assertFalse(v.all()) if rank == 1: v.set_block(1, np.ones(3)) @@ -148,7 +152,6 @@ def test_min(self): v.set_block(0, np.arange(3) + 10) if rank == 1: v.set_block(1, np.arange(3)) - v.broadcast_block_sizes() self.assertEqual(v.min(), 0.0) if rank == 1: v.set_block(1, -np.arange(3)) @@ -161,7 +164,6 @@ def test_min(self): if rank == 1: v.set_block(1, np.arange(3)) v.set_block(2, -np.arange(6)) - v.broadcast_block_sizes() self.assertEqual(v.min(), -5.0) self.assertEqual(self.v1.min(), 0.0) self.assertEqual(self.v2.min(), 0.0) @@ -173,7 +175,6 @@ def test_max(self): v.set_block(0, np.arange(3) + 10) if rank == 1: v.set_block(1, np.arange(3)) - v.broadcast_block_sizes() self.assertEqual(v.max(), 12.0) v = MPIBlockVector(3, [0,1,-1], comm) @@ -183,7 +184,6 @@ def test_max(self): if rank == 1: v.set_block(1, np.arange(3)) v.set_block(2, np.arange(60)) - v.broadcast_block_sizes() self.assertEqual(v.max(), 59.0) self.assertEqual(self.v1.max(), 1.0) self.assertEqual(self.v2.max(), 3.0) @@ -196,7 +196,6 @@ def test_sum(self): if rank == 1: v.set_block(1, np.arange(3) + 3) v.set_block(2, np.arange(3) + 6) - v.broadcast_block_sizes() b = np.arange(9) self.assertEqual(b.sum(), v.sum()) @@ -211,7 +210,6 @@ def test_prod(self): if rank == 1: v.set_block(1, np.ones(3)) v.set_block(2, np.ones(3)) - v.broadcast_block_sizes() self.assertEqual(1.0, v.prod()) if rank == 1: v.set_block(1, np.ones(3) * 2) @@ -230,7 +228,6 @@ def test_conj(self): if rank == 1: v.set_block(1, np.ones(3)) v.set_block(2, np.ones(3)) - v.broadcast_block_sizes() res = v.conj() self.assertTrue(isinstance(res, MPIBlockVector)) self.assertEqual(res.nblocks, v.nblocks) @@ -245,7 +242,6 @@ def test_conjugate(self): if rank == 1: v.set_block(1, np.ones(3)) v.set_block(2, np.ones(3)) - v.broadcast_block_sizes() res = v.conjugate() self.assertTrue(isinstance(res, MPIBlockVector)) self.assertEqual(res.nblocks, v.nblocks) @@ -260,7 +256,6 @@ def test_nonzero(self): if rank == 1: v.set_block(1, np.array([0,0,2])) v.set_block(2, np.ones(3)) - v.broadcast_block_sizes() res = v.nonzero()[0] self.assertTrue(isinstance(res, MPIBlockVector)) self.assertEqual(res.nblocks, v.nblocks) @@ -286,7 +281,6 @@ def test_round(self): if rank == 1: v.set_block(1, np.arange(3) + 3 + 0.01) v.set_block(2, np.arange(3) + 6 + 0.01) - v.broadcast_block_sizes() res = v.round() self.assertTrue(isinstance(res, MPIBlockVector)) @@ -305,7 +299,6 @@ def test_clip(self): if rank == 1: v.set_block(1, np.arange(3) + 3) v.set_block(2, np.arange(3) + 6) - v.broadcast_block_sizes() res = v.clip(min=2.0) self.assertTrue(isinstance(res, MPIBlockVector)) @@ -343,7 +336,6 @@ def test_compress(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() cond = MPIBlockVector(3, [0, 1, -1], comm) rank = comm.Get_rank() @@ -352,7 +344,6 @@ def test_compress(self): if rank == 1: cond.set_block(1, np.array([True, True, True, False])) cond.set_block(2, np.array([True, True])) - cond.broadcast_block_sizes() res = v.compress(cond) self.assertTrue(isinstance(res, MPIBlockVector)) @@ -458,7 +449,6 @@ def test_copyto(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() vv = MPIBlockVector(3, [0, 1, -1], comm) v.copyto(vv) @@ -482,7 +472,6 @@ def test_fill(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() v.fill(7.0) self.assertTrue(isinstance(v, MPIBlockVector)) @@ -504,7 +493,6 @@ def test_dot(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() all_v = np.concatenate([np.arange(3), np.arange(4), np.arange(2)]) expected = all_v.dot(all_v) @@ -523,7 +511,6 @@ def test_add(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() res = v + v self.assertTrue(isinstance(res, MPIBlockVector)) @@ -572,7 +559,6 @@ def test_sub(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() res = v - v self.assertTrue(isinstance(res, MPIBlockVector)) @@ -621,7 +607,6 @@ def test_mul(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() res = v * v self.assertTrue(isinstance(res, MPIBlockVector)) @@ -670,7 +655,6 @@ def test_truediv(self): if rank == 1: v.set_block(1, np.arange(4) + 1.0) v.set_block(2, np.arange(2) + 1.0) - v.broadcast_block_sizes() res = v / v self.assertTrue(isinstance(res, MPIBlockVector)) @@ -720,7 +704,6 @@ def test_floordiv(self): if rank == 1: v.set_block(1, np.arange(4) + 1.0) v.set_block(2, np.arange(2) + 1.0) - v.broadcast_block_sizes() res = v // v self.assertTrue(isinstance(res, MPIBlockVector)) @@ -777,7 +760,6 @@ def test_isum(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() v += v self.assertTrue(isinstance(v, MPIBlockVector)) @@ -795,7 +777,6 @@ def test_isum(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() v = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -804,7 +785,6 @@ def test_isum(self): if rank == 1: v.set_block(1, np.arange(4, dtype='d')) v.set_block(2, np.arange(2, dtype='d')) - v.broadcast_block_sizes() v += 7.0 self.assertTrue(isinstance(v, MPIBlockVector)) @@ -824,7 +804,6 @@ def test_isub(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() v -= v self.assertTrue(isinstance(v, MPIBlockVector)) @@ -842,7 +821,6 @@ def test_isub(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() v = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -851,7 +829,6 @@ def test_isub(self): if rank == 1: v.set_block(1, np.arange(4, dtype='d')) v.set_block(2, np.arange(2, dtype='d')) - v.broadcast_block_sizes() v -= 7.0 self.assertTrue(isinstance(v, MPIBlockVector)) @@ -871,7 +848,6 @@ def test_imul(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() v *= v self.assertTrue(isinstance(v, MPIBlockVector)) @@ -889,7 +865,6 @@ def test_imul(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() v = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -898,7 +873,6 @@ def test_imul(self): if rank == 1: v.set_block(1, np.arange(4, dtype='d')) v.set_block(2, np.arange(2, dtype='d')) - v.broadcast_block_sizes() v *= 7.0 self.assertTrue(isinstance(v, MPIBlockVector)) @@ -918,7 +892,6 @@ def test_itruediv(self): if rank == 1: v.set_block(1, np.arange(4) + 1.0) v.set_block(2, np.arange(2) + 1.0) - v.broadcast_block_sizes() v /= v self.assertTrue(isinstance(v, MPIBlockVector)) @@ -936,7 +909,6 @@ def test_itruediv(self): if rank == 1: v.set_block(1, np.arange(4) + 1.0) v.set_block(2, np.arange(2) + 1.0) - v.broadcast_block_sizes() v = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -945,7 +917,6 @@ def test_itruediv(self): if rank == 1: v.set_block(1, np.arange(4, dtype='d')) v.set_block(2, np.arange(2, dtype='d')) - v.broadcast_block_sizes() v /= 2.0 self.assertTrue(isinstance(v, MPIBlockVector)) @@ -964,7 +935,6 @@ def test_le(self): if rank == 1: v.set_block(1, np.ones(4) * 2) v.set_block(2, np.ones(2) * 4) - v.broadcast_block_sizes() v1 = MPIBlockVector(3, [0, 1, -1], comm) rank = comm.Get_rank() @@ -973,7 +943,6 @@ def test_le(self): if rank == 1: v1.set_block(1, np.ones(4) * 8) v1.set_block(2, np.ones(2) * 4) - v1.broadcast_block_sizes() res = v <= v1 @@ -1043,7 +1012,6 @@ def test_lt(self): if rank == 1: v.set_block(1, np.ones(4) * 2) v.set_block(2, np.ones(2) * 4) - v.broadcast_block_sizes() v1 = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -1052,7 +1020,6 @@ def test_lt(self): if rank == 1: v1.set_block(1, np.ones(4) * 8) v1.set_block(2, np.ones(2) * 4) - v1.broadcast_block_sizes() res = v < v1 @@ -1122,7 +1089,6 @@ def test_ge(self): if rank == 1: v.set_block(1, np.ones(4) * 2) v.set_block(2, np.ones(2) * 4) - v.broadcast_block_sizes() v1 = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -1131,7 +1097,6 @@ def test_ge(self): if rank == 1: v1.set_block(1, np.ones(4) * 8) v1.set_block(2, np.ones(2) * 4) - v1.broadcast_block_sizes() res = v >= v1 @@ -1201,7 +1166,6 @@ def test_gt(self): if rank == 1: v.set_block(1, np.ones(4) * 2) v.set_block(2, np.ones(2) * 4) - v.broadcast_block_sizes() v1 = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -1210,7 +1174,6 @@ def test_gt(self): if rank == 1: v1.set_block(1, np.ones(4) * 8) v1.set_block(2, np.ones(2) * 4) - v1.broadcast_block_sizes() res = v > v1 @@ -1280,7 +1243,6 @@ def test_eq(self): if rank == 1: v.set_block(1, np.ones(4) * 2) v.set_block(2, np.ones(2) * 4) - v.broadcast_block_sizes() v1 = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -1289,7 +1251,6 @@ def test_eq(self): if rank == 1: v1.set_block(1, np.ones(4) * 8) v1.set_block(2, np.ones(2) * 4) - v1.broadcast_block_sizes() res = v == v1 @@ -1359,7 +1320,6 @@ def test_ne(self): if rank == 1: v.set_block(1, np.ones(4) * 2) v.set_block(2, np.ones(2) * 4) - v.broadcast_block_sizes() v1 = MPIBlockVector(3, [0,1,-1], comm) rank = comm.Get_rank() @@ -1368,7 +1328,6 @@ def test_ne(self): if rank == 1: v1.set_block(1, np.ones(4) * 8) v1.set_block(2, np.ones(2) * 4) - v1.broadcast_block_sizes() res = v != v1 @@ -1482,7 +1441,6 @@ def test_reduce_ufuncs(self): v.set_block(0, np.ones(3) * 0.5) if rank == 1: v.set_block(1, np.ones(2) * 0.8) - v.broadcast_block_sizes() bv = BlockVector(2) bv.set_block(0, np.ones(3) * 0.5) @@ -1605,23 +1563,10 @@ def test_contains(self): v.set_block(0, np.ones(3)) if rank == 1: v.set_block(1, np.zeros(2)) - v.broadcast_block_sizes() self.assertTrue(0 in v) self.assertFalse(3 in v) - def test_len(self): - - v = MPIBlockVector(2, [0,1], comm) - - rank = comm.Get_rank() - if rank == 0: - v.set_block(0, np.ones(3)) - if rank == 1: - v.set_block(1, np.zeros(2)) - v.broadcast_block_sizes() - self.assertEqual(len(v), 2) - def test_copyfrom(self): v = MPIBlockVector(3, [0,1,-1], comm) @@ -1631,7 +1576,6 @@ def test_copyfrom(self): if rank == 1: v.set_block(1, np.arange(4)) v.set_block(2, np.arange(2)) - v.broadcast_block_sizes() bv = BlockVector(3) bv.set_blocks([np.arange(3), np.arange(4), np.arange(2)]) diff --git a/pyomo/contrib/pynumero/src/CMakeLists.txt b/pyomo/contrib/pynumero/src/CMakeLists.txt index 001e1319175..1258519e545 100644 --- a/pyomo/contrib/pynumero/src/CMakeLists.txt +++ b/pyomo/contrib/pynumero/src/CMakeLists.txt @@ -23,6 +23,9 @@ OPTION(BUILD_AMPLMP_IF_NEEDED "Automatically enable AMPLMP build if ASL not found" OFF) MARK_AS_ADVANCED(BUILD_AMPLMP_IF_NEEDED) +OPTION(ENABLE_HSL "Enable the HSL library interfaces" ON) +MARK_AS_ADVANCED(ENABLE_HSL) + #OPTION(STATIC_LINK "STATIC_LINK" OFF) # If we build AMPLMP, then we will get a dependency on dlopen @@ -48,7 +51,7 @@ ENDIF() # cmake does not search LD_LIBRARY_PATH by default. So that libraries # like HSL can be added through mechanisms like 'environment modules', -# we will explicitly add LD_LIBRARY_PATH to teh search path +# we will explicitly add LD_LIBRARY_PATH to the search path string(REPLACE ":" ";" LD_LIBRARY_DIR_LIST $ENV{LD_LIBRARY_PATH}:$ENV{DYLD_LIBRARY_PATH} ) @@ -98,6 +101,12 @@ IF( MA27_LIBRARY OR MA27_OBJECT ) set_property(CACHE BUILD_MA27 PROPERTY VALUE ON) ENDIF() +#...but if the HSL interface is not enabled, do not build the MA* libraries +IF( NOT ENABLE_HSL ) + set_property(CACHE BUILD_MA27 PROPERTY VALUE OFF) + set_property(CACHE BUILD_MA57 PROPERTY VALUE OFF) +ENDIF() + # If BUILD_AMPLMP_IF_NEEDED is set and we couldn't find / weren't # pointed to an ASL build, then we will forcibly enable the AMPLMP build # to provide the ASL. diff --git a/pyomo/contrib/pynumero/tests/test_cyipopt_examples.py b/pyomo/contrib/pynumero/tests/test_cyipopt_examples.py new file mode 100644 index 00000000000..09cd3982289 --- /dev/null +++ b/pyomo/contrib/pynumero/tests/test_cyipopt_examples.py @@ -0,0 +1,99 @@ +import os.path +from pyomo.common.fileutils import this_file_dir +import pyutilib.th as unittest +from pyutilib.misc import import_file +import pyomo.environ as pyo + +from pyomo.contrib.pynumero.dependencies import ( + numpy as np, numpy_available, scipy_sparse as spa, scipy_available +) +if not (numpy_available and scipy_available): + raise unittest.SkipTest("Pynumero needs scipy and numpy to run CyIpopt tests") + +from pyomo.contrib.pynumero.asl import AmplInterface +if not AmplInterface.available(): + raise unittest.SkipTest( + "Pynumero needs the ASL extension to run CyIpopt tests") + +import pyomo.contrib.pynumero.algorithms.solvers.cyipopt_solver as cyipopt_solver +if not cyipopt_solver.ipopt_available: + raise unittest.SkipTest("PyNumero needs CyIpopt installed to run CyIpopt tests") +import cyipopt as cyipopt_core + +example_dir = os.path.join(this_file_dir(), '..', 'examples') + +class TestPyomoCyIpoptSolver(unittest.TestCase): + def test_status_maps(self): + self.assertEqual(len(cyipopt_core.STATUS_MESSAGES), + len(cyipopt_solver._cyipopt_status_enum)) + self.assertEqual(len(cyipopt_core.STATUS_MESSAGES), + len(cyipopt_solver._ipopt_term_cond)) + for msg in cyipopt_core.STATUS_MESSAGES.values(): + self.assertIn(msg, cyipopt_solver._cyipopt_status_enum) + for status in cyipopt_solver._cyipopt_status_enum.values(): + self.assertIn(status, cyipopt_solver._ipopt_term_cond) + + +class TestExamples(unittest.TestCase): + def test_external_grey_box_react_example_maximize_cb_outputs(self): + ex = import_file(os.path.join(example_dir, 'external_grey_box', 'react-example', 'maximize_cb_outputs.py')) + m = ex.maximize_cb_outputs() + self.assertAlmostEqual(pyo.value(m.reactor.inputs['sv']), 1.34381, places=3) + self.assertAlmostEqual(pyo.value(m.reactor.outputs['cb']), 1072.4372, places=2) + + def test_external_grey_box_react_example_maximize_cb_outputs_scaling(self): + ex = import_file(os.path.join(example_dir, 'external_grey_box', 'react-example', 'maximize_cb_ratio_residuals.py')) + aoptions={'nlp_scaling_method': 'user-scaling', + 'output_file': '_cyipopt-external-greybox-react-scaling.log', + 'file_print_level':10} + m = ex.maximize_cb_ratio_residuals_with_output_scaling(additional_options=aoptions) + self.assertAlmostEqual(pyo.value(m.reactor.inputs['sv']), 1.26541996, places=3) + self.assertAlmostEqual(pyo.value(m.reactor.inputs['cb']), 1071.7410089, places=2) + self.assertAlmostEqual(pyo.value(m.reactor.outputs['cb_ratio']), 0.15190409266, places=3) + + with open('_cyipopt-external-greybox-react-scaling.log', 'r') as fd: + solver_trace = fd.read() + os.remove('_cyipopt-external-greybox-react-scaling.log') + + self.assertIn('nlp_scaling_method = user-scaling', solver_trace) + self.assertIn('output_file = _cyipopt-external-greybox-react-scaling.log', solver_trace) + self.assertIn('objective scaling factor = 1', solver_trace) + self.assertIn('x scaling provided', solver_trace) + self.assertIn('c scaling provided', solver_trace) + self.assertIn('d scaling provided', solver_trace) + self.assertIn('DenseVector "x scaling vector" with 7 elements:', solver_trace) + self.assertIn('x scaling vector[ 1]= 1.2000000000000000e+00', solver_trace) + self.assertIn('x scaling vector[ 2]= 1.7000000000000000e+00', solver_trace) + self.assertIn('x scaling vector[ 3]= 1.1000000000000001e+00', solver_trace) + self.assertIn('x scaling vector[ 4]= 1.3000000000000000e+00', solver_trace) + self.assertIn('x scaling vector[ 5]= 1.3999999999999999e+00', solver_trace) + self.assertIn('x scaling vector[ 6]= 1.5000000000000000e+00', solver_trace) + self.assertIn('x scaling vector[ 7]= 1.6000000000000001e+00', solver_trace) + self.assertIn('DenseVector "c scaling vector" with 6 elements:', solver_trace) + self.assertIn('c scaling vector[ 1]= 4.2000000000000000e+01', solver_trace) + self.assertIn('c scaling vector[ 2]= 1.0000000000000001e-01', solver_trace) + self.assertIn('c scaling vector[ 3]= 2.0000000000000001e-01', solver_trace) + self.assertIn('c scaling vector[ 4]= 2.9999999999999999e-01', solver_trace) + self.assertIn('c scaling vector[ 5]= 4.0000000000000002e-01', solver_trace) + self.assertIn('c scaling vector[ 6]= 1.0000000000000000e+01', solver_trace) + + def test_external_grey_box_react_example_maximize_with_output(self): + ex = import_file(os.path.join(example_dir, 'external_grey_box', 'react-example', 'maximize_cb_ratio_residuals.py')) + m = ex.maximize_cb_ratio_residuals_with_output() + self.assertAlmostEqual(pyo.value(m.reactor.inputs['sv']), 1.26541996, places=3) + self.assertAlmostEqual(pyo.value(m.reactor.inputs['cb']), 1071.7410089, places=2) + self.assertAlmostEqual(pyo.value(m.reactor.outputs['cb_ratio']), 0.15190409266, places=3) + + def test_external_grey_box_react_example_maximize_with_obj(self): + ex = import_file(os.path.join(example_dir, 'external_grey_box', 'react-example', 'maximize_cb_ratio_residuals.py')) + m = ex.maximize_cb_ratio_residuals_with_obj() + self.assertAlmostEqual(pyo.value(m.reactor.inputs['sv']), 1.26541996, places=3) + self.assertAlmostEqual(pyo.value(m.reactor.inputs['cb']), 1071.7410089, places=2) + self.assertAlmostEqual(pyo.value(m.obj), 0.15190409266, places=3) + + def test_external_grey_box_react_example_maximize_with_additional_pyomo_variables(self): + ex = import_file(os.path.join(example_dir, 'external_grey_box', 'react-example', 'maximize_cb_ratio_residuals.py')) + m = ex.maximize_cb_ratio_residuals_with_pyomo_variables() + self.assertAlmostEqual(pyo.value(m.reactor.inputs['sv']), 1.26541996, places=3) + self.assertAlmostEqual(pyo.value(m.reactor.inputs['cb']), 1071.7410089, places=2) + self.assertAlmostEqual(pyo.value(m.cb_ratio), 0.15190409266, places=3) diff --git a/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py b/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py index b52197589a6..16e744e0b99 100755 --- a/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py @@ -14,9 +14,10 @@ # from __future__ import division -from pyomo.environ import * +from pyomo.environ import (ConcreteModel, Param, Var, Objective, + Constraint, Set, Expression, Suffix, + value, exp, TransformationFactory) from pyomo.dae import ContinuousSet, DerivativeVar -from pyomo.core import TransformationFactory from pyomo.dae.simulator import Simulator from pyomo.contrib.sensitivity_toolbox.sens import sipopt @@ -24,7 +25,7 @@ def create_model(): m = ConcreteModel() - + m.tf = Param(initialize=20) m.t = ContinuousSet(bounds=(0,m.tf)) m.i = Set(initialize=[0,1,2,3,4,5],ordered=True) @@ -276,12 +277,3 @@ def initialize_model(m,n_sim,n_nfe,n_ncp): m_sipopt = sipopt(m,[m.eps,m.qq,m.aa], [m.epsDelta,m.qqDelta,m.aaDelta], streamSoln = True) - - - - - - - - - diff --git a/pyomo/contrib/sensitivity_toolbox/examples/feedbackController.py b/pyomo/contrib/sensitivity_toolbox/examples/feedbackController.py index 30d33634431..d1f0530c7b6 100644 --- a/pyomo/contrib/sensitivity_toolbox/examples/feedbackController.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/feedbackController.py @@ -17,9 +17,8 @@ # # min J(u) = (1/2)*H*x^2(T)+\int_0^T ((1/4)*u^2(t)dt) -from pyomo.environ import * -from pyomo.core import TransformationFactory -from pyomo.opt import SolverFactory +from pyomo.environ import (ConcreteModel, Param, Var, Objective, Constraint, + Suffix, value, TransformationFactory, SolverFactory) from pyomo.dae import ContinuousSet, DerivativeVar from pyomo.dae.simulator import Simulator from pyomo.contrib.sensitivity_toolbox.sens import sipopt diff --git a/pyomo/contrib/sensitivity_toolbox/examples/parameter.py b/pyomo/contrib/sensitivity_toolbox/examples/parameter.py index 5f594ee7c82..88fd21c8116 100644 --- a/pyomo/contrib/sensitivity_toolbox/examples/parameter.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/parameter.py @@ -14,8 +14,8 @@ # from __future__ import print_function -from pyomo.environ import * -from pyomo.contrib.sensitivity_toolbox.sens import sipopt +from pyomo.environ import ConcreteModel, Param, Var, Objective, Constraint, NonNegativeReals, value +from pyomo.contrib.sensitivity_toolbox.sens import sensitivity_calculation def create_model(): ''' Create a concrete Pyomo model for this example @@ -52,9 +52,9 @@ def run_example(print_flag=True): m.perturbed_eta2 = Param(initialize = 1.0) - m_sipopt = sipopt(m,[m.eta1,m.eta2], - [m.perturbed_eta1,m.perturbed_eta2], - streamSoln=True) + m_sipopt = sensitivity_calculation('sipopt',m,[m.eta1,m.eta2], + [m.perturbed_eta1,m.perturbed_eta2], + streamSoln=True) diff --git a/pyomo/contrib/sensitivity_toolbox/examples/parameter_kaug.py b/pyomo/contrib/sensitivity_toolbox/examples/parameter_kaug.py index 41511d5d8ef..3161c8a563a 100644 --- a/pyomo/contrib/sensitivity_toolbox/examples/parameter_kaug.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/parameter_kaug.py @@ -15,7 +15,7 @@ from pyomo.environ import * -from pyomo.contrib.sensitivity_toolbox.sens import kaug +from pyomo.contrib.sensitivity_toolbox.sens import sensitivity_calculation def create_model(): ''' Create a concrete Pyomo model for this example @@ -35,7 +35,7 @@ def create_model(): return m -def example(print_flag=True): +def run_example(print_flag=True): ''' Execute the example @@ -52,11 +52,9 @@ def example(print_flag=True): m.perturbed_eta2 = Param(initialize = 1.0) - m_kaug_dsdp = kaug(m,[m.eta1,m.eta2], - [m.perturbed_eta1,m.perturbed_eta2], - streamSoln=True) - - + m_kaug_dsdp = sensitivity_calculation('kaug',m,[m.eta1,m.eta2], + [m.perturbed_eta1,m.perturbed_eta2], + streamSoln=True) if print_flag: print("\nOriginal parameter values:") @@ -99,4 +97,4 @@ def example(print_flag=True): return d if __name__=='__main__': - d = example() + d = run_example() diff --git a/pyomo/contrib/sensitivity_toolbox/examples/rangeInequality.py b/pyomo/contrib/sensitivity_toolbox/examples/rangeInequality.py index b6097c9eda4..9b55b6ee0bf 100644 --- a/pyomo/contrib/sensitivity_toolbox/examples/rangeInequality.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/rangeInequality.py @@ -9,9 +9,8 @@ # ___________________________________________________________________________ # -from pyomo.environ import * -from pyomo.opt import SolverFactory -from pyomo.dae import ContinuousSet +from pyomo.environ import ConcreteModel, Param, Var, Constraint, inequality + from pyomo.contrib.sensitivity_toolbox.sens import sipopt diff --git a/pyomo/contrib/sensitivity_toolbox/sens.py b/pyomo/contrib/sensitivity_toolbox/sens.py index 114063fa532..3aa238edd3c 100644 --- a/pyomo/contrib/sensitivity_toolbox/sens.py +++ b/pyomo/contrib/sensitivity_toolbox/sens.py @@ -7,21 +7,21 @@ # rights in this software. # This software is distributed under the 3-clause BSD License # ______________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import Param, Var, Block, ComponentMap, Objective, Constraint, ConstraintList, Suffix, value -from pyomo.core.base import _ConstraintData, _ObjectiveData, _ExpressionData from pyomo.core.base.misc import sorted_robust -from pyomo.core.expr.current import (clone_expression, identify_variables, - ExpressionReplacementVisitor) +from pyomo.core.expr.current import ExpressionReplacementVisitor from pyomo.common.modeling import unique_component_name from pyomo.common.deprecation import deprecated from pyomo.opt import SolverFactory import logging -_log = logging.getLogger('__name__') +_log = logging.getLogger('pyomo.contrib.sensitivity_toolbox') -@deprecated('The sipopt function has been deprecated. Use the sensitivity_calculation() function with method="sipopt" to access this functionality.', +@deprecated("The sipopt function has been deprecated. Use the sensitivity_calculation() " + "function with method='sipopt' to access this functionality.", + logger='pyomo.contrib.sensitivity_toolbox', version='TBD') def sipopt(instance, paramSubList, perturbList, cloneModel=True, streamSoln=False, keepfiles=False): @@ -30,7 +30,8 @@ def sipopt(instance, paramSubList, perturbList, return m -@deprecated('The kaug function has been deprecated. Use the sensitivity_calculation() function with method="kaug" to access this functionality.', +@deprecated("The kaug function has been deprecated. Use the sensitivity_calculation() " + "function with method='kaug' to access this functionality.", version='TBD') def kaug(instance, paramSubList, perturbList, cloneModel=True, streamSoln=False, keepfiles=False, optarg=None): diff --git a/pyomo/contrib/sensitivity_toolbox/tests/test_sens.py b/pyomo/contrib/sensitivity_toolbox/tests/test_sens.py index f3fe0473ff4..5c6e11ae4f9 100644 --- a/pyomo/contrib/sensitivity_toolbox/tests/test_sens.py +++ b/pyomo/contrib/sensitivity_toolbox/tests/test_sens.py @@ -13,13 +13,18 @@ """ import pyutilib.th as unittest +from six import StringIO +import logging -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Param, Var, Block, Suffix, value from pyomo.opt import SolverFactory from pyomo.dae import ContinuousSet from pyomo.common.dependencies import scipy_available +from pyomo.common.log import LoggingIntercept from pyomo.core.expr.current import identify_variables -from pyomo.contrib.sensitivity_toolbox.sens import sipopt, kaug +from pyomo.contrib.sensitivity_toolbox.sens import sipopt, kaug, sensitivity_calculation +import pyomo.contrib.sensitivity_toolbox.examples.parameter as param_ex +import pyomo.contrib.sensitivity_toolbox.examples.parameter_kaug as param_kaug_ex import pyomo.contrib.sensitivity_toolbox.examples.feedbackController as fc import pyomo.contrib.sensitivity_toolbox.examples.rangeInequality as ri import pyomo.contrib.sensitivity_toolbox.examples.HIV_Transmission as hiv @@ -28,6 +33,87 @@ opt_kaug = SolverFactory('k_aug',solver_io='nl') opt_dotsens = SolverFactory('dot_sens',solver_io='nl') + +class FunctionDeprecationTest(unittest.TestCase): + + @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") + def test_sipopt_deprecated(self): + m = param_ex.create_model() + m.perturbed_eta1 = Param(initialize = 4.0) + m.perturbed_eta2 = Param(initialize = 1.0) + + output = StringIO() + with LoggingIntercept(output, 'pyomo.contrib.sensitivity_toolbox', logging.WARNING): + sipopt(m,[m.eta1,m.eta1], + [m.perturbed_eta1,m.perturbed_eta2], + cloneModel=False) + self.assertIn("DEPRECATED: The sipopt function has been deprecated. Use the " + "sensitivity_calculation() function with method='sipopt' to access", + output.getvalue().replace('\n', ' ')) + + + @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") + def test_sipopt_equivalent(self): + m1 = param_ex.create_model() + m1.perturbed_eta1 = Param(initialize = 4.0) + m1.perturbed_eta2 = Param(initialize = 1.0) + + m2 = param_ex.create_model() + m2.perturbed_eta1 = Param(initialize = 4.0) + m2.perturbed_eta2 = Param(initialize = 1.0) + + m11 = sipopt(m1,[m1.eta1,m1.eta2], + [m1.perturbed_eta1,m1.perturbed_eta2], + cloneModel=True) + m22 = sensitivity_calculation('sipopt',m2,[m2.eta1,m2.eta2], + [m2.perturbed_eta1,m2.perturbed_eta2], + cloneModel=True) + out1 = StringIO() + out2 = StringIO() + m11._sipopt_data.constList.pprint(ostream=out1) + m22._sipopt_data.constList.pprint(ostream=out2) + self.assertMultiLineEqual(out1.getvalue(), out2.getvalue()) + + @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") + @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") + def test_kaug_deprecated(self): + m = param_ex.create_model() + m.perturbed_eta1 = Param(initialize = 4.0) + m.perturbed_eta2 = Param(initialize = 1.0) + + output = StringIO() + with LoggingIntercept(output, 'pyomo.contrib.sensitivity_toolbox', logging.WARNING): + kaug(m,[m.eta1,m.eta1], + [m.perturbed_eta1,m.perturbed_eta2], + cloneModel=False) + self.assertIn("DEPRECATED: The kaug function has been deprecated. Use the " + "sensitivity_calculation() function with method='kaug'", + output.getvalue().replace('\n', ' ')) + + @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") + @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") + def test_kaug_equivalent(self): + m1 = param_ex.create_model() + m1.perturbed_eta1 = Param(initialize = 4.0) + m1.perturbed_eta2 = Param(initialize = 1.0) + + m2 = param_ex.create_model() + m2.perturbed_eta1 = Param(initialize = 4.0) + m2.perturbed_eta2 = Param(initialize = 1.0) + + m11 = kaug(m1,[m1.eta1,m1.eta2], + [m1.perturbed_eta1,m1.perturbed_eta2], + cloneModel=True) + m22 = sensitivity_calculation('kaug',m2,[m2.eta1,m2.eta2], + [m2.perturbed_eta1,m2.perturbed_eta2], + cloneModel=True) + out1 = StringIO() + out2 = StringIO() + m11.pprint(ostream=out1) + m22.pprint(ostream=out2) + self.assertMultiLineEqual(out1.getvalue(), out2.getvalue()) + + class TestSensitivityToolbox(unittest.TestCase): # test arguments @@ -49,35 +135,30 @@ def test_bad_arg(self): # verify ValueError thrown when param and perturb list are different # lengths - try: - Result = sipopt(m,list_one,list_two) - self.fail("Expected ValueError: for different length lists") - except ValueError: - pass + with self.assertRaises(ValueError) as context: + Result = sensitivity_calculation('sipopt',m,list_one,list_two) + self.assertTrue("Length of paramSubList argument does not equal " + "length of perturbList" in str(context.exception)) # verify ValueError thrown when param list has a Var in it - try: - Result = sipopt(m,list_three,list_two) - self.fail("Expected ValueError: variable sent through paramSubList") - except ValueError: - pass + with self.assertRaises(ValueError) as context: + Result = sensitivity_calculation('sipopt',m,list_three,list_one) + self.assertTrue("paramSubList argument is expecting a list of Params" in str(context.exception)) # verify ValueError thrown when perturb list has Var in it - try: - Result = sipopt(m,list_one,list_three) - self.fail("Expected ValueError: variable sent through perturbList") - except ValueError: - pass + with self.assertRaises(ValueError) as context: + Result = sensitivity_calculation('sipopt',m,list_one,list_three) + self.assertTrue("perturbList argument is expecting a list of Params" in str(context.exception)) # verify ValueError thrown when param list has an unmutable param - try: - Result = sipopt(m,list_four,list_one) - self.fail("Expected ValueError:" - "unmutable param sent through paramSubList") - except ValueError: - pass - + with self.assertRaises(ValueError) as context: + Result = sensitivity_calculation('sipopt',m,list_four,list_one) + self.assertTrue("parameters within paramSubList must be mutable" in str(context.exception)) + # verify ValueError thrown when an invalid method is specified + with self.assertRaises(ValueError) as context: + Result = sensitivity_calculation('foo',m,list_four,list_one) + self.assertTrue("method should be 'sipopt' or 'kaug'" in str(context.exception)) # test feedbackController Solution when the model gets cloned @unittest.skipIf(not scipy_available, "scipy is required for this test") @@ -90,7 +171,7 @@ def test_clonedModel_soln(self): m_orig.perturbed_a = Param(initialize=-0.25) m_orig.perturbed_H = Param(initialize=0.55) - m_sipopt = sipopt(m_orig,[m_orig.a,m_orig.H], + m_sipopt = sensitivity_calculation('sipopt',m_orig,[m_orig.a,m_orig.H], [m_orig.perturbed_a,m_orig.perturbed_H], cloneModel=True) @@ -185,7 +266,7 @@ def test_noClone_soln(self): m_orig.perturbed_a = Param(initialize=-0.25) m_orig.perturbed_H = Param(initialize=0.55) - m_sipopt = sipopt(m_orig,[m_orig.a,m_orig.H], + m_sipopt = sensitivity_calculation('sipopt',m_orig,[m_orig.a,m_orig.H], [m_orig.perturbed_a,m_orig.perturbed_H], cloneModel=False) @@ -257,8 +338,6 @@ def test_noClone_soln(self): self.assertAlmostEqual(value(m_sipopt.J),0.0048956783,8) - - # test indexed param mapping to var and perturbed values @unittest.skipIf(not scipy_available, "scipy is required for this test") @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") @@ -282,7 +361,7 @@ def test_indexedParamsMapping(self): m.aaDelta = Param(initialize =0.0001001) - m_sipopt = sipopt(m, [m.eps,m.qq,m.aa], + m_sipopt = sensitivity_calculation('sipopt',m, [m.eps,m.qq,m.aa], [m.epsDelta,m.qqDelta,m.aaDelta]) # param to var data @@ -316,7 +395,7 @@ def test_constraintSub(self): m.pert_a = Param(initialize=0.01) m.pert_b = Param(initialize=1.01) - m_sipopt = sipopt(m,[m.a,m.b], [m.pert_a,m.pert_b]) + m_sipopt = sensitivity_calculation('sipopt',m,[m.a,m.b], [m.pert_a,m.pert_b]) # verify substitutions in equality constraint self.assertTrue(m_sipopt.C_equal.lower.ctype is Param and @@ -357,8 +436,7 @@ def test_constraintSub(self): @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") def test_parameter_example(self): - from pyomo.contrib.sensitivity_toolbox.examples.parameter import run_example - d = run_example() + d = param_ex.run_example() d_correct = {'eta1':4.5, 'eta2':1.0, 'x1_init':0.15, 'x2_init':0.15, 'x3_init':0.0, 'cost_sln':0.5, 'x1_sln':0.5, 'x2_sln':0.5, 'x3_sln':0.0, 'eta1_pert':4.0, @@ -372,53 +450,7 @@ def test_parameter_example(self): # Test kaug - # Perform the same tests as ipopt_sens - # test arguments - @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") - @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") - def test_kaug_bad_arg_kaug(self): - m = ConcreteModel() - m.t = ContinuousSet(bounds=(0,1)) - - m.a = Param(initialize=1, mutable=True) - m.b = Param(initialize=2, mutable=True) - m.c = Param(initialize=3, mutable=False) - - m.x = Var(m.t) - - list_one = [m.a,m.b] - list_two = [m.a,m.b,m.c] - list_three = [m.a, m.x] - list_four = [m.a,m.c] - - # verify ValueError thrown when param and perturb list are different - # lengths - with self.assertRaises(ValueError): - Result = kaug(m, list_one, list_two) - - # verify ValueError thrown when param list has a Var in it - try: - Result = kaug(m,list_three,list_two) - self.fail("Expected ValueError: variable sent through paramSubList") - except ValueError: - pass - - # verify ValueError thrown when perturb list has Var in it - try: - Result = kaug(m,list_one,list_three) - self.fail("Expected ValueError: variable sent through perturbList") - except ValueError: - pass - - # verify ValueError thrown when param list has an unmutable param - try: - Result = kaug(m,list_four,list_one) - self.fail("Expected ValueError:" - "unmutable param sent through paramSubList") - except ValueError: - pass - - + # Perform the same tests as for sipopt # test feedbackController Solution when the model gets cloned @unittest.skipIf(not scipy_available, "scipy is required for this test") @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") @@ -430,7 +462,7 @@ def test_kaug_clonedModel_soln_kaug(self): m_orig.perturbed_a = Param(initialize=-0.25) m_orig.perturbed_H = Param(initialize=0.55) - m_kaug = kaug(m_orig,[m_orig.a,m_orig.H], + m_kaug = sensitivity_calculation('kaug',m_orig,[m_orig.a,m_orig.H], [m_orig.perturbed_a,m_orig.perturbed_H], cloneModel=True) @@ -525,7 +557,7 @@ def test_noClone_soln_kaug(self): m_orig.perturbed_a = Param(initialize=-0.25) m_orig.perturbed_H = Param(initialize=0.55) - m_kaug = kaug(m_orig,[m_orig.a,m_orig.H], + m_kaug = sensitivity_calculation('kaug',m_orig,[m_orig.a,m_orig.H], [m_orig.perturbed_a,m_orig.perturbed_H], cloneModel=False) @@ -612,7 +644,7 @@ def test_indexedParamsMapping_kaug(self): m.aaDelta = Param(initialize =0.0001001) - m_kaug = kaug(m, [m.eps,m.qq,m.aa], + m_kaug = sensitivity_calculation('kaug',m, [m.eps,m.qq,m.aa], [m.epsDelta,m.qqDelta,m.aaDelta]) # param to var data @@ -648,7 +680,7 @@ def test_constraintSub_kaug(self): # m_kaug = kaug(m,[m.a,m.b], [m.pert_a,m.pert_b]) # verify ValueError thrown when param list has an unmutable param with self.assertRaises(Exception) as context: - m_kaug = kaug(m,[m.a,m.b], [m.pert_a,m.pert_b]) + m_kaug = sensitivity_calculation('kaug',m,[m.a,m.b], [m.pert_a,m.pert_b]) self.assertTrue('kaug does not support inequality constraints.' in str(context.exception)) # Test example `parameter_kaug.py` @@ -656,8 +688,7 @@ def test_constraintSub_kaug(self): @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") def test_parameter_example_kaug(self): - from parameter_kaug import example - d = example() + d = param_kaug_ex.run_example() d_correct = {'eta1':4.5, 'eta2':1.0, 'x1_init':0.15, 'x2_init':0.15, 'x3_init':0.0, 'eta1_pert':4.0, 'eta2_pert':1.0, 'x1_pert':0.3333333,'x2_pert':0.6666667, diff --git a/pyomo/contrib/sensitivity_toolbox/tests/test_sens_sensitivity_calculation.py b/pyomo/contrib/sensitivity_toolbox/tests/test_sens_sensitivity_calculation.py deleted file mode 100644 index aaef6b4d088..00000000000 --- a/pyomo/contrib/sensitivity_toolbox/tests/test_sens_sensitivity_calculation.py +++ /dev/null @@ -1,743 +0,0 @@ -# ____________________________________________________________________________ -# -# Pyomo: Python Optimization Modeling Objects -# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC -# Under the terms of Contract DE-NA0003525 with National Technology and -# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain -# rights in this software. -# This software is distributed under the 3-clause BSD License. -# ____________________________________________________________________________ - -""" -Unit Tests for interfacing with sensitivity_calculation -""" - -import pyutilib.th as unittest - -from pyomo.environ import * -from pyomo.opt import SolverFactory -from pyomo.dae import ContinuousSet -from pyomo.common.dependencies import scipy_available -from pyomo.core.expr.current import identify_variables -from pyomo.contrib.sensitivity_toolbox.sens import sensitivity_calculation -import pyomo.contrib.sensitivity_toolbox.examples.feedbackController as fc -import pyomo.contrib.sensitivity_toolbox.examples.rangeInequality as ri -import pyomo.contrib.sensitivity_toolbox.examples.HIV_Transmission as hiv - -opt = SolverFactory('ipopt_sens', solver_io='nl') -opt_kaug = SolverFactory('k_aug',solver_io='nl') -opt_dotsens = SolverFactory('dot_sens',solver_io='nl') - -class TestSensitivityToolbox(unittest.TestCase): - - # test arguments - @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") - def test_bad_arg(self): - m = ConcreteModel() - m.t = ContinuousSet(bounds=(0,1)) - - m.a = Param(initialize=1, mutable=True) - m.b = Param(initialize=2, mutable=True) - m.c = Param(initialize=3, mutable=False) - - m.x = Var(m.t) - - list_one = [m.a,m.b] - list_two = [m.a,m.b,m.c] - list_three = [m.a, m.x] - list_four = [m.a,m.c] - - # verify ValueError thrown when param and perturb list are different - # lengths - try: - Result = sensitivity_calculation('sipopt', m,list_one,list_two) - self.fail("Expected ValueError: for different length lists") - except ValueError: - pass - - # verify ValueError thrown when param list has a Var in it - try: - Result = sensitivity_calculation('sipopt', m,list_three,list_two) - self.fail("Expected ValueError: variable sent through paramSubList") - except ValueError: - pass - - # verify ValueError thrown when perturb list has Var in it - try: - Result = sensitivity_calculation('sipopt', m,list_one,list_three) - self.fail("Expected ValueError: variable sent through perturbList") - except ValueError: - pass - - # verify ValueError thrown when param list has an unmutable param - try: - Result = sensitivity_calculation('sipopt', m,list_four,list_one) - self.fail("Expected ValueError:" - "unmutable param sent through paramSubList") - except ValueError: - pass - - - - # test feedbackController Solution when the model gets cloned - @unittest.skipIf(not scipy_available, "scipy is required for this test") - @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") - def test_clonedModel_soln(self): - - m_orig = fc.create_model() - fc.initialize_model(m_orig,100) - - m_orig.perturbed_a = Param(initialize=-0.25) - m_orig.perturbed_H = Param(initialize=0.55) - - m_sipopt = sensitivity_calculation('sipopt', m_orig,[m_orig.a,m_orig.H], - [m_orig.perturbed_a,m_orig.perturbed_H], - cloneModel=True) - - # verify cloned model has _sipopt_data block - # and original model is untouched - self.assertFalse(m_sipopt == m_orig) - - self.assertTrue(hasattr(m_sipopt,'_sipopt_data') and - m_sipopt._sipopt_data.ctype is Block) - - self.assertFalse(hasattr(m_orig,'_sipopt_data')) - self.assertFalse(hasattr(m_orig,'b')) - - # verify variable declaration - self.assertTrue(hasattr(m_sipopt._sipopt_data,'a') and - m_sipopt._sipopt_data.a.ctype is Var) - self.assertTrue(hasattr(m_sipopt._sipopt_data,'H') and - m_sipopt._sipopt_data.H.ctype is Var) - - # verify suffixes - self.assertTrue(hasattr(m_sipopt,'sens_state_0') and - m_sipopt.sens_state_0.ctype is Suffix and - m_sipopt.sens_state_0[m_sipopt._sipopt_data.H]==2 and - m_sipopt.sens_state_0[m_sipopt._sipopt_data.a]==1) - - self.assertTrue(hasattr(m_sipopt,'sens_state_1') and - m_sipopt.sens_state_1.ctype is Suffix and - m_sipopt.sens_state_1[m_sipopt._sipopt_data.H]==2 and - m_sipopt.sens_state_1[m_sipopt._sipopt_data.a]==1) - - self.assertTrue(hasattr(m_sipopt,'sens_state_value_1') and - m_sipopt.sens_state_value_1.ctype is Suffix and - m_sipopt.sens_state_value_1[ - m_sipopt._sipopt_data.H]==0.55 and - m_sipopt.sens_state_value_1[ - m_sipopt._sipopt_data.a]==-0.25) - - self.assertTrue(hasattr(m_sipopt,'sens_init_constr') and - m_sipopt.sens_init_constr.ctype is Suffix and - m_sipopt.sens_init_constr[ - m_sipopt._sipopt_data.paramConst[1]]==1 and - m_sipopt.sens_init_constr[ - m_sipopt._sipopt_data.paramConst[2]]==2) - - self.assertTrue(hasattr(m_sipopt,'sens_sol_state_1') and - m_sipopt.sens_sol_state_1.ctype is Suffix) - self.assertAlmostEqual( - m_sipopt.sens_sol_state_1[ - m_sipopt.F[15]],-0.00102016765,8) - - self.assertTrue(hasattr(m_sipopt,'sens_sol_state_1_z_L') and - m_sipopt.sens_sol_state_1_z_L.ctype is Suffix) - self.assertAlmostEqual( - m_sipopt.sens_sol_state_1_z_L[ - m_sipopt.u[15]],-2.181712e-09,13) - - self.assertTrue(hasattr(m_sipopt,'sens_sol_state_1_z_U') and - m_sipopt.sens_sol_state_1_z_U.ctype is Suffix) - self.assertAlmostEqual( - m_sipopt.sens_sol_state_1_z_U[ - m_sipopt.u[15]],6.580899e-09,13) - - # verify deactivated constraints for cloned model - self.assertFalse(m_sipopt.FDiffCon[0].active and - m_sipopt.FDiffCon[7.5].active and - m_sipopt.FDiffCon[15].active ) - - self.assertFalse(m_sipopt.x_dot[0].active and - m_sipopt.x_dot[7.5].active and - m_sipopt.x_dot[15].active ) - - # verify constraints on original model are still active - self.assertTrue(m_orig.FDiffCon[0].active and - m_orig.FDiffCon[7.5].active and - m_orig.FDiffCon[15].active ) - - self.assertTrue(m_orig.x_dot[0].active and - m_orig.x_dot[7.5].active and - m_orig.x_dot[15].active ) - - # verify solution - self.assertAlmostEqual(value(m_sipopt.J),0.0048956783,8) - - - @unittest.skipIf(not scipy_available, "scipy is required for this test") - @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") - def test_noClone_soln(self): - - m_orig = fc.create_model() - fc.initialize_model(m_orig,100) - - m_orig.perturbed_a = Param(initialize=-0.25) - m_orig.perturbed_H = Param(initialize=0.55) - - m_sipopt = sensitivity_calculation('sipopt', m_orig,[m_orig.a,m_orig.H], - [m_orig.perturbed_a,m_orig.perturbed_H], - cloneModel=False) - - self.assertTrue(m_sipopt == m_orig) - - # test _sipopt_data block exists - self.assertTrue(hasattr(m_orig,'_sipopt_data') and - m_orig._sipopt_data.ctype is Block) - - # test variable declaration - self.assertTrue(hasattr(m_sipopt._sipopt_data,'a') and - m_sipopt._sipopt_data.a.ctype is Var) - self.assertTrue(hasattr(m_sipopt._sipopt_data,'H') and - m_sipopt._sipopt_data.H.ctype is Var) - - # test for suffixes - self.assertTrue(hasattr(m_sipopt,'sens_state_0') and - m_sipopt.sens_state_0.ctype is Suffix and - m_sipopt.sens_state_0[m_sipopt._sipopt_data.H]==2 and - m_sipopt.sens_state_0[m_sipopt._sipopt_data.a]==1) - - self.assertTrue(hasattr(m_sipopt,'sens_state_1') and - m_sipopt.sens_state_1.ctype is Suffix and - m_sipopt.sens_state_1[m_sipopt._sipopt_data.H]==2 and - m_sipopt.sens_state_1[m_sipopt._sipopt_data.a]==1) - - self.assertTrue(hasattr(m_sipopt,'sens_state_value_1') and - m_sipopt.sens_state_value_1.ctype is Suffix and - m_sipopt.sens_state_value_1[ - m_sipopt._sipopt_data.H]==0.55 and - m_sipopt.sens_state_value_1[ - m_sipopt._sipopt_data.a]==-0.25) - - self.assertTrue(hasattr(m_sipopt,'sens_init_constr') and - m_sipopt.sens_init_constr.ctype is Suffix and - m_sipopt.sens_init_constr[ - m_sipopt._sipopt_data.paramConst[1]]==1 and - m_sipopt.sens_init_constr[ - m_sipopt._sipopt_data.paramConst[2]]==2) - - self.assertTrue(hasattr(m_sipopt,'sens_sol_state_1') and - m_sipopt.sens_sol_state_1.ctype is Suffix) - self.assertAlmostEqual( - m_sipopt.sens_sol_state_1[ - m_sipopt.F[15]],-0.00102016765,8) - - self.assertTrue(hasattr(m_sipopt,'sens_sol_state_1_z_L') and - m_sipopt.sens_sol_state_1_z_L.ctype is Suffix) - self.assertAlmostEqual( - m_sipopt.sens_sol_state_1_z_L[ - m_sipopt.u[15]],-2.181712e-09,13) - - self.assertTrue(hasattr(m_sipopt,'sens_sol_state_1_z_U') and - m_sipopt.sens_sol_state_1_z_U.ctype is Suffix) - self.assertAlmostEqual( - m_sipopt.sens_sol_state_1_z_U[ - m_sipopt.u[15]],6.580899e-09,13) - - # verify deactivated constraints on model - self.assertFalse(m_sipopt.FDiffCon[0].active and - m_sipopt.FDiffCon[7.5].active and - m_sipopt.FDiffCon[15].active ) - - self.assertFalse(m_sipopt.x_dot[0].active and - m_sipopt.x_dot[7.5].active and - m_sipopt.x_dot[15].active ) - - # test model solution - self.assertAlmostEqual(value(m_sipopt.J),0.0048956783,8) - - - - - # test indexed param mapping to var and perturbed values - @unittest.skipIf(not scipy_available, "scipy is required for this test") - @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") - def test_indexedParamsMapping(self): - - m = hiv.create_model() - hiv.initialize_model(m,10,5,1) - - m.epsDelta = Param(initialize = 0.75001) - - q_del = {} - q_del[(0,0)] = 1.001 - q_del[(0,1)] = 1.002 - q_del[(1,0)] = 1.003 - q_del[(1,1)] = 1.004 - q_del[(2,0)] = 0.83001 - q_del[(2,1)] = 0.83002 - q_del[(3,0)] = 0.42001 - q_del[(4,0)] = 0.17001 - m.qqDelta = Param(m.ij, initialize = q_del) - - m.aaDelta = Param(initialize =0.0001001) - - m_sipopt = sensitivity_calculation('sipopt', m, [m.eps,m.qq,m.aa], - [m.epsDelta,m.qqDelta,m.aaDelta]) - - # param to var data - self.assertEqual( - m_sipopt._sipopt_data.paramConst[1].lower.local_name, 'eps') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[1].body.local_name, 'eps') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[1].upper.local_name, 'eps') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[6].lower.local_name, 'qq[2,0]') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[6].body.local_name, 'qq[2,0]') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[6].upper.local_name, 'qq[2,0]') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[10].lower.local_name, 'aa') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[10].body.local_name, 'aa') - self.assertEqual( - m_sipopt._sipopt_data.paramConst[10].upper.local_name, 'aa') - - - - # test Constraint substitution - @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") - def test_constraintSub(self): - - m = ri.create_model() - - m.pert_a = Param(initialize=0.01) - m.pert_b = Param(initialize=1.01) - - m_sipopt = sensitivity_calculation('sipopt', m,[m.a,m.b], [m.pert_a,m.pert_b]) - - # verify substitutions in equality constraint - self.assertTrue(m_sipopt.C_equal.lower.ctype is Param and - m_sipopt.C_equal.upper.ctype is Param) - self.assertFalse(m_sipopt.C_equal.active) - - self.assertTrue(m_sipopt._sipopt_data.constList[3].lower == 0.0 and - m_sipopt._sipopt_data.constList[3].upper == 0.0 and - len(list(identify_variables( - m_sipopt._sipopt_data.constList[3].body))) == 2) - - # verify substitutions in one-sided bounded constraint - self.assertTrue(m_sipopt.C_singleBnd.lower is None and - m_sipopt.C_singleBnd.upper.ctype is Param) - self.assertFalse(m_sipopt.C_singleBnd.active) - - self.assertTrue(m_sipopt._sipopt_data.constList[4].lower is None and - m_sipopt._sipopt_data.constList[4].upper == 0.0 and - len(list(identify_variables( - m_sipopt._sipopt_data.constList[4].body))) == 2) - - # verify substitutions in ranged inequality constraint - self.assertTrue(m_sipopt.C_rangedIn.lower.ctype is Param and - m_sipopt.C_rangedIn.upper.ctype is Param) - self.assertFalse(m_sipopt.C_rangedIn.active) - - self.assertTrue(m_sipopt._sipopt_data.constList[1].lower is None and - m_sipopt._sipopt_data.constList[1].upper == 0.0 and - len(list(identify_variables( - m_sipopt._sipopt_data.constList[1].body))) == 2) - - self.assertTrue(m_sipopt._sipopt_data.constList[2].lower is None and - m_sipopt._sipopt_data.constList[2].upper == 0.0 and - len(list(identify_variables( - m_sipopt._sipopt_data.constList[2].body))) == 2) - - # Test example `parameter.py` - @unittest.skipIf(not opt.available(False), "ipopt_sens is not available") - def test_parameter_example(self): - - m = ConcreteModel() - - m.x1 = Var(initialize = 0.15, within=NonNegativeReals) - m.x2 = Var(initialize = 0.15, within=NonNegativeReals) - m.x3 = Var(initialize = 0.0, within=NonNegativeReals) - - m.eta1 = Param(initialize=4.5, mutable=True) - m.eta2 = Param(initialize=1.0, mutable=True) - - m.const1 = Constraint(expr=6*m.x1+3*m.x2+2*m.x3-m.eta1 ==0) - m.const2 = Constraint(expr=m.eta2*m.x1+m.x2-m.x3-1 ==0) - m.cost = Objective(expr=m.x1**2+m.x2**2+m.x3**2) - - m.perturbed_eta1 = Param(initialize = 4.0) - m.perturbed_eta2 = Param(initialize = 1.0) - - - m_sipopt = sensitivity_calculation('sipopt', m,[m.eta1,m.eta2], - [m.perturbed_eta1,m.perturbed_eta2], - streamSoln=True) - x1 = m_sipopt.sens_sol_state_1[m_sipopt.x1] - x2 = m_sipopt.sens_sol_state_1[m_sipopt.x2] - x3 = m_sipopt.sens_sol_state_1[m_sipopt.x3] - obj = x1**2 + x2**2 + x3**2 - - d = dict() - d['eta1'] = m.eta1() - d['eta2'] = m.eta2() - d['x1_init'] = m.x1() - d['x2_init'] = m.x2() - d['x3_init'] = m.x3() - d['x1_sln'] = m_sipopt.x1() - d['x2_sln'] = m_sipopt.x2() - d['x3_sln'] = m_sipopt.x3() - d['cost_sln'] = m_sipopt.cost() - d['eta1_pert'] = m_sipopt.perturbed_eta1() - d['eta2_pert'] = m_sipopt.perturbed_eta2() - d['x1_pert'] = x1 - d['x2_pert'] = x2 - d['x3_pert'] = x3 - d['cost_pert'] = obj - - d_correct = {'eta1':4.5, 'eta2':1.0, 'x1_init':0.15, 'x2_init':0.15, 'x3_init':0.0, - 'cost_sln':0.5, 'x1_sln':0.5, 'x2_sln':0.5, 'x3_sln':0.0, 'eta1_pert':4.0, - 'eta2_pert':1.0, 'x1_pert':0.3333333,'x2_pert':0.6666667,'x3_pert':0.0, - 'cost_pert':0.55555556} - - for k in d_correct.keys(): - # Check each element of the 'correct' dictionary against the returned - # dictionary to 3 decimal places - self.assertAlmostEqual(d[k],d_correct[k],3) - - - # Test kaug - # Perform the same tests as ipopt_sens - # test arguments - @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") - @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") - def test_kaug_bad_arg_kaug(self): - m = ConcreteModel() - m.t = ContinuousSet(bounds=(0,1)) - - m.a = Param(initialize=1, mutable=True) - m.b = Param(initialize=2, mutable=True) - m.c = Param(initialize=3, mutable=False) - - m.x = Var(m.t) - - list_one = [m.a,m.b] - list_two = [m.a,m.b,m.c] - list_three = [m.a, m.x] - list_four = [m.a,m.c] - - # verify ValueError thrown when param and perturb list are different - # lengths - with self.assertRaises(ValueError): - Result = sensitivity_calculation('kaug', m, list_one, list_two) - - # verify ValueError thrown when param list has a Var in it - try: - Result = sensitivity_calculation('kaug', m,list_three,list_two) - self.fail("Expected ValueError: variable sent through paramSubList") - except ValueError: - pass - - # verify ValueError thrown when perturb list has Var in it - try: - Result = sensitivity_calculation('kaug', m,list_one,list_three) - self.fail("Expected ValueError: variable sent through perturbList") - except ValueError: - pass - - # verify ValueError thrown when param list has an unmutable param - try: - Result = sensitivity_calculation('kaug', m,list_four,list_one) - self.fail("Expected ValueError:" - "unmutable param sent through paramSubList") - except ValueError: - pass - - - # test feedbackController Solution when the model gets cloned - @unittest.skipIf(not scipy_available, "scipy is required for this test") - @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") - @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") - def test_kaug_clonedModel_soln_kaug(self): - m_orig = fc.create_model() - fc.initialize_model(m_orig,100) - - m_orig.perturbed_a = Param(initialize=-0.25) - m_orig.perturbed_H = Param(initialize=0.55) - - m_kaug = sensitivity_calculation('kaug', m_orig,[m_orig.a,m_orig.H], - [m_orig.perturbed_a,m_orig.perturbed_H], - cloneModel=True) - - # verify cloned model has _kaug_data block - # and original model is untouched - self.assertFalse(m_kaug == m_orig) - - self.assertTrue(hasattr(m_kaug,'_kaug_data') and - m_kaug._kaug_data.ctype is Block) - - self.assertFalse(hasattr(m_orig,'_kaug_data')) - self.assertFalse(hasattr(m_orig,'b')) - - # verify variable declaration - self.assertTrue(hasattr(m_kaug._kaug_data,'a') and - m_kaug._kaug_data.a.ctype is Var) - self.assertTrue(hasattr(m_kaug._kaug_data,'H') and - m_kaug._kaug_data.H.ctype is Var) - - # verify suffixes - self.assertTrue(hasattr(m_kaug,'sens_state_0') and - m_kaug.sens_state_0.ctype is Suffix and - m_kaug.sens_state_0[m_kaug._kaug_data.H]==2 and - m_kaug.sens_state_0[m_kaug._kaug_data.a]==1) - self.assertTrue(hasattr(m_kaug,'sens_state_1') and - m_kaug.sens_state_1.ctype is Suffix and - m_kaug.sens_state_1[m_kaug._kaug_data.H]==2 and - m_kaug.sens_state_1[m_kaug._kaug_data.a]==1) - self.assertTrue(hasattr(m_kaug,'sens_state_value_1') and - m_kaug.sens_state_value_1.ctype is Suffix and - m_kaug.sens_state_value_1[ - m_kaug._kaug_data.H]==0.55 and - m_kaug.sens_state_value_1[ - m_kaug._kaug_data.a]==-0.25) - self.assertTrue(hasattr(m_kaug,'sens_init_constr') and - m_kaug.sens_init_constr.ctype is Suffix and - m_kaug.sens_init_constr[ - m_kaug._kaug_data.paramConst[1]]==1 and - m_kaug.sens_init_constr[ - m_kaug._kaug_data.paramConst[2]]==2) - self.assertTrue(hasattr(m_kaug,'DeltaP') and - m_kaug.DeltaP.ctype is Suffix and - m_kaug.DeltaP[m_kaug._kaug_data.paramConst[1]]==0.04999999999999999 and - m_kaug.DeltaP[m_kaug._kaug_data.paramConst[2]]==-0.050000000000000044) - self.assertTrue(hasattr(m_kaug,'dcdp') and - m_kaug.dcdp.ctype is Suffix and - m_kaug.dcdp[m_kaug._kaug_data.paramConst[1]]==1 and - m_kaug.dcdp[m_kaug._kaug_data.paramConst[2]]==2) - self.assertTrue(hasattr(m_kaug,'sens_sol_state_1') and - m_kaug.sens_sol_state_1.ctype is Suffix) - - self.assertTrue(hasattr(m_kaug,'ipopt_zL_in') and - m_kaug.ipopt_zL_in.ctype is Suffix) - self.assertAlmostEqual( - m_kaug.ipopt_zL_in[ - m_kaug.u[15]],7.162686166847096e-09,13) - - self.assertTrue(hasattr(m_kaug,'ipopt_zU_in') and - m_kaug.ipopt_zU_in.ctype is Suffix) - self.assertAlmostEqual( - m_kaug.ipopt_zU_in[ - m_kaug.u[15]],-1.2439730261288605e-08,13) - # verify deactivated constraints for cloned model - self.assertFalse(m_kaug.FDiffCon[0].active and - m_kaug.FDiffCon[7.5].active and - m_kaug.FDiffCon[15].active ) - - self.assertFalse(m_kaug.x_dot[0].active and - m_kaug.x_dot[7.5].active and - m_kaug.x_dot[15].active ) - - # verify constraints on original model are still active - self.assertTrue(m_orig.FDiffCon[0].active and - m_orig.FDiffCon[7.5].active and - m_orig.FDiffCon[15].active ) - - self.assertTrue(m_orig.x_dot[0].active and - m_orig.x_dot[7.5].active and - m_orig.x_dot[15].active ) - - # verify solution - self.assertAlmostEqual(value(m_kaug.J),0.002633263921107476,8) - - @unittest.skipIf(not scipy_available, "scipy is required for this test") - @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") - @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") - def test_noClone_soln_kaug(self): - - m_orig = fc.create_model() - fc.initialize_model(m_orig,100) - - m_orig.perturbed_a = Param(initialize=-0.25) - m_orig.perturbed_H = Param(initialize=0.55) - - m_kaug = sensitivity_calculation('kaug', m_orig,[m_orig.a,m_orig.H], - [m_orig.perturbed_a,m_orig.perturbed_H], - cloneModel=False) - - self.assertTrue(m_kaug == m_orig) - - # verify suffixes - self.assertTrue(hasattr(m_kaug,'sens_state_0') and - m_kaug.sens_state_0.ctype is Suffix and - m_kaug.sens_state_0[m_kaug._kaug_data.H]==2 and - m_kaug.sens_state_0[m_kaug._kaug_data.a]==1) - self.assertTrue(hasattr(m_kaug,'sens_state_1') and - m_kaug.sens_state_1.ctype is Suffix and - m_kaug.sens_state_1[m_kaug._kaug_data.H]==2 and - m_kaug.sens_state_1[m_kaug._kaug_data.a]==1) - self.assertTrue(hasattr(m_kaug,'sens_state_value_1') and - m_kaug.sens_state_value_1.ctype is Suffix and - m_kaug.sens_state_value_1[ - m_kaug._kaug_data.H]==0.55 and - m_kaug.sens_state_value_1[ - m_kaug._kaug_data.a]==-0.25) - self.assertTrue(hasattr(m_kaug,'sens_init_constr') and - m_kaug.sens_init_constr.ctype is Suffix and - m_kaug.sens_init_constr[ - m_kaug._kaug_data.paramConst[1]]==1 and - m_kaug.sens_init_constr[ - m_kaug._kaug_data.paramConst[2]]==2) - self.assertTrue(hasattr(m_kaug,'DeltaP') and - m_kaug.DeltaP.ctype is Suffix and - m_kaug.DeltaP[m_kaug._kaug_data.paramConst[1]]==0.04999999999999999 and - m_kaug.DeltaP[m_kaug._kaug_data.paramConst[2]]==-0.050000000000000044) - self.assertTrue(hasattr(m_kaug,'dcdp') and - m_kaug.dcdp.ctype is Suffix and - m_kaug.dcdp[m_kaug._kaug_data.paramConst[1]]==1 and - m_kaug.dcdp[m_kaug._kaug_data.paramConst[2]]==2) - self.assertTrue(hasattr(m_kaug,'sens_sol_state_1') and - m_kaug.sens_sol_state_1.ctype is Suffix) - - self.assertTrue(hasattr(m_kaug,'ipopt_zL_in') and - m_kaug.ipopt_zL_in.ctype is Suffix) - self.assertAlmostEqual( - m_kaug.ipopt_zL_in[ - m_kaug.u[15]],7.162686166847096e-09,13) - - self.assertTrue(hasattr(m_kaug,'ipopt_zU_in') and - m_kaug.ipopt_zU_in.ctype is Suffix) - self.assertAlmostEqual( - m_kaug.ipopt_zU_in[ - m_kaug.u[15]],-1.2439730261288605e-08,13) - # verify deactivated constraints for cloned model - self.assertFalse(m_kaug.FDiffCon[0].active and - m_kaug.FDiffCon[7.5].active and - m_kaug.FDiffCon[15].active ) - - self.assertFalse(m_kaug.x_dot[0].active and - m_kaug.x_dot[7.5].active and - m_kaug.x_dot[15].active ) - - - # verify solution - self.assertAlmostEqual(value(m_kaug.J),0.002633263921107476,8) - - - # test indexed param mapping to var and perturbed values - @unittest.skipIf(not scipy_available, "scipy is required for this test") - @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") - @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") - def test_indexedParamsMapping_kaug(self): - - m = hiv.create_model() - hiv.initialize_model(m,10,5,1) - - m.epsDelta = Param(initialize = 0.75001) - - q_del = {} - q_del[(0,0)] = 1.001 - q_del[(0,1)] = 1.002 - q_del[(1,0)] = 1.003 - q_del[(1,1)] = 1.004 - q_del[(2,0)] = 0.83001 - q_del[(2,1)] = 0.83002 - q_del[(3,0)] = 0.42001 - q_del[(4,0)] = 0.17001 - m.qqDelta = Param(m.ij, initialize = q_del) - - m.aaDelta = Param(initialize =0.0001001) - - m_kaug = sensitivity_calculation('kaug', m, [m.eps,m.qq,m.aa], - [m.epsDelta,m.qqDelta,m.aaDelta]) - - # param to var data - self.assertEqual( - m_kaug._kaug_data.paramConst[1].lower.local_name, 'eps') - self.assertEqual( - m_kaug._kaug_data.paramConst[1].body.local_name, 'eps') - self.assertEqual( - m_kaug._kaug_data.paramConst[1].upper.local_name, 'eps') - self.assertEqual( - m_kaug._kaug_data.paramConst[6].lower.local_name, 'qq[2,0]') - self.assertEqual( - m_kaug._kaug_data.paramConst[6].body.local_name, 'qq[2,0]') - self.assertEqual( - m_kaug._kaug_data.paramConst[6].upper.local_name, 'qq[2,0]') - self.assertEqual( - m_kaug._kaug_data.paramConst[10].lower.local_name, 'aa') - self.assertEqual( - m_kaug._kaug_data.paramConst[10].body.local_name, 'aa') - self.assertEqual( - m_kaug._kaug_data.paramConst[10].upper.local_name, 'aa') - - - # test Constraint substitution - @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") - @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") - def test_constraintSub_kaug(self): - m = ri.create_model() - - m.pert_a = Param(initialize=0.01) - m.pert_b = Param(initialize=1.01) - - # m_kaug = kaug(m,[m.a,m.b], [m.pert_a,m.pert_b]) - # verify ValueError thrown when param list has an unmutable param - with self.assertRaises(Exception) as context: - m_kaug = sensitivity_calculation('kaug', m,[m.a,m.b], [m.pert_a,m.pert_b]) - self.assertTrue('kaug does not support inequality constraints.' in str(context.exception)) - - # Test example `parameter_kaug.py` - @unittest.skipIf(not opt_kaug.available(False), "k_aug is not available") - @unittest.skipIf(not opt_dotsens.available(False), "dot_sens is not available") - def test_parameter_example_kaug(self): - - - m = ConcreteModel() - - m.x1 = Var(initialize = 0.15, within=NonNegativeReals) - m.x2 = Var(initialize = 0.15, within=NonNegativeReals) - m.x3 = Var(initialize = 0.0, within=NonNegativeReals) - - m.eta1 = Param(initialize=4.5, mutable=True) - m.eta2 = Param(initialize=1.0, mutable=True) - - m.const1 = Constraint(expr=6*m.x1+3*m.x2+2*m.x3-m.eta1 ==0) - m.const2 = Constraint(expr=m.eta2*m.x1+m.x2-m.x3-1 ==0) - m.cost = Objective(expr=m.x1**2+m.x2**2+m.x3**2) - - m.perturbed_eta1 = Param(initialize = 4.0) - m.perturbed_eta2 = Param(initialize = 1.0) - - - m_kaug_dsdp = sensitivity_calculation('kaug', m,[m.eta1,m.eta2], - [m.perturbed_eta1,m.perturbed_eta2], - streamSoln=True) - - d = dict() - d['eta1'] = m.eta1() - d['eta2'] = m.eta2() - d['x1_init'] = m.x1() - d['x2_init'] = m.x2() - d['x3_init'] = m.x3() - d['eta1_pert'] = m_kaug_dsdp.perturbed_eta1() - d['eta2_pert'] = m_kaug_dsdp.perturbed_eta2() - d['cost_pert'] = m_kaug_dsdp.cost() - d['x1_pert'] = m_kaug_dsdp.x1() - d['x2_pert'] = m_kaug_dsdp.x2() - d['x3_pert'] = m_kaug_dsdp.x3() - - d_correct = {'eta1':4.5, 'eta2':1.0, 'x1_init':0.15, 'x2_init':0.15, 'x3_init':0.0, - 'eta1_pert':4.0, 'eta2_pert':1.0, 'x1_pert':0.3333333,'x2_pert':0.6666667, - 'x3_pert':0.0, 'cost_pert':0.55555556} - - for k in d_correct.keys(): - # Check each element of the 'correct' dictionary against the returned - # dictionary to 3 decimal places - self.assertAlmostEqual(d[k],d_correct[k],3) - -if __name__=="__main__": - unittest.main() diff --git a/pyomo/contrib/trustregion/GeometryGenerator.py b/pyomo/contrib/trustregion/GeometryGenerator.py index d973838e29d..86b9bb1bedd 100644 --- a/pyomo/contrib/trustregion/GeometryGenerator.py +++ b/pyomo/contrib/trustregion/GeometryGenerator.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import logging # This is an auto geometry generator for quadratic ROM @@ -72,7 +82,7 @@ def generate_quadratic_rom_geometry(lx, NUM_SEEDS=None): print(" LX: single number or range in the form nn:nn") print(" COUNT: number of random matricies to generate") - from os.path import abspath, dirname, exists, join + from os.path import abspath, dirname, join from inspect import getfile, currentframe CACHE_FILE = join(dirname(abspath(getfile(currentframe()))), 'cache.py') if not os.path.isfile (CACHE_FILE) or not os.access(CACHE_FILE, os.W_OK): diff --git a/pyomo/contrib/trustregion/Logger.py b/pyomo/contrib/trustregion/Logger.py index 70855c9213e..1ea7be25570 100644 --- a/pyomo/contrib/trustregion/Logger.py +++ b/pyomo/contrib/trustregion/Logger.py @@ -1,6 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import numpy as np from numpy.linalg import norm -from pyomo.contrib.trustregion.helper import * +from pyomo.contrib.trustregion.helper import packXYZ class IterLog: # # Todo: Include the following in high printlevel diff --git a/pyomo/contrib/trustregion/PyomoInterface.py b/pyomo/contrib/trustregion/PyomoInterface.py index 51a03be1557..6d82d6247de 100644 --- a/pyomo/contrib/trustregion/PyomoInterface.py +++ b/pyomo/contrib/trustregion/PyomoInterface.py @@ -1,23 +1,31 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import logging -import os import numpy as np from pyutilib.math import infinity from pyomo.common.collections import ComponentSet -from pyomo.common.modeling import randint, unique_component_name +from pyomo.common.modeling import unique_component_name from pyomo.core import ( - Block, Var, Param, Set, VarList, ConstraintList, Constraint, Objective, - RangeSet, value, ConcreteModel, Reals, sqrt, minimize, maximize + Block, Var, Param, VarList, ConstraintList, Constraint, Objective, + RangeSet, value, ConcreteModel, Reals, sqrt, minimize, maximize, ) from pyomo.core.expr import current as EXPR from pyomo.core.base.external import PythonCallbackFunction -from pyomo.core.base.var import _VarData from pyomo.core.base.numvalue import nonpyomo_leaf_types from pyomo.opt import SolverFactory, SolverStatus, TerminationCondition from pyomo.contrib.trustregion.GeometryGenerator import ( generate_quadratic_rom_geometry ) -from pyomo.contrib.trustregion.helper import * +from pyomo.contrib.trustregion.helper import maxIgnoreNone, minIgnoreNone logger = logging.getLogger('pyomo.contrib.trustregion') diff --git a/pyomo/contrib/trustregion/TRF.py b/pyomo/contrib/trustregion/TRF.py index e3548b270e1..e504411a610 100644 --- a/pyomo/contrib/trustregion/TRF.py +++ b/pyomo/contrib/trustregion/TRF.py @@ -1,12 +1,20 @@ -import numpy as np +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from math import pow from numpy import inf from numpy.linalg import norm from pyomo.contrib.trustregion.filterMethod import ( FilterElement, Filter) -from pyomo.contrib.trustregion.helper import (cloneXYZ, packXYZ, - minIgnoreNone, maxIgnoreNone) -from pyomo.contrib.trustregion.Logger import (IterLog, Logger) +from pyomo.contrib.trustregion.helper import (cloneXYZ, packXYZ) +from pyomo.contrib.trustregion.Logger import Logger from pyomo.contrib.trustregion.PyomoInterface import ( PyomoInterface, ROMType) diff --git a/pyomo/contrib/trustregion/examples/Example1.py b/pyomo/contrib/trustregion/examples/Example1.py index 123efee5003..b95078ccf1a 100644 --- a/pyomo/contrib/trustregion/examples/Example1.py +++ b/pyomo/contrib/trustregion/examples/Example1.py @@ -1,6 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ -from pyomo.environ import * -from pyomo.opt import SolverFactory, SolverStatus, TerminationCondition +from pyomo.environ import ConcreteModel, Var, Reals, ExternalFunction, sin, sqrt, Constraint, Objective +from pyomo.opt import SolverFactory m = ConcreteModel() m.z = Var(range(3), domain=Reals, initialize=2.) diff --git a/pyomo/contrib/trustregion/readgjh.py b/pyomo/contrib/trustregion/readgjh.py index b4ae8c35304..2c0ed797480 100644 --- a/pyomo/contrib/trustregion/readgjh.py +++ b/pyomo/contrib/trustregion/readgjh.py @@ -1,5 +1,14 @@ - -import os, glob, six +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import glob, six # build obj gradient and constraint Jacobian # from a gjh file written by the ASL gjh 'solver' diff --git a/pyomo/contrib/trustregion/tests/TestPyomoInterface.py b/pyomo/contrib/trustregion/tests/TestPyomoInterface.py index 20a66b5f0e7..9ea1aff7ea7 100644 --- a/pyomo/contrib/trustregion/tests/TestPyomoInterface.py +++ b/pyomo/contrib/trustregion/tests/TestPyomoInterface.py @@ -1,16 +1,26 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + #!/usr/bin/env python import pyutilib.th as unittest from pyutilib.misc.config import ConfigBlock from pyomo.core.expr.current import identify_variables -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Reals, Objective, Constraint, ExternalFunction, SolverFactory, value, sqrt, sin from pyomo.opt import check_available_solvers try: import numpy numpy_available = True - from pyomo.contrib.trustregion.PyomoInterface import * + from pyomo.contrib.trustregion.PyomoInterface import PyomoInterface except: numpy_available = False diff --git a/pyomo/contrib/trustregion/tests/TestTRConfig.py b/pyomo/contrib/trustregion/tests/TestTRConfig.py index 461f7881f60..807da1ce267 100644 --- a/pyomo/contrib/trustregion/tests/TestTRConfig.py +++ b/pyomo/contrib/trustregion/tests/TestTRConfig.py @@ -1,15 +1,21 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + #!/usr/python/env python import pyutilib.th as unittest -from pyutilib.misc.config import ConfigBlock, ConfigValue, ConfigList -from pyomo.common.config import ( - PositiveInt, PositiveFloat, NonNegativeFloat, In) from pyomo.common.dependencies import numpy_available -from pyomo.core import Var, value -from pyomo.environ import * -from pyomo.opt import SolverFactory, SolverStatus, TerminationCondition +from pyomo.environ import (Var, ConcreteModel, Reals, ExternalFunction, + Objective, Constraint, sqrt, sin, SolverFactory) @unittest.skipIf(not SolverFactory('ipopt').available(False), "The IPOPT solver is not available") @unittest.skipIf(not SolverFactory('gjh').available(False), "The GJH solver is not available") diff --git a/pyomo/contrib/viewer/model_browser.py b/pyomo/contrib/viewer/model_browser.py index 73e8772b35c..f4ecc2c3b2d 100644 --- a/pyomo/contrib/viewer/model_browser.py +++ b/pyomo/contrib/viewer/model_browser.py @@ -16,9 +16,7 @@ __author__ = "John Eslick" import os -import warnings import logging -import re _log = logging.getLogger(__name__) @@ -28,8 +26,6 @@ from pyomo.core.base.block import _BlockData from pyomo.core.base.var import _VarData from pyomo.core.base.constraint import _ConstraintData -from pyomo.core.base.expression import _ExpressionData -from pyomo.network.port import SimplePort from pyomo.core.base.param import _ParamData from pyomo.environ import Block, Var, Constraint, Param, Expression, value @@ -119,7 +115,7 @@ def __init__(self, ui_data, parent=None, standard="Var"): self.setWindowTitle("Constraints") elif standard == "Param": components = Param - columns = ["name", "value", "_mutable"] + columns = ["name", "value", "mutable"] editable = ["value"] self.setWindowTitle("Parameters") elif standard == "Expression": @@ -263,7 +259,7 @@ def _set_value_callback(self, val): except: return elif isinstance(self.data, _ParamData): - if not self.data._mutable: return + if not self.data.parent_component().mutable: return try: self.data.value = val except: diff --git a/pyomo/contrib/viewer/report.py b/pyomo/contrib/viewer/report.py index 6ad2f6267e3..73531b13393 100644 --- a/pyomo/contrib/viewer/report.py +++ b/pyomo/contrib/viewer/report.py @@ -8,10 +8,9 @@ # # This software is distributed under the 3-clause BSD License. ############################################################################## -from pyomo.environ import * from pyomo.common.collections import ComponentSet from pyomo.core.expr.current import identify_variables -from pyomo.network.port import _PortData, SimplePort +from pyomo.environ import Constraint, value def value_no_exception(c, div0=None): """ diff --git a/pyomo/contrib/viewer/residual_table.py b/pyomo/contrib/viewer/residual_table.py index 7fbced8817d..dbb69c4579f 100644 --- a/pyomo/contrib/viewer/residual_table.py +++ b/pyomo/contrib/viewer/residual_table.py @@ -74,7 +74,7 @@ def update_model(self): else: ac = True self._items = list(self.ui_data.model.component_data_objects( - pyo.Constraint, active=ac)) + pyo.Constraint, active=ac)) def sort(self): self._items.sort(key= diff --git a/pyomo/contrib/viewer/tests/pytest_qt.py b/pyomo/contrib/viewer/tests/pytest_qt.py index 8049f58549e..5194d54a9fe 100644 --- a/pyomo/contrib/viewer/tests/pytest_qt.py +++ b/pyomo/contrib/viewer/tests/pytest_qt.py @@ -12,10 +12,8 @@ UI Tests """ import pyutilib.th as unittest -import time -import pytest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Constraint, Objective, Reals, Block, Expression, ExternalFunction, sin, sqrt, log from pyomo.contrib.viewer.qt import qt_available from pyomo.contrib.viewer.qt import QtCore, QMessageBox from pyomo.contrib.viewer.ui import get_mainwindow, ModelBrowser diff --git a/pyomo/contrib/viewer/tests/test_data_model_item.py b/pyomo/contrib/viewer/tests/test_data_model_item.py index 3399cead22d..9b997282b50 100644 --- a/pyomo/contrib/viewer/tests/test_data_model_item.py +++ b/pyomo/contrib/viewer/tests/test_data_model_item.py @@ -14,7 +14,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Block, Param, Expression, Constraint, Objective, ExternalFunction, Reals, log, sin, sqrt, expr from pyomo.contrib.viewer.model_browser import ComponentDataItem from pyomo.contrib.viewer.ui_data import UIData diff --git a/pyomo/contrib/viewer/tests/test_data_model_tree.py b/pyomo/contrib/viewer/tests/test_data_model_tree.py index 5a0047d1eec..eee6b437cda 100644 --- a/pyomo/contrib/viewer/tests/test_data_model_tree.py +++ b/pyomo/contrib/viewer/tests/test_data_model_tree.py @@ -13,7 +13,7 @@ """ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Constraint, Objective, Reals, Block, Expression, ExternalFunction, sin, sqrt from pyomo.contrib.viewer.model_browser import ComponentDataModel try: no_pyqt = False diff --git a/pyomo/contrib/viewer/tests/test_qt.py b/pyomo/contrib/viewer/tests/test_qt.py index fd53b451515..6c4791e56aa 100644 --- a/pyomo/contrib/viewer/tests/test_qt.py +++ b/pyomo/contrib/viewer/tests/test_qt.py @@ -14,7 +14,6 @@ from subprocess import Popen import os import time -from pyomo.environ import * import pyutilib.th as unittest test_file = os.path.join(os.path.dirname(__file__), "pytest_qt.py") diff --git a/pyomo/contrib/viewer/ui.py b/pyomo/contrib/viewer/ui.py index 841cdd954e3..5a3bb087a12 100644 --- a/pyomo/contrib/viewer/ui.py +++ b/pyomo/contrib/viewer/ui.py @@ -15,21 +15,15 @@ __author__ = "John Eslick" -import time import os -import warnings import logging -import threading -import datetime -import json -import sys try: from IPython import get_ipython except ImportError: def get_ipython(): raise AttributeError("IPython not available") import pyomo.contrib.viewer.report as rpt -import pyomo.environ as pe +import pyomo.environ as pyo _log = logging.getLogger(__name__) @@ -70,7 +64,7 @@ def get_mainwindow(model=None, show=True, testing=False): model. If no model is provided a new ConcreteModel is created """ if model is None: - model = pe.ConcreteModel(name="Default") + model = pyo.ConcreteModel(name="Default") ui = MainWindow(model=model, testing=testing) try: get_ipython().events.register('post_execute', ui.refresh_on_execute) diff --git a/pyomo/contrib/viewer/ui_data.py b/pyomo/contrib/viewer/ui_data.py index 9e2d5278f19..bb6579b3f01 100644 --- a/pyomo/contrib/viewer/ui_data.py +++ b/pyomo/contrib/viewer/ui_data.py @@ -8,6 +8,7 @@ # # This software is distributed under the 3-clause BSD License. ############################################################################## + """ UI data objects for sharing data and settings between different parts of the UI. """ @@ -18,7 +19,7 @@ import logging from pyomo.common.collections import ComponentMap from pyomo.contrib.viewer.qt import * -import pyomo.environ as pe +import pyomo.environ as pyo _log = logging.getLogger(__name__) @@ -82,17 +83,17 @@ def model(self, value): self.emit_update() def calculate_constraints(self): - for o in self.model.component_data_objects(pe.Constraint, active=True): + for o in self.model.component_data_objects(pyo.Constraint, active=True): try: - self.value_cache[o] = pe.value(o.body, exception=False) + self.value_cache[o] = pyo.value(o.body, exception=False) except ZeroDivisionError: self.value_cache[o] = "Divide_by_0" self.emit_exec_refresh() def calculate_expressions(self): - for o in self.model.component_data_objects(pe.Expression, active=True): + for o in self.model.component_data_objects(pyo.Expression, active=True): try: - self.value_cache[o] = pe.value(o, exception=False) + self.value_cache[o] = pyo.value(o, exception=False) except ZeroDivisionError: self.value_cache[o] = "Divide_by_0" self.emit_exec_refresh() diff --git a/pyomo/core/__init__.py b/pyomo/core/__init__.py index 7e640859918..92b2c269afe 100644 --- a/pyomo/core/__init__.py +++ b/pyomo/core/__init__.py @@ -8,9 +8,146 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core.expr import * +from six import iteritems, iterkeys +from pyomo.core.expr.numvalue import ( + value, is_constant, is_fixed, is_variable_type, + is_potentially_variable, NumericValue, ZeroConstant, + native_numeric_types, native_types, polynomial_degree, +) + +from pyomo.core.expr.boolean_value import BooleanValue + +from pyomo.core.expr.numeric_expr import linear_expression, nonlinear_expression +from pyomo.core.expr.logical_expr import (land, lor, equivalent, exactly, + atleast, atmost, implies, lnot, + xor, inequality) + +from pyomo.core.expr.current import ( + log, log10, sin, cos, tan, cosh, sinh, tanh, + asin, acos, atan, exp, sqrt, asinh, acosh, + atanh, ceil, floor, + Expr_if, +) + +from pyomo.core.expr.calculus.derivatives import differentiate +from pyomo.core.expr.taylor_series import taylor_series_expansion + import pyomo.core.kernel import pyomo.core.base._pyomo -from pyomo.core.base import * + +from pyomo.common.collections import ComponentMap +from pyomo.core.expr.symbol_map import SymbolMap +from pyomo.core.expr import (numvalue, numeric_expr, boolean_value, + logical_expr, current, symbol_map, sympy_tools, + taylor_series, visitor, expr_common, expr_errors, + calculus) +from pyomo.core import expr, preprocess, util, kernel + +from pyomo.core.expr.numvalue import (nonpyomo_leaf_types, + PyomoObject, + native_numeric_types, + value, is_constant, + is_fixed, is_variable_type, + is_potentially_variable, + polynomial_degree, + NumericValue, + ZeroConstant) +from pyomo.core.expr.boolean_value import ( + as_boolean, BooleanConstant, BooleanValue, + native_logical_values) +from pyomo.core.kernel.objective import (minimize, + maximize) +from pyomo.core.base.config import PyomoOptions + +from pyomo.core.base.expression import Expression +from pyomo.core.base.label import (CuidLabeler, + CounterLabeler, NumericLabeler, + CNameLabeler, TextLabeler, + AlphaNumericTextLabeler, NameLabeler, + ShortNameLabeler) + +# +# Components +# +from pyomo.core.base.component import (name, Component, ComponentUID) +import pyomo.core.base.indexed_component +from pyomo.core.base.action import BuildAction +from pyomo.core.base.check import BuildCheck +from pyomo.core.base.set import ( + Set, SetOf, simple_set_rule, RangeSet, +) +from pyomo.core.base.param import Param +from pyomo.core.base.var import (Var, SimpleVar, VarList) +from pyomo.core.base.boolean_var import ( + BooleanVar, BooleanVarList, SimpleBooleanVar) +from pyomo.core.base.constraint import (logical_expr, + simple_constraint_rule, + simple_constraintlist_rule, + ConstraintList, Constraint) +from pyomo.core.base.logical_constraint import ( + LogicalConstraint, LogicalConstraintList) +from pyomo.core.base.objective import (simple_objective_rule, + simple_objectivelist_rule, + Objective, ObjectiveList) +from pyomo.core.base.connector import Connector +from pyomo.core.base.sos import SOSConstraint +from pyomo.core.base.piecewise import Piecewise +from pyomo.core.base.suffix import (active_export_suffix_generator, + active_import_suffix_generator, + Suffix) +from pyomo.core.base.external import ExternalFunction +from pyomo.core.base.symbol_map import symbol_map_from_instance +from pyomo.core.base.reference import Reference + +from pyomo.core.base.set import (Reals, PositiveReals, NonPositiveReals, + NegativeReals, NonNegativeReals, Integers, + PositiveIntegers, NonPositiveIntegers, + NegativeIntegers, NonNegativeIntegers, + Boolean, Binary, Any, AnyWithNone, EmptySet, + UnitInterval, PercentFraction, RealInterval, + IntegerInterval) +from pyomo.core.base.misc import display +from pyomo.core.base.block import (SortComponents, TraversalStrategy, + Block, SimpleBlock, + active_components, + components, active_components_data, + components_data) +from pyomo.core.base.PyomoModel import (global_option, + Model, ConcreteModel, + AbstractModel) +from pyomo.core.base.plugin import (pyomo_callback, + IPyomoExpression, ExpressionFactory, + ExpressionRegistration, IPyomoPresolver, + IPyomoPresolveAction, + IParamRepresentation, + ParamRepresentationFactory, + IPyomoScriptPreprocess, + IPyomoScriptCreateModel, + IPyomoScriptCreateDataPortal, + IPyomoScriptModifyInstance, + IPyomoScriptPrintModel, + IPyomoScriptPrintInstance, + IPyomoScriptSaveInstance, + IPyomoScriptPrintResults, + IPyomoScriptSaveResults, + IPyomoScriptPostprocess, + ModelComponentFactory, Transformation, + TransformationFactory) +# +import pyomo.core.base._pyomo +# +from pyomo.core.base import util + +from pyomo.core.base.instance2dat import instance2dat + +# These APIs are deprecated and should be removed in the near future +from pyomo.core.base.set import ( + set_options, RealSet, IntegerSet, BooleanSet, +) + import pyomo.core.preprocess -from pyomo.core.util import * + +from pyomo.core.util import (prod, quicksum, sum_product, dot_product, + summation, sequence) + +from weakref import ref as weakref_ref diff --git a/pyomo/core/base/PyomoModel.py b/pyomo/core/base/PyomoModel.py index c238d08dca0..dbc338c9bce 100644 --- a/pyomo/core/base/PyomoModel.py +++ b/pyomo/core/base/PyomoModel.py @@ -16,45 +16,40 @@ import gc import time import math -import functools try: from collections import OrderedDict except ImportError: #pragma:nocover from ordereddict import OrderedDict -from pyutilib.math import * -from pyutilib.misc import Container, PauseGC, Bunch +from pyutilib.misc import Container, PauseGC -import pyomo.common +from pyomo.common import timing, PyomoAPIFactory from pyomo.common.dependencies import pympler, pympler_available from pyomo.common.deprecation import deprecation_warning from pyomo.common.plugin import ExtensionPoint -from pyomo.common._task import pyomo_api from pyomo.core.expr import expr_common from pyomo.core.expr.symbol_map import SymbolMap +from pyomo.core.expr.numeric_expr import clone_counter from pyomo.core.base.var import Var from pyomo.core.base.constraint import Constraint from pyomo.core.base.objective import Objective -from pyomo.core.base.set_types import * from pyomo.core.base.suffix import active_import_suffix_generator from pyomo.core.base.indexed_component import IndexedComponent from pyomo.dataportal.DataPortal import DataPortal -from pyomo.core.base.plugin import * -from pyomo.core.base.numvalue import * +from pyomo.core.base.plugin import IPyomoPresolver +from pyomo.core.base.numvalue import value from pyomo.core.base.block import SimpleBlock -from pyomo.core.base.set import Set, UnknownSetDimen +from pyomo.core.base.set import Set from pyomo.core.base.component import Component, ComponentUID from pyomo.core.base.plugin import ModelComponentFactory, TransformationFactory from pyomo.core.base.label import CNameLabeler, CuidLabeler -import pyomo.opt -from pyomo.opt.results import SolverResults, Solution, SolutionStatus, UndefinedData +from pyomo.opt.results import SolverResults, Solution, SolverStatus, UndefinedData from six import itervalues, iteritems, StringIO, string_types -from six.moves import xrange try: unicode except: @@ -221,7 +216,7 @@ def load_from(self, # # If there is a warning, then print a warning message. # - if (results.solver.status == pyomo.opt.SolverStatus.warning): + if (results.solver.status == SolverStatus.warning): logger.warning( 'Loading a SolverResults object with a ' 'warning status into model=%s;\n' @@ -230,8 +225,8 @@ def load_from(self, # # If the solver status not one of either OK or Warning, then generate an error. # - elif results.solver.status != pyomo.opt.SolverStatus.ok: - if (results.solver.status == pyomo.opt.SolverStatus.aborted) and \ + elif results.solver.status != SolverStatus.ok: + if (results.solver.status == SolverStatus.aborted) and \ (len(results.solution) > 0): logger.warning( "Loading a SolverResults object with " @@ -684,7 +679,7 @@ def create_instance( self, filename=None, data=None, name=None, return self.clone() if report_timing: - pyomo.common.timing.report_timing() + timing.report_timing() if name is None: name = self.name @@ -743,7 +738,7 @@ def preprocess(self, preprocessor=None): with PauseGC() as pgc: if preprocessor is None: preprocessor = self.config.preprocessor - pyomo.common.PyomoAPIFactory(preprocessor)(self.config, model=self) + PyomoAPIFactory(preprocessor)(self.config, model=self) def load(self, arg, namespaces=[None], profile_memory=0, report_timing=None): """ diff --git a/pyomo/core/base/__init__.py b/pyomo/core/base/__init__.py index 023a4e6cede..72ad13b8223 100644 --- a/pyomo/core/base/__init__.py +++ b/pyomo/core/base/__init__.py @@ -10,9 +10,43 @@ # TODO: this import is for historical backwards compatibility and should # probably be removed + +from six import iteritems, iterkeys +import pyomo.core.expr.numvalue +import pyomo.core.expr.logical_expr from pyomo.common.collections import ComponentMap +from pyomo.core.expr.symbol_map import SymbolMap +import pyomo.core.base.action +import pyomo.core.base.boolean_var +import pyomo.core.base.check +import pyomo.core.base.component +import pyomo.core.base.config +import pyomo.core.base.constraint +import pyomo.core.base.expression +import pyomo.core.base.global_set +import pyomo.core.base.indexed_component +import pyomo.core.base.indexed_component_slice +import pyomo.core.base.label +import pyomo.core.base.logical_constraint +import pyomo.core.base.misc +import pyomo.core.base.param +import pyomo.core.base.plugin +import pyomo.core.base.range +import pyomo.core.base.set_types +import pyomo.core.base.set +import pyomo.core.base.units_container +import pyomo.core.base.util +import pyomo.core.base.var -from pyomo.core.expr.numvalue import * +from pyomo.core.expr.numvalue import (nonpyomo_leaf_types, + native_types, + native_numeric_types, + value, is_constant, + is_fixed, is_variable_type, + is_potentially_variable, + polynomial_degree, + NumericValue, + ZeroConstant) from pyomo.core.expr.boolean_value import ( as_boolean, BooleanConstant, BooleanValue, native_logical_values) @@ -20,53 +54,96 @@ maximize) from pyomo.core.base.config import PyomoOptions -from pyomo.core.base.expression import * -from pyomo.core.base.label import * +from pyomo.core.base.expression import (Expression, _ExpressionData) +from pyomo.core.base.label import (CuidLabeler, + CounterLabeler, NumericLabeler, + CNameLabeler, TextLabeler, + AlphaNumericTextLabeler, NameLabeler, + ShortNameLabeler) # # Components # -from pyomo.core.base.component import * -import pyomo.core.base.indexed_component -from pyomo.core.base.action import * -from pyomo.core.base.check import * +from pyomo.core.base.component import (name, Component, ComponentUID) +from pyomo.core.base.action import BuildAction +from pyomo.core.base.check import BuildCheck from pyomo.core.base.set import ( Set, SetOf, simple_set_rule, RangeSet, ) -from pyomo.core.base.param import * -from pyomo.core.base.var import * +from pyomo.core.base.param import Param +from pyomo.core.base.var import (Var, _VarData, _GeneralVarData, + SimpleVar, VarList) from pyomo.core.base.boolean_var import ( - BooleanVar, _BooleanVarData, _GeneralBooleanVarData, + BooleanVar, _BooleanVarData, _GeneralBooleanVarData, BooleanVarList, SimpleBooleanVar) -from pyomo.core.base.constraint import * +from pyomo.core.base.constraint import (simple_constraint_rule, + simple_constraintlist_rule, + ConstraintList, Constraint, + _ConstraintData) from pyomo.core.base.logical_constraint import ( LogicalConstraint, LogicalConstraintList, _LogicalConstraintData) -from pyomo.core.base.objective import * -from pyomo.core.base.connector import * -from pyomo.core.base.sos import * -from pyomo.core.base.piecewise import * -from pyomo.core.base.suffix import * -from pyomo.core.base.external import * -from pyomo.core.base.symbol_map import * +from pyomo.core.base.objective import (simple_objective_rule, + simple_objectivelist_rule, + Objective, ObjectiveList, + _ObjectiveData) +from pyomo.core.base.connector import Connector +from pyomo.core.base.sos import SOSConstraint +from pyomo.core.base.piecewise import Piecewise +from pyomo.core.base.suffix import (active_export_suffix_generator, + active_import_suffix_generator, + Suffix) +from pyomo.core.base.external import ExternalFunction +from pyomo.core.base.symbol_map import symbol_map_from_instance from pyomo.core.base.reference import Reference -# -from pyomo.core.base.set_types import * -from pyomo.core.base.misc import * -from pyomo.core.base.block import * -from pyomo.core.base.PyomoModel import * -from pyomo.core.base.plugin import * + +from pyomo.core.base.set import (Reals, PositiveReals, NonPositiveReals, + NegativeReals, NonNegativeReals, Integers, + PositiveIntegers, NonPositiveIntegers, + NegativeIntegers, NonNegativeIntegers, + Boolean, Binary, Any, AnyWithNone, EmptySet, + UnitInterval, PercentFraction, RealInterval, + IntegerInterval) +from pyomo.core.base.misc import display +from pyomo.core.base.block import (SortComponents, TraversalStrategy, + Block, SimpleBlock, active_components, + components, active_components_data, + components_data) +from pyomo.core.base.PyomoModel import (global_option, + ModelSolution, + ModelSolutions, Model, ConcreteModel, + AbstractModel) +from pyomo.core.base.plugin import (pyomo_callback, + IPyomoExpression, ExpressionFactory, + ExpressionRegistration, IPyomoPresolver, + IPyomoPresolveAction, + IParamRepresentation, + ParamRepresentationFactory, + IPyomoScriptPreprocess, + IPyomoScriptCreateModel, + IPyomoScriptCreateDataPortal, + IPyomoScriptModifyInstance, + IPyomoScriptPrintModel, + IPyomoScriptPrintInstance, + IPyomoScriptSaveInstance, + IPyomoScriptPrintResults, + IPyomoScriptSaveResults, + IPyomoScriptPostprocess, + ModelComponentFactory, Transformation, + TransformationFactory) # import pyomo.core.base._pyomo # import pyomo.core.base.util -from pyomo.core.base.instance2dat import * +from pyomo.core.base.instance2dat import instance2dat # These APIs are deprecated and should be removed in the near future from pyomo.core.base.set import ( set_options, RealSet, IntegerSet, BooleanSet, ) +from weakref import ref as weakref_ref + # # This is a hack to strip out modules, which shouldn't have been included in these imports # diff --git a/pyomo/core/base/_pyomo.py b/pyomo/core/base/_pyomo.py index e217eeaae37..8337882283c 100644 --- a/pyomo/core/base/_pyomo.py +++ b/pyomo/core/base/_pyomo.py @@ -9,7 +9,29 @@ # ___________________________________________________________________________ from six import iteritems -from pyomo.core.base.plugin import * +from pyomo.core.base.plugin import (unique_component_name, Factory, implements, + Interface, Plugin, CreatePluginFactory, + ExtensionPoint, TransformationTimer, + registered_callback, pyomo_callback, + IPyomoExpression, ExpressionFactory, + ExpressionRegistration, IPyomoPresolver, + IPyomoPresolveAction, IParamRepresentation, + ParamRepresentationFactory, + IPyomoScriptPreprocess, + IPyomoScriptCreateModel, + IPyomoScriptCreateDataPortal, + IPyomoScriptModifyInstance, + IPyomoScriptPrintModel, + IPyomoScriptPrintInstance, + IPyomoScriptSaveInstance, + IPyomoScriptPrintResults, + IPyomoScriptSaveResults, + IPyomoScriptPostprocess, + ModelComponentFactory, Transformation, + TransformationFactory, + ModelComponentFactoryClass, + TransformationInfo, TransformationData, + apply_transformation) def predefined_sets(): from pyomo.core.base.set import GlobalSets diff --git a/pyomo/core/base/alias.py b/pyomo/core/base/alias.py index 29ab613ec8b..f7e69cccbd1 100644 --- a/pyomo/core/base/alias.py +++ b/pyomo/core/base/alias.py @@ -1,6 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import weakref import logging -from copy import deepcopy from pyomo.common.timing import ConstructionTimer from pyomo.core.base.component import Component, ComponentData diff --git a/pyomo/core/base/block.py b/pyomo/core/base/block.py index 3655f4fc5a0..41ad6a893c6 100644 --- a/pyomo/core/base/block.py +++ b/pyomo/core/base/block.py @@ -20,7 +20,7 @@ import textwrap from inspect import isclass -from operator import itemgetter, attrgetter +from operator import itemgetter from six import iteritems, iterkeys, itervalues, StringIO, string_types, \ advance_iterator, PY3 @@ -33,14 +33,16 @@ from pyomo.common.collections import ComponentMap from pyomo.common.timing import ConstructionTimer -from pyomo.core.base.plugin import * # ModelComponentFactory -from pyomo.core.base.component import Component, ActiveComponentData, \ - ComponentUID -from pyomo.core.base.set import Set, RangeSet, GlobalSetBase, _SetDataBase +from pyomo.core.base.plugin import ModelComponentFactory +from pyomo.core.base.component import ( + Component, ActiveComponentData, ComponentUID, +) +from pyomo.core.base.set import GlobalSetBase, _SetDataBase from pyomo.core.base.var import Var from pyomo.core.base.misc import apply_indexed_rule -from pyomo.core.base.indexed_component import IndexedComponent, \ - ActiveIndexedComponent, UnindexedComponent_set +from pyomo.core.base.indexed_component import ( + ActiveIndexedComponent, UnindexedComponent_set, +) from pyomo.opt.base import ProblemFormat, guess_format from pyomo.opt import WriterFactory @@ -2153,21 +2155,15 @@ def components_data(block, ctype, class _IndexedCustomBlockMeta(type): - """Metaclass for creating an indexed block with - a custom block data type.""" - - def __new__(meta, name, bases, dct): - def __init__(self, *args, **kwargs): - bases[0].__init__(self, *args, **kwargs) + """Metaclass for creating an indexed custom block. + """ - dct["__init__"] = __init__ - return type.__new__(meta, name, bases, dct) + pass class _ScalarCustomBlockMeta(type): - '''Metaclass used to create a scalar block with a - custom block data type - ''' + """Metaclass for creating a scalar custom block. + """ def __new__(meta, name, bases, dct): def __init__(self, *args, **kwargs): @@ -2182,9 +2178,14 @@ def __init__(self, *args, **kwargs): class CustomBlock(Block): - ''' This CustomBlock is the base class that allows - for easy creation of specialized derived blocks - ''' + """ The base class used by instances of custom block components + """ + + def __init__(self, *args, **kwds): + if self._default_ctype is not None: + kwds.setdefault('ctype', self._default_ctype) + Block.__init__(self, *args, **kwds) + def __new__(cls, *args, **kwds): if cls.__name__.startswith('_Indexed') or \ @@ -2193,37 +2194,65 @@ def __new__(cls, *args, **kwds): # therefore, we need to create what we have return super(CustomBlock, cls).__new__(cls) if not args or (args[0] is UnindexedComponent_set and len(args) == 1): - bname = "_Scalar{}".format(cls.__name__) - n = _ScalarCustomBlockMeta(bname, (cls._ComponentDataClass, cls), {}) + n = _ScalarCustomBlockMeta( + "_Scalar%s" % (cls.__name__,), + (cls._ComponentDataClass, cls), + {} + ) return n.__new__(n) else: - bname = "_Indexed{}".format(cls.__name__) - n = _IndexedCustomBlockMeta(bname, (cls,), {}) + n = _IndexedCustomBlockMeta( + "_Indexed%s" % (cls.__name__,), + (cls,), + {} + ) return n.__new__(n) -def declare_custom_block(name): - ''' Decorator to declare the custom component - that goes along with a custom block data +def declare_custom_block(name, new_ctype=None): + """ Decorator to declare components for a custom block data class - @declare_custom_block(name=FooBlock) - class FooBlockData(_BlockData): - # custom block data class - ''' + >>> @declare_custom_block(name=FooBlock) + ... class FooBlockData(_BlockData): + ... # custom block data class + ... pass + """ def proc_dec(cls): # this is the decorator function that # creates the block component class + + # Default (derived) Block attributes + clsbody = { + "__module__": cls.__module__, # magic to fix the module + # Default IndexedComponent data object is the decorated class: + "_ComponentDataClass": cls, + # By default this new block does not declare a new ctype + "_default_ctype": None, + } + c = type( - name, - # name of new class - (CustomBlock,), - # base classes - {"__module__": cls.__module__, - "_ComponentDataClass": cls}) # magic to fix the module + name, # name of new class + (CustomBlock,), # base classes + clsbody, # class body definitions (will populate __dict__) + ) - # are these necessary? + if new_ctype is not None: + if new_ctype is True: + c._default_ctype = c + elif type(new_ctype) is type: + c._default_ctype = new_ctype + else: + raise ValueError("Expected new_ctype to be either type " + "or 'True'; received: %s" % (new_ctype,)) + + # Register the new Block type in the same module as the BlockData setattr(sys.modules[cls.__module__], name, c) + # TODO: can we also register concrete Indexed* and Scalar* + # classes into the original BlockData module (instead of relying + # on metaclasses)? + + # are these necessary? setattr(cls, '_orig_name', name) setattr(cls, '_orig_module', cls.__module__) return cls diff --git a/pyomo/core/base/config.py b/pyomo/core/base/config.py index 247452d6042..7a77b267d70 100644 --- a/pyomo/core/base/config.py +++ b/pyomo/core/base/config.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import os import json diff --git a/pyomo/core/base/connector.py b/pyomo/core/base/connector.py index 94968de5666..b4a145e3a7a 100644 --- a/pyomo/core/base/connector.py +++ b/pyomo/core/base/connector.py @@ -12,8 +12,7 @@ import logging import sys -from six import iteritems, itervalues, iterkeys -from six.moves import xrange +from six import iteritems, itervalues from weakref import ref as weakref_ref from pyomo.common import deprecated diff --git a/pyomo/core/base/constraint.py b/pyomo/core/base/constraint.py index df7556d75e0..415c03a549d 100644 --- a/pyomo/core/base/constraint.py +++ b/pyomo/core/base/constraint.py @@ -24,8 +24,7 @@ value, as_numeric, is_constant, - native_numeric_types, - _sub) + native_numeric_types) from pyomo.core.base.plugin import ModelComponentFactory from pyomo.core.base.component import ActiveComponentData from pyomo.core.base.indexed_component import \ @@ -494,15 +493,14 @@ def set_value(self, expr): self._upper = None self._equality = False - if expr is Constraint.Infeasible: + if expr is Constraint.Skip: + del self.parent_component()[self.index()] + return + elif expr is Constraint.Infeasible: del self.parent_component()[self.index()] raise ValueError( "Constraint '%s' is always infeasible" % (self.name,) ) - elif ( expr is Constraint.Skip or - expr is Constraint.Feasible ): - del self.parent_component()[self.index()] - return else: raise ValueError( "Constraint '%s' does not have a proper " @@ -760,7 +758,7 @@ class Constraint(ActiveIndexedComponent): _ComponentDataClass = _GeneralConstraintData class Infeasible(object): pass - class Feasible(object): pass + Feasible = ActiveIndexedComponent.Skip NoConstraint = ActiveIndexedComponent.Skip Violated = Infeasible Satisfied = Feasible diff --git a/pyomo/core/base/expression.py b/pyomo/core/base/expression.py index 456c287fffb..1aef485e1e3 100644 --- a/pyomo/core/base/expression.py +++ b/pyomo/core/base/expression.py @@ -16,7 +16,6 @@ from pyomo.common.timing import ConstructionTimer -from pyomo.core.expr import current as EXPR from pyomo.core.base.component import ComponentData from pyomo.core.base.plugin import ModelComponentFactory from pyomo.core.base.indexed_component import ( diff --git a/pyomo/core/base/indexed_component.py b/pyomo/core/base/indexed_component.py index 607a934a105..8b6027d2884 100644 --- a/pyomo/core/base/indexed_component.py +++ b/pyomo/core/base/indexed_component.py @@ -10,7 +10,7 @@ __all__ = ['IndexedComponent', 'ActiveIndexedComponent'] -import pyutilib.misc +import logging from pyomo.core.expr.expr_errors import TemplateExpressionError from pyomo.core.expr.numvalue import native_types @@ -26,6 +26,8 @@ from collections.abc import Sequence as collections_Sequence else: from collections import Sequence as collections_Sequence + +logger = logging.getLogger('pyomo.core') sequence_types = {tuple, list} def normalize_index(x): diff --git a/pyomo/core/base/indexed_component_slice.py b/pyomo/core/base/indexed_component_slice.py index 3cc2271e52b..6d67d5bb597 100644 --- a/pyomo/core/base/indexed_component_slice.py +++ b/pyomo/core/base/indexed_component_slice.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ import copy -from six import PY3, iteritems, iterkeys, advance_iterator +from six import iteritems, iterkeys, advance_iterator from pyomo.common import DeveloperError class IndexedComponent_slice(object): @@ -276,6 +276,14 @@ def expanded_items(self): return ((_iter.get_last_index(), _) for _ in _iter) +def _tuple_from_possible_scalar(source): + if type(source) is not tuple: + # This will behave poorly for non-tuple, + # non-string iterables, but we do not + # expect non-tuple, non-string iterables. + return (source,) + return source + def _freeze(info): if info[0] == IndexedComponent_slice.slice_info: return ( @@ -286,17 +294,17 @@ def _freeze(info): info[1][3] # elipsis index ) elif info[0] & IndexedComponent_slice.ITEM_MASK: + index = _tuple_from_possible_scalar(info[1]) return ( info[0], tuple( (x.start,x.stop,x.step) if type(x) is slice else x - for x in info[1] ), + for x in index ), info[2:], ) else: return info - class _slice_generator(object): """Utility (iterator) for generating the elements of one slice diff --git a/pyomo/core/base/instance2dat.py b/pyomo/core/base/instance2dat.py index 569e309c2e9..9df7e9af6d2 100644 --- a/pyomo/core/base/instance2dat.py +++ b/pyomo/core/base/instance2dat.py @@ -29,21 +29,21 @@ def instance2dat(instance, output_filename): if set_object.dim() == 0: if len(set_object) == 0: continue - print >>output_file, "set "+set_name+" := " + output_file.write("set " + set_name + " := \n") for element in set_object: - print >>output_file, element - print >>output_file, ";" + output_file.write(element,) + output_file.write(";\n") elif set_object.dim() == 1: for index in set_object: - print >>output_file, "set "+set_name+"[\""+str(index)+"\"]"+" := ", + output_file.write("set " + set_name + "[\""+str(index) + "\"]"+" :=") for element in set_object[index]: - print >>output_file, element, - print >>output_file, ";" + output_file.write(element,) + output_file.write(";\n") else: - print >>output_file, "***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!" + output_file.write("***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!\n") pass - print >>output_file, "" + output_file.write("\n") for param_name, param_object in iteritems(instance.component_map(Param, active=True)): if (param_object._initialize is not None) and (type(param_object._initialize) is types.FunctionType): @@ -52,19 +52,20 @@ def instance2dat(instance, output_filename): continue if None in param_object: - print >>output_file, "param "+param_name+" := "+str(value(param_object[None]))+" ;" - print >>output_file, "" + output_file.write("param "+param_name+" := " + + str(value(param_object[None])) + " ;\n") + output_file.write("\n") else: - print >>output_file, "param "+param_name+" := " + output_file.write("param " + param_name + " := \n") if param_object.dim() == 1: for index in param_object: - print >>output_file, index, str(value(param_object[index])) + output_file.write(str(index) + str(value(param_object[index])) + "\n") else: for index in param_object: for i in index: - print >>output_file, i, - print >>output_file, str(value(param_object[index])) - print >>output_file, ";" - print >>output_file, "" + output_file.write(i,) + output_file.write(str(value(param_object[index])) + "\n") + output_file.write(";\n") + output_file.write("\n") output_file.close() diff --git a/pyomo/core/base/logical_constraint.py b/pyomo/core/base/logical_constraint.py index 623bc717888..cf96a5c89b3 100644 --- a/pyomo/core/base/logical_constraint.py +++ b/pyomo/core/base/logical_constraint.py @@ -17,7 +17,7 @@ import pyutilib.math from pyomo.common.timing import ConstructionTimer -from pyomo.core.base import Constraint +from pyomo.core.base.constraint import Constraint from pyomo.core.expr import logical_expr from pyomo.core.expr.boolean_value import as_boolean, BooleanConstant from pyomo.core.expr.numvalue import native_types, native_logical_types diff --git a/pyomo/core/base/objective.py b/pyomo/core/base/objective.py index 3a90221181b..d9b171ee74f 100644 --- a/pyomo/core/base/objective.py +++ b/pyomo/core/base/objective.py @@ -23,11 +23,11 @@ from pyomo.common.timing import ConstructionTimer from pyomo.core.expr.numvalue import value -from pyomo.core.expr import current as EXPR from pyomo.core.base.plugin import ModelComponentFactory from pyomo.core.base.component import ActiveComponentData from pyomo.core.base.indexed_component import (ActiveIndexedComponent, - UnindexedComponent_set) + UnindexedComponent_set, + _get_indexed_component_data_name) from pyomo.core.base.expression import (_ExpressionData, _GeneralExpressionDataImpl) from pyomo.core.base.misc import apply_indexed_rule, tabular_writer diff --git a/pyomo/core/base/param.py b/pyomo/core/base/param.py index 6d0bc2c7754..177de9fbe94 100644 --- a/pyomo/core/base/param.py +++ b/pyomo/core/base/param.py @@ -225,8 +225,11 @@ class Param(IndexedComponent): initialize A dictionary or rule for setting up this parameter with existing model data - unit: pyomo unit expression + unit: pyomo unit expression An expression containing the units for the parameter + mutable: `boolean` + Flag indicating if the value of the parameter may change between + calls to a solver. Defaults to `False` """ DefaultMutable = False @@ -248,8 +251,8 @@ def __init__(self, *args, **kwd): self._mutable = kwd.pop('mutable', Param.DefaultMutable ) self._default_val = kwd.pop('default', _NotValid ) self._dense_initialize = kwd.pop('initialize_as_dense', False) - self._units = kwd.pop('units', None) - if self._units is not None: + self._units = kwd.pop('units', None) + if self._units is not None: self._mutable = True # if 'repn' in kwd: @@ -290,6 +293,10 @@ def __iter__(self): return self._data.__iter__() return self._index.__iter__() + @property + def mutable(self): + return self._mutable + # # These are "sparse equivalent" access / iteration methods that # only loop over the defined data. diff --git a/pyomo/core/base/plugin.py b/pyomo/core/base/plugin.py index 5ca11f04e9f..87ae2076a06 100644 --- a/pyomo/core/base/plugin.py +++ b/pyomo/core/base/plugin.py @@ -29,13 +29,11 @@ ] import logging -import pyutilib.misc from pyomo.common.deprecation import deprecated from pyomo.common.modeling import unique_component_name from pyomo.common import Factory from pyomo.common.plugin import ( - alias, implements, Interface, Plugin, PluginFactory, CreatePluginFactory, - PluginError, ExtensionPoint ) + implements, Interface, Plugin, CreatePluginFactory, ExtensionPoint ) from pyomo.common.timing import TransformationTimer logger = logging.getLogger('pyomo.core') diff --git a/pyomo/core/base/set.py b/pyomo/core/base/set.py index 71ada7f23a5..8e381a56359 100644 --- a/pyomo/core/base/set.py +++ b/pyomo/core/base/set.py @@ -16,7 +16,7 @@ import sys import weakref -from six import iteritems, iterkeys +from six import iteritems from six.moves import xrange from pyomo.common.deprecation import deprecated, deprecation_warning @@ -27,8 +27,8 @@ ) from pyomo.core.base.plugin import ModelComponentFactory from pyomo.core.base.util import ( - disable_methods, InitializerBase, Initializer, ConstantInitializer, - CountedCallInitializer, ItemInitializer, IndexedCallInitializer, + disable_methods, InitializerBase, Initializer, + CountedCallInitializer, IndexedCallInitializer, ) from pyomo.core.base.range import ( NumericRange, NonNumericRange, AnyRange, RangeProduct, @@ -411,7 +411,7 @@ def __call__(self, parent, index): return _val elif _val is Set.Skip: return _val - elif not _val: + elif _val is None: return _val if not isinstance(_val, collections_Sequence): @@ -3990,7 +3990,7 @@ class GlobalSet(GlobalSetBase, obj.__class__): global_name = None - def __new__(cls, **kwds): + def __new__(cls, *args, **kwds): """Hijack __new__ to mock up old RealSet el al. interface In the original Set implementation (Pyomo<=5.6.7), the @@ -4003,6 +4003,17 @@ def __new__(cls, **kwds): """ if cls is GlobalSet and GlobalSet.global_name \ and issubclass(GlobalSet, RangeSet): + deprecation_warning( + "The use of RealSet, IntegerSet, BinarySet and " + "BooleanSet as Pyomo Set class generators is " + "deprecated. Please either use one of the pre-declared " + "global Sets (e.g., Reals, NonNegativeReals, Integers, " + "PositiveIntegers, Binary), or create a custom RangeSet.", + version='5.7.1') + # Note: we will completely ignore any positional + # arguments. In this situation, these could be the + # parent_block and any indices; e.g., + # Var(m.I, within=RealSet) base_set = GlobalSets[GlobalSet.global_name] bounds = kwds.pop('bounds', None) range_init = SetInitializer(base_set) @@ -4021,7 +4032,7 @@ def __new__(cls, **kwds): cls_name is not None or bounds is not None): ans._name += str(ans.bounds()) else: - ans = super(GlobalSet, cls).__new__(cls, **kwds) + ans = super(GlobalSet, cls).__new__(cls, *args, **kwds) if kwds: raise RuntimeError("Unexpected keyword arguments: %s" % (kwds,)) return ans diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 03fa8ba8588..9c6d9134f2b 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -13,7 +13,6 @@ 'active_import_suffix_generator') import logging -import pprint from pyomo.common.collections import ComponentMap from pyomo.common.timing import ConstructionTimer diff --git a/pyomo/core/base/symbol_map.py b/pyomo/core/base/symbol_map.py index 9a23252c7e5..9fd0fc30021 100644 --- a/pyomo/core/base/symbol_map.py +++ b/pyomo/core/base/symbol_map.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core.expr.symbol_map import * +from pyomo.core.expr.symbol_map import SymbolMap from pyomo.core.base.label import TextLabeler def symbol_map_from_instance(instance): diff --git a/pyomo/core/base/symbolic.py b/pyomo/core/base/symbolic.py index 9631c6159af..6fa3969ffdc 100644 --- a/pyomo/core/base/symbolic.py +++ b/pyomo/core/base/symbolic.py @@ -10,7 +10,8 @@ from pyomo.common.deprecation import deprecated import pyomo.core.expr.calculus.derivatives as diff_core -from pyomo.core.expr.calculus.diff_with_sympy import differentiate_available, NondifferentiableError +from pyomo.core.expr.calculus.diff_with_sympy import differentiate_available +from pyomo.common.errors import NondifferentiableError @deprecated(msg=('The differentiate function in pyomo.core.base.symbolic has been deprecated. Please use the ' + diff --git a/pyomo/core/base/units_container.py b/pyomo/core/base/units_container.py index 1161d0073b9..3527c79b5c8 100644 --- a/pyomo/core/base/units_container.py +++ b/pyomo/core/base/units_container.py @@ -24,6 +24,7 @@ this module using common notation. .. doctest:: + :skipif: not pint_available >>> from pyomo.environ import units as u >>> print(3.0*u.kg) @@ -37,6 +38,7 @@ There are other methods there that may be helpful for verifying correct units on a model. .. doctest:: + :skipif: not pint_available >>> from pyomo.environ import ConcreteModel, Var, Objective >>> from pyomo.environ import units as u @@ -106,6 +108,7 @@ # * Extend external function interface to support units for the arguments in addition to the function itself import six +import sys from pyomo.common.dependencies import attempt_import from pyomo.core.expr.numvalue import NumericValue, nonpyomo_leaf_types, value, native_numeric_types @@ -1106,7 +1109,7 @@ def exitNode(self, node, data): return (pyomo_unit, pint_unit) raise TypeError('An unhandled expression node type: {} was encountered while retrieving the' - ' units of expression'.format(str(node_type), str(node))) + ' units of expression {}'.format(str(type(node)), str(node))) class PyomoUnitsContainer(object): @@ -1154,6 +1157,7 @@ def load_definitions_from_file(self, definition_file): Then we can add this to the container with: .. doctest:: + :skipif: not pint_available :hide: # get a local units object (to avoid duplicate registration @@ -1164,6 +1168,7 @@ def load_definitions_from_file(self, definition_file): ... tmp = FILE.write("USD = [currency]\\n") .. doctest:: + :skipif: not pint_available >>> u.load_definitions_from_file('my_additional_units.txt') >>> print(u.USD) @@ -1184,6 +1189,7 @@ def load_definitions_from_strings(self, definition_string_list): unit, use .. doctest:: + :skipif: not pint_available :hide: # get a local units object (to avoid duplicate registration @@ -1192,6 +1198,7 @@ def load_definitions_from_strings(self, definition_string_list): >>> u = _units.PyomoUnitsContainer() .. doctest:: + :skipif: not pint_available >>> u.load_definitions_from_strings(['USD = [currency]']) >>> print(u.USD) diff --git a/pyomo/core/base/util.py b/pyomo/core/base/util.py index f2cd15ee231..519577c9c34 100644 --- a/pyomo/core/base/util.py +++ b/pyomo/core/base/util.py @@ -11,7 +11,6 @@ # # Utility functions # -import collections import functools import inspect import six diff --git a/pyomo/core/expr/__init__.py b/pyomo/core/expr/__init__.py index 66af3deb4c4..e2845871788 100644 --- a/pyomo/core/expr/__init__.py +++ b/pyomo/core/expr/__init__.py @@ -16,29 +16,27 @@ # symbols that are used by developers. # -from . import numvalue, numeric_expr, boolean_value, logical_expr, current +from pyomo.core.expr import numvalue, numeric_expr, boolean_value, logical_expr, current -from .numvalue import ( +from pyomo.core.expr.numvalue import ( value, is_constant, is_fixed, is_variable_type, is_potentially_variable, NumericValue, ZeroConstant, native_numeric_types, native_types, polynomial_degree, ) -from .boolean_value import BooleanValue +from pyomo.core.expr.boolean_value import BooleanValue -from .numeric_expr import linear_expression, nonlinear_expression -from .logical_expr import inequality +from pyomo.core.expr.numeric_expr import linear_expression, nonlinear_expression +from pyomo.core.expr.logical_expr import (land, lor, equivalent, exactly, + atleast, atmost, implies, lnot, + xor, inequality) -from .logical_expr import ( - land, lor, equivalent, exactly, atleast, atmost, implies, lnot, xor -) - -from .current import ( +from pyomo.core.expr.current import ( log, log10, sin, cos, tan, cosh, sinh, tanh, asin, acos, atan, exp, sqrt, asinh, acosh, atanh, ceil, floor, Expr_if, ) -from .calculus.derivatives import differentiate -from .taylor_series import taylor_series_expansion +from pyomo.core.expr.calculus.derivatives import differentiate +from pyomo.core.expr.taylor_series import taylor_series_expansion diff --git a/pyomo/core/expr/calculus/derivatives.py b/pyomo/core/expr/calculus/derivatives.py index 2537b6d78bd..a41e4bc5e67 100644 --- a/pyomo/core/expr/calculus/derivatives.py +++ b/pyomo/core/expr/calculus/derivatives.py @@ -1,5 +1,14 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import enum -from pyomo.common.collections import ComponentMap from .diff_with_sympy import differentiate as sympy_diff from .diff_with_pyomo import reverse_sd, reverse_ad diff --git a/pyomo/core/expr/calculus/diff_with_pyomo.py b/pyomo/core/expr/calculus/diff_with_pyomo.py index b08b253f401..cb4d12182fe 100644 --- a/pyomo/core/expr/calculus/diff_with_pyomo.py +++ b/pyomo/core/expr/calculus/diff_with_pyomo.py @@ -1,8 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.common.collections import ComponentMap from pyomo.core.expr import current as _expr from pyomo.core.expr.visitor import ExpressionValueVisitor, nonpyomo_leaf_types from pyomo.core.expr.numvalue import value -from pyomo.core.expr.current import exp, log, sin, cos, tan, asin, acos, atan +from pyomo.core.expr.current import exp, log, sin, cos import math @@ -53,6 +63,15 @@ def _diff_SumExpression(node, val_dict, der_dict): der_dict[arg] += der +def _diff_LinearExpression(node, val_dict, der_dict): + der = der_dict[node] + for ndx, v in enumerate(node.linear_vars): + coef = node.linear_coefs[ndx] + der_dict[v] += der * val_dict[coef] + der_dict[coef] += der * val_dict[v] + + der_dict[node.constant] += der + def _diff_PowExpression(node, val_dict, der_dict): """ @@ -325,6 +344,16 @@ def _diff_ExternalFunctionExpression(node, val_dict, der_dict): _diff_map[_expr.NegationExpression] = _diff_NegationExpression _diff_map[_expr.UnaryFunctionExpression] = _diff_UnaryFunctionExpression _diff_map[_expr.ExternalFunctionExpression] = _diff_ExternalFunctionExpression +_diff_map[_expr.LinearExpression] = _diff_LinearExpression + +_diff_map[_expr.NPV_ProductExpression] = _diff_ProductExpression +_diff_map[_expr.NPV_DivisionExpression] = _diff_DivisionExpression +_diff_map[_expr.NPV_ReciprocalExpression] = _diff_ReciprocalExpression +_diff_map[_expr.NPV_PowExpression] = _diff_PowExpression +_diff_map[_expr.NPV_SumExpression] = _diff_SumExpression +_diff_map[_expr.NPV_NegationExpression] = _diff_NegationExpression +_diff_map[_expr.NPV_UnaryFunctionExpression] = _diff_UnaryFunctionExpression +_diff_map[_expr.NPV_ExternalFunctionExpression] = _diff_ExternalFunctionExpression class _NamedExpressionCollector(ExpressionValueVisitor): @@ -393,6 +422,18 @@ def visiting_potential_leaf(self, node): self.der_dict[node] = 0 return True, node + if node.__class__ is _expr.LinearExpression: + for v in node.linear_vars + node.linear_coefs + [node.constant]: + val = value(v) + self.val_dict[v] = val + if v not in self.der_dict: + self.der_dict[v] = 0 + val = value(node) + self.val_dict[node] = val + if node not in self.der_dict: + self.der_dict[node] = 0 + return True, val + if not node.is_expression_type(): val = value(node) self.val_dict[node] = val @@ -488,6 +529,18 @@ def visiting_potential_leaf(self, node): self.der_dict[node] = 0 return True, node + if node.__class__ is _expr.LinearExpression: + for v in node.linear_vars + node.linear_coefs + [node.constant]: + val = v + self.val_dict[v] = val + if v not in self.der_dict: + self.der_dict[v] = 0 + val = node + self.val_dict[node] = val + if node not in self.der_dict: + self.der_dict[node] = 0 + return True, val + if not node.is_expression_type(): val = node self.val_dict[node] = val diff --git a/pyomo/core/expr/calculus/diff_with_sympy.py b/pyomo/core/expr/calculus/diff_with_sympy.py index 3e290da8db8..bc4c58c89c1 100644 --- a/pyomo/core/expr/calculus/diff_with_sympy.py +++ b/pyomo/core/expr/calculus/diff_with_sympy.py @@ -9,7 +9,6 @@ # ___________________________________________________________________________ from pyomo.core.expr.sympy_tools import sympy_available, sympyify_expression, sympy2pyomo_expression -from pyomo.common.errors import NondifferentiableError # A "public" attribute indicating that differentiate() can be called # ... this provides a bit of future-proofing for alternative approaches diff --git a/pyomo/core/expr/current.py b/pyomo/core/expr/current.py index d36f3c4ac12..590e00092ac 100755 --- a/pyomo/core/expr/current.py +++ b/pyomo/core/expr/current.py @@ -11,6 +11,11 @@ from __future__ import division import math +# +# Common intrinsic functions +# +from pyomo.core.expr import expr_common as common + # # Provide a global value that indicates which expression system is being used # @@ -18,10 +23,6 @@ class Mode(object): pyomo5_trees = (3,) _mode = Mode.pyomo5_trees -# -# Common intrinsic functions -# -from pyomo.core.expr import expr_common as common # # Pull symbols from the appropriate expression system # @@ -31,22 +32,111 @@ class Mode(object): # Pyomo5 if _mode == Mode.pyomo5_trees: from pyomo.core.expr import numeric_expr as _numeric_expr - from pyomo.core.expr.numeric_expr import * - from pyomo.core.expr.numeric_expr import ( - _generate_sum_expression, - _generate_mul_expression, - _generate_other_expression, - _generate_intrinsic_function_expression, - ) + from pyomo.core.expr.numeric_expr import (_add, _sub, _mul, _div, _pow, + _neg, _abs, _inplace, _unary, + NumericValue, native_types, + nonpyomo_leaf_types, + native_numeric_types, + as_numeric, value, + evaluate_expression, + expression_to_string, + polynomial_degree, + clone_expression, + sizeof_expression, + _expression_is_fixed, + clone_counter, + nonlinear_expression, + linear_expression, ExpressionBase, + NegationExpression, + NPV_NegationExpression, + ExternalFunctionExpression, + NPV_ExternalFunctionExpression, + PowExpression, NPV_PowExpression, + ProductExpression, + NPV_ProductExpression, + MonomialTermExpression, + DivisionExpression, + NPV_DivisionExpression, + ReciprocalExpression, + NPV_ReciprocalExpression, + _LinearOperatorExpression, + SumExpressionBase, + NPV_SumExpression, SumExpression, + _MutableSumExpression, + Expr_ifExpression, + UnaryFunctionExpression, + NPV_UnaryFunctionExpression, + AbsExpression, NPV_AbsExpression, + LinearExpression, + _MutableLinearExpression, + decompose_term, + LinearDecompositionError, + _decompose_linear_terms, + _process_arg, + _generate_sum_expression, + _generate_mul_expression, + _generate_other_expression, + _generate_intrinsic_function_expression, + _balanced_parens, + NPV_expression_types) from pyomo.core.expr import logical_expr as _logical_expr - from pyomo.core.expr.logical_expr import * - from pyomo.core.expr.logical_expr import ( - _generate_relational_expression, - _chainedInequality, - ) - from pyomo.core.expr.template_expr import * + from pyomo.core.expr.logical_expr import (native_logical_types, BooleanValue, + BooleanConstant, _lt, _le, _eq, + _and, _or, _equiv, _inv, _xor, + _impl, _chainedInequality, + RangedExpression, + InequalityExpression, inequality, + EqualityExpression, + _generate_relational_expression, + _generate_logical_proposition, + BooleanExpressionBase, lnot, + equivalent, xor, implies, + _flattened, land, lor, exactly, + atmost, atleast, + UnaryBooleanExpression, + NotExpression, + BinaryBooleanExpression, + EquivalenceExpression, + XorExpression, + ImplicationExpression, + NaryBooleanExpression, + _add_to_and_or_expression, + AndExpression, OrExpression, + ExactlyExpression, + AtMostExpression, + AtLeastExpression, + special_boolean_atom_types) + from pyomo.core.expr.template_expr import (TemplateExpressionError, + _NotSpecified, GetItemExpression, + GetAttrExpression, + _TemplateSumExpression_argList, + TemplateSumExpression, + IndexTemplate, resolve_template, + ReplaceTemplateExpression, + substitute_template_expression, + _GetItemIndexer, + substitute_getitem_with_param, + substitute_template_with_value, + _set_iterator_template_generator, + _template_iter_context, + templatize_rule, + templatize_constraint) from pyomo.core.expr import visitor as _visitor - from pyomo.core.expr.visitor import * + from pyomo.core.expr.visitor import (SymbolMap, StreamBasedExpressionVisitor, + SimpleExpressionVisitor, + ExpressionValueVisitor, + replace_expressions, + ExpressionReplacementVisitor, + _EvaluationVisitor, + FixedExpressionError, + NonConstantExpressionError, + _EvaluateConstantExpressionVisitor, + _ComponentVisitor, identify_components, + _VariableVisitor, identify_variables, + _MutableParamVisitor, + identify_mutable_parameters, + _PolynomialDegreeVisitor, + _IsFixedVisitor, _ToStringVisitor) # FIXME: we shouldn't need circular dependencies between modules _visitor.LinearExpression = _numeric_expr.LinearExpression _visitor.MonomialTermExpression = _numeric_expr.MonomialTermExpression diff --git a/pyomo/core/expr/logical_expr.py b/pyomo/core/expr/logical_expr.py index dda4d89bd93..f15a41e49f0 100644 --- a/pyomo/core/expr/logical_expr.py +++ b/pyomo/core/expr/logical_expr.py @@ -34,11 +34,7 @@ ) from .expr_common import ( - _add, _sub, _mul, _div, - _pow, _neg, _abs, _inplace, - _unary, _radd, _rsub, _rmul, - _rdiv, _rpow, _iadd, _isub, - _imul, _idiv, _ipow, _lt, _le, + _lt, _le, _eq, _and, _or, _equiv, _inv, _xor, _impl) diff --git a/pyomo/core/expr/numeric_expr.py b/pyomo/core/expr/numeric_expr.py index 418061dad2c..0fe25d98139 100644 --- a/pyomo/core/expr/numeric_expr.py +++ b/pyomo/core/expr/numeric_expr.py @@ -13,25 +13,19 @@ import math import logging from itertools import islice -from six import itervalues logger = logging.getLogger('pyomo.core') from pyutilib.math.util import isclose from pyomo.common.deprecation import deprecated -from pyomo.common.errors import DeveloperError from .expr_common import ( _add, _sub, _mul, _div, _pow, _neg, _abs, _inplace, - _unary, _radd, _rsub, _rmul, - _rdiv, _rpow, _iadd, _isub, - _imul, _idiv, _ipow, _lt, _le, - _eq, + _unary ) from .numvalue import ( NumericValue, - NumericConstant, native_types, nonpyomo_leaf_types, native_numeric_types, diff --git a/pyomo/core/expr/numvalue.py b/pyomo/core/expr/numvalue.py index 4ce25f5019a..b516f729e5a 100644 --- a/pyomo/core/expr/numvalue.py +++ b/pyomo/core/expr/numvalue.py @@ -15,11 +15,11 @@ import sys import logging -from six import iteritems, PY3, string_types, text_type, binary_type +from six import iteritems, PY3 from pyomo.core.expr.expr_common import \ (_add, _sub, _mul, _div, _pow, - _neg, _abs, _inplace, _radd, + _neg, _abs, _radd, _rsub, _rmul, _rdiv, _rpow, _iadd, _isub, _imul, _idiv, _ipow, _lt, _le, _eq) @@ -118,6 +118,7 @@ def __setstate__(self, state): else: native_types.add(unicode) native_boolean_types.add(unicode) + native_types.update( native_numeric_types ) native_types.update( native_integer_types ) native_types.update( native_boolean_types ) diff --git a/pyomo/core/expr/symbol_map.py b/pyomo/core/expr/symbol_map.py index 8f73181e981..15f7979fc22 100644 --- a/pyomo/core/expr/symbol_map.py +++ b/pyomo/core/expr/symbol_map.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ from weakref import ref as weakref_ref -from six import iteritems, iterkeys +from six import iteritems class SymbolMap(object): """ diff --git a/pyomo/core/expr/sympy_tools.py b/pyomo/core/expr/sympy_tools.py index 568debcf098..b1cba66f67a 100644 --- a/pyomo/core/expr/sympy_tools.py +++ b/pyomo/core/expr/sympy_tools.py @@ -8,13 +8,11 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from six import StringIO, iterkeys -import pyutilib.misc +from six import iterkeys from pyomo.common import DeveloperError from pyomo.common.collections import ComponentMap from pyomo.common.dependencies import attempt_import from pyomo.common.errors import NondifferentiableError -from pyomo.core.expr import current from pyomo.core.expr import current as EXPR, native_types from pyomo.core.expr.numvalue import value @@ -35,23 +33,23 @@ def _configure_sympy(sympy, available): sympy.Add: _sum, sympy.Mul: _prod, sympy.Pow: lambda x, y: x**y, - sympy.exp: lambda x: current.exp(x), - sympy.log: lambda x: current.log(x), - sympy.sin: lambda x: current.sin(x), - sympy.asin: lambda x: current.asin(x), - sympy.sinh: lambda x: current.sinh(x), - sympy.asinh: lambda x: current.asinh(x), - sympy.cos: lambda x: current.cos(x), - sympy.acos: lambda x: current.acos(x), - sympy.cosh: lambda x: current.cosh(x), - sympy.acosh: lambda x: current.acosh(x), - sympy.tan: lambda x: current.tan(x), - sympy.atan: lambda x: current.atan(x), - sympy.tanh: lambda x: current.tanh(x), - sympy.atanh: lambda x: current.atanh(x), - sympy.ceiling: lambda x: current.ceil(x), - sympy.floor: lambda x: current.floor(x), - sympy.sqrt: lambda x: current.sqrt(x), + sympy.exp: lambda x: EXPR.exp(x), + sympy.log: lambda x: EXPR.log(x), + sympy.sin: lambda x: EXPR.sin(x), + sympy.asin: lambda x: EXPR.asin(x), + sympy.sinh: lambda x: EXPR.sinh(x), + sympy.asinh: lambda x: EXPR.asinh(x), + sympy.cos: lambda x: EXPR.cos(x), + sympy.acos: lambda x: EXPR.acos(x), + sympy.cosh: lambda x: EXPR.cosh(x), + sympy.acosh: lambda x: EXPR.acosh(x), + sympy.tan: lambda x: EXPR.tan(x), + sympy.atan: lambda x: EXPR.atan(x), + sympy.tanh: lambda x: EXPR.tanh(x), + sympy.atanh: lambda x: EXPR.atanh(x), + sympy.ceiling: lambda x: EXPR.ceil(x), + sympy.floor: lambda x: EXPR.floor(x), + sympy.sqrt: lambda x: EXPR.sqrt(x), sympy.Abs: lambda x: abs(x), sympy.Derivative: _nondifferentiable, sympy.Tuple: lambda *x: x, diff --git a/pyomo/core/expr/template_expr.py b/pyomo/core/expr/template_expr.py index 6c45fa6f07e..72420d5df97 100644 --- a/pyomo/core/expr/template_expr.py +++ b/pyomo/core/expr/template_expr.py @@ -12,12 +12,12 @@ import itertools import logging import sys -from six import iteritems, itervalues +from six import itervalues from six.moves import builtins from pyomo.core.expr.expr_errors import TemplateExpressionError from pyomo.core.expr.numvalue import ( - NumericValue, native_numeric_types, native_types, nonpyomo_leaf_types, + NumericValue, native_types, nonpyomo_leaf_types, as_numeric, value, ) from pyomo.core.expr.numeric_expr import ExpressionBase, SumExpression diff --git a/pyomo/core/expr/visitor.py b/pyomo/core/expr/visitor.py index 33b241318f1..c74b324539e 100644 --- a/pyomo/core/expr/visitor.py +++ b/pyomo/core/expr/visitor.py @@ -25,9 +25,6 @@ logger = logging.getLogger('pyomo.core') -from pyutilib.misc.visitor import SimpleVisitor, ValueVisitor -from pyutilib.math.util import isclose - from .symbol_map import SymbolMap from . import expr_common as common from .expr_errors import TemplateExpressionError diff --git a/pyomo/core/kernel/__init__.py b/pyomo/core/kernel/__init__.py index 45845531093..331280a1477 100644 --- a/pyomo/core/kernel/__init__.py +++ b/pyomo/core/kernel/__init__.py @@ -8,7 +8,26 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core.expr import * +from pyomo.core.expr import numvalue, numeric_expr, boolean_value, logical_expr, current +from pyomo.core.expr.numvalue import ( + value, is_constant, is_fixed, is_variable_type, + is_potentially_variable, NumericValue, ZeroConstant, + native_numeric_types, native_types, polynomial_degree, +) +from pyomo.core.expr.boolean_value import BooleanValue +from pyomo.core.expr.numeric_expr import linear_expression, nonlinear_expression +from pyomo.core.expr.logical_expr import (land, lor, equivalent, exactly, + atleast, atmost, implies, lnot, + xor, inequality) +from pyomo.core.expr.current import ( + log, log10, sin, cos, tan, cosh, sinh, tanh, + asin, acos, atan, exp, sqrt, asinh, acosh, + atanh, ceil, floor, + Expr_if, +) +from pyomo.core.expr.calculus.derivatives import differentiate +from pyomo.core.expr.taylor_series import taylor_series_expansion + import pyomo.core.kernel.register_numpy_types import pyomo.core.kernel.base import pyomo.core.kernel.homogeneous_container diff --git a/pyomo/core/kernel/block.py b/pyomo/core/kernel/block.py index 6b804f407c3..18190888e10 100644 --- a/pyomo/core/kernel/block.py +++ b/pyomo/core/kernel/block.py @@ -357,7 +357,6 @@ def load_solution(self, value in the solution is consistent with the value of a fixed variable. """ - import pyomo.opt from pyomo.core.kernel.suffix import \ import_suffix_generator diff --git a/pyomo/core/kernel/component_map.py b/pyomo/core/kernel/component_map.py index 6485385d42c..e4cc4f8b172 100644 --- a/pyomo/core/kernel/component_map.py +++ b/pyomo/core/kernel/component_map.py @@ -13,4 +13,4 @@ deprecation_warning( 'The pyomo.core.kernel.component_map module is deprecated. ' 'Import ComponentMap from pyomo.common.collections.', - version='TBD') + version='5.7.1') diff --git a/pyomo/core/kernel/component_set.py b/pyomo/core/kernel/component_set.py index b789aaefe97..db830400a2c 100644 --- a/pyomo/core/kernel/component_set.py +++ b/pyomo/core/kernel/component_set.py @@ -13,4 +13,4 @@ deprecation_warning( 'The pyomo.core.kernel.component_set module is deprecated. ' 'Import ComponentSet from pyomo.common.collections.', - version='TBD') + version='5.7.1') diff --git a/pyomo/core/kernel/constraint.py b/pyomo/core/kernel/constraint.py index 93d3a92728a..4e23f4188a1 100644 --- a/pyomo/core/kernel/constraint.py +++ b/pyomo/core/kernel/constraint.py @@ -9,22 +9,17 @@ # ___________________________________________________________________________ from pyomo.core.expr.numvalue import (ZeroConstant, - is_constant, as_numeric, - native_types, is_potentially_variable, is_numeric_data, - value, - _sub) + value) from pyomo.core.expr import logical_expr from pyomo.core.kernel.base import \ (ICategorizedObject, - _abstract_readwrite_property, _abstract_readonly_property) from pyomo.core.kernel.container_utils import \ define_simple_containers -import six from six.moves import zip _pos_inf = float('inf') diff --git a/pyomo/core/kernel/dict_container.py b/pyomo/core/kernel/dict_container.py index cfc3d4485e1..eebf65b205a 100644 --- a/pyomo/core/kernel/dict_container.py +++ b/pyomo/core/kernel/dict_container.py @@ -24,7 +24,7 @@ IHomogeneousContainer import six -from six import itervalues, iteritems +from six import itervalues if six.PY3: from collections.abc import MutableMapping as collections_MutableMapping diff --git a/pyomo/core/kernel/expression.py b/pyomo/core/kernel/expression.py index 2a98c8e7ebd..7fda9f49ff5 100644 --- a/pyomo/core/kernel/expression.py +++ b/pyomo/core/kernel/expression.py @@ -8,26 +8,19 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys - -from pyomo.core.expr import expr_common from pyomo.core.expr import current as EXPR from pyomo.core.kernel.base import \ (ICategorizedObject, - _abstract_readwrite_property, - _abstract_readonly_property) + _abstract_readwrite_property) from pyomo.core.kernel.container_utils import \ define_simple_containers from pyomo.core.expr.numvalue import (NumericValue, is_fixed, is_constant, - is_variable_type, is_potentially_variable, is_numeric_data, value) -import six - class IIdentityExpression(NumericValue): """The interface for classes that simply wrap another expression and perform no additional operations. diff --git a/pyomo/core/kernel/homogeneous_container.py b/pyomo/core/kernel/homogeneous_container.py index 1ead62255ef..8cdb855ebf5 100644 --- a/pyomo/core/kernel/homogeneous_container.py +++ b/pyomo/core/kernel/homogeneous_container.py @@ -8,10 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core.kernel.base import \ - (_no_ctype, - _convert_descend_into, - ICategorizedObjectContainer) +from pyomo.core.kernel.base import ICategorizedObjectContainer class IHomogeneousContainer(ICategorizedObjectContainer): """ diff --git a/pyomo/core/kernel/matrix_constraint.py b/pyomo/core/kernel/matrix_constraint.py index 044631cf4a4..9b081c1534f 100644 --- a/pyomo/core/kernel/matrix_constraint.py +++ b/pyomo/core/kernel/matrix_constraint.py @@ -12,13 +12,11 @@ numpy, numpy_available as has_numpy, scipy, scipy_available as has_scipy, ) -import pyomo.core.expr from pyomo.core.expr.numvalue import NumericValue from pyomo.core.kernel.constraint import \ (IConstraint, constraint_tuple) -import six from six.moves import zip, xrange _noarg = object() diff --git a/pyomo/core/kernel/objective.py b/pyomo/core/kernel/objective.py index 15cae0b0e39..b5d006a18f9 100644 --- a/pyomo/core/kernel/objective.py +++ b/pyomo/core/kernel/objective.py @@ -9,20 +9,15 @@ # ___________________________________________________________________________ from pyomo.core.expr.numvalue import as_numeric -from pyomo.core.kernel.base import \ - (ICategorizedObject, - _abstract_readwrite_property, - _abstract_readonly_property) -from pyomo.core.kernel.container_utils import \ - define_simple_containers +from pyomo.core.kernel.base import _abstract_readwrite_property +from pyomo.core.kernel.container_utils import define_simple_containers from pyomo.core.kernel.expression import IExpression -import six - # Constants used to define the optimization sense minimize=1 maximize=-1 + class IObjective(IExpression): """ The interface for optimization objectives. @@ -46,6 +41,7 @@ class IObjective(IExpression): def is_minimizing(self): return self.sense == minimize + class objective(IObjective): """An optimization objective.""" _ctype = IObjective @@ -73,6 +69,7 @@ def __init__(self, expr=None, sense=minimize): @property def expr(self): return self._expr + @expr.setter def expr(self, expr): self._expr = as_numeric(expr) if (expr is not None) else None @@ -84,6 +81,7 @@ def expr(self, expr): @property def sense(self): return self._sense + @sense.setter def sense(self, sense): """Set the sense (direction) of this objective.""" @@ -96,6 +94,7 @@ def sense(self, sense): "[minimize (%s), maximize (%s)]. Invalid " "value: %s'" % (minimize, maximize, sense)) + # inserts class definitions for simple _tuple, _list, and # _dict containers into this module define_simple_containers(globals(), diff --git a/pyomo/core/kernel/parameter.py b/pyomo/core/kernel/parameter.py index c6bde55277c..a985e3205dd 100644 --- a/pyomo/core/kernel/parameter.py +++ b/pyomo/core/kernel/parameter.py @@ -8,17 +8,11 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pyomo.core.expr from pyomo.core.expr.numvalue import (is_numeric_data, NumericValue) -from pyomo.core.kernel.base import \ - (ICategorizedObject, - _abstract_readwrite_property, - _abstract_readonly_property) -from pyomo.core.kernel.container_utils import \ - define_simple_containers +from pyomo.core.kernel.base import ICategorizedObject +from pyomo.core.kernel.container_utils import define_simple_containers -import six class IParameter(ICategorizedObject, NumericValue): """The interface for mutable numeric data.""" @@ -64,6 +58,7 @@ def polynomial_degree(self): variables.""" return 0 + class parameter(IParameter): """A object for storing a mutable, numeric value that can be used to build a symbolic expression.""" @@ -95,10 +90,12 @@ def __call__(self, exception=True): def value(self): """The value of the paramater""" return self._value + @value.setter def value(self, value): self._value = value + class functional_value(IParameter): """An object for storing a numeric function that can be used in a symbolic expression. @@ -146,10 +143,12 @@ def __call__(self, exception=True): def fn(self): """The function stored with this object""" return self._fn + @fn.setter def fn(self, fn): self._fn = fn + # inserts class definitions for simple _tuple, _list, and # _dict containers into this module define_simple_containers(globals(), diff --git a/pyomo/core/kernel/piecewise_library/transforms.py b/pyomo/core/kernel/piecewise_library/transforms.py index db5f1df8928..dcc588b6bf6 100644 --- a/pyomo/core/kernel/piecewise_library/transforms.py +++ b/pyomo/core/kernel/piecewise_library/transforms.py @@ -39,8 +39,7 @@ variable_tuple, variable_dict, variable) -from pyomo.core.kernel.constraint import (constraint, - constraint_list, +from pyomo.core.kernel.constraint import (constraint_list, constraint_tuple, linear_constraint) from pyomo.core.kernel.sos import sos2 @@ -52,8 +51,7 @@ generate_gray_code, PiecewiseValidationError) -import six -from six.moves import xrange, zip +from six.moves import xrange logger = logging.getLogger('pyomo.core') diff --git a/pyomo/core/kernel/piecewise_library/util.py b/pyomo/core/kernel/piecewise_library/util.py index 14072d10323..5c389e852dc 100644 --- a/pyomo/core/kernel/piecewise_library/util.py +++ b/pyomo/core/kernel/piecewise_library/util.py @@ -11,7 +11,6 @@ import operator import itertools -import six from six.moves import xrange from six import advance_iterator diff --git a/pyomo/core/kernel/set_types.py b/pyomo/core/kernel/set_types.py index c77cb970d52..ca82d2fcca1 100644 --- a/pyomo/core/kernel/set_types.py +++ b/pyomo/core/kernel/set_types.py @@ -9,11 +9,6 @@ # ___________________________________________________________________________ import logging -from weakref import ref as weakref_ref - -from pyomo.core.expr.numvalue import (native_numeric_types, - native_integer_types, - native_boolean_types) logger = logging.getLogger('pyomo.core') diff --git a/pyomo/core/kernel/sos.py b/pyomo/core/kernel/sos.py index fd8798b1821..fdb2401b849 100644 --- a/pyomo/core/kernel/sos.py +++ b/pyomo/core/kernel/sos.py @@ -8,16 +8,13 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pyomo.core.expr from pyomo.core.expr.numvalue import is_numeric_data from pyomo.core.kernel.base import \ (ICategorizedObject, - _abstract_readwrite_property, - _abstract_readonly_property) + _abstract_readwrite_property) from pyomo.core.kernel.container_utils import \ define_simple_containers -import six from six.moves import zip class ISOS(ICategorizedObject): diff --git a/pyomo/core/kernel/suffix.py b/pyomo/core/kernel/suffix.py index 6c2488f5637..90592cf665e 100644 --- a/pyomo/core/kernel/suffix.py +++ b/pyomo/core/kernel/suffix.py @@ -12,9 +12,7 @@ from pyomo.common.collections import ComponentMap from pyomo.core.kernel.base import ( - ICategorizedObject, - _abstract_readwrite_property, - _abstract_readonly_property, + ICategorizedObject, _abstract_readonly_property ) from pyomo.core.kernel.dict_container import DictContainer from pyomo.core.kernel.container_utils import ( diff --git a/pyomo/core/kernel/tuple_container.py b/pyomo/core/kernel/tuple_container.py index e37d108e090..34b32cbdf4d 100644 --- a/pyomo/core/kernel/tuple_container.py +++ b/pyomo/core/kernel/tuple_container.py @@ -12,7 +12,6 @@ IHomogeneousContainer import six -from six.moves import xrange as range if six.PY3: from collections.abc import Sequence as collections_Sequence diff --git a/pyomo/core/kernel/variable.py b/pyomo/core/kernel/variable.py index bd22bdcdd12..382aad7e70e 100644 --- a/pyomo/core/kernel/variable.py +++ b/pyomo/core/kernel/variable.py @@ -13,8 +13,7 @@ value) from pyomo.core.kernel.base import \ (ICategorizedObject, - _abstract_readwrite_property, - _abstract_readonly_property) + _abstract_readwrite_property) from pyomo.core.kernel.container_utils import \ define_simple_containers from pyomo.core.kernel.set_types import (RealSet, diff --git a/pyomo/core/plugins/transform/add_slack_vars.py b/pyomo/core/plugins/transform/add_slack_vars.py index 6ce699121c5..16c13edb582 100644 --- a/pyomo/core/plugins/transform/add_slack_vars.py +++ b/pyomo/core/plugins/transform/add_slack_vars.py @@ -1,15 +1,26 @@ -import pyomo.environ -from pyomo.core import * -from pyomo.gdp import * -from pyomo.opt import SolverFactory +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import TransformationFactory, Var, NonNegativeReals, Constraint, Objective, Block, value + +from six import iterkeys from pyomo.common.modeling import unique_component_name from pyomo.core.plugins.transform.hierarchy import NonIsomorphicTransformation from pyomo.common.config import ConfigBlock, ConfigValue from pyomo.core.base.component import ComponentUID -from pyomo.core.base import Constraint, _ConstraintData +from pyomo.core.base import _ConstraintData from pyomo.common.deprecation import deprecation_warning +NAME_BUFFER = {} + def target_list(x): deprecation_msg = ("In future releases ComponentUID targets will no " "longer be supported in the core.add_slack_variables " @@ -46,6 +57,9 @@ def target_list(x): "Expected Constraint or list of Constraints." "\n\tRecieved %s" % (type(x),)) +import logging +logger = logging.getLogger('pyomo.core') + @TransformationFactory.register('core.add_slack_variables', \ doc="Create a model where we add slack variables to every constraint " @@ -72,6 +86,13 @@ def __init__(self, **kwds): super(AddSlackVariables, self).__init__(**kwds) def _apply_to(self, instance, **kwds): + try: + assert not NAME_BUFFER + self._apply_to_impl(instance, **kwds) + finally: + NAME_BUFFER.clear() + + def _apply_to_impl(self, instance, **kwds): config = self.CONFIG(kwds.pop('options', {})) config.set_value(kwds) targets = config.targets @@ -116,10 +137,12 @@ def _apply_to(self, instance, **kwds): raise RuntimeError("Lower bound exceeds upper bound in " "constraint %s" % cons.name) if not cons.active: continue + cons_name = cons.getname(fully_qualified=True, + name_buffer=NAME_BUFFER) if cons.lower is not None: # we add positive slack variable to body: # declare positive slack - varName = "_slack_plus_" + cons.name + varName = "_slack_plus_" + cons_name posSlack = Var(within=NonNegativeReals) xblock.add_component(varName, posSlack) # add positive slack to body expression @@ -129,7 +152,7 @@ def _apply_to(self, instance, **kwds): if cons.upper is not None: # we subtract a positive slack variable from the body: # declare slack - varName = "_slack_minus_" + cons.name + varName = "_slack_minus_" + cons_name negSlack = Var(within=NonNegativeReals) xblock.add_component(varName, negSlack) # add negative slack to body expression diff --git a/pyomo/core/plugins/transform/eliminate_fixed_vars.py b/pyomo/core/plugins/transform/eliminate_fixed_vars.py index 4844604b625..138c2f9c878 100644 --- a/pyomo/core/plugins/transform/eliminate_fixed_vars.py +++ b/pyomo/core/plugins/transform/eliminate_fixed_vars.py @@ -10,7 +10,7 @@ from pyomo.core.expr.current import ExpressionBase from pyomo.core.expr.numvalue import as_numeric -from pyomo.core import Constraint, Objective +from pyomo.core import Constraint, Objective, TransformationFactory from pyomo.core.base.var import Var, _VarData from pyomo.core.base.util import sequence from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation diff --git a/pyomo/core/plugins/transform/equality_transform.py b/pyomo/core/plugins/transform/equality_transform.py index 9356217af6e..8f45851ed52 100644 --- a/pyomo/core/plugins/transform/equality_transform.py +++ b/pyomo/core/plugins/transform/equality_transform.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core import * +from pyomo.core import TransformationFactory, Var, NonNegativeReals from pyomo.core.base.misc import create_name from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation diff --git a/pyomo/core/plugins/transform/expand_connectors.py b/pyomo/core/plugins/transform/expand_connectors.py index 30cc718397a..c606e59b90b 100644 --- a/pyomo/core/plugins/transform/expand_connectors.py +++ b/pyomo/core/plugins/transform/expand_connectors.py @@ -16,7 +16,7 @@ from pyomo.common.collections import ComponentMap, ComponentSet from pyomo.core.expr import current as EXPR from pyomo.core.base import Transformation, TransformationFactory, Connector, Constraint, \ - ConstraintList, Var, VarList, TraversalStrategy, SortComponents + ConstraintList, Var, SortComponents from pyomo.core.base.connector import _ConnectorData, SimpleConnector diff --git a/pyomo/core/plugins/transform/nonnegative_transform.py b/pyomo/core/plugins/transform/nonnegative_transform.py index f50dd4490fd..d3afa2e38a6 100644 --- a/pyomo/core/plugins/transform/nonnegative_transform.py +++ b/pyomo/core/plugins/transform/nonnegative_transform.py @@ -8,13 +8,14 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import copy - from pyomo.core.expr import current as EXPR -from pyomo.core import * -from pyomo.core.base.expression import _ExpressionData -from pyomo.core.base.var import SimpleVar, _VarData +from pyomo.core import (nonpyomo_leaf_types, TransformationFactory, IntegerSet, + Integers, PositiveIntegers, NonPositiveIntegers, + NegativeIntegers, NonNegativeIntegers, Reals, PositiveReals, + NonNegativeReals, NegativeReals, NonPositiveReals, + PercentFraction, RealSet, Var, Set, value, Binary, + Constraint, Objective) from pyomo.core.base.misc import create_name from pyomo.core.plugins.transform.util import partial from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation diff --git a/pyomo/core/plugins/transform/radix_linearization.py b/pyomo/core/plugins/transform/radix_linearization.py index 252ec2e2778..c428e1a53f2 100644 --- a/pyomo/core/plugins/transform/radix_linearization.py +++ b/pyomo/core/plugins/transform/radix_linearization.py @@ -11,6 +11,7 @@ from pyomo.core.expr.current import ProductExpression, PowExpression from pyomo.core import Binary, value from pyomo.core.base import Transformation, TransformationFactory, Var, Constraint, ConstraintList, Block, RangeSet +from pyomo.core.base.numvalue import as_numeric from pyomo.core.base.var import _VarData from six import iteritems diff --git a/pyomo/core/plugins/transform/standard_form.py b/pyomo/core/plugins/transform/standard_form.py index f20a2d53796..0125173f8a6 100644 --- a/pyomo/core/plugins/transform/standard_form.py +++ b/pyomo/core/plugins/transform/standard_form.py @@ -8,16 +8,16 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core import * +from pyomo.core import TransformationFactory from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation -from pyomo.core.plugins.transform.nonnegative_transform import * -from pyomo.core.plugins.transform.equality_transform import * +from pyomo.core.plugins.transform.nonnegative_transform import NonNegativeTransformation +from pyomo.core.plugins.transform.equality_transform import EqualityTransform @TransformationFactory.register("core.standard_form", doc="Create an equivalent LP model in standard form.") class StandardForm(IsomorphicTransformation): """ - Produces a standard-form representation of the model. This form has + Produces a standard-form representation of the model. This form has the coefficient matrix (A), the cost vector (c), and the constraint vector (b), where the 'standard form' problem is diff --git a/pyomo/core/plugins/transform/util.py b/pyomo/core/plugins/transform/util.py index e7f997ff32c..958f0ac7982 100644 --- a/pyomo/core/plugins/transform/util.py +++ b/pyomo/core/plugins/transform/util.py @@ -13,7 +13,7 @@ # """ from inspect import isroutine -from pyomo.core import * +from pyomo.core import Var, Objective, Constraint, Set, Param def collectAbstractComponents(model): diff --git a/pyomo/core/tests/data/test_odbc_ini.py b/pyomo/core/tests/data/test_odbc_ini.py index 50e63c701aa..7202c243283 100644 --- a/pyomo/core/tests/data/test_odbc_ini.py +++ b/pyomo/core/tests/data/test_odbc_ini.py @@ -12,19 +12,17 @@ # import os - import pyutilib.th as unittest -from pyomo.environ import * - try: import pyodbc pyodbc_available = True - from pyomo.core.plugins.data.db_table import ODBCConfig, ODBCError + from pyomo.dataportal.plugins.db_table import ODBCConfig, ODBCError except ImportError: pyodbc_available = False + @unittest.skipIf(not pyodbc_available, "PyODBC is not installed.") class TestODBCIni(unittest.TestCase): @@ -66,33 +64,33 @@ def test_create(self): def test_init_empty_data(self): config = ODBCConfig() - self.assertEquals({}, config.sources) - self.assertEquals({}, config.source_specs) - self.assertEquals({}, config.odbc_info) + self.assertEqual({}, config.sources) + self.assertEqual({}, config.source_specs) + self.assertEqual({}, config.odbc_info) def test_init_simple_data(self): config = ODBCConfig(data=self.simple_data) - self.assertEquals({'testdb' : self.ACCESS_CONFIGSTR}, config.sources) - self.assertEquals({'testdb' : {'Database' : "testdb.mdb"}}, config.source_specs) - self.assertEquals({}, config.odbc_info) + self.assertEqual({'testdb' : self.ACCESS_CONFIGSTR}, config.sources) + self.assertEqual({'testdb' : {'Database' : "testdb.mdb"}}, config.source_specs) + self.assertEqual({}, config.odbc_info) def test_init_complex_data(self): config = ODBCConfig(data=self.complex_data) - self.assertEquals({'test1' : self.ACCESS_CONFIGSTR, 'test2' : self.EXCEL_CONFIGSTR}, config.sources) - self.assertEquals({'test1' : {'Database' : "test1.db", 'LogonID' : "Admin", 'pwd' : "secret_pass"}, 'test2' : {'Database' : "test2.xls"}}, config.source_specs) - self.assertEquals({'UNICODE' : "UTF-8"}, config.odbc_info) + self.assertEqual({'test1' : self.ACCESS_CONFIGSTR, 'test2' : self.EXCEL_CONFIGSTR}, config.sources) + self.assertEqual({'test1' : {'Database' : "test1.db", 'LogonID' : "Admin", 'pwd' : "secret_pass"}, 'test2' : {'Database' : "test2.xls"}}, config.source_specs) + self.assertEqual({'UNICODE' : "UTF-8"}, config.odbc_info) def test_add_source(self): config = ODBCConfig() config.add_source("testdb", self.ACCESS_CONFIGSTR) - self.assertEquals({'testdb' : self.ACCESS_CONFIGSTR}, config.sources) - self.assertEquals({}, config.source_specs) - self.assertEquals({}, config.odbc_info) + self.assertEqual({'testdb' : self.ACCESS_CONFIGSTR}, config.sources) + self.assertEqual({}, config.source_specs) + self.assertEqual({}, config.odbc_info) def test_del_source(self): config = ODBCConfig(data=self.simple_data) config.del_source('testdb') - self.assertEquals({}, config.sources) + self.assertEqual({}, config.sources) def test_add_source_reserved(self): config = ODBCConfig() @@ -105,7 +103,7 @@ def test_add_source_spec(self): config = ODBCConfig() config.add_source("testdb", self.ACCESS_CONFIGSTR) config.add_source_spec("testdb", {'Database' : "testdb.mdb"}) - self.assertEquals({'testdb' : {'Database' : "testdb.mdb"}}, config.source_specs) + self.assertEqual({'testdb' : {'Database' : "testdb.mdb"}}, config.source_specs) def test_add_spec_bad(self): config = ODBCConfig() @@ -117,13 +115,13 @@ def test_del_source_dependent(self): config.add_source("testdb", self.ACCESS_CONFIGSTR) config.add_source_spec("testdb", {'Database' : "testdb.mdb"}) config.del_source("testdb") - self.assertEquals({}, config.sources) - self.assertEquals({}, config.source_specs) + self.assertEqual({}, config.sources) + self.assertEqual({}, config.source_specs) def test_set_odbc_info(self): config = ODBCConfig() config.set_odbc_info("UNICODE", "UTF-8") - self.assertEquals({'UNICODE' : "UTF-8"}, config.odbc_info) + self.assertEqual({'UNICODE' : "UTF-8"}, config.odbc_info) def test_odbc_repr(self): config = ODBCConfig(data=self.simple_data) @@ -140,7 +138,7 @@ def test_baselines(self): config.write(outPath) written = ODBCConfig(filename = outPath) - self.assertEquals(config, written) + self.assertEqual(config, written) try: os.remove(outPath) @@ -148,13 +146,13 @@ def test_baselines(self): pass def test_eq(self): - self.assertEquals(ODBCConfig(), ODBCConfig()) + self.assertEqual(ODBCConfig(), ODBCConfig()) configA = ODBCConfig(data=self.simple_data) configB = ODBCConfig() configB.sources = {'testdb' : self.ACCESS_CONFIGSTR} configB.source_specs = {'testdb' : {'Database' : 'testdb.mdb'}} - self.assertEquals(configA, configB) + self.assertEqual(configA, configB) if __name__ == "__main__": unittest.main() diff --git a/pyomo/core/tests/examples/pmedian.py b/pyomo/core/tests/examples/pmedian.py index d3ce86b9485..5e7071fa33b 100644 --- a/pyomo/core/tests/examples/pmedian.py +++ b/pyomo/core/tests/examples/pmedian.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Param, RangeSet, Var, Reals, Binary, PositiveIntegers, Constraint, Objective import math model = AbstractModel() diff --git a/pyomo/core/tests/examples/pmedian4.py b/pyomo/core/tests/examples/pmedian4.py index c34b39b1c42..ad1bb0b1337 100644 --- a/pyomo/core/tests/examples/pmedian4.py +++ b/pyomo/core/tests/examples/pmedian4.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, RangeSet, Param, Var, Reals, Binary, Objective, Constraint, ConstraintList import math N = 5 diff --git a/pyomo/core/tests/examples/test_book.py b/pyomo/core/tests/examples/test_book.py index c5d9e282953..dfcd07617d9 100644 --- a/pyomo/core/tests/examples/test_book.py +++ b/pyomo/core/tests/examples/test_book.py @@ -22,7 +22,6 @@ os.chdir(test_dir) sys.path.append(test_dir) -#from test_book_examples import * if __name__ == "__main__": unittest.main() diff --git a/pyomo/core/tests/examples/test_kernel_examples.py b/pyomo/core/tests/examples/test_kernel_examples.py index 54f44001f66..ff8c7faa139 100644 --- a/pyomo/core/tests/examples/test_kernel_examples.py +++ b/pyomo/core/tests/examples/test_kernel_examples.py @@ -12,7 +12,6 @@ # Tests for Pyomo kernel examples # -import os import glob import sys from os.path import basename, dirname, abspath, join diff --git a/pyomo/core/tests/examples/test_tutorials.py b/pyomo/core/tests/examples/test_tutorials.py index 8811c091734..c797d252180 100644 --- a/pyomo/core/tests/examples/test_tutorials.py +++ b/pyomo/core/tests/examples/test_tutorials.py @@ -17,11 +17,9 @@ currdir = dirname(abspath(__file__))+os.sep tutorial_dir=topdir+os.sep+"examples"+os.sep+"pyomo"+os.sep+"tutorials"+os.sep -import pyutilib.misc +from pyutilib.misc import run_file import pyutilib.th as unittest -from pyomo.environ import * - try: from win32com.client.dynamic import Dispatch _win32com=True @@ -59,24 +57,24 @@ def construct(self,filename): pass def test_data(self): - pyutilib.misc.run_file(tutorial_dir+"data.py", logfile=currdir+"data.log", execdir=tutorial_dir) + run_file(tutorial_dir+"data.py", logfile=currdir+"data.log", execdir=tutorial_dir) self.assertFileEqualsBaseline(currdir+"data.log", tutorial_dir+"data.out") @unittest.skipIf(not ((_win32com and _excel_available) or _xlrd or _openpyxl), "Cannot read excel file.") def test_excel(self): - pyutilib.misc.run_file(tutorial_dir+"excel.py", logfile=currdir+"excel.log", execdir=tutorial_dir) + run_file(tutorial_dir+"excel.py", logfile=currdir+"excel.log", execdir=tutorial_dir) self.assertFileEqualsBaseline(currdir+"excel.log", tutorial_dir+"excel.out") def test_set(self): - pyutilib.misc.run_file(tutorial_dir+"set.py", logfile=currdir+"set.log", execdir=tutorial_dir) + run_file(tutorial_dir+"set.py", logfile=currdir+"set.log", execdir=tutorial_dir) self.assertFileEqualsBaseline(currdir+"set.log", tutorial_dir+"set.out") def test_table(self): - pyutilib.misc.run_file(tutorial_dir+"table.py", logfile=currdir+"table.log", execdir=tutorial_dir) + run_file(tutorial_dir+"table.py", logfile=currdir+"table.log", execdir=tutorial_dir) self.assertFileEqualsBaseline(currdir+"table.log", tutorial_dir+"table.out") def test_param(self): - pyutilib.misc.run_file(tutorial_dir+"param.py", logfile=currdir+"param.log", execdir=tutorial_dir) + run_file(tutorial_dir+"param.py", logfile=currdir+"param.log", execdir=tutorial_dir) self.assertFileEqualsBaseline(currdir+"param.log", tutorial_dir+"param.out") if __name__ == "__main__": diff --git a/pyomo/core/tests/transform/test_add_slacks.py b/pyomo/core/tests/transform/test_add_slacks.py index ace1126704f..e8d7c80f580 100644 --- a/pyomo/core/tests/transform/test_add_slacks.py +++ b/pyomo/core/tests/transform/test_add_slacks.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import os from os.path import abspath, dirname currdir = dirname(abspath(__file__))+os.sep @@ -6,14 +16,17 @@ from pyomo.common.log import LoggingIntercept import pyutilib.th as unittest -import pyutilib.services import random -import pyomo.opt -from pyomo.environ import * +from pyomo.opt import check_available_solvers +from pyomo.environ import (ConcreteModel, Set, Objective, + Constraint, Var, Block, Param, + NonNegativeReals, TransformationFactory, ComponentUID, + inequality) + import pyomo.core.expr.current as EXPR -solvers = pyomo.opt.check_available_solvers('glpk') +solvers = check_available_solvers('glpk') class TestAddSlacks(unittest.TestCase): diff --git a/pyomo/core/tests/transform/test_scaling.py b/pyomo/core/tests/transform/test_scaling.py index 64023bdbff7..49c8691e7ce 100644 --- a/pyomo/core/tests/transform/test_scaling.py +++ b/pyomo/core/tests/transform/test_scaling.py @@ -2,30 +2,30 @@ # # Pyomo: Python Optimization Modeling Objects # Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC -# Under the terms of Contract DE-NA0003525 with National Technology and -# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ # - import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.opt.base.solvers import UnknownSolver + class TestScaleModelTransformation(unittest.TestCase): def test_linear_scaling(self): - model = pe.ConcreteModel() - model.x = pe.Var([1,2,3], bounds=(-10,10), initialize=5.0) - model.z = pe.Var(bounds=(10,20)) - model.obj = pe.Objective(expr=model.z + model.x[1]) + model = pyo.ConcreteModel() + model.x = pyo.Var([1, 2, 3], bounds=(-10, 10), initialize=5.0) + model.z = pyo.Var(bounds=(10, 20)) + model.obj = pyo.Objective(expr=model.z + model.x[1]) # test scaling of duals as well - model.dual = pe.Suffix(direction=pe.Suffix.IMPORT) - model.rc = pe.Suffix(direction=pe.Suffix.IMPORT) - + model.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT) + model.rc = pyo.Suffix(direction=pyo.Suffix.IMPORT) + def con_rule(m, i): if i == 1: return m.x[1] + 2*m.x[2] + 1*m.x[3] == 4.0 @@ -33,8 +33,8 @@ def con_rule(m, i): return m.x[1] + 2*m.x[2] + 2*m.x[3] == 5.0 if i == 3: return m.x[1] + 3.0*m.x[2] + 1*m.x[3] == 5.0 - model.con = pe.Constraint([1,2,3], rule=con_rule) - model.zcon = pe.Constraint(expr=model.z >= model.x[2]) + model.con = pyo.Constraint([1,2,3], rule=con_rule) + model.zcon = pyo.Constraint(expr=model.z >= model.x[2]) x_scale = 0.5 obj_scale = 2.0 @@ -43,9 +43,9 @@ def con_rule(m, i): con_scale2 = 2.0 con_scale3 = -5.0 zcon_scale = -3.0 - + unscaled_model = model.clone() - unscaled_model.scaling_factor = pe.Suffix(direction=pe.Suffix.EXPORT) + unscaled_model.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) unscaled_model.scaling_factor[unscaled_model.obj] = obj_scale unscaled_model.scaling_factor[unscaled_model.x] = x_scale unscaled_model.scaling_factor[unscaled_model.z] = z_scale @@ -53,15 +53,15 @@ def con_rule(m, i): unscaled_model.scaling_factor[unscaled_model.con[2]] = con_scale2 unscaled_model.scaling_factor[unscaled_model.con[3]] = con_scale3 unscaled_model.scaling_factor[unscaled_model.zcon] = zcon_scale - - scaled_model = pe.TransformationFactory('core.scale_model').create_using(unscaled_model) + + scaled_model = pyo.TransformationFactory('core.scale_model').create_using(unscaled_model) # print('*** unscaled ***') # unscaled_model.pprint() # print('*** scaled ***') # scaled_model.pprint() - glpk_solver = pe.SolverFactory('glpk') + glpk_solver = pyo.SolverFactory('glpk') if isinstance(glpk_solver, UnknownSolver) or \ (not glpk_solver.available()): raise unittest.SkipTest("glpk solver not available") @@ -70,63 +70,64 @@ def con_rule(m, i): glpk_solver.solve(scaled_model) # check vars - self.assertAlmostEqual(pe.value(unscaled_model.x[1]), pe.value(scaled_model.scaled_x[1])/x_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.x[2]), pe.value(scaled_model.scaled_x[2])/x_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.x[3]), pe.value(scaled_model.scaled_x[3])/x_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.z), pe.value(scaled_model.scaled_z)/z_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[1]), pyo.value(scaled_model.scaled_x[1])/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[2]), pyo.value(scaled_model.scaled_x[2])/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[3]), pyo.value(scaled_model.scaled_x[3])/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.z), pyo.value(scaled_model.scaled_z)/z_scale, 4) # check var lb - self.assertAlmostEqual(pe.value(unscaled_model.x[1].lb), pe.value(scaled_model.scaled_x[1].lb)/x_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.x[2].lb), pe.value(scaled_model.scaled_x[2].lb)/x_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.x[3].lb), pe.value(scaled_model.scaled_x[3].lb)/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[1].lb), pyo.value(scaled_model.scaled_x[1].lb)/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[2].lb), pyo.value(scaled_model.scaled_x[2].lb)/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[3].lb), pyo.value(scaled_model.scaled_x[3].lb)/x_scale, 4) # note: z_scale is negative, therefore, the inequality directions swap - self.assertAlmostEqual(pe.value(unscaled_model.z.lb), pe.value(scaled_model.scaled_z.ub)/z_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.z.lb), pyo.value(scaled_model.scaled_z.ub)/z_scale, 4) # check var ub - self.assertAlmostEqual(pe.value(unscaled_model.x[1].ub), pe.value(scaled_model.scaled_x[1].ub)/x_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.x[2].ub), pe.value(scaled_model.scaled_x[2].ub)/x_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.x[3].ub), pe.value(scaled_model.scaled_x[3].ub)/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[1].ub), pyo.value(scaled_model.scaled_x[1].ub)/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[2].ub), pyo.value(scaled_model.scaled_x[2].ub)/x_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.x[3].ub), pyo.value(scaled_model.scaled_x[3].ub)/x_scale, 4) # note: z_scale is negative, therefore, the inequality directions swap - self.assertAlmostEqual(pe.value(unscaled_model.z.ub), pe.value(scaled_model.scaled_z.lb)/z_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.z.ub), pyo.value(scaled_model.scaled_z.lb)/z_scale, 4) # check var multipliers (rc) - self.assertAlmostEqual(pe.value(unscaled_model.rc[unscaled_model.x[1]]), pe.value(scaled_model.rc[scaled_model.scaled_x[1]])*x_scale/obj_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.rc[unscaled_model.x[2]]), pe.value(scaled_model.rc[scaled_model.scaled_x[2]])*x_scale/obj_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.rc[unscaled_model.x[3]]), pe.value(scaled_model.rc[scaled_model.scaled_x[3]])*x_scale/obj_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.rc[unscaled_model.z]), pe.value(scaled_model.rc[scaled_model.scaled_z])*z_scale/obj_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.x[1]]), pyo.value(scaled_model.rc[scaled_model.scaled_x[1]])*x_scale/obj_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.x[2]]), pyo.value(scaled_model.rc[scaled_model.scaled_x[2]])*x_scale/obj_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.x[3]]), pyo.value(scaled_model.rc[scaled_model.scaled_x[3]])*x_scale/obj_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.rc[unscaled_model.z]), pyo.value(scaled_model.rc[scaled_model.scaled_z])*z_scale/obj_scale, 4) # check constraint multipliers - self.assertAlmostEqual(pe.value(unscaled_model.dual[unscaled_model.con[1]]),pe.value(scaled_model.dual[scaled_model.scaled_con[1]])*con_scale1/obj_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.dual[unscaled_model.con[2]]),pe.value(scaled_model.dual[scaled_model.scaled_con[2]])*con_scale2/obj_scale, 4) - self.assertAlmostEqual(pe.value(unscaled_model.dual[unscaled_model.con[3]]),pe.value(scaled_model.dual[scaled_model.scaled_con[3]])*con_scale3/obj_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.dual[unscaled_model.con[1]]),pyo.value(scaled_model.dual[scaled_model.scaled_con[1]])*con_scale1/obj_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.dual[unscaled_model.con[2]]),pyo.value(scaled_model.dual[scaled_model.scaled_con[2]])*con_scale2/obj_scale, 4) + self.assertAlmostEqual(pyo.value(unscaled_model.dual[unscaled_model.con[3]]),pyo.value(scaled_model.dual[scaled_model.scaled_con[3]])*con_scale3/obj_scale, 4) # put the solution from the scaled back into the original - pe.TransformationFactory('core.scale_model').propagate_solution(scaled_model, model) + pyo.TransformationFactory('core.scale_model').propagate_solution(scaled_model, model) # compare var values and rc with the unscaled soln - for vm in model.component_objects(ctype=pe.Var, descend_into=True): - cuid = pe.ComponentUID(vm) + for vm in model.component_objects(ctype=pyo.Var, descend_into=True): + cuid = pyo.ComponentUID(vm) vum = cuid.find_component_on(unscaled_model) self.assertEqual((vm in model.rc), (vum in unscaled_model.rc)) if vm in model.rc: - self.assertAlmostEqual(pe.value(model.rc[vm]), pe.value(unscaled_model.rc[vum]), 4) + self.assertAlmostEqual(pyo.value(model.rc[vm]), pyo.value(unscaled_model.rc[vum]), 4) for k in vm: vmk = vm[k] vumk = vum[k] - self.assertAlmostEqual(pe.value(vmk), pe.value(vumk), 4) + self.assertAlmostEqual(pyo.value(vmk), pyo.value(vumk), 4) self.assertEqual((vmk in model.rc), (vumk in unscaled_model.rc)) if vmk in model.rc: - self.assertAlmostEqual(pe.value(model.rc[vmk]), pe.value(unscaled_model.rc[vumk]), 4) + self.assertAlmostEqual(pyo.value(model.rc[vmk]), pyo.value(unscaled_model.rc[vumk]), 4) # compare constraint duals and value - for model_con in model.component_objects(ctype=pe.Constraint, descend_into=True): - cuid = pe.ComponentUID(model_con) + for model_con in model.component_objects(ctype=pyo.Constraint, descend_into=True): + cuid = pyo.ComponentUID(model_con) unscaled_model_con = cuid.find_component_on(unscaled_model) self.assertEqual((model_con in model.rc), (unscaled_model_con in unscaled_model.rc)) if model_con in model.dual: - self.assertAlmostEqual(pe.value(model.dual[model_con]), pe.value(unscaled_model.dual[unscaled_model_con]), 4) + self.assertAlmostEqual(pyo.value(model.dual[model_con]), pyo.value(unscaled_model.dual[unscaled_model_con]), 4) for k in model_con: mk = model_con[k] umk = unscaled_model_con[k] self.assertEqual((mk in model.dual), (umk in unscaled_model.dual)) if mk in model.dual: - self.assertAlmostEqual(pe.value(model.dual[mk]), pe.value(unscaled_model.dual[umk]), 4) - + self.assertAlmostEqual(pyo.value(model.dual[mk]), pyo.value(unscaled_model.dual[umk]), 4) + + if __name__ == "__main__": unittest.main() diff --git a/pyomo/core/tests/transform/test_transform.py b/pyomo/core/tests/transform/test_transform.py index 15eb6ff97ec..5a9ff0a9f20 100644 --- a/pyomo/core/tests/transform/test_transform.py +++ b/pyomo/core/tests/transform/test_transform.py @@ -16,13 +16,21 @@ currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest -import pyutilib.services +from pyutilib.services import TempfileManager -import pyomo.opt -from pyomo.environ import * +from pyomo.opt import check_available_solvers +from pyomo.environ import (AbstractModel, Set, RangeSet, Objective, + Constraint, Var, Block, Integers, Boolean, + Binary, Reals, RealSet, NonNegativeIntegers, + NonNegativeReals, NegativeReals, NegativeIntegers, + PositiveReals, PositiveIntegers, NonPositiveIntegers, + NonPositiveReals, TransformationFactory, SolverFactory, + sum_product) +from pyomo.core.plugins.transform.standard_form import StandardForm +from pyomo.core.plugins.transform.nonnegative_transform import NonNegativeTransformation -solvers = pyomo.opt.check_available_solvers('glpk') +solvers = check_available_solvers('glpk') class Test(unittest.TestCase): @@ -33,7 +41,7 @@ def setUp(self): def tearDown(self): if os.path.exists("unknown.lp"): os.unlink("unknown.lp") - pyutilib.services.TempfileManager.clear_tempfiles() + TempfileManager.clear_tempfiles() if os.path.exists(os.path.join(currdir,'result.yml')): os.remove(os.path.join(currdir,'result.yml')) self.model = None diff --git a/pyomo/core/tests/unit/kernel/test_block.py b/pyomo/core/tests/unit/kernel/test_block.py index ecefb7d1b16..f72f6549b52 100644 --- a/pyomo/core/tests/unit/kernel/test_block.py +++ b/pyomo/core/tests/unit/kernel/test_block.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import tempfile import os import pickle @@ -50,7 +60,6 @@ from pyomo.core.kernel.sos import sos from pyomo.opt.results import Solution -import six from six import StringIO def _path_to_object_exists(obj, descendent): diff --git a/pyomo/core/tests/unit/kernel/test_conic.py b/pyomo/core/tests/unit/kernel/test_conic.py index a05d534ac35..fb3bd045d12 100644 --- a/pyomo/core/tests/unit/kernel/test_conic.py +++ b/pyomo/core/tests/unit/kernel/test_conic.py @@ -1,8 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import math import pyutilib.th as unittest -import pyomo.kernel +from pyomo.kernel import pprint, IntegerSet from pyomo.core.kernel.base import ICategorizedObject from pyomo.core.kernel.constraint import (IConstraint, linear_constraint, @@ -23,7 +33,6 @@ primal_power, dual_exponential, dual_power) -from pyomo.kernel import IntegerSet class _conic_tester_base(object): @@ -33,21 +42,20 @@ def setUp(self): assert self._object_factory is not None def test_pprint(self): - import pyomo.kernel # Not really testing what the output is, just that # an error does not occur. The pprint functionality # is still in the early stages. c = self._object_factory() - pyomo.kernel.pprint(c) + pprint(c) b = block() b.c = c - pyomo.kernel.pprint(c) - pyomo.kernel.pprint(b) + pprint(c) + pprint(b) m = block() m.b = b - pyomo.kernel.pprint(c) - pyomo.kernel.pprint(b) - pyomo.kernel.pprint(m) + pprint(c) + pprint(b) + pprint(m) def test_type(self): c = self._object_factory() diff --git a/pyomo/core/tests/unit/kernel/test_constraint.py b/pyomo/core/tests/unit/kernel/test_constraint.py index 27cc1bd2dcf..ed10d014430 100644 --- a/pyomo/core/tests/unit/kernel/test_constraint.py +++ b/pyomo/core/tests/unit/kernel/test_constraint.py @@ -1,8 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest from pyomo.core.expr import logical_expr -import pyomo.kernel +from pyomo.kernel import pprint from pyomo.core.tests.unit.kernel.test_dict_container import \ _TestActiveDictContainerBase from pyomo.core.tests.unit.kernel.test_tuple_container import \ @@ -21,28 +31,25 @@ from pyomo.core.kernel.expression import (expression, data_expression) from pyomo.core.kernel.block import block -from pyomo.core.kernel.set_types import (RealSet, - IntegerSet) class Test_constraint(unittest.TestCase): def test_pprint(self): - import pyomo.kernel # Not really testing what the output is, just that # an error does not occur. The pprint functionality # is still in the early stages. v = variable() c = constraint((1, v**2, 2)) - pyomo.kernel.pprint(c) + pprint(c) b = block() b.c = c - pyomo.kernel.pprint(c) - pyomo.kernel.pprint(b) + pprint(c) + pprint(b) m = block() m.b = b - pyomo.kernel.pprint(c) - pyomo.kernel.pprint(b) - pyomo.kernel.pprint(m) + pprint(c) + pprint(b) + pprint(m) def test_ctype(self): c = constraint() @@ -1588,16 +1595,16 @@ def test_pprint(self): # is still in the early stages. v = variable() c = linear_constraint(lb=1, terms=[(v,1)], ub=1) - pyomo.kernel.pprint(c) + pprint(c) b = block() b.c = c - pyomo.kernel.pprint(c) - pyomo.kernel.pprint(b) + pprint(c) + pprint(b) m = block() m.b = b - pyomo.kernel.pprint(c) - pyomo.kernel.pprint(b) - pyomo.kernel.pprint(m) + pprint(c) + pprint(b) + pprint(m) def test_ctype(self): c = linear_constraint([],[]) diff --git a/pyomo/core/tests/unit/kernel/test_dict_container.py b/pyomo/core/tests/unit/kernel/test_dict_container.py index a74b387fe48..3ac7fdff12c 100644 --- a/pyomo/core/tests/unit/kernel/test_dict_container.py +++ b/pyomo/core/tests/unit/kernel/test_dict_container.py @@ -19,8 +19,7 @@ from pyomo.core.kernel.homogeneous_container import \ IHomogeneousContainer from pyomo.core.kernel.dict_container import DictContainer -from pyomo.core.kernel.block import (IBlock, - block, +from pyomo.core.kernel.block import (block, block_dict) import six @@ -46,9 +45,11 @@ # and weakref (bas _pickle_test_protocol = pickle.HIGHEST_PROTOCOL + class _bad_ctype(object): ctype = "_this_is_definitely_not_the_ctype_being_tested" + class _TestDictContainerBase(object): # set by derived class @@ -102,7 +103,7 @@ def test_init1(self): cdict = self._container_type() def test_init2(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] self._container_type((i, self._ctype_factory()) for i in index) self._container_type(((i, self._ctype_factory()) @@ -155,7 +156,7 @@ def test_len2(self): def test_setitem(self): cdict = self._container_type() - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] for i in index: self.assertTrue(i not in cdict) for cnt, i in enumerate(index, 1): @@ -168,13 +169,13 @@ def test_setitem(self): # examined more carefully before supporting it. # For now just test that implicit assignment raises an exception def test_wrong_type_init(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] with self.assertRaises(TypeError): c = self._container_type( (i, _bad_ctype()) for i in index) def test_wrong_type_update(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] c = self._container_type() with self.assertRaises(TypeError): c.update((i, _bad_ctype()) for i in index) @@ -221,7 +222,7 @@ def test_has_parent_setitem(self): # make sure an existing Data object IS replaced # by a call to setitem and not simply updated. def test_setitem_exists_overwrite(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] c = self._container_type((i, self._ctype_factory()) for i in index) self.assertEqual(len(c), len(index)) @@ -235,7 +236,7 @@ def test_setitem_exists_overwrite(self): self.assertEqual(cdata.parent, None) def test_delitem(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] c = self._container_type((i, self._ctype_factory()) for i in index) self.assertEqual(len(c), len(index)) @@ -250,7 +251,7 @@ def test_delitem(self): self.assertEqual(cdata.parent, None) def test_iter(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] c = self._container_type((i, self._ctype_factory()) for i in index) self.assertEqual(len(c), len(index)) @@ -260,7 +261,7 @@ def test_iter(self): self.assertTrue(idx in index) def test_pickle(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] cdict = self._container_type((i, self._ctype_factory()) for i in index) cdict[0] = self._container_type() @@ -279,7 +280,7 @@ def test_pickle(self): self.assertTrue(cdict[i].parent is cdict) def test_keys(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] raw_constraint_dict = {i:self._ctype_factory() for i in index} c = self._container_type(raw_constraint_dict) self.assertEqual(sorted(list(raw_constraint_dict.keys()), @@ -287,7 +288,7 @@ def test_keys(self): sorted(list(c.keys()), key=str)) def test_values(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] raw_constraint_dict = {i:self._ctype_factory() for i in index} c = self._container_type(raw_constraint_dict) self.assertEqual( @@ -299,7 +300,7 @@ def test_values(self): key=str)) def test_items(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] raw_constraint_dict = {i:self._ctype_factory() for i in index} c = self._container_type(raw_constraint_dict) self.assertEqual( @@ -311,7 +312,7 @@ def test_items(self): key=str)) def test_update(self): - index = ['a', 1, None, (1,), (1,2)] + index = ['a', 1, None, (1,), (1, 2)] raw_constraint_dict = {i:self._ctype_factory() for i in index} c = self._container_type() c.update(raw_constraint_dict) @@ -596,6 +597,7 @@ def descend(x): [id(c) for c in descend.seen]) return cdict, traversal + class _TestActiveDictContainerBase(_TestDictContainerBase): def test_active_type(self): @@ -613,7 +615,7 @@ def test_active(self): children[1] = self._ctype_factory() children[None] = self._ctype_factory() children[(1,)] = self._ctype_factory() - children[(1,2)] = self._ctype_factory() + children[(1, 2)] = self._ctype_factory() children['(1,2)'] = self._ctype_factory() cdict = self._container_type() @@ -956,5 +958,6 @@ def descend(x): self.assertEqual(len(descend.seen), 1) self.assertIs(descend.seen[0], cdict) + if __name__ == "__main__": unittest.main() diff --git a/pyomo/core/tests/unit/kernel/test_expression.py b/pyomo/core/tests/unit/kernel/test_expression.py index c5c9e773248..4ab8c904fa9 100644 --- a/pyomo/core/tests/unit/kernel/test_expression.py +++ b/pyomo/core/tests/unit/kernel/test_expression.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest @@ -26,11 +36,8 @@ from pyomo.core.kernel.parameter import parameter from pyomo.core.kernel.objective import objective from pyomo.core.kernel.block import block -from pyomo.core.kernel.set_types import (RealSet, - IntegerSet) import six -from six import StringIO try: import numpy diff --git a/pyomo/core/tests/unit/kernel/test_kernel.py b/pyomo/core/tests/unit/kernel/test_kernel.py index a10a831bc60..817e595fd37 100644 --- a/pyomo/core/tests/unit/kernel/test_kernel.py +++ b/pyomo/core/tests/unit/kernel/test_kernel.py @@ -1,5 +1,12 @@ -import pickle -import itertools +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ import pyutilib.th as unittest import pyomo.kernel as pmo @@ -7,9 +14,6 @@ from pyomo.core.kernel.variable import IVariable from pyomo.core.kernel.constraint import IConstraint -import six -from six import StringIO - class IJunk(IBlock): __slots__ = () diff --git a/pyomo/core/tests/unit/kernel/test_list_container.py b/pyomo/core/tests/unit/kernel/test_list_container.py index 07970435c08..1613886a96c 100644 --- a/pyomo/core/tests/unit/kernel/test_list_container.py +++ b/pyomo/core/tests/unit/kernel/test_list_container.py @@ -19,8 +19,7 @@ from pyomo.core.kernel.homogeneous_container import \ IHomogeneousContainer from pyomo.core.kernel.list_container import ListContainer -from pyomo.core.kernel.block import (IBlock, - block, +from pyomo.core.kernel.block import (block, block_list) import six diff --git a/pyomo/core/tests/unit/kernel/test_matrix_constraint.py b/pyomo/core/tests/unit/kernel/test_matrix_constraint.py index d03d45b63dd..b675c2c8f58 100644 --- a/pyomo/core/tests/unit/kernel/test_matrix_constraint.py +++ b/pyomo/core/tests/unit/kernel/test_matrix_constraint.py @@ -1,13 +1,17 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest import pyomo.kernel as pmo -from pyomo.core.tests.unit.kernel.test_dict_container import \ - _TestActiveDictContainerBase -from pyomo.core.tests.unit.kernel.test_tuple_container import \ - _TestActiveTupleContainerBase -from pyomo.core.tests.unit.kernel.test_list_container import \ - _TestActiveListContainerBase from pyomo.core.kernel.base import \ (ICategorizedObject, ICategorizedObjectContainer) @@ -16,7 +20,6 @@ from pyomo.core.kernel.tuple_container import TupleContainer from pyomo.core.kernel.constraint import (IConstraint, constraint, - linear_constraint, constraint_dict, constraint_tuple, constraint_list) @@ -26,12 +29,9 @@ from pyomo.core.kernel.variable import (variable, variable_list) from pyomo.core.kernel.parameter import parameter -from pyomo.core.kernel.expression import (expression, - data_expression) +from pyomo.core.kernel.expression import expression from pyomo.core.kernel.block import (block, block_list) -from pyomo.core.kernel.set_types import (RealSet, - IntegerSet) try: import numpy @@ -47,6 +47,7 @@ has_scipy = False _scipy_ver = (0,0) + def _create_variable_list(size, **kwds): assert size > 0 vlist = variable_list() @@ -54,12 +55,12 @@ def _create_variable_list(size, **kwds): vlist.append(variable(**kwds)) return vlist + @unittest.skipUnless(has_numpy and has_scipy, "NumPy or SciPy is not available") class Test_matrix_constraint(unittest.TestCase): def test_pprint(self): - import pyomo.kernel # Not really testing what the output is, just that # an error does not occur. The pprint functionality # is still in the early stages. diff --git a/pyomo/core/tests/unit/kernel/test_objective.py b/pyomo/core/tests/unit/kernel/test_objective.py index 858b3b7f0f1..75b3db2177e 100644 --- a/pyomo/core/tests/unit/kernel/test_objective.py +++ b/pyomo/core/tests/unit/kernel/test_objective.py @@ -1,8 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest from pyomo.core.expr.numvalue import NumericValue -import pyomo.kernel +from pyomo.kernel import pprint from pyomo.core.tests.unit.kernel.test_dict_container import \ _TestActiveDictContainerBase from pyomo.core.tests.unit.kernel.test_tuple_container import \ @@ -19,28 +29,25 @@ maximize) from pyomo.core.kernel.variable import variable from pyomo.core.kernel.block import block -from pyomo.core.kernel.set_types import (RealSet, - IntegerSet) class Test_objective(unittest.TestCase): def test_pprint(self): - import pyomo.kernel # Not really testing what the output is, just that # an error does not occur. The pprint functionality # is still in the early stages. v = variable() o = objective(expr=v**2) - pyomo.kernel.pprint(o) + pprint(o) b = block() b.o = o - pyomo.kernel.pprint(o) - pyomo.kernel.pprint(b) + pprint(o) + pprint(b) m = block() m.b = b - pyomo.kernel.pprint(o) - pyomo.kernel.pprint(b) - pyomo.kernel.pprint(m) + pprint(o) + pprint(b) + pprint(m) def test_ctype(self): o = objective() diff --git a/pyomo/core/tests/unit/kernel/test_parameter.py b/pyomo/core/tests/unit/kernel/test_parameter.py index c8015479df8..90058439247 100644 --- a/pyomo/core/tests/unit/kernel/test_parameter.py +++ b/pyomo/core/tests/unit/kernel/test_parameter.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest @@ -7,7 +17,7 @@ is_fixed, is_constant, is_potentially_variable) -import pyomo.kernel +from pyomo.kernel import pprint from pyomo.core.tests.unit.kernel.test_dict_container import \ _TestActiveDictContainerBase from pyomo.core.tests.unit.kernel.test_tuple_container import \ @@ -23,27 +33,24 @@ parameter_list) from pyomo.core.kernel.variable import variable from pyomo.core.kernel.block import block -from pyomo.core.kernel.set_types import (RealSet, - IntegerSet) class Test_parameter(unittest.TestCase): def test_pprint(self): - import pyomo.kernel # Not really testing what the output is, just that # an error does not occur. The pprint functionality # is still in the early stages. p = parameter() - pyomo.kernel.pprint(p) + pprint(p) b = block() b.p = p - pyomo.kernel.pprint(p) - pyomo.kernel.pprint(b) + pprint(p) + pprint(b) m = block() m.b = b - pyomo.kernel.pprint(p) - pyomo.kernel.pprint(b) - pyomo.kernel.pprint(m) + pprint(p) + pprint(b) + pprint(m) def test_ctype(self): p = parameter() @@ -134,21 +141,20 @@ def test_is_parameter_type(self): class Test_functional_value(unittest.TestCase): def test_pprint(self): - import pyomo.kernel # Not really testing what the output is, just that # an error does not occur. The pprint functionality # is still in the early stages. f = functional_value() - pyomo.kernel.pprint(f) + pprint(f) b = block() b.f = f - pyomo.kernel.pprint(f) - pyomo.kernel.pprint(b) + pprint(f) + pprint(b) m = block() m.b = b - pyomo.kernel.pprint(f) - pyomo.kernel.pprint(b) - pyomo.kernel.pprint(m) + pprint(f) + pprint(b) + pprint(m) def test_ctype(self): f = functional_value() diff --git a/pyomo/core/tests/unit/kernel/test_piecewise.py b/pyomo/core/tests/unit/kernel/test_piecewise.py index 2b197200b67..da2aa26cbc0 100644 --- a/pyomo/core/tests/unit/kernel/test_piecewise.py +++ b/pyomo/core/tests/unit/kernel/test_piecewise.py @@ -1,5 +1,14 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle -import abc import pyutilib.th as unittest import pyomo.kernel as pmo @@ -29,7 +38,6 @@ import pyomo.core.kernel.piecewise_library.transforms_nd as \ transforms_nd import pyomo.core.kernel.piecewise_library.util as util -from pyomo.core.base.block import Block # for the multi-dimensional piecewise tests _test_v = None diff --git a/pyomo/core/tests/unit/kernel/test_sos.py b/pyomo/core/tests/unit/kernel/test_sos.py index 33884a1b9c7..8bb5e1f1063 100644 --- a/pyomo/core/tests/unit/kernel/test_sos.py +++ b/pyomo/core/tests/unit/kernel/test_sos.py @@ -1,7 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest -import pyomo.kernel from pyomo.core.tests.unit.kernel.test_dict_container import \ _TestActiveDictContainerBase from pyomo.core.tests.unit.kernel.test_tuple_container import \ diff --git a/pyomo/core/tests/unit/kernel/test_suffix.py b/pyomo/core/tests/unit/kernel/test_suffix.py index 2a17759e3f2..b715d275d3d 100644 --- a/pyomo/core/tests/unit/kernel/test_suffix.py +++ b/pyomo/core/tests/unit/kernel/test_suffix.py @@ -1,8 +1,17 @@ -import sys +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest -import pyomo.kernel +from pyomo.kernel import pprint from pyomo.core.tests.unit.kernel.test_dict_container import \ _TestActiveDictContainerBase from pyomo.core.kernel.base import ICategorizedObject @@ -13,17 +22,13 @@ import_suffix_generator, local_suffix_generator, suffix_generator) -from pyomo.core.kernel.variable import (variable, - variable_dict) +from pyomo.core.kernel.variable import variable from pyomo.core.kernel.constraint import (constraint, constraint_list) from pyomo.core.kernel.block import (block, block_dict) -from pyomo.core.kernel.set_types import (RealSet, - IntegerSet) import six -from six import StringIO if six.PY3: from collections.abc import Mapping as collections_Mapping @@ -36,7 +41,6 @@ class Test_suffix(unittest.TestCase): def test_pprint(self): - import pyomo.kernel # Not really testing what the output is, just that # an error does not occur. The pprint functionality # is still in the early stages. @@ -45,18 +49,18 @@ def test_pprint(self): s = suffix() s[v] = 1 s[clist] = None - pyomo.kernel.pprint(s) + pprint(s) b = block() b.s = s - pyomo.kernel.pprint(s) - pyomo.kernel.pprint(b) + pprint(s) + pprint(b) m = block() m.b = b - pyomo.kernel.pprint(s) - pyomo.kernel.pprint(b) - pyomo.kernel.pprint(m) + pprint(s) + pprint(b) + pprint(m) - pyomo.kernel.pprint({'a': 1, 'b': 2}) + pprint({'a': 1, 'b': 2}) def test_str(self): s = suffix() diff --git a/pyomo/core/tests/unit/kernel/test_tuple_container.py b/pyomo/core/tests/unit/kernel/test_tuple_container.py index e94bc3cebbe..156e83a22cc 100644 --- a/pyomo/core/tests/unit/kernel/test_tuple_container.py +++ b/pyomo/core/tests/unit/kernel/test_tuple_container.py @@ -18,8 +18,7 @@ from pyomo.core.kernel.homogeneous_container import \ IHomogeneousContainer from pyomo.core.kernel.tuple_container import TupleContainer -from pyomo.core.kernel.block import (IBlock, - block, +from pyomo.core.kernel.block import (block, block_list) import six diff --git a/pyomo/core/tests/unit/kernel/test_variable.py b/pyomo/core/tests/unit/kernel/test_variable.py index 46a124da364..5f484070d72 100644 --- a/pyomo/core/tests/unit/kernel/test_variable.py +++ b/pyomo/core/tests/unit/kernel/test_variable.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pickle import pyutilib.th as unittest @@ -5,7 +15,6 @@ is_fixed, is_constant, is_potentially_variable) -import pyomo.kernel from pyomo.core.tests.unit.kernel.test_dict_container import \ _TestActiveDictContainerBase from pyomo.core.tests.unit.kernel.test_tuple_container import \ @@ -29,15 +38,11 @@ NonNegativeReals, NegativeReals, Reals, - Integers, NonNegativeIntegers, NegativeIntegers, RealInterval, IntegerInterval) -import six -from six import StringIO - class Test_variable(unittest.TestCase): def test_pprint(self): diff --git a/pyomo/core/tests/unit/test_action.py b/pyomo/core/tests/unit/test_action.py index ef2cc3df09d..9723de5e687 100644 --- a/pyomo/core/tests/unit/test_action.py +++ b/pyomo/core/tests/unit/test_action.py @@ -20,7 +20,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, Param, Set, BuildAction, value def action1_fn(model): diff --git a/pyomo/core/tests/unit/test_block.py b/pyomo/core/tests/unit/test_block.py index a5223e59d82..76c4a49dd11 100644 --- a/pyomo/core/tests/unit/test_block.py +++ b/pyomo/core/tests/unit/test_block.py @@ -16,7 +16,7 @@ import six import types -from six import StringIO +from six import StringIO, iterkeys from copy import deepcopy from os.path import abspath, dirname, join @@ -24,13 +24,13 @@ currdir = dirname( abspath(__file__) ) import pyutilib.th as unittest -import pyutilib.services +from pyutilib.services import TempfileManager -from pyomo.environ import * +from pyomo.environ import AbstractModel, ConcreteModel, Var, Set, Param, Block, Suffix, Constraint, Component, Objective, Expression, SOSConstraint, SortComponents, NonNegativeIntegers, TraversalStrategy, RangeSet, SolverFactory, value, sum_product from pyomo.common.log import LoggingIntercept from pyomo.core.base.block import SimpleBlock, SubclassOf, _BlockData, declare_custom_block from pyomo.core.expr import current as EXPR -from pyomo.opt import * +from pyomo.opt import check_available_solvers from pyomo.gdp import Disjunct @@ -535,7 +535,7 @@ def tearDown(self): self.block = None if os.path.exists("unknown.lp"): os.unlink("unknown.lp") - pyutilib.services.TempfileManager.clear_tempfiles() + TempfileManager.clear_tempfiles() def test_collect_ctypes(self): b = Block(concrete=True) diff --git a/pyomo/core/tests/unit/test_block_model.py b/pyomo/core/tests/unit/test_block_model.py index efe8fbf23e3..5288e2a9fb7 100644 --- a/pyomo/core/tests/unit/test_block_model.py +++ b/pyomo/core/tests/unit/test_block_model.py @@ -20,7 +20,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, Param, Block, Set, Var, RangeSet, Constraint, Connector, value class Test(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_bounds.py b/pyomo/core/tests/unit/test_bounds.py index ec0b97dff19..991e295a028 100644 --- a/pyomo/core/tests/unit/test_bounds.py +++ b/pyomo/core/tests/unit/test_bounds.py @@ -17,7 +17,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, Param, Var, Constraint class Test(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_check.py b/pyomo/core/tests/unit/test_check.py index a67d77930c9..4b40103ab45 100644 --- a/pyomo/core/tests/unit/test_check.py +++ b/pyomo/core/tests/unit/test_check.py @@ -20,7 +20,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, BuildCheck, Param, Set, value class PyomoModel(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_component.py b/pyomo/core/tests/unit/test_component.py index f5639caf091..28c061cf029 100644 --- a/pyomo/core/tests/unit/test_component.py +++ b/pyomo/core/tests/unit/test_component.py @@ -16,7 +16,7 @@ from pyomo.common import DeveloperError import pyomo.core.base._pyomo from pyomo.core.base.block import generate_cuid_names -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Component, Block, Var, Set, Param, ComponentUID class TestComponent(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_con.py b/pyomo/core/tests/unit/test_con.py index 139b8d264e4..f7e919a13de 100644 --- a/pyomo/core/tests/unit/test_con.py +++ b/pyomo/core/tests/unit/test_con.py @@ -14,7 +14,6 @@ # TestArrayCon Class for testing array of constraint # -import logging import sys import os from os.path import abspath, dirname @@ -28,8 +27,6 @@ from pyomo.core.expr import logical_expr from pyomo.core.base.constraint import _GeneralConstraintData -from six import StringIO - class TestConstraintCreation(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_concrete.py b/pyomo/core/tests/unit/test_concrete.py index 9b69ce8a44e..30a9baa570f 100644 --- a/pyomo/core/tests/unit/test_concrete.py +++ b/pyomo/core/tests/unit/test_concrete.py @@ -17,10 +17,10 @@ import pyutilib.th as unittest -import pyomo.opt -from pyomo.environ import * +from pyomo.opt import check_available_solvers +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, SolverFactory -solvers = pyomo.opt.check_available_solvers('glpk') +solvers = check_available_solvers('glpk') @unittest.skipIf(not 'glpk' in solvers, "glpk solver is not available") class Test(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_connector.py b/pyomo/core/tests/unit/test_connector.py index 1283dddac2d..9e5be8ed21e 100644 --- a/pyomo/core/tests/unit/test_connector.py +++ b/pyomo/core/tests/unit/test_connector.py @@ -21,7 +21,7 @@ import pyutilib.th as unittest from six import StringIO -from pyomo.environ import * +from pyomo.environ import ConcreteModel, AbstractModel, Connector, Var, NonNegativeReals, Set, Constraint, TransformationFactory, Binary, Reals, VarList class TestConnector(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_deprecation.py b/pyomo/core/tests/unit/test_deprecation.py index b461abdf784..3da118032a7 100644 --- a/pyomo/core/tests/unit/test_deprecation.py +++ b/pyomo/core/tests/unit/test_deprecation.py @@ -8,12 +8,11 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import os import pyutilib.th as unittest import sys from importlib import import_module -from six import StringIO, PY3 +from six import StringIO from pyomo.common.log import LoggingIntercept diff --git a/pyomo/core/tests/unit/test_derivs.py b/pyomo/core/tests/unit/test_derivs.py index 812e50555eb..d3f3721c677 100644 --- a/pyomo/core/tests/unit/test_derivs.py +++ b/pyomo/core/tests/unit/test_derivs.py @@ -1,7 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.core.expr.calculus.diff_with_pyomo import reverse_ad, reverse_sd from pyomo.common.getGSL import find_GSL +from pyomo.core.expr.numeric_expr import LinearExpression tol = 6 @@ -10,176 +21,176 @@ def approx_deriv(expr, wrt, delta=0.001): numerator = 0 wrt.value += 2*delta - numerator -= pe.value(expr) + numerator -= pyo.value(expr) wrt.value -= delta - numerator += 8*pe.value(expr) + numerator += 8*pyo.value(expr) wrt.value -= 2*delta - numerator -= 8*pe.value(expr) + numerator -= 8*pyo.value(expr) wrt.value -= delta - numerator += pe.value(expr) + numerator += pyo.value(expr) wrt.value += 2*delta return numerator / (12*delta) class TestDerivs(unittest.TestCase): def test_prod(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - m.y = pe.Var(initialize=3.0) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + m.y = pyo.Var(initialize=3.0) e = m.x * m.y derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) - self.assertAlmostEqual(derivs[m.y], pe.value(symbolic[m.y]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) def test_sum(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - m.y = pe.Var(initialize=3.0) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + m.y = pyo.Var(initialize=3.0) e = 2.0*m.x + 3.0*m.y - m.x*m.y derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) - self.assertAlmostEqual(derivs[m.y], pe.value(symbolic[m.y]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) def test_div(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - m.y = pe.Var(initialize=3.0) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + m.y = pyo.Var(initialize=3.0) e = m.x / m.y derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) - self.assertAlmostEqual(derivs[m.y], pe.value(symbolic[m.y]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) def test_pow(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - m.y = pe.Var(initialize=3.0) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + m.y = pyo.Var(initialize=3.0) e = m.x ** m.y derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) - self.assertAlmostEqual(derivs[m.y], pe.value(symbolic[m.y]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) def test_sqrt(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - m.y = pe.Var(initialize=3.0) - e = pe.sqrt(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + m.y = pyo.Var(initialize=3.0) + e = pyo.sqrt(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_exp(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - e = pe.exp(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + e = pyo.exp(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_log(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - e = pe.log(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + e = pyo.log(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_log10(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - e = pe.log10(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + e = pyo.log10(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_sin(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - e = pe.sin(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + e = pyo.sin(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_cos(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - e = pe.cos(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + e = pyo.cos(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_tan(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - e = pe.tan(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + e = pyo.tan(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_asin(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=0.5) - e = pe.asin(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=0.5) + e = pyo.asin(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_acos(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=0.5) - e = pe.acos(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=0.5) + e = pyo.acos(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_atan(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2.0) - e = pe.atan(m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + e = pyo.atan(m.x) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) def test_nested(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=2) - m.y = pe.Var(initialize=3) - m.p = pe.Param(initialize=0.5, mutable=True) - e = pe.exp(m.x**m.p + 3.2*m.y - 12) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2) + m.y = pyo.Var(initialize=3) + m.p = pyo.Param(initialize=0.5, mutable=True) + e = pyo.exp(m.x**m.p + 3.2*m.y - 12) derivs = reverse_ad(e) symbolic = reverse_sd(e) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol+3) - self.assertAlmostEqual(derivs[m.y], pe.value(symbolic[m.y]), tol+3) - self.assertAlmostEqual(derivs[m.p], pe.value(symbolic[m.p]), tol+3) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol+3) + self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol+3) + self.assertAlmostEqual(derivs[m.p], pyo.value(symbolic[m.p]), tol+3) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) self.assertAlmostEqual(derivs[m.p], approx_deriv(e, m.p), tol) def test_expressiondata(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=3) - m.e = pe.Expression(expr=m.x * 2) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=3) + m.e = pyo.Expression(expr=m.x * 2) @m.Expression([1, 2]) def e2(m, i): @@ -187,36 +198,58 @@ def e2(m, i): return m.x + 4 else: return m.x ** 2 - m.o = pe.Objective(expr=m.e + 1 + m.e2[1] + m.e2[2]) + m.o = pyo.Objective(expr=m.e + 1 + m.e2[1] + m.e2[2]) derivs = reverse_ad(m.o.expr) symbolic = reverse_sd(m.o.expr) - self.assertAlmostEqual(derivs[m.x], pe.value(symbolic[m.x]), tol) + self.assertAlmostEqual(derivs[m.x], pyo.value(symbolic[m.x]), tol) def test_multiple_named_expressions(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() m.x.value = 1 m.y.value = 1 - m.E = pe.Expression(expr=m.x*m.y) + m.E = pyo.Expression(expr=m.x*m.y) e = m.E - m.E derivs = reverse_ad(e) self.assertAlmostEqual(derivs[m.x], 0) self.assertAlmostEqual(derivs[m.y], 0) symbolic = reverse_sd(e) - self.assertAlmostEqual(pe.value(symbolic[m.x]), 0) - self.assertAlmostEqual(pe.value(symbolic[m.y]), 0) + self.assertAlmostEqual(pyo.value(symbolic[m.x]), 0) + self.assertAlmostEqual(pyo.value(symbolic[m.y]), 0) def test_external(self): DLL = find_GSL() if not DLL: self.skipTest('Could not find the amplgsl.dll library') - m = pe.ConcreteModel() - m.hypot = pe.ExternalFunction(library=DLL, function='gsl_hypot') - m.x = pe.Var(initialize=0.5) - m.y = pe.Var(initialize=1.5) + m = pyo.ConcreteModel() + m.hypot = pyo.ExternalFunction(library=DLL, function='gsl_hypot') + m.x = pyo.Var(initialize=0.5) + m.y = pyo.Var(initialize=1.5) e = 2 * m.hypot(m.x, m.x*m.y) derivs = reverse_ad(e) self.assertAlmostEqual(derivs[m.x], approx_deriv(e, m.x), tol) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) + + def test_linear_expression(self): + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=2.0) + m.y = pyo.Var(initialize=3.0) + m.p = pyo.Param(initialize=2.5, mutable=True) + e = LinearExpression(constant=m.p, linear_vars=[m.x, m.y], linear_coefs=[1.8, m.p]) + e = pyo.log(e) + derivs = reverse_ad(e) + symbolic = reverse_sd(e) + for v in [m.x, m.y, m.p]: + self.assertAlmostEqual(derivs[v], pyo.value(symbolic[v]), tol) + self.assertAlmostEqual(derivs[v], approx_deriv(e, v), tol) + + def test_NPV(self): + m = pyo.ConcreteModel() + m.p = pyo.Param(initialize=2.0, mutable=True) + e = pyo.log(m.p) + derivs = reverse_ad(e) + symbolic = reverse_sd(e) + self.assertAlmostEqual(derivs[m.p], pyo.value(symbolic[m.p]), tol) + self.assertAlmostEqual(derivs[m.p], approx_deriv(e, m.p), tol) diff --git a/pyomo/core/tests/unit/test_expr_misc.py b/pyomo/core/tests/unit/test_expr_misc.py index 4a7a2c9544f..3866169b037 100644 --- a/pyomo/core/tests/unit/test_expr_misc.py +++ b/pyomo/core/tests/unit/test_expr_misc.py @@ -15,10 +15,9 @@ from os.path import abspath, dirname currdir = dirname(abspath(__file__))+os.sep -import pyomo.core.expr.current as EXPR import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, ConcreteModel, ConstraintList, Set, Param, Var, Constraint, Objective, sum_product, quicksum, sequence, prod def obj_rule(model): return sum(model.x[a] + model.y[a] for a in model.A) diff --git a/pyomo/core/tests/unit/test_expression.py b/pyomo/core/tests/unit/test_expression.py index 920a6b532f8..218a0dfd495 100644 --- a/pyomo/core/tests/unit/test_expression.py +++ b/pyomo/core/tests/unit/test_expression.py @@ -17,11 +17,9 @@ import pyutilib.th as unittest from pyutilib.misc.redirect_io import capture_output -from pyomo.environ import * +from pyomo.environ import ConcreteModel, AbstractModel, Expression, Var, Set, Param, Objective, value, sum_product from pyomo.core.base.expression import _GeneralExpressionData -import six - class TestExpressionData(unittest.TestCase): def test_exprdata_get_set(self): diff --git a/pyomo/core/tests/unit/test_external.py b/pyomo/core/tests/unit/test_external.py index 2adf354c343..be71a1cd0fb 100644 --- a/pyomo/core/tests/unit/test_external.py +++ b/pyomo/core/tests/unit/test_external.py @@ -8,12 +8,13 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ # -import os + import pyutilib.th as unittest from pyomo.common.getGSL import find_GSL -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, SolverFactory, value from pyomo.core.base.external import (PythonCallbackFunction, + ExternalFunction, AMPLExternalFunction) from pyomo.opt import check_available_solvers diff --git a/pyomo/core/tests/unit/test_indexed.py b/pyomo/core/tests/unit/test_indexed.py index 846c18b07ed..680f761d553 100644 --- a/pyomo/core/tests/unit/test_indexed.py +++ b/pyomo/core/tests/unit/test_indexed.py @@ -17,7 +17,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Set from pyomo.core.base.indexed_component import normalize_index class TestSimpleVar(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_indexed_slice.py b/pyomo/core/tests/unit/test_indexed_slice.py index c0d7b627f53..63604a00ba4 100644 --- a/pyomo/core/tests/unit/test_indexed_slice.py +++ b/pyomo/core/tests/unit/test_indexed_slice.py @@ -11,12 +11,11 @@ # Unit Tests for indexed components # -import os import pickle import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import Var, Block, ConcreteModel, RangeSet, Set from pyomo.core.base.block import _BlockData from pyomo.core.base.indexed_component_slice import IndexedComponent_slice @@ -680,6 +679,19 @@ def test_flatten_false(self): finally: normalize_index.flatten = _old_flatten + def test_compare_1dim_slice(self): + m = ConcreteModel() + m.I = Set(initialize=range(2)) + m.J = Set(initialize=range(2,4)) + m.K = Set(initialize=['a','b']) + + @m.Block(m.I, m.J) + def b(b, i, j): + b.v = Var(m.K) + + self.assertEqual(m.b[0,:].v[:], m.b[0,:].v[:]) + self.assertNotEqual(m.b[0,:].v[:], m.b[0,:].v['a']) + if __name__ == "__main__": unittest.main() diff --git a/pyomo/core/tests/unit/test_labelers.py b/pyomo/core/tests/unit/test_labelers.py index 12e509c53cf..1f3c0c80849 100644 --- a/pyomo/core/tests/unit/test_labelers.py +++ b/pyomo/core/tests/unit/test_labelers.py @@ -10,7 +10,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, RangeSet, Block, Constraint, CounterLabeler, NumericLabeler, TextLabeler, ComponentUID, ShortNameLabeler, CNameLabeler, CuidLabeler, AlphaNumericTextLabeler, NameLabeler class LabelerTests(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_logical_expr.py b/pyomo/core/tests/unit/test_logical_expr.py index 356de9414b7..fd3fe61913a 100644 --- a/pyomo/core/tests/unit/test_logical_expr.py +++ b/pyomo/core/tests/unit/test_logical_expr.py @@ -20,7 +20,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, ConcreteModel, Set, Var, Param, Constraint, inequality, display import pyomo.core.expr.logical_expr as logical_expr from pyomo.core.expr.logical_expr import ( InequalityExpression, EqualityExpression, RangedExpression, diff --git a/pyomo/core/tests/unit/test_matrix_constraint.py b/pyomo/core/tests/unit/test_matrix_constraint.py index 8b21c661c88..54f38843979 100644 --- a/pyomo/core/tests/unit/test_matrix_constraint.py +++ b/pyomo/core/tests/unit/test_matrix_constraint.py @@ -1,15 +1,22 @@ -import pickle +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ import pyutilib.th as unittest -import pyomo.environ as aml +import pyomo.environ as pyo from pyomo.core.base.matrix_constraint import MatrixConstraint -from pyomo.core.kernel.set_types import (RealSet, - IntegerSet) + def _create_variable_list(size, **kwds): assert size > 0 - return aml.Var(aml.RangeSet(0,size-1), **kwds) + return pyo.Var(pyo.RangeSet(0,size-1), **kwds) def _get_csr(m, n, value): data = [value] * (m * n) @@ -22,7 +29,7 @@ def _get_csr(m, n, value): class TestMatrixConstraint(unittest.TestCase): def test_init(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() m.v = _create_variable_list(3, initialize=1.0) data, indices, indptr = _get_csr(3,3,0.0) lb = [None] * 3 @@ -48,7 +55,7 @@ def test_init(self): self.assertEqual(c.has_lb(), False) self.assertEqual(c.has_ub(), False) - m = aml.ConcreteModel() + m = pyo.ConcreteModel() m.v = _create_variable_list(3, initialize=3) data, indices, indptr = _get_csr(2,3,1.0) m.c = MatrixConstraint(data, indices, indptr, @@ -69,7 +76,7 @@ def test_init(self): self.assertEqual(c.equality, False) - m = aml.ConcreteModel() + m = pyo.ConcreteModel() m.v = _create_variable_list(3, initialize=3) data, indices, indptr = _get_csr(2,3,1.0) m.c = MatrixConstraint(data, indices, indptr, diff --git a/pyomo/core/tests/unit/test_misc.py b/pyomo/core/tests/unit/test_misc.py index c27b0ec355c..9737b369cbc 100644 --- a/pyomo/core/tests/unit/test_misc.py +++ b/pyomo/core/tests/unit/test_misc.py @@ -19,7 +19,7 @@ from pyomo.opt import check_available_solvers import pyomo.scripting.pyomo_command as main -from pyomo.core import * +from pyomo.core import AbstractModel, ConcreteModel, Block, Set, Param, Var, Objective, Constraint, Reals, display from six import StringIO diff --git a/pyomo/core/tests/unit/test_model.py b/pyomo/core/tests/unit/test_model.py index 52cd82965c0..b0aaa268c96 100644 --- a/pyomo/core/tests/unit/test_model.py +++ b/pyomo/core/tests/unit/test_model.py @@ -14,7 +14,6 @@ # import os -import sys from os.path import abspath, dirname, join currdir = dirname(abspath(__file__)) import pickle @@ -24,8 +23,8 @@ from pyomo.common.dependencies import yaml_available from pyomo.core.expr import current as EXPR -from pyomo.environ import * -from pyomo.opt import SolutionStatus, check_available_solvers +from pyomo.environ import RangeSet, ConcreteModel, Var, Param, Block, AbstractModel, Set, Constraint, Objective, value, sum_product, SolverFactory, VarList, ObjectiveList, ConstraintList +from pyomo.opt import check_available_solvers from pyomo.opt.parallel.local import SolverManager_Serial solvers = check_available_solvers('glpk') diff --git a/pyomo/core/tests/unit/test_mutable.py b/pyomo/core/tests/unit/test_mutable.py index 0520098dca6..8403b9a0e17 100644 --- a/pyomo/core/tests/unit/test_mutable.py +++ b/pyomo/core/tests/unit/test_mutable.py @@ -20,7 +20,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, Param, Var, Constraint, value class TestMutable(unittest.TestCase): def test_mutable_constraint_upper(self): diff --git a/pyomo/core/tests/unit/test_numeric_expr.py b/pyomo/core/tests/unit/test_numeric_expr.py index 7026515bf15..af4cab4dfb0 100644 --- a/pyomo/core/tests/unit/test_numeric_expr.py +++ b/pyomo/core/tests/unit/test_numeric_expr.py @@ -15,50 +15,37 @@ import pickle import math import os -import re from collections import defaultdict -import six -import sys from os.path import abspath, dirname currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest from pyutilib.th import nottest -from pyomo.environ import * -import pyomo.kernel -from pyomo.core.expr.numvalue import ( - native_types, nonpyomo_leaf_types, NumericConstant, as_numeric, - is_potentially_variable, -) +from pyomo.environ import ConcreteModel, AbstractModel, RangeSet, Var, Param, Set, Constraint, ConstraintList, Expression, Objective, Reals, ExternalFunction, PositiveReals, log10, exp, floor, ceil, log, cos, sin, tan, acos, asin, atan, sinh, cosh, tanh, acosh, asinh, atanh, sqrt, value, quicksum, sum_product, is_fixed, is_constant +from pyomo.kernel import variable, expression, objective +from pyomo.core.expr.numvalue import (NumericConstant, as_numeric, + native_numeric_types, + is_potentially_variable, polynomial_degree) from pyomo.core.expr.numeric_expr import ( ExpressionBase, UnaryFunctionExpression, SumExpression, PowExpression, - ProductExpression, ReciprocalExpression, NegationExpression, + ProductExpression, NegationExpression, linear_expression, MonomialTermExpression, LinearExpression, DivisionExpression, - NPV_NegationExpression, NPV_ProductExpression, NPV_ReciprocalExpression, + NPV_NegationExpression, NPV_ProductExpression, NPV_PowExpression, NPV_DivisionExpression, - decompose_term, clone_counter, + decompose_term, clone_counter, nonlinear_expression, _MutableLinearExpression, _MutableSumExpression, _decompose_linear_terms, LinearDecompositionError, ) import pyomo.core.expr.logical_expr as logical_expr -from pyomo.core.expr.logical_expr import ( - InequalityExpression, EqualityExpression, RangedExpression, -) -from pyomo.core.expr.visitor import ( - FixedExpressionError, NonConstantExpressionError, - StreamBasedExpressionVisitor, ExpressionReplacementVisitor, - evaluate_expression, expression_to_string, replace_expressions, - clone_expression, sizeof_expression, - identify_variables, identify_components, identify_mutable_parameters, -) +from pyomo.core.expr.visitor import (expression_to_string, + clone_expression) from pyomo.core.expr.current import Expr_if -from pyomo.core.base.var import SimpleVar -from pyomo.core.base.param import _ParamData, SimpleParam -from pyomo.core.base.label import * +from pyomo.core.base.label import NumericLabeler from pyomo.core.expr.template_expr import IndexTemplate -from pyomo.core.expr.expr_errors import TemplateExpressionError +from pyomo.core.expr import expr_common +from pyomo.core.base.var import _GeneralVarData from pyomo.repn import generate_standard_repn @@ -229,7 +216,6 @@ def test_arithmetic(self): class TestExpression_EvaluateVarData(TestExpression_EvaluateNumericConstant): def setUp(self): - import pyomo.core.base.var # # Create Model # @@ -241,7 +227,7 @@ def setUp(self): self.expectConstExpression = False def create(self, val, domain): - tmp=pyomo.core.base.var._GeneralVarData() + tmp=_GeneralVarData() tmp.domain = domain tmp.value=val return tmp @@ -250,7 +236,6 @@ def create(self, val, domain): class TestExpression_EvaluateVar(TestExpression_EvaluateNumericConstant): def setUp(self): - import pyomo.core.base.var # # Create Model # @@ -271,7 +256,6 @@ def create(self, val, domain): class TestExpression_EvaluateFixedVar(TestExpression_EvaluateNumericConstant): def setUp(self): - import pyomo.core.base.var # # Create Model # @@ -293,7 +277,6 @@ def create(self, val, domain): class TestExpression_EvaluateImmutableParam(TestExpression_EvaluateNumericConstant): def setUp(self): - import pyomo.core.base.var # # Create Model # @@ -313,7 +296,6 @@ def create(self, val, domain): class TestExpression_Evaluate_MutableParam(TestExpression_EvaluateNumericConstant): def setUp(self): - import pyomo.core.base.var # # Create Model # @@ -5099,14 +5081,14 @@ def test_ExpressionIndex(self): self.check_api(M.e[0]) def test_expression(self): - x = pyomo.kernel.variable() - e = pyomo.kernel.expression() + x = variable() + e = expression() e.expr = x self.check_api(e) def test_objective(self): - x = pyomo.kernel.variable() - e = pyomo.kernel.objective() + x = variable() + e = objective() e.expr = x self.check_api(e) @@ -5151,7 +5133,7 @@ def test_VarIndex(self): self.check_api(M.x[0]) def test_variable(self): - x = pyomo.kernel.variable() + x = variable() self.check_api(x) class TestDirect_LinearExpression(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_numvalue.py b/pyomo/core/tests/unit/test_numvalue.py index 7965d569e8b..2d50cd71b8c 100644 --- a/pyomo/core/tests/unit/test_numvalue.py +++ b/pyomo/core/tests/unit/test_numvalue.py @@ -15,10 +15,12 @@ from os.path import abspath, dirname currdir = dirname(abspath(__file__))+os.sep -import pyutilib.math +from pyutilib.math import nan, infinity import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import (value, ConcreteModel, Param, Var, + polynomial_degree, is_constant, is_fixed, + is_potentially_variable, is_variable_type) from pyomo.core.expr.numvalue import (NumericConstant, as_numeric, is_numeric_data) @@ -163,11 +165,11 @@ def test_long(self): self.assertEqual(val, value(val)) def test_nan(self): - val = pyutilib.math.nan + val = nan self.assertEqual(id(val), id(value(val))) def test_inf(self): - val = pyutilib.math.infinity + val = infinity self.assertEqual(id(val), id(value(val))) def test_string(self): @@ -179,12 +181,12 @@ def test_const1(self): self.assertEqual(1.0, value(val)) def test_const3(self): - val = NumericConstant(pyutilib.math.nan) - self.assertEqual(id(pyutilib.math.nan), id(value(val))) + val = NumericConstant(nan) + self.assertEqual(id(nan), id(value(val))) def test_const4(self): - val = NumericConstant(pyutilib.math.infinity) - self.assertEqual(id(pyutilib.math.infinity), id(value(val))) + val = NumericConstant(infinity) + self.assertEqual(id(infinity), id(value(val))) def test_param1(self): m = ConcreteModel() @@ -255,11 +257,11 @@ def test_long(self): self.assertEqual(0, polynomial_degree(val)) def test_nan(self): - val = pyutilib.math.nan + val = nan self.assertEqual(0, polynomial_degree(val)) def test_inf(self): - val = pyutilib.math.infinity + val = infinity self.assertEqual(0, polynomial_degree(val)) def test_string(self): @@ -271,11 +273,11 @@ def test_const1(self): self.assertEqual(0, polynomial_degree(val)) def test_const3(self): - val = NumericConstant(pyutilib.math.nan) + val = NumericConstant(nan) self.assertEqual(0, polynomial_degree(val)) def test_const4(self): - val = NumericConstant(pyutilib.math.infinity) + val = NumericConstant(infinity) self.assertEqual(0, polynomial_degree(val)) def test_param1(self): diff --git a/pyomo/core/tests/unit/test_obj.py b/pyomo/core/tests/unit/test_obj.py index 9216a1f2ae0..3a7c03447d9 100644 --- a/pyomo/core/tests/unit/test_obj.py +++ b/pyomo/core/tests/unit/test_obj.py @@ -20,7 +20,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, AbstractModel, Objective, ObjectiveList, Var, Param, Set, RangeSet, value, maximize, minimize, simple_objective_rule, simple_objectivelist_rule class TestSimpleObj(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_param.py b/pyomo/core/tests/unit/test_param.py index 7c079694200..d949ea5d354 100644 --- a/pyomo/core/tests/unit/test_param.py +++ b/pyomo/core/tests/unit/test_param.py @@ -22,12 +22,12 @@ import os import sys -import pyutilib.services +from pyutilib.services import TempfileManager import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import Set, RangeSet, Param, ConcreteModel, AbstractModel, Constraint, Var, NonNegativeIntegers, Integers, NonNegativeReals, Boolean, Reals, Any, display, value, set_options, sin, cos, tan, log, log10, exp, sqrt, ceil, floor, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh from pyomo.common.log import LoggingIntercept -from pyomo.core.base.param import _NotValid +from pyomo.core.base.param import _NotValid, _ParamData from six import iteritems, itervalues, StringIO @@ -121,7 +121,7 @@ def test_getitem(self): def test_setitem_index_error(self): try: self.instance.A[2] = 4.3 - if not self.instance.A._mutable: + if not self.instance.A.mutable: self.fail("Expected setitem[%s] to fail for immutable Params" % (idx,)) self.fail("Expected KeyError because 2 is not a valid key") @@ -129,7 +129,7 @@ def test_setitem_index_error(self): pass except TypeError: # immutable Params should raise a TypeError exception - if self.instance.A._mutable: + if self.instance.A.mutable: raise def test_setitem_preexisting(self): @@ -139,28 +139,28 @@ def test_setitem_preexisting(self): idx = sorted(keys)[0] self.assertEqual(self.instance.A[idx], self.data[idx]) - if self.instance.A._mutable: + if self.instance.A.mutable: self.assertTrue( isinstance( self.instance.A[idx], - pyomo.core.base.param._ParamData ) ) + _ParamData ) ) else: self.assertEqual(type(self.instance.A[idx]), float) try: self.instance.A[idx] = 4.3 - if not self.instance.A._mutable: + if not self.instance.A.mutable: self.fail("Expected setitem[%s] to fail for immutable Params" % (idx,)) self.assertEqual( self.instance.A[idx], 4.3) self.assertTrue( isinstance(self.instance.A[idx], - pyomo.core.base.param._ParamData ) ) + _ParamData ) ) except TypeError: # immutable Params should raise a TypeError exception - if self.instance.A._mutable: + if self.instance.A.mutable: raise try: self.instance.A[idx] = -4.3 - if not self.instance.A._mutable: + if not self.instance.A.mutable: self.fail("Expected setitem[%s] to fail for immutable Params" % (idx,)) if self.expectNegativeDomainError: @@ -174,12 +174,12 @@ def test_setitem_preexisting(self): % ( str(sys.exc_info()[1]), idx ) ) except TypeError: # immutable Params should raise a TypeError exception - if self.instance.A._mutable: + if self.instance.A.mutable: raise try: self.instance.A[idx] = 'x' - if not self.instance.A._mutable: + if not self.instance.A.mutable: self.fail("Expected setitem[%s] to fail for immutable Params" % (idx,)) if self.expectTextDomainError: @@ -193,7 +193,7 @@ def test_setitem_preexisting(self): % ( str(sys.exc_info()[1]), idx ) ) except TypeError: # immutable Params should raise a TypeError exception - if self.instance.A._mutable: + if self.instance.A.mutable: raise def test_setitem_default_override(self): @@ -213,29 +213,29 @@ def test_setitem_default_override(self): self.assertEqual( value(self.instance.A[idx]), self.instance.A._default_val ) - if self.instance.A._mutable: + if self.instance.A.mutable: self.assertIsInstance( self.instance.A[idx], - pyomo.core.base.param._ParamData ) + _ParamData ) else: self.assertEqual(type(self.instance.A[idx]), type(value(self.instance.A._default_val))) try: self.instance.A[idx] = 4.3 - if not self.instance.A._mutable: + if not self.instance.A.mutable: self.fail("Expected setitem[%s] to fail for immutable Params" % (idx,)) self.assertEqual( self.instance.A[idx], 4.3) self.assertIsInstance( self.instance.A[idx], - pyomo.core.base.param._ParamData ) + _ParamData ) except TypeError: # immutable Params should raise a TypeError exception - if self.instance.A._mutable: + if self.instance.A.mutable: raise try: self.instance.A[idx] = -4.3 - if not self.instance.A._mutable: + if not self.instance.A.mutable: self.fail("Expected setitem[%s] to fail for immutable Params" % (idx,)) if self.expectNegativeDomainError: @@ -249,12 +249,12 @@ def test_setitem_default_override(self): % ( str(sys.exc_info()[1]), idx ) ) except TypeError: # immutable Params should raise a TypeError exception - if self.instance.A._mutable: + if self.instance.A.mutable: raise try: self.instance.A[idx] = 'x' - if not self.instance.A._mutable: + if not self.instance.A.mutable: self.fail("Expected setitem[%s] to fail for immutable Params" % (idx,)) if self.expectTextDomainError: @@ -268,7 +268,7 @@ def test_setitem_default_override(self): % ( str(sys.exc_info()[1]), idx) ) except TypeError: # immutable Params should raise a TypeError exception - if self.instance.A._mutable: + if self.instance.A.mutable: raise def test_dim(self): @@ -293,7 +293,7 @@ def test_keys(self): def test_values(self): expectException = False # len(self.sparse_data) < len(self.data) and \ - # not self.instance.A._mutable + # not self.instance.A.mutable try: test = self.instance.A.values() #self.assertEqual( type(test), list ) @@ -311,7 +311,7 @@ def test_items(self): expectException = False # len(self.sparse_data) < len(self.data) and \ # not self.instance.A._default_val is _NotValid and \ - # not self.instance.A._mutable + # not self.instance.A.mutable try: test = self.instance.A.items() #self.assertEqual( type(test), list ) @@ -332,7 +332,7 @@ def test_itervalues(self): expectException = False # len(self.sparse_data) < len(self.data) and \ # not self.instance.A._default_val is None and \ - # not self.instance.A._mutable + # not self.instance.A.mutable try: test = itervalues(self.instance.A) test = zip(self.instance.A.keys(), test) @@ -349,7 +349,7 @@ def test_iteritems(self): expectException = False # len(self.sparse_data) < len(self.data) and \ # not self.instance.A._default_val is None and \ - # not self.instance.A._mutable + # not self.instance.A.mutable try: test = iteritems(self.instance.A) if self.instance.A._default_val is _NotValid: @@ -416,7 +416,7 @@ def test_get_default(self): return idx = list(set(self.data) - set(self.sparse_data))[0] expectException = self.instance.A._default_val is _NotValid \ - and not self.instance.A._mutable + and not self.instance.A.mutable try: test = self.instance.A[idx] if expectException: @@ -1465,7 +1465,7 @@ def test_mutable_self(self): # Test that display actually displays the correct param value def test_mutable_display(self): - tmp_stream = pyutilib.services.TempfileManager.create_tempfile(suffix = '.param_display.test') + tmp_stream = TempfileManager.create_tempfile(suffix = '.param_display.test') model = ConcreteModel() model.Q = Param(initialize=0.0, mutable=True) self.assertEqual(model.Q, 0.0) @@ -1634,7 +1634,7 @@ def test_mutable_self4(self): # Test that display actually displays the correct param value def test_mutable_display(self): - tmp_stream = pyutilib.services.TempfileManager.create_tempfile(suffix = '.param_display.test') + tmp_stream = TempfileManager.create_tempfile(suffix = '.param_display.test') model = ConcreteModel() model.P = Param([1,2],default=0.0, mutable=True) model.Q = Param([1,2],initialize=0.0, mutable=True) @@ -1697,7 +1697,7 @@ def test_mutable_display(self): # Test that pprint actually displays the correct param value def test_mutable_pprint(self): - tmp_stream = pyutilib.services.TempfileManager.create_tempfile(suffix = '.param_display.test') + tmp_stream = TempfileManager.create_tempfile(suffix = '.param_display.test') model = ConcreteModel() model.P = Param([1,2],default=0.0, mutable=True) model.Q = Param([1,2],initialize=0.0, mutable=True) diff --git a/pyomo/core/tests/unit/test_pickle.py b/pyomo/core/tests/unit/test_pickle.py index 74a58a10ea8..8e1061ca12f 100644 --- a/pyomo/core/tests/unit/test_pickle.py +++ b/pyomo/core/tests/unit/test_pickle.py @@ -20,8 +20,7 @@ import platform import pyutilib.th as unittest -from pyomo.environ import * -import pyomo.core.expr.current as EXPR +from pyomo.environ import AbstractModel, ConcreteModel, Set, Param, Var, Constraint, Objective, Reals, NonNegativeReals, sum_product using_pypy = platform.python_implementation() == "PyPy" diff --git a/pyomo/core/tests/unit/test_piecewise.py b/pyomo/core/tests/unit/test_piecewise.py index c0847c3cbc3..e4004ef289f 100644 --- a/pyomo/core/tests/unit/test_piecewise.py +++ b/pyomo/core/tests/unit/test_piecewise.py @@ -17,7 +17,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, ConcreteModel, Set, Var, Piecewise, Constraint class TestMiscPiecewise(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_preprocess.py b/pyomo/core/tests/unit/test_preprocess.py index d37d9f6aa1d..319f08d2b38 100644 --- a/pyomo/core/tests/unit/test_preprocess.py +++ b/pyomo/core/tests/unit/test_preprocess.py @@ -17,7 +17,7 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import AbstractModel, Set, Param, Var, Objective class TestPreprocess(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_reference.py b/pyomo/core/tests/unit/test_reference.py index 883b2442f5a..7086e795a96 100644 --- a/pyomo/core/tests/unit/test_reference.py +++ b/pyomo/core/tests/unit/test_reference.py @@ -16,9 +16,9 @@ currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest -from six import itervalues, StringIO +from six import itervalues, StringIO, iterkeys, iteritems -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Block, Var, Set, RangeSet, Param, value from pyomo.core.base.var import IndexedVar from pyomo.core.base.set import SetProduct, UnorderedSetOf from pyomo.core.base.indexed_component import ( diff --git a/pyomo/core/tests/unit/test_set.py b/pyomo/core/tests/unit/test_set.py index acf0d65dcc0..9dd848799e5 100644 --- a/pyomo/core/tests/unit/test_set.py +++ b/pyomo/core/tests/unit/test_set.py @@ -25,6 +25,7 @@ from pyomo.common import DeveloperError from pyomo.common.dependencies import numpy as np, numpy_available +from pyomo.common.dependencies import pandas as pd, pandas_available from pyomo.common.log import LoggingIntercept from pyomo.core.expr import native_numeric_types, native_types import pyomo.core.base.set as SetModule @@ -37,12 +38,12 @@ AnyRange, _AnySet, Any, AnyWithNone, _EmptySet, EmptySet, Binary, Reals, NonNegativeReals, PositiveReals, NonPositiveReals, NegativeReals, Integers, PositiveIntegers, NegativeIntegers, - NonPositiveIntegers, NonNegativeIntegers, + NonNegativeIntegers, Set, SetOf, OrderedSetOf, UnorderedSetOf, RangeSet, _FiniteRangeSetData, _InfiniteRangeSetData, FiniteSimpleRangeSet, InfiniteSimpleRangeSet, - AbstractFiniteSimpleRangeSet, AbstractInfiniteSimpleRangeSet, + AbstractFiniteSimpleRangeSet, SetUnion_InfiniteSet, SetUnion_FiniteSet, SetUnion_OrderedSet, SetIntersection_InfiniteSet, SetIntersection_FiniteSet, SetIntersection_OrderedSet, @@ -1768,6 +1769,34 @@ def x_init(m,i): self.assertEqual(i.x[1].domain, i.A*i.B) self.assertEqual(i.x[1], []) + @unittest.skipIf(not pandas_available, "pandas is not available") + def test_pandas_multiindex_set_init(self): + # Test that TuplizeValuesInitializer does not assume truthiness + # If it does, pandas will complain with the following error: + # ValueError: The truth value of a MultiIndex is ambiguous. + # Use a.empty, a.bool(), a.item(), a.any() or a.all(). + iterables = [['bar', 'baz', 'foo', 'qux'], ['one', 'two']] + pandas_index = pd.MultiIndex.from_product( + iterables, + names=['first', 'second'] + ) + + model = ConcreteModel() + model.a = Set(initialize=pandas_index, + dimen=pandas_index.nlevels) + + # we will confirm that dimension is inferred correctly + model.b = Set(initialize=pandas_index) + + self.assertIsInstance(model.a, Set) + self.assertEquals(list(model.a), list(pandas_index)) + self.assertEquals(model.a.dimen, pandas_index.nlevels) + + self.assertIsInstance(model.b, Set) + self.assertEquals(list(model.b), list(pandas_index)) + self.assertEquals(model.b.dimen, pandas_index.nlevels) + + class TestSetUnion(unittest.TestCase): def test_pickle(self): a = SetOf([1,3,5]) | SetOf([2,3,4]) @@ -3333,21 +3362,50 @@ def test_exceptions(self): RangeSet( name='foo', ranges=(NR(0,2,1),) ), NS) def test_RealSet_IntegerSet(self): - a = SetModule.RealSet() + output = StringIO() + with LoggingIntercept(output, 'pyomo.core'): + a = SetModule.RealSet() + self.assertIn('DEPRECATED: The use of RealSet,', output.getvalue()) self.assertEqual(a, Reals) self.assertIsNot(a, Reals) - a = SetModule.RealSet(bounds=(1,3)) + output = StringIO() + with LoggingIntercept(output, 'pyomo.core'): + a = SetModule.RealSet(bounds=(1,3)) + self.assertIn('DEPRECATED: The use of RealSet,', output.getvalue()) self.assertEqual(a.bounds(), (1,3)) - a = SetModule.IntegerSet() + output = StringIO() + with LoggingIntercept(output, 'pyomo.core'): + a = SetModule.IntegerSet() + self.assertIn('DEPRECATED: The use of RealSet,', output.getvalue()) self.assertEqual(a, Integers) self.assertIsNot(a, Integers) - a = SetModule.IntegerSet(bounds=(1,3)) + output = StringIO() + with LoggingIntercept(output, 'pyomo.core'): + a = SetModule.IntegerSet(bounds=(1,3)) + self.assertIn('DEPRECATED: The use of RealSet,', output.getvalue()) self.assertEqual(a.bounds(), (1,3)) self.assertEqual(list(a), [1,2,3]) + m = ConcreteModel() + + output = StringIO() + with LoggingIntercept(output, 'pyomo.core'): + m.x = Var(within=SetModule.RealSet) + self.assertIn('DEPRECATED: The use of RealSet,', output.getvalue()) + + output = StringIO() + with LoggingIntercept(output, 'pyomo.core'): + m.y = Var(within=SetModule.RealSet()) + self.assertIn('DEPRECATED: The use of RealSet,', output.getvalue()) + + output = StringIO() + with LoggingIntercept(output, 'pyomo.core'): + m.z = Var(within=SetModule.RealSet(bounds=(0,None))) + self.assertIn('DEPRECATED: The use of RealSet,', output.getvalue()) + with self.assertRaisesRegex( RuntimeError, "Unexpected keyword arguments: \{'foo': 5\}"): IntegerSet(foo=5) diff --git a/pyomo/core/tests/unit/test_sets.py b/pyomo/core/tests/unit/test_sets.py index 97176726285..d25245f9a2d 100644 --- a/pyomo/core/tests/unit/test_sets.py +++ b/pyomo/core/tests/unit/test_sets.py @@ -38,9 +38,16 @@ import pyutilib.th as unittest import pyomo.core.base -from pyomo.environ import * +from pyomo.environ import (Set, SetOf, RangeSet, Param, ConcreteModel, + AbstractModel, Expression, EmptySet, NonPositiveIntegers, + NonPositiveReals, PositiveReals, NegativeReals, + IntegerSet, NegativeIntegers, + PositiveIntegers, RealSet, BooleanSet, + IntegerInterval, RealInterval, Binary, + PercentFraction, UnitInterval, NonNegativeIntegers, + Integers, NonNegativeReals, Boolean, Reals, + Any, value, set_options, simple_set_rule) from pyomo.core.base.set import _AnySet, RangeDifferenceError -from pyomo.core.base.component import CloneError _has_numpy = False try: diff --git a/pyomo/core/tests/unit/test_smap.py b/pyomo/core/tests/unit/test_smap.py index c45e73d885a..dfb3dd77b64 100644 --- a/pyomo/core/tests/unit/test_smap.py +++ b/pyomo/core/tests/unit/test_smap.py @@ -11,10 +11,9 @@ # Unit Tests for SymbolMap # -from six import StringIO -import os import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Set, Var, Objective, Constraint, Block, SymbolMap, TextLabeler +from pyomo.core.base.symbol_map import symbol_map_from_instance class Test(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_sos.py b/pyomo/core/tests/unit/test_sos.py index 3a1d88b2f9c..fb3d31172c8 100644 --- a/pyomo/core/tests/unit/test_sos.py +++ b/pyomo/core/tests/unit/test_sos.py @@ -16,8 +16,7 @@ currdir = dirname(abspath(__file__))+os.sep from six.moves import xrange import pyutilib.th as unittest -from pyomo.core.base import IntegerSet -from pyomo.environ import * +from pyomo.environ import ConcreteModel, AbstractModel, SOSConstraint, Var, Set class TestErrors(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_suffix.py b/pyomo/core/tests/unit/test_suffix.py index 957f35a3b64..0f3fc5d33d7 100644 --- a/pyomo/core/tests/unit/test_suffix.py +++ b/pyomo/core/tests/unit/test_suffix.py @@ -27,7 +27,7 @@ local_suffix_generator, active_suffix_generator, suffix_generator) -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Suffix, Var, Param, Set, Objective, Constraint, Block, sum_product from six import StringIO diff --git a/pyomo/core/tests/unit/test_symbol_map.py b/pyomo/core/tests/unit/test_symbol_map.py index 11d6a059898..16645fd7029 100644 --- a/pyomo/core/tests/unit/test_symbol_map.py +++ b/pyomo/core/tests/unit/test_symbol_map.py @@ -1,7 +1,14 @@ -import pickle +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ import pyutilib.th as unittest -import pyomo.environ from pyomo.core.expr.symbol_map import SymbolMap from pyomo.core.kernel.variable import variable diff --git a/pyomo/core/tests/unit/test_symbolic.py b/pyomo/core/tests/unit/test_symbolic.py index c0ba5055efb..82cc7ef959d 100644 --- a/pyomo/core/tests/unit/test_symbolic.py +++ b/pyomo/core/tests/unit/test_symbolic.py @@ -10,9 +10,11 @@ import pyutilib.th as unittest -import pyomo.environ from pyomo.common.errors import DeveloperError, NondifferentiableError -from pyomo.core import * +from pyomo.environ import (ConcreteModel, Var, Param, Set, NonNegativeReals, + Expression, RangeSet, sin, cos, tan, sinh, cosh, + tanh, asin, acos, atan, asinh, acosh, atanh, + log, log10, exp, sqrt, ceil, floor) from pyomo.core.expr.calculus.diff_with_sympy import differentiate from pyomo.core.expr.sympy_tools import PyomoSympyBimap, sympy_available, sympy2pyomo_expression diff --git a/pyomo/core/tests/unit/test_taylor_series.py b/pyomo/core/tests/unit/test_taylor_series.py index acd251e401b..fe6d2a93506 100644 --- a/pyomo/core/tests/unit/test_taylor_series.py +++ b/pyomo/core/tests/unit/test_taylor_series.py @@ -1,5 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.core.expr.taylor_series import taylor_series_expansion from pyomo.core.expr.current import polynomial_degree from pyomo.core.expr.calculus.derivatives import differentiate @@ -7,20 +17,20 @@ class TestTaylorSeries(unittest.TestCase): def test_first_order_taylor_series(self): - m = pe.ConcreteModel() - m.x = pe.Var() + m = pyo.ConcreteModel() + m.x = pyo.Var() m.x.value = 1 - exprs_to_test = [m.x**2, pe.exp(m.x), (m.x + 2)**2] + exprs_to_test = [m.x**2, pyo.exp(m.x), (m.x + 2)**2] for e in exprs_to_test: tsa = taylor_series_expansion(e) - self.assertAlmostEqual(pe.differentiate(e, wrt=m.x), pe.differentiate(tsa, wrt=m.x)) - self.assertAlmostEqual(pe.value(e), pe.value(tsa)) + self.assertAlmostEqual(pyo.differentiate(e, wrt=m.x), pyo.differentiate(tsa, wrt=m.x)) + self.assertAlmostEqual(pyo.value(e), pyo.value(tsa)) self.assertEqual(polynomial_degree(tsa), 1) def test_higher_order_taylor_series(self): - m = pe.ConcreteModel() - m.x = pe.Var(initialize=0.5) - m.y = pe.Var(initialize=1.5) + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=0.5) + m.y = pyo.Var(initialize=1.5) e = m.x * m.y tse = taylor_series_expansion(e, diff_mode=differentiate.Modes.reverse_symbolic, order=2) @@ -28,7 +38,7 @@ def test_higher_order_taylor_series(self): for _y in [-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2]: m.x.value = _x m.y.value = _y - self.assertAlmostEqual(pe.value(e), pe.value(tse)) + self.assertAlmostEqual(pyo.value(e), pyo.value(tse)) e = m.x**3 + m.y**3 tse = taylor_series_expansion(e, diff_mode=differentiate.Modes.reverse_symbolic, order=3) @@ -36,7 +46,7 @@ def test_higher_order_taylor_series(self): for _y in [-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2]: m.x.value = _x m.y.value = _y - self.assertAlmostEqual(pe.value(e), pe.value(tse)) + self.assertAlmostEqual(pyo.value(e), pyo.value(tse)) e = (m.x*m.y)**2 tse = taylor_series_expansion(e, diff_mode=differentiate.Modes.reverse_symbolic, order=4) @@ -44,4 +54,4 @@ def test_higher_order_taylor_series(self): for _y in [-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2]: m.x.value = _x m.y.value = _y - self.assertAlmostEqual(pe.value(e), pe.value(tse)) + self.assertAlmostEqual(pyo.value(e), pyo.value(tse)) diff --git a/pyomo/core/tests/unit/test_template_expr.py b/pyomo/core/tests/unit/test_template_expr.py index 159c4646d62..f2c9ac22182 100644 --- a/pyomo/core/tests/unit/test_template_expr.py +++ b/pyomo/core/tests/unit/test_template_expr.py @@ -27,7 +27,6 @@ substitute_template_with_value, ) -import six class TestTemplateExpressions(unittest.TestCase): def setUp(self): diff --git a/pyomo/core/tests/unit/test_units.py b/pyomo/core/tests/unit/test_units.py index 8a2e647b9f1..3d34a9188ff 100644 --- a/pyomo/core/tests/unit/test_units.py +++ b/pyomo/core/tests/unit/test_units.py @@ -12,9 +12,8 @@ # import pyutilib.th as unittest -from pyomo.environ import * -from pyomo.util.check_units import assert_units_consistent, assert_units_equivalent -from pyomo.core.base.template_expr import IndexTemplate +from pyomo.environ import ConcreteModel, Var, Param, Set, Constraint, Objective, Expression, ExternalFunction, value, log, log10, exp, sqrt, cos, sin, tan, asin, acos, atan, cosh, sinh, tanh, asinh, acosh, atanh, ceil, floor, sum_product, maximize, units +from pyomo.util.check_units import assert_units_consistent from pyomo.core.expr import inequality import pyomo.core.expr.current as EXPR from pyomo.core.base.units_container import ( diff --git a/pyomo/core/tests/unit/test_var.py b/pyomo/core/tests/unit/test_var.py index 5faf0c44ef8..12408734167 100644 --- a/pyomo/core/tests/unit/test_var.py +++ b/pyomo/core/tests/unit/test_var.py @@ -21,7 +21,7 @@ import pyutilib.th as unittest from pyomo.core.base import IntegerSet -from pyomo.environ import * +from pyomo.environ import AbstractModel, ConcreteModel, Set, Param, Var, VarList, RangeSet, Suffix, Expression, NonPositiveReals, PositiveReals, Reals, RealSet, NonNegativeReals, Integers, Binary, value class PyomoModel(unittest.TestCase): diff --git a/pyomo/core/tests/unit/test_var_set_bounds.py b/pyomo/core/tests/unit/test_var_set_bounds.py index 7b19dfebc74..21b9292169a 100644 --- a/pyomo/core/tests/unit/test_var_set_bounds.py +++ b/pyomo/core/tests/unit/test_var_set_bounds.py @@ -18,10 +18,10 @@ import pyutilib.th as unittest from six.moves import xrange -import pyomo.opt -from pyomo.environ import * +from pyomo.opt import check_available_solvers +from pyomo.environ import ConcreteModel, RangeSet, Var, Set, Objective, Constraint, SolverFactory, AbstractModel -solvers = pyomo.opt.check_available_solvers('glpk') +solvers = check_available_solvers('glpk') # GAH: These tests been temporarily disabled. It is no longer the job of Var # to validate its domain at the time of construction. It only needs to diff --git a/pyomo/core/tests/unit/test_visitor.py b/pyomo/core/tests/unit/test_visitor.py index 9c3227f88be..2e26e62ee37 100644 --- a/pyomo/core/tests/unit/test_visitor.py +++ b/pyomo/core/tests/unit/test_visitor.py @@ -11,53 +11,30 @@ # Unit Tests for expression generation # -import copy -import pickle -import math import os -import re -import six -import sys from os.path import abspath, dirname currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest from pyutilib.th import nottest -from pyomo.environ import * -import pyomo.kernel -from pyomo.common.log import LoggingIntercept -from pyomo.core.expr.numvalue import ( - native_types, nonpyomo_leaf_types, NumericConstant, as_numeric, - is_potentially_variable, -) +from pyomo.environ import ConcreteModel, RangeSet, Param, Var, Expression, ExternalFunction, VarList, sum_product, inequality, quicksum, sin, tanh +from pyomo.core.expr.numvalue import nonpyomo_leaf_types from pyomo.core.expr.numeric_expr import ( - ExpressionBase, UnaryFunctionExpression, SumExpression, PowExpression, - ProductExpression, DivisionExpression, NegationExpression, - MonomialTermExpression, LinearExpression, - NPV_NegationExpression, NPV_ProductExpression, NPV_DivisionExpression, - NPV_PowExpression, - decompose_term, clone_counter, - _MutableLinearExpression, _MutableSumExpression, _decompose_linear_terms, - LinearDecompositionError, -) -import pyomo.core.expr.logical_expr as logical_expr -from pyomo.core.expr.logical_expr import ( - InequalityExpression, EqualityExpression, RangedExpression, -) + SumExpression, ProductExpression, + MonomialTermExpression, LinearExpression) from pyomo.core.expr.visitor import ( FixedExpressionError, NonConstantExpressionError, StreamBasedExpressionVisitor, ExpressionReplacementVisitor, evaluate_expression, expression_to_string, replace_expressions, - clone_expression, sizeof_expression, + sizeof_expression, identify_variables, identify_components, identify_mutable_parameters, ) -from pyomo.core.expr.current import Expr_if -from pyomo.core.base.var import SimpleVar from pyomo.core.base.param import _ParamData, SimpleParam -from pyomo.core.base.label import * from pyomo.core.expr.template_expr import IndexTemplate from pyomo.core.expr.expr_errors import TemplateExpressionError +from pyomo.common.log import LoggingIntercept +from six import StringIO class TestExpressionUtilities(unittest.TestCase): @@ -758,7 +735,7 @@ def before(node, child): if type(child) in nonpyomo_leaf_types \ or not child.is_expression_type(): return False, [child] - os = six.StringIO() + os = StringIO() with LoggingIntercept(os, 'pyomo'): walker = StreamBasedExpressionVisitor(beforeChild=before) self.assertIn( @@ -938,7 +915,7 @@ def accept(node, data, child_result): def after(node, child): counts[2] += 1 - os = six.StringIO() + os = StringIO() with LoggingIntercept(os, 'pyomo'): walker = StreamBasedExpressionVisitor( beforeChild=before, acceptChildResult=accept, afterChild=after) @@ -1170,7 +1147,7 @@ def afterChild(self, node, child): % (name(child), name(node))) def finalizeResult(self, result): self.ans.append("Finalize") - os = six.StringIO() + os = StringIO() with LoggingIntercept(os, 'pyomo'): walker = all_callbacks() self.assertIn( diff --git a/pyomo/core/tests/unit/test_xfrm_discrete_vars.py b/pyomo/core/tests/unit/test_xfrm_discrete_vars.py index 713a1778d10..f6a5905799a 100644 --- a/pyomo/core/tests/unit/test_xfrm_discrete_vars.py +++ b/pyomo/core/tests/unit/test_xfrm_discrete_vars.py @@ -12,9 +12,10 @@ import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Constraint, Objective, Suffix, Binary, TransformationFactory, SolverFactory, Reals +from pyomo.opt import check_available_solvers -solvers = pyomo.opt.check_available_solvers('cplex', 'gurobi', 'glpk') +solvers = check_available_solvers('cplex', 'gurobi', 'glpk') def _generateModel(): model = ConcreteModel() diff --git a/pyomo/core/tests/unit/uninstantiated_model_linear.py b/pyomo/core/tests/unit/uninstantiated_model_linear.py index b5900b085b0..3f2f6ef9d47 100644 --- a/pyomo/core/tests/unit/uninstantiated_model_linear.py +++ b/pyomo/core/tests/unit/uninstantiated_model_linear.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Set, Param, Var, Objective, minimize model = AbstractModel() diff --git a/pyomo/core/tests/unit/uninstantiated_model_quadratic.py b/pyomo/core/tests/unit/uninstantiated_model_quadratic.py index 503f907bd65..0a512c043a8 100644 --- a/pyomo/core/tests/unit/uninstantiated_model_quadratic.py +++ b/pyomo/core/tests/unit/uninstantiated_model_quadratic.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, Objective, minimize model = AbstractModel() diff --git a/pyomo/core/util.py b/pyomo/core/util.py index 0ddc98317e7..7a4e992e3d2 100644 --- a/pyomo/core/util.py +++ b/pyomo/core/util.py @@ -15,12 +15,11 @@ __all__ = ['sum_product', 'summation', 'dot_product', 'sequence', 'prod', 'quicksum'] from six.moves import xrange -from functools import reduce -import operator from pyomo.core.expr.numvalue import native_numeric_types from pyomo.core.expr.numeric_expr import decompose_term from pyomo.core.expr import current as EXPR -import pyomo.core.base.var +from pyomo.core.base.var import Var +from pyomo.core.base.expression import Expression def prod(terms): @@ -176,11 +175,11 @@ def sum_product(*args, **kwds): else: if nargs > 0: iarg=args[-1] - if not isinstance(iarg,pyomo.core.base.var.Var) and not isinstance(iarg, pyomo.core.base.expression.Expression): + if not isinstance(iarg,Var) and not isinstance(iarg, Expression): raise ValueError("Error executing sum_product(): The last argument value must be a variable or expression object if no 'index' option is specified") else: iarg=denom[-1] - if not isinstance(iarg,pyomo.core.base.var.Var) and not isinstance(iarg, pyomo.core.base.expression.Expression): + if not isinstance(iarg,Var) and not isinstance(iarg, Expression): raise ValueError("Error executing sum_product(): The last denom argument value must be a variable or expression object if no 'index' option is specified") index = iarg.index_set() @@ -188,7 +187,7 @@ def sum_product(*args, **kwds): vars_ = [] params_ = [] for arg in args: - if isinstance(arg, pyomo.core.base.var.Var): + if isinstance(arg, Var): vars_.append(arg) else: params_.append(arg) diff --git a/pyomo/dae/contset.py b/pyomo/dae/contset.py index db8564e0c75..e3f81eb6c43 100644 --- a/pyomo/dae/contset.py +++ b/pyomo/dae/contset.py @@ -11,7 +11,6 @@ import logging import bisect from pyomo.common.timing import ConstructionTimer -from pyomo.core import * from pyomo.core.base.plugin import ModelComponentFactory from pyomo.core.base.set import SortedSimpleSet from pyomo.core.base.numvalue import native_numeric_types diff --git a/pyomo/dae/diffvar.py b/pyomo/dae/diffvar.py index 0184e86167d..2428862f4cb 100644 --- a/pyomo/dae/diffvar.py +++ b/pyomo/dae/diffvar.py @@ -11,7 +11,7 @@ import weakref from pyomo.common.collections import ComponentMap from pyomo.core.base.set import UnknownSetDimen -from pyomo.core.base.var import Var, _VarData +from pyomo.core.base.var import Var from pyomo.core.base.plugin import ModelComponentFactory from pyomo.dae.contset import ContinuousSet from six import iterkeys diff --git a/pyomo/dae/initialization.py b/pyomo/dae/initialization.py index 7ee2b10895f..8386af7edb5 100644 --- a/pyomo/dae/initialization.py +++ b/pyomo/dae/initialization.py @@ -87,7 +87,8 @@ def get_inconsistent_initial_conditions(model, time, tol=1e-8, t0=None, return list(inconsistent) -def solve_consistent_initial_conditions(model, time, solver, tee=False): +def solve_consistent_initial_conditions(model, time, solver, tee=False, + allow_skip=True, suppress_warnings=False): """ Solves a model with all Constraints and Blocks deactivated except at the initial value of the Set time. Reactivates Constraints and @@ -98,6 +99,11 @@ def solve_consistent_initial_conditions(model, time, solver, tee=False): time: Set whose initial conditions will remain active for solve solver: Something that implements a solve method that accepts a model and tee keyword as arguments + tee: tee argument that will be sent to solver's solve method + allow_skip: If True, KeyErrors due to Constraint.Skip being + used will be ignored + suppress_warnings: If True, warnings due to ignored + KeyErrors will be suppressed Returns: The object returned by the solver's solve method @@ -119,9 +125,13 @@ def solve_consistent_initial_conditions(model, time, solver, tee=False): raise NotImplementedError( '%s discretization scheme is not supported' % scheme) - t0 = time.first() timelist = list(time)[1:] - deactivated_dict = deactivate_model_at(model, time, timelist) + deactivated_dict = deactivate_model_at( + model, + time, + timelist, + allow_skip=allow_skip, + suppress_warnings=suppress_warnings) result = solver.solve(model, tee=tee) diff --git a/pyomo/dae/integral.py b/pyomo/dae/integral.py index 9e531e5ce0d..cd56998b035 100644 --- a/pyomo/dae/integral.py +++ b/pyomo/dae/integral.py @@ -15,7 +15,6 @@ _GeneralExpressionData, SimpleExpression, IndexedExpression) -from pyomo.dae.misc import create_access_function, create_partial_expression __all__ = ('Integral', ) diff --git a/pyomo/dae/misc.py b/pyomo/dae/misc.py index 25044370538..a5511d189b7 100644 --- a/pyomo/dae/misc.py +++ b/pyomo/dae/misc.py @@ -10,17 +10,15 @@ import logging +from pyomo.common.collections import ComponentMap +from pyomo.common.log import LoggingIntercept from pyomo.core import Suffix, Var, Constraint, Piecewise, Block from pyomo.core import Expression, Param -from pyomo.core.base.indexed_component import IndexedComponent from pyomo.core.base.misc import apply_indexed_rule -from pyomo.core.base.block import _BlockData, IndexedBlock -from pyomo.dae import ContinuousSet, DerivativeVar, DAE_Error -from pyomo.common.collections import ComponentMap -from pyomo.core.base.block import SortComponents -from pyomo.common.log import LoggingIntercept +from pyomo.core.base.block import IndexedBlock, SortComponents +from pyomo.dae import ContinuousSet, DAE_Error -from six import iterkeys, itervalues, iteritems, StringIO +from six import iterkeys, itervalues, StringIO logger = logging.getLogger('pyomo.dae') @@ -171,7 +169,7 @@ def expand_components(block): N = len(redo_expansion) - except Exception as e: + except Exception: logger.error(buf.getvalue()) raise @@ -451,7 +449,6 @@ def get_index_information(var, ds): # Find index order of ContinuousSet in the variable indargs = [] dsindex = 0 - tmpds2 = None if var.dim() != 1: indCount = 0 diff --git a/pyomo/dae/set_utils.py b/pyomo/dae/set_utils.py index dbd19ea71d3..40698e3f535 100644 --- a/pyomo/dae/set_utils.py +++ b/pyomo/dae/set_utils.py @@ -116,7 +116,6 @@ def get_index_set_except(comp, *sets): and a value for each set excluded. These values must be provided in the same order their Sets were provided in the sets argument. """ - n_set = len(sets) s_set = ComponentSet(sets) try: total_s_dim = sum([s.dimen for s in sets]) diff --git a/pyomo/dae/simulator.py b/pyomo/dae/simulator.py index 9d4659c860e..12ea70c3a66 100644 --- a/pyomo/dae/simulator.py +++ b/pyomo/dae/simulator.py @@ -6,20 +6,16 @@ # the U.S. Government retains certain rights in this software. # This software is distributed under the BSD License. # _________________________________________________________________________ -from pyomo.core.base import Constraint, Param, Var, value, Suffix, Block +from pyomo.core.base import Constraint, Param, value, Suffix, Block from pyomo.dae import ContinuousSet, DerivativeVar from pyomo.dae.diffvar import DAE_Error from pyomo.core.expr import current as EXPR -from pyomo.core.expr.numvalue import ( - NumericValue, native_numeric_types, nonpyomo_leaf_types, -) +from pyomo.core.expr.numvalue import native_numeric_types from pyomo.core.expr.template_expr import IndexTemplate, _GetItemIndexer -from pyomo.core.base.indexed_component_slice import IndexedComponent_slice -from pyomo.core.base.reference import Reference -from six import iterkeys, itervalues +from six import iterkeys import logging diff --git a/pyomo/dae/tests/test_colloc.py b/pyomo/dae/tests/test_colloc.py index 688ffe7fe8a..6c4ad431fdd 100644 --- a/pyomo/dae/tests/test_colloc.py +++ b/pyomo/dae/tests/test_colloc.py @@ -11,17 +11,14 @@ from __future__ import print_function import pyutilib.th as unittest -from pyomo.environ import (Var, Set, ConcreteModel, value, Constraint, +from pyomo.environ import (Var, Set, ConcreteModel, TransformationFactory, pyomo) from pyomo.dae import ContinuousSet, DerivativeVar from pyomo.dae.diffvar import DAE_Error from pyomo.repn import generate_standard_repn -import os from six import StringIO -from pyutilib.misc import setup_redirect, reset_redirect -from pyutilib.misc import import_file from pyomo.common.log import LoggingIntercept diff --git a/pyomo/dae/tests/test_diffvar.py b/pyomo/dae/tests/test_diffvar.py index 98c8b110d63..381957aa954 100644 --- a/pyomo/dae/tests/test_diffvar.py +++ b/pyomo/dae/tests/test_diffvar.py @@ -20,7 +20,6 @@ from pyomo.environ import ConcreteModel, Var, Set, TransformationFactory from pyomo.dae import ContinuousSet, DerivativeVar from pyomo.dae.diffvar import DAE_Error -from six import StringIO currdir = dirname(abspath(__file__)) + os.sep diff --git a/pyomo/dae/tests/test_finite_diff.py b/pyomo/dae/tests/test_finite_diff.py index dbe20a44f7c..05964fd5538 100644 --- a/pyomo/dae/tests/test_finite_diff.py +++ b/pyomo/dae/tests/test_finite_diff.py @@ -11,15 +11,12 @@ from __future__ import print_function import pyutilib.th as unittest -from pyomo.environ import (Var, Set, ConcreteModel, value, Constraint, - TransformationFactory, pyomo) +from pyomo.environ import (Var, Set, ConcreteModel, + TransformationFactory) from pyomo.dae import ContinuousSet, DerivativeVar from pyomo.dae.diffvar import DAE_Error -import os from six import StringIO -from pyutilib.misc import setup_redirect, reset_redirect -from pyutilib.misc import import_file from pyomo.common.log import LoggingIntercept diff --git a/pyomo/dae/tests/test_initialization.py b/pyomo/dae/tests/test_initialization.py index c0419b78e97..fdb1026f6ee 100644 --- a/pyomo/dae/tests/test_initialization.py +++ b/pyomo/dae/tests/test_initialization.py @@ -14,16 +14,12 @@ import os from os.path import abspath, dirname -from six import StringIO import pyutilib.th as unittest -from pyomo.core.base import * -from pyomo.environ import SolverFactory -from pyomo.common.collections import ComponentMap -from pyomo.common.log import LoggingIntercept -from pyomo.dae import * -from pyomo.dae.initialization import * +from pyomo.environ import SolverFactory, ConcreteModel, Set, Block, Var, Constraint, TransformationFactory +from pyomo.dae import ContinuousSet, DerivativeVar +from pyomo.dae.initialization import solve_consistent_initial_conditions, get_inconsistent_initial_conditions currdir = dirname(abspath(__file__)) + os.sep @@ -77,6 +73,12 @@ def con2(fs, x): return fs.b1.v[m.time.first(), x] == fs.v0[x] # will be consistent + @m.fs.Constraint(m.time, m.space) + def con3(fs, t, x): + if x == m.space.first(): + return Constraint.Skip + return fs.b2[t, x].v['a'] == 7. + disc = TransformationFactory('dae.collocation') disc.apply_to(m, wrt=m.time, nfe=5, ncp=2, scheme='LAGRANGE-RADAU') disc.apply_to(m, wrt=m.space, nfe=5, ncp=2, scheme='LAGRANGE-RADAU') @@ -101,7 +103,7 @@ def test_get_inconsistent_initial_conditions(self): def test_solve_consistent_initial_conditions(self): m = make_model() solver = SolverFactory('ipopt') - solve_consistent_initial_conditions(m, m.time, solver) + solve_consistent_initial_conditions(m, m.time, solver, allow_skip=True) inconsistent = get_inconsistent_initial_conditions(m, m.time) self.assertFalse(inconsistent) @@ -110,6 +112,14 @@ def test_solve_consistent_initial_conditions(self): self.assertTrue(m.fs.b1.con[m.time[1], m.space[1]].active) self.assertTrue(m.fs.b1.con[m.time[3], m.space[1]].active) + with self.assertRaises(KeyError): + solve_consistent_initial_conditions( + m, + m.time, + solver, + allow_skip=False, + ) + if __name__ == "__main__": unittest.main() diff --git a/pyomo/dae/tests/test_integral.py b/pyomo/dae/tests/test_integral.py index dc5fd2f1312..dec26c332c5 100644 --- a/pyomo/dae/tests/test_integral.py +++ b/pyomo/dae/tests/test_integral.py @@ -24,7 +24,6 @@ from pyomo.repn import generate_standard_repn -from six import StringIO currdir = dirname(abspath(__file__)) + os.sep diff --git a/pyomo/dae/tests/test_misc.py b/pyomo/dae/tests/test_misc.py index ba078b4b06d..0d3ba0db5f4 100644 --- a/pyomo/dae/tests/test_misc.py +++ b/pyomo/dae/tests/test_misc.py @@ -14,15 +14,23 @@ import os from os.path import abspath, dirname -from six import StringIO +from six import StringIO, iterkeys import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ( + ConcreteModel, Set, Param, Var, Constraint, Expression, Block, + TransformationFactory, Piecewise, Objective, ExternalFunction, + Suffix, value, +) from pyomo.common.collections import ComponentMap from pyomo.common.log import LoggingIntercept -from pyomo.dae import * -from pyomo.dae.misc import * +from pyomo.dae import ContinuousSet, DerivativeVar +from pyomo.dae.misc import ( + generate_finite_elements, generate_colloc_points, + update_contset_indexed_component, expand_components, + get_index_information, +) currdir = dirname(abspath(__file__)) + os.sep diff --git a/pyomo/dae/tests/test_set_utils.py b/pyomo/dae/tests/test_set_utils.py index 2efe22a8b0f..902d0f534e5 100644 --- a/pyomo/dae/tests/test_set_utils.py +++ b/pyomo/dae/tests/test_set_utils.py @@ -14,16 +14,16 @@ import os from os.path import abspath, dirname -from six import StringIO - import pyutilib.th as unittest -from pyomo.core.base import (Block, Constraint, ConcreteModel, Var, Set, - TransformationFactory) -from pyomo.common.collections import ComponentMap -from pyomo.common.log import LoggingIntercept -from pyomo.dae import * -from pyomo.dae.set_utils import * +from pyomo.core.base import ( + Block, Constraint, ConcreteModel, Var, Set, TransformationFactory +) +from pyomo.dae import ContinuousSet, DerivativeVar +from pyomo.dae.set_utils import ( + is_explicitly_indexed_by, is_in_block_indexed_by, get_index_set_except, + deactivate_model_at, +) currdir = dirname(abspath(__file__)) + os.sep diff --git a/pyomo/dae/tests/test_simulator.py b/pyomo/dae/tests/test_simulator.py index 9d4a9906443..e62c0b581ea 100644 --- a/pyomo/dae/tests/test_simulator.py +++ b/pyomo/dae/tests/test_simulator.py @@ -13,7 +13,7 @@ from pyomo.core.expr import current as EXPR from pyomo.environ import ( - ConcreteModel, RangeSet, Param, Var, Set, value, Constraint, + ConcreteModel, Param, Var, Set, Constraint, sin, log, sqrt, TransformationFactory) from pyomo.dae import ContinuousSet, DerivativeVar from pyomo.dae.diffvar import DAE_Error diff --git a/pyomo/dataportal/factory.py b/pyomo/dataportal/factory.py index d17fa139e3a..5760c819ca0 100644 --- a/pyomo/dataportal/factory.py +++ b/pyomo/dataportal/factory.py @@ -14,8 +14,8 @@ ] import logging -import pyutilib.misc from pyomo.common import Factory +from pyutilib.component.core import PluginError logger = logging.getLogger('pyomo.core') diff --git a/pyomo/dataportal/parse_datacmds.py b/pyomo/dataportal/parse_datacmds.py index a38f356563a..272cf6bd8dd 100644 --- a/pyomo/dataportal/parse_datacmds.py +++ b/pyomo/dataportal/parse_datacmds.py @@ -20,10 +20,8 @@ from inspect import getfile, currentframe from six.moves import xrange -from pyutilib.misc import flatten_list, import_file - -from pyomo.common import config -from pyomo.common.fileutils import this_file_dir, this_file +from pyutilib.misc import flatten_list +from pyomo.common.fileutils import this_file _re_number = r'[-+]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][-+]?[0-9]+)?' diff --git a/pyomo/dataportal/process_data.py b/pyomo/dataportal/process_data.py index 34a941c2e22..e1d534cdc2c 100644 --- a/pyomo/dataportal/process_data.py +++ b/pyomo/dataportal/process_data.py @@ -11,10 +11,9 @@ import sys import re import copy -import math import logging -from pyutilib.misc import quote_split, Options +from pyutilib.misc import Options import pyutilib.common from pyutilib.misc import flatten @@ -258,7 +257,6 @@ def _process_set_data(cmd, sname, _model): logger.debug("DEBUG: _process_set_data(start) %s",cmd) if len(cmd) == 0: return [] - sd = sname ans=[] i=0 template=None @@ -549,7 +547,7 @@ def _apply_templates(cmd): nindex = len(tmp) template=tmp ilist = set() - for kk in range(len(tmp)): + for kk in range(nindex): if tmp[kk] == '*': ilist.add(kk) elif len(ilist) == 0: @@ -868,7 +866,6 @@ def _process_load(cmd, _model, _data, _default, options=None): isinstance(data, UnknownDataManager): raise pyutilib.common.ApplicationError("Data manager '%s' is not available." % options.using) set_name=None - param_name=None # # Create symbol map # diff --git a/pyomo/dataportal/tests/test_dataportal.py b/pyomo/dataportal/tests/test_dataportal.py index e985374c3c0..360bf047ae7 100644 --- a/pyomo/dataportal/tests/test_dataportal.py +++ b/pyomo/dataportal/tests/test_dataportal.py @@ -19,7 +19,7 @@ import pyutilib.th as unittest from pyomo.dataportal.factory import DataManagerFactory -from pyomo.environ import * +from pyomo.environ import AbstractModel, ConcreteModel, Set, DataPortal, Param, Boolean, Any, value currdir=dirname(abspath(__file__))+os.sep example_dir=pyomo_dir+os.sep+".."+os.sep+"examples"+os.sep+"pyomo"+os.sep+"tutorials"+os.sep+"tab"+os.sep diff --git a/pyomo/duality/lagrangian_dual.py b/pyomo/duality/lagrangian_dual.py index f5252f0d208..df4d630279c 100644 --- a/pyomo/duality/lagrangian_dual.py +++ b/pyomo/duality/lagrangian_dual.py @@ -12,11 +12,11 @@ # NOTE: deprecated code # from pyomo.common.deprecation import deprecated -from pyomo.core import * +from pyomo.core import TransformationFactory, Constraint, Set, Var, Objective, AbstractModel, maximize +from pyomo.repn import generate_standard_repn from pyomo.core.plugins.transform.hierarchy import IsomorphicTransformation from pyomo.core.plugins.transform.standard_form import StandardForm from pyomo.core.plugins.transform.util import partial, process_canonical_repn -from pyomo.common.deprecation import deprecated @TransformationFactory.register("core.lagrangian_dual", doc="Create the LP dual model.") diff --git a/pyomo/duality/plugins.py b/pyomo/duality/plugins.py index f81e7d1b98a..74534e209ad 100644 --- a/pyomo/duality/plugins.py +++ b/pyomo/duality/plugins.py @@ -17,7 +17,6 @@ Var, Constraint, Objective, - Set, minimize, NonNegativeReals, NonPositiveReals, @@ -26,7 +25,6 @@ Model, ConcreteModel) from pyomo.duality.collect import collect_linear_terms -from pyomo.common.deprecation import deprecated def load(): pass diff --git a/pyomo/duality/tests/test_linear_dual.py b/pyomo/duality/tests/test_linear_dual.py index 33eb47d42e5..28e88391b4d 100644 --- a/pyomo/duality/tests/test_linear_dual.py +++ b/pyomo/duality/tests/test_linear_dual.py @@ -20,8 +20,6 @@ import pyutilib.th as unittest from pyomo.common.dependencies import yaml, yaml_available, yaml_load_args -import pyomo.opt -from pyomo.environ import * from pyomo.scripting.util import cleanup import pyomo.scripting.pyomo_main as main diff --git a/pyomo/environ/__init__.py b/pyomo/environ/__init__.py index 062841abc43..1ef0c6563ad 100644 --- a/pyomo/environ/__init__.py +++ b/pyomo/environ/__init__.py @@ -65,8 +65,8 @@ def _import_packages(): # # Import required packages # - for name in _packages: - pname = name+'.plugins' + for _package in _packages: + pname = _package + '.plugins' try: _do_import(pname) except ImportError: @@ -87,8 +87,8 @@ def _import_packages(): # # Import optional packages # - for name in _optional_packages: - pname = name+'.plugins' + for _package in _optional_packages: + pname = _package + '.plugins' try: _do_import(pname) except ImportError: @@ -102,10 +102,87 @@ def _import_packages(): # Expose the symbols from pyomo.core # from pyomo.dataportal import DataPortal -from pyomo.core import * +import pyomo.core.kernel +import pyomo.core.base._pyomo +from pyomo.common.collections import ComponentMap +import pyomo.core.base.indexed_component +import pyomo.core.base._pyomo +from six import iterkeys, iteritems +import pyomo.core.base.util +from pyomo.core import expr, base, beta, kernel, plugins, preprocess +from pyomo.core.base import util +import pyomo.core.preprocess + +from pyomo.core import (numvalue, numeric_expr, boolean_value, + current, symbol_map, sympy_tools, + taylor_series, visitor, expr_common, expr_errors, + calculus, native_types, + linear_expression, nonlinear_expression, + land, lor, equivalent, exactly, + atleast, atmost, implies, lnot, + xor, inequality, log, log10, sin, cos, tan, cosh, + sinh, tanh, asin, acos, atan, exp, sqrt, asinh, acosh, + atanh, ceil, floor, Expr_if, differentiate, + taylor_series_expansion, SymbolMap, PyomoObject, + nonpyomo_leaf_types, native_numeric_types, + value, is_constant, is_fixed, is_variable_type, + is_potentially_variable, polynomial_degree, + NumericValue, ZeroConstant, as_boolean, BooleanConstant, + BooleanValue, native_logical_values, minimize, + maximize, PyomoOptions, Expression, CuidLabeler, + CounterLabeler, NumericLabeler, + CNameLabeler, TextLabeler, + AlphaNumericTextLabeler, NameLabeler, ShortNameLabeler, + name, Component, ComponentUID, BuildAction, + BuildCheck, Set, SetOf, simple_set_rule, RangeSet, + Param, Var, VarList, SimpleVar, + BooleanVar, BooleanVarList, SimpleBooleanVar, + logical_expr, simple_constraint_rule, + simple_constraintlist_rule, ConstraintList, + Constraint, LogicalConstraint, + LogicalConstraintList, simple_objective_rule, + simple_objectivelist_rule, Objective, + ObjectiveList, Connector, SOSConstraint, + Piecewise, active_export_suffix_generator, + active_import_suffix_generator, Suffix, + ExternalFunction, symbol_map_from_instance, + Reference, Reals, PositiveReals, NonPositiveReals, + NegativeReals, NonNegativeReals, Integers, + PositiveIntegers, NonPositiveIntegers, + NegativeIntegers, NonNegativeIntegers, + Boolean, Binary, Any, AnyWithNone, EmptySet, + UnitInterval, PercentFraction, RealInterval, + IntegerInterval, display, SortComponents, + TraversalStrategy, Block, SimpleBlock, + active_components, components, + active_components_data, components_data, + global_option, Model, ConcreteModel, + AbstractModel, pyomo_callback, + IPyomoExpression, ExpressionFactory, + ExpressionRegistration, IPyomoPresolver, + IPyomoPresolveAction, + IParamRepresentation, + ParamRepresentationFactory, + IPyomoScriptPreprocess, + IPyomoScriptCreateModel, + IPyomoScriptCreateDataPortal, + IPyomoScriptModifyInstance, + IPyomoScriptPrintModel, + IPyomoScriptPrintInstance, + IPyomoScriptSaveInstance, + IPyomoScriptPrintResults, + IPyomoScriptSaveResults, + IPyomoScriptPostprocess, + ModelComponentFactory, Transformation, + TransformationFactory, instance2dat, + set_options, RealSet, IntegerSet, BooleanSet, + prod, quicksum, sum_product, dot_product, + summation, sequence) + from pyomo.opt import ( SolverFactory, SolverManagerFactory, UnknownSolver, TerminationCondition, SolverStatus, check_optimal_termination, assert_optimal_termination ) from pyomo.core.base.units_container import units +from weakref import ref as weakref_ref diff --git a/pyomo/environ/tests/test_environ.py b/pyomo/environ/tests/test_environ.py index 59a96c413be..24901dc247d 100644 --- a/pyomo/environ/tests/test_environ.py +++ b/pyomo/environ/tests/test_environ.py @@ -122,7 +122,7 @@ def test_tpl_import_time(self): # of slow-loading TPLs can vary from platform to platform. ref = {'tempfile', 'logging', 'ctypes', 'ssl', 'argparse', 'textwrap', 'inspect', 'xml', 'platform', 'uuid', - 'optparse'} + 'optparse', 'filecmp'} # Non-standard-library TPLs that Pyomo will load unconditionally ref.add('six') ref.add('ply') diff --git a/pyomo/gdp/disjunct.py b/pyomo/gdp/disjunct.py index 8308c9b2971..8d8f884826b 100644 --- a/pyomo/gdp/disjunct.py +++ b/pyomo/gdp/disjunct.py @@ -24,7 +24,7 @@ from pyomo.core.base.component import ( ActiveComponent, ActiveComponentData, ComponentData ) -from pyomo.core.base.numvalue import native_types +from pyomo.core.base.numvalue import native_types, value from pyomo.core.base.block import _BlockData from pyomo.core.base.misc import apply_indexed_rule from pyomo.core.base.indexed_component import ActiveIndexedComponent diff --git a/pyomo/gdp/plugins/bigm.py b/pyomo/gdp/plugins/bigm.py index bb895fcdef3..79574f86f21 100644 --- a/pyomo/gdp/plugins/bigm.py +++ b/pyomo/gdp/plugins/bigm.py @@ -11,22 +11,18 @@ """Big-M Generalized Disjunctive Programming transformation module.""" import logging -import textwrap from pyomo.common.collections import ComponentMap, ComponentSet from pyomo.common.config import ConfigBlock, ConfigValue from pyomo.common.modeling import unique_component_name -from pyomo.common.deprecation import deprecation_warning +from pyomo.common.deprecation import deprecated from pyomo.contrib.fbbt.fbbt import compute_bounds_on_expr -from pyomo.contrib.fbbt.interval import inf from pyomo.core import ( Block, BooleanVar, Connector, Constraint, Param, Set, SetOf, Suffix, Var, Expression, SortComponents, TraversalStrategy, value, RangeSet, NonNegativeIntegers, LogicalConstraint, ) from pyomo.core.base.external import ExternalFunction from pyomo.core.base import Transformation, TransformationFactory, Reference -from pyomo.core.base.component import ComponentUID, ActiveComponent -from pyomo.core.base.PyomoModel import ConcreteModel, AbstractModel import pyomo.core.expr.current as EXPR from pyomo.gdp import Disjunct, Disjunction, GDP_Error from pyomo.gdp.util import ( @@ -172,7 +168,6 @@ def _get_bigm_suffix_list(self, block, stopping_block=None): # SimpleBlocks. Though it is possible at this point to stick them # on whatever components you want, we won't pick them up. suffix_list = [] - orig_block = block # go searching above block in the tree, stop when we hit stopping_block # (This is so that we can search on each Disjunct once, but get any @@ -440,11 +435,15 @@ def _transform_disjunct(self, obj, transBlock, bigM, arg_list, suffix_list): relaxedDisjuncts = transBlock.relaxedDisjuncts relaxationBlock = relaxedDisjuncts[len(relaxedDisjuncts)] # we will keep a map of constraints (hashable, ha!) to a tuple to - # indicate where their m value came from, either (arg dict, key) if it - # came from args, (Suffix, key) if it came from Suffixes, or (M_lower, - # M_upper) if we calcualted it ourselves. I am keeping it here because I - # want it to move with the disjunct transformation blocks in the case of - # nested constraints, to make it easier to query. + # indicate what their M value is and where it came from, of the form: + # ((lower_value, lower_source, lower_key), (upper_value, upper_source, + # upper_key)), where the first tuple is the information for the lower M, + # the second tuple is the info for the upper M, source is the Suffix or + # argument dictionary and None if the value was calculated, and key is + # the key in the Suffix or argument dictionary, and None if it was + # calculated. (Note that it is possible the lower or upper is + # user-specified and the other is not, hence the need to store + # information for both.) relaxationBlock.bigm_src = {} relaxationBlock.localVarReferences = Block() obj._transformation_block = weakref_ref(relaxationBlock) @@ -574,6 +573,27 @@ def _get_constraint_map_dict(self, transBlock): 'transformedConstraints': ComponentMap()} return transBlock._constraintMap + def _convert_M_to_tuple(self, M, constraint_name): + if not isinstance(M, (tuple, list)): + if M is None: + M = (None, None) + else: + try: + M = (-M, M) + except: + logger.error("Error converting scalar M-value %s " + "to (-M,M). Is %s not a numeric type?" + % (M, type(M))) + raise + if len(M) != 2: + raise GDP_Error("Big-M %s for constraint %s is not of " + "length two. " + "Expected either a single value or " + "tuple or list of length two for M." + % (str(M), constraint_name)) + + return M + def _transform_constraint(self, obj, disjunct, bigMargs, arg_list, disjunct_suffix_list): # add constraint to the transformation block, we'll transform it there. @@ -607,10 +627,15 @@ def _transform_constraint(self, obj, disjunct, bigMargs, arg_list, if not c.active: continue + lower = (None, None, None) + upper = (None, None, None) + # first, we see if an M value was specified in the arguments. # (This returns None if not) - M = self._get_M_from_args(c, bigMargs, arg_list, bigm_src) - + lower, upper = self._get_M_from_args(c, bigMargs, arg_list, lower, + upper) + M = (lower[0], upper[0]) + if __debug__ and logger.isEnabledFor(logging.DEBUG): _name = obj.getname( fully_qualified=True, name_buffer=NAME_BUFFER) @@ -618,14 +643,17 @@ def _transform_constraint(self, obj, disjunct, bigMargs, arg_list, "from the BigM argument is %s." % (cons_name, str(M))) - # if we didn't get something from args, try suffixes: - if M is None: + # if we didn't get something we need from args, try suffixes: + if (M[0] is None and c.lower is not None) or \ + (M[1] is None and c.upper is not None): # first get anything parent to c but below disjunct suffix_list = self._get_bigm_suffix_list(c.parent_block(), stopping_block=disjunct) # prepend that to what we already collected for the disjunct. suffix_list.extend(disjunct_suffix_list) - M = self._get_M_from_suffixes(c, suffix_list, bigm_src) + lower, upper = self._update_M_from_suffixes(c, suffix_list, + lower, upper) + M = (lower[0], upper[0]) if __debug__ and logger.isEnabledFor(logging.DEBUG): _name = obj.getname( @@ -634,30 +662,12 @@ def _transform_constraint(self, obj, disjunct, bigMargs, arg_list, "after checking suffixes is %s." % (cons_name, str(M))) - if not isinstance(M, (tuple, list)): - if M is None: - M = (None, None) - else: - try: - M = (-M, M) - except: - logger.error("Error converting scalar M-value %s " - "to (-M,M). Is %s not a numeric type?" - % (M, type(M))) - raise - if len(M) != 2: - raise GDP_Error("Big-M %s for constraint %s is not of " - "length two. " - "Expected either a single value or " - "tuple or list of length two for M." - % (str(M), name)) - if c.lower is not None and M[0] is None: M = (self._estimate_M(c.body, name)[0] - c.lower, M[1]) - bigm_src[c] = M + lower = (M[0], None, None) if c.upper is not None and M[1] is None: M = (M[0], self._estimate_M(c.body, name)[1] - c.upper) - bigm_src[c] = M + upper = (M[1], None, None) if __debug__ and logger.isEnabledFor(logging.DEBUG): _name = obj.getname( @@ -666,6 +676,9 @@ def _transform_constraint(self, obj, disjunct, bigMargs, arg_list, "after estimating (if needed) is %s." % (cons_name, str(M))) + # save the source information + bigm_src[c] = (lower, upper) + # Handle indices for both SimpleConstraint and IndexedConstraint if i.__class__ is tuple: i_lb = i + ('lb',) @@ -704,56 +717,128 @@ def _transform_constraint(self, obj, disjunct, bigMargs, arg_list, # deactivate because we relaxed c.deactivate() - def _get_M_from_args(self, constraint, bigMargs, arg_list, bigm_src): + def _process_M_value(self, m, lower, upper, need_lower, need_upper, src, + key, constraint_name, from_args=False): + m = self._convert_M_to_tuple(m, constraint_name) + if need_lower and m[0] is not None: + if from_args: + self.used_args[key] = m + lower = (m[0], src, key) + need_lower = False + if need_upper and m[1] is not None: + if from_args: + self.used_args[key] = m + upper = (m[1], src, key) + need_upper = False + return lower, upper, need_lower, need_upper + + def _get_M_from_args(self, constraint, bigMargs, arg_list, lower, upper): # check args: we first look in the keys for constraint and # constraintdata. In the absence of those, we traverse up the blocks, # and as a last resort check for a value for None if bigMargs is None: - return None + return (lower, upper) + + # since we check for args first, we know lower[0] and upper[0] are both + # None + need_lower = constraint.lower is not None + need_upper = constraint.upper is not None + constraint_name = constraint.getname(fully_qualified=True, + name_buffer=NAME_BUFFER) # check for the constraint itself and its container parent = constraint.parent_component() if constraint in bigMargs: m = bigMargs[constraint] - self.used_args[constraint] = m - bigm_src[constraint] = (bigMargs, constraint) - return m + (lower, upper, + need_lower, need_upper) = self._process_M_value(m, lower, upper, + need_lower, + need_upper, + bigMargs, + constraint, + constraint_name, + from_args=True) + if not need_lower and not need_upper: + return lower, upper elif parent in bigMargs: m = bigMargs[parent] - self.used_args[parent] = m - bigm_src[constraint] = (bigMargs, parent) - return m + (lower, upper, + need_lower, need_upper) = self._process_M_value(m, lower, upper, + need_lower, + need_upper, + bigMargs, parent, + constraint_name, + from_args=True) + if not need_lower and not need_upper: + return lower, upper # use the precomputed traversal up the blocks for arg in arg_list: for block, val in iteritems(arg): - self.used_args[block] = val - bigm_src[constraint] = (bigMargs, block) - return val + (lower, upper, + need_lower, need_upper) = self._process_M_value(val, lower, + upper, + need_lower, + need_upper, + bigMargs, + block, + constraint_name, + from_args=True) + if not need_lower and not need_upper: + return lower, upper # last check for value for None! if None in bigMargs: m = bigMargs[None] - self.used_args[None] = m - bigm_src[constraint] = (bigMargs, None) - return m - return None - - def _get_M_from_suffixes(self, constraint, suffix_list, bigm_src): + (lower, upper, + need_lower, need_upper) = self._process_M_value(m, lower, upper, + need_lower, + need_upper, + bigMargs, None, + constraint_name, + from_args=True) + if not need_lower and not need_upper: + return lower, upper + + return lower, upper + + def _update_M_from_suffixes(self, constraint, suffix_list, lower, upper): + # It's possible we found half the answer in args, but we are still + # looking for half the answer. + need_lower = constraint.lower is not None and lower[0] is None + need_upper = constraint.upper is not None and upper[0] is None + constraint_name = constraint.getname(fully_qualified=True, + name_buffer=NAME_BUFFER) M = None # first we check if the constraint or its parent is a key in any of the # suffix lists for bigm in suffix_list: if constraint in bigm: M = bigm[constraint] - bigm_src[constraint] = (bigm, constraint) - break + (lower, upper, + need_lower, need_upper) = self._process_M_value(M, lower, + upper, + need_lower, + need_upper, + bigm, + constraint, + constraint_name) + if not need_lower and not need_upper: + return lower, upper # if c is indexed, check for the parent component if constraint.parent_component() in bigm: - M = bigm[constraint.parent_component()] - bigm_src[constraint] = (bigm, constraint.parent_component()) - break + parent = constraint.parent_component() + M = bigm[parent] + (lower, upper, + need_lower, need_upper) = self._process_M_value(M, lower, + upper, + need_lower, + need_upper, + bigm, parent, + constraint_name) + if not need_lower and not need_upper: + return lower, upper # if we didn't get an M that way, traverse upwards through the blocks # and see if None has a value on any of them. @@ -761,9 +846,15 @@ def _get_M_from_suffixes(self, constraint, suffix_list, bigm_src): for bigm in suffix_list: if None in bigm: M = bigm[None] - bigm_src[constraint] = (bigm, None) - break - return M + (lower, upper, + need_lower, + need_upper) = self._process_M_value(M, lower, upper, + need_lower, need_upper, + bigm, None, + constraint_name) + if not need_lower and not need_upper: + return lower, upper + return lower, upper def _estimate_M(self, expr, name): # If there are fixed variables here, unfix them for this calculation, @@ -839,22 +930,52 @@ def get_src_constraint(self, transformedConstraint): def get_transformed_constraints(self, srcConstraint): return get_transformed_constraints(srcConstraint) + @deprecated("The get_m_value_src function is deprecated. Use " + "the get_M_value_src function is you need source " + "information or the get_M_value function if you " + "only need values.", version='5.7.1') def get_m_value_src(self, constraint): + transBlock = _get_constraint_transBlock(constraint) + ((lower_val, lower_source, lower_key), + (upper_val, upper_source, upper_key)) = transBlock.bigm_src[constraint] + + if constraint.lower is not None and constraint.upper is not None and \ + (not lower_source is upper_source or not lower_key is upper_key): + raise GDP_Error("This is why this method is deprecated: The lower " + "and upper M values for constraint %s came from " + "different sources, please use the get_M_value_src " + "method." % constraint.name) + # if source and key are equal for the two, this is representable in the + # old format. + if constraint.lower is not None and lower_source is not None: + return (lower_source, lower_key) + if constraint.upper is not None and upper_source is not None: + return (upper_source, upper_key) + # else it was calculated: + return (lower_val, upper_val) + + def get_M_value_src(self, constraint): """Return a tuple indicating how the M value used to transform constraint was specified. (In particular, this can be used to verify which BigM Suffixes were actually necessary to the transformation.) - If the M value came from an arg, returns (bigm_arg_dict, key), where - bigm_arg_dict is the dictionary itself and key is the key in that - dictionary which gave us the M value. + Return is of the form: ((lower_M_val, lower_M_source, lower_M_key), + (upper_M_val, upper_M_source, upper_M_key)) + + If the constraint does not have a lower bound (or an upper bound), + the first (second) element will be (None, None, None). Note that if + a constraint is of the form a <= expr <= b or is an equality constraint, + it is not necessarily true that the source of lower_M and upper_M + are the same. - If the M value came from a Suffix, returns (suffix, key) where suffix - is the BigM suffix used and key is the key in that Suffix. + If the M value came from an arg, source is the dictionary itself and + key is the key in that dictionary which gave us the M value. - If the transformation calculated the value, returns (M_lower, M_upper), - where M_lower is the float we calculated for the lower bound constraint - and M_upper is the value calculated for the upper bound constraint. + If the M value came from a Suffix, source is the BigM suffix used and + key is the key in that Suffix. + + If the transformation calculated the value, both source and key are None. Parameters ---------- @@ -865,3 +986,19 @@ def get_m_value_src(self, constraint): # This is a KeyError if it fails, but it is also my fault if it # fails... (That is, it's a bug in the mapping.) return transBlock.bigm_src[constraint] + + def get_M_value(self, constraint): + """Returns the M values used to transform constraint. Return is a tuple: + (lower_M_value, upper_M_value). Either can be None if constraint does + not have a lower or upper bound, respectively. + + Parameters + ---------- + constraint: Constraint, which must be in the subtree of a transformed + Disjunct + """ + transBlock = _get_constraint_transBlock(constraint) + # This is a KeyError if it fails, but it is also my fault if it + # fails... (That is, it's a bug in the mapping.) + lower, upper = transBlock.bigm_src[constraint] + return (lower[0], upper[0]) diff --git a/pyomo/gdp/plugins/bilinear.py b/pyomo/gdp/plugins/bilinear.py index 08fabe84e2c..82a96fb503f 100644 --- a/pyomo/gdp/plugins/bilinear.py +++ b/pyomo/gdp/plugins/bilinear.py @@ -9,12 +9,9 @@ # ___________________________________________________________________________ import logging -from six import iteritems -from pyomo.core.expr.current import ProductExpression -from pyomo.core import * -from pyomo.core.base.var import _VarData -from pyomo.gdp import * +from pyomo.core import TransformationFactory, Transformation, Block, VarList, Set, SortComponents, Objective, Constraint +from pyomo.gdp import Disjunct, Disjunction from pyomo.repn import generate_standard_repn logger = logging.getLogger('pyomo.gdp') @@ -27,7 +24,6 @@ def __init__(self): super(Bilinear_Transformation, self).__init__() def _apply_to(self, instance, **kwds): - options = kwds.pop('options', {}) # TODO: This data should be stored differently. We cannot nest this transformation with itself if getattr(instance, 'bilinear_data_', None) is None: instance.bilinear_data_ = Block() diff --git a/pyomo/gdp/plugins/chull.py b/pyomo/gdp/plugins/chull.py index ab9393900fc..6901a2ead52 100644 --- a/pyomo/gdp/plugins/chull.py +++ b/pyomo/gdp/plugins/chull.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.common.deprecation import deprecation_warning deprecation_warning( 'The pyomo.gdp.plugins.chull module is deprecated. ' diff --git a/pyomo/gdp/plugins/cuttingplane.py b/pyomo/gdp/plugins/cuttingplane.py index d650bd471c5..f8e979c0c2f 100644 --- a/pyomo/gdp/plugins/cuttingplane.py +++ b/pyomo/gdp/plugins/cuttingplane.py @@ -23,16 +23,16 @@ from pyomo.common.modeling import unique_component_name from pyomo.core import ( Any, Block, Constraint, Objective, Param, Var, SortComponents, - Transformation, TransformationFactory, value, TransformationFactory + Transformation, TransformationFactory, value ) from pyomo.opt import SolverFactory -from pyomo.gdp import Disjunct, Disjunction, GDP_Error +from pyomo.gdp import Disjunct, GDP_Error from pyomo.gdp.util import ( - verify_successful_solve, NORMAL, INFEASIBLE, NONOPTIMAL + verify_successful_solve, NORMAL ) -from six import iterkeys, itervalues +from six import iterkeys import math import logging diff --git a/pyomo/gdp/plugins/gdp_var_mover.py b/pyomo/gdp/plugins/gdp_var_mover.py index fd9c03d1b8d..39d1f2b5e35 100644 --- a/pyomo/gdp/plugins/gdp_var_mover.py +++ b/pyomo/gdp/plugins/gdp_var_mover.py @@ -15,8 +15,6 @@ """ import logging -import textwrap -from pyomo.common.plugin import alias from pyomo.core.base import Transformation, Block, Constraint from pyomo.gdp import Disjunct, GDP_Error, Disjunction from pyomo.core import TraversalStrategy, TransformationFactory diff --git a/pyomo/gdp/tests/common_tests.py b/pyomo/gdp/tests/common_tests.py index acf30dd155c..7823cc52853 100644 --- a/pyomo/gdp/tests/common_tests.py +++ b/pyomo/gdp/tests/common_tests.py @@ -1,9 +1,20 @@ -from pyomo.environ import * -from pyomo.gdp import * -from pyomo.core.base import constraint +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + + +from pyomo.environ import TransformationFactory, ConcreteModel, Constraint, Var, Objective, Block, Any, RangeSet, Expression, value +from pyomo.gdp import Disjunct, Disjunction, GDP_Error +from pyomo.core.base import constraint, ComponentUID from pyomo.repn import generate_standard_repn import pyomo.gdp.tests.models as models -from six import StringIO +from six import StringIO, iteritems import random # utitility functions diff --git a/pyomo/gdp/tests/test_bigm.py b/pyomo/gdp/tests/test_bigm.py index 0b70196019d..e493ddbcc1d 100644 --- a/pyomo/gdp/tests/test_bigm.py +++ b/pyomo/gdp/tests/test_bigm.py @@ -10,10 +10,9 @@ import pyutilib.th as unittest -from pyomo.environ import * -from pyomo.gdp import * +from pyomo.environ import TransformationFactory, Block, Set, Constraint, ComponentMap, Suffix, ConcreteModel, Var, Any, value +from pyomo.gdp import Disjunct, Disjunction, GDP_Error from pyomo.core.base import constraint, _ConstraintData -from pyomo.core.expr import current as EXPR from pyomo.repn import generate_standard_repn from pyomo.common.log import LoggingIntercept import logging @@ -22,9 +21,8 @@ import pyomo.gdp.tests.common_tests as ct import random -import sys -from six import iteritems, StringIO +from six import StringIO class CommonTests: def diff_apply_to_and_create_using(self, model): @@ -779,18 +777,53 @@ def test_suffix_M_onBlock(self): self.checkMs(m, -34, 34, 34, -3, 1.5) # check the source of the values - (src, key) = bigm.get_m_value_src(m.simpledisj.c) - self.assertEqual(src, -3) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.simpledisj2.c) - self.assertIsNone(src) - self.assertEqual(key, 1.5) - (src, key) = bigm.get_m_value_src(m.b.disjunct[0].c) - self.assertIs(src, m.b.BigM) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.b.disjunct[1].c) - self.assertIs(src, m.b.BigM) - self.assertIsNone(key) + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj.c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -3) + self.assertIsNone(u_val) + (l_val, u_val) = bigm.get_M_value(m.simpledisj.c) + self.assertEqual(l_val, -3) + self.assertIsNone(u_val) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj2.c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 1.5) + (l_val, u_val) = bigm.get_M_value(m.simpledisj2.c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 1.5) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[0].c) + self.assertIs(l_src, m.b.BigM) + self.assertIs(u_src, m.b.BigM) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -34) + self.assertEqual(u_val, 34) + l_val, u_val = bigm.get_M_value(m.b.disjunct[0].c) + self.assertEqual(l_val, -34) + self.assertEqual(u_val, 34) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[1].c) + self.assertIsNone(l_src) + self.assertIs(u_src, m.b.BigM) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 34) + l_val, u_val = bigm.get_M_value(m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 34) def test_block_M_arg(self): m = models.makeTwoTermDisjOnBlock() @@ -801,18 +834,53 @@ def test_block_M_arg(self): self.checkMs(m, -100, 100, 13, -3, 1.5) # check the source of the values - (src, key) = bigm.get_m_value_src(m.simpledisj.c) - self.assertEqual(src, -3) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.simpledisj2.c) - self.assertIsNone(src) - self.assertEqual(key, 1.5) - (src, key) = bigm.get_m_value_src(m.b.disjunct[0].c) - self.assertIs(src, bigms) - self.assertIs(key, m.b) - (src, key) = bigm.get_m_value_src(m.b.disjunct[1].c) - self.assertIs(src, bigms) - self.assertIs(key, m.b.disjunct[1].c) + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj.c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -3) + self.assertIsNone(u_val) + (l_val, u_val) = bigm.get_M_value(m.simpledisj.c) + self.assertEqual(l_val, -3) + self.assertIsNone(u_val) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj2.c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 1.5) + (l_val, u_val) = bigm.get_M_value(m.simpledisj2.c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 1.5) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[0].c) + self.assertIs(l_src, bigms) + self.assertIs(u_src, bigms) + self.assertIs(l_key, m.b) + self.assertIs(u_key, m.b) + self.assertEqual(l_val, -100) + self.assertEqual(u_val, 100) + l_val, u_val = bigm.get_M_value(m.b.disjunct[0].c) + self.assertEqual(l_val, -100) + self.assertEqual(u_val, 100) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[1].c) + self.assertIsNone(l_src) + self.assertIs(u_src, bigms) + self.assertIsNone(l_key) + self.assertIs(u_key, m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 13) + l_val, u_val = bigm.get_M_value(m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 13) def test_disjunct_M_arg(self): m = models.makeTwoTermDisjOnBlock() @@ -823,40 +891,111 @@ def test_disjunct_M_arg(self): self.checkMs(m, -100, 100, 13, -3, 1.5) # check the source of the values - (src, key) = bigm.get_m_value_src(m.simpledisj.c) - self.assertEqual(src, -3) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.simpledisj2.c) - self.assertIsNone(src) - self.assertEqual(key, 1.5) - (src, key) = bigm.get_m_value_src(m.b.disjunct[0].c) - self.assertIs(src, bigms) - self.assertIs(key, m.b) - (src, key) = bigm.get_m_value_src(m.b.disjunct[1].c) - self.assertIs(src, bigms) - self.assertIs(key, m.b.disjunct[1]) + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj.c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -3) + self.assertIsNone(u_val) + (l_val, u_val) = bigm.get_M_value(m.simpledisj.c) + self.assertEqual(l_val, -3) + self.assertIsNone(u_val) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj2.c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 1.5) + (l_val, u_val) = bigm.get_M_value(m.simpledisj2.c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 1.5) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[0].c) + self.assertIs(l_src, bigms) + self.assertIs(u_src, bigms) + self.assertIs(l_key, m.b) + self.assertIs(u_key, m.b) + self.assertEqual(l_val, -100) + self.assertEqual(u_val, 100) + l_val, u_val = bigm.get_M_value(m.b.disjunct[0].c) + self.assertEqual(l_val, -100) + self.assertEqual(u_val, 100) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[1].c) + self.assertIsNone(l_src) + self.assertIs(u_src, bigms) + self.assertIsNone(l_key) + self.assertIs(u_key, m.b.disjunct[1]) + self.assertIsNone(l_val) + self.assertEqual(u_val, 13) + l_val, u_val = bigm.get_M_value(m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 13) def test_block_M_arg_with_default(self): m = models.makeTwoTermDisjOnBlock() m = models.add_disj_not_on_block(m) bigm = TransformationFactory('gdp.bigm') - bigms = {m.b: 100, m.b.disjunct[1].c: 13, None: 34} + bigms = {m.b: 100, m.b.disjunct[1].c: 13, + m.b.disjunct[0].c: (None, 50), None: 34} bigm.apply_to(m, bigM=bigms) - self.checkMs(m, -100, 100, 13, -34, 34) + self.checkMs(m, -100, 50, 13, -34, 34) # check the source of the values - (src, key) = bigm.get_m_value_src(m.simpledisj.c) - self.assertIs(src, bigms) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.simpledisj2.c) - self.assertIs(src, bigms) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.b.disjunct[0].c) - self.assertIs(src, bigms) - self.assertIs(key, m.b) - (src, key) = bigm.get_m_value_src(m.b.disjunct[1].c) - self.assertIs(src, bigms) - self.assertIs(key, m.b.disjunct[1].c) + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj.c) + self.assertIs(l_src, bigms) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -34) + self.assertIsNone(u_val) + l_val, u_val = bigm.get_M_value(m.simpledisj.c) + self.assertEqual(l_val, -34) + self.assertIsNone(u_val) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj2.c) + self.assertIsNone(l_src) + self.assertIs(u_src, bigms) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 34) + l_val, u_val = bigm.get_M_value(m.simpledisj2.c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 34) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[0].c) + self.assertIs(l_src, bigms) + self.assertIs(u_src, bigms) + self.assertIs(l_key, m.b) + self.assertIs(u_key, m.b.disjunct[0].c) + self.assertEqual(l_val, -100) + self.assertEqual(u_val, 50) + l_val, u_val = bigm.get_M_value(m.b.disjunct[0].c) + self.assertEqual(l_val, -100) + self.assertEqual(u_val, 50) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[1].c) + self.assertIsNone(l_src) + self.assertIs(u_src, bigms) + self.assertIsNone(l_key) + self.assertIs(u_key, m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 13) + l_val, u_val = bigm.get_M_value(m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 13) def test_model_M_arg(self): m = models.makeTwoTermDisjOnBlock() @@ -955,18 +1094,53 @@ def test_suffix_M_simple_disj(self): self.checkMs(m, -20, 20, 20, -45, 20) # check source of the m values - (src, key) = bigm.get_m_value_src(m.simpledisj.c) - self.assertIs(src, m.simpledisj.BigM) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.simpledisj2.c) - self.assertIs(src, m.BigM) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.b.disjunct[0].c) - self.assertIs(src, m.BigM) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.b.disjunct[1].c) - self.assertIs(src, m.BigM) - self.assertIsNone(key) + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj.c) + self.assertIs(l_src, m.simpledisj.BigM) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -45) + self.assertIsNone(u_val) + l_val, u_val = bigm.get_M_value(m.simpledisj.c) + self.assertEqual(l_val, -45) + self.assertIsNone(u_val) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj2.c) + self.assertIsNone(l_src) + self.assertIs(u_src, m.BigM) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) + l_val, u_val = bigm.get_M_value(m.simpledisj2.c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[0].c) + self.assertIs(l_src, m.BigM) + self.assertIs(u_src, m.BigM) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -20) + self.assertEqual(u_val, 20) + l_val, u_val = bigm.get_M_value(m.b.disjunct[0].c) + self.assertEqual(l_val, -20) + self.assertEqual(u_val, 20) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[1].c) + self.assertIsNone(l_src) + self.assertIs(u_src, m.BigM) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) + l_val, u_val = bigm.get_M_value(m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) def test_suffix_M_constraintKeyOnBlock(self): m = models.makeTwoTermDisjOnBlock() @@ -996,9 +1170,74 @@ def test_suffix_M_constraintKeyOnSimpleDisj(self): m.BigM = Suffix(direction=Suffix.LOCAL) m.BigM[None] = 20 + bigms = {m.b.disjunct[0].c: (-15, None)} bigm = TransformationFactory('gdp.bigm') - bigm.apply_to(m) - self.checkMs(m, -20, 20, 20, -87, 20) + + bigm.apply_to(m, bigM=bigms) + self.checkMs(m, -15, 20, 20, -87, 20) + + # check source of the m values + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj.c) + self.assertIs(l_src, m.simpledisj.BigM) + self.assertIsNone(u_src) + self.assertIs(l_key, m.simpledisj.c) + self.assertIsNone(u_key) + self.assertEqual(l_val, -87) + self.assertIsNone(u_val) + l_val, u_val = bigm.get_M_value(m.simpledisj.c) + self.assertEqual(l_val, -87) + self.assertIsNone(u_val) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.simpledisj2.c) + self.assertIsNone(l_src) + self.assertIs(u_src, m.BigM) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) + l_val, u_val = bigm.get_M_value(m.simpledisj2.c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[0].c) + self.assertIs(l_src, bigms) + self.assertIs(u_src, m.BigM) + self.assertIs(l_key, m.b.disjunct[0].c) + self.assertIsNone(u_key) + self.assertEqual(l_val, -15) + self.assertEqual(u_val, 20) + l_val, u_val = bigm.get_M_value(m.b.disjunct[0].c) + self.assertEqual(l_val, -15) + self.assertEqual(u_val, 20) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.b.disjunct[1].c) + self.assertIsNone(l_src) + self.assertIs(u_src, m.BigM) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) + l_val, u_val = bigm.get_M_value(m.b.disjunct[1].c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 20) + + def test_suffix_M_constraintKeyOnSimpleDisj_deprecated_m_src_method(self): + m = models.makeTwoTermDisjOnBlock() + m = models.add_disj_not_on_block(m) + m.simpledisj.BigM = Suffix(direction=Suffix.LOCAL) + m.simpledisj.BigM[None] = 45 + m.simpledisj.BigM[m.simpledisj.c] = 87 + m.BigM = Suffix(direction=Suffix.LOCAL) + m.BigM[None] = 20 + + bigms = {m.b.disjunct[0].c: (-15, None)} + bigm = TransformationFactory('gdp.bigm') + + bigm.apply_to(m, bigM=bigms) # check source of the m values (src, key) = bigm.get_m_value_src(m.simpledisj.c) @@ -1007,13 +1246,40 @@ def test_suffix_M_constraintKeyOnSimpleDisj(self): (src, key) = bigm.get_m_value_src(m.simpledisj2.c) self.assertIs(src, m.BigM) self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.b.disjunct[0].c) - self.assertIs(src, m.BigM) - self.assertIsNone(key) + self.assertRaisesRegexp( + GDP_Error, + "This is why this method is deprecated: The lower " + "and upper M values for constraint b.disjunct\[0\].c " + "came from different sources, please use the " + "get_M_value_src method.", + bigm.get_m_value_src, + m.b.disjunct[0].c) (src, key) = bigm.get_m_value_src(m.b.disjunct[1].c) self.assertIs(src, m.BigM) self.assertIsNone(key) + def test_disjunct_M_arg_deprecated_m_src_method(self): + m = models.makeTwoTermDisjOnBlock() + m = models.add_disj_not_on_block(m) + bigm = TransformationFactory('gdp.bigm') + bigms = {m.b: 100, m.b.disjunct[1]: 13} + bigm.apply_to(m, bigM=bigms) + self.checkMs(m, -100, 100, 13, -3, 1.5) + + # check the source of the values + (src, key) = bigm.get_m_value_src(m.simpledisj.c) + self.assertEqual(src, -3) + self.assertIsNone(key) + (src, key) = bigm.get_m_value_src(m.simpledisj2.c) + self.assertIsNone(src) + self.assertEqual(key, 1.5) + (src, key) = bigm.get_m_value_src(m.b.disjunct[0].c) + self.assertIs(src, bigms) + self.assertIs(key, m.b) + (src, key) = bigm.get_m_value_src(m.b.disjunct[1].c) + self.assertIs(src, bigms) + self.assertIs(key, m.b.disjunct[1]) + def test_block_targets_inactive(self): ct.check_block_targets_inactive(self, 'bigm') @@ -1506,24 +1772,63 @@ def test_m_value_mappings(self): bigms = {m.disjunct[1].innerdisjunct[0]: 89} bigm.apply_to(m, bigM=bigms) - (src, key) = bigm.get_m_value_src(m.disjunct[1].innerdisjunct[0].c) - self.assertIs(src, bigms) - self.assertIs(key, m.disjunct[1].innerdisjunct[0]) - (src, key) = bigm.get_m_value_src(m.disjunct[1].innerdisjunct[1].c) - self.assertEqual(src, -5) - self.assertIsNone(key) - (src, key) = bigm.get_m_value_src(m.disjunct[0].c) - self.assertEqual(src, -11) - self.assertEqual(key, 7) - (src, key) = bigm.get_m_value_src(m.disjunct[1].c) - self.assertIsNone(src) - self.assertEqual(key, 21) - (src, key) = bigm.get_m_value_src(m.simpledisjunct.innerdisjunct0.c) - self.assertIs(src, m.simpledisjunct.BigM) - self.assertIs(key, m.simpledisjunct.innerdisjunct0.c) - (src, key) = bigm.get_m_value_src(m.simpledisjunct.innerdisjunct1.c) - self.assertIs(src, m.simpledisjunct.BigM) - self.assertIsNone(key) + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src( + m.disjunct[1].innerdisjunct[0].c) + self.assertIs(l_src, bigms) + self.assertIs(u_src, bigms) + self.assertIs(l_key, m.disjunct[1].innerdisjunct[0]) + self.assertIs(u_key, m.disjunct[1].innerdisjunct[0]) + self.assertEqual(l_val, -89) + self.assertEqual(u_val, 89) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src( + m.disjunct[1].innerdisjunct[1].c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -5) + self.assertIsNone(u_val) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.disjunct[0].c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -11) + self.assertEqual(u_val, 7) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src(m.disjunct[1].c) + self.assertIsNone(l_src) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertIsNone(l_val) + self.assertEqual(u_val, 21) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src( + m.simpledisjunct.innerdisjunct0.c) + self.assertIsNone(l_src) + self.assertIs(u_src, m.simpledisjunct.BigM) + self.assertIsNone(l_key) + self.assertIs(u_key, m.simpledisjunct.innerdisjunct0.c) + self.assertIsNone(l_val) + self.assertEqual(u_val, 42) + + ((l_val, l_src, l_key), + (u_val, u_src, u_key)) = bigm.get_M_value_src( + m.simpledisjunct.innerdisjunct1.c) + self.assertIs(l_src, m.simpledisjunct.BigM) + self.assertIsNone(u_src) + self.assertIsNone(l_key) + self.assertIsNone(u_key) + self.assertEqual(l_val, -58) + self.assertIsNone(u_val) # many of the transformed constraints look like this, so can call this # function to test them. diff --git a/pyomo/gdp/tests/test_cuttingplane.py b/pyomo/gdp/tests/test_cuttingplane.py index e8d4b449ad7..94ccbaf4ea7 100644 --- a/pyomo/gdp/tests/test_cuttingplane.py +++ b/pyomo/gdp/tests/test_cuttingplane.py @@ -10,17 +10,14 @@ import pyutilib.th as unittest -from pyomo.environ import * -from pyomo.gdp import * +from pyomo.environ import ConcreteModel, Var, Constraint, Objective, Block, TransformationFactory +from pyomo.gdp import Disjunct, Disjunction, GDP_Error import pyomo.opt from pyomo.repn import generate_standard_repn -import random from six import StringIO -from nose.tools import set_trace - solvers = pyomo.opt.check_available_solvers('ipopt') # TODO: diff --git a/pyomo/gdp/tests/test_disjunct.py b/pyomo/gdp/tests/test_disjunct.py index bf0ed065b73..332a68b5c69 100644 --- a/pyomo/gdp/tests/test_disjunct.py +++ b/pyomo/gdp/tests/test_disjunct.py @@ -10,10 +10,8 @@ import pyutilib.th as unittest -from pyomo.core import ConcreteModel, Var, Constraint, Block, \ - TransformationFactory -from pyomo.gdp import Disjunction, Disjunct, GDP_Error -import pyomo.gdp.plugins.bigm +from pyomo.core import ConcreteModel, Var, Constraint +from pyomo.gdp import Disjunction, Disjunct from six import iterkeys diff --git a/pyomo/gdp/tests/test_fix_disjuncts.py b/pyomo/gdp/tests/test_fix_disjuncts.py index eaa01fd58d1..dab265c4f3e 100644 --- a/pyomo/gdp/tests/test_fix_disjuncts.py +++ b/pyomo/gdp/tests/test_fix_disjuncts.py @@ -1,9 +1,20 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + + # -*- coding: UTF-8 -*- """Tests disjunct fixing.""" import pyutilib.th as unittest from pyomo.environ import (Block, Constraint, ConcreteModel, TransformationFactory, - RangeSet, NonNegativeReals) + NonNegativeReals) from pyomo.gdp import Disjunct, Disjunction, GDP_Error diff --git a/pyomo/gdp/tests/test_gdp.py b/pyomo/gdp/tests/test_gdp.py index 86c4b4b7c89..1bbc2dcee5d 100644 --- a/pyomo/gdp/tests/test_gdp.py +++ b/pyomo/gdp/tests/test_gdp.py @@ -28,8 +28,7 @@ from pyomo.common.dependencies import yaml, yaml_available, yaml_load_args import pyomo.opt -import pyomo.scripting.pyomo_main as main -from pyomo.environ import * +from pyomo.environ import SolverFactory, TransformationFactory from six import iteritems diff --git a/pyomo/gdp/tests/test_gdp_reclassification_error.py b/pyomo/gdp/tests/test_gdp_reclassification_error.py index 18a4419019a..3377f34fbce 100644 --- a/pyomo/gdp/tests/test_gdp_reclassification_error.py +++ b/pyomo/gdp/tests/test_gdp_reclassification_error.py @@ -1,6 +1,17 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe -import pyomo.gdp as gdp + +import pyomo.environ as pyo +from pyomo.gdp import Disjunct, Disjunction from pyomo.gdp.util import check_model_algebraic from pyomo.common.log import LoggingIntercept import logging @@ -9,13 +20,14 @@ class TestGDPReclassificationError(unittest.TestCase): def test_disjunct_not_in_disjunction(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.d1 = gdp.Disjunct() - m.d1.c = pe.Constraint(expr=m.x == 1) - m.d2 = gdp.Disjunct() - m.d2.c = pe.Constraint(expr=m.x == 0) - pe.TransformationFactory('gdp.bigm').apply_to(m) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.d1 = Disjunct() + m.d1.c = pyo.Constraint(expr=m.x == 1) + m.d2 = Disjunct() + m.d2.c = pyo.Constraint(expr=m.x == 0) + + pyo.TransformationFactory('gdp.bigm').apply_to(m) log = StringIO() with LoggingIntercept(log, 'pyomo.gdp', logging.WARNING): check_model_algebraic(m) @@ -23,15 +35,15 @@ def test_disjunct_not_in_disjunction(self): '.*not found in any Disjunctions.*') def test_disjunct_not_in_active_disjunction(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.d1 = gdp.Disjunct() - m.d1.c = pe.Constraint(expr=m.x == 1) - m.d2 = gdp.Disjunct() - m.d2.c = pe.Constraint(expr=m.x == 0) - m.disjunction = gdp.Disjunction(expr=[m.d1, m.d2]) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.d1 = Disjunct() + m.d1.c = pyo.Constraint(expr=m.x == 1) + m.d2 = Disjunct() + m.d2.c = pyo.Constraint(expr=m.x == 0) + m.disjunction = Disjunction(expr=[m.d1, m.d2]) m.disjunction.deactivate() - pe.TransformationFactory('gdp.bigm').apply_to(m) + pyo.TransformationFactory('gdp.bigm').apply_to(m) log = StringIO() with LoggingIntercept(log, 'pyomo.gdp', logging.WARNING): check_model_algebraic(m) diff --git a/pyomo/gdp/tests/test_hull.py b/pyomo/gdp/tests/test_hull.py index 4aa852754d8..8d05d62040d 100644 --- a/pyomo/gdp/tests/test_hull.py +++ b/pyomo/gdp/tests/test_hull.py @@ -12,11 +12,10 @@ from pyomo.common.log import LoggingIntercept import logging -from pyomo.environ import * -from pyomo.core.base import constraint +from pyomo.environ import TransformationFactory, Block, Set, Constraint, Var, RealSet, ComponentMap, value, log, ConcreteModel, Any, Suffix, SolverFactory from pyomo.repn import generate_standard_repn -from pyomo.gdp import * +from pyomo.gdp import Disjunct, Disjunction, GDP_Error import pyomo.gdp.tests.models as models import pyomo.gdp.tests.common_tests as ct @@ -25,7 +24,7 @@ 'glpk','cbc','gurobi','cplex') import random -from six import iteritems, iterkeys, StringIO +from six import iteritems, StringIO EPS = TransformationFactory('gdp.hull').CONFIG.EPS diff --git a/pyomo/gdp/tests/test_reclassify.py b/pyomo/gdp/tests/test_reclassify.py index 99c456e16da..7356d7918af 100644 --- a/pyomo/gdp/tests/test_reclassify.py +++ b/pyomo/gdp/tests/test_reclassify.py @@ -3,8 +3,6 @@ import pyutilib.th as unittest from pyomo.core import (Block, ConcreteModel, TransformationFactory, RangeSet, Constraint, Var) from pyomo.gdp import Disjunct, Disjunction, GDP_Error -from pyomo.gdp.plugins.gdp_var_mover import HACK_GDP_Disjunct_Reclassifier -from pyomo.gdp.plugins import bigm class TestDisjunctReclassify(unittest.TestCase): diff --git a/pyomo/gdp/util.py b/pyomo/gdp/util.py index 97544e51f76..2c9e6d098d0 100644 --- a/pyomo/gdp/util.py +++ b/pyomo/gdp/util.py @@ -8,20 +8,15 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from six import string_types - import pyomo.core.expr.current as EXPR -from pyomo.core.expr.numvalue import nonpyomo_leaf_types, native_numeric_types from pyomo.gdp import GDP_Error, Disjunction from pyomo.gdp.disjunct import _DisjunctData, Disjunct -from copy import deepcopy -from pyomo.core.base.component import _ComponentBase, ComponentUID +from pyomo.core.base.component import _ComponentBase + from pyomo.core import Block, TraversalStrategy from pyomo.opt import TerminationCondition, SolverStatus -from pyomo.common.deprecation import deprecation_warning from six import iterkeys -import sys from weakref import ref as weakref_ref import logging diff --git a/pyomo/kernel/__init__.py b/pyomo/kernel/__init__.py index d0b296b2458..9db31cead1b 100644 --- a/pyomo/kernel/__init__.py +++ b/pyomo/kernel/__init__.py @@ -23,7 +23,36 @@ # Define the modeling namespace # from pyomo.common.collections import ComponentMap, ComponentSet -from pyomo.core.expr import * +from pyomo.core.expr import ( + numvalue, numeric_expr, boolean_value, logical_expr, current, + calculus, symbol_map, expr_errors, visitor, sympy_tools, taylor_series, + expr_common, cnf_walker, template_expr +) + + +from pyomo.core.expr.numvalue import ( + value, is_constant, is_fixed, is_variable_type, + is_potentially_variable, NumericValue, ZeroConstant, + native_numeric_types, native_types, polynomial_degree, +) + +from pyomo.core.expr.boolean_value import BooleanValue + +from pyomo.core.expr.numeric_expr import linear_expression, nonlinear_expression + +from pyomo.core.expr.logical_expr import (land, lor, equivalent, exactly, + atleast, atmost, implies, lnot, + xor, inequality) + +from pyomo.core.expr.current import ( + log, log10, sin, cos, tan, cosh, sinh, tanh, + asin, acos, atan, exp, sqrt, asinh, acosh, + atanh, ceil, floor, + Expr_if, +) + +from pyomo.core.expr.calculus.derivatives import differentiate +from pyomo.core.expr.taylor_series import taylor_series_expansion import pyomo.core.kernel from pyomo.kernel.util import (generate_names, preorder_traversal, diff --git a/pyomo/kernel/util.py b/pyomo/kernel/util.py index f436896c937..c6e2405879d 100644 --- a/pyomo/kernel/util.py +++ b/pyomo/kernel/util.py @@ -20,7 +20,6 @@ _no_ctype, _convert_ctype, _convert_descend_into) -from pyomo.core.kernel.block import block import six diff --git a/pyomo/mpec/complementarity.py b/pyomo/mpec/complementarity.py index 72accd3be0e..0b2f0fa818a 100644 --- a/pyomo/mpec/complementarity.py +++ b/pyomo/mpec/complementarity.py @@ -8,18 +8,14 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys -import inspect -from six import iteritems, StringIO +from six import iteritems from collections import namedtuple from pyomo.common.timing import ConstructionTimer from pyomo.core.expr import current as EXPR -from pyomo.core.expr.numvalue import ZeroConstant, _sub, native_numeric_types, as_numeric -from pyomo.core import * +from pyomo.core.expr.numvalue import ZeroConstant, native_numeric_types, as_numeric +from pyomo.core import Constraint, Var, Block, Set from pyomo.core.base.plugin import ModelComponentFactory -from pyomo.core.base.numvalue import ZeroConstant, _sub -from pyomo.core.base.misc import apply_indexed_rule, tabular_writer from pyomo.core.base.block import _BlockData from pyomo.core.base.util import ( disable_methods, Initializer, IndexedCallInitializer, CountedCallInitializer diff --git a/pyomo/mpec/plugins/mpec2.py b/pyomo/mpec/plugins/mpec2.py index 84c69765ab0..40d8b3875be 100644 --- a/pyomo/mpec/plugins/mpec2.py +++ b/pyomo/mpec/plugins/mpec2.py @@ -33,7 +33,6 @@ def __init__(self): super(MPEC2_Transformation, self).__init__() def _apply_to(self, instance, **kwds): - options = kwds.pop('options', {}) # # Setup transformation data # diff --git a/pyomo/mpec/plugins/mpec3.py b/pyomo/mpec/plugins/mpec3.py index 92930fe7eb8..adef87ce994 100644 --- a/pyomo/mpec/plugins/mpec3.py +++ b/pyomo/mpec/plugins/mpec3.py @@ -12,7 +12,6 @@ from pyomo.core.base import (Transformation, TransformationFactory, - Constraint, Block, SortComponents) from pyomo.mpec.complementarity import Complementarity @@ -35,7 +34,6 @@ def __init__(self): super(MPEC3_Transformation, self).__init__() def _apply_to(self, instance, **kwds): - options = kwds.pop('options', {}) # # Iterate over the model finding Complementarity components # diff --git a/pyomo/mpec/plugins/mpec4.py b/pyomo/mpec/plugins/mpec4.py index a6aa7f0b57e..7b2b924b6c3 100644 --- a/pyomo/mpec/plugins/mpec4.py +++ b/pyomo/mpec/plugins/mpec4.py @@ -36,7 +36,6 @@ def __init__(self): super(MPEC4_Transformation, self).__init__() def _apply_to(self, instance, **kwds): - options = kwds.pop('options', {}) # # Find the free variables # @@ -55,7 +54,6 @@ def _apply_to(self, instance, **kwds): for cobj in instance.component_objects(Complementarity, active=True, descend_into=(Block, Disjunct), sort=SortComponents.deterministic): - bdata = cobj.parent_block() cobjs.append(cobj) for index in sorted(iterkeys(cobj)): _cdata = cobj[index] diff --git a/pyomo/mpec/plugins/pathampl.py b/pyomo/mpec/plugins/pathampl.py index d9023ef03a4..4a5f573b2da 100644 --- a/pyomo/mpec/plugins/pathampl.py +++ b/pyomo/mpec/plugins/pathampl.py @@ -9,16 +9,11 @@ # ___________________________________________________________________________ import logging -import os -import six -import pyutilib.services -import pyutilib.misc +from pyutilib.misc import Options from pyomo.opt.base.solvers import SolverFactory -from pyomo.opt.base import * -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.common import Executable from pyomo.solvers.plugins.solvers.ASL import ASL logger = logging.getLogger('pyomo.solvers') @@ -38,11 +33,11 @@ def __init__(self, **kwds): # # Define solver capabilities, which default to 'None' # - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True def _default_executable(self): - executable = pyomo.common.Executable("pathampl") + executable = Executable("pathampl") if not executable: #pragma:nocover logger.warning("Could not locate the 'pathampl' executable, " "which is required for solver %s" % self.name) diff --git a/pyomo/mpec/tests/test_complementarity.py b/pyomo/mpec/tests/test_complementarity.py index d25b1cb04e5..3e06b654eaa 100644 --- a/pyomo/mpec/tests/test_complementarity.py +++ b/pyomo/mpec/tests/test_complementarity.py @@ -23,7 +23,7 @@ from pyutilib.misc import setup_redirect, reset_redirect from pyomo.opt import ProblemFormat -from pyomo.core import * +from pyomo.core import ConcreteModel, Var, Constraint, TransformationFactory, Objective, Block, inequality from pyomo.mpec import Complementarity, complements, ComplementarityList from pyomo.gdp import Disjunct, Disjunction diff --git a/pyomo/mpec/tests/test_minlp.py b/pyomo/mpec/tests/test_minlp.py index 0f66f765d00..fd46233fd78 100644 --- a/pyomo/mpec/tests/test_minlp.py +++ b/pyomo/mpec/tests/test_minlp.py @@ -12,7 +12,6 @@ # Test the mpec_minlp solver # -import sys import os from os.path import abspath, dirname, normpath, join currdir = dirname(abspath(__file__)) @@ -24,7 +23,6 @@ import pyomo.opt import pyomo.scripting.pyomo_main as pyomo_main from pyomo.scripting.util import cleanup -from pyomo.environ import * from six import iteritems diff --git a/pyomo/mpec/tests/test_nlp.py b/pyomo/mpec/tests/test_nlp.py index 0ec56c616a0..6db9f206ee3 100644 --- a/pyomo/mpec/tests/test_nlp.py +++ b/pyomo/mpec/tests/test_nlp.py @@ -12,11 +12,10 @@ # Test the mpec_nlp solver # -import sys import os from os.path import abspath, dirname, normpath, join currdir = dirname(abspath(__file__)) -exdir = normpath(join(currdir,'..','..','..','examples','mpec')) +exdir = normpath(join(currdir, '..', '..', '..', 'examples', 'mpec')) import pyutilib.th as unittest @@ -24,12 +23,12 @@ import pyomo.opt import pyomo.scripting.pyomo_main as pyomo_main from pyomo.scripting.util import cleanup -from pyomo.environ import * from six import iteritems solvers = pyomo.opt.check_available_solvers('ipopt') + class CommonTests: solve = True diff --git a/pyomo/mpec/tests/test_path.py b/pyomo/mpec/tests/test_path.py index 37484d6bb62..58bee669d0a 100644 --- a/pyomo/mpec/tests/test_path.py +++ b/pyomo/mpec/tests/test_path.py @@ -12,7 +12,6 @@ # Test the path solver # -import sys import os from os.path import abspath, dirname, normpath, join currdir = dirname(abspath(__file__)) @@ -25,7 +24,6 @@ import pyomo.opt import pyomo.scripting.pyomo_main as pyomo_main from pyomo.scripting.util import cleanup -from pyomo.environ import * from six import iteritems diff --git a/pyomo/neos/kestrel.py b/pyomo/neos/kestrel.py index 3db7f7640fb..1fc6d805215 100644 --- a/pyomo/neos/kestrel.py +++ b/pyomo/neos/kestrel.py @@ -316,7 +316,7 @@ def formXML(self,stub): if six.PY2: nl_string = base64.encodestring(zipped_nl_file.getvalue()) else: - nl_string = (base64.encodestring(zipped_nl_file.getvalue())).decode('utf-8') + nl_string = (base64.encodebytes(zipped_nl_file.getvalue())).decode('utf-8') xml = """ kestrel diff --git a/pyomo/neos/plugins/NEOS.py b/pyomo/neos/plugins/NEOS.py index 6678658f2af..56731a505bc 100644 --- a/pyomo/neos/plugins/NEOS.py +++ b/pyomo/neos/plugins/NEOS.py @@ -10,12 +10,11 @@ import logging -import pyutilib.misc -import pyutilib.services +from pyutilib.misc import Bunch +from pyutilib.services import TempfileManager -from pyomo.opt.base import * -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.opt.base import SolverFactory, ProblemFormat, ResultsFormat +from pyomo.opt.solver import SystemCallSolver logger = logging.getLogger('pyomo.neos') @@ -38,10 +37,10 @@ def create_command_line(self, executable, problem_files): populated by NEOS. """ if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix=".neos.log") if self._soln_file is None: - self._soln_file = pyutilib.services.TempfileManager.\ + self._soln_file = TempfileManager.\ create_tempfile(suffix=".neos.sol") self._results_file = self._soln_file @@ -55,7 +54,7 @@ def create_command_line(self, executable, problem_files): if self._problem_files is not []: logger.info("Solver problem files: %s" % (self._problem_files,)) - return pyutilib.misc.Bunch(cmd="", log_file=self._log_file, env="") + return Bunch(cmd="", log_file=self._log_file, env="") def _default_executable(self): return True diff --git a/pyomo/neos/plugins/kestrel_plugin.py b/pyomo/neos/plugins/kestrel_plugin.py index 2146bf63569..8a8c0681f6c 100644 --- a/pyomo/neos/plugins/kestrel_plugin.py +++ b/pyomo/neos/plugins/kestrel_plugin.py @@ -15,11 +15,10 @@ from pyomo.common.dependencies import attempt_import from pyomo.opt import SolverFactory, SolverManagerFactory, OptSolver -from pyomo.opt.parallel.manager import ActionManagerError +from pyomo.opt.parallel.manager import ActionManagerError, ActionStatus from pyomo.opt.parallel.async_solver import ( - AsynchronousSolverManager, ActionStatus + AsynchronousSolverManager ) -from pyomo.opt.base import OptSolver from pyomo.core.base import Block import pyomo.neos.kestrel diff --git a/pyomo/neos/tests/t1.py b/pyomo/neos/tests/t1.py index 2662b8af625..9f71c7c3bb4 100644 --- a/pyomo/neos/tests/t1.py +++ b/pyomo/neos/tests/t1.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, maximize M = ConcreteModel() M.x = Var(bounds=(0,1)) diff --git a/pyomo/neos/tests/test_neos.py b/pyomo/neos/tests/test_neos.py index 3fbd7b04d13..8925b08d6cb 100644 --- a/pyomo/neos/tests/test_neos.py +++ b/pyomo/neos/tests/test_neos.py @@ -12,7 +12,7 @@ # import os -from os.path import abspath, dirname, join +from os.path import abspath, dirname currdir = dirname(abspath(__file__)) import pyutilib.th as unittest @@ -24,8 +24,6 @@ import pyomo.environ as pyo -from six import iteritems - neos_available = False try: # Attempt a connection to NEOS. Any failure will result in skipping diff --git a/pyomo/network/__init__.py b/pyomo/network/__init__.py index 20988328875..d7972d22f58 100644 --- a/pyomo/network/__init__.py +++ b/pyomo/network/__init__.py @@ -8,6 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.network.arc import * -from pyomo.network.port import * -from pyomo.network.decomposition import * +from pyomo.network.arc import Arc +from pyomo.network.port import Port +from pyomo.network.decomposition import SequentialDecomposition diff --git a/pyomo/network/arc.py b/pyomo/network/arc.py index 0de604722e7..d30e06bf801 100644 --- a/pyomo/network/arc.py +++ b/pyomo/network/arc.py @@ -15,8 +15,7 @@ from pyomo.core.base.indexed_component import (ActiveIndexedComponent, UnindexedComponent_set) from pyomo.core.base.misc import apply_indexed_rule -from pyomo.core.base.plugin import (ModelComponentFactory, - IPyomoScriptModifyInstance, TransformationFactory) +from pyomo.core.base.plugin import ModelComponentFactory from pyomo.common.timing import ConstructionTimer from six import iteritems from weakref import ref as weakref_ref diff --git a/pyomo/network/decomposition.py b/pyomo/network/decomposition.py index 8c093a07ea2..c81fb9a0b69 100644 --- a/pyomo/network/decomposition.py +++ b/pyomo/network/decomposition.py @@ -18,8 +18,8 @@ from pyomo.core.expr.current import identify_variables from pyomo.repn import generate_standard_repn from pyutilib.misc import Options -import copy, logging, time -from six import iteritems, itervalues +import logging, time +from six import iteritems from pyomo.common.dependencies import ( networkx as nx, networkx_available, diff --git a/pyomo/network/plugins/expand_arcs.py b/pyomo/network/plugins/expand_arcs.py index 8b45da64deb..4044afca11a 100644 --- a/pyomo/network/plugins/expand_arcs.py +++ b/pyomo/network/plugins/expand_arcs.py @@ -11,13 +11,13 @@ import logging logger = logging.getLogger('pyomo.network') -from six import next, iteritems, itervalues +from six import iteritems, itervalues from pyomo.common.modeling import unique_component_name from pyomo.common.collections import ComponentMap, ComponentSet from pyomo.core.base.indexed_component import UnindexedComponent_set -from pyomo.core.base import Transformation, Var, Block, SortComponents, TransformationFactory +from pyomo.core.base import Transformation, Block, SortComponents, TransformationFactory from pyomo.network import Arc from pyomo.network.util import replicate_var @@ -72,7 +72,6 @@ def _collect_ports(self, instance): matched_ports = ComponentMap() for arc in instance.component_data_objects(**obj_iter_kwds): - ports = ComponentSet(arc.ports) ref = None for p in arc.ports: diff --git a/pyomo/network/port.py b/pyomo/network/port.py index 0fe53893a8a..8af09cb7291 100644 --- a/pyomo/network/port.py +++ b/pyomo/network/port.py @@ -11,9 +11,10 @@ __all__ = [ 'Port' ] import logging, sys -from six import iteritems, itervalues +from six import iteritems from weakref import ref as weakref_ref +from pyomo.common.collections import ComponentMap from pyomo.common.timing import ConstructionTimer from pyomo.common.modeling import unique_component_name @@ -26,9 +27,7 @@ from pyomo.core.base.numvalue import as_numeric, value from pyomo.core.expr.current import identify_variables from pyomo.core.base.label import alphanum_label_from_name -from pyomo.core.base.plugin import ModelComponentFactory, \ - IPyomoScriptModifyInstance, TransformationFactory -from pyomo.common.collections import ComponentMap +from pyomo.core.base.plugin import ModelComponentFactory from pyomo.network.util import create_var, tighten_var_domain diff --git a/pyomo/network/tests/test_arc.py b/pyomo/network/tests/test_arc.py index e2964a81cac..a438d47a5bf 100644 --- a/pyomo/network/tests/test_arc.py +++ b/pyomo/network/tests/test_arc.py @@ -15,8 +15,8 @@ from six import StringIO import logging -from pyomo.environ import * -from pyomo.network import * +from pyomo.environ import ConcreteModel, AbstractModel, Var, Set, Constraint, RangeSet, NonNegativeReals, Reals, Binary, TransformationFactory, Block +from pyomo.network import Arc, Port class TestArc(unittest.TestCase): diff --git a/pyomo/network/tests/test_decomposition.py b/pyomo/network/tests/test_decomposition.py index 38aeaa634d5..dc18a4ddc8d 100644 --- a/pyomo/network/tests/test_decomposition.py +++ b/pyomo/network/tests/test_decomposition.py @@ -14,8 +14,8 @@ import pyutilib.th as unittest from pyomo.common.dependencies import numpy_available, networkx_available -from pyomo.environ import * -from pyomo.network import * +from pyomo.environ import SolverFactory, value, ConcreteModel, Set, Block, Var, TransformationFactory, Reference +from pyomo.network import Port, SequentialDecomposition, Arc from types import MethodType import_available = numpy_available and networkx_available diff --git a/pyomo/network/tests/test_port.py b/pyomo/network/tests/test_port.py index 4054491ec17..caf70424f59 100644 --- a/pyomo/network/tests/test_port.py +++ b/pyomo/network/tests/test_port.py @@ -14,8 +14,8 @@ import pyutilib.th as unittest from six import StringIO -from pyomo.environ import * -from pyomo.network import * +from pyomo.environ import ConcreteModel, AbstractModel, Var, Set, NonNegativeReals, Binary, Reals, Integers, RangeSet +from pyomo.network import Port, Arc class TestPort(unittest.TestCase): diff --git a/pyomo/opt/__init__.py b/pyomo/opt/__init__.py index 6d46edea0c1..e1988d69dab 100644 --- a/pyomo/opt/__init__.py +++ b/pyomo/opt/__init__.py @@ -8,9 +8,37 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.opt.base import * -from pyomo.opt.results import * +import pyomo.opt.base.opt_config import pyomo.opt.solver -from pyomo.opt.problem import * -from pyomo.opt.parallel import * + +from pyomo.opt.base import ( + check_available_solvers, convert, convert_problem, error, formats, + guess_format, opt_config, solvers, + AbstractProblemWriter, AbstractResultsReader, BaseProblemConfig, + BranchDirection, ConverterError, OptSolver, ProblemConfigFactory, + ProblemFormat, ReaderFactory, ResultsFormat, SolverFactory, + UnknownSolver, WriterFactory, +) + +from pyomo.opt.results import ( + container, problem, solution, + ScalarData, ScalarType, + default_print_options, + ListContainer, MapContainer, + UndefinedData, undefined, ignore, + SolverStatus, TerminationCondition, + check_optimal_termination, assert_optimal_termination, + ProblemSense, + SolutionStatus, Solution, results_, + SolverResults +) + +from pyomo.opt.problem import ( + ampl, AmplModel +) + +from pyomo.opt.parallel import ( + pyro, manager, async_solver, local, + SolverManagerFactory, AsynchronousSolverManager +) diff --git a/pyomo/opt/base/__init__.py b/pyomo/opt/base/__init__.py index 49d36bebed5..3b56aff8854 100644 --- a/pyomo/opt/base/__init__.py +++ b/pyomo/opt/base/__init__.py @@ -10,9 +10,16 @@ import pyomo.opt.base.opt_config -from pyomo.opt.base.error import * -from pyomo.opt.base.convert import * -from pyomo.opt.base.solvers import * -from pyomo.opt.base.results import * -from pyomo.opt.base.problem import * -from pyomo.opt.base.formats import * +from pyomo.opt.base.error import ConverterError +from pyomo.opt.base.convert import convert_problem +from pyomo.opt.base.solvers import ( + UnknownSolver, SolverFactory, check_available_solvers, OptSolver, +) +from pyomo.opt.base.results import ReaderFactory, AbstractResultsReader +from pyomo.opt.base.problem import ( + ProblemConfigFactory, BaseProblemConfig, AbstractProblemWriter, + BranchDirection, WriterFactory +) +from pyomo.opt.base.formats import ( + ProblemFormat, ResultsFormat, guess_format, +) diff --git a/pyomo/opt/base/solvers.py b/pyomo/opt/base/solvers.py index 85378114d23..2ff919d2b3e 100644 --- a/pyomo/opt/base/solvers.py +++ b/pyomo/opt/base/solvers.py @@ -14,16 +14,14 @@ 'check_available_solvers') import re -import os import sys import time import logging from pyutilib.misc.config import ConfigBlock, ConfigList, ConfigValue from pyomo.common import Factory -import pyutilib.common -import pyutilib.misc -import pyutilib.services +from pyutilib.common import ApplicationError +from pyutilib.misc import Options, quote_split from pyomo.opt.base.problem import ProblemConfigFactory from pyomo.opt.base.convert import convert_problem @@ -95,8 +93,7 @@ def __exit__(self, t, v, traceback): def available(self, exception_flag=True): """Determine if this optimizer is available.""" if exception_flag: - from pyutilib.common import ApplicationError - raise pyutilib.common.ApplicationError("Solver (%s) not available" % str(self.name)) + raise ApplicationError("Solver (%s) not available" % str(self.name)) return False def warm_start_capable(self): @@ -343,7 +340,7 @@ def __init__(self, **kwds): # through the solve command. Everything else is reset inside # presolve # - self.options = pyutilib.misc.Options() + self.options = Options() if 'options' in kwds and not kwds['options'] is None: for key in kwds['options']: setattr(self.options, key, kwds['options'][key]) @@ -392,7 +389,7 @@ def __init__(self, **kwds): # We define no capabilities for the generic solver; base # classes must override this - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() @staticmethod def _options_string_to_dict(istr): @@ -402,7 +399,7 @@ def _options_string_to_dict(istr): return ans if istr[0] == "'" or istr[0] == '"': istr = eval(istr) - tokens = pyutilib.misc.quote_split('[ ]+',istr) + tokens = quote_split('[ ]+',istr) for token in tokens: index = token.find('=') if index == -1: @@ -560,7 +557,7 @@ def solve(self, *args, **kwds): orig_options = self.options - self.options = pyutilib.misc.Options() + self.options = Options() self.options.update(orig_options) self.options.update(kwds.pop('options', {})) self.options.update( @@ -596,7 +593,7 @@ def solve(self, *args, **kwds): "See the solver log above for diagnostic information." ) elif hasattr(_status, 'log') and _status.log: logger.error("Solver log:\n" + str(_status.log)) - raise pyutilib.common.ApplicationError( + raise ApplicationError( "Solver (%s) did not exit normally" % self.name) solve_completion_time = time.time() if self._report_timing: @@ -787,7 +784,7 @@ def fn(solver, model): a Pyomo model instance object. """ if not self._allow_callbacks: - raise pyutilib.common.ApplicationError( + raise ApplicationError( "Callbacks disabled for solver %s" % self.name) if callback_fn is None: if name in self._callback: diff --git a/pyomo/opt/blackbox/__init__.py b/pyomo/opt/blackbox/__init__.py index 4749cab5f7c..14b59881e8d 100644 --- a/pyomo/opt/blackbox/__init__.py +++ b/pyomo/opt/blackbox/__init__.py @@ -8,6 +8,8 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.opt.blackbox.point import * -from pyomo.opt.blackbox.problem import * +from pyomo.opt.blackbox.point import MixedIntVars, RealVars +from pyomo.opt.blackbox.problem import (BlackBoxOptProblemIOFactory, + response_enum, OptProblem, + MixedIntOptProblem, RealOptProblem) import pyomo.opt.blackbox.solver diff --git a/pyomo/opt/blackbox/solver.py b/pyomo/opt/blackbox/solver.py index f8c57c8c63f..1c01e3f62cf 100644 --- a/pyomo/opt/blackbox/solver.py +++ b/pyomo/opt/blackbox/solver.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.opt.base import * +from pyomo.opt.base import OptSolver, ProblemFormat, ResultsFormat class COLINSolver(OptSolver): diff --git a/pyomo/opt/parallel/__init__.py b/pyomo/opt/parallel/__init__.py index 8518e0078f6..5fe03e3ed04 100644 --- a/pyomo/opt/parallel/__init__.py +++ b/pyomo/opt/parallel/__init__.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.opt.parallel.async_solver import * +from pyomo.opt.parallel.async_solver import (Factory, AsynchronousActionManager, SolverManagerFactory, AsynchronousSolverManager) import pyomo.opt.parallel.manager import pyomo.opt.parallel.pyro import pyomo.opt.parallel.local diff --git a/pyomo/opt/parallel/async_solver.py b/pyomo/opt/parallel/async_solver.py index f006b02a71b..cb1e28d1b4c 100644 --- a/pyomo/opt/parallel/async_solver.py +++ b/pyomo/opt/parallel/async_solver.py @@ -12,7 +12,7 @@ __all__ = ['AsynchronousSolverManager', 'SolverManagerFactory'] from pyomo.common import Factory -from pyomo.opt.parallel.manager import * +from pyomo.opt.parallel.manager import AsynchronousActionManager diff --git a/pyomo/opt/parallel/local.py b/pyomo/opt/parallel/local.py index 442dc1d42db..b667eefacfa 100644 --- a/pyomo/opt/parallel/local.py +++ b/pyomo/opt/parallel/local.py @@ -24,7 +24,6 @@ ActionHandle) from pyomo.opt.parallel.async_solver import AsynchronousSolverManager, SolverManagerFactory -import six from six import string_types diff --git a/pyomo/opt/parallel/pyro.py b/pyomo/opt/parallel/pyro.py index b9128372d58..8428a3a36f1 100644 --- a/pyomo/opt/parallel/pyro.py +++ b/pyomo/opt/parallel/pyro.py @@ -18,8 +18,6 @@ from pyomo.common.dependencies import attempt_import from pyomo.opt.parallel.manager import \ (AsynchronousActionManager, - ActionManagerError, - ActionHandle, ActionStatus) pyu_pyro = attempt_import('pyutilib.pyro', alt_names=['pyu_pyro'])[0] diff --git a/pyomo/opt/plugins/problem_config.py b/pyomo/opt/plugins/problem_config.py index 9e67a5216d6..9204fdb62d0 100644 --- a/pyomo/opt/plugins/problem_config.py +++ b/pyomo/opt/plugins/problem_config.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ from pyutilib.misc.config import ConfigBlock, ConfigList, ConfigValue -from pyomo.opt.base.problem import * +from pyomo.opt.base.problem import ProblemConfigFactory, BaseProblemConfig @ProblemConfigFactory.register('default') diff --git a/pyomo/opt/plugins/sol.py b/pyomo/opt/plugins/sol.py index 19873008268..889c7b97b66 100644 --- a/pyomo/opt/plugins/sol.py +++ b/pyomo/opt/plugins/sol.py @@ -200,8 +200,8 @@ def _load(self, fin, res, soln, suffixes): if (unmasked_kind & 4) == 4: convert_function = float nvalues = int(line[2]) - namelen = int(line[3]) - tablen = int(line[4]) + # namelen = int(line[3]) + # tablen = int(line[4]) tabline = int(line[5]) suffix_name = fin.readline().strip() if any(re.match(suf,suffix_name) for suf in suffixes): diff --git a/pyomo/opt/problem/__init__.py b/pyomo/opt/problem/__init__.py index 5b52c12dbc2..2953259e21e 100644 --- a/pyomo/opt/problem/__init__.py +++ b/pyomo/opt/problem/__init__.py @@ -8,4 +8,5 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.opt.problem.ampl import * +from pyomo.opt.problem.ampl import (ProblemFormat, convert_problem, + guess_format, AmplModel) diff --git a/pyomo/opt/results/__init__.py b/pyomo/opt/results/__init__.py index 0d5a66eb363..696711e8bb4 100644 --- a/pyomo/opt/results/__init__.py +++ b/pyomo/opt/results/__init__.py @@ -8,8 +8,10 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -#from old_results import * -from pyomo.opt.results.container import * +from pyomo.opt.results.container import (ScalarData, ScalarType, + default_print_options, strict, + ListContainer, MapContainer, + UndefinedData, undefined, ignore) import pyomo.opt.results.problem from pyomo.opt.results.solver import SolverStatus, TerminationCondition, \ check_optimal_termination, assert_optimal_termination diff --git a/pyomo/opt/results/container.py b/pyomo/opt/results/container.py index 1ff70f9c041..7d52354320a 100644 --- a/pyomo/opt/results/container.py +++ b/pyomo/opt/results/container.py @@ -11,13 +11,13 @@ __all__ = ['UndefinedData', 'undefined', 'ignore', 'ScalarData', 'ListContainer', 'MapContainer', 'default_print_options', 'ScalarType'] import copy -import math -import pyutilib.math +from pyutilib.math import infinity from pyutilib.misc import Bunch import enum -from six import iterkeys, itervalues, iteritems, advance_iterator, StringIO +from six import StringIO from six.moves import xrange + try: unicode except NameError: @@ -106,9 +106,9 @@ def pprint(self, ostream, option, prefix="", repn=None): value = self.yaml_fix(self.get_value()) - if value is pyutilib.math.infinity: + if value is infinity: value = '.inf' - elif value is - pyutilib.math.infinity: + elif value is - infinity: value = '-.inf' if not option.schema and self.description is None and self.units is None: diff --git a/pyomo/opt/results/problem.py b/pyomo/opt/results/problem.py index 7dc0f3a5eae..014f8a552bf 100644 --- a/pyomo/opt/results/problem.py +++ b/pyomo/opt/results/problem.py @@ -11,7 +11,7 @@ __all__ = ['ProblemInformation', 'ProblemSense'] import enum -from pyomo.opt.results.container import * +from pyomo.opt.results.container import MapContainer class ProblemSense(str, enum.Enum): unknown='unknown' diff --git a/pyomo/opt/results/solution.py b/pyomo/opt/results/solution.py index 86224c53dcd..5bd8506c621 100644 --- a/pyomo/opt/results/solution.py +++ b/pyomo/opt/results/solution.py @@ -15,12 +15,12 @@ from collections import OrderedDict except: from ordereddict import OrderedDict -from six import iterkeys, advance_iterator, itervalues, iteritems +from six import iterkeys, iteritems from six.moves import xrange from pyutilib.misc import Bunch import enum from pyutilib.math import as_number -from pyomo.opt.results.container import * +from pyomo.opt.results.container import MapContainer, ListContainer, ignore default_print_options = Bunch(schema=False, sparse=True, diff --git a/pyomo/opt/results/solver.py b/pyomo/opt/results/solver.py index 6dbf226faaf..8904a9bd082 100644 --- a/pyomo/opt/results/solver.py +++ b/pyomo/opt/results/solver.py @@ -81,6 +81,50 @@ class TerminationCondition(str, enum.Enum): def __str__(self): return self.value + @staticmethod + def to_solver_status(tc): + """Maps a TerminationCondition to SolverStatus based on enum value + + Parameters + ---------- + tc: TerminationCondition + + Returns + ------- + SolverStatus + """ + if tc in { + TerminationCondition.maxTimeLimit, + TerminationCondition.maxIterations, + TerminationCondition.minFunctionValue, + TerminationCondition.minStepLength, + TerminationCondition.globallyOptimal, + TerminationCondition.locallyOptimal, + TerminationCondition.feasible, + TerminationCondition.optimal, + TerminationCondition.maxEvaluations, + TerminationCondition.other }: + return SolverStatus.ok + if tc in { + TerminationCondition.unbounded, + TerminationCondition.infeasible, + TerminationCondition.infeasibleOrUnbounded, + TerminationCondition.invalidProblem, + TerminationCondition.intermediateNonInteger, + TerminationCondition.noSolution }: + return SolverStatus.warning + if tc in { + TerminationCondition.solverFailure, + TerminationCondition.internalSolverError, + TerminationCondition.error }: + return SolverStatus.error + if tc in { + TerminationCondition.userInterrupt, + TerminationCondition.resourceInterrupt, + TerminationCondition.licensingProblems }: + return SolverStatus.aborted + return SolverStatus.unknown + def check_optimal_termination(results): """ diff --git a/pyomo/opt/solver/__init__.py b/pyomo/opt/solver/__init__.py index 67b09e8aa67..4bbe1283d53 100644 --- a/pyomo/opt/solver/__init__.py +++ b/pyomo/opt/solver/__init__.py @@ -8,5 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.opt.solver.shellcmd import * -from pyomo.opt.solver.ilmcmd import * +from pyomo.opt.solver.shellcmd import (ResultsFormat, OptSolver, SolverStatus, + SolverResults, SystemCallSolver) +from pyomo.opt.solver.ilmcmd import ILMLicensedSystemCallSolver diff --git a/pyomo/opt/solver/shellcmd.py b/pyomo/opt/solver/shellcmd.py index ef824d794cd..bb69b623aee 100644 --- a/pyomo/opt/solver/shellcmd.py +++ b/pyomo/opt/solver/shellcmd.py @@ -15,15 +15,14 @@ import time import logging -import pyutilib.misc from pyutilib.common import ApplicationError, WindowsError from pyutilib.misc import Bunch from pyutilib.services import TempfileManager from pyutilib.subprocess import run import pyomo.common -from pyomo.opt.base import * -from pyomo.opt.base.solvers import * +from pyomo.opt.base import ResultsFormat +from pyomo.opt.base.solvers import OptSolver from pyomo.opt.results import SolverStatus, SolverResults logger = logging.getLogger('pyomo.opt') diff --git a/pyomo/opt/testing/__init__.py b/pyomo/opt/testing/__init__.py index b0840150dd9..6c0dde2519d 100644 --- a/pyomo/opt/testing/__init__.py +++ b/pyomo/opt/testing/__init__.py @@ -8,4 +8,4 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.opt.testing.pyunit import * +from pyomo.opt.testing.pyunit import (_failIfPyomoResultsDiffer, TestCase) \ No newline at end of file diff --git a/pyomo/opt/testing/pyunit.py b/pyomo/opt/testing/pyunit.py index de61b8dc3f0..a08f76c4017 100644 --- a/pyomo/opt/testing/pyunit.py +++ b/pyomo/opt/testing/pyunit.py @@ -38,7 +38,7 @@ def _failIfPyomoResultsDiffer(self, cmd=None, baseline=None, cwd=None): if output[0] != 0: self.fail("Command terminated with nonzero status: '%s'" % cmd) - results = extract_results( re.split('\n',output[1]) ) + results = extract_results(re.split('\n',output[1])) try: compare_results(results, baseline) except IOError: diff --git a/pyomo/pysp/annotations.py b/pyomo/pysp/annotations.py index 0395e0c981f..04e8aec6a02 100644 --- a/pyomo/pysp/annotations.py +++ b/pyomo/pysp/annotations.py @@ -18,7 +18,6 @@ from pyomo.core.base.objective import Objective, _ObjectiveData from pyomo.core.base.block import Block, _BlockData -import pyomo.pysp logger = logging.getLogger('pyomo.pysp') def locate_annotations(model, annotation_type, max_allowed=None): diff --git a/pyomo/pysp/bbph/brancher.py b/pyomo/pysp/bbph/brancher.py index 4f489aebe1a..a63848fc38a 100644 --- a/pyomo/pysp/bbph/brancher.py +++ b/pyomo/pysp/bbph/brancher.py @@ -7,20 +7,17 @@ outputfilename = "brancherout.p" -import sys import time import os import pickle # so we can send index across with its type (I hope) import pyomo.common.plugin -from pyomo.core import * from pyomo.pysp import phextension -from pyomo.pysp.phutils import * -from pyomo.pysp.generators import \ - scenario_tree_node_variables_generator_noinstances import pyomo.solvers.plugins.smanager.phpyro from pyomo.pysp import phsolverserverutils +from six import iteritems + thisfile = os.path.abspath(__file__) # diff --git a/pyomo/pysp/benders.py b/pyomo/pysp/benders.py index 71a9ded9d25..0cc0b165087 100644 --- a/pyomo/pysp/benders.py +++ b/pyomo/pysp/benders.py @@ -9,8 +9,54 @@ # ___________________________________________________________________________ from pyomo.common import pyomo_command -from pyomo.pysp.solvers.benders import * +from pyomo.opt import (SolverFactory, + TerminationCondition, + undefined) +from pyomo.core import (value, minimize, Set, + Objective, SOSConstraint, + Constraint, Var, RangeSet, + Expression, Suffix, Reals, Param) +from pyomo.core.base.constraint import _GeneralConstraintData +from pyomo.core.beta.list_objects import XConstraintList +from pyomo.pysp.util.configured_object import PySPConfiguredObject +from pyomo.pysp.util.config import (PySPConfigValue, + PySPConfigBlock, + safe_register_common_option, + safe_declare_common_option, + safe_declare_unique_option, + _domain_percent, + _domain_nonnegative, + _domain_positive_integer, + _domain_must_be_str, + _domain_unit_interval, + _domain_tuple_of_str, + _domain_tuple_of_str_or_dict) +from pyomo.pysp.util.misc import (parse_command_line, + launch_command) +from pyomo.pysp.scenariotree.manager import \ + (InvocationType, + ScenarioTreeManager, + ScenarioTreeManagerFactory) +from pyomo.pysp.scenariotree.manager_solver import \ + ScenarioTreeManagerSolverFactory +from pyomo.pysp.phutils import find_active_objective +from pyomo.pysp.ef import create_ef_instance +from pyomo.pysp.solvers.spsolver import (SPSolver, + SPSolverResults, + SPSolverFactory) +from pyomo.pysp.solvers.benders import (EXTERNAL_deactivate_rootnode_costs, + EXTERNAL_activate_rootnode_costs, + EXTERNAL_activate_fix_constraints, + EXTERNAL_deactivate_fix_constraints, + EXTERNAL_cleanup_from_benders, + EXTERNAL_initialize_for_benders, + EXTERNAL_update_fix_constraints, + EXTERNAL_collect_cut_data, + BendersOptimalityCut, BendersAlgorithm, + BendersSolver, + runbenders_register_options, runbenders, + main as b_main) @pyomo_command('runbenders', 'Optimize with the Benders solver') def Benders_main(args=None): - return main(args=args) + return b_main(args=args) diff --git a/pyomo/pysp/computeconf.py b/pyomo/pysp/computeconf.py index 9bca1c0efad..a92663dd2df 100755 --- a/pyomo/pysp/computeconf.py +++ b/pyomo/pysp/computeconf.py @@ -15,9 +15,9 @@ import time import traceback -import pyutilib.common +from pyutilib.common import ApplicationError -from pyomo.core import * +from pyomo.core import minimize # this is a hack, in order to pick up the UndefinedData class. this is # needed currently, as CPLEX is periodically barfing on cvar # formulations, yielding an undefined gap. technically, the gap is @@ -33,13 +33,10 @@ PHAlgorithmBuilder, PHFromScratch, PHCleanup) -from pyomo.pysp.ef import (create_ef_instance, - solve_ef) from pyomo.pysp.ef_writer_script import ExtensiveFormAlgorithm from pyomo.pysp.phutils import _OLD_OUTPUT -import pyomo.pysp.phboundbase -from six import iteritems, iterkeys, advance_iterator +from six import iteritems, iterkeys # to avoid the pain of user lookup of parameter in t-tables, we # provide decent coverage automatically. feel free to add more @@ -807,7 +804,7 @@ def main(args=None): except IOError as str: print("IO ERROR:") print(str) - except pyutilib.common.ApplicationError as str: + except ApplicationError as str: print("APPLICATION ERROR:") print(str) except RuntimeError as str: diff --git a/pyomo/pysp/convergence.py b/pyomo/pysp/convergence.py index f2f5349f3fc..96e32035a72 100644 --- a/pyomo/pysp/convergence.py +++ b/pyomo/pysp/convergence.py @@ -14,7 +14,7 @@ from pyomo.pysp.generators import \ scenario_tree_node_variables_generator_noinstances -from six import iteritems, iterkeys +from six import iterkeys # # This module contains a hierarchy of convergence "computers" for PH diff --git a/pyomo/pysp/convert/ddsip.py b/pyomo/pysp/convert/ddsip.py index 9c71e1f14eb..2675ef02919 100644 --- a/pyomo/pysp/convert/ddsip.py +++ b/pyomo/pysp/convert/ddsip.py @@ -18,10 +18,7 @@ from pyomo.opt import WriterFactory from pyomo.common.collections import ComponentMap from pyomo.core.base.numvalue import value, as_numeric -from pyomo.core.base.block import (Block, - _BlockData, - SortComponents) -from pyomo.core.base.var import Var, _VarData +from pyomo.core.base.var import _VarData from pyomo.core.base.constraint import Constraint, _ConstraintData from pyomo.core.base import TextLabeler, NumericLabeler from pyomo.pysp.scenariotree.manager import InvocationType @@ -36,8 +33,7 @@ _safe_remove_file, _no_negative_zero, _deterministic_check_value, - _deterministic_check_constant, - ProblemStats) + _deterministic_check_constant) from pyomo.pysp.util.config import (PySPConfigValue, PySPConfigBlock, safe_register_common_option, @@ -47,8 +43,9 @@ (ScenarioTreeManagerClientSerial, ScenarioTreeManagerClientPyro) from pyomo.pysp.util.misc import launch_command +import pyomo.environ -from six import iteritems, itervalues +from six import iteritems thisfile = os.path.abspath(__file__) @@ -106,7 +103,6 @@ def _convert_external_setup_without_cleanup( firststage_var_suffix, enforce_derived_nonanticipativity, io_options): - import pyomo.environ assert os.path.exists(output_directory) io_options = dict(io_options) @@ -1065,7 +1061,6 @@ def convertddsip_register_options(options=None): # def run_convertddsip(options): - import pyomo.environ if not os.path.exists(options.output_directory): os.makedirs(options.output_directory) diff --git a/pyomo/pysp/convert/schuripopt.py b/pyomo/pysp/convert/schuripopt.py index e07c0588019..b3b8b56f85d 100644 --- a/pyomo/pysp/convert/schuripopt.py +++ b/pyomo/pysp/convert/schuripopt.py @@ -327,7 +327,6 @@ def convertschuripopt(options): Construct a senario tree manager and write the schuripopt input files. """ - import pyomo.environ start_time = time.time() diff --git a/pyomo/pysp/convert/smps.py b/pyomo/pysp/convert/smps.py index c53303df048..2c551086605 100644 --- a/pyomo/pysp/convert/smps.py +++ b/pyomo/pysp/convert/smps.py @@ -22,9 +22,7 @@ from pyomo.common.collections import ComponentMap from pyomo.opt import WriterFactory from pyomo.core.base.numvalue import value, as_numeric -from pyomo.core.base.block import (Block, - _BlockData, - SortComponents) +from pyomo.core.base.block import SortComponents from pyomo.core.base.objective import Objective from pyomo.core.base.var import Var, _VarData from pyomo.core.base.constraint import Constraint, _ConstraintData diff --git a/pyomo/pysp/dualphmodel.py b/pyomo/pysp/dualphmodel.py index 64dd745b6c0..f392144e293 100644 --- a/pyomo/pysp/dualphmodel.py +++ b/pyomo/pysp/dualphmodel.py @@ -10,7 +10,7 @@ import copy -from pyomo.core import * +from pyomo.core import ConcreteModel, Suffix, Var, Block, Set, RangeSet, Objective, Constraint, NonNegativeReals, sum_product, value from pyomo.opt import SolverFactory from pyomo.core.expr.current import ExpressionBase from pyomo.pysp.phutils import update_all_rhos, find_active_objective diff --git a/pyomo/pysp/ef.py b/pyomo/pysp/ef.py index a0464827362..4b264480217 100644 --- a/pyomo/pysp/ef.py +++ b/pyomo/pysp/ef.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core.base import * +from pyomo.core import ConcreteModel, Var, Set, Objective, Constraint, ConstraintList, NonNegativeReals, NonPositiveReals, Expression, ComponentMap, maximize, minimize from pyomo.opt import (ProblemFormat, SolverFactory, SolverManagerFactory, diff --git a/pyomo/pysp/ef_writer_script.py b/pyomo/pysp/ef_writer_script.py index 4fe3267dfff..72718483a72 100644 --- a/pyomo/pysp/ef_writer_script.py +++ b/pyomo/pysp/ef_writer_script.py @@ -12,17 +12,12 @@ import logging import sys import time -import itertools -import math - -import pyutilib.misc -from pyutilib.pyro import shutdown_pyro_components +import argparse import pyomo.solvers from pyomo.common.dependencies import yaml from pyomo.common import pyomo_command from pyomo.opt import (SolverFactory, - TerminationCondition, undefined, UndefinedData, ProblemFormat, @@ -36,14 +31,10 @@ safe_register_unique_option, safe_declare_common_option, safe_declare_unique_option, - _domain_percent, _domain_nonnegative, - _domain_nonnegative_integer, - _domain_positive_integer, _domain_must_be_str, _domain_unit_interval, _domain_tuple_of_str, - _domain_tuple_of_str_or_dict, _output_options_group_title, _extension_options_group_title, _deprecated_options_group_title) @@ -52,8 +43,7 @@ sort_extensions_by_precedence) from pyomo.pysp.phutils import find_active_objective from pyomo.pysp.scenariotree.manager_solver import \ - (ScenarioTreeManager, - ScenarioTreeManagerClientSerial) + (ScenarioTreeManagerClientSerial) from pyomo.pysp.solutionioextensions import \ (IPySPSolutionSaverExtension, IPySPSolutionLoaderExtension) @@ -629,8 +619,7 @@ def runef_register_options(options=None): safe_register_common_option(options, "pyro_shutdown_workers") - class _DeprecatedActivateJSONIOSolutionSaver( - pyutilib.misc.config.argparse.Action): + class _DeprecatedActivateJSONIOSolutionSaver(argparse.Action): def __init__(self, option_strings, dest, nargs=None, **kwargs): if nargs is not None: raise ValueError("nargs not allowed") diff --git a/pyomo/pysp/embeddedsp.py b/pyomo/pysp/embeddedsp.py index f74024cf0f3..fcfcc5127df 100644 --- a/pyomo/pysp/embeddedsp.py +++ b/pyomo/pysp/embeddedsp.py @@ -16,13 +16,11 @@ from pyomo.common.collections import ComponentMap from pyomo.core.expr import current as EXPR -import pyomo.core.base.param from pyomo.core.base import ComponentUID -from pyomo.core.base.numvalue import is_fixed, is_constant from pyomo.core.base.block import (Block, SortComponents, generate_cuid_names) -from pyomo.core.base.var import Var, _VarData +from pyomo.core.base.var import Var from pyomo.core.base.objective import (Objective, _ObjectiveData) from pyomo.core.base.constraint import (Constraint, @@ -37,7 +35,6 @@ StochasticConstraintBodyAnnotation, StochasticObjectiveAnnotation, StochasticVariableBoundsAnnotation) -from pyomo.pysp.scenariotree.tree_structure import ScenarioTree from pyomo.pysp.scenariotree.tree_structure_model import \ CreateAbstractScenarioTreeModel from pyomo.pysp.scenariotree.manager import \ diff --git a/pyomo/pysp/evaluate_xhat.py b/pyomo/pysp/evaluate_xhat.py index d35388608a4..f479833e31e 100644 --- a/pyomo/pysp/evaluate_xhat.py +++ b/pyomo/pysp/evaluate_xhat.py @@ -8,13 +8,11 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys import time import copy from pyomo.common import pyomo_command from pyomo.common.dependencies import yaml -from pyomo.core import minimize from pyomo.pysp.util.config import (PySPConfigValue, PySPConfigBlock, safe_register_common_option, @@ -151,10 +149,8 @@ def run_evaluate_xhat(options, solution_loaders=(), solution_savers=()): - import pyomo.environ - start_time = time.time() - + import pyomo.environ solution_loaders = sort_extensions_by_precedence(solution_loaders) solution_savers = sort_extensions_by_precedence(solution_savers) diff --git a/pyomo/pysp/lagrangeutils.py b/pyomo/pysp/lagrangeutils.py index 815959f68a1..61a581e27a3 100644 --- a/pyomo/pysp/lagrangeutils.py +++ b/pyomo/pysp/lagrangeutils.py @@ -9,17 +9,11 @@ # ___________________________________________________________________________ import sys -import os import time from pyomo.core import minimize from pyomo.pysp.ef_writer_script import ExtensiveFormAlgorithm from pyomo.pysp.phinit import run_ph -from pyomo.pysp.phutils import (reset_nonconverged_variables, - extractVariableNameAndIndex, - reset_stage_cost_variables) -from pyomo.pysp.solutionwriter import ISolutionWriterExtension -from pyomo.common.plugin import ExtensionPoint # Tear the scenario instances off the ef instance when it is no longer required # so warnings are not generated next time scenarios instances are placed inside diff --git a/pyomo/pysp/ph.py b/pyomo/pysp/ph.py index e8b22071128..b852a522404 100644 --- a/pyomo/pysp/ph.py +++ b/pyomo/pysp/ph.py @@ -10,12 +10,10 @@ import gc import logging -import pickle import sys import time import inspect import uuid -from operator import itemgetter from math import fabs, sqrt try: @@ -26,15 +24,13 @@ import pyutilib.common -from pyomo.core import * +from pyomo.core import Var, Set, BooleanSet, IntegerSet, Suffix, value, minimize, maximize from pyomo.opt import (UndefinedData, - ProblemFormat, undefined, SolverFactory, SolverStatus, TerminationCondition, - SolutionStatus, - SolverStatus) + SolutionStatus) import pyomo.pysp.convergence from pyomo.pysp.phboundbase import (_PHBoundBase, @@ -1945,10 +1941,10 @@ def __init__(self, options): # and in that order. def convert_value_string_to_number(s): try: - return float(s) + return int(s) except ValueError: try: - return int(s) + return float(s) except ValueError: return s diff --git a/pyomo/pysp/phboundbase.py b/pyomo/pysp/phboundbase.py index 15a89f014e6..76616ce75f3 100644 --- a/pyomo/pysp/phboundbase.py +++ b/pyomo/pysp/phboundbase.py @@ -17,7 +17,7 @@ from six import iteritems from operator import itemgetter -from pyomo.pysp.phutils import * +from pyomo.pysp.phutils import indexToString from pyomo.opt import UndefinedData logger = logging.getLogger('pyomo.pysp') diff --git a/pyomo/pysp/phinit.py b/pyomo/pysp/phinit.py index 12063c490aa..08484e01413 100644 --- a/pyomo/pysp/phinit.py +++ b/pyomo/pysp/phinit.py @@ -8,7 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import gc import sys import time import contextlib @@ -41,7 +40,6 @@ from pyomo.pysp.solutionwriter import ISolutionWriterExtension from pyomo.pysp.util.misc import (launch_command, load_extensions) -import pyomo.pysp.phsolverserverutils # # utility method to construct an option parser for ph arguments, @@ -1257,9 +1255,8 @@ def run_ph(options, ph): def exec_runph(options): - import pyomo.environ - start_time = time.time() + import pyomo.environ try: diff --git a/pyomo/pysp/phobjective.py b/pyomo/pysp/phobjective.py index 16675b37c50..8f588bcf5a5 100644 --- a/pyomo/pysp/phobjective.py +++ b/pyomo/pysp/phobjective.py @@ -13,8 +13,9 @@ from math import fabs, log, exp from six.moves import xrange +import sys -from pyomo.core import * +from pyomo.core import Set, Constraint, Expression, BooleanSet, value from pyomo.pysp.phutils import indexToString # IMPT: In general, the breakpoint computation codes can return a diff --git a/pyomo/pysp/phsolverserver.py b/pyomo/pysp/phsolverserver.py index bc0e078393f..ecf3a8afb6a 100644 --- a/pyomo/pysp/phsolverserver.py +++ b/pyomo/pysp/phsolverserver.py @@ -8,7 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import gc # garbage collection control. import os import socket import sys @@ -23,7 +22,7 @@ TaskWorkerServer, shutdown_pyro_components) -from pyomo.core import * +from pyomo.core import Var, Suffix, Constraint from pyomo.opt import UndefinedData from pyomo.common import pyomo_command from pyomo.common.plugin import ExtensionPoint, SingletonPlugin @@ -38,7 +37,6 @@ from pyomo.pysp.ph import _PHBase from pyomo.pysp.phutils import (reset_nonconverged_variables, reset_stage_cost_variables, - find_active_objective, extract_solve_times) from pyomo.pysp.util.misc import launch_command @@ -1359,7 +1357,7 @@ def exec_phsolverserver(options): if module_to_find.rfind(".py"): module_to_find = module_to_find.rstrip(".py") if module_to_find.find("/") != -1: - module_to_find = string.split(module_to_find,"/")[-1] + module_to_find = module_to_find.split("/")[-1] for name, obj in inspect.getmembers(sys.modules[module_to_find], inspect.isclass): # the second condition gets around goofyness related to issubclass returning diff --git a/pyomo/pysp/phsolverserverutils.py b/pyomo/pysp/phsolverserverutils.py index b92e98a2cec..d1545d8c108 100644 --- a/pyomo/pysp/phsolverserverutils.py +++ b/pyomo/pysp/phsolverserverutils.py @@ -15,7 +15,7 @@ import itertools import enum -from pyomo.core import * +from pyomo.core import Var from six import iteritems, itervalues diff --git a/pyomo/pysp/phutils.py b/pyomo/pysp/phutils.py index aa6fa175884..6f0243e0a50 100644 --- a/pyomo/pysp/phutils.py +++ b/pyomo/pysp/phutils.py @@ -8,10 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys - -from pyomo.core import * -from pyomo.opt import ProblemFormat +from pyomo.core import Var, Block, Set, Objective, Constraint, SortComponents, SOSConstraint, Piecewise, BuildAction, Param, Any, Binary from pyomo.repn.standard_repn import (preprocess_block_objectives, preprocess_block_constraints, diff --git a/pyomo/pysp/plugins/adaptive_rho_converger.py b/pyomo/pysp/plugins/adaptive_rho_converger.py index 5ac95554d02..d07060c759b 100644 --- a/pyomo/pysp/plugins/adaptive_rho_converger.py +++ b/pyomo/pysp/plugins/adaptive_rho_converger.py @@ -1,13 +1,21 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import logging import copy import math -import pyomo.common.plugin +from pyomo.common.plugin import implements, alias, SingletonPlugin from pyomo.pysp import phextension from pyomo.pysp.phutils import indexToString -from pyomo.pysp.plugins.phboundextension import _PHBoundExtensionImpl - logger = logging.getLogger('pyomo.pysp') class _AdaptiveRhoBase(object): @@ -189,12 +197,12 @@ def ph_convergence_check(self, ph): def post_ph_execution(self, ph): pass -class admm(pyomo.common.plugin.SingletonPlugin, +class admm(SingletonPlugin, _AdaptiveRhoBase): - pyomo.common.plugin.implements(phextension.IPHExtension) + implements(phextension.IPHExtension) - pyomo.common.plugin.alias("admm") + alias("admm") def __init__(self): _AdaptiveRhoBase.__init__(self) diff --git a/pyomo/pysp/plugins/convexhullboundextension.py b/pyomo/pysp/plugins/convexhullboundextension.py index aa5120bbd3d..1fdfd4ed074 100644 --- a/pyomo/pysp/plugins/convexhullboundextension.py +++ b/pyomo/pysp/plugins/convexhullboundextension.py @@ -14,7 +14,7 @@ import pyomo.common.plugin from pyomo.opt import SolverFactory -from pyomo.core import * +from pyomo.core import ConcreteModel, Var, Reals, Param, Objective, ConstraintList, minimize from pyomo.pysp import phextension from pyomo.pysp.plugins.phboundextension import (_PHBoundBase, ExtractInternalNodeSolutionsforInner) diff --git a/pyomo/pysp/plugins/ddextensionnew.py b/pyomo/pysp/plugins/ddextensionnew.py index 9b86a3f74b5..1abb459ea20 100644 --- a/pyomo/pysp/plugins/ddextensionnew.py +++ b/pyomo/pysp/plugins/ddextensionnew.py @@ -16,13 +16,12 @@ import csv import sys -import itertools from operator import itemgetter import os thisfile = os.path.abspath(__file__) import pyomo.common.plugin -from pyomo.core import * +from pyomo.core import TextLabeler, Var, Constraint, Piecewise, Expression, SOSConstraint from pyomo.core.base.var import _VarData from pyomo.core.base.piecewise import _PiecewiseData from pyomo.pysp import phextension diff --git a/pyomo/pysp/plugins/ddextensionold.py b/pyomo/pysp/plugins/ddextensionold.py index 458306fb262..27e0675bfda 100644 --- a/pyomo/pysp/plugins/ddextensionold.py +++ b/pyomo/pysp/plugins/ddextensionold.py @@ -16,16 +16,16 @@ import os import csv import sys -import itertools thisfile = os.path.abspath(__file__) -from pyomo.core.base import * -from pyomo.core.base.set_types import * +from pyomo.core.base import Var, TextLabeler, Expression, Piecewise, _PiecewiseData, SOSConstraint, Constraint, components_data from pyomo.pysp.plugins.ddextensionnew import (MatrixEntriesClass, LPFileObjClass, LPFileConstraintClass) +from pyomo.pysp.phutils import create_block_symbol_maps +from pyomo.solvers.plugins.smanager.phpyro import SolverManager_PHPyro from six import iteritems @@ -98,8 +98,7 @@ def post_ph_initialization(self, ph): print(("\nUsing %s as reference scenario" % (scenario_name))) - if isinstance(ph._solver_manager, - pyomo.solvers.plugins.smanager.phpyro.SolverManager_PHPyro): + if isinstance(ph._solver_manager, SolverManager_PHPyro): # If this is parallel ph, the instances do not exist on # this process, so let's construct the one we need singleton_tree = ph._scenario_tree._scenario_instance_factory.generate_scenario_tree() diff --git a/pyomo/pysp/plugins/ecksteincombettesextension.py b/pyomo/pysp/plugins/ecksteincombettesextension.py index da7a4c9c1e7..5d9f4aec249 100644 --- a/pyomo/pysp/plugins/ecksteincombettesextension.py +++ b/pyomo/pysp/plugins/ecksteincombettesextension.py @@ -10,14 +10,14 @@ import pyomo.common.plugin -from six import iteritems, itervalues, print_ +from six import iteritems, print_ import random from pyomo.pysp import phextension from pyomo.pysp.convergence import ConvergenceBase -from pyomo.core.base import minimize, maximize +from pyomo.core.base import minimize import math diff --git a/pyomo/pysp/plugins/examplephextension.py b/pyomo/pysp/plugins/examplephextension.py index c11464b45cb..b106a7f156c 100644 --- a/pyomo/pysp/plugins/examplephextension.py +++ b/pyomo/pysp/plugins/examplephextension.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.common.plugin import * +from pyomo.common.plugin import implements, SingletonPlugin from pyomo.pysp import phextension diff --git a/pyomo/pysp/plugins/interscenario.py b/pyomo/pysp/plugins/interscenario.py index f29ff9091ee..c61b89186eb 100644 --- a/pyomo/pysp/plugins/interscenario.py +++ b/pyomo/pysp/plugins/interscenario.py @@ -14,8 +14,8 @@ from six.moves import xrange import weakref -import pyutilib -from pyutilib.misc.timing import tic, toc +from pyutilib.misc import reset_redirect, setup_redirect +from pyutilib.misc.timing import toc from pyomo.core import ( minimize, value, TransformationFactory, @@ -31,10 +31,7 @@ from pyomo.repn.standard_repn import (preprocess_block_constraints, preprocess_block_objectives) -from pyomo.pysp.phsolverserverutils import ( - InvocationType, - transmit_external_function_invocation, - transmit_external_function_invocation_to_worker ) +from pyomo.pysp.phsolverserverutils import InvocationType from pyomo.pysp.convergence import NormalizedTermDiffConvergence import logging @@ -277,7 +274,7 @@ def solve_separation_problem(solver, model, fallback): #SOLVE output_buffer = StringIO() - pyutilib.misc.setup_redirect(output_buffer) + setup_redirect(output_buffer) try: results = solver.solve(model, tee=True) except: @@ -286,7 +283,7 @@ def solve_separation_problem(solver, model, fallback): logger.warning("Solver log:\n%s" % output_buffer.getvalue()) raise finally: - pyutilib.misc.reset_redirect() + reset_redirect() ss = results.solver.status tc = results.solver.termination_condition @@ -364,7 +361,7 @@ def add_new_cuts( ph, scenario_tree, scenario_or_bundle, for i, (_sep, _par) in iteritems(cut) if abs(_sep) > epsilon*max(1,_par) ) - if expr is not 0: + if expr != 0: _cutlist.add( expr >= 0 ) for cut in incumbent_cuts: @@ -479,7 +476,7 @@ def solve_fixed_scenario_solutions( toc("preprocessed scenario %s" % ( scenario_or_bundle._name, )) output_buffer = StringIO() - pyutilib.misc.setup_redirect(output_buffer) + setup_redirect(output_buffer) try: results = ph._solver.solve(model, tee=True) # warmstart=True) except: @@ -488,7 +485,7 @@ def solve_fixed_scenario_solutions( logger.warning("Solver log:\n%s" % output_buffer.getvalue()) raise finally: - pyutilib.misc.reset_redirect() + reset_redirect() toc("solved solution from scenario set %s on scenario %s" % ( scenario_name_list, scenario_or_bundle._name, )) diff --git a/pyomo/pysp/plugins/jsonsolutionwriter.py b/pyomo/pysp/plugins/jsonsolutionwriter.py index 231e6dac173..a084d1ed3f3 100644 --- a/pyomo/pysp/plugins/jsonsolutionwriter.py +++ b/pyomo/pysp/plugins/jsonsolutionwriter.py @@ -8,9 +8,9 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.common.plugin import * +from pyomo.common.plugin import SingletonPlugin, implements from pyomo.pysp import solutionwriter -from pyomo.pysp.scenariotree import * +from pyomo.pysp.scenariotree import ScenarioTree from pyomo.pysp.plugins.phhistoryextension \ import extract_scenario_tree_structure, \ extract_scenario_solutions, \ diff --git a/pyomo/pysp/plugins/phboundextension.py b/pyomo/pysp/plugins/phboundextension.py index 1a845e031e9..c5e709d9f5e 100644 --- a/pyomo/pysp/plugins/phboundextension.py +++ b/pyomo/pysp/plugins/phboundextension.py @@ -14,16 +14,10 @@ from __future__ import division import os -import logging -import copy -import pyomo.common.plugin +from pyomo.common.plugin import implements, alias, SingletonPlugin from pyomo.pysp import phextension from pyomo.core.base import minimize -from pyomo.opt import UndefinedData - -from operator import itemgetter -from six import iteritems from pyomo.pysp.phboundbase import (_PHBoundBase, ExtractInternalNodeSolutionsforInner) @@ -319,11 +313,11 @@ def post_ph_execution(self, ph): self.ReportBoundHistory() self.ReportBestBound() -class phboundextension(pyomo.common.plugin.SingletonPlugin, _PHBoundExtensionImpl): +class phboundextension(SingletonPlugin, _PHBoundExtensionImpl): - pyomo.common.plugin.implements(phextension.IPHExtension) + implements(phextension.IPHExtension) - pyomo.common.plugin.alias("phboundextension") + alias("phboundextension") def __init__(self): diff --git a/pyomo/pysp/plugins/phhistoryextension.py b/pyomo/pysp/plugins/phhistoryextension.py index 0aa190b813c..efdde8eca5c 100644 --- a/pyomo/pysp/plugins/phhistoryextension.py +++ b/pyomo/pysp/plugins/phhistoryextension.py @@ -17,7 +17,7 @@ from pyutilib.misc import ArchiveReaderFactory -from pyomo.common.plugin import * +from pyomo.common.plugin import implements, alias, SingletonPlugin from pyomo.pysp import phextension from pyomo.pysp.phutils import indexToString from pyomo.pysp.phsolverserverutils import TransmitType diff --git a/pyomo/pysp/plugins/schuripwriter.py b/pyomo/pysp/plugins/schuripwriter.py index 6d6d81e5e12..21c64339b8d 100644 --- a/pyomo/pysp/plugins/schuripwriter.py +++ b/pyomo/pysp/plugins/schuripwriter.py @@ -8,13 +8,11 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyutilib.misc import * - -from pyomo.common.plugin import * +from pyomo.common.plugin import implements, SingletonPlugin from pyomo.pysp import phextension -from pyomo.core import * -import pyomo.opt +from pyomo.core import Suffix +from pyomo.opt import WriterFactory import os import sys @@ -50,7 +48,7 @@ def post_ph_initialization(self, ph): os.system("rm -rf "+output_directory_name) os.mkdir(output_directory_name) - nl_writer = pyomo.opt.WriterFactory('nl') + nl_writer = WriterFactory('nl') root_node = ph._scenario_tree.findRootNode() diff --git a/pyomo/pysp/plugins/sorgw.py b/pyomo/pysp/plugins/sorgw.py index 6d2064f3179..60a4258c375 100755 --- a/pyomo/pysp/plugins/sorgw.py +++ b/pyomo/pysp/plugins/sorgw.py @@ -18,22 +18,21 @@ import sys -import pyomo.common.plugin -from pyomo.core import * +from pyomo.common.plugin import implements, alias, SingletonPlugin from pyomo.pysp import phextension -from pyomo.pysp.phutils import * +from pyomo.pysp.phutils import indexToString from pyomo.pysp.generators import \ scenario_tree_node_variables_generator_noinstances #================================================== -class sorgwextension(pyomo.common.plugin.SingletonPlugin): +class sorgwextension(SingletonPlugin): - pyomo.common.plugin.implements(phextension.IPHExtension) + implements(phextension.IPHExtension) # the below is a hack to get this extension into the # set of IPHExtension objects, so it can be queried # automagically by PH. - pyomo.common.plugin.alias("sorgwextension") + alias("sorgwextension") def __init__(self, *args, **kwds): diff --git a/pyomo/pysp/plugins/testphextension.py b/pyomo/pysp/plugins/testphextension.py index 2b6ea0dea8f..6b767f9475e 100644 --- a/pyomo/pysp/plugins/testphextension.py +++ b/pyomo/pysp/plugins/testphextension.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.common.plugin import * +from pyomo.common.plugin import SingletonPlugin, implements from pyomo.pysp import phextension diff --git a/pyomo/pysp/plugins/wwphextension.py b/pyomo/pysp/plugins/wwphextension.py index 5ce1929cd98..faedc699f66 100644 --- a/pyomo/pysp/plugins/wwphextension.py +++ b/pyomo/pysp/plugins/wwphextension.py @@ -17,12 +17,11 @@ from pyomo.common.dependencies import yaml, yaml_load_args import pyomo.common.plugin from pyomo.pysp import phextension -from pyomo.pysp.phutils import * +from pyomo.pysp.phutils import isVariableNameIndexed, extractVariableNameAndIndex, indexMatchesTemplate, indexToString from pyomo.pysp.phsolverserverutils import \ transmit_external_function_invocation_to_worker from pyomo.pysp.generators import \ scenario_tree_node_variables_generator_noinstances -from pyomo.core.base import * import six from six import iteritems diff --git a/pyomo/pysp/scenariotree/__init__.py b/pyomo/pysp/scenariotree/__init__.py index f5cd1081f46..3d5be968212 100644 --- a/pyomo/pysp/scenariotree/__init__.py +++ b/pyomo/pysp/scenariotree/__init__.py @@ -9,9 +9,38 @@ # ___________________________________________________________________________ import pyomo.pysp.scenariotree.util -from pyomo.pysp.scenariotree.tree_structure_model import * -from pyomo.pysp.scenariotree.tree_structure import * -from pyomo.pysp.scenariotree.instance_factory import * +from pyomo.pysp.scenariotree.tree_structure_model import ( + CreateAbstractScenarioTreeModel, CreateConcreteTwoStageScenarioTreeModel, + ScenarioTreeModelFromNetworkX) +from pyomo.common.collections import ComponentMap +from pyomo.core import (value, minimize, maximize, + Var, Expression, Block, + Objective, SOSConstraint, + ComponentUID) +from pyomo.core.base.block import generate_cuid_names +from pyomo.core.base.sos import _SOSConstraintData +from pyomo.repn import generate_standard_repn +from pyomo.pysp.phutils import (BasicSymbolMap, + indexToString, + isVariableNameIndexed, + extractVariableNameAndIndex, + extractComponentIndices, + find_active_objective) +from pyomo.pysp.scenariotree.tree_structure import (_CUIDLabeler, + ScenarioTreeNode, + ScenarioTreeStage, + Scenario, + ScenarioTreeBundle, + ScenarioTree) +from pyomo.pysp.scenariotree.instance_factory import (DataPortal, Block, + IPyomoScriptModifyInstance, + AbstractModel, _BlockData, + ExtensionPoint, + load_external_module, + _extract_pathspec, + _find_reference_model_or_callback, + _find_scenariotree, + ScenarioTreeInstanceFactory) import pyomo.pysp.scenariotree.action_manager_pyro import pyomo.pysp.scenariotree.server_pyro diff --git a/pyomo/pysp/scenariotree/instance_factory.py b/pyomo/pysp/scenariotree/instance_factory.py index cf4710a205a..47521417607 100644 --- a/pyomo/pysp/scenariotree/instance_factory.py +++ b/pyomo/pysp/scenariotree/instance_factory.py @@ -11,7 +11,6 @@ __all__ = ('ScenarioTreeInstanceFactory',) import os -import time import posixpath import tempfile import shutil @@ -29,7 +28,6 @@ from pyomo.core.base.block import _BlockData from pyomo.common.dependencies import yaml, yaml_available, yaml_load_args from pyomo.common.plugin import ExtensionPoint -from pyomo.pysp.phutils import _OLD_OUTPUT from pyomo.pysp.util.misc import load_external_module from pyomo.pysp.scenariotree.tree_structure_model import \ (CreateAbstractScenarioTreeModel, diff --git a/pyomo/pysp/scenariotree/manager.py b/pyomo/pysp/scenariotree/manager.py index 0b753dc89e1..9054f848959 100644 --- a/pyomo/pysp/scenariotree/manager.py +++ b/pyomo/pysp/scenariotree/manager.py @@ -34,8 +34,7 @@ SolutionStatus) from pyomo.opt.parallel.manager import ActionHandle from pyomo.pysp.util.configured_object import PySPConfiguredObject -from pyomo.pysp.util.config import (PySPConfigValue, - PySPConfigBlock, +from pyomo.pysp.util.config import (PySPConfigBlock, safe_declare_common_option, safe_register_common_option, _domain_must_be_str, @@ -3485,7 +3484,7 @@ def _invoke_function_impl( (invocation_type == InvocationType.OnBundles): _get_worker_func = None - _inocation_type = None + _invocation_type = None if invocation_type == InvocationType.OnScenarios: _get_worker_func = self.get_worker_for_scenario _invocation_type = InvocationType.OnScenarios @@ -3563,7 +3562,7 @@ def _invoke_function_impl( % (invocation_type)) _get_worker_func = None - _inocation_type = None + _invocation_type = None if invocation_type == InvocationType.OnScenariosChained: _get_worker_func = self.get_worker_for_scenario _invocation_type = InvocationType.OnScenariosChained diff --git a/pyomo/pysp/scenariotree/manager_solver.py b/pyomo/pysp/scenariotree/manager_solver.py index 21c1ce2a6b0..574934cf1eb 100644 --- a/pyomo/pysp/scenariotree/manager_solver.py +++ b/pyomo/pysp/scenariotree/manager_solver.py @@ -15,21 +15,17 @@ # TODO: handle pyro as the solver manager when even when the # pyro scenario tree manager is used -import math import time import sys from pyutilib.pyro import shutdown_pyro_components from pyomo.opt import (SolverFactory, SolverStatus, - TerminationCondition, SolutionStatus) from pyomo.opt.base.solvers import OptSolver from pyomo.opt.parallel import SolverManagerFactory -from pyomo.pysp.util.config import (PySPConfigValue, - PySPConfigBlock, - safe_declare_common_option, - safe_register_common_option) +from pyomo.pysp.util.config import (PySPConfigBlock, + safe_declare_common_option) from pyomo.pysp.util.configured_object import \ PySPConfiguredObject from pyomo.pysp.scenariotree.preprocessor import \ @@ -43,7 +39,7 @@ ScenarioTreeManagerClientPyro, ScenarioTreeSolveResults) -from six import itervalues, iteritems +from six import itervalues # # The ScenarioTreeManagerSolver interface adds additional diff --git a/pyomo/pysp/scenariotree/manager_solver_worker_pyro.py b/pyomo/pysp/scenariotree/manager_solver_worker_pyro.py index a1f2d989c88..0ab810926df 100644 --- a/pyomo/pysp/scenariotree/manager_solver_worker_pyro.py +++ b/pyomo/pysp/scenariotree/manager_solver_worker_pyro.py @@ -10,13 +10,9 @@ __all__ = ("ScenarioTreeManagerSolverWorkerPyro",) -import time - -from pyomo.opt import (SolverFactory, - undefined) +from pyomo.opt import undefined from pyomo.pysp.util.configured_object import PySPConfiguredObject -from pyomo.pysp.util.config import (PySPConfigBlock, - safe_declare_common_option) +from pyomo.pysp.util.config import PySPConfigBlock from pyomo.pysp.scenariotree.manager_worker_pyro import \ ScenarioTreeManagerWorkerPyro from pyomo.pysp.scenariotree.manager_solver import \ diff --git a/pyomo/pysp/scenariotree/preprocessor.py b/pyomo/pysp/scenariotree/preprocessor.py index 4a2350b085d..33137f8d539 100644 --- a/pyomo/pysp/scenariotree/preprocessor.py +++ b/pyomo/pysp/scenariotree/preprocessor.py @@ -19,11 +19,7 @@ # simple_preprocessor, which in turn is invoked by the preprocess() # method of PyomoModel. from pyomo.core.base.objective import Objective -from pyomo.core.base.var import Var from pyomo.core.base.constraint import Constraint -from pyomo.core.base.sos import SOSConstraint -from pyomo.opt import ProblemFormat -from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver from pyomo.repn.standard_repn import (preprocess_block_objectives, preprocess_block_constraints, preprocess_constraint_data) diff --git a/pyomo/pysp/scenariotree/server_pyro.py b/pyomo/pysp/scenariotree/server_pyro.py index 6e90403d170..55135f3d86d 100644 --- a/pyomo/pysp/scenariotree/server_pyro.py +++ b/pyomo/pysp/scenariotree/server_pyro.py @@ -13,11 +13,8 @@ import os import six -from six import iteritems import sys import socket -import copy -import argparse import logging import traceback import base64 @@ -26,26 +23,18 @@ except: #pragma:nocover import pickle -import pyutilib.misc -from pyutilib.misc import PauseGC +from pyutilib.misc import Bunch from pyomo.common.dependencies import attempt_import, dill, dill_available from pyomo.common import pyomo_command -from pyomo.opt import (SolverFactory, - TerminationCondition, - SolutionStatus) - -from pyomo.opt.parallel.manager import ActionManagerError from pyomo.pysp.util.misc import (parse_command_line, launch_command, load_external_module) from pyomo.pysp.util.config import (PySPConfigValue, PySPConfigBlock, - safe_declare_common_option, safe_register_common_option, safe_register_unique_option, _domain_tuple_of_str) -from pyomo.pysp.util.configured_object import PySPConfiguredObject from pyomo.pysp.scenariotree.tree_structure import \ ScenarioTree from pyomo.pysp.scenariotree.instance_factory import \ @@ -158,7 +147,7 @@ def process(self, data): traceback.format_exc())) def _process(self, data): - data = pyutilib.misc.Bunch(**data) + data = Bunch(**data) result = None if not data.action.startswith('ScenarioTreeServerPyro_'): #with PauseGC() as pgc: diff --git a/pyomo/pysp/scenariotree/tree_structure.py b/pyomo/pysp/scenariotree/tree_structure.py index 1c7d74dd874..f4b1fb2f53a 100644 --- a/pyomo/pysp/scenariotree/tree_structure.py +++ b/pyomo/pysp/scenariotree/tree_structure.py @@ -28,11 +28,9 @@ from pyomo.common.collections import ComponentMap from pyomo.core import (value, minimize, maximize, Var, Expression, Block, - CounterLabeler, - Objective, SOSConstraint, Set, + Objective, SOSConstraint, ComponentUID) -from pyomo.core.base.block import (_BlockData, - generate_cuid_names) +from pyomo.core.base.block import generate_cuid_names from pyomo.core.base.sos import _SOSConstraintData from pyomo.repn import generate_standard_repn from pyomo.pysp.phutils import (BasicSymbolMap, @@ -42,7 +40,6 @@ extractComponentIndices, find_active_objective) -import six from six import iterkeys, iteritems, itervalues from six.moves import xrange @@ -1529,12 +1526,12 @@ def __init__(self, if tree_node_name not in node_stage_ids: raise ValueError("No stage is assigned to tree node=%s" - % (tree_node.name)) + % (tree_node_name)) stage_name = value(node_stage_ids[tree_node_name]) if stage_name not in self._stage_map: raise ValueError("Unknown stage=%s assigned to tree node=%s" - % (stage_name, tree_node.name)) + % (stage_name, tree_node_name)) node_stage = self._stage_map[stage_name] new_tree_node = ScenarioTreeNode( diff --git a/pyomo/pysp/scenariotree/util.py b/pyomo/pysp/scenariotree/util.py index 1cb7535cb80..98acc695922 100644 --- a/pyomo/pysp/scenariotree/util.py +++ b/pyomo/pysp/scenariotree/util.py @@ -1,8 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import hashlib import uuid -import six - pysp_namespace_hash = hashlib.md5('pysp'.encode()) def _compute_namespace(node_name): node_namespace_hash = pysp_namespace_hash.copy() diff --git a/pyomo/pysp/solvers/admm.py b/pyomo/pysp/solvers/admm.py index 91d4924557a..ab29b66532a 100644 --- a/pyomo/pysp/solvers/admm.py +++ b/pyomo/pysp/solvers/admm.py @@ -11,7 +11,6 @@ import sys import time import math -import array # TODO: run y update on the server side so that bounds can be # returned efficiently if enabled diff --git a/pyomo/pysp/solvers/benders.py b/pyomo/pysp/solvers/benders.py index 9950288971e..049b05e1831 100644 --- a/pyomo/pysp/solvers/benders.py +++ b/pyomo/pysp/solvers/benders.py @@ -46,7 +46,6 @@ from pyomo.pysp.util.config import (PySPConfigValue, PySPConfigBlock, safe_register_common_option, - safe_register_unique_option, safe_declare_common_option, safe_declare_unique_option, _domain_percent, @@ -94,7 +93,7 @@ def EXTERNAL_activate_rootnode_costs(manager, assert len(manager.scenario_tree.stages) == 2 assert scenario in manager.scenario_tree.scenarios rootnode = manager.scenario_tree.findRootNode() - nodecost_var = instance.find_component( + nodecost_var = scenario._instance.find_component( rootnode._cost_variable[0])[rootnode._cost_variable[1]] scenario._instance.find_component( "PYSP_BENDERS_NODE_COST_TERM_"+rootnode.name).set_value(nodecost_var) diff --git a/pyomo/pysp/solvers/ddsip.py b/pyomo/pysp/solvers/ddsip.py index 62dd4979083..953d57257cc 100644 --- a/pyomo/pysp/solvers/ddsip.py +++ b/pyomo/pysp/solvers/ddsip.py @@ -16,14 +16,12 @@ import os import sys import time -import shutil import logging import traceback import pyutilib.subprocess import pyutilib.services -from pyomo.core import maximize from pyomo.opt import (TerminationCondition, SolverStatus, SolutionStatus) @@ -38,11 +36,8 @@ _domain_must_be_str) from pyomo.pysp.util.misc import (parse_command_line, launch_command) -from pyomo.pysp.scenariotree.manager import \ - (InvocationType, - ScenarioTreeManagerFactory) +from pyomo.pysp.scenariotree.manager import ScenarioTreeManagerFactory import pyomo.pysp.convert.ddsip -from pyomo.pysp.embeddedsp import EmbeddedSP from pyomo.pysp.solvers.spsolver import (SPSolverResults, SPSolverFactory) from pyomo.pysp.solvers.spsolvershellcommand import \ diff --git a/pyomo/pysp/solvers/ef.py b/pyomo/pysp/solvers/ef.py index 9f415e85b7a..256358a3fb5 100755 --- a/pyomo/pysp/solvers/ef.py +++ b/pyomo/pysp/solvers/ef.py @@ -11,17 +11,11 @@ import logging import sys import time -import itertools -import math - -import pyutilib.misc -from pyutilib.pyro import shutdown_pyro_components +import argparse import pyomo.solvers from pyomo.common.dependencies import yaml -from pyomo.core.base import ComponentUID from pyomo.opt import (SolverFactory, - TerminationCondition, UndefinedData, ProblemFormat, UnknownSolver, @@ -34,14 +28,10 @@ safe_register_unique_option, safe_declare_common_option, safe_declare_unique_option, - _domain_percent, _domain_nonnegative, - _domain_nonnegative_integer, - _domain_positive_integer, _domain_must_be_str, _domain_unit_interval, _domain_tuple_of_str, - _domain_tuple_of_str_or_dict, _output_options_group_title, _extension_options_group_title, _deprecated_options_group_title) @@ -49,9 +39,7 @@ launch_command, sort_extensions_by_precedence) from pyomo.pysp.phutils import find_active_objective -from pyomo.pysp.scenariotree.manager_solver import \ - (ScenarioTreeManager, - ScenarioTreeManagerClientSerial) +from pyomo.pysp.scenariotree.manager_solver import ScenarioTreeManagerClientSerial from pyomo.pysp.solutionioextensions import \ (IPySPSolutionSaverExtension, IPySPSolutionLoaderExtension) @@ -704,8 +692,7 @@ def runef_register_options(options=None): # Deprecated # - class _DeprecatedActivateJSONIOSolutionSaver( - pyutilib.misc.config.argparse.Action): + class _DeprecatedActivateJSONIOSolutionSaver(argparse.Action): def __init__(self, option_strings, dest, nargs=None, **kwargs): if nargs is not None: raise ValueError("nargs not allowed") @@ -786,6 +773,7 @@ def runef(options, if options.output_file is not None: with ExtensiveFormAlgorithm(sp, options) as ef: ef.build_ef() + ## !!THIS SEEMS LIKE A BUG!! - mrmundt # ef.write(filename) else: print("") diff --git a/pyomo/pysp/solvers/schuripopt.py b/pyomo/pysp/solvers/schuripopt.py index eefdc55b9b9..06eaf5c365a 100644 --- a/pyomo/pysp/solvers/schuripopt.py +++ b/pyomo/pysp/solvers/schuripopt.py @@ -23,16 +23,12 @@ from pyomo.core import ComponentUID from pyomo.opt import (ReaderFactory, ResultsFormat, - ProblemFormat, UndefinedData) from pyomo.pysp.util.configured_object import PySPConfiguredObject from pyomo.pysp.util.config import (PySPConfigValue, PySPConfigBlock, safe_register_common_option, safe_register_unique_option, - safe_declare_common_option, - safe_declare_unique_option, - _domain_must_be_str, _domain_tuple_of_str_or_dict) from pyomo.pysp.util.misc import (parse_command_line, launch_command) @@ -52,9 +48,6 @@ _write_scenario_nl, _write_problem_list_file) -from six.moves import xrange -# use fast version of pickle (python 2 or 3) -from six.moves import cPickle as pickle # generate an absolute path to this file thisfile = os.path.abspath(__file__) diff --git a/pyomo/pysp/solvers/sd.py b/pyomo/pysp/solvers/sd.py index fffa70b24e5..49636ed86f9 100644 --- a/pyomo/pysp/solvers/sd.py +++ b/pyomo/pysp/solvers/sd.py @@ -40,13 +40,11 @@ from pyomo.pysp.util.config import (PySPConfigValue, PySPConfigBlock, safe_register_common_option, - safe_register_unique_option, safe_declare_common_option, safe_declare_unique_option, _domain_positive, _domain_positive_integer, - _domain_nonnegative_integer, - _domain_must_be_str) + _domain_nonnegative_integer) from pyomo.pysp.util.misc import (parse_command_line, launch_command) from pyomo.pysp.scenariotree.manager import \ diff --git a/pyomo/pysp/solvers/spsolver.py b/pyomo/pysp/solvers/spsolver.py index 6dc0c38bad3..592899c8512 100644 --- a/pyomo/pysp/solvers/spsolver.py +++ b/pyomo/pysp/solvers/spsolver.py @@ -16,8 +16,6 @@ import time import logging -import pyutilib.misc - from pyomo.opt import UndefinedData from pyomo.core import ComponentUID from pyomo.pysp.embeddedsp import EmbeddedSP diff --git a/pyomo/pysp/tests/benders/test_benders.py b/pyomo/pysp/tests/benders/test_benders.py index 66f76bc087c..5b7c8f39830 100644 --- a/pyomo/pysp/tests/benders/test_benders.py +++ b/pyomo/pysp/tests/benders/test_benders.py @@ -8,23 +8,17 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys -import socket import os from os.path import join, dirname, abspath import time import subprocess -import pyutilib.services import pyutilib.th as unittest from pyutilib.pyro import using_pyro3, using_pyro4 from pyomo.pysp.util.misc import (_get_test_nameserver, _get_test_dispatcher, _poll, _kill) -from pyomo.environ import * - -from six import StringIO thisdir = dirname(abspath(__file__)) baselineDir = join(thisdir, "baselines") diff --git a/pyomo/pysp/tests/convert/piecewise_model.py b/pyomo/pysp/tests/convert/piecewise_model.py index a21ad8d7b8a..15612776f02 100644 --- a/pyomo/pysp/tests/convert/piecewise_model.py +++ b/pyomo/pysp/tests/convert/piecewise_model.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Param, Expression, Objective, Constraint, Piecewise, sum_product, inequality from pyomo.pysp.annotations import (StochasticConstraintBoundsAnnotation, StochasticConstraintBodyAnnotation, StochasticObjectiveAnnotation) diff --git a/pyomo/pysp/tests/convert/piecewise_model_alt.py b/pyomo/pysp/tests/convert/piecewise_model_alt.py index 9b949ff7d8f..115d4710b78 100644 --- a/pyomo/pysp/tests/convert/piecewise_model_alt.py +++ b/pyomo/pysp/tests/convert/piecewise_model_alt.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import Constraint from pyomo.pysp.annotations import (StochasticConstraintBoundsAnnotation, StochasticConstraintBodyAnnotation, StochasticObjectiveAnnotation) diff --git a/pyomo/pysp/tests/convert/piecewise_model_embedded.py b/pyomo/pysp/tests/convert/piecewise_model_embedded.py index 37ad37cb417..ae3fc4f6266 100644 --- a/pyomo/pysp/tests/convert/piecewise_model_embedded.py +++ b/pyomo/pysp/tests/convert/piecewise_model_embedded.py @@ -1,4 +1,14 @@ -import pyomo.environ as aml +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +import pyomo.environ as pyo from pyomo.pysp.embeddedsp import (EmbeddedSP, StochasticDataAnnotation, TableDistribution, @@ -7,47 +17,47 @@ def create_embedded(): - model = aml.ConcreteModel() - model.d1 = aml.Param(mutable=True, initialize=0) - model.d2 = aml.Param(mutable=True, initialize=0) - model.d3 = aml.Param(mutable=True, initialize=0) - model.d4 = aml.Param(mutable=True, initialize=0) + model = pyo.ConcreteModel() + model.d1 = pyo.Param(mutable=True, initialize=0) + model.d2 = pyo.Param(mutable=True, initialize=0) + model.d3 = pyo.Param(mutable=True, initialize=0) + model.d4 = pyo.Param(mutable=True, initialize=0) # first stage - model.x = aml.Var(bounds=(0,10)) + model.x = pyo.Var(bounds=(0,10)) # first stage derived - model.y = aml.Expression(expr=model.x + 1) - model.fx = aml.Var() + model.y = pyo.Expression(expr=model.x + 1) + model.fx = pyo.Var() # second stage - model.z = aml.Var(bounds=(-10, 10)) + model.z = pyo.Var(bounds=(-10, 10)) # second stage derived - model.q = aml.Expression(expr=model.z**2) - model.fz = aml.Var() - model.r = aml.Var() + model.q = pyo.Expression(expr=model.z**2) + model.fz = pyo.Var() + model.r = pyo.Var() # stage costs - model.StageCost = aml.Expression([1,2]) + model.StageCost = pyo.Expression([1,2]) model.StageCost.add(1, model.fx) model.StageCost.add(2, -model.fz + model.r + model.d1) - model.o = aml.Objective(expr=aml.sum_product(model.StageCost)) + model.o = pyo.Objective(expr=pyo.sum_product(model.StageCost)) - model.c_first_stage = aml.Constraint(expr= model.x >= 0) + model.c_first_stage = pyo.Constraint(expr= model.x >= 0) # test our handling of intermediate variables that # are created by Piecewise but can not necessarily # be declared on the scenario tree - model.p_first_stage = aml.Piecewise(model.fx, model.x, + model.p_first_stage = pyo.Piecewise(model.fx, model.x, pw_pts=[0.,2.,5.,7.,10.], pw_constr_type='EQ', pw_repn='INC', f_rule=[10.,10.,9.,10.,10.], force_pw=True) - model.c_second_stage = aml.Constraint(expr= model.x + model.r * model.d2 >= -100) - model.cL_second_stage = aml.Constraint(expr= model.d3 >= -model.r) - model.cU_second_stage = aml.Constraint(expr= model.r <= 0) + model.c_second_stage = pyo.Constraint(expr= model.x + model.r * model.d2 >= -100) + model.cL_second_stage = pyo.Constraint(expr= model.d3 >= -model.r) + model.cU_second_stage = pyo.Constraint(expr= model.r <= 0) # exercise more of the code by making this an indexed # block - model.p_second_stage = aml.Piecewise([1], model.fz, model.z, + model.p_second_stage = pyo.Piecewise([1], model.fz, model.z, pw_pts=[-10,-5.,0.,5.,10.], pw_constr_type='EQ', pw_repn='INC', diff --git a/pyomo/pysp/tests/convert/test_schuripopt.py b/pyomo/pysp/tests/convert/test_schuripopt.py index f42ebef5a3c..09ff3bf7b68 100644 --- a/pyomo/pysp/tests/convert/test_schuripopt.py +++ b/pyomo/pysp/tests/convert/test_schuripopt.py @@ -11,7 +11,6 @@ import os from os.path import join, dirname, abspath import time -import difflib import filecmp import shutil import subprocess @@ -22,9 +21,6 @@ _get_test_dispatcher, _poll, _kill) -import pyomo.environ - -from six import StringIO thisdir = dirname(abspath(__file__)) baselinedir = os.path.join(thisdir, "schuripopt_baselines") diff --git a/pyomo/pysp/tests/convert/test_smps.py b/pyomo/pysp/tests/convert/test_smps.py index 6ca6b65299a..b29c3ac463d 100644 --- a/pyomo/pysp/tests/convert/test_smps.py +++ b/pyomo/pysp/tests/convert/test_smps.py @@ -17,15 +17,13 @@ import shutil import subprocess import sys -import pyutilib.subprocess -import pyutilib.services +from pyutilib.subprocess import run import pyutilib.th as unittest from pyutilib.pyro import using_pyro3, using_pyro4 from pyomo.pysp.util.misc import (_get_test_nameserver, _get_test_dispatcher, _poll, _kill) -from pyomo.environ import * from six import StringIO @@ -90,7 +88,7 @@ def _get_cmd(self, def _run_bad_conversion_test(self, *args, **kwds): cmd, output_dir = self._get_cmd(*args, **kwds) outfile = output_dir+".out" - rc = pyutilib.subprocess.run(cmd, outfile=outfile) + rc = run(cmd, outfile=outfile) self.assertNotEqual(rc[0], 0) self._assert_contains( outfile, @@ -153,7 +151,7 @@ def test_too_many_declarations(self): cmd, output_dir = self._get_cmd( join(thisdir, "model_too_many_declarations.py")) outfile = output_dir+".out" - rc = pyutilib.subprocess.run(cmd, outfile=outfile) + rc = run(cmd, outfile=outfile) self.assertNotEqual(rc[0], 0) self._assert_contains( outfile, @@ -172,7 +170,7 @@ def test_bad_component_type(self): cmd, output_dir = self._get_cmd( join(thisdir, "model_bad_component_type.py")) outfile = output_dir+".out" - rc = pyutilib.subprocess.run(cmd, outfile=outfile) + rc = run(cmd, outfile=outfile) self.assertNotEqual(rc[0], 0) self._assert_contains( outfile, @@ -188,7 +186,7 @@ def test_unsupported_variable_bounds(self): cmd, output_dir = self._get_cmd( join(thisdir, "model_unsupported_variable_bounds.py")) outfile = output_dir+".out" - rc = pyutilib.subprocess.run(cmd, outfile=outfile) + rc = run(cmd, outfile=outfile) self.assertNotEqual(rc[0], 0) self._assert_contains( outfile, diff --git a/pyomo/pysp/tests/convert/test_smps_embedded.py b/pyomo/pysp/tests/convert/test_smps_embedded.py index fe19ace5096..f28151c5d6c 100644 --- a/pyomo/pysp/tests/convert/test_smps_embedded.py +++ b/pyomo/pysp/tests/convert/test_smps_embedded.py @@ -9,7 +9,6 @@ # ___________________________________________________________________________ import sys -import re import os import tempfile from os.path import join, dirname, abspath @@ -17,7 +16,7 @@ import filecmp import shutil -import pyutilib.misc +from pyutilib.misc import import_file import pyutilib.th as unittest from six import StringIO @@ -27,14 +26,14 @@ pysp_examples_dir = \ join(dirname(dirname(dirname(dirname(thisdir)))), "examples", "pysp") -import pyomo.environ -import pyomo.environ as aml +import pyomo.environ as pyo from pyomo.pysp.embeddedsp import (EmbeddedSP, StochasticDataAnnotation, TableDistribution, UniformDistribution, StageCostAnnotation, VariableStageAnnotation) +from pyomo.pysp.convert.smps import convert_embedded baa99_basemodel = None piecewise_model_embedded = None @@ -46,13 +45,13 @@ def setUpModule(): fname = os.path.join(pysp_examples_dir, "baa99", "baa99_basemodel.py") if os.path.exists(fname+"c"): os.remove(fname+"c") - baa99_basemodel = pyutilib.misc.import_file(fname) + baa99_basemodel = import_file(fname) if "piecewise_model_embedded" in sys.modules: del sys.modules["piecewise_model_embedded"] fname = os.path.join(thisdir, "piecewise_model_embedded.py") if os.path.exists(fname+"c"): os.remove(fname+"c") - piecewise_model_embedded = pyutilib.misc.import_file(fname) + piecewise_model_embedded = import_file(fname) def tearDownModule(): global baa99_basemodel @@ -78,18 +77,18 @@ def tearDownClass(cls): cls.tmpdir = None def _get_base_model(self): - model = aml.ConcreteModel() - model.x = aml.Var() - model.y = aml.Var() - model.d1 = aml.Param(mutable=True, initialize=0.0) - model.d2 = aml.Param(mutable=True, initialize=0.0) - model.d3 = aml.Param(mutable=True, initialize=0.0) - model.cost = aml.Expression([1,2]) + model = pyo.ConcreteModel() + model.x = pyo.Var() + model.y = pyo.Var() + model.d1 = pyo.Param(mutable=True, initialize=0.0) + model.d2 = pyo.Param(mutable=True, initialize=0.0) + model.d3 = pyo.Param(mutable=True, initialize=0.0) + model.cost = pyo.Expression([1,2]) model.cost[1].expr = model.x model.cost[2].expr = model.d1*model.y - model.o = aml.Objective(expr= model.cost[1]+model.cost[2]) - model.c1 = aml.Constraint(expr= model.x >= 0) - model.c2 = aml.Constraint(expr= model.y*model.d2 >= model.d3) + model.o = pyo.Objective(expr= model.cost[1]+model.cost[2]) + model.c1 = pyo.Constraint(expr= model.x >= 0) + model.c2 = pyo.Constraint(expr= model.y*model.d2 >= model.d3) model.varstage = VariableStageAnnotation() model.varstage.declare(model.x, 1) model.varstage.declare(model.y, 2) @@ -115,9 +114,7 @@ def test_makes_directory(self): shutil.rmtree(tmpdir, ignore_errors=True) self.assertFalse(os.path.exists(tmpdir)) sp = EmbeddedSP(self._get_base_model()) - pyomo.pysp.convert.smps.convert_embedded(tmpdir, - 'test', - sp) + convert_embedded(tmpdir, 'test', sp) self.assertTrue(os.path.exists(tmpdir)) shutil.rmtree(tmpdir, ignore_errors=True) @@ -125,9 +122,7 @@ def test_too_many_stages(self): sp = EmbeddedSP(self._get_base_model()) sp.time_stages = [1,2,3] with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual(str(cm.exception), ("SMPS conversion does not yet handle more " "than 2 time-stages")) @@ -144,9 +139,7 @@ class _Junk(object): sp.has_stochastic_variable_bounds = True self.assertEqual(sp.has_stochastic_variable_bounds, True) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual(str(cm.exception), ("Problems with stochastic variables bounds " "can not be converted into an embedded " @@ -157,9 +150,7 @@ def test_nonlinear_stoch_objective(self): model.cost[2].expr = model.y**2 + model.d1 sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertTrue(str(cm.exception).startswith( "Cannot output embedded SP representation for component " "'o'. The embedded SMPS writer does not yet handle " @@ -170,9 +161,7 @@ def test_stoch_data_too_many_uses_objective(self): model.cost[2].expr = model.d1*model.y + model.d1 sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Cannot output embedded SP representation for component " @@ -190,9 +179,7 @@ def test_stoch_data_nontrivial_expression_objective1(self): model.cost[2].expr = -model.d1*model.y sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertTrue(str(cm.exception).startswith( "Cannot output embedded SP representation for component " "'o'. The embedded SMPS writer does not yet handle the " @@ -204,16 +191,14 @@ def test_stoch_data_nontrivial_expression_objective1(self): def test_stoch_data_nontrivial_expression_objective2(self): model = self._get_base_model() - model.q = aml.Param(mutable=True, initialize=0.0) + model.q = pyo.Param(mutable=True, initialize=0.0) model.stochdata.declare( model.q, distribution=TableDistribution([0.0,1.0])) model.cost[2].expr = (model.d1+model.q)*model.y sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Cannot output embedded SP representation for component " @@ -232,9 +217,7 @@ def test_bad_distribution_objective(self): distribution=UniformDistribution(0.0,1.0)) sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Invalid distribution type 'UniformDistribution' for stochastic " @@ -247,9 +230,7 @@ def test_nonlinear_stoch_constraint(self): model.c2._body = model.d2*model.y**2 sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertTrue(str(cm.exception).startswith( "Cannot output embedded SP representation for component " "'c2'. The embedded SMPS writer does not yet handle " @@ -257,16 +238,14 @@ def test_nonlinear_stoch_constraint(self): def test_stoch_constraint_body_constant(self): model = self._get_base_model() - model.q = aml.Param(mutable=True, initialize=0.0) + model.q = pyo.Param(mutable=True, initialize=0.0) model.stochdata.declare( model.q, distribution=TableDistribution([0.0,1.0])) model.c2._body = model.d2*model.y + model.q sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Cannot output embedded SP representation for component " @@ -279,16 +258,14 @@ def test_stoch_constraint_body_constant(self): def test_stoch_range_constraint(self): model = self._get_base_model() - model.q = aml.Param(mutable=True, initialize=0.0) + model.q = pyo.Param(mutable=True, initialize=0.0) model.stochdata.declare( model.q, distribution=TableDistribution([0.0,1.0])) - model.c3 = aml.Constraint(expr=aml.inequality(model.q, model.y, 0)) + model.c3 = pyo.Constraint(expr=pyo.inequality(model.q, model.y, 0)) sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Cannot output embedded SP representation for component " @@ -300,9 +277,7 @@ def test_stoch_data_too_many_uses_constraint(self): model.c2._lower = model.d2 sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Cannot output embedded SP representation for component " @@ -320,9 +295,7 @@ def test_stoch_data_nontrivial_expression_constraint1(self): model.c2._body = -model.d2*model.y sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertTrue(str(cm.exception).startswith( "Cannot output embedded SP representation for component " "'c2'. The embedded SMPS writer does not yet handle the " @@ -334,16 +307,14 @@ def test_stoch_data_nontrivial_expression_constraint1(self): def test_stoch_data_nontrivial_expression_constraint2(self): model = self._get_base_model() - model.q = aml.Param(mutable=True, initialize=0.0) + model.q = pyo.Param(mutable=True, initialize=0.0) model.stochdata.declare( model.q, distribution=TableDistribution([0.0,1.0])) model.c2._body = (model.d2+model.q)*model.y sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Cannot output embedded SP representation for component " @@ -362,9 +333,7 @@ def test_bad_distribution_constraint(self): distribution=UniformDistribution(0.0,1.0)) sp = EmbeddedSP(model) with self.assertRaises(ValueError) as cm: - pyomo.pysp.convert.smps.convert_embedded(self.tmpdir, - 'test', - sp) + convert_embedded(self.tmpdir, 'test', sp) self.assertEqual( str(cm.exception), ("Invalid distribution type 'UniformDistribution' for stochastic " @@ -419,10 +388,7 @@ def _run(self, sp, basename, **kwds): shutil.rmtree(output_directory, ignore_errors=True) os.makedirs(output_directory) - pyomo.pysp.convert.smps.convert_embedded(output_directory, - basename, - sp, - **kwds) + convert_embedded(output_directory, basename, sp, **kwds) return output_directory def _get_baa99_sp(self): diff --git a/pyomo/pysp/tests/convert/utils.py b/pyomo/pysp/tests/convert/utils.py index 29b38e59aa6..b5e7d17435c 100644 --- a/pyomo/pysp/tests/convert/utils.py +++ b/pyomo/pysp/tests/convert/utils.py @@ -1,12 +1,21 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + from pyomo.core import * from pyomo.pysp.scenariotree.tree_structure_model import \ ScenarioTreeModelFromNetworkX -from pyomo.pysp.annotations import \ - (ConstraintStageAnnotation, - StochasticConstraintBoundsAnnotation, - StochasticConstraintBodyAnnotation, - StochasticObjectiveAnnotation, - StochasticVariableBoundsAnnotation) +from pyomo.pysp.annotations import (ConstraintStageAnnotation, + StochasticConstraintBoundsAnnotation, + StochasticConstraintBodyAnnotation, + StochasticObjectiveAnnotation, + StochasticVariableBoundsAnnotation) def simple_twostage_scenario_tree(): from pyomo.pysp.scenariotree.tree_structure_model \ @@ -53,7 +62,7 @@ def simple_threestage_scenario_tree(): st_model.StageCost[second_stage] = 'StageCost[2]' st_model.StageVariables[second_stage].add('y') # Third Stage - st_model.StageCost[thrid_stage] = 'StageCost[3]' + st_model.StageCost[third_stage] = 'StageCost[3]' st_model.StageVariables[second_stage].add('z') return st_model diff --git a/pyomo/pysp/tests/evalxhat/test_evaluate_xhat.py b/pyomo/pysp/tests/evalxhat/test_evaluate_xhat.py index ef91d1fc337..b2660d1c87b 100644 --- a/pyomo/pysp/tests/evalxhat/test_evaluate_xhat.py +++ b/pyomo/pysp/tests/evalxhat/test_evaluate_xhat.py @@ -8,24 +8,18 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys -import socket import os from os.path import join, dirname, abspath import time import subprocess import shutil -import pyutilib.services import pyutilib.th as unittest from pyutilib.pyro import using_pyro3, using_pyro4 from pyomo.pysp.util.misc import (_get_test_nameserver, _get_test_dispatcher, _poll, _kill) -from pyomo.environ import * - -from six import StringIO networkx_available = False try: diff --git a/pyomo/pysp/tests/examples/ef_checker.py b/pyomo/pysp/tests/examples/ef_checker.py index 265bbb430d0..60b880e82af 100644 --- a/pyomo/pysp/tests/examples/ef_checker.py +++ b/pyomo/pysp/tests/examples/ef_checker.py @@ -325,5 +325,4 @@ def main(args=None): if __name__ == "__main__": - import sys main(args=sys.argv[1:]) diff --git a/pyomo/pysp/tests/examples/ph_checker.py b/pyomo/pysp/tests/examples/ph_checker.py index 0caffd2ab36..416ebc02c68 100644 --- a/pyomo/pysp/tests/examples/ph_checker.py +++ b/pyomo/pysp/tests/examples/ph_checker.py @@ -625,5 +625,4 @@ def main(args=None): if __name__ == "__main__": - import sys sys.exit(main(args=sys.argv[1:])) diff --git a/pyomo/pysp/tests/examples/rapper_based.py b/pyomo/pysp/tests/examples/rapper_based.py index 222203b7445..69a0ee40b0f 100644 --- a/pyomo/pysp/tests/examples/rapper_based.py +++ b/pyomo/pysp/tests/examples/rapper_based.py @@ -9,19 +9,13 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import collections -import math import sys - import pyutilib.th as unittest import tempfile -import sys import os import shutil -import json import pyomo.environ as pyo import pyomo.pysp.util.rapper as rapper -from pyomo.pysp.scenariotree.tree_structure_model import CreateAbstractScenarioTreeModel import pyomo as pyomoroot solvername = "ipopt" # could use almost any solver diff --git a/pyomo/pysp/tests/examples/test_ef.py b/pyomo/pysp/tests/examples/test_ef.py index 380a0c417c5..910a199cecf 100644 --- a/pyomo/pysp/tests/examples/test_ef.py +++ b/pyomo/pysp/tests/examples/test_ef.py @@ -11,7 +11,6 @@ import sys import fnmatch import os -import subprocess from os.path import abspath, dirname, join, basename try: @@ -356,7 +355,6 @@ def setUpClass(cls): _disable_stdout_test = False - import sys if '--include' in sys.argv: _test_name_wildcard_include = [] while '--include' in sys.argv: diff --git a/pyomo/pysp/tests/examples/test_examples.py b/pyomo/pysp/tests/examples/test_examples.py index f52872a9984..ec61beba5f7 100644 --- a/pyomo/pysp/tests/examples/test_examples.py +++ b/pyomo/pysp/tests/examples/test_examples.py @@ -13,13 +13,10 @@ from os.path import join, dirname, abspath import time import subprocess -import difflib -import filecmp import shutil import sys from pyutilib.pyro import using_pyro3, using_pyro4 -import pyutilib.services import pyutilib.th as unittest from pyomo.common.dependencies import networkx_available as have_networkx @@ -27,10 +24,8 @@ _get_test_dispatcher, _poll, _kill) -import pyomo.environ from pyomo.opt import check_available_solvers -from six import StringIO have_dot = True try: diff --git a/pyomo/pysp/tests/examples/test_model/continuous/ReferenceModel.py b/pyomo/pysp/tests/examples/test_model/continuous/ReferenceModel.py index 66272a8949e..e4696ea798d 100644 --- a/pyomo/pysp/tests/examples/test_model/continuous/ReferenceModel.py +++ b/pyomo/pysp/tests/examples/test_model/continuous/ReferenceModel.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, Param, Expression model = AbstractModel() diff --git a/pyomo/pysp/tests/examples/test_model/discrete/ReferenceModel.py b/pyomo/pysp/tests/examples/test_model/discrete/ReferenceModel.py index a887c6cf6b8..3dc9798adc4 100644 --- a/pyomo/pysp/tests/examples/test_model/discrete/ReferenceModel.py +++ b/pyomo/pysp/tests/examples/test_model/discrete/ReferenceModel.py @@ -8,12 +8,11 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, Param, Constraint, Objective, Expression, Boolean model = AbstractModel() model.x = Var(within=Boolean) - model.y = Var() model.c = Param() diff --git a/pyomo/pysp/tests/examples/test_model/feas/ReferenceModel.py b/pyomo/pysp/tests/examples/test_model/feas/ReferenceModel.py index ee800411975..d1ffe11d3a8 100644 --- a/pyomo/pysp/tests/examples/test_model/feas/ReferenceModel.py +++ b/pyomo/pysp/tests/examples/test_model/feas/ReferenceModel.py @@ -7,52 +7,52 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ - -from pyomo.environ import * - -model = AbstractModel() - -model.xIndex = RangeSet(2) - -model.x = Var(model.xIndex, within=Boolean) -model.y = Var() -model.slackbool = Var(within=Boolean) - -model.a = Param() -model.b = Param() -model.c = Param() -model.M = Param() - -# Making x1=0 infeasible in scenario 2. Note that default is b1 = 0, b2 = 1, so this is -# trivially true in scenario 1 and makes x1=0 infeasible in scenario 2. -def test_rule(model): - return model.b <= model.b * model.x[1] -model.test = Constraint(rule=test_rule) - -# you can get y=one with anything, but for y=zero, you need all x zero -def y_is_geq_x_rule(model, i): - return model.y >= model.x[i] -model.y_is_geq_x = Constraint(model.xIndex, rule=y_is_geq_x_rule) - -# you can get y=zero with anything, but for y=one, you need at least one one -# but if you don't have at least one one, you have to have y=0 -def y_is_leq_sum_x_rule(model): - return model.y <= sum_product(model.x) -model.y_is_leq_sum_x = Constraint(rule=y_is_leq_sum_x_rule) - -def slacker_rule(model): - return model.a * model.y + model.slackbool >= model.b -model.slacker = Constraint(rule=slacker_rule) - -def FirstStageCost_rule(model): - return 0 -model.FirstStageCost = Expression(rule=FirstStageCost_rule) - -def SecondStageCost_rule(model): - return model.c * model.y + model.M * model.slackbool + sum_product(model.x) -model.SecondStageCost = Expression(rule=SecondStageCost_rule) - -def Obj_rule(model): - #return model.FirstStageCost + model.SecondStageCost - return model.FirstStageCost + model.SecondStageCost -model.Obj = Objective(rule=Obj_rule) + +from pyomo.environ import AbstractModel, RangeSet, Var, Param, Constraint, Objective, Expression, Boolean, sum_product + +model = AbstractModel() + +model.xIndex = RangeSet(2) + +model.x = Var(model.xIndex, within=Boolean) +model.y = Var() +model.slackbool = Var(within=Boolean) + +model.a = Param() +model.b = Param() +model.c = Param() +model.M = Param() + +# Making x1=0 infeasible in scenario 2. Note that default is b1 = 0, b2 = 1, so this is +# trivially true in scenario 1 and makes x1=0 infeasible in scenario 2. +def test_rule(model): + return model.b <= model.b * model.x[1] +model.test = Constraint(rule=test_rule) + +# you can get y=one with anything, but for y=zero, you need all x zero +def y_is_geq_x_rule(model, i): + return model.y >= model.x[i] +model.y_is_geq_x = Constraint(model.xIndex, rule=y_is_geq_x_rule) + +# you can get y=zero with anything, but for y=one, you need at least one one +# but if you don't have at least one one, you have to have y=0 +def y_is_leq_sum_x_rule(model): + return model.y <= sum_product(model.x) +model.y_is_leq_sum_x = Constraint(rule=y_is_leq_sum_x_rule) + +def slacker_rule(model): + return model.a * model.y + model.slackbool >= model.b +model.slacker = Constraint(rule=slacker_rule) + +def FirstStageCost_rule(model): + return 0 +model.FirstStageCost = Expression(rule=FirstStageCost_rule) + +def SecondStageCost_rule(model): + return model.c * model.y + model.M * model.slackbool + sum_product(model.x) +model.SecondStageCost = Expression(rule=SecondStageCost_rule) + +def Obj_rule(model): + #return model.FirstStageCost + model.SecondStageCost + return model.FirstStageCost + model.SecondStageCost +model.Obj = Objective(rule=Obj_rule) diff --git a/pyomo/pysp/tests/examples/test_model/slackpenalty/ReferenceModel.py b/pyomo/pysp/tests/examples/test_model/slackpenalty/ReferenceModel.py index b42fd79f40a..6e563c3b991 100644 --- a/pyomo/pysp/tests/examples/test_model/slackpenalty/ReferenceModel.py +++ b/pyomo/pysp/tests/examples/test_model/slackpenalty/ReferenceModel.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, Var, Param, Constraint, Objective, Expression, Boolean model = AbstractModel() diff --git a/pyomo/pysp/tests/examples/test_model/twovarslack/ReferenceModel.py b/pyomo/pysp/tests/examples/test_model/twovarslack/ReferenceModel.py index 261f4e9f273..1b0592ffb03 100644 --- a/pyomo/pysp/tests/examples/test_model/twovarslack/ReferenceModel.py +++ b/pyomo/pysp/tests/examples/test_model/twovarslack/ReferenceModel.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, RangeSet, Var, Param, Constraint, Objective, Expression, Boolean, sum_product model = AbstractModel() diff --git a/pyomo/pysp/tests/examples/test_ph.py b/pyomo/pysp/tests/examples/test_ph.py index a081f332542..3bd3c382386 100644 --- a/pyomo/pysp/tests/examples/test_ph.py +++ b/pyomo/pysp/tests/examples/test_ph.py @@ -26,7 +26,6 @@ import pyutilib.th as unittest from pyutilib.misc.comparison import open_possibly_compressed_file -import pyutilib.services from pyomo.common.dependencies import yaml_available from pyomo.pysp.util.misc import (_get_test_nameserver, _get_test_dispatcher, diff --git a/pyomo/pysp/tests/rapper/abstract_rapper_tester.py b/pyomo/pysp/tests/rapper/abstract_rapper_tester.py index 26abd574ae8..8ac05a23e9c 100644 --- a/pyomo/pysp/tests/rapper/abstract_rapper_tester.py +++ b/pyomo/pysp/tests/rapper/abstract_rapper_tester.py @@ -1,22 +1,28 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # Provide some test for rapper # Author: David L. Woodruff (Sept 2018) import pyutilib.th as unittest -import tempfile import sys import os -import shutil -import json -import pyomo.environ as pyo +from pyomo.environ import SolverFactory, TerminationCondition import pyomo.pysp.util.rapper as rapper -from pyomo.pysp.scenariotree.tree_structure_model import CreateAbstractScenarioTreeModel import pyomo as pyomoroot __author__ = 'David L. Woodruff ' __version__ = 1.5 solvername = "ipopt" # could use almost any solver -solver_available = pyo.SolverFactory(solvername).available(False) +solver_available = SolverFactory(solvername).available(False) class Test_abstract_rapper(unittest.TestCase): """ Test the rapper code.""" @@ -67,7 +73,7 @@ def test_Abstract_ef(self): phopts = None) ef_sol = stsolver.solve_ef(solvername) assert(ef_sol.solver.termination_condition \ - == pyo.TerminationCondition.optimal) + == TerminationCondition.optimal) # see also foo.py if __name__ == '__main__': diff --git a/pyomo/pysp/tests/rapper/rapper_tester.py b/pyomo/pysp/tests/rapper/rapper_tester.py index c428b0d3f34..8813ad7358e 100644 --- a/pyomo/pysp/tests/rapper/rapper_tester.py +++ b/pyomo/pysp/tests/rapper/rapper_tester.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # Provide some test for rapper; most are smoke because PySP is tested elsewhere # Author: David L. Woodruff (circa March 2017; Sept 2018; Feb 2020) @@ -6,8 +16,7 @@ import sys import os import shutil -import json -import pyomo.environ as pyo +from pyomo.environ import SolverFactory, TerminationCondition import pyomo.pysp.util.rapper as rapper from pyomo.pysp.scenariotree.tree_structure_model import CreateAbstractScenarioTreeModel import pyomo.pysp.plugins.csvsolutionwriter as csvw @@ -23,7 +32,7 @@ __version__ = 1.7 solvername = "ipopt" # could use almost any solver -solver_available = pyo.SolverFactory(solvername).available(False) +solver_available = SolverFactory(solvername).available(False) class Testrapper(unittest.TestCase): """ Test the rapper code.""" @@ -109,7 +118,7 @@ def test_ef_solve(self): tree_model = self.farmer_concrete_tree) ef_sol = stsolver.solve_ef(solvername) assert(ef_sol.solver.termination_condition \ - == pyo.TerminationCondition.optimal) + == TerminationCondition.optimal) for name, varval in stsolver.root_Var_solution(): #print (name, str(varval)) pass diff --git a/pyomo/pysp/tests/scenariotreemanager/dummy_model.py b/pyomo/pysp/tests/scenariotreemanager/dummy_model.py index 937db42bf07..d82ed037594 100644 --- a/pyomo/pysp/tests/scenariotreemanager/dummy_model.py +++ b/pyomo/pysp/tests/scenariotreemanager/dummy_model.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Expression, ConstraintList, Objective, sum_product def pysp_scenario_tree_model_callback(): from pyomo.pysp.scenariotree.tree_structure_model \ diff --git a/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanager.py b/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanager.py index 015d16822ee..d12ff13d46e 100644 --- a/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanager.py +++ b/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanager.py @@ -24,7 +24,6 @@ _ordered_dict_ = ordereddict.OrderedDict from pyutilib.pyro import using_pyro3, using_pyro4 -import pyutilib.services import pyutilib.th as unittest from pyomo.common.dependencies import dill, dill_available @@ -49,7 +48,7 @@ from pyomo.pysp.scenariotree.instance_factory import \ ScenarioTreeInstanceFactory -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Expression, Constraint, Objective, sum_product thisfile = os.path.abspath(__file__) thisdir = os.path.dirname(thisfile) diff --git a/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanagersolver.py b/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanagersolver.py index 527b527240d..828fcdf9c56 100644 --- a/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanagersolver.py +++ b/pyomo/pysp/tests/scenariotreemanager/test_scenariotreemanagersolver.py @@ -14,7 +14,6 @@ import subprocess from pyutilib.pyro import using_pyro3, using_pyro4 -import pyutilib.services import pyutilib.th as unittest from pyomo.common.dependencies import dill, dill_available as has_dill @@ -22,7 +21,6 @@ _get_test_dispatcher, _poll, _kill) -from pyomo.pysp.util.config import PySPConfigBlock from pyomo.pysp.scenariotree.manager import \ (ScenarioTreeManagerClientSerial, ScenarioTreeManagerClientPyro) @@ -31,9 +29,8 @@ from pyomo.pysp.scenariotree.manager_solver import \ (ScenarioTreeManagerSolverFactory, PySPFailedSolveStatus) -from pyomo.opt import undefined -import pyomo.environ as aml +import pyomo.environ as pyo from pyomo.common.dependencies import ( networkx, networkx_available as has_networkx @@ -143,17 +140,17 @@ def get_factory(): cost="t1_cost") tree.add_edge("r", "s"+str(i), weight=1.0/3) - model = aml.ConcreteModel() - model.x = aml.Var() - model.Y = aml.Var([1]) - model.stale = aml.Var(initialize=0.0) - model.fixed = aml.Var(initialize=0.0) + model = pyo.ConcreteModel() + model.x = pyo.Var() + model.Y = pyo.Var([1]) + model.stale = pyo.Var(initialize=0.0) + model.fixed = pyo.Var(initialize=0.0) model.fixed.fix() - model.p = aml.Param(mutable=True) - model.t0_cost = aml.Expression(expr=model.x) - model.t1_cost = aml.Expression(expr=model.Y[1]) - model.o = aml.Objective(expr=model.t0_cost + model.t1_cost) - model.c = aml.ConstraintList() + model.p = pyo.Param(mutable=True) + model.t0_cost = pyo.Expression(expr=model.x) + model.t1_cost = pyo.Expression(expr=model.Y[1]) + model.o = pyo.Objective(expr=model.t0_cost + model.t1_cost) + model.c = pyo.ConstraintList() model.c.add(model.x >= 1) model.c.add(model.Y[1] >= model.p) @@ -227,17 +224,17 @@ def get_factory(): cost="t1_cost") tree.add_edge("r", "s"+str(i), weight=1.0/3) - model = aml.ConcreteModel() - model.x = aml.Var() - model.Y = aml.Var([1], bounds=(None, 1)) - model.stale = aml.Var(initialize=0.0) - model.fixed = aml.Var(initialize=0.0) + model = pyo.ConcreteModel() + model.x = pyo.Var() + model.Y = pyo.Var([1], bounds=(None, 1)) + model.stale = pyo.Var(initialize=0.0) + model.fixed = pyo.Var(initialize=0.0) model.fixed.fix() - model.p = aml.Param(mutable=True) - model.t0_cost = aml.Expression(expr=model.x) - model.t1_cost = aml.Expression(expr=model.Y[1]) - model.o = aml.Objective(expr=model.t0_cost + model.t1_cost) - model.c = aml.ConstraintList() + model.p = pyo.Param(mutable=True) + model.t0_cost = pyo.Expression(expr=model.x) + model.t1_cost = pyo.Expression(expr=model.Y[1]) + model.o = pyo.Objective(expr=model.t0_cost + model.t1_cost) + model.c = pyo.ConstraintList() model.c.add(model.x >= 1) model.c.add(model.Y[1] >= model.p) @@ -341,17 +338,17 @@ def get_factory(): bundle="b"+str(i)) tree.add_edge("r", "s"+str(i), weight=1.0/3) - model = aml.ConcreteModel() - model.x = aml.Var() - model.Y = aml.Var([1]) - model.stale = aml.Var(initialize=0.0) - model.fixed = aml.Var(initialize=0.0) + model = pyo.ConcreteModel() + model.x = pyo.Var() + model.Y = pyo.Var([1]) + model.stale = pyo.Var(initialize=0.0) + model.fixed = pyo.Var(initialize=0.0) model.fixed.fix() - model.p = aml.Param(mutable=True) - model.t0_cost = aml.Expression(expr=model.x) - model.t1_cost = aml.Expression(expr=model.Y[1]) - model.o = aml.Objective(expr=model.t0_cost + model.t1_cost) - model.c = aml.ConstraintList() + model.p = pyo.Param(mutable=True) + model.t0_cost = pyo.Expression(expr=model.x) + model.t1_cost = pyo.Expression(expr=model.Y[1]) + model.o = pyo.Objective(expr=model.t0_cost + model.t1_cost) + model.c = pyo.ConstraintList() model.c.add(model.x >= 1) model.c.add(model.Y[1] >= model.p) @@ -435,17 +432,17 @@ def get_factory(): bundle="b"+str(i)) tree.add_edge("r", "s"+str(i), weight=1.0/3) - model = aml.ConcreteModel() - model.x = aml.Var() - model.Y = aml.Var([1], bounds=(None, 1)) - model.stale = aml.Var(initialize=0.0) - model.fixed = aml.Var(initialize=0.0) + model = pyo.ConcreteModel() + model.x = pyo.Var() + model.Y = pyo.Var([1], bounds=(None, 1)) + model.stale = pyo.Var(initialize=0.0) + model.fixed = pyo.Var(initialize=0.0) model.fixed.fix() - model.p = aml.Param(mutable=True) - model.t0_cost = aml.Expression(expr=model.x) - model.t1_cost = aml.Expression(expr=model.Y[1]) - model.o = aml.Objective(expr=model.t0_cost + model.t1_cost) - model.c = aml.ConstraintList() + model.p = pyo.Param(mutable=True) + model.t0_cost = pyo.Expression(expr=model.x) + model.t1_cost = pyo.Expression(expr=model.Y[1]) + model.o = pyo.Objective(expr=model.t0_cost + model.t1_cost) + model.c = pyo.ConstraintList() model.c.add(model.x >= 1) model.c.add(model.Y[1] >= model.p) diff --git a/pyomo/pysp/tests/test_benders.py b/pyomo/pysp/tests/test_benders.py index 4ba3ce7351f..d4d8b8b5f1d 100644 --- a/pyomo/pysp/tests/test_benders.py +++ b/pyomo/pysp/tests/test_benders.py @@ -27,8 +27,6 @@ # import pyutilib.th as unittest -import pyomo.opt - def filter_fn(line): tmp = line.strip() return tmp.startswith('WARNING') and 'CBC' in tmp diff --git a/pyomo/pysp/tests/unit/test_annotations.py b/pyomo/pysp/tests/unit/test_annotations.py index 8ecb46591b3..791c5cc7782 100644 --- a/pyomo/pysp/tests/unit/test_annotations.py +++ b/pyomo/pysp/tests/unit/test_annotations.py @@ -7,9 +7,10 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as aml +import pyomo.environ as pyo from pyomo.pysp.annotations import (locate_annotations, StageCostAnnotation, PySP_StageCostAnnotation, @@ -49,40 +50,40 @@ def test_deprecated(self): type(PySP_StochasticObjectiveAnnotation())) def _populate_block_with_vars_expressions(self, b): - b.x = aml.Var() - b.X1 = aml.Var([1]) - b.X2 = aml.Var([1]) - b.e = aml.Expression() - b.E1 = aml.Expression([1]) - b.E2 = aml.Expression([1]) + b.x = pyo.Var() + b.X1 = pyo.Var([1]) + b.X2 = pyo.Var([1]) + b.e = pyo.Expression() + b.E1 = pyo.Expression([1]) + b.E2 = pyo.Expression([1]) def _populate_block_with_vars(self, b): - b.x = aml.Var() - b.X1 = aml.Var([1]) - b.X2 = aml.Var([1]) + b.x = pyo.Var() + b.X1 = pyo.Var([1]) + b.X2 = pyo.Var([1]) def _populate_block_with_constraints(self, b): - b.x = aml.Var() - b.c = aml.Constraint(expr= b.x == 1) - b.C1 = aml.Constraint([1], rule=lambda m, i: m.x == 1) - b.C2 = aml.Constraint([1], rule=lambda m, i: m.x == 1) - b.C3 = aml.ConstraintList() + b.x = pyo.Var() + b.c = pyo.Constraint(expr= b.x == 1) + b.C1 = pyo.Constraint([1], rule=lambda m, i: m.x == 1) + b.C2 = pyo.Constraint([1], rule=lambda m, i: m.x == 1) + b.C3 = pyo.ConstraintList() b.C3.add(b.x == 1) def _populate_block_with_objectives(self, b): - b.x = aml.Var() - b.o = aml.Objective(expr= b.x + 1) - b.O1 = aml.Objective([1], rule=lambda m, i: m.x + 1) - b.O2 = aml.Objective([1], rule=lambda m, i: m.x + 1) + b.x = pyo.Var() + b.o = pyo.Objective(expr= b.x + 1) + b.O1 = pyo.Objective([1], rule=lambda m, i: m.x + 1) + b.O2 = pyo.Objective([1], rule=lambda m, i: m.x + 1) def _populate_block_with_params(self, b): - b.p = aml.Param(mutable=True ,initialize=0) - b.P1 = aml.Param([1], mutable=True, initialize=0) - b.P2 = aml.Param([1], mutable=True, initialize=0) + b.p = pyo.Param(mutable=True ,initialize=0) + b.P1 = pyo.Param([1], mutable=True, initialize=0) + b.P2 = pyo.Param([1], mutable=True, initialize=0) def test_multiple_declarations(self): - m = aml.ConcreteModel() - m.x = aml.Var() + m = pyo.ConcreteModel() + m.x = pyo.Var() a = StageCostAnnotation() a.declare(m, 1) a.declare(m.x, 1) @@ -90,9 +91,9 @@ def test_multiple_declarations(self): a.expand_entries() def test_locate_annotations(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() m.a = StageCostAnnotation() - m.b = aml.Block() + m.b = pyo.Block() m.b.a = StageCostAnnotation() self.assertEqual(locate_annotations(m, StageCostAnnotation), [('a', m.a), ('a', m.b.a)]) @@ -105,14 +106,14 @@ def test_locate_annotations(self): []) def test_stage_cost(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_vars_expressions(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_vars_expressions(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_vars_expressions(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_vars_expressions(b)) @@ -151,14 +152,14 @@ def test_stage_cost(self): ('B[1].e', 2), ('B[1].E1', 2), ('B[1].E2', 2)])) def test_variable_stage(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_vars_expressions(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_vars_expressions(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_vars_expressions(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_vars_expressions(b)) @@ -197,14 +198,14 @@ def test_variable_stage(self): ('B[1].e', (2,True)), ('B[1].E1', (2,True)), ('B[1].E2', (2,True))])) def test_constraint_stage(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_constraints(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_constraints(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_constraints(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_constraints(b)) @@ -235,14 +236,14 @@ def test_constraint_stage(self): ('B[1].c', 2), ('B[1].C1', 2), ('B[1].C2', 2), ('B[1].C3', 2)])) def test_stochastic_data(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_params(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_params(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_params(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_params(b)) @@ -270,14 +271,14 @@ def test_stochastic_data(self): ('B[1].p', 2), ('B[1].P1', 2), ('B[1].P2', 2)])) def test_constraint_bounds(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_constraints(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_constraints(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_constraints(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_constraints(b)) @@ -308,14 +309,14 @@ def test_constraint_bounds(self): ('B[1].C2', (False,True)), ('B[1].C3', (False,True))])) def test_stochastic_constraint_body(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_constraints(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_constraints(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_constraints(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_constraints(b)) @@ -344,14 +345,14 @@ def test_stochastic_constraint_body(self): ('B[1].c', 2), ('B[1].C1', 2), ('B[1].C2', 2), ('B[1].C3', 2)])) def test_stochastic_objective(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_objectives(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_objectives(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_objectives(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_objectives(b)) @@ -379,14 +380,14 @@ def test_stochastic_objective(self): ('B[1].o', (1,False)), ('B[1].O1', (1,False)), ('B[1].O2', (1,False))])) def test_stochastic_variable_bounds(self): - m = aml.ConcreteModel() + m = pyo.ConcreteModel() self._populate_block_with_vars(m) - m.b = aml.Block() + m.b = pyo.Block() self._populate_block_with_vars(m.b) - m.b_inactive = aml.Block() + m.b_inactive = pyo.Block() self._populate_block_with_vars(m.b_inactive) m.b_inactive.deactivate() - m.B = aml.Block([1], + m.B = pyo.Block([1], rule=lambda b: \ self._populate_block_with_vars(b)) diff --git a/pyomo/pysp/tests/unit/test_ph.py b/pyomo/pysp/tests/unit/test_ph.py index 111969e9267..7893a97f7cb 100644 --- a/pyomo/pysp/tests/unit/test_ph.py +++ b/pyomo/pysp/tests/unit/test_ph.py @@ -15,7 +15,6 @@ import os import sys import subprocess -import time from os.path import abspath, dirname try: @@ -38,7 +37,6 @@ # import pyutilib.misc import pyutilib.th as unittest -import pyutilib.services from pyutilib.pyro import using_pyro3, using_pyro4 from pyomo.pysp.util.misc import (_get_test_nameserver, @@ -49,6 +47,7 @@ import pyomo.pysp import pyomo.pysp.phinit import pyomo.pysp.ef_writer_script +import pyomo.environ _diff_tolerance = 1e-5 _diff_tolerance_relaxed = 1e-3 diff --git a/pyomo/pysp/tests/unit/testdata/ReferenceModel.py b/pyomo/pysp/tests/unit/testdata/ReferenceModel.py index d0015c24935..dd34bdc1e4e 100644 --- a/pyomo/pysp/tests/unit/testdata/ReferenceModel.py +++ b/pyomo/pysp/tests/unit/testdata/ReferenceModel.py @@ -1,4 +1,14 @@ -from pyomo.environ import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.environ import AbstractModel, Var, Param, Expression, Objective, Constraint model = AbstractModel() model.x = Var() diff --git a/pyomo/pysp/tests/unit/testdata/both_callbacks.py b/pyomo/pysp/tests/unit/testdata/both_callbacks.py index 3ea3fb3d6cb..f7cd1861ea6 100644 --- a/pyomo/pysp/tests/unit/testdata/both_callbacks.py +++ b/pyomo/pysp/tests/unit/testdata/both_callbacks.py @@ -1,4 +1,14 @@ -from pyomo.environ import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.environ import AbstractModel, Var, Param, Expression, Objective, Constraint model = AbstractModel() model.x = Var() diff --git a/pyomo/pysp/tests/unit/testdata/reference_test_model.py b/pyomo/pysp/tests/unit/testdata/reference_test_model.py index d0015c24935..dd34bdc1e4e 100644 --- a/pyomo/pysp/tests/unit/testdata/reference_test_model.py +++ b/pyomo/pysp/tests/unit/testdata/reference_test_model.py @@ -1,4 +1,14 @@ -from pyomo.environ import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.environ import AbstractModel, Var, Param, Expression, Objective, Constraint model = AbstractModel() model.x = Var() diff --git a/pyomo/pysp/tests/unit/testdata/reference_test_model_with_callback.py b/pyomo/pysp/tests/unit/testdata/reference_test_model_with_callback.py index 996bcd23984..36ebb7023ad 100644 --- a/pyomo/pysp/tests/unit/testdata/reference_test_model_with_callback.py +++ b/pyomo/pysp/tests/unit/testdata/reference_test_model_with_callback.py @@ -1,4 +1,14 @@ -from pyomo.environ import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.environ import AbstractModel, Var, Param, Expression, Objective, Constraint model = AbstractModel() model.x = Var() diff --git a/pyomo/pysp/tests/unit/testdata/reference_test_scenario_tree_model.py b/pyomo/pysp/tests/unit/testdata/reference_test_scenario_tree_model.py index 3dd8635d928..429c0e00fa4 100644 --- a/pyomo/pysp/tests/unit/testdata/reference_test_scenario_tree_model.py +++ b/pyomo/pysp/tests/unit/testdata/reference_test_scenario_tree_model.py @@ -1,7 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import os import tempfile -from pyomo.environ import * from pyomo.pysp.scenariotree.tree_structure_model import \ CreateAbstractScenarioTreeModel diff --git a/pyomo/pysp/util/configured_object.py b/pyomo/pysp/util/configured_object.py index ecf81ca53c7..53e3a05739c 100644 --- a/pyomo/pysp/util/configured_object.py +++ b/pyomo/pysp/util/configured_object.py @@ -11,13 +11,10 @@ __all__ = ('PySPConfiguredObject', 'PySPConfiguredExtension') import inspect -import copy -import sys from collections import defaultdict from pyomo.common.plugin import SingletonPlugin -from pyomo.pysp.util.config import (PySPConfigValue, - PySPConfigBlock, +from pyomo.pysp.util.config import (PySPConfigBlock, safe_declare_option, check_options_match, safe_declare_common_option, diff --git a/pyomo/pysp/util/misc.py b/pyomo/pysp/util/misc.py index d19c6ee9c39..9a904c030aa 100644 --- a/pyomo/pysp/util/misc.py +++ b/pyomo/pysp/util/misc.py @@ -17,23 +17,12 @@ import six import sys import subprocess -import traceback import inspect import argparse -# for profiling -try: - import cProfile as profile -except ImportError: - import profile -try: - import pstats - pstats_available=True -except ImportError: - pstats_available=False from pyutilib.misc import PauseGC, import_file from pyutilib.services import TempfileManager -import pyutilib.common +from pyutilib.common import ApplicationError from pyomo.opt.base import ConverterError from pyomo.common.dependencies import attempt_import from pyomo.common.plugin import (ExtensionPoint, @@ -307,7 +296,20 @@ def launch_command(command, rc = 0 - if pstats_available and (profile_count > 0): + if profile_count > 0: + # Defer import of profiling packages until we know that they + # are needed + try: + try: + import cProfile as profile + except ImportError: + import profile + import pstats + except ImportError: + configure_loggers(shutdown=True) + raise ValueError( + "Cannot use the 'profile' option: the Python " + "'profile' or 'pstats' package cannot be imported!") # # Call the main routine with profiling. # @@ -372,7 +374,7 @@ def launch_command(command, sys.stderr.write(error_label+"CONVERTER ERROR:\n") sys.stderr.write(str(sys.exc_info()[1])+"\n") raise - except pyutilib.common.ApplicationError: + except ApplicationError: sys.stderr.write(error_label+"APPLICATION ERROR:\n") sys.stderr.write(str(sys.exc_info()[1])+"\n") raise diff --git a/pyomo/pysp/util/rapper.py b/pyomo/pysp/util/rapper.py index ee588e7b2b8..87d972c1696 100644 --- a/pyomo/pysp/util/rapper.py +++ b/pyomo/pysp/util/rapper.py @@ -6,17 +6,12 @@ """ import inspect -from pyomo.environ import * +from pyomo.environ import SolverFactory from pyomo.pysp.scenariotree.instance_factory \ import ScenarioTreeInstanceFactory -import pyomo.pysp.ef as pyspef # import (create_ef_instance, solve_ef) -from pyomo.pysp.ef_writer_script import ExtensiveFormAlgorithm -from pyomo.pysp.scenariotree.tree_structure_model import CreateAbstractScenarioTreeModel -from pyomo.pysp.scenariotree.instance_factory import \ - ScenarioTreeInstanceFactory +from pyomo.pysp.ef import create_ef_instance -import pyomo.pysp.phinit as phinit -import os +from pyomo.pysp.phinit import construct_ph_options_parser, GenerateScenarioTreeForPH, PHAlgorithmBuilder def _optiondict_2_list(phopts, args_list = None): """ A little utility to change the format of options""" @@ -100,7 +95,7 @@ def __init__(self, fsfile, if fsfct is None: # Changed in October 2018: None implies AbstractModel args_list = _optiondict_2_list(phopts) - parser = phinit.construct_ph_options_parser("") + parser = construct_ph_options_parser("") options = parser.parse_args(args_list) scenario_instance_factory = \ @@ -108,7 +103,7 @@ def __init__(self, fsfile, try: self.scenario_tree = \ - phinit.GenerateScenarioTreeForPH(options, + GenerateScenarioTreeForPH(options, scenario_instance_factory) except: print ("ERROR in StochSolver called from",inspect.stack()[1][3]) @@ -169,7 +164,7 @@ def make_ef(self, Returns: ef_instance: the ef object """ - return pyspef.create_ef_instance(self.scenario_tree, + return create_ef_instance(self.scenario_tree, verbose_output=verbose, generate_weighted_cvar = generate_weighted_cvar, cvar_weight = cvar_weight, @@ -268,7 +263,7 @@ def solve_ph(self, subsolver, default_rho, phopts = None, sopts = None): ph = None # Build up the options for PH. - parser = phinit.construct_ph_options_parser("") + parser = construct_ph_options_parser("") phargslist = ['--default-rho',str(default_rho)] phargslist.append('--solver') phargslist.append(str(subsolver)) @@ -285,7 +280,7 @@ def solve_ph(self, subsolver, default_rho, phopts = None, sopts = None): # construct the PH solver object try: - ph = phinit.PHAlgorithmBuilder(phoptions, self.scenario_tree) + ph = PHAlgorithmBuilder(phoptions, self.scenario_tree) except: print ("Internal error: ph construction failed.") if ph is not None: diff --git a/pyomo/repn/__init__.py b/pyomo/repn/__init__.py index df023a13d6a..8dc77c6c02b 100644 --- a/pyomo/repn/__init__.py +++ b/pyomo/repn/__init__.py @@ -8,5 +8,5 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.repn.standard_repn import * -from pyomo.repn.standard_aux import * +from pyomo.repn.standard_repn import StandardRepn, generate_standard_repn +from pyomo.repn.standard_aux import compute_standard_repn diff --git a/pyomo/repn/beta/matrix.py b/pyomo/repn/beta/matrix.py index ef7d3724769..2d0e3daeb82 100644 --- a/pyomo/repn/beta/matrix.py +++ b/pyomo/repn/beta/matrix.py @@ -18,8 +18,7 @@ from pyomo.core.base.set_types import Any from pyomo.core.base import (SortComponents, - Var, - Constraint) + Var) from pyomo.core.base.numvalue import (is_fixed, value, ZeroConstant) @@ -511,7 +510,6 @@ def index(self): def variables(self): """A tuple of variables comprising the constraint body.""" comp = self.parent_component() - index = self.index() prows = comp._prows jcols = comp._jcols varmap = comp._varmap @@ -528,7 +526,6 @@ def variables(self): def coefficients(self): """A tuple of coefficients associated with the variables.""" comp = self.parent_component() - index = self.index() prows = comp._prows jcols = comp._jcols vals = comp._vals @@ -548,7 +545,6 @@ def coefficients(self): def constant(self): """The constant value associated with the constraint body.""" comp = self.parent_component() - index = self.index() prows = comp._prows jcols = comp._jcols vals = comp._vals diff --git a/pyomo/repn/plugins/ampl/__init__.py b/pyomo/repn/plugins/ampl/__init__.py index d5cdd62fbe1..24d85f1f4eb 100644 --- a/pyomo/repn/plugins/ampl/__init__.py +++ b/pyomo/repn/plugins/ampl/__init__.py @@ -8,4 +8,4 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.repn.plugins.ampl.ampl_ import * +from pyomo.repn.plugins.ampl.ampl_ import ProblemWriter_nl diff --git a/pyomo/repn/plugins/ampl/ampl_.py b/pyomo/repn/plugins/ampl/ampl_.py index b3a054fadff..e53f147a045 100644 --- a/pyomo/repn/plugins/ampl/ampl_.py +++ b/pyomo/repn/plugins/ampl/ampl_.py @@ -28,20 +28,15 @@ from pyutilib.math.util import isclose from pyutilib.misc import PauseGC -from pyomo.opt import ProblemFormat -from pyomo.opt.base import * +from pyomo.opt import ProblemFormat, AbstractProblemWriter, WriterFactory from pyomo.core.expr import current as EXPR from pyomo.core.expr.numvalue import (NumericConstant, native_numeric_types, - value) -from pyomo.core.base import * -from pyomo.core.base import SymbolMap, Block -from pyomo.core.base.var import Var -from pyomo.core.base import _ExpressionData, Expression, SortComponents -from pyomo.core.base import var -from pyomo.core.base import param + value, + is_fixed) +from pyomo.core.base import SymbolMap, NameLabeler, _ExpressionData, SortComponents, var, param, Var, ExternalFunction, ComponentMap, Objective, Constraint, SOSConstraint, Suffix import pyomo.core.base.suffix -from pyomo.repn.standard_repn import StandardRepn, generate_standard_repn +from pyomo.repn.standard_repn import generate_standard_repn import pyomo.core.kernel.suffix from pyomo.core.kernel.block import IBlock diff --git a/pyomo/repn/plugins/baron_writer.py b/pyomo/repn/plugins/baron_writer.py index d53f59a36a3..a4d49a6cb7e 100644 --- a/pyomo/repn/plugins/baron_writer.py +++ b/pyomo/repn/plugins/baron_writer.py @@ -15,15 +15,13 @@ import itertools import logging import math -from six import iteritems, StringIO, iterkeys -from six.moves import xrange -from pyutilib.math import isclose +from six import iteritems, StringIO from pyomo.common.collections import OrderedSet from pyomo.opt import ProblemFormat from pyomo.opt.base import AbstractProblemWriter, WriterFactory from pyomo.core.expr.numvalue import ( - is_fixed, value, native_numeric_types, native_types, nonpyomo_leaf_types, + value, native_numeric_types, native_types, nonpyomo_leaf_types, ) from pyomo.core.expr import current as EXPR from pyomo.core.base import (SortComponents, @@ -32,10 +30,8 @@ NumericLabeler, Constraint, Objective, - Var, Param) + Param) from pyomo.core.base.component import ActiveComponent -from pyomo.core.base.set_types import * -from pyomo.core.kernel.base import ICategorizedObject #CLH: EXPORT suffixes "constraint_types" and "branching_priorities" # pass their respective information to the .bar file import pyomo.core.base.suffix @@ -364,7 +360,7 @@ def _skip_trivial(constraint_data): else: def mutable_param_gen(b): for param in block.component_objects(Param): - if param._mutable and param.is_indexed(): + if param.mutable and param.is_indexed(): param_data_iter = \ (param_data for index, param_data in iteritems(param)) @@ -635,7 +631,7 @@ def __call__(self, # GAH: Not sure this is necessary, and also it would break for # non-mutable indexed params so I am commenting out for now. #for param_data in active_components_data(block, Param, sort=sorter): - #instead of checking if param_data._mutable: + #instead of checking if param_data.mutable: #if not param_data.is_constant(): # create_symbol_func(symbol_map, param_data, labeler) diff --git a/pyomo/repn/plugins/cpxlp.py b/pyomo/repn/plugins/cpxlp.py index f0262b1e508..d0ef1e9d4f4 100644 --- a/pyomo/repn/plugins/cpxlp.py +++ b/pyomo/repn/plugins/cpxlp.py @@ -13,11 +13,8 @@ # import logging -import math -import operator -from six import iterkeys, iteritems, StringIO -from six.moves import xrange +from six import iteritems from pyutilib.misc import PauseGC from pyomo.opt import ProblemFormat diff --git a/pyomo/repn/plugins/gams_writer.py b/pyomo/repn/plugins/gams_writer.py index 091054fdaaf..1c3000a6d30 100644 --- a/pyomo/repn/plugins/gams_writer.py +++ b/pyomo/repn/plugins/gams_writer.py @@ -13,18 +13,17 @@ # from six import StringIO, string_types, iteritems -from six.moves import xrange from pyutilib.misc import PauseGC from pyomo.core.expr import current as EXPR from pyomo.core.expr.numvalue import ( - is_fixed, value, as_numeric, native_types, native_numeric_types, + value, as_numeric, native_types, native_numeric_types, nonpyomo_leaf_types, ) from pyomo.core.base import ( - SymbolMap, ShortNameLabeler, NumericLabeler, Block, Constraint, Expression, - Objective, Var, Param, minimize, Suffix, SortComponents) + SymbolMap, ShortNameLabeler, NumericLabeler, Constraint, + Objective, Var, minimize, SortComponents) from pyomo.core.base.component import ActiveComponent from pyomo.core.kernel.base import ICategorizedObject from pyomo.opt import ProblemFormat diff --git a/pyomo/repn/plugins/mps.py b/pyomo/repn/plugins/mps.py index 8e3651b6e35..d5131c8bf24 100644 --- a/pyomo/repn/plugins/mps.py +++ b/pyomo/repn/plugins/mps.py @@ -13,10 +13,8 @@ # import logging -import math -import operator -from six import iteritems, iterkeys, StringIO +from six import iteritems, StringIO from six.moves import xrange from pyutilib.misc import PauseGC diff --git a/pyomo/repn/standard_repn.py b/pyomo/repn/standard_repn.py index b543f264e59..447d27f0c59 100644 --- a/pyomo/repn/standard_repn.py +++ b/pyomo/repn/standard_repn.py @@ -15,14 +15,12 @@ import sys import logging -import math import itertools from pyomo.core.base import (Constraint, Objective, ComponentMap) -from pyutilib.misc import Bunch from pyutilib.math.util import isclose as isclose_default from pyomo.core.expr import current as EXPR @@ -33,20 +31,15 @@ from pyomo.core.base.var import (SimpleVar, Var, _GeneralVarData, - _VarData, value) -from pyomo.core.base.param import _ParamData from pyomo.core.base.numvalue import (NumericConstant, - native_numeric_types, - is_fixed) -from pyomo.core.kernel.expression import IIdentityExpression, expression, noclone -from pyomo.core.kernel.variable import IVariable + native_numeric_types) +from pyomo.core.kernel.expression import expression, noclone +from pyomo.core.kernel.variable import IVariable, variable from pyomo.core.kernel.objective import objective -import six -from six import iteritems -from six import itervalues, iteritems, StringIO -from six.moves import xrange, zip +from six import iteritems, StringIO, PY3 +from six.moves import zip try: basestring except: @@ -54,10 +47,8 @@ logger = logging.getLogger('pyomo.core') -using_py3 = six.PY3 +using_py3 = PY3 -from pyomo.core.base import _VarData, _GeneralVarData, SimpleVar -from pyomo.core.kernel.variable import IVariable, variable # @@ -621,10 +612,10 @@ def _collect_prod(exp, multiplier, idMap, compute_values, verbose, quadratic): ans = Results() ans.constant = multiplier*lhs.constant * rhs.constant if not (lhs.constant.__class__ in native_numeric_types and lhs.constant == 0): - for key, coef in six.iteritems(rhs.linear): + for key, coef in iteritems(rhs.linear): ans.linear[key] = multiplier*coef*lhs.constant if not (rhs.constant.__class__ in native_numeric_types and rhs.constant == 0): - for key, coef in six.iteritems(lhs.linear): + for key, coef in iteritems(lhs.linear): if key in ans.linear: ans.linear[key] += multiplier*coef*rhs.constant else: @@ -632,26 +623,26 @@ def _collect_prod(exp, multiplier, idMap, compute_values, verbose, quadratic): if quadratic: if not (lhs.constant.__class__ in native_numeric_types and lhs.constant == 0): - for key, coef in six.iteritems(rhs.quadratic): + for key, coef in iteritems(rhs.quadratic): ans.quadratic[key] = multiplier*coef*lhs.constant if not (rhs.constant.__class__ in native_numeric_types and rhs.constant == 0): - for key, coef in six.iteritems(lhs.quadratic): + for key, coef in iteritems(lhs.quadratic): if key in ans.quadratic: ans.quadratic[key] += multiplier*coef*rhs.constant else: ans.quadratic[key] = multiplier*coef*rhs.constant - for lkey, lcoef in six.iteritems(lhs.linear): - for rkey, rcoef in six.iteritems(rhs.linear): + for lkey, lcoef in iteritems(lhs.linear): + for rkey, rcoef in iteritems(rhs.linear): ndx = (lkey, rkey) if lkey <= rkey else (rkey, lkey) if ndx in ans.quadratic: ans.quadratic[ndx] += multiplier*lcoef*rcoef else: ans.quadratic[ndx] = multiplier*lcoef*rcoef # TODO - Use quicksum here? - el_linear = multiplier*sum(coef*idMap[key] for key, coef in six.iteritems(lhs.linear)) - er_linear = multiplier*sum(coef*idMap[key] for key, coef in six.iteritems(rhs.linear)) - el_quadratic = multiplier*sum(coef*idMap[key[0]]*idMap[key[1]] for key, coef in six.iteritems(lhs.quadratic)) - er_quadratic = multiplier*sum(coef*idMap[key[0]]*idMap[key[1]] for key, coef in six.iteritems(rhs.quadratic)) + el_linear = multiplier*sum(coef*idMap[key] for key, coef in iteritems(lhs.linear)) + er_linear = multiplier*sum(coef*idMap[key] for key, coef in iteritems(rhs.linear)) + el_quadratic = multiplier*sum(coef*idMap[key[0]]*idMap[key[1]] for key, coef in iteritems(lhs.quadratic)) + er_quadratic = multiplier*sum(coef*idMap[key[0]]*idMap[key[1]] for key, coef in iteritems(rhs.quadratic)) ans.nonl += el_linear*er_quadratic + el_quadratic*er_linear return ans @@ -908,7 +899,7 @@ def _collect_comparison(exp, multiplier, idMap, compute_values, verbose, quadrat def _collect_external_fn(exp, multiplier, idMap, compute_values, verbose, quadratic): if compute_values and exp.is_fixed(): - return Results(nonl=multiplier*value(exp)) + return Results(constant=multiplier*value(exp)) return Results(nonl=multiplier*exp) diff --git a/pyomo/repn/tests/ampl/small10_testCase.py b/pyomo/repn/tests/ampl/small10_testCase.py index 069d78fd66c..fca443d6470 100644 --- a/pyomo/repn/tests/ampl/small10_testCase.py +++ b/pyomo/repn/tests/ampl/small10_testCase.py @@ -18,7 +18,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Objective, Constraint, simple_constraint_rule model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small11_testCase.py b/pyomo/repn/tests/ampl/small11_testCase.py index 34d2b4a3c30..3f9159312f4 100644 --- a/pyomo/repn/tests/ampl/small11_testCase.py +++ b/pyomo/repn/tests/ampl/small11_testCase.py @@ -20,7 +20,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, RangeSet model = ConcreteModel() n=3 diff --git a/pyomo/repn/tests/ampl/small12_testCase.py b/pyomo/repn/tests/ampl/small12_testCase.py index 7dc5e5b980d..33e49d89142 100644 --- a/pyomo/repn/tests/ampl/small12_testCase.py +++ b/pyomo/repn/tests/ampl/small12_testCase.py @@ -16,7 +16,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Objective, Constraint, inequality from pyomo.core.expr.current import Expr_if diff --git a/pyomo/repn/tests/ampl/small13_testCase.py b/pyomo/repn/tests/ampl/small13_testCase.py index c08c59b2c53..93fe17338cc 100644 --- a/pyomo/repn/tests/ampl/small13_testCase.py +++ b/pyomo/repn/tests/ampl/small13_testCase.py @@ -16,7 +16,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, maximize model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small14_testCase.py b/pyomo/repn/tests/ampl/small14_testCase.py index 822be0f3f64..c83ca30b945 100644 --- a/pyomo/repn/tests/ampl/small14_testCase.py +++ b/pyomo/repn/tests/ampl/small14_testCase.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, log, log10, sin, cos, tan, sinh, cosh, tanh, asin, acos, atan, asinh, acosh, atanh, exp, sqrt, ceil, floor from math import e, pi model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small15_testCase.py b/pyomo/repn/tests/ampl/small15_testCase.py index d600fe525f3..08d18d03fa4 100644 --- a/pyomo/repn/tests/ampl/small15_testCase.py +++ b/pyomo/repn/tests/ampl/small15_testCase.py @@ -17,7 +17,7 @@ # Test if variables in deactivated blocks are found # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Block, Objective, Constraint model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small1_testCase.py b/pyomo/repn/tests/ampl/small1_testCase.py index 7d3ba631cc8..afd3379c71c 100644 --- a/pyomo/repn/tests/ampl/small1_testCase.py +++ b/pyomo/repn/tests/ampl/small1_testCase.py @@ -15,7 +15,7 @@ # '# nonlinear vars in constraints, objectives, both' # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small2_testCase.py b/pyomo/repn/tests/ampl/small2_testCase.py index 300c3b2d58b..add77c8053d 100644 --- a/pyomo/repn/tests/ampl/small2_testCase.py +++ b/pyomo/repn/tests/ampl/small2_testCase.py @@ -15,7 +15,7 @@ # '# nonlinear vars in constraints, objectives, both' # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small3_testCase.py b/pyomo/repn/tests/ampl/small3_testCase.py index b1ebbe59060..e945a4d60e8 100644 --- a/pyomo/repn/tests/ampl/small3_testCase.py +++ b/pyomo/repn/tests/ampl/small3_testCase.py @@ -15,7 +15,7 @@ # '# nonlinear vars in constraints, objectives, both' # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small4_testCase.py b/pyomo/repn/tests/ampl/small4_testCase.py index 34d9b028d78..a1d8defe56a 100644 --- a/pyomo/repn/tests/ampl/small4_testCase.py +++ b/pyomo/repn/tests/ampl/small4_testCase.py @@ -16,7 +16,7 @@ # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/small5_testCase.py b/pyomo/repn/tests/ampl/small5_testCase.py index c5345b19568..a9a4ff147c4 100644 --- a/pyomo/repn/tests/ampl/small5_testCase.py +++ b/pyomo/repn/tests/ampl/small5_testCase.py @@ -22,7 +22,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Objective, Constraint model = ConcreteModel() model.x = Var(bounds=(-1.0,1.0),initialize=1.0) diff --git a/pyomo/repn/tests/ampl/small6_testCase.py b/pyomo/repn/tests/ampl/small6_testCase.py index 7051c24867b..80b8349c16f 100644 --- a/pyomo/repn/tests/ampl/small6_testCase.py +++ b/pyomo/repn/tests/ampl/small6_testCase.py @@ -22,7 +22,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint model = ConcreteModel() model.x = Var(bounds=(-1.0,1.0),initialize=1.0) diff --git a/pyomo/repn/tests/ampl/small7_testCase.py b/pyomo/repn/tests/ampl/small7_testCase.py index 33eea2c5f01..6f110a8c235 100644 --- a/pyomo/repn/tests/ampl/small7_testCase.py +++ b/pyomo/repn/tests/ampl/small7_testCase.py @@ -22,7 +22,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Objective, Constraint model = ConcreteModel() model.x = Var(bounds=(-1.0,1.0),initialize=1.0) diff --git a/pyomo/repn/tests/ampl/small8_testCase.py b/pyomo/repn/tests/ampl/small8_testCase.py index 7da9fbb44b3..0cca02bac46 100644 --- a/pyomo/repn/tests/ampl/small8_testCase.py +++ b/pyomo/repn/tests/ampl/small8_testCase.py @@ -18,7 +18,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import AbstractModel, Param, Var, NonNegativeReals, Objective, Constraint, minimize model = AbstractModel() diff --git a/pyomo/repn/tests/ampl/small9_testCase.py b/pyomo/repn/tests/ampl/small9_testCase.py index f93971739b3..9856e8f4ea0 100644 --- a/pyomo/repn/tests/ampl/small9_testCase.py +++ b/pyomo/repn/tests/ampl/small9_testCase.py @@ -18,7 +18,7 @@ # will not solve if sent to a real optimizer. # -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Objective, Constraint, simple_constraint_rule model = ConcreteModel() diff --git a/pyomo/repn/tests/ampl/test_ampl_nl.py b/pyomo/repn/tests/ampl/test_ampl_nl.py index 60155a8ba31..cab2319f543 100644 --- a/pyomo/repn/tests/ampl/test_ampl_nl.py +++ b/pyomo/repn/tests/ampl/test_ampl_nl.py @@ -12,13 +12,11 @@ # import os -import random import pyutilib.th as unittest from pyomo.common.getGSL import find_GSL -from pyomo.environ import * -import pyomo.opt +from pyomo.environ import ConcreteModel, Var, Constraint, Objective, Param, Block, ExternalFunction, value thisdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/repn/tests/ampl/test_ampl_repn.py b/pyomo/repn/tests/ampl/test_ampl_repn.py index a80efa54125..8dc2674fe80 100644 --- a/pyomo/repn/tests/ampl/test_ampl_repn.py +++ b/pyomo/repn/tests/ampl/test_ampl_repn.py @@ -1,6 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -from pyomo.core import * +from pyomo.core import ConcreteModel, Var, Param, Constraint, Objective, exp from pyomo.repn.standard_repn import generate_standard_repn as gar diff --git a/pyomo/repn/tests/ampl/test_suffixes.py b/pyomo/repn/tests/ampl/test_suffixes.py index beb4bc0920f..f9d0e63c74f 100644 --- a/pyomo/repn/tests/ampl/test_suffixes.py +++ b/pyomo/repn/tests/ampl/test_suffixes.py @@ -18,7 +18,7 @@ import pyutilib.th as unittest from pyomo.opt import ProblemFormat -from pyomo.core import * +from pyomo.core import ConcreteModel, Suffix, Var, Objective, Constraint, SOSConstraint, sum_product class TestSuffix(unittest.TestCase): diff --git a/pyomo/repn/tests/baron/small14a_testCase.py b/pyomo/repn/tests/baron/small14a_testCase.py index bd0ee1d6d98..8fbec10ddd4 100644 --- a/pyomo/repn/tests/baron/small14a_testCase.py +++ b/pyomo/repn/tests/baron/small14a_testCase.py @@ -8,8 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * -from math import e, pi +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, log, log10, exp, sqrt model = ConcreteModel() diff --git a/pyomo/repn/tests/baron/test_baron.py b/pyomo/repn/tests/baron/test_baron.py index 52fe1ad0467..52faafbbf0d 100644 --- a/pyomo/repn/tests/baron/test_baron.py +++ b/pyomo/repn/tests/baron/test_baron.py @@ -15,8 +15,7 @@ import pyutilib.th as unittest -from pyomo.environ import * -import pyomo.opt +from pyomo.environ import ConcreteModel, Var, Param, Constraint, Objective, Block, sin thisdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/repn/tests/baron/test_baron_comparison.py b/pyomo/repn/tests/baron/test_baron_comparison.py index 35fe5808f76..1b89126bff8 100644 --- a/pyomo/repn/tests/baron/test_baron_comparison.py +++ b/pyomo/repn/tests/baron/test_baron_comparison.py @@ -19,7 +19,6 @@ datadir = abspath(join(currdir, "..", "ampl"))+os.sep import pyutilib.th as unittest -import pyutilib.subprocess import pyomo.scripting.pyomo_main as main diff --git a/pyomo/repn/tests/cpxlp/test_cpxlp.py b/pyomo/repn/tests/cpxlp/test_cpxlp.py index 1efcf7d116c..848deb64705 100644 --- a/pyomo/repn/tests/cpxlp/test_cpxlp.py +++ b/pyomo/repn/tests/cpxlp/test_cpxlp.py @@ -16,8 +16,7 @@ import pyutilib.th as unittest -from pyomo.environ import * -import pyomo.opt +from pyomo.environ import ConcreteModel, Var, Constraint, Objective, Block, ComponentMap thisdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/repn/tests/gams/small14a_testCase.py b/pyomo/repn/tests/gams/small14a_testCase.py index c4e1edf194f..5c9e2cc3525 100644 --- a/pyomo/repn/tests/gams/small14a_testCase.py +++ b/pyomo/repn/tests/gams/small14a_testCase.py @@ -8,8 +8,8 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * -from math import e, pi +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, log, log10, sin, cos, tan, sinh, cosh, tanh, asin, acos, atan, exp, sqrt, ceil, floor +from math import pi model = ConcreteModel() diff --git a/pyomo/repn/tests/gams/test_gams_comparison.py b/pyomo/repn/tests/gams/test_gams_comparison.py index 75eaedf94c2..fc44c5280b3 100644 --- a/pyomo/repn/tests/gams/test_gams_comparison.py +++ b/pyomo/repn/tests/gams/test_gams_comparison.py @@ -19,7 +19,6 @@ datadir = abspath(join(currdir, "..", "ampl"))+os.sep import pyutilib.th as unittest -import pyutilib.subprocess import pyomo.scripting.pyomo_main as main diff --git a/pyomo/repn/tests/mps/test_mps.py b/pyomo/repn/tests/mps/test_mps.py index 62403c0fb3f..a3984863e22 100644 --- a/pyomo/repn/tests/mps/test_mps.py +++ b/pyomo/repn/tests/mps/test_mps.py @@ -16,8 +16,7 @@ import pyutilib.th as unittest -from pyomo.environ import * -import pyomo.opt +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, ComponentMap thisdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/repn/tests/test_standard.py b/pyomo/repn/tests/test_standard.py index fd12f26de7a..98cc5d0ba36 100644 --- a/pyomo/repn/tests/test_standard.py +++ b/pyomo/repn/tests/test_standard.py @@ -17,12 +17,11 @@ currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest -import pyutilib.services from pyomo.core.expr.current import Expr_if -from pyomo.core.expr import expr_common, current as EXPR -from pyomo.repn import * -from pyomo.environ import * +from pyomo.core.expr import current as EXPR +from pyomo.repn import generate_standard_repn +from pyomo.environ import AbstractModel, ConcreteModel, Var, Param, Set, Expression, RangeSet, ExternalFunction, quicksum, cos, sin, summation, sum_product import pyomo.kernel from pyomo.core.base.numvalue import native_numeric_types, as_numeric @@ -4147,14 +4146,21 @@ def _g(*args): e = 100*m.g(1,2.0,'3') rep = generate_standard_repn(e, compute_values=True) self.assertEqual(str(rep.to_expression()), "300") + self.assertEqual(rep.polynomial_degree(), 0) rep = generate_standard_repn(e, compute_values=False) + self.assertEqual(rep.polynomial_degree(), 0) # The function ID is inconsistent, so we don't do a test #self.assertEqual(str(rep.to_expression()), "100*g(0, 1, 2.0, '3')") e = 100*m.g(1,2.0,'3',m.v) rep = generate_standard_repn(e, compute_values=True) self.assertEqual(str(rep.to_expression()), "400") + self.assertEqual(rep.polynomial_degree(), 0) rep = generate_standard_repn(e, compute_values=False) + # FIXME: this is a lie: the degree should be 0, but because + # compute_falues=False creates a "structural" standard repn, the + # computed degree appears to be general nonlinear. + self.assertEqual(rep.polynomial_degree(), None) # The function ID is inconsistent, so we don't do a test #self.assertEqual(str(rep.to_expression()), "100*g(0, 1, 2.0, '3', v)") diff --git a/pyomo/scripting/convert.py b/pyomo/scripting/convert.py index a4458130bab..4d175c5e73f 100644 --- a/pyomo/scripting/convert.py +++ b/pyomo/scripting/convert.py @@ -15,14 +15,12 @@ from pyutilib.misc import Options, Container -from pyomo.common import pyomo_command from pyomo.opt import ProblemFormat from pyomo.core.base import (Objective, Var, Constraint, value, ConcreteModel) -import pyomo.scripting.util _format = None diff --git a/pyomo/scripting/driver_help.py b/pyomo/scripting/driver_help.py index 2d81e47fae7..5f3f476f14f 100644 --- a/pyomo/scripting/driver_help.py +++ b/pyomo/scripting/driver_help.py @@ -15,7 +15,6 @@ import datetime import textwrap import logging -import argparse import socket import pyutilib.subprocess @@ -221,7 +220,6 @@ def help_api(options): print(" "+line) def help_environment(): - cmddir = os.path.dirname(os.path.abspath(sys.executable))+os.sep info = Options() # info.python = Options() diff --git a/pyomo/scripting/plugins/build_ext.py b/pyomo/scripting/plugins/build_ext.py index ad3d5fc6578..bab611d0301 100644 --- a/pyomo/scripting/plugins/build_ext.py +++ b/pyomo/scripting/plugins/build_ext.py @@ -10,7 +10,6 @@ import logging import sys -from six import iteritems from pyomo.common.extensions import ExtensionBuilderFactory from pyomo.scripting.pyomo_parser import add_subparser diff --git a/pyomo/scripting/plugins/convert.py b/pyomo/scripting/plugins/convert.py index 2654e2b2796..061392a545f 100644 --- a/pyomo/scripting/plugins/convert.py +++ b/pyomo/scripting/plugins/convert.py @@ -13,7 +13,7 @@ import argparse from pyutilib.misc import Options -from pyomo.opt import ProblemFormat, ProblemConfigFactory, guess_format +from pyomo.opt import ProblemConfigFactory, guess_format from pyomo.scripting.pyomo_parser import add_subparser, CustomHelpFormatter diff --git a/pyomo/scripting/plugins/download.py b/pyomo/scripting/plugins/download.py index 8e2034ac5aa..e271c2fa847 100644 --- a/pyomo/scripting/plugins/download.py +++ b/pyomo/scripting/plugins/download.py @@ -10,7 +10,6 @@ import logging import sys -import traceback from pyomo.common.download import FileDownloader, DownloadFactory from pyomo.scripting.pyomo_parser import add_subparser diff --git a/pyomo/scripting/plugins/extras.py b/pyomo/scripting/plugins/extras.py index 6b5bab1150a..18dcc664dfe 100644 --- a/pyomo/scripting/plugins/extras.py +++ b/pyomo/scripting/plugins/extras.py @@ -36,7 +36,7 @@ def get_packages(): "Use of the pyomo install-extras is deprecated." "The current recommended course of action is to manually install " "optional dependencies as needed.", - version='TBD') + version='5.7.1') def install_extras(args=[], quiet=False): # # Verify that pip is installed diff --git a/pyomo/scripting/pyomo_command.py b/pyomo/scripting/pyomo_command.py index 9090eae1b99..150215fb962 100644 --- a/pyomo/scripting/pyomo_command.py +++ b/pyomo/scripting/pyomo_command.py @@ -8,12 +8,9 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import sys -import argparse from pyutilib.misc import Options, Container -from pyomo.common import pyomo_command from pyomo.common.dependencies import pympler_available import pyomo.scripting.util from pyomo.core import ConcreteModel @@ -309,7 +306,7 @@ def run_pyomo(options=Options(), parser=None): # model(=None) results in an a different error related to # task port values. Not sure how to interpret that. pyomo.scripting.util.finalize(data, - model=ConcretModel(), + model=ConcreteModel(), instance=None, results=None) return Container() #pragma:nocover diff --git a/pyomo/scripting/pyomo_parser.py b/pyomo/scripting/pyomo_parser.py index 151da763237..79754902a7a 100644 --- a/pyomo/scripting/pyomo_parser.py +++ b/pyomo/scripting/pyomo_parser.py @@ -11,7 +11,6 @@ __all__ = ['add_subparser', 'get_parser', 'subparsers'] import argparse -import warnings import sys # diff --git a/pyomo/scripting/pyro_mip_server.py b/pyomo/scripting/pyro_mip_server.py index 75aed32a481..61787bb0b50 100755 --- a/pyomo/scripting/pyro_mip_server.py +++ b/pyomo/scripting/pyro_mip_server.py @@ -29,7 +29,7 @@ import pyutilib.services import pyutilib.pyro -from pyutilib.pyro import using_pyro4 +from pyutilib.pyro import using_pyro4, TaskProcessingError import pyutilib.common from pyomo.common import pyomo_command from pyomo.opt.base import SolverFactory, ConverterError diff --git a/pyomo/scripting/runtests.py b/pyomo/scripting/runtests.py index e0cf765db7b..e5fee5d42cd 100644 --- a/pyomo/scripting/runtests.py +++ b/pyomo/scripting/runtests.py @@ -31,136 +31,136 @@ def runPyomoTests(argv=None): return pyutilib.dev.runtests.run(targets, basedir, argv) -def OLD_runPyomoTests(): - parser = optparse.OptionParser(usage='test.pyomo [options] ') - - parser.add_option('-d','--dir', - action='store', - dest='dir', - default=None, - help='Top-level source directory where the tests are applied.') - parser.add_option('-e','--exclude', - action='append', - dest='exclude', - default=[], - help='Top-level source directories that are excluded.') - parser.add_option('--cat','--category', - action='store', - dest='cat', - default='smoke', - help='Specify test category.') - parser.add_option('--cov','--coverage', - action='store_true', - dest='coverage', - default=False, - help='Indicate that coverage information is collected') - parser.add_option('-v','--verbose', - action='store_true', - dest='verbose', - default=False, - help='Verbose output') - parser.add_option('-o','--output', - action='store', - dest='output', - default=None, - help='Redirect output to a file') - parser.add_option('--with-doctest', - action='store_true', - dest='doctests', - default=False, - help='Run tests included in Sphinx documentation') - parser.add_option('--doc-dir', - action='store', - dest='docdir', - default=None, - help='Top-level source directory for Sphinx documentation') - - _options, args = parser.parse_args(sys.argv) - - if _options.output: - outfile = os.path.abspath(_options.output) - else: - outfile = None - - if _options.docdir: - docdir = os.path.abspath(_options.docdir) - if not os.path.exists(docdir): - raise ValueError("Invalid documentation directory, " - "path does not exist") - elif _options.doctests: - docdir = os.path.join('pyomo','doc','OnlineDocs') - else: - docdir = None - - if _options.dir is None: - # the /src directory (for development installations) - os.chdir( os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) ) - else: - if os.path.exists(_options.dir): - os.chdir( _options.dir ) - - print("Running tests in directory %s" % os.getcwd()) - _options.cat = os.environ.get('PYUTILIB_UNITTEST_CATEGORY', _options.cat) - if _options.cat == 'all': - if 'PYUTILIB_UNITTEST_CATEGORY' in os.environ: - del os.environ['PYUTILIB_UNITTEST_CATEGORY'] - elif _options.cat: - os.environ['PYUTILIB_UNITTEST_CATEGORY'] = _options.cat - print(" ... for test category: %s" % os.environ['PYUTILIB_UNITTEST_CATEGORY']) - - options=[] - if _options.coverage: - options.append('--coverage') - if _options.verbose: - options.append('-v') - if _options.doctests: - options.append('--with-doctest') - if outfile: - options.append('-o') - options.append(outfile) - - if len(args) > 1: - mydirs = args[1:] - else: - mydirs = [os.path.join('pyomo','pyomo'), 'pyomo-model-libraries'] - # - dirs=[] - for dir in mydirs: - if dir in _options.exclude: - continue - if dir.startswith('-'): - options.append(dir) - if dir.startswith('pyomo'): - if os.path.exists(dir): - dirs.append(dir) - elif '.' in dir: - dirs.append(os.path.join('pyomo','pyomo',dir.split('.')[1])) - else: - if os.path.exists('pyomo.'+dir): - dirs.append('pyomo.'+dir) - else: - dirs.append(os.path.join('pyomo','pyomo',dir)) - # - excluding = set() - for e in _options.exclude: - excluding.add(os.path.join('pyomo','pyomo',e)) - testdirs = [] - for topdir in dirs: - for root, subdirs, files in os.walk(topdir): - if not '__init__.py' in files: - # Skip directories that do not contain a __init__.py file. - continue - for f in files: - if f.startswith("test"): - skip=False - for e in excluding: - if root.startswith(e): - skip=True - if not skip: - testdirs.append(root) - break - # - if docdir: - testdirs.append(docdir) - - return pyutilib.dev.runtests.run('pyomo', ['runtests']+options+['-p','pyomo']+testdirs) +# def OLD_runPyomoTests(): +# parser = optparse.OptionParser(usage='test.pyomo [options] ') + +# parser.add_option('-d','--dir', +# action='store', +# dest='dir', +# default=None, +# help='Top-level source directory where the tests are applied.') +# parser.add_option('-e','--exclude', +# action='append', +# dest='exclude', +# default=[], +# help='Top-level source directories that are excluded.') +# parser.add_option('--cat','--category', +# action='store', +# dest='cat', +# default='smoke', +# help='Specify test category.') +# parser.add_option('--cov','--coverage', +# action='store_true', +# dest='coverage', +# default=False, +# help='Indicate that coverage information is collected') +# parser.add_option('-v','--verbose', +# action='store_true', +# dest='verbose', +# default=False, +# help='Verbose output') +# parser.add_option('-o','--output', +# action='store', +# dest='output', +# default=None, +# help='Redirect output to a file') +# parser.add_option('--with-doctest', +# action='store_true', +# dest='doctests', +# default=False, +# help='Run tests included in Sphinx documentation') +# parser.add_option('--doc-dir', +# action='store', +# dest='docdir', +# default=None, +# help='Top-level source directory for Sphinx documentation') + +# _options, args = parser.parse_args(sys.argv) + +# if _options.output: +# outfile = os.path.abspath(_options.output) +# else: +# outfile = None + +# if _options.docdir: +# docdir = os.path.abspath(_options.docdir) +# if not os.path.exists(docdir): +# raise ValueError("Invalid documentation directory, " +# "path does not exist") +# elif _options.doctests: +# docdir = os.path.join('pyomo','doc','OnlineDocs') +# else: +# docdir = None + +# if _options.dir is None: +# # the /src directory (for development installations) +# os.chdir( os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) ) +# else: +# if os.path.exists(_options.dir): +# os.chdir( _options.dir ) + +# print("Running tests in directory %s" % os.getcwd()) +# _options.cat = os.environ.get('PYUTILIB_UNITTEST_CATEGORY', _options.cat) +# if _options.cat == 'all': +# if 'PYUTILIB_UNITTEST_CATEGORY' in os.environ: +# del os.environ['PYUTILIB_UNITTEST_CATEGORY'] +# elif _options.cat: +# os.environ['PYUTILIB_UNITTEST_CATEGORY'] = _options.cat +# print(" ... for test category: %s" % os.environ['PYUTILIB_UNITTEST_CATEGORY']) + +# options=[] +# if _options.coverage: +# options.append('--coverage') +# if _options.verbose: +# options.append('-v') +# if _options.doctests: +# options.append('--with-doctest') +# if outfile: +# options.append('-o') +# options.append(outfile) + +# if len(args) > 1: +# mydirs = args[1:] +# else: +# mydirs = [os.path.join('pyomo','pyomo'), 'pyomo-model-libraries'] +# # +# dirs=[] +# for dir in mydirs: +# if dir in _options.exclude: +# continue +# if dir.startswith('-'): +# options.append(dir) +# if dir.startswith('pyomo'): +# if os.path.exists(dir): +# dirs.append(dir) +# elif '.' in dir: +# dirs.append(os.path.join('pyomo','pyomo',dir.split('.')[1])) +# else: +# if os.path.exists('pyomo.'+dir): +# dirs.append('pyomo.'+dir) +# else: +# dirs.append(os.path.join('pyomo','pyomo',dir)) +# # +# excluding = set() +# for e in _options.exclude: +# excluding.add(os.path.join('pyomo','pyomo',e)) +# testdirs = [] +# for topdir in dirs: +# for root, subdirs, files in os.walk(topdir): +# if not '__init__.py' in files: +# # Skip directories that do not contain a __init__.py file. +# continue +# for f in files: +# if f.startswith("test"): +# skip=False +# for e in excluding: +# if root.startswith(e): +# skip=True +# if not skip: +# testdirs.append(root) +# break +# # +# if docdir: +# testdirs.append(docdir) + +# return pyutilib.dev.runtests.run('pyomo', ['runtests']+options+['-p','pyomo']+testdirs) diff --git a/pyomo/scripting/tests/test_pms.py b/pyomo/scripting/tests/test_pms.py index 9bd9b0a88bc..6b40d1ff7f9 100644 --- a/pyomo/scripting/tests/test_pms.py +++ b/pyomo/scripting/tests/test_pms.py @@ -14,7 +14,6 @@ import base64 import ast import os -import sys from os.path import abspath, dirname from pyutilib.pyro import using_pyro4 @@ -22,10 +21,9 @@ import pyutilib.services from pyutilib.misc import Options import pyomo.opt -from pyomo.environ import * +from pyomo.environ import ConcreteModel, RangeSet, Var, Objective, Constraint, sum_product import pyomo.scripting.pyro_mip_server -import six currdir = dirname(abspath(__file__))+os.sep diff --git a/pyomo/scripting/util.py b/pyomo/scripting/util.py index 0734f4ccf3c..2beeb08a61f 100644 --- a/pyomo/scripting/util.py +++ b/pyomo/scripting/util.py @@ -17,20 +17,9 @@ import types import time import json -from six import itervalues, iterkeys, iteritems -from six.moves import xrange +from six import iteritems from pyomo.common import pyomo_api -try: - import cProfile as profile -except ImportError: - import profile -try: - import pstats - pstats_available=True -except ImportError: - pstats_available=False - from pyutilib.misc import Options memory_data = Options() @@ -47,9 +36,8 @@ from pyomo.opt.base import SolverFactory from pyomo.opt.parallel import SolverManagerFactory from pyomo.dataportal import DataPortal -from pyomo.core import * -from pyomo.core.base import TextLabeler -import pyomo.core.base +from pyomo.core import IPyomoScriptCreateModel, IPyomoScriptCreateDataPortal, IPyomoScriptPrintModel, IPyomoScriptModifyInstance, IPyomoScriptPrintInstance, IPyomoScriptSaveInstance, IPyomoScriptPrintResults, IPyomoScriptSaveResults, IPyomoScriptPostprocess, IPyomoScriptPreprocess, Model, TransformationFactory, Suffix, display + # Importing IPython is slow; defer the import to the point that it is # actually needed. @@ -926,11 +914,19 @@ def run_command(command=None, parser=None, args=None, name='unknown', data=None, TempfileManager.push() pcount = options.runtime.profile_count if pcount > 0: - if not pstats_available: - msg = "Cannot use the 'profile' option. The Python 'pstats' " \ - 'package cannot be imported!' + # Defer import of profiling packages until we know that they + # are needed + try: + try: + import cProfile as profile + except ImportError: + import profile + import pstats + except ImportError: configure_loggers(shutdown=True) - raise ValueError(msg) + raise ValueError( + "Cannot use the 'profile' option: the Python " + "'profile' or 'pstats' package cannot be imported!") tfile = TempfileManager.create_tempfile(suffix=".profile") tmp = profile.runctx( command.__name__ + '(options=options,parser=parser)', command.__globals__, locals(), tfile diff --git a/pyomo/solvers/plugins/converter/ampl.py b/pyomo/solvers/plugins/converter/ampl.py index 490076b28bc..7012eb4bd0e 100644 --- a/pyomo/solvers/plugins/converter/ampl.py +++ b/pyomo/solvers/plugins/converter/ampl.py @@ -13,7 +13,7 @@ import pyutilib.common import pyomo.common -from pyomo.opt.base import * +from pyomo.opt.base import ProblemFormat, ConverterError from pyomo.opt.base.convert import ProblemConverterFactory try: diff --git a/pyomo/solvers/plugins/converter/glpsol.py b/pyomo/solvers/plugins/converter/glpsol.py index 120d893482b..b61eb167f8c 100644 --- a/pyomo/solvers/plugins/converter/glpsol.py +++ b/pyomo/solvers/plugins/converter/glpsol.py @@ -14,7 +14,7 @@ import pyutilib.subprocess import pyutilib.common import pyomo.common -from pyomo.opt.base import * +from pyomo.opt.base import ProblemFormat, ConverterError from pyomo.opt.base.convert import ProblemConverterFactory diff --git a/pyomo/solvers/plugins/converter/model.py b/pyomo/solvers/plugins/converter/model.py index 06b50d566e5..ab604d6db99 100644 --- a/pyomo/solvers/plugins/converter/model.py +++ b/pyomo/solvers/plugins/converter/model.py @@ -13,7 +13,7 @@ from six import iteritems, PY3 import pyutilib.services -from pyomo.opt.base import * +from pyomo.opt.base import ProblemFormat from pyomo.opt.base.convert import ProblemConverterFactory from pyomo.solvers.plugins.converter.pico import PicoMIPConverter from pyomo.core.kernel.block import IBlock diff --git a/pyomo/solvers/plugins/converter/pico.py b/pyomo/solvers/plugins/converter/pico.py index 8fafe70fe4a..fd9562128dc 100644 --- a/pyomo/solvers/plugins/converter/pico.py +++ b/pyomo/solvers/plugins/converter/pico.py @@ -16,7 +16,7 @@ import pyutilib.subprocess import pyomo.common -from pyomo.opt.base import * +from pyomo.opt.base import ProblemFormat, ConverterError class PicoMIPConverter(object): diff --git a/pyomo/solvers/plugins/smanager/phpyro.py b/pyomo/solvers/plugins/smanager/phpyro.py index 5fbb0b3ad23..cab0759ea84 100644 --- a/pyomo/solvers/plugins/smanager/phpyro.py +++ b/pyomo/solvers/plugins/smanager/phpyro.py @@ -11,7 +11,6 @@ __all__ = ["SolverManager_PHPyro"] -import sys import time import itertools from collections import defaultdict @@ -20,12 +19,10 @@ from pyutilib.pyro import using_pyro3, using_pyro4 from pyutilib.pyro import Pyro as _pyro from pyutilib.pyro.util import _connection_problem -from pyomo.opt.parallel.manager import * -from pyomo.opt.parallel.async_solver import * +from pyomo.opt.parallel.manager import AsynchronousActionManager, ActionStatus +from pyomo.opt.parallel.async_solver import SolverManagerFactory, AsynchronousSolverManager -import six from six import advance_iterator, iteritems, itervalues -from six.moves import xrange # # a specialized asynchronous solver manager for Progressive Hedging. diff --git a/pyomo/solvers/plugins/smanager/pyro.py b/pyomo/solvers/plugins/smanager/pyro.py index e00c55f1044..5905c0c440d 100644 --- a/pyomo/solvers/plugins/smanager/pyro.py +++ b/pyomo/solvers/plugins/smanager/pyro.py @@ -21,7 +21,7 @@ import pyutilib.pyro from pyutilib.pyro import using_pyro4, TaskProcessingError import pyutilib.misc -from pyomo.opt.base import OptSolver +from pyomo.opt.base import OptSolver, SolverFactory from pyomo.opt.parallel.manager import ActionManagerError, ActionStatus from pyomo.opt.parallel.async_solver import (AsynchronousSolverManager, SolverManagerFactory) diff --git a/pyomo/solvers/plugins/solvers/ASL.py b/pyomo/solvers/plugins/solvers/ASL.py index 0bb1717b992..ecb75c3d737 100644 --- a/pyomo/solvers/plugins/solvers/ASL.py +++ b/pyomo/solvers/plugins/solvers/ASL.py @@ -12,17 +12,18 @@ import os import six -import pyomo.common -import pyutilib.common -import pyutilib.misc - -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * -from pyomo.core.base import TransformationFactory +from pyomo.common import Executable +from pyutilib.common import ApplicationError +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run + +from pyomo.opt.base import ProblemFormat, ResultsFormat +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.solver import SystemCallSolver from pyomo.core.kernel.block import IBlock from pyomo.solvers.mockmip import MockMIP +from pyomo.core import TransformationFactory import logging logger = logging.getLogger('pyomo.solvers') @@ -53,7 +54,7 @@ def __init__(self, **kwds): # # Note: Undefined capabilities default to 'None' # - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.integer = True self._capabilities.quadratic_objective = True @@ -76,7 +77,7 @@ def _default_executable(self): logger.warning( "No solver option specified for ASL solver interface") return None - executable = pyomo.common.Executable(self.options.solver) + executable = Executable(self.options.solver) if not executable: logger.warning( "Could not locate the '%s' executable, which is required " @@ -92,7 +93,7 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = pyutilib.subprocess.run( [solver_exec,"-v"], timelimit=1 ) + results = run( [solver_exec,"-v"], timelimit=1 ) return _extract_version(results[1]) def create_command_line(self, executable, problem_files): @@ -103,7 +104,7 @@ def create_command_line(self, executable, problem_files): # solver_name = os.path.basename(self.options.solver) if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix="_%s.log" % solver_name) # @@ -171,7 +172,7 @@ def create_command_line(self, executable, problem_files): # Merge with any options coming in through the environment env[envstr] = " ".join(opt) - return pyutilib.misc.Bunch(cmd=cmd, log_file=self._log_file, env=env) + return Bunch(cmd=cmd, log_file=self._log_file, env=env) def _presolve(self, *args, **kwds): if (not isinstance(args[0], six.string_types)) and \ @@ -214,7 +215,7 @@ class MockASL(ASL,MockMIP): def __init__(self, **kwds): try: ASL.__init__(self,**kwds) - except pyutilib.common.ApplicationError: #pragma:nocover + except ApplicationError: #pragma:nocover pass #pragma:nocover MockMIP.__init__(self,"asl") self._assert_available = True diff --git a/pyomo/solvers/plugins/solvers/BARON.py b/pyomo/solvers/plugins/solvers/BARON.py index 57cd04b1836..ac5de1eabb7 100644 --- a/pyomo/solvers/plugins/solvers/BARON.py +++ b/pyomo/solvers/plugins/solvers/BARON.py @@ -8,28 +8,23 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import itertools import logging import os import subprocess import re import tempfile -import pyomo.common -import pyutilib -from pyutilib.misc import Options +from pyomo.common import Executable +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * -from pyomo.core.base import SortComponents -from pyomo.core.base.objective import Objective -from pyomo.core.base import Constraint -from pyomo.core.base.set_types import * -from pyomo.repn.plugins.baron_writer import ProblemWriter_bar +from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverResults, Solution, SolverStatus, TerminationCondition, SolutionStatus +from pyomo.opt.solver import SystemCallSolver -from six.moves import xrange, zip +from six.moves import zip logger = logging.getLogger('pyomo.solvers') @@ -154,7 +149,7 @@ def license_is_valid(executable='baron'): return True def _default_executable(self): - executable = pyomo.common.Executable("baron") + executable = Executable("baron") if not executable: logger.warning("Could not locate the 'baron' executable, " "which is required for solver %s" % self.name) @@ -173,7 +168,7 @@ def _get_version(self): else: fnames = self._get_dummy_input_files(check_license=False) try: - results = pyutilib.subprocess.run([solver_exec, fnames[0]]) + results = run([solver_exec, fnames[0]]) return _extract_version(results[1]) finally: self._remove_dummy_input_files(fnames) @@ -189,7 +184,7 @@ def create_command_line(self, executable, problem_files): cmd = [executable, problem_files[0]] if self._timer: cmd.insert(0, self._timer) - return pyutilib.misc.Bunch( cmd=cmd, + return Bunch( cmd=cmd, log_file=self._log_file, env=None ) @@ -215,17 +210,17 @@ def _convert_problem(self, # Define log file # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix = '.baron.log') # # Define solution file # if self._soln_file is None: - self._soln_file = pyutilib.services.TempfileManager.\ + self._soln_file = TempfileManager.\ create_tempfile(suffix = '.baron.soln') - self._tim_file = pyutilib.services.TempfileManager.\ + self._tim_file = TempfileManager.\ create_tempfile(suffix = '.baron.tim') # @@ -324,7 +319,7 @@ def _process_soln_file(self, results, TimFile, INPUT): # file will be excluded from the results object. # - # TODO: Is there a way to hanle non-zero return values from baron? + # TODO: Is there a way to handle non-zero return values from baron? # Example: the "NonLinearity Error if POW expression" # (caused by x ^ y) when both x and y are variables # causes an ugly python error and the solver log has a single diff --git a/pyomo/solvers/plugins/solvers/CBCplugin.py b/pyomo/solvers/plugins/solvers/CBCplugin.py index f08b84e0b34..58012569ab3 100644 --- a/pyomo/solvers/plugins/solvers/CBCplugin.py +++ b/pyomo/solvers/plugins/solvers/CBCplugin.py @@ -15,20 +15,20 @@ import time import logging -from six import iteritems -from six import string_types +from six import iteritems, string_types -import pyomo.common -import pyutilib.misc -import pyutilib.common -import pyutilib.subprocess +from pyomo.common import Executable +from pyutilib.common import ApplicationError +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run -from pyomo.core.base import Var from pyomo.core.kernel.block import IBlock -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.core import Var +from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverResults, SolverStatus, TerminationCondition, SolutionStatus, ProblemSense, Solution +from pyomo.opt.solver import SystemCallSolver from pyomo.solvers.mockmip import MockMIP logger = logging.getLogger('pyomo.solvers') @@ -49,13 +49,13 @@ def configure_cbc(): return # manually look for the cbc executable to prevent the # CBC.execute() from logging an error when CBC is missing - executable = pyomo.common.Executable("cbc") + executable = Executable("cbc") if not executable: return cbc_exec = executable.path() - results = pyutilib.subprocess.run( [cbc_exec,"-stop"], timelimit=1 ) + results = run( [cbc_exec,"-stop"], timelimit=1 ) _cbc_version = _extract_version(results[1]) - results = pyutilib.subprocess.run( + results = run( [cbc_exec,"dummy","-AMPL","-stop"], timelimit=1 ) _cbc_compiled_with_asl = not ('No match for AMPL' in results[1]) if _cbc_version is not None: @@ -158,7 +158,7 @@ def __init__(self, **kwds): self._valid_result_formats[ProblemFormat.mps] = [ResultsFormat.soln] # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.integer = True # The quadratic capabilities may be true but there is @@ -234,7 +234,7 @@ def _presolve(self, *args, **kwds): # create a context in the temporary file manager for # this plugin - is "pop"ed in the _postsolve method. - pyutilib.services.TempfileManager.push() + TempfileManager.push() # if the first argument is a string (representing a filename), # then we don't have an instance => the solver is being applied @@ -260,7 +260,7 @@ def _presolve(self, *args, **kwds): # and the warm start file-name is (obviously) needed there. if self._warm_start_file_name is None: assert not user_warmstart - self._warm_start_file_name = pyutilib.services.TempfileManager.\ + self._warm_start_file_name = TempfileManager.\ create_tempfile(suffix = '.cbc.soln') # let the base class handle any remaining keywords/actions. @@ -289,7 +289,7 @@ def _presolve(self, *args, **kwds): def _default_executable(self): - executable = pyomo.common.Executable("cbc") + executable = Executable("cbc") if not executable: logger.warning( "Could not locate the 'cbc' executable, which is " @@ -311,7 +311,7 @@ def create_command_line(self, executable, problem_files): # Define the log file # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.create_tempfile(suffix=".cbc.log") + self._log_file = TempfileManager.create_tempfile(suffix=".cbc.log") # # Define the solution file @@ -404,7 +404,7 @@ def _check_and_escape_options(options): "-solve", "-solu", self._soln_file]) - return pyutilib.misc.Bunch(cmd=cmd, log_file=self._log_file, env=None) + return Bunch(cmd=cmd, log_file=self._log_file, env=None) def process_logfile(self): """ @@ -873,7 +873,7 @@ def _postsolve(self): # manager, created populated *directly* by this plugin. does not # include, for example, the execution script. but does include # the warm-start file. - pyutilib.services.TempfileManager.pop(remove=not self._keepfiles) + TempfileManager.pop(remove=not self._keepfiles) return results @@ -886,7 +886,7 @@ class MockCBC(CBCSHELL,MockMIP): def __init__(self, **kwds): try: CBCSHELL.__init__(self,**kwds) - except pyutilib.common.ApplicationError: #pragma:nocover + except ApplicationError: #pragma:nocover pass #pragma:nocover MockMIP.__init__(self,"cbc") diff --git a/pyomo/solvers/plugins/solvers/CONOPT.py b/pyomo/solvers/plugins/solvers/CONOPT.py index 998d871aed8..a61cf9551c8 100644 --- a/pyomo/solvers/plugins/solvers/CONOPT.py +++ b/pyomo/solvers/plugins/solvers/CONOPT.py @@ -10,13 +10,15 @@ import os -import pyomo.common -import pyutilib.misc +from pyomo.common import Executable +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.opt.base import ProblemFormat, ResultsFormat +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverStatus +from pyomo.opt.solver import SystemCallSolver import logging logger = logging.getLogger('pyomo.solvers') @@ -50,7 +52,7 @@ def __init__(self, **kwds): self.set_problem_format(ProblemFormat.nl) # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.integer = True self._capabilities.quadratic_objective = True @@ -62,7 +64,7 @@ def _default_results_format(self, prob_format): return ResultsFormat.sol def _default_executable(self): - executable = pyomo.common.Executable("conopt") + executable = Executable("conopt") if not executable: logger.warning("Could not locate the 'conopt' executable, " "which is required for solver %s" % self.name) @@ -77,7 +79,7 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = pyutilib.subprocess.run( [solver_exec], timelimit=1 ) + results = run( [solver_exec], timelimit=1 ) return _extract_version(results[1]) def create_command_line(self, executable, problem_files): @@ -89,7 +91,7 @@ def create_command_line(self, executable, problem_files): # Define log file # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix="_conopt.log") fname = problem_files[0] @@ -147,7 +149,7 @@ def create_command_line(self, executable, problem_files): # Merge with any options coming in through the environment env[envstr] = " ".join(opt) - return pyutilib.misc.Bunch(cmd=cmd, log_file=self._log_file, env=env) + return Bunch(cmd=cmd, log_file=self._log_file, env=env) def _postsolve(self): results = super(CONOPT, self)._postsolve() diff --git a/pyomo/solvers/plugins/solvers/CPLEX.py b/pyomo/solvers/plugins/solvers/CPLEX.py index a596f4e4691..6b103dd5c17 100644 --- a/pyomo/solvers/plugins/solvers/CPLEX.py +++ b/pyomo/solvers/plugins/solvers/CPLEX.py @@ -14,17 +14,25 @@ import time import logging -import pyomo.common -import pyutilib.common -import pyutilib.misc +from pyomo.common import Executable +from pyutilib.common import ApplicationError +from pyutilib.misc import Options, Bunch, yaml_fix +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run from pyomo.common.collections import ComponentMap -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.opt.base import ( + ProblemFormat, ResultsFormat, OptSolver, BranchDirection, +) +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import ( + SolverResults, SolverStatus, TerminationCondition, SolutionStatus, + ProblemSense, Solution, +) +from pyomo.opt.solver import ILMLicensedSystemCallSolver from pyomo.solvers.mockmip import MockMIP from pyomo.core.base import Var, Suffix, active_export_suffix_generator +from pyomo.core.kernel.suffix import export_suffix_generator from pyomo.core.kernel.block import IBlock from pyomo.util.components import iter_component @@ -166,7 +174,7 @@ def __init__(self, **kwds): self.set_problem_format(ProblemFormat.cpxlp) # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.quadratic_objective = True self._capabilities.quadratic_constraint = True @@ -237,7 +245,7 @@ def _get_suffixes(self, instance): if isinstance(instance, IBlock): suffixes = ( (suf.name, suf) - for suf in pyomo.core.kernel.suffix.export_suffix_generator( + for suf in export_suffix_generator( instance, datatype=Suffix.INT, active=True, descend_into=False ) ) @@ -294,7 +302,7 @@ def _presolve(self, *args, **kwds): # create a context in the temporary file manager for # this plugin - is "pop"ed in the _postsolve method. - pyutilib.services.TempfileManager.push() + TempfileManager.push() # if the first argument is a string (representing a filename), # then we don't have an instance => the solver is being applied @@ -319,7 +327,7 @@ def _presolve(self, *args, **kwds): # and the warm start file-name is (obviously) needed there. if self._warm_start_file_name is None: assert not user_warmstart - self._warm_start_file_name = pyutilib.services.TempfileManager.\ + self._warm_start_file_name = TempfileManager.\ create_tempfile(suffix = '.cplex.mst') self._priorities_solve = kwds.pop("priorities", False) @@ -333,7 +341,7 @@ def _presolve(self, *args, **kwds): and not isinstance(args[0], basestring) and not user_priorities ): - self._priorities_file_name = pyutilib.services.TempfileManager.create_tempfile( + self._priorities_file_name = TempfileManager.create_tempfile( suffix=".cplex.ord" ) @@ -373,7 +381,7 @@ def _presolve(self, *args, **kwds): ) def _default_executable(self): - executable = pyomo.common.Executable("cplex") + executable = Executable("cplex") if not executable: logger.warning("Could not locate the 'cplex' executable" ", which is required for solver %s" @@ -389,7 +397,7 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = pyutilib.subprocess.run( [solver_exec,'-c','quit'], timelimit=1 ) + results = run( [solver_exec,'-c','quit'], timelimit=1 ) return _extract_version(results[1]) def create_command_line(self, executable, problem_files): @@ -399,7 +407,7 @@ def create_command_line(self, executable, problem_files): # The log file in CPLEX contains the solution trace, but the solver status can be found in the solution file. # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix = '.cplex.log') self._log_file = _validate_file_name(self, self._log_file, "log") @@ -408,7 +416,7 @@ def create_command_line(self, executable, problem_files): # As indicated above, contains (in XML) both the solution and solver status. # if self._soln_file is None: - self._soln_file = pyutilib.services.TempfileManager.\ + self._soln_file = TempfileManager.\ create_tempfile(suffix = '.cplex.sol') self._soln_file = _validate_file_name(self, self._soln_file, "solution") @@ -455,7 +463,7 @@ def create_command_line(self, executable, problem_files): # dump the script and warm-start file names for the # user if we're keeping files around. if self._keepfiles: - script_fname = pyutilib.services.TempfileManager.\ + script_fname = TempfileManager.\ create_tempfile(suffix = '.cplex.script') tmp = open(script_fname,'w') tmp.write(script) @@ -476,7 +484,7 @@ def create_command_line(self, executable, problem_files): cmd = [executable] if self._timer: cmd.insert(0, self._timer) - return pyutilib.misc.Bunch(cmd=cmd, script=script, + return Bunch(cmd=cmd, script=script, log_file=self._log_file, env=None) def process_logfile(self): @@ -613,7 +621,7 @@ def process_logfile(self): results.solver.termination_message = ' '.join(tokens) try: - results.solver.termination_message = pyutilib.misc.yaml_fix(results.solver.termination_message) + results.solver.termination_message = yaml_fix(results.solver.termination_message) except: pass return results @@ -889,7 +897,7 @@ def _postsolve(self): # manager, created populated *directly* by this plugin. does not # include, for example, the execution script. but does include # the warm-start file. - pyutilib.services.TempfileManager.pop(remove=not self._keepfiles) + TempfileManager.pop(remove=not self._keepfiles) return results @@ -902,7 +910,7 @@ class MockCPLEX(CPLEXSHELL,MockMIP): def __init__(self, **kwds): try: CPLEXSHELL.__init__(self, **kwds) - except pyutilib.common.ApplicationError: #pragma:nocover + except ApplicationError: #pragma:nocover pass #pragma:nocover MockMIP.__init__(self,"cplex") diff --git a/pyomo/solvers/plugins/solvers/GLPK.py b/pyomo/solvers/plugins/solvers/GLPK.py index 597ddbd03ef..b7c4722c4cc 100644 --- a/pyomo/solvers/plugins/solvers/GLPK.py +++ b/pyomo/solvers/plugins/solvers/GLPK.py @@ -18,7 +18,7 @@ from pyutilib.services import TempfileManager from pyomo.common import Executable -from pyomo.opt import * +from pyomo.opt import SolverFactory, OptSolver, ProblemFormat, ResultsFormat, SolverResults, TerminationCondition, SolutionStatus, ProblemSense from pyomo.opt.base.solvers import _extract_version from pyomo.opt.solver import SystemCallSolver diff --git a/pyomo/solvers/plugins/solvers/GLPK_old.py b/pyomo/solvers/plugins/solvers/GLPK_old.py index b404bbc8b38..b529014da7f 100644 --- a/pyomo/solvers/plugins/solvers/GLPK_old.py +++ b/pyomo/solvers/plugins/solvers/GLPK_old.py @@ -18,11 +18,11 @@ from pyutilib.services import TempfileManager import pyutilib.subprocess -import pyomo.common -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.common import Executable +from pyomo.opt.base import ProblemFormat, ResultsFormat +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverResults, SolverStatus, TerminationCondition, SolutionStatus, ProblemSense +from pyomo.opt.solver import SystemCallSolver from pyomo.solvers.mockmip import MockMIP from pyomo.solvers.plugins.solvers.GLPK import _glpk_version, configure_glpk @@ -85,7 +85,7 @@ def _default_results_format(self, prob_format): return ResultsFormat.soln def _default_executable(self): - executable = pyomo.common.Executable('glpsol') + executable = Executable('glpsol') if not executable: msg = ("Could not locate the 'glpsol' executable, which is " "required for solver '%s'") @@ -439,7 +439,7 @@ def _default_results_format(self, prob_format): return ResultsFormat.soln def _default_executable(self): - executable = pyomo.common.Executable('glpsol') + executable = Executable('glpsol') if not executable: msg = "Could not locate the 'glpsol' executable, which is " \ "required for solver '%s'" @@ -722,7 +722,7 @@ def process_soln_file(self, results): ("***ERROR: Unexpected constraint index " + \ "encountered on line=%s; expected " + \ "value=%s; actual value=%s") % \ - (line, str(number_of_consrtaints_read), + (line, str(number_of_constraints_read), str(index))) else: index = None diff --git a/pyomo/solvers/plugins/solvers/GUROBI.py b/pyomo/solvers/plugins/solvers/GUROBI.py index 859d057e824..5c927b9104c 100644 --- a/pyomo/solvers/plugins/solvers/GUROBI.py +++ b/pyomo/solvers/plugins/solvers/GUROBI.py @@ -15,13 +15,15 @@ import logging import subprocess -import pyomo.common -import pyutilib.misc - -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.common import Executable +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run + +from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverStatus, TerminationCondition, SolutionStatus, ProblemSense, Solution +from pyomo.opt.solver import ILMLicensedSystemCallSolver from pyomo.core.kernel.block import IBlock logger = logging.getLogger('pyomo.solvers') @@ -108,7 +110,7 @@ def __init__(self, **kwds): self.set_problem_format(ProblemFormat.cpxlp) # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.quadratic_objective = True self._capabilities.quadratic_constraint = True @@ -190,7 +192,7 @@ def _presolve(self, *args, **kwds): # create a context in the temporary file manager for # this plugin - is "pop"ed in the _postsolve method. - pyutilib.services.TempfileManager.push() + TempfileManager.push() # if the first argument is a string (representing a filename), # then we don't have an instance => the solver is being applied @@ -216,7 +218,7 @@ def _presolve(self, *args, **kwds): # and the warm start file-name is (obviously) needed there. if self._warm_start_file_name is None: assert not user_warmstart - self._warm_start_file_name = pyutilib.services.TempfileManager.\ + self._warm_start_file_name = TempfileManager.\ create_tempfile(suffix = '.gurobi.mst') # let the base class handle any remaining keywords/actions. @@ -244,9 +246,9 @@ def _presolve(self, *args, **kwds): def _default_executable(self): if sys.platform == 'win32': - executable = pyomo.common.Executable("gurobi.bat") + executable = Executable("gurobi.bat") else: - executable = pyomo.common.Executable("gurobi.sh") + executable = Executable("gurobi.sh") if not executable: logger.warning("Could not locate the 'gurobi' executable, " "which is required for solver %s" % self.name) @@ -262,7 +264,7 @@ def _get_version(self): if solver_exec is None: return _extract_version('') f = StringIO() - results = pyutilib.subprocess.run([solver_exec], + results = run([solver_exec], stdin=('from gurobipy import *; ' 'print(gurobi.version()); exit()'), ostream=f) @@ -285,7 +287,7 @@ def create_command_line(self,executable,problem_files): # The log file in CPLEX contains the solution trace, but the solver status can be found in the solution file. # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix = '.gurobi.log') # @@ -293,7 +295,7 @@ def create_command_line(self,executable,problem_files): # As indicated above, contains (in XML) both the solution and solver status. # if self._soln_file is None: - self._soln_file = pyutilib.services.TempfileManager.\ + self._soln_file = TempfileManager.\ create_tempfile(suffix = '.gurobi.txt') # @@ -336,7 +338,7 @@ def create_command_line(self,executable,problem_files): # dump the script and warm-start file names for the # user if we're keeping files around. if self._keepfiles: - script_fname = pyutilib.services.TempfileManager.create_tempfile(suffix = '.gurobi.script') + script_fname = TempfileManager.create_tempfile(suffix = '.gurobi.script') script_file = open(script_fname, 'w') script_file.write( script ) script_file.close() @@ -353,7 +355,7 @@ def create_command_line(self,executable,problem_files): cmd = [executable] if self._timer: cmd.insert(0, self._timer) - return pyutilib.misc.Bunch(cmd=cmd, script=script, + return Bunch(cmd=cmd, script=script, log_file=self._log_file, env=None) def process_logfile(self): @@ -532,6 +534,6 @@ def _postsolve(self): # manager, created populated *directly* by this plugin. does not # include, for example, the execution script. but does include # the warm-start file. - pyutilib.services.TempfileManager.pop(remove=not self._keepfiles) + TempfileManager.pop(remove=not self._keepfiles) return results diff --git a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py index ff6f14b1152..cfd9d41a0b8 100644 --- a/pyomo/solvers/plugins/solvers/GUROBI_RUN.py +++ b/pyomo/solvers/plugins/solvers/GUROBI_RUN.py @@ -16,7 +16,7 @@ This script is run using the Gurobi/system python. Do not assume any third party packages are available! """ -from gurobipy import * +from gurobipy import gurobi, read, GRB import sys if sys.version_info[0] < 3: from itertools import izip as zip diff --git a/pyomo/solvers/plugins/solvers/IPOPT.py b/pyomo/solvers/plugins/solvers/IPOPT.py index b5b40342a88..a46915ec36a 100644 --- a/pyomo/solvers/plugins/solvers/IPOPT.py +++ b/pyomo/solvers/plugins/solvers/IPOPT.py @@ -10,13 +10,15 @@ import os -import pyomo.common -import pyutilib.misc +from pyomo.common import Executable +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.opt.base import ProblemFormat, ResultsFormat +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverStatus, SolverResults, TerminationCondition +from pyomo.opt.solver import SystemCallSolver import logging logger = logging.getLogger('pyomo.solvers') @@ -49,7 +51,7 @@ def __init__(self, **kwds): self.set_problem_format(ProblemFormat.nl) # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.integer = False self._capabilities.quadratic_objective = True @@ -61,7 +63,7 @@ def _default_results_format(self, prob_format): return ResultsFormat.sol def _default_executable(self): - executable = pyomo.common.Executable("ipopt") + executable = Executable("ipopt") if not executable: logger.warning("Could not locate the 'ipopt' executable, " "which is required for solver %s" % self.name) @@ -76,7 +78,7 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = pyutilib.subprocess.run( [solver_exec,"-v"], timelimit=1 ) + results = run( [solver_exec,"-v"], timelimit=1 ) return _extract_version(results[1]) def create_command_line(self, executable, problem_files): @@ -88,7 +90,7 @@ def create_command_line(self, executable, problem_files): # Define log file # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix="_ipopt.log") fname = problem_files[0] @@ -170,7 +172,7 @@ def create_command_line(self, executable, problem_files): default_of_name)) # Now write the new options file - options_filename = pyutilib.services.TempfileManager.\ + options_filename = TempfileManager.\ create_tempfile(suffix="_ipopt.opt") with open(options_filename, "w") as f: for key, val in of_opt: @@ -185,7 +187,7 @@ def create_command_line(self, executable, problem_files): # Merge with any options coming in through the environment env[envstr] = " ".join(env_opt) - return pyutilib.misc.Bunch(cmd=cmd, log_file=self._log_file, env=env) + return Bunch(cmd=cmd, log_file=self._log_file, env=env) def process_output(self, rc): if os.path.exists(self._results_file): diff --git a/pyomo/solvers/plugins/solvers/PICO.py b/pyomo/solvers/plugins/solvers/PICO.py index ba552849d89..0884f995c55 100644 --- a/pyomo/solvers/plugins/solvers/PICO.py +++ b/pyomo/solvers/plugins/solvers/PICO.py @@ -8,21 +8,21 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ - import re import os from six import iteritems -import pyomo.common -import pyutilib.common -import pyutilib.common -import pyutilib.misc +from pyomo.common import Executable +from pyutilib.common import ApplicationError +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverResults, SolverStatus, TerminationCondition, SolutionStatus, ProblemSense, Solution +from pyomo.opt.solver import SystemCallSolver from pyomo.solvers.mockmip import MockMIP import logging @@ -92,7 +92,7 @@ def __init__(self, **kwds): self.set_problem_format(ProblemFormat.cpxlp) # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.integer = True #self._capabilities.sos1 = True @@ -119,7 +119,7 @@ def _default_results_format(self, prob_format): return ResultsFormat.soln def _default_executable(self): - executable = pyomo.common.Executable("PICO_deprecated_not_supported") + executable = Executable("PICO_deprecated_not_supported") if not executable: logger.warning("Could not locate the 'PICO' executable, " "which is required for solver %s" % self.name) @@ -134,7 +134,7 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = pyutilib.subprocess.run([solver_exec, "--version"], timelimit=1) + results = run([solver_exec, "--version"], timelimit=1) # 'PICO --version' seems to print 'pebble , PICO # we don't wan't the pebble version being advertised so we split # the string at the comma before extracting a version number. It @@ -151,7 +151,7 @@ def create_command_line(self,executable,problem_files): # Define log file # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix="PICO.log") problem_filename_prefix = problem_files[0] @@ -240,7 +240,7 @@ def _check_and_escape_options(options): cmd.extend(['--output', self._soln_file, problem_files[0]]) - return pyutilib.misc.Bunch(cmd=cmd, log_file=self._log_file, env=env) + return Bunch(cmd=cmd, log_file=self._log_file, env=env) def process_logfile(self): """ @@ -423,7 +423,7 @@ class MockPICO(PICOSHELL,MockMIP): def __init__(self, **kwds): try: PICOSHELL.__init__(self,**kwds) - except pyutilib.common.ApplicationError: #pragma:nocover + except ApplicationError: #pragma:nocover pass #pragma:nocover MockMIP.__init__(self,"pico") diff --git a/pyomo/solvers/plugins/solvers/SCIPAMPL.py b/pyomo/solvers/plugins/solvers/SCIPAMPL.py index f0f15dbf8e3..76ad8e00959 100644 --- a/pyomo/solvers/plugins/solvers/SCIPAMPL.py +++ b/pyomo/solvers/plugins/solvers/SCIPAMPL.py @@ -10,13 +10,15 @@ import os -import pyomo.common -import pyutilib.misc +from pyomo.common import Executable +from pyutilib.misc import Options, Bunch +from pyutilib.services import TempfileManager +from pyutilib.subprocess import run -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.opt.base import ProblemFormat, ResultsFormat +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverStatus, TerminationCondition, SolutionStatus +from pyomo.opt.solver import SystemCallSolver import logging logger = logging.getLogger('pyomo.solvers') @@ -48,7 +50,7 @@ def __init__(self, **kwds): self.set_problem_format(ProblemFormat.nl) # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.integer = True self._capabilities.quadratic_objective = True @@ -60,7 +62,7 @@ def _default_results_format(self, prob_format): return ResultsFormat.sol def _default_executable(self): - executable = pyomo.common.Executable("scipampl") + executable = Executable("scipampl") if not executable: logger.warning("Could not locate the 'scipampl' executable, " "which is required for solver %s" % self.name) @@ -75,7 +77,7 @@ def _get_version(self): solver_exec = self.executable() if solver_exec is None: return _extract_version('') - results = pyutilib.subprocess.run( [solver_exec], timelimit=1 ) + results = run( [solver_exec], timelimit=1 ) return _extract_version(results[1]) def create_command_line(self, executable, problem_files): @@ -87,7 +89,7 @@ def create_command_line(self, executable, problem_files): # Define log file # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix="_scipampl.log") fname = problem_files[0] @@ -156,7 +158,7 @@ def create_command_line(self, executable, problem_files): % (default_of_name, default_of_name)) # Now write the new options file - options_filename = pyutilib.services.TempfileManager.\ + options_filename = TempfileManager.\ create_tempfile(suffix="_scip.set") with open(options_filename, "w") as f: for line in of_opt: @@ -164,7 +166,7 @@ def create_command_line(self, executable, problem_files): cmd.append(options_filename) - return pyutilib.misc.Bunch(cmd=cmd, log_file=self._log_file, env=env) + return Bunch(cmd=cmd, log_file=self._log_file, env=env) def _postsolve(self): results = super(SCIPAMPL, self)._postsolve() diff --git a/pyomo/solvers/plugins/solvers/XPRESS.py b/pyomo/solvers/plugins/solvers/XPRESS.py index 70fda964626..e7518481ea8 100644 --- a/pyomo/solvers/plugins/solvers/XPRESS.py +++ b/pyomo/solvers/plugins/solvers/XPRESS.py @@ -13,14 +13,15 @@ import re import logging -import pyomo.common -import pyutilib.common -import pyutilib.misc - -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.common import Executable +from pyutilib.common import ApplicationError +from pyutilib.misc import Options, Bunch, yaml_fix +from pyutilib.services import TempfileManager + +from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverResults, SolverStatus, TerminationCondition, ProblemSense, Solution +from pyomo.opt.solver import ILMLicensedSystemCallSolver from pyomo.solvers.mockmip import MockMIP logger = logging.getLogger('pyomo.solvers') @@ -98,7 +99,7 @@ def __init__(self, **kwds): # # Note: Undefined capabilities default to 'None' - self._capabilities = pyutilib.misc.Options() + self._capabilities = Options() self._capabilities.linear = True self._capabilities.quadratic_objective = True self._capabilities.quadratic_constraint = True @@ -117,7 +118,7 @@ def warm_start_capable(self): return False def _default_executable(self): - executable = pyomo.common.Executable("optimizer") + executable = Executable("optimizer") if not executable: logger.warning("Could not locate the 'optimizer' executable, " "which is required for solver %s" % self.name) @@ -140,14 +141,14 @@ def create_command_line(self, executable, problem_files): # The log file in XPRESS contains the solution trace, but the solver status can be found in the solution file. # if self._log_file is None: - self._log_file = pyutilib.services.TempfileManager.\ + self._log_file = TempfileManager.\ create_tempfile(suffix = '.xpress.log') # # Define solution file # As indicated above, contains (in XML) both the solution and solver status. # - self._soln_file = pyutilib.services.TempfileManager.\ + self._soln_file = TempfileManager.\ create_tempfile(suffix = '.xpress.wrtsol') # @@ -190,7 +191,7 @@ def create_command_line(self, executable, problem_files): # dump the script and warm-start file names for the # user if we're keeping files around. if self._keepfiles: - script_fname = pyutilib.services.TempfileManager.create_tempfile(suffix = '.xpress.script') + script_fname = TempfileManager.create_tempfile(suffix = '.xpress.script') tmp = open(script_fname,'w') tmp.write(script) tmp.close() @@ -203,7 +204,7 @@ def create_command_line(self, executable, problem_files): cmd = [executable] if self._timer: cmd.insert(0, self._timer) - return pyutilib.misc.Bunch(cmd=cmd, script=script, + return Bunch(cmd=cmd, script=script, log_file=self._log_file, env=None) def process_logfile(self): @@ -301,7 +302,7 @@ def process_logfile(self): results.solver.termination_message = ' '.join(tokens) try: - results.solver.termination_message = pyutilib.misc.yaml_fix(results.solver.termination_message) + results.solver.termination_message = yaml_fix(results.solver.termination_message) except: pass return results @@ -368,7 +369,8 @@ def process_soln_file(self, results): variable_name = name variable_value = primary_value variable_reduced_cost = None - + ### TODO: What is going on here with field_name, and shortly thereafter, with variable_status and whatnot? They've never been defined. + ### It seems like this is trying to mimic the CPLEX solver but has some issues if (extract_reduced_costs is True) and (field_name == "reducedCost"): variable_reduced_cost = tertiary_value @@ -426,7 +428,7 @@ class MockXPRESS(XPRESS_shell,MockMIP): def __init__(self, **kwds): try: XPRESS_shell.__init__(self, **kwds) - except pyutilib.common.ApplicationError: #pragma:nocover + except ApplicationError: #pragma:nocover pass #pragma:nocover MockMIP.__init__(self,"cplex") diff --git a/pyomo/solvers/plugins/solvers/cplex_direct.py b/pyomo/solvers/plugins/solvers/cplex_direct.py index 20baff38df7..06d222d6c84 100644 --- a/pyomo/solvers/plugins/solvers/cplex_direct.py +++ b/pyomo/solvers/plugins/solvers/cplex_direct.py @@ -10,11 +10,13 @@ import logging import re +import six import sys -import pyomo.common + from pyutilib.misc import Bunch from pyutilib.services import TempfileManager from pyomo.common.collections import ComponentSet, ComponentMap +from pyomo.core.base import Suffix, Var, Constraint, SOSConstraint, Objective from pyomo.core.expr.numvalue import is_fixed from pyomo.core.expr.numvalue import value from pyomo.repn import generate_standard_repn @@ -157,20 +159,29 @@ def _init(self): def _apply_solver(self): if not self._save_results: for block in self._pyomo_model.block_data_objects(descend_into=True, active=True): - for var in block.component_data_objects(ctype=pyomo.core.base.var.Var, descend_into=False, active=True, sort=False): + for var in block.component_data_objects(ctype=Var, descend_into=False, active=True, sort=False): var.stale = True - _log_file = self._log_file - if self.version() >= (12, 10): - _log_file = open(self._log_file, 'w') + # In recent versions of CPLEX it is helpful to manually open the + # log file and then explicitly close it after CPLEX is finished. + # This ensures that the file is closed (and unlocked) on Windows + # before the TempfileManager (or user) attempts to delete the + # log file. Passing in an opened file object is supported at + # least as far back as CPLEX 12.5.1 [the oldest version + # supported by IBM as of 1 Oct 2020] + if self.version() >= (12, 5, 1) \ + and isinstance(self._log_file, six.string_types): + _log_file = (open(self._log_file, 'a'),) + _close_log_file = True + else: + _log_file = (self._log_file,) + _close_log_file = False + if self._tee: + def _process_stream(arg): + sys.stdout.write(arg) + return arg + _log_file += (_process_stream,) try: - if self._tee: - def _process_stream(arg): - sys.stdout.write(arg) - return arg - self._solver_model.set_results_stream(_log_file, _process_stream) - else: - self._solver_model.set_results_stream(_log_file) - + self._solver_model.set_results_stream(*_log_file) if self._keepfiles: print("Solver log file: "+self._log_file) @@ -240,8 +251,9 @@ def _process_stream(arg): t1 = time.time() self._wallclock_time = t1 - t0 finally: - if self.version() >= (12, 10): - _log_file.close() + self._solver_model.set_results_stream(None) + if _close_log_file: + _log_file[0].close() # FIXME: can we get a return code indicating if CPLEX had a significant failure? return Bunch(rc=None, log=None) @@ -359,7 +371,7 @@ def _set_instance(self, model, kwds={}): def _add_block(self, block): var_data = _VariableData(self._solver_model) for var in block.component_data_objects( - ctype=pyomo.core.base.var.Var, descend_into=True, active=True, sort=True + ctype=Var, descend_into=True, active=True, sort=True ): self._add_var(var, var_data) var_data.store_in_cplex() @@ -367,7 +379,7 @@ def _add_block(self, block): lin_con_data = _LinearConstraintData(self._solver_model) for sub_block in block.block_data_objects(descend_into=True, active=True): for con in sub_block.component_data_objects( - ctype=pyomo.core.base.constraint.Constraint, + ctype=Constraint, descend_into=False, active=True, sort=True, @@ -379,7 +391,7 @@ def _add_block(self, block): self._add_constraint(con, lin_con_data) for con in sub_block.component_data_objects( - ctype=pyomo.core.base.sos.SOSConstraint, + ctype=SOSConstraint, descend_into=False, active=True, sort=True, @@ -388,7 +400,7 @@ def _add_block(self, block): obj_counter = 0 for obj in sub_block.component_data_objects( - ctype=pyomo.core.base.objective.Objective, + ctype=Objective, descend_into=False, active=True, ): diff --git a/pyomo/solvers/plugins/solvers/cplex_persistent.py b/pyomo/solvers/plugins/solvers/cplex_persistent.py index d9866f27542..e91346548c6 100644 --- a/pyomo/solvers/plugins/solvers/cplex_persistent.py +++ b/pyomo/solvers/plugins/solvers/cplex_persistent.py @@ -9,10 +9,6 @@ # ___________________________________________________________________________ from pyomo.core.expr.numvalue import value -from pyomo.core.base.PyomoModel import ConcreteModel -from pyomo.core.base.constraint import Constraint -from pyomo.core.base.var import Var -from pyomo.core.base.sos import SOSConstraint from pyomo.solvers.plugins.solvers.cplex_direct import CPLEXDirect from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver from pyomo.opt.base import SolverFactory diff --git a/pyomo/solvers/plugins/solvers/direct_solver.py b/pyomo/solvers/plugins/solvers/direct_solver.py index 9b7641af07e..2e0a48008b8 100644 --- a/pyomo/solvers/plugins/solvers/direct_solver.py +++ b/pyomo/solvers/plugins/solvers/direct_solver.py @@ -8,15 +8,18 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ +import time +import logging + from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import DirectOrPersistentSolver from pyomo.core.base.block import _BlockData from pyomo.core.kernel.block import IBlock from pyomo.core.base.suffix import active_import_suffix_generator from pyomo.core.kernel.suffix import import_suffix_generator -import pyutilib.misc -import pyutilib.common -import time +from pyutilib.common import ApplicationError +from pyutilib.misc import Options +logger = logging.getLogger('pyomo.solvers') class DirectSolver(DirectOrPersistentSolver): """ @@ -103,7 +106,7 @@ def solve(self, *args, **kwds): orig_options = self.options - self.options = pyutilib.misc.Options() + self.options = Options() self.options.update(orig_options) self.options.update(kwds.pop('options', {})) self.options.update( @@ -139,7 +142,7 @@ def solve(self, *args, **kwds): "See the solver log above for diagnostic information." ) elif hasattr(_status, 'log') and _status.log: logger.error("Solver log:\n" + str(_status.log)) - raise pyutilib.common.ApplicationError( + raise ApplicationError( "Solver (%s) did not exit normally" % self.name) solve_completion_time = time.time() if self._report_timing: diff --git a/pyomo/solvers/plugins/solvers/glpk_direct.py b/pyomo/solvers/plugins/solvers/glpk_direct.py index 6f37d2d616f..a6cfb1d5a63 100644 --- a/pyomo/solvers/plugins/solvers/glpk_direct.py +++ b/pyomo/solvers/plugins/solvers/glpk_direct.py @@ -40,10 +40,9 @@ def configure_glpk_direct(): from pyutilib.misc import Bunch, Options -from pyomo.opt.base import * -from pyomo.opt.base.solvers import _extract_version -from pyomo.opt.results import * -from pyomo.opt.solver import * +from pyomo.opt.base import OptSolver +from pyomo.opt.base.solvers import _extract_version, SolverFactory +from pyomo.opt.results import SolverResults, SolverStatus, SolutionStatus, ProblemSense from pyomo.core.base.numvalue import value import logging diff --git a/pyomo/solvers/plugins/solvers/gurobi_direct.py b/pyomo/solvers/plugins/solvers/gurobi_direct.py index 533ad22a701..cdb2a56d5d8 100644 --- a/pyomo/solvers/plugins/solvers/gurobi_direct.py +++ b/pyomo/solvers/plugins/solvers/gurobi_direct.py @@ -244,7 +244,7 @@ def _set_instance(self, model, kwds={}): e = sys.exc_info()[1] msg = ("Unable to create Gurobi model. " "Have you installed the Python " - "bindings for Gurboi?\n\n\t"+ + "bindings for Gurobi?\n\n\t"+ "Error message: {0}".format(e)) raise Exception(msg) diff --git a/pyomo/solvers/plugins/solvers/gurobi_persistent.py b/pyomo/solvers/plugins/solvers/gurobi_persistent.py index 32bc59d1fbc..7b127a50fe4 100644 --- a/pyomo/solvers/plugins/solvers/gurobi_persistent.py +++ b/pyomo/solvers/plugins/solvers/gurobi_persistent.py @@ -8,7 +8,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core.base.PyomoModel import ConcreteModel from pyomo.solvers.plugins.solvers.gurobi_direct import GurobiDirect from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver from pyomo.core.expr.numvalue import value, is_fixed diff --git a/pyomo/solvers/plugins/solvers/persistent_solver.py b/pyomo/solvers/plugins/solvers/persistent_solver.py index 98e23b6c9b0..d7d599774aa 100644 --- a/pyomo/solvers/plugins/solvers/persistent_solver.py +++ b/pyomo/solvers/plugins/solvers/persistent_solver.py @@ -9,22 +9,20 @@ # ___________________________________________________________________________ from pyomo.solvers.plugins.solvers.direct_or_persistent_solver import DirectOrPersistentSolver -from pyomo.core.base.PyomoModel import ConcreteModel -from pyomo.core.base.block import _BlockData, Block -from pyomo.core.base.objective import Objective +from pyomo.core.base.block import _BlockData from pyomo.core.kernel.block import IBlock from pyomo.core.base.suffix import active_import_suffix_generator from pyomo.core.kernel.suffix import import_suffix_generator -from pyomo.core.expr import current as EXPR from pyomo.core.expr.numvalue import native_numeric_types -import pyutilib.misc -import pyutilib.common -import time -import logging from pyomo.core.base.constraint import Constraint from pyomo.core.base.var import Var from pyomo.core.base.sos import SOSConstraint +from pyutilib.common import ApplicationError +from pyutilib.misc import Options + +import time +import logging logger = logging.getLogger('pyomo.solvers') @@ -450,7 +448,7 @@ def solve(self, *args, **kwds): orig_options = self.options - self.options = pyutilib.misc.Options() + self.options = Options() self.options.update(orig_options) self.options.update(kwds.pop('options', {})) self.options.update(self._options_string_to_dict(kwds.pop('options_string', ''))) @@ -485,7 +483,7 @@ def solve(self, *args, **kwds): "See the solver log above for diagnostic information.") elif hasattr(_status, 'log') and _status.log: logger.error("Solver log:\n" + str(_status.log)) - raise pyutilib.common.ApplicationError( + raise ApplicationError( "Solver (%s) did not exit normally" % self.name) solve_completion_time = time.time() if self._report_timing: diff --git a/pyomo/solvers/plugins/testdriver/mip.py b/pyomo/solvers/plugins/testdriver/mip.py index 7f4fed597f1..0e28ad0e4a3 100644 --- a/pyomo/solvers/plugins/testdriver/mip.py +++ b/pyomo/solvers/plugins/testdriver/mip.py @@ -15,7 +15,7 @@ import pyomo.common from pyutilib.misc import Options -from pyomo.common.plugin import * +from pyomo.common.plugin import Plugin, implements, alias import pyomo.opt old_tempdir = pyutilib.services.TempfileManager.tempdir diff --git a/pyomo/solvers/tests/checks/test_CBCplugin.py b/pyomo/solvers/tests/checks/test_CBCplugin.py index 38fd3283083..46c1a8d4661 100644 --- a/pyomo/solvers/tests/checks/test_CBCplugin.py +++ b/pyomo/solvers/tests/checks/test_CBCplugin.py @@ -7,14 +7,19 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + import os import sys from os.path import dirname, abspath import pyutilib.th as unittest -from pyomo.environ import * -from pyomo.opt import * +from pyomo.environ import (ConcreteModel, Var, Objective, RangeSet, + Constraint, Reals, NonNegativeIntegers, + NonNegativeReals, Integers, Binary, + maximize, minimize) +from pyomo.opt import (SolverFactory, ProblemSense, + TerminationCondition, SolverStatus) cbc_available = SolverFactory('cbc', solver_io='lp').available(exception_flag=False) diff --git a/pyomo/solvers/tests/checks/test_CPLEXDirect.py b/pyomo/solvers/tests/checks/test_CPLEXDirect.py index f1954885483..40e1cffd641 100644 --- a/pyomo/solvers/tests/checks/test_CPLEXDirect.py +++ b/pyomo/solvers/tests/checks/test_CPLEXDirect.py @@ -12,8 +12,11 @@ import pyutilib.th as unittest -from pyomo.environ import * -from pyomo.opt import * +from pyomo.environ import (ConcreteModel, AbstractModel, Var, Objective, + Block, Constraint, Suffix, NonNegativeIntegers, + NonNegativeReals, Integers, Binary, is_fixed, + value) +from pyomo.opt import SolverFactory, TerminationCondition, SolutionStatus from pyomo.solvers.plugins.solvers.cplex_direct import (_CplexExpr, _LinearConstraintData, _VariableData) diff --git a/pyomo/solvers/tests/checks/test_CPLEXPersistent.py b/pyomo/solvers/tests/checks/test_CPLEXPersistent.py index f81e4a5310d..28f3b33610f 100644 --- a/pyomo/solvers/tests/checks/test_CPLEXPersistent.py +++ b/pyomo/solvers/tests/checks/test_CPLEXPersistent.py @@ -1,7 +1,19 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -from pyomo.environ import * -from pyomo.opt import * +import pyomo.environ +from pyomo.core import (ConcreteModel, Var, Objective, + Constraint, NonNegativeReals) +from pyomo.opt import SolverFactory try: import cplex diff --git a/pyomo/solvers/tests/checks/test_GAMS.py b/pyomo/solvers/tests/checks/test_GAMS.py index f7fe0655e62..b843a615c05 100644 --- a/pyomo/solvers/tests/checks/test_GAMS.py +++ b/pyomo/solvers/tests/checks/test_GAMS.py @@ -9,7 +9,9 @@ # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Objective, Constraint, maximize, Expression, log10 +from pyomo.opt import SolverFactory, TerminationCondition + from pyomo.solvers.plugins.solvers.GAMS import ( GAMSShell, GAMSDirect, gdxcc_available ) diff --git a/pyomo/solvers/tests/checks/test_cbc.py b/pyomo/solvers/tests/checks/test_cbc.py index e77ecb0e63a..ab92b88b3f7 100644 --- a/pyomo/solvers/tests/checks/test_cbc.py +++ b/pyomo/solvers/tests/checks/test_cbc.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import SolverFactory, ConcreteModel, Var, Constraint, Objective, Integers, Boolean import pyutilib.th as unittest from pyutilib.misc import capture_output diff --git a/pyomo/solvers/tests/checks/test_cplex.py b/pyomo/solvers/tests/checks/test_cplex.py index a0959fee9f4..877b1514397 100644 --- a/pyomo/solvers/tests/checks/test_cplex.py +++ b/pyomo/solvers/tests/checks/test_cplex.py @@ -10,12 +10,11 @@ import os -import pyutilib +from pyutilib.services import TempfileManager import pyutilib.th as unittest import pyomo.kernel as pmo from pyomo.core import Binary, ConcreteModel, Constraint, Objective, Var, Integers, RangeSet, minimize, quicksum, Suffix -from pyomo.environ import * from pyomo.opt import ProblemFormat, convert_problem, SolverFactory, BranchDirection from pyomo.solvers.plugins.solvers.CPLEX import CPLEXSHELL, MockCPLEX, _validate_file_name @@ -69,12 +68,12 @@ class CPLEXShellWritePrioritiesFile(unittest.TestCase): def setUp(self): self.mock_model = self.get_mock_model() self.mock_cplex_shell = self.get_mock_cplex_shell(self.mock_model) - self.mock_cplex_shell._priorities_file_name = pyutilib.services.TempfileManager.create_tempfile( + self.mock_cplex_shell._priorities_file_name = TempfileManager.create_tempfile( suffix=".cplex.ord" ) def tearDown(self): - pyutilib.services.TempfileManager.clear_tempfiles() + TempfileManager.clear_tempfiles() def get_mock_model(self): model = ConcreteModel() diff --git a/pyomo/solvers/tests/checks/test_gurobi_persistent.py b/pyomo/solvers/tests/checks/test_gurobi_persistent.py index c723e7ff84b..091b9303584 100644 --- a/pyomo/solvers/tests/checks/test_gurobi_persistent.py +++ b/pyomo/solvers/tests/checks/test_gurobi_persistent.py @@ -1,5 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo from pyomo.core.expr.taylor_series import taylor_series_expansion try: import gurobipy @@ -12,13 +22,13 @@ class TestGurobiPersistent(unittest.TestCase): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_basics(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(-10, 10)) - m.y = pe.Var() - m.obj = pe.Objective(expr=m.x**2 + m.y**2) - m.c1 = pe.Constraint(expr=m.y >= 2*m.x + 1) + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(-10, 10)) + m.y = pyo.Var() + m.obj = pyo.Objective(expr=m.x**2 + m.y**2) + m.c1 = pyo.Constraint(expr=m.y >= 2*m.x + 1) - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) self.assertEqual(opt.get_model_attr('NumVars'), 2) @@ -34,7 +44,7 @@ def test_basics(self): self.assertAlmostEqual(m.dual[m.c1], -0.4) del m.dual - m.c2 = pe.Constraint(expr=m.y >= -m.x + 1) + m.c2 = pyo.Constraint(expr=m.y >= -m.x + 1) opt.add_constraint(m.c2) self.assertEqual(opt.get_model_attr('NumVars'), 2) self.assertEqual(opt.get_model_attr('NumConstrs'), 2) @@ -75,7 +85,7 @@ def test_basics(self): self.assertEqual(opt.get_var_attr(m.x, 'LB'), -5) self.assertEqual(opt.get_var_attr(m.x, 'UB'), 5) - m.c2 = pe.Constraint(expr=m.y >= m.x**2) + m.c2 = pyo.Constraint(expr=m.y >= m.x**2) opt.add_constraint(m.c2) self.assertEqual(opt.get_model_attr('NumVars'), 2) self.assertEqual(opt.get_model_attr('NumConstrs'), 1) @@ -87,7 +97,7 @@ def test_basics(self): self.assertEqual(opt.get_model_attr('NumConstrs'), 1) self.assertEqual(opt.get_model_attr('NumQConstrs'), 0) - m.z = pe.Var() + m.z = pyo.Var() opt.add_var(m.z) self.assertEqual(opt.get_model_attr('NumVars'), 3) opt.remove_var(m.z) @@ -96,14 +106,14 @@ def test_basics(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update1(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.z = pe.Var() - m.obj = pe.Objective(expr=m.z) - m.c1 = pe.Constraint(expr=m.z >= m.x**2 + m.y**2) - - opt = pe.SolverFactory('gurobi_persistent') + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.z = pyo.Var() + m.obj = pyo.Objective(expr=m.z) + m.c1 = pyo.Constraint(expr=m.z >= m.x**2 + m.y**2) + + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) self.assertEqual(opt._solver_model.getAttr('NumQConstrs'), 0) @@ -118,14 +128,14 @@ def test_update1(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update2(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.z = pe.Var() - m.obj = pe.Objective(expr=m.z) - m.c2 = pe.Constraint(expr=m.x + m.y == 1) - - opt = pe.SolverFactory('gurobi_persistent') + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.z = pyo.Var() + m.obj = pyo.Objective(expr=m.z) + m.c2 = pyo.Constraint(expr=m.x + m.y == 1) + + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) self.assertEqual(opt._solver_model.getAttr('NumConstrs'), 0) @@ -140,18 +150,18 @@ def test_update2(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update3(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.z = pe.Var() - m.obj = pe.Objective(expr=m.z) - m.c1 = pe.Constraint(expr=m.z >= m.x**2 + m.y**2) - - opt = pe.SolverFactory('gurobi_persistent') + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.z = pyo.Var() + m.obj = pyo.Objective(expr=m.z) + m.c1 = pyo.Constraint(expr=m.z >= m.x**2 + m.y**2) + + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) opt.update() self.assertEqual(opt._solver_model.getAttr('NumQConstrs'), 1) - m.c2 = pe.Constraint(expr=m.y >= m.x**2) + m.c2 = pyo.Constraint(expr=m.y >= m.x**2) opt.add_constraint(m.c2) self.assertEqual(opt._solver_model.getAttr('NumQConstrs'), 1) opt.remove_constraint(m.c2) @@ -160,18 +170,18 @@ def test_update3(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update4(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.z = pe.Var() - m.obj = pe.Objective(expr=m.z) - m.c1 = pe.Constraint(expr=m.z >= m.x + m.y) - - opt = pe.SolverFactory('gurobi_persistent') + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.z = pyo.Var() + m.obj = pyo.Objective(expr=m.z) + m.c1 = pyo.Constraint(expr=m.z >= m.x + m.y) + + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) opt.update() self.assertEqual(opt._solver_model.getAttr('NumConstrs'), 1) - m.c2 = pe.Constraint(expr=m.y >= m.x) + m.c2 = pyo.Constraint(expr=m.y >= m.x) opt.add_constraint(m.c2) self.assertEqual(opt._solver_model.getAttr('NumConstrs'), 1) opt.remove_constraint(m.c2) @@ -180,14 +190,14 @@ def test_update4(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update5(self): - m = pe.ConcreteModel() - m.a = pe.Set(initialize=[1,2,3], ordered=True) - m.x = pe.Var(m.a, within=pe.Binary) - m.y = pe.Var(within=pe.Binary) - m.obj = pe.Objective(expr=m.y) - m.c1 = pe.SOSConstraint(var=m.x, sos=1) - - opt = pe.SolverFactory('gurobi_persistent') + m = pyo.ConcreteModel() + m.a = pyo.Set(initialize=[1,2,3], ordered=True) + m.x = pyo.Var(m.a, within=pyo.Binary) + m.y = pyo.Var(within=pyo.Binary) + m.obj = pyo.Objective(expr=m.y) + m.c1 = pyo.SOSConstraint(var=m.x, sos=1) + + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) self.assertEqual(opt._solver_model.getAttr('NumSOS'), 0) @@ -202,18 +212,18 @@ def test_update5(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update6(self): - m = pe.ConcreteModel() - m.a = pe.Set(initialize=[1,2,3], ordered=True) - m.x = pe.Var(m.a, within=pe.Binary) - m.y = pe.Var(within=pe.Binary) - m.obj = pe.Objective(expr=m.y) - m.c1 = pe.SOSConstraint(var=m.x, sos=1) - - opt = pe.SolverFactory('gurobi_persistent') + m = pyo.ConcreteModel() + m.a = pyo.Set(initialize=[1,2,3], ordered=True) + m.x = pyo.Var(m.a, within=pyo.Binary) + m.y = pyo.Var(within=pyo.Binary) + m.obj = pyo.Objective(expr=m.y) + m.c1 = pyo.SOSConstraint(var=m.x, sos=1) + + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) opt.update() self.assertEqual(opt._solver_model.getAttr('NumSOS'), 1) - m.c2 = pe.SOSConstraint(var=m.x, sos=2) + m.c2 = pyo.SOSConstraint(var=m.x, sos=2) opt.add_sos_constraint(m.c2) self.assertEqual(opt._solver_model.getAttr('NumSOS'), 1) opt.remove_sos_constraint(m.c2) @@ -222,11 +232,11 @@ def test_update6(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_update7(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) self.assertEqual(opt._solver_model.getAttr('NumVars'), 0) @@ -248,44 +258,44 @@ def test_update7(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_linear_constraint_attr(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.c = pe.Constraint(expr=m.x + m.y == 1) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.c = pyo.Constraint(expr=m.x + m.y == 1) - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) opt.set_linear_constraint_attr(m.c, 'Lazy', 1) self.assertEqual(opt.get_linear_constraint_attr(m.c, 'Lazy'), 1) @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_quadratic_constraint_attr(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.y = pe.Var() - m.c = pe.Constraint(expr=m.y >= m.x**2) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.y = pyo.Var() + m.c = pyo.Constraint(expr=m.y >= m.x**2) - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) self.assertEqual(opt.get_quadratic_constraint_attr(m.c, 'QCRHS'), 0) @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_var_attr(self): - m = pe.ConcreteModel() - m.x = pe.Var(within=pe.Binary) + m = pyo.ConcreteModel() + m.x = pyo.Var(within=pyo.Binary) - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) opt.set_var_attr(m.x, 'Start', 1) self.assertEqual(opt.get_var_attr(m.x, 'Start'), 1) @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_callback(self): - m = pe.ConcreteModel() - m.x = pe.Var(bounds=(0, 4)) - m.y = pe.Var(within=pe.Integers, bounds=(0, None)) - m.obj = pe.Objective(expr=2*m.x + m.y) - m.cons = pe.ConstraintList() + m = pyo.ConcreteModel() + m.x = pyo.Var(bounds=(0, 4)) + m.y = pyo.Var(within=pyo.Integers, bounds=(0, None)) + m.obj = pyo.Objective(expr=2*m.x + m.y) + m.cons = pyo.ConstraintList() def _add_cut(xval): m.x.value = xval @@ -294,7 +304,7 @@ def _add_cut(xval): _add_cut(0) _add_cut(4) - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) opt.set_gurobi_param('PreCrush', 1) opt.set_gurobi_param('LazyConstraints', 1) @@ -312,17 +322,17 @@ def _my_callback(cb_m, cb_opt, cb_where): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_add_column(self): - m = pe.ConcreteModel() - m.x = pe.Var(within=pe.NonNegativeReals) - m.c = pe.Constraint(expr=(0, m.x, 1)) - m.obj = pe.Objective(expr=-m.x) + m = pyo.ConcreteModel() + m.x = pyo.Var(within=pyo.NonNegativeReals) + m.c = pyo.Constraint(expr=(0, m.x, 1)) + m.obj = pyo.Objective(expr=-m.x) - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') opt.set_instance(m) opt.solve() self.assertAlmostEqual(m.x.value, 1) - m.y = pe.Var(within=pe.NonNegativeReals) + m.y = pyo.Var(within=pyo.NonNegativeReals) opt.add_column(m, m.y, -3, [m.c], [2]) opt.solve() @@ -332,35 +342,35 @@ def test_add_column(self): @unittest.skipIf(not gurobipy_available, "gurobipy is not available") def test_add_column_exceptions(self): - m = pe.ConcreteModel() - m.x = pe.Var() - m.c = pe.Constraint(expr=(0, m.x, 1)) - m.ci = pe.Constraint([1,2], rule=lambda m,i:(0,m.x,i+1)) - m.cd = pe.Constraint(expr=(0, -m.x, 1)) + m = pyo.ConcreteModel() + m.x = pyo.Var() + m.c = pyo.Constraint(expr=(0, m.x, 1)) + m.ci = pyo.Constraint([1,2], rule=lambda m,i:(0,m.x,i+1)) + m.cd = pyo.Constraint(expr=(0, -m.x, 1)) m.cd.deactivate() - m.obj = pe.Objective(expr=-m.x) + m.obj = pyo.Objective(expr=-m.x) - opt = pe.SolverFactory('gurobi_persistent') + opt = pyo.SolverFactory('gurobi_persistent') # set_instance not called self.assertRaises(RuntimeError, opt.add_column, m, m.x, 0, [m.c], [1]) opt.set_instance(m) - m2 = pe.ConcreteModel() - m2.y = pe.Var() - m2.c = pe.Constraint(expr=(0,m.x,1)) + m2 = pyo.ConcreteModel() + m2.y = pyo.Var() + m2.c = pyo.Constraint(expr=(0,m.x,1)) # different model than attached to opt self.assertRaises(RuntimeError, opt.add_column, m2, m2.y, 0, [], []) # pyomo var attached to different model self.assertRaises(RuntimeError, opt.add_column, m, m2.y, 0, [], []) - z = pe.Var() + z = pyo.Var() # pyomo var floating self.assertRaises(RuntimeError, opt.add_column, m, z, -2, [m.c, z], [1]) - m.y = pe.Var() + m.y = pyo.Var() # len(coefficents) == len(constraints) self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c], [1,2]) self.assertRaises(RuntimeError, opt.add_column, m, m.y, -2, [m.c, z], [1]) diff --git a/pyomo/solvers/tests/checks/test_mosek.py b/pyomo/solvers/tests/checks/test_mosek.py index e4e54fce903..3b19e60f5ee 100755 --- a/pyomo/solvers/tests/checks/test_mosek.py +++ b/pyomo/solvers/tests/checks/test_mosek.py @@ -1,9 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import pyutilib.th as unittest from pyomo.opt import (TerminationCondition, - SolutionStatus, - SolverStatus) -import pyomo.environ as aml + SolutionStatus) +import pyomo.environ as pyo import pyomo.kernel as pmo import sys @@ -31,13 +40,13 @@ def tearDown(self): def test_infeasible_lp(self): - model = aml.ConcreteModel() - model.X = aml.Var(within=aml.NonNegativeReals) - model.C1 = aml.Constraint(expr=model.X == 1) - model.C2 = aml.Constraint(expr=model.X == 2) - model.O = aml.Objective(expr=model.X) + model = pyo.ConcreteModel() + model.X = pyo.Var(within=pyo.NonNegativeReals) + model.C1 = pyo.Constraint(expr=model.X == 1) + model.C2 = pyo.Constraint(expr=model.X == 2) + model.O = pyo.Objective(expr=model.X) - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(model) self.assertIn(results.solver.termination_condition, @@ -46,11 +55,11 @@ def test_infeasible_lp(self): def test_unbounded_lp(self): - model = aml.ConcreteModel() - model.X = aml.Var() - model.O = aml.Objective(expr=model.X) + model = pyo.ConcreteModel() + model.X = pyo.Var() + model.O = pyo.Objective(expr=model.X) - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(model) self.assertIn(results.solver.termination_condition, @@ -59,11 +68,11 @@ def test_unbounded_lp(self): def test_optimal_lp(self): - model = aml.ConcreteModel() - model.X = aml.Var(within=aml.NonNegativeReals) - model.O = aml.Objective(expr=model.X) + model = pyo.ConcreteModel() + model.X = pyo.Var(within=pyo.NonNegativeReals) + model.O = pyo.Objective(expr=model.X) - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(model, load_solutions=False) self.assertEqual(results.solution.status, @@ -71,19 +80,19 @@ def test_optimal_lp(self): def test_get_duals_lp(self): - model = aml.ConcreteModel() - model.X = aml.Var(within=aml.NonNegativeReals) - model.Y = aml.Var(within=aml.NonNegativeReals) + model = pyo.ConcreteModel() + model.X = pyo.Var(within=pyo.NonNegativeReals) + model.Y = pyo.Var(within=pyo.NonNegativeReals) - model.C1 = aml.Constraint(expr=2*model.X + model.Y >= 8) - model.C2 = aml.Constraint(expr=model.X + 3*model.Y >= 6) + model.C1 = pyo.Constraint(expr=2*model.X + model.Y >= 8) + model.C2 = pyo.Constraint(expr=model.X + 3*model.Y >= 6) - model.O = aml.Objective(expr=model.X + model.Y) + model.O = pyo.Objective(expr=model.X + model.Y) - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(model, suffixes=['dual'], load_solutions=False) - model.dual = aml.Suffix(direction=aml.Suffix.IMPORT) + model.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT) model.solutions.load_from(results) self.assertAlmostEqual(model.dual[model.C1], 0.4, 4) @@ -91,13 +100,13 @@ def test_get_duals_lp(self): def test_infeasible_mip(self): - model = aml.ConcreteModel() - model.X = aml.Var(within=aml.NonNegativeIntegers) - model.C1 = aml.Constraint(expr=model.X == 1) - model.C2 = aml.Constraint(expr=model.X == 2) - model.O = aml.Objective(expr=model.X) + model = pyo.ConcreteModel() + model.X = pyo.Var(within=pyo.NonNegativeIntegers) + model.C1 = pyo.Constraint(expr=model.X == 1) + model.C2 = pyo.Constraint(expr=model.X == 2) + model.O = pyo.Objective(expr=model.X) - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(model) self.assertIn(results.solver.termination_condition, @@ -106,12 +115,12 @@ def test_infeasible_mip(self): def test_unbounded_mip(self): - model = aml.AbstractModel() - model.X = aml.Var(within=aml.Integers) - model.O = aml.Objective(expr=model.X) + model = pyo.AbstractModel() + model.X = pyo.Var(within=pyo.Integers) + model.O = pyo.Objective(expr=model.X) instance = model.create_instance() - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(instance) self.assertIn(results.solver.termination_condition, @@ -120,11 +129,11 @@ def test_unbounded_mip(self): def test_optimal_mip(self): - model = aml.ConcreteModel() - model.X = aml.Var(within=aml.NonNegativeIntegers) - model.O = aml.Objective(expr=model.X) + model = pyo.ConcreteModel() + model.X = pyo.Var(within=pyo.NonNegativeIntegers) + model.O = pyo.Objective(expr=model.X) - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(model, load_solutions=False) self.assertEqual(results.solution.status, @@ -132,11 +141,11 @@ def test_optimal_mip(self): def test_optimal_mip(self): - model = aml.ConcreteModel() - model.X = aml.Var(within=aml.NonNegativeIntegers) - model.O = aml.Objective(expr=model.X) + model = pyo.ConcreteModel() + model.X = pyo.Var(within=pyo.NonNegativeIntegers) + model.O = pyo.Objective(expr=model.X) - opt = aml.SolverFactory("mosek") + opt = pyo.SolverFactory("mosek") results = opt.solve(model, load_solutions=False) self.assertEqual(results.solution.status, diff --git a/pyomo/solvers/tests/checks/test_no_solution_behavior.py b/pyomo/solvers/tests/checks/test_no_solution_behavior.py index 9a84696a06f..8e3c8bdda9f 100644 --- a/pyomo/solvers/tests/checks/test_no_solution_behavior.py +++ b/pyomo/solvers/tests/checks/test_no_solution_behavior.py @@ -22,7 +22,6 @@ from pyomo.solvers.tests.testcases import test_scenarios from pyomo.common.log import LoggingIntercept -import six from six import StringIO # The test directory diff --git a/pyomo/solvers/tests/checks/test_pickle.py b/pyomo/solvers/tests/checks/test_pickle.py index c9ec50867bb..5e903365d32 100644 --- a/pyomo/solvers/tests/checks/test_pickle.py +++ b/pyomo/solvers/tests/checks/test_pickle.py @@ -9,9 +9,6 @@ # ___________________________________________________________________________ import pickle -import os -from os.path import join, dirname, abspath -import warnings import types try: import new diff --git a/pyomo/solvers/tests/core/diet1.py b/pyomo/solvers/tests/core/diet1.py index 36e5dbd6ed1..b5560bcf526 100644 --- a/pyomo/solvers/tests/core/diet1.py +++ b/pyomo/solvers/tests/core/diet1.py @@ -1,10 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # # Imports # -import sys -import os -from pyomo.core import * +from pyomo.core import AbstractModel, Set, Param, Var, PositiveReals, NonNegativeReals, NonNegativeIntegers, Constraint, Objective, minimize infinity = float('inf') diff --git a/pyomo/solvers/tests/core/plugins/__init__.py b/pyomo/solvers/tests/core/plugins/__init__.py index 5961a8d1d96..8b137891791 100644 --- a/pyomo/solvers/tests/core/plugins/__init__.py +++ b/pyomo/solvers/tests/core/plugins/__init__.py @@ -1,2 +1 @@ -#from pyomo.data.core.plugins.lp import * -#from pyomo.data.core.plugins.mip import * + diff --git a/pyomo/solvers/tests/core/plugins/lp.py b/pyomo/solvers/tests/core/plugins/lp.py index 21fef0c4b64..220ee516f96 100644 --- a/pyomo/solvers/tests/core/plugins/lp.py +++ b/pyomo/solvers/tests/core/plugins/lp.py @@ -1,7 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ -import sys -import pyutilib.math -import pyomo.opt +from pyutilib.math import approx_equal +from pyomo.opt import undefined, SolutionStatus, SolverStatus, TerminationCondition def lp_bounds(results, data, options): @@ -13,14 +21,14 @@ def lp_bounds(results, data, options): # # value = results.problem[0].lower_bound - data['lower bound'] = pyutilib.math.approx_equal(value, options._baseline.problem[0].lower_bound, options.abstol, options.reltol), "'{0}' lower bound for {2} LP (baseline={1})".format(value, options._baseline.problem[0].lower_bound, name) + data['lower bound'] = approx_equal(value, options._baseline.problem[0].lower_bound, options.abstol, options.reltol), "'{0}' lower bound for {2} LP (baseline={1})".format(value, options._baseline.problem[0].lower_bound, name) # value = results.problem[0].upper_bound - data['upper bound'] = pyutilib.math.approx_equal(value, options._baseline.problem[0].upper_bound, options.abstol, options.reltol), "'{0}' upper bound for {2} LP (baseline={1})".format(value, options._baseline.problem[0].upper_bound, name) + data['upper bound'] = approx_equal(value, options._baseline.problem[0].upper_bound, options.abstol, options.reltol), "'{0}' upper bound for {2} LP (baseline={1})".format(value, options._baseline.problem[0].upper_bound, name) # value = results.solution[0].gap - if value != pyomo.opt.undefined: - data['solution gap'] = pyutilib.math.approx_equal(value, 0.0, options.abstol, options.reltol), "'{0}' solution gap for {1} LP solution".format(value, name) + if value != undefined: + data['solution gap'] = approx_equal(value, 0.0, options.abstol, options.reltol), "'{0}' solution gap for {1} LP solution".format(value, name) # # @@ -44,10 +52,10 @@ def lp_feasible_solution(results, data, options, name): # # value = results.solver.status - data['solver status'] = value==pyomo.opt.SolverStatus.ok, "'{0}' solver status for {1} LP".format(value, name) + data['solver status'] = value==SolverStatus.ok, "'{0}' solver status for {1} LP".format(value, name) # value = results.solver.termination_condition - data['termination condition'] = value==pyomo.opt.TerminationCondition.optimal, "'{0}' termination condition for {1} LP".format(value, name) + data['termination condition'] = value==TerminationCondition.optimal, "'{0}' termination condition for {1} LP".format(value, name) # value = results.solver.get('error_rc',None) data['error rc'] = value==0 or value==None, "'{0}' error rc for {1} LP".format(value, name) @@ -57,7 +65,7 @@ def lp_feasible_solution(results, data, options, name): data['single solution'] = value==1, '{0} solutions found for {1} LP'.format(value, name) # value = results.solution[0].status - data['solution status'] = value==pyomo.opt.SolutionStatus.optimal or value==pyomo.opt.SolutionStatus.feasible, "'{0}' solution status for {1} LP solution".format(value, name) + data['solution status'] = value==SolutionStatus.optimal or value==SolutionStatus.feasible, "'{0}' solution status for {1} LP solution".format(value, name) # if len(results.solution[0].objective) > 0: objname = results.solution[0].objective.keys()[0] @@ -65,7 +73,7 @@ def lp_feasible_solution(results, data, options, name): data['objective name'] = objname == _objname, "'{0}' objective name for {2} LP solution (baseline={1})".format(objname, _objname, name) # value = results.solution[0].objective[objname].value - data['objective value'] = pyutilib.math.approx_equal(value, options._baseline.solution[0].objective[_objname].value, options.abstol, options.reltol), "'{0}' objective value for {2} LP solution (baseline={1})".format(value, options._baseline.solution[0].objective[_objname].value, name) + data['objective value'] = approx_equal(value, options._baseline.solution[0].objective[_objname].value, options.abstol, options.reltol), "'{0}' objective value for {2} LP solution (baseline={1})".format(value, options._baseline.solution[0].objective[_objname].value, name) def lp_feasible(results, data, options): @@ -88,10 +96,10 @@ def lp_unbounded(results, data, options): data['no solution'] = value==0, '{0} solutions found for unbounded LP'.format(value) # value = results.solver.status - data['solver status'] = value==pyomo.opt.SolverStatus.ok, "'{0}' solver status for unbounded LP".format(value) + data['solver status'] = value==SolverStatus.ok, "'{0}' solver status for unbounded LP".format(value) # value = results.solver.termination_condition - data['termination condition'] = value==pyomo.opt.TerminationCondition.unbounded, "'{0}' termination condition for unbounded LP".format(value) + data['termination condition'] = value==TerminationCondition.unbounded, "'{0}' termination condition for unbounded LP".format(value) def lp_infeasible(results, data, options): @@ -104,10 +112,10 @@ def lp_infeasible(results, data, options): data['single objective'] = value==1, '{0} objectives found for infeasible LP'.format(value) # value = results.solver.status - data['solver status'] = value==pyomo.opt.SolverStatus.ok, "'{0}' solver status for infeasible LP".format(value) + data['solver status'] = value==SolverStatus.ok, "'{0}' solver status for infeasible LP".format(value) # value = results.solver.termination_condition - data['termination condition'] = value==pyomo.opt.TerminationCondition.infeasible, "'{0}' termination condition for infeasible LP".format(value) + data['termination condition'] = value==TerminationCondition.infeasible, "'{0}' termination condition for infeasible LP".format(value) def lp_unique_dual(results, data, options): diff --git a/pyomo/solvers/tests/core/plugins/mip.py b/pyomo/solvers/tests/core/plugins/mip.py index cb8fe1e1a4c..5c3b40353bb 100644 --- a/pyomo/solvers/tests/core/plugins/mip.py +++ b/pyomo/solvers/tests/core/plugins/mip.py @@ -1,7 +1,15 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ -import sys -import pyutilib.math -import pyomo.opt +from pyutilib.math import approx_equal +from pyomo.opt import undefined, SolutionStatus, SolverStatus, TerminationCondition def mip_unique(results, data, options, name='unique-feasible'): @@ -15,11 +23,11 @@ def mip_unique(results, data, options, name='unique-feasible'): data['single solution'] = value==1, '{0} solutions found for {1} MIP'.format(value, name) # value = results.solution[0].gap - if value != pyomo.opt.undefined: - data['solution gap'] = pyutilib.math.approx_equal(value, 0.0, options.abstol, options.reltol), "'{0}' solution gap for {1} MIP solution".format(value, name) + if value != undefined: + data['solution gap'] = approx_equal(value, 0.0, options.abstol, options.reltol), "'{0}' solution gap for {1} MIP solution".format(value, name) # value = results.solution[0].status - data['solution status'] = value==pyomo.opt.SolutionStatus.optimal, "'{0}' solution status for {1} MIP solution".format(value, name) + data['solution status'] = value==SolutionStatus.optimal, "'{0}' solution status for {1} MIP solution".format(value, name) # if len(results.solution[0].objective) > 0: objname = results.solution[0].objective.keys()[0] @@ -27,7 +35,7 @@ def mip_unique(results, data, options, name='unique-feasible'): data['objective name'] = objname == _objname, "'{0}' objective name for {2} MIP solution (baseline={1})".format(objname, _objname, name) # value = results.solution[0].objective[objname].value - data['objective value'] = pyutilib.math.approx_equal(value, options._baseline.solution[0].objective[_objname].value, options.abstol, options.reltol), "'{0}' objective value for {2} MIP solution (baseline={1})".format(value, options._baseline.solution[0].objective[_objname].value, name) + data['objective value'] = approx_equal(value, options._baseline.solution[0].objective[_objname].value, options.abstol, options.reltol), "'{0}' objective value for {2} MIP solution (baseline={1})".format(value, options._baseline.solution[0].objective[_objname].value, name) # @@ -46,20 +54,20 @@ def mip_feasible_solution(results, data, options, name): data['single objective'] = value==1, '{0} objectives found for {1} MIP'.format(value, name) # value = results.problem[0].lower_bound - data['lower bound'] = pyutilib.math.approx_equal(value, options._baseline.problem[0].lower_bound, options.abstol, options.reltol), "'{0}' lower bound for {2} MIP (baseline={1})".format(value, options._baseline.problem[0].lower_bound, name) + data['lower bound'] = approx_equal(value, options._baseline.problem[0].lower_bound, options.abstol, options.reltol), "'{0}' lower bound for {2} MIP (baseline={1})".format(value, options._baseline.problem[0].lower_bound, name) # value = results.problem[0].upper_bound - data['upper bound'] = pyutilib.math.approx_equal(value, options._baseline.problem[0].upper_bound, options.abstol, options.reltol), "'{0}' upper bound for {2} MIP (baseline={1})".format(value, options._baseline.problem[0].upper_bound, name) + data['upper bound'] = approx_equal(value, options._baseline.problem[0].upper_bound, options.abstol, options.reltol), "'{0}' upper bound for {2} MIP (baseline={1})".format(value, options._baseline.problem[0].upper_bound, name) # value = str(results.problem[0].sense) data['sense'] = value==options._baseline.problem[0].sense, "'{0}' sense for {2} MIP (baseline={1})".format(value, options._baseline.problem[0].sense, name) # # value = results.solver.status - data['solver status'] = value==pyomo.opt.SolverStatus.ok, "'{0}' solver status for {1} MIP".format(value, name) + data['solver status'] = value==SolverStatus.ok, "'{0}' solver status for {1} MIP".format(value, name) # value = results.solver.termination_condition - data['termination condition'] = value==pyomo.opt.TerminationCondition.optimal, "'{0}' termination condition for {1} MIP".format(value, name) + data['termination condition'] = value==TerminationCondition.optimal, "'{0}' termination condition for {1} MIP".format(value, name) # value = results.solver.error_rc data['error rc'] = value==0, "'{0}' error rc for {1} MIP".format(value, name) @@ -89,10 +97,10 @@ def mip_unbounded(results, data, options): data['no solution'] = value==0, '{0} solutions found for unbounded MIP'.format(value) # value = results.solver.status - data['solver status'] = value==pyomo.opt.SolverStatus.ok, "'{0}' solver status for unbounded MIP".format(value) + data['solver status'] = value==SolverStatus.ok, "'{0}' solver status for unbounded MIP".format(value) # value = results.solver.termination_condition - data['termination condition'] = value==pyomo.opt.TerminationCondition.unbounded, "'{0}' termination condition for unbounded MIP".format(value) + data['termination condition'] = value==TerminationCondition.unbounded, "'{0}' termination condition for unbounded MIP".format(value) def mip_infeasible(results, data, options): @@ -105,8 +113,8 @@ def mip_infeasible(results, data, options): data['single objective'] = value==1, '{0} objectives found for infeasible MIP'.format(value) # value = results.solver.status - data['solver status'] = value==pyomo.opt.SolverStatus.ok, "'{0}' solver status for infeasible MIP".format(value) + data['solver status'] = value==SolverStatus.ok, "'{0}' solver status for infeasible MIP".format(value) # value = results.solver.termination_condition - data['termination condition'] = value==pyomo.opt.TerminationCondition.infeasible, "'{0}' termination condition for infeasible MIP".format(value) + data['termination condition'] = value==TerminationCondition.infeasible, "'{0}' termination condition for infeasible MIP".format(value) diff --git a/pyomo/solvers/tests/core/problems/constant1a.py b/pyomo/solvers/tests/core/problems/constant1a.py index 86e0d9ea595..e3bf181ae21 100644 --- a/pyomo/solvers/tests/core/problems/constant1a.py +++ b/pyomo/solvers/tests/core/problems/constant1a.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Constraint model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/constant1b.py b/pyomo/solvers/tests/core/problems/constant1b.py index b3bf3ac374d..77821fdb304 100644 --- a/pyomo/solvers/tests/core/problems/constant1b.py +++ b/pyomo/solvers/tests/core/problems/constant1b.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Constraint, maximize model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/diet1.py b/pyomo/solvers/tests/core/problems/diet1.py index 36e5dbd6ed1..cbee2b8241c 100644 --- a/pyomo/solvers/tests/core/problems/diet1.py +++ b/pyomo/solvers/tests/core/problems/diet1.py @@ -1,10 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # # Imports # -import sys -import os -from pyomo.core import * +from pyomo.core import AbstractModel, Set, Param, Var, PositiveReals, NonNegativeIntegers, NonNegativeReals, Constraint, Objective, minimize infinity = float('inf') diff --git a/pyomo/solvers/tests/core/problems/infeasible1a.py b/pyomo/solvers/tests/core/problems/infeasible1a.py index 29638c30e45..52c83b35d89 100644 --- a/pyomo/solvers/tests/core/problems/infeasible1a.py +++ b/pyomo/solvers/tests/core/problems/infeasible1a.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Constraint model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/infeasible1b.py b/pyomo/solvers/tests/core/problems/infeasible1b.py index beab9eba23c..d2b7626e995 100644 --- a/pyomo/solvers/tests/core/problems/infeasible1b.py +++ b/pyomo/solvers/tests/core/problems/infeasible1b.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Constraint, maximize model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/lp_basic.py b/pyomo/solvers/tests/core/problems/lp_basic.py index 2203457beda..63ca386dc3c 100644 --- a/pyomo/solvers/tests/core/problems/lp_basic.py +++ b/pyomo/solvers/tests/core/problems/lp_basic.py @@ -1,8 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ # # A LP model adapted from the Wikipedia description of the simplex algorithm: # http://en.wikipedia.org/wiki/Simplex_algorithm # -from pyomo.core import * + +from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/lp_unique1.py b/pyomo/solvers/tests/core/problems/lp_unique1.py index 46f3c7ea9ff..b3a7ab91030 100644 --- a/pyomo/solvers/tests/core/problems/lp_unique1.py +++ b/pyomo/solvers/tests/core/problems/lp_unique1.py @@ -1,6 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # This problem has a unique primal and dual solution. -from pyomo.core import * +from pyomo.core import ConcreteModel, Param, RangeSet, Var, NonNegativeReals, Objective, Constraint, Suffix, sum_product model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/lp_unique1_proof.py b/pyomo/solvers/tests/core/problems/lp_unique1_proof.py index fca1e306cde..69ce9bf3b7a 100644 --- a/pyomo/solvers/tests/core/problems/lp_unique1_proof.py +++ b/pyomo/solvers/tests/core/problems/lp_unique1_proof.py @@ -1,6 +1,16 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # This is a computational proof that lp_unique1.py has a unique primal and dual solution. -from pyomo.core import * +from pyomo.core import ConcreteModel, Param, RangeSet, Var, Constraint, Objective, NonNegativeReals from pyomo.opt import SolverFactory def get_model(flag, fixprim, fixdual): @@ -75,12 +85,12 @@ def primcons_rule(model, k, i): model = get_model(True, i, 1) results = opt.solve(model) if results.solution[0].objective['ydiff'].value != 0: - print "ERROR: nonzero difference i=%d ydiff=%f" % (i, results.solution[0].objective['ydiff'].value) + print("ERROR: nonzero difference i=%d ydiff=%f" % (i, results.solution[0].objective['ydiff'].value)) for i in range(1,8): model = get_model(False, 1, i) results = opt.solve(model) if results.solution[0].objective['xdiff'].value != 0: - print "ERROR: nonzero difference i=%d xdiff=%f" % (i, results.solution[0].objective['xdiff'].value) + print("ERROR: nonzero difference i=%d xdiff=%f" % (i, results.solution[0].objective['xdiff'].value)) -print "SUCCESS!" +print("SUCCESS!") diff --git a/pyomo/solvers/tests/core/problems/mip_constant1a.py b/pyomo/solvers/tests/core/problems/mip_constant1a.py index 04cdb7263f8..0aeea0a523f 100644 --- a/pyomo/solvers/tests/core/problems/mip_constant1a.py +++ b/pyomo/solvers/tests/core/problems/mip_constant1a.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Constraint, Integers model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/mip_infeasible1a.py b/pyomo/solvers/tests/core/problems/mip_infeasible1a.py index 6752c015c2d..9c60342f4e5 100644 --- a/pyomo/solvers/tests/core/problems/mip_infeasible1a.py +++ b/pyomo/solvers/tests/core/problems/mip_infeasible1a.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Constraint, Binary model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/mip_unbounded1a.py b/pyomo/solvers/tests/core/problems/mip_unbounded1a.py index 153bdf2dfec..aa65356917d 100644 --- a/pyomo/solvers/tests/core/problems/mip_unbounded1a.py +++ b/pyomo/solvers/tests/core/problems/mip_unbounded1a.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Integers model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/mip_unique1.py b/pyomo/solvers/tests/core/problems/mip_unique1.py index 5391770e190..37bd0a1a922 100644 --- a/pyomo/solvers/tests/core/problems/mip_unique1.py +++ b/pyomo/solvers/tests/core/problems/mip_unique1.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, Constraint, Binary model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/multidimen_sos1.py b/pyomo/solvers/tests/core/problems/multidimen_sos1.py index 95f6c8cf6a9..0798e13314f 100644 --- a/pyomo/solvers/tests/core/problems/multidimen_sos1.py +++ b/pyomo/solvers/tests/core/problems/multidimen_sos1.py @@ -1,4 +1,16 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Set, Var, Objective, Constraint, NonNegativeReals, SOSConstraint, maximize, sum_product + +from six.moves import xrange INDEX_SET1 = [1,2] INDEX_SET2 = [1,2,3] diff --git a/pyomo/solvers/tests/core/problems/multidimen_sos2.py b/pyomo/solvers/tests/core/problems/multidimen_sos2.py index 3014b00e56c..cbe2149baac 100644 --- a/pyomo/solvers/tests/core/problems/multidimen_sos2.py +++ b/pyomo/solvers/tests/core/problems/multidimen_sos2.py @@ -1,4 +1,16 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Set, Var, Objective, Constraint, NonNegativeReals, SOSConstraint, maximize, sum_product + +from six.moves import xrange INDEX_SET1 = [1,2] diff --git a/pyomo/solvers/tests/core/problems/simple1a.py b/pyomo/solvers/tests/core/problems/simple1a.py index 61c4d0237be..2b16de975fa 100644 --- a/pyomo/solvers/tests/core/problems/simple1a.py +++ b/pyomo/solvers/tests/core/problems/simple1a.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/simple1b.py b/pyomo/solvers/tests/core/problems/simple1b.py index 51a36bd7ef1..1fa11526e1d 100644 --- a/pyomo/solvers/tests/core/problems/simple1b.py +++ b/pyomo/solvers/tests/core/problems/simple1b.py @@ -1,4 +1,15 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective, maximize + model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/sos1.py b/pyomo/solvers/tests/core/problems/sos1.py index fcd2e941756..88234d71f33 100644 --- a/pyomo/solvers/tests/core/problems/sos1.py +++ b/pyomo/solvers/tests/core/problems/sos1.py @@ -1,4 +1,16 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Set, Var, Objective, Constraint, NonNegativeReals, SOSConstraint, maximize, sum_product + +from six.moves import xrange INDEX_SET = [1,2] PIECEWISE_PTS = {1:[1,2,3], 2:[1,2,3]} diff --git a/pyomo/solvers/tests/core/problems/sos2.py b/pyomo/solvers/tests/core/problems/sos2.py index df87b5464d8..46efb837eca 100644 --- a/pyomo/solvers/tests/core/problems/sos2.py +++ b/pyomo/solvers/tests/core/problems/sos2.py @@ -1,4 +1,16 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Set, Var, Objective, Constraint, NonNegativeReals, SOSConstraint, maximize, sum_product + +from six.moves import xrange INDEX_SET = [1,2] PIECEWISE_PTS = {1:[1,2,3], 2:[1,2,3]} diff --git a/pyomo/solvers/tests/core/problems/unbounded1a.py b/pyomo/solvers/tests/core/problems/unbounded1a.py index 1074e1a1d1f..e94543de633 100644 --- a/pyomo/solvers/tests/core/problems/unbounded1a.py +++ b/pyomo/solvers/tests/core/problems/unbounded1a.py @@ -1,4 +1,14 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + +from pyomo.core import ConcreteModel, Var, Objective model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/problems/unbounded1b.py b/pyomo/solvers/tests/core/problems/unbounded1b.py index 15f3a98a899..b01db8d60b2 100644 --- a/pyomo/solvers/tests/core/problems/unbounded1b.py +++ b/pyomo/solvers/tests/core/problems/unbounded1b.py @@ -1,4 +1,15 @@ -from pyomo.core import * +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + + +from pyomo.core import ConcreteModel, Var, Objective, maximize model = ConcreteModel() diff --git a/pyomo/solvers/tests/core/solvers.py b/pyomo/solvers/tests/core/solvers.py index 39fa5e792a9..0c3a1a7f8af 100644 --- a/pyomo/solvers/tests/core/solvers.py +++ b/pyomo/solvers/tests/core/solvers.py @@ -1,14 +1,22 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ __all__ = ['test_solvers'] import re import os import sys -import time import csv from os.path import abspath, dirname import logging -import pprint +from functools import reduce from pyutilib.misc import Bunch, Options import pyutilib.th as unittest diff --git a/pyomo/solvers/tests/core/test_baselines.py b/pyomo/solvers/tests/core/test_baselines.py index 662c6f6032c..bec832044dd 100644 --- a/pyomo/solvers/tests/core/test_baselines.py +++ b/pyomo/solvers/tests/core/test_baselines.py @@ -1,3 +1,12 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ # # Tests driven by test_baselines.yml # @@ -11,16 +20,13 @@ if __name__ == "__main__": import os - import sys from os.path import abspath, dirname currdir = dirname(abspath(__file__))+os.sep - import pyutilib.th as unittest - import pyutilib.services - import pyomo.environ + from pyutilib.services import TempfileManager - import pyutilib.autotest - pyutilib.autotest.create_test_suites(filename=currdir+'test_baselines.yml', _globals=globals()) + from pyutilib.autotest import create_test_suites + create_test_suites(filename=currdir+'test_baselines.yml', _globals=globals()) # GAH: The pyutilib.autotest.create_test_suites function # does some strange things to the TempfileManager state @@ -28,7 +34,7 @@ # updated (I grep'ed for TempfileManager inside pyomo.data # and pyutilib.autotest - nothing). Is this a bug? def tearDownModule(): - pyutilib.services.TempfileManager.clear_tempfiles() - pyutilib.services.TempfileManager.tempdir = None - pyutilib.services.TempfileManager.unique_files() + TempfileManager.clear_tempfiles() + TempfileManager.tempdir = None + TempfileManager.unique_files() diff --git a/pyomo/solvers/tests/core/test_component_perf.py b/pyomo/solvers/tests/core/test_component_perf.py index 70220aa65b9..8a4e0af86aa 100644 --- a/pyomo/solvers/tests/core/test_component_perf.py +++ b/pyomo/solvers/tests/core/test_component_perf.py @@ -1,4 +1,13 @@ -import gc +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import os thisdir = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyomo/solvers/tests/core/test_param_perf.py b/pyomo/solvers/tests/core/test_param_perf.py index 3385f841ad5..3273c365e02 100644 --- a/pyomo/solvers/tests/core/test_param_perf.py +++ b/pyomo/solvers/tests/core/test_param_perf.py @@ -1,8 +1,18 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + import os thisdir = os.path.dirname(os.path.abspath(__file__)) import time -from pyomo.core import * +from pyomo.core import ConcreteModel, RangeSet, Set, Param import pyutilib.th as unittest _plot_filename = os.path.join(thisdir, "param_performance.pdf") diff --git a/pyomo/solvers/tests/core/test_perf.py b/pyomo/solvers/tests/core/test_perf.py index 6dc207b9ac7..5edb6329651 100644 --- a/pyomo/solvers/tests/core/test_perf.py +++ b/pyomo/solvers/tests/core/test_perf.py @@ -1,14 +1,23 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # # Test the Pyomo command-line interface # import os -import gc # __file__ fails if script is called in different ways on Windows # __file__ fails if someone does os.chdir() before # sys.argv[0] also fails because it doesn't not always contains the path -from os.path import abspath, dirname, exists, join +from os.path import abspath, dirname, join from inspect import getfile, currentframe currdir = dirname(abspath(getfile(currentframe()))) datadir = os.path.normpath(join( diff --git a/pyomo/solvers/tests/core/test_solvers.py b/pyomo/solvers/tests/core/test_solvers.py index e333ea5ae76..100810ac560 100644 --- a/pyomo/solvers/tests/core/test_solvers.py +++ b/pyomo/solvers/tests/core/test_solvers.py @@ -1,3 +1,13 @@ +# ___________________________________________________________________________ +# +# Pyomo: Python Optimization Modeling Objects +# Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC +# Under the terms of Contract DE-NA0003525 with National Technology and +# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain +# rights in this software. +# This software is distributed under the 3-clause BSD License. +# ___________________________________________________________________________ + # # Tests driven by test_solvers.yml # @@ -7,14 +17,12 @@ if __name__ == "__main__": import os - import sys from os.path import abspath, dirname currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest - import pyutilib.autotest - #import pyomo.data.core + from pyutilib.autotest import create_test_suites - pyutilib.autotest.create_test_suites(filename=currdir+'test_solvers.yml', _globals=globals()) + create_test_suites(filename=currdir+'test_solvers.yml', _globals=globals()) unittest.main() diff --git a/pyomo/solvers/tests/mip/model.py b/pyomo/solvers/tests/mip/model.py index 7729c312cc8..efd22389ab0 100644 --- a/pyomo/solvers/tests/mip/model.py +++ b/pyomo/solvers/tests/mip/model.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import AbstractModel, RangeSet, Var, Objective, sum_product model = AbstractModel() diff --git a/pyomo/solvers/tests/mip/test_asl.py b/pyomo/solvers/tests/mip/test_asl.py index 7db7367d2d3..8c4388af86a 100644 --- a/pyomo/solvers/tests/mip/test_asl.py +++ b/pyomo/solvers/tests/mip/test_asl.py @@ -14,26 +14,24 @@ currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest -import pyutilib.services -import pyutilib.common +from pyutilib.services import TempfileManager from pyomo.core import ConcreteModel -import pyomo.opt -from pyomo.opt import ResultsFormat, ProblemFormat +from pyomo.opt import ResultsFormat, SolverResults, SolverFactory old_ignore_time = None old_tempdir = None def setUpModule(): global old_tempdir global old_ignore_time - old_tempdir = pyutilib.services.TempfileManager.tempdir - old_ignore_time = pyomo.opt.SolverResults.default_print_options.ignore_time - pyomo.opt.SolverResults.default_print_options.ignore_time = True - pyutilib.services.TempfileManager.tempdir = currdir + old_tempdir = TempfileManager.tempdir + old_ignore_time = SolverResults.default_print_options.ignore_time + SolverResults.default_print_options.ignore_time = True + TempfileManager.tempdir = currdir def tearDownModule(): - pyutilib.services.TempfileManager.tempdir = old_tempdir - pyomo.opt.SolverResults.default_print_options.ignore_time = old_ignore_time + TempfileManager.tempdir = old_tempdir + SolverResults.default_print_options.ignore_time = old_ignore_time cplexamp_available = False class mock_all(unittest.TestCase): @@ -52,18 +50,18 @@ def do_setup(self,flag): global tmpdir tmpdir = os.getcwd() os.chdir(currdir) - pyutilib.services.TempfileManager.sequential_files(0) + TempfileManager.sequential_files(0) if flag: if not cplexamp_available: self.skipTest("The 'cplexamp' command is not available") - self.asl = pyomo.opt.SolverFactory('asl:cplexamp') + self.asl = SolverFactory('asl:cplexamp') else: - self.asl = pyomo.opt.SolverFactory('_mock_asl:cplexamp') + self.asl = SolverFactory('_mock_asl:cplexamp') def tearDown(self): global tmpdir - pyutilib.services.TempfileManager.clear_tempfiles() - pyutilib.services.TempfileManager.unique_files() + TempfileManager.clear_tempfiles() + TempfileManager.unique_files() os.chdir(tmpdir) self.asl = None diff --git a/pyomo/solvers/tests/mip/test_convert.py b/pyomo/solvers/tests/mip/test_convert.py index ae7547a2c9b..8999a5b7bf9 100644 --- a/pyomo/solvers/tests/mip/test_convert.py +++ b/pyomo/solvers/tests/mip/test_convert.py @@ -19,10 +19,11 @@ currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest -import pyutilib.services -import pyutilib.common +from pyutilib.services import TempfileManager +from pyutilib.common import ApplicationError -import pyomo.opt +from pyomo.opt import ProblemFormat, ConverterError, convert_problem +from pyomo.common import Executable def filter(line): return 'Problem' in line or line.startswith('NAME') @@ -30,11 +31,11 @@ def filter(line): old_tempdir = None def setUpModule(): global old_tempdir - old_tempdir = pyutilib.services.TempfileManager.tempdir - pyutilib.services.TempfileManager.tempdir = currdir + old_tempdir = TempfileManager.tempdir + TempfileManager.tempdir = currdir def tearDownModule(): - pyutilib.services.TempfileManager.tempdir = old_tempdir + TempfileManager.tempdir = old_tempdir class MockArg(object): @@ -42,7 +43,7 @@ def __init__(self): pass def valid_problem_types(self): - return [pyomo.opt.ProblemFormat.pyomo] + return [ProblemFormat.pyomo] def write(self,filename="", format=None, solver_capability=None, io_options={}): return (filename,None) @@ -50,7 +51,7 @@ def write(self,filename="", format=None, solver_capability=None, io_options={}): class MockArg2(MockArg): def valid_problem_types(self): - return [pyomo.opt.ProblemFormat.nl] + return [ProblemFormat.nl] def write(self,filename="", format=None, solver_capability=None, io_options={}): OUTPUT=open(filename,"w") @@ -64,7 +65,7 @@ def write(self,filename="", format=None, solver_capability=None, io_options={}): class MockArg3(MockArg): def valid_problem_types(self): - return [pyomo.opt.ProblemFormat.mod] + return [ProblemFormat.mod] def write(self,filename="", format=None, solver_capability=None, io_options={}): return (filename,None) @@ -88,31 +89,31 @@ def setUpClass(cls): import pyomo.environ def tearDown(self): - pyutilib.services.TempfileManager.clear_tempfiles() + TempfileManager.clear_tempfiles() def test_nl_nl1(self): #""" Convert from NL to NL """ - ans = pyomo.opt.convert_problem( ("test4.nl",), None, [pyomo.opt.ProblemFormat.nl]) + ans = convert_problem( ("test4.nl",), None, [ProblemFormat.nl]) self.assertEqual(ans[0],("test4.nl",)) def test_nl_nl2(self): #""" Convert from NL to NL """ - ans = pyomo.opt.convert_problem( ("test4.nl","tmp.nl"), None, [pyomo.opt.ProblemFormat.nl]) + ans = convert_problem( ("test4.nl","tmp.nl"), None, [ProblemFormat.nl]) self.assertEqual(ans[0],("test4.nl","tmp.nl")) def test_nl_lp1(self): #""" Convert from NL to LP """ try: - ans = pyomo.opt.convert_problem( (currdir+"test4.nl",), None, [pyomo.opt.ProblemFormat.cpxlp]) - except pyutilib.common.ApplicationError: + ans = convert_problem( (currdir+"test4.nl",), None, [ProblemFormat.cpxlp]) + except ApplicationError: err = sys.exc_info()[1] - if pyomo.common.Executable("pico_convert").available(): + if Executable("pico_convert").available(): self.fail("Unexpected ApplicationError - pico_convert is " "enabled but not available: '%s'" % str(err)) return - except pyomo.opt.ConverterError: + except ConverterError: err = sys.exc_info()[1] - if pyomo.common.Executable("pico_convert").available(): + if Executable("pico_convert").available(): self.fail("Unexpected ConverterError - pico_convert is " "enabled but not available: '%s'" % str(err)) return @@ -122,16 +123,16 @@ def test_nl_lp1(self): def test_mod_lp1(self): #""" Convert from MOD to LP """ try: - ans = pyomo.opt.convert_problem( (currdir+"test3.mod",), None, [pyomo.opt.ProblemFormat.cpxlp]) - except pyutilib.common.ApplicationError: + ans = convert_problem( (currdir+"test3.mod",), None, [ProblemFormat.cpxlp]) + except ApplicationError: err = sys.exc_info()[1] - if pyomo.common.Executable("glpsol").available(): + if Executable("glpsol").available(): self.fail("Unexpected ApplicationError - glpsol is " "enabled but not available: '%s'" % str(err)) return - except pyomo.opt.ConverterError: + except ConverterError: err = sys.exc_info()[1] - if pyomo.common.Executable("glpsol").available(): + if Executable("glpsol").available(): self.fail("Unexpected ConverterError - glpsol is " "enabled but not available: '%s'" % str(err)) return @@ -141,16 +142,16 @@ def test_mod_lp1(self): def test_mod_lp2(self): #""" Convert from MOD+DAT to LP """ try: - ans = pyomo.opt.convert_problem( (currdir+"test5.mod",currdir+"test5.dat"), None, [pyomo.opt.ProblemFormat.cpxlp]) - except pyutilib.common.ApplicationError: + ans = convert_problem( (currdir+"test5.mod",currdir+"test5.dat"), None, [ProblemFormat.cpxlp]) + except ApplicationError: err = sys.exc_info()[1] - if pyomo.common.Executable("glpsol").available(): + if Executable("glpsol").available(): self.fail("Unexpected ApplicationError - glpsol is " "enabled but not available: '%s'" % str(err)) return - except pyomo.opt.ConverterError: + except ConverterError: err = sys.exc_info()[1] - if pyomo.common.Executable("glpsol").available(): + if Executable("glpsol").available(): self.fail("Unexpected ConverterError - glpsol is " "enabled but not available: '%s'" % str(err)) return @@ -160,16 +161,16 @@ def test_mod_lp2(self): def test_mod_nl1(self): #""" Convert from MOD to NL """ try: - ans = pyomo.opt.convert_problem( (currdir+"test3.mod",), None, [pyomo.opt.ProblemFormat.nl]) - except pyutilib.common.ApplicationError: + ans = convert_problem( (currdir+"test3.mod",), None, [ProblemFormat.nl]) + except ApplicationError: err = sys.exc_info()[1] - if pyomo.common.Executable("ampl").available(): + if Executable("ampl").available(): self.fail("Unexpected ApplicationError - ampl is " "enabled but not available: '%s'" % str(err)) return - except pyomo.opt.ConverterError: + except ConverterError: err = sys.exc_info()[1] - if pyomo.common.Executable("ampl").available(): + if Executable("ampl").available(): self.fail("Unexpected ConverterError - ampl is " "enabled but not available: '%s'" % str(err)) return @@ -179,16 +180,16 @@ def test_mod_nl1(self): def test_mod_nl2(self): #""" Convert from MOD+DAT to NL """ try: - ans = pyomo.opt.convert_problem( (currdir+"test5.mod",currdir+"test5.dat"), None, [pyomo.opt.ProblemFormat.nl]) - except pyutilib.common.ApplicationError: + ans = convert_problem( (currdir+"test5.mod",currdir+"test5.dat"), None, [ProblemFormat.nl]) + except ApplicationError: err = sys.exc_info()[1] - if pyomo.common.Executable("ampl").available(): + if Executable("ampl").available(): self.fail("Unexpected ApplicationError - ampl is " "enabled but not available: '%s'" % str(err)) return - except pyomo.opt.ConverterError: + except ConverterError: err = sys.exc_info()[1] - if pyomo.common.Executable("ampl").available(): + if Executable("ampl").available(): self.fail("Unexpected ConverterError - ampl is " "enabled but not available: '%s'" % str(err)) return @@ -198,22 +199,22 @@ def test_mod_nl2(self): def test_mock_lp1(self): #""" Convert from Pyomo to LP """ arg=MockArg() - ans = pyomo.opt.convert_problem( (arg,pyomo.opt.ProblemFormat.cpxlp,arg), None, [pyomo.opt.ProblemFormat.cpxlp]) + ans = convert_problem( (arg, ProblemFormat.cpxlp,arg), None, [ProblemFormat.cpxlp]) self.assertNotEqual(re.match(".*tmp.*pyomo.lp$",ans[0][0]), None) def test_pyomo_lp1(self): #""" Convert from Pyomo to LP with file""" - ans = pyomo.opt.convert_problem( (currdir+'model.py',pyomo.opt.ProblemFormat.cpxlp,), None, [pyomo.opt.ProblemFormat.cpxlp]) + ans = convert_problem( (currdir+'model.py', ProblemFormat.cpxlp,), None, [ProblemFormat.cpxlp]) self.assertNotEqual(re.match(".*tmp.*pyomo.lp$",ans[0][0]), None) def test_mock_lp2(self): #""" Convert from NL to LP """ arg=MockArg2() try: - ans = pyomo.opt.convert_problem( (arg,), None, [pyomo.opt.ProblemFormat.cpxlp]) - except pyomo.opt.ConverterError: + ans = convert_problem( (arg,), None, [ProblemFormat.cpxlp]) + except ConverterError: err = sys.exc_info()[1] - if not pyomo.common.Executable("pico_convert"): + if not Executable("pico_convert"): return else: self.fail("Expected ApplicationError because pico_convert " @@ -227,10 +228,10 @@ def Xtest_mock_mps1(self): #""" Convert from Pyomo to MPS """ arg=MockArg4() try: - ans = pyomo.opt.convert_problem( (arg,pyomo.opt.ProblemFormat.mps,arg), None, [pyomo.opt.ProblemFormat.mps]) - except pyomo.opt.ConverterError: + ans = convert_problem((arg, ProblemFormat.mps,arg), None, [ProblemFormat.mps]) + except ConverterError: err = sys.exc_info()[1] - if not pyomo.common.Executable("pico_convert"): + if not Executable("pico_convert"): return else: self.fail("Expected ApplicationError because pico_convert " @@ -241,10 +242,10 @@ def Xtest_mock_mps1(self): def test_pyomo_mps1(self): #""" Convert from Pyomo to MPS with file""" try: - ans = pyomo.opt.convert_problem( (currdir+'model.py',pyomo.opt.ProblemFormat.mps,), None, [pyomo.opt.ProblemFormat.mps]) - except pyomo.opt.ConverterError: + ans = convert_problem( (currdir+'model.py', ProblemFormat.mps,), None, [ProblemFormat.mps]) + except ConverterError: err = sys.exc_info()[1] - if not pyomo.common.Executable("pico_convert"): + if not Executable("pico_convert"): return else: self.fail("Expected ApplicationError because pico_convert " @@ -254,111 +255,111 @@ def test_pyomo_mps1(self): def test_mock_nl1(self): #""" Convert from Pyomo to NL """ - arg=MockArg4() - ans = pyomo.opt.convert_problem( (arg,pyomo.opt.ProblemFormat.nl,arg), None, [pyomo.opt.ProblemFormat.nl]) + arg = MockArg4() + ans = convert_problem( (arg, ProblemFormat.nl,arg), None, [ProblemFormat.nl]) self.assertNotEqual(re.match(".*tmp.*pyomo.nl$",ans[0][0]), None) os.remove(ans[0][0]) def test_pyomo_nl1(self): #""" Convert from Pyomo to NL with file""" - ans = pyomo.opt.convert_problem( (currdir+'model.py',pyomo.opt.ProblemFormat.nl,), None, [pyomo.opt.ProblemFormat.nl]) + ans = convert_problem( (currdir+'model.py', ProblemFormat.nl,), None, [ProblemFormat.nl]) self.assertNotEqual(re.match(".*tmp.*pyomo.nl$",ans[0][0]), None) os.remove(ans[0][0]) def test_error1(self): #""" No valid problem types """ try: - pyomo.opt.convert_problem( ("test4.nl","tmp.nl"), pyomo.opt.ProblemFormat.nl, []) + convert_problem( ("test4.nl","tmp.nl"), ProblemFormat.nl, []) self.fail("Expected ConverterError exception") - except pyomo.opt.ConverterError: + except ConverterError: err = sys.exc_info()[1] pass def test_error2(self): #""" Target problem type is not valid """ try: - pyomo.opt.convert_problem( ("test4.nl","tmp.nl"), pyomo.opt.ProblemFormat.nl, [pyomo.opt.ProblemFormat.mps]) + convert_problem( ("test4.nl","tmp.nl"), ProblemFormat.nl, [ProblemFormat.mps]) self.fail("Expected ConverterError exception") - except pyomo.opt.ConverterError: + except ConverterError: pass def test_error3(self): #""" Empty argument list """ try: - pyomo.opt.convert_problem( (), None, [pyomo.opt.ProblemFormat.mps]) + convert_problem( (), None, [ProblemFormat.mps]) self.fail("Expected ConverterError exception") - except pyomo.opt.ConverterError: + except ConverterError: pass def test_error4(self): #""" Unknown source type """ try: - pyomo.opt.convert_problem( ("prob.foo",), None, [pyomo.opt.ProblemFormat.mps]) + convert_problem( ("prob.foo",), None, [ProblemFormat.mps]) self.fail("Expected ConverterError exception") - except pyomo.opt.ConverterError: + except ConverterError: pass def test_error5(self): #""" Unknown source type """ try: - pyomo.opt.convert_problem( ("prob.lp",), pyomo.opt.ProblemFormat.nl, [pyomo.opt.ProblemFormat.nl]) + convert_problem( ("prob.lp",), ProblemFormat.nl, [ProblemFormat.nl]) self.fail("Expected ConverterError exception") - except pyomo.opt.ConverterError: + except ConverterError: pass def test_error6(self): #""" Cannot use pico_convert with more than one file """ try: - ans = pyomo.opt.convert_problem( (currdir+"test4.nl","foo"), None, [pyomo.opt.ProblemFormat.cpxlp]) + ans = convert_problem( (currdir+"test4.nl","foo"), None, [ProblemFormat.cpxlp]) self.fail("Expected ConverterError exception") - except pyomo.opt.ConverterError: + except ConverterError: pass def test_error8(self): #""" Error when source file cannot be found """ try: - ans = pyomo.opt.convert_problem( (currdir+"unknown.nl",), None, [pyomo.opt.ProblemFormat.cpxlp]) + ans = convert_problem( (currdir+"unknown.nl",), None, [ProblemFormat.cpxlp]) self.fail("Expected ConverterError exception") - except pyutilib.common.ApplicationError: + except ApplicationError: err = sys.exc_info()[1] - if not pyomo.common.Executable("pico_convert"): + if not Executable("pico_convert"): self.fail("Expected ApplicationError because pico_convert " "is not available: '%s'" % str(err)) return - except pyomo.opt.ConverterError: + except ConverterError: pass def test_error9(self): #""" The Opt configuration has not been initialized """ - cmd = pyomo.common.Executable("pico_convert").disable() + cmd = Executable("pico_convert").disable() try: - ans = pyomo.opt.convert_problem( (currdir+"test4.nl",), None, [pyomo.opt.ProblemFormat.cpxlp]) + ans = convert_problem( (currdir+"test4.nl",), None, [ProblemFormat.cpxlp]) self.fail("This test didn't fail, but pico_convert should not be defined.") - except pyomo.opt.ConverterError: + except ConverterError: pass - cmd = pyomo.common.Executable("pico_convert").rehash() + cmd = Executable("pico_convert").rehash() def test_error10(self): #""" GLPSOL can only convert file data """ try: arg = MockArg3() - ans = pyomo.opt.convert_problem( (arg,pyomo.opt.ProblemFormat.cpxlp,arg), None, [pyomo.opt.ProblemFormat.cpxlp]) + ans = convert_problem( (arg, ProblemFormat.cpxlp,arg), None, [ProblemFormat.cpxlp]) self.fail("This test didn't fail, but glpsol cannot handle objects.") - except pyomo.opt.ConverterError: + except ConverterError: pass def test_error11(self): #""" Cannot convert MOD that contains data """ try: - ans = pyomo.opt.convert_problem( (currdir+"test3.mod",currdir+"test5.dat"), None, [pyomo.opt.ProblemFormat.cpxlp]) + ans = convert_problem( (currdir+"test3.mod",currdir+"test5.dat"), None, [ProblemFormat.cpxlp]) self.fail("Expected ConverterError exception because we provided a MOD file with a 'data;' declaration") - except pyutilib.common.ApplicationError: + except ApplicationError: err = sys.exc_info()[1] - if pyomo.common.Executable("glpsol"): + if Executable("glpsol"): self.fail("Expected ApplicationError because glpsol " "is not available: '%s'" % str(err)) return - except pyomo.opt.ConverterError: + except ConverterError: pass if __name__ == "__main__": diff --git a/pyomo/solvers/tests/mip/test_factory.py b/pyomo/solvers/tests/mip/test_factory.py index 6a9415b941c..4f757356c44 100644 --- a/pyomo/solvers/tests/mip/test_factory.py +++ b/pyomo/solvers/tests/mip/test_factory.py @@ -17,39 +17,41 @@ currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest -import pyutilib.services +from pyutilib.services import TempfileManager -import pyomo -import pyomo.opt +from pyomo.opt import AbstractProblemWriter, AbstractResultsReader, OptSolver, ReaderFactory, SolverFactory, WriterFactory from pyomo.opt.base.solvers import UnknownSolver +from pyomo.opt.plugins.sol import ResultsReader_sol +from pyomo.solvers.plugins.solvers import PICO + old_tempdir = None def setUpModule(): global old_tempdir - old_tempdir = pyutilib.services.TempfileManager.tempdir - pyutilib.services.TempfileManager.tempdir = currdir + old_tempdir = TempfileManager.tempdir + TempfileManager.tempdir = currdir def tearDownModule(): - pyutilib.services.TempfileManager.tempdir = old_tempdir + TempfileManager.tempdir = old_tempdir -class TestWriter(pyomo.opt.AbstractProblemWriter): +class TestWriter(AbstractProblemWriter): def __init__(self, name=None): - pyomo.opt.AbstractProblemWriter.__init__(self,name) + AbstractProblemWriter.__init__(self,name) -class TestReader(pyomo.opt.AbstractResultsReader): +class TestReader(AbstractResultsReader): def __init__(self, name=None): - pyomo.opt.AbstractResultsReader.__init__(self,name) + AbstractResultsReader.__init__(self,name) -class TestSolver(pyomo.opt.OptSolver): +class TestSolver(OptSolver): def __init__(self, **kwds): kwds['type'] = 'stest_type' kwds['doc'] = 'TestSolver Documentation' - pyomo.opt.OptSolver.__init__(self,**kwds) + OptSolver.__init__(self,**kwds) class OptFactoryDebug(unittest.TestCase): @@ -57,20 +59,19 @@ class OptFactoryDebug(unittest.TestCase): @classmethod def setUpClass(cls): import pyomo.environ - import pyomo.solvers.plugins def tearDown(self): - pyutilib.services.TempfileManager.clear_tempfiles() - pyomo.opt.ReaderFactory.unregister('rtest3') - pyomo.opt.ReaderFactory.unregister('stest3') - pyomo.opt.ReaderFactory.unregister('wtest3') + TempfileManager.clear_tempfiles() + ReaderFactory.unregister('rtest3') + ReaderFactory.unregister('stest3') + ReaderFactory.unregister('wtest3') def test_solver_factory(self): """ Testing the pyomo.opt solver factory with MIP solvers """ - pyomo.opt.SolverFactory.register('stest3')(TestSolver) - ans = sorted(pyomo.opt.SolverFactory) + SolverFactory.register('stest3')(TestSolver) + ans = sorted(SolverFactory) tmp = ['_mock_asl', '_mock_cbc', '_mock_cplex', '_mock_glpk', '_mock_pico', 'cbc', 'cplex', 'glpk', 'pico', 'stest3', 'asl'] tmp.sort() self.assertTrue(set(tmp) <= set(ans), msg="Set %s is not a subset of set %s" %(tmp,ans)) @@ -79,30 +80,30 @@ def test_solver_instance(self): """ Testing that we get a specific solver instance """ - ans = pyomo.opt.SolverFactory("none") + ans = SolverFactory("none") self.assertTrue(isinstance(ans, UnknownSolver)) - ans = pyomo.opt.SolverFactory("_mock_pico") - self.assertEqual(type(ans), pyomo.solvers.plugins.solvers.PICO.MockPICO) - ans = pyomo.opt.SolverFactory("_mock_pico", name="mymock") - self.assertEqual(type(ans), pyomo.solvers.plugins.solvers.PICO.MockPICO) + ans = SolverFactory("_mock_pico") + self.assertEqual(type(ans), PICO.MockPICO) + ans = SolverFactory("_mock_pico", name="mymock") + self.assertEqual(type(ans), PICO.MockPICO) self.assertEqual(ans.name, "mymock") def test_solver_registration(self): """ Testing methods in the solverwriter factory registration process """ - pyomo.opt.SolverFactory.unregister('stest3') - self.assertTrue('stest3' not in pyomo.opt.SolverFactory) - pyomo.opt.SolverFactory.register('stest3')(TestSolver) - self.assertTrue('stest3' in pyomo.opt.SolverFactory) - self.assertTrue('_mock_pico' in pyomo.opt.SolverFactory) + SolverFactory.unregister('stest3') + self.assertTrue('stest3' not in SolverFactory) + SolverFactory.register('stest3')(TestSolver) + self.assertTrue('stest3' in SolverFactory) + self.assertTrue('_mock_pico' in SolverFactory) def test_writer_factory(self): """ Testing the pyomo.opt writer factory with MIP writers """ - pyomo.opt.WriterFactory.register('wtest3')(TestWriter) - factory = pyomo.opt.WriterFactory + WriterFactory.register('wtest3')(TestWriter) + factory = WriterFactory self.assertTrue(set(['wtest3']) <= set(factory)) def test_writer_instance(self): @@ -112,27 +113,27 @@ def test_writer_instance(self): Note: this simply provides code coverage right now, but later it should be adapted to generate a specific writer. """ - ans = pyomo.opt.WriterFactory("none") + ans = WriterFactory("none") self.assertEqual(ans, None) - ans = pyomo.opt.WriterFactory("wtest3") + ans = WriterFactory("wtest3") self.assertNotEqual(ans, None) def test_writer_registration(self): """ Testing methods in the writer factory registration process """ - pyomo.opt.WriterFactory.unregister('wtest3') - self.assertTrue(not 'wtest3' in pyomo.opt.WriterFactory) - pyomo.opt.WriterFactory.register('wtest3')(TestWriter) - self.assertTrue('wtest3' in pyomo.opt.WriterFactory) + WriterFactory.unregister('wtest3') + self.assertTrue(not 'wtest3' in WriterFactory) + WriterFactory.register('wtest3')(TestWriter) + self.assertTrue('wtest3' in WriterFactory) def test_reader_factory(self): """ Testing the pyomo.opt reader factory """ - pyomo.opt.ReaderFactory.register('rtest3')(TestReader) - ans = pyomo.opt.ReaderFactory + ReaderFactory.register('rtest3')(TestReader) + ans = ReaderFactory #self.assertEqual(len(ans),4) self.assertTrue(set(ans) >= set(["rtest3", "sol","yaml", "json"])) @@ -140,10 +141,10 @@ def test_reader_instance(self): """ Testing that we get a specific reader instance """ - ans = pyomo.opt.ReaderFactory("none") + ans = ReaderFactory("none") self.assertEqual(ans, None) - ans = pyomo.opt.ReaderFactory("sol") - self.assertEqual(type(ans), pyomo.opt.plugins.sol.ResultsReader_sol) + ans = ReaderFactory("sol") + self.assertEqual(type(ans), ResultsReader_sol) #ans = pyomo.opt.ReaderFactory("osrl", "myreader") #self.assertEqual(type(ans), pyomo.opt.reader.OS.ResultsReader_osrl) #self.assertEqual(ans.name, "myreader") @@ -152,10 +153,10 @@ def test_reader_registration(self): """ Testing methods in the reader factory registration process """ - pyomo.opt.ReaderFactory.unregister('rtest3') - self.assertTrue(not 'rtest3' in pyomo.opt.ReaderFactory) - pyomo.opt.ReaderFactory.register('rtest3')(TestReader) - self.assertTrue('rtest3' in pyomo.opt.ReaderFactory) + ReaderFactory.unregister('rtest3') + self.assertTrue(not 'rtest3' in ReaderFactory) + ReaderFactory.register('rtest3')(TestReader) + self.assertTrue('rtest3' in ReaderFactory) if __name__ == "__main__": unittest.main() diff --git a/pyomo/solvers/tests/mip/test_ipopt.py b/pyomo/solvers/tests/mip/test_ipopt.py index 0b67aa83eea..91655b471de 100644 --- a/pyomo/solvers/tests/mip/test_ipopt.py +++ b/pyomo/solvers/tests/mip/test_ipopt.py @@ -16,7 +16,7 @@ import pyutilib.services import pyomo.opt -from pyomo.core import * +from pyomo.core import ConcreteModel, RangeSet, Var, Param, Objective, ConstraintList, value, minimize old_tempdir = None def setUpModule(): diff --git a/pyomo/solvers/tests/mip/test_mip.py b/pyomo/solvers/tests/mip/test_mip.py index 51ca63a34f8..f5991590d8f 100644 --- a/pyomo/solvers/tests/mip/test_mip.py +++ b/pyomo/solvers/tests/mip/test_mip.py @@ -16,8 +16,6 @@ currdir = dirname(abspath(__file__))+os.sep import pyutilib.th as unittest -import pyutilib.autotest -#pyutilib.autotest.create_test_suites(filename=currdir+'test_mip.yml', _globals=globals()) if __name__ == "__main__": unittest.main() diff --git a/pyomo/solvers/tests/mip/test_scip.py b/pyomo/solvers/tests/mip/test_scip.py index 71407495f2c..f536a43a15f 100644 --- a/pyomo/solvers/tests/mip/test_scip.py +++ b/pyomo/solvers/tests/mip/test_scip.py @@ -13,19 +13,18 @@ currdir = dirname(abspath(__file__)) import pyutilib.th as unittest -import pyutilib.services - -import pyomo.opt -from pyomo.core import * +from pyutilib.services import TempfileManager +from pyomo.opt import SolverFactory +from pyomo.core import ConcreteModel, Var, Objective, Constraint old_tempdir = None def setUpModule(): global old_tempdir - old_tempdir = pyutilib.services.TempfileManager.tempdir - pyutilib.services.TempfileManager.tempdir = currdir + old_tempdir = TempfileManager.tempdir + TempfileManager.tempdir = currdir def tearDownModule(): - pyutilib.services.TempfileManager.tempdir = old_tempdir + TempfileManager.tempdir = old_tempdir scip_available = False class Test(unittest.TestCase): @@ -46,9 +45,9 @@ def do_setup(self): global tmpdir tmpdir = os.getcwd() os.chdir(currdir) - pyutilib.services.TempfileManager.sequential_files(0) + TempfileManager.sequential_files(0) - self.scip = pyomo.opt.SolverFactory('scip', solver_io='nl') + self.scip = SolverFactory('scip', solver_io='nl') m = self.model = ConcreteModel() m.v = Var() @@ -57,8 +56,8 @@ def do_setup(self): def tearDown(self): global tmpdir - pyutilib.services.TempfileManager.clear_tempfiles() - pyutilib.services.TempfileManager.unique_files() + TempfileManager.clear_tempfiles() + TempfileManager.unique_files() os.chdir(tmpdir) def test_version_scip(self): diff --git a/pyomo/solvers/tests/models/LP_block.py b/pyomo/solvers/tests/models/LP_block.py index 527bf44f3f6..535e5d53c7c 100644 --- a/pyomo/solvers/tests/models/LP_block.py +++ b/pyomo/solvers/tests/models/LP_block.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Block, NonNegativeReals +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, Block, NonNegativeReals from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/LP_compiled.py b/pyomo/solvers/tests/models/LP_compiled.py index 88d63666c62..8c6a1b9e886 100644 --- a/pyomo/solvers/tests/models/LP_compiled.py +++ b/pyomo/solvers/tests/models/LP_compiled.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, RangeSet, ConstraintList +from pyomo.core import ConcreteModel, Var, Objective, Constraint, RangeSet, ConstraintList from pyomo.solvers.tests.models.base import _BaseTestModel, register_model from pyomo.repn.beta.matrix import compile_block_linear_constraints diff --git a/pyomo/solvers/tests/models/LP_constant_objective1.py b/pyomo/solvers/tests/models/LP_constant_objective1.py index d6a7336020e..7fb0d51e80d 100644 --- a/pyomo/solvers/tests/models/LP_constant_objective1.py +++ b/pyomo/solvers/tests/models/LP_constant_objective1.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals +from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/LP_constant_objective2.py b/pyomo/solvers/tests/models/LP_constant_objective2.py index eab6b5f2b23..32df7145f5c 100644 --- a/pyomo/solvers/tests/models/LP_constant_objective2.py +++ b/pyomo/solvers/tests/models/LP_constant_objective2.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals +from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/LP_duals_maximize.py b/pyomo/solvers/tests/models/LP_duals_maximize.py index 496cc9815c8..da735eec5d9 100644 --- a/pyomo/solvers/tests/models/LP_duals_maximize.py +++ b/pyomo/solvers/tests/models/LP_duals_maximize.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, RangeSet, maximize, ConstraintList +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, RangeSet, maximize, ConstraintList from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/LP_duals_minimize.py b/pyomo/solvers/tests/models/LP_duals_minimize.py index 9a6a8d22d09..398830064a5 100644 --- a/pyomo/solvers/tests/models/LP_duals_minimize.py +++ b/pyomo/solvers/tests/models/LP_duals_minimize.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, RangeSet, ConstraintList +from pyomo.core import ConcreteModel, Var, Objective, Constraint, RangeSet, ConstraintList from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/LP_inactive_index.py b/pyomo/solvers/tests/models/LP_inactive_index.py index c6e98575851..dfd52ed381a 100644 --- a/pyomo/solvers/tests/models/LP_inactive_index.py +++ b/pyomo/solvers/tests/models/LP_inactive_index.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Set, ConstraintList, Block +from pyomo.core import ConcreteModel, Var, Objective, Constraint, Set, ConstraintList, Block from pyomo.solvers.tests.models.base import _BaseTestModel, register_model def inactive_index_LP_obj_rule(model,i): diff --git a/pyomo/solvers/tests/models/LP_infeasible1.py b/pyomo/solvers/tests/models/LP_infeasible1.py index 05258d6b59f..6114b94b6db 100644 --- a/pyomo/solvers/tests/models/LP_infeasible1.py +++ b/pyomo/solvers/tests/models/LP_infeasible1.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals +from pyomo.core import ConcreteModel, Var, Objective, Constraint from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/LP_infeasible2.py b/pyomo/solvers/tests/models/LP_infeasible2.py index fc11f51a1ad..f3f5f78e160 100644 --- a/pyomo/solvers/tests/models/LP_infeasible2.py +++ b/pyomo/solvers/tests/models/LP_infeasible2.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals, maximize +from pyomo.core import ConcreteModel, Var, Objective, Constraint, maximize from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/LP_piecewise.py b/pyomo/solvers/tests/models/LP_piecewise.py index 545d68e7a48..48a63e146f4 100644 --- a/pyomo/solvers/tests/models/LP_piecewise.py +++ b/pyomo/solvers/tests/models/LP_piecewise.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Piecewise +from pyomo.core import ConcreteModel, Var, Objective, Piecewise from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/LP_trivial_constraints.py b/pyomo/solvers/tests/models/LP_trivial_constraints.py index 35e0b966127..ac3867aef1b 100644 --- a/pyomo/solvers/tests/models/LP_trivial_constraints.py +++ b/pyomo/solvers/tests/models/LP_trivial_constraints.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, RealInterval, ConstraintList +from pyomo.core import ConcreteModel, Var, Objective, Constraint, RealInterval, ConstraintList from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/LP_unbounded.py b/pyomo/solvers/tests/models/LP_unbounded.py index 33a1e6d1266..8099d469af7 100644 --- a/pyomo/solvers/tests/models/LP_unbounded.py +++ b/pyomo/solvers/tests/models/LP_unbounded.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals +from pyomo.core import ConcreteModel, Var, Objective from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/LP_unique_duals.py b/pyomo/solvers/tests/models/LP_unique_duals.py index 1fd416f358e..f9248dc41f4 100644 --- a/pyomo/solvers/tests/models/LP_unique_duals.py +++ b/pyomo/solvers/tests/models/LP_unique_duals.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, RangeSet, ConstraintList, NonNegativeReals, Suffix, summation +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, RangeSet, NonNegativeReals, Suffix, sum_product from pyomo.solvers.tests.models.base import _BaseTestModel, register_model def c_rule(model, j): diff --git a/pyomo/solvers/tests/models/LP_unused_vars.py b/pyomo/solvers/tests/models/LP_unused_vars.py index fe898bba923..040b9874a79 100644 --- a/pyomo/solvers/tests/models/LP_unused_vars.py +++ b/pyomo/solvers/tests/models/LP_unused_vars.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Set, ConstraintList, sum_product, Block +from pyomo.core import ConcreteModel, Var, Objective, Set, ConstraintList, sum_product, Block from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py b/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py index 1f9095ece98..86c39a11774 100644 --- a/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py +++ b/pyomo/solvers/tests/models/MILP_discrete_var_bounds.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Binary, Integers, NonNegativeReals +from pyomo.core import ConcreteModel, Var, Objective, Constraint, Binary, Integers from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/MILP_infeasible1.py b/pyomo/solvers/tests/models/MILP_infeasible1.py index f7f7fb1ae90..85699ff2717 100644 --- a/pyomo/solvers/tests/models/MILP_infeasible1.py +++ b/pyomo/solvers/tests/models/MILP_infeasible1.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Integers, Binary, NonNegativeReals +from pyomo.core import ConcreteModel, Var, Objective, Constraint, Binary from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/MILP_simple.py b/pyomo/solvers/tests/models/MILP_simple.py index cb49de5b613..fcac9c2c618 100644 --- a/pyomo/solvers/tests/models/MILP_simple.py +++ b/pyomo/solvers/tests/models/MILP_simple.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Integers, Binary, NonNegativeReals +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, Binary, NonNegativeReals from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/MILP_unbounded.py b/pyomo/solvers/tests/models/MILP_unbounded.py index 81a8274da50..832b537b776 100644 --- a/pyomo/solvers/tests/models/MILP_unbounded.py +++ b/pyomo/solvers/tests/models/MILP_unbounded.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Integers, Binary, NonNegativeReals, Integers +from pyomo.core import ConcreteModel, Var, Objective, Integers from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/MILP_unused_vars.py b/pyomo/solvers/tests/models/MILP_unused_vars.py index cc23748aa66..d1f3e142253 100644 --- a/pyomo/solvers/tests/models/MILP_unused_vars.py +++ b/pyomo/solvers/tests/models/MILP_unused_vars.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, ConstraintList, Set, Integers, RangeSet, sum_product, Block +from pyomo.core import ConcreteModel, Var, Objective, ConstraintList, Set, Integers, RangeSet, sum_product, Block from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/MIQCP_simple.py b/pyomo/solvers/tests/models/MIQCP_simple.py index d84601a48d0..4443b722e60 100644 --- a/pyomo/solvers/tests/models/MIQCP_simple.py +++ b/pyomo/solvers/tests/models/MIQCP_simple.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, Binary, maximize +from pyomo.core import ConcreteModel, Var, Objective, Constraint, Binary, maximize from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/MIQP_simple.py b/pyomo/solvers/tests/models/MIQP_simple.py index a621ff1ecfc..5d5ac3c2d37 100644 --- a/pyomo/solvers/tests/models/MIQP_simple.py +++ b/pyomo/solvers/tests/models/MIQP_simple.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals, Binary +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, NonNegativeReals, Binary from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/QCP_simple.py b/pyomo/solvers/tests/models/QCP_simple.py index 606352ca449..9da9ddd4fc1 100644 --- a/pyomo/solvers/tests/models/QCP_simple.py +++ b/pyomo/solvers/tests/models/QCP_simple.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals, maximize, ConstraintList +from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals, maximize, ConstraintList from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/QP_constant_objective.py b/pyomo/solvers/tests/models/QP_constant_objective.py index 94cde97c5f3..0e0a306ef88 100644 --- a/pyomo/solvers/tests/models/QP_constant_objective.py +++ b/pyomo/solvers/tests/models/QP_constant_objective.py @@ -8,8 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals +from pyomo.core import ConcreteModel, Var, Objective, Constraint, NonNegativeReals from pyomo.solvers.tests.models.base import _BaseTestModel, register_model # NOTE: We could test this problem on solvers that only handle diff --git a/pyomo/solvers/tests/models/QP_simple.py b/pyomo/solvers/tests/models/QP_simple.py index 86a783a9504..a9a15d7e540 100644 --- a/pyomo/solvers/tests/models/QP_simple.py +++ b/pyomo/solvers/tests/models/QP_simple.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, NonNegativeReals from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import _BaseTestModel, register_model diff --git a/pyomo/solvers/tests/models/SOS1_simple.py b/pyomo/solvers/tests/models/SOS1_simple.py index 4921e42950b..c35ddb70b5e 100644 --- a/pyomo/solvers/tests/models/SOS1_simple.py +++ b/pyomo/solvers/tests/models/SOS1_simple.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, NonNegativeReals, SOSConstraint, sum_product +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, NonNegativeReals, SOSConstraint, sum_product from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/SOS2_simple.py b/pyomo/solvers/tests/models/SOS2_simple.py index 320c8c71c8e..edee12b9f24 100644 --- a/pyomo/solvers/tests/models/SOS2_simple.py +++ b/pyomo/solvers/tests/models/SOS2_simple.py @@ -9,7 +9,7 @@ # ___________________________________________________________________________ import pyomo.kernel as pmo -from pyomo.core import ConcreteModel, Param, Var, Expression, Objective, Constraint, SOSConstraint, NonNegativeReals, ConstraintList, sum_product +from pyomo.core import ConcreteModel, Param, Var, Objective, Constraint, SOSConstraint, NonNegativeReals, ConstraintList, sum_product from pyomo.solvers.tests.models.base import _BaseTestModel, register_model @register_model diff --git a/pyomo/solvers/tests/models/base.py b/pyomo/solvers/tests/models/base.py index b7e25ff0692..0031a0dc8a4 100644 --- a/pyomo/solvers/tests/models/base.py +++ b/pyomo/solvers/tests/models/base.py @@ -19,7 +19,6 @@ from pyomo.core import Suffix, Var, Constraint, Objective from pyomo.opt import ProblemFormat, SolverFactory, TerminationCondition from pyomo.solvers.plugins.solvers.persistent_solver import PersistentSolver -from pyomo.solvers.plugins.solvers.direct_solver import DirectSolver thisDir = dirname(abspath( __file__ )) @@ -366,7 +365,7 @@ def validate_current_solution(self, **kwds): "Expected solution to be missing suffix %s" % suffix_name) elif not abs(solution[block.name][suffix_name] - \ - suffix.get(block)) < sefl.diff_tol: + suffix.get(block)) < self.diff_tol: return (False, error_str.format( block.name, diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py index 317ba440162..72bb42b5baf 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/concave_var.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pyomo.kernel as pmo +from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list breakpoints = list(range(-5,0))+list(range(1,5)) values = [-x**2 for x in breakpoints] @@ -17,31 +17,31 @@ def define_model(**kwds): sense = kwds.pop("sense") - m = pmo.block() + m = block() - m.x = pmo.variable_list() - m.Fx = pmo.variable_list() - m.piecewise = pmo.block_list() + m.x = variable_list() + m.Fx = variable_list() + m.piecewise = block_list() for i in range(7): - m.x.append(pmo.variable(lb=-5, ub=4)) - m.Fx.append(pmo.variable()) + m.x.append(variable(lb=-5, ub=4)) + m.Fx.append(variable()) m.piecewise.append( - pmo.piecewise(breakpoints, values, + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds)) - m.obj = pmo.objective(expr=sum(m.Fx), + m.obj = objective(expr=sum(m.Fx), sense=sense) # fix the answer for testing purposes - m.set_answer = pmo.constraint_list() - m.set_answer.append(pmo.constraint(m.x[0] == -5.0)) - m.set_answer.append(pmo.constraint(m.x[1] == -3.0)) - m.set_answer.append(pmo.constraint(m.x[2] == -2.5)) - m.set_answer.append(pmo.constraint(m.x[3] == -1.5)) - m.set_answer.append(pmo.constraint(m.x[4] == 2.0)) - m.set_answer.append(pmo.constraint(m.x[5] == 3.5)) - m.set_answer.append(pmo.constraint(m.x[6] == 4.0)) + m.set_answer = constraint_list() + m.set_answer.append(constraint(m.x[0] == -5.0)) + m.set_answer.append(constraint(m.x[1] == -3.0)) + m.set_answer.append(constraint(m.x[2] == -2.5)) + m.set_answer.append(constraint(m.x[3] == -1.5)) + m.set_answer.append(constraint(m.x[4] == 2.0)) + m.set_answer.append(constraint(m.x[5] == 3.5)) + m.set_answer.append(constraint(m.x[6] == 4.0)) return m diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py index 3b59078a025..f4192ae945d 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/convex_var.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pyomo.kernel as pmo +from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list breakpoints = list(range(-5,0))+list(range(1,5)) values = [x**2 for x in breakpoints] @@ -17,31 +17,31 @@ def define_model(**kwds): sense = kwds.pop("sense") - m = pmo.block() + m = block() - m.x = pmo.variable_list() - m.Fx = pmo.variable_list() - m.piecewise = pmo.block_list() + m.x = variable_list() + m.Fx = variable_list() + m.piecewise = block_list() for i in range(7): - m.x.append(pmo.variable(lb=-5, ub=4)) - m.Fx.append(pmo.variable()) + m.x.append(variable(lb=-5, ub=4)) + m.Fx.append(variable()) m.piecewise.append( - pmo.piecewise(breakpoints, values, + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds)) - m.obj = pmo.objective(expr=sum(m.Fx), + m.obj = objective(expr=sum(m.Fx), sense=sense) # fix the answer for testing purposes - m.set_answer = pmo.constraint_list() - m.set_answer.append(pmo.constraint(m.x[0] == -5.0)) - m.set_answer.append(pmo.constraint(m.x[1] == -3.0)) - m.set_answer.append(pmo.constraint(m.x[2] == -2.5)) - m.set_answer.append(pmo.constraint(m.x[3] == -1.5)) - m.set_answer.append(pmo.constraint(m.x[4] == 2.0)) - m.set_answer.append(pmo.constraint(m.x[5] == 3.5)) - m.set_answer.append(pmo.constraint(m.x[6] == 4.0)) + m.set_answer = constraint_list() + m.set_answer.append(constraint(m.x[0] == -5.0)) + m.set_answer.append(constraint(m.x[1] == -3.0)) + m.set_answer.append(constraint(m.x[2] == -2.5)) + m.set_answer.append(constraint(m.x[3] == -1.5)) + m.set_answer.append(constraint(m.x[4] == 2.0)) + m.set_answer.append(constraint(m.x[5] == 3.5)) + m.set_answer.append(constraint(m.x[6] == 4.0)) return m diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py index 2df219a1849..0e319fdc783 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/piecewise_var.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pyomo.kernel as pmo +from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list breakpoints = [0,1,3,5,6] values = [0,2,3,-3,-1] @@ -17,28 +17,28 @@ def define_model(**kwds): sense = kwds.pop("sense") - m = pmo.block() + m = block() - m.x = pmo.variable_list() - m.Fx = pmo.variable_list() - m.piecewise = pmo.block_list() + m.x = variable_list() + m.Fx = variable_list() + m.piecewise = block_list() for i in range(4): - m.x.append(pmo.variable(lb=0, ub=6)) - m.Fx.append(pmo.variable()) + m.x.append(variable(lb=0, ub=6)) + m.Fx.append(variable()) m.piecewise.append( - pmo.piecewise(breakpoints, values, + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds)) - m.obj = pmo.objective(expr=sum(m.Fx), + m.obj = objective(expr=sum(m.Fx), sense=sense) # fix the answer for testing purposes - m.set_answer = pmo.constraint_list() - m.set_answer.append(pmo.constraint(m.x[0] == 0.0)) - m.set_answer.append(pmo.constraint(m.x[1] == 3.0)) - m.set_answer.append(pmo.constraint(m.x[2] == 5.5)) - m.set_answer.append(pmo.constraint(m.x[3] == 6.0)) + m.set_answer = constraint_list() + m.set_answer.append(constraint(m.x[0] == 0.0)) + m.set_answer.append(constraint(m.x[1] == 3.0)) + m.set_answer.append(constraint(m.x[2] == 5.5)) + m.set_answer.append(constraint(m.x[3] == 6.0)) return m diff --git a/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py b/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py index 66166a62f20..8ef6b14ed64 100644 --- a/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py +++ b/pyomo/solvers/tests/piecewise_linear/kernel_problems/step_var.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pyomo.kernel as pmo +from pyomo.kernel import block, variable, variable_list, block_list, piecewise, objective, constraint, constraint_list breakpoints = [0, 1, 1, 2, 3] values = [0, 0, 1, 1, 2] @@ -17,31 +17,31 @@ def define_model(**kwds): sense = kwds.pop("sense") - m = pmo.block() + m = block() - m.x = pmo.variable_list() - m.Fx = pmo.variable_list() - m.piecewise = pmo.block_list() + m.x = variable_list() + m.Fx = variable_list() + m.piecewise = block_list() for i in range(4): - m.x.append(pmo.variable(lb=0, ub=3)) - m.Fx.append(pmo.variable()) + m.x.append(variable(lb=0, ub=3)) + m.Fx.append(variable()) m.piecewise.append( - pmo.piecewise(breakpoints, values, + piecewise(breakpoints, values, input=m.x[i], output=m.Fx[i], **kwds)) - m.obj = pmo.objective(expr=sum(m.Fx) + sum(m.x), + m.obj = objective(expr=sum(m.Fx) + sum(m.x), sense=sense) # fix the answer for testing purposes - m.set_answer = pmo.constraint_list() + m.set_answer = constraint_list() # Fx1 should solve to 0 - m.set_answer.append(pmo.constraint(expr= m.x[0] == 0.5)) - m.set_answer.append(pmo.constraint(expr= m.x[1] == 1.0)) - m.set_answer.append(pmo.constraint(expr= m.Fx[1] == 0.5)) + m.set_answer.append(constraint(expr= m.x[0] == 0.5)) + m.set_answer.append(constraint(expr= m.x[1] == 1.0)) + m.set_answer.append(constraint(expr= m.Fx[1] == 0.5)) # Fx[2] should solve to 1 - m.set_answer.append(pmo.constraint(expr= m.x[2] == 1.5)) + m.set_answer.append(constraint(expr= m.x[2] == 1.5)) # Fx[3] should solve to 1.5 - m.set_answer.append(pmo.constraint(expr= m.x[3] == 2.5)) + m.set_answer.append(constraint(expr= m.x[3] == 2.5)) return m diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py index f6ffb43b434..7ec3e4f1782 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray1.py @@ -19,7 +19,7 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product INDEX_SET1 = range(1,8) # There will be two copies of this function INDEX_SET2 = range(0,2) diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py index 070eddd6d68..8244a55968d 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_multi_vararray2.py @@ -19,7 +19,8 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product + INDEX_SET = [(t1,t2) for t1 in range(1,8) for t2 in range(0,2)] DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py index 78500e31af7..0ebdd54ba79 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_var.py @@ -19,7 +19,7 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize DOMAIN_PTS = [float(i) for i in (list(range(-5,0))+list(range(1,5)))] diff --git a/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py index 25441bd4013..36cf2f4fa2b 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/concave_vararray.py @@ -19,7 +19,7 @@ \ -7x+12, 3 <= x <= 4 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product INDEX_SET = range(1,8) # There will be two copies of this function DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py index c3f2465c9f6..6e3c921b85e 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray1.py @@ -20,7 +20,7 @@ """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product INDEX_SET1 = range(1,8) INDEX_SET2 = range(0,2) diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py index 170ec987a03..a2f8b5f98fe 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_multi_vararray2.py @@ -20,7 +20,7 @@ """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product INDEX_SET = [(t1,t2) for t1 in range(1,8) for t2 in range(0,2)] DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py index 2add715f2bc..a82e9e7f00c 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_var.py @@ -20,7 +20,7 @@ """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize DOMAIN_PTS = [float(i) for i in (list(range(-5,0))+list(range(1,5)))] diff --git a/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py index 3a9813d0c76..29af01767fe 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/convex_vararray.py @@ -20,7 +20,7 @@ """ -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Piecewise, Constraint, Objective, sum_product, maximize INDEX_SET = range(1,8) # There will be two copies of this function DOMAIN_PTS = dict([(t,[float(i) for i in (list(range(-5,0))+list(range(1,5)))]) for t in INDEX_SET]) diff --git a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py index b6ef0b30ad6..150e74bfe1d 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_multi_vararray.py @@ -15,7 +15,7 @@ \ 2x-13 , 5 <= x <= 6 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product INDEX_SET1 = ['1','2','3','40'] # There will be two copies of this function INDEX_SET2 = [(t1,t2) for t1 in range(1,4) for t2 in range(1,5)] diff --git a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py index b27ff0c5741..f9b91a7242d 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_var.py @@ -15,7 +15,7 @@ \ 2x-13 , 5 <= x <= 6 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize DOMAIN_PTS = [float(i) for i in [0,1,3,5,6]] RANGE_PTS = {0.0:0.0, 1.0:2.0, 3.0:3.0, 5.0:-3.0, 6.0:-1.0} diff --git a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py index dc74878eba0..4ffe5193c4c 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/piecewise_vararray.py @@ -15,7 +15,7 @@ \ 2x-13 , 5 <= x <= 6 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize, sum_product INDEX_SET = [1,2,3,4] # There will be two copies of this function DOMAIN_PTS = dict([(t,[float(i) for i in [0,1,3,5,6]]) for t in INDEX_SET]) diff --git a/pyomo/solvers/tests/piecewise_linear/problems/step_var.py b/pyomo/solvers/tests/piecewise_linear/problems/step_var.py index 133ca61abf3..288b9ca0ca5 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/step_var.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/step_var.py @@ -16,7 +16,7 @@ \ x-1 , 2 < x <= 3 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Param, Piecewise, Constraint, maximize DOMAIN_PTS = [0, 1, 1, 2, 3] F = [0, 0, 1, 1, 2] diff --git a/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py b/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py index ed4c81f8248..9437923d934 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/step_vararray.py @@ -16,7 +16,7 @@ \ x-1 , 2 < x <= 3 """ -from pyomo.environ import * +from pyomo.core import ConcreteModel, Var, Objective, Piecewise, Constraint, maximize, sum_product INDEX = [1,2,3,4] DOMAIN_PTS = [0, 1, 1, 2, 3] diff --git a/pyomo/solvers/tests/piecewise_linear/problems/tester.py b/pyomo/solvers/tests/piecewise_linear/problems/tester.py index 2008690b929..1e2dcfabbcd 100644 --- a/pyomo/solvers/tests/piecewise_linear/problems/tester.py +++ b/pyomo/solvers/tests/piecewise_linear/problems/tester.py @@ -8,7 +8,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from pyomo.environ import * +from pyomo.environ import Var, maximize, value from pyomo.opt import SolverFactory from six import itervalues diff --git a/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py b/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py index 33bf531c4dd..2f7170e6d7f 100644 --- a/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py +++ b/pyomo/solvers/tests/piecewise_linear/test_piecewise_linear_kernel.py @@ -14,9 +14,9 @@ thisDir = dirname( abspath(__file__) ) import pyutilib.th as unittest -import pyutilib.misc +from pyutilib.misc import import_file -import pyomo.kernel as pmo +from pyomo.kernel import SolverFactory, variable, maximize, minimize from pyomo.solvers.tests.solvers import test_solver_cases problems = ['convex_var', @@ -43,19 +43,19 @@ def testMethod(obj): obj.skipTest("Solver %s (interface=%s) is not available" % (solver, writer)) - m = pyutilib.misc.import_file(os.path.join(thisDir, + m = import_file(os.path.join(thisDir, 'kernel_problems', problem), clear_cache=True) model = m.define_model(**kwds) - opt = pmo.SolverFactory(solver, solver_io=writer) + opt = SolverFactory(solver, solver_io=writer) results = opt.solve(model) # non-recursive new_results = ((var.name, var.value) - for var in model.components(ctype=pmo.variable.ctype, + for var in model.components(ctype=variable.ctype, active=True, descend_into=False)) baseline_results = getattr(obj,problem+'_results') @@ -79,18 +79,18 @@ def assignTests(cls, problem_list): for AUX in aux_list: for REPN in ['sos2','mc','inc','cc','dcc','dlog','log']: for BOUND_TYPE in ['lb','ub','eq']: - for SENSE in [pmo.maximize,pmo.minimize]: - if not( ((BOUND_TYPE == 'lb') and (SENSE == pmo.maximize)) or \ - ((BOUND_TYPE == 'ub') and (SENSE == pmo.minimize)) or \ + for SENSE in [maximize, minimize]: + if not( ((BOUND_TYPE == 'lb') and (SENSE == maximize)) or \ + ((BOUND_TYPE == 'ub') and (SENSE == minimize)) or \ ((REPN == 'mc') and ('step' in PROBLEM)) ): kwds = {} kwds['sense'] = SENSE kwds['repn'] = REPN kwds['bound'] = BOUND_TYPE - if SENSE == pmo.maximize: + if SENSE == maximize: attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format(PROBLEM,REPN,BOUND_TYPE,'maximize',solver,writer) else: - assert SENSE == pmo.minimize + assert SENSE == minimize attrName = "test_{0}_{1}_{2}_{3}_{4}_{5}".format(PROBLEM,REPN,BOUND_TYPE,'minimize',solver,writer) assert len(AUX) == 1 kwds.update(AUX) diff --git a/pyomo/solvers/tests/solvers.py b/pyomo/solvers/tests/solvers.py index 17792895aff..5534d452387 100644 --- a/pyomo/solvers/tests/solvers.py +++ b/pyomo/solvers/tests/solvers.py @@ -10,7 +10,6 @@ __all__ = ['test_solver_cases', 'available_solvers'] -import os import six import logging diff --git a/pyomo/solvers/tests/testcases.py b/pyomo/solvers/tests/testcases.py index f2307c95ace..c12a880542d 100644 --- a/pyomo/solvers/tests/testcases.py +++ b/pyomo/solvers/tests/testcases.py @@ -17,7 +17,6 @@ from pyomo.opt import TerminationCondition from pyomo.solvers.tests.models.base import test_models from pyomo.solvers.tests.solvers import test_solver_cases -import pyomo.kernel from pyomo.core.kernel.block import IBlock # For expected failures that appear in all known version diff --git a/pyomo/util/tests/test_check_units.py b/pyomo/util/tests/test_check_units.py index f9832bfc0b8..dd91ffb2741 100644 --- a/pyomo/util/tests/test_check_units.py +++ b/pyomo/util/tests/test_check_units.py @@ -12,10 +12,9 @@ # import pyutilib.th as unittest -from pyomo.environ import * +from pyomo.environ import ConcreteModel, Var, Param, Set, Constraint, Objective, Expression, Suffix, RangeSet, ExternalFunction, units, maximize, sin, cos from pyomo.network import Port, Arc from pyomo.dae import ContinuousSet, DerivativeVar -from pyomo.mpec import Complementarity, complements from pyomo.gdp import Disjunct, Disjunction from pyomo.core.base.units_container import ( pint_available, UnitsError, diff --git a/pyomo/util/tests/test_components.py b/pyomo/util/tests/test_components.py index a133e0c841d..c453fb0e569 100644 --- a/pyomo/util/tests/test_components.py +++ b/pyomo/util/tests/test_components.py @@ -7,31 +7,32 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ + from six.moves import zip_longest import pyutilib.th as unittest -import pyomo.environ as pe +import pyomo.environ as pyo import pyomo.kernel as pmo from pyomo.util.components import iter_component, rename_components class TestUtilComponents(unittest.TestCase): def test_rename_components(self): - model = pe.ConcreteModel() - model.x = pe.Var([1, 2, 3], bounds=(-10, 10), initialize=5.0) - model.z = pe.Var(bounds=(10, 20)) - model.obj = pe.Objective(expr=model.z + model.x[1]) + model = pyo.ConcreteModel() + model.x = pyo.Var([1, 2, 3], bounds=(-10, 10), initialize=5.0) + model.z = pyo.Var(bounds=(10, 20)) + model.obj = pyo.Objective(expr=model.z + model.x[1]) def con_rule(m, i): return m.x[i] + m.z == i - model.con = pe.Constraint([1, 2, 3], rule=con_rule) - model.zcon = pe.Constraint(expr=model.z >= model.x[2]) - model.b = pe.Block() - model.b.bx = pe.Var([1,2,3], initialize=42) - model.b.bz = pe.Var(initialize=42) + model.con = pyo.Constraint([1, 2, 3], rule=con_rule) + model.zcon = pyo.Constraint(expr=model.z >= model.x[2]) + model.b = pyo.Block() + model.b.bx = pyo.Var([1,2,3], initialize=42) + model.b.bz = pyo.Var(initialize=42) - c_list = list(model.component_objects(ctype=[pe.Var,pe.Constraint,pe.Objective])) + c_list = list(model.component_objects(ctype=[pyo.Var,pyo.Constraint,pyo.Objective])) name_map = rename_components(model=model, component_list=c_list, prefix='scaled_') @@ -54,15 +55,15 @@ def assertSameComponents(self, obj, other_obj): self.assertEqual(id(i), id(j)) def test_iter_component_base(self): - model = pe.ConcreteModel() - model.x = pe.Var([1, 2, 3], initialize=0) - model.z = pe.Var(initialize=0) + model = pyo.ConcreteModel() + model.x = pyo.Var([1, 2, 3], initialize=0) + model.z = pyo.Var(initialize=0) def con_rule(m, i): return m.x[i] + m.z == i - model.con = pe.Constraint([1, 2, 3], rule=con_rule) - model.zcon = pe.Constraint(expr=model.z >= model.x[2]) + model.con = pyo.Constraint([1, 2, 3], rule=con_rule) + model.zcon = pyo.Constraint(expr=model.z >= model.x[2]) self.assertSameComponents(list(iter_component(model.x)), list(model.x.values())) self.assertSameComponents(list(iter_component(model.z)), [model.z[None]]) diff --git a/pyomo/version/info.py b/pyomo/version/info.py index 4544123c205..63ada308734 100644 --- a/pyomo/version/info.py +++ b/pyomo/version/info.py @@ -25,7 +25,7 @@ # master and needs a hard reference to "suitably new" development. major=5 minor=7 -micro=1 +micro=2 releaselevel='invalid' #releaselevel='final' serial=0