ltlcross: support --products=+N

* src/bin/ltlcross.cc: Implement it.
* NEWS, doc/org/ltlcross.org: Document it.
* src/tgbatest/ltlcross3.test: Test it.
This commit is contained in:
Alexandre Duret-Lutz 2013-11-21 22:31:59 +01:00
parent 9577e5d528
commit 2b10745dfb
4 changed files with 145 additions and 68 deletions

3
NEWS
View file

@ -14,6 +14,9 @@ New in spot 1.2a (not released)
area left empty (in CVS) or null (in JSON). A new option, area left empty (in CVS) or null (in JSON). A new option,
--omit-missing can be used to remove lines for failed --omit-missing can be used to remove lines for failed
translations, and remove these two columns. translations, and remove these two columns.
- if ltlcross is used with --products=+5 instead of --products=5
then the stastics for each of the five products will be output
separately instead of being averaged.
* Bug fixes: * Bug fixes:
- ltlcross' CSV output now stricly follows RFC 4180. - ltlcross' CSV output now stricly follows RFC 4180.
- ltlcross failed to report missing input or output escape sequences - ltlcross failed to report missing input or output escape sequences

View file

@ -444,6 +444,14 @@ is used, =N= products are builds instead of one, and the fields
=product_states=, =product_transitions=, and =product_scc= contain =product_states=, =product_transitions=, and =product_scc= contain
average values. average values.
If the option =--products=+N= is used (with a =+= in front of the
number), then no average value is computed. Instead, three columns
=product_states=, =product_transitions=, and =product_scc= are output
for each individual product (i.e., $3\times N$ columns are output).
This might be useful if you want to compute different kind of
statistic (e.g., a median instead of a mean) or if you want to build
scatter plots of all these products.
** Changing the name of the translators ** Changing the name of the translators
By default, the names used in the CSV and JSON output to designate the By default, the names used in the CSV and JSON output to designate the

View file

@ -142,9 +142,9 @@ static const argp_option options[] =
"two states (0.1 by default)", 0 }, "two states (0.1 by default)", 0 },
{ "seed", OPT_SEED, "INT", 0, { "seed", OPT_SEED, "INT", 0,
"seed for the random number generator (0 by default)", 0 }, "seed for the random number generator (0 by default)", 0 },
{ "products", OPT_PRODUCTS, "INT", 0, { "products", OPT_PRODUCTS, "[+]INT", 0,
"number of product to perform (1 by default), statistics will be " "number of products to perform (1 by default), statistics will be "
"averaged", 0 }, "averaged unless the number is prefixed with '+'", 0 },
/**************************************************/ /**************************************************/
{ 0, 0, 0, 0, "Statistics output:", 6 }, { 0, 0, 0, 0, "Statistics output:", 6 },
{ "json", OPT_JSON, "FILENAME", OPTION_ARG_OPTIONAL, { "json", OPT_JSON, "FILENAME", OPTION_ARG_OPTIONAL,
@ -203,6 +203,7 @@ bool no_complement = false;
bool stop_on_error = false; bool stop_on_error = false;
int seed = 0; int seed = 0;
unsigned products = 1; unsigned products = 1;
bool products_avg = true;
bool opt_omit = false; bool opt_omit = false;
struct translator_spec struct translator_spec
@ -306,10 +307,7 @@ struct statistics
nondeterministic(false), nondeterministic(false),
terminal_aut(false), terminal_aut(false),
weak_aut(false), weak_aut(false),
strong_aut(false), strong_aut(false)
product_states(0),
product_transitions(0),
product_scc(0)
{ {
} }
@ -333,9 +331,9 @@ struct statistics
bool terminal_aut; bool terminal_aut;
bool weak_aut; bool weak_aut;
bool strong_aut; bool strong_aut;
double product_states; std::vector<double> product_states;
double product_transitions; std::vector<double> product_transitions;
double product_scc; std::vector<double> product_scc;
static void static void
fields(std::ostream& os, bool all) fields(std::ostream& os, bool all)
@ -356,10 +354,10 @@ struct statistics
"\"nondet_aut\"," "\"nondet_aut\","
"\"terminal_aut\"," "\"terminal_aut\","
"\"weak_aut\"," "\"weak_aut\","
"\"strong_aut\"," "\"strong_aut\"");
"\"product_states\"," size_t m = products_avg ? 1U : products;
"\"product_transitions\"," for (size_t i = 0; i < m; ++i)
"\"product_scc\""); os << ",\"product_states\",\"product_transitions\",\"product_scc\"";
} }
void void
@ -369,41 +367,64 @@ struct statistics
os << '"' << status_str << "\"," << status_code << ','; os << '"' << status_str << "\"," << status_code << ',';
os << time << ','; os << time << ',';
if (ok) if (ok)
os << states << ',' {
<< edges << ',' os << states << ','
<< transitions << ',' << edges << ','
<< acc << ',' << transitions << ','
<< scc << ',' << acc << ','
<< nonacc_scc << ',' << scc << ','
<< terminal_scc << ',' << nonacc_scc << ','
<< weak_scc << ',' << terminal_scc << ','
<< strong_scc << ',' << weak_scc << ','
<< nondetstates << ',' << strong_scc << ','
<< nondeterministic << ',' << nondetstates << ','
<< terminal_aut << ',' << nondeterministic << ','
<< weak_aut << ',' << terminal_aut << ','
<< strong_aut << ',' << weak_aut << ','
<< product_states << ',' << strong_aut;
<< product_transitions << ',' if (!products_avg)
<< product_scc; {
for (size_t i = 0; i < products; ++i)
os << ',' << product_states[i]
<< ',' << product_transitions[i]
<< ',' << product_scc[i];
}
else
{
double st = 0.0;
double tr = 0.0;
double sc = 0.0;
for (size_t i = 0; i < products; ++i)
{
st += product_states[i];
tr += product_transitions[i];
sc += product_scc[i];
}
os << ',' << (st / products)
<< ',' << (tr / products)
<< ',' << (sc / products);
}
}
else else
os << na << ',' {
<< na << ',' os << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na << ','
<< na << ',' << na;
<< na << ',' size_t m = products_avg ? 1U : products;
<< na; for (size_t i = 0; i < m; ++i)
os << ',' << na << ',' << na << ',' << na;
}
} }
}; };
@ -492,6 +513,11 @@ parse_opt(int key, char* arg, struct argp_state*)
json_output = arg ? arg : "-"; json_output = arg ? arg : "-";
break; break;
case OPT_PRODUCTS: case OPT_PRODUCTS:
if (*arg == '+')
{
products_avg = false;
++arg;
}
products = to_pos_int(arg); products = to_pos_int(arg);
break; break;
case OPT_NOCHECKS: case OPT_NOCHECKS:
@ -1313,6 +1339,19 @@ namespace
spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f); spot::ltl::atomic_prop_set* ap = spot::ltl::atomic_prop_collect(f);
f->destroy(); f->destroy();
if (want_stats)
for (size_t i = 0; i < m; ++i)
{
(*pstats)[i].product_states.reserve(products);
(*pstats)[i].product_transitions.reserve(products);
(*pstats)[i].product_scc.reserve(products);
if (neg[i])
{
(*nstats)[i].product_states.reserve(products);
(*nstats)[i].product_transitions.reserve(products);
(*nstats)[i].product_scc.reserve(products);
}
}
for (unsigned p = 0; p < products; ++p) for (unsigned p = 0; p < products; ++p)
{ {
// build a random state-space. // build a random state-space.
@ -1339,14 +1378,13 @@ namespace
// Statistics // Statistics
if (want_stats) if (want_stats)
{ {
(*pstats)[i].product_scc += sm->scc_count(); (*pstats)[i].product_scc.push_back(sm->scc_count());
spot::tgba_statistics s = spot::stats_reachable(p); spot::tgba_statistics s = spot::stats_reachable(p);
(*pstats)[i].product_states += s.states; (*pstats)[i].product_states.push_back(s.states);
(*pstats)[i].product_transitions += s.transitions; (*pstats)[i].product_transitions.push_back(s.transitions);
} }
} }
if (!no_checks) if (!no_checks)
for (size_t i = 0; i < m; ++i) for (size_t i = 0; i < m; ++i)
if (neg[i]) if (neg[i])
@ -1360,10 +1398,10 @@ namespace
// Statistics // Statistics
if (want_stats) if (want_stats)
{ {
(*nstats)[i].product_scc += sm->scc_count(); (*nstats)[i].product_scc.push_back(sm->scc_count());
spot::tgba_statistics s = spot::stats_reachable(p); spot::tgba_statistics s = spot::stats_reachable(p);
(*nstats)[i].product_states += s.states; (*nstats)[i].product_states.push_back(s.states);
(*nstats)[i].product_transitions += s.transitions; (*nstats)[i].product_transitions.push_back(s.transitions);
} }
} }
@ -1391,29 +1429,16 @@ namespace
} }
// Cleanup. // Cleanup.
if (!no_checks) if (!no_checks)
for (size_t i = 0; i < m; ++i) for (size_t i = 0; i < m; ++i)
{ {
delete neg_map[i]; delete neg_map[i];
delete neg_prod[i]; delete neg_prod[i];
if (want_stats)
{
(*nstats)[i].product_scc /= products;
(*nstats)[i].product_states /= products;
(*nstats)[i].product_transitions /= products;
}
} }
for (size_t i = 0; i < m; ++i) for (size_t i = 0; i < m; ++i)
{ {
delete pos_map[i]; delete pos_map[i];
delete pos_prod[i]; delete pos_prod[i];
if (want_stats)
{
(*pstats)[i].product_scc /= products;
(*pstats)[i].product_states /= products;
(*pstats)[i].product_transitions /= products;
}
} }
delete statespace; delete statespace;
++seed; ++seed;

View file

@ -70,3 +70,44 @@ grep '"exit_code"' out.csv && exit 1
test `grep 'warning:.*timeout' stderr | wc -l` -eq 2 test `grep 'warning:.*timeout' stderr | wc -l` -eq 2
test `wc -l < out.csv` -eq 1 test `wc -l < out.csv` -eq 1
check_csv out.csv check_csv out.csv
# Check with --products=5
run 1 ../../bin/ltlcross "$ltl2tgba -s %f >%N" 'false %f >%N' \
-f a --csv=out.csv --products=5 2>stderr
p=`sed 's/[^,]//g;q' out.csv | wc -c`
grep '"exit_status"' out.csv
grep '"exit_code"' out.csv
test `grep 'error:.*returned exit code 1' stderr | wc -l` -eq 2
test `grep '"exit code",1' out.csv | wc -l` -eq 2
check_csv out.csv
# ... unless --omit-missing is supplied.
run 1 ../../bin/ltlcross "$ltl2tgba -s %f >%N" 'false %f >%N' \
-f a --csv=out.csv --omit-missing --products=5 2>stderr
grep '"exit_status"' out.csv && exit 1
grep '"exit_code"' out.csv && exit 1
test `grep 'error:.*returned exit code 1' stderr | wc -l` -eq 2
test `grep '"exit code",1' out.csv | wc -l` -eq 0
check_csv out.csv
# Check with --products=+5
run 1 ../../bin/ltlcross "$ltl2tgba -s %f >%N" 'false %f >%N' \
-f a --csv=out.csv --products=+5 2>stderr
q=`sed 's/[^,]//g;q' out.csv | wc -c`
grep '"exit_status"' out.csv
grep '"exit_code"' out.csv
test `grep 'error:.*returned exit code 1' stderr | wc -l` -eq 2
test `grep '"exit code",1' out.csv | wc -l` -eq 2
check_csv out.csv
# ... unless --omit-missing is supplied.
run 1 ../../bin/ltlcross "$ltl2tgba -s %f >%N" 'false %f >%N' \
-f a --csv=out.csv --omit-missing --products=+5 2>stderr
grep '"exit_status"' out.csv && exit 1
grep '"exit_code"' out.csv && exit 1
test `grep 'error:.*returned exit code 1' stderr | wc -l` -eq 2
test `grep '"exit code",1' out.csv | wc -l` -eq 0
check_csv out.csv
test $q -eq `expr $p + 12`