├── .github └── workflows │ ├── ci-sage.yml │ ├── ghpages.yml │ └── unittests.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README ├── cmake ├── CMRConfig.cmake.in ├── FindGMP.cmake ├── FindGTestGit.cmake ├── FindGTestSources.cmake └── FindGUROBI.cmake ├── doc ├── CMakeLists.txt ├── TEMPLATE.md ├── balanced.md ├── build.md ├── camion.md ├── changes.md ├── consecutive-ones.md ├── ctu.md ├── equimodular.md ├── file-formats.md ├── generators.md ├── graphic.md ├── header.html ├── index.md ├── k_ary.md ├── logo.png ├── matroids.md ├── max-flow-min-cut.md ├── named.md ├── network.md ├── perfect.md ├── regular.md ├── series-parallel.md ├── seymour.md ├── staircase-compatibility.md ├── totally-balanced.md ├── tu.md └── utilities.md ├── experiments ├── config.py ├── config_modwheel.py ├── config_network.py ├── config_rndcamion.py ├── eval_modwheel.py ├── eval_parse.py ├── gen_modwheel.py ├── gen_modwheel.sbatch ├── gen_network.py ├── gen_network.sbatch ├── gen_rndcamion.py ├── gen_rndcamion.sbatch ├── graphic.py ├── run_modwheel.py ├── run_modwheel.sbatch ├── run_network.py ├── run_network.sbatch ├── run_rndcamion.py ├── run_rndcamion.sbatch └── series_parallel.py ├── include └── cmr │ ├── balanced.h │ ├── camion.h │ ├── config.h.in │ ├── ctu.h │ ├── element.h │ ├── env.h │ ├── equimodular.h │ ├── graph.h │ ├── graphic.h │ ├── linear_algebra.h │ ├── matrix.h │ ├── matroid.h │ ├── named.h │ ├── network.h │ ├── regular.h │ ├── separation.h │ ├── series_parallel.h │ ├── seymour.h │ └── tu.h ├── src ├── cmr │ ├── balanced.c │ ├── bipartite_graph.c │ ├── bipartite_graph.h │ ├── block_decomposition.c │ ├── block_decomposition.h │ ├── camion.c │ ├── camion_internal.h │ ├── ctu.c │ ├── densematrix.c │ ├── densematrix.h │ ├── element.c │ ├── env.c │ ├── env_internal.h │ ├── equimodular.c │ ├── graph.c │ ├── graphic.c │ ├── graphic_internal.h │ ├── hashtable.c │ ├── hashtable.h │ ├── heap.c │ ├── heap.h │ ├── hereditary_property.c │ ├── hereditary_property.h │ ├── linear_algebra.c │ ├── linear_algebra_internal.h │ ├── listmatrix.c │ ├── listmatrix.h │ ├── matrix.c │ ├── matrix_internal.h │ ├── matroid.c │ ├── named.c │ ├── network.c │ ├── regular.c │ ├── regular_dec.c │ ├── regularity_graphic.c │ ├── regularity_nested_minor_sequence.c │ ├── regularity_onesum.c │ ├── regularity_partition.c │ ├── regularity_r10.c │ ├── regularity_series_parallel.c │ ├── regularity_simple_three_separations.c │ ├── regularity_threesum.c │ ├── separation.c │ ├── series_parallel.c │ ├── seymour.c │ ├── seymour_internal.h │ ├── sign.c │ ├── sort.c │ ├── sort.h │ └── tu.c ├── gen │ ├── graphic_gen.c │ ├── gurobi_extract.c │ ├── network_gen.c │ ├── random_gen.c │ ├── random_perturb.c │ ├── series_parallel_gen.c │ └── wheel_gen.c └── main │ ├── balanced_main.c │ ├── camion_main.c │ ├── ctu_main.c │ ├── equimodular_main.c │ ├── graphic_main.c │ ├── k_ary_main.c │ ├── matrix_main.c │ ├── named_main.c │ ├── network_main.c │ ├── regular_main.c │ ├── series_parallel_main.c │ └── tu_main.c └── test ├── CMakeLists.txt ├── common.c ├── common.h ├── test_balanced.cpp ├── test_block_decomposition.cpp ├── test_camion.cpp ├── test_ctu.cpp ├── test_equimodular.cpp ├── test_graph.cpp ├── test_graphic.cpp ├── test_hashtable.cpp ├── test_main.cpp ├── test_matrix.cpp ├── test_matroid.cpp ├── test_network.cpp ├── test_regular.cpp ├── test_separation.cpp ├── test_series_parallel.cpp └── test_tu.cpp /.github/workflows/ci-sage.yml: -------------------------------------------------------------------------------- 1 | name: Run Sage CI 2 | 3 | ## This GitHub Actions workflow provides: 4 | ## 5 | ## - portability testing, by building and testing this project on many platforms 6 | ## (Linux variants) 7 | ## 8 | ## - continuous integration, by building and testing other software 9 | ## that depends on this project. 10 | ## 11 | ## It runs on every push to the GitHub repository. 12 | ## 13 | ## The testing can be monitored in the "Actions" tab of the GitHub repository. 14 | ## 15 | ## After all jobs have finished (or are canceled) and a short delay, 16 | ## tar files of all logs are made available as "build artifacts". 17 | ## 18 | ## This GitHub Actions workflow uses the portability testing framework 19 | ## of SageMath (https://www.sagemath.org/). For more information, see 20 | ## https://doc.sagemath.org/html/en/developer/portability_testing.html 21 | 22 | ## The workflow consists of two jobs: 23 | ## 24 | ## - First, it builds a source distribution of the project 25 | ## and generates a script "update-pkgs.sh". It uploads them 26 | ## as a build artifact named upstream. 27 | ## 28 | ## - Second, it checks out a copy of the SageMath source tree. 29 | ## It downloads the upstream artifact and replaces the project's 30 | ## package in the SageMath distribution by the newly packaged one 31 | ## from the upstream artifact, by running the script "update-pkgs.sh". 32 | ## Then it builds a small portion of the Sage distribution. 33 | ## 34 | ## Many copies of the second step are run in parallel for each of the tested 35 | ## systems/configurations. 36 | 37 | on: 38 | pull_request: 39 | branches: 40 | - main 41 | push: 42 | tags: 43 | branches: 44 | - main 45 | workflow_dispatch: 46 | # Allow to run manually 47 | 48 | concurrency: 49 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 50 | cancel-in-progress: true 51 | 52 | env: 53 | # Ubuntu packages to install so that the project can build an sdist 54 | DIST_PREREQ: 55 | # Name of this project in the Sage distribution 56 | SPKG: cmr 57 | REMOVE_PATCHES: 58 | 59 | jobs: 60 | 61 | dist: 62 | runs-on: ubuntu-latest 63 | steps: 64 | - name: Check out ${{ env.SPKG }} 65 | uses: actions/checkout@v4 66 | with: 67 | path: build/pkgs/${{ env.SPKG }}/src 68 | - name: Install prerequisites 69 | run: | 70 | sudo DEBIAN_FRONTEND=noninteractive apt-get update 71 | sudo DEBIAN_FRONTEND=noninteractive apt-get install $DIST_PREREQ 72 | - name: Run make dist, prepare upstream artifact 73 | run: | 74 | (cd build/pkgs/${{ env.SPKG }}/src && git archive --format=tar.gz --prefix=${{ env.SPKG }}-git/ HEAD > ${{ env.SPKG }}-git.tar.gz) \ 75 | && mkdir -p upstream && cp build/pkgs/${{ env.SPKG }}/src/*.tar.gz upstream/${{ env.SPKG }}-git.tar.gz \ 76 | && echo "sage-package create ${{ env.SPKG }} --version git --tarball ${{ env.SPKG }}-git.tar.gz --type=standard" > upstream/update-pkgs.sh \ 77 | && if [ -n "${{ env.REMOVE_PATCHES }}" ]; then echo "(cd ../build/pkgs/${{ env.SPKG }}/patches && rm -f ${{ env.REMOVE_PATCHES }}; :)" >> upstream/update-pkgs.sh; fi \ 78 | && ls -l upstream/ 79 | - uses: actions/upload-artifact@v4 80 | with: 81 | path: upstream 82 | name: upstream 83 | 84 | linux: 85 | uses: passagemath/passagemath/.github/workflows/docker.yml@main 86 | with: 87 | # Extra system packages to install. See available packages at 88 | # https://github.com/passagemath/passagemath/tree/main/build/pkgs 89 | extra_sage_packages: "patch cmake gfortran openblas" 90 | # Sage distribution packages to build 91 | targets: SAGE_CHECK=no SAGE_CHECK_PACKAGES="cmr" cmr 92 | # Standard setting: Test the current HEAD of passagemath: 93 | sage_repo: passagemath/passagemath 94 | sage_ref: main 95 | upstream_artifact: upstream 96 | # Docker targets (stages) to tag 97 | docker_targets: "with-targets" 98 | # We prefix the image name with the SPKG name ("cmr_") to avoid the error 99 | # 'Package "sage-docker-..." is already associated with another repository.' 100 | docker_push_repository: ghcr.io/${{ github.repository }}/cmr_ 101 | needs: [dist] 102 | 103 | macos: 104 | uses: passagemath/passagemath/.github/workflows/macos.yml@main 105 | with: 106 | # Extra system packages to install. See available packages at 107 | # https://github.com/passagemath/passagemath/tree/main/build/pkgs 108 | extra_sage_packages: "patch cmake gfortran openblas" 109 | # Sage distribution packages to build 110 | targets: SAGE_CHECK=no SAGE_CHECK_PACKAGES="cmr" cmr 111 | # Standard setting: Test the current beta release of Sage: 112 | sage_repo: passagemath/passagemath 113 | sage_ref: main 114 | upstream_artifact: upstream 115 | needs: [dist] 116 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: Doxygen CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | submodules: true 14 | - name: Install requirements 15 | run: | 16 | sudo apt-get update 17 | sudo apt-get install -y libboost-all-dev cmake doxygen 18 | - name: Create build directory 19 | run: mkdir build-debug-generators 20 | - name: Configure 21 | run: cd build-debug-generators && cmake .. -DCMAKE_BUILD_TYPE=Debug -DGENERATORS=on 22 | - name: Compile 23 | run: cd build-debug-generators && make 24 | - name: Test 25 | env: 26 | CTEST_OUTPUT_ON_FAILURE: 1 27 | run: cd build-debug-generators && make test 28 | - name: Create documentation 29 | run: cd build-debug-generators && make doc 30 | - name: Cleanup submodules 31 | run: git reset --hard HEAD && git submodule sync && git submodule deinit -f . || true 32 | - name: Checkout gh-pages 33 | uses: actions/checkout@v4 34 | with: 35 | ref: "gh-pages" 36 | clean: false 37 | - name: Move and commit files 38 | run: | 39 | git config --global user.name github-actions 40 | git config --global user.email '${GITHUB_ACTOR}@users.noreply.github.com' 41 | git remote set-url origin https://x-access-token:${{ secrets.DEPLOY_TOKEN }}@github.com/$GITHUB_REPOSITORY 42 | cp -rp ./build-debug-generators/doc/html/* . 43 | rm -rf ./build-debug-generators 44 | ls -lah 45 | git add . 46 | git commit -m "Documentation for $GITHUB_SHA" || true 47 | git push --force || true 48 | -------------------------------------------------------------------------------- /.github/workflows/unittests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ main, develop ] 6 | pull_request: 7 | branches: [ main, develop ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Install gtest manually 14 | run: sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake CMakeLists.txt && sudo make && sudo cp lib/*.a /usr/lib && sudo ln -s /usr/lib/libgtest.a /usr/local/lib/libgtest.a && sudo ln -s /usr/lib/libgtest_main.a /usr/local/lib/libgtest_main.a 15 | - uses: actions/checkout@v1 16 | - name: configure debug build 17 | run: mkdir build-debug && cd build-debug && cmake -DCMAKE_BUILD_TYPE=Debug .. 18 | - name: compile debug build 19 | run: cd build-debug && make 20 | - name: Run debug tests 21 | run: cd build-debug && ctest && ctest --rerun-failed --output-on-failure 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | *.kdev4 3 | *.csv 4 | *.ods 5 | __pycache__ 6 | experiments/network/ 7 | experiments/modwheel/ 8 | experiments/rndcamion/ 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/doxygen-awesome-css"] 2 | path = doc/doxygen-awesome-css 3 | url = https://github.com/jothepro/doxygen-awesome-css.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | addons: 8 | apt: 9 | packages: 10 | - cmake 11 | - doxygen 12 | - libboost-all-dev 13 | 14 | script: 15 | - mkdir build-debug-generators 16 | - cd build-debug-generators 17 | - cmake .. -DCMAKE_BUILD_TYPE=Debug -DGENERATORS=on 18 | - make 19 | - make test 20 | - cd .. 21 | - mkdir build-release-generators 22 | - cd build-release-generators 23 | - cmake .. -DCMAKE_BUILD_TYPE=Release -DGENERATORS=on 24 | - make 25 | - make test 26 | - cd .. 27 | - mkdir build-release 28 | - cd build-release 29 | - cmake .. -DCMAKE_BUILD_TYPE=Release 30 | - make 31 | - make test 32 | - make doc 33 | 34 | deploy: 35 | provider: pages 36 | skip_cleanup: true 37 | local_dir: build-release/doc/html 38 | github_token: $GH_REPO_TOKEN 39 | on: 40 | branch: master 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 by CMR Authors: https://discopt.github.io/cmr/#authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 1. Create a build directory: 2 | 3 | mkdir build 4 | 5 | 2. Go to the build directory: 6 | 7 | cd build/ 8 | 9 | 3. Configure the build: 10 | 11 | cmake .. 12 | 13 | 4. Compile: 14 | 15 | make 16 | 17 | 5. Use one of executables: 18 | 19 | ./cmr-camion # Camion's signing algorithm 20 | ./cmr-matrix # Basic matrix utililies (dense <-> sparse, transpose, submatrix, support) 21 | ./cmr-ctu # Recognition of complementary totally unimodular matrices 22 | ./cmr-graphic # Recognition and construction of graphic matrices 23 | ./cmr-network # Recognition and construction of network matrices 24 | ./cmr-tu # Recognition of totally unimodular matrices 25 | ./cmr-regular # Recognition of regular matrices / matroids 26 | ./cmr-k-ary # Extraction of large binary or ternary submatrices. 27 | ./cmr-equimodular # Recognition of (strongly) equimodular and unimodular matrices 28 | ./cmr-series-parallel # Recognition of series-parallel matrices 29 | 30 | 6. Optionally, you can also install the executables. 31 | 32 | make install 33 | 34 | 35 | 36 | If you configure cmake with -DGENERATORS=on then a couple of matrix generators are compiled. 37 | In particular, you can use 38 | 39 | ./cmr-extract-gurobi # Extract the coefficient matrix of a mixed-integer program. 40 | 41 | -------------------------------------------------------------------------------- /cmake/CMRConfig.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component(CMR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | include(CMakeFindDependencyMacro) 3 | 4 | if(NOT TARGET CMR::CMR) 5 | include("${CMR_CMAKE_DIR}/CMRTargets.cmake") 6 | endif() 7 | -------------------------------------------------------------------------------- /cmake/FindGMP.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the GMP libraries 2 | # This module defines: 3 | # GMP_FOUND - system has GMP lib 4 | # GMP_INCLUDE_DIR - the GMP include directory 5 | # GMP_LIBRARIES_DIR - directory where the GMP libraries are located 6 | # GMP_LIBRARIES - Link these to use GMP 7 | 8 | # TODO: support MacOSX 9 | 10 | include(FindPackageHandleStandardArgs) 11 | 12 | find_path(GMP_INCLUDE_DIR 13 | NAMES gmp.h 14 | HINTS ENV GMP_INC_DIR 15 | ENV GMP_DIR 16 | $ENV{GMP_DIR}/include 17 | PATH_SUFFIXES include 18 | DOC "The directory containing the GMP header files" 19 | ) 20 | 21 | find_library(GMP_LIBRARY_RELEASE NAMES gmp libgmp-10 gmp-10 mpir 22 | HINTS ENV GMP_LIB_DIR 23 | ENV GMP_DIR 24 | $ENV{GMP_DIR}/lib 25 | PATH_SUFFIXES lib 26 | DOC "Path to the Release GMP library" 27 | ) 28 | 29 | find_library(GMP_LIBRARY_DEBUG NAMES gmpd gmp libgmp-10 gmp-10 mpir 30 | HINTS ENV GMP_LIB_DIR 31 | ENV GMP_DIR 32 | $ENV{GMP_DIR}/include 33 | PATH_SUFFIXES lib 34 | DOC "Path to the Debug GMP library" 35 | ) 36 | 37 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") 38 | set(GMP_LIBRARIES ${GMP_LIBRARY_DEBUG}) 39 | else() 40 | set(GMP_LIBRARIES ${GMP_LIBRARY_RELEASE}) 41 | endif() 42 | 43 | # Attempt to load a user-defined configuration for GMP if couldn't be found 44 | if ( NOT GMP_INCLUDE_DIR OR NOT GMP_LIBRARIES) 45 | include( GMPConfig OPTIONAL ) 46 | endif() 47 | 48 | find_package_handle_standard_args(GMP "DEFAULT_MSG" GMP_LIBRARIES GMP_INCLUDE_DIR) 49 | -------------------------------------------------------------------------------- /cmake/FindGTestGit.cmake: -------------------------------------------------------------------------------- 1 | # FindGTestGit 2 | # ------------ 3 | # 4 | # Download the sources of the Google C++ Testing Framework, build them and add them in a subdirectory. 5 | # 6 | # Sets variables from the subproject. 7 | 8 | message(STATUS "Using GTest sources from git repository.") 9 | 10 | # Create a cmake subproject for downloading in gtestgit-control/. 11 | file(WRITE ${CMAKE_BINARY_DIR}/gtestgit-control/CMakeLists.txt "\ 12 | cmake_minimum_required(VERSION 3.5.0) 13 | project(gtestgit-control NONE) 14 | include(ExternalProject) 15 | ExternalProject_Add(googletest 16 | GIT_REPOSITORY https://github.com/google/googletest.git 17 | GIT_TAG main 18 | SOURCE_DIR \"${CMAKE_BINARY_DIR}/gtestgit-src\" 19 | BINARY_DIR \"${CMAKE_BINARY_DIR}/gtestgit-build\" 20 | CONFIGURE_COMMAND \"\" 21 | BUILD_COMMAND \"\" 22 | INSTALL_COMMAND \"\" 23 | TEST_COMMAND \"\" 24 | )") 25 | 26 | # Run cmake in gtestgit-control. 27 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 28 | RESULT_VARIABLE result 29 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtestgit-control ) 30 | if(result) 31 | message(FATAL_ERROR "CMake step for FindGTestGit failed: ${result}") 32 | endif() 33 | 34 | # Build gtestgit-control. 35 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 36 | RESULT_VARIABLE result 37 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtestgit-control ) 38 | if(result) 39 | message(FATAL_ERROR "Build step for FindGTestGit failed: ${result}") 40 | endif() 41 | 42 | # Add subdirectory with sources in gtest-src and build in gtest-build. 43 | add_subdirectory(${CMAKE_BINARY_DIR}/gtestgit-src 44 | ${CMAKE_BINARY_DIR}/gtestgit-build 45 | EXCLUDE_FROM_ALL) 46 | -------------------------------------------------------------------------------- /cmake/FindGTestSources.cmake: -------------------------------------------------------------------------------- 1 | # FindGTestSources 2 | # ---------------- 3 | # 4 | # Locate the sources of the Google C++ Testing Framework, build them and add them in a subdirectory. 5 | # 6 | # Result variables 7 | # ^^^^^^^^^^^^^^^^ 8 | # 9 | # This module will set the following variables in your project: 10 | # 11 | # ``GTESTSOURCES_FOUND`` 12 | # Found the sources of the Google Testing framework 13 | # ``GTESTSOURCES_BASE`` 14 | # the directory containing the sources of the Google Test headers 15 | # 16 | # In addition also sets variables from the subproject. 17 | 18 | find_path(GTESTSOURCES_BASE 19 | googletest/src/gtest-all.cc 20 | PATHS 21 | $ENV{GTESTSOURCES_ROOT} 22 | ${GTESTSOURCES_ROOT} 23 | HINTS 24 | /usr/src/googletest 25 | /usr/src/gtest 26 | ) 27 | if(GTESTSOURCES_BASE) 28 | message(STATUS "Found GTest sources in ${GTESTSOURCES_BASE}.") 29 | set(GTESTSOURCES_FOUND TRUE) 30 | set(OLD_CMAKE_POLICY_DEFAULT_CMP0048 ${CMAKE_POLICY_DEFAULT_CMP0048}) 31 | set(CMAKE_POLICY_DEFAULT_CMP0048 NEW) 32 | 33 | set(OLD_CMAKE_POLICY_DEFAULT_CMP0054 ${CMAKE_POLICY_DEFAULT_CMP0054}) 34 | set(CMAKE_POLICY_DEFAULT_CMP0054 NEW) 35 | 36 | file(WRITE ${CMAKE_BINARY_DIR}/gtestsources-control/CMakeLists.txt "\ 37 | cmake_minimum_required(VERSION 3.5.0) 38 | project(gtestsources-control NONE) 39 | include(ExternalProject) 40 | ExternalProject_Add(googletest 41 | SOURCE_DIR \"${GTESTSOURCES_BASE}\" 42 | BINARY_DIR \"${CMAKE_BINARY_DIR}/gtestsources-build\" 43 | CONFIGURE_COMMAND \"\" 44 | BUILD_COMMAND \"\" 45 | INSTALL_COMMAND \"\" 46 | TEST_COMMAND \"\" 47 | )") 48 | add_subdirectory(${GTESTSOURCES_BASE} 49 | ${CMAKE_BINARY_DIR}/gtestsources-build 50 | EXCLUDE_FROM_ALL) 51 | set(CMAKE_POLICY_DEFAULT_CMP0054 ${OLD_CMAKE_POLICY_DEFAULT_CMP0054}) 52 | set(CMAKE_POLICY_DEFAULT_CMP0048 ${OLD_CMAKE_POLICY_DEFAULT_CMP0048}) 53 | else() 54 | message(STATUS "Could not find GTestSources.") 55 | set(GTESTSOURCES_FOUND FALSE) 56 | endif() 57 | -------------------------------------------------------------------------------- /cmake/FindGUROBI.cmake: -------------------------------------------------------------------------------- 1 | find_path(GUROBI_INCLUDE_DIRS 2 | NAMES gurobi_c.h 3 | HINTS ${GUROBI_DIR} $ENV{GUROBI_DIR} 4 | PATH_SUFFIXES linux64/include include) 5 | 6 | find_library( GUROBI_CXX_LIBRARY 7 | NAMES gurobi_c++ gurobi_stdc++ 8 | HINTS ${GUROBI_DIR} $ENV{GUROBI_DIR} 9 | PATH_SUFFIXES linux64/lib lib) 10 | 11 | find_library(GUROBI_LIBRARY 12 | NAMES gurobi gurobi70 gurobi75 gurobi80 gurobi81 gurobi90 gurobi95 gurobi100 13 | HINTS ${GUROBI_DIR} $ENV{GUROBI_DIR} 14 | PATH_SUFFIXES linux64/lib lib) 15 | 16 | set(GUROBI_LIBRARIES ${GUROBI_LIBRARY} ${GUROBI_CXX_LIBRARY} ) 17 | 18 | include(FindPackageHandleStandardArgs) 19 | find_package_handle_standard_args(GUROBI DEFAULT_MSG GUROBI_INCLUDE_DIRS GUROBI_LIBRARIES) 20 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Find Doxygen package. 2 | find_package(Doxygen) 3 | 4 | if(DOXYGEN_FOUND) 5 | file(COPY 6 | ${CMAKE_SOURCE_DIR}/doc/doxygen-awesome-css 7 | ${CMAKE_SOURCE_DIR}/doc/header.html 8 | ${CMAKE_SOURCE_DIR}/doc/logo.png 9 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 10 | file(COPY 11 | ${CMAKE_SOURCE_DIR}/doc/header.html 12 | DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 13 | set(DOXYGEN_EXTRA_PACKAGES "amsmath" "amssymb" "mathtools" "amsfont") 14 | set(DOXYGEN_EXTRACT_ALL "yes") 15 | set(DOXYGEN_EXTRACT_STATIC "yes") 16 | set(DOXYGEN_USE_MATHJAX "yes") 17 | set(DOXYGEN_MATHJAX_RELPATH "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/") 18 | set(DOXYGEN_INCLUDE_GRAPH "no") 19 | set(DOXYGEN_INCLUDED_BY_GRAPH "no") 20 | set(DOXYGEN_HTML_HEADER "header.html") 21 | set(DOXYGEN_PROJECT_LOGO "logo.png") 22 | set(DOXYGEN_HTML_EXTRA_STYLESHEET "doxygen-awesome-css/doxygen-awesome.css") 23 | doxygen_add_docs(doc 24 | ${CMAKE_CURRENT_SOURCE_DIR}/index.md 25 | ${CMAKE_CURRENT_SOURCE_DIR}/balanced.md 26 | ${CMAKE_CURRENT_SOURCE_DIR}/build.md 27 | ${CMAKE_CURRENT_SOURCE_DIR}/camion.md 28 | ${CMAKE_CURRENT_SOURCE_DIR}/changes.md 29 | ${CMAKE_CURRENT_SOURCE_DIR}/consecutive-ones.md 30 | ${CMAKE_CURRENT_SOURCE_DIR}/ctu.md 31 | ${CMAKE_CURRENT_SOURCE_DIR}/k_ary.md 32 | ${CMAKE_CURRENT_SOURCE_DIR}/file-formats.md 33 | ${CMAKE_CURRENT_SOURCE_DIR}/graphic.md 34 | ${CMAKE_CURRENT_SOURCE_DIR}/generators.md 35 | ${CMAKE_CURRENT_SOURCE_DIR}/equimodular.md 36 | ${CMAKE_CURRENT_SOURCE_DIR}/matroids.md 37 | ${CMAKE_CURRENT_SOURCE_DIR}/max-flow-min-cut.md 38 | ${CMAKE_CURRENT_SOURCE_DIR}/named.md 39 | ${CMAKE_CURRENT_SOURCE_DIR}/network.md 40 | ${CMAKE_CURRENT_SOURCE_DIR}/perfect.md 41 | ${CMAKE_CURRENT_SOURCE_DIR}/regular.md 42 | ${CMAKE_CURRENT_SOURCE_DIR}/series-parallel.md 43 | ${CMAKE_CURRENT_SOURCE_DIR}/seymour.md 44 | ${CMAKE_CURRENT_SOURCE_DIR}/totally-balanced.md 45 | ${CMAKE_CURRENT_SOURCE_DIR}/tu.md 46 | ${CMAKE_CURRENT_SOURCE_DIR}/utilities.md 47 | ${CMAKE_SOURCE_DIR}/include/cmr 48 | ${CMAKE_SOURCE_DIR}/src/cmr 49 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 50 | COMMENT "Generating doxygen documentation in ${CMAKE_CURRENT_BINARY_DIR}.") 51 | else() 52 | message(WARNING "Could not find Doxygen. Generating a documentation is not supported.") 53 | endif() 54 | -------------------------------------------------------------------------------- /doc/TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description # {#shortcut} 2 | 3 | Definitions, basic tool description. 4 | 5 | 6 | ## Application ## 7 | 8 | The command 9 | 10 | cmr-NAME IN-MAT OUT-MAT [OPTION...] 11 | 12 | reads the input matrix and outputs a submatrix. 13 | 14 | **Options**: 15 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 16 | - `-o FORMAT` Format of file `OUT-MAT`; default: [dense](\ref dense-matrix). 17 | - `-t` Consider the transpose of the matrix. 18 | - `-O OUT-MAT` Write the ... matrix to file `OUT-MAT`; default: stdout. 19 | - `-N OUT-SUB` Write a minimal .. submatrix to file `OUT-SUB`; default: skip computation. 20 | 21 | **Advanced options**: 22 | - `--stats` Print statistics about the computation to stderr. 23 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 24 | 25 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 26 | If `IN-MAT` or `IN-FOO` is `-` then the matrix (resp. foo) is read from stdin. 27 | If `OUT-MAT` is `-` then the matrix is written to stdout. 28 | 29 | ### Algorithm ### 30 | 31 | The implemented algorithm... 32 | For a matrix \f$ M \in \{0,1\}^{m \times n}\f$ with \f$ k \f$ nonzeros it runs in \f$ \mathcal{O}( ? ) \f$ time. 33 | 34 | ### C Interface ### 35 | 36 | The corresponding function in the library is 37 | 38 | - CMRjobOnMatrix() does something. 39 | 40 | and is defined in \ref header.h. 41 | 42 | ... 43 | -------------------------------------------------------------------------------- /doc/balanced.md: -------------------------------------------------------------------------------- 1 | # Balanced / Balanceable Matrices # {#balanced} 2 | 3 | A ternary matrix \f$ M \in \{-1,0,+1\}^{m \times n} \f$ is called **balanced** if it does not contain a square submatrix with two nonzero entries per row and per column in which the sum of all entries is 2 modulo 4. 4 | A binary matrix \f$ M \in \{0,1\}^{m \times n} \f$ is called **balanceable** if and its nonzero entries can be signed so that the resulting matrix is balanced. 5 | 6 | ## Recognizing Balanced Matrices ## 7 | 8 | The command 9 | 10 | cmr-balanced IN-MAT [OPTION...] 11 | 12 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is balanced. 13 | 14 | **Options:** 15 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 16 | - `-N NON-SUB` Write a minimal non-balanced submatrix to file `NON-SUB`; default: skip computation. 17 | 18 | **Advanced options:** 19 | - `--stats` Print statistics about the computation to stderr. 20 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 21 | - `--algorithm ALGO` Algorithm to use, among `enumerate` and `graph`; default: choose best. 22 | - `--no-series-parallel` Do not try series-parallel operations for preprocessing. 23 | 24 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 25 | 26 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 27 | 28 | If `NON-SUB` is `-` then the [submatrix](\ref file-formats-submatrix) is written to stdout. 29 | 30 | ## Algorithm ## 31 | 32 | Two types of algorithms exist for both, the binary and the ternary case. 33 | The first algorithm types enumerate subsets of rows and then finds subsets of columns such that the resulting submatrix defines an odd cycle. 34 | Its running time is exponential in the size of the matrix. 35 | The second algorithm types are the [polynomial-time algorithms](https://doi.org/10.1016/j.jctb.2005.02.006) by Giacomo Zambelli (Journal of Combinatorial Theory, Series B, 2005). 36 | They run in \f$ \mathcal{O}( (m+n)^9 ) \f$ time for binary matrices and in \f$ \mathcal{O}( (m+n)^{11} ) \f$ time for ternary matrices. 37 | 38 | \note Only the first algorithm is implemented so far. 39 | 40 | ## C Interface ## 41 | 42 | The corresponding function in the library is 43 | 44 | - CMRbalancedTest() tests a matrix for balancedness. 45 | 46 | and is defined in \ref balanced.h. 47 | 48 | -------------------------------------------------------------------------------- /doc/build.md: -------------------------------------------------------------------------------- 1 | # Build instructions {#build} 2 | 3 | The library can be built using the [CMake build system](https://cmake.org/). 4 | A simple command sequence on Linux looks like this: 5 | 6 | mkdir build 7 | cd build 8 | cmake .. 9 | make 10 | 11 | Below you can find a list of all parameters for building CMR. 12 | As an optional step, one can execute unittests: 13 | 14 | make test 15 | 16 | The executables now reside in the current directory. One can also install them via 17 | 18 | make install 19 | 20 | **Build Parameters** 21 | 22 | - `-DCMAKE_BUILD_TYPE=Release` Compiles the code with optimization turned on. Make sue to use this if you need fast code for large matrices. 23 | - `-DGENERATORS=on` Builds [generator tools](\ref generators) for certain matrices. 24 | - `-DGMP=off` Disables large numbers; see \ref equimodular. 25 | 26 | -------------------------------------------------------------------------------- /doc/camion.md: -------------------------------------------------------------------------------- 1 | # Camion's Signing Algorithm # {#camion} 2 | 3 | A key tool for the recognition of [network matrices](\ref network), [totally unimodular matrices](\ref tu) and [balanceable matrices](\ref balanced) is Camion's signing algorithm. 4 | Its input is a binary matrix \f$ M \in \{0,1\}^{m \times n} \f$ and it computes a ternary matrix \f$ M' \in \{-1,0,+1\}^{m \times n} \f$ with the same support, i.e., the same nonzero entries. 5 | The output matrix \f$ M' \f$ is guaranteed to be [balanced](\ref balanced) if (and only if) \f$ M \f$ was [balanceable](\ref balanced). 6 | This means that for every square submatrix of \f$ M' \f$ with two nonzero entries per row and per column the the sum of all entries is divisible by 4. 7 | The algorithm always outputs a **Camion-signed matrix**, regardless of whether the input matrix was balanceable. 8 | In particular, it does not recognize whether it deals with a balanceable matrix or not. 9 | 10 | If \f$ M \f$ is balanceable, then \f$ M' \f$ is unique up to scaling rows/columns with \f$ -1 \f$. 11 | 12 | 13 | ## Checking for being Camion-signed ## 14 | 15 | The command 16 | 17 | cmr-camion IN-MAT [OPTION]... 18 | 19 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is Camion-signed. 20 | 21 | **Options:** 22 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 23 | - `-N NON-SUB` Write a minimal non-Camion submatrix to file `NON-SUB`; default: skip computation. 24 | 25 | **Advanced options**: 26 | - `--stats` Print statistics about the computation to stderr. 27 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 28 | 29 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 30 | 31 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 32 | 33 | If `NON-SUB` is `-` then the [submatrix](\ref file-formats-submatrix) is written to stdout. 34 | 35 | ### Algorithm ### 36 | 37 | The implemented recognition algorithm is based on Section 18 of [A decomposition theory for matroids. V. Testing of matrix total unimodularity](https://doi.org/10.1016/0095-8956(90)90030-4) by Klaus Truemper (Journal of Combinatorial Theory, Series B, 1990). 38 | For a matrix \f$ M \in \{-1,0,1\}^{m \times n} \f$ it runs in \f$ \mathcal{O}( \min(m^2 \cdot n, m \cdot n^2) ) \f$ time. 39 | 40 | ### C Interface ### 41 | 42 | The corresponding function in the library is 43 | 44 | - CMRcamionTestSigns() tests a matrix for being Camion-signed. 45 | 46 | and is defined in \ref camion.h. 47 | 48 | 49 | ## Camion-signing a Matrix ## 50 | 51 | The command 52 | 53 | cmr-camion IN-MAT -S OUT-MAT [OPTION]... 54 | 55 | modifies the signs of the matrix given in file `IN-MAT` such that it is Camion-signed and writes the resulting new matrix to file `OUT-MAT`. 56 | 57 | **Options:** 58 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 59 | - `-o FORMAT` Format of file `OUT-MAT`; default: same as format of `IN-MAT`. 60 | 61 | **Advanced options**: 62 | - `--stats` Print statistics about the computation to stderr. 63 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 64 | 65 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 66 | 67 | If `IN-MAT` is `-` then the matrix is read from stdin. 68 | 69 | If `OUT-MAT` is `-` then the matrix is written to stdout. 70 | 71 | ### Algorithm ### 72 | 73 | The implemented algorithm is based on Section 18 of [A decomposition theory for matroids. V. Testing of matrix total unimodularity](https://doi.org/10.1016/0095-8956(90)90030-4) by Klaus Truemper (Journal of Combinatorial Theory, Series B, 1990). 74 | For a matrix \f$ M \in \{-1,0,1\}^{m \times n} \f$ it runs in \f$ \mathcal{O}( \min(m^2 \cdot n, m \cdot n^2) ) \f$ time. 75 | 76 | ### C Interface ### 77 | 78 | The corresponding function in the library is 79 | 80 | - CMRcamionComputeSigns() Computes a Camion-signed version of a given ternary matrix. 81 | 82 | and is defined in \ref camion.h. 83 | -------------------------------------------------------------------------------- /doc/changes.md: -------------------------------------------------------------------------------- 1 | # Change Log # {#changes} 2 | 3 | - Revised the Seymour decomposition structure. 4 | - Fixed a bug in the code for printing dot-files for transposed graphic/network matrices; reported by Christopher Hojny. 5 | - Added a `timeLimit` parameter to all potentially time intensive functions. 6 | - Bugfix for the `timeLimit` parameter. 7 | - Added generator for wheel matrices. 8 | - Added first enumerative code for recognition of balanced matrices. 9 | 10 | ## Version 1.3 ## 11 | 12 | - Redesigned as [Combinatorial Matrix Recognition](\ref index) Library. 13 | - Added recognition of [(co)graphic](\ref graphic) and [(co)network](\ref network) matrices. 14 | - Added recognition of [series-parallel matrices](\ref series-parallel). 15 | - Moved all C++ code to internal implementation. The library interface is only in C. 16 | - Added generators for network, graphic and random matrices. 17 | 18 | ## Version 1.2 ## 19 | 20 | ### 1.2h (2019-09-27) ### 21 | 22 | - Bugfix in search for violating submatrix. 23 | - Added direct test for regularity of binary matroids. 24 | 25 | ### 1.2g (2019-08-15) ### 26 | 27 | - Added support for complement total unimodularity. 28 | 29 | ### 1.2f (2017-03-14) ### 30 | 31 | - Raise an error message if an integer overflow occurs during k-modularity check; reported by Filippo Quondam. 32 | 33 | ### 1.2e (2016-02-28) ### 34 | 35 | - Bugfix for violator search; reported by Bala Krishnamoorthy. 36 | 37 | ### 1.2d (2015-02-06) ### 38 | 39 | - Bugfix for memory bug; reported by Tobias Windisch. 40 | 41 | ### 1.2c (2014-05-08) ### 42 | 43 | - Compatibility fix for more recent boost library; fix by Volker Braun. 44 | 45 | ### 1.2b (2012-10-12) ### 46 | 47 | - Compatibility fix for Mac OS; reported by Yvon Sauvagneau. 48 | 49 | ### 1.2 (2012-04-13) ### 50 | 51 | - Official version for article [Implementation of a unimodularity test](https://doi.org/10.1007/s12532-012-0048-x) by Matthias Walter and Klaus Truemper (Mathematical Programming Computation, 2013). 52 | - Polymake users can now analyze the matroid decomposition. 53 | 54 | ## Version 1.1 (2012-02-09) ## 55 | 56 | - Bug in graphicness test. Older versions may produce wrong results; reported by Paulo Seijas. 57 | - Update for polymake version 2.11 by Marc Pfetsch. 58 | 59 | ## Version 1.0 (2011-06-13) ## 60 | 61 | - First release of the software. 62 | 63 | \anchor TUtest 64 | # TUtest Library # 65 | 66 | This library is the successor of the **TUtest** library, originally developed by Matthias Walter and Klaus Truemper for the recognition of [totally unimodular matrices](\ref tu). 67 | The new name and additional functionality are available since version 1.3. 68 | -------------------------------------------------------------------------------- /doc/consecutive-ones.md: -------------------------------------------------------------------------------- 1 | # Consecutive Ones # {#consecutive-ones} 2 | 3 | A matrix \f$ M \in \{0,1\}^{m \times n} \f$ has the **consecutive ones property for columns** if there is a permutation of the columns such that the permuted matrix \f$ M' \in \{0,1\}^{m \times n} \f$ has the \f$ 1 \f$'s of each row consecutive. 4 | Similarly, \f$ M \in \{0,1\}^{m \times n} \f$ has the **consecutive ones property for rows** if \f$ M^{\mathsf{T}} \f$ has the consecutive ones property for columns. 5 | 6 | -------------------------------------------------------------------------------- /doc/ctu.md: -------------------------------------------------------------------------------- 1 | # Complement Totally Unimodular Matrices # {#ctu} 2 | 3 | Consider a binary matrix \f$ M \in \{0,1\}^{m \times n} \f$. 4 | A **row complement** for row \f$ i \f$ is obtained by complementing all entries \f$ M_{r,c} \f$ with \f$ r \neq i \f$ 5 | and \f$ M_{i,c} = 1 \f$. 6 | A **column complement** is defined as a row complement on the transpose matrix. 7 | A binary matrix \f$ M \f$ is **complement totally unimodular** if all matrices obtainable by successive row- and column complements are [totally unimodular](\ref tu). 8 | It turns out that all such matrices can be obtained already by at most one row complement and at most one column complement. 9 | Hence, complement total unimodularity can be checked by checking \f$ (m+1) \cdot (n+1) \f$ matrices for [total unimodularity](\ref tu). 10 | 11 | ## Recognizing Complement Totally Unimodular Matrices ## 12 | 13 | The command 14 | 15 | cmr-ctu IN-MAT [OPTION]... 16 | 17 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is complement totally unimodular. 18 | 19 | **Options**: 20 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 21 | - `-o FORMAT` Format of file `OUT-MAT`; default: same as for `IN-MAT`. 22 | - `-n OUT-OPS` Write complement operations that leads to a non-totally-unimodular matrix to file `OUT-OPS`; default: skip computation. 23 | - `-N OUT-MAT` Write a complemented matrix that is non-totally-unimodular to file `OUT-MAT`; default: skip computation. 24 | 25 | **Advanced options**: 26 | - `--stats` Print statistics about the computation to stderr. 27 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 28 | 29 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 30 | 31 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 32 | 33 | If `OUT-OPS` or `OUT-MAT` is `-` then the list of operations (resp. the [matrix](\ref file-formats-matrix)) is written to stdout. 34 | 35 | ### C Interface ### 36 | 37 | The corresponding function in the library is 38 | 39 | - CMRctuTest() tests a matrix for being complement totally unimodular. 40 | 41 | and is defined in \ref ctu.h. 42 | 43 | 44 | ## Applying Complement Operations ## 45 | 46 | The command 47 | 48 | cmr-ctu IN-MAT OUT-MAT [OPTION]... 49 | 50 | applies a sequence of row or column complement operations the matrix given in file `IN-MAT` and writes the result to `OUT-MAT`. 51 | 52 | **Options**: 53 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 54 | - `-o FORMAT` Format of file `OUT-MAT`; default: same as for `IN-MAT`. 55 | - `-r ROW` Apply row complement operation to row `ROW`. 56 | - `-c COLUMN` Apply column complement operation to column `COLUMN`. 57 | 58 | **Advanced options**: 59 | - `--stats` Print statistics about the computation to stderr. 60 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 61 | 62 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 63 | If `IN-MAT` is `-` then the matrix is read from stdin. 64 | If `OUT-MAT` is `-` then the matrix is written to stdout. 65 | 66 | ## C Interface ## 67 | 68 | The corresponding function in the library is 69 | 70 | - CMRctuComplementRowColumn() carries out a row- or column-complement operation for a matrix. 71 | 72 | and is defined in \ref ctu.h. 73 | -------------------------------------------------------------------------------- /doc/equimodular.md: -------------------------------------------------------------------------------- 1 | # (Strongly) Equimodular and Unimodular Matrices # {#equimodular} 2 | 3 | Consider a matrix \f$ M \in \mathbb{Z}^{m \times n} \f$ of rank \f$ r \f$. 4 | The matrix \f$ M \f$ is called **equimodular** *with determinant gcd* \f$ k \in \{1,2,\dotsc \} \f$ if these two conditions are satisfied: 5 | 6 | - for some column basis \f$ B \subseteq \{1,2,\dotsc,n\} \f$ of \f$ M \f$, the greatest common divisor of the determinants of all \f$r \f$-by-\f$ r \f$ submatrices of \f$ M_{\star,B} \f$ is equal to \f$ k \f$. 7 | - The matrix \f$ X \f$ such that \f$ M = M_{\star,B} X \f$ is [totally unimodular](\ref tu). 8 | 9 | In case \f$ M \f$ has full row-rank, the first property requires that the determinant of any basis matrix shall be \f$ \pm k \f$, while the second property requires that \f$ M_{\star,B}^{-1} M \f$ is [totally unimodular](\ref tu). 10 | Otherwise, \f$ M_{\star,B} \f$ is not square, and hence the property is more technical. 11 | 12 | \note Equimodularity is independent of the choice of the column basis \f$ B \f$. 13 | 14 | Additionally, \f$ M \f$ is called **strongly equimodular** if \f$ M \f$ and \f$ M^{\textsf{T}} \f$ are both equimodular, which implies that they are equimodular for the same gcd determinants. 15 | The special cases with \f$ k = 1 \f$ are called **unimodular** and **strongly unimodular**, respectively. 16 | 17 | ## Usage ## 18 | 19 | The executable `cmr-equimodular` determines whether a given [matrix](\ref file-formats-matrix) \f$ M \f$ with determinant gcd \f$ k \f$. 20 | 21 | ./cmr-equimodular IN-MAT [OPTION]... 22 | 23 | **Options:** 24 | - `-i FORMAT` Format of of file `IN-MAT`; default: [dense](\ref dense-matrix). 25 | - `-t` Test \f$ M^{\textsf{T}} \f$ instead. 26 | - `-s` Test for strong equimodularity. 27 | - `-u` Test only for unimodularity, i.e., \f$ k = 1 \f$. 28 | 29 | **Advanced options**: 30 | - `--stats` Print statistics about the computation to stderr. 31 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 32 | 33 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 34 | 35 | If `IN-MAT` is `-`, then the input will be read from stdin. 36 | 37 | ## C Interface ## 38 | 39 | The functionality is defined in \ref equimodular.h. 40 | The main functions are: 41 | 42 | - CMRequimodularTest() tests a matrix for being equimodular. 43 | - CMRequimodularTestStrong() tests a matrix for being strongly equimodular. 44 | - CMRunimodularTest() tests a matrix for being unimodular. 45 | - CMRunimodularTestStrong() tests a matrix for being strongly unimodular. 46 | -------------------------------------------------------------------------------- /doc/file-formats.md: -------------------------------------------------------------------------------- 1 | # File Formats # {#file-formats} 2 | 3 | ## Matrix File Formats ## {#file-formats-matrix} 4 | 5 | There are two accepted file formats for matrices. 6 | 7 | \anchor dense-matrix 8 | ### Dense Matrix ### 9 | 10 | The format **dense** is a text format in which a matrix \f$ A \in \mathbb{Z}^{m \times n} \f$ is represented as a whitespace-delimited list of \f$ m \f$, \f$ n \f$, and the \f$ m \cdot n \f$ entries \f$ A_{1,1} \f$, \f$ A_{1,2} \f$, etc.. 11 | The matrix \f$ A = \begin{pmatrix} 1 & -1 & 0 \\ 0 & 1 & 1 \end{pmatrix} \f$ is represented as follows: 12 | 13 | 2 3 14 | 15 | 1 -1 0 16 | 0 1 1 17 | 18 | \anchor sparse-matrix 19 | ### Sparse Matrix ### 20 | 21 | The format **sparse** is a text format in which a matrix \f$ A \in \mathbb{Z}^{m \times n} \f$ with \f$ k \f$ non-zeros are represented as a whitespace-delimited list of \f$ m \f$, \f$ n \f$, \f$ k \f$ and \f$ k \f$ triples \f$ (r,c,v) \f$ for each nonzero value \f$ v \f$ in row \f$ r \f$ and column \f$ c \f$. 22 | Note that \f$ r \in \{1,2,\dotsc,m\} \f$ and \f$ c \in \{1,2,\dotsc,n\} \f$. 23 | The matrix \f$ A = \begin{pmatrix} 1 & -1 & 0 \\ 0 & 1 & 1 \end{pmatrix} \f$ is represented as follows: 24 | 25 | 2 3 4 26 | 27 | 1 1 1 28 | 1 2 -1 29 | 2 2 1 30 | 2 3 1 31 | 32 | ## Submatrices ## {#file-formats-submatrix} 33 | 34 | Several algorithms produce a submatrix of a matrix \f$ A \in \mathbb{Z}^{m \times n} \f$ as output. 35 | The corresponding format consists of three lines with whitespace-delimited integers. 36 | The first has the four entries \f$ m \f$, \f$ n \f$, \f$ r \f$, \f$ c \f$ for an \f$ r \f$-by-\f$ c \f$ submatrix of \f$ A \f$. 37 | The second line consists of \f$ r \f$ entries indicating the row subset (indexing starts at 1) and the third line consists of \f$ c \f$ entries indicating the column subset. 38 | The submatrix obtained from \f$ A = \begin{pmatrix} 1 & -1 & 0 \\ 0 & 1 & 1 \end{pmatrix} \f$ by removing the first column is represented as follows: 39 | 40 | 2 3 2 2 41 | 1 2 42 | 2 3 43 | 44 | In order to extract the actual submatrix, please use the [cmr-matrix](\ref utilities) command. 45 | 46 | ## Graph File Formats ## 47 | 48 | Currently, graphs can only be specified by means of edge lists. 49 | 50 | \anchor edge-list 51 | ### Edge List ### 52 | 53 | The format **edgelist** is a text format in which a graph \f$ G = (V,E) \f$ is represented as a line-separated list of edges. 54 | The line for each edge contains two or three tokens, separated by whitespace. 55 | The first two are names of nodes and the optional third specifies a row/column element. 56 | The graph \f$ G = (V,E) \f$ with nodes \f$ V = \{v,x,y,z\} \f$ and edges \f$ E = \{ \{v,x\}, \{v,y\}, \{v,z\}, \{x,y\}, \{y,z\}, \{z,x\} \} \f$, labeled as \f$ r_1, r_2, r_3, c_1, c_2, c_3 \f$, respectively, is represented as follows: 57 | 58 | v x r1 59 | v y r2 60 | v z r3 61 | x y c1 62 | y z c2 63 | z x c3 64 | 65 | Row/column element labels are useful for \ref graphic. 66 | In fact, the format represents a digraph, which is useful for \ref network. 67 | 68 | \anchor dot 69 | ### Dot ### 70 | 71 | The format **dot** is a text format for drawing graphs, e.g., via the [GraphViz](https://graphviz.org) toolbox. 72 | See [here](https://graphviz.org/doc/info/lang.html) for the language specification. 73 | -------------------------------------------------------------------------------- /doc/graphic.md: -------------------------------------------------------------------------------- 1 | # Graphic / Cographic / Planar Matrices # {#graphic} 2 | 3 | Let \f$ G = (V,E) \f$ be a graph and let \f$ T \f$ be a spanning forest. 4 | The matrix \f$ M(G,T) \in \{0,1\}^{T \times (E \setminus T)} \f$ defined via 5 | \f[ 6 | M(G,T)_{e,f} := \begin{cases} 7 | 1 & \text{if } e \text{ is contained in the unique cycle of } T \cup \{f\} \\ 8 | 0 & \text{otherwise} 9 | \end{cases} 10 | \f] 11 | is called the **graphic matrix** of \f$ G \f$ with respect to \f$ T \f$. 12 | A binary matrix \f$ M \f$ is called **graphic** if there exists a graph \f$ G \f$ with a spanning forest \f$ T \f$ such that \f$ M = M(G,T) \f$. 13 | Moreover, \f$ M \f$ is called **cographic** if \f$ M^{\textsf{T}} \f$ is graphic, and 14 | it is called **planar** if it is graphic and cographic. 15 | 16 | 17 | ## Recognizing Graphic Matrices ## 18 | 19 | The command 20 | 21 | cmr-graphic IN-MAT [OPTION]... 22 | 23 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is (co)graphic. 24 | 25 | **Options**: 26 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 27 | - `-t` Test for being cographic; default: test for being graphic. 28 | - `-G OUT-GRAPH` Write a graph to file `OUT-GRAPH`; default: skip computation. 29 | - `-T OUT-TREE` Write a spanning tree to file `OUT-TREE`; default: skip computation. 30 | - `-D OUT-DOT` Write a dot file `OUT-DOT` with the graph and the spanning tree; default: skip computation. 31 | 32 | **Advanced options**: 33 | - `--stats` Print statistics about the computation to stderr. 34 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 35 | 36 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 37 | 38 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 39 | 40 | If `OUT-GRAPH`, `OUT-TREE`, `OUT-DOT` or `NON-SUB` is `-` then the graph (resp. the tree, dot file or non-(co)graphic [submatrix](\ref file-formats-submatrix)) is written to stdout. 41 | 42 | ### Algorithm ### 43 | 44 | The implemented recognition algorithm is based on [An Almost Linear-Time Algorithm for Graph Realization](https://doi.org/10.1287/moor.13.1.99) by Robert E. Bixby and Donald K. Wagner (Mathematics of Operations Research, 1988). 45 | For a matrix \f$ M \in \{0,1\}^{m \times n}\f$ with \f$ k \f$ nonzeros it runs in \f$ \mathcal{O}( k \cdot \alpha(k, m) ) \f$ time, where \f$ \alpha(\cdot) \f$ denotes the inverse Ackerman function. 46 | 47 | ### C Interface ### 48 | 49 | The corresponding functions in the library are 50 | 51 | - CMRgraphicTestMatrix() tests a matrix for being graphic. 52 | - CMRgraphicTestTranspose() tests a matrix for being cographic. 53 | 54 | and are defined in \ref network.h. 55 | 56 | 57 | ## Computing graphic matrices ## 58 | 59 | The command 60 | 61 | cmr-graphic -c IN-GRAPH OUT-MAT [OPTION]... 62 | 63 | computes a (co)graphic [matrix](\ref file-formats-matrix) corresponding to the graph from file `IN-GRAPH` and writes it to `OUT-MAT`. 64 | 65 | **Options**: 66 | - `-o FORMAT` Format of file `OUT-MAT`; default: [dense](\ref dense-matrix). 67 | - `-t` Return the transpose of the graphic matrix. 68 | - `-T IN-TREE` Read a tree from file `IN-TREE`; default: use first specified arcs as tree edges. 69 | 70 | **Advanced options**: 71 | - `--stats` Print statistics about the computation to stderr. 72 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 73 | 74 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 75 | 76 | If `IN-GRAPH` or `IN-TREE` is `-` then the graph (resp. tree) is read from stdin. 77 | 78 | If `OUT-MAT` is `-` then the [matrix](\ref file-formats-matrix) is written to stdout. 79 | 80 | ### C Interface ### 81 | 82 | The corresponding function in the library is 83 | 84 | - CMRgraphicComputeMatrix() constructs a graphic matrix for a given graph. 85 | 86 | and is defined in \ref network.h. 87 | -------------------------------------------------------------------------------- /doc/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | $projectname: $title 10 | $title 11 | 12 | 13 | 14 | $treeview 15 | $search 16 | $mathjax 17 | 18 | $extrastylesheet 19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
33 |
$projectname 34 |  $projectnumber 35 |
36 |
$projectbrief
37 |
42 |
$projectbrief
43 |
$searchbox
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # Combinatorial Matrix Recognition Library # {#index} 2 | 3 | This C/C++ library contains efficient implementations of various algorithms for recognizing combinatorial matrices. 4 | It is the successor of the [TUtest library](\ref TUtest). 5 | The following matrix classes can be recognized: 6 | 7 | - \ref tu 8 | - \ref network 9 | - \ref ctu 10 | - \ref equimodular 11 | - \ref balanced (currently only via enumerative methods) 12 | 13 | Moreover, [representation matrices](\ref matroids) for the following matroid classes can be recognized: 14 | 15 | - [Binary](\ref binary_regular) and [ternary](\ref tu) regular matroids 16 | - [Graphic, cographic and planar](\ref graphic) matroids 17 | - [Series-parallel](\ref series-parallel) matroids 18 | 19 | The following additional functionality is also part of the library: 20 | 21 | - \ref utilities 22 | - \ref k_ary 23 | - \ref camion 24 | - \ref named 25 | 26 | The library also contains various [instance generators](\ref generators). 27 | 28 | The following matrix/matroid classes are **planned**: 29 | 30 | - \ref totally-balanced 31 | - \ref perfect 32 | - \ref consecutive-ones 33 | - \ref max-flow-min-cut 34 | 35 | # Installation and Usage # 36 | 37 | The library can be found on [github](https://github.com/discopt/cmr/), or directly as a [ZIP Archive](https://github.com/discopt/cmr/archive/refs/heads/master.zip). 38 | The functionality can be used via command-line tools or via its C interface (see [build instructions](\ref build) for more information). 39 | There is one executable per matrix/matroid class. 40 | Its documentation and that of the interface can be found on the respective pages. 41 | The executables accept several \ref file-formats. 42 | 43 | Changes and bug fixes are listed [here](\ref changes). 44 | 45 | # License # 46 | 47 | The software is released under the [MIT License](https://en.wikipedia.org/wiki/MIT_License). 48 | Please cite the paper(s) corresponding to the respective tools. 49 | 50 | # Authors # {#authors} 51 | 52 | - [Rolf van der Hulst](https://people.utwente.nl/r.p.vanderhulst) (graphicness, network matrices) 53 | - Henk Kraaij (balancedness) 54 | - [Klaus Truemper](https://personal.utdallas.edu/~klaus/) (total unimodularity, regularity, complement total unimodularity) 55 | - [Matthias Walter](https://people.utwente.nl/m.walter) (maintainer) 56 | 57 | # Funding support 58 | 59 | - The software library acknowledges support from the Dutch Research Council (NWO) on grant number OCENW.M20.151 (11/2022-10/2026). 60 | -------------------------------------------------------------------------------- /doc/k_ary.md: -------------------------------------------------------------------------------- 1 | # Matrix Entries Inspection # {#k_ary} 2 | 3 | This tool is useful for basic matrix inspection: 4 | 5 | - Test if the input matrix is integer. 6 | - Test if the input matrix is ternary, i.e, it has only entries in \f$ \{-1,0,+1\} \f$. 7 | - Test if the input matrix is binary, i.e, it has only entries in \f$ \{0,+1\} \f$. 8 | 9 | 10 | ## Recognizing Binary and Ternary Matrices ## 11 | 12 | The command 13 | 14 | cmr-k-ary IN-MAT [OPTION...] 15 | 16 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is integer (resp. binary or ternary). 17 | 18 | **Options**: 19 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 20 | - `-b` Test whether the matrix is binary, i.e., has entries in \f$ \{0,+1\} \f$. 21 | - `-t` Test whether the matrix is ternary, i.e., has entries in \f$ \{-1,0,+1\} \f$. 22 | - `-I` Test whether the matrix is integer. 23 | - `-e EPSILON` Allows rounding of numbers up to tolerance `EPSILON`; default: \f$ 10^{-9} \f$. 24 | 25 | **Advanced options**: 26 | - `--stats` Print statistics about the computation to stderr. 27 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 28 | 29 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 30 | 31 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 32 | 33 | 34 | ## Finding Large Binary or Ternary Submatrices ## 35 | 36 | The command 37 | 38 | cmr-k-ary IN-MAT -R OUT-SUB [OPTION...] 39 | 40 | finds a large binary (resp. ternary) [submatrix](\ref file-formats-matrix) of the [matrix](\ref file-formats-matrix) given in file `IN-MAT`. 41 | 42 | **Options:** 43 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 44 | - `-b` Find a large binary submatrix, i.e., one with only entries in \f$ \{0,+1\} \f$. 45 | - `-t` Find a large ternary submatrix, i.e., one with only entries in \f$ \{-1,0,+1\} \f$. 46 | - `-e EPSILON` Allows rounding of numbers up to tolerance `EPSILON`; default: \f$ 10^{-9} \f$. 47 | 48 | **Advanced options**: 49 | - `--stats` Print statistics about the computation to stderr. 50 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 51 | 52 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 53 | 54 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 55 | 56 | If `OUT-SUB` is `-` then the [submatrix](\ref file-formats-submatrix) is written to stdout. 57 | 58 | ## Algorithm ## 59 | 60 | The implemented algorithm successively removes a row or a column with the maximum number of forbidden entries. 61 | -------------------------------------------------------------------------------- /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discopt/cmr/acfda4fe72cb73eb68a7ed20ebfcf4fdb3027542/doc/logo.png -------------------------------------------------------------------------------- /doc/matroids.md: -------------------------------------------------------------------------------- 1 | # Representation of Matroids # {#matroids} 2 | 3 | The matroid **represented** by a matrix \f$ M \in \mathbb{F}^{m \times n} \f$ has \f$ m + n \f$ elements that correspond to the columns of \f$ [ \mathbb{I} \mid M ] \f$. 4 | A subset of these columns is independent if and only if it is linearly independent over the field \f$ \mathbb{F} \f$. 5 | Applying a **pivot operation** (over \f$ \mathbb{F} \f$) and exchanging the corresponding row and column elements leaves 6 | the matroid unchanged. 7 | In CMR, pivots are carried out via \ref CMRchrmatBinaryPivot, \ref CMRchrmatBinaryPivots, \ref CMRchrmatTernaryPivot and \ref CMRchrmatTernaryPivots. 8 | 9 | ## Minors ## 10 | 11 | A **minor** of a represented matroid is another one obtained by **deleting** or **contracting** elements. 12 | An element associated with a row (resp. column) can be contracted (resp. deleted) by removing the corresponding matrix row (resp. column). 13 | In order to delete a row element or contract a column element one must first pivot such that the element type (row/column) is changed. 14 | 15 | A minor of a matroid represented by matrix \f$ M \f$ is represented by means of a \ref CMR_MINOR object. 16 | It consists of a (potentially empty) array of pivots and a \ref CMR_SUBMAT object indicating a submatrix \f$ M' \f$ of the matrix obtained from \f$ M \f$ after applying the pivots. 17 | Moreover, the **type** field indicates a certain structure of \f$ M' \f$: 18 | 19 | - A [determinant](\ref CMR_MINOR_TYPE_DETERMINANT) type indicates that \f$ M' \f$ is a submatrix of \f$ M \f$ with \f$ |\det(M')| \geq 2 \f$. 20 | In particular, no pivots are applied. 21 | - A [Fano](\ref CMR_MINOR_TYPE_FANO) type indicates that \f$ M' \f$ represents the Fano matroid \f$ F_7 \f$. 22 | - A [Fano-dual](\ref CMR_MINOR_TYPE_FANO_DUAL) type indicates that \f$ M' \f$ represents the dual \f$ F_7^\star \f$ of the Fano matroid. 23 | - A [K5](\ref CMR_MINOR_TYPE_K5) type indicates that \f$ M' \f$ represents the graphic matroid \f$ M(K_5) \f$ of the complete graph \f$ K_5 \f$. 24 | - A [K5-dual](\ref CMR_MINOR_TYPE_K5_DUAL) type indicates that \f$ M' \f$ represents the dual matroid \f$ M(K_5)^\star \f$ of the complete graph \f$ K_5 \f$. 25 | - A [K33](\ref CMR_MINOR_TYPE_K33) type indicates that \f$ M' \f$ represents the graphic matroid \f$ M(K_{3,3}) \f$ of the complete bipartite graph \f$ K_{3,3} \f$. 26 | - A [K33-dual](\ref CMR_MINOR_TYPE_K33_DUAL) type indicates that \f$ M' \f$ represents the dual matroid \f$ M(K_{3,3})^\star \f$ of the complete bipartite graph \f$ K_{3,3} \f$. 27 | -------------------------------------------------------------------------------- /doc/max-flow-min-cut.md: -------------------------------------------------------------------------------- 1 | # Max-Flow-Min-Cut Matroids # {#max-flow-min-cut} 2 | 3 | See [here](https://doi.org/10.1287/moor.12.1.72) for a definition. 4 | -------------------------------------------------------------------------------- /doc/named.md: -------------------------------------------------------------------------------- 1 | # Named Matrices # {#named} 2 | 3 | There exist several matrices that play a special role in theory. 4 | 5 | The command 6 | 7 | cmr-named IN-MAT [OPTION]... 8 | 9 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is (up to row/column permutations) one of the named matrices below. 10 | 11 | **Representation matrices of:** 12 | - `R_10` regular matroid \f$ R_{10} \f$ 13 | 14 | **Variants (can be combined):** 15 | - `* ` refers to the dual matroid. 16 | - `.` refers to a specific representation matrix, \f$ k = 1,2, \dots \f$ 17 | 18 | **Other matrices:** 19 | - `I_` Identity matrix of order SIZE. 20 | 21 | **Options:** 22 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 23 | - `-e EPSILON` Allows rounding of numbers up to tolerance EPSILON; default: \f$ 10^{-9} \f$. 24 | 25 | 26 | 27 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 28 | 29 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 30 | 31 | ### Algorithm ### 32 | 33 | The implemented algorithm is based on Section 18 of [A decomposition theory for matroids. V. Testing of matrix total unimodularity](https://doi.org/10.1016/0095-8956(90)90030-4) by Klaus Truemper (Journal of Combinatorial Theory, Series B, 1990). 34 | For a matrix \f$ M \in \{-1,0,1\}^{m \times n} \f$ it runs in \f$ \mathcal{O}( \min(m^2 \cdot n, m \cdot n^2) ) \f$ time. 35 | 36 | ### C Interface ### 37 | 38 | The corresponding function in the library is 39 | 40 | - CMRcamionComputeSigns() Computes a Camion-signed version of a given ternary matrix. 41 | 42 | and is defined in \ref camion.h. 43 | -------------------------------------------------------------------------------- /doc/network.md: -------------------------------------------------------------------------------- 1 | # Network Matrices # {#network} 2 | 3 | Let \f$ D = (V,A) \f$ be a digraph and let \f$ T \f$ be an (arbitrarily) directed spanning forest of the underlying undirected graph. 4 | The matrix \f$ M(D,T) \in \{-1,0,1\}^{T \times (A \setminus T)} \f$ defined via 5 | \f[ 6 | M(D,T)_{a,(v,w)} := \begin{cases} 7 | +1 & \text{if the unique $v$-$w$-path in $T$ passes through $a$ forwardly}, \\ 8 | -1 & \text{if the unique $v$-$w$-path in $T$ passes through $a$ backwardly}, \\ 9 | 0 & \text{otherwise} 10 | \end{cases} 11 | \f] 12 | is called the **network matrix** of \f$ D \f$ with respect to \f$ T \f$. 13 | A matrix \f$ M \f$ is called **network matrix** if there exists a digraph \f$ D \f$ with a directed spanning forest \f$ T \f$ such that \f$ M = M(D,T) \f$. 14 | Moreover, \f$ M \f$ is called **conetwork matrix** if \f$ M^{\textsf{T}} \f$ is a network matrix. 15 | 16 | 17 | ## Recognizing Network Matrices ## 18 | 19 | The command 20 | 21 | cmr-network IN-MAT [OPTION]... 22 | 23 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is (co)network. 24 | 25 | **Options**: 26 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 27 | - `-t` Test for being conetwork; default: test for being network. 28 | - `-G OUT-GRAPH` Write a digraph to file `OUT-GRAPH`; default: skip computation. 29 | - `-T OUT-TREE` Write a directed spanning tree to file `OUT-TREE`; default: skip computation. 30 | - `-D OUT-DOT` Write a dot file `OUT-DOT` with the digraph and the directed spanning tree; default: skip computation. 31 | 32 | **Advanced options**: 33 | - `--stats` Print statistics about the computation to stderr. 34 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 35 | 36 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 37 | 38 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 39 | 40 | If `OUT-GRAPH`, `OUT-TREE`, `OUT-DOT` or `NON-SUB` is `-` then the graph (resp. the tree, dot file or non-(co)network [submatrix](\ref file-formats-submatrix)) is written to stdout. 41 | 42 | ### Algorithm ### 43 | 44 | The implemented recognition algorithm first tests the support matrix of \f$ M \f$ for being [(co)graphic](\ref graphic) and uses \ref camion for testing whether \f$ M \f$ is signed correctly. 45 | 46 | ### C Interface ### 47 | 48 | The corresponding functions in the library are 49 | 50 | - CMRnetworkTestMatrix() tests a matrix for being network. 51 | - CMRnetworkTestTranspose() tests a matrix for being conetwork. 52 | 53 | and are defined in \ref network.h. 54 | 55 | 56 | ## Computing network matrices ## 57 | 58 | The command 59 | 60 | cmr-network -c IN-GRAPH OUT-MAT [OPTION]... 61 | 62 | computes a (co)network [matrix](\ref file-formats-matrix) corresponding to the digraph from file `IN-GRAPH` and writes it to `OUT-MAT`. 63 | 64 | **Options**: 65 | - `-o FORMAT` Format of file `OUT-MAT`; default: [dense](\ref dense-matrix). 66 | - `-t` Return the transpose of the network matrix. 67 | - `-T IN-TREE` Read a directed tree from file `IN-TREE`; default: use first specified arcs as tree edges. 68 | 69 | **Advanced options**: 70 | - `--stats` Print statistics about the computation to stderr. 71 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 72 | 73 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 74 | 75 | If `IN-GRAPH` or `IN-TREE` is `-` then the digraph (resp. directed tree) is read from stdin. 76 | 77 | If `OUT-MAT` is `-` then the [matrix](\ref file-formats-matrix) is written to stdout. 78 | 79 | ### C Interface ### 80 | 81 | The corresponding function in the library is 82 | 83 | - CMRnetworkComputeMatrix() constructs a network matrix for a given digraph. 84 | 85 | and is defined in \ref network.h. 86 | -------------------------------------------------------------------------------- /doc/perfect.md: -------------------------------------------------------------------------------- 1 | # Perfect Matrices # {#perfect} 2 | 3 | A matrix \f$ M \in \{0,1\}^{m \times n} \f$ is called **perfect** if it is the clique-node incidence matrix of a [perfect graph](https://en.wikipedia.org/wiki/Perfect_graph). 4 | In [A Characterization of Perfect Matrices](https://doi.org/10.1016/S0304-0208(08)72932-3) by Manfred W. Padberg (North-Holland Mathematics Studies, 1984) a characterization in terms of forbidden submatrices is provided. 5 | -------------------------------------------------------------------------------- /doc/regular.md: -------------------------------------------------------------------------------- 1 | # Binary Regular Matrices # {#binary_regular} 2 | 3 | A matrix \f$ M \in \{0,1\}^{m \times n} \f$ is **regular** if the binary matroid [represented](\ref matroids) by \f$ M \f$ is representable over *every* field. 4 | This is equivalent to requiring that the matroid is equal to the ternary matroid [represented](\ref matroids) by the [Camion-signed](\ref camion) version \f$ M' \f$ of \f$ M \f$, and thus equivalent to [total unimodularity](\ref tu) of \f$ M' \f$. 5 | 6 | 7 | ## Recognizing Binary Regular Matrices ## 8 | 9 | The command 10 | 11 | cmr-regular IN-MAT [OPTION...] 12 | 13 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is regular. 14 | 15 | **Options:** 16 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 17 | - `-D OUT-DEC` Write a decomposition tree of the regular matroid to file `OUT-DEC`; default: skip computation. 18 | - `-N NON-MINOR` Write a minimal non-regular submatrix to file `NON-SUB`; default: skip computation. 19 | 20 | **Advanced options:** 21 | - `--stats` Print statistics about the computation to stderr. 22 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 23 | - `--decompose STRATEGY` Strategy for decomposing among {`DP`, `YP`, `P3`, `D3`, `Y3`}; default: `D3`. 24 | - `--no-direct-graphic` Check only 3-connected matrices for regularity. 25 | - `--no-series-parallel` Do not allow series-parallel operations in decomposition tree. 26 | - `--no-simple-3-sepa` Do not allow testing for simple 3-separations. 27 | 28 | 29 | **Decomposition strategies:** 1st letter for distributed, 2nd for concentrated rank(s). 30 | - `D` Delta-sum (distributed ranks) 31 | - `Y` Y-sum (distributed ranks) 32 | - `3` 3-sum (concentrated rank) 33 | - `P` pivot (changes rank type) 34 | Note that D3 and Y3 do not produce pivots. 35 | 36 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 37 | 38 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 39 | 40 | If `OUT-DEC` or `NON-SUB` is `-` then the decomposition tree (resp. the [submatrix](\ref file-formats-submatrix)) is written to stdout. 41 | 42 | ## Algorithm ## 43 | 44 | The implemented recognition algorithm is based on [Implementation of a unimodularity test](https://doi.org/10.1007/s12532-012-0048-x) by Matthias Walter and Klaus Truemper (Mathematical Programming Computation, 2013). 45 | It is based on Seymour's [decomposition theorem for regular matroids](https://doi.org/10.1016/0095-8956(80)90075-1). 46 | The algorithm runs in \f$ \mathcal{O}( (m+n)^5 ) \f$ time and is a simplified version of [Truemper's cubic algorithm](https://doi.org/10.1016/0095-8956(90)90030-4). 47 | Please cite the following paper in case the implementation contributed to your research: 48 | 49 | @Article{WalterT13, 50 | author = {Walter, Matthias and Truemper, Klaus}, 51 | title = {Implementation of a unimodularity test}, 52 | journal = {Mathematical Programming Computation}, 53 | year = {2013}, 54 | volume = {5}, 55 | number = {1}, 56 | pages = {57--73}, 57 | issn = {1867-2949}, 58 | doi = {10.1007/s12532-012-0048-x}, 59 | publisher = {Springer-Verlag}, 60 | } 61 | 62 | ## C Interface ## 63 | 64 | The corresponding function in the library is 65 | 66 | - CMRregularTest() tests a binary matrix for regularity. 67 | 68 | and is defined in \ref regular.h. 69 | -------------------------------------------------------------------------------- /doc/series-parallel.md: -------------------------------------------------------------------------------- 1 | # Series-Parallel Matrices # {#series-parallel} 2 | 3 | A matrix \f$ A \in \{-1,0,+1\}^{m \times n} \f$ is called **series-parallel** if it can be obtained from a \f$ 0 \f$-by-\f$ 0 \f$ matrix by successively adjoining 4 | 5 | - a zero row/column vector, 6 | - a (negated) standard unit row/column vector, or 7 | - a (negated) copy of an existing row/column. 8 | 9 | The removal of such a row/column is called an **SP-reduction**. 10 | A matroid is called **series-parallel** if it is [represented](\ref matroids) by a series-parallel matrix. 11 | This is equivalent to being the graphic matroid of a series-parallel graph. 12 | 13 | **Theorem.** A matrix \f$ A \in \{-1,0,1\}^{m \times n} \f$ is *either* series-parallel or it contains, up to scaling of rows/columns with \f$ -1 \f$, 14 | 15 | - a \f$ 2 \f$-by-\f$ 2 \f$ submatrix \f$ M_2 := \begin{pmatrix} -1 & 1 \\ 1 & 1 \end{pmatrix} \f$, 16 | - a \f$ 3 \f$-by-\f$ 3 \f$ submatrix \f$ M_3' := \begin{pmatrix} 1 & 1 & 0 \\ 1 & 1 & 1 \\ 0 & 1 & 1 \end{pmatrix} \f$, or 17 | - a \f$ k \f$-by-\f$ k \f$-submatrix \f$ M_k := \begin{pmatrix} 18 | 1 & 0 & 0 & 0 & \dotsb & 0 & 1 \\ 19 | 1 & 1 & 0 & 0 & \dotsb & 0 & 0 \\ 20 | 0 & 1 & 1 & 0 & \dotsb & 0 & 0 \\ 21 | 0 & 0 & 1 & 1 & \ddots & 0 & 0 \\ 22 | \vdots & \vdots & \vdots & \ddots & \ddots & \vdots & \vdots \\ 23 | 0 & 0 & 0 & 0 & \dotsb & 1 & 0 \\ 24 | 0 & 0 & 0 & 0 & \dotsb & 1 & 1 25 | \end{pmatrix} \f$ for \f$ k \geq 3 \f$. 26 | 27 | The latter two matrices are called **wheel matrices** since their represented matroids are the graphic matroids of wheel graphs. 28 | 29 | 30 | ## Recognizing Series-Parallel Matrices ## 31 | 32 | The command 33 | 34 | cmr-series-parallel IN-MAT [OPTION...] 35 | 36 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is series-parallel. 37 | If this is not the case, then a maximal number of SP-reductions is carried out, leading to the **reduced** matrix. 38 | Moreover, one can ask for one of the minimal non-series-parallel submatrices above. 39 | 40 | **Options:** 41 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 42 | - `-S OUT-SP` Write the list of series-parallel reductions to file `OUT-SP`; default: skip computation. 43 | - `-R OUT-REDUCED` Write the reduced submatrix to file `OUT-REDUCED`; default: skip computation. 44 | - `-N NON-SUB` Write a minimal non-series-parallel submatrix to file `NON-SUB`; default: skip computation. 45 | - `-b` Test for being binary series-parallel; default: ternary. 46 | 47 | **Advanced options**: 48 | - `--stats` Print statistics about the computation to stderr. 49 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 50 | 51 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 52 | 53 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 54 | 55 | If `OUT-SP`, `OUT-REDUCED` or `NON-SUB` is `-` then the list of reductions (resp. the [submatrix](\ref file-formats-submatrix)) is written to stdout. 56 | 57 | ## Algorithm ## 58 | 59 | The implemented algorithm is not yet published. 60 | For a matrix \f$ A \in \{0,1\}^{m \times n}\f$ with \f$ k \f$ (sorted) nonzeros it runs in \f$ \mathcal{O}( m + n + k ) \f$ time assuming no hashtable collisions. 61 | 62 | ## C Interface ## 63 | 64 | The corresponding functions in the library are 65 | 66 | - CMRspTestTernary() tests a binary matrix for being series-parallel. 67 | - CMRspTestBinary() tests a binary matrix for being series-parallel. 68 | - CMRspDecomposeBinary() tests a binary matrix for being series-parallel, but may also terminate early, returning a 2-separation of \f$ A \f$. 69 | - CMRspDecomposeTernary() tests a ternary matrix for being series-parallel, but may also terminate early, returning a 2-separation of \f$ A \f$. 70 | 71 | and are defined in \ref series_parallel.h. 72 | -------------------------------------------------------------------------------- /doc/staircase-compatibility.md: -------------------------------------------------------------------------------- 1 | # Staircase Compatibility # {#staircase-compatibility} 2 | 3 | See [here](http://www.optimization-online.org/DB_HTML/2018/01/6435.html) for a definition. 4 | -------------------------------------------------------------------------------- /doc/totally-balanced.md: -------------------------------------------------------------------------------- 1 | # Totally Balanced Matrices # {#totally-balanced} 2 | 3 | A binary matrix \f$M \in \{0,1\}^{m \times n} \f$ is called **totally balanced** if it does not contain a square matrix that is the incidence matrix of any cycle of length at least 3. 4 | 5 | -------------------------------------------------------------------------------- /doc/tu.md: -------------------------------------------------------------------------------- 1 | # Totally Unimodular Matrices # {#tu} 2 | 3 | A matrix \f$ M \in \mathbb{Z}^{m \times n} \f$ is **totally unimodular** if all its square submatrices have a determinant in \f$ \{-1,0,+1\} \f$. 4 | Here, a submatrix does not need to be contiguous, i.e., the matrix \f$M = \begin{pmatrix} 1 & 0 & -1 \\ 1 & 0 & 1 \end{pmatrix} \f$ is not totally unimodular since the submatrix indexed by rows \f$ \{1, 2 \} \f$ and columns \f$ \{ 1,3 \} \f$ has determinant 2. 5 | In particular, every totally unimodular matrix has only entries in \f$ \{-1,0,+1\} \f$ as these are the 1-by-1 submatrices. 6 | 7 | 8 | ## Recognizing Totally Unimodular Matrices ## 9 | 10 | The command 11 | 12 | cmr-tu IN-MAT [OPTION...] 13 | 14 | determines whether the [matrix](\ref file-formats-matrix) given in file `IN-MAT` is totally unimodular. 15 | 16 | **Options:** 17 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 18 | - `-D OUT-DEC` Write a decomposition tree of the underlying regular matroid to file `OUT-DEC`; default: skip computation. 19 | - `-N NON-SUB` Write a minimal non-totally-unimodular submatrix to file `NON-SUB`; default: skip computation. 20 | 21 | **Advanced options:** 22 | - `--stats` Print statistics about the computation to stderr. 23 | - `--time-limit LIMIT` Allow at most `LIMIT` seconds for the computation. 24 | - `--decompose STRATEGY` Strategy for decomposing among {`DP`, `YP`, `P3`, `D3`, `Y3`}; default: `D3`. 25 | - `--no-direct-graphic` Check only 3-connected matrices for regularity. 26 | - `--no-series-parallel` Do not allow series-parallel operations in decomposition tree. 27 | - `--no-simple-3-sepa` Do not allow testing for simple 3-separations. 28 | - `--naive-submatrix` Use naive bad submatrix algorithm instead of greedy heuristic. 29 | - `--algo ALGO` Use algorithm from {`decomposition`, `submatrix`, `partition`}; default: `decomposition`. 30 | 31 | **Decomposition strategies:** 1st letter for distributed, 2nd for concentrated rank(s). 32 | - `D` Delta-sum (distributed ranks) 33 | - `Y` Y-sum (distributed ranks) 34 | - `3` 3-sum (concentrated rank) 35 | - `P` pivot (changes rank type) 36 | Note that D3 and Y3 do not produce pivots. 37 | 38 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 39 | 40 | If `IN-MAT` is `-` then the [matrix](\ref file-formats-matrix) is read from stdin. 41 | 42 | If `OUT-DEC` or `NON-SUB` is `-` then the decomposition tree (resp. the [submatrix](\ref file-formats-submatrix)) is written to stdout. 43 | 44 | ## Algorithms ## 45 | 46 | The implemented default recognition algorithm is based on [Implementation of a unimodularity test](https://doi.org/10.1007/s12532-012-0048-x) by Matthias Walter and Klaus Truemper (Mathematical Programming Computation, 2013). 47 | It either runs \ref camion to reduce the question to that of [recognizing binary regular matroids](\ref binary_regular) or decomposes a given ternary matrix directly by means of a [Seymour decomposition](\ref seymour_decomposition). 48 | Please cite the paper in case the implementation contributed to your research: 49 | 50 | @Article{WalterT13, 51 | author = {Walter, Matthias and Truemper, Klaus}, 52 | title = {Implementation of a unimodularity test}, 53 | journal = {Mathematical Programming Computation}, 54 | year = {2013}, 55 | volume = {5}, 56 | number = {1}, 57 | pages = {57--73}, 58 | issn = {1867-2949}, 59 | doi = {10.1007/s12532-012-0048-x}, 60 | publisher = {Springer-Verlag}, 61 | } 62 | 63 | In order to repeat experiments described in the paper above, the function can be parameterized as to use exponential-time algorithms. 64 | 65 | - The first is based on the criterion of Ghouila-Houri and runs in time \f$ \mathcal{O}( (m + n) \cdot 3^{\min(m, n)}) \f$. 66 | - The second enumerates square [Eulerian submatrices](https://www.ams.org/journals/proc/1965-016-05/S0002-9939-1965-0180568-2/) and runs in time \f$ \mathcal{O}( (m+n) \cdot 2^{ m + n } ) \f$. 67 | 68 | ## C Interface ## 69 | 70 | The corresponding function in the library is 71 | 72 | - CMRtuTest() tests a matrix for being totally unimodular. 73 | 74 | and is defined in \ref tu.h. 75 | Its parameters also allow to choose one of the enumeration algorithms with exponential running time instead of the decomposition algorithm. 76 | 77 | -------------------------------------------------------------------------------- /doc/utilities.md: -------------------------------------------------------------------------------- 1 | # Basic Utilities # {#utilities} 2 | 3 | This tool is useful for basic matrix operations: 4 | 5 | - Transposing the input matrix. 6 | - Turning a submatrix of a matrix into an explicit matrix. 7 | - Computing the support matrix of the input matrix. 8 | - Computing the signed support matrix (negative entries are turned into \f$ -1 \f$'s, positive into \f$ +1 \f$'s) of the input matrix. 9 | 10 | ## Matrix Utilities ## 11 | 12 | The command 13 | 14 | cmr-matrix IN-MAT OUT-MAT [OPTION]... 15 | 16 | copies the [matrix](\ref file-formats-matrix) from file `IN-MAT` to file `OUT-MAT`, potentially applying certain operations. 17 | 18 | **Options:** 19 | - `-i FORMAT` Format of file `IN-MAT`; default: [dense](\ref dense-matrix). 20 | - `-o FORMAT` Format of file `OUT-MAT`; default: same as format of `IN-MAT`. 21 | - `-S IN-SUB` Consider the [submatrix](\ref file-formats-submatrix) of `IN-MAT` specified in file `IN-SUB` instead of `IN-MAT` itself; can be combined with other operations. 22 | - `-t` Transpose the matrix; can be combined with other operations. 23 | - `-c` Compute the support matrix instead of copying. 24 | - `-C` Compute the signed support matrix instead of copying. 25 | - `-r` Randomize the output matrix by randomly permuting rows/columns. 26 | - `-R2 NUM` Randomize the output matrix by performing `NUM` binary random pivots. 27 | - `-R3 NUM` Randomize the output matrix by performing `NUM` ternary random pivots. 28 | - `-d` Use double arithmetic instead of integers. 29 | 30 | Formats for matrices: [dense](\ref dense-matrix), [sparse](\ref sparse-matrix) 31 | If `IN-MAT` or `IN-SUB` is `-` then the input matrix (resp. submatrix) is read from stdin. 32 | If `OUT-MAT` is `-` then the output matrix is written to stdout. 33 | -------------------------------------------------------------------------------- /experiments/config.py: -------------------------------------------------------------------------------- 1 | BUILD_DIRECTORY='/home/walterm/code/cmr-develop.git/build-release' 2 | UNIMOD_DIRECTORY='/home/walterm/code/unimodularity-library-1.2h/src' 3 | LOCAL_STORAGE='/local/walterm/cmr' 4 | ALGORITHMS = ['cmrdec', 'cmrpart', 'cmreuler', 'cmrcert', 'unimod', 'unimodcert'] 5 | -------------------------------------------------------------------------------- /experiments/config_modwheel.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | INSTANCE_DIRECTORY = 'modwheel' 4 | INSTANCES = sorted(list(range(9, 99, 10)) + list(range(99, 900, 100))) 5 | SAMPLES = list(range(1, 10+1)) 6 | 7 | -------------------------------------------------------------------------------- /experiments/config_network.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | INSTANCE_DIRECTORY = 'network' 4 | INSTANCES = sorted(set(range(1,41)) | set(100 * i for i in range(1,41) ) | set(1000 * i for i in range(1,41) )) 5 | SAMPLES = list(range(1, 10+1)) 6 | 7 | -------------------------------------------------------------------------------- /experiments/config_rndcamion.py: -------------------------------------------------------------------------------- 1 | from config import * 2 | 3 | INSTANCE_DIRECTORY = 'rndcamion' 4 | INSTANCES = [ (order,prob) for order in range(50,2001,50) for prob in [0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1] ] 5 | SAMPLES = list(range(1, 10+1)) 6 | 7 | -------------------------------------------------------------------------------- /experiments/eval_modwheel.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | import eval_parse 6 | 7 | from config_modwheel import * 8 | 9 | assert os.path.exists(INSTANCE_DIRECTORY) 10 | 11 | results = [] 12 | for instance in INSTANCES: 13 | file_prefix = f'{INSTANCE_DIRECTORY}/modwheel-{instance:05d}x{instance:05d}' 14 | for algo in ALGORITHMS: 15 | for sample in SAMPLES: 16 | result = eval_parse.parse(file_prefix, instance, algo, sample) 17 | 18 | if algo.startswith('unimod') and result.time >= 3700.0: 19 | result.time = float('inf') 20 | 21 | results.append( result ) 22 | print(f' {result}') 23 | 24 | times = eval_parse.averageTimes(results) 25 | 26 | for instance in INSTANCES: 27 | for algo in ALGORITHMS: 28 | print(instance, algo, times[ (instance, algo) ]) 29 | 30 | -------------------------------------------------------------------------------- /experiments/eval_parse.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | 4 | class Result: 5 | def __init__(self, instance, algorithm, run=None): 6 | self._run = run 7 | self._instance = instance 8 | self._algorithm = algorithm 9 | self.time = float('inf') 10 | self.time_max = 0.0 11 | self.time_min = 0.0 12 | 13 | @property 14 | def instance(self): 15 | return self._instance 16 | 17 | @property 18 | def algorithm(self): 19 | return self._algorithm 20 | 21 | @property 22 | def run(self): 23 | return self._run 24 | 25 | def __repr__(self): 26 | run_str = '' if self._run is None else f' (run #{self._run})' 27 | return f'{self._instance} via {self._algorithm}{run_str}: {self.time} in [{self.time_min},{self.time_max}]' 28 | 29 | def averageTimes(result_list): 30 | result_map = { (result.instance, result.algorithm): [] for result in result_list } 31 | for result in result_list: 32 | result_map[result.instance,result.algorithm].append(result) 33 | 34 | def avg(instance, algorithm, L): 35 | times = [ result.time for result in L ] 36 | res = Result(instance, algorithm) 37 | res.time = sum(times) / len(L) 38 | res.time_min = min(times) 39 | res.time_max = max(times) 40 | return res 41 | 42 | return { key: avg(key[0], key[1], value) for key,value in result_map.items() } 43 | 44 | def parseCMR(file_base, instance, algorithm, run): 45 | result = Result(instance, algorithm, run) 46 | 47 | sys.stderr.write(f'Reading {file_base}.out|.err\n') 48 | 49 | re_part_time = re.compile(' partition time: ([0-9.]*)( seconds|)') 50 | re_euler_time = re.compile(' eulerian enumeration time: ([0-9.]*)( seconds|)') 51 | re_dec_time = re.compile(' seymour total: [0-9]* in ([0-9.]*)( seconds|)') 52 | re_time_limit = re.compile('Time limit exceeded!') 53 | 54 | try: 55 | time_limit = False 56 | with open(file_base + '.err', 'r') as err_file: 57 | for line in err_file: 58 | match = re_time_limit.match(line) 59 | if match: 60 | time_limit = True 61 | continue 62 | match = re_part_time.match(line) 63 | if algorithm == 'cmrpart' and match and not time_limit: 64 | result.time = float(match.group(1)) 65 | continue 66 | match = re_euler_time.match(line) 67 | if algorithm == 'cmreuler' and match and not time_limit: 68 | result.time = float(match.group(1)) 69 | continue 70 | match = re_dec_time.match(line) 71 | if algorithm in ['cmrdec', 'cmrcert'] and match and not time_limit: 72 | result.time = float(match.group(1)) 73 | continue 74 | except: 75 | pass 76 | 77 | return result 78 | 79 | def parseUnimodularityTest(file_base, instance, algorithm, run): 80 | result = Result(instance, algorithm, run) 81 | 82 | sys.stderr.write(f'Reading {file_base}.out\n') 83 | 84 | re_total_time = re.compile('Total time: ([0-9.]*)') 85 | 86 | try: 87 | with open(file_base + '.out', 'r') as out_file: 88 | for line in out_file: 89 | match = re_total_time.match(line) 90 | if algorithm in ['unimod', 'unimodcert'] and match: 91 | result.time = float(match.group(1)) 92 | continue 93 | except: 94 | pass 95 | 96 | return result 97 | 98 | def parse(file_prefix, instance, algorithm, run): 99 | if algorithm[:3] == 'cmr': 100 | return parseCMR(f'{file_prefix}#{run:03d}-{algorithm}', instance, algorithm, run) 101 | elif algorithm[:6] == 'unimod': 102 | return parseUnimodularityTest(f'{file_prefix}#{run:03d}-{algorithm}', instance, algorithm, run) 103 | 104 | if __name__ == '__main__': 105 | result = parse(sys.argv[1], sys.argv[2], sys.argv[3], int(sys.argv[4])) 106 | print(result) 107 | 108 | -------------------------------------------------------------------------------- /experiments/gen_modwheel.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | from config_modwheel import * 6 | 7 | assert os.path.exists(INSTANCE_DIRECTORY) 8 | 9 | for sample in SAMPLES: 10 | for instance in INSTANCES: 11 | file_base = f'{INSTANCE_DIRECTORY}/modwheel-{instance:05d}x{instance:05d}#{sample:03d}' 12 | os.system(f'{BUILD_DIRECTORY}/cmr-generate-wheel -01 {instance} -o sparse | {BUILD_DIRECTORY}/cmr-matrix -i sparse - -r -R2 {instance//2+1} -o sparse - | gzip > {file_base}.sparse.gz') 13 | 14 | -------------------------------------------------------------------------------- /experiments/gen_modwheel.sbatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -c 1 # Number of cores reserved for each task 3 | #SBATCH --mem=1G # Memory reserved for each task 4 | #SBATCH --mail-type=END,FAIL # When to send an email 5 | #SBATCH -J gen_modwheel 6 | #SBATCH --output=modwheel/gen-%A.log 7 | #SBATCH --time=0:05:00 # Time limit 8 | 9 | 10 | HOSTNAME=`hostname` 11 | NUM_CPUS=`lscpu | egrep '^CPU\(s\):' | cut -d ':' -f2- | sed -e 's/^[[:space:]]*//'` 12 | NUM_CORES=`egrep '^cpu cores' /proc/cpuinfo | uniq | cut -d ':' -f2 | cut -d ' ' -f2-` 13 | MEM=`lsmem | egrep 'Total online memory:' | cut -d: -f2- | sed -e 's/^[[:space:]]*//'` 14 | echo "Run on ${HOSTNAME} which has ${NUM_CPUS} CPUs (${NUM_CORES} cores) and ${MEM} memory." 15 | 16 | python3 gen_modwheel.py 17 | -------------------------------------------------------------------------------- /experiments/gen_network.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | 6 | from config_network import * 7 | 8 | assert os.path.exists(INSTANCE_DIRECTORY) 9 | 10 | for sample in SAMPLES: 11 | for instance in INSTANCES: 12 | order = instance 13 | file_base = f'{INSTANCE_DIRECTORY}/network-{order:05d}x{order:05d}#{sample:03d}' 14 | os.system(f'{BUILD_DIRECTORY}/cmr-generate-network {order} {order} -o sparse | gzip > {file_base}.sparse.gz') 15 | 16 | -------------------------------------------------------------------------------- /experiments/gen_network.sbatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -c 1 # Number of cores reserved for each task 3 | #SBATCH --mem=1G # Memory reserved for each task 4 | #SBATCH --mail-type=END,FAIL # When to send an email 5 | #SBATCH -J gen_network 6 | #SBATCH --output=network/gen-%A.log 7 | #SBATCH --time=8:00:00 # Time limit 8 | 9 | 10 | HOSTNAME=`hostname` 11 | NUM_CPUS=`lscpu | egrep '^CPU\(s\):' | cut -d ':' -f2- | sed -e 's/^[[:space:]]*//'` 12 | NUM_CORES=`egrep '^cpu cores' /proc/cpuinfo | uniq | cut -d ':' -f2 | cut -d ' ' -f2-` 13 | MEM=`lsmem | egrep 'Total online memory:' | cut -d: -f2- | sed -e 's/^[[:space:]]*//'` 14 | echo "Run on ${HOSTNAME} which has ${NUM_CPUS} CPUs (${NUM_CORES} cores) and ${MEM} memory." 15 | 16 | python3 gen_network.py 17 | -------------------------------------------------------------------------------- /experiments/gen_rndcamion.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | 6 | from config_rndcamion import * 7 | 8 | assert os.path.exists(INSTANCE_DIRECTORY) 9 | 10 | for sample in SAMPLES: 11 | for instance in INSTANCES: 12 | order,p = instance 13 | file_base = f'{INSTANCE_DIRECTORY}/rndcamion-{order:05d}x{order:05d}-p{p:0.2f}#{sample:03d}' 14 | os.system(f'{BUILD_DIRECTORY}/cmr-generate-random -o sparse {order} {order} {p} | {BUILD_DIRECTORY}/cmr-camion -i sparse - -S - | gzip > {file_base}.sparse.gz') 15 | 16 | -------------------------------------------------------------------------------- /experiments/gen_rndcamion.sbatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -c 1 # Number of cores reserved for each task 3 | #SBATCH --mem=1G # Memory reserved for each task 4 | #SBATCH --mail-type=END,FAIL # When to send an email 5 | #SBATCH -J gen_rndcamion 6 | #SBATCH --output=rndcamion/gen-%A.log 7 | #SBATCH --time=8:00:00 # Time limit 8 | 9 | 10 | HOSTNAME=`hostname` 11 | NUM_CPUS=`lscpu | egrep '^CPU\(s\):' | cut -d ':' -f2- | sed -e 's/^[[:space:]]*//'` 12 | NUM_CORES=`egrep '^cpu cores' /proc/cpuinfo | uniq | cut -d ':' -f2 | cut -d ' ' -f2-` 13 | MEM=`lsmem | egrep 'Total online memory:' | cut -d: -f2- | sed -e 's/^[[:space:]]*//'` 14 | echo "Run on ${HOSTNAME} which has ${NUM_CPUS} CPUs (${NUM_CORES} cores) and ${MEM} memory." 15 | 16 | python3 gen_rndcamion.py 17 | -------------------------------------------------------------------------------- /experiments/graphic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | 6 | GENERATOR = '../build-release/cmr-generate-graphic' 7 | 8 | # Parameter of script: #rows and number of repetitions 9 | try: 10 | numRows = int(sys.argv[1]) 11 | numRepetitions = int(sys.argv[2]) 12 | except: 13 | print(f'Usage: {sys.argv[0]} #ROWS NUM-REPETITIONS') 14 | sys.exit(1) 15 | 16 | sys.stdout.write('rows,cols,#nzs,tTrans,tCheck,tApply,tTotal\n') 17 | sys.stdout.flush() 18 | 19 | def run(numRows, numColumns, repetitions): 20 | command = [GENERATOR, str(int(numRows)), str(int(numColumns)), '-b', str(repetitions)] 21 | process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 22 | timeTranspose = 0.0 23 | timeCheck = 0.0 24 | timeApply = 0.0 25 | timeTotal = 0.0 26 | avgNonzeros = 0 27 | for line in process.stdout.decode('utf-8').split('\n'): 28 | if line.startswith('Generated a'): 29 | avgNonzeros += int(line.split(' ')[5].strip()) 30 | if line.startswith('Transposition:'): 31 | timeTranspose += float(line.split('/')[1].strip()) 32 | if line.startswith('Check:'): 33 | timeCheck += float(line.split('/')[1].strip()) 34 | if line.startswith('Apply:'): 35 | timeApply += float(line.split('/')[1].strip()) 36 | if line.startswith('Total:'): 37 | timeTotal += float(line.split('/')[1].strip()) 38 | timeTranspose /= repetitions 39 | timeCheck /= repetitions 40 | timeApply /= repetitions 41 | timeTotal /= repetitions 42 | avgNonzeros /= repetitions 43 | sys.stdout.write(f'{numRows:.0f},{numColumns:.0f},{avgNonzeros:.1f},{timeTranspose},{timeCheck},{timeApply},{timeTotal}\n') 44 | sys.stdout.flush() 45 | 46 | run(numRows, int(4.0*numRows), numRepetitions) 47 | sys.exit(0) 48 | 49 | run(numRows, int(0.5*numRows), numRepetitions) 50 | run(numRows, int(1.0*numRows), numRepetitions) 51 | run(numRows, int(1.5*numRows), numRepetitions) 52 | run(numRows, int(2.0*numRows), numRepetitions) 53 | run(numRows, int(2.5*numRows), numRepetitions) 54 | run(numRows, int(3.0*numRows), numRepetitions) 55 | run(numRows, int(3.5*numRows), numRepetitions) 56 | run(numRows, int(4.0*numRows), numRepetitions) 57 | run(numRows, int(4.5*numRows), numRepetitions) 58 | run(numRows, int(5.0*numRows), numRepetitions) 59 | run(numRows, int(5.5*numRows), numRepetitions) 60 | run(numRows, int(6.0*numRows), numRepetitions) 61 | run(numRows, int(6.5*numRows), numRepetitions) 62 | run(numRows, int(7.0*numRows), numRepetitions) 63 | run(numRows, int(7.5*numRows), numRepetitions) 64 | run(numRows, int(8.0*numRows), numRepetitions) 65 | 66 | -------------------------------------------------------------------------------- /experiments/run_modwheel.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | from datetime import datetime 6 | 7 | from config_modwheel import * 8 | 9 | assert os.path.exists(INSTANCE_DIRECTORY) 10 | 11 | sample = int(sys.argv[1]) 12 | sampleStorage = f'{LOCAL_STORAGE}/modwheel_{sample}/' 13 | 14 | os.system(f'mkdir -p {sampleStorage}') 15 | 16 | def call(command): 17 | # print('[' + command + ']') 18 | os.system(command) 19 | 20 | for instance in INSTANCES: 21 | file_base = f'{INSTANCE_DIRECTORY}/modwheel-{instance:05d}x{instance:05d}#{sample:03d}' 22 | 23 | print(f'Considering {file_base} at {datetime.now()}.', flush=True) 24 | 25 | # Run cmr-tu. 26 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo decomposition --time-limit 3600 1> {file_base}-cmrdec.out 2> {file_base}-cmrdec.err') 27 | if instance <= 50: 28 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo eulerian --time-limit 3600 1> {file_base}-cmreuler.out 2> {file_base}-cmreuler.err') 29 | if instance <= 50: 30 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo partition --time-limit 3600 1> {file_base}-cmrpart.out 2> {file_base}-cmrpart.err') 31 | 32 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo decomposition --time-limit 3600 -N {file_base}-cmrcert.sub 1> {file_base}-cmrcert.out 2> {file_base}-cmrcert.err') 33 | 34 | # Run unimodularity-test. 35 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-matrix - -i sparse -o dense {sampleStorage}/input.dense') 36 | call(f'{UNIMOD_DIRECTORY}/unimodularity-test {sampleStorage}/input.dense -s 2> /dev/null | egrep \'^[ 0-9-]*$\' 1> {sampleStorage}/signed.dense') 37 | call(f'{UNIMOD_DIRECTORY}/unimodularity-test {sampleStorage}/signed.dense -t -v 1> {file_base}-unimod.out 2> {file_base}-unimod.err') 38 | if instance <= 199: 39 | call(f'{UNIMOD_DIRECTORY}/unimodularity-test {sampleStorage}/signed.dense -t -v -c 1> {file_base}-unimodcert.out 2> {file_base}-unimodcert.err') 40 | 41 | os.system(f'rm -r {sampleStorage}') 42 | 43 | -------------------------------------------------------------------------------- /experiments/run_modwheel.sbatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -c 24 # Number of cores reserved for each task 3 | #SBATCH --mem=20G # Memory reserved for each task 4 | #SBATCH --mail-type=END,FAIL # When to send an email 5 | #SBATCH -J run_modwheel 6 | #SBATCH --output=modwheel/run-%A.log 7 | #SBATCH --constraint=titan-x 8 | #SBATCH --time=72:00:00 # Time limit 9 | 10 | 11 | HOSTNAME=`hostname` 12 | NUM_CPUS=`lscpu | egrep '^CPU\(s\):' | cut -d ':' -f2- | sed -e 's/^[[:space:]]*//'` 13 | NUM_CORES=`egrep '^cpu cores' /proc/cpuinfo | uniq | cut -d ':' -f2 | cut -d ' ' -f2-` 14 | MEM=`lsmem | egrep 'Total online memory:' | cut -d: -f2- | sed -e 's/^[[:space:]]*//'` 15 | echo "Run on ${HOSTNAME} which has ${NUM_CPUS} CPUs (${NUM_CORES} cores) and ${MEM} memory." 16 | 17 | python3 run_modwheel.py 1 & 18 | pid1=$! 19 | 20 | python3 run_modwheel.py 2 & 21 | pid2=$! 22 | 23 | python3 run_modwheel.py 3 & 24 | pid3=$! 25 | 26 | python3 run_modwheel.py 4 & 27 | pid4=$! 28 | 29 | python3 run_modwheel.py 5 & 30 | pid5=$! 31 | 32 | python3 run_modwheel.py 6 & 33 | pid6=$! 34 | 35 | python3 run_modwheel.py 7 & 36 | pid7=$! 37 | 38 | python3 run_modwheel.py 8 & 39 | pid8=$! 40 | 41 | python3 run_modwheel.py 9 & 42 | pid9=$! 43 | 44 | python3 run_modwheel.py 10 & 45 | pid10=$! 46 | 47 | wait $pid1 $pid2 $pid3 $pid4 $pid5 $pid6 $pid7 $pid8 $pid9 $pid10 48 | 49 | -------------------------------------------------------------------------------- /experiments/run_network.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | 6 | from config_network import * 7 | 8 | assert os.path.exists(INSTANCE_DIRECTORY) 9 | 10 | sample = int(sys.argv[1]) 11 | sampleStorage = f'{LOCAL_STORAGE}/network_{sample}/' 12 | 13 | def call(command): 14 | # print('[' + command + ']') 15 | os.system(command) 16 | 17 | for instance in INSTANCES: 18 | file_base = f'{INSTANCE_DIRECTORY}/network-{order:05d}x{order:05d}#{sample:03d}' 19 | 20 | print(f'Considering {file_base}.') 21 | 22 | # Run cmr-tu. 23 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo decomposition --time-limit 3600 1> {file_base}-cmrdec.out 2> {file_base}-cmrdec.err') 24 | if order <= 20: 25 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo eulerian --time-limit 3600 1> {file_base}-cmreuler.out 2> {file_base}-cmreuler.err') 26 | if order <= 30: 27 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo partition --time-limit 3600 1> {file_base}-cmrpart.out 2> {file_base}-cmrpart.err') 28 | 29 | # Run unimodularity-test. 30 | if order <= 4000: 31 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-matrix - -i sparse -o dense {sampleStorage}/input.dense') 32 | call(f'{UNIMOD_DIRECTORY}/unimodularity-test {sampleStorage}/input.dense -t -v 1> {file_base}-unimod.out 2> {file_base}-unimod.err') 33 | 34 | os.system(f'rm -r {sampleStorage}') 35 | 36 | -------------------------------------------------------------------------------- /experiments/run_network.sbatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -c 1 # Number of cores reserved for each task 3 | #SBATCH --mem=40G # Memory reserved for each task 4 | #SBATCH --mail-type=END,FAIL # When to send an email 5 | #SBATCH -J run_network 6 | #SBATCH --output=network/run-%A.log 7 | #SBATCH --constraint=titan-x 8 | #SBATCH --time=168:00:00 # Time limit 9 | 10 | 11 | HOSTNAME=`hostname` 12 | NUM_CPUS=`lscpu | egrep '^CPU\(s\):' | cut -d ':' -f2- | sed -e 's/^[[:space:]]*//'` 13 | NUM_CORES=`egrep '^cpu cores' /proc/cpuinfo | uniq | cut -d ':' -f2 | cut -d ' ' -f2-` 14 | MEM=`lsmem | egrep 'Total online memory:' | cut -d: -f2- | sed -e 's/^[[:space:]]*//'` 15 | echo "Run on ${HOSTNAME} which has ${NUM_CPUS} CPUs (${NUM_CORES} cores) and ${MEM} memory." 16 | 17 | for SAMPLE in `seq 1 10`; do 18 | python3 run_network.py ${SAMPLE} 19 | done 20 | -------------------------------------------------------------------------------- /experiments/run_rndcamion.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | from datetime import datetime 6 | 7 | from config_rndcamion import * 8 | 9 | assert os.path.exists(INSTANCE_DIRECTORY) 10 | 11 | sample = int(sys.argv[1]) 12 | sampleStorage = f'{LOCAL_STORAGE}/rndcamion_{sample}/' 13 | 14 | os.system(f'mkdir -p {sampleStorage}') 15 | 16 | 17 | 18 | def call(command): 19 | # print('[' + command + ']') 20 | os.system(command) 21 | 22 | for instance in INSTANCES: 23 | order,p = instance 24 | file_base = f'{INSTANCE_DIRECTORY}/rndcamion-{order:05d}x{order:05d}-p{p:0.2f}#{sample:03d}' 25 | 26 | print(f'Considering {file_base} at {datetime.now()}.', flush=True) 27 | 28 | # Run cmr-tu. 29 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo decomposition --time-limit 3600 1> {file_base}-cmrdec.out 2> {file_base}-cmrdec.err') 30 | if order <= 600: 31 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo eulerian --time-limit 3600 1> {file_base}-cmreuler.out 2> {file_base}-cmreuler.err') 32 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo partition --time-limit 3600 1> {file_base}-cmrpart.out 2> {file_base}-cmrpart.err') 33 | 34 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-tu - -i sparse --stats --algo decomposition --time-limit 3600 -N {file_base}-cmrcert.sub 1> {file_base}-cmrcert.out 2> {file_base}-cmrcert.err') 35 | 36 | # Run unimodularity-test. 37 | call(f'gunzip -cd {file_base}.sparse.gz | {BUILD_DIRECTORY}/cmr-matrix - -i sparse -o dense {sampleStorage}/input.dense') 38 | call(f'{UNIMOD_DIRECTORY}/unimodularity-test {sampleStorage}/input.dense -s 2> /dev/null | egrep \'^[ 0-9-]*$\' 1> {sampleStorage}/signed.dense') 39 | call(f'{UNIMOD_DIRECTORY}/unimodularity-test {sampleStorage}/signed.dense -t -v 1> {file_base}-unimod.out 2> {file_base}-unimod.err') 40 | if order <= 1000: 41 | call(f'{UNIMOD_DIRECTORY}/unimodularity-test {sampleStorage}/signed.dense -t -v -c 1> {file_base}-unimodcert.out 2> {file_base}-unimodcert.err') 42 | 43 | os.system(f'rm -r {sampleStorage}') 44 | 45 | -------------------------------------------------------------------------------- /experiments/run_rndcamion.sbatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -c 24 # Number of cores reserved for each task 3 | #SBATCH --mem=200G # Memory reserved for each task 4 | #SBATCH --mail-type=END,FAIL # When to send an email 5 | #SBATCH -J run_rndcamion 6 | #SBATCH --output=rndcamion/run-%A.log 7 | #SBATCH --constraint=titan-x 8 | #SBATCH --time=48:00:00 # Time limit 9 | 10 | 11 | HOSTNAME=`hostname` 12 | NUM_CPUS=`lscpu | egrep '^CPU\(s\):' | cut -d ':' -f2- | sed -e 's/^[[:space:]]*//'` 13 | NUM_CORES=`egrep '^cpu cores' /proc/cpuinfo | uniq | cut -d ':' -f2 | cut -d ' ' -f2-` 14 | MEM=`lsmem | egrep 'Total online memory:' | cut -d: -f2- | sed -e 's/^[[:space:]]*//'` 15 | echo "Run on ${HOSTNAME} which has ${NUM_CPUS} CPUs (${NUM_CORES} cores) and ${MEM} memory." 16 | 17 | python3 run_rndcamion.py 1 & 18 | pid1=$! 19 | 20 | python3 run_rndcamion.py 2 & 21 | pid2=$! 22 | 23 | python3 run_rndcamion.py 3 & 24 | pid3=$! 25 | 26 | python3 run_rndcamion.py 4 & 27 | pid4=$! 28 | 29 | python3 run_rndcamion.py 5 & 30 | pid5=$! 31 | 32 | python3 run_rndcamion.py 6 & 33 | pid6=$! 34 | 35 | python3 run_rndcamion.py 7 & 36 | pid7=$! 37 | 38 | python3 run_rndcamion.py 8 & 39 | pid8=$! 40 | 41 | python3 run_rndcamion.py 9 & 42 | pid9=$! 43 | 44 | python3 run_rndcamion.py 10 & 45 | pid10=$! 46 | 47 | wait $pid1 $pid2 $pid3 $pid4 $pid5 $pid6 $pid7 $pid8 $pid9 $pid10 48 | 49 | -------------------------------------------------------------------------------- /experiments/series_parallel.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import math 4 | import subprocess 5 | 6 | GENERATOR = '../build-release/cmr-generate-series-parallel' 7 | 8 | # Parameter of script: max amount of GB to be used. 9 | try: 10 | maxMemory = float(sys.argv[1]) * 1024 * 1024 * 1024 11 | numRepetitions = int(sys.argv[2]) 12 | except: 13 | print(f'Usage: {sys.argv[0]} MAX-MEMORY-IN-GIGABYTES NUM-REPETITIONS') 14 | sys.exit(1) 15 | maxNumNonzeros = maxMemory / 45 # This is the approximate number of bytes required per nonzero. 16 | 17 | bitsNonzeros = int(round(math.log(maxNumNonzeros) / math.log(2), 0)) 18 | 19 | sys.stdout.write('rTot,cTot,rBase,cBase,rZero,cZero,rUnit,cUnit,rCopy,cCopy,sp,tRed,tWheel,tTern,tTotal,#nzs\n') 20 | sys.stdout.flush() 21 | 22 | def run(sparsity, numBaseRows, numBaseColumns, numZeroRows, numZeroColumns, numUnitRows, numUnitColumns, numCopiedRows, numCopiedColumns, ternary, repetitions): 23 | command = [GENERATOR, str(int(numBaseRows)), str(int(numBaseColumns)), '-z', str(int(numZeroRows)), str(int(numZeroColumns)), '-u', str(int(numUnitRows)), str(int(numUnitColumns)), '-c', str(int(numCopiedRows)), str(int(numCopiedColumns)), '-s', str(sparsity), '-b', str(repetitions)] 24 | if ternary: 25 | command.append('-t') 26 | process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 27 | timeReductions = 0.0 28 | timeWheel = 0.0 29 | timeTernary = 0.0 30 | timeTotal = 0.0 31 | avgNonzeros = None 32 | for line in process.stdout.decode('utf-8').split('\n'): 33 | if line.startswith('Search for reductions:'): 34 | timeReductions = float(line.split('/')[1].strip()) 35 | if line.startswith('Search for wheel matrices:'): 36 | timeWheel = float(line.split('/')[1].strip()) 37 | if line.startswith('Search for ternary certificate:'): 38 | timeTernary = float(line.split('/')[1].strip()) 39 | if line.startswith('Total:'): 40 | timeTotal = float(line.split('/')[1].strip()) 41 | if line.startswith('Average number of nonzeros:'): 42 | avgNonzeros = float(line.split(':')[1].strip()) 43 | timeReductions /= repetitions 44 | timeWheel /= repetitions 45 | timeTernary /= repetitions 46 | timeTotal /= repetitions 47 | sys.stdout.write(f'{numBaseRows+numZeroRows+numUnitRows+numCopiedRows:.0f},{numBaseColumns+numZeroColumns+numUnitColumns+numCopiedColumns:.0f},{numBaseRows},{numBaseColumns},{numZeroRows},{numZeroColumns},{numUnitRows},{numUnitColumns},{numCopiedRows},{numCopiedColumns},{sparsity},{timeReductions},{timeWheel},{timeTernary},{timeTotal},{avgNonzeros}\n') 48 | sys.stdout.flush() 49 | 50 | for ternary in [False, True]: 51 | 52 | # Different portions of unit/copied. 53 | if True: 54 | size = 2**(bitsNonzeros-16) 55 | run(1, 1, 1, 0, 0, 1.0*size-1, 1.0*size-1, 0, 0, ternary, numRepetitions) 56 | run(1, 1, 1, 0, 0, 0.9*size-1, 0.9*size-1, 0.1*size, 0.1*size, ternary, numRepetitions) 57 | run(1, 1, 1, 0, 0, 0.8*size-1, 0.8*size-1, 0.2*size, 0.2*size, ternary, numRepetitions) 58 | run(1, 1, 1, 0, 0, 0.7*size-1, 0.7*size-1, 0.3*size, 0.3*size, ternary, numRepetitions) 59 | run(1, 1, 1, 0, 0, 0.6*size-1, 0.6*size-1, 0.4*size, 0.4*size, ternary, numRepetitions) 60 | run(1, 1, 1, 0, 0, 0.5*size-1, 0.5*size-1, 0.5*size, 0.5*size, ternary, numRepetitions) 61 | run(1, 1, 1, 0, 0, 0.4*size-1, 0.4*size-1, 0.6*size, 0.6*size, ternary, numRepetitions) 62 | run(1, 1, 1, 0, 0, 0.3*size-1, 0.3*size-1, 0.7*size, 0.7*size, ternary, numRepetitions) 63 | run(1, 1, 1, 0, 0, 0.2*size-1, 0.2*size-1, 0.8*size, 0.8*size, ternary, numRepetitions) 64 | run(1, 1, 1, 0, 0, 0.1*size-1, 0.1*size-1, 0.9*size, 0.9*size, ternary, numRepetitions) 65 | run(1, 1, 1, 0, 0, 0, 0, 1.0*size-1, 1.0*size-1, ternary, numRepetitions) 66 | sys.stdout.write('\n') 67 | 68 | if True: 69 | # Different portion of base. 70 | size = 2**(bitsNonzeros-16) 71 | run(1, 1, 1, 0, 0, 0.5*size-1, 0.5*size-1, 0.5*size, 0.5*size, ternary, numRepetitions) 72 | run(0.05*size, 0.1*size, 0.1*size, 0, 0, 0.45*size, 0.45*size, 0.45*size, 0.45*size, ternary, numRepetitions) 73 | run(0.10*size, 0.2*size, 0.2*size, 0, 0, 0.40*size, 0.40*size, 0.40*size, 0.40*size, ternary, numRepetitions) 74 | run(0.15*size, 0.3*size, 0.3*size, 0, 0, 0.35*size, 0.35*size, 0.35*size, 0.35*size, ternary, numRepetitions) 75 | run(0.20*size, 0.4*size, 0.4*size, 0, 0, 0.30*size, 0.30*size, 0.30*size, 0.30*size, ternary, numRepetitions) 76 | run(0.25*size, 0.5*size, 0.5*size, 0, 0, 0.25*size, 0.25*size, 0.25*size, 0.25*size, ternary, numRepetitions) 77 | run(0.30*size, 0.6*size, 0.6*size, 0, 0, 0.20*size, 0.20*size, 0.20*size, 0.20*size, ternary, numRepetitions) 78 | run(0.35*size, 0.7*size, 0.7*size, 0, 0, 0.15*size, 0.15*size, 0.15*size, 0.15*size, ternary, numRepetitions) 79 | run(0.40*size, 0.8*size, 0.8*size, 0, 0, 0.10*size, 0.10*size, 0.10*size, 0.10*size, ternary, numRepetitions) 80 | run(0.45*size, 0.9*size, 0.9*size, 0, 0, 0.05*size, 0.05*size, 0.05*size, 0.05*size, ternary, numRepetitions) 81 | run(0.50*size, 1.0*size, 1.0*size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 82 | sys.stdout.write('\n') 83 | 84 | if True: 85 | # Different densities of full base. 86 | size = 2**(bitsNonzeros-16) 87 | run(0.0*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 88 | run(0.1*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 89 | run(0.2*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 90 | run(0.3*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 91 | run(0.4*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 92 | run(0.5*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 93 | run(0.6*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 94 | run(0.7*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 95 | run(0.8*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 96 | run(0.9*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 97 | run(1.0*size, size, size, 0, 0, 0, 0, 0, 0, ternary, numRepetitions) 98 | sys.stdout.write('\n') 99 | 100 | -------------------------------------------------------------------------------- /include/cmr/balanced.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_BALANCED_H 2 | #define CMR_BALANCED_H 3 | 4 | /** 5 | * \file balanced.h 6 | * 7 | * \author Henk Kraaij and Matthias Walter 8 | * 9 | * \brief Recognition of [balanced matrices](\ref balanced). 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | typedef enum 23 | { 24 | CMR_BALANCED_ALGORITHM_AUTO = 0, /**< \brief Automatically select a fast algorithm. */ 25 | CMR_BALANCED_ALGORITHM_SUBMATRIX = 1, /**< \brief Exponential-time enumeration algorithm based on submatrices. */ 26 | CMR_BALANCED_ALGORITHM_GRAPH = 2 /**< \brief Polynomial-time algorithm based on graphs. */ 27 | } CMR_BALANCED_ALGORITHM; 28 | 29 | typedef struct 30 | { 31 | CMR_BALANCED_ALGORITHM algorithm; /**< \brief Algorithm to use. */ 32 | bool seriesParallel; /**< \brief Whether to carry out series-parallel operations as preprocessing. */ 33 | } CMR_BALANCED_PARAMS; 34 | 35 | /** 36 | * \brief Initializes the default parameters for recognition of [balanced](\ref balanced) matrices. 37 | */ 38 | 39 | CMR_EXPORT 40 | CMR_ERROR CMRbalancedParamsInit( 41 | CMR_BALANCED_PARAMS* params /**< Pointer to parameters. */ 42 | ); 43 | 44 | /** 45 | * \brief Statistics for recognition algorithm for [balanced](\ref balanced) matrices. 46 | */ 47 | 48 | typedef struct 49 | { 50 | uint32_t totalCount; /**< Total number of invocations. */ 51 | double totalTime; /**< Total time of all invocations. */ 52 | CMR_SP_STATISTICS seriesParallel; /**< Statistics for series-parallel algorithm. */ 53 | size_t enumeratedRowSubsets; /**< Number of enumerated row subsets. */ 54 | size_t enumeratedColumnSubsets; /**< Number of enumerated column subsets. */ 55 | } CMR_BALANCED_STATS; 56 | 57 | /** 58 | * \brief Initializes all statistics for recognition algorithm for [balanced](\ref balanced) matrices. 59 | */ 60 | 61 | CMR_EXPORT 62 | CMR_ERROR CMRbalancedStatsInit( 63 | CMR_BALANCED_STATS* stats /**< Pointer to statistics. */ 64 | ); 65 | 66 | /** 67 | * \brief Prints statistics for recognition algorithm for [balanced](\ref balanced) matrices. 68 | */ 69 | 70 | CMR_EXPORT 71 | CMR_ERROR CMRbalancedStatsPrint( 72 | FILE* stream, /**< File stream to print to. */ 73 | CMR_BALANCED_STATS* stats, /**< Pointer to statistics. */ 74 | const char* prefix /**< Prefix string to prepend to each printed line (may be \c NULL). */ 75 | ); 76 | 77 | /** 78 | * \brief Tests a matrix \f$ M \f$ for being [balanced](\ref balanced). 79 | * 80 | * Tests if matrix \f$ M \f$ is balanced and sets \p *pisBalanced accordingly. 81 | * Automatically decides which algorithm to use. 82 | * 83 | * If \f$ M \f$ is not balanced and \p psubmatrix != \c NULL, then \p *psubmatrix will indicate a submatrix 84 | * of \f$ M \f$ with exactly two nonzeros in each row and in each column and with determinant \f$ -2 \f$ or \f$ 2 \f$. 85 | */ 86 | 87 | CMR_EXPORT 88 | CMR_ERROR CMRbalancedTest( 89 | CMR* cmr, /**< \ref CMR environment */ 90 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$. */ 91 | bool* pisBalanced, /**< Pointer for storing whether \f$ M \f$ is balanced. */ 92 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a minimal nonbalanced submatrix (may be \c NULL). */ 93 | CMR_BALANCED_PARAMS* params, /**< Parameters for the computation (may be \c NULL for defaults). */ 94 | CMR_BALANCED_STATS* stats, /**< Statistics for the computation (may be \c NULL). */ 95 | double timeLimit /**< Time limit to impose. */ 96 | ); 97 | 98 | #ifdef __cplusplus 99 | } 100 | #endif 101 | 102 | #endif /* CMR_BALANCED_H */ 103 | -------------------------------------------------------------------------------- /include/cmr/camion.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_CAMION_H 2 | #define CMR_CAMION_H 3 | 4 | /** 5 | * \file camion.h 6 | * 7 | * \author Matthias Walter 8 | * 9 | * \brief Testing whether a matrix is [Camion-signed](\ref camion). 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | /** 22 | * \brief Statistics for [Camion-signing](\ref camion) algorithm. 23 | */ 24 | 25 | typedef struct 26 | { 27 | uint32_t generalCount; /**< Number of invocations for general matrices. */ 28 | double generalTime; /**< Total time for all invocations for general matrices. */ 29 | uint32_t graphCount; /**< Number of invocations for graphic matrices. */ 30 | double graphTime; /**< Total time for all invocations for graphic matrices. */ 31 | uint32_t totalCount; /**< Total number of invocations. */ 32 | double totalTime; /**< Total time for all invocations. */ 33 | } CMR_CAMION_STATISTICS; 34 | 35 | /** 36 | * \brief Initializes all statistics for [Camion-signing](\ref camion) algorithm. 37 | */ 38 | 39 | CMR_EXPORT 40 | CMR_ERROR CMRcamionStatsInit( 41 | CMR_CAMION_STATISTICS* stats /**< Pointer to statistics. */ 42 | ); 43 | 44 | /** 45 | * \brief Prints statistics for [Camion-signing](\ref camion) algorithm. 46 | */ 47 | 48 | CMR_EXPORT 49 | CMR_ERROR CMRcamionStatsPrint( 50 | FILE* stream, /**< File stream to print to. */ 51 | CMR_CAMION_STATISTICS* stats, /**< Pointer to statistics. */ 52 | const char* prefix /**< Prefix string to prepend to each printed line (may be \c NULL). */ 53 | ); 54 | 55 | 56 | /** 57 | * \brief Tests a matrix \f$ M \f$ for being a [Camion-signed](\ref camion). 58 | */ 59 | 60 | CMR_EXPORT 61 | CMR_ERROR CMRcamionTestSigns( 62 | CMR* cmr, /**< \ref CMR environment. */ 63 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$. */ 64 | bool* pisCamionSigned, /**< Pointer for storing whether \f$ M \f$ is [Camion-signed](\ref camion). */ 65 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a non-Camion submatrix (may be \c NULL). */ 66 | CMR_CAMION_STATISTICS* stats, /**< Statistics for the computation (may be \c NULL). */ 67 | double timeLimit /**< Time limit to impose. */ 68 | ); 69 | 70 | /** 71 | * \brief Computes a [Camion-signed](\ref camion) version of a given ternary matrix \f$ M \f$. 72 | */ 73 | 74 | CMR_EXPORT 75 | CMR_ERROR CMRcamionComputeSigns( 76 | CMR* cmr, /**< \ref CMR environment. */ 77 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$ to be modified. */ 78 | bool* pwasCamionSigned, /**< Pointer for storing whether \f$ M \f$ was already [Camion-signed](\ref camion). */ 79 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a non-Camion submatrix (may be \c NULL). */ 80 | CMR_CAMION_STATISTICS* stats, /**< Statistics for the computation (may be \c NULL). */ 81 | double timeLimit /**< Time limit to impose. */ 82 | ); 83 | 84 | 85 | /** 86 | * \brief Orients the edges of the graph \p cograph such that the matrix \p matrix \f$ M \f$ is the corresponding 87 | * network matrix, which implicitly tests if \p matrix is [Camion-signed](\ref camion). 88 | * 89 | * The cograph \f$ G = (V,E) \f$ has a spanning tree \f$ T \subseteq E \f$ indexed by the columns of \f$ M \f$. 90 | * Its complement \f$ E \setminus T \f$ is indexed by the rows of \f$ M \f$. The function assumes that 91 | * \f$ supp(M) = M(G,T)^\textsf{T} \f$ holds and attempts to compute an orientation \f$ A \f$ of the edges \f$ E \f$ 92 | * (which is stored in \p arcsReversed) that corresponds to the signs of \f$ M \f$. \p *pisCamionSigned indicates 93 | * success. If successful, \f$ M \f$ is the network matrix of the digraph \f$ D = (V,A) \f$. Otherwise, 94 | * \p *psubmatrix will indicate a violating submatrix (if not \c NULL). 95 | */ 96 | 97 | CMR_EXPORT 98 | CMR_ERROR CMRcamionCographicOrient( 99 | CMR* cmr, /**< \ref CMR environment. */ 100 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$. */ 101 | CMR_GRAPH* cograph, /**< Cograph \f$ G = (V,E) \f$ claimed to correspond to \f$ M \f$. */ 102 | CMR_GRAPH_EDGE* forestEdges, /**< \f$ T \f$, ordered by the columns of \f$ M \f$. */ 103 | CMR_GRAPH_EDGE* coforestEdges, /**< \f$ E \setminus T \f$, ordered by the rows of \f$ M \f$. */ 104 | bool* arcsReversed, /**< Indicates, for each edge \f$ \{u, v\} \in E\f$, whether \f$ (u,v) \in A \f$ 105 | ** (if \c false) or \f$ (v,u) \in A \f$ (if \c true). */ 106 | bool* pisCamionSigned, /**< Pointer for storing whether \f$ M \f$ is [Camion-signed](\ref camion). */ 107 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a non-Camion submatrix (may be \c NULL). */ 108 | CMR_CAMION_STATISTICS* stats /**< Statistics for the computation (may be \c NULL). */ 109 | ); 110 | 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif 115 | 116 | #endif /* CMR_CAMION_H */ 117 | -------------------------------------------------------------------------------- /include/cmr/config.h.in: -------------------------------------------------------------------------------- 1 | #define CMR_CMAKE_BUILD_TYPE "@CMAKE_BUILD_TYPE@" 2 | #define CMR_VERSION_MAJOR @CMR_VERSION_MAJOR@ 3 | #define CMR_VERSION_MINOR @CMR_VERSION_MINOR@ 4 | #define CMR_VERSION_PATCH @CMR_VERSION_PATCH@ 5 | 6 | #cmakedefine CMR_WITH_GMP 7 | -------------------------------------------------------------------------------- /include/cmr/ctu.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_CTU_H 2 | #define CMR_CTU_H 3 | 4 | /** 5 | * \file ctu.h 6 | * 7 | * \author Matthias Walter and Klaus Truemper 8 | * 9 | * \brief Recognition of [complement totally unimodular matrices](\ref ctu). 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | typedef struct 21 | { 22 | CMR_TU_PARAMS tu; /**< \brief Parameters for TU test. */ 23 | } CMR_CTU_PARAMS; 24 | 25 | /** 26 | * \brief Initializes the default parameters for recognition of [complement totally unimodular](\ref ctu) matrices. 27 | * 28 | * These are selected for minimum running time. 29 | */ 30 | 31 | CMR_EXPORT 32 | CMR_ERROR CMRctuParamsInit( 33 | CMR_CTU_PARAMS* params /**< Pointer to parameters. */ 34 | ); 35 | 36 | /** 37 | * \brief Statistics for recognition algorithm for [totally unimodular](\ref tu) matrices. 38 | */ 39 | 40 | typedef struct 41 | { 42 | uint32_t totalCount; /**< Total number of invocations. */ 43 | double totalTime; /**< Total time of all invocations. */ 44 | CMR_TU_STATS tu; /**< Total unimodularity test. */ 45 | } CMR_CTU_STATISTICS; 46 | 47 | /** 48 | * \brief Initializes all statistics for recognition algorithm for [complement totally unimodular](\ref ctu) matrices. 49 | */ 50 | 51 | CMR_EXPORT 52 | CMR_ERROR CMRstatsComplementTotalUnimodularityInit( 53 | CMR_CTU_STATISTICS* stats /**< Pointer to statistics. */ 54 | ); 55 | 56 | /** 57 | * \brief Prints statistics for recognition algorithm for [complement totally unimodular](\ref ctu) matrices. 58 | */ 59 | 60 | CMR_EXPORT 61 | CMR_ERROR CMRstatsComplementTotalUnimodularityPrint( 62 | FILE* stream, /**< File stream to print to. */ 63 | CMR_CTU_STATISTICS* stats, /**< Pointer to statistics. */ 64 | const char* prefix /**< Prefix string to prepend to each printed line (may be \c NULL). */ 65 | ); 66 | 67 | /** 68 | * \brief Carries out a row- and column-complement operations on the binary matrix. 69 | */ 70 | 71 | CMR_EXPORT 72 | CMR_ERROR CMRctuComplementRowColumn( 73 | CMR* cmr, /**< \ref CMR environment */ 74 | CMR_CHRMAT* matrix, /**< Input matrix. */ 75 | size_t complementRow, /**< Row to be complemented (\c SIZE_MAX for no row complement). */ 76 | size_t complementColumn, /**< Column to be complemented (\c SIZE_MAX for no column complement). */ 77 | CMR_CHRMAT** presult /**< Resulting matrix. */ 78 | ); 79 | 80 | /** 81 | * \brief Tests a matrix \f$ M \f$ for being [complement totally unimodular](\ref ctu). 82 | * 83 | * Tests if matrix \f$ M \f$ is complement totally unimodular and sets \p *pisComplementTotallyUnimodular accordingly. 84 | * 85 | * If \f$ M \f$ is not complement totally unimodular and \p pcomplementRow != \c NULL and 86 | * \p pcomplementColumn != \c NULL, then \p *pcomplementRow and \p *pcomplementColumn will indicate the row and column 87 | * that need to be complemented for obtaining a matrix that is not [totally unimodular](\ref tu). 88 | * If no row/column needs to be complemented, then the respective variables are set to \c SIZE_MAX. 89 | */ 90 | 91 | CMR_EXPORT 92 | CMR_ERROR CMRctuTest( 93 | CMR* cmr, /**< \ref CMR environment */ 94 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$. */ 95 | bool* pisComplementTotallyUnimodular, /**< Pointer for storing whether \f$ M \f$ is complement totally unimodular. */ 96 | size_t* pcomplementRow, /**< Pointer for storing the row to be complemented (may be \c NULL). */ 97 | size_t* pcomplementColumn, /**< Pointer for storing the column to be complemented (may be \c NULL). */ 98 | CMR_CTU_PARAMS* params, /**< Parameters for the computation (may be \c NULL for defaults). */ 99 | CMR_CTU_STATISTICS* stats, /**< Statistics for the computation (may be \c NULL). */ 100 | double timeLimit /**< Time limit to impose. */ 101 | ); 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | #endif /* CMR_CTU_H */ 108 | -------------------------------------------------------------------------------- /include/cmr/element.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_ELEMENT_H 2 | #define CMR_ELEMENT_H 3 | 4 | /** 5 | * \file element.h 6 | * 7 | * \author Matthias Walter 8 | * 9 | * \brief Functionality for the row and column **elements** of a matrix. 10 | */ 11 | 12 | #include 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef int CMR_ELEMENT; 21 | 22 | CMR_EXPORT 23 | const char* CMRelementString( 24 | CMR_ELEMENT element, /**< Element to print. */ 25 | char* buffer /**< Buffer of size at least 32. May be \c NULL, in which case a static buffer is used. */ 26 | ); 27 | 28 | /** 29 | * \brief Returns \c true if \p element is a row or a column element. 30 | */ 31 | 32 | static inline 33 | bool CMRelementIsValid( 34 | CMR_ELEMENT element /**< Element to check for validity. */ 35 | ) 36 | { 37 | return element != 0; 38 | } 39 | 40 | static inline 41 | CMR_ELEMENT CMRrowToElement( 42 | size_t row /**< Row index. */ 43 | ) 44 | { 45 | return -1 - (int)row; 46 | } 47 | 48 | static inline 49 | CMR_ELEMENT CMRcolumnToElement( 50 | size_t column /**< Column index. */ 51 | ) 52 | { 53 | return 1 + (int)column; 54 | } 55 | 56 | static inline 57 | bool CMRelementIsRow( 58 | CMR_ELEMENT element /**< Element to check. */ 59 | ) 60 | { 61 | return element < 0; 62 | } 63 | 64 | static inline 65 | size_t CMRelementToRowIndex( 66 | CMR_ELEMENT element /**< Element to convert. */ 67 | ) 68 | { 69 | assert(element < 0); 70 | return -1 - element; 71 | } 72 | 73 | static inline 74 | bool CMRelementIsColumn( 75 | CMR_ELEMENT element /**< Element to check. */ 76 | ) 77 | { 78 | return element > 0; 79 | } 80 | 81 | static inline 82 | size_t CMRelementToColumnIndex( 83 | CMR_ELEMENT element /**< Element to convert. */ 84 | ) 85 | { 86 | assert(element > 0); 87 | return -1 + element; 88 | } 89 | 90 | /** 91 | * \brief Transposes \p element, i.e., turns rows into columns and vice versa. 92 | */ 93 | 94 | static inline 95 | CMR_ELEMENT CMRelementTranspose( 96 | CMR_ELEMENT element /**< Element to transpose. */ 97 | ) 98 | { 99 | return -element; 100 | } 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #endif /* CMR_ELEMENT_H */ 107 | -------------------------------------------------------------------------------- /include/cmr/linear_algebra.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_LINEAR_ALGEBRA_H 2 | #define CMR_LINEAR_ALGEBRA_H 3 | 4 | /** 5 | * \file linear_algebra.h 6 | * 7 | * \author Matthias Walter 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | /** 21 | * \brief Computes the determinant of an 8-bit integer matrix. 22 | */ 23 | 24 | CMR_EXPORT 25 | CMR_ERROR CMRchrmatDeterminant( 26 | CMR* cmr, /**< \ref CMR environment. */ 27 | CMR_CHRMAT* matrix, /**< Matrix. */ 28 | int64_t* pdeterminant /**< Pointer for storing the determinant. */ 29 | ); 30 | 31 | /** 32 | * \brief Computes the determinant of an int matrix. 33 | */ 34 | 35 | CMR_EXPORT 36 | CMR_ERROR CMRintmatDeterminant( 37 | CMR* cmr, /**< \ref CMR environment. */ 38 | CMR_INTMAT* matrix, /**< Matrix. */ 39 | int64_t* pdeterminant /**< Pointer for storing the determinant. */ 40 | ); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif /* CMR_LINEAR_ALGEBRA_H */ 47 | -------------------------------------------------------------------------------- /include/cmr/named.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_NAMED_H 2 | #define CMR_NAMED_H 3 | 4 | /** 5 | * \file special.h 6 | * 7 | * \author Matthias Walter 8 | * 9 | * \brief Functionality for special matrix. 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | /** 20 | * \brief Checks if the given \p matrix is an identity matrix. 21 | * 22 | * If \p matrix is not identity matrix then \p *porder indicates the order; Otherwise, it is set to \c SIZE_MAX. 23 | */ 24 | 25 | CMR_EXPORT 26 | CMR_ERROR CMRisIdentityMatrix( 27 | CMR* cmr, /**< \ref CMR environment. */ 28 | CMR_CHRMAT* matrix, /**< Matrix. */ 29 | size_t* porder /**< Pointer for storing the order of the matrix. */ 30 | ); 31 | 32 | /** 33 | * \brief Constructs an identity matrix of given \p order. 34 | */ 35 | 36 | CMR_EXPORT 37 | CMR_ERROR CMRcreateIdentityMatrix( 38 | CMR* cmr, /**< \ref CMR environment. */ 39 | size_t order, /**< Order of the matrix. */ 40 | CMR_CHRMAT** presult /**< Pointer for storing the matrix. */ 41 | ); 42 | 43 | /** 44 | * \brief Checks if the given \p matrix represents the matroid \f$ R_{10} \f$. 45 | */ 46 | 47 | CMR_EXPORT 48 | CMR_ERROR CMRisR10Matrix( 49 | CMR* cmr, /**< \ref CMR environment. */ 50 | CMR_CHRMAT* matrix, /**< Matrix. */ 51 | size_t* pisR10 /**< Pointer for storing the representation matrix index. */ 52 | ); 53 | 54 | /** 55 | * \brief Constructs a representation matrix for \f$ R_{10} \f$. 56 | */ 57 | 58 | CMR_EXPORT 59 | CMR_ERROR CMRcreateR10Matrix( 60 | CMR* cmr, /**< \ref CMR environment. */ 61 | size_t index, /**< Which of the two matrices to return; among {1,2}. */ 62 | CMR_CHRMAT** presult /**< Pointer for storing the matrix. */ 63 | ); 64 | 65 | /** 66 | * \brief Checks if the given \p matrix represents the matroid \f$ R_{12} \f$. 67 | */ 68 | 69 | CMR_EXPORT 70 | CMR_ERROR CMRisR12Matrix( 71 | CMR* cmr, /**< \ref CMR environment. */ 72 | CMR_CHRMAT* matrix, /**< Matrix. */ 73 | size_t* pisR12 /**< Pointer for storing the representation matrix index. */ 74 | ); 75 | 76 | /** 77 | * \brief Constructs a representation matrix for \f$ R_{12} \f$. 78 | */ 79 | 80 | CMR_EXPORT 81 | CMR_ERROR CMRcreateR12Matrix( 82 | CMR* cmr, /**< \ref CMR environment. */ 83 | size_t index, /**< Which of the matrices to return; must be 1. */ 84 | CMR_CHRMAT** presult /**< Pointer for storing the matrix. */ 85 | ); 86 | 87 | /** 88 | * \brief Constructs a representation matrix for \f$ M(K_5) \f$. 89 | */ 90 | 91 | CMR_EXPORT 92 | CMR_ERROR CMRcreateK5Matrix( 93 | CMR* cmr, /**< \ref CMR environment. */ 94 | size_t index, /**< Which of the matrices to return; must be 1. */ 95 | CMR_CHRMAT** presult /**< Pointer for storing the matrix. */ 96 | ); 97 | 98 | /** 99 | * \brief Constructs a representation matrix for \f$ M(K_{3,3}) \f$. 100 | */ 101 | 102 | CMR_EXPORT 103 | CMR_ERROR CMRcreateK33Matrix( 104 | CMR* cmr, /**< \ref CMR environment. */ 105 | size_t index, /**< Which of the matrices to return; must be 1. */ 106 | CMR_CHRMAT** presult /**< Pointer for storing the matrix. */ 107 | ); 108 | 109 | #ifdef __cplusplus 110 | } 111 | #endif 112 | 113 | #endif /* CMR_NAMED_H */ 114 | 115 | -------------------------------------------------------------------------------- /include/cmr/regular.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_REGULAR_H 2 | #define CMR_REGULAR_H 3 | 4 | /** 5 | * \file regular.h 6 | * 7 | * \author Matthias Walter and Klaus Truemper 8 | * 9 | * \brief Recognition of [binary regular matrices](\ref binary_regular). 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | typedef struct 26 | { 27 | CMR_SEYMOUR_PARAMS seymour; 28 | } CMR_REGULAR_PARAMS; 29 | 30 | /** 31 | * \brief Initializes the default parameters for regularity testing. 32 | * 33 | * These are selected for minimum running time. 34 | */ 35 | 36 | CMR_EXPORT 37 | CMR_ERROR CMRregularParamsInit( 38 | CMR_REGULAR_PARAMS* params /**< Pointer to parameters. */ 39 | ); 40 | 41 | /** 42 | * \brief Statistics for regular matroid recognition algorithm. 43 | */ 44 | 45 | typedef struct 46 | { 47 | CMR_SEYMOUR_STATS seymour; /**< Statistics for Seymour decomposition computations. */ 48 | } CMR_REGULAR_STATS; 49 | 50 | 51 | /** 52 | * \brief Initializes all statistics for regularity test computations. 53 | */ 54 | 55 | CMR_EXPORT 56 | CMR_ERROR CMRregularStatsInit( 57 | CMR_REGULAR_STATS* stats /**< Pointer to statistics. */ 58 | ); 59 | 60 | /** 61 | * \brief Prints statistics for regularity test computations. 62 | */ 63 | 64 | CMR_EXPORT 65 | CMR_ERROR CMRregularStatsPrint( 66 | FILE* stream, /**< File stream to print to. */ 67 | CMR_REGULAR_STATS* stats, /**< Pointer to statistics. */ 68 | const char* prefix /**< Prefix string to prepend to each printed line (may be \c NULL). */ 69 | ); 70 | 71 | /** 72 | * \brief Tests binary linear matroid for regularity. 73 | * 74 | * If \p pdec is not \c NULL, \c *pdec will be a (partial) decomposition tree. 75 | * If \p completeTree is \c true, then the decomposition tree is complete. Otherwise, it must only contain sufficient 76 | * information in order to determine regularity. 77 | * 78 | * If \p pminor is not \c NULL and \p matrix is not regular, then an \f$ F_7 \f$ or \f$ F_7^\star \f$ minor is searched. 79 | * This causes additional computational effort! 80 | */ 81 | 82 | CMR_EXPORT 83 | CMR_ERROR CMRregularTest( 84 | CMR* cmr, /**< \ref CMR environment. */ 85 | CMR_CHRMAT* matrix, /**< Input matrix. */ 86 | bool *pisRegular, /**< Pointer for storing whether \p matrix is regular. */ 87 | CMR_SEYMOUR_NODE** proot, /**< Pointer for storing the Seymour decomposition tree (may be \c NULL). */ 88 | CMR_MINOR** pminor, /**< Pointer for storing an \f$ F_7 \f$ or \f$ F_7^\star \f$ minor. */ 89 | CMR_REGULAR_PARAMS* params, /**< Parameters for the computation (may be \c NULL for defaults). */ 90 | CMR_REGULAR_STATS* stats, /**< Statistics for the computation (may be \c NULL). */ 91 | double timeLimit /**< Time limit to impose. */ 92 | ); 93 | 94 | /** 95 | * \brief Completes a subtree of an existing decomposition tree. 96 | * 97 | * Replace the node's subtree by a new one even if it exists. Note that different parameters may yield a different 98 | * subtree. 99 | */ 100 | 101 | CMR_EXPORT 102 | CMR_ERROR CMRregularCompleteDecomposition( 103 | CMR* cmr, /**< \ref CMR environment. */ 104 | CMR_SEYMOUR_NODE* dec, /**< Pointer to the decomposition node that is the root of the new subtree. */ 105 | CMR_REGULAR_PARAMS* params, /**< Parameters for the computation (may be \c NULL). */ 106 | CMR_REGULAR_STATS* stats, /**< Statistics for the computation (may be \c NULL). */ 107 | double timeLimit /**< Time limit to impose. */ 108 | ); 109 | 110 | /** 111 | * \brief Refines a list of decomposition nodes. 112 | * 113 | * Replace the nodes' subtrees by new ones even if they exist. 114 | */ 115 | 116 | CMR_EXPORT 117 | CMR_ERROR CMRregularRefineDecomposition( 118 | CMR* cmr, /**< \ref CMR environment. */ 119 | size_t numNodes, /**< Number of nodes to refine. */ 120 | CMR_SEYMOUR_NODE** nodes, /**< Array of decomposition nodes to refine. */ 121 | CMR_REGULAR_PARAMS* params, /**< Parameters for the computation (may be \c NULL). */ 122 | CMR_REGULAR_STATS* stats, /**< Statistics for the computation (may be \c NULL). */ 123 | double timeLimit /**< Time limit to impose. */ 124 | ); 125 | 126 | #ifdef __cplusplus 127 | } 128 | #endif 129 | 130 | #endif /* CMR_REGULAR_H */ 131 | 132 | -------------------------------------------------------------------------------- /include/cmr/tu.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_TU_H 2 | #define CMR_TU_H 3 | 4 | /** 5 | * \file tu.h 6 | * 7 | * \author Matthias Walter and Klaus Truemper 8 | * 9 | * \brief Recognition of [totally unimodular matrices](\ref tu). 10 | */ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | typedef enum 22 | { 23 | CMR_TU_ALGORITHM_DECOMPOSITION = 0, /**< \brief Algorithm based on Seymour's decomposition of regular matroids. */ 24 | CMR_TU_ALGORITHM_EULERIAN = 1, /**< \brief Enumeration algorithm based on Eulerian submatrices. */ 25 | CMR_TU_ALGORITHM_PARTITION = 2 /**< \brief Enumeration algorithm based on criterion of Ghouila-Houri. */ 26 | } CMR_TU_ALGORITHM; 27 | 28 | typedef struct 29 | { 30 | CMR_TU_ALGORITHM algorithm; /**< \brief Algorithm to use. */ 31 | CMR_SEYMOUR_PARAMS seymour; /**< \brief Parameters for testing via Seymour decomposition. */ 32 | bool ternary; /**< \brief Whether to create a ternary Seymour decomposition tree (default: \c true). */ 33 | bool camionFirst; /**< \brief If \c ternary is \c false, then whether to run the Camion test first. */ 34 | bool naiveSubmatrix; /**< \brief Whether to use the naive submatrix search instead of a greedy algorithm 35 | ** (default: \c false). */ 36 | } CMR_TU_PARAMS; 37 | 38 | /** 39 | * \brief Initializes the default parameters for recognition of [totally unimodular](\ref tu) matrices. 40 | * 41 | * These are selected for minimum running time. 42 | */ 43 | 44 | CMR_EXPORT 45 | CMR_ERROR CMRtuParamsInit( 46 | CMR_TU_PARAMS* params /**< Pointer to parameters. */ 47 | ); 48 | 49 | /** 50 | * \brief Statistics for recognition algorithm for [totally unimodular](\ref tu) matrices. 51 | */ 52 | 53 | typedef struct 54 | { 55 | CMR_SEYMOUR_STATS seymour; /**< Statistics for Seymour decomposition computation. */ 56 | CMR_CAMION_STATISTICS camion; /**< Statistics for Camion signing. */ 57 | 58 | uint32_t enumerationTotalCount; /**< Total number of invocations. */ 59 | uint32_t enumerationRowSubsets; /**< Number of considered row subsets in enumeration algorithm. */ 60 | uint32_t enumerationColumnSubsets; /**< Number of considered column subsets in enumeration algorithm. */ 61 | double enumerationTime; /**< Total time of enumeration algorithm. */ 62 | 63 | uint32_t partitionTotalCount; /**< Total number of invocations. */ 64 | uint32_t partitionRowSubsets; /**< Number of considered row subsets in partition algorithm. */ 65 | uint32_t partitionColumnSubsets; /**< Number of considered column subsets in partition algorithm. */ 66 | double partitionTime; /**< Total time of partition algorithm. */ 67 | } CMR_TU_STATS; 68 | 69 | /** 70 | * \brief Initializes all statistics for recognition algorithm for [totally unimodular](\ref tu) matrices. 71 | */ 72 | 73 | CMR_EXPORT 74 | CMR_ERROR CMRtuStatsInit( 75 | CMR_TU_STATS* stats /**< Pointer to statistics. */ 76 | ); 77 | 78 | /** 79 | * \brief Prints statistics for recognition algorithm for [totally unimodular](\ref tu) matrices. 80 | */ 81 | 82 | CMR_EXPORT 83 | CMR_ERROR CMRtuStatsPrint( 84 | FILE* stream, /**< File stream to print to. */ 85 | CMR_TU_STATS* stats, /**< Pointer to statistics. */ 86 | const char* prefix /**< Prefix string to prepend to each printed line (may be \c NULL). */ 87 | ); 88 | 89 | /** 90 | * \brief Tests a matrix \f$ M \f$ for being [totally unimodular](\ref tu). 91 | * 92 | * Tests if matrix \f$ M \f$ is totally unimodular and sets \p *pisTotallyUnimodular accordingly. 93 | * 94 | * If \f$ M \f$ is totally unimodular and \p pdec != \c NULL, then \p *pdec will contain a decomposition tree of the 95 | * regular matroid. The caller must release it via \ref CMRdecFree(). 96 | * 97 | * If \f$ M \f$ is not totally unimodular and \p psubmatrix != \c NULL, then \p *psubmatrix will indicate a submatrix 98 | * of \f$ M \f$ with determinant \f$ -2 \f$ or \f$ 2 \f$. 99 | */ 100 | 101 | CMR_EXPORT 102 | CMR_ERROR CMRtuTest( 103 | CMR* cmr, /**< \ref CMR environment */ 104 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$. */ 105 | bool* pisTotallyUnimodular, /**< Pointer for storing whether \f$ M \f$ is totally unimodular. */ 106 | CMR_SEYMOUR_NODE** proot, /**< Pointer for storing the decomposition tree (may be \c NULL). */ 107 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a submatrix with non-ternary determinant (may be \c NULL). */ 108 | CMR_TU_PARAMS* params, /**< Parameters for the computation (may be \c NULL for defaults). */ 109 | CMR_TU_STATS* stats, /**< Statistics for the computation (may be \c NULL). */ 110 | double timeLimit /**< Time limit to impose. */ 111 | ); 112 | 113 | /** 114 | * \brief Completes a subtree of an existing decomposition tree. 115 | * 116 | * Replace the node's subtree by a new one even if it exists. Note that different parameters may yield a different 117 | * subtree. 118 | * 119 | * \note Requires \p params.algorithm to be \ref CMR_TU_ALGORITHM_DECOMPOSITION. 120 | */ 121 | 122 | CMR_EXPORT 123 | CMR_ERROR CMRtuCompleteDecomposition( 124 | CMR* cmr, /**< \ref CMR environment. */ 125 | CMR_SEYMOUR_NODE* dec, /**< Pointer to the decomposition node that is the root of the new subtree. */ 126 | CMR_TU_PARAMS* params, /**< Parameters for the computation. */ 127 | CMR_TU_STATS* stats, /**< Statistics for the computation (may be \c NULL). */ 128 | double timeLimit /**< Time limit to impose. */ 129 | ); 130 | 131 | #ifdef __cplusplus 132 | } 133 | #endif 134 | 135 | #endif /* CMR_TU_H */ 136 | -------------------------------------------------------------------------------- /src/cmr/bipartite_graph.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_BIPARTITE_GRAPH_INTERNAL_H 2 | #define CMR_BIPARTITE_GRAPH_INTERNAL_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * \brief Finds a shortest path between different vertex groups in the bipartite graph of a submatrix of the given 13 | * \p matrix. 14 | * 15 | * The bipartite graph of a matrix \f$ M \f$ has the rows and columns of as vertices and edges for all nonzeros of 16 | * \f$ M \f$. The rows and columns are assigned to groups, and some shortest path from any nonzero group to any larger 17 | * nonzero group will be returned. Vertices whose group is negative are ignored. 18 | * 19 | * A negative row/column group value means to disable the node. Positive values indicate different groups. 20 | */ 21 | 22 | CMR_ERROR CMRchrmatSubmatrixBipartitePath( 23 | CMR* cmr, /**< \ref CMR environment. */ 24 | CMR_CHRMAT* matrix, /**< Matrix. */ 25 | CMR_CHRMAT* transpose, /**< Transpose of \p matrix. */ 26 | int* rowsGroup, /**< Array that specifies each row's group. */ 27 | int* columnsGroup, /**< Array that specifies each column's group. */ 28 | bool* pconnected, /**< Pointer for storing whether such a path exists; may be \c NULL. */ 29 | CMR_ELEMENT* ppathSource, /**< Pointer for storing the source row/column; set to invalid if no path exists; 30 | ** may be \c NULL. */ 31 | CMR_ELEMENT* ppathTarget, /**< Pointer for storing the target row/column; set to invalid if no path exists; 32 | ** may be \c NULL. */ 33 | CMR_ELEMENT* rowsPredecessor, /**< Array for storing the predecessor of each row vertex; may be \c NULL. */ 34 | CMR_ELEMENT* columnsPredecessor, /**< Array for storing the predecessor of each column vertex; may be \c NULL. */ 35 | int* psum /**< Pointer for storing the sum of the edge entries on the path; may be \c NULL. */ 36 | ); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif /* CMR_BIPARTITE_GRAPH_INTERNAL_H */ 43 | -------------------------------------------------------------------------------- /src/cmr/block_decomposition.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_BLOCK_DECOMPOSITION_H 2 | #define CMR_BLOCK_DECOMPOSITION_H 3 | 4 | #include 5 | #include "matrix_internal.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * \brief Information on one block of a block decomposition of a matrix. 13 | */ 14 | 15 | typedef struct 16 | { 17 | CMR_MATRIX* matrix; /**< \brief Sparse matrix. */ 18 | CMR_MATRIX* transpose; /**< \brief Sparse transposed matrix. */ 19 | size_t* rowsToOriginal; /**< \brief Maps component rows to original matrix rows. */ 20 | size_t* columnsToOriginal; /**< \brief Maps component columns to original matrix columns. */ 21 | } CMR_BLOCK; 22 | 23 | /** 24 | * \brief Decomposes int matrix into 1-connected submatrices. 25 | * 26 | * Allocates an array \p components with an entry per 1-connected submatrix. The caller has to free this array and 27 | * its members. 28 | */ 29 | 30 | CMR_ERROR CMRdecomposeBlocks( 31 | CMR* cmr, /**< \ref CMR environment */ 32 | CMR_MATRIX* matrix, /**< Matrix */ 33 | size_t matrixType, /**< Size of base type of matrix. */ 34 | size_t targetType, /**< Size of base type of component matrices. */ 35 | size_t* pnumBlocks, /**< Pointer for storing the number of components. */ 36 | CMR_BLOCK** pblocks, /**< Pointer for storing the array with component information. */ 37 | size_t* rowsToBlock, /**< Mapping of rows of \p matrix to components (may be \c NULL). */ 38 | size_t* columnsToBlock, /**< Mapping of columns of \p matrix to components (may be \c NULL). */ 39 | size_t* rowsToBlockRows, /**< Mapping of rows to rows of the component (may be \c NULL). */ 40 | size_t* columnsToBlockColumns /**< Mapping of columns to columns of the component (may be \c NULL). */ 41 | ); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif /* CMR_BLOCK_DECOMPOSITION_H */ 48 | -------------------------------------------------------------------------------- /src/cmr/camion_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_CAMION_INTERNAL_H 2 | #define CMR_CAMION_INTERNAL_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * \brief Ensures that sequentially connected matrix \f$ M \f$ is [Camion-signed](\ref camion). 13 | * 14 | * The matrix \f$ M \f$ is assumed to be ternary. If sign changes are necessary, only \p matrix is modified. 15 | * In particular, \p transpose remains unchanged. 16 | * 17 | * If \p submatrix is not \c NULL and sign changes are necessary, then a submatrix with determinant 18 | * -2 or +2 is stored in \p *psubmatrix and the caller must use \ref CMRsubmatFree() to free its 19 | * memory. It is set to \c NULL if no sign changes are needed. 20 | */ 21 | 22 | CMR_ERROR CMRcamionComputeSignSequentiallyConnected( 23 | CMR* cmr, /**< \ref CMR environment. */ 24 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$. */ 25 | CMR_CHRMAT* transpose, /**< Transpose \f$ M^{\mathsf{T}} \f$. */ 26 | bool change, /**< Whether signs of \p matrix should be changed if necessary. */ 27 | char* pmodification, /**< Pointer for storing which matrix was modified. */ 28 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a submatrix with a bad determinant (may be \c NULL). */ 29 | double timeLimit /**< Time limit to impose. */ 30 | ); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif /* CMR_CAMION_INTERNAL_H */ 37 | -------------------------------------------------------------------------------- /src/cmr/densematrix.c: -------------------------------------------------------------------------------- 1 | // #define CMR_DEBUG /* Uncomment to debug this file. */ 2 | 3 | #include "env_internal.h" 4 | #include "densematrix.h" 5 | 6 | #include 7 | #include 8 | 9 | 10 | CMR_ERROR CMRdensebinmatrixCreate(CMR* cmr, size_t numRows, size_t numColumns, DenseBinaryMatrix** presult) 11 | { 12 | assert(cmr); 13 | 14 | CMR_CALL( CMRallocBlock(cmr, presult) ); 15 | DenseBinaryMatrix* matrix = *presult; 16 | 17 | size_t size = (numRows * numColumns + (8 * sizeof(size_t)) - 1) / (8 * sizeof(size_t)); 18 | CMRdbgMsg(10, "Creating %zux%zu DenseBinaryMatrix using %zu blocks each of which having %zu bytes.\n", numRows, 19 | numColumns, size, sizeof(size_t)); 20 | matrix->numRows = numRows; 21 | matrix->numColumns = numColumns; 22 | matrix->data = NULL; 23 | CMR_CALL( CMRallocBlockArray(cmr, &matrix->data, size) ); 24 | for (size_t i = 0; i < size; ++i) 25 | matrix->data[i] = 0UL; 26 | 27 | return CMR_OKAY; 28 | } 29 | 30 | CMR_ERROR CMRdensebinmatrixFree(CMR* cmr, DenseBinaryMatrix** pmatrix) 31 | { 32 | assert(cmr); 33 | assert(pmatrix); 34 | 35 | if (!*pmatrix) 36 | return CMR_OKAY; 37 | 38 | CMR_CALL( CMRfreeBlockArray(cmr, &(*pmatrix)->data) ); 39 | CMR_CALL( CMRfreeBlockArray(cmr, pmatrix) ); 40 | 41 | return CMR_OKAY; 42 | } 43 | -------------------------------------------------------------------------------- /src/cmr/densematrix.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_DENSEMATRIX_INTERNAL_H 2 | #define CMR_DENSEMATRIX_INTERNAL_H 3 | 4 | #include "env_internal.h" 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /** 13 | * \brief Dense matrix. 14 | */ 15 | 16 | typedef struct 17 | { 18 | size_t* data; 19 | size_t numRows; 20 | size_t numColumns; 21 | } DenseBinaryMatrix; 22 | 23 | CMR_ERROR CMRdensebinmatrixCreate( 24 | CMR* cmr, /**< \ref CMR environment. */ 25 | size_t numRows, /**< Number of rows. */ 26 | size_t numColumns, /**< Number of columns. */ 27 | DenseBinaryMatrix** presult /**< Pointer for storing the result. */ 28 | ); 29 | 30 | CMR_ERROR CMRdensebinmatrixFree( 31 | CMR* cmr, /**< \ref CMR environment. */ 32 | DenseBinaryMatrix** pmatrix /**< Pointer for storing the result. */ 33 | ); 34 | 35 | static inline 36 | bool CMRdensebinmatrixGet( 37 | DenseBinaryMatrix* matrix, /**< Matrix. */ 38 | size_t row, /**< Row index. */ 39 | size_t column /**< Column index. */ 40 | ) 41 | { 42 | size_t index = row * matrix->numColumns + column; 43 | size_t block = matrix->data[index / (8 * sizeof(size_t))]; 44 | // CMRdbgMsg(8, "CMRdensebinmatrixGet(%zu,%zu) uses index %zu in block %zu at %zu -> %d\n", row, column, index, 45 | // index / (8 * sizeof(size_t)), (index % (8 * sizeof(size_t))), 46 | // (block & (1UL << (index % (8 * sizeof(size_t))))) ? 1 : 0); 47 | return block & (1UL << (index % (8 * sizeof(size_t)))); 48 | } 49 | 50 | static inline 51 | void CMRdensebinmatrixSet0( 52 | DenseBinaryMatrix* matrix, /**< Matrix. */ 53 | size_t row, /**< Row index. */ 54 | size_t column /**< Column index. */ 55 | ) 56 | { 57 | size_t index = row * matrix->numColumns + column; 58 | // CMRdbgMsg(8, "CMRdensebinmatrixSet0(%zu,%zu) uses index %zu in block %zu at %zu.\n", row, column, index, 59 | // index / (8 * sizeof(size_t)), (index % (8 * sizeof(size_t)))); 60 | size_t* pblock = &matrix->data[index / (8 * sizeof(size_t))]; 61 | *pblock &= ~(1UL << (index % (8 * sizeof(size_t)))); 62 | } 63 | 64 | static inline 65 | void CMRdensebinmatrixSet1( 66 | DenseBinaryMatrix* matrix, /**< Matrix. */ 67 | size_t row, /**< Row index. */ 68 | size_t column /**< Column index. */ 69 | ) 70 | { 71 | size_t index = row * matrix->numColumns + column; 72 | // CMRdbgMsg(8, "CMRdensebinmatrixSet1(%zu,%zu) uses index %zu in block %zu at %zu.\n", row, column, index, 73 | // index / (8 * sizeof(size_t)), (index % (8 * sizeof(size_t)))); 74 | size_t* pblock = &matrix->data[index / (8 * sizeof(size_t))]; 75 | *pblock |= (1UL << (index % (8 * sizeof(size_t)))); 76 | } 77 | 78 | static inline 79 | void CMRdensebinmatrixSet( 80 | DenseBinaryMatrix* matrix, /**< Matrix. */ 81 | size_t row, /**< Row index. */ 82 | size_t column, /**< Column index. */ 83 | bool value /**< Value. */ 84 | ) 85 | { 86 | size_t index = row * matrix->numColumns + column; 87 | CMRdbgMsg(8, "CMRdensebinmatrixSet(%zu,%zu,%d) uses index %zu in block %zu at %zu.\n", row, column, value ? 1 : 0, index, 88 | index / (8 * sizeof(size_t)), (index % (8 * sizeof(size_t)))); 89 | size_t* pblock = &matrix->data[index / (8 * sizeof(size_t))]; 90 | size_t mask = (1UL << (index % (8 * sizeof(size_t)))); 91 | if (value) 92 | *pblock |= mask; 93 | else 94 | *pblock &= ~mask; 95 | } 96 | 97 | 98 | static inline 99 | void CMRdensebinmatrixFlip( 100 | DenseBinaryMatrix* matrix, /**< Matrix. */ 101 | size_t row, /**< Row index. */ 102 | size_t column /**< Column index. */ 103 | ) 104 | { 105 | size_t index = row * matrix->numColumns + column; 106 | // CMRdbgMsg(8, "CMRdensebinmatrixFlip(%zu,%zu) uses index %zu in block %zu at %zu.\n", row, column, index, 107 | // index / (8 * sizeof(size_t)), (index % (8 * sizeof(size_t)))); 108 | size_t* pblock = &matrix->data[index / (8 * sizeof(size_t))]; 109 | size_t mask = (1UL << (index % (8 * sizeof(size_t)))); 110 | if (*pblock & mask) 111 | *pblock &= ~mask; 112 | else 113 | *pblock |= mask; 114 | } 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | 120 | #endif /* CMR_LISTMATRIX_INTERNAL_H */ 121 | -------------------------------------------------------------------------------- /src/cmr/element.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | static char elementStringBuffer[32]; 7 | 8 | CMR_EXPORT 9 | const char* CMRelementString(CMR_ELEMENT element, char* buffer) 10 | { 11 | if (!buffer) 12 | buffer = elementStringBuffer; 13 | 14 | if (element < 0) 15 | sprintf(buffer, "r%d", -element); 16 | else if (element > 0) 17 | sprintf(buffer, "c%d", element); 18 | else 19 | strcpy(buffer, ""); 20 | return buffer; 21 | } 22 | -------------------------------------------------------------------------------- /src/cmr/env_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_ENV_INTERNAL_H 2 | #define CMR_ENV_INTERNAL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #if defined(CMR_DEBUG) 11 | 12 | static inline 13 | void CMRdbgMsg(int indent, const char* format, ...) 14 | { 15 | va_list args; 16 | 17 | for (int i = 0; i < indent; ++i) 18 | putchar(' '); 19 | va_start(args, format); 20 | vprintf(format, args); 21 | va_end(args); 22 | fflush(stdout); 23 | } 24 | 25 | #else /* !CMR_DEBUG */ 26 | 27 | static inline 28 | void CMRdbgMsg(int indent, const char* format, ...) 29 | { 30 | CMR_UNUSED(indent); 31 | CMR_UNUSED(format); 32 | } 33 | /*#define CMRdbgMsg(...) */ 34 | 35 | #endif /* CMR_DEBUG */ 36 | 37 | 38 | typedef struct 39 | { 40 | char* memory; /**< \brief Raw memory. */ 41 | size_t top; /**< \brief First used byte. */ 42 | } CMR_STACK; 43 | 44 | struct CMR_ENVIRONMENT 45 | { 46 | char* errorMessage; /**< \brief Error message. */ 47 | 48 | FILE* output; /**< \brief Output stream or \c NULL if silent. */ 49 | bool closeOutput; /**< \brief Whether to close the output stream at the end. */ 50 | int verbosity; /**< \brief Verbosity level. */ 51 | int numThreads; /**< \brief Number of threads to use. */ 52 | 53 | size_t numStacks; /**< \brief Number of allocated stacks in stack array. */ 54 | size_t memStacks; /**< \brief Memory for stack array. */ 55 | size_t currentStack; /**< \brief Index of last used stack. */ 56 | CMR_STACK* stacks; /**< \brief Array of stacks. */ 57 | }; 58 | 59 | #include 60 | 61 | /** 62 | * \brief Allocates statck memory for *\p ptr. 63 | * 64 | * Stack memory shall be freed with \ref CMRfreeStack in the reverse order of allocation. 65 | * The size is determined automatically. 66 | */ 67 | 68 | #define CMRallocStack(cmr, ptr) \ 69 | _CMRallocStack(cmr, (void**) ptr, sizeof(**ptr)) 70 | 71 | /** 72 | * \brief Carries out the allocation for \ref CMRallocStack. 73 | * 74 | * \note Use \ref CMRallocStack to allocate stack memory. 75 | */ 76 | 77 | CMR_EXPORT 78 | CMR_ERROR _CMRallocStack( 79 | CMR* cmr, /**< \ref CMR environment. */ 80 | void** ptr, /**< Pointer where the space shall be allocated. */ 81 | size_t size /**< Space to allocate. */ 82 | ); 83 | 84 | /** 85 | * \brief Frees a stack memory chunk allocated with \ref CMRallocStack. 86 | */ 87 | 88 | #define CMRfreeStack(cmr, ptr) \ 89 | _CMRfreeStack(cmr, (void**) ptr, sizeof(**ptr)) 90 | 91 | /** 92 | * \brief Carries out the deallocation for \ref CMRfreeStack. 93 | * 94 | * \note Use \ref CMRfreeStack to free stack memory. 95 | */ 96 | 97 | CMR_EXPORT 98 | CMR_ERROR _CMRfreeStack( 99 | CMR* cmr, /**< \ref CMR environment. */ 100 | void** ptr /**< Pointer of space to be freed. */ 101 | ); 102 | 103 | /** 104 | * \brief Allocates memory for an array of blocks on the stack. 105 | */ 106 | 107 | #define CMRallocStackArray(cmr, ptr, length) \ 108 | _CMRallocStack(cmr, (void**) ptr, sizeof(**ptr) * (length)) 109 | 110 | /** 111 | * \brief Frees memory of an array of blocks on the stack. 112 | */ 113 | 114 | #define CMRfreeStackArray(cmr, ptr) \ 115 | _CMRfreeStack(cmr, (void**) ptr) 116 | 117 | #if !defined(NDEBUG) 118 | 119 | /** 120 | * \brief Checks stack protection fields for corruption. 121 | * 122 | * Useful for debugging memory errors. 123 | */ 124 | 125 | void CMRassertStackConsistency( 126 | CMR* cmr /**< \ref CMR environment. */ 127 | ); 128 | 129 | #else 130 | 131 | static inline 132 | void CMRassertStackConsistency( 133 | CMR* cmr /**< \ref CMR environment. */ 134 | ) 135 | { 136 | CMR_UNUSED(cmr); 137 | assert(cmr); 138 | } 139 | 140 | #endif /* !NDEBUG */ 141 | 142 | void CMRraiseErrorMessage( 143 | CMR* cmr, /**< \ref CMR environment. */ 144 | const char* format, /**< Format string in printf-style. */ 145 | ... /**< Variadic arguments. */ 146 | ); 147 | 148 | size_t CMRgetStackUsage( 149 | CMR* cmr /**< \ref CMR environment. */ 150 | ); 151 | 152 | char* CMRconsistencyMessage(const char* format, ...); 153 | 154 | #if defined(CMR_DEBUG) 155 | 156 | /** 157 | * \brief Asserts that \p call reports consistency. Otherwise, the inconsistency explanation is printed and the program 158 | * terminates. 159 | * 160 | * The following example code checks \c matrix (of type \ref CMR_CHRMAT*) for consistency using 161 | * \ref CMRchrmatConsistency. 162 | * 163 | * CMRconsistencyAssert( CMRchrmatConsistency(matrix) ); 164 | */ 165 | 166 | #define CMRdbgConsistencyAssert( call ) \ 167 | do \ 168 | { \ 169 | char* __message = call; \ 170 | if (__message) \ 171 | { \ 172 | fflush(stdout); \ 173 | fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, __message); \ 174 | fflush(stderr); \ 175 | free(__message); \ 176 | assert(!"Consistency assertion raised!"); \ 177 | } \ 178 | } \ 179 | while (false); 180 | 181 | #else 182 | 183 | #define CMRdbgConsistencyAssert( call ) 184 | 185 | #endif 186 | 187 | #endif /* CMR_ENV_INTERNAL_H */ 188 | -------------------------------------------------------------------------------- /src/cmr/graphic_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_GRAPHIC_INTERNAL_H 2 | #define CMR_GRAPHIC_INTERNAL_H 3 | 4 | #include 5 | 6 | /** 7 | * \brief Computes the network or graphic matrix of a given (di)graph \f$ D = (V,A) \f$. 8 | * 9 | * Computes the [network matrix](\ref network) \f$ M := M(D,T) \f$ for given \f$ D \f$ and optionally given (directed) 10 | * spanning forest \f$ T \subseteq A \f$ or the support matrix of \f$ M(D,T) \f$. 11 | * If \f$ T \f$ is not given, an arbitrary (directed) spanning forest of \f$ D \f$ is used. 12 | * The direction of the edges is that of \p digraph, but may be flipped by specifying \p arcsReversed. 13 | * If \p forestArcs is \c NULL, an arbitrary (directed) spanning forest \f$ T \f$ of \f$ D \f$ is computed. 14 | * The ordering of the columns can be specified via \p coforestArcs. 15 | * 16 | * \note The function computes a network matrix of \f$ D \f$ (and \f$ T \f$) regardless of whether \p forestArcs is 17 | * a correct (directed) spanning forest. Whether this was the case is indicated via \p *pisCorrectForest. 18 | */ 19 | 20 | CMR_ERROR CMRcomputeRepresentationMatrix( 21 | CMR* cmr, /**< \ref CMR environment. */ 22 | CMR_GRAPH* digraph, /**< Digraph \f$ D = (V,A) \f$. */ 23 | bool ternary, /**< Whether we need to compute correct signs. */ 24 | CMR_CHRMAT** ptranspose, /**< Pointer for storing \f$ M^{\mathsf{T}} \f$ (may be \c NULL). */ 25 | bool* arcsReversed, /**< Indicates, for each edge \f$ \{u, v\}\f$, whether we consider \f$ (u, v)\f$ 26 | ** (if \c false) or \f$ (v,u)\f$ (if \c true). */ 27 | int numForestArcs, /**< \f$ |T| \f$ (0 if \c forestArcs is \c NULL). */ 28 | CMR_GRAPH_EDGE* forestArcs, /**< \f$ T \f$, ordered by the rows of \f$ M \f$ (may be \c NULL). */ 29 | int numCoforestArcs, /**< \f$ |A \setminus T| \f$ (0 if \c coforestArcs is \c NULL). */ 30 | CMR_GRAPH_EDGE* coforestArcs, /**< \f$ A \setminus T \f$, ordered by the columns of \f$ M \f$ (may be 31 | ** \c NULL). */ 32 | bool* pisCorrectForest /**< Pointer for storing whether \c forestArcs is a (directed) spanning forest of 33 | ** \f$ D \f$'s underlying undirected graph (may be \c NULL). */ 34 | ); 35 | 36 | /** 37 | * \brief Tests the support matrix \f$ M \f$ of the input matrix for being a [cographic matrix](\ref graphic). 38 | * 39 | * Tests if \f$ M = M(G,T)^{\mathsf{T}} \f$ for some graph \f$ G = (V,E) \f$ and some spanning forest 40 | * \f$ T \subseteq E \f$ of \f$ G \f$ and sets \p *pisCographic accordingly. 41 | * 42 | * If \f$ M \f$ is a cographic matrix and \p pgraph != \c NULL, then one possible graph \f$ G \f$ is computed and 43 | * stored in \p *pgraph. The caller must release its memory via \ref CMRgraphFree. 44 | * If in addition to \p pgraph also \p pforestEdges != \c NULL (resp. \p pcoforestEdges != \c NULL), then a 45 | * corresponding spanning forest \f$ T \f$ (resp.\ its complement \f$ E \setminus T \f$) is stored in 46 | * \p *pforestEdges (resp. \p *pcoforestEdges). The caller must release this memory via \ref CMRfreeBlockArray. 47 | * 48 | * \note Retrieval of minimal non-cographic submatrices via \p *psubmatrix is not implemented, yet. 49 | */ 50 | 51 | CMR_ERROR CMRcographicTestSupport( 52 | CMR* cmr, /**< \ref CMR environment. */ 53 | CMR_CHRMAT* matrix, /**< Matrix \f$ M \f$ */ 54 | bool* pisCographic, /**< Returns true if and only if \f$ M \f$ is a cographic matrix. */ 55 | CMR_GRAPH** pgraph, /**< Pointer for storing the graph \f$ G \f$ (if \f$ M \f$ is graphic). */ 56 | CMR_GRAPH_EDGE** pforestEdges, /**< Pointer for storing \f$ T \f$, indexed by the rows of \f$ M \f$ (if \f$ M \f$ 57 | ** is graphic). */ 58 | CMR_GRAPH_EDGE** pcoforestEdges, /**< Pointer for storing \f$ E \setminus T \f$, indexed by the columns of \f$ M \f$ 59 | ** (if \f$ M \f$ is graphic). */ 60 | CMR_GRAPHIC_STATISTICS* stats, /**< Pointer to statistics (may be \c NULL). */ 61 | double timeLimit /**< Time limit to impose. */ 62 | ); 63 | 64 | #endif /* CMR_GRAPHIC_INTERNAL_H */ 65 | -------------------------------------------------------------------------------- /src/cmr/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_HEAP_INTERNAL_H 2 | #define CMR_HEAP_INTERNAL_H 3 | 4 | #include 5 | #include "env_internal.h" 6 | #include 7 | 8 | #ifdef __cplus 9 | extern "C" { 10 | #endif 11 | 12 | /** 13 | * \brief Structure for min-heap with unsigned int values. 14 | */ 15 | 16 | typedef struct 17 | { 18 | int size; /**< \brief Current size of the heap. */ 19 | int memKeys; /**< \brief Memory for keys. */ 20 | int* values; /**< \brief Array that maps keys to values. */ 21 | int* positions; /**< \brief Array that maps keys to heap positions. */ 22 | int* data; /**< \brief Array that maps heap positions to keys. */ 23 | } CMR_INTHEAP; 24 | 25 | /** 26 | * \brief Initializes an empty heap using stack memory. 27 | */ 28 | 29 | CMR_ERROR CMRintheapInitStack( 30 | CMR* cmr, /**< \ref CMR environment. */ 31 | CMR_INTHEAP* heap, /**< Heap pointer. */ 32 | int memKeys /**< Maximum number of elements and bound on key entries. */ 33 | ); 34 | 35 | /** 36 | * \brief Clears the given \p heap. 37 | */ 38 | 39 | CMR_ERROR CMRintheapClearStack( 40 | CMR* cmr, /**< \ref CMR environment. */ 41 | CMR_INTHEAP* heap /**< Heap pointer. */ 42 | ); 43 | 44 | /** 45 | * \brief Inserts a \p key \p value pair into the heap. 46 | */ 47 | 48 | CMR_ERROR CMRintheapInsert( 49 | CMR_INTHEAP* heap, /**< Heap pointer. */ 50 | int key, /**< Key of new element. */ 51 | int value /**< Value of new element. */ 52 | ); 53 | 54 | /** 55 | * \brief Decreases the value of \p key to \p newValue. 56 | */ 57 | 58 | CMR_ERROR CMRintheapDecrease( 59 | CMR_INTHEAP* heap, /**< Heap pointer. */ 60 | int key, /**< Key of element. */ 61 | int newValue /**< New value of element. */ 62 | ); 63 | 64 | /** 65 | * \brief Decreases the value of \p key to \p newValue or inserts it. 66 | */ 67 | 68 | CMR_ERROR CMRintheapDecreaseInsert( 69 | CMR_INTHEAP* heap, /**< Heap pointer. */ 70 | int key, /**< Key of element. */ 71 | int newValue /**< New value of element. */ 72 | ); 73 | 74 | /** 75 | * \brief Returns \c true if the heap is empty. 76 | */ 77 | 78 | static inline 79 | bool CMRintheapEmpty( 80 | CMR_INTHEAP* heap /**< Heap pointer. */ 81 | ) 82 | { 83 | return heap->size == 0; 84 | } 85 | 86 | /** 87 | * \brief Returns the key of the minimum element. 88 | */ 89 | 90 | static inline 91 | int CMRintheapMinimumKey( 92 | CMR_INTHEAP* heap /**< Heap pointer. */ 93 | ) 94 | { 95 | return heap->data[0]; 96 | } 97 | 98 | /** 99 | * \brief Returns the value of the minimum element. 100 | */ 101 | 102 | static inline 103 | int CMRintheapMinimumValue( 104 | CMR_INTHEAP* heap /**< Heap. */ 105 | ) 106 | { 107 | return heap->values[heap->data[0]]; 108 | } 109 | 110 | /** 111 | * \brief Returns \c true if an element with \p key is present in the heap. 112 | */ 113 | 114 | static inline 115 | bool CMRintheapContains( 116 | CMR_INTHEAP* heap, /**< Heap pointer. */ 117 | int key /**< Key whose existence shall be checked. */ 118 | ) 119 | { 120 | return heap->positions[key] >= 0; 121 | } 122 | 123 | static inline 124 | int CMRintheapGetValue( 125 | CMR_INTHEAP* heap, /*< Heap. */ 126 | int key /*< Key whose value shall be returned. */ 127 | ) 128 | { 129 | return heap->values[key]; 130 | } 131 | 132 | /** 133 | * \brief Reutrns the value of \p key or \c INT_MAX if there is no such element. 134 | */ 135 | 136 | static inline 137 | int CMRintheapGetValueInfinity( 138 | CMR_INTHEAP* heap, /**< Heap pointer. */ 139 | int key /**< Key to be searched. */ 140 | ) 141 | { 142 | if (heap->positions[key] >= 0) 143 | return heap->values[key]; 144 | else 145 | return INT_MAX; 146 | } 147 | 148 | /** 149 | * \brief Extracts the minimum element and returns its key. 150 | */ 151 | 152 | int CMRintheapExtractMinimum( 153 | CMR_INTHEAP* heap /**< Heap pointer. */ 154 | ); 155 | 156 | #ifdef __cplusplus 157 | } 158 | #endif 159 | 160 | #endif /* CMR_HEAP_INTERNAL_H */ 161 | -------------------------------------------------------------------------------- /src/cmr/hereditary_property.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_HEREDITARY_PROPERTY_H 2 | #define CMR_HEREDITARY_PROPERTY_H 3 | 4 | #include 5 | #include "env_internal.h" 6 | #include 7 | #include 8 | 9 | #ifdef __cplus 10 | extern "C" { 11 | #endif 12 | 13 | typedef CMR_ERROR (*HereditaryPropertyTest)( 14 | CMR* cmr, /**< \ref CMR environment. */ 15 | CMR_CHRMAT* matrix, /**< Some matrix to be tested for the property. */ 16 | void* data, /**< Potential additional data for the test function. */ 17 | bool* phasProperty, /**< Pointer for storing whether \p matrix has the property. */ 18 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a proper submatrix of \p matrix without the property. */ 19 | double timeLimit /**< Time limit to impose. */ 20 | ); /**< Function pointer for functions that test a hereditary matrix property. */ 21 | 22 | /** 23 | * \brief Tests a given \p matrix for the hereditary property defined by a given \p testFunction. 24 | * 25 | * The algorithm finds the submatrix by successively single zeroing out rows or columns. 26 | */ 27 | 28 | CMR_ERROR CMRtestHereditaryPropertyNaive( 29 | CMR* cmr, /**< \ref CMR environment. */ 30 | CMR_CHRMAT* matrix, /**< Some matrix not having the hereditary property. */ 31 | HereditaryPropertyTest testFunction, /**< Test function. */ 32 | void* testData, /**< Data to be forwarded to the test function. */ 33 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a minimal submatrix not having the property. */ 34 | double timeLimit /**< Time limit to impose. */ 35 | ); 36 | 37 | /** 38 | * \brief Greedily tests a given \p matrix for the hereditary property defined by a given \p testFunction. 39 | * 40 | * The algorithm finds the submatrix by successively zeroing out sets of rows or columns. 41 | */ 42 | 43 | CMR_ERROR CMRtestHereditaryPropertyGreedy( 44 | CMR* cmr, /**< \ref CMR environment. */ 45 | CMR_CHRMAT* matrix, /**< Some matrix not having the hereditary property. */ 46 | HereditaryPropertyTest testFunction, /**< Test function. */ 47 | void* testData, /**< Data to be forwarded to the test function. */ 48 | CMR_SUBMAT** psubmatrix, /**< Pointer for storing a minimal submatrix not having the property. */ 49 | double timeLimit /**< Time limit to impose. */ 50 | ); 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | 56 | #endif /* CMR_HEREDITARY_PROPERTY_H */ 57 | -------------------------------------------------------------------------------- /src/cmr/linear_algebra_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_LINALG_INTERNAL_H 2 | #define CMR_LINALG_INTERNAL_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | static inline 11 | int moduloNonnegative(int p, int q) 12 | { 13 | if (q == 0) 14 | return p; 15 | if (q < 0) 16 | q = -q; 17 | int r = p % q; 18 | if (r < 0) 19 | r += q; 20 | return r; 21 | } 22 | 23 | static inline 24 | int moduloTernary(int p, int q) 25 | { 26 | if (q == 0) 27 | return p; 28 | if (q < 0) 29 | q = -q; 30 | int r = p % q; 31 | if (r < 0) 32 | r += q; 33 | return (r == 2 && q == 3) ? -1 : r; 34 | } 35 | 36 | /** 37 | * \brief Transforms \p matrix into a new matrix by applying integer row operations and row- and column swaps to find 38 | * an upper-diagonal basis matrix. 39 | * 40 | * The rank \f$ r \f$ is stored in \p *prank and the row- and column permutations are stored in \p *ppermutations, 41 | * such that the first \f$ r \f$ rows and columns of the resulting matrix form an invertible upper-diagonal matrix. 42 | * If \p invert is \c true then in this \f$ r \f$-by-\f$ r \f$ submatrix, the largest (in terms of absolute value) 43 | * entry in each column is on the diagonal. 44 | */ 45 | 46 | CMR_ERROR CMRintmatComputeUpperDiagonal( 47 | CMR* cmr, /**< \ref CMR environment. */ 48 | CMR_INTMAT* matrix, /**< A matrix */ 49 | bool invert, /**< Whether the transformed basis columns shall be strictly diagonally dominant. */ 50 | size_t* prank, /**< Pointer for storing the rank of the basis matrix. */ 51 | CMR_SUBMAT** ppermutations, /**< Pointer for storing the row- and column permutations applied to \p matrix 52 | ** (may be \c NULL). */ 53 | CMR_INTMAT** presult, /**< Pointer for storing the resulting int matrix (may be \c NULL). */ 54 | CMR_INTMAT** ptranspose /**< Pointer for storing the transpose of the result int matrix (may be \c NULL). */ 55 | ); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif /* CMR_LINALG_INTERNAL_H */ 62 | -------------------------------------------------------------------------------- /src/cmr/matrix_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_MATRIX_INTERNAL_H 2 | #define CMR_MATRIX_INTERNAL_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * \brief Abstract struct for row-wise representations of sparse matrices. 12 | */ 13 | 14 | typedef struct 15 | { 16 | size_t numRows; /**< \brief Number of rows. */ 17 | size_t numColumns; /**< \brief Number of columns. */ 18 | size_t numNonzeros; /**< \brief Number of and memory allocated for nonzeros. */ 19 | size_t * rowSlice; /**< \brief Array mapping each row to the index of its first entry. */ 20 | size_t* entryColumns; /**< \brief Array mapping each entry to its column.*/ 21 | void* entryValues; /**< \brief Array mapping each entry to its value. */ 22 | } CMR_MATRIX; 23 | 24 | /** 25 | * \brief Sorts the row and column indices of \p submatrix. 26 | */ 27 | 28 | CMR_ERROR CMRsortSubmatrix( 29 | CMR* cmr, /**< \ref CMR environment. */ 30 | CMR_SUBMAT* submatrix /**< The submatrix. */ 31 | ); 32 | 33 | /** 34 | * \brief Creates a \p numRows \f$ \times \f$ \p numColumns submatrix of the char \p matrix indexed by \p rows and 35 | * \p columns. 36 | */ 37 | 38 | CMR_ERROR CMRchrmatFilter( 39 | CMR* cmr, /**< \ref CMR environment. */ 40 | CMR_CHRMAT* matrix, /**< Matrix. */ 41 | size_t numRows, /**< Number of rows of submatrix. */ 42 | size_t* rows, /**< Rows of \p matrix to be copied into the submatrix. */ 43 | size_t numColumns, /**< Number of columns of submatrix. */ 44 | size_t* columns, /**< Columns of \p matrix to be copied into the submatrix. */ 45 | CMR_CHRMAT** presult /**< Pointer for storing the created submatrix. */ 46 | ); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif /* CMR_MATRIX_INTERNAL_H */ 53 | -------------------------------------------------------------------------------- /src/cmr/regular.c: -------------------------------------------------------------------------------- 1 | // #define CMR_DEBUG /** Uncomment to debug this file. */ 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "env_internal.h" 10 | #include "seymour_internal.h" 11 | 12 | CMR_ERROR CMRregularParamsInit(CMR_REGULAR_PARAMS* params) 13 | { 14 | assert(params); 15 | 16 | CMR_CALL( CMRseymourParamsInit(&(params->seymour)) ); 17 | 18 | return CMR_OKAY; 19 | } 20 | 21 | CMR_ERROR CMRregularStatsInit(CMR_REGULAR_STATS* stats) 22 | { 23 | assert(stats); 24 | 25 | CMR_CALL( CMRseymourStatsInit(&(stats->seymour)) ); 26 | 27 | return CMR_OKAY; 28 | } 29 | 30 | CMR_ERROR CMRregularStatsPrint(FILE* stream, CMR_REGULAR_STATS* stats, const char* prefix) 31 | { 32 | assert(stream); 33 | assert(stats); 34 | 35 | CMR_CALL( CMRseymourStatsPrint(stream, &(stats->seymour), prefix) ); 36 | 37 | return CMR_OKAY; 38 | } 39 | 40 | 41 | CMR_ERROR CMRregularTest(CMR* cmr, CMR_CHRMAT* matrix, bool *pisRegular, CMR_SEYMOUR_NODE** proot, 42 | CMR_MINOR** pminor, CMR_REGULAR_PARAMS* params, CMR_REGULAR_STATS* stats, double timeLimit) 43 | { 44 | assert(cmr); 45 | assert(matrix); 46 | 47 | CMR_REGULAR_PARAMS defaultParams; 48 | if (!params) 49 | { 50 | CMR_CALL( CMRregularParamsInit(&defaultParams) ); 51 | params = &defaultParams; 52 | } 53 | 54 | CMR_SUBMAT* submatrix = NULL; 55 | if (!CMRchrmatIsBinary(cmr, matrix, pminor ? &submatrix : NULL)) 56 | { 57 | if (pisRegular) 58 | *pisRegular = false; 59 | if (pminor) 60 | CMR_CALL( CMRminorCreate(cmr, pminor, 0, submatrix, CMR_MINOR_TYPE_ENTRY) ); 61 | return CMR_OKAY; 62 | } 63 | 64 | CMR_SEYMOUR_NODE* root = NULL; 65 | CMR_CALL( CMRseymourDecompose(cmr, matrix, false, &root, &(params->seymour), stats ? &(stats->seymour) : NULL, 66 | timeLimit) ); 67 | int8_t regularity = CMRseymourRegularity(root); 68 | if (regularity) 69 | *pisRegular = regularity > 0; 70 | if (proot) 71 | *proot = root; 72 | else 73 | CMR_CALL( CMRseymourRelease(cmr, &root) ); 74 | 75 | return CMR_OKAY; 76 | } 77 | 78 | CMR_ERROR CMRregularCompleteDecomposition(CMR* cmr, CMR_SEYMOUR_NODE* dec, CMR_REGULAR_PARAMS* params, 79 | CMR_REGULAR_STATS* stats, double timeLimit) 80 | { 81 | assert(cmr); 82 | assert(dec); 83 | assert(timeLimit > 0); 84 | 85 | CMR_REGULAR_PARAMS defaultParams; 86 | if (!params) 87 | { 88 | CMR_CALL( CMRregularParamsInit(&defaultParams) ); 89 | params = &defaultParams; 90 | } 91 | 92 | CMR_CALL( CMRregularityCompleteDecomposition(cmr, dec, &(params->seymour), stats ? &(stats->seymour) : NULL, 93 | timeLimit) ); 94 | 95 | return CMR_OKAY; 96 | } 97 | 98 | CMR_ERROR CMRregularRefineDecomposition(CMR* cmr, size_t numNodes, CMR_SEYMOUR_NODE** nodes, CMR_REGULAR_PARAMS* params, 99 | CMR_REGULAR_STATS* stats, double timeLimit) 100 | { 101 | assert(cmr); 102 | assert(timeLimit > 0); 103 | 104 | if (numNodes == 0) 105 | return CMR_OKAY; 106 | assert(nodes); 107 | 108 | CMR_REGULAR_PARAMS defaultParams; 109 | if (!params) 110 | { 111 | CMR_CALL( CMRregularParamsInit(&defaultParams) ); 112 | params = &defaultParams; 113 | } 114 | 115 | CMR_CALL( CMRregularityRefineDecomposition(cmr, numNodes, nodes, &(params->seymour), stats ? &(stats->seymour) : NULL, 116 | timeLimit) ); 117 | 118 | return CMR_OKAY; 119 | } 120 | 121 | -------------------------------------------------------------------------------- /src/cmr/regularity_onesum.c: -------------------------------------------------------------------------------- 1 | // #define CMR_DEBUG /** Uncomment to debug this file. */ 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "env_internal.h" 9 | #include "seymour_internal.h" 10 | #include "sort.h" 11 | #include "block_decomposition.h" 12 | 13 | int compareOneSumComponents(const void* a, const void* b) 14 | { 15 | return ((CMR_BLOCK*)b)->matrix->numNonzeros - 16 | ((CMR_BLOCK*)a)->matrix->numNonzeros; 17 | } 18 | 19 | 20 | CMR_ERROR CMRregularitySearchOnesum(CMR* cmr, DecompositionTask* task, DecompositionQueue* queue) 21 | { 22 | assert(cmr); 23 | assert(task); 24 | assert(task->node); 25 | assert(task->node->matrix); 26 | 27 | #if defined(CMR_DEBUG) 28 | CMRdbgMsg(6, "Searching for 1-separations for the following matrix:\n"); 29 | CMR_CALL( CMRchrmatPrintDense(cmr, task->dec->matrix, stdout, '0', true) ); 30 | #endif /* CMR_DEBUG */ 31 | 32 | /* Perform 1-sum decomposition. */ 33 | 34 | size_t numComponents; 35 | CMR_BLOCK* components = NULL; 36 | CMR_CALL( CMRdecomposeBlocks(cmr, (CMR_MATRIX*) task->node->matrix, sizeof(char), sizeof(char), &numComponents, &components, 37 | NULL, NULL, NULL, NULL) ); 38 | 39 | if (numComponents == 1) 40 | { 41 | CMRdbgMsg(6, "Matrix is 2-connected.\n", numComponents); 42 | 43 | CMR_CALL( CMRchrmatFree(cmr, (CMR_CHRMAT**) &components[0].matrix) ); 44 | CMR_CALL( CMRchrmatFree(cmr, (CMR_CHRMAT**) &components[0].transpose) ); 45 | CMR_CALL( CMRfreeBlockArray(cmr, &components[0].rowsToOriginal) ); 46 | CMR_CALL( CMRfreeBlockArray(cmr, &components[0].columnsToOriginal) ); 47 | 48 | /* Just mark it as 2-connected and add it back to the list of unprocessed tasks. */ 49 | task->node->testedTwoConnected = true; 50 | CMRregularityQueueAdd(queue, task); 51 | } 52 | else if (numComponents >= 2) 53 | { 54 | CMRdbgMsg(6, "The 1-sum consists of %zu components.\n", numComponents); 55 | 56 | /* Space for mapping of rows/columns to rows. */ 57 | CMR_ELEMENT* rowsToParent = NULL; 58 | CMR_CALL( CMRallocStackArray(cmr, &rowsToParent, task->node->numRows) ); 59 | CMR_ELEMENT* columnsToParent = NULL; 60 | CMR_CALL( CMRallocStackArray(cmr, &columnsToParent, task->node->numColumns) ); 61 | 62 | /* We now create the children. */ 63 | CMR_CALL( CMRseymourUpdateOnesum(cmr, task->node, numComponents) ); 64 | for (size_t comp = 0; comp < numComponents; ++comp) 65 | { 66 | CMR_BLOCK* component = &components[comp]; 67 | for (size_t row = 0; row < component->matrix->numRows; ++row) 68 | rowsToParent[row] = CMRrowToElement(component->rowsToOriginal[row]); 69 | for (size_t column = 0; column < component->matrix->numColumns; ++column) 70 | columnsToParent[column] = CMRcolumnToElement(component->columnsToOriginal[column]); 71 | CMR_CALL( CMRseymourCreateChildFromMatrices(cmr, task->node, comp, (CMR_CHRMAT*) component->matrix, 72 | (CMR_CHRMAT*) component->transpose, rowsToParent, columnsToParent) ); 73 | 74 | CMR_CALL( CMRfreeBlockArray(cmr, &component->rowsToOriginal) ); 75 | CMR_CALL( CMRfreeBlockArray(cmr, &component->columnsToOriginal) ); 76 | } 77 | task->node->type = CMR_SEYMOUR_NODE_TYPE_ONESUM; 78 | 79 | CMR_CALL( CMRfreeStackArray(cmr, &columnsToParent) ); 80 | CMR_CALL( CMRfreeStackArray(cmr, &rowsToParent) ); 81 | 82 | for (size_t c = numComponents; c > 0; --c) 83 | { 84 | size_t child = c - 1; 85 | task->node->children[child]->testedTwoConnected = true; 86 | DecompositionTask* childTask = NULL; 87 | CMR_CALL( CMRregularityTaskCreateRoot(cmr, task->node->children[child], &childTask, task->params, task->stats, 88 | task->startClock, task->timeLimit) ); 89 | CMRregularityQueueAdd(queue, childTask); 90 | } 91 | 92 | CMR_CALL( CMRregularityTaskFree(cmr, &task) ); 93 | } 94 | 95 | CMR_CALL( CMRfreeBlockArray(cmr, &components) ); 96 | 97 | return CMR_OKAY; 98 | } 99 | -------------------------------------------------------------------------------- /src/cmr/regularity_r10.c: -------------------------------------------------------------------------------- 1 | // #define CMR_DEBUG /* Uncomment to debug this file. */ 2 | 3 | #include "env_internal.h" 4 | #include "seymour_internal.h" 5 | 6 | #include 7 | 8 | CMR_ERROR CMRregularityTestR10(CMR* cmr, DecompositionTask* task, DecompositionQueue* queue) 9 | { 10 | assert(task); 11 | assert(queue); 12 | 13 | CMR_SEYMOUR_NODE* dec = task->node; 14 | assert(dec); 15 | 16 | #if defined(CMR_DEBUG) 17 | CMRdbgMsg(2, "Testing for representation of R10.\n"); 18 | CMR_CALL( CMRchrmatPrintDense(cmr, dec->matrix, stdout, '0', true) ); 19 | #endif /* CMR_DEBUG */ 20 | 21 | 22 | dec->testedR10 = true; 23 | if (dec->numRows != 5 || dec->numColumns != 5) 24 | goto cleanup; 25 | 26 | size_t count3 = 0; 27 | size_t count5 = 0; 28 | for (size_t row = 0; row < 5; ++row) 29 | { 30 | size_t numNonzeros = dec->matrix->rowSlice[row + 1] - dec->matrix->rowSlice[row]; 31 | if (numNonzeros == 3) 32 | ++count3; 33 | else if (numNonzeros == 5) 34 | ++count5; 35 | else 36 | goto cleanup; 37 | } 38 | if ((count3 != 4 || count5 != 1) && count3 != 5) 39 | goto cleanup; 40 | 41 | count3 = 0; 42 | count5 = 0; 43 | size_t columnCount[5] = {0, 0, 0, 0, 0}; 44 | for (size_t e = 0; e < dec->matrix->numNonzeros; ++e) 45 | columnCount[dec->matrix->entryColumns[e]]++; 46 | for (size_t column = 0; column < 5; ++column) 47 | { 48 | if (columnCount[column] == 3) 49 | ++count3; 50 | else if (columnCount[column] == 5) 51 | ++count5; 52 | else 53 | goto cleanup; 54 | } 55 | if ((count3 != 4 || count5 != 1) && count3 != 5) 56 | goto cleanup; 57 | 58 | /* The number of nonzeros in the rows/columns are 2, 2, 2, 2 and 5. Every 3-connected 5-by-5 matrix with this property 59 | * represents R10. 60 | */ 61 | 62 | if (dec->isTernary) 63 | { 64 | bool isCamion; 65 | CMR_SUBMAT* violatorSubmatrix = NULL; 66 | CMR_CALL( CMRcamionTestSigns(cmr, dec->matrix, &isCamion, &violatorSubmatrix, 67 | task->stats ? &task->stats->network.camion : NULL, DBL_MAX) ); 68 | 69 | if (violatorSubmatrix) 70 | { 71 | assert(!isCamion); 72 | 73 | /* TODO: Do we actually need the child node or just the irregularity information? */ 74 | CMR_CALL( CMRseymourUpdateViolator(cmr, dec, violatorSubmatrix) ); 75 | 76 | /* Task is done. */ 77 | CMR_CALL( CMRregularityTaskFree(cmr, &task) ); 78 | queue->foundIrregularity = true; 79 | 80 | return CMR_OKAY; 81 | } 82 | else 83 | { 84 | assert(isCamion); 85 | } 86 | } 87 | 88 | dec->type = CMR_SEYMOUR_NODE_TYPE_R10; 89 | queue->foundNongraphicness = true; 90 | queue->foundNoncographicness = true; 91 | 92 | cleanup: 93 | 94 | if (dec->type == CMR_SEYMOUR_NODE_TYPE_R10) 95 | { 96 | CMR_CALL( CMRregularityTaskFree(cmr, &task) ); 97 | } 98 | else 99 | { 100 | CMRregularityQueueAdd(queue, task); 101 | } 102 | 103 | return CMR_OKAY; 104 | } 105 | -------------------------------------------------------------------------------- /src/cmr/sign.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discopt/cmr/acfda4fe72cb73eb68a7ed20ebfcf4fdb3027542/src/cmr/sign.c -------------------------------------------------------------------------------- /src/cmr/sort.c: -------------------------------------------------------------------------------- 1 | // #define CMR_DEBUG /* Uncomment to debug the sorting. */ 2 | 3 | #include "sort.h" 4 | 5 | #include 6 | #include 7 | 8 | #define MOVE_ELEMENT(source, target, size) \ 9 | do \ 10 | { \ 11 | size_t __size = (size); \ 12 | char* __s = (source); \ 13 | char* __t = (target); \ 14 | do \ 15 | { \ 16 | *__t++ = *__s++; \ 17 | } \ 18 | while (--__size > 0); \ 19 | } \ 20 | while (false) 21 | 22 | CMR_ERROR CMRsort(CMR* cmr, size_t length, void* array, size_t elementSize, int (*compare)(const void*, const void*)) 23 | { 24 | CMR_UNUSED(cmr); 25 | 26 | assert(cmr); 27 | 28 | qsort(array, length, elementSize, compare); 29 | 30 | return CMR_OKAY; 31 | } 32 | 33 | CMR_ERROR CMRsort2(CMR* cmr, size_t length, void* array1, size_t elementSize1, void* array2, size_t elementSize2, 34 | int (*compare)(const void**, const void**)) 35 | { 36 | assert(cmr); 37 | 38 | char* data1 = array1; 39 | char* data2 = array2; 40 | 41 | CMRassertStackConsistency(cmr); 42 | 43 | /* Allocate space for temporary elements of both arrays. */ 44 | char* temp1 = NULL; 45 | CMR_CALL( CMRallocStackArray(cmr, &temp1, elementSize1) ); 46 | char* temp2 = NULL; 47 | CMR_CALL( CMRallocStackArray(cmr, &temp2, elementSize2) ); 48 | 49 | /* Create an array with pointers into array1. */ 50 | char** pointerArray = NULL; 51 | CMR_CALL( CMRallocStackArray(cmr, &pointerArray, length) ); 52 | char* pointer = data1; 53 | for (size_t i = 0; i < length; ++i) 54 | { 55 | pointerArray[i] = pointer; 56 | pointer += elementSize1; 57 | } 58 | 59 | qsort(pointerArray, length, sizeof(size_t*), (int (*)(const void*, const void*)) compare); 60 | 61 | /* Reorder data1 and data2 according to the array of pointers. */ 62 | for (size_t i = 0; i < length; ++i) 63 | { 64 | CMRdbgMsg(0, "i = %d, pointerArray[i] = %p array1 = %p, difference = %ld, scaled = %d\n", i, pointerArray[i], data1, 65 | pointerArray[i] - data1, (pointerArray[i] - data1) / elementSize1); 66 | if (i != (pointerArray[i] - data1) / elementSize1) 67 | { 68 | MOVE_ELEMENT(data1 + elementSize1 * i, temp1, elementSize1); /* temp1 = array1[i] */ 69 | MOVE_ELEMENT(data2 + elementSize2 * i, temp2, elementSize2); /* temp2 = array2[i] */ 70 | size_t j; 71 | size_t k = i; 72 | while (i != (j = (pointerArray[k] - data1) / elementSize1)) 73 | { 74 | MOVE_ELEMENT(data1 + elementSize1 * j, data1 + elementSize1 * k, elementSize1); /* data1[k] = data1[j] */ 75 | MOVE_ELEMENT(data2 + elementSize2 * j, data2 + elementSize2 * k, elementSize2); /* data2[k] = data2[j] */ 76 | pointerArray[k] = data1 + elementSize1 * k; 77 | k = j; 78 | } 79 | MOVE_ELEMENT(temp1, data1 + elementSize1 * k, elementSize1); /* data1[k] = temp1 */ 80 | MOVE_ELEMENT(temp2, data2 + elementSize2 * k, elementSize2); /* data2[k] = temp2 */ 81 | pointerArray[k] = data1 + elementSize1 * k; 82 | } 83 | } 84 | 85 | /* Free the temporary space. */ 86 | CMR_CALL( CMRfreeStackArray(cmr, &pointerArray) ); 87 | CMR_CALL( CMRfreeStackArray(cmr, &temp2) ); 88 | CMR_CALL( CMRfreeStackArray(cmr, &temp1) ); 89 | CMRassertStackConsistency(cmr); 90 | 91 | return CMR_OKAY; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /src/cmr/sort.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_SORT_INTERNAL_H 2 | #define CMR_SORT_INTERNAL_H 3 | 4 | #include "env_internal.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * \brief Sorts an array. 12 | * 13 | * The user must provide a \p compare function that is called several times with pointers into the first array. 14 | * The arguments must be dereferenced once in order to access the actual element. 15 | */ 16 | 17 | CMR_ERROR CMRsort( 18 | CMR* cmr, /**< \ref CMR environment. */ 19 | size_t length, /**< Number of elements in both arrays. */ 20 | void *array, /**< Pointer to the first element of the array. */ 21 | size_t elementSize, /**< Size (in bytes) of each element of the array. */ 22 | int (*compare)(const void*, const void*) /**< Comparison function for comparing two elements. */ 23 | ); 24 | 25 | /** 26 | * \brief Sorts two arrays simultaneously. 27 | * 28 | * Sorts two arrays using the qsort function from the C library. The user must provide a \p compare function that is 29 | * called several times with pointers to pointers into the first array. Hence, the arguments must be dereferenced twice 30 | * in order to access the actual element. 31 | */ 32 | 33 | CMR_ERROR CMRsort2( 34 | CMR* cmr, /**< \ref CMR environment. */ 35 | size_t length, /**< Number of elements in both arrays. */ 36 | void *array1, /**< Pointer to the first element of the first array. */ 37 | size_t elementSize1, /**< Size (in bytes) of each element of the first array. */ 38 | void* array2, /**< Pointer to the first element of the second array. */ 39 | size_t elementSize2, /**< Size (in bytes) of each element of the second array. */ 40 | int (*compare)(const void**, const void**) /**< Comparison function for comparing two elements. */ 41 | ); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif /* CMR_SORT_INTERNAL_H */ 48 | -------------------------------------------------------------------------------- /src/gen/gurobi_extract.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | typedef enum 14 | { 15 | FILEFORMAT_UNDEFINED = 0, /**< Whether the file format of output was defined by the user. */ 16 | FILEFORMAT_MATRIX_DENSE = 1, /**< Dense matrix format. */ 17 | FILEFORMAT_MATRIX_SPARSE = 2, /**< Sparse matrix format. */ 18 | } FileFormat; 19 | 20 | #define GRB_CALL(x) \ 21 | do \ 22 | { \ 23 | int _grb_error = x; \ 24 | if (_grb_error) \ 25 | { \ 26 | fprintf(stderr, "[Gurobi error <%d>: %s]", _grb_error, GRBgeterrormsg(env)); \ 27 | } \ 28 | } while(0) 29 | 30 | static 31 | CMR_ERROR extractMatrixGurobi( 32 | char* mipFileName, /**< File name of MIP. */ 33 | FileFormat outputFormat /**< Output file format. */ 34 | ) 35 | { 36 | GRBenv* env = NULL; 37 | GRBemptyenv(&env); 38 | GRB_CALL( GRBsetintparam(env, "outputflag", 0) ); 39 | GRBstartenv(env); 40 | 41 | GRBmodel* model = NULL; 42 | GRB_CALL( GRBreadmodel(env, mipFileName, &model) ); 43 | 44 | int numRows, numColumns, numNonzeros; 45 | GRB_CALL( GRBgetintattr(model, GRB_INT_ATTR_NUMCONSTRS, &numRows) ); 46 | GRB_CALL( GRBgetintattr(model, GRB_INT_ATTR_NUMVARS, &numColumns) ); 47 | GRB_CALL( GRBgetintattr(model, GRB_INT_ATTR_NUMNZS, &numNonzeros) ); 48 | 49 | CMR* cmr = NULL; 50 | CMR_CALL( CMRcreateEnvironment(&cmr) ); 51 | 52 | CMR_DBLMAT* matrix = NULL; 53 | CMR_CALL( CMRdblmatCreate(cmr, &matrix, numRows, numColumns, numNonzeros) ); 54 | size_t entry = 0; 55 | 56 | int* begin = NULL; 57 | int* indices = NULL; 58 | double* values = NULL; 59 | CMR_CALL( CMRallocBlockArray(cmr, &begin, numRows+1) ); 60 | CMR_CALL( CMRallocBlockArray(cmr, &indices, numNonzeros) ); 61 | CMR_CALL( CMRallocBlockArray(cmr, &values, numNonzeros) ); 62 | 63 | GRB_CALL( GRBgetconstrs(model, &numNonzeros, begin, indices, values, 0, numRows) ); 64 | for (size_t row = 0; row < numRows; ++row) 65 | { 66 | matrix->rowSlice[row] = entry; 67 | size_t beyond = (row+1 < numRows) ? begin[row+1] : numNonzeros; 68 | for (size_t i = begin[row]; i < beyond; ++i) 69 | { 70 | matrix->entryColumns[entry] = indices[i]; 71 | matrix->entryValues[entry] = values[i]; 72 | ++entry; 73 | } 74 | } 75 | matrix->rowSlice[numRows] = entry; 76 | assert(entry == numNonzeros); 77 | 78 | if (outputFormat == FILEFORMAT_MATRIX_SPARSE) 79 | CMR_CALL( CMRdblmatPrintSparse(cmr, matrix, stdout) ); 80 | else if (outputFormat == FILEFORMAT_MATRIX_DENSE) 81 | CMR_CALL( CMRdblmatPrintDense(cmr, matrix, stdout, '0', false) ); 82 | 83 | CMR_CALL( CMRfreeBlockArray(cmr, &values) ); 84 | CMR_CALL( CMRfreeBlockArray(cmr, &indices) ); 85 | CMR_CALL( CMRfreeBlockArray(cmr, &begin) ); 86 | CMR_CALL( CMRdblmatFree(cmr, &matrix) ); 87 | CMR_CALL( CMRfreeEnvironment(&cmr) ); 88 | 89 | GRBfreemodel(model); 90 | 91 | GRBfreeenv(env); 92 | 93 | return CMR_OKAY; 94 | } 95 | 96 | /** 97 | * \brief Prints the usage of the \p program to stdout. 98 | * 99 | * \returns \c EXIT_FAILURE. 100 | */ 101 | 102 | int printUsage(const char* program) 103 | { 104 | fputs("Usage:\n", stderr); 105 | 106 | fprintf(stderr, "%s MIPFILE [OPTION]...\n", program); 107 | fputs(" extracts the coefficient matrix of a mixed-integer program in MIPFILE and writes it to stdout.\n", stderr); 108 | fputs("\n", stderr); 109 | 110 | fputs("Options:\n", stderr); 111 | fputs(" -o FORMAT Format of output matrix; default: dense.\n", stderr); 112 | fputs("\n", stderr); 113 | 114 | fputs("Formats for matrices: dense, sparse\n", stderr); 115 | fputs("MIPFILE must refer to a file that Gurobi can read.\n", stderr); 116 | 117 | return EXIT_FAILURE; 118 | } 119 | 120 | int main(int argc, char** argv) 121 | { 122 | FileFormat outputFormat = FILEFORMAT_MATRIX_DENSE; 123 | char* mipFileName = NULL; 124 | for (int a = 1; a < argc; ++a) 125 | { 126 | if (!strcmp(argv[a], "-h")) 127 | { 128 | printUsage(argv[0]); 129 | return EXIT_SUCCESS; 130 | } 131 | else if (!strcmp(argv[a], "-o") && (a+1 < argc)) 132 | { 133 | if (!strcmp(argv[a+1], "dense")) 134 | outputFormat = FILEFORMAT_MATRIX_DENSE; 135 | else if (!strcmp(argv[a+1], "sparse")) 136 | outputFormat = FILEFORMAT_MATRIX_SPARSE; 137 | else 138 | { 139 | printf("Error: unknown output format <%s>.\n\n", argv[a+1]); 140 | return printUsage(argv[0]); 141 | } 142 | ++a; 143 | } 144 | else if (!mipFileName) 145 | { 146 | mipFileName = argv[a]; 147 | } 148 | else 149 | { 150 | printf("Error: more than two MIP file names: %s %s\n\n", mipFileName, argv[a]); 151 | return printUsage(argv[0]); 152 | } 153 | } 154 | 155 | if (!mipFileName) 156 | { 157 | puts("Error: no MIP file name specified.\n"); 158 | return printUsage(argv[0]); 159 | } 160 | 161 | CMR_ERROR error = extractMatrixGurobi(mipFileName, outputFormat); 162 | switch (error) 163 | { 164 | case CMR_ERROR_INPUT: 165 | puts("Input error."); 166 | return EXIT_FAILURE; 167 | case CMR_ERROR_MEMORY: 168 | puts("Memory error."); 169 | return EXIT_FAILURE; 170 | default: 171 | return EXIT_SUCCESS; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/gen/wheel_gen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | typedef enum 13 | { 14 | FILEFORMAT_UNDEFINED = 0, /**< Whether the file format of output was defined by the user. */ 15 | FILEFORMAT_MATRIX_DENSE = 1, /**< Dense matrix format. */ 16 | FILEFORMAT_MATRIX_SPARSE = 2, /**< Sparse matrix format. */ 17 | } FileFormat; 18 | 19 | /** 20 | * \brief Writes the actual wheel matrix to stdout. 21 | */ 22 | 23 | CMR_ERROR genMatrixWheel( 24 | size_t numRowsColumns, /**< Number of rows and columns of matrix. */ 25 | bool change01, /**< In each column with two 0s in rows 1 and 2, replace them by 1s. */ 26 | FileFormat outputFormat /**< Output file format. */ 27 | ) 28 | { 29 | clock_t startTime = clock(); 30 | CMR* cmr = NULL; 31 | CMR_CALL( CMRcreateEnvironment(&cmr) ); 32 | 33 | CMR_CHRMAT* matrix = NULL; 34 | CMRchrmatCreate(cmr, &matrix, numRowsColumns, numRowsColumns, 4 * numRowsColumns); 35 | 36 | /* Create the nonzeros. */ 37 | matrix->numNonzeros = 0; 38 | for (size_t row = 0; row < numRowsColumns; ++row) 39 | { 40 | matrix->rowSlice[row] = matrix->numNonzeros; 41 | if (row + 1 == numRowsColumns) 42 | { 43 | matrix->entryValues[matrix->numNonzeros] = 1; 44 | matrix->entryColumns[matrix->numNonzeros++] = 0; 45 | } 46 | matrix->entryValues[matrix->numNonzeros] = 1; 47 | matrix->entryColumns[matrix->numNonzeros++] = row; 48 | if (row + 1 < numRowsColumns) 49 | { 50 | matrix->entryValues[matrix->numNonzeros] = 1; 51 | matrix->entryColumns[matrix->numNonzeros++] = row + 1; 52 | } 53 | if (change01 && row < 2) 54 | { 55 | for (size_t c = 3; c < numRowsColumns; ++c) 56 | { 57 | matrix->entryValues[matrix->numNonzeros] = 1; 58 | matrix->entryColumns[matrix->numNonzeros++] = c; 59 | } 60 | } 61 | } 62 | matrix->rowSlice[numRowsColumns] = matrix->numNonzeros; 63 | 64 | double generationTime = (clock() - startTime) * 1.0 / CLOCKS_PER_SEC; 65 | fprintf(stderr, "Generated a %zux%zu matrix with %zu nonzeros in %f seconds.\n", numRowsColumns, numRowsColumns, 66 | matrix->numNonzeros, generationTime); 67 | 68 | /* Print matrix. */ 69 | if (outputFormat == FILEFORMAT_MATRIX_DENSE) 70 | CMR_CALL( CMRchrmatPrintDense(cmr, matrix, stdout, '0', false) ); 71 | else if (outputFormat == FILEFORMAT_MATRIX_SPARSE) 72 | CMR_CALL( CMRchrmatPrintSparse(cmr, matrix, stdout) ); 73 | 74 | /* Cleanup. */ 75 | CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 76 | 77 | CMR_CALL( CMRfreeEnvironment(&cmr) ); 78 | 79 | return CMR_OKAY; 80 | } 81 | 82 | /** 83 | * \brief Prints the usage of the \p program to stdout. 84 | * 85 | * \returns \c EXIT_FAILURE. 86 | */ 87 | 88 | int printUsage(const char* program) 89 | { 90 | fputs("Usage:\n", stderr); 91 | fprintf(stderr, "%s ORDER [OPTION]...\n", program); 92 | fputs(" creates an ORDER-by-ORDER wheel matrix and writes it to stdout.\n", stderr); 93 | fputs("\n", stderr); 94 | 95 | fputs("Options:\n", stderr); 96 | fputs(" -01 In each column with two 0s in rows 1 and 2, replace them by 1s; default: off.\n", stderr); 97 | fputs(" -o FORMAT Format of output matrix; default: dense.\n", stderr); 98 | fputs("\n", stderr); 99 | 100 | fputs("Formats for matrices: dense, sparse\n", stderr); 101 | 102 | return EXIT_FAILURE; 103 | } 104 | 105 | int main(int argc, char** argv) 106 | { 107 | struct timeval curTime; 108 | gettimeofday(&curTime, NULL); 109 | srand(curTime.tv_usec); 110 | 111 | FileFormat outputFormat = FILEFORMAT_UNDEFINED; 112 | size_t numRowsColumns = SIZE_MAX; 113 | bool change01 = false; 114 | for (int a = 1; a < argc; ++a) 115 | { 116 | if (!strcmp(argv[a], "-h")) 117 | { 118 | printUsage(argv[0]); 119 | return EXIT_SUCCESS; 120 | } 121 | else if (!strcmp(argv[a], "-01")) 122 | change01 = true; 123 | else if (!strcmp(argv[a], "-o") && (a+1 < argc)) 124 | { 125 | if (!strcmp(argv[a+1], "dense")) 126 | outputFormat = FILEFORMAT_MATRIX_DENSE; 127 | else if (!strcmp(argv[a+1], "sparse")) 128 | outputFormat = FILEFORMAT_MATRIX_SPARSE; 129 | else 130 | { 131 | fprintf(stderr, "Error: unknown output format <%s>.\n\n", argv[a+1]); 132 | return printUsage(argv[0]); 133 | } 134 | ++a; 135 | } 136 | else if (numRowsColumns == SIZE_MAX) 137 | { 138 | char* p = NULL; 139 | numRowsColumns = strtoull(argv[a], &p, 10); 140 | if (*p != '\0') 141 | { 142 | fprintf(stderr, "Error: invalid number of rows/columns <%s>.\n\n", argv[a]); 143 | printUsage(argv[0]); 144 | return EXIT_FAILURE; 145 | } 146 | } 147 | else 148 | { 149 | fprintf(stderr, "Error: more than one size indicator specified: %zu %s\n\n", numRowsColumns, argv[a]); 150 | return printUsage(argv[0]); 151 | } 152 | } 153 | 154 | if (numRowsColumns == SIZE_MAX) 155 | { 156 | fputs("Error: no size indicator specified.\n", stderr); 157 | return printUsage(argv[0]); 158 | } 159 | else if (numRowsColumns <= 0) 160 | { 161 | fputs("Error: matrix must have at least 1 row/column.\n", stderr); 162 | return printUsage(argv[0]); 163 | } 164 | if (outputFormat == FILEFORMAT_UNDEFINED) 165 | outputFormat = FILEFORMAT_MATRIX_DENSE; 166 | 167 | CMR_ERROR error = genMatrixWheel(numRowsColumns, change01, outputFormat); 168 | switch (error) 169 | { 170 | case CMR_ERROR_INPUT: 171 | fputs("Input error.\n", stderr); 172 | return EXIT_FAILURE; 173 | case CMR_ERROR_MEMORY: 174 | fputs("Memory error.\n", stderr); 175 | return EXIT_FAILURE; 176 | default: 177 | return EXIT_SUCCESS; 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(GTest) 2 | if(NOT GTEST_FOUND) 3 | find_package(GTestSources) 4 | if(NOT GTESTSOURCES_FOUND) 5 | find_package(GTestGit) 6 | endif() 7 | endif() 8 | 9 | # Target for the googletest. 10 | add_executable(cmr_gtest 11 | common.c 12 | test_balanced.cpp 13 | test_camion.cpp 14 | test_ctu.cpp 15 | test_equimodular.cpp 16 | test_graph.cpp 17 | test_graphic.cpp 18 | test_hashtable.cpp 19 | test_matrix.cpp 20 | test_matroid.cpp 21 | test_main.cpp 22 | test_network.cpp 23 | test_regular.cpp 24 | test_separation.cpp 25 | test_series_parallel.cpp 26 | test_tu.cpp) 27 | 28 | # Set standard to C++ 14. 29 | set_property(TARGET cmr_gtest PROPERTY CXX_STANDARD 14) 30 | 31 | # Add tests for non-exported functions only for static library. 32 | if(NOT SHARED) 33 | target_sources(cmr_gtest 34 | PRIVATE 35 | test_block_decomposition.cpp 36 | ) 37 | target_include_directories(cmr_gtest 38 | PRIVATE 39 | $ # contains private headers. 40 | ) 41 | 42 | endif() 43 | 44 | # Configure cmr_gtest target. 45 | target_compile_features(cmr_gtest PRIVATE cxx_auto_type) 46 | target_link_libraries(cmr_gtest gtest_main gtest CMR::cmr) 47 | 48 | include(GoogleTest) 49 | gtest_discover_tests(cmr_gtest) 50 | -------------------------------------------------------------------------------- /test/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | CMR_ERROR stringToDoubleMatrix(CMR* cmr, CMR_DBLMAT** pmatrix, const char* string) 9 | { 10 | assert(cmr); 11 | char* end; 12 | int numRows, numColumns; 13 | 14 | /* Read size of matrix. */ 15 | numRows = strtol(string, &end, 10); 16 | assert(end > string); 17 | string = end; 18 | numColumns = strtol(string, &end, 10); 19 | assert(end > string); 20 | string = end; 21 | 22 | CMRdblmatCreate(cmr, pmatrix, numRows, numColumns, numRows * numColumns); 23 | 24 | (*pmatrix)->numNonzeros = 0; 25 | for (size_t row = 0; row < (*pmatrix)->numRows; ++row) 26 | { 27 | (*pmatrix)->rowSlice[row] = (*pmatrix)->numNonzeros; 28 | for (size_t column = 0; column < (*pmatrix)->numColumns; ++column) 29 | { 30 | double x = strtod(string, &end); 31 | assert(end > string); 32 | string = end; 33 | 34 | if (x != 0.0) 35 | { 36 | (*pmatrix)->entryColumns[(*pmatrix)->numNonzeros] = column; 37 | (*pmatrix)->entryValues[(*pmatrix)->numNonzeros] = x; 38 | (*pmatrix)->numNonzeros++; 39 | } 40 | } 41 | } 42 | (*pmatrix)->rowSlice[numRows] = (*pmatrix)->numNonzeros; 43 | 44 | return CMR_OKAY; 45 | } 46 | 47 | CMR_ERROR stringToIntMatrix(CMR* cmr, CMR_INTMAT** pmatrix, const char* string) 48 | { 49 | assert(cmr); 50 | char* end; 51 | int numRows, numColumns; 52 | 53 | /* Read size of matrix. */ 54 | numRows = strtol(string, &end, 10); 55 | assert(end > string); 56 | string = end; 57 | numColumns = strtol(string, &end, 10); 58 | assert(end > string); 59 | string = end; 60 | 61 | CMRintmatCreate(cmr, pmatrix, numRows, numColumns, numRows * numColumns); 62 | 63 | (*pmatrix)->numNonzeros = 0; 64 | for (size_t row = 0; row < (*pmatrix)->numRows; ++row) 65 | { 66 | (*pmatrix)->rowSlice[row] = (*pmatrix)->numNonzeros; 67 | for (size_t column = 0; column < (*pmatrix)->numColumns; ++column) 68 | { 69 | int x = strtol(string, &end, 10); 70 | assert(end > string); 71 | string = end; 72 | 73 | if (x != 0) 74 | { 75 | (*pmatrix)->entryColumns[(*pmatrix)->numNonzeros] = column; 76 | (*pmatrix)->entryValues[(*pmatrix)->numNonzeros] = x; 77 | (*pmatrix)->numNonzeros++; 78 | } 79 | } 80 | } 81 | (*pmatrix)->rowSlice[numRows] = (*pmatrix)->numNonzeros; 82 | 83 | return CMR_OKAY; 84 | } 85 | 86 | CMR_ERROR stringToCharMatrix(CMR* cmr, CMR_CHRMAT** pmatrix, const char* string) 87 | { 88 | assert(cmr); 89 | char* end; 90 | size_t numRows, numColumns; 91 | 92 | /* Read size of matrix. */ 93 | numRows = strtol(string, &end, 10); 94 | assert(end > string); 95 | string = end; 96 | numColumns = strtol(string, &end, 10); 97 | assert(end > string); 98 | string = end; 99 | 100 | CMR_CALL( CMRchrmatCreate(cmr, pmatrix, numRows, numColumns, numRows * numColumns) ); 101 | 102 | (*pmatrix)->numNonzeros = 0; 103 | for (size_t row = 0; row < (*pmatrix)->numRows; ++row) 104 | { 105 | (*pmatrix)->rowSlice[row] = (*pmatrix)->numNonzeros; 106 | for (size_t column = 0; column < (*pmatrix)->numColumns; ++column) 107 | { 108 | int x = strtol(string, &end, 10); 109 | if (end == string) 110 | return CMR_ERROR_INPUT; 111 | string = end; 112 | 113 | if (x != 0) 114 | { 115 | (*pmatrix)->entryColumns[(*pmatrix)->numNonzeros] = column; 116 | (*pmatrix)->entryValues[(*pmatrix)->numNonzeros] = x; 117 | (*pmatrix)->numNonzeros++; 118 | } 119 | } 120 | } 121 | (*pmatrix)->rowSlice[numRows] = (*pmatrix)->numNonzeros; 122 | 123 | return CMR_OKAY; 124 | } 125 | -------------------------------------------------------------------------------- /test/common.h: -------------------------------------------------------------------------------- 1 | #ifndef CMR_TEST_COMMON_H 2 | #define CMR_TEST_COMMON_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | CMR_ERROR stringToDoubleMatrix(CMR* cmr, CMR_DBLMAT** matrix, const char* string); 12 | 13 | CMR_ERROR stringToIntMatrix(CMR* cmr, CMR_INTMAT** matrix, const char* string); 14 | 15 | CMR_ERROR stringToCharMatrix(CMR* cmr, CMR_CHRMAT** matrix, const char* string); 16 | 17 | #define ASSERT_CMR_CALL(x) \ 18 | do \ 19 | { \ 20 | CMR_ERROR _error = (x); \ 21 | ASSERT_EQ(_error, CMR_OKAY); \ 22 | } \ 23 | while (false) 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | #endif /* CMR_TEST_COMMON_H */ 30 | -------------------------------------------------------------------------------- /test/test_balanced.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | 5 | #include 6 | 7 | TEST(Balanced, Submatrix) 8 | { 9 | CMR* cmr = NULL; 10 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 11 | 12 | { 13 | CMR_CHRMAT* matrix = NULL; 14 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "4 4 " 15 | " 1 0 0 1 " 16 | " 1 1 0 0 " 17 | " 0 1 1 0 " 18 | " 0 0 1 1 " 19 | ) ); 20 | 21 | bool isBalanced; 22 | CMR_BALANCED_PARAMS params; 23 | ASSERT_CMR_CALL( CMRbalancedParamsInit(¶ms) ); 24 | params.algorithm = CMR_BALANCED_ALGORITHM_SUBMATRIX; 25 | ASSERT_CMR_CALL( CMRbalancedTest(cmr, matrix, &isBalanced, NULL, ¶ms, NULL, DBL_MAX) ); 26 | 27 | ASSERT_TRUE( isBalanced ); 28 | 29 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 30 | } 31 | 32 | { 33 | CMR_CHRMAT* matrix = NULL; 34 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "4 4 " 35 | " 1 0 0 1 " 36 | " 1 1 0 0 " 37 | " 0 1 1 0 " 38 | " 0 0 1 -1 " 39 | ) ); 40 | 41 | bool isBalanced; 42 | CMR_BALANCED_PARAMS params; 43 | ASSERT_CMR_CALL( CMRbalancedParamsInit(¶ms) ); 44 | params.algorithm = CMR_BALANCED_ALGORITHM_SUBMATRIX; 45 | ASSERT_CMR_CALL( CMRbalancedTest(cmr, matrix, &isBalanced, NULL, ¶ms, NULL, DBL_MAX) ); 46 | 47 | ASSERT_FALSE( isBalanced ); 48 | 49 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 50 | } 51 | 52 | { 53 | CMR_CHRMAT* matrix = NULL; 54 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "5 5 " 55 | " 1 0 0 0 1 " 56 | " 1 1 0 0 0 " 57 | " 0 1 1 0 0 " 58 | " 0 0 1 1 0 " 59 | " 0 0 0 1 1 " 60 | ) ); 61 | 62 | bool isBalanced; 63 | CMR_BALANCED_PARAMS params; 64 | ASSERT_CMR_CALL( CMRbalancedParamsInit(¶ms) ); 65 | params.algorithm = CMR_BALANCED_ALGORITHM_SUBMATRIX; 66 | ASSERT_CMR_CALL( CMRbalancedTest(cmr, matrix, &isBalanced, NULL, ¶ms, NULL, DBL_MAX) ); 67 | 68 | ASSERT_FALSE( isBalanced ); 69 | 70 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 71 | } 72 | 73 | { 74 | CMR_CHRMAT* matrix = NULL; 75 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "5 5 " 76 | " 1 0 0 0 0 1 " 77 | " 1 1 0 0 0 0 " 78 | " 0 1 1 0 0 0 " 79 | " 0 0 1 1 0 0 " 80 | " 0 0 0 1 1 0 " 81 | " 0 0 0 0 1 1 " 82 | ) ); 83 | 84 | bool isBalanced; 85 | CMR_BALANCED_PARAMS params; 86 | ASSERT_CMR_CALL( CMRbalancedParamsInit(¶ms) ); 87 | params.algorithm = CMR_BALANCED_ALGORITHM_SUBMATRIX; 88 | ASSERT_CMR_CALL( CMRbalancedTest(cmr, matrix, &isBalanced, NULL, ¶ms, NULL, DBL_MAX) ); 89 | 90 | ASSERT_TRUE( isBalanced ); 91 | 92 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 93 | } 94 | 95 | { 96 | CMR_CHRMAT* matrix = NULL; 97 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "5 4 " 98 | " 1 1 0 0 " 99 | " 1 1 1 0 " 100 | " 1 0 0 -1 " 101 | " 0 1 1 1 " 102 | " 0 0 1 0 " 103 | ) ); 104 | 105 | bool isBalanced; 106 | CMR_BALANCED_PARAMS params; 107 | ASSERT_CMR_CALL( CMRbalancedParamsInit(¶ms) ); 108 | params.algorithm = CMR_BALANCED_ALGORITHM_SUBMATRIX; 109 | ASSERT_CMR_CALL( CMRbalancedTest(cmr, matrix, &isBalanced, NULL, ¶ms, NULL, DBL_MAX) ); 110 | 111 | ASSERT_TRUE( isBalanced ); 112 | 113 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 114 | } 115 | 116 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /test/test_camion.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include 5 | 6 | TEST(Camion, Change) 7 | { 8 | CMR* cmr = NULL; 9 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 10 | 11 | CMR_CHRMAT* matrix = NULL; 12 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "10 10 " 13 | "+1 -1 0 0 0 0 0 0 0 0 " 14 | "-1 +1 0 0 0 0 0 0 0 0 " 15 | "0 0 +1 0 0 0 0 -1 0 0 " 16 | "0 0 0 0 -1 0 +1 0 0 0 " 17 | "0 0 0 0 0 0 -1 -1 -1 0 " 18 | "0 0 0 -1 0 +1 0 0 0 0 " 19 | "0 0 0 0 +1 -1 0 0 0 0 " 20 | "0 0 -1 +1 0 0 0 0 0 0 " 21 | "0 0 0 0 0 0 0 0 +1 -1 " 22 | "0 0 0 0 0 0 0 -1 0 +1 " 23 | ) ); 24 | CMR_CHRMAT* check = NULL; 25 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &check, "10 10 " 26 | "+1 -1 0 0 0 0 0 0 0 0 " 27 | "-1 +1 0 0 0 0 0 0 0 0 " 28 | "0 0 +1 0 0 0 0 -1 0 0 " 29 | "0 0 0 0 -1 0 +1 0 0 0 " 30 | "0 0 0 0 0 0 -1 -1 -1 0 " 31 | "0 0 0 -1 0 +1 0 0 0 0 " 32 | "0 0 0 0 +1 -1 0 0 0 0 " 33 | "0 0 -1 -1 0 0 0 0 0 0 " 34 | "0 0 0 0 0 0 0 0 -1 -1 " 35 | "0 0 0 0 0 0 0 -1 0 +1 " 36 | ) ); 37 | CMR_CHRMAT* checkViolator = NULL; 38 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &checkViolator, "3 3 " 39 | "-1 -1 0 " 40 | "0 1 -1 " 41 | "-1 0 1 " 42 | ) ); 43 | 44 | CMR_SUBMAT* submatrix = NULL; 45 | CMR_CHRMAT* violator = NULL; 46 | 47 | bool alreadySigned; 48 | ASSERT_CMR_CALL( CMRcamionTestSigns(cmr, matrix, &alreadySigned, &submatrix, NULL, DBL_MAX) ); 49 | ASSERT_FALSE(alreadySigned); 50 | ASSERT_TRUE(submatrix != NULL); 51 | ASSERT_CMR_CALL( CMRchrmatSlice(cmr, matrix, submatrix, &violator) ); 52 | ASSERT_TRUE(CMRchrmatCheckEqual(violator, checkViolator)); 53 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &violator) ); 54 | ASSERT_CMR_CALL( CMRsubmatFree(cmr, &submatrix) ); 55 | 56 | ASSERT_CMR_CALL( CMRcamionComputeSigns(cmr, matrix, &alreadySigned, NULL, NULL, DBL_MAX) ); 57 | ASSERT_FALSE(alreadySigned); 58 | ASSERT_TRUE(CMRchrmatCheckEqual(matrix, check)); 59 | 60 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &checkViolator) ); 61 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &check) ); 62 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 63 | 64 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 65 | } 66 | 67 | 68 | TEST(Camion, Issue10) 69 | { 70 | CMR* cmr = NULL; 71 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 72 | 73 | CMR_CHRMAT* matrix = NULL; 74 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "4 4 " 75 | "1 1 0 0 " 76 | "1 0 1 1 " 77 | "0 1 1 0 " 78 | "0 0 1 1 " 79 | ) ); 80 | CMR_CHRMAT* check = NULL; 81 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &check, "4 4 " 82 | "1 1 0 0 " 83 | "1 0 -1 1 " 84 | "0 1 1 0 " 85 | "0 0 1 -1 " 86 | ) ); 87 | 88 | 89 | bool alreadySigned; 90 | ASSERT_CMR_CALL( CMRcamionComputeSigns(cmr, matrix, &alreadySigned, NULL, NULL, DBL_MAX) ); 91 | ASSERT_FALSE(alreadySigned); 92 | ASSERT_TRUE(CMRchrmatCheckEqual(matrix, check)); 93 | 94 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &check) ); 95 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 96 | 97 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 98 | } 99 | -------------------------------------------------------------------------------- /test/test_ctu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include 5 | 6 | TEST(CTU, ExamplesSeymour) 7 | { 8 | CMR* cmr = NULL; 9 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 10 | CMR_CHRMAT* matrix = NULL; 11 | 12 | { 13 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, " 4 5 " 14 | "1 0 0 1 1 " 15 | "1 1 0 0 1 " 16 | "0 1 1 0 1 " 17 | "0 0 1 1 1 " 18 | ) ); 19 | 20 | bool isCTU; 21 | CMR_CTU_PARAMS params; 22 | ASSERT_CMR_CALL( CMRctuParamsInit(¶ms) ); 23 | params.tu.seymour.decomposeStrategy = CMR_SEYMOUR_DECOMPOSE_FLAG_SEYMOUR; 24 | 25 | ASSERT_CMR_CALL( CMRctuTest(cmr, matrix, &isCTU, NULL, NULL, ¶ms, NULL, DBL_MAX) ); 26 | 27 | ASSERT_TRUE(isCTU); 28 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 29 | } 30 | 31 | { 32 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, " 5 5 " 33 | "0 1 0 1 0 " 34 | "1 1 1 0 1 " 35 | "0 1 0 0 1 " 36 | "1 0 0 0 1 " 37 | "0 1 1 1 1 " 38 | ) ); 39 | 40 | bool isCTU; 41 | size_t complementRow; 42 | size_t complementColumn; 43 | CMR_CTU_PARAMS params; 44 | ASSERT_CMR_CALL( CMRctuParamsInit(¶ms) ); 45 | params.tu.seymour.decomposeStrategy = CMR_SEYMOUR_DECOMPOSE_FLAG_SEYMOUR; 46 | 47 | ASSERT_CMR_CALL( CMRctuTest(cmr, matrix, &isCTU, &complementRow, &complementColumn, ¶ms, NULL, DBL_MAX) ); 48 | ASSERT_FALSE(isCTU); 49 | ASSERT_EQ(complementRow, 0UL); 50 | ASSERT_EQ(complementColumn, 0UL); 51 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 52 | } 53 | 54 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 55 | } 56 | -------------------------------------------------------------------------------- /test/test_equimodular.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | TEST(Equimodular, Examples) 10 | { 11 | CMR* cmr = NULL; 12 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 13 | 14 | { 15 | CMR_INTMAT* matrix = NULL; 16 | ASSERT_CMR_CALL( stringToIntMatrix(cmr, &matrix, "5 9 " 17 | " -2 -1 1 5 -3 -2 -7 5 6 " 18 | " 1 0 1 7 1 2 -6 8 8 " 19 | " -3 -1 1 4 -4 -3 -7 4 5 " 20 | " 6 2 0 4 8 8 2 6 4 " 21 | " 4 2 -2 -9 6 4 13 -9 -11 " 22 | ) ); 23 | 24 | 25 | bool isEquimodular; 26 | int64_t k = 0; 27 | CMR_EQUIMODULAR_STATS stats; 28 | CMRequimodularStatsInit(&stats); 29 | ASSERT_CMR_CALL( CMRequimodularTest(cmr, matrix, &isEquimodular, &k, NULL, &stats, DBL_MAX) ); 30 | 31 | ASSERT_TRUE(isEquimodular); 32 | ASSERT_EQ(k, 1); 33 | ASSERT_EQ(stats.totalCount, 1UL); 34 | 35 | ASSERT_CMR_CALL( CMRintmatFree(cmr, &matrix) ); 36 | } 37 | 38 | { 39 | CMR_INTMAT* matrix = NULL; 40 | ASSERT_CMR_CALL( stringToIntMatrix(cmr, &matrix, "5 9 " 41 | " -2 -1 1 5 -3 -2 -7 5 6 " 42 | " 1 0 1 7 1 2 -6 8 8 " 43 | " -3 -1 1 4 -4 -3 -7 4 5 " 44 | " 6 2 0 4 8 8 2 6 4 " 45 | " 4 2 -2 -9 6 4 13 -9 -12 " 46 | ) ); 47 | 48 | bool isEquimodular; 49 | int64_t k = 0; 50 | CMR_EQUIMODULAR_STATS stats; 51 | CMRequimodularStatsInit(&stats); 52 | ASSERT_CMR_CALL( CMRequimodularTest(cmr, matrix, &isEquimodular, &k, NULL, &stats, DBL_MAX) ); 53 | 54 | ASSERT_FALSE(isEquimodular); 55 | ASSERT_EQ(k, 0); 56 | ASSERT_EQ(stats.totalCount, 1UL); 57 | 58 | ASSERT_CMR_CALL( CMRintmatFree(cmr, &matrix) ); 59 | } 60 | 61 | { 62 | CMR_INTMAT* matrix = NULL; 63 | ASSERT_CMR_CALL( stringToIntMatrix(cmr, &matrix, "4 4 " 64 | " 1 1 1 1 " 65 | " 1 1 0 0 " 66 | " 1 0 1 0 " 67 | " 1 0 0 1 " 68 | ) ); 69 | 70 | 71 | bool isEquimodular; 72 | int64_t k = 0; 73 | CMR_EQUIMODULAR_STATS stats; 74 | CMRequimodularStatsInit(&stats); 75 | ASSERT_CMR_CALL( CMRequimodularTest(cmr, matrix, &isEquimodular, &k, NULL, &stats, DBL_MAX) ); 76 | 77 | ASSERT_TRUE(isEquimodular); 78 | ASSERT_EQ(k, 2); 79 | ASSERT_EQ(stats.totalCount, 1); 80 | 81 | ASSERT_CMR_CALL( CMRintmatFree(cmr, &matrix) ); 82 | } 83 | 84 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 85 | } 86 | 87 | #if defined(CMR_WITH_GMP) 88 | 89 | TEST(Equimodular, GMP) 90 | { 91 | CMR* cmr = NULL; 92 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 93 | 94 | { 95 | CMR_INTMAT* matrix = NULL; 96 | ASSERT_CMR_CALL( stringToIntMatrix(cmr, &matrix, "9 9 " 97 | " 1 2 7 3 5 0 2 0 7 " 98 | " 0 0 8 1 0 7 0 4 8 " 99 | " 0 7 6 0 9 0 7 0 9 " 100 | " 2 0 9 0 2 5 8 0 1 " 101 | " 0 1 8 5 3 5 1 6 3 " 102 | " 0 9 0 4 8 1 5 2 0 " 103 | " 7 8 0 2 0 0 9 7 0 " 104 | " 4 0 7 0 0 7 5 0 2 " 105 | " 0 0 0 8 0 5 5 0 6 " 106 | ) ); 107 | 108 | bool isEquimodular; 109 | int64_t k = 0; 110 | CMR_EQUIMODULAR_STATS stats; 111 | CMRequimodularStatsInit(&stats); 112 | CMR_ERROR error = CMRequimodularTest(cmr, matrix, &isEquimodular, &k, NULL, &stats, DBL_MAX); 113 | 114 | ASSERT_EQ(error, CMR_OKAY); 115 | ASSERT_FALSE(isEquimodular); 116 | ASSERT_EQ(k, 0); 117 | ASSERT_EQ(stats.totalCount, 1); 118 | 119 | ASSERT_CMR_CALL( CMRintmatFree(cmr, &matrix) ); 120 | } 121 | 122 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 123 | } 124 | 125 | #else /* CMR_WITH_GMP */ 126 | 127 | TEST(Equimodular, Overflow) 128 | { 129 | CMR* cmr = NULL; 130 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 131 | 132 | { 133 | CMR_INTMAT* matrix = NULL; 134 | ASSERT_CMR_CALL( stringToIntMatrix(cmr, &matrix, "9 9 " 135 | " 1 2 7 3 5 0 2 0 7 " 136 | " 0 0 8 1 0 7 0 4 8 " 137 | " 0 7 6 0 9 0 7 0 9 " 138 | " 2 0 9 0 2 5 8 0 1 " 139 | " 0 1 8 5 3 5 1 6 3 " 140 | " 0 9 0 4 8 1 5 2 0 " 141 | " 7 8 0 2 0 0 9 7 0 " 142 | " 4 0 7 0 0 7 5 0 2 " 143 | " 0 0 0 8 0 5 5 0 6 " 144 | ) ); 145 | 146 | bool isEquimodular; 147 | int64_t k = 0; 148 | CMR_EQUIMODULAR_STATS stats; 149 | CMRequimodularStatsInit(&stats); 150 | CMR_ERROR error = CMRequimodularTest(cmr, matrix, &isEquimodular, &k, NULL, &stats, DBL_MAX); 151 | 152 | ASSERT_EQ(error, CMR_ERROR_OVERFLOW); 153 | ASSERT_EQ(k, 0); 154 | 155 | ASSERT_CMR_CALL( CMRintmatFree(cmr, &matrix) ); 156 | } 157 | 158 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 159 | } 160 | 161 | #endif /* CMR_WITH_GMP */ 162 | 163 | -------------------------------------------------------------------------------- /test/test_graph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include 5 | 6 | TEST(Graph, Modifications) 7 | { 8 | CMR* cmr = NULL; 9 | CMRcreateEnvironment(&cmr); 10 | 11 | CMR_GRAPH* graph = NULL; 12 | CMRgraphCreateEmpty(cmr, &graph, 1, 1); 13 | 14 | CMR_GRAPH_NODE a,b,c,d; 15 | CMRgraphAddNode(cmr, graph, &a); 16 | CMRgraphAddNode(cmr, graph, &b); 17 | CMRgraphAddNode(cmr, graph, &c); 18 | CMRgraphAddNode(cmr, graph, &d); 19 | 20 | CMR_GRAPH_EDGE ab, ac, ad, bc, bd, cd; 21 | CMRgraphAddEdge(cmr, graph, a, b, &ab); 22 | CMRgraphAddEdge(cmr, graph, a, c, &ac); 23 | CMRgraphAddEdge(cmr, graph, a, d, &ad); 24 | CMRgraphAddEdge(cmr, graph, b, c, &bc); 25 | CMRgraphAddEdge(cmr, graph, b, d, &bd); 26 | CMRgraphAddEdge(cmr, graph, c, d, &cd); 27 | 28 | ASSERT_EQ(CMRgraphNumNodes(graph), 4UL); 29 | ASSERT_EQ(CMRgraphNumEdges(graph), 6UL); 30 | 31 | size_t countNodes = 0; 32 | for (CMR_GRAPH_NODE v = CMRgraphNodesFirst(graph); CMRgraphNodesValid(graph, v); 33 | v = CMRgraphNodesNext(graph, v)) 34 | { 35 | ++countNodes; 36 | } 37 | ASSERT_EQ(countNodes, CMRgraphNumNodes(graph)); 38 | 39 | int countIncidentEdges = 0; 40 | for (CMR_GRAPH_ITER i = CMRgraphIncFirst(graph, b); 41 | CMRgraphIncValid(graph, i); i = CMRgraphIncNext(graph, i)) 42 | { 43 | CMR_GRAPH_EDGE e = CMRgraphIncEdge(graph, i); 44 | ASSERT_GE(e, 0); 45 | ASSERT_LT(e, (int) graph->memEdges); 46 | ++countIncidentEdges; 47 | } 48 | ASSERT_EQ(countIncidentEdges, 3); 49 | 50 | CMRgraphDeleteEdge(cmr, graph, bc); 51 | 52 | CMRgraphDeleteNode(cmr, graph, a); 53 | 54 | ASSERT_EQ(CMRgraphNumNodes(graph), 3UL); 55 | ASSERT_EQ(CMRgraphNumEdges(graph), 2UL); 56 | 57 | CMRgraphFree(cmr, &graph); 58 | 59 | CMRfreeEnvironment(&cmr); 60 | } 61 | -------------------------------------------------------------------------------- /test/test_hashtable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common.h" 4 | #include "../src/cmr/hashtable.h" 5 | 6 | TEST(Hashtable, Functionality) 7 | { 8 | CMR* cmr = NULL; 9 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 10 | 11 | CMR_LINEARHASHTABLE_ARRAY* hashtable = NULL; 12 | ASSERT_CMR_CALL( CMRlinearhashtableArrayCreate(cmr, &hashtable, 16, 16) ); 13 | 14 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "q", strlen("q"), (void*) 17) ); 15 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "a", strlen("a"), (void*) 1) ); 16 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "b", strlen("b"), (void*) 2) ); 17 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "r", strlen("r"), (void*) 18) ); 18 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "c", strlen("c"), (void*) 3) ); 19 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "d", strlen("d"), (void*) 4) ); 20 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "e", strlen("e"), (void*) 5) ); 21 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "f", strlen("f"), (void*) 6) ); 22 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "g", strlen("g"), (void*) 7) ); 23 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "h", strlen("h"), (void*) 8) ); 24 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "i", strlen("i"), (void*) 9) ); 25 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "j", strlen("j"), (void*) 10) ); 26 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "k", strlen("k"), (void*) 11) ); 27 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "l", strlen("l"), (void*) 12) ); 28 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "m", strlen("m"), (void*) 13) ); 29 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "n", strlen("n"), (void*) 14) ); 30 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "o", strlen("o"), (void*) 15) ); 31 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "p", strlen("p"), (void*) 16) ); 32 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "s", strlen("s"), (void*) 19) ); 33 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "t", strlen("t"), (void*) 20) ); 34 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "u", strlen("u"), (void*) 21) ); 35 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "v", strlen("v"), (void*) 22) ); 36 | ASSERT_CMR_CALL( CMRlinearhashtableArrayInsert(cmr, hashtable, "w", strlen("w"), (void*) 23) ); 37 | 38 | ASSERT_CMR_CALL( CMRlinearhashtableArrayFree(cmr, &hashtable) ); 39 | 40 | CMRfreeEnvironment(&cmr); 41 | } 42 | -------------------------------------------------------------------------------- /test/test_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) 4 | { 5 | ::testing::InitGoogleTest(&argc, argv); 6 | return RUN_ALL_TESTS(); 7 | } 8 | -------------------------------------------------------------------------------- /test/test_network.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "common.h" 6 | 7 | #include 8 | 9 | void testNetworkMatrix( 10 | CMR* cmr, /**< \ref CMR environment. */ 11 | CMR_CHRMAT* matrix, /**< Matrix to be used for testing. */ 12 | bool knownNetwork /**< Whether \p matrix is known to be network. */ 13 | ) 14 | { 15 | bool isNetwork; 16 | CMR_GRAPH* graph = NULL; 17 | CMR_GRAPH_EDGE* basis = NULL; 18 | CMR_GRAPH_EDGE* cobasis = NULL; 19 | bool* edgesReversed = NULL; 20 | CMR_SUBMAT* violatorSubmatrix = NULL; 21 | 22 | ASSERT_CMR_CALL( CMRnetworkTestMatrix(cmr, matrix, &isNetwork, NULL, &graph, &basis, &cobasis, &edgesReversed, 23 | &violatorSubmatrix, NULL, DBL_MAX) ); 24 | 25 | ASSERT_EQ( isNetwork, knownNetwork ); 26 | if (isNetwork) 27 | { 28 | ASSERT_TRUE( basis ); 29 | ASSERT_TRUE( cobasis ); 30 | 31 | CMR_CHRMAT* result = NULL; 32 | bool isCorrectBasis; 33 | ASSERT_CMR_CALL( CMRnetworkComputeMatrix(cmr, graph, &result, NULL, edgesReversed, matrix->numRows, 34 | basis, matrix->numColumns, cobasis, &isCorrectBasis) ); 35 | ASSERT_TRUE( isCorrectBasis ); 36 | ASSERT_TRUE( result ); 37 | 38 | if (!CMRchrmatCheckEqual(matrix, result)) 39 | { 40 | printf("Input matrix:\n"); 41 | ASSERT_CMR_CALL( CMRchrmatPrintDense(cmr, matrix, stdout, '0', true) ); 42 | 43 | printf("Graph:\n"); 44 | ASSERT_CMR_CALL( CMRgraphPrint(graph, stdout) ); 45 | 46 | printf("Representation matrix:\n"); 47 | ASSERT_CMR_CALL( CMRchrmatPrintDense(cmr, result, stdout, '0', true) ); 48 | 49 | printf("Basis:"); 50 | for (size_t r = 0; r < matrix->numRows; ++r) 51 | printf(" %d", basis[r]); 52 | printf("\n"); 53 | 54 | printf("Cobasis:"); 55 | for (size_t c = 0; c < matrix->numColumns; ++c) 56 | printf(" %d", cobasis[c]); 57 | printf("\n"); 58 | } 59 | 60 | ASSERT_TRUE( CMRchrmatCheckEqual(matrix, result) ); 61 | 62 | ASSERT_CMR_CALL( CMRgraphFree(cmr, &graph) ); 63 | ASSERT_CMR_CALL( CMRfreeBlockArray(cmr, &basis) ); 64 | ASSERT_CMR_CALL( CMRfreeBlockArray(cmr, &cobasis) ); 65 | ASSERT_CMR_CALL( CMRfreeBlockArray(cmr, &edgesReversed) ); 66 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &result) ); 67 | ASSERT_EQ( violatorSubmatrix, (CMR_SUBMAT*) NULL ); 68 | } 69 | else 70 | { 71 | ASSERT_EQ( basis, (CMR_GRAPH_EDGE*) NULL ); 72 | ASSERT_EQ( cobasis, (CMR_GRAPH_EDGE*) NULL ); 73 | ASSERT_EQ( edgesReversed, (bool*) NULL ); 74 | 75 | ASSERT_CMR_CALL( CMRsubmatPrint(cmr, violatorSubmatrix, matrix->numRows, matrix->numColumns, stdout) ); 76 | 77 | ASSERT_CMR_CALL( CMRsubmatFree(cmr, &violatorSubmatrix) ); 78 | } 79 | } 80 | 81 | TEST(Network, Basic) 82 | { 83 | CMR* cmr = NULL; 84 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 85 | CMR_CHRMAT* matrix = NULL; 86 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "6 7 " 87 | "-1 0 0 0 1 -1 0 " 88 | " 1 0 0 1 -1 1 0 " 89 | " 0 -1 0 -1 1 -1 0 " 90 | " 0 1 0 0 0 0 1 " 91 | " 0 0 1 -1 1 0 1 " 92 | " 0 0 -1 1 -1 0 0 " 93 | ) ); 94 | 95 | testNetworkMatrix(cmr, matrix, true); 96 | 97 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 98 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 99 | } 100 | 101 | TEST(Network, NonCamion) 102 | { 103 | CMR* cmr = NULL; 104 | ASSERT_CMR_CALL( CMRcreateEnvironment(&cmr) ); 105 | CMR_CHRMAT* matrix = NULL; 106 | ASSERT_CMR_CALL( stringToCharMatrix(cmr, &matrix, "6 7 " 107 | "-1 0 0 0 1 -1 0 " 108 | " 1 0 0 1 -1 1 0 " 109 | " 0 -1 0 -1 1 -1 0 " 110 | " 0 1 0 0 0 0 -1 " 111 | " 0 0 1 -1 1 0 1 " 112 | " 0 0 -1 1 -1 0 0 " 113 | ) ); 114 | 115 | testNetworkMatrix(cmr, matrix, false); 116 | 117 | ASSERT_CMR_CALL( CMRchrmatFree(cmr, &matrix) ); 118 | ASSERT_CMR_CALL( CMRfreeEnvironment(&cmr) ); 119 | } 120 | --------------------------------------------------------------------------------