kill the ltl namespace

* NEWS: Mention it.
* bench/stutter/stutter_invariance_formulas.cc,
bench/stutter/stutter_invariance_randomgraph.cc, doc/mainpage.dox,
doc/org/tut01.org, doc/org/tut02.org, doc/org/tut10.org, doc/tl/tl.tex,
iface/ltsmin/ltsmin.cc, iface/ltsmin/ltsmin.hh,
iface/ltsmin/modelcheck.cc, src/bin/autfilt.cc,
src/bin/common_aoutput.cc, src/bin/common_aoutput.hh,
src/bin/common_finput.cc, src/bin/common_finput.hh,
src/bin/common_output.cc, src/bin/common_output.hh, src/bin/common_r.hh,
src/bin/common_trans.cc, src/bin/common_trans.hh, src/bin/dstar2tgba.cc,
src/bin/genltl.cc, src/bin/ltl2tgba.cc, src/bin/ltl2tgta.cc,
src/bin/ltlcross.cc, src/bin/ltldo.cc, src/bin/ltlfilt.cc,
src/bin/ltlgrind.cc, src/bin/randaut.cc, src/bin/randltl.cc,
src/kripke/kripkeexplicit.cc, src/kripke/kripkeexplicit.hh,
src/kripkeparse/kripkeparse.yy, src/kripkeparse/public.hh,
src/ltlparse/fmterror.cc, src/ltlparse/ltlparse.yy,
src/ltlparse/ltlscan.ll, src/ltlparse/parsedecl.hh,
src/ltlparse/public.hh, src/parseaut/parseaut.yy,
src/parseaut/public.hh, src/tests/checkpsl.cc, src/tests/checkta.cc,
src/tests/complementation.cc, src/tests/consterm.cc,
src/tests/emptchk.cc, src/tests/equalsf.cc, src/tests/ikwiad.cc,
src/tests/kind.cc, src/tests/length.cc, src/tests/ltlprod.cc,
src/tests/ltlrel.cc, src/tests/parse.test,
src/tests/parse_print_test.cc, src/tests/randtgba.cc,
src/tests/readltl.cc, src/tests/reduc.cc, src/tests/syntimpl.cc,
src/tests/taatgba.cc, src/tests/tostring.cc, src/tests/tostring.test,
src/tl/apcollect.cc, src/tl/apcollect.hh, src/tl/contain.cc,
src/tl/contain.hh, src/tl/declenv.cc, src/tl/declenv.hh,
src/tl/defaultenv.cc, src/tl/defaultenv.hh, src/tl/dot.cc,
src/tl/dot.hh, src/tl/environment.hh, src/tl/exclusive.cc,
src/tl/exclusive.hh, src/tl/formula.cc, src/tl/formula.hh,
src/tl/length.cc, src/tl/length.hh, src/tl/mark.cc, src/tl/mark.hh,
src/tl/mutation.cc, src/tl/mutation.hh, src/tl/nenoform.cc,
src/tl/nenoform.hh, src/tl/print.cc, src/tl/print.hh,
src/tl/randomltl.cc, src/tl/randomltl.hh, src/tl/relabel.cc,
src/tl/relabel.hh, src/tl/remove_x.cc, src/tl/remove_x.hh,
src/tl/simpfg.cc, src/tl/simpfg.hh, src/tl/simplify.cc,
src/tl/simplify.hh, src/tl/snf.cc, src/tl/snf.hh, src/tl/unabbrev.cc,
src/tl/unabbrev.hh, src/twa/bdddict.cc, src/twa/bdddict.hh,
src/twa/bddprint.cc, src/twa/formula2bdd.cc, src/twa/formula2bdd.hh,
src/twa/taatgba.cc, src/twa/taatgba.hh, src/twa/twa.hh,
src/twa/twagraph.cc, src/twa/twagraph.hh, src/twaalgos/compsusp.cc,
src/twaalgos/compsusp.hh, src/twaalgos/ltl2taa.cc,
src/twaalgos/ltl2taa.hh, src/twaalgos/ltl2tgba_fm.cc,
src/twaalgos/ltl2tgba_fm.hh, src/twaalgos/minimize.cc,
src/twaalgos/minimize.hh, src/twaalgos/neverclaim.cc,
src/twaalgos/postproc.cc, src/twaalgos/postproc.hh,
src/twaalgos/powerset.cc, src/twaalgos/powerset.hh,
src/twaalgos/randomgraph.cc, src/twaalgos/randomgraph.hh,
src/twaalgos/relabel.cc, src/twaalgos/relabel.hh,
src/twaalgos/remprop.cc, src/twaalgos/remprop.hh, src/twaalgos/stats.cc,
src/twaalgos/stats.hh, src/twaalgos/stutter.cc, src/twaalgos/stutter.hh,
src/twaalgos/translate.cc, src/twaalgos/translate.hh,
wrap/python/spot_impl.i: Remove the ltl namespace.
This commit is contained in:
Alexandre Duret-Lutz 2015-09-28 16:20:53 +02:00
parent 6ded5e75c4
commit cb39210166
137 changed files with 10771 additions and 10919 deletions

View file

@ -26,43 +26,40 @@
namespace spot
{
namespace ltl
atomic_prop_set create_atomic_prop_set(unsigned n)
{
atomic_prop_set create_atomic_prop_set(unsigned n)
{
atomic_prop_set res;
for (unsigned i = 0; i < n; ++i)
{
std::ostringstream p;
p << 'p' << i;
res.insert(formula::ap(p.str()));
}
return res;
}
atomic_prop_set res;
for (unsigned i = 0; i < n; ++i)
{
std::ostringstream p;
p << 'p' << i;
res.insert(formula::ap(p.str()));
}
return res;
}
atomic_prop_set*
atomic_prop_collect(formula f, atomic_prop_set* s)
{
if (!s)
s = new atomic_prop_set;
f.traverse([&](const formula& f)
{
if (f.is(op::ap))
s->insert(f);
return false;
});
return s;
}
atomic_prop_set*
atomic_prop_collect(formula f, atomic_prop_set* s)
{
if (!s)
s = new atomic_prop_set;
f.traverse([&](const formula& f)
{
if (f.is(op::ap))
s->insert(f);
return false;
});
return s;
}
bdd
atomic_prop_collect_as_bdd(formula f, const twa_ptr& a)
{
spot::ltl::atomic_prop_set aps;
atomic_prop_collect(f, &aps);
bdd res = bddtrue;
for (auto f: aps)
res &= bdd_ithvar(a->register_ap(f));
return res;
}
bdd
atomic_prop_collect_as_bdd(formula f, const twa_ptr& a)
{
spot::atomic_prop_set aps;
atomic_prop_collect(f, &aps);
bdd res = bddtrue;
for (auto f: aps)
res &= bdd_ithvar(a->register_ap(f));
return res;
}
}

View file

@ -29,38 +29,35 @@
namespace spot
{
namespace ltl
{
/// \addtogroup ltl_misc
/// @{
/// \addtogroup ltl_misc
/// @{
/// Set of atomic propositions.
typedef std::set<formula> atomic_prop_set;
/// Set of atomic propositions.
typedef std::set<formula> atomic_prop_set;
/// \brief construct an atomic_prop_set with n propositions
SPOT_API
atomic_prop_set create_atomic_prop_set(unsigned n);
/// \brief construct an atomic_prop_set with n propositions
SPOT_API
atomic_prop_set create_atomic_prop_set(unsigned n);
/// \brief Return the set of atomic propositions occurring in a formula.
///
/// \param f the formula to inspect
/// \param s an existing set to fill with atomic_propositions discovered,
/// or 0 if the set should be allocated by the function.
/// \return A pointer to the supplied set, \c s, augmented with
/// atomic propositions occurring in \c f; or a newly allocated
/// set containing all these atomic propositions if \c s is 0.
SPOT_API atomic_prop_set*
atomic_prop_collect(formula f, atomic_prop_set* s = nullptr);
/// \brief Return the set of atomic propositions occurring in a formula.
///
/// \param f the formula to inspect
/// \param s an existing set to fill with atomic_propositions discovered,
/// or 0 if the set should be allocated by the function.
/// \return A pointer to the supplied set, \c s, augmented with
/// atomic propositions occurring in \c f; or a newly allocated
/// set containing all these atomic propositions if \c s is 0.
SPOT_API atomic_prop_set*
atomic_prop_collect(formula f, atomic_prop_set* s = nullptr);
/// \brief Return the set of atomic propositions occurring in a
/// formula, as a BDD.
///
/// \param f the formula to inspect
/// \param a that automaton that should register the BDD variables used.
/// \return A conjunction the atomic propositions.
SPOT_API bdd
atomic_prop_collect_as_bdd(formula f, const twa_ptr& a);
/// \brief Return the set of atomic propositions occurring in a
/// formula, as a BDD.
///
/// \param f the formula to inspect
/// \param a that automaton that should register the BDD variables used.
/// \return A conjunction the atomic propositions.
SPOT_API bdd
atomic_prop_collect_as_bdd(formula f, const twa_ptr& a);
/// @}
}
/// @}
}

View file

@ -27,114 +27,110 @@
namespace spot
{
namespace ltl
{
language_containment_checker::language_containment_checker
(const bdd_dict_ptr& dict, bool exprop, bool symb_merge,
bool branching_postponement, bool fair_loop_approx)
: dict_(dict), exprop_(exprop), symb_merge_(symb_merge),
language_containment_checker::language_containment_checker
(const bdd_dict_ptr& dict, bool exprop, bool symb_merge,
bool branching_postponement, bool fair_loop_approx)
: dict_(dict), exprop_(exprop), symb_merge_(symb_merge),
branching_postponement_(branching_postponement),
fair_loop_approx_(fair_loop_approx)
{
}
{
}
language_containment_checker::~language_containment_checker()
{
clear();
}
language_containment_checker::~language_containment_checker()
{
clear();
}
void
language_containment_checker::clear()
{
translated_.clear();
}
void
language_containment_checker::clear()
{
translated_.clear();
}
bool
language_containment_checker::incompatible_(record_* l, record_* g)
{
record_::incomp_map::const_iterator i = l->incompatible.find(g);
if (i != l->incompatible.end())
return i->second;
bool
language_containment_checker::incompatible_(record_* l, record_* g)
{
record_::incomp_map::const_iterator i = l->incompatible.find(g);
if (i != l->incompatible.end())
return i->second;
bool res = product(l->translation, g->translation)->is_empty();
l->incompatible[g] = res;
g->incompatible[l] = res;
return res;
}
bool res = product(l->translation, g->translation)->is_empty();
l->incompatible[g] = res;
g->incompatible[l] = res;
return res;
}
// Check whether L(l) is a subset of L(g).
bool
language_containment_checker::contained(formula l,
formula g)
{
if (l == g)
return true;
record_* rl = register_formula_(l);
record_* rng = register_formula_(formula::Not(g));
return incompatible_(rl, rng);
}
// Check whether L(l) is a subset of L(g).
bool
language_containment_checker::contained(formula l,
formula g)
{
if (l == g)
return true;
record_* rl = register_formula_(l);
record_* rng = register_formula_(formula::Not(g));
return incompatible_(rl, rng);
}
// Check whether L(!l) is a subset of L(g).
bool
language_containment_checker::neg_contained(formula l,
formula g)
{
if (l == g)
return false;
formula nl = formula::Not(l);
record_* rnl = register_formula_(nl);
record_* rng = register_formula_(formula::Not(g));
if (nl == g)
return true;
return incompatible_(rnl, rng);
}
// Check whether L(!l) is a subset of L(g).
bool
language_containment_checker::neg_contained(formula l,
formula g)
{
if (l == g)
return false;
formula nl = formula::Not(l);
record_* rnl = register_formula_(nl);
record_* rng = register_formula_(formula::Not(g));
if (nl == g)
return true;
return incompatible_(rnl, rng);
}
// Check whether L(l) is a subset of L(!g).
bool
language_containment_checker::contained_neg(formula l,
formula g)
{
if (l == g)
return false;
record_* rl = register_formula_(l);
record_* rg = register_formula_(g);
return incompatible_(rl, rg);
}
// Check whether L(l) is a subset of L(!g).
bool
language_containment_checker::contained_neg(formula l,
formula g)
{
if (l == g)
return false;
record_* rl = register_formula_(l);
record_* rg = register_formula_(g);
return incompatible_(rl, rg);
}
// Check whether L(l) = L(g).
bool
language_containment_checker::equal(formula l, formula g)
{
return contained(l, g) && contained(g, l);
}
// Check whether L(l) = L(g).
bool
language_containment_checker::equal(formula l, formula g)
{
return contained(l, g) && contained(g, l);
}
language_containment_checker::record_*
language_containment_checker::register_formula_(formula f)
{
trans_map::iterator i = translated_.find(f);
if (i != translated_.end())
return &i->second;
language_containment_checker::record_*
language_containment_checker::register_formula_(formula f)
{
trans_map::iterator i = translated_.find(f);
if (i != translated_.end())
return &i->second;
auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_,
branching_postponement_, fair_loop_approx_);
record_& r = translated_[f];
r.translation = e;
return &r;
}
auto e = ltl_to_tgba_fm(f, dict_, exprop_, symb_merge_,
branching_postponement_, fair_loop_approx_);
record_& r = translated_[f];
r.translation = e;
return &r;
}
formula
reduce_tau03(formula f, bool stronger)
{
if (!f.is_psl_formula())
return f;
formula
reduce_tau03(formula f, bool stronger)
{
if (!f.is_psl_formula())
return f;
ltl_simplifier_options opt(false, false, false,
true, stronger);
ltl_simplifier simpl(opt);
return simpl.simplify(f);
}
ltl_simplifier_options opt(false, false, false,
true, stronger);
ltl_simplifier simpl(opt);
return simpl.simplify(f);
}
}

View file

@ -29,54 +29,51 @@
namespace spot
{
namespace ltl
/// Check containment between LTL formulae.
class SPOT_API language_containment_checker
{
/// Check containment between LTL formulae.
class SPOT_API language_containment_checker
struct record_
{
struct record_
{
const_twa_graph_ptr translation;
typedef std::map<const record_*, bool> incomp_map;
incomp_map incompatible;
};
typedef std::unordered_map<formula, record_> trans_map;
public:
/// This class uses spot::ltl_to_tgba_fm to translate LTL
/// formulae. See that function for the meaning of these options.
language_containment_checker(const bdd_dict_ptr& dict, bool exprop,
bool symb_merge,
bool branching_postponement,
bool fair_loop_approx);
~language_containment_checker();
/// Clear the cache.
void clear();
/// Check whether L(l) is a subset of L(g).
bool contained(formula l, formula g);
/// Check whether L(!l) is a subset of L(g).
bool neg_contained(formula l, formula g);
/// Check whether L(l) is a subset of L(!g).
bool contained_neg(formula l, formula g);
/// Check whether L(l) = L(g).
bool equal(formula l, formula g);
protected:
bool incompatible_(record_* l, record_* g);
record_* register_formula_(formula f);
/* Translation options */
bdd_dict_ptr dict_;
bool exprop_;
bool symb_merge_;
bool branching_postponement_;
bool fair_loop_approx_;
/* Translation Maps */
trans_map translated_;
const_twa_graph_ptr translation;
typedef std::map<const record_*, bool> incomp_map;
incomp_map incompatible;
};
}
typedef std::unordered_map<formula, record_> trans_map;
public:
/// This class uses spot::ltl_to_tgba_fm to translate LTL
/// formulae. See that function for the meaning of these options.
language_containment_checker(const bdd_dict_ptr& dict, bool exprop,
bool symb_merge,
bool branching_postponement,
bool fair_loop_approx);
~language_containment_checker();
/// Clear the cache.
void clear();
/// Check whether L(l) is a subset of L(g).
bool contained(formula l, formula g);
/// Check whether L(!l) is a subset of L(g).
bool neg_contained(formula l, formula g);
/// Check whether L(l) is a subset of L(!g).
bool contained_neg(formula l, formula g);
/// Check whether L(l) = L(g).
bool equal(formula l, formula g);
protected:
bool incompatible_(record_* l, record_* g);
record_* register_formula_(formula f);
/* Translation options */
bdd_dict_ptr dict_;
bool exprop_;
bool symb_merge_;
bool branching_postponement_;
bool fair_loop_approx_;
/* Translation Maps */
trans_map translated_;
};
}

View file

@ -24,42 +24,38 @@
namespace spot
{
namespace ltl
declarative_environment::declarative_environment()
{
}
declarative_environment::declarative_environment()
{
}
bool
declarative_environment::declare(const std::string& prop_str)
{
if (props_.find(prop_str) != props_.end())
return false;
props_[prop_str] = formula::ap(prop_str);
return true;
}
bool
declarative_environment::declare(const std::string& prop_str)
{
if (props_.find(prop_str) != props_.end())
return false;
props_[prop_str] = formula::ap(prop_str);
return true;
}
formula
declarative_environment::require(const std::string& prop_str)
{
prop_map::iterator i = props_.find(prop_str);
if (i == props_.end())
return nullptr;
return i->second;
}
formula
declarative_environment::require(const std::string& prop_str)
{
prop_map::iterator i = props_.find(prop_str);
if (i == props_.end())
return nullptr;
return i->second;
}
const std::string&
declarative_environment::name() const
{
static std::string name("declarative environment");
return name;
}
const std::string&
declarative_environment::name() const
{
static std::string name("declarative environment");
return name;
}
const declarative_environment::prop_map&
declarative_environment::get_prop_map() const
{
return props_;
}
const declarative_environment::prop_map&
declarative_environment::get_prop_map() const
{
return props_;
}
}

View file

@ -29,36 +29,32 @@
namespace spot
{
namespace ltl
/// \ingroup ltl_environment
/// \brief A declarative environment.
///
/// This environment recognizes all atomic propositions
/// that have been previously declared. It will reject other.
class SPOT_API declarative_environment : public environment
{
public:
declarative_environment();
~declarative_environment() = default;
/// \ingroup ltl_environment
/// \brief A declarative environment.
///
/// This environment recognizes all atomic propositions
/// that have been previously declared. It will reject other.
class SPOT_API declarative_environment : public environment
{
public:
declarative_environment();
~declarative_environment() = default;
/// Declare an atomic proposition. Return false iff the
/// proposition was already declared.
bool declare(const std::string& prop_str);
/// Declare an atomic proposition. Return false iff the
/// proposition was already declared.
bool declare(const std::string& prop_str);
virtual formula require(const std::string& prop_str);
virtual formula require(const std::string& prop_str);
/// Get the name of the environment.
virtual const std::string& name() const;
/// Get the name of the environment.
virtual const std::string& name() const;
typedef std::map<const std::string, formula> prop_map;
typedef std::map<const std::string, formula> prop_map;
/// Get the map of atomic proposition known to this environment.
const prop_map& get_prop_map() const;
/// Get the map of atomic proposition known to this environment.
const prop_map& get_prop_map() const;
private:
prop_map props_;
};
}
private:
prop_map props_;
};
}

View file

@ -24,36 +24,31 @@
namespace spot
{
namespace ltl
default_environment::~default_environment()
{
}
default_environment::~default_environment()
{
}
formula
default_environment::require(const std::string& s)
{
return formula::ap(s);
}
formula
default_environment::require(const std::string& s)
{
return formula::ap(s);
}
const std::string&
default_environment::name() const
{
static std::string name("default environment");
return name;
}
const std::string&
default_environment::name() const
{
static std::string name("default environment");
return name;
}
default_environment::default_environment()
{
}
default_environment&
default_environment::instance()
{
static default_environment* singleton = new default_environment();
return *singleton;
}
default_environment::default_environment()
{
}
default_environment&
default_environment::instance()
{
static default_environment* singleton = new default_environment();
return *singleton;
}
}

View file

@ -27,27 +27,23 @@
namespace spot
{
namespace ltl
/// \ingroup ltl_environment
/// \brief A laxist environment.
///
/// This environment recognizes all atomic propositions.
///
/// This is a singleton. Use default_environment::instance()
/// to obtain the instance.
class SPOT_API default_environment final: public environment
{
public:
virtual ~default_environment();
virtual formula require(const std::string& prop_str);
virtual const std::string& name() const;
/// \ingroup ltl_environment
/// \brief A laxist environment.
///
/// This environment recognizes all atomic propositions.
///
/// This is a singleton. Use default_environment::instance()
/// to obtain the instance.
class SPOT_API default_environment final: public environment
{
public:
virtual ~default_environment();
virtual formula require(const std::string& prop_str);
virtual const std::string& name() const;
/// Get the sole instance of spot::ltl::default_environment.
static default_environment& instance();
protected:
default_environment();
};
}
/// Get the sole instance of spot::default_environment.
static default_environment& instance();
protected:
default_environment();
};
}

View file

@ -28,110 +28,107 @@
namespace spot
{
namespace ltl
namespace
{
namespace
struct dot_printer final
{
struct dot_printer final
{
std::ostream& os_;
std::unordered_map<formula, int> node_;
std::ostringstream* sinks_;
std::ostream& os_;
std::unordered_map<formula, int> node_;
std::ostringstream* sinks_;
dot_printer(std::ostream& os, formula f)
: os_(os), sinks_(new std::ostringstream)
{
os_ << "digraph G {\n";
rec(f);
os_ << " subgraph atoms {\n rank=sink;\n"
<< sinks_->str() << " }\n}\n";
}
dot_printer(std::ostream& os, formula f)
: os_(os), sinks_(new std::ostringstream)
{
os_ << "digraph G {\n";
rec(f);
os_ << " subgraph atoms {\n rank=sink;\n"
<< sinks_->str() << " }\n}\n";
}
~dot_printer()
~dot_printer()
{
delete sinks_;
}
int rec(formula f)
{
auto i = node_.emplace(f, node_.size());
int src = i.first->second;
if (!i.second)
return src;
op o = f.kind();
std::string str = (o == op::ap) ? f.ap_name() : f.kindstr();
if (o == op::ap || f.is_constant())
*sinks_ << " " << src << " [label=\""
<< str << "\", shape=box];\n";
else
os_ << " " << src << " [label=\"" << str << "\"];\n";
int childnum = 0;
switch (o)
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
childnum = 0; // No number for children
break;
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
childnum = -2; // L and R markers
break;
case op::Concat:
case op::Fusion:
childnum = 1; // Numbered children
break;
}
for (auto c: f)
{
os_ << " " << src << " -> " << rec(c);
if (childnum > 0)
os_ << " [taillabel=\"" << childnum << "\"]";
if (childnum == -2)
os_ << " [taillabel=\"L\"]";
else if (childnum == -1)
os_ << " [taillabel=\"R\"]";
os_ << ";\n";
++childnum;
}
int rec(formula f)
{
auto i = node_.emplace(f, node_.size());
int src = i.first->second;
if (!i.second)
return src;
}
};
}
std::ostream&
print_dot_psl(std::ostream& os, formula f)
{
dot_printer p(os, f);
return os;
}
op o = f.kind();
std::string str = (o == op::ap) ? f.ap_name() : f.kindstr();
if (o == op::ap || f.is_constant())
*sinks_ << " " << src << " [label=\""
<< str << "\", shape=box];\n";
else
os_ << " " << src << " [label=\"" << str << "\"];\n";
int childnum = 0;
switch (o)
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
childnum = 0; // No number for children
break;
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
childnum = -2; // L and R markers
break;
case op::Concat:
case op::Fusion:
childnum = 1; // Numbered children
break;
}
for (auto c: f)
{
os_ << " " << src << " -> " << rec(c);
if (childnum > 0)
os_ << " [taillabel=\"" << childnum << "\"]";
if (childnum == -2)
os_ << " [taillabel=\"L\"]";
else if (childnum == -1)
os_ << " [taillabel=\"R\"]";
os_ << ";\n";
++childnum;
}
return src;
}
};
}
std::ostream&
print_dot_psl(std::ostream& os, formula f)
{
dot_printer p(os, f);
return os;
}
}

View file

@ -26,16 +26,13 @@
namespace spot
{
namespace ltl
{
/// \ingroup ltl_io
/// \brief Write a formula tree using dot's syntax.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
///
/// \c dot is part of the GraphViz package
/// http://www.graphviz.org/
SPOT_API
std::ostream& print_dot_psl(std::ostream& os, formula f);
}
/// \ingroup ltl_io
/// \brief Write a formula tree using dot's syntax.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
///
/// \c dot is part of the GraphViz package
/// http://www.graphviz.org/
SPOT_API
std::ostream& print_dot_psl(std::ostream& os, formula f);
}

View file

@ -27,34 +27,30 @@
namespace spot
{
namespace ltl
/// \ingroup ltl_essential
/// \brief An environment that describes atomic propositions.
class environment
{
/// \ingroup ltl_essential
/// \brief An environment that describes atomic propositions.
class environment
public:
/// \brief Obtain the formula associated to \a prop_str
///
/// Usually \a prop_str, is the name of an atomic proposition,
/// and spot::require simply returns the associated
/// spot::formula.
///
/// Note this is not a \c const method. Some environments will
/// "create" the atomic proposition when requested.
///
/// \return 0 iff \a prop_str is not part of the environment,
/// or the associated spot::formula otherwise.
virtual formula require(const std::string& prop_str) = 0;
/// Get the name of the environment.
virtual const std::string& name() const = 0;
virtual
~environment()
{
public:
/// \brief Obtain the formula associated to \a prop_str
///
/// Usually \a prop_str, is the name of an atomic proposition,
/// and spot::ltl::require simply returns the associated
/// spot::ltl::formula.
///
/// Note this is not a \c const method. Some environments will
/// "create" the atomic proposition when requested.
///
/// \return 0 iff \a prop_str is not part of the environment,
/// or the associated spot::ltl::formula otherwise.
virtual formula require(const std::string& prop_str) = 0;
/// Get the name of the environment.
virtual const std::string& name() const = 0;
virtual
~environment()
{
}
};
}
}
};
}

View file

@ -27,10 +27,10 @@ namespace spot
{
namespace
{
static const std::vector<ltl::formula>
static const std::vector<formula>
split_aps(const char* arg)
{
std::vector<ltl::formula> group;
std::vector<formula> group;
auto start = arg;
while (*start)
{
@ -61,7 +61,7 @@ namespace spot
throw std::invalid_argument(s);
}
std::string ap(start, end - start);
group.emplace_back(ltl::formula::ap(ap));
group.emplace_back(formula::ap(ap));
do
++end;
while (*end == ' ' || *end == '\t');
@ -86,7 +86,7 @@ namespace spot
while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t'))
--rend;
std::string ap(start, rend - start);
group.emplace_back(ltl::formula::ap(ap));
group.emplace_back(formula::ap(ap));
if (*end == ',')
start = end + 1;
else
@ -102,27 +102,27 @@ namespace spot
add_group(split_aps(ap_csv));
}
void exclusive_ap::add_group(std::vector<ltl::formula> ap)
void exclusive_ap::add_group(std::vector<formula> ap)
{
groups.push_back(ap);
}
namespace
{
ltl::formula
nand(ltl::formula lhs, ltl::formula rhs)
formula
nand(formula lhs, formula rhs)
{
return ltl::formula::Not(ltl::formula::And({lhs, rhs}));
return formula::Not(formula::And({lhs, rhs}));
}
}
ltl::formula
exclusive_ap::constrain(ltl::formula f) const
formula
exclusive_ap::constrain(formula f) const
{
auto* s = atomic_prop_collect(f);
std::vector<ltl::formula> group;
std::vector<ltl::formula> v;
std::vector<formula> group;
std::vector<formula> v;
for (auto& g: groups)
{
@ -139,7 +139,7 @@ namespace spot
};
delete s;
return ltl::formula::And({f, ltl::formula::G(ltl::formula::And(v))});
return formula::And({f, formula::G(formula::And(v))});
}
twa_graph_ptr exclusive_ap::constrain(const_twa_graph_ptr aut,

View file

@ -27,10 +27,10 @@ namespace spot
{
class SPOT_API exclusive_ap final
{
std::vector<std::vector<ltl::formula>> groups;
std::vector<std::vector<formula>> groups;
public:
#ifndef SWIG
void add_group(std::vector<ltl::formula> ap);
void add_group(std::vector<formula> ap);
#endif
void add_group(const char* ap_csv);
@ -39,7 +39,7 @@ namespace spot
return groups.empty();
}
ltl::formula constrain(ltl::formula f) const;
formula constrain(formula f) const;
twa_graph_ptr constrain(const_twa_graph_ptr aut,
bool simplify_guards = false) const;
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -25,56 +25,53 @@
namespace spot
{
namespace ltl
int
length(formula f)
{
int
length(formula f)
{
int len = 0;
f.traverse([&len](const formula& x)
{
auto s = x.size();
if (s > 1)
len += s - 1;
else
++len;
return false;
});
return len;
}
int
length_boolone(formula f)
{
int len = 0;
f.traverse([&len](const formula& x)
{
if (x.is_boolean())
{
++len;
return true;
}
auto s = x.size();
if (s > 2)
{
int b = 0;
for (const auto& y: x)
if (y.is_boolean())
++b;
len += s - b * 2 + 1;
}
else if (s > 1)
{
len += s - 1;
}
else
{
++len;
}
return false;
});
return len;
}
int len = 0;
f.traverse([&len](const formula& x)
{
auto s = x.size();
if (s > 1)
len += s - 1;
else
++len;
return false;
});
return len;
}
int
length_boolone(formula f)
{
int len = 0;
f.traverse([&len](const formula& x)
{
if (x.is_boolean())
{
++len;
return true;
}
auto s = x.size();
if (s > 2)
{
int b = 0;
for (const auto& y: x)
if (y.is_boolean())
++b;
len += s - b * 2 + 1;
}
else if (s > 1)
{
len += s - 1;
}
else
{
++len;
}
return false;
});
return len;
}
}

View file

@ -26,28 +26,25 @@
namespace spot
{
namespace ltl
{
/// \ingroup ltl_misc
/// \brief Compute the length of a formula.
///
/// The length of a formula is the number of atomic propositions,
/// constants, and operators (logical and temporal) occurring in
/// the formula. n-ary operators count for n-1; for instance
/// <code>a | b | c</code> has length 5, even if there is only as
/// single <code>|</code> node internally.
///
/// If squash_boolean is set, all Boolean formulae are assumed
/// to have length one.
SPOT_API
int length(formula f);
/// \ingroup ltl_misc
/// \brief Compute the length of a formula.
///
/// The length of a formula is the number of atomic propositions,
/// constants, and operators (logical and temporal) occurring in
/// the formula. n-ary operators count for n-1; for instance
/// <code>a | b | c</code> has length 5, even if there is only as
/// single <code>|</code> node internally.
///
/// If squash_boolean is set, all Boolean formulae are assumed
/// to have length one.
SPOT_API
int length(formula f);
/// \ingroup ltl_misc
/// \brief Compute the length of a formula, squashing Boolean formulae
///
/// This is similar to spot::ltl::length(), except all Boolean
/// formulae are assumed to have length one.
SPOT_API
int length_boolone(formula f);
}
/// \ingroup ltl_misc
/// \brief Compute the length of a formula, squashing Boolean formulae
///
/// This is similar to spot::length(), except all Boolean
/// formulae are assumed to have length one.
SPOT_API
int length_boolone(formula f);
}

View file

@ -25,170 +25,166 @@
namespace spot
{
namespace ltl
formula
mark_tools::mark_concat_ops(formula f)
{
formula
mark_tools::mark_concat_ops(formula f)
{
f2f_map::iterator i = markops_.find(f);
if (i != markops_.end())
return i->second;
f2f_map::iterator i = markops_.find(f);
if (i != markops_.end())
return i->second;
ltl::formula res;
switch (f.kind())
formula res;
switch (f.kind())
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosureMarked:
case op::OrRat:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcatMarked:
case op::UConcat:
case op::Concat:
case op::Fusion:
res = f;
break;
case op::NegClosure:
res = formula::NegClosureMarked(f[0]);
break;
case op::EConcat:
res = formula::EConcatMarked(f[0], f[1]);
break;
case op::Or:
case op::And:
res = f.map([this](formula f)
{
return this->mark_concat_ops(f);
});
break;
case op::Xor:
case op::Implies:
case op::Equiv:
SPOT_UNIMPLEMENTED();
}
markops_[f] = res;
return res;
}
formula
mark_tools::simplify_mark(formula f)
{
if (!f.is_marked())
return f;
f2f_map::iterator i = simpmark_.find(f);
if (i != simpmark_.end())
return i->second;
auto recurse = [this](formula f)
{
return this->simplify_mark(f);
};
formula res;
switch (f.kind())
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
res = f;
break;
case op::Or:
res = f.map(recurse);
break;
case op::And:
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosureMarked:
case op::OrRat:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcatMarked:
case op::UConcat:
case op::Concat:
case op::Fusion:
res = f;
break;
case op::NegClosure:
res = ltl::formula::NegClosureMarked(f[0]);
break;
case op::EConcat:
res = ltl::formula::EConcatMarked(f[0], f[1]);
break;
case op::Or:
case op::And:
res = f.map([this](formula f)
{
return this->mark_concat_ops(f);
});
break;
case op::Xor:
case op::Implies:
case op::Equiv:
SPOT_UNIMPLEMENTED();
std::set<std::pair<formula, formula>> empairs;
std::set<formula> nmset;
std::vector<formula> elist;
std::vector<formula> nlist;
std::vector<formula> v;
for (auto c: f)
{
if (c.is(op::EConcatMarked))
{
empairs.emplace(c[0], c[1]);
v.push_back(c.map(recurse));
}
else if (c.is(op::EConcat))
{
elist.push_back(c);
}
else if (c.is(op::NegClosureMarked))
{
nmset.insert(c[0]);
v.push_back(c.map(recurse));
}
else if (c.is(op::NegClosure))
{
nlist.push_back(c);
}
else
{
v.push_back(c);
}
}
// Keep only the non-marked EConcat for which we
// have not seen a similar EConcatMarked.
for (auto e: elist)
if (empairs.find(std::make_pair(e[0], e[1]))
== empairs.end())
v.push_back(e);
// Keep only the non-marked NegClosure for which we
// have not seen a similar NegClosureMarked.
for (auto n: nlist)
if (nmset.find(n[0]) == nmset.end())
v.push_back(n);
res = formula::And(v);
}
break;
case op::Xor:
case op::Implies:
case op::Equiv:
case op::OrRat:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
case op::Concat:
case op::Fusion:
SPOT_UNIMPLEMENTED();
}
markops_[f] = res;
return res;
}
formula
mark_tools::simplify_mark(formula f)
{
if (!f.is_marked())
return f;
f2f_map::iterator i = simpmark_.find(f);
if (i != simpmark_.end())
return i->second;
auto recurse = [this](formula f)
{
return this->simplify_mark(f);
};
ltl::formula res;
switch (f.kind())
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
res = f;
break;
case op::Or:
res = f.map(recurse);
break;
case op::And:
{
std::set<std::pair<formula, formula>> empairs;
std::set<formula> nmset;
std::vector<formula> elist;
std::vector<formula> nlist;
std::vector<formula> v;
for (auto c: f)
{
if (c.is(op::EConcatMarked))
{
empairs.emplace(c[0], c[1]);
v.push_back(c.map(recurse));
}
else if (c.is(op::EConcat))
{
elist.push_back(c);
}
else if (c.is(op::NegClosureMarked))
{
nmset.insert(c[0]);
v.push_back(c.map(recurse));
}
else if (c.is(op::NegClosure))
{
nlist.push_back(c);
}
else
{
v.push_back(c);
}
}
// Keep only the non-marked EConcat for which we
// have not seen a similar EConcatMarked.
for (auto e: elist)
if (empairs.find(std::make_pair(e[0], e[1]))
== empairs.end())
v.push_back(e);
// Keep only the non-marked NegClosure for which we
// have not seen a similar NegClosureMarked.
for (auto n: nlist)
if (nmset.find(n[0]) == nmset.end())
v.push_back(n);
res = ltl::formula::And(v);
}
break;
case op::Xor:
case op::Implies:
case op::Equiv:
case op::OrRat:
case op::AndRat:
case op::AndNLM:
case op::Star:
case op::FStar:
case op::Concat:
case op::Fusion:
SPOT_UNIMPLEMENTED();
}
simpmark_[f] = res;
return res;
}
simpmark_[f] = res;
return res;
}
}

View file

@ -24,24 +24,20 @@
namespace spot
{
namespace ltl
class mark_tools final
{
class mark_tools final
{
public:
/// \ingroup ltl_rewriting
/// \brief Mark operators NegClosure and EConcat.
///
/// \param f The formula to rewrite.
formula mark_concat_ops(formula f);
public:
/// \ingroup ltl_rewriting
/// \brief Mark operators NegClosure and EConcat.
///
/// \param f The formula to rewrite.
formula mark_concat_ops(formula f);
formula simplify_mark(formula f);
formula simplify_mark(formula f);
private:
typedef std::unordered_map<formula, formula> f2f_map;
f2f_map simpmark_;
f2f_map markops_;
};
}
private:
typedef std::unordered_map<formula, formula> f2f_map;
f2f_map simpmark_;
f2f_map markops_;
};
}

View file

@ -32,340 +32,337 @@
namespace spot
{
namespace ltl
namespace
{
namespace
formula substitute_ap(formula f, formula ap_src, formula ap_dst)
{
formula substitute_ap(formula f, formula ap_src, formula ap_dst)
return f.map([&](formula f)
{
if (f == ap_src)
return ap_dst;
else
return substitute_ap(f, ap_src, ap_dst);
});
}
typedef std::vector<formula> vec;
class mutator final
{
int mutation_counter_ = 0;
formula f_;
unsigned opts_;
public:
mutator(formula f, unsigned opts) : f_(f), opts_(opts)
{
return f.map([&](formula f)
{
if (f == ap_src)
return ap_dst;
else
return substitute_ap(f, ap_src, ap_dst);
});
}
typedef std::vector<formula> vec;
class mutator final
formula mutate(formula f)
{
int mutation_counter_ = 0;
formula f_;
unsigned opts_;
public:
mutator(formula f, unsigned opts) : f_(f), opts_(opts)
{
}
auto recurse = [this](formula f)
{
return this->mutate(f);
};
formula mutate(formula f)
{
auto recurse = [this](formula f)
{
return this->mutate(f);
};
switch (f.kind())
{
case op::ff:
case op::tt:
case op::eword:
switch (f.kind())
{
case op::ff:
case op::tt:
case op::eword:
return f;
case op::ap:
if (opts_ & Mut_Ap2Const)
{
if (mutation_counter_-- == 0)
return formula::tt();
if (mutation_counter_-- == 0)
return formula::ff();
}
return f;
case op::Not:
case op::X:
case op::F:
case op::G:
if ((opts_ & Mut_Remove_Ops)
&& mutation_counter_-- == 0)
return f[0];
// fall through
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
if (mutation_counter_ < 0)
return f;
case op::ap:
if (opts_ & Mut_Ap2Const)
else
return f.map(recurse);
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Concat:
case op::Fusion:
{
int mos = f.size();
if (opts_ & Mut_Remove_Multop_Operands)
{
if (mutation_counter_-- == 0)
return formula::tt();
if (mutation_counter_-- == 0)
return formula::ff();
for (int i = 0; i < mos; ++i)
if (mutation_counter_-- == 0)
return f.all_but(i);
}
return f;
case op::Not:
case op::X:
case op::F:
case op::G:
if ((opts_ & Mut_Remove_Ops)
&& mutation_counter_-- == 0)
return f[0];
// fall through
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
if (opts_ & Mut_Split_Ops && f.is(op::AndNLM))
{
if (mutation_counter_ >= 0
&& mutation_counter_ < 2 * (mos - 1))
{
vec v1;
vec v2;
v1.push_back(f[0]);
bool reverse = false;
int i = 1;
while (i < mos)
{
if (mutation_counter_-- == 0)
break;
if (mutation_counter_-- == 0)
{
reverse = true;
break;
}
v1.push_back(f[i++]);
}
for (; i < mos; ++i)
v2.push_back(f[i]);
formula first = AndNLM_(v1);
formula second = AndNLM_(v2);
formula ost = formula::one_star();
if (!reverse)
return AndRat_(Concat_(first, ost), second);
else
return AndRat_(Concat_(second, ost), first);
}
else
{
mutation_counter_ -= 2 * (mos - 1);
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Concat:
case op::Fusion:
{
int mos = f.size();
if (opts_ & Mut_Remove_Multop_Operands)
{
for (int i = 0; i < mos; ++i)
if (mutation_counter_-- == 0)
return f.all_but(i);
}
if (opts_ & Mut_Split_Ops && f.is(op::AndNLM))
{
if (mutation_counter_ >= 0
&& mutation_counter_ < 2 * (mos - 1))
{
vec v1;
vec v2;
v1.push_back(f[0]);
bool reverse = false;
int i = 1;
while (i < mos)
{
if (mutation_counter_-- == 0)
break;
if (mutation_counter_-- == 0)
{
reverse = true;
break;
}
v1.push_back(f[i++]);
}
for (; i < mos; ++i)
v2.push_back(f[i]);
formula first = AndNLM_(v1);
formula second = AndNLM_(v2);
formula ost = formula::one_star();
if (!reverse)
return AndRat_(Concat_(first, ost), second);
else
return AndRat_(Concat_(second, ost), first);
}
else
{
mutation_counter_ -= 2 * (mos - 1);
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
{
formula first = f[0];
formula second = f[1];
op o = f.kind();
bool left_is_sere = o == op::EConcat
|| o == op::EConcatMarked
|| o == op::UConcat;
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
{
if (!left_is_sere)
return first;
else if (o == op::UConcat)
return formula::NegClosure(first);
else // EConcat or EConcatMarked
return formula::Closure(first);
}
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
return second;
if (opts_ & Mut_Rewrite_Ops)
{
switch (o)
{
case op::U:
if (mutation_counter_-- == 0)
return formula::W(first, second);
break;
case op::M:
if (mutation_counter_-- == 0)
return formula::R(first, second);
if (mutation_counter_-- == 0)
return formula::U(second, first);
break;
case op::R:
if (mutation_counter_-- == 0)
return formula::W(second, first);
break;
default:
break;
}
}
if (opts_ & Mut_Split_Ops)
{
switch (o)
{
case op::Equiv:
if (mutation_counter_-- == 0)
return formula::Implies(first, second);
if (mutation_counter_-- == 0)
return formula::Implies(second, first);
if (mutation_counter_-- == 0)
return formula::And({first, second});
if (mutation_counter_-- == 0)
{
// Negate the two argument sequentially (in this
// case right before left, otherwise different
// compilers will make different choices.
auto right = formula::Not(second);
return formula::And({formula::Not(first), right});
}
break;
case op::Xor:
if (mutation_counter_-- == 0)
return formula::And({first, formula::Not(second)});
if (mutation_counter_-- == 0)
return formula::And({formula::Not(first), second});
break;
default:
break;
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
case op::Star:
case op::FStar:
{
formula c = f[0];
op o = f.kind();
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
return c;
if (opts_ & Mut_Simplify_Bounds)
{
auto min = f.min();
auto max = f.max();
if (min > 0)
{
if (mutation_counter_-- == 0)
return formula::bunop(o, c, min - 1, max);
if (mutation_counter_-- == 0)
return formula::bunop(o, c, 0, max);
}
if (max != formula::unbounded())
{
if (max > min && mutation_counter_-- == 0)
return formula::bunop(o, c, min, max - 1);
if (mutation_counter_-- == 0)
return formula::bunop(o, c, min,
formula::unbounded());
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
}
SPOT_UNREACHABLE();
}
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
{
formula first = f[0];
formula second = f[1];
op o = f.kind();
bool left_is_sere = o == op::EConcat
|| o == op::EConcatMarked
|| o == op::UConcat;
formula
get_mutation(int n)
{
mutation_counter_ = n;
formula mut = mutate(f_);
if (mut == f_)
return nullptr;
return mut;
}
};
bool
formula_length_less_than(formula left, formula right)
{
assert(left != nullptr);
assert(right != nullptr);
if (left == right)
return false;
auto ll = length(left);
auto lr = length(right);
if (ll < lr)
return true;
if (ll > lr)
return false;
return left < right;
}
typedef std::set<formula> fset_t;
void
single_mutation_rec(formula f, fset_t& mutations, unsigned opts,
unsigned& n, unsigned m)
{
if (m == 0)
{
if (mutations.insert(f).second)
--n;
}
else
{
formula mut;
int i = 0;
mutator mv(f, opts);
while (n > 0 && ((mut = mv.get_mutation(i++)) != nullptr))
single_mutation_rec(mut, mutations, opts, n, m - 1);
}
}
void
replace_ap_rec(formula f, fset_t& mutations, unsigned opts,
unsigned& n, unsigned m)
{
if (m == 0)
{
if (mutations.insert(f).second)
--n;
}
else
{
if (!n)
return;
auto aps =
std::unique_ptr<atomic_prop_set>(atomic_prop_collect(f));
for (auto ap1: *aps)
for (auto ap2: *aps)
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
{
if (ap1 == ap2)
continue;
auto mut = substitute_ap(f, ap1, ap2);
replace_ap_rec(mut, mutations, opts, n, m - 1);
if (!n)
return;
if (!left_is_sere)
return first;
else if (o == op::UConcat)
return formula::NegClosure(first);
else // EConcat or EConcatMarked
return formula::Closure(first);
}
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
return second;
if (opts_ & Mut_Rewrite_Ops)
{
switch (o)
{
case op::U:
if (mutation_counter_-- == 0)
return formula::W(first, second);
break;
case op::M:
if (mutation_counter_-- == 0)
return formula::R(first, second);
if (mutation_counter_-- == 0)
return formula::U(second, first);
break;
case op::R:
if (mutation_counter_-- == 0)
return formula::W(second, first);
break;
default:
break;
}
}
if (opts_ & Mut_Split_Ops)
{
switch (o)
{
case op::Equiv:
if (mutation_counter_-- == 0)
return formula::Implies(first, second);
if (mutation_counter_-- == 0)
return formula::Implies(second, first);
if (mutation_counter_-- == 0)
return formula::And({first, second});
if (mutation_counter_-- == 0)
{
// Negate the two argument sequentially (in this
// case right before left, otherwise different
// compilers will make different choices.
auto right = formula::Not(second);
return formula::And({formula::Not(first), right});
}
break;
case op::Xor:
if (mutation_counter_-- == 0)
return formula::And({first, formula::Not(second)});
if (mutation_counter_-- == 0)
return formula::And({formula::Not(first), second});
break;
default:
break;
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
case op::Star:
case op::FStar:
{
formula c = f[0];
op o = f.kind();
if (opts_ & Mut_Remove_Ops && mutation_counter_-- == 0)
return c;
if (opts_ & Mut_Simplify_Bounds)
{
auto min = f.min();
auto max = f.max();
if (min > 0)
{
if (mutation_counter_-- == 0)
return formula::bunop(o, c, min - 1, max);
if (mutation_counter_-- == 0)
return formula::bunop(o, c, 0, max);
}
if (max != formula::unbounded())
{
if (max > min && mutation_counter_-- == 0)
return formula::bunop(o, c, min, max - 1);
if (mutation_counter_-- == 0)
return formula::bunop(o, c, min,
formula::unbounded());
}
}
if (mutation_counter_ < 0)
return f;
else
return f.map(recurse);
}
}
SPOT_UNREACHABLE();
}
formula
get_mutation(int n)
{
mutation_counter_ = n;
formula mut = mutate(f_);
if (mut == f_)
return nullptr;
return mut;
}
};
bool
formula_length_less_than(formula left, formula right)
{
assert(left != nullptr);
assert(right != nullptr);
if (left == right)
return false;
auto ll = length(left);
auto lr = length(right);
if (ll < lr)
return true;
if (ll > lr)
return false;
return left < right;
}
std::vector<formula>
mutate(formula f, unsigned opts, unsigned max_output,
unsigned mutation_count, bool sort)
{
fset_t mutations;
single_mutation_rec(f, mutations, opts, max_output, mutation_count);
if (opts & Mut_Remove_One_Ap)
replace_ap_rec(f, mutations, opts, max_output, mutation_count);
typedef std::set<formula> fset_t;
vec res(mutations.begin(), mutations.end());
if (sort)
std::sort(res.begin(), res.end(), formula_length_less_than);
return res;
void
single_mutation_rec(formula f, fset_t& mutations, unsigned opts,
unsigned& n, unsigned m)
{
if (m == 0)
{
if (mutations.insert(f).second)
--n;
}
else
{
formula mut;
int i = 0;
mutator mv(f, opts);
while (n > 0 && ((mut = mv.get_mutation(i++)) != nullptr))
single_mutation_rec(mut, mutations, opts, n, m - 1);
}
}
void
replace_ap_rec(formula f, fset_t& mutations, unsigned opts,
unsigned& n, unsigned m)
{
if (m == 0)
{
if (mutations.insert(f).second)
--n;
}
else
{
if (!n)
return;
auto aps =
std::unique_ptr<atomic_prop_set>(atomic_prop_collect(f));
for (auto ap1: *aps)
for (auto ap2: *aps)
{
if (ap1 == ap2)
continue;
auto mut = substitute_ap(f, ap1, ap2);
replace_ap_rec(mut, mutations, opts, n, m - 1);
if (!n)
return;
}
}
}
}
std::vector<formula>
mutate(formula f, unsigned opts, unsigned max_output,
unsigned mutation_count, bool sort)
{
fset_t mutations;
single_mutation_rec(f, mutations, opts, max_output, mutation_count);
if (opts & Mut_Remove_One_Ap)
replace_ap_rec(f, mutations, opts, max_output, mutation_count);
vec res(mutations.begin(), mutations.end());
if (sort)
std::sort(res.begin(), res.end(), formula_length_less_than);
return res;
}
}

View file

@ -24,25 +24,22 @@
namespace spot
{
namespace ltl
{
enum mut_opts
{
Mut_Ap2Const = 1U<<0,
Mut_Simplify_Bounds = 1U<<1,
Mut_Remove_Multop_Operands = 1U<<2,
Mut_Remove_Ops = 1U<<3,
Mut_Split_Ops = 1U<<4,
Mut_Rewrite_Ops = 1U<<5,
Mut_Remove_One_Ap = 1U<<6,
Mut_All = -1U
};
enum mut_opts
{
Mut_Ap2Const = 1U<<0,
Mut_Simplify_Bounds = 1U<<1,
Mut_Remove_Multop_Operands = 1U<<2,
Mut_Remove_Ops = 1U<<3,
Mut_Split_Ops = 1U<<4,
Mut_Rewrite_Ops = 1U<<5,
Mut_Remove_One_Ap = 1U<<6,
Mut_All = -1U
};
SPOT_API
std::vector<formula> mutate(formula f,
unsigned opts = Mut_All,
unsigned max_output = -1U,
unsigned mutation_count = 1,
bool sort = true);
}
SPOT_API
std::vector<formula> mutate(formula f,
unsigned opts = Mut_All,
unsigned max_output = -1U,
unsigned mutation_count = 1,
bool sort = true);
}

View file

@ -25,17 +25,13 @@
namespace spot
{
namespace ltl
formula
negative_normal_form(formula f, bool negated)
{
formula
negative_normal_form(formula f, bool negated)
{
if (!negated && f.is_in_nenoform())
return f;
ltl_simplifier s;
return s.negative_normal_form(f, negated);
}
if (!negated && f.is_in_nenoform())
return f;
ltl_simplifier s;
return s.negative_normal_form(f, negated);
}
}

View file

@ -26,24 +26,21 @@
namespace spot
{
namespace ltl
{
/// \ingroup ltl_rewriting
/// \brief Build the negative normal form of \a f.
///
/// All negations of the formula are pushed in front of the
/// atomic propositions.
///
/// \param f The formula to normalize.
/// \param negated If \c true, return the negative normal form of
/// \c !f
///
/// Note that this will not remove abbreviated operators. If you
/// want to remove abbreviations, call spot::ltl::unabbreviate
/// first. (Calling this function after
/// spot::ltl::negative_normal_form would likely produce a formula
/// which is not in negative normal form.)
SPOT_API formula
negative_normal_form(formula f, bool negated = false);
}
/// \ingroup ltl_rewriting
/// \brief Build the negative normal form of \a f.
///
/// All negations of the formula are pushed in front of the
/// atomic propositions.
///
/// \param f The formula to normalize.
/// \param negated If \c true, return the negative normal form of
/// \c !f
///
/// Note that this will not remove abbreviated operators. If you
/// want to remove abbreviations, call spot::unabbreviate
/// first. (Calling this function after
/// spot::negative_normal_form would likely produce a formula
/// which is not in negative normal form.)
SPOT_API formula
negative_normal_form(formula f, bool negated = false);
}

File diff suppressed because it is too large Load diff

View file

@ -27,198 +27,195 @@
namespace spot
{
namespace ltl
{
/// \addtogroup ltl_io
/// @{
/// \addtogroup ltl_io
/// @{
/// \brief Output a PSL formula as a string which is parsable.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_psl(std::ostream& os, formula f, bool full_parent = false);
/// \brief Output a PSL formula as a string which is parsable.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_psl(std::ostream& os, formula f, bool full_parent = false);
/// \brief Convert a PSL formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_psl(formula f, bool full_parent = false);
/// \brief Convert a PSL formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_psl(formula f, bool full_parent = false);
/// \brief Output a PSL formula as an utf-8 string which is parsable.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_utf8_psl(std::ostream& os, formula f,
/// \brief Output a PSL formula as an utf-8 string which is parsable.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_utf8_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert a PSL formula into a utf-8 string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_utf8_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a string which is parsable.
/// \param f The formula to translate.
/// \param os The stream where it should be output.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sere(std::ostream& os, formula f, bool full_parent = false);
/// \brief Convert a SERE formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sere(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a utf-8 string which is parsable.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_utf8_sere(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert a SERE formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_utf8_sere(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string parsable by Spin.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_spin_ltl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert an LTL formula into a string parsable by Spin.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_spin_ltl(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string parsable by Wring.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
SPOT_API std::ostream&
print_wring_ltl(std::ostream& os, formula f);
/// \brief Convert a formula into a string parsable by Wring
/// \param f The formula to translate.
SPOT_API std::string
str_wring_ltl(formula f);
/// \brief Output a PSL formula as a LaTeX string.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_latex_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a formula as a LaTeX string which is parsable.
/// unless the formula contains automaton operators (used in ELTL formulae).
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_latex_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a LaTeX string.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_latex_sere(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert a PSL formula into a utf-8 string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_utf8_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a LaTeX string which is parsable.
/// unless the formula contains automaton operators (used in ELTL formulae).
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_latex_sere(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a string which is parsable.
/// \param f The formula to translate.
/// \param os The stream where it should be output.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sere(std::ostream& os, formula f, bool full_parent = false);
/// \brief Convert a SERE formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sere(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a utf-8 string which is parsable.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_utf8_sere(std::ostream& os, formula f,
/// \brief Output a PSL formula as a self-contained LaTeX string.
///
/// The result cannot be parsed back.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sclatex_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert a SERE formula into a string which is parsable
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_utf8_sere(formula f, bool full_parent = false);
/// \brief Output a PSL formula as a self-contained LaTeX string.
///
/// The result cannot be parsed bacl.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sclatex_psl(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string parsable by Spin.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_spin_ltl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Convert an LTL formula into a string parsable by Spin.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_spin_ltl(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string parsable by Wring.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
SPOT_API std::ostream&
print_wring_ltl(std::ostream& os, formula f);
/// \brief Convert a formula into a string parsable by Wring
/// \param f The formula to translate.
SPOT_API std::string
str_wring_ltl(formula f);
/// \brief Output a PSL formula as a LaTeX string.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_latex_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a formula as a LaTeX string which is parsable.
/// unless the formula contains automaton operators (used in ELTL formulae).
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_latex_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a LaTeX string.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_latex_sere(std::ostream& os, formula f,
/// \brief Output a SERE formula as a self-contained LaTeX string.
///
/// The result cannot be parsed back.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sclatex_sere(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a SERE formula as a LaTeX string which is parsable.
/// unless the formula contains automaton operators (used in ELTL formulae).
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_latex_sere(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a self-contained LaTeX string.
///
/// The result cannot be parsed bacl.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sclatex_sere(formula f, bool full_parent = false);
/// \brief Output a PSL formula as a self-contained LaTeX string.
///
/// The result cannot be parsed back.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sclatex_psl(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output an LTL formula as a string in LBT's format.
///
/// The formula must be an LTL formula (ELTL and PSL operators
/// are not supported). The M and W operator will be output
/// as-is, because this is accepted by LBTT, however if you
/// plan to use the output with other tools, you should probably
/// rewrite these two operators using unabbreviate_wm().
///
/// \param f The formula to translate.
/// \param os The stream where it should be output.
SPOT_API std::ostream&
print_lbt_ltl(std::ostream& os, formula f);
/// \brief Output a PSL formula as a self-contained LaTeX string.
///
/// The result cannot be parsed bacl.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sclatex_psl(formula f, bool full_parent = false);
/// \brief Output a SERE formula as a self-contained LaTeX string.
///
/// The result cannot be parsed back.
/// \param os The stream where it should be output.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::ostream&
print_sclatex_sere(std::ostream& os, formula f,
bool full_parent = false);
/// \brief Output a SERE formula as a self-contained LaTeX string.
///
/// The result cannot be parsed bacl.
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
SPOT_API std::string
str_sclatex_sere(formula f, bool full_parent = false);
/// \brief Output an LTL formula as a string in LBT's format.
///
/// The formula must be an LTL formula (ELTL and PSL operators
/// are not supported). The M and W operator will be output
/// as-is, because this is accepted by LBTT, however if you
/// plan to use the output with other tools, you should probably
/// rewrite these two operators using unabbreviate_wm().
///
/// \param f The formula to translate.
/// \param os The stream where it should be output.
SPOT_API std::ostream&
print_lbt_ltl(std::ostream& os, formula f);
/// \brief Output an LTL formula as a string in LBT's format.
///
/// The formula must be an LTL formula (ELTL and PSL operators
/// are not supported). The M and W operator will be output
/// as-is, because this is accepted by LBTT, however if you
/// plan to use the output with other tools, you should probably
/// rewrite these two operators using unabbreviate_wm().
///
/// \param f The formula to translate.
SPOT_API std::string
str_lbt_ltl(formula f);
/// @}
}
/// \brief Output an LTL formula as a string in LBT's format.
///
/// The formula must be an LTL formula (ELTL and PSL operators
/// are not supported). The M and W operator will be output
/// as-is, because this is accepted by LBTT, however if you
/// plan to use the output with other tools, you should probably
/// rewrite these two operators using unabbreviate_wm().
///
/// \param f The formula to translate.
SPOT_API std::string
str_lbt_ltl(formula f);
/// @}
}

File diff suppressed because it is too large Load diff

View file

@ -38,316 +38,312 @@
namespace spot
{
namespace ltl
/// \ingroup ltl_io
/// \brief Base class for random formula generators
class SPOT_API random_formula
{
/// \ingroup ltl_io
/// \brief Base class for random formula generators
class SPOT_API random_formula
public:
random_formula(unsigned proba_size,
const atomic_prop_set* ap):
proba_size_(proba_size), proba_(new op_proba[proba_size_]), ap_(ap)
{
public:
random_formula(unsigned proba_size,
const atomic_prop_set* ap):
proba_size_(proba_size), proba_(new op_proba[proba_size_]), ap_(ap)
{
}
}
virtual ~random_formula()
{
delete[] proba_;
}
virtual ~random_formula()
{
delete[] proba_;
}
/// Return the set of atomic proposition used to build formulae.
const atomic_prop_set*
/// Return the set of atomic proposition used to build formulae.
const atomic_prop_set*
ap() const
{
return ap_;
}
/// \brief Generate a formula of size \a n.
///
/// It is possible to obtain formulae that are smaller than \a
/// n, because some simple simplifications are performed by the
/// AST. (For instance the formula <code>a | a</code> is
/// automatically reduced to <code>a</code> by spot::ltl::multop.)
formula generate(int n) const;
/// \brief Print the priorities of each operator, constants,
/// and atomic propositions.
std::ostream& dump_priorities(std::ostream& os) const;
/// \brief Update the priorities used to generate the formulae.
///
/// \a options should be comma-separated list of KEY=VALUE
/// assignments, using keys from the above list.
/// For instance <code>"xor=0, F=3"</code> will prevent \c xor
/// from being used, and will raise the relative probability of
/// occurrences of the \c F operator.
const char* parse_options(char* options);
protected:
void update_sums();
struct op_proba
{
const char* name;
int min_n;
double proba;
typedef formula (*builder)(const random_formula* rl, int n);
builder build;
void setup(const char* name, int min_n, builder build);
};
unsigned proba_size_;
op_proba* proba_;
double total_1_;
op_proba* proba_2_;
double total_2_;
op_proba* proba_2_or_more_;
double total_2_and_more_;
const atomic_prop_set* ap_;
};
/// \ingroup ltl_io
/// \brief Generate random LTL formulae.
///
/// This class recursively constructs LTL formulae of a given
/// size. The formulae will use the use atomic propositions from
/// the set of propositions passed to the constructor, in addition
/// to the constant and all LTL operators supported by Spot.
///
/// By default each operator has equal chance to be selected.
/// Also, each atomic proposition has as much chance as each
/// constant (i.e., true and false) to be picked. This can be
/// tuned using parse_options().
class SPOT_API random_ltl: public random_formula
{
public:
/// Create a random LTL generator using atomic propositions from \a ap.
///
/// The default priorities are defined as follows:
///
/** \verbatim
ap n
false 1
true 1
not 1
F 1
G 1
X 1
equiv 1
implies 1
xor 1
R 1
U 1
W 1
M 1
and 1
or 1
\endverbatim */
///
/// Where \c n is the number of atomic propositions in the
/// set passed to the constructor.
///
/// This means that each operator has equal chance to be
/// selected. Also, each atomic proposition has as much chance
/// as each constant (i.e., true and false) to be picked.
///
/// These priorities can be changed use the parse_options method.
random_ltl(const atomic_prop_set* ap);
return ap_;
}
protected:
void setup_proba_();
random_ltl(int size, const atomic_prop_set* ap);
};
/// \brief Generate a formula of size \a n.
///
/// It is possible to obtain formulae that are smaller than \a
/// n, because some simple simplifications are performed by the
/// AST. (For instance the formula <code>a | a</code> is
/// automatically reduced to <code>a</code> by spot::multop.)
formula generate(int n) const;
/// \ingroup ltl_io
/// \brief Generate random Boolean formulae.
/// \brief Print the priorities of each operator, constants,
/// and atomic propositions.
std::ostream& dump_priorities(std::ostream& os) const;
/// \brief Update the priorities used to generate the formulae.
///
/// This class recursively constructs Boolean formulae of a given size.
/// The formulae will use the use atomic propositions from the
/// set of propositions passed to the constructor, in addition to the
/// constant and all Boolean operators supported by Spot.
///
/// By default each operator has equal chance to be selected.
class SPOT_API random_boolean: public random_formula
/// \a options should be comma-separated list of KEY=VALUE
/// assignments, using keys from the above list.
/// For instance <code>"xor=0, F=3"</code> will prevent \c xor
/// from being used, and will raise the relative probability of
/// occurrences of the \c F operator.
const char* parse_options(char* options);
protected:
void update_sums();
struct op_proba
{
public:
/// Create a random Boolean formula generator using atomic
/// propositions from \a ap.
///
/// The default priorities are defined as follows:
///
/** \verbatim
ap n
false 1
true 1
not 1
equiv 1
implies 1
xor 1
and 1
or 1
\endverbatim */
///
/// Where \c n is the number of atomic propositions in the
/// set passed to the constructor.
///
/// This means that each operator has equal chance to be
/// selected. Also, each atomic proposition has as much chance
/// as each constant (i.e., true and false) to be picked.
///
/// These priorities can be changed use the parse_options method.
random_boolean(const atomic_prop_set* ap);
const char* name;
int min_n;
double proba;
typedef formula (*builder)(const random_formula* rl, int n);
builder build;
void setup(const char* name, int min_n, builder build);
};
unsigned proba_size_;
op_proba* proba_;
double total_1_;
op_proba* proba_2_;
double total_2_;
op_proba* proba_2_or_more_;
double total_2_and_more_;
const atomic_prop_set* ap_;
};
/// \ingroup ltl_io
/// \brief Generate random SERE.
/// \ingroup ltl_io
/// \brief Generate random LTL formulae.
///
/// This class recursively constructs LTL formulae of a given
/// size. The formulae will use the use atomic propositions from
/// the set of propositions passed to the constructor, in addition
/// to the constant and all LTL operators supported by Spot.
///
/// By default each operator has equal chance to be selected.
/// Also, each atomic proposition has as much chance as each
/// constant (i.e., true and false) to be picked. This can be
/// tuned using parse_options().
class SPOT_API random_ltl: public random_formula
{
public:
/// Create a random LTL generator using atomic propositions from \a ap.
///
/// This class recursively constructs SERE of a given size.
/// The formulae will use the use atomic propositions from the
/// set of propositions passed to the constructor, in addition to the
/// constant and all SERE operators supported by Spot.
/// The default priorities are defined as follows:
///
/// By default each operator has equal chance to be selected.
class SPOT_API random_sere: public random_formula
{
public:
/// Create a random SERE genere using atomic propositions from \a ap.
///
/// The default priorities are defined as follows:
///
/** \verbatim
eword 1
boolform 1
star 1
star_b 1
equal_b 1
goto_b 1
and 1
andNLM 1
or 1
concat 1
fusion 1
\endverbatim */
///
/// Where "boolfrom" designates a Boolean formula generated
/// by random_boolean.
///
/// These priorities can be changed use the parse_options method.
///
/// In addition, you can set the properties of the Boolean
/// formula generator used to build Boolean subformulae using
/// the parse_options method of the \c rb attribute.
random_sere(const atomic_prop_set* ap);
random_boolean rb;
};
/// \ingroup ltl_io
/// \brief Generate random PSL formulae.
/** \verbatim
ap n
false 1
true 1
not 1
F 1
G 1
X 1
equiv 1
implies 1
xor 1
R 1
U 1
W 1
M 1
and 1
or 1
\endverbatim */
///
/// This class recursively constructs PSL formulae of a given size.
/// The formulae will use the use atomic propositions from the
/// set of propositions passed to the constructor, in addition to the
/// constant and all PSL operators supported by Spot.
class SPOT_API random_psl: public random_ltl
{
public:
/// Create a random PSL generator using atomic propositions from \a ap.
///
/// PSL formulae are built by combining LTL operators, plus
/// three operators (EConcat, UConcat, Closure) taking a SERE
/// as parameter.
///
/// The default priorities are defined as follows:
///
/** \verbatim
ap n
false 1
true 1
not 1
F 1
G 1
X 1
Closure 1
equiv 1
implies 1
xor 1
R 1
U 1
W 1
M 1
and 1
or 1
EConcat 1
UConcat 1
\endverbatim */
///
/// Where \c n is the number of atomic propositions in the
/// set passed to the constructor.
///
/// This means that each operator has equal chance to be
/// selected. Also, each atomic proposition has as much chance
/// as each constant (i.e., true and false) to be picked.
///
/// These priorities can be changed use the parse_options method.
///
/// In addition, you can set the properties of the SERE generator
/// used to build SERE subformulae using the parse_options method
/// of the \c rs attribute.
random_psl(const atomic_prop_set* ap);
/// Where \c n is the number of atomic propositions in the
/// set passed to the constructor.
///
/// This means that each operator has equal chance to be
/// selected. Also, each atomic proposition has as much chance
/// as each constant (i.e., true and false) to be picked.
///
/// These priorities can be changed use the parse_options method.
random_ltl(const atomic_prop_set* ap);
/// The SERE generator used to generate SERE subformulae.
random_sere rs;
};
protected:
void setup_proba_();
random_ltl(int size, const atomic_prop_set* ap);
};
class SPOT_API randltlgenerator
{
typedef std::unordered_set<formula> fset_t;
/// \ingroup ltl_io
/// \brief Generate random Boolean formulae.
///
/// This class recursively constructs Boolean formulae of a given size.
/// The formulae will use the use atomic propositions from the
/// set of propositions passed to the constructor, in addition to the
/// constant and all Boolean operators supported by Spot.
///
/// By default each operator has equal chance to be selected.
class SPOT_API random_boolean: public random_formula
{
public:
/// Create a random Boolean formula generator using atomic
/// propositions from \a ap.
///
/// The default priorities are defined as follows:
///
/** \verbatim
ap n
false 1
true 1
not 1
equiv 1
implies 1
xor 1
and 1
or 1
\endverbatim */
///
/// Where \c n is the number of atomic propositions in the
/// set passed to the constructor.
///
/// This means that each operator has equal chance to be
/// selected. Also, each atomic proposition has as much chance
/// as each constant (i.e., true and false) to be picked.
///
/// These priorities can be changed use the parse_options method.
random_boolean(const atomic_prop_set* ap);
};
/// \ingroup ltl_io
/// \brief Generate random SERE.
///
/// This class recursively constructs SERE of a given size.
/// The formulae will use the use atomic propositions from the
/// set of propositions passed to the constructor, in addition to the
/// constant and all SERE operators supported by Spot.
///
/// By default each operator has equal chance to be selected.
class SPOT_API random_sere: public random_formula
{
public:
/// Create a random SERE genere using atomic propositions from \a ap.
///
/// The default priorities are defined as follows:
///
/** \verbatim
eword 1
boolform 1
star 1
star_b 1
equal_b 1
goto_b 1
and 1
andNLM 1
or 1
concat 1
fusion 1
\endverbatim */
///
/// Where "boolfrom" designates a Boolean formula generated
/// by random_boolean.
///
/// These priorities can be changed use the parse_options method.
///
/// In addition, you can set the properties of the Boolean
/// formula generator used to build Boolean subformulae using
/// the parse_options method of the \c rb attribute.
random_sere(const atomic_prop_set* ap);
random_boolean rb;
};
/// \ingroup ltl_io
/// \brief Generate random PSL formulae.
///
/// This class recursively constructs PSL formulae of a given size.
/// The formulae will use the use atomic propositions from the
/// set of propositions passed to the constructor, in addition to the
/// constant and all PSL operators supported by Spot.
class SPOT_API random_psl: public random_ltl
{
public:
/// Create a random PSL generator using atomic propositions from \a ap.
///
/// PSL formulae are built by combining LTL operators, plus
/// three operators (EConcat, UConcat, Closure) taking a SERE
/// as parameter.
///
/// The default priorities are defined as follows:
///
/** \verbatim
ap n
false 1
true 1
not 1
F 1
G 1
X 1
Closure 1
equiv 1
implies 1
xor 1
R 1
U 1
W 1
M 1
and 1
or 1
EConcat 1
UConcat 1
\endverbatim */
///
/// Where \c n is the number of atomic propositions in the
/// set passed to the constructor.
///
/// This means that each operator has equal chance to be
/// selected. Also, each atomic proposition has as much chance
/// as each constant (i.e., true and false) to be picked.
///
/// These priorities can be changed use the parse_options method.
///
/// In addition, you can set the properties of the SERE generator
/// used to build SERE subformulae using the parse_options method
/// of the \c rs attribute.
random_psl(const atomic_prop_set* ap);
/// The SERE generator used to generate SERE subformulae.
random_sere rs;
};
class SPOT_API randltlgenerator
{
typedef std::unordered_set<formula> fset_t;
public:
randltlgenerator(int aprops_n, const option_map& opts,
char* opt_pL = nullptr,
char* opt_pS = nullptr,
char* opt_pB = nullptr);
public:
randltlgenerator(int aprops_n, const option_map& opts,
char* opt_pL = nullptr,
char* opt_pS = nullptr,
char* opt_pB = nullptr);
randltlgenerator(atomic_prop_set aprops, const option_map& opts,
char* opt_pL = nullptr,
char* opt_pS = nullptr,
char* opt_pB = nullptr);
randltlgenerator(atomic_prop_set aprops, const option_map& opts,
char* opt_pL = nullptr,
char* opt_pS = nullptr,
char* opt_pB = nullptr);
~randltlgenerator();
~randltlgenerator();
formula next();
formula next();
void dump_ltl_priorities(std::ostream& os);
void dump_bool_priorities(std::ostream& os);
void dump_psl_priorities(std::ostream& os);
void dump_sere_priorities(std::ostream& os);
void dump_sere_bool_priorities(std::ostream& os);
void remove_some_props(atomic_prop_set& s);
void dump_ltl_priorities(std::ostream& os);
void dump_bool_priorities(std::ostream& os);
void dump_psl_priorities(std::ostream& os);
void dump_sere_priorities(std::ostream& os);
void dump_sere_bool_priorities(std::ostream& os);
void remove_some_props(atomic_prop_set& s);
formula GF_n();
formula GF_n();
private:
fset_t unique_set_;
atomic_prop_set aprops_;
private:
fset_t unique_set_;
atomic_prop_set aprops_;
int opt_seed_;
int opt_tree_size_min_;
int opt_tree_size_max_;
bool opt_unique_;
bool opt_wf_;
int opt_simpl_level_;
ltl_simplifier simpl_;
int opt_seed_;
int opt_tree_size_min_;
int opt_tree_size_max_;
bool opt_unique_;
bool opt_wf_;
int opt_simpl_level_;
ltl_simplifier simpl_;
int output_;
int output_;
random_formula* rf_ = nullptr;
random_psl* rp_ = nullptr;
random_sere* rs_ = nullptr;
};
}
random_formula* rf_ = nullptr;
random_psl* rp_ = nullptr;
random_sere* rs_ = nullptr;
};
}

View file

@ -27,461 +27,457 @@
namespace spot
{
namespace ltl
//////////////////////////////////////////////////////////////////////
// Basic relabeler
//////////////////////////////////////////////////////////////////////
namespace
{
//////////////////////////////////////////////////////////////////////
// Basic relabeler
//////////////////////////////////////////////////////////////////////
namespace
struct ap_generator
{
struct ap_generator
{
virtual formula next() = 0;
virtual ~ap_generator() {}
};
virtual formula next() = 0;
virtual ~ap_generator() {}
};
struct pnn_generator final: ap_generator
{
unsigned nn;
pnn_generator()
: nn(0)
{
}
formula next()
{
std::ostringstream s;
s << 'p' << nn++;
return formula::ap(s.str());
}
};
struct abc_generator final: ap_generator
{
public:
abc_generator()
: nn(0)
{
}
unsigned nn;
formula next()
{
std::string s;
unsigned n = nn++;
do
{
s.push_back('a' + (n % 26));
n /= 26;
}
while (n);
return formula::ap(s);
}
};
class relabeler
{
public:
typedef std::unordered_map<formula, formula> map;
map newname;
ap_generator* gen;
relabeling_map* oldnames;
relabeler(ap_generator* gen, relabeling_map* m)
: gen(gen), oldnames(m)
{
}
~relabeler()
{
delete gen;
}
formula rename(formula old)
{
auto r = newname.emplace(old, nullptr);
if (!r.second)
{
return r.first->second;
}
else
{
formula res = gen->next();
r.first->second = res;
if (oldnames)
(*oldnames)[res] = old;
return res;
}
}
formula
visit(formula f)
{
if (f.is(op::ap))
return rename(f);
else
return f.map([this](formula f)
{
return this->visit(f);
});
}
};
}
formula
relabel(formula f, relabeling_style style, relabeling_map* m)
struct pnn_generator final: ap_generator
{
ap_generator* gen = nullptr;
switch (style)
unsigned nn;
pnn_generator()
: nn(0)
{
case Pnn:
gen = new pnn_generator;
break;
case Abc:
gen = new abc_generator;
break;
}
relabeler r(gen, m);
return r.visit(f);
}
//////////////////////////////////////////////////////////////////////
// Boolean-subexpression relabeler
//////////////////////////////////////////////////////////////////////
// Here we want to rewrite a formula such as
// "a & b & X(c & d) & GF(c & d)" into "p0 & Xp1 & GFp1"
// where Boolean subexpressions are replaced by fresh propositions.
//
// Detecting Boolean subexpressions is not a problem.
// Furthermore, because we are already representing LTL formulas
// with sharing of identical sub-expressions we can easily rename
// a subexpression (such as c&d above) only once. However this
// scheme has two problems:
//
// 1. It will not detect inter-dependent Boolean subexpressions.
// For instance it will mistakenly relabel "(a & b) U (a & !b)"
// as "p0 U p1", hiding the dependency between a&b and a&!b.
//
// 2. Because of our n-ary operators, it will fail to
// notice that (a & b) is a sub-expression of (a & b & c).
//
// The code below only addresses point 1 so that interdependent
// subexpressions are not relabeled. Point 2 could be improved in
// a future version of somebody feels inclined to do so.
//
// The way we compute the subexpressions that can be relabeled is
// by transforming the formula syntax tree into an undirected
// graph, and computing the cut points of this graph. The cut
// points (or articulation points) are the nodes whose removal
// would split the graph in two components. To ensure that a
// Boolean operator is only considered as a cut point if it would
// separate all of its children from the rest of the graph, we
// connect all the children of Boolean operators.
//
// For instance (a & b) U (c & d) has two (Boolean) cut points
// corresponding to the two AND operators:
//
// (a&b)U(c&d)
//
// a&b c&d
//
// a─────b c─────d
//
// (The root node is also a cut-point, but we only consider Boolean
// cut-points for relabeling.)
//
// On the other hand, (a & b) U (b & !c) has only one Boolean
// cut-point which corresponds to the NOT operator:
//
// (a&b)U(b&!c)
//
// a&b b&c
//
// a─────b────!c
// │
// c
//
// Note that if the children of a&b and b&c were not connected,
// a&b and b&c would be considered as cut points because they
// separate "a" or "!c" from the rest of the graph.
//
// The relabeling of a formula is therefore done in 3 passes:
// 1. convert the formula's syntax tree into an undirected graph,
// adding links between children of Boolean operators
// 2. compute the (Boolean) cut points of that graph, using the
// Hopcroft-Tarjan algorithm (see below for a reference)
// 3. recursively scan the formula's tree until we reach
// either a (Boolean) cut point or an atomic proposition, and
// replace that node by a fresh atomic proposition.
//
// In the example above (a&b)U(b&!c), the last recursion
// stop a, b, and !c, producing (p0&p1)U(p1&p2).
namespace
{
typedef std::vector<formula> succ_vec;
typedef std::map<formula, succ_vec> fgraph;
// Convert the formula's syntax tree into an undirected graph
// labeled by subformulas.
class formula_to_fgraph final
formula next()
{
public:
fgraph& g;
std::stack<formula> s;
std::ostringstream s;
s << 'p' << nn++;
return formula::ap(s.str());
}
};
formula_to_fgraph(fgraph& g):
g(g)
struct abc_generator final: ap_generator
{
public:
abc_generator()
: nn(0)
{
}
~formula_to_fgraph()
{
}
unsigned nn;
void
visit(formula f)
{
formula next()
{
std::string s;
unsigned n = nn++;
do
{
// Connect to parent
auto in = g.emplace(f, succ_vec());
if (!s.empty())
{
formula top = s.top();
in.first->second.push_back(top);
g[top].push_back(f);
if (!in.second)
return;
}
else
{
assert(in.second);
}
s.push_back('a' + (n % 26));
n /= 26;
}
s.push(f);
while (n);
return formula::ap(s);
}
};
unsigned sz = f.size();
unsigned i = 0;
if (sz > 2 && !f.is_boolean())
class relabeler
{
public:
typedef std::unordered_map<formula, formula> map;
map newname;
ap_generator* gen;
relabeling_map* oldnames;
relabeler(ap_generator* gen, relabeling_map* m)
: gen(gen), oldnames(m)
{
}
~relabeler()
{
delete gen;
}
formula rename(formula old)
{
auto r = newname.emplace(old, nullptr);
if (!r.second)
{
return r.first->second;
}
else
{
formula res = gen->next();
r.first->second = res;
if (oldnames)
(*oldnames)[res] = old;
return res;
}
}
formula
visit(formula f)
{
if (f.is(op::ap))
return rename(f);
else
return f.map([this](formula f)
{
return this->visit(f);
});
}
};
}
formula
relabel(formula f, relabeling_style style, relabeling_map* m)
{
ap_generator* gen = nullptr;
switch (style)
{
case Pnn:
gen = new pnn_generator;
break;
case Abc:
gen = new abc_generator;
break;
}
relabeler r(gen, m);
return r.visit(f);
}
//////////////////////////////////////////////////////////////////////
// Boolean-subexpression relabeler
//////////////////////////////////////////////////////////////////////
// Here we want to rewrite a formula such as
// "a & b & X(c & d) & GF(c & d)" into "p0 & Xp1 & GFp1"
// where Boolean subexpressions are replaced by fresh propositions.
//
// Detecting Boolean subexpressions is not a problem.
// Furthermore, because we are already representing LTL formulas
// with sharing of identical sub-expressions we can easily rename
// a subexpression (such as c&d above) only once. However this
// scheme has two problems:
//
// 1. It will not detect inter-dependent Boolean subexpressions.
// For instance it will mistakenly relabel "(a & b) U (a & !b)"
// as "p0 U p1", hiding the dependency between a&b and a&!b.
//
// 2. Because of our n-ary operators, it will fail to
// notice that (a & b) is a sub-expression of (a & b & c).
//
// The code below only addresses point 1 so that interdependent
// subexpressions are not relabeled. Point 2 could be improved in
// a future version of somebody feels inclined to do so.
//
// The way we compute the subexpressions that can be relabeled is
// by transforming the formula syntax tree into an undirected
// graph, and computing the cut points of this graph. The cut
// points (or articulation points) are the nodes whose removal
// would split the graph in two components. To ensure that a
// Boolean operator is only considered as a cut point if it would
// separate all of its children from the rest of the graph, we
// connect all the children of Boolean operators.
//
// For instance (a & b) U (c & d) has two (Boolean) cut points
// corresponding to the two AND operators:
//
// (a&b)U(c&d)
//
// a&b c&d
//
// a─────b c─────d
//
// (The root node is also a cut-point, but we only consider Boolean
// cut-points for relabeling.)
//
// On the other hand, (a & b) U (b & !c) has only one Boolean
// cut-point which corresponds to the NOT operator:
//
// (a&b)U(b&!c)
//
// a&b b&c
//
// a─────b────!c
// │
// c
//
// Note that if the children of a&b and b&c were not connected,
// a&b and b&c would be considered as cut points because they
// separate "a" or "!c" from the rest of the graph.
//
// The relabeling of a formula is therefore done in 3 passes:
// 1. convert the formula's syntax tree into an undirected graph,
// adding links between children of Boolean operators
// 2. compute the (Boolean) cut points of that graph, using the
// Hopcroft-Tarjan algorithm (see below for a reference)
// 3. recursively scan the formula's tree until we reach
// either a (Boolean) cut point or an atomic proposition, and
// replace that node by a fresh atomic proposition.
//
// In the example above (a&b)U(b&!c), the last recursion
// stop a, b, and !c, producing (p0&p1)U(p1&p2).
namespace
{
typedef std::vector<formula> succ_vec;
typedef std::map<formula, succ_vec> fgraph;
// Convert the formula's syntax tree into an undirected graph
// labeled by subformulas.
class formula_to_fgraph final
{
public:
fgraph& g;
std::stack<formula> s;
formula_to_fgraph(fgraph& g):
g(g)
{
}
~formula_to_fgraph()
{
}
void
visit(formula f)
{
{
// Connect to parent
auto in = g.emplace(f, succ_vec());
if (!s.empty())
{
/// If we have a formula like (a & b & Xc), consider
/// it as ((a & b) & Xc) in the graph to isolate the
/// Boolean operands as a single node.
formula b = f.boolean_operands(&i);
if (b)
visit(b);
formula top = s.top();
in.first->second.push_back(top);
g[top].push_back(f);
if (!in.second)
return;
}
for (; i < sz; ++i)
visit(f[i]);
if (sz > 1 && f.is_boolean())
else
{
// For Boolean nodes, connect all children in a
// loop. This way the node can only be a cut-point
// if it separates all children from the reset of
// the graph (not only one).
formula pred = f[0];
for (i = 1; i < sz; ++i)
assert(in.second);
}
}
s.push(f);
unsigned sz = f.size();
unsigned i = 0;
if (sz > 2 && !f.is_boolean())
{
/// If we have a formula like (a & b & Xc), consider
/// it as ((a & b) & Xc) in the graph to isolate the
/// Boolean operands as a single node.
formula b = f.boolean_operands(&i);
if (b)
visit(b);
}
for (; i < sz; ++i)
visit(f[i]);
if (sz > 1 && f.is_boolean())
{
// For Boolean nodes, connect all children in a
// loop. This way the node can only be a cut-point
// if it separates all children from the reset of
// the graph (not only one).
formula pred = f[0];
for (i = 1; i < sz; ++i)
{
formula next = f[i];
// Note that we only add an edge in one
// direction, because we are building a cycle
// between all children anyway.
g[pred].push_back(next);
pred = next;
}
g[pred].push_back(f[0]);
}
s.pop();
}
};
typedef std::set<formula> fset;
struct data_entry // for each node of the graph
{
unsigned num; // serial number, in pre-order
unsigned low; // lowest number accessible via unstacked descendants
data_entry(unsigned num = 0, unsigned low = 0)
: num(num), low(low)
{
}
};
typedef std::unordered_map<formula, data_entry> fmap_t;
struct stack_entry
{
formula grand_parent;
formula parent; // current node
succ_vec::const_iterator current_child;
succ_vec::const_iterator last_child;
};
typedef std::stack<stack_entry> stack_t;
// Fill c with the Boolean cutpoints of g, starting from start.
//
// This is based no "Efficient Algorithms for Graph
// Manipulation", J. Hopcroft & R. Tarjan, in Communications of
// the ACM, 16 (6), June 1973.
//
// It differs from the original algorithm by returning only the
// Boolean cutpoints, and not dealing with the initial state
// properly (our initial state will always be considered as a
// cut-point, but since we only return Boolean cut-points it's
// OK: if the top-most formula is Boolean we want to replace it
// as a whole).
void cut_points(const fgraph& g, fset& c, formula start)
{
stack_t s;
unsigned num = 0;
fmap_t data;
data_entry d = { num, num };
data[start] = d;
++num;
const succ_vec& children = g.find(start)->second;
stack_entry e = { start, start, children.begin(), children.end() };
s.push(e);
while (!s.empty())
{
stack_entry& e = s.top();
if (e.current_child != e.last_child)
{
// Skip the edge if it is just the reverse of the one
// we took.
formula child = *e.current_child;
if (child == e.grand_parent)
{
formula next = f[i];
// Note that we only add an edge in one
// direction, because we are building a cycle
// between all children anyway.
g[pred].push_back(next);
pred = next;
++e.current_child;
continue;
}
g[pred].push_back(f[0]);
}
s.pop();
}
};
typedef std::set<formula> fset;
struct data_entry // for each node of the graph
{
unsigned num; // serial number, in pre-order
unsigned low; // lowest number accessible via unstacked descendants
data_entry(unsigned num = 0, unsigned low = 0)
: num(num), low(low)
{
}
};
typedef std::unordered_map<formula, data_entry> fmap_t;
struct stack_entry
{
formula grand_parent;
formula parent; // current node
succ_vec::const_iterator current_child;
succ_vec::const_iterator last_child;
};
typedef std::stack<stack_entry> stack_t;
// Fill c with the Boolean cutpoints of g, starting from start.
//
// This is based no "Efficient Algorithms for Graph
// Manipulation", J. Hopcroft & R. Tarjan, in Communications of
// the ACM, 16 (6), June 1973.
//
// It differs from the original algorithm by returning only the
// Boolean cutpoints, and not dealing with the initial state
// properly (our initial state will always be considered as a
// cut-point, but since we only return Boolean cut-points it's
// OK: if the top-most formula is Boolean we want to replace it
// as a whole).
void cut_points(const fgraph& g, fset& c, formula start)
{
stack_t s;
unsigned num = 0;
fmap_t data;
data_entry d = { num, num };
data[start] = d;
++num;
const succ_vec& children = g.find(start)->second;
stack_entry e = { start, start, children.begin(), children.end() };
s.push(e);
while (!s.empty())
{
stack_entry& e = s.top();
if (e.current_child != e.last_child)
{
// Skip the edge if it is just the reverse of the one
// we took.
formula child = *e.current_child;
if (child == e.grand_parent)
{
++e.current_child;
continue;
}
auto i = data.emplace(std::piecewise_construct,
std::forward_as_tuple(child),
std::forward_as_tuple(num, num));
if (i.second) // New destination.
{
++num;
const succ_vec& children = g.find(child)->second;
stack_entry newe = { e.parent, child,
children.begin(), children.end() };
s.push(newe);
}
else // Destination exists.
{
data_entry& dparent = data[e.parent];
data_entry& dchild = i.first->second;
// If this is a back-edge, update
// the low field of the parent.
if (dchild.num <= dparent.num)
if (dparent.low > dchild.num)
dparent.low = dchild.num;
}
++e.current_child;
}
else
{
formula grand_parent = e.grand_parent;
formula parent = e.parent;
s.pop();
if (!s.empty())
{
data_entry& dparent = data[parent];
data_entry& dgrand_parent = data[grand_parent];
if (dparent.low >= dgrand_parent.num // cut-point
&& grand_parent.is_boolean())
c.insert(grand_parent);
if (dparent.low < dgrand_parent.low)
dgrand_parent.low = dparent.low;
}
}
}
}
class bse_relabeler final: public relabeler
{
public:
fset& c;
bse_relabeler(ap_generator* gen, fset& c,
relabeling_map* m)
: relabeler(gen, m), c(c)
{
}
using relabeler::visit;
formula
visit(formula f)
{
if (f.is(op::ap) || (c.find(f) != c.end()))
return rename(f);
unsigned sz = f.size();
if (sz <= 2)
return f.map([this](formula f)
{
return visit(f);
});
unsigned i = 0;
std::vector<formula> res;
/// If we have a formula like (a & b & Xc), consider
/// it as ((a & b) & Xc) in the graph to isolate the
/// Boolean operands as a single node.
formula b = f.boolean_operands(&i);
if (b)
{
res.reserve(sz - i + 1);
res.push_back(visit(b));
auto i = data.emplace(std::piecewise_construct,
std::forward_as_tuple(child),
std::forward_as_tuple(num, num));
if (i.second) // New destination.
{
++num;
const succ_vec& children = g.find(child)->second;
stack_entry newe = { e.parent, child,
children.begin(), children.end() };
s.push(newe);
}
else // Destination exists.
{
data_entry& dparent = data[e.parent];
data_entry& dchild = i.first->second;
// If this is a back-edge, update
// the low field of the parent.
if (dchild.num <= dparent.num)
if (dparent.low > dchild.num)
dparent.low = dchild.num;
}
++e.current_child;
}
else
{
res.reserve(sz);
formula grand_parent = e.grand_parent;
formula parent = e.parent;
s.pop();
if (!s.empty())
{
data_entry& dparent = data[parent];
data_entry& dgrand_parent = data[grand_parent];
if (dparent.low >= dgrand_parent.num // cut-point
&& grand_parent.is_boolean())
c.insert(grand_parent);
if (dparent.low < dgrand_parent.low)
dgrand_parent.low = dparent.low;
}
}
for (; i < sz; ++i)
res.push_back(visit(f[i]));
return formula::multop(f.kind(), res);
}
};
}
formula
relabel_bse(formula f, relabeling_style style, relabeling_map* m)
class bse_relabeler final: public relabeler
{
fgraph g;
// Build the graph g from the formula f.
public:
fset& c;
bse_relabeler(ap_generator* gen, fset& c,
relabeling_map* m)
: relabeler(gen, m), c(c)
{
formula_to_fgraph conv(g);
conv.visit(f);
}
// Compute its cut-points
fset c;
cut_points(g, c, f);
using relabeler::visit;
// Relabel the formula recursively, stopping
// at cut-points or atomic propositions.
ap_generator* gen = nullptr;
switch (style)
{
case Pnn:
gen = new pnn_generator;
break;
case Abc:
gen = new abc_generator;
break;
}
bse_relabeler rel(gen, c, m);
return rel.visit(f);
formula
visit(formula f)
{
if (f.is(op::ap) || (c.find(f) != c.end()))
return rename(f);
unsigned sz = f.size();
if (sz <= 2)
return f.map([this](formula f)
{
return visit(f);
});
unsigned i = 0;
std::vector<formula> res;
/// If we have a formula like (a & b & Xc), consider
/// it as ((a & b) & Xc) in the graph to isolate the
/// Boolean operands as a single node.
formula b = f.boolean_operands(&i);
if (b)
{
res.reserve(sz - i + 1);
res.push_back(visit(b));
}
else
{
res.reserve(sz);
}
for (; i < sz; ++i)
res.push_back(visit(f[i]));
return formula::multop(f.kind(), res);
}
};
}
formula
relabel_bse(formula f, relabeling_style style, relabeling_map* m)
{
fgraph g;
// Build the graph g from the formula f.
{
formula_to_fgraph conv(g);
conv.visit(f);
}
// Compute its cut-points
fset c;
cut_points(g, c, f);
// Relabel the formula recursively, stopping
// at cut-points or atomic propositions.
ap_generator* gen = nullptr;
switch (style)
{
case Pnn:
gen = new pnn_generator;
break;
case Abc:
gen = new abc_generator;
break;
}
bse_relabeler rel(gen, c, m);
return rel.visit(f);
}
}

View file

@ -25,30 +25,27 @@
namespace spot
{
namespace ltl
{
enum relabeling_style { Abc, Pnn };
enum relabeling_style { Abc, Pnn };
typedef std::map<formula, formula> relabeling_map;
typedef std::map<formula, formula> relabeling_map;
/// \ingroup ltl_rewriting
/// \brief Relabel the atomic propositions in a formula.
///
/// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values).
SPOT_API
formula relabel(formula f, relabeling_style style,
relabeling_map* m = nullptr);
/// \ingroup ltl_rewriting
/// \brief Relabel the atomic propositions in a formula.
///
/// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values).
SPOT_API
formula relabel(formula f, relabeling_style style,
relabeling_map* m = nullptr);
/// \ingroup ltl_rewriting
/// \brief Relabel Boolean subexpressions in a formula using
/// atomic propositions.
///
/// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values).
SPOT_API
formula relabel_bse(formula f, relabeling_style style,
relabeling_map* m = nullptr);
}
/// \ingroup ltl_rewriting
/// \brief Relabel Boolean subexpressions in a formula using
/// atomic propositions.
///
/// If \a m is non-null, it is filled with correspondence
/// between the new names (keys) and the old names (values).
SPOT_API
formula relabel_bse(formula f, relabeling_style style,
relabeling_map* m = nullptr);
}

View file

@ -23,80 +23,77 @@
namespace spot
{
namespace ltl
namespace
{
namespace
{
static formula
remove_x_rec(formula f, atomic_prop_set& aps)
{
if (f.is_syntactic_stutter_invariant())
return f;
auto rec = [&aps](formula f)
{
return remove_x_rec(f, aps);
};
if (!f.is(op::X))
return f.map(rec);
formula c = rec(f[0]);
std::vector<formula> vo;
for (auto i: aps)
{
// First line
std::vector<formula> va1;
formula npi = formula::Not(i);
va1.push_back(i);
va1.push_back(formula::U(i, formula::And({npi, c})));
for (auto j: aps)
if (j != i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::U(formula::Not(j), npi);
va1.push_back(formula::Or({formula::U(j, npi), tmp}));
}
vo.push_back(formula::And(va1));
// Second line
std::vector<formula> va2;
va2.push_back(npi);
va2.push_back(formula::U(npi, formula::And({i, c})));
for (auto j: aps)
if (j != i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::U(formula::Not(j), i);
va2.push_back(formula::Or({formula::U(j, i), tmp}));
}
vo.push_back(formula::And(va2));
}
// Third line
std::vector<formula> va3;
for (auto i: aps)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::G(formula::Not(i));
va3.push_back(formula::Or({formula::G(i), tmp}));
}
va3.push_back(c);
vo.push_back(formula::And(va3));
return formula::Or(vo);
}
}
formula remove_x(formula f)
static formula
remove_x_rec(formula f, atomic_prop_set& aps)
{
if (f.is_syntactic_stutter_invariant())
return f;
atomic_prop_set aps;
atomic_prop_collect(f, &aps);
return remove_x_rec(f, aps);
auto rec = [&aps](formula f)
{
return remove_x_rec(f, aps);
};
if (!f.is(op::X))
return f.map(rec);
formula c = rec(f[0]);
std::vector<formula> vo;
for (auto i: aps)
{
// First line
std::vector<formula> va1;
formula npi = formula::Not(i);
va1.push_back(i);
va1.push_back(formula::U(i, formula::And({npi, c})));
for (auto j: aps)
if (j != i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::U(formula::Not(j), npi);
va1.push_back(formula::Or({formula::U(j, npi), tmp}));
}
vo.push_back(formula::And(va1));
// Second line
std::vector<formula> va2;
va2.push_back(npi);
va2.push_back(formula::U(npi, formula::And({i, c})));
for (auto j: aps)
if (j != i)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::U(formula::Not(j), i);
va2.push_back(formula::Or({formula::U(j, i), tmp}));
}
vo.push_back(formula::And(va2));
}
// Third line
std::vector<formula> va3;
for (auto i: aps)
{
// make sure the arguments of OR are created in a
// deterministic order
auto tmp = formula::G(formula::Not(i));
va3.push_back(formula::Or({formula::G(i), tmp}));
}
va3.push_back(c);
vo.push_back(formula::And(va3));
return formula::Or(vo);
}
}
formula remove_x(formula f)
{
if (f.is_syntactic_stutter_invariant())
return f;
atomic_prop_set aps;
atomic_prop_collect(f, &aps);
return remove_x_rec(f, aps);
}
}

View file

@ -23,27 +23,24 @@
namespace spot
{
namespace ltl
{
/// \brief Rewrite a stutter-insensitive formula \a f without
/// using the X operator.
///
/// This function may also be applied to stutter-sensitive formulas,
/// but in that case the resulting formula is not equivalent.
///
/** \verbatim
@Article{ etessami.00.ipl,
author = {Kousha Etessami},
title = {A note on a question of {P}eled and {W}ilke regarding
stutter-invariant {LTL}},
journal = {Information Processing Letters},
volume = {75},
number = {6},
year = {2000},
pages = {261--263}
}
\endverbatim */
SPOT_API
formula remove_x(formula f);
}
/// \brief Rewrite a stutter-insensitive formula \a f without
/// using the X operator.
///
/// This function may also be applied to stutter-sensitive formulas,
/// but in that case the resulting formula is not equivalent.
///
/** \verbatim
@Article{ etessami.00.ipl,
author = {Kousha Etessami},
title = {A note on a question of {P}eled and {W}ilke regarding
stutter-invariant {LTL}},
journal = {Information Processing Letters},
volume = {75},
number = {6},
year = {2000},
pages = {261--263}
}
\endverbatim */
SPOT_API
formula remove_x(formula f);
}

View file

@ -24,24 +24,20 @@
namespace spot
{
namespace ltl
formula simplify_f_g(formula p)
{
formula simplify_f_g(formula p)
{
// 1 U p = Fp
if (p.is(op::U) && p[0].is_tt())
return formula::F(p[1]);
// 0 R p = Gp
if (p.is(op::R) && p[0].is_ff())
return formula::G(p[1]);
// p W 0 = Gp
if (p.is(op::W) && p[1].is_ff())
return formula::G(p[0]);
// p M 1 = Fp
if (p.is(op::M) && p[1].is_tt())
return formula::F(p[0]);
return p.map(simplify_f_g);
}
// 1 U p = Fp
if (p.is(op::U) && p[0].is_tt())
return formula::F(p[1]);
// 0 R p = Gp
if (p.is(op::R) && p[0].is_ff())
return formula::G(p[1]);
// p W 0 = Gp
if (p.is(op::W) && p[1].is_ff())
return formula::G(p[0]);
// p M 1 = Fp
if (p.is(op::M) && p[1].is_tt())
return formula::F(p[0]);
return p.map(simplify_f_g);
}
}

View file

@ -26,19 +26,16 @@
namespace spot
{
namespace ltl
{
/// \ingroup ltl_rewriting
/// \brief Replace <code>true U f</code> and <code>false R g</code> by
/// <code>F f</code> and <code>G g</code>.
///
/// Perform the following rewriting (from left to right):
///
/// - true U a = F a
/// - a M true = F a
/// - false R a = G a
/// - a W false = G a
///
SPOT_API formula simplify_f_g(formula f);
}
/// \ingroup ltl_rewriting
/// \brief Replace <code>true U f</code> and <code>false R g</code> by
/// <code>F f</code> and <code>G g</code>.
///
/// Perform the following rewriting (from left to right):
///
/// - true U a = F a
/// - a M true = F a
/// - false R a = G a
/// - a W false = G a
///
SPOT_API formula simplify_f_g(formula f);
}

File diff suppressed because it is too large Load diff

View file

@ -26,179 +26,176 @@
namespace spot
{
namespace ltl
class ltl_simplifier_options
{
class ltl_simplifier_options
public:
ltl_simplifier_options(bool basics = true,
bool synt_impl = true,
bool event_univ = true,
bool containment_checks = false,
bool containment_checks_stronger = false,
bool nenoform_stop_on_boolean = false,
bool reduce_size_strictly = false,
bool boolean_to_isop = false,
bool favor_event_univ = false)
: reduce_basics(basics),
synt_impl(synt_impl),
event_univ(event_univ),
containment_checks(containment_checks),
containment_checks_stronger(containment_checks_stronger),
nenoform_stop_on_boolean(nenoform_stop_on_boolean),
reduce_size_strictly(reduce_size_strictly),
boolean_to_isop(boolean_to_isop),
favor_event_univ(favor_event_univ)
{
public:
ltl_simplifier_options(bool basics = true,
bool synt_impl = true,
bool event_univ = true,
bool containment_checks = false,
bool containment_checks_stronger = false,
bool nenoform_stop_on_boolean = false,
bool reduce_size_strictly = false,
bool boolean_to_isop = false,
bool favor_event_univ = false)
: reduce_basics(basics),
synt_impl(synt_impl),
event_univ(event_univ),
containment_checks(containment_checks),
containment_checks_stronger(containment_checks_stronger),
nenoform_stop_on_boolean(nenoform_stop_on_boolean),
reduce_size_strictly(reduce_size_strictly),
boolean_to_isop(boolean_to_isop),
favor_event_univ(favor_event_univ)
{
}
}
ltl_simplifier_options(int level) :
ltl_simplifier_options(false, false, false)
{
switch (level)
{
case 3:
containment_checks = true;
containment_checks_stronger = true;
// fall through
case 2:
synt_impl = true;
// fall through
case 1:
reduce_basics = true;
event_univ = true;
// fall through
default:
break;
}
}
bool reduce_basics;
bool synt_impl;
bool event_univ;
bool containment_checks;
bool containment_checks_stronger;
// If true, Boolean subformulae will not be put into
// negative normal form.
bool nenoform_stop_on_boolean;
// If true, some rules that produce slightly larger formulae
// will be disabled. Those larger formulae are normally easier
// to translate, so we recommend to set this to false.
bool reduce_size_strictly;
// If true, Boolean subformulae will be rewritten in ISOP form.
bool boolean_to_isop;
// Try to isolate subformulae that are eventual and universal.
bool favor_event_univ;
};
// fwd declaration to hide technical details.
class ltl_simplifier_cache;
/// \ingroup ltl_rewriting
/// \brief Rewrite or simplify \a f in various ways.
class SPOT_API ltl_simplifier
ltl_simplifier_options(int level) :
ltl_simplifier_options(false, false, false)
{
public:
ltl_simplifier(const bdd_dict_ptr& dict = make_bdd_dict());
ltl_simplifier(const ltl_simplifier_options& opt,
bdd_dict_ptr dict = make_bdd_dict());
~ltl_simplifier();
switch (level)
{
case 3:
containment_checks = true;
containment_checks_stronger = true;
// fall through
case 2:
synt_impl = true;
// fall through
case 1:
reduce_basics = true;
event_univ = true;
// fall through
default:
break;
}
}
/// Simplify the formula \a f (using options supplied to the
/// constructor).
formula simplify(formula f);
bool reduce_basics;
bool synt_impl;
bool event_univ;
bool containment_checks;
bool containment_checks_stronger;
// If true, Boolean subformulae will not be put into
// negative normal form.
bool nenoform_stop_on_boolean;
// If true, some rules that produce slightly larger formulae
// will be disabled. Those larger formulae are normally easier
// to translate, so we recommend to set this to false.
bool reduce_size_strictly;
// If true, Boolean subformulae will be rewritten in ISOP form.
bool boolean_to_isop;
// Try to isolate subformulae that are eventual and universal.
bool favor_event_univ;
};
/// Build the negative normal form of formula \a f.
/// All negations of the formula are pushed in front of the
/// atomic propositions. Operators <=>, =>, xor are all removed
/// (calling spot::ltl::unabbreviate_ltl is not needed).
///
/// \param f The formula to normalize.
/// \param negated If \c true, return the negative normal form of
/// \c !f
formula
// fwd declaration to hide technical details.
class ltl_simplifier_cache;
/// \ingroup ltl_rewriting
/// \brief Rewrite or simplify \a f in various ways.
class SPOT_API ltl_simplifier
{
public:
ltl_simplifier(const bdd_dict_ptr& dict = make_bdd_dict());
ltl_simplifier(const ltl_simplifier_options& opt,
bdd_dict_ptr dict = make_bdd_dict());
~ltl_simplifier();
/// Simplify the formula \a f (using options supplied to the
/// constructor).
formula simplify(formula f);
/// Build the negative normal form of formula \a f.
/// All negations of the formula are pushed in front of the
/// atomic propositions. Operators <=>, =>, xor are all removed
/// (calling spot::unabbreviate_ltl is not needed).
///
/// \param f The formula to normalize.
/// \param negated If \c true, return the negative normal form of
/// \c !f
formula
negative_normal_form(formula f, bool negated = false);
/// \brief Syntactic implication.
///
/// Returns whether \a f syntactically implies \a g.
///
/// This is adapted from
/** \verbatim
@InProceedings{ somenzi.00.cav,
author = {Fabio Somenzi and Roderick Bloem},
title = {Efficient {B\"u}chi Automata for {LTL} Formulae},
booktitle = {Proceedings of the 12th International Conference on
Computer Aided Verification (CAV'00)},
pages = {247--263},
year = {2000},
volume = {1855},
series = {Lecture Notes in Computer Science},
publisher = {Springer-Verlag}
}
\endverbatim */
///
bool syntactic_implication(formula f, formula g);
/// \brief Syntactic implication with one negated argument.
///
/// If \a right is true, this method returns whether
/// \a f implies !\a g. If \a right is false, this returns
/// whether !\a f implies \a g.
bool syntactic_implication_neg(formula f, formula g,
bool right);
/// \brief Syntactic implication.
///
/// Returns whether \a f syntactically implies \a g.
///
/// This is adapted from
/** \verbatim
@InProceedings{ somenzi.00.cav,
author = {Fabio Somenzi and Roderick Bloem},
title = {Efficient {B\"u}chi Automata for {LTL} Formulae},
booktitle = {Proceedings of the 12th International Conference on
Computer Aided Verification (CAV'00)},
pages = {247--263},
year = {2000},
volume = {1855},
series = {Lecture Notes in Computer Science},
publisher = {Springer-Verlag}
}
\endverbatim */
///
bool syntactic_implication(formula f, formula g);
/// \brief Syntactic implication with one negated argument.
///
/// If \a right is true, this method returns whether
/// \a f implies !\a g. If \a right is false, this returns
/// whether !\a f implies \a g.
bool syntactic_implication_neg(formula f, formula g,
bool right);
/// \brief check whether two formulae are equivalent.
///
/// This costly check performs up to four translations,
/// two products, and two emptiness checks.
bool are_equivalent(formula f, formula g);
/// \brief check whether two formulae are equivalent.
///
/// This costly check performs up to four translations,
/// two products, and two emptiness checks.
bool are_equivalent(formula f, formula g);
/// \brief Check whether \a f implies \a g.
///
/// This operation is costlier than syntactic_implication()
/// because it requires two translation, one product and one
/// emptiness check.
bool implication(formula f, formula g);
/// \brief Check whether \a f implies \a g.
///
/// This operation is costlier than syntactic_implication()
/// because it requires two translation, one product and one
/// emptiness check.
bool implication(formula f, formula g);
/// \brief Convert a Boolean formula as a BDD.
///
/// If you plan to use this method, be sure to pass a bdd_dict
/// to the constructor.
bdd as_bdd(formula f);
/// \brief Convert a Boolean formula as a BDD.
///
/// If you plan to use this method, be sure to pass a bdd_dict
/// to the constructor.
bdd as_bdd(formula f);
/// \brief Clear the as_bdd() cache.
///
/// Calling this function is recommended before running other
/// algorithms that create BDD variables in a more natural
/// order. For instance ltl_to_tgba_fm() will usually be more
/// efficient if the BDD variables for atomic propositions have
/// not been ordered before hand.
///
/// This also clears the language containment cache.
void clear_as_bdd_cache();
/// \brief Clear the as_bdd() cache.
///
/// Calling this function is recommended before running other
/// algorithms that create BDD variables in a more natural
/// order. For instance ltl_to_tgba_fm() will usually be more
/// efficient if the BDD variables for atomic propositions have
/// not been ordered before hand.
///
/// This also clears the language containment cache.
void clear_as_bdd_cache();
/// Return the bdd_dict used.
bdd_dict_ptr get_dict() const;
/// Return the bdd_dict used.
bdd_dict_ptr get_dict() const;
/// Cached version of spot::ltl::star_normal_form().
formula star_normal_form(formula f);
/// Cached version of spot::star_normal_form().
formula star_normal_form(formula f);
/// \brief Rewrite a Boolean formula \a f into as an irredundant
/// sum of product.
///
/// This uses a cache, so it is OK to call this with identical
/// arguments.
formula boolean_to_isop(formula f);
/// \brief Rewrite a Boolean formula \a f into as an irredundant
/// sum of product.
///
/// This uses a cache, so it is OK to call this with identical
/// arguments.
formula boolean_to_isop(formula f);
/// Dump statistics about the caches.
void print_stats(std::ostream& os) const;
/// Dump statistics about the caches.
void print_stats(std::ostream& os) const;
private:
ltl_simplifier_cache* cache_;
// Copy disallowed.
ltl_simplifier(const ltl_simplifier&) SPOT_DELETED;
void operator=(const ltl_simplifier&) SPOT_DELETED;
};
}
private:
ltl_simplifier_cache* cache_;
// Copy disallowed.
ltl_simplifier(const ltl_simplifier&) SPOT_DELETED;
void operator=(const ltl_simplifier&) SPOT_DELETED;
};
}

View file

@ -21,129 +21,125 @@
namespace spot
{
namespace ltl
namespace
{
namespace
// E° if bounded=false
// E^□ if nounded=true
template<bool bounded>
class snf_visitor
{
// E° if bounded=false
// E^□ if nounded=true
template<bool bounded>
class snf_visitor
protected:
formula result_;
snf_cache* cache_;
public:
snf_visitor(snf_cache* c)
: cache_(c)
{
protected:
formula result_;
snf_cache* cache_;
public:
snf_visitor(snf_cache* c)
: cache_(c)
{
}
}
formula visit(formula f)
{
if (!f.accepts_eword())
return f;
formula visit(formula f)
{
if (!f.accepts_eword())
return f;
snf_cache::const_iterator i = cache_->find(f);
if (i != cache_->end())
return i->second;
snf_cache::const_iterator i = cache_->find(f);
if (i != cache_->end())
return i->second;
formula out;
switch (f.kind())
{
case op::eword:
out = formula::ff();
break;
case op::Star:
if (!bounded)
out = visit(f[0]); // Strip the star.
else
out = formula::Star(visit(f[0]),
std::max(unsigned(f.min()), 1U), f.max());
break;
case op::Concat:
if (bounded)
{
out = f;
break;
}
// Fall through
case op::OrRat:
case op::AndNLM:
// Let F designate expressions that accept [*0],
// and G designate expressions that do not.
// (G₁;G₂;G₃)° = G₁;G₂;G₃
// (G₁;F₂;G₃)° = (G₁°);F₂;(G₃°) = G₁;F₂;G₃
// because there is nothing to do recursively on a G.
//
// AndNLM can be dealt with similarly.
//
// The above cases are already handled by the
// accepts_eword() tests at the top of this method. So
// we reach this switch, we only have to deal with...
//
// (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°)
// (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°)
// (F₁|G₂|F₃)° = (F₁°)|(G₂°)|(F₃°)
formula out;
switch (f.kind())
{
case op::eword:
out = formula::ff();
break;
case op::Star:
if (!bounded)
out = visit(f[0]); // Strip the star.
else
out = formula::Star(visit(f[0]),
std::max(unsigned(f.min()), 1U), f.max());
break;
case op::Concat:
if (bounded)
{
unsigned s = f.size();
std::vector<formula> v;
v.reserve(s);
for (unsigned pos = 0; pos < s; ++pos)
v.emplace_back(visit(f[pos]));
out = formula::OrRat(v);
out = f;
break;
}
case op::ff:
case op::tt:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
case op::Fusion:
case op::Or:
case op::And:
SPOT_UNREACHABLE();
case op::AndRat: // Can AndRat be handled better?
case op::FStar: // Can FStar be handled better?
out = f;
// Fall through
case op::OrRat:
case op::AndNLM:
// Let F designate expressions that accept [*0],
// and G designate expressions that do not.
// (G₁;G₂;G₃)° = G₁;G₂;G₃
// (G₁;F₂;G₃)° = (G₁°);F₂;(G₃°) = G₁;F₂;G₃
// because there is nothing to do recursively on a G.
//
// AndNLM can be dealt with similarly.
//
// The above cases are already handled by the
// accepts_eword() tests at the top of this method. So
// we reach this switch, we only have to deal with...
//
// (F₁;F₂;F₃)° = (F₁°)|(F₂°)|(F₃°)
// (F₁&F₂&F₃)° = (F₁°)|(F₂°)|(F₃°)
// (F₁|G₂|F₃)° = (F₁°)|(G₂°)|(F₃°)
{
unsigned s = f.size();
std::vector<formula> v;
v.reserve(s);
for (unsigned pos = 0; pos < s; ++pos)
v.emplace_back(visit(f[pos]));
out = formula::OrRat(v);
break;
}
case op::ff:
case op::tt:
case op::ap:
case op::Not:
case op::X:
case op::F:
case op::G:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::Xor:
case op::Implies:
case op::Equiv:
case op::U:
case op::R:
case op::W:
case op::M:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
case op::Fusion:
case op::Or:
case op::And:
SPOT_UNREACHABLE();
case op::AndRat: // Can AndRat be handled better?
case op::FStar: // Can FStar be handled better?
out = f;
break;
}
return (*cache_)[f] = out;
}
};
}
return (*cache_)[f] = out;
}
};
}
formula
star_normal_form(formula sere, snf_cache* cache)
{
snf_visitor<false> v(cache);
return v.visit(sere);
}
formula
star_normal_form_bounded(formula sere, snf_cache* cache)
{
snf_visitor<true> v(cache);
return v.visit(sere);
}
formula
star_normal_form(formula sere, snf_cache* cache)
{
snf_visitor<false> v(cache);
return v.visit(sere);
}
formula
star_normal_form_bounded(formula sere, snf_cache* cache)
{
snf_visitor<true> v(cache);
return v.visit(sere);
}
}

View file

@ -24,35 +24,31 @@
namespace spot
{
namespace ltl
{
typedef std::unordered_map<formula, formula> snf_cache;
typedef std::unordered_map<formula, formula> snf_cache;
/// Helper to rewrite a sere in Star Normal Form.
///
/// This should only be called on children of a Star operator. It
/// corresponds to the E° operation defined in the following
/// paper.
///
/** \verbatim
@Article{ bruggeman.96.tcs,
author = {Anne Br{\"u}ggemann-Klein},
title = {Regular Expressions into Finite Automata},
journal = {Theoretical Computer Science},
year = {1996},
volume = {120},
pages = {87--98}
}
\endverbatim */
///
/// \param sere the SERE to rewrite
/// \param cache an optional cache
SPOT_API formula
star_normal_form(formula sere, snf_cache* cache = nullptr);
/// Helper to rewrite a sere in Star Normal Form.
///
/// This should only be called on children of a Star operator. It
/// corresponds to the E° operation defined in the following
/// paper.
///
/** \verbatim
@Article{ bruggeman.96.tcs,
author = {Anne Br{\"u}ggemann-Klein},
title = {Regular Expressions into Finite Automata},
journal = {Theoretical Computer Science},
year = {1996},
volume = {120},
pages = {87--98}
}
\endverbatim */
///
/// \param sere the SERE to rewrite
/// \param cache an optional cache
SPOT_API formula
star_normal_form(formula sere, snf_cache* cache = nullptr);
/// A variant of star_normal_form() for r[*0..j] where j < ω.
SPOT_API formula
star_normal_form_bounded(formula sere, snf_cache* cache = nullptr);
}
/// A variant of star_normal_form() for r[*0..j] where j < ω.
SPOT_API formula
star_normal_form_bounded(formula sere, snf_cache* cache = nullptr);
}

View file

@ -22,232 +22,229 @@
namespace spot
{
namespace ltl
unabbreviator::unabbreviator(const char* opt)
{
unabbreviator::unabbreviator(const char* opt)
{
while (*opt)
switch (char c = *opt++)
while (*opt)
switch (char c = *opt++)
{
case 'e':
re_e_ = true;
re_some_bool_ = true;
break;
case 'F':
re_f_ = true;
re_some_f_g_ = true;
break;
case 'G':
re_g_ = true;
re_some_f_g_ = true;
break;
case 'i':
re_i_ = true;
re_some_bool_ = true;
break;
case 'M':
re_m_ = true;
re_some_other_ = true;
break;
case 'R':
re_r_ = true;
re_some_other_ = true;
break;
case 'W':
re_w_ = true;
re_some_other_ = true;
break;
case '^':
re_xor_ = true;
re_some_bool_ = true;
break;
default:
throw std::runtime_error
(std::string("unknown unabbreviation option: ")
+ c);
}
}
formula unabbreviator::run(formula in)
{
auto entry = cache_.emplace(in, nullptr);
if (!entry.second)
return entry.first->second;
// Skip recursion whenever possible
bool no_boolean_rewrite = !re_some_bool_ || in.is_sugar_free_boolean();
bool no_f_g_rewrite = !re_some_f_g_ || in.is_sugar_free_ltl();
if (no_boolean_rewrite
&& (in.is_boolean() || (no_f_g_rewrite && !re_some_other_)))
return entry.first->second = in;
auto rec = [this](formula f)
{
return this->run(f);
};
formula out = in;
if (in.size() > 0)
out = in.map(rec);
switch (out.kind())
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
case op::U:
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Concat:
case op::Fusion:
case op::Star:
case op::FStar:
break;
case op::F:
// F f = true U f
if (!re_f_)
break;
out = formula::U(formula::tt(), out[0]);
break;
case op::G:
// G f = false R f
// G f = f W false
// G f = !F!f
// G f = !(true U !f)
if (!re_g_)
break;
if (!re_r_)
{
case 'e':
re_e_ = true;
re_some_bool_ = true;
out = formula::R(formula::ff(), out[0]);
break;
}
if (!re_w_)
{
out = formula::W(out[0], formula::ff());
break;
case 'F':
re_f_ = true;
re_some_f_g_ = true;
break;
case 'G':
re_g_ = true;
re_some_f_g_ = true;
break;
case 'i':
re_i_ = true;
re_some_bool_ = true;
break;
case 'M':
re_m_ = true;
re_some_other_ = true;
break;
case 'R':
re_r_ = true;
re_some_other_ = true;
break;
case 'W':
re_w_ = true;
re_some_other_ = true;
break;
case '^':
re_xor_ = true;
re_some_bool_ = true;
break;
default:
throw std::runtime_error
(std::string("unknown unabbreviation option: ")
+ c);
}
}
formula unabbreviator::run(formula in)
{
auto entry = cache_.emplace(in, nullptr);
if (!entry.second)
return entry.first->second;
// Skip recursion whenever possible
bool no_boolean_rewrite = !re_some_bool_ || in.is_sugar_free_boolean();
bool no_f_g_rewrite = !re_some_f_g_ || in.is_sugar_free_ltl();
if (no_boolean_rewrite
&& (in.is_boolean() || (no_f_g_rewrite && !re_some_other_)))
return entry.first->second = in;
auto rec = [this](formula f)
{
return this->run(f);
};
formula out = in;
if (in.size() > 0)
out = in.map(rec);
switch (out.kind())
auto nc = formula::Not(out[0]);
if (!re_f_)
{
out = formula::Not(formula::F(nc));
break;
}
out = formula::Not(formula::U(formula::tt(), nc));
break;
}
case op::Xor:
// f1 ^ f2 == !(f1 <-> f2)
// f1 ^ f2 == (f1 & !f2) | (f2 & !f1)
if (!re_xor_)
break;
{
case op::ff:
case op::tt:
case op::eword:
case op::ap:
case op::Not:
case op::X:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked:
case op::EConcat:
case op::EConcatMarked:
case op::UConcat:
case op::U:
case op::Or:
case op::OrRat:
case op::And:
case op::AndRat:
case op::AndNLM:
case op::Concat:
case op::Fusion:
case op::Star:
case op::FStar:
break;
case op::F:
// F f = true U f
if (!re_f_)
break;
out = formula::U(formula::tt(), out[0]);
break;
case op::G:
// G f = false R f
// G f = f W false
// G f = !F!f
// G f = !(true U !f)
if (!re_g_)
break;
if (!re_r_)
{
out = formula::R(formula::ff(), out[0]);
break;
}
if (!re_w_)
{
out = formula::W(out[0], formula::ff());
break;
}
auto f1 = out[0];
auto f2 = out[1];
if (!re_e_)
{
auto nc = formula::Not(out[0]);
if (!re_f_)
{
out = formula::Not(formula::F(nc));
break;
}
out = formula::Not(formula::U(formula::tt(), nc));
break;
out = formula::Not(formula::Equiv(f1, f2));
}
case op::Xor:
// f1 ^ f2 == !(f1 <-> f2)
// f1 ^ f2 == (f1 & !f2) | (f2 & !f1)
if (!re_xor_)
break;
else
{
auto f1 = out[0];
auto f2 = out[1];
if (!re_e_)
{
out = formula::Not(formula::Equiv(f1, f2));
}
else
{
auto a = formula::And({f1, formula::Not(f2)});
auto b = formula::And({f2, formula::Not(f1)});
out = formula::Or({a, b});
}
}
break;
case op::Implies:
// f1 => f2 == !f1 | f2
if (!re_i_)
break;
out = formula::Or({formula::Not(out[0]), out[1]});
break;
case op::Equiv:
// f1 <=> f2 == (f1 & f2) | (!f1 & !f2)
if (!re_e_)
break;
{
auto f1 = out[0];
auto f2 = out[1];
auto nf1 = formula::Not(f1);
auto nf2 = formula::Not(f2);
auto term1 = formula::And({f1, f2});
auto term2 = formula::And({nf1, nf2});
out = formula::Or({term1, term2});
break;
}
case op::R:
// f1 R f2 = f2 W (f1 & f2)
// f1 R f2 = f2 U ((f1 & f2) | Gf2)
// f1 R f2 = f2 U ((f1 & f2) | !F!f2)
// f1 R f2 = f2 U ((f1 & f2) | !(1 U !f2))
if (!re_r_)
break;
{
auto f1 = out[0];
auto f2 = out[1];
auto f12 = formula::And({f1, f2});
if (!re_w_)
{
out = formula::W(f2, f12);
break;
}
auto gf2 = formula::G(f2);
if (re_g_)
gf2 = run(gf2);
out = formula::U(f2, formula::Or({f12, out}));
break;
}
case op::W:
// f1 W f2 = f2 R (f2 | f1)
// f1 W f2 = f1 U (f2 | G f1)
// f1 W f2 = f1 U (f2 | !F !f1)
// f1 W f2 = f1 U (f2 | !(1 U !f1))
if (!re_w_)
break;
{
auto f1 = out[0];
auto f2 = out[1];
if (!re_r_)
{
out = formula::R(f2, formula::Or({f2, f1}));
break;
}
auto gf1 = formula::G(f1);
if (re_g_)
gf1 = rec(gf1);
out = formula::U(f1, formula::Or({f2, out}));
break;
}
case op::M:
// f1 M f2 = f2 U (g2 & f1)
if (!re_m_)
break;
{
auto f2 = out[1];
out = formula::U(f2, formula::And({f2, out[0]}));
break;
auto a = formula::And({f1, formula::Not(f2)});
auto b = formula::And({f2, formula::Not(f1)});
out = formula::Or({a, b});
}
}
return entry.first->second = out;
}
break;
case op::Implies:
// f1 => f2 == !f1 | f2
if (!re_i_)
break;
out = formula::Or({formula::Not(out[0]), out[1]});
break;
case op::Equiv:
// f1 <=> f2 == (f1 & f2) | (!f1 & !f2)
if (!re_e_)
break;
{
auto f1 = out[0];
auto f2 = out[1];
auto nf1 = formula::Not(f1);
auto nf2 = formula::Not(f2);
auto term1 = formula::And({f1, f2});
auto term2 = formula::And({nf1, nf2});
out = formula::Or({term1, term2});
break;
}
case op::R:
// f1 R f2 = f2 W (f1 & f2)
// f1 R f2 = f2 U ((f1 & f2) | Gf2)
// f1 R f2 = f2 U ((f1 & f2) | !F!f2)
// f1 R f2 = f2 U ((f1 & f2) | !(1 U !f2))
if (!re_r_)
break;
{
auto f1 = out[0];
auto f2 = out[1];
auto f12 = formula::And({f1, f2});
if (!re_w_)
{
out = formula::W(f2, f12);
break;
}
auto gf2 = formula::G(f2);
if (re_g_)
gf2 = run(gf2);
out = formula::U(f2, formula::Or({f12, out}));
break;
}
case op::W:
// f1 W f2 = f2 R (f2 | f1)
// f1 W f2 = f1 U (f2 | G f1)
// f1 W f2 = f1 U (f2 | !F !f1)
// f1 W f2 = f1 U (f2 | !(1 U !f1))
if (!re_w_)
break;
{
auto f1 = out[0];
auto f2 = out[1];
if (!re_r_)
{
out = formula::R(f2, formula::Or({f2, f1}));
break;
}
auto gf1 = formula::G(f1);
if (re_g_)
gf1 = rec(gf1);
out = formula::U(f1, formula::Or({f2, out}));
break;
}
case op::M:
// f1 M f2 = f2 U (g2 & f1)
if (!re_m_)
break;
{
auto f2 = out[1];
out = formula::U(f2, formula::And({f2, out[0]}));
break;
}
}
return entry.first->second = out;
}
formula unabbreviate(formula in, const char* opt)
{
unabbreviator un(opt);
return un.run(in);
}
formula unabbreviate(formula in, const char* opt)
{
unabbreviator un(opt);
return un.run(in);
}
}

View file

@ -24,49 +24,45 @@
namespace spot
{
namespace ltl
constexpr const char* default_unabbrev_string = "eFGiMW^";
/// \ingroup ltl_rewriting
/// \brief Clone and rewrite a formula to remove specified operators
/// logical operators.
class SPOT_API unabbreviator final
{
constexpr const char* default_unabbrev_string = "eFGiMW^";
/// \ingroup ltl_rewriting
/// \brief Clone and rewrite a formula to remove specified operators
/// logical operators.
class SPOT_API unabbreviator final
{
private:
// What to rewrite?
bool re_e_ = false;
bool re_f_ = false;
bool re_g_ = false;
bool re_i_ = false;
bool re_m_ = false;
bool re_r_ = false;
bool re_w_ = false;
bool re_xor_ = false;
bool re_some_bool_ = false; // rewrite xor, i, or e
bool re_some_f_g_ = false; // rewrite F or G
bool re_some_other_ = false; // rewrite W, M, or R
// Cache of rewritten subformulas
std::unordered_map<formula, formula> cache_;
public:
/// \brief Constructor
///
/// The set of operators to remove should be passed as a string
/// which in which each letter denote an operator (using LBT's
/// convention).
unabbreviator(const char* opt = default_unabbrev_string);
formula run(formula in);
};
/// \ingroup ltl_rewriting
/// \brief Clone and rewrite a formula to remove specified operators
/// logical operators.
private:
// What to rewrite?
bool re_e_ = false;
bool re_f_ = false;
bool re_g_ = false;
bool re_i_ = false;
bool re_m_ = false;
bool re_r_ = false;
bool re_w_ = false;
bool re_xor_ = false;
bool re_some_bool_ = false; // rewrite xor, i, or e
bool re_some_f_g_ = false; // rewrite F or G
bool re_some_other_ = false; // rewrite W, M, or R
// Cache of rewritten subformulas
std::unordered_map<formula, formula> cache_;
public:
/// \brief Constructor
///
/// The set of operators to remove should be passed as a string
/// which in which each letter denote an operator (using LBT's
/// convention).
SPOT_API formula
unabbreviate(formula in, const char* opt= default_unabbrev_string);
}
unabbreviator(const char* opt = default_unabbrev_string);
formula run(formula in);
};
/// \ingroup ltl_rewriting
/// \brief Clone and rewrite a formula to remove specified operators
/// logical operators.
///
/// The set of operators to remove should be passed as a string
/// which in which each letter denote an operator (using LBT's
/// convention).
SPOT_API formula
unabbreviate(formula in, const char* opt= default_unabbrev_string);
}