dtgbacomp: Rewrite using tgba_digraph instead of tgba_explicit_number
* src/tgbaalgos/dtgbacomp.cc, src/tgbaalgos/dtgbacomp.hh: Rewrite. Also prefer simple loops over reachiter. * src/tgbatest/det.test: Adjust.
This commit is contained in:
parent
0909c2fe3d
commit
ead04cb1ac
3 changed files with 99 additions and 140 deletions
|
|
@ -19,124 +19,87 @@
|
||||||
|
|
||||||
#include "dtgbacomp.hh"
|
#include "dtgbacomp.hh"
|
||||||
#include "ltlast/constant.hh"
|
#include "ltlast/constant.hh"
|
||||||
#include "reachiter.hh"
|
#include "dupexp.hh"
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
tgba_digraph* dtgba_complement(const tgba* aut)
|
||||||
namespace
|
|
||||||
{
|
{
|
||||||
class dtgbacomp_iter: public tgba_reachable_iterator_depth_first_stack
|
// Clone the original automaton.
|
||||||
{
|
tgba_digraph* res = tgba_dupexp_dfs(aut);
|
||||||
bdd orig_acc_;
|
|
||||||
bdd all_neg_;
|
|
||||||
bdd acc_;
|
|
||||||
bdd_dict* dict_;
|
|
||||||
tgba_explicit_number* out_;
|
|
||||||
int num_acc_;
|
|
||||||
|
|
||||||
typedef state_explicit_number::transition trans;
|
auto dict = res->get_dict();
|
||||||
public:
|
|
||||||
dtgbacomp_iter(const tgba* a)
|
bdd oldaccs = aut->all_acceptance_conditions();
|
||||||
: tgba_reachable_iterator_depth_first_stack(a),
|
bdd oldnegs = aut->neg_acceptance_conditions();
|
||||||
dict_(a->get_dict()),
|
|
||||||
out_(new tgba_explicit_number(dict_))
|
// We will modify res in place, and the resulting
|
||||||
|
// automaton will only have one acceptance set.
|
||||||
|
dict->unregister_all_typed_variables(bdd_dict::acc, res);
|
||||||
|
bdd theacc =
|
||||||
|
bdd_ithvar(dict->register_acceptance_variable
|
||||||
|
(ltl::constant::true_instance(), res));
|
||||||
|
res->set_acceptance_conditions(theacc);
|
||||||
|
|
||||||
|
unsigned num_acc = aut->number_of_acceptance_conditions();
|
||||||
|
unsigned n = res->num_states();
|
||||||
|
// We will duplicate the automaton as many times as we have
|
||||||
|
// acceptance sets, and we need one extra sink state.
|
||||||
|
res->new_states(num_acc * n + 1);
|
||||||
|
unsigned sink = res->num_states() - 1;
|
||||||
|
// The sink state has an accepting self-loop.
|
||||||
|
res->new_transition(sink, sink, bddtrue, theacc);
|
||||||
|
|
||||||
|
for (unsigned src = 0; src < n; ++src)
|
||||||
{
|
{
|
||||||
dict_->register_all_variables_of(a, out_);
|
// Keep track of all conditions on transition leaving state
|
||||||
orig_acc_ = a->all_acceptance_conditions();
|
// SRC, so we can complete it.
|
||||||
all_neg_ = a->neg_acceptance_conditions();
|
bdd missingcond = bddtrue;
|
||||||
num_acc_ = a->number_of_acceptance_conditions();
|
for (auto& t: res->out(src))
|
||||||
|
|
||||||
// Register one acceptance condition for the result.
|
|
||||||
int accvar = dict_->register_acceptance_variable
|
|
||||||
(ltl::constant::true_instance(), out_);
|
|
||||||
acc_ = bdd_ithvar(accvar);
|
|
||||||
out_->set_acceptance_conditions(acc_);
|
|
||||||
}
|
|
||||||
|
|
||||||
tgba_explicit_number*
|
|
||||||
result()
|
|
||||||
{
|
|
||||||
return out_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
end()
|
|
||||||
{
|
|
||||||
out_->merge_transitions();
|
|
||||||
// create a sink state if needed.
|
|
||||||
if (out_->has_state(0))
|
|
||||||
{
|
{
|
||||||
trans* t = out_->create_transition(0, 0);
|
if (t.dst >= n) // Ignore transition we added.
|
||||||
t->condition = bddtrue;
|
break;
|
||||||
t->acceptance_conditions = acc_;
|
missingcond -= t.cond;
|
||||||
}
|
bdd curacc = t.acc;
|
||||||
}
|
// The original transition must not accept anymore.
|
||||||
|
t.acc = bddfalse;
|
||||||
|
// Transition that were fully accepting are never cloned.
|
||||||
|
if (curacc == oldaccs)
|
||||||
|
continue;
|
||||||
|
// Save t.cond and t.dst as the reference to t
|
||||||
|
// is invalided by calls to new_transition().
|
||||||
|
unsigned dst = t.dst;
|
||||||
|
bdd cond = t.cond;
|
||||||
|
// We want all acceptance bdd variable to appear in curacc.
|
||||||
|
if (curacc == bddfalse)
|
||||||
|
curacc = oldnegs;
|
||||||
|
|
||||||
void process_state(const state*, int n,
|
// Iterate over all the acceptance conditions in 'curacc',
|
||||||
tgba_succ_iterator* i)
|
// an duplicate it each each clone for which it does not
|
||||||
{
|
// belong to the acceptance set.
|
||||||
// add a transition to a sink state if the state is not complete.
|
unsigned add = 0;
|
||||||
bdd all = bddtrue;
|
while (curacc != bddtrue)
|
||||||
if (i->first())
|
|
||||||
do
|
|
||||||
all -= i->current_condition();
|
|
||||||
while (i->next());
|
|
||||||
if (all != bddfalse)
|
|
||||||
{
|
|
||||||
trans* t = out_->create_transition(n, 0);
|
|
||||||
t->condition = all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
process_link(const state*, int in,
|
|
||||||
const state*, int out,
|
|
||||||
const tgba_succ_iterator* si)
|
|
||||||
{
|
|
||||||
assert(in > 0);
|
|
||||||
assert(out > 0);
|
|
||||||
bdd a = si->current_acceptance_conditions();
|
|
||||||
|
|
||||||
// Positive states represent a non-accepting copy of the
|
|
||||||
// original automaton.
|
|
||||||
trans* t1 = out_->create_transition(in, out);
|
|
||||||
t1->condition = si->current_condition();
|
|
||||||
|
|
||||||
// Negative states encode NUM_ACC_ copies of the automaton.
|
|
||||||
// In each copy transitions labeled by one of the acceptance
|
|
||||||
// set have been removed, and all the remaining transitions
|
|
||||||
// are now accepting.
|
|
||||||
// For each state S, we have NUM_ACC_ additional copies
|
|
||||||
// labeled S*-NUM_ACC, S*-NUM_ACC+1, ... S*-NUM_ACC+(NUM_ACC-1),
|
|
||||||
if (a != orig_acc_)
|
|
||||||
{
|
|
||||||
bool backlink = on_stack(out);
|
|
||||||
int add = 0;
|
|
||||||
if (a == bddfalse)
|
|
||||||
a = all_neg_;
|
|
||||||
// Iterate over all the acceptance conditions in 'a'.
|
|
||||||
bdd ac = a;
|
|
||||||
while (ac != bddtrue)
|
|
||||||
{
|
{
|
||||||
bdd h = bdd_high(ac);
|
add += n;
|
||||||
|
bdd h = bdd_high(curacc);
|
||||||
if (h == bddfalse)
|
if (h == bddfalse)
|
||||||
{
|
{
|
||||||
trans* t2 = out_->create_transition(in * -num_acc_ + add,
|
// Clone the transition
|
||||||
out * -num_acc_ + add);
|
res->new_transition(src + add, dst + add, cond, theacc);
|
||||||
t2->condition = si->current_condition();
|
assert(dst + add < sink);
|
||||||
t2->acceptance_conditions = acc_;
|
// Using `t' is disallowed from now on as it is a
|
||||||
|
// reference to a transition that may have been
|
||||||
|
// reallocated.
|
||||||
|
|
||||||
if (backlink)
|
// At least one transition per cycle should have a
|
||||||
{
|
// nondeterministic copy from the original clone.
|
||||||
// Since we are closing a cycle, add
|
// We use state numbers to select it, as any cycle
|
||||||
// a non-deterministic transition from
|
// is guaranteed to have at least one transition
|
||||||
// the original automaton to this copy.
|
// with dst <= src. FIXME: Computing a feedback
|
||||||
trans* t3 =
|
// arc set would be better.
|
||||||
out_->create_transition(in, out * -num_acc_ + add);
|
if (dst <= src)
|
||||||
t3->condition = si->current_condition();
|
res->new_transition(src, dst + add, cond);
|
||||||
}
|
curacc = bdd_low(curacc);
|
||||||
ac = bdd_low(ac);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -148,26 +111,21 @@ namespace spot
|
||||||
// branch is false. In that case we take the HIGH
|
// branch is false. In that case we take the HIGH
|
||||||
// branch to enumerate all the remaining negated
|
// branch to enumerate all the remaining negated
|
||||||
// variables.
|
// variables.
|
||||||
bdd tmp = bdd_low(ac);
|
bdd tmp = bdd_low(curacc);
|
||||||
if (tmp != bddfalse)
|
if (tmp != bddfalse)
|
||||||
ac = tmp;
|
curacc = tmp;
|
||||||
else
|
else
|
||||||
ac = h;
|
curacc = h;
|
||||||
}
|
}
|
||||||
++add;
|
|
||||||
}
|
}
|
||||||
assert(add == num_acc_);
|
assert(add == num_acc * n);
|
||||||
}
|
}
|
||||||
|
// Complete the original automaton.
|
||||||
|
if (missingcond != bddfalse)
|
||||||
|
res->new_transition(src, sink, missingcond);
|
||||||
}
|
}
|
||||||
|
res->merge_transitions();
|
||||||
};
|
// FIXME: Remove unreachable states.
|
||||||
|
return res;
|
||||||
} // anonymous
|
|
||||||
|
|
||||||
tgba_explicit_number* dtgba_complement(const tgba* aut)
|
|
||||||
{
|
|
||||||
dtgbacomp_iter dci(aut);
|
|
||||||
dci.run();
|
|
||||||
return dci.result();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#ifndef SPOT_TGBAALGOS_DTGBACOMP_HH
|
#ifndef SPOT_TGBAALGOS_DTGBACOMP_HH
|
||||||
# define SPOT_TGBAALGOS_DTGBACOMP_HH
|
# define SPOT_TGBAALGOS_DTGBACOMP_HH
|
||||||
|
|
||||||
#include "tgba/tgbaexplicit.hh"
|
#include "tgba/tgbagraph.hh"
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
{
|
{
|
||||||
|
|
@ -30,7 +30,7 @@ namespace spot
|
||||||
/// to be complete. Acceptance can be transition-based, or
|
/// to be complete. Acceptance can be transition-based, or
|
||||||
/// state-based. The resulting automaton is very unlikely to be
|
/// state-based. The resulting automaton is very unlikely to be
|
||||||
/// deterministic.
|
/// deterministic.
|
||||||
SPOT_API tgba_explicit_number*
|
SPOT_API tgba_digraph*
|
||||||
dtgba_complement(const tgba* aut);
|
dtgba_complement(const tgba* aut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (C) 2013 Laboratoire de Recherche et Développement
|
# Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement
|
||||||
# de l'Epita (LRDE).
|
# de l'Epita (LRDE).
|
||||||
#
|
#
|
||||||
# This file is part of Spot, a model checking library.
|
# This file is part of Spot, a model checking library.
|
||||||
|
|
@ -72,21 +72,22 @@ EOF
|
||||||
# FIXME: we should improve this output
|
# FIXME: we should improve this output
|
||||||
cat >ex.tgba <<'EOF'
|
cat >ex.tgba <<'EOF'
|
||||||
acc = "1";
|
acc = "1";
|
||||||
|
"0", "1", "a",;
|
||||||
|
"0", "0", "!a",;
|
||||||
|
"0", "3", "!a",;
|
||||||
"1", "2", "a",;
|
"1", "2", "a",;
|
||||||
"1", "1", "!a",;
|
"1", "0", "!a",;
|
||||||
"1", "-1", "!a",;
|
"1", "3", "!a",;
|
||||||
"2", "3", "a",;
|
"3", "3", "!a", "1";
|
||||||
"2", "1", "!a",;
|
"2", "2", "a",;
|
||||||
"2", "-1", "!a",;
|
"2", "0", "!a",;
|
||||||
"-1", "-1", "!a", "1";
|
"2", "5", "a",;
|
||||||
"3", "3", "a",;
|
"2", "3", "!a",;
|
||||||
"3", "-3", "a",;
|
"5", "5", "a", "1";
|
||||||
"3", "1", "!a",;
|
"5", "3", "!a", "1";
|
||||||
"3", "-1", "!a",;
|
|
||||||
"-3", "-3", "a", "1";
|
|
||||||
"-3", "-1", "!a", "1";
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
run 0 ../ltl2tgba -b -DC -X in.tgba > out.tgba
|
run 0 ../ltl2tgba -b -DC -X in.tgba > out.tgba
|
||||||
diff out.tgba ex.tgba
|
diff out.tgba ex.tgba
|
||||||
|
|
||||||
|
|
@ -96,15 +97,15 @@ cat >ex.tgba <<EOF
|
||||||
digraph G {
|
digraph G {
|
||||||
0 [label="", style=invis, height=0]
|
0 [label="", style=invis, height=0]
|
||||||
0 -> 1
|
0 -> 1
|
||||||
1 [label="1"]
|
1 [label="0"]
|
||||||
1 -> 2 [label="1\n"]
|
1 -> 2 [label="1\n"]
|
||||||
2 [label="2"]
|
2 [label="1"]
|
||||||
2 -> 2 [label="1\n"]
|
2 -> 2 [label="1\n"]
|
||||||
2 -> 3 [label="!a\n"]
|
2 -> 3 [label="!a\n"]
|
||||||
2 -> 4 [label="!b\n"]
|
2 -> 4 [label="!b\n"]
|
||||||
3 [label="-4"]
|
3 [label="3"]
|
||||||
3 -> 3 [label="!a\n{Acc[1]}"]
|
3 -> 3 [label="!a\n{Acc[1]}"]
|
||||||
4 [label="-3"]
|
4 [label="5"]
|
||||||
4 -> 4 [label="!b\n{Acc[1]}"]
|
4 -> 4 [label="!b\n{Acc[1]}"]
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue