ltlsynt: allow regular expressions in --ins/--outs
* bin/ltlsynt.cc: Implement this. * doc/org/ltlsynt.org, NEWS: Adjust documentation. * tests/core/ltlsynt.test: Add test cases.
This commit is contained in:
parent
31462d84ba
commit
15b876d368
4 changed files with 227 additions and 117 deletions
13
NEWS
13
NEWS
|
|
@ -16,6 +16,19 @@ New in spot 2.11.6.dev (not yet released)
|
||||||
will replace boolean subformulas by fresh atomic propositions even
|
will replace boolean subformulas by fresh atomic propositions even
|
||||||
if those subformulas share atomic propositions.
|
if those subformulas share atomic propositions.
|
||||||
|
|
||||||
|
- ltlsynt's --ins and --outs options will iterpret any atomic
|
||||||
|
proposition surrounded by '/' as a regular expressions.
|
||||||
|
For intance with
|
||||||
|
|
||||||
|
ltlsynt --ins='/^in/,/env/' --outs=/^out/,/control/' ...
|
||||||
|
|
||||||
|
any atomic proposition that start with 'in' or contains 'env'
|
||||||
|
will be considered as inputs, and those that start with 'out'
|
||||||
|
or contain 'control' will be considered output.
|
||||||
|
|
||||||
|
By default, if neither --ins nor --outs is given, ltlsynt will
|
||||||
|
behave as if --ins='/^[iI]/' and --outs='/^[oO]/ were used.
|
||||||
|
|
||||||
- ltlsynt will now check for atomic propositions that always have
|
- ltlsynt will now check for atomic propositions that always have
|
||||||
the same polarity in the specification. When this happens, these
|
the same polarity in the specification. When this happens, these
|
||||||
output APs are replaced by true or false before running the
|
output APs are replaced by true or false before running the
|
||||||
|
|
|
||||||
214
bin/ltlsynt.cc
214
bin/ltlsynt.cc
|
|
@ -43,6 +43,7 @@
|
||||||
#include <spot/twaalgos/product.hh>
|
#include <spot/twaalgos/product.hh>
|
||||||
#include <spot/twaalgos/synthesis.hh>
|
#include <spot/twaalgos/synthesis.hh>
|
||||||
#include <spot/twaalgos/translate.hh>
|
#include <spot/twaalgos/translate.hh>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
|
@ -73,10 +74,10 @@ static const argp_option options[] =
|
||||||
{ nullptr, 0, nullptr, 0, "Input options:", 1 },
|
{ nullptr, 0, nullptr, 0, "Input options:", 1 },
|
||||||
{ "outs", OPT_OUTPUT, "PROPS", 0,
|
{ "outs", OPT_OUTPUT, "PROPS", 0,
|
||||||
"comma-separated list of controllable (a.k.a. output) atomic"
|
"comma-separated list of controllable (a.k.a. output) atomic"
|
||||||
" propositions", 0 },
|
" propositions, , interpreted as a regex if enclosed in slashes", 0 },
|
||||||
{ "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", 0 },
|
" propositions, interpreted as a regex if enclosed in slashes", 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 },
|
||||||
|
|
@ -171,9 +172,17 @@ Exit status:\n\
|
||||||
1 if at least one input problem was not realizable\n\
|
1 if at least one input problem was not realizable\n\
|
||||||
2 if any error has been reported";
|
2 if any error has been reported";
|
||||||
|
|
||||||
|
// --ins and --outs, as supplied on the command-line
|
||||||
static std::optional<std::vector<std::string>> all_output_aps;
|
static std::optional<std::vector<std::string>> all_output_aps;
|
||||||
static std::optional<std::vector<std::string>> all_input_aps;
|
static std::optional<std::vector<std::string>> all_input_aps;
|
||||||
|
|
||||||
|
// first, separate the filters that are regular expressions from
|
||||||
|
// the others. Compile the regular expressions while we are at it.
|
||||||
|
static std::vector<std::regex> regex_in;
|
||||||
|
static std::vector<std::regex> regex_out;
|
||||||
|
// map identifier to input/output (false=input, true=output)
|
||||||
|
static std::unordered_map<std::string, bool> identifier_map;
|
||||||
|
|
||||||
static const char* opt_csv = nullptr;
|
static const char* opt_csv = nullptr;
|
||||||
static bool opt_print_pg = false;
|
static bool opt_print_pg = false;
|
||||||
static bool opt_print_hoa = false;
|
static bool opt_print_hoa = false;
|
||||||
|
|
@ -690,78 +699,111 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::unordered_set<std::string>
|
||||||
|
list_aps_in_formula(spot::formula f)
|
||||||
|
{
|
||||||
|
std::unordered_set<std::string> aps;
|
||||||
|
f.traverse([&aps](spot::formula s) {
|
||||||
|
if (s.is(spot::op::ap))
|
||||||
|
aps.emplace(s.ap_name());
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return aps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes a set of the atomic propositions appearing in the formula,
|
||||||
|
// and seperate them into two vectors: input APs and output APs.
|
||||||
|
static std::pair<std::vector<std::string>, std::vector<std::string>>
|
||||||
|
filter_list_of_aps(const std::unordered_set<std::string>& aps,
|
||||||
|
const char* filename, int linenum)
|
||||||
|
{
|
||||||
|
// now iterate over the list of atomic propositions to filter them
|
||||||
|
std::vector<std::string> matched[2]; // 0 = input, 1 = output
|
||||||
|
for (const std::string& a: aps)
|
||||||
|
{
|
||||||
|
if (auto it = identifier_map.find(a); it != identifier_map.end())
|
||||||
|
{
|
||||||
|
matched[it->second].push_back(a);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found_in = false;
|
||||||
|
for (const std::regex& r: regex_in)
|
||||||
|
if (std::regex_search(a, r))
|
||||||
|
{
|
||||||
|
found_in = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bool found_out = false;
|
||||||
|
for (const std::regex& r: regex_out)
|
||||||
|
if (std::regex_search(a, r))
|
||||||
|
{
|
||||||
|
found_out = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (all_input_aps.has_value() == all_output_aps.has_value())
|
||||||
|
{
|
||||||
|
if (!all_input_aps.has_value())
|
||||||
|
{
|
||||||
|
// If the atomic proposition hasn't been classified
|
||||||
|
// because neither --ins nor --out were specified,
|
||||||
|
// attempt to classify automatically using the first
|
||||||
|
// letter.
|
||||||
|
int fl = a[0];
|
||||||
|
if (fl == 'i' || fl == 'I')
|
||||||
|
found_in = true;
|
||||||
|
else if (fl == 'o' || fl == 'O')
|
||||||
|
found_out = true;
|
||||||
|
}
|
||||||
|
if (found_in && found_out)
|
||||||
|
error_at_line(2, 0, filename, linenum,
|
||||||
|
"'%s' matches both --ins and --outs",
|
||||||
|
a.c_str());
|
||||||
|
if (!found_in && !found_out)
|
||||||
|
{
|
||||||
|
if (all_input_aps.has_value() || all_output_aps.has_value())
|
||||||
|
error_at_line(2, 0, filename, linenum,
|
||||||
|
"one of --ins or --outs should match '%s'",
|
||||||
|
a.c_str());
|
||||||
|
else
|
||||||
|
error_at_line(2, 0, filename, linenum,
|
||||||
|
"since '%s' does not start with 'i' or 'o', "
|
||||||
|
"it is unclear if it is an input or "
|
||||||
|
"an output;\n use --ins or --outs",
|
||||||
|
a.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if we had only --ins or only --outs, anything not
|
||||||
|
// matching was was given is assumed to belong to the
|
||||||
|
// other one.
|
||||||
|
if (!all_input_aps.has_value() && !found_out)
|
||||||
|
found_in = true;
|
||||||
|
else if (!all_output_aps.has_value() && !found_in)
|
||||||
|
found_out = true;
|
||||||
|
}
|
||||||
|
matched[found_out].push_back(a);
|
||||||
|
}
|
||||||
|
return {matched[0], matched[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ltl_processor final : public job_processor
|
class ltl_processor final : public job_processor
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::optional<std::vector<std::string>> input_aps_;
|
|
||||||
std::optional<std::vector<std::string>> output_aps_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ltl_processor(std::optional<std::vector<std::string>> input_aps_,
|
ltl_processor()
|
||||||
std::optional<std::vector<std::string>> output_aps_)
|
|
||||||
: input_aps_(std::move(input_aps_)),
|
|
||||||
output_aps_(std::move(output_aps_))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int process_formula(spot::formula f,
|
int process_formula(spot::formula f,
|
||||||
const char* filename, int linenum) override
|
const char* filename, int linenum) override
|
||||||
{
|
{
|
||||||
auto unknown_aps = [](spot::formula f,
|
std::unordered_set<std::string> aps = list_aps_in_formula(f);
|
||||||
const std::optional<std::vector<std::string>>& known,
|
auto [input_aps, output_aps] =
|
||||||
const std::optional<std::vector<std::string>>& known2 = {})
|
filter_list_of_aps(aps, filename, linenum);
|
||||||
{
|
int res = solve_formula(f, input_aps, output_aps);
|
||||||
std::vector<std::string> unknown;
|
|
||||||
std::set<spot::formula> seen;
|
|
||||||
// If we don't have --ins and --outs, we must not find an AP.
|
|
||||||
bool can_have_ap = known.has_value();
|
|
||||||
f.traverse([&](const spot::formula& s)
|
|
||||||
{
|
|
||||||
if (s.is(spot::op::ap))
|
|
||||||
{
|
|
||||||
if (!seen.insert(s).second)
|
|
||||||
return false;
|
|
||||||
const std::string& a = s.ap_name();
|
|
||||||
if (!can_have_ap
|
|
||||||
|| (std::find(known->begin(), known->end(), a) == known->end()
|
|
||||||
&& (!known2.has_value()
|
|
||||||
|| std::find(known2->begin(),
|
|
||||||
known2->end(), a) == known2->end())))
|
|
||||||
unknown.push_back(a);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
return unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decide which atomic propositions are input or output.
|
|
||||||
int res;
|
|
||||||
if (!input_aps_.has_value() && output_aps_.has_value())
|
|
||||||
{
|
|
||||||
res = solve_formula(f, unknown_aps(f, output_aps_), *output_aps_);
|
|
||||||
}
|
|
||||||
else if (!output_aps_.has_value() && input_aps_.has_value())
|
|
||||||
{
|
|
||||||
res = solve_formula(f, *input_aps_, unknown_aps(f, input_aps_));
|
|
||||||
}
|
|
||||||
else if (!output_aps_.has_value() && !input_aps_.has_value())
|
|
||||||
{
|
|
||||||
for (const std::string& ap: unknown_aps(f, input_aps_, output_aps_))
|
|
||||||
error_at_line(2, 0, filename, linenum,
|
|
||||||
"one of --ins or --outs should list '%s'",
|
|
||||||
ap.c_str());
|
|
||||||
res = solve_formula(f, *input_aps_, *output_aps_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const std::string& ap: unknown_aps(f, input_aps_, output_aps_))
|
|
||||||
error_at_line(2, 0, filename, linenum,
|
|
||||||
"both --ins and --outs are specified, "
|
|
||||||
"but '%s' is unlisted",
|
|
||||||
ap.c_str());
|
|
||||||
res = solve_formula(f, *input_aps_, *output_aps_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt_csv)
|
if (opt_csv)
|
||||||
print_csv(f);
|
print_csv(f);
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -782,7 +824,7 @@ namespace
|
||||||
// The set of atomic proposition will be temporary set to those
|
// The set of atomic proposition will be temporary set to those
|
||||||
// given by syfco, unless they were forced from the command-line.
|
// given by syfco, unless they were forced from the command-line.
|
||||||
bool reset_aps = false;
|
bool reset_aps = false;
|
||||||
if (!input_aps_.has_value() && !output_aps_.has_value())
|
if (!all_input_aps.has_value() && !all_output_aps.has_value())
|
||||||
{
|
{
|
||||||
reset_aps = true;
|
reset_aps = true;
|
||||||
static char arg5[] = "--print-output-signals";
|
static char arg5[] = "--print-output-signals";
|
||||||
|
|
@ -790,12 +832,17 @@ namespace
|
||||||
const_cast<char*>(filename), nullptr };
|
const_cast<char*>(filename), nullptr };
|
||||||
std::string res = read_stdout_of_command(command);
|
std::string res = read_stdout_of_command(command);
|
||||||
|
|
||||||
output_aps_.emplace(std::vector<std::string>{});
|
all_output_aps.emplace(std::vector<std::string>{});
|
||||||
split_aps(res, *output_aps_);
|
split_aps(res, *all_output_aps);
|
||||||
|
for (const std::string& a: *all_output_aps)
|
||||||
|
identifier_map.emplace(a, true);
|
||||||
}
|
}
|
||||||
int res = process_string(tlsf_string, filename);
|
int res = process_string(tlsf_string, filename);
|
||||||
if (reset_aps)
|
if (reset_aps)
|
||||||
output_aps_.reset();
|
{
|
||||||
|
all_output_aps.reset();
|
||||||
|
identifier_map.clear();
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1077,14 +1124,29 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
check_no_formula();
|
check_no_formula();
|
||||||
|
|
||||||
// Check if inputs and outputs are distinct
|
// Filter identifiers from regexes.
|
||||||
if (all_input_aps.has_value() && all_output_aps.has_value())
|
if (all_input_aps.has_value())
|
||||||
for (const std::string& ai : *all_input_aps)
|
for (const std::string& f: *all_input_aps)
|
||||||
if (std::find(all_output_aps->begin(), all_output_aps->end(), ai)
|
{
|
||||||
!= all_output_aps->end())
|
unsigned sz = f.size();
|
||||||
error(2, 0, "'%s' appears both in --ins and --outs", ai.c_str());
|
if (f[0] == '/' && f[sz - 1] == '/')
|
||||||
|
regex_in.push_back(std::regex(f.substr(1, sz - 2)));
|
||||||
|
else
|
||||||
|
identifier_map.emplace(f, false);
|
||||||
|
}
|
||||||
|
if (all_output_aps.has_value())
|
||||||
|
for (const std::string& f: *all_output_aps)
|
||||||
|
{
|
||||||
|
unsigned sz = f.size();
|
||||||
|
if (f[0] == '/' && f[sz - 1] == '/')
|
||||||
|
regex_out.push_back(std::regex(f.substr(1, sz - 2)));
|
||||||
|
else if (auto [it, is_new] = identifier_map.try_emplace(f, true);
|
||||||
|
!is_new && !it->second)
|
||||||
|
error(2, 0, "'%s' appears in both --ins and --outs",
|
||||||
|
f.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
ltl_processor processor(all_input_aps, all_output_aps);
|
ltl_processor processor;
|
||||||
if (int res = processor.run(); res == 0 || res == 1)
|
if (int res = processor.run(); res == 0 || res == 1)
|
||||||
{
|
{
|
||||||
// Diagnose unused -x options
|
// Diagnose unused -x options
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,15 @@ 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:
|
=ltlsynt= has three mandatory options:
|
||||||
- =--ins=: a comma-separated list of input atomic propositions;
|
- =--ins=: a comma-separated list of input atomic propositions, or input regexes enclosed in slashes;
|
||||||
- =--outs=: a comma-separated list of output atomic propositions;
|
- =--outs=: a comma-separated list of output atomic propositions, or output regexes enclosed in slashes;
|
||||||
- =--formula= or =--file=: a specification in LTL or PSL.
|
- =--formula= or =--file=: a specification in LTL or PSL.
|
||||||
|
|
||||||
One of =--ins= or =--outs= may be omitted, as any atomic proposition not listed
|
One of =--ins= or =--outs= may be omitted, as any atomic proposition
|
||||||
as input can be assumed to be output and vice versa.
|
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
|
The following example illustrates the synthesis of a controller
|
||||||
ensuring that input =i1= and =i2= are both true initially if and only
|
ensuring that input =i1= and =i2= are both true initially if and only
|
||||||
|
|
@ -36,7 +39,7 @@ Note that this is an equivalence, not an implication.
|
||||||
|
|
||||||
#+NAME: example
|
#+NAME: example
|
||||||
#+BEGIN_SRC sh :exports both
|
#+BEGIN_SRC sh :exports both
|
||||||
ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))'
|
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))'
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+RESULTS: example
|
#+RESULTS: example
|
||||||
|
|
@ -55,24 +58,27 @@ State: 0
|
||||||
[0&1&2] 1
|
[0&1&2] 1
|
||||||
[!0&2 | !1&2] 2
|
[!0&2 | !1&2] 2
|
||||||
State: 1
|
State: 1
|
||||||
[!2] 0
|
[!2] 1
|
||||||
State: 2
|
State: 2
|
||||||
[2] 2
|
[2] 2
|
||||||
--END--
|
--END--
|
||||||
#+end_example
|
#+end_example
|
||||||
|
|
||||||
The output is composed of two parts:
|
The output is composed of two parts:
|
||||||
- The first one is a single line =REALIZABLE= or =UNREALIZABLE=; the presence of this
|
- The first part is a single line stating =REALIZABLE= or
|
||||||
line, required by the [[http://http://www.syntcomp.org/][SyntComp competition]], can be disabled with option =--hide-status=.
|
=UNREALIZABLE=; the presence of this line, required by the [[http://http://www.syntcomp.org/][SyntComp
|
||||||
- The second one, only present in the =REALIZABLE= case, is an automaton describing the controller.
|
competition]], can be disabled with option =--hide-status=.
|
||||||
|
- The second part, only present in the =REALIZABLE= case, is an
|
||||||
|
automaton describing the controller.
|
||||||
|
|
||||||
The controller contains the line =controllable-AP: 2=, which means that this automaton
|
The controller contains the line =controllable-AP: 2=, which means
|
||||||
should be interpreted as a Mealy machine where =o0= is part of the output.
|
that this automaton should be interpreted as a Mealy machine where
|
||||||
Using the =--dot= option, makes it easier to visualize this machine.
|
=o0= is part of the output. Using the =--dot= option, makes it easier
|
||||||
|
to visualize this machine.
|
||||||
|
|
||||||
#+NAME: exampledot
|
#+NAME: exampledot
|
||||||
#+BEGIN_SRC sh :exports code
|
#+BEGIN_SRC sh :exports code
|
||||||
ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --hide-status --dot
|
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!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,28 +105,32 @@ flag. This is the output format required for the [[http://syntcomp.org/][SYNTCOM
|
||||||
|
|
||||||
#+NAME: exampleaig
|
#+NAME: exampleaig
|
||||||
#+BEGIN_SRC sh :exports both
|
#+BEGIN_SRC sh :exports both
|
||||||
ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --aiger
|
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))' --aiger
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+RESULTS: exampleaig
|
#+RESULTS: exampleaig
|
||||||
#+begin_example
|
#+begin_example
|
||||||
REALIZABLE
|
REALIZABLE
|
||||||
aag 14 2 2 1 10
|
aag 18 2 2 1 14
|
||||||
2
|
2
|
||||||
4
|
4
|
||||||
6 14
|
6 23
|
||||||
8 29
|
8 37
|
||||||
7
|
7
|
||||||
10 7 9
|
10 6 9
|
||||||
12 4 10
|
12 4 9
|
||||||
14 2 12
|
14 5 10
|
||||||
16 7 8
|
16 13 15
|
||||||
18 4 16
|
18 2 17
|
||||||
20 5 7
|
20 3 10
|
||||||
22 21 19
|
22 19 21
|
||||||
24 2 23
|
24 7 8
|
||||||
26 3 7
|
26 4 24
|
||||||
28 27 25
|
28 5 7
|
||||||
|
30 27 29
|
||||||
|
32 2 31
|
||||||
|
34 3 7
|
||||||
|
36 33 35
|
||||||
i0 i1
|
i0 i1
|
||||||
i1 i2
|
i1 i2
|
||||||
o0 o1
|
o0 o1
|
||||||
|
|
@ -132,7 +142,7 @@ the controller:
|
||||||
|
|
||||||
#+NAME: exampleaigdot
|
#+NAME: exampleaigdot
|
||||||
#+BEGIN_SRC sh :exports code
|
#+BEGIN_SRC sh :exports code
|
||||||
ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --hide-status --aiger --dot
|
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))' --hide-status --aiger --dot
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC dot :file ltlsyntexaig.svg :var txt=exampleaigdot :exports results
|
#+BEGIN_SRC dot :file ltlsyntexaig.svg :var txt=exampleaigdot :exports results
|
||||||
|
|
@ -147,7 +157,7 @@ circles represent inversions (or negations), colored triangles are
|
||||||
used to represent input signals (at the bottom) and output signals (at
|
used to represent input signals (at the bottom) and output signals (at
|
||||||
the top), and finally rectangles represent latches. A latch is a one
|
the top), and finally rectangles represent latches. A latch is a one
|
||||||
bit register that delays the signal by one step. Initially, all
|
bit register that delays the signal by one step. Initially, all
|
||||||
latches are assumed to contain =false=, and them emit their value from
|
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,
|
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=
|
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
|
boxes at the top. In =ltlsynt='s encoding, the set of latches is used
|
||||||
|
|
@ -172,8 +182,9 @@ be synthesized using =syfco= and =ltlsynt=:
|
||||||
ltlsynt --tlsf FILE
|
ltlsynt --tlsf FILE
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
The above =--tlsf= option will call =syfco= to perform the conversion
|
The above =--tlsf= option will call =syfco= (which must be on your
|
||||||
and extract output signals, as if you had used:
|
=$PATH=) to perform the conversion and extract output signals, as if
|
||||||
|
you had used:
|
||||||
|
|
||||||
#+BEGIN_SRC sh :export code
|
#+BEGIN_SRC sh :export code
|
||||||
LTL=$(syfco -f ltlxba -m fully FILE)
|
LTL=$(syfco -f ltlxba -m fully FILE)
|
||||||
|
|
@ -181,6 +192,7 @@ OUT=$(syfco --print-output-signals FILE)
|
||||||
ltlsynt --formula="$LTL" --outs="$OUT"
|
ltlsynt --formula="$LTL" --outs="$OUT"
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
* 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
|
||||||
|
|
@ -237,13 +249,13 @@ be tried by separating them using commas. For instance
|
||||||
|
|
||||||
You can also ask =ltlsynt= to print to obtained parity game into
|
You can also ask =ltlsynt= to print to obtained parity game into
|
||||||
[[https://github.com/tcsprojects/pgsolver][PGSolver]] format, with the flag =--print-pg=, or in the HOA format,
|
[[https://github.com/tcsprojects/pgsolver][PGSolver]] format, with the flag =--print-pg=, or in the HOA format,
|
||||||
using =--print-game-hoa=. These flag deactivate the resolution of the
|
using =--print-game-hoa=. These flags deactivate the resolution of the
|
||||||
parity game. Note that if any of those flag is used with =--dot=, the game
|
parity game. Note that if any of those flag is used with =--dot=, the game
|
||||||
will be printed in the Dot format instead:
|
will be printed in the Dot format instead:
|
||||||
|
|
||||||
#+NAME: examplegamedot
|
#+NAME: examplegamedot
|
||||||
#+BEGIN_SRC sh :exports code
|
#+BEGIN_SRC sh :exports code
|
||||||
ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --print-game-hoa --dot
|
ltlsynt -f '(i1 & i2) <-> F(o1 & X(!o1))' --print-game-hoa --dot
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
#+BEGIN_SRC dot :file ltlsyntexgame.svg :var txt=examplegamedot :exports results
|
#+BEGIN_SRC dot :file ltlsyntexgame.svg :var txt=examplegamedot :exports results
|
||||||
$txt
|
$txt
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ automaton has 6 states
|
||||||
solving game with acceptance: co-Büchi
|
solving game with acceptance: co-Büchi
|
||||||
game solved in X seconds
|
game solved in X seconds
|
||||||
EOF
|
EOF
|
||||||
ltlsynt -f "G(Fi0 && Fi1 && Fi2) -> G(i1 <-> o0)" --outs="o0" --algo=lar \
|
ltlsynt -f "G(Fi0 && Fi1 && Fi2) -> G(i1 <-> o0)" --algo=lar \
|
||||||
--verbose --realizability 2> out
|
--verbose --realizability 2> out
|
||||||
sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx
|
sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx
|
||||||
diff outx exp
|
diff outx exp
|
||||||
|
|
@ -625,14 +625,14 @@ diff stdout expected
|
||||||
|
|
||||||
ltlsynt --ins=a,b --outs=c,a -f 'GFa | FGc | GFb' 2>stderr && :
|
ltlsynt --ins=a,b --outs=c,a -f 'GFa | FGc | GFb' 2>stderr && :
|
||||||
test $? -eq 2
|
test $? -eq 2
|
||||||
grep "'a' appears both" stderr
|
grep "'a' appears in both" stderr
|
||||||
|
|
||||||
ltlsynt --ins=a --outs=c -f 'GFa | FGb | GFc' 2>stderr && :
|
ltlsynt --ins=a --outs=c -f 'GFa | FGb | GFc' 2>stderr && :
|
||||||
test $? -eq 2
|
test $? -eq 2
|
||||||
grep "both.*but 'b' is unlisted" 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 "one of --ins or --outs" stderr
|
grep "[-]-ins or --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
|
||||||
|
|
@ -903,7 +903,7 @@ ltlsynt --outs="" -f "GFb" | grep "UNREALIZABLE"
|
||||||
ltlsynt --outs="" -f "1"
|
ltlsynt --outs="" -f "1"
|
||||||
|
|
||||||
ltlsynt --outs="" --ins="" -f "GFa" 2>&1 | \
|
ltlsynt --outs="" --ins="" -f "GFa" 2>&1 | \
|
||||||
grep "both --ins and --outs are specified"
|
grep "one of --ins or --outs should match 'a'"
|
||||||
|
|
||||||
LTL='(((((G (((((((g_0) && (G (! (r_0)))) -> (F (! (g_0)))) && (((g_0) &&
|
LTL='(((((G (((((((g_0) && (G (! (r_0)))) -> (F (! (g_0)))) && (((g_0) &&
|
||||||
(X ((! (r_0)) && (! (g_0))))) -> (X ((r_0) R (! (g_0)))))) && (((g_1) &&
|
(X ((! (r_0)) && (! (g_0))))) -> (X ((r_0) R (! (g_0)))))) && (((g_1) &&
|
||||||
|
|
@ -1099,17 +1099,23 @@ s7="G(o07 <-> (i7 & i8)) & G((i7 & i8) -> (o11 U i3)) & GFo12 & G(o04 <-> "
|
||||||
s8="(i4 & i6)) & G(o05 <-> !(i4 & i6)) & G(o15 <-> (i7 & i8)) & G(i7 -> o02) & "
|
s8="(i4 & i6)) & G(o05 <-> !(i4 & i6)) & G(o15 <-> (i7 & i8)) & G(i7 -> o02) & "
|
||||||
s9="G((!i7 & !(i1 & i2 & !i5 & i6)) -> o03) & G(o01 <-> (i1 & i2 & !i5 & i6))))"
|
s9="G((!i7 & !(i1 & i2 & !i5 & i6)) -> o03) & G(o01 <-> (i1 & i2 & !i5 & i6))))"
|
||||||
s=$s1$s2$s3$s4$s5$s6$s7$s8$s9
|
s=$s1$s2$s3$s4$s5$s6$s7$s8$s9
|
||||||
ltlsynt --decomp=yes -f "$s" --ins=i1,i2,i3,i4,i5,i6,i7,i8 --realizability >out
|
ltlsynt --decomp=yes -f "$s" --realizability >out
|
||||||
ltlsynt --decomp=no -f "$s" --ins=i1,i2,i3,i4,i5,i6,i7,i8 --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
|
||||||
cat >expected <<EOF
|
cat >expected <<EOF
|
||||||
REALIZABLE
|
REALIZABLE
|
||||||
REALIZABLE
|
REALIZABLE
|
||||||
|
UNREALIZABLE
|
||||||
|
REALIZABLE
|
||||||
EOF
|
EOF
|
||||||
diff out expected
|
diff out expected
|
||||||
|
|
||||||
f1="((G ((p0) <-> (! (p1)))) && (((((F ((b) && (G (F (a))))) ||\
|
f1="((G ((p0) <-> (! (p1)))) && (((((F ((b) && (G (F (a))))) ||\
|
||||||
(F ((c) && (G (F (! (a))))))) && (F (b))) && (F (c))) <-> (G (F (p0)))))"
|
(F ((c) && (G (F (! (a))))))) && (F (b))) && (F (c))) <-> (G (F (p0)))))"
|
||||||
ltlsynt -f "$f1" --outs="p1, p0" --aiger > /dev/null
|
ltlsynt -f "$f1" --outs="p1, p0" --aiger > out1.hoa
|
||||||
|
ltlsynt -f "$f1" --outs="p1, /^p/" --aiger > out2.hoa
|
||||||
|
diff out1.hoa out2.hoa
|
||||||
|
|
||||||
# issue #557
|
# issue #557
|
||||||
ltlsynt -f 'G(in1 <-> out0) & G(in0 <-> out1)' --ins=in1,in0 --verb 2>err >out
|
ltlsynt -f 'G(in1 <-> out0) & G(in0 <-> out1)' --ins=in1,in0 --verb 2>err >out
|
||||||
|
|
@ -1120,3 +1126,20 @@ cat >err2.ex <<EOF
|
||||||
EOF
|
EOF
|
||||||
diff err2 err2.ex
|
diff err2 err2.ex
|
||||||
grep -F '[!0&!1&!2&!3 | !0&!1&2&3 | 0&1&!2&!3 | 0&1&2&3] 0' out
|
grep -F '[!0&!1&!2&!3 | !0&!1&2&3 | 0&1&!2&!3 | 0&1&2&3] 0' out
|
||||||
|
|
||||||
|
ltlsynt -f 'G((in1 && in2) <-> XXout2) && G((in1 | !in2) -> Fout2)' \
|
||||||
|
--realizability >out && :
|
||||||
|
test $? -eq 1
|
||||||
|
# specitying --outs=in2 should have priority over regular expressions
|
||||||
|
ltlsynt -f 'G((in1 && in2) <-> XXout2) && G((in1 | !in2) -> Fout2)' \
|
||||||
|
--realizability --ins='/^i/' --outs='out2,in2' >>out
|
||||||
|
cat >expected <<EOF
|
||||||
|
UNREALIZABLE
|
||||||
|
REALIZABLE
|
||||||
|
EOF
|
||||||
|
diff out expected
|
||||||
|
|
||||||
|
ltlsynt --ins='/^in/,/env/' --outs='/^out/,/control/' \
|
||||||
|
-f 'G(controlenv <-> input)' 2>err && :
|
||||||
|
test $? -eq 2
|
||||||
|
grep 'controlenv.*matches both' err
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue