Commit 32f040fa authored by Alexandre GBAGUIDI AISSE's avatar Alexandre GBAGUIDI AISSE
Browse files

spot: Add Picosat to Spot library & Update satsolver class

* Makefile.am: Add picosat to subdirs.
* configure.ac: Add picosat/Makefile to AC_CONFIG_FILES.
* README: Add picosat/ in the list of directories.
* debian/copyright: Add picosat licence and details.
* picosat/Makefile.am: Implement Makefile.am in picosat directory.
* spot/Makefile.am: Tell the compiler to add libpico.la into libspot.la.
* picosat/LICENSE: Add picosat licence.
* picosat/NEWS: Add picosat NEWS.
* picosat/VERSION: Add picosat VERSION.
* picosat/picosat.c: Add picosat c file.
* picosat/picosat.h: Add picosat header file.
* spot/misc/satsolver.cc: Update functions.
* spot/misc/satsolver.hh: Add documentation, clean code, change
some functions visibility and separate templates functions.
* spot/twaalgos/dtbasat.cc: Update dtba_to_sat function.
* spot/twaalgos/dtwasat.cc: Update dtwa_to_sat function.
parent 596bdec9
......@@ -32,7 +32,8 @@ if USE_PYTHON
PYTHON_SUBDIR = python
endif
SUBDIRS = buddy lib ltdl spot bin tests $(PYTHON_SUBDIR) doc $(NEVER_SUBDIRS)
SUBDIRS = picosat buddy lib ltdl spot bin tests $(PYTHON_SUBDIR) doc \
$(NEVER_SUBDIRS)
UTF8 = utf8/doc/ReleaseNotes utf8/doc/utf8cpp.html utf8/utf8.h \
utf8/utf8/checked.h utf8/utf8/core.h utf8/utf8/unchecked.h
......
......@@ -218,6 +218,7 @@ ltdl/ Libtool's portable dlopen() wrapper library.
lib/ Gnulib's portability modules.
utf8/ Nemanja Trifunovic's utf-8 routines.
elisp/ Related emacs modes, used for building the documentation.
picosat/ A distribution of PicoSAT 965 (a satsolver library).
Build-system stuff
------------------
......
......@@ -261,6 +261,7 @@ AC_CONFIG_FILES([
doc/org/init.el
elisp/Makefile
lib/Makefile
picosat/Makefile
spot/graph/Makefile
spot/kripke/Makefile
spot/ltsmin/Makefile
......
......@@ -162,3 +162,24 @@ License: GPL-3+
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
Files: picosat/*
Copyright: 2006 - 2015, Armin Biere, Johannes Kepler University.
License: MIT style
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.
Copyright (c) 2006 - 2014, Armin Biere, Johannes Kepler University.
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.
noinst_LTLIBRARIES = libpico.la
libpico_la_SOURCES = picosat.c picosat.h
# Force -DNDEBUG regardless of the Spot settings
libpico_la_CPPFLAGS = -DNDEBUG -DNGETRUSAGE
EXTRA_DIST = LICENSE NEWS VERSION
news for release 965 since 959
------------------------------
* ADC code works again (spotted by Himanshu Jain)
* include into R projects (with Christoph Muessel) (--rcode)
* fixed 'undefined' + 'ptrdiff_' issues (thanks to Christoph Muessel)
* added 'picosat_set_interrupt' and '-a <alarm>' command line option
* fixed various issues pointed out by Stefan Hengelein:
- fixed incremental usage of 'picosat_adjust'
- added CPP fixes (STATS, NO_BINARY_CLAUSE versus TRACE mix-ups)
- removed redundant explicit set to zero on reset
* fixed various usage bugs with 'picomus' (thanks to Stefan Hengelein)
* removed '-fno-strict-aliasing' (thanks to Jerry James)
news for release 959 since 953
------------------------------
* fixed header comments
* fixed minor compilation issues
* fixed unitialized memory access problem for 'picosat_deref_partial'
and another issue with partial models
* added 'picosat_add_arg' and 'picosat_add_lits'
* '--plain' and 'picosat_set_plain' to disable failed literal probing
* new '#define PICOSAT_REENTRANT_API' in 'picosat.h'
* added manager so no more global variables
(allows multiple instances, requires manager object)
news for release 951 since 941
------------------------------
* cleaned up code (based on comments by Donald Knuth)
* lreduce=O(conflicts^.5)
* added 'picosat_visits' and 'picosat_decisions'
* added '--partial' command line option
* added 'picosat_deref_partial' and 'picosat_save_original_clauses'
* added 'picomcs' as example for MSS computation
news for release 941 since 936
------------------------------
* added 'picogcnf'
* added All-SAT mode ('--all' command line option)
* statistics include time spent in failed literal preprocessing (probing)
* 'picosat_failed_context' for 'push & pop'
(and tested failed assumptions for 'push & pop')
* 'picosat_simplify' for forced garbage collection
* undefined NFL, defined NADC (= failed literals on, ADC's off)
* 'picosat_push' and 'picosat_pop' (beta version)
* fixed some issues related to binary clause handling and
generating list of failed assumptions
news for release 936 since 935
------------------------------
* simple minimal unsatisfiable core (MUS) extractor 'picomus'
(example for using 'picosat_mus_assumptions' and 'picosat_coreclause')
* added 'picosat_mus_assumptions'
* added 'picosat_{set_}propagations'
* new 'int' return value for 'picosat_enable_trace_generation' to
check for trace code being compiled
news for release 935 since 926
------------------------------
* added 'picosat_failed_assumptions' (plural)
* new '-A <failedlits>' command line option
* fixed failed assumption issues
* added 'picosat_remove_learned'
* added 'picosat_reset_{phases,scores}'
* added 'picosat_set_less_important_lit'
* added 'picosat_res'
news for release 926 since 846
------------------------------
* random initial phase (API of 'picosat_set_default_phase' changed)
* fixed accumulative failed assumption (multiple times)
* fixed missing original clause in core generation with assumptions
* fixed debugging code for memory allocation
* shared library in addition to static library
* removed potential UNKNOWN result without decision limit
* added picosat_set_more_important_lit
* added picosat_coreclause
* propagation of binary clauses until completion
* fixed API usage 'assume;sat;sat'
* literals move to front (LMTF) during traversal of visited clauses
* switched from inner/outer to Luby style restart scheduling
* less agressive reduce schedule
* replaced watched literals with head and tail pointers
* add 'picosat_failed_assumption', which allows to avoid tracing and core
generation, if one is only interested in assumptions in the core
* fixed a BUG in the generic iterator code of clauses
(should rarely happen unless you use a very sophisticated malloc lib)
news for release 846 since 632
------------------------------
* cleaned up assumption handling (actually removed buggy optimization)
* incremental core generation
* experimental 'all different constraint' handling as in our FMCAD'08 paper
* new API calls:
- picosat_add_ado_lit (add all different object literal)
- picosat_deref_top_level (deref top level assignment)
- picosat_changed (check whether extension was possible)
- picosat_measure_all_calls (per default do not measure adding time)
- picosat_set_prefix (set prefix for messages)
* 64 bit port (and compilation options)
* optional NVSIDS visualization code
* resource controlled failed literal implementation
* disconnect long clauses satisfied at lower decision level
* controlling restarts
This diff is collapsed.
This diff is collapsed.
......@@ -42,7 +42,8 @@ libspot_la_LIBADD = \
tl/libtl.la \
twaalgos/libtwaalgos.la \
twa/libtwa.la \
../lib/libgnu.la
../lib/libgnu.la \
../picosat/libpico.la
# Dummy C++ source to cause C++ linking.
nodist_EXTRA_libspot_la_SOURCES = _.cc
......
......@@ -23,66 +23,13 @@
#include <sstream>
#include <stdexcept>
#include <spot/misc/satsolver.hh>
#include <picosat/picosat.h>
#include <fstream>
#include <limits>
#include <sys/wait.h>
namespace spot
{
namespace
{
struct satsolver_command: formater
{
const char* satsolver;
satsolver_command()
{
satsolver = getenv("SPOT_SATSOLVER");
if (!satsolver)
{
satsolver = "glucose -verb=0 -model %I >%O";
return;
}
prime(satsolver);
if (!has('I'))
throw std::runtime_error("SPOT_SATSOLVER should contain %I to "
"indicate how to use the input filename.");
if (!has('O'))
throw std::runtime_error("SPOT_SATSOLVER should contain %O to "
"indicate how to use the output filename.");
}
int
run(printable* in, printable* out)
{
declare('I', in);
declare('O', out);
std::ostringstream s;
format(s, satsolver);
int res = system(s.str().c_str());
if (res < 0 || (WIFEXITED(res) && WEXITSTATUS(res) == 127))
{
s << ": failed to execute";
throw std::runtime_error(s.str());
}
// For POSIX shells, "The exit status of a command that
// terminated because it received a signal shall be reported
// as greater than 128."
if (WIFEXITED(res) && WEXITSTATUS(res) >= 128)
{
s << ": terminated by signal";
throw std::runtime_error(s.str());
}
if (WIFSIGNALED(res))
{
s << ": terminated by signal " << WTERMSIG(res);
throw std::runtime_error(s.str());
}
return res;
}
};
}
satsolver::solution
satsolver_get_solution(const char* filename)
{
......@@ -122,17 +69,35 @@ namespace spot
return sol;
}
// In other functions, command_given() won't be called anymore as it is more
// easy to check if psat_ was initialized or not.
satsolver::satsolver()
: cnf_tmp_(nullptr), cnf_stream_(nullptr)
: cnf_tmp_(nullptr), cnf_stream_(nullptr), nclauses_(0), nvars_(0),
psat_(nullptr)
{
start();
if (cmd_.command_given())
{
start();
}
else
{
psat_ = picosat_init();
picosat_set_seed(psat_, 0);
}
}
satsolver::~satsolver()
{
delete cnf_tmp_;
delete cnf_stream_;
delete nclauses_;
if (psat_)
{
picosat_reset(psat_);
psat_ = nullptr;
}
else
{
delete cnf_tmp_;
delete cnf_stream_;
}
}
void satsolver::start()
......@@ -140,70 +105,192 @@ namespace spot
cnf_tmp_ = create_tmpfile("sat-", ".cnf");
cnf_stream_ = new std::ofstream(cnf_tmp_->name(), std::ios_base::trunc);
cnf_stream_->exceptions(std::ofstream::failbit | std::ofstream::badbit);
nclauses_ = new clause_counter();
// Add empty line for the header
*cnf_stream_ << " \n";
}
// Must be called only when SPOT_SATSOLVER is given
void satsolver::end_clause()
{
*cnf_stream_ << '\n';
*nclauses_ += 1;
nclauses_ += 1;
if (nclauses_ < 0)
throw std::runtime_error("too many SAT clauses (more than INT_MAX)");
}
void satsolver::adjust_nvars(int nvars)
{
if (nvars < 0)
throw std::runtime_error("variable number must be at least 0");
if (psat_)
{
picosat_adjust(psat_, nvars);
}
else
{
if (nvars < nvars_)
{
throw std::runtime_error(
"wrong number of variables, a bigger one was already added");
}
nvars_ = nvars;
}
}
void satsolver::add(std::initializer_list<int> values)
{
for (auto& v : values)
{
*cnf_stream_ << v << ' ';
if (!v) // ..., 0)
end_clause();
if (psat_)
{
picosat_add(psat_, v);
}
else
{
*cnf_stream_ << v << ' ';
if (!v) // ..., 0)
end_clause();
if (nvars_ < v)
nvars_ = v;
}
}
}
void satsolver::add(int v)
{
*cnf_stream_ << v << ' ';
if (!v) // 0
end_clause();
if (psat_)
{
picosat_add(psat_, v);
}
else
{
*cnf_stream_ << v << ' ';
if (!v) // 0
end_clause();
if (v && nvars_ < v)
nvars_ = v;
}
}
int satsolver::get_nb_clauses() const
{
return nclauses_->nb_clauses();
if (psat_)
return picosat_added_original_clauses(psat_);
return nclauses_;
}
std::pair<int, int> satsolver::stats(int nvars)
int satsolver::get_nb_vars() const
{
int nclaus = nclauses_->nb_clauses();
cnf_stream_->seekp(0);
*cnf_stream_ << "p cnf " << nvars << ' ' << nclaus;
return std::make_pair(nvars, nclaus);
if (psat_)
return picosat_variables(psat_);
return nvars_;
}
std::pair<int, int> satsolver::stats()
{
*cnf_stream_ << "p cnf 1 2\n-1 0\n1 0\n";
return std::make_pair(1, 2);
return std::make_pair(get_nb_vars(), get_nb_clauses());
}
satsolver::solution
satsolver::picosat_get_solution(int res)
{
satsolver::solution sol;
if (res == PICOSAT_SATISFIABLE)
{
int nvars = get_nb_vars();
for (int lit = 1; lit <= nvars; ++lit)
{
if (picosat_deref(psat_, lit) > 0)
sol.push_back(lit);
else
sol.push_back(-lit);
}
}
return sol;
}
satsolver::solution_pair
satsolver::get_solution()
{
delete cnf_stream_; // Close the file.
cnf_stream_ = nullptr;
temporary_file* output = create_tmpfile("sat-", ".out");
solution_pair p;
if (psat_)
{
p.first = 0; // A subprocess was not executed so nothing failed.
int res = picosat_sat(psat_, -1); // -1: no limit (number of decisions).
p.second = picosat_get_solution(res);
}
else
{
// Update header
cnf_stream_->seekp(0);
*cnf_stream_ << "p cnf " << get_nb_vars() << ' ' << get_nb_clauses();
cnf_stream_->seekp(0, std::ios_base::end);
if (!*cnf_stream_)
throw std::runtime_error("Failed to update cnf header");
// Make this static, so the SPOT_SATSOLVER lookup is done only on
// the first call to run_sat().
static satsolver_command cmd;
p.first = cmd.run(cnf_tmp_, output);
p.second = satsolver_get_solution(output->name());
delete output;
temporary_file* output = create_tmpfile("sat-", ".out");
p.first = cmd_.run(cnf_tmp_, output);
p.second = satsolver_get_solution(output->name());
delete output;
}
return p;
}
satsolver_command::satsolver_command() : satsolver(nullptr)
{
satsolver = getenv("SPOT_SATSOLVER");
if (!satsolver)
return;
prime(satsolver);
if (!has('I'))
throw std::runtime_error("SPOT_SATSOLVER should contain %I to "
"indicate how to use the input filename.");
if (!has('O'))
throw std::runtime_error("SPOT_SATSOLVER should contain %O to "
"indicate how to use the output filename.");
}
bool
satsolver_command::command_given()
{
return satsolver != nullptr;
}
int
satsolver_command::run(printable* in, printable* out)
{
declare('I', in);
declare('O', out);
std::ostringstream s;
format(s, satsolver);
int res = system(s.str().c_str());
if (res < 0 || (WIFEXITED(res) && WEXITSTATUS(res) == 127))
{
s << ": failed to execute";
throw std::runtime_error(s.str());
}
// For POSIX shells, "The exit status of a command that
// terminated because it received a signal shall be reported
// as greater than 128."
if (WIFEXITED(res) && WEXITSTATUS(res) >= 128)
{
s << ": terminated by signal";
throw std::runtime_error(s.str());
}
if (WIFSIGNALED(res))
{
s << ": terminated by signal " << WTERMSIG(res);
throw std::runtime_error(s.str());
}
return res;
}
}
......@@ -26,68 +26,58 @@
#include <iosfwd>
#include <initializer_list>
struct PicoSAT; // forward
namespace spot
{
class printable;
class clause_counter
/// \brief Interface with a given sat solver.
///
/// When created, it checks if SPOT_SATSOLVER env var is set. If so,
/// its value is parsed and saved internally. The env variable musb be set
/// like this: "<satsolver> [its_options] %I > %O"
/// where %I and %O are replaced by input and output files.
///
/// The run method permits of course to run the given sat solver.
class satsolver_command: formater
{
private:
int count_;
const char* satsolver;
public:
clause_counter()
: count_(0)
{
}
void check() const
{
if (count_ < 0)
throw std::runtime_error("too many SAT clauses (more than INT_MAX)");
}
satsolver_command();
clause_counter& operator++()
{