ltlfilt, ltlsynt, ltlmix: add a --part-file option
* bin/common_ioap.cc, bin/common_ioap.hh (read_part_file): New function. * bin/ltlfilt.cc, bin/ltlmix.cc, bin/ltlsynt.cc: Use it. * doc/org/ltlfilt.org, doc/org/ltlmix.org, doc/org/ltlsynt.org: Mention that new option, and improve the links to its description in ltlsynt.org. * NEWS: Mention the new option. * tests/core/ltlfilt.test, tests/core/ltlmix.test, tests/core/ltlsynt.test: Adjust test cases.
This commit is contained in:
parent
388d005635
commit
e6ebbdf65f
12 changed files with 271 additions and 138 deletions
9
NEWS
9
NEWS
|
|
@ -2,9 +2,13 @@ New in spot 2.12.0.dev (not yet released)
|
||||||
|
|
||||||
Command-line tools:
|
Command-line tools:
|
||||||
|
|
||||||
- ltlmix is a new tool that generate formulas by combining existing
|
- ltlmix is a new tool that generates formulas by combining existing
|
||||||
ones. See https://spot.lre.epita.fr/ltlmix.html for examples.
|
ones. See https://spot.lre.epita.fr/ltlmix.html for examples.
|
||||||
|
|
||||||
|
- ltlsynt learned a --part-file option, to specify the partition of
|
||||||
|
input/output proposition from a *.part file, as used in several
|
||||||
|
other tools.
|
||||||
|
|
||||||
- ltlfilt learned a --relabel=io mode, that is useful to shorten
|
- ltlfilt learned a --relabel=io mode, that is useful to shorten
|
||||||
atomic propositions in the context of LTL synthesis. For instance
|
atomic propositions in the context of LTL synthesis. For instance
|
||||||
|
|
||||||
|
|
@ -13,7 +17,8 @@ New in spot 2.12.0.dev (not yet released)
|
||||||
|
|
||||||
The resulting formulas are now usable by ltlsynt without having to
|
The resulting formulas are now usable by ltlsynt without having to
|
||||||
specify which atomic propositions are input or output, as this can
|
specify which atomic propositions are input or output, as this can
|
||||||
be inferred from their name.
|
be inferred from their name. (This suspports a --part-file option
|
||||||
|
as well.)
|
||||||
|
|
||||||
- genltl learned --lily-patterns to generate the example LTL
|
- genltl learned --lily-patterns to generate the example LTL
|
||||||
synthesis specifications from Lily 1.0.2. Those come with input
|
synthesis specifications from Lily 1.0.2. Those come with input
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include "common_ioap.hh"
|
#include "common_ioap.hh"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include <fstream>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
// --ins and --outs, as supplied on the command-line
|
// --ins and --outs, as supplied on the command-line
|
||||||
|
|
@ -31,6 +32,8 @@ std::vector<std::regex> regex_out;
|
||||||
// map identifier to input/output (false=input, true=output)
|
// map identifier to input/output (false=input, true=output)
|
||||||
std::unordered_map<std::string, bool> identifier_map;
|
std::unordered_map<std::string, bool> identifier_map;
|
||||||
|
|
||||||
|
static bool a_part_file_was_read = false;
|
||||||
|
|
||||||
static std::string
|
static std::string
|
||||||
str_tolower(std::string s)
|
str_tolower(std::string s)
|
||||||
{
|
{
|
||||||
|
|
@ -71,7 +74,10 @@ void process_io_options()
|
||||||
regex_out.push_back(std::regex(f.substr(1, sz - 2)));
|
regex_out.push_back(std::regex(f.substr(1, sz - 2)));
|
||||||
else if (auto [it, is_new] = identifier_map.try_emplace(f, true);
|
else if (auto [it, is_new] = identifier_map.try_emplace(f, true);
|
||||||
!is_new && !it->second)
|
!is_new && !it->second)
|
||||||
error(2, 0, "'%s' appears in both --ins and --outs",
|
error(2, 0,
|
||||||
|
a_part_file_was_read ?
|
||||||
|
"'%s' appears in both inputs and outputs" :
|
||||||
|
"'%s' appears in both --ins and --outs",
|
||||||
f.c_str());
|
f.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,19 +131,23 @@ is_output(const std::string& a, const char* filename, int linenum)
|
||||||
}
|
}
|
||||||
if (found_in && found_out)
|
if (found_in && found_out)
|
||||||
error_at_line(2, 0, filename, linenum,
|
error_at_line(2, 0, filename, linenum,
|
||||||
|
a_part_file_was_read ?
|
||||||
|
"'%s' matches both inputs and outputs" :
|
||||||
"'%s' matches both --ins and --outs",
|
"'%s' matches both --ins and --outs",
|
||||||
a.c_str());
|
a.c_str());
|
||||||
if (!found_in && !found_out)
|
if (!found_in && !found_out)
|
||||||
{
|
{
|
||||||
if (all_input_aps.has_value() || all_output_aps.has_value())
|
if (all_input_aps.has_value() || all_output_aps.has_value())
|
||||||
error_at_line(2, 0, filename, linenum,
|
error_at_line(2, 0, filename, linenum,
|
||||||
|
a_part_file_was_read ?
|
||||||
|
"'%s' does not match any input or output" :
|
||||||
"one of --ins or --outs should match '%s'",
|
"one of --ins or --outs should match '%s'",
|
||||||
a.c_str());
|
a.c_str());
|
||||||
else
|
else
|
||||||
error_at_line(2, 0, filename, linenum,
|
error_at_line(2, 0, filename, linenum,
|
||||||
"since '%s' does not start with 'i' or 'o', "
|
"since '%s' does not start with 'i' or 'o', "
|
||||||
"it is unclear if it is an input or "
|
"it is unclear if it is an input or "
|
||||||
"an output;\n use --ins or --outs",
|
"an output;\n use --ins, --outs, or --part-file",
|
||||||
a.c_str());
|
a.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,3 +210,59 @@ spot::formula relabel_io(spot::formula f, spot::relabeling_map& fro,
|
||||||
}
|
}
|
||||||
return spot::relabel_apply(f, &to);
|
return spot::relabel_apply(f, &to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read FILENAME as a ".part" file. It should
|
||||||
|
// contains lines of text of the following form:
|
||||||
|
//
|
||||||
|
// .inputs IN1 IN2 IN3...
|
||||||
|
// .outputs OUT1 OUT2 OUT3...
|
||||||
|
void read_part_file(const char* filename)
|
||||||
|
{
|
||||||
|
std::ifstream in(filename);
|
||||||
|
if (!in)
|
||||||
|
error(2, errno, "cannot open '%s'", filename);
|
||||||
|
|
||||||
|
// This parsing is inspired from Lily's parser for .part files. We
|
||||||
|
// read words one by one, and change the "mode" if we the word is
|
||||||
|
// ".inputs" or ".outputs". A '#' introduce a comment until the end
|
||||||
|
// of the line.
|
||||||
|
std::string word;
|
||||||
|
enum { Unknown, Input, Output } mode = Unknown;
|
||||||
|
while (in >> word)
|
||||||
|
{
|
||||||
|
// The benchmarks for Syft use ".inputs:" instead of ".inputs".
|
||||||
|
if (word == ".inputs" || word == ".inputs:")
|
||||||
|
{
|
||||||
|
mode = Input;
|
||||||
|
if (!all_input_aps.has_value())
|
||||||
|
all_input_aps.emplace();
|
||||||
|
}
|
||||||
|
// The benchmarks for Syft use ".outputs:" instead of ".outputs".
|
||||||
|
else if (word == ".outputs" || word == ".outputs:")
|
||||||
|
{
|
||||||
|
mode = Output;
|
||||||
|
if (!all_output_aps.has_value())
|
||||||
|
all_output_aps.emplace();
|
||||||
|
}
|
||||||
|
else if (word[0] == '#')
|
||||||
|
{
|
||||||
|
// Skip the rest of the line.
|
||||||
|
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||||
|
}
|
||||||
|
else if (mode == Unknown)
|
||||||
|
{
|
||||||
|
error_at_line(2, 0, filename, 0,
|
||||||
|
"expected '.inputs' or '.outputs' instead of '%s'",
|
||||||
|
word.c_str());
|
||||||
|
}
|
||||||
|
else if (mode == Input)
|
||||||
|
{
|
||||||
|
all_input_aps->push_back(str_tolower(word));
|
||||||
|
}
|
||||||
|
else /* mode == Output */
|
||||||
|
{
|
||||||
|
all_output_aps->push_back(str_tolower(word));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a_part_file_was_read = true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,3 +64,6 @@ filter_list_of_aps(spot::formula f, const char* filename, int linenum);
|
||||||
// Relabel APs incrementally, based on i/o class.
|
// Relabel APs incrementally, based on i/o class.
|
||||||
spot::formula relabel_io(spot::formula f, spot::relabeling_map& fro,
|
spot::formula relabel_io(spot::formula f, spot::relabeling_map& fro,
|
||||||
const char* filename, int linenum);
|
const char* filename, int linenum);
|
||||||
|
|
||||||
|
// Read a .part file.
|
||||||
|
void read_part_file(const char* filename);
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ enum {
|
||||||
OPT_SYNTACTIC_SI,
|
OPT_SYNTACTIC_SI,
|
||||||
OPT_TO_DELTA2,
|
OPT_TO_DELTA2,
|
||||||
OPT_OUTS,
|
OPT_OUTS,
|
||||||
|
OPT_PART_FILE,
|
||||||
OPT_UNABBREVIATE,
|
OPT_UNABBREVIATE,
|
||||||
OPT_UNIVERSAL,
|
OPT_UNIVERSAL,
|
||||||
};
|
};
|
||||||
|
|
@ -187,6 +188,9 @@ static const argp_option options[] =
|
||||||
{ "outs", OPT_OUTS, "PROPS", 0,
|
{ "outs", OPT_OUTS, "PROPS", 0,
|
||||||
"comma-separated list of output atomic propositions to use with "
|
"comma-separated list of output atomic propositions to use with "
|
||||||
"--relabel=io, interpreted as a regex if enclosed in slashes", 0 },
|
"--relabel=io, interpreted as a regex if enclosed in slashes", 0 },
|
||||||
|
{ "part-file", OPT_PART_FILE, "FILENAME", 0,
|
||||||
|
"file containing the partition of atomic propositions to use with "
|
||||||
|
"--relabel=io", 0 },
|
||||||
DECLARE_OPT_R,
|
DECLARE_OPT_R,
|
||||||
LEVEL_DOC(4),
|
LEVEL_DOC(4),
|
||||||
/**************************************************/
|
/**************************************************/
|
||||||
|
|
@ -516,11 +520,9 @@ parse_opt(int key, char* arg, struct argp_state*)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OPT_INS:
|
case OPT_INS:
|
||||||
{
|
all_input_aps.emplace();
|
||||||
all_input_aps.emplace(std::vector<std::string>{});
|
split_aps(arg, *all_input_aps);
|
||||||
split_aps(arg, *all_input_aps);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OPT_LIVENESS:
|
case OPT_LIVENESS:
|
||||||
liveness = true;
|
liveness = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -537,11 +539,12 @@ parse_opt(int key, char* arg, struct argp_state*)
|
||||||
nnf = true;
|
nnf = true;
|
||||||
break;
|
break;
|
||||||
case OPT_OUTS:
|
case OPT_OUTS:
|
||||||
{
|
all_output_aps.emplace();
|
||||||
all_output_aps.emplace(std::vector<std::string>{});
|
split_aps(arg, *all_output_aps);
|
||||||
split_aps(arg, *all_output_aps);
|
break;
|
||||||
break;
|
case OPT_PART_FILE:
|
||||||
}
|
read_part_file(arg);
|
||||||
|
break;
|
||||||
case OPT_SONF:
|
case OPT_SONF:
|
||||||
sonf = arg ? arg : "sonf_";
|
sonf = arg ? arg : "sonf_";
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ enum {
|
||||||
OPT_INS,
|
OPT_INS,
|
||||||
OPT_LTL_PRIORITIES,
|
OPT_LTL_PRIORITIES,
|
||||||
OPT_OUTS,
|
OPT_OUTS,
|
||||||
|
OPT_PART_FILE,
|
||||||
OPT_SEED,
|
OPT_SEED,
|
||||||
OPT_TREE_SIZE,
|
OPT_TREE_SIZE,
|
||||||
};
|
};
|
||||||
|
|
@ -102,8 +103,10 @@ static const argp_option options[] = {
|
||||||
"comma-separated list of atomic propositions to consider as input, "
|
"comma-separated list of atomic propositions to consider as input, "
|
||||||
"interpreted as a regex if enclosed in slashes", 0 },
|
"interpreted as a regex if enclosed in slashes", 0 },
|
||||||
{ "outs", OPT_OUTS, "PROPS", 0,
|
{ "outs", OPT_OUTS, "PROPS", 0,
|
||||||
"comma-separated list of atomic propositions to consider as putput, "
|
"comma-separated list of atomic propositions to consider as output, "
|
||||||
"interpreted as a regex if enclosed in slashes", 0 },
|
"interpreted as a regex if enclosed in slashes", 0 },
|
||||||
|
{ "part-file", OPT_PART_FILE, "FILENAME", 0,
|
||||||
|
"read the I/O partition of atomic propositions from FILENAME", 0 },
|
||||||
RANGE_DOC,
|
RANGE_DOC,
|
||||||
/**************************************************/
|
/**************************************************/
|
||||||
{ nullptr, 0, nullptr, 0, "Adjusting probabilities:", 4 },
|
{ nullptr, 0, nullptr, 0, "Adjusting probabilities:", 4 },
|
||||||
|
|
@ -253,19 +256,19 @@ parse_opt(int key, char* arg, struct argp_state*)
|
||||||
opt_unique = false;
|
opt_unique = false;
|
||||||
break;
|
break;
|
||||||
case OPT_INS:
|
case OPT_INS:
|
||||||
{
|
all_input_aps.emplace();
|
||||||
all_input_aps.emplace(std::vector<std::string>{});
|
split_aps(arg, *all_input_aps);
|
||||||
split_aps(arg, *all_input_aps);
|
opt_io = true;
|
||||||
opt_io = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OPT_OUTS:
|
case OPT_OUTS:
|
||||||
{
|
all_output_aps.emplace();
|
||||||
all_output_aps.emplace(std::vector<std::string>{});
|
split_aps(arg, *all_output_aps);
|
||||||
split_aps(arg, *all_output_aps);
|
opt_io = true;
|
||||||
opt_io = true;
|
break;
|
||||||
break;
|
case OPT_PART_FILE:
|
||||||
}
|
read_part_file(arg);
|
||||||
|
opt_io = true;
|
||||||
|
break;
|
||||||
case OPT_SEED:
|
case OPT_SEED:
|
||||||
opt_seed = to_int(arg, "--seed");
|
opt_seed = to_int(arg, "--seed");
|
||||||
break;
|
break;
|
||||||
|
|
@ -303,7 +306,7 @@ main(int argc, char* argv[])
|
||||||
|
|
||||||
if (opt_io && !opt_out_ap_count)
|
if (opt_io && !opt_out_ap_count)
|
||||||
error(2, 0,
|
error(2, 0,
|
||||||
"options --ins and --outs only make sense when the "
|
"options --ins, --outs, --part-file only make sense when the "
|
||||||
"two-argument version of '-A N,M' or '-P N,M' is used.");
|
"two-argument version of '-A N,M' or '-P N,M' is used.");
|
||||||
if (opt_out_ap_count > 0)
|
if (opt_out_ap_count > 0)
|
||||||
// Do not require --ins/--outs to be used, as the input
|
// Do not require --ins/--outs to be used, as the input
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ enum
|
||||||
OPT_HIDE,
|
OPT_HIDE,
|
||||||
OPT_INPUT,
|
OPT_INPUT,
|
||||||
OPT_OUTPUT,
|
OPT_OUTPUT,
|
||||||
|
OPT_PART_FILE,
|
||||||
OPT_POLARITY,
|
OPT_POLARITY,
|
||||||
OPT_PRINT,
|
OPT_PRINT,
|
||||||
OPT_PRINT_AIGER,
|
OPT_PRINT_AIGER,
|
||||||
|
|
@ -79,6 +80,8 @@ static const argp_option options[] =
|
||||||
{ "ins", OPT_INPUT, "PROPS", 0,
|
{ "ins", OPT_INPUT, "PROPS", 0,
|
||||||
"comma-separated list of uncontrollable (a.k.a. input) atomic"
|
"comma-separated list of uncontrollable (a.k.a. input) atomic"
|
||||||
" propositions, interpreted as a regex if enclosed in slashes", 0 },
|
" propositions, interpreted as a regex if enclosed in slashes", 0 },
|
||||||
|
{ "part-file", OPT_PART_FILE, "FILENAME", 0,
|
||||||
|
"read the I/O partition of atomic propositions from FILENAME", 0 },
|
||||||
{ "tlsf", OPT_TLSF, "FILENAME", 0,
|
{ "tlsf", OPT_TLSF, "FILENAME", 0,
|
||||||
"Read a TLSF specification from FILENAME, and call syfco to "
|
"Read a TLSF specification from FILENAME, and call syfco to "
|
||||||
"convert it into LTL", 0 },
|
"convert it into LTL", 0 },
|
||||||
|
|
@ -993,17 +996,16 @@ parse_opt(int key, char *arg, struct argp_state *)
|
||||||
show_status = false;
|
show_status = false;
|
||||||
break;
|
break;
|
||||||
case OPT_INPUT:
|
case OPT_INPUT:
|
||||||
{
|
all_input_aps.emplace();
|
||||||
all_input_aps.emplace(std::vector<std::string>{});
|
split_aps(arg, *all_input_aps);
|
||||||
split_aps(arg, *all_input_aps);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OPT_OUTPUT:
|
case OPT_OUTPUT:
|
||||||
{
|
all_output_aps.emplace();
|
||||||
all_output_aps.emplace(std::vector<std::string>{});
|
split_aps(arg, *all_output_aps);
|
||||||
split_aps(arg, *all_output_aps);
|
break;
|
||||||
break;
|
case OPT_PART_FILE:
|
||||||
}
|
read_part_file(arg);
|
||||||
|
break;
|
||||||
case OPT_POLARITY:
|
case OPT_POLARITY:
|
||||||
opt_polarity = XARGMATCH("--polarity", arg,
|
opt_polarity = XARGMATCH("--polarity", arg,
|
||||||
polarity_args, polarity_values);
|
polarity_args, polarity_values);
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,7 @@ to be told using options such as =--ins= or =--outs= which atomic
|
||||||
propositions are input or output. Often these atomic propositions
|
propositions are input or output. Often these atomic propositions
|
||||||
can have very long names, so it is useful to be able to rename
|
can have very long names, so it is useful to be able to rename
|
||||||
them without fogeting about their nature. Option =--relabel=io=
|
them without fogeting about their nature. Option =--relabel=io=
|
||||||
combined with one if =--ins= or =--outs= will do exactly that:
|
combined with one if =--ins=, =--outs=, or =--part-file= will do exactly that:
|
||||||
|
|
||||||
#+BEGIN_SRC sh
|
#+BEGIN_SRC sh
|
||||||
ltlfilt -f 'G(req -> Fack) & G(go -> Fgrant)' --relabel=io --ins=req,go
|
ltlfilt -f 'G(req -> Fack) & G(go -> Fgrant)' --relabel=io --ins=req,go
|
||||||
|
|
@ -322,16 +322,14 @@ ltlfilt -f 'G(req -> Fack) & G(go -> Fgrant)' --relabel=io --ins=req,go
|
||||||
#+RESULTS:
|
#+RESULTS:
|
||||||
: G(i1 -> Fo1) & G(i0 -> Fo0)
|
: G(i1 -> Fo1) & G(i0 -> Fo0)
|
||||||
|
|
||||||
Like in [[file:ltlsynt.org][=ltlsynt=]], options =--ins= and =--outs= take a comma-separated
|
The syntax for options =--ins=, =--outs= and =--part-file= is the
|
||||||
list of atomic propositions as argument. Additionally, if an atomic
|
same as for [[file:ltlsynt.org::#input-options][=ltlsynt=]].
|
||||||
proposition in this list is enclosed in slashes (as in
|
|
||||||
=--out=req,/^go/=), it is used as a regular expression for matching
|
|
||||||
atomic propositions.
|
|
||||||
|
|
||||||
By the way, such an IO-renamed formula can be given to [[file:ltlsynt.org][=ltlsynt=]] without
|
By the way, such an IO-renamed formula can be given to [[file:ltlsynt.org][=ltlsynt=]]
|
||||||
having to specify =--ins= or =--outs=, because when these two options
|
without having to specify =--ins=, =--outs=, or =--part-file=, because
|
||||||
are missing the convention is that anything starting with =i= is an
|
when these two options are missing the convention is that anything
|
||||||
input, and anything starting with =o= is an output.
|
starting with =i= is an input, and anything starting with =o= is an
|
||||||
|
output.
|
||||||
|
|
||||||
An example showing how to use the =--from-ltlf= option is on [[file:tut12.org][a
|
An example showing how to use the =--from-ltlf= option is on [[file:tut12.org][a
|
||||||
separate page]].
|
separate page]].
|
||||||
|
|
|
||||||
|
|
@ -489,6 +489,6 @@ chose from may help to get more realizable formulas.
|
||||||
|
|
||||||
|
|
||||||
When the original LTL synthesis specification formulas have atomic
|
When the original LTL synthesis specification formulas have atomic
|
||||||
proposition that do not start with =i= or =o=, options =--ins= and
|
proposition that do not start with =i= or =o=, options =--ins=,
|
||||||
=--outs= can be used to specify the nature of the atomic propositions.
|
=--outs=, or =--part-file= can be used to specify the nature of the
|
||||||
These options work as with [[file:ltlsynt.org][=ltlsynt=]].
|
atomic propositions. These options work as [[file:ltlsynt.org::#input-options][=ltlsynt='s input options]].
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
This tool synthesizes reactive controllers from LTL/PSL formulas.
|
This tool synthesizes reactive controllers from LTL/PSL formulas.
|
||||||
|
|
||||||
Consider a set $I$ of /input/ atomic propositions, a set $O$ of output atomic
|
Consider a set $I$ of /input/ atomic propositions, a set $O$ of /output/ atomic
|
||||||
propositions, and a PSL formula \phi over the propositions in $I \cup O$. A
|
propositions, and a PSL formula \phi over the propositions in $I \cup O$. A
|
||||||
*reactive controller* realizing \phi is a function $c: (2^{I})^\star \times 2^I \mapsto
|
*reactive controller* realizing \phi is a function $c: (2^{I})^\star \times 2^I \mapsto
|
||||||
2^O$ such that, for every \omega-word $(u_i)_{i \in N} \in (2^I)^\omega$ over
|
2^O$ such that, for every \omega-word $(u_i)_{i \in N} \in (2^I)^\omega$ over
|
||||||
|
|
@ -21,46 +21,34 @@ exists. Such controllers are easily represented as automata (or more
|
||||||
specifically as Mealy machines). In the automaton representing the
|
specifically as Mealy machines). In the automaton representing the
|
||||||
controller, the acceptance condition is irrelevant and trivially true.
|
controller, the acceptance condition is irrelevant and trivially true.
|
||||||
|
|
||||||
=ltlsynt= has three mandatory options:
|
Here is a small example where $I=\{i_1,i_2\}$ and $O=\{o_1\}$. The
|
||||||
- =--ins=: a comma-separated list of input atomic propositions, or input regexes enclosed in slashes;
|
specification asks that $o_1$ hold at some point if and only if $i_1$
|
||||||
- =--outs=: a comma-separated list of output atomic propositions, or output regexes enclosed in slashes;
|
and $i_2$ hold one after the other at some point.
|
||||||
- =--formula= or =--file=: a specification in LTL or PSL.
|
|
||||||
|
|
||||||
One of =--ins= or =--outs= may be omitted, as any atomic proposition
|
|
||||||
not listed as input can be assumed to be output and vice versa. If
|
|
||||||
both are omitted, =ltlsynts= will assume ~--ins=/^[iI]/~ and
|
|
||||||
~--outs=/^[oO]/~, i.e., atomic propositions will be classified as
|
|
||||||
input or output based on their first letter.
|
|
||||||
|
|
||||||
The following example illustrates the synthesis of a controller
|
|
||||||
ensuring that input =i1= and =i2= are both true initially if and only
|
|
||||||
if eventually output =o1= will go from true to false at some point.
|
|
||||||
Note that this is an equivalence, not an implication.
|
|
||||||
|
|
||||||
#+NAME: example
|
#+NAME: example
|
||||||
#+BEGIN_SRC sh :exports both
|
#+BEGIN_SRC sh :exports both
|
||||||
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))'
|
ltlsynt -f 'F(i1 & Xi2) <-> F(o1)'
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+RESULTS: example
|
#+RESULTS: example
|
||||||
#+begin_example
|
#+begin_example
|
||||||
REALIZABLE
|
REALIZABLE
|
||||||
HOA: v1
|
HOA: v1
|
||||||
States: 3
|
States: 2
|
||||||
Start: 0
|
Start: 0
|
||||||
AP: 3 "i1" "i2" "o1"
|
AP: 3 "i1" "o1" "i2"
|
||||||
acc-name: all
|
acc-name: all
|
||||||
Acceptance: 0 t
|
Acceptance: 0 t
|
||||||
properties: trans-labels explicit-labels state-acc deterministic
|
properties: trans-labels explicit-labels state-acc deterministic
|
||||||
controllable-AP: 2
|
controllable-AP: 1
|
||||||
--BODY--
|
--BODY--
|
||||||
State: 0
|
State: 0
|
||||||
[0&1&2] 1
|
[!0&!1] 0
|
||||||
[!0&2 | !1&2] 2
|
[0&!1] 1
|
||||||
State: 1
|
State: 1
|
||||||
[!2] 1
|
[!0&!1&!2] 0
|
||||||
State: 2
|
[0&!1&!2] 1
|
||||||
[2] 2
|
[1&2] 1
|
||||||
--END--
|
--END--
|
||||||
#+end_example
|
#+end_example
|
||||||
|
|
||||||
|
|
@ -78,7 +66,7 @@ to visualize this machine.
|
||||||
|
|
||||||
#+NAME: exampledot
|
#+NAME: exampledot
|
||||||
#+BEGIN_SRC sh :exports code
|
#+BEGIN_SRC sh :exports code
|
||||||
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))' --hide-status --dot
|
ltlsynt -f 'F(i1 & Xi2) <-> F(o1)' --hide-status --dot
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC dot :file ltlsyntex.svg :var txt=exampledot :exports results
|
#+BEGIN_SRC dot :file ltlsyntex.svg :var txt=exampledot :exports results
|
||||||
|
|
@ -99,75 +87,49 @@ ltlsynt --ins=a -f 'F a'
|
||||||
#+RESULTS:
|
#+RESULTS:
|
||||||
: UNREALIZABLE
|
: UNREALIZABLE
|
||||||
|
|
||||||
By default, the controller is output in HOA format, but it can be
|
* Input options
|
||||||
output as an And-Inverter-Graph in [[http://fmv.jku.at/aiger/][AIGER format]] using the =--aiger=
|
:PROPERTIES:
|
||||||
flag. This is the output format required for the [[http://syntcomp.org/][SYNTCOMP]] competition.
|
:CUSTOM_ID: input-options
|
||||||
|
:END:
|
||||||
|
|
||||||
#+NAME: exampleaig
|
=ltlsynt= require two pieces of information two solve a reactive
|
||||||
#+BEGIN_SRC sh :exports both
|
LTL/PSL synthesis problem: an LTL (or PSL) formula, and a partition of
|
||||||
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))' --aiger
|
its atomic propositions as input and output.
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
#+RESULTS: exampleaig
|
The specification formula can be passed with =-f/--formula= or
|
||||||
#+begin_example
|
=-F/--file=. If multiple specifications formulas are passed, they
|
||||||
REALIZABLE
|
will all be solved individually.
|
||||||
aag 18 2 2 1 14
|
|
||||||
2
|
|
||||||
4
|
|
||||||
6 23
|
|
||||||
8 37
|
|
||||||
7
|
|
||||||
10 6 9
|
|
||||||
12 4 9
|
|
||||||
14 5 10
|
|
||||||
16 13 15
|
|
||||||
18 2 17
|
|
||||||
20 3 10
|
|
||||||
22 19 21
|
|
||||||
24 7 8
|
|
||||||
26 4 24
|
|
||||||
28 5 7
|
|
||||||
30 27 29
|
|
||||||
32 2 31
|
|
||||||
34 3 7
|
|
||||||
36 33 35
|
|
||||||
i0 i1
|
|
||||||
i1 i2
|
|
||||||
o0 o1
|
|
||||||
#+end_example
|
|
||||||
|
|
||||||
The above format is not very human friendly. Again, by passing both
|
The input/output partition can be given in several ways. If it is
|
||||||
=--aiger= and =--dot=, one can display the And-Inverter-Graph representing
|
not specified, =ltlsynt= assumes that input variables should start
|
||||||
the controller:
|
with =i=, and output variables should start with =o=.
|
||||||
|
|
||||||
#+NAME: exampleaigdot
|
Options =--ins= and =--outs= should be followed by a comma-separated
|
||||||
#+BEGIN_SRC sh :exports code
|
list of input atomic propositions, or input regexes enclosed in
|
||||||
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))' --hide-status --aiger --dot
|
slashes. E.g., =--ins=switch,/^in/,car=. If only one of these
|
||||||
#+END_SRC
|
options is given, atomic propositions not matched by that option
|
||||||
|
are assumed to belong to the other set.
|
||||||
|
|
||||||
#+BEGIN_SRC dot :file ltlsyntexaig.svg :var txt=exampleaigdot :exports results
|
Another way to specify the input/output partition is using a =*.part=
|
||||||
$txt
|
file passed to the =--part-file= option. Such a file is used by
|
||||||
#+END_SRC
|
several other synthesis tools. The format is space-separated list of
|
||||||
|
words representing atomic-propositions. Two keywords =.inputs= and
|
||||||
|
=.outputs= indicate the set of the atomic-propositions that follow.
|
||||||
|
For instance:
|
||||||
|
|
||||||
#+RESULTS:
|
#+BEGIN_EXAMPLE
|
||||||
[[file:ltlsyntexaig.svg]]
|
.inputs request cancel
|
||||||
|
.outputs grant ack
|
||||||
|
#+END_EXAMPLE
|
||||||
|
|
||||||
In the above diagram, round nodes represent AND gates. Small black
|
Using =--part-file=THEABOVEFILE= is equivalent to
|
||||||
circles represent inversions (or negations), colored triangles are
|
=--ins=request,cancel --outs=grant,ack=.
|
||||||
used to represent input signals (at the bottom) and output signals (at
|
|
||||||
the top), and finally rectangles represent latches. A latch is a one
|
|
||||||
bit register that delays the signal by one step. Initially, all
|
|
||||||
latches are assumed to contain =false=, and they emit their value from
|
|
||||||
the =L0_out= and =L1_out= rectangles at the bottom. Their input value,
|
|
||||||
to be emitted at the next step, is received via the =L0_in= and =L1_in=
|
|
||||||
boxes at the top. In =ltlsynt='s encoding, the set of latches is used
|
|
||||||
to keep track of the current state of the Mealy machine.
|
|
||||||
|
|
||||||
The generation of a controller can be disabled with the flag
|
As an extension to this simple =*.part= format, words enclosed in
|
||||||
=--realizability=. In this case, =ltlsynt='s output is limited to
|
slashes are interpreted as regexes, like for the =--ins= and =--outs=
|
||||||
=REALIZABLE= or =UNREALIZABLE=.
|
options.
|
||||||
|
|
||||||
* TLSF
|
* TLSF input
|
||||||
|
|
||||||
=ltlsynt= was made with the [[http://syntcomp.org/][SYNTCOMP]] competition in mind, and more
|
=ltlsynt= was made with the [[http://syntcomp.org/][SYNTCOMP]] competition in mind, and more
|
||||||
specifically the TLSF track of this competition. TLSF is a high-level
|
specifically the TLSF track of this competition. TLSF is a high-level
|
||||||
|
|
@ -193,6 +155,65 @@ ltlsynt --formula="$LTL" --outs="$OUT"
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
* Output options
|
||||||
|
|
||||||
|
By default, the controller is output in HOA format, but it can be
|
||||||
|
output as an And-Inverter-Graph in [[http://fmv.jku.at/aiger/][AIGER format]] using the =--aiger=
|
||||||
|
flag. This is the output format required for the [[http://syntcomp.org/][SYNTCOMP]] competition.
|
||||||
|
|
||||||
|
#+NAME: exampleaig
|
||||||
|
#+BEGIN_SRC sh :exports both
|
||||||
|
ltlsynt -f 'F(i1 & Xi2) <-> F(o1)' --aiger
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS: exampleaig
|
||||||
|
#+begin_example
|
||||||
|
REALIZABLE
|
||||||
|
aag 5 2 1 1 2
|
||||||
|
2
|
||||||
|
4
|
||||||
|
6 11
|
||||||
|
8
|
||||||
|
8 4 6
|
||||||
|
10 3 9
|
||||||
|
i0 i1
|
||||||
|
i1 i2
|
||||||
|
o0 o1
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
The above format is not very human friendly. Again, by passing both
|
||||||
|
=--aiger= and =--dot=, one can display the And-Inverter-Graph representing
|
||||||
|
the controller:
|
||||||
|
|
||||||
|
#+NAME: exampleaigdot
|
||||||
|
#+BEGIN_SRC sh :exports code
|
||||||
|
ltlsynt -f 'F(i1 & Xi2) <-> F(o1)' --hide-status --aiger --dot
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS: exampleaigdot
|
||||||
|
|
||||||
|
#+BEGIN_SRC dot :file ltlsyntexaig.svg :var txt=exampleaigdot :exports results
|
||||||
|
$txt
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file:ltlsyntexaig.svg]]
|
||||||
|
|
||||||
|
In the above diagram, round nodes represent AND gates. Small black
|
||||||
|
circles represent inversions (or negations), colored triangles are
|
||||||
|
used to represent input signals (at the bottom) and output signals (at
|
||||||
|
the top), and finally rectangles represent latches. A latch is a one
|
||||||
|
bit register that delays the signal by one step. Initially, all
|
||||||
|
latches are assumed to contain =false=, and they emit their value from
|
||||||
|
the =*_out= rectangles at the bottom. Their input value, to be
|
||||||
|
emitted at the next step, is received via the =*_in= boxes at the top.
|
||||||
|
In =ltlsynt='s encoding, the set of latches is used to keep track of
|
||||||
|
the current state of the Mealy machine.
|
||||||
|
|
||||||
|
The generation of a controller can be disabled with the flag
|
||||||
|
=--realizability=. In this case, =ltlsynt='s output is limited to
|
||||||
|
=REALIZABLE= or =UNREALIZABLE=.
|
||||||
|
|
||||||
* Internal details
|
* Internal details
|
||||||
|
|
||||||
The tool reduces the synthesis problem to a parity game, and solves the parity
|
The tool reduces the synthesis problem to a parity game, and solves the parity
|
||||||
|
|
|
||||||
|
|
@ -626,6 +626,18 @@ run 0 ltlfilt -s -u --relabel=io --ins='/[ab]/' --define in >out
|
||||||
diff exp out
|
diff exp out
|
||||||
run 0 ltlfilt -s -u --relabel=io --outs='/[^ab]/' --define in >out
|
run 0 ltlfilt -s -u --relabel=io --outs='/[^ab]/' --define in >out
|
||||||
diff exp out
|
diff exp out
|
||||||
|
echo '.inputs a b' >rel.part
|
||||||
|
run 0 ltlfilt -s -u --relabel=io --part=rel.part --define in >out
|
||||||
|
diff exp out
|
||||||
|
echo '.inputs /[ab]/ # .output ignored' >rel.part
|
||||||
|
run 0 ltlfilt -s -u --relabel=io --part=rel.part --define in >out
|
||||||
|
diff exp out
|
||||||
|
echo '.outputs /[^ab]/ # .input ignored' >rel.part
|
||||||
|
run 0 ltlfilt -s -u --relabel=io --part=rel.part --define in >out
|
||||||
|
diff exp out
|
||||||
|
echo 'error /[^ab]/' >rel.part
|
||||||
|
run 2 ltlfilt -s -u --relabel=io --part=rel.part --define in 2>err
|
||||||
|
grep "expected '.inputs' or '.outputs'" err
|
||||||
|
|
||||||
cat >exp <<EOF
|
cat >exp <<EOF
|
||||||
#define p0 (a & c)
|
#define p0 (a & c)
|
||||||
|
|
|
||||||
|
|
@ -121,3 +121,19 @@ diff out expected
|
||||||
ltlmix -fGa -fFz -A3,3 -C4 -n10 2> err && exit 1
|
ltlmix -fGa -fFz -A3,3 -C4 -n10 2> err && exit 1
|
||||||
cat err
|
cat err
|
||||||
grep 'ins.*outs' err
|
grep 'ins.*outs' err
|
||||||
|
|
||||||
|
echo '.inputs a c .outputs b d ' > part.part
|
||||||
|
ltlmix -fXa -fGb -f'c U d' --part-file=part.part -C4 -A3,3 -n10 >out
|
||||||
|
cat >expected <<EOF
|
||||||
|
(i1 U o2) & Go1 & (i0 U o2)
|
||||||
|
Go1 & (i0 U o2) & Xi1 & Go2
|
||||||
|
Go2 & (i2 U o1) & Xi2 & Go0
|
||||||
|
Go2 & Go0 & (i1 U o0) & Xi0
|
||||||
|
Go1 & Xi2 & Xi0 & (i0 U o0)
|
||||||
|
Xi1 & Xi2 & Go0 & (i2 U o0)
|
||||||
|
Xi1 & (i1 U o0) & Xi0 & (i1 U o1)
|
||||||
|
Go2 & Xi2 & (i0 U o0)
|
||||||
|
(i0 U o2) & (i0 U o0) & (i0 U o1)
|
||||||
|
Go1 & Xi0 & (i0 U o0) & (i0 U o1)
|
||||||
|
EOF
|
||||||
|
diff out expected
|
||||||
|
|
|
||||||
|
|
@ -648,7 +648,7 @@ test $? -eq 2
|
||||||
grep "one.*should match 'b'" stderr
|
grep "one.*should match 'b'" stderr
|
||||||
ltlsynt -f 'GFa | FGb | GFc' 2>stderr && :
|
ltlsynt -f 'GFa | FGb | GFc' 2>stderr && :
|
||||||
test $? -eq 2
|
test $? -eq 2
|
||||||
grep "[-]-ins or --outs" stderr
|
grep "[-]-ins.*--outs" stderr
|
||||||
|
|
||||||
# Try to find a direct strategy for GFa <-> GFb and a direct strategy for
|
# Try to find a direct strategy for GFa <-> GFb and a direct strategy for
|
||||||
# Gc
|
# Gc
|
||||||
|
|
@ -1139,11 +1139,15 @@ ltlsynt --decomp=yes -f "$s" --realizability >out
|
||||||
ltlsynt --decomp=no --outs='/^o[0-9]*$/' -f "$s" --realizability >>out
|
ltlsynt --decomp=no --outs='/^o[0-9]*$/' -f "$s" --realizability >>out
|
||||||
ltlsynt --decomp=no --outs='/^o[0-9]$/' -f "$s" --realizability >>out && :
|
ltlsynt --decomp=no --outs='/^o[0-9]$/' -f "$s" --realizability >>out && :
|
||||||
ltlsynt -f "$s" --ins='/^i[0-9]*$/' --realizability >>out
|
ltlsynt -f "$s" --ins='/^i[0-9]*$/' --realizability >>out
|
||||||
|
echo ".inputs i1 i2 i3 i4 i5 i6 i7 i8" > part.part
|
||||||
|
echo ".outputs /^o1[0-9]*/ o01 o02 o03 o04 o05 o06 o07 o08 o09" >> part.part
|
||||||
|
ltlsynt -f "$s" --part-file=part.part --realizability >>out
|
||||||
cat >expected <<EOF
|
cat >expected <<EOF
|
||||||
REALIZABLE
|
REALIZABLE
|
||||||
REALIZABLE
|
REALIZABLE
|
||||||
UNREALIZABLE
|
UNREALIZABLE
|
||||||
REALIZABLE
|
REALIZABLE
|
||||||
|
REALIZABLE
|
||||||
EOF
|
EOF
|
||||||
diff out expected
|
diff out expected
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue