Commit 3bf3d2c8 authored by Thibaud Michaud's avatar Thibaud Michaud Committed by Alexandre Duret-Lutz

Adding python functions to mirror the functionalities found in src/bin

* wrap/python/spot.i: Rename to...
* wrap/python/spot_impl.i: ...this, and import spot_impl from spot.py so
that it is not needed to recompile everything when modifying python
code.
* wrap/python/spot.py: Adding python functions to mirror the
functionalities found in src/bin.
* src/bin/common_r.cc: Move simplification level...
* src/ltlvisit/simplify.hh: ... here as a constructor of
ltl_simplifier_options, to make it available in wrap/python.
* src/bin/ltlfilt.cc: Set simplification level using the new
ltl_simplifier_options constructor.
* src/bin/randltl.cc: Move most of the code...
* src/ltlvisit/randomltl.cc, src/ltlvisit/randomltl.hh: ... here, as a
class named randltlgenerator.
* wrap/python/tests/bddnqueen.py, wrap/python/tests/minato.py: Avoid
calling bdd_init twice by moving 'import spot' after bdd initialization.
* wrap/python/Makefile.am: Rename spot to spot_impl
* wrap/python/tests/Makefile.am: Add ipnbdoctest.py.
* wrap/python/.gitignore: Rename spot.py to spot_impl.py
* src/ltlvisit/tostring.cc: \ttrue and \ffalse should be \top and \bot.
* wrap/python/tests/ipnbdoctest.py: Run code cells of a python notebook
and compare the output to the actual content of the notebook.
* wrap/python/tests/randltl.ipynb: Document and test randltl.
* wrap/python/tests/run.in: Call ipnbdoctest.py to run ipython
notebooks.
parent cb9867b7
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement // Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE). // de l'Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
...@@ -38,26 +38,3 @@ parse_r(const char* arg) ...@@ -38,26 +38,3 @@ parse_r(const char* arg)
} }
error(2, 0, "invalid simplification level '%s'", arg); error(2, 0, "invalid simplification level '%s'", arg);
} }
spot::ltl::ltl_simplifier_options
simplifier_options()
{
spot::ltl::ltl_simplifier_options options(false, false, false);
switch (simplification_level)
{
case 3:
options.containment_checks = true;
options.containment_checks_stronger = true;
// fall through
case 2:
options.synt_impl = true;
// fall through
case 1:
options.reduce_basics = true;
options.event_univ = true;
// fall through
default:
break;
}
return options;
}
...@@ -638,7 +638,7 @@ main(int argc, char** argv) ...@@ -638,7 +638,7 @@ main(int argc, char** argv)
if (boolean_to_isop && simplification_level == 0) if (boolean_to_isop && simplification_level == 0)
simplification_level = 1; simplification_level = 1;
spot::ltl::ltl_simplifier_options opt = simplifier_options(); spot::ltl::ltl_simplifier_options opt(simplification_level);
opt.boolean_to_isop = boolean_to_isop; opt.boolean_to_isop = boolean_to_isop;
spot::ltl::ltl_simplifier simpl(opt); spot::ltl::ltl_simplifier simpl(opt);
try try
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "ltlenv/defaultenv.hh" #include "ltlenv/defaultenv.hh"
#include "misc/random.hh" #include "misc/random.hh"
#include "misc/hash.hh" #include "misc/hash.hh"
#include "misc/optionmap.hh"
const char argp_program_doc[] ="\ const char argp_program_doc[] ="\
Generate random temporal logic formulas.\n\n\ Generate random temporal logic formulas.\n\n\
...@@ -135,9 +136,8 @@ const struct argp_child children[] = ...@@ -135,9 +136,8 @@ const struct argp_child children[] =
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
static enum { OutputBool, OutputLTL, OutputSERE, OutputPSL }
output = OutputLTL;
spot::ltl::atomic_prop_set aprops; spot::ltl::atomic_prop_set aprops;
static int output = OUTPUTLTL;
static char* opt_pL = 0; static char* opt_pL = 0;
static char* opt_pS = 0; static char* opt_pS = 0;
static char* opt_pB = 0; static char* opt_pB = 0;
...@@ -149,39 +149,6 @@ static bool opt_unique = true; ...@@ -149,39 +149,6 @@ static bool opt_unique = true;
static bool opt_wf = false; static bool opt_wf = false;
static bool ap_count_given = false; static bool ap_count_given = false;
void
remove_some_props(spot::ltl::atomic_prop_set& s)
{
// How many propositions to remove from s?
// (We keep at least one.)
size_t n = spot::mrand(s.size());
while (n--)
{
auto i = s.begin();
std::advance(i, spot::mrand(s.size()));
s.erase(i);
}
}
// GF(p_1) & GF(p_2) & ... & GF(p_n)
const spot::ltl::formula*
GF_n(spot::ltl::atomic_prop_set& ap)
{
const spot::ltl::formula* res = 0;
for (auto v: ap)
{
const spot::ltl::formula* f =
spot::ltl::unop::instance(spot::ltl::unop::F, v->clone());
f = spot::ltl::unop::instance(spot::ltl::unop::G, f);
if (res)
res = spot::ltl::multop::instance(spot::ltl::multop::And, f, res);
else
res = f;
}
return res;
}
static int static int
parse_opt(int key, char* arg, struct argp_state* as) parse_opt(int key, char* arg, struct argp_state* as)
{ {
...@@ -189,22 +156,22 @@ parse_opt(int key, char* arg, struct argp_state* as) ...@@ -189,22 +156,22 @@ parse_opt(int key, char* arg, struct argp_state* as)
switch (key) switch (key)
{ {
case 'B': case 'B':
output = OutputBool; output = OUTPUTBOOL;
break; break;
case 'L': case 'L':
output = OutputLTL; output = OUTPUTLTL;
break; break;
case 'n': case 'n':
opt_formulas = to_int(arg); opt_formulas = to_int(arg);
break; break;
case 'P': case 'P':
output = OutputPSL; output = OUTPUTPSL;
break; break;
case OPT_R: case OPT_R:
parse_r(arg); parse_r(arg);
break; break;
case 'S': case 'S':
output = OutputSERE; output = OUTPUTSERE;
break; break;
case OPT_BOOLEAN_PRIORITIES: case OPT_BOOLEAN_PRIORITIES:
opt_pB = arg; opt_pB = arg;
...@@ -271,90 +238,6 @@ main(int argc, char** argv) ...@@ -271,90 +238,6 @@ main(int argc, char** argv)
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, 0, 0)) if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, 0, 0))
exit(err); exit(err);
spot::ltl::random_formula* rf = 0;
spot::ltl::random_psl* rp = 0;
spot::ltl::random_sere* rs = 0;
const char* tok_pL = 0;
const char* tok_pS = 0;
const char* tok_pB = 0;
switch (output)
{
case OutputLTL:
rf = new spot::ltl::random_ltl(&aprops);
tok_pL = rf->parse_options(opt_pL);
if (opt_pS)
error(2, 0, "option --sere-priorities unsupported for LTL output");
if (opt_pB)
error(2, 0, "option --boolean-priorities unsupported for LTL output");
break;
case OutputBool:
rf = new spot::ltl::random_boolean(&aprops);
tok_pB = rf->parse_options(opt_pB);
if (opt_pL)
error(2, 0, "option --ltl-priorities unsupported for Boolean output");
if (opt_pS)
error(2, 0, "option --sere-priorities unsupported for Boolean output");
break;
case OutputSERE:
rf = rs = new spot::ltl::random_sere(&aprops);
tok_pS = rs->parse_options(opt_pS);
tok_pB = rs->rb.parse_options(opt_pB);
if (opt_pL)
error(2, 0, "option --ltl-priorities unsupported for SERE output");
break;
case OutputPSL:
rf = rp = new spot::ltl::random_psl(&aprops);
rs = &rp->rs;
tok_pL = rp->parse_options(opt_pL);
tok_pS = rs->parse_options(opt_pS);
tok_pB = rs->rb.parse_options(opt_pB);
break;
}
if (tok_pL)
error(2, 0, "failed to parse LTL priorities near '%s'", tok_pL);
if (tok_pS)
error(2, 0, "failed to parse SERE priorities near '%s'", tok_pS);
if (tok_pB)
error(2, 0, "failed to parse Boolean priorities near '%s'", tok_pB);
if (opt_dump_priorities)
{
switch (output)
{
case OutputLTL:
std::cout
<< "Use --ltl-priorities to set the following LTL priorities:\n";
rf->dump_priorities(std::cout);
break;
case OutputBool:
std::cout
<< ("Use --boolean-priorities to set the following Boolean "
"formula priorities:\n");
rf->dump_priorities(std::cout);
break;
case OutputPSL:
std::cout
<< "Use --ltl-priorities to set the following LTL priorities:\n";
rp->dump_priorities(std::cout);
// Fall through.
case OutputSERE:
std::cout
<< "Use --sere-priorities to set the following SERE priorities:\n";
rs->dump_priorities(std::cout);
std::cout
<< ("Use --boolean-priorities to set the following Boolean "
"formula priorities:\n");
rs->rb.dump_priorities(std::cout);
break;
default:
error(2, 0, "internal error: unknown type of output");
}
destroy_atomic_prop_set(aprops);
exit(0);
}
// running 'randltl 0' is one way to generate formulas using no // running 'randltl 0' is one way to generate formulas using no
// atomic propositions so do not complain in that case. // atomic propositions so do not complain in that case.
if (aprops.empty() && !ap_count_given) if (aprops.empty() && !ap_count_given)
...@@ -363,69 +246,71 @@ main(int argc, char** argv) ...@@ -363,69 +246,71 @@ main(int argc, char** argv)
spot::srand(opt_seed); spot::srand(opt_seed);
typedef spot::option_map opts;
std::unordered_set<const spot::ltl::formula*, opts.set("output", output);
const spot::ptr_hash<const spot::ltl::formula>> fset_t; opts.set("tree_size_min", opt_tree_size.min);
fset_t unique_set; opts.set("tree_size_max", opt_tree_size.max);
opts.set("opt_wf", opt_wf);
opts.set("opt_seed", opt_seed);
opts.set("simplification_level", simplification_level);
spot::ltl::randltlgenerator rg(aprops, opts, opt_pL, opt_pS, opt_pB);
spot::ltl::ltl_simplifier simpl(simplifier_options()); if (opt_dump_priorities)
{
switch (output)
{
case OUTPUTLTL:
std::cout
<< "Use --ltl-priorities to set the following LTL priorities:\n";
rg.dump_ltl_priorities(std::cout);
break;
case OUTPUTBOOL:
std::cout
<< ("Use --boolean-priorities to set the following Boolean "
"formula priorities:\n");
rg.dump_bool_priorities(std::cout);
break;
case OUTPUTPSL:
std::cout
<< "Use --ltl-priorities to set the following LTL priorities:\n";
rg.dump_psl_priorities(std::cout);
// Fall through.
case OUTPUTSERE:
std::cout
<< "Use --sere-priorities to set the following SERE priorities:\n";
rg.dump_sere_priorities(std::cout);
std::cout
<< ("Use --boolean-priorities to set the following Boolean "
"formula priorities:\n");
rg.dump_sere_bool_priorities(std::cout);
break;
default:
error(2, 0, "internal error: unknown type of output");
}
opts.~option_map();
destroy_atomic_prop_set(aprops);
exit(0);
}
while (opt_formulas < 0 || opt_formulas--) while (opt_formulas < 0 || opt_formulas--)
{ {
#define MAX_TRIALS 100000
unsigned trials = MAX_TRIALS;
bool ignore;
const spot::ltl::formula* f = 0;
do
{
ignore = false;
int size = opt_tree_size.min;
if (size != opt_tree_size.max)
size = spot::rrand(size, opt_tree_size.max);
f = rf->generate(size);
if (opt_wf)
{
spot::ltl::atomic_prop_set s = aprops;
remove_some_props(s);
f = spot::ltl::multop::instance(spot::ltl::multop::And,
f, GF_n(s));
}
if (simplification_level)
{
const spot::ltl::formula* tmp = simpl.simplify(f);
f->destroy();
f = tmp;
}
if (opt_unique)
{
if (unique_set.insert(f).second)
{
f->clone();
}
else
{
ignore = true;
f->destroy();
}
}
}
while (ignore && --trials);
if (trials == 0)
error(2, 0, "failed to generate a new unique formula after %d trials",
MAX_TRIALS);
static int count = 0; static int count = 0;
output_formula_checked(f, 0, ++count); spot::ltl::randltlgenerator rg2(aprops, opts);
f->destroy(); const spot::ltl::formula* f = rg.next();
if (!f)
{
opts.~option_map();
error(2, 0, "failed to generate a new unique formula after %d "\
"trials", MAX_TRIALS);
}
else
{
output_formula_checked(f, 0, ++count);
f->destroy();
}
}; };
delete rf;
// Cleanup the unicity table.
for (auto i: unique_set)
i->destroy();
// Cleanup the atomic_prop set.
destroy_atomic_prop_set(aprops); destroy_atomic_prop_set(aprops);
return 0; return 0;
} }
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
#include "misc/random.hh" #include "misc/random.hh"
#include <iostream> #include <iostream>
#include <cstring> #include <cstring>
#include "misc/optionmap.hh"
#include "ltlenv/defaultenv.hh"
#include <sstream>
namespace spot namespace spot
{ {
...@@ -266,6 +269,8 @@ namespace spot ...@@ -266,6 +269,8 @@ namespace spot
const char* const char*
random_formula::parse_options(char* options) random_formula::parse_options(char* options)
{ {
if (!options)
return nullptr;
char* key = strtok(options, "=\t, :;"); char* key = strtok(options, "=\t, :;");
while (key) while (key)
{ {
...@@ -399,5 +404,221 @@ namespace spot ...@@ -399,5 +404,221 @@ namespace spot
update_sums(); update_sums();
} }
} // ltl void
} // spot randltlgenerator::construct(atomic_prop_set aprops, option_map& opts,
char* opt_pL,
char* opt_pS,
char* opt_pB)
{
aprops_ = aprops;
output_ = opts.get("output", OUTPUTLTL);
opt_seed_ = opts.get("seed", 0);
opt_tree_size_min_ = opts.get("tree_size_min", 15);
opt_tree_size_max_ = opts.get("tree_size_max", 15);
opt_unique_ = opts.get("unique", true);
opt_wf_ = opts.get("wf", false);
opt_simpl_level_ = opts.get("simplification_level", 3);
const char* tok_pL = 0;
const char* tok_pS = 0;
const char* tok_pB = 0;
switch (output_)
{
case OUTPUTLTL:
rf_ = new random_ltl(&aprops_);
if (opt_pS)
throw std::invalid_argument("Cannot set sere priorities with "\
"LTL output");
if (opt_pB)
throw std::invalid_argument("Cannot set boolean priorities with "\
"LTL output");
tok_pL = rf_->parse_options(opt_pL);
break;
case OUTPUTBOOL:
rf_ = new random_boolean(&aprops_);
tok_pB = rf_->parse_options(opt_pB);
if (opt_pL)
throw std::invalid_argument("Cannot set ltl priorities with "\
"Boolean output");
if (opt_pS)
throw std::invalid_argument("Cannot set sere priorities "\
"with Boolean output");
break;
case OUTPUTSERE:
rf_ = rs_ = new random_sere(&aprops_);
tok_pS = rs_->parse_options(opt_pS);
tok_pB = rs_->rb.parse_options(opt_pB);
if (opt_pL)
throw std::invalid_argument("Cannot set ltl priorities "\
"with SERE output");
break;
case OUTPUTPSL:
rf_ = rp_ = new random_psl(&aprops_);
rs_ = &rp_->rs;
tok_pL = rp_->parse_options(opt_pL);
tok_pS = rs_->parse_options(opt_pS);
tok_pB = rs_->rb.parse_options(opt_pB);
break;
}
if (tok_pL)
throw("failed to parse LTL priorities near '" + std::string(tok_pL));
if (tok_pS)
throw("failed to parse SERE priorities near " + std::string(tok_pS));
if (tok_pB)
throw("failed to parse Boolean priorities near "
+ std::string(tok_pB));
spot::srand(opt_seed_);
ltl_simplifier_options simpl_opts(opt_simpl_level_);
ltl_simplifier simpl_(simpl_opts);
}
randltlgenerator::randltlgenerator(int aprops_n, option_map& opts,
char* opt_pL,
char* opt_pS,
char* opt_pB)
{
atomic_prop_set aprops_;
default_environment& e =
default_environment::instance();
for (int i = 0; i < aprops_n; ++i)
{
std::ostringstream p;
p << 'p' << i;
aprops_.insert(static_cast<const atomic_prop*>
(e.require(p.str())));
}
construct(aprops_, opts, opt_pL, opt_pS, opt_pB);
}
randltlgenerator::randltlgenerator(atomic_prop_set aprops,
option_map& opts,
char* opt_pL,
char* opt_pS,
char* opt_pB)
{
construct(aprops, opts, opt_pL, opt_pS, opt_pB);
}
randltlgenerator::~randltlgenerator()
{
delete rf_;
// Cleanup the unicity table.
for (auto i: unique_set_)
i->destroy();
}
const formula* randltlgenerator::next()
{
unsigned trials = MAX_TRIALS;
bool ignore;
const formula* f = nullptr;
do
{
ignore = false;
int size = opt_tree_size_min_;
if (size != opt_tree_size_max_)
size = spot::rrand(size, opt_tree_size_max_);
f = rf_->generate(size);
if (opt_wf_)
{
atomic_prop_set s = aprops_;
remove_some_props(s);
f = multop::instance(multop::And,
f, GF_n());
}
if (opt_simpl_level_)
{
const spot::ltl::formula* tmp = simpl_.simplify(f);
f->destroy();
f = tmp;
}
if (opt_unique_)
{
if (unique_set_.insert(f).second)
{
f->clone();
}
else
{
ignore = true;
f->destroy();
}
}
} while (ignore && --trials);
if (trials <= 0)
return nullptr;
return f;
}
void
randltlgenerator::remove_some_props(atomic_prop_set& s)
{
// How many propositions to remove from s?
// (We keep at least one.)
size_t n = spot::mrand(aprops_.size());
while (n--)
{
auto i = s.begin();
std::advance(i, spot::mrand(s.size()));
s.erase(i);
}
}
// GF(p_1) & GF(p_2) & ... & GF(p_n)
const formula*
randltlgenerator::GF_n()
{
const formula* res = 0;
for (auto v: aprops_)
{
const formula* f =
unop::instance(unop::F, v->clone());
f = unop::instance(unop::G, f);
if (res)
res = multop::instance(multop::And, f, res);
else
res = f;
}
return res;
}
void