Adding ltlgrind as a command-line tool
* src/bin/ltlgrind.cc: New file, command-line tool to get mutations of a formula. * src/bin/Makefile.am: Add it. * src/ltlvisit/mutation.hh, src/ltlvisit/mutation.cc: New files providing the get_mutations function. * src/ltlvisit/Makefile.am: Add it. * src/ltltest/ltlgrind.test: Test it. * src/ltltest/Makefile.am: Add it. * src/bin/man/ltlgrind.x: Document it. * src/bin/man/Makefile.am: Add it. * doc/org/ltlgrind.org: Document it. * doc/org/tools.org: Add link to ltlgrind documentation page.
This commit is contained in:
parent
51fe5108fe
commit
e327f6ea11
11 changed files with 923 additions and 1 deletions
399
src/ltlvisit/mutation.cc
Normal file
399
src/ltlvisit/mutation.cc
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
// Copyright (C) 2013 Laboratoire de Recherche et Developpement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
|
||||
#include "ltlast/allnodes.hh"
|
||||
#include "ltlvisit/apcollect.hh"
|
||||
#include "ltlvisit/clone.hh"
|
||||
#include "ltlvisit/mutation.hh"
|
||||
#include "ltlvisit/length.hh"
|
||||
#include "misc/hash.hh"
|
||||
|
||||
#define Implies_(x, y) \
|
||||
spot::ltl::binop::instance(spot::ltl::binop::Implies, (x), (y))
|
||||
#define And_(x, y) \
|
||||
spot::ltl::multop::instance(spot::ltl::multop::And, (x), (y))
|
||||
#define AndRat_(x, y) \
|
||||
spot::ltl::multop::instance(spot::ltl::multop::AndRat, (x), (y))
|
||||
#define AndNLM_(x) \
|
||||
spot::ltl::multop::instance(spot::ltl::multop::AndNLM, (x))
|
||||
#define Concat_(x, y) \
|
||||
spot::ltl::multop::instance(spot::ltl::multop::Concat, (x), (y))
|
||||
#define Not_(x) \
|
||||
spot::ltl::unop::instance(spot::ltl::unop::Not, (x))
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace ltl
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class replace_visitor : public clone_visitor
|
||||
{
|
||||
public:
|
||||
void visit(const atomic_prop* ap)
|
||||
{
|
||||
if (ap == (*ap1_))
|
||||
result_ = (*ap2_)->clone();
|
||||
else
|
||||
result_ = ap->clone();
|
||||
}
|
||||
|
||||
const formula*
|
||||
replace(const formula* f,
|
||||
atomic_prop_set::iterator ap1, atomic_prop_set::iterator ap2)
|
||||
{
|
||||
ap1_ = ap1;
|
||||
ap2_ = ap2;
|
||||
return recurse(f);
|
||||
}
|
||||
|
||||
private:
|
||||
atomic_prop_set::iterator ap1_;
|
||||
atomic_prop_set::iterator ap2_;
|
||||
};
|
||||
|
||||
typedef std::vector<const formula*> vec;
|
||||
class mutation_visitor : public clone_visitor
|
||||
{
|
||||
public:
|
||||
mutation_visitor(const formula* f, unsigned opts) : f_(f), opts_(opts)
|
||||
{
|
||||
}
|
||||
|
||||
void visit(const atomic_prop* ap)
|
||||
{
|
||||
result_ = 0;
|
||||
if (opts_ & MUT_AP2CONST)
|
||||
{
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = constant::true_instance();
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = constant::false_instance();
|
||||
}
|
||||
if (!result_)
|
||||
result_ = ap->clone();
|
||||
}
|
||||
|
||||
void visit(const unop* uo)
|
||||
{
|
||||
result_ = 0;
|
||||
if (opts_ & MUT_REMOVE_OPS)
|
||||
{
|
||||
if ((uo->op() == unop::G
|
||||
|| uo->op() == unop::F
|
||||
|| uo->op() == unop::X
|
||||
|| uo->op() == unop::Not)
|
||||
&& mutation_counter_-- == 0)
|
||||
result_ = uo->child()->clone();
|
||||
}
|
||||
if (!result_)
|
||||
{
|
||||
if (mutation_counter_ < 0)
|
||||
result_ = uo->clone();
|
||||
else
|
||||
{
|
||||
result_ = unop::instance(uo->op(), recurse(uo->child()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(const binop* bo)
|
||||
{
|
||||
const formula* first = bo->first();
|
||||
const formula* second = bo->second();
|
||||
result_ = 0;
|
||||
|
||||
if (opts_ & MUT_REMOVE_OPS && mutation_counter_-- == 0)
|
||||
result_ = first->clone();
|
||||
if (opts_ & MUT_REMOVE_OPS && mutation_counter_-- == 0)
|
||||
result_ = second->clone();
|
||||
if (opts_ & MUT_REWRITE_OPS)
|
||||
{
|
||||
switch (bo->op())
|
||||
{
|
||||
case binop::U:
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = binop::instance(binop::W, first->clone(),
|
||||
second->clone());
|
||||
break;
|
||||
case binop::M:
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = binop::instance(binop::R, first->clone(),
|
||||
second->clone());
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = binop::instance(binop::U, second->clone(),
|
||||
first->clone());
|
||||
break;
|
||||
case binop::R:
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = binop::instance(binop::W, second->clone(),
|
||||
first->clone());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (opts_ & MUT_SPLIT_OPS)
|
||||
{
|
||||
switch (bo->op())
|
||||
{
|
||||
case binop::Equiv:
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = Implies_(first->clone(), second->clone());
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = Implies_(second->clone(), first->clone());
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = And_(first->clone(), second->clone());
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = And_(Not_(first->clone()),
|
||||
Not_(second->clone()));
|
||||
break;
|
||||
case binop::Xor:
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = And_(first->clone(), Not_(second->clone()));
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = And_(Not_(first->clone()), second->clone());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!result_)
|
||||
{
|
||||
if (mutation_counter_ < 0)
|
||||
result_ = bo->clone();
|
||||
else
|
||||
result_ =
|
||||
binop::instance(bo->op(), recurse(first), recurse(second));
|
||||
}
|
||||
}
|
||||
|
||||
void visit(const bunop* bu)
|
||||
{
|
||||
const formula* c = bu->child()->clone();
|
||||
result_ = 0;
|
||||
|
||||
if (opts_ & MUT_REMOVE_OPS && mutation_counter_-- == 0)
|
||||
result_ = c->clone();
|
||||
if (opts_ & MUT_SIMPLIFY_BOUNDS)
|
||||
{
|
||||
if (bu->min() > 0 && mutation_counter_-- == 0)
|
||||
result_ =
|
||||
bunop::instance(bu->op(), c, bu->min() - 1, bu->max());
|
||||
if (bu->min() > 0 && mutation_counter_-- == 0)
|
||||
result_ = bunop::instance(bu->op(), c, 0, bu->max());
|
||||
if (bu->max() != bunop::unbounded && bu->max() > bu->min()
|
||||
&& mutation_counter_-- == 0)
|
||||
result_ =
|
||||
bunop::instance(bu->op(), c, bu->min(), bu->max() - 1);
|
||||
if (bu->max() != bunop::unbounded && mutation_counter_-- == 0)
|
||||
result_ = bunop::instance(bu->op(), c, bu->min(),
|
||||
bunop::unbounded);
|
||||
}
|
||||
if (!result_)
|
||||
{
|
||||
c->destroy();
|
||||
if (mutation_counter_ < 0)
|
||||
result_ = bu->clone();
|
||||
else
|
||||
result_ = bunop::instance(bu->op(), recurse(c), bu->min(),
|
||||
bu->max());
|
||||
}
|
||||
}
|
||||
|
||||
void visit(const multop* mo)
|
||||
{
|
||||
int mos = mo->size();
|
||||
int i;
|
||||
result_ = 0;
|
||||
|
||||
if (opts_ & MUT_REMOVE_MULTOP_OPERANDS)
|
||||
{
|
||||
for (i = 0; i < mos; ++i)
|
||||
if (mutation_counter_-- == 0)
|
||||
result_ = mo->all_but(i);
|
||||
}
|
||||
|
||||
if (opts_ & MUT_SPLIT_OPS && mo->op() == multop::AndNLM)
|
||||
{
|
||||
if (mutation_counter_ >= 0 && mutation_counter_ < 2 * (mos - 1))
|
||||
{
|
||||
vec* v1 = new vec();
|
||||
vec* v2 = new vec();
|
||||
v1->push_back(mo->nth(0)->clone());
|
||||
bool reverse = false;
|
||||
i = 1;
|
||||
while (i < mos)
|
||||
{
|
||||
if (mutation_counter_-- == 0)
|
||||
break;
|
||||
if (mutation_counter_-- == 0)
|
||||
{
|
||||
reverse = true;
|
||||
break;
|
||||
}
|
||||
v1->push_back(mo->nth(i++)->clone());
|
||||
}
|
||||
for (; i < mos; ++i)
|
||||
v2->push_back(mo->nth(i)->clone());
|
||||
const formula* tstar =
|
||||
bunop::instance(bunop::Star, constant::true_instance(),
|
||||
0,
|
||||
bunop::unbounded);
|
||||
const formula* first = AndNLM_(v1);
|
||||
const formula* second = AndNLM_(v2);
|
||||
if (!reverse)
|
||||
result_ = AndRat_(Concat_(first, tstar), second);
|
||||
else
|
||||
result_ = AndRat_(Concat_(second, tstar), first);
|
||||
}
|
||||
else
|
||||
mutation_counter_ -= 2 * (mos - 1);
|
||||
}
|
||||
|
||||
if (!result_)
|
||||
{
|
||||
if (mutation_counter_ < 0)
|
||||
result_ = mo->clone();
|
||||
else
|
||||
{
|
||||
vec* v = new vec();
|
||||
for (i = 0; i < mos; ++i)
|
||||
v->push_back(recurse(mo->nth(i)));
|
||||
result_ = multop::instance(mo->op(), v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const formula*
|
||||
recurse(const formula* f)
|
||||
{
|
||||
f->accept(*this);
|
||||
return result_;
|
||||
}
|
||||
|
||||
const formula*
|
||||
get_mutation(int n)
|
||||
{
|
||||
mutation_counter_ = n;
|
||||
const formula* mut = recurse(f_);
|
||||
if (mut == f_)
|
||||
{
|
||||
mut->destroy();
|
||||
return 0;
|
||||
}
|
||||
return mut;
|
||||
}
|
||||
|
||||
private:
|
||||
const formula* f_;
|
||||
int mutation_counter_ = 0;
|
||||
unsigned opts_;
|
||||
};
|
||||
|
||||
bool
|
||||
formula_length_less_than(const formula* left, const formula* right)
|
||||
{
|
||||
assert(left);
|
||||
assert(right);
|
||||
if (left == right)
|
||||
return false;
|
||||
return length(left) < length(right);
|
||||
}
|
||||
|
||||
typedef std::set<const formula*, formula_ptr_less_than> fset_t;
|
||||
|
||||
void
|
||||
single_mutation_rec(const formula* f, fset_t& mutations, unsigned opts,
|
||||
int& n, unsigned m)
|
||||
{
|
||||
if (m == 0)
|
||||
{
|
||||
if (mutations.insert(f).second)
|
||||
{
|
||||
f->clone();
|
||||
--n;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const formula* mut(nullptr);
|
||||
int i = 0;
|
||||
mutation_visitor mv(f, opts);
|
||||
while (n > 0 && (mut = mv.get_mutation(i++)))
|
||||
{
|
||||
single_mutation_rec(mut, mutations, opts, n, m - 1);
|
||||
mut->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
replace_ap_rec(const formula* f, fset_t& mutations, unsigned opts,
|
||||
int& n, unsigned m)
|
||||
{
|
||||
if (m == 0)
|
||||
{
|
||||
if (mutations.insert(f).second)
|
||||
{
|
||||
f->clone();
|
||||
--n;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const formula* mut;
|
||||
replace_visitor rv;
|
||||
std::unique_ptr<atomic_prop_set> aps =
|
||||
std::unique_ptr<atomic_prop_set>(atomic_prop_collect(f));
|
||||
atomic_prop_set::iterator ap1;
|
||||
atomic_prop_set::iterator ap2;
|
||||
for (ap1 = aps->begin(); ap1 != aps->end() && n > 0; ++ap1)
|
||||
for (ap2 = aps->begin(); ap2 != aps->end() && n > 0; ++ap2)
|
||||
{
|
||||
if (ap1 == ap2)
|
||||
continue;
|
||||
mut = rv.replace(f, ap1, ap2);
|
||||
replace_ap_rec(mut, mutations, opts, n, m - 1);
|
||||
mut->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec get_mutations(const formula* f,
|
||||
unsigned opts,
|
||||
bool sort,
|
||||
int n,
|
||||
unsigned m)
|
||||
{
|
||||
fset_t mutations;
|
||||
single_mutation_rec(f, mutations, opts, n, m);
|
||||
if (opts & MUT_REMOVE_ONE_AP)
|
||||
replace_ap_rec(f, mutations, opts, n, m);
|
||||
|
||||
vec res(mutations.begin(), mutations.end());
|
||||
if (sort)
|
||||
std::sort(res.begin(), res.end(), formula_length_less_than);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue