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:
Alexandre Lewkowicz 2015-05-30 12:41:35 +02:00 committed by Alexandre Duret-Lutz
parent 64cdd1adc7
commit 64b27a9a26
4 changed files with 194 additions and 31 deletions

View file

@ -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);
} }

View file

@ -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"

View file

@ -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;
} }
} }

View file

@ -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);
} }