// -*- coding: utf-8 -*-
// Copyright (C) 2015, 2016 Laboratoire de Recherche et
// Developpement de l'Epita
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#pragma once
#include
#include
#include
namespace spot
{
/// \brief This class implements the sequential emptiness check as
/// presented in "Three SCC-based Emptiness Checks for Generalized
/// B\¨uchi Automata" (Renault et al, LPAR 2013). Among the three
/// emptiness check that has been proposed we opted to implement
/// the Gabow's one.
template
class ec_renault13lpar : public intersect>
{
// Ease the manipulation
using typename intersect>::product_state;
public:
ec_renault13lpar(kripkecube& sys,
twacube* twa)
: intersect>(sys, twa),
acc_(twa->acc()), sccs_(0U)
{
}
virtual ~ec_renault13lpar()
{
}
/// \brief This method is called at the begining of the exploration.
/// here we do not need to setup any information.
void setup()
{
}
/// \brief This method is called to notify the emptiness checks
/// that a new state has been discovered. If this method return
/// false, the state will not be explored. The parameter \a dfsnum
/// specify an unique id for the state \a s. Parameter \a cond represents
/// The value on the ingoing edge to \a s.
bool push_state(product_state, unsigned dfsnum, acc_cond::mark_t cond)
{
uf_.makeset(dfsnum);
roots_.push_back({dfsnum, cond, 0U});
return true;
}
/// \brief This method is called to notify the emptiness checks
/// that a state will be popped. If the method return false, then
/// the state will be popped. Otherwise the state \a newtop will
/// become the new top of the DFS stack. If the state \a top is
/// the only one in the DFS stak, the parameter \a is_initial is set
/// to true and both \a newtop and \a newtop_dfsnum have inconsistency
/// values.
bool pop_state(product_state, unsigned top_dfsnum, bool,
product_state, unsigned)
{
if (top_dfsnum == roots_.back().dfsnum)
{
roots_.pop_back();
++sccs_;
uf_.markdead(top_dfsnum);
}
dfs_ = this->todo.size() > dfs_ ? this->todo.size() : dfs_;
return true;
}
/// \brief This method is called for every closing, back, or forward edge.
/// Return true if a counterexample has been found.
bool update(product_state, unsigned,
product_state, unsigned dst_dfsnum,
acc_cond::mark_t cond)
{
if (uf_.isdead(dst_dfsnum))
return false;
while (!uf_.sameset(dst_dfsnum, roots_.back().dfsnum))
{
auto& el = roots_.back();
roots_.pop_back();
uf_.unite(dst_dfsnum, el.dfsnum);
cond |= el.acc | el.ingoing;
}
roots_.back().acc |= cond;
found_ = acc_.accepting(roots_.back().acc);
return found_;
}
bool counterexample_found()
{
return found_;
}
std::string trace()
{
assert(counterexample_found());
std::string res = "Prefix:\n";
// Compute the prefix of the accepting run
for (auto& s : this->todo)
res += " " + std::to_string(s.st.st_prop) +
+ "*" + this->sys_.to_string(s.st.st_kripke) + "\n";
// Compute the accepting cycle
res += "Cycle:\n";
struct ctrx_element
{
const product_state* prod_st;
ctrx_element* parent_st;
SuccIterator* it_kripke;
std::shared_ptr it_prop;
};
std::queue bfs;
acc_cond::mark_t acc = 0U;
bfs.push(new ctrx_element({&this->todo.back().st, nullptr,
this->sys_.succ(this->todo.back().st.st_kripke),
this->twa_->succ(this->todo.back().st.st_prop)}));
while (true)
{
here:
auto* front = bfs.front();
bfs.pop();
// PUSH all successors of the state.
while (!front->it_kripke->done())
{
while (!front->it_prop->done())
{
if (this->twa_->get_cubeset().intersect
(this->twa_->trans_data(front->it_prop).cube_,
front->it_kripke->condition()))
{
const product_state dst = {
front->it_kripke->state(),
this->twa_->trans_storage(front->it_prop).dst
};
// Skip Unknown states or not same SCC
auto it = this->map.find(dst);
if (it == this->map.end() ||
!uf_.sameset(it->second,
this->map[this->todo.back().st]))
{
front->it_prop->next();
continue;
}
// This is a valid transition. If this transition
// is the one we are looking for, update the counter-
// -example and flush the bfs queue.
auto mark = this->twa_->trans_data(front->it_prop).acc_;
if (!acc.has(mark))
{
ctrx_element* current = front;
while (current != nullptr)
{
// FIXME also display acc?
res = res + " " +
std::to_string(current->prod_st->st_prop) +
+ "*" +
this->sys_. to_string(current->prod_st
->st_kripke) +
"\n";
current = current->parent_st;
}
// empty the queue
while (!bfs.empty())
{
auto* e = bfs.front();
bfs.pop();
delete e;
}
// update acceptance
acc |= mark;
if (this->twa_->acc().accepting(acc))
return res;
const product_state* q = &(it->first);
ctrx_element* root = new ctrx_element({
q , nullptr,
this->sys_.succ(q->st_kripke),
this->twa_->succ(q->st_prop)
});
bfs.push(root);
goto here;
}
// Otherwise increment iterator and push successor.
const product_state* q = &(it->first);
ctrx_element* root = new ctrx_element({
q , nullptr,
this->sys_.succ(q->st_kripke),
this->twa_->succ(q->st_prop)
});
bfs.push(root);
}
front->it_prop->next();
}
front->it_prop->reset();
front->it_kripke->next();
}
}
// never reach here;
return res;
}
virtual istats stats() override
{
return {this->states(), this->trans(), sccs_,
(unsigned)roots_.size(), dfs_};
}
private:
bool found_ = false; ///< \brief A counterexample is detected?
struct root_element {
unsigned dfsnum;
acc_cond::mark_t ingoing;
acc_cond::mark_t acc;
};
/// \brief the root stack.
std::vector roots_;
int_unionfind uf_;
acc_cond acc_;
unsigned sccs_;
unsigned dfs_;
};
}