diff --git a/.gitignore b/.gitignore index 43e8b1b4c..4efc82d51 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build/ *.synctex.gz /.vs *.json +__pycache__ diff --git a/.travis.yml b/.travis.yml index fc37737e9..44a304bb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ script: - mkdir -p build - cd build # set compiler, python executable, and Z3 build verbosity -- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DPYTHON_EXECUTABLE=$(which $PY_CMD) -DBUILD_Z3_VERBOSE=TRUE -DENABLE_PROGRESS_BARS=OFF .. +- cmake -DCMAKE_CXX_COMPILER=$COMPILER -DPYTHON_EXECUTABLE=$(which $PY_CMD) -DBUILD_LIBS_VERBOSE=TRUE -DENABLE_PROGRESS_BARS=OFF .. # build fiction - make -j2 # run integration tests diff --git a/CHANGELOG.md b/CHANGELOG.md index ec483f9b9..ab839c493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## v0.3.2 - 2021-01-06 +*Sometimes fiction is more easily understood than true events.* — Young-ha Kim + +### Added +- Command `onepass` for a combined SAT-based logic synthesis and physical design using [Mugen](https://github.com/whaaswijk/mugen). Thanks to Winston Haaswijk for cooperating with me on this project! +- SVG output for irregular (cell-based) clocked `fcn_cell_layout`s (thanks to Gregor Kuhn!) +- `csv_writer` for conveniently formatting experiments' results +- `tt_reader` for reading truth tables from a [file format used by Alan Mishchenko](https://people.eecs.berkeley.edu/~alanmi/temp5/) + +### Changed +- `exact --asynchronous/-a` has been renamed to `exact --async/-a` and `exact --asynchronous_max/-A` has been renamed to `exact --async_max` +- outsourced Verilog and AIGER file handling into a distinct `network_reader` class so that it can be used in custom experiments + +### Fixed +- `Docker` build that broke down due to updates to `mockturtle` and `bill` + ## v0.3.1 - 2020-06-04 *There is no doubt fiction makes a better job of the truth.* — Doris Lessing @@ -30,9 +46,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Moved to the latest releases of all libraries ### Fixed +- Python detection in CMake under different versions - Runtime logging in `exact` - Performance issues in `ortho` -- Python detection in CMake under different versions +- SEGFAULTS caused by `ortho` on large networks when compiling with gcc - `ortho -b` losing bent wire connections - `fcn_layout::random_face`'s index to coordinate mapping again, but for real now (thanks to Till Schlechtweg!) - `logic_network`s are deep-copied for each physical design call now to secure them from external changes diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d67dace9..7a64dca56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.2) project(fiction LANGUAGES CXX - VERSION 0.3.1) + VERSION 0.3.2) # C++17 set(CMAKE_CXX_STANDARD 17) @@ -30,6 +30,12 @@ if (NOT WIN32) endif () endif() +# Enable Mugen +option(ENABLE_MUGEN "Enable the usage of Mugen, a Python3 library by Winston Haaswijk for FCN one-pass synthesis, and its dependencies" ON) +if (ENABLE_MUGEN) + add_definitions(-DMUGEN) +endif () + # Include header files include_directories(src/algo/ src/io/ src/io/cmd/ src/tech/ src/topo/ src/util/) @@ -53,11 +59,18 @@ else () link_directories(${Boost_CUSTOM_LIBRARY_DIRS}) endif () +# Add option to compile libraries with verbose output +option(BUILD_LIBS_VERBOSE "Output status and warnings during library build" OFF) +# Use /dev/null to silence libraries. Not elegant but efficient +if (NOT BUILD_LIBS_VERBOSE) + set(LIB_OUTPUT_CHANNEL > /dev/null) +endif () + # Custom install prefix for libraries set(LIB_PREFIX ${CMAKE_SOURCE_DIR}/libs) # Set up a directory for Z3 solver -set(Z3_DIR ${CMAKE_BINARY_DIR}/z3) +set(Z3_DIR ${PROJECT_BINARY_DIR}/z3) if (UNIX) # Require Python interpreter @@ -65,14 +78,8 @@ if (UNIX) find_package(PythonInterp) # deprecated since CMake 3.12, but works far better set(Z3_PYTHON ${PYTHON_EXECUTABLE}) else () - find_package(Python COMPONENTS Interpreter) - set(Z3_PYTHON ${Python_EXECUTABLE}) - endif () - # Add option to compile Z3 with verbose output - option(BUILD_Z3_VERBOSE "Output status and warnings during Z3 build" OFF) - # Use /dev/null to silence Z3. Not elegant but efficient - if (NOT BUILD_Z3_VERBOSE) - set(Z3_OUTPUT_CHANNEL > /dev/null) + find_package(Python3 COMPONENTS Interpreter Development) + set(Z3_PYTHON ${Python3_EXECUTABLE}) endif () # Add option to build Z3 as a static library @@ -96,9 +103,9 @@ if (UNIX) add_custom_command( OUTPUT ${Z3_LIB_DIR}/${Z3_LINK_TARGET} PRE_BUILD - COMMAND ${Z3_PYTHON} scripts/mk_make.py -s --prefix=${Z3_DIR} ${Z3_LIB_FLAG} ${Z3_OUTPUT_CHANNEL} - COMMAND $(MAKE) -C build ${Z3_OUTPUT_CHANNEL} - COMMAND $(MAKE) -C build install ${Z3_OUTPUT_CHANNEL} + COMMAND ${Z3_PYTHON} scripts/mk_make.py -s --prefix=${Z3_DIR} ${Z3_LIB_FLAG} ${LIB_OUTPUT_CHANNEL} + COMMAND $(MAKE) -C build ${LIB_OUTPUT_CHANNEL} + COMMAND $(MAKE) -C build install ${LIB_OUTPUT_CHANNEL} WORKING_DIRECTORY ${LIB_PREFIX}/z3/) # Make sure Z3's custom build commands are actually being executed @@ -125,11 +132,28 @@ add_subdirectory(${LIB_PREFIX}/alice/) # Include mockturtle add_subdirectory(${LIB_PREFIX}/mockturtle/) +# Build glucose-syrup-4.1-parallel if Mugen is enabled +if (ENABLE_MUGEN) + add_custom_command( + OUTPUT ${PROJECT_BINARY_DIR}/glucose-syrup + PRE_BUILD + COMMAND make ${LIB_OUTPUT_CHANNEL} + COMMAND mv glucose-syrup ${PROJECT_BINARY_DIR}/glucose-syrup ${LIB_OUTPUT_CHANNEL} + COMMAND make clean ${LIB_OUTPUT_CHANNEL} + WORKING_DIRECTORY ${LIB_PREFIX}/mugen/glucose-syrup-4.1/parallel/) + + # Make sure glucose's custom build commands are actually being executed + add_custom_target(glucose_syrup + ALL + DEPENDS ${PROJECT_BINARY_DIR}/glucose-syrup) +endif () + # Build executable add_executable(fiction ${SOURCES}) if (UNIX) add_dependencies(fiction z3) endif () -# Link against Boost, Z3, alice, and lorina -target_link_libraries(fiction ${Boost_FILESYSTEM_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${Z3_LIB_DIR}/${Z3_LINK_TARGET} alice mockturtle) +# Link against Boost, Z3, alice, lorina, and pybind11 +target_link_libraries(fiction PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} + ${Z3_LIB_DIR}/${Z3_LINK_TARGET} alice mockturtle pybind11::embed ${Python3_LIBRARIES}) diff --git a/Dockerfile b/Dockerfile index 1cf7d26de..72a52ac29 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ ARG NUMBER_OF_JOBS=1 cmake cmake-doc extra-cmake-modules extra-cmake-modules-doc \ # # Install packages needed to build fiction - git g++ cmake boost-dev python readline-dev + git g++ cmake boost-dev python3 python3-dev readline-dev zlib-dev # Clone the repository including submodules RUN git clone --recursive https://github.com/marcelwa/fiction.git diff --git a/README.md b/README.md index 2e5522492..c347a8d5a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Let there be a fiction [![Build Status](https://travis-ci.com/marcelwa/fiction.svg?branch=master)](https://travis-ci.com/marcelwa/fiction) +[![deepcode](https://www.deepcode.ai/api/gh/badge?key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwbGF0Zm9ybTEiOiJnaCIsIm93bmVyMSI6Im1hcmNlbHdhIiwicmVwbzEiOiJmaWN0aW9uIiwiaW5jbHVkZUxpbnQiOmZhbHNlLCJhdXRob3JJZCI6MTU1MTUsImlhdCI6MTYxMDAxNTE1OH0.CGPNrF5TRz3lojNPfCoM_dOBS8Qxm8aZ1IzlvHfbRA0)](https://www.deepcode.ai/app/gh/marcelwa/fiction/_/dashboard?utm_content=gh%2Fmarcelwa%2Ffiction) This code base provides a framework for **fi**eld-**c**oupled **t**echnology-**i**ndependent **o**pen **n**anocomputing in C++17 using the [EPFL Logic Synthesis Libraries](https://github.com/lsils/lstools-showcase). *fiction* focuses on the @@ -16,7 +17,7 @@ technologies, and cell types, these can easily be compiled down to any desired F ### Implemented physical design algorithms -Thus for, *fiction* provides two types of physical design approaches. +Thus for, *fiction* provides three physical design approaches. First, an [exact approach](https://ieeexplore.ieee.org/document/8342060) ([PDF](http://www.informatik.uni-bremen.de/agra/doc/konf/2018DATE_ExactMethodforDesignExplorationOfQCA.pdf)) determines @@ -32,7 +33,15 @@ is implemented which is based on Orthogonal Graph Drawing (OGD). It has a huge r Although the layouts generated by this approach are not optimal in terms of area, this technique is applicable even for larger circuits and provides results in reasonable runtime. (See [OGD-based physical design](#ogd-based-ortho).) -This is ongoing research and more algorithms are to come! +Finally, an experimental [one-pass algorithm](http://www.informatik.uni-bremen.de/agra/doc/konf/ASP-DAC2021_One-pass_Synthesis_for_Field-coupled_Nanocomputing_Technologies.pdf) +([PDF](http://www.informatik.uni-bremen.de/agra/doc/konf/ASP-DAC2021_One-pass_Synthesis_for_Field-coupled_Nanocomputing_Technologies.pdf)) +is provided that uses SAT solver calls to perform logic synthesis and physical design combined in a single run. This +enables absolute minimality, but has the largest runtime overhead. Thereby, it is not suited for large-scale circuits. +(See [SAT-based one-pass synthesis](#sat-based-one-pass-synthesis-onepass).) + +Furthermore, *fiction* provides [formal verification](http://www.informatik.uni-bremen.de/agra/doc/konf/2020_DAC_Verification_for_Field-coupled_Nanocomputing_Circuits.pdf) +([PDF](http://www.informatik.uni-bremen.de/agra/doc/konf/2020_DAC_Verification_for_Field-coupled_Nanocomputing_Circuits.pdf)) +and utility functions. See the [CLI functionality](#Using-the-command-line-interface-(CLI)) for more details. ### Supported technologies @@ -155,11 +164,11 @@ Learn more about [benchmarking and scripting](#benchmarking-and-scripting). ### Logic synthesis -*fiction* focuses on physical design, i.e. placement, routing, and timing, of circuits and takes logic synthesis for granted. As +*fiction* focuses on physical design, i.e., placement, routing, and timing, of circuits and takes logic synthesis for granted. As described [later](#preface), the tool [ABC](https://github.com/berkeley-abc/abc) for example could be used to generate synthesized logic networks which can be passed to *fiction* as circuit specifications. -However, *fiction* makes use of the logic network library [mockturtle](https://github.com/lsils/mockturtle) by Mathias Soeken which +However, *fiction* makes use of the logic network library [mockturtle](https://github.com/lsils/mockturtle) by Mathias Soeken, which comes with various logic synthesis and optimization algorithms. The reader may feel free to make use of them prior to physical design in order to obtain optimized layouts. @@ -167,9 +176,12 @@ The `mockturtle` library is also used to provide functionality for generating ne See the section about [logic networks](#circuit-specifications-in-terms-of-logic-networks) for more information about network generation and manipulation. +The [one-pass synthesis algorithm](#sat-based-one-pass-synthesis) can be utilized for combined logic synthesis and +physical design from truth table or logic network specifications. + ## Building process -*For building within a Docker container, see section [Docker](#docker).* +*For building within a Docker container, see the [Docker directions](#docker).* Git, g++, cmake and the Boost libraries are necessary in order to build *fiction*. Since a Python interpreter and GNU readline are utilized by some dependencies, it is also recommended to set them up. @@ -196,7 +208,7 @@ Afterwards, *fiction* is ready to be built. Use this command to install all necessary libraries. ```sh -sudo apt-get install git g++ cmake libboost-all-dev python libreadline-dev +sudo apt-get install git g++ cmake libboost-all-dev graphviz python3 python3-dev libreadline-dev ``` Note that there is no guarantee that a system does not lack some required packages which are not listed here! @@ -216,6 +228,23 @@ One have the choice to change the `cmake` call to `cmake -DCMAKE_BUILD_TYPE=Debu information is preferred. The build mode can also be toggled via the `ccmake` CLI. Note that building with debug information will have a significant negative impact on *fiction*'s runtime! + +The [one-pass synthesis algorithm](#sat-based-one-pass-synthesis) is embedded via the Python3 script +[Mugen](https://github.com/whaaswijk/mugen) by Winston Haaswijk using [pybind11](https://github.com/pybind/pybind11). +It has some further Python dependencies that can be installed via `pip`: + +```sh +pip3 install python-sat wrapt_timeout_decorator graphviz +``` + +The Python3 integration may cause issues on some systems. In case, you want to utilize Mugen, make sure to follow the +setup thoroughly. Note that Mugen requires at least Python 3.7! Mugen can be disabled via toggling the `ENABLE_MUGEN` +flag, e.g., calling + +```sh +cmake -DENABLE_MUGEN=OFF .. +``` + ### Docker [Docker](https://www.docker.com/) can be used to build an image to run *fiction* or to use it for development @@ -497,7 +526,7 @@ The most important ones are - Route all I/Os to the layout's borders (`-b`) - Allow for de-synchronized circuits (`-d`) - Allow artificial clock latches (`-l`) -- Enable parallelism (`-a ...` / `-A`) +- Enable parallelism (`-a ...` / `--async_max`) See `exact -h` for a full list. @@ -524,6 +553,20 @@ This scalable approach only works on logic networks which are AOIGs (MAJ gates d [2DDWave](https://ieeexplore.ieee.org/document/1717097) and the algorithm can only be slightly parameterized (see `ortho -h`). The recommended setting is `ortho -b`. +#### SAT-based one-pass synthesis (`onepass`) + +The idea of the one-pass synthesis is to combine logic synthesis and physical design into a single run and, thereby, +obtain even smaller layouts than possible with the SMT-based exact placement & routing approach. The backend of this +algorithm was developed by Winston Haaswijk as the Python3 library [Mugen](https://github.com/whaaswijk/mugen). +It utilizes the SAT solver [Glucose](https://www.labri.fr/perso/lsimon/glucose/) to solve instances of said combined +physical design problem. Given a clocking scheme and a set of gate types to use, this algorithm finds the true minimum +FCN circuit implementation of some specification under the provided parameters. For more information, see +[the paper](http://www.informatik.uni-bremen.de/agra/doc/konf/ASP-DAC2021_One-pass_Synthesis_for_Field-coupled_Nanocomputing_Technologies.pdf) +([PDF](http://www.informatik.uni-bremen.de/agra/doc/konf/ASP-DAC2021_One-pass_Synthesis_for_Field-coupled_Nanocomputing_Technologies.pdf)) + +The possible parameters are similar to the ones used for `exact`. See `onepass -h` for a full list. + + ### Design rule checking (`check`) Physical integrity of designed circuits can be verified using command `check`. It triggers a design rule checker which diff --git a/libs/.dcignore b/libs/.dcignore new file mode 100644 index 000000000..41aa5cefa --- /dev/null +++ b/libs/.dcignore @@ -0,0 +1,2 @@ +* +** diff --git a/libs/mockturtle b/libs/mockturtle index 78c297489..5341ae102 160000 --- a/libs/mockturtle +++ b/libs/mockturtle @@ -1 +1 @@ -Subproject commit 78c29748964b5542d12b9a36417bdf4a5da64f75 +Subproject commit 5341ae102cb5dce71e8086044a23e156a0804c74 diff --git a/libs/mugen/README.md b/libs/mugen/README.md new file mode 100644 index 000000000..e5b6d9617 --- /dev/null +++ b/libs/mugen/README.md @@ -0,0 +1,9 @@ +[![Documentation Status](https://readthedocs.org/projects/mugen/badge/?version=latest)](http://mugen.readthedocs.io/en/latest/?badge=latest) + +# Mugen + +Mugen is a Python library for gate-level SAT-based physical synthesis +of [Field-coupled Nanocomputing +(FCN)](https://www.springer.com/de/book/9783662437216) devices. + +[Read the full documentation.](https://mugen.readthedocs.io/en/latest/?badge=latest) diff --git a/libs/mugen/glucose-syrup-4.1/Changelog b/libs/mugen/glucose-syrup-4.1/Changelog new file mode 100644 index 000000000..dbf045fba --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/Changelog @@ -0,0 +1,30 @@ +Version 4.1 + - Adaptative (activated by default) strategies. See SAT 2016 paper. + +Version 4.0 + - Add a Multithread version, called syrup (many glucose ;-) + See SAT14 paper: Lazy Clause Exchange Policy for parallel SAT solvers. + + - Can work indepentently in sequential or with many cores + +Version 3.0 (2013) + - Add incremental features. + See SAT13 paper: Improving Glucose for Incremental SAT Solving with Assumptions: Application to MUS Extraction + + - Add certified UNSAT proof. + +Version 2.3 (2012) + - Add new restart strategy + See CP12 paper: Refining Restarts Strategies For SAT and UNSAT + + - Add additionnal features to speed the search + +Version 2.0 (2011) + - Add additionnal features (freeze potential good clauses for one turn) + + - Based on Minisat 2.2 + +Version 1.0 (2009) + - Based on Minisat 2.0 + First release of glucose. + See ijcai 2009 paper: Predicting Learnt Clauses Quality in Modern SAT Solver diff --git a/libs/mugen/glucose-syrup-4.1/LICENCE b/libs/mugen/glucose-syrup-4.1/LICENCE new file mode 100644 index 000000000..510035063 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/LICENCE @@ -0,0 +1,47 @@ + Glucose -- Copyright (c) 2009-2017, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/libs/mugen/glucose-syrup-4.1/README b/libs/mugen/glucose-syrup-4.1/README new file mode 100644 index 000000000..19cd95f42 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/README @@ -0,0 +1,24 @@ +Directory overview: +================== + +mtl/ Minisat Template Library +core/ A core version of the solver glucose (no main here) +simp/ An extended solver with simplification capabilities +parallel/ A multicore version of glucose +README +LICENSE +Changelog + +To build (release version: without assertions, statically linked, etc): +====================================================================== +Like minisat.... + +cd { simp | parallel } +make rs + +Usage: +====== + +in simp directory: ./glucose --help + +in parallel directory: ./glucose-syrup --help \ No newline at end of file diff --git a/libs/mugen/glucose-syrup-4.1/core/BoundedQueue.h b/libs/mugen/glucose-syrup-4.1/core/BoundedQueue.h new file mode 100644 index 000000000..5269c9b74 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/core/BoundedQueue.h @@ -0,0 +1,148 @@ +/***************************************************************************************[BoundedQueue.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + + +#ifndef BoundedQueue_h +#define BoundedQueue_h + +#include "mtl/Vec.h" + +//================================================================================================= + +namespace Glucose { + +template +class bqueue { + vec elems; + int first; + int last; + unsigned long long sumofqueue; + int maxsize; + int queuesize; // Number of current elements (must be < maxsize !) + bool expComputed; + double exp,value; +public: + bqueue(void) : first(0), last(0), sumofqueue(0), maxsize(0), queuesize(0),expComputed(false) { } + + void initSize(int size) {growTo(size);exp = 2.0/(size+1);} // Init size of bounded size queue + + void push(T x) { + expComputed = false; + if (queuesize==maxsize) { + assert(last==first); // The queue is full, next value to enter will replace oldest one + sumofqueue -= elems[last]; + if ((++last) == maxsize) last = 0; + } else + queuesize++; + sumofqueue += x; + elems[first] = x; + if ((++first) == maxsize) {first = 0;last = 0;} + } + + T peek() { assert(queuesize>0); return elems[last]; } + void pop() {sumofqueue-=elems[last]; queuesize--; if ((++last) == maxsize) last = 0;} + + unsigned long long getsum() const {return sumofqueue;} + unsigned int getavg() const {return (unsigned int)(sumofqueue/((unsigned long long)queuesize));} + int maxSize() const {return maxsize;} + double getavgDouble() const { + double tmp = 0; + for(int i=0;i + +#include "utils/ParseUtils.h" +#include "core/SolverTypes.h" + +namespace Glucose { + +//================================================================================================= +// DIMACS Parser: + +template +static void readClause(B& in, Solver& S, vec& lits) { + int parsed_lit, var; + lits.clear(); + for (;;){ + parsed_lit = parseInt(in); + if (parsed_lit == 0) break; + var = abs(parsed_lit)-1; + while (var >= S.nVars()) S.newVar(); + lits.push( (parsed_lit > 0) ? mkLit(var) : ~mkLit(var) ); + } +} + +template +static void parse_DIMACS_main(B& in, Solver& S) { + vec lits; + int vars = 0; + int clauses = 0; + int cnt = 0; + for (;;){ + skipWhitespace(in); + if (*in == EOF) break; + else if (*in == 'p'){ + if (eagerMatch(in, "p cnf")){ + vars = parseInt(in); + clauses = parseInt(in); + // SATRACE'06 hack + // if (clauses > 4000000) + // S.eliminate(true); + }else{ + printf("PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + } + } else if (*in == 'c' || *in == 'p') + skipLine(in); + else{ + cnt++; + readClause(in, S, lits); + S.addClause_(lits); } + } + if (vars != S.nVars()) + fprintf(stderr, "WARNING! DIMACS header mismatch: wrong number of variables.\n"); + if (cnt != clauses) + fprintf(stderr, "WARNING! DIMACS header mismatch: wrong number of clauses.\n"); +} + +// Inserts problem into solver. +// +template +static void parse_DIMACS(gzFile input_stream, Solver& S) { + StreamBuffer in(input_stream); + parse_DIMACS_main(in, S); } + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/core/Makefile b/libs/mugen/glucose-syrup-4.1/core/Makefile new file mode 100644 index 000000000..14c4e9cf1 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/core/Makefile @@ -0,0 +1,3 @@ +PHONY: + @echo "** Careful ** Since 4.0 you have to use the simp or parallel directory only for typing make" + diff --git a/libs/mugen/glucose-syrup-4.1/core/Solver.cc b/libs/mugen/glucose-syrup-4.1/core/Solver.cc new file mode 100644 index 000000000..38f60a406 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/core/Solver.cc @@ -0,0 +1,2017 @@ +/***************************************************************************************[Solver.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#include + +#include "utils/System.h" +#include "mtl/Sort.h" +#include "core/Solver.h" +#include "core/Constants.h" +#include"simp/SimpSolver.h" + +using namespace Glucose; + + +//================================================================================================= +// Statistics +//================================================================================================= + + + +//================================================================================================= +// Options: + +static const char *_cat = "CORE"; +static const char *_cr = "CORE -- RESTART"; +static const char *_cred = "CORE -- REDUCE"; +static const char *_cm = "CORE -- MINIMIZE"; + + +static DoubleOption opt_K(_cr, "K", "The constant used to force restart", 0.8, DoubleRange(0, false, 1, false)); +static DoubleOption opt_R(_cr, "R", "The constant used to block restart", 1.4, DoubleRange(1, false, 5, false)); +static IntOption opt_size_lbd_queue(_cr, "szLBDQueue", "The size of moving average for LBD (restarts)", 50, IntRange(10, INT32_MAX)); +static IntOption opt_size_trail_queue(_cr, "szTrailQueue", "The size of moving average for trail (block restarts)", 5000, IntRange(10, INT32_MAX)); + +static IntOption opt_first_reduce_db(_cred, "firstReduceDB", "The number of conflicts before the first reduce DB (or the size of leernts if chanseok is used)", + 2000, IntRange(0, INT32_MAX)); +static IntOption opt_inc_reduce_db(_cred, "incReduceDB", "Increment for reduce DB", 300, IntRange(0, INT32_MAX)); +static IntOption opt_spec_inc_reduce_db(_cred, "specialIncReduceDB", "Special increment for reduce DB", 1000, IntRange(0, INT32_MAX)); +static IntOption opt_lb_lbd_frozen_clause(_cred, "minLBDFrozenClause", "Protect clauses if their LBD decrease and is lower than (for one turn)", 30, + IntRange(0, INT32_MAX)); +static BoolOption opt_chanseok_hack(_cred, "chanseok", + "Use Chanseok Oh strategy for LBD (keep all LBD<=co and remove half of firstreduceDB other learnt clauses", false); +static IntOption opt_chanseok_limit(_cred, "co", "Chanseok Oh: all learnt clauses with LBD<=co are permanent", 5, IntRange(2, INT32_MAX)); + + +static IntOption opt_lb_size_minimzing_clause(_cm, "minSizeMinimizingClause", "The min size required to minimize clause", 30, IntRange(3, INT32_MAX)); +static IntOption opt_lb_lbd_minimzing_clause(_cm, "minLBDMinimizingClause", "The min LBD required to minimize clause", 6, IntRange(3, INT32_MAX)); + + +static DoubleOption opt_var_decay(_cat, "var-decay", "The variable activity decay factor (starting point)", 0.8, DoubleRange(0, false, 1, false)); +static DoubleOption opt_max_var_decay(_cat, "max-var-decay", "The variable activity decay factor", 0.95, DoubleRange(0, false, 1, false)); +static DoubleOption opt_clause_decay(_cat, "cla-decay", "The clause activity decay factor", 0.999, DoubleRange(0, false, 1, false)); +static DoubleOption opt_random_var_freq(_cat, "rnd-freq", "The frequency with which the decision heuristic tries to choose a random variable", 0, + DoubleRange(0, true, 1, true)); +static DoubleOption opt_random_seed(_cat, "rnd-seed", "Used by the random variable selection", 91648253, DoubleRange(0, false, HUGE_VAL, false)); +static IntOption opt_ccmin_mode(_cat, "ccmin-mode", "Controls conflict clause minimization (0=none, 1=basic, 2=deep)", 2, IntRange(0, 2)); +static IntOption opt_phase_saving(_cat, "phase-saving", "Controls the level of phase saving (0=none, 1=limited, 2=full)", 2, IntRange(0, 2)); +static BoolOption opt_rnd_init_act(_cat, "rnd-init", "Randomize the initial activity", false); +static DoubleOption opt_garbage_frac(_cat, "gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered", 0.20, + DoubleRange(0, false, HUGE_VAL, false)); +static BoolOption opt_glu_reduction(_cat, "gr", "glucose strategy to fire clause database reduction (must be false to fire Chanseok strategy)", true); +static BoolOption opt_luby_restart(_cat, "luby", "Use the Luby restart sequence", false); +static DoubleOption opt_restart_inc(_cat, "rinc", "Restart interval increase factor", 2, DoubleRange(1, false, HUGE_VAL, false)); +static IntOption opt_luby_restart_factor(_cred, "luby-factor", "Luby restart factor", 100, IntRange(1, INT32_MAX)); + +static IntOption opt_randomize_phase_on_restarts(_cat, "phase-restart", + "The amount of randomization for the phase at each restart (0=none, 1=first branch, 2=first branch (no bad clauses), 3=first branch (only initial clauses)", + 0, IntRange(0, 3)); +static BoolOption opt_fixed_randomize_phase_on_restarts(_cat, "fix-phas-rest", "Fixes the first 7 levels at random phase", false); + +static BoolOption opt_adapt(_cat, "adapt", "Adapt dynamically stategies after 100000 conflicts", true); + +static BoolOption opt_forceunsat(_cat,"forceunsat","Force the phase for UNSAT",true); +//================================================================================================= +// Constructor/Destructor: + +Solver::Solver() : + +// Parameters (user settable): +// +verbosity(0) +, showModel(0) +, K(opt_K) +, R(opt_R) +, sizeLBDQueue(opt_size_lbd_queue) +, sizeTrailQueue(opt_size_trail_queue) +, firstReduceDB(opt_first_reduce_db) +, incReduceDB(opt_chanseok_hack ? 0 : opt_inc_reduce_db) +, specialIncReduceDB(opt_chanseok_hack ? 0 : opt_spec_inc_reduce_db) +, lbLBDFrozenClause(opt_lb_lbd_frozen_clause) +, chanseokStrategy(opt_chanseok_hack) +, coLBDBound (opt_chanseok_limit) +, lbSizeMinimizingClause(opt_lb_size_minimzing_clause) +, lbLBDMinimizingClause(opt_lb_lbd_minimzing_clause) +, var_decay(opt_var_decay) +, max_var_decay(opt_max_var_decay) +, clause_decay(opt_clause_decay) +, random_var_freq(opt_random_var_freq) +, random_seed(opt_random_seed) +, ccmin_mode(opt_ccmin_mode) +, phase_saving(opt_phase_saving) +, rnd_pol(false) +, rnd_init_act(opt_rnd_init_act) +, randomizeFirstDescent(false) +, garbage_frac(opt_garbage_frac) +, certifiedOutput(NULL) +, certifiedUNSAT(false) // Not in the first parallel version +, vbyte(false) +, panicModeLastRemoved(0), panicModeLastRemovedShared(0) +, useUnaryWatched(false) +, promoteOneWatchedClause(true) +,solves(0),starts(0),decisions(0),propagations(0),conflicts(0),conflictsRestarts(0) +, curRestart(1) +, glureduce(opt_glu_reduction) +, restart_inc(opt_restart_inc) +, luby_restart(opt_luby_restart) +, adaptStrategies(opt_adapt) +, luby_restart_factor(opt_luby_restart_factor) +, randomize_on_restarts(opt_randomize_phase_on_restarts) +, fixed_randomize_on_restarts(opt_fixed_randomize_phase_on_restarts) +, newDescent(0) +, randomDescentAssignments(0) +, forceUnsatOnNewDescent(opt_forceunsat) + +, ok(true) +, cla_inc(1) +, var_inc(1) +, watches(WatcherDeleted(ca)) +, watchesBin(WatcherDeleted(ca)) +, unaryWatches(WatcherDeleted(ca)) +, qhead(0) +, simpDB_assigns(-1) +, simpDB_props(0) +, order_heap(VarOrderLt(activity)) +, progress_estimate(0) +, remove_satisfied(true) +,lastLearntClause(CRef_Undef) +// Resource constraints: +// +, conflict_budget(-1) +, propagation_budget(-1) +, asynch_interrupt(false) +, incremental(false) +, nbVarsInitialFormula(INT32_MAX) +, totalTime4Sat(0.) +, totalTime4Unsat(0.) +, nbSatCalls(0) +, nbUnsatCalls(0) +{ + MYFLAG = 0; + // Initialize only first time. Useful for incremental solving (not in // version), useless otherwise + // Kept here for simplicity + lbdQueue.initSize(sizeLBDQueue); + trailQueue.initSize(sizeTrailQueue); + sumLBD = 0; + nbclausesbeforereduce = firstReduceDB; + stats.growTo(coreStatsSize, 0); +} + +//------------------------------------------------------- +// Special constructor used for cloning solvers +//------------------------------------------------------- + +Solver::Solver(const Solver &s) : + verbosity(s.verbosity) +, showModel(s.showModel) +, K(s.K) +, R(s.R) +, sizeLBDQueue(s.sizeLBDQueue) +, sizeTrailQueue(s.sizeTrailQueue) +, firstReduceDB(s.firstReduceDB) +, incReduceDB(s.incReduceDB) +, specialIncReduceDB(s.specialIncReduceDB) +, lbLBDFrozenClause(s.lbLBDFrozenClause) +, chanseokStrategy(opt_chanseok_hack) +, coLBDBound (opt_chanseok_limit) +, lbSizeMinimizingClause(s.lbSizeMinimizingClause) +, lbLBDMinimizingClause(s.lbLBDMinimizingClause) +, var_decay(s.var_decay) +, max_var_decay(s.max_var_decay) +, clause_decay(s.clause_decay) +, random_var_freq(s.random_var_freq) +, random_seed(s.random_seed) +, ccmin_mode(s.ccmin_mode) +, phase_saving(s.phase_saving) +, rnd_pol(s.rnd_pol) +, rnd_init_act(s.rnd_init_act) +, randomizeFirstDescent(s.randomizeFirstDescent) +, garbage_frac(s.garbage_frac) +, certifiedOutput(NULL) +, certifiedUNSAT(false) // Not in the first parallel version +, panicModeLastRemoved(s.panicModeLastRemoved), panicModeLastRemovedShared(s.panicModeLastRemovedShared) +, useUnaryWatched(s.useUnaryWatched) +, promoteOneWatchedClause(s.promoteOneWatchedClause) +// Statistics: (formerly in 'SolverStats') +// +,solves(0),starts(0),decisions(0),propagations(0),conflicts(0),conflictsRestarts(0) + +, curRestart(s.curRestart) +, glureduce(s.glureduce) +, restart_inc(s.restart_inc) +, luby_restart(s.luby_restart) +, adaptStrategies(s.adaptStrategies) +, luby_restart_factor(s.luby_restart_factor) +, randomize_on_restarts(s.randomize_on_restarts) +, fixed_randomize_on_restarts(s.fixed_randomize_on_restarts) +, newDescent(s.newDescent) +, randomDescentAssignments(s.randomDescentAssignments) +, forceUnsatOnNewDescent(s.forceUnsatOnNewDescent) +, ok(true) +, cla_inc(s.cla_inc) +, var_inc(s.var_inc) +, watches(WatcherDeleted(ca)) +, watchesBin(WatcherDeleted(ca)) +, unaryWatches(WatcherDeleted(ca)) +, qhead(s.qhead) +, simpDB_assigns(s.simpDB_assigns) +, simpDB_props(s.simpDB_props) +, order_heap(VarOrderLt(activity)) +, progress_estimate(s.progress_estimate) +, remove_satisfied(s.remove_satisfied) +,lastLearntClause(CRef_Undef) +// Resource constraints: +// +, conflict_budget(s.conflict_budget) +, propagation_budget(s.propagation_budget) +, asynch_interrupt(s.asynch_interrupt) +, incremental(s.incremental) +, nbVarsInitialFormula(s.nbVarsInitialFormula) +, totalTime4Sat(s.totalTime4Sat) +, totalTime4Unsat(s.totalTime4Unsat) +, nbSatCalls(s.nbSatCalls) +, nbUnsatCalls(s.nbUnsatCalls) +{ + // Copy clauses. + s.ca.copyTo(ca); + ca.extra_clause_field = s.ca.extra_clause_field; + + // Initialize other variables + MYFLAG = 0; + // Initialize only first time. Useful for incremental solving (not in // version), useless otherwise + // Kept here for simplicity + sumLBD = s.sumLBD; + nbclausesbeforereduce = s.nbclausesbeforereduce; + + // Copy all search vectors + s.watches.copyTo(watches); + s.watchesBin.copyTo(watchesBin); + s.unaryWatches.copyTo(unaryWatches); + s.assigns.memCopyTo(assigns); + s.vardata.memCopyTo(vardata); + s.activity.memCopyTo(activity); + s.seen.memCopyTo(seen); + s.permDiff.memCopyTo(permDiff); + s.polarity.memCopyTo(polarity); + s.decision.memCopyTo(decision); + s.trail.memCopyTo(trail); + s.order_heap.copyTo(order_heap); + s.clauses.memCopyTo(clauses); + s.learnts.memCopyTo(learnts); + s.permanentLearnts.memCopyTo(permanentLearnts); + + s.lbdQueue.copyTo(lbdQueue); + s.trailQueue.copyTo(trailQueue); + s.forceUNSAT.copyTo(forceUNSAT); + s.stats.copyTo(stats); +} + + +Solver::~Solver() { +} + + +/**************************************************************** + Certified UNSAT proof in binary format +****************************************************************/ + + +void Solver::write_char(unsigned char ch) { + if(putc_unlocked((int) ch, certifiedOutput) == EOF) + exit(1); +} + + +void Solver::write_lit(int n) { + for(; n > 127; n >>= 7) + write_char(128 | (n & 127)); + write_char(n); +} + +/**************************************************************** + Set the incremental mode +****************************************************************/ + +// This function set the incremental mode to true. +// You can add special code for this mode here. + +void Solver::setIncrementalMode() { +#ifdef INCREMENTAL + incremental = true; +#else + fprintf(stderr, "c Trying to set incremental mode, but not compiled properly for this.\n"); + exit(1); +#endif +} + + +// Number of variables without selectors +void Solver::initNbInitialVars(int nb) { + nbVarsInitialFormula = nb; +} + + +bool Solver::isIncremental() { + return incremental; +} + + +//================================================================================================= +// Minor methods: + + +// Creates a new SAT variable in the solver. If 'decision' is cleared, variable will not be +// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result). +// + +Var Solver::newVar(bool sign, bool dvar) { + int v = nVars(); + watches.init(mkLit(v, false)); + watches.init(mkLit(v, true)); + watchesBin.init(mkLit(v, false)); + watchesBin.init(mkLit(v, true)); + unaryWatches.init(mkLit(v, false)); + unaryWatches.init(mkLit(v, true)); + assigns.push(l_Undef); + vardata.push(mkVarData(CRef_Undef, 0)); + activity.push(rnd_init_act ? drand(random_seed) * 0.00001 : 0); + seen.push(0); + permDiff.push(0); + polarity.push(sign); + forceUNSAT.push(0); + decision.push(); + trail.capacity(v + 1); + setDecisionVar(v, dvar); + return v; +} + + +bool Solver::addClause_(vec &ps) { + + assert(decisionLevel() == 0); + if(!ok) return false; + + // Check if clause is satisfied and remove false/duplicate literals: + sort(ps); + + vec oc; + oc.clear(); + + Lit p; + int i, j, flag = 0; + if(certifiedUNSAT) { + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) { + oc.push(ps[i]); + if(value(ps[i]) == l_True || ps[i] == ~p || value(ps[i]) == l_False) + flag = 1; + } + } + + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) + if(value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if(value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + if(flag && (certifiedUNSAT)) { + if(vbyte) { + write_char('a'); + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) + write_lit(2 * (var(ps[i]) + 1) + sign(ps[i])); + write_lit(0); + + write_char('d'); + for(i = j = 0, p = lit_Undef; i < oc.size(); i++) + write_lit(2 * (var(oc[i]) + 1) + sign(oc[i])); + write_lit(0); + } + else { + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) + fprintf(certifiedOutput, "%i ", (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + + fprintf(certifiedOutput, "d "); + for(i = j = 0, p = lit_Undef; i < oc.size(); i++) + fprintf(certifiedOutput, "%i ", (var(oc[i]) + 1) * (-2 * sign(oc[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + + if(ps.size() == 0) + return ok = false; + else if(ps.size() == 1) { + uncheckedEnqueue(ps[0]); + return ok = (propagate() == CRef_Undef); + } else { + CRef cr = ca.alloc(ps, false); + clauses.push(cr); + attachClause(cr); + } + + return true; +} + + +void Solver::attachClause(CRef cr) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + if(c.size() == 2) { + watchesBin[~c[0]].push(Watcher(cr, c[1])); + watchesBin[~c[1]].push(Watcher(cr, c[0])); + } else { + watches[~c[0]].push(Watcher(cr, c[1])); + watches[~c[1]].push(Watcher(cr, c[0])); + } + if(c.learnt()) stats[learnts_literals] += c.size(); + else stats[clauses_literals] += c.size(); +} + + +void Solver::attachClausePurgatory(CRef cr) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + unaryWatches[~c[0]].push(Watcher(cr, c[1])); + +} + + +void Solver::detachClause(CRef cr, bool strict) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + if(c.size() == 2) { + if(strict) { + remove(watchesBin[~c[0]], Watcher(cr, c[1])); + remove(watchesBin[~c[1]], Watcher(cr, c[0])); + } else { + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watchesBin.smudge(~c[0]); + watchesBin.smudge(~c[1]); + } + } else { + if(strict) { + remove(watches[~c[0]], Watcher(cr, c[1])); + remove(watches[~c[1]], Watcher(cr, c[0])); + } else { + // Lazy detaching: (NOTE! Must clean all watcher lists before garbage collecting this clause) + watches.smudge(~c[0]); + watches.smudge(~c[1]); + } + } + if(c.learnt()) stats[learnts_literals] -= c.size(); + else stats[clauses_literals] -= c.size(); +} + + +// The purgatory is the 1-Watched scheme for imported clauses + +void Solver::detachClausePurgatory(CRef cr, bool strict) { + const Clause &c = ca[cr]; + + assert(c.size() > 1); + if(strict) + remove(unaryWatches[~c[0]], Watcher(cr, c[1])); + else + unaryWatches.smudge(~c[0]); +} + + +void Solver::removeClause(CRef cr, bool inPurgatory) { + + Clause &c = ca[cr]; + + if(certifiedUNSAT) { + if(vbyte) { + write_char('d'); + for(int i = 0; i < c.size(); i++) + write_lit(2 * (var(c[i]) + 1) + sign(c[i])); + write_lit(0); + } + else { + fprintf(certifiedOutput, "d "); + for(int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i ", (var(c[i]) + 1) * (-2 * sign(c[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + if(inPurgatory) + detachClausePurgatory(cr); + else + detachClause(cr); + // Don't leave pointers to free'd memory! + if(locked(c)) vardata[var(c[0])].reason = CRef_Undef; + c.mark(1); + ca.free(cr); +} + + +bool Solver::satisfied(const Clause &c) const { +#ifdef INCREMENTAL + if(incremental) + return (value(c[0]) == l_True) || (value(c[1]) == l_True); +#endif + + // Default mode + for(int i = 0; i < c.size(); i++) + if(value(c[i]) == l_True) + return true; + return false; +} + + +/************************************************************ + * Compute LBD functions + *************************************************************/ + +template inline unsigned int Solver::computeLBD(const T &lits, int end) { + int nblevels = 0; + MYFLAG++; +#ifdef INCREMENTAL + if(incremental) { // ----------------- INCREMENTAL MODE + if(end==-1) end = lits.size(); + int nbDone = 0; + for(int i=0;i=end) break; + if(isSelector(var(lits[i]))) continue; + nbDone++; + int l = level(var(lits[i])); + if (permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } + } else { // -------- DEFAULT MODE. NOT A LOT OF DIFFERENCES... BUT EASIER TO READ +#endif + for(int i = 0; i < lits.size(); i++) { + int l = level(var(lits[i])); + if(permDiff[l] != MYFLAG) { + permDiff[l] = MYFLAG; + nblevels++; + } + } +#ifdef INCREMENTAL + } +#endif + return nblevels; +} + + + +/****************************************************************** + * Minimisation with binary reolution + ******************************************************************/ +void Solver::minimisationWithBinaryResolution(vec &out_learnt) { + + // Find the LBD measure + unsigned int lbd = computeLBD(out_learnt); + Lit p = ~out_learnt[0]; + + if(lbd <= lbLBDMinimizingClause) { + MYFLAG++; + + for(int i = 1; i < out_learnt.size(); i++) { + permDiff[var(out_learnt[i])] = MYFLAG; + } + + vec &wbin = watchesBin[p]; + int nb = 0; + for(int k = 0; k < wbin.size(); k++) { + Lit imp = wbin[k].blocker; + if(permDiff[var(imp)] == MYFLAG && value(imp) == l_True) { + nb++; + permDiff[var(imp)] = MYFLAG - 1; + } + } + int l = out_learnt.size() - 1; + if(nb > 0) { + stats[nbReducedClauses]++; + for(int i = 1; i < out_learnt.size() - nb; i++) { + if(permDiff[var(out_learnt[i])] != MYFLAG) { + Lit p = out_learnt[l]; + out_learnt[l] = out_learnt[i]; + out_learnt[i] = p; + l--; + i--; + } + } + + out_learnt.shrink(nb); + + } + } +} + +// Revert to the state at given level (keeping all assignment at 'level' but not beyond). +// + +void Solver::cancelUntil(int level) { + if(decisionLevel() > level) { + for(int c = trail.size() - 1; c >= trail_lim[level]; c--) { + Var x = var(trail[c]); + assigns[x] = l_Undef; + if(phase_saving > 1 || ((phase_saving == 1) && c > trail_lim.last())) { + polarity[x] = sign(trail[c]); + } + insertVarOrder(x); + } + qhead = trail_lim[level]; + trail.shrink(trail.size() - trail_lim[level]); + trail_lim.shrink(trail_lim.size() - level); + } +} + + +//================================================================================================= +// Major methods: + +Lit Solver::pickBranchLit() { + Var next = var_Undef; + + // Random decision: + if(((randomizeFirstDescent && conflicts == 0) || drand(random_seed) < random_var_freq) && !order_heap.empty()) { + next = order_heap[irand(random_seed, order_heap.size())]; + if(value(next) == l_Undef && decision[next]) + stats[rnd_decisions]++; + } + + // Activity based decision: + while(next == var_Undef || value(next) != l_Undef || !decision[next]) + if(order_heap.empty()) { + next = var_Undef; + break; + } else { + next = order_heap.removeMin(); + } + + if(randomize_on_restarts && !fixed_randomize_on_restarts && newDescent && (decisionLevel() % 2 == 0)) { + return mkLit(next, (randomDescentAssignments >> (decisionLevel() % 32)) & 1); + } + + if(fixed_randomize_on_restarts && decisionLevel() < 7) { + return mkLit(next, (randomDescentAssignments >> (decisionLevel() % 32)) & 1); + } + + if(next == var_Undef) return lit_Undef; + + if(forceUnsatOnNewDescent && newDescent) { + if(forceUNSAT[next] != 0) + return mkLit(next, forceUNSAT[next] < 0); + return mkLit(next, polarity[next]); + + } + + return next == var_Undef ? lit_Undef : mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : polarity[next]); +} + + +/*_________________________________________________________________________________________________ +| +| analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] +| +| Description: +| Analyze conflict and produce a reason clause. +| +| Pre-conditions: +| * 'out_learnt' is assumed to be cleared. +| * Current decision level must be greater than root level. +| +| Post-conditions: +| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the +| rest of literals. There may be others from the same level though. +| +|________________________________________________________________________________________________@*/ +void Solver::analyze(CRef confl, vec &out_learnt, vec &selectors, int &out_btlevel, unsigned int &lbd, unsigned int &szWithoutSelectors) { + int pathC = 0; + Lit p = lit_Undef; + + + // Generate conflict clause: + // + out_learnt.push(); // (leave room for the asserting literal) + int index = trail.size() - 1; + do { + assert(confl != CRef_Undef); // (otherwise should be UIP) + Clause &c = ca[confl]; + // Special case for binary clauses + // The first one has to be SAT + if(p != lit_Undef && c.size() == 2 && value(c[0]) == l_False) { + + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + if(c.learnt()) { + parallelImportClauseDuringConflictAnalysis(c, confl); + claBumpActivity(c); + } else { // original clause + if(!c.getSeen()) { + stats[originalClausesSeen]++; + c.setSeen(true); + } + } + + // DYNAMIC NBLEVEL trick (see competition'09 companion paper) + if(c.learnt() && c.lbd() > 2) { + unsigned int nblevels = computeLBD(c); + if(nblevels + 1 < c.lbd()) { // improve the LBD + if(c.lbd() <= lbLBDFrozenClause) { + // seems to be interesting : keep it for the next round + c.setCanBeDel(false); + } + if(chanseokStrategy && nblevels <= coLBDBound) { + c.nolearnt(); + learnts.remove(confl); + permanentLearnts.push(confl); + stats[nbPermanentLearnts]++; + + } else { + c.setLBD(nblevels); // Update it + } + } + } + + + for(int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++) { + Lit q = c[j]; + + if(!seen[var(q)]) { + if(level(var(q)) == 0) { + } else { // Here, the old case + if(!isSelector(var(q))) + varBumpActivity(var(q)); + + // This variable was responsible for a conflict, + // consider it as a UNSAT assignation for this literal + bumpForceUNSAT(~q); // Negation because q is false here + + seen[var(q)] = 1; + if(level(var(q)) >= decisionLevel()) { + pathC++; + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(!isSelector(var(q)) && (reason(var(q)) != CRef_Undef) && ca[reason(var(q))].learnt()) + lastDecisionLevel.push(q); + } else { + if(isSelector(var(q))) { + assert(value(q) == l_False); + selectors.push(q); + } else + out_learnt.push(q); + } + } + } //else stats[sumResSeen]++; + } + + // Select next clause to look at: + while (!seen[var(trail[index--])]); + p = trail[index + 1]; + //stats[sumRes]++; + confl = reason(var(p)); + seen[var(p)] = 0; + pathC--; + + } while(pathC > 0); + out_learnt[0] = ~p; + + // Simplify conflict clause: + // + int i, j; + + for(int i = 0; i < selectors.size(); i++) + out_learnt.push(selectors[i]); + + out_learnt.copyTo(analyze_toclear); + if(ccmin_mode == 2) { + uint32_t abstract_level = 0; + for(i = 1; i < out_learnt.size(); i++) + abstract_level |= abstractLevel(var(out_learnt[i])); // (maintain an abstraction of levels involved in conflict) + + for(i = j = 1; i < out_learnt.size(); i++) + if(reason(var(out_learnt[i])) == CRef_Undef || !litRedundant(out_learnt[i], abstract_level)) + out_learnt[j++] = out_learnt[i]; + + } else if(ccmin_mode == 1) { + for(i = j = 1; i < out_learnt.size(); i++) { + Var x = var(out_learnt[i]); + + if(reason(x) == CRef_Undef) + out_learnt[j++] = out_learnt[i]; + else { + Clause &c = ca[reason(var(out_learnt[i]))]; + // Thanks to Siert Wieringa for this bug fix! + for(int k = ((c.size() == 2) ? 0 : 1); k < c.size(); k++) + if(!seen[var(c[k])] && level(var(c[k])) > 0) { + out_learnt[j++] = out_learnt[i]; + break; + } + } + } + } else + i = j = out_learnt.size(); + + // stats[max_literals]+=out_learnt.size(); + out_learnt.shrink(i - j); + // stats[tot_literals]+=out_learnt.size(); + + + /* *************************************** + Minimisation with binary clauses of the asserting clause + First of all : we look for small clauses + Then, we reduce clauses with small LBD. + Otherwise, this can be useless + */ + if(!incremental && out_learnt.size() <= lbSizeMinimizingClause) { + minimisationWithBinaryResolution(out_learnt); + } + // Find correct backtrack level: + // + if(out_learnt.size() == 1) + out_btlevel = 0; + else { + int max_i = 1; + // Find the first literal assigned at the next-highest level: + for(int i = 2; i < out_learnt.size(); i++) + if(level(var(out_learnt[i])) > level(var(out_learnt[max_i]))) + max_i = i; + // Swap-in this literal at index 1: + Lit p = out_learnt[max_i]; + out_learnt[max_i] = out_learnt[1]; + out_learnt[1] = p; + out_btlevel = level(var(p)); + } +#ifdef INCREMENTAL + if(incremental) { + szWithoutSelectors = 0; + for(int i=0;i0) break; + } + } else +#endif + szWithoutSelectors = out_learnt.size(); + + // Compute LBD + lbd = computeLBD(out_learnt, out_learnt.size() - selectors.size()); + + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + if(lastDecisionLevel.size() > 0) { + for(int i = 0; i < lastDecisionLevel.size(); i++) { + if(ca[reason(var(lastDecisionLevel[i]))].lbd() < lbd) + varBumpActivity(var(lastDecisionLevel[i])); + } + lastDecisionLevel.clear(); + } + + + for(int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared) + for(int j = 0; j < selectors.size(); j++) seen[var(selectors[j])] = 0; +} + + +// Check if 'p' can be removed. 'abstract_levels' is used to abort early if the algorithm is +// visiting literals at levels that cannot be removed later. + +bool Solver::litRedundant(Lit p, uint32_t abstract_levels) { + analyze_stack.clear(); + analyze_stack.push(p); + int top = analyze_toclear.size(); + while(analyze_stack.size() > 0) { + assert(reason(var(analyze_stack.last())) != CRef_Undef); + Clause &c = ca[reason(var(analyze_stack.last()))]; + analyze_stack.pop(); // + if(c.size() == 2 && value(c[0]) == l_False) { + assert(value(c[1]) == l_True); + Lit tmp = c[0]; + c[0] = c[1], c[1] = tmp; + } + + for(int i = 1; i < c.size(); i++) { + Lit p = c[i]; + if(!seen[var(p)]) { + if(level(var(p)) > 0) { + if(reason(var(p)) != CRef_Undef && (abstractLevel(var(p)) & abstract_levels) != 0) { + seen[var(p)] = 1; + analyze_stack.push(p); + analyze_toclear.push(p); + } else { + for(int j = top; j < analyze_toclear.size(); j++) + seen[var(analyze_toclear[j])] = 0; + analyze_toclear.shrink(analyze_toclear.size() - top); + return false; + } + } + } + } + } + + return true; +} + + +/*_________________________________________________________________________________________________ +| +| analyzeFinal : (p : Lit) -> [void] +| +| Description: +| Specialized analysis procedure to express the final conflict in terms of assumptions. +| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and +| stores the result in 'out_conflict'. +|________________________________________________________________________________________________@*/ +void Solver::analyzeFinal(Lit p, vec &out_conflict) { + out_conflict.clear(); + out_conflict.push(p); + + if(decisionLevel() == 0) + return; + + seen[var(p)] = 1; + + for(int i = trail.size() - 1; i >= trail_lim[0]; i--) { + Var x = var(trail[i]); + if(seen[x]) { + if(reason(x) == CRef_Undef) { + assert(level(x) > 0); + out_conflict.push(~trail[i]); + } else { + Clause &c = ca[reason(x)]; + // for (int j = 1; j < c.size(); j++) Minisat (glucose 2.0) loop + // Bug in case of assumptions due to special data structures for Binary. + // Many thanks to Sam Bayless (sbayless@cs.ubc.ca) for discover this bug. + for(int j = ((c.size() == 2) ? 0 : 1); j < c.size(); j++) + if(level(var(c[j])) > 0) + seen[var(c[j])] = 1; + } + + seen[x] = 0; + } + } + + seen[var(p)] = 0; +} + + +void Solver::uncheckedEnqueue(Lit p, CRef from) { + assert(value(p) == l_Undef); + assigns[var(p)] = lbool(!sign(p)); + vardata[var(p)] = mkVarData(from, decisionLevel()); + trail.push_(p); +} + + +void Solver::bumpForceUNSAT(Lit q) { + forceUNSAT[var(q)] = sign(q) ? -1 : +1; + return; +} + + +/*_________________________________________________________________________________________________ +| +| propagate : [void] -> [Clause*] +| +| Description: +| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, +| otherwise CRef_Undef. +| +| Post-conditions: +| * the propagation queue is empty, even if there was a conflict. +|________________________________________________________________________________________________@*/ +CRef Solver::propagate() { + CRef confl = CRef_Undef; + int num_props = 0; + watches.cleanAll(); + watchesBin.cleanAll(); + unaryWatches.cleanAll(); + while(qhead < trail.size()) { + Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate. + vec &ws = watches[p]; + Watcher *i, *j, *end; + num_props++; + + + // First, Propagate binary clauses + vec &wbin = watchesBin[p]; + for(int k = 0; k < wbin.size(); k++) { + + Lit imp = wbin[k].blocker; + + if(value(imp) == l_False) { + return wbin[k].cref; + } + + if(value(imp) == l_Undef) { + uncheckedEnqueue(imp, wbin[k].cref); + } + } + + // Now propagate other 2-watched clauses + for(i = j = (Watcher *) ws, end = i + ws.size(); i != end;) { + // Try to avoid inspecting the clause: + Lit blocker = i->blocker; + if(value(blocker) == l_True) { + *j++ = *i++; + continue; + } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause &c = ca[cr]; + assert(!c.getOneWatched()); + Lit false_lit = ~p; + if(c[0] == false_lit) + c[0] = c[1], c[1] = false_lit; + assert(c[1] == false_lit); + i++; + + // If 0th watch is true, then clause is already satisfied. + Lit first = c[0]; + Watcher w = Watcher(cr, first); + if(first != blocker && value(first) == l_True) { + + *j++ = w; + continue; + } +#ifdef INCREMENTAL + if(incremental) { // ----------------- INCREMENTAL MODE + int choosenPos = -1; + for (int k = 2; k < c.size(); k++) { + + if (value(c[k]) != l_False){ + if(decisionLevel()>assumptions.size()) { + choosenPos = k; + break; + } else { + choosenPos = k; + + if(value(c[k])==l_True || !isSelector(var(c[k]))) { + break; + } + } + + } + } + if(choosenPos!=-1) { + c[1] = c[choosenPos]; c[choosenPos] = false_lit; + watches[~c[1]].push(w); + goto NextClause; } + } else { // ----------------- DEFAULT MODE (NOT INCREMENTAL) +#endif + for(int k = 2; k < c.size(); k++) { + + if(value(c[k]) != l_False) { + c[1] = c[k]; + c[k] = false_lit; + watches[~c[1]].push(w); + goto NextClause; + } + } +#ifdef INCREMENTAL + } +#endif + // Did not find watch -- clause is unit under assignment: + *j++ = w; + if(value(first) == l_False) { + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while(i < end) + *j++ = *i++; + } else { + uncheckedEnqueue(first, cr); + + + } + NextClause:; + } + ws.shrink(i - j); + + // unaryWatches "propagation" + if(useUnaryWatched && confl == CRef_Undef) { + confl = propagateUnaryWatches(p); + + } + + } + + + propagations += num_props; + simpDB_props -= num_props; + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| propagateUnaryWatches : [Lit] -> [Clause*] +| +| Description: +| Propagates unary watches of Lit p, return a conflict +| otherwise CRef_Undef +| +|________________________________________________________________________________________________@*/ + +CRef Solver::propagateUnaryWatches(Lit p) { + CRef confl = CRef_Undef; + Watcher *i, *j, *end; + vec &ws = unaryWatches[p]; + for(i = j = (Watcher *) ws, end = i + ws.size(); i != end;) { + // Try to avoid inspecting the clause: + Lit blocker = i->blocker; + if(value(blocker) == l_True) { + *j++ = *i++; + continue; + } + + // Make sure the false literal is data[1]: + CRef cr = i->cref; + Clause &c = ca[cr]; + assert(c.getOneWatched()); + Lit false_lit = ~p; + assert(c[0] == false_lit); // this is unary watch... No other choice if "propagated" + //if (c[0] == false_lit) + //c[0] = c[1], c[1] = false_lit; + //assert(c[1] == false_lit); + i++; + Watcher w = Watcher(cr, c[0]); + for(int k = 1; k < c.size(); k++) { + if(value(c[k]) != l_False) { + c[0] = c[k]; + c[k] = false_lit; + unaryWatches[~c[0]].push(w); + goto NextClauseUnary; + } + } + + // Did not find watch -- clause is empty under assignment: + *j++ = w; + + confl = cr; + qhead = trail.size(); + // Copy the remaining watches: + while(i < end) + *j++ = *i++; + + // We can add it now to the set of clauses when backtracking + //printf("*"); + if(promoteOneWatchedClause) { + stats[nbPromoted]++; + // Let's find the two biggest decision levels in the clause s.t. it will correctly be propagated when we'll backtrack + int maxlevel = -1; + int index = -1; + for(int k = 1; k < c.size(); k++) { + assert(value(c[k]) == l_False); + assert(level(var(c[k])) <= level(var(c[0]))); + if(level(var(c[k])) > maxlevel) { + index = k; + maxlevel = level(var(c[k])); + } + } + detachClausePurgatory(cr, true); // TODO: check that the cleanAll is ok (use ",true" otherwise) + assert(index != -1); + Lit tmp = c[1]; + c[1] = c[index], c[index] = tmp; + attachClause(cr); + // TODO used in function ParallelSolver::reportProgressArrayImports + //Override :-( + //goodImportsFromThreads[ca[cr].importedFrom()]++; + ca[cr].setOneWatched(false); + ca[cr].setExported(2); + } + NextClauseUnary:; + } + ws.shrink(i - j); + + return confl; +} + + +/*_________________________________________________________________________________________________ +| +| reduceDB : () -> [void] +| +| Description: +| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked +| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +|________________________________________________________________________________________________@*/ + + +void Solver::reduceDB() { + + int i, j; + stats[nbReduceDB]++; + if(chanseokStrategy) + sort(learnts, reduceDBAct_lt(ca)); + else { + sort(learnts, reduceDB_lt(ca)); + + // We have a lot of "good" clauses, it is difficult to compare them. Keep more ! + if(ca[learnts[learnts.size() / RATIOREMOVECLAUSES]].lbd() <= 3) nbclausesbeforereduce += specialIncReduceDB; + // Useless :-) + if(ca[learnts.last()].lbd() <= 5) nbclausesbeforereduce += specialIncReduceDB; + + } + // Don't delete binary or locked clauses. From the rest, delete clauses from the first half + // Keep clauses which seem to be usefull (their lbd was reduce during this sequence) + + int limit = learnts.size() / 2; + + for(i = j = 0; i < learnts.size(); i++) { + Clause &c = ca[learnts[i]]; + if(c.lbd() > 2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(learnts[i]); + stats[nbRemovedClauses]++; + } + else { + if(!c.canBeDel()) limit++; //we keep c, so we can delete an other clause + c.setCanBeDel(true); // At the next step, c can be delete + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + checkGarbage(); +} + + +void Solver::removeSatisfied(vec &cs) { + + int i, j; + for(i = j = 0; i < cs.size(); i++) { + Clause &c = ca[cs[i]]; + + + if(satisfied(c)) if(c.getOneWatched()) + removeClause(cs[i], true); + else + removeClause(cs[i]); + else + cs[j++] = cs[i]; + } + cs.shrink(i - j); +} + + +void Solver::rebuildOrderHeap() { + vec vs; + for(Var v = 0; v < nVars(); v++) + if(decision[v] && value(v) == l_Undef) + vs.push(v); + order_heap.build(vs); + +} + + +/*_________________________________________________________________________________________________ +| +| simplify : [void] -> [bool] +| +| Description: +| Simplify the clause database according to the current top-level assigment. Currently, the only +| thing done here is the removal of satisfied clauses, but more things can be put here. +|________________________________________________________________________________________________@*/ +bool Solver::simplify() { + assert(decisionLevel() == 0); + + if(!ok) return ok = false; + else { + CRef cr = propagate(); + if(cr != CRef_Undef) { + return ok = false; + } + } + + + if(nAssigns() == simpDB_assigns || (simpDB_props > 0)) + return true; + + // Remove satisfied clauses: + removeSatisfied(learnts); + removeSatisfied(permanentLearnts); + removeSatisfied(unaryWatchedClauses); + if(remove_satisfied) // Can be turned off. + removeSatisfied(clauses); + checkGarbage(); + rebuildOrderHeap(); + + simpDB_assigns = nAssigns(); + simpDB_props = stats[clauses_literals] + stats[learnts_literals]; // (shouldn't depend on stats really, but it will do for now) + + return true; +} + + +void Solver::adaptSolver() { + bool adjusted = false; + bool reinit = false; + printf("c\nc Try to adapt solver strategies\nc \n"); + /* printf("c Adjusting solver for the SAT Race 2015 (alpha feature)\n"); + printf("c key successive Conflicts : %" PRIu64"\n",stats[noDecisionConflict]); + printf("c nb unary clauses learnt : %" PRIu64"\n",stats[nbUn]); + printf("c key avg dec per conflicts : %.2f\n", (float)decisions / (float)conflicts);*/ + float decpc = (float) decisions / (float) conflicts; + if(decpc <= 1.2) { + chanseokStrategy = true; + coLBDBound = 4; + glureduce = true; + adjusted = true; + printf("c Adjusting for low decision levels.\n"); + reinit = true; + firstReduceDB = 2000; + nbclausesbeforereduce = firstReduceDB; + curRestart = (conflicts / nbclausesbeforereduce) + 1; + incReduceDB = 0; + } + if(stats[noDecisionConflict] < 30000) { + luby_restart = true; + luby_restart_factor = 100; + + var_decay = 0.999; + max_var_decay = 0.999; + adjusted = true; + printf("c Adjusting for low successive conflicts.\n"); + } + if(stats[noDecisionConflict] > 54400) { + printf("c Adjusting for high successive conflicts.\n"); + chanseokStrategy = true; + glureduce = true; + coLBDBound = 3; + firstReduceDB = 30000; + var_decay = 0.99; + max_var_decay = 0.99; + randomize_on_restarts = 1; + adjusted = true; + } + if(stats[nbDL2] - stats[nbBin] > 20000) { + var_decay = 0.91; + max_var_decay = 0.91; + adjusted = true; + printf("c Adjusting for a very large number of true glue clauses found.\n"); + } + if(!adjusted) { + printf("c Nothing extreme in this problem, continue with glucose default strategies.\n"); + } + printf("c\n"); + if(adjusted) { // Let's reinitialize the glucose restart strategy counters + lbdQueue.fastclear(); + sumLBD = 0; + conflictsRestarts = 0; + } + + if(chanseokStrategy && adjusted) { + int moved = 0; + int i, j; + for(i = j = 0; i < learnts.size(); i++) { + Clause &c = ca[learnts[i]]; + if(c.lbd() <= coLBDBound) { + permanentLearnts.push(learnts[i]); + moved++; + } + else { + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + printf("c Activating Chanseok Strategy: moved %d clauses to the permanent set.\n", moved); + } + + if(reinit) { + assert(decisionLevel() == 0); + for(int i = 0; i < learnts.size(); i++) { + removeClause(learnts[i]); + } + learnts.shrink(learnts.size()); + checkGarbage(); +/* + order_heap.clear(); + for(int i=0;i [lbool] +| +| Description: +| Search for a model the specified number of conflicts. +| NOTE! Use negative value for 'nof_conflicts' indicate infinity. +| +| Output: +| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If +| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' +| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +|________________________________________________________________________________________________@*/ +lbool Solver::search(int nof_conflicts) { + assert(ok); + int backtrack_level; + int conflictC = 0; + vec learnt_clause, selectors; + unsigned int nblevels, szWithoutSelectors = 0; + bool blocked = false; + bool aDecisionWasMade = false; + + starts++; + for(; ;) { + if(decisionLevel() == 0) { // We import clauses FIXME: ensure that we will import clauses enventually (restart after some point) + parallelImportUnaryClauses(); + + if(parallelImportClauses()) + return l_False; + + } + CRef confl = propagate(); + + if(confl != CRef_Undef) { + newDescent = false; + if(parallelJobIsFinished()) + return l_Undef; + + if(!aDecisionWasMade) + stats[noDecisionConflict]++; + aDecisionWasMade = false; + + stats[sumDecisionLevels] += decisionLevel(); + stats[sumTrail] += trail.size(); + // CONFLICT + conflicts++; + conflictC++; + conflictsRestarts++; + if(conflicts % 5000 == 0 && var_decay < max_var_decay) + var_decay += 0.01; + + if(verbosity >= 1 && starts>0 && conflicts % verbEveryConflicts == 0) { + printf("c | %8d %7d %5d | %7d %8d %8d | %5d %8d %6d %8d | %6.3f %% |\n", + (int) starts, (int) stats[nbstopsrestarts], (int) (conflicts / starts), + (int) stats[dec_vars] - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int) stats[clauses_literals], + (int) stats[nbReduceDB], nLearnts(), (int) stats[nbDL2], (int) stats[nbRemovedClauses], progressEstimate() * 100); + } + if(decisionLevel() == 0) { + return l_False; + + } + if(adaptStrategies && conflicts == 100000) { + cancelUntil(0); + adaptSolver(); + adaptStrategies = false; + return l_Undef; + } + + trailQueue.push(trail.size()); + // BLOCK RESTART (CP 2012 paper) + if(conflictsRestarts > LOWER_BOUND_FOR_BLOCKING_RESTART && lbdQueue.isvalid() && trail.size() > R * trailQueue.getavg()) { + lbdQueue.fastclear(); + stats[nbstopsrestarts]++; + if(!blocked) { + stats[lastblockatrestart] = starts; + stats[nbstopsrestartssame]++; + blocked = true; + } + } + + learnt_clause.clear(); + selectors.clear(); + + analyze(confl, learnt_clause, selectors, backtrack_level, nblevels, szWithoutSelectors); + + lbdQueue.push(nblevels); + sumLBD += nblevels; + + cancelUntil(backtrack_level); + + if(certifiedUNSAT) { + if(vbyte) { + write_char('a'); + for(int i = 0; i < learnt_clause.size(); i++) + write_lit(2 * (var(learnt_clause[i]) + 1) + sign(learnt_clause[i])); + write_lit(0); + } + else { + for(int i = 0; i < learnt_clause.size(); i++) + fprintf(certifiedOutput, "%i ", (var(learnt_clause[i]) + 1) * + (-2 * sign(learnt_clause[i]) + 1)); + fprintf(certifiedOutput, "0\n"); + } + } + + + if(learnt_clause.size() == 1) { + uncheckedEnqueue(learnt_clause[0]); + stats[nbUn]++; + parallelExportUnaryClause(learnt_clause[0]); + } else { + CRef cr; + if(chanseokStrategy && nblevels <= coLBDBound) { + cr = ca.alloc(learnt_clause, false); + permanentLearnts.push(cr); + stats[nbPermanentLearnts]++; + } else { + cr = ca.alloc(learnt_clause, true); + ca[cr].setLBD(nblevels); + ca[cr].setOneWatched(false); + learnts.push(cr); + claBumpActivity(ca[cr]); + } +#ifdef INCREMENTAL + ca[cr].setSizeWithoutSelectors(szWithoutSelectors); +#endif + if(nblevels <= 2) { stats[nbDL2]++; } // stats + if(ca[cr].size() == 2) stats[nbBin]++; // stats + attachClause(cr); + lastLearntClause = cr; // Use in multithread (to hard to put inside ParallelSolver) + parallelExportClauseDuringSearch(ca[cr]); + uncheckedEnqueue(learnt_clause[0], cr); + + } + varDecayActivity(); + claDecayActivity(); + + + } else { + // Our dynamic restart, see the SAT09 competition compagnion paper + if((luby_restart && nof_conflicts <= conflictC) || + (!luby_restart && (lbdQueue.isvalid() && ((lbdQueue.getavg() * K) > (sumLBD / conflictsRestarts))))) { + lbdQueue.fastclear(); + progress_estimate = progressEstimate(); + int bt = 0; +#ifdef INCREMENTAL + if(incremental) // DO NOT BACKTRACK UNTIL 0.. USELESS + bt = (decisionLevel() firstReduceDB) || + (glureduce && conflicts >= ((unsigned int) curRestart * nbclausesbeforereduce))) { + + if(learnts.size() > 0) { + curRestart = (conflicts / nbclausesbeforereduce) + 1; + reduceDB(); + if(!panicModeIsEnabled()) + nbclausesbeforereduce += incReduceDB; + } + } + + lastLearntClause = CRef_Undef; + Lit next = lit_Undef; + while(decisionLevel() < assumptions.size()) { + // Perform user provided assumption: + Lit p = assumptions[decisionLevel()]; + if(value(p) == l_True) { + // Dummy decision level: + newDecisionLevel(); + } else if(value(p) == l_False) { + analyzeFinal(~p, conflict); + return l_False; + } else { + next = p; + break; + } + } + + if(next == lit_Undef) { + // New variable decision: + decisions++; + next = pickBranchLit(); + if(next == lit_Undef) { + printf("c last restart ## conflicts : %d %d \n", conflictC, decisionLevel()); + // Model found: + return l_True; + } + } + + // Increase decision level and enqueue 'next' + aDecisionWasMade = true; + newDecisionLevel(); + uncheckedEnqueue(next); + } + } +} + + +double Solver::progressEstimate() const { + double progress = 0; + double F = 1.0 / nVars(); + + for(int i = 0; i <= decisionLevel(); i++) { + int beg = i == 0 ? 0 : trail_lim[i - 1]; + int end = i == decisionLevel() ? trail.size() : trail_lim[i]; + progress += pow(F, i) * (end - beg); + } + + return progress / nVars(); +} + + +void Solver::printIncrementalStats() { + + printf("c---------- Glucose Stats -------------------------\n"); + printf("c restarts : %" + PRIu64 + "\n", starts); + printf("c nb ReduceDB : %" + PRIu64 + "\n", stats[nbReduceDB]); + printf("c nb removed Clauses : %" + PRIu64 + "\n", stats[nbRemovedClauses]); + printf("c nb learnts DL2 : %" + PRIu64 + "\n", stats[nbDL2]); + printf("c nb learnts size 2 : %" + PRIu64 + "\n", stats[nbBin]); + printf("c nb learnts size 1 : %" + PRIu64 + "\n", stats[nbUn]); + + printf("c conflicts : %" + PRIu64 + "\n", conflicts); + printf("c decisions : %" + PRIu64 + "\n", decisions); + printf("c propagations : %" + PRIu64 + "\n", propagations); + + printf("\nc SAT Calls : %d in %g seconds\n", nbSatCalls, totalTime4Sat); + printf("c UNSAT Calls : %d in %g seconds\n", nbUnsatCalls, totalTime4Unsat); + + printf("c--------------------------------------------------\n"); +} + + +double Solver::luby(double y, int x) { + + // Find the finite subsequence that contains index 'x', and the + // size of that subsequence: + int size, seq; + for(size = 1, seq = 0; size < x + 1; seq++, size = 2 * size + 1); + + while(size - 1 != x) { + size = (size - 1) >> 1; + seq--; + x = x % size; + } + + return pow(y, seq); +} + + +// NOTE: assumptions passed in member-variable 'assumptions'. + +lbool Solver::solve_(bool do_simp, bool turn_off_simp) // Parameters are useless in core but useful for SimpSolver.... +{ + + if(incremental && certifiedUNSAT) { + printf("Can not use incremental and certified unsat in the same time\n"); + exit(-1); + } + + model.clear(); + conflict.clear(); + if(!ok) return l_False; + double curTime = cpuTime(); + + solves++; + + + lbool status = l_Undef; + if(!incremental && verbosity >= 1) { + printf("c ========================================[ MAGIC CONSTANTS ]==============================================\n"); + printf("c | Constants are supposed to work well together :-) |\n"); + printf("c | however, if you find better choices, please let us known... |\n"); + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + if(adaptStrategies) { + printf("c | Adapt dynamically the solver after 100000 conflicts (restarts, reduction strategies...) |\n"); + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + } + printf("c | | | |\n"); + printf("c | - Restarts: | - Reduce Clause DB: | - Minimize Asserting: |\n"); + if(chanseokStrategy) { + printf("c | * LBD Queue : %6d | chanseok Strategy | * size < %3d |\n", lbdQueue.maxSize(), + lbSizeMinimizingClause); + printf("c | * Trail Queue : %6d | * learnts size : %6d | * lbd < %3d |\n", trailQueue.maxSize(), + firstReduceDB, lbLBDMinimizingClause); + printf("c | * K : %6.2f | * Bound LBD : %6d | |\n", K, coLBDBound); + printf("c | * R : %6.2f | * Protected : (lbd)< %2d | |\n", R, lbLBDFrozenClause); + } else { + printf("c | * LBD Queue : %6d | * First : %6d | * size < %3d |\n", lbdQueue.maxSize(), + nbclausesbeforereduce, lbSizeMinimizingClause); + printf("c | * Trail Queue : %6d | * Inc : %6d | * lbd < %3d |\n", trailQueue.maxSize(), incReduceDB, + lbLBDMinimizingClause); + printf("c | * K : %6.2f | * Special : %6d | |\n", K, specialIncReduceDB); + printf("c | * R : %6.2f | * Protected : (lbd)< %2d | |\n", R, lbLBDFrozenClause); + } + printf("c | | | |\n"); + printf("c ==================================[ Search Statistics (every %6d conflicts) ]=========================\n", verbEveryConflicts); + printf("c | |\n"); + + printf("c | RESTARTS | ORIGINAL | LEARNT | Progress |\n"); + printf("c | NB Blocked Avg Cfc | Vars Clauses Literals | Red Learnts LBD2 Removed | |\n"); + printf("c =========================================================================================================\n"); + } + + // Search: + int curr_restarts = 0; + while(status == l_Undef) { + status = search( + luby_restart ? luby(restart_inc, curr_restarts) * luby_restart_factor : 0); // the parameter is useless in glucose, kept to allow modifications + + if(!withinBudget()) break; + curr_restarts++; + } + + if(!incremental && verbosity >= 1) + printf("c =========================================================================================================\n"); + + if(certifiedUNSAT) { // Want certified output + if(status == l_False) { + if(vbyte) { + write_char('a'); + write_lit(0); + } + else { + fprintf(certifiedOutput, "0\n"); + } + } + fclose(certifiedOutput); + } + + + if(status == l_True) { + // Extend & copy model: + model.growTo(nVars()); + for(int i = 0; i < nVars(); i++) model[i] = value(i); + } else if(status == l_False && conflict.size() == 0) + ok = false; + + + cancelUntil(0); + + + double finalTime = cpuTime(); + if(status == l_True) { + nbSatCalls++; + totalTime4Sat += (finalTime - curTime); + } + if(status == l_False) { + nbUnsatCalls++; + totalTime4Unsat += (finalTime - curTime); + } + + + return status; + +} + + + + + +//================================================================================================= +// Writing CNF to DIMACS: +// +// FIXME: this needs to be rewritten completely. + +static Var mapVar(Var x, vec &map, Var &max) { + if(map.size() <= x || map[x] == -1) { + map.growTo(x + 1, -1); + map[x] = max++; + } + return map[x]; +} + + +void Solver::toDimacs(FILE *f, Clause &c, vec &map, Var &max) { + if(satisfied(c)) return; + + for(int i = 0; i < c.size(); i++) + if(value(c[i]) != l_False) + fprintf(f, "%s%d ", sign(c[i]) ? "-" : "", mapVar(var(c[i]), map, max) + 1); + fprintf(f, "0\n"); +} + + +void Solver::toDimacs(const char *file, const vec &assumps) { + FILE *f = fopen(file, "wr"); + if(f == NULL) + fprintf(stderr, "could not open file %s\n", file), exit(1); + toDimacs(f, assumps); + fclose(f); +} + + +void Solver::toDimacs(FILE *f, const vec &assumps) { + // Handle case when solver is in contradictory state: + if(!ok) { + fprintf(f, "p cnf 1 2\n1 0\n-1 0\n"); + return; + } + + vec map; + Var max = 0; + + // Cannot use removeClauses here because it is not safe + // to deallocate them at this point. Could be improved. + int cnt = 0; + for(int i = 0; i < clauses.size(); i++) + if(!satisfied(ca[clauses[i]])) + cnt++; + + for(int i = 0; i < clauses.size(); i++) + if(!satisfied(ca[clauses[i]])) { + Clause &c = ca[clauses[i]]; + for(int j = 0; j < c.size(); j++) + if(value(c[j]) != l_False) + mapVar(var(c[j]), map, max); + } + + // Assumptions are added as unit clauses: + cnt += assumptions.size(); + + fprintf(f, "p cnf %d %d\n", max, cnt); + + for(int i = 0; i < assumptions.size(); i++) { + assert(value(assumptions[i]) != l_False); + fprintf(f, "%s%d 0\n", sign(assumptions[i]) ? "-" : "", mapVar(var(assumptions[i]), map, max) + 1); + } + + for(int i = 0; i < clauses.size(); i++) + toDimacs(f, ca[clauses[i]], map, max); + + if(verbosity > 0) + printf("Wrote %d clauses with %d variables.\n", cnt, max); +} + + +//================================================================================================= +// Garbage Collection methods: + +void Solver::relocAll(ClauseAllocator &to) { + // All watchers: + // for (int i = 0; i < watches.size(); i++) + watches.cleanAll(); + watchesBin.cleanAll(); + unaryWatches.cleanAll(); + for(int v = 0; v < nVars(); v++) + for(int s = 0; s < 2; s++) { + Lit p = mkLit(v, s); + // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); + vec &ws = watches[p]; + for(int j = 0; j < ws.size(); j++) + ca.reloc(ws[j].cref, to); + vec &ws2 = watchesBin[p]; + for(int j = 0; j < ws2.size(); j++) + ca.reloc(ws2[j].cref, to); + vec &ws3 = unaryWatches[p]; + for(int j = 0; j < ws3.size(); j++) + ca.reloc(ws3[j].cref, to); + } + + // All reasons: + // + for(int i = 0; i < trail.size(); i++) { + Var v = var(trail[i]); + + if(reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) + ca.reloc(vardata[v].reason, to); + } + + // All learnt: + // + for(int i = 0; i < learnts.size(); i++) + ca.reloc(learnts[i], to); + + for(int i = 0; i < permanentLearnts.size(); i++) + ca.reloc(permanentLearnts[i], to); + + // All original: + // + for(int i = 0; i < clauses.size(); i++) + ca.reloc(clauses[i], to); + + for(int i = 0; i < unaryWatchedClauses.size(); i++) + ca.reloc(unaryWatchedClauses[i], to); +} + + +void Solver::garbageCollect() { + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + relocAll(to); + if(verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, to.size() * ClauseAllocator::Unit_Size); + to.moveTo(ca); +} + +//-------------------------------------------------------------- +// Functions related to MultiThread. +// Useless in case of single core solver (aka original glucose) +// Keep them empty if you just use core solver +//-------------------------------------------------------------- + +bool Solver::panicModeIsEnabled() { + return false; +} + + +void Solver::parallelImportUnaryClauses() { +} + + +bool Solver::parallelImportClauses() { + return false; +} + + +void Solver::parallelExportUnaryClause(Lit p) { +} + + +void Solver::parallelExportClauseDuringSearch(Clause &c) { +} + + +bool Solver::parallelJobIsFinished() { + // Parallel: another job has finished let's quit + return false; +} + + +void Solver::parallelImportClauseDuringConflictAnalysis(Clause &c, CRef confl) { +} diff --git a/libs/mugen/glucose-syrup-4.1/core/Solver.h b/libs/mugen/glucose-syrup-4.1/core/Solver.h new file mode 100644 index 000000000..54335ffd2 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/core/Solver.h @@ -0,0 +1,650 @@ +/***************************************************************************************[Solver.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#ifndef Glucose_Solver_h +#define Glucose_Solver_h + +#include "mtl/Heap.h" +#include "mtl/Alg.h" +#include "utils/Options.h" +#include "core/SolverTypes.h" +#include "core/BoundedQueue.h" +#include "core/Constants.h" +#include "mtl/Clone.h" +#include "core/SolverStats.h" + + +namespace Glucose { +// Core stats + +enum CoreStats { + sumResSeen, + sumRes, + sumTrail, + nbPromoted, + originalClausesSeen, + sumDecisionLevels, + nbPermanentLearnts, + nbRemovedClauses, + nbRemovedUnaryWatchedClauses, + nbReducedClauses, + nbDL2, + nbBin, + nbUn, + nbReduceDB, + rnd_decisions, + nbstopsrestarts, + nbstopsrestartssame, + lastblockatrestart, + dec_vars, + clauses_literals, + learnts_literals, + max_literals, + tot_literals, + noDecisionConflict +} ; + +#define coreStatsSize 24 +//================================================================================================= +// Solver -- the main class: + +class Solver : public Clone { + + friend class SolverConfiguration; + +public: + + // Constructor/Destructor: + // + Solver(); + Solver(const Solver &s); + + virtual ~Solver(); + + /** + * Clone function + */ + virtual Clone* clone() const { + return new Solver(*this); + } + + // Problem specification: + // + virtual Var newVar (bool polarity = true, bool dvar = true); // Add a new variable with parameters specifying variable mode. + bool addClause (const vec& ps); // Add a clause to the solver. + bool addEmptyClause(); // Add the empty clause, making the solver contradictory. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + virtual bool addClause_( vec& ps); // Add a clause to the solver without making superflous internal copy. Will + // change the passed vector 'ps'. + // Solving: + // + bool simplify (); // Removes already satisfied clauses. + bool solve (const vec& assumps); // Search for a model that respects a given set of assumptions. + lbool solveLimited (const vec& assumps); // Search for a model that respects a given set of assumptions (With resource constraints). + bool solve (); // Search without assumptions. + bool solve (Lit p); // Search for a model that respects a single assumption. + bool solve (Lit p, Lit q); // Search for a model that respects two assumptions. + bool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. + bool okay () const; // FALSE means solver is in a conflicting state + + // Convenience versions of 'toDimacs()': + void toDimacs (FILE* f, const vec& assumps); // Write CNF to file in DIMACS-format. + void toDimacs (const char *file, const vec& assumps); + void toDimacs (FILE* f, Clause& c, vec& map, Var& max); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); + + // Display clauses and literals + void printLit(Lit l); + void printClause(CRef c); + void printInitialClause(CRef c); + + // Variable mode: + // + void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. + void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. + + // Read state: + // + lbool value (Var x) const; // The current value of a variable. + lbool value (Lit p) const; // The current value of a literal. + lbool modelValue (Var x) const; // The value of a variable in the last model. The last call to solve must have been satisfiable. + lbool modelValue (Lit p) const; // The value of a literal in the last model. The last call to solve must have been satisfiable. + int nAssigns () const; // The current number of assigned literals. + int nClauses () const; // The current number of original clauses. + int nLearnts () const; // The current number of learnt clauses. + int nVars () const; // The current number of variables. + int nFreeVars () ; + + inline char valuePhase(Var v) {return polarity[v];} + + // Incremental mode + void setIncrementalMode(); + void initNbInitialVars(int nb); + void printIncrementalStats(); + bool isIncremental(); + // Resource contraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); + void budgetOff(); + void interrupt(); // Trigger a (potentially asynchronous) interruption of the solver. + void clearInterrupt(); // Clear interrupt indicator flag. + + // Memory managment: + // + virtual void garbageCollect(); + void checkGarbage(double gf); + void checkGarbage(); + + // Extra results: (read-only member variable) + // + vec model; // If problem is satisfiable, this vector contains the model (if any). + vec conflict; // If problem is unsatisfiable (possibly under assumptions), + // this vector represent the final conflict clause expressed in the assumptions. + + // Mode of operation: + // + int verbosity; + int verbEveryConflicts; + int showModel; + + // Constants For restarts + double K; + double R; + double sizeLBDQueue; + double sizeTrailQueue; + + // Constants for reduce DB + int firstReduceDB; + int incReduceDB; + int specialIncReduceDB; + unsigned int lbLBDFrozenClause; + bool chanseokStrategy; + int coLBDBound; // Keep all learnts with lbd<=coLBDBound + // Constant for reducing clause + int lbSizeMinimizingClause; + unsigned int lbLBDMinimizingClause; + + // Constant for heuristic + double var_decay; + double max_var_decay; + double clause_decay; + double random_var_freq; + double random_seed; + int ccmin_mode; // Controls conflict clause minimization (0=none, 1=basic, 2=deep). + int phase_saving; // Controls the level of phase saving (0=none, 1=limited, 2=full). + bool rnd_pol; // Use random polarities for branching heuristics. + bool rnd_init_act; // Initialize variable activities with a small random value. + bool randomizeFirstDescent; // the first decisions (until first cnflict) are made randomly + // Useful for syrup! + + // Constant for Memory managment + double garbage_frac; // The fraction of wasted memory allowed before a garbage collection is triggered. + + // Certified UNSAT ( Thanks to Marijn Heule + // New in 2016 : proof in DRAT format, possibility to use binary output + FILE* certifiedOutput; + bool certifiedUNSAT; + bool vbyte; + + void write_char (unsigned char c); + void write_lit (int n); + + + // Panic mode. + // Save memory + uint32_t panicModeLastRemoved, panicModeLastRemovedShared; + + bool useUnaryWatched; // Enable unary watched literals + bool promoteOneWatchedClause; // One watched clauses are promotted to two watched clauses if found empty + + // Functions useful for multithread solving + // Useless in the sequential case + // Overide in ParallelSolver + virtual void parallelImportClauseDuringConflictAnalysis(Clause &c,CRef confl); + virtual bool parallelImportClauses(); // true if the empty clause was received + virtual void parallelImportUnaryClauses(); + virtual void parallelExportUnaryClause(Lit p); + virtual void parallelExportClauseDuringSearch(Clause &c); + virtual bool parallelJobIsFinished(); + virtual bool panicModeIsEnabled(); + + + double luby(double y, int x); + + // Statistics + vec stats; + + // Important stats completely related to search. Keep here + uint64_t solves,starts,decisions,propagations,conflicts,conflictsRestarts; + +protected: + + long curRestart; + + // Alpha variables + bool glureduce; + uint32_t restart_inc; + bool luby_restart; + bool adaptStrategies; + uint32_t luby_restart_factor; + bool randomize_on_restarts, fixed_randomize_on_restarts, newDescent; + uint32_t randomDescentAssignments; + bool forceUnsatOnNewDescent; + // Helper structures: + // + struct VarData { CRef reason; int level; }; + static inline VarData mkVarData(CRef cr, int l){ VarData d = {cr, l}; return d; } + + struct Watcher { + CRef cref; + Lit blocker; + Watcher(CRef cr, Lit p) : cref(cr), blocker(p) {} + bool operator==(const Watcher& w) const { return cref == w.cref; } + bool operator!=(const Watcher& w) const { return cref != w.cref; } +/* Watcher &operator=(Watcher w) { + this->cref = w.cref; + this->blocker = w.blocker; + return *this; + } +*/ + }; + + struct WatcherDeleted + { + const ClauseAllocator& ca; + WatcherDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const Watcher& w) const { return ca[w.cref].mark() == 1; } + }; + + struct VarOrderLt { + const vec& activity; + bool operator () (Var x, Var y) const { return activity[x] > activity[y]; } + VarOrderLt(const vec& act) : activity(act) { } + }; + + + // Solver state: + // + int lastIndexRed; + bool ok; // If FALSE, the constraints are already unsatisfiable. No part of the solver state may be used! + double cla_inc; // Amount to bump next clause with. + vec activity; // A heuristic measurement of the activity of a variable. + double var_inc; // Amount to bump next variable with. + OccLists, WatcherDeleted> + watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + OccLists, WatcherDeleted> + watchesBin; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). + OccLists, WatcherDeleted> + unaryWatches; // Unary watch scheme (clauses are seen when they become empty + vec clauses; // List of problem clauses. + vec learnts; // List of learnt clauses. + vec permanentLearnts; // The list of learnts clauses kept permanently + vec unaryWatchedClauses; // List of imported clauses (after the purgatory) // TODO put inside ParallelSolver + + vec assigns; // The current assignments. + vec polarity; // The preferred polarity of each variable. + vec forceUNSAT; + void bumpForceUNSAT(Lit q); // Handles the forces + + vec decision; // Declares if a variable is eligible for selection in the decision heuristic. + vec trail; // Assignment stack; stores all assigments made in the order they were made. + vec nbpos; + vec trail_lim; // Separator indices for different decision levels in 'trail'. + vec vardata; // Stores reason and level for each variable. + int qhead; // Head of queue (as index into the trail -- no more explicit propagation queue in MiniSat). + int simpDB_assigns; // Number of top-level assignments since last execution of 'simplify()'. + int64_t simpDB_props; // Remaining number of propagations that must be made before next execution of 'simplify()'. + vec assumptions; // Current set of assumptions provided to solve by the user. + Heap order_heap; // A priority queue of variables ordered with respect to the variable activity. + double progress_estimate;// Set by 'search()'. + bool remove_satisfied; // Indicates whether possibly inefficient linear scan for satisfied clauses should be performed in 'simplify'. + vec permDiff; // permDiff[var] contains the current conflict number... Used to count the number of LBD + + + // UPDATEVARACTIVITY trick (see competition'09 companion paper) + vec lastDecisionLevel; + + ClauseAllocator ca; + + int nbclausesbeforereduce; // To know when it is time to reduce clause database + + // Used for restart strategies + bqueue trailQueue,lbdQueue; // Bounded queues for restarts. + float sumLBD; // used to compute the global average of LBD. Restarts... + int sumAssumptions; + CRef lastLearntClause; + + + // Temporaries (to reduce allocation overhead). Each variable is prefixed by the method in which it is + // used, exept 'seen' wich is used in several places. + // + vec seen; + vec analyze_stack; + vec analyze_toclear; + vec add_tmp; + unsigned int MYFLAG; + + // Initial reduceDB strategy + double max_learnts; + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + + // Resource contraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. + bool asynch_interrupt; + + // Variables added for incremental mode + int incremental; // Use incremental SAT Solver + int nbVarsInitialFormula; // nb VAR in formula without assumptions (incremental SAT) + double totalTime4Sat,totalTime4Unsat; + int nbSatCalls,nbUnsatCalls; + vec assumptionPositions,initialPositions; + + + // Main internal methods: + // + void insertVarOrder (Var x); // Insert a variable in the decision order priority queue. + Lit pickBranchLit (); // Return the next decision variable. + void newDecisionLevel (); // Begins a new decision level. + void uncheckedEnqueue (Lit p, CRef from = CRef_Undef); // Enqueue a literal. Assumes value of literal is undefined. + bool enqueue (Lit p, CRef from = CRef_Undef); // Test if fact 'p' contradicts current state, enqueue otherwise. + CRef propagate (); // Perform unit propagation. Returns possibly conflicting clause. + CRef propagateUnaryWatches(Lit p); // Perform propagation on unary watches of p, can find only conflicts + void cancelUntil (int level); // Backtrack until a certain level. + void analyze (CRef confl, vec& out_learnt, vec & selectors, int& out_btlevel,unsigned int &nblevels,unsigned int &szWithoutSelectors); // (bt = backtrack) + void analyzeFinal (Lit p, vec& out_conflict); // COULD THIS BE IMPLEMENTED BY THE ORDINARIY "analyze" BY SOME REASONABLE GENERALIZATION? + bool litRedundant (Lit p, uint32_t abstract_levels); // (helper method for 'analyze()') + lbool search (int nof_conflicts); // Search for a given number of conflicts. + virtual lbool solve_ (bool do_simp = true, bool turn_off_simp = false); // Main solve method (assumptions given in 'assumptions'). + virtual void reduceDB (); // Reduce the set of learnt clauses. + void removeSatisfied (vec& cs); // Shrink 'cs' to contain only non-satisfied clauses. + void rebuildOrderHeap (); + + void adaptSolver(); // Adapt solver strategies + + // Maintaining Variable/Clause activity: + // + void varDecayActivity (); // Decay all variables with the specified factor. Implemented by increasing the 'bump' value instead. + void varBumpActivity (Var v, double inc); // Increase a variable with the current 'bump' value. + void varBumpActivity (Var v); // Increase a variable with the current 'bump' value. + void claDecayActivity (); // Decay all clauses with the specified factor. Implemented by increasing the 'bump' value instead. + void claBumpActivity (Clause& c); // Increase a clause with the current 'bump' value. + + // Operations on clauses: + // + void attachClause (CRef cr); // Attach a clause to watcher lists. + void detachClause (CRef cr, bool strict = false); // Detach a clause to watcher lists. + void detachClausePurgatory(CRef cr, bool strict = false); + void attachClausePurgatory(CRef cr); + void removeClause (CRef cr, bool inPurgatory = false); // Detach and free a clause. + bool locked (const Clause& c) const; // Returns TRUE if a clause is a reason for some implication in the current state. + bool satisfied (const Clause& c) const; // Returns TRUE if a clause is satisfied in the current state. + + template unsigned int computeLBD(const T & lits,int end=-1); + void minimisationWithBinaryResolution(vec &out_learnt); + + virtual void relocAll (ClauseAllocator& to); + + // Misc: + // + int decisionLevel () const; // Gives the current decisionlevel. + uint32_t abstractLevel (Var x) const; // Used to represent an abstraction of sets of decision levels. + CRef reason (Var x) const; + int level (Var x) const; + double progressEstimate () const; // DELETE THIS ?? IT'S NOT VERY USEFUL ... + bool withinBudget () const; + inline bool isSelector(Var v) {return (incremental && v>nbVarsInitialFormula);} + + // Static helpers: + // + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } +}; + + +//================================================================================================= +// Implementation of inline methods: + +inline CRef Solver::reason(Var x) const { return vardata[x].reason; } +inline int Solver::level (Var x) const { return vardata[x].level; } + +inline void Solver::insertVarOrder(Var x) { + if (!order_heap.inHeap(x) && decision[x]) order_heap.insert(x); } + +inline void Solver::varDecayActivity() { var_inc *= (1 / var_decay); } +inline void Solver::varBumpActivity(Var v) { varBumpActivity(v, var_inc); } +inline void Solver::varBumpActivity(Var v, double inc) { + if ( (activity[v] += inc) > 1e100 ) { + // Rescale: + for (int i = 0; i < nVars(); i++) + activity[i] *= 1e-100; + var_inc *= 1e-100; } + + // Update order_heap with respect to new activity: + if (order_heap.inHeap(v)) + order_heap.decrease(v); } + +inline void Solver::claDecayActivity() { cla_inc *= (1 / clause_decay); } +inline void Solver::claBumpActivity (Clause& c) { + if ( (c.activity() += cla_inc) > 1e20 ) { + // Rescale: + for (int i = 0; i < learnts.size(); i++) + ca[learnts[i]].activity() *= 1e-20; + cla_inc *= 1e-20; } } + +inline void Solver::checkGarbage(void){ return checkGarbage(garbage_frac); } +inline void Solver::checkGarbage(double gf){ + if (ca.wasted() > ca.size() * gf) + garbageCollect(); } + +// NOTE: enqueue does not set the ok flag! (only public methods do) +inline bool Solver::enqueue (Lit p, CRef from) { return value(p) != l_Undef ? value(p) != l_False : (uncheckedEnqueue(p, from), true); } +inline bool Solver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool Solver::addEmptyClause () { add_tmp.clear(); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool Solver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } + inline bool Solver::locked (const Clause& c) const { + if(c.size()>2) + return value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c; + return + (value(c[0]) == l_True && reason(var(c[0])) != CRef_Undef && ca.lea(reason(var(c[0]))) == &c) + || + (value(c[1]) == l_True && reason(var(c[1])) != CRef_Undef && ca.lea(reason(var(c[1]))) == &c); + } +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); } + +inline int Solver::decisionLevel () const { return trail_lim.size(); } +inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } +inline lbool Solver::value (Var x) const { return assigns[x]; } +inline lbool Solver::value (Lit p) const { return assigns[var(p)] ^ sign(p); } +inline lbool Solver::modelValue (Var x) const { return model[x]; } +inline lbool Solver::modelValue (Lit p) const { return model[var(p)] ^ sign(p); } +inline int Solver::nAssigns () const { return trail.size(); } +inline int Solver::nClauses () const { return clauses.size(); } +inline int Solver::nLearnts () const { return learnts.size(); } +inline int Solver::nVars () const { return vardata.size(); } +inline int Solver::nFreeVars () { + int a = stats[dec_vars]; + return (int)(a) - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } +inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } +inline void Solver::setDecisionVar(Var v, bool b) +{ + if ( b && !decision[v]) stats[dec_vars]++; + else if (!b && decision[v]) stats[dec_vars]--; + + decision[v] = b; + insertVarOrder(v); +} +inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } +inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } +inline void Solver::interrupt(){ asynch_interrupt = true; } +inline void Solver::clearInterrupt(){ asynch_interrupt = false; } +inline void Solver::budgetOff(){ conflict_budget = propagation_budget = -1; } +inline bool Solver::withinBudget() const { + return !asynch_interrupt && + (conflict_budget < 0 || conflicts < (uint64_t)conflict_budget) && + (propagation_budget < 0 || propagations < (uint64_t)propagation_budget); } + +// FIXME: after the introduction of asynchronous interrruptions the solve-versions that return a +// pure bool do not give a safe interface. Either interrupts must be possible to turn off here, or +// all calls to solve must return an 'lbool'. I'm not yet sure which I prefer. +inline bool Solver::solve () { budgetOff(); assumptions.clear(); return solve_() == l_True; } +inline bool Solver::solve (Lit p) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_() == l_True; } +inline bool Solver::solve (Lit p, Lit q, Lit r) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_() == l_True; } +inline bool Solver::solve (const vec& assumps){ budgetOff(); assumps.copyTo(assumptions); return solve_() == l_True; } +inline lbool Solver::solveLimited (const vec& assumps){ assumps.copyTo(assumptions); return solve_(); } +inline bool Solver::okay () const { return ok; } + +inline void Solver::toDimacs (const char* file){ vec as; toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p){ vec as; as.push(p); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q){ vec as; as.push(p); as.push(q); toDimacs(file, as); } +inline void Solver::toDimacs (const char* file, Lit p, Lit q, Lit r){ vec as; as.push(p); as.push(q); as.push(r); toDimacs(file, as); } + + + +//================================================================================================= +// Debug etc: + + +inline void Solver::printLit(Lit l) +{ + printf("%s%d:%c", sign(l) ? "-" : "", var(l)+1, value(l) == l_True ? '1' : (value(l) == l_False ? '0' : 'X')); +} + + +inline void Solver::printClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + printLit(c[i]); + printf(" "); + } +} + +inline void Solver::printInitialClause(CRef cr) +{ + Clause &c = ca[cr]; + for (int i = 0; i < c.size(); i++){ + if(!isSelector(var(c[i]))) { + printLit(c[i]); + printf(" "); + } + } +} + +//================================================================================================= +struct reduceDBAct_lt { + ClauseAllocator& ca; + + reduceDBAct_lt(ClauseAllocator& ca_) : ca(ca_) { + } + + bool operator()(CRef x, CRef y) { + + // Main criteria... Like in MiniSat we keep all binary clauses + if (ca[x].size() > 2 && ca[y].size() == 2) return 1; + + if (ca[y].size() > 2 && ca[x].size() == 2) return 0; + if (ca[x].size() == 2 && ca[y].size() == 2) return 0; + + return ca[x].activity() < ca[y].activity(); + } +}; + +struct reduceDB_lt { + ClauseAllocator& ca; + + reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) { + } + + bool operator()(CRef x, CRef y) { + + // Main criteria... Like in MiniSat we keep all binary clauses + if (ca[x].size() > 2 && ca[y].size() == 2) return 1; + + if (ca[y].size() > 2 && ca[x].size() == 2) return 0; + if (ca[x].size() == 2 && ca[y].size() == 2) return 0; + + // Second one based on literal block distance + if (ca[x].lbd() > ca[y].lbd()) return 1; + if (ca[x].lbd() < ca[y].lbd()) return 0; + + + // Finally we can use old activity or size, we choose the last one + return ca[x].activity() < ca[y].activity(); + //return x->size() < y->size(); + + //return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + } +}; + + +} + + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/core/SolverStats.h b/libs/mugen/glucose-syrup-4.1/core/SolverStats.h new file mode 100644 index 000000000..90ad632ac --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/core/SolverStats.h @@ -0,0 +1,99 @@ +/***************************************************************************************[Solver.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#ifndef SOLVERSTATS_H +#define SOLVERSTATS_H + +#include "mtl/Map.h" +#include +namespace Glucose { + + class SolverStats { + protected: + Map map; + + public: + + SolverStats(std::string all[],int sz) : map() { + addStats(all,sz); + } + + void addStats(std::string names[],int sz) { + for(int i = 0;i map[name]) + map[name] = val; + } + + void minimize(const std::string name,uint64_t val) { + if(val < map[name]) + map[name] = val; + } + +}; + +} + +#endif /* SOLVERSTATS_H */ + diff --git a/libs/mugen/glucose-syrup-4.1/core/SolverTypes.h b/libs/mugen/glucose-syrup-4.1/core/SolverTypes.h new file mode 100644 index 000000000..422ec78be --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/core/SolverTypes.h @@ -0,0 +1,519 @@ +/***************************************************************************************[SolverTypes.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + + +#ifndef Glucose_SolverTypes_h +#define Glucose_SolverTypes_h + +#include +#include +#include + +#include "mtl/IntTypes.h" +#include "mtl/Alg.h" +#include "mtl/Vec.h" +#include "mtl/Map.h" +#include "mtl/Alloc.h" + + +namespace Glucose { + +//================================================================================================= +// Variables, literals, lifted booleans, clauses: + + +// NOTE! Variables are just integers. No abstraction here. They should be chosen from 0..N, +// so that they can be used as array indices. + +typedef int Var; +#define var_Undef (-1) + + +struct Lit { + int x; + + // Use this as a constructor: + friend Lit mkLit(Var var, bool sign); + + bool operator == (Lit p) const { return x == p.x; } + bool operator != (Lit p) const { return x != p.x; } + bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. +}; + + +inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } +inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } +inline bool sign (Lit p) { return p.x & 1; } +inline int var (Lit p) { return p.x >> 1; } + +// Mapping Literals to and from compact integers suitable for array indexing: +inline int toInt (Var v) { return v; } +inline int toInt (Lit p) { return p.x; } +inline Lit toLit (int i) { Lit p; p.x = i; return p; } + +//const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. +//const Lit lit_Error = mkLit(var_Undef, true ); // } + +const Lit lit_Undef = { -2 }; // }- Useful special constants. +const Lit lit_Error = { -1 }; // } + + +//================================================================================================= +// Lifted booleans: +// +// NOTE: this implementation is optimized for the case when comparisons between values are mostly +// between one variable and one constant. Some care had to be taken to make sure that gcc +// does enough constant propagation to produce sensible code, and this appears to be somewhat +// fragile unfortunately. + +#define l_True (Glucose::lbool((uint8_t)0)) // gcc does not do constant propagation if these are real constants. +#define l_False (Glucose::lbool((uint8_t)1)) +#define l_Undef (Glucose::lbool((uint8_t)2)) + +class lbool { + uint8_t value; + +public: + explicit lbool(uint8_t v) : value(v) { } + + lbool() : value(0) { } + explicit lbool(bool x) : value(!x) { } + + bool operator == (lbool b) const { return ((b.value&2) & (value&2)) | (!(b.value&2)&(value == b.value)); } + bool operator != (lbool b) const { return !(*this == b); } + lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } + + lbool operator && (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); } + + lbool operator || (lbool b) const { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xFCFCF400 >> sel) & 3; + return lbool(v); } + + friend int toInt (lbool l); + friend lbool toLbool(int v); +}; +inline int toInt (lbool l) { return l.value; } +inline lbool toLbool(int v) { return lbool((uint8_t)v); } + +//================================================================================================= +// Clause -- a simple class for representing a clause: + +class Clause; +typedef RegionAllocator::Ref CRef; + +#define BITS_LBD 20 +#ifdef INCREMENTAL + #define BITS_SIZEWITHOUTSEL 19 +#endif +#define BITS_REALSIZE 32 +class Clause { + struct { + unsigned mark : 2; + unsigned learnt : 1; + unsigned canbedel : 1; + unsigned extra_size : 2; // extra size (end of 32bits) 0..3 + unsigned seen : 1; + unsigned reloced : 1; + unsigned exported : 2; // Values to keep track of the clause status for exportations + unsigned oneWatched : 1; + unsigned lbd : BITS_LBD; + + unsigned size : BITS_REALSIZE; + +#ifdef INCREMENTAL + unsigned szWithoutSelectors : BITS_SIZEWITHOUTSEL; +#endif + } header; + + union { Lit lit; float act; uint32_t abs; CRef rel; } data[0]; + + friend class ClauseAllocator; + + // NOTE: This constructor cannot be used directly (doesn't allocate enough memory). + template + Clause(const V& ps, int _extra_size, bool learnt) { + assert(_extra_size < (1<<2)); + header.mark = 0; + header.learnt = learnt; + header.extra_size = _extra_size; + header.reloced = 0; + header.size = ps.size(); + header.lbd = 0; + header.canbedel = 1; + header.exported = 0; + header.oneWatched = 0; + header.seen = 0; + for (int i = 0; i < ps.size(); i++) + data[i].lit = ps[i]; + + if (header.extra_size > 0){ + if (header.learnt) + data[header.size].act = 0; + else + calcAbstraction(); + if (header.extra_size > 1) { + data[header.size+1].abs = 0; // learntFrom + } + } + } + +public: + void calcAbstraction() { + assert(header.extra_size > 0); + uint32_t abstraction = 0; + for (int i = 0; i < size(); i++) + abstraction |= 1 << (var(data[i].lit) & 31); + data[header.size].abs = abstraction; } + + int size () const { return header.size; } + void shrink (int i) { assert(i <= size()); + if (header.extra_size > 0) { + data[header.size-i] = data[header.size]; + if (header.extra_size > 1) { // Special case for imported clauses + data[header.size-i-1] = data[header.size-1]; + } + } + header.size -= i; } + void pop () { shrink(1); } + bool learnt () const { return header.learnt; } + void nolearnt () { header.learnt = false;} + bool has_extra () const { return header.extra_size > 0; } + uint32_t mark () const { return header.mark; } + void mark (uint32_t m) { header.mark = m; } + const Lit& last () const { return data[header.size-1].lit; } + + bool reloced () const { return header.reloced; } + CRef relocation () const { return data[0].rel; } + void relocate (CRef c) { header.reloced = 1; data[0].rel = c; } + + // NOTE: somewhat unsafe to change the clause in-place! Must manually call 'calcAbstraction' afterwards for + // subsumption operations to behave correctly. + Lit& operator [] (int i) { return data[i].lit; } + Lit operator [] (int i) const { return data[i].lit; } + operator const Lit* (void) const { return (Lit*)data; } + + float& activity () { assert(header.extra_size > 0); return data[header.size].act; } + uint32_t abstraction () const { assert(header.extra_size > 0); return data[header.size].abs; } + + // Handle imported clauses lazy sharing + bool wasImported() const {return header.extra_size > 1;} + uint32_t importedFrom () const { assert(header.extra_size > 1); return data[header.size + 1].abs;} + void setImportedFrom(uint32_t ifrom) {assert(header.extra_size > 1); data[header.size+1].abs = ifrom;} + + Lit subsumes (const Clause& other) const; + void strengthen (Lit p); + void setLBD(int i) {header.lbd=i; /*if (i < (1<<(BITS_LBD-1))) header.lbd = i; else header.lbd = (1<<(BITS_LBD-1));*/} + // unsigned int& lbd () { return header.lbd; } + unsigned int lbd () const { return header.lbd; } + void setCanBeDel(bool b) {header.canbedel = b;} + bool canBeDel() {return header.canbedel;} + void setSeen(bool b) {header.seen = b;} + bool getSeen() {return header.seen;} + void setExported(unsigned int b) {header.exported = b;} + unsigned int getExported() {return header.exported;} + void setOneWatched(bool b) {header.oneWatched = b;} + bool getOneWatched() {return header.oneWatched;} +#ifdef INCREMNENTAL + void setSizeWithoutSelectors (unsigned int n) {header.szWithoutSelectors = n; } + unsigned int sizeWithoutSelectors () const { return header.szWithoutSelectors; } +#endif + +}; + + +//================================================================================================= +// ClauseAllocator -- a simple class for allocating memory for clauses: + + + const CRef CRef_Undef = RegionAllocator::Ref_Undef; + class ClauseAllocator : public RegionAllocator + { + static int clauseWord32Size(int size, int extra_size){ + return (sizeof(Clause) + (sizeof(Lit) * (size + extra_size))) / sizeof(uint32_t); } + public: + bool extra_clause_field; + + ClauseAllocator(uint32_t start_cap) : RegionAllocator(start_cap), extra_clause_field(false){} + ClauseAllocator() : extra_clause_field(false){} + + void moveTo(ClauseAllocator& to){ + to.extra_clause_field = extra_clause_field; + RegionAllocator::moveTo(to); } + + template + CRef alloc(const Lits& ps, bool learnt = false, bool imported = false) + { + assert(sizeof(Lit) == sizeof(uint32_t)); + assert(sizeof(float) == sizeof(uint32_t)); + + bool use_extra = learnt | extra_clause_field; + int extra_size = imported?3:(use_extra?1:0); + CRef cid = RegionAllocator::alloc(clauseWord32Size(ps.size(), extra_size)); + new (lea(cid)) Clause(ps, extra_size, learnt); + + return cid; + } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + Clause& operator[](Ref r) { return (Clause&)RegionAllocator::operator[](r); } + const Clause& operator[](Ref r) const { return (Clause&)RegionAllocator::operator[](r); } + Clause* lea (Ref r) { return (Clause*)RegionAllocator::lea(r); } + const Clause* lea (Ref r) const { return (Clause*)RegionAllocator::lea(r); } + Ref ael (const Clause* t){ return RegionAllocator::ael((uint32_t*)t); } + + void free(CRef cid) + { + Clause& c = operator[](cid); + RegionAllocator::free(clauseWord32Size(c.size(), c.has_extra())); + } + + void reloc(CRef& cr, ClauseAllocator& to) + { + Clause& c = operator[](cr); + + if (c.reloced()) { cr = c.relocation(); return; } + + cr = to.alloc(c, c.learnt(), c.wasImported()); + c.relocate(cr); + + // Copy extra data-fields: + // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) + to[cr].mark(c.mark()); + if (to[cr].learnt()) { + to[cr].activity() = c.activity(); + to[cr].setLBD(c.lbd()); + to[cr].setExported(c.getExported()); + to[cr].setOneWatched(c.getOneWatched()); +#ifdef INCREMENTAL + to[cr].setSizeWithoutSelectors(c.sizeWithoutSelectors()); +#endif + to[cr].setCanBeDel(c.canBeDel()); + if (c.wasImported()) { + to[cr].setImportedFrom(c.importedFrom()); + } + } + else { + to[cr].setSeen(c.getSeen()); + if (to[cr].has_extra()) to[cr].calcAbstraction(); + } + } + }; + + +//================================================================================================= +// OccLists -- a class for maintaining occurence lists with lazy deletion: + +template +class OccLists +{ + vec occs; + vec dirty; + vec dirties; + Deleted deleted; + + public: + OccLists(const Deleted& d) : deleted(d) {} + + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } + // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } + Vec& lookup (const Idx& idx){ if (dirty[toInt(idx)]) clean(idx); return occs[toInt(idx)]; } + + void cleanAll (); + void copyTo(OccLists ©) const { + + copy.occs.growTo(occs.size()); + for(int i = 0;i +void OccLists::cleanAll() +{ + for (int i = 0; i < dirties.size(); i++) + // Dirties may contain duplicates so check here if a variable is already cleaned: + if (dirty[toInt(dirties[i])]) + clean(dirties[i]); + dirties.clear(); +} + + +template +void OccLists::clean(const Idx& idx) +{ + Vec& vec = occs[toInt(idx)]; + int i, j; + for (i = j = 0; i < vec.size(); i++) + if (!deleted(vec[i])) + vec[j++] = vec[i]; + vec.shrink(i - j); + dirty[toInt(idx)] = 0; +} + + +//================================================================================================= +// CMap -- a class for mapping clauses to values: + + +template +class CMap +{ + struct CRefHash { + uint32_t operator()(CRef cr) const { return (uint32_t)cr; } }; + + typedef Map HashTable; + HashTable map; + + public: + // Size-operations: + void clear () { map.clear(); } + int size () const { return map.elems(); } + + + // Insert/Remove/Test mapping: + void insert (CRef cr, const T& t){ map.insert(cr, t); } + void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility + void remove (CRef cr) { map.remove(cr); } + bool has (CRef cr, T& t) { return map.peek(cr, t); } + + // Vector interface (the clause 'c' must already exist): + const T& operator [] (CRef cr) const { return map[cr]; } + T& operator [] (CRef cr) { return map[cr]; } + + // Iteration (not transparent at all at the moment): + int bucket_count() const { return map.bucket_count(); } + const vec& bucket(int i) const { return map.bucket(i); } + + // Move contents to other map: + void moveTo(CMap& other){ map.moveTo(other.map); } + + // TMP debug: + void debug(){ + printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } +}; + + +/*_________________________________________________________________________________________________ +| +| subsumes : (other : const Clause&) -> Lit +| +| Description: +| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' +| by subsumption resolution. +| +| Result: +| lit_Error - No subsumption or simplification +| lit_Undef - Clause subsumes 'other' +| p - The literal p can be deleted from 'other' +|________________________________________________________________________________________________@*/ +inline Lit Clause::subsumes(const Clause& other) const +{ + //if (other.size() < size() || (extra.abst & ~other.extra.abst) != 0) + //if (other.size() < size() || (!learnt() && !other.learnt() && (extra.abst & ~other.extra.abst) != 0)) + assert(!header.learnt); assert(!other.header.learnt); + assert(header.extra_size > 0); assert(other.header.extra_size > 0); + if (other.header.size < header.size || (data[header.size].abs & ~other.data[other.header.size].abs) != 0) + return lit_Error; + + Lit ret = lit_Undef; + const Lit* c = (const Lit*)(*this); + const Lit* d = (const Lit*)other; + + for (unsigned i = 0; i < header.size; i++) { + // search for c[i] or ~c[i] + for (unsigned j = 0; j < other.header.size; j++) + if (c[i] == d[j]) + goto ok; + else if (ret == lit_Undef && c[i] == ~d[j]){ + ret = c[i]; + goto ok; + } + + // did not find it + return lit_Error; + ok:; + } + + return ret; +} + +inline void Clause::strengthen(Lit p) +{ + remove(*this, p); + calcAbstraction(); +} + +//================================================================================================= +} + + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Alg.h b/libs/mugen/glucose-syrup-4.1/mtl/Alg.h new file mode 100644 index 000000000..9afb4552b --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Alg.h @@ -0,0 +1,84 @@ +/*******************************************************************************************[Alg.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_Alg_h +#define Glucose_Alg_h + +#include "mtl/Vec.h" + +namespace Glucose { + +//================================================================================================= +// Useful functions on vector-like types: + +//================================================================================================= +// Removing and searching for elements: +// + +template +static inline void remove(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + assert(j < ts.size()); + for (; j < ts.size()-1; j++) ts[j] = ts[j+1]; + ts.pop(); +} + + +template +static inline bool find(V& ts, const T& t) +{ + int j = 0; + for (; j < ts.size() && ts[j] != t; j++); + return j < ts.size(); +} + + +//================================================================================================= +// Copying vectors with support for nested vector types: +// + +// Base case: +template +static inline void copy(const T& from, T& to) +{ + to = from; +} + +// Recursive case: +template +static inline void copy(const vec& from, vec& to, bool append = false) +{ + if (!append) + to.clear(); + for (int i = 0; i < from.size(); i++){ + to.push(); + copy(from[i], to.last()); + } +} + +template +static inline void append(const vec& from, vec& to){ copy(from, to, true); } + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Alloc.h b/libs/mugen/glucose-syrup-4.1/mtl/Alloc.h new file mode 100644 index 000000000..e027666f9 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Alloc.h @@ -0,0 +1,141 @@ +/*****************************************************************************************[Alloc.h] +Copyright (c) 2008-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + + +#ifndef Glucose_Alloc_h +#define Glucose_Alloc_h + +#include "mtl/XAlloc.h" +#include "mtl/Vec.h" + +namespace Glucose { + +//================================================================================================= +// Simple Region-based memory allocator: + +template +class RegionAllocator +{ + T* memory; + uint32_t sz; + uint32_t cap; + uint32_t wasted_; + + void capacity(uint32_t min_cap); + + public: + // TODO: make this a class for better type-checking? + typedef uint32_t Ref; + enum { Ref_Undef = UINT32_MAX }; + enum { Unit_Size = sizeof(uint32_t) }; + + explicit RegionAllocator(uint32_t start_cap = 1024*1024) : memory(NULL), sz(0), cap(0), wasted_(0){ capacity(start_cap); } + ~RegionAllocator() + { + if (memory != NULL) + ::free(memory); + } + + + uint32_t size () const { return sz; } + uint32_t getCap () const { return cap;} + uint32_t wasted () const { return wasted_; } + + Ref alloc (int size); + void free (int size) { wasted_ += size; } + + // Deref, Load Effective Address (LEA), Inverse of LEA (AEL): + T& operator[](Ref r) { assert(r >= 0 && r < sz); return memory[r]; } + const T& operator[](Ref r) const { assert(r >= 0 && r < sz); return memory[r]; } + + T* lea (Ref r) { assert(r >= 0 && r < sz); return &memory[r]; } + const T* lea (Ref r) const { assert(r >= 0 && r < sz); return &memory[r]; } + Ref ael (const T* t) { assert((void*)t >= (void*)&memory[0] && (void*)t < (void*)&memory[sz-1]); + return (Ref)(t - &memory[0]); } + + void moveTo(RegionAllocator& to) { + if (to.memory != NULL) ::free(to.memory); + to.memory = memory; + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + + memory = NULL; + sz = cap = wasted_ = 0; + } + + void copyTo(RegionAllocator& to) const { + // if (to.memory != NULL) ::free(to.memory); + to.memory = (T*)xrealloc(to.memory, sizeof(T)*cap); + memcpy(to.memory,memory,sizeof(T)*cap); + to.sz = sz; + to.cap = cap; + to.wasted_ = wasted_; + } + + + +}; + +template +void RegionAllocator::capacity(uint32_t min_cap) +{ + if (cap >= min_cap) return; + uint32_t prev_cap = cap; + while (cap < min_cap){ + // NOTE: Multiply by a factor (13/8) without causing overflow, then add 2 and make the + // result even by clearing the least significant bit. The resulting sequence of capacities + // is carefully chosen to hit a maximum capacity that is close to the '2^32-1' limit when + // using 'uint32_t' as indices so that as much as possible of this space can be used. + uint32_t delta = ((cap >> 1) + (cap >> 3) + 2) & ~1; + cap += delta; + + if (cap <= prev_cap) + throw OutOfMemoryException(); + } + //printf(" .. (%p) cap = %u\n", this, cap); + + assert(cap > 0); + memory = (T*)xrealloc(memory, sizeof(T)*cap); +} + + +template +typename RegionAllocator::Ref +RegionAllocator::alloc(int size) +{ + //printf("ALLOC called (this = %p, size = %d)\n", this, size); fflush(stdout); + assert(size > 0); + capacity(sz + size); + + uint32_t prev_sz = sz; + sz += size; + + // Handle overflow: + if (sz < prev_sz) + throw OutOfMemoryException(); + + return prev_sz; +} + + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Clone.h b/libs/mugen/glucose-syrup-4.1/mtl/Clone.h new file mode 100644 index 000000000..c0ec225ce --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Clone.h @@ -0,0 +1,13 @@ +#ifndef Glucose_Clone_h +#define Glucose_Clone_h + + +namespace Glucose { + + class Clone { + public: + virtual Clone* clone() const = 0; + }; +}; + +#endif \ No newline at end of file diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Heap.h b/libs/mugen/glucose-syrup-4.1/mtl/Heap.h new file mode 100644 index 000000000..0c40c4ff3 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Heap.h @@ -0,0 +1,150 @@ +/******************************************************************************************[Heap.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_Heap_h +#define Glucose_Heap_h + +#include "mtl/Vec.h" + +namespace Glucose { + +//================================================================================================= +// A heap implementation with support for decrease/increase key. + + +template +class Heap { + Comp lt; // The heap is a minimum-heap with respect to this comparator + vec heap; // Heap of integers + vec indices; // Each integers position (index) in the Heap + + // Index "traversal" functions + static inline int left (int i) { return i*2+1; } + static inline int right (int i) { return (i+1)*2; } + static inline int parent(int i) { return (i-1) >> 1; } + + + + void percolateUp(int i) + { + int x = heap[i]; + int p = parent(i); + + while (i != 0 && lt(x, heap[p])){ + heap[i] = heap[p]; + indices[heap[p]] = i; + i = p; + p = parent(p); + } + heap [i] = x; + indices[x] = i; + } + + + void percolateDown(int i) + { + int x = heap[i]; + while (left(i) < heap.size()){ + int child = right(i) < heap.size() && lt(heap[right(i)], heap[left(i)]) ? right(i) : left(i); + if (!lt(heap[child], x)) break; + heap[i] = heap[child]; + indices[heap[i]] = i; + i = child; + } + heap [i] = x; + indices[x] = i; + } + + + public: + Heap(const Comp& c) : lt(c) { } + + int size () const { return heap.size(); } + bool empty () const { return heap.size() == 0; } + bool inHeap (int n) const { return n < indices.size() && indices[n] >= 0; } + int operator[](int index) const { assert(index < heap.size()); return heap[index]; } + + + void decrease (int n) { assert(inHeap(n)); percolateUp (indices[n]); } + void increase (int n) { assert(inHeap(n)); percolateDown(indices[n]); } + + void copyTo(Heap& copy) const {heap.copyTo(copy.heap);indices.copyTo(copy.indices);} + + // Safe variant of insert/decrease/increase: + void update(int n) + { + if (!inHeap(n)) + insert(n); + else { + percolateUp(indices[n]); + percolateDown(indices[n]); } + } + + + void insert(int n) + { + indices.growTo(n+1, -1); + assert(!inHeap(n)); + + indices[n] = heap.size(); + heap.push(n); + percolateUp(indices[n]); + } + + + int removeMin() + { + int x = heap[0]; + heap[0] = heap.last(); + indices[heap[0]] = 0; + indices[x] = -1; + heap.pop(); + if (heap.size() > 1) percolateDown(0); + return x; + } + + + // Rebuild the heap from scratch, using the elements in 'ns': + void build(vec& ns) { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(); + + for (int i = 0; i < ns.size(); i++){ + indices[ns[i]] = i; + heap.push(ns[i]); } + + for (int i = heap.size() / 2 - 1; i >= 0; i--) + percolateDown(i); + } + + void clear(bool dealloc = false) + { + for (int i = 0; i < heap.size(); i++) + indices[heap[i]] = -1; + heap.clear(dealloc); + } +}; + + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/IntTypes.h b/libs/mugen/glucose-syrup-4.1/mtl/IntTypes.h new file mode 100644 index 000000000..2d8d4e872 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/IntTypes.h @@ -0,0 +1,46 @@ +/**************************************************************************************[IntTypes.h] +Copyright (c) 2009-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_IntTypes_h +#define Glucose_IntTypes_h + +#ifdef __sun + // Not sure if there are newer versions that support C99 headers. The + // needed features are implemented in the headers below though: + +# include +# include +# include + +#else + +# include +# include + +#endif + +#include + +#ifndef PRIu64 +#define PRIu64 "lu" +#define PRIi64 "ld" +#endif +//================================================================================================= + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Map.h b/libs/mugen/glucose-syrup-4.1/mtl/Map.h new file mode 100644 index 000000000..34063cbac --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Map.h @@ -0,0 +1,200 @@ +/*******************************************************************************************[Map.h] +Copyright (c) 2006-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_Map_h +#define Glucose_Map_h + +#include "mtl/IntTypes.h" +#include "mtl/Vec.h" +#include +#include + +namespace Glucose { + +//================================================================================================= +// Default hash/equals functions +// + +static inline uint32_t hash(std::string x) {std::hash hasher;return hasher(x); } + +template struct Hash { uint32_t operator()(const K& k) const { return hash(k); } }; +template struct Equal { bool operator()(const K& k1, const K& k2) const { return k1 == k2; } }; + +template struct DeepHash { uint32_t operator()(const K* k) const { return hash(*k); } }; +template struct DeepEqual { bool operator()(const K* k1, const K* k2) const { return *k1 == *k2; } }; + +static inline uint32_t hash(uint32_t x){ return x; } +static inline uint32_t hash(uint64_t x){ return (uint32_t)x; } +static inline uint32_t hash(int32_t x) { return (uint32_t)x; } +static inline uint32_t hash(int64_t x) { return (uint32_t)x; } + + +//================================================================================================= +// Some primes +// + +static const int nprimes = 25; +static const int primes [nprimes] = { 31, 73, 151, 313, 643, 1291, 2593, 5233, 10501, 21013, 42073, 84181, 168451, 337219, 674701, 1349473, 2699299, 5398891, 10798093, 21596719, 43193641, 86387383, 172775299, 345550609, 691101253 }; + +//================================================================================================= +// Hash table implementation of Maps +// + +template, class E = Equal > +class Map { + public: + struct Pair { K key; D data; }; + + private: + H hash; + E equals; + + vec* table; + int cap; + int size; + + // Don't allow copying (error prone): + Map& operator = (Map& other) { assert(0); } + Map (Map& other) { assert(0); } + + bool checkCap(int new_size) const { return new_size > cap; } + + int32_t index (const K& k) const { return hash(k) % cap; } + void _insert (const K& k, const D& d) { + vec& ps = table[index(k)]; + ps.push(); ps.last().key = k; ps.last().data = d; } + + void rehash () { + const vec* old = table; + + int old_cap = cap; + int newsize = primes[0]; + for (int i = 1; newsize <= cap && i < nprimes; i++) + newsize = primes[i]; + + table = new vec[newsize]; + cap = newsize; + + for (int i = 0; i < old_cap; i++){ + for (int j = 0; j < old[i].size(); j++){ + _insert(old[i][j].key, old[i][j].data); }} + + delete [] old; + + // printf(" --- rehashing, old-cap=%d, new-cap=%d\n", cap, newsize); + } + + + public: + + Map () : table(NULL), cap(0), size(0) {} + Map (const H& h, const E& e) : hash(h), equals(e), table(NULL), cap(0), size(0){} + ~Map () { delete [] table; } + + // PRECONDITION: the key must already exist in the map. + const D& operator [] (const K& k) const + { + assert(size != 0); + const D* res = NULL; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; +// if(res==NULL) printf("%s\n",k.c_str()); + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must already exist in the map. + D& operator [] (const K& k) + { + assert(size != 0); + D* res = NULL; + vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + res = &ps[i].data; +// if(res==NULL) printf("%s\n",k.c_str()); + + assert(res != NULL); + return *res; + } + + // PRECONDITION: the key must *NOT* exist in the map. + void insert (const K& k, const D& d) { if (checkCap(size+1)) rehash(); _insert(k, d); size++; } + bool peek (const K& k, D& d) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)){ + d = ps[i].data; + return true; } + return false; + } + + bool has (const K& k) const { + if (size == 0) return false; + const vec& ps = table[index(k)]; + for (int i = 0; i < ps.size(); i++) + if (equals(ps[i].key, k)) + return true; + return false; + } + + // PRECONDITION: the key must exist in the map. + void remove(const K& k) { + assert(table != NULL); + vec& ps = table[index(k)]; + int j = 0; + for (; j < ps.size() && !equals(ps[j].key, k); j++); + assert(j < ps.size()); + ps[j] = ps.last(); + ps.pop(); + size--; + } + + void clear () { + cap = size = 0; + delete [] table; + table = NULL; + } + + int elems() const { return size; } + int bucket_count() const { return cap; } + + // NOTE: the hash and equality objects are not moved by this method: + void moveTo(Map& other){ + delete [] other.table; + + other.table = table; + other.cap = cap; + other.size = size; + + table = NULL; + size = cap = 0; + } + + // NOTE: given a bit more time, I could make a more C++-style iterator out of this: + const vec& bucket(int i) const { return table[i]; } +}; + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Queue.h b/libs/mugen/glucose-syrup-4.1/mtl/Queue.h new file mode 100644 index 000000000..c71e45bae --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Queue.h @@ -0,0 +1,80 @@ +/*****************************************************************************************[Queue.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_Queue_h +#define Glucose_Queue_h + +#include "mtl/Vec.h" + +namespace Glucose { + +//================================================================================================= + +template +class Queue { + vec buf; + int first; + int end; + +public: + typedef T Key; + + Queue() : buf(1), first(0), end(0) {} + + void clear (bool dealloc = false) { buf.clear(dealloc); buf.growTo(1); first = end = 0; } + int size () const { return (end >= first) ? end - first : end - first + buf.size(); } + + + + const T& operator [] (int index) const { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + T& operator [] (int index) { assert(index >= 0); assert(index < size()); return buf[(first + index) % buf.size()]; } + + T peek () const { assert(first != end); return buf[first]; } + void pop () { assert(first != end); first++; if (first == buf.size()) first = 0; } + + + void copyTo(Queue& copy) const { + copy.first = first; + copy.end = end; + buf.memCopyTo(copy.buf); + } + + + void insert(T elem) { // INVARIANT: buf[end] is always unused + buf[end++] = elem; + if (end == buf.size()) end = 0; + if (first == end){ // Resize: + vec tmp((buf.size()*3 + 1) >> 1); + //**/printf("queue alloc: %d elems (%.1f MB)\n", tmp.size(), tmp.size() * sizeof(T) / 1000000.0); + int i = 0; + for (int j = first; j < buf.size(); j++) tmp[i++] = buf[j]; + for (int j = 0 ; j < end ; j++) tmp[i++] = buf[j]; + first = 0; + end = buf.size(); + tmp.moveTo(buf); + } + } +}; + + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Sort.h b/libs/mugen/glucose-syrup-4.1/mtl/Sort.h new file mode 100644 index 000000000..50cb44844 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Sort.h @@ -0,0 +1,98 @@ +/******************************************************************************************[Sort.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_Sort_h +#define Glucose_Sort_h + +#include "mtl/Vec.h" + +//================================================================================================= +// Some sorting algorithms for vec's + + +namespace Glucose { + +template +struct LessThan_default { + bool operator () (T x, T y) { return x < y; } +}; + + +template +void selectionSort(T* array, int size, LessThan lt) +{ + int i, j, best_i; + T tmp; + + for (i = 0; i < size-1; i++){ + best_i = i; + for (j = i+1; j < size; j++){ + if (lt(array[j], array[best_i])) + best_i = j; + } + tmp = array[i]; array[i] = array[best_i]; array[best_i] = tmp; + } +} +template static inline void selectionSort(T* array, int size) { + selectionSort(array, size, LessThan_default()); } + +template +void sort(T* array, int size, LessThan lt) +{ + if (size <= 15) + selectionSort(array, size, lt); + + else{ + T pivot = array[size / 2]; + T tmp; + int i = -1; + int j = size; + + for(;;){ + do i++; while(lt(array[i], pivot)); + do j--; while(lt(pivot, array[j])); + + if (i >= j) break; + + tmp = array[i]; array[i] = array[j]; array[j] = tmp; + } + + sort(array , i , lt); + sort(&array[i], size-i, lt); + } +} +template static inline void sort(T* array, int size) { + sort(array, size, LessThan_default()); } + + +//================================================================================================= +// For 'vec's: + + +template void sort(vec& v, LessThan lt) { + sort((T*)v, v.size(), lt); } +template void sort(vec& v) { + sort(v, LessThan_default()); } + + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/Vec.h b/libs/mugen/glucose-syrup-4.1/mtl/Vec.h new file mode 100644 index 000000000..77841c7ea --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/Vec.h @@ -0,0 +1,152 @@ +/*******************************************************************************************[Vec.h] +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_Vec_h +#define Glucose_Vec_h + +#include +#include + +#include "mtl/IntTypes.h" +#include "mtl/XAlloc.h" +#include + +namespace Glucose { + +//================================================================================================= +// Automatically resizable arrays +// +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template +class vec { + T* data; + int sz; + int cap; + + // Don't allow copying (error prone): + vec& operator = (vec& other) { assert(0); return *this; } + vec (vec& other) { assert(0); } + + // Helpers for calculating next capacity: + static inline int imax (int x, int y) { int mask = (y-x) >> (sizeof(int)*8-1); return (x&mask) + (y&(~mask)); } + //static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + +public: + // Constructors: + vec() : data(NULL) , sz(0) , cap(0) { } + explicit vec(int size) : data(NULL) , sz(0) , cap(0) { growTo(size); } + vec(int size, const T& pad) : data(NULL) , sz(0) , cap(0) { growTo(size, pad); } + ~vec() { clear(true); } + + // Pointer to first element: + operator T* (void) { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void shrink_ (int nelems) { assert(nelems <= sz); sz -= nelems; } + int capacity (void) const { return cap; } + void capacity (int min_cap); + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + + // Stack interface: + void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } + void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } + void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } + void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } + + void remove(const T &elem) { + int tmp; + for(tmp = 0;tmp& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) copy[i] = data[i]; } + void moveTo(vec& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } + void memCopyTo(vec& copy) const{ + copy.capacity(cap); + copy.sz = sz; + memcpy(copy.data,data,sizeof(T)*cap); + } + +}; + + +template +void vec::capacity(int min_cap) { + if (cap >= min_cap) return; + int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + if (add > INT_MAX - cap || ((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM) + throw OutOfMemoryException(); + } + + +template +void vec::growTo(int size, const T& pad) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) data[i] = pad; + sz = size; } + + +template +void vec::growTo(int size) { + if (sz >= size) return; + capacity(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; } + + +template +void vec::clear(bool dealloc) { + if (data != NULL){ + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; } } + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/VecThreads.h b/libs/mugen/glucose-syrup-4.1/mtl/VecThreads.h new file mode 100644 index 000000000..e3951958e --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/VecThreads.h @@ -0,0 +1,216 @@ +/*******************************************************************************************[VecThreads.h] + * Threads safe version used in Glucose-Syrup, 2015, Gilles Audemard, Laurent Simon +Copyright (c) 2003-2007, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_VecThreads_h +#define Glucose_VecThreads_h + +#include +#include + +#include "mtl/IntTypes.h" +#include "mtl/XAlloc.h" +#include + +namespace Glucose { + +//================================================================================================= +// Automatically resizable arrays +// +// NOTE! Don't use this vector on datatypes that cannot be re-located in memory (with realloc) + +template +class vecThreads { + T* data; + int sz; + int cap; + bool lock; + int nbusers; + + // Don't allow copying (error prone): + vecThreads& operator = (vecThreads& other) { assert(0); return *this; } + vecThreads (vecThreads& other) { assert(0); } + + // Helpers for calculating next capacity: + static inline int imax (int x, int y) { int mask = (y-x) >> (sizeof(int)*8-1); return (x&mask) + (y&(~mask)); } + //static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + static inline void nextCap(int& cap){ cap += ((cap >> 1) + 2) & ~1; } + +public: + // Constructors: + vecThreads() : data(NULL) , sz(0) , cap(0), lock(false), nbusers(0) { } + explicit vecThreads(int size) : data(NULL) , sz(0) , cap(0), lock(false), nbusers(0) { growTo(size); } + vecThreads(int size, const T& pad) : data(NULL) , sz(0) , cap(0), lock(false), nbusers(0) { growTo(size, pad); } + ~vecThreads() { clear(true); } + + // Pointer to first element: + operator T* (void) { return data; } + + // Size operations: + int size (void) const { return sz; } + void shrink (int nelems) { assert(nelems <= sz); for (int i = 0; i < nelems; i++) sz--, data[sz].~T(); } + void shrink_ (int nelems) { assert(nelems <= sz); sz -= nelems; } + int capacity (void) const { return cap; } + void capacity (int min_cap); + void capacityProtected (int min_cap); + void growTo (int size); + void growTo (int size, const T& pad); + void clear (bool dealloc = false); + + // Stack interface: + void push (void) { if (sz == cap) capacity(sz+1); new (&data[sz]) T(); sz++; } + void push (const T& elem) { if (sz == cap) capacity(sz+1); data[sz++] = elem; } + void push_ (const T& elem) { assert(sz < cap); data[sz++] = elem; } + void pop (void) { assert(sz > 0); sz--, data[sz].~T(); } + + void startMaintenance(); + void endMaintenance(); + void startLoop(); + void endLoop(); + + void remove(const T &elem) { + int tmp; + for(tmp = 0;tmp& copy) const { copy.clear(); copy.growTo(sz); + startLoop();for (int i = 0; i < sz; i++) copy[i] = data[i]; endLoop();} + void moveTo(vecThreads& dest) { + assert(false); // This cannot be made thread safe from here. + dest.clear(true); + startMaintenance(); + dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; + endMaintenance(); } + void memCopyTo(vecThreads& copy) const{ + copy.capacity(cap); + copy.sz = sz; + memcpy(copy.data,data,sizeof(T)*cap); + } + +}; + +template +void vecThreads::startLoop() { + bool retry = true; + while (retry) { + while(!__sync_bool_compare_and_swap(&lock,false, true)); + if (nbusers >= 0) {nbusers++; retry=false;} + lock = false; + } +} + +template +void vecThreads::endLoop() { + while(!__sync_bool_compare_and_swap(&lock,false, true)); + nbusers--; + lock = false; +} + +template +inline void vecThreads::startMaintenance() { + bool retry = true; + while (retry) { + while(!__sync_bool_compare_and_swap(&lock,false, true)); + if (nbusers == 0) {nbusers--; retry=false;} + lock = false; + } +} + +template +inline void vecThreads::endMaintenance() { + while(!__sync_bool_compare_and_swap(&lock,false, true)); + nbusers++; + lock = false; +} +template +inline void vecThreads::capacityProtected(int min_cap) { + startMaintenance(); + capacity(min_cap); + endMaintenance(); +} + +template +void vecThreads::capacity(int min_cap) { + if (cap >= min_cap) return; + + int add = imax((min_cap - cap + 1) & ~1, ((cap >> 1) + 2) & ~1); // NOTE: grow by approximately 3/2 + if (add > INT_MAX - cap || ((data = (T*)::realloc(data, (cap += add) * sizeof(T))) == NULL) && errno == ENOMEM) + throw OutOfMemoryException(); + + } + + +template +void vecThreads::growTo(int size, const T& pad) { + if (sz >= size) return; + startMaintenance(); + capacity(size); + for (int i = sz; i < size; i++) data[i] = pad; + sz = size; + endMaintenance(); +} + + +template +void vecThreads::growTo(int size) { + if (sz >= size) return; + startMaintenance(); + capacity(size); + for (int i = sz; i < size; i++) new (&data[i]) T(); + sz = size; + endMaintenance(); +} + + +template +void vecThreads::clear(bool dealloc) { + if (data != NULL){ + startMaintenance(); + for (int i = 0; i < sz; i++) data[i].~T(); + sz = 0; + if (dealloc) free(data), data = NULL, cap = 0; + endMaintenance();} } + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/XAlloc.h b/libs/mugen/glucose-syrup-4.1/mtl/XAlloc.h new file mode 100644 index 000000000..f8ca4fec4 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/XAlloc.h @@ -0,0 +1,47 @@ +/****************************************************************************************[XAlloc.h] +Copyright (c) 2009-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + + +#ifndef Glucose_XAlloc_h +#define Glucose_XAlloc_h + +#include +#include +#include + +namespace Glucose { + +//================================================================================================= +// Simple layer on top of malloc/realloc to catch out-of-memory situtaions and provide some typing: + +class OutOfMemoryException{}; +static inline void* xrealloc(void *ptr, size_t size) +{ + void* mem = realloc(ptr, size); + if (mem == NULL && errno == ENOMEM){ + throw OutOfMemoryException(); + }else { + return mem; + } +} + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/mtl/config.mk b/libs/mugen/glucose-syrup-4.1/mtl/config.mk new file mode 100644 index 000000000..b5c36fc6b --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/config.mk @@ -0,0 +1,6 @@ +## +## This file is for system specific configurations. For instance, on +## some systems the path to zlib needs to be added. Example: +## +## CFLAGS += -I/usr/local/include +## LFLAGS += -L/usr/local/lib diff --git a/libs/mugen/glucose-syrup-4.1/mtl/template.mk b/libs/mugen/glucose-syrup-4.1/mtl/template.mk new file mode 100644 index 000000000..db3327a19 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/mtl/template.mk @@ -0,0 +1,109 @@ +## +## Template makefile for Standard, Profile, Debug, Release, and Release-static versions +## +## eg: "make rs" for a statically linked release version. +## "make d" for a debug version (no optimizations). +## "make" for the standard version (optimized, but with debug information and assertions active) + +PWD = $(shell pwd) +EXEC ?= $(notdir $(PWD)) + +CSRCS = $(wildcard $(PWD)/*.cc) +DSRCS = $(foreach dir, $(DEPDIR), $(filter-out $(MROOT)/$(dir)/Main.cc, $(wildcard $(MROOT)/$(dir)/*.cc))) +CHDRS = $(wildcard $(PWD)/*.h) +COBJS = $(CSRCS:.cc=.o) $(DSRCS:.cc=.o) + +PCOBJS = $(addsuffix p, $(COBJS)) +DCOBJS = $(addsuffix d, $(COBJS)) +RCOBJS = $(addsuffix r, $(COBJS)) + +CXX ?= g++ +CFLAGS ?= -Wall -Wno-parentheses -std=c++11 +LFLAGS ?= -Wall -lpthread + +COPTIMIZE ?= -O3 + +CFLAGS += -I$(MROOT) -D __STDC_LIMIT_MACROS -D __STDC_FORMAT_MACROS +LFLAGS += -lz + +.PHONY : s p d r rs clean + +s: $(EXEC) +p: $(EXEC)_profile +d: $(EXEC)_debug +r: $(EXEC)_release +rs: $(EXEC)_static + +libs: lib$(LIB)_standard.a +libp: lib$(LIB)_profile.a +libd: lib$(LIB)_debug.a +libr: lib$(LIB)_release.a + +## Compile options +%.o: CFLAGS +=$(COPTIMIZE) -g -D DEBUG +%.op: CFLAGS +=$(COPTIMIZE) -pg -g -D NDEBUG +%.od: CFLAGS +=-O0 -g -D DEBUG +%.or: CFLAGS +=$(COPTIMIZE) -g -D NDEBUG + +## Link options +$(EXEC): LFLAGS += -g +$(EXEC)_profile: LFLAGS += -g -pg +$(EXEC)_debug: LFLAGS += -g +#$(EXEC)_release: LFLAGS += ... +$(EXEC)_static: LFLAGS += --static + +## Dependencies +$(EXEC): $(COBJS) +$(EXEC)_profile: $(PCOBJS) +$(EXEC)_debug: $(DCOBJS) +$(EXEC)_release: $(RCOBJS) +$(EXEC)_static: $(RCOBJS) + +lib$(LIB)_standard.a: $(filter-out */Main.o, $(COBJS)) +lib$(LIB)_profile.a: $(filter-out */Main.op, $(PCOBJS)) +lib$(LIB)_debug.a: $(filter-out */Main.od, $(DCOBJS)) +lib$(LIB)_release.a: $(filter-out */Main.or, $(RCOBJS)) + + +## Build rule +%.o %.op %.od %.or: %.cc + @echo Compiling: $(subst $(MROOT)/,,$@) + @$(CXX) $(CFLAGS) -c -o $@ $< + +## Linking rules (standard/profile/debug/release) +$(EXEC) $(EXEC)_profile $(EXEC)_debug $(EXEC)_release $(EXEC)_static: + @echo Linking: "$@ ( $(foreach f,$^,$(subst $(MROOT)/,,$f)) )" + @$(CXX) $^ $(LFLAGS) -o $@ + +## Library rules (standard/profile/debug/release) +lib$(LIB)_standard.a lib$(LIB)_profile.a lib$(LIB)_release.a lib$(LIB)_debug.a: + @echo Making library: "$@ ( $(foreach f,$^,$(subst $(MROOT)/,,$f)) )" + @$(AR) -rcsv $@ $^ + +## Library Soft Link rule: +libs libp libd libr: + @echo "Making Soft Link: $^ -> lib$(LIB).a" + @ln -sf $^ lib$(LIB).a + +## Clean rule +allclean: clean + + @rm -f ../simp/*.o ../simp/*.or ../simp/*.od ../core/*.o ../core/*.or ../core/*.od +clean: + rm -f $(EXEC) $(EXEC)_profile $(EXEC)_debug $(EXEC)_release $(EXEC)_static \ + $(COBJS) $(PCOBJS) $(DCOBJS) $(RCOBJS) *.core depend.mk + +## Make dependencies +depend.mk: $(CSRCS) $(CHDRS) + @echo Making dependencies + @$(CXX) $(CFLAGS) -I$(MROOT) \ + $(CSRCS) -MM | sed 's|\(.*\):|$(PWD)/\1 $(PWD)/\1r $(PWD)/\1d $(PWD)/\1p:|' > depend.mk + @for dir in $(DEPDIR); do \ + if [ -r $(MROOT)/$${dir}/depend.mk ]; then \ + echo Depends on: $${dir}; \ + cat $(MROOT)/$${dir}/depend.mk >> depend.mk; \ + fi; \ + done + +-include $(MROOT)/mtl/config.mk +-include depend.mk diff --git a/libs/mugen/glucose-syrup-4.1/parallel/ClausesBuffer.cc b/libs/mugen/glucose-syrup-4.1/parallel/ClausesBuffer.cc new file mode 100644 index 000000000..5b18767a0 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/ClausesBuffer.cc @@ -0,0 +1,235 @@ +/**********************************************************************************[ClausesBuffer.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +/* ClausesBuffer + * + * This class is responsible for exchanging clauses between threads. + * It is based on a fixed-length FIFO array of literals. + * If the FIFO is full, then old clauses are removed (even if it was not yet sent to all threads) + * + * a clause " l1 l2 l3" is pushed in the FIFO with the following 6 unsigned integers + * 3 nseen origin l1 l2 l3 + * + 3 is the size of the pushed clause + * + nseen is the number of thread which imported this clause (initialized with nthreads-1) + * (when set to 0, the clause is removed from the fifo) + * + origin is the thread id of the thread which added this clause to the fifo + * + l1 l2 l3 are the literals of the clause + * + * ********************************************************************************************** + * **CAREFUL** This class is not thread-safe. In glucose-syrup, the SharedCompanion is + * responsible for ensuring atomicity of main functions + * ********************************************************************************************** + * + * */ + +#include "parallel/ClausesBuffer.h" + +//================================================================================================= + +using namespace Glucose; + +extern BoolOption opt_whenFullRemoveOlder; +extern IntOption opt_fifoSizeByCore; + +// index : size clause +// index + 1 : nbSeen +// index + 2 : threadId +// index + 3 : .. index + 3 + size : Lit of clause +ClausesBuffer::ClausesBuffer(int _nbThreads, unsigned int _maxsize) : first(0), last(_maxsize-1), + maxsize(_maxsize), queuesize(0), + removedClauses(0), + forcedRemovedClauses(0), nbThreads(_nbThreads), + whenFullRemoveOlder(opt_whenFullRemoveOlder), fifoSizeByCore(opt_fifoSizeByCore) { + lastOfThread.growTo(_nbThreads); + for(int i=0;i= maxsize) + return i - maxsize; + return i; +} + +void ClausesBuffer::removeLastClause() { + assert(queuesize > 0); + do { + unsigned int size = (unsigned int) elems[nextIndex(last)]; + unsigned int nextlast = addIndex(last, size+headerSize); + + for(int i=0;i 0); + queuesize --; + } + removedClauses ++; + assert(last >= 0); + assert(last < maxsize); + assert(last == nextlast); + } while (queuesize > 0 && (elems[addIndex(last,2)] == 0)); + +} + + +// Pushes a single uint to the fifo +inline void ClausesBuffer::noCheckPush(uint32_t x) { + elems[first] = x; + first = nextIndex(first); +} + +// Pops a single uint from the fifo +inline uint32_t ClausesBuffer::noCheckPop(uint32_t & index) { + index = nextIndex(index); + uint32_t ret = elems[index]; + return ret; +} + + + +// Return true if the clause was succesfully added +bool ClausesBuffer::pushClause(int threadId, Clause & c) { + if (!whenFullRemoveOlder && (queuesize + c.size() + headerSize >= maxsize)) + return false; // We need to remove some old clauses + while (queuesize + c.size() + headerSize >= maxsize) { // We need to remove some old clauses + forcedRemovedClauses ++; + removeLastClause(); + assert(queuesize > 0); + } + noCheckPush(c.size()); + noCheckPush(nbThreads>1?nbThreads-1:1); + noCheckPush(threadId); + for(int i=0;i (%d, %d)\n", first, last); +} + +bool ClausesBuffer::getClause(int threadId, int & threadOrigin, vec & resultClause, bool firstFound) { + assert(lastOfThread.size() > threadId); + unsigned int thislast = lastOfThread[threadId]; + assert(!firstFound || thislast == last); // FIXME: Gilles has this assertion on his cluster + + // Early exiting + if (nextIndex(thislast) == first) return false; + + if ( ( thislast < last && last < first) || + ( first < thislast && thislast < last ) || + ( last < first && first < thislast) ) { + // Special case where last has moved and lastOfThread[threadId] is no more valid (is behind) + thislast = last; + } + assert(!firstFound); + // Go to next clause for this thread id + if (!firstFound) { + while (nextIndex(thislast) != first && elems[addIndex(thislast,3)] == ((unsigned int)threadId)) { // 3 = 2 + 1 + thislast = addIndex(thislast, elems[nextIndex(thislast)] + headerSize); // + assert(thislast >= 0); + assert(thislast < maxsize); + } + assert(nextIndex(thislast)==first || elems[addIndex(thislast,3)] != (unsigned int)threadId); + } + + if (nextIndex(thislast) == first) { + lastOfThread[threadId] = thislast; + return false; + } + assert(elems[addIndex(thislast,3)] != ((unsigned int) threadId)); + unsigned int previouslast = thislast; + bool removeAfter = false; + int csize = noCheckPop(thislast); + removeAfter = (--elems[addIndex(thislast,1)] == 0); // We are sure this is not one of our own clause + thislast = nextIndex(thislast); // Skips the removeAfter fieldr + threadOrigin = noCheckPop(thislast); + assert(threadOrigin != threadId); + resultClause.clear(); + for(int i=0;i elems; + unsigned int first; + unsigned int last; + unsigned int maxsize; + unsigned int queuesize; // Number of current elements (must be < maxsize !) + unsigned int removedClauses; + unsigned int forcedRemovedClauses; + static const int headerSize = 3; + int nbThreads; + bool whenFullRemoveOlder; + unsigned int fifoSizeByCore; + vec lastOfThread; // Last value for a thread + + public: + ClausesBuffer(int _nbThreads, unsigned int _maxsize); + ClausesBuffer(); + + void setNbThreads(int _nbThreads); + unsigned int nextIndex(unsigned int i); + unsigned int addIndex(unsigned int i, unsigned int a); + void removeLastClause(); + + void noCheckPush(uint32_t x); + uint32_t noCheckPop(unsigned int & index); + + // Return true if the clause was succesfully added + bool pushClause(int threadId, Clause & c); + bool getClause(int threadId, int & threadOrigin, vec & resultClause, bool firstFound = false); + + int maxSize() const {return maxsize;} + uint32_t getCap(); + void growTo(int size) { + assert(0); // Not implemented (essentially for efficiency reasons) + elems.growTo(size); + first=0; maxsize=size; queuesize = 0;last = 0; + for(int i=0;i + +#include +#include + + +#include "utils/System.h" +#include "utils/ParseUtils.h" +#include "utils/Options.h" +#include "core/Dimacs.h" +#include "core/SolverTypes.h" + +#include "simp/SimpSolver.h" +#include "parallel/ParallelSolver.h" +#include "parallel/MultiSolvers.h" + +using namespace Glucose; + + + +static MultiSolvers* pmsolver; + +// Terminate by notifying the solver and back out gracefully. This is mainly to have a test-case +// for this feature of the Solver as it may take longer than an immediate call to '_exit()'. +//static void SIGINT_interrupt(int signum) { pmsolver->interrupt(); } + + +// Note that '_exit()' rather than 'exit()' has to be used. The reason is that 'exit()' calls +// destructors and may cause deadlocks if a malloc/free function happens to be running (these +// functions are guarded by locks for multithreaded use). +static void SIGINT_exit(int signum) { + printf("\n"); printf("*** INTERRUPTED ***\n"); + if (pmsolver->verbosity() > 0){ + pmsolver->printFinalStats(); + printf("\n"); printf("*** INTERRUPTED ***\n"); } + _exit(1); } + + +//================================================================================================= +// Main: + + +int main(int argc, char** argv) +{ + double realTimeStart = realTime(); + printf("c\nc This is glucose-syrup 4.0 (glucose in many threads) -- based on MiniSAT (Many thanks to MiniSAT team)\nc\n"); + try { + setUsageHelp("c USAGE: %s [options] \n\n where input may be either in plain or gzipped DIMACS.\n"); + // printf("This is MiniSat 2.0 beta\n"); + + // Extra options: + // + IntOption verb ("MAIN", "verb", "Verbosity level (0=silent, 1=some, 2=more).", 1, IntRange(0, 2)); + BoolOption mod ("MAIN", "model", "show model.", false); + IntOption vv ("MAIN", "vv", "Verbosity every vv conflicts", 10000, IntRange(1,INT32_MAX)); + BoolOption pre ("MAIN", "pre", "Completely turn on/off any preprocessing.", true); + + IntOption cpu_lim("MAIN", "cpu-lim","Limit on CPU time allowed in seconds.\n", INT32_MAX, IntRange(0, INT32_MAX)); + IntOption mem_lim("MAIN", "mem-lim","Limit on memory usage in megabytes.\n", INT32_MAX, IntRange(0, INT32_MAX)); + + parseOptions(argc, argv, true); + + MultiSolvers msolver; + pmsolver = & msolver; + msolver.setVerbosity(verb); + msolver.setVerbEveryConflicts(vv); + msolver.setShowModel(mod); + + double initial_time = cpuTime(); + + // Use signal handlers that forcibly quit until the solver will be able to respond to + // interrupts: + signal(SIGINT, SIGINT_exit); + signal(SIGXCPU,SIGINT_exit); + + // Set limit on CPU-time: + if (cpu_lim != INT32_MAX){ + rlimit rl; + getrlimit(RLIMIT_CPU, &rl); + if (rl.rlim_max == RLIM_INFINITY || (rlim_t)cpu_lim < rl.rlim_max){ + rl.rlim_cur = cpu_lim; + if (setrlimit(RLIMIT_CPU, &rl) == -1) + printf("c WARNING! Could not set resource limit: CPU-time.\n"); + } } + + // Set limit on virtual memory: + if (mem_lim != INT32_MAX){ + rlim_t new_mem_lim = (rlim_t)mem_lim * 1024*1024; + rlimit rl; + getrlimit(RLIMIT_AS, &rl); + if (rl.rlim_max == RLIM_INFINITY || new_mem_lim < rl.rlim_max){ + rl.rlim_cur = new_mem_lim; + if (setrlimit(RLIMIT_AS, &rl) == -1) + printf("c WARNING! Could not set resource limit: Virtual memory.\n"); + } } + + if (argc == 1) + printf("c Reading from standard input... Use '--help' for help.\n"); + + gzFile in = (argc == 1) ? gzdopen(0, "rb") : gzopen(argv[1], "rb"); + if (in == NULL) + printf("c ERROR! Could not open file: %s\n", argc == 1 ? "" : argv[1]), exit(1); + + if (msolver.verbosity() > 0){ + printf("c ========================================[ Problem Statistics ]===========================================\n"); + printf("c | |\n"); } + + parse_DIMACS(in, msolver); + gzclose(in); + + + + FILE* res = (argc >= 3) ? fopen(argv[argc-1], "wb") : NULL; + + if (msolver.verbosity() > 0){ + printf("c | Number of variables: %12d |\n", msolver.nVars()); + printf("c | Number of clauses: %12d |\n", msolver.nClauses()); } + + double parsed_time = cpuTime(); + if (msolver.verbosity() > 0){ + printf("c | Parse time: %12.2f s |\n", parsed_time - initial_time); + printf("c | |\n"); } + + // Change to signal-handlers that will only notify the solver and allow it to terminate + // voluntarily: + //signal(SIGINT, SIGINT_interrupt); + //signal(SIGXCPU,SIGINT_interrupt); + + + int ret2 = msolver.simplify(); + msolver.use_simplification = pre; + if(ret2) + msolver.eliminate(); + if(pre) { + double simplified_time = cpuTime(); + if (msolver.verbosity() > 0){ + printf("c | Simplification time: %12.2f s |\n", simplified_time - parsed_time); + printf("c | |\n"); } + } + + if (!ret2 || !msolver.okay()){ + //if (S.certifiedOutput != NULL) fprintf(S.certifiedOutput, "0\n"), fclose(S.certifiedOutput); + if (res != NULL) fprintf(res, "UNSAT\n"), fclose(res); + if (msolver.verbosity() > 0){ + printf("c =========================================================================================================\n"); + printf("Solved by unit propagation\n"); + printf("c real time : %g s\n", realTime() - realTimeStart); + printf("c cpu time : %g s\n", cpuTime()); + printf("\n"); } + printf("s UNSATISFIABLE\n"); + exit(20); + } + + // vec dummy; + lbool ret = msolver.solve(); + + + printf("c\n"); + printf("c real time : %g s\n", realTime() - realTimeStart); + printf("c cpu time : %g s\n", cpuTime()); + if (msolver.verbosity() > 0){ + msolver.printFinalStats(); + printf("\n"); } + + //-------------- Result is put in a external file + /* I must admit I have to print the model of one thread... But which one? FIXME !! + if (res != NULL){ + if (ret == l_True){ + fprintf(res, "SAT\n"); + for (int i = 0; i < S.nVars(); i++) + if (S.model[i] != l_Undef) + fprintf(res, "%s%s%d", (i==0)?"":" ", (S.model[i]==l_True)?"":"-", i+1); + fprintf(res, " 0\n"); + }else if (ret == l_False) + fprintf(res, "UNSAT\n"); + else + fprintf(res, "INDET\n"); + fclose(res); + + //-------------- Want certified output + } else { + */ + printf(ret == l_True ? "s SATISFIABLE\n" : ret == l_False ? "s UNSATISFIABLE\n" : "s INDETERMINATE\n"); + + if(msolver.getShowModel() && ret==l_True) { + printf("v "); + for (int i = 0; i < msolver.model.size() ; i++) { + assert(msolver.model[i] != l_Undef); + if (msolver.model[i] != l_Undef) + printf("%s%s%d", (i==0)?"":" ", (msolver.model[i]==l_True)?"":"-", i+1); + } + printf(" 0\n"); + } + + + +#ifdef NDEBUG + exit(ret == l_True ? 10 : ret == l_False ? 20 : 0); // (faster than "return", which will invoke the destructor for 'Solver') +#else + return (ret == l_True ? 10 : ret == l_False ? 20 : 0); +#endif + } catch (OutOfMemoryException&){ + printf("c ===================================================================================================\n"); + printf("INDETERMINATE\n"); + exit(0); + } +} diff --git a/libs/mugen/glucose-syrup-4.1/parallel/Makefile b/libs/mugen/glucose-syrup-4.1/parallel/Makefile new file mode 100644 index 000000000..0da6c2595 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/Makefile @@ -0,0 +1,4 @@ +EXEC = glucose-syrup +DEPDIR = mtl utils core simp +MROOT = $(PWD)/.. +include $(MROOT)/mtl/template.mk diff --git a/libs/mugen/glucose-syrup-4.1/parallel/MultiSolvers.cc b/libs/mugen/glucose-syrup-4.1/parallel/MultiSolvers.cc new file mode 100644 index 000000000..c55a37466 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/MultiSolvers.cc @@ -0,0 +1,699 @@ +/***************************************************************************************[MultiSolvers.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#include +#include "parallel/MultiSolvers.h" +#include "mtl/Sort.h" +#include "utils/System.h" +#include "simp/SimpSolver.h" +#include +#include +#include "parallel/SolverConfiguration.h" + +using namespace Glucose; + +extern const char *_parallel; +extern const char *_cunstable; +// Options at the parallel solver level +static IntOption opt_nbsolversmultithreads(_parallel, "nthreads", "Number of core threads for syrup (0 for automatic)", 0); +static IntOption opt_maxnbsolvers(_parallel, "maxnbthreads", "Maximum number of core threads to ask for (when nbthreads=0)", 4); +static IntOption opt_maxmemory(_parallel, "maxmemory", "Maximum memory to use (in Mb, 0 for no software limit)", 20000); +static IntOption opt_statsInterval(_parallel, "statsinterval", "Seconds (real time) between two stats reports", 5); +// +// Shared with ClausesBuffer.cc +BoolOption opt_whenFullRemoveOlder(_parallel, "removeolder", "When the FIFO for exchanging clauses between threads is full, remove older clauses", false); +IntOption opt_fifoSizeByCore(_parallel, "fifosize", "Size of the FIFO structure for exchanging clauses between threads, by threads", 100000); +// +// Shared options with Solver.cc +BoolOption opt_dontExportDirectReusedClauses(_cunstable, "reusedClauses", "Don't export directly reused clauses", false); +BoolOption opt_plingeling(_cunstable, "plingeling", "plingeling strategy for sharing clauses (exploratory feature)", false); + +#include +#include +#include + + +static inline double cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000; +} + + +void MultiSolvers::informEnd(lbool res) { + result = res; + pthread_cond_broadcast(&cfinished); +} + + +MultiSolvers::MultiSolvers(ParallelSolver *s) : + use_simplification(true), ok(true), maxnbthreads(4), nbthreads(opt_nbsolversmultithreads), nbsolvers(opt_nbsolversmultithreads), nbcompanions(4), nbcompbysolver(2), + allClonesAreBuilt(0), showModel(false), winner(-1), var_decay(1 / 0.95), clause_decay(1 / 0.999), cla_inc(1), var_inc(1), random_var_freq(0.02), restart_first(100), + restart_inc(1.5), learntsize_factor((double) 1 / (double) 3), learntsize_inc(1.1), expensive_ccmin(true), polarity_mode(polarity_false), maxmemory(opt_maxmemory), + maxnbsolvers(opt_maxnbsolvers), verb(0), verbEveryConflicts(10000), numvar(0), numclauses(0) { + result = l_Undef; + SharedCompanion *sc = new SharedCompanion(); + this->sharedcomp = sc; + + // Generate only solver 0. + // It loads the formula + // All others solvers are clone of this one + solvers.push(s); + s->verbosity = 0; // No reportf in solvers... All is done in MultiSolver + s->setThreadNumber(0); + //s->belongsto = this; + s->sharedcomp = sc; + sc->addSolver(s); + assert(solvers[0]->threadNumber() == 0); + + pthread_mutex_init(&m, NULL); //PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_init(&mfinished, NULL); //PTHREAD_MUTEX_INITIALIZER; + pthread_cond_init(&cfinished, NULL); + + if(nbsolvers > 0) + fprintf(stdout, "c %d solvers engines and 1 companion as a blackboard created.\n", nbsolvers); +} + + +MultiSolvers::MultiSolvers() : MultiSolvers(new ParallelSolver(-1)) { + +} + + +MultiSolvers::~MultiSolvers() { } + + +/** + * Generate All solvers + */ + +void MultiSolvers::generateAllSolvers() { + assert(solvers[0] != NULL); + assert(allClonesAreBuilt == 0); + + for(int i = 1; i < nbsolvers; i++) { + ParallelSolver *s = (ParallelSolver *) solvers[0]->clone(); + solvers.push(s); + s->verbosity = 0; // No reportf in solvers... All is done in MultiSolver + s->setThreadNumber(i); + s->sharedcomp = this->sharedcomp; + this->sharedcomp->addSolver(s); + assert(solvers[i]->threadNumber() == i); + } + + adjustParameters(); + + allClonesAreBuilt = 1; +} + + +/** + * Choose solver for threads i (if no given in command line see above) + */ + + +ParallelSolver *MultiSolvers::retrieveSolver(int i) { + return new ParallelSolver(i); +} + + +Var MultiSolvers::newVar(bool sign, bool dvar) { + assert(solvers[0] != NULL); + numvar++; + int v; + sharedcomp->newVar(sign); + if(!allClonesAreBuilt) { // At the beginning we want to generate only solvers 0 + v = solvers[0]->newVar(sign, dvar); + assert(numvar == v + 1); // Just a useless check + } else { + for(int i = 0; i < nbsolvers; i++) { + v = solvers[i]->newVar(sign, dvar); + } + } + return numvar; +} + + +bool MultiSolvers::addClause_(vec &ps) { + assert(solvers[0] != NULL); // There is at least one solver. + // Check if clause is satisfied and remove false/duplicate literals: + if(!okay()) return false; + + sort(ps); + Lit p; + int i, j; + for(i = j = 0, p = lit_Undef; i < ps.size(); i++) + if(solvers[0]->value(ps[i]) == l_True || ps[i] == ~p) + return true; + else if(solvers[0]->value(ps[i]) != l_False && ps[i] != p) + ps[j++] = p = ps[i]; + ps.shrink(i - j); + + + if(ps.size() == 0) { + return ok = false; + } + else if(ps.size() == 1) { + assert(solvers[0]->value(ps[0]) == l_Undef); // TODO : Passes values to all threads + solvers[0]->uncheckedEnqueue(ps[0]); + if(!allClonesAreBuilt) { + return ok = ((solvers[0]->propagate()) == CRef_Undef); // checks only main solver here for propagation constradiction + } + + // Here, all clones are built. + // Gives the unit clause to everybody + for(int i = 0; i < nbsolvers; i++) + solvers[i]->uncheckedEnqueue(ps[0]); + return ok = ((solvers[0]->propagate()) == CRef_Undef); // checks only main solver here for propagation constradiction + } else { + // printf("Adding clause %0xd for solver %d.\n",(void*)c, thn); + // At the beginning only solver 0 load the formula + solvers[0]->addClause(ps); + + if(!allClonesAreBuilt) { + numclauses++; + return true; + } + // Clones are built, need to pass the clause to all the threads + for(int i = 1; i < nbsolvers; i++) { + solvers[i]->addClause(ps); + } + numclauses++; + } + return true; +} + + +bool MultiSolvers::simplify() { + assert(solvers[0] != NULL); // There is at least one solver. + + if(!okay()) return false; + return ok = solvers[0]->simplify(); +} + + +bool MultiSolvers::eliminate() { + + // TODO allow variable elimination when all threads are built! + assert(allClonesAreBuilt == false); + + SimpSolver *s = (SimpSolver *) getPrimarySolver(); + s->use_simplification = use_simplification; + if(!use_simplification) return true; + + return s->eliminate(true); +} + + +// TODO: Use a template here +void *localLaunch(void *arg) { + ParallelSolver *s = (ParallelSolver *) arg; + + (void) s->solve(); + + pthread_exit(NULL); +} + + +#define MAXIMUM_SLEEP_DURATION 5 + + +void MultiSolvers::printStats() { + static int nbprinted = 1; + double cpu_time = cpuTime(); + printf("c\n"); + + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + printf("c | id | starts | decisions | confls | Init T | learnts | exported | imported | promoted | %% | \n"); + printf("c |-------------------------------------------------------------------------------------------------------|\n"); + + //printf("%.0fs | ",cpu_time); + for(int i = 0; i < solvers.size(); i++) { + solvers[i]->reportProgress(); + //printf(" %2d: %12ld confl. |", i, (long int) solvers[i]->conflicts); + } + long long int totalconf = 0; + long long int totalprop = 0; + for(int i = 0; i < solvers.size(); i++) { + totalconf += (long int) solvers[i]->conflicts; + totalprop += solvers[i]->propagations; + } + printf("c \n"); + + printf("c synthesis %11lld conflicts %11lld propagations %8.0f conflicts/sec %8.0f propagations/sec\n", + totalconf, totalprop, (double) totalconf / cpu_time, (double) totalprop / cpu_time); + + + nbprinted++; +} + + +// Still a ugly function... To be rewritten with some statistics class some day +void MultiSolvers::printFinalStats() { + sharedcomp->printStats(); + printf("c\nc\n"); + printf("c\n"); + printf("c |---------------------------------------- FINAL STATS --------------------------------------------------|\n"); + printf("c\n"); + + printf("c |---------------|-----------------"); + for(int i = 0; i < solvers.size(); i++) + printf("|------------"); + printf("|\n"); + + printf("c | Threads | Total "); + for(int i = 0; i < solvers.size(); i++) { + printf("| %10d ", i); + } + printf("|\n"); + + printf("c |---------------|-----------------"); + for(int i = 0; i < solvers.size(); i++) + printf("|------------"); + printf("|\n"); + + +//-- + printf("c | Conflicts "); + long long int totalconf = 0; + for(int i = 0; i < solvers.size(); i++) + totalconf += solvers[i]->conflicts; + printf("| %15lld ", totalconf); + + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->conflicts); + printf("|\n"); + + //-- + printf("c | Decisions "); + long long int totaldecs = 0; + for(int i = 0; i < solvers.size(); i++) + totaldecs += solvers[i]->decisions; + printf("| %15lld ", totaldecs); + + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->decisions); + printf("|\n"); + + //-- + printf("c | Propagations "); + long long int totalprops = 0; + for(int i = 0; i < solvers.size(); i++) + totalprops += solvers[i]->propagations; + printf("| %15lld ", totalprops); + + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->propagations); + printf("|\n"); + + + printf("c | Avg_Trail "); + printf("| "); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->conflicts==0 ? 0 : solvers[i]->stats[sumTrail] / solvers[i]->conflicts); + printf("|\n"); + + //-- + printf("c | Avg_DL "); + printf("| "); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->conflicts==0 ? 0 : solvers[i]->stats[sumDecisionLevels] / solvers[i]->conflicts); + printf("|\n"); + + //-- + printf("c | Avg_Res "); + printf("| "); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->conflicts==0 ? 0 : solvers[i]->stats[sumRes] / solvers[i]->conflicts); + printf("|\n"); + + //-- + printf("c | Avg_Res_Seen "); + printf("| "); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->conflicts==0 ? 0 : solvers[i]->stats[sumResSeen] / solvers[i]->conflicts); + printf("|\n"); + + //-- + + printf("c |---------------|-----------------"); + for(int i = 0; i < solvers.size(); i++) + printf("|------------"); + printf("|\n"); + + printf("c | Exported "); + uint64_t exported = 0; + for(int i = 0; i < solvers.size(); i++) + exported += solvers[i]->stats[nbexported]; + printf("| %15" PRIu64" ", exported); + + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->stats[nbexported]); + printf("|\n"); +//-- + printf("c | Imported "); + uint64_t imported = 0; + for(int i = 0; i < solvers.size(); i++) + imported += solvers[i]->stats[nbimported]; + printf("| %15" PRIu64" ", imported); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->stats[nbimported]); + printf("|\n"); +//-- + + printf("c | Good "); + uint64_t importedGood = 0; + for(int i = 0; i < solvers.size(); i++) + importedGood += solvers[i]->stats[nbImportedGoodClauses]; + printf("| %15" PRIu64" ", importedGood); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->stats[nbImportedGoodClauses]); + printf("|\n"); +//-- + + printf("c | Purge "); + uint64_t importedPurg = 0; + for(int i = 0; i < solvers.size(); i++) + importedPurg += solvers[i]->stats[nbimportedInPurgatory]; + printf("| %15" PRIu64" ", importedPurg); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->stats[nbimportedInPurgatory]); + printf("|\n"); +//-- + + printf("c | Promoted "); + uint64_t promoted = 0; + for(int i = 0; i < solvers.size(); i++) + promoted += solvers[i]->stats[nbPromoted]; + printf("| %15" PRIu64" ", promoted); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->stats[nbPromoted]); + printf("|\n"); +//-- + + printf("c | Remove_Imp "); + uint64_t removedimported = 0; + for(int i = 0; i < solvers.size(); i++) + removedimported += solvers[i]->stats[nbRemovedUnaryWatchedClauses]; + printf("| %15" PRIu64" ", removedimported); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->stats[nbRemovedUnaryWatchedClauses]); + printf("|\n"); +//-- + + printf("c | Blocked_Reuse "); + uint64_t blockedreused = 0; + for(int i = 0; i < solvers.size(); i++) + blockedreused += solvers[i]->nbNotExportedBecauseDirectlyReused; + printf("| %15" PRIu64" ", blockedreused); + for(int i = 0; i < solvers.size(); i++) + printf("| %10" PRIu64" ", solvers[i]->nbNotExportedBecauseDirectlyReused); + printf("|\n"); +//-- + printf("c |---------------|-----------------"); + for(int i = 0; i < solvers.size(); i++) + printf("|------------"); + printf("|\n"); + + printf("c | Unaries "); + printf("| "); + for(int i = 0; i < solvers.size(); i++) { + printf("| %10" PRIu64" ", solvers[i]->stats[nbUn]); + } + printf("|\n"); +//-- + + printf("c | Binaries "); + printf("| "); + for(int i = 0; i < solvers.size(); i++) { + printf("| %10" PRIu64" ", solvers[i]->stats[nbBin]); + } + printf("|\n"); +//-- + + + printf("c | Glues "); + printf("| "); + for(int i = 0; i < solvers.size(); i++) { + printf("| %10" PRIu64" ", solvers[i]->stats[nbDL2]); + } + printf("|\n"); +//-- + + printf("c |---------------|-----------------"); + for(int i = 0; i < solvers.size(); i++) + printf("|------------"); + printf("|\n"); + + printf("c | Orig_Seen "); + uint64_t origseen = 0; + + for(int i = 0; i < solvers.size(); i++) { + origseen += solvers[i]->stats[originalClausesSeen]; + } + printf("| %13" PRIu64" %% ", origseen * 100 / nClauses() / solvers.size()); + + for(int i = 0; i < solvers.size(); i++) { + printf("| %10" PRIu64" ", solvers[i]->stats[originalClausesSeen]); + } + + printf("|\n"); + + + int winner = -1; + for(int i = 0; i < solvers.size(); i++) { + if(sharedcomp->winner() == solvers[i]) + winner = i; + } + +//-- + if(winner != -1) { + printf("c | Diff Orig seen"); + printf("| "); + + for(int i = 0; i < solvers.size(); i++) { + if(i == winner) { + printf("| X "); + continue; + } + if(solvers[i]->stats[originalClausesSeen] > solvers[winner]->stats[originalClausesSeen]) + printf("| %10" PRIu64" ", solvers[i]->stats[originalClausesSeen] - solvers[winner]->stats[originalClausesSeen]); + else + printf("| -%9" PRIu64" ", solvers[winner]->stats[originalClausesSeen] - solvers[i]->stats[originalClausesSeen]); + + } + + printf("|\n"); + } + + +//-- + + if(winner != -1) { + int sum = 0; + printf("c | Hamming "); + for(int i = 0; i < solvers.size(); i++) { + if(i == winner) + continue; + int nb = 0; + for(int j = 0; j < nVars(); j++) { + if(solvers[i]->valuePhase(j) != solvers[winner]->valuePhase(j)) nb++; + } + sum += nb; + + } + sum = sum / (solvers.size() > 1 ? solvers.size() - 1 : 1); + + printf("| %13d %% ", sum * 100 / nVars()); + + for(int i = 0; i < solvers.size(); i++) { + if(i == winner) { + printf("| X "); + continue; + } + int nb = 0; + for(int j = 0; j < nVars(); j++) { + if(solvers[i]->valuePhase(j) != solvers[winner]->valuePhase(j)) nb++; + } + printf("| %10d ", nb); + sum += nb; + + } + printf("|\n"); + } + + printf("c |---------------|-----------------"); + for(int i = 0; i < solvers.size(); i++) + printf("|------------"); + printf("|\n"); + + +} + + +// Well, all those parameteres are just naive guesses... No experimental evidences for this. +void MultiSolvers::adjustParameters() { + SolverConfiguration::configure(this, nbsolvers); +} + + +void MultiSolvers::adjustNumberOfCores() { + float mem = memUsed(); + if(nbthreads == 0) { // Automatic configuration + if(verb >= 1) + printf("c | Automatic Adjustement of the number of solvers. MaxMemory=%5d, MaxCores=%3d. |\n", maxmemory, maxnbsolvers); + unsigned int tmpnbsolvers = maxmemory * 4 / 10 / mem; + if(tmpnbsolvers > maxnbsolvers) tmpnbsolvers = maxnbsolvers; + if(tmpnbsolvers < 1) tmpnbsolvers = 1; + if(verb >= 1) + printf("c | One Solver is taking %.2fMb... Let's take %d solvers for this run (max 40%% of the maxmemory). |\n", mem, tmpnbsolvers); + nbsolvers = tmpnbsolvers; + nbthreads = nbsolvers; + } else { + assert(nbthreads == nbsolvers); + } +} + + +lbool MultiSolvers::solve() { + pthread_attr_t thAttr; + int i; + + adjustNumberOfCores(); + sharedcomp->setNbThreads(nbsolvers); + if(verb >= 1) + printf("c | Generating clones |\n"); + generateAllSolvers(); + if(verb >= 1) { + printf("c |  all clones generated. Memory = %6.2fMb. |\n", memUsed()); + printf("c ========================================================================================================|\n"); + } + + + model.clear(); + + /* Initialize and set thread detached attribute */ + pthread_attr_init(&thAttr); + pthread_attr_setdetachstate(&thAttr, PTHREAD_CREATE_JOINABLE); + + + + // Launching all solvers + for(i = 0; i < nbsolvers; i++) { + pthread_t *pt = (pthread_t *) malloc(sizeof(pthread_t)); + threads.push(pt); + solvers[i]->pmfinished = &mfinished; + solvers[i]->pcfinished = &cfinished; + pthread_create(threads[i], &thAttr, &localLaunch, (void *) solvers[i]); + } + + bool done = false; + bool adjustedlimitonce = false; + + (void) pthread_mutex_lock(&m); + while(!done) { + struct timespec timeout; + time(&timeout.tv_sec); + timeout.tv_sec += MAXIMUM_SLEEP_DURATION; + timeout.tv_nsec = 0; + if(pthread_cond_timedwait(&cfinished, &mfinished, &timeout) != ETIMEDOUT) + done = true; + else + printStats(); + + float mem = memUsed(); + if(verb >= 1) printf("c Total Memory so far : %.2fMb\n", mem); + if((maxmemory > 0) && (mem > maxmemory) && !sharedcomp->panicMode) + printf("c ** reduceDB switching to Panic Mode due to memory limitations !\n"), sharedcomp->panicMode = true; + + if(!done && !adjustedlimitonce) { + uint64_t sumconf = 0; + uint64_t sumimported = 0; + for(int i = 0; i < nbsolvers; i++) { + sumconf += solvers[i]->conflicts; + sumimported += solvers[i]->stats[nbimported]; + } + if(sumconf > 10000000 && sumimported > 4 * sumconf) { // too many many imported clauses (after a while) + for(int i = 0; i < nbsolvers; i++) { // we have like 32 threads, so we need to export just very good clauses + solvers[i]->goodlimitlbd -= 2; + solvers[i]->goodlimitsize -= 4; + } + adjustedlimitonce = true; + printf("c adjusting (once) the limits to send fewer clauses.\n"); + } + } + } + + (void) pthread_mutex_unlock(&m); + + for(i = 0; i < nbsolvers; i++) { // Wait for all threads to finish + pthread_join(*threads[i], NULL); + } + + assert(sharedcomp != NULL); + result = sharedcomp->jobStatus; + if(result == l_True) { + sharedcomp->jobFinishedBy->extendModel(); + int n = sharedcomp->jobFinishedBy->nVars(); + model.growTo(n); + for(int i = 0; i < n; i++) { + model[i] = sharedcomp->jobFinishedBy->model[i]; + assert(model[i] != l_Undef); + } + } + + + return result; + /* + for(int i=0;i& ps); // Add a clause to the solver. NOTE! 'ps' may be shrunk by this method! + bool addClause_( vec& ps); + + bool simplify (); // Removes already satisfied clauses. + + int nVars () const; // The current number of variables. + int nClauses () const; // The current number of variables. + ParallelSolver *getPrimarySolver(); + + void generateAllSolvers(); + + // Solving: + // + lbool solve (); // Search without assumptions. + bool eliminate(); // Perform variable elimination + void adjustParameters(); + void adjustNumberOfCores(); + void interrupt() {} + vec model; // If problem is satisfiable, this vector contains the model (if any). + inline bool okay() { + if(!ok) return ok; + for(int i = 0;iokay()) { + ok = false; + return false; + } + } + return true; + + } + + bool use_simplification; + + + protected: + friend class ParallelSolver; + friend class SolverCompanion; + +struct Stats { + uint64_t min, max, avg, std, med; + Stats(uint64_t _min = 0,uint64_t _max = 0,uint64_t _avg = 0,uint64_t _std = 0,uint64_t _med = 0) : + min(_min), max(_max), avg(_avg), std(_std), med(_med) {} +}; + + void printStats(); + int ok; + lbool result; + int maxnbthreads; // Maximal number of threads + int nbthreads; // Current number of threads + int nbsolvers; // Number of CDCL solvers + int nbcompanions; // Number of companions + int nbcompbysolver; // Number of companions by solvers + bool immediateSharingGlue ; + int allClonesAreBuilt; + bool showModel; // show model on/off + + int winner; + + vec add_tmp; + + double var_decay; // Inverse of the variable activity decay factor. (default 1 / 0.95) + double clause_decay; // Inverse of the clause activity decay factor. (1 / 0.999) + double cla_inc; // Amount to bump next clause with. + double var_inc; // Amount to bump next variable with. + double random_var_freq; // The frequency with which the decision heuristic tries to choose a random variable. (default 0.02) + int restart_first; // The initial restart limit. (default 100) + double restart_inc; // The factor with which the restart limit is multiplied in each restart. (default 1.5) + double learntsize_factor; // The intitial limit for learnt clauses is a factor of the original clauses. (default 1 / 3) + double learntsize_inc; // The limit for learnt clauses is multiplied with this factor each restart. (default 1.1) + bool expensive_ccmin; // Controls conflict clause minimization. (default TRUE) + int polarity_mode; // Controls which polarity the decision heuristic chooses. See enum below for allowed modes. (default polarity_false) + unsigned int maxmemory; + unsigned int maxnbsolvers; + int verb; + int verbEveryConflicts; + int numvar; // Number of variables + int numclauses; // Number of clauses + + enum { polarity_true = 0, polarity_false = 1, polarity_user = 2, polarity_rnd = 3 }; + + //ClauseAllocator ca; + SharedCompanion * sharedcomp; + + void informEnd(lbool res); + ParallelSolver* retrieveSolver(int i); + + pthread_mutex_t m; // mutex for any high level sync between all threads (like reportf) + pthread_mutex_t mfinished; // mutex on which main process may wait for... As soon as one process finishes it release the mutex + pthread_cond_t cfinished; // condition variable that says that a thread has finished + + vec solvers; // set of plain solvers + vec solvercompanions; // set of companion solvers + vec threads; // all threads of this process + vec threadIndexOfSolver; // threadIndexOfSolver[solvers[i]] is the index in threads[] of the solver i + vec threadIndexOfSolverCompanion; // threadIndexOfSolverCompanion[solvercompanions[i]] is the index in threads[] of the solvercompanion i +}; + +inline bool MultiSolvers::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } + +inline void MultiSolvers::setVerbosity(int i) {verb = i;} +inline void MultiSolvers::setVerbEveryConflicts(int i) {verbEveryConflicts=i;} +inline int MultiSolvers::nVars () const { return numvar; } +inline int MultiSolvers::nClauses () const { return numclauses; } +inline int MultiSolvers::verbosity() {return verb;} +inline ParallelSolver* MultiSolvers::getPrimarySolver() {return solvers[0];} + + +} +#endif + diff --git a/libs/mugen/glucose-syrup-4.1/parallel/ParallelSolver.cc b/libs/mugen/glucose-syrup-4.1/parallel/ParallelSolver.cc new file mode 100644 index 000000000..260dcec99 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/ParallelSolver.cc @@ -0,0 +1,511 @@ +/***************************************************************************************[ParallelSolver.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#include "parallel/ParallelSolver.h" +#include "mtl/Sort.h" + +using namespace Glucose; + +//===================================================================== +// == Options + +const char* _cunstable = "CORE/PARALLEL -- UNSTABLE FEATURES"; +const char* _parallel = "PARALLEL"; + +extern BoolOption opt_dontExportDirectReusedClauses; // (_cunstable, "reusedClauses", "Don't export directly reused clauses", false); +extern BoolOption opt_plingeling; // (_cunstable, "plingeling", "plingeling strategy for sharing clauses (exploratory feature)", false); + +//===================================================================== + +//===================================================================== + + +ParallelSolver::ParallelSolver(int threadId) : + SimpSolver() +, thn(threadId) // The thread number of this solver +, goodlimitlbd(7) +, goodlimitsize(25) +, purgatory(true) +, shareAfterProbation(!opt_plingeling) // only share clauses after probation +, plingeling(opt_plingeling) +, nbTimesSeenBeforeExport(2) +, firstSharing(5000) // Strong limit : do not share anything (except unary clauses) before this number of conflicts +, limitSharingByGoodLBD(true) // Moving limit of what a good LBD is (median value of last learnt clauses set) +, limitSharingByFixedLimitLBD(0) // No fixed bound (like 8 in plingeling) +, limitSharingByFixedLimitSize(0) // No fixed boud (like 40 in plingeling) +, dontExportDirectReusedClauses(opt_dontExportDirectReusedClauses) +, nbNotExportedBecauseDirectlyReused(0) +{ + useUnaryWatched = true; // We want to use promoted clauses here ! + stats.growTo(parallelStatsSize,0); +} + + + + +ParallelSolver::~ParallelSolver() { + printf("c Solver of thread %d ended.\n", thn); + fflush(stdout); +} + +ParallelSolver::ParallelSolver(const ParallelSolver &s) : + SimpSolver(s) + , sharedcomp(s.sharedcomp) +, goodlimitlbd(s.goodlimitlbd) +, goodlimitsize(s.goodlimitsize) +, purgatory(s.purgatory) +, shareAfterProbation(s.shareAfterProbation) // only share clauses after probation +, plingeling(s.plingeling) +,nbTimesSeenBeforeExport(2) +, firstSharing(s.firstSharing) // Strong limit : do not share anything (except unary clauses) before this number of conflicts +, limitSharingByGoodLBD(s.limitSharingByGoodLBD) // Moving limit of what a good LBD is (median value of last learnt clauses set) +, limitSharingByFixedLimitLBD(s.limitSharingByFixedLimitLBD) // No fixed bound (like 8 in plingeling) +, limitSharingByFixedLimitSize(s.limitSharingByFixedLimitSize) // No fixed boud (like 40 in plingeling) +, dontExportDirectReusedClauses(s.dontExportDirectReusedClauses) +, nbNotExportedBecauseDirectlyReused(s.nbNotExportedBecauseDirectlyReused) +{ + s.goodImportsFromThreads.memCopyTo(goodImportsFromThreads); + useUnaryWatched = s.useUnaryWatched; + s.stats.copyTo(stats); + s.elimclauses.copyTo(elimclauses); // This should be done more efficiently some day +} + + +// Strategy to reduce unary watches list +struct reduceDB_oneWatched_lt { + ClauseAllocator& ca; + + reduceDB_oneWatched_lt(ClauseAllocator& ca_) : ca(ca_) { + } + + bool operator()(CRef x, CRef y) { + + // Main criteria... Like in MiniSat we keep all binary clauses + if (ca[x].size() > 2 && ca[y].size() == 2) return 1; + + if (ca[y].size() > 2 && ca[x].size() == 2) return 0; + if (ca[x].size() == 2 && ca[y].size() == 2) return 0; + + // Second one based on literal block distance + if (ca[x].size() > ca[y].size()) return 1; + if (ca[x].size() < ca[y].size()) return 0; + + if (ca[x].lbd() > ca[y].lbd()) return 1; + if (ca[x].lbd() < ca[y].lbd()) return 0; + + // Finally we can use old activity or size, we choose the last one + return ca[x].activity() < ca[y].activity(); + //return x->size() < y->size(); + + //return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + } +}; + +// @overide +void ParallelSolver::reduceDB() { + + int i, j; + stats[nbReduceDB]++; + + int limit; + + if (chanseokStrategy) + sort(learnts, reduceDBAct_lt(ca)); + else + sort(learnts, reduceDB_lt(ca)); + + if (!chanseokStrategy && !panicModeIsEnabled()) { + // We have a lot of "good" clauses, it is difficult to compare them. Keep more ! + if (ca[learnts[learnts.size() / RATIOREMOVECLAUSES]].lbd() <= 3) nbclausesbeforereduce += specialIncReduceDB; + // Useless :-) + if (ca[learnts.last()].lbd() <= 5) nbclausesbeforereduce += specialIncReduceDB; + } + // Don't delete binary or locked clauses. From the rest, delete clauses from the first half + // Keep clauses which seem to be usefull (their lbd was reduce during this sequence) + + if (!panicModeIsEnabled()) { + limit = learnts.size() / 2; + } else { + limit = panicModeLastRemoved; + } + panicModeLastRemoved = 0; + + uint64_t sumsize = 0; + for (i = j = 0; i < learnts.size(); i++) { + + Clause& c = ca[learnts[i]]; + if (i == learnts.size() / 2) + goodlimitlbd = c.lbd(); + sumsize += c.size(); + if (c.lbd() > 2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(learnts[i]); + stats[nbRemovedClauses]++; + panicModeLastRemoved++; + } else { + if (!c.canBeDel()) limit++; //we keep c, so we can delete an other clause + c.setCanBeDel(true); // At the next step, c can be delete + learnts[j++] = learnts[i]; + } + } + learnts.shrink(i - j); + + if (learnts.size() > 0) + goodlimitsize = 1 + (double) sumsize / (double) learnts.size(); + + // Special treatment for imported clauses + if (!panicModeIsEnabled()) + limit = unaryWatchedClauses.size() - (learnts.size() * (chanseokStrategy?4:2)); + else + limit = panicModeLastRemovedShared; + panicModeLastRemovedShared = 0; + if ((unaryWatchedClauses.size() > 100) && (limit > 0)) { + + sort(unaryWatchedClauses, reduceDB_oneWatched_lt(ca)); + + for (i = j = 0; i < unaryWatchedClauses.size(); i++) { + Clause& c = ca[unaryWatchedClauses[i]]; + if (c.lbd() > 2 && c.size() > 2 && c.canBeDel() && !locked(c) && (i < limit)) { + removeClause(unaryWatchedClauses[i], c.getOneWatched()); // remove from the purgatory (or not) + stats[nbRemovedUnaryWatchedClauses]++; + panicModeLastRemovedShared++; + } else { + if (!c.canBeDel()) limit++; //we keep c, so we can delete an other clause + c.setCanBeDel(true); // At the next step, c can be delete + unaryWatchedClauses[j++] = unaryWatchedClauses[i]; + } + } + unaryWatchedClauses.shrink(i - j); + } + + checkGarbage(); +} + + +/*_________________________________________________________________________________________________ +| +| parallelImportClauseDuringConflictAnalysis : (Clause &c,CRef confl) -> [void] +| +| Description: +| Verify if the clause using during conflict analysis is good for export +| @see : analyze +| Output: +|________________________________________________________________________________________________@*/ + + +void ParallelSolver::parallelImportClauseDuringConflictAnalysis(Clause &c,CRef confl) { + if (dontExportDirectReusedClauses && (confl == lastLearntClause) && (c.getExported() < nbTimesSeenBeforeExport)) { // Experimental stuff + c.setExported(nbTimesSeenBeforeExport); + nbNotExportedBecauseDirectlyReused++; + } else if (shareAfterProbation && c.getExported() != nbTimesSeenBeforeExport && conflicts > firstSharing) { + c.setExported(c.getExported() + 1); + if (!c.wasImported() && c.getExported() == nbTimesSeenBeforeExport) { // It's a new interesting clause: + if (c.lbd() == 2 || (c.size() < goodlimitsize && c.lbd() <= goodlimitlbd)) { + shareClause(c); + } + } + } + +} + + + +// These Two functions are useless here !! +void ParallelSolver::reportProgress() { + printf("c | %2d | %6d | %10d | %10d | %8d | %8d | %8d | %8d | %8d | %6.3f |\n",(int)thn,(int)starts,(int)decisions,(int)conflicts,(int)stats[originalClausesSeen],(int)learnts.size(),(int)stats[nbexported],(int)stats[nbimported],(int)stats[nbPromoted],progressEstimate()*100); + + //printf("c thread=%d confl=%lld starts=%llu reduceDB=%llu learnts=%d broadcast=%llu blockedReuse=%lld imported=%llu promoted=%llu limitlbd=%llu limitsize=%llu\n", thn, conflicts, starts, nbReduceDB, learnts.size(), nbexported, nbNotExportedBecauseDirectlyReused, nbimported, nbPromoted, goodlimitlbd, goodlimitsize); +} + +void ParallelSolver::reportProgressArrayImports(vec &totalColumns) { + return ; // TODO : does not currently work + unsigned int totalImports = 0; + printf("c %3d | ", thn); + for (int i = 0; i < sharedcomp->nbThreads; i++) { + totalImports += goodImportsFromThreads[i]; + totalColumns[i] += goodImportsFromThreads[i]; + printf(" %8d", goodImportsFromThreads[i]); + } + printf(" | %8d\n", totalImports); + +} + + + +/*_________________________________________________________________________________________________ +| +| shareClause : (Clause &c) -> [bool] +| +| Description: +| share a clause to other cores +| @see : analyze +| Output: true if the clause is indeed sent +|________________________________________________________________________________________________@*/ + +bool ParallelSolver::shareClause(Clause & c) { + bool sent = sharedcomp->addLearnt(this, c); + if (sent) + stats[nbexported]++; + return sent; +} + +/*_________________________________________________________________________________________________ +| +| panicModeIsEnabled : () -> [bool] +| +| Description: +| is panic mode (save memory) is enabled ? +|________________________________________________________________________________________________@*/ + +bool ParallelSolver::panicModeIsEnabled() { + return sharedcomp->panicMode; +} + +/*_________________________________________________________________________________________________ +| +| parallelImportUnaryClauses : () -> [void] +| +| Description: +| import all unary clauses from other cores +|________________________________________________________________________________________________@*/ + +void ParallelSolver::parallelImportUnaryClauses() { + Lit l; + while ((l = sharedcomp->getUnary(this)) != lit_Undef) { + if (value(var(l)) == l_Undef) { + uncheckedEnqueue(l); + stats[nbimportedunit]++; + } + } +} + +/*_________________________________________________________________________________________________ +| +| parallelImportClauses : () -> [bool] +| +| Description: +| import all clauses from other cores +| Output : if there is a final conflict +|________________________________________________________________________________________________@*/ + +bool ParallelSolver::parallelImportClauses() { + + assert(decisionLevel() == 0); + int importedFromThread; + while (sharedcomp->getNewClause(this, importedFromThread, importedClause)) { + assert(importedFromThread <= sharedcomp->nbThreads); + assert(importedFromThread >= 0); + + assert(importedFromThread != thn); + + if (importedClause.size() == 0) + return true; + + //printf("Thread %d imports clause from thread %d\n", threadNumber(), importedFromThread); + CRef cr = ca.alloc(importedClause, true, true); + ca[cr].setLBD(importedClause.size()); + if (plingeling) // 0 means a broadcasted clause (good clause), 1 means a survivor clause, broadcasted + ca[cr].setExported(2); // A broadcasted clause (or a survivor clause) do not share it anymore + else { + ca[cr].setExported(1); // next time we see it in analyze, we share it (follow route / broadcast depending on the global strategy, part of an ongoing experimental stuff: a clause in one Watched will be set to exported 2 when promotted. + } + ca[cr].setImportedFrom(importedFromThread); + if(useUnaryWatched) + unaryWatchedClauses.push(cr); + else + learnts.push(cr); + + if (plingeling || ca[cr].size() <= 2) {//|| importedRoute == 0) { // importedRoute == 0 means a glue clause in another thread (or any very good clause) + ca[cr].setOneWatched(false); // Warning: those clauses will never be promoted by a conflict clause (or rarely: they are propagated!) + attachClause(cr); + stats[nbImportedGoodClauses]++; + } else { + if(useUnaryWatched) { + attachClausePurgatory(cr); // + ca[cr].setOneWatched(true); + } else { + attachClause(cr); + ca[cr].setOneWatched(false); + } + stats[nbimportedInPurgatory]++; + } + assert(ca[cr].learnt()); + stats[nbimported]++; + } + return false; +} + + +/*_________________________________________________________________________________________________ +| +| parallelExportUnaryClause : (Lit p) -> [void] +| +| Description: +| export unary clauses to other cores +|________________________________________________________________________________________________@*/ + +void ParallelSolver::parallelExportUnaryClause(Lit p) { + // Multithread + sharedcomp->addLearnt(this,p ); // TODO: there can be a contradiction here (two theads proving a and -a) + stats[nbexportedunit]++; +} + + +/*_________________________________________________________________________________________________ +| +| parallelExportClauseDuringSearch : (Clause &c) -> [void] +| +| Description: +| Verify if a new learnt clause is useful for export +| @see search +| +|________________________________________________________________________________________________@*/ + +void ParallelSolver::parallelExportClauseDuringSearch(Clause &c) { + // + // Multithread + // Now I'm sharing the clause if seen in at least two conflicts analysis shareClause(ca[cr]); + if ((plingeling && !shareAfterProbation && c.lbd() < 8 && c.size() < 40) || + (c.lbd() <= 2)) { // For this class of clauses, I'm sharing them asap (they are Glue CLauses, no probation for them) + shareClause(c); + c.setExported(2); + } + +} + + +/*_________________________________________________________________________________________________ +| +| parallelJobIsFinished : () -> [bool] +| +| Description: +| Is a core already finish the search +| +|________________________________________________________________________________________________@*/ + +bool ParallelSolver::parallelJobIsFinished() { + // Parallel: another job has finished let's quit + return (sharedcomp->jobFinished()); +} + +// @overide +lbool ParallelSolver::solve_(bool do_simp, bool turn_off_simp) { + vec extra_frozen; + lbool result = l_True; + do_simp &= use_simplification; + if (do_simp){ + // Assumptions must be temporarily frozen to run variable elimination: + for (int i = 0; i < assumptions.size(); i++){ + Var v = var(assumptions[i]); + + // If an assumption has been eliminated, remember it. + assert(!isEliminated(v)); + + if (!frozen[v]){ + // Freeze and store. + setFrozen(v, true); + extra_frozen.push(v); + } } + + result = lbool(eliminate(turn_off_simp)); + } + + model.clear(); + conflict.clear(); + if (!ok) return l_False; + + solves++; + + + lbool status = l_Undef; + + // Search: + int curr_restarts = 0; + while (status == l_Undef && !sharedcomp->jobFinished()) { + status = search(luby_restart?luby(restart_inc, curr_restarts)*luby_restart_factor:0); // the parameter is useless in glucose, kept to allow modifications + if (!withinBudget()) break; + curr_restarts++; + } + + if (verbosity >= 1) + printf("c =========================================================================================================\n"); + +/* + if (do_simp) + // Unfreeze the assumptions that were frozen: + for (int i = 0; i < extra_frozen.size(); i++) + setFrozen(extra_frozen[i], false); +*/ + + bool firstToFinish = false; + if (status != l_Undef) + firstToFinish = sharedcomp->IFinished(this); + if (firstToFinish) { + printf("c Thread %d is 100%% pure glucose! First thread to finish! (%s answer).\n", threadNumber(), status == l_True ? "SAT" : status == l_False ? "UNSAT" : "UNKOWN"); + sharedcomp->jobStatus = status; + } + + if (firstToFinish && status == l_True) { + extendModel(); + + + // Extend & copy model: + model.growTo(nVars()); + for (int i = 0; i < nVars(); i++) model[i] = value(i); + } else if (status == l_False && conflict.size() == 0) + ok = false; + + + pthread_cond_signal(pcfinished); + + //cancelUntil(0); + + + return status; + +} diff --git a/libs/mugen/glucose-syrup-4.1/parallel/ParallelSolver.h b/libs/mugen/glucose-syrup-4.1/parallel/ParallelSolver.h new file mode 100644 index 000000000..38e826d91 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/ParallelSolver.h @@ -0,0 +1,152 @@ +/**************************************************************************************[ParallelSolver.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#ifndef PARALLELSOLVER_H +#define PARALLELSOLVER_H + +#include "core/SolverTypes.h" +#include "core/Solver.h" +#include "simp/SimpSolver.h" +#include "parallel/SharedCompanion.h" +namespace Glucose { + + enum ParallelStats{ + nbexported=coreStatsSize, + nbimported, + nbexportedunit, + nbimportedunit, + nbimportedInPurgatory, + nbImportedGoodClauses + } ; +#define parallelStatsSize (coreStatsSize + 6) + +//================================================================================================= + //class MultiSolvers; + //class SolverCompanion; + // class MultiSolvers; + +class ParallelSolver : public SimpSolver { + friend class MultiSolvers; + friend class SolverCompanion; + friend class SharedCompanion; +// friend class ReasoningCompanion; +// friend class SolverConfiguration; + +protected : + // Multithread : + int thn; // internal thread number + //MultiSolvers* belongsto; // Not working (due to incomplete types) + SharedCompanion *sharedcomp; + bool coreFUIP; // true if one core is specialized for branching on all FUIP + bool ImTheSolverFUIP; + pthread_mutex_t *pmfinished; // mutex on which main process may wait for... As soon as one process finishes it release the mutex + pthread_cond_t *pcfinished; // condition variable that says that a thread as finished + +public: + // Constructor/Destructor: + // + ParallelSolver(int threadId); + ParallelSolver(const ParallelSolver &s); + ~ParallelSolver(); + + /** + * Clone function + */ + virtual Clone* clone() const { + return new ParallelSolver(*this); + } + + int threadNumber () const; + void setThreadNumber (int i); + void reportProgress(); + void reportProgressArrayImports(vec &totalColumns); + virtual void reduceDB(); + virtual lbool solve_ (bool do_simp = true, bool turn_off_simp = false); + + vec importedClause; // Temporary clause used to copy each imported clause + unsigned int goodlimitlbd; // LBD score of the "good" clauses, locally + int goodlimitsize; + bool purgatory; // mode of operation + bool shareAfterProbation; // Share any none glue clause only after probation (seen 2 times in conflict analysis) + bool plingeling; // plingeling strategy for sharing clauses (experimental) + int nbTimesSeenBeforeExport; + // Stats front end +// uint64_t getNbExported() { return nbexported;} + // uint64_t getNbImported() { return nbimported;} + // uint64_t getNbExportedUnit() {return nbexportedunit;} + + uint32_t firstSharing, limitSharingByGoodLBD, limitSharingByFixedLimitLBD, limitSharingByFixedLimitSize; + uint32_t probationByFollowingRoads, probationByFriend; + uint32_t survivorLayers; // Number of layers for a common clause to survive + bool dontExportDirectReusedClauses ; // When true, directly reused clauses are not exported + uint64_t nbNotExportedBecauseDirectlyReused; + + + vec goodImportsFromThreads; // Stats of good importations from other threads + + virtual void parallelImportClauseDuringConflictAnalysis(Clause &c,CRef confl); + virtual bool parallelImportClauses(); // true if the empty clause was received + virtual void parallelImportUnaryClauses(); + virtual void parallelExportUnaryClause(Lit p); + virtual void parallelExportClauseDuringSearch(Clause &c); + virtual bool parallelJobIsFinished(); + virtual bool panicModeIsEnabled(); + + bool shareClause(Clause & c); // true if the clause was succesfully sent + + + +}; + + + inline int ParallelSolver::threadNumber () const {return thn;} + inline void ParallelSolver::setThreadNumber (int i) {thn = i;} +} +#endif /* PARALLELSOLVER_H */ + diff --git a/libs/mugen/glucose-syrup-4.1/parallel/SharedCompanion.cc b/libs/mugen/glucose-syrup-4.1/parallel/SharedCompanion.cc new file mode 100644 index 000000000..dadc6724b --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/SharedCompanion.cc @@ -0,0 +1,167 @@ +/***************************************************************************************[SharedCompanion.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#include "core/Solver.h" +#include "parallel/ParallelSolver.h" +#include "core/SolverTypes.h" +#include "parallel/ClausesBuffer.h" +#include "parallel/SharedCompanion.h" + + +using namespace Glucose; + +SharedCompanion::SharedCompanion(int _nbThreads) : + nbThreads(_nbThreads), + bjobFinished(false), + jobFinishedBy(NULL), + panicMode(false), // The bug in the SAT2014 competition :) + jobStatus(l_Undef), + random_seed(9164825) { + + pthread_mutex_init(&mutexSharedClauseCompanion,NULL); // This is the shared companion lock + pthread_mutex_init(&mutexSharedUnitCompanion,NULL); // This is the shared companion lock + pthread_mutex_init(&mutexSharedCompanion,NULL); // This is the shared companion lock + pthread_mutex_init(&mutexJobFinished,NULL); // This is the shared companion lock + if (_nbThreads> 0) { + setNbThreads(_nbThreads); + fprintf(stdout,"c Shared companion initialized: handling of clauses of %d threads.\nc %d ints for the sharing clause buffer (not expandable) .\n", _nbThreads, clausesBuffer.maxSize()); + } + +} + +void SharedCompanion::setNbThreads(int _nbThreads) { + nbThreads = _nbThreads; + clausesBuffer.setNbThreads(_nbThreads); +} + +void SharedCompanion::printStats() { +} + +// No multithread safe +bool SharedCompanion::addSolver(ParallelSolver* s) { + watchedSolvers.push(s); + pthread_mutex_t* mu = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(mu,NULL); + assert(s->thn == watchedSolvers.size()-1); // all solvers must have been registered in the good order + nextUnit.push(0); + + return true; +} +void SharedCompanion::newVar(bool sign) { + isUnary .push(l_Undef); +} + +void SharedCompanion::addLearnt(ParallelSolver *s,Lit unary) { + pthread_mutex_lock(&mutexSharedUnitCompanion); + if (isUnary[var(unary)]==l_Undef) { + unitLit.push(unary); + isUnary[var(unary)] = sign(unary)?l_False:l_True; + } + pthread_mutex_unlock(&mutexSharedUnitCompanion); +} + +Lit SharedCompanion::getUnary(ParallelSolver *s) { + int sn = s->thn; + Lit ret = lit_Undef; + + pthread_mutex_lock(&mutexSharedUnitCompanion); + if (nextUnit[sn] < unitLit.size()) + ret = unitLit[nextUnit[sn]++]; + pthread_mutex_unlock(&mutexSharedUnitCompanion); + return ret; +} + +// Specialized functions for this companion +// must be multithread safe +// Add a clause to the threads-wide clause database (all clauses, through) +bool SharedCompanion::addLearnt(ParallelSolver *s, Clause & c) { + int sn = s->thn; // thread number of the solver + bool ret = false; + assert(watchedSolvers.size()>sn); + + pthread_mutex_lock(&mutexSharedClauseCompanion); + ret = clausesBuffer.pushClause(sn, c); + pthread_mutex_unlock(&mutexSharedClauseCompanion); + return ret; +} + + +bool SharedCompanion::getNewClause(ParallelSolver *s, int & threadOrigin, vec& newclause) { // gets a new interesting clause for solver s + int sn = s->thn; + + // First, let's get the clauses on the big blackboard + pthread_mutex_lock(&mutexSharedClauseCompanion); + bool b = clausesBuffer.getClause(sn, threadOrigin, newclause); + pthread_mutex_unlock(&mutexSharedClauseCompanion); + + return b; +} + +bool SharedCompanion::jobFinished() { + bool ret = false; + pthread_mutex_lock(&mutexJobFinished); + ret = bjobFinished; + pthread_mutex_unlock(&mutexJobFinished); + return ret; +} + +bool SharedCompanion::IFinished(ParallelSolver *s) { + bool ret = false; + pthread_mutex_lock(&mutexJobFinished); + if (!bjobFinished) { + ret = true; + bjobFinished = true; + jobFinishedBy = s; + } + pthread_mutex_unlock(&mutexJobFinished); + return ret; +} + + + diff --git a/libs/mugen/glucose-syrup-4.1/parallel/SharedCompanion.h b/libs/mugen/glucose-syrup-4.1/parallel/SharedCompanion.h new file mode 100644 index 000000000..9ab34614b --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/SharedCompanion.h @@ -0,0 +1,122 @@ +/***************************************************************************************[SharedCompanion.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +/* This class is responsible for protecting (by mutex) information exchange between threads. + * It also allows each solver to send / receive clause / unary clauses. + * + * Only one sharedCompanion is created for all the solvers + */ + + +#ifndef SharedCompanion_h +#define SharedCompanion_h +#include "core/SolverTypes.h" +#include "parallel/ParallelSolver.h" +#include "parallel/SolverCompanion.h" +#include "parallel/ClausesBuffer.h" + +namespace Glucose { + + +class SharedCompanion : public SolverCompanion { + friend class MultiSolvers; + friend class ParallelSolver; +public: + SharedCompanion(int nbThreads=0); + void setNbThreads(int _nbThreads); // Sets the number of threads (cannot by changed once the solver is running) + void newVar(bool sign); // Adds a var (used to keep track of unary variables) + void printStats(); // Printing statistics of all solvers + + bool jobFinished(); // True if the job is over + bool IFinished(ParallelSolver *s); // returns true if you are the first solver to finish + bool addSolver(ParallelSolver*); // attach a solver to accompany + void addLearnt(ParallelSolver *s,Lit unary); // Add a unary clause to share + bool addLearnt(ParallelSolver *s, Clause & c); // Add a clause to the shared companion, as a database manager + + bool getNewClause(ParallelSolver *s, int &th, vec & nc); // gets a new interesting clause for solver s + Lit getUnary(ParallelSolver *s); // Gets a new unary literal + inline ParallelSolver* winner(){return jobFinishedBy;} // Gets the first solver that called IFinished() + + protected: + + ClausesBuffer clausesBuffer; // A big blackboard for all threads sharing non unary clauses + int nbThreads; // Number of threads + + // A set of mutex variables + pthread_mutex_t mutexSharedCompanion; // mutex for any high level sync between all threads (like reportf) + pthread_mutex_t mutexSharedClauseCompanion; // mutex for reading/writing clauses on the blackboard + pthread_mutex_t mutexSharedUnitCompanion; // mutex for reading/writing unit clauses on the blackboard + pthread_mutex_t mutexJobFinished; + + bool bjobFinished; + ParallelSolver *jobFinishedBy; + bool panicMode; // panicMode means no more increasing space needed + lbool jobStatus; // globale status of the job + + // Shared clauses are a queue of lits... + // friend class wholearnt; + vec nextUnit; // indice of next unit clause to retrieve for solver number i + vec unitLit; // Set of unit literals found so far + vec isUnary; // sign of the unary var (if proved, or l_Undef if not) + double random_seed; + + // Returns a random float 0 <= x < 1. Seed must never be 0. + static inline double drand(double& seed) { + seed *= 1389796; + int q = (int)(seed / 2147483647); + seed -= (double)q * 2147483647; + return seed / 2147483647; } + + // Returns a random integer 0 <= x < size. Seed must never be 0. + static inline int irand(double& seed, int size) { + return (int)(drand(seed) * size); } + +}; +} +#endif diff --git a/libs/mugen/glucose-syrup-4.1/parallel/SolverCompanion.cc b/libs/mugen/glucose-syrup-4.1/parallel/SolverCompanion.cc new file mode 100644 index 000000000..8cd5c5199 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/SolverCompanion.cc @@ -0,0 +1,87 @@ +/***************************************************************************************[SolverCompanion.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +/* This class is a general companion for a solver. + * The idea is to be able to have different kind of companions: + * - SharedCompanion that shares clauses between threads + * - NetworkCompanion (Not in this version) that sends clauses over the network + * + * The implementaton is trivial. Just keep track of watched Solvers by the companion. + **/ + +#include "parallel/SolverCompanion.h" + +using namespace Glucose; + +SolverCompanion::SolverCompanion() +{} + +SolverCompanion::~SolverCompanion() +{} + + +bool SolverCompanion::addSolver(ParallelSolver* s) { + watchedSolvers.push(s); + return true; +} + +int SolverCompanion::runOnceCompanion() { + int errcode = 0; + for(int indexSolver = 0; indexSolver watchedSolvers; +}; +} +#endif + diff --git a/libs/mugen/glucose-syrup-4.1/parallel/SolverConfiguration.cc b/libs/mugen/glucose-syrup-4.1/parallel/SolverConfiguration.cc new file mode 100644 index 000000000..bca361ca1 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/SolverConfiguration.cc @@ -0,0 +1,176 @@ +/***************************************************************************************[SolverConfiguration.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#include "parallel/MultiSolvers.h" +#include "core/Solver.h" +//#include "parallel/ParallelSolver.h" +#include "parallel/SolverConfiguration.h" + +using namespace Glucose; + + +void SolverConfiguration::configure(MultiSolvers *ms, int nbsolvers) { + for(int i = 1;isolvers[i]->randomizeFirstDescent = true; + ms->solvers[i]->adaptStrategies = (i%2==0); // Just half of the cores are in adaptive mode + ms->solvers[i]->forceUnsatOnNewDescent = (i%4==0); // Just half of adaptive cores have the unsat force + } + if (nbsolvers > 8) { // configuration for the second phase of the sat race 2015 + for(int i=0;isolvers[i]->goodlimitlbd = 5; + ms->solvers[i]->goodlimitsize = 15; + } + } + +} + + +void SolverConfiguration::configureSAT15Adapt(MultiSolvers *ms, int nbsolvers) { + for(int i = 1;isolvers[i]->randomizeFirstDescent = true; + ms->solvers[i]->adaptStrategies = (i%2==0); // Just half of the cores are in adaptive mode + } + if (nbsolvers > 8) { // configuration for the second phase of the sat race 2015 + for(int i=0;isolvers[i]->goodlimitlbd = 5; + ms->solvers[i]->goodlimitsize = 15; + } + } +} + + +void SolverConfiguration::configureSAT15Default(MultiSolvers *ms, int nbsolvers) { + for(int i = 1;isolvers[i]->randomizeFirstDescent = true; + + if (nbsolvers > 8) { // configuration for the second phase of the sat race 2015 + for(int i=0;isolvers[i]->goodlimitlbd = 5; + ms->solvers[i]->goodlimitsize = 15; + + } + } + +} + +void SolverConfiguration::configureSAT14(MultiSolvers *ms, int nbsolvers) { + + if (nbsolvers < 2 ) return; + + ms->solvers[1]->var_decay = 0.94; + ms->solvers[1]->max_var_decay = 0.96; + ms->solvers[1]->firstReduceDB=600; + + if (nbsolvers < 3 ) return; + + ms->solvers[2]->var_decay = 0.90; + ms->solvers[2]->max_var_decay = 0.97; + ms->solvers[2]->firstReduceDB=500; + + if (nbsolvers < 4 ) return; + + ms->solvers[3]->var_decay = 0.85; + ms->solvers[3]->max_var_decay = 0.93; + ms->solvers[3]->firstReduceDB=400; + + if (nbsolvers < 5 ) return; + + // Glucose 2.0 (+ blocked restarts) + ms->solvers[4]->var_decay = 0.95; + ms->solvers[4]->max_var_decay = 0.95; + ms->solvers[4]->firstReduceDB=4000; + ms->solvers[4]->lbdQueue.growTo(100); + ms->solvers[4]->sizeLBDQueue = 100; + ms->solvers[4]->K = 0.7; + ms->solvers[4]->incReduceDB = 500; + + if (nbsolvers < 6 ) return; + + ms->solvers[5]->var_decay = 0.93; + ms->solvers[5]->max_var_decay = 0.96; + ms->solvers[5]->firstReduceDB=100; + ms->solvers[5]->incReduceDB = 500; + + if (nbsolvers < 7 ) return; + + ms->solvers[6]->var_decay = 0.75; + ms->solvers[6]->max_var_decay = 0.94; + ms->solvers[6]->firstReduceDB=2000; + + if (nbsolvers < 8 ) return; + + ms->solvers[7]->var_decay = 0.94; + ms->solvers[7]->max_var_decay = 0.96; + ms->solvers[7]->firstReduceDB=800; + + if (nbsolvers < 9) return; + +// ms->solvers[8]->reduceOnSize = true; // NOT USED ANYMORE + + if (nbsolvers < 10 ) return; + +// ms->solvers[9]->reduceOnSize = true; // NOT USED ANYMORE +// ms->solvers[9]->reduceOnSizeSize = 14; + + if (nbsolvers < 11 ) return; + + double noisevar_decay = 0.005; + int noiseReduceDB = 50; + for (int i=10;isolvers[i]-> var_decay = ms->solvers[i%8]->var_decay; + ms->solvers[i]-> max_var_decay = ms->solvers[i%8]->max_var_decay; + ms->solvers[i]-> firstReduceDB= ms->solvers[i%8]->firstReduceDB; + ms->solvers[i]->var_decay += noisevar_decay; + ms->solvers[i]->firstReduceDB+=noiseReduceDB; + if ((i+1) % 8 == 0) { + noisevar_decay += 0.006; + noiseReduceDB += 25; + } + } + } diff --git a/libs/mugen/glucose-syrup-4.1/parallel/SolverConfiguration.h b/libs/mugen/glucose-syrup-4.1/parallel/SolverConfiguration.h new file mode 100644 index 000000000..94118562c --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/parallel/SolverConfiguration.h @@ -0,0 +1,73 @@ +/***************************************************************************************[SolverConfiguration.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + + +#ifndef SolverConfiguration_h +#define SolverConfiguration_h + + + +namespace Glucose { + +class MultiSolvers; + +class SolverConfiguration { + +public : + static void configure(MultiSolvers *ms, int nbsolvers); + + // Special configurations + static void configureSAT14(MultiSolvers *ms, int nbsolvers); + void configureSAT15Adapt(MultiSolvers *ms, int nbsolvers); + void configureSAT15Default(MultiSolvers *ms, int nbsolvers); + +}; + +} +#endif diff --git a/libs/mugen/glucose-syrup-4.1/simp/Main.cc b/libs/mugen/glucose-syrup-4.1/simp/Main.cc new file mode 100644 index 000000000..415d21f25 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/simp/Main.cc @@ -0,0 +1,299 @@ +/***************************************************************************************[Main.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#include + +#include +#include +#include + +#include "utils/System.h" +#include "utils/ParseUtils.h" +#include "utils/Options.h" +#include "core/Dimacs.h" +#include "simp/SimpSolver.h" + +using namespace Glucose; + +//================================================================================================= + +static const char* _certified = "CORE -- CERTIFIED UNSAT"; + +void printStats(Solver& solver) +{ + double cpu_time = cpuTime(); + double mem_used = 0;//memUsedPeak(); + printf("c restarts : %" PRIu64" (%" PRIu64" conflicts in avg)\n", solver.starts,(solver.starts>0 ?solver.conflicts/solver.starts : 0)); + printf("c blocked restarts : %" PRIu64" (multiple: %" PRIu64") \n", solver.stats[nbstopsrestarts],solver.stats[nbstopsrestartssame]); + printf("c last block at restart : %" PRIu64"\n",solver.stats[lastblockatrestart]); + printf("c nb ReduceDB : %" PRIu64"\n", solver.stats[nbReduceDB]); + printf("c nb removed Clauses : %" PRIu64"\n",solver.stats[nbRemovedClauses]); + printf("c nb learnts DL2 : %" PRIu64"\n", solver.stats[nbDL2]); + printf("c nb learnts size 2 : %" PRIu64"\n", solver.stats[nbBin]); + printf("c nb learnts size 1 : %" PRIu64"\n", solver.stats[nbUn]); + if(solver.chanseokStrategy) + printf("c nb permanent learnts : %" PRIu64"\n", solver.stats[nbPermanentLearnts]); + + printf("c conflicts : %-12" PRIu64" (%.0f /sec)\n", solver.conflicts , solver.conflicts /cpu_time); + printf("c decisions : %-12" PRIu64" (%4.2f %% random) (%.0f /sec)\n", solver.decisions, (float)solver.stats[rnd_decisions]*100 / (float)solver.decisions, solver.decisions /cpu_time); + printf("c propagations : %-12" PRIu64" (%.0f /sec)\n", solver.propagations, solver.propagations/cpu_time); + // printf("c conflict literals : %-12" PRIu64" (%4.2f %% deleted)\n", solver.stats[tot_literals], (solver.stats[max_literals] - solver.stats[tot_literals])*100 / (double)solver.stats[max_literals]); + // printf("c Average resolutions : %-12" PRIu64" (%.0f seen ones)\n",solver.stats[sumRes]/solver.conflicts,((double)solver.stats[sumResSeen])/solver.conflicts); + printf("c nb reduced Clauses : %" PRIu64"\n",solver.stats[nbReducedClauses]); + + if (mem_used != 0) printf("Memory used : %.2f MB\n", mem_used); + printf("c CPU time : %g s\n", cpu_time); +} + + + +static Solver* solver; +// Terminate by notifying the solver and back out gracefully. This is mainly to have a test-case +// for this feature of the Solver as it may take longer than an immediate call to '_exit()'. +static void SIGINT_interrupt(int signum) { solver->interrupt(); } + +// Note that '_exit()' rather than 'exit()' has to be used. The reason is that 'exit()' calls +// destructors and may cause deadlocks if a malloc/free function happens to be running (these +// functions are guarded by locks for multithreaded use). +static void SIGINT_exit(int signum) { + printf("\n"); printf("*** INTERRUPTED ***\n"); + if (solver->verbosity > 0){ + printStats(*solver); + printf("\n"); printf("*** INTERRUPTED ***\n"); } + _exit(1); } + + +//================================================================================================= +// Main: + +int main(int argc, char** argv) +{ + try { + printf("c\nc This is glucose 4.0 -- based on MiniSAT (Many thanks to MiniSAT team)\nc\n"); + + + setUsageHelp("c USAGE: %s [options] \n\n where input may be either in plain or gzipped DIMACS.\n"); + + // Extra options: + // + IntOption verb ("MAIN", "verb", "Verbosity level (0=silent, 1=some, 2=more).", 1, IntRange(0, 2)); + BoolOption mod ("MAIN", "model", "show model.", false); + IntOption vv ("MAIN", "vv", "Verbosity every vv conflicts", 10000, IntRange(1,INT32_MAX)); + BoolOption pre ("MAIN", "pre", "Completely turn on/off any preprocessing.", true); + StringOption dimacs ("MAIN", "dimacs", "If given, stop after preprocessing and write the result to this file."); + IntOption cpu_lim("MAIN", "cpu-lim","Limit on CPU time allowed in seconds.\n", INT32_MAX, IntRange(0, INT32_MAX)); + IntOption mem_lim("MAIN", "mem-lim","Limit on memory usage in megabytes.\n", INT32_MAX, IntRange(0, INT32_MAX)); + // BoolOption opt_incremental ("MAIN","incremental", "Use incremental SAT solving",false); + + BoolOption opt_certified (_certified, "certified", "Certified UNSAT using DRUP format", false); + StringOption opt_certified_file (_certified, "certified-output", "Certified UNSAT output file", "NULL"); + BoolOption opt_vbyte (_certified, "vbyte", "Emit proof in variable-byte encoding", false); + + parseOptions(argc, argv, true); + + SimpSolver S; + double initial_time = cpuTime(); + + S.parsing = 1; + S.use_simplification = pre; + + //if (!pre) S.eliminate(true); + + S.verbosity = verb; + S.verbEveryConflicts = vv; + S.showModel = mod; + + S.certifiedUNSAT = opt_certified; + S.vbyte = opt_vbyte; + if(S.certifiedUNSAT) { + if(!strcmp(opt_certified_file,"NULL")) { + S.vbyte = false; // Cannot write binary to stdout + S.certifiedOutput = fopen("/dev/stdout", "wb"); + if(S.verbosity >= 1) + printf("c\nc Write unsat proof on stdout using text format\nc\n"); + } else + S.certifiedOutput = fopen(opt_certified_file, "wb"); + const char *name = opt_certified_file; + if(S.verbosity >= 1) + printf("c\nc Write unsat proof on %s using %s format\nc\n",name,S.vbyte ? "binary" : "text"); + } + + solver = &S; + // Use signal handlers that forcibly quit until the solver will be able to respond to + // interrupts: + signal(SIGINT, SIGINT_exit); + signal(SIGXCPU,SIGINT_exit); + + + // Set limit on CPU-time: + if (cpu_lim != INT32_MAX){ + rlimit rl; + getrlimit(RLIMIT_CPU, &rl); + if (rl.rlim_max == RLIM_INFINITY || (rlim_t)cpu_lim < rl.rlim_max){ + rl.rlim_cur = cpu_lim; + if (setrlimit(RLIMIT_CPU, &rl) == -1) + printf("c WARNING! Could not set resource limit: CPU-time.\n"); + } } + + // Set limit on virtual memory: + if (mem_lim != INT32_MAX){ + rlim_t new_mem_lim = (rlim_t)mem_lim * 1024*1024; + rlimit rl; + getrlimit(RLIMIT_AS, &rl); + if (rl.rlim_max == RLIM_INFINITY || new_mem_lim < rl.rlim_max){ + rl.rlim_cur = new_mem_lim; + if (setrlimit(RLIMIT_AS, &rl) == -1) + printf("c WARNING! Could not set resource limit: Virtual memory.\n"); + } } + + if (argc == 1) + printf("c Reading from standard input... Use '--help' for help.\n"); + + gzFile in = (argc == 1) ? gzdopen(0, "rb") : gzopen(argv[1], "rb"); + if (in == NULL) + printf("ERROR! Could not open file: %s\n", argc == 1 ? "" : argv[1]), exit(1); + + if (S.verbosity > 0){ + printf("c ========================================[ Problem Statistics ]===========================================\n"); + printf("c | |\n"); } + + FILE* res = (argc >= 3) ? fopen(argv[argc-1], "wb") : NULL; + parse_DIMACS(in, S); + gzclose(in); + + if (S.verbosity > 0){ + printf("c | Number of variables: %12d |\n", S.nVars()); + printf("c | Number of clauses: %12d |\n", S.nClauses()); } + + double parsed_time = cpuTime(); + if (S.verbosity > 0){ + printf("c | Parse time: %12.2f s |\n", parsed_time - initial_time); + printf("c | |\n"); } + + // Change to signal-handlers that will only notify the solver and allow it to terminate + // voluntarily: + signal(SIGINT, SIGINT_interrupt); + signal(SIGXCPU,SIGINT_interrupt); + + S.parsing = 0; + if(pre/* && !S.isIncremental()*/) { + printf("c | Preprocesing is fully done\n"); + S.eliminate(true); + double simplified_time = cpuTime(); + if (S.verbosity > 0){ + printf("c | Simplification time: %12.2f s |\n", simplified_time - parsed_time); + } + } + printf("c | |\n"); + if (!S.okay()){ + if (S.certifiedUNSAT) fprintf(S.certifiedOutput, "0\n"), fclose(S.certifiedOutput); + if (res != NULL) fprintf(res, "UNSAT\n"), fclose(res); + if (S.verbosity > 0){ + printf("c =========================================================================================================\n"); + printf("Solved by simplification\n"); + printStats(S); + printf("\n"); } + printf("s UNSATISFIABLE\n"); + exit(20); + } + + if (dimacs){ + if (S.verbosity > 0) + printf("c =======================================[ Writing DIMACS ]===============================================\n"); + S.toDimacs((const char*)dimacs); + if (S.verbosity > 0) + printStats(S); + exit(0); + } + + vec dummy; + lbool ret = S.solveLimited(dummy); + + if (S.verbosity > 0){ + printStats(S); + printf("\n"); } + printf(ret == l_True ? "s SATISFIABLE\n" : ret == l_False ? "s UNSATISFIABLE\n" : "s INDETERMINATE\n"); + + if (res != NULL){ + if (ret == l_True){ + printf("SAT\n"); + for (int i = 0; i < S.nVars(); i++) + if (S.model[i] != l_Undef) + fprintf(res, "%s%s%d", (i==0)?"":" ", (S.model[i]==l_True)?"":"-", i+1); + fprintf(res, " 0\n"); + } else { + if (ret == l_False){ + fprintf(res, "UNSAT\n"); + } + } + fclose(res); + } else { + if(S.showModel && ret==l_True) { + printf("v "); + for (int i = 0; i < S.nVars(); i++) + if (S.model[i] != l_Undef) + printf("%s%s%d", (i==0)?"":" ", (S.model[i]==l_True)?"":"-", i+1); + printf(" 0\n"); + } + + + } + + +#ifdef NDEBUG + exit(ret == l_True ? 10 : ret == l_False ? 20 : 0); // (faster than "return", which will invoke the destructor for 'Solver') +#else + return (ret == l_True ? 10 : ret == l_False ? 20 : 0); +#endif + } catch (OutOfMemoryException&){ + printf("c =========================================================================================================\n"); + printf("INDETERMINATE\n"); + exit(0); + } +} diff --git a/libs/mugen/glucose-syrup-4.1/simp/Makefile b/libs/mugen/glucose-syrup-4.1/simp/Makefile new file mode 100644 index 000000000..f5d4481aa --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/simp/Makefile @@ -0,0 +1,5 @@ +EXEC = glucose +DEPDIR = mtl utils core +MROOT = $(PWD)/.. + +include $(MROOT)/mtl/template.mk diff --git a/libs/mugen/glucose-syrup-4.1/simp/SimpSolver.cc b/libs/mugen/glucose-syrup-4.1/simp/SimpSolver.cc new file mode 100644 index 000000000..7c36bd026 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/simp/SimpSolver.cc @@ -0,0 +1,850 @@ +/***************************************************************************************[SimpSolver.cc] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#include "mtl/Sort.h" +#include "simp/SimpSolver.h" +#include "utils/System.h" + +using namespace Glucose; + +//================================================================================================= +// Options: + + +static const char* _cat = "SIMP"; + +static BoolOption opt_use_asymm (_cat, "asymm", "Shrink clauses by asymmetric branching.", false); +static BoolOption opt_use_rcheck (_cat, "rcheck", "Check if a clause is already implied. (costly)", false); +static BoolOption opt_use_elim (_cat, "elim", "Perform variable elimination.", true); +static IntOption opt_grow (_cat, "grow", "Allow a variable elimination step to grow by a number of clauses.", 0); +static IntOption opt_clause_lim (_cat, "cl-lim", "Variables are not eliminated if it produces a resolvent with a length above this limit. -1 means no limit", 20, IntRange(-1, INT32_MAX)); +static IntOption opt_subsumption_lim (_cat, "sub-lim", "Do not check if subsumption against a clause larger than this. -1 means no limit.", 1000, IntRange(-1, INT32_MAX)); +static DoubleOption opt_simp_garbage_frac(_cat, "simp-gc-frac", "The fraction of wasted memory allowed before a garbage collection is triggered during simplification.", 0.5, DoubleRange(0, false, HUGE_VAL, false)); + + +//================================================================================================= +// Constructor/Destructor: + + +SimpSolver::SimpSolver() : + Solver() + , grow (opt_grow) + , clause_lim (opt_clause_lim) + , subsumption_lim (opt_subsumption_lim) + , simp_garbage_frac (opt_simp_garbage_frac) + , use_asymm (opt_use_asymm) + , use_rcheck (opt_use_rcheck) + , use_elim (opt_use_elim) + , merges (0) + , asymm_lits (0) + , eliminated_vars (0) + , use_simplification (true) + , elimorder (1) + , occurs (ClauseDeleted(ca)) + , elim_heap (ElimLt(n_occ)) + , bwdsub_assigns (0) + , n_touched (0) +{ + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; +} + + +SimpSolver::~SimpSolver() +{ +} + + + +SimpSolver::SimpSolver(const SimpSolver &s) : Solver(s) + , grow (s.grow) + , clause_lim (s.clause_lim) + , subsumption_lim (s.subsumption_lim) + , simp_garbage_frac (s.simp_garbage_frac) + , use_asymm (s.use_asymm) + , use_rcheck (s.use_rcheck) + , use_elim (s.use_elim) + , merges (s.merges) + , asymm_lits (s.asymm_lits) + , eliminated_vars (s.eliminated_vars) + , use_simplification (s.use_simplification) + , elimorder (s.elimorder) + , occurs (ClauseDeleted(ca)) + , elim_heap (ElimLt(n_occ)) + , bwdsub_assigns (s.bwdsub_assigns) + , n_touched (s.n_touched) +{ + // TODO: Copy dummy... what is it??? + vec dummy(1,lit_Undef); + ca.extra_clause_field = true; // NOTE: must happen before allocating the dummy clause below. + bwdsub_tmpunit = ca.alloc(dummy); + remove_satisfied = false; + //End TODO + + + s.elimclauses.memCopyTo(elimclauses); + s.touched.memCopyTo(touched); + s.occurs.copyTo(occurs); + s.n_occ.memCopyTo(n_occ); + s.elim_heap.copyTo(elim_heap); + s.subsumption_queue.copyTo(subsumption_queue); + s.frozen.memCopyTo(frozen); + s.eliminated.memCopyTo(eliminated); + + use_simplification = s.use_simplification; + bwdsub_assigns = s.bwdsub_assigns; + n_touched = s.n_touched; + bwdsub_tmpunit = s.bwdsub_tmpunit; + qhead = s.qhead; + ok = s.ok; +} + + + +Var SimpSolver::newVar(bool sign, bool dvar) { + Var v = Solver::newVar(sign, dvar); + frozen .push((char)false); + eliminated.push((char)false); + + if (use_simplification){ + n_occ .push(0); + n_occ .push(0); + occurs .init(v); + touched .push(0); + elim_heap .insert(v); + } + return v; } + +lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) +{ + vec extra_frozen; + lbool result = l_True; + do_simp &= use_simplification; + + if (do_simp){ + // Assumptions must be temporarily frozen to run variable elimination: + for (int i = 0; i < assumptions.size(); i++){ + Var v = var(assumptions[i]); + + // If an assumption has been eliminated, remember it. + assert(!isEliminated(v)); + + if (!frozen[v]){ + // Freeze and store. + setFrozen(v, true); + extra_frozen.push(v); + } } + + result = lbool(eliminate(turn_off_simp)); + } + + if (result == l_True) + result = Solver::solve_(); + else if (verbosity >= 1) + printf("===============================================================================\n"); + + if (result == l_True) + extendModel(); + + if (do_simp) + // Unfreeze the assumptions that were frozen: + for (int i = 0; i < extra_frozen.size(); i++) + setFrozen(extra_frozen[i], false); + + + return result; +} + + + +bool SimpSolver::addClause_(vec& ps) +{ +#ifndef NDEBUG + for (int i = 0; i < ps.size(); i++) + assert(!isEliminated(var(ps[i]))); +#endif + int nclauses = clauses.size(); + + if (use_rcheck && implied(ps)) + return true; + + if (!Solver::addClause_(ps)) + return false; + + if(!parsing && certifiedUNSAT) { + if (vbyte) { + write_char('a'); + for (int i = 0; i < ps.size(); i++) + write_lit(2*(var(ps[i])+1) + sign(ps[i])); + write_lit(0); + } + else { + for (int i = 0; i < ps.size(); i++) + fprintf(certifiedOutput, "%i " , (var(ps[i]) + 1) * (-2 * sign(ps[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + if (use_simplification && clauses.size() == nclauses + 1){ + CRef cr = clauses.last(); + const Clause& c = ca[cr]; + + // NOTE: the clause is added to the queue immediately and then + // again during 'gatherTouchedClauses()'. If nothing happens + // in between, it will only be checked once. Otherwise, it may + // be checked twice unnecessarily. This is an unfortunate + // consequence of how backward subsumption is used to mimic + // forward subsumption. + subsumption_queue.insert(cr); + for (int i = 0; i < c.size(); i++){ + occurs[var(c[i])].push(cr); + n_occ[toInt(c[i])]++; + touched[var(c[i])] = 1; + n_touched++; + if (elim_heap.inHeap(var(c[i]))) + elim_heap.increase(var(c[i])); + } + } + + return true; +} + + + +void SimpSolver::removeClause(CRef cr,bool inPurgatory) +{ + const Clause& c = ca[cr]; + + if (use_simplification) + for (int i = 0; i < c.size(); i++){ + n_occ[toInt(c[i])]--; + updateElimHeap(var(c[i])); + occurs.smudge(var(c[i])); + } + + Solver::removeClause(cr,inPurgatory); +} + + +bool SimpSolver::strengthenClause(CRef cr, Lit l) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + assert(use_simplification); + + // FIX: this is too inefficient but would be nice to have (properly implemented) + // if (!find(subsumption_queue, &c)) + subsumption_queue.insert(cr); + + if (certifiedUNSAT) { + if (vbyte) { + write_char('a'); + for (int i = 0; i < c.size(); i++) + if (c[i] != l) write_lit(2*(var(c[i])+1) + sign(c[i])); + write_lit(0); + } + else { + for (int i = 0; i < c.size(); i++) + if (c[i] != l) fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + if (c.size() == 2){ + removeClause(cr); + c.strengthen(l); + }else{ + if (certifiedUNSAT) { + if (vbyte) { + write_char('d'); + for (int i = 0; i < c.size(); i++) + write_lit(2*(var(c[i])+1) + sign(c[i])); + write_lit(0); + } + else { + fprintf(certifiedOutput, "d "); + for (int i = 0; i < c.size(); i++) + fprintf(certifiedOutput, "%i " , (var(c[i]) + 1) * (-2 * sign(c[i]) + 1) ); + fprintf(certifiedOutput, "0\n"); + } + } + + detachClause(cr, true); + c.strengthen(l); + attachClause(cr); + remove(occurs[var(l)], cr); + n_occ[toInt(l)]--; + updateElimHeap(var(l)); + } + + return c.size() == 1 ? enqueue(c[0]) && propagate() == CRef_Undef : true; +} + + +// Returns FALSE if clause is always satisfied ('out_clause' should not be used). +bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, vec& out_clause) +{ + merges++; + out_clause.clear(); + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + + for (int i = 0; i < qs.size(); i++){ + if (var(qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(ps[j]) == var(qs[i])) + if (ps[j] == ~qs[i]) + return false; + else + goto next; + out_clause.push(qs[i]); + } + next:; + } + + for (int i = 0; i < ps.size(); i++) + if (var(ps[i]) != v) + out_clause.push(ps[i]); + + return true; +} + + +// Returns FALSE if clause is always satisfied. +bool SimpSolver::merge(const Clause& _ps, const Clause& _qs, Var v, int& size) +{ + merges++; + + bool ps_smallest = _ps.size() < _qs.size(); + const Clause& ps = ps_smallest ? _qs : _ps; + const Clause& qs = ps_smallest ? _ps : _qs; + const Lit* __ps = (const Lit*)ps; + const Lit* __qs = (const Lit*)qs; + + size = ps.size()-1; + + for (int i = 0; i < qs.size(); i++){ + if (var(__qs[i]) != v){ + for (int j = 0; j < ps.size(); j++) + if (var(__ps[j]) == var(__qs[i])) + if (__ps[j] == ~__qs[i]) + return false; + else + goto next; + size++; + } + next:; + } + + return true; +} + + +void SimpSolver::gatherTouchedClauses() +{ + if (n_touched == 0) return; + + int i,j; + for (i = j = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 0) + ca[subsumption_queue[i]].mark(2); + + for (i = 0; i < touched.size(); i++) + if (touched[i]){ + const vec& cs = occurs.lookup(i); + for (j = 0; j < cs.size(); j++) + if (ca[cs[j]].mark() == 0){ + subsumption_queue.insert(cs[j]); + ca[cs[j]].mark(2); + } + touched[i] = 0; + } + + for (i = 0; i < subsumption_queue.size(); i++) + if (ca[subsumption_queue[i]].mark() == 2) + ca[subsumption_queue[i]].mark(0); + + n_touched = 0; +} + + +bool SimpSolver::implied(const vec& c) +{ + assert(decisionLevel() == 0); + + trail_lim.push(trail.size()); + for (int i = 0; i < c.size(); i++) + if (value(c[i]) == l_True){ + cancelUntil(0); + return false; + }else if (value(c[i]) != l_False){ + assert(value(c[i]) == l_Undef); + uncheckedEnqueue(~c[i]); + } + + bool result = propagate() != CRef_Undef; + cancelUntil(0); + return result; +} + + +// Backward subsumption + backward subsumption resolution +bool SimpSolver::backwardSubsumptionCheck(bool verbose) +{ + int cnt = 0; + int subsumed = 0; + int deleted_literals = 0; + assert(decisionLevel() == 0); + + while (subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()){ + + // Empty subsumption queue and return immediately on user-interrupt: + if (asynch_interrupt){ + subsumption_queue.clear(); + bwdsub_assigns = trail.size(); + break; } + + // Check top-level assignments by creating a dummy clause and placing it in the queue: + if (subsumption_queue.size() == 0 && bwdsub_assigns < trail.size()){ + Lit l = trail[bwdsub_assigns++]; + ca[bwdsub_tmpunit][0] = l; + ca[bwdsub_tmpunit].calcAbstraction(); + subsumption_queue.insert(bwdsub_tmpunit); } + + CRef cr = subsumption_queue.peek(); subsumption_queue.pop(); + Clause& c = ca[cr]; + + if (c.mark()) continue; + + if (verbose && verbosity >= 2 && cnt++ % 1000 == 0) + printf("subsumption left: %10d (%10d subsumed, %10d deleted literals)\r", subsumption_queue.size(), subsumed, deleted_literals); + + assert(c.size() > 1 || value(c[0]) == l_True); // Unit-clauses should have been propagated before this point. + + // Find best variable to scan: + Var best = var(c[0]); + for (int i = 1; i < c.size(); i++) + if (occurs[var(c[i])].size() < occurs[best].size()) + best = var(c[i]); + + // Search all candidates: + vec& _cs = occurs.lookup(best); + CRef* cs = (CRef*)_cs; + + for (int j = 0; j < _cs.size(); j++) + if (c.mark()) + break; + else if (!ca[cs[j]].mark() && cs[j] != cr && (subsumption_lim == -1 || ca[cs[j]].size() < subsumption_lim)){ + Lit l = c.subsumes(ca[cs[j]]); + + if (l == lit_Undef) + subsumed++, removeClause(cs[j]); + else if (l != lit_Error){ + deleted_literals++; + + if (!strengthenClause(cs[j], ~l)) + return false; + + // Did current candidate get deleted from cs? Then check candidate at index j again: + if (var(l) == best) + j--; + } + } + } + + return true; +} + + +bool SimpSolver::asymm(Var v, CRef cr) +{ + Clause& c = ca[cr]; + assert(decisionLevel() == 0); + + if (c.mark() || satisfied(c)) return true; + + trail_lim.push(trail.size()); + Lit l = lit_Undef; + for (int i = 0; i < c.size(); i++) + if (var(c[i]) != v && value(c[i]) != l_False) + uncheckedEnqueue(~c[i]); + else + l = c[i]; + + if (propagate() != CRef_Undef){ + cancelUntil(0); + asymm_lits++; + if (!strengthenClause(cr, l)) + return false; + }else + cancelUntil(0); + + return true; +} + + +bool SimpSolver::asymmVar(Var v) +{ + assert(use_simplification); + + const vec& cls = occurs.lookup(v); + + if (value(v) != l_Undef || cls.size() == 0) + return true; + + for (int i = 0; i < cls.size(); i++) + if (!asymm(v, cls[i])) + return false; + + return backwardSubsumptionCheck(); +} + + +static void mkElimClause(vec& elimclauses, Lit x) +{ + elimclauses.push(toInt(x)); + elimclauses.push(1); +} + + +static void mkElimClause(vec& elimclauses, Var v, Clause& c) +{ + int first = elimclauses.size(); + int v_pos = -1; + + // Copy clause to elimclauses-vector. Remember position where the + // variable 'v' occurs: + for (int i = 0; i < c.size(); i++){ + elimclauses.push(toInt(c[i])); + if (var(c[i]) == v) + v_pos = i + first; + } + assert(v_pos != -1); + + // Swap the first literal with the 'v' literal, so that the literal + // containing 'v' will occur first in the clause: + uint32_t tmp = elimclauses[v_pos]; + elimclauses[v_pos] = elimclauses[first]; + elimclauses[first] = tmp; + + // Store the length of the clause last: + elimclauses.push(c.size()); +} + + + +bool SimpSolver::eliminateVar(Var v) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + // Split the occurrences into positive and negative: + // + const vec& cls = occurs.lookup(v); + vec pos, neg; + for (int i = 0; i < cls.size(); i++) + (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); + + // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no + // clause must exceed the limit on the maximal clause size (if it is set): + // + int cnt = 0; + int clause_size = 0; + + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) && + (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim))) + return true; + + // Delete and store old clauses + eliminated[v] = true; + setDecisionVar(v, false); + eliminated_vars++; + + if (pos.size() > neg.size()){ + for (int i = 0; i < neg.size(); i++) + mkElimClause(elimclauses, v, ca[neg[i]]); + mkElimClause(elimclauses, mkLit(v)); + }else{ + for (int i = 0; i < pos.size(); i++) + mkElimClause(elimclauses, v, ca[pos[i]]); + mkElimClause(elimclauses, ~mkLit(v)); + } + + + // Produce clauses in cross product: + vec& resolvent = add_tmp; + for (int i = 0; i < pos.size(); i++) + for (int j = 0; j < neg.size(); j++) + if (merge(ca[pos[i]], ca[neg[j]], v, resolvent) && !addClause_(resolvent)) + return false; + + for (int i = 0; i < cls.size(); i++) + removeClause(cls[i]); + + // Free occurs list for this variable: + occurs[v].clear(true); + + // Free watchers lists for this variable, if possible: + if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true); + if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true); + + return backwardSubsumptionCheck(); +} + + +bool SimpSolver::substitute(Var v, Lit x) +{ + assert(!frozen[v]); + assert(!isEliminated(v)); + assert(value(v) == l_Undef); + + if (!ok) return false; + + eliminated[v] = true; + setDecisionVar(v, false); + const vec& cls = occurs.lookup(v); + + vec& subst_clause = add_tmp; + for (int i = 0; i < cls.size(); i++){ + Clause& c = ca[cls[i]]; + + subst_clause.clear(); + for (int j = 0; j < c.size(); j++){ + Lit p = c[j]; + subst_clause.push(var(p) == v ? x ^ sign(p) : p); + } + + + if (!addClause_(subst_clause)) + return ok = false; + + removeClause(cls[i]); + + } + + return true; +} + + +void SimpSolver::extendModel() +{ + int i, j; + Lit x; + + if(model.size()==0) model.growTo(nVars()); + + for (i = elimclauses.size()-1; i > 0; i -= j){ + for (j = elimclauses[i--]; j > 1; j--, i--) + if (modelValue(toLit(elimclauses[i])) != l_False) + goto next; + + x = toLit(elimclauses[i]); + model[var(x)] = lbool(!sign(x)); + next:; + } +} + + +bool SimpSolver::eliminate(bool turn_off_elim) +{ + if (!simplify()) { + ok = false; + return false; + } + else if (!use_simplification) + return true; + + // Main simplification loop: + // + + int toPerform = clauses.size()<=4800000; + + if(!toPerform) { + printf("c Too many clauses... No preprocessing\n"); + } + + while (toPerform && (n_touched > 0 || bwdsub_assigns < trail.size() || elim_heap.size() > 0)){ + + gatherTouchedClauses(); + // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) && + !backwardSubsumptionCheck(true)){ + ok = false; goto cleanup; } + + // Empty elim_heap and return immediately on user-interrupt: + if (asynch_interrupt){ + assert(bwdsub_assigns == trail.size()); + assert(subsumption_queue.size() == 0); + assert(n_touched == 0); + elim_heap.clear(); + goto cleanup; } + + // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); + for (int cnt = 0; !elim_heap.empty(); cnt++){ + Var elim = elim_heap.removeMin(); + + if (asynch_interrupt) break; + + if (isEliminated(elim) || value(elim) != l_Undef) continue; + + if (verbosity >= 2 && cnt % 100 == 0) + printf("elimination left: %10d\r", elim_heap.size()); + + if (use_asymm){ + // Temporarily freeze variable. Otherwise, it would immediately end up on the queue again: + bool was_frozen = frozen[elim]; + frozen[elim] = true; + if (!asymmVar(elim)){ + ok = false; goto cleanup; } + frozen[elim] = was_frozen; } + + // At this point, the variable may have been set by assymetric branching, so check it + // again. Also, don't eliminate frozen variables: + if (use_elim && value(elim) == l_Undef && !frozen[elim] && !eliminateVar(elim)){ + ok = false; goto cleanup; } + + checkGarbage(simp_garbage_frac); + } + + assert(subsumption_queue.size() == 0); + } + cleanup: + + // If no more simplification is needed, free all simplification-related data structures: + if (turn_off_elim){ + touched .clear(true); + occurs .clear(true); + n_occ .clear(true); + elim_heap.clear(true); + subsumption_queue.clear(true); + + use_simplification = false; + remove_satisfied = true; + ca.extra_clause_field = false; + + // Force full cleanup (this is safe and desirable since it only happens once): + rebuildOrderHeap(); + garbageCollect(); + }else{ + // Cheaper cleanup: + cleanUpClauses(); // TODO: can we make 'cleanUpClauses()' not be linear in the problem size somehow? + checkGarbage(); + } + + if (verbosity >= 0 && elimclauses.size() > 0) + printf("c | Eliminated clauses: %10.2f Mb |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024)); + + + return ok; + + +} + + +void SimpSolver::cleanUpClauses() +{ + occurs.cleanAll(); + int i,j; + for (i = j = 0; i < clauses.size(); i++) + if (ca[clauses[i]].mark() == 0) + clauses[j++] = clauses[i]; + clauses.shrink(i - j); +} + + +//================================================================================================= +// Garbage Collection methods: + + +void SimpSolver::relocAll(ClauseAllocator& to) +{ + if (!use_simplification) return; + + // All occurs lists: + // + for (int i = 0; i < nVars(); i++){ + vec& cs = occurs[i]; + for (int j = 0; j < cs.size(); j++) + ca.reloc(cs[j], to); + } + + // Subsumption queue: + // + for (int i = 0; i < subsumption_queue.size(); i++) + ca.reloc(subsumption_queue[i], to); + + // Temporary clause: + // + ca.reloc(bwdsub_tmpunit, to); +} + + +void SimpSolver::garbageCollect() +{ + // Initialize the next region to a size corresponding to the estimated utilization degree. This + // is not precise but should avoid some unnecessary reallocations for the new region: + ClauseAllocator to(ca.size() - ca.wasted()); + + cleanUpClauses(); + to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. + relocAll(to); + Solver::relocAll(to); + if (verbosity >= 2) + printf("| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + to.moveTo(ca); +} diff --git a/libs/mugen/glucose-syrup-4.1/simp/SimpSolver.h b/libs/mugen/glucose-syrup-4.1/simp/SimpSolver.h new file mode 100644 index 000000000..42f68e89c --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/simp/SimpSolver.h @@ -0,0 +1,237 @@ +/***************************************************************************************[SimpSolver.h] + Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + LRI - Univ. Paris Sud, France (2009-2013) + Labri - Univ. Bordeaux, France + + Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon + CRIL - Univ. Artois, France + Labri - Univ. Bordeaux, France + +Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of +Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it +is based on. (see below). + +Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel +version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software +without restriction, including the rights to use, copy, modify, merge, publish, distribute, +sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +- The above and below copyrights notices and this permission notice shall be included in all +copies or substantial portions of the Software; +- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot +be used in any competitive event (sat competitions/evaluations) without the express permission of +the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event +using Glucose Parallel as an embedded SAT engine (single core or not). + + +--------------- Original Minisat Copyrights + +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **************************************************************************************************/ + +#ifndef Glucose_SimpSolver_h +#define Glucose_SimpSolver_h + +#include "mtl/Queue.h" +#include "core/Solver.h" +#include "mtl/Clone.h" + +namespace Glucose { + +//================================================================================================= + + +class SimpSolver : public Solver { + public: + // Constructor/Destructor: + // + SimpSolver(); + ~SimpSolver(); + + SimpSolver(const SimpSolver &s); + + + /** + * Clone function + */ + virtual Clone* clone() const { + return new SimpSolver(*this); + } + + + // Problem specification: + // + virtual Var newVar (bool polarity = true, bool dvar = true); // Add a new variable with parameters specifying variable mode. + bool addClause (const vec& ps); + bool addEmptyClause(); // Add the empty clause to the solver. + bool addClause (Lit p); // Add a unit clause to the solver. + bool addClause (Lit p, Lit q); // Add a binary clause to the solver. + bool addClause (Lit p, Lit q, Lit r); // Add a ternary clause to the solver. + virtual bool addClause_( vec& ps); + bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction). + + // Variable mode: + // + void setFrozen (Var v, bool b); // If a variable is frozen it will not be eliminated. + bool isEliminated(Var v) const; + + // Solving: + // + bool solve (const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + lbool solveLimited(const vec& assumps, bool do_simp = true, bool turn_off_simp = false); + bool solve ( bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p , bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, bool do_simp = true, bool turn_off_simp = false); + bool solve (Lit p, Lit q, Lit r, bool do_simp = true, bool turn_off_simp = false); + bool eliminate (bool turn_off_elim = false); // Perform variable elimination based simplification. + + // Memory managment: + // + virtual void garbageCollect(); + + + // Generate a (possibly simplified) DIMACS file: + // +#if 0 + void toDimacs (const char* file, const vec& assumps); + void toDimacs (const char* file); + void toDimacs (const char* file, Lit p); + void toDimacs (const char* file, Lit p, Lit q); + void toDimacs (const char* file, Lit p, Lit q, Lit r); +#endif + + // Mode of operation: + // + int parsing; + int grow; // Allow a variable elimination step to grow by a number of clauses (default to zero). + int clause_lim; // Variables are not eliminated if it produces a resolvent with a length above this limit. + // -1 means no limit. + int subsumption_lim; // Do not check if subsumption against a clause larger than this. -1 means no limit. + double simp_garbage_frac; // A different limit for when to issue a GC during simplification (Also see 'garbage_frac'). + + bool use_asymm; // Shrink clauses by asymmetric branching. + bool use_rcheck; // Check if a clause is already implied. Prett costly, and subsumes subsumptions :) + bool use_elim; // Perform variable elimination. + // Statistics: + // + int merges; + int asymm_lits; + int eliminated_vars; + bool use_simplification; + + protected: + + // Helper structures: + // + struct ElimLt { + const vec& n_occ; + explicit ElimLt(const vec& no) : n_occ(no) {} + + // TODO: are 64-bit operations here noticably bad on 32-bit platforms? Could use a saturating + // 32-bit implementation instead then, but this will have to do for now. + uint64_t cost (Var x) const { return (uint64_t)n_occ[toInt(mkLit(x))] * (uint64_t)n_occ[toInt(~mkLit(x))]; } + bool operator()(Var x, Var y) const { return cost(x) < cost(y); } + + // TODO: investigate this order alternative more. + // bool operator()(Var x, Var y) const { + // int c_x = cost(x); + // int c_y = cost(y); + // return c_x < c_y || c_x == c_y && x < y; } + }; + + struct ClauseDeleted { + const ClauseAllocator& ca; + explicit ClauseDeleted(const ClauseAllocator& _ca) : ca(_ca) {} + bool operator()(const CRef& cr) const { return ca[cr].mark() == 1; } }; + + // Solver state: + // + int elimorder; + vec elimclauses; + vec touched; + OccLists, ClauseDeleted> + occurs; + vec n_occ; + Heap elim_heap; + Queue subsumption_queue; + vec frozen; + vec eliminated; + int bwdsub_assigns; + int n_touched; + + // Temporaries: + // + CRef bwdsub_tmpunit; + + // Main internal methods: + // + virtual lbool solve_ (bool do_simp = true, bool turn_off_simp = false); + bool asymm (Var v, CRef cr); + bool asymmVar (Var v); + void updateElimHeap (Var v); + void gatherTouchedClauses (); + bool merge (const Clause& _ps, const Clause& _qs, Var v, vec& out_clause); + bool merge (const Clause& _ps, const Clause& _qs, Var v, int& size); + bool backwardSubsumptionCheck (bool verbose = false); + bool eliminateVar (Var v); + void extendModel (); + + void removeClause (CRef cr,bool inPurgatory=false); + bool strengthenClause (CRef cr, Lit l); + void cleanUpClauses (); + bool implied (const vec& c); + virtual void relocAll (ClauseAllocator& to); +}; + + +//================================================================================================= +// Implementation of inline methods: + + +inline bool SimpSolver::isEliminated (Var v) const { return eliminated[v]; } +inline void SimpSolver::updateElimHeap(Var v) { + assert(use_simplification); + // if (!frozen[v] && !isEliminated(v) && value(v) == l_Undef) + if (elim_heap.inHeap(v) || (!frozen[v] && !isEliminated(v) && value(v) == l_Undef)) + elim_heap.update(v); } + + +inline bool SimpSolver::addClause (const vec& ps) { ps.copyTo(add_tmp); return addClause_(add_tmp); } +inline bool SimpSolver::addEmptyClause() { add_tmp.clear(); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p) { add_tmp.clear(); add_tmp.push(p); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp); } +inline bool SimpSolver::addClause (Lit p, Lit q, Lit r) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp); } +inline void SimpSolver::setFrozen (Var v, bool b) { frozen[v] = (char)b; if (use_simplification && !b) { updateElimHeap(v); } } + +inline bool SimpSolver::solve ( bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p , bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (Lit p, Lit q, Lit r, bool do_simp, bool turn_off_simp) { budgetOff(); assumptions.clear(); assumptions.push(p); assumptions.push(q); assumptions.push(r); return solve_(do_simp, turn_off_simp) == l_True; } +inline bool SimpSolver::solve (const vec& assumps, bool do_simp, bool turn_off_simp){ + budgetOff(); assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp) == l_True; } + +inline lbool SimpSolver::solveLimited (const vec& assumps, bool do_simp, bool turn_off_simp){ + assumps.copyTo(assumptions); return solve_(do_simp, turn_off_simp); } + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/utils/Makefile b/libs/mugen/glucose-syrup-4.1/utils/Makefile new file mode 100644 index 000000000..204cea541 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/utils/Makefile @@ -0,0 +1,4 @@ +EXEC = system_test +DEPDIR = mtl + +include $(MROOT)/mtl/template.mk diff --git a/libs/mugen/glucose-syrup-4.1/utils/Options.cc b/libs/mugen/glucose-syrup-4.1/utils/Options.cc new file mode 100644 index 000000000..73d7f5805 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/utils/Options.cc @@ -0,0 +1,91 @@ +/**************************************************************************************[Options.cc] +Copyright (c) 2008-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#include "mtl/Sort.h" +#include "utils/Options.h" +#include "utils/ParseUtils.h" + +using namespace Glucose; + +void Glucose::parseOptions(int& argc, char** argv, bool strict) +{ + int i, j; + for (i = j = 1; i < argc; i++){ + const char* str = argv[i]; + if (match(str, "--") && match(str, Option::getHelpPrefixString()) && match(str, "help")){ + if (*str == '\0') + printUsageAndExit(argc, argv); + else if (match(str, "-verb")) + printUsageAndExit(argc, argv, true); + } else { + bool parsed_ok = false; + + for (int k = 0; !parsed_ok && k < Option::getOptionList().size(); k++){ + parsed_ok = Option::getOptionList()[k]->parse(argv[i]); + + // fprintf(stderr, "checking %d: %s against flag <%s> (%s)\n", i, argv[i], Option::getOptionList()[k]->name, parsed_ok ? "ok" : "skip"); + } + + if (!parsed_ok) + if (strict && match(argv[i], "-")) + fprintf(stderr, "ERROR! Unknown flag \"%s\". Use '--%shelp' for help.\n", argv[i], Option::getHelpPrefixString()), exit(1); + else + argv[j++] = argv[i]; + } + } + + argc -= (i - j); +} + + +void Glucose::setUsageHelp (const char* str){ Option::getUsageString() = str; } +void Glucose::setHelpPrefixStr (const char* str){ Option::getHelpPrefixString() = str; } +void Glucose::printUsageAndExit (int argc, char** argv, bool verbose) +{ + const char* usage = Option::getUsageString(); + if (usage != NULL) + fprintf(stderr, usage, argv[0]); + + sort(Option::getOptionList(), Option::OptionLt()); + + const char* prev_cat = NULL; + const char* prev_type = NULL; + + for (int i = 0; i < Option::getOptionList().size(); i++){ + const char* cat = Option::getOptionList()[i]->category; + const char* type = Option::getOptionList()[i]->type_name; + + if (cat != prev_cat) + fprintf(stderr, "\n%s OPTIONS:\n\n", cat); + else if (type != prev_type) + fprintf(stderr, "\n"); + + Option::getOptionList()[i]->help(verbose); + + prev_cat = Option::getOptionList()[i]->category; + prev_type = Option::getOptionList()[i]->type_name; + } + + fprintf(stderr, "\nHELP OPTIONS:\n\n"); + fprintf(stderr, " --%shelp Print help message.\n", Option::getHelpPrefixString()); + fprintf(stderr, " --%shelp-verb Print verbose help message.\n", Option::getHelpPrefixString()); + fprintf(stderr, "\n"); + exit(0); +} + diff --git a/libs/mugen/glucose-syrup-4.1/utils/Options.h b/libs/mugen/glucose-syrup-4.1/utils/Options.h new file mode 100644 index 000000000..a86e4c7e4 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/utils/Options.h @@ -0,0 +1,386 @@ +/***************************************************************************************[Options.h] +Copyright (c) 2008-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_Options_h +#define Glucose_Options_h + +#include +#include +#include +#include + +#include "mtl/IntTypes.h" +#include "mtl/Vec.h" +#include "utils/ParseUtils.h" + +namespace Glucose { + +//================================================================================================== +// Top-level option parse/help functions: + + +extern void parseOptions (int& argc, char** argv, bool strict = false); +extern void printUsageAndExit(int argc, char** argv, bool verbose = false); +extern void setUsageHelp (const char* str); +extern void setHelpPrefixStr (const char* str); + + +//================================================================================================== +// Options is an abstract class that gives the interface for all types options: + + +class Option +{ + protected: + const char* name; + const char* description; + const char* category; + const char* type_name; + + static vec& getOptionList () { static vec options; return options; } + static const char*& getUsageString() { static const char* usage_str; return usage_str; } + static const char*& getHelpPrefixString() { static const char* help_prefix_str = ""; return help_prefix_str; } + + struct OptionLt { + bool operator()(const Option* x, const Option* y) { + int test1 = strcmp(x->category, y->category); + return test1 < 0 || test1 == 0 && strcmp(x->type_name, y->type_name) < 0; + } + }; + + Option(const char* name_, + const char* desc_, + const char* cate_, + const char* type_) : + name (name_) + , description(desc_) + , category (cate_) + , type_name (type_) + { + getOptionList().push(this); + } + + public: + virtual ~Option() {} + + virtual bool parse (const char* str) = 0; + virtual void help (bool verbose = false) = 0; + + friend void parseOptions (int& argc, char** argv, bool strict); + friend void printUsageAndExit (int argc, char** argv, bool verbose); + friend void setUsageHelp (const char* str); + friend void setHelpPrefixStr (const char* str); +}; + + +//================================================================================================== +// Range classes with specialization for floating types: + + +struct IntRange { + int begin; + int end; + IntRange(int b, int e) : begin(b), end(e) {} +}; + +struct Int64Range { + int64_t begin; + int64_t end; + Int64Range(int64_t b, int64_t e) : begin(b), end(e) {} +}; + +struct DoubleRange { + double begin; + double end; + bool begin_inclusive; + bool end_inclusive; + DoubleRange(double b, bool binc, double e, bool einc) : begin(b), end(e), begin_inclusive(binc), end_inclusive(einc) {} +}; + + +//================================================================================================== +// Double options: + + +class DoubleOption : public Option +{ + protected: + DoubleRange range; + double value; + + public: + DoubleOption(const char* c, const char* n, const char* d, double def = double(), DoubleRange r = DoubleRange(-HUGE_VAL, false, HUGE_VAL, false)) + : Option(n, d, c, ""), range(r), value(def) { + // FIXME: set LC_NUMERIC to "C" to make sure that strtof/strtod parses decimal point correctly. + } + + operator double (void) const { return value; } + operator double& (void) { return value; } + DoubleOption& operator=(double x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + double tmp = strtod(span, &end); + + if (end == NULL) + return false; + else if (tmp >= range.end && (!range.end_inclusive || tmp != range.end)){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp <= range.begin && (!range.begin_inclusive || tmp != range.begin)){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + // fprintf(stderr, "READ VALUE: %g\n", value); + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s %c%4.2g .. %4.2g%c (default: %g)\n", + name, type_name, + range.begin_inclusive ? '[' : '(', + range.begin, + range.end, + range.end_inclusive ? ']' : ')', + value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Int options: + + +class IntOption : public Option +{ + protected: + IntRange range; + int32_t value; + + public: + IntOption(const char* c, const char* n, const char* d, int32_t def = int32_t(), IntRange r = IntRange(INT32_MIN, INT32_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int32_t (void) const { return value; } + operator int32_t& (void) { return value; } + IntOption& operator= (int32_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int32_t tmp = strtol(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT32_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4d", range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT32_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4d", range.end); + + fprintf(stderr, "] (default: %d)\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +// Leave this out for visual C++ until Microsoft implements C99 and gets support for strtoll. +#ifndef _MSC_VER + +class Int64Option : public Option +{ + protected: + Int64Range range; + int64_t value; + + public: + Int64Option(const char* c, const char* n, const char* d, int64_t def = int64_t(), Int64Range r = Int64Range(INT64_MIN, INT64_MAX)) + : Option(n, d, c, ""), range(r), value(def) {} + + operator int64_t (void) const { return value; } + operator int64_t& (void) { return value; } + Int64Option& operator= (int64_t x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + char* end; + int64_t tmp = strtoll(span, &end, 10); + + if (end == NULL) + return false; + else if (tmp > range.end){ + fprintf(stderr, "ERROR! value <%s> is too large for option \"%s\".\n", span, name); + exit(1); + }else if (tmp < range.begin){ + fprintf(stderr, "ERROR! value <%s> is too small for option \"%s\".\n", span, name); + exit(1); } + + value = tmp; + + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-12s = %-8s [", name, type_name); + if (range.begin == INT64_MIN) + fprintf(stderr, "imin"); + else + fprintf(stderr, "%4" PRIi64, range.begin); + + fprintf(stderr, " .. "); + if (range.end == INT64_MAX) + fprintf(stderr, "imax"); + else + fprintf(stderr, "%4" PRIi64, range.end); + + fprintf(stderr, "] (default: %" PRIi64")\n", value); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; +#endif + +//================================================================================================== +// String option: + + +class StringOption : public Option +{ + const char* value; + public: + StringOption(const char* c, const char* n, const char* d, const char* def = NULL) + : Option(n, d, c, ""), value(def) {} + + operator const char* (void) const { return value; } + operator const char*& (void) { return value; } + StringOption& operator= (const char* x) { value = x; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (!match(span, "-") || !match(span, name) || !match(span, "=")) + return false; + + value = span; + return true; + } + + virtual void help (bool verbose = false){ + fprintf(stderr, " -%-10s = %8s\n", name, type_name); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + + +//================================================================================================== +// Bool option: + + +class BoolOption : public Option +{ + bool value; + + public: + BoolOption(const char* c, const char* n, const char* d, bool v) + : Option(n, d, c, ""), value(v) {} + + operator bool (void) const { return value; } + operator bool& (void) { return value; } + BoolOption& operator=(bool b) { value = b; return *this; } + + virtual bool parse(const char* str){ + const char* span = str; + + if (match(span, "-")){ + bool b = !match(span, "no-"); + + if (strcmp(span, name) == 0){ + value = b; + return true; } + } + + return false; + } + + virtual void help (bool verbose = false){ + + fprintf(stderr, " -%s, -no-%s", name, name); + + for (uint32_t i = 0; i < 32 - strlen(name)*2; i++) + fprintf(stderr, " "); + + fprintf(stderr, " "); + fprintf(stderr, "(default: %s)\n", value ? "on" : "off"); + if (verbose){ + fprintf(stderr, "\n %s\n", description); + fprintf(stderr, "\n"); + } + } +}; + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/utils/ParseUtils.h b/libs/mugen/glucose-syrup-4.1/utils/ParseUtils.h new file mode 100644 index 000000000..f41108610 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/utils/ParseUtils.h @@ -0,0 +1,151 @@ +/************************************************************************************[ParseUtils.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_ParseUtils_h +#define Glucose_ParseUtils_h + +#include +#include +#include + +#include + +namespace Glucose { + +//------------------------------------------------------------------------------------------------- +// A simple buffered character stream class: + +static const int buffer_size = 1048576; + + +class StreamBuffer { + gzFile in; + unsigned char buf[buffer_size]; + int pos; + int size; + + void assureLookahead() { + if (pos >= size) { + pos = 0; + size = gzread(in, buf, sizeof(buf)); } } + +public: + explicit StreamBuffer(gzFile i) : in(i), pos(0), size(0) { assureLookahead(); } + + int operator * () const { return (pos >= size) ? EOF : buf[pos]; } + void operator ++ () { pos++; assureLookahead(); } + int position () const { return pos; } +}; + + +//------------------------------------------------------------------------------------------------- +// End-of-file detection functions for StreamBuffer and char*: + + +static inline bool isEof(StreamBuffer& in) { return *in == EOF; } +static inline bool isEof(const char* in) { return *in == '\0'; } + +//------------------------------------------------------------------------------------------------- +// Generic parse functions parametrized over the input-stream type. + + +template +static void skipWhitespace(B& in) { + while ((*in >= 9 && *in <= 13) || *in == 32) + ++in; } + + +template +static void skipLine(B& in) { + for (;;){ + if (isEof(in)) return; + if (*in == '\n') { ++in; return; } + ++in; } } + +template +static double parseDouble(B& in) { // only in the form X.XXXXXe-XX + bool neg= false; + double accu = 0.0; + double currentExponent = 1; + int exponent; + + skipWhitespace(in); + if(*in == EOF) return 0; + if (*in == '-') neg = true, ++in; + else if (*in == '+') ++in; + if (*in < '1' || *in > '9') printf("PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + accu = (double)(*in - '0'); + ++in; + if (*in != '.') printf("PARSE ERROR! Unexpected char: %c\n", *in),exit(3); + ++in; // skip dot + currentExponent = 0.1; + while (*in >= '0' && *in <= '9') + accu = accu + currentExponent * ((double)(*in - '0')), + currentExponent /= 10, + ++in; + if (*in != 'e') printf("PARSE ERROR! Unexpected char: %c\n", *in),exit(3); + ++in; // skip dot + exponent = parseInt(in); // read exponent + accu *= pow(10,exponent); + return neg ? -accu:accu; +} + + +template +static int parseInt(B& in) { + int val = 0; + bool neg = false; + skipWhitespace(in); + if (*in == '-') neg = true, ++in; + else if (*in == '+') ++in; + if (*in < '0' || *in > '9') fprintf(stderr, "PARSE ERROR! Unexpected char: %c\n", *in), exit(3); + while (*in >= '0' && *in <= '9') + val = val*10 + (*in - '0'), + ++in; + return neg ? -val : val; } + + +// String matching: in case of a match the input iterator will be advanced the corresponding +// number of characters. +template +static bool match(B& in, const char* str) { + int i; + for (i = 0; str[i] != '\0'; i++) + if (in[i] != str[i]) + return false; + + in += i; + + return true; +} + +// String matching: consumes characters eagerly, but does not require random access iterator. +template +static bool eagerMatch(B& in, const char* str) { + for (; *str != '\0'; ++str, ++in) + if (*str != *in) + return false; + return true; } + + +//================================================================================================= +} + +#endif diff --git a/libs/mugen/glucose-syrup-4.1/utils/System.cc b/libs/mugen/glucose-syrup-4.1/utils/System.cc new file mode 100644 index 000000000..a516e0b9d --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/utils/System.cc @@ -0,0 +1,95 @@ +/***************************************************************************************[System.cc] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#include "utils/System.h" + +#if defined(__linux__) + +#include +#include + +using namespace Glucose; + +// TODO: split the memory reading functions into two: one for reading high-watermark of RSS, and +// one for reading the current virtual memory size. + +static inline int memReadStat(int field) +{ + char name[256]; + pid_t pid = getpid(); + int value; + + sprintf(name, "/proc/%d/statm", pid); + FILE* in = fopen(name, "rb"); + if (in == NULL) return 0; + + for (; field >= 0; field--) + if (fscanf(in, "%d", &value) != 1) + printf("ERROR! Failed to parse memory statistics from \"/proc\".\n"), exit(1); + fclose(in); + return value; +} + + +static inline int memReadPeak(void) +{ + char name[256]; + pid_t pid = getpid(); + + sprintf(name, "/proc/%d/status", pid); + FILE* in = fopen(name, "rb"); + if (in == NULL) return 0; + + // Find the correct line, beginning with "VmPeak:": + int peak_kb = 0; + while (!feof(in) && fscanf(in, "VmPeak: %d kB", &peak_kb) != 1) + while (!feof(in) && fgetc(in) != '\n') + ; + fclose(in); + + return peak_kb; +} + +double Glucose::memUsed() { return (double)memReadStat(0) * (double)getpagesize() / (1024*1024); } +double Glucose::memUsedPeak() { + double peak = memReadPeak() / 1024; + return peak == 0 ? memUsed() : peak; } + +#elif defined(__FreeBSD__) + +double Glucose::memUsed(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_maxrss / 1024; } +double MiniSat::memUsedPeak(void) { return memUsed(); } + + +#elif defined(__APPLE__) +#include + +double Glucose::memUsed(void) { + malloc_statistics_t t; + malloc_zone_statistics(NULL, &t); + return (double)t.max_size_in_use / (1024*1024); } + +#else +double Glucose::memUsed() { + return 0; } +#endif diff --git a/libs/mugen/glucose-syrup-4.1/utils/System.h b/libs/mugen/glucose-syrup-4.1/utils/System.h new file mode 100644 index 000000000..9b43c12a0 --- /dev/null +++ b/libs/mugen/glucose-syrup-4.1/utils/System.h @@ -0,0 +1,64 @@ +/****************************************************************************************[System.h] +Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson +Copyright (c) 2007-2010, Niklas Sorensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +**************************************************************************************************/ + +#ifndef Glucose_System_h +#define Glucose_System_h + + +#include "mtl/IntTypes.h" + +//------------------------------------------------------------------------------------------------- + +namespace Glucose { + +static inline double cpuTime(void); // CPU-time in seconds. +static inline double realTime(void); +extern double memUsed(); // Memory in mega bytes (returns 0 for unsupported architectures). +extern double memUsedPeak(); // Peak-memory in mega bytes (returns 0 for unsupported architectures). + +} + +//------------------------------------------------------------------------------------------------- +// Implementation of inline functions: + +#if defined(_MSC_VER) || defined(__MINGW32__) +#include + +static inline double Glucose::cpuTime(void) { return (double)clock() / CLOCKS_PER_SEC; } + +#else +#include +#include +#include + +static inline double Glucose::cpuTime(void) { + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; } + +#endif + +// Laurent: I know that this will not compile directly under Windows... sorry for that +static inline double Glucose::realTime() { + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double) tv.tv_usec / 1000000; } + +#endif diff --git a/libs/mugen/mugen.py b/libs/mugen/mugen.py new file mode 100644 index 000000000..c379ca363 --- /dev/null +++ b/libs/mugen/mugen.py @@ -0,0 +1,1443 @@ +try: + from graphviz import Digraph +except ModuleNotFoundError: + pass +import itertools +from math import log2 +from pysat.solvers import Glucose3 +from pysat.card import * +import sys +import subprocess +import os +import tempfile +import wrapt_timeout_decorator + +class SynthesisException(Exception): + ''' + Mugen's generic exception class that is thrown whenever something + unexpected happens during synthesis. + ''' + + def __init__(self, message): + ''' + :param message: The message to show when the exception is thrown. + ''' + self.message = message + +CARDINAL_DIRECTIONS = set(['NORTH', 'EAST', 'SOUTH', 'WEST']) + +OPPOSITE_DIRECTION = { + 'NORTH': 'SOUTH', + 'EAST' : 'WEST', + 'SOUTH': 'NORTH', + 'WEST' : 'EAST' +} + +# Mapping gate types to the corresponding number of fanins. +GATE_FANIN_RANGE = { + 'NOT': 1, + 'AND': 2, + 'OR': 2, + 'MAJ': 3, + 'WIRE': 1, + 'EMPTY': 0, + 'CROSS': 2 +} + +# Some utility functions. +def is_north(coords1, coords2): + ''' + Returns true if and only if coords1 lies to the north of coords2. We say + this is the case if they have the same horizontal position but coords1 has + a lower vertical position. + ''' + return coords1[0] == coords2[0] and coords1[1] < coords2[1] + +def is_east(coords1, coords2): + ''' + Returns true if and only if coords1 lies to the east of coords2. We say + this is the case if they have the same vertical position but coords1 has a + higher horizontal position. + ''' + return coords1[0] > coords2[0] and coords1[1] == coords2[1] + +def is_south(coords1, coords2): + ''' + Returns true if and only if coords1 lies to the south of coords2. We say + this is the case if they have the same horizontal position but coords1 has + a higher vertical position. + ''' + return coords1[0] == coords2[0] and coords1[1] > coords2[1] + +def is_west(coords1, coords2): + ''' + Returns true if and only if coords1 lies to the west of coords2. We say + this is the case if they have the same vertical position but coords1 has + a lower horizontal position. + ''' + return coords1[0] < coords2[0] and coords1[1] == coords2[1] + +def get_direction(coords1, coords2): + ''' + Given a pair of coordinates, determines in which cardinal + direction the information flows. Returns the result as a pair + (d1, d2), where d1 is the direction in which the signal leaves + from coords1 and d2 is the direction in which it arrives at + coords2. + ''' + if is_north(coords1, coords2): + return ('SOUTH', 'NORTH') + elif is_east(coords1, coords2): + return ('WEST', 'EAST') + elif is_south(coords1, coords2): + return ('NORTH', 'SOUTH') + elif is_west(coords1, coords2): + return ('EAST', 'WEST') + else: + raise SynthesisException('Unknown direction') + +def get_coords_in_direction(coords, direction): + ''' + Given a set of coordinates and a cardinal direction. Returns the + coordinates of a node immediately adjacent to the current one in + the given direction. Note that this may result in a set of + coordinates that are not within the bounds of a clocking scheme + (e.g. this may result in negative coordinates). + ''' + if direction == 'NORTH': + return (coords[0], coords[1] - 1) + elif direction == 'EAST': + return (coords[0] + 1, coords[1]) + elif direction == 'SOUTH': + return (coords[0], coords[1] + 1) + elif direction == 'WEST': + return (coords[0] - 1, coords[1]) + else: + raise SynthesisException("Unknown cardinal direction: '{}'".format(direction)) + +def eval_gate(gate_type, inputs): + ''' + Evaluates a certain gate type on a list of binary input values. Returns the + result. + + :param gate_type: The type of gate to evaluate. Choices are EMPTY, WIRE, NOT, AND, OR, and MAJ. + :param inputs: List of input values. + ''' + if gate_type == 'EMPTY': + return 0 + elif gate_type == 'WIRE': + return inputs[0] + elif gate_type == 'NOT': + return 1 - inputs[0] + elif gate_type == 'AND': + return inputs[0] & inputs[1] + elif gate_type == 'OR': + return inputs[0] | inputs[1] + elif gate_type == 'MAJ': + return (inputs[0] & inputs[1]) | (inputs[0] & inputs[2]) | (inputs[1] & inputs[2]) + else: + raise SynthesisException("No evaluation support for gate type '{}'".format(gate_type)) + +class node: + ''' + A generic node class, used by both clocking scheme graphs + and logic networks. + + :ivar coords: The grid coordinates of the node in the clocking scheme. + :ivar is_pi: Is the node a primary input. + :ivar is_po: Is the node a primary output. + :ivar fanin: A list of nodes containing the fanin of the current node. + :ivar fanout: A list of nodes containing the fanin of the current node. + :ivar is_border_node: This Boolean flag is True iff the node lies on the border of the clocking scheme. + :ivar gate_type: The gate type of the node. + :ivar dir_map: This optional attribute is set only for nodes with gate_type CROSS. It is a dictionary which maps fanin directions to output directions. For example, if dir_map = { 'WEST': 'NORTH', 'EAST': 'SOUTH'}, then the western fanin is mapped to the northern fanout port and the eastern fanin is mapped to the southern fanout port. + ''' + + def __init__(self, *, coords=None, is_pi=False, is_po=False): + ''' + Creates a new logic node. + + :param coords: the grid coordinates of the node in the clocking scheme. + :param is_pi: is the node a primary input. + :param is_po: is the node a primary output. + + ''' + self.coords = coords + self.is_pi = is_pi + self.virtual_fanin = [] + self.virtual_fanout = [] + self.is_border_node = False + self.is_po = is_po + self.fanin = {} + self.fanout = {} + self.gate_type = None + # self.dir_map = {} + + def set_fanin(self, in_dir, innode, out_dir): + ''' + Sets the fanin port at direction d of this node to innode and + updates the fanout of innode by appending this node to it. + ''' + self.fanin[in_dir] = innode + innode.fanout[out_dir] = self + + def __repr__(self): + if self.is_pi: + return 'PI{}'.format(self.coords) + else: + return ''.format(self.coords) + + def __lt__(self, other): + return self.coords < other.coords + +class logic_network: + + ''' + A logic_network is the result of synthesis. Its design is similar + to a clocking scheme graph. A major difference is that it cannot + contain cycles. However, it can also be accessed using the same + tile coordinate based API. + + :ivar shape: a 2-tuple containing the size of the grid + (width x height). + :ivar nr_pis: number of PIs. + :ivar nr_pos: number of POs. + :ivar nodes: A list of all the nodes in the logic network, including the PIs. + :ivar node_map: A map from tile coordinates to nodes nodes in the logic network. E.g. to access the node corresponding to tile (0,0) one refers to node_map[(0,0)]. + :ivar po_map: A list of size nr_pos mapping output indices to nodes in the network. E.g. to access the first output, one refers to po_map[0]. + ''' + + def __init__(self, shape, nr_pis, nr_pos): + ''' + Creates a new logic network. + + :param shape: a 2-tuple containing the size of the grid + (width x height). + :param nr_pis: number of PIs. + :param nr_pos: number of POs. + ''' + + + self.shape = shape + self.nr_pis = nr_pis + self.nr_pos = nr_pos + + self.nodes = [node(coords=i, is_pi=True) for i in range(nr_pis)] + self.node_map = {} + for y in range(shape[1]): + for x in range(shape[0]): + n = node(coords=(x, y)) + if x == 0 or x == (shape[0] - 1) or y == 0 or y == (shape[1] - 1): + n.is_border_node = True + self.nodes.append(n) + self.node_map[(x,y)] = n + + self.po_map = [None] * nr_pos + + def set_output(self, h, coords, d): + ''' + Marks the output port in direction d for the node at coords as the + h-th output of the network. + ''' + n = self.node_map[coords] + n.fanout[d] = 'PO{}'.format(h) + n.is_po = True + self.po_map[h] = (n, d) + + def __repr__(self): + r = '\n' + for n in self.nodes: + if n.is_pi: + continue + r += ' 1: + return False + return True + + def verify_designated_pi(self): + ''' + The same as :func:`has_designated_pi` but raises a + :class:`SynthesisException` if the spec is not met. + ''' + for n in self.nodes: + if n.is_pi: + continue + if n.gate_type != 'WIRE': + for innode in n.fanin.values(): + if innode.is_pi: + raise SynthesisException('{} has gate type {} and fanin PI_{}'.format( + n.coords, n.gate_type, innode.coords)) + else: + nr_fanout = len(n.fanout) + for innode in n.fanin.values(): + if innode.is_pi and nr_fanout > 1: + raise SynthesisException('{} is designated PI WIRE and has multiple fanout') + + def has_designated_po(self): + ''' + Checks if only WIREs are connected to POs. Moreover, verifies that + those designated PO WIREs have no other fanout. Returns True + of this is the case and False otherwise. + ''' + for (n, d) in self.po_map: + if n.gate_type != 'WIRE': + return False + if len(n.fanout) > 1: + return False + return True + + def verify_designated_po(self): + ''' + The same as :func:`has_designated_po` but raises a + :class:`SynthesisException` if the spec is not met. + ''' + for (n, d) in self.po_map: + if n.gate_type != 'WIRE': + raise SynthesisException('{} is designated PO but has gate type {}'.format( + n.coords, n.gate_type)) + if len(n.fanout) > 1: + raise SynthesisException('{} is designated PO but has multiple fanout'.format( + n.coords)) + + def verify_consecutive_not(self): + ''' + Verifies that the network contains no consecutive NOT gates. Raises a + :class:`SynthesisException` if it does. + ''' + for n in self.nodes: + if n.is_pi: + continue + if n.gate_type == 'NOT': + for _, innode in n.fanin.items(): + if not innode.is_pi and innode.gate_type == 'NOT': + raise SynthesisException('{} is NOT gate and has NOT fanin {}'.format( + n.coords, innode.coords)) + + def verify_no_crossing_io(self): + ''' + Verifies that the network contains no crossings that are directly + connected to I/O pins. Raises a :class:`SynthesisException` if it does. + ''' + for n in self.nodes: + if n.is_pi: + continue + if n.gate_type == 'CROSS': + if n.is_po: + raise SynthesisException('{} is CROSS so cannot be PO'.format(n.coords)) + for _, innode in n.fanin.items(): + if innode.is_pi: + raise SynthesisException('{} is CROSS so cannot have PI fanin {}'.format( + n.coords, innode.coords)) + + def to_png(self, filename): + ''' + Creates a PNG of the logic network using Graphviz. + In the resulting PNG, all border nodes are filled in + with a gray color. All internal nodes are white. + PO nodes are marked by a double border. Every non-PI + node is also marked with its tile-space coordinates + as well as the function it computes. + ''' + dot = Digraph() + dot.attr(newrank='true') + dot.attr(rankdir='TB') + dot.attr('node', shape='circle') + dot.attr('node', fixedsize='true') + dot.attr('node', width='1.1') + dot.attr('node', height='1.1') + + # Find I/O coords. + pi_coords = {} + po_coords = {} + for n in self.nodes: + if n.is_pi: + continue + for in_dir, innode in n.fanin.items(): + if innode.is_pi: + coords = get_coords_in_direction(n.coords, in_dir) + pi_coords[coords] = innode + for h in range(self.nr_pos): + n, d = self.po_map[h] + coords = get_coords_in_direction(n.coords, d) + po_coords[coords] = h + + # Draw nodes in a grid. Make the grid a bit bigger so we can + # fit the PI nodes on there neatly. + y_range = (-1, self.shape[1] + 1) # y coordinate range (top value exclusive) + x_range = (-1, self.shape[0] + 1) # x " " " + boundary_counter = 0 + coord_names = {} + for y in range(y_range[0], y_range[1]): + with dot.subgraph() as s: + s.attr(rank='same') + for x in range(x_range[0], x_range[1]): + if (x,y) in pi_coords: + n = pi_coords[(x,y)] + name = 'PI{}'.format(n.coords) + label = 'x[{}]'.format(n.coords) + s.node(name, label, fillcolor='deepskyblue1', style='filled') + elif (x,y) in po_coords: + h = po_coords[(x,y)] + name = 'PO{}'.format(h) + label = 'f[{}]'.format(h) + s.node(name, label, fillcolor='coral1', style='filled') + elif (x,y) in self.node_map: + n = self.node_map[(x, y)] + name = 'N_{}_{}'.format(x, y) + label = '{}\n{}'.format(n.coords, n.gate_type) + fill = 'gray' if n.is_border_node else 'white' + s.node(name, label, fillcolor=fill, style='filled') + else: # Empty boundary node + name = 'B_{}'.format(boundary_counter) + boundary_counter += 1 + s.node(name, name, style='invis') + coord_names[(x,y)] = name + + for coord, name in coord_names.items(): + if coord[0] < self.shape[0]: + dot.edge(name, coord_names[(coord[0]+1, coord[1])], style='invis') + if coord[1] < self.shape[1]: + dot.edge(name, coord_names[(coord[0], coord[1]+1)], style='invis') + + for n in self.nodes: + if n.is_pi: + continue + name = 'N_{}_{}'.format(n.coords[0], n.coords[1]) + for in_dir, innode in n.fanin.items(): + if innode.is_pi: + inname = 'PI{}'.format(innode.coords) + else: + inname = 'N_{}_{}'.format(innode.coords[0], innode.coords[1]) + dot.edge(inname, name, + tailport=OPPOSITE_DIRECTION[in_dir][0:1].lower(), + headport=in_dir[0:1].lower()) + + for h in range(self.nr_pos): + n, d = self.po_map[h] + name = 'N_{}_{}'.format(n.coords[0], n.coords[1]) + oname = 'PO{}'.format(h) + olabel = 'PO{}'.format(h) + dot.edge(name, oname, tailport=d[0:1].lower(), headport=OPPOSITE_DIRECTION[d][0:1].lower()) + + dot.render(filename=filename, format='png', cleanup=True) + + def rec_simulate(self, n, sim_vals, marked_nodes): + ''' + Recursive helper method for :func:`simulate`. + ''' + if n.is_pi: + return + for innode in n.fanin.values(): + if not innode in marked_nodes: + self.rec_simulate(innode, sim_vals, marked_nodes) + marked_nodes.add(n) + for out_dir in n.fanout.keys(): + if n.gate_type == 'EMPTY': + # Empty gates are not referred to by anything. + continue + invals = [] + for in_dir, innode in n.fanin.items(): + if innode.is_pi: + invals.append(sim_vals[innode][None]) + else: + invals.append(sim_vals[innode][OPPOSITE_DIRECTION[in_dir]]) + + if n.gate_type == 'WIRE': + # Only one fanin, retrieve its sim_val and copy it. + sim_vals[n][out_dir] = invals[0] + elif n.gate_type == 'NOT': + # Only one fanin, retrieve its sim_val and negate it. + sim_vals[n][out_dir] = 1 - invals[0] + elif n.gate_type == 'AND': + sim_vals[n][out_dir] = invals[0] & invals[1] + elif n.gate_type == 'OR': + sim_vals[n][out_dir] = invals[0] | invals[1] + elif n.gate_type == 'MAJ': + sim_vals[n][out_dir] = eval_gate('MAJ', invals) + elif n.gate_type == 'CROSS': + # Copy input to direction described in dir_map + for indir, outdir in n.dir_map.items(): + if outdir == out_dir: + if n.fanin[indir].is_pi: + sim_vals[n][out_dir] = sim_vals[n.fanin[indir][None]] + else: + sim_vals[n][out_dir] = sim_vals[n.fanin[indir]][OPPOSITE_DIRECTION[indir]] + else: + raise SynthesisException("Unknown gate type '{}' in simulation".format(n.gate_type)) + + def simulate(self): + ''' + Simulates the logic network and returns a list which contains the + simulated function for each output. + ''' + sim_tt = [[0] * (2 ** self.nr_pis) for i in range(self.nr_pos)] + sim_idx = 0 + for input_pattern in itertools.product('01', repeat=self.nr_pis): + # Reverse input pattern, since our PI ordering is the + # inverse of itertools.product. + input_pattern = input_pattern[::-1] + sim_vals = {} + marked_nodes = set() + for n in self.nodes: + sim_vals[n] = {} + for i in range(self.nr_pis): + n = self.nodes[i] + sim_vals[n][None] = int(input_pattern[i]) + for i in range(self.nr_pos): + n, d = self.po_map[i] + self.rec_simulate(n, sim_vals, marked_nodes) + sim_tt[i][sim_idx] = sim_vals[n][d] + sim_idx += 1 + if self.nr_pis == 2: + for i in range(self.nr_pos): + sim_tt[i] = sim_tt[i] + sim_tt[i] + return sim_tt + +class scheme_graph: + ''' + A scheme_graph (short for clocking-scheme graph) is used + to specify a clocking scheme and to synthesize logic + networks according to that specification. + ''' + + def __init__(self, *, shape=(1,1), + enable_wire=True, enable_not=True, enable_and=True, + enable_or=True, enable_maj=True, enable_crossings=True, + designated_pi=False, designated_po=False, nr_threads=1, + timeout=0): + ''' + Creates a new clocking scheme graph. + + :param shape: A 2-tuple specifying the dimensions of the clocking scheme. + :param enable_not: Enable synthesis of WIREs. + :param enable_not: Enable synthesis of NOT gates. + :param enable_and: Enable synthesis of AND gates. + :param enable_or: Enable synthesis of OR gates. + :param enable_maj: Enable synthesis of MAJ gates. + :param enable_crossings: Enable wire crossings. + :param designated_pi: True iff only WIRES can have PI fanin. + :param designated_po: True iff only WIRES can have PO fanout. + :param nr_threads: How many threads to use in parallel solving. + :param timeout: the timeout for the synthesize call (in seconds) + + ''' + self.shape = shape + self.node_map = {} + for y in range(shape[1]): + for x in range(shape[0]): + n = node(coords=(x, y)) + if x == 0 or x == (shape[0] - 1) or y == 0 or y == (shape[1] - 1): + n.is_border_node = True + self.node_map[(x,y)] = n + + self.enable_wire = enable_wire + self.enable_not = enable_not + self.enable_and = enable_and + self.enable_or = enable_or + self.enable_maj = enable_maj + self.enable_crossings = enable_crossings + self.designated_pi = designated_pi + self.designated_po = designated_po + self.nr_threads = nr_threads + self.model = None + self.timeout = timeout + + def add_virtual_edge(self, coords1, coords2): + ''' + Adds a virtual edge from the node corresponding to the tile at + coords1 to the node corresponding to the tile at coords2. + A virtual edge specifies that the node at coords2 may have + the node at coords1 in its fanin. However, it does not force + this to happen. Hence, the connection is virtual and may be + actualized by the synthesis process. + ''' + node1 = self.node_map[coords1] + node2 = self.node_map[coords2] + node1.virtual_fanout.append(node2) + node2.virtual_fanin.append(node1) + + def _dfs_find_cycles(self, cycles, start, n, path): + if n in path: + if n == start: + cycles.append([n] + path) + return + for innode in n.virtual_fanin: + self._dfs_find_cycles(self, cycles, start, innode, [n] + path) + + def find_cycles(self): + ''' + Examines the clocking scheme graph and finds any cycles it may + contain. + ''' + cycles = [] + for n in self.node_map.values(): + for innode in n.virtual_fanin: + self._dfs_find_cycles(self, cycles, n, innode, [n]) + return cycles + + def to_png(self, filename): + ''' + Creates a PNG of the graph underlying the clock scheme + using Graphviz. + ''' + dot = Digraph() + dot.attr('node', shape='box') + dot.attr(splines='ortho') + + for y in range(self.shape[1]): + with dot.subgraph() as s: + s.attr(rank='same') + for x in range(self.shape[0]): + n = self.node_map[(x, y)] + name = 'N_{}_{}'.format(x, y) + label = str((x, y)) + s.node(name, label) + if x > 0: + prevname = 'N_{}_{}'.format(x-1, y) + s.edge(prevname, name, style='invis') + + + for y in range(self.shape[1]): + for x in range(self.shape[0]): + n1 = self.node_map[(x, y)] + n1name = 'N_{}_{}'.format(x, y) + for n2 in n1.virtual_fanout: + n2name = 'N_{}_{}'.format(n2.coords[0], n2.coords[1]) + dot.edge(n1name, n2name) + + dot.render(filename=filename, format='png', cleanup=True) + + def satisfies_spec(self, net, functions): + ''' + Verifies that a network satisfies the specifications represented by + this scheme_graph object. Raises a :class:`SynthesisException` if this + is not the case. + ''' + # Make sure PIs do not have more than one fanout. + for n in net.nodes: + if not n.is_pi: + continue + if len(n.fanout) > 1: + raise SynthesisException('PI_{} has more than one fanin'.format(n.coords)) + if not self.enable_wire: + for n in net.nodes: + if not n.is_pi and n.gate_type == 'WIRE': + raise SynthesisException('{} has type WIRE'.format(n.coords)) + if not self.enable_not: + for n in net.nodes: + if not n.is_pi and n.gate_type == 'NOT': + raise SynthesisException('{} has type NOT'.format(n.coords)) + if not self.enable_and: + for n in net.nodes: + if not n.is_pi and n.gate_type == 'AND': + raise SynthesisException('{} has type AND'.format(n.coords)) + if not self.enable_or: + for n in net.nodes: + if not n.is_pi and n.gate_type == 'OR': + raise SynthesisException('{} has type OR'.format(n.coords)) + if not self.enable_maj: + for n in net.nodes: + if not n.is_pi and n.gate_type == 'MAJ': + raise SynthesisException('{} has type MAJ'.format(n.coords)) + if not self.enable_crossings: + for n in net.nodes: + if not n.is_pi and n.gate_type == 'CROSS': + raise SynthesisException('{} has type CROSS'.format(n.coords)) + if not net.has_border_io(): + raise SynthesisException('Net does not have border I/O') + if self.designated_pi: + net.verify_designated_pi() + if self.designated_po: + net.verify_designated_po() + net.verify_consecutive_not() + net.verify_no_crossing_io() + sim_tts = net.simulate() + for i in range(len(functions)): + if functions[i] != sim_tts[i]: + raise SynthesisException('Specified f[{}] = {}, net out[{}] = {}'.format( + i, functions[i], i, sim_tts[i])) + + def _discover_connectivity(self, n, pi_fanin_options): + # Check which directions support fanouts. + fanout_directions = set() + for outnode in n.virtual_fanout: + outdir, indir = get_direction(n.coords, outnode.coords) + fanout_directions.add(outdir) + # Check which directions support fanins. + fanin_directions = set() + for innode in n.virtual_fanin: + outdir, indir = get_direction(innode.coords, n.coords) + fanin_directions.add(indir) + + # Border nodes may have PI/PO fanins/fanouts coming from any + # directions that are not used by virtual fanin or fanout. + io_directions = CARDINAL_DIRECTIONS.difference(fanout_directions).difference(fanin_directions) + if n.is_border_node and len(io_directions) == 0: + raise SynthesisException('Unexpected I/O state at border node') + elif not n.is_border_node and len(io_directions) > 0: + raise SynthesisException('Unexpected I/O state at internal node') + # Add I/O directions to potential fanin/fanout directions. + for direction in io_directions: + fanout_directions.add(direction) + fanin_directions.add(direction) + + fanin_options = {} + for d in io_directions: + fanin_options[d] = [(pi, None) for pi in pi_fanin_options] + for innode in n.virtual_fanin: + outdir, indir = get_direction(innode.coords, n.coords) + assert(indir not in fanin_options) + fanin_options[indir] = [(innode, outdir)] + n.fanout_directions = fanout_directions + n.fanin_directions = fanin_directions + n.fanin_options = fanin_options + n.io_directions = io_directions + + def synthesize(self, functions, verbosity=0): + ''' + Synthesizes the given list of functions. Returns an iterator of + :class:`logic_network` objects, so the caller may iterate on this + method to synthesize all networks that satisfy the specifications given + by the clocking scheme and the functions. + + :param functions: A list of lists of binary integers. Every list is a function to be synthesized. Every list is to be computed by the resulting logic network and corresponds to one of its outputs. The n-th list corresponds to the n-th logic network output. + :param verbosity: Parameter to view debugging output. + ''' + + @wrapt_timeout_decorator.timeout(self.timeout) + def timeout_call(self, functions, verbosity): + for net in self._synthesize(self, functions, verbosity): + return net + + if self.timeout <= 0: + for net in self._synthesize(self, functions, verbosity): + yield net + else: + net = timeout_call(self, functions, verbosity) + yield net + + def _synthesize(self, functions, verbosity): + ''' + Synthesizes a logic network according to the clocking scheme + specifications encoded in the graph and the functional + specification encoded by the truth tables in the functions + list. + + NOTE: this function may be called multiple times, which + will result in it generating zero or more logic networks. + ''' + assert(len(functions) > 0) + assert(log2(len(functions[0])).is_integer()) + + self.nr_pis = round(log2(len(functions[0]))) + self.nr_pos = len(functions) + var_idx = 1 + + self.nodes = [node(coords=i,is_pi=True) for i in range(self.nr_pis)] + for y in range(self.shape[1]): + for x in range(self.shape[0]): + self.nodes.append(self.node_map[(x,y)]) + + legend = {} + + # Pre-process specified functions, make sure truth tables are + # at least size 8. + if len(functions[0]) == 4: + for i in range(len(functions)): + functions[i] = functions[i] + functions[i] + + # Determine the possible local connectivity options for each node + pi_fanin_options = [self.nodes[x] for x in range(self.nr_pis)] + for n in self.nodes: + if n.is_pi: + continue + self._discover_connectivity(self, n, pi_fanin_options) + + # Determine what gate types are supported by each tile node. + for n in self.nodes: + if n.is_pi: + continue + enabled_gates = ['EMPTY'] + if self.enable_wire: + enabled_gates.append('WIRE') + if self.enable_not: + enabled_gates.append('NOT') + if self.enable_and: + enabled_gates.append('AND') + if self.enable_or: + enabled_gates.append('OR') + if self.enable_maj and len(n.fanin_options) > 2: + # assert(self.nr_pis > 2) + enabled_gates.append('MAJ') + if self.enable_crossings: + if not n.is_border_node and (len(n.virtual_fanin) == 2): + enabled_gates.append('CROSS') + n.enabled_gate_types = enabled_gates + + # Based on the enabled gates we can determine the simulation + # variables and the gate type variables. + nr_local_sim_vars = len(functions[0]) + for n in self.nodes: + sim_vars = {} + if n.is_pi: + varlist = [0] * nr_local_sim_vars + for i in range(nr_local_sim_vars): + varlist[i] = var_idx + legend[var_idx] = 'sim_vars[PI{}][None][{}]'.format(n.coords, i) + var_idx += 1 + sim_vars[None] = varlist + else: + for d in n.fanout_directions: + varlist = [0] * nr_local_sim_vars + for i in range(nr_local_sim_vars): + varlist[i] = var_idx + legend[var_idx] = 'sim_var[{}][d][{}]'.format(n.coords, i) + var_idx += 1 + sim_vars[d] = varlist + n.sim_vars = sim_vars + + for n in self.nodes: + if n.is_pi: + continue + gate_type_vars = [] + gate_type_map = {} + for t in n.enabled_gate_types: + gate_type_vars.append(var_idx) + gate_type_map[t] = var_idx + legend[var_idx] = 'gate {} has type {}'.format(n.coords, t) + var_idx += 1 + n.gate_type_vars = gate_type_vars + n.gate_type_map = gate_type_map + + # Based on the local connectivity and gate types we can + # determine the selection variables. + for n in self.nodes: + # Keeps track of all variables that select this node as + # fanin. + n.ref_vars = [] + # Maps ref vars to the node that refers to n. + n.ref_var_map = {} + # Maps fanout directions to the selection variables that + # refer to this node. + n.ref_var_direction_map = {} + if n.is_pi: + n.ref_var_direction_map[None] = [] + else: + for direction in n.fanout_directions: + n.ref_var_direction_map[direction] = [] + for n in self.nodes: + if n.is_pi: + continue + svar_map = {} + # dir_map tracks to which output direction an fanin gets mapped. + dir_map = {} + # Track all selection variables for a given fanin + # direction. + svar_direction_map = {} + for direction in n.fanin_directions: + svar_direction_map[direction] = [] + svars = [] + fanin_size_options = set([GATE_FANIN_RANGE[gate] for gate in n.enabled_gate_types]) + for size_option in fanin_size_options: + if size_option == 0: + # Handle 0 as a special case where this node + # corresponds to an empty tile. + continue + # Select all possible combinations of size_option fanins. + svar_map[size_option] = {} + dir_list = list(n.fanin_options.keys()) + for directions in itertools.combinations(dir_list, size_option): + dir_opt_list = [] + for d in directions: + dir_opt_list.append([(d, o) for o in n.fanin_options[d]]) + fanin_combinations = itertools.product(*dir_opt_list) +# Warning: enabling the print statement below iterates over fanin_combinations, rendering the for loop useless! +# print('{} fanin combinations: {}'.format(n.coords, list(fanin_combinations))) + for comb in fanin_combinations: + # Filter out redundant combinations. + if size_option == 2 and comb[0][1][0] == comb[1][1][0]: + continue + elif size_option == 3 and ( + (comb[0][1][0] == comb[1][1][0]) or + (comb[0][1][0] == comb[2][1][0]) or + (comb[1][1][0] == comb[2][1][0])): + continue + # If designated PIs are enabled, we don't want + # gates with more than 1 fanin referring to + # PIs. + if self.designated_pi and size_option == 2 and ( + comb[0][1][0].is_pi or comb[1][1][0].is_pi): + continue + elif self.designated_pi and size_option == 3 and ( + comb[0][1][0].is_pi or comb[1][1][0].is_pi or comb[2][1][0].is_pi): + continue + + if not self.enable_crossings or size_option != 2: + svar_map[size_option][var_idx] = comb + legend[var_idx] = '{} has fanin {}'.format(n.coords, comb) + svars.append(var_idx) + # print(comb) + for direction, option in comb: + # option[0] is the node, option[1] is the + # output port direction. + option[0].ref_vars.append(var_idx) + option[0].ref_var_direction_map[option[1]].append(var_idx) + option[0].ref_var_map[var_idx] = n + svar_direction_map[direction].append(var_idx) + var_idx += 1 + else: + # If we have crossings, we must create a separate + # selection variable for every possible mapping + # from the input directions to the output + # directions. + input_directions = set([comb[0][0], comb[1][0]]) + output_directions = list(CARDINAL_DIRECTIONS.difference(input_directions)) + for i in range(2): + svar_map[size_option][var_idx] = comb + dir_map[var_idx] = { comb[0][0] : output_directions[i], comb[1][0] : output_directions[1 - i] } + legend[var_idx] = '{} has fanin {}'.format(n.coords, comb) + svars.append(var_idx) + for direction, option in comb: + # option[0] is the node, option[1] is the + # output port direction. + option[0].ref_vars.append(var_idx) + option[0].ref_var_direction_map[option[1]].append(var_idx) + option[0].ref_var_map[var_idx] = n + svar_direction_map[direction].append(var_idx) + var_idx += 1 + + n.svar_map = svar_map + n.dir_map = dir_map + n.svar_direction_map = svar_direction_map + n.svars = svars + + # Create the output variables. + nr_outputs = len(functions) + out_vars = {} + for h in range(nr_outputs): + houtvars = {} + for n in self.nodes: + if n.is_pi or not n.is_border_node: + continue + for direction in n.io_directions: + houtvars[var_idx] = (n, direction) + n.ref_var_direction_map[direction].append(var_idx) + n.ref_vars.append(var_idx) + legend[var_idx] = 'PO_{} points to ({}, {})'.format(h, n.coords, direction) + var_idx += 1 + out_vars[h] = houtvars + + + ''' + print('{}.enabled_gate_types: {}'.format((0,0), self.node_map[(0,0)].enabled_gate_types)) + print('{}.svar_map: {}'.format((0,0), self.node_map[(0,0)].svar_map)) + print('{}.svar_direction_map: {}'.format((0,0), self.node_map[(0,0)].svar_direction_map)) + print('{}.svars = {}'.format((0,0), self.node_map[(0,0)].svars)) + print('{}.ref_var_map = {}'.format((0,0), self.node_map[(0,0)].ref_var_map)) + print('{}.ref_var_direction_map = {}'.format((0,0), self.node_map[(0,0)].ref_var_direction_map)) + print('{}.ref_vars = {}'.format((0,0), self.node_map[(0,0)].ref_vars)) + ''' + + # Create graph connection and path variables + cycles = self.find_cycles(self) + connection_vars = {} + for n in self.nodes: + if n.is_pi: + continue + connection_vars[n] = {} + for n in self.nodes: + if n.is_pi: + continue + for np in n.virtual_fanin: + connection_vars[np][n] = var_idx + legend[var_idx] = '{} and {} are connected'.format(np.coords, n.coords) + var_idx += 1 + + # Create the simulation propagation constraints. + clauses = [] + for n in self.nodes: + if n.is_pi: + continue + for gate_type in n.enabled_gate_types: + if gate_type == 'EMPTY': + # We handle the empty gate as a special case. + continue + elif gate_type == 'CROSS': + # CROSS is also handled as a special case. + gate_var = n.gate_type_map['CROSS'] + fanin_options = n.svar_map[2] + for svar, fanins in fanin_options.items(): + inport1, (innode1, outport1) = fanins[0] + inport2, (innode2, outport2) = fanins[1] + assert(inport1 == OPPOSITE_DIRECTION[outport1]) + assert(inport2 == OPPOSITE_DIRECTION[outport2]) + if innode1.is_pi or innode2.is_pi: + # Crossings cannot have PI fanin. + clauses.append([-gate_var, -svar]) + continue + out_directions = n.dir_map[svar] + assert(out_directions[inport1] in n.sim_vars.keys()) + assert(out_directions[inport2] in n.sim_vars.keys()) + for tt_idx in range(nr_local_sim_vars): + permutations = list(itertools.product('01', repeat=2)) + for permutation in permutations: + clause1 = [0] * 5 + clause2 = [0] * 5 + const_vals = [int(permutation[0]), int(permutation[1])] + clause1[0] = -svar + clause1[1] = -gate_var + clause2[0] = -svar + clause2[1] = -gate_var + for i in range(2): + if const_vals[i] == 1: + clause1[i+2] = -innode1.sim_vars[outport1][tt_idx] + clause2[i+2] = -innode2.sim_vars[outport2][tt_idx] + else: + clause1[i+2] = innode1.sim_vars[outport1][tt_idx] + clause2[i+2] = innode2.sim_vars[outport2][tt_idx] + if const_vals[0] == 1: + clause1[4] = n.sim_vars[out_directions[inport1]][tt_idx] + else: + clause1[4] = -n.sim_vars[out_directions[inport1]][tt_idx] + if const_vals[1] == 1: + clause2[4] = n.sim_vars[out_directions[inport2]][tt_idx] + else: + clause2[4] = -n.sim_vars[out_directions[inport2]][tt_idx] + + clauses.append(clause1) + clauses.append(clause2) + else: + fanin_size = GATE_FANIN_RANGE[gate_type] + gate_var = n.gate_type_map[gate_type] + fanin_options = n.svar_map[fanin_size] + for svar, fanins in fanin_options.items(): + for fanout_direction in n.fanout_directions: + for tt_idx in range(nr_local_sim_vars): + permutations = list(itertools.product('01', repeat=(fanin_size))) + for permutation in permutations: + const_vals = [] + for i in range(fanin_size): + const_val = int(permutation[i]) + const_vals.append(const_val) + function_output = eval_gate(gate_type, const_vals) + clause = [0] * (fanin_size + 3) + clause[0] = -svar + clause[1] = -gate_var + for i in range(len(const_vals)): + _, (innode, output_port) = fanins[i] + if const_vals[i] == 1: + clause[i+2] = -innode.sim_vars[output_port][tt_idx] + else: + clause[i+2] = innode.sim_vars[output_port][tt_idx] + if function_output == 1: + clause[fanin_size+2] = n.sim_vars[fanout_direction][tt_idx] + else: + clause[fanin_size+2] = -n.sim_vars[fanout_direction][tt_idx] + clauses.append(clause) + + + # Make sure that every I/O port is used at most once, and that + # PIs are used at most once. + for n in self.nodes: + if n.is_pi: + cnf = CardEnc.atmost(lits=n.ref_vars, encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append(clause) + else: + cnf = CardEnc.atmost(lits=n.svars, encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append(clause) + for direction, svars in n.ref_var_direction_map.items(): + cnf = CardEnc.atmost(lits=svars, encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append(clause) + + # Make sure that every node selects at least some fanin + # option, unless its the EMPTY fanin. We do this based on + # fanin range, so that e.g. if a node corrsponds to an AND + # gate it cannot select only a single fanin. + for n in self.nodes: + if n.is_pi: + continue + empty_var = n.gate_type_map['EMPTY'] + for gate_type in n.enabled_gate_types: + if gate_type == 'EMPTY': + continue + gate_type_var = n.gate_type_map[gate_type] + fanin_range = GATE_FANIN_RANGE[gate_type] + svars = list(n.svar_map[fanin_range].keys()) + clauses.append([empty_var, -gate_type_var] + svars) + + # Create cycle-prevention constraints. + for n in self.nodes: + if n.is_pi: + continue + # If svar -> n selects innode as fanin, then there is a + # connection between innode and n. Conversely, if there is + # a connection between innode and n, one of the selection + # variables that picks innode as a fanin of n must be + # true. This converse case is not stricly necessary to + # prevent cycles, but we add it anyway to break + # symmetries. + for svar, outnode in n.ref_var_map.items(): + clause = [0] * 2 + clause[0] = -svar + clause[1] = connection_vars[n][outnode] + clauses.append(clause) + for innode in n.virtual_fanin: + potential_svars = [] + for svar, outnode in innode.ref_var_map.items(): + if outnode == n: + potential_svars.append(svar) + clause = [-connection_vars[innode][n]] + potential_svars + clauses.append(clause) + + # For every cycle in the graph, one of the variables + # representing a step on the cycle must be false. + for cycle in cycles: + cycle_steps = zip(cycle, cycle[1:]) + cycle_lits = [-connection_vars[s[0]][s[1]] for s in cycle_steps] + clauses.append(cycle_lits) + + # Fix input vars + for var in range(self.nr_pis): + n = self.nodes[var] + for idx in range(nr_local_sim_vars): + if idx & (1 << var): + clauses.append([n.sim_vars[None][idx]]) + else: + clauses.append([-n.sim_vars[None][idx]]) + + # Fix output vars + for h in range(nr_outputs): + houtvars = out_vars[h] + # Ensure that output h points to exactly one gate + cnf = CardEnc.equals(lits=list(houtvars.keys()), encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append(clause) + # If output h points node n at the output port in + # direction d, then the truth table of n must + # agree with that of function h at that output + # port. + for houtvar, (n, d) in houtvars.items(): + for idx in range(nr_local_sim_vars): + if functions[h][idx] == 1: + clauses.append([-houtvar, n.sim_vars[d][idx]]) + else: + clauses.append([-houtvar, -n.sim_vars[d][idx]]) + + # Add gate constraints: every node must pick exactly one gate + # type. + for n in self.nodes: + if n.is_pi: + continue + cnf = CardEnc.equals(lits=n.gate_type_vars, encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append(clause) + + # Make sure that every non-PI non-empty gate is used at least + # once. If not, some gates perform useless computations. + for n in self.nodes: + if n.is_pi: + continue + empty_var = n.gate_type_map['EMPTY'] + clauses.append([empty_var] + n.ref_vars) + + # Add cardinality constraints on gate fanouts. These + # constraints depend on the gate type. AND/OR/NOT/MAJ gates + # are restricted to single fanout, while wires may have fanout + # up to three, and crossings have exactly two fanouts. Note + # that we count PO references here as well, since we don't + # want e.g. an AND gate being both a PO and referenced by an + # internal node. + for n in self.nodes: + if n.is_pi: + continue + for gate_type in n.enabled_gate_types: + gate_var = n.gate_type_map[gate_type] + if gate_type == 'WIRE': + cnf = CardEnc.atmost(lits=n.ref_vars, encoding=EncType.pairwise, bound=3) + for clause in cnf.clauses: + clauses.append([-gate_var] + clause) + elif gate_type == 'EMPTY': + # We'll handle EMPTY gates elsewhere. + continue + elif gate_type == 'CROSS': + cnf = CardEnc.equals(lits=n.ref_vars, encoding=EncType.pairwise, bound=2) + for clause in cnf.clauses: + clauses.append([-gate_var] + clause) + else: + cnf = CardEnc.equals(lits=n.ref_vars, encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append([-gate_var] + clause) + + + # If a tile has the EMPTY gate make sure it does not select + # any fanin and that no gate selects it as fanin. Moreover, + # set its simulation variables to zero in every output + # direction. + for n in self.nodes: + if n.is_pi: + continue + empty_var = n.gate_type_map['EMPTY'] + for svar in n.svars: + clauses.append([-empty_var, -svar]) + for ref_var in n.ref_vars: + clauses.append([-empty_var, -ref_var]) + for direction in n.fanout_directions: + for tt_idx in range(nr_local_sim_vars): + clauses.append([-empty_var, -n.sim_vars[direction][tt_idx]]) + + # We cannot have a PI and a PO on the same I/O port. + for n in self.nodes: + if n.is_pi or not n.is_border_node: + continue + for direction in n.io_directions: + pi_vars = n.svar_direction_map[direction] + po_vars = [] + for h in range(nr_outputs): + houtvars = out_vars[h] + for houtvar, (po_n, d) in houtvars.items(): + if po_n == n and d == direction: + po_vars.append(houtvar) + for pi_var in pi_vars: + for po_var in po_vars: + clauses.append([-pi_var, -po_var]) + + # If designated_io is enabled only WIRE elements can have + # PI/PO fanin/fanout. + if self.designated_pi: + assert(self.enable_wire) + for n in self.nodes: + if not n.is_pi: + continue + for svar, out_node in n.ref_var_map.items(): + # If this svar refers to + wire_var = out_node.gate_type_map['WIRE'] + clauses.append([-svar, wire_var]) + # A designated PI can only have a single fanout. + cnf = CardEnc.atmost(lits=out_node.ref_vars, encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append([-svar] + clause) + + if self.designated_po: + assert(self.enable_wire) + for n in self.nodes: + if n.is_pi or not n.is_border_node: + continue + # If one of the POs points to this gate, it has to be + # a WIRE. Moreover, it cannot have any other fanout. + wire_type_var = n.gate_type_map['WIRE'] + for h in range(nr_outputs): + houtvars = out_vars[h] + for houtvar, (out_node, _) in houtvars.items(): + if out_node == n: + clauses.append([-houtvar, wire_type_var]) + cnf = CardEnc.atmost(lits=out_node.ref_vars, encoding=EncType.pairwise) + for clause in cnf.clauses: + clauses.append([-houtvar] + clause) + + # Symmetry break: disallow consecutive NOT gates. + if self.enable_not: + for n in self.nodes: + if n.is_pi: + continue + not_type_var = n.gate_type_map['NOT'] + for svar, fanins in n.svar_map[1].items(): + innode = fanins[0][1][0] + if innode.is_pi: + continue + innode_not_var = innode.gate_type_map['NOT'] + clauses.append([-not_type_var, -svar, -innode_not_var]) + + if self.nr_threads <= 1: + # Create solver instance, add clauses, and start solving. + solver = Glucose3() + for clause in clauses: + solver.add_clause(clause) + + # Decode network from solutions + model_idx = 1 + prev_model = None + for model in solver.enum_models(): + if verbosity > 1: + logfile = open('model-{}.log'.format(model_idx), 'w') + if prev_model != None: + for i in range(len(model)): + if model[i] != prev_model[i]: + logfile.write('model[{}] = {}, prev_model[{}] = {}\n'.format( + i, 1 if model[i] > 0 else 0, i, 1 if prev_model[i] > 0 else 0 + )) + for v in model: + logfile.write('{}\n'.format(v)) + for v, s in legend.items(): + logfile.write('{}: {} ({})\n'.format(v, s, True if model[v-1] > 0 else False)) + logfile.close() + self.model = model + prev_model = model + net = self.model_to_network(self, model, nr_outputs, out_vars, nr_local_sim_vars, verbosity) + yield net + else: + # Use Glucose::MultiSolvers to find a solution. + # Start by creating the temporary CNF file for it to act on. + models = [] + while True: + proc = None + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + f.write('p cnf {} {}\n'.format(var_idx - 1, len(clauses) + len(models))) + for clause in clauses: + for v in clause: + f.write('{} '.format(v)) + f.write('0\n') + for model in models: + for v in model: + f.write('{} '.format(-v)) + f.write('0\n') + f.close() + proc = subprocess.run(['./glucose-syrup', '-nthreads={}'.format(self.nr_threads), '-model', f.name], capture_output=True, text=True) + os.remove(f.name) + if proc.returncode == 10: + # Glucose returns 10 on SAT + output = proc.stdout.split('\n') + model = [] + for line in output: + if line[:1] == 'v': + modelvals = line.split(' ')[1:] + for modelval in modelvals: + if modelval != '0': + model.append(int(modelval)) + models.append(model) + net = self.model_to_network(self, model, nr_outputs, out_vars, nr_local_sim_vars, verbosity) + yield net + elif proc.returncode == 20: + # Glucose returns 20 on UNSAT + break + elif proc.returncode == 0: + # Glucose returns 0 on timeout/interrupt + break + else: + # Error in calling SAT solver. + raise SynthesisException('Error calling Glucose::MultiSolvers') + + def model_to_network(self, model, nr_outputs, out_vars, nr_local_sim_vars, verbosity): + ''' + Decodes a SAT model (i.e. a list of integer values) and creates a + :class:`logic_network` from it. + ''' + net = logic_network(self.shape, self.nr_pis, self.nr_pos) + for h in range(nr_outputs): + houtvars = out_vars[h] + out_found = 0 + for houtvar, (n, d) in houtvars.items(): + if model[houtvar - 1] > 0: + #if verbosity > 1: +# print('out[{}] -> ({}, {}) (houtvar={})'.format(h, n.coords, d, houtvar)) + out_found += 1 + net.set_output(h, n.coords, d) + # Every output must point to exactly one output port. + assert(out_found == 1) + + for n in self.nodes: + if n.is_pi: + continue + if verbosity > 1: + for gate_type in n.enabled_gate_types: + print('{} gate type {}: {} ({})'.format( + n.coords, gate_type, 1 if model[n.gate_type_map[gate_type]-1] > 0 else 0, + model[n.gate_type_map[gate_type]-1])) + for direction in n.fanout_directions: + print('{} tt[{}]: '.format(n.coords, direction), end='') + for tt_idx in range(nr_local_sim_vars): + print('{}'.format(1 if model[n.sim_vars[direction][tt_idx]-1] > 0 else 0), end='') + print(' ', end='') + for tt_idx in range(nr_local_sim_vars): + print('({})'.format(model[n.sim_vars[direction][tt_idx]-1]), end='') + print('') + + netnode = net.node_map[n.coords] + # Find out the gate type. + gate_types_found = 0 + for gate_type, gate_var in n.gate_type_map.items(): + if model[gate_var - 1] > 0: + gate_types_found += 1 + netnode.gate_type = gate_type + assert(gate_types_found == 1) +# print('{} is {}-gate'.format(netnode.coords, netnode.gate_type)) + netnode.is_border_node = n.is_border_node + nr_selected_svars = 0 + nr_fanin = 0 + for size_option in n.svar_map.keys(): + for svar, comb in n.svar_map[size_option].items(): + if model[svar - 1] > 0: + nr_selected_svars += 1 +# print('{} has fanin {}'.format(n.coords, comb)) + for i in range(size_option): + in_dir = comb[i][0] + innode = comb[i][1][0] + out_dir = comb[i][1][1] + if innode.is_pi: + netnode.set_fanin(in_dir, net.nodes[innode.coords], out_dir) + else: + netnode.set_fanin(in_dir, net.node_map[innode.coords], out_dir) + if netnode.gate_type == 'CROSS': + netnode.dir_map = n.dir_map[svar] + assert(nr_selected_svars <= 1) # may be zero if EMPTY + return net + + + def print_model(self): + ''' + Prints the model of the latest successful SAT call (if any). + ''' + if self.model == None: + raise Exception('No model available') + for lit in self.model: + print(lit) diff --git a/src/algo/exact.cpp b/src/algo/exact.cpp index c5a25e2d4..c9484e00c 100644 --- a/src/algo/exact.cpp +++ b/src/algo/exact.cpp @@ -127,7 +127,7 @@ z3::expr exact::smt_handler::get_lit_s() noexcept exact::smt_handler::solver_check_point exact::smt_handler::fetch_solver(const fcn_dimension_xy& dim) noexcept { - const auto create_assumptions = [this](const solver_state& state)->z3::expr_vector + const auto create_assumptions = [this](const solver_state& state) -> z3::expr_vector { z3::expr_vector assumptions{*ctx}; assumptions.push_back(state.lit.s); @@ -684,7 +684,6 @@ void exact::smt_handler::global_synchronization() noexcept void exact::smt_handler::prevent_insufficiencies() noexcept { -// for (auto&& t : iter::chain(check_point->added_tiles, check_point->updated_tiles)) for (auto&& t : layout->tiles()) { if (!layout->is_eastern_border(t) && !layout->is_southern_border(t) && !is_updated_tile(t)) @@ -1681,7 +1680,7 @@ exact::pd_result exact::run_asynchronously() noexcept thread_bar(config.num_threads); auto post_toggle = false; - mockturtle::progress_bar post_bar("[i] some layout has been found; waiting for threads examining smaller dimensions to complete"); + mockturtle::progress_bar post_bar("[i] some layout has been found; waiting for threads examining smaller dimensions to terminate"); #endif for (auto&& i : iter::range(config.num_threads)) diff --git a/src/algo/exact.h b/src/algo/exact.h index b1214d0e3..3313a9558 100644 --- a/src/algo/exact.h +++ b/src/algo/exact.h @@ -107,11 +107,11 @@ class exact : public physical_design * @param fgl The gate layout pointer that is going to contain the created layout. * @param c The configurations to respect in the SMT instance generation process. */ - explicit smt_handler(ctx_ptr ctx, fcn_gate_layout_ptr fgl, const exact_pd_config& c) noexcept; + smt_handler(ctx_ptr ctx, fcn_gate_layout_ptr fgl, const exact_pd_config& c) noexcept; /** * Evaluates a given dimension regarding the stored configurations whether it can be skipped, i.e. does not - * need to be explored by the SMT solver. The better this function is, the less UNSAT instances can be skipped - * without losing the optimality guaranty. This function should never be overly restrictive! + * need to be explored by the SMT solver. The better this function is, the more UNSAT instances can be skipped + * without losing the optimality guarantee. This function should never be overly restrictive! * * @param dim Dimension to evaluate. * @return True if dim can safely be skipped because it is UNSAT anyways. diff --git a/src/algo/one_pass_synthesis.cpp b/src/algo/one_pass_synthesis.cpp new file mode 100644 index 000000000..fc22ed41a --- /dev/null +++ b/src/algo/one_pass_synthesis.cpp @@ -0,0 +1,446 @@ +// +// Created by marcel on 09.04.20. +// + +#include "one_pass_synthesis.h" + + +const pybind11::scoped_interpreter one_pass_synthesis::instance{}; + +one_pass_synthesis::one_pass_synthesis(std::vector&& tts, onepass_pd_config&& config) + : + physical_design(), + spec(std::move(tts)), + config{std::move(config)}, + dit{std::max(this->config.fixed_size, 1ul)} +{ + assert(!spec.empty()); + assert(std::adjacent_find(spec.begin(), spec.end(), + [](const auto& a, const auto& b){return a.num_vars() != b.num_vars();}) == spec.end()); + + // add Mugen's path to Python's sys.path module scope + pybind11::module::import("sys").attr("path").attr("append")(MUGEN_PATH); +} + +physical_design::pd_result one_pass_synthesis::operator()() +{ + if (!test_dependencies()) + return pd_result{false, nlohmann::json{{"runtime (s)", 0.0}}}; + + mockturtle::stopwatch<>::duration time{0}; + + auto layout_sketch = std::make_shared(*config.scheme, + std::make_shared(std::move(config.name))); + + mugen_handler handler{spec, layout_sketch, config}; + + for (; dit <= config.upper_bound; ++dit) // <= to prevent overflow + { + +#if (PROGRESS_BARS) + mockturtle::progress_bar bar("[i] examining layout dimensions: {:>2} × {:<2}"); +#endif + + auto dimension = *dit; + + if (handler.skippable(dimension)) + continue; + +#if (PROGRESS_BARS) + bar(dimension[X], dimension[Y]); +#endif + + handler.update(dimension); + + try + { + auto sat = mockturtle::call_with_stopwatch(time, [&handler] + { + return handler.is_satisfiable(); + }); + + if (sat) + { + layout = layout_sketch; + return pd_result{true, nlohmann::json{{"runtime (s)", mockturtle::to_seconds(time)}}}; + } + + if (config.timeout) + update_timeout(handler, time); + } + // timeout reached + catch (...) + { + return pd_result{false, nlohmann::json{{"runtime (s)", mockturtle::to_seconds(time)}}}; + } + } + + return pd_result{false, nlohmann::json{{"runtime (s)", mockturtle::to_seconds(time)}}}; +} + +bool one_pass_synthesis::test_dependencies() const +{ + namespace py = pybind11; + using namespace py::literals; + + // test for graphviz + try + { + py::exec("from graphviz import *"); + } + catch (...) + { + std::cout << "[e] Python module 'graphviz' could not be detected" << std::endl; + return false; + } + + // test for PySAT + try + { + py::exec("from pysat.solvers import Glucose3"); + } + catch (...) + { + std::cout << "[e] Python module 'PySAT' could not be detected" << std::endl; + return false; + } + + // test for wrapt_timeout_decorator + try + { + py::exec("from wrapt_timeout_decorator import *"); + } + catch (...) + { + std::cout << "[e] Python module 'wrapt_timeout_decorator' could not be detected" << std::endl; + return false; + } + + return true; +} + +one_pass_synthesis::mugen_handler::mugen_handler(const std::vector& s, fcn_gate_layout_ptr fgl, onepass_pd_config c) noexcept + : + spec{s}, + num_pis{spec[0].num_vars()}, + layout{std::move(fgl)}, + config{std::move(c)}, + mugen{pybind11::module::import("mugen")} +{} + +bool one_pass_synthesis::mugen_handler::skippable(const fcn_dimension_xy& dim) const noexcept +{ + // OPEN clocking optimization: rotated dimensions don't need to be explored + if (!layout->is_regularly_clocked()) + { + if (dim[X] != dim[Y] && dim[X] == layout->y() && dim[Y] == layout->x()) + return true; + } + + return false; +} + +void one_pass_synthesis::mugen_handler::update(const fcn_dimension_xy& dim) noexcept +{ + layout->resize(dim); +} + +void one_pass_synthesis::mugen_handler::update(const uint32_t timeout) noexcept +{ + config.timeout = timeout; +} + +bool one_pass_synthesis::mugen_handler::is_satisfiable() +{ + namespace py = pybind11; + using namespace py::literals; + + auto scheme_graph = generate_scheme_graph(); + + // Mugen modifies its parameters, so you better have a copy + auto py_spec = as_py_lists(spec); + + auto nets = scheme_graph.attr("synthesize")(scheme_graph, py_spec); + for (auto net_it = nets.begin(); net_it != nets.end(); ++net_it) + { + if (net_it->is_none()) + return false; + + to_gate_layout(*net_it); + + return true; + } + + return false; +} + +pybind11::list one_pass_synthesis::mugen_handler::as_py_lists(const std::vector& tts) const +{ + namespace py = pybind11; + + py::list spec_py_list{}; + + for (const auto& tt : tts) + { + auto tt_py_list = py::list(); + + for (auto&& b : iter::reversed(kitty::to_binary(tt))) + { + tt_py_list.append(py::int_(b == '0' ? 0 : 1)); + } + + spec_py_list.append(tt_py_list); + } + + return spec_py_list; +} + +pybind11::object one_pass_synthesis::mugen_handler::generate_scheme_graph() +{ + namespace py = pybind11; + using namespace py::literals; + + auto scheme_graph = mugen.attr("scheme_graph"); + scheme_graph.attr("__init__")(scheme_graph, "shape"_a = py::make_tuple(layout->x(), layout->y()), + "enable_wire"_a = config.enable_wires, "enable_not"_a = config.enable_not, + "enable_and"_a = config.enable_and, "enable_or"_a = config.enable_or, "enable_maj"_a = config.enable_maj, + "enable_crossings"_a = config.crossings, "designated_pi"_a = config.io_ports, + "designated_po"_a = config.io_ports, "nr_threads"_a = config.num_threads, "timeout"_a = config.timeout); + + for (auto&& t : layout->ground_layer()) + { + for (auto&& at : layout->outgoing_clocked_tiles(t)) + { + scheme_graph.attr("add_virtual_edge")(scheme_graph, py::make_tuple(t[X], t[Y]), py::make_tuple(at[X], at[Y])); + } + } + + return scheme_graph; +} + +void one_pass_synthesis::mugen_handler::to_gate_layout(pybind11::handle net) const +{ + namespace py = pybind11; + using namespace py::literals; + + using vertex_map = std::map; + vertex_map v_map{}; + + using crossing_map = std::map; + crossing_map c_map{}; + + auto network = layout->get_network(); + + // extracts the gate type from a node + const auto get_gate_type = [](const auto& node) -> operation + { + static const std::map op_map + { + {std::string("None"), operation::NONE}, + {std::string("AND"), operation::AND}, + {std::string("OR"), operation::OR}, + {std::string("NOT"), operation::NOT}, + {std::string("MAJ"), operation::MAJ}, + {std::string("WIRE"), operation::W}, + {std::string("CROSS"), operation::W} + }; + + return op_map.at(py::str(node.attr("gate_type"))); + }; + + // returns whether a node is a crossing node + const auto is_crossing = [](const auto& node) -> bool + { + return py::str(node.attr("gate_type")).equal(py::str("CROSS")); + }; + + // returns whether fanin_node -> node -> fanout_node is a valid crossing path, where fanout_node is available in c_map + const auto is_crossing_path = [this, &c_map](const auto& node, const auto& fanin_node) -> bool + { + static const std::map str_to_dir + { + {std::string("NORTH"), layout::DIR_N}, + {std::string("EAST"), layout::DIR_E}, + {std::string("SOUTH"), layout::DIR_S}, + {std::string("WEST"), layout::DIR_W} + }; + + auto dir_map = node.attr("dir_map"); + auto fanout_node = c_map.at(node); + auto get_direction = mugen.attr("get_direction"); + + auto inp_dir = get_direction(node.attr("coords"), fanin_node.attr("coords"))[py::int_(0)]; + auto out_dir = get_direction(node.attr("coords"), fanout_node.attr("coords"))[py::int_(0)]; + + return str_to_dir.at(py::str(dir_map[inp_dir])) == str_to_dir.at(py::str(out_dir)); + }; + + // extract a Python tuple representing coordinates from a node and converts it to a tile + const auto get_tile = [](const auto& node, const bool second_layer = false) -> fcn_gate_layout::tile + { + const auto tuple = py::tuple(node.attr("coords")); + return {py::int_(tuple[0]), py::int_(tuple[1]), second_layer ? CROSSING : GROUND}; + }; + + // checks if the given node is PI + const auto is_pi = [](const auto& node) -> bool + { + return py::bool_(node.attr("is_pi")); + }; + + // checks if the given node is PO + const auto is_po = [](const auto& node)->bool + { + return py::bool_(node.attr("is_po")); + }; + + // returns the ID of a PI node + const auto get_pi_id = [](const auto& node) + { + return py::int_(node.attr("coords")); + }; + + // pre-compute PIs and store them + std::vector pis(num_pis); + for (auto&& i : iter::range(num_pis)) + { + pis[i] = network->create_pi(fmt::format("pi{}", i)); + } + + // checks whether a node has a PI as fan-in + const auto has_pi_fanin = [&pis, &is_pi, &get_pi_id](const auto& node) -> std::optional + { + auto fanin = node.attr("fanin"); + for (auto fanin_it = fanin.begin(); fanin_it != fanin.end(); ++fanin_it) + { + auto fanin_n = fanin[*fanin_it]; + if (is_pi(fanin_n)) + return pis[get_pi_id(fanin_n)]; + } + + return std::nullopt; + }; + + // assigns tile directions to both given tiles assuming they are adjacent as fanin_t -> cur_t + const auto assign_directions = [this](const auto& fanin_t, const auto& cur_t) -> void + { + layout->assign_tile_out_dir(fanin_t, layout->get_bearing(fanin_t, cur_t)); + layout->assign_tile_inp_dir(cur_t, layout->get_bearing(cur_t, fanin_t)); + }; + + auto oc = 0u; + const std::function + traverse_and_construct = [&](const py::handle& node, const logic_network::vertex v, const fcn_gate_layout::tile& t) + { + auto fanin = node.attr("fanin"); + for (auto fanin_it = fanin.begin(); fanin_it != fanin.end(); ++fanin_it) + { + // access the dict + auto fanin_n = fanin[*fanin_it]; + + // make sure that only the intended fan-ins of crossing nodes are explored further + if (is_crossing(node) && !is_crossing_path(node, fanin_n)) + { + continue; + } + + auto n_type = get_gate_type(fanin_n); + + // exit condition + if (is_pi(fanin_n)) + { + network->create_edge(pis[get_pi_id(fanin_n)], v); + continue; + } + + auto second_layer = false; + + // has fanin node been created already? + if (auto vm_it = v_map.find(fanin_n); vm_it != v_map.end()) + { + // if a crossing node is revisited, just flag the tile to be used in second layer + if (is_crossing(fanin_n)) + { + second_layer = true; + } + // else, add connections (and update wire type) + else + { + network->create_edge(vm_it->second, v); + auto fanin_t = *layout->get_logic_tile(vm_it->second); + assign_directions(fanin_t, t); + + // update operation as fan-out increases + if (n_type == operation::W || n_type == operation::PI) + network->assign_op(vm_it->second, operation::F1O2); + else if (n_type == operation::F1O2) + network->assign_op(vm_it->second, operation::F1O3); + + continue; + } + } + + // is input pin? + if (auto pi = has_pi_fanin(fanin_n); pi && n_type == operation::W) + { + v_map[fanin_n] = *pi; + auto fanin_t = get_tile(fanin_n); + layout->assign_logic_vertex(fanin_t, *pi, true, is_po(fanin_n)); + assign_directions(fanin_t, t); + + // recurse with the current vertex instead of *pi to not create self-loops in exit condition branch + traverse_and_construct(fanin_n, v, fanin_t); + + continue; + } + + auto fanin_v = network->create_logic_vertex(n_type); + network->create_edge(fanin_v, v); + v_map[fanin_n] = fanin_v; + + auto fanin_t = get_tile(fanin_n, second_layer); + layout->assign_logic_vertex(fanin_t, fanin_v, has_pi_fanin(fanin_n).has_value()); + assign_directions(fanin_t, t); + + // store crossing fan-out for later crossing path checks + if (is_crossing(fanin_n)) + c_map[fanin_n] = node; + + traverse_and_construct(fanin_n, fanin_v, fanin_t); + } + }; + + auto pos = net.attr("po_map"); + for (auto po_it = pos.begin(); po_it != pos.end(); ++po_it) + { + // for some reason, pybind11 seems to not like lists of tuples; it could not infer the type of *po_it itself + auto po = py::reinterpret_borrow(*po_it)[0]; + auto n_type = get_gate_type(po); + + logic_network::vertex v; + if (n_type == operation::W) + { + v = network->create_po(fmt::format("po{}", oc++)); + } + else + { + v = network->create_logic_vertex(n_type); + network->create_po(v, fmt::format("po{}", oc++)); + } + + v_map[po] = v; + + auto tile = get_tile(po); + layout->assign_logic_vertex(tile, v, is_pi(po), true); + traverse_and_construct(po, v, tile); + } +} + +void one_pass_synthesis::update_timeout(mugen_handler& handler, mockturtle::stopwatch<>::duration time) const noexcept +{ + auto time_elapsed = std::chrono::duration_cast(time).count(); + // remaining time must be 1 because 0 means unlimited time + auto time_left = (config.timeout - time_elapsed > 0 ? static_cast(config.timeout - time_elapsed) : 1u); + + handler.update(time_left); +} diff --git a/src/algo/one_pass_synthesis.h b/src/algo/one_pass_synthesis.h new file mode 100644 index 000000000..fd827804c --- /dev/null +++ b/src/algo/one_pass_synthesis.h @@ -0,0 +1,182 @@ +// +// Created by marcel on 09.04.20. +// + +#ifndef FICTION_ONE_PASS_SYNTHESIS_H +#define FICTION_ONE_PASS_SYNTHESIS_H + + +#include "physical_design.h" +#include "onepass_pd_config.h" +#include "iter/dimension_iterator.h" +#include +#include +#include + + +/** + * An exact approach combining logic synthesis and physical design using SAT solving. This class provides the interface + * via which the technique can be called from the CLI. The instance generation happens in its sub-class mugen_handler. + * + * Mugen is a framework for one-pass synthesis of FCN circuit layouts developed by Winston Haaswijk. It can be found + * on GitHub: https://github.com/whaaswijk/mugen + * + * Using iterative SAT calls, an optimal synthesis & placement & routing for a given specification will be found. + * Starting with n, each possible layout dimension in n tiles will be examined by factorization and tested for + * realizability using the SAT solver glucose. When no upper bound is given, this approach will run until it finds a + * solution to the synthesis & placement & routing problem instance under all given constraints. Note that there are + * combinations of constraints for which no valid solution under the given parameters might exist. + * + * Since Mugen is written in Python3, fiction uses pybind11 for interoperability. This can lead to performance and + * integration issues. Make sure to follow all steps given in the README carefully if you decide to run this code. + * + * This approach is still experimental and can be excluded from compilation. + */ +class one_pass_synthesis : public physical_design +{ +public: + /** + * Standard constructor. + * + * @param tts List of truth tables that serve as specification. + * @param config Configuration object storing all the bounds, flags, and so on. + */ + one_pass_synthesis(std::vector&& tts, onepass_pd_config&& config); + /** + * Starts the physical design process by translating the problem to a Mugen instance. The layout size will be + * incremented after each UNSAT result until the given upper bound is reached. + * + * Returns a pd_result eventually. + * + * @return Result type containing statistical information about the process. + */ + pd_result operator()() override; + +private: + /** + * Specification of the Boolean functions to synthesize. + */ + const std::vector spec; + /** + * Arguments, flags, and options for the physical design process stored in one configuration object. + */ + onepass_pd_config config; + /** + * Iterates over the possible dimensions for the fcn_gate_layout to find. + */ + dimension_iterator dit{0}; + /** + * A Python interpreter instance that is necessary to call Mugen, a library written in Python. This instance is + * scoped and only need to exist. No operations are to be performed on this object. It handles creation and proper + * destruction of all Python objects used during this session and deals with the CPython API. + */ + static const pybind11::scoped_interpreter instance; + /** + * Path to the Mugen library. + */ + const char* MUGEN_PATH = "../libs/mugen/"; + /** + * Tests whether all needed dependencies have been installed and can be accessed via Python. + * + * @return true iff all dependencies are met. + */ + bool test_dependencies() const; + + /** + * Sub-class to handle interaction with the Python code Mugen as well as some house-keeping. + */ + class mugen_handler + { + public: + /** + * Standard constructor. + * + * @param s The Boolean functions to synthesize. + * @param fgl The gate layout pointer that is going to contain the created layout. + * @param c The configurations to respect in the SMT instance generation process. + */ + explicit mugen_handler(const std::vector& s, fcn_gate_layout_ptr fgl, onepass_pd_config c) noexcept; + /** + * Evaluates a given dimension regarding the stored configurations whether it can be skipped, i.e. does not + * need to be explored by Mugen. The better this function is, the more UNSAT instances can be skipped without + * losing the optimality guarantee. This function should never be overly restrictive! + * + * @param dim Dimension to evaluate. + * @return True if dim can safely be skipped because it is UNSAT anyways. + */ + bool skippable(const fcn_dimension_xy& dim) const noexcept; + /** + * Resizes the layout. + * + * @param dim Current dimension to work on. + */ + void update(const fcn_dimension_xy& dim) noexcept; + /** + * Sets the timeout to the given value. + * + * @param timeout New timeout value. + */ + void update(const uint32_t timeout) noexcept; + /** + * Passes the current scheme_graph to Mugen and synthesizes it. If there is an implementation on this graph + * realizing the specification, this function returns true. + * + * @return true iff the instance generated for the current configuration is SAT. + */ + bool is_satisfiable(); + + private: + /** + * The Boolean functions to synthesize. + */ + const std::vector& spec; + /** + * Number of primary inputs and primary outputs according to spec respectively. + */ + const uint64_t num_pis; + /** + * The sketch that later contains the layout generated from a model. + */ + fcn_gate_layout_ptr layout; + /** + * Configurations specifying layout restrictions. Used in instance generation among other places. + */ + onepass_pd_config config; + /** + * The Python module named Mugen. + */ + pybind11::module mugen; + /** + * Converts a vector of truth tables into a list of lists, i.e., Python data types. + * + * @param tts Truth tables. + * @return Python list of lists representing the truth tables. + */ + pybind11::list as_py_lists(const std::vector& tts) const; + /** + * Generates a scheme_graph object that serves as a blueprint for the synthesis task. It works as an imprint of + * the layout that is to be created by modeling the data flow connections of the used clocking scheme. + * + * @return A scheme_graph object representing the clocking scheme in its dimension and data flow. + */ + pybind11::object generate_scheme_graph(); + /** + * Extracts an fcn_gate_layout from the network synthesized by Mugen. + * + * @param net Synthesis result returned by Mugen. + */ + void to_gate_layout(pybind11::handle net) const; + }; + + /** + * Calculates the time left for solving by subtracting the time passed from the configured timeout and updates + * Mugen's timeout accordingly. + * + * @param handler Handler whose timeout is to be updated. + * @param time Time passed since beginning of the solving process. + */ + void update_timeout(mugen_handler& handler, mockturtle::stopwatch<>::duration time) const noexcept; +}; + + +#endif //FICTION_ONE_PASS_SYNTHESIS_H diff --git a/src/algo/onepass_pd_config.h b/src/algo/onepass_pd_config.h new file mode 100644 index 000000000..268581a34 --- /dev/null +++ b/src/algo/onepass_pd_config.h @@ -0,0 +1,74 @@ +// +// Created by marcel on 22.04.20. +// + +#ifndef ONEPASS_PD_CONFIGURATION_H +#define ONEPASS_PD_CONFIGURATION_H + +#include +#include +#include +#include "fcn_clocking_scheme.h" + + +/** + * Configuration struct to set up onepass physical design calls. + */ +struct onepass_pd_config +{ + /** + * Number of tiles to use. + */ + std::size_t upper_bound = std::numeric_limits::max(); + /** + * Use just one fixed tile size. + */ + std::size_t fixed_size = 0ul; + /** + * Number of threads to use for exploring the possible dimensions. + */ + std::size_t num_threads = 1ul; + /** + * Enable the use of wire elements. + */ + bool enable_wires = false; + /** + * Enable the use of NOT gates. + */ + bool enable_not = false; + /** + * Enable the use of AND gates. + */ + bool enable_and = false; + /** + * Enable the use of OR gates. + */ + bool enable_or = false; + /** + * Enable the use of MAJ gates. + */ + bool enable_maj = false; + /** + * Flag to indicate that crossings should be used. + */ + bool crossings = false; + /** + * Flag to indicate that designated wires should be routed to balance I/O port paths. + */ + bool io_ports = false; + /** + * Sets a timeout in seconds for the solving process, where 0 allows for unlimited time. + */ + uint32_t timeout = 0; + /** + * Clocking scheme to be used. + */ + std::shared_ptr scheme = nullptr; + /** + * Name of the resulting network. + */ + std::string name = ""; +}; + + +#endif //ONEPASS_PD_CONFIGURATION_H diff --git a/src/algo/physical_design.h b/src/algo/physical_design.h index 9f1919cc9..c6f3cb98b 100644 --- a/src/algo/physical_design.h +++ b/src/algo/physical_design.h @@ -11,13 +11,15 @@ #include "mockturtle/utils/progress_bar.hpp" #include "nlohmann/json.hpp" #include -#include #include -#include #include +#include +#include #include #include -#include +#include +#include +#include #include /** @@ -37,6 +39,10 @@ class physical_design }; public: + /** + * Default constructor for cases in which no logic network shall be passed. + */ + physical_design() = default; /** * Standard constructor. * diff --git a/src/io/cmd/equiv.h b/src/io/cmd/equiv.h index e313e89a5..343f89f02 100644 --- a/src/io/cmd/equiv.h +++ b/src/io/cmd/equiv.h @@ -66,7 +66,7 @@ class equiv_command : public command // check whether an index was given if (this->is_set("gate_layout")) { - if (gli == s.current_index()) + if (static_cast(gli) == s.current_index()) { std::cout << "[w] comparison against self" << std::endl; reset_flags(); @@ -143,7 +143,7 @@ class equiv_command : public command /** * Index of gate layout that current gate layout is to be compared against. */ - int gli = -1; + unsigned gli = -1; /** * Stores the result of the last equivalence check for easier access to result and logging data. */ @@ -154,7 +154,7 @@ class equiv_command : public command */ void reset_flags() { - gli = -1; + gli = 0; } }; diff --git a/src/io/cmd/exact.h b/src/io/cmd/exact.h index f8a290def..8ac60067b 100644 --- a/src/io/cmd/exact.h +++ b/src/io/cmd/exact.h @@ -35,17 +35,17 @@ namespace alice "A minimum FCN layout will be found that meets all given constraints.") { add_option("--clk_scheme,-s", clocking, - "Clocking scheme to be used {OPEN[3|4], 2DDWAVE[3|4], USE, RES, BANCS, TOPOLINANO[3|4]}", true); + "Clocking scheme to use {OPEN[3|4], 2DDWAVE[3|4], USE, RES, BANCS, TOPOLINANO[3|4]}", true); add_option("--upper_bound,-u", config.upper_bound, "Number of FCN gate tiles to use at maximum"); add_option("--fixed_size,-f", config.fixed_size, "Execute only one iteration with the given number of tiles"); add_option("--timeout,-t", config.timeout, "Timeout in seconds"); - add_option("--asynchronous,-a", config.num_threads, + add_option("--async,-a", config.num_threads, "Number of layout dimensions to examine in parallel"); - add_flag("--asynchronous_max,-A", + add_flag("--async_max,", "Examine as many layout dimensions in parallel as threads are available"); add_flag("--crossings,-x", config.crossings, "Enable wire crossings"); @@ -119,11 +119,11 @@ namespace alice } // fetch number of threads available on the system - if (this->is_set("asynchronous_max")) + if (this->is_set("async_max")) { if (auto threads_available = std::thread::hardware_concurrency(); threads_available == 0) { - env->out() << "[w] could not detect the number of available threads on your system" << std::endl; + env->out() << "[w] could not detect the number of threads available to your system" << std::endl; } else { @@ -131,7 +131,7 @@ namespace alice } } - // convert timeout seconds to milliseconds + // convert timeout entered in seconds to milliseconds config.timeout *= 1000; // perform exact physical design diff --git a/src/io/cmd/onepass.h b/src/io/cmd/onepass.h new file mode 100644 index 000000000..78c145b42 --- /dev/null +++ b/src/io/cmd/onepass.h @@ -0,0 +1,248 @@ +// +// Created by marcel on 09.04.20. +// + +#if MUGEN + +#ifndef FICTION_CMD_ONEPASS_H +#define FICTION_CMD_ONEPASS_H + + +#include "../../algo/one_pass_synthesis.h" +#include "fcn_gate_layout.h" +#include "fcn_clocking_scheme.h" +#include +#include +#include + + +namespace alice +{ + /** + * Executes a SAT-driven topology-based logic re-synthesis, i.e., a one-pass synthesis. Utilizes the synthesis tool + * Mugen by Winston Haaswijk. + * See algo/one_pass_synthesis.h for more details. + */ + class onepass_command : public command + { + public: + /** + * Standard constructor. Adds descriptive information, options, and flags. + * + * @param env alice::environment that specifies stores etc. + */ + explicit onepass_command(const environment::ptr& env) + : + command(env, "SAT-driven topology-based logic re-synthesis, i.e., one-pass synthesis. Uses " + "Mugen by Winston Haaswijk to synthesize a specification in terms of a truth table or a " + "logic network onto a given clocking scheme. Gate types to be used can be specified. If " + "none are given, all are enabled, because synthesis without gates cannot work. Layouts " + "resulting from this approach might be desynchronized. I/Os are always located at the " + "layout's borders.") + { + add_option("--clk_scheme,-s", clocking, + "Clocking scheme to use {2DDWAVE[3|4], USE, RES, BANCS}", true); + add_option("--upper_bound,-u", config.upper_bound, + "Number of FCN gate tiles to use at maximum"); + add_option("--fixed_size,-f", config.fixed_size, + "Execute only one iteration with the given number of tiles"); + add_option("--timeout,-t", config.timeout, + "Timeout in seconds"); + add_option("--async,-a", config.num_threads, + "Number of threads to use for parallel solving"); + + add_flag("--async_max", + "Use the maximum number of threads available to the system"); + add_flag("--network,-n", + "Re-synthesize the current logic network in store instead of the current truth table"); + add_flag("--and,-A", config.enable_and, + "Enable the use of AND gates"); + add_flag("--or,-O", config.enable_or, + "Enable the use of OR gates"); + add_flag("--not,-N", config.enable_not, + "Enable the use of NOT gates"); + add_flag("--maj,-M", config.enable_maj, + "Enable the use of MAJ gates"); + add_flag("--wires,-W", config.enable_wires, + "Enable the use of wire segments and fan-outs"); + add_flag("--crossings,-x", config.crossings, + "Enable wire crossings"); + add_flag("--io_ports,-i", config.io_ports, + "Use I/O port elements instead of gate pins"); + } + + protected: + /** + * Function to perform the physical design call. + * Given arguments are parsed and a placed and routed FCN gate layout is generated if possible. + */ + void execute() override + { + if (this->is_set("network")) + { + auto& s = store(); + + // error case: empty logic network store + if (s.empty()) + { + env->out() << "[w] no logic network in store" << std::endl; + reset_flags(); + return; + } + + auto ln = s.current(); + config.name = ln->get_name(); + + try + { + spec = ln->simulate(); + } + catch (const std::bad_alloc&) + { + env->out() << "[e] " << ln->get_name() << " has too many inputs to store its truth table" << std::endl; + return; + } + } + else + { + auto& s = store(); + + // error case: empty truth table store + if (s.empty()) + { + env->out() << "[w] no truth table in store" << std::endl; + reset_flags(); + return; + } + + auto tt = s.current(); + config.name = kitty::to_hex(tt); + spec = {tt}; + } + + // error case: -f and -u are both set + if (this->is_set("fixed_size") && this->is_set("upper_bound")) + { + env->out() << "[e] -u and -f cannot be set together" << std::endl; + reset_flags(); + return; + } + // set the value of fixed_size as the upper bound if set + else if (this->is_set("fixed_size")) + { + config.upper_bound = config.fixed_size; + } + + + // if no gate types are specified, enable them all + if (!config.enable_and && !config.enable_or && !config.enable_not && !config.enable_maj && !config.enable_wires) + { + config.enable_and = true; config.enable_or = true; config.enable_not = true; + config.enable_maj = true; config.enable_wires = true; + } + + // choose clocking + if (auto clk = get_clocking_scheme(clocking)) + { + if (auto name = clk->name; name == "OPEN3" || name == "OPEN4" || + name == "TOPOLINANO3" || name == "TOPOLINANO4") + { + env->out() << "[e] the \"" << name << "\" clocking scheme is not supported by this approach" << std::endl; + reset_flags(); + return; + } + + config.scheme = std::make_shared(*clk); + if (config.scheme->name != "RES" && config.enable_maj) + { + config.enable_maj = false; + env->out() << "[w] disabling MAJ gates as they are not supported by the " << config.scheme->name << + " clocking scheme" << std::endl; + } + } + else + { + env->out() << "[e] \"" << clocking << "\" does not refer to a supported clocking scheme" << std::endl; + reset_flags(); + return; + } + + // fetch number of threads available on the system + if (this->is_set("async_max")) + { + if (auto threads_available = std::thread::hardware_concurrency(); threads_available == 0) + { + env->out() << "[w] could not detect the number of threads available to your system" << std::endl; + } + else + { + config.num_threads = threads_available; + } + } + + // scope to destruct the one_pass_synthesis object to end Python interpreter's lifetime + { + auto print_name = config.name; + // perform one-pass synthesis + one_pass_synthesis physical_design{std::move(spec), std::move(config)}; + + if (auto result = physical_design(); result.success) + { + store().extend() = physical_design.get_layout(); + pd_result = result.json; + } + else + { + env->out() << "[e] impossible to synthesize " << print_name + << " within the given parameters" << std::endl; + } + } + + reset_flags(); + } + + /** + * Logs the resulting information in a log file. + * + * @return JSON object containing information about the solving process. + */ + nlohmann::json log() const override + { + return pd_result; + } + + private: + /** + * Configuration object extracted from arguments and flags. + */ + onepass_pd_config config{}; + /** + * Identifier of clocking scheme to use. + */ + std::string clocking = "2DDWave"; + /** + * Specification to synthesize. + */ + std::vector spec; + /** + * Resulting logging information. + */ + nlohmann::json pd_result; + + /** + * Reset all flags. Necessary for some reason... alice bug? + */ + void reset_flags() + { + config = onepass_pd_config{}; + clocking = "2DDWave"; + } + }; + + ALICE_ADD_COMMAND(onepass, "Physical Design") +} + + +#endif //FICTION_CMD_ONEPASS_H + +#endif // MUGEN diff --git a/src/io/cmd/read.h b/src/io/cmd/read.h index ea8531844..8b9a84687 100644 --- a/src/io/cmd/read.h +++ b/src/io/cmd/read.h @@ -6,14 +6,9 @@ #define FICTION_READ_H -#include "logic_network.h" +#include "network_reader.h" #include -#include -#include -#include -#include #include -#include namespace alice @@ -49,7 +44,7 @@ namespace alice add_option("filename", filename, "Verilog or AIGER filename or directory")->required(); add_flag("--sort,-s", sort, - "Sort files in given directory by file size prior to parsing"); + "Sort networks in given directory by vertex count prior to storing them"); } protected: @@ -58,77 +53,19 @@ namespace alice */ void execute() override { - // checks for extension validity - auto is_valid_extension = [](const auto& _f)->bool - { - const std::vector extensions{".v", ".aig"}; - return std::any_of(extensions.cbegin(), extensions.cend(), - [&_f](const auto& _e) { return boost::filesystem::extension(_f) == _e; }); - }; - - // check for for given file's properties - if (boost::filesystem::exists(filename)) + try { - if (boost::filesystem::is_regular_file(filename)) - { - // collect valid files only - if (is_valid_extension(filename)) - paths.push_back(filename); - } + network_reader reader{filename, env->out()}; - else if (boost::filesystem::is_directory(filename)) - { - // iterate over each file in the directory - for (auto& file : boost::make_iterator_range(boost::filesystem::directory_iterator(filename), {})) - { - if (boost::filesystem::is_regular_file(file)) - { - // collect valid files only - if (is_valid_extension(file)) - paths.push_back(file.path().string()); - } - } - } - else // existing file but a weird one - env->out() << "[e] given file name does not point to a regular file" << std::endl; - } - else // path leads nowhere - env->out() << "[e] given file name does not exist" << std::endl; - - // sort by file size to make the small ones go first - if (sort) - { - std::sort(paths.begin(), paths.end(), [](std::string& f1, std::string& f2) - { - return boost::filesystem::file_size(f1) >= boost::filesystem::file_size(f2); - }); + for (const auto& ln : reader.get_networks(sort)) + store().extend() = ln; } - - // handle collected files - for (const auto& f : paths) + catch (...) { - // parse Verilog - if (boost::filesystem::extension(f) == ".v") - { - read, - lorina::return_code(const std::string&, - const lorina::verilog_reader&, - lorina::diagnostic_engine*)>(f, lorina::read_verilog); - } - // parse Aiger - else if (boost::filesystem::extension(f) == ".aig") - { - read, - lorina::return_code(const std::string&, - const lorina::aiger_reader&, - lorina::diagnostic_engine*)>(f, lorina::read_aiger); - } - // parse ... - // else if (boost::filesystem::extension(f) == ...) + env->out() << "[e] no networks were read" << std::endl; } // reset flags, necessary for some reason... alice bug? - paths = {}; sort = false; } @@ -137,48 +74,10 @@ namespace alice * Verilog filename. */ std::string filename; - /** - * All paths found under filename. - */ - std::vector paths{}; /** * Flag to indicate that files should be sorted by file size. */ bool sort = false; - /** - * Actual read function that constructs the logic network from a file. - * - * @tparam Reader The functor type which constructs the network, i.e. handles the callbacks. - * @tparam ReadFun The file parser type. - * @param file The file name. - * @param rfun The actual parsing function. - */ - template - void read(const std::string& file, ReadFun rfun) const noexcept - { - auto name = boost::filesystem::path{file}.stem().string(); - logic_network::mig_nt mig; - - try - { - if (lorina::diagnostic_engine diag{}; - rfun(file, Reader{mig}, &diag) == lorina::return_code::success) - { - if (mig.is_combinational()) - store().extend() = std::make_shared(std::move(mig), std::move(name)); - else - env->out() << "[e] sequential networks are not yet supported for FCN physical design" << std::endl; - } - else - { - env->out() << "[e] parsing error in " << file << std::endl; - } - } - catch (...) - { - env->out() << "[e] " << file << " contains unsupported features" << std::endl; - } - } }; ALICE_ADD_COMMAND(read, "I/O") diff --git a/src/io/cmd/tt.h b/src/io/cmd/tt.h index ecad99dc9..0ee23b5c7 100644 --- a/src/io/cmd/tt.h +++ b/src/io/cmd/tt.h @@ -7,8 +7,8 @@ #include -#include #include +#include #include #include diff --git a/src/io/commands.h b/src/io/commands.h index 955f5c6d3..84955adf6 100644 --- a/src/io/commands.h +++ b/src/io/commands.h @@ -17,6 +17,7 @@ #include "cmd/simulate.h" #include "cmd/akers.h" #include "cmd/exact.h" +#include "cmd/onepass.h" #include "cmd/ortho.h" #include "cmd/check.h" #include "cmd/equiv.h" diff --git a/src/io/csv_writer.h b/src/io/csv_writer.h new file mode 100644 index 000000000..81b9eed97 --- /dev/null +++ b/src/io/csv_writer.h @@ -0,0 +1,64 @@ +// +// Created by marcel on 06.05.20. +// + +#ifndef FICTION_CSV_WRITER_H +#define FICTION_CSV_WRITER_H + + +#include +#include + + +class csv_writer +{ +public: + /** + * Standard constructor. Opens a file. + * + * @param filename CSV file to write into. + */ + explicit csv_writer(const std::string& filename) + : + file{filename, std::ios::out | std::ios::app} + {} + /** + * Writes a single line of comma-separated values to the stored file. Note that no escape checks are performed. + * + * @tparam Ts Types of the variadic parameter pack. + * @param args Arguments to write to the file. + */ + template + void write_line(Ts&&... args) + { + if (file.is_open()) + (file << ... << add_delimiter(std::forward(args))) << std::endl; + } + +private: + /** + * CSV file to write into. + */ + std::ofstream file; + /** + * The delimiter to use. + */ + static constexpr const char* DELIMITER = ", "; + /** + * Writes the given argument to the stored file and returns a delimiter. + * + * @tparam T Type of the given parameter. + * @param arg Argument to write to the file. + * @return Delimiter. + */ + template + const char* add_delimiter(T&& arg) + { + if (file.is_open()) + file << arg; + + return DELIMITER; + } +}; + +#endif //FICTION_CSV_WRITER_H diff --git a/src/io/network_reader.cpp b/src/io/network_reader.cpp new file mode 100644 index 000000000..618805af1 --- /dev/null +++ b/src/io/network_reader.cpp @@ -0,0 +1,86 @@ +// +// Created by marcel on 01.09.20. +// + +#include "network_reader.h" + +network_reader::network_reader(const std::string& filename, std::ostream& o) + : + out{o} +{ + // checks for extension validity + auto is_valid_extension = [](const auto& _f)->bool + { + const std::vector extensions{".v", ".aig"}; + return std::any_of(extensions.cbegin(), extensions.cend(), + [&_f](const auto& _e) { return boost::filesystem::extension(_f) == _e; }); + }; + + std::vector paths{}; + + // check for for given file's properties + if (boost::filesystem::exists(filename)) + { + if (boost::filesystem::is_regular_file(filename)) + { + // collect valid files only + if (is_valid_extension(filename)) + paths.push_back(filename); + } + + else if (boost::filesystem::is_directory(filename)) + { + // iterate over each file in the directory + for (auto& file : boost::make_iterator_range(boost::filesystem::directory_iterator(filename), {})) + { + if (boost::filesystem::is_regular_file(file)) + { + // collect valid files only + if (is_valid_extension(file)) + paths.push_back(file.path().string()); + } + } + } + else // existing file but a weird one + out << "[e] given file name does not point to a regular file" << std::endl; + } + else // path leads nowhere + out << "[e] given file name does not exist" << std::endl; + + // handle collected files + for (const auto& f : paths) + { + // parse Verilog + if (boost::filesystem::extension(f) == ".v") + { + read, + lorina::return_code(const std::string&, + const lorina::verilog_reader&, + lorina::diagnostic_engine*)>(f, lorina::read_verilog); + } + // parse Aiger + else if (boost::filesystem::extension(f) == ".aig") + { + read, + lorina::return_code(const std::string&, + const lorina::aiger_reader&, + lorina::diagnostic_engine*)>(f, lorina::read_aiger); + } + // parse ... + // else if (boost::filesystem::extension(f) == ...) + } +} + +const std::vector& network_reader::get_networks(bool sorted) +{ + // sort by network size to make the small ones go first + if (sorted) + { + std::sort(networks.begin(), networks.end(), [](auto n1, auto n2) + { + return n1->vertex_count() >= n2->vertex_count(); + }); + } + + return networks; +} diff --git a/src/io/network_reader.h b/src/io/network_reader.h new file mode 100644 index 000000000..2859616cd --- /dev/null +++ b/src/io/network_reader.h @@ -0,0 +1,83 @@ +// +// Created by marcel on 01.09.20. +// + +#ifndef FICTION_NETWORK_READER_H +#define FICTION_NETWORK_READER_H + + +#include "logic_network.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class network_reader +{ +public: + /** + * Standard constructor. Reads and constructs logic networks. May throw. + * + * @param filename Path to the file or folder of files to read. + * @param out Output stream to write status updates into. + */ + network_reader(const std::string& filename, std::ostream& out); + /** + * Getter for networks generated from given files. + * + * @param sorted Flag to indicate that the networks are to be returned in ascending order of their vertex count. + * @return Vector of read and constructed logic networks. + */ + const std::vector& get_networks(bool sorted = false); + +private: + /** + * Output stream to which status updates are to be written. + */ + std::ostream& out; + /** + * Vector of logic networks read from files given at construction. + */ + std::vector networks; + /** + * Actual read function that constructs the logic network from a file. + * + * @tparam Reader The functor type which constructs the network, i.e. handles the callbacks. + * @tparam ReadFun The file parser type. + * @param file The file name. + * @param rfun The actual parsing function. + */ + template + void read(const std::string& file, ReadFun rfun) noexcept + { + auto name = boost::filesystem::path{file}.stem().string(); + logic_network::mig_nt mig; + + try + { + if (lorina::diagnostic_engine diag{}; + rfun(file, Reader{mig}, &diag) == lorina::return_code::success) + { + if (mig.is_combinational()) + networks.push_back(std::make_shared(std::move(mig), std::move(name))); + else + out << "[e] sequential networks are not yet supported for FCN physical design" << std::endl; + } + else + { + out << "[e] parsing error in " << file << std::endl; + } + } + catch (...) + { + out << "[e] " << file << " contains unsupported features" << std::endl; + } + } +}; + + +#endif //FICTION_NETWORK_READER_H diff --git a/src/io/qca_writer.cpp b/src/io/qca_writer.cpp index e41fbe01f..c4a30fbf9 100644 --- a/src/io/qca_writer.cpp +++ b/src/io/qca_writer.cpp @@ -8,7 +8,7 @@ namespace qca { void write(fcn_cell_layout_ptr fcl, const std::string& filename) { - std::ofstream file(filename, std::ofstream::out); + std::ofstream file(filename, std::ios::out); if (!file.is_open()) throw std::ofstream::failure("could not open file"); diff --git a/src/io/stores.h b/src/io/stores.h index 886975724..6b2712433 100644 --- a/src/io/stores.h +++ b/src/io/stores.h @@ -140,17 +140,17 @@ namespace alice { auto [cp, tp] = layout->critical_path_length_and_throughput(); os << fmt::format(" {} - {} × {}, #G: {}, #W: {}, #C: {}, #L: {}, CP: {}, TP: 1/{}", layout->get_name(), - layout->x(), layout->y(), layout->gate_count(), layout->wire_count(), - layout->crossing_count(), layout->latch_count(), cp, tp) << std::endl; + layout->x(), layout->y(), layout->gate_count(true), layout->wire_count(true), + layout->crossing_count(true), layout->latch_count(), cp, tp) << std::endl; } ALICE_LOG_STORE_STATISTICS(fcn_gate_layout_ptr, layout) { auto area = layout->x() * layout->y(); auto bb = layout->determine_bounding_box(); - auto gate_tiles = layout->gate_count(); - auto wire_tiles = layout->wire_count(); - auto crossings = layout->crossing_count(); + auto gate_tiles = layout->gate_count(true); + auto wire_tiles = layout->wire_count(true); + auto crossings = layout->crossing_count(true); auto [cp, tp] = layout->critical_path_length_and_throughput(); return nlohmann::json @@ -176,7 +176,7 @@ namespace alice {"crossings", crossings}, {"latches", layout->latch_count()}, {"critical path", cp}, - {"throughput", "1/" + std::to_string(tp)} + {"throughput", fmt::format("1/{}", tp)} }; } diff --git a/src/io/svg_writer.cpp b/src/io/svg_writer.cpp index ac38f65c7..e08437156 100644 --- a/src/io/svg_writer.cpp +++ b/src/io/svg_writer.cpp @@ -7,20 +7,33 @@ namespace svg { std::string generate_svg_string(fcn_cell_layout_ptr fcl, bool simple) + { + // The existence of a library implies the utilization of a regular clocking scheme. Vice versa, the existence of + // a regular clocking implies that there has to be a library assigned to the layout. A scheme itself does not + // give enough information about some cell's clocking because it is dependent on its position within a tile. + // Therefore, the zones can only be deterministically assigned to a circuit if their sizes are known. The tile + // size is only given in gate-libraries. Consequently, there has to be one assigned in order to enable for + // unambiguous clock zone mapping. + if(fcl->get_library() == nullptr) + return generate_cell_based_svg(fcl, simple); + else + return generate_tile_based_svg(fcl, simple); + } + + std::string generate_tile_based_svg(fcn_cell_layout_ptr fcl, bool simple) { // Collects ALL tile-descriptions std::stringstream tile_descriptions{}; // Used for generating tile-descriptions with information about the tile's coordinates and clock zone - // It is needed because cells may not be in "tile-order" when read from cell-layout + // It is needed because cells may not be in "tile-order" when read from a cell layout coord_to_tile_mapping coord_to_tile{}; coord_to_cell_list_mapping coord_to_cells{}; coord_to_latch_mapping coord_to_latch_tile{}; coord_to_cell_list_mapping coord_to_latch_cells{}; // Used to determine the color of cells, tiles and text based on its clock zone - std::vector - cell_colors{{clock_zone_1_cell, clock_zone_2_cell, clock_zone_3_cell, clock_zone_4_cell}}, + static const std::vector tile_colors{{clock_zone_1_tile, clock_zone_2_tile, clock_zone_3_tile, clock_zone_4_tile}}, text_colors{{clock_zone_12_text, clock_zone_12_text, clock_zone_34_text, clock_zone_34_text}}; @@ -48,6 +61,7 @@ namespace svg // It also makes sure that all required tiles are created coord_to_latch_tile[tile_coords] = {latch, clock_zone, latch_delay}; } + } else { @@ -63,78 +77,19 @@ namespace svg } } - // Determines cell type and color - std::string cell_description, cell_color; - switch (fcl->get_cell_type(c)) - { - case fcn::EMPTY_CELL: - break; - case fcn::NORMAL_CELL: - { - cell_color = latch_delay ? clock_zone_latch_cell : cell_colors[clock_zone]; - if (simple) - { - cell_description = simple_cell; - } - else if (cell[Z] != 0 && fcl->get_cell_mode(c) == fcn::cell_mode::VERTICAL) - { - cell_description = via; - } - else if ((c[Z] == 0 && !fcl->is_free_cell(fcl->above(c))) || - (c[Z] != 0 && fcl->is_free_cell(fcl->below(c)))) - { - cell_description = cross; - } - else if (c[Z] != 0) - { - break; - } - else - { - cell_description = cell; - } - break; - } - case fcn::INPUT_CELL: - { - cell_color = pi_cell; - cell_description = simple ? simple_cell : cell; - break; - } - case fcn::OUTPUT_CELL: - { - cell_color = po_cell; - cell_description = simple ? simple_cell : cell; - break; - } - case fcn::qca::CONST_0_CELL: - { - cell_color = "#000000"; - cell_description = simple ? simple_cell : const0; - break; - } - case fcn::qca::CONST_1_CELL: - { - cell_color = "#000000"; - cell_description = simple ? simple_cell : const1; - break; - } - default: - { - throw std::invalid_argument("unsupported cell type(s) occurred in layout"); - } - } - // Represent the x- and y-coordinates inside the c's tile coord_t in_tile_x = c[X] % fcl->get_library()->gate_x_size(); coord_t in_tile_y = c[Y] % fcl->get_library()->gate_y_size(); + // Determines cell type and color + auto desc_col = generate_description_color(fcl, c, simple); + // Current cell-description can now be appended to the description of all cells in the current tile if (latch_delay) { coord_to_latch_cells[tile_coords] = current_cells.append( - fmt::format(cell_description, cell_color, + fmt::format(desc_col.first, desc_col.second, starting_offset_latch_cell_x + in_tile_x * cell_distance, starting_offset_latch_cell_y + in_tile_y * cell_distance)); @@ -142,7 +97,7 @@ namespace svg else { coord_to_cells[tile_coords] = current_cells.append( - fmt::format(cell_description, cell_color, + fmt::format(desc_col.first, desc_col.second, starting_offset_cell_x + in_tile_x * cell_distance, starting_offset_cell_y + in_tile_y * cell_distance)); } @@ -161,8 +116,8 @@ namespace svg double y_pos = starting_offset_tile_y + y * tile_distance; auto c_descr = fmt::format(descr, x_pos, y_pos, tile_colors[czone], cell_descriptions, - simple ? "" : text_colors[czone], - simple ? "" : std::to_string(czone + 1)); + simple ? "" : text_colors[czone], + simple ? "" : std::to_string(czone + 1)); tile_descriptions << c_descr; } @@ -179,11 +134,10 @@ namespace svg double x_pos = starting_offset_latch_x + x * tile_distance; double y_pos = starting_offset_latch_y + y * tile_distance; - auto t_descr = fmt::format(descr, x_pos, y_pos, tile_colors[czone_lo], tile_colors[czone_up], - cell_descriptions, - text_colors[czone_up], simple ? "" : std::to_string(czone_up + 1), - text_colors[czone_lo], - simple ? "" : std::to_string(czone_lo + 1)); + auto t_descr = fmt::format(descr, x_pos, y_pos, tile_colors[czone_lo], tile_colors[czone_up], cell_descriptions, + text_colors[czone_up], simple ? "" : std::to_string(czone_up + 1), + text_colors[czone_lo], + simple ? "" : std::to_string(czone_lo + 1)); tile_descriptions << t_descr; } @@ -195,6 +149,111 @@ namespace svg double viewbox_y = 2 * viewbox_distance + length_y * tile_distance; return fmt::format(header, fiction::VERSION, fiction::REPO, boost::lexical_cast(viewbox_x), - boost::lexical_cast(viewbox_y), tile_descriptions.str()); + boost::lexical_cast(viewbox_y), tile_descriptions.str()); + } + + std::string generate_cell_based_svg(fcn_cell_layout_ptr fcl, bool simple) + { + std::stringstream cell_descriptions{}; + + static const std::vector cell_colors{{clock_zone_1_cell, clock_zone_2_cell, clock_zone_3_cell, clock_zone_4_cell}}; + + for (const auto& c : fcl->cells() | iter::filterfalse([fcl](const fcn_cell_layout::cell& _c) + { return fcl->is_free_cell(_c); })) + { + + // Determines cell type and color + auto desc_col = generate_description_color(fcl, c, simple); + + // Current cell-description can now be appended to the description of all cells + if (fcl->get_latch(c)) + { + cell_descriptions << fmt::format(desc_col.first, desc_col.second, + starting_offset_tile_x + starting_offset_latch_cell_x + c[X] * cell_distance, + starting_offset_tile_y + starting_offset_latch_cell_y + c[Y] * cell_distance); + } + else + { + cell_descriptions << fmt::format(desc_col.first, desc_col.second, + starting_offset_tile_x + starting_offset_cell_x + c[X] * cell_distance, + starting_offset_tile_y + starting_offset_cell_y + c[Y] * cell_distance); + } + } + + double viewbox_x = 2 * viewbox_distance + fcl->x() * cell_distance; + double viewbox_y = 2 * viewbox_distance + fcl->y() * cell_distance; + + return fmt::format(header, fiction::VERSION, fiction::REPO, boost::lexical_cast(viewbox_x), + boost::lexical_cast(viewbox_y), cell_descriptions.str()); + } + + std::pair generate_description_color(fcn_cell_layout_ptr fcl, const fcn_cell_layout::cell& c, bool simple) + { + std::string cell_description, cell_color{}; + static const std::vector cell_colors{{clock_zone_1_cell, clock_zone_2_cell, clock_zone_3_cell, clock_zone_4_cell}}; + + switch (fcl->get_cell_type(c)) + { + case fcn::EMPTY_CELL: + { + break; + } + case fcn::NORMAL_CELL: + { + cell_color = fcl->get_latch(c) ? clock_zone_latch_cell : cell_colors[*fcl->cell_clocking(c)]; + if (simple) + { + cell_description = simple_cell; + } + else if (cell[Z] != 0 && fcl->get_cell_mode(c) == fcn::cell_mode::VERTICAL) + { + cell_description = via; + } + else if ((c[Z] == 0 && !fcl->is_free_cell(fcl->above(c))) || + (c[Z] != 0 && fcl->is_free_cell(fcl->below(c)))) + { + cell_description = cross; + } + else if (c[Z] != 0) + { + break; + } + else + { + cell_description = cell; + } + break; + } + case fcn::INPUT_CELL: + { + cell_color = pi_cell; + cell_description = simple ? simple_cell : cell; + break; + } + case fcn::OUTPUT_CELL: + { + cell_color = po_cell; + cell_description = simple ? simple_cell : cell; + break; + } + case fcn::qca::CONST_0_CELL: + { + cell_color = "#000000"; + cell_description = simple ? simple_cell : const0; + break; + } + case fcn::qca::CONST_1_CELL: + { + cell_color = "#000000"; + cell_description = simple ? simple_cell : const1; + break; + } + default: + { + throw std::invalid_argument("unsupported cell type(s) occurred in layout"); + } + } + + return std::make_pair(cell_description, cell_color); } } diff --git a/src/io/svg_writer.h b/src/io/svg_writer.h index 005b4e8b1..e242da51c 100644 --- a/src/io/svg_writer.h +++ b/src/io/svg_writer.h @@ -50,7 +50,7 @@ namespace svg // SVG parts with placeholders constexpr const char *header = "\n" - "\n" + "\n" "\n" ">; /** - * Returns an SVG string representing the given cell layout. + * Returns an SVG string representing the given cell layout. Both tile- and cell-based layouts are supported. * - * Currently, this works for tile-based 4-phase QCA cell layouts generated from a 5 x 5 gate library. - * TODO make this generic for cell-based designs + * For tile-based layouts, only QCA of tile size 5 x 5 is supported so far. * * @param fcl The cell layout to generate an SVG representation for. * @param simple Flag to indicate that the SVG representation should be generated with less details. Recommended @@ -316,6 +315,32 @@ namespace svg * @return The SVG string containing a visual representation of the given layout. */ std::string generate_svg_string(fcn_cell_layout_ptr fcl, bool simple); + /** + * Returns an SVG string representing the given tile-based clocked cell layout. + * + * @param fcl The cell layout to generate an SVG representation for. + * @param simple Flag to indicate that the SVG representation should be generated with less details. Recommended + * for large layouts. + * @return The SVG string containing a visual representation of the given layout. + */ + std::string generate_tile_based_svg(fcn_cell_layout_ptr fcl, bool simple); + /** + * Returns an SVG string representing the given cell-based clocked cell layout + * + * @param fcl The cell layout to generate an SVG representation for. + * @param simple Flag to indicate that the SVG representation should be generated with less details. Recommended + * for large layouts. + * @return The SVG string containing a visual representation of the given layout. + */ + std::string generate_cell_based_svg(fcn_cell_layout_ptr fcl, bool simple); + /** + * Creates a pair of the correct template and color for the given cell as SVG strings. + * + * @param c The cell to select a template and a color for. + * @return A pair of cell description and color SVG strings. + */ + std::pair generate_description_color(fcn_cell_layout_ptr fcl, const fcn_cell_layout::cell& c, + bool simple); } #endif //FICTION_SVG_WRITER_H diff --git a/src/io/tt_reader.cpp b/src/io/tt_reader.cpp new file mode 100644 index 000000000..926149ee6 --- /dev/null +++ b/src/io/tt_reader.cpp @@ -0,0 +1,37 @@ +// +// Created by marcel on 05.05.20. +// + +#include "tt_reader.h" + + +tt_reader::tt_reader(const std::string& filename) +{ + std::ifstream file{filename, std::ios::in}; + if (file.is_open()) + { + // read the file in a vector of strings + elements = std::vector{std::istream_iterator{file}, + std::istream_iterator{}}; + // TODO fill the vector with an std::copy_if instead where you increase a counter and only copy if counter is even + } +} + +std::optional tt_reader::next() +{ + if (pos >= elements.size() - 1) + return std::nullopt; + + // remove the 0x prefix + auto table = elements[pos].substr(2u); + // determine number of truth table variables + uint32_t num_vars = std::log2(table.size() << 2u); + // create truth table + kitty::dynamic_truth_table tt{num_vars}; + kitty::create_from_hex_string(tt, table); + + // TODO change pos to an iterator + pos += 2; + + return tt; +} diff --git a/src/io/tt_reader.h b/src/io/tt_reader.h new file mode 100644 index 000000000..3637a8fdf --- /dev/null +++ b/src/io/tt_reader.h @@ -0,0 +1,55 @@ +// +// Created by marcel on 05.05.20. +// + +#ifndef FICTION_TT_READER_H +#define FICTION_TT_READER_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Simple reader to parse truth tables from files. The files must be organized as follows: Each line consists of a + * truth table in hexadecimal form plus its corresponding Boolean expression. The two are separated by a space. + * + * This format is used by, e.g., Alan Mishchenko for his DSD functions: https://people.eecs.berkeley.edu/~alanmi/temp5/ + */ +class tt_reader +{ +public: + /** + * Standard constructor. Reads the given file in a vector of lines. + * + * @param filename File to parse. + */ + explicit tt_reader(const std::string& filename); + /** + * Returns the next truth table parsed from the file or std::nullopt if no further truth tables are available. + * + * @return Next truth table from the file. + */ + std::optional next(); + +private: + /** + * Contains the hexadecimal truth tables and their Boolean expressions in string representation. The two are + * alternating. + */ + std::vector elements; + /** + * Current position in the vector. + */ + std::size_t pos = 0ul; +}; + + +#endif //FICTION_TT_READER_H diff --git a/src/tech/fcn_cell_layout.cpp b/src/tech/fcn_cell_layout.cpp index 53bf52ddb..d7f043d89 100644 --- a/src/tech/fcn_cell_layout.cpp +++ b/src/tech/fcn_cell_layout.cpp @@ -286,8 +286,15 @@ fcn_layout::bounding_box fcn_cell_layout::determine_bounding_box() const noexcep return bounding_box{min_x, min_y, max_x, max_y}; } -void fcn_cell_layout::write_layout(std::ostream& os, bool io_color) const noexcept +void fcn_cell_layout::write_layout(std::ostream& os, const bool io_color, const bool clk_color) const noexcept { + // empty layout + if (!area()) + { + os << "[i] empty layout" << std::endl; + return; + } + // Escape color sequence for input colors (green). const char* INP_COLOR = "\033[38;5;28m"; // Escape color sequence for output colors (red). @@ -296,6 +303,12 @@ void fcn_cell_layout::write_layout(std::ostream& os, bool io_color) const noexce const char* LATCH_COLOR = "\033[48;5;232;38;5;226m"; // Escape color sequence for resetting colors. const char* COLOR_RESET = "\033[0m"; + // Escape color sequences for clock background colors (white to dark grey). + std::vector CLOCK_COLORS{{"\033[48;5;255;38;5;232m", // white tile, black text + "\033[48;5;248;38;5;232m", // light grey tile, black text + "\033[48;5;240;38;5;255m", // grey tile, white text + "\033[48;5;236;38;5;255m" // dark grey tile, white text + }}; for (auto y_pos : iter::range(y())) { @@ -306,6 +319,9 @@ void fcn_cell_layout::write_layout(std::ostream& os, bool io_color) const noexce auto type_0 = get_cell_type(c); auto type_1 = get_cell_type(above(c)); + if (clk_color && cell_clocking(c)) + os << CLOCK_COLORS[*cell_clocking(c)]; + // is crossing if (type_1 != fcn::EMPTY_CELL) os << std::string(1u, type_1); diff --git a/src/tech/fcn_cell_layout.h b/src/tech/fcn_cell_layout.h index ba48f0c72..8492face2 100644 --- a/src/tech/fcn_cell_layout.h +++ b/src/tech/fcn_cell_layout.h @@ -224,8 +224,9 @@ class fcn_cell_layout : public fcn_layout * * @param os An std::ostream channel to write the textual representation of this layout into. * @param io_color Flag to indicate features like PI/PO should be printed with color escape. + * @param clk_color Flag to indicate that clock zones should be printed with color escape. Can look weird. */ - void write_layout(std::ostream& os = std::cout, bool io_color = true) const noexcept; + void write_layout(std::ostream& os = std::cout, const bool io_color = true, const bool clk_color = false) const noexcept; private: /** diff --git a/src/tech/port_router.cpp b/src/tech/port_router.cpp index 7ddc6e480..e6f32517b 100644 --- a/src/tech/port_router.cpp +++ b/src/tech/port_router.cpp @@ -80,8 +80,11 @@ void port_router::compute_qca_5x5_ports() p.inp.emplace(0u, 2u); // NONE (WEST) - if (layout->get_tile_inp_dirs(t) == layout::DIR_NONE) - p.inp.emplace(0u, 2u); + if (auto op = layout->get_op(t); op != operation::NOT && op != operation::W) + { + if (layout->get_tile_inp_dirs(t) == layout::DIR_NONE) + p.inp.emplace(0u, 2u); + } // determine outgoing connector ports if (layout->is_tile_out_dir(t, layout::DIR_N)) @@ -94,8 +97,11 @@ void port_router::compute_qca_5x5_ports() p.out.emplace(0u, 2u); // NONE (EAST) - if (layout->get_tile_inp_dirs(t) == layout::DIR_NONE) - p.out.emplace(4u, 2u); + if (auto op = layout->get_op(t); op != operation::NOT && op != operation::W) + { + if (layout->get_tile_out_dirs(t) == layout::DIR_NONE) + p.out.emplace(4u, 2u); + } // assign connector ports g_ports[std::make_pair(t, *layout->get_logic_vertex(t))] = p; diff --git a/src/topo/bidirectional_graph.h b/src/topo/bidirectional_graph.h index bfbf229b5..a28ea0bba 100644 --- a/src/topo/bidirectional_graph.h +++ b/src/topo/bidirectional_graph.h @@ -6,7 +6,6 @@ #define FICTION_BIDIRECTIONAL_GRAPH_H #include "range.h" -#include "grid_topology.h" #include #include #include diff --git a/src/topo/fcn_gate_layout.cpp b/src/topo/fcn_gate_layout.cpp index 76005c48f..aca8bd350 100644 --- a/src/topo/fcn_gate_layout.cpp +++ b/src/topo/fcn_gate_layout.cpp @@ -995,7 +995,7 @@ fcn_layout::bounding_box fcn_gate_layout::determine_bounding_box() const noexcep bool elem_found = false; for (coord_t y = 0u; y < this->y(); ++y) { - if (!this->is_free_tile(tile{x, y, GROUND}) || !this->is_free_tile(tile{x, y, 1})) + if (!this->is_free_tile(tile{x, y, GROUND}) || !this->is_free_tile(tile{x, y, CROSSING})) { elem_found = true; break; @@ -1014,7 +1014,7 @@ fcn_layout::bounding_box fcn_gate_layout::determine_bounding_box() const noexcep bool elem_found = false; for (coord_t x = 0u; x < this->x(); ++x) { - if (!this->is_free_tile(tile{x, y, GROUND}) || !this->is_free_tile(tile{x, y, 1})) + if (!this->is_free_tile(tile{x, y, GROUND}) || !this->is_free_tile(tile{x, y, CROSSING})) { elem_found = true; break; @@ -1034,7 +1034,7 @@ fcn_layout::bounding_box fcn_gate_layout::determine_bounding_box() const noexcep for (coord_t y = 0u; y < this->y(); ++y) { if (!this->is_free_tile(tile{static_cast(x), y, GROUND}) || - !this->is_free_tile(tile{static_cast(x), y, 1})) + !this->is_free_tile(tile{static_cast(x), y, CROSSING})) { elem_found = true; break; @@ -1054,7 +1054,7 @@ fcn_layout::bounding_box fcn_gate_layout::determine_bounding_box() const noexcep for (coord_t x = 0u; x < this->x(); ++x) { if (!this->is_free_tile(tile{x, static_cast(y), GROUND}) || - !this->is_free_tile(tile{x, static_cast(y), 1})) + !this->is_free_tile(tile{x, static_cast(y), CROSSING})) { elem_found = true; break; @@ -1142,7 +1142,7 @@ energy::info fcn_gate_layout::calculate_energy() const noexcept return {slow_energy, fast_energy}; } -void fcn_gate_layout::write_layout(std::ostream& os, bool io_color, bool clk_color) const noexcept +void fcn_gate_layout::write_layout(std::ostream& os, const bool io_color, const bool clk_color) const noexcept { // empty layout if (!area()) @@ -1248,3 +1248,14 @@ void fcn_gate_layout::write_layout(std::ostream& os, bool io_color, bool clk_col // flush stream os << std::endl; } + +void fcn_gate_layout::clear_layout() noexcept +{ + fcn_layout::clear_layout(); + v_map.clear(); + e_map.clear(); + inp_dir_map.clear(); + out_dir_map.clear(); + edge_inp_dir_map.clear(); + edge_out_dir_map.clear(); +} diff --git a/src/topo/fcn_gate_layout.h b/src/topo/fcn_gate_layout.h index 9af931614..893c70557 100644 --- a/src/topo/fcn_gate_layout.h +++ b/src/topo/fcn_gate_layout.h @@ -14,9 +14,6 @@ #include #include - -#include - /** * Represents layouts of field-coupled nanocomputing (FCN) devices on a gate level abstraction. Inherits from fcn_layout * so it is a 3-dimensional grid-like structure as well. Faces are called tiles in a gate layout. Tiles can be occupied @@ -620,9 +617,13 @@ class fcn_gate_layout : public fcn_layout * * @return Number of gates in the layout. */ - auto gate_count() const noexcept + auto gate_count(const bool ignore_wire_vertices = false) const noexcept { - return v_map.size(); + if (ignore_wire_vertices) + return static_cast(std::count_if(v_map.left.begin(), v_map.left.end(), + [this](const auto& v){ return get_op(v.first) != operation::W; })); + else + return v_map.size(); } /** * Returns the number of tiles that are assigned with logic edges. Note that wire tiles in higher layers are counted @@ -630,18 +631,26 @@ class fcn_gate_layout : public fcn_layout * * @return Number of wires in the layout. */ - auto wire_count() const noexcept + auto wire_count(const bool count_wire_vertices = false) const noexcept { - return e_map.size(); + if (count_wire_vertices) + return static_cast(std::count_if(v_map.left.begin(), v_map.left.end(), + [this](const auto& v){ return get_op(v.first) == operation::W; })); + else + return e_map.size(); } /** * Returns the number of logic edges assigned to tiles above ground layer. * * @return Number of logic_network::edge assignments above ground layer. */ - auto crossing_count() const noexcept + auto crossing_count(const bool count_wire_vertices = false) const noexcept { - return std::count_if(e_map.cbegin(), e_map.cend(), [](auto& te){return te.first[Z] != GROUND;}); + if (count_wire_vertices) + return std::count_if(v_map.left.begin(), v_map.left.end(), + [this](const auto& v) { return v.first[Z] != GROUND && get_op(v.first) == operation::W; }); + else + return std::count_if(e_map.cbegin(), e_map.cend(), [](auto& te){return te.first[Z] != GROUND;}); } /** * Container to store statistical information about paths. @@ -757,7 +766,11 @@ class fcn_gate_layout : public fcn_layout * @param io_color Flag to indicate features like PI/PO should be printed with color escape. * @param clk_color Flag to indicate that clock zones should be printed with color escape. Can look weird. */ - void write_layout(std::ostream& os = std::cout, bool io_color = true, bool clk_color = false) const noexcept; + void write_layout(std::ostream& os = std::cout, const bool io_color = true, const bool clk_color = false) const noexcept; + /** + * Clears all maps and sets stored in the layout. + */ + void clear_layout() noexcept; private: /** diff --git a/src/topo/fcn_layout.cpp b/src/topo/fcn_layout.cpp index 7592b18ff..f7356486e 100644 --- a/src/topo/fcn_layout.cpp +++ b/src/topo/fcn_layout.cpp @@ -340,3 +340,11 @@ void fcn_layout::shrink_to_fit() noexcept auto bb = determine_bounding_box(); resize(fcn_dimension_xyz{std::max(bb.max_x + 1, coord_t{2}), std::max(bb.max_y + 1, coord_t{2}), z()}); // incorporate BGL bug } + +void fcn_layout::clear_layout() noexcept +{ + c_map.clear(); + pi_set.clear(); + po_set.clear(); + l_map.clear(); +} diff --git a/src/topo/fcn_layout.h b/src/topo/fcn_layout.h index a90b7453e..5bd3e63fb 100644 --- a/src/topo/fcn_layout.h +++ b/src/topo/fcn_layout.h @@ -35,6 +35,10 @@ using layer_t = uint8_t; * Constant referring to ground layer. */ constexpr const layer_t GROUND = 0u; +/** + * Constant referring to crossing layer. + */ +constexpr const layer_t CROSSING = 1u; /** * Constant referring to x-dimension value of faces. */ @@ -654,6 +658,10 @@ class fcn_layout : protected grid_graph<3> * all maps would not work properly anymore. */ void shrink_to_fit() noexcept; + /** + * Clears all maps and sets stored in the layout. + */ + void clear_layout() noexcept; protected: /** diff --git a/src/topo/grid_topology.h b/src/topo/grid_topology.h index 426d8408e..63aeda187 100644 --- a/src/topo/grid_topology.h +++ b/src/topo/grid_topology.h @@ -121,7 +121,7 @@ class grid_topology : public boost::convex_topology } private: - uint64_t edge_length; + int64_t edge_length; std::shared_ptr rand = nullptr; std::shared_ptr distr = nullptr; }; diff --git a/src/topo/logic_network.cpp b/src/topo/logic_network.cpp index 6e8673b75..ac03dbc8d 100644 --- a/src/topo/logic_network.cpp +++ b/src/topo/logic_network.cpp @@ -69,6 +69,11 @@ logic_network::vertex logic_network::create_logic_vertex(const operation o) noex void logic_network::remove_logic_vertex(const vertex v) noexcept { decrement_op_counter(get_op(v)); + + pi_set.erase(v); + po_set.erase(v); + io_port_map.left.erase(v); + remove_vertex(v); } @@ -81,12 +86,21 @@ logic_network::vertex logic_network::create_pi(const std::string& name) noexcept return v; } -void logic_network::create_po(const vertex a, const std::string& name) noexcept +logic_network::vertex logic_network::create_po(const std::string& name) noexcept { auto v = create_logic_vertex(operation::PO); - add_edge(a, v); po_set.emplace(v); io_port_map.insert(port_map::value_type(v, name)); + + return v; +} + +logic_network::vertex logic_network::create_po(const vertex a, const std::string& name) noexcept +{ + auto v = create_po(name); + add_edge(a, v); + + return v; } logic_network::vertex logic_network::create_not(const vertex a) noexcept @@ -411,7 +425,7 @@ void logic_network::substitute_fan_outs(const std::size_t degree, const substitu } } -void logic_network::write_dot(std::ostream& os) noexcept +void logic_network::write_dot(std::ostream& os) const noexcept { os << "digraph G {\n"; @@ -438,7 +452,7 @@ void logic_network::write_dot(std::ostream& os) noexcept os << "}\n" << std::endl; } -void logic_network::write_simple_dot(std::ostream& os) noexcept +void logic_network::write_simple_dot(std::ostream& os) const noexcept { os << "digraph G {\nnode[label=\"\",style=filled,shape=circle];\n"; @@ -474,6 +488,14 @@ void logic_network::write_simple_dot(std::ostream& os) noexcept os << "}\n" << std::endl; } +void logic_network::clear_network() noexcept +{ + clear_graph(); + + for (auto i = 0u; i < OP_COUNT; ++i) + operation_counter[i] = 0; +} + void logic_network::increment_op_counter(const operation o) noexcept { ++operation_counter[o]; @@ -486,7 +508,7 @@ void logic_network::decrement_op_counter(const operation o) noexcept void logic_network::initialize_network_from_logic() noexcept { - clear_graph(); + clear_network(); mockturtle::topo_view topo_mig(mig); std::unordered_map::node, vertex> node_to_vertex{}; diff --git a/src/topo/logic_network.h b/src/topo/logic_network.h index 736cf0eb1..146116eb0 100644 --- a/src/topo/logic_network.h +++ b/src/topo/logic_network.h @@ -151,6 +151,8 @@ class logic_network : protected bidirectional_graph