safra: Add pretty printer for states
* src/tests/safra.cc, src/tests/safra.test: Add options and test. * src/twaalgos/safra.cc, src/twaalgos/safra.hh: Here.
This commit is contained in:
parent
64cdd1adc7
commit
64b27a9a26
4 changed files with 194 additions and 31 deletions
|
|
@ -30,14 +30,58 @@
|
||||||
#include "twaalgos/degen.hh"
|
#include "twaalgos/degen.hh"
|
||||||
|
|
||||||
|
|
||||||
|
int help()
|
||||||
|
{
|
||||||
|
std::cerr << "safra [OPTIONS]\n";
|
||||||
|
std::cerr << "\t-f ltl_formula\tinput string is an ltl formulae\n";
|
||||||
|
std::cerr << "\t--hoa file.hoa\tinput file has hoa format\n";
|
||||||
|
std::cerr << "\t-p\tpretty print states\n";
|
||||||
|
std::cerr << "\t-H\toutput hoa format\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
bool in_hoa = false;
|
||||||
|
bool in_ltl = false;
|
||||||
|
bool out_dot = true;
|
||||||
|
bool out_hoa = false;
|
||||||
|
bool pretty_print = false;
|
||||||
|
|
||||||
|
char* input = nullptr;
|
||||||
if (argc <= 2)
|
if (argc <= 2)
|
||||||
return 1;
|
return help();
|
||||||
char* input = argv[2];
|
for (int i = 1; i < argc; ++i)
|
||||||
|
{
|
||||||
|
if (!strncmp(argv[i], "--hoa", 5))
|
||||||
|
{
|
||||||
|
in_hoa = true;
|
||||||
|
if (i + 1 >= argc)
|
||||||
|
return help();
|
||||||
|
input = argv[++i];
|
||||||
|
}
|
||||||
|
else if (!strncmp(argv[i], "-f", 2))
|
||||||
|
{
|
||||||
|
in_ltl = true;
|
||||||
|
if (i + 1 >= argc)
|
||||||
|
return help();
|
||||||
|
input = argv[++i];
|
||||||
|
}
|
||||||
|
else if (!strncmp(argv[i], "-H", 2))
|
||||||
|
{
|
||||||
|
out_dot = false;
|
||||||
|
out_hoa = true;
|
||||||
|
}
|
||||||
|
else if (!strncmp(argv[i], "-p", 2))
|
||||||
|
pretty_print = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input)
|
||||||
|
help();
|
||||||
|
|
||||||
auto dict = spot::make_bdd_dict();
|
auto dict = spot::make_bdd_dict();
|
||||||
spot::twa_graph_ptr res;
|
spot::twa_graph_ptr res;
|
||||||
if (!strncmp(argv[1], "-f", 2))
|
if (in_ltl)
|
||||||
{
|
{
|
||||||
spot::ltl::parse_error_list pel;
|
spot::ltl::parse_error_list pel;
|
||||||
const spot::ltl::formula* f =
|
const spot::ltl::formula* f =
|
||||||
|
|
@ -48,24 +92,26 @@ int main(int argc, char* argv[])
|
||||||
spot::translator trans(dict);
|
spot::translator trans(dict);
|
||||||
trans.set_pref(spot::postprocessor::Deterministic);
|
trans.set_pref(spot::postprocessor::Deterministic);
|
||||||
auto tmp = trans.run(f);
|
auto tmp = trans.run(f);
|
||||||
res = spot::tgba_determinisation(tmp);
|
res = spot::tgba_determinisation(tmp, pretty_print);
|
||||||
f->destroy();
|
f->destroy();
|
||||||
}
|
}
|
||||||
else if (!strncmp(argv[1], "--hoa", 5))
|
else if (in_hoa)
|
||||||
{
|
{
|
||||||
spot::hoa_parse_error_list pel;
|
spot::hoa_parse_error_list pel;
|
||||||
auto aut = spot::hoa_parse(input, pel, dict);
|
auto aut = spot::hoa_parse(input, pel, dict);
|
||||||
if (spot::format_hoa_parse_errors(std::cerr, input, pel))
|
if (spot::format_hoa_parse_errors(std::cerr, input, pel))
|
||||||
return 2;
|
return 2;
|
||||||
res = tgba_determinisation(aut->aut);
|
res = tgba_determinisation(aut->aut, pretty_print);
|
||||||
}
|
}
|
||||||
res->merge_transitions();
|
res->merge_transitions();
|
||||||
|
|
||||||
if (argc >= 4 && !strncmp(argv[3], "-H", 2))
|
if (out_hoa)
|
||||||
{
|
{
|
||||||
spot::hoa_reachable(std::cout, res, "t");
|
spot::hoa_reachable(std::cout, res, "t");
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else if (out_dot)
|
||||||
spot::dotty_reachable(std::cout, res);
|
spot::dotty_reachable(std::cout, res);
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,3 +119,24 @@ EOF
|
||||||
|
|
||||||
run 0 ../safra --hoa double_b.hoa -H > out.hoa
|
run 0 ../safra --hoa double_b.hoa -H > out.hoa
|
||||||
diff out.hoa out.exp
|
diff out.hoa out.exp
|
||||||
|
|
||||||
|
# Formulas from bench/dtgbasat/formulas
|
||||||
|
cat >formulae <<EOF
|
||||||
|
X((a M F((!c & !b) | (c & b))) W (G!c U b))
|
||||||
|
X(((a & b) R (!a U !c)) R b)
|
||||||
|
XXG(Fa U Xb)
|
||||||
|
(!a M !b) W F!c
|
||||||
|
(b & Fa & GFc) R a
|
||||||
|
(a R (b W a)) W G(!a M (c | b))
|
||||||
|
(Fa W b) R (Fc | !a)
|
||||||
|
X(G(!a M !b) | G(a | G!a))
|
||||||
|
Fa W Gb
|
||||||
|
Ga | GFb
|
||||||
|
a M G(F!b | X!a)
|
||||||
|
G!a R XFb
|
||||||
|
EOF
|
||||||
|
run 0 ../safra --hoa double_b.hoa -H > out.hoa
|
||||||
|
ltl2tgba=../../bin/ltl2tgba
|
||||||
|
../../bin/ltlcross -F formulae \
|
||||||
|
"../safra -f %f -H > %O" \
|
||||||
|
"$ltl2tgba -f %f -H > %O"
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
@ -28,6 +29,97 @@ namespace spot
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
using power_set = std::map<safra_state, int>;
|
||||||
|
const char* const sub[10] =
|
||||||
|
{
|
||||||
|
"\u2080",
|
||||||
|
"\u2081",
|
||||||
|
"\u2082",
|
||||||
|
"\u2083",
|
||||||
|
"\u2084",
|
||||||
|
"\u2085",
|
||||||
|
"\u2086",
|
||||||
|
"\u2087",
|
||||||
|
"\u2088",
|
||||||
|
"\u2089",
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string subscript(unsigned start)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
res = sub[start % 10] + res;
|
||||||
|
start /= 10;
|
||||||
|
}
|
||||||
|
while (start);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(unsigned start, const safra_state::nodes_t& nodes,
|
||||||
|
std::ostringstream& os, std::vector<unsigned>& idx)
|
||||||
|
{
|
||||||
|
std::string s = subscript(start);
|
||||||
|
os << '[' << s;
|
||||||
|
std::vector<unsigned> new_idx;
|
||||||
|
std::vector<unsigned> todo;
|
||||||
|
unsigned next = -1U;
|
||||||
|
bool first = true;
|
||||||
|
for (auto& i: idx)
|
||||||
|
{
|
||||||
|
auto it = std::lower_bound(nodes.at(i).cbegin(), nodes.at(i).cend(),
|
||||||
|
start + 1);
|
||||||
|
if (it == nodes.at(i).cend())
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
os << i;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
os << ' ' << i;
|
||||||
|
}
|
||||||
|
else if (*it == (start + 1))
|
||||||
|
new_idx.push_back(i);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
todo.push_back(i);
|
||||||
|
next = std::min(next, *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!new_idx.empty())
|
||||||
|
print(start + 1, nodes, os, new_idx);
|
||||||
|
if (next != -1U)
|
||||||
|
{
|
||||||
|
std::vector<unsigned> todo2;
|
||||||
|
std::vector<unsigned> todo_next;
|
||||||
|
unsigned new_next = -1U;
|
||||||
|
while (!todo.empty())
|
||||||
|
{
|
||||||
|
for (auto& i: todo)
|
||||||
|
{
|
||||||
|
auto it = std::lower_bound(nodes.at(i).cbegin(),
|
||||||
|
nodes.at(i).cend(), next);
|
||||||
|
if (*it == next)
|
||||||
|
todo_next.push_back(i);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
todo2.push_back(i);
|
||||||
|
next = std::min(new_next, *it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print(next, nodes, os, todo_next);
|
||||||
|
|
||||||
|
next = new_next;
|
||||||
|
new_next = -1;
|
||||||
|
todo = todo2;
|
||||||
|
todo2.clear();
|
||||||
|
todo_next.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os << s << ']';
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if lhs has a smaller nesting pattern than rhs
|
// Returns true if lhs has a smaller nesting pattern than rhs
|
||||||
// If lhs and rhs are the same, return false.
|
// If lhs and rhs are the same, return false.
|
||||||
bool nesting_cmp(const std::vector<node_helper::brace_t>& lhs,
|
bool nesting_cmp(const std::vector<node_helper::brace_t>& lhs,
|
||||||
|
|
@ -53,6 +145,25 @@ namespace spot
|
||||||
t.acc &= mask;
|
t.acc &= mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>*
|
||||||
|
print_debug(std::map<safra_state, int>& states)
|
||||||
|
{
|
||||||
|
std::vector<std::string>* res = nullptr;
|
||||||
|
res = new std::vector<std::string>(states.size());
|
||||||
|
std::vector<unsigned> idx;
|
||||||
|
for (auto& p: states)
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
for (auto& n: p.first.nodes_)
|
||||||
|
idx.push_back(n.first);
|
||||||
|
print(0, p.first.nodes_, os, idx);
|
||||||
|
(*res)[p.second] = os.str();
|
||||||
|
idx.clear();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
auto
|
auto
|
||||||
safra_state::compute_succs(const const_twa_graph_ptr& aut,
|
safra_state::compute_succs(const const_twa_graph_ptr& aut,
|
||||||
|
|
@ -255,23 +366,8 @@ namespace spot
|
||||||
return nodes_ < other.nodes_;
|
return nodes_ < other.nodes_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void safra_state::print_debug(unsigned state_id) const
|
|
||||||
{
|
|
||||||
std::cerr << "State: " << state_id << "{ ";
|
|
||||||
for (auto& n: nodes_)
|
|
||||||
{
|
|
||||||
std::cerr << n.first << " [";
|
|
||||||
for (auto& b: n.second)
|
|
||||||
{
|
|
||||||
std::cerr << b << ' ';
|
|
||||||
}
|
|
||||||
std::cerr << "], ";
|
|
||||||
}
|
|
||||||
std::cerr << "}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
twa_graph_ptr
|
twa_graph_ptr
|
||||||
tgba_determinisation(const const_twa_graph_ptr& a)
|
tgba_determinisation(const const_twa_graph_ptr& a, bool pretty_print)
|
||||||
{
|
{
|
||||||
// Degeneralize
|
// Degeneralize
|
||||||
const_twa_graph_ptr aut;
|
const_twa_graph_ptr aut;
|
||||||
|
|
@ -331,7 +427,6 @@ namespace spot
|
||||||
|
|
||||||
// Given a safra_state get its associated state in output automata.
|
// Given a safra_state get its associated state in output automata.
|
||||||
// Required to create new transitions from 2 safra-state
|
// Required to create new transitions from 2 safra-state
|
||||||
typedef std::map<safra_state, int> power_set;
|
|
||||||
power_set seen;
|
power_set seen;
|
||||||
safra_state init(aut->get_init_state_number(), true);
|
safra_state init(aut->get_init_state_number(), true);
|
||||||
unsigned num = res->new_state();
|
unsigned num = res->new_state();
|
||||||
|
|
@ -374,11 +469,12 @@ namespace spot
|
||||||
res->new_transition(src_num, dst_num, num2bdd[s.second]);
|
res->new_transition(src_num, dst_num, num2bdd[s.second]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
|
||||||
remove_dead_acc(res, sets);
|
remove_dead_acc(res, sets);
|
||||||
res->set_acceptance(sets, acc_cond::acc_code::parity(false, false, sets));
|
res->set_acceptance(sets, acc_cond::acc_code::parity(false, false, sets));
|
||||||
res->prop_deterministic(true);
|
res->prop_deterministic(true);
|
||||||
res->prop_state_based_acc(false);
|
res->prop_state_based_acc(false);
|
||||||
|
if (pretty_print)
|
||||||
|
res->set_named_prop("state-names", print_debug(seen));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,9 @@ namespace spot
|
||||||
class safra_state
|
class safra_state
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::vector<std::pair<safra_state, unsigned>> succs_t;
|
using nodes_t = std::map<unsigned, std::vector<node_helper::brace_t>>;
|
||||||
|
using succs_t = std::vector<std::pair<safra_state, unsigned>>;
|
||||||
bool operator<(const safra_state& other) const;
|
bool operator<(const safra_state& other) const;
|
||||||
// Print each sub-state with their associated braces of a safra state
|
|
||||||
void print_debug(unsigned state_id) const;
|
|
||||||
// Printh the number of states in each brace
|
// Printh the number of states in each brace
|
||||||
safra_state(unsigned state_number, bool init_state = false);
|
safra_state(unsigned state_number, bool init_state = false);
|
||||||
// Given a certain transition_label, compute all the successors of that
|
// Given a certain transition_label, compute all the successors of that
|
||||||
|
|
@ -63,7 +62,7 @@ namespace spot
|
||||||
// A list of nodes similar to the ones of a
|
// A list of nodes similar to the ones of a
|
||||||
// safra tree. These are constructed in the same way as the powerset
|
// safra tree. These are constructed in the same way as the powerset
|
||||||
// algorithm.
|
// algorithm.
|
||||||
std::map<unsigned, std::vector<node_helper::brace_t>> nodes_;
|
nodes_t nodes_;
|
||||||
// A counter that indicates the nomber of states within a brace.
|
// A counter that indicates the nomber of states within a brace.
|
||||||
// This enables us to compute the red value
|
// This enables us to compute the red value
|
||||||
std::vector<size_t> nb_braces_;
|
std::vector<size_t> nb_braces_;
|
||||||
|
|
@ -73,5 +72,6 @@ namespace spot
|
||||||
};
|
};
|
||||||
|
|
||||||
SPOT_API twa_graph_ptr
|
SPOT_API twa_graph_ptr
|
||||||
tgba_determinisation(const const_twa_graph_ptr& aut);
|
tgba_determinisation(const const_twa_graph_ptr& aut,
|
||||||
|
bool pretty_print = false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue