bin: new ltlmix tool
Fixes #400. * spot/tl/randomltl.cc, spot/tl/randomltl.hh: Adjust to accept a set of formula to replace the atomic propositions. * bin/ltlmix.cc: New file. * bin/Makefile.am: Add it. * bin/man/ltlmix.x: New file. * bin/man/Makefile.am: Add it. * doc/org/ltlmix.org: New file. * doc/Makefile.am: Add it. * bin/man/genltl.x, bin/man/randltl.x, bin/man/spot.x, bin/spot.cc, doc/org/arch.tex, doc/org/concepts.org, doc/org/tools.org, NEWS: Mention ltlmix. * tests/core/ltlmix.test: New file. * tests/Makefile.am: Add it.
This commit is contained in:
parent
baf2778c9a
commit
c8b8ac60be
18 changed files with 995 additions and 87 deletions
|
|
@ -69,6 +69,7 @@ bin_PROGRAMS = \
|
|||
ltldo \
|
||||
ltlfilt \
|
||||
ltlgrind \
|
||||
ltlmix \
|
||||
ltlsynt \
|
||||
randaut \
|
||||
randltl
|
||||
|
|
@ -92,6 +93,7 @@ ltl2tgta_SOURCES = ltl2tgta.cc
|
|||
ltlcross_SOURCES = ltlcross.cc
|
||||
ltlgrind_SOURCES = ltlgrind.cc
|
||||
ltldo_SOURCES = ltldo.cc
|
||||
ltlmix_SOURCES = ltlmix.cc
|
||||
ltlsynt_SOURCES = ltlsynt.cc
|
||||
dstar2tgba_SOURCES = dstar2tgba.cc
|
||||
spot_x_SOURCES = spot-x.cc
|
||||
|
|
|
|||
302
bin/ltlmix.cc
Normal file
302
bin/ltlmix.cc
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) by the Spot authors, see the AUTHORS file for details.
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include "common_sys.hh"
|
||||
#include <argp.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_output.hh"
|
||||
#include "common_conv.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_range.hh"
|
||||
|
||||
#include <spot/tl/defaultenv.hh>
|
||||
#include <spot/tl/randomltl.hh>
|
||||
#include <spot/misc/random.hh>
|
||||
|
||||
enum {
|
||||
OPT_BOOLEAN_PRIORITIES = 256,
|
||||
OPT_DUMP_PRIORITIES,
|
||||
OPT_DUPS,
|
||||
OPT_LTL_PRIORITIES,
|
||||
OPT_SEED,
|
||||
OPT_TREE_SIZE,
|
||||
};
|
||||
|
||||
static const char * argp_program_doc =
|
||||
"Combine formulas taken randomly from an input set.\n\n\
|
||||
The input set is specified using FILENAME, -F FILENAME, or -f formula.\n\
|
||||
By default this generates a Boolean pattern of size 5, for instance\n\
|
||||
\"(φ₁ & φ₂) | φ₃\", where each φᵢ is randomly taken from the input set.\n\
|
||||
The size and nature of the pattern can be changed using generation\n\
|
||||
parameters. Additionally, it is possible to rename the atomic propositions\n\
|
||||
in each φᵢ using -A or -P.\v\
|
||||
Example:\n\
|
||||
\n\
|
||||
Generates 10 random Boolean combinations of terms of the form GFa, with\n\
|
||||
'a' picked from a set of 5 atomic propositions:\n\
|
||||
% ltlmix -f GFa -n10 -A5\n\
|
||||
\n\
|
||||
Build a single LTL formula over subformulas taken randomly from the list of\n\
|
||||
55 patterns by Dwyer et al., using a choice of 10 atomic propositions to\n\
|
||||
relabel subformulas:\n\
|
||||
% genltl --dac | ltlmix -L -A10\n\
|
||||
\n\
|
||||
Build 5 random positive Boolean combination of GFa and GFb:\n"
|
||||
// next line is in its own double-quote to please sanity.test
|
||||
" % ltlmix -f GFa -f GFb --boolean-prio=not=0,xor=0,implies=0,equiv=0 -n5";
|
||||
|
||||
static const argp_option options[] = {
|
||||
// Keep this alphabetically sorted (expect for aliases).
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Generation parameters:", 2 },
|
||||
{ "ap-count", 'A', "N", 0,
|
||||
"rename the atomic propositions in each selected formula by drawing "
|
||||
"randomly from N atomic propositions (the rewriting is bijective "
|
||||
"if N is larger than the original set)", 0 },
|
||||
{ "polarized-ap", 'P', "N", 0,
|
||||
"similar to -A N, but randomize the polarity of the new atomic "
|
||||
"proposition", 0 },
|
||||
{ "boolean", 'B', nullptr, 0,
|
||||
"generate Boolean combination of formulas (default)", 0 },
|
||||
{ "allow-dups", OPT_DUPS, nullptr, 0,
|
||||
"allow duplicate formulas to be output", 0 },
|
||||
{ "ltl", 'L', nullptr, 0, "generate LTL combinations of subformulas", 0 },
|
||||
{ "formulas", 'n', "INT", 0,
|
||||
"number of formulas to generate (default: 1);\n"
|
||||
"use a negative value for unbounded generation", 0 },
|
||||
{ "seed", OPT_SEED, "INT", 0,
|
||||
"seed for the random number generator (default: 0)", 0 },
|
||||
{ "tree-size", OPT_TREE_SIZE, "RANGE", 0,
|
||||
"tree size of main pattern generated (default: 5);\n"
|
||||
"input formulas count as size 1.", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Adjusting probabilities:", 4 },
|
||||
{ "dump-priorities", OPT_DUMP_PRIORITIES, nullptr, 0,
|
||||
"show current priorities, do not generate any formula", 0 },
|
||||
{ "ltl-priorities", OPT_LTL_PRIORITIES, "STRING", 0,
|
||||
"set priorities for LTL formulas", 0 },
|
||||
{ "boolean-priorities", OPT_BOOLEAN_PRIORITIES, "STRING", 0,
|
||||
"set priorities for Boolean formulas", 0 },
|
||||
{ nullptr, 0, nullptr, 0, "STRING should be a comma-separated list of "
|
||||
"assignments, assigning integer priorities to the tokens "
|
||||
"listed by --dump-priorities.", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Output options:", -20 },
|
||||
{ nullptr, 0, nullptr, 0, "The FORMAT string passed to --format may use "
|
||||
"the following interpreted sequences:", -19 },
|
||||
{ "%f", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the formula (in the selected syntax)", 0 },
|
||||
{ "%l", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the (serial) number of the formula (0-based)", 0 },
|
||||
{ "%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"the (serial) number of the formula (1-based)", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"a single %", 0 },
|
||||
COMMON_LTL_OUTPUT_SPECS,
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static const argp_child children[] = {
|
||||
{ &finput_argp, 0, nullptr, 0 },
|
||||
{ &output_argp, 0, nullptr, 0 },
|
||||
{ &misc_argp, 0, nullptr, 0 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int opt_formulas = 1;
|
||||
static spot::randltlgenerator::output_type output =
|
||||
spot::randltlgenerator::Bool;
|
||||
static char* opt_pL = nullptr;
|
||||
static char* opt_pB = nullptr;
|
||||
static bool opt_dump_priorities = false;
|
||||
static int opt_seed = 0;
|
||||
static range opt_tree_size = { 5, 5 };
|
||||
static bool opt_unique = true;
|
||||
static int opt_ap_count = 0;
|
||||
static bool opt_literal = false;
|
||||
|
||||
namespace
|
||||
{
|
||||
// We want all these variables to be destroyed when we exit main, to
|
||||
// make sure it happens before all other global variables (like the
|
||||
// atomic propositions maps) are destroyed. Otherwise we risk
|
||||
// accessing deleted stuff.
|
||||
static struct opt_t
|
||||
{
|
||||
spot::atomic_prop_set sub;
|
||||
}* opt = nullptr;
|
||||
|
||||
class sub_processor final: public job_processor
|
||||
{
|
||||
public:
|
||||
int
|
||||
process_formula(spot::formula f, const char* filename = nullptr,
|
||||
int linenum = 0) override
|
||||
{
|
||||
(void) filename;
|
||||
(void) linenum;
|
||||
opt->sub.insert(f);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static sub_processor subreader;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
// Called from C code, so should not raise any exception.
|
||||
BEGIN_EXCEPTION_PROTECT;
|
||||
switch (key)
|
||||
{
|
||||
case 'A':
|
||||
opt_ap_count = to_int(arg, "-A/--ap-count");
|
||||
opt_literal = false;
|
||||
break;
|
||||
case 'B':
|
||||
output = spot::randltlgenerator::Bool;
|
||||
break;
|
||||
case 'L':
|
||||
output = spot::randltlgenerator::LTL;
|
||||
break;
|
||||
case 'n':
|
||||
opt_formulas = to_int(arg, "-n/--formulas");
|
||||
break;
|
||||
case 'P':
|
||||
opt_ap_count = to_int(arg, "-P/--polarized-ap");
|
||||
opt_literal = true;
|
||||
break;
|
||||
case OPT_BOOLEAN_PRIORITIES:
|
||||
opt_pB = arg;
|
||||
break;
|
||||
case OPT_LTL_PRIORITIES:
|
||||
opt_pL = arg;
|
||||
break;
|
||||
case OPT_DUMP_PRIORITIES:
|
||||
opt_dump_priorities = true;
|
||||
break;
|
||||
case OPT_DUPS:
|
||||
opt_unique = false;
|
||||
break;
|
||||
case OPT_SEED:
|
||||
opt_seed = to_int(arg, "--seed");
|
||||
break;
|
||||
case OPT_TREE_SIZE:
|
||||
opt_tree_size = parse_range(arg);
|
||||
if (opt_tree_size.min > opt_tree_size.max)
|
||||
std::swap(opt_tree_size.min, opt_tree_size.max);
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
jobs.emplace_back(arg, job_type::LTL_FILENAME);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
END_EXCEPTION_PROTECT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
return protected_main(argv, [&] {
|
||||
const argp ap = { options, parse_opt, "[FILENAME[/COL]...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
// This will ensure that all objects stored in this struct are
|
||||
// destroyed before global variables.
|
||||
opt_t o;
|
||||
opt = &o;
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
check_no_formula("combine");
|
||||
|
||||
if (subreader.run())
|
||||
return 2;
|
||||
|
||||
if (opt->sub.empty())
|
||||
error(2, 0, "the set of subformulas to build from is empty");
|
||||
|
||||
spot::srand(opt_seed);
|
||||
|
||||
spot::randltlgenerator rg
|
||||
(opt_ap_count,
|
||||
[&] (){
|
||||
spot::option_map opts;
|
||||
opts.set("output", output);
|
||||
opts.set("tree_size_min", opt_tree_size.min);
|
||||
opts.set("tree_size_max", opt_tree_size.max);
|
||||
opts.set("seed", opt_seed);
|
||||
opts.set("simplification_level", 0);
|
||||
opts.set("unique", opt_unique);
|
||||
opts.set("literals", opt_literal);
|
||||
return opts;
|
||||
}(), opt_pL, nullptr, opt_pB, &opt->sub);
|
||||
|
||||
if (opt_dump_priorities)
|
||||
{
|
||||
switch (output)
|
||||
{
|
||||
case spot::randltlgenerator::LTL:
|
||||
std::cout <<
|
||||
"Use --ltl-priorities to set the following LTL priorities:\n";
|
||||
rg.dump_ltl_priorities(std::cout);
|
||||
break;
|
||||
case spot::randltlgenerator::Bool:
|
||||
std::cout <<
|
||||
"Use --boolean-priorities to set the following Boolean "
|
||||
"formula priorities:\n";
|
||||
rg.dump_bool_priorities(std::cout);
|
||||
break;
|
||||
case spot::randltlgenerator::PSL:
|
||||
case spot::randltlgenerator::SERE:
|
||||
error(2, 0, "PSL/SERE output is unsupported");
|
||||
break;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
while (opt_formulas < 0 || opt_formulas--)
|
||||
{
|
||||
spot::formula f = rg.next();
|
||||
if (!f)
|
||||
{
|
||||
error(2, 0, "failed to generate a new unique formula after %d " \
|
||||
"trials", spot::randltlgenerator::MAX_TRIALS);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_formula_checked(f, nullptr, nullptr, count + 1, count);
|
||||
++count;
|
||||
}
|
||||
};
|
||||
|
||||
flush_cout();
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ dist_man1_MANS = \
|
|||
ltldo.1 \
|
||||
ltlfilt.1 \
|
||||
ltlgrind.1 \
|
||||
ltlmix.1 \
|
||||
ltlsynt.1 \
|
||||
randaut.1 \
|
||||
randltl.1
|
||||
|
|
@ -72,6 +73,9 @@ ltlfilt.1: $(common_dep) $(srcdir)/ltlfilt.x $(srcdir)/../ltlfilt.cc
|
|||
ltlgrind.1: $(common_dep) $(srcdir)/ltlgrind.x $(srcdir)/../ltlgrind.cc
|
||||
$(convman) ../ltlgrind$(EXEEXT) $(srcdir)/ltlgrind.x $@
|
||||
|
||||
ltlmix.1: $(common_dep) $(srcdir)/ltlmix.x $(srcdir)/../ltlmix.cc
|
||||
$(convman) ../ltlmix$(EXEEXT) $(srcdir)/ltlmix.x $@
|
||||
|
||||
ltlsynt.1: $(common_dep) $(srcdir)/ltlsynt.x $(srcdir)/../ltlsynt.cc
|
||||
$(convman) ../ltlsynt$(EXEEXT) $(srcdir)/ltlsynt.x $@
|
||||
|
||||
|
|
|
|||
|
|
@ -80,3 +80,4 @@ Proceedings of RV'10. LNCS 6418.
|
|||
.BR ltlfilt (1),
|
||||
.BR randaut (1),
|
||||
.BR randltl (1)
|
||||
.BR ltlmix (1)
|
||||
|
|
|
|||
7
bin/man/ltlmix.x
Normal file
7
bin/man/ltlmix.x
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[NAME]
|
||||
ltlmix \- combine formulas selected randomly
|
||||
[DESCRIPTION]
|
||||
.\" Add any additional description here
|
||||
[SEE ALSO]
|
||||
.BR randltl (1),
|
||||
.BR genltl (1)
|
||||
|
|
@ -14,3 +14,4 @@ Proceedings of ATVA'13. LNCS 8172.
|
|||
.BR genltl (1),
|
||||
.BR ltlfilt (1),
|
||||
.BR randaut (1)
|
||||
.BR ltlmix (1)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ that are listed below.
|
|||
.BR ltldo (1)
|
||||
.BR ltlfilt (1)
|
||||
.BR ltlgrind (1)
|
||||
.BR ltlmix (1)
|
||||
.BR ltlsynt (1)
|
||||
.BR randaut (1)
|
||||
.BR randltl (1)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ static const argp_option options[] =
|
|||
{ DOC("ltlgrind", "Mutate LTL or PSL formulas to generate similar but "
|
||||
"simpler ones. Use this when looking for shorter formula to "
|
||||
"reproduce a bug.") },
|
||||
{ DOC("ltlmix",
|
||||
"Combine LTL/PSL formulas taken randomly from some input set.") },
|
||||
{ nullptr, 0, nullptr, 0, "Tools that output automata or circuits:", 0 },
|
||||
{ DOC("randaut", "Generate random ω-automata.") },
|
||||
{ DOC("genaut", "Generate ω-automata from scalable patterns.") },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue