├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── AUTHORS ├── CMakeLists.txt ├── Doxyfile ├── Doxyfile.in ├── INSTALL.md ├── LICENSE.md ├── README.md ├── Rhea.podspec ├── _clang-format ├── debian ├── changelog ├── compat ├── control ├── copyright ├── librhea1-dev.install ├── librhea1.install ├── rules └── source │ └── format ├── doc ├── .gitignore ├── doxygen │ └── mainpage.md ├── make.bat ├── requirements.txt └── source │ ├── conf.py │ ├── examples.rst │ ├── index.rst │ ├── ref │ ├── class_abstract_variable.rst │ ├── class_constraint.rst │ ├── class_dummy_variable.rst │ ├── class_linear_equation.rst │ ├── class_linear_expression.rst │ ├── class_simplex_solver.rst │ └── class_variable.rst │ └── theory.rst ├── rhea ├── CMakeLists.txt ├── abstract_constraint.hpp ├── abstract_variable.cpp ├── abstract_variable.hpp ├── action_variable.hpp ├── approx.hpp ├── constraint.hpp ├── dummy_variable.hpp ├── edit_constraint.hpp ├── edit_or_stay_constraint.hpp ├── errors.hpp ├── errors_expl.hpp ├── flat_map.hpp ├── float_variable.hpp ├── iostream.hpp ├── linear_constraint.hpp ├── linear_equation.hpp ├── linear_expression.cpp ├── linear_expression.hpp ├── linear_inequality.hpp ├── link_variable.hpp ├── objective_variable.hpp ├── relation.hpp ├── simplex_solver.cpp ├── simplex_solver.hpp ├── slack_variable.hpp ├── solver.hpp ├── stay_constraint.hpp ├── strength.hpp ├── symbolic_weight.cpp ├── symbolic_weight.hpp ├── tableau.cpp ├── tableau.hpp ├── variable.hpp └── version.hpp.in └── unit_tests ├── CMakeLists.txt ├── speed_test.cpp └── unit_tests.cpp /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | 15 | # Backup files 16 | *~ 17 | *.bak 18 | *.log 19 | .*.sw[a-z] 20 | \#*\# 21 | .\#* 22 | *.tmp 23 | 24 | # CMake cruft 25 | /build/ 26 | CMakeFiles 27 | Makefile 28 | *.cmake 29 | CMakeCache.txt 30 | CMakeLists.txt.user 31 | CMakeLists.txt.user.* 32 | tags 33 | *.json 34 | .excludes 35 | krb5doxy.tag 36 | 37 | # Generated headers 38 | rhea/version.hpp 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | before_install: 6 | - sudo apt-get update -qq 7 | - sudo apt-get install -qq libboost-test-dev lcov libgemplugin-ruby python-yaml 8 | - gem install coveralls-lcov 9 | script: 10 | - mkdir build 11 | - cd build 12 | - cmake -DBUILD_UNITTESTS=ON -DBUILD_COVERAGE=ON .. 13 | - make 14 | - unit_tests/unit_tests 15 | after_success: 16 | - lcov --directory unit_tests --base-directory=.. --capture --output-file=coverage.info 17 | - lcov --remove coverage.info '/usr*' -o coverage.info 18 | - cd .. 19 | - coveralls-lcov build/coverage.info 20 | 21 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | 2 | Developers: 3 | Nocte 4 | 5 | Documentation: 6 | Russell Keith-Magee 7 | 8 | Contributors: 9 | Håvard Fossli 10 | Juan Pedro Bolívar Puuente 11 | 12 | 13 | This library is based on the Cassowary Constraint Solving Toolkit, 14 | implemented by: 15 | 16 | Greg J. Badros and 17 | Alan Borning 18 | University of Washington 19 | Computer Science and Engineering 20 | Seattle, WA 98195-2350 21 | 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(rhea) 2 | cmake_minimum_required(VERSION 2.8.3) 3 | 4 | set(SOVERSION "1") 5 | set(VERSION_MAJOR "0") 6 | set(VERSION_MINOR "2") 7 | set(VERSION_PATCH "4") 8 | set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") 9 | 10 | set(CPACK_PACKAGE_NAME "librhea${SOVERSION}") 11 | 12 | if(NOT CMAKE_BUILD_TYPE) 13 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build (Debug or Release)" FORCE) 14 | endif() 15 | 16 | set(BUILD_UNITTESTS 0 CACHE BOOL "Build the unit tests") 17 | set(BUILD_COVERAGE 0 CACHE BOOL "Generate a coverage report (gcc only)") 18 | set(BUILD_DOCUMENTATION 0 CACHE BOOL "Generate Doxygen documentation") 19 | 20 | # Prevent problems with RPATH on mac 21 | # 22 | if(POLICY CMP0042) 23 | cmake_policy(SET CMP0042 NEW) 24 | endif() 25 | set(CMAKE_MACOSX_RPATH ON) 26 | 27 | # Set up the compiler 28 | # 29 | if(MSVC) 30 | add_definitions(/D_WIN32_WINNT=0x0501 /D_CRT_SECURE_NO_WARNINGS) 31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /EHsc /wd4244 /wd4996 ") 32 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /DNDEBUG /MP /GS- /Ox /Ob2 /Oi /Oy /arch:SSE /fp:fast /Zi") 33 | set(CMAKE_LIB_LINKER_FLAGS "${CMAKE_LIB_LINKER_FLAGS} /OPT:REF /SUBSYSTEM:WINDOWS") 34 | 35 | else() # Most likely gcc or clang 36 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 38 | elseif ("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "4.7") 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 40 | else() 41 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 42 | endif() 43 | 44 | if (BUILD_COVERAGE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 45 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") 46 | endif() 47 | 48 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wno-strict-aliasing") 49 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") 50 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 51 | endif() 52 | 53 | add_subdirectory(rhea) 54 | 55 | if(BUILD_UNITTESTS) 56 | add_subdirectory(unit_tests) 57 | endif() 58 | 59 | # Doxygen documentation 60 | # 61 | if(BUILD_DOCUMENTATION) 62 | find_package(Doxygen) 63 | if (DOXYGEN_FOUND STREQUAL "YES") 64 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 65 | add_custom_target(doxygen ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 66 | set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES api-doc) 67 | get_target_property(DOC_TARGET doc TYPE) 68 | if (NOT DOC_TARGET) 69 | add_custom_target(doc) 70 | endif() 71 | add_dependencies(doc doxygen) 72 | endif() 73 | endif() 74 | 75 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Incremental constraint solver C++ library") 76 | set(CPACK_PACKAGE_DESCRIPTION "Rhea is a constraint solver that is especially useful for creating user interfaces. It is based on Cassowary.") 77 | set(CPACK_PACKAGE_VENDOR "Nocte") 78 | set(CPACK_PACKAGE_CONTACT "Nocte ") 79 | set(CPACK_SOURCE_IGNORE_FILES "CMakefiles;Makefile;CMakeCache.txt;_CPack_Packages;/.git/;.gitignore;") 80 | set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${VERSION}") 81 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md") 82 | set(CPACK_SOURCE_GENERATOR "TGZ") 83 | set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION}") 84 | 85 | if(UNIX) 86 | set(CPACK_GENERATOR "DEB") 87 | add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) 88 | 89 | set(CPACK_DEBIAN_PACKAGE_SECTION "devel") 90 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.0), libstdc++6 (>= 4.0)") 91 | add_custom_target(deb dpkg-buildpackage) 92 | add_dependencies(deb dist) 93 | 94 | elseif(WIN32) 95 | set(CPACK_GENERATOR "ZIP") 96 | endif() 97 | 98 | include(CPack) 99 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Rhea is built using [CMake](http://www.cmake.org/). To keep the source tree 5 | clean, it is a good idea to build it in a separate directory: 6 | 7 | * mkdir build; cd build 8 | * cmake .. 9 | * make 10 | * sudo make install 11 | 12 | This will install it to /usr/local. Changing this is easiest using `ccmake`, 13 | or one of the graphical user interfaces for CMake. To build project files 14 | for IDEs, please refer to the CMake documentation. 15 | 16 | 17 | Running the unit tests 18 | ---------------------- 19 | 20 | To run the unit and performance tests, you need to have the 21 | [Boost Unit Test framework](http://boost.org/) installed. 22 | 23 | * cmake .. -DBUILD_UNITTESTS=1 24 | * make 25 | * cd unit_tests 26 | * ./unit_tests 27 | * ./speed_test 28 | 29 | 30 | Generating documentation 31 | ------------------------ 32 | 33 | Documentation can be autogenerated using [Doxygen](http://www.doxygen.org/). 34 | 35 | * cmake .. -DBUILD_DOCUMENTATION=1 36 | * make doc 37 | 38 | The documentation ends up in the doc directory. 39 | 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 nocte@hippie.nu 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rhea 2 | ==== 3 | [![Linux build status](https://travis-ci.org/Nocte-/rhea.png?branch=master)](https://travis-ci.org/Nocte-/rhea) [![Windows build status](https://ci.appveyor.com/api/projects/status/vnt20tikld91en78?svg=true)](https://ci.appveyor.com/project/Nocte-/rhea) [![Coverage Status](https://coveralls.io/repos/Nocte-/rhea/badge.png)](https://coveralls.io/r/Nocte-/rhea) [![Readthedocs build](https://readthedocs.org/projects/rhea/badge/?version=latest)](https://readthedocs.org/projects/rhea/) 4 | [![Slack Status](https://overconstrained-slack.herokuapp.com/badge.svg)](https://overconstrained-slack.herokuapp.com) 5 | 6 | About 7 | ----- 8 | Rhea is an incremental constraint solver based on 9 | [Cassowary](http://www.cs.washington.edu/research/constraints/cassowary), 10 | originally developed by Greg J. Badros and Alan Borning. The main 11 | differences are: 12 | 13 | * Allows the programmer to write constraints in a natural way 14 | * Rewritten in C++11 15 | * CMake instead of GNU Autoconfig 16 | * Unit tests use the Boost Test Framework 17 | * Uses Doxygen for documentation 18 | * Expression parser based on Boost Spirit 19 | * Does not have a finite domain subsolver 20 | 21 | 22 | Quick example 23 | ------------- 24 | 25 | ```c++ 26 | #include 27 | #include 28 | 29 | main() 30 | { 31 | rhea::variable left, mid, right; 32 | rhea::simplex_solver solver; 33 | 34 | solver.add_constraints( 35 | { 36 | mid == (left + right) / 2, 37 | right == left + 10, 38 | right <= 100, 39 | left >= 0 40 | }); 41 | solver.suggest(mid, 2); 42 | 43 | std::cout << left << " " << mid << " " << right << std::endl; 44 | // Prints "0 5 10" 45 | } 46 | ``` 47 | 48 | This is the line example from the original paper. The constraints make sure 49 | the line is at least 10 wide, fits inside the 0..100 range, and that the mid 50 | point is halfway the left and the right. 51 | 52 | Note that even though we suggest the mid point to be at 2, the solver decides 53 | to move it over to 5 so all the constraints can be met. 54 | 55 | 56 | Status 57 | ------ 58 | This software is beta. It does pass all unit tests, it is in active use 59 | by several applications, but the interface is not stable yet. 60 | 61 | License 62 | ------- 63 | Rhea is free software: you can redistribute it and/or modify it under the 64 | terms of the [MIT/Expat license](https://opensource.org/licenses/MIT). 65 | 66 | Links 67 | ----- 68 | Curious about similar projects? Head over to [overconstrained.io](http://overconstrained.io). 69 | -------------------------------------------------------------------------------- /Rhea.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Rhea" 3 | s.version = '0.2.4' 4 | s.summary = "A modern c++ constraint solver based on Cassowary." 5 | s.homepage = "https://github.com/Nocte-/rhea" 6 | s.authors = { 7 | "Nocte" => "nocte@hippie.nu" 8 | } 9 | s.license = 'MIT' 10 | s.source = { 11 | :git => "https://github.com/Nocte-/rhea.git", 12 | :tag => s.version.to_s 13 | } 14 | s.default_subspec = 'Default' 15 | s.library = 'c++' 16 | s.xcconfig = { 17 | 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11', 18 | 'CLANG_CXX_LIBRARY' => 'libc++' 19 | } 20 | s.platform = :ios, '6.0' 21 | s.requires_arc = true 22 | 23 | s.subspec 'Default' do |ss| 24 | ss.dependency 'Rhea/Core' 25 | end 26 | 27 | s.subspec 'Core' do |ss| 28 | ss.source_files = 'rhea/**/*.{h,hpp,cpp,c,m}' 29 | end 30 | end 31 | 32 | -------------------------------------------------------------------------------- /_clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AccessModifierOffset: -4 3 | AllowShortFunctionsOnASingleLine: Inline 4 | AlwaysBreakTemplateDeclarations: true 5 | BreakBeforeBraces: Linux 6 | BreakBeforeBinaryOperators: true 7 | BreakConstructorInitializersBeforeComma: true 8 | ColumnLimit: 79 9 | Cpp11BracedListStyle: true 10 | IndentWidth: 4 11 | PointerBindsToType: true 12 | Standard: Cpp11 13 | 14 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | rhea (0.2.4) raring; urgency=medium 2 | 3 | * More flexibility in adding/removing edit constraints 4 | 5 | -- Nocte Thu, 12 Feb 2015 19:16:13 +0100 6 | 7 | rhea (0.2.3) raring; urgency=medium 8 | 9 | * Bugfix: throw when denominator is not constant (#26) 10 | 11 | -- Nocte Thu, 27 Nov 2014 09:20:54 +0100 12 | 13 | rhea (0.2.2) raring; urgency=medium 14 | 15 | * Renamed COPYING to LICENSE.md 16 | 17 | -- Nocte Wed, 05 Nov 2014 22:24:57 +0100 18 | 19 | rhea (0.2.1) raring; urgency=medium 20 | 21 | * Changed the license to MIT/Expat. 22 | 23 | -- Nocte Wed, 05 Nov 2014 22:03:58 +0100 24 | 25 | rhea (0.2.0) raring; urgency=medium 26 | 27 | * Add linked and action variables. 28 | 29 | -- Nocte Mon, 20 Oct 2014 21:46:28 +0200 30 | 31 | rhea (0.1.6) raring; urgency=low 32 | 33 | * Fix a bug with required strength stays and edits (#18). 34 | 35 | -- Nocte Mon, 20 Oct 2014 19:28:18 +0200 36 | 37 | rhea (0.1.4) raring; urgency=low 38 | 39 | * Fix for Launchpad. 40 | 41 | -- Nocte Mon, 02 Jun 2014 22:40:56 +0200 42 | 43 | rhea (0.1.3) raring; urgency=low 44 | 45 | * Formatting with clang-format. 46 | 47 | -- Nocte Mon, 02 Jun 2014 21:08:29 +0200 48 | 49 | rhea (0.1.2) raring; urgency=low 50 | 51 | * Small fixes for Debian packaging. 52 | 53 | -- Nocte Sat, 08 Jun 2013 12:30:37 +0200 54 | 55 | rhea (0.1.1) raring; urgency=low 56 | 57 | * Initial release. 58 | 59 | -- Nocte Tue, 04 Jun 2013 13:58:45 +0200 60 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: rhea 2 | Maintainer: Nocte 3 | Section: libs 4 | Priority: optional 5 | Build-Depends: 6 | debhelper (>= 9), 7 | cmake (>= 2.8), 8 | cdbs, 9 | libboost-dev (>= 1.47) 10 | Standards-Version: 3.9.4 11 | Homepage: http://github.com/Nocte-/rhea 12 | 13 | Package: librhea1-dev 14 | Section: libdevel 15 | Architecture: any 16 | Depends: 17 | librhea1 (= ${binary:Version}), 18 | ${misc:Depends} 19 | Description: Linear constraint solver library 20 | Simplex linear constraint solver in C++11, based on the Cassowary library. 21 | 22 | Package: librhea1 23 | Section: libs 24 | Architecture: any 25 | Depends: 26 | ${shlibs:Depends}, 27 | ${misc:Depends} 28 | Description: Linear constraint solver library 29 | Simplex linear constraint solver in C++11, based on the Cassowary library. 30 | 31 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: rhea 3 | Source: http://github.com/Nocte-/rhea/ 4 | 5 | Files: * 6 | Copyright: Copyright 2015 Nocte 7 | License: Expat 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | . 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | . 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | . 27 | On Debian systems, the full text of the Expat License can be found in 28 | the file `/usr/share/common-licenses/Expat'. 29 | 30 | -------------------------------------------------------------------------------- /debian/librhea1-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/rhea 2 | usr/lib/librhea-s.a 3 | usr/lib/librhea.so 4 | -------------------------------------------------------------------------------- /debian/librhea1.install: -------------------------------------------------------------------------------- 1 | usr/lib/librhea.so.* 2 | 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | include /usr/share/cdbs/1/rules/debhelper.mk 4 | include /usr/share/cdbs/1/class/cmake.mk 5 | 6 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | 3 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | doxyxml/ 3 | html/ 4 | latex/ 5 | doxygen_sqlite3.db 6 | 7 | -------------------------------------------------------------------------------- /doc/doxygen/mainpage.md: -------------------------------------------------------------------------------- 1 | Rhea documentation {#mainpage} 2 | ================== 3 | 4 | Rhea is an incremental constraint solver based on 5 | [Cassowary](http://www.cs.washington.edu/research/constraints/cassowary), 6 | originally developed by Greg J. Badros and Alan Borning. 7 | 8 | Example 9 | ------- 10 | 11 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} 12 | #include 13 | #include 14 | 15 | main() 16 | { 17 | rhea::variable x (0), y (0); 18 | rhea::simplex_solver solver; 19 | 20 | solver.add_constraints( 21 | { 22 | x <= y, 23 | y == x + 3, 24 | x == 10 25 | }); 26 | 27 | std::cout << x << " " << y << std::endl; 28 | // Prints "10 13" 29 | } 30 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31 | 32 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Rhea.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Rhea.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | breathe 2 | 3 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Rhea documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Dec 2 22:18:37 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import subprocess 18 | 19 | subprocess.call('cd ../..;doxygen;', shell=True) 20 | 21 | sys.path.append("/home/unicorn/devel/breathe") 22 | 23 | # If extensions (or modules to document with autodoc) are in another directory, 24 | # add these directories to sys.path here. If the directory is relative to the 25 | # documentation root, use os.path.abspath to make it absolute, like shown here. 26 | #sys.path.insert(0, os.path.abspath('.')) 27 | 28 | # -- General configuration ------------------------------------------------ 29 | 30 | # If your documentation needs a minimal Sphinx version, state it here. 31 | #needs_sphinx = '1.0' 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be 34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 35 | # ones. 36 | extensions = [ 37 | 'sphinx.ext.mathjax', 38 | 'sphinx.ext.todo', 39 | 'sphinx.ext.viewcode', 40 | 'breathe', 41 | ] 42 | 43 | breathe_projects = { "Rhea": "../doxyxml/" } 44 | breathe_default_project = "Rhea" 45 | 46 | # Add any paths that contain templates here, relative to this directory. 47 | templates_path = ['_templates'] 48 | 49 | # The suffix of source filenames. 50 | source_suffix = '.rst' 51 | 52 | # The encoding of source files. 53 | #source_encoding = 'utf-8-sig' 54 | 55 | # The master toctree document. 56 | master_doc = 'index' 57 | 58 | # General information about the project. 59 | project = u'Rhea' 60 | copyright = u'2014, Nocte' 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = '0.2' 68 | # The full version, including alpha/beta/rc tags. 69 | release = '0.2' 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | #language = None 74 | 75 | # There are two options for replacing |today|: either, you set today to some 76 | # non-false value, then it is used: 77 | #today = '' 78 | # Else, today_fmt is used as the format for a strftime call. 79 | #today_fmt = '%B %d, %Y' 80 | 81 | # List of patterns, relative to source directory, that match files and 82 | # directories to ignore when looking for source files. 83 | exclude_patterns = [] 84 | 85 | # The reST default role (used for this markup: `text`) to use for all 86 | # documents. 87 | #default_role = None 88 | 89 | # If true, '()' will be appended to :func: etc. cross-reference text. 90 | #add_function_parentheses = True 91 | 92 | # If true, the current module name will be prepended to all description 93 | # unit titles (such as .. function::). 94 | #add_module_names = True 95 | 96 | # If true, sectionauthor and moduleauthor directives will be shown in the 97 | # output. They are ignored by default. 98 | #show_authors = False 99 | 100 | # The name of the Pygments (syntax highlighting) style to use. 101 | pygments_style = 'sphinx' 102 | highlight_language = 'cpp' 103 | 104 | # A list of ignored prefixes for module index sorting. 105 | #modindex_common_prefix = [] 106 | 107 | # If true, keep warnings as "system message" paragraphs in the built documents. 108 | #keep_warnings = False 109 | 110 | 111 | # -- Options for HTML output ---------------------------------------------- 112 | 113 | # The theme to use for HTML and HTML Help pages. See the documentation for 114 | # a list of builtin themes. 115 | html_theme = 'default' 116 | 117 | # Theme options are theme-specific and customize the look and feel of a theme 118 | # further. For a list of options available for each theme, see the 119 | # documentation. 120 | #html_theme_options = {} 121 | 122 | # Add any paths that contain custom themes here, relative to this directory. 123 | #html_theme_path = [] 124 | 125 | # The name for this set of Sphinx documents. If None, it defaults to 126 | # " v documentation". 127 | #html_title = None 128 | 129 | # A shorter title for the navigation bar. Default is the same as html_title. 130 | #html_short_title = None 131 | 132 | # The name of an image file (relative to this directory) to place at the top 133 | # of the sidebar. 134 | #html_logo = None 135 | 136 | # The name of an image file (within the static path) to use as favicon of the 137 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 138 | # pixels large. 139 | #html_favicon = None 140 | 141 | # Add any paths that contain custom static files (such as style sheets) here, 142 | # relative to this directory. They are copied after the builtin static files, 143 | # so a file named "default.css" will overwrite the builtin "default.css". 144 | html_static_path = ['_static'] 145 | 146 | # Add any extra paths that contain custom files (such as robots.txt or 147 | # .htaccess) here, relative to this directory. These files are copied 148 | # directly to the root of the documentation. 149 | #html_extra_path = [] 150 | 151 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 152 | # using the given strftime format. 153 | #html_last_updated_fmt = '%b %d, %Y' 154 | 155 | # If true, SmartyPants will be used to convert quotes and dashes to 156 | # typographically correct entities. 157 | #html_use_smartypants = True 158 | 159 | # Custom sidebar templates, maps document names to template names. 160 | #html_sidebars = {} 161 | 162 | # Additional templates that should be rendered to pages, maps page names to 163 | # template names. 164 | #html_additional_pages = {} 165 | 166 | # If false, no module index is generated. 167 | #html_domain_indices = True 168 | 169 | # If false, no index is generated. 170 | #html_use_index = True 171 | 172 | # If true, the index is split into individual pages for each letter. 173 | #html_split_index = False 174 | 175 | # If true, links to the reST sources are added to the pages. 176 | #html_show_sourcelink = True 177 | 178 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 179 | #html_show_sphinx = True 180 | 181 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 182 | #html_show_copyright = True 183 | 184 | # If true, an OpenSearch description file will be output, and all pages will 185 | # contain a tag referring to it. The value of this option must be the 186 | # base URL from which the finished HTML is served. 187 | #html_use_opensearch = '' 188 | 189 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 190 | #html_file_suffix = None 191 | 192 | # Output file base name for HTML help builder. 193 | htmlhelp_basename = 'Rheadoc' 194 | 195 | 196 | # -- Options for LaTeX output --------------------------------------------- 197 | 198 | latex_elements = { 199 | # The paper size ('letterpaper' or 'a4paper'). 200 | 'papersize': 'a4paper', 201 | 202 | # The font size ('10pt', '11pt' or '12pt'). 203 | #'pointsize': '10pt', 204 | 205 | # Additional stuff for the LaTeX preamble. 206 | #'preamble': '', 207 | } 208 | 209 | # Grouping the document tree into LaTeX files. List of tuples 210 | # (source start file, target name, title, 211 | # author, documentclass [howto, manual, or own class]). 212 | latex_documents = [ 213 | ('index', 'Rhea.tex', u'Rhea Documentation', 214 | u'Nocte', 'manual'), 215 | ] 216 | 217 | # The name of an image file (relative to this directory) to place at the top of 218 | # the title page. 219 | #latex_logo = None 220 | 221 | # For "manual" documents, if this is true, then toplevel headings are parts, 222 | # not chapters. 223 | #latex_use_parts = False 224 | 225 | # If true, show page references after internal links. 226 | #latex_show_pagerefs = False 227 | 228 | # If true, show URL addresses after external links. 229 | #latex_show_urls = False 230 | 231 | # Documents to append as an appendix to all manuals. 232 | #latex_appendices = [] 233 | 234 | # If false, no module index is generated. 235 | #latex_domain_indices = True 236 | 237 | 238 | # -- Options for manual page output --------------------------------------- 239 | 240 | # One entry per manual page. List of tuples 241 | # (source start file, name, description, authors, manual section). 242 | man_pages = [ 243 | ('index', 'rhea', u'Rhea Documentation', 244 | [u'Nocte'], 1) 245 | ] 246 | 247 | # If true, show URL addresses after external links. 248 | #man_show_urls = False 249 | 250 | 251 | # -- Options for Texinfo output ------------------------------------------- 252 | 253 | # Grouping the document tree into Texinfo files. List of tuples 254 | # (source start file, target name, title, author, 255 | # dir menu entry, description, category) 256 | texinfo_documents = [ 257 | ('index', 'Rhea', u'Rhea Documentation', 258 | u'Nocte', 'Rhea', 'A C++11 implementation of the Cassowary algorithm.', 259 | 'Miscellaneous'), 260 | ] 261 | 262 | # Documents to append as an appendix to all manuals. 263 | #texinfo_appendices = [] 264 | 265 | # If false, no module index is generated. 266 | #texinfo_domain_indices = True 267 | 268 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 269 | #texinfo_show_urls = 'footnote' 270 | 271 | # If true, do not generate a @detailmenu in the "Top" node's menu. 272 | #texinfo_no_detailmenu = False 273 | -------------------------------------------------------------------------------- /doc/source/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | The following examples demonstrate the use of Rhea in practical 5 | constraint-solving problems. 6 | 7 | Quadrilaterals 8 | -------------- 9 | 10 | The "Bounded Quadrilateral" demo is the `online example`_ provided for 11 | Cassowary. The online example is implemented in JavaScript, but the 12 | implementation doesn't alter the way the Cassowary algoritm is used. 13 | 14 | .. _online example: http://www.badros.com/greg/cassowary/js/quaddemo.html 15 | 16 | The Bounded Quadrilateral problem starts with a bounded, two-dimensional 17 | canvas. We want to draw a quadilateral on this plane, subject to a number of 18 | constraints. 19 | 20 | Firstly, we set up the solver system itself:: 21 | 22 | #include 23 | 24 | rhea::simplex_solver solver; 25 | 26 | Then, we set up a convenience class for holding information about points 27 | on a 2D plane:: 28 | 29 | struct point 30 | { 31 | rhea::variable x, y; 32 | }; 33 | 34 | 35 | Now we can set up a set of points to describe the initial location of our 36 | quadrilateral - a 190×190 square:: 37 | 38 | auto p = std::vector{{10, 10}, {10, 200}, {200, 200}, {200, 10}}; 39 | auto m = std::vector(4); 40 | 41 | Note that even though we're drawing a quadrilateral, we have 8 points. We're 42 | tracking the position of the midpoints independent of the corners of our 43 | quadrilateral. However, we don't need to define the position of the midpoints. 44 | The position of the midpoints will be set by defining constraints. 45 | 46 | Next, we set up some stays. A stay is a constraint that says that a particular 47 | variable shouldn't be modified unless it needs to be - that it should "stay" 48 | as is unless there is a reason not to. In this case, we're going to set a stay 49 | for each of the four corners - that is, don't move the corners unless you have 50 | to:: 51 | 52 | for (auto& i : p) 53 | solver.add_stays({i.x, i.y}); 54 | 55 | Now we can set up the constraints to define where the midpoints fall. By 56 | definition, each midpoint **must** fall exactly halfway between two points 57 | that form a line, and that's exactly what we describe - an expression that 58 | computes the position of the midpoint. This expression is used to construct a 59 | :class:`constraint`, describing that the value of the midpoint must 60 | equal the value of the expression. The :class:`constraint` is then 61 | added to the solver system:: 62 | 63 | for (auto i = 0; i < 4; ++i) 64 | solver.add_constraints({ 65 | m[i].x == p[i].x / 2 + p[i + 1 % 4].x / 2, 66 | m[i].y == p[i].y / 2 + p[i + 1 % 4].y / 2 67 | }); 68 | 69 | Next, lets add some constraints to ensure that the left side of the quadrilateral 70 | stays on the left, and the top stays on top:: 71 | 72 | solver.add_constraints({ 73 | p[0].x + 20 <= p[2].x, 74 | p[0].x + 20 <= p[3].x, 75 | 76 | p[1].x + 20 <= p[2].x, 77 | p[1].x + 20 <= p[3].x, 78 | 79 | p[0].y + 20 <= p[1].y, 80 | p[0].y + 20 <= p[1].y, 81 | 82 | p[3].y + 20 <= p[1].y, 83 | p[3].y + 20 <= p[1].y 84 | }); 85 | 86 | Each of these constraints is posed as an :class:`~cassowary.Constraint`. For 87 | example, the first expression describes a point 20 pixels to the right of the 88 | x coordinate of the top left point. This :class:`~cassowary.Constraint` is 89 | then added as a constraint on the x coordinate of the bottom right (point 2) 90 | and top right (point 3) corners - the x coordinate of these points must be at 91 | least 20 pixels greater than the x coordinate of the top left corner (point 92 | 0). 93 | 94 | Lastly, we set the overall constraints -- the constraints that limit how large 95 | our 2D canvas is. We'll constraint the canvas to be 500x500 pixels, and 96 | require that all points fall on that canvas:: 97 | 98 | for (auto& i : p) 99 | solver.add_constraints({ 100 | i.x >= 0, 101 | i.y >= 0, 102 | i.x <= 500, 103 | i.y <= 500 104 | }); 105 | 106 | This gives us a fully formed constraint system. Now we can use it to answer 107 | layout questions. The most obvious question -- where are the midpoints? 108 | 109 | :: 110 | 111 | #include 112 | 113 | namespace std 114 | { 115 | ostream& operator<< (ostream& in, const point& p) 116 | { 117 | return in << "(" << p.x << ", " << p.y << ")"; 118 | } 119 | } 120 | 121 | // ... 122 | 123 | for (auto& i : m) 124 | std::cout << i << std::endl; 125 | 126 | Output:: 127 | 128 | (10.0, 105.0) 129 | (105.0, 200.0) 130 | (200.0, 105.0) 131 | (105.0, 10.0) 132 | 133 | You can see from this that the midpoints have been positioned exactly where 134 | you'd expect - half way between the corners - without having to explicitly 135 | specify their positions. 136 | 137 | These relationships will be maintained if we then edit the position of the 138 | corners. Lets move the position of the bottom right corner (point 2). We mark 139 | the variables associated with that corner as being **Edit variables**:: 140 | 141 | solver.suggest({{p[2].x, 300}, {p[2].y, 400}}); 142 | 143 | As a result of this edit, the midpoints have automatically been updated:: 144 | 145 | for (auto& i : m) 146 | std::cout << i << std::endl; 147 | 148 | Output:: 149 | 150 | (10.0, 105.0) 151 | (155.0, 300.0) 152 | (250.0, 205.0) 153 | (105.0, 10.0) 154 | 155 | If you want, you can now repeat the edit process for any of the points - 156 | including the midpoints. 157 | 158 | GUI layout 159 | ---------- 160 | 161 | The most common usage (by deployment count) of the Cassowary algoritm is as 162 | the Autolayout mechanism that underpins GUIs in OS X Lion and iOS6. Although 163 | there's lots of code required to make a full GUI toolkit work, the layout 164 | problem is a relatively simple case of solving constraints regarding the size 165 | and position of widgets in a window. 166 | 167 | In this example, we'll show a set of constraints used to determine the 168 | placement of a pair of buttons in a GUI. To simplify the problem, we'll only 169 | worry about the X coordinate; expanding the implementation to include the Y 170 | coordinate is a relatively simple exercise left for the reader. 171 | 172 | When laying out a GUI, widgets have a width; however, widgets can also change 173 | size. To accomodate this, a widget has two size constraints in each dimension: 174 | a minimum size, and a preferred size. The miniumum size is a ``required`` 175 | constraint that must be met; the preferred size is a ``strong`` constraint 176 | that the solver should try to accomodate, but may break if necessary. 177 | 178 | The GUI also needs to be concerned about the size of the window that is being 179 | laid out. The size of the window can be handled in two ways: 180 | 181 | * a ``required`` constraint -- i.e., this *is* the size of the window; 182 | show me how to lay out the widgets; or 183 | 184 | * a ``weak`` constraint -- i.e., come up with a value for the window size that 185 | accomodates all the other widget constraints. This is the interpretation used 186 | to determine an initial window size. 187 | 188 | As with the Quadrilateral demo, we start by creating the solver, and creating 189 | a storage mechanism to hold details about buttons:: 190 | 191 | using namespace rhea; 192 | simplex_solver solver; 193 | 194 | struct button 195 | { 196 | variable left, width; 197 | 198 | linear_expression right() const { return left + width; } 199 | linear_expression center() const { return left + width * 0.5; } 200 | }; 201 | 202 | The button's edges, width, and center are all interrelated, so we don't have 203 | to create variables for everything. We could also make variables for left and 204 | right, and define width as an expression. 205 | 206 | We then define our two buttons, and the variables describing the size of the 207 | window on which the buttons will be placed:: 208 | 209 | button b1, b2; 210 | variable left_limit{0}, right_limit; 211 | 212 | solver.add_constraint(left_limit == 0); 213 | solver.add_stay(right_limit, strength::weak()); 214 | 215 | The left limit is set as a required constraint -- the left border can't 216 | move from coordinate 0. However, the window can expand if necessary to 217 | accomodate the widgets it contains, so the right limit is a ``weak`` stay 218 | constraint. 219 | 220 | Now we can define the constraints on the button layouts:: 221 | 222 | solver.add_constraints({ 223 | // The two buttons are the same width. 224 | b1.width == b2.width, 225 | 226 | // Button1 starts 50 from the left margin. 227 | b1.left == left_limit + 50, 228 | 229 | // Button2 ends 50 from the right margin. 230 | left_limit + right_limit == b2.right() + 50, 231 | 232 | // Button2 starts at least 100 from the end of Button1. This is the 233 | // "elastic" constraint in the system that will absorb extra space 234 | // in the layout. 235 | b2.left == b1.right() + 100, 236 | 237 | // Button1 has a minimum width of 87. 238 | b1.width >= 87, 239 | 240 | // Button1's preferred width is 87. 241 | b1.width == 87 | strength::strong(), 242 | 243 | // Button2's minimum width is 113. 244 | b2.width >= 113, 245 | 246 | // Button2's preferred width is 113. 247 | b2.width == 113 | strength::strong() 248 | }); 249 | 250 | Since we haven't imposed a hard constraint on the right hand side, the 251 | constraint system will give us the smallest window that will satisfy these 252 | constraints:: 253 | 254 | namespace std 255 | { 256 | ostream& operator<< (ostream& str, const button& btn) 257 | { 258 | return str << "left=" << str.left << ", width=" << str.width; 259 | } 260 | } 261 | 262 | std::cout << b1 << std::endl; 263 | // left=50, width=113 264 | 265 | std::cout << b2 << std::endl; 266 | // left=263, width=113 267 | 268 | std::cout << right_limit.value() << std::endl; 269 | // 426 270 | 271 | That is, the smallest window that can accomodate these constraints is 426 272 | pixels wide. However, if the user makes the window larger, we can still lay 273 | out widgets. We impose a new ``required`` constraint with the size of the 274 | window:: 275 | 276 | constraint right_limit_stay {right_limit == 500}; 277 | solver.add_constraint(right_limit_stay); 278 | 279 | std::cout << b1 << std::endl; 280 | // left=50, width=113 281 | 282 | std::cout << b2 << std::endl; 283 | // left=337, width=113 284 | 285 | std::cout << right_limit.value() << std::endl; 286 | // 500 287 | 288 | That is - if the window size is 500 pixels, the layout will compensate by 289 | putting ``button2`` a little further to the right. The ``weak`` stay on the 290 | right limit that we established at the start is ignored in preference for the 291 | ``required`` stay. 292 | 293 | If the window is then resized again, we can remove the 500 pixel limit, and 294 | impose a new limit:: 295 | 296 | solver.remove_constraint(right_limit_stay); 297 | right_limit_stay = (right_limit == 475); 298 | solver.add_constraint(right_limit_stay); 299 | 300 | std::cout << b2 << std::endl; 301 | // left=312, width=113 302 | 303 | std::cout << right_limit.value() << std::endl; 304 | // 475 305 | 306 | Again, ``button2`` has been moved, this time to the left, compensating for the 307 | space that was lost by the contracting window size. 308 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | Rhea 2 | ==== 3 | 4 | A C++11 implementation of the `Cassowary constraint-solving algorithm`_. 5 | Cassowary is the algorithm that forms the core of the OS X and iOS `user 6 | interface layout mechanism`_. Rhea is a lighter, friendlier version of the 7 | original implementation by Greg J. Badros and Alan Borning. 8 | 9 | This manual is based on `PyBee Cassowary`_'s documentation, written by Russell 10 | Keith-Magee. 11 | 12 | .. _Cassowary constraint-solving algorithm: http://www.cs.washington.edu/research/constraints/cassowary/ 13 | .. _user interface layout mechanism: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Introduction/Introduction.html 14 | .. _Pybee Cassowary: https://github.com/pybee/cassowary 15 | 16 | Quickstart 17 | ---------- 18 | 19 | Grabbing, building, and installing the library works pretty much like any 20 | other CMake project:: 21 | 22 | $ git clone https://github.com/Nocte-/rhea 23 | $ mkdir rhea-build 24 | $ cd rhea-build 25 | $ cmake ../rhea 26 | $ make 27 | $ sudo make install 28 | 29 | main.cpp:: 30 | 31 | #include 32 | #include 33 | 34 | main() 35 | { 36 | rhea::variable left, mid, right; 37 | rhea::simplex_solver solver; 38 | 39 | solver.add_constraints( 40 | { 41 | mid == (left + right) / 2, 42 | right == left + 10, 43 | right <= 100, 44 | left >= 0 45 | }); 46 | solver.suggest(mid, 2); 47 | 48 | std::cout << left << " " << mid << " " << right << std::endl; 49 | } 50 | 51 | Build and run:: 52 | 53 | $ g++ --std=c++11 main.cpp -lrhea 54 | $ ./a.out 55 | 0.000 5.000 10.000 56 | 57 | User guide 58 | ---------- 59 | 60 | .. toctree:: 61 | :maxdepth: 2 62 | 63 | theory 64 | examples 65 | 66 | Contribute 67 | ---------- 68 | 69 | * Browse the source on Github_. 70 | * `Issue tracker`_. 71 | 72 | .. _Github: https://github.com/Nocte-/rhea 73 | .. _Issue tracker: https://github.com/Nocte-/rhea/issues 74 | 75 | 76 | API reference 77 | ------------- 78 | 79 | .. toctree:: 80 | ref/class_variable 81 | ref/class_constraint 82 | ref/class_dummy_variable 83 | ref/class_abstract_variable 84 | ref/class_linear_equation 85 | ref/class_linear_expression 86 | ref/class_simplex_solver 87 | -------------------------------------------------------------------------------- /doc/source/ref/class_abstract_variable.rst: -------------------------------------------------------------------------------- 1 | class rhea::abstract_variable 2 | ============================= 3 | 4 | .. doxygenclass:: rhea::abstract_variable 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /doc/source/ref/class_constraint.rst: -------------------------------------------------------------------------------- 1 | class rhea::constraint 2 | ====================== 3 | 4 | .. doxygenclass:: rhea::constraint 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /doc/source/ref/class_dummy_variable.rst: -------------------------------------------------------------------------------- 1 | class rhea::dummy_variable 2 | ========================== 3 | 4 | .. doxygenclass:: rhea::dummy_variable 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /doc/source/ref/class_linear_equation.rst: -------------------------------------------------------------------------------- 1 | class rhea::linear_equation 2 | =========================== 3 | 4 | .. doxygenclass:: rhea::linear_equation 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /doc/source/ref/class_linear_expression.rst: -------------------------------------------------------------------------------- 1 | class rhea::linear_expression 2 | ============================= 3 | 4 | .. doxygenclass:: rhea::linear_expression 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /doc/source/ref/class_simplex_solver.rst: -------------------------------------------------------------------------------- 1 | class rhea::simplex_solver 2 | ========================== 3 | 4 | .. doxygenclass:: rhea::simplex_solver 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /doc/source/ref/class_variable.rst: -------------------------------------------------------------------------------- 1 | class rhea::variable 2 | ==================== 3 | 4 | .. doxygenclass:: rhea::variable 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /doc/source/theory.rst: -------------------------------------------------------------------------------- 1 | Constraint solving systems 2 | ========================== 3 | 4 | Constraint solving systems are an algorithmic approach to solving linear 5 | programming problems. A linear programming problem is a mathematical problem 6 | where you have a set of non-negative, real-valued variables (:math:`x_1, x_2, 7 | \dotsc x_n`), and a series of linear constraints on those variables. These 8 | constraints are expressed as a set of equations of the form: 9 | 10 | .. math:: 11 | 12 | \begin{align*} 13 | a_1 x_1 + a_2 x_2 + \dotsc + a_n x_n &= b \\ 14 | &\leq b , or: \\ 15 | &\geq b , or: 16 | \end{align*} 17 | 18 | Given these constraints, the problem is to find the values of :math:`x_i` that 19 | minimizes or maximizes the value of an **objective function**: 20 | 21 | .. math:: 22 | 23 | c + d_1 x_1 + d_2 x_2 + \dotsc + d_n x_n 24 | 25 | Cassowary is an algorithm designed to solve linear programming problems of 26 | this type. Published in 1997, it now forms the basis fo the UI layout tools 27 | in OS X Lion, and iOS 6+ (the approach known as `Auto Layout`_). The Cassowary 28 | algorithm (and this implementation of it) provides the tools to describe a set 29 | of constraints, and then find an optimal solution for it. 30 | 31 | .. _Auto Layout: https://developer.apple.com/library/ios/documentation/userexperience/conceptual/AutolayoutPG/Introduction/Introduction.html 32 | 33 | Variables 34 | --------- 35 | 36 | At the core of the constraint problem are the variables in the system. 37 | In the formal mathematical system, these are the :math:`x_i` terms; in C++, 38 | these are instances of the :class:`~rhea::variable` class:: 39 | 40 | #include 41 | 42 | // Create a variable with a default value. 43 | rhea::variable x1; 44 | 45 | // Create a variable with a specific value. 46 | rhea::variable x2 {42.0}; 47 | 48 | Any value provided for the variable is just a starting point. When constraints 49 | are imposed, this value can and will change, subject to the requirements of the 50 | constraints. However, providing an initial value may affect the search process; 51 | if there's an ambiguity in the constraints (i.e., there's more than one 52 | possible solution), the initial value provided to variables will affect the 53 | solution on which the system converges. 54 | 55 | Constraints 56 | ----------- 57 | 58 | A constraint is a mathematical equality or inequality that defines the linear 59 | programming system. Its declaration in C++ looks essentially the same as the 60 | mathematical expression:: 61 | 62 | rhea::variable x1, x2, x3; 63 | 64 | // Define the constraint 65 | auto constraint = x1 + 3 * x2 <= 4 * x3 + 2; 66 | 67 | In this example, `constraint` holds the defintion for the constraint system. 68 | Although the statement uses the C++ comparison operator `<=`, the result is 69 | *not* a boolean. The comparison operators `<=`, `>=`, and `==` have 70 | been overridden for instances of :class:`~rhea::linear_expression` to enable 71 | you to easily define constraints. 72 | 73 | In a similar way, the operators `+`, `-`, `*`, and `/` have been overridden 74 | for :class:`~rhea::variable` to easily create linear expressions. 75 | 76 | Solvers 77 | ------- 78 | 79 | The solver is the engine that resolves the linear constraints into a solution. 80 | There are many approaches to this problem, and the development of algorithmic 81 | approaches has been the subject of math and computer science research for over 82 | 70 years. Rhea provides one implementation -- a :class:`~rhea::simplex_solver`, 83 | implementing the Simplex algorithm defined by Dantzig in the 1940s. 84 | 85 | The solver takes no arguments during constructions; once constructed, you simply 86 | add constraints to the system. 87 | 88 | As a simple example, let's solve the problem posed in Section 2 of the `Badros 89 | & Borning's paper on Cassowary`_. In this problem, we have a 1-dimensional 90 | number line spanning from 0 to 100. There are three points on it (left, middle 91 | and right), with the following constraints: 92 | 93 | * The middle point must be halfway between the left and right point; 94 | * The left point must be at least 10 to the left of the right point; 95 | * All points must fall in the range 0-100. 96 | 97 | This system can be defined in C++ as follows:: 98 | 99 | #include 100 | 101 | rhea::simplex_solver solver; 102 | rhea::variable left, middle, right; 103 | 104 | solver.add_constraints({ 105 | middle == (left + right) / 2, 106 | right == left + 10, 107 | right <= 100, 108 | left >= 0 109 | }); 110 | 111 | There are an infinite number of possible solutions to this system; if we 112 | interrogate the variables, you'll see that the solver has provided one 113 | possible solution:: 114 | 115 | left = 90 116 | middle = 95 117 | right = 100 118 | 119 | .. _Badros & Borning's paper on Cassowary: http://www.cs.washington.edu/research/constraints/cassowary/cassowary-tr.pdf 120 | 121 | Stay constraints 122 | ---------------- 123 | 124 | If we want a particular solution to our left/right/middle problem, we need to 125 | fix a value somewhere. To do this, we add a `stay` -- a special constraint that 126 | says that the value should not be altered. 127 | 128 | For example, we might want to enforce the fact that the middle value should 129 | stay at a value of 45. We construct the system as before, but add:: 130 | 131 | middle = 45.0; 132 | solver.add_stay(middle); 133 | 134 | Now when we interrogate the solver, we'll get values that reflect this fixed 135 | point:: 136 | 137 | left = 40 138 | middle = 45 139 | right = 50 140 | 141 | Constraint strength 142 | ------------------- 143 | 144 | Not all constraints are equal. Some are absolute requirements -- for example, a 145 | requirement that all values remain in a specific range. However, other 146 | constraints may be suggestions, rather than hard requirements. 147 | 148 | To accomodate this, Rhea allows all constraints to have a **strength**. Strength 149 | can be one of: 150 | 151 | * ``required`` 152 | * ``strong`` 153 | * ``medium`` 154 | * ``weak`` 155 | 156 | ``required`` constraints **must** be satisfied; the remaining strengths will 157 | be satisfied with declining priority. 158 | 159 | To define a strength, provide the strength value as an argument when adding 160 | the constraint (or stay):: 161 | 162 | using namespace rhea; 163 | 164 | solver solver; 165 | variable x; 166 | 167 | // Define some non-required constraints 168 | solver.add_constraint(x <= 100, strength::medium()); 169 | solver.add_stay(x, strength::strong()); 170 | 171 | By default: 172 | 173 | * Explicit constraints are ``required`` 174 | * Edit constraints are ``strong`` 175 | * Stay constraints are ``weak`` 176 | 177 | Constraint weight 178 | ----------------- 179 | 180 | If you have multiple constraints of the same strength, you may want to have a 181 | tie-breaker between them. To do this, you can set a **weight**, in addition to 182 | a strength:: 183 | 184 | // Define some non-required constraints 185 | solver.add_constraint(x <= 100, strength::strong(), 10); 186 | solver.add_constraint(x >= 50, strength::medium(), 20); 187 | 188 | Editing constraints 189 | ------------------- 190 | 191 | Any constraint can be removed from a system; just retain the reference provided 192 | when you add the constraint:: 193 | 194 | // Define a constraint 195 | constraint c1 {x <= 100}; 196 | solver.add_constraint(c1); 197 | 198 | // Remove it again 199 | solver.remove_constraint(c1); 200 | 201 | Once a constraint is removed, the system will be automatically re-evaluated, 202 | with the possible side effect that the values in the system will change. 203 | 204 | But what if you want to change a variable's value without introducing a 205 | new constraint? In this case, you can ``suggest`` a new value for it. 206 | 207 | Here's an example of an edit context in practice:: 208 | 209 | // Add a stay to x - try to keep it the same 210 | solver.add_stay(x); 211 | 212 | // Suggest a new value for x: 213 | solver.suggest(x, 42.0); 214 | 215 | When the edit context exits, the system will re-evaluate itself, and the 216 | variable will have the new value. However, the variable isn't guaranteed 217 | to have the value you provided - in this case it will, but if your 218 | constraint system has other constraints, they may affect the value of 219 | the variable after the suggestion has been applied. 220 | -------------------------------------------------------------------------------- /rhea/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8.3) 2 | set(LIBNAME rhea) 3 | 4 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/version.hpp) 5 | file(GLOB SOURCE_FILES "*.cpp") 6 | file(GLOB HEADER_FILES "*.hpp") 7 | 8 | set (LIBNAME_S "${LIBNAME}-s") 9 | add_library(${LIBNAME_S} STATIC ${SOURCE_FILES} ${HEADER_FILES}) 10 | add_library(${LIBNAME} SHARED ${SOURCE_FILES} ${HEADER_FILES}) 11 | set_target_properties(${LIBNAME} PROPERTIES VERSION ${VERSION} SOVERSION ${SOVERSION}) 12 | 13 | install(TARGETS ${LIBNAME_S} ${LIBNAME} DESTINATION lib) 14 | install(FILES ${HEADER_FILES} DESTINATION include/rhea) 15 | 16 | -------------------------------------------------------------------------------- /rhea/abstract_constraint.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file abstract_constraint.hpp 3 | /// \brief Base class for constraints 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include "linear_expression.hpp" 12 | #include "strength.hpp" 13 | #include "variable.hpp" 14 | 15 | namespace rhea 16 | { 17 | 18 | // Forward declaration 19 | class solver; 20 | 21 | /** Base class for constraints. */ 22 | class abstract_constraint 23 | { 24 | public: 25 | abstract_constraint(strength s = strength::required(), double weight = 1.0) 26 | : strength_{std::move(s)} 27 | , weight_{weight} 28 | { 29 | if (weight_ == 0.0) 30 | throw std::runtime_error("constraint weight cannot be zero"); 31 | } 32 | 33 | virtual ~abstract_constraint() {} 34 | 35 | virtual linear_expression expression() const = 0; 36 | 37 | /** Check if this is an edit_constraint. */ 38 | virtual bool is_edit_constraint() const { return false; } 39 | 40 | /** Check if this is a linear_inequality. */ 41 | virtual bool is_inequality() const { return false; } 42 | 43 | /** Check if this is a required constraint. */ 44 | virtual bool is_required() const { return strength_.is_required(); } 45 | 46 | /** Check if this is a stay_constraint. */ 47 | virtual bool is_stay_constraint() const { return false; } 48 | 49 | /** Get the strength of this constraint. */ 50 | const strength& get_strength() const { return strength_; } 51 | 52 | /** Get the weight of this constraint. */ 53 | virtual double weight() const { return weight_; } 54 | 55 | public: 56 | /** Returns true iff this constraint is satisfied. */ 57 | virtual bool is_satisfied() const = 0; 58 | 59 | public: 60 | /** Change the strength. 61 | * Note that Rhea does not allow changing the weight of a constraint 62 | * that is already part of a solver. */ 63 | void change_strength(const strength& new_strength) 64 | { 65 | strength_ = new_strength; 66 | } 67 | 68 | /** Change the weight. 69 | * Note that Rhea does not allow changing the weight of a constraint 70 | * that is already part of a solver. */ 71 | void change_weight(double new_weight) { weight_ = new_weight; } 72 | 73 | /** Get the symbolic weight. */ 74 | symbolic_weight get_symbolic_weight() const { return strength_.weight(); } 75 | 76 | /** Get the symbolic weight after adjusting it for weight. */ 77 | double adjusted_symbolic_weight() const 78 | { 79 | return get_symbolic_weight().as_double() * weight(); 80 | } 81 | 82 | /** Set a new strength without any checks. 83 | * \see change_strength */ 84 | void set_strength(const strength& n) { strength_ = n; } 85 | 86 | /** Set a new weight without any checks. 87 | * \see change_weight */ 88 | void set_weight(double n) { weight_ = n; } 89 | 90 | protected: 91 | strength strength_; 92 | double weight_; 93 | }; 94 | 95 | } // namespace rhea 96 | -------------------------------------------------------------------------------- /rhea/abstract_variable.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // abstract_variable.cpp 3 | // 4 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 5 | //--------------------------------------------------------------------------- 6 | #include "abstract_variable.hpp" 7 | 8 | namespace rhea 9 | { 10 | 11 | size_t abstract_variable::count_ = 0; 12 | 13 | } // namespace rhea 14 | -------------------------------------------------------------------------------- /rhea/abstract_variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file abstract_variable.hpp 3 | /// \brief Base class for variables 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include "errors.hpp" 12 | 13 | namespace rhea 14 | { 15 | 16 | /** Base class for variables. */ 17 | class abstract_variable 18 | { 19 | public: 20 | abstract_variable() 21 | : id_{++count_} 22 | { 23 | } 24 | 25 | virtual ~abstract_variable() {} 26 | 27 | size_t id() const { return id_; } 28 | 29 | /** Return true if this is a floating point variable. 30 | * \sa float_variable */ 31 | virtual bool is_float() const { return false; } 32 | 33 | /** Return true if this is a variable in a finite domain. */ 34 | virtual bool is_fd() const { return false; } 35 | 36 | /** Return true if this a dummy variable. 37 | * Dummies are used as a marker variable for required equality 38 | * constraints. Such variables aren't allowed to enter the basis 39 | * when pivoting. \sa dummy_variable */ 40 | virtual bool is_dummy() const { return false; } 41 | 42 | /** Return true if this a variable known outside the solver. */ 43 | virtual bool is_external() const { return false; } 44 | 45 | /** Return true if we can pivot on this variable. 46 | * \sa simplex_solver::pivot() */ 47 | virtual bool is_pivotable() const 48 | { 49 | throw too_difficult("variable not usable inside simplex_solver"); 50 | } 51 | 52 | /** Return true if this is a restricted (or slack) variable. 53 | * Such variables are constrained to be non-negative and occur only 54 | * internally to the simplex solver. 55 | * \sa slack_variable */ 56 | virtual bool is_restricted() const 57 | { 58 | throw too_difficult("variable not usable inside simplex_solver"); 59 | } 60 | 61 | /** Get the value of this variable. */ 62 | virtual double value() const { return 0.0; } 63 | 64 | /** Get the value of this variable as an integer */ 65 | virtual int int_value() const { return 0; } 66 | 67 | // LCOV_EXCL_START 68 | virtual void set_value(double) { assert(false); } 69 | 70 | virtual void change_value(double) { assert(false); } 71 | // LCOV_EXCL_STOP 72 | 73 | /** Get the value as a string. */ 74 | virtual std::string to_string() const { return "abstract"; } 75 | 76 | private: 77 | // Not happy with this, but it appears the algorithm needs this to run 78 | // with the autosolver turned off. (Expression terms need a stable 79 | // iteration order, see also Github issue #16.) 80 | static size_t count_; 81 | size_t id_; 82 | }; 83 | 84 | } // namespace rhea 85 | -------------------------------------------------------------------------------- /rhea/action_variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file action_variable.hpp 3 | /// \brief A variable that calls a function whenever it changes 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include "float_variable.hpp" 11 | 12 | namespace rhea 13 | { 14 | 15 | /** A variable that calls a function whenever it changes. 16 | */ 17 | class action_variable : public float_variable 18 | { 19 | public: 20 | action_variable(std::function callback, double value) 21 | : float_variable{value} 22 | , callback_{callback} 23 | { 24 | } 25 | 26 | virtual ~action_variable() {} 27 | 28 | virtual void set_value(double new_value) 29 | { 30 | value_ = new_value; 31 | callback_(new_value); 32 | } 33 | 34 | virtual void change_value(double new_value) { set_value(new_value); } 35 | 36 | protected: 37 | std::function callback_; 38 | }; 39 | 40 | } // namespace rhea 41 | -------------------------------------------------------------------------------- /rhea/approx.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file approx.hpp 3 | /// \brief See if two values are equal within a margin 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace rhea 12 | { 13 | 14 | /** Return true iff a and b are approximately the same. */ 15 | inline bool approx(double a, double b) 16 | { 17 | const double epsilon = 1.0e-8; 18 | if (a > b) { 19 | return (a - b) < epsilon; 20 | } else { 21 | return (b - a) < epsilon; 22 | } 23 | } 24 | 25 | /** Return true iff a is almost zero. */ 26 | inline bool near_zero(double a) 27 | { 28 | return approx(a, 0.0); 29 | } 30 | 31 | } // namespace rhea 32 | -------------------------------------------------------------------------------- /rhea/constraint.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file constraint.hpp 3 | /// \brief Base class for constraints 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include "abstract_constraint.hpp" 12 | #include "linear_equation.hpp" 13 | #include "linear_inequality.hpp" 14 | 15 | namespace rhea 16 | { 17 | 18 | /** An equation or inequality involving one or more variables. 19 | * Constraints can be defined as "normal" C++ expressions: 20 | * \code 21 | 22 | variable x (1), y (2); 23 | 24 | constraint a (x + 4 <= y * 2); 25 | constraint b (x * 2 == y * 3); 26 | 27 | * \endcode 28 | */ 29 | class constraint 30 | { 31 | public: 32 | constraint() {} 33 | 34 | template 35 | constraint(std::shared_ptr&& p) 36 | : p_{std::move(p)} 37 | { 38 | } 39 | 40 | constraint(const linear_equation& eq) 41 | : p_{std::make_shared(eq)} 42 | { 43 | } 44 | 45 | constraint(const linear_equation& eq, strength s, double weight = 1) 46 | : p_{std::make_shared(eq.expression(), std::move(s), 47 | weight)} 48 | { 49 | } 50 | 51 | constraint(const linear_inequality& eq) 52 | : p_{std::make_shared(eq)} 53 | { 54 | } 55 | 56 | constraint(const linear_inequality& eq, strength s, double weight = 1) 57 | : p_{std::make_shared(eq.expression(), std::move(s), 58 | weight)} 59 | { 60 | } 61 | 62 | public: 63 | linear_expression expression() const { return p_->expression(); } 64 | 65 | bool is_edit_constraint() const { return p_->is_edit_constraint(); } 66 | 67 | bool is_inequality() const { return p_->is_inequality(); } 68 | 69 | bool is_required() const { return p_->is_required(); } 70 | 71 | bool is_stay_constraint() const { return p_->is_stay_constraint(); } 72 | 73 | const strength& get_strength() const { return p_->get_strength(); } 74 | 75 | double weight() const { return p_->weight(); } 76 | 77 | bool is_satisfied() const { return p_->is_satisfied(); } 78 | 79 | void change_strength(const strength& new_strength) 80 | { 81 | p_->change_strength(new_strength); 82 | } 83 | 84 | void change_weight(double new_weight) { p_->change_weight(new_weight); } 85 | 86 | symbolic_weight get_symbolic_weight() const 87 | { 88 | return p_->get_symbolic_weight(); 89 | } 90 | 91 | double adjusted_symbolic_weight() const 92 | { 93 | return p_->adjusted_symbolic_weight(); 94 | } 95 | 96 | void set_strength(const strength& n) { p_->set_strength(n); } 97 | 98 | void set_weight(double n) { p_->set_weight(n); } 99 | 100 | template 101 | T& as() 102 | { 103 | return dynamic_cast(*p_); 104 | } 105 | 106 | template 107 | const T& as() const 108 | { 109 | return dynamic_cast(*p_); 110 | } 111 | 112 | bool is_nil() const { return !p_; } 113 | 114 | public: 115 | constraint& operator=(const constraint& assign) 116 | { 117 | p_ = assign.p_; 118 | return *this; 119 | } 120 | 121 | virtual bool operator==(const constraint& other) const 122 | { 123 | return p_ == other.p_; 124 | } 125 | 126 | virtual bool operator!=(const constraint& other) const 127 | { 128 | return p_ != other.p_; 129 | } 130 | 131 | size_t hash() const 132 | { 133 | return std::hash>()(p_); 134 | } 135 | 136 | private: 137 | std::shared_ptr p_; 138 | }; 139 | 140 | /** Convenience typedef for bundling constraints. */ 141 | typedef std::list constraint_list; 142 | 143 | } // namespace rhea 144 | 145 | namespace std 146 | { 147 | 148 | /** Hash function, required for std::unordered_map. */ 149 | template <> 150 | struct hash : public unary_function 151 | { 152 | size_t operator()(const rhea::constraint& c) const { return c.hash(); } 153 | }; 154 | 155 | } // namespace std 156 | -------------------------------------------------------------------------------- /rhea/dummy_variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file dummy_variable.hpp 3 | /// \brief Dummy placeholder used when solving a tableau 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "abstract_variable.hpp" 10 | 11 | namespace rhea 12 | { 13 | 14 | /** Dummy variables are inserted by the simplex solver as a marker when 15 | ** incrementally removing a required equality constraint. */ 16 | class dummy_variable : public abstract_variable 17 | { 18 | public: 19 | dummy_variable() 20 | : abstract_variable() 21 | { 22 | } 23 | 24 | virtual ~dummy_variable() {} 25 | 26 | virtual bool is_dummy() const { return true; } 27 | virtual bool is_external() const { return false; } 28 | virtual bool is_pivotable() const { return false; } 29 | virtual bool is_restricted() const { return true; } 30 | 31 | virtual std::string to_string() const { return "dummy"; } 32 | }; 33 | 34 | } // namespace rhea 35 | -------------------------------------------------------------------------------- /rhea/edit_constraint.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file edit_constraint.hpp 3 | /// \brief Edit constraint 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "edit_or_stay_constraint.hpp" 10 | 11 | namespace rhea 12 | { 13 | 14 | /** Edit constraints are added to a tableau on a variable, so that a 15 | ** new value can be suggested for that variable later on. */ 16 | class edit_constraint : public edit_or_stay_constraint 17 | { 18 | public: 19 | edit_constraint(const variable& v, const strength& s = strength::strong(), 20 | double weight = 1.0) 21 | : edit_or_stay_constraint{v, s, weight} 22 | { 23 | } 24 | 25 | virtual ~edit_constraint() {} 26 | 27 | virtual bool is_edit_constraint() const { return true; } 28 | 29 | virtual bool is_satisfied() const { return false; } 30 | }; 31 | 32 | } // namespace rhea 33 | -------------------------------------------------------------------------------- /rhea/edit_or_stay_constraint.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file edit_or_stay_constraint.hpp 3 | /// \brief Base class for edit- and stay-constraints 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "abstract_constraint.hpp" 10 | #include "linear_expression.hpp" 11 | 12 | namespace rhea 13 | { 14 | 15 | /** Abstract constraint that can be related to a variable, used only as 16 | ** a base class for edit_constraint and stay_constraint. */ 17 | class edit_or_stay_constraint : public abstract_constraint 18 | { 19 | public: 20 | edit_or_stay_constraint(const variable& v, 21 | strength s = strength::required(), 22 | double weight = 1.0) 23 | : abstract_constraint{s, weight} 24 | , var_{v} 25 | { 26 | } 27 | 28 | virtual ~edit_or_stay_constraint() {} 29 | 30 | const variable& var() const { return var_; } 31 | 32 | linear_expression expression() const 33 | { 34 | return linear_expression(var_, -1, var_.value()); 35 | } 36 | 37 | protected: 38 | variable var_; 39 | }; 40 | 41 | } // namespace rhea 42 | -------------------------------------------------------------------------------- /rhea/errors.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file errors.hpp 3 | /// \brief Exception classes 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace rhea 13 | { 14 | 15 | class variable; 16 | 17 | /** Base class for all Rhea exceptions. */ 18 | class error : public std::exception 19 | { 20 | public: 21 | virtual ~error() throw() {} 22 | 23 | // LCOV_EXCL_START 24 | virtual const char* what() const throw() { return "unspecified error"; } 25 | // LCOV_EXCL_STOP 26 | }; 27 | 28 | /** Signals an internal inconsistency in the solver. */ 29 | class internal_error : public error 30 | { 31 | std::string msg; 32 | 33 | public: 34 | internal_error(std::string m) 35 | : msg(m) 36 | { 37 | } 38 | 39 | virtual ~internal_error() throw() {} 40 | 41 | virtual const char* what() const throw() { return msg.c_str(); } 42 | }; 43 | 44 | /** Thrown whenever the usual ordering of setting up edit constraints is 45 | ** not observed. 46 | * The usual order is: 47 | * - simplex_solver::add_edit_var() 48 | * - simplex_solver::begin_edit() 49 | * - simplex_solver::suggest_value() 50 | * - simplex_solver::end_edit() 51 | * 52 | * This is done automatically by simplex_solver::suggest(). */ 53 | class edit_misuse : public error 54 | { 55 | const variable* var_; 56 | 57 | public: 58 | edit_misuse() 59 | : var_{nullptr} 60 | { 61 | } 62 | edit_misuse(const rhea::variable& v) 63 | : var_{&v} 64 | { 65 | } 66 | virtual ~edit_misuse() throw() {} 67 | 68 | virtual const char* what() const throw() 69 | { 70 | return "edit protocol usage violation"; 71 | } 72 | 73 | const variable& var() const { return *var_; } 74 | }; 75 | 76 | /** The constraints are too difficult to solve. */ 77 | class too_difficult : public error 78 | { 79 | std::string msg; 80 | 81 | public: 82 | too_difficult() {} 83 | 84 | too_difficult(std::string m) 85 | : msg(m) 86 | { 87 | } 88 | 89 | virtual ~too_difficult() throw() {} 90 | 91 | virtual const char* what() const throw() 92 | { 93 | return msg.empty() ? "the constraints are too difficult to solve" 94 | : msg.c_str(); 95 | } 96 | }; 97 | 98 | /** Cyclic dependencies between constraints are not allowed. */ 99 | class cycle_not_allowed : public too_difficult 100 | { 101 | public: 102 | virtual ~cycle_not_allowed() throw() {} 103 | 104 | virtual const char* what() const throw() 105 | { 106 | return "a cyclic constraint graph is not permitted by the solver"; 107 | } 108 | }; 109 | 110 | /** One of the required constraints cannot be satisfied. */ 111 | class required_failure : public error 112 | { 113 | public: 114 | virtual ~required_failure() throw() {} 115 | 116 | virtual const char* what() const throw() 117 | { 118 | return "a required constraint cannot be satisfied"; 119 | } 120 | }; 121 | 122 | /** Not enough stay constraints were specified to give specific values 123 | ** to every variable. */ 124 | class not_enough_stays : public error 125 | { 126 | public: 127 | virtual ~not_enough_stays() throw() {} 128 | 129 | virtual const char* what() const throw() 130 | { 131 | return "there are not enough stays to give specific values to every " 132 | "variable"; 133 | } 134 | }; 135 | 136 | /** The resulting expression would be nonlinear. 137 | * This usually happens when multiplying two expressions that have the 138 | * same variable in them, resulting in a quadratic expression. */ 139 | class nonlinear_expression : public error 140 | { 141 | public: 142 | virtual ~nonlinear_expression() throw() {} 143 | 144 | virtual const char* what() const throw() 145 | { 146 | return "the resulting expression would be nonlinear"; 147 | } 148 | }; 149 | 150 | /** The application tried to remove a constraint that doesn't exist in 151 | ** the solver. */ 152 | class constraint_not_found : public error 153 | { 154 | public: 155 | virtual ~constraint_not_found() throw() {} 156 | 157 | virtual const char* what() const throw() 158 | { 159 | return "tried to remove a constraint that was never added"; 160 | } 161 | }; 162 | 163 | /** The application tried to remove a row that doesn't exist. */ 164 | class row_not_found : public error 165 | { 166 | public: 167 | virtual ~row_not_found() throw() {} 168 | 169 | virtual const char* what() const throw() { return "row does not exist"; } 170 | }; 171 | 172 | } // namespace rhea 173 | -------------------------------------------------------------------------------- /rhea/errors_expl.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file errors_expl.hpp 3 | /// \brief Adds an explanation to the required_failure exception class 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "constraint.hpp" 10 | #include "errors.hpp" 11 | 12 | namespace rhea 13 | { 14 | 15 | /** One of the required constraints cannot be satisfied. 16 | * This exception extends required_failure with a list of the constraints 17 | * that were involved. Dropping one or more of the constraints, or 18 | * lowering their priority, will usually solve the problem. */ 19 | class required_failure_with_explanation : public required_failure 20 | { 21 | public: 22 | required_failure_with_explanation(constraint_list cl) 23 | : expl_(std::move(cl)) 24 | { 25 | } 26 | 27 | virtual ~required_failure_with_explanation() throw() {} 28 | 29 | const constraint_list& explanation() const { return expl_; } 30 | 31 | void add(constraint c) { expl_.emplace_back(c); } 32 | 33 | protected: 34 | constraint_list expl_; 35 | }; 36 | 37 | } // namespace rhea 38 | -------------------------------------------------------------------------------- /rhea/flat_map.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file flat_map.hpp 3 | /// \brief Replacement for boost::flat_map (VS2013 workaround) 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | // Because VS2013 can't compile boost::flat_map, we use this one as a 9 | // temporary fix. 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace rhea 17 | { 18 | 19 | namespace detail 20 | { 21 | 22 | template 23 | class flat_map_compare : public C 24 | { 25 | typedef std::pair Data; 26 | typedef typename C::first_argument_type first_argument_type; 27 | 28 | public: 29 | flat_map_compare() {} 30 | 31 | flat_map_compare(const C& src) 32 | : C(src) 33 | { 34 | } 35 | 36 | bool operator()(const first_argument_type& lhs, 37 | const first_argument_type& rhs) const 38 | { 39 | return C::operator()(lhs, rhs); 40 | } 41 | 42 | bool operator()(const Data& lhs, const Data& rhs) const 43 | { 44 | return operator()(lhs.first, rhs.first); 45 | } 46 | 47 | bool operator()(const Data& lhs, const first_argument_type& rhs) const 48 | { 49 | return operator()(lhs.first, rhs); 50 | } 51 | 52 | bool operator()(const first_argument_type& lhs, const Data& rhs) const 53 | { 54 | return operator()(lhs, rhs.first); 55 | } 56 | }; 57 | } 58 | 59 | template , 60 | class A = std::allocator>> 61 | class flat_map : private std::vector, A>, 62 | private detail::flat_map_compare 63 | { 64 | typedef std::vector, A> base_type; 65 | typedef detail::flat_map_compare compare_type; 66 | 67 | public: 68 | typedef K key_type; 69 | typedef V mapped_type; 70 | typedef typename base_type::value_type value_type; 71 | 72 | typedef C key_compare; 73 | typedef A allocator_type; 74 | typedef typename A::reference reference; 75 | typedef typename A::const_reference const_reference; 76 | typedef typename base_type::iterator iterator; 77 | typedef typename base_type::const_iterator const_iterator; 78 | typedef typename base_type::size_type size_type; 79 | typedef typename base_type::difference_type difference_type; 80 | typedef typename A::pointer pointer; 81 | typedef typename A::const_pointer const_pointer; 82 | typedef typename base_type::reverse_iterator reverse_iterator; 83 | typedef typename base_type::const_reverse_iterator const_reverse_iterator; 84 | 85 | typedef flat_map self; 86 | 87 | class value_compare 88 | : public std::binary_function, 89 | private key_compare 90 | { 91 | friend class flat_map; 92 | 93 | protected: 94 | value_compare(key_compare pred) 95 | : key_compare(pred) 96 | { 97 | } 98 | 99 | public: 100 | bool operator()(const value_type& lhs, const value_type& rhs) const 101 | { 102 | return key_compare::operator()(lhs.first, rhs.first); 103 | } 104 | }; 105 | 106 | explicit flat_map(const key_compare& comp = key_compare(), 107 | const A& alloc = A()) 108 | : base_type(alloc) 109 | , compare_type(comp) 110 | { 111 | } 112 | 113 | template 114 | flat_map(InputIterator first, InputIterator last, 115 | const key_compare& comp = key_compare(), const A& alloc = A()) 116 | : base_type(first, last, alloc) 117 | , compare_type(comp) 118 | { 119 | compare_type& me = *this; 120 | std::sort(begin(), end(), me); 121 | } 122 | 123 | flat_map(self&& m) 124 | : base_type(std::move(m)) 125 | { 126 | } 127 | 128 | flat_map(const self& m) 129 | : base_type(m) 130 | { 131 | } 132 | 133 | flat_map& operator=(const flat_map& rhs) 134 | { 135 | base_type::operator=(rhs); 136 | return *this; 137 | } 138 | 139 | flat_map& operator=(flat_map&& rhs) 140 | { 141 | base_type::operator=(std::move(rhs)); 142 | return *this; 143 | } 144 | 145 | iterator begin() { return base_type::begin(); } 146 | const_iterator begin() const { return base_type::begin(); } 147 | iterator end() { return base_type::end(); } 148 | const_iterator end() const { return base_type::end(); } 149 | reverse_iterator rbegin() { return base_type::rbegin(); } 150 | const_reverse_iterator rbegin() const { return base_type::rbegin(); } 151 | reverse_iterator rend() { return base_type::rend(); } 152 | const_reverse_iterator rend() const { return base_type::rend(); } 153 | 154 | bool empty() const { return base_type::empty(); } 155 | size_type size() const { return base_type::size(); } 156 | size_type max_size() { return base_type::max_size(); } 157 | 158 | mapped_type& operator[](const key_type& key) 159 | { 160 | return insert(value_type(key, mapped_type())).first->second; 161 | } 162 | 163 | std::pair insert(const value_type& val) 164 | { 165 | bool new_element = false; 166 | iterator i = lower_bound(val.first); 167 | 168 | if (i == end() || this->operator()(val.first, i->first)) { 169 | i = base_type::insert(i, val); 170 | new_element = true; 171 | } 172 | return std::make_pair(i, new_element); 173 | } 174 | 175 | std::pair insert(value_type&& val) 176 | { 177 | bool new_element = false; 178 | iterator i = lower_bound(val.first); 179 | 180 | if (i == end() || this->operator()(val.first, i->first)) { 181 | i = base_type::insert(i, std::move(val)); 182 | new_element = true; 183 | } 184 | return std::make_pair(i, new_element); 185 | } 186 | 187 | iterator insert(iterator pos, const value_type& val) 188 | { 189 | if ((pos == begin() || this->operator()(*(pos - 1), val)) 190 | && (pos == end() || this->operator()(val, *pos))) { 191 | return base_type::insert(pos, val); 192 | } 193 | return insert(val).first; 194 | } 195 | 196 | template 197 | void insert(InputIterator first, InputIterator last) 198 | { 199 | for (; first != last; ++first) 200 | insert(*first); 201 | } 202 | 203 | void erase(iterator pos) { base_type::erase(pos); } 204 | 205 | size_type erase(const key_type& k) 206 | { 207 | iterator i = find(k); 208 | if (i == end()) 209 | return 0; 210 | 211 | erase(i); 212 | return 1; 213 | } 214 | 215 | void erase(iterator first, iterator last) 216 | { 217 | base_type::erase(first, last); 218 | } 219 | 220 | void swap(flat_map& other) 221 | { 222 | base_type::swap(other); 223 | compare_type& me = *this; 224 | compare_type& rhs = other; 225 | std::swap(me, rhs); 226 | } 227 | 228 | void clear() { base_type::clear(); } 229 | 230 | key_compare key_comp() const { return *this; } 231 | 232 | value_compare value_comp() const 233 | { 234 | const key_compare& comp = *this; 235 | return value_compare(comp); 236 | } 237 | 238 | iterator find(const key_type& k) 239 | { 240 | iterator i(lower_bound(k)); 241 | if (i != end() && this->operator()(k, i->first)) { 242 | i = end(); 243 | } 244 | return i; 245 | } 246 | 247 | const_iterator find(const key_type& k) const 248 | { 249 | const_iterator i(lower_bound(k)); 250 | if (i != end() && this->operator()(k, i->first)) { 251 | i = end(); 252 | } 253 | return i; 254 | } 255 | 256 | size_type count(const key_type& k) const { return find(k) != end(); } 257 | 258 | iterator lower_bound(const key_type& k) 259 | { 260 | compare_type& me = *this; 261 | return std::lower_bound(begin(), end(), k, me); 262 | } 263 | 264 | const_iterator lower_bound(const key_type& k) const 265 | { 266 | const compare_type& me = *this; 267 | return std::lower_bound(begin(), end(), k, me); 268 | } 269 | 270 | iterator upper_bound(const key_type& k) 271 | { 272 | compare_type& me = *this; 273 | return std::upper_bound(begin(), end(), k, me); 274 | } 275 | 276 | const_iterator upper_bound(const key_type& k) const 277 | { 278 | const compare_type& me = *this; 279 | return std::upper_bound(begin(), end(), k, me); 280 | } 281 | 282 | std::pair equal_range(const key_type& k) 283 | { 284 | compare_type& me = *this; 285 | return std::equal_range(begin(), end(), k, me); 286 | } 287 | 288 | std::pair 289 | equal_range(const key_type& k) const 290 | { 291 | const compare_type& me(*this); 292 | return std::equal_range(begin(), end(), k, me); 293 | } 294 | 295 | template 296 | friend bool operator==(const flat_map& lhs, 297 | const flat_map& rhs); 298 | 299 | bool operator<(const flat_map& rhs) const 300 | { 301 | const base_type& me(*this); 302 | const base_type& yo(rhs); 303 | return me < yo; 304 | } 305 | 306 | template 307 | friend bool operator!=(const flat_map& lhs, 308 | const flat_map& rhs); 309 | 310 | template 311 | friend bool operator>(const flat_map& lhs, 312 | const flat_map& rhs); 313 | 314 | template 315 | friend bool operator>=(const flat_map& lhs, 316 | const flat_map& rhs); 317 | 318 | template 319 | friend bool operator<=(const flat_map& lhs, 320 | const flat_map& rhs); 321 | }; 322 | 323 | template 324 | inline bool operator==(const flat_map& lhs, 325 | const flat_map& rhs) 326 | { 327 | const std::vector, A>& me(lhs); 328 | return me == rhs; 329 | } 330 | 331 | template 332 | inline bool operator!=(const flat_map& lhs, 333 | const flat_map& rhs) 334 | { 335 | return !(lhs == rhs); 336 | } 337 | 338 | template 339 | inline bool operator>(const flat_map& lhs, 340 | const flat_map& rhs) 341 | { 342 | return rhs < lhs; 343 | } 344 | 345 | template 346 | inline bool operator>=(const flat_map& lhs, 347 | const flat_map& rhs) 348 | { 349 | return !(lhs < rhs); 350 | } 351 | 352 | template 353 | inline bool operator<=(const flat_map& lhs, 354 | const flat_map& rhs) 355 | { 356 | return !(rhs < lhs); 357 | } 358 | 359 | template 360 | void swap(flat_map& lhs, flat_map& rhs) 361 | { 362 | lhs.swap(rhs); 363 | } 364 | 365 | } // namespace rhea 366 | -------------------------------------------------------------------------------- /rhea/float_variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file float_variable.hpp 3 | /// \brief A floating point variable that can be used in an expression 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "abstract_variable.hpp" 10 | 11 | namespace rhea 12 | { 13 | 14 | /** A plain-old-datatype variable. */ 15 | template 16 | class pod_variable : public abstract_variable 17 | { 18 | typedef abstract_variable super; 19 | 20 | public: 21 | pod_variable(T value) 22 | : abstract_variable{} 23 | , value_{value} 24 | { 25 | } 26 | 27 | virtual ~pod_variable() {} 28 | 29 | virtual bool is_dummy() const { return false; } 30 | virtual bool is_external() const { return true; } 31 | virtual bool is_pivotable() const { return false; } 32 | virtual bool is_restricted() const { return false; } 33 | 34 | virtual void set_value(T new_value) { value_ = new_value; } 35 | 36 | virtual void change_value(T new_value) { value_ = new_value; } 37 | 38 | virtual std::string to_string() const { return "var"; } 39 | 40 | protected: 41 | T value_; 42 | }; 43 | 44 | /** A floating-point variable. */ 45 | class float_variable : public pod_variable 46 | { 47 | public: 48 | float_variable() 49 | : pod_variable{0.0} 50 | { 51 | } 52 | 53 | float_variable(double value) 54 | : pod_variable{value} 55 | { 56 | } 57 | 58 | virtual ~float_variable() {} 59 | 60 | virtual bool is_float() const { return true; } 61 | 62 | virtual double value() const { return value_; } 63 | 64 | virtual int int_value() const 65 | { 66 | return static_cast(value_ + (value_ > 0.0 ? 0.5 : -0.5)); 67 | } 68 | }; 69 | 70 | } // namespace rhea 71 | -------------------------------------------------------------------------------- /rhea/iostream.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file iostream.hpp 3 | /// \brief Standard Library iostream support for variables 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "float_variable.hpp" 14 | #include "linear_expression.hpp" 15 | #include "tableau.hpp" 16 | #include "strength.hpp" 17 | #include "constraint.hpp" 18 | 19 | namespace std 20 | { 21 | 22 | inline ostream& operator<<(ostream& str, const rhea::variable& v) 23 | { 24 | return v.is_nil() 25 | ? str << "NIL" 26 | : str << "{" << v.to_string() << v.id() << ":" << v.value() << "}"; 27 | } 28 | 29 | inline ostream& operator<<(ostream& str, const rhea::linear_expression& v) 30 | { 31 | for (auto& t : v.terms()) 32 | str << t.first << "*" << t.second << " + "; 33 | 34 | return str << v.constant(); 35 | } 36 | 37 | inline ostream& operator<<(ostream& str, const rhea::strength& s) 38 | { 39 | return 40 | s == rhea::strength::required() ? str << "required" : 41 | s == rhea::strength::strong() ? str << "strong" : 42 | s == rhea::strength::medium() ? str << "medium" : 43 | s == rhea::strength::weak() ? str << "weak" : 44 | /* else */ str << s.weight().as_double(); 45 | } 46 | 47 | inline ostream& operator<<(ostream& str, const rhea::abstract_constraint& c) 48 | { 49 | return str 50 | << (c.is_edit_constraint() ? "edit" : 51 | c.is_stay_constraint() ? "stay" : 52 | /* else */ "linear") 53 | << " [" << c.get_strength() << ", " << c.weight() << "] " 54 | << c.expression() 55 | << (c.is_inequality() ? " >= " : 56 | /* else */ " == ") 57 | << "0"; 58 | } 59 | 60 | inline ostream& operator<<(ostream& str, const rhea::constraint& c) 61 | { 62 | if (c.is_nil()) 63 | str << "NIL"; 64 | else 65 | str << c.as(); 66 | 67 | return str; 68 | } 69 | 70 | inline ostream& operator<<(ostream& str, const rhea::tableau& v) 71 | { 72 | str << "Tableau columns" << std::endl; 73 | for (auto& col : v.columns()) { 74 | str << " " << col.first << " : "; 75 | for (auto& var : col.second) 76 | str << var << " "; 77 | 78 | str << std::endl; 79 | } 80 | 81 | str << "Tableau rows" << std::endl; 82 | for (auto& row : v.rows()) 83 | str << " " << row.first << " : " << row.second << std::endl; 84 | 85 | return str; 86 | } 87 | 88 | } // namespace std 89 | 90 | -------------------------------------------------------------------------------- /rhea/linear_constraint.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file linear_constraint.hpp 3 | /// \brief Linear constraint 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "abstract_constraint.hpp" 10 | #include "linear_expression.hpp" 11 | 12 | namespace rhea 13 | { 14 | 15 | /** A constraint based on a linear expression. 16 | * Used as a base class for linear_equation and linear_inequality. */ 17 | class linear_constraint : public abstract_constraint 18 | { 19 | public: 20 | linear_constraint(const linear_expression& expr = linear_expression(), 21 | const strength& s = strength::required(), 22 | double weight = 1.0) 23 | : abstract_constraint{s, weight} 24 | , expr_{expr} 25 | { 26 | } 27 | 28 | virtual ~linear_constraint() {} 29 | 30 | linear_expression expression() const { return expr_; } 31 | 32 | void change_constant(double c) { expr_.set_constant(c); } 33 | 34 | protected: 35 | linear_expression expr_; 36 | }; 37 | 38 | } // namespace rhea 39 | -------------------------------------------------------------------------------- /rhea/linear_equation.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file linear_equation.hpp 3 | /// \brief A linear equation constraint 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "linear_constraint.hpp" 10 | #include "linear_expression.hpp" 11 | 12 | namespace rhea 13 | { 14 | 15 | /** A constraint of the form \f$expr = 0\f$. */ 16 | class linear_equation : public linear_constraint 17 | { 18 | public: 19 | /** Create a new constraint of the form \f$e = 0\f$. 20 | * \param e The expression 21 | * \param s The constraint's strength 22 | * \param weight The constraint's weight */ 23 | linear_equation(linear_expression e = linear_expression(0.0), 24 | const strength& s = strength::required(), 25 | double weight = 1.0) 26 | : linear_constraint{std::move(e), s, weight} 27 | { 28 | } 29 | 30 | /** Create a new constraint of the form \f$e = v\f$, or rather 31 | ** \f$e - v = 0\f$. 32 | * \param v The variable 33 | * \param e The expression 34 | * \param s The constraint's strength 35 | * \param weight The constraint's weight */ 36 | linear_equation(const variable& v, linear_expression e, 37 | const strength& s = strength::required(), 38 | double weight = 1.0) 39 | : linear_constraint{std::move(e), s, weight} 40 | { 41 | expr_.set(v, -1); 42 | } 43 | 44 | /** Create a new constraint of the form \f$e = v\f$, or rather 45 | ** \f$e - v = 0\f$. 46 | * \param v The variable 47 | * \param e The expression 48 | * \param s The constraint's strength 49 | * \param weight The constraint's weight */ 50 | linear_equation(linear_expression e, const variable& v, 51 | const strength& s = strength::required(), 52 | double weight = 1.0) 53 | : linear_constraint{std::move(e), s, weight} 54 | { 55 | expr_.set(v, -1); 56 | } 57 | 58 | /** Create a new constraint of the form \f$e_a = e_b\f$, or rather 59 | ** \f$e_a - e_b = 0\f$. 60 | * \param lhs The expression \f$e_a\f$ 61 | * \param rhs The expression \f$e_b\f$ 62 | * \param s The constraint's strength 63 | * \param weight The constraint's weight */ 64 | linear_equation(linear_expression lhs, const linear_expression& rhs, 65 | strength s = strength::required(), double weight = 1.0) 66 | : linear_constraint{std::move(lhs), s, weight} 67 | { 68 | expr_ -= rhs; 69 | } 70 | 71 | virtual ~linear_equation() {} 72 | 73 | virtual bool is_satisfied() const { return expr_.evaluate() == 0.0; } 74 | }; 75 | 76 | //-------------------------------------------------------------------------- 77 | 78 | inline linear_equation operator==(linear_expression lhs, const variable& rhs) 79 | { 80 | return lhs -= rhs; 81 | } 82 | 83 | inline linear_equation operator==(linear_expression lhs, 84 | const linear_expression& rhs) 85 | { 86 | return lhs -= rhs; 87 | } 88 | 89 | inline linear_equation operator==(const variable& lhs, 90 | const linear_expression& rhs) 91 | { 92 | return rhs - lhs; 93 | } 94 | 95 | inline linear_equation operator==(const variable& lhs, const variable& rhs) 96 | { 97 | return linear_expression{lhs} -= rhs; 98 | } 99 | 100 | inline linear_equation operator==(const variable& lhs, double rhs) 101 | { 102 | return linear_expression{lhs, 1, -rhs}; 103 | } 104 | 105 | } // namespace rhea 106 | -------------------------------------------------------------------------------- /rhea/linear_expression.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // linear_expression.cpp 3 | // 4 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 5 | //--------------------------------------------------------------------------- 6 | #include "linear_expression.hpp" 7 | 8 | #include 9 | #include 10 | 11 | #include "approx.hpp" 12 | #include "tableau.hpp" 13 | 14 | namespace rhea 15 | { 16 | 17 | linear_expression::linear_expression(double constant) 18 | : constant_{constant} 19 | { 20 | } 21 | 22 | linear_expression::linear_expression(const variable& v, double mul, 23 | double constant) 24 | : constant_{constant} 25 | { 26 | terms_[v] = mul; 27 | } 28 | 29 | linear_expression& linear_expression::operator*=(double x) 30 | { 31 | constant_ *= x; 32 | for (auto& p : terms_) 33 | p.second *= x; 34 | 35 | return *this; 36 | } 37 | 38 | linear_expression& linear_expression::operator/=(double x) 39 | { 40 | constant_ /= x; 41 | for (auto& p : terms_) 42 | p.second /= x; 43 | 44 | return *this; 45 | } 46 | 47 | linear_expression& linear_expression::operator*=(const linear_expression& x) 48 | { 49 | if (is_constant()) 50 | return *this = x * constant(); 51 | 52 | if (!x.is_constant()) 53 | throw nonlinear_expression(); 54 | 55 | return operator*=(x.constant()); 56 | } 57 | 58 | linear_expression& linear_expression::operator/=(const linear_expression& x) 59 | { 60 | if (!x.is_constant()) 61 | throw nonlinear_expression(); 62 | 63 | return operator/=(x.constant()); 64 | } 65 | 66 | linear_expression& linear_expression::operator+=(const linear_expression& x) 67 | { 68 | constant_ += x.constant_; 69 | for (auto& p : x.terms_) 70 | operator+=(p); 71 | 72 | return *this; 73 | } 74 | 75 | linear_expression& linear_expression::operator+=(const term& x) 76 | { 77 | auto i = terms_.find(x.first); 78 | if (i == terms_.end()) { 79 | if (!near_zero(x.second)) 80 | terms_[x.first] = x.second; 81 | } else if (near_zero(i->second += x.second)) { 82 | terms_.erase(i); 83 | } 84 | return *this; 85 | } 86 | 87 | linear_expression& linear_expression::operator-=(const linear_expression& x) 88 | { 89 | constant_ -= x.constant_; 90 | for (auto& p : x.terms_) 91 | operator-=(p); 92 | 93 | return *this; 94 | } 95 | 96 | linear_expression& linear_expression::operator-=(const term& x) 97 | { 98 | auto i = terms_.find(x.first); 99 | if (i == terms_.end()) { 100 | if (!near_zero(x.second)) 101 | terms_[x.first] = -x.second; 102 | } else if (near_zero(i->second -= x.second)) { 103 | terms_.erase(i); 104 | } 105 | return *this; 106 | } 107 | 108 | linear_expression& linear_expression::add(const linear_expression& x, 109 | const variable& subject, 110 | tableau& solver) 111 | { 112 | constant_ += x.constant_; 113 | for (const auto& p : x.terms_) 114 | add(p.first, p.second, subject, solver); 115 | 116 | return *this; 117 | } 118 | 119 | linear_expression& linear_expression::add(const variable& v, double c, 120 | const variable& subject, 121 | tableau& solver) 122 | { 123 | auto i = terms_.find(v); 124 | if (i == terms_.end()) { 125 | if (!near_zero(c)) { 126 | terms_[v] = c; 127 | solver.note_added_variable(v, subject); 128 | } 129 | } else if (near_zero(i->second += c)) { 130 | terms_.erase(i); 131 | solver.note_removed_variable(v, subject); 132 | } 133 | 134 | return *this; 135 | } 136 | 137 | variable linear_expression::find_pivotable_variable() const 138 | { 139 | assert(!is_constant()); 140 | auto found = std::find_if( 141 | terms_.begin(), terms_.end(), 142 | [&](const value_type& x) { return x.first.is_pivotable(); }); 143 | 144 | return found == terms_.end() ? variable::nil_var() : found->first; 145 | } 146 | 147 | double linear_expression::evaluate() const 148 | { 149 | double result = constant_; 150 | for (const term& p : terms_) 151 | result += p.first.value() * p.second; 152 | 153 | return result; 154 | } 155 | 156 | double linear_expression::new_subject(const variable& subj) 157 | { 158 | auto i = terms_.find(subj); 159 | assert(i != terms_.end()); 160 | double reciprocal(1.0 / i->second); 161 | terms_.erase(i); 162 | operator*=(-reciprocal); 163 | 164 | return reciprocal; 165 | } 166 | 167 | void linear_expression::change_subject(const variable& old_subj, 168 | const variable& new_subj) 169 | { 170 | assert(!new_subj.is_nil()); 171 | if (old_subj.is(new_subj)) 172 | return; 173 | 174 | double tmp = new_subject(new_subj); 175 | terms_[old_subj] = tmp; 176 | } 177 | 178 | void linear_expression::substitute_out(const variable& var, 179 | const linear_expression& expr, 180 | const variable& subj, tableau& solver) 181 | { 182 | auto it = terms_.find(var); 183 | if (it == terms_.end()) { 184 | throw std::runtime_error( 185 | "substitute variable is not part of the expression"); 186 | } 187 | double multiplier = it->second; 188 | terms_.erase(it); 189 | 190 | if (near_zero(multiplier)) 191 | return; 192 | 193 | increment_constant(multiplier * expr.constant()); 194 | for (auto& p : expr.terms()) { 195 | const variable& v = p.first; 196 | double mc = multiplier * p.second; 197 | 198 | auto oc = terms_.find(v); 199 | if (oc != terms_.end()) { 200 | if (near_zero(oc->second += mc)) { 201 | solver.note_removed_variable(oc->first, subj); 202 | terms_.erase(oc); 203 | } 204 | } else { 205 | terms_[v] = mc; 206 | solver.note_added_variable(v, subj); 207 | } 208 | } 209 | } 210 | 211 | } // namespace rhea 212 | -------------------------------------------------------------------------------- /rhea/linear_expression.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file linear_expression.hpp 3 | /// \brief A linear expression 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "flat_map.hpp" 10 | #include "approx.hpp" 11 | #include "variable.hpp" 12 | 13 | namespace rhea 14 | { 15 | 16 | class tableau; 17 | 18 | /** Linear expression. 19 | * Expressions have the form \f$av_0 + bv_1 + \ldots + c\f$, where \f$v_n\f$ 20 | * is a variable, \f$a, b, \ldots{}\f$ are non-zero coefficients, and 21 | * \f$c\f$ is a constant. 22 | * 23 | * Expressions can be built from variables in a natural way: 24 | * 25 | * \code 26 | 27 | variable x (3), y (5); 28 | auto expr (x * 5 + y + 2); 29 | expr.evaluate(); // Returns '22' 30 | 31 | * \endcode 32 | */ 33 | class linear_expression 34 | { 35 | public: 36 | // It would be nice to use an unordered_map here, but it appears 37 | // the algorithm is sensitive to the order in which the terms are 38 | // iterated. (Github issue #16.) 39 | typedef flat_map terms_map; 40 | 41 | typedef terms_map::value_type value_type; 42 | typedef terms_map::value_type term; 43 | 44 | public: 45 | linear_expression(double num = 0); 46 | 47 | linear_expression(const variable& clv, double value = 1, 48 | double constant = 0); 49 | 50 | linear_expression& operator*=(double x); 51 | linear_expression& operator/=(double x); 52 | linear_expression& operator*=(const linear_expression& x); 53 | linear_expression& operator/=(const linear_expression& x); 54 | linear_expression& operator+=(const linear_expression& x); 55 | linear_expression& operator-=(const linear_expression& x); 56 | linear_expression& operator+=(const term& x); 57 | linear_expression& operator-=(const term& x); 58 | 59 | linear_expression& operator+=(const variable& x) 60 | { 61 | return operator+=(term(x, 1)); 62 | } 63 | 64 | linear_expression& operator-=(const variable& x) 65 | { 66 | return operator+=(term(x, -1)); 67 | } 68 | 69 | linear_expression& operator+=(double x) 70 | { 71 | constant_ += x; 72 | return *this; 73 | } 74 | 75 | linear_expression& operator-=(double x) 76 | { 77 | constant_ -= x; 78 | return *this; 79 | } 80 | 81 | linear_expression& set(const variable& v, double x) 82 | { 83 | if (!near_zero(x)) 84 | terms_[v] = x; 85 | return *this; 86 | } 87 | 88 | /** Add \a expr to this expression. 89 | * Notifies the solver if a variable is added or deleted from this 90 | * expression. */ 91 | linear_expression& add(const linear_expression& expr, 92 | const variable& subject, tableau& solver); 93 | 94 | /** Add a term \f$c\cdot{}v\f$ to this expression. 95 | * If the expression already contains a term involving v, it adds c 96 | * to the existing coefficient. If the new coefficient is approximately 97 | * zero, v is removed from the expression. The solver is notified if 98 | * v is added or removed. 99 | * \param v The variable to be added 100 | * \param c The coefficient 101 | * \param subj The subject to report back to the solver 102 | * \param solver The solver that will be informed of any added or 103 | * removed variables */ 104 | linear_expression& add(const variable& v, double c, 105 | const variable& subject, tableau& solver); 106 | 107 | /** Erase a variable from the expression. */ 108 | void erase(const variable& v) { terms_.erase(v); } 109 | 110 | /** Return a pivotable variable. 111 | * \pre is_constant() == false 112 | * \throws internal_error 113 | * \return A pivotable variable, or variable::nil() if no variable 114 | * was found. */ 115 | variable find_pivotable_variable() const; 116 | 117 | /** Replace \a var with a symbolic expression that is equal to it. 118 | * If a variable has been added to this expression that wasn't there 119 | * before, or if a variable has been dropped from this expression 120 | * because it now has a coefficient of 0, inform the solver. 121 | * \param v The variable to be replaced 122 | * \param expr The expression to replace it with 123 | * \param subj The subject to report back to the solver 124 | * \param solver The solver that will be informed of any added or 125 | * removed variables */ 126 | void substitute_out(const variable& v, const linear_expression& expr, 127 | const variable& subj, tableau& solver); 128 | 129 | /** This linear expression currently represents the equation 130 | ** oldSubject=self, destructively modify it so that it represents 131 | ** the equation NewSubject=self. 132 | * Suppose this expression is \f$av_{new} + bv_0 + \ldots + c\f$. 133 | * Then, if the current equation is: 134 | * \f[v_{old} = av_{new} + bv_0 + \ldots + c\f] 135 | * The new equation will become: 136 | * \f[v_{new} = -\frac{1}{a}v_{old} - \frac{b}{a}v_0 - \ldots - 137 | *\frac{c}{a}\f] 138 | * 139 | * \pre \a new_subj has a nonzero coefficient in this expression. 140 | */ 141 | void change_subject(const variable& old_subj, const variable& new_subj); 142 | 143 | /** If this linear expression currently represents the equation 144 | ** \f$expr = 0\f$,destructively modify it so that 145 | ** \f$expr = subj\f$ represents an equivalent equation. 146 | * 147 | * Suppose this expression represents: 148 | * \f[av_{subject} + bv_0 + \ldots + xv_n + c = 0\f] 149 | * Then the modified expression will become: 150 | * \f[-\frac{b}{a}v_0 - \ldots - \frac{x}{a}v_n - \frac{c}{a}\f] 151 | * 152 | * \pre \a new_subj has a nonzero coefficient in this expression. 153 | * \param new_subj The new subject variable 154 | * \return The reciprocal */ 155 | double new_subject(const variable& new_subj); 156 | 157 | /** Evaluate the expression. 158 | * \return The result of the evaluation */ 159 | double evaluate() const; 160 | 161 | /** Get the coefficient of one of the terms. 162 | * \param v The variable to look for 163 | * \return The term's coefficient, or zero if \a v does not occur 164 | * in this expression */ 165 | double coefficient(const variable& v) const 166 | { 167 | auto i(terms_.find(v)); 168 | return i == terms_.end() ? 0.0 : i->second; 169 | } 170 | 171 | /** Get the constant \f$c\f$ of the expression. */ 172 | double constant() const { return constant_; } 173 | 174 | /** Set the constant \f$c\f$ to a new value. */ 175 | void set_constant(double c) { constant_ = c; } 176 | 177 | /** Add a given value to the constant \f$c\f$. */ 178 | void increment_constant(double c) { constant_ += c; } 179 | 180 | /** Get a map of all terms and their coefficients. */ 181 | const terms_map& terms() const { return terms_; } 182 | 183 | /** Returns true iff this expression is constant. */ 184 | bool is_constant() const { return terms_.empty(); } 185 | 186 | private: 187 | /** The expression's constant term. */ 188 | double constant_; 189 | /** A map of all variables and their coefficients. */ 190 | terms_map terms_; 191 | }; 192 | 193 | //-------------------------------------------------------------------------- 194 | 195 | inline linear_expression operator*(linear_expression e, double x) 196 | { 197 | return e *= x; 198 | } 199 | 200 | inline linear_expression operator*(double x, linear_expression e) 201 | { 202 | return e *= x; 203 | } 204 | 205 | inline linear_expression operator/(linear_expression e, double x) 206 | { 207 | return e /= x; 208 | } 209 | 210 | inline linear_expression operator*(linear_expression e, 211 | const linear_expression& x) 212 | { 213 | return e *= x; 214 | } 215 | 216 | inline linear_expression operator/(linear_expression e, 217 | const linear_expression& x) 218 | { 219 | return e /= x; 220 | } 221 | 222 | inline linear_expression operator+(linear_expression e, 223 | const linear_expression& x) 224 | { 225 | return e += x; 226 | } 227 | 228 | inline linear_expression operator-(linear_expression e, 229 | const linear_expression& x) 230 | { 231 | return e -= x; 232 | } 233 | 234 | //-------------------------------------------------------------------------- 235 | 236 | inline linear_expression operator*(const variable& v, double x) 237 | { 238 | return linear_expression(v, x); 239 | } 240 | 241 | inline linear_expression operator*(const variable& v, int x) 242 | { 243 | return linear_expression(v, x); 244 | } 245 | 246 | inline linear_expression operator*(double x, const variable& v) 247 | { 248 | return linear_expression(v, x); 249 | } 250 | 251 | inline linear_expression operator*(int x, const variable& v) 252 | { 253 | return linear_expression(v, x); 254 | } 255 | 256 | inline linear_expression operator/(const variable& v, double x) 257 | { 258 | return linear_expression(v, 1.0 / x); 259 | } 260 | 261 | inline linear_expression operator+(const variable& v, double x) 262 | { 263 | return linear_expression(v, 1, x); 264 | } 265 | 266 | inline linear_expression operator+(const variable& v, int x) 267 | { 268 | return linear_expression(v, 1, x); 269 | } 270 | 271 | inline linear_expression operator-(const variable& v, double x) 272 | { 273 | return linear_expression(v, 1, -x); 274 | } 275 | 276 | inline linear_expression operator-(const variable& v, int x) 277 | { 278 | return linear_expression(v, 1, -x); 279 | } 280 | 281 | inline linear_expression operator-(double x, const variable& v) 282 | { 283 | return linear_expression(v, -1, x); 284 | } 285 | 286 | inline linear_expression operator-(int x, const variable& v) 287 | { 288 | return linear_expression(v, -1, x); 289 | } 290 | 291 | inline linear_expression operator+(const variable& v, const variable& w) 292 | { 293 | return linear_expression(v) += w; 294 | } 295 | 296 | inline linear_expression operator-(const variable& v, const variable& w) 297 | { 298 | return linear_expression(v) -= w; 299 | } 300 | 301 | } // namespace rhea 302 | -------------------------------------------------------------------------------- /rhea/linear_inequality.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file linear_inequality.hpp 3 | /// \brief A linear inequality constraint 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "errors.hpp" 10 | #include "linear_constraint.hpp" 11 | #include "relation.hpp" 12 | 13 | namespace rhea 14 | { 15 | 16 | /** A constraint of the form \f$expr \geq 0\f$. */ 17 | class linear_inequality : public linear_constraint 18 | { 19 | public: 20 | linear_inequality() 21 | : linear_constraint{0.0, strength::required(), 1.0} 22 | { 23 | } 24 | 25 | linear_inequality(linear_expression expr, 26 | strength s = strength::required(), double weight = 1.0) 27 | : linear_constraint{std::move(expr), s, weight} 28 | { 29 | } 30 | 31 | linear_inequality(const variable& v, relation op, linear_expression expr, 32 | strength s = strength::required(), double weight = 1.0) 33 | : linear_constraint{std::move(expr), s, weight} 34 | { 35 | switch (op.type()) { 36 | case relation::geq: 37 | expr_ *= -1; 38 | expr_ += v; 39 | break; 40 | 41 | case relation::leq: 42 | expr_ -= v; 43 | break; 44 | 45 | default: 46 | throw edit_misuse(); // LCOV_EXCL_LINE 47 | }; 48 | } 49 | 50 | linear_inequality(linear_expression lhs, relation op, 51 | linear_expression rhs, strength s = strength::required(), 52 | double weight = 1.0) 53 | : linear_constraint{std::move(rhs), s, weight} 54 | { 55 | switch (op.type()) { 56 | case relation::geq: 57 | expr_ *= -1.0; 58 | expr_ += lhs; 59 | break; 60 | 61 | case relation::leq: 62 | expr_ -= lhs; 63 | break; 64 | 65 | default: 66 | throw edit_misuse(); // LCOV_EXCL_LINE 67 | }; 68 | } 69 | 70 | virtual ~linear_inequality() {} 71 | 72 | virtual bool is_inequality() const { return true; } 73 | 74 | virtual bool is_satisfied() const { return expr_.evaluate() >= 0; } 75 | }; 76 | 77 | //------------------------------------------------------------------------- 78 | 79 | inline linear_inequality operator<=(const linear_expression& lhs, 80 | const linear_expression& rhs) 81 | { 82 | return linear_inequality(lhs, relation::leq, rhs); 83 | } 84 | 85 | inline linear_inequality operator>=(const linear_expression& lhs, 86 | const linear_expression& rhs) 87 | { 88 | return linear_inequality(lhs, relation::geq, rhs); 89 | } 90 | 91 | //------------------------------------------------------------------------- 92 | 93 | inline linear_inequality operator<=(const variable& lhs, 94 | const linear_expression& rhs) 95 | { 96 | return linear_inequality(lhs, relation::leq, rhs); 97 | } 98 | 99 | inline linear_inequality operator>=(const variable& lhs, 100 | const linear_expression& rhs) 101 | { 102 | return linear_inequality(lhs, relation::geq, rhs); 103 | } 104 | 105 | //------------------------------------------------------------------------- 106 | 107 | inline linear_inequality operator<=(const variable& lhs, const variable& rhs) 108 | { 109 | return linear_inequality(lhs, relation::leq, rhs); 110 | } 111 | 112 | inline linear_inequality operator>=(const variable& lhs, const variable& rhs) 113 | { 114 | return linear_inequality(lhs, relation::geq, rhs); 115 | } 116 | 117 | //------------------------------------------------------------------------- 118 | 119 | inline linear_inequality operator<=(const variable& lhs, double rhs) 120 | { 121 | return linear_inequality(lhs, relation::leq, rhs); 122 | } 123 | 124 | inline linear_inequality operator>=(const variable& lhs, double rhs) 125 | { 126 | return linear_inequality(lhs, relation::geq, rhs); 127 | } 128 | 129 | //------------------------------------------------------------------------- 130 | 131 | inline linear_inequality operator<=(const variable& lhs, int rhs) 132 | { 133 | return linear_inequality(lhs, relation::leq, rhs); 134 | } 135 | 136 | inline linear_inequality operator>=(const variable& lhs, int rhs) 137 | { 138 | return linear_inequality(lhs, relation::geq, rhs); 139 | } 140 | 141 | } // namespace rhea 142 | -------------------------------------------------------------------------------- /rhea/link_variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file link_variable.hpp 3 | /// \brief An external variable that can be used in an expression 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "float_variable.hpp" 10 | 11 | namespace rhea 12 | { 13 | 14 | /** A link to an external variable. 15 | * It is up to you to make sure the linked variable isn't destroyed while the 16 | * solver is still using it. 17 | */ 18 | template 19 | class link_variable : public abstract_variable 20 | { 21 | typedef abstract_variable super; 22 | 23 | public: 24 | link_variable(T& value) 25 | : abstract_variable{} 26 | , value_{value} 27 | { 28 | } 29 | 30 | virtual ~link_variable() {} 31 | 32 | virtual bool is_dummy() const { return false; } 33 | virtual bool is_external() const { return true; } 34 | virtual bool is_pivotable() const { return false; } 35 | virtual bool is_restricted() const { return false; } 36 | virtual bool is_float() const { return true; } 37 | 38 | virtual void set_value(double new_value) 39 | { 40 | value_ = static_cast(new_value); 41 | } 42 | virtual void change_value(double new_value) 43 | { 44 | value_ = static_cast(new_value); 45 | } 46 | 47 | virtual double value() const { return static_cast(value_); } 48 | 49 | virtual int int_value() const 50 | { 51 | return static_cast(value_ + (value_ > T(0.0) ? T(0.5) : T(-0.5))); 52 | } 53 | 54 | virtual std::string to_string() const { return "link"; } 55 | 56 | protected: 57 | T& value_; 58 | }; 59 | 60 | /** A link to an external integer. 61 | * It is up to you to make sure the linked integer isn't destroyed while the 62 | * solver is still using it. The library will keep an internal double for 63 | * the calculations, and update the linked integer with its rounded value. 64 | */ 65 | class link_int : public float_variable 66 | { 67 | typedef abstract_variable super; 68 | 69 | public: 70 | link_int(int& value) 71 | : float_variable{static_cast(value)} 72 | , ivalue_{value} 73 | { 74 | } 75 | 76 | virtual ~link_int() {} 77 | 78 | virtual void set_value(double new_value) 79 | { 80 | value_ = new_value; 81 | ivalue_ = int_value(); 82 | } 83 | 84 | virtual void change_value(double new_value) { set_value(new_value); } 85 | 86 | protected: 87 | int& ivalue_; 88 | }; 89 | 90 | } // namespace rhea 91 | -------------------------------------------------------------------------------- /rhea/objective_variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file objective_variable.hpp 3 | /// \brief The objective for a solver to work towards 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "abstract_variable.hpp" 10 | #include "tableau.hpp" 11 | 12 | namespace rhea 13 | { 14 | 15 | /** A special variable that is used internally by the solver as the 16 | ** objective to solve for. */ 17 | class objective_variable : public abstract_variable 18 | { 19 | public: 20 | objective_variable() {} 21 | 22 | virtual ~objective_variable() {} 23 | 24 | virtual bool is_pivotable() const { return false; } 25 | virtual bool is_restricted() const { return false; } 26 | 27 | std::string to_string() const { return "objective"; } 28 | }; 29 | 30 | } // namespace rhea 31 | -------------------------------------------------------------------------------- /rhea/relation.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file relation.hpp 3 | /// \brief Relation between two sides in an equation or inequality 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | namespace rhea 10 | { 11 | 12 | /** An (in)equality relation. */ 13 | class relation 14 | { 15 | public: 16 | /** This enumeration is set up in such a way that additive inverse flips 17 | ** the direction of the inequality. */ 18 | typedef enum { 19 | /** Equal to. */ 20 | eq = 0, 21 | /** Less than or equal to. */ 22 | leq = 1, 23 | /** Greater than or equal to. */ 24 | geq = -1, 25 | } comp_t; 26 | 27 | public: 28 | relation(comp_t type = eq) 29 | : type_(type) 30 | { 31 | } 32 | 33 | comp_t type() const { return type_; } 34 | 35 | relation reverse_inequality() const { return comp_t(-(int)type_); } 36 | 37 | std::string to_string() const 38 | { 39 | switch (type_) { 40 | case eq: 41 | return "=="; 42 | case leq: 43 | return "<="; 44 | case geq: 45 | return ">="; 46 | default: 47 | assert(false); 48 | return "?"; 49 | } 50 | } 51 | 52 | bool operator==(comp_t c) const { return type_ == c; } 53 | 54 | private: 55 | comp_t type_; 56 | }; 57 | 58 | } // namespace rhea 59 | -------------------------------------------------------------------------------- /rhea/simplex_solver.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // simplex_solver.cpp 3 | // 4 | // Copyright 2012-2015, nocte@hippie.nu Released under the MIT License. 5 | //--------------------------------------------------------------------------- 6 | #include "simplex_solver.hpp" 7 | 8 | #include 9 | #include 10 | 11 | #include "errors_expl.hpp" 12 | #include "slack_variable.hpp" 13 | #include "dummy_variable.hpp" 14 | 15 | namespace rhea 16 | { 17 | 18 | simplex_solver::simplex_solver() 19 | : solver() 20 | , objective_(std::make_shared()) 21 | , auto_reset_stay_constants_(true) 22 | , needs_solving_(false) 23 | , explain_failure_(false) 24 | { 25 | rows_[objective_]; // Create an empty row for the objective 26 | cedcns_.push(0); 27 | } 28 | 29 | template 30 | void remove_from_container_if(container& c, func pred) 31 | { 32 | c.erase(std::remove_if(c.begin(), c.end(), pred)); 33 | } 34 | 35 | simplex_solver::expression_result 36 | simplex_solver::make_expression(const constraint& c) 37 | { 38 | expression_result result; 39 | 40 | auto& expr = result.expr; 41 | auto cexpr = c.expression(); 42 | expr.set_constant(cexpr.constant()); 43 | 44 | for (const auto& term : cexpr.terms()) { 45 | if (is_basic_var(term.first)) 46 | expr += row_expression(term.first) * term.second; 47 | else 48 | expr += term; 49 | } 50 | 51 | if (c.is_inequality()) { 52 | // cn is an inequality, so add a slack variable. The original 53 | // constraint is expr>=0, so that the resulting equality is 54 | // expr-slackVar=0. If cn is also non-required, add a negative 55 | // error variable, giving: 56 | // expr - slackVar = -errorVar 57 | // expr - slackVar + errorVar = 0. 58 | // Since both of these variables are newly created we can just add 59 | // them to the expression (they can't be basic). 60 | variable slack{std::make_shared()}; 61 | expr.set(slack, -1); 62 | marker_vars_[c] = slack; 63 | constraints_marked_[slack] = c; 64 | 65 | if (!c.is_required()) { 66 | variable eminus{std::make_shared()}; 67 | expr.set(eminus, 1); 68 | linear_expression& row = row_expression(objective_); 69 | double sw{c.adjusted_symbolic_weight()}; 70 | row += linear_expression::term(eminus, sw); 71 | error_vars_[c].insert(eminus); 72 | note_added_variable(eminus, objective_); 73 | } 74 | } else { 75 | // c is an equality 76 | if (c.is_required()) { 77 | // Add a dummy variable to the Expression to serve as a marker 78 | // for this constraint. The dummy variable is never allowed to 79 | // enter the basis when pivoting. 80 | variable dum{std::make_shared()}; 81 | 82 | if (c.is_stay_constraint()) { 83 | stay_plus_error_vars_.push_back(dum); 84 | stay_minus_error_vars_.push_back(dum); 85 | } else if (c.is_edit_constraint()) { 86 | result.previous_constant = c.expression().constant(); 87 | result.plus = dum; 88 | result.minus = dum; 89 | } 90 | 91 | expr.set(dum, 1); 92 | marker_vars_[c] = dum; 93 | constraints_marked_[dum] = c; 94 | } else { 95 | // cn is a non-required equality. Add a positive and a negative 96 | // error variable, making the resulting constraint 97 | // expr = eplus - eminus, 98 | // in other words: expr-eplus+eminus=0 99 | variable eplus{std::make_shared()}; 100 | variable eminus{std::make_shared()}; 101 | 102 | expr.set(eplus, -1); 103 | expr.set(eminus, 1); 104 | 105 | marker_vars_[c] = eplus; 106 | constraints_marked_[eplus] = c; 107 | 108 | auto& rowexp = row_expression(objective_); 109 | double coeff = c.adjusted_symbolic_weight(); 110 | 111 | rowexp.set(eplus, coeff); 112 | note_added_variable(eplus, objective_); 113 | error_vars_[c].insert(eplus); 114 | 115 | rowexp.set(eminus, coeff); 116 | note_added_variable(eminus, objective_); 117 | error_vars_[c].insert(eminus); 118 | 119 | if (c.is_stay_constraint()) { 120 | stay_plus_error_vars_.emplace_back(std::move(eplus)); 121 | stay_minus_error_vars_.emplace_back(std::move(eminus)); 122 | } else if (c.is_edit_constraint()) { 123 | result.plus = std::move(eplus); 124 | result.minus = std::move(eminus); 125 | result.previous_constant = c.expression().constant(); 126 | } 127 | } 128 | } 129 | 130 | // the Constant in the Expression should be non-negative. 131 | // If necessary normalize the Expression by multiplying by -1 132 | if (expr.constant() < 0) 133 | expr *= -1; 134 | 135 | return result; 136 | } 137 | 138 | solver& simplex_solver::add_constraint_(const constraint& c) 139 | { 140 | if (c.is_edit_constraint()) { 141 | auto& ec = c.as(); 142 | const auto& v = ec.var(); 143 | if (!v.is_external()) 144 | throw edit_misuse(v); 145 | } 146 | 147 | auto r = make_expression(c); 148 | 149 | bool added_ok_directly = false; 150 | try { 151 | added_ok_directly = try_adding_directly(r.expr); 152 | } catch (required_failure&) { 153 | remove_constraint_(c); 154 | throw; 155 | } 156 | 157 | if (!added_ok_directly) { 158 | auto result = add_with_artificial_variable(r.expr); 159 | if (!result.first) { 160 | remove_constraint_(c); 161 | throw required_failure_with_explanation(std::move(result.second)); 162 | } 163 | } 164 | 165 | needs_solving_ = true; 166 | 167 | if (c.is_edit_constraint()) { 168 | auto& ec = c.as(); 169 | edit_info_list_.emplace_back(ec.var(), c, r.plus, r.minus, 170 | r.previous_constant); 171 | } 172 | 173 | if (auto_solve_) 174 | solve_(); 175 | 176 | return *this; 177 | } 178 | 179 | solver& simplex_solver::remove_constraint_(const constraint& c) 180 | { 181 | needs_solving_ = true; 182 | reset_stay_constants(); 183 | 184 | auto& rowexpr = row_expression(objective_); 185 | auto i = error_vars_.find(c); 186 | if (i != error_vars_.end()) { 187 | for (const variable& var : i->second) { 188 | if (is_basic_var(var)) { 189 | const linear_expression& expr = row_expression(var); 190 | rowexpr.add(expr * -c.adjusted_symbolic_weight(), objective_, 191 | *this); 192 | } else { 193 | rowexpr.add(var, -c.adjusted_symbolic_weight(), objective_, 194 | *this); 195 | } 196 | } 197 | } 198 | 199 | auto im = marker_vars_.find(c); 200 | if (im == marker_vars_.end()) 201 | throw constraint_not_found(); 202 | 203 | variable marker{im->second}; 204 | marker_vars_.erase(im); 205 | constraints_marked_.erase(marker); 206 | 207 | if (!is_basic_var(marker)) { 208 | // Try to make this marker variable basic. 209 | auto& col = columns_[marker]; 210 | bool exit_var_set = false; 211 | double min_ratio = 0.0; 212 | variable exit_var{variable::nil_var()}; 213 | 214 | for (auto& v : col) { 215 | if (v.is_restricted()) { 216 | auto& expr = row_expression(v); 217 | double coeff = expr.coefficient(marker); 218 | 219 | if (coeff >= 0.0) 220 | continue; // Only consider negative coefficients 221 | 222 | double r = -expr.constant() / coeff; 223 | if (!exit_var_set || r < min_ratio) { 224 | min_ratio = r; 225 | exit_var = v; 226 | exit_var_set = true; 227 | } 228 | } 229 | } 230 | // If we didn't set exitvar above, then either the marker 231 | // variable has a positive coefficient in all equations, or it 232 | // only occurs in equations for unrestricted variables. If it 233 | // does occur in an equation for a restricted variable, pick the 234 | // equation that gives the smallest ratio. (The row with the 235 | // marker variable will become infeasible, but all the other rows 236 | // will still be feasible; and we will be dropping the row with 237 | // the marker variable. In effect we are removing the 238 | // non-negativity restriction on the marker variable.) 239 | if (!exit_var_set) { 240 | for (auto& v : col) { 241 | if (v.is_restricted()) { 242 | auto& expr = row_expression(v); 243 | double coeff = expr.coefficient(marker); 244 | double r = expr.constant() / coeff; 245 | 246 | if (!exit_var_set || r < min_ratio) { 247 | min_ratio = r; 248 | exit_var = v; 249 | exit_var_set = true; 250 | } 251 | } 252 | } 253 | } 254 | 255 | if (!exit_var_set) { 256 | // exitVar is still nil 257 | // If col is empty, then exitVar doesn't occur in any equations, 258 | // so just remove it. Otherwise pick an exit var from among the 259 | // unrestricted variables whose equation involves the marker var 260 | if (col.empty()) { 261 | remove_column(marker); 262 | } else { 263 | for (auto& v : col) { 264 | if (!v.is(objective_)) { 265 | exit_var = v; 266 | exit_var_set = true; 267 | break; 268 | } 269 | } 270 | } 271 | } 272 | 273 | if (exit_var_set) 274 | pivot(marker, exit_var); 275 | } 276 | 277 | if (is_basic_var(marker)) 278 | remove_row(marker); 279 | 280 | // Delete any error variables. If cn is an inequality, it also 281 | // contains a slack variable; but we use that as the marker variable 282 | // and so it has been deleted when we removed its row. 283 | if (i != error_vars_.end()) { 284 | for (const auto& v : i->second) { 285 | if (!v.is(marker)) 286 | remove_column(v); 287 | } 288 | } 289 | 290 | if (c.is_stay_constraint()) { 291 | if (i != error_vars_.end()) { 292 | auto pred = [&](variable x) { return i->second.count(x) > 0; }; 293 | remove_from_container_if(stay_plus_error_vars_, pred); 294 | remove_from_container_if(stay_minus_error_vars_, pred); 295 | } 296 | } else if (c.is_edit_constraint()) { 297 | auto ei = std::find(edit_info_list_.begin(), edit_info_list_.end(), 298 | c); 299 | assert(ei != edit_info_list_.end()); 300 | remove_column(ei->minus); 301 | // ei->plus is a marker and will be removed later 302 | edit_info_list_.erase(ei); 303 | } 304 | 305 | if (i != error_vars_.end()) 306 | error_vars_.erase(i); 307 | 308 | if (auto_solve_) 309 | solve_(); 310 | 311 | return *this; 312 | } 313 | 314 | void simplex_solver::resolve() 315 | { 316 | dual_optimize(); 317 | set_external_variables(); 318 | infeasible_rows_.clear(); 319 | if (auto_reset_stay_constants_) 320 | reset_stay_constants(); 321 | } 322 | 323 | simplex_solver& simplex_solver::suggest_value(const variable& v, double x) 324 | { 325 | auto ei = std::find(edit_info_list_.rbegin(), edit_info_list_.rend(), v); 326 | if (ei == edit_info_list_.rend()) 327 | throw edit_misuse(v); 328 | 329 | while (ei != edit_info_list_.rend()) { 330 | double delta{x - ei->prev_constant}; 331 | ei->prev_constant = x; 332 | delta_edit_constant(delta, ei->plus, ei->minus); 333 | ei = std::find(std::next(ei), edit_info_list_.rend(), v); 334 | } 335 | 336 | return *this; 337 | } 338 | 339 | simplex_solver& simplex_solver::suggest_value(const constraint& c, double x) 340 | { 341 | if (!c.is_edit_constraint()) { 342 | throw edit_misuse(); 343 | } 344 | auto& e = c.as(); 345 | auto ei = std::find(edit_info_list_.rbegin(), edit_info_list_.rend(), c); 346 | if (ei == edit_info_list_.rend()) 347 | throw edit_misuse(e.var()); 348 | 349 | double delta{x - ei->prev_constant}; 350 | ei->prev_constant = x; 351 | delta_edit_constant(delta, ei->plus, ei->minus); 352 | 353 | return *this; 354 | } 355 | 356 | simplex_solver& simplex_solver::suggest(const variable& v, double x) 357 | { 358 | add_edit_var(v); 359 | begin_edit(); 360 | suggest_value(v, x); 361 | end_edit(); 362 | return *this; 363 | } 364 | 365 | simplex_solver& 366 | simplex_solver::suggest(const std::list& suggestions) 367 | { 368 | for (auto& sugg : suggestions) 369 | add_edit_var(sugg.v); 370 | 371 | begin_edit(); 372 | for (auto& sugg : suggestions) 373 | suggest_value(sugg.v, sugg.suggested_value); 374 | 375 | end_edit(); 376 | return *this; 377 | } 378 | 379 | simplex_solver& simplex_solver::solve() 380 | { 381 | if (needs_solving_) 382 | solve_(); 383 | 384 | return *this; 385 | } 386 | 387 | void simplex_solver::solve_() 388 | { 389 | optimize(objective_); 390 | set_external_variables(); 391 | needs_solving_ = false; 392 | 393 | if (on_resolve) 394 | on_resolve(*this); 395 | } 396 | 397 | std::pair 398 | simplex_solver::add_with_artificial_variable(linear_expression& expr) 399 | { 400 | // The artificial objective is av, which we know is equal to expr 401 | // (which contains only parametric variables). 402 | variable av{std::make_shared()}; 403 | variable az{std::make_shared()}; 404 | linear_expression row{expr}; 405 | 406 | // Objective is treated as a row in the tableau, 407 | // so do the substitution for its value (we are minimizing 408 | // the artificial variable). 409 | // This row will be removed from the tableau after optimizing. 410 | add_row(az, row); 411 | 412 | // Add the normal row to the tableau -- when artifical 413 | // variable is minimized to 0 (if possible) 414 | // this row remains in the tableau to maintain the constraint 415 | // we are trying to add. 416 | add_row(av, expr); 417 | 418 | // Try to optimize az to 0. 419 | // Note we are *not* optimizing the real objective, but optimizing 420 | // the artificial objective to see if the error in the constraint 421 | // we are adding can be set to 0. 422 | optimize(az); 423 | 424 | // Careful, we want to get the Expression that is in 425 | // the tableau, not the one we initialized it with! 426 | auto& tableau_row = row_expression(az); 427 | 428 | // Check that we were able to make the objective value 0 429 | // If not, the original constraint was not satisfiable 430 | if (!near_zero(tableau_row.constant())) { 431 | constraint_list result; 432 | if (explain_failure_) 433 | result = build_explanation(az, tableau_row); 434 | 435 | return std::make_pair(false, result); 436 | } 437 | 438 | if (is_basic_var(av)) { 439 | const auto& e = row_expression(av); 440 | 441 | // Find another variable in this row and Pivot, so that av becomes 442 | // parametric 443 | // If there isn't another variable in the row then 444 | // the tableau contains the equation av = 0 -- just delete av's row 445 | if (e.is_constant()) { 446 | assert(near_zero(e.constant())); 447 | remove_row(av); 448 | return {true, constraint_list()}; 449 | } 450 | variable entry(e.find_pivotable_variable()); 451 | if (entry.is_nil()) { 452 | constraint_list result; 453 | if (explain_failure_) 454 | result = build_explanation(av, e); 455 | 456 | return {false, result}; 457 | } 458 | pivot(entry, av); 459 | } 460 | 461 | assert(is_parametric_var(av)); 462 | remove_column(av); 463 | remove_row(az); 464 | 465 | return {true, constraint_list()}; 466 | } 467 | 468 | simplex_solver& simplex_solver::remove_edit_vars_to(size_t n) 469 | { 470 | while (edit_info_list_.size() > n) { 471 | remove_edit_var(edit_info_list_.back().v); 472 | } 473 | 474 | return *this; 475 | } 476 | 477 | bool simplex_solver::try_adding_directly(linear_expression& expr) 478 | { 479 | variable subj{choose_subject(expr)}; 480 | if (subj.is_nil()) 481 | return false; 482 | 483 | expr.new_subject(subj); 484 | if (columns_has_key(subj)) 485 | substitute_out(subj, expr); 486 | 487 | add_row(subj, expr); 488 | return true; 489 | } 490 | 491 | simplex_solver& simplex_solver::remove_edit_var(const variable& v) 492 | { 493 | auto i = std::find(edit_info_list_.rbegin(), edit_info_list_.rend(), v); 494 | if (i == edit_info_list_.rend()) 495 | throw edit_misuse(v); 496 | 497 | remove_constraint(i->c); 498 | 499 | return *this; 500 | } 501 | 502 | variable simplex_solver::choose_subject(linear_expression& expr) 503 | { 504 | variable subj{variable::nil_var()}; 505 | bool found_unrestricted = false, found_new_restricted = false; 506 | 507 | for (auto& term : expr.terms()) { 508 | const variable& v = term.first; 509 | double c = term.second; 510 | 511 | if (found_unrestricted) { 512 | // We have already found an unrestricted variable. The only 513 | // time we will want to use v instead of the current choice 514 | // 'subject' is if v is unrestricted and new to the solver and 515 | // 'subject' isn't new. If this is the case just pick v 516 | // immediately and return. 517 | if (!v.is_restricted() && !columns_has_key(v)) 518 | return v; 519 | } else { 520 | if (v.is_restricted()) { 521 | // v is restricted. If we have already found a suitable 522 | // restricted variable just stick with that. Otherwise, if v 523 | // is new to the solver and has a negative coefficient pick 524 | // it. Regarding being new to the solver -- if the variable 525 | // occurs only in the objective function we regard it as being 526 | // new to the solver, since error variables are added to the 527 | // objective function when we make the Expression. We also 528 | // never pick a dummy variable here. 529 | if (!found_new_restricted && !v.is_dummy() && c < 0.0) { 530 | auto i(columns_.find(v)); 531 | if (i == columns_.end() 532 | || (columns_.size() == 1 533 | && columns_has_key(objective_))) { 534 | subj = v; 535 | found_new_restricted = true; 536 | } 537 | } 538 | } else { 539 | // v is unrestricted. 540 | // If v is also new to the solver just pick it now. 541 | subj = v; 542 | found_unrestricted = true; 543 | } 544 | } 545 | } 546 | 547 | if (!subj.is_nil()) 548 | return subj; 549 | 550 | // Make one last check -- if all of the variables in expr are dummy 551 | // variables, then we can pick a dummy variable as the subject. 552 | double coeff = 0.0; 553 | for (auto& term : expr.terms()) { 554 | const variable& v = term.first; 555 | if (!v.is_dummy()) 556 | return variable::nil_var(); // Nope, no luck. 557 | 558 | if (!columns_has_key(v)) { 559 | subj = v; 560 | coeff = term.second; 561 | } 562 | } 563 | 564 | // If we get this far, all of the variables in the expression should 565 | // be dummy variables. If the constant is nonzero we are trying to 566 | // add an unsatisfiable required constraint. (Remember that dummy 567 | // variables must take on a value of 0.) 568 | if (!near_zero(expr.constant())) 569 | throw required_failure(); 570 | 571 | // Otherwise, if the constant is zero, multiply by -1 if necessary to 572 | // make the coefficient for the subject negative. 573 | if (coeff > 0) 574 | expr *= -1; 575 | 576 | return subj; 577 | } 578 | 579 | void simplex_solver::optimize(const variable& v) 580 | { 581 | std::less ord; 582 | auto& row = row_expression(v); 583 | 584 | variable entry(variable::nil_var()), exit(variable::nil_var()); 585 | 586 | while (true) { 587 | // Find the most negative coefficient in the objective function 588 | // (ignoring the non-pivotable dummy variables). If all 589 | // coefficients are positive we're done. 590 | bool found_negative = false; 591 | for (auto& p : row.terms()) { 592 | const auto& var = p.first; 593 | if (var.is_pivotable() && p.second < 0.0) { 594 | entry = var; 595 | found_negative = true; 596 | break; 597 | } 598 | } 599 | 600 | // If all coefficients were positive (or if the objective 601 | // function has no pivotable variables) we are at an optimum. 602 | if (!found_negative) 603 | return; 604 | 605 | // Choose which variable to move out of the basis. 606 | // Only consider pivotable basic variables 607 | // (i.e. restricted, non-dummy variables). 608 | double min_ratio{std::numeric_limits::max()}; 609 | double r = 0.0; 610 | for (const variable& var : columns_[entry]) { 611 | if (var.is_pivotable()) { 612 | const auto& expr = row_expression(var); 613 | double coeff = expr.coefficient(entry); 614 | 615 | if (coeff >= 0) // Only consider negative coefficients 616 | continue; 617 | 618 | r = -expr.constant() / coeff; 619 | if (r < min_ratio || (approx(r, min_ratio) && ord(var, exit))) { 620 | min_ratio = r; 621 | exit = var; 622 | } 623 | } 624 | } 625 | 626 | // If minRatio is still nil at this point, it means that the 627 | // objective function is unbounded, i.e. it can become 628 | // arbitrarily negative. This should never happen in this 629 | // application. 630 | if (min_ratio == std::numeric_limits::max()) 631 | throw internal_error("objective function is unbounded"); 632 | 633 | pivot(entry, exit); 634 | } 635 | } 636 | 637 | void simplex_solver::delta_edit_constant(double delta, const variable& plus, 638 | const variable& minus) 639 | { 640 | // Check if the variables are basic 641 | if (is_basic_var(plus)) { 642 | auto& expr = row_expression(plus); 643 | expr.increment_constant(delta); 644 | if (expr.constant() < 0) 645 | infeasible_rows_.insert(plus); 646 | 647 | return; 648 | } 649 | if (is_basic_var(minus)) { 650 | auto& expr = row_expression(minus); 651 | expr.increment_constant(-delta); 652 | if (expr.constant() < 0) 653 | infeasible_rows_.insert(minus); 654 | 655 | return; 656 | } 657 | 658 | // Neither is basic. So they must both be nonbasic, and will both 659 | // occur in exactly the same expressions. Find all the expressions 660 | // in which they occur by finding the column for the minusErrorVar 661 | // (it doesn't matter whether we look for that one or for 662 | // plusErrorVar). Fix the constants in these expressions. 663 | 664 | for (auto& v : columns_[minus]) { 665 | auto& expr(row_expression(v)); 666 | expr.increment_constant(expr.coefficient(minus) * delta); 667 | 668 | if (v.is_restricted() && expr.constant() < 0) 669 | infeasible_rows_.insert(v); 670 | } 671 | } 672 | 673 | void simplex_solver::dual_optimize() 674 | { 675 | auto& row = row_expression(objective_); 676 | while (!infeasible_rows_.empty()) { 677 | auto ii = infeasible_rows_.begin(); 678 | variable exit_var{*ii}; 679 | infeasible_rows_.erase(ii); 680 | 681 | // exit_var might have become basic after some other pivoting 682 | // so allow for the case of its not being there any longer. 683 | if (!is_basic_var(exit_var)) 684 | continue; 685 | 686 | auto& expr = row_expression(exit_var); 687 | if (expr.constant() >= 0) 688 | continue; // Skip this row if it's feasible. 689 | 690 | double ratio = std::numeric_limits::max(); 691 | double r = 0.0; 692 | variable entry_var{variable::nil_var()}; 693 | 694 | for (auto& p : expr.terms()) { 695 | const variable& v = p.first; 696 | double c = p.second; 697 | if (c > 0 && v.is_pivotable()) { 698 | r = row.coefficient(v) / c; 699 | if (r < ratio) { 700 | entry_var = v; 701 | ratio = r; 702 | } 703 | } 704 | } 705 | 706 | if (ratio == std::numeric_limits::max()) 707 | throw internal_error("dual_optimize: no pivot found"); 708 | 709 | pivot(entry_var, exit_var); 710 | } 711 | } 712 | 713 | void simplex_solver::pivot(const variable& entry, const variable& exit) 714 | { 715 | // The entryVar might be non-pivotable if we're doing a RemoveConstraint -- 716 | // otherwise it should be a pivotable variable -- enforced at call sites, 717 | // hopefully 718 | 719 | // expr is the Expression for the exit variable (about to leave the basis) 720 | // -- 721 | // so that the old tableau includes the equation: 722 | // exitVar = expr 723 | auto expr = remove_row(exit); 724 | 725 | // Compute an Expression for the entry variable. Since expr has 726 | // been deleted from the tableau we can destructively modify it to 727 | // build this Expression. 728 | expr.change_subject(exit, entry); 729 | substitute_out(entry, expr); 730 | 731 | if (entry.is_external()) 732 | external_parametric_vars_.erase(entry); 733 | 734 | add_row(entry, expr); 735 | } 736 | 737 | void simplex_solver::reset_stay_constants() 738 | { 739 | auto ip = stay_plus_error_vars_.begin(); 740 | auto im = stay_minus_error_vars_.begin(); 741 | 742 | for (; ip != stay_plus_error_vars_.end(); ++ip, ++im) { 743 | assert(im != stay_minus_error_vars_.end()); 744 | if (is_basic_var(*ip)) 745 | row_expression(*ip).set_constant(0); 746 | if (is_basic_var(*im)) 747 | row_expression(*im).set_constant(0); 748 | } 749 | } 750 | 751 | void simplex_solver::set_external_variables() 752 | { 753 | // Set external parametric variables first 754 | // in case I've screwed up 755 | for (variable v : external_parametric_vars_) { 756 | if (is_basic_var(v)) { 757 | assert(false); 758 | } else { 759 | change(v, 0.0); 760 | } 761 | } 762 | 763 | // Only iterate over the rows w/ external variables 764 | for (variable v : external_rows_) 765 | change(v, row_expression(v).constant()); 766 | 767 | needs_solving_ = false; 768 | } 769 | 770 | bool simplex_solver::is_constraint_satisfied(const constraint& c) const 771 | { 772 | if (marker_vars_.count(c) == 0) 773 | throw constraint_not_found(); 774 | 775 | auto ie = error_vars_.find(c); 776 | if (ie != error_vars_.end()) { 777 | for (variable v : ie->second) { 778 | if (is_parametric_var(v)) 779 | continue; 780 | 781 | if (!near_zero(row_expression(v).constant())) 782 | return false; 783 | } 784 | } 785 | return true; 786 | } 787 | 788 | void simplex_solver::change_strength_and_weight(constraint c, 789 | const strength& s, 790 | double weight) 791 | { 792 | auto ie = error_vars_.find(c); 793 | if (ie == error_vars_.end()) 794 | return; 795 | 796 | double old_coeff = c.adjusted_symbolic_weight(); 797 | c.set_strength(s); 798 | c.set_weight(weight); 799 | double new_coeff = c.adjusted_symbolic_weight(); 800 | 801 | if (new_coeff == old_coeff) 802 | return; 803 | 804 | auto& row = row_expression(objective_); 805 | for (const variable& v : ie->second) { 806 | if (!is_basic_var(v)) { 807 | row.add(v, -old_coeff, objective_, *this); 808 | row.add(v, new_coeff, objective_, *this); 809 | } else { 810 | const linear_expression& expr(row_expression(v)); 811 | row.add(expr * -old_coeff, objective_, *this); 812 | row.add(expr * new_coeff, objective_, *this); 813 | } 814 | } 815 | needs_solving_ = true; 816 | 817 | if (auto_solve_) 818 | solve_(); 819 | } 820 | 821 | void simplex_solver::change_strength(constraint c, const strength& s) 822 | { 823 | change_strength_and_weight(c, s, c.weight()); 824 | } 825 | 826 | void simplex_solver::change_weight(constraint c, double weight) 827 | { 828 | change_strength_and_weight(c, c.get_strength(), weight); 829 | } 830 | 831 | simplex_solver& simplex_solver::begin_edit() 832 | { 833 | if (edit_info_list_.empty()) 834 | throw edit_misuse(); 835 | 836 | infeasible_rows_.clear(); 837 | reset_stay_constants(); 838 | cedcns_.push(edit_info_list_.size()); 839 | 840 | return *this; 841 | } 842 | 843 | simplex_solver& simplex_solver::end_edit() 844 | { 845 | if (edit_info_list_.empty()) 846 | throw edit_misuse(); 847 | 848 | resolve(); 849 | cedcns_.pop(); 850 | remove_edit_vars_to(cedcns_.top()); 851 | 852 | return *this; 853 | } 854 | 855 | constraint_list 856 | simplex_solver::build_explanation(const variable& v, 857 | const linear_expression& expr) const 858 | { 859 | constraint_list result; 860 | 861 | auto found = constraints_marked_.find(v); 862 | if (found != constraints_marked_.end()) 863 | result.push_back(found->second); 864 | 865 | for (auto& term : expr.terms()) { 866 | auto found2 = constraints_marked_.find(term.first); 867 | if (found2 != constraints_marked_.end()) 868 | result.push_back(found2->second); 869 | } 870 | 871 | return result; 872 | } 873 | 874 | } // namespace rhea 875 | -------------------------------------------------------------------------------- /rhea/simplex_solver.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file simplex_solver.hpp 3 | /// \brief Implementation of a solver using a simplex algorithm 4 | // 5 | // Copyright 2012-2015, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "edit_constraint.hpp" 15 | #include "linear_expression.hpp" 16 | #include "linear_inequality.hpp" 17 | #include "solver.hpp" 18 | #include "stay_constraint.hpp" 19 | #include "tableau.hpp" 20 | #include "objective_variable.hpp" 21 | 22 | namespace rhea 23 | { 24 | 25 | /** Solver that implements the Cassowary incremental simplex algorithm. */ 26 | class simplex_solver : public solver, public tableau 27 | { 28 | public: 29 | typedef std::function event_cb; 30 | typedef std::function variable_cb; 31 | 32 | /** Gets called whenever the tableau is resolved. */ 33 | event_cb on_resolve; 34 | /** Gets called whenever a variable has changed. */ 35 | variable_cb on_variable_change; 36 | 37 | public: 38 | /** This struct is used as a parameter for the suggest() function. */ 39 | struct suggestion 40 | { 41 | const variable& v; 42 | double suggested_value; 43 | }; 44 | 45 | public: 46 | simplex_solver(); 47 | 48 | virtual ~simplex_solver() {} 49 | 50 | /** Add an edit constraint for a given variable. 51 | * The application should call this for every variable it is planning 52 | * to suggest a new value for, before calling begin_edit(). */ 53 | simplex_solver& add_edit_var(const variable& v, 54 | const strength& s = strength::strong(), 55 | double weight = 1.0) 56 | { 57 | add_constraint(std::make_shared(v, s, weight)); 58 | return *this; 59 | } 60 | 61 | /** Begin suggesting new values for edit variables. 62 | * The application should call add_edit_var() first for every variable 63 | * it is planning to call suggest_value() for. In most cases, it is 64 | * more convenient to use suggest() instead. */ 65 | simplex_solver& begin_edit(); 66 | 67 | /** We're done with the edit variables, resolve the constraints. */ 68 | simplex_solver& end_edit(); 69 | 70 | simplex_solver& remove_edit_var(const variable& v); 71 | 72 | simplex_solver& remove_edit_vars_to(size_t n); 73 | 74 | simplex_solver& remove_all_edit_vars() { return remove_edit_vars_to(0); } 75 | 76 | void resolve(); 77 | 78 | /** Suggest a new value for an edit variable. 79 | * The variable needs to be added as an edit variable, 80 | * and begin_edit() needs to be called first. 81 | * The tableau will not be solved completely until 82 | * after resolve() or end_edit() has been called. */ 83 | simplex_solver& suggest_value(const variable& v, double x); 84 | 85 | /** Suggest a new value for an edit constraint. 86 | * The constraint needs to be an edit constraint and needs to 87 | * have been added before. The tableau will not be solved 88 | * completely until resolve() or end_edit() has been called. */ 89 | simplex_solver& suggest_value(const constraint& v, double x); 90 | 91 | /** Suggest a new value for a variables. 92 | * This function calls add_edit_variable(), begin_edit(), and 93 | * end_edit() as well. 94 | * \code 95 | solver.suggest(width, 200); 96 | * \endcode */ 97 | simplex_solver& suggest(const variable& v, double x); 98 | 99 | /** Suggest new values for a list of variables. 100 | * This function calls add_edit_variable(), begin_edit(), and 101 | * end_edit() as well. 102 | * \code 103 | solver.suggest({{ width, 200 }, { height, 150 }}); 104 | * \endcode */ 105 | simplex_solver& suggest(const std::list& suggestions); 106 | 107 | /** If autosolving has been turned off, client code needs to explicitly 108 | ** call this function before accessing variables values. */ 109 | simplex_solver& solve(); 110 | 111 | /** Check if the solver knows of a given variable. 112 | * \param v The variable to check for 113 | * \return True iff v is a column in the tableau or a basic variable */ 114 | bool contains_variable(const variable& v) 115 | { 116 | return columns_has_key(v) || is_basic_var(v); 117 | } 118 | 119 | /** Check if the solver knows of a given constraint. 120 | * \param c The constraint to check for 121 | * \return True iff c has been added to the solver */ 122 | bool contains_constraint(const constraint& c) 123 | { 124 | return marker_vars_.find(c) != marker_vars_.end(); 125 | } 126 | 127 | /** Check if this constraint was satisfied. */ 128 | bool is_constraint_satisfied(const constraint& c) const; 129 | 130 | /** Reset all external variables to their current values. 131 | * Note: this triggers all callbacks, which might be used to copy the 132 | * variable's value to another variable. */ 133 | void update_external_variables() { set_external_variables(); } 134 | 135 | void change_strength_and_weight(constraint c, const strength& s, 136 | double weight); 137 | void change_strength(constraint c, const strength& s); 138 | void change_weight(constraint c, double weight); 139 | 140 | /** Reset all stay constraint constants. 141 | * Each of the non-required stays will be represented by the equation 142 | * \f$v = v' + e_{plus} - e_{minus}\f$, where \f$v\f$ is the variable 143 | * associated with the stay, \f$v'\f$ is the previous value of 144 | * \f$v\f$, and \f$e_{plus}\f$ and \f$e_{minus}\f$ are slack variables 145 | * that hold the error for satisfying the constraint. 146 | * 147 | * We are about to change something, and we want to fix the constants 148 | * in the equations representing the stays. If both \f$e_{plus}\f$ 149 | * and \f$e_{minus}\f$ are nonbasic, they are zero in the current 150 | * solution, meaning the previous stay was exactly satisfied. In this 151 | * case nothing needs to be changed. Otherwise one of them is basic, 152 | * and the other must occur only in the expression for that basic error 153 | * variable. In that case, the constant in the expression is set to 154 | * zero. */ 155 | void reset_stay_constants(); 156 | 157 | simplex_solver& set_auto_reset_stay_constants(bool f = true) 158 | { 159 | auto_reset_stay_constants_ = f; 160 | if (f) 161 | reset_stay_constants(); 162 | 163 | return *this; 164 | } 165 | 166 | bool is_auto_reset_stay_constants() const 167 | { 168 | return auto_reset_stay_constants_; 169 | } 170 | 171 | void set_explaining(bool flag) { explain_failure_ = flag; } 172 | 173 | bool is_explaining() const { return explain_failure_; } 174 | 175 | protected: 176 | solver& add_constraint_(const constraint& c); 177 | solver& remove_constraint_(const constraint& c); 178 | 179 | /** This is a privately-used struct that bundles a constraint, its 180 | ** positive and negative error variables, and its prior edit constant. 181 | */ 182 | struct edit_info 183 | { 184 | edit_info(const variable& v_, constraint c_, variable plus_, 185 | variable minus_, double prev_constant_) 186 | : v{v_} 187 | , c{c_} 188 | , plus{plus_} 189 | , minus{minus_} 190 | , prev_constant{prev_constant_} 191 | { 192 | } 193 | 194 | bool operator==(const variable& comp) const { return v.is(comp); } 195 | bool operator==(const constraint& comp) const { return c == comp; } 196 | 197 | variable v; 198 | constraint c; 199 | variable plus; 200 | variable minus; 201 | double prev_constant; 202 | }; 203 | 204 | /** Bundles an expression, a plus and minus slack variable, and a 205 | ** prior edit constant. 206 | * This struct is only used as a return variable of make_epression().*/ 207 | struct expression_result 208 | { 209 | linear_expression expr; 210 | variable minus; 211 | variable plus; 212 | double previous_constant; 213 | 214 | expression_result() 215 | : minus{variable::nil_var()} 216 | , plus{variable::nil_var()} 217 | , previous_constant{0.0} 218 | { 219 | } 220 | }; 221 | 222 | /** Make a new linear expression representing the constraint c, 223 | ** replacing any basic variables with their defining expressions. 224 | * Normalize if necessary so that the constant is non-negative. If 225 | * the constraint is non-required, give its error variables an 226 | * appropriate weight in the objective function. */ 227 | expression_result make_expression(const constraint& c); 228 | 229 | /** Add the constraint \f$expr = 0\f$ to the inequality tableau using 230 | ** an artificial variable. 231 | * To do this, create an artificial variable \f$a_0\f$, and add the 232 | * expression \f$a_0 = expr\f$ to the inequality tableau. 233 | * Then we try to solve for \f$a_0 = 0\f$, the return value indicates 234 | * whether this has succeeded or not. 235 | * @return True iff the expression could be added. 236 | * False and a list of the constraints involved if not */ 237 | std::pair 238 | add_with_artificial_variable(linear_expression& expr); 239 | 240 | /** Add the constraint \f$expr = 0\f$ to the inequality tableau. 241 | * @return True iff the expression could be added */ 242 | bool try_adding_directly(linear_expression& expr); 243 | 244 | /** Try to choose a subject (that is, a variable to become basic) from 245 | ** among the current variables in \a expr. 246 | * If expr contains any unrestricted variables, then we must choose an 247 | * unrestricted variable as the subject. Also, if the subject is new to 248 | * the solver, we won't have to do any substitutions, so we prefer new 249 | * variables to ones that are currently noted as parametric. 250 | * 251 | * If expr contains only restricted variables, if there is a restricted 252 | * variable with a negative coefficient that is new to the solver we can 253 | * make that the subject. Otherwise we return nil, and have to add an 254 | * artificial variable and use that variable as the subject -- this is 255 | * done outside this method though. 256 | * 257 | * Note: in checking for variables that are new to the solver, we 258 | * ignore whether a variable occurs in the objective function, since 259 | * new slack variables are added to the objective function by 260 | * make_expression(), which is called before this method. 261 | * 262 | * \param expr The expression that is being added to the solver 263 | * \return An appropriate subject, or nil */ 264 | variable choose_subject(linear_expression& expr); 265 | 266 | void delta_edit_constant(double delta, const variable& v1, 267 | const variable& v2); 268 | 269 | /** Optimize using the dual algorithm. */ 270 | void dual_optimize(); 271 | 272 | /** Minimize the value of an objective. 273 | * \pre The tableau is feasible. 274 | * \param z The objective to optimize for */ 275 | void optimize(const variable& z); 276 | 277 | /** Perform a pivot operation. 278 | * Move entry into the basis (i.e. make it a basic variable), and move 279 | * exit out of the basis (i.e., make it a parametric variable). 280 | */ 281 | void pivot(const variable& entry, const variable& exit); 282 | 283 | /** Set the external variables known to this solver to their appropriate 284 | ** values. 285 | * Set each external basic variable to its value, and set each 286 | * external parametric variable to zero. Variables that are internal 287 | * to the solver don't actually store values — their 288 | * values are just implicit in the tableu — so we don't need to 289 | * set them. */ 290 | void set_external_variables(); 291 | 292 | void solve_(); 293 | 294 | void change(variable& v, double n) 295 | { 296 | if (n != v.value()) { 297 | v.change_value(n); 298 | if (on_variable_change) 299 | on_variable_change(v, *this); 300 | } 301 | } 302 | 303 | constraint_list build_explanation(const variable& v, 304 | const linear_expression& expr) const; 305 | 306 | private: 307 | typedef std::unordered_map 308 | constraint_to_varset_map; 309 | typedef std::unordered_map constraint_to_var_map; 310 | typedef std::unordered_map var_to_constraint_map; 311 | 312 | // The arrays of positive and negative error vars for the stay 313 | // constraints. (We need to keep positive and negative separate, 314 | // since the error vars are always non-negative.) 315 | std::vector stay_minus_error_vars_; 316 | std::vector stay_plus_error_vars_; 317 | 318 | constraint_to_varset_map error_vars_; 319 | constraint_to_var_map marker_vars_; 320 | var_to_constraint_map constraints_marked_; 321 | 322 | variable objective_; 323 | 324 | // Map edit variables to their constraints, errors, and prior value. 325 | std::list edit_info_list_; 326 | 327 | bool auto_reset_stay_constants_; 328 | bool needs_solving_; 329 | bool explain_failure_; 330 | 331 | std::stack cedcns_; 332 | }; 333 | 334 | /** Scoped edit action. 335 | * This class calls begin_edit() on a simplex_solver upon construction, 336 | * and end_edit() as it goes out of scope. This can be used as an 337 | * alternative to calling these two functions manually. 338 | * 339 | * \code 340 | 341 | variable x(4), y(6); 342 | simplex_solver solv; 343 | 344 | solv.add_edit_variable(x).add_edit_variable(y); 345 | { 346 | scoped_edit user_input(solv); 347 | solv.suggest_value(x, 2) 348 | .suggest_value(y, 7); 349 | } 350 | // 'user_input' goes out of scope here and calls solv.end_edit() 351 | * \endcode */ 352 | class scoped_edit 353 | { 354 | public: 355 | scoped_edit(simplex_solver& s) 356 | : s_(s.begin_edit()) 357 | { 358 | } 359 | ~scoped_edit() { s_.end_edit(); } 360 | 361 | private: 362 | simplex_solver& s_; 363 | }; 364 | 365 | } // namespace rhea 366 | -------------------------------------------------------------------------------- /rhea/slack_variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file slack_variable.hpp 3 | /// \brief Slack variable 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "abstract_variable.hpp" 10 | 11 | namespace rhea 12 | { 13 | 14 | /** Slack variables are used to turn inequalities into equations. 15 | * For example, this inequality: 16 | * \f$ 17 | * 3x + 5y \leq 10 18 | * \f$ 19 | * becomes the equation: 20 | * \f$ 21 | * 3x + 5y + s = 10 22 | * \f$ by introducing the slack variable \f$s\f$. 23 | */ 24 | class slack_variable : public abstract_variable 25 | { 26 | public: 27 | slack_variable() 28 | : abstract_variable{} 29 | { 30 | } 31 | ~slack_variable() {} 32 | 33 | virtual bool is_external() const { return false; } 34 | virtual bool is_pivotable() const { return true; } 35 | virtual bool is_restricted() const { return true; } 36 | 37 | std::string to_string() const { return "slack"; } 38 | }; 39 | 40 | } // namespace rhea 41 | -------------------------------------------------------------------------------- /rhea/solver.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file solver.hpp 3 | /// \brief Abstract base class for solvers 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include "constraint.hpp" 11 | #include "stay_constraint.hpp" 12 | #include "linear_inequality.hpp" 13 | #include "variable.hpp" 14 | 15 | namespace rhea 16 | { 17 | 18 | /** Base class for solvers. \sa simplex_solver */ 19 | class solver 20 | { 21 | public: 22 | solver() 23 | : auto_solve_{true} 24 | { 25 | } 26 | 27 | virtual ~solver() {} 28 | 29 | virtual solver& solve() = 0; 30 | 31 | virtual void resolve() = 0; 32 | 33 | solver& set_autosolve(bool is_auto = true) 34 | { 35 | auto_solve_ = is_auto; 36 | if (auto_solve_) 37 | solve(); 38 | 39 | return *this; 40 | } 41 | 42 | public: 43 | solver& add_constraint(const constraint& c) 44 | { 45 | add_constraint_(c); 46 | return *this; 47 | } 48 | 49 | solver& add_constraint(const linear_equation& c, 50 | const strength& s = strength::required(), 51 | double weight = 1.0) 52 | { 53 | return add_constraint(constraint(c, s, weight)); 54 | } 55 | 56 | solver& add_constraint(const linear_inequality& c, 57 | const strength& s = strength::required(), 58 | double weight = 1.0) 59 | { 60 | return add_constraint(constraint(c, s, weight)); 61 | } 62 | 63 | solver& add_constraints(const constraint_list& cs) 64 | { 65 | for (auto& c : cs) 66 | add_constraint(c); 67 | return *this; 68 | } 69 | 70 | solver& add_lower_bound(const variable& v, double lower) 71 | { 72 | return add_constraint(v >= lower); 73 | } 74 | 75 | solver& add_upper_bound(const variable& v, double upper) 76 | { 77 | return add_constraint(v <= upper); 78 | } 79 | 80 | solver& add_bounds(const variable& v, double lower, double upper) 81 | { 82 | return add_lower_bound(v, lower).add_upper_bound(v, upper); 83 | } 84 | 85 | solver& add_stay(const variable& v, const strength& s = strength::weak(), 86 | double weight = 1.0) 87 | { 88 | add_constraint(std::make_shared(v, s, weight)); 89 | return *this; 90 | } 91 | 92 | solver& add_stays(const variable_set& vs, 93 | const strength& s = strength::weak(), 94 | double weight = 1.0) 95 | { 96 | for (auto& v : vs) 97 | add_constraint(std::make_shared(v, s, weight)); 98 | 99 | return *this; 100 | } 101 | 102 | solver& remove_constraint(const constraint& c) 103 | { 104 | remove_constraint_(c); 105 | return *this; 106 | } 107 | 108 | solver& remove_constraints(const constraint_list& cs) 109 | { 110 | for (auto& c : cs) 111 | remove_constraint(c); 112 | return *this; 113 | } 114 | 115 | protected: 116 | virtual solver& add_constraint_(const constraint& c) = 0; 117 | virtual solver& remove_constraint_(const constraint& c) = 0; 118 | 119 | bool auto_solve_; 120 | }; 121 | 122 | } // namespace rhea 123 | -------------------------------------------------------------------------------- /rhea/stay_constraint.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file stay_constraint.hpp 3 | /// \brief Stay constraint 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "edit_or_stay_constraint.hpp" 10 | 11 | namespace rhea 12 | { 13 | 14 | /** Each variable that is to stay at an old value needs an explicit stay 15 | ** constraint. 16 | * These stay constraints need to be added before any other constraints, 17 | * since otherwise the variable's value is likely to be changed 18 | * inappropriately to satisfy the other constraints while initially building 19 | * the tableau. 20 | * 21 | * Stay constraints will be represented as equations of the form 22 | * \f$v = \alpha + \delta_v^{+} - \delta_v^{-}\f$, where 23 | * \f$\delta_v^{+}\f$ and \f$\delta_v^{-}\f$ are non-negative variables 24 | * representing the deviation of $v$ from the desired value $\alpha$. If 25 | * the constraint is satisfied both \f$\delta_v^{+}\f$ and 26 | * \f$\delta_v^{-}\f$ will be 0. Otherwise, \f$\delta_v^{+}\f$ will be 27 | * positive and \f$\delta_v^{-}\f$ will be 0 if \f$v\f$ is too big, 28 | * or vice versa if \f$v\f$ is too small. 29 | * Since we want \f$\delta_v^{+}\f$ and \f$\delta_v^{-}\f$ to be 0 if 30 | * possible, we make them part of the objective function, with larger 31 | * coefficients for the error variables for stronger constraints. 32 | */ 33 | class stay_constraint : public edit_or_stay_constraint 34 | { 35 | public: 36 | stay_constraint(const variable& v, strength s = strength::weak(), 37 | double weight = 1.0) 38 | : edit_or_stay_constraint{v, s, weight} 39 | { 40 | } 41 | 42 | virtual ~stay_constraint() {} 43 | 44 | virtual bool is_stay_constraint() const { return true; } 45 | 46 | virtual bool is_satisfied() const { return false; } 47 | }; 48 | 49 | } // namespace rhea 50 | -------------------------------------------------------------------------------- /rhea/strength.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file strength.hpp 3 | /// \brief The strength of a constraint 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include "symbolic_weight.hpp" 12 | 13 | namespace rhea 14 | { 15 | 16 | /** Every constraint has a strength that determines where it sits in the 17 | ** hierarchy; strong constraints are always satisfied in preference 18 | ** to weaker ones. 19 | * 20 | * Since we want to be able to express preferences as well as requirements 21 | * in the constraint system, we need a specification for how conflicting 22 | * preferences are to be traded off. In a constraint hierarchy each 23 | * constraint has a strength. The 'required' strength is special, in that 24 | * required constraints must be satisfied. The other strengths 25 | * all label non-required constraints. A constraint of a given strength 26 | * completely dominates any constraint with a weaker strength. In the 27 | * hierarchy theory, a comparator is used to compare different possible 28 | * solutions to the constraints and select among them. 29 | * 30 | * Within this framework a number of variations are possible. One decision 31 | * is whether we only compare solutions on a constraint-by-constraint basis 32 | * (a \e local comparator), or whether we take some aggregate measure of the 33 | * unsatisfied constraints of a given strength (a \e global comparator). A 34 | * second choice is whether we are concerned only whether a constraint is 35 | * satisfied or not (a \e predicate comparator), or whether we also want to 36 | * know how nearly satisfied it is (a \e metric comparator). 37 | * 38 | * Constraints whose domain is a metric space, for example the reals, can 39 | * have an associated error function. The error in satisfying a constraint 40 | * is zero if and only if the constraint is satisfied, and becomes larger 41 | * the less nearly satisfied it is. For a given collection of constraints, 42 | * Cassowary finds a \e locally-error-better or a \e weighted-sum-better 43 | * solution. 44 | */ 45 | class strength 46 | { 47 | public: 48 | /** */ 49 | strength(const symbolic_weight& weight) 50 | : weight_{weight} 51 | { 52 | } 53 | 54 | /** Construct a strength from three weight factors. 55 | * \param a 'Strong' weight 56 | * \param b 'Medium weight 57 | * \param c 'Weak' weight */ 58 | strength(double a, double b, double c) 59 | : weight_{a, b, c} 60 | { 61 | } 62 | 63 | /** Constraints with this strength must be satisfied. 64 | * Used by default for constraints provided by the programmer. */ 65 | static strength required() 66 | { 67 | const double z = std::numeric_limits::max(); 68 | return {z, z, z}; 69 | } 70 | 71 | /** The default strength for edit constraints. */ 72 | static strength strong() { return {1, 0, 0}; } 73 | 74 | /** Medium constraint strength. */ 75 | static strength medium() { return {0, 1, 0}; } 76 | 77 | /** The default strength for stay constraints. */ 78 | static strength weak() { return {0, 0, 1}; } 79 | 80 | /** Check if this strength signals a required constraint. */ 81 | virtual bool is_required() const { return weight_ == required().weight_; } 82 | 83 | /** Get the 3-tuple symbolic weight. */ 84 | const symbolic_weight& weight() const { return weight_; } 85 | 86 | bool operator==(const strength& c) const { return weight_ == c.weight_; } 87 | bool operator!=(const strength& c) const { return weight_ != c.weight_; } 88 | bool operator<=(const strength& c) const { return weight_ <= c.weight_; } 89 | bool operator<(const strength& c) const { return weight_ < c.weight_; } 90 | bool operator>=(const strength& c) const { return weight_ >= c.weight_; } 91 | bool operator>(const strength& c) const { return weight_ > c.weight_; } 92 | 93 | private: 94 | symbolic_weight weight_; 95 | }; 96 | 97 | } // namespace rhea 98 | -------------------------------------------------------------------------------- /rhea/symbolic_weight.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // symbolic_weight.cpp 3 | // 4 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 5 | //--------------------------------------------------------------------------- 6 | #include "symbolic_weight.hpp" 7 | #include 8 | 9 | namespace rhea 10 | { 11 | 12 | symbolic_weight::symbolic_weight() 13 | { 14 | values_[0] = values_[1] = values_[2] = 0; 15 | } 16 | 17 | symbolic_weight::symbolic_weight(double w1, double w2, double w3) 18 | { 19 | values_[0] = w1; 20 | values_[1] = w2; 21 | values_[2] = w3; 22 | } 23 | 24 | symbolic_weight symbolic_weight::zero() 25 | { 26 | return symbolic_weight(0, 0, 0); 27 | } 28 | 29 | symbolic_weight& symbolic_weight::negate() 30 | { 31 | for (double& v : values_) 32 | v = -v; 33 | 34 | return *this; 35 | } 36 | 37 | symbolic_weight& symbolic_weight::operator*=(double n) 38 | { 39 | for (double& v : values_) 40 | v *= n; 41 | 42 | return *this; 43 | } 44 | 45 | symbolic_weight& symbolic_weight::operator/=(double n) 46 | { 47 | assert(n != 0); 48 | for (double& v : values_) 49 | v /= n; 50 | 51 | return *this; 52 | } 53 | 54 | symbolic_weight& symbolic_weight::operator+=(const symbolic_weight& n) 55 | { 56 | assert(levels() == n.levels()); 57 | auto i1 = values_.begin(); 58 | auto i2 = n.values_.begin(); 59 | 60 | for (; i1 != values_.end(); ++i1, ++i2) 61 | *i1 += *i2; 62 | 63 | return *this; 64 | } 65 | 66 | symbolic_weight& symbolic_weight::operator-=(const symbolic_weight& n) 67 | { 68 | assert(levels() == n.levels()); 69 | auto i1 = values_.begin(); 70 | auto i2 = n.values_.begin(); 71 | 72 | for (; i1 != values_.end(); ++i1, ++i2) 73 | *i1 -= *i2; 74 | 75 | return *this; 76 | } 77 | 78 | bool symbolic_weight::operator<(const symbolic_weight& comp) const 79 | { 80 | return values_ < comp.values_; 81 | } 82 | 83 | bool symbolic_weight::operator<=(const symbolic_weight& comp) const 84 | { 85 | return values_ <= comp.values_; 86 | } 87 | 88 | bool symbolic_weight::operator==(const symbolic_weight& comp) const 89 | { 90 | return values_ == comp.values_; 91 | } 92 | 93 | bool symbolic_weight::operator!=(const symbolic_weight& comp) const 94 | { 95 | return values_ != comp.values_; 96 | } 97 | 98 | bool symbolic_weight::operator>=(const symbolic_weight& comp) const 99 | { 100 | return values_ >= comp.values_; 101 | } 102 | 103 | bool symbolic_weight::operator>(const symbolic_weight& comp) const 104 | { 105 | return values_ > comp.values_; 106 | } 107 | 108 | bool symbolic_weight::is_negative() const 109 | { 110 | return values_ < zero().values_; 111 | } 112 | 113 | } // namespace rhea 114 | -------------------------------------------------------------------------------- /rhea/symbolic_weight.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file symbolic_weight.hpp 3 | /// \brief A 3-tuple weight for constraints 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace rhea 13 | { 14 | 15 | /** A 3-tuple weight for constraint strengths. 16 | * In the original implementation this was an n-tuple, but it has been fixed 17 | * at 3 in Rhea. The three elements correspond to the strong, medium and 18 | * weak constraints. Every constraint can also have a weight (1 by 19 | * default). Symbolic weights are then ordered lexicographically: strong 20 | * weights always outclass medium weights, no matter what the values. 21 | * 22 | * The end effect is that strong constraints are satisfied before the 23 | * medium ones, and the weak constraints are satisfied last. Within each 24 | * of the three classes of constraints, you can make further adjustments 25 | * by changing the weight. */ 26 | class symbolic_weight 27 | { 28 | public: 29 | symbolic_weight(); 30 | symbolic_weight(double w1, double w2, double w3); 31 | 32 | static symbolic_weight zero(); 33 | 34 | symbolic_weight& negate(); 35 | symbolic_weight& operator*=(double n); 36 | symbolic_weight& operator/=(double n); 37 | symbolic_weight& operator+=(const symbolic_weight& n); 38 | symbolic_weight& operator-=(const symbolic_weight& n); 39 | 40 | bool operator<(const symbolic_weight& comp) const; 41 | bool operator<=(const symbolic_weight& comp) const; 42 | bool operator==(const symbolic_weight& comp) const; 43 | bool operator!=(const symbolic_weight& comp) const; 44 | bool operator>(const symbolic_weight& comp) const; 45 | bool operator>=(const symbolic_weight& comp) const; 46 | 47 | bool is_negative() const; 48 | 49 | double as_double() const 50 | { 51 | return values_[2] + values_[1] * 10000. + values_[0] * 10000000.; 52 | } 53 | 54 | size_t levels() const { return values_.size(); } 55 | 56 | private: 57 | std::array values_; 58 | }; 59 | 60 | inline symbolic_weight operator*(symbolic_weight w, double n) 61 | { 62 | return w *= n; 63 | } 64 | 65 | inline symbolic_weight operator/(symbolic_weight w, double n) 66 | { 67 | return w /= n; 68 | } 69 | 70 | inline symbolic_weight operator+(symbolic_weight w, const symbolic_weight& n) 71 | { 72 | return w += n; 73 | } 74 | 75 | inline symbolic_weight operator-(symbolic_weight w, const symbolic_weight& n) 76 | { 77 | return w -= n; 78 | } 79 | 80 | } // namespace rhea 81 | -------------------------------------------------------------------------------- /rhea/tableau.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // tableau.cpp 3 | // 4 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 5 | //--------------------------------------------------------------------------- 6 | #include "tableau.hpp" 7 | 8 | namespace rhea 9 | { 10 | 11 | void tableau::add_row(const variable& var, const linear_expression& expr) 12 | { 13 | assert(!var.is_nil()); 14 | rows_[var] = expr; 15 | for (auto& p : expr.terms()) { 16 | const variable& v = p.first; 17 | columns_[v].insert(var); 18 | if (v.is_external() && !is_basic_var(v)) 19 | external_parametric_vars_.insert(v); 20 | } 21 | 22 | if (var.is_external()) 23 | external_rows_.insert(var); 24 | } 25 | 26 | bool tableau::remove_column(const variable& var) 27 | { 28 | assert(!var.is_nil()); 29 | auto ic = columns_.find(var); 30 | if (ic == columns_.end()) 31 | return false; 32 | 33 | for (const variable& v : ic->second) 34 | rows_[v].erase(var); 35 | 36 | if (var.is_external()) { 37 | external_rows_.erase(var); 38 | external_parametric_vars_.erase(var); 39 | } 40 | columns_.erase(ic); 41 | 42 | return true; 43 | } 44 | 45 | linear_expression tableau::remove_row(const variable& var) 46 | { 47 | assert(!var.is_nil()); 48 | auto ir = rows_.find(var); 49 | assert(ir != rows_.end()); 50 | for (auto& p : ir->second.terms()) { 51 | auto ic = columns_.find(p.first); 52 | assert(ic != columns_.end()); 53 | ic->second.erase(var); 54 | if (ic->second.empty()) { 55 | columns_.erase(ic); 56 | external_parametric_vars_.erase(p.first); 57 | } 58 | } 59 | 60 | infeasible_rows_.erase(var); 61 | if (var.is_external()) { 62 | external_rows_.erase(var); 63 | external_parametric_vars_.erase(var); 64 | } 65 | 66 | linear_expression result{std::move(ir->second)}; 67 | rows_.erase(ir); 68 | 69 | return result; 70 | } 71 | 72 | void tableau::substitute_out(const variable& old, 73 | const linear_expression& expr) 74 | { 75 | auto ic = columns_.find(old); 76 | if (ic == columns_.end()) 77 | return; 78 | 79 | for (auto& v : ic->second) { 80 | auto& row = rows_[v]; 81 | row.substitute_out(old, expr, v, *this); 82 | if (v.is_restricted() && row.constant() < 0) 83 | infeasible_rows_.insert(v); 84 | } 85 | 86 | columns_.erase(old); 87 | 88 | if (old.is_external()) { 89 | if (!columns_[old].empty()) 90 | external_rows_.insert(old); 91 | 92 | external_parametric_vars_.erase(old); 93 | } 94 | } 95 | 96 | bool tableau::is_valid() const 97 | { 98 | for (auto& r : rows_) { 99 | const auto& clv = r.first; 100 | if (clv.is_external()) { 101 | if (external_rows_.count(clv) == 0) 102 | return false; 103 | } 104 | 105 | auto& expr = r.second; 106 | for (auto& p : expr.terms()) { 107 | const variable& v = p.first; 108 | if (v.is_external()) { 109 | if (external_parametric_vars_.count(v) == 0) 110 | return false; 111 | } 112 | } 113 | } 114 | return true; 115 | } 116 | 117 | void tableau::note_removed_variable(const variable& v, const variable& subj) 118 | { 119 | auto& column = columns_[v]; 120 | auto i(column.find(subj)); 121 | if (i == column.end()) 122 | throw internal_error("note_removed_variable: subject not in column"); 123 | 124 | column.erase(i); 125 | if (column.empty()) { 126 | columns_.erase(v); 127 | external_rows_.erase(v); 128 | external_parametric_vars_.erase(v); 129 | } 130 | } 131 | 132 | void tableau::note_added_variable(const variable& v, const variable& subj) 133 | { 134 | columns_[v].insert(subj); 135 | if (v.is_external() && !is_basic_var(v)) 136 | external_parametric_vars_.insert(v); 137 | } 138 | 139 | } // namespace rhea 140 | -------------------------------------------------------------------------------- /rhea/tableau.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file tableau.hpp 3 | /// \brief Tableau for holding and manipulating linear expressions 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include "errors.hpp" 12 | #include "variable.hpp" 13 | #include "linear_expression.hpp" 14 | 15 | namespace rhea 16 | { 17 | 18 | /** A tableau, or augmented matrix, represents the coefficients and 19 | ** solution of a set of equations. 20 | * For example, given the following set of equations: 21 | * \f[\begin{align} 22 | * a + 2b + 3c &= 0 \\ 23 | * 3a + 4b + 7c &= 2 \\ 24 | * 6a + 5b + 9c &= 11 25 | * \end{align}\f] 26 | * The tableau would be: 27 | * \f[\left[\begin{array}{ccc|c} 28 | * 1 & 2 & 3 & 0 \\ 29 | * 3 & 4 & 7 & 2 \\ 30 | * 6 & 5 & 9 & 11 31 | * \end{array}\right]\f] 32 | * So every column corresponds to a variable, and every row to a 33 | * linear equation. If the first row is the objective, and the first 34 | * column the objective variable, we get a tableau of the form: 35 | * \f[\left[\begin{array}{cc|c} 36 | * 1 & -c^T & 0 \\ 37 | * 0 & A & b 38 | * \end{array}\right]\f] 39 | * If \f$A\f$ contains an identity matrix, the tableau is in canonical 40 | * form. The variables corresponding to the identity matrix are the 41 | * basic variables, the others are the free variables. (Since it is an 42 | * identity matrix, every row is also associated with exactly one basic 43 | * variable.) 44 | * If the free variables are assumed to be zero, the solution can be read 45 | * from the first row. 46 | */ 47 | class tableau 48 | { 49 | public: 50 | typedef std::unordered_map columns_map; 51 | typedef std::unordered_map rows_map; 52 | 53 | public: 54 | /** This function should be invoked when v has been removed from an 55 | ** expression, so the column indices can be updated. */ 56 | void note_removed_variable(const variable& v, const variable& subj); 57 | 58 | /** This function should be invoked when v has been added to an 59 | ** expression, so the column indices can be updated. */ 60 | void note_added_variable(const variable& v, const variable& subj); 61 | 62 | /** Check the internal consistency of this data structure. */ 63 | bool is_valid() const; 64 | 65 | public: 66 | tableau() {} 67 | 68 | virtual ~tableau() {} 69 | 70 | /** Add a new row to the tableau. */ 71 | void add_row(const variable& v, const linear_expression& e); 72 | 73 | /** Remove a variable from the tableau. 74 | * \return True iff the variable was known */ 75 | bool remove_column(const variable& v); 76 | 77 | /** Remove a row from the tableau. 78 | * \param v The basic variable that is used to index the row 79 | * \return The expression represented by the removed row */ 80 | linear_expression remove_row(const variable& v); 81 | 82 | /** Replace all occurrences of \a old_var with \a expr, and update 83 | ** column cross indices. 84 | * \a old_var should now be a basic variable. 85 | * This function calls substitute_out on each row that has old_var 86 | * in it. 87 | * @post old_var is no longer a basic variable */ 88 | void substitute_out(const variable& old_var, 89 | const linear_expression& expr); 90 | 91 | const columns_map& columns() const { return columns_; } 92 | 93 | const rows_map& rows() const { return rows_; } 94 | 95 | bool columns_has_key(const variable& subj) const 96 | { 97 | return columns_.count(subj) > 0; 98 | } 99 | 100 | /** Get the linear expression that the given row represents. */ 101 | const linear_expression& row_expression(const variable& v) const 102 | { 103 | auto i = rows_.find(v); 104 | if (i == rows_.end()) 105 | throw row_not_found(); 106 | 107 | return i->second; 108 | } 109 | 110 | /** Get the linear expression that the given row represents. */ 111 | linear_expression& row_expression(const variable& v) 112 | { 113 | auto i = rows_.find(v); 114 | if (i == rows_.end()) 115 | throw row_not_found(); 116 | 117 | return i->second; 118 | } 119 | 120 | /** Check if v is one of the basic variables. */ 121 | bool is_basic_var(const variable& v) const { return rows_.count(v) > 0; } 122 | 123 | /** Check if f is one of the parametric (aka. free) variables. */ 124 | bool is_parametric_var(const variable& v) const 125 | { 126 | return rows_.count(v) == 0; 127 | } 128 | 129 | protected: 130 | /** A mapping from variables which occur in expressions to the 131 | ** rows whose expressions contain them. */ 132 | columns_map columns_; 133 | 134 | /** A mapping from the basic variables to the expressions for that 135 | ** row in the tableau. */ 136 | rows_map rows_; 137 | 138 | /** The collection of basic variables that have infeasible rows. 139 | * This is used internally when optimizing. */ 140 | variable_set infeasible_rows_; 141 | 142 | /** A map to quickly find rows with external basic variables. */ 143 | variable_set external_rows_; 144 | 145 | /** A map to quickly find rows with external parametric variables. */ 146 | variable_set external_parametric_vars_; 147 | }; 148 | 149 | } // namespace rhea 150 | -------------------------------------------------------------------------------- /rhea/variable.hpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file variable.hpp 3 | /// \brief A variable as used in an expression 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "approx.hpp" 17 | #include "abstract_variable.hpp" 18 | #include "float_variable.hpp" 19 | #include "link_variable.hpp" 20 | #include "action_variable.hpp" 21 | 22 | namespace rhea 23 | { 24 | 25 | /** This tag is used in \a variable to link to external variables. */ 26 | struct linked 27 | { 28 | }; 29 | 30 | /** A variable as used in an expression. 31 | * Variables don't use the normal C++ copy semantics: objects are actually 32 | * counted references to an abstract_variable. The following example 33 | * illustrates this: 34 | * 35 | * \code 36 | 37 | variable x(1), y(0); 38 | 39 | y = x; 40 | // y is now 1 41 | 42 | x.set_value(2); 43 | // both x and y are now 2 44 | 45 | * \endcode 46 | * 47 | * Also note that a variable is nullable. A variable that has been 48 | * constructed without a type cannot be used in expressions. 49 | * 50 | * Another caveat: "x == y" is not a boolean, but a linear_equality that 51 | * can be evaluated and used in constraints. There are two ways to compare 52 | * two variables, depending on whether you want to test for equality or 53 | * equivalence: 54 | * 55 | * \code 56 | 57 | variable x(2), y(x), z(2); 58 | 59 | x.is(y); // True: y was constructed from x 60 | x.is(z); // False: x and z both have the value 2, but they are different 61 | variables 62 | 63 | x.value() == y.value(); // True 64 | x.value() == z.value(); // Also true 65 | 66 | * \endcode 67 | * */ 68 | class variable 69 | { 70 | public: 71 | variable() 72 | : p_{std::make_shared(0.0)} 73 | { 74 | } 75 | 76 | /** An explicit nil variable. 77 | * This function only serves to make code more readable. */ 78 | static variable nil_var() { return {nil_()}; } 79 | 80 | /** Wrap an abstract variable on the heap. 81 | * \param p Shared pointer to a variable. 82 | */ 83 | template 84 | variable(std::shared_ptr&& p) 85 | : p_{std::move(p)} 86 | { 87 | } 88 | 89 | /** "Copy" a variable. 90 | * The resulting variable won't be a true copy, but rather another 91 | * counted reference to the same variable. */ 92 | variable(const variable& copy) 93 | : p_{copy.p_} 94 | { 95 | } 96 | 97 | /** Move constructor. */ 98 | variable(variable&& copy) 99 | : p_{std::move(copy.p_)} 100 | { 101 | } 102 | 103 | /** Create a new floating pointe variable. 104 | * \param value The variable's initial value 105 | */ 106 | variable(int value) 107 | : p_{std::make_shared(value)} 108 | { 109 | } 110 | 111 | /** Create a new floating point variable. 112 | * \param value The variable's initial value 113 | */ 114 | variable(unsigned int value) 115 | : p_{std::make_shared(value)} 116 | { 117 | } 118 | 119 | /** Create a new floating point variable. 120 | * \param value The variable's initial value 121 | */ 122 | variable(float value) 123 | : p_{std::make_shared(value)} 124 | { 125 | } 126 | 127 | /** Create a new floating point variable. 128 | * \param value The variable's initial value 129 | */ 130 | variable(double value) 131 | : p_{std::make_shared(value)} 132 | { 133 | } 134 | 135 | /** Create variable that is linked to an existing integer. 136 | * It is up to you to make sure the linked variable isn't destroyed 137 | * while the solver is still using it. 138 | * \param value This variable will be automatically updated 139 | */ 140 | variable(int& value, const linked&) 141 | : p_{std::make_shared(value)} 142 | { 143 | } 144 | 145 | /** Create variable that is linked to an existing float. 146 | * It is up to you to make sure the linked variable isn't destroyed 147 | * while the solver is still using it. 148 | * \param value This variable will be automatically updated 149 | */ 150 | variable(float& value, const linked&) 151 | : p_{std::make_shared>(value)} 152 | { 153 | } 154 | 155 | /** Create variable that is linked to an existing double. 156 | * It is up to you to make sure the linked variable isn't destroyed 157 | * while the solver is still using it. 158 | * \param value This variable will be automatically updated 159 | */ 160 | variable(double& value, const linked&) 161 | : p_{std::make_shared>(value)} 162 | { 163 | } 164 | 165 | /** Create a variable that calls a function whenever it is updated. */ 166 | variable(std::function callback, double init_val = 0.0) 167 | : p_{std::make_shared(callback, init_val)} 168 | { 169 | } 170 | 171 | variable& operator=(const variable& assign) 172 | { 173 | p_ = assign.p_; 174 | return *this; 175 | } 176 | 177 | variable& operator=(variable&& move) 178 | { 179 | p_ = std::move(move.p_); 180 | return *this; 181 | } 182 | 183 | /** Check if this variable is of the type float_variable. */ 184 | bool is_float() const { return p_->is_float(); } 185 | 186 | /** Check if this variable is used in the finite domain subsolver. */ 187 | bool is_fd() const { return p_->is_fd(); } 188 | 189 | /** Check if this variable is a dummy variable. */ 190 | bool is_dummy() const { return p_->is_dummy(); } 191 | 192 | /** Check if this variable is used outside the solver. */ 193 | bool is_external() const { return p_->is_external(); } 194 | 195 | /** Check if this variable can be used as a pivot element in a tableau. */ 196 | bool is_pivotable() const { return p_->is_pivotable(); } 197 | 198 | /** Check if this variable is restricted, or in other words, if it is 199 | ** a dummy or a slack variable. */ 200 | bool is_restricted() const { return p_->is_restricted(); } 201 | 202 | /** Get the value of this variable. */ 203 | double value() const 204 | { 205 | assert(!is_nil()); 206 | return p_->value(); 207 | } 208 | 209 | /** Get the value of this variable, converted to an integer. */ 210 | int int_value() const { return p_->int_value(); } 211 | 212 | /** Set this variable to a new value. */ 213 | void set_value(double x) { p_->set_value(x); } 214 | 215 | /** Change this variable's value. */ 216 | void change_value(double x) { p_->change_value(x); } 217 | 218 | /** Check if this is a nil variable. */ 219 | bool is_nil() const { return p_ == nullptr; } 220 | 221 | /** Calculate a hash value. 222 | * This function is only used for placing variables in hash tables. */ 223 | size_t hash() const { return id(); } 224 | 225 | /** Get a string representation. 226 | * For ordinary variables, this will be the value. Special variables 227 | * will print 'dummy', 'slack', or 'edit'. */ 228 | std::string to_string() const 229 | { 230 | return is_nil() ? "NIL" : p_->to_string(); 231 | } 232 | 233 | /** Check if two variables refer to the same abstract_variable. 234 | * This will not return 'true' for two distinct variables that happen 235 | * to have the same value. Example: 236 | * \code 237 | variable x(3), y(3), z; 238 | x.is(y); // False! 239 | z = x; // z now refers to x 240 | z.set_value(5); 241 | x.is(z); // True (x.value() == 5 as well) 242 | * \endcode 243 | */ 244 | bool is(const variable& x) const { return p_ == x.p_; } 245 | 246 | /** Helper function so rhea::variable can be used in a std::map */ 247 | bool is_less(const variable& x) const { return id() < x.id(); } 248 | 249 | /** Get the variable's unique ID. 250 | * Do not use: this function may disappear in future versions. */ 251 | size_t id() const { return p_->id(); } 252 | 253 | private: 254 | struct nil_ 255 | { 256 | }; 257 | 258 | variable(const nil_&) {} 259 | 260 | private: 261 | /** Reference counted pointer to the actual variable. */ 262 | std::shared_ptr p_; 263 | }; 264 | 265 | /** Convenience typedef for sets of variables. */ 266 | typedef std::unordered_set variable_set; 267 | 268 | } // namespace rhea 269 | 270 | //------------------------------------------------------------------------- 271 | 272 | namespace std 273 | { 274 | 275 | /** Hash function, required for std::unordered_map and -set. */ 276 | template <> 277 | struct hash : public unary_function 278 | { 279 | size_t operator()(const rhea::variable& v) const { return v.hash(); } 280 | }; 281 | 282 | /** Equality test, required for std::unordered_map and -set. */ 283 | template <> 284 | struct equal_to 285 | : public binary_function 286 | { 287 | bool operator()(const rhea::variable& a, const rhea::variable& b) const 288 | { 289 | return a.is(b); 290 | } 291 | }; 292 | 293 | /** Strict weak ordering, required for std::map and set. */ 294 | template <> 295 | struct less 296 | : public binary_function 297 | { 298 | bool operator()(const rhea::variable& a, const rhea::variable& b) const 299 | { 300 | return a.is_less(b); 301 | } 302 | }; 303 | 304 | /** Get a string representation of a variable. */ 305 | inline string to_string(const rhea::variable& v) 306 | { 307 | return v.to_string(); 308 | } 309 | 310 | } // namespace std 311 | -------------------------------------------------------------------------------- /rhea/version.hpp.in: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | /// \file version.hpp 3 | /// \brief Header file generated by CMake 4 | // 5 | // Copyright 2012-2014, nocte@hippie.nu Released under the MIT License. 6 | //--------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | /** The library's version number as a dotted triplet string. */ 10 | #define RHEA_VERSION "@VERSION@" 11 | 12 | /** The library's major version number. 13 | * This gets bumped whenever the API breaks. */ 14 | #define RHEA_VERSION_MAJOR "@VERSION_MAJOR@" 15 | 16 | /** The library's minor version number. 17 | * This gets bumped whenever the API is extended. */ 18 | #define RHEA_VERSION_MINOR "@VERSION_MINOR@" 19 | 20 | /** The library's patch number. 21 | * This gets bumped for bug fixes and optimizations. */ 22 | #define RHEA_VERSION_PATCH "@VERSION_PATCH@" 23 | 24 | -------------------------------------------------------------------------------- /unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (unit-tests) 2 | cmake_minimum_required (VERSION 2.8.3) 3 | set(EXE unit_tests) 4 | 5 | add_executable(${EXE} unit_tests.cpp) 6 | # add_executable(speed_test speed_test.cpp) 7 | 8 | include_directories(..) 9 | 10 | find_package(Boost 1.46 REQUIRED COMPONENTS unit_test_framework) 11 | 12 | include_directories(${Boost_INCLUDE_DIRS}) 13 | 14 | target_link_libraries(${EXE} rhea-s ${Boost_LIBRARIES}) 15 | if(BUILD_COVERAGE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 16 | target_link_libraries(${EXE} -fprofile-arcs -ftest-coverage) 17 | endif() 18 | 19 | # target_link_libraries(speed_test rhea) 20 | 21 | -------------------------------------------------------------------------------- /unit_tests/speed_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Does the same speed test as the original C++ version 3 | // 4 | // Example (with g++ -O3) 5 | // Cassowary: add: 5622 edit: 30 resolve: 453 edit: 5ms 6 | // Rhea: add: 356 edit: 8 resolve: 423 edit: 6ms 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../rhea/simplex_solver.hpp" 14 | #include "../rhea/linear_equation.hpp" 15 | 16 | inline double uniform_rand() 17 | { 18 | return double(rand()) / RAND_MAX; 19 | } 20 | 21 | inline double grained_rand() 22 | { 23 | const double grain(1.0e-4); 24 | return int(uniform_rand() / grain) * grain; 25 | } 26 | 27 | template 28 | double msec(const v& duration) 29 | { 30 | return std::chrono::duration_cast(duration) 31 | .count(); 32 | } 33 | 34 | int main(int argc, char** argv) 35 | { 36 | using std::chrono::system_clock; 37 | 38 | size_t cns(500), resolves(500), solvers(10); 39 | 40 | const double ineq_prob(0.12); 41 | const unsigned int max_vars(3), nr_vars(cns); 42 | 43 | system_clock clock; 44 | 45 | std::vector slv(solvers); 46 | for (auto& s : slv) 47 | s.set_autosolve(false); 48 | 49 | std::vector vars; 50 | for (size_t i(0); i < nr_vars; ++i) { 51 | vars.emplace_back((int)i); 52 | for (auto& s : slv) 53 | s.add_stay(vars[i]); 54 | } 55 | 56 | size_t cns_made(cns * 2); 57 | std::vector constraints(cns_made); 58 | 59 | for (size_t j(0); j < cns_made; ++j) { 60 | size_t nvs((uniform_rand() * max_vars) + 1); 61 | rhea::linear_expression expr(grained_rand() * 20.0 - 10.0); 62 | for (size_t k(0); k < nvs; ++k) { 63 | double coeff(grained_rand() * 10.0 - 5.0); 64 | expr += rhea::linear_expression(vars[uniform_rand() * nr_vars]) 65 | * coeff; 66 | } 67 | if (uniform_rand() < ineq_prob) 68 | constraints[j] = rhea::linear_inequality(std::move(expr)); 69 | else 70 | constraints[j] = rhea::linear_equation(std::move(expr)); 71 | } 72 | 73 | auto timer(clock.now()); 74 | for (auto& s : slv) { 75 | size_t added(0), exceptions(0); 76 | for (size_t j(0); added < cns && j < cns_made; ++j) { 77 | try { 78 | s.add_constraint(constraints[j]); 79 | ++added; 80 | } catch (...) { 81 | ++exceptions; 82 | } 83 | } 84 | } 85 | auto end(clock.now()); 86 | auto time_add(end - timer); 87 | 88 | // ------ 89 | 90 | rhea::variable e1(vars[uniform_rand() * nr_vars]); 91 | rhea::variable e2(vars[uniform_rand() * nr_vars]); 92 | 93 | timer = clock.now(); 94 | for (auto& s : slv) 95 | s.add_edit_var(e1).add_edit_var(e2); 96 | 97 | auto time_edit(clock.now() - timer); 98 | 99 | // ------ 100 | 101 | timer = clock.now(); 102 | for (auto& s : slv) { 103 | s.begin_edit(); 104 | for (size_t m(0); m < resolves; ++m) { 105 | s.suggest_value(e1, e1.value() * 1.001) 106 | .suggest_value(e2, e2.value() * 1.001) 107 | .resolve(); 108 | } 109 | } 110 | auto time_resolve(clock.now() - timer); 111 | 112 | // ------ 113 | 114 | timer = clock.now(); 115 | for (auto& s : slv) 116 | s.end_edit(); 117 | 118 | auto time_endedit(clock.now() - timer); 119 | 120 | // ------ 121 | 122 | std::cout << "add: " << msec(time_add) << " edit: " << msec(time_edit) 123 | << " resolve: " << msec(time_resolve) 124 | << " endedit: " << msec(time_endedit) << std::endl; 125 | 126 | return 0; 127 | } 128 | --------------------------------------------------------------------------------