diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6b8b098 --- /dev/null +++ b/.clang-format @@ -0,0 +1,177 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +#IncludeBlocks: Regroup +IncludeBlocks: Preserve +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' +ReflowComments: true +#SortIncludes: true +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseTab: Never +--- +Language: Java +BasedOnStyle: Google +AlignTrailingComments: true +AllowShortFunctionsOnASingleLine: All +BinPackArguments: true +BreakAfterJavaFieldAnnotations: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +ColumnLimit: 120 +IndentWidth: 4 +ReflowComments: true +TabWidth: 4 +UseTab: Never +... diff --git a/.github/workflows/build-macos-13.yml b/.github/workflows/build-macos-13.yml new file mode 100644 index 0000000..ad0f54f --- /dev/null +++ b/.github/workflows/build-macos-13.yml @@ -0,0 +1,56 @@ +--- +name: Build for macOS-13 +on: + push: + branches: [master] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-macos-13.yml + pull_request: + branches: [master] + types: [synchronize, opened, reopened, ready_for_review] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-macos-13.yml + workflow_dispatch: + +jobs: + macOS-13: + runs-on: macos-13 + env: + CMAKE_GENERATOR: Ninja + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '14.3.1' + - name: Setup Runner Environment + run: | + uname -a + xcrun --show-sdk-version + CORES=$(sysctl -n hw.ncpu) + echo "CMAKE_BUILD_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_LOAD=$CORES" >> $GITHUB_ENV + - name: Install Build Tools + run: | + brew update + brew install cmake ninja boost + - uses: actions/checkout@v4 + - name: Configure Multi-Config + run: cmake --preset multi-san + - name: Build Debug + run: cmake --build --preset debug-san + - name: Test Debug + run: ctest --preset debug-san + - name: Build Release + run: cmake --build --preset release-san + - name: Test Release + run: ctest --preset release-san diff --git a/.github/workflows/build-macos-14.yml b/.github/workflows/build-macos-14.yml new file mode 100644 index 0000000..39e979c --- /dev/null +++ b/.github/workflows/build-macos-14.yml @@ -0,0 +1,56 @@ +--- +name: Build for macOS-14 +on: + push: + branches: [master] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-macos-14.yml + pull_request: + branches: [master] + types: [synchronize, opened, reopened, ready_for_review] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-macos-14.yml + workflow_dispatch: + +jobs: + macOS-14: + runs-on: macos-14 + env: + CMAKE_GENERATOR: Ninja + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.4.0' + - name: Setup Runner Environment + run: | + uname -a + xcrun --show-sdk-version + CORES=$(sysctl -n hw.ncpu) + echo "CMAKE_BUILD_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_LOAD=$CORES" >> $GITHUB_ENV + - name: Install Build Tools + run: | + brew update + brew install cmake ninja boost + - uses: actions/checkout@v4 + - name: Configure Multi-Config + run: cmake --preset multi-san + - name: Build Debug + run: cmake --build --preset debug-san + - name: Test Debug + run: ctest --preset debug-san + - name: Build Release + run: cmake --build --preset release-san + - name: Test Release + run: ctest --preset release-san diff --git a/.github/workflows/build-macos-15.yml b/.github/workflows/build-macos-15.yml new file mode 100644 index 0000000..61a3ef5 --- /dev/null +++ b/.github/workflows/build-macos-15.yml @@ -0,0 +1,56 @@ +--- +name: Build for macOS-15 +on: + push: + branches: [master] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-macos-15.yml + pull_request: + branches: [master] + types: [synchronize, opened, reopened, ready_for_review] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-macos-15.yml + workflow_dispatch: + +jobs: + macOS-15: + runs-on: macos-15 + env: + CMAKE_GENERATOR: Ninja + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.4.0' + - name: Setup Runner Environment + run: | + uname -a + xcrun --show-sdk-version + CORES=$(sysctl -n hw.ncpu) + echo "CMAKE_BUILD_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_LOAD=$CORES" >> $GITHUB_ENV + - name: Install Build Tools + run: | + brew update + brew install cmake ninja boost + - uses: actions/checkout@v4 + - name: Configure Multi-Config + run: cmake --preset multi-san + - name: Build Debug + run: cmake --build --preset debug-san + - name: Test Debug + run: ctest --preset debug-san + - name: Build Release + run: cmake --build --preset release-san + - name: Test Release + run: ctest --preset release-san diff --git a/.github/workflows/build-ubuntu-2404.yml b/.github/workflows/build-ubuntu-2404.yml new file mode 100644 index 0000000..73e5c87 --- /dev/null +++ b/.github/workflows/build-ubuntu-2404.yml @@ -0,0 +1,53 @@ +--- +name: Build for Ubuntu-24.04 +on: + push: + branches: [master] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-ubuntu-2404.yml + pull_request: + branches: [master] + types: [synchronize, opened, reopened, ready_for_review] + paths: + - src/** + - test/** + - cmake/** + - CMakeLists.txt + - CMakePresets.json + - .github/workflows/build-ubuntu-2404.yml + workflow_dispatch: + +jobs: + ubuntu-2404: + runs-on: ubuntu-24.04 + env: + CMAKE_GENERATOR: Ninja + steps: + - name: Setup Runner Environment + run: | + uname -a + lsb_release -a + CORES=$(nproc) + echo "CMAKE_BUILD_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_PARALLEL_LEVEL=$CORES" >> $GITHUB_ENV + echo "CTEST_TEST_LOAD=$CORES" >> $GITHUB_ENV + - name: Install Build Tools + run: | + sudo apt-get -qq update + sudo apt-get -qq install cmake ninja-build g++ libboost-program-options-dev libboost-test-dev + - uses: actions/checkout@v4 + - name: Configure Multi-Config + run: cmake --preset multi-san + - name: Build Debug + run: cmake --build --preset debug-san + - name: Test Debug + run: ctest --preset debug-san + - name: Build Release + run: cmake --build --preset release-san + - name: Test Release + run: ctest --preset release-san diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65b1f8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*~ +.idea +/build +/build-* +/cmake-build-* +/local diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f948b4..67c4927 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,55 +1,36 @@ -cmake_minimum_required(VERSION 3.7) - -cmake_policy(SET CMP0048 NEW) - -include(ExternalProject) +cmake_minimum_required(VERSION 3.28) +project(libstrategy VERSION 1.0.9 LANGUAGES CXX C) set(CMAKE_CXX_STANDARD 17) -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif (NOT CMAKE_BUILD_TYPE) - +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -project(libstrategy VERSION 1.0.0 LANGUAGES CXX C) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -Wall -Wpedantic -I -fPIC") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -DNDEBUG -Wall -Wpedantic -fPIC") +include(cmake/warnings.cmake) +include(cmake/sanitizers.cmake) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(EXTERNAL_INSTALL_LOCATION ${CMAKE_BINARY_DIR}/external) - -option(STRATEGY_GetDependencies "Fetch external dependencies from web." ON) - -if (STRATEGY_GetDependencies) - ExternalProject_add(ptrie - URL https://github.com/petergjoel/ptrie/archive/v1.1.1.zip - URL_HASH SHA512=5435D6F8132D273B50ECCD721790D559F7DC67FFDADA75FEC315F4C53705199F31F1DDC80DDA0CB86F883DB27076FA6D324D28516B442FECCE3FFA51213AEF4E - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_LOCATION} -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release - ) - - ExternalProject_add(nlohmann_json - URL https://github.com/nlohmann/json/archive/v3.7.3.zip - URL_HASH SHA512=b47a07de9a071cce645a173d084df5dd31f7669154fc00f6c99e0506474d30e8376acaee1d3c79a50def4f25a36042951bfa4fca9a704687e59c368d05053158 - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_LOCATION} -DJSON_BuildTests=OFF -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} - ) - - include_directories(${EXTERNAL_INSTALL_LOCATION}/include) - link_directories(${EXTERNAL_INSTALL_LOCATION}/lib) -else (STRATEGY_GetDependencies) - find_package(nlohmann_json 3.7.3 REQUIRED) - find_package(ptrie 1.1.1 REQUIRED) -endif (STRATEGY_GetDependencies) - -# Unset Boost_ROOT here if set, not needed for JSON or source of strategy -if(Boost_ROOT) - unset(Boost_ROOT) + +option(LIBSTRATEGY_TESTS "libstrategy Unit Tests" ON) +option(LIBSTRATEGY_OnlyLibrary "Build only the library (no binary utilities)" OFF) + +include(cmake/ptrie.cmake) +include(cmake/nlohmann_json.cmake) + +if(NOT LIBSTRATEGY_OnlyLibrary) + cmake_policy(SET CMP0069 NEW) # INTERPROCEDURAL_OPTIMIZATIONS flags + if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.30.0") + cmake_policy(SET CMP0167 NEW) # use FindBoost from boost installation (as opposed to CMake own) + endif () + find_package(Boost 1.83 COMPONENTS program_options REQUIRED) endif() -add_subdirectory(${CMAKE_SOURCE_DIR}/src/) +add_subdirectory(src) -set(LIBSTRATEGY_BuildTests "Build tests of libstrategy" ON) -if(BUILD_TESTING AND LIBSTRATEGY_BuildTests) +if (LIBSTRATEGY_TESTS) enable_testing() add_subdirectory(test) endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..0dd7936 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,56 @@ +{ + "version": 8, + "cmakeMinimumRequired": { + "major": 3, + "minor": 28, + "patch": 0 + }, + "include": ["cmake/CommonPresets.json"], + "configurePresets": [ + { + "name": "libonly", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-libonly", + "cacheVariables": { + "LIBSTRATEGY_TESTS": "OFF" , + "LIBSTRATEGY_OnlyLibrary": "ON" + }, + "displayName": "Configure Library-Only for Debug and Release" + } + ], + "buildPresets": [ + { + "name": "libonly-release", + "inherits": "release", + "configurePreset": "libonly", + "displayName": "Build Library-Only for Debug" + }, + { + "name": "libonly-debug", + "inherits": "debug", + "configurePreset": "libonly", + "displayName": "Build Library-Only for Release" + } + ], + "testPresets": [], + "workflowPresets": [ + { + "name": "libonly", + "displayName": "Configure and Build Library-Only for Debug and Release (no testing)", + "steps": [ + { + "type": "configure", + "name": "libonly" + }, + { + "type": "build", + "name": "libonly-release" + }, + { + "type": "build", + "name": "libonly-debug" + } + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 8bff0df..2d2eed3 100644 --- a/README.md +++ b/README.md @@ -1 +1,59 @@ -# libstrategy \ No newline at end of file +# libstrategy + +Library for learning strategies. + +## Dependencies + +Library-only build requires: +- [ptrie](https://github.com/petergjoel/ptrie) (CMake will fetch automatically if not installed) +- [nlohmann_json](https://github.com/nlohmann/json) (CMake will fetch automatically if not installed) + +Full project requires: +- [Boost](https://boost.org): [program_options](https://www.boost.org/library/latest/program_options/), [test](https://www.boost.org/library/latest/test/) + +For Ubuntu 24.04 install build tools and library dependencies: +```shell +sudo apt install cmake ninja-build g++ libboost-program-options-dev libboost-test-dev +``` + +For macOS install build tools and library dependencies: +```shell +brew install cmake ninja boost +``` + +## Compile and Install +Run minimal compilation (just the library) with `Debug` and `Release` settings into `build-libonly/lib`: +```shell +cmake --workflow libonly +``` +Install the `Release` build of `build-libonly` into `$PWD/local` path: +```shell +cmake --install build-libonly --config Release --prefix $PWD/local +``` + +## Other Presets +Inspect workflow presets: +```shell +cmake --workflow --list-presets +``` + +For example, configure, build and **test** for Development with **Sanitizers** (GCC/Clang/AppleClang): +```shell +cmake --workflow debug-san +``` + +Other configuration presets: +```shell +cmake --list-presets=configure +``` + +Other build presets: +```shell +cmake --list-presets=build +``` + +Other test presets: +```shell +cmake --list-presets=test +``` + diff --git a/cmake/CommonPresets.json b/cmake/CommonPresets.json new file mode 100644 index 0000000..0f6a6fe --- /dev/null +++ b/cmake/CommonPresets.json @@ -0,0 +1,453 @@ +{ + "version": 8, + "cmakeMinimumRequired": { + "major": 3, + "minor": 28, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "binaryDir": "${sourceDir}/build", + "displayName": "Configure with Defaults (Debug)" + }, + { + "name": "multi", + "inherits": "default", + "binaryDir": "${sourceDir}/build-multi", + "generator": "Ninja Multi-Config", + "displayName": "Configure for Ninja Debug/Release" + }, + { + "name": "multi-san", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-multi-san", + "cacheVariables": { + "UBSAN" : { "type": "BOOL", "value": "ON"}, + "ASAN" : { "type": "BOOL", "value": "ON"}, + "SSP" : { "type": "BOOL", "value": "ON"} + }, + "displayName": "Configure for Ninja Debug/Release with Sanitizers" + }, + { + "name": "gcc", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-gcc", + "environment": { + "CC": "gcc", + "CXX": "g++" + }, + "displayName": "Configure GCC for Ninja Debug/Release" + }, + { + "name": "clang", + "inherits": "multi", + "binaryDir": "${sourceDir}/build-clang", + "environment": { + "CC": "clang", + "CXX": "clang++" + }, + "displayName": "Configure Clang for Ninja Debug/Release" + }, + { + "name": "clang-san", + "inherits": "multi-san", + "binaryDir": "${sourceDir}/build-clang-san", + "environment": { + "CC": "clang", + "CXX": "clang++" + }, + "displayName": "Configure Clang for Ninja Debug/Release with Sanitizers" + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default", + "displayName": "Build using default configuration" + }, + { + "name": "debug", + "configurePreset": "multi", + "configuration": "Debug", + "displayName": "Build with Debug" + }, + { + "name": "debug-san", + "inherits": "debug", + "configurePreset": "multi-san", + "displayName": "Build with Debug and Sanitizers" + }, + { + "name": "debug-gcc", + "inherits": "debug", + "configurePreset": "gcc", + "displayName": "Build with GCC Debug" + }, + { + "name": "debug-clang", + "inherits": "debug", + "configurePreset": "clang", + "displayName": "Build with Clang Debug" + }, + { + "name": "debug-clang-san", + "inherits": "debug-san", + "configurePreset": "clang-san", + "displayName": "Build with Clang Debug and Sanitizers" + }, + { + "name": "release", + "configurePreset": "multi", + "configuration": "Release", + "displayName": "Build with Release" + }, + { + "name": "release-san", + "inherits": "release", + "configurePreset": "multi-san", + "displayName": "Build with Release and Sanitizers" + }, + { + "name": "release-gcc", + "inherits": "release", + "configurePreset": "gcc", + "displayName": "Build with GCC Release" + }, + { + "name": "release-clang", + "inherits": "release", + "configurePreset": "clang", + "displayName": "Build with Clang Release" + }, + { + "name": "release-clang-san", + "inherits": "release-san", + "configurePreset": "clang-san", + "displayName": "Build with Clang Release and Sanitizers" + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "default", + "output": { "outputOnFailure": true }, + "execution": { "noTestsAction": "error", "stopOnFailure": true }, + "displayName": "Test with default options" + }, + { + "name": "debug", + "inherits": "default", + "configurePreset": "multi", + "configuration": "Debug", + "displayName": "Test the Debug" + }, + { + "name": "debug-san", + "inherits": "debug", + "configurePreset": "multi-san", + "displayName": "Test the Debug with Sanitizers" + }, + { + "name": "debug-gcc", + "inherits": "debug", + "configurePreset": "gcc", + "displayName": "Test the GCC Debug" + }, + { + "name": "debug-clang", + "inherits": "debug", + "configurePreset": "clang", + "displayName": "Test the Clang Debug" + }, + { + "name": "debug-clang-san", + "inherits": "debug-san", + "configurePreset": "clang-san", + "displayName": "Test the Clang Debug and Sanitizers" + }, + { + "name": "release", + "inherits": "default", + "configurePreset": "multi", + "configuration": "Release", + "displayName": "Test the Release" + }, + { + "name": "release-san", + "inherits": "release", + "configurePreset": "multi-san", + "displayName": "Test the Release with Sanitizers" + }, + { + "name": "release-gcc", + "inherits": "release", + "configurePreset": "gcc", + "displayName": "Test the GCC Release" + }, + { + "name": "release-clang", + "inherits": "release", + "configurePreset": "clang", + "displayName": "Test the Clang Release" + }, + { + "name": "release-clang-san", + "inherits": "release-san", + "configurePreset": "clang-san", + "displayName": "Test the Clang Release with Sanitizers" + } + ], + "workflowPresets": [ + { + "name": "default", + "displayName": "Configure, Build and Test with Defaults (Debug)", + "steps": [ + { + "type": "configure", + "name": "default" + }, + { + "type": "build", + "name": "default" + }, + { + "type": "test", + "name": "default" + } + ] + }, + { + "name": "debug", + "displayName": "Configure, Build and Test with Debug", + "steps": [ + { + "type": "configure", + "name": "multi" + }, + { + "type": "build", + "name": "debug" + }, + { + "type": "test", + "name": "debug" + } + ] + }, + { + "name": "debug-san", + "displayName": "Configure, Build and Test with Debug and Sanitizers", + "steps": [ + { + "type": "configure", + "name": "multi-san" + }, + { + "type": "build", + "name": "debug-san" + }, + { + "type": "test", + "name": "debug-san" + } + ] + }, + { + "name": "debug-gcc", + "displayName": "Configure, Build and Test with Debug using GCC", + "steps": [ + { + "type": "configure", + "name": "gcc" + }, + { + "type": "build", + "name": "debug-gcc" + }, + { + "type": "test", + "name": "debug-gcc" + } + ] + }, + { + "name": "debug-clang", + "displayName": "Configure, Build and Test with Debug using Clang", + "steps": [ + { + "type": "configure", + "name": "clang" + }, + { + "type": "build", + "name": "debug-clang" + }, + { + "type": "test", + "name": "debug-clang" + } + ] + }, + { + "name": "debug-clang-san", + "displayName": "Configure, Build and Test with Debug and Sanitizers using Clang", + "steps": [ + { + "type": "configure", + "name": "clang-san" + }, + { + "type": "build", + "name": "debug-clang-san" + }, + { + "type": "test", + "name": "debug-clang-san" + } + ] + }, + { + "name": "release", + "displayName": "Configure, Build and Test with Release", + "steps": [ + { + "type": "configure", + "name": "multi" + }, + { + "type": "build", + "name": "release" + }, + { + "type": "test", + "name": "release" + } + ] + }, + { + "name": "release-san", + "displayName": "Configure, Build and Test with Release and Sanitizers", + "steps": [ + { + "type": "configure", + "name": "multi-san" + }, + { + "type": "build", + "name": "release-san" + }, + { + "type": "test", + "name": "release-san" + } + ] + }, + { + "name": "release-gcc", + "displayName": "Configure, Build and Test with Release using GCC", + "steps": [ + { + "type": "configure", + "name": "gcc" + }, + { + "type": "build", + "name": "release-gcc" + }, + { + "type": "test", + "name": "release-gcc" + } + ] + }, + { + "name": "release-clang", + "displayName": "Configure, Build and Test with Release using Clang", + "steps": [ + { + "type": "configure", + "name": "clang" + }, + { + "type": "build", + "name": "release-clang" + }, + { + "type": "test", + "name": "release-clang" + } + ] + }, + { + "name": "release-clang-san", + "displayName": "Configure, Build and Test with Release and Sanitizers using Clang", + "steps": [ + { + "type": "configure", + "name": "clang-san" + }, + { + "type": "build", + "name": "release-clang-san" + }, + { + "type": "test", + "name": "release-clang-san" + } + ] + }, + { + "name": "multi", + "displayName": "Configure, Build and Test with Debug and Release", + "steps": [ + { + "type": "configure", + "name": "multi" + }, + { + "type": "build", + "name": "debug" + }, + { + "type": "test", + "name": "debug" + }, + { + "type": "build", + "name": "release" + }, + { + "type": "test", + "name": "release" + } + ] + }, + { + "name": "multi-san", + "displayName": "Configure, Build and Test with Debug and Release and Sanitizers", + "steps": [ + { + "type": "configure", + "name": "multi-san" + }, + { + "type": "build", + "name": "debug-san" + }, + { + "type": "test", + "name": "debug-san" + }, + { + "type": "build", + "name": "release-san" + }, + { + "type": "test", + "name": "release-san" + } + ] + } + ] +} diff --git a/cmake/nlohmann_json.cmake b/cmake/nlohmann_json.cmake new file mode 100644 index 0000000..e792b7f --- /dev/null +++ b/cmake/nlohmann_json.cmake @@ -0,0 +1,30 @@ +find_package(nlohmann_json 3.7.3 QUIET) +if (nlohmann_json_FOUND) + get_target_property(nlohmann_json_INCLUDE_DIRS nlohmann_json::nlohmann_json INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "Found nlohmann_json: ${nlohmann_json_INCLUDE_DIRS}") +else (nlohmann_json_FOUND) + message(STATUS "Failed to find nlohmann_json, going to fetch from source") + set(JSON_BuildTests OFF CACHE BOOL "Build the unit tests when BUILD_TESTING is enabled.") + set(JSON_CI OFF CACHE BOOL "Enable CI build targets.") + set(JSON_Diagnostics OFF CACHE BOOL "Use extended diagnostic messages.") + set(JSON_Diagnostic_Positions OFF CACHE BOOL "Enable diagnostic positions.") + set(JSON_GlobalUDLs ON CACHE BOOL "Place user-defined string literals in the global namespace.") + set(JSON_ImplicitConversions ON CACHE BOOL "Enable implicit conversions.") + set(JSON_DisableEnumSerialization OFF CACHE BOOL "Disable default integer enum serialization.") + set(JSON_LegacyDiscardedValueComparison OFF CACHE BOOL "Enable legacy discarded value comparison.") + set(JSON_Install OFF CACHE BOOL "Install CMake targets during install step.") + set(JSON_MultipleHeaders ON CACHE BOOL "Use non-amalgamated version of the library.") + set(JSON_SystemInclude OFF CACHE BOOL "Include as system headers (skip for clang-tidy).") + set(FETCHCONTENT_QUIET ON) + set(FETCHCONTENT_UPDATES_DISCONNECTED ON) + include(FetchContent) + FetchContent_Declare(nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.12.0 + GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + GIT_PROGRESS TRUE # show download progress in Ninja + FIND_PACKAGE_ARGS NAMES nlohmann_json + USES_TERMINAL_DOWNLOAD TRUE) + FetchContent_MakeAvailable(nlohmann_json) + message(STATUS "Got nlohmann_json: ${nlohmann_json_SOURCE_DIR}") +endif (nlohmann_json_FOUND) diff --git a/cmake/ptrie.cmake b/cmake/ptrie.cmake new file mode 100644 index 0000000..f189046 --- /dev/null +++ b/cmake/ptrie.cmake @@ -0,0 +1,26 @@ +find_package(ptrie 1.1.1 QUIET) +if (ptrie_FOUND) + get_target_property(ptrie_INCLUDE_DIRS ptrie::ptrie INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "Found ptrie: ${ptrie_INCLUDE_DIRS}") +else (ptrie_FOUND) + message(STATUS "Failed to find ptrie, going to fetch from source") + set(PTRIE_BuildTests OFF CACHE BOOL "Build the unit tests when BUILD_TESTING is enabled.") + set(PTRIE_BuildBenchmark OFF CACHE BOOL "Build the simple benchmark suite") + set(PTRIE_AddressSanitizer OFF CACHE BOOL "Enables address sanitization during compilation.") + set(PTRIE_GetDependencies OFF CACHE BOOL "Fetch external dependencies from web.") + set(FETCHCONTENT_QUIET ON) + set(FETCHCONTENT_UPDATES_DISCONNECTED ON) + include(FetchContent) + FetchContent_Declare(ptrie + GIT_REPOSITORY https://github.com/petergjoel/ptrie + GIT_TAG v1.1.1 + GIT_SHALLOW TRUE # download specific revision only (git clone --depth 1) + GIT_PROGRESS TRUE # show download progress in Ninja + FIND_PACKAGE_ARGS NAMES ptrie + USES_TERMINAL_DOWNLOAD TRUE) + FetchContent_MakeAvailable(ptrie) + message(STATUS "Got ptrie: ${ptrie_SOURCE_DIR}") + # Workaround until ptrie exports proper cmake config: + add_library(ptrie::ptrie INTERFACE IMPORTED GLOBAL) + target_include_directories(ptrie::ptrie INTERFACE ${ptrie_SOURCE_DIR}/src) +endif (ptrie_FOUND) diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake new file mode 100644 index 0000000..7471460 --- /dev/null +++ b/cmake/sanitizers.cmake @@ -0,0 +1,99 @@ +# Various sanitizers (runtime checks) for debugging +option(SSP "Stack Protection (GCC/Clang/ApplClang on Unix, MSVC on Windows)" OFF) +option(UBSAN "Undefined Behavior Sanitizer (GCC/Clang/AppleClang on Unix)" OFF) +option(ASAN "Address Sanitizer (GCC/Clang/AppleClang on Unix, MSVC on Windows)" OFF) +option(LSAN "Leak Sanitizer (GCC/Clang/AppleClang on Unix, MSVC on Windows)" OFF) +option(TSAN "Thread Sanitizer (GCC/Clang/AppleClang on Unix)" OFF) +option(RTC_C "Runtime Checks for Conversions (MSVC on Windows)" OFF) +option(RTC_S "Runtime Checks for Stack (MSVC on Windows)" OFF) +option(RTC_U "Runtime Checks for Uninitialized (MSVC on Windows)" OFF) + +if (SSP) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check + add_compile_options(/GS) + message(STATUS "Enabled Guard Stack") + else() + add_compile_options(-fstack-protector) + add_link_options(-fstack-protector) + message(STATUS "Enable Stack Protector") + endif () +endif(SSP) + +if (ASAN OR UBSAN OR LSAN OR TSAN) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + else() + add_compile_options(-fno-omit-frame-pointer) + add_link_options(-fno-omit-frame-pointer) + endif() +endif() + +if (UBSAN) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=undefined) + message(STATUS "Please see if MSVC supports undefined behavior sanitizer: https://learn.microsoft.com/en-us/cpp/sanitizers/asan") + else() + add_compile_options(-fsanitize=undefined) + add_link_options(-fsanitize=undefined) + endif() + message(STATUS "Enabled Undefined Behavior Sanitizer") +endif(UBSAN) + +if (ASAN) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=address) + else() + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) + endif() + message(STATUS "Enabled Address Sanitizer") +endif(ASAN) + +if (LSAN) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=leak) + else() + add_compile_options(-fsanitize=leak) + add_link_options(-fsanitize=leak) + endif() + message(STATUS "Enabled Leak Sanitizer") +endif(LSAN) + +if (TSAN) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/fsanitize=thread) + message(STATUS "Please see if MSVC supports thread sanitizer: https://learn.microsoft.com/en-us/cpp/sanitizers/asan") + else() + add_compile_options(-fsanitize=thread) + add_link_options(-fsanitize=thread) + endif() + message(STATUS "Enabled Thread Sanitizer") +endif(TSAN) + +if (RTC_C) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/RTCc) + add_compile_definitions(_ALLOW_RTCc_IN_STL) + message(STATUS "Enabled Runtime Check Conversions") + else() + message(WARNING "Runtime Check Conversions is not enabled for ${CMAKE_CXX_COMPILER_ID}") + endif() +endif(RTC_C) + +if (RTC_S) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/RTCs) + message(STATUS "Enabled Runtime Check Stack") + else() + message(WARNING "Runtime Check Stack is not enabled for ${CMAKE_CXX_COMPILER_ID}") + endif() +endif(RTC_S) + +if (RTC_U) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/RTCu) + message(STATUS "Enabled Runtime Check Uninitialized") + else() + message(WARNING "Runtime Check Uninitialized is not enabled for ${CMAKE_CXX_COMPILER_ID}") + endif() +endif(RTC_U) diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake new file mode 100644 index 0000000..409ab03 --- /dev/null +++ b/cmake/warnings.cmake @@ -0,0 +1,9 @@ +if (CMAKE_CXX_COMPILER_ID MATCHES GNU) + add_compile_options(-Wpedantic -Wall -Wextra) +elseif (CMAKE_CXX_COMPILER_ID MATCHES AppleClang) + add_compile_options(-Wpedantic -Wall -Wextra) +elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) + add_compile_options(-Wpedantic -Wall -Wextra) +elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) + add_compile_options(/W4) +endif () \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ac6d1b..a08a070 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,40 +1,19 @@ -cmake_minimum_required(VERSION 3.7) -project(libstrategy C CXX) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_INCLUDE_CURRENT_DIR ON) -option(LIBSTRATEGY_OnlyLibrary "Build only as library." OFF) +add_library(SimpleTree OBJECT SimpleTree.cpp) +target_link_libraries(SimpleTree PUBLIC ptrie::ptrie nlohmann_json::nlohmann_json) -if(NOT LIBSTRATEGY_OnlyLibrary) - cmake_policy(SET CMP0069 NEW) - cmake_policy(SET CMP0074 NEW) - - find_package(Boost 1.66 COMPONENTS program_options REQUIRED) - include_directories(${Boost_INCLUDE_DIR}) -endif() +add_library(strategy SHARED libz2s.cpp ZonotopStrategy.cpp) +target_link_libraries(strategy PUBLIC SimpleTree) -add_library(strategy SHARED ${HEADER_FILES} libz2s.cpp ZonotopStrategy.cpp SimpleTree.cpp) -if (STRATEGY_GetDependencies) - add_dependencies(strategy ptrie nlohmann_json) -endif (STRATEGY_GetDependencies) -target_include_directories (strategy PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -add_library(strategyStatic STATIC ${HEADER_FILES} libz2s.cpp ZonotopStrategy.cpp SimpleTree.cpp) -if (STRATEGY_GetDependencies) - add_dependencies(strategyStatic ptrie nlohmann_json) -endif (STRATEGY_GetDependencies) -target_include_directories (strategyStatic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(strategyStatic STATIC libz2s.cpp ZonotopStrategy.cpp) +target_link_libraries(strategyStatic PUBLIC SimpleTree) set_target_properties(strategyStatic PROPERTIES OUTPUT_NAME strategy) if(NOT LIBSTRATEGY_OnlyLibrary) - add_executable(z2s ${HEADER_FILES} main.cpp ZonotopStrategy.cpp SimpleTree.cpp) - if (STRATEGY_GetDependencies) - add_dependencies(z2s ptrie nlohmann_json) - endif (STRATEGY_GetDependencies) - target_link_libraries(z2s PRIVATE stdc++fs ${Boost_LIBRARIES}) + add_executable(z2s main.cpp ZonotopStrategy.cpp) + target_link_libraries(z2s PUBLIC SimpleTree Boost::program_options) endif() - -install(TARGETS strategy +install(TARGETS strategy strategyStatic RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) diff --git a/src/SimpleTree.cpp b/src/SimpleTree.cpp index b30ce34..12b18e9 100644 --- a/src/SimpleTree.cpp +++ b/src/SimpleTree.cpp @@ -1,155 +1,144 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - -/* +/* * File: SimpleTree.cpp * Author: Peter G. Jensen - * + * * Created on May 9, 2019, 10:21 PM */ #include "SimpleTree.h" #include "errors.h" +#include "utilities.hpp" + +#include #include -#include -#include -#include #include +#include using json = nlohmann::json; -const std::vector& SimpleTree::actions() const -{ - return _actions; -} +const std::vector& SimpleTree::actions() const { return _actions; } -SimpleTree SimpleTree::parse(std::istream& input, bool simplify, bool subsumption, double accuracy) +SimpleTree SimpleTree::parse(std::istream& input, bool simplify, bool subsumption, double accuracy) { - std::vector empty; + auto empty = std::vector{}; return parse(input, simplify, subsumption, accuracy, empty); } -SimpleTree SimpleTree::parse(std::istream& input, bool simplify, bool subsumption, double accuracy, std::vector& exactness) +SimpleTree SimpleTree::parse(std::istream& input, bool simplify [[maybe_unused]], bool subsumption, double accuracy, + std::vector& exactness) { auto raw = json::parse(input); - if(!raw.is_object()) + if (!raw.is_object()) throw base_error("Input JSON not well formatted"); - if(!raw["version"].is_number_float() || raw["version"].get() != 1.0) + if (!raw["version"].is_number_float() || raw["version"].get() != 1.0) throw base_error("Input version not supported"); - if(!raw["type"].is_string() || raw["type"].get() != "state->regressor") + if (!raw["type"].is_string() || raw["type"].get() != "state->regressor") throw base_error("Input type not supported"); - if(!raw["representation"].is_string() || raw["representation"].get() != "map") + if (!raw["representation"].is_string() || raw["representation"].get() != "map") throw base_error("Input representation not supported"); - - if(!raw["actions"].is_object()) + + if (!raw["actions"].is_object()) throw base_error("Expected actions object"); // store actions SimpleTree tree; - if(tree._root == nullptr) - { + if (tree._root == nullptr) { tree._root = std::make_shared(); tree._root->_limit = -std::numeric_limits::infinity(); tree._root->_cost = std::numeric_limits::infinity(); tree._root->_var = std::numeric_limits::max(); - } - for(auto it = raw["actions"].begin(); it != raw["actions"].end(); ++it) - { - auto id = atoi(it.key().c_str()); - if(id >= (int)tree._actions.size()) - tree._actions.resize(id+1); - tree._actions[id] = it.value().get(); } - - if(!raw["statevars"].is_array()) + for (const auto& [act_key, act_value] : raw["actions"].items()) { + auto id = std::stoi(act_key); + if (id >= static_cast(tree._actions.size())) + tree._actions.resize(id + 1); + tree._actions[id] = act_value.get(); + } + + if (!raw["statevars"].is_array()) throw base_error("Expected statevars as array"); tree._statevars = raw["statevars"].get>(); - - if(!raw["pointvars"].is_array()) + + if (!raw["pointvars"].is_array()) throw base_error("Expected pointvars as array"); tree._pointvars = raw["pointvars"].get>(); - - if(!raw["regressors"].is_object()) + if (!raw["regressors"].is_object()) throw base_error("Expected regressors as object"); - auto& regressors = raw["regressors"]; - int minim = -1; - typeof(raw) inf = "inf"; - - bool first_element = true; - for(auto it = regressors.begin(); it != regressors.end(); ++it) - { - auto key = parse_key(it.key()); - if(key.size() != tree._statevars.size() && (tree._statevars.size() != 0 || key[0] != 1.0)) - { - std::cerr << it.key() << std::endl; + auto minim = -1; + auto inf = json("inf"); // json string (whereas {"inf"} is json array!) + auto first_element = true; + for (const auto& [raw_key, raw_value] : raw["regressors"].items()) { + auto key = parse_key(raw_key); + if (key.size() != tree._statevars.size() && (!tree._statevars.empty() || key[0] != 1.0)) { + std::cerr << raw_key << std::endl; std::cerr << key.size() << std::endl; std::cerr << tree._statevars.size() << std::endl; - throw base_error("Expected cardinality of key does not match cardinality of statevars"); + throw base_error("Expected cardinality of key does not match cardinality " + "of statevars"); } - else if(tree._statevars.size() == 0) + if (tree._statevars.empty()) key.clear(); - - if(!it.value().is_object()) + if (!raw_value.is_object()) throw base_error("Expected regressor-element as object"); - auto& reg = it.value(); - if(reg["type"].get() != "act->point->val") + if (raw_value["type"].get() != "act->point->val") throw base_error("Expected regressor-element of type act->point->val"); - if(reg["representation"].get() != "simpletree") + if (raw_value["representation"].get() != "simpletree") throw base_error("Expected regressor-element represented as simpletree"); - bool is_minimize = reg["minimize"].is_boolean() ? reg["minimize"].get() : reg["minimize"].get(); - if(!reg["regressor"].is_object()) + bool is_minimize = raw_value["minimize"].is_boolean() ? raw_value["minimize"].get() + : raw_value["minimize"].get(); + if (!raw_value["regressor"].is_object()) throw base_error("Expected regressor-sub-element as an object"); - if(minim == -1) + if (minim == -1) minim = is_minimize; - if(minim != is_minimize) + if (minim != is_minimize) throw base_error("Expected all sub-regressors to have same minimization flag"); - if(first_element) + if (first_element) tree._root->_cost = (is_minimize ? 1 : -1) * std::numeric_limits::infinity(); first_element = false; // make sure all actions are mapped initially - for(size_t i = 0; i < tree._actions.size(); ++i) + for (size_t i = 0; i < tree._actions.size(); ++i) tree._root->insert(key, inf, i, tree, 0, is_minimize, 0, exactness); - for(auto iit = reg["regressor"].begin(); iit != reg["regressor"].end(); ++iit) - { - size_t action = atoi(iit.key().c_str()); - if(action >= tree._actions.size()) + for (const auto& [reg_key, reg_value] : raw_value["regressor"].items()) { + auto action = std::stoi(reg_key); + if (action >= static_cast(tree._actions.size())) throw base_error("Action-index is out of bounds"); - - if(tree._root == nullptr) - { + + if (tree._root == nullptr) { tree._root = std::make_shared(); tree._root->_limit = -std::numeric_limits::infinity(); tree._root->_cost = std::numeric_limits::infinity(); tree._root->_cost = (is_minimize ? 1 : -1) * std::numeric_limits::infinity(); tree._root->_var = std::numeric_limits::max(); } - - auto obj = iit.value(); - tree._root->insert(key, obj, action, tree, 0, is_minimize, accuracy, exactness); + + tree._root->insert(key, reg_value, action, tree, 0, is_minimize, accuracy, exactness); } } tree._is_minimization = minim != 0; - if(subsumption) tree._root->subsumption_reduction(minim, tree); + if (subsumption) + tree._root->subsumption_reduction(minim, tree); /*if(simplify) // disabled for now { nodemap_t nodemap; @@ -161,359 +150,308 @@ SimpleTree SimpleTree::parse(std::istream& input, bool simplify, bool subsumptio return tree; } -std::vector SimpleTree::parse_key(const std::string& key) { - size_t i = 0; - std::vector res; - if(key[i] != '(') - { - throw base_error("Key is incorrectly formatted"); - } - ++i; - while(i < key.size() && key[i] != ')') - { - std::string ok(key.begin() + i, key.end()); - std::istringstream ss(ok); - { - std::string num; - std::getline(ss, num, ','); - std::istringstream ns(num); - res.emplace_back(); - ns >> res.back(); - i += num.length() + 1; - } - } - return res; -} - -std::pair SimpleTree::node_t::compute_min_max() { +std::pair SimpleTree::node_t::compute_min_max() const +{ double mincost = std::numeric_limits::infinity(); double maxcost = -mincost; - if(_low) - { - auto r = _low->compute_min_max(); - mincost = std::min(r.first, mincost); - maxcost = std::max(r.second, maxcost); + if (_low) { + const auto [r_min, r_max] = _low->compute_min_max(); + mincost = std::min(r_min, mincost); + maxcost = std::max(r_max, maxcost); } - if(_high) - { - auto r = _high->compute_min_max(); - mincost = std::min(r.first, mincost); - maxcost = std::max(r.second, maxcost); + if (_high) { + const auto [r_min, r_max] = _high->compute_min_max(); + mincost = std::min(r_min, mincost); + maxcost = std::max(r_max, maxcost); } - - if(is_leaf() && !std::isinf(_cost)) - { + + if (is_leaf() && !std::isinf(_cost)) { mincost = std::min(_cost, mincost); maxcost = std::max(_cost, maxcost); } return std::make_pair(mincost, maxcost); } -void SimpleTree::node_t::action_nodes(std::vector>& nodes, uint32_t low, uint32_t high, uint32_t varid) { - if(_var != varid) - { - if(!is_leaf() || !(std::isnan(_cost) || std::isinf(_cost))) - { +void SimpleTree::node_t::action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid) +{ + if (_var != varid) { + if (!is_leaf() || !(std::isnan(_cost) || std::isinf(_cost))) { auto ptr = shared_from_this(); auto lb = std::lower_bound(nodes.begin(), nodes.end(), ptr); - if(lb == nodes.end() || (*lb).get() != this) + if (lb == nodes.end() || lb->get() != this) nodes.insert(lb, ptr); } - } - else - { - if(_low) + } else { + if (_low) _low->action_nodes(nodes, low, _limit, varid); - if(_high) - _high->action_nodes(nodes, _limit+1, high, varid); + if (_high) + _high->action_nodes(nodes, _limit + 1, high, varid); } } -void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& parent) +void SimpleTree::node_t::subsumption_reduction(bool minimization, SimpleTree& parent) { - if(_var < parent._statevars.size()) - { - if(_low) + if (_var < parent._statevars.size()) { + if (_low) _low->subsumption_reduction(minimization, parent); - if(_high) + if (_high) _high->subsumption_reduction(minimization, parent); - } - else - { - if(_var != parent._statevars.size()) - { + } else { + if (_var != parent._statevars.size()) { return; } size_t prev_vals = std::numeric_limits::max(); size_t p = 0; - while(true) - { + while (true) { ++p; bool outer = false; - std::vector> nodes; - action_nodes(nodes, 0, parent._actions.size()-1, parent._statevars.size()); + std::vector nodes; + action_nodes(nodes, 0, parent._actions.size() - 1, parent._statevars.size()); auto val = std::numeric_limits::infinity(); - if(!minimization) val *= -1; + if (!minimization) + val *= -1; auto best = val; auto worst = -val; - for(auto& n : nodes) - { - auto mm = n->compute_min_max(); - if(std::isinf(mm.first)) continue; - assert(!std::isinf(mm.second)); - if(minimization) - { - val = std::min(val, mm.second); - best = std::min(best, mm.first); - worst = std::max(worst, mm.second); - } - else - { - val = std::max(val, mm.first); - best = std::max(best, mm.second); - worst = std::min(worst, mm.first); + for (const auto& n : nodes) { + auto [min, max] = n->compute_min_max(); + if (std::isinf(min)) + continue; + assert(!std::isinf(max)); + if (minimization) { + val = std::min(val, max); + best = std::min(best, min); + worst = std::max(worst, max); + } else { + val = std::max(val, min); + best = std::max(best, max); + worst = std::min(worst, min); } } - std::vector> bounds(parent._pointvars.size(), std::make_pair(-std::numeric_limits::infinity(), std::numeric_limits::infinity())); - for(auto& n : nodes) - { - outer |= n->check_tiles(n.get(), nodes, bounds, val, best, worst, minimization, parent._statevars.size() + 1); + std::vector> bounds( + parent._pointvars.size(), + std::make_pair(-std::numeric_limits::infinity(), std::numeric_limits::infinity())); + for (const auto& n : nodes) { + outer |= n->check_tiles(n.get(), nodes, bounds, val, best, worst, minimization, + parent._statevars.size() + 1); } - std::set> values; + std::set> values; { - std::vector> nodes(parent._actions.size()); - action_nodes(nodes, 0, parent._actions.size()-1, parent._statevars.size()); - for(auto& n : nodes) - { - if(n == nullptr) continue; + auto nodes = std::vector(parent._actions.size()); + action_nodes(nodes, 0, parent._actions.size() - 1, parent._statevars.size()); + for (auto& n : nodes) { + if (n == nullptr) + continue; get_ranks(values, n.get()); } - std::unordered_map replace; - for(auto& e : values) - { - if(replace.count(e.first) > 0) continue; + std::unordered_map replace; + for (auto& e : values) { + if (replace.count(e.first) > 0) + continue; double id = replace.size(); replace[e.first] = id; - // std::cerr << e.first << " : " << e.second << std::endl; + // std::cerr << e.first << " : " << e.second << std::endl; } set_ranks(replace); } - if(!outer && prev_vals <= values.size()) break; + if (!outer && prev_vals <= values.size()) + break; prev_vals = values.size(); } } } -void SimpleTree::node_t::set_ranks(std::unordered_map& values) { - if(is_leaf()) - { - if(std::isinf(_cost) || std::isnan(_cost)) return; +void SimpleTree::node_t::set_ranks(std::unordered_map& values) +{ + if (is_leaf()) { + if (std::isinf(_cost) || std::isnan(_cost)) + return; _cost = values[_cost]; - } - else - { - if(_low) _low->set_ranks(values); - if(_high) _high->set_ranks(values); + } else { + if (_low) + _low->set_ranks(values); + if (_high) + _high->set_ranks(values); } } - -void SimpleTree::node_t::get_ranks(std::set >& values, node_t* start) +void SimpleTree::node_t::get_ranks(std::set>& values, node_t* start) { - if(is_leaf()) - { - if(std::isinf(_cost) || std::isnan(_cost)) return; + if (is_leaf()) { + if (std::isinf(_cost) || std::isnan(_cost)) + return; values.emplace(_cost, start); - } - else - { - if(_low) _low->get_ranks(values, start); - if(_high) _high->get_ranks(values, start); + } else { + if (_low) + _low->get_ranks(values, start); + if (_high) + _high->get_ranks(values, start); } } - -bool SimpleTree::node_t::subsumes(const std::vector >& bounds, std::vector >& obounds, const double val, const bool minimization, size_t offset, double& best, std::pair& closest) { - if(is_leaf()) - { - // TODO: continue in other trees here, maybe combination is +bool SimpleTree::node_t::subsumes(const std::vector>& bounds, + std::vector>& obounds, const double val, + const bool minimization, size_t offset, double& best, + std::pair& closest) const +{ + if (is_leaf()) { + // TODO: continue in other trees here, maybe combination is // enough to prove subsumption! - if(_cost <= val) closest.first = std::max(_cost, closest.first); - if(_cost >= val) closest.second = std::min(_cost, closest.second); - if(minimization) best = std::min(_cost, best); - else best = std::max(_cost, best); - if(minimization && _cost <= val) return true; - else if(!minimization && _cost >= val) return true; + if (_cost <= val) + closest.first = std::max(_cost, closest.first); + if (_cost >= val) + closest.second = std::min(_cost, closest.second); + if (minimization) + best = std::min(_cost, best); + else + best = std::max(_cost, best); + if (minimization && _cost <= val) + return true; + if (!minimization && _cost >= val) + return true; return false; - } - else - { + } else { /*if(minimization && _maxcost <= val) { best = std::min(_maxcost, best); return true; } - else if(!minimization && _mincost >= val) + else if(!minimization && _mincost >= val) { best = std::max(_mincost, best); return true; }*/ assert(std::isinf(_cost)); assert(_var >= offset); - assert(bounds[_var-offset].first <= bounds[_var-offset].second); + assert(bounds[_var - offset].first <= bounds[_var - offset].second); double org = _limit; - std::swap(obounds[_var-offset].second, org); + std::swap(obounds[_var - offset].second, org); bool res = true; - if(bounds[_var-offset].first <= _limit && _low) - if(!_low->subsumes(bounds, obounds, val, minimization, offset, best, closest)) + if (bounds[_var - offset].first <= _limit && _low) + if (!_low->subsumes(bounds, obounds, val, minimization, offset, best, closest)) res = false; - std::swap(obounds[_var-offset].second, org); - std::swap(obounds[_var-offset].first, org); - if(bounds[_var-offset].second > _limit && _high) - if(!_high->subsumes(bounds, obounds, val, minimization, offset, best, closest)) + std::swap(obounds[_var - offset].second, org); + std::swap(obounds[_var - offset].first, org); + if (bounds[_var - offset].second > _limit && _high) + if (!_high->subsumes(bounds, obounds, val, minimization, offset, best, closest)) res = false; - std::swap(obounds[_var-offset].first, org); + std::swap(obounds[_var - offset].first, org); return res; } } -bool SimpleTree::node_t::check_tiles(node_t* start, std::vector >& nodes, std::vector >& bounds, double val, - double best_val, double worst_val, bool minimization, size_t offset) +bool SimpleTree::node_t::check_tiles(node_t* start, std::vector& nodes, + std::vector>& bounds, double val, double best_val, + double worst_val, bool minimization, size_t offset) { auto obounds = bounds; - if(is_leaf()) - { + if (is_leaf()) { double best = (minimization ? 1 : -1) * std::numeric_limits::infinity(); _cost_bounds.first = -std::numeric_limits::infinity(); _cost_bounds.second = std::numeric_limits::infinity(); - if(_cost == (minimization ? 1 : -1) * std::numeric_limits::infinity()) + if (_cost == (minimization ? 1 : -1) * std::numeric_limits::infinity()) return false; - for(auto& n : nodes) - { - if(n.get() == start || n == nullptr) continue; - if(n->subsumes(bounds, obounds, _cost, minimization, offset, best, _cost_bounds)) - { + for (auto& n : nodes) { + if (n.get() == start || n == nullptr) + continue; + if (n->subsumes(bounds, obounds, _cost, minimization, offset, best, _cost_bounds)) { _cost = (minimization ? 1 : -1) * std::numeric_limits::infinity(); _cost_bounds.second = std::numeric_limits::infinity(); return true; } - assert(best <= best_val || minimization); // doing max - assert(best >= best_val || !minimization); // doing min + assert(best <= best_val || minimization); // doing max + assert(best >= best_val || !minimization); // doing min } - assert(best <= best_val || minimization); // doing max - assert(best >= best_val || !minimization); // doing min - if((minimization && best >= _cost) || - (!minimization && best <= _cost)) - { - //std::cerr << "BEST " << _cost << " MV " << minval << " BND " << _cost_bounds.first << ": " << _cost_bounds.second << std::endl; + assert(best <= best_val || minimization); // doing max + assert(best >= best_val || !minimization); // doing min + if ((minimization && best >= _cost) || (!minimization && best <= _cost)) { + // std::cerr << "BEST " << _cost << " MV " << minval << " BND " << + // _cost_bounds.first << ": " << _cost_bounds.second << std::endl; _cost = best_val; - } - else - { - if(!std::isinf(_cost_bounds.first)) - { + } else { + if (!std::isinf(_cost_bounds.first)) { _cost = std::nextafter(_cost_bounds.first, std::numeric_limits::infinity()); - if(!std::isinf(_cost_bounds.second) && std::ceil(_cost) < _cost_bounds.second) + if (!std::isinf(_cost_bounds.second) && std::ceil(_cost) < _cost_bounds.second) _cost = std::ceil(_cost); - } - else if(!std::isinf(_cost_bounds.second)) - { + } else if (!std::isinf(_cost_bounds.second)) { _cost = std::nextafter(_cost_bounds.second, -std::numeric_limits::infinity()); } } return false; } - - if(!is_leaf()) { + + if (!is_leaf()) { double org = _limit; - auto& bnd = bounds[_var- offset]; - std::shared_ptr switchnode = nullptr; - if(bnd.second <= _limit) - { + auto& bnd = bounds[_var - offset]; + node_ptr switchnode = nullptr; + if (bnd.second <= _limit) { switchnode = _low; } - if(bnd.first > _limit) - { + if (bnd.first > _limit) { switchnode = _high; } -/* std::cerr << "HANDLE " << std::endl; - print(std::cerr); - std::cerr << std::endl;*/ + /* std::cerr << "HANDLE " << std::endl; + print(std::cerr); + std::cerr << std::endl;*/ bool res = false; - if(switchnode) - { + if (switchnode) { _var = switchnode->_var; _limit = switchnode->_limit; _cost = switchnode->_cost; _low = switchnode->_low; _high = switchnode->_high; return check_tiles(start, nodes, bounds, val, best_val, worst_val, minimization, offset); - } - else - { + } else { std::swap(org, bnd.second); - if(_low) + if (_low) res |= _low->check_tiles(start, nodes, bounds, val, best_val, worst_val, minimization, offset); std::swap(org, bnd.second); std::swap(org, bnd.first); - if(_high) + if (_high) res |= _high->check_tiles(start, nodes, bounds, val, best_val, worst_val, minimization, offset); std::swap(org, bnd.first); } _cost_bounds.first = std::max(_low->_cost_bounds.first, _high->_cost_bounds.first); _cost_bounds.second = std::min(_low->_cost_bounds.second, _high->_cost_bounds.second); - // if both are leafs and can be merged. - if(_low->is_leaf() && _high->is_leaf() && _low->cost_intersect(*_high) && - std::isinf(_low->_cost) == std::isinf(_high->_cost)) - { - /*std::cerr << "[" << _low->_cost_bounds.first << ", " << _low->_cost_bounds.second << "]" << std::endl; - std::cerr << "[" << _high->_cost_bounds.first << ", " << _high->_cost_bounds.second << "]" << std::endl; - std::cerr << "[" << _cost_bounds.first << ", " << _cost_bounds.second << "]" << std::endl; - std::cerr << "FIXING " << std::endl; - print(std::cerr); - std::cerr << std::endl;*/ + // if both are leafs and can be merged. + if (_low->is_leaf() && _high->is_leaf() && _low->cost_intersect(*_high) && + std::isinf(_low->_cost) == std::isinf(_high->_cost)) { + /*std::cerr << "[" << _low->_cost_bounds.first << ", " << + _low->_cost_bounds.second << "]" << std::endl; std::cerr << "[" << + _high->_cost_bounds.first << ", " << _high->_cost_bounds.second << "]" << + std::endl; std::cerr << "[" << _cost_bounds.first << ", " << + _cost_bounds.second << "]" << std::endl; std::cerr << "FIXING " << + std::endl; print(std::cerr); std::cerr << std::endl;*/ _cost = _low->midcost(*_high, best_val, worst_val); - //std::cerr << "NC " << _cost << std::endl; + // std::cerr << "NC " << _cost << std::endl; _low = nullptr; _high = nullptr; - _var = std::numeric_limits::max(); + _var = std::numeric_limits::max(); _limit = std::numeric_limits::infinity(); return true; - //std::cerr << "REMO 2" << std::endl; + // std::cerr << "REMO 2" << std::endl; } { // check if one is leaf and sibiling has leaf on same side which // can be merged: double nc = 0; - std::shared_ptr node, other; - if(!_high->is_leaf() && - _high->_var == _var && - _high->_low->cost_intersect(*_low) && - _low->is_leaf() && - _high->_low->is_leaf() && - std::isinf(_low->_cost) == std::isinf(_high->_low->_cost)) - { + node_ptr node, other; + if (!_high->is_leaf() && _high->_var == _var && _high->_low->cost_intersect(*_low) && _low->is_leaf() && + _high->_low->is_leaf() && std::isinf(_low->_cost) == std::isinf(_high->_low->_cost)) { node = _high; other = _high->_low; nc = other->midcost(*_low, best_val, worst_val); assert(!std::isnan(nc)); - } - else if( !_low->is_leaf() && - _low->_var == _var && - _low->_high->cost_intersect(*_high) && - _high->is_leaf() && - _low->_high->is_leaf() && - std::isinf(_high->_cost) == std::isinf(_low->_high->_cost)) - { + } else if (!_low->is_leaf() && _low->_var == _var && _low->_high->cost_intersect(*_high) && + _high->is_leaf() && _low->_high->is_leaf() && + std::isinf(_high->_cost) == std::isinf(_low->_high->_cost)) { node = _low; other = _low->_high; nc = other->midcost(*_high, best_val, worst_val); assert(!std::isnan(nc)); } - - if(node) - { + + if (node) { _var = node->_var; _limit = node->_limit; _low = node->_low; @@ -523,13 +461,10 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vectoris_leaf() && !_high->is_leaf() && - _low->_var == _high->_var && _low->_var == _var && - _low->_high->is_leaf() && _high->_low->is_leaf() && - _low->_high->cost_intersect(*_high->_low) && - std::isinf(_low->_high->_cost) == std::isinf(_high->_low->_cost)) - { + + if (!_low->is_leaf() && !_high->is_leaf() && _low->_var == _high->_var && _low->_var == _var && + _low->_high->is_leaf() && _high->_low->is_leaf() && _low->_high->cost_intersect(*_high->_low) && + std::isinf(_low->_high->_cost) == std::isinf(_high->_low->_cost)) { assert(_high->_low->is_leaf()); assert(_low->_high->is_leaf()); _high->_low->_cost = _low->_high->midcost(*_high->_low, best_val, worst_val); @@ -540,7 +475,6 @@ bool SimpleTree::node_t::check_tiles(node_t* start, std::vector= high) - c = (low + high)/2; + else { + c = std::round(low + high / 2); + if (c <= low || c >= high) + c = (low + high) / 2; } return c; } - - - -void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, bool minimize, double accuracy, std::vector& exactness) -{ - if((tree.is_number() || tree.is_string()) && key.size() < prefix) - { - if(is_leaf()) - { +void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, + bool minimize, double accuracy, std::vector& exactness) +{ + if ((tree.is_number() || tree.is_string()) && key.size() < prefix) { + if (is_leaf()) { auto nc = std::numeric_limits::infinity(); - if(!minimize) + if (!minimize) nc *= -1; - if(tree.is_number()) + if (tree.is_number()) nc = tree.get(); - if(minimize) + if (minimize) _cost = std::isinf(_cost) ? nc : std::min(nc, _cost); else _cost = std::isinf(_cost) ? nc : std::max(nc, _cost); - if(accuracy != 0) - _cost = std::round(_cost/accuracy)*accuracy; - } - else - { + if (accuracy != 0) + _cost = std::round(_cost / accuracy) * accuracy; + } else { assert(false); _low->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); _high->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); } consistent(key.size() + 1); return; - } - else if(is_leaf()) - { - if(std::isinf(_limit)) - { - //assert(std::isinf(_cost)); + } else if (is_leaf()) { + if (std::isinf(_limit)) { + // assert(std::isinf(_cost)); assert(_var == std::numeric_limits::max()); _low = std::make_shared(); _high = std::make_shared(); _low->_cost = _high->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); - if(!std::isinf(_cost)) - { + if (!std::isinf(_cost)) { std::swap(_low->_cost, _cost); _high->_cost = _low->_cost; } _low->_parent = _high->_parent = this; - if(prefix == key.size()) - { + if (prefix == key.size()) { _var = prefix; _limit = action; - } - else if(prefix < key.size()) - { + } else if (prefix < key.size()) { _var = prefix; _limit = key[prefix]; - } - else - { - _var = key.size() + 1 + tree["var"].get(); - auto ivar = tree["var"].get(); + } else { + const auto& tree_var = tree["var"]; + _var = key.size() + 1 + tree_var.get(); + auto ivar = tree_var.get(); _limit = tree["bound"].get(); - if(exactness.size() > ivar && exactness[ivar] != 0) - _limit = std::round(_limit/exactness[ivar])*exactness[ivar]; + if (exactness.size() > ivar && exactness[ivar] != 0) + _limit = std::round(_limit / exactness[ivar]) * exactness[ivar]; } consistent(key.size() + 1); - if(prefix <= key.size()) - _low->insert(key, tree, action, parent, prefix+1, minimize, accuracy, exactness); - else - { - _low->insert(key, tree["low"], action, parent, prefix+1, minimize, accuracy, exactness); - _high->insert(key, tree["high"], action, parent, prefix+1, minimize, accuracy, exactness); + if (prefix <= key.size()) + _low->insert(key, tree, action, parent, prefix + 1, minimize, accuracy, exactness); + else { + _low->insert(key, tree["low"], action, parent, prefix + 1, minimize, accuracy, exactness); + _high->insert(key, tree["high"], action, parent, prefix + 1, minimize, accuracy, exactness); consistent(key.size() + 1); } - } - else - { + } else { assert(!std::isinf(_cost)); assert(_var != std::numeric_limits::max()); assert(false); @@ -655,96 +571,69 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act consistent(key.size() + 1); return; } - - if(prefix > key.size()) - { - size_t var = tree["var"].get() + 1 + key.size(); - if(_var != var) - { + + if (prefix > key.size()) { + if (size_t var = tree["var"].get() + 1 + key.size(); var != _var) { //_parent->replace(this, key, tree, action, parent, prefix); assert(false); - } - else - { + } else { size_t ivar = tree["var"].get(); double bound = tree["bound"].get(); - if(exactness.size() > ivar && exactness[ivar] != 0) - { - bound = std::round(bound/exactness[ivar])*exactness[ivar]; + if (exactness.size() > ivar && exactness[ivar] != 0) { + bound = std::round(bound / exactness[ivar]) * exactness[ivar]; } - if(_limit == bound) - { - _low->insert(key, tree["low"], action, parent, prefix+1, minimize, accuracy, exactness); - _high->insert(key, tree["high"], action, parent, prefix+1, minimize, accuracy, exactness); - } - else - { - if(_limit > bound) - { + if (_limit == bound) { + _low->insert(key, tree["low"], action, parent, prefix + 1, minimize, accuracy, exactness); + _high->insert(key, tree["high"], action, parent, prefix + 1, minimize, accuracy, exactness); + } else { + if (_limit > bound) { _low->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); _high->insert(key, tree["high"], action, parent, prefix + 1, minimize, accuracy, exactness); - } - else - { + } else { _high->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); _low->insert(key, tree["low"], action, parent, prefix + 1, minimize, accuracy, exactness); } } - } + } consistent(key.size() + 1); - } - else if(prefix == key.size()) - { - if(_var != prefix) - { + } else if (prefix == key.size()) { + if (_var != prefix) { assert(false); - } - else - { + } else { consistent(key.size() + 1); - if(_limit == action) - { + if (_limit == action) { _low->insert(key, tree, action, parent, prefix + 1, minimize, accuracy, exactness); consistent(key.size() + 1); - } - else - { + } else { assert(_low->_var > prefix); - if(_limit > action) - { + if (_limit > action) { auto tmp = std::make_shared(); tmp->_low = std::make_shared(); tmp->_cost = tmp->_low->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); tmp->_high = shared_from_this(); tmp->_limit = action; tmp->_var = prefix; - if(_parent) - { + if (_parent) { tmp->_parent = _parent; - if(_parent->_high.get() == this) + if (_parent->_high.get() == this) _parent->_high = tmp; else _parent->_low = tmp; - } - else - { + } else { parent._root = tmp; } tmp->_low->_parent = tmp->_high->_parent = tmp.get(); tmp->consistent(key.size() + 1); tmp->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); - } - else if(_high->_var == prefix && _high->_limit < action) - { + } else if (_high->_var == prefix && _high->_limit < action) { _high->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); consistent(key.size() + 1); - } - else if(_high->_var > prefix) - { + } else if (_high->_var > prefix) { assert(std::isinf(_high->_cost)); _high->_low = std::make_shared(); _high->_high = std::make_shared(); - _high->_low->_cost = _high->_high->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); + _high->_low->_cost = _high->_high->_cost = + (minimize ? 1 : -1) * std::numeric_limits::infinity(); _high->_limit = action; _high->_var = prefix; _high->_low->_parent = _high.get(); @@ -753,9 +642,7 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act _high->consistent(key.size() + 1); _high->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); consistent(key.size() + 1); - } - else if(_high->_var == prefix && _high->_limit > action) - { + } else if (_high->_var == prefix && _high->_limit > action) { auto tmp = std::make_shared(); tmp->_low = std::make_shared(); tmp->_low->_cost = tmp->_cost = (minimize ? 1 : -1) * std::numeric_limits::infinity(); @@ -769,38 +656,26 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act tmp->consistent(key.size() + 1); tmp->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); consistent(key.size() + 1); - } - else if(_high->_var == prefix && _high->_limit == action) - { + } else if (_high->_var == prefix && _high->_limit == action) { _high->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); - } - else - { + } else { assert(false); } } } consistent(key.size() + 1); - } - else if(prefix < key.size()) - { - if(_var != prefix) - { + } else if (prefix < key.size()) { + if (_var != prefix) { //_parent->replace(this, key, tree, action, parent, prefix); assert(false); - } - else - { - if(_limit == key[prefix]) - { - _low->insert(key, tree, action, parent, prefix + (_low->_var != _var ? 1 : 0), minimize, accuracy, exactness); - } - else - { + } else { + if (_limit == key[prefix]) { + _low->insert(key, tree, action, parent, prefix + (_low->_var != _var ? 1 : 0), minimize, accuracy, + exactness); + } else { const auto b = _limit < key[prefix]; auto branch = (*this)[b]; - if(branch->_var != prefix) - { + if (branch->_var != prefix) { // we need to inject a node here auto next = std::make_shared(); (*next)[false] = std::make_shared(); @@ -809,7 +684,7 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act next->_var = prefix; next->_limit = key[prefix]; // example - // original [0,10] | [11,20] made for element 10, + // original [0,10] | [11,20] made for element 10, // we inject element 7, so we now get // ([0-7] | [8-10] ) | [11,20 // where the original low is moved to the high @@ -819,8 +694,8 @@ void SimpleTree::node_t::insert(std::vector& key, json& tree, size_t act (*next)[true]->_parent = next.get(); (*next)[false]->_parent = next.get(); next->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); - } - else branch->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); + } else + branch->insert(key, tree, action, parent, prefix, minimize, accuracy, exactness); } } consistent(key.size() + 1); @@ -836,109 +711,94 @@ void SimpleTree::node_t::consistent(size_t prefix) const assert(_parent == nullptr || _parent->_low.get() == this || _parent->_high.get() == this); assert(_low == nullptr || _low->_parent == this); assert(_high == nullptr || _high->_parent == this); - if(_var == prefix-1 && _parent && _parent->_var == _var) - { + if (_var == prefix - 1 && _parent && _parent->_var == _var) { assert(_parent->_limit < _limit); } - if(_var < prefix) assert(_parent == nullptr || _parent->_var <= _var); - if(_low) _low->consistent(prefix); - if(_high) _high->consistent(prefix); + if (_var < prefix) + assert(_parent == nullptr || _parent->_var <= _var); + if (_low) + _low->consistent(prefix); + if (_high) + _high->consistent(prefix); #endif } - -double SimpleTree::value(const double* disc, const double* cont, uint32_t action) const { - if(_root) +double SimpleTree::value(const double* disc, const double* cont, uint32_t action) const +{ + if (_root) return _root->value(disc, cont, action, _statevars.size()); return std::numeric_limits::infinity(); } -double SimpleTree::node_t::value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const { - if(is_leaf()) +double SimpleTree::node_t::value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const +{ + if (is_leaf()) return _cost; - if(_var < ndisc) - { - if(disc[_var] <= _limit) - return _low ? _low->value(disc, cont, action, ndisc) : _cost; - else - return _high ? _high->value(disc, cont, action, ndisc) : _cost; - } - else if(_var == ndisc) - { - if(action <= _limit) + if (_var < ndisc) { + if (disc[_var] <= _limit) return _low ? _low->value(disc, cont, action, ndisc) : _cost; - else - return _high ? _high->value(disc, cont, action, ndisc) : _cost; + return _high ? _high->value(disc, cont, action, ndisc) : _cost; } - else - { - if(cont[_var - (ndisc+1)] <= _limit) + if (_var == ndisc) { + if (action <= _limit) return _low ? _low->value(disc, cont, action, ndisc) : _cost; - else - return _high ? _high->value(disc, cont, action, ndisc) : _cost; + return _high ? _high->value(disc, cont, action, ndisc) : _cost; } + if (cont[_var - (ndisc + 1)] <= _limit) + return _low ? _low->value(disc, cont, action, ndisc) : _cost; + return _high ? _high->value(disc, cont, action, ndisc) : _cost; } - - -std::shared_ptr SimpleTree::node_t::simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent) { - if(_low) _low = _low->simplify(make_dd, nodemap, parent); - if(_high) _high = _high->simplify(make_dd, nodemap, parent); - if(_low) +SimpleTree::node_ptr SimpleTree::node_t::simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent) +{ + if (_low) + _low = _low->simplify(make_dd, nodemap, parent); + if (_high) + _high = _high->simplify(make_dd, nodemap, parent); + if (_low) _low->_parent = this; - if(_high) + if (_high) _high->_parent = this; - if(_var < parent._statevars.size()) - { - if(_low && _low->is_leaf() && std::isinf(_low->_cost)) + if (_var < parent._statevars.size()) { + if (_low && _low->is_leaf() && std::isinf(_low->_cost)) return _high; - if(_high && _high->is_leaf() && std::isinf(_high->_cost)) + if (_high && _high->is_leaf() && std::isinf(_high->_cost)) return _low; } assert(_low.get() != this); assert(_high.get() != this); - if( (_low == nullptr || _low->is_leaf()) && - (_high == nullptr || _high->is_leaf())) - { + if ((_low == nullptr || _low->is_leaf()) && (_high == nullptr || _high->is_leaf())) { auto lc = _low ? _low->_cost : _cost; auto hc = _high ? _high->_cost : _cost; - if(lc == hc) - { + if (lc == hc) { _low = nullptr; _high = nullptr; _cost = lc; _limit = std::numeric_limits::infinity(); - _var = std::numeric_limits::max(); + _var = std::numeric_limits::max(); return shared_from_this(); } } - if(std::isinf(_limit) && (_low != nullptr || _high != nullptr)) - { + if (std::isinf(_limit) && (_low != nullptr || _high != nullptr)) { assert(false); return _low == nullptr ? _high : _low; } - + // if comparison on same variable in on child and other child is leaf, then // if the non-leaf child has a leaf-child in, we can merge. { - if(_low && _low->is_leaf() && - _high && (!_high->is_leaf() || !std::isinf(_high->_cost)) && - _high->_var == _var && _high->_low && _high->_low->is_leaf() && _high->_low->_cost == _low->_cost) - { + if (_low && _low->is_leaf() && _high && (!_high->is_leaf() || !std::isinf(_high->_cost)) && + _high->_var == _var && _high->_low && _high->_low->is_leaf() && _high->_low->_cost == _low->_cost) { return _high; } - if(_high && _high->is_leaf() && - _low && (!_low->is_leaf() || !std::isinf(_low->_cost)) && - _low->_var == _var && _low->_high && _low->_high->is_leaf() && _low->_high->_cost == _high->_cost) - { + if (_high && _high->is_leaf() && _low && (!_low->is_leaf() || !std::isinf(_low->_cost)) && _low->_var == _var && + _low->_high && _low->_high->is_leaf() && _low->_high->_cost == _high->_cost) { return _low; } } - - /*if(make_dd) { @@ -961,50 +821,23 @@ std::shared_ptr SimpleTree::node_t::simplify(bool make_dd, n return _high; } }*/ - if(_high != nullptr && _high == _low) - { + if (_high != nullptr && _high == _low) { return _high; } - if(make_dd) - { - auto& ptr = nodemap[*this]; - if(ptr == nullptr) + if (make_dd) { + auto& ptr = nodemap[*this]; + if (ptr == nullptr) ptr = shared_from_this(); return ptr; } return shared_from_this(); } -SimpleTree::signature_t::signature_t(const SimpleTree::node_t& node) -{ - if(node.is_leaf()) - { - _limit = node._limit; - _low = node._low.get(); - _high = node._high.get(); - _var = node._var; - } - else - { - _var = 0; - _low = nullptr; - _high = nullptr; - _limit = node._cost; - } -} - -bool SimpleTree::node_t::is_leaf() const { - return _low == nullptr && _high == nullptr; -} - -/*void SimpleTree::node_t::rec_insert(double value, std::vector>& handled, std::vector >& bounds, bool minimize) { - auto nid = _var; - assert(nid < bounds.size()); - bool reset_low = false; - bool reset_high = false; - bool best = false; - if( std::isnan(_cost) || - (minimize && value < _cost) || +/*void SimpleTree::node_t::rec_insert(double value, +std::vector>& handled, std::vector >& bounds, bool minimize) { auto nid = _var; assert(nid < +bounds.size()); bool reset_low = false; bool reset_high = false; bool best = +false; if( std::isnan(_cost) || (minimize && value < _cost) || (!minimize && value > _cost)) { _cost = value; @@ -1013,7 +846,7 @@ bool SimpleTree::node_t::is_leaf() const { if(bounds[_var].second == _limit && !handled[_var].second) reset_high = handled[_var].second = true; if(bounds[_var].first == _limit && !handled[_var].first) - reset_low = handled[_var].first = true; + reset_low = handled[_var].first = true; if(bounds[_var].second <= _limit) { if(handled[_var].first && handled[_var].second) @@ -1041,7 +874,8 @@ bool SimpleTree::node_t::is_leaf() const { _low = std::make_shared(); _low->_var = nid; _low->_parent = this; - _low->_limit = (!handled[nid].first ? bounds[nid].first : bounds[nid].second); + _low->_limit = (!handled[nid].first ? bounds[nid].first : +bounds[nid].second); } _low->rec_insert(value, handled, bounds, minimize); if(reset_low) handled[_var].first = false; @@ -1075,7 +909,8 @@ bool SimpleTree::node_t::is_leaf() const { _high = std::make_shared(); _high->_parent = this; _high->_var = nid; - _high->_limit = (!handled[nid].first ? bounds[nid].first : bounds[nid].second); + _high->_limit = (!handled[nid].first ? bounds[nid].first : +bounds[nid].second); } _high->rec_insert(value, handled, bounds, minimize); if(reset_low) handled[_var].first = false; @@ -1083,11 +918,9 @@ bool SimpleTree::node_t::is_leaf() const { } else { - if(best && handled[_var].first && handled[_var].second && _var == handled.size()-1) { - _low = nullptr; - _high = nullptr; - if(reset_low) handled[_var].first = false; - if(reset_high) handled[_var].second = false; + if(best && handled[_var].first && handled[_var].second && _var == +handled.size()-1) { _low = nullptr; _high = nullptr; if(reset_low) +handled[_var].first = false; if(reset_high) handled[_var].second = false; } if(is_leaf() && !best) { @@ -1099,14 +932,16 @@ bool SimpleTree::node_t::is_leaf() const { _low = std::make_shared(); _low->_var = nid; _low->_parent = this; - _low->_limit = (!handled[nid].first ? bounds[nid].first : bounds[nid].second); + _low->_limit = (!handled[nid].first ? bounds[nid].first : +bounds[nid].second); } if(_high == nullptr) { _high = std::make_shared(); _high->_parent = this; _high->_var = nid; - _high->_limit = (!handled[nid].first ? bounds[nid].first : bounds[nid].second); + _high->_limit = (!handled[nid].first ? bounds[nid].first : +bounds[nid].second); } _low->rec_insert(value, handled, bounds, minimize); @@ -1122,62 +957,59 @@ std::ostream& SimpleTree::print(std::ostream& stream) const return stream; } -std::ostream& SimpleTree::node_t::print(std::ostream& out, size_t tabs) const { - for(size_t i = 0; i < tabs; ++i) out << "\t"; +std::ostream& SimpleTree::node_t::print(std::ostream& out, size_t tabs) const +{ + for (size_t i = 0; i < tabs; ++i) + out << "\t"; out << "{\"var\":" << _var << ",\"bound\":" << _limit; - if(!std::isnan(_cost)) - { + if (!std::isnan(_cost)) { out << ",\"value\":" << _cost; } - if(_low) - { + if (_low) { out << ",\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; + for (size_t i = 0; i < tabs; ++i) + out << "\t"; out << "\"low\":\n"; _low->print(out, tabs + 1); } - if(_high) - { + if (_high) { out << ",\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; + for (size_t i = 0; i < tabs; ++i) + out << "\t"; out << "\"high\":\n"; _high->print(out, tabs + 1); } out << "\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; + for (size_t i = 0; i < tabs; ++i) + out << "\t"; out << "}"; return out; } -std::ostream& SimpleTree::print_c(std::ostream& stream, std::string name) const +std::ostream& SimpleTree::print_c(std::ostream& os, const std::string& name) const { - if(_root == nullptr) - { - stream << "double " << name << "(unsigned int action, const double* disc, const double* vars)\n{\n"; - stream << "\treturn 0 ; \n}\n"; - return stream; + if (_root == nullptr) { + os << "double " << name << "(unsigned int action, const double* disc, const double* vars)\n{\n"; + os << "\treturn 0 ; \n}\n"; + return os; } - std::unordered_map _idnode; - std::unordered_map _nodeid; + auto _idnode = std::unordered_map{}; + auto _nodeid = std::unordered_map{}; _idnode[0] = _root.get(); _nodeid[_root.get()] = 0; - + size_t lid = 0; - while(lid != _nodeid.size()) - { + while (lid != _nodeid.size()) { auto n = _idnode[lid]; - if(!n->is_leaf()) - { + if (!n->is_leaf()) { auto ln = n->_low.get(); auto hn = n->_high.get(); - if(_nodeid.count(ln) == 0) - { + if (_nodeid.count(ln) == 0) { auto id = _idnode.size(); _idnode[id] = ln; _nodeid[ln] = id; } - if(_nodeid.count(hn) == 0) - { + if (_nodeid.count(hn) == 0) { auto id = _idnode.size(); _idnode[id] = hn; _nodeid[hn] = id; @@ -1185,50 +1017,44 @@ std::ostream& SimpleTree::print_c(std::ostream& stream, std::string name) const } ++lid; } - - stream << "const int " << name << "_nodes[] = {"; - for(size_t i = 0; i < lid; ++i) - { - if(i != 0) stream << ","; + + os << "const int " << name << "_nodes[] = {"; + for (size_t i = 0; i < lid; ++i) { + if (i != 0) + os << ","; auto n = _idnode[i]; - if(n->is_leaf()) - { - stream << "-1,-1,-1"; - } - else - { - stream << _nodeid[n->_low.get()] << ","; - stream << _nodeid[n->_high.get()] << ","; - stream << n->_var; + if (n->is_leaf()) { + os << "-1,-1,-1"; + } else { + os << _nodeid[n->_low.get()] << ","; + os << _nodeid[n->_high.get()] << ","; + os << n->_var; } } - stream << "};\n"; - auto mm = _root->compute_min_max(); - auto v = is_minimization() ? mm.second + 1 : mm.first -1; - stream << "const double " << name << "_values[] = {"; - for(size_t i = 0; i < lid; ++i) - { - if(i != 0) stream << ","; + os << "};\n"; + const auto [mmin, mmax] = _root->compute_min_max(); + auto v = is_minimization() ? mmax + 1 : mmin - 1; + os << "const double " << name << "_values[] = {"; + for (size_t i = 0; i < lid; ++i) { + if (i != 0) + os << ","; auto n = _idnode[i]; - if(n->is_leaf()) - { - if(!std::isinf(n->_cost) && !std::isnan(n->_cost)) - stream << n->_cost; + if (n->is_leaf()) { + if (!std::isinf(n->_cost) && !std::isnan(n->_cost)) + os << n->_cost; else - stream << v; + os << v; + } else { + os << n->_limit; } - else - { - stream << n->_limit; - } } - stream << "};\n"; - stream << "double " << name << "(unsigned int action, const double* disc, const double* vars)\n{\n"; - //stream << "\t// Depth = " << _root->depth() << std::endl; - stream << "\t// Actions = " << _actions.size() << std::endl; - stream << "\t// Disc = " << _statevars.size() << std::endl; - stream << "\t// Cont = " << _pointvars.size() << std::endl; - stream << "\t// Nodes = " << lid << std::endl; + os << "};\n"; + os << "double " << name << "(unsigned int action, const double* disc, const double* vars)\n{\n"; + // stream << "\t// Depth = " << _root->depth() << std::endl; + os << "\t// Actions = " << _actions.size() << std::endl; + os << "\t// Disc = " << _statevars.size() << std::endl; + os << "\t// Cont = " << _pointvars.size() << std::endl; + os << "\t// Nodes = " << lid << std::endl; /*std::unordered_set printed; std::vector toprint; if(_root) @@ -1243,142 +1069,138 @@ std::ostream& SimpleTree::print_c(std::ostream& stream, std::string name) const } stream << "\treturn " << v << ";\n"; * */ - stream << "\tint ins = 0;\n\twhile(true) {\n"; - stream << "\t\tint l = " << name << "_nodes[ins*3]; int h = " << name << "_nodes[1+(ins*3)]; int v = " << name << "_nodes[2+(ins*3)];\n"; - stream << "\t\tif(v == -1) return " << name << "_values[ins];\n"; - stream << "\t\tdouble val = 0;\n"; - stream << "\t\tif(v == " << _statevars.size() << ") val = action;\n"; - stream << "\t\telse if(v > " << _statevars.size() << ") val = vars[v-" << (_statevars.size()+1) << "];\n"; - stream << "\t\telse val = disc[v];\n"; - stream << "\t\tif(val <= " << name << "_values[ins])\n"; - stream << "\t\t\tins = l;\n"; - stream << "\t\telse\n"; - stream << "\t\t\tins = h;\n"; - stream << "\t}\n"; - stream << "\treturn " << v << ";\n"; - stream << "}\n"; - return stream; + os << "\tint ins = 0;\n\twhile(true) {\n"; + os << "\t\tint l = " << name << "_nodes[ins*3]; int h = " << name << "_nodes[1+(ins*3)]; int v = " << name + << "_nodes[2+(ins*3)];\n"; + os << "\t\tif(v == -1) return " << name << "_values[ins];\n"; + os << "\t\tdouble val = 0;\n"; + os << "\t\tif(v == " << _statevars.size() << ") val = action;\n"; + os << "\t\telse if(v > " << _statevars.size() << ") val = vars[v-" << (_statevars.size() + 1) << "];\n"; + os << "\t\telse val = disc[v];\n"; + os << "\t\tif(val <= " << name << "_values[ins])\n"; + os << "\t\t\tins = l;\n"; + os << "\t\telse\n"; + os << "\t\t\tins = h;\n"; + os << "\t}\n"; + os << "\treturn " << v << ";\n"; + os << "}\n"; + return os; } -size_t SimpleTree::node_t::depth() const { - if(_low && _high == nullptr) +size_t SimpleTree::node_t::depth() const +{ + if (_low && _high == nullptr) return 1 + _low->depth(); - if(_high && _low == nullptr) + if (_high && _low == nullptr) return 1 + _high->depth(); - if(_low == nullptr && _high == nullptr) + if (_low == nullptr && _high == nullptr) return 0; return 1 + std::max(_low->depth(), _high->depth()); } - -std::ostream& SimpleTree::node_t::print_c(std::ostream& stream, size_t disc, std::unordered_set& printed, size_t tabs) const { - - if(printed.count(this) > 0) return stream; +std::ostream& SimpleTree::node_t::print_c(std::ostream& os, size_t disc, std::unordered_set& printed, + size_t tabs) const +{ + if (printed.count(this) > 0) + return os; printed.insert(this); - //for(size_t i = 0; i < tabs; ++i) stream << "\t"; - stream << "l" << this << ":\n"; + // for(size_t i = 0; i < tabs; ++i) stream << "\t"; + os << "l" << this << ":\n"; std::vector toprint; - if(is_leaf()) - { - // for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; - if(!std::isinf(_cost)) - stream << "return " << _cost << ";" << std::endl; - else stream << "{}"; - return stream; - } - if(std::isinf(_limit)) - { - if(_low) - return _low->print_c(stream, disc, printed, tabs); - else if(_high) - return _high->print_c(stream, disc, printed, tabs); - return stream; + if (is_leaf()) { + // for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; + if (!std::isinf(_cost)) + os << "return " << _cost << ";" << std::endl; + else + os << "{}"; + return os; } - //for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; - if(_var == disc) - { - stream << "if(action <= " << _limit << ") "; + if (std::isinf(_limit)) { + if (_low) + return _low->print_c(os, disc, printed, tabs); + if (_high) + return _high->print_c(os, disc, printed, tabs); + return os; } - else - { + // for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; + if (_var == disc) { + os << "if(action <= " << _limit << ") "; + } else { auto type = disc > _var ? "disc" : "vars"; - auto vid = disc > _var ? _var : _var - (disc+1); - stream << "if(" << type << "[" << vid << "] <= " << _limit << ") "; + auto vid = disc > _var ? _var : _var - (disc + 1); + os << "if(" << type << "[" << vid << "] <= " << _limit << ") "; } - if(_low) - _low->print_c_nested(stream, disc, tabs + 1, toprint, _low); - else - { - if(!std::isinf(_cost)) - stream << "return " << _cost << ";"; - else stream << "{}"; + if (_low) + _low->print_c_nested(os, disc, tabs + 1, toprint, _low); + else { + if (!std::isinf(_cost)) + os << "return " << _cost << ";"; + else + os << "{}"; } - stream << "\n"; -// for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; - stream << "else "; - if(_high) - _high->print_c_nested(stream, disc, tabs + 1, toprint, _high); - else - { - if(!std::isinf(_cost)) - stream << "return " << _cost << ";"; - else stream << "{}"; + os << "\n"; + // for(size_t i = 0; i < tabs+1; ++i) stream << "\t"; + os << "else "; + if (_high) + _high->print_c_nested(os, disc, tabs + 1, toprint, _high); + else { + if (!std::isinf(_cost)) + os << "return " << _cost << ";"; + else + os << "{}"; } - stream << "\n"; - for(auto n : toprint) - n->print_c(stream, disc, printed, tabs); - return stream; + os << "\n"; + for (auto n : toprint) + n->print_c(os, disc, printed, tabs); + return os; } -std::ostream& SimpleTree::node_t::print_c_nested(std::ostream& stream, size_t disc, size_t tabs, std::vector& toprint, const std::shared_ptr& node) const +std::ostream& SimpleTree::node_t::print_c_nested(std::ostream& os, size_t disc, size_t tabs, + std::vector& toprint, const node_ptr& node) const { assert(node.get() == this); - if(is_leaf() && std::isinf(_limit)) - { - if(!std::isinf(_cost)) - stream << "return " << _cost << ";"; - else stream << "{}"; - return stream; - } - if(node.use_count() == 1) - { - stream << "{\n"; -// for(size_t i = 0; i < tabs; ++i) stream << "\t"; - if(_var == disc) - { - stream << "if(action <= " << _limit << ") "; - } + if (is_leaf() && std::isinf(_limit)) { + if (!std::isinf(_cost)) + os << "return " << _cost << ";"; else - { + os << "{}"; + return os; + } + if (node.use_count() == 1) { + os << "{\n"; + // for(size_t i = 0; i < tabs; ++i) stream << "\t"; + if (_var == disc) { + os << "if(action <= " << _limit << ") "; + } else { auto type = disc > _var ? "disc" : "vars"; - auto vid = disc > _var ? _var : _var - (disc+1); - stream << "if(" << type << "[" << vid << "] <= " << _limit << ") "; + auto vid = disc > _var ? _var : _var - (disc + 1); + os << "if(" << type << "[" << vid << "] <= " << _limit << ") "; } - if(_low) - _low->print_c_nested(stream, disc, tabs + 1, toprint, _low); - else - { - if(!std::isinf(_cost)) - stream << "return " << _cost << ";"; - else stream << "{}"; + if (_low) + _low->print_c_nested(os, disc, tabs + 1, toprint, _low); + else { + if (!std::isinf(_cost)) + os << "return " << _cost << ";"; + else + os << "{}"; } - stream << "\n"; - //for(size_t i = 0; i < tabs; ++i) stream << "\t"; - stream << "else "; - if(_high) - _high->print_c_nested(stream, disc, tabs + 1, toprint, _high); - else - { - if(!std::isinf(_cost)) - stream << "return " << _cost << ";"; - else stream << "{}"; + os << "\n"; + // for(size_t i = 0; i < tabs; ++i) stream << "\t"; + os << "else "; + if (_high) + _high->print_c_nested(os, disc, tabs + 1, toprint, _high); + else { + if (!std::isinf(_cost)) + os << "return " << _cost << ";"; + else + os << "{}"; } - stream << "\n"; - //for(size_t i = 0; i < tabs-1; ++i) stream << "\t"; - stream << "}"; - return stream; + os << "\n"; + // for(size_t i = 0; i < tabs-1; ++i) stream << "\t"; + os << "}"; + return os; } - stream << "goto l" << this << ";"; + os << "goto l" << this << ";"; toprint.push_back(this); - return stream; + return os; } diff --git a/src/SimpleTree.h b/src/SimpleTree.h index bd9d5c6..c0ec46e 100644 --- a/src/SimpleTree.h +++ b/src/SimpleTree.h @@ -1,21 +1,21 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -/* +/* * File: SimpleTree.h * Author: Peter G. Jensen * @@ -25,67 +25,75 @@ #ifndef SIMPLETREE_H #define SIMPLETREE_H -#include +#include +#include // includes iostream :-( + +#include #include -#include -#include #include +#include +#include -#include -#include - -class SimpleTree { +class SimpleTree +{ public: SimpleTree(const SimpleTree& orig) = default; - virtual ~SimpleTree() = default; static SimpleTree parse(std::istream&, bool simplify = false, bool subsumption = false, double accuracy = 0); - static SimpleTree parse(std::istream&, bool simplify, bool subsumption, double accuracy, std::vector& exactness); - std::ostream& print(std::ostream& stream) const; - std::ostream& print_c(std::ostream& stream, std::string name) const; + static SimpleTree parse(std::istream&, bool simplify, bool subsumption, double accuracy, + std::vector& exactness); + + std::ostream& print(std::ostream& os) const; + std::ostream& print_c(std::ostream& os, const std::string& name) const; double value(const double* disc, const double* cont, uint32_t action) const; bool is_minimization() const { return _is_minimization; } - const std::vector &actions() const; - const std::vector &discrete_features() const { return _statevars; } - const std::vector &continous_features() const { return _pointvars; } + const std::vector& actions() const; + const std::vector& discrete_features() const { return _statevars; } + const std::vector& continous_features() const { return _pointvars; } + private: - - using json = nlohmann::json; struct node_t; - struct signature_t { - uint32_t _var; - double _limit; - node_t* _low; - node_t* _high; - signature_t(const SimpleTree::node_t&); + using node_ptr = std::shared_ptr; + struct signature_t + { + uint32_t _var{0}; + double _limit{0}; + node_t* _low{nullptr}; + node_t* _high{nullptr}; } __attribute__((packed)); friend struct ptrie::byte_iterator; - using nodemap_t = ptrie::map>; + using nodemap_t = ptrie::map; + using json = nlohmann::json; SimpleTree() = default; - - static std::vector parse_key(const std::string& key); - - struct node_t : public std::enable_shared_from_this { + + struct node_t : std::enable_shared_from_this + { uint32_t _var = std::numeric_limits::max(); double _limit = -std::numeric_limits::infinity(); double _cost = std::numeric_limits::infinity(); - std::pair _cost_bounds; - std::shared_ptr _low; - std::shared_ptr _high; - node_t* _parent; - void insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, bool minimize, double accuracy, std::vector& exactness); + std::pair _cost_bounds; + node_ptr _low; + node_ptr _high; + node_t* _parent{nullptr}; + void insert(std::vector& key, json& tree, size_t action, SimpleTree& parent, size_t prefix, + bool minimize, double accuracy, std::vector& exactness); std::ostream& print(std::ostream& out, size_t tabs = 0) const; - bool is_leaf() const; - std::shared_ptr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); + bool is_leaf() const { return _low == nullptr && _high == nullptr; } + node_ptr simplify(bool make_dd, nodemap_t& nodemap, SimpleTree& parent); void subsumption_reduction(bool minimization, SimpleTree& parent); - void action_nodes(std::vector>& nodes, uint32_t low, uint32_t high, uint32_t varid); - std::pair compute_min_max(); - bool check_tiles(node_t* start, std::vector>& , std::vector>& bounds, double val, double minval, double maxval, bool minimization, size_t offset); - bool subsumes(const std::vector>& bounds, std::vector>& obounds, const double val, const bool minimization, size_t offset, double& best, std::pair& closest); + void action_nodes(std::vector& nodes, uint32_t low, uint32_t high, uint32_t varid); + std::pair compute_min_max() const; + bool check_tiles(node_t* start, std::vector&, std::vector>& bounds, + double val, double minval, double maxval, bool minimization, size_t offset); + bool subsumes(const std::vector>& bounds, + std::vector>& obounds, double val, bool minimization, size_t offset, + double& best, std::pair& closest) const; void get_ranks(std::set>& values, node_t* start); - void set_ranks(std::unordered_map& values); - std::ostream& print_c(std::ostream& stream, size_t disc, std::unordered_set& printed, size_t tabs = 0) const; - std::ostream& print_c_nested(std::ostream& stream, size_t disc, size_t tabs, std::vector& toprint, const std::shared_ptr& node) const; + void set_ranks(std::unordered_map& values); + std::ostream& print_c(std::ostream& os, size_t disc, std::unordered_set& printed, + size_t tabs = 0) const; + std::ostream& print_c_nested(std::ostream& os, size_t disc, size_t tabs, std::vector& toprint, + const node_ptr& node) const; size_t depth() const; double value(const double* disc, const double* cont, uint32_t action, size_t ndisc) const; void consistent(size_t) const; @@ -93,63 +101,62 @@ class SimpleTree { double midcost(const node_t& other, double minval, double maxval) const; bool operator==(const node_t& other) const { - if(_limit != other._limit) + if (_limit != other._limit) return false; - if(_var != other._var) + if (_var != other._var) return false; - if(_low != other._low) + if (_low != other._low) return false; - if(_high != other._high) + if (_high != other._high) return false; return true; } bool operator<(const node_t& other) const { - if(_limit != other._limit) + if (_limit != other._limit) return _limit < other._limit; - if(_var != other._var) + if (_var != other._var) return _var < other._var; - if(_low != other._low) + if (_low != other._low) return _low < other._low; return _high < other._high; } - std::shared_ptr& operator[](bool b) { - return b ? _high : _low; + node_ptr& operator[](bool b) { return b ? _high : _low; } + operator signature_t() const + { ///< support implicit conversion for map keys + if (is_leaf()) + return signature_t{_var, _limit, _low.get(), _high.get()}; + return signature_t{0, _cost, nullptr, nullptr}; } }; - + std::vector _actions; std::vector _statevars; std::vector _pointvars; - std::shared_ptr _root; + node_ptr _root; bool _is_minimization = true; }; -namespace ptrie{ - template<> - struct byte_iterator { - static uchar& access(SimpleTree::signature_t* data, size_t id) - { - return ((uchar*)data)[id]; - } - - static const uchar& const_access(const SimpleTree::signature_t* data, size_t id) - { - return ((const uchar*)data)[id]; - } - - static constexpr size_t element_size() - { - return sizeof(size_t)*2+sizeof(double)+sizeof(uint32_t); - } - - static constexpr bool continious() - { - return true; - } - - // add read_blob, write_blob - }; -} -#endif /* SIMPLETREE_H */ +template <> +struct ptrie::byte_iterator +{ + static uchar& access(SimpleTree::signature_t* data, size_t id) { return reinterpret_cast(data)[id]; } + + static const uchar& const_access(const SimpleTree::signature_t* data, size_t id) + { + return reinterpret_cast(data)[id]; + } + static constexpr size_t element_size() + { + constexpr auto member_size = sizeof(SimpleTree::signature_t::_var) + sizeof(SimpleTree::signature_t::_limit) + + sizeof(SimpleTree::signature_t::_high) + sizeof(SimpleTree::signature_t::_low); + static_assert(sizeof(SimpleTree::signature_t) == member_size, "tightly packed struct"); + return sizeof(SimpleTree::signature_t); + } + + static constexpr bool continious() { return true; } + + // add read_blob, write_blob +}; +#endif /* SIMPLETREE_H */ diff --git a/src/ZonotopStrategy.cpp b/src/ZonotopStrategy.cpp index 2b8406e..22fc42b 100644 --- a/src/ZonotopStrategy.cpp +++ b/src/ZonotopStrategy.cpp @@ -1,49 +1,57 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -/* +/* * File: ZonotopStrategy.cpp * Author: Peter G. Jensen - * + * * Created on December 13, 2018, 2:54 PM */ #include "ZonotopStrategy.h" +#include "errors.h" #include #include -#include #include #include - - using json = nlohmann::json; -std::vector ZonotopStrategy::get_bounds(const std::string& zonotop, size_t& vars) +/// For printing a number of tab ('\t') characters. +struct Tabs { - size_t i = 0; - size_t vid = 0; - std::vector bounds; - while(i < zonotop.size() && zonotop[i] != ')') + size_t count; + friend std::ostream& operator<<(std::ostream& os, const Tabs& tabs) { - if(zonotop[i] == '[') - { + for (auto i = 0u; i < tabs.count; ++i) + os << '\t'; + return os; + } +}; + +std::vector ZonotopStrategy::get_bounds(const std::string& zonotop, size_t& vars) +{ + auto i = size_t{0}; + auto vid = size_t{0}; + auto bounds = std::vector{}; + while (i < zonotop.size() && zonotop[i] != ')') { + if (zonotop[i] == '[') { double lower = 0, upper = 0; // new element ++i; @@ -56,100 +64,84 @@ std::vector ZonotopStrategy::get_bounds(const std::str ns >> lower; i += num.length() + 1; } - while(i < zonotop.size() && zonotop[i] == ' ') ++i; + while (i < zonotop.size() && zonotop[i] == ' ') + ++i; { std::string num; std::getline(ss, num, ']'); std::istringstream ns(num); - ns >> upper; + ns >> upper; i += num.length() + 1; } bounds.emplace_back(lower, upper); } ++i; } - if(vars == 0) + if (vars == 0) vars = vid; - if(vars != vid) - { + if (vars != vid) { throw base_error("Dimensionality of controller differs"); } return bounds; } -ZonotopStrategy ZonotopStrategy::parse(std::istream& input) +ZonotopStrategy ZonotopStrategy::parse(std::istream& input) { auto raw = json::parse(input); - ZonotopStrategy strategy; - size_t vars = 0; - if(raw.is_object()) - { - for(auto it = raw.begin(); it != raw.end(); ++it) - { - auto bounds = get_bounds(it.key(), vars); - - if(it.value().is_array()) - { - for(auto e : it.value()) - { - std::vector pat = e.get>(); - if(pat.size() > 0) - { - auto res = strategy._states.insert((unsigned char*)pat.data(), pat.size()*sizeof(uint16_t)); + auto strategy = ZonotopStrategy{}; + auto vars = size_t{0}; + if (raw.is_object()) { + for (auto& [raw_key, raw_value] : raw.items()) { + auto bounds = get_bounds(raw_key, vars); + if (raw_value.is_array()) { + for (auto e : raw_value) { + auto pat = e.get>(); + if (pat.size() > 0) { + auto res = strategy._states.insert((unsigned char*)pat.data(), pat.size() * sizeof(uint16_t)); strategy._max_length = std::max(strategy._max_length, pat.size()); - if(strategy.add(bounds, res.second)) - { - //std::cerr << "COL " << it.key() << " : " << e << std::endl; - //exit(-1); + if (strategy.add(bounds, res.second)) { + // std::cerr << "COL " << it.key() << " : " << e << std::endl; + // exit(-1); } } } + } else { + throw base_error("Input JSON not well formatted (array field expected)"); } - else - { - throw base_error("Input JSON not well formatted"); - } } - } - else - { - throw base_error("Input JSON not well formatted"); + } else { + throw base_error("Input JSON not well formatted (object expected)"); } return strategy; } -bool ZonotopStrategy::add(std::vector& bounds, size_t state) +bool ZonotopStrategy::add(std::vector& bounds, size_t state) { - if(_root == nullptr) - { + if (_root == nullptr) { _root = std::make_shared(); _root->_varid = 0; _root->_limit = bounds[0]._lower; } - std::vector> handled(bounds.size()); + auto handled = std::vector>(bounds.size()); return rec_insert(_root.get(), bounds, handled, state); } -bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, std::vector>& handled, size_t state) { +bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, + std::vector>& handled, size_t state) +{ auto nid = node->_varid; assert(nid < bounds.size()); - bool reset = false; - bool col = false; - if(bounds[node->_varid]._upper <= node->_limit) - { - if(bounds[node->_varid]._upper == node->_limit) + auto reset = false; + auto col = false; + if (bounds[node->_varid]._upper <= node->_limit) { + if (bounds[node->_varid]._upper == node->_limit) reset = handled[node->_varid].second = true; - if(handled[node->_varid].first && handled[node->_varid].second) - { - if(node->_varid == bounds.size()-1) - { + if (handled[node->_varid].first && handled[node->_varid].second) { + if (node->_varid == bounds.size() - 1) { auto lb = std::lower_bound(node->_low_patterns.begin(), node->_low_patterns.end(), state); - if(lb != node->_low_patterns.end() && *lb == state) - { + if (lb != node->_low_patterns.end() && *lb == state) { col = true; - } - else - { + } else { node->_low_patterns.insert(lb, state); try_merge(node, state); } @@ -159,31 +151,24 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, std assert(nid < bounds.size()); } - if(node->_low == nullptr) - { + if (node->_low == nullptr) { node->_low = std::make_shared(); node->_low->_varid = nid; node->_low->_parent = node; node->_low->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); } col |= rec_insert(node->_low.get(), bounds, handled, state); - if(reset) handled[node->_varid].second = false; - } - else if(bounds[node->_varid]._lower >= node->_limit) - { - if(bounds[node->_varid]._lower == node->_limit) + if (reset) + handled[node->_varid].second = false; + } else if (bounds[node->_varid]._lower >= node->_limit) { + if (bounds[node->_varid]._lower == node->_limit) reset = handled[node->_varid].first = true; - if(handled[node->_varid].first && handled[node->_varid].second) - { - if(node->_varid == bounds.size()-1) - { + if (handled[node->_varid].first && handled[node->_varid].second) { + if (node->_varid == bounds.size() - 1) { auto lb = std::lower_bound(node->_high_patterns.begin(), node->_high_patterns.end(), state); - if(lb != node->_high_patterns.end() && *lb == state) - { + if (lb != node->_high_patterns.end() && *lb == state) { col = true; - } - else - { + } else { node->_high_patterns.insert(lb, state); try_merge(node, state); } @@ -192,32 +177,28 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, std ++nid; assert(nid < bounds.size()); } - - if(node->_high == nullptr) - { + + if (node->_high == nullptr) { node->_high = std::make_shared(); node->_high->_parent = node; node->_high->_varid = nid; node->_high->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); } col |= rec_insert(node->_high.get(), bounds, handled, state); - if(reset) handled[node->_varid].first = false; - } - else - { - if(node->_low == nullptr) - { + if (reset) + handled[node->_varid].first = false; + } else { + if (node->_low == nullptr) { node->_low = std::make_shared(); node->_low->_varid = nid; node->_low->_parent = node; - node->_low->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); + node->_low->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); } - if(node->_high == nullptr) - { + if (node->_high == nullptr) { node->_high = std::make_shared(); node->_high->_parent = node; node->_high->_varid = nid; - node->_high->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); + node->_high->_limit = (!handled[nid].first ? bounds[nid]._lower : bounds[nid]._upper); } col |= rec_insert(node->_low.get(), bounds, handled, state); @@ -226,230 +207,211 @@ bool ZonotopStrategy::rec_insert(node_t* node, std::vector& bounds, std return col; } -void ZonotopStrategy::try_merge(node_t* node, size_t state) { - if(node == nullptr) return; +void ZonotopStrategy::try_merge(node_t* node, size_t state) +{ + if (node == nullptr) + return; auto hlb = std::lower_bound(node->_high_patterns.begin(), node->_high_patterns.end(), state); - if(hlb != std::end(node->_high_patterns) && *hlb == state) - { + if (hlb != std::end(node->_high_patterns) && *hlb == state) { auto llb = std::lower_bound(node->_low_patterns.begin(), node->_low_patterns.end(), state); - if(llb != std::end(node->_low_patterns) && *llb == state) - { + if (llb != std::end(node->_low_patterns) && *llb == state) { node->_high_patterns.erase(hlb); node->_low_patterns.erase(llb); - bool empty = (node->_high == nullptr && node->_low == nullptr && node->_high_patterns.empty() && node->_low_patterns.empty()); + bool empty = (node->_high == nullptr && node->_low == nullptr && node->_high_patterns.empty() && + node->_low_patterns.empty()); auto parent = node->_parent; - if(node->_parent->_low.get() == node) - { - parent->_low_patterns.push_back(state); - if(empty) parent->_low = nullptr; - } - else - { + if (node->_parent->_low.get() == node) { + parent->_low_patterns.push_back(state); + if (empty) + parent->_low = nullptr; + } else { parent->_high_patterns.push_back(state); - if(empty) parent->_high = nullptr; + if (empty) + parent->_high = nullptr; } try_merge(parent, state); } } } - -std::ostream& ZonotopStrategy::node_t::print(std::ostream& out, const ZonotopStrategy* parent, size_t tabs) const { - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "{\"var\":" << _varid << ",\"bound\":" << _limit; +std::ostream& ZonotopStrategy::node_t::print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs) const +{ + os << Tabs{tabs}; + os << "{\"var\":" << _varid << ",\"bound\":" << _limit; auto buffer = std::make_unique(parent->_max_length); - if(!_low_patterns.empty()) - { - out << ",\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "\"low_patterns\":["; + if (!_low_patterns.empty()) { + os << ",\n"; + os << Tabs{tabs}; + os << "\"low_patterns\":["; bool fp = true; - for(auto p : _low_patterns) - { - if(!fp) - out << ","; - out << "["; + for (auto p : _low_patterns) { + if (!fp) + os << ","; + os << "["; /*size_t length = parent->_states.unpack(p, (unsigned char*)buffer.get()); bool fe = true; for(size_t i = 0; i < length/sizeof(uint16_t); ++i) { if(!fe) - out << ","; + os << ","; fe = false; - out << buffer[i]; + os << buffer[i]; }*/ - out << p; - out << "]"; + os << p; + os << "]"; } - out << "]"; + os << "]"; } - if(!_high_patterns.empty()) - { - out << ",\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "\"high_patterns\":["; + if (!_high_patterns.empty()) { + os << ",\n"; + os << Tabs{tabs}; + os << "\"high_patterns\":["; bool fp = true; - for(auto p : _high_patterns) - { - if(!fp) - out << ","; - out << "["; + for (auto p : _high_patterns) { + if (!fp) + os << ","; + os << "["; size_t length = const_cast(parent)->_states.unpack(p, (unsigned char*)buffer.get()); bool fe = true; - for(size_t i = 0; i < length/sizeof(uint16_t); ++i) - { - if(!fe) - out << ","; + for (size_t i = 0; i < length / sizeof(uint16_t); ++i) { + if (!fe) + os << ","; fe = false; - out << buffer[i]; + os << buffer[i]; } - out << "]"; + os << "]"; } - out << "]"; + os << "]"; } - if(_low) - { - out << ",\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "\"low\":\n"; - _low->print(out, parent, tabs + 1); + if (_low) { + os << ",\n"; + os << Tabs{tabs}; + os << "\"low\":\n"; + _low->print(os, parent, tabs + 1); } - if(_high) - { - out << ",\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "\"high\":\n"; - _high->print(out, parent, tabs + 1); + if (_high) { + os << ",\n"; + os << Tabs{tabs}; + os << "\"high\":\n"; + _high->print(os, parent, tabs + 1); } - out << "\n"; - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "}"; - return out; + os << "\n"; + os << Tabs{tabs}; + os << "}"; + return os; } -void ZonotopStrategy::active(const double* sampel, bool* write) const { - //std::cerr << "LOOKUP [" << sampel[0] << ", " << sampel[1] << "]" << std::endl; - memset(write, 0, sizeof(bool)*_states.size()); - if(_root == nullptr) +void ZonotopStrategy::active(const double* sampel, bool* write) const +{ + // std::cerr << "LOOKUP [" << sampel[0] << ", " << sampel[1] << "]" << + // std::endl; + memset(write, 0, sizeof(bool) * _states.size()); + if (_root == nullptr) return; auto current = _root.get(); int active = 0; -// _root->print(std::cerr, (ZonotopStrategy*)this); - while(current) - { - if(sampel[current->_varid] >= current->_limit) - { - for(auto p : current->_high_patterns) - { + // _root->print(std::cerr, (ZonotopStrategy*)this); + while (current) { + if (sampel[current->_varid] >= current->_limit) { + for (auto p : current->_high_patterns) { write[p] = true; - //std::cerr << "ACTIVE " << p << std::endl; + // std::cerr << "ACTIVE " << p << std::endl; } active += current->_high_patterns.size(); } - if(sampel[current->_varid] <= current->_limit) - { - for(auto p : current->_low_patterns) - { + if (sampel[current->_varid] <= current->_limit) { + for (auto p : current->_low_patterns) { write[p] = true; - //std::cerr << "ACTIVE " << p << std::endl; + // std::cerr << "ACTIVE " << p << std::endl; } active += current->_low_patterns.size(); } - if(sampel[current->_varid] <= current->_limit && current->_low) - { + if (sampel[current->_varid] <= current->_limit && current->_low) { current = current->_low.get(); continue; } - if(sampel[current->_varid] >= current->_limit && current->_high) - { + if (sampel[current->_varid] >= current->_limit && current->_high) { current = current->_high.get(); continue; - } + } break; } - if(active == 0) + if (active == 0) std::cerr << "No active for [" << sampel[0] << ", " << sampel[1] << "]" << std::endl; } -int ZonotopStrategy::max_pattern_length() const { - return _max_length; -} +int ZonotopStrategy::max_pattern_length() const { return _max_length; } -int ZonotopStrategy::num_patterns() const { - return _states.size(); -} +int ZonotopStrategy::num_patterns() const { return _states.size(); } -int ZonotopStrategy::get_pattern(int el, int* write) { +int ZonotopStrategy::get_pattern(int el, int* write) +{ auto buffer = std::make_unique(_max_length); size_t length = _states.unpack(el, (unsigned char*)buffer.get()); - for(size_t i = 0; i < length/sizeof(uint16_t); ++i) - { + for (size_t i = 0; i < length / sizeof(uint16_t); ++i) { write[i] = buffer[i]; } - return length/sizeof(uint16_t); + return length / sizeof(uint16_t); } -std::ostream& ZonotopStrategy::print(std::ostream& stream) const { - if(_root) +std::ostream& ZonotopStrategy::print(std::ostream& stream) const +{ + if (_root) _root->print(stream, this); return stream; } -double ZonotopStrategy::get_max(size_t dimen) const { - return _root->get_max(dimen); -} +double ZonotopStrategy::get_max(size_t dimen) const { return _root->get_max(dimen); } -double ZonotopStrategy::get_min(size_t dimen) const { - return _root->get_min(dimen); -} +double ZonotopStrategy::get_min(size_t dimen) const { return _root->get_min(dimen); } -double ZonotopStrategy::node_t::get_max(size_t dimen) const { +double ZonotopStrategy::node_t::get_max(size_t dimen) const +{ double maxval = _varid == dimen ? _limit : -std::numeric_limits::infinity(); - if(_high) + if (_high) maxval = std::max(maxval, _high->get_max(dimen)); - if(_low) + if (_low) maxval = std::max(maxval, _low->get_max(dimen)); return maxval; } -double ZonotopStrategy::node_t::get_min(size_t dimen) const { +double ZonotopStrategy::node_t::get_min(size_t dimen) const +{ double minval = _varid == dimen ? _limit : std::numeric_limits::infinity(); - if(_high) + if (_high) minval = std::min(minval, _high->get_min(dimen)); - if(_low) + if (_low) minval = std::min(minval, _low->get_min(dimen)); return minval; } - - std::ostream& ZonotopStrategy::print_c(std::ostream& stream, std::string function_name) const { -/* stream << "const int " << function_name << "_patterns[][" << (_max_length + 1) << "] = {\n"; - bool first = true; - auto buffer = std::make_unique(_max_length); - for(size_t i = 0; i < _states.size(); ++i) - { - memset(buffer.get(), 0, max_pattern_length()*sizeof(uint16_t)); - auto size = _states.unpack(i, (unsigned char*)buffer.get()); - if(!first) - stream << ","; - stream << "{"; - bool bf = true; - for(int j = 0; j < max_pattern_length()+1; ++j) + /* stream << "const int " << function_name << "_patterns[][" << + (_max_length + 1) << "] = {\n"; bool first = true; auto buffer = + std::make_unique(_max_length); for(size_t i = 0; i < + _states.size(); ++i) { - if(!bf) + memset(buffer.get(), 0, max_pattern_length()*sizeof(uint16_t)); + auto size = _states.unpack(i, (unsigned char*)buffer.get()); + if(!first) stream << ","; - if((size_t)j < size/sizeof(uint16_t)) - stream << buffer[j]; - else - stream << "-1"; - bf = false; + stream << "{"; + bool bf = true; + for(int j = 0; j < max_pattern_length()+1; ++j) + { + if(!bf) + stream << ","; + if((size_t)j < size/sizeof(uint16_t)) + stream << buffer[j]; + else + stream << "-1"; + bf = false; + } + stream << "}\n"; + first = false; } - stream << "}\n"; - first = false; - } - stream << "};\n\n";*/ + stream << "};\n\n";*/ stream << "bool " << function_name << "(const double* args, bool* patterns)\n"; stream << "{\n"; _root->print_c(stream, 1); @@ -458,39 +420,36 @@ std::ostream& ZonotopStrategy::print_c(std::ostream& stream, std::string functio return stream; } -std::ostream& ZonotopStrategy::node_t::print_c(std::ostream& out, size_t tabs) const { - if(_low != nullptr || !_low_patterns.empty()) - { - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "if(args[" << _varid << "] <= " << _limit << ") {\n"; - if(_low_patterns.size() > 0) - { - for(size_t i = 0; i < tabs+1; ++i) out << "\t"; - for(auto p : _low_patterns) - out << "patterns[" << p << "] = true; "; - out << "\n"; +std::ostream& ZonotopStrategy::node_t::print_c(std::ostream& os, size_t tabs) const +{ + if (_low != nullptr || !_low_patterns.empty()) { + os << Tabs{tabs}; + os << "if(args[" << _varid << "] <= " << _limit << ") {\n"; + if (_low_patterns.size() > 0) { + os << Tabs{tabs + 1}; + for (auto p : _low_patterns) + os << "patterns[" << p << "] = true; "; + os << "\n"; } - if(_low) - _low->print_c(out, tabs + 1); - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "}\n"; + if (_low) + _low->print_c(os, tabs + 1); + os << Tabs{tabs}; + os << "}\n"; } - if(_high != nullptr || !_high_patterns.empty()) - { - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "if(args[" << _varid << "] >= " << _limit << ") {\n"; - if(_high_patterns.size() > 0) - { - for(size_t i = 0; i < tabs+1; ++i) out << "\t"; - for(auto p : _high_patterns) - out << "patterns[" << p << "] = true; "; - out << "\n"; + if (_high != nullptr || !_high_patterns.empty()) { + os << Tabs{tabs}; + os << "if(args[" << _varid << "] >= " << _limit << ") {\n"; + if (_high_patterns.size() > 0) { + os << Tabs{tabs + 1}; + for (auto p : _high_patterns) + os << "patterns[" << p << "] = true; "; + os << "\n"; } - if(_high) - _high->print_c(out, tabs + 1); - for(size_t i = 0; i < tabs; ++i) out << "\t"; - out << "}\n"; + if (_high) + _high->print_c(os, tabs + 1); + os << Tabs{tabs}; + os << "}\n"; } - return out; + return os; } diff --git a/src/ZonotopStrategy.h b/src/ZonotopStrategy.h index d5522ad..b6dd261 100644 --- a/src/ZonotopStrategy.h +++ b/src/ZonotopStrategy.h @@ -1,21 +1,21 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -/* +/* * File: ZonotopStrategy.h * Author: Peter G. Jensen * @@ -25,17 +25,17 @@ #ifndef ZONOTOPSTRATEGY_H #define ZONOTOPSTRATEGY_H +#include "SimpleTree.h" + #include -#include -#include -#include "SimpleTree.h" -#include "errors.h" +#include +#include -class ZonotopStrategy { +class ZonotopStrategy +{ public: ZonotopStrategy(ZonotopStrategy&&) = default; - virtual ~ZonotopStrategy() = default; ZonotopStrategy& operator=(ZonotopStrategy&&) = default; static ZonotopStrategy parse(std::istream&); int num_patterns() const; @@ -46,39 +46,40 @@ class ZonotopStrategy { double get_max(size_t dimen) const; std::ostream& print(std::ostream&) const; std::ostream& print_c(std::ostream& stream, std::string function_name) const; + private: ZonotopStrategy() = default; ptrie::set_stable<> _states; size_t _max_length = 0; - struct node_t { - uint32_t _varid = 0; + struct node_t; + using node_ptr = std::shared_ptr; + struct node_t : std::enable_shared_from_this + { + uint32_t _varid = 0; double _limit = 0; - std::shared_ptr _low = nullptr; - std::shared_ptr _high = nullptr; + node_ptr _low = nullptr; + node_ptr _high = nullptr; std::vector _low_patterns; std::vector _high_patterns; - node_t* _parent; - std::ostream& print(std::ostream& out, const ZonotopStrategy* parent, size_t tabs = 0) const; - std::ostream& print_c(std::ostream& out, size_t tabs = 0) const; + node_t* _parent{nullptr}; + std::ostream& print(std::ostream& os, const ZonotopStrategy* parent, size_t tabs = 0) const; + std::ostream& print_c(std::ostream& os, size_t tabs = 0) const; double get_min(size_t dimen) const; double get_max(size_t dimen) const; }; - std::shared_ptr _root; - struct bound_t { + node_ptr _root; + struct bound_t + { double _lower = 0; double _upper = 0; bound_t() = default; - bound_t(const bound_t&) = default; - bound_t(bound_t&&) = default; - bound_t(double l, double u) - : _lower(l), _upper(u) {} + bound_t(double l, double u): _lower{l}, _upper{u} {} }; static std::vector get_bounds(const std::string& zonotop, size_t& vars); bool add(std::vector& bounds, size_t state); - bool rec_insert(node_t* node, std::vector& bounds, std::vector>& handled, size_t state); + bool rec_insert(node_t* node, std::vector& bounds, std::vector>& handled, + size_t state); void try_merge(node_t* node, size_t state); }; - #endif /* ZONOTOPSTRATEGY_H */ - diff --git a/src/errors.h b/src/errors.h index ad056ab..f22baa0 100644 --- a/src/errors.h +++ b/src/errors.h @@ -1,21 +1,21 @@ /* * Copyright (C) 2020 Peter G. Jensen - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -/* +/* * File: errors.h * Author: Peter G. Jensen * @@ -25,26 +25,11 @@ #ifndef ERRORS_H #define ERRORS_H -struct base_error : public std::exception { - std::string _message; +#include - explicit base_error(std::string m) - : _message(std::move(m)) { - } - - const char *what() const noexcept override { - return _message.c_str(); - } - - virtual void print(std::ostream &os) const { - os << what() << std::endl; - } - - friend std::ostream &operator<<(std::ostream &os, const base_error &el) { - el.print(os); - return os; - } +struct base_error : std::logic_error +{ + using std::logic_error::logic_error; }; #endif /* ERRORS_H */ - diff --git a/src/utilities.hpp b/src/utilities.hpp new file mode 100644 index 0000000..8ddb049 --- /dev/null +++ b/src/utilities.hpp @@ -0,0 +1,82 @@ +#ifndef UTILITIES_HPP +#define UTILITIES_HPP + +#include "errors.h" + +/** + * Avoid including this header into headers (include into cpp instead) + * as it includes streams (instead of iosfwd) and slows down compilation. + */ + +#include // from_chars +#include // fallback if from_chars is not available +#include // true_type for detecting from_chars +#include // declval for detecting from_chars +#include + +/** C++17 compile-time test for presence of std::from_chars(const char*, const char*, T&) + * Replace it with C++20 concepts later (or perhaps AppleClang will implement proper from_chars by then). + * History: C++17 introduced std::from_chars, but STL vendors were late, then provided only integral versions... + */ +template +struct has_from_chars : std::false_type +{}; // primary template declaration (used when specializations fail) + +template +struct has_from_chars< // template partial specialization + T, + std::void_t< // tests if the following expression computes into a type: + decltype(std::from_chars(std::declval(), std::declval(), std::declval()))>> + : std::true_type +{}; + +template +constexpr auto has_from_chars_v = has_from_chars::value; + +/// Parses numbers from a key in a form of "(number,number,number)" +template // has to be a template, otherwise AppleClang ignores constexpr +std::vector parse_key(const std::string& key) +{ + static_assert(std::is_arithmetic_v, "only numeric keys are supported"); + auto res = std::vector{}; + T number; // the number to parse into + if constexpr (has_from_chars_v) { // fast floating point parsing + auto it = key.c_str(); + const auto end = it + key.size(); + if (it == end || *it != '(') + throw base_error("incorrectly formatted key ('(' expected): " + key); + if (*++it == ')') + return res; + while (it != end) { + if (auto [p, ec] = std::from_chars(it, end, number); ec == std::errc()) { + res.push_back(number); + it = p; + if (it != end && *it == ',') + ++it; + else + break; + } else + throw base_error("failed to parse number in key: " + key); + } + if (it == end || *it != ')') + throw base_error("incorrectly formatted key (')' expected): " + key); + } else { // fallback to slow stream parsing (AppleClang does not support from_chars) + auto is = std::istringstream{key}; + char c; + if (!is.get(c) || c != '(') + throw base_error("incorrectly formatted key ('(' expected): " + key); + if (is && is.peek() == ')') + return res; + while (is >> number) { + res.push_back(number); + if (!is.get(c) || c != ',') + break; + } + if (c != ')') { + throw base_error("incorrectly formatted key (')' expected): " + key); + } + } + return res; +} + +#endif // UTILITIES_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3417e26..620da41 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,18 +1,19 @@ - find_package (Boost COMPONENTS unit_test_framework REQUIRED) -include_directories (${TEST_SOURCE_DIR}/src - ${Boost_INCLUDE_DIRS} - ${libstrategy_SOURCE_DIR} - ) -add_definitions (-DBOOST_TEST_DYN_LINK) +include_directories (${CMAKE_SOURCE_DIR}/src) + +add_executable (utilities_test utilities_test.cpp) +add_test(NAME utilities_test COMMAND utilities_test) -add_executable (unordered_load unordered_load.cpp) -add_executable (inconsistent_lookup inconsistent_lookup.cpp) -target_link_libraries(unordered_load ${Boost_LIBRARIES} strategy) -target_link_libraries(inconsistent_lookup ${Boost_LIBRARIES} strategy) +add_executable (SimpleTree_test SimpleTree_test.cpp) +target_link_libraries(SimpleTree_test PRIVATE SimpleTree Boost::unit_test_framework) +add_test(NAME SimpleTree_test COMMAND SimpleTree_test) +add_executable (unordered_load unordered_load.cpp) +target_link_libraries(unordered_load PRIVATE strategy Boost::unit_test_framework) add_test(NAME unordered_load COMMAND unordered_load) + +add_executable (inconsistent_lookup inconsistent_lookup.cpp) +target_link_libraries(inconsistent_lookup PRIVATE strategy Boost::unit_test_framework) add_test(NAME inconsistent_lookup COMMAND inconsistent_lookup) -set_tests_properties(inconsistent_lookup PROPERTIES - ENVIRONMENT STRATEGY_DIR=${CMAKE_CURRENT_SOURCE_DIR}/strategies) +set_tests_properties(inconsistent_lookup PROPERTIES ENVIRONMENT STRATEGY_DIR=${CMAKE_CURRENT_SOURCE_DIR}/strategies) diff --git a/test/SimpleTree_test.cpp b/test/SimpleTree_test.cpp new file mode 100644 index 0000000..b5fce7b --- /dev/null +++ b/test/SimpleTree_test.cpp @@ -0,0 +1,46 @@ +#define BOOST_TEST_MODULE UnorderedLoad + +#include "SimpleTree.h" +#include "errors.h" +#include "utilities.hpp" + +#include + +BOOST_AUTO_TEST_CASE(SimpleTreeParseKey) +{ + const auto res_blank = parse_key("()"); + BOOST_CHECK(res_blank.empty()); + + const auto res_one = parse_key("(1)"); + BOOST_REQUIRE_EQUAL(res_one.size(), 1); + BOOST_CHECK_EQUAL(res_one[0], 1); + + const auto res_two = parse_key("(2,1)"); + BOOST_REQUIRE_EQUAL(res_two.size(), 2); + BOOST_CHECK_EQUAL(res_two[0], 2); + BOOST_CHECK_EQUAL(res_two[1], 1); + + const auto res_three = parse_key("(3,2,1)"); + BOOST_REQUIRE_EQUAL(res_three.size(), 3); + BOOST_CHECK_EQUAL(res_three[0], 3); + BOOST_CHECK_EQUAL(res_three[1], 2); + BOOST_CHECK_EQUAL(res_three[2], 1); + + const auto res_float = parse_key("(3.141)"); + BOOST_REQUIRE_EQUAL(res_float.size(), 1); + BOOST_CHECK_EQUAL(res_float[0], 3.141); + + const auto res_floats = parse_key("(4.3,2.1)"); + BOOST_REQUIRE_EQUAL(res_floats.size(), 2); + BOOST_CHECK_EQUAL(res_floats[0], 4.3); + BOOST_CHECK_EQUAL(res_floats[1], 2.1); + + // a few negative tests: + BOOST_CHECK_THROW(parse_key(""), base_error); + BOOST_CHECK_THROW(parse_key("1"), base_error); + BOOST_CHECK_THROW(parse_key(")"), base_error); + BOOST_CHECK_THROW(parse_key("("), base_error); + BOOST_CHECK_THROW(parse_key("(1"), base_error); + BOOST_CHECK_THROW(parse_key("(2,"), base_error); + BOOST_CHECK_THROW(parse_key("(2,1"), base_error); +} diff --git a/test/inconsistent_lookup.cpp b/test/inconsistent_lookup.cpp index c5ef33b..bd20e7a 100644 --- a/test/inconsistent_lookup.cpp +++ b/test/inconsistent_lookup.cpp @@ -17,15 +17,12 @@ #define BOOST_TEST_MODULE UnorderedLoad +#include "SimpleTree.h" + #include #include -#include "SimpleTree.h" - -BOOST_AUTO_TEST_CASE(DirectoryTest) -{ - BOOST_REQUIRE(getenv("STRATEGY_DIR")); -} +BOOST_AUTO_TEST_CASE(DirectoryTest) { BOOST_REQUIRE(getenv("STRATEGY_DIR")); } BOOST_AUTO_TEST_CASE(Inconsistent1) { @@ -34,8 +31,8 @@ BOOST_AUTO_TEST_CASE(Inconsistent1) std::ifstream in(strategy); auto tree = SimpleTree::parse(in, false, false); double vars[] = {10}; - auto act18 = tree.value(vars,nullptr, 0); - auto act19 = tree.value(vars,nullptr, 1); + auto act18 = tree.value(vars, nullptr, 0); + auto act19 = tree.value(vars, nullptr, 1); BOOST_REQUIRE_LT(act18, act19); } @@ -46,7 +43,7 @@ BOOST_AUTO_TEST_CASE(Inconsistent1Simplify) std::ifstream in(strategy); auto tree = SimpleTree::parse(in, true, false); double vars[] = {10}; - BOOST_REQUIRE_LT(tree.value(vars,nullptr, 0), tree.value(vars,nullptr, 1)); + BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); } BOOST_AUTO_TEST_CASE(Inconsistent1SimplifySubsumption) @@ -56,5 +53,5 @@ BOOST_AUTO_TEST_CASE(Inconsistent1SimplifySubsumption) std::ifstream in(strategy); auto tree = SimpleTree::parse(in, true, true); double vars[] = {10}; - BOOST_REQUIRE_LT(tree.value(vars,nullptr, 0), tree.value(vars,nullptr, 1)); + BOOST_REQUIRE_LT(tree.value(vars, nullptr, 0), tree.value(vars, nullptr, 1)); } diff --git a/test/unordered_load.cpp b/test/unordered_load.cpp index 4589c50..22ec8f6 100644 --- a/test/unordered_load.cpp +++ b/test/unordered_load.cpp @@ -17,236 +17,313 @@ #define BOOST_TEST_MODULE UnorderedLoad -#include - #include "SimpleTree.h" +#include -const std::string simple_unordered_strategy = "{\"version\":1.0,\"type\":\"state->regressor\",\"representation\":\"map\",\"actions\":{" -" \"0\":\"Controller._id4->Controller._id2 { 1, tau, minute_clock := TIME_OFFSET, initValues() }\"," -" \"1\":\"Controller._id2->Controller.Wait { 0 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 0, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"2\":\"Controller._id2->Controller.Wait { 1 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 1, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"3\":\"Controller._id2->Controller.Wait { 2 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 2, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"4\":\"Controller._id2->Controller.Wait { 3 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 3, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"5\":\"Controller._id2->Controller.Wait { 4 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 4, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"6\":\"Controller._id2->Controller.Wait { 5 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 5, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"7\":\"Controller._id2->Controller.Wait { 6 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 6, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"9\":\"Controller._id2->Controller.Wait { 8 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 8, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"10\":\"Controller._id2->Controller.Wait { 9 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 9, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"11\":\"Controller._id2->Controller.Wait { 10 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 10, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"12\":\"Controller._id2->Controller.Wait { 1, tau, consumed_power := 0, heat_produced := 0 }\"," -" \"13\":\"WAIT\"" -"},\"statevars\":[" -" \"round(minute_clock) \"" -"],\"pointvars\":[" -"],\"locationnames\":{" -" \"Fetch_Data.location\":{" -" \"0\":\"_id5\"" -" }," -" \"Room1.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Room2.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Room3.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Room4.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Optimization.location\":{" -" \"0\":\"_id1\"" -" }," -" \"Controller.location\":{" -" \"0\":\"_id2\"," -" \"1\":\"Wait\"," -" \"2\":\"_id4\"" -" }" -"},\"regressors\":{" -" \"(30)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 1.294636921229531," -" \"12\" : 3.091107032150207" -" }" -" }," -" \"(210)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 1.009248500699941," -" \"12\" : 1.028772620412916" -" }" -" }," -" \"(15)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 2.855407148131672," -" \"12\" : 1.352930166302546" -" }" -" }" -" }" -"}"; - - - - -const std::string unordered_strategy = "{\"version\":1.0,\"type\":\"state->regressor\",\"representation\":\"map\",\"actions\":{" -" \"0\":\"Controller._id4->Controller._id2 { 1, tau, minute_clock := TIME_OFFSET, initValues() }\"," -" \"1\":\"Controller._id2->Controller.Wait { 0 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 0, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"2\":\"Controller._id2->Controller.Wait { 1 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 1, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"3\":\"Controller._id2->Controller.Wait { 2 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 2, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"4\":\"Controller._id2->Controller.Wait { 3 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 3, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"5\":\"Controller._id2->Controller.Wait { 4 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 4, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"6\":\"Controller._id2->Controller.Wait { 5 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 5, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"7\":\"Controller._id2->Controller.Wait { 6 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 6, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"9\":\"Controller._id2->Controller.Wait { 8 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 8, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"10\":\"Controller._id2->Controller.Wait { 9 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 9, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"11\":\"Controller._id2->Controller.Wait { 10 >= 10 && any_on(), tau, consumed_power := 0.625000 + 0.187500 * 10, heat_produced := consumed_power * calculateCOP(), setMassFlow() }\"," -" \"12\":\"Controller._id2->Controller.Wait { 1, tau, consumed_power := 0, heat_produced := 0 }\"," -" \"13\":\"WAIT\"" -"},\"statevars\":[" -" \"round(minute_clock) \"" -"],\"pointvars\":[" -"],\"locationnames\":{" -" \"Fetch_Data.location\":{" -" \"0\":\"_id5\"" -" }," -" \"Room1.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Room2.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Room3.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Room4.location\":{" -" \"0\":\"_id0\"" -" }," -" \"Optimization.location\":{" -" \"0\":\"_id1\"" -" }," -" \"Controller.location\":{" -" \"0\":\"_id2\"," -" \"1\":\"Wait\"," -" \"2\":\"_id4\"" -" }" -"},\"regressors\":{" -" \"(195)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 1.767005809675129," -" \"12\" : 0.5472758743181302" -" }" -" }," -" \"(180)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 2.107357559876886," -" \"12\" : 0.841995896951961" -" }" -" }," -" \"(225)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 0.3895094003837855," -" \"12\" : 1.438914715299473" -" }" -" }," -" \"(120)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 3.562263183988571," -" \"12\" : 3.01531941566271" -" }" -" }," -" \"(90)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 4.595961684754502," -" \"12\" : 4.520572775155376" -" }" -" }," -" \"(75)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 5.200779712113728," -" \"12\" : 1.1120904838198" -" }" -" }," -" \"(165)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 2.422010195182813," -" \"12\" : 1.850311355224428" -" }" -" }," -" \"(240)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 0.1842626478060555," -" \"12\" : 0.5429048553483474" -" }" -" }," -" \"(150)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 1.802785410011689," -" \"12\" : 2.744820174794056" -" }" -" }," -" \"(135)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 4.082210512180363," -" \"12\" : 1.046474095029413" -" }" -" }," -" \"(105)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 3.506084076677214," -" \"12\" : 4.529894227895771" -" }" -" }," -" \"(45)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 2.923974662511487," -" \"12\" : 3.712782046133843" -" }" -" }," -" \"(30)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 1.294636921229531," -" \"12\" : 3.091107032150207" -" }" -" }," -" \"(210)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 1.009248500699941," -" \"12\" : 1.028772620412916" -" }" -" }," -" \"(15)\":" -" {\"type\":\"act->point->val\",\"representation\":\"simpletree\",\"minimize\":1,\"regressor\":" -" {" -" \"11\" : 2.855407148131672," -" \"12\" : 1.352930166302546" -" }" -" }" -" }" -"}"; - +const std::string simple_unordered_strategy = + "{\"version\":1.0,\"type\":\"state->regressor\",\"representation\":\"map\"," + "\"actions\":{" + " \"0\":\"Controller._id4->Controller._id2 { 1, tau, minute_clock := " + "TIME_OFFSET, initValues() }\"," + " \"1\":\"Controller._id2->Controller.Wait { 0 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 0, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"2\":\"Controller._id2->Controller.Wait { 1 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 1, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"3\":\"Controller._id2->Controller.Wait { 2 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 2, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"4\":\"Controller._id2->Controller.Wait { 3 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 3, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"5\":\"Controller._id2->Controller.Wait { 4 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 4, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"6\":\"Controller._id2->Controller.Wait { 5 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 5, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"7\":\"Controller._id2->Controller.Wait { 6 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 6, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"9\":\"Controller._id2->Controller.Wait { 8 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 8, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"10\":\"Controller._id2->Controller.Wait { 9 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 9, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"11\":\"Controller._id2->Controller.Wait { 10 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 10, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"12\":\"Controller._id2->Controller.Wait { 1, tau, consumed_power := " + "0, heat_produced := 0 }\"," + " \"13\":\"WAIT\"" + "},\"statevars\":[" + " \"round(minute_clock) \"" + "],\"pointvars\":[" + "],\"locationnames\":{" + " \"Fetch_Data.location\":{" + " \"0\":\"_id5\"" + " }," + " \"Room1.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Room2.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Room3.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Room4.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Optimization.location\":{" + " \"0\":\"_id1\"" + " }," + " \"Controller.location\":{" + " \"0\":\"_id2\"," + " \"1\":\"Wait\"," + " \"2\":\"_id4\"" + " }" + "},\"regressors\":{" + " \"(30)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 1.294636921229531," + " \"12\" : 3.091107032150207" + " }" + " }," + " \"(210)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 1.009248500699941," + " \"12\" : 1.028772620412916" + " }" + " }," + " \"(15)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 2.855407148131672," + " \"12\" : 1.352930166302546" + " }" + " }" + " }" + "}"; +const std::string unordered_strategy = "{\"version\":1.0,\"type\":\"state->regressor\",\"representation\":\"map\"," + "\"actions\":{" + " \"0\":\"Controller._id4->Controller._id2 { 1, tau, minute_clock := " + "TIME_OFFSET, initValues() }\"," + " \"1\":\"Controller._id2->Controller.Wait { 0 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 0, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"2\":\"Controller._id2->Controller.Wait { 1 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 1, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"3\":\"Controller._id2->Controller.Wait { 2 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 2, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"4\":\"Controller._id2->Controller.Wait { 3 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 3, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"5\":\"Controller._id2->Controller.Wait { 4 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 4, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"6\":\"Controller._id2->Controller.Wait { 5 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 5, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"7\":\"Controller._id2->Controller.Wait { 6 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 6, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"9\":\"Controller._id2->Controller.Wait { 8 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 8, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"10\":\"Controller._id2->Controller.Wait { 9 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 9, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"11\":\"Controller._id2->Controller.Wait { 10 >= 10 && any_on(), tau, " + "consumed_power := 0.625000 + 0.187500 * 10, heat_produced := " + "consumed_power * calculateCOP(), setMassFlow() }\"," + " \"12\":\"Controller._id2->Controller.Wait { 1, tau, consumed_power := " + "0, heat_produced := 0 }\"," + " \"13\":\"WAIT\"" + "},\"statevars\":[" + " \"round(minute_clock) \"" + "],\"pointvars\":[" + "],\"locationnames\":{" + " \"Fetch_Data.location\":{" + " \"0\":\"_id5\"" + " }," + " \"Room1.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Room2.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Room3.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Room4.location\":{" + " \"0\":\"_id0\"" + " }," + " \"Optimization.location\":{" + " \"0\":\"_id1\"" + " }," + " \"Controller.location\":{" + " \"0\":\"_id2\"," + " \"1\":\"Wait\"," + " \"2\":\"_id4\"" + " }" + "},\"regressors\":{" + " \"(195)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 1.767005809675129," + " \"12\" : 0.5472758743181302" + " }" + " }," + " \"(180)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 2.107357559876886," + " \"12\" : 0.841995896951961" + " }" + " }," + " \"(225)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 0.3895094003837855," + " \"12\" : 1.438914715299473" + " }" + " }," + " \"(120)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 3.562263183988571," + " \"12\" : 3.01531941566271" + " }" + " }," + " \"(90)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 4.595961684754502," + " \"12\" : 4.520572775155376" + " }" + " }," + " \"(75)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 5.200779712113728," + " \"12\" : 1.1120904838198" + " }" + " }," + " \"(165)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 2.422010195182813," + " \"12\" : 1.850311355224428" + " }" + " }," + " \"(240)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 0.1842626478060555," + " \"12\" : 0.5429048553483474" + " }" + " }," + " \"(150)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 1.802785410011689," + " \"12\" : 2.744820174794056" + " }" + " }," + " \"(135)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 4.082210512180363," + " \"12\" : 1.046474095029413" + " }" + " }," + " \"(105)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 3.506084076677214," + " \"12\" : 4.529894227895771" + " }" + " }," + " \"(45)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 2.923974662511487," + " \"12\" : 3.712782046133843" + " }" + " }," + " \"(30)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 1.294636921229531," + " \"12\" : 3.091107032150207" + " }" + " }," + " \"(210)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 1.009248500699941," + " \"12\" : 1.028772620412916" + " }" + " }," + " \"(15)\":" + " " + "{\"type\":\"act->point->val\",\"representation\":\"simpletree\"," + "\"minimize\":1,\"regressor\":" + " {" + " \"11\" : 2.855407148131672," + " \"12\" : 1.352930166302546" + " }" + " }" + " }" + "}"; BOOST_AUTO_TEST_CASE(SimpleUnorderedKeyLoad) { - std::stringstream ss(simple_unordered_strategy); - auto strategy = SimpleTree::parse(ss, false, false, 0); + auto is = std::istringstream{simple_unordered_strategy}; + auto strategy = SimpleTree::parse(is, false, false, 0); double disc[1] = {15.0}; BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 2.855407148131672); BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.352930166302546); @@ -262,8 +339,8 @@ BOOST_AUTO_TEST_CASE(SimpleUnorderedKeyLoad) BOOST_AUTO_TEST_CASE(UnorderedKeyLoad) { - std::stringstream ss(unordered_strategy); - auto strategy = SimpleTree::parse(ss, false, false, 0); + auto is = std::stringstream{unordered_strategy}; + auto strategy = SimpleTree::parse(is, false, false, 0); double disc[1] = {15.0}; BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 11), 2.855407148131672); BOOST_CHECK_EQUAL(strategy.value(disc, nullptr, 12), 1.352930166302546); diff --git a/test/utilities_test.cpp b/test/utilities_test.cpp new file mode 100644 index 0000000..b0e3d04 --- /dev/null +++ b/test/utilities_test.cpp @@ -0,0 +1,27 @@ +#include "utilities.hpp" + +#include + +template +void test(const std::string& name) +{ + if constexpr (has_from_chars_v) { + std::cout << "from_chars(" << name << ") is supported\n"; + } else { + std::cout << "from_chars(" << name << ") is NOT supported\n"; + } +} + +int main() +{ + test("bool"); + test("char"); + test("int"); + test("unsigned int"); + test("long"); + test("unsigned long"); + test("long long"); + test("unsigned long long"); + test("float"); + test("double"); +} \ No newline at end of file