diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..cc1eda2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,28 @@
+---
+name: Bug report
+about: Create a report to help us improve
+---
+
+
+**What version of OR-Tools and what language are you using?**
+Version: main/v9.5/v9.4 etc.
+Language: C++/Java/Python/C#
+
+**Which solver are you using (e.g. CP-SAT, Routing Solver, GLOP, BOP, Gurobi)**
+
+**What operating system (Linux, Windows, ...) and version?**
+
+**What did you do?**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**What did you expect to see**
+
+**What did you see instead?**
+
+Make sure you include information that can help us debug (full error message, model Proto).
+
+**Anything else we should know about your project / environment**
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..e8155a1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: true
+
+contact_links:
+- name: Modeling/Usage problem
+ url: https://groups.google.com/forum/#!forum/or-tools-discuss
+ about: Need help creating your model ?
+ Please use the mailing list for modeling issues.
+ Github issues have limited audience and answers on the github page will not benefit the rest of the users.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..99ad00e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,18 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+---
+
+
+**What language and solver does this apply to?**
+All, Python, Java, C#, C++ / CP-SAT, Routing, Linear Solver
+
+**Describe the problem you are trying to solve.**
+
+**Describe the solution you'd like**
+
+**Describe alternatives you've considered**
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
+
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..10a6bde
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,12 @@
+
diff --git a/.github/workflows/centos.yml b/.github/workflows/centos.yml
new file mode 100644
index 0000000..4a43290
--- /dev/null
+++ b/.github/workflows/centos.yml
@@ -0,0 +1,174 @@
+name: CentOS
+
+on:
+ workflow_dispatch:
+ inputs:
+ rtefrance_ortools_branch:
+ description: 'rte-france/or-tools branch name'
+ required: true
+ default: 'main'
+ push:
+ branches:
+ - main
+ - feature/*
+ - merge*
+ - fix/*
+ - release/*
+ release:
+ types: [ created ]
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+ RELEASE_CREATED: ${{ github.event_name == 'release' && github.event.action == 'created' }}
+
+jobs:
+
+ build:
+ name: shrd=${{ matrix.shared }} sirius=${{ matrix.sirius }} extras=${{ matrix.extras }} java=${{ matrix.java }} dotnet=${{ matrix.dotnet }} python=${{ matrix.python }}-${{ matrix.python-version }}
+ runs-on: ubuntu-latest
+ container: 'centos:centos7'
+ env:
+ SIRIUS_RELEASE_TAG: antares-integration-v1.4
+ XPRESS_INSTALL_DIR: xpressmp
+ strategy:
+ fail-fast: false
+ matrix:
+ sirius: [ON, OFF]
+ shared: [ON, OFF]
+ extras: [OFF]
+ include:
+ - extras: OFF
+ python: OFF
+ java: OFF
+ dotnet: OFF
+ - sirius: ON
+ shared: OFF
+ extras: OFF
+ python: OFF
+ java: OFF
+ dotnet: OFF
+
+ steps:
+ - name: set name variables
+ id: names
+ run: |
+ SHARED=${{ matrix.shared }}
+ [ $SHARED == "ON" ] && WITH_SHARED="_shared" || WITH_SHARED="_static"
+ SIRIUS=${{ matrix.sirius }}
+ [ $SIRIUS == "ON" ] && WITH_SIRIUS="_sirius" || WITH_SIRIUS=""
+ OS="_centos7"
+ APPENDIX="${OS}${WITH_SIRIUS}"
+ APPENDIX_WITH_SHARED="${OS}${WITH_SHARED}${WITH_SIRIUS}"
+ echo "appendix=$APPENDIX" >> $GITHUB_OUTPUT
+ echo "appendix_with_shared=$APPENDIX_WITH_SHARED" >> $GITHUB_OUTPUT
+
+ # Fill variable ${BRANCH_NAME}
+ - uses: nelonoel/branch-name@v1.0.1
+
+ - name: Install requirements (yum)
+ run: |
+ yum install -y epel-release
+ yum install -y git redhat-lsb-core make wget centos-release-scl scl-utils python3
+ yum install -y devtoolset-11
+ python3 -m pip install --upgrade pip
+ python3 -m pip install dataclasses
+ - name: Setup cmake
+ uses: jwlawson/actions-setup-cmake@v1.13
+ with:
+ cmake-version: '3.22.x'
+
+ - name: Checkout OR-Tools
+ run: |
+ git clone $GITHUB_SERVER_URL/rte-france/or-tools.git -b '${{ github.event.inputs.rtefrance_ortools_branch || 'main' }}' .
+
+ - name: Checkout this repository
+ run: |
+ git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git -b ${BRANCH_NAME} "patch"
+
+ - name: Apply patch
+ run: |
+ cp -r patch/* .
+ python3 patch.py
+
+ - name: Set-up Xpress from wheel
+ run: |
+ python3 -m pip install --upgrade pip
+ mkdir xpress
+ cd xpress
+ python3 -m pip download --only-binary=:all: --python-version 310 "xpress>=9.2,<9.3"
+ unzip xpr*.whl
+ XPRESS_DIR=$PWD/xpress
+ echo "XPRESSDIR=$XPRESS_DIR" >> $GITHUB_ENV
+ echo "XPAUTH_PATH=$XPRESS_DIR/license/community-xpauth.xpr" >> $GITHUB_ENV
+ ln -s $XPRESS_DIR/lib/libxprs.so.42 $XPRESS_DIR/lib/libxprs.so
+
+
+ - name: Download Sirius
+ if : ${{ matrix.sirius == 'ON' }}
+ run: |
+ zipfile=centos-7_sirius-solver.zip
+ wget https://github.com/rte-france/sirius-solver/releases/download/${{ env.SIRIUS_RELEASE_TAG }}/$zipfile
+ unzip $zipfile
+ mv centos-7_sirius-solver-install sirius_install
+ echo "LD_LIBRARY_PATH=$PWD/sirius_install/lib" >> $GITHUB_ENV
+ echo "SIRIUS_CMAKE_DIR=$PWD/sirius_install/cmake" >> $GITHUB_ENV
+
+ - name: Configure OR-Tools
+ run: |
+ source /opt/rh/devtoolset-11/enable
+ cmake --version
+ cmake -S . -B build \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=${{ matrix.shared }} \
+ -DBUILD_PYTHON=${{ matrix.python }} \
+ -DBUILD_JAVA=${{ matrix.java }} \
+ -DBUILD_DOTNET=${{ matrix.dotnet }} \
+ -DBUILD_EXAMPLES=${{ env.RELEASE_CREATED == 'true' && 'OFF' || 'ON' }} \
+ -DBUILD_DEPS=ON \
+ -DUSE_SIRIUS=${{ matrix.sirius }} \
+ -Dsirius_solver_DIR="${{ env.SIRIUS_CMAKE_DIR }}" \
+ -DCMAKE_INSTALL_PREFIX="build/install" \
+ -DBUILD_SAMPLES=OFF \
+ -DBUILD_FLATZINC=OFF
+
+ - name: Build OR-Tools Linux
+ run: |
+ source /opt/rh/devtoolset-11/enable
+ cmake --build build --config Release --target all install -j4
+
+ - name: run tests not xpress
+ if: ${{ matrix.shared == 'ON' }}
+ run: |
+ cd build
+ ctest -C Release --output-on-failure -E "_xpress"
+
+ - name: run tests xpress
+ run: |
+ cd build
+ ctest -V -C Release --output-on-failure -R "_xpress"
+
+ - name: run tests sirius
+ run: |
+ cd build
+ ctest -V -C Release --output-on-failure -R "sirius"
+
+ - name: Prepare OR-Tools install
+ id: or-install
+ run: |
+ cd build
+ ARCHIVE_NAME="ortools_cxx${{ steps.names.outputs.appendix_with_shared }}.zip"
+ ARCHIVE_PATH="$PWD/${ARCHIVE_NAME}"
+ zip -r $ARCHIVE_PATH ./install
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools install artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ steps.or-install.outputs.archive_name }}
+ path: ${{ steps.or-install.outputs.archive_path }}
+ - name: Publish OR-Tools install asset
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.or-install.outputs.archive_path }}
diff --git a/.github/workflows/oracle.yml b/.github/workflows/oracle.yml
new file mode 100644
index 0000000..3106566
--- /dev/null
+++ b/.github/workflows/oracle.yml
@@ -0,0 +1,252 @@
+name: OracleLinux
+
+on:
+ workflow_dispatch:
+ inputs:
+ rtefrance_ortools_branch:
+ description: 'rte-france/or-tools branch name'
+ required: true
+ default: 'main'
+ push:
+ branches:
+ - main
+ - feature/*
+ - merge*
+ - fix/*
+ - release/*
+ release:
+ types: [ created ]
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+ RELEASE_CREATED: ${{ github.event_name == 'release' && github.event.action == 'created' }}
+
+jobs:
+
+ build:
+ name: sirius=${{ matrix.sirius }} shrd=${{ matrix.cmake.shared }} java=${{ matrix.cmake.java }} python=${{ matrix.cmake.python }}
+ runs-on: ubuntu-latest
+ container: 'oraclelinux:8'
+ env:
+ SIRIUS_RELEASE_TAG: antares-integration-v1.4
+ strategy:
+ fail-fast: false
+ matrix:
+ sirius: [ON, OFF]
+ cmake: [
+ { shared: OFF, java: OFF, dotnet: OFF, python: OFF},
+ { shared: ON, java: ON, dotnet: OFF, python: ON},
+ ]
+ steps:
+ - name: set name variables
+ id: names
+ run: |
+ SHARED=${{ matrix.cmake.shared }}
+ [ $SHARED == "ON" ] && WITH_SHARED="_shared" || WITH_SHARED="_static"
+ SIRIUS=${{ matrix.sirius }}
+ [ $SIRIUS == "ON" ] && WITH_SIRIUS="_sirius" || WITH_SIRIUS=""
+ OS="_oraclelinux-8"
+ APPENDIX="${OS}${WITH_SIRIUS}"
+ echo "appendix=$APPENDIX" >> $GITHUB_OUTPUT
+ APPENDIX_WITH_SHARED="${OS}${WITH_SHARED}${WITH_SIRIUS}"
+ echo "appendix_with_shared=$APPENDIX_WITH_SHARED" >> $GITHUB_OUTPUT
+
+ # Fill variable ${BRANCH_NAME}
+ - uses: nelonoel/branch-name@v1.0.1
+
+ - name: Install requirements (dnf)
+ run: |
+ dnf -y update
+ dnf -y install git wget openssl-devel
+ dnf -y groupinstall "Development Tools"
+ dnf -y install gcc-toolset-11
+ dnf clean all
+ rm -rf /var/cache/dnf
+ - name: Install swig (dnf)
+ run: |
+ dnf -y update
+ dnf -y install pcre-devel
+ dnf clean all
+ rm -rf /var/cache/dnf
+ wget -q "https://downloads.sourceforge.net/project/swig/swig/swig-4.1.1/swig-4.1.1.tar.gz"
+ tar xvf swig-4.1.1.tar.gz
+ rm swig-4.1.1.tar.gz
+ cd swig-4.1.1
+ ./configure --prefix=/usr
+ make -j 4
+ make install
+ cd ..
+ rm -rf swig-4.1.1
+ - name: Install java (jdk)
+ if: ${{ matrix.cmake.java == 'ON' }}
+ run: |
+ dnf -y update
+ dnf -y install java-1.8.0-openjdk java-1.8.0-openjdk-devel maven
+ dnf clean all
+ rm -rf /var/cache/dnf
+ - name: Install python
+ run: |
+ export PATH=/root/.local/bin:$PATH
+ dnf -y update
+ dnf -y install python39-devel python39-pip python39-numpy
+ dnf remove python3.11
+ dnf clean all
+ echo "/root/.local/bin" >> $GITHUB_PATH
+ echo "$HOME/.local/bin" >> $GITHUB_PATH
+ python3 -m pip install --upgrade pip
+ python3 -m pip install protobuf mypy-protobuf absl-py setuptools wheel pandas virtualenv
+
+ - name: Setup cmake
+ uses: jwlawson/actions-setup-cmake@v1.13
+ with:
+ cmake-version: '3.26.x'
+
+ - name: Checkout or-tools
+ uses: actions/checkout@v4
+ with:
+ repository: rte-france/or-tools
+ ref: ${{ github.event.inputs.rtefrance_ortools_branch || 'main' }}
+
+ - name: Checkout this repository
+ uses: actions/checkout@v4
+ with:
+ path: "patch"
+
+ - name: Apply patch
+ run: |
+ cp -r patch/* .
+ python3 patch.py
+
+ - name: Set-up Xpress with pip
+ run: |
+ python3 -m pip install "xpress>=9.2,<9.3"
+ XPRESS_DIR=/usr/local/lib64/python3.9/site-packages/xpress
+ echo "XPRESSDIR=$XPRESS_DIR" >> $GITHUB_ENV
+ echo "XPAUTH_PATH=$XPRESS_DIR/license/community-xpauth.xpr" >> $GITHUB_ENV
+ ln -s $XPRESS_DIR/lib/libxprs.so.42 $XPRESS_DIR/lib/libxprs.so
+
+ - name: Download Sirius
+ if: ${{ matrix.sirius == 'ON' }}
+ run: |
+ zipfile=oraclelinux-8_sirius-solver.zip
+ wget https://github.com/rte-france/sirius-solver/releases/download/${{ env.SIRIUS_RELEASE_TAG }}/$zipfile
+ unzip $zipfile
+ mv oraclelinux-8_sirius-solver-install sirius_install
+ echo "LD_LIBRARY_PATH=$PWD/sirius_install/lib" >> $GITHUB_ENV
+ echo "SIRIUS_CMAKE_DIR=$PWD/sirius_install/cmake" >> $GITHUB_ENV
+
+ - name: Configure OR-Tools
+ run: |
+ source /opt/rh/gcc-toolset-11/enable
+ cmake --version
+ cmake -S . -B build \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=${{ matrix.cmake.shared }} \
+ -DBUILD_PYTHON=${{ matrix.cmake.python }} \
+ -DBUILD_JAVA=${{ matrix.cmake.java }} \
+ -DBUILD_DOTNET=${{ matrix.cmake.dotnet }} \
+ -DBUILD_EXAMPLES=${{ env.RELEASE_CREATED == 'true' && 'OFF' || 'ON' }} \
+ -DBUILD_DEPS=ON \
+ -DUSE_SIRIUS=${{ matrix.sirius }} \
+ -Dsirius_solver_DIR="${{ env.SIRIUS_CMAKE_DIR }}" \
+ -DCMAKE_INSTALL_PREFIX="build/install" \
+ -DBUILD_SAMPLES=OFF \
+ -DBUILD_FLATZINC=OFF
+
+ - name: Build OR-Tools Linux
+ run: |
+ source /opt/rh/gcc-toolset-11/enable
+ cmake --build build --config Release --target all install -j4
+
+ - name: run tests not xpress
+ run: |
+ cd build
+ ctest -C Release --output-on-failure -E "(_xpress|sirius)"
+
+ - name: run tests xpress
+ run: |
+ cd build
+ ctest -V -C Release --output-on-failure -R "_xpress"
+
+ - name: run test sirius
+ if: ${{ matrix.sirius == 'ON' }}
+ run: |
+ cd build
+ ctest -V -C Release --output-on-failure -R 'sirius'
+
+ - name: Prepare OR-Tools install
+ id: or-install
+ run: |
+ cd build
+ ARCHIVE_NAME="ortools_cxx${{ steps.names.outputs.appendix_with_shared }}.zip"
+ ARCHIVE_PATH="$PWD/${ARCHIVE_NAME}"
+ zip -r $ARCHIVE_PATH ./install
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools install artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.or-install.outputs.archive_name }}
+ path: ${{ steps.or-install.outputs.archive_path }}
+ - name: Publish OR-Tools install asset
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.or-install.outputs.archive_path }}
+
+ - name: prepare OR-Tools wheel
+ if: ${{ matrix.cmake.python == 'ON' }}
+ id: wheel
+ run: |
+ MY_DIR="ortools_python-3.9${{ steps.names.outputs.appendix }}"
+ mkdir $MY_DIR
+ cp build/python/dist/*.whl $MY_DIR
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="$PWD/${ARCHIVE_NAME}"
+ zip -r ${ARCHIVE_PATH} ${MY_DIR}
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools wheel artifact
+ if: ${{ matrix.cmake.python == 'ON' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.wheel.outputs.archive_name }}
+ path: ${{ steps.wheel.outputs.archive_path }}
+ - name: Publish OR-Tools wheel asset
+ if: ${{ matrix.cmake.python == 'ON' && env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.wheel.outputs.archive_path }}
+
+ - name: prepare OR-Tools jar
+ if: ${{ matrix.cmake.java == 'ON' }}
+ id: jar
+ run: |
+ MY_DIR="ortools_java${{ steps.names.outputs.appendix }}"
+ mkdir ${MY_DIR}
+ cp build/java/ortools-*/target/*.jar $MY_DIR
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="$PWD/${ARCHIVE_NAME}"
+ df -h
+ pwd
+ ls -ltr
+ ls -ltr ${MY_DIR}
+ echo "ARCHIVE_PATH=${ARCHIVE_PATH}"
+ echo "MY_DIR=${MY_DIR}"
+ echo "PWD=$PWD"
+ zip -r $ARCHIVE_PATH $MY_DIR
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+ - name: Upload OR-Tools jar artifact
+ if: ${{ matrix.cmake.java == 'ON' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.jar.outputs.archive_name }}
+ path: ${{ steps.jar.outputs.archive_path }}
+ - name: Publish OR-Tools jar asset
+ if: ${{ matrix.cmake.java == 'ON' && env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.jar.outputs.archive_path }}
\ No newline at end of file
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
new file mode 100644
index 0000000..2ab2738
--- /dev/null
+++ b/.github/workflows/ubuntu.yml
@@ -0,0 +1,300 @@
+name: Ubuntu
+
+on:
+ workflow_dispatch:
+ inputs:
+ rtefrance_ortools_branch:
+ description: 'rte-france/or-tools branch name'
+ required: true
+ default: 'main'
+ push:
+ branches:
+ - main
+ - feature/*
+ - merge*
+ - fix/*
+ - release/*
+ release:
+ types: [ created ]
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+jobs:
+ build:
+ name: ${{ matrix.os }} shrd=${{ matrix.cmake.shared }} sirius=${{ matrix.sirius }} java=${{ matrix.cmake.java }} dotnet=${{ matrix.cmake.dotnet }} python=${{ matrix.cmake.python }}-${{ matrix.cmake.python-version }}
+ runs-on: ${{ matrix.os }}
+ env:
+ XPRESSDIR: ${{ github.workspace }}/xpressmp
+ XPAUTH_PATH: ${{ github.workspace }}/xpressmp/bin/xpauth.xpr
+ SIRIUS_RELEASE_TAG: ${{ matrix.sirius-release-tag }}
+ SIRIUS_INSTALL_DIR: ${{ github.workspace }}/sirius_install
+ SIRIUS: ${{ github.workspace }}/sirius_install/bin
+ RELEASE_CREATED: ${{ github.event_name == 'release' && github.event.action == 'created' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: ["ubuntu-20.04"]
+ cmake: [
+ {shared: OFF, java: OFF, dotnet: OFF, python: OFF, python-version: "3.8", publish-cxx-or: ON},
+ {shared: ON, java: ON, dotnet: ON, python: OFF, python-version: "3.8", publish-cxx-or: ON},
+ {shared: ON, java: OFF, dotnet: OFF, python: ON, python-version: "3.8", publish-cxx-or: OFF},
+ {shared: ON, java: OFF, dotnet: OFF, python: ON, python-version: "3.9", publish-cxx-or: OFF},
+ {shared: ON, java: OFF, dotnet: OFF, python: ON, python-version: "3.10", publish-cxx-or: OFF},
+ ]
+ sirius: [ON, OFF]
+ sirius-release-tag: [antares-integration-v1.4]
+ steps:
+ - name: Checkout or-tools
+ uses: actions/checkout@v4
+ with:
+ repository: rte-france/or-tools
+ ref: ${{ github.event.inputs.rtefrance_ortools_branch || 'main' }}
+
+ - name: Checkout this repository
+ uses: actions/checkout@v4
+ with:
+ path: "patch"
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.cmake.python-version }}
+
+ - name: Apply patch
+ run: |
+ cp -r patch/* .
+ python patch.py
+
+ - name: Install GCC11 and others
+ shell: bash
+ run: |
+ sudo apt update
+ sudo apt install gcc-11 g++-11
+ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 110 --slave /usr/bin/g++ g++ /usr/bin/g++-11 --slave /usr/bin/gcov gcov /usr/bin/gcov-11
+ sudo apt install -yq build-essential lsb-release zlib1g-dev
+
+ - name: Swig install
+ run: |
+ sudo apt install -y swig
+ swig -version
+ - name: Update Path
+ run: echo "$HOME/.local/bin" >> $GITHUB_PATH
+ - name: install python packages
+ run: >
+ python -m pip install absl-py mypy mypy-protobuf
+ - name: Setup cmake
+ uses: jwlawson/actions-setup-cmake@v1.13
+ with:
+ cmake-version: '3.26.x'
+
+ - name: Setup .NET 6.0
+ if: ${{ matrix.cmake.dotnet == 'ON' }}
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.x
+
+ - name: Set-up Xpress with pip for Ubuntu
+ shell: bash
+ run: |
+ python -m pip install "xpress>=9.2,<9.3"
+ echo ${{ env.pythonLocation }}
+ XPRESS_DIR=${{ env.pythonLocation }}/lib/python${{ matrix.cmake.python-version }}/site-packages/xpress
+ echo "XPRESSDIR=$XPRESS_DIR" >> $GITHUB_ENV
+ echo "XPAUTH_PATH=$XPRESS_DIR/license/community-xpauth.xpr" >> $GITHUB_ENV
+ echo "Create symbolic link for XPRESS library file because it is missing in the Python installation"
+ ln -s $XPRESS_DIR/lib/libxprs.so.42 $XPRESS_DIR/lib/libxprs.so
+
+ - name: Download Sirius
+ if: ${{ matrix.sirius == 'ON' }}
+ shell: bash
+ run: |
+ zipfile=${{ matrix.os }}_sirius-solver.zip
+ wget https://github.com/rte-france/sirius-solver/releases/download/${{ env.SIRIUS_RELEASE_TAG }}/$zipfile
+ unzip $zipfile
+ mv "${{ matrix.os }}_sirius-solver-install" sirius_install
+ echo "LD_LIBRARY_PATH=$PWD/sirius_install/lib" >> $GITHUB_ENV
+ echo "SIRIUS_CMAKE_DIR=$PWD/sirius_install/cmake" >> $GITHUB_ENV
+
+ - name: ccache
+ if: ${{ startsWith(matrix.os, 'ubuntu') }}
+ uses: hendrikmuhs/ccache-action@v1.2
+ with:
+ key: ${{ matrix.os }}-${{ matrix.cmake.shared }}-${{ matrix.sirius }}-${{ matrix.cmake.dotnet }}-${{ matrix.cmake.java }}-${{ matrix.cmake.python }}-${{ matrix.cmake.python-version }}
+
+ - name: Check cmake
+ run: cmake --version
+ - name: Configure OR-Tools
+ run: >
+ cmake -S . -B build
+ -DCMAKE_BUILD_TYPE=Release
+ -DBUILD_SHARED_LIBS=${{ matrix.cmake.shared }}
+ -DBUILD_PYTHON=${{ matrix.cmake.python }}
+ -DBUILD_JAVA=${{ matrix.cmake.java }}
+ -DBUILD_DOTNET=${{ matrix.cmake.dotnet }}
+ -DBUILD_EXAMPLES=${{ env.RELEASE_CREATED == 'true' && 'OFF' || 'ON' }}
+ -DBUILD_DEPS=ON
+ -DUSE_SIRIUS=${{ matrix.sirius }}
+ -Dsirius_solver_DIR="${{ env.SIRIUS_CMAKE_DIR }}"
+ -DBUILD_SAMPLES=OFF
+ -DCMAKE_INSTALL_PREFIX="build/install"
+
+ - name: Build OR-Tools Linux
+ run: >
+ cmake
+ --build build
+ --config Release
+ --target all install -j4
+
+ - name: run tests not xpress
+ if: ${{ matrix.cmake.shared == 'ON' && (matrix.cmake.python != 'ON' || matrix.cmake.python-version != '3.8' ) }}
+ working-directory: ./build/
+ run: >
+ ctest
+ -C Release
+ --output-on-failure
+ -E "(_xpress|_sirius|python_init_)"
+
+ - name: run tests xpress
+ working-directory: ./build/
+ run: >
+ ctest
+ -V
+ -C Release
+ --output-on-failure
+ -R "_xpress"
+
+ - name: run tests sirius
+ working-directory: ./build/
+ run: >
+ ctest
+ -V
+ -C Release
+ --output-on-failure
+ -R "sirius"
+
+ - name: set name variables
+ id: names
+ shell: bash
+ run: |
+ SHARED=${{ matrix.cmake.shared }}
+ [ $SHARED == "ON" ] && WITH_SHARED="_shared" || WITH_SHARED="_static"
+ SIRIUS=${{ matrix.sirius }}
+ [ $SIRIUS == "ON" ] && WITH_SIRIUS="_sirius" || WITH_SIRIUS=""
+ OS="_${{ matrix.os }}"
+ APPENDIX="${OS}${WITH_SIRIUS}"
+ echo "appendix=$APPENDIX" >> $GITHUB_OUTPUT
+ APPENDIX_WITH_SHARED="${OS}${WITH_SHARED}${WITH_SIRIUS}"
+ echo "appendix_with_shared=$APPENDIX_WITH_SHARED" >> $GITHUB_OUTPUT
+
+ - name: Get release
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ id: get_release
+ uses:
+ bruceadams/get-release@v1.3.2
+
+ - name: Prepare OR-Tools install
+ if: ${{ matrix.cmake.publish-cxx-or == 'ON' }}
+ id: or-install
+ shell: bash
+ run: |
+ cd build
+ ARCHIVE_NAME="ortools_cxx${{ steps.names.outputs.appendix_with_shared }}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "$ARCHIVE_PATH" ./install
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools install artifact
+ uses: actions/upload-artifact@v4
+ if: ${{ matrix.cmake.publish-cxx-or == 'ON' }}
+ with:
+ name: ${{ steps.or-install.outputs.archive_name }}
+ path: ${{ steps.or-install.outputs.archive_path }}
+ - name: Publish OR-Tools install asset
+ if: ${{ env.RELEASE_CREATED == 'true' && matrix.cmake.publish-cxx-or == 'ON' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.or-install.outputs.archive_path }}
+
+ - name: prepare OR-Tools wheel
+ if: ${{ matrix.cmake.python == 'ON' }}
+ id: wheel
+ shell: bash
+ run: |
+ cd ./build/python/dist
+ MY_DIR="ortools_python-${{ matrix.cmake.python-version }}${{ steps.names.outputs.appendix }}"
+ mkdir ${MY_DIR}
+ cp *.whl "${MY_DIR}"
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "${ARCHIVE_PATH}" "${MY_DIR}"
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools wheel artifact
+ if: ${{ matrix.cmake.python == 'ON' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.wheel.outputs.archive_name }}
+ path: ${{ steps.wheel.outputs.archive_path }}
+ - name: Publish OR-Tools wheel asset
+ if: ${{ env.RELEASE_CREATED == 'true' && matrix.cmake.python == 'ON' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.wheel.outputs.archive_path }}
+
+ - name: prepare OR-Tools jar
+ if: ${{ matrix.cmake.java == 'ON' }}
+ id: jar
+ shell: bash
+ run: |
+ cd ./build/java
+ MY_DIR="ortools_java${{ steps.names.outputs.appendix }}"
+ mkdir ${MY_DIR}
+ cp ortools-*/target/*.jar "${MY_DIR}"
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "${ARCHIVE_PATH}" "${MY_DIR}"
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools jar artifact
+ if: ${{ matrix.cmake.java == 'ON' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.jar.outputs.archive_name }}
+ path: ${{ steps.jar.outputs.archive_path }}
+ - name: Publish OR-Tools jar asset
+ if: ${{ env.RELEASE_CREATED == 'true' && matrix.cmake.java == 'ON' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.jar.outputs.archive_path }}
+
+ - name: prepare OR-Tools dotnet
+ if: ${{ matrix.cmake.dotnet == 'ON' }}
+ id: dotnet
+ shell: bash
+ run: |
+ cd ./build/dotnet/packages/
+ MY_DIR="ortools_dotnet${{ steps.names.outputs.appendix }}"
+ mkdir ${MY_DIR}
+ cp Google.OrTools.*.nupkg "${MY_DIR}"
+ cp Google.OrTools.runtime.*.nupkg "${MY_DIR}"
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "${ARCHIVE_PATH}" "${MY_DIR}"
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools dotnet artifact
+ if: ${{ matrix.cmake.dotnet == 'ON' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.dotnet.outputs.archive_name }}
+ path: ${{ steps.dotnet.outputs.archive_path }}
+ - name: Publish OR-Tools dotnet asset
+ if: ${{ env.RELEASE_CREATED == 'true' && matrix.cmake.dotnet == 'ON' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.dotnet.outputs.archive_path }}
diff --git a/.github/workflows/windows-cpp.yml b/.github/workflows/windows-cpp.yml
new file mode 100644
index 0000000..c9d06b1
--- /dev/null
+++ b/.github/workflows/windows-cpp.yml
@@ -0,0 +1,175 @@
+name: Windows-cpp
+
+on:
+ workflow_dispatch:
+ inputs:
+ rtefrance_ortools_branch:
+ description: 'rte-france/or-tools branch name'
+ required: true
+ default: 'main'
+ push:
+ branches:
+ - main
+ - feature/*
+ - merge*
+ - fix/*
+ - release/*
+ release:
+ types: [ created ]
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+ RELEASE_CREATED: ${{ github.event_name == 'release' && github.event.action == 'created' }}
+
+jobs:
+ build:
+ name: Windows cpp sirius=${{ matrix.sirius }} sirius-release-tag=${{ matrix.sirius-release-tag }}
+ runs-on: ${{ matrix.os }}
+ env:
+ SIRIUS_RELEASE_TAG: ${{ matrix.sirius-release-tag }}
+ SIRIUS_INSTALL_PATH: ${{ github.workspace }}/sirius_install
+ SIRIUS: ${{ github.workspace }}/sirius_install/bin
+ strategy:
+ fail-fast: false
+ matrix:
+ sirius: [ON, OFF]
+ os: [windows-latest]
+ sirius-release-tag: [ antares-integration-v1.4 ]
+ steps:
+ - name: Checkout or-tools
+ uses: actions/checkout@v4
+ with:
+ repository: rte-france/or-tools
+ ref: ${{ github.event.inputs.rtefrance_ortools_branch || 'main' }}
+
+ - name: Checkout this repository
+ uses: actions/checkout@v4
+ with:
+ path: "patch"
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.8"
+
+ - name: Apply patch
+ shell: bash
+ run: |
+ cp -r patch/* .
+ python patch.py
+
+ - name: Set-up Xpress with pip
+ shell: bash
+ run: |
+ python -m pip install --no-cache-dir "xpress>=9.2,<9.3"
+ XPRESS_DIR="${{ env.pythonLocation }}\Lib\site-packages\xpress"
+ cp -r $XPRESS_DIR/lib $XPRESS_DIR/bin
+ echo "XPRESSDIR=$XPRESS_DIR" >> $GITHUB_ENV
+ echo "$XPRESS_DIR/bin" >> $GITHUB_PATH
+
+ - name: Download Sirius
+ if: ${{ matrix.sirius == 'ON' }}
+ run: |
+ (New-Object System.Net.WebClient).DownloadFile("https://github.com/rte-france/sirius-solver/releases/download/${{ env.SIRIUS_RELEASE_TAG }}/${{ matrix.os }}_sirius-solver.zip","sirius-solver.zip");
+ Expand-Archive .\sirius-solver.zip .;
+ Remove-Item .\sirius-solver.zip
+ - name: Install Sirius
+ if: ${{ matrix.sirius == 'ON' }}
+ shell: bash
+ run: |
+ mv "${{ matrix.os }}_sirius-solver-install" "${{ env.SIRIUS_INSTALL_PATH }}"
+ echo "${{ env.SIRIUS }}" >> $GITHUB_PATH
+
+ - name: Check cmake
+ run: cmake --version
+ - name: Configure
+ run: >
+ cmake -S. -Bbuild
+ -G "Visual Studio 17 2022"
+ -DCMAKE_BUILD_TYPE=Release
+ -DBUILD_DEPS=ON
+ -DBUILD_EXAMPLES=${{ env.RELEASE_CREATED == 'true' && 'OFF' || 'ON' }}
+ -DBUILD_SAMPLES=OFF
+ -DCMAKE_INSTALL_PREFIX=install
+ -DUSE_SIRIUS=${{ matrix.sirius }}
+
+ - name: Build
+ run: >
+ cmake --build build
+ --config Release
+ --target ALL_BUILD
+ -v -j2
+
+ - name: Tests not xpress
+ working-directory: ./build/
+ run: >
+ ctest -C Release
+ --output-on-failure
+ -E "(_xpress|_sirius)"
+
+ - name: Tests xpress
+ working-directory: ./build/
+ run: |
+ $env:XPRESSDIR
+ Get-ChildItem -Path $env:XPRESSDIR
+ ctest -V -C Release --output-on-failure -R "_xpress"
+
+ - name: Tests sirius
+ working-directory: ./build/
+ run: >
+ ctest -V -C Release --output-on-failure -R "sirius"
+
+ - name: Install
+ run: >
+ cmake --build build
+ --config Release
+ --target INSTALL
+ -v
+
+ - name: set name variables
+ id: names
+ shell: bash
+ run: |
+ SHARED=${{ matrix.shared }}
+ [ $SHARED == "ON" ] && WITH_SHARED="_shared" || WITH_SHARED="_static"
+ SIRIUS_TAG=${{ matrix.sirius-release-tag }}
+ [ $SIRIUS_TAG == "metrix-integration-v0.1" ] && SIRIUS_BRANCH="-metrix" || SIRIUS_BRANCH=""
+ SIRIUS=${{ matrix.sirius }}
+ [ $SIRIUS == "ON" ] && WITH_SIRIUS="_sirius$SIRIUS_BRANCH" || WITH_SIRIUS=""
+ OS="_${{ matrix.os }}"
+ APPENDIX="${OS}${WITH_SIRIUS}"
+ echo "appendix=$APPENDIX" >> $GITHUB_OUTPUT
+ APPENDIX_WITH_SHARED="${OS}${WITH_SHARED}${WITH_SIRIUS}"
+ echo "appendix_with_shared=$APPENDIX_WITH_SHARED" >> $GITHUB_OUTPUT
+
+ - name: Get release
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ id: get_release
+ uses:
+ bruceadams/get-release@v1.3.2
+
+ - name: install zip
+ shell: cmd
+ run: |
+ choco install zip --no-progress
+
+ - name: Prepare OR-Tools install
+ id: or-install
+ shell: bash
+ run: |
+ ARCHIVE_NAME="ortools_cxx${{ steps.names.outputs.appendix_with_shared }}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "$ARCHIVE_PATH" ./install
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools install artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.or-install.outputs.archive_name }}
+ path: ${{ steps.or-install.outputs.archive_path }}
+ - name: Publish OR-Tools install asset
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: build/${{ steps.or-install.outputs.archive_name }}
diff --git a/.github/workflows/windows-java-dotnet.yml b/.github/workflows/windows-java-dotnet.yml
new file mode 100644
index 0000000..e6469aa
--- /dev/null
+++ b/.github/workflows/windows-java-dotnet.yml
@@ -0,0 +1,201 @@
+name: Windows Java Dotnet
+
+on:
+ workflow_dispatch:
+ inputs:
+ rtefrance_ortools_branch:
+ description: 'rte-france/or-tools branch name'
+ required: true
+ default: 'main'
+ push:
+ branches:
+ - main
+ - feature/*
+ - merge*
+ - fix/*
+ - release/*
+ release:
+ types: [ created ]
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+ RELEASE_CREATED: ${{ github.event_name == 'release' && github.event.action == 'created' }}
+
+jobs:
+ build:
+ name: Windows java and dotnet
+ runs-on: windows-latest
+ env:
+ XPRESSDIR: ${{ github.workspace }}\xpressmp
+ XPRESS: ${{ github.workspace }}\xpressmp\\bin
+ XPAUTH_PATH: ${{ github.workspace }}\xpressmp\bin\xpauth.xpr
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout or-tools
+ uses: actions/checkout@v4
+ with:
+ repository: rte-france/or-tools
+ ref: ${{ github.event.inputs.rtefrance_ortools_branch || 'main' }}
+
+ - name: Checkout this repository
+ uses: actions/checkout@v4
+ with:
+ path: "patch"
+
+ - name: Setup .NET 6.0
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.x
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.9"
+
+ - name: Apply patch
+ shell: bash
+ run: |
+ cp -r patch/* .
+ python patch.py
+
+ - name: Install SWIG 4.1.1
+ run: |
+ (New-Object System.Net.WebClient).DownloadFile("http://prdownloads.sourceforge.net/swig/swigwin-4.1.1.zip","swigwin-4.1.1.zip");
+ Expand-Archive .\swigwin-4.1.1.zip .;
+ echo "$((Get-Item .).FullName)/swigwin-4.1.1" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+ Remove-Item .\swigwin-4.1.1.zip
+ - name: Check swig
+ run: swig -version
+
+ - name: Set-up Xpress with pip
+ shell: bash
+ run: |
+ python -m pip install --no-cache-dir "xpress>=9.2,<9.3"
+ XPRESS_DIR="${{ env.pythonLocation }}\Lib\site-packages\xpress"
+ cp -r $XPRESS_DIR/lib $XPRESS_DIR/bin
+ echo "XPRESSDIR=$XPRESS_DIR" >> $GITHUB_ENV
+ echo "XPAUTH_PATH=$XPRESS_DIR\license\community-xpauth.xpr" >> $GITHUB_ENV
+ echo "$XPRESS_DIR/bin" >> $GITHUB_PATH
+
+ - name: Check cmake
+ run: cmake --version
+ - name: Configure
+ run: >
+ cmake -S. -Bbuild
+ -G "Visual Studio 17 2022"
+ -DCMAKE_BUILD_TYPE=Release
+ -DBUILD_JAVA=ON
+ -DBUILD_DOTNET=ON
+ -DBUILD_EXAMPLES=${{ env.RELEASE_CREATED == 'true' && 'OFF' || 'ON' }}
+ -DBUILD_CXX_SAMPLES=OFF
+ -DBUILD_SAMPLES=OFF
+ -DCMAKE_INSTALL_PREFIX=install
+ -DBUILD_FLATZINC=OFF
+
+ - name: Build
+ run: >
+ cmake --build build
+ --config Release
+ --target ALL_BUILD
+ -v -j2
+
+ - name: Tests not xpress
+ working-directory: ./build/
+ run: >
+ ctest -C Release
+ --output-on-failure
+ -E "_xpress"
+
+ - name: Check xpress installation
+ shell: bash
+ run: |
+ echo "ls -l $XPRESSDIR"
+ ls -l $XPRESSDIR
+ echo "ls -l $XPRESSDIR/bin"
+ ls -l $XPRESSDIR/bin
+ echo "ls -l $XPRESSDIR/lib"
+ ls -l $XPRESSDIR/lib
+ echo $XPAUTH_PATH
+ cat $XPAUTH_PATH
+
+ - name: Tests xpress
+ working-directory: ./build/
+ run: |
+ $env:XPRESSDIR
+ Get-ChildItem -Path $env:XPRESSDIR
+ ctest -V -C Release --output-on-failure -R "_xpress"
+
+ - name: set name variables
+ id: names
+ shell: bash
+ run: |
+ SHARED=${{ matrix.shared }}
+ [ $SHARED == "ON" ] && WITH_SHARED="_shared" || WITH_SHARED="_static"
+ OS="_windows-latest"
+ APPENDIX="${OS}"
+ echo "appendix=$APPENDIX" >> $GITHUB_OUTPUT
+ APPENDIX_WITH_SHARED="${OS}${WITH_SHARED}"
+ echo "appendix_with_shared=$APPENDIX_WITH_SHARED" >> $GITHUB_OUTPUT
+
+ - name: Get release
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ id: get_release
+ uses:
+ bruceadams/get-release@v1.3.2
+
+ - name: install zip
+ shell: cmd
+ run: |
+ choco install zip --no-progress
+
+ - name: prepare OR-Tools jar
+ id: jar
+ shell: bash
+ run: |
+ cd ./build/java
+ MY_DIR="ortools_java${{ steps.names.outputs.appendix }}"
+ mkdir ${MY_DIR}
+ cp ortools-*/target/*.jar "${MY_DIR}"
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "${ARCHIVE_PATH}" "${MY_DIR}"
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools jar artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.jar.outputs.archive_name }}
+ path: ${{ steps.jar.outputs.archive_path }}
+ - name: Publish OR-Tools jar asset
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: build/${{ steps.jar.outputs.archive_name }}
+
+ - name: prepare OR-Tools dotnet
+ id: dotnet
+ shell: bash
+ run: |
+ cd ./build/dotnet/packages/
+ MY_DIR="ortools_dotnet${{ steps.names.outputs.appendix }}"
+ mkdir ${MY_DIR}
+ cp Google.OrTools.*.nupkg "${MY_DIR}"
+ cp Google.OrTools.runtime.*.nupkg "${MY_DIR}"
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "${ARCHIVE_PATH}" "${MY_DIR}"
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools dotnet artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.dotnet.outputs.archive_name }}
+ path: ${{ steps.dotnet.outputs.archive_path }}
+ - name: Publish OR-Tools dotnet asset
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: build/${{ steps.dotnet.outputs.archive_name }}
diff --git a/.github/workflows/windows-python.yml b/.github/workflows/windows-python.yml
new file mode 100644
index 0000000..22d7946
--- /dev/null
+++ b/.github/workflows/windows-python.yml
@@ -0,0 +1,167 @@
+name: Windows-python
+
+on:
+ workflow_dispatch:
+ inputs:
+ rtefrance_ortools_branch:
+ description: 'rte-france/or-tools branch name'
+ required: true
+ default: 'main'
+ push:
+ branches:
+ - main
+ - feature/*
+ - merge*
+ - fix/*
+ - release/*
+ release:
+ types: [ created ]
+
+env:
+ GITHUB_TOKEN: ${{ github.token }}
+ RELEASE_CREATED: ${{ github.event_name == 'release' && github.event.action == 'created' }}
+
+jobs:
+ build:
+ name: Windows python ${{ matrix.python.version }}
+ runs-on: windows-latest
+ env:
+ XPRESSDIR: ${{ github.workspace }}\xpressmp
+ XPRESS: ${{ github.workspace }}\xpressmp\bin
+ XPAUTH_PATH: ${{ github.workspace }}\xpressmp\bin\xpauth.xpr
+ strategy:
+ fail-fast: false
+ matrix:
+ python: [
+ { version: "3.9", dir: Python309 },
+ { version: "3.10", dir: Python310 },
+ { version: "3.11", dir: Python311 },
+ ]
+ steps:
+ - name: Checkout or-tools
+ uses: actions/checkout@v4
+ with:
+ repository: rte-france/or-tools
+ ref: ${{ github.event.inputs.rtefrance_ortools_branch || 'main' }}
+
+ - name: Checkout this repository
+ uses: actions/checkout@v4
+ with:
+ path: "patch"
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python.version }}
+
+ - name: Install python3
+ run: python3 -m pip install --user mypy-protobuf absl-py setuptools wheel numpy pandas
+
+ - name: Apply patch
+ shell: bash
+ run: |
+ cp -r patch/* .
+ python patch.py
+
+ - name: Install SWIG 4.1.1
+ run: |
+ (New-Object System.Net.WebClient).DownloadFile("http://prdownloads.sourceforge.net/swig/swigwin-4.1.1.zip","swigwin-4.1.1.zip");
+ Expand-Archive .\swigwin-4.1.1.zip .;
+ echo "$((Get-Item .).FullName)/swigwin-4.1.1" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+ Remove-Item .\swigwin-4.1.1.zip
+ - name: Check swig
+ run: swig -version
+ - name: Add Python binaries to path Windows
+ run: >
+ echo "$((Get-Item ~).FullName)/AppData/Roaming/Python/${{ matrix.python.dir }}/Scripts" |
+ Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+
+ - name: Set-up Xpress with pip
+ shell: bash
+ run: |
+ python -m pip install --no-cache-dir "xpress>=9.2,<9.3"
+ XPRESS_DIR="${{ env.pythonLocation }}\Lib\site-packages\xpress"
+ cp -r $XPRESS_DIR/lib $XPRESS_DIR/bin
+ echo "XPRESSDIR=$XPRESS_DIR" >> $GITHUB_ENV
+ echo "XPAUTH_PATH=$XPRESS_DIR\license\community-xpauth.xpr" >> $GITHUB_ENV
+ echo "$XPRESS_DIR/bin" >> $GITHUB_PATH
+
+ - name: Check cmake
+ run: cmake --version
+ - name: Configure
+ run: >
+ cmake -S. -Bbuild
+ -G "Visual Studio 17 2022"
+ -DCMAKE_BUILD_TYPE=Release
+ -DBUILD_SAMPLES=OFF
+ -DBUILD_EXAMPLES=${{ env.RELEASE_CREATED == 'true' && 'OFF' || 'ON' }}
+ -DBUILD_CXX_SAMPLES=OFF
+ -DBUILD_PYTHON=ON
+ -DBUILD_SAMPLES=OFF
+
+ - name: Build
+ run: >
+ cmake --build build
+ --config Release
+ --target ALL_BUILD
+ -v -j2
+
+ - name: Tests not xpress
+ working-directory: ./build/
+ run: >
+ ctest -C Release
+ --output-on-failure
+ -E "_xpress"
+
+ - name: Tests xpress
+ working-directory: ./build/
+ run: |
+ ctest -V -C Release --output-on-failure -R "_xpress"
+
+ - name: set name variables
+ id: names
+ shell: bash
+ run: |
+ SHARED=${{ matrix.shared }}
+ [ $SHARED == "ON" ] && WITH_SHARED="_shared" || WITH_SHARED="_static"
+ OS="_windows-latest"
+ APPENDIX="${OS}"
+ echo "appendix=$APPENDIX" >> $GITHUB_OUTPUT
+ APPENDIX_WITH_SHARED="${OS}${WITH_SHARED}"
+ echo "appendix_with_shared=$APPENDIX_WITH_SHARED" >> $GITHUB_OUTPUT
+
+ - name: Get release
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ id: get_release
+ uses:
+ bruceadams/get-release@v1.3.2
+
+ - name: install zip
+ shell: cmd
+ run: |
+ choco install zip --no-progress
+
+ - name: prepare OR-Tools wheel
+ id: wheel
+ shell: bash
+ run: |
+ cd ./build/python/dist
+ MY_DIR="ortools_python-${{ matrix.python.version }}${{ steps.names.outputs.appendix }}"
+ mkdir ${MY_DIR}
+ cp *.whl "${MY_DIR}"
+ ARCHIVE_NAME="${MY_DIR}.zip"
+ ARCHIVE_PATH="${{ github.workspace }}/build/${ARCHIVE_NAME}"
+ zip -r "${ARCHIVE_PATH}" "${MY_DIR}"
+ echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT
+ echo "archive_path=$ARCHIVE_PATH" >> $GITHUB_OUTPUT
+
+ - name: Upload OR-Tools wheel artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.wheel.outputs.archive_name }}
+ path: ${{ steps.wheel.outputs.archive_path }}
+ - name: Publish OR-Tools wheel asset
+ if: ${{ env.RELEASE_CREATED == 'true' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ files: build/${{ steps.wheel.outputs.archive_name }}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..54dfa84
--- /dev/null
+++ b/README.md
@@ -0,0 +1,145 @@
+# OR-Tools - Google Optimization Tools
+
+[](https://pypi.org/project/ortools/)
+[](https://pypi.org/project/ortools/#files)
+[](https://mybinder.org/v2/gh/google/or-tools/main)
+\
+[](https://www.nuget.org/packages/Google.OrTools)
+[](https://www.nuget.org/packages/Google.OrTools)
+\
+[](https://mvnrepository.com/artifact/com.google.ortools/ortools-java)
+\
+[](https://discord.gg/ENkQrdf)
+
+Google's software suite for combinatorial optimization.
+
+## Table of Contents
+
+* [About OR-Tools](#about)
+* [Codemap](#codemap)
+* [Installation](#installation)
+* [Quick Start](#quick-start)
+* [Documentation](#documentation)
+* [Contributing](#contributing)
+* [License](#license)
+
+
+## About OR-Tools
+
+Google Optimization Tools (a.k.a., OR-Tools) is an open-source, fast and
+portable software suite for solving combinatorial optimization problems.
+
+The suite contains:
+
+* Two constraint programming solver (CP* and CP-SAT);
+* Two linear programming solvers (Glop and PDLP);
+* Wrappers around commercial and other open source solvers, including mixed
+ integer solvers;
+* Bin packing and knapsack algorithms;
+* Algorithms for the Traveling Salesman Problem and Vehicle Routing Problem;
+* Graph algorithms (shortest paths, min cost flow, max flow, linear sum
+ assignment).
+
+We wrote OR-Tools in C++, but provide wrappers in Python, C# and Java.
+
+## Codemap
+
+This software suite is composed of the following components:
+
+* [Makefile](Makefile) Top-level for
+ [GNU Make](https://www.gnu.org/software/make/manual/make.html) based build.
+* [makefiles](makefiles) Subsidiary Make files, CI and build system documentation.
+* [CMakeLists.txt](CMakeLists.txt) Top-level for
+ [CMake](https://cmake.org/cmake/help/latest/) based build.
+* [cmake](cmake) Subsidiary CMake files, CI and build system documentation.
+* [WORKSPACE](WORKSPACE) Top-level for
+ [Bazel](https://bazel.build/start/bazel-intro) based build.
+* [bazel](bazel) Subsidiary Bazel files, CI and build system documentation.
+* [ortools](ortools) Root directory for source code.
+ * [base](ortools/base) Basic utilities.
+ * [algorithms](ortools/algorithms) Basic algorithms.
+ * [samples](ortools/algorithms/samples) Carefully crafted samples.
+ * [graph](ortools/graph) Graph algorithms.
+ * [samples](ortools/graph/samples) Carefully crafted samples.
+ * [linear_solver](ortools/linear_solver) Linear solver wrapper.
+ * [samples](ortools/linear_solver/samples) Carefully crafted samples.
+ * [glop](ortools/glop) Simplex-based linear programming solver.
+ * [samples](ortools/glop/samples) Carefully crafted samples.
+ * [pdlp](ortools/pdlp) First-order linear programming solver.
+ * [samples](ortools/pdlp/samples) Carefully crafted samples.
+ * [lp_data](ortools/lp_data) Data structures for linear models.
+ * [constraint_solver](ortools/constraint_solver) Constraint and Routing
+ solver.
+ * [docs](ortools/constraint_solver/docs) Documentation of the component.
+ * [samples](ortools/constraint_solver/samples) Carefully crafted samples.
+ * [sat](ortools/sat) SAT solver.
+ * [docs](ortools/sat/docs) Documentation of the component.
+ * [samples](ortools/sat/samples) Carefully crafted samples.
+ * [bop](ortools/bop) Boolean solver based on SAT.
+ * [util](ortools/util) Utilities needed by the constraint solver
+* [examples](examples) Root directory for all examples.
+ * [contrib](examples/contrib) Examples from the community.
+ * [cpp](examples/cpp) C++ examples.
+ * [dotnet](examples/dotnet) .Net examples.
+ * [java](examples/java) Java examples.
+ * [python](examples/python) Python examples.
+ * [notebook](examples/notebook) Jupyter/IPython notebooks.
+ * [flatzinc](examples/flatzinc) FlatZinc examples.
+ * [tests](examples/tests) Unit tests and bug reports.
+* [tools](tools) Delivery Tools (e.g. Windows GNU binaries, scripts, release dockers)
+
+## Installation
+
+This software suite has been tested under:
+
+* Ubuntu 18.04 LTS and up (64-bit);
+* Apple macOS Mojave with Xcode 9.x (64-bit);
+* Microsoft Windows with Visual Studio 2022 (64-bit).
+
+OR-Tools currently builds with a Makefile, but also provides Bazel and CMake
+support.
+
+For installation instructions (both source and binary), please visit
+https://developers.google.com/optimization/introduction/installing.
+
+### Build from source using Make (legacy)
+
+We provide a Make based build.
Please check the
+[Make build instructions](makefiles/README.md).
+
+### Build from source using CMake
+
+We provide a CMake based build.
Please check the
+[CMake build instructions](cmake/README.md).
+
+### Build from source using Bazel
+
+We provide a Bazel based build.
Please check the
+[Bazel build instructions](bazel/README.md).
+
+## Quick Start
+
+The best way to learn how to use OR-Tools is to follow the tutorials in our
+developer guide:
+
+https://developers.google.com/optimization/introduction/get_started
+
+If you want to learn from code examples, take a look at the examples in the
+[examples](examples) directory.
+
+## Documentation
+
+The complete documentation for OR-Tools is available at:
+https://developers.google.com/optimization/
+
+## Contributing
+
+The [CONTRIBUTING.md](CONTRIBUTING.md) file contains instructions on how to
+submit the Contributor License Agreement before sending any pull requests (PRs).
+Of course, if you're new to the project, it's usually best to discuss any
+proposals and reach consensus before sending your first PR.
+
+## License
+
+The OR-Tools software suite is licensed under the terms of the Apache License 2.0.
+
See [LICENSE](LICENSE) for more information.
diff --git a/ortools/linear_solver/sirius_interface.cc b/ortools/linear_solver/sirius_interface.cc
new file mode 100644
index 0000000..65443d8
--- /dev/null
+++ b/ortools/linear_solver/sirius_interface.cc
@@ -0,0 +1,1417 @@
+// Copyright 2019 RTE
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Initial version of this code was provided by RTE
+
+#include
+#include
+#include
+
+#include "absl/strings/str_format.h"
+#include "ortools/base/logging.h"
+#include "ortools/base/timer.h"
+#include "ortools/linear_solver/linear_solver.h"
+
+#if defined(USE_SIRIUS)
+
+extern "C" {
+#include "srs_api.h"
+}
+
+#define SRS_INTEGER 2
+#define SRS_CONTINUOUS 1
+
+//# define EN_BASE 0
+//# define EN_BASE_LIBRE 1
+//# define EN_BASE_SUR_BORNE_INF 2
+//# define EN_BASE_SUR_BORNE_SUP 3
+//# define HORS_BASE_SUR_BORNE_INF 4
+//# define HORS_BASE_SUR_BORNE_SUP 5
+//# define HORS_BASE_A_ZERO 6 /* Pour les variables non bornees qui restent hors base */
+
+//FREE = 0,
+//AT_LOWER_BOUND,
+//AT_UPPER_BOUND,
+//FIXED_VALUE,
+//BASIC
+
+enum SRS_BASIS_STATUS {
+ SRS_BASIC = EN_BASE,
+ SRS_BASIC_FREE = EN_BASE_LIBRE,
+ SRS_AT_LOWER = EN_BASE_SUR_BORNE_INF,
+ SRS_AT_UPPER = EN_BASE_SUR_BORNE_SUP,
+ SRS_FREE_LOWER = HORS_BASE_SUR_BORNE_INF,
+ SRS_FREE_UPPER = HORS_BASE_SUR_BORNE_SUP,
+ SRS_FREE_ZERO = HORS_BASE_A_ZERO,
+};
+
+// In case we need to return a double but don't have a value for that
+// we just return a NaN.
+#if !defined(CPX_NAN)
+#define SRS_NAN std::numeric_limits::quiet_NaN()
+#endif
+
+// The argument to this macro is the invocation of a SRS function that
+// returns a status. If the function returns non-zero the macro aborts
+// the program with an appropriate error message.
+#define CHECK_STATUS(s) \
+ do { \
+ int const status_ = s; \
+ CHECK_EQ(0, status_); \
+ } while (0)
+
+namespace operations_research {
+
+ using std::unique_ptr;
+
+ // For a model that is extracted to an instance of this class there is a
+ // 1:1 corresponence between MPVariable instances and SIRIUS columns: the
+ // index of an extracted variable is the column index in the SIRIUS model.
+ // Similiar for instances of MPConstraint: the index of the constraint in
+ // the model is the row index in the SIRIUS model.
+ class SiriusInterface : public MPSolverInterface {
+ public:
+ // NOTE: 'mip' specifies the type of the problem (either continuous or
+ // mixed integer. This type is fixed for the lifetime of the
+ // instance. There are no dynamic changes to the model type.
+ explicit SiriusInterface(MPSolver *const solver, bool mip);
+ ~SiriusInterface();
+
+ // Sets the optimization direction (min/max).
+ virtual void SetOptimizationDirection(bool maximize);
+
+ // ----- Solve -----
+ // Solve the problem using the parameter values specified.
+ virtual MPSolver::ResultStatus Solve(MPSolverParameters const ¶m);
+
+ // Writes the model.
+ void Write(const std::string& filename) override;
+
+ // ----- Model modifications and extraction -----
+ // Resets extracted model
+ virtual void Reset();
+
+ virtual void SetVariableBounds(int var_index, double lb, double ub);
+ virtual void SetVariableInteger(int var_index, bool integer);
+ virtual void SetConstraintBounds(int row_index, double lb, double ub);
+
+ virtual void AddRowConstraint(MPConstraint *const ct);
+ virtual void AddVariable(MPVariable *const var);
+ virtual void SetCoefficient(MPConstraint *const constraint,
+ MPVariable const *const variable,
+ double new_value, double old_value);
+
+ // Clear a constraint from all its terms.
+ virtual void ClearConstraint(MPConstraint *const constraint);
+ // Change a coefficient in the linear objective
+ virtual void SetObjectiveCoefficient(MPVariable const *const variable,
+ double coefficient);
+ // Change the constant term in the linear objective.
+ virtual void SetObjectiveOffset(double value);
+ // Clear the objective from all its terms.
+ virtual void ClearObjective();
+
+ // ------ Query statistics on the solution and the solve ------
+ // Number of simplex iterations
+ virtual int64_t iterations() const;
+ // Number of branch-and-bound nodes. Only available for discrete problems.
+ virtual int64_t nodes() const;
+
+ // Returns the basis status of a row.
+ virtual MPSolver::BasisStatus row_status(int constraint_index) const;
+ // Returns the basis status of a column.
+ virtual MPSolver::BasisStatus column_status(int variable_index) const;
+
+ bool SetSolverSpecificParametersAsString(const std::string& parameters) override;
+
+ // ----- Misc -----
+
+ // Query problem type.
+ // Remember that problem type is a static property that is set
+ // in the constructor and never changed.
+ virtual bool IsContinuous() const { return IsLP(); }
+ virtual bool IsLP() const { return !mMip; }
+ virtual bool IsMIP() const { return mMip; }
+
+ virtual void ExtractNewVariables();
+ virtual void ExtractNewConstraints();
+ virtual void ExtractObjective();
+
+ virtual std::string SolverVersion() const;
+
+ virtual void *underlying_solver() { return reinterpret_cast(mLp); }
+
+ virtual double ComputeExactConditionNumber() const {
+ if (!IsContinuous()) {
+ LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
+ << " SIRIUS_MIXED_INTEGER_PROGRAMMING";
+ return 0.0;
+ }
+
+ // TODO(user,user): Not yet working.
+ LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
+ << " SIRIUS_LINEAR_PROGRAMMING";
+ return 0.0;
+ }
+
+ protected:
+ // Set all parameters in the underlying solver.
+ virtual void SetParameters(MPSolverParameters const ¶m);
+ // Set each parameter in the underlying solver.
+ virtual void SetRelativeMipGap(double value);
+ virtual void SetPrimalTolerance(double value);
+ virtual void SetDualTolerance(double value);
+ virtual void SetPresolveMode(int value) override;
+ virtual void SetScalingMode(int value);
+ virtual void SetLpAlgorithm(int value);
+
+ virtual bool ReadParameterFile(std::string const &filename);
+ virtual absl::Status SetNumThreads(int num_threads) override;
+ virtual std::string ValidFileExtensionForParameterFile() const;
+
+ private:
+ // Mark modeling object "out of sync". This implicitly invalidates
+ // solution information as well. It is the counterpart of
+ // MPSolverInterface::InvalidateSolutionSynchronization
+ void InvalidateModelSynchronization() {
+ mCstat = 0;
+ mRstat = 0;
+ sync_status_ = MUST_RELOAD;
+ }
+
+ // Transform SIRIUS basis status to MPSolver basis status.
+ static MPSolver::BasisStatus xformBasisStatus(char sirius_basis_status);
+
+ private:
+ SRS_PROBLEM * mLp;
+ bool const mMip;
+ // Incremental extraction.
+ // Without incremental extraction we have to re-extract the model every
+ // time we perform a solve. Due to the way the Reset() function is
+ // implemented, this will lose MIP start or basis information from a
+ // previous solve. On the other hand, if there is a significant changes
+ // to the model then just re-extracting everything is usually faster than
+ // keeping the low-level modeling object in sync with the high-level
+ // variables/constraints.
+ // Note that incremental extraction is particularly expensive in function
+ // ExtractNewVariables() since there we must scan _all_ old constraints
+ // and update them with respect to the new variables.
+ bool const supportIncrementalExtraction;
+
+ // Use slow and immediate updates or try to do bulk updates.
+ // For many updates to the model we have the option to either perform
+ // the update immediately with a potentially slow operation or to
+ // just mark the low-level modeling object out of sync and re-extract
+ // the model later.
+ enum SlowUpdates {
+ SlowSetCoefficient = 0x0001,
+ SlowClearConstraint = 0x0002,
+ SlowSetObjectiveCoefficient = 0x0004,
+ SlowClearObjective = 0x0008,
+ SlowSetConstraintBounds = 0x0010,
+ SlowSetVariableInteger = 0x0020,
+ SlowSetVariableBounds = 0x0040,
+ SlowUpdatesAll = 0xffff
+ } const slowUpdates;
+ // SIRIUS has no method to query the basis status of a single variable.
+ // Hence we query the status only once and cache the array. This is
+ // much faster in case the basis status of more than one row/column
+ // is required.
+ unique_ptr mutable mCstat;
+ unique_ptr mutable mRstat;
+
+ // Setup the right-hand side of a constraint from its lower and upper bound.
+ static void MakeRhs(double lb, double ub, double &rhs, char &sense,
+ double &range);
+
+ std::map > > fixedOrderCoefficientsPerConstraint;
+
+ // vector to store TypeDeBorneDeLaVariable values
+ std::vector varBoundsTypeValues;
+ };
+
+ // Creates a LP/MIP instance.
+ SiriusInterface::SiriusInterface(MPSolver *const solver, bool mip)
+ : MPSolverInterface(solver),
+ mLp(NULL),
+ mMip(mip),
+ slowUpdates(static_cast(SlowSetObjectiveCoefficient |
+ SlowClearObjective)),
+ supportIncrementalExtraction(false),
+ mCstat(),
+ mRstat() {
+ //google::InitGoogleLogging("Sirius");
+ int status;
+
+ char const *name = solver_->name_.c_str();
+ mLp = SRScreateprob();
+ DCHECK(mLp != nullptr); // should not be NULL if status=0
+ //FIXME CHECK_STATUS(SRSloadlp(mLp, "newProb", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ //FIXME CHECK_STATUS(SRSchgobjsense(mLp, maximize_ ? SRS_OBJ_MAXIMIZE : SRS_OBJ_MINIMIZE));
+ }
+
+ SiriusInterface::~SiriusInterface() {
+ CHECK_STATUS(SRSfreeprob(mLp));
+ //google::ShutdownGoogleLogging();
+ }
+
+ std::string SiriusInterface::SolverVersion() const {
+ // We prefer SRSversionnumber() over SRSversion() since the
+ // former will never pose any encoding issues.
+ return absl::StrFormat("SIRIUS library version %s", SRSversion());
+ }
+
+ // ------ Model modifications and extraction -----
+
+ void SiriusInterface::Reset() {
+ // Instead of explicitly clearing all modeling objects we
+ // just delete the problem object and allocate a new one.
+ CHECK_STATUS(SRSfreeprob(mLp));
+
+ const char *const name = solver_->name_.c_str();
+ mLp = SRScreateprob();
+ DCHECK(mLp != nullptr); // should not be NULL if status=0
+ //FIXME CHECK_STATUS(SRSloadlp(mLp, "newProb", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ //FIXME CHECK_STATUS(SRSchgobjsense(mLp, maximize_ ? SRS_OBJ_MAXIMIZE : SRS_OBJ_MINIMIZE));
+
+ ResetExtractionInformation();
+ mCstat = 0;
+ mRstat = 0;
+ }
+
+ void SiriusInterface::SetOptimizationDirection(bool maximize) {
+ InvalidateSolutionSynchronization();
+ //FIXME SRSchgobjsense(mLp, maximize ? SRS_OBJ_MAXIMIZE : SRS_OBJ_MINIMIZE);
+ }
+
+ void SiriusInterface::SetVariableBounds(int var_index, double lb, double ub) {
+ InvalidateSolutionSynchronization();
+
+ // Changing the bounds of a variable is fast. However, doing this for
+ // many variables may still be slow. So we don't perform the update by
+ // default. However, if we support incremental extraction
+ // (supportIncrementalExtraction is true) then we MUST perform the
+ // update here or we will lose it.
+
+ //if (!supportIncrementalExtraction && !(slowUpdates & SlowSetVariableBounds)) {
+ // InvalidateModelSynchronization();
+ //}
+ //else
+ {
+ if (variable_is_extracted(var_index)) {
+ // Variable has already been extracted, so we must modify the
+ // modeling object.
+ DCHECK_LT(var_index, last_variable_index_);
+ int const idx[1] = { var_index };
+ double lb_l = (lb == -MPSolver::infinity() ? -SRS_infinite : lb);
+ double ub_l = (ub == MPSolver::infinity() ? SRS_infinite : ub);
+ CHECK_STATUS(SRSchgbds(mLp, 1, idx, &lb_l, &ub_l));
+ }
+ else {
+ // Variable is not yet extracted. It is sufficient to just mark
+ // the modeling object "out of sync"
+ InvalidateModelSynchronization();
+ }
+ }
+ }
+
+ // Modifies integrality of an extracted variable.
+ void SiriusInterface::SetVariableInteger(int var_index, bool integer) {
+ InvalidateSolutionSynchronization();
+
+ // NOTE: The type of the model (continuous or mixed integer) is
+ // defined once and for all in the constructor. There are no
+ // dynamic changes to the model type.
+
+ // Changing the type of a variable should be fast. Still, doing all
+ // updates in one big chunk right before solve() is usually faster.
+ // However, if we support incremental extraction
+ // (supportIncrementalExtraction is true) then we MUST change the
+ // type of extracted variables here.
+
+ if (!supportIncrementalExtraction &&
+ !(slowUpdates & SlowSetVariableInteger)) {
+ InvalidateModelSynchronization();
+ }
+ else {
+ if (mMip) {
+ if (variable_is_extracted(var_index)) {
+ // Variable is extracted. Change the type immediately.
+ // TODO: Should we check the current type and don't do anything
+ // in case the type does not change?
+ DCHECK_LE(var_index, SRSgetnbcols(mLp));
+ char const type = integer ? SRS_INTEGER : SRS_CONTINUOUS;
+ throw std::logic_error("Not implemented");
+ //FIXME CHECK_STATUS(SRSchgcoltype(mLp, 1, &var_index, &type));
+ }
+ else
+ InvalidateModelSynchronization();
+ }
+ else {
+ LOG(DFATAL)
+ << "Attempt to change variable to integer in non-MIP problem!";
+ }
+ }
+ }
+
+ // Setup the right-hand side of a constraint.
+ void SiriusInterface::MakeRhs(double lb, double ub, double &rhs, char &sense,
+ double &range) {
+ if (lb == ub) {
+ // Both bounds are equal -> this is an equality constraint
+ rhs = lb;
+ range = 0.0;
+ sense = '=';
+ }
+ else if (lb > (-SRS_infinite) && ub < SRS_infinite) {
+ // Both bounds are finite -> this is a ranged constraint
+ throw std::logic_error("Sirius does not handle ranged constraint.");
+ if (ub < lb) {
+ CHECK_STATUS(-1);
+ }
+ CHECK_STATUS(-1);
+ }
+ else if (ub < SRS_infinite ||
+ (std::abs(ub) == SRS_infinite && std::abs(lb) > SRS_infinite)) {
+ // Finite upper, infinite lower bound -> this is a <= constraint
+ rhs = ub;
+ range = 0.0;
+ sense = '<';
+ }
+ else if (lb > (-SRS_infinite) ||
+ (std::abs(lb) == SRS_infinite && std::abs(ub) > SRS_infinite)) {
+ // Finite lower, infinite upper bound -> this is a >= constraint
+ rhs = lb;
+ range = 0.0;
+ sense = '>';
+ }
+ else {
+ // Lower and upper bound are both infinite.
+ // This is used for example in .mps files to specify alternate
+ // objective functions.
+ // Note that the case lb==ub was already handled above, so we just
+ // pick the bound with larger magnitude and create a constraint for it.
+ // Note that we replace the infinite bound by SRS_infinite since
+ // bounds with larger magnitude may cause other SIRIUS functions to
+ // fail (for example the export to LP files).
+ DCHECK_GT(std::abs(lb), SRS_infinite);
+ DCHECK_GT(std::abs(ub), SRS_infinite);
+ if (std::abs(lb) > std::abs(ub)) {
+ rhs = (lb < 0) ? -SRS_infinite : SRS_infinite;
+ sense = '>';
+ }
+ else {
+ rhs = (ub < 0) ? -SRS_infinite : SRS_infinite;
+ sense = '<';
+ }
+ range = 0.0;
+ }
+ }
+
+ void SiriusInterface::SetConstraintBounds(int index, double lb, double ub) {
+ InvalidateSolutionSynchronization();
+
+ // Changing rhs, sense, or range of a constraint is not too slow.
+ // Still, doing all the updates in one large operation is faster.
+ // Note however that if we do not want to re-extract the full model
+ // for each solve (supportIncrementalExtraction is true) then we MUST
+ // update the constraint here, otherwise we lose this update information.
+
+ //if (!supportIncrementalExtraction &&
+ // !(slowUpdates & SlowSetConstraintBounds)) {
+ // InvalidateModelSynchronization();
+ //}
+ //else
+ {
+ if (constraint_is_extracted(index)) {
+ // Constraint is already extracted, so we must update its bounds
+ // and its type.
+ DCHECK(mLp != NULL);
+ char sense;
+ double range, rhs;
+ MakeRhs(lb, ub, rhs, sense, range);
+ CHECK_STATUS(SRSchgrhs(mLp, 1, &index, &rhs));
+ CHECK_STATUS(SRSchgsens(mLp, 1, &index, &sense));
+ CHECK_STATUS(SRSchgrangeval(mLp, 1, &index, &range));
+ }
+ else {
+ // Constraint is not yet extracted. It is sufficient to mark the
+ // modeling object as "out of sync"
+ InvalidateModelSynchronization();
+ }
+ }
+ }
+
+ void SiriusInterface::AddRowConstraint(MPConstraint *const ct) {
+ // This is currently only invoked when a new constraint is created,
+ // see MPSolver::MakeRowConstraint().
+ // At this point we only have the lower and upper bounds of the
+ // constraint. We could immediately call SRSaddrows() here but it is
+ // usually much faster to handle the fully populated constraint in
+ // ExtractNewConstraints() right before the solve.
+ InvalidateModelSynchronization();
+ }
+
+ void SiriusInterface::AddVariable(MPVariable *const ct) {
+ // This is currently only invoked when a new variable is created,
+ // see MPSolver::MakeVar().
+ // At this point the variable does not appear in any constraints or
+ // the objective function. We could invoke SRSaddcols() to immediately
+ // create the variable here but it is usually much faster to handle the
+ // fully setup variable in ExtractNewVariables() right before the solve.
+ InvalidateModelSynchronization();
+ }
+
+ void SiriusInterface::SetCoefficient(MPConstraint *const constraint,
+ MPVariable const *const variable,
+ double new_value, double) {
+ InvalidateSolutionSynchronization();
+
+ fixedOrderCoefficientsPerConstraint[constraint->index()].push_back(std::make_pair(variable->index(), new_value));
+
+ // Changing a single coefficient in the matrix is potentially pretty
+ // slow since that coefficient has to be found in the sparse matrix
+ // representation. So by default we don't perform this update immediately
+ // but instead mark the low-level modeling object "out of sync".
+ // If we want to support incremental extraction then we MUST perform
+ // the modification immediately or we will lose it.
+
+ if (!supportIncrementalExtraction && !(slowUpdates & SlowSetCoefficient)) {
+ InvalidateModelSynchronization();
+ }
+ else {
+ int const row = constraint->index();
+ int const col = variable->index();
+ if (constraint_is_extracted(row) && variable_is_extracted(col)) {
+ // If row and column are both extracted then we can directly
+ // update the modeling object
+ DCHECK_LE(row, last_constraint_index_);
+ DCHECK_LE(col, last_variable_index_);
+ //FIXME CHECK_STATUS(SRSchgcoef(mLp, row, col, new_value));
+ }
+ else {
+ // If either row or column is not yet extracted then we can
+ // defer the update to ExtractModel()
+ InvalidateModelSynchronization();
+ }
+ }
+ }
+
+ void SiriusInterface::ClearConstraint(MPConstraint *const constraint) {
+ int const row = constraint->index();
+ if (!constraint_is_extracted(row))
+ // There is nothing to do if the constraint was not even extracted.
+ return;
+
+ // Clearing a constraint means setting all coefficients in the corresponding
+ // row to 0 (we cannot just delete the row since that would renumber all
+ // the constraints/rows after it).
+ // Modifying coefficients in the matrix is potentially pretty expensive
+ // since they must be found in the sparse matrix representation. That is
+ // why by default we do not modify the coefficients here but only mark
+ // the low-level modeling object "out of sync".
+
+ if (!(slowUpdates & SlowClearConstraint)) {
+ InvalidateModelSynchronization();
+ }
+ else {
+ InvalidateSolutionSynchronization();
+
+ int const len = constraint->coefficients_.size();
+ unique_ptr rowind(new int[len]);
+ unique_ptr colind(new int[len]);
+ unique_ptr val(new double[len]);
+ int j = 0;
+ const auto& coeffs = constraint->coefficients_;
+ for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
+ int const col = it->first->index();
+ if (variable_is_extracted(col)) {
+ rowind[j] = row;
+ colind[j] = col;
+ val[j] = 0.0;
+ ++j;
+ }
+ }
+ if (j) {
+ //FIXME CHECK_STATUS(SRSchgmcoef(mLp, j, rowind.get(), colind.get(), val.get()));
+ }
+ }
+ }
+
+ void SiriusInterface::SetObjectiveCoefficient(MPVariable const *const variable,
+ double coefficient) {
+ int const col = variable->index();
+ if (!variable_is_extracted(col))
+ // Nothing to do if variable was not even extracted
+ return;
+
+ InvalidateSolutionSynchronization();
+
+ // The objective function is stored as a dense vector, so updating a
+ // single coefficient is O(1). So by default we update the low-level
+ // modeling object here.
+ // If we support incremental extraction then we have no choice but to
+ // perform the update immediately.
+
+ if (supportIncrementalExtraction ||
+ (slowUpdates & SlowSetObjectiveCoefficient)) {
+ CHECK_STATUS(SRSchgobj(mLp, 1, &col, &coefficient));
+ }
+ else
+ InvalidateModelSynchronization();
+ }
+
+ void SiriusInterface::SetObjectiveOffset(double value) {
+ // Changing the objective offset is O(1), so we always do it immediately.
+ InvalidateSolutionSynchronization();
+ throw std::logic_error("Not implemented");
+ //FIXME CHECK_STATUS(SRSsetobjoffset(mLp, value));
+ }
+
+ void SiriusInterface::ClearObjective() {
+ InvalidateSolutionSynchronization();
+
+ // Since the objective function is stored as a dense vector updating
+ // it is O(n), so we usually perform the update immediately.
+ // If we want to support incremental extraction then we have no choice
+ // but to perform the update immediately.
+
+ if (supportIncrementalExtraction || (slowUpdates & SlowClearObjective)) {
+ int const cols = SRSgetnbcols(mLp);
+ unique_ptr ind(new int[cols]);
+ unique_ptr zero(new double[cols]);
+ int j = 0;
+ const auto& coeffs = solver_->objective_->coefficients_;
+ for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
+ int const idx = it->first->index();
+ // We only need to reset variables that have been extracted.
+ if (variable_is_extracted(idx)) {
+ DCHECK_LT(idx, cols);
+ ind[j] = idx;
+ zero[j] = 0.0;
+ ++j;
+ }
+ }
+ if (j > 0) CHECK_STATUS(SRSchgobj(mLp, j, ind.get(), zero.get()));
+ //FIXME CHECK_STATUS(SRSsetobjoffset(mLp, 0.0));
+ }
+ else
+ InvalidateModelSynchronization();
+ }
+
+ // ------ Query statistics on the solution and the solve ------
+
+ int64_t SiriusInterface::iterations() const {
+ int iter = 0;
+ if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfIterations;
+ CHECK_STATUS(SRSgetspxitercount(mLp, &iter));
+ return static_cast(iter);
+ }
+
+ int64_t SiriusInterface::nodes() const {
+ if (mMip) {
+ int nodes = 0;
+ if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfNodes;
+ CHECK_STATUS(SRSgetmipnodecount(mLp, &nodes));
+ return static_cast(nodes);
+ }
+ else {
+ LOG(DFATAL) << "Number of nodes only available for discrete problems";
+ return kUnknownNumberOfNodes;
+ }
+ }
+
+ // Transform a SIRIUS basis status to an MPSolver basis status.
+ MPSolver::BasisStatus SiriusInterface::xformBasisStatus(char sirius_basis_status) {
+ switch (sirius_basis_status) {
+ case SRS_AT_LOWER:
+ return MPSolver::AT_LOWER_BOUND;
+ case SRS_BASIC:
+ return MPSolver::BASIC;
+ case SRS_AT_UPPER:
+ return MPSolver::AT_UPPER_BOUND;
+ case SRS_FREE_LOWER:
+ case SRS_FREE_UPPER:
+ case SRS_FREE_ZERO:
+ case SRS_BASIC_FREE:
+ return MPSolver::FREE;
+ default:
+ LOG(DFATAL) << "Unknown SIRIUS basis status";
+ return MPSolver::FREE;
+ }
+ }
+
+ // Returns the basis status of a row.
+ MPSolver::BasisStatus SiriusInterface::row_status(int constraint_index) const {
+ if (mMip) {
+ LOG(FATAL) << "Basis status only available for continuous problems";
+ return MPSolver::FREE;
+ }
+
+ if (CheckSolutionIsSynchronized()) {
+ if (!mRstat) {
+ int const rows = SRSgetnbrows(mLp);
+ unique_ptr data(new char[rows]);
+ char * ptrToData = data.get();
+ mRstat.swap(data);
+ CHECK_STATUS(SRSgetrowbasisstatus(mLp, &ptrToData));
+ }
+ }
+ else
+ mRstat = 0;
+
+ if (mRstat) {
+ return xformBasisStatus(mRstat[constraint_index]);
+ }
+ else {
+ LOG(FATAL) << "Row basis status not available";
+ return MPSolver::FREE;
+ }
+ }
+
+ // Returns the basis status of a column.
+ MPSolver::BasisStatus SiriusInterface::column_status(int variable_index) const {
+ if (mMip) {
+ LOG(FATAL) << "Basis status only available for continuous problems";
+ return MPSolver::FREE;
+ }
+
+ if (CheckSolutionIsSynchronized()) {
+ if (!mCstat) {
+ int const cols = SRSgetnbcols(mLp);
+ unique_ptr data(new char[cols]);
+ char * ptrToData = data.get();
+ mCstat.swap(data);
+ CHECK_STATUS(SRSgetcolbasisstatus(mLp, &ptrToData));
+ }
+ }
+ else
+ mCstat = 0;
+
+ if (mCstat) {
+ return xformBasisStatus(mCstat[variable_index]);
+ }
+ else {
+ LOG(FATAL) << "Column basis status not available";
+ return MPSolver::FREE;
+ }
+ }
+
+ // Extract all variables that have not yet been extracted.
+ void SiriusInterface::ExtractNewVariables() {
+ // NOTE: The code assumes that a linear expression can never contain
+ // non-zero duplicates.
+
+ InvalidateSolutionSynchronization();
+
+ if (!supportIncrementalExtraction) {
+ // Without incremental extraction ExtractModel() is always called
+ // to extract the full model.
+ CHECK(last_variable_index_ == 0 ||
+ last_variable_index_ == solver_->variables_.size());
+ CHECK(last_constraint_index_ == 0 ||
+ last_constraint_index_ == solver_->constraints_.size());
+ }
+
+ int const last_extracted = last_variable_index_;
+ int const var_count = solver_->variables_.size();
+ int newcols = var_count - last_extracted;
+ if (newcols > 0) {
+ // There are non-extracted variables. Extract them now.
+
+ unique_ptr obj(new double[newcols]);
+ unique_ptr lb(new double[newcols]);
+ unique_ptr ub(new double[newcols]);
+ unique_ptr ctype(new int[newcols]);
+ unique_ptr colname(new const char *[newcols]);
+
+ bool have_names = false;
+ for (int j = 0, varidx = last_extracted; j < newcols; ++j, ++varidx) {
+ MPVariable const *const var = solver_->variables_[varidx];
+ lb[j] = var->lb();
+ ub[j] = var->ub();
+ ctype[j] = var->integer() ? SRS_INTEGER : SRS_CONTINUOUS;
+ colname[j] = var->name().empty() ? 0 : var->name().c_str();
+ have_names = have_names || var->name().empty();
+ obj[j] = solver_->objective_->GetCoefficient(var);
+ }
+
+ // Arrays for modifying the problem are setup. Update the index
+ // of variables that will get extracted now. Updating indices
+ // _before_ the actual extraction makes things much simpler in
+ // case we support incremental extraction.
+ // In case of error we just reset the indeces.
+ std::vector const &variables = solver_->variables();
+ for (int j = last_extracted; j < var_count; ++j) {
+ CHECK(!variable_is_extracted(variables[j]->index()));
+ set_variable_as_extracted(variables[j]->index(), true);
+ }
+
+ try {
+ bool use_newcols = true;
+
+ if (supportIncrementalExtraction) {
+ // If we support incremental extraction then we must
+ // update existing constraints with the new variables.
+ // To do that we use SRSaddcols() to actually create the
+ // variables. This is supposed to be faster than combining
+ // SRSnewcols() and SRSchgcoeflist().
+
+ // For each column count the size of the intersection with
+ // existing constraints.
+ unique_ptr collen(new int[newcols]);
+ for (int j = 0; j < newcols; ++j) collen[j] = 0;
+ int nonzeros = 0;
+ // TODO: Use a bitarray to flag the constraints that actually
+ // intersect new variables?
+ for (int i = 0; i < last_constraint_index_; ++i) {
+ MPConstraint const *const ct = solver_->constraints_[i];
+ CHECK(constraint_is_extracted(ct->index()));
+ const auto& coeffs = ct->coefficients_;
+ for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
+ int const idx = it->first->index();
+ if (variable_is_extracted(idx) && idx > last_variable_index_) {
+ collen[idx - last_variable_index_]++;
+ ++nonzeros;
+ }
+ }
+ }
+
+ if (nonzeros > 0) {
+ // At least one of the new variables did intersect with an
+ // old constraint. We have to create the new columns via
+ // SRSaddcols().
+ use_newcols = false;
+ unique_ptr begin(new int[newcols + 2]);
+ unique_ptr cmatind(new int[nonzeros]);
+ unique_ptr cmatval(new double[nonzeros]);
+
+ // Here is how cmatbeg[] is setup:
+ // - it is initialized as
+ // [ 0, 0, collen[0], collen[0]+collen[1], ... ]
+ // so that cmatbeg[j+1] tells us where in cmatind[] and
+ // cmatval[] we need to put the next nonzero for column
+ // j
+ // - after nonzeros have been setup the array looks like
+ // [ 0, collen[0], collen[0]+collen[1], ... ]
+ // so that it is the correct input argument for SRSaddcols
+ int *cmatbeg = begin.get();
+ cmatbeg[0] = 0;
+ cmatbeg[1] = 0;
+ ++cmatbeg;
+ for (int j = 0; j < newcols; ++j)
+ cmatbeg[j + 1] = cmatbeg[j] + collen[j];
+
+ for (int i = 0; i < last_constraint_index_; ++i) {
+ MPConstraint const *const ct = solver_->constraints_[i];
+ int const row = ct->index();
+ const auto& coeffs = ct->coefficients_;
+ for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
+ int const idx = it->first->index();
+ if (variable_is_extracted(idx) && idx > last_variable_index_) {
+ int const nz = cmatbeg[idx]++;
+ cmatind[nz] = row;
+ cmatval[nz] = it->second;
+ }
+ }
+ }
+ --cmatbeg;
+ //FIXME CHECK_STATUS(SRScreatecols(mLp, newcols, nonzeros, obj.get(), cmatbeg, cmatind.get(), cmatval.get(),lb.get(), ub.get()));
+ }
+ }
+
+ if (use_newcols) {
+ // Either incremental extraction is not supported or none of
+ // the new variables did intersect an existing constraint.
+ // We can just use SRSnewcols() to create the new variables.
+ std::vector collen(newcols, 0);
+ std::vector cmatbeg(newcols, 0);
+ unique_ptr cmatind(new int[1]);
+ unique_ptr cmatval(new double[1]);
+ cmatind[0] = 0;
+ cmatval[0] = 1.0;
+
+ CHECK_STATUS(
+ SRScreatecols(mLp, newcols, obj.get(), ctype.get(), lb.get(), ub.get(), colname.get())
+ );
+ //int const cols = SRSgetnbcols(mLp);
+ //unique_ptr ind(new int[newcols]);
+ //for (int j = 0; j < cols; ++j)
+ // ind[j] = j;
+ //CHECK_STATUS(
+ // SRSchgcoltype(mLp, cols - last_extracted, ind.get(), ctype.get())
+ //);
+ }
+ else {
+ // Incremental extraction: we must update the ctype of the
+ // newly created variables (SRSaddcols() does not allow
+ // specifying the ctype)
+ if (mMip && SRSgetnbcols(mLp) > 0) {
+ // Query the actual number of columns in case we did not
+ // manage to extract all columns.
+ int const cols = SRSgetnbcols(mLp);
+ unique_ptr ind(new int[newcols]);
+ for (int j = last_extracted; j < cols; ++j)
+ ind[j - last_extracted] = j;
+ //FIXME CHECK_STATUS(SRSchgcoltype(mLp, cols - last_extracted, ind.get(),ctype.get()));
+ }
+ }
+ }
+ catch (...) {
+ // Undo all changes in case of error.
+ int const cols = SRSgetnbcols(mLp);
+ if (cols > last_extracted)
+ {
+ std::vector colsToDelete;
+ for (int i = last_extracted; i < cols; ++i)
+ colsToDelete.push_back(i);
+ //FIXME (void)SRSdelcols(mLp, colsToDelete.size(), colsToDelete.data());
+ }
+ std::vector const &variables = solver_->variables();
+ int const size = variables.size();
+ for (int j = last_extracted; j < size; ++j)
+ set_variable_as_extracted(j, false);
+ throw;
+ }
+ }
+ }
+
+ // Extract constraints that have not yet been extracted.
+ void SiriusInterface::ExtractNewConstraints() {
+ // NOTE: The code assumes that a linear expression can never contain
+ // non-zero duplicates.
+ if (!supportIncrementalExtraction) {
+ // Without incremental extraction ExtractModel() is always called
+ // to extract the full model.
+ CHECK(last_variable_index_ == 0 ||
+ last_variable_index_ == solver_->variables_.size());
+ CHECK(last_constraint_index_ == 0 ||
+ last_constraint_index_ == solver_->constraints_.size());
+ }
+
+ int const offset = last_constraint_index_;
+ int const total = solver_->constraints_.size();
+
+ if (total > offset) {
+ // There are constraints that are not yet extracted.
+
+ InvalidateSolutionSynchronization();
+
+ int newCons = total - offset;
+ int const cols = SRSgetnbcols(mLp);
+ //DCHECK_EQ(last_variable_index_, cols);
+
+ // Update indices of new constraints _before_ actually extracting
+ // them. In case of error we will just reset the indices.
+ for (int c = offset; c < total; ++c)
+ set_constraint_as_extracted(c, true);
+
+ try {
+ int nbTerms = 0;
+ for (int c = 0; c < newCons; ++c) {
+ MPConstraint const *const ct = solver_->constraints_[offset + c];
+ nbTerms += ct->coefficients_.size();
+ }
+ unique_ptr rmatbeg(new int[newCons]);
+ unique_ptr rmatrownbterms(new int[newCons]);
+ unique_ptr rmatind(new int[nbTerms]);
+ unique_ptr rmatval(new double[nbTerms]);
+
+ unique_ptr sense(new char[newCons]);
+ unique_ptr rhs(new double[newCons]);
+ unique_ptr name(new char const *[newCons]);
+ unique_ptr rngval(new double[newCons]);
+ unique_ptr rngind(new int[newCons]);
+ bool haveRanges = false;
+
+ // Loop over the new constraints, collecting rows for up to
+ // CHUNK constraints into the arrays so that adding constraints
+ // is faster.
+ int nextNz = 0;
+ for (int c = 0; c < newCons; ++c) {
+ // Collect up to CHUNK constraints into the arrays.
+ MPConstraint const *const ct = solver_->constraints_[offset + c];
+
+ // Setup right-hand side of constraint.
+ MakeRhs(ct->lb(), ct->ub(), rhs[c], sense[c],
+ rngval[c]);
+ haveRanges = haveRanges || (rngval[c] != 0.0);
+ rngind[c] = offset + c;
+
+ // Setup left-hand side of constraint.
+ rmatbeg[c] = nextNz;
+ //const auto& coeffs = ct->coefficients_;
+ const auto& coeffs = fixedOrderCoefficientsPerConstraint[ct->index()];
+ for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
+ int const idxVar = it->first;
+ if (variable_is_extracted(idxVar)) {
+ //DCHECK_LT(nextNz, cols);
+ //DCHECK_LT(idxVar, cols);
+ rmatind[nextNz] = idxVar;
+ rmatval[nextNz] = it->second;
+ ++nextNz;
+ }
+ }
+ rmatrownbterms[c] = nextNz - rmatbeg[c];
+ //std::cout
+ // << c << " "
+ // << rmatbeg[c] << " "
+ // << rmatrownbterms[c] << " "
+ // << rmatind[c] << " "
+ // << rmatval[c] << " "
+ // << std::endl;
+
+ // Finally the name of the constraint.
+ name[c] = ct->name().empty() ? NULL : ct->name().c_str();
+ }
+
+ CHECK_STATUS(
+ //SRSaddrows(mLp, nextRow, nextNz, sense.get(), rhs.get(), rngval.get(), rmatbeg.get(), rmatind.get(), rmatval.get())
+ SRScreaterows(mLp, newCons, rhs.get(), rngval.get(), sense.get(), name.get())
+ );
+ SRSsetcoefs(mLp, rmatbeg.get(), rmatrownbterms.get(), rmatind.get(), rmatval.get());
+ }
+ catch (...) {
+ // Undo all changes in case of error.
+ int const rows = SRSgetnbrows(mLp);
+ std::vector rowsToDelete;
+ for (int i = offset; i < rows; ++i)
+ rowsToDelete.push_back(i);
+ //FIXME if (rows > offset) (void)SRSdelrows(mLp, rowsToDelete.size(), rowsToDelete.data());
+ std::vector const &constraints = solver_->constraints();
+ int const size = constraints.size();
+ for (int i = offset; i < size; ++i) set_constraint_as_extracted(i, false);
+ throw;
+ }
+ }
+ }
+
+ // Extract the objective function.
+ void SiriusInterface::ExtractObjective() {
+ // NOTE: The code assumes that the objective expression does not contain
+ // any non-zero duplicates.
+
+ int const cols = SRSgetnbcols(mLp);
+ //DCHECK_EQ(last_variable_index_, cols);
+
+ unique_ptr ind(new int[cols]);
+ unique_ptr val(new double[cols]);
+ for (int j = 0; j < cols; ++j) {
+ ind[j] = j;
+ val[j] = 0.0;
+ }
+
+ const auto& coeffs = solver_->objective_->coefficients_;
+ for (auto it = coeffs.begin(); it != coeffs.end(); ++it) {
+ int const idx = it->first->index();
+ if (variable_is_extracted(idx)) {
+ //DCHECK_LT(idx, cols);
+ val[idx] = it->second;
+ }
+ }
+
+ CHECK_STATUS(SRSchgobj(mLp, cols, ind.get(), val.get()));
+ //FIXME CHECK_STATUS(SRSsetobjoffset(mLp, solver_->Objective().offset()));
+ }
+
+ // ------ Parameters -----
+
+ // WIP : Use SetSolverSpecificParametersAsString to pass TypeDeBorneDeLaVariable
+ bool SiriusInterface::SetSolverSpecificParametersAsString(const std::string& parameters)
+ {
+ std::stringstream ss(parameters);
+ std::string paramName;
+ std::getline(ss, paramName, ' ');
+ if (paramName == "VAR_BOUNDS_TYPE") {
+ std::string paramValue;
+ varBoundsTypeValues.clear();
+ while (std::getline(ss, paramValue, ' ')) {
+ varBoundsTypeValues.push_back(std::stoi(paramValue));
+ }
+ return true;
+ }
+ else {
+ // unknow paramName
+ return false;
+ }
+ }
+
+ void SiriusInterface::SetParameters(const MPSolverParameters ¶m) {
+ SetCommonParameters(param);
+ if (mMip) SetMIPParameters(param);
+ }
+
+ void SiriusInterface::SetRelativeMipGap(double value) {
+ if (mMip) {
+ LOG(WARNING) << "SetRelativeMipGap not implemented for sirius_interface";
+ //FIXME CHECK_STATUS(SRSsetdblcontrol(mLp, SRS_MIPRELSTOP, value));
+ }
+ else {
+ LOG(WARNING) << "The relative MIP gap is only available "
+ << "for discrete problems.";
+ }
+ }
+
+ void SiriusInterface::SetPrimalTolerance(double value) {
+ LOG(WARNING) << "SetPrimalTolerance not implemented for sirius_interface";
+ //FIXME CHECK_STATUS(SRSsetdblcontrol(mLp, SRS_FEASTOL, value));
+ }
+
+ void SiriusInterface::SetDualTolerance(double value) {
+ LOG(WARNING) << "SetDualTolerance not implemented for sirius_interface";
+ //FIXME CHECK_STATUS(SRSsetdblcontrol(mLp, SRS_OPTIMALITYTOL, value));
+ }
+
+ void SiriusInterface::SetPresolveMode(int value) {
+ MPSolverParameters::PresolveValues const presolve =
+ static_cast(value);
+
+ switch (presolve) {
+ case MPSolverParameters::PRESOLVE_OFF:
+ SRSsetintparams(mLp, SRS_PARAM_PRESOLVE, 0);
+ return;
+ case MPSolverParameters::PRESOLVE_ON:
+ SRSsetintparams(mLp, SRS_PARAM_PRESOLVE, 1);
+ return;
+ }
+ SetIntegerParamToUnsupportedValue(MPSolverParameters::PRESOLVE, value);
+ }
+
+ // Sets the scaling mode.
+ void SiriusInterface::SetScalingMode(int value) {
+ MPSolverParameters::ScalingValues const scaling =
+ static_cast(value);
+
+ switch (scaling) {
+ case MPSolverParameters::SCALING_OFF:
+ LOG(WARNING) << "SetScalingMode not implemented for sirius_interface";
+ //FIXME CHECK_STATUS(SRSsetintcontrol(mLp, SRS_SCALING, 0));
+ break;
+ case MPSolverParameters::SCALING_ON:
+ LOG(WARNING) << "SetScalingMode not implemented for sirius_interface";
+ //FIXME CHECK_STATUS(SRSsetintcontrol(mLp, SRS_SCALING, 1));
+ break;
+ }
+ }
+
+ // Sets the LP algorithm : primal, dual or barrier. Note that SIRIUS offers other
+ // LP algorithm (e.g. network) and automatic selection
+ void SiriusInterface::SetLpAlgorithm(int value) {
+ MPSolverParameters::LpAlgorithmValues const algorithm =
+ static_cast(value);
+
+ int alg = 1;
+
+ switch (algorithm) {
+ case MPSolverParameters::DUAL:
+ alg = 2;
+ break;
+ case MPSolverParameters::PRIMAL:
+ alg = 3;
+ break;
+ case MPSolverParameters::BARRIER:
+ alg = 4;
+ break;
+ }
+
+ if (alg == 1)
+ SetIntegerParamToUnsupportedValue(MPSolverParameters::LP_ALGORITHM, value);
+ else {
+ LOG(WARNING) << "SetLpAlgorithm not implemented for sirius_interface";
+ //FIXME CHECK_STATUS(SRSsetintcontrol(mLp, SRS_DEFAULTALG, alg));
+ }
+ }
+
+ bool SiriusInterface::ReadParameterFile(std::string const &filename) {
+ // Return true on success and false on error.
+ return true;
+ // LOG(DFATAL) << "ReadParameterFile not implemented for Sirius interface";
+ // return false;
+ }
+
+ absl::Status SiriusInterface::SetNumThreads(int num_threads)
+ {
+ // sirius does not support mt
+ LOG(WARNING) << "SetNumThreads: sirius does not support multithreading";
+ return absl::OkStatus();
+ }
+
+ std::string SiriusInterface::ValidFileExtensionForParameterFile() const {
+ return ".prm";
+ }
+
+ MPSolver::ResultStatus SiriusInterface::Solve(MPSolverParameters const ¶m) {
+ int status;
+
+ // Delete chached information
+ mCstat = 0;
+ mRstat = 0;
+
+ WallTimer timer;
+ timer.Start();
+
+ // Set incrementality
+ MPSolverParameters::IncrementalityValues const inc =
+ static_cast(
+ param.GetIntegerParam(MPSolverParameters::INCREMENTALITY));
+ switch (inc) {
+ case MPSolverParameters::INCREMENTALITY_OFF:
+ Reset(); /* This should not be required but re-extracting everything
+ * may be faster, so we do it. */
+ break;
+ case MPSolverParameters::INCREMENTALITY_ON:
+ //FIXME SRSsetintcontrol(mLp, SRS_CRASH, 0);
+ break;
+ }
+
+ // Extract the model to be solved.
+ // If we don't support incremental extraction and the low-level modeling
+ // is out of sync then we have to re-extract everything. Note that this
+ // will lose MIP starts or advanced basis information from a previous
+ // solve.
+ if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset();
+ ExtractModel();
+ VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get());
+
+ // Set log level.
+ CHECK_STATUS(SRSsetintparams(mLp, SRS_PARAM_VERBOSE_SPX, quiet() ? 0 : 1));
+ CHECK_STATUS(SRSsetintparams(mLp, SRS_PARAM_VERBOSE_PNE, quiet() ? 0 : 1));
+
+ // Set parameters.
+ // NOTE: We must invoke SetSolverSpecificParametersAsString() _first_.
+ // Its current implementation invokes ReadParameterFile() which in
+ // turn invokes SRSreadcopyparam(). The latter will _overwrite_
+ // all current parameter settings in the environment.
+ solver_->SetSolverSpecificParametersAsString(solver_->solver_specific_parameter_string_);
+ SetParameters(param);
+ if (solver_->time_limit()) {
+ VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
+ CHECK_STATUS(SRSsetdoubleparams(mLp, SRS_PARAM_MAX_TIME, solver_->time_limit() * 1e-3));
+ }
+
+ // Solve.
+ // Do not CHECK_STATUS here since some errors (for example CPXERR_NO_MEMORY)
+ // still allow us to query useful information.
+ timer.Restart();
+ int problemStatus = 0;
+ if (maximize_) {
+ SRSsetintparams(mLp, SRS_PARAM_MAXIMIZE, 1);
+ }
+ else {
+ SRSsetintparams(mLp, SRS_PARAM_MAXIMIZE, 0);
+ }
+
+ //std::cout
+ // << "nbVar " << mLp->problem_mps->NbVar << std::endl
+ // << "nbRow " << mLp->problem_mps->NbCnt << std::endl;
+ //
+ //std::cout << "Xmins ";
+ //for (int i = 0; i < mLp->problem_mps->NbVar; ++i)
+ // std::cout << mLp->problem_mps->Umin[i] << " ";
+ //std::cout << std::endl;
+ //
+ //std::cout << "Xmaxs ";
+ //for (int i = 0; i < mLp->problem_mps->NbVar; ++i)
+ // std::cout << mLp->problem_mps->Umax[i] << " ";
+ //std::cout << std::endl;
+ //
+ //std::cout << "rhs" << std::endl;
+ //for (int i = 0; i < mLp->problem_mps->NbCnt; ++i)
+ // std::cout << mLp->problem_mps->SensDeLaContrainte[i] << " " << mLp->problem_mps->Rhs[i] << std::endl;
+ //
+ //exit(0);
+
+ // set variables's bound's type if any
+ if (!varBoundsTypeValues.empty()) {
+ SRScopyvarboundstype(mLp, varBoundsTypeValues.data());
+ }
+
+ // set solution hints if any
+ if (!solver_->solution_hint_.empty()) {
+ // store X values
+ for (std::pair& solution_hint_elt : solver_->solution_hint_) {
+ SRSsetxvalue(mLp, solution_hint_elt.first->index(), solution_hint_elt.second);
+ }
+
+ }
+ if (IsMIP())
+ SRSsetintparams(mLp, SRS_FORCE_PNE, 1);
+
+ status = SRSoptimize(mLp);
+
+ if (status) {
+ VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status);
+ // NOTE: We do not return immediately since there may be information
+ // to grab (for example an incumbent)
+ }
+ else {
+ VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
+ }
+
+ problemStatus = SRSgetproblemstatus(mLp);
+ VLOG(1) << absl::StrFormat("SIRIUS solution status %d.", problemStatus);
+
+ // Figure out what solution we have.
+ bool const feasible = !(problemStatus == SRS_STATUS_UNFEASIBLE);
+
+ // Get problem dimensions for solution queries below.
+ int const rows = SRSgetnbrows(mLp);
+ int const cols = SRSgetnbcols(mLp);
+ DCHECK_EQ(rows, solver_->constraints_.size());
+ DCHECK_EQ(cols, solver_->variables_.size());
+
+ // Capture objective function value.
+ objective_value_ = SRS_NAN;
+ if (feasible) {
+ (SRSgetobjval(mLp, &objective_value_));
+ objective_value_ += solver_->Objective().offset();
+ }
+ VLOG(1) << "objective = " << objective_value_;
+
+ // Capture primal and dual solutions
+ if (mMip) {
+ // If there is a primal feasible solution then capture it.
+ if (feasible) {
+ if (cols > 0) {
+ double * x = new double[cols];
+ CHECK_STATUS(SRSgetx(mLp, &x));
+ for (int i = 0; i < solver_->variables_.size(); ++i) {
+ MPVariable *const var = solver_->variables_[i];
+ var->set_solution_value(x[i]);
+ VLOG(3) << var->name() << ": value =" << x[i];
+ }
+ delete[] x;
+ }
+ }
+ else {
+ for (int i = 0; i < solver_->variables_.size(); ++i)
+ solver_->variables_[i]->set_solution_value(SRS_NAN);
+ }
+
+ // MIP does not have duals
+ for (int i = 0; i < solver_->variables_.size(); ++i)
+ solver_->variables_[i]->set_reduced_cost(SRS_NAN);
+ unique_ptr pi(new double[rows]);
+ if (feasible) {
+ double * dualValues = pi.get();
+ CHECK_STATUS(SRSgetdualvalues(mLp, &dualValues));
+ }
+ for (int i = 0; i < solver_->constraints_.size(); ++i) {
+ MPConstraint *const ct = solver_->constraints_[i];
+ bool dual = false;
+ if (feasible) {
+ ct->set_dual_value(pi[i]);
+ dual = true;
+ }
+ else
+ ct->set_dual_value(SRS_NAN);
+ VLOG(4) << "row " << ct->index() << ":"
+ << (dual ? absl::StrFormat(" dual = %f", pi[i]) : "");
+ }
+ }
+ else {
+ // Continuous problem.
+ if (cols > 0) {
+ double * dj = new double[cols];
+ double * x = new double[cols];
+ CHECK_STATUS(SRSgetx(mLp, &x));
+
+ if (feasible) {
+ CHECK_STATUS(SRSgetreducedcosts(mLp, &dj));
+ }
+ for (int i = 0; i < solver_->variables_.size(); ++i) {
+ MPVariable *const var = solver_->variables_[i];
+ var->set_solution_value(x[i]);
+ bool value = false, dual = false;
+
+ if (feasible) {
+ var->set_solution_value(x[i]);
+ value = true;
+ }
+ else
+ var->set_solution_value(SRS_NAN);
+ if (feasible) {
+ var->set_reduced_cost(dj[i]);
+ dual = true;
+ }
+ else
+ var->set_reduced_cost(SRS_NAN);
+ VLOG(3) << var->name() << ":"
+ << (value ? absl::StrFormat(" value = %f", x[i]) : "")
+ << (dual ? absl::StrFormat(" reduced cost = %f", dj[i]) : "");
+ }
+ delete[] x;
+ delete[] dj;
+ }
+
+ if (rows > 0) {
+ unique_ptr pi(new double[rows]);
+ if (feasible) {
+ double * dualValues = pi.get();
+ CHECK_STATUS(SRSgetdualvalues(mLp, &dualValues));
+ }
+ for (int i = 0; i < solver_->constraints_.size(); ++i) {
+ MPConstraint *const ct = solver_->constraints_[i];
+ bool dual = false;
+ if (feasible) {
+ ct->set_dual_value(pi[i]);
+ dual = true;
+ }
+ else
+ ct->set_dual_value(SRS_NAN);
+ VLOG(4) << "row " << ct->index() << ":"
+ << (dual ? absl::StrFormat(" dual = %f", pi[i]) : "");
+ }
+ }
+ }
+
+ // Map SIRIUS status to more generic solution status in MPSolver
+ switch (problemStatus) {
+ case SRS_STATUS_OPTIMAL:
+ result_status_ = MPSolver::OPTIMAL;
+ break;
+ case SRS_STATUS_UNFEASIBLE:
+ result_status_ = MPSolver::INFEASIBLE;
+ break;
+ case SRS_STATUS_UNBOUNDED:
+ result_status_ = MPSolver::UNBOUNDED;
+ break;
+ default:
+ result_status_ = feasible ? MPSolver::FEASIBLE : MPSolver::ABNORMAL;
+ break;
+ }
+
+ sync_status_ = SOLUTION_SYNCHRONIZED;
+ return result_status_;
+ }
+
+ void SiriusInterface::Write(const std::string& filename) {
+ if (sync_status_ == MUST_RELOAD) {
+ Reset();
+ }
+ ExtractModel();
+ VLOG(1) << "Writing Sirius MPS \"" << filename << "\".";
+ const int status = SRSwritempsprob(mLp->problem_mps, filename.c_str());
+ if (status) {
+ LOG(ERROR) << "Sirius: Failed to write MPS!";
+ }
+ }
+
+ MPSolverInterface *BuildSiriusInterface(bool mip, MPSolver *const solver) {
+ return new SiriusInterface(solver, mip);
+ }
+
+} // namespace operations_research
+#endif // #if defined(USE_SIRUS)
diff --git a/ortools/linear_solver/sirius_interface_test.cc b/ortools/linear_solver/sirius_interface_test.cc
new file mode 100644
index 0000000..081c400
--- /dev/null
+++ b/ortools/linear_solver/sirius_interface_test.cc
@@ -0,0 +1,883 @@
+//#include "ortools/linear_solver/sirius_interface.cc"
+#include "ortools/linear_solver/linear_solver.h"
+#include "gtest/gtest.h"
+extern "C" {
+#include "srs_api.h"
+}
+#include
+
+namespace operations_research {
+
+ class SRSGetter {
+ public:
+ SRSGetter(MPSolver* solver) : solver_(solver) {}
+
+ bool isMip() {
+ return prob()->is_mip;
+ }
+
+ int getNumVariables() {
+ return SRSgetnbcols(prob());
+ }
+
+ std::string getVariableName(int n) {
+ return prob()->problem_mps->LabelDeLaVariable[n];
+ }
+
+ int getNumConstraints() { return SRSgetnbrows(prob()); }
+
+ std::string getConstraintName(int n) {
+ return prob()->problem_mps->LabelDeLaContrainte[n];
+ }
+
+ double getLb(int n) {
+ EXPECT_LT(n, getNumVariables());
+ return prob()->problem_mps->Umin[n];
+ }
+
+ double getUb(int n) {
+ EXPECT_LT(n, getNumVariables());
+ return prob()->problem_mps->Umax[n];
+ }
+
+ int getVariableType(int n) {
+ EXPECT_LT(n, getNumVariables());
+ return prob()->problem_mps->TypeDeVariable[n];
+ }
+
+ char getConstraintType(int n) {
+ EXPECT_LT(n, getNumConstraints());
+ return prob()->problem_mps->SensDeLaContrainte[n];
+ }
+
+ double getConstraintRhs(int n) {
+ EXPECT_LT(n, getNumConstraints());
+ return prob()->problem_mps->B[n];
+ }
+
+ double getConstraintCoef(int row, int col) {
+ EXPECT_LT(col, getNumVariables());
+ EXPECT_LT(row, getNumConstraints());
+ PROBLEME_MPS* problem_mps = prob()->problem_mps;
+ int rowBeg = problem_mps->Mdeb[row];
+ for (int i = 0; i < problem_mps->NbTerm[row]; ++i) {
+ if (problem_mps->Nuvar[rowBeg + i] == col) {
+ return problem_mps->A[rowBeg + i];
+ }
+ }
+ return 0.;
+ }
+
+ double getObjectiveCoef(int n) {
+ EXPECT_LT(n, getNumVariables());
+ return prob()->problem_mps->L[n];
+ }
+
+ bool getObjectiveSense() {
+ return prob()->maximize;
+ }
+
+ int getPresolve() {
+ return prob()->presolve;
+ }
+
+ int getScaling() {
+ return prob()->scaling;
+ }
+
+ double getRelativeMipGap() {
+ return prob()->relativeGap;
+ }
+
+ int getVarBoundType(int n) {
+ EXPECT_LT(n, getNumVariables());
+ return prob()->problem_mps->TypeDeBorneDeLaVariable[n];
+ }
+
+ private:
+ MPSolver* solver_;
+
+ SRS_PROBLEM* prob() {
+ return (SRS_PROBLEM*)solver_->underlying_solver();
+ }
+ };
+
+#define UNITTEST_INIT_MIP() \
+ MPSolver solver("SIRIUS_MIP", MPSolver::SIRIUS_MIXED_INTEGER_PROGRAMMING);\
+ SRSGetter getter(&solver)
+#define UNITTEST_INIT_LP() \
+ MPSolver solver("SIRIUS_LP", MPSolver::SIRIUS_LINEAR_PROGRAMMING);\
+ SRSGetter getter(&solver)
+
+ void _unittest_verify_var(SRSGetter* getter, MPVariable* x, int type, double lb, double ub) {
+ EXPECT_EQ(getter->getVariableType(x->index()), type);
+ EXPECT_EQ(getter->getLb(x->index()), lb);
+ EXPECT_EQ(getter->getUb(x->index()), ub);
+ }
+
+ void _unittest_verify_constraint(SRSGetter* getter, MPConstraint* c, char type, double lb, double ub) {
+ int idx = c->index();
+ EXPECT_EQ(getter->getConstraintType(idx), type);
+ switch (type) {
+ case SRS_LESSER_THAN:
+ EXPECT_EQ(getter->getConstraintRhs(idx), ub);
+ break;
+ case SRS_GREATER_THAN:
+ EXPECT_EQ(getter->getConstraintRhs(idx), lb);
+ break;
+ case SRS_EQUAL:
+ EXPECT_EQ(getter->getConstraintRhs(idx), ub);
+ EXPECT_EQ(getter->getConstraintRhs(idx), lb);
+ break;
+ }
+ }
+
+ TEST(TestSiriusInterface, isMIP) {
+ UNITTEST_INIT_MIP();
+ EXPECT_EQ(solver.IsMIP(), true);
+ }
+
+ TEST(TestSiriusInterface, isLP) {
+ UNITTEST_INIT_LP();
+ EXPECT_EQ(solver.IsMIP(), false);
+ }
+
+ TEST(TestSiriusInterface, NumVariables) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPVariable* x1 = solver.MakeNumVar(-1., 5.1, "x1");
+ MPVariable* x2 = solver.MakeNumVar(3.14, 5.1, "x2");
+ std::vector xs;
+ solver.MakeBoolVarArray(500, "xs", &xs);
+ solver.Solve();
+ EXPECT_EQ(getter.getNumVariables(), 502);
+ }
+
+ TEST(TestSiriusInterface, VariablesName) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ std::string pi("Pi");
+ std::string secondVar("Name");
+ MPVariable* x1 = solver.MakeNumVar(-1., 5.1, pi);
+ MPVariable* x2 = solver.MakeNumVar(3.14, 5.1, secondVar);
+ solver.Solve();
+ EXPECT_EQ(getter.getVariableName(0), pi);
+ EXPECT_EQ(getter.getVariableName(1), secondVar);
+ }
+
+ TEST(TestSiriusInterface, NumConstraints) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(100.0, 100.0);
+ solver.MakeRowConstraint(-solver.infinity(), 13.1);
+ solver.MakeRowConstraint(12.1, solver.infinity());
+ solver.Solve();
+ EXPECT_EQ(getter.getNumConstraints(), 3);
+ }
+
+ TEST(TestSiriusInterface, ConstraintsName) {
+ UNITTEST_INIT_MIP();
+
+ std::string phi("Phi");
+ std::string otherCnt("constraintName");
+ solver.MakeRowConstraint(100.0, 100.0, phi);
+ solver.MakeRowConstraint(-solver.infinity(), 13.1, otherCnt);
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintName(0), phi);
+ EXPECT_EQ(getter.getConstraintName(1), otherCnt);
+ }
+
+ TEST(TestSiriusInterface, Reset) {
+ UNITTEST_INIT_MIP();
+ solver.MakeBoolVar("x1");
+ solver.MakeBoolVar("x2");
+ solver.MakeRowConstraint(-solver.infinity(), 100.0);
+ solver.Solve();
+ EXPECT_EQ(getter.getNumConstraints(), 1);
+ EXPECT_EQ(getter.getNumVariables(), 2);
+ solver.Reset();
+ EXPECT_EQ(getter.getNumConstraints(), 0);
+ EXPECT_EQ(getter.getNumVariables(), 0);
+ }
+
+ TEST(TestSiriusInterface, MakeIntVar) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ int lb = 0, ub = 10;
+ MPVariable* x = solver.MakeIntVar(lb, ub, "x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, SRS_INTEGER_VAR, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, MakeNumVar) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ double lb = 1.5, ub = 158.2;
+ MPVariable* x = solver.MakeNumVar(lb, ub, "x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, SRS_CONTINUOUS_VAR, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, MakeBoolVar) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPVariable* x = solver.MakeBoolVar("x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, SRS_INTEGER_VAR, 0, 1);
+ }
+
+ TEST(TestSiriusInterface, MakeIntVarArray) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ int n1 = 25, lb1 = -7, ub1 = 18;
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+ std::vector xs1;
+ solver.MakeIntVarArray(n1, lb1, ub1, "xs1", &xs1);
+ int n2 = 37, lb2 = 19, ub2 = 189;
+ std::vector xs2;
+ solver.MakeIntVarArray(n2, lb2, ub2, "xs2", &xs2);
+ solver.Solve();
+ for (int i = 0; i < n1; ++i) {
+ _unittest_verify_var(&getter, xs1[i], SRS_INTEGER_VAR, lb1, ub1);
+ }
+ for (int i = 0; i < n2; ++i) {
+ _unittest_verify_var(&getter, xs2[i], SRS_INTEGER_VAR, lb2, ub2);
+ }
+ }
+
+ TEST(TestSiriusInterface, MakeNumVarArray) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ int n1 = 1;
+ double lb1 = 5.1, ub1 = 8.1;
+ std::vector xs1;
+ solver.MakeNumVarArray(n1, lb1, ub1, "xs1", &xs1);
+ int n2 = 13;
+ double lb2 = -11.5, ub2 = 189.9;
+ std::vector xs2;
+ solver.MakeNumVarArray(n2, lb2, ub2, "xs2", &xs2);
+ solver.Solve();
+ for (int i = 0; i < n1; ++i) {
+ _unittest_verify_var(&getter, xs1[i], SRS_CONTINUOUS_VAR, lb1, ub1);
+ }
+ for (int i = 0; i < n2; ++i) {
+ _unittest_verify_var(&getter, xs2[i], SRS_CONTINUOUS_VAR, lb2, ub2);
+ }
+ }
+
+ TEST(TestSiriusInterface, MakeBoolVarArray) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ double n = 43;
+ std::vector xs;
+ solver.MakeBoolVarArray(n, "xs", &xs);
+ solver.Solve();
+ for (int i = 0; i < n; ++i) {
+ _unittest_verify_var(&getter, xs[i], SRS_INTEGER_VAR, 0, 1);
+ }
+ }
+
+ TEST(TestSiriusInterface, SetVariableBounds) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ int lb1 = 3, ub1 = 4;
+ MPVariable* x1 = solver.MakeIntVar(lb1, ub1, "x1");
+ double lb2 = 3.7, ub2 = 4;
+ MPVariable* x2 = solver.MakeNumVar(lb2, ub2, "x2");
+ solver.Solve();
+ _unittest_verify_var(&getter, x1, SRS_INTEGER_VAR, lb1, ub1);
+ _unittest_verify_var(&getter, x2, SRS_CONTINUOUS_VAR, lb2, ub2);
+ lb1 = 12, ub1 = 15;
+ x1->SetBounds(lb1, ub1);
+ lb2 = -1.1, ub2 = 0;
+ x2->SetBounds(lb2, ub2);
+ solver.Solve();
+ _unittest_verify_var(&getter, x1, SRS_INTEGER_VAR, lb1, ub1);
+ _unittest_verify_var(&getter, x2, SRS_CONTINUOUS_VAR, lb2, ub2);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SetVariableInteger) {
+ // Here we test a badly definied behaviour
+ // depending on the sirius version the sirius-workflow breaks at:
+ // either the call of x->SetInteger(false) like the test suggest
+ // or at solver.Solve() because integer variables are not supported
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ int lb = -1, ub = 7;
+ MPVariable* x = solver.MakeIntVar(lb, ub, "x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, SRS_INTEGER_VAR, lb, ub);
+ EXPECT_THROW(x->SetInteger(false), std::logic_error);
+ }
+
+ TEST(TestSiriusInterface, ConstraintL) {
+ UNITTEST_INIT_MIP();
+ double lb = -solver.infinity(), ub = 10.;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_LESSER_THAN, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, ConstraintR) {
+ UNITTEST_INIT_MIP();
+ double lb = -2, ub = -1;
+ solver.MakeRowConstraint(lb, ub);
+ EXPECT_THROW(solver.Solve(), std::logic_error);
+ }
+
+ TEST(TestSiriusInterface, ConstraintG) {
+ UNITTEST_INIT_MIP();
+ double lb = 8.1, ub = solver.infinity();
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_GREATER_THAN, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, ConstraintE) {
+ UNITTEST_INIT_MIP();
+ double lb = 18947.3, ub = lb;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_EQUAL, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, SetConstraintBoundsL) {
+ UNITTEST_INIT_MIP();
+ double lb = 18947.3, ub = lb;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_EQUAL, lb, ub);
+ lb = -solver.infinity(), ub = 16.6;
+ c->SetBounds(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_LESSER_THAN, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, SetConstraintBoundsG) {
+ UNITTEST_INIT_MIP();
+ double lb = 18947.3, ub = lb;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_EQUAL, lb, ub);
+ lb = 5, ub = solver.infinity();
+ c->SetBounds(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_GREATER_THAN, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, SetConstraintBoundsE) {
+ UNITTEST_INIT_MIP();
+ double lb = -1, ub = solver.infinity();
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_GREATER_THAN, lb, ub);
+ lb = 128, ub = lb;
+ c->SetBounds(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, SRS_EQUAL, lb, ub);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_ConstraintCoef) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x1 = solver.MakeBoolVar("x1");
+ MPVariable* x2 = solver.MakeBoolVar("x2");
+ MPConstraint* c1 = solver.MakeRowConstraint(4.1, solver.infinity());
+ MPConstraint* c2 = solver.MakeRowConstraint(-solver.infinity(), 0.1);
+ double c11 = -15.6, c12 = 0.4, c21 = -11, c22 = 4.5;
+ c1->SetCoefficient(x1, c11);
+ c1->SetCoefficient(x2, c12);
+ c2->SetCoefficient(x1, c21);
+ c2->SetCoefficient(x2, c22);
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), c11);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), c12);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), c21);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), c22);
+
+ // Next part causes sirius to crash ("free(): invalid next size (fast)")
+ c11 = 0.11, c12 = 0.12, c21 = 0.21, c22 = 0.22;
+ c1->SetCoefficient(x1, c11);
+ c1->SetCoefficient(x2, c12);
+ c2->SetCoefficient(x1, c21);
+ c2->SetCoefficient(x2, c22);
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), c11);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), c12);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), c21);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), c22);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_ClearConstraint) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x1 = solver.MakeBoolVar("x1");
+ MPVariable* x2 = solver.MakeBoolVar("x2");
+ MPConstraint* c1 = solver.MakeRowConstraint(4.1, solver.infinity());
+ MPConstraint* c2 = solver.MakeRowConstraint(-solver.infinity(), 0.1);
+ double c11 = -1533.6, c12 = 3.4, c21 = -11000, c22 = 0.0001;
+ c1->SetCoefficient(x1, c11);
+ c1->SetCoefficient(x2, c12);
+ c2->SetCoefficient(x1, c21);
+ c2->SetCoefficient(x2, c22);
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), c11);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), c12);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), c21);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), c22);
+ c1->Clear();
+ c2->Clear();
+
+ // next part causes sirius to crash ("free(): invalid next size (fast)")
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), 0);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), 0);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), 0);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), 0);
+ }
+
+ TEST(TestSiriusInterface, ObjectiveCoef) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPVariable* x = solver.MakeBoolVar("x");
+ MPObjective* obj = solver.MutableObjective();
+ double coef = 3112.4;
+ obj->SetCoefficient(x, coef);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), coef);
+ coef = 0.2;
+ obj->SetCoefficient(x, coef);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), coef);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_ObjectiveOffset) {
+ // ObjectiveOffset not implemented for sirius_interface
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPVariable* x = solver.MakeBoolVar("x");
+ MPObjective* obj = solver.MutableObjective();
+ double offset = 4.3;
+ obj->SetOffset(offset);
+ solver.Solve();
+ // EXPECT_EQ(getter.getObjectiveOffset(), offset);
+ offset = 3.6;
+ obj->SetOffset(offset);
+ solver.Solve();
+ // EXPECT_EQ(getter.getObjectiveOffset(), offset);
+ }
+
+ TEST(TestSiriusInterface, ObjectiveOffset) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPVariable* x = solver.MakeBoolVar("x");
+ MPObjective* obj = solver.MutableObjective();
+ double offset = 4.3;
+ EXPECT_THROW(obj->SetOffset(offset), std::logic_error);
+ }
+
+ TEST(TestSiriusInterface, ClearObjective) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPVariable* x = solver.MakeBoolVar("x");
+ MPObjective* obj = solver.MutableObjective();
+ double coef = -15.6;
+ obj->SetCoefficient(x, coef);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), coef);
+ obj->Clear();
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), 0);
+ }
+
+ TEST(TestSiriusInterface, ObjectiveSense) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPObjective* const objective = solver.MutableObjective();
+ objective->SetMinimization();
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveSense(), false);
+ objective->SetMaximization();
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveSense(), true);
+ }
+
+ TEST(TestSiriusInterface, interations) {
+ UNITTEST_INIT_LP();
+ int nc = 100, nv = 100;
+ std::vector cs(nc);
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[ci] = solver.MakeRowConstraint(-solver.infinity(), ci + 1);
+ }
+ MPObjective* const objective = solver.MutableObjective();
+ for (int vi = 0; vi < nv; ++vi) {
+ MPVariable* v = solver.MakeNumVar(0, nv, "x" + std::to_string(vi));
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[ci]->SetCoefficient(v, vi + ci);
+ }
+ objective->SetCoefficient(v, 1);
+ }
+ solver.Solve();
+ EXPECT_GT(solver.iterations(), 0);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_nodes) {
+ // The problem seems to be incorrectly returned as infeasible
+ UNITTEST_INIT_MIP();
+ int nc = 100, nv = 100;
+ std::vector cs(2*nc);
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[2*ci ] = solver.MakeRowConstraint(-solver.infinity(), ci + 1);
+ cs[2*ci + 1] = solver.MakeRowConstraint(ci, solver.infinity());
+ }
+ MPObjective* const objective = solver.MutableObjective();
+ for (int vi = 0; vi < nv; ++vi) {
+ MPVariable* v = solver.MakeIntVar(0, nv, "x" + std::to_string(vi));
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[2*ci ]->SetCoefficient(v, vi + ci);
+ cs[2*ci + 1]->SetCoefficient(v, vi + ci);
+ }
+ objective->SetCoefficient(v, 1);
+ }
+ VLOG(0) << solver.Solve();
+ EXPECT_GT(solver.nodes(), 0);
+ }
+
+ TEST(TestSiriusInterface, SolverVersion) {
+ UNITTEST_INIT_MIP();
+ EXPECT_GE(solver.SolverVersion().size(), 36);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_Write) {
+ // SRSwritempsprob has different implementations on the metrix branch
+ UNITTEST_INIT_MIP();
+ MPVariable* x1 = solver.MakeIntVar(-1.2, 9.3, "x1");
+ MPVariable* x2 = solver.MakeNumVar(-1, 5, "x2");
+ MPConstraint* c1 = solver.MakeRowConstraint(-solver.infinity(), 1);
+ c1->SetCoefficient(x1, 3);
+ c1->SetCoefficient(x2, 1.5);
+ MPConstraint* c2 = solver.MakeRowConstraint(3, solver.infinity());
+ c2->SetCoefficient(x2, -1.1);
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetMaximization();
+ obj->SetCoefficient(x1, 1);
+ obj->SetCoefficient(x2, 2);
+
+ std::string tmpName = std::string(std::tmpnam(nullptr)) + ".mps";
+ solver.Write(tmpName);
+
+ std::ifstream tmpFile(tmpName);
+ std::stringstream tmpBuffer;
+ tmpBuffer << tmpFile.rdbuf();
+ tmpFile.close();
+ std::remove(tmpName.c_str());
+
+ EXPECT_EQ(tmpBuffer.str(), R"(* Number of variables: 2
+* Number of constraints: 2
+NAME Pb Solve
+ROWS
+ N OBJECTIF
+ L R0000000
+ G R0000001
+COLUMNS
+ C0000000 OBJECTIF 1.0000000000
+ C0000000 R0000000 3.0000000000
+ C0000001 OBJECTIF 2.0000000000
+ C0000001 R0000000 1.5000000000
+ C0000001 R0000001 -1.1000000000
+RHS
+ RHSVAL R0000000 1.000000000
+ RHSVAL R0000001 3.000000000
+BOUNDS
+ LI BNDVALUE C0000000 -1
+ UI BNDVALUE C0000000 9
+ LO BNDVALUE C0000001 -1.000000000
+ UP BNDVALUE C0000001 5.000000000
+ENDATA
+)");
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SetPrimalTolerance) {
+ // SetPrimalTolerance not implemented for sirius_interface
+ UNITTEST_INIT_LP();
+ MPConstraint* c = solver.MakeRowConstraint(-solver.infinity(), 0.5);
+ MPVariable* x = solver.MakeNumVar(0, 1, "x");
+ MPObjective* obj = solver.MutableObjective();
+ c->SetCoefficient(x, 1);
+ obj->SetCoefficient(x, 1);
+
+ MPSolverParameters params;
+ double tol = 1e-4;
+ params.SetDoubleParam(MPSolverParameters::PRIMAL_TOLERANCE, tol);
+ solver.Solve(params);
+ // EXPECT_EQ(getter.getPrimalTolerance(), tol);
+ }
+
+
+ TEST(TestSiriusInterface, SetPrimalTolerance) {
+ UNITTEST_INIT_LP();
+ MPConstraint* c = solver.MakeRowConstraint(-solver.infinity(), 0.5);
+ MPVariable* x = solver.MakeNumVar(0, 1, "x");
+ MPObjective* obj = solver.MutableObjective();
+ c->SetCoefficient(x, 1);
+ obj->SetCoefficient(x, 1);
+
+ MPSolverParameters params;
+ double tol = 1e-4;
+ params.SetDoubleParam(MPSolverParameters::PRIMAL_TOLERANCE, tol);
+ solver.Solve(params);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SetDualTolerance) {
+ // SetDualTolerance not implemented for sirius_interface
+ UNITTEST_INIT_LP();
+ MPConstraint* c = solver.MakeRowConstraint(-solver.infinity(), 0.5);
+ MPVariable* x = solver.MakeNumVar(0, 1, "x");
+ MPObjective* obj = solver.MutableObjective();
+ c->SetCoefficient(x, 1);
+ obj->SetCoefficient(x, 1);
+
+ MPSolverParameters params;
+ double tol = 1e-2;
+ params.SetDoubleParam(MPSolverParameters::DUAL_TOLERANCE, tol);
+ solver.Solve(params);
+ // EXPECT_EQ(getter.getDualTolerance(), tol) << "Not available";
+ }
+
+ TEST(TestSiriusInterface, SetDualTolerance) {
+ UNITTEST_INIT_LP();
+ MPConstraint* c = solver.MakeRowConstraint(-solver.infinity(), 0.5);
+ MPVariable* x = solver.MakeNumVar(0, 1, "x");
+ MPObjective* obj = solver.MutableObjective();
+ c->SetCoefficient(x, 1);
+ obj->SetCoefficient(x, 1);
+
+ MPSolverParameters params;
+ double tol = 1e-2;
+ params.SetDoubleParam(MPSolverParameters::DUAL_TOLERANCE, tol);
+ solver.Solve(params);
+ }
+
+ TEST(TestSiriusInterface, SetPresolveMode) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::PRESOLVE, MPSolverParameters::PRESOLVE_OFF);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getPresolve(), 0);
+ params.SetIntegerParam(MPSolverParameters::PRESOLVE, MPSolverParameters::PRESOLVE_ON);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getPresolve(), 1);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SetLpAlgorithm) {
+ // SetLpAlgorithm not implemented for sirius_interface
+ UNITTEST_INIT_LP();
+ MPConstraint* c = solver.MakeRowConstraint(-solver.infinity(), 0.5);
+ MPVariable* x = solver.MakeNumVar(0, 1, "x");
+ MPObjective* obj = solver.MutableObjective();
+ c->SetCoefficient(x, 1);
+ obj->SetCoefficient(x, 1);
+
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM, MPSolverParameters::DUAL);
+ solver.Solve(params);
+ // EXPECT_EQ(getter.getLpAlgorithm(), 2);
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM, MPSolverParameters::PRIMAL);
+ solver.Solve(params);
+ // EXPECT_EQ(getter.getLpAlgorithm(), 3);
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM, MPSolverParameters::BARRIER);
+ solver.Solve(params);
+ // EXPECT_EQ(getter.getLpAlgorithm(), 4);
+ }
+
+ TEST(TestSiriusInterface, SetLpAlgorithm) {
+ UNITTEST_INIT_LP();
+ MPConstraint* c = solver.MakeRowConstraint(-solver.infinity(), 0.5);
+ MPVariable* x = solver.MakeNumVar(0, 1, "x");
+ MPObjective* obj = solver.MutableObjective();
+ c->SetCoefficient(x, 1);
+ obj->SetCoefficient(x, 1);
+
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM, MPSolverParameters::DUAL);
+ solver.Solve(params);
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM, MPSolverParameters::PRIMAL);
+ solver.Solve(params);
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM, MPSolverParameters::BARRIER);
+ solver.Solve(params);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SetScaling) {
+ // SetScaling not implemented for sirius_interface
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::SCALING, MPSolverParameters::SCALING_OFF);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getScaling(), 0);
+ params.SetIntegerParam(MPSolverParameters::SCALING, MPSolverParameters::SCALING_ON);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getScaling(), 1);
+ }
+
+ TEST(TestSiriusInterface, SetScaling) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::SCALING, MPSolverParameters::SCALING_OFF);
+ solver.Solve(params);
+ params.SetIntegerParam(MPSolverParameters::SCALING, MPSolverParameters::SCALING_ON);
+ solver.Solve(params);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SetRelativeMipGap) {
+ // SetRelativeMipGap not implemented for sirius_interface
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPSolverParameters params;
+ double relativeMipGap = 1e-3;
+ params.SetDoubleParam(MPSolverParameters::RELATIVE_MIP_GAP, relativeMipGap);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getRelativeMipGap(), relativeMipGap);
+ }
+
+ TEST(TestSiriusInterface, SetRelativeMipGap) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+
+ MPSolverParameters params;
+ double relativeMipGap = 1e-3;
+ params.SetDoubleParam(MPSolverParameters::RELATIVE_MIP_GAP, relativeMipGap);
+ solver.Solve(params);
+ }
+
+ TEST(TestSiriusInterface, setVarBoundType) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(-solver.infinity(), 0);
+ double infty = solver.infinity();
+ solver.MakeIntVar(2, 2, "VARIABLE_FIXE");
+ solver.MakeIntVar(-10, -1, "VARIABLE_BORNEE_DES_DEUX_COTES");
+ solver.MakeIntVar(3, infty, "VARIABLE_BORNEE_INFERIEUREMENT");
+ solver.MakeIntVar(-infty, -1, "VARIABLE_BORNEE_SUPERIEUREMENT");
+ solver.MakeIntVar(-infty, infty, "VARIABLE_NON_BORNEE");
+
+ std::array varBoundTypes = {
+ VARIABLE_FIXE,
+ VARIABLE_BORNEE_DES_DEUX_COTES,
+ VARIABLE_BORNEE_INFERIEUREMENT,
+ VARIABLE_BORNEE_SUPERIEUREMENT,
+ VARIABLE_NON_BORNEE
+ };
+ std::string xpressParamString = "VAR_BOUNDS_TYPE";
+ for (int boundType : varBoundTypes)
+ xpressParamString += " " + std::to_string(boundType);
+
+ solver.SetSolverSpecificParametersAsString(xpressParamString);
+ solver.Solve();
+
+ for (int i = 0; i < varBoundTypes.size(); ++i)
+ EXPECT_EQ(getter.getVarBoundType(i), varBoundTypes[i]);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SolveMIP) {
+ // The problem seems to incorrectly be returned as infeasible
+ UNITTEST_INIT_MIP();
+
+ // max x + 2y
+ // st. -x + y <= 1
+ // 2x + 3y <= 12
+ // 3x + 2y <= 12
+ // x , y >= 0
+ // x , y \in Z
+
+ double inf = solver.infinity();
+ MPVariable* x = solver.MakeIntVar(0, inf, "x");
+ MPVariable* y = solver.MakeIntVar(0, inf, "y");
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetCoefficient(x, 1);
+ obj->SetCoefficient(y, 2);
+ obj->SetMaximization();
+ MPConstraint* c1 = solver.MakeRowConstraint(-inf, 1);
+ c1->SetCoefficient(x, -1);
+ c1->SetCoefficient(y, 1);
+ MPConstraint* c2 = solver.MakeRowConstraint(-inf, 12);
+ c2->SetCoefficient(x, 3);
+ c2->SetCoefficient(y, 2);
+ MPConstraint* c3 = solver.MakeRowConstraint(-inf, 12);
+ c3->SetCoefficient(x, 2);
+ c3->SetCoefficient(y, 3);
+
+ solver.Solve();
+ EXPECT_EQ(obj->Value(), 6);
+ EXPECT_EQ(obj->BestBound(), 6);
+ EXPECT_EQ(x->solution_value(), 2);
+ EXPECT_EQ(y->solution_value(), 2);
+ }
+
+ TEST(TestSiriusInterface, DISABLED_SolveLP) {
+ // Sign of dual values seems to be off
+ // This sign problem occurs with presolve on and presolve off
+ UNITTEST_INIT_LP();
+
+ // max x + 2y
+ // st. -x + y <= 1
+ // 2x + 3y <= 12
+ // 3x + 2y <= 12
+ // x , y \in R+
+
+ double inf = solver.infinity();
+ MPVariable* x = solver.MakeNumVar(0, inf, "x");
+ MPVariable* y = solver.MakeNumVar(0, inf, "y");
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetCoefficient(x, 1);
+ obj->SetCoefficient(y, 2);
+ obj->SetMaximization();
+ MPConstraint* c1 = solver.MakeRowConstraint(-inf, 1);
+ c1->SetCoefficient(x, -1);
+ c1->SetCoefficient(y, 1);
+ MPConstraint* c2 = solver.MakeRowConstraint(-inf, 12);
+ c2->SetCoefficient(x, 3);
+ c2->SetCoefficient(y, 2);
+ MPConstraint* c3 = solver.MakeRowConstraint(-inf, 12);
+ c3->SetCoefficient(x, 2);
+ c3->SetCoefficient(y, 3);
+
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::PRESOLVE, MPSolverParameters::PRESOLVE_OFF);
+ solver.Solve(params);
+
+ EXPECT_NEAR(obj->Value(), 7.4, 1e-8);
+ EXPECT_NEAR(x->solution_value(), 1.8, 1e-8);
+ EXPECT_NEAR(y->solution_value(), 2.8, 1e-8);
+ EXPECT_NEAR(x->reduced_cost(), 0, 1e-8);
+ EXPECT_NEAR(y->reduced_cost(), 0, 1e-8);
+ EXPECT_NEAR(c1->dual_value(), 0.2, 1e-8);
+ EXPECT_NEAR(c2->dual_value(), 0, 1e-8);
+ EXPECT_NEAR(c3->dual_value(), 0.6, 1e-8);
+ }
+
+} // namespace operations_research
+
+int main(int argc, char** argv) {
+ absl::SetFlag(&FLAGS_logtostderr, 1);
+ testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/ortools/xpress/parse_header_xpress.py b/ortools/xpress/parse_header_xpress.py
new file mode 100644
index 0000000..0745f43
--- /dev/null
+++ b/ortools/xpress/parse_header_xpress.py
@@ -0,0 +1,330 @@
+#!/usr/bin/env python3
+"""Xpress header parser script to generate code for the environment.{cc|h}.
+
+To use, run the script
+ ./parse_header_xpress.py
+
+This will printout on the console 9 sections:
+
+------------------- header -------------------
+
+to copy paste in environment.h
+
+------------------- define -------------------
+
+to copy in the define part of environment.cc
+
+------------------- assign -------------------
+
+to copy in the assign part of environment.cc
+
+------------------- string parameters -------------------
+
+to copy in the "getMapStringControls" function of linear_solver/xpress_interface.cc
+
+------------------- string parameters tests -------------------
+
+to copy in the "setStringControls" TEST of linear_solver/unittests/xpress_interface.cc
+
+------------------- double parameters -------------------
+
+to copy in the "getMapDoubleControls" function of linear_solver/xpress_interface.cc
+
+------------------- double parameters tests -------------------
+
+to copy in the "setDoubleControls" TEST of linear_solver/unittests/xpress_interface.cc
+
+------------------- int parameters -------------------
+
+to copy in the "getMapIntControls" function of linear_solver/xpress_interface.cc
+
+------------------- int parameters tests -------------------
+
+to copy in the "setIntControl" TEST of linear_solver/unittests/xpress_interface.cc
+
+------------------- int64 parameters -------------------
+
+to copy in the "getMapInt64Controls" function of linear_solver/xpress_interface.cc
+
+------------------- int64 parameters tests -------------------
+
+to copy in the "setInt64Control" TEST of linear_solver/unittests/xpress_interface.cc
+"""
+
+import argparse
+import re
+from enum import Enum
+
+
+# from absl import app
+
+# This enum is used to detect different sections in the xprs.h document
+class XprsDocumentSection(Enum):
+ STRING_PARAMS = 1
+ DOUBLE_PARAMS = 2
+ INT_PARAMS = 3
+ INT64_PARAMS = 4
+ OTHER = 5
+
+
+class XpressHeaderParser(object):
+ """Converts xprs.h to something pastable in ./environment.h|.cc."""
+
+ def __init__(self):
+ self.__header = ''
+ self.__define = ''
+ self.__assign = ''
+ self.__state = 0
+ self.__return_type = ''
+ self.__args = ''
+ self.__fun_name = ''
+ self.__string_parameters = ''
+ self.__string_parameters_unittest = ''
+ self.__double_parameters = ''
+ self.__double_parameters_unittest = ''
+ self.__int_parameters = ''
+ self.__int_parameters_unittest = ''
+ self.__int64_parameters = ''
+ self.__int64_parameters_unittest = ''
+ # These are the definitions required for compiling the XPRESS interface, excluding control parameters
+ self.__required_defines = {"XPRS_STOP_USER", "XPRS_TYPE_NOTDEFINED", "XPRS_TYPE_INT", "XPRS_TYPE_INT64",
+ "XPRS_TYPE_DOUBLE", "XPRS_PLUSINFINITY", "XPRS_MINUSINFINITY", "XPRS_MAXBANNERLENGTH", "XPVERSION",
+ "XPRS_LPOBJVAL", "XPRS_MIPOBJVAL", "XPRS_BESTBOUND", "XPRS_OBJRHS", "XPRS_OBJSENSE",
+ "XPRS_ROWS", "XPRS_SIMPLEXITER", "XPRS_LPSTATUS", "XPRS_MIPSTATUS", "XPRS_NODES",
+ "XPRS_COLS", "XPRS_LP_OPTIMAL", "XPRS_LP_INFEAS", "XPRS_LP_UNBOUNDED",
+ "XPRS_MIP_SOLUTION", "XPRS_MIP_INFEAS", "XPRS_MIP_OPTIMAL", "XPRS_MIP_UNBOUNDED",
+ "XPRS_OBJ_MINIMIZE", "XPRS_OBJ_MAXIMIZE", "XPRS_NAMES_ROW", "XPRS_NAMES_COLUMN"}
+ self.__missing_required_defines = self.__required_defines
+ # These enum will detect control parameters that will all be imported
+ self.__doc_section = XprsDocumentSection.OTHER
+ # These parameters are not supported
+ self.__excluded_defines = {"XPRS_COMPUTE"}
+ # These are the functions required for compiling the XPRESS interface
+ self.__required_functions = {"XPRScreateprob", "XPRSdestroyprob", "XPRSinit", "XPRSfree", "XPRSgetlicerrmsg",
+ "XPRSlicense", "XPRSgetbanner", "XPRSgetversion", "XPRSsetdefaultcontrol",
+ "XPRSsetintcontrol", "XPRSsetintcontrol64", "XPRSsetdblcontrol",
+ "XPRSsetstrcontrol", "XPRSgetintcontrol", "XPRSgetintcontrol64",
+ "XPRSgetdblcontrol", "XPRSgetstringcontrol", "XPRSgetintattrib",
+ "XPRSgetdblattrib", "XPRSloadlp", "XPRSloadlp64", "XPRSgetobj", "XPRSgetrhs",
+ "XPRSgetrhsrange", "XPRSgetlb", "XPRSgetub", "XPRSgetcoef", "XPRSaddrows",
+ "XPRSdelrows", "XPRSaddcols", "XPRSaddnames", "XPRSgetnames", "XPRSdelcols", "XPRSchgcoltype", "XPRSloadbasis",
+ "XPRSpostsolve", "XPRSchgobjsense", "XPRSgetlasterror", "XPRSgetbasis",
+ "XPRSwriteprob", "XPRSgetrowtype", "XPRSgetcoltype", "XPRSgetlpsol",
+ "XPRSgetmipsol", "XPRSchgbounds", "XPRSchgobj", "XPRSchgcoef", "XPRSchgmcoef",
+ "XPRSchgrhs", "XPRSchgrhsrange", "XPRSchgrowtype", "XPRSaddcbmessage", "XPRSsetcbmessage",
+ "XPRSaddmipsol", "XPRSaddcbintsol", "XPRSremovecbintsol",
+ "XPRSinterrupt", "XPRSlpoptimize", "XPRSmipoptimize"}
+ self.__missing_required_functions = self.__required_functions
+ self.__XPRSprob_section = False
+
+ def write_define(self, symbol, value):
+ if symbol in self.__excluded_defines:
+ print('skipping ' + symbol)
+ return
+
+ # If it is a control parameter, import it to expose it to the user
+ # Else import it only if required
+ if self.__doc_section in [XprsDocumentSection.STRING_PARAMS, XprsDocumentSection.DOUBLE_PARAMS,
+ XprsDocumentSection.INT_PARAMS, XprsDocumentSection.INT64_PARAMS]:
+ self.__header += f'#define {symbol} {value}\n'
+ ortools_symbol = symbol.replace("XPRS_", "")
+ if self.__doc_section == XprsDocumentSection.STRING_PARAMS:
+ self.__string_parameters += f'{{\"{ortools_symbol}\", {symbol}}},\n'
+ self.__string_parameters_unittest += f'{{\"{ortools_symbol}\", {symbol}, "default_value"}},\n'
+ elif self.__doc_section == XprsDocumentSection.DOUBLE_PARAMS:
+ self.__double_parameters += f'{{\"{ortools_symbol}\", {symbol}}},\n'
+ self.__double_parameters_unittest += f'{{\"{ortools_symbol}\", {symbol}, 1.}},\n'
+ elif self.__doc_section == XprsDocumentSection.INT_PARAMS:
+ self.__int_parameters += f'{{\"{ortools_symbol}\", {symbol}}},\n'
+ self.__int_parameters_unittest += f'{{\"{ortools_symbol}\", {symbol}, 1}},\n'
+ elif self.__doc_section == XprsDocumentSection.INT64_PARAMS:
+ self.__int64_parameters += f'{{\"{ortools_symbol}\", {symbol}}},\n'
+ self.__int64_parameters_unittest += f'{{\"{ortools_symbol}\", {symbol}, 1}},\n'
+ elif symbol in self.__required_defines:
+ self.__header += f'#define {symbol} {value}\n'
+ self.__missing_required_defines.remove(symbol)
+ else:
+ print('skipping ' + symbol)
+
+ def write_fun(self, return_type, name, args):
+ if name in self.__required_functions:
+ self.__header += f'extern std::function<{return_type}({args})> {name};\n'
+ self.__define += f'std::function<{return_type}({args})> {name} = nullptr;\n'
+ self.__assign += f' xpress_dynamic_library->GetFunction(&{name}, '
+ self.__assign += f'"{name}");\n'
+ self.__missing_required_functions.remove(name)
+ else:
+ print('skipping ' + name)
+
+ def parse(self, filepath):
+ """Main method to parser the Xpress header."""
+
+ with open(filepath) as fp:
+ all_lines = fp.read()
+
+ self.__XPRSprob_section = False
+
+ for line in all_lines.splitlines():
+ if not line: # Ignore empty lines.
+ continue
+
+ self.detect_XPRSprob_section(line)
+
+ if re.match(r'/\*', line, re.M): # Comments in xprs.h indicate the section
+ if self.__XPRSprob_section:
+ if "string control parameters" in line.lower():
+ self.__doc_section = XprsDocumentSection.STRING_PARAMS
+ elif "double control parameters" in line.lower():
+ self.__doc_section = XprsDocumentSection.DOUBLE_PARAMS
+ elif "integer control parameters" in line.lower() and "64-bit" in line.lower():
+ self.__doc_section = XprsDocumentSection.INT64_PARAMS
+ elif "integer control parameters" in line.lower():
+ self.__doc_section = XprsDocumentSection.INT_PARAMS
+ else:
+ self.__doc_section = XprsDocumentSection.OTHER
+ else:
+ self.__doc_section = XprsDocumentSection.OTHER
+
+ if self.__state == 0:
+ match_def = re.match(r'#define ([A-Z0-9_]*)\s+([^/]+)', line,
+ re.M)
+ if match_def:
+ self.write_define(match_def.group(1), match_def.group(2))
+ continue
+
+ # Single line function definition.
+ match_fun = re.match(
+ r'([a-z]+) XPRS_CC (XPRS[A-Za-z0-9_]*)\(([^;]*)\);', line,
+ re.M)
+ if match_fun:
+ self.write_fun(match_fun.group(1), match_fun.group(2),
+ match_fun.group(3))
+ continue
+
+ # Simple type declaration (i.e. int XPRS_CC).
+ match_fun = re.match(r'([a-z]+) XPRS_CC\s*$', line, re.M)
+ if match_fun:
+ self.__return_type = match_fun.group(1)
+ self.__state = 1
+ continue
+
+ # Complex type declaration with pointer.
+ match_fun = re.match(r'([A-Za-z0-9 ]+)\*\s*XPRS_CC\s*$', line,
+ re.M)
+ if match_fun:
+ self.__return_type = match_fun.group(1) + '*'
+ self.__state = 1
+ continue
+
+ elif self.__state == 1: # The return type was defined at the line before.
+ # Function definition terminates in this line.
+ match_fun = re.match(r'\s*(XPRS[A-Za-z0-9_]*)\(([^;]+)\);', line,
+ re.M)
+ if match_fun:
+ self.write_fun(match_fun.group(1), self.__return_type,
+ match_fun.group(2))
+ self.__state = 0
+ self.__return_type = ''
+ continue
+
+ # Function definition does not terminate in this line.
+ match_fun = re.match(r'\s*(XPRS[A-Za-z0-9_]*)\(([^;]+)$', line,
+ re.M)
+ if match_fun:
+ self.__fun_name = match_fun.group(1)
+ self.__args = match_fun.group(2)
+ self.__state = 2
+ continue
+
+ elif self.__state == 2: # Extra arguments.
+ # Arguments end in this line.
+ match_fun = re.match(r'\s*([^;]+)\);', line, re.M)
+ if match_fun:
+ self.__args += match_fun.group(1)
+ self.write_fun(self.__fun_name, self.__return_type,
+ self.__args)
+ self.__args = ''
+ self.__fun_name = ''
+ self.__return_type = ''
+ self.__state = 0
+ continue
+
+ # Arguments do not end in this line.
+ match_fun = re.match(r'\s*([^;]+)$', line, re.M)
+ if match_fun:
+ self.__args += match_fun.group(1)
+ continue
+
+ def detect_XPRSprob_section(self, line):
+ """This method detects the section between these commented lines:
+ /***************************************************************************\
+ * control parameters for XPRSprob *
+ ...
+ /***************************************************************************\
+ """
+ if " * control parameters for XPRSprob" in line:
+ self.__XPRSprob_section = True
+ elif self.__XPRSprob_section and \
+ "/***************************************************************************\\" in line:
+ self.__XPRSprob_section = False
+
+ def output(self):
+ """Output the 3 generated code on standard out."""
+ print('------------------- header (to copy in environment.h) -------------------')
+ print(self.__header)
+
+ print('------------------- define (to copy in the define part of environment.cc) -------------------')
+ print(self.__define)
+
+ print('------------------- assign (to copy in the assign part of environment.cc) -------------------')
+ print(self.__assign)
+
+ print('------------------- string params (to copy in the "getMapStringControls" function of linear_solver/xpress_interface.cc) -------------------')
+ print(self.__string_parameters)
+
+ print('------------------- string params test (to copy in the "setStringControls" TEST of linear_solver/unittests/xpress_interface.cc) -------------------')
+ print(self.__string_parameters_unittest)
+
+ print('------------------- double params (to copy in the "getMapDoubleControls" function of linear_solver/xpress_interface.cc) -------------------')
+ print(self.__double_parameters)
+
+ print('------------------- double params test (to copy in the "setDoubleControls" TEST of linear_solver/unittests/xpress_interface.cc) -------------------')
+ print(self.__double_parameters_unittest)
+
+ print('------------------- int params (to copy in the "getMapIntControls" function of linear_solver/xpress_interface.cc) -------------------')
+ print(self.__int_parameters)
+
+ print('------------------- int params test (to copy in the "setIntControls" TEST of linear_solver/unittests/xpress_interface.cc) -------------------')
+ print(self.__int_parameters_unittest)
+
+ print('------------------- int64 params (to copy in the "getMapInt64Controls" function of linear_solver/xpress_interface.cc) -------------------')
+ print(self.__int64_parameters)
+
+ print('------------------- int64 params test (to copy in the "setInt64Controls" TEST of linear_solver/unittests/xpress_interface.cc) -------------------')
+ print(self.__int64_parameters_unittest)
+
+ def print_missing_elements(self):
+ if self.__missing_required_defines:
+ print('------WARNING------ missing required defines -------------------')
+ print(self.__missing_required_defines)
+
+ if self.__missing_required_functions:
+ print('------WARNING------ missing required functions -------------------')
+ print(self.__missing_required_functions)
+
+ if self.__missing_required_defines or self.__missing_required_functions:
+ raise LookupError("Some required defines or functions are missing (see detail above)")
+
+
+def main(path: str) -> None:
+ parser = XpressHeaderParser()
+ parser.parse(path)
+ parser.output()
+ parser.print_missing_elements()
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Xpress header parser.')
+ parser.add_argument('filepath', type=str)
+ args = parser.parse_args()
+ main(args.filepath)
diff --git a/patch.py b/patch.py
new file mode 100644
index 0000000..e6bff18
--- /dev/null
+++ b/patch.py
@@ -0,0 +1,184 @@
+from pathlib import Path
+from typing import List
+from patch_utils import *
+
+full_patch: List[Addition] = []
+
+# add the USE_SIRIUS configuration flag in CMakeLists.txt
+full_patch.append(Addition(
+ Path.cwd()/'CMakeLists.txt',
+ '''option(USE_CPLEX "Use the CPLEX solver" OFF)
+message(STATUS "CPLEX support: ${USE_CPLEX}")
+
+''',
+ '''option(USE_SIRIUS "Build and use SIRIUS interface" OFF)
+message(STATUS "SIRIUS support: ${USE_SIRIUS}")
+
+'''))
+
+# add the USE_SIRIUS configuration flag in cpp.cmake
+
+full_patch.append(
+ Addition(
+ Path.cwd()/'cmake'/'cpp.cmake',
+ ' $<$:libscip>\n',
+ ' $<$:sirius_solver>\n'))
+full_patch.append(Addition(
+ Path.cwd()/'cmake'/'cpp.cmake',
+ '''
+if(USE_CPLEX)
+ list(APPEND OR_TOOLS_COMPILE_DEFINITIONS "USE_CPLEX")
+endif()
+''',
+ '''
+if(USE_SIRIUS)
+ list(APPEND OR_TOOLS_COMPILE_DEFINITIONS "USE_SIRIUS")
+endif()
+'''))
+
+# add the USE_SIRIUS configuration flag in deps.cmake
+full_patch.append(Addition(
+ Path.cwd()/'cmake'/'deps.cmake',
+ '''
+if(USE_CPLEX)
+ find_package(CPLEX REQUIRED)
+endif()
+''',
+ '''
+#add SIRIUS
+if (USE_SIRIUS)
+ if(POLICY CMP0074)
+ cmake_policy(SET CMP0074 NEW)
+ endif()
+ find_package(sirius_solver CONFIG REQUIRED)
+endif(USE_SIRIUS)
+'''))
+
+# add the USE_SIRIUS configuration flag in ortoolsConfig.cmake.in
+full_patch.append(Addition(
+ Path.cwd()/'cmake'/'ortoolsConfig.cmake.in',
+ '''
+if(@USE_SCIP@)
+ if(NOT scip_FOUND AND NOT TARGET libscip)
+ find_dependency(SCIP REQUIRED)
+ endif()
+endif()
+''',
+ '''
+if(@USE_SIRIUS@)
+ if(POLICY CMP0074)
+ cmake_policy(SET CMP0074 NEW)
+ endif()
+ if(NOT sirius_solver_FOUND AND NOT TARGET sirius_solver)
+ find_dependency(sirius_solver REQUIRED ${CONFIG_FLAG})
+ endif()
+endif()
+'''))
+
+# add SIRIUS execution in example files
+full_patch.append(Addition(
+ Path.cwd()/'examples'/'cpp'/'linear_programming.cc',
+ ' RunLinearProgrammingExample("XPRESS_LP");\n',
+ ' RunLinearProgrammingExample("SIRIUS_LP");\n'
+ ))
+
+full_patch.append(Addition(
+ Path.cwd()/'examples'/'dotnet'/'cslinearprogramming.cs',
+ ' RunLinearProgrammingExample("XPRESS_LP");\n',
+ ' RunLinearProgrammingExample("SIRIUS_LP");\n'
+ ))
+
+full_patch.append(Addition(
+ Path.cwd()/'examples'/'java'/'LinearProgramming.java',
+ ''' runLinearProgrammingExample("CLP", false);
+''',
+ ''' System.out.println("---- Linear programming example with Sirius ----");
+ runLinearProgrammingExample("SIRIUS_LP", false);
+'''))
+
+full_patch.append(Addition(
+ Path.cwd()/'examples'/'python'/'linear_programming.py',
+ ' RunLinearExampleCppStyleAPI("XPRESS_LP")\n',
+ ' RunLinearExampleCppStyleAPI("SIRIUS_LP")\n'))
+
+# add the USE_SIRIUS configuration flag in ortools/linear_solver/CMakeLists.txt
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'CMakeLists.txt',
+ ' $<$:libscip>\n',
+ ' $<$:sirius_solver>\n'))
+
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'CMakeLists.txt',
+ ''' add_test(NAME cxx_unittests_xpress_interface COMMAND test_xprs_interface)
+''',
+ ''' if (USE_SIRIUS)
+ add_executable(test_sirius_interface sirius_interface_test.cc)
+ target_compile_features(test_sirius_interface PRIVATE cxx_std_17)
+ target_link_libraries(test_sirius_interface PRIVATE ortools::ortools GTest::gtest_main)
+
+ add_test(NAME cxx_unittests_sirius_interface COMMAND test_sirius_interface)
+ endif()
+'''))
+
+# add the SIRIUS support in ortools/linear_solver/linear_solver.cc & .h
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'linear_solver.cc',
+ '''extern MPSolverInterface* BuildXpressInterface(bool mip,
+ MPSolver* const solver);
+''',
+ '''#if defined(USE_SIRIUS)
+extern MPSolverInterface* BuildSiriusInterface(bool mip, MPSolver* const solver);
+#endif
+'''))
+
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'linear_solver.cc',
+ ''' return BuildXpressInterface(false, solver);
+''',
+ '''#if defined(USE_SIRIUS)
+ case MPSolver::SIRIUS_LINEAR_PROGRAMMING:
+ return BuildSiriusInterface(false, solver);
+ case MPSolver::SIRIUS_MIXED_INTEGER_PROGRAMMING:
+ return BuildSiriusInterface(true, solver);
+#endif
+'''))
+
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'linear_solver.cc',
+ '''#ifdef USE_CPLEX
+ if (problem_type == CPLEX_LINEAR_PROGRAMMING ||
+ problem_type == CPLEX_MIXED_INTEGER_PROGRAMMING) {
+ return true;
+ }
+#endif
+''',
+ '''#ifdef USE_SIRIUS
+ if (problem_type == SIRIUS_MIXED_INTEGER_PROGRAMMING) return true;
+ if (problem_type == SIRIUS_LINEAR_PROGRAMMING) return true;
+#endif
+'''))
+
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'linear_solver.cc',
+ ''' {MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING, "xpress"},
+''',
+ ''' {MPSolver::SIRIUS_LINEAR_PROGRAMMING, "sirius_lp"},
+ {MPSolver::SIRIUS_MIXED_INTEGER_PROGRAMMING, "sirius"},
+'''))
+
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'linear_solver.h',
+ ''' COPT_MIXED_INTEGER_PROGRAMMING = 104,
+''',
+ ''' SIRIUS_LINEAR_PROGRAMMING = 105,
+ SIRIUS_MIXED_INTEGER_PROGRAMMING = 106,
+'''))
+
+full_patch.append(Addition(
+ Path.cwd()/'ortools'/'linear_solver'/'linear_solver.h',
+ ' friend class XpressInterface;\n',
+ ' friend class SiriusInterface;\n'))
+
+# run patch
+for a in full_patch:
+ replace_in_file(a.filepath, a.search, a.search+a.add)
diff --git a/patch_utils.py b/patch_utils.py
new file mode 100644
index 0000000..d5411b7
--- /dev/null
+++ b/patch_utils.py
@@ -0,0 +1,17 @@
+from pathlib import Path
+from dataclasses import dataclass
+
+def replace_in_file(filepath, search, replace):
+ with open(filepath, 'r', encoding="utf8") as file:
+ data = file.read()
+ data = data.replace(search, replace)
+
+ with open(filepath, 'w', encoding="utf8") as file:
+ file.write(data)
+
+
+@dataclass
+class Addition:
+ filepath: Path
+ search: str
+ add: str
\ No newline at end of file