├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── src ├── CodeGen │ ├── CodeGen.cpp │ ├── CodeGen.h │ ├── FPExprCodeGenerator.cpp │ ├── FPExprCodeGenerator.h │ ├── FPExprLibGenerator.cpp │ └── FPExprLibGenerator.h ├── ExprAnalyzer │ ├── FPExprAnalyzer.cpp │ └── FPExprAnalyzer.h ├── IRGen │ ├── FPIRGenerator.cpp │ └── FPIRGenerator.h ├── Optimizer │ ├── ModelValidator.cpp │ ├── ModelValidator.h │ ├── NLoptOptimizer.cpp │ └── NLoptOptimizer.h ├── Utils │ ├── FPAUtils.cpp │ └── FPAUtils.h └── main.cpp └── tools ├── README.md ├── bh_solver ├── .gitignore └── bh_solver.py └── nl_solver ├── .gitignore ├── CMakeLists.txt ├── GOFuncsMap.h └── nl_solver.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directories 2 | /build-* 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | 33 | # Idea Project 34 | .idea 35 | 36 | # Vim backups 37 | *~ 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(gosat CXX) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fno-rtti") 5 | 6 | set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -Wno-unused-parameter\ 7 | -pedantic -fno-inline -fno-omit-frame-pointer" CACHE STRING 8 | "Flags used by the C++ compiler for Debug builds." 9 | FORCE) 10 | 11 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 " CACHE STRING 12 | "Flags used by the C++ compiler for Release builds." 13 | FORCE) 14 | 15 | set(CMAKE_VERBOSE_MAKEFILE ON) 16 | 17 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 18 | 19 | # Finds and configures LLVM build directories 20 | find_package(LLVM REQUIRED CONFIG) 21 | add_definitions(${LLVM_DEFINITIONS}) 22 | include_directories(${LLVM_INCLUDE_DIRS}) 23 | link_directories(${LLVM_LIBRARY_DIRS}) 24 | 25 | if(CMAKE_PREFIX_PATH) 26 | include_directories(${CMAKE_PREFIX_PATH}/include) 27 | endif() 28 | 29 | if(NOT CMAKE_BUILD_TYPE) 30 | message(STATUS "Setting build type to 'Debug' as none was specified.") 31 | message(STATUS "Possible build types are Debug and Release.") 32 | set(CMAKE_BUILD_TYPE Debug) 33 | else() 34 | message(STATUS "Current build type is ${CMAKE_BUILD_TYPE}") 35 | endif() 36 | 37 | include_directories(src) 38 | 39 | add_library(libz3 SHARED IMPORTED) 40 | add_library(libnlopt SHARED IMPORTED) 41 | 42 | find_library(libz3-path NAMES "libz3.so") 43 | if(NOT libz3-path) 44 | message(FATAL_ERROR "libz3.so was not found! please check your installation and/or cmake configurations. ") 45 | endif() 46 | 47 | find_library(libnlopt-path NAMES "libnlopt.so") 48 | if(NOT libnlopt-path) 49 | message(FATAL_ERROR "libnlopt.so was not found! please check your installation and/or cmake configurations. ") 50 | endif() 51 | 52 | set_property(TARGET libz3 PROPERTY IMPORTED_LOCATION ${libz3-path}) 53 | set_property(TARGET libnlopt PROPERTY IMPORTED_LOCATION ${libnlopt-path}) 54 | 55 | llvm_map_components_to_libnames(llvm_libs_required 56 | Core 57 | ExecutionEngine 58 | MCJIT 59 | native) 60 | 61 | set(SOURCE_FILES 62 | src/main.cpp 63 | src/Utils/FPAUtils.cpp 64 | src/ExprAnalyzer/FPExprAnalyzer.cpp 65 | src/IRGen/FPIRGenerator.cpp 66 | src/CodeGen/FPExprCodeGenerator.cpp 67 | src/CodeGen/FPExprLibGenerator.cpp 68 | src/Optimizer/NLoptOptimizer.cpp 69 | src/CodeGen/CodeGen.cpp 70 | src/Optimizer/ModelValidator.cpp) 71 | 72 | add_subdirectory(tools/nl_solver) 73 | add_executable(gosat ${SOURCE_FILES}) 74 | 75 | target_link_libraries(gosat libz3 libnlopt ${llvm_libs_required}) 76 | 77 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 University of Kaiserslautern 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goSAT 2 | goSAT is an SMT solver for the theory of floating-point arithmetic (FPA) that is based 3 | on global optimization. 4 | 5 | ## Overview 6 | goSAT is an SMT solver based on mathematical global optimization. It casts the satisfiability 7 | problem of FPA to a corresponding global optimization problem. 8 | It builds on the ideas proposed in [XSat]. Compared to XSat, we implemented 9 | the following key features: 10 | 11 | - JIT compilation of SMT formulas using LLVM. 12 | - Integration with NLopt, a feature-rich mathematical optimization package. 13 | 14 | In our experiments, goSAT demonstrated better *efficiency* compared to `z3` and `mathsat` 15 | by an order of magnitude or more. 16 | More details are available in our FMCAD'17 [paper] and this [appendix]. 17 | 18 | ## Citing 19 | 20 | If you use goSAT in an academic work, please cite: 21 | 22 | ``` 23 | @inproceedings{BenKhadra2017a, 24 | author = {{Ben Khadra}, M Ammar and Stoffel, Dominik and Kunz, Wolfgang}, 25 | booktitle = {Proceedings of Formal Methods in Computer-Aided Design (FMCAD'17)}, 26 | doi = {10.23919/FMCAD.2017.8102235}, 27 | pages = {11--14}, 28 | title = {{goSAT: Floating-point Satisfiability as Global Optimization}}, 29 | year = {2017} 30 | } 31 | ``` 32 | 33 | ## Dependencies 34 | 35 | This project depends on: 36 | 37 | - [Z3] library for input file parsing and model validation 38 | - [LLVM] for JIT execution of SMT formulas. 39 | - [NLopt] for finding the minima of the objective function. 40 | 41 | Installing z3 and nlopt should be straightforward. As for installing LLVM, you might 42 | find this [tutorial] to be useful. 43 | 44 | goSAT is known to work with z3 v4.6, LLVM v4.0.1, nlopt v2.4.2. 45 | We recommend building LLVM from source as the official pre-built package might be broken. 46 | Specifically, we are aware of such issues on Ubuntu 16.04. 47 | 48 | ## Building 49 | 50 | You can build the project using a command like, 51 | 52 | ```bash 53 | mkdir build; cd build 54 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/home/gosat/local/ -DLLVM_DIR=/home/gosat/local/llvm/lib/cmake/llvm/ .. 55 | make 56 | ``` 57 | assuming that `libnlopt` and `libz3` are installed at the prefix `${HOME}/local/` 58 | while `llvm` is installed at `${HOME}/local/llvm`. 59 | 60 | ## Usage 61 | An SMT file needs to be provided using `-f` option. Additionally, the tool operation mode 62 | can be set. goSAT supports three operation modes: 63 | 64 | - **Native solving**. This is the default mode where a given formula is first transformed 65 | to an objective function in LLVM IR. Then, the objective function is jitted using MCJIT 66 | and solved using the NLopt backend. This mode can be explicitly set 67 | using `-mode=go` option. 68 | 69 | - **Code generation**. This mode can be enabled using `-mode=cg` option. 70 | It mimics the behavior of [XSat] where C code gets generated representing 71 | the objective function. 72 | Generated code needs to be compiled and solved using on of the tools we provide in 73 | `tools` folder. Moreover, you can specify an API dump format like 74 | `-mode=cg -fmt=plain` for generating several objective functions. The latter is useful for 75 | building a library as discussed [here](tools/README.md). 76 | 77 | - **Formula analysis**. A simple analysis to show the number of variables, their 78 | types, and other misc facts about a given SMT formula. This mode is enabled 79 | using `-mode=fa` option. 80 | 81 | The default output of goSAT is in csv format. It lists the benchmark name, sat result, 82 | elapsed time (seconds), minimum found, and status code returned by `nlopt`. 83 | The minimum found should be zero in case of `sat`. 84 | Use option `-smtlib-output` to obtain conventional solver output which is more succinct. 85 | 86 | Note that goSAT relies on stochastic search which means that we can 87 | not generally *prove* an instance to be `unsat` even if it is actually so. 88 | Therefore, goSAT reports back either `sat` or `unknown`. 89 | Being stochastic, gives goSAT an edge in efficiency over conventional solvers like `z3` 90 | and `mathsat`. However, this also restricts the application domains of goSAT. 91 | 92 | ## Model validation 93 | 94 | In the case of `sat` result, it is possible to intruct `goSAT` to externally validate the 95 | generated model using `z3`. This can be done by providing parameter `-c`. For example, 96 | 97 | ```bash 98 | ./gosat -c -f formula.smt2 99 | ``` 100 | 101 | So far, we have not encountered any unsound result. Please report to us if you 102 | find any such cases. 103 | 104 | 105 | [Z3]: 106 | [LLVM]: 107 | [online]: 108 | [XSat]: 109 | [NLopt]: 110 | [paper]: 111 | [appendix]: 112 | [tutorial]: 113 | -------------------------------------------------------------------------------- /src/CodeGen/CodeGen.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "CodeGen.h" 11 | 12 | namespace gosat { 13 | 14 | const std::string CodeGenStr::kFunName = "gofunc"; 15 | const std::string CodeGenStr::kFunInput = "x"; 16 | const std::string CodeGenStr::kFunDis = "fp64_dis"; 17 | const std::string CodeGenStr::kFunEqDis = "fp64_eq_dis"; 18 | const std::string CodeGenStr::kFunNEqDis = "fp64_neq_dis"; 19 | const std::string CodeGenStr::kFunIsNan = "fp64_isnan"; 20 | 21 | Symbol::Symbol(SymbolKind kind, const z3::expr expr) : 22 | m_kind{kind}, 23 | m_expr{expr}, 24 | m_name{"expr_" + std::to_string(expr.hash()) 25 | + (kind == SymbolKind::kNegatedExpr ? "n" 26 | : "")} 27 | {} 28 | 29 | SymbolKind Symbol::kind() const noexcept 30 | { 31 | return m_kind; 32 | } 33 | 34 | const z3::expr* Symbol::expr() const noexcept 35 | { 36 | return &m_expr; 37 | } 38 | 39 | const char* Symbol::name() const noexcept 40 | { 41 | return m_name.c_str(); 42 | } 43 | 44 | bool Symbol::isNegated() const noexcept 45 | { 46 | return m_kind == SymbolKind::kNegatedExpr; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/CodeGen/CodeGen.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include "z3++.h" 13 | 14 | namespace gosat { 15 | /** 16 | * /brief String constants used for code generation 17 | */ 18 | class CodeGenStr { 19 | public: 20 | static const std::string kFunName; 21 | static const std::string kFunInput; 22 | static const std::string kFunDis; 23 | static const std::string kFunEqDis; 24 | static const std::string kFunNEqDis; 25 | static const std::string kFunIsNan; 26 | }; 27 | 28 | enum class SymbolKind : unsigned { 29 | kExpr = 0, 30 | kNegatedExpr = 1, 31 | kFP32Const = 2, 32 | kFP64Const = 4, 33 | kFP32Var = 8, 34 | kFP64Var = 16, 35 | kUnknown = 32 36 | }; 37 | 38 | enum class FPVarKind : unsigned { 39 | kUnknown, 40 | kFP32, 41 | kFP64, 42 | }; 43 | 44 | class Symbol { 45 | public: 46 | Symbol() = delete; 47 | 48 | virtual ~Symbol() = default; 49 | 50 | Symbol(const Symbol&) = default; 51 | 52 | Symbol& operator=(const Symbol&) = default; 53 | 54 | Symbol& operator=(Symbol&&) = default; 55 | 56 | explicit Symbol(SymbolKind kind, const z3::expr expr); 57 | 58 | SymbolKind kind() const noexcept; 59 | 60 | const z3::expr* expr() const noexcept; 61 | 62 | const char* name() const noexcept; 63 | 64 | bool isNegated() const noexcept; 65 | 66 | private: 67 | const SymbolKind m_kind; 68 | const z3::expr m_expr; 69 | const std::string m_name; 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /src/CodeGen/FPExprCodeGenerator.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "FPExprCodeGenerator.h" 11 | #include "Utils/FPAUtils.h" 12 | #include 13 | 14 | namespace gosat { 15 | 16 | FPExprCodeGenerator::FPExprCodeGenerator() : 17 | m_has_invalid_fp_const{false}, 18 | m_has_unsupported_expr{false} 19 | { 20 | } 21 | 22 | bool 23 | FPExprCodeGenerator::hasRNERoundingMode(const z3::expr& expr) const noexcept 24 | { 25 | return expr.arg(0).decl().decl_kind() == Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; 26 | } 27 | 28 | void 29 | FPExprCodeGenerator::genFuncCode 30 | (const std::string& func_name, const z3::expr& expr) noexcept 31 | { 32 | m_func_code = ""; 33 | m_func_code.reserve(16 * 1024); 34 | includeFuncHeader(func_name); 35 | genFuncCodeRecursive(expr, false); 36 | m_func_code.append("return expr_" + std::to_string(expr.hash()) + ";\n"); 37 | includeFuncFooter(); 38 | } 39 | 40 | void 41 | FPExprCodeGenerator::includeFuncHeader(const std::string& func_name) noexcept 42 | { 43 | m_func_code += FPExprCodeGenerator::genFuncSignature(func_name) + "{ \n"; 44 | } 45 | 46 | void 47 | FPExprCodeGenerator::includeFuncFooter() noexcept 48 | { 49 | m_func_code += "}\n"; 50 | } 51 | 52 | const Symbol* 53 | FPExprCodeGenerator::genNumeralCode(const z3::expr& expr) 54 | { 55 | if (expr.get_sort().sort_kind() == Z3_FLOATING_POINT_SORT) { 56 | unsigned signd = Z3_fpa_get_sbits(expr.ctx(), expr.get_sort()); 57 | unsigned expo = Z3_fpa_get_ebits(expr.ctx(), expr.get_sort()); 58 | if (fpa_util::isFloat32(expo, signd)) { 59 | auto result_pair = insertSymbol(SymbolKind::kFP32Const, expr); 60 | if (result_pair.second) { 61 | // TODO: handling FP32 should be configurable 62 | float result = fpa_util::toFloat32(expr); 63 | m_func_code += "const float "; 64 | m_func_code += result_pair.first->name(); 65 | m_func_code += " = " + std::to_string(result) + "f ;\n"; 66 | } 67 | return result_pair.first; 68 | } else { 69 | assert(fpa_util::isFloat64(expo, signd) 70 | && "Invalid float format!"); 71 | auto result_pair = insertSymbol(SymbolKind::kFP64Const, expr); 72 | if (result_pair.second) { 73 | double result = fpa_util::toFloat64(expr); 74 | m_func_code += "const double "; 75 | m_func_code += result_pair.first->name(); 76 | m_func_code += " = " + std::to_string(result) + " ;\n"; 77 | } 78 | return result_pair.first; 79 | } 80 | } 81 | if (expr.decl().decl_kind() == Z3_OP_BNUM) { 82 | auto result_pair = insertSymbol(SymbolKind::kFP64Const, expr); 83 | if (result_pair.second) { 84 | std::string result = 85 | Z3_ast_to_string(expr.ctx(), static_cast(expr)); 86 | result.replace(0, 1, 1, '0'); 87 | m_func_code += "const double "; 88 | m_func_code += result_pair.first->name(); 89 | m_func_code += " = " + result + " ;\n"; 90 | } 91 | return result_pair.first; 92 | } 93 | return nullptr; 94 | } 95 | 96 | const Symbol* 97 | FPExprCodeGenerator::genFuncCodeRecursive 98 | (const z3::expr& expr, bool is_negated) noexcept 99 | { 100 | if (!expr.is_app()) { 101 | // is_app <==> Z3_NUMERAL_AST || Z3_APP_AST 102 | m_has_unsupported_expr = true; 103 | return nullptr; 104 | } 105 | if (expr.is_numeral()) { 106 | return genNumeralCode(expr); 107 | } 108 | if (fpa_util::isFPVar(expr)) { 109 | // TODO: handling FP32 might be configurable here 110 | auto result_pair = insertSymbol(SymbolKind::kFP64Var, expr); 111 | if (result_pair.second) { 112 | m_func_code += "const double "; 113 | m_func_code += result_pair.first->name(); 114 | m_func_code += " = " + CodeGenStr::kFunInput; 115 | m_func_code += "[" + std::to_string(m_var_sym_vec.size()) + "] ;\n"; 116 | m_var_sym_vec.push_back(result_pair.first); 117 | } 118 | return result_pair.first; 119 | } 120 | if (!fpa_util::isBoolExpr(expr)) { 121 | is_negated = false; 122 | } 123 | SymbolKind kind = 124 | (is_negated) ? SymbolKind::kNegatedExpr : SymbolKind::kExpr; 125 | if (expr.decl().decl_kind() == Z3_OP_NOT && !is_negated) { 126 | is_negated = true; 127 | } 128 | auto result_pair = insertSymbol(kind, expr); 129 | if (!result_pair.second) { 130 | return result_pair.first; 131 | } 132 | // Expr not visited before 133 | std::vector arg_syms; 134 | arg_syms.reserve(expr.num_args()); 135 | for (uint i = 0; i < expr.num_args(); ++i) { 136 | arg_syms.push_back(genFuncCodeRecursive(expr.arg(i), is_negated)); 137 | } 138 | genExprCode(result_pair.first, arg_syms); 139 | return result_pair.first; 140 | } 141 | 142 | const std::string& 143 | FPExprCodeGenerator::getFuncCode() const noexcept 144 | { 145 | return m_func_code; 146 | } 147 | 148 | static inline std::string 149 | genBinArgExpr(const char* op, const Symbol* arg_1, const Symbol* arg_2) 150 | { 151 | return std::string() 152 | + arg_1->name() 153 | + op 154 | + arg_2->name() 155 | + ";\n"; 156 | } 157 | 158 | static std::string 159 | genMultiArgExpr(const char* op, std::vector& args_syms) 160 | { 161 | assert(args_syms.size() >= 2 && "Invalid multi-argument expression"); 162 | if (args_syms.size() == 2) { 163 | return genBinArgExpr(op, args_syms[0], args_syms[1]); 164 | } 165 | std::string result; 166 | for (unsigned i = 0; i < args_syms.size() - 1; ++i) { 167 | result += args_syms[i]->name(); 168 | result += op; 169 | } 170 | return result + args_syms[args_syms.size() - 1]->name() + ";\n"; 171 | } 172 | 173 | static inline 174 | std::string genBinArgCompExpr 175 | (const char* op, const Symbol* arg_1, const Symbol* arg_2) 176 | { 177 | return std::string("(") 178 | + arg_1->name() 179 | + op 180 | + arg_2->name() 181 | + ")" 182 | + "? 0: " 183 | + CodeGenStr::kFunDis 184 | + "(" 185 | + arg_1->name() 186 | + "," 187 | + arg_2->name() 188 | + ")" 189 | + ";\n"; 190 | } 191 | 192 | static inline 193 | std::string genBinArgCompExpr2 194 | (const char* op, const Symbol* arg_1, const Symbol* arg_2) 195 | { 196 | return std::string("(") 197 | + arg_1->name() 198 | + op 199 | + arg_2->name() 200 | + ")" 201 | + "? 0: " 202 | + CodeGenStr::kFunDis 203 | + "(" 204 | + arg_1->name() 205 | + ", " 206 | + arg_2->name() 207 | + ")" 208 | + " + 1;\n"; 209 | } 210 | 211 | static inline 212 | std::string genEqCompExpr(const Symbol* arg_1, const Symbol* arg_2) 213 | { 214 | return CodeGenStr::kFunDis 215 | + "(" 216 | + arg_1->name() 217 | + "," 218 | + arg_2->name() 219 | + ")" 220 | + ";\n"; 221 | } 222 | 223 | static inline 224 | std::string genNotEqCompExpr(const Symbol* arg_1, const Symbol* arg_2) 225 | { 226 | return std::string("(") 227 | + arg_1->name() 228 | + " != " 229 | + arg_2->name() 230 | + ")" 231 | + "? 0: 1 ;\n"; 232 | } 233 | 234 | void 235 | FPExprCodeGenerator::genExprCode(const Symbol* expr_sym, 236 | std::vector& args_syms) noexcept 237 | { 238 | m_func_code.append("const double "); 239 | m_func_code.append(expr_sym->name()); 240 | m_func_code.append(" = "); 241 | switch (expr_sym->expr()->decl().decl_kind()) { 242 | // Boolean operations 243 | case Z3_OP_TRUE: 244 | if (expr_sym->isNegated()) 245 | m_func_code.append("1.0;\n"); 246 | else 247 | m_func_code.append("0.0;\n"); 248 | break; 249 | case Z3_OP_FALSE: 250 | if (expr_sym->isNegated()) 251 | m_func_code.append("0.0;\n"); 252 | else 253 | m_func_code.append("1.0;\n"); 254 | break; 255 | case Z3_OP_EQ: 256 | case Z3_OP_FPA_EQ: 257 | if (expr_sym->isNegated()) 258 | m_func_code.append 259 | (genNotEqCompExpr(args_syms[0], args_syms[1])); 260 | else 261 | m_func_code.append 262 | (genEqCompExpr(args_syms[0], args_syms[1])); 263 | break; 264 | case Z3_OP_NOT: 265 | m_func_code.append 266 | (std::string(args_syms[0]->name()) + ";\n"); 267 | break; 268 | case Z3_OP_AND: 269 | if (expr_sym->isNegated()) 270 | m_func_code.append(genMultiArgExpr(" * ", args_syms)); 271 | else 272 | m_func_code.append(genMultiArgExpr(" + ", args_syms)); 273 | break; 274 | case Z3_OP_OR: 275 | if (expr_sym->isNegated()) 276 | m_func_code.append(genMultiArgExpr(" + ", args_syms)); 277 | else 278 | m_func_code.append(genMultiArgExpr(" * ", args_syms)); 279 | break; 280 | // Floating point operations 281 | case Z3_OP_FPA_PLUS_INF: 282 | m_func_code.append 283 | ("INFINITY;\n"); 284 | break; 285 | case Z3_OP_FPA_MINUS_INF: 286 | m_func_code.append 287 | ("-INFINITY;\n"); 288 | break; 289 | case Z3_OP_FPA_NAN: 290 | m_func_code.append 291 | ("NAN;\n"); 292 | break; 293 | case Z3_OP_FPA_PLUS_ZERO: 294 | m_func_code.append 295 | ("0;\n"); 296 | break; 297 | case Z3_OP_FPA_MINUS_ZERO: 298 | m_func_code.append 299 | ("-0;\n"); 300 | break; 301 | case Z3_OP_FPA_ADD: 302 | m_func_code.append( 303 | genBinArgExpr(" + ", args_syms[1], args_syms[2])); 304 | break; 305 | case Z3_OP_FPA_SUB: 306 | m_func_code.append( 307 | genBinArgExpr(" - ", args_syms[1], args_syms[2])); 308 | break; 309 | case Z3_OP_FPA_NEG: 310 | m_func_code.append 311 | ("-" + std::string(args_syms[0]->name()) + ";\n"); 312 | break; 313 | case Z3_OP_FPA_MUL: 314 | m_func_code.append( 315 | genBinArgExpr(" * ", args_syms[1], args_syms[2])); 316 | break; 317 | case Z3_OP_FPA_DIV: 318 | m_func_code.append( 319 | genBinArgExpr(" / ", args_syms[1], args_syms[2])); 320 | break; 321 | case Z3_OP_FPA_REM: 322 | m_func_code.append( 323 | genBinArgExpr(" % ", args_syms[1], args_syms[2])); 324 | break; 325 | case Z3_OP_FPA_ABS: 326 | m_func_code.append( 327 | "abs(" + std::string(args_syms[0]->name()) + ");\n"); 328 | break; 329 | case Z3_OP_FPA_LT: 330 | if (expr_sym->isNegated()) 331 | m_func_code.append 332 | (genBinArgCompExpr(" >= ", args_syms[0], args_syms[1])); 333 | else 334 | m_func_code.append 335 | (genBinArgCompExpr2(" < ", args_syms[0], args_syms[1])); 336 | break; 337 | case Z3_OP_FPA_GT: 338 | if (expr_sym->isNegated()) 339 | m_func_code.append 340 | (genBinArgCompExpr(" <= ", args_syms[0], args_syms[1])); 341 | else 342 | m_func_code.append 343 | (genBinArgCompExpr2(" > ", args_syms[0], args_syms[1])); 344 | break; 345 | case Z3_OP_FPA_LE: 346 | if (expr_sym->isNegated()) 347 | m_func_code.append 348 | (genBinArgCompExpr2(" > ", args_syms[0], args_syms[1])); 349 | else 350 | m_func_code.append 351 | (genBinArgCompExpr(" <= ", args_syms[0], args_syms[1])); 352 | break; 353 | case Z3_OP_FPA_GE: 354 | if (expr_sym->isNegated()) 355 | m_func_code.append 356 | (genBinArgCompExpr2(" < ", args_syms[0], args_syms[1])); 357 | else 358 | m_func_code.append 359 | (genBinArgCompExpr(" >= ", args_syms[0], args_syms[1])); 360 | break; 361 | case Z3_OP_FPA_TO_FP: 362 | m_func_code.append( 363 | std::string(args_syms[args_syms.size() - 1]->name()) + 364 | ";\n"); 365 | break; 366 | default: 367 | m_func_code.append( 368 | "Unsupported expr:" + expr_sym->expr()->decl().name().str() 369 | + "\n"); 370 | } 371 | } 372 | 373 | unsigned long 374 | FPExprCodeGenerator::getVarCount() const noexcept 375 | { 376 | return m_var_sym_vec.size(); 377 | } 378 | 379 | std::pair 380 | FPExprCodeGenerator::insertSymbol(const SymbolKind kind, 381 | const z3::expr expr) noexcept 382 | { 383 | if (kind != SymbolKind::kNegatedExpr) { 384 | auto result = m_expr_sym_map.insert 385 | ({expr.hash(), Symbol(kind, expr)}); 386 | return {&(*result.first).second, result.second}; 387 | } else { 388 | // XXX: would this weaken hashing? 389 | auto result = m_expr_sym_map.insert 390 | ({expr.hash() + static_cast(kind), 391 | Symbol(kind, expr)}); 392 | return {&(*result.first).second, result.second}; 393 | } 394 | } 395 | 396 | const char* 397 | FPExprCodeGenerator::getSymbolName(const SymbolKind kind, 398 | const z3::expr& expr) const noexcept 399 | { 400 | return (*m_expr_sym_map.find( 401 | expr.hash() + static_cast(kind))).second.name(); 402 | } 403 | 404 | const Symbol* 405 | FPExprCodeGenerator::findSymbol(const SymbolKind kind, 406 | const z3::expr& expr) const noexcept 407 | { 408 | auto result = m_expr_sym_map.find( 409 | expr.hash() + static_cast(kind)); 410 | if (result != m_expr_sym_map.cend()) { 411 | return &(*result).second; 412 | } 413 | return nullptr; 414 | } 415 | 416 | std::string 417 | FPExprCodeGenerator::getFuncNameFrom(const std::string& file_path) 418 | { 419 | size_t start_idx = file_path.rfind('/'); 420 | size_t end_idx = file_path.rfind('.'); 421 | if (start_idx != std::string::npos) { 422 | auto res = file_path.substr(start_idx + 1, end_idx - start_idx - 1); 423 | std::replace(res.begin(), res.end(), '.', '_'); 424 | return res; 425 | } 426 | return (""); 427 | } 428 | 429 | std::string 430 | FPExprCodeGenerator::genFuncSignature(const std::string& func_name) 431 | { 432 | std::string res; 433 | res = "double " + func_name + "(unsigned n, const double * " 434 | + CodeGenStr::kFunInput 435 | + ", double * grad, void * data)"; 436 | return res; 437 | } 438 | } 439 | -------------------------------------------------------------------------------- /src/CodeGen/FPExprCodeGenerator.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include "CodeGen.h" 13 | #include 14 | #include 15 | 16 | namespace gosat { 17 | 18 | /** 19 | * /brief C++ code generator based on GO transformation 20 | */ 21 | class FPExprCodeGenerator { 22 | public: 23 | FPExprCodeGenerator(); 24 | 25 | virtual ~FPExprCodeGenerator() = default; 26 | 27 | FPExprCodeGenerator(const FPExprCodeGenerator&) = default; 28 | 29 | FPExprCodeGenerator& operator=(const FPExprCodeGenerator&) = default; 30 | 31 | FPExprCodeGenerator& operator=(FPExprCodeGenerator&&) = default; 32 | 33 | bool hasRNERoundingMode(const z3::expr& expr) const noexcept; 34 | 35 | void 36 | genFuncCode(const std::string& func_name, const z3::expr& expr) noexcept; 37 | 38 | const std::string& getFuncCode() const noexcept; 39 | 40 | unsigned long getVarCount() const noexcept; 41 | 42 | static std::string getFuncNameFrom(const std::string& file_path); 43 | 44 | static std::string genFuncSignature(const std::string& func_name); 45 | 46 | private: 47 | const Symbol* 48 | genFuncCodeRecursive(const z3::expr& expr, bool is_negated) noexcept; 49 | 50 | void genExprCode(const Symbol* expr_sym, 51 | std::vector& args_syms) noexcept; 52 | 53 | void includeFuncHeader(const std::string& func_name) noexcept; 54 | 55 | void includeFuncFooter() noexcept; 56 | 57 | std::pair 58 | insertSymbol(const SymbolKind kind, const z3::expr expr) noexcept; 59 | 60 | const char* 61 | getSymbolName(const SymbolKind kind, const z3::expr& expr) const noexcept; 62 | 63 | const Symbol* 64 | findSymbol(const SymbolKind kind, const z3::expr& expr) const noexcept; 65 | 66 | const Symbol* genNumeralCode(const z3::expr& expr); 67 | 68 | private: 69 | bool m_has_invalid_fp_const; 70 | bool m_has_unsupported_expr; 71 | std::vector m_var_sym_vec; 72 | std::unordered_map m_expr_sym_map; 73 | std::string m_func_code; 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /src/CodeGen/FPExprLibGenerator.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "FPExprLibGenerator.h" 11 | 12 | namespace gosat { 13 | 14 | static std::string kHeaderFileName = kDumpFileName + ".h"; 15 | static std::string kCFileName = kDumpFileName + ".c"; 16 | static std::string kApiFileName = kDumpFileName + ".api"; 17 | static std::string kDistanceFunc = 18 | "double fp64_dis(const double a, const double b) {\n" 19 | " if (a == b || isnan(a) || isnan(b)) {\n" 20 | " return 0;\n" 21 | " }\n" 22 | " const double scale = pow(2, 54);\n" 23 | " uint64_t a_uint = *(const uint64_t *)(&a);\n" 24 | " uint64_t b_uint = *(const uint64_t *)(&b);\n" 25 | " if ((a_uint & 0x8000000000000000) != (b_uint & 0x8000000000000000)) {\n" 26 | " // signs are not equal return sum\n" 27 | " return ((double)\n" 28 | " ((a_uint & 0x7FFFFFFFFFFFFFFF) + (b_uint & 0x7FFFFFFFFFFFFFFF)))/scale;\n" 29 | " }\n" 30 | " b_uint &= 0x7FFFFFFFFFFFFFFF;\n" 31 | " a_uint &= 0x7FFFFFFFFFFFFFFF;\n" 32 | " if (a_uint < b_uint) {\n" 33 | " return ((double)(b_uint - a_uint))/scale;\n" 34 | " }\n" 35 | " return ((double)(a_uint - b_uint))/scale;\n" 36 | "}\n\n"; 37 | 38 | FPExprLibGenerator::FPExprLibGenerator() : 39 | m_api_gen_mode{LibAPIGenMode::kPlainAPI} 40 | {} 41 | 42 | void 43 | FPExprLibGenerator::init(LibAPIGenMode mode) 44 | { 45 | m_api_gen_mode = mode; 46 | if (!dumpFilesExists()) { 47 | m_h_file.open(kHeaderFileName, std::ios::out | std::ios::trunc); 48 | m_h_file << "/* goSAT: automatically generated file*/\n\n" 49 | << "#pragma once\n\n"; 50 | m_h_file.close(); 51 | 52 | m_c_file.open(kCFileName, std::ios::out | std::ios::trunc); 53 | m_c_file << "/* goSAT: automatically generated file */\n\n" 54 | << "#include \""; 55 | m_c_file << kHeaderFileName << "\"\n" 56 | << "#include \n\n" 57 | << "#include \n\n" 58 | << kDistanceFunc; 59 | m_c_file.close(); 60 | 61 | m_api_file.open(kApiFileName, std::ios::out | std::ios::trunc); 62 | m_api_file.close(); 63 | } 64 | m_h_file.open(kHeaderFileName, std::ios::app); 65 | m_c_file.open(kCFileName, std::ios::app); 66 | m_api_file.open(kApiFileName, std::ios::app); 67 | } 68 | 69 | void 70 | FPExprLibGenerator::appendFunction 71 | (size_t args_count, 72 | const std::string& func_name, 73 | const std::string& func_sig, 74 | const std::string& func_def) 75 | { 76 | 77 | m_h_file << func_sig << ";\n\n"; 78 | 79 | m_c_file << func_def << "\n\n"; 80 | 81 | if (m_api_gen_mode == LibAPIGenMode::kPlainAPI) { 82 | m_api_file << func_name << "," 83 | << std::to_string(args_count) << "\n"; 84 | } else { 85 | m_api_file << "{\""; 86 | m_api_file << func_name << "\", {" 87 | << func_name << ", " 88 | << std::to_string(args_count) << "}}, \n"; 89 | } 90 | } 91 | 92 | bool 93 | FPExprLibGenerator::dumpFilesExists() 94 | { 95 | std::ifstream h_file(kHeaderFileName.c_str()); 96 | if (!h_file.good()) 97 | return false; 98 | std::ifstream c_file(kCFileName.c_str()); 99 | if (!c_file.good()) 100 | return false; 101 | std::ifstream api_file(kApiFileName.c_str()); 102 | return api_file.good(); 103 | } 104 | 105 | FPExprLibGenerator::~FPExprLibGenerator() 106 | { 107 | m_h_file.close(); 108 | m_c_file.close(); 109 | m_api_file.close(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/CodeGen/FPExprLibGenerator.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | namespace gosat { 16 | static std::string kDumpFileName = "gofuncs"; 17 | 18 | enum LibAPIGenMode { 19 | kUnsetAPI = 0, 20 | kPlainAPI, 21 | kCppAPI 22 | }; 23 | 24 | class FPExprLibGenerator { 25 | public: 26 | FPExprLibGenerator(); 27 | 28 | virtual ~FPExprLibGenerator(); 29 | 30 | FPExprLibGenerator(const FPExprLibGenerator&) = default; 31 | 32 | FPExprLibGenerator& operator=(const FPExprLibGenerator&) = default; 33 | 34 | FPExprLibGenerator& operator=(FPExprLibGenerator&&) = default; 35 | 36 | void init(LibAPIGenMode mode = LibAPIGenMode::kPlainAPI); 37 | 38 | void appendFunction 39 | (size_t args_count, 40 | const std::string& func_name, 41 | const std::string& func_sig, 42 | const std::string& func_def); 43 | 44 | bool dumpFilesExists(); 45 | 46 | private: 47 | LibAPIGenMode m_api_gen_mode; 48 | std::ofstream m_h_file; 49 | std::ofstream m_c_file; 50 | std::ofstream m_api_file; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /src/ExprAnalyzer/FPExprAnalyzer.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "FPExprAnalyzer.h" 11 | #include "Utils/FPAUtils.h" 12 | 13 | namespace gosat { 14 | 15 | FPExprAnalyzer::FPExprAnalyzer() : 16 | m_float_var_count{0}, 17 | m_double_var_count{0}, 18 | m_const_count{0}, 19 | m_is_linear{true}, 20 | m_has_double_const{false}, 21 | m_has_float_const{false}, 22 | m_has_non_fp_const{false}, 23 | m_has_non_rne_round_mode{false}, 24 | m_has_unsupported_expr{false} 25 | { 26 | } 27 | 28 | bool FPExprAnalyzer::hasRNERoundingMode(const z3::expr& exp) const noexcept 29 | { 30 | return exp.arg(0).decl().decl_kind() == Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; 31 | } 32 | 33 | void FPExprAnalyzer::analyze(const z3::expr& expr) noexcept 34 | { 35 | //std::cout << expr << "\n"; 36 | // FP expression can be 37 | // - Unary (uninterpreted) function 38 | // - Binary (uninterpreted) function 39 | // - Binary function with rounding mode 40 | if (!expr.is_app()) { 41 | // is_app <==> Z3_NUMERAL_AST || Z3_APP_AST 42 | m_has_unsupported_expr = true; 43 | return; 44 | } 45 | if (expr.is_numeral()) { 46 | if (expr.get_sort().sort_kind() == Z3_FLOATING_POINT_SORT) { 47 | unsigned signd = Z3_fpa_get_sbits(expr.ctx(), expr.get_sort()); 48 | unsigned expo = Z3_fpa_get_ebits(expr.ctx(), expr.get_sort()); 49 | if (fpa_util::isFloat32(expo, signd)) { 50 | if (m_fp32_const_sym_map.find(expr.hash()) 51 | == m_fp32_const_sym_map.cend()) { 52 | float result = fpa_util::toFloat32(expr); 53 | m_fp32_const_sym_map[expr.hash()] = result; 54 | m_const_count++; 55 | } 56 | return; 57 | } 58 | if (fpa_util::isFloat64(expo, signd)) { 59 | if (m_fp64_const_sym_map.find(expr.hash()) 60 | == m_fp64_const_sym_map.cend()) { 61 | double result = fpa_util::toFloat64(expr); 62 | m_fp64_const_sym_map[expr.hash()] = result; 63 | m_const_count++; 64 | } 65 | } else 66 | m_has_non_fp_const = true; 67 | return; 68 | } 69 | if (expr.get_sort().sort_kind() == Z3_ROUNDING_MODE_SORT) { 70 | if (expr.decl().decl_kind() != Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN) { 71 | m_has_non_rne_round_mode = true; 72 | } 73 | return; 74 | } 75 | m_has_non_fp_const = true; 76 | return; 77 | } 78 | if (fpa_util::isFPVar(expr)) { 79 | if (m_var_sym_map.find(expr.hash()) == m_var_sym_map.cend()) { 80 | m_var_sym_map[expr.hash()] = expr.decl().name().str(); 81 | if (fpa_util::isFloat32VarDecl(expr)) 82 | m_float_var_count++; 83 | else 84 | m_double_var_count++; 85 | } 86 | return; 87 | } 88 | if (fpa_util::isNonLinearFPExpr(expr)) { 89 | m_is_linear = false; 90 | } 91 | for (uint i = 0; i < expr.num_args(); ++i) { 92 | analyze(expr.arg(i)); 93 | } 94 | } 95 | 96 | void FPExprAnalyzer::prettyPrintSummary( 97 | const std::string& formula_name) const noexcept 98 | { 99 | std::cout << "Formula: " << formula_name 100 | << "\nIs linear (" 101 | << std::string((m_is_linear) ? "yes" : "no") 102 | << ")\nHas float variables (" << m_float_var_count 103 | << ")\nHas double variables (" << m_double_var_count 104 | << ")\nHas const values (" << m_const_count 105 | << ")\nHas unsupported expr (" 106 | << std::string((m_has_unsupported_expr) ? "yes" : "no") 107 | << ")\n"; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/ExprAnalyzer/FPExprAnalyzer.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.TXT for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include "z3++.h" 13 | #include 14 | 15 | namespace gosat { 16 | 17 | /** 18 | * /brief An analyzer to get relevant properties of FP expressions 19 | */ 20 | class FPExprAnalyzer { 21 | public: 22 | FPExprAnalyzer(); 23 | 24 | virtual ~FPExprAnalyzer() = default; 25 | 26 | FPExprAnalyzer(const FPExprAnalyzer&) = default; 27 | 28 | FPExprAnalyzer& operator=(const FPExprAnalyzer&) = default; 29 | 30 | FPExprAnalyzer& operator=(FPExprAnalyzer&&) = default; 31 | 32 | bool hasRNERoundingMode(const z3::expr& exp) const noexcept; 33 | 34 | void analyze(const z3::expr& expr) noexcept; 35 | 36 | void prettyPrintSummary(const std::string& formula_name) const noexcept; 37 | 38 | public: 39 | uint m_float_var_count; 40 | uint m_double_var_count; 41 | uint m_const_count; 42 | bool m_is_linear; 43 | bool m_has_double_const; 44 | bool m_has_float_const; 45 | bool m_has_non_fp_const; 46 | bool m_has_non_rne_round_mode; 47 | bool m_has_unsupported_expr; 48 | std::unordered_map m_var_sym_map; 49 | std::unordered_map m_fp32_const_sym_map; 50 | std::unordered_map m_fp64_const_sym_map; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /src/IRGen/FPIRGenerator.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "FPIRGenerator.h" 11 | #include "Utils/FPAUtils.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | namespace gosat { 24 | 25 | FPIRGenerator::FPIRGenerator 26 | (llvm::LLVMContext* context, llvm::Module* module) : 27 | m_has_invalid_fp_const(false), 28 | m_found_unsupported_smt_expr(false), 29 | m_gofunc(nullptr), 30 | m_ctx(context), 31 | m_mod(module) 32 | {} 33 | 34 | const IRSymbol* FPIRGenerator::genNumeralIR 35 | (llvm::IRBuilder<>& builder, const z3::expr& expr) noexcept 36 | { 37 | using namespace llvm; 38 | if (expr.get_sort().sort_kind() == Z3_FLOATING_POINT_SORT) { 39 | unsigned sigd = Z3_fpa_get_sbits(expr.ctx(), expr.get_sort()); 40 | unsigned expo = Z3_fpa_get_ebits(expr.ctx(), expr.get_sort()); 41 | if (fpa_util::isFloat32(expo, sigd)) { 42 | auto result_iter = findSymbol(SymbolKind::kFP32Const, &expr); 43 | if (result_iter != m_expr_sym_map.cend()) { 44 | return &(*result_iter).second; 45 | } 46 | // TODO: handling FP32 should be configurable 47 | float numeral = fpa_util::toFloat32(expr); 48 | Value* value = ConstantFP::get(builder.getDoubleTy(), numeral); 49 | auto res_pair = insertSymbol(SymbolKind::kFP32Const, expr, value, 0); 50 | return res_pair.first; 51 | } else { 52 | if (!fpa_util::isFloat64(expo, sigd)) { 53 | std::cerr << "unsupported\n"; 54 | std::exit(5); 55 | } 56 | auto result_iter = findSymbol(SymbolKind::kFP64Const, &expr); 57 | if (result_iter != m_expr_sym_map.cend()) { 58 | return &(*result_iter).second; 59 | } 60 | double numeral = fpa_util::toFloat64(expr); 61 | Value* value = ConstantFP::get(builder.getDoubleTy(), numeral); 62 | auto res_pair = insertSymbol(SymbolKind::kFP64Const, expr, value, 0); 63 | return res_pair.first; 64 | } 65 | } 66 | if (expr.decl().decl_kind() == Z3_OP_BNUM) { 67 | auto result_iter = findSymbol(SymbolKind::kFP64Const, &expr); 68 | if (result_iter != m_expr_sym_map.cend()) { 69 | return &(*result_iter).second; 70 | } 71 | std::string numeral_str = Z3_ast_to_string(expr.ctx(), 72 | static_cast(expr)); 73 | numeral_str.replace(0, 1, 1, '0'); 74 | Value* value = ConstantFP::get(builder.getDoubleTy(), 75 | std::stod(numeral_str)); 76 | auto res_pair = insertSymbol(SymbolKind::kFP64Const, expr, value, 0); 77 | return res_pair.first; 78 | } 79 | return nullptr; 80 | } 81 | 82 | llvm::Function* FPIRGenerator::getDistanceFunction() const noexcept 83 | { 84 | return m_func_fp64_dis; 85 | } 86 | 87 | llvm::Function* FPIRGenerator::genFunction 88 | (const z3::expr& expr) noexcept 89 | { 90 | using namespace llvm; 91 | if (m_gofunc != nullptr) { 92 | return m_gofunc; 93 | } 94 | m_gofunc = cast( 95 | m_mod->getOrInsertFunction(StringRef(CodeGenStr::kFunName), 96 | Type::getDoubleTy(*m_ctx), 97 | Type::getInt32Ty(*m_ctx), 98 | Type::getDoublePtrTy(*m_ctx), 99 | Type::getDoublePtrTy((*m_ctx)), 100 | Type::getInt8PtrTy(*m_ctx), 101 | nullptr)); 102 | Function::arg_iterator cur_arg = m_gofunc->arg_begin(); 103 | (*cur_arg).setName("n"); 104 | cur_arg++; 105 | (*cur_arg).setName("x"); 106 | (*cur_arg).addAttr(Attribute::NoCapture); 107 | (*cur_arg).addAttr(Attribute::ReadOnly); 108 | cur_arg++; 109 | (*cur_arg).setName("grad"); 110 | (*cur_arg).addAttr(Attribute::NoCapture); 111 | (*cur_arg).addAttr(Attribute::ReadNone); 112 | cur_arg++; 113 | (*cur_arg).setName("data"); 114 | (*cur_arg).addAttr(Attribute::NoCapture); 115 | (*cur_arg).addAttr(Attribute::ReadNone); 116 | 117 | BasicBlock* BB = BasicBlock::Create(*m_ctx, "EntryBlock", m_gofunc); 118 | IRBuilder<> builder(BB); 119 | 120 | // TBAA Metadata 121 | MDBuilder md_builder(*m_ctx); 122 | auto md_scalar = md_builder.createConstant(builder.getInt64(0)); 123 | auto md_node_1 = MDNode::get(*m_ctx, 124 | MDString::get(*m_ctx, "Simple C/C++ TBAA")); 125 | auto md_node_2 = MDNode::get(*m_ctx, 126 | {MDString::get(*m_ctx, "omnipotent char"), 127 | md_node_1, md_scalar}); 128 | auto md_node_3 = MDNode::get(*m_ctx, 129 | {MDString::get(*m_ctx, "double"), md_node_2, 130 | md_scalar}); 131 | m_tbaa_node = MDNode::get(*m_ctx, {md_node_3, md_node_3, md_scalar}); 132 | 133 | // Initialize external functions to be linked to JIT 134 | m_func_fp64_dis = cast( 135 | m_mod->getOrInsertFunction(StringRef(CodeGenStr::kFunDis), 136 | Type::getDoubleTy(*m_ctx), 137 | Type::getDoubleTy(*m_ctx), 138 | Type::getDoubleTy((*m_ctx)), 139 | nullptr)); 140 | m_func_fp64_eq_dis = cast( 141 | m_mod->getOrInsertFunction(StringRef(CodeGenStr::kFunEqDis), 142 | Type::getDoubleTy(*m_ctx), 143 | Type::getDoubleTy(*m_ctx), 144 | Type::getDoubleTy((*m_ctx)), 145 | nullptr)); 146 | m_func_fp64_neq_dis = cast( 147 | m_mod->getOrInsertFunction(StringRef(CodeGenStr::kFunNEqDis), 148 | Type::getDoubleTy(*m_ctx), 149 | Type::getDoubleTy(*m_ctx), 150 | Type::getDoubleTy((*m_ctx)), 151 | nullptr)); 152 | m_func_isnan = cast( 153 | m_mod->getOrInsertFunction(StringRef(CodeGenStr::kFunIsNan), 154 | Type::getDoubleTy(*m_ctx), 155 | Type::getDoubleTy(*m_ctx), 156 | Type::getDoubleTy((*m_ctx)), 157 | nullptr)); 158 | 159 | m_func_fp64_dis->setLinkage(Function::ExternalLinkage); 160 | m_func_fp64_eq_dis->setLinkage(Function::ExternalLinkage); 161 | m_func_fp64_neq_dis->setLinkage(Function::ExternalLinkage); 162 | m_func_isnan->setLinkage(Function::ExternalLinkage); 163 | m_const_zero = ConstantFP::get(builder.getDoubleTy(), 0.0); 164 | m_const_one = ConstantFP::get(builder.getDoubleTy(), 1.0); 165 | auto return_val_sym = genFuncRecursive(builder, expr, false); 166 | builder.CreateRet(return_val_sym->getValue()); 167 | return m_gofunc; 168 | } 169 | 170 | const IRSymbol* FPIRGenerator::genFuncRecursive 171 | (llvm::IRBuilder<>& builder, const z3::expr expr, 172 | bool is_negated) noexcept 173 | { 174 | if (!expr.is_app()) { 175 | // is_app <==> Z3_NUMERAL_AST || Z3_APP_AST 176 | std::cerr << "unsupported\n"; 177 | std::exit(4); 178 | } 179 | if (fpa_util::isRoundingModeApp(expr) && 180 | expr.decl().decl_kind() != Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN) { 181 | m_found_unsupported_smt_expr = true; 182 | } 183 | if (expr.is_numeral()) { 184 | return genNumeralIR(builder, expr); 185 | } 186 | if (fpa_util::isFPVar(expr)) { 187 | // TODO: handle FP16 and FP128 variables 188 | SymbolKind kind; 189 | if (fpa_util::isFloat32VarDecl(expr)) { 190 | kind = SymbolKind::kFP32Var; 191 | } else if (fpa_util::isFloat64VarDecl(expr)) { 192 | kind = SymbolKind::kFP64Var; 193 | } else { 194 | // XXX: instead of failing directly we give it a try. 195 | // The result might still be useful 196 | m_found_unsupported_smt_expr = true; 197 | } 198 | auto result_iter = findSymbol(kind, &expr); 199 | if (result_iter != m_expr_sym_map.cend()) { 200 | return &(*result_iter).second; 201 | } 202 | using namespace llvm; 203 | Argument* arg2 = &(*(++m_gofunc->arg_begin())); 204 | auto idx_ptr = builder.CreateInBoundsGEP 205 | (llvm::cast(arg2), 206 | builder.getInt64(getVarCount())); 207 | auto loaded_val = builder.CreateAlignedLoad(idx_ptr, 8); 208 | loaded_val->setMetadata(llvm::LLVMContext::MD_tbaa, m_tbaa_node); 209 | auto result_pair = 210 | insertSymbol(kind, expr, loaded_val, getVarCount()); 211 | m_var_sym_vec.emplace_back(result_pair.first); 212 | return result_pair.first; 213 | } 214 | if (!fpa_util::isBoolExpr(expr)) { 215 | is_negated = false; 216 | } else if (expr.decl().decl_kind() == Z3_OP_NOT) { 217 | is_negated = !is_negated; 218 | } 219 | SymbolKind kind = (is_negated) ? SymbolKind::kNegatedExpr : SymbolKind::kExpr; 220 | if (is_negated && 221 | expr.decl().decl_kind() != Z3_OP_NOT && 222 | expr.decl().decl_kind() != Z3_OP_AND && 223 | expr.decl().decl_kind() != Z3_OP_OR) { 224 | // propagate negation according to de-morgan's 225 | is_negated = false; 226 | } 227 | auto result_iter = findSymbol(kind, &expr); 228 | if (result_iter != m_expr_sym_map.cend()) { 229 | return &(*result_iter).second; 230 | } 231 | // Expr not visited before 232 | std::vector arg_syms; 233 | arg_syms.reserve(expr.num_args()); 234 | for (uint i = 0; i < expr.num_args(); ++i) { 235 | arg_syms.push_back(genFuncRecursive(builder, expr.arg(i), is_negated)); 236 | } 237 | auto res_pair = insertSymbol(kind, expr, nullptr); 238 | res_pair.first->setValue(genExprIR(builder, res_pair.first, arg_syms)); 239 | if (expr.decl().decl_kind() == Z3_OP_FPA_TO_FP && 240 | fpa_util::isFPVar(expr.arg(1))) { 241 | m_var_sym_fpa_vec.emplace_back( 242 | std::make_pair(res_pair.first, arg_syms[1])); 243 | } 244 | return res_pair.first; 245 | } 246 | 247 | llvm::Value* FPIRGenerator::genExprIR 248 | (llvm::IRBuilder<>& builder, const IRSymbol* expr_sym, 249 | std::vector& arg_syms) noexcept 250 | { 251 | using namespace llvm; 252 | switch (expr_sym->expr()->decl().decl_kind()) { 253 | // Boolean operations 254 | case Z3_OP_TRUE: 255 | if (expr_sym->isNegated()) 256 | return m_const_one; 257 | else 258 | return m_const_zero; 259 | case Z3_OP_FALSE: 260 | if (expr_sym->isNegated()) 261 | return m_const_zero; 262 | else 263 | return m_const_one; 264 | case Z3_OP_EQ: 265 | case Z3_OP_FPA_EQ: 266 | return genEqualityIR(builder, expr_sym, arg_syms); 267 | case Z3_OP_NOT: 268 | // Do nothing, negation is handled with de-morgans 269 | return arg_syms[0]->getValue(); 270 | case Z3_OP_AND: 271 | if (expr_sym->isNegated()) 272 | return genMultiArgMulIR(builder, arg_syms); 273 | else 274 | return genMultiArgAddIR(builder, arg_syms); 275 | case Z3_OP_OR: 276 | if (expr_sym->isNegated()) 277 | return genMultiArgAddIR(builder, arg_syms); 278 | else 279 | return genMultiArgMulIR(builder, arg_syms); 280 | // Floating point operations 281 | case Z3_OP_FPA_PLUS_INF: 282 | return ConstantFP::get(builder.getDoubleTy(), INFINITY); 283 | case Z3_OP_FPA_MINUS_INF: 284 | return ConstantFP::get(builder.getDoubleTy(), -INFINITY); 285 | case Z3_OP_FPA_NAN: 286 | return ConstantFP::get(builder.getDoubleTy(), NAN); 287 | case Z3_OP_FPA_PLUS_ZERO: 288 | return ConstantFP::get(builder.getDoubleTy(), 0.0); 289 | case Z3_OP_FPA_MINUS_ZERO: 290 | return ConstantFP::get(builder.getDoubleTy(), -0.0); 291 | case Z3_OP_FPA_ADD: 292 | return builder.CreateFAdd(arg_syms[1]->getValue(), 293 | arg_syms[2]->getValue()); 294 | case Z3_OP_FPA_SUB: 295 | return builder.CreateFSub(arg_syms[1]->getValue(), 296 | arg_syms[2]->getValue()); 297 | case Z3_OP_FPA_NEG: 298 | return builder.CreateFSub( 299 | ConstantFP::get(builder.getDoubleTy(), -0.0), 300 | arg_syms[0]->getValue()); 301 | case Z3_OP_FPA_MUL: 302 | return builder.CreateFMul(arg_syms[1]->getValue(), 303 | arg_syms[2]->getValue()); 304 | case Z3_OP_FPA_DIV: 305 | return builder.CreateFDiv(arg_syms[1]->getValue(), 306 | arg_syms[2]->getValue()); 307 | case Z3_OP_FPA_REM: 308 | return builder.CreateFRem(arg_syms[1]->getValue(), 309 | arg_syms[2]->getValue()); 310 | case Z3_OP_FPA_ABS: { 311 | auto fabs_func = Intrinsic::getDeclaration(m_mod, Intrinsic::fabs); 312 | return builder.CreateCall(fabs_func, arg_syms[0]->getValue()); 313 | } 314 | case Z3_OP_FPA_LT: 315 | if (expr_sym->isNegated()) { 316 | auto comp_res = builder.CreateFCmpOGE(arg_syms[0]->getValue(), 317 | arg_syms[1]->getValue()); 318 | return genBinArgCmpIR(builder, arg_syms, comp_res); 319 | } else { 320 | auto comp_res = builder.CreateFCmpOLT(arg_syms[0]->getValue(), 321 | arg_syms[1]->getValue()); 322 | return genBinArgCmpIR2(builder, arg_syms, comp_res); 323 | } 324 | case Z3_OP_FPA_GT: 325 | if (expr_sym->isNegated()) { 326 | auto comp_res = builder.CreateFCmpOLE(arg_syms[0]->getValue(), 327 | arg_syms[1]->getValue()); 328 | return genBinArgCmpIR(builder, arg_syms, comp_res); 329 | } else { 330 | auto comp_res = builder.CreateFCmpOGT(arg_syms[0]->getValue(), 331 | arg_syms[1]->getValue()); 332 | return genBinArgCmpIR2(builder, arg_syms, comp_res); 333 | } 334 | case Z3_OP_FPA_LE: 335 | if (expr_sym->isNegated()) { 336 | auto comp_res = builder.CreateFCmpOGT(arg_syms[0]->getValue(), 337 | arg_syms[1]->getValue()); 338 | return genBinArgCmpIR2(builder, arg_syms, comp_res); 339 | } else { 340 | auto comp_res = builder.CreateFCmpOLE(arg_syms[0]->getValue(), 341 | arg_syms[1]->getValue()); 342 | return genBinArgCmpIR(builder, arg_syms, comp_res); 343 | } 344 | case Z3_OP_FPA_GE: 345 | if (expr_sym->isNegated()) { 346 | auto comp_res = builder.CreateFCmpOLT(arg_syms[0]->getValue(), 347 | arg_syms[1]->getValue()); 348 | return genBinArgCmpIR2(builder, arg_syms, comp_res); 349 | } else { 350 | auto comp_res = builder.CreateFCmpOGE(arg_syms[0]->getValue(), 351 | arg_syms[1]->getValue()); 352 | return genBinArgCmpIR(builder, arg_syms, comp_res); 353 | } 354 | case Z3_OP_FPA_TO_FP: 355 | return (arg_syms[arg_syms.size() - 1])->getValue(); 356 | case Z3_OP_FPA_IS_NAN: 357 | if (expr_sym->isNegated()) { 358 | auto call_res = builder.CreateCall(m_func_isnan, {arg_syms[0]->getValue(), m_const_one}); 359 | call_res->setTailCall(false); 360 | return call_res; 361 | } else { 362 | auto call_res = builder.CreateCall(m_func_isnan, {arg_syms[0]->getValue(), m_const_zero}); 363 | call_res->setTailCall(false); 364 | return call_res; 365 | } 366 | default: 367 | std::cerr << "unsupported: " + 368 | expr_sym->expr()->decl().name().str() + "\n"; 369 | std::exit(3); 370 | } 371 | } 372 | 373 | llvm::Value* FPIRGenerator::genBinArgCmpIR 374 | (llvm::IRBuilder<>& builder, std::vector& arg_syms, 375 | llvm::Value* comp_result) noexcept 376 | { 377 | using namespace llvm; 378 | BasicBlock* bb_first = BasicBlock::Create(*m_ctx, "", m_gofunc); 379 | BasicBlock* bb_second = BasicBlock::Create(*m_ctx, "", m_gofunc); 380 | BasicBlock* bb_cur = builder.GetInsertBlock(); 381 | builder.CreateCondBr(comp_result, bb_second, bb_first); 382 | builder.SetInsertPoint(bb_first); 383 | auto call_res = builder.CreateCall(m_func_fp64_dis, {arg_syms[0]->getValue(), 384 | arg_syms[1]->getValue()}); 385 | call_res->setTailCall(false); 386 | builder.CreateBr(bb_second); 387 | builder.SetInsertPoint(bb_second); 388 | auto phi_inst = builder.CreatePHI(builder.getDoubleTy(), 2); 389 | phi_inst->addIncoming(call_res, bb_first); 390 | phi_inst->addIncoming(m_const_zero, bb_cur); 391 | return phi_inst; 392 | } 393 | 394 | llvm::Value* FPIRGenerator::genBinArgCmpIR2 395 | (llvm::IRBuilder<>& builder, std::vector& arg_syms, 396 | llvm::Value* comp_result) noexcept 397 | { 398 | using namespace llvm; 399 | BasicBlock* bb_first = BasicBlock::Create(*m_ctx, "", m_gofunc); 400 | BasicBlock* bb_second = BasicBlock::Create(*m_ctx, "", m_gofunc); 401 | BasicBlock* bb_cur = builder.GetInsertBlock(); 402 | builder.CreateCondBr(comp_result, bb_second, bb_first); 403 | builder.SetInsertPoint(bb_first); 404 | auto call_res = builder.CreateCall(m_func_fp64_dis, {arg_syms[0]->getValue(), 405 | arg_syms[1]->getValue()}); 406 | call_res->setTailCall(false); 407 | auto dis_res = builder.CreateFAdd(call_res, m_const_one); 408 | builder.CreateBr(bb_second); 409 | builder.SetInsertPoint(bb_second); 410 | auto phi_inst = builder.CreatePHI(builder.getDoubleTy(), 2); 411 | phi_inst->addIncoming(dis_res, bb_first); 412 | phi_inst->addIncoming(m_const_zero, bb_cur); 413 | return phi_inst; 414 | } 415 | 416 | llvm::Value* FPIRGenerator::genMultiArgAddIR 417 | (llvm::IRBuilder<>& builder, 418 | std::vector& arg_syms) noexcept 419 | { 420 | auto result = builder.CreateFAdd(arg_syms[0]->getValue(), 421 | arg_syms[1]->getValue()); 422 | for (unsigned i = 2; i < arg_syms.size(); ++i) { 423 | result = builder.CreateFAdd(result, arg_syms[i]->getValue()); 424 | } 425 | return result; 426 | } 427 | 428 | llvm::Value* FPIRGenerator::genMultiArgMulIR 429 | (llvm::IRBuilder<>& builder, 430 | std::vector& arg_syms) noexcept 431 | { 432 | auto result = builder.CreateFMul(arg_syms[0]->getValue(), 433 | arg_syms[1]->getValue()); 434 | for (unsigned i = 2; i < arg_syms.size(); ++i) { 435 | result = builder.CreateFMul(result, arg_syms[i]->getValue()); 436 | } 437 | return result; 438 | } 439 | 440 | llvm::Value *FPIRGenerator::genEqualityIR 441 | (llvm::IRBuilder<> &builder, const IRSymbol *expr_sym, 442 | std::vector &arg_syms) noexcept 443 | { 444 | 445 | // workaround were we explicitly check the sort of the arguments, 446 | // because z3 provides Z3_OP_EQ were we expect Z3_OP_FPA_EQ. 447 | // See wintersteiger/eq/eq-has-no-other-solution-10015.smt2 448 | 449 | bool is_fpa_args = (arg_syms[0]->expr()->get_sort().sort_kind() == 450 | Z3_FLOATING_POINT_SORT); 451 | assert(is_fpa_args == (arg_syms[1]->expr()->get_sort().sort_kind() == 452 | Z3_FLOATING_POINT_SORT)); 453 | if (expr_sym->expr()->decl().decl_kind() == Z3_OP_FPA_EQ || is_fpa_args) { 454 | if (expr_sym->isNegated()) { 455 | auto result = builder.CreateFCmpONE(arg_syms[0]->getValue(), 456 | arg_syms[1]->getValue()); 457 | return builder.CreateSelect(result, m_const_zero, m_const_one); 458 | } else { 459 | return builder.CreateCall(m_func_fp64_dis, {arg_syms[0]->getValue(), 460 | arg_syms[1]->getValue()}); 461 | } 462 | } 463 | // assuming Z3_OP_EQ 464 | if (expr_sym->isNegated()) { 465 | return builder.CreateCall(m_func_fp64_neq_dis, 466 | {arg_syms[0]->getValue(), 467 | arg_syms[1]->getValue()}); 468 | } else { 469 | return builder.CreateCall(m_func_fp64_eq_dis, 470 | {arg_syms[0]->getValue(), 471 | arg_syms[1]->getValue()}); 472 | } 473 | } 474 | 475 | unsigned FPIRGenerator::getVarCount() const noexcept 476 | { 477 | return static_cast(m_var_sym_vec.size()); 478 | } 479 | 480 | const std::vector& 481 | FPIRGenerator::getVars() const noexcept 482 | { 483 | return m_var_sym_vec; 484 | } 485 | 486 | const std::vector>& 487 | FPIRGenerator::getVarsFPAWrapped() const noexcept 488 | { 489 | return m_var_sym_fpa_vec; 490 | } 491 | 492 | std::pair FPIRGenerator::insertSymbol 493 | (const SymbolKind kind, const z3::expr expr, llvm::Value* value, 494 | unsigned id) noexcept 495 | { 496 | if (kind != SymbolKind::kNegatedExpr) { 497 | auto result = m_expr_sym_map.insert 498 | ({expr.hash(), IRSymbol(kind, expr, value, id)}); 499 | return {&(*result.first).second, result.second}; 500 | } else { 501 | // XXX: would this weaken hashing? 502 | auto result = m_expr_sym_map.insert 503 | ({expr.hash() + static_cast(kind), 504 | IRSymbol(kind, expr, value, id)}); 505 | return {&(*result.first).second, result.second}; 506 | } 507 | } 508 | 509 | SymMapType::const_iterator FPIRGenerator::findSymbol 510 | (const SymbolKind kind, const z3::expr* expr) const noexcept 511 | { 512 | if (kind != SymbolKind::kNegatedExpr) { 513 | return m_expr_sym_map.find(expr->hash()); 514 | } else { 515 | // XXX: would this weaken hashing? 516 | return m_expr_sym_map.find(expr->hash() + static_cast(kind)); 517 | } 518 | } 519 | 520 | void FPIRGenerator::addGlobalFunctionMappings(llvm::ExecutionEngine *engine) 521 | { 522 | double (*func_ptr_dis)(double, double) = fp64_dis; 523 | double (*func_ptr_eq_dis)(double, double) = fp64_eq_dis; 524 | double (*func_ptr_neq_dis)(double, double) = fp64_neq_dis; 525 | double (*func_ptr_isnan)(double, double) = fp64_isnan; 526 | engine->addGlobalMapping(this->m_func_fp64_dis, (void *)func_ptr_dis); 527 | engine->addGlobalMapping(this->m_func_fp64_eq_dis, (void *)func_ptr_eq_dis); 528 | engine->addGlobalMapping(this->m_func_fp64_neq_dis, (void *)func_ptr_neq_dis); 529 | engine->addGlobalMapping(this->m_func_isnan, (void *)func_ptr_isnan); 530 | } 531 | 532 | bool FPIRGenerator::isFoundUnsupportedSMTExpr() noexcept 533 | { 534 | return m_found_unsupported_smt_expr; 535 | } 536 | } 537 | -------------------------------------------------------------------------------- /src/IRGen/FPIRGenerator.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include "CodeGen/CodeGen.h" 13 | #include "z3++.h" 14 | #include "llvm/IR/Module.h" 15 | #include 16 | #include 17 | #include 18 | 19 | namespace gosat { 20 | 21 | class IRSymbol : public Symbol { 22 | public: 23 | IRSymbol() = delete; 24 | 25 | IRSymbol(SymbolKind kind, const z3::expr expr, llvm::Value* value, 26 | unsigned id = 0) 27 | : Symbol{kind, expr}, m_value{value}, m_id{id} 28 | {} 29 | 30 | IRSymbol(const IRSymbol&) = default; 31 | 32 | IRSymbol& operator=(const IRSymbol&) = default; 33 | 34 | IRSymbol& operator=(IRSymbol&&) = delete; 35 | 36 | virtual ~IRSymbol() = default; 37 | 38 | void setValue(llvm::Value* value) noexcept 39 | { 40 | m_value = value; 41 | } 42 | 43 | llvm::Value* getValue() const noexcept 44 | { 45 | return m_value; 46 | } 47 | 48 | unsigned id() const noexcept 49 | { 50 | return m_id; 51 | } 52 | 53 | private: 54 | llvm::Value* m_value; 55 | unsigned m_id; 56 | }; 57 | 58 | using SymMapType = std::unordered_map; 59 | 60 | class FPIRGenerator { 61 | public: 62 | FPIRGenerator() = delete; 63 | 64 | explicit FPIRGenerator(llvm::LLVMContext* context, llvm::Module* module); 65 | 66 | virtual ~FPIRGenerator() = default; 67 | 68 | FPIRGenerator(const FPIRGenerator&) = default; 69 | 70 | FPIRGenerator& operator=(const FPIRGenerator&) = default; 71 | 72 | FPIRGenerator& operator=(FPIRGenerator&&) = default; 73 | 74 | llvm::Function* genFunction(const z3::expr& expr) noexcept; 75 | 76 | llvm::Function* getDistanceFunction() const noexcept; 77 | 78 | unsigned getVarCount() const noexcept; 79 | 80 | const std::vector& getVars() const noexcept; 81 | 82 | const std::vector>& 83 | getVarsFPAWrapped() const noexcept; 84 | 85 | void addGlobalFunctionMappings(llvm::ExecutionEngine *engine); 86 | 87 | bool isFoundUnsupportedSMTExpr() noexcept; 88 | 89 | private: 90 | const IRSymbol* genFuncRecursive 91 | (llvm::IRBuilder<>& builder, const z3::expr expr, 92 | bool is_negated) noexcept; 93 | 94 | const IRSymbol* 95 | genNumeralIR(llvm::IRBuilder<>& builder, const z3::expr& expr) noexcept; 96 | 97 | llvm::Value* genExprIR 98 | (llvm::IRBuilder<>& builder, const IRSymbol* expr_sym, 99 | std::vector& arg_syms) noexcept; 100 | 101 | llvm::Value* genBinArgCmpIR 102 | (llvm::IRBuilder<>& builder, 103 | std::vector& arg_syms, 104 | llvm::Value* comp_result) noexcept; 105 | 106 | llvm::Value* genBinArgCmpIR2 107 | (llvm::IRBuilder<>& builder, 108 | std::vector& arg_syms, 109 | llvm::Value* comp_result) noexcept; 110 | 111 | llvm::Value* genMultiArgAddIR 112 | (llvm::IRBuilder<>& builder, 113 | std::vector& arg_syms) noexcept; 114 | 115 | llvm::Value* genMultiArgMulIR 116 | (llvm::IRBuilder<>& builder, 117 | std::vector& arg_syms) noexcept; 118 | 119 | llvm::Value *genEqualityIR 120 | (llvm::IRBuilder<> &builder, const IRSymbol *expr_sym, 121 | std::vector &arg_syms) noexcept; 122 | 123 | std::pair insertSymbol 124 | (const SymbolKind kind, const z3::expr expr, llvm::Value* value, 125 | unsigned id = 0) noexcept; 126 | 127 | SymMapType::const_iterator 128 | findSymbol(const SymbolKind kind, const z3::expr* expr) const noexcept; 129 | 130 | private: 131 | bool m_has_invalid_fp_const; 132 | bool m_found_unsupported_smt_expr; 133 | llvm::Function* m_gofunc; 134 | llvm::Function* m_func_fp64_dis; 135 | llvm::Function* m_func_fp64_eq_dis; 136 | llvm::Function* m_func_fp64_neq_dis; 137 | llvm::Function* m_func_isnan; 138 | llvm::Constant* m_const_zero; 139 | llvm::Constant* m_const_one; 140 | llvm::LLVMContext* m_ctx; 141 | llvm::Module* m_mod; 142 | llvm::MDNode* m_tbaa_node; 143 | std::vector m_var_sym_vec; 144 | std::vector> m_var_sym_fpa_vec; 145 | SymMapType m_expr_sym_map; 146 | }; 147 | } 148 | -------------------------------------------------------------------------------- /src/Optimizer/ModelValidator.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "ModelValidator.h" 11 | 12 | namespace gosat { 13 | 14 | ModelValidator::ModelValidator 15 | (const FPIRGenerator* ir_generator) : 16 | m_ir_gen{ir_generator} 17 | {} 18 | 19 | bool 20 | ModelValidator::isValid(z3::expr smt_expr, const std::vector& model) 21 | { 22 | auto var_symbols = m_ir_gen->getVars(); 23 | assert((var_symbols.size() == model.size()) && "Model size mismatch!"); 24 | 25 | // now substituting fpa wrapped variables with consts from model 26 | z3::expr_vector src_1(smt_expr.ctx()), dst_1(smt_expr.ctx()); 27 | for (const auto& symbol_pair:m_ir_gen->getVarsFPAWrapped()) { 28 | src_1.push_back(*symbol_pair.first->expr()); 29 | // XXX: assuming TO_FPA should invert type casting 30 | auto kind = symbol_pair.second->kind() == SymbolKind::kFP32Var 31 | ? SymbolKind::kFP64Var : SymbolKind::kFP32Var; 32 | dst_1.push_back(genFPConst(smt_expr, kind, 33 | symbol_pair.second->id(), model)); 34 | } 35 | z3::expr expr_sub_1 = smt_expr.substitute(src_1, dst_1); 36 | 37 | // now substituting actual variables with consts from model 38 | z3::expr_vector src_2(smt_expr.ctx()), dst_2(smt_expr.ctx()); 39 | for (const auto symbol:var_symbols) { 40 | src_2.push_back(*symbol->expr()); 41 | dst_2.push_back(genFPConst(smt_expr, symbol->kind(), 42 | symbol->id(), model)); 43 | } 44 | z3::expr expr_sub_2 = expr_sub_1.substitute(src_2, dst_2); 45 | z3::solver solver(smt_expr.ctx()); 46 | solver.add(expr_sub_2); 47 | return solver.check() == z3::check_result::sat; 48 | } 49 | 50 | z3::expr 51 | ModelValidator::genFPConst(z3::expr expr, SymbolKind kind, unsigned id, 52 | const std::vector& model) const noexcept 53 | { 54 | if (kind == SymbolKind::kFP32Var) { 55 | auto var_ast = 56 | Z3_mk_fpa_numeral_float(expr.ctx(), 57 | static_cast(model[id]), 58 | Z3_mk_fpa_sort_32(expr.ctx())); 59 | return z3::to_expr(expr.ctx(), var_ast); 60 | } else { 61 | assert(kind == SymbolKind::kFP64Var && "Bad variable kind!"); 62 | //TODO: handle FP16 and FP128 constants 63 | auto var_ast = 64 | Z3_mk_fpa_numeral_double(expr.ctx(), model[id], 65 | Z3_mk_fpa_sort_64(expr.ctx())); 66 | return z3::to_expr(expr.ctx(), var_ast); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Optimizer/ModelValidator.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include "IRGen/FPIRGenerator.h" 13 | #include "z3++.h" 14 | 15 | namespace gosat { 16 | class IRSymbol; 17 | 18 | class FPIRGenerator; 19 | 20 | class ModelValidator { 21 | public: 22 | ModelValidator(const FPIRGenerator* ir_generator); 23 | 24 | virtual ~ModelValidator() = default; 25 | 26 | ModelValidator(const ModelValidator&) = default; 27 | 28 | ModelValidator& operator=(const ModelValidator&) = default; 29 | 30 | ModelValidator& operator=(ModelValidator&&) = default; 31 | 32 | bool isValid(z3::expr smt_expr, const std::vector& model); 33 | 34 | private: 35 | z3::expr genFPConst(z3::expr expr, SymbolKind kind, unsigned id, 36 | const std::vector& model) const noexcept; 37 | 38 | private: 39 | const FPIRGenerator* m_ir_gen; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/Optimizer/NLoptOptimizer.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "NLoptOptimizer.h" 11 | #include 12 | #include 13 | #include 14 | 15 | namespace gosat { 16 | OptConfig::OptConfig() : 17 | MaxEvalCount{500000}, 18 | MaxLocalEvalCount{50000}, 19 | RelTolerance{1e-10}, 20 | Bound{1e9}, 21 | StepSize{0.5}, 22 | InitialPopulation{0} 23 | {} 24 | 25 | OptConfig::OptConfig(nlopt_algorithm global_alg, nlopt_algorithm local_alg) : 26 | MaxEvalCount{500000}, 27 | MaxLocalEvalCount{1000}, 28 | RelTolerance{1e-10}, 29 | Bound{1e9}, 30 | StepSize{0.5}, 31 | InitialPopulation{0} 32 | { 33 | assert(local_alg == NLOPT_LN_BOBYQA && 34 | "Invalid local optimization algorithms!"); 35 | if (global_alg == NLOPT_G_MLSL_LDS || global_alg == NLOPT_G_MLSL) { 36 | MaxEvalCount = 50000; 37 | RelTolerance = 1e-8; 38 | } 39 | } 40 | 41 | NLoptOptimizer::NLoptOptimizer() : 42 | m_global_opt_alg{NLOPT_GN_DIRECT}, 43 | m_local_opt_alg{NLOPT_LN_BOBYQA} 44 | {} 45 | 46 | NLoptOptimizer::NLoptOptimizer(nlopt_algorithm global_alg, 47 | nlopt_algorithm local_alg) : 48 | m_global_opt_alg{global_alg}, 49 | m_local_opt_alg{local_alg}, 50 | Config{global_alg, local_alg} 51 | {} 52 | 53 | int 54 | NLoptOptimizer::optimize 55 | (nlopt_func func, unsigned dim, double* x, double* min) const noexcept 56 | { 57 | if (func(dim, x, nullptr, nullptr) == 0) { 58 | // trivially satisfiable algorithm 59 | *min = 0; 60 | return 0; 61 | } 62 | assert(NLoptOptimizer::isSupportedGlobalOptAlg(m_global_opt_alg) 63 | && "Unsupported global optimization algorithm"); 64 | nlopt_opt opt; 65 | opt = nlopt_create(m_global_opt_alg, dim); 66 | nlopt_set_min_objective(opt, func, NULL); 67 | nlopt_set_upper_bounds1(opt, Config.Bound); 68 | nlopt_set_lower_bounds1(opt, -Config.Bound); 69 | std::vector step_size_arr(dim, Config.StepSize); 70 | nlopt_set_initial_step(opt, step_size_arr.data()); 71 | nlopt_set_stopval(opt, 0); 72 | nlopt_set_xtol_rel(opt, Config.RelTolerance); 73 | nlopt_set_maxeval(opt, Config.MaxEvalCount); 74 | if (NLoptOptimizer::isRequirePopulation(m_global_opt_alg)) { 75 | nlopt_set_population(opt, Config.InitialPopulation); 76 | } 77 | if (!NLoptOptimizer::isRequireLocalOptAlg(m_global_opt_alg)) { 78 | auto status = nlopt_optimize(opt, x, min); 79 | nlopt_destroy(opt); 80 | return status; 81 | } 82 | assert(NLoptOptimizer::isSupportedLocalOptAlg(m_local_opt_alg) 83 | && "Unsupported local optimization algorithm!"); 84 | nlopt_opt local_opt; 85 | local_opt = nlopt_create(m_local_opt_alg, dim); 86 | nlopt_set_min_objective(local_opt, func, NULL); 87 | nlopt_set_initial_step(local_opt, step_size_arr.data()); 88 | nlopt_set_stopval(local_opt, 0); 89 | nlopt_set_maxeval(local_opt, Config.MaxLocalEvalCount); 90 | nlopt_set_local_optimizer(opt, local_opt); 91 | auto status = nlopt_optimize(opt, x, min); 92 | nlopt_destroy(local_opt); 93 | nlopt_destroy(opt); 94 | return status; 95 | } 96 | 97 | bool 98 | NLoptOptimizer::isSupportedLocalOptAlg(nlopt_algorithm local_opt_alg) noexcept 99 | { 100 | return (local_opt_alg == NLOPT_LN_BOBYQA 101 | || local_opt_alg == NLOPT_LN_SBPLX); 102 | } 103 | 104 | bool 105 | NLoptOptimizer::isRequireLocalOptAlg(nlopt_algorithm opt_alg) noexcept 106 | { 107 | return opt_alg == NLOPT_G_MLSL || opt_alg == NLOPT_G_MLSL_LDS; 108 | } 109 | 110 | bool 111 | NLoptOptimizer::isSupportedGlobalOptAlg(nlopt_algorithm opt_alg) noexcept 112 | { 113 | switch (opt_alg) { 114 | case NLOPT_GN_DIRECT: 115 | case NLOPT_GN_DIRECT_L: 116 | case NLOPT_GN_DIRECT_L_RAND: 117 | case NLOPT_GN_ORIG_DIRECT: 118 | case NLOPT_GN_ORIG_DIRECT_L: 119 | case NLOPT_GN_MLSL_LDS: 120 | case NLOPT_G_MLSL: 121 | case NLOPT_G_MLSL_LDS: 122 | case NLOPT_GN_CRS2_LM: 123 | case NLOPT_GN_ISRES: 124 | case NLOPT_GN_ESCH: 125 | return true; 126 | default: 127 | return false; 128 | } 129 | } 130 | 131 | bool 132 | NLoptOptimizer::isRequirePopulation(nlopt_algorithm opt_alg) noexcept 133 | { 134 | switch (opt_alg) { 135 | case NLOPT_GN_MLSL: 136 | case NLOPT_GN_CRS2_LM: 137 | case NLOPT_GN_ISRES: 138 | case NLOPT_GN_ESCH: 139 | return true; 140 | default: 141 | return false; 142 | } 143 | } 144 | 145 | int 146 | NLoptOptimizer::refineResult 147 | (nlopt_func func, unsigned dim, double* x, double* min) 148 | { 149 | nlopt_opt opt; 150 | opt = nlopt_create(NLOPT_LN_BOBYQA, dim); 151 | nlopt_set_min_objective(opt, func, NULL); 152 | nlopt_set_initial_step(opt, &Config.StepSize); 153 | nlopt_set_xtol_rel(opt, Config.RelTolerance); 154 | nlopt_set_maxeval(opt, Config.MaxLocalEvalCount); 155 | auto status = nlopt_optimize(opt, x, min); 156 | nlopt_destroy(opt); 157 | return status; 158 | } 159 | 160 | bool 161 | NLoptOptimizer::existsRoundingError 162 | (nlopt_func func, 163 | unsigned int dim, 164 | const double* x, 165 | const double* min) const noexcept 166 | { 167 | return func(dim, x, nullptr, nullptr) != *min; 168 | } 169 | 170 | void 171 | NLoptOptimizer::fixRoundingErrorNearZero(nlopt_func const func, 172 | unsigned dim, 173 | double* x, 174 | double* min) const noexcept 175 | { 176 | if (*min == 0 || std::fabs(*min) > 1e-6) { 177 | return; 178 | } 179 | for (unsigned int i = 0; i < dim; ++i) { 180 | double int_part; 181 | std::modf(x[i], &int_part); 182 | if (std::fabs(x[i] - int_part) < 1e-6) { 183 | double temp = x[i]; 184 | x[i] = int_part; 185 | const auto min_x = func(dim, x, nullptr, nullptr); 186 | if (*min < min_x || std::fpclassify(min_x) == FP_NAN) { 187 | x[i] = temp; 188 | } 189 | } 190 | } 191 | *min = func(dim, x, nullptr, nullptr); 192 | } 193 | 194 | double NLoptOptimizer::eval 195 | (nlopt_func func, unsigned dim, const double* x) const noexcept 196 | { 197 | return func(dim, x, nullptr, nullptr); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/Optimizer/NLoptOptimizer.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | namespace gosat { 15 | 16 | class OptConfig { 17 | public: 18 | OptConfig(); 19 | 20 | OptConfig(nlopt_algorithm global_alg, nlopt_algorithm local_alg); 21 | 22 | virtual ~OptConfig() = default; 23 | 24 | int MaxEvalCount; 25 | int MaxLocalEvalCount; 26 | double RelTolerance; 27 | double Bound; 28 | double StepSize; 29 | unsigned InitialPopulation; 30 | }; 31 | 32 | class NLoptOptimizer { 33 | public: 34 | NLoptOptimizer(); 35 | 36 | NLoptOptimizer(nlopt_algorithm global_alg, 37 | nlopt_algorithm local_alg = NLOPT_LN_BOBYQA); 38 | 39 | virtual ~NLoptOptimizer() = default; 40 | 41 | int optimize 42 | (nlopt_func func, unsigned dim, double* x, 43 | double* min) const noexcept; 44 | 45 | double eval 46 | (nlopt_func func, unsigned dim, const double* x) const noexcept; 47 | 48 | bool existsRoundingError 49 | (nlopt_func func, 50 | unsigned int dim, 51 | const double* x, 52 | const double* min) const noexcept; 53 | 54 | void fixRoundingErrorNearZero 55 | (nlopt_func const func, 56 | unsigned dim, 57 | double* x, 58 | double* min) const noexcept; 59 | 60 | int refineResult(nlopt_func func, unsigned dim, double* x, double* min); 61 | 62 | static bool isSupportedGlobalOptAlg(nlopt_algorithm opt_alg) noexcept; 63 | 64 | static bool isSupportedLocalOptAlg(nlopt_algorithm local_opt_alg) noexcept; 65 | 66 | static bool isRequireLocalOptAlg(nlopt_algorithm opt_alg) noexcept; 67 | 68 | static bool isRequirePopulation(nlopt_algorithm opt_alg) noexcept; 69 | 70 | private: 71 | const nlopt_algorithm m_global_opt_alg; 72 | const nlopt_algorithm m_local_opt_alg; 73 | public: 74 | OptConfig Config; 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /src/Utils/FPAUtils.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.TXT for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "FPAUtils.h" 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | * /brief Provides a scaled distance value between representations 17 | * of two double variables 18 | */ 19 | double fp64_dis(const double a, const double b) 20 | { 21 | /* 22 | * Helpful. Ordered layout of FP32 23 | * 1 11111111 ----------------------- -nan 24 | * 1 11111111 00000000000000000000000 -oo 25 | * 1 -------- ----------------------- -norm 26 | * 1 00000000 ----------------------- -denorm 27 | * 1 00000000 00000000000000000000000 -o 28 | * 0 11111111 ----------------------- +nan 29 | * 0 11111111 00000000000000000000000 +oo 30 | * 0 -------- ----------------------- +norm 31 | * 0 00000000 ----------------------- +denorm 32 | * 0 00000000 00000000000000000000000 +o 33 | */ 34 | 35 | if (a == b) { 36 | return 0; 37 | } 38 | if ( std::isnan(a) || std::isnan(b)) { 39 | // any non-zero should do 40 | return 1024; 41 | } 42 | const double scale = pow(2, 54); 43 | uint64_t a_uint = *(const uint64_t*) (&a); 44 | uint64_t b_uint = *(const uint64_t*) (&b); 45 | if ((a_uint & 0x8000000000000000) != (b_uint & 0x8000000000000000)) { 46 | // signs are not equal return sum 47 | return ((double) 48 | ((a_uint & 0x7FFFFFFFFFFFFFFF) + 49 | (b_uint & 0x7FFFFFFFFFFFFFFF))) / scale; 50 | } 51 | b_uint &= 0x7FFFFFFFFFFFFFFF; 52 | a_uint &= 0x7FFFFFFFFFFFFFFF; 53 | if (a_uint < b_uint) { 54 | return ((double) (b_uint - a_uint)) / scale; 55 | } 56 | return ((double) (a_uint - b_uint)) / scale; 57 | } 58 | 59 | double fp64_eq_dis(const double a, const double b) 60 | { 61 | if (a == 0 && b == 0) { 62 | return 0; 63 | } 64 | 65 | if (a != 0 && b != 0) { 66 | return 0; 67 | } 68 | 69 | return fp64_dis(a, b); 70 | } 71 | 72 | double fp64_neq_dis(const double a, const double b) 73 | { 74 | if (a == 0 && b != 0) { 75 | return 0; 76 | } 77 | if (a != 0 && b == 0) { 78 | return 0; 79 | } 80 | if (a != b) { 81 | // it is possible that both sides are false, i.e. a != 0 && b != 0, 82 | // yet a == b and thus fp64_dis(a,b) would return 0 which is unsound. 83 | return fp64_dis(a, b); 84 | } 85 | return 1; 86 | } 87 | 88 | double fp64_isnan(const double a, const double flag) 89 | { 90 | if (flag != 0) { 91 | // flag set, invert result 92 | return std::isnan(a)? 1.0: 0.0; 93 | } else { 94 | return std::isnan(a)? 0.0: 1.0; 95 | } 96 | } 97 | 98 | namespace gosat { 99 | namespace fpa_util { 100 | 101 | bool isRoundingModeApp(const z3::expr expr) noexcept 102 | { 103 | if (expr.num_args() != 0) { 104 | return false; 105 | } 106 | if (expr.decl().decl_kind() == Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN || 107 | expr.decl().decl_kind() == Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY || 108 | expr.decl().decl_kind() == Z3_OP_FPA_RM_TOWARD_POSITIVE || 109 | expr.decl().decl_kind() == Z3_OP_FPA_RM_TOWARD_NEGATIVE || 110 | expr.decl().decl_kind() == Z3_OP_FPA_RM_TOWARD_ZERO) { 111 | return true; 112 | } 113 | return false; 114 | } 115 | 116 | bool isBoolExpr(const z3::expr& expr) noexcept 117 | { 118 | switch (expr.decl().decl_kind()) { 119 | case Z3_OP_TRUE: 120 | case Z3_OP_FALSE: 121 | case Z3_OP_EQ: 122 | case Z3_OP_FPA_EQ: 123 | case Z3_OP_NOT: 124 | case Z3_OP_AND: 125 | case Z3_OP_OR: 126 | // Floating point operations 127 | case Z3_OP_FPA_LT: 128 | case Z3_OP_FPA_GT: 129 | case Z3_OP_FPA_LE: 130 | case Z3_OP_FPA_GE: 131 | case Z3_OP_FPA_IS_NAN: 132 | case Z3_OP_FPA_IS_INF: 133 | case Z3_OP_FPA_IS_ZERO: 134 | case Z3_OP_FPA_IS_NORMAL: 135 | case Z3_OP_FPA_IS_SUBNORMAL: 136 | case Z3_OP_FPA_IS_NEGATIVE: 137 | case Z3_OP_FPA_IS_POSITIVE: 138 | return true; 139 | default: 140 | return false; 141 | } 142 | } 143 | 144 | bool 145 | isFloat32VarDecl(const z3::expr& expr) noexcept 146 | { 147 | unsigned sigd = Z3_fpa_get_sbits(expr.ctx(), expr.get_sort()); 148 | unsigned expo = Z3_fpa_get_ebits(expr.ctx(), expr.get_sort()); 149 | return isFloat32(expo, sigd)? true: false; 150 | } 151 | 152 | bool 153 | isFloat64VarDecl(const z3::expr& expr) noexcept 154 | { 155 | unsigned sigd = Z3_fpa_get_sbits(expr.ctx(), expr.get_sort()); 156 | unsigned expo = Z3_fpa_get_ebits(expr.ctx(), expr.get_sort()); 157 | return isFloat64(expo, sigd)? true: false; 158 | } 159 | 160 | bool 161 | isNonLinearFPExpr(const z3::expr& expr) noexcept 162 | { 163 | if (expr.get_sort().sort_kind() != Z3_FLOATING_POINT_SORT) { 164 | return false; 165 | } 166 | switch (expr.decl().decl_kind()) { 167 | case Z3_OP_FPA_MUL: 168 | case Z3_OP_FPA_DIV: 169 | case Z3_OP_FPA_REM: 170 | case Z3_OP_FPA_ABS: 171 | case Z3_OP_FPA_MIN: 172 | case Z3_OP_FPA_MAX: 173 | case Z3_OP_FPA_FMA: 174 | case Z3_OP_FPA_SQRT: 175 | case Z3_OP_FPA_ROUND_TO_INTEGRAL: 176 | return true; 177 | default: 178 | return false; 179 | } 180 | } 181 | 182 | inline unsigned short 183 | getBaseofNumStr(const char* numerical) noexcept 184 | { 185 | if (numerical[1] == 'b') { return 2; } 186 | if (numerical[1] == 'x') { return 16; } 187 | if (numerical[1] == 'o') { return 8; } 188 | return 0; 189 | } 190 | 191 | /** 192 | * 193 | * @param expr 194 | * @return float value of expression 195 | * @pre isFloat32 196 | */ 197 | float 198 | toFloat32(const z3::expr& expr) noexcept 199 | { 200 | switch (expr.decl().decl_kind()) { 201 | case Z3_OP_FPA_PLUS_INF: 202 | return std::numeric_limits::infinity(); 203 | case Z3_OP_FPA_MINUS_INF: 204 | return -(std::numeric_limits::infinity()); 205 | case Z3_OP_FPA_NAN: 206 | return std::numeric_limits::quiet_NaN(); 207 | case Z3_OP_FPA_PLUS_ZERO: 208 | return 0; 209 | case Z3_OP_FPA_MINUS_ZERO: 210 | return -0; 211 | default: 212 | break; 213 | } 214 | // XXX: proper functions are not working as of Z3 v4.5.0 215 | // - Z3_fpa_get_numeral_sign(expr.ctx(), static_cast(expr), &sign); 216 | // - Z3_fpa_get_numeral_exponent_int64(expr.ctx(), static_cast(expr), &exponent); 217 | // - Z3_fpa_get_numeral_significand_uint64(expr.ctx(), static_cast(expr), &significand); 218 | assert(expr.num_args() == 3 && " Invalid FP constant"); 219 | Z3_string bv_str = Z3_ast_to_string(expr.ctx(), 220 | static_cast(expr.arg(0))); 221 | int sign = std::stoi(bv_str + 2, NULL, getBaseofNumStr(bv_str)); 222 | bv_str = Z3_ast_to_string(expr.ctx(), 223 | static_cast(expr.arg(1))); 224 | uint64_t exponent = std::stoul(bv_str + 2, NULL, getBaseofNumStr(bv_str)); 225 | bv_str = Z3_ast_to_string(expr.ctx(), 226 | static_cast(expr.arg(2))); 227 | uint64_t significand = std::stoull(bv_str + 2, NULL, 228 | getBaseofNumStr(bv_str)); 229 | uint32_t result = static_cast(exponent); 230 | result <<= 23; 231 | result |= static_cast(significand); 232 | if (sign) result |= 0x80000000; 233 | return *(reinterpret_cast(&result)); 234 | } 235 | 236 | /** 237 | * 238 | * @param exp 239 | * @return double value of expression 240 | * @pre isFloat64 241 | */ 242 | double 243 | toFloat64(const z3::expr& expr) noexcept 244 | { 245 | switch (expr.decl().decl_kind()) { 246 | case Z3_OP_FPA_PLUS_INF: 247 | return std::numeric_limits::infinity(); 248 | case Z3_OP_FPA_MINUS_INF: 249 | return -(std::numeric_limits::infinity()); 250 | case Z3_OP_FPA_NAN: 251 | return std::numeric_limits::quiet_NaN(); 252 | case Z3_OP_FPA_PLUS_ZERO: 253 | return 0; 254 | case Z3_OP_FPA_MINUS_ZERO: 255 | return -0; 256 | default: 257 | break; 258 | } 259 | assert(expr.num_args() == 3 && " Invalid FP constant"); 260 | Z3_string bv_str = Z3_ast_to_string(expr.ctx(), 261 | static_cast(expr.arg(0))); 262 | int sign = std::stoi(bv_str + 2, NULL, getBaseofNumStr(bv_str)); 263 | bv_str = Z3_ast_to_string(expr.ctx(), 264 | static_cast(expr.arg(1))); 265 | uint64_t exponent = std::stoul(bv_str + 2, NULL, getBaseofNumStr(bv_str)); 266 | bv_str = Z3_ast_to_string(expr.ctx(), 267 | static_cast(expr.arg(2))); 268 | uint64_t significand = std::stoull(bv_str + 2, NULL, 269 | getBaseofNumStr(bv_str)); 270 | // Hidden bit must not be represented see: 271 | // http://smtlib.cs.uiowa.edu/theories-FloatingPoint.shtml 272 | uint64_t result = exponent; 273 | result <<= 52; 274 | result |= significand; 275 | if (sign) result |= 0x8000000000000000; 276 | return *(reinterpret_cast(&result)); 277 | } 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/Utils/FPAUtils.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.TXT for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include "z3++.h" 13 | 14 | // functions called by JIT engine 15 | 16 | double fp64_dis(double a, double b); 17 | double fp64_eq_dis(double a, double b); 18 | double fp64_neq_dis(double a, double b); 19 | double fp64_isnan(double a, double flag); 20 | 21 | // end of functions 22 | 23 | namespace gosat { 24 | namespace fpa_util { 25 | 26 | bool inline 27 | isFloat32(const unsigned exponent, const unsigned significand) noexcept 28 | { 29 | return (exponent == 8 && significand == 24); 30 | } 31 | 32 | bool inline 33 | isFloat64(const unsigned exponent, const unsigned significand) noexcept 34 | { 35 | return (exponent == 11 && significand == 53); 36 | } 37 | 38 | 39 | bool inline isFPVar(const z3::expr& expr) noexcept 40 | { 41 | return (expr.num_args() == 0 42 | && expr.decl().decl_kind() == Z3_OP_UNINTERPRETED 43 | && expr.get_sort().sort_kind() == Z3_FLOATING_POINT_SORT); 44 | } 45 | 46 | bool isNonLinearFPExpr(const z3::expr& expr) noexcept; 47 | 48 | bool isFloat32VarDecl(const z3::expr& expr) noexcept; 49 | 50 | bool isFloat64VarDecl(const z3::expr& expr) noexcept; 51 | 52 | bool isRoundingModeApp(const z3::expr expr) noexcept; 53 | 54 | bool isBoolExpr(const z3::expr& expr) noexcept; 55 | 56 | /** 57 | * 58 | * @param expr 59 | * @return float value of expression 60 | * @pre isFloat32 61 | */ 62 | float toFloat32(const z3::expr& expr) noexcept; 63 | 64 | /** 65 | * 66 | * @param exp 67 | * @return double value of expression 68 | * @pre isFloat64 69 | */ 70 | double toFloat64(const z3::expr& expr) noexcept; 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "llvm/Support/CommandLine.h" 11 | #include "ExprAnalyzer/FPExprAnalyzer.h" 12 | #include "CodeGen/FPExprLibGenerator.h" 13 | #include "CodeGen/FPExprCodeGenerator.h" 14 | #include "IRGen/FPIRGenerator.h" 15 | #include "Utils/FPAUtils.h" 16 | #include "llvm/ExecutionEngine/ExecutionEngine.h" 17 | #include "llvm/ExecutionEngine/MCJIT.h" 18 | #include "llvm/Support/TargetSelect.h" 19 | #include "Optimizer/ModelValidator.h" 20 | #include 21 | #include 22 | #include 23 | 24 | typedef std::numeric_limits dbl; 25 | 26 | enum goSATMode { 27 | kUndefinedMode = 0, 28 | kFormulaAnalysis, 29 | kCCodeGeneration, 30 | kNativeSolving 31 | }; 32 | 33 | enum goSATAlgorithm { 34 | kUndefinedAlg = 0, 35 | kCRS2 = NLOPT_GN_CRS2_LM, 36 | kISRES = NLOPT_GN_ISRES, 37 | kMLSL = NLOPT_G_MLSL, 38 | kDirect = NLOPT_GN_DIRECT_L 39 | }; 40 | 41 | llvm::cl::OptionCategory 42 | SolverCategory("Solver Options", "Options for controlling FPA solver."); 43 | 44 | static llvm::cl::opt 45 | opt_input_file(llvm::cl::Required, 46 | "f", 47 | llvm::cl::desc("path to smt file"), 48 | llvm::cl::value_desc("filename"), 49 | llvm::cl::cat(SolverCategory)); 50 | 51 | static llvm::cl::opt 52 | validate_model(llvm::cl::Optional, 53 | "c", 54 | llvm::cl::desc( 55 | "validates sat model using z3 if applicable"), 56 | llvm::cl::value_desc("filename"), 57 | llvm::cl::cat(SolverCategory)); 58 | 59 | static llvm::cl::opt 60 | opt_tool_mode("mode", llvm::cl::Optional, 61 | llvm::cl::desc("Tool operation mode:"), 62 | llvm::cl::cat(SolverCategory), 63 | llvm::cl::values(clEnumValN(kNativeSolving, 64 | "go", 65 | "SMT solving for FP (default) "), 66 | clEnumValN(kFormulaAnalysis, 67 | "fa", 68 | "formula analysis"), 69 | clEnumValN(kCCodeGeneration, 70 | "cg", 71 | "C code generation"))); 72 | 73 | static llvm::cl::opt 74 | opt_api_dump_mode("fmt", llvm::cl::Optional, 75 | llvm::cl::desc( 76 | "API dump format in code generation mode:"), 77 | llvm::cl::cat(SolverCategory), 78 | llvm::cl::values(clEnumValN(gosat::kPlainAPI, 79 | "plain", 80 | "CSV dump of API (default)"), 81 | clEnumValN(gosat::kCppAPI, 82 | "cpp", 83 | "C++ dump of API"))); 84 | 85 | static llvm::cl::opt 86 | opt_go_algorithm("alg", llvm::cl::Optional, 87 | llvm::cl::desc("Applied GO algorithm:"), 88 | llvm::cl::cat(SolverCategory), 89 | llvm::cl::values(clEnumValN(kDirect, 90 | "direct", 91 | "Direct algorithm"), 92 | clEnumValN(kCRS2, 93 | "crs2", 94 | "CRS2 algorithm (default)"), 95 | clEnumValN(kISRES, 96 | "isres", 97 | "ISRES algorithm"), 98 | clEnumValN(kMLSL, 99 | "mlsl", 100 | "MLSL algorithm"))); 101 | 102 | static llvm::cl::opt smtlib_compliant_output( 103 | "smtlib-output", llvm::cl::cat(SolverCategory), 104 | llvm::cl::desc("Make output SMT-LIBv2 compliant (default false)"), 105 | llvm::cl::init(false)); 106 | 107 | void versionPrinter() 108 | { 109 | std::cout << "goSAT v0.1 \n" 110 | << "Copyright (c) 2017 University of Kaiserslautern\n"; 111 | } 112 | 113 | inline float elapsedTimeFrom(std::chrono::steady_clock::time_point& st_time) 114 | { 115 | const auto res = std::chrono::duration_cast 116 | (std::chrono::steady_clock::now() - st_time).count(); 117 | return static_cast(res) / 1000; 118 | } 119 | 120 | bool isFileExist(const char *fileName) 121 | { 122 | std::ifstream infile(fileName); 123 | return infile.good(); 124 | } 125 | 126 | int main(int argc, const char** argv) 127 | { 128 | llvm::cl::SetVersionPrinter(versionPrinter); 129 | llvm::cl::HideUnrelatedOptions(SolverCategory); 130 | llvm::cl::ParseCommandLineOptions 131 | (argc, argv, 132 | "goSAT v0.1 Copyright (c) 2017 University of Kaiserslautern\n"); 133 | 134 | if (!isFileExist(opt_input_file.c_str())) { 135 | std::cerr << "Input file does not exists!" << std::endl; 136 | std::exit(1); 137 | } 138 | try { 139 | z3::context smt_ctx; 140 | z3::expr smt_expr = smt_ctx.parse_file(opt_input_file.c_str()); 141 | if (opt_tool_mode == kFormulaAnalysis) { 142 | gosat::FPExprAnalyzer analyzer; 143 | analyzer.analyze(smt_expr); 144 | analyzer.prettyPrintSummary( 145 | gosat::FPExprCodeGenerator::getFuncNameFrom( 146 | opt_input_file)); 147 | return 0; 148 | } 149 | if (opt_tool_mode == kCCodeGeneration) { 150 | using namespace gosat; 151 | FPExprCodeGenerator code_generator; 152 | std::string func_name = 153 | FPExprCodeGenerator::getFuncNameFrom(opt_input_file); 154 | code_generator.genFuncCode(func_name, smt_expr); 155 | if (opt_api_dump_mode != kUnsetAPI) { 156 | FPExprLibGenerator lib_generator; 157 | // TODO: implement error handling for output files 158 | lib_generator.init(opt_api_dump_mode); 159 | auto func_sig = 160 | FPExprCodeGenerator::genFuncSignature(func_name); 161 | lib_generator.appendFunction 162 | (code_generator.getVarCount(), 163 | func_name, 164 | func_sig, 165 | code_generator.getFuncCode()); 166 | std::cout << func_name << ". code generated successfully!" 167 | << "\n"; 168 | } else { 169 | std::cout << code_generator.getFuncCode() << "\n"; 170 | } 171 | return 0; 172 | } 173 | std::chrono::steady_clock::time_point 174 | time_start = std::chrono::steady_clock::now(); 175 | using namespace llvm; 176 | std::string func_name = gosat::FPExprCodeGenerator::getFuncNameFrom( 177 | opt_input_file); 178 | 179 | // JIT formula to an objective function 180 | InitializeNativeTarget(); 181 | InitializeNativeTargetAsmPrinter(); 182 | atexit(llvm_shutdown); 183 | atexit(Z3_finalize_memory); 184 | 185 | LLVMContext context; 186 | std::unique_ptr module = std::make_unique( 187 | StringRef(func_name), context); 188 | gosat::FPIRGenerator ir_gen(&context, module.get()); 189 | auto ll_func_ptr = ir_gen.genFunction(smt_expr); 190 | std::string err_str; 191 | std::unique_ptr exec_engine( 192 | EngineBuilder(std::move(module)) 193 | .setEngineKind(EngineKind::JIT) 194 | .setOptLevel(CodeGenOpt::Less) 195 | .setErrorStr(&err_str) 196 | .create()); 197 | if (exec_engine == nullptr) { 198 | std::cerr << func_name << ": Failed to construct ExecutionEngine: " 199 | << err_str 200 | << "\n"; 201 | return 1; 202 | } 203 | ir_gen.addGlobalFunctionMappings(exec_engine.get()); 204 | exec_engine->finalizeObject(); 205 | auto func_ptr = reinterpret_cast(exec_engine-> 206 | getPointerToFunction(ll_func_ptr)); 207 | 208 | // Now working with optimization backend 209 | goSATAlgorithm current_alg = 210 | (opt_go_algorithm == kUndefinedAlg) ? kCRS2 : opt_go_algorithm; 211 | 212 | gosat::NLoptOptimizer nl_opt(static_cast(current_alg)); 213 | int status = 0; 214 | double minima = 1.0; /* minimum getValue */ 215 | std::vector model_vec(ir_gen.getVarCount(), 0.0); 216 | if (ir_gen.getVarCount() == 0) { 217 | // const function 218 | minima = (func_ptr)(0, nullptr, nullptr, nullptr); 219 | } else { 220 | status = nl_opt.optimize(func_ptr, 221 | static_cast(model_vec.size()), 222 | model_vec.data(), 223 | &minima); 224 | } 225 | if (ir_gen.isFoundUnsupportedSMTExpr()) { 226 | std::cout<< "unsupported\n"; 227 | 228 | } 229 | std::string result = (minima == 0 && !ir_gen.isFoundUnsupportedSMTExpr()) 230 | ? "sat" : "unknown"; 231 | if (smtlib_compliant_output) { 232 | std::cout << result << std::endl; 233 | } else { 234 | if (status < 0) { 235 | std::cout << std::setprecision(4); 236 | std::cout << func_name << "," << result << "," 237 | << elapsedTimeFrom(time_start) 238 | << ",INF," << status; 239 | } else { 240 | std::cout << std::setprecision(4); 241 | std::cout << func_name << "," << result << ","; 242 | std::cout << elapsedTimeFrom(time_start) << ","; 243 | std::cout << std::setprecision(dbl::digits10) << minima << "," 244 | << status; 245 | } 246 | if (minima == 0 && validate_model) { 247 | gosat::ModelValidator validator(&ir_gen); 248 | if (validator.isValid(smt_expr, model_vec)) { 249 | std::cout << ",valid"; 250 | } else { 251 | std::cout << ",invalid"; 252 | } 253 | } 254 | std::cout << std::endl; 255 | } 256 | } catch (const z3::exception &exp) { 257 | std::cerr << "Error occurred while processing your input: " 258 | << exp.msg() << std::endl; 259 | std::exit(2); 260 | } 261 | return 0; 262 | } 263 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Building a library ## 3 | It's possible to instruct goSAT to generate C files required to build a dynamic library (`libgofuncs`) 4 | using code generated from several SMT formulas. This library can be provided as input to our `bh_solver` 5 | and `nl_solver` utilities which are located in this folder. 6 | 7 | In ordered to reproduce (most) results published in XSat. 8 | First, download and unzip the `griggio` benchmarks formulas which are available [online]. 9 | Then move to folder `griggio/fmcad12`. 10 | 11 | ```shell 12 | unzip QF_FP_Hierarchy.zip 13 | cd QF_FP/griggio/fmcad12 14 | ``` 15 | Now, instruct goSAT to generate library files. 16 | 17 | ```shell 18 | ls */*|while read file; do gosat -mode=cg -fmt=plain -f $file;done 19 | ``` 20 | If successful, three files will be generated which are `gofuncs.h`, `gofuncs.c`, and 21 | `gofuncs.api`. The latter file simply lists pairs of function name and dimension size 22 | (number of variables) which are required to properly use the function. 23 | Now, you can compile the library by issuing the following command 24 | 25 | ```shell 26 | gcc -Wall -O2 -shared -o libgofuncs.so -fPIC gofuncs.c 27 | ``` 28 | 29 | Assuming that you have python3 with packages `numpy` and `scipy` installed, you 30 | can try solving the benchmarks using this command 31 | 32 | ```shell 33 | python3 ~/artifact/gosat/extras/bh_solver.py $[path-to-library-and-api-file] 34 | ``` 35 | 36 | Alternatively, you can check out `nl_solver` utility by building it from source. 37 | Building `nl_solver` can be done using cmake. 38 | 39 | [online]: 40 | -------------------------------------------------------------------------------- /tools/bh_solver/.gitignore: -------------------------------------------------------------------------------- 1 | # Auto generated files 2 | gofuncs.api 3 | -------------------------------------------------------------------------------- /tools/bh_solver/bh_solver.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import numpy as np 3 | from scipy.optimize import basinhopping 4 | from timeit import default_timer as perftimer 5 | import sys 6 | 7 | 8 | class NloptFunctor: 9 | def __init__(self, dim, func): 10 | self._dim = dim 11 | self._func = func 12 | self._func.restype = ctypes.c_double 13 | 14 | def __call__(self, ndarr): 15 | return self._func(self._dim, ndarr.ctypes.data_as(ctypes.POINTER(ctypes.c_double)), None, None) 16 | 17 | 18 | class BHConfig: 19 | def __init__(self): 20 | self.Timeout = 600 21 | self.MaxIter = 100 22 | self.MaxLocalIter = 100000 23 | self.RelTolerance = 1e-8 24 | self.Temperature = 1.0 25 | self.StepSize = 0.5 26 | self.Interval = 50 27 | 28 | def __str__(self): 29 | res = "TO:" + str(self.Timeout) + "," \ 30 | + "GITER:" + str(self.MaxIter) + "," \ 31 | + "LITER:" + str(self.MaxLocalIter) + "," \ 32 | + "FTOL:" + str(self.RelTolerance) + "," \ 33 | + "TEMP:" + str(self.Temperature) + "," \ 34 | + "STEP:" + str(self.StepSize) + "," \ 35 | + "IVAl:" + str(self.Interval) 36 | return res 37 | 38 | 39 | bh_cfg = BHConfig() 40 | #print(bh_cfg) 41 | 42 | 43 | def basinhopping_t(func, x_arr): 44 | import signal 45 | 46 | def handler(signum, frame): 47 | raise TimeoutError() 48 | 49 | signal.signal(signal.SIGALRM, handler) 50 | # default timeout in seconds 51 | signal.alarm(bh_cfg.Timeout) 52 | try: 53 | nperror = 'ignore' 54 | with np.errstate(divide=nperror, over=nperror, invalid=nperror): 55 | # You can try to play with options of local minimizer. Doing that in Scipy 0.18 can result in slowdown 56 | # options = {'maxiter': bh_cfg.MaxLocalIter, 'ftol': bh_cfg.RelTolerance} 57 | # minimizer_kwargs = {"method": "Powell", "options": options} 58 | 59 | minimizer_kwargs = {"method": "Powell"} 60 | result = basinhopping(func, x_arr, niter=bh_cfg.MaxIter, T=bh_cfg.Temperature, stepsize=bh_cfg.StepSize, 61 | minimizer_kwargs=minimizer_kwargs, 62 | interval=bh_cfg.Interval) 63 | status = '' 64 | except TimeoutError as to: 65 | result = None 66 | status = 'timeout' 67 | except RuntimeError as rt: 68 | result = None 69 | status = 'error' 70 | finally: 71 | signal.alarm(0) 72 | return result, status 73 | 74 | def elapsed_time(end_time, start_time): 75 | return "%0.3f" % (end_time - start_time) 76 | 77 | 78 | def main(): 79 | if len(sys.argv) == 1: 80 | sys.stderr.write("Please provide path to folder where libgofuncs exists!!\n") 81 | sys.exit() 82 | 83 | funcs_lib_path = sys.argv[1] 84 | funcs_api = funcs_lib_path + '/gofuncs.api' 85 | funcs_lib = funcs_lib_path + '/libgofuncs.so' 86 | 87 | gofuncs_lib = ctypes.cdll.LoadLibrary(funcs_lib) 88 | # load api functions 89 | func_name_dims = [line.rstrip('\n').split(',') for line in open(funcs_api)] 90 | # get handles to functions and set attributes 91 | func_handles = [] 92 | for i in range(len(func_name_dims)): 93 | func_handles.append(NloptFunctor(int(func_name_dims[i][1]), getattr(gofuncs_lib, func_name_dims[i][0]))) 94 | 95 | for i in range(len(func_handles)): 96 | # set up initial guess vector 97 | x_arr = np.zeros(int(func_name_dims[i][1])) 98 | start_time = perftimer() 99 | res = basinhopping_t(func_handles[i], x_arr) 100 | if res[0] is None: 101 | end_time = perftimer() 102 | print(func_name_dims[i][0] + "," + res[1] + "," 103 | + elapsed_time(end_time, start_time) + "," + "INF") 104 | continue 105 | if res[0].fun == 0: 106 | end_time = perftimer() 107 | status = 'sat' 108 | print(func_name_dims[i][0] + "," + status + "," 109 | + elapsed_time(end_time, start_time) + "," + str(res[0].fun)) 110 | # print satisfying model 111 | # print(str(res[0].x)) 112 | continue 113 | end_time = perftimer() 114 | status = 'unsat' 115 | print(func_name_dims[i][0] + "," + status + "," 116 | + elapsed_time(end_time, start_time) + "," + str(res[0].fun)) 117 | 118 | 119 | if __name__ == '__main__': 120 | main() 121 | -------------------------------------------------------------------------------- /tools/nl_solver/.gitignore: -------------------------------------------------------------------------------- 1 | # automatically generated files 2 | gofuncs.h 3 | gofuncs.api 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | # Fortran module files 21 | *.mod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Idea Project 35 | .idea 36 | 37 | # Vim backups 38 | *~ 39 | -------------------------------------------------------------------------------- /tools/nl_solver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | ### PLEASE SET PATH OF libgofuncs.so ### 3 | set(libgofuncs_path $ENV{HOME}/prefix/lib/libgofuncs.so) 4 | ######################################## 5 | 6 | set(prerequisites_available "FALSE") 7 | 8 | if((EXISTS "${libgofuncs_path}") 9 | AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gofuncs.h") 10 | AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gofuncs.api")) 11 | set(prerequisites_available "TRUE") 12 | else() 13 | message(STATUS "One or more of nl_solver prerequisites were not found. Utility nl_solver will not be built") 14 | endif() 15 | 16 | if(prerequisites_available) 17 | message(STATUS "Tool nl_solver will be built..") 18 | set(SOURCE_FILES 19 | nl_solver.cpp 20 | GOFuncsMap.h 21 | ${CMAKE_SOURCE_DIR}/src/Optimizer/NLoptOptimizer.cpp 22 | ) 23 | add_library(libgofuncs SHARED IMPORTED) 24 | set_target_properties(libgofuncs PROPERTIES IMPORTED_LOCATION ${libgofuncs_path}) 25 | add_executable(nl_solver ${SOURCE_FILES}) 26 | target_link_libraries(nl_solver libnlopt libgofuncs) 27 | endif() -------------------------------------------------------------------------------- /tools/nl_solver/GOFuncsMap.h: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #pragma once 11 | 12 | #include "nlopt.h" 13 | #include 14 | #include 15 | 16 | extern "C" { 17 | #include "gofuncs.h" 18 | } 19 | 20 | namespace gosat { 21 | 22 | class GOFuncsMap { 23 | using FuncEntry = std::pair; 24 | public: 25 | GOFuncsMap() 26 | { 27 | m_global_func_vec = 28 | { 29 | #include "gofuncs.api" 30 | 31 | {"_dummy_func", {m_dummy_func, 1}} 32 | }; 33 | m_global_func_vec.pop_back(); 34 | } 35 | 36 | virtual ~GOFuncsMap() = default; 37 | 38 | const std::vector>& 39 | getFuncMap() const noexcept 40 | { return m_global_func_vec; }; 41 | 42 | private: 43 | nlopt_func m_dummy_func; 44 | std::vector> m_global_func_vec; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /tools/nl_solver/nl_solver.cpp: -------------------------------------------------------------------------------- 1 | //===------------------------------------------------------------*- C++ -*-===// 2 | // 3 | // This file is distributed under MIT License. See LICENSE.txt for details. 4 | // 5 | //===----------------------------------------------------------------------===// 6 | // 7 | // Copyright (c) 2017 University of Kaiserslautern. 8 | // 9 | 10 | #include "GOFuncsMap.h" 11 | #include "Optimizer/NLoptOptimizer.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | typedef std::numeric_limits dbl; 18 | 19 | inline float elapsedTimeFrom(std::chrono::steady_clock::time_point& st_time) 20 | { 21 | const auto res = std::chrono::duration_cast 22 | (std::chrono::steady_clock::now() - st_time).count(); 23 | return static_cast(res) / 1000; 24 | } 25 | 26 | int main() 27 | { 28 | /* 29 | * The following global optimization algorithms seem to give the best 30 | * results. Note that all of them are derivative-free algorithms. 31 | * 32 | * - NLOPT_GN_DIRECT_L (provide similar performance to its variants) 33 | * - NLOPT_GN_CRS2_LM 34 | * - NLOPT_GN_ISRES 35 | * - NLOPT_G_MLSL 36 | * 37 | */ 38 | gosat::NLoptOptimizer opt(NLOPT_GN_CRS2_LM); 39 | gosat::GOFuncsMap func_map; 40 | int status = 0; 41 | for (const auto& entry: func_map.getFuncMap()) { 42 | std::chrono::steady_clock::time_point 43 | begin = std::chrono::steady_clock::now(); 44 | const auto func = entry.second.first; 45 | const auto dim = entry.second.second; 46 | std::vector x(dim, 0.0); 47 | double minima = 1.0; /* minimum getValue */ 48 | status = opt.optimize(func, dim, x.data(), &minima); 49 | if (status < 0) { 50 | std::cout << std::setprecision(4); 51 | std::cout << entry.first << ",error," 52 | << elapsedTimeFrom(begin) << ",INF," << status 53 | << std::endl; 54 | } else { 55 | std::string result = (minima == 0) ? "sat" : "unsat"; 56 | std::cout << std::setprecision(4); 57 | std::cout << entry.first << "," << result << ","; 58 | std::cout << elapsedTimeFrom(begin) << ","; 59 | std::cout << std::setprecision(dbl::digits10) << minima 60 | << "," << status << std::endl; 61 | } 62 | } 63 | return 0; 64 | } 65 | --------------------------------------------------------------------------------