swarming: add support everywhere
Swarming implies that a single instance of the kripke structure (or product) will be explored by diffrent threads with their own exploration order. Most of the modification aims to have a thread safe kripke structure. * spot/kripke/kripke.hh, spot/ltsmin/ltsmin.cc, spot/ltsmin/ltsmin.hh, spot/mc/ec.hh, spot/mc/intersect.hh, spot/mc/reachability.hh, spot/misc/hash.hh, spot/twacube/twacube.hh, tests/core/twacube.test, tests/ltsmin/modelcheck.cc: here.
This commit is contained in:
parent
ae1a3601e6
commit
72948661e9
10 changed files with 248 additions and 119 deletions
|
|
@ -38,18 +38,19 @@ namespace spot
|
||||||
public std::enable_shared_from_this<kripkecube<State, SuccIterator>>
|
public std::enable_shared_from_this<kripkecube<State, SuccIterator>>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// \brief Returns the initial State of the System
|
/// \brief Returns the initial State of the System. The \a tid parameter
|
||||||
State initial();
|
/// is used internally for sharing this structure among threads.
|
||||||
|
State initial(unsigned tid);
|
||||||
|
|
||||||
/// \brief Provides a string representation of the parameter state
|
/// \brief Provides a string representation of the parameter state
|
||||||
std::string to_string(const State) const;
|
std::string to_string(const State, unsigned tid) const;
|
||||||
|
|
||||||
/// \brief Returns an iterator over the successors of the parameter state.
|
/// \brief Returns an iterator over the successors of the parameter state.
|
||||||
SuccIterator* succ(const State);
|
SuccIterator* succ(const State, unsigned tid);
|
||||||
|
|
||||||
/// \brief Allocation and deallocation of iterator is costly. This
|
/// \brief Allocation and deallocation of iterator is costly. This
|
||||||
/// method allows to reuse old iterators.
|
/// method allows to reuse old iterators.
|
||||||
void recycle(SuccIterator*);
|
void recycle(SuccIterator*, unsigned tid);
|
||||||
|
|
||||||
/// \brief This method allow to deallocate a given state.
|
/// \brief This method allow to deallocate a given state.
|
||||||
const std::vector<std::string> get_ap();
|
const std::vector<std::string> get_ap();
|
||||||
|
|
@ -62,11 +63,11 @@ namespace spot
|
||||||
bool is_a_kripkecube(kripkecube<State, SuccIter>&)
|
bool is_a_kripkecube(kripkecube<State, SuccIter>&)
|
||||||
{
|
{
|
||||||
// Hardly waiting C++17 concepts...
|
// Hardly waiting C++17 concepts...
|
||||||
State (kripkecube<State, SuccIter>::*test_initial)() =
|
State (kripkecube<State, SuccIter>::*test_initial)(unsigned) =
|
||||||
&kripkecube<State, SuccIter>::initial;
|
&kripkecube<State, SuccIter>::initial;
|
||||||
std::string (kripkecube<State, SuccIter>::*test_to_string)
|
std::string (kripkecube<State, SuccIter>::*test_to_string)
|
||||||
(const State) const = &kripkecube<State, SuccIter>::to_string;
|
(const State, unsigned) const = &kripkecube<State, SuccIter>::to_string;
|
||||||
auto (kripkecube<State, SuccIter>::*test_recycle)(SuccIter*) =
|
auto (kripkecube<State, SuccIter>::*test_recycle)(SuccIter*, unsigned) =
|
||||||
&kripkecube<State, SuccIter>::recycle;
|
&kripkecube<State, SuccIter>::recycle;
|
||||||
const std::vector<std::string>
|
const std::vector<std::string>
|
||||||
(kripkecube<State, SuccIter>::*test_get_ap)() =
|
(kripkecube<State, SuccIter>::*test_get_ap)() =
|
||||||
|
|
|
||||||
|
|
@ -1260,8 +1260,9 @@ namespace spot
|
||||||
cube cond,
|
cube cond,
|
||||||
bool compress,
|
bool compress,
|
||||||
bool selfloopize,
|
bool selfloopize,
|
||||||
cubeset& cubeset, int dead_idx)
|
cubeset& cubeset,
|
||||||
: current_(0), cond_(cond)
|
int dead_idx, unsigned tid)
|
||||||
|
: current_(0), cond_(cond), tid_(tid)
|
||||||
{
|
{
|
||||||
successors_.reserve(10);
|
successors_.reserve(10);
|
||||||
inner.manager = &manager;
|
inner.manager = &manager;
|
||||||
|
|
@ -1307,8 +1308,9 @@ namespace spot
|
||||||
cube cond,
|
cube cond,
|
||||||
bool compress,
|
bool compress,
|
||||||
bool selfloopize,
|
bool selfloopize,
|
||||||
cubeset& cubeset, int dead_idx)
|
cubeset& cubeset, int dead_idx, unsigned tid)
|
||||||
{
|
{
|
||||||
|
tid_ = tid;
|
||||||
cond_ = cond;
|
cond_ = cond;
|
||||||
current_ = 0;
|
current_ = 0;
|
||||||
// Constant time since int* is is_trivially_destructible
|
// Constant time since int* is is_trivially_destructible
|
||||||
|
|
@ -1365,7 +1367,10 @@ namespace spot
|
||||||
|
|
||||||
cspins_state state() const
|
cspins_state state() const
|
||||||
{
|
{
|
||||||
return successors_[current_];
|
if (SPOT_UNLIKELY(!tid_))
|
||||||
|
return successors_[current_];
|
||||||
|
return successors_[(((current_+1)*primes[tid_])
|
||||||
|
% ((int)successors_.size()))];
|
||||||
}
|
}
|
||||||
|
|
||||||
cube condition() const
|
cube condition() const
|
||||||
|
|
@ -1377,6 +1382,7 @@ namespace spot
|
||||||
std::vector<cspins_state> successors_;
|
std::vector<cspins_state> successors_;
|
||||||
unsigned int current_;
|
unsigned int current_;
|
||||||
cube cond_;
|
cube cond_;
|
||||||
|
unsigned tid_;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -1409,45 +1415,68 @@ namespace spot
|
||||||
public:
|
public:
|
||||||
kripkecube(spins_interface_ptr sip, bool compress,
|
kripkecube(spins_interface_ptr sip, bool compress,
|
||||||
std::vector<std::string> visible_aps,
|
std::vector<std::string> visible_aps,
|
||||||
bool selfloopize, std::string dead_prop)
|
bool selfloopize, std::string dead_prop,
|
||||||
: sip_(sip), d_(sip.get()), manager_(d_->get_state_size(), compress),
|
unsigned int nb_threads)
|
||||||
compress_(compress), cubeset_(visible_aps.size()),
|
: sip_(sip), d_(sip.get()),
|
||||||
selfloopize_(selfloopize), aps_(visible_aps)
|
compress_(compress), cubeset_(visible_aps.size()),
|
||||||
|
selfloopize_(selfloopize), aps_(visible_aps), nb_threads_(nb_threads)
|
||||||
{
|
{
|
||||||
map_.setSize(2000000);
|
map_.setSize(2000000);
|
||||||
recycle_.reserve(2000000);
|
manager_ = static_cast<cspins_state_manager*>
|
||||||
inner_.compressed_ = new int[d_->get_state_size() * 2];
|
(::operator new(sizeof(cspins_state_manager) * nb_threads));
|
||||||
inner_.uncompressed_ = new int[d_->get_state_size()+30];
|
inner_ = new inner_callback_parameters[nb_threads_];
|
||||||
|
for (unsigned i = 0; i < nb_threads_; ++i)
|
||||||
|
{
|
||||||
|
recycle_.push_back(std::vector<cspins_iterator*>());
|
||||||
|
recycle_.back().reserve(2000000);
|
||||||
|
new (&manager_[i])
|
||||||
|
cspins_state_manager(d_->get_state_size(), compress);
|
||||||
|
inner_[i].compressed_ = new int[d_->get_state_size() * 2];
|
||||||
|
inner_[i].uncompressed_ = new int[d_->get_state_size()+30];
|
||||||
|
}
|
||||||
dead_idx_ = -1;
|
dead_idx_ = -1;
|
||||||
match_aps(visible_aps, dead_prop);
|
match_aps(visible_aps, dead_prop);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~kripkecube()
|
~kripkecube()
|
||||||
{
|
{
|
||||||
for (auto i: recycle_)
|
for (auto i: recycle_)
|
||||||
{
|
{
|
||||||
cubeset_.release(i->condition());
|
for (auto j: i)
|
||||||
delete i;
|
{
|
||||||
|
cubeset_.release(j->condition());
|
||||||
|
delete j;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete inner_.compressed_;
|
|
||||||
delete inner_.uncompressed_;
|
for (unsigned i = 0; i < nb_threads_; ++i)
|
||||||
|
{
|
||||||
|
manager_[i].~cspins_state_manager();
|
||||||
|
delete inner_[i].compressed_;
|
||||||
|
delete inner_[i].uncompressed_;
|
||||||
|
}
|
||||||
|
delete[] inner_;
|
||||||
}
|
}
|
||||||
|
|
||||||
cspins_state initial()
|
cspins_state initial(unsigned tid)
|
||||||
{
|
{
|
||||||
d_->get_initial_state(inner_.uncompressed_);
|
d_->get_initial_state(inner_[tid].uncompressed_);
|
||||||
return manager_.alloc_setup(inner_.uncompressed_, inner_.compressed_,
|
return manager_[tid].alloc_setup(inner_[tid].uncompressed_,
|
||||||
manager_.size() * 2);
|
inner_[tid].compressed_,
|
||||||
|
manager_[tid].size() * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string(const cspins_state s) const
|
std::string to_string(const cspins_state s, unsigned tid = 0) const
|
||||||
{
|
{
|
||||||
std::string res = "";
|
std::string res = "";
|
||||||
cspins_state out = manager_.unbox_state(s);
|
cspins_state out = manager_[tid].unbox_state(s);
|
||||||
cspins_state ref = out;
|
cspins_state ref = out;
|
||||||
if (compress_)
|
if (compress_)
|
||||||
{
|
{
|
||||||
manager_.decompress(s, inner_.uncompressed_, manager_.size());
|
manager_[tid].decompress(s, inner_[tid].uncompressed_,
|
||||||
ref = inner_.uncompressed_;
|
manager_[tid].size());
|
||||||
|
ref = inner_[tid].uncompressed_;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < d_->get_state_size(); ++i)
|
for (int i = 0; i < d_->get_state_size(); ++i)
|
||||||
res += (d_->get_state_variable_name(i)) +
|
res += (d_->get_state_variable_name(i)) +
|
||||||
|
|
@ -1456,28 +1485,28 @@ namespace spot
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
cspins_iterator* succ(const cspins_state s)
|
cspins_iterator* succ(const cspins_state s, unsigned tid)
|
||||||
{
|
{
|
||||||
if (SPOT_LIKELY(!recycle_.empty()))
|
if (SPOT_LIKELY(!recycle_[tid].empty()))
|
||||||
{
|
{
|
||||||
auto tmp = recycle_.back();
|
auto tmp = recycle_[tid].back();
|
||||||
recycle_.pop_back();
|
recycle_[tid].pop_back();
|
||||||
compute_condition(tmp->condition(), s);
|
compute_condition(tmp->condition(), s, tid);
|
||||||
tmp->recycle(s, d_, manager_, map_, inner_, -1,
|
tmp->recycle(s, d_, manager_[tid], map_, inner_[tid], -1,
|
||||||
tmp->condition(), compress_, selfloopize_,
|
tmp->condition(), compress_, selfloopize_,
|
||||||
cubeset_, dead_idx_);
|
cubeset_, dead_idx_, tid);
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
cube cond = cubeset_.alloc();
|
cube cond = cubeset_.alloc();
|
||||||
compute_condition(cond, s);
|
compute_condition(cond, s, tid);
|
||||||
return new cspins_iterator(s, d_, manager_, map_, inner_,
|
return new cspins_iterator(s, d_, manager_[tid], map_, inner_[tid],
|
||||||
-1, cond, compress_, selfloopize_,
|
-1, cond, compress_, selfloopize_,
|
||||||
cubeset_, dead_idx_);
|
cubeset_, dead_idx_, tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void recycle(cspins_iterator* it)
|
void recycle(cspins_iterator* it, unsigned tid)
|
||||||
{
|
{
|
||||||
recycle_.push_back(it);
|
recycle_[tid].push_back(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> get_ap()
|
const std::vector<std::string> get_ap()
|
||||||
|
|
@ -1485,6 +1514,11 @@ namespace spot
|
||||||
return aps_;
|
return aps_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned get_threads()
|
||||||
|
{
|
||||||
|
return nb_threads_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The two followings functions are too big to be inlined in
|
// The two followings functions are too big to be inlined in
|
||||||
// this class. See below for more details
|
// this class. See below for more details
|
||||||
|
|
@ -1495,34 +1529,36 @@ namespace spot
|
||||||
|
|
||||||
/// Compute the cube associated to each state. The cube
|
/// Compute the cube associated to each state. The cube
|
||||||
/// will then be given to all iterators.
|
/// will then be given to all iterators.
|
||||||
void compute_condition(cube c, cspins_state s);
|
void compute_condition(cube c, cspins_state s, unsigned tid = 0);
|
||||||
|
|
||||||
|
|
||||||
spins_interface_ptr sip_;
|
spins_interface_ptr sip_;
|
||||||
const spot::spins_interface* d_; // To avoid numerous sip_.get()
|
const spot::spins_interface* d_; // To avoid numerous sip_.get()
|
||||||
cspins_state_manager manager_; // FIXME One per thread!
|
cspins_state_manager* manager_;
|
||||||
bool compress_;
|
bool compress_;
|
||||||
std::vector<cspins_iterator*> recycle_;
|
std::vector<std::vector<cspins_iterator*>> recycle_;
|
||||||
inner_callback_parameters inner_; // FIXME Should be an array for threads.
|
inner_callback_parameters* inner_;
|
||||||
cubeset cubeset_;
|
cubeset cubeset_;
|
||||||
bool selfloopize_;
|
bool selfloopize_;
|
||||||
int dead_idx_;
|
int dead_idx_;
|
||||||
std::vector<std::string> aps_;
|
std::vector<std::string> aps_;
|
||||||
cspins_state_map map_;
|
cspins_state_map map_;
|
||||||
|
unsigned int nb_threads_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
kripkecube<cspins_state,
|
kripkecube<cspins_state, cspins_iterator>::compute_condition
|
||||||
cspins_iterator>::compute_condition(cube c, cspins_state s)
|
(cube c, cspins_state s, unsigned tid)
|
||||||
{
|
{
|
||||||
int i = -1;
|
int i = -1;
|
||||||
int *vars = manager_.unbox_state(s);
|
int *vars = manager_[tid].unbox_state(s);
|
||||||
|
|
||||||
// State is compressed, uncompress it
|
// State is compressed, uncompress it
|
||||||
if (compress_)
|
if (compress_)
|
||||||
{
|
{
|
||||||
manager_.decompress(s, inner_.uncompressed_+2, manager_.size());
|
manager_[tid].decompress(s, inner_[tid].uncompressed_+2,
|
||||||
vars = inner_.uncompressed_ + 2;
|
manager_[tid].size());
|
||||||
|
vars = inner_[tid].uncompressed_ + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& ap: pset_)
|
for (auto& ap: pset_)
|
||||||
|
|
@ -1943,7 +1979,8 @@ namespace spot
|
||||||
|
|
||||||
ltsmin_kripkecube_ptr
|
ltsmin_kripkecube_ptr
|
||||||
ltsmin_model::kripkecube(std::vector<std::string> to_observe,
|
ltsmin_model::kripkecube(std::vector<std::string> to_observe,
|
||||||
const formula dead, int compress) const
|
const formula dead, int compress,
|
||||||
|
unsigned int nb_threads) const
|
||||||
{
|
{
|
||||||
// Register the "dead" proposition. There are three cases to
|
// Register the "dead" proposition. There are three cases to
|
||||||
// consider:
|
// consider:
|
||||||
|
|
@ -1973,7 +2010,7 @@ namespace spot
|
||||||
// Finally build the system.
|
// Finally build the system.
|
||||||
return std::make_shared<spot::kripkecube<spot::cspins_state,
|
return std::make_shared<spot::kripkecube<spot::cspins_state,
|
||||||
spot::cspins_iterator>>
|
spot::cspins_iterator>>
|
||||||
(iface, compress, to_observe, selfloopize, dead_ap);
|
(iface, compress, to_observe, selfloopize, dead_ap, nb_threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
kripke_ptr
|
kripke_ptr
|
||||||
|
|
@ -2008,7 +2045,7 @@ namespace spot
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<bool, std::string, istats>
|
std::tuple<bool, std::string, std::vector<istats>>
|
||||||
ltsmin_model::modelcheck(ltsmin_kripkecube_ptr sys,
|
ltsmin_model::modelcheck(ltsmin_kripkecube_ptr sys,
|
||||||
spot::twacube_ptr twa, bool compute_ctrx)
|
spot::twacube_ptr twa, bool compute_ctrx)
|
||||||
{
|
{
|
||||||
|
|
@ -2018,13 +2055,33 @@ namespace spot
|
||||||
for (unsigned int i = 0; i < sys->get_ap().size(); ++i)
|
for (unsigned int i = 0; i < sys->get_ap().size(); ++i)
|
||||||
assert(sys->get_ap()[i].compare(twa->get_ap()[i]) == 0);
|
assert(sys->get_ap()[i].compare(twa->get_ap()[i]) == 0);
|
||||||
|
|
||||||
ec_renault13lpar<cspins_state, cspins_iterator,
|
bool stop = false;
|
||||||
cspins_state_hash, cspins_state_equal> ec(*sys, twa);
|
std::vector<ec_renault13lpar<cspins_state, cspins_iterator,
|
||||||
bool has_ctrx = ec.run();
|
cspins_state_hash, cspins_state_equal>> ecs;
|
||||||
|
for (unsigned i = 0; i < sys->get_threads(); ++i)
|
||||||
|
ecs.push_back({*sys, twa, i, stop});
|
||||||
|
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
for (unsigned i = 0; i < sys->get_threads(); ++i)
|
||||||
|
threads.push_back
|
||||||
|
(std::thread(&ec_renault13lpar<cspins_state, cspins_iterator,
|
||||||
|
cspins_state_hash, cspins_state_equal>::run, &ecs[i]));
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < sys->get_threads(); ++i)
|
||||||
|
threads[i].join();
|
||||||
|
|
||||||
|
bool has_ctrx = false;
|
||||||
std::string trace = "";
|
std::string trace = "";
|
||||||
if (has_ctrx && compute_ctrx)
|
std::vector<istats> stats;
|
||||||
trace = ec.trace();
|
for (unsigned i = 0; i < sys->get_threads(); ++i)
|
||||||
return std::make_tuple(has_ctrx, trace, ec.stats());
|
{
|
||||||
|
has_ctrx |= ecs[i].counterexample_found();
|
||||||
|
if (compute_ctrx && ecs[i].counterexample_found()
|
||||||
|
&& trace.compare("") == 0)
|
||||||
|
trace = ecs[i].trace(); // Pick randomly one !
|
||||||
|
stats.push_back(ecs[i].stats());
|
||||||
|
}
|
||||||
|
return std::make_tuple(has_ctrx, trace, stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ltsmin_model::state_size() const
|
int ltsmin_model::state_size() const
|
||||||
|
|
|
||||||
|
|
@ -85,13 +85,14 @@ namespace spot
|
||||||
// atomic propositions such as "P.a == P.c"
|
// atomic propositions such as "P.a == P.c"
|
||||||
ltsmin_kripkecube_ptr kripkecube(std::vector<std::string> to_observe,
|
ltsmin_kripkecube_ptr kripkecube(std::vector<std::string> to_observe,
|
||||||
formula dead = formula::tt(),
|
formula dead = formula::tt(),
|
||||||
int compress = 0) const;
|
int compress = 0,
|
||||||
|
unsigned int nb_threads = 1) const;
|
||||||
|
|
||||||
/// \brief Check for the emptiness between a system and a twa.
|
/// \brief Check for the emptiness between a system and a twa.
|
||||||
/// Return a pair containing a boolean indicating wether a counterexample
|
/// Return a pair containing a boolean indicating wether a counterexample
|
||||||
/// has been found and a string representing the counterexample if the
|
/// has been found and a string representing the counterexample if the
|
||||||
/// computation have been required
|
/// computation have been required
|
||||||
static std::tuple<bool, std::string, istats>
|
static std::tuple<bool, std::string, std::vector<istats>>
|
||||||
modelcheck(ltsmin_kripkecube_ptr sys,
|
modelcheck(ltsmin_kripkecube_ptr sys,
|
||||||
spot::twacube_ptr twa, bool compute_ctrx = false);
|
spot::twacube_ptr twa, bool compute_ctrx = false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,10 @@ namespace spot
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ec_renault13lpar(kripkecube<State, SuccIterator>& sys,
|
ec_renault13lpar(kripkecube<State, SuccIterator>& sys,
|
||||||
twacube_ptr twa)
|
twacube_ptr twa, unsigned tid, bool stop)
|
||||||
: intersect<State, SuccIterator, StateHash, StateEqual,
|
: intersect<State, SuccIterator, StateHash, StateEqual,
|
||||||
ec_renault13lpar<State, SuccIterator,
|
ec_renault13lpar<State, SuccIterator,
|
||||||
StateHash, StateEqual>>(sys, twa),
|
StateHash, StateEqual>>(sys, twa, tid, stop),
|
||||||
acc_(twa->acc()), sccs_(0U)
|
acc_(twa->acc()), sccs_(0U)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -114,6 +114,8 @@ namespace spot
|
||||||
}
|
}
|
||||||
roots_.back().acc |= cond;
|
roots_.back().acc |= cond;
|
||||||
found_ = acc_.accepting(roots_.back().acc);
|
found_ = acc_.accepting(roots_.back().acc);
|
||||||
|
if (SPOT_UNLIKELY(found_))
|
||||||
|
this->stop_ = true;
|
||||||
return found_;
|
return found_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +149,7 @@ namespace spot
|
||||||
acc_cond::mark_t acc = 0U;
|
acc_cond::mark_t acc = 0U;
|
||||||
|
|
||||||
bfs.push(new ctrx_element({&this->todo.back().st, nullptr,
|
bfs.push(new ctrx_element({&this->todo.back().st, nullptr,
|
||||||
this->sys_.succ(this->todo.back().st.st_kripke),
|
this->sys_.succ(this->todo.back().st.st_kripke, this->tid_),
|
||||||
this->twa_->succ(this->todo.back().st.st_prop)}));
|
this->twa_->succ(this->todo.back().st.st_prop)}));
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|
@ -160,7 +162,7 @@ namespace spot
|
||||||
while (!front->it_prop->done())
|
while (!front->it_prop->done())
|
||||||
{
|
{
|
||||||
if (this->twa_->get_cubeset().intersect
|
if (this->twa_->get_cubeset().intersect
|
||||||
(this->twa_->trans_data(front->it_prop).cube_,
|
(this->twa_->trans_data(front->it_prop, this->tid_).cube_,
|
||||||
front->it_kripke->condition()))
|
front->it_kripke->condition()))
|
||||||
{
|
{
|
||||||
const product_state dst = {
|
const product_state dst = {
|
||||||
|
|
@ -181,7 +183,8 @@ namespace spot
|
||||||
// This is a valid transition. If this transition
|
// This is a valid transition. If this transition
|
||||||
// is the one we are looking for, update the counter-
|
// is the one we are looking for, update the counter-
|
||||||
// -example and flush the bfs queue.
|
// -example and flush the bfs queue.
|
||||||
auto mark = this->twa_->trans_data(front->it_prop).acc_;
|
auto mark = this->twa_->trans_data(front->it_prop,
|
||||||
|
this->tid_).acc_;
|
||||||
if (!acc.has(mark))
|
if (!acc.has(mark))
|
||||||
{
|
{
|
||||||
ctrx_element* current = front;
|
ctrx_element* current = front;
|
||||||
|
|
@ -213,7 +216,7 @@ namespace spot
|
||||||
const product_state* q = &(it->first);
|
const product_state* q = &(it->first);
|
||||||
ctrx_element* root = new ctrx_element({
|
ctrx_element* root = new ctrx_element({
|
||||||
q , nullptr,
|
q , nullptr,
|
||||||
this->sys_.succ(q->st_kripke),
|
this->sys_.succ(q->st_kripke, this->tid_),
|
||||||
this->twa_->succ(q->st_prop)
|
this->twa_->succ(q->st_prop)
|
||||||
});
|
});
|
||||||
bfs.push(root);
|
bfs.push(root);
|
||||||
|
|
@ -224,7 +227,7 @@ namespace spot
|
||||||
const product_state* q = &(it->first);
|
const product_state* q = &(it->first);
|
||||||
ctrx_element* root = new ctrx_element({
|
ctrx_element* root = new ctrx_element({
|
||||||
q , nullptr,
|
q , nullptr,
|
||||||
this->sys_.succ(q->st_kripke),
|
this->sys_.succ(q->st_kripke, this->tid_),
|
||||||
this->twa_->succ(q->st_prop)
|
this->twa_->succ(q->st_prop)
|
||||||
});
|
});
|
||||||
bfs.push(root);
|
bfs.push(root);
|
||||||
|
|
@ -243,7 +246,7 @@ namespace spot
|
||||||
virtual istats stats() override
|
virtual istats stats() override
|
||||||
{
|
{
|
||||||
return {this->states(), this->trans(), sccs_,
|
return {this->states(), this->trans(), sccs_,
|
||||||
(unsigned)roots_.size(), dfs_};
|
(unsigned) roots_.size(), dfs_, found_};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ namespace spot
|
||||||
unsigned sccs;
|
unsigned sccs;
|
||||||
unsigned instack_sccs;
|
unsigned instack_sccs;
|
||||||
unsigned instack_item;
|
unsigned instack_item;
|
||||||
|
bool is_empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief This class explores (with a DFS) a product between a
|
/// \brief This class explores (with a DFS) a product between a
|
||||||
|
|
@ -59,8 +60,8 @@ namespace spot
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
intersect(kripkecube<State, SuccIterator>& sys,
|
intersect(kripkecube<State, SuccIterator>& sys,
|
||||||
twacube_ptr twa):
|
twacube_ptr twa, unsigned tid, bool& stop):
|
||||||
sys_(sys), twa_(twa)
|
sys_(sys), twa_(twa), tid_(tid), stop_(stop)
|
||||||
{
|
{
|
||||||
SPOT_ASSERT(is_a_kripkecube(sys));
|
SPOT_ASSERT(is_a_kripkecube(sys));
|
||||||
map.reserve(2000000);
|
map.reserve(2000000);
|
||||||
|
|
@ -87,10 +88,10 @@ namespace spot
|
||||||
bool run()
|
bool run()
|
||||||
{
|
{
|
||||||
self().setup();
|
self().setup();
|
||||||
product_state initial = {sys_.initial(), twa_->get_initial()};
|
product_state initial = {sys_.initial(tid_), twa_->get_initial()};
|
||||||
if (SPOT_LIKELY(self().push_state(initial, dfs_number+1, 0U)))
|
if (SPOT_LIKELY(self().push_state(initial, dfs_number+1, 0U)))
|
||||||
{
|
{
|
||||||
todo.push_back({initial, sys_.succ(initial.st_kripke),
|
todo.push_back({initial, sys_.succ(initial.st_kripke, tid_),
|
||||||
twa_->succ(initial.st_prop)});
|
twa_->succ(initial.st_prop)});
|
||||||
|
|
||||||
// Not going further! It's a product with a single state.
|
// Not going further! It's a product with a single state.
|
||||||
|
|
@ -100,7 +101,7 @@ namespace spot
|
||||||
forward_iterators(true);
|
forward_iterators(true);
|
||||||
map[initial] = ++dfs_number;
|
map[initial] = ++dfs_number;
|
||||||
}
|
}
|
||||||
while (!todo.empty())
|
while (!todo.empty() && !stop_)
|
||||||
{
|
{
|
||||||
// Check the kripke is enough since it's the outer loop. More
|
// Check the kripke is enough since it's the outer loop. More
|
||||||
// details in forward_iterators.
|
// details in forward_iterators.
|
||||||
|
|
@ -113,8 +114,8 @@ namespace spot
|
||||||
is_init,
|
is_init,
|
||||||
newtop,
|
newtop,
|
||||||
map[newtop])))
|
map[newtop])))
|
||||||
{
|
{
|
||||||
sys_.recycle(todo.back().it_kripke);
|
sys_.recycle(todo.back().it_kripke, tid_);
|
||||||
// FIXME a local storage for twacube iterator?
|
// FIXME a local storage for twacube iterator?
|
||||||
todo.pop_back();
|
todo.pop_back();
|
||||||
if (SPOT_UNLIKELY(self().counterexample_found()))
|
if (SPOT_UNLIKELY(self().counterexample_found()))
|
||||||
|
|
@ -126,9 +127,9 @@ namespace spot
|
||||||
++transitions;
|
++transitions;
|
||||||
product_state dst = {
|
product_state dst = {
|
||||||
todo.back().it_kripke->state(),
|
todo.back().it_kripke->state(),
|
||||||
twa_->trans_storage(todo.back().it_prop).dst
|
twa_->trans_storage(todo.back().it_prop, tid_).dst
|
||||||
};
|
};
|
||||||
auto acc = twa_->trans_data(todo.back().it_prop).acc_;
|
auto acc = twa_->trans_data(todo.back().it_prop, tid_).acc_;
|
||||||
forward_iterators(false);
|
forward_iterators(false);
|
||||||
auto it = map.find(dst);
|
auto it = map.find(dst);
|
||||||
if (it == map.end())
|
if (it == map.end())
|
||||||
|
|
@ -136,7 +137,7 @@ namespace spot
|
||||||
if (SPOT_LIKELY(self().push_state(dst, dfs_number+1, acc)))
|
if (SPOT_LIKELY(self().push_state(dst, dfs_number+1, acc)))
|
||||||
{
|
{
|
||||||
map[dst] = ++dfs_number;
|
map[dst] = ++dfs_number;
|
||||||
todo.push_back({dst, sys_.succ(dst.st_kripke),
|
todo.push_back({dst, sys_.succ(dst.st_kripke, tid_),
|
||||||
twa_->succ(dst.st_prop)});
|
twa_->succ(dst.st_prop)});
|
||||||
forward_iterators(true);
|
forward_iterators(true);
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +168,7 @@ namespace spot
|
||||||
|
|
||||||
virtual istats stats()
|
virtual istats stats()
|
||||||
{
|
{
|
||||||
return {dfs_number, transitions, 0U, 0U, 0U};
|
return {dfs_number, transitions, 0U, 0U, 0U, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
@ -190,7 +191,7 @@ namespace spot
|
||||||
// There is no need to move iterators forward.
|
// There is no need to move iterators forward.
|
||||||
SPOT_ASSERT(!(todo.back().it_prop->done()));
|
SPOT_ASSERT(!(todo.back().it_prop->done()));
|
||||||
if (just_pushed && twa_->get_cubeset()
|
if (just_pushed && twa_->get_cubeset()
|
||||||
.intersect(twa_->trans_data(todo.back().it_prop).cube_,
|
.intersect(twa_->trans_data(todo.back().it_prop, tid_).cube_,
|
||||||
todo.back().it_kripke->condition()))
|
todo.back().it_kripke->condition()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -207,7 +208,7 @@ namespace spot
|
||||||
while (!todo.back().it_prop->done())
|
while (!todo.back().it_prop->done())
|
||||||
{
|
{
|
||||||
if (SPOT_UNLIKELY(twa_->get_cubeset()
|
if (SPOT_UNLIKELY(twa_->get_cubeset()
|
||||||
.intersect(twa_->trans_data(todo.back().it_prop).cube_,
|
.intersect(twa_->trans_data(todo.back().it_prop, tid_).cube_,
|
||||||
todo.back().it_kripke->condition())))
|
todo.back().it_kripke->condition())))
|
||||||
return;
|
return;
|
||||||
todo.back().it_prop->next();
|
todo.back().it_prop->next();
|
||||||
|
|
@ -263,5 +264,7 @@ namespace spot
|
||||||
visited_map map;
|
visited_map map;
|
||||||
unsigned int dfs_number = 0;
|
unsigned int dfs_number = 0;
|
||||||
unsigned int transitions = 0;
|
unsigned int transitions = 0;
|
||||||
|
unsigned tid_;
|
||||||
|
bool& stop_; // Do not need to be atomic.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ namespace spot
|
||||||
class SPOT_API seq_reach_kripke
|
class SPOT_API seq_reach_kripke
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
seq_reach_kripke(kripkecube<State, SuccIterator>& sys):
|
seq_reach_kripke(kripkecube<State, SuccIterator>& sys, unsigned tid):
|
||||||
sys_(sys)
|
sys_(sys), tid_(tid)
|
||||||
{
|
{
|
||||||
SPOT_ASSERT(is_a_kripkecube(sys));
|
SPOT_ASSERT(is_a_kripkecube(sys));
|
||||||
visited.reserve(2000000);
|
visited.reserve(2000000);
|
||||||
|
|
@ -54,15 +54,15 @@ namespace spot
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
self().setup();
|
self().setup();
|
||||||
State initial = sys_.initial();
|
State initial = sys_.initial(tid_);
|
||||||
todo.push_back({initial, sys_.succ(initial)});
|
todo.push_back({initial, sys_.succ(initial, tid_)});
|
||||||
visited[initial] = ++dfs_number;
|
visited[initial] = ++dfs_number;
|
||||||
self().push(initial, dfs_number);
|
self().push(initial, dfs_number);
|
||||||
while (!todo.empty())
|
while (!todo.empty())
|
||||||
{
|
{
|
||||||
if (todo.back().it->done())
|
if (todo.back().it->done())
|
||||||
{
|
{
|
||||||
sys_.recycle(todo.back().it);
|
sys_.recycle(todo.back().it, tid_);
|
||||||
todo.pop_back();
|
todo.pop_back();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -76,7 +76,7 @@ namespace spot
|
||||||
self().push(dst, dfs_number);
|
self().push(dst, dfs_number);
|
||||||
self().edge(visited[todo.back().s], dfs_number);
|
self().edge(visited[todo.back().s], dfs_number);
|
||||||
todo.back().it->next();
|
todo.back().it->next();
|
||||||
todo.push_back({dst, sys_.succ(dst)});
|
todo.push_back({dst, sys_.succ(dst, tid_)});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -113,5 +113,6 @@ namespace spot
|
||||||
visited_map visited;
|
visited_map visited;
|
||||||
unsigned int dfs_number = 0;
|
unsigned int dfs_number = 0;
|
||||||
unsigned int transitions = 0;
|
unsigned int transitions = 0;
|
||||||
|
unsigned int tid_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,4 +87,34 @@ namespace spot
|
||||||
static_cast<size_t>(uh(p.second)));
|
static_cast<size_t>(uh(p.second)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// From primes.utm.edu shuffled. This primes are used when we simulate
|
||||||
|
// transition shuffling using "primitive root modulo n" technique.
|
||||||
|
static const unsigned primes[144] =
|
||||||
|
{
|
||||||
|
295075531, 334214857, 314607103, 314607191, 334214891, 334214779,
|
||||||
|
295075421, 472882073, 256203329, 275604599, 314606953, 256203397,
|
||||||
|
275604547, 256203631, 275604617, 472882141, 472882297, 472882219,
|
||||||
|
256203229, 256203469, 275604643, 472882169, 275604803, 472882283,
|
||||||
|
295075463, 334214593, 295075213, 256203373, 314607019, 314607193,
|
||||||
|
295075399, 256203523, 314607001, 295075289, 256203293, 256203641,
|
||||||
|
256203307, 314607047, 295075373, 314607053, 314606977, 334214681,
|
||||||
|
275604691, 275604577, 472882447, 314607031, 275605019, 472882477,
|
||||||
|
472882499, 314607173, 295075241, 295075471, 295075367, 256203559,
|
||||||
|
314607233, 275604881, 334214941, 472882103, 275604821, 472882511,
|
||||||
|
295075357, 472882517, 314607023, 256203317, 295075337, 275605007,
|
||||||
|
472882391, 256203223, 334214723, 295075381, 295075423, 275604733,
|
||||||
|
314607113, 256203257, 472882453, 256203359, 295075283, 314607043,
|
||||||
|
256203403, 472882259, 314606891, 472882253, 314606917, 256203349,
|
||||||
|
256203457, 295075457, 472882171, 314607229, 295075513, 472882429,
|
||||||
|
334214953, 275604841, 295075309, 472882099, 334214467, 334214939,
|
||||||
|
275604869, 314607077, 314607089, 275604947, 275605027, 295075379,
|
||||||
|
334214861, 314606909, 334214911, 314607199, 275604983, 314606969,
|
||||||
|
256203221, 334214899, 256203611, 256203679, 472882337, 275604767,
|
||||||
|
472882199, 295075523, 472882049, 275604817, 334214561, 334214581,
|
||||||
|
334214663, 295075489, 295075163, 334214869, 334214521, 295075499,
|
||||||
|
275604913, 334214753, 334214687, 256203491, 295075153, 334214893,
|
||||||
|
472882411, 472882117, 275604793, 334214833, 334214591, 314607091,
|
||||||
|
256203419, 275604727, 256203659, 275604961, 334214557, 275604677
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <spot/graph/graph.hh>
|
#include <spot/graph/graph.hh>
|
||||||
|
#include <spot/misc/hash.hh>
|
||||||
#include <spot/twa/acc.hh>
|
#include <spot/twa/acc.hh>
|
||||||
#include <spot/twacube/cube.hh>
|
#include <spot/twacube/cube.hh>
|
||||||
#include <spot/twacube/fwd.hh>
|
#include <spot/twacube/fwd.hh>
|
||||||
|
|
@ -93,8 +94,14 @@ namespace spot
|
||||||
// swarming, we expect swarming is activated.
|
// swarming, we expect swarming is activated.
|
||||||
if (SPOT_UNLIKELY(!seed))
|
if (SPOT_UNLIKELY(!seed))
|
||||||
return idx_;
|
return idx_;
|
||||||
// swarming.
|
// Here swarming performs a technique called "primitive
|
||||||
return (((idx_-st_.succ+1)*seed) % (st_.succ_tail-st_.succ+1)) + st_.succ;
|
// root modulo n", i. e. for i in [1..n]: i*seed (mod n). We
|
||||||
|
// also must have seed prime with n: to solve this, we use
|
||||||
|
// precomputed primes and seed access one of this primes. Note
|
||||||
|
// that the chosen prime must be greater than n.
|
||||||
|
SPOT_ASSERT(primes[seed] > (st_.succ_tail-st_.succ+1));
|
||||||
|
return (((idx_-st_.succ+1)*primes[seed]) % (st_.succ_tail-st_.succ+1))
|
||||||
|
+ st_.succ;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement de
|
# Copyright (C) 2014, 2015, 2016 Laboratoire de Recherche et Développement de
|
||||||
# l'Epita (LRDE).
|
# l'Epita (LRDE).
|
||||||
#
|
#
|
||||||
# This file is part of Spot, a model checking library.
|
# This file is part of Spot, a model checking library.
|
||||||
|
|
@ -61,10 +61,10 @@ init : 0
|
||||||
2->1 : p1&p2 {}
|
2->1 : p1&p2 {}
|
||||||
2->2 : 1 {0,1}
|
2->2 : 1 {0,1}
|
||||||
-----------
|
-----------
|
||||||
2 1 !p1 {}
|
|
||||||
2 2 1 {0,1}
|
2 2 1 {0,1}
|
||||||
2 0 p1 {0,1}
|
|
||||||
2 1 p1&p2 {}
|
2 1 p1&p2 {}
|
||||||
|
2 1 !p1 {}
|
||||||
|
2 0 p1 {0,1}
|
||||||
2 0 !p1&p2 {0,1}
|
2 0 !p1&p2 {0,1}
|
||||||
digraph G {
|
digraph G {
|
||||||
rankdir=LR
|
rankdir=LR
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,8 @@ static int checked_main()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
modelcube = spot::ltsmin_model::load(mc_options.model)
|
modelcube = spot::ltsmin_model::load(mc_options.model)
|
||||||
.kripkecube(propcube->get_ap(), deadf, mc_options.compress);
|
.kripkecube(propcube->get_ap(), deadf, mc_options.compress,
|
||||||
|
mc_options.nb_threads);
|
||||||
}
|
}
|
||||||
catch (std::runtime_error& e)
|
catch (std::runtime_error& e)
|
||||||
{
|
{
|
||||||
|
|
@ -432,32 +433,57 @@ static int checked_main()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display statistics
|
// Display statistics
|
||||||
std::cout << std::get<2>(res).states << " unique states visited\n";
|
unsigned smallest = 0;
|
||||||
std::cout << std::get<2>(res).instack_sccs
|
for (unsigned i = 0; i < std::get<2>(res).size(); ++i)
|
||||||
<< " strongly connected components in search stack\n";
|
|
||||||
std::cout << std::get<2>(res).transitions
|
|
||||||
<< " transitions explored\n";
|
|
||||||
std::cout << std::get<2>(res).instack_item
|
|
||||||
<< " items max in DFS search stack\n";
|
|
||||||
std::cout << memused << " pages allocated for emptiness check\n";
|
|
||||||
|
|
||||||
if (!std::get<0>(res))
|
|
||||||
std::cout << "no accepting run found";
|
|
||||||
else if (!mc_options.compute_counterexample)
|
|
||||||
{
|
{
|
||||||
std::cout << "an accepting run exists "
|
if (std::get<2>(res)[i].states < std::get<2>(res)[smallest].states)
|
||||||
<< "(use -c to print it)" << std::endl;
|
smallest = i;
|
||||||
exit_code = 1;
|
|
||||||
|
std::cout << "\n---- Thread number : " << i << '\n';
|
||||||
|
std::cout << std::get<2>(res)[i].states << " unique states visited\n";
|
||||||
|
std::cout << std::get<2>(res)[i].instack_sccs
|
||||||
|
<< " strongly connected components in search stack\n";
|
||||||
|
std::cout << std::get<2>(res)[i].transitions
|
||||||
|
<< " transitions explored\n";
|
||||||
|
std::cout << std::get<2>(res)[i].instack_item
|
||||||
|
<< " items max in DFS search stack\n";
|
||||||
|
|
||||||
|
// FIXME: produce walltime for each threads.
|
||||||
|
if (mc_options.csv)
|
||||||
|
{
|
||||||
|
std::cout << "Find following the csv: "
|
||||||
|
<< "thread_id,walltimems,type,"
|
||||||
|
<< "states,transitions\n";
|
||||||
|
std::cout << "@th_" << i << ','
|
||||||
|
<< tm.timer("emptiness check").walltime() << ','
|
||||||
|
<< (!std::get<2>(res)[i].is_empty ?
|
||||||
|
"EMPTY," : "NONEMPTY,")
|
||||||
|
<< std::get<2>(res)[i].states << ','
|
||||||
|
<< std::get<2>(res)[i].transitions
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
std::cout << "an accepting run exists!\n" << std::get<1>(res)
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
if (mc_options.csv)
|
if (mc_options.csv)
|
||||||
{
|
{
|
||||||
std::cout << "\nFind following the csv: "
|
std::cout << "\nSummary :\n";
|
||||||
<< "model,formula,walltimems,memused,type,"
|
if (!std::get<0>(res))
|
||||||
|
std::cout << "no accepting run found\n";
|
||||||
|
else if (!mc_options.compute_counterexample)
|
||||||
|
{
|
||||||
|
std::cout << "an accepting run exists "
|
||||||
|
<< "(use -c to print it)" << std::endl;
|
||||||
|
exit_code = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
std::cout << "an accepting run exists!\n" << std::get<1>(res)
|
||||||
|
<< '\n';
|
||||||
|
|
||||||
|
std::cout << "Find following the csv: "
|
||||||
|
<< "model,formula,walltimems,memused,type"
|
||||||
<< "states,transitions\n";
|
<< "states,transitions\n";
|
||||||
|
|
||||||
std::cout << '#'
|
std::cout << '#'
|
||||||
<< split_filename(mc_options.model)
|
<< split_filename(mc_options.model)
|
||||||
<< ','
|
<< ','
|
||||||
|
|
@ -465,9 +491,9 @@ static int checked_main()
|
||||||
<< tm.timer("emptiness check").walltime() << ','
|
<< tm.timer("emptiness check").walltime() << ','
|
||||||
<< memused << ','
|
<< memused << ','
|
||||||
<< (!std::get<0>(res) ? "EMPTY," : "NONEMPTY,")
|
<< (!std::get<0>(res) ? "EMPTY," : "NONEMPTY,")
|
||||||
<< std::get<2>(res).states << ','
|
<< std::get<2>(res)[smallest].states << ','
|
||||||
<< std::get<2>(res).transitions
|
<< std::get<2>(res)[smallest].transitions
|
||||||
<< std::endl;
|
<< '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue